2 * Copyright 2022 Google LLC
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
8 #include "src/gpu/graphite/render/TessellateWedgesRenderStep.h"
10 #include "src/gpu/graphite/DrawGeometry.h"
11 #include "src/gpu/graphite/DrawWriter.h"
12 #include "src/gpu/graphite/render/DynamicInstancesPatchAllocator.h"
14 #include "src/gpu/tessellate/AffineMatrix.h"
15 #include "src/gpu/tessellate/FixedCountBufferUtils.h"
16 #include "src/gpu/tessellate/MidpointContourParser.h"
17 #include "src/gpu/tessellate/PatchWriter.h"
19 namespace skgpu::graphite {
23 using namespace skgpu::tess;
25 // Only kFanPoint, no stroke params, since this is for filled wedges.
26 // No explicit curve type, since we assume infinity is supported on GPUs using graphite
27 // No color or wide color attribs, since it might always be part of the PaintParams
28 // or we'll add a color-only fast path to RenderStep later.
29 static constexpr PatchAttribs kAttribs = PatchAttribs::kFanPoint |
30 PatchAttribs::kPaintDepth;
32 using Writer = PatchWriter<DynamicInstancesPatchAllocator<FixedCountWedges>,
33 Required<PatchAttribs::kFanPoint>,
34 Required<PatchAttribs::kPaintDepth>>;
38 TessellateWedgesRenderStep::TessellateWedgesRenderStep(std::string_view variantName,
39 DepthStencilSettings depthStencilSettings)
40 : RenderStep("TessellateWedgesRenderStep",
42 Flags::kRequiresMSAA |
43 (depthStencilSettings.fDepthWriteEnabled ? Flags::kPerformsShading
46 PrimitiveType::kTriangles,
48 /*vertexAttrs=*/ {{"resolveLevel_and_idx",
49 VertexAttribType::kFloat2, SkSLType::kFloat2}},
50 /*instanceAttrs=*/{{"p01", VertexAttribType::kFloat4, SkSLType::kFloat4},
51 {"p23", VertexAttribType::kFloat4, SkSLType::kFloat4},
52 {"fanPointAttrib", VertexAttribType::kFloat2,
54 {"depth", VertexAttribType::kFloat, SkSLType::kFloat}}) {
55 SkASSERT(this->instanceStride() == PatchStride(kAttribs));
58 TessellateWedgesRenderStep::~TessellateWedgesRenderStep() {}
60 const char* TessellateWedgesRenderStep::vertexSkSL() const {
61 return "float4 devPosition = float4("
62 "middle_out_wedge(resolveLevel_and_idx.x, resolveLevel_and_idx.y, p01, p23, "
63 "fanPointAttrib), depth, 1.0);\n";
66 void TessellateWedgesRenderStep::writeVertices(DrawWriter* dw, const DrawGeometry& geom) const {
67 SkPath path = geom.shape().asPath(); // TODO: Iterate the Shape directly
69 BindBufferInfo fixedVertexBuffer = dw->bufferManager()->getStaticBuffer(
71 FixedCountWedges::WriteVertexBuffer,
72 FixedCountWedges::VertexBufferSize);
73 BindBufferInfo fixedIndexBuffer = dw->bufferManager()->getStaticBuffer(
75 FixedCountWedges::WriteIndexBuffer,
76 FixedCountWedges::IndexBufferSize);
78 int patchReserveCount = FixedCountWedges::PreallocCount(path.countVerbs());
79 Writer writer{kAttribs, *dw, fixedVertexBuffer, fixedIndexBuffer, patchReserveCount};
80 writer.updatePaintDepthAttrib(geom.order().depthAsFloat());
82 // TODO: Is it better to pre-transform on the CPU and only have a matrix uniform to compute
83 // local coords, or is it better to always transform on the GPU (less CPU usage, more
84 // uniform data to upload, dependent on push constants or storage buffers for good batching)
86 // Currently no additional transform is applied by the GPU.
87 writer.setShaderTransform(wangs_formula::VectorXform{});
88 // TODO: This doesn't handle perspective yet, and ideally wouldn't go through SkMatrix.
89 // It may not be relevant, though, if transforms are applied on the GPU and we only need to
90 // determine an approximate 2x2 for 'shaderXform' and Wang's formula evaluation.
91 AffineMatrix m(geom.transform().matrix().asM33());
93 // TODO: Essentially the same as PathWedgeTessellator::write_patches but with a different
94 // PatchWriter template.
95 // For wedges, we iterate over each contour explicitly, using a fan point position that is in
96 // the midpoint of the current contour.
97 MidpointContourParser parser{path};
98 while (parser.parseNextContour()) {
99 writer.updateFanPointAttrib(m.mapPoint(parser.currentMidpoint()));
100 skvx::float2 lastPoint = {0, 0};
101 skvx::float2 startPoint = {0, 0};
102 for (auto [verb, pts, w] : parser.currentContour()) {
104 case SkPathVerb::kMove: {
105 startPoint = lastPoint = m.map1Point(pts);
109 case SkPathVerb::kLine: {
110 // Unlike curve tessellation, wedges have to handle lines as part of the patch,
111 // effectively forming a single triangle with the fan point.
112 auto [p0, p1] = m.map2Points(pts);
113 writer.writeLine(p0, p1);
118 case SkPathVerb::kQuad: {
119 auto [p0, p1] = m.map2Points(pts);
120 auto p2 = m.map1Point(pts+2);
122 writer.writeQuadratic(p0, p1, p2);
127 case SkPathVerb::kConic: {
128 auto [p0, p1] = m.map2Points(pts);
129 auto p2 = m.map1Point(pts+2);
131 writer.writeConic(p0, p1, p2, *w);
136 case SkPathVerb::kCubic: {
137 auto [p0, p1] = m.map2Points(pts);
138 auto [p2, p3] = m.map2Points(pts+2);
140 writer.writeCubic(p0, p1, p2, p3);
149 // Explicitly close the contour with another line segment, which also differs from curve
150 // tessellation since that approach's triangle step automatically closes the contour.
151 if (any(lastPoint != startPoint)) {
152 writer.writeLine(lastPoint, startPoint);
157 void TessellateWedgesRenderStep::writeUniforms(const DrawGeometry&, SkPipelineDataGatherer*) const {
158 // Control points are pre-transformed to device space on the CPU, so no uniforms needed.
161 } // namespace skgpu::graphite