diff options
-rw-r--r-- | samplecode/SampleCCPRGeometry.cpp | 41 | ||||
-rw-r--r-- | src/gpu/ccpr/GrCCCoverageProcessor.cpp | 33 | ||||
-rw-r--r-- | src/gpu/ccpr/GrCCCoverageProcessor.h | 130 | ||||
-rw-r--r-- | src/gpu/ccpr/GrCCCoverageProcessor_GSImpl.cpp | 154 | ||||
-rw-r--r-- | src/gpu/ccpr/GrCCCoverageProcessor_VSImpl.cpp | 563 | ||||
-rw-r--r-- | src/gpu/ccpr/GrCCPathParser.cpp | 37 | ||||
-rw-r--r-- | src/gpu/ccpr/GrCCPathParser.h | 4 |
7 files changed, 441 insertions, 521 deletions
diff --git a/samplecode/SampleCCPRGeometry.cpp b/samplecode/SampleCCPRGeometry.cpp index c6174516d3..ea3eafb591 100644 --- a/samplecode/SampleCCPRGeometry.cpp +++ b/samplecode/SampleCCPRGeometry.cpp @@ -28,7 +28,7 @@ using TriPointInstance = GrCCCoverageProcessor::TriPointInstance; using QuadPointInstance = GrCCCoverageProcessor::QuadPointInstance; -using RenderPass = GrCCCoverageProcessor::RenderPass; +using PrimitiveType = GrCCCoverageProcessor::PrimitiveType; static constexpr float kDebugBloat = 40; @@ -56,7 +56,7 @@ private: void updateGpuData(); - RenderPass fRenderPass = RenderPass::kTriangles; + PrimitiveType fPrimitiveType = PrimitiveType::kTriangles; SkCubicType fCubicType; SkMatrix fCubicKLM; @@ -90,7 +90,6 @@ private: bool onCombineIfPossible(GrOp* other, const GrCaps& caps) override { return false; } void onPrepare(GrOpFlushState*) override {} void onExecute(GrOpFlushState*) override; - void drawRenderPass(GrOpFlushState*, RenderPass); CCPRGeometryView* fView; @@ -149,9 +148,9 @@ void CCPRGeometryView::onDrawContent(SkCanvas* canvas) { SkPath outline; outline.moveTo(fPoints[0]); - if (RenderPass::kCubics == fRenderPass) { + if (PrimitiveType::kCubics == fPrimitiveType) { outline.cubicTo(fPoints[1], fPoints[2], fPoints[3]); - } else if (RenderPass::kQuadratics == fRenderPass) { + } else if (PrimitiveType::kQuadratics == fPrimitiveType) { outline.quadTo(fPoints[1], fPoints[3]); } else { outline.lineTo(fPoints[1]); @@ -205,8 +204,8 @@ void CCPRGeometryView::onDrawContent(SkCanvas* canvas) { SkRect::MakeIWH(this->width(), this->height())); // Add label. - caption.appendf("RenderPass_%s", GrCCCoverageProcessor::RenderPassName(fRenderPass)); - if (RenderPass::kCubics == fRenderPass) { + caption.appendf("RenderPass_%s", GrCCCoverageProcessor::PrimitiveTypeName(fPrimitiveType)); + if (PrimitiveType::kCubics == fPrimitiveType) { caption.appendf(" (%s)", SkCubicTypeName(fCubicType)); } } else { @@ -218,7 +217,7 @@ void CCPRGeometryView::onDrawContent(SkCanvas* canvas) { pointsPaint.setStrokeWidth(8); pointsPaint.setAntiAlias(true); - if (RenderPass::kCubics == fRenderPass) { + if (PrimitiveType::kCubics == fPrimitiveType) { int w = this->width(), h = this->height(); canvas->drawPoints(SkCanvas::kPoints_PointMode, 4, fPoints, pointsPaint); draw_klm_line(w, h, canvas, &fCubicKLM[0], SK_ColorYELLOW); @@ -240,7 +239,7 @@ void CCPRGeometryView::updateGpuData() { fTriPointInstances.reset(); fQuadPointInstances.reset(); - if (RenderPass::kCubics == fRenderPass) { + if (PrimitiveType::kCubics == fPrimitiveType) { double t[2], s[2]; fCubicType = GrPathUtils::getCubicKLM(fPoints, &fCubicKLM, t, s); GrCCGeometry geometry; @@ -264,7 +263,7 @@ void CCPRGeometryView::updateGpuData() { continue; } } - } else if (RenderPass::kQuadratics == fRenderPass) { + } else if (PrimitiveType::kQuadratics == fPrimitiveType) { GrCCGeometry geometry; geometry.beginContour(fPoints[0]); geometry.quadraticTo(fPoints[1], fPoints[3]); @@ -290,27 +289,18 @@ void CCPRGeometryView::updateGpuData() { } void CCPRGeometryView::DrawCoverageCountOp::onExecute(GrOpFlushState* state) { - this->drawRenderPass(state, fView->fRenderPass); - - RenderPass cornerPass = RenderPass((int)fView->fRenderPass + 1); - if (GrCCCoverageProcessor::DoesRenderPass(cornerPass, state->caps())) { - this->drawRenderPass(state, cornerPass); - } -} - -void CCPRGeometryView::DrawCoverageCountOp::drawRenderPass(GrOpFlushState* state, - RenderPass renderPass) { GrResourceProvider* rp = state->resourceProvider(); GrContext* context = state->gpu()->getContext(); GrGLGpu* glGpu = kOpenGL_GrBackend == context->contextPriv().getBackend() ? static_cast<GrGLGpu*>(state->gpu()) : nullptr; - GrCCCoverageProcessor proc(rp, renderPass, GrCCCoverageProcessor::WindMethod::kCrossProduct); + GrCCCoverageProcessor proc(rp, fView->fPrimitiveType, + GrCCCoverageProcessor::WindMethod::kCrossProduct); SkDEBUGCODE(proc.enableDebugBloat(kDebugBloat)); SkSTArray<1, GrMesh> mesh; - if (GrCCCoverageProcessor::RenderPassIsCubic(renderPass)) { + if (PrimitiveType::kCubics == fView->fPrimitiveType) { sk_sp<GrBuffer> instBuff(rp->createBuffer( fView->fQuadPointInstances.count() * sizeof(QuadPointInstance), kVertex_GrBufferType, kDynamic_GrAccessPattern, @@ -341,8 +331,7 @@ void CCPRGeometryView::DrawCoverageCountOp::drawRenderPass(GrOpFlushState* state if (!mesh.empty()) { SkASSERT(1 == mesh.count()); - GrGpuRTCommandBuffer* cmdBuff = state->rtCommandBuffer(); - cmdBuff->draw(pipeline, proc, mesh.begin(), nullptr, 1, this->bounds()); + proc.draw(state, pipeline, mesh.begin(), nullptr, 1, this->bounds()); } if (glGpu) { @@ -375,7 +364,7 @@ private: SkView::Click* CCPRGeometryView::onFindClickHandler(SkScalar x, SkScalar y, unsigned) { for (int i = 0; i < 4; ++i) { - if (RenderPass::kCubics != fRenderPass && 2 == i) { + if (PrimitiveType::kCubics != fPrimitiveType && 2 == i) { continue; } if (fabs(x - fPoints[i].x()) < 20 && fabsf(y - fPoints[i].y()) < 20) { @@ -400,7 +389,7 @@ bool CCPRGeometryView::onQuery(SkEvent* evt) { SkUnichar unichar; if (SampleCode::CharQ(*evt, &unichar)) { if (unichar >= '1' && unichar <= '3') { - fRenderPass = RenderPass((unichar - '1') * 2); + fPrimitiveType = PrimitiveType(unichar - '1'); this->updateAndInval(); return true; } diff --git a/src/gpu/ccpr/GrCCCoverageProcessor.cpp b/src/gpu/ccpr/GrCCCoverageProcessor.cpp index 8e7249e9e4..c44d464826 100644 --- a/src/gpu/ccpr/GrCCCoverageProcessor.cpp +++ b/src/gpu/ccpr/GrCCCoverageProcessor.cpp @@ -7,6 +7,8 @@ #include "GrCCCoverageProcessor.h" +#include "GrGpuCommandBuffer.h" +#include "GrOpFlushState.h" #include "SkMakeUnique.h" #include "ccpr/GrCCCubicShader.h" #include "ccpr/GrCCQuadraticShader.h" @@ -129,7 +131,10 @@ void GrCCCoverageProcessor::Shader::CalcCornerCoverageAttenuation(GrGLSLVertexGe void GrCCCoverageProcessor::getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const { - int key = (int)fRenderPass << 2; + int key = (int)fPrimitiveType << 3; + if (GSSubpass::kCorners == fGSSubpass) { + key |= 4; + } if (WindMethod::kInstanceData == fWindMethod) { key |= 2; } @@ -146,20 +151,32 @@ void GrCCCoverageProcessor::getGLSLProcessorKey(const GrShaderCaps&, GrGLSLPrimitiveProcessor* GrCCCoverageProcessor::createGLSLInstance(const GrShaderCaps&) const { std::unique_ptr<Shader> shader; - switch (fRenderPass) { - case RenderPass::kTriangles: - case RenderPass::kTriangleCorners: + switch (fPrimitiveType) { + case PrimitiveType::kTriangles: shader = skstd::make_unique<GrCCTriangleShader>(); break; - case RenderPass::kQuadratics: - case RenderPass::kQuadraticCorners: + case PrimitiveType::kQuadratics: shader = skstd::make_unique<GrCCQuadraticShader>(); break; - case RenderPass::kCubics: - case RenderPass::kCubicCorners: + case PrimitiveType::kCubics: shader = skstd::make_unique<GrCCCubicShader>(); break; } return Impl::kGeometryShader == fImpl ? this->createGSImpl(std::move(shader)) : this->createVSImpl(std::move(shader)); } + +void GrCCCoverageProcessor::draw(GrOpFlushState* flushState, const GrPipeline& pipeline, + const GrMesh meshes[], + const GrPipeline::DynamicState dynamicStates[], int meshCount, + const SkRect& drawBounds) const { + GrGpuRTCommandBuffer* cmdBuff = flushState->rtCommandBuffer(); + cmdBuff->draw(pipeline, *this, meshes, dynamicStates, meshCount, drawBounds); + + // Geometry shader backend draws primitives in two subpasses. + if (Impl::kGeometryShader == fImpl) { + SkASSERT(GSSubpass::kHulls == fGSSubpass); + GrCCCoverageProcessor cornerProc(*this, GSSubpass::kCorners); + cmdBuff->draw(pipeline, cornerProc, meshes, dynamicStates, meshCount, drawBounds); + } +} diff --git a/src/gpu/ccpr/GrCCCoverageProcessor.h b/src/gpu/ccpr/GrCCCoverageProcessor.h index 8b2b00392b..8824646a05 100644 --- a/src/gpu/ccpr/GrCCCoverageProcessor.h +++ b/src/gpu/ccpr/GrCCCoverageProcessor.h @@ -10,6 +10,7 @@ #include "GrCaps.h" #include "GrGeometryProcessor.h" +#include "GrPipeline.h" #include "GrShaderCaps.h" #include "SkNx.h" #include "glsl/GrGLSLGeometryProcessor.h" @@ -18,21 +19,29 @@ class GrGLSLFPFragmentBuilder; class GrGLSLVertexGeoBuilder; class GrMesh; +class GrOpFlushState; /** * This is the geometry processor for the simple convex primitive shapes (triangles and closed, * convex bezier curves) from which ccpr paths are composed. The output is a single-channel alpha * value, positive for clockwise shapes and negative for counter-clockwise, that indicates coverage. * - * The caller is responsible to execute all render passes for all applicable primitives into a - * cleared, floating point, alpha-only render target using SkBlendMode::kPlus (see RenderPass - * below). Once all of a path's primitives have been drawn, the render target contains a composite - * coverage count that can then be used to draw the path (see GrCCPathProcessor). + * The caller is responsible to draw all primitives as produced by GrCCGeometry into a cleared, + * floating point, alpha-only render target using SkBlendMode::kPlus. Once all of a path's + * primitives have been drawn, the render target contains a composite coverage count that can then + * be used to draw the path (see GrCCPathProcessor). * - * To draw a renderer pass, see appendMesh below. + * To draw primitives, use appendMesh() and draw() (defined below). */ class GrCCCoverageProcessor : public GrGeometryProcessor { public: + enum class PrimitiveType { + kTriangles, + kQuadratics, + kCubics, + }; + static const char* PrimitiveTypeName(PrimitiveType); + // Defines a single primitive shape with 3 input points (i.e. Triangles and Quadratics). // X,Y point values are transposed. struct TriPointInstance { @@ -54,50 +63,18 @@ public: void set(const SkPoint&, const SkPoint&, const SkPoint&, const Sk2f& trans, float w); }; - // All primitive shapes (triangles and closed, convex bezier curves) may require two render - // passes: One to draw a rough outline of the shape, and a second pass to touch up the corners. - // Check DoesRenderPass() before attempting to draw a given RenderPass. Here we enumerate every - // possible render pass needed in order to produce a complete coverage count mask. This is an - // exhaustive list of all ccpr coverage shaders. - enum class RenderPass { - kTriangles, - kTriangleCorners, - kQuadratics, - kQuadraticCorners, - kCubics, - kCubicCorners - }; - static bool RenderPassIsCubic(RenderPass); - static const char* RenderPassName(RenderPass); - - static bool DoesRenderPass(RenderPass renderPass, const GrCaps& caps) { - switch (renderPass) { - case RenderPass::kTriangles: - case RenderPass::kQuadratics: - case RenderPass::kCubics: - return true; - case RenderPass::kTriangleCorners: - case RenderPass::kQuadraticCorners: - case RenderPass::kCubicCorners: - return caps.shaderCaps()->geometryShaderSupport(); - } - SK_ABORT("Invalid RenderPass"); - return false; - } - enum class WindMethod : bool { kCrossProduct, // Calculate wind = +/-1 by sign of the cross product. kInstanceData // Instance data provides custom, signed wind values of any magnitude. // (For tightly-wound tessellated triangles.) }; - GrCCCoverageProcessor(GrResourceProvider* rp, RenderPass pass, WindMethod windMethod) + GrCCCoverageProcessor(GrResourceProvider* rp, PrimitiveType type, WindMethod windMethod) : INHERITED(kGrCCCoverageProcessor_ClassID) - , fRenderPass(pass) + , fPrimitiveType(type) , fWindMethod(windMethod) , fImpl(rp->caps()->shaderCaps()->geometryShaderSupport() ? Impl::kGeometryShader : Impl::kVertexShader) { - SkASSERT(DoesRenderPass(pass, *rp->caps())); if (Impl::kGeometryShader == fImpl) { this->initGS(); } else { @@ -106,7 +83,7 @@ public: } // GrPrimitiveProcessor overrides. - const char* name() const override { return RenderPassName(fRenderPass); } + const char* name() const override { return PrimitiveTypeName(fPrimitiveType); } SkString dumpInfo() const override { return SkStringPrintf("%s\n%s", this->name(), this->INHERITED::dumpInfo().c_str()); } @@ -132,6 +109,9 @@ public: } } + void draw(GrOpFlushState*, const GrPipeline&, const GrMesh[], const GrPipeline::DynamicState[], + int meshCount, const SkRect& drawBounds) const; + // The Shader provides code to calculate each pixel's coverage in a RenderPass. It also // provides details about shape-specific geometry. class Shader { @@ -224,13 +204,30 @@ private: static constexpr float kAABloatRadius = 0.491111f; // Number of bezier points for curves, or 3 for triangles. - int numInputPoints() const { return RenderPassIsCubic(fRenderPass) ? 4 : 3; } + int numInputPoints() const { return PrimitiveType::kCubics == fPrimitiveType ? 4 : 3; } enum class Impl : bool { kGeometryShader, kVertexShader }; + // Geometry shader backend draws primitives in two subpasses. + enum class GSSubpass : bool { + kHulls, + kCorners + }; + + GrCCCoverageProcessor(const GrCCCoverageProcessor& proc, GSSubpass subpass) + : INHERITED(kGrCCCoverageProcessor_ClassID) + , fPrimitiveType(proc.fPrimitiveType) + , fWindMethod(proc.fWindMethod) + , fImpl(Impl::kGeometryShader) + SkDEBUGCODE(, fDebugBloat(proc.fDebugBloat)) + , fGSSubpass(subpass) { + SkASSERT(Impl::kGeometryShader == proc.fImpl); + this->initGS(); + } + void initGS(); void initVS(GrResourceProvider*); @@ -242,20 +239,33 @@ private: GrGLSLPrimitiveProcessor* createGSImpl(std::unique_ptr<Shader>) const; GrGLSLPrimitiveProcessor* createVSImpl(std::unique_ptr<Shader>) const; - const RenderPass fRenderPass; + const PrimitiveType fPrimitiveType; const WindMethod fWindMethod; const Impl fImpl; SkDEBUGCODE(float fDebugBloat = 0); + // Used by GSImpl. + const GSSubpass fGSSubpass = GSSubpass::kHulls; + // Used by VSImpl. - sk_sp<const GrBuffer> fVertexBuffer; - sk_sp<const GrBuffer> fIndexBuffer; - int fNumIndicesPerInstance; - GrPrimitiveType fPrimitiveType; + sk_sp<const GrBuffer> fVSVertexBuffer; + sk_sp<const GrBuffer> fVSIndexBuffer; + int fVSNumIndicesPerInstance; + GrPrimitiveType fVSTriangleType; typedef GrGeometryProcessor INHERITED; }; +inline const char* GrCCCoverageProcessor::PrimitiveTypeName(PrimitiveType type) { + switch (type) { + case PrimitiveType::kTriangles: return "kTriangles"; + case PrimitiveType::kQuadratics: return "kQuadratics"; + case PrimitiveType::kCubics: return "kCubics"; + } + SK_ABORT("Invalid PrimitiveType"); + return ""; +} + inline void GrCCCoverageProcessor::TriPointInstance::set(const SkPoint p[3], const Sk2f& trans) { this->set(p[0], p[1], p[2], trans); } @@ -285,32 +295,4 @@ inline void GrCCCoverageProcessor::QuadPointInstance::set(const SkPoint& p0, con Sk2f::Store4(this, P0, P1, P2, W); } -inline bool GrCCCoverageProcessor::RenderPassIsCubic(RenderPass pass) { - switch (pass) { - case RenderPass::kTriangles: - case RenderPass::kTriangleCorners: - case RenderPass::kQuadratics: - case RenderPass::kQuadraticCorners: - return false; - case RenderPass::kCubics: - case RenderPass::kCubicCorners: - return true; - } - SK_ABORT("Invalid RenderPass"); - return false; -} - -inline const char* GrCCCoverageProcessor::RenderPassName(RenderPass pass) { - switch (pass) { - case RenderPass::kTriangles: return "kTriangles"; - case RenderPass::kTriangleCorners: return "kTriangleCorners"; - case RenderPass::kQuadratics: return "kQuadratics"; - case RenderPass::kQuadraticCorners: return "kQuadraticCorners"; - case RenderPass::kCubics: return "kCubics"; - case RenderPass::kCubicCorners: return "kCubicCorners"; - } - SK_ABORT("Invalid RenderPass"); - return ""; -} - #endif diff --git a/src/gpu/ccpr/GrCCCoverageProcessor_GSImpl.cpp b/src/gpu/ccpr/GrCCCoverageProcessor_GSImpl.cpp index 907de46019..0bb34b16a0 100644 --- a/src/gpu/ccpr/GrCCCoverageProcessor_GSImpl.cpp +++ b/src/gpu/ccpr/GrCCCoverageProcessor_GSImpl.cpp @@ -229,6 +229,72 @@ public: }; /** + * Generates a conservative raster around a convex quadrilateral that encloses a cubic or quadratic. + */ +class GrCCCoverageProcessor::GSCurveHullImpl : public GrCCCoverageProcessor::GSImpl { +public: + GSCurveHullImpl(std::unique_ptr<Shader> shader) : GSImpl(std::move(shader)) {} + + void onEmitGeometryShader(const GrCCCoverageProcessor&, GrGLSLGeometryBuilder* g, + const GrShaderVar& wind, const char* emitVertexFn) const override { + const char* hullPts = "pts"; + fShader->emitSetupCode(g, "pts", wind.c_str(), &hullPts); + + // Visualize the input (convex) quadrilateral as a square. Paying special attention to wind, + // we can identify the points by their corresponding corner. + // + // NOTE: We split the square down the diagonal from top-right to bottom-left, and generate + // the hull in two independent invocations. Each invocation designates the corner it will + // begin with as top-left. + g->codeAppend ("int i = sk_InvocationID * 2;"); + g->codeAppendf("float2 topleft = %s[i];", hullPts); + g->codeAppendf("float2 topright = %s[%s > 0 ? i + 1 : 3 - i];", hullPts, wind.c_str()); + g->codeAppendf("float2 bottomleft = %s[%s > 0 ? 3 - i : i + 1];", hullPts, wind.c_str()); + g->codeAppendf("float2 bottomright = %s[2 - i];", hullPts); + + // Determine how much to outset the conservative raster hull from the relevant edges. + g->codeAppend ("float2 leftbloat = float2(topleft.y > bottomleft.y ? +bloat : -bloat, " + "topleft.x > bottomleft.x ? -bloat : bloat);"); + g->codeAppend ("float2 upbloat = float2(topright.y > topleft.y ? +bloat : -bloat, " + "topright.x > topleft.x ? -bloat : +bloat);"); + g->codeAppend ("float2 rightbloat = float2(bottomright.y > topright.y ? +bloat : -bloat, " + "bottomright.x > topright.x ? -bloat : +bloat);"); + + // Here we generate the conservative raster geometry. It is the convex hull of 4 pixel-size + // boxes centered on the input points, split evenly between two invocations. This translates + // to a polygon with either one, two, or three vertices at each input point, depending on + // how sharp the corner is. For more details on conservative raster, see: + // https://developer.nvidia.com/gpugems/GPUGems2/gpugems2_chapter42.html + g->codeAppendf("bool2 left_up_notequal = notEqual(leftbloat, upbloat);"); + g->codeAppend ("if (all(left_up_notequal)) {"); + // The top-left corner will have three conservative raster vertices. + // Emit the middle one first to the triangle strip. + g->codeAppendf( "%s(topleft + float2(-leftbloat.y, leftbloat.x));", emitVertexFn); + g->codeAppend ("}"); + g->codeAppend ("if (any(left_up_notequal)) {"); + // Second conservative raster vertex for the top-left corner. + g->codeAppendf( "%s(topleft + leftbloat);", emitVertexFn); + g->codeAppend ("}"); + + // Main interior body of this invocation's half of the hull. + g->codeAppendf("%s(topleft + upbloat);", emitVertexFn); + g->codeAppendf("%s(bottomleft + leftbloat);", emitVertexFn); + g->codeAppendf("%s(topright + upbloat);", emitVertexFn); + + // Remaining two conservative raster vertices for the top-right corner. + g->codeAppendf("bool2 up_right_notequal = notEqual(upbloat, rightbloat);"); + g->codeAppend ("if (any(up_right_notequal)) {"); + g->codeAppendf( "%s(topright + rightbloat);", emitVertexFn); + g->codeAppend ("}"); + g->codeAppend ("if (all(up_right_notequal)) {"); + g->codeAppendf( "%s(topright + float2(-upbloat.y, upbloat.x));", emitVertexFn); + g->codeAppend ("}"); + + g->configure(InputType::kLines, OutputType::kTriangleStrip, 7, 2); + } +}; + +/** * Generates conservative rasters around corners (aka pixel-size boxes) and calculates * coverage and attenuation ramps to fix up the coverage values written by the hulls. */ @@ -243,7 +309,7 @@ public: const GrShaderVar& wind, const char* emitVertexFn) const override { fShader->emitSetupCode(g, "pts", wind.c_str()); - bool isTriangle = RenderPass::kTriangleCorners == proc.fRenderPass; + bool isTriangle = PrimitiveType::kTriangles == proc.fPrimitiveType; g->codeAppendf("int corneridx = sk_InvocationID;"); if (!isTriangle) { g->codeAppendf("corneridx *= %i;", proc.numInputPoints() - 1); @@ -314,75 +380,9 @@ public: } }; -/** - * Generates a conservative raster around a convex quadrilateral that encloses a cubic or quadratic. - */ -class GrCCCoverageProcessor::GSCurveHullImpl : public GrCCCoverageProcessor::GSImpl { -public: - GSCurveHullImpl(std::unique_ptr<Shader> shader) : GSImpl(std::move(shader)) {} - - void onEmitGeometryShader(const GrCCCoverageProcessor&, GrGLSLGeometryBuilder* g, - const GrShaderVar& wind, const char* emitVertexFn) const override { - const char* hullPts = "pts"; - fShader->emitSetupCode(g, "pts", wind.c_str(), &hullPts); - - // Visualize the input (convex) quadrilateral as a square. Paying special attention to wind, - // we can identify the points by their corresponding corner. - // - // NOTE: We split the square down the diagonal from top-right to bottom-left, and generate - // the hull in two independent invocations. Each invocation designates the corner it will - // begin with as top-left. - g->codeAppend ("int i = sk_InvocationID * 2;"); - g->codeAppendf("float2 topleft = %s[i];", hullPts); - g->codeAppendf("float2 topright = %s[%s > 0 ? i + 1 : 3 - i];", hullPts, wind.c_str()); - g->codeAppendf("float2 bottomleft = %s[%s > 0 ? 3 - i : i + 1];", hullPts, wind.c_str()); - g->codeAppendf("float2 bottomright = %s[2 - i];", hullPts); - - // Determine how much to outset the conservative raster hull from the relevant edges. - g->codeAppend ("float2 leftbloat = float2(topleft.y > bottomleft.y ? +bloat : -bloat, " - "topleft.x > bottomleft.x ? -bloat : bloat);"); - g->codeAppend ("float2 upbloat = float2(topright.y > topleft.y ? +bloat : -bloat, " - "topright.x > topleft.x ? -bloat : +bloat);"); - g->codeAppend ("float2 rightbloat = float2(bottomright.y > topright.y ? +bloat : -bloat, " - "bottomright.x > topright.x ? -bloat : +bloat);"); - - // Here we generate the conservative raster geometry. It is the convex hull of 4 pixel-size - // boxes centered on the input points, split evenly between two invocations. This translates - // to a polygon with either one, two, or three vertices at each input point, depending on - // how sharp the corner is. For more details on conservative raster, see: - // https://developer.nvidia.com/gpugems/GPUGems2/gpugems2_chapter42.html - g->codeAppendf("bool2 left_up_notequal = notEqual(leftbloat, upbloat);"); - g->codeAppend ("if (all(left_up_notequal)) {"); - // The top-left corner will have three conservative raster vertices. - // Emit the middle one first to the triangle strip. - g->codeAppendf( "%s(topleft + float2(-leftbloat.y, leftbloat.x));", emitVertexFn); - g->codeAppend ("}"); - g->codeAppend ("if (any(left_up_notequal)) {"); - // Second conservative raster vertex for the top-left corner. - g->codeAppendf( "%s(topleft + leftbloat);", emitVertexFn); - g->codeAppend ("}"); - - // Main interior body of this invocation's half of the hull. - g->codeAppendf("%s(topleft + upbloat);", emitVertexFn); - g->codeAppendf("%s(bottomleft + leftbloat);", emitVertexFn); - g->codeAppendf("%s(topright + upbloat);", emitVertexFn); - - // Remaining two conservative raster vertices for the top-right corner. - g->codeAppendf("bool2 up_right_notequal = notEqual(upbloat, rightbloat);"); - g->codeAppend ("if (any(up_right_notequal)) {"); - g->codeAppendf( "%s(topright + rightbloat);", emitVertexFn); - g->codeAppend ("}"); - g->codeAppend ("if (all(up_right_notequal)) {"); - g->codeAppendf( "%s(topright + float2(-upbloat.y, upbloat.x));", emitVertexFn); - g->codeAppend ("}"); - - g->configure(InputType::kLines, OutputType::kTriangleStrip, 7, 2); - } -}; - void GrCCCoverageProcessor::initGS() { SkASSERT(Impl::kGeometryShader == fImpl); - if (RenderPassIsCubic(fRenderPass) || WindMethod::kInstanceData == fWindMethod) { + if (PrimitiveType::kCubics == fPrimitiveType || WindMethod::kInstanceData == fWindMethod) { SkASSERT(WindMethod::kCrossProduct == fWindMethod || 3 == this->numInputPoints()); this->addVertexAttrib("x_or_y_values", kFloat4_GrVertexAttribType); SkASSERT(sizeof(QuadPointInstance) == this->getVertexStride() * 2); @@ -408,17 +408,11 @@ void GrCCCoverageProcessor::appendGSMesh(GrBuffer* instanceBuffer, int instanceC } GrGLSLPrimitiveProcessor* GrCCCoverageProcessor::createGSImpl(std::unique_ptr<Shader> shadr) const { - switch (fRenderPass) { - case RenderPass::kTriangles: - return new GSTriangleHullImpl(std::move(shadr)); - case RenderPass::kQuadratics: - case RenderPass::kCubics: - return new GSCurveHullImpl(std::move(shadr)); - case RenderPass::kTriangleCorners: - case RenderPass::kQuadraticCorners: - case RenderPass::kCubicCorners: - return new GSCornerImpl(std::move(shadr)); + if (GSSubpass::kHulls == fGSSubpass) { + return (PrimitiveType::kTriangles == fPrimitiveType) + ? (GSImpl*) new GSTriangleHullImpl(std::move(shadr)) + : (GSImpl*) new GSCurveHullImpl(std::move(shadr)); } - SK_ABORT("Invalid RenderPass"); - return nullptr; + SkASSERT(GSSubpass::kCorners == fGSSubpass); + return new GSCornerImpl(std::move(shadr)); } diff --git a/src/gpu/ccpr/GrCCCoverageProcessor_VSImpl.cpp b/src/gpu/ccpr/GrCCCoverageProcessor_VSImpl.cpp index d0fc4e0f71..7af1a739fc 100644 --- a/src/gpu/ccpr/GrCCCoverageProcessor_VSImpl.cpp +++ b/src/gpu/ccpr/GrCCCoverageProcessor_VSImpl.cpp @@ -10,92 +10,30 @@ #include "GrMesh.h" #include "glsl/GrGLSLVertexGeoBuilder.h" -using Shader = GrCCCoverageProcessor::Shader; - -static constexpr int kAttribIdx_X = 0; -static constexpr int kAttribIdx_Y = 1; -static constexpr int kAttribIdx_VertexData = 2; - -/** - * This class and its subclasses implement the coverage processor with vertex shaders. - */ +// This class implements the coverage processor with vertex shaders. class GrCCCoverageProcessor::VSImpl : public GrGLSLGeometryProcessor { -protected: - VSImpl(std::unique_ptr<Shader> shader) : fShader(std::move(shader)) {} +public: + VSImpl(std::unique_ptr<Shader> shader, int numSides) + : fShader(std::move(shader)), fNumSides(numSides) {} +private: void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor&, FPCoordTransformIter&& transformIter) final { this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter); } - struct Coverages { - const char* fCoverage = nullptr; // half - const char* fAttenuatedCoverage = nullptr; // half2 - }; - - void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) final { - const GrCCCoverageProcessor& proc = args.fGP.cast<GrCCCoverageProcessor>(); - - // Vertex shader. - GrGLSLVertexBuilder* v = args.fVertBuilder; - int numInputPoints = proc.numInputPoints(); - - const char* swizzle = (4 == numInputPoints) ? "xyzw" : "xyz"; - v->codeAppendf("float%ix2 pts = transpose(float2x%i(%s.%s, %s.%s));", - numInputPoints, numInputPoints, proc.getAttrib(kAttribIdx_X).fName, swizzle, - proc.getAttrib(kAttribIdx_Y).fName, swizzle); - - if (WindMethod::kCrossProduct == proc.fWindMethod) { - v->codeAppend ("float area_x2 = determinant(float2x2(pts[0] - pts[1], " - "pts[0] - pts[2]));"); - if (4 == numInputPoints) { - v->codeAppend ("area_x2 += determinant(float2x2(pts[0] - pts[2], " - "pts[0] - pts[3]));"); - } - v->codeAppend ("half wind = sign(area_x2);"); - } else { - SkASSERT(WindMethod::kInstanceData == proc.fWindMethod); - SkASSERT(3 == numInputPoints); - SkASSERT(kFloat4_GrVertexAttribType == proc.getAttrib(kAttribIdx_X).fType); - v->codeAppendf("half wind = %s.w;", proc.getAttrib(kAttribIdx_X).fName); - } - - float bloat = kAABloatRadius; -#ifdef SK_DEBUG - if (proc.debugBloatEnabled()) { - bloat *= proc.debugBloat(); - } -#endif - v->defineConstant("bloat", bloat); - - Coverages coverages; - this->emitVertexPosition(proc, v, gpArgs, &coverages); - SkASSERT(kFloat2_GrSLType == gpArgs->fPositionVar.getType()); - - GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler; - SkString varyingCode; - fShader->emitVaryings(varyingHandler, GrGLSLVarying::Scope::kVertToFrag, &varyingCode, - gpArgs->fPositionVar.c_str(), coverages.fCoverage, - coverages.fAttenuatedCoverage); - v->codeAppend(varyingCode.c_str()); - - varyingHandler->emitAttributes(proc); - SkASSERT(!args.fFPCoordTransformHandler->nextCoordTransform()); - - // Fragment shader. - fShader->emitFragmentCode(proc, args.fFragBuilder, args.fOutputColor, args.fOutputCoverage); - } - - virtual void emitVertexPosition(const GrCCCoverageProcessor&, GrGLSLVertexBuilder*, GrGPArgs*, - Coverages* outCoverages) const = 0; - - virtual ~VSImpl() {} + void onEmitCode(EmitArgs&, GrGPArgs*) override; const std::unique_ptr<Shader> fShader; - - typedef GrGLSLGeometryProcessor INHERITED; + const int fNumSides; }; +static constexpr int kAttribIdx_X = 0; // Transposed X values of all input points. +static constexpr int kAttribIdx_Y = 1; // Transposed Y values of all input points. +static constexpr int kAttribIdx_VertexData = 2; + +// Vertex data tells the shader how to offset vertices for conservative raster, as well as how to +// calculate coverage values for corners and edges. static constexpr int kVertexData_LeftNeighborIdShift = 10; static constexpr int kVertexData_RightNeighborIdShift = 8; static constexpr int kVertexData_BloatIdxShift = 6; @@ -104,10 +42,6 @@ static constexpr int kVertexData_IsCornerBit = 1 << 4; static constexpr int kVertexData_IsEdgeBit = 1 << 3; static constexpr int kVertexData_IsHullBit = 1 << 2; -/** - * Vertex data tells the shader how to offset vertices for conservative raster, and how/whether to - * calculate initial coverage values for edges. See VSHullAndEdgeImpl. - */ static constexpr int32_t pack_vertex_data(int32_t leftNeighborID, int32_t rightNeighborID, int32_t bloatIdx, int32_t cornerID, int32_t extraData = 0) { @@ -244,7 +178,8 @@ static constexpr uint16_t kTriangleIndicesAsTris[] = { GR_DECLARE_STATIC_UNIQUE_KEY(gTriangleIndexBufferKey); -static constexpr int32_t kHull4Vertices[] = { +// Curves, including quadratics, are drawn with a four-sided hull. +static constexpr int32_t kCurveVertices[] = { hull_vertex_data(0, 0, 4), hull_vertex_data(0, 1, 4), hull_vertex_data(0, 2, 4), @@ -269,16 +204,16 @@ static constexpr int32_t kHull4Vertices[] = { corner_vertex_data(2, 3, 0, 3), }; -GR_DECLARE_STATIC_UNIQUE_KEY(gHull4VertexBufferKey); +GR_DECLARE_STATIC_UNIQUE_KEY(gCurveVertexBufferKey); -static constexpr uint16_t kHull4IndicesAsStrips[] = { +static constexpr uint16_t kCurveIndicesAsStrips[] = { 1, 0, 2, 11, 3, 5, 4, kRestartStrip, // First half of the hull (split diagonally). 7, 6, 8, 5, 9, 11, 10, kRestartStrip, // Second half of the hull. 13, 12, 14, 15, kRestartStrip, // First corner. - 17, 16, 18, 19 // Second corner. + 17, 16, 18, 19 // Final corner. }; -static constexpr uint16_t kHull4IndicesAsTris[] = { +static constexpr uint16_t kCurveIndicesAsTris[] = { // First half of the hull (split diagonally). 1, 0, 2, 0, 11, 2, @@ -297,243 +232,265 @@ static constexpr uint16_t kHull4IndicesAsTris[] = { 13, 12, 14, 12, 15, 14, - // Second corner. + // Final corner. 17, 16, 18, 16, 19, 18, }; -GR_DECLARE_STATIC_UNIQUE_KEY(gHull4IndexBufferKey); - - -/** - * Generates a conservative raster hull around a triangle or curve. For triangles we generate - * additional conservative rasters with coverage ramps around the edges and corners. - * - * Triangles are drawn in three steps: (1) Draw a conservative raster of the entire triangle, with a - * coverage of +1. (2) Draw conservative rasters around each edge, with a coverage ramp from -1 to - * 0. These edge coverage values convert jagged conservative raster edges into smooth, antialiased - * ones. (3) Draw conservative rasters (aka pixel-size boxes) around each corner, replacing the - * previous coverage values with ones that ramp to zero in the bloat vertices that fall outside the - * triangle. - * - * Curves are drawn in two separate passes. Here we just draw a conservative raster around the input - * points. The Shader takes care of everything else for now. The final curve corners get touched up - * in a later step by VSCornerImpl. - */ -class VSHullAndEdgeImpl : public GrCCCoverageProcessor::VSImpl { -public: - VSHullAndEdgeImpl(std::unique_ptr<Shader> shader, int numSides) - : VSImpl(std::move(shader)), fNumSides(numSides) {} - - void emitVertexPosition(const GrCCCoverageProcessor& proc, GrGLSLVertexBuilder* v, - GrGPArgs* gpArgs, Coverages* outCoverages) const override { - const char* hullPts = "pts"; - fShader->emitSetupCode(v, "pts", "wind", &hullPts); - - // Reverse all indices if the wind is counter-clockwise: [0, 1, 2] -> [2, 1, 0]. - v->codeAppendf("int clockwise_indices = wind > 0 ? %s : 0x%x - %s;", - proc.getAttrib(kAttribIdx_VertexData).fName, - ((fNumSides - 1) << kVertexData_LeftNeighborIdShift) | - ((fNumSides - 1) << kVertexData_RightNeighborIdShift) | - (((1 << kVertexData_RightNeighborIdShift) - 1) ^ 3) | - (fNumSides - 1), - proc.getAttrib(kAttribIdx_VertexData).fName); - - // Here we generate conservative raster geometry for the input polygon. It is the convex - // hull of N pixel-size boxes, one centered on each the input points. Each corner has three - // vertices, where one or two may cause degenerate triangles. The vertex data tells us how - // to offset each vertex. Triangle edges and corners are also handled here using the same - // concept. For more details on conservative raster, see: - // https://developer.nvidia.com/gpugems/GPUGems2/gpugems2_chapter42.html - v->codeAppendf("float2 corner = %s[clockwise_indices & 3];", hullPts); - v->codeAppendf("float2 left = %s[clockwise_indices >> %i];", - hullPts, kVertexData_LeftNeighborIdShift); - v->codeAppendf("float2 right = %s[(clockwise_indices >> %i) & 3];", - hullPts, kVertexData_RightNeighborIdShift); - - v->codeAppend ("float2 leftbloat = sign(corner - left);"); - v->codeAppend ("leftbloat = float2(0 != leftbloat.y ? leftbloat.y : leftbloat.x, " - "0 != leftbloat.x ? -leftbloat.x : -leftbloat.y);"); - - v->codeAppend ("float2 rightbloat = sign(right - corner);"); - v->codeAppend ("rightbloat = float2(0 != rightbloat.y ? rightbloat.y : rightbloat.x, " - "0 != rightbloat.x ? -rightbloat.x : -rightbloat.y);"); - - v->codeAppend ("bool2 left_right_notequal = notEqual(leftbloat, rightbloat);"); - - v->codeAppend ("float2 bloatdir = leftbloat;"); - - v->codeAppend ("float2 leftdir = corner - left;"); - v->codeAppend ("leftdir = (float2(0) != leftdir) ? normalize(leftdir) : float2(1, 0);"); - - v->codeAppend ("float2 rightdir = right - corner;"); - v->codeAppend ("rightdir = (float2(0) != rightdir)" - "? normalize(rightdir) : float2(1, 0);"); - - v->codeAppendf("if (0 != (%s & %i)) {", // Are we a corner? - proc.getAttrib(kAttribIdx_VertexData).fName, kVertexData_IsCornerBit); - - // In corner boxes, all 4 coverage values will not map linearly. - // Therefore it is important to align the box so its diagonal shared - // edge points out of the triangle, in the direction that ramps to 0. - v->codeAppend ( "bloatdir = float2(leftdir.x > rightdir.x ? +1 : -1, " - "leftdir.y > rightdir.y ? +1 : -1);"); - - // For corner boxes, we hack left_right_notequal to always true. This - // in turn causes the upcoming code to always rotate, generating all - // 4 vertices of the corner box. - v->codeAppendf( "left_right_notequal = bool2(true);"); - v->codeAppend ("}"); - - // At each corner of the polygon, our hull will have either 1, 2, or 3 vertices (or 4 if - // it's a corner box). We begin with this corner's first raster vertex (leftbloat), then - // continue rotating 90 degrees clockwise until we reach the desired raster vertex for this - // invocation. Corners with less than 3 corresponding raster vertices will result in - // redundant vertices and degenerate triangles. - v->codeAppendf("int bloatidx = (%s >> %i) & 3;", - proc.getAttrib(kAttribIdx_VertexData).fName, kVertexData_BloatIdxShift); - v->codeAppend ("switch (bloatidx) {"); - v->codeAppend ( "case 3:"); - // Only corners will have bloatidx=3, and corners always rotate. - v->codeAppend ( "bloatdir = float2(-bloatdir.y, +bloatdir.x);"); // 90 deg CW. - // fallthru. - v->codeAppend ( "case 2:"); - v->codeAppendf( "if (all(left_right_notequal)) {"); - v->codeAppend ( "bloatdir = float2(-bloatdir.y, +bloatdir.x);"); // 90 deg CW. - v->codeAppend ( "}"); - // fallthru. - v->codeAppend ( "case 1:"); - v->codeAppendf( "if (any(left_right_notequal)) {"); - v->codeAppend ( "bloatdir = float2(-bloatdir.y, +bloatdir.x);"); // 90 deg CW. - v->codeAppend ( "}"); - // fallthru. - v->codeAppend ("}"); - - v->codeAppend ("float2 vertex = corner + bloatdir * bloat;"); - gpArgs->fPositionVar.set(kFloat2_GrSLType, "vertex"); - - v->codeAppend ("half left_coverage; {"); - Shader::CalcEdgeCoverageAtBloatVertex(v, "left", "corner", "bloatdir", "left_coverage"); - v->codeAppend ("}"); - - v->codeAppend ("half right_coverage; {"); - Shader::CalcEdgeCoverageAtBloatVertex(v, "corner", "right", "bloatdir", "right_coverage"); - v->codeAppend ("}"); - - v->codeAppend ("half attenuation; {"); - Shader::CalcCornerCoverageAttenuation(v, "leftdir", "rightdir", "attenuation"); - v->codeAppend ("}"); - - // Hulls have a coverage of +1 all around. - v->codeAppend ("half coverage = +1;"); - - if (3 == fNumSides) { - v->codeAppendf("if (0 != (%s & %i)) {", // Are we an edge? - proc.getAttrib(kAttribIdx_VertexData).fName, kVertexData_IsEdgeBit); - v->codeAppend ( "coverage = left_coverage;"); - v->codeAppend ("}"); - - v->codeAppendf("if (0 != (%s & %i)) {", // Invert coverage? - proc.getAttrib(kAttribIdx_VertexData).fName, - kVertexData_InvertNegativeCoverageBit); - v->codeAppend ( "coverage = -1 - coverage;"); - v->codeAppend ("}"); +GR_DECLARE_STATIC_UNIQUE_KEY(gCurveIndexBufferKey); + +// Generates a conservative raster hull around a triangle or curve. For triangles we generate +// additional conservative rasters with coverage ramps around the edges and corners. +// +// Triangles are drawn in three steps: (1) Draw a conservative raster of the entire triangle, with a +// coverage of +1. (2) Draw conservative rasters around each edge, with a coverage ramp from -1 to +// 0. These edge coverage values convert jagged conservative raster edges into smooth, antialiased +// ones. (3) Draw conservative rasters (aka pixel-size boxes) around each corner, replacing the +// previous coverage values with ones that ramp to zero in the bloat vertices that fall outside the +// triangle. +// +// Curves are drawn in two separate passes. Here we just draw a conservative raster around the input +// points. The Shader takes care of everything else for now. The final curve corners get touched up +// in a later step by VSCornerImpl. +void GrCCCoverageProcessor::VSImpl::onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) { + const GrCCCoverageProcessor& proc = args.fGP.cast<GrCCCoverageProcessor>(); + GrGLSLVertexBuilder* v = args.fVertBuilder; + int numInputPoints = proc.numInputPoints(); + + const char* swizzle = (4 == numInputPoints) ? "xyzw" : "xyz"; + v->codeAppendf("float%ix2 pts = transpose(float2x%i(%s.%s, %s.%s));", + numInputPoints, numInputPoints, proc.getAttrib(kAttribIdx_X).fName, swizzle, + proc.getAttrib(kAttribIdx_Y).fName, swizzle); + + if (WindMethod::kCrossProduct == proc.fWindMethod) { + v->codeAppend ("float area_x2 = determinant(float2x2(pts[0] - pts[1], " + "pts[0] - pts[2]));"); + if (4 == numInputPoints) { + v->codeAppend ("area_x2 += determinant(float2x2(pts[0] - pts[2], " + "pts[0] - pts[3]));"); } + v->codeAppend ("half wind = sign(area_x2);"); + } else { + SkASSERT(WindMethod::kInstanceData == proc.fWindMethod); + SkASSERT(3 == numInputPoints); + SkASSERT(kFloat4_GrVertexAttribType == proc.getAttrib(kAttribIdx_X).fType); + v->codeAppendf("half wind = %s.w;", proc.getAttrib(kAttribIdx_X).fName); + } - // Corner boxes require attenuation. - v->codeAppend ("half2 attenuated_coverage = half2(0, 1);"); + float bloat = kAABloatRadius; +#ifdef SK_DEBUG + if (proc.debugBloatEnabled()) { + bloat *= proc.debugBloat(); + } +#endif + v->defineConstant("bloat", bloat); + + const char* hullPts = "pts"; + fShader->emitSetupCode(v, "pts", "wind", &hullPts); + + // Reverse all indices if the wind is counter-clockwise: [0, 1, 2] -> [2, 1, 0]. + v->codeAppendf("int clockwise_indices = wind > 0 ? %s : 0x%x - %s;", + proc.getAttrib(kAttribIdx_VertexData).fName, + ((fNumSides - 1) << kVertexData_LeftNeighborIdShift) | + ((fNumSides - 1) << kVertexData_RightNeighborIdShift) | + (((1 << kVertexData_RightNeighborIdShift) - 1) ^ 3) | + (fNumSides - 1), + proc.getAttrib(kAttribIdx_VertexData).fName); + + // Here we generate conservative raster geometry for the input polygon. It is the convex + // hull of N pixel-size boxes, one centered on each the input points. Each corner has three + // vertices, where one or two may cause degenerate triangles. The vertex data tells us how + // to offset each vertex. Triangle edges and corners are also handled here using the same + // concept. For more details on conservative raster, see: + // https://developer.nvidia.com/gpugems/GPUGems2/gpugems2_chapter42.html + v->codeAppendf("float2 corner = %s[clockwise_indices & 3];", hullPts); + v->codeAppendf("float2 left = %s[clockwise_indices >> %i];", + hullPts, kVertexData_LeftNeighborIdShift); + v->codeAppendf("float2 right = %s[(clockwise_indices >> %i) & 3];", + hullPts, kVertexData_RightNeighborIdShift); + + v->codeAppend ("float2 leftbloat = sign(corner - left);"); + v->codeAppend ("leftbloat = float2(0 != leftbloat.y ? leftbloat.y : leftbloat.x, " + "0 != leftbloat.x ? -leftbloat.x : -leftbloat.y);"); + + v->codeAppend ("float2 rightbloat = sign(right - corner);"); + v->codeAppend ("rightbloat = float2(0 != rightbloat.y ? rightbloat.y : rightbloat.x, " + "0 != rightbloat.x ? -rightbloat.x : -rightbloat.y);"); + + v->codeAppend ("bool2 left_right_notequal = notEqual(leftbloat, rightbloat);"); + + v->codeAppend ("float2 bloatdir = leftbloat;"); + + v->codeAppend ("float2 leftdir = corner - left;"); + v->codeAppend ("leftdir = (float2(0) != leftdir) ? normalize(leftdir) : float2(1, 0);"); + + v->codeAppend ("float2 rightdir = right - corner;"); + v->codeAppend ("rightdir = (float2(0) != rightdir) ? normalize(rightdir) : float2(1, 0);"); + + v->codeAppendf("if (0 != (%s & %i)) {", // Are we a corner? + proc.getAttrib(kAttribIdx_VertexData).fName, kVertexData_IsCornerBit); + + // In corner boxes, all 4 coverage values will not map linearly. + // Therefore it is important to align the box so its diagonal shared + // edge points out of the triangle, in the direction that ramps to 0. + v->codeAppend ( "bloatdir = float2(leftdir.x > rightdir.x ? +1 : -1, " + "leftdir.y > rightdir.y ? +1 : -1);"); + + // For corner boxes, we hack left_right_notequal to always true. This + // in turn causes the upcoming code to always rotate, generating all + // 4 vertices of the corner box. + v->codeAppendf( "left_right_notequal = bool2(true);"); + v->codeAppend ("}"); + + // At each corner of the polygon, our hull will have either 1, 2, or 3 vertices (or 4 if + // it's a corner box). We begin with this corner's first raster vertex (leftbloat), then + // continue rotating 90 degrees clockwise until we reach the desired raster vertex for this + // invocation. Corners with less than 3 corresponding raster vertices will result in + // redundant vertices and degenerate triangles. + v->codeAppendf("int bloatidx = (%s >> %i) & 3;", + proc.getAttrib(kAttribIdx_VertexData).fName, kVertexData_BloatIdxShift); + v->codeAppend ("switch (bloatidx) {"); + v->codeAppend ( "case 3:"); + // Only corners will have bloatidx=3, and corners always rotate. + v->codeAppend ( "bloatdir = float2(-bloatdir.y, +bloatdir.x);"); // 90 deg CW. + // fallthru. + v->codeAppend ( "case 2:"); + v->codeAppendf( "if (all(left_right_notequal)) {"); + v->codeAppend ( "bloatdir = float2(-bloatdir.y, +bloatdir.x);"); // 90 deg CW. + v->codeAppend ( "}"); + // fallthru. + v->codeAppend ( "case 1:"); + v->codeAppendf( "if (any(left_right_notequal)) {"); + v->codeAppend ( "bloatdir = float2(-bloatdir.y, +bloatdir.x);"); // 90 deg CW. + v->codeAppend ( "}"); + // fallthru. + v->codeAppend ("}"); + + v->codeAppend ("float2 vertex = corner + bloatdir * bloat;"); + gpArgs->fPositionVar.set(kFloat2_GrSLType, "vertex"); + + v->codeAppend ("half left_coverage; {"); + Shader::CalcEdgeCoverageAtBloatVertex(v, "left", "corner", "bloatdir", "left_coverage"); + v->codeAppend ("}"); + + v->codeAppend ("half right_coverage; {"); + Shader::CalcEdgeCoverageAtBloatVertex(v, "corner", "right", "bloatdir", "right_coverage"); + v->codeAppend ("}"); + + v->codeAppend ("half attenuation; {"); + Shader::CalcCornerCoverageAttenuation(v, "leftdir", "rightdir", "attenuation"); + v->codeAppend ("}"); + + // Hulls have a coverage of +1 all around. + v->codeAppend ("half coverage = +1;"); + + if (3 == fNumSides) { + v->codeAppendf("if (0 != (%s & %i)) {", // Are we an edge? + proc.getAttrib(kAttribIdx_VertexData).fName, kVertexData_IsEdgeBit); + v->codeAppend ( "coverage = left_coverage;"); + v->codeAppend ("}"); - v->codeAppendf("if (0 != (%s & %i)) {", // Are we a corner? - proc.getAttrib(kAttribIdx_VertexData).fName, kVertexData_IsCornerBit); - // We use coverage=-1 to erase what the hull geometry wrote. - v->codeAppend ( "coverage = -1;"); - if (3 == fNumSides) { - // Triangle corners also have to erase what the edge geometry wrote. - v->codeAppend ("coverage -= left_coverage + right_coverage;"); - } - // The x and y components of "attenuated_coverage" are multiplied - // together by the fragment shader. They ramp to 0 with attenuation in - // the diagonal that points out of the corner, and linearly from - // left-edge coverage to right in the opposite diagonal. - // bloatidx=0 is the outermost vertex; the one that has attenuation. - v->codeAppend ( "attenuated_coverage = (0 == bloatidx)" - "? half2(0, attenuation) : half2(1);"); - v->codeAppend ( "if (1 == bloatidx || 2 == bloatidx) {"); - v->codeAppend ( "attenuated_coverage.x += right_coverage;"); - v->codeAppend ( "}"); - v->codeAppend ( "if (bloatidx >= 2) {"); - v->codeAppend ( "attenuated_coverage.x += left_coverage;"); - v->codeAppend ( "}"); + v->codeAppendf("if (0 != (%s & %i)) {", // Invert coverage? + proc.getAttrib(kAttribIdx_VertexData).fName, + kVertexData_InvertNegativeCoverageBit); + v->codeAppend ( "coverage = -1 - coverage;"); v->codeAppend ("}"); + } - v->codeAppend ("coverage *= wind;"); - outCoverages->fCoverage = "coverage"; + // Corner boxes require attenuation. + v->codeAppend ("half2 attenuated_coverage = half2(0, 1);"); - v->codeAppend ("attenuated_coverage.x *= wind;"); - outCoverages->fAttenuatedCoverage = "attenuated_coverage"; + v->codeAppendf("if (0 != (%s & %i)) {", // Are we a corner? + proc.getAttrib(kAttribIdx_VertexData).fName, kVertexData_IsCornerBit); + // We use coverage=-1 to erase what the hull geometry wrote. + v->codeAppend ( "coverage = -1;"); + if (3 == fNumSides) { + // Triangle corners also have to erase what the edge geometry wrote. + v->codeAppend ("coverage -= left_coverage + right_coverage;"); } - -private: - const int fNumSides; -}; + // The x and y components of "attenuated_coverage" are multiplied + // together by the fragment shader. They ramp to 0 with attenuation in + // the diagonal that points out of the corner, and linearly from + // left-edge coverage to right in the opposite diagonal. + // bloatidx=0 is the outermost vertex; the one that has attenuation. + v->codeAppend ( "attenuated_coverage = (0 == bloatidx)" + "? half2(0, attenuation) : half2(1);"); + v->codeAppend ( "if (1 == bloatidx || 2 == bloatidx) {"); + v->codeAppend ( "attenuated_coverage.x += right_coverage;"); + v->codeAppend ( "}"); + v->codeAppend ( "if (bloatidx >= 2) {"); + v->codeAppend ( "attenuated_coverage.x += left_coverage;"); + v->codeAppend ( "}"); + v->codeAppend ("}"); + + GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler; + SkString varyingCode; + v->codeAppend ("coverage *= wind;"); + v->codeAppend ("attenuated_coverage.x *= wind;"); + fShader->emitVaryings(varyingHandler, GrGLSLVarying::Scope::kVertToFrag, &varyingCode, + gpArgs->fPositionVar.c_str(), "coverage", "attenuated_coverage"); + v->codeAppend(varyingCode.c_str()); + + varyingHandler->emitAttributes(proc); + SkASSERT(!args.fFPCoordTransformHandler->nextCoordTransform()); + + // Fragment shader. + fShader->emitFragmentCode(proc, args.fFragBuilder, args.fOutputColor, args.fOutputCoverage); +} void GrCCCoverageProcessor::initVS(GrResourceProvider* rp) { SkASSERT(Impl::kVertexShader == fImpl); const GrCaps& caps = *rp->caps(); - switch (fRenderPass) { - case RenderPass::kTriangles: { + switch (fPrimitiveType) { + case PrimitiveType::kTriangles: { GR_DEFINE_STATIC_UNIQUE_KEY(gTriangleVertexBufferKey); - fVertexBuffer = rp->findOrMakeStaticBuffer(kVertex_GrBufferType, - sizeof(kTriangleVertices), - kTriangleVertices, - gTriangleVertexBufferKey); + fVSVertexBuffer = rp->findOrMakeStaticBuffer(kVertex_GrBufferType, + sizeof(kTriangleVertices), + kTriangleVertices, + gTriangleVertexBufferKey); GR_DEFINE_STATIC_UNIQUE_KEY(gTriangleIndexBufferKey); if (caps.usePrimitiveRestart()) { - fIndexBuffer = rp->findOrMakeStaticBuffer(kIndex_GrBufferType, - sizeof(kTriangleIndicesAsStrips), - kTriangleIndicesAsStrips, - gTriangleIndexBufferKey); - fNumIndicesPerInstance = SK_ARRAY_COUNT(kTriangleIndicesAsStrips); + fVSIndexBuffer = rp->findOrMakeStaticBuffer(kIndex_GrBufferType, + sizeof(kTriangleIndicesAsStrips), + kTriangleIndicesAsStrips, + gTriangleIndexBufferKey); + fVSNumIndicesPerInstance = SK_ARRAY_COUNT(kTriangleIndicesAsStrips); } else { - fIndexBuffer = rp->findOrMakeStaticBuffer(kIndex_GrBufferType, - sizeof(kTriangleIndicesAsTris), - kTriangleIndicesAsTris, - gTriangleIndexBufferKey); - fNumIndicesPerInstance = SK_ARRAY_COUNT(kTriangleIndicesAsTris); + fVSIndexBuffer = rp->findOrMakeStaticBuffer(kIndex_GrBufferType, + sizeof(kTriangleIndicesAsTris), + kTriangleIndicesAsTris, + gTriangleIndexBufferKey); + fVSNumIndicesPerInstance = SK_ARRAY_COUNT(kTriangleIndicesAsTris); } break; } - case RenderPass::kQuadratics: - case RenderPass::kCubics: { - GR_DEFINE_STATIC_UNIQUE_KEY(gHull4VertexBufferKey); - fVertexBuffer = rp->findOrMakeStaticBuffer(kVertex_GrBufferType, sizeof(kHull4Vertices), - kHull4Vertices, gHull4VertexBufferKey); - GR_DEFINE_STATIC_UNIQUE_KEY(gHull4IndexBufferKey); + case PrimitiveType::kQuadratics: + case PrimitiveType::kCubics: { + GR_DEFINE_STATIC_UNIQUE_KEY(gCurveVertexBufferKey); + fVSVertexBuffer = rp->findOrMakeStaticBuffer(kVertex_GrBufferType, + sizeof(kCurveVertices), kCurveVertices, + gCurveVertexBufferKey); + GR_DEFINE_STATIC_UNIQUE_KEY(gCurveIndexBufferKey); if (caps.usePrimitiveRestart()) { - fIndexBuffer = rp->findOrMakeStaticBuffer(kIndex_GrBufferType, - sizeof(kHull4IndicesAsStrips), - kHull4IndicesAsStrips, - gHull4IndexBufferKey); - fNumIndicesPerInstance = SK_ARRAY_COUNT(kHull4IndicesAsStrips); + fVSIndexBuffer = rp->findOrMakeStaticBuffer(kIndex_GrBufferType, + sizeof(kCurveIndicesAsStrips), + kCurveIndicesAsStrips, + gCurveIndexBufferKey); + fVSNumIndicesPerInstance = SK_ARRAY_COUNT(kCurveIndicesAsStrips); } else { - fIndexBuffer = rp->findOrMakeStaticBuffer(kIndex_GrBufferType, - sizeof(kHull4IndicesAsTris), - kHull4IndicesAsTris, - gHull4IndexBufferKey); - fNumIndicesPerInstance = SK_ARRAY_COUNT(kHull4IndicesAsTris); + fVSIndexBuffer = rp->findOrMakeStaticBuffer(kIndex_GrBufferType, + sizeof(kCurveIndicesAsTris), + kCurveIndicesAsTris, + gCurveIndexBufferKey); + fVSNumIndicesPerInstance = SK_ARRAY_COUNT(kCurveIndicesAsTris); } break; } - - case RenderPass::kTriangleCorners: - case RenderPass::kQuadraticCorners: - case RenderPass::kCubicCorners: - SK_ABORT("Corners are not used by VSImpl."); } - if (RenderPassIsCubic(fRenderPass) || WindMethod::kInstanceData == fWindMethod) { + if (PrimitiveType::kCubics == fPrimitiveType || WindMethod::kInstanceData == fWindMethod) { SkASSERT(WindMethod::kCrossProduct == fWindMethod || 3 == this->numInputPoints()); SkASSERT(kAttribIdx_X == this->numAttribs()); @@ -563,32 +520,28 @@ void GrCCCoverageProcessor::initVS(GrResourceProvider* rp) { if (caps.usePrimitiveRestart()) { this->setWillUsePrimitiveRestart(); - fPrimitiveType = GrPrimitiveType::kTriangleStrip; + fVSTriangleType = GrPrimitiveType::kTriangleStrip; } else { - fPrimitiveType = GrPrimitiveType::kTriangles; + fVSTriangleType = GrPrimitiveType::kTriangles; } } void GrCCCoverageProcessor::appendVSMesh(GrBuffer* instanceBuffer, int instanceCount, int baseInstance, SkTArray<GrMesh>* out) const { SkASSERT(Impl::kVertexShader == fImpl); - GrMesh& mesh = out->emplace_back(fPrimitiveType); - mesh.setIndexedInstanced(fIndexBuffer.get(), fNumIndicesPerInstance, instanceBuffer, + GrMesh& mesh = out->emplace_back(fVSTriangleType); + mesh.setIndexedInstanced(fVSIndexBuffer.get(), fVSNumIndicesPerInstance, instanceBuffer, instanceCount, baseInstance); - mesh.setVertexData(fVertexBuffer.get(), 0); + mesh.setVertexData(fVSVertexBuffer.get(), 0); } GrGLSLPrimitiveProcessor* GrCCCoverageProcessor::createVSImpl(std::unique_ptr<Shader> shadr) const { - switch (fRenderPass) { - case RenderPass::kTriangles: - return new VSHullAndEdgeImpl(std::move(shadr), 3); - case RenderPass::kQuadratics: - case RenderPass::kCubics: - return new VSHullAndEdgeImpl(std::move(shadr), 4); - case RenderPass::kTriangleCorners: - case RenderPass::kQuadraticCorners: - case RenderPass::kCubicCorners: - SK_ABORT("Corners are not used by VSImpl."); + switch (fPrimitiveType) { + case PrimitiveType::kTriangles: + return new VSImpl(std::move(shadr), 3); + case PrimitiveType::kQuadratics: + case PrimitiveType::kCubics: + return new VSImpl(std::move(shadr), 4); } SK_ABORT("Invalid RenderPass"); return nullptr; diff --git a/src/gpu/ccpr/GrCCPathParser.cpp b/src/gpu/ccpr/GrCCPathParser.cpp index 1114367ac7..abeb164bbf 100644 --- a/src/gpu/ccpr/GrCCPathParser.cpp +++ b/src/gpu/ccpr/GrCCPathParser.cpp @@ -500,7 +500,7 @@ bool GrCCPathParser::finalize(GrOnFlushResourceProvider* onFlushRP) { void GrCCPathParser::drawCoverageCount(GrOpFlushState* flushState, CoverageCountBatchID batchID, const SkIRect& drawBounds) const { - using RenderPass = GrCCCoverageProcessor::RenderPass; + using PrimitiveType = GrCCCoverageProcessor::PrimitiveType; using WindMethod = GrCCCoverageProcessor::WindMethod; SkASSERT(fInstanceBuffer); @@ -511,54 +511,40 @@ void GrCCPathParser::drawCoverageCount(GrOpFlushState* flushState, CoverageCount SkBlendMode::kPlus); if (batchTotalCounts.fTriangles) { - this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kTriangles, + this->drawPrimitives(flushState, pipeline, batchID, PrimitiveType::kTriangles, WindMethod::kCrossProduct, &PrimitiveTallies::fTriangles, drawBounds); - this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kTriangleCorners, - WindMethod::kCrossProduct, &PrimitiveTallies::fTriangles, - drawBounds); // Might get skipped. } if (batchTotalCounts.fWoundTriangles) { - this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kTriangles, + this->drawPrimitives(flushState, pipeline, batchID, PrimitiveType::kTriangles, WindMethod::kInstanceData, &PrimitiveTallies::fWoundTriangles, drawBounds); - this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kTriangleCorners, - WindMethod::kInstanceData, &PrimitiveTallies::fWoundTriangles, - drawBounds); // Might get skipped. } if (batchTotalCounts.fQuadratics) { - this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kQuadratics, - WindMethod::kCrossProduct, &PrimitiveTallies::fQuadratics, drawBounds); - this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kQuadraticCorners, + this->drawPrimitives(flushState, pipeline, batchID, PrimitiveType::kQuadratics, WindMethod::kCrossProduct, &PrimitiveTallies::fQuadratics, drawBounds); } if (batchTotalCounts.fCubics) { - this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kCubics, - WindMethod::kCrossProduct, &PrimitiveTallies::fCubics, drawBounds); - this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kCubicCorners, + this->drawPrimitives(flushState, pipeline, batchID, PrimitiveType::kCubics, WindMethod::kCrossProduct, &PrimitiveTallies::fCubics, drawBounds); } } -void GrCCPathParser::drawRenderPass(GrOpFlushState* flushState, const GrPipeline& pipeline, +void GrCCPathParser::drawPrimitives(GrOpFlushState* flushState, const GrPipeline& pipeline, CoverageCountBatchID batchID, - GrCCCoverageProcessor::RenderPass renderPass, + GrCCCoverageProcessor::PrimitiveType primitiveType, GrCCCoverageProcessor::WindMethod windMethod, int PrimitiveTallies::*instanceType, const SkIRect& drawBounds) const { SkASSERT(pipeline.getScissorState().enabled()); - if (!GrCCCoverageProcessor::DoesRenderPass(renderPass, flushState->caps())) { - return; - } - // Don't call reset(), as that also resets the reserve count. fMeshesScratchBuffer.pop_back_n(fMeshesScratchBuffer.count()); fDynamicStatesScratchBuffer.pop_back_n(fDynamicStatesScratchBuffer.count()); - GrCCCoverageProcessor proc(flushState->resourceProvider(), renderPass, windMethod); + GrCCCoverageProcessor proc(flushState->resourceProvider(), primitiveType, windMethod); SkASSERT(batchID > 0); SkASSERT(batchID < fCoverageCountBatches.count()); @@ -600,9 +586,8 @@ void GrCCPathParser::drawRenderPass(GrOpFlushState* flushState, const GrPipeline SkASSERT(totalInstanceCount == batch.fTotalPrimitiveCounts.*instanceType); if (!fMeshesScratchBuffer.empty()) { - SkASSERT(flushState->rtCommandBuffer()); - flushState->rtCommandBuffer()->draw(pipeline, proc, fMeshesScratchBuffer.begin(), - fDynamicStatesScratchBuffer.begin(), - fMeshesScratchBuffer.count(), SkRect::Make(drawBounds)); + proc.draw(flushState, pipeline, fMeshesScratchBuffer.begin(), + fDynamicStatesScratchBuffer.begin(), fMeshesScratchBuffer.count(), + SkRect::Make(drawBounds)); } } diff --git a/src/gpu/ccpr/GrCCPathParser.h b/src/gpu/ccpr/GrCCPathParser.h index b1a1ee5906..b28ab3b489 100644 --- a/src/gpu/ccpr/GrCCPathParser.h +++ b/src/gpu/ccpr/GrCCPathParser.h @@ -127,8 +127,8 @@ private: void parsePath(const SkPath&, const SkPoint* deviceSpacePts); void endContourIfNeeded(bool insideContour); - void drawRenderPass(GrOpFlushState*, const GrPipeline&, CoverageCountBatchID, - GrCCCoverageProcessor::RenderPass, GrCCCoverageProcessor::WindMethod, + void drawPrimitives(GrOpFlushState*, const GrPipeline&, CoverageCountBatchID, + GrCCCoverageProcessor::PrimitiveType, GrCCCoverageProcessor::WindMethod, int PrimitiveTallies::*instanceType, const SkIRect& drawBounds) const; // Staging area for the path being parsed. |