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