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::CompilationInfoCallbackInfo callbackInfo;
132 callbackInfo.mode = wgpu::CallbackMode::WaitAnyOnly;
133 callbackInfo.callback = [](WGPUCompilationInfoRequestStatus status,
134 struct WGPUCompilationInfo const *compilationInfo,
135 void *userdata) {
136 CreateWGPUShaderModuleTask *task = static_cast<CreateWGPUShaderModuleTask *>(userdata);
137
138 if (status != WGPUCompilationInfoRequestStatus_Success)
139 {
140 task->mResult = angle::Result::Stop;
141 }
142
143 for (size_t msgIdx = 0; msgIdx < compilationInfo->messageCount; ++msgIdx)
144 {
145 const WGPUCompilationMessage &message = compilationInfo->messages[msgIdx];
146 switch (message.type)
147 {
148 case WGPUCompilationMessageType_Error:
149 task->mLog << "Error: ";
150 break;
151 case WGPUCompilationMessageType_Warning:
152 task->mLog << "Warning: ";
153 break;
154 case WGPUCompilationMessageType_Info:
155 task->mLog << "Info: ";
156 break;
157 default:
158 task->mLog << "Unknown: ";
159 break;
160 }
161 task->mLog << message.lineNum << ":" << message.linePos << ": " << message.message
162 << std::endl;
163 }
164 };
165 callbackInfo.userdata = this;
166
167 wgpu::FutureWaitInfo waitInfo;
168 waitInfo.future = mShaderModule.module.GetCompilationInfo(callbackInfo);
169
170 wgpu::WaitStatus waitStatus = mInstance.WaitAny(1, &waitInfo, -1);
171 if (waitStatus != wgpu::WaitStatus::Success)
172 {
173 mResult = angle::Result::Stop;
174 }
175 }
176
177 private:
178 wgpu::Instance mInstance;
179 wgpu::Device mDevice;
180 gl::SharedCompiledShaderState mCompiledShaderState;
181 const gl::ProgramExecutable &mExecutable;
182 gl::ProgramMergedVaryings mMergedVaryings;
183
184 TranslatedWGPUShaderModule &mShaderModule;
185
186 std::ostringstream mLog;
187 angle::Result mResult = angle::Result::Continue;
188 };
189
190 class LinkTaskWgpu : public LinkTask
191 {
192 public:
LinkTaskWgpu(wgpu::Instance instance,wgpu::Device device,ProgramWgpu * program)193 LinkTaskWgpu(wgpu::Instance instance, wgpu::Device device, ProgramWgpu *program)
194 : mInstance(instance),
195 mDevice(device),
196 mProgram(program),
197 mExecutable(&mProgram->getState().getExecutable())
198 {}
199 ~LinkTaskWgpu() override = default;
200
link(const gl::ProgramLinkedResources & resources,const gl::ProgramMergedVaryings & mergedVaryings,std::vector<std::shared_ptr<LinkSubTask>> * linkSubTasksOut,std::vector<std::shared_ptr<LinkSubTask>> * postLinkSubTasksOut)201 void link(const gl::ProgramLinkedResources &resources,
202 const gl::ProgramMergedVaryings &mergedVaryings,
203 std::vector<std::shared_ptr<LinkSubTask>> *linkSubTasksOut,
204 std::vector<std::shared_ptr<LinkSubTask>> *postLinkSubTasksOut) override
205 {
206 ASSERT(linkSubTasksOut && linkSubTasksOut->empty());
207 ASSERT(postLinkSubTasksOut && postLinkSubTasksOut->empty());
208
209 ProgramExecutableWgpu *executable =
210 GetImplAs<ProgramExecutableWgpu>(&mProgram->getState().getExecutable());
211
212 const gl::ShaderMap<gl::SharedCompiledShaderState> &shaders =
213 mProgram->getState().getAttachedShaders();
214 for (gl::ShaderType shaderType : gl::AllShaderTypes())
215 {
216 if (shaders[shaderType])
217 {
218 auto task = std::make_shared<CreateWGPUShaderModuleTask>(
219 mInstance, mDevice, shaders[shaderType], *executable->getExecutable(),
220 mergedVaryings, executable->getShaderModule(shaderType));
221 linkSubTasksOut->push_back(task);
222 }
223 }
224
225 // The default uniform block's CPU buffer needs to be allocated and the layout calculated,
226 // now that the list of uniforms is known.
227 angle::Result initUniformBlocksResult = initDefaultUniformBlocks();
228 if (IsError(initUniformBlocksResult))
229 {
230 mLinkResult = initUniformBlocksResult;
231 return;
232 }
233
234 mLinkResult = angle::Result::Continue;
235 }
236
getResult(const gl::Context * context,gl::InfoLog & infoLog)237 angle::Result getResult(const gl::Context *context, gl::InfoLog &infoLog) override
238 {
239 return mLinkResult;
240 }
241
242 private:
initDefaultUniformBlocks()243 angle::Result initDefaultUniformBlocks()
244 {
245 ProgramExecutableWgpu *executableWgpu = webgpu::GetImpl(mExecutable);
246
247 // Process vertex and fragment uniforms into std140 packing.
248 gl::ShaderMap<sh::BlockLayoutMap> layoutMap;
249 gl::ShaderMap<size_t> requiredBufferSize;
250 requiredBufferSize.fill(0);
251
252 generateUniformLayoutMapping(&layoutMap, &requiredBufferSize);
253 initDefaultUniformLayoutMapping(&layoutMap);
254
255 // All uniform initializations are complete, now resize the buffers accordingly and return
256 ANGLE_TRY(executableWgpu->resizeUniformBlockMemory(requiredBufferSize));
257
258 executableWgpu->markDefaultUniformsDirty();
259
260 return angle::Result::Continue;
261 }
262
generateUniformLayoutMapping(gl::ShaderMap<sh::BlockLayoutMap> * layoutMapOut,gl::ShaderMap<size_t> * requiredBufferSizeOut)263 void generateUniformLayoutMapping(gl::ShaderMap<sh::BlockLayoutMap> *layoutMapOut,
264 gl::ShaderMap<size_t> *requiredBufferSizeOut)
265 {
266 for (const gl::ShaderType shaderType : mExecutable->getLinkedShaderStages())
267 {
268 const gl::SharedCompiledShaderState &shader =
269 mProgram->getState().getAttachedShader(shaderType);
270
271 if (shader)
272 {
273 const std::vector<sh::ShaderVariable> &uniforms = shader->uniforms;
274 InitDefaultUniformBlock(uniforms, &(*layoutMapOut)[shaderType],
275 &(*requiredBufferSizeOut)[shaderType]);
276 }
277 }
278 }
279
initDefaultUniformLayoutMapping(gl::ShaderMap<sh::BlockLayoutMap> * layoutMapOut)280 void initDefaultUniformLayoutMapping(gl::ShaderMap<sh::BlockLayoutMap> *layoutMapOut)
281 {
282 // Init the default block layout info.
283 ProgramExecutableWgpu *executableWgpu = webgpu::GetImpl(mExecutable);
284 const auto &uniforms = mExecutable->getUniforms();
285
286 for (const gl::VariableLocation &location : mExecutable->getUniformLocations())
287 {
288 gl::ShaderMap<sh::BlockMemberInfo> layoutInfo;
289
290 if (location.used() && !location.ignored)
291 {
292 const auto &uniform = uniforms[location.index];
293 if (uniform.isInDefaultBlock() && !uniform.isSampler() && !uniform.isImage() &&
294 !uniform.isFragmentInOut())
295 {
296 std::string uniformName = mExecutable->getUniformNameByIndex(location.index);
297 if (uniform.isArray())
298 {
299 // Gets the uniform name without the [0] at the end.
300 uniformName = gl::StripLastArrayIndex(uniformName);
301 ASSERT(uniformName.size() !=
302 mExecutable->getUniformNameByIndex(location.index).size());
303 }
304
305 bool found = false;
306
307 for (const gl::ShaderType shaderType : mExecutable->getLinkedShaderStages())
308 {
309 auto it = (*layoutMapOut)[shaderType].find(uniformName);
310 if (it != (*layoutMapOut)[shaderType].end())
311 {
312 found = true;
313 layoutInfo[shaderType] = it->second;
314 }
315 }
316
317 ASSERT(found);
318 }
319 }
320
321 for (const gl::ShaderType shaderType : mExecutable->getLinkedShaderStages())
322 {
323 executableWgpu->getSharedDefaultUniformBlock(shaderType)
324 ->uniformLayout.push_back(layoutInfo[shaderType]);
325 }
326 }
327 }
328
329 wgpu::Instance mInstance;
330 wgpu::Device mDevice;
331 ProgramWgpu *mProgram = nullptr;
332 const gl::ProgramExecutable *mExecutable;
333 angle::Result mLinkResult = angle::Result::Stop;
334 };
335 } // anonymous namespace
336
ProgramWgpu(const gl::ProgramState & state)337 ProgramWgpu::ProgramWgpu(const gl::ProgramState &state) : ProgramImpl(state) {}
338
~ProgramWgpu()339 ProgramWgpu::~ProgramWgpu() {}
340
load(const gl::Context * context,gl::BinaryInputStream * stream,std::shared_ptr<LinkTask> * loadTaskOut,egl::CacheGetResult * resultOut)341 angle::Result ProgramWgpu::load(const gl::Context *context,
342 gl::BinaryInputStream *stream,
343 std::shared_ptr<LinkTask> *loadTaskOut,
344 egl::CacheGetResult *resultOut)
345 {
346 *loadTaskOut = {};
347 *resultOut = egl::CacheGetResult::Success;
348 return angle::Result::Continue;
349 }
350
save(const gl::Context * context,gl::BinaryOutputStream * stream)351 void ProgramWgpu::save(const gl::Context *context, gl::BinaryOutputStream *stream) {}
352
setBinaryRetrievableHint(bool retrievable)353 void ProgramWgpu::setBinaryRetrievableHint(bool retrievable) {}
354
setSeparable(bool separable)355 void ProgramWgpu::setSeparable(bool separable) {}
356
link(const gl::Context * context,std::shared_ptr<LinkTask> * linkTaskOut)357 angle::Result ProgramWgpu::link(const gl::Context *context, std::shared_ptr<LinkTask> *linkTaskOut)
358 {
359 wgpu::Device device = webgpu::GetDevice(context);
360 wgpu::Instance instance = webgpu::GetInstance(context);
361
362 *linkTaskOut = std::shared_ptr<LinkTask>(new LinkTaskWgpu(instance, device, this));
363 return angle::Result::Continue;
364 }
365
validate(const gl::Caps & caps)366 GLboolean ProgramWgpu::validate(const gl::Caps &caps)
367 {
368 return GL_TRUE;
369 }
370
371 } // namespace rx
372