• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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