xref: /aosp_15_r20/external/angle/src/libANGLE/renderer/wgpu/ProgramExecutableWgpu.cpp (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
1 //
2 // Copyright 2024 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 // ProgramExecutableWgpu.cpp: Implementation of ProgramExecutableWgpu.
7 
8 #include "libANGLE/renderer/wgpu/ProgramExecutableWgpu.h"
9 
10 #include <iterator>
11 
12 #include "angle_gl.h"
13 #include "anglebase/numerics/safe_conversions.h"
14 #include "common/PackedGLEnums_autogen.h"
15 #include "compiler/translator/wgsl/OutputUniformBlocks.h"
16 #include "libANGLE/Error.h"
17 #include "libANGLE/Program.h"
18 #include "libANGLE/renderer/renderer_utils.h"
19 #include "libANGLE/renderer/wgpu/ContextWgpu.h"
20 #include "libANGLE/renderer/wgpu/wgpu_helpers.h"
21 #include "libANGLE/renderer/wgpu/wgpu_pipeline_state.h"
22 
23 namespace rx
24 {
25 
ProgramExecutableWgpu(const gl::ProgramExecutable * executable)26 ProgramExecutableWgpu::ProgramExecutableWgpu(const gl::ProgramExecutable *executable)
27     : ProgramExecutableImpl(executable)
28 {
29     for (std::shared_ptr<BufferAndLayout> &defaultBlock : mDefaultUniformBlocks)
30     {
31         defaultBlock = std::make_shared<BufferAndLayout>();
32     }
33 }
34 
35 ProgramExecutableWgpu::~ProgramExecutableWgpu() = default;
36 
destroy(const gl::Context * context)37 void ProgramExecutableWgpu::destroy(const gl::Context *context) {}
38 
updateUniformsAndGetBindGroup(ContextWgpu * contextWgpu,wgpu::BindGroup * outBindGroup)39 angle::Result ProgramExecutableWgpu::updateUniformsAndGetBindGroup(ContextWgpu *contextWgpu,
40                                                                    wgpu::BindGroup *outBindGroup)
41 {
42     if (mDefaultUniformBlocksDirty.any())
43     {
44         // TODO(anglebug.com/376553328): this creates an entire new buffer every time a single
45         // uniform changes, and the old ones are just garbage collected. This should be optimized.
46         webgpu::BufferHelper defaultUniformBuffer;
47 
48         gl::ShaderMap<uint64_t> offsets =
49             {};  // offset in the GPU-side buffer of each shader stage's uniform data.
50         size_t requiredSpace;
51 
52         angle::CheckedNumeric<size_t> requiredSpaceChecked =
53             calcUniformUpdateRequiredSpace(contextWgpu, &offsets);
54         if (!requiredSpaceChecked.AssignIfValid(&requiredSpace))
55         {
56             return angle::Result::Stop;
57         }
58 
59         ANGLE_TRY(defaultUniformBuffer.initBuffer(
60             contextWgpu->getDevice(), requiredSpace,
61             wgpu::BufferUsage::Uniform | wgpu::BufferUsage::CopyDst, webgpu::MapAtCreation::Yes));
62 
63         ASSERT(defaultUniformBuffer.valid());
64 
65         // Copy all of the CPU-side data into this buffer which will be visible to the GPU after it
66         // is unmapped here on the CPU.
67         uint8_t *bufferData = defaultUniformBuffer.getMapWritePointer(0, requiredSpace);
68         for (gl::ShaderType shaderType : mExecutable->getLinkedShaderStages())
69         {
70             const angle::MemoryBuffer &uniformData = mDefaultUniformBlocks[shaderType]->uniformData;
71             memcpy(&bufferData[offsets[shaderType]], uniformData.data(), uniformData.size());
72             mDefaultUniformBlocksDirty.reset(shaderType);
73         }
74         ANGLE_TRY(defaultUniformBuffer.unmap());
75 
76         // Create the BindGroupEntries
77         std::vector<wgpu::BindGroupEntry> bindings;
78         auto addBindingToGroupIfNecessary = [&](uint32_t bindingIndex, gl::ShaderType shaderType) {
79             if (mDefaultUniformBlocks[shaderType]->uniformData.size() != 0)
80             {
81                 wgpu::BindGroupEntry bindGroupEntry;
82                 bindGroupEntry.binding = bindingIndex;
83                 bindGroupEntry.buffer  = defaultUniformBuffer.getBuffer();
84                 bindGroupEntry.offset  = offsets[shaderType];
85                 bindGroupEntry.size    = mDefaultUniformBlocks[shaderType]->uniformData.size();
86                 bindings.push_back(bindGroupEntry);
87             }
88         };
89 
90         // Add the BindGroupEntry for the default blocks of both the vertex and fragment shaders.
91         // They will use the same buffer with a different offset.
92         addBindingToGroupIfNecessary(sh::kDefaultVertexUniformBlockBinding, gl::ShaderType::Vertex);
93         addBindingToGroupIfNecessary(sh::kDefaultFragmentUniformBlockBinding,
94                                      gl::ShaderType::Fragment);
95 
96         // A bind group contains one or multiple bindings
97         wgpu::BindGroupDescriptor bindGroupDesc{};
98         bindGroupDesc.layout = mDefaultBindGroupLayout;
99         // There must be as many bindings as declared in the layout!
100         bindGroupDesc.entryCount = bindings.size();
101         bindGroupDesc.entries    = bindings.data();
102         mDefaultBindGroup        = contextWgpu->getDevice().CreateBindGroup(&bindGroupDesc);
103     }
104 
105     ASSERT(mDefaultBindGroup);
106     *outBindGroup = mDefaultBindGroup;
107 
108     return angle::Result::Continue;
109 }
110 
getDefaultUniformAlignedSize(ContextWgpu * context,gl::ShaderType shaderType) const111 angle::CheckedNumeric<size_t> ProgramExecutableWgpu::getDefaultUniformAlignedSize(
112     ContextWgpu *context,
113     gl::ShaderType shaderType) const
114 {
115     size_t alignment = angle::base::checked_cast<size_t>(
116         context->getDisplay()->getLimitsWgpu().minUniformBufferOffsetAlignment);
117     return CheckedRoundUp(mDefaultUniformBlocks[shaderType]->uniformData.size(), alignment);
118 }
119 
calcUniformUpdateRequiredSpace(ContextWgpu * context,gl::ShaderMap<uint64_t> * uniformOffsets) const120 angle::CheckedNumeric<size_t> ProgramExecutableWgpu::calcUniformUpdateRequiredSpace(
121     ContextWgpu *context,
122     gl::ShaderMap<uint64_t> *uniformOffsets) const
123 {
124     angle::CheckedNumeric<size_t> requiredSpace = 0;
125     for (gl::ShaderType shaderType : mExecutable->getLinkedShaderStages())
126     {
127         (*uniformOffsets)[shaderType] = requiredSpace.ValueOrDie();
128         requiredSpace += getDefaultUniformAlignedSize(context, shaderType);
129         if (!requiredSpace.IsValid())
130         {
131             break;
132         }
133     }
134     return requiredSpace;
135 }
136 
resizeUniformBlockMemory(const gl::ShaderMap<size_t> & requiredBufferSize)137 angle::Result ProgramExecutableWgpu::resizeUniformBlockMemory(
138     const gl::ShaderMap<size_t> &requiredBufferSize)
139 {
140     for (gl::ShaderType shaderType : mExecutable->getLinkedShaderStages())
141     {
142         if (requiredBufferSize[shaderType] > 0)
143         {
144             if (!mDefaultUniformBlocks[shaderType]->uniformData.resize(
145                     requiredBufferSize[shaderType]))
146             {
147                 return angle::Result::Stop;
148             }
149 
150             // Initialize uniform buffer memory to zero by default.
151             mDefaultUniformBlocks[shaderType]->uniformData.fill(0);
152             mDefaultUniformBlocksDirty.set(shaderType);
153         }
154     }
155 
156     return angle::Result::Continue;
157 }
158 
markDefaultUniformsDirty()159 void ProgramExecutableWgpu::markDefaultUniformsDirty()
160 {
161     // Mark all linked stages as having dirty default uniforms
162     mDefaultUniformBlocksDirty = getExecutable()->getLinkedShaderStages();
163 }
164 
setUniform1fv(GLint location,GLsizei count,const GLfloat * v)165 void ProgramExecutableWgpu::setUniform1fv(GLint location, GLsizei count, const GLfloat *v)
166 {
167     SetUniform(mExecutable, location, count, v, GL_FLOAT, &mDefaultUniformBlocks,
168                &mDefaultUniformBlocksDirty);
169 }
170 
setUniform2fv(GLint location,GLsizei count,const GLfloat * v)171 void ProgramExecutableWgpu::setUniform2fv(GLint location, GLsizei count, const GLfloat *v)
172 {
173     SetUniform(mExecutable, location, count, v, GL_FLOAT_VEC2, &mDefaultUniformBlocks,
174                &mDefaultUniformBlocksDirty);
175 }
176 
setUniform3fv(GLint location,GLsizei count,const GLfloat * v)177 void ProgramExecutableWgpu::setUniform3fv(GLint location, GLsizei count, const GLfloat *v)
178 {
179     SetUniform(mExecutable, location, count, v, GL_FLOAT_VEC3, &mDefaultUniformBlocks,
180                &mDefaultUniformBlocksDirty);
181 }
182 
setUniform4fv(GLint location,GLsizei count,const GLfloat * v)183 void ProgramExecutableWgpu::setUniform4fv(GLint location, GLsizei count, const GLfloat *v)
184 {
185     SetUniform(mExecutable, location, count, v, GL_FLOAT_VEC4, &mDefaultUniformBlocks,
186                &mDefaultUniformBlocksDirty);
187 }
188 
setUniform1iv(GLint location,GLsizei count,const GLint * v)189 void ProgramExecutableWgpu::setUniform1iv(GLint location, GLsizei count, const GLint *v)
190 {
191     const gl::VariableLocation &locationInfo = mExecutable->getUniformLocations()[location];
192     const gl::LinkedUniform &linkedUniform   = mExecutable->getUniforms()[locationInfo.index];
193     if (linkedUniform.isSampler())
194     {
195         // TODO(anglebug.com/42267100): handle samplers.
196         return;
197     }
198 
199     SetUniform(mExecutable, location, count, v, GL_INT, &mDefaultUniformBlocks,
200                &mDefaultUniformBlocksDirty);
201 }
202 
setUniform2iv(GLint location,GLsizei count,const GLint * v)203 void ProgramExecutableWgpu::setUniform2iv(GLint location, GLsizei count, const GLint *v)
204 {
205     SetUniform(mExecutable, location, count, v, GL_INT_VEC2, &mDefaultUniformBlocks,
206                &mDefaultUniformBlocksDirty);
207 }
208 
setUniform3iv(GLint location,GLsizei count,const GLint * v)209 void ProgramExecutableWgpu::setUniform3iv(GLint location, GLsizei count, const GLint *v)
210 {
211     SetUniform(mExecutable, location, count, v, GL_INT_VEC3, &mDefaultUniformBlocks,
212                &mDefaultUniformBlocksDirty);
213 }
214 
setUniform4iv(GLint location,GLsizei count,const GLint * v)215 void ProgramExecutableWgpu::setUniform4iv(GLint location, GLsizei count, const GLint *v)
216 {
217     SetUniform(mExecutable, location, count, v, GL_INT_VEC4, &mDefaultUniformBlocks,
218                &mDefaultUniformBlocksDirty);
219 }
220 
setUniform1uiv(GLint location,GLsizei count,const GLuint * v)221 void ProgramExecutableWgpu::setUniform1uiv(GLint location, GLsizei count, const GLuint *v)
222 {
223     SetUniform(mExecutable, location, count, v, GL_UNSIGNED_INT, &mDefaultUniformBlocks,
224                &mDefaultUniformBlocksDirty);
225 }
226 
setUniform2uiv(GLint location,GLsizei count,const GLuint * v)227 void ProgramExecutableWgpu::setUniform2uiv(GLint location, GLsizei count, const GLuint *v)
228 {
229     SetUniform(mExecutable, location, count, v, GL_UNSIGNED_INT_VEC2, &mDefaultUniformBlocks,
230                &mDefaultUniformBlocksDirty);
231 }
232 
setUniform3uiv(GLint location,GLsizei count,const GLuint * v)233 void ProgramExecutableWgpu::setUniform3uiv(GLint location, GLsizei count, const GLuint *v)
234 {
235     SetUniform(mExecutable, location, count, v, GL_UNSIGNED_INT_VEC3, &mDefaultUniformBlocks,
236                &mDefaultUniformBlocksDirty);
237 }
238 
setUniform4uiv(GLint location,GLsizei count,const GLuint * v)239 void ProgramExecutableWgpu::setUniform4uiv(GLint location, GLsizei count, const GLuint *v)
240 {
241     SetUniform(mExecutable, location, count, v, GL_UNSIGNED_INT_VEC4, &mDefaultUniformBlocks,
242                &mDefaultUniformBlocksDirty);
243 }
244 
setUniformMatrix2fv(GLint location,GLsizei count,GLboolean transpose,const GLfloat * value)245 void ProgramExecutableWgpu::setUniformMatrix2fv(GLint location,
246                                                 GLsizei count,
247                                                 GLboolean transpose,
248                                                 const GLfloat *value)
249 {
250     SetUniformMatrixfv<2, 2>(mExecutable, location, count, transpose, value, &mDefaultUniformBlocks,
251                              &mDefaultUniformBlocksDirty);
252 }
253 
setUniformMatrix3fv(GLint location,GLsizei count,GLboolean transpose,const GLfloat * value)254 void ProgramExecutableWgpu::setUniformMatrix3fv(GLint location,
255                                                 GLsizei count,
256                                                 GLboolean transpose,
257                                                 const GLfloat *value)
258 {
259     SetUniformMatrixfv<3, 3>(mExecutable, location, count, transpose, value, &mDefaultUniformBlocks,
260                              &mDefaultUniformBlocksDirty);
261 }
262 
setUniformMatrix4fv(GLint location,GLsizei count,GLboolean transpose,const GLfloat * value)263 void ProgramExecutableWgpu::setUniformMatrix4fv(GLint location,
264                                                 GLsizei count,
265                                                 GLboolean transpose,
266                                                 const GLfloat *value)
267 {
268     SetUniformMatrixfv<4, 4>(mExecutable, location, count, transpose, value, &mDefaultUniformBlocks,
269                              &mDefaultUniformBlocksDirty);
270 }
271 
setUniformMatrix2x3fv(GLint location,GLsizei count,GLboolean transpose,const GLfloat * value)272 void ProgramExecutableWgpu::setUniformMatrix2x3fv(GLint location,
273                                                   GLsizei count,
274                                                   GLboolean transpose,
275                                                   const GLfloat *value)
276 {
277     SetUniformMatrixfv<2, 3>(mExecutable, location, count, transpose, value, &mDefaultUniformBlocks,
278                              &mDefaultUniformBlocksDirty);
279 }
280 
setUniformMatrix3x2fv(GLint location,GLsizei count,GLboolean transpose,const GLfloat * value)281 void ProgramExecutableWgpu::setUniformMatrix3x2fv(GLint location,
282                                                   GLsizei count,
283                                                   GLboolean transpose,
284                                                   const GLfloat *value)
285 {
286     SetUniformMatrixfv<3, 2>(mExecutable, location, count, transpose, value, &mDefaultUniformBlocks,
287                              &mDefaultUniformBlocksDirty);
288 }
289 
setUniformMatrix2x4fv(GLint location,GLsizei count,GLboolean transpose,const GLfloat * value)290 void ProgramExecutableWgpu::setUniformMatrix2x4fv(GLint location,
291                                                   GLsizei count,
292                                                   GLboolean transpose,
293                                                   const GLfloat *value)
294 {
295     SetUniformMatrixfv<2, 4>(mExecutable, location, count, transpose, value, &mDefaultUniformBlocks,
296                              &mDefaultUniformBlocksDirty);
297 }
298 
setUniformMatrix4x2fv(GLint location,GLsizei count,GLboolean transpose,const GLfloat * value)299 void ProgramExecutableWgpu::setUniformMatrix4x2fv(GLint location,
300                                                   GLsizei count,
301                                                   GLboolean transpose,
302                                                   const GLfloat *value)
303 {
304     SetUniformMatrixfv<4, 2>(mExecutable, location, count, transpose, value, &mDefaultUniformBlocks,
305                              &mDefaultUniformBlocksDirty);
306 }
307 
setUniformMatrix3x4fv(GLint location,GLsizei count,GLboolean transpose,const GLfloat * value)308 void ProgramExecutableWgpu::setUniformMatrix3x4fv(GLint location,
309                                                   GLsizei count,
310                                                   GLboolean transpose,
311                                                   const GLfloat *value)
312 {
313     SetUniformMatrixfv<3, 4>(mExecutable, location, count, transpose, value, &mDefaultUniformBlocks,
314                              &mDefaultUniformBlocksDirty);
315 }
316 
setUniformMatrix4x3fv(GLint location,GLsizei count,GLboolean transpose,const GLfloat * value)317 void ProgramExecutableWgpu::setUniformMatrix4x3fv(GLint location,
318                                                   GLsizei count,
319                                                   GLboolean transpose,
320                                                   const GLfloat *value)
321 {
322     SetUniformMatrixfv<4, 3>(mExecutable, location, count, transpose, value, &mDefaultUniformBlocks,
323                              &mDefaultUniformBlocksDirty);
324 }
325 
getUniformfv(const gl::Context * context,GLint location,GLfloat * params) const326 void ProgramExecutableWgpu::getUniformfv(const gl::Context *context,
327                                          GLint location,
328                                          GLfloat *params) const
329 {
330     GetUniform(mExecutable, location, params, GL_FLOAT, &mDefaultUniformBlocks);
331 }
332 
getUniformiv(const gl::Context * context,GLint location,GLint * params) const333 void ProgramExecutableWgpu::getUniformiv(const gl::Context *context,
334                                          GLint location,
335                                          GLint *params) const
336 {
337     GetUniform(mExecutable, location, params, GL_INT, &mDefaultUniformBlocks);
338 }
339 
getUniformuiv(const gl::Context * context,GLint location,GLuint * params) const340 void ProgramExecutableWgpu::getUniformuiv(const gl::Context *context,
341                                           GLint location,
342                                           GLuint *params) const
343 {
344     GetUniform(mExecutable, location, params, GL_UNSIGNED_INT, &mDefaultUniformBlocks);
345 }
346 
getShaderModule(gl::ShaderType type)347 TranslatedWGPUShaderModule &ProgramExecutableWgpu::getShaderModule(gl::ShaderType type)
348 {
349     return mShaderModules[type];
350 }
351 
getRenderPipeline(ContextWgpu * context,const webgpu::RenderPipelineDesc & desc,wgpu::RenderPipeline * pipelineOut)352 angle::Result ProgramExecutableWgpu::getRenderPipeline(ContextWgpu *context,
353                                                        const webgpu::RenderPipelineDesc &desc,
354                                                        wgpu::RenderPipeline *pipelineOut)
355 {
356     gl::ShaderMap<wgpu::ShaderModule> shaders;
357     for (gl::ShaderType shaderType : gl::AllShaderTypes())
358     {
359         shaders[shaderType] = mShaderModules[shaderType].module;
360     }
361 
362     genBindingLayoutIfNecessary(context);
363 
364     return mPipelineCache.getRenderPipeline(context, desc, mPipelineLayout, shaders, pipelineOut);
365 }
366 
genBindingLayoutIfNecessary(ContextWgpu * context)367 void ProgramExecutableWgpu::genBindingLayoutIfNecessary(ContextWgpu *context)
368 {
369     if (mPipelineLayout)
370     {
371         return;
372     }
373     // TODO(anglebug.com/42267100): for now, only create a wgpu::PipelineLayout with the default
374     // uniform block. Will need to be extended for driver uniforms, UBOs, and textures/samplers.
375     // Also, possibly provide this layout as a compilation hint to createShaderModule().
376 
377     std::vector<wgpu::BindGroupLayoutEntry> bindGroupLayoutEntries;
378     auto addBindGroupLayoutEntryIfNecessary = [&](uint32_t bindingIndex, gl::ShaderType shaderType,
379                                                   wgpu::ShaderStage wgpuVisibility) {
380         if (mDefaultUniformBlocks[shaderType]->uniformData.size() != 0)
381         {
382             wgpu::BindGroupLayoutEntry bindGroupLayoutEntry;
383             bindGroupLayoutEntry.visibility  = wgpuVisibility;
384             bindGroupLayoutEntry.binding     = bindingIndex;
385             bindGroupLayoutEntry.buffer.type = wgpu::BufferBindingType::Uniform;
386             // By setting a `minBindingSize`, some validation is pushed from every draw call to
387             // pipeline creation time.
388             bindGroupLayoutEntry.buffer.minBindingSize =
389                 mDefaultUniformBlocks[shaderType]->uniformData.size();
390             bindGroupLayoutEntries.push_back(bindGroupLayoutEntry);
391         }
392     };
393     // Default uniform blocks for each of the vertex shader and the fragment shader.
394     addBindGroupLayoutEntryIfNecessary(sh::kDefaultVertexUniformBlockBinding,
395                                        gl::ShaderType::Vertex, wgpu::ShaderStage::Vertex);
396     addBindGroupLayoutEntryIfNecessary(sh::kDefaultFragmentUniformBlockBinding,
397                                        gl::ShaderType::Fragment, wgpu::ShaderStage::Fragment);
398 
399     // Create a bind group layout with these entries.
400     wgpu::BindGroupLayoutDescriptor bindGroupLayoutDesc{};
401     bindGroupLayoutDesc.entryCount = bindGroupLayoutEntries.size();
402     bindGroupLayoutDesc.entries    = bindGroupLayoutEntries.data();
403     mDefaultBindGroupLayout = context->getDevice().CreateBindGroupLayout(&bindGroupLayoutDesc);
404 
405     // Create the pipeline layout. This is a list where each element N corresponds to the @group(N)
406     // in the compiled shaders.
407     wgpu::PipelineLayoutDescriptor layoutDesc{};
408     layoutDesc.bindGroupLayoutCount = 1;
409     layoutDesc.bindGroupLayouts     = &mDefaultBindGroupLayout;
410     mPipelineLayout                 = context->getDevice().CreatePipelineLayout(&layoutDesc);
411 }
412 
413 }  // namespace rx
414