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 // ProgramWgpu.cpp:
7 // Implements the class methods for ProgramWgpu.
8 //
9
10 #include "libANGLE/renderer/wgpu/ProgramWgpu.h"
11
12 #include "GLES2/gl2.h"
13 #include "common/PackedEnums.h"
14 #include "common/PackedGLEnums_autogen.h"
15 #include "common/debug.h"
16 #include "common/log_utils.h"
17 #include "libANGLE/Error.h"
18 #include "libANGLE/ProgramExecutable.h"
19 #include "libANGLE/renderer/wgpu/ProgramExecutableWgpu.h"
20 #include "libANGLE/renderer/wgpu/wgpu_utils.h"
21 #include "libANGLE/renderer/wgpu/wgpu_wgsl_util.h"
22 #include "libANGLE/trace.h"
23
24 #include <dawn/webgpu_cpp.h>
25
26 namespace rx
27 {
28 namespace
29 {
30 const bool kOutputFinalSource = false;
31
32 // Identical to Std140 encoder in all aspects, except it ignores opaque uniform types.
33 class WgpuDefaultBlockEncoder : public sh::Std140BlockEncoder
34 {
35 public:
advanceOffset(GLenum type,const std::vector<unsigned int> & arraySizes,bool isRowMajorMatrix,int arrayStride,int matrixStride)36 void advanceOffset(GLenum type,
37 const std::vector<unsigned int> &arraySizes,
38 bool isRowMajorMatrix,
39 int arrayStride,
40 int matrixStride) override
41 {
42 if (gl::IsOpaqueType(type))
43 {
44 return;
45 }
46
47 sh::Std140BlockEncoder::advanceOffset(type, arraySizes, isRowMajorMatrix, arrayStride,
48 matrixStride);
49 }
50 };
51
InitDefaultUniformBlock(const std::vector<sh::ShaderVariable> & uniforms,sh::BlockLayoutMap * blockLayoutMapOut,size_t * blockSizeOut)52 void InitDefaultUniformBlock(const std::vector<sh::ShaderVariable> &uniforms,
53 sh::BlockLayoutMap *blockLayoutMapOut,
54 size_t *blockSizeOut)
55 {
56 if (uniforms.empty())
57 {
58 *blockSizeOut = 0;
59 return;
60 }
61
62 WgpuDefaultBlockEncoder blockEncoder;
63 sh::GetActiveUniformBlockInfo(uniforms, "", &blockEncoder, blockLayoutMapOut);
64
65 *blockSizeOut = blockEncoder.getCurrentOffset();
66 return;
67 }
68
69 class CreateWGPUShaderModuleTask : public LinkSubTask
70 {
71 public:
CreateWGPUShaderModuleTask(wgpu::Instance instance,wgpu::Device device,const gl::SharedCompiledShaderState & compiledShaderState,const gl::ProgramExecutable & executable,gl::ProgramMergedVaryings mergedVaryings,TranslatedWGPUShaderModule & resultShaderModule)72 CreateWGPUShaderModuleTask(wgpu::Instance instance,
73 wgpu::Device device,
74 const gl::SharedCompiledShaderState &compiledShaderState,
75 const gl::ProgramExecutable &executable,
76 gl::ProgramMergedVaryings mergedVaryings,
77 TranslatedWGPUShaderModule &resultShaderModule)
78 : mInstance(instance),
79 mDevice(device),
80 mCompiledShaderState(compiledShaderState),
81 mExecutable(executable),
82 mMergedVaryings(std::move(mergedVaryings)),
83 mShaderModule(resultShaderModule)
84 {}
85
getResult(const gl::Context * context,gl::InfoLog & infoLog)86 angle::Result getResult(const gl::Context *context, gl::InfoLog &infoLog) override
87 {
88 infoLog << mLog.str();
89 return mResult;
90 }
91
operator ()()92 void operator()() override
93 {
94 ANGLE_TRACE_EVENT0("gpu.angle", "CreateWGPUShaderModuleTask");
95
96 gl::ShaderType shaderType = mCompiledShaderState->shaderType;
97
98 ASSERT((mExecutable.getLinkedShaderStages() &
99 ~gl::ShaderBitSet({gl::ShaderType::Vertex, gl::ShaderType::Fragment}))
100 .none());
101 std::string finalShaderSource;
102 if (shaderType == gl::ShaderType::Vertex)
103 {
104 finalShaderSource = webgpu::WgslAssignLocations(mCompiledShaderState->translatedSource,
105 mExecutable.getProgramInputs(),
106 mMergedVaryings, shaderType);
107 }
108 else if (shaderType == gl::ShaderType::Fragment)
109 {
110 finalShaderSource = webgpu::WgslAssignLocations(mCompiledShaderState->translatedSource,
111 mExecutable.getOutputVariables(),
112 mMergedVaryings, shaderType);
113 }
114 else
115 {
116 UNIMPLEMENTED();
117 }
118 if (kOutputFinalSource)
119 {
120 std::cout << finalShaderSource;
121 }
122
123 wgpu::ShaderModuleWGSLDescriptor shaderModuleWGSLDescriptor;
124 shaderModuleWGSLDescriptor.code = finalShaderSource.c_str();
125
126 wgpu::ShaderModuleDescriptor shaderModuleDescriptor;
127 shaderModuleDescriptor.nextInChain = &shaderModuleWGSLDescriptor;
128
129 mShaderModule.module = mDevice.CreateShaderModule(&shaderModuleDescriptor);
130
131 wgpu::CompilationInfoCallback<CreateWGPUShaderModuleTask *> *getCompilationInfoCallback =
132 [](wgpu::CompilationInfoRequestStatus status,
133 wgpu::CompilationInfo const *compilationInfo, CreateWGPUShaderModuleTask *task) {
134 if (status != wgpu::CompilationInfoRequestStatus::Success)
135 {
136 task->mResult = angle::Result::Stop;
137 }
138
139 for (size_t msgIdx = 0; msgIdx < compilationInfo->messageCount; ++msgIdx)
140 {
141 const wgpu::CompilationMessage &message = compilationInfo->messages[msgIdx];
142 switch (message.type)
143 {
144 case wgpu::CompilationMessageType::Error:
145 task->mLog << "Error: ";
146 break;
147 case wgpu::CompilationMessageType::Warning:
148 task->mLog << "Warning: ";
149 break;
150 case wgpu::CompilationMessageType::Info:
151 task->mLog << "Info: ";
152 break;
153 default:
154 task->mLog << "Unknown: ";
155 break;
156 }
157 task->mLog << message.lineNum << ":" << message.linePos << ": "
158 << std::string(message.message) << std::endl;
159 }
160 };
161 wgpu::FutureWaitInfo waitInfo;
162 waitInfo.future = mShaderModule.module.GetCompilationInfo(wgpu::CallbackMode::WaitAnyOnly,
163 getCompilationInfoCallback, this);
164
165 wgpu::WaitStatus waitStatus = mInstance.WaitAny(1, &waitInfo, -1);
166 if (waitStatus != wgpu::WaitStatus::Success)
167 {
168 mResult = angle::Result::Stop;
169 }
170 }
171
172 private:
173 wgpu::Instance mInstance;
174 wgpu::Device mDevice;
175 gl::SharedCompiledShaderState mCompiledShaderState;
176 const gl::ProgramExecutable &mExecutable;
177 gl::ProgramMergedVaryings mMergedVaryings;
178
179 TranslatedWGPUShaderModule &mShaderModule;
180
181 std::ostringstream mLog;
182 angle::Result mResult = angle::Result::Continue;
183 };
184
185 class LinkTaskWgpu : public LinkTask
186 {
187 public:
LinkTaskWgpu(wgpu::Instance instance,wgpu::Device device,ProgramWgpu * program)188 LinkTaskWgpu(wgpu::Instance instance, wgpu::Device device, ProgramWgpu *program)
189 : mInstance(instance),
190 mDevice(device),
191 mProgram(program),
192 mExecutable(&mProgram->getState().getExecutable())
193 {}
194 ~LinkTaskWgpu() override = default;
195
link(const gl::ProgramLinkedResources & resources,const gl::ProgramMergedVaryings & mergedVaryings,std::vector<std::shared_ptr<LinkSubTask>> * linkSubTasksOut,std::vector<std::shared_ptr<LinkSubTask>> * postLinkSubTasksOut)196 void link(const gl::ProgramLinkedResources &resources,
197 const gl::ProgramMergedVaryings &mergedVaryings,
198 std::vector<std::shared_ptr<LinkSubTask>> *linkSubTasksOut,
199 std::vector<std::shared_ptr<LinkSubTask>> *postLinkSubTasksOut) override
200 {
201 ASSERT(linkSubTasksOut && linkSubTasksOut->empty());
202 ASSERT(postLinkSubTasksOut && postLinkSubTasksOut->empty());
203
204 ProgramExecutableWgpu *executable =
205 GetImplAs<ProgramExecutableWgpu>(&mProgram->getState().getExecutable());
206
207 const gl::ShaderMap<gl::SharedCompiledShaderState> &shaders =
208 mProgram->getState().getAttachedShaders();
209 for (gl::ShaderType shaderType : gl::AllShaderTypes())
210 {
211 if (shaders[shaderType])
212 {
213 auto task = std::make_shared<CreateWGPUShaderModuleTask>(
214 mInstance, mDevice, shaders[shaderType], *executable->getExecutable(),
215 mergedVaryings, executable->getShaderModule(shaderType));
216 linkSubTasksOut->push_back(task);
217 }
218 }
219
220 // The default uniform block's CPU buffer needs to be allocated and the layout calculated,
221 // now that the list of uniforms is known.
222 angle::Result initUniformBlocksResult = initDefaultUniformBlocks();
223 if (IsError(initUniformBlocksResult))
224 {
225 mLinkResult = initUniformBlocksResult;
226 return;
227 }
228
229 mLinkResult = angle::Result::Continue;
230 }
231
getResult(const gl::Context * context,gl::InfoLog & infoLog)232 angle::Result getResult(const gl::Context *context, gl::InfoLog &infoLog) override
233 {
234 return mLinkResult;
235 }
236
237 private:
initDefaultUniformBlocks()238 angle::Result initDefaultUniformBlocks()
239 {
240 ProgramExecutableWgpu *executableWgpu = webgpu::GetImpl(mExecutable);
241
242 // Process vertex and fragment uniforms into std140 packing.
243 gl::ShaderMap<sh::BlockLayoutMap> layoutMap;
244 gl::ShaderMap<size_t> requiredBufferSize;
245 requiredBufferSize.fill(0);
246
247 generateUniformLayoutMapping(&layoutMap, &requiredBufferSize);
248 initDefaultUniformLayoutMapping(&layoutMap);
249
250 // All uniform initializations are complete, now resize the buffers accordingly and return
251 ANGLE_TRY(executableWgpu->resizeUniformBlockMemory(requiredBufferSize));
252
253 executableWgpu->markDefaultUniformsDirty();
254
255 return angle::Result::Continue;
256 }
257
generateUniformLayoutMapping(gl::ShaderMap<sh::BlockLayoutMap> * layoutMapOut,gl::ShaderMap<size_t> * requiredBufferSizeOut)258 void generateUniformLayoutMapping(gl::ShaderMap<sh::BlockLayoutMap> *layoutMapOut,
259 gl::ShaderMap<size_t> *requiredBufferSizeOut)
260 {
261 for (const gl::ShaderType shaderType : mExecutable->getLinkedShaderStages())
262 {
263 const gl::SharedCompiledShaderState &shader =
264 mProgram->getState().getAttachedShader(shaderType);
265
266 if (shader)
267 {
268 const std::vector<sh::ShaderVariable> &uniforms = shader->uniforms;
269 InitDefaultUniformBlock(uniforms, &(*layoutMapOut)[shaderType],
270 &(*requiredBufferSizeOut)[shaderType]);
271 }
272 }
273 }
274
initDefaultUniformLayoutMapping(gl::ShaderMap<sh::BlockLayoutMap> * layoutMapOut)275 void initDefaultUniformLayoutMapping(gl::ShaderMap<sh::BlockLayoutMap> *layoutMapOut)
276 {
277 // Init the default block layout info.
278 ProgramExecutableWgpu *executableWgpu = webgpu::GetImpl(mExecutable);
279 const auto &uniforms = mExecutable->getUniforms();
280
281 for (const gl::VariableLocation &location : mExecutable->getUniformLocations())
282 {
283 gl::ShaderMap<sh::BlockMemberInfo> layoutInfo;
284
285 if (location.used() && !location.ignored)
286 {
287 const auto &uniform = uniforms[location.index];
288 if (uniform.isInDefaultBlock() && !uniform.isSampler() && !uniform.isImage() &&
289 !uniform.isFragmentInOut())
290 {
291 std::string uniformName = mExecutable->getUniformNameByIndex(location.index);
292 if (uniform.isArray())
293 {
294 // Gets the uniform name without the [0] at the end.
295 uniformName = gl::StripLastArrayIndex(uniformName);
296 ASSERT(uniformName.size() !=
297 mExecutable->getUniformNameByIndex(location.index).size());
298 }
299
300 bool found = false;
301
302 for (const gl::ShaderType shaderType : mExecutable->getLinkedShaderStages())
303 {
304 auto it = (*layoutMapOut)[shaderType].find(uniformName);
305 if (it != (*layoutMapOut)[shaderType].end())
306 {
307 found = true;
308 layoutInfo[shaderType] = it->second;
309 }
310 }
311
312 ASSERT(found);
313 }
314 }
315
316 for (const gl::ShaderType shaderType : mExecutable->getLinkedShaderStages())
317 {
318 executableWgpu->getSharedDefaultUniformBlock(shaderType)
319 ->uniformLayout.push_back(layoutInfo[shaderType]);
320 }
321 }
322 }
323
324 wgpu::Instance mInstance;
325 wgpu::Device mDevice;
326 ProgramWgpu *mProgram = nullptr;
327 const gl::ProgramExecutable *mExecutable;
328 angle::Result mLinkResult = angle::Result::Stop;
329 };
330 } // anonymous namespace
331
ProgramWgpu(const gl::ProgramState & state)332 ProgramWgpu::ProgramWgpu(const gl::ProgramState &state) : ProgramImpl(state) {}
333
~ProgramWgpu()334 ProgramWgpu::~ProgramWgpu() {}
335
load(const gl::Context * context,gl::BinaryInputStream * stream,std::shared_ptr<LinkTask> * loadTaskOut,egl::CacheGetResult * resultOut)336 angle::Result ProgramWgpu::load(const gl::Context *context,
337 gl::BinaryInputStream *stream,
338 std::shared_ptr<LinkTask> *loadTaskOut,
339 egl::CacheGetResult *resultOut)
340 {
341 *loadTaskOut = {};
342 *resultOut = egl::CacheGetResult::Success;
343 return angle::Result::Continue;
344 }
345
save(const gl::Context * context,gl::BinaryOutputStream * stream)346 void ProgramWgpu::save(const gl::Context *context, gl::BinaryOutputStream *stream) {}
347
setBinaryRetrievableHint(bool retrievable)348 void ProgramWgpu::setBinaryRetrievableHint(bool retrievable) {}
349
setSeparable(bool separable)350 void ProgramWgpu::setSeparable(bool separable) {}
351
link(const gl::Context * context,std::shared_ptr<LinkTask> * linkTaskOut)352 angle::Result ProgramWgpu::link(const gl::Context *context, std::shared_ptr<LinkTask> *linkTaskOut)
353 {
354 wgpu::Device device = webgpu::GetDevice(context);
355 wgpu::Instance instance = webgpu::GetInstance(context);
356
357 *linkTaskOut = std::shared_ptr<LinkTask>(new LinkTaskWgpu(instance, device, this));
358 return angle::Result::Continue;
359 }
360
validate(const gl::Caps & caps)361 GLboolean ProgramWgpu::validate(const gl::Caps &caps)
362 {
363 return GL_TRUE;
364 }
365
366 } // namespace rx
367