• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2023 Google LLC
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "src/gpu/graphite/dawn/DawnComputePipeline.h"
9 
10 #include "include/gpu/GpuTypes.h"
11 #include "src/gpu/SkSLToBackend.h"
12 #include "src/gpu/graphite/Caps.h"
13 #include "src/gpu/graphite/ComputePipelineDesc.h"
14 #include "src/gpu/graphite/ContextUtils.h"
15 #include "src/gpu/graphite/TextureInfoPriv.h"
16 #include "src/gpu/graphite/dawn/DawnAsyncWait.h"
17 #include "src/gpu/graphite/dawn/DawnErrorChecker.h"
18 #include "src/gpu/graphite/dawn/DawnGraphiteUtils.h"
19 #include "src/gpu/graphite/dawn/DawnSharedContext.h"
20 #include "src/sksl/SkSLProgramSettings.h"
21 
22 namespace skgpu::graphite {
23 namespace {
24 
25 struct ShaderInfo {
26     wgpu::ShaderModule fModule;
27     std::string fEntryPoint;
28 
isValidskgpu::graphite::__anonf1508eda0111::ShaderInfo29     bool isValid() const { return static_cast<bool>(fModule); }
30 };
31 
compile_shader_module(const DawnSharedContext * sharedContext,const ComputePipelineDesc & pipelineDesc)32 static ShaderInfo compile_shader_module(const DawnSharedContext* sharedContext,
33                                         const ComputePipelineDesc& pipelineDesc) {
34     SkASSERT(sharedContext);
35 
36     ShaderInfo info;
37 
38     const Caps* caps = sharedContext->caps();
39     const ComputeStep* step = pipelineDesc.computeStep();
40     ShaderErrorHandler* errorHandler = caps->shaderErrorHandler();
41 
42     if (step->supportsNativeShader()) {
43         auto nativeShader = step->nativeShaderSource(ComputeStep::NativeShaderFormat::kWGSL);
44         if (!DawnCompileWGSLShaderModule(sharedContext,
45                                          step->name(),
46                                          std::string(nativeShader.fSource),
47                                          &info.fModule,
48                                          errorHandler)) {
49             return {};
50         }
51         info.fEntryPoint = std::move(nativeShader.fEntryPoint);
52     } else {
53         std::string wgsl;
54         SkSL::Program::Interface interface;
55         SkSL::ProgramSettings settings;
56 
57         std::string sksl = BuildComputeSkSL(caps, step, BackendApi::kDawn);
58         if (skgpu::SkSLToWGSL(caps->shaderCaps(),
59                               sksl,
60                               SkSL::ProgramKind::kCompute,
61                               settings,
62                               &wgsl,
63                               &interface,
64                               errorHandler)) {
65             if (!DawnCompileWGSLShaderModule(sharedContext, step->name(), wgsl,
66                                              &info.fModule, errorHandler)) {
67                 return {};
68             }
69             info.fEntryPoint = "main";
70         }
71     }
72 
73     return info;
74 }
75 
76 }  // namespace
77 
Make(const DawnSharedContext * sharedContext,const ComputePipelineDesc & pipelineDesc)78 sk_sp<DawnComputePipeline> DawnComputePipeline::Make(const DawnSharedContext* sharedContext,
79                                                      const ComputePipelineDesc& pipelineDesc) {
80     auto [shaderModule, entryPointName] = compile_shader_module(sharedContext, pipelineDesc);
81     if (!shaderModule) {
82         return nullptr;
83     }
84 
85     const ComputeStep* step = pipelineDesc.computeStep();
86 
87     // ComputeStep resources are listed in the order that they must be declared in the shader. This
88     // order is then used for the index assignment using an "indexed by order" policy that has
89     // backend-specific semantics. The semantics on Dawn is to assign the index number in increasing
90     // order.
91     //
92     // For compute pipelines, all resources get assigned to a single bind group at index 0 (ignoring
93     // bind group indices assigned in by DawnCaps's ResourceBindingRequirements, which are for
94     // non-compute shaders).
95     std::vector<wgpu::BindGroupLayoutEntry> bindGroupLayoutEntries;
96     auto resources = step->resources();
97 
98     // Sampled textures count as 2 resources (1 texture and 1 sampler). All other types count as 1.
99     size_t resourceCount = 0;
100     for (const ComputeStep::ResourceDesc& r : resources) {
101         resourceCount++;
102         if (r.fType == ComputeStep::ResourceType::kSampledTexture) {
103             resourceCount++;
104         }
105     }
106 
107     bindGroupLayoutEntries.reserve(resourceCount);
108     int declarationIndex = 0;
109     for (const ComputeStep::ResourceDesc& r : resources) {
110         bindGroupLayoutEntries.emplace_back();
111         uint32_t bindingIndex = bindGroupLayoutEntries.size() - 1;
112 
113         wgpu::BindGroupLayoutEntry& entry = bindGroupLayoutEntries.back();
114         entry.binding = bindingIndex;
115         entry.visibility = wgpu::ShaderStage::Compute;
116         switch (r.fType) {
117             case ComputeStep::ResourceType::kUniformBuffer:
118                 entry.buffer.type = wgpu::BufferBindingType::Uniform;
119                 break;
120             case ComputeStep::ResourceType::kStorageBuffer:
121             case ComputeStep::ResourceType::kIndirectBuffer:
122                 entry.buffer.type = wgpu::BufferBindingType::Storage;
123                 break;
124             case ComputeStep::ResourceType::kReadOnlyStorageBuffer:
125                 entry.buffer.type = wgpu::BufferBindingType::ReadOnlyStorage;
126                 break;
127             case ComputeStep::ResourceType::kReadOnlyTexture:
128                 entry.texture.sampleType = wgpu::TextureSampleType::Float;
129                 entry.texture.viewDimension = wgpu::TextureViewDimension::e2D;
130                 break;
131             case ComputeStep::ResourceType::kWriteOnlyStorageTexture: {
132                 entry.storageTexture.access = wgpu::StorageTextureAccess::WriteOnly;
133                 entry.storageTexture.viewDimension = wgpu::TextureViewDimension::e2D;
134 
135                 auto [_, colorType] = step->calculateTextureParameters(declarationIndex, r);
136                 auto textureInfo = sharedContext->caps()->getDefaultStorageTextureInfo(colorType);
137                 entry.storageTexture.format =
138                         TextureInfoPriv::Get<DawnTextureInfo>(textureInfo).getViewFormat();
139                 break;
140             }
141             case ComputeStep::ResourceType::kSampledTexture: {
142                 entry.sampler.type = wgpu::SamplerBindingType::Filtering;
143 
144                 // Add an additional entry for the texture.
145                 bindGroupLayoutEntries.emplace_back();
146                 wgpu::BindGroupLayoutEntry& texEntry = bindGroupLayoutEntries.back();
147                 texEntry.binding = bindingIndex + 1;
148                 texEntry.visibility = wgpu::ShaderStage::Compute;
149                 texEntry.texture.sampleType = wgpu::TextureSampleType::Float;
150                 texEntry.texture.viewDimension = wgpu::TextureViewDimension::e2D;
151                 break;
152             }
153         }
154         declarationIndex++;
155     }
156 
157     const wgpu::Device& device = sharedContext->device();
158 
159     // All resources of a ComputeStep currently get assigned to a single bind group at index 0.
160     wgpu::BindGroupLayoutDescriptor bindGroupLayoutDesc;
161     bindGroupLayoutDesc.entryCount = bindGroupLayoutEntries.size();
162     bindGroupLayoutDesc.entries = bindGroupLayoutEntries.data();
163     wgpu::BindGroupLayout bindGroupLayout = device.CreateBindGroupLayout(&bindGroupLayoutDesc);
164     if (!bindGroupLayout) {
165         return nullptr;
166     }
167 
168     wgpu::PipelineLayoutDescriptor pipelineLayoutDesc;
169     if (sharedContext->caps()->setBackendLabels()) {
170         pipelineLayoutDesc.label = step->name();
171     }
172     pipelineLayoutDesc.bindGroupLayoutCount = 1;
173     pipelineLayoutDesc.bindGroupLayouts = &bindGroupLayout;
174     wgpu::PipelineLayout layout = device.CreatePipelineLayout(&pipelineLayoutDesc);
175     if (!layout) {
176         return nullptr;
177     }
178 
179     wgpu::ComputePipelineDescriptor descriptor;
180     // Always set the label for pipelines, dawn may need it for tracing.
181     descriptor.label = step->name();
182     descriptor.compute.module = std::move(shaderModule);
183     descriptor.compute.entryPoint = entryPointName.c_str();
184     descriptor.layout = std::move(layout);
185 
186     std::optional<DawnErrorChecker> errorChecker;
187     if (sharedContext->dawnCaps()->allowScopedErrorChecks()) {
188         errorChecker.emplace(sharedContext);
189     }
190     wgpu::ComputePipeline pipeline = device.CreateComputePipeline(&descriptor);
191     SkASSERT(pipeline);
192     if (errorChecker.has_value() && errorChecker->popErrorScopes() != DawnErrorType::kNoError) {
193         return nullptr;
194     }
195 
196     return sk_sp<DawnComputePipeline>(new DawnComputePipeline(
197             sharedContext, std::move(pipeline), std::move(bindGroupLayout)));
198 }
199 
DawnComputePipeline(const SharedContext * sharedContext,wgpu::ComputePipeline pso,wgpu::BindGroupLayout groupLayout)200 DawnComputePipeline::DawnComputePipeline(const SharedContext* sharedContext,
201                                          wgpu::ComputePipeline pso,
202                                          wgpu::BindGroupLayout groupLayout)
203         : ComputePipeline(sharedContext)
204         , fPipeline(std::move(pso))
205         , fGroupLayout(std::move(groupLayout)) {}
206 
freeGpuData()207 void DawnComputePipeline::freeGpuData() { fPipeline = nullptr; }
208 
209 }  // namespace skgpu::graphite
210