xref: /aosp_15_r20/external/angle/src/libANGLE/renderer/wgpu/ProgramWgpu.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 // 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