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