1 /*
2 * Copyright 2022 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/DawnGraphicsPipeline.h"
9
10 #include "include/gpu/graphite/TextureInfo.h"
11 #include "src/gpu/graphite/Attribute.h"
12 #include "src/gpu/graphite/ContextUtils.h"
13 #include "src/gpu/graphite/GraphicsPipelineDesc.h"
14 #include "src/gpu/graphite/Log.h"
15 #include "src/gpu/graphite/RendererProvider.h"
16 #include "src/gpu/graphite/UniformManager.h"
17 #include "src/gpu/graphite/dawn/DawnResourceProvider.h"
18 #include "src/gpu/graphite/dawn/DawnSharedContext.h"
19 #include "src/gpu/graphite/dawn/DawnUtilsPriv.h"
20 #include "src/sksl/SkSLProgramSettings.h"
21 #include "src/sksl/ir/SkSLProgram.h"
22
23 #include <vector>
24
25 namespace skgpu::graphite {
26
27 namespace {
28
attribute_type_to_dawn(VertexAttribType type)29 inline wgpu::VertexFormat attribute_type_to_dawn(VertexAttribType type) {
30 switch (type) {
31 case VertexAttribType::kFloat:
32 return wgpu::VertexFormat::Float32;
33 case VertexAttribType::kFloat2:
34 return wgpu::VertexFormat::Float32x2;
35 case VertexAttribType::kFloat3:
36 return wgpu::VertexFormat::Float32x3;
37 case VertexAttribType::kFloat4:
38 return wgpu::VertexFormat::Float32x4;
39 case VertexAttribType::kHalf:
40 return wgpu::VertexFormat::Undefined;
41 case VertexAttribType::kHalf2:
42 return wgpu::VertexFormat::Float16x2;
43 case VertexAttribType::kHalf4:
44 return wgpu::VertexFormat::Float16x4;
45 case VertexAttribType::kInt2:
46 return wgpu::VertexFormat::Sint32x2;
47 case VertexAttribType::kInt3:
48 return wgpu::VertexFormat::Sint32x3;
49 case VertexAttribType::kInt4:
50 return wgpu::VertexFormat::Sint32x4;
51 case VertexAttribType::kByte:
52 return wgpu::VertexFormat::Undefined;
53 case VertexAttribType::kByte2:
54 return wgpu::VertexFormat::Sint8x2;
55 case VertexAttribType::kByte4:
56 return wgpu::VertexFormat::Sint8x4;
57 case VertexAttribType::kUByte:
58 return wgpu::VertexFormat::Undefined;
59 case VertexAttribType::kUByte2:
60 return wgpu::VertexFormat::Uint8x2;
61 case VertexAttribType::kUByte4:
62 return wgpu::VertexFormat::Uint8x4;
63 case VertexAttribType::kUByte_norm:
64 return wgpu::VertexFormat::Undefined;
65 case VertexAttribType::kUByte4_norm:
66 return wgpu::VertexFormat::Unorm8x4;
67 case VertexAttribType::kShort2:
68 return wgpu::VertexFormat::Sint16x2;
69 case VertexAttribType::kShort4:
70 return wgpu::VertexFormat::Sint16x4;
71 case VertexAttribType::kUShort2:
72 return wgpu::VertexFormat::Uint16x2;
73 case VertexAttribType::kUShort2_norm:
74 return wgpu::VertexFormat::Unorm16x2;
75 case VertexAttribType::kInt:
76 return wgpu::VertexFormat::Sint32;
77 case VertexAttribType::kUInt:
78 return wgpu::VertexFormat::Uint32;
79 case VertexAttribType::kUShort_norm:
80 return wgpu::VertexFormat::Undefined;
81 case VertexAttribType::kUShort4_norm:
82 return wgpu::VertexFormat::Unorm16x4;
83 }
84 SkUNREACHABLE;
85 }
86
compare_op_to_dawn(CompareOp op)87 wgpu::CompareFunction compare_op_to_dawn(CompareOp op) {
88 switch (op) {
89 case CompareOp::kAlways:
90 return wgpu::CompareFunction::Always;
91 case CompareOp::kNever:
92 return wgpu::CompareFunction::Never;
93 case CompareOp::kGreater:
94 return wgpu::CompareFunction::Greater;
95 case CompareOp::kGEqual:
96 return wgpu::CompareFunction::GreaterEqual;
97 case CompareOp::kLess:
98 return wgpu::CompareFunction::Less;
99 case CompareOp::kLEqual:
100 return wgpu::CompareFunction::LessEqual;
101 case CompareOp::kEqual:
102 return wgpu::CompareFunction::Equal;
103 case CompareOp::kNotEqual:
104 return wgpu::CompareFunction::NotEqual;
105 }
106 SkUNREACHABLE;
107 }
108
stencil_op_to_dawn(StencilOp op)109 wgpu::StencilOperation stencil_op_to_dawn(StencilOp op) {
110 switch (op) {
111 case StencilOp::kKeep:
112 return wgpu::StencilOperation::Keep;
113 case StencilOp::kZero:
114 return wgpu::StencilOperation::Zero;
115 case StencilOp::kReplace:
116 return wgpu::StencilOperation::Replace;
117 case StencilOp::kInvert:
118 return wgpu::StencilOperation::Invert;
119 case StencilOp::kIncWrap:
120 return wgpu::StencilOperation::IncrementWrap;
121 case StencilOp::kDecWrap:
122 return wgpu::StencilOperation::DecrementWrap;
123 case StencilOp::kIncClamp:
124 return wgpu::StencilOperation::IncrementClamp;
125 case StencilOp::kDecClamp:
126 return wgpu::StencilOperation::DecrementClamp;
127 }
128 SkUNREACHABLE;
129 }
130
stencil_face_to_dawn(DepthStencilSettings::Face face)131 wgpu::StencilFaceState stencil_face_to_dawn(DepthStencilSettings::Face face) {
132 wgpu::StencilFaceState state;
133 state.compare = compare_op_to_dawn(face.fCompareOp);
134 state.failOp = stencil_op_to_dawn(face.fStencilFailOp);
135 state.depthFailOp = stencil_op_to_dawn(face.fDepthFailOp);
136 state.passOp = stencil_op_to_dawn(face.fDepthStencilPassOp);
137 return state;
138 }
139
create_vertex_attributes(SkSpan<const Attribute> attrs,int shaderLocationOffset,std::vector<wgpu::VertexAttribute> * out)140 size_t create_vertex_attributes(SkSpan<const Attribute> attrs,
141 int shaderLocationOffset,
142 std::vector<wgpu::VertexAttribute>* out) {
143 SkASSERT(out && out->empty());
144 out->resize(attrs.size());
145 size_t vertexAttributeOffset = 0;
146 int attributeIndex = 0;
147 for (const auto& attr : attrs) {
148 wgpu::VertexAttribute& vertexAttribute = (*out)[attributeIndex];
149 vertexAttribute.format = attribute_type_to_dawn(attr.cpuType());
150 SkASSERT(vertexAttribute.format != wgpu::VertexFormat::Undefined);
151 vertexAttribute.offset = vertexAttributeOffset;
152 vertexAttribute.shaderLocation = shaderLocationOffset + attributeIndex;
153 vertexAttributeOffset += attr.sizeAlign4();
154 attributeIndex++;
155 }
156 return vertexAttributeOffset;
157 }
158
159 // TODO: share this w/ Ganesh dawn backend?
blend_coeff_to_dawn_blend(skgpu::BlendCoeff coeff)160 static wgpu::BlendFactor blend_coeff_to_dawn_blend(skgpu::BlendCoeff coeff) {
161 switch (coeff) {
162 case skgpu::BlendCoeff::kZero:
163 return wgpu::BlendFactor::Zero;
164 case skgpu::BlendCoeff::kOne:
165 return wgpu::BlendFactor::One;
166 case skgpu::BlendCoeff::kSC:
167 return wgpu::BlendFactor::Src;
168 case skgpu::BlendCoeff::kISC:
169 return wgpu::BlendFactor::OneMinusSrc;
170 case skgpu::BlendCoeff::kDC:
171 return wgpu::BlendFactor::Dst;
172 case skgpu::BlendCoeff::kIDC:
173 return wgpu::BlendFactor::OneMinusDst;
174 case skgpu::BlendCoeff::kSA:
175 return wgpu::BlendFactor::SrcAlpha;
176 case skgpu::BlendCoeff::kISA:
177 return wgpu::BlendFactor::OneMinusSrcAlpha;
178 case skgpu::BlendCoeff::kDA:
179 return wgpu::BlendFactor::DstAlpha;
180 case skgpu::BlendCoeff::kIDA:
181 return wgpu::BlendFactor::OneMinusDstAlpha;
182 case skgpu::BlendCoeff::kConstC:
183 return wgpu::BlendFactor::Constant;
184 case skgpu::BlendCoeff::kIConstC:
185 return wgpu::BlendFactor::OneMinusConstant;
186 case skgpu::BlendCoeff::kS2C:
187 case skgpu::BlendCoeff::kIS2C:
188 case skgpu::BlendCoeff::kS2A:
189 case skgpu::BlendCoeff::kIS2A:
190 case skgpu::BlendCoeff::kIllegal:
191 return wgpu::BlendFactor::Zero;
192 }
193 SkUNREACHABLE;
194 }
195
196 // TODO: share this w/ Ganesh Metal backend?
blend_equation_to_dawn_blend_op(skgpu::BlendEquation equation)197 static wgpu::BlendOperation blend_equation_to_dawn_blend_op(skgpu::BlendEquation equation) {
198 static const wgpu::BlendOperation gTable[] = {
199 wgpu::BlendOperation::Add, // skgpu::BlendEquation::kAdd
200 wgpu::BlendOperation::Subtract, // skgpu::BlendEquation::kSubtract
201 wgpu::BlendOperation::ReverseSubtract, // skgpu::BlendEquation::kReverseSubtract
202 };
203 static_assert(std::size(gTable) == (int)skgpu::BlendEquation::kFirstAdvanced);
204 static_assert(0 == (int)skgpu::BlendEquation::kAdd);
205 static_assert(1 == (int)skgpu::BlendEquation::kSubtract);
206 static_assert(2 == (int)skgpu::BlendEquation::kReverseSubtract);
207
208 SkASSERT((unsigned)equation < skgpu::kBlendEquationCnt);
209 return gTable[(int)equation];
210 }
211
212 } // anonymous namespace
213
214 // static
Make(const DawnSharedContext * sharedContext,SkSL::Compiler * compiler,const RuntimeEffectDictionary * runtimeDict,const GraphicsPipelineDesc & pipelineDesc,const RenderPassDesc & renderPassDesc)215 sk_sp<DawnGraphicsPipeline> DawnGraphicsPipeline::Make(const DawnSharedContext* sharedContext,
216 SkSL::Compiler* compiler,
217 const RuntimeEffectDictionary* runtimeDict,
218 const GraphicsPipelineDesc& pipelineDesc,
219 const RenderPassDesc& renderPassDesc) {
220 const auto& device = sharedContext->device();
221
222 SkSL::Program::Inputs vsInputs, fsInputs;
223 SkSL::ProgramSettings settings;
224
225 settings.fForceNoRTFlip = true;
226 settings.fSPIRVDawnCompatMode = true;
227
228 ShaderErrorHandler* errorHandler = sharedContext->caps()->shaderErrorHandler();
229
230 const RenderStep* step =
231 sharedContext->rendererProvider()->lookup(pipelineDesc.renderStepID());
232
233 bool useShadingSsboIndex =
234 sharedContext->caps()->storageBufferPreferred() && step->performsShading();
235
236 std::string vsSPIRV, fsSPIRV;
237 wgpu::ShaderModule fsModule, vsModule;
238
239 // Some steps just render depth buffer but not color buffer, so the fragment
240 // shader is null.
241 const FragSkSLInfo fsSkSLInfo = GetSkSLFS(sharedContext->caps()->resourceBindingRequirements(),
242 sharedContext->shaderCodeDictionary(),
243 runtimeDict,
244 step,
245 pipelineDesc.paintParamsID(),
246 useShadingSsboIndex);
247 const std::string& fsSKSL = fsSkSLInfo.fSkSL;
248 const BlendInfo& blendInfo = fsSkSLInfo.fBlendInfo;
249 const bool localCoordsNeeded = fsSkSLInfo.fRequiresLocalCoords;
250 const int numTexturesAndSamplers = fsSkSLInfo.fNumTexturesAndSamplers;
251
252 bool hasFragment = !fsSKSL.empty();
253 if (hasFragment) {
254 if (!SkSLToSPIRV(compiler,
255 fsSKSL,
256 SkSL::ProgramKind::kGraphiteFragment,
257 settings,
258 &fsSPIRV,
259 &fsInputs,
260 errorHandler)) {
261 return {};
262 }
263 fsModule = DawnCompileSPIRVShaderModule(sharedContext,
264 fsSPIRV,
265 errorHandler);
266 if (!fsModule) {
267 return {};
268 }
269 }
270
271 if (!SkSLToSPIRV(compiler,
272 GetSkSLVS(sharedContext->caps()->resourceBindingRequirements(),
273 step,
274 useShadingSsboIndex,
275 localCoordsNeeded),
276 SkSL::ProgramKind::kGraphiteVertex,
277 settings,
278 &vsSPIRV,
279 &vsInputs,
280 errorHandler)) {
281 return {};
282 }
283
284 vsModule = DawnCompileSPIRVShaderModule(sharedContext, vsSPIRV, errorHandler);
285 if (!vsModule) {
286 return {};
287 }
288
289 wgpu::RenderPipelineDescriptor descriptor;
290 #if defined(SK_DEBUG)
291 descriptor.label = step->name();
292 #endif
293
294 // Fragment state
295 skgpu::BlendEquation equation = blendInfo.fEquation;
296 skgpu::BlendCoeff srcCoeff = blendInfo.fSrcBlend;
297 skgpu::BlendCoeff dstCoeff = blendInfo.fDstBlend;
298 bool blendOn = !skgpu::BlendShouldDisable(equation, srcCoeff, dstCoeff);
299
300 wgpu::BlendState blend;
301 if (blendOn) {
302 blend.color.operation = blend_equation_to_dawn_blend_op(equation);
303 blend.color.srcFactor = blend_coeff_to_dawn_blend(srcCoeff);
304 blend.color.dstFactor = blend_coeff_to_dawn_blend(dstCoeff);
305 blend.alpha.operation = blend_equation_to_dawn_blend_op(equation);
306 blend.alpha.srcFactor = blend_coeff_to_dawn_blend(srcCoeff);
307 blend.alpha.dstFactor = blend_coeff_to_dawn_blend(dstCoeff);
308 }
309
310 wgpu::ColorTargetState colorTarget;
311 colorTarget.format = renderPassDesc.fColorAttachment.fTextureInfo.dawnTextureSpec().fFormat;
312 colorTarget.blend = blendOn ? &blend : nullptr;
313 colorTarget.writeMask = blendInfo.fWritesColor && hasFragment ? wgpu::ColorWriteMask::All
314 : wgpu::ColorWriteMask::None;
315
316 wgpu::FragmentState fragment;
317 // Dawn doesn't allow having a color attachment but without fragment shader, so have to use a
318 // noop fragment shader, if fragment shader is null.
319 fragment.module = hasFragment ? std::move(fsModule) : sharedContext->noopFragment();
320 fragment.entryPoint = "main";
321 fragment.targetCount = 1;
322 fragment.targets = &colorTarget;
323 descriptor.fragment = &fragment;
324
325 // Depth stencil state
326 const auto& depthStencilSettings = step->depthStencilSettings();
327 SkASSERT(depthStencilSettings.fDepthTestEnabled ||
328 depthStencilSettings.fDepthCompareOp == CompareOp::kAlways);
329 wgpu::DepthStencilState depthStencil;
330 if (renderPassDesc.fDepthStencilAttachment.fTextureInfo.isValid()) {
331 wgpu::TextureFormat dsFormat =
332 renderPassDesc.fDepthStencilAttachment.fTextureInfo.dawnTextureSpec().fFormat;
333 depthStencil.format =
334 DawnFormatIsDepthOrStencil(dsFormat) ? dsFormat : wgpu::TextureFormat::Undefined;
335 if (depthStencilSettings.fDepthTestEnabled) {
336 depthStencil.depthWriteEnabled = depthStencilSettings.fDepthWriteEnabled;
337 }
338 depthStencil.depthCompare = compare_op_to_dawn(depthStencilSettings.fDepthCompareOp);
339 depthStencil.stencilFront = stencil_face_to_dawn(depthStencilSettings.fFrontStencil);
340 depthStencil.stencilBack = stencil_face_to_dawn(depthStencilSettings.fBackStencil);
341 depthStencil.stencilReadMask = depthStencilSettings.fFrontStencil.fReadMask;
342 depthStencil.stencilWriteMask = depthStencilSettings.fFrontStencil.fWriteMask;
343
344 descriptor.depthStencil = &depthStencil;
345 }
346
347 // Pipeline layout
348 {
349 std::array<wgpu::BindGroupLayout, 2> groupLayouts;
350 {
351 std::array<wgpu::BindGroupLayoutEntry, 3> entries;
352 entries[0].binding = kIntrinsicUniformBufferIndex;
353 entries[0].visibility = wgpu::ShaderStage::Vertex | wgpu::ShaderStage::Fragment;
354 entries[0].buffer.type = wgpu::BufferBindingType::Uniform;
355 entries[0].buffer.hasDynamicOffset = false;
356 entries[0].buffer.minBindingSize = 0;
357
358 uint32_t numBuffers = 1;
359
360 if (!step->uniforms().empty()) {
361 entries[numBuffers].binding = kRenderStepUniformBufferIndex;
362 entries[numBuffers].visibility =
363 wgpu::ShaderStage::Vertex | wgpu::ShaderStage::Fragment;
364 entries[numBuffers].buffer.type = wgpu::BufferBindingType::Uniform;
365 entries[numBuffers].buffer.hasDynamicOffset = false;
366 entries[numBuffers].buffer.minBindingSize = 0;
367 ++numBuffers;
368 }
369
370 if (hasFragment) {
371 entries[numBuffers].binding = kPaintUniformBufferIndex;
372 entries[numBuffers].visibility = wgpu::ShaderStage::Fragment;
373 entries[numBuffers].buffer.type = wgpu::BufferBindingType::Uniform;
374 entries[numBuffers].buffer.hasDynamicOffset = false;
375 entries[numBuffers].buffer.minBindingSize = 0;
376 ++numBuffers;
377 }
378
379 wgpu::BindGroupLayoutDescriptor groupLayoutDesc;
380 #if defined(SK_DEBUG)
381 groupLayoutDesc.label = step->name();
382 #endif
383
384 groupLayoutDesc.entryCount = numBuffers;
385 groupLayoutDesc.entries = entries.data();
386 groupLayouts[0] = device.CreateBindGroupLayout(&groupLayoutDesc);
387 if (!groupLayouts[0]) {
388 return {};
389 }
390 }
391
392 bool hasFragmentSamplers = hasFragment && numTexturesAndSamplers > 0;
393 if (hasFragmentSamplers) {
394 std::vector<wgpu::BindGroupLayoutEntry> entries(numTexturesAndSamplers);
395 for (int i = 0; i < numTexturesAndSamplers;) {
396 entries[i].binding = static_cast<uint32_t>(i);
397 entries[i].visibility = wgpu::ShaderStage::Fragment;
398 entries[i].sampler.type = wgpu::SamplerBindingType::Filtering;
399 ++i;
400 entries[i].binding = i;
401 entries[i].visibility = wgpu::ShaderStage::Fragment;
402 entries[i].texture.sampleType = wgpu::TextureSampleType::Float;
403 entries[i].texture.viewDimension = wgpu::TextureViewDimension::e2D;
404 entries[i].texture.multisampled = false;
405 ++i;
406 }
407
408 wgpu::BindGroupLayoutDescriptor groupLayoutDesc;
409 #if defined(SK_DEBUG)
410 groupLayoutDesc.label = step->name();
411 #endif
412 groupLayoutDesc.entryCount = entries.size();
413 groupLayoutDesc.entries = entries.data();
414 groupLayouts[1] = device.CreateBindGroupLayout(&groupLayoutDesc);
415 if (!groupLayouts[1]) {
416 return {};
417 }
418 }
419
420 wgpu::PipelineLayoutDescriptor layoutDesc;
421 #if defined(SK_DEBUG)
422 layoutDesc.label = step->name();
423 #endif
424 layoutDesc.bindGroupLayoutCount =
425 hasFragmentSamplers ? groupLayouts.size() : groupLayouts.size() - 1;
426 layoutDesc.bindGroupLayouts = groupLayouts.data();
427 auto layout = device.CreatePipelineLayout(&layoutDesc);
428 if (!layout) {
429 return {};
430 }
431 descriptor.layout = std::move(layout);
432 }
433
434 // Vertex state
435 std::array<wgpu::VertexBufferLayout, kNumVertexBuffers> vertexBufferLayouts;
436 // Vertex buffer layout
437 std::vector<wgpu::VertexAttribute> vertexAttributes;
438 {
439 auto arrayStride = create_vertex_attributes(step->vertexAttributes(),
440 0,
441 &vertexAttributes);
442 auto& layout = vertexBufferLayouts[kVertexBufferIndex];
443 if (arrayStride) {
444 layout.arrayStride = arrayStride;
445 layout.stepMode = wgpu::VertexStepMode::Vertex;
446 layout.attributeCount = vertexAttributes.size();
447 layout.attributes = vertexAttributes.data();
448 } else {
449 layout.arrayStride = 0;
450 layout.stepMode = wgpu::VertexStepMode::VertexBufferNotUsed;
451 layout.attributeCount = 0;
452 layout.attributes = nullptr;
453 }
454 }
455
456 // Instance buffer layout
457 std::vector<wgpu::VertexAttribute> instanceAttributes;
458 {
459 auto arrayStride = create_vertex_attributes(step->instanceAttributes(),
460 step->vertexAttributes().size(),
461 &instanceAttributes);
462 auto& layout = vertexBufferLayouts[kInstanceBufferIndex];
463 if (arrayStride) {
464 layout.arrayStride = arrayStride;
465 layout.stepMode = wgpu::VertexStepMode::Instance;
466 layout.attributeCount = instanceAttributes.size();
467 layout.attributes = instanceAttributes.data();
468 } else {
469 layout.arrayStride = 0;
470 layout.stepMode = wgpu::VertexStepMode::VertexBufferNotUsed;
471 layout.attributeCount = 0;
472 layout.attributes = nullptr;
473 }
474 }
475
476 auto& vertex = descriptor.vertex;
477 vertex.module = std::move(vsModule);
478 vertex.entryPoint = "main";
479 vertex.constantCount = 0;
480 vertex.constants = nullptr;
481 vertex.bufferCount = vertexBufferLayouts.size();
482 vertex.buffers = vertexBufferLayouts.data();
483
484 // Other state
485 descriptor.primitive.frontFace = wgpu::FrontFace::CCW;
486 descriptor.primitive.cullMode = wgpu::CullMode::None;
487 switch(step->primitiveType()) {
488 case PrimitiveType::kTriangles:
489 descriptor.primitive.topology = wgpu::PrimitiveTopology::TriangleList;
490 break;
491 case PrimitiveType::kTriangleStrip:
492 descriptor.primitive.topology = wgpu::PrimitiveTopology::TriangleStrip;
493 break;
494 case PrimitiveType::kPoints:
495 descriptor.primitive.topology = wgpu::PrimitiveTopology::PointList;
496 break;
497 }
498 descriptor.primitive.stripIndexFormat = wgpu::IndexFormat::Uint16;
499
500 descriptor.multisample.count = renderPassDesc.fColorAttachment.fTextureInfo.numSamples();
501 descriptor.multisample.mask = 0xFFFFFFFF;
502 descriptor.multisample.alphaToCoverageEnabled = false;
503
504 auto pipeline = device.CreateRenderPipeline(&descriptor);
505 if (!pipeline) {
506 return {};
507 }
508
509 return sk_sp<DawnGraphicsPipeline>(
510 new DawnGraphicsPipeline(sharedContext,
511 std::move(pipeline),
512 step->primitiveType(),
513 depthStencilSettings.fStencilReferenceValue,
514 !step->uniforms().empty(),
515 hasFragment));
516 }
517
freeGpuData()518 void DawnGraphicsPipeline::freeGpuData() {
519 fRenderPipeline = nullptr;
520 }
521
dawnRenderPipeline() const522 const wgpu::RenderPipeline& DawnGraphicsPipeline::dawnRenderPipeline() const {
523 return fRenderPipeline;
524 }
525
526 } // namespace skgpu::graphite
527