| /* | |
| * Copyright 2022 Google LLC | |
| * | |
| * Use of this source code is governed by a BSD-style license that can be | |
| * found in the LICENSE file. | |
| */ | |
| #include "src/gpu/graphite/render/TessellateWedgesRenderStep.h" | |
| #include "include/core/SkPath.h" | |
| #include "include/core/SkPathTypes.h" | |
| #include "include/core/SkPoint.h" | |
| #include "include/core/SkSpan.h" | |
| #include "include/private/SkAssert.h" | |
| #include "include/private/SkAttributes.h" | |
| #include "include/private/SkDebug.h" | |
| #include "include/private/SkEnumBitMask.h" | |
| #include "src/core/SkSLTypeShared.h" | |
| #include "src/gpu/BufferWriter.h" | |
| #include "src/gpu/graphite/Attribute.h" | |
| #include "src/gpu/graphite/BufferManager.h" | |
| #include "src/gpu/graphite/DrawOrder.h" | |
| #include "src/gpu/graphite/DrawParams.h" | |
| #include "src/gpu/graphite/DrawTypes.h" | |
| #include "src/gpu/graphite/PipelineData.h" | |
| #include "src/gpu/graphite/geom/Geometry.h" | |
| #include "src/gpu/graphite/geom/Shape.h" | |
| #include "src/gpu/graphite/geom/Transform.h" | |
| #include "src/gpu/graphite/render/CommonDepthStencilSettings.h" | |
| #include "src/gpu/graphite/render/DynamicInstancesPatchAllocator.h" | |
| #include "src/gpu/tessellate/FixedCountBufferUtils.h" | |
| #include "src/gpu/tessellate/MidpointContourParser.h" | |
| #include "src/gpu/tessellate/PatchWriter.h" | |
| #include "src/gpu/tessellate/Tessellation.h" | |
| #include "src/gpu/tessellate/WangsFormula.h" | |
| #include "src/sksl/SkSLString.h" | |
| #include <cstddef> | |
| namespace skgpu::graphite { | |
| namespace { | |
| using namespace skgpu::tess; | |
| // Only kFanPoint, no stroke params, since this is for filled wedges. | |
| // No color or wide color attribs, since it might always be part of the PaintParams | |
| // or we'll add a color-only fast path to RenderStep later. | |
| // No explicit curve type on platforms that support infinity. | |
| static constexpr PatchAttribs kAttribs = PatchAttribs::kFanPoint | | |
| PatchAttribs::kPaintDepth | | |
| PatchAttribs::kSsboIndex; | |
| static constexpr PatchAttribs kAttribsWithCurveType = kAttribs | PatchAttribs::kExplicitCurveType; | |
| using Writer = PatchWriter<DynamicInstancesPatchAllocator<FixedCountWedges>, | |
| Required<PatchAttribs::kFanPoint>, | |
| Required<PatchAttribs::kPaintDepth>, | |
| Required<PatchAttribs::kSsboIndex>, | |
| Optional<PatchAttribs::kExplicitCurveType>>; | |
| // The order of the attribute declarations must match the order used by | |
| // PatchWriter::emitPatchAttribs, i.e.: | |
| // join << fanPoint << stroke << color << depth << curveType << ssboIndex | |
| static constexpr Attribute kBaseAttributes[] = { | |
| {"p01", VertexAttribType::kFloat4, SkSLType::kFloat4}, | |
| {"p23", VertexAttribType::kFloat4, SkSLType::kFloat4}, | |
| {"fanPointAttrib", VertexAttribType::kFloat2, SkSLType::kFloat2}, | |
| {"depth", VertexAttribType::kFloat, SkSLType::kFloat}, | |
| {"ssboIndex", VertexAttribType::kUInt, SkSLType::kUInt}}; | |
| static constexpr Attribute kAttributesWithCurveType[] = { | |
| {"p01", VertexAttribType::kFloat4, SkSLType::kFloat4}, | |
| {"p23", VertexAttribType::kFloat4, SkSLType::kFloat4}, | |
| {"fanPointAttrib", VertexAttribType::kFloat2, SkSLType::kFloat2}, | |
| {"depth", VertexAttribType::kFloat, SkSLType::kFloat}, | |
| {"curveType", VertexAttribType::kFloat, SkSLType::kFloat}, | |
| {"ssboIndex", VertexAttribType::kUInt, SkSLType::kUInt}}; | |
| static constexpr SkSpan<const Attribute> kAttributes[2] = {kAttributesWithCurveType, | |
| kBaseAttributes}; | |
| } // namespace | |
| // When this is used for even-odd or winding fills, it's a non-shading step so any self | |
| // intersections are acceptable and handled gracefully with stencil buffer updates. When it's used | |
| // as the convex path renderer, none of its emitted geometry will have self-intersections because | |
| // the paths are known to be convex (and thus also a single contour). | |
| TessellateWedgesRenderStep::TessellateWedgesRenderStep(Layout layout, | |
| RenderStepID renderStepID, | |
| bool infinitySupport, | |
| SkEnumBitMask<Flags> xtraFlags, | |
| StencilSettings stencilSettings, | |
| StaticBufferManager* bufferManager) | |
| : RenderStep(layout, | |
| renderStepID, | |
| Flags::kRequiresMSAA | Flags::kAppendDynamicInstances | |
| | Flags::kIgnoreInverseFill | |
| | xtraFlags, | |
| /*uniforms=*/{{"localToDevice", SkSLType::kFloat4x4}}, | |
| PrimitiveType::kTriangles, | |
| stencilSettings, | |
| /*staticAttrs=*/{{{"resolveLevel_and_idx", | |
| VertexAttribType::kFloat2, SkSLType::kFloat2}}}, | |
| /*appendAttrs=*/SK_UNSAFE_TODO(kAttributes[infinitySupport]), | |
| /*storageUniforms=*/{}) | |
| , fInfinitySupport(infinitySupport) { | |
| // Initialize the static buffers we'll use when recording draw calls. | |
| // NOTE: Each instance of this RenderStep gets its own copy of the data. If this ends up causing | |
| // problems, we can modify StaticBufferManager to de-duplicate requests. | |
| auto vertexData = bufferManager->getVertexWriter(FixedCountWedges::VertexBufferVertexCount(), | |
| FixedCountWedges::VertexBufferStride(), | |
| &fVertexBuffer); | |
| if (vertexData) { | |
| FixedCountWedges::WriteVertexBuffer(std::move(vertexData), | |
| FixedCountWedges::VertexBufferSize()); | |
| } // otherwise static buffer creation failed, so do nothing; Context initialization will fail. | |
| const size_t indexSize = FixedCountWedges::IndexBufferSize(); | |
| auto indexData = bufferManager->getIndexWriter(indexSize, &fIndexBuffer); | |
| if (indexData) { | |
| FixedCountWedges::WriteIndexBuffer(std::move(indexData), indexSize); | |
| } // otherwise static buffer creation failed, so do nothing; Context initialization will fail. | |
| } | |
| TessellateWedgesRenderStep::~TessellateWedgesRenderStep() {} | |
| std::unique_ptr<TessellateWedgesRenderStep> TessellateWedgesRenderStep::StencilFill( | |
| Layout layout, bool evenOdd, bool infinitySupport, StaticBufferManager* bufferManager) { | |
| // For stencil fills, allow self intersections to update the stencil buffer counts appropriately | |
| // The final winding rule will be evaluated by a separate cover step that won't result in | |
| // double blending for its shading pass. | |
| static constexpr SkEnumBitMask<Flags> kStencilFlags = Flags::kAllowsSelfIntersection; | |
| RenderStepID id = evenOdd ? RenderStepID::kTessellateWedges_EvenOdd | |
| : RenderStepID::kTessellateWedges_Winding; | |
| auto stencil = evenOdd ? kEvenOddStencilPass : kWindingStencilPass; | |
| return std::unique_ptr<TessellateWedgesRenderStep>(new TessellateWedgesRenderStep( | |
| layout, id, infinitySupport, kStencilFlags, stencil, bufferManager)); | |
| } | |
| std::unique_ptr<TessellateWedgesRenderStep> TessellateWedgesRenderStep::ConvexFill( | |
| Layout layout, bool infinitySupport, StaticBufferManager* bufferManager) { | |
| // When convex, the render step shades and doesn't use the stencil buffer at all. Since the | |
| // paths are known to be convex, the emitted triangles should not produce self intersections. | |
| static constexpr SkEnumBitMask<Flags> kConvexFlags = | |
| Flags::kPerformsShading | Flags::kNoSelfIntersections; | |
| return std::unique_ptr<TessellateWedgesRenderStep>(new TessellateWedgesRenderStep( | |
| layout, RenderStepID::kTessellateWedges_Convex, infinitySupport, | |
| kConvexFlags, /*stencilSettings=*/{}, bufferManager)); | |
| } | |
| std::string TessellateWedgesRenderStep::vertexSkSL(const RootNodesInfo&) const { | |
| return SK_UNSAFE_TODO(SkSL::String::printf( | |
| "float2 localCoord;\n" | |
| "if (resolveLevel_and_idx.x < 0) {\n" | |
| // A negative resolve level means this is the fan point. | |
| "localCoord = fanPointAttrib;\n" | |
| "} else {\n" | |
| // TODO: Approximate perspective scaling to match how PatchWriter is configured | |
| // (or provide explicit tessellation level in instance data instead of | |
| // replicating work) | |
| "float2x2 vectorXform = float2x2(localToDevice[0].xy, localToDevice[1].xy);\n" | |
| "localCoord = tessellate_filled_curve(" | |
| "vectorXform, resolveLevel_and_idx.x, resolveLevel_and_idx.y, p01, p23, %s);\n" | |
| "}\n" | |
| "float4 devPosition = localToDevice * float4(localCoord, 0.0, 1.0);\n" | |
| "devPosition.z = depth;\n" | |
| "stepLocalCoords = localCoord;\n", | |
| fInfinitySupport ? "curve_type_using_inf_support(p23)" : "curveType")); | |
| } | |
| void TessellateWedgesRenderStep::writeVertices(DrawWriter* dw, | |
| StorageContext* /*storageContext*/, | |
| const DrawParams& params, | |
| uint32_t ssboIndex) const { | |
| SkASSERT(this->appendDataStride(params) == | |
| PatchStride(fInfinitySupport ? kAttribs : kAttribsWithCurveType)); | |
| SkPath path = params.geometry().shape().asPath(); // TODO: Iterate the Shape directly | |
| int patchReserveCount = FixedCountWedges::PreallocCount(path.countVerbs()); | |
| Writer writer{fInfinitySupport ? kAttribs : kAttribsWithCurveType, | |
| *dw, | |
| fVertexBuffer, | |
| fIndexBuffer, | |
| patchReserveCount}; | |
| writer.updatePaintDepthAttrib(params.order().depthAsFloat()); | |
| writer.updateSsboIndexAttrib(ssboIndex); | |
| // The vector xform approximates how the control points are transformed by the shader to | |
| // more accurately compute how many *parametric* segments are needed. | |
| // TODO: This doesn't account for perspective division yet, which will require updating the | |
| // approximate transform based on each verb's control points' bounding box. | |
| SkASSERT(params.transform().type() < Transform::Type::kPerspective); | |
| writer.setShaderTransform(wangs_formula::VectorXform{params.transform().matrix()}, | |
| params.transform().maxScaleFactor()); | |
| // TODO: Essentially the same as PathWedgeTessellator::write_patches but with a different | |
| // PatchWriter template. | |
| // For wedges, we iterate over each contour explicitly, using a fan point position that is in | |
| // the midpoint of the current contour. | |
| MidpointContourParser parser{path}; | |
| while (parser.parseNextContour()) { | |
| writer.updateFanPointAttrib(parser.currentMidpoint()); | |
| SkPoint lastPoint = {0, 0}; | |
| SkPoint startPoint = {0, 0}; | |
| for (auto [verb, pts, w] : parser.currentContour()) { | |
| switch (verb) { | |
| case SkPathVerb::kMove: | |
| startPoint = lastPoint = pts[0]; | |
| break; | |
| case SkPathVerb::kLine: | |
| // Unlike curve tessellation, wedges have to handle lines as part of the patch, | |
| // effectively forming a single triangle with the fan point. | |
| writer.writeLine(pts[0], SK_UNSAFE_TODO(pts[1])); | |
| lastPoint = SK_UNSAFE_TODO(pts[1]); | |
| break; | |
| case SkPathVerb::kQuad: | |
| writer.writeQuadratic(pts); | |
| lastPoint = SK_UNSAFE_TODO(pts[2]); | |
| break; | |
| case SkPathVerb::kConic: | |
| writer.writeConic(pts, *w); | |
| lastPoint = SK_UNSAFE_TODO(pts[2]); | |
| break; | |
| case SkPathVerb::kCubic: | |
| writer.writeCubic(pts); | |
| lastPoint = SK_UNSAFE_TODO(pts[3]); | |
| break; | |
| default: break; | |
| } | |
| } | |
| // Explicitly close the contour with another line segment, which also differs from curve | |
| // tessellation since that approach's triangle step automatically closes the contour. | |
| if (lastPoint != startPoint) { | |
| writer.writeLine(lastPoint, startPoint); | |
| } | |
| } | |
| } | |
| void TessellateWedgesRenderStep::writeUniformsAndTextures(const DrawParams& params, | |
| PipelineDataGatherer* gatherer) const { | |
| SkDEBUGCODE(gatherer->checkRewind()); | |
| SkDEBUGCODE(UniformExpectationsValidator uev(gatherer, this->uniforms());) | |
| gatherer->write(params.transform().matrix()); | |
| } | |
| } // namespace skgpu::graphite |