1 // Copyright 2017 The Dawn Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "utils/WGPUHelpers.h"
16 
17 #include "common/Constants.h"
18 #include "common/Log.h"
19 
20 #include <shaderc/shaderc.hpp>
21 
22 #include <cstring>
23 #include <iomanip>
24 #include <mutex>
25 #include <sstream>
26 
27 namespace utils {
28 
29     namespace {
30 
ShadercShaderKind(SingleShaderStage stage)31         shaderc_shader_kind ShadercShaderKind(SingleShaderStage stage) {
32             switch (stage) {
33                 case SingleShaderStage::Vertex:
34                     return shaderc_glsl_vertex_shader;
35                 case SingleShaderStage::Fragment:
36                     return shaderc_glsl_fragment_shader;
37                 case SingleShaderStage::Compute:
38                     return shaderc_glsl_compute_shader;
39                 default:
40                     UNREACHABLE();
41             }
42         }
43 
CreateShaderModuleFromResult(const wgpu::Device & device,const shaderc::SpvCompilationResult & result)44         wgpu::ShaderModule CreateShaderModuleFromResult(
45             const wgpu::Device& device,
46             const shaderc::SpvCompilationResult& result) {
47             // result.cend and result.cbegin return pointers to uint32_t.
48             const uint32_t* resultBegin = result.cbegin();
49             const uint32_t* resultEnd = result.cend();
50             // So this size is in units of sizeof(uint32_t).
51             ptrdiff_t resultSize = resultEnd - resultBegin;
52             // SetSource takes data as uint32_t*.
53 
54             wgpu::ShaderModuleSPIRVDescriptor spirvDesc;
55             spirvDesc.codeSize = static_cast<uint32_t>(resultSize);
56             spirvDesc.code = result.cbegin();
57 
58             wgpu::ShaderModuleDescriptor descriptor;
59             descriptor.nextInChain = &spirvDesc;
60 
61             return device.CreateShaderModule(&descriptor);
62         }
63 
64         class CompilerSingleton {
65           public:
Get()66             static shaderc::Compiler* Get() {
67                 std::call_once(mInitFlag, &CompilerSingleton::Initialize);
68                 return mCompiler;
69             }
70 
71           private:
72             CompilerSingleton() = default;
73             ~CompilerSingleton() = default;
74             CompilerSingleton(const CompilerSingleton&) = delete;
75             CompilerSingleton& operator=(const CompilerSingleton&) = delete;
76 
77             static shaderc::Compiler* mCompiler;
78             static std::once_flag mInitFlag;
79 
Initialize()80             static void Initialize() {
81                 mCompiler = new shaderc::Compiler();
82             }
83         };
84 
85         shaderc::Compiler* CompilerSingleton::mCompiler = nullptr;
86         std::once_flag CompilerSingleton::mInitFlag;
87 
88     }  // anonymous namespace
89 
CreateShaderModule(const wgpu::Device & device,SingleShaderStage stage,const char * source)90     wgpu::ShaderModule CreateShaderModule(const wgpu::Device& device,
91                                           SingleShaderStage stage,
92                                           const char* source) {
93         shaderc_shader_kind kind = ShadercShaderKind(stage);
94 
95         shaderc::Compiler* compiler = CompilerSingleton::Get();
96         auto result = compiler->CompileGlslToSpv(source, strlen(source), kind, "myshader?");
97         if (result.GetCompilationStatus() != shaderc_compilation_status_success) {
98             dawn::ErrorLog() << result.GetErrorMessage();
99             return {};
100         }
101 #ifdef DUMP_SPIRV_ASSEMBLY
102         {
103             shaderc::CompileOptions options;
104             auto resultAsm = compiler->CompileGlslToSpvAssembly(source, strlen(source), kind,
105                                                                 "myshader?", options);
106             size_t sizeAsm = (resultAsm.cend() - resultAsm.cbegin());
107 
108             char* buffer = reinterpret_cast<char*>(malloc(sizeAsm + 1));
109             memcpy(buffer, resultAsm.cbegin(), sizeAsm);
110             buffer[sizeAsm] = '\0';
111             printf("SPIRV ASSEMBLY DUMP START\n%s\nSPIRV ASSEMBLY DUMP END\n", buffer);
112             free(buffer);
113         }
114 #endif
115 
116 #ifdef DUMP_SPIRV_JS_ARRAY
117         printf("SPIRV JS ARRAY DUMP START\n");
118         for (size_t i = 0; i < size; i++) {
119             printf("%#010x", result.cbegin()[i]);
120             if ((i + 1) % 4 == 0) {
121                 printf(",\n");
122             } else {
123                 printf(", ");
124             }
125         }
126         printf("\n");
127         printf("SPIRV JS ARRAY DUMP END\n");
128 #endif
129 
130         return CreateShaderModuleFromResult(device, result);
131     }
132 
CreateShaderModuleFromASM(const wgpu::Device & device,const char * source)133     wgpu::ShaderModule CreateShaderModuleFromASM(const wgpu::Device& device, const char* source) {
134         shaderc::Compiler* compiler = CompilerSingleton::Get();
135         shaderc::SpvCompilationResult result = compiler->AssembleToSpv(source, strlen(source));
136         if (result.GetCompilationStatus() != shaderc_compilation_status_success) {
137             dawn::ErrorLog() << result.GetErrorMessage();
138             return {};
139         }
140 
141         return CreateShaderModuleFromResult(device, result);
142     }
143 
CreateShaderModuleFromWGSL(const wgpu::Device & device,const char * source)144     wgpu::ShaderModule CreateShaderModuleFromWGSL(const wgpu::Device& device, const char* source) {
145         wgpu::ShaderModuleWGSLDescriptor wgslDesc;
146         wgslDesc.source = source;
147         wgpu::ShaderModuleDescriptor descriptor;
148         descriptor.nextInChain = &wgslDesc;
149         return device.CreateShaderModule(&descriptor);
150     }
151 
CompileGLSLToSpirv(SingleShaderStage stage,const char * source)152     std::vector<uint32_t> CompileGLSLToSpirv(SingleShaderStage stage, const char* source) {
153         shaderc_shader_kind kind = ShadercShaderKind(stage);
154 
155         shaderc::Compiler* compiler = CompilerSingleton::Get();
156         auto result = compiler->CompileGlslToSpv(source, strlen(source), kind, "myshader?");
157         if (result.GetCompilationStatus() != shaderc_compilation_status_success) {
158             dawn::ErrorLog() << result.GetErrorMessage();
159             return {};
160         }
161         return {result.cbegin(), result.cend()};
162     }
163 
CreateBufferFromData(const wgpu::Device & device,const void * data,uint64_t size,wgpu::BufferUsage usage)164     wgpu::Buffer CreateBufferFromData(const wgpu::Device& device,
165                                       const void* data,
166                                       uint64_t size,
167                                       wgpu::BufferUsage usage) {
168         wgpu::BufferDescriptor descriptor;
169         descriptor.size = size;
170         descriptor.usage = usage | wgpu::BufferUsage::CopyDst;
171         wgpu::Buffer buffer = device.CreateBuffer(&descriptor);
172 
173         device.GetDefaultQueue().WriteBuffer(buffer, 0, data, size);
174         return buffer;
175     }
176 
ComboRenderPassDescriptor(std::initializer_list<wgpu::TextureView> colorAttachmentInfo,wgpu::TextureView depthStencil)177     ComboRenderPassDescriptor::ComboRenderPassDescriptor(
178         std::initializer_list<wgpu::TextureView> colorAttachmentInfo,
179         wgpu::TextureView depthStencil) {
180         for (uint32_t i = 0; i < kMaxColorAttachments; ++i) {
181             cColorAttachments[i].loadOp = wgpu::LoadOp::Clear;
182             cColorAttachments[i].storeOp = wgpu::StoreOp::Store;
183             cColorAttachments[i].clearColor = {0.0f, 0.0f, 0.0f, 0.0f};
184         }
185 
186         cDepthStencilAttachmentInfo.clearDepth = 1.0f;
187         cDepthStencilAttachmentInfo.clearStencil = 0;
188         cDepthStencilAttachmentInfo.depthLoadOp = wgpu::LoadOp::Clear;
189         cDepthStencilAttachmentInfo.depthStoreOp = wgpu::StoreOp::Store;
190         cDepthStencilAttachmentInfo.stencilLoadOp = wgpu::LoadOp::Clear;
191         cDepthStencilAttachmentInfo.stencilStoreOp = wgpu::StoreOp::Store;
192 
193         colorAttachmentCount = static_cast<uint32_t>(colorAttachmentInfo.size());
194         uint32_t colorAttachmentIndex = 0;
195         for (const wgpu::TextureView& colorAttachment : colorAttachmentInfo) {
196             if (colorAttachment.Get() != nullptr) {
197                 cColorAttachments[colorAttachmentIndex].attachment = colorAttachment;
198             }
199             ++colorAttachmentIndex;
200         }
201         colorAttachments = cColorAttachments.data();
202 
203         if (depthStencil.Get() != nullptr) {
204             cDepthStencilAttachmentInfo.attachment = depthStencil;
205             depthStencilAttachment = &cDepthStencilAttachmentInfo;
206         } else {
207             depthStencilAttachment = nullptr;
208         }
209     }
210 
ComboRenderPassDescriptor(const ComboRenderPassDescriptor & other)211     ComboRenderPassDescriptor::ComboRenderPassDescriptor(const ComboRenderPassDescriptor& other) {
212         *this = other;
213     }
214 
operator =(const ComboRenderPassDescriptor & otherRenderPass)215     const ComboRenderPassDescriptor& ComboRenderPassDescriptor::operator=(
216         const ComboRenderPassDescriptor& otherRenderPass) {
217         cDepthStencilAttachmentInfo = otherRenderPass.cDepthStencilAttachmentInfo;
218         cColorAttachments = otherRenderPass.cColorAttachments;
219         colorAttachmentCount = otherRenderPass.colorAttachmentCount;
220 
221         colorAttachments = cColorAttachments.data();
222 
223         if (otherRenderPass.depthStencilAttachment != nullptr) {
224             // Assign desc.depthStencilAttachment to this->depthStencilAttachmentInfo;
225             depthStencilAttachment = &cDepthStencilAttachmentInfo;
226         } else {
227             depthStencilAttachment = nullptr;
228         }
229 
230         return *this;
231     }
232 
BasicRenderPass()233     BasicRenderPass::BasicRenderPass()
234         : width(0),
235           height(0),
236           color(nullptr),
237           colorFormat(wgpu::TextureFormat::RGBA8Unorm),
238           renderPassInfo({}) {
239     }
240 
BasicRenderPass(uint32_t texWidth,uint32_t texHeight,wgpu::Texture colorAttachment,wgpu::TextureFormat textureFormat)241     BasicRenderPass::BasicRenderPass(uint32_t texWidth,
242                                      uint32_t texHeight,
243                                      wgpu::Texture colorAttachment,
244                                      wgpu::TextureFormat textureFormat)
245         : width(texWidth),
246           height(texHeight),
247           color(colorAttachment),
248           colorFormat(textureFormat),
249           renderPassInfo({colorAttachment.CreateView()}) {
250     }
251 
CreateBasicRenderPass(const wgpu::Device & device,uint32_t width,uint32_t height)252     BasicRenderPass CreateBasicRenderPass(const wgpu::Device& device,
253                                           uint32_t width,
254                                           uint32_t height) {
255         DAWN_ASSERT(width > 0 && height > 0);
256 
257         wgpu::TextureDescriptor descriptor;
258         descriptor.dimension = wgpu::TextureDimension::e2D;
259         descriptor.size.width = width;
260         descriptor.size.height = height;
261         descriptor.size.depth = 1;
262         descriptor.sampleCount = 1;
263         descriptor.format = BasicRenderPass::kDefaultColorFormat;
264         descriptor.mipLevelCount = 1;
265         descriptor.usage = wgpu::TextureUsage::RenderAttachment | wgpu::TextureUsage::CopySrc;
266         wgpu::Texture color = device.CreateTexture(&descriptor);
267 
268         return BasicRenderPass(width, height, color);
269     }
270 
CreateBufferCopyView(wgpu::Buffer buffer,uint64_t offset,uint32_t bytesPerRow,uint32_t rowsPerImage)271     wgpu::BufferCopyView CreateBufferCopyView(wgpu::Buffer buffer,
272                                               uint64_t offset,
273                                               uint32_t bytesPerRow,
274                                               uint32_t rowsPerImage) {
275         wgpu::BufferCopyView bufferCopyView = {};
276         bufferCopyView.buffer = buffer;
277         bufferCopyView.layout = CreateTextureDataLayout(offset, bytesPerRow, rowsPerImage);
278 
279         return bufferCopyView;
280     }
281 
CreateTextureCopyView(wgpu::Texture texture,uint32_t mipLevel,wgpu::Origin3D origin,wgpu::TextureAspect aspect)282     wgpu::TextureCopyView CreateTextureCopyView(wgpu::Texture texture,
283                                                 uint32_t mipLevel,
284                                                 wgpu::Origin3D origin,
285                                                 wgpu::TextureAspect aspect) {
286         wgpu::TextureCopyView textureCopyView;
287         textureCopyView.texture = texture;
288         textureCopyView.mipLevel = mipLevel;
289         textureCopyView.origin = origin;
290         textureCopyView.aspect = aspect;
291 
292         return textureCopyView;
293     }
294 
CreateTextureDataLayout(uint64_t offset,uint32_t bytesPerRow,uint32_t rowsPerImage)295     wgpu::TextureDataLayout CreateTextureDataLayout(uint64_t offset,
296                                                     uint32_t bytesPerRow,
297                                                     uint32_t rowsPerImage) {
298         wgpu::TextureDataLayout textureDataLayout;
299         textureDataLayout.offset = offset;
300         textureDataLayout.bytesPerRow = bytesPerRow;
301         textureDataLayout.rowsPerImage = rowsPerImage;
302 
303         return textureDataLayout;
304     }
305 
GetDefaultSamplerDescriptor()306     wgpu::SamplerDescriptor GetDefaultSamplerDescriptor() {
307         wgpu::SamplerDescriptor desc = {};
308 
309         desc.minFilter = wgpu::FilterMode::Linear;
310         desc.magFilter = wgpu::FilterMode::Linear;
311         desc.mipmapFilter = wgpu::FilterMode::Linear;
312         desc.addressModeU = wgpu::AddressMode::Repeat;
313         desc.addressModeV = wgpu::AddressMode::Repeat;
314         desc.addressModeW = wgpu::AddressMode::Repeat;
315 
316         return desc;
317     }
318 
MakeBasicPipelineLayout(const wgpu::Device & device,const wgpu::BindGroupLayout * bindGroupLayout)319     wgpu::PipelineLayout MakeBasicPipelineLayout(const wgpu::Device& device,
320                                                  const wgpu::BindGroupLayout* bindGroupLayout) {
321         wgpu::PipelineLayoutDescriptor descriptor;
322         if (bindGroupLayout != nullptr) {
323             descriptor.bindGroupLayoutCount = 1;
324             descriptor.bindGroupLayouts = bindGroupLayout;
325         } else {
326             descriptor.bindGroupLayoutCount = 0;
327             descriptor.bindGroupLayouts = nullptr;
328         }
329         return device.CreatePipelineLayout(&descriptor);
330     }
331 
MakeBindGroupLayout(const wgpu::Device & device,std::initializer_list<wgpu::BindGroupLayoutEntry> entriesInitializer)332     wgpu::BindGroupLayout MakeBindGroupLayout(
333         const wgpu::Device& device,
334         std::initializer_list<wgpu::BindGroupLayoutEntry> entriesInitializer) {
335         std::vector<wgpu::BindGroupLayoutEntry> entries;
336         for (const wgpu::BindGroupLayoutEntry& entry : entriesInitializer) {
337             entries.push_back(entry);
338         }
339 
340         wgpu::BindGroupLayoutDescriptor descriptor;
341         descriptor.entryCount = static_cast<uint32_t>(entries.size());
342         descriptor.entries = entries.data();
343         return device.CreateBindGroupLayout(&descriptor);
344     }
345 
BindingInitializationHelper(uint32_t binding,const wgpu::Sampler & sampler)346     BindingInitializationHelper::BindingInitializationHelper(uint32_t binding,
347                                                              const wgpu::Sampler& sampler)
348         : binding(binding), sampler(sampler) {
349     }
350 
BindingInitializationHelper(uint32_t binding,const wgpu::TextureView & textureView)351     BindingInitializationHelper::BindingInitializationHelper(uint32_t binding,
352                                                              const wgpu::TextureView& textureView)
353         : binding(binding), textureView(textureView) {
354     }
355 
BindingInitializationHelper(uint32_t binding,const wgpu::Buffer & buffer,uint64_t offset,uint64_t size)356     BindingInitializationHelper::BindingInitializationHelper(uint32_t binding,
357                                                              const wgpu::Buffer& buffer,
358                                                              uint64_t offset,
359                                                              uint64_t size)
360         : binding(binding), buffer(buffer), offset(offset), size(size) {
361     }
362 
GetAsBinding() const363     wgpu::BindGroupEntry BindingInitializationHelper::GetAsBinding() const {
364         wgpu::BindGroupEntry result;
365 
366         result.binding = binding;
367         result.sampler = sampler;
368         result.textureView = textureView;
369         result.buffer = buffer;
370         result.offset = offset;
371         result.size = size;
372 
373         return result;
374     }
375 
MakeBindGroup(const wgpu::Device & device,const wgpu::BindGroupLayout & layout,std::initializer_list<BindingInitializationHelper> entriesInitializer)376     wgpu::BindGroup MakeBindGroup(
377         const wgpu::Device& device,
378         const wgpu::BindGroupLayout& layout,
379         std::initializer_list<BindingInitializationHelper> entriesInitializer) {
380         std::vector<wgpu::BindGroupEntry> entries;
381         for (const BindingInitializationHelper& helper : entriesInitializer) {
382             entries.push_back(helper.GetAsBinding());
383         }
384 
385         wgpu::BindGroupDescriptor descriptor;
386         descriptor.layout = layout;
387         descriptor.entryCount = entries.size();
388         descriptor.entries = entries.data();
389 
390         return device.CreateBindGroup(&descriptor);
391     }
392 
393 }  // namespace utils
394