diff options
-rw-r--r-- | gn/gpu.gni | 1 | ||||
-rw-r--r-- | samplecode/SampleCCPRGeometry.cpp | 51 | ||||
-rw-r--r-- | src/gpu/ccpr/GrCCCoverageProcessor.cpp | 25 | ||||
-rw-r--r-- | src/gpu/ccpr/GrCCCoverageProcessor.h | 46 | ||||
-rw-r--r-- | src/gpu/ccpr/GrCCCoverageProcessor_GSImpl.cpp | 68 | ||||
-rw-r--r-- | src/gpu/ccpr/GrCCCoverageProcessor_VSImpl.cpp | 214 | ||||
-rw-r--r-- | src/gpu/ccpr/GrCCPathParser.cpp | 9 | ||||
-rw-r--r-- | src/gpu/ccpr/GrCCTriangleShader.cpp | 149 | ||||
-rw-r--r-- | src/gpu/ccpr/GrCCTriangleShader.h | 43 |
9 files changed, 311 insertions, 295 deletions
diff --git a/gn/gpu.gni b/gn/gpu.gni index 457b4057cc..ac52fe2bc1 100644 --- a/gn/gpu.gni +++ b/gn/gpu.gni @@ -318,7 +318,6 @@ skia_gpu_sources = [ "$_src/gpu/ccpr/GrCCPathProcessor.h", "$_src/gpu/ccpr/GrCCQuadraticShader.cpp", "$_src/gpu/ccpr/GrCCQuadraticShader.h", - "$_src/gpu/ccpr/GrCCTriangleShader.cpp", "$_src/gpu/ccpr/GrCCTriangleShader.h", "$_src/gpu/ccpr/GrCoverageCountingPathRenderer.cpp", "$_src/gpu/ccpr/GrCoverageCountingPathRenderer.h", diff --git a/samplecode/SampleCCPRGeometry.cpp b/samplecode/SampleCCPRGeometry.cpp index a90ece09a1..4b521dea48 100644 --- a/samplecode/SampleCCPRGeometry.cpp +++ b/samplecode/SampleCCPRGeometry.cpp @@ -32,10 +32,6 @@ using RenderPass = GrCCCoverageProcessor::RenderPass; static constexpr float kDebugBloat = 40; -static int is_quadratic(RenderPass pass) { - return pass == RenderPass::kQuadratics || pass == RenderPass::kQuadraticCorners; -} - /** * This sample visualizes the AA bloat geometry generated by the ccpr geometry shaders. It * increases the AA bloat by 50x and outputs color instead of coverage (coverage=+1 -> green, @@ -91,6 +87,7 @@ 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; @@ -119,14 +116,13 @@ static void draw_klm_line(int w, int h, SkCanvas* canvas, const SkScalar line[3] } void CCPRGeometryView::onDrawContent(SkCanvas* canvas) { - SkAutoCanvasRestore acr(canvas, true); - canvas->setMatrix(SkMatrix::I()); + canvas->clear(SK_ColorBLACK); SkPath outline; outline.moveTo(fPoints[0]); - if (GrCCCoverageProcessor::RenderPassIsCubic(fRenderPass)) { + if (RenderPass::kCubics == fRenderPass) { outline.cubicTo(fPoints[1], fPoints[2], fPoints[3]); - } else if (is_quadratic(fRenderPass)) { + } else if (RenderPass::kQuadratics == fRenderPass) { outline.quadTo(fPoints[1], fPoints[3]); } else { outline.lineTo(fPoints[1]); @@ -135,7 +131,7 @@ void CCPRGeometryView::onDrawContent(SkCanvas* canvas) { } SkPaint outlinePaint; - outlinePaint.setColor(0x30000000); + outlinePaint.setColor(0x80ffffff); outlinePaint.setStyle(SkPaint::kStroke_Style); outlinePaint.setStrokeWidth(0); outlinePaint.setAntiAlias(true); @@ -159,7 +155,7 @@ void CCPRGeometryView::onDrawContent(SkCanvas* canvas) { if (GrRenderTargetContext* rtc = canvas->internal_private_accessTopLayerRenderTargetContext()) { rtc->priv().testingOnly_addDrawOp(skstd::make_unique<Op>(this)); caption.appendf("RenderPass_%s", GrCCCoverageProcessor::RenderPassName(fRenderPass)); - if (GrCCCoverageProcessor::RenderPassIsCubic(fRenderPass)) { + if (RenderPass::kCubics == fRenderPass) { caption.appendf(" (%s)", SkCubicTypeName(fCubicType)); } } else { @@ -171,7 +167,7 @@ void CCPRGeometryView::onDrawContent(SkCanvas* canvas) { pointsPaint.setStrokeWidth(8); pointsPaint.setAntiAlias(true); - if (GrCCCoverageProcessor::RenderPassIsCubic(fRenderPass)) { + if (RenderPass::kCubics == fRenderPass) { 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); @@ -184,7 +180,7 @@ void CCPRGeometryView::onDrawContent(SkCanvas* canvas) { SkPaint captionPaint; captionPaint.setTextSize(20); - captionPaint.setColor(SK_ColorBLACK); + captionPaint.setColor(SK_ColorWHITE); captionPaint.setAntiAlias(true); canvas->drawText(caption.c_str(), caption.size(), 10, 30, captionPaint); } @@ -193,7 +189,7 @@ void CCPRGeometryView::updateGpuData() { fTriPointInstances.reset(); fQuadPointInstances.reset(); - if (GrCCCoverageProcessor::RenderPassIsCubic(fRenderPass)) { + if (RenderPass::kCubics == fRenderPass) { double t[2], s[2]; fCubicType = GrPathUtils::getCubicKLM(fPoints, &fCubicKLM, t, s); GrCCGeometry geometry; @@ -217,7 +213,7 @@ void CCPRGeometryView::updateGpuData() { continue; } } - } else if (is_quadratic(fRenderPass)) { + } else if (RenderPass::kQuadratics == fRenderPass) { GrCCGeometry geometry; geometry.beginContour(fPoints[0]); geometry.quadraticTo(fPoints[1], fPoints[3]); @@ -243,18 +239,26 @@ void CCPRGeometryView::updateGpuData() { } void CCPRGeometryView::Op::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::Op::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, fView->fRenderPass, - GrCCCoverageProcessor::WindMethod::kCrossProduct); + GrCCCoverageProcessor proc(rp, renderPass, GrCCCoverageProcessor::WindMethod::kCrossProduct); SkDEBUGCODE(proc.enableDebugVisualizations(kDebugBloat)); SkSTArray<1, GrMesh> mesh; - if (GrCCCoverageProcessor::RenderPassIsCubic(fView->fRenderPass)) { + if (RenderPass::kCubics == renderPass) { sk_sp<GrBuffer> instBuff(rp->createBuffer( fView->fQuadPointInstances.count() * sizeof(QuadPointInstance), kVertex_GrBufferType, kDynamic_GrAccessPattern, @@ -275,17 +279,18 @@ void CCPRGeometryView::Op::onExecute(GrOpFlushState* state) { } GrPipeline pipeline(state->drawOpArgs().fProxy, GrPipeline::ScissorState::kDisabled, - SkBlendMode::kSrcOver); + SkBlendMode::kPlus); if (glGpu) { glGpu->handleDirtyContext(); - GR_GL_CALL(glGpu->glInterface(), PolygonMode(GR_GL_FRONT_AND_BACK, GR_GL_LINE)); + // GR_GL_CALL(glGpu->glInterface(), PolygonMode(GR_GL_FRONT_AND_BACK, GR_GL_LINE)); GR_GL_CALL(glGpu->glInterface(), Enable(GR_GL_LINE_SMOOTH)); } if (!mesh.empty()) { SkASSERT(1 == mesh.count()); - state->rtCommandBuffer()->draw(pipeline, proc, mesh.begin(), nullptr, 1, this->bounds()); + GrGpuRTCommandBuffer* cmdBuff = state->rtCommandBuffer(); + cmdBuff->draw(pipeline, proc, mesh.begin(), nullptr, 1, this->bounds()); } if (glGpu) { @@ -318,7 +323,7 @@ private: SkView::Click* CCPRGeometryView::onFindClickHandler(SkScalar x, SkScalar y, unsigned) { for (int i = 0; i < 4; ++i) { - if (!GrCCCoverageProcessor::RenderPassIsCubic(fRenderPass) && 2 == i) { + if (RenderPass::kCubics != fRenderPass && 2 == i) { continue; } if (fabs(x - fPoints[i].x()) < 20 && fabsf(y - fPoints[i].y()) < 20) { @@ -342,8 +347,8 @@ bool CCPRGeometryView::onQuery(SkEvent* evt) { } SkUnichar unichar; if (SampleCode::CharQ(*evt, &unichar)) { - if (unichar >= '1' && unichar <= '6') { - fRenderPass = RenderPass(unichar - '1'); + if (unichar >= '1' && unichar <= '3') { + fRenderPass = RenderPass((unichar - '1') * 2); this->updateAndInval(); return true; } diff --git a/src/gpu/ccpr/GrCCCoverageProcessor.cpp b/src/gpu/ccpr/GrCCCoverageProcessor.cpp index 686ab5514d..efe003dd60 100644 --- a/src/gpu/ccpr/GrCCCoverageProcessor.cpp +++ b/src/gpu/ccpr/GrCCCoverageProcessor.cpp @@ -25,8 +25,8 @@ void GrCCCoverageProcessor::Shader::emitFragmentCode(const GrCCCoverageProcessor f->codeAppendf("%s = half4(1);", skOutputCoverage); #ifdef SK_DEBUG if (proc.debugVisualizationsEnabled()) { - f->codeAppendf("%s = half4(-%s.a, %s.a, 0, 1);", - skOutputColor, skOutputColor, skOutputColor); + f->codeAppendf("%s = half4(-%s.a, %s.a, 0, abs(%s.a));", + skOutputColor, skOutputColor, skOutputColor, skOutputColor); } #endif } @@ -79,6 +79,23 @@ void GrCCCoverageProcessor::Shader::CalcEdgeCoverageAtBloatVertex(GrGLSLVertexGe s->codeAppendf("%s = (abs(t) != nwidth ? t / nwidth : sign(t)) * -.5 - .5;", outputCoverage); } +void GrCCCoverageProcessor::Shader::CalcEdgeCoveragesAtBloatVertices(GrGLSLVertexGeoBuilder* s, + const char* leftPt, + const char* rightPt, + const char* bloatDir1, + const char* bloatDir2, + const char* outputCoverages) { + // See comments in CalcEdgeCoverageAtBloatVertex. + s->codeAppendf("float2 n = float2(%s.y - %s.y, %s.x - %s.x);", + rightPt, leftPt, leftPt, rightPt); + s->codeAppend ("float nwidth = abs(n.x) + abs(n.y);"); + s->codeAppendf("float2 t = n * float2x2(%s, %s);", bloatDir1, bloatDir2); + s->codeAppendf("for (int i = 0; i < 2; ++i) {"); + s->codeAppendf( "%s[i] = (abs(t[i]) != nwidth ? t[i] / nwidth : sign(t[i])) * -.5 - .5;", + outputCoverages); + s->codeAppendf("}"); +} + int GrCCCoverageProcessor::Shader::DefineSoftSampleLocations(GrGLSLFPFragmentBuilder* f, const char* samplesName) { // Standard DX11 sample locations. @@ -120,10 +137,8 @@ GrGLSLPrimitiveProcessor* GrCCCoverageProcessor::createGLSLInstance(const GrShad std::unique_ptr<Shader> shader; switch (fRenderPass) { case RenderPass::kTriangles: - shader = skstd::make_unique<GrCCTriangleShader>(); - break; case RenderPass::kTriangleCorners: - shader = skstd::make_unique<GrCCTriangleCornerShader>(); + shader = skstd::make_unique<GrCCTriangleShader>(); break; case RenderPass::kQuadratics: shader = skstd::make_unique<GrCCQuadraticHullShader>(); diff --git a/src/gpu/ccpr/GrCCCoverageProcessor.h b/src/gpu/ccpr/GrCCCoverageProcessor.h index c1f85993a1..5c10630f49 100644 --- a/src/gpu/ccpr/GrCCCoverageProcessor.h +++ b/src/gpu/ccpr/GrCCCoverageProcessor.h @@ -54,10 +54,11 @@ public: void set(const SkPoint&, const SkPoint&, const SkPoint&, const Sk2f& trans, float w); }; - // All primitive shapes (triangles and closed, convex bezier curves) require two - // render passes: One to draw a rough outline of the shape, and a second pass to touch up the - // corners. Here we enumerate every render pass needed in order to produce a complete - // coverage count mask. This is an exhaustive list of all ccpr coverage shaders. + // 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, @@ -69,6 +70,11 @@ public: static bool RenderPassIsCubic(RenderPass); static const char* RenderPassName(RenderPass); + constexpr static bool DoesRenderPass(RenderPass renderPass, const GrCaps& caps) { + return RenderPass::kTriangleCorners != renderPass || + caps.shaderCaps()->geometryShaderSupport(); + } + 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. @@ -81,6 +87,7 @@ public: , fWindMethod(windMethod) , fImpl(rp->caps()->shaderCaps()->geometryShaderSupport() ? Impl::kGeometryShader : Impl::kVertexShader) { + SkASSERT(DoesRenderPass(pass, *rp->caps())); if (Impl::kGeometryShader == fImpl) { this->initGS(); } else { @@ -88,18 +95,6 @@ public: } } - // Appends a GrMesh that will draw the provided instances. The instanceBuffer must be an array - // of either TriPointInstance or QuadPointInstance, depending on this processor's RendererPass, - // with coordinates in the desired shape's final atlas-space position. - void appendMesh(GrBuffer* instanceBuffer, int instanceCount, int baseInstance, - SkTArray<GrMesh>* out) { - if (Impl::kGeometryShader == fImpl) { - this->appendGSMesh(instanceBuffer, instanceCount, baseInstance, out); - } else { - this->appendVSMesh(instanceBuffer, instanceCount, baseInstance, out); - } - } - // GrPrimitiveProcessor overrides. const char* name() const override { return RenderPassName(fRenderPass); } SkString dumpInfo() const override { @@ -116,6 +111,18 @@ public: float debugBloat() const { SkASSERT(this->debugVisualizationsEnabled()); return fDebugBloat; } #endif + // Appends a GrMesh that will draw the provided instances. The instanceBuffer must be an array + // of either TriPointInstance or QuadPointInstance, depending on this processor's RendererPass, + // with coordinates in the desired shape's final atlas-space position. + void appendMesh(GrBuffer* instanceBuffer, int instanceCount, int baseInstance, + SkTArray<GrMesh>* out) const { + if (Impl::kGeometryShader == fImpl) { + this->appendGSMesh(instanceBuffer, instanceCount, baseInstance, out); + } else { + this->appendVSMesh(instanceBuffer, instanceCount, baseInstance, out); + } + } + // The Shader provides code to calculate each pixel's coverage in a RenderPass. It also // provides details about shape-specific geometry. class Shader { @@ -169,6 +176,13 @@ public: const char* rightPt, const char* rasterVertexDir, const char* outputCoverage); + // Calculates an edge's coverage at two conservative raster vertices. + // (See CalcEdgeCoverageAtBloatVertex). + static void CalcEdgeCoveragesAtBloatVertices(GrGLSLVertexGeoBuilder*, const char* leftPt, + const char* rightPt, const char* bloatDir1, + const char* bloatDir2, + const char* outputCoverages); + virtual ~Shader() {} protected: diff --git a/src/gpu/ccpr/GrCCCoverageProcessor_GSImpl.cpp b/src/gpu/ccpr/GrCCCoverageProcessor_GSImpl.cpp index d9febc0e66..b210aa1615 100644 --- a/src/gpu/ccpr/GrCCCoverageProcessor_GSImpl.cpp +++ b/src/gpu/ccpr/GrCCCoverageProcessor_GSImpl.cpp @@ -76,7 +76,8 @@ protected: SkSTArray<2, GrShaderVar> emitArgs; const char* position = emitArgs.emplace_back("position", kFloat2_GrSLType).c_str(); const char* coverage = nullptr; - if (RenderPass::kTriangles == proc.fRenderPass) { + if (RenderPass::kTriangles == proc.fRenderPass || + RenderPass::kTriangleCorners == proc.fRenderPass) { coverage = emitArgs.emplace_back("coverage", kHalf_GrSLType).c_str(); } g->emitFunction(kVoid_GrSLType, "emitVertex", emitArgs.count(), emitArgs.begin(), [&]() { @@ -116,7 +117,7 @@ protected: * coverage ramp from -1 to 0. These edge coverage values convert jagged conservative raster edges * into smooth, antialiased ones. * - * The final corners get touched up in a later step by GSCornerImpl. + * The final corners get touched up in a later step by GSTriangleCornerImpl. */ class GSTriangleImpl : public GrCCCoverageProcessor::GSImpl { public: @@ -124,6 +125,10 @@ public: void onEmitGeometryShader(GrGLSLGeometryBuilder* g, const GrShaderVar& wind, const char* emitVertexFn) const override { + Shader::GeometryVars vars; + fShader->emitSetupCode(g, "pts", nullptr, wind.c_str(), &vars); + SkASSERT(!vars.fHullVars.fAlternatePoints); + // Visualize the input triangle as upright and equilateral, with a flat base. Paying special // attention to wind, we can identify the points as top, bottom-left, and bottom-right. // @@ -212,6 +217,61 @@ public: }; /** + * Generates conservative rasters around triangle corners (aka pixel-size boxes) and calculates + * coverage ramps that fix up the coverage values written by GSTriangleImpl. + */ +class GSTriangleCornerImpl : public GrCCCoverageProcessor::GSImpl { +public: + GSTriangleCornerImpl(std::unique_ptr<Shader> shader) : GSImpl(std::move(shader)) {} + + void onEmitGeometryShader(GrGLSLGeometryBuilder* g, const GrShaderVar& wind, + const char* emitVertexFn) const override { + Shader::GeometryVars vars; + fShader->emitSetupCode(g, "pts", nullptr, wind.c_str(), &vars); + SkASSERT(!vars.fHullVars.fAlternatePoints); + + g->codeAppendf("float2 corner = pts[sk_InvocationID];"); + g->codeAppendf("float2 left = pts[(sk_InvocationID + (%s > 0 ? 2 : 1)) %% 3];", + wind.c_str()); + g->codeAppendf("float2 right = pts[(sk_InvocationID + (%s > 0 ? 1 : 2)) %% 3];", + wind.c_str()); + + // Find "outbloat" and "crossbloat" at our corner. The outbloat points diagonally out of the + // triangle, in the direction that should ramp to zero coverage. The crossbloat runs + // perpindicular to outbloat, and ramps from left-edge coverage to right-edge coverage. + g->codeAppend ("float2 leftdir = normalize(corner - left);"); + g->codeAppend ("float2 rightdir = normalize(right - corner);"); + g->codeAppend ("float2 outbloat = float2(leftdir.x > rightdir.x ? +1 : -1, " + "leftdir.y > rightdir.y ? +1 : -1);"); + g->codeAppend ("float2 crossbloat = float2(-outbloat.y, +outbloat.x);"); + + g->codeAppend ("half2 left_coverages; {"); + Shader::CalcEdgeCoveragesAtBloatVertices(g, "left", "corner", "outbloat", "crossbloat", + "left_coverages"); + g->codeAppend ("}"); + + g->codeAppend ("half2 right_coverages; {"); + Shader::CalcEdgeCoveragesAtBloatVertices(g, "corner", "right", "outbloat", "-crossbloat", + "right_coverages"); + g->codeAppend ("}"); + + // Emit a corner box that erases whatever coverage was written previously, and replaces it + // using linearly-interpolated values that ramp to zero in bloat vertices that fall outside + // the triangle. + // + // NOTE: Since this is not a linear mapping, it is important that the box's diagonal shared + // edge points out of the triangle as much as possible. + g->codeAppendf("%s(corner - crossbloat * bloat, -right_coverages[1]);", emitVertexFn); + g->codeAppendf("%s(corner + outbloat * bloat, " + "-1 - left_coverages[0] - right_coverages[0]);", emitVertexFn); + g->codeAppendf("%s(corner - outbloat * bloat, 0);", emitVertexFn); + g->codeAppendf("%s(corner + crossbloat * bloat, -left_coverages[1]);", emitVertexFn); + + g->configure(InputType::kLines, OutputType::kTriangleStrip, 4, 3); + } +}; + +/** * Generates a conservative raster around a convex quadrilateral that encloses a cubic or quadratic. */ class GSHull4Impl : public GrCCCoverageProcessor::GSImpl { @@ -317,12 +377,10 @@ void GrCCCoverageProcessor::initGS() { this->addVertexAttrib("x_or_y_values", kFloat4_GrVertexAttribType); SkASSERT(sizeof(QuadPointInstance) == this->getVertexStride() * 2); SkASSERT(offsetof(QuadPointInstance, fY) == this->getVertexStride()); - GR_STATIC_ASSERT(0 == offsetof(QuadPointInstance, fX)); } else { this->addVertexAttrib("x_or_y_values", kFloat3_GrVertexAttribType); SkASSERT(sizeof(TriPointInstance) == this->getVertexStride() * 2); SkASSERT(offsetof(TriPointInstance, fY) == this->getVertexStride()); - GR_STATIC_ASSERT(0 == offsetof(TriPointInstance, fX)); } this->setWillUseGeoShader(); } @@ -344,7 +402,7 @@ GrGLSLPrimitiveProcessor* GrCCCoverageProcessor::createGSImpl(std::unique_ptr<Sh case RenderPass::kTriangles: return new GSTriangleImpl(std::move(shadr)); case RenderPass::kTriangleCorners: - return new GSCornerImpl(std::move(shadr), 3); + return new GSTriangleCornerImpl(std::move(shadr)); case RenderPass::kQuadratics: case RenderPass::kCubics: return new GSHull4Impl(std::move(shadr)); diff --git a/src/gpu/ccpr/GrCCCoverageProcessor_VSImpl.cpp b/src/gpu/ccpr/GrCCCoverageProcessor_VSImpl.cpp index 144a4a5d58..28ed6fc319 100644 --- a/src/gpu/ccpr/GrCCCoverageProcessor_VSImpl.cpp +++ b/src/gpu/ccpr/GrCCCoverageProcessor_VSImpl.cpp @@ -89,10 +89,11 @@ protected: typedef GrGLSLGeometryProcessor INHERITED; }; -static constexpr int kVertexData_LeftNeighborIdShift = 9; -static constexpr int kVertexData_RightNeighborIdShift = 7; -static constexpr int kVertexData_BloatIdxShift = 5; -static constexpr int kVertexData_InvertCoverageBit = 1 << 4; +static constexpr int kVertexData_LeftNeighborIdShift = 10; +static constexpr int kVertexData_RightNeighborIdShift = 8; +static constexpr int kVertexData_BloatIdxShift = 6; +static constexpr int kVertexData_InvertNegativeCoverageBit = 1 << 5; +static constexpr int kVertexData_IsCornerBit = 1 << 4; static constexpr int kVertexData_IsEdgeBit = 1 << 3; static constexpr int kVertexData_IsHullBit = 1 << 2; @@ -120,10 +121,15 @@ static constexpr int32_t edge_vertex_data(int32_t edgeID, int32_t endptIdx, int3 0 == endptIdx ? (edgeID + 1) % n : edgeID, bloatIdx, 0 == endptIdx ? edgeID : (edgeID + 1) % n, kVertexData_IsEdgeBit | - (!endptIdx ? kVertexData_InvertCoverageBit : 0)); + (!endptIdx ? kVertexData_InvertNegativeCoverageBit : 0)); } -static constexpr int32_t kHull3AndEdgeVertices[] = { +static constexpr int32_t triangle_corner_vertex_data(int32_t cornerID, int32_t bloatIdx) { + return pack_vertex_data((cornerID + 2) % 3, (cornerID + 1) % 3, bloatIdx, cornerID, + kVertexData_IsCornerBit); +} + +static constexpr int32_t kTriangleVertices[] = { hull_vertex_data(0, 0, 3), hull_vertex_data(0, 1, 3), hull_vertex_data(0, 2, 3), @@ -154,21 +160,39 @@ static constexpr int32_t kHull3AndEdgeVertices[] = { edge_vertex_data(2, 1, 0, 3), edge_vertex_data(2, 1, 1, 3), edge_vertex_data(2, 1, 2, 3), + + triangle_corner_vertex_data(0, 0), + triangle_corner_vertex_data(0, 1), + triangle_corner_vertex_data(0, 2), + triangle_corner_vertex_data(0, 3), + + triangle_corner_vertex_data(1, 0), + triangle_corner_vertex_data(1, 1), + triangle_corner_vertex_data(1, 2), + triangle_corner_vertex_data(1, 3), + + triangle_corner_vertex_data(2, 0), + triangle_corner_vertex_data(2, 1), + triangle_corner_vertex_data(2, 2), + triangle_corner_vertex_data(2, 3), }; -GR_DECLARE_STATIC_UNIQUE_KEY(gHull3AndEdgeVertexBufferKey); +GR_DECLARE_STATIC_UNIQUE_KEY(gTriangleVertexBufferKey); static constexpr uint16_t kRestartStrip = 0xffff; -static constexpr uint16_t kHull3AndEdgeIndicesAsStrips[] = { +static constexpr uint16_t kTriangleIndicesAsStrips[] = { 1, 2, 0, 3, 8, kRestartStrip, // First corner and main body of the hull. 4, 5, 3, 6, 8, 7, kRestartStrip, // Opposite side and corners of the hull. 10, 9, 11, 14, 12, 13, kRestartStrip, // First edge. 16, 15, 17, 20, 18, 19, kRestartStrip, // Second edge. - 22, 21, 23, 26, 24, 25 // Third edge. + 22, 21, 23, 26, 24, 25, kRestartStrip, // Third edge. + 27, 28, 30, 29, kRestartStrip, // First corner. + 31, 32, 34, 33, kRestartStrip, // Second corner. + 35, 36, 38, 37 // Third corner. }; -static constexpr uint16_t kHull3AndEdgeIndicesAsTris[] = { +static constexpr uint16_t kTriangleIndicesAsTris[] = { // First corner and main body of the hull. 1, 2, 0, 2, 3, 0, @@ -197,9 +221,21 @@ static constexpr uint16_t kHull3AndEdgeIndicesAsTris[] = { 21, 26, 23, 23, 26, 24, 26, 25, 24, + + // First corner. + 27, 28, 30, + 28, 29, 30, + + // Second corner. + 31, 32, 34, + 32, 33, 34, + + // Third corner. + 35, 36, 38, + 36, 37, 38, }; -GR_DECLARE_STATIC_UNIQUE_KEY(gHull3AndEdgeIndexBufferKey); +GR_DECLARE_STATIC_UNIQUE_KEY(gTriangleIndexBufferKey); static constexpr int32_t kHull4Vertices[] = { hull_vertex_data(0, 0, 4), @@ -243,19 +279,21 @@ static constexpr uint16_t kHull4IndicesAsTris[] = { GR_DECLARE_STATIC_UNIQUE_KEY(gHull4IndexBufferKey); + /** - * Generates a conservative raster hull around a convex polygon. For triangles we generate - * additional conservative rasters around the edges and calculate coverage ramps. - * - * Triangle rough outlines are drawn in two steps: (1) draw a conservative raster of the entire - * triangle, with a coverage of +1, and (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. + * 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. * - * Curve rough outlines are just the conservative raster of a convex quadrilateral that encloses the - * curve. The Shader takes care of everything else for now. + * 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. * - * The final corners get touched up in a later step by VSCornerImpl. + * 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: @@ -284,10 +322,9 @@ public: // 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. For more details on conservative raster, see: + // 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 - // - // Triangle edges are also handled here using the same concept (see kHull3AndEdgeVertices). v->codeAppendf("float2 corner = %s[clockwise_indices & 3];", hullPts); v->codeAppendf("float2 left = %s[clockwise_indices >> %i];", hullPts, kVertexData_LeftNeighborIdShift); @@ -304,48 +341,97 @@ public: v->codeAppend ("bool2 left_right_notequal = notEqual(leftbloat, rightbloat);"); - // At each corner of the polygon, our hull will have either 1, 2, or 3 vertices. We begin - // with the first hull vertex (leftbloat), then continue rotating 90 degrees clockwise until - // we reach the desired vertex for this invocation. Corners with less than 3 corresponding - // hull vertices will result in redundant vertices and degenerate triangles. v->codeAppend ("float2 bloatdir = leftbloat;"); + + if (3 == fNumSides) { // Only triangles emit corner boxes. + v->codeAppendf("if (0 != (%s & %i)) {", // Are we a corner? + proc.getAttrib(kAttribIdx_VertexData).fName, kVertexData_IsCornerBit); + + // For corner boxes, we hack 'left_right_notequal' to [true, true]. + // This causes the upcoming code to always rotate, which is the right + // thing for corners. + v->codeAppendf( "left_right_notequal = bool2(true, true);"); + + // In corner boxes, all 4 coverage values will not map linearly, so + // it is important to rotate the box so its diagonal shared edge + // points out of the triangle, in the direction that ramps to zero. + v->codeAppend ( "float2 bisect = normalize(corner - right) +" + "normalize(corner - left);"); + v->codeAppend ( "if (sign(bisect) == sign(leftbloat)) {"); + v->codeAppend ( "bloatdir = float2(+bloatdir.y, -bloatdir.x);"); + v->codeAppend ( "}"); + 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) {"); + if (3 == fNumSides) { // Only triangles emit corner boxes. + 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);"); + 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);"); + 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"); + // For triangles, we also emit coverage in order to handle edges and corners. const char* coverage = nullptr; if (3 == fNumSides) { - v->codeAppend ("half coverage;"); - Shader::CalcEdgeCoverageAtBloatVertex(v, "left", "corner", "bloatdir", "coverage"); - v->codeAppendf("if (0 != (%s & %i)) {", // Are we the opposite endpoint of an edge? + // The hull has a coverage of +1 all around. + v->codeAppend ("half coverage = +1;"); + + v->codeAppendf("if (0 != (%s & %i)) {", // Are we an edge OR corner? proc.getAttrib(kAttribIdx_VertexData).fName, - kVertexData_InvertCoverageBit); - v->codeAppend ( "coverage = -1 - coverage;"); + kVertexData_IsEdgeBit | kVertexData_IsCornerBit); + Shader::CalcEdgeCoverageAtBloatVertex(v, "left", "corner", "bloatdir", "coverage"); v->codeAppend ("}"); - v->codeAppendf("if (0 != (%s & %i)) {", // Are we a hull vertex? - proc.getAttrib(kAttribIdx_VertexData).fName, kVertexData_IsHullBit); - v->codeAppend ( "coverage = +1;"); // Hull coverage is +1 all around. + v->codeAppendf("if (0 != (%s & %i)) {", // Are we a corner? + proc.getAttrib(kAttribIdx_VertexData).fName, kVertexData_IsCornerBit); + // Corner boxes erase whatever coverage was written previously, and + // replace it with linearly-interpolated values that ramp to zero in + // the diagonal that points out of the triangle, and ramp from + // left-edge coverage to right-edge coverage in the other diagonal. + v->codeAppend ( "half left_coverage = coverage;"); + v->codeAppend ( "half right_coverage;"); + Shader::CalcEdgeCoverageAtBloatVertex(v, "corner", "right", "bloatdir", + "right_coverage"); + v->codeAppend ( "coverage = (1 == bloatidx) ? -1 : 0;"); + v->codeAppend ( "if (((bloatidx + 3) & 3) < 2) {"); + v->codeAppend ( "coverage -= left_coverage;"); + v->codeAppend ( "}"); + v->codeAppend ( "if (bloatidx < 2) {"); + v->codeAppend ( "coverage -= right_coverage;"); + v->codeAppend ( "}"); + v->codeAppend ("}"); + + v->codeAppendf("if (0 != (%s & %i)) {", // Invert coverage? + proc.getAttrib(kAttribIdx_VertexData).fName, + kVertexData_InvertNegativeCoverageBit); + v->codeAppend ( "coverage = -1 - coverage;"); v->codeAppend ("}"); coverage = "coverage"; } - v->codeAppend ("float2 vertex = corner + bloatdir * bloat;"); - gpArgs->fPositionVar.set(kFloat2_GrSLType, "vertex"); - return coverage; } @@ -355,8 +441,7 @@ private: static constexpr uint16_t kCornerIndicesAsStrips[] = { 0, 1, 2, 3, kRestartStrip, // First corner. - 4, 5, 6, 7, kRestartStrip, // Second corner. - 8, 9, 10, 11 // Third corner. + 4, 5, 6, 7 // Second corner. }; static constexpr uint16_t kCornerIndicesAsTris[] = { @@ -367,10 +452,6 @@ static constexpr uint16_t kCornerIndicesAsTris[] = { // Second corner. 4, 5, 6, 5, 7, 6, - - // Third corner. - 8, 9, 10, - 9, 11, 10, }; GR_DECLARE_STATIC_UNIQUE_KEY(gCornerIndexBufferKey); @@ -403,28 +484,31 @@ void GrCCCoverageProcessor::initVS(GrResourceProvider* rp) { switch (fRenderPass) { case RenderPass::kTriangles: { - GR_DEFINE_STATIC_UNIQUE_KEY(gHull3AndEdgeVertexBufferKey); + GR_DEFINE_STATIC_UNIQUE_KEY(gTriangleVertexBufferKey); fVertexBuffer = rp->findOrMakeStaticBuffer(kVertex_GrBufferType, - sizeof(kHull3AndEdgeVertices), - kHull3AndEdgeVertices, - gHull3AndEdgeVertexBufferKey); - GR_DEFINE_STATIC_UNIQUE_KEY(gHull3AndEdgeIndexBufferKey); + sizeof(kTriangleVertices), + kTriangleVertices, + gTriangleVertexBufferKey); + GR_DEFINE_STATIC_UNIQUE_KEY(gTriangleIndexBufferKey); if (caps.usePrimitiveRestart()) { fIndexBuffer = rp->findOrMakeStaticBuffer(kIndex_GrBufferType, - sizeof(kHull3AndEdgeIndicesAsStrips), - kHull3AndEdgeIndicesAsStrips, - gHull3AndEdgeIndexBufferKey); - fNumIndicesPerInstance = SK_ARRAY_COUNT(kHull3AndEdgeIndicesAsStrips); + sizeof(kTriangleIndicesAsStrips), + kTriangleIndicesAsStrips, + gTriangleIndexBufferKey); + fNumIndicesPerInstance = SK_ARRAY_COUNT(kTriangleIndicesAsStrips); } else { fIndexBuffer = rp->findOrMakeStaticBuffer(kIndex_GrBufferType, - sizeof(kHull3AndEdgeIndicesAsTris), - kHull3AndEdgeIndicesAsTris, - gHull3AndEdgeIndexBufferKey); - fNumIndicesPerInstance = SK_ARRAY_COUNT(kHull3AndEdgeIndicesAsTris); + sizeof(kTriangleIndicesAsTris), + kTriangleIndicesAsTris, + gTriangleIndexBufferKey); + fNumIndicesPerInstance = SK_ARRAY_COUNT(kTriangleIndicesAsTris); } break; } + case RenderPass::kTriangleCorners: + SK_ABORT("RenderPass::kTriangleCorners is unused by VSImpl."); + case RenderPass::kQuadratics: case RenderPass::kCubics: { GR_DEFINE_STATIC_UNIQUE_KEY(gHull4VertexBufferKey); @@ -447,7 +531,6 @@ void GrCCCoverageProcessor::initVS(GrResourceProvider* rp) { break; } - case RenderPass::kTriangleCorners: case RenderPass::kQuadraticCorners: case RenderPass::kCubicCorners: { GR_DEFINE_STATIC_UNIQUE_KEY(gCornerIndexBufferKey); @@ -463,12 +546,9 @@ void GrCCCoverageProcessor::initVS(GrResourceProvider* rp) { kCornerIndicesAsTris, gCornerIndexBufferKey); fNumIndicesPerInstance = SK_ARRAY_COUNT(kCornerIndicesAsTris); - } - if (RenderPass::kTriangleCorners != fRenderPass) { - fNumIndicesPerInstance = fNumIndicesPerInstance * 2/3; - } - break; - } + } + break; + } } if (RenderPassIsCubic(fRenderPass) || WindMethod::kInstanceData == fWindMethod) { diff --git a/src/gpu/ccpr/GrCCPathParser.cpp b/src/gpu/ccpr/GrCCPathParser.cpp index cb5311f3a1..1114367ac7 100644 --- a/src/gpu/ccpr/GrCCPathParser.cpp +++ b/src/gpu/ccpr/GrCCPathParser.cpp @@ -514,7 +514,8 @@ void GrCCPathParser::drawCoverageCount(GrOpFlushState* flushState, CoverageCount this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kTriangles, WindMethod::kCrossProduct, &PrimitiveTallies::fTriangles, drawBounds); this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kTriangleCorners, - WindMethod::kCrossProduct, &PrimitiveTallies::fTriangles, drawBounds); + WindMethod::kCrossProduct, &PrimitiveTallies::fTriangles, + drawBounds); // Might get skipped. } if (batchTotalCounts.fWoundTriangles) { @@ -523,7 +524,7 @@ void GrCCPathParser::drawCoverageCount(GrOpFlushState* flushState, CoverageCount drawBounds); this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kTriangleCorners, WindMethod::kInstanceData, &PrimitiveTallies::fWoundTriangles, - drawBounds); + drawBounds); // Might get skipped. } if (batchTotalCounts.fQuadratics) { @@ -549,6 +550,10 @@ void GrCCPathParser::drawRenderPass(GrOpFlushState* flushState, const GrPipeline 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()); diff --git a/src/gpu/ccpr/GrCCTriangleShader.cpp b/src/gpu/ccpr/GrCCTriangleShader.cpp deleted file mode 100644 index e086201b42..0000000000 --- a/src/gpu/ccpr/GrCCTriangleShader.cpp +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright 2017 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "GrCCTriangleShader.h" - -#include "glsl/GrGLSLFragmentShaderBuilder.h" -#include "glsl/GrGLSLVertexGeoBuilder.h" - -using Shader = GrCCCoverageProcessor::Shader; - -void GrCCTriangleShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler, - GrGLSLVarying::Scope scope, SkString* code, - const char* /*position*/, const char* inputCoverage, - const char* wind) { - SkASSERT(inputCoverage); - fCoverageTimesWind.reset(kHalf_GrSLType, scope); - varyingHandler->addVarying("coverage_times_wind", &fCoverageTimesWind); - code->appendf("%s = %s * %s;", OutName(fCoverageTimesWind), inputCoverage, wind); -} - -void GrCCTriangleShader::onEmitFragmentCode(GrGLSLFPFragmentBuilder* f, - const char* outputCoverage) const { - f->codeAppendf("%s = %s;", outputCoverage, fCoverageTimesWind.fsIn()); -} - -void GrCCTriangleCornerShader::emitSetupCode(GrGLSLVertexGeoBuilder* s, const char* pts, - const char* repetitionID, const char* wind, - GeometryVars* vars) const { - s->codeAppendf("float2 corner = %s[%s];", pts, repetitionID); - vars->fCornerVars.fPoint = "corner"; - - s->codeAppendf("float2x2 vectors = float2x2(corner - %s[0 != %s ? %s - 1 : 2], " - "corner - %s[2 != %s ? %s + 1 : 0]);", - pts, repetitionID, repetitionID, pts, repetitionID, - repetitionID); - - // Make sure neither vector is 0 to avoid a divide-by-zero. Wind will be zero anyway if this - // is the case, so whatever we output won't have any effect as long it isn't NaN or Inf. - s->codeAppend ("for (int i = 0; i < 2; ++i) {"); - s->codeAppend ( "vectors[i] = (vectors[i] != float2(0)) ? vectors[i] : float2(1);"); - s->codeAppend ("}"); - - // Find the vector that bisects the region outside the incoming edges. Each edge is - // responsible to subtract the outside region on its own the side of the bisector. - s->codeAppendf("float2 leftdir = normalize(vectors[%s > 0 ? 0 : 1]);", wind); - s->codeAppendf("float2 rightdir = normalize(vectors[%s > 0 ? 1 : 0]);", wind); - s->codeAppend ("float2 bisect = dot(leftdir, rightdir) >= 0 ? " - "leftdir + rightdir : " - "float2(leftdir.y - rightdir.y, rightdir.x - leftdir.x);"); - - // In ccpr we don't calculate exact geometric pixel coverage. What the distance-to-edge - // method actually finds is coverage inside a logical "AA box", one that is rotated inline - // with the edge, and in our case, up-scaled to circumscribe the actual pixel. Below we set - // up transformations into normalized logical AA box space for both incoming edges. These - // will tell the fragment shader where the corner is located within each edge's AA box. - s->declareGlobal(fAABoxMatrices); - s->declareGlobal(fAABoxTranslates); - s->declareGlobal(fGeoShaderBisects); - s->codeAppendf("for (int i = 0; i < 2; ++i) {"); - // The X component runs parallel to the edge (i.e. distance to the corner). - s->codeAppendf( "float2 n = -vectors[%s > 0 ? i : 1 - i];", wind); - s->codeAppend ( "float nwidth = (abs(n.x) + abs(n.y)) * (bloat * 2);"); - s->codeAppend ( "n /= nwidth;"); // nwidth != 0 because both vectors != 0. - s->codeAppendf( "%s[i][0] = n;", fAABoxMatrices.c_str()); - s->codeAppendf( "%s[i][0] = -dot(n, corner) + .5;", fAABoxTranslates.c_str()); - - // The Y component runs perpendicular to the edge (i.e. distance-to-edge). - s->codeAppend ( "n = (i == 0) ? float2(-n.y, n.x) : float2(n.y, -n.x);"); - s->codeAppendf( "%s[i][1] = n;", fAABoxMatrices.c_str()); - s->codeAppendf( "%s[i][1] = -dot(n, corner) + .5;", fAABoxTranslates.c_str()); - - // Translate the bisector into logical AA box space. - // NOTE: Since the region outside two edges of a convex shape is in [180 deg, 360 deg], the - // bisector will therefore be in [90 deg, 180 deg]. Or, x >= 0 and y <= 0 in AA box space. - s->codeAppendf( "%s[i] = -bisect * %s[i];", - fGeoShaderBisects.c_str(), fAABoxMatrices.c_str()); - s->codeAppend ("}"); -} - -void GrCCTriangleCornerShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler, - GrGLSLVarying::Scope scope, SkString* code, - const char* position, const char* inputCoverage, - const char* wind) { - using Interpolation = GrGLSLVaryingHandler::Interpolation; - SkASSERT(!inputCoverage); - - fCornerLocationInAABoxes.reset(kFloat2x2_GrSLType, scope); - varyingHandler->addVarying("corner_location_in_aa_boxes", &fCornerLocationInAABoxes); - - fBisectInAABoxes.reset(kFloat2x2_GrSLType, scope); - varyingHandler->addVarying("bisect_in_aa_boxes", &fBisectInAABoxes, Interpolation::kCanBeFlat); - - code->appendf("for (int i = 0; i < 2; ++i) {"); - code->appendf( "%s[i] = %s * %s[i] + %s[i];", - OutName(fCornerLocationInAABoxes), position, fAABoxMatrices.c_str(), - fAABoxTranslates.c_str()); - code->appendf( "%s[i] = %s[i];", OutName(fBisectInAABoxes), fGeoShaderBisects.c_str()); - code->appendf("}"); - - fWindTimesHalf.reset(kHalf_GrSLType, scope); - varyingHandler->addVarying("wind_times_half", &fWindTimesHalf, Interpolation::kCanBeFlat); - code->appendf("%s = %s * .5;", OutName(fWindTimesHalf), wind); -} - -void GrCCTriangleCornerShader::onEmitFragmentCode(GrGLSLFPFragmentBuilder* f, - const char* outputCoverage) const { - // By the time we reach this shader, the pixel is in the following state: - // - // 1. The hull shader has emitted a coverage of 1. - // 2. Both edges have subtracted the area on their outside. - // - // This generally works, but it is a problem for corner pixels. There is a region within - // corner pixels that is outside both edges at the same time. This means the region has been - // double subtracted (once by each edge). The purpose of this shader is to fix these corner - // pixels. - // - // More specifically, each edge redoes its coverage analysis so that it only subtracts the - // outside area that falls on its own side of the bisector line. - // - // NOTE: unless the edges fall on multiples of 90 deg from one another, they will have - // different AA boxes. (For an explanation of AA boxes, see comments in - // onEmitGeometryShader.) This means the coverage analysis will only be approximate. It - // seems acceptable, but if we want exact coverage we will need to switch to a more - // expensive model. - f->codeAppendf("for (int i = 0; i < 2; ++i) {"); // Loop through both edges. - f->codeAppendf( "half2 corner = %s[i];", fCornerLocationInAABoxes.fsIn()); - f->codeAppendf( "half2 bisect = %s[i];", fBisectInAABoxes.fsIn()); - - // Find the point at which the bisector exits the logical AA box. - // (The inequality works because bisect.x is known >= 0 and bisect.y is known <= 0.) - f->codeAppendf( "half2 d = half2(1 - corner.x, -corner.y);"); - f->codeAppendf( "half T = d.y * bisect.x >= d.x * bisect.y ? d.y / bisect.y " - ": d.x / bisect.x;"); - f->codeAppendf( "half2 exit = corner + bisect * T;"); - - // These lines combined (and the final multiply by .5) accomplish the following: - // 1. Add back the area beyond the corner that was subtracted out previously. - // 2. Subtract out the area beyond the corner, but under the bisector. - // The other edge will take care of the area on its own side of the bisector. - f->codeAppendf( "%s += (2 - corner.x - exit.x) * corner.y;", outputCoverage); - f->codeAppendf( "%s += (corner.x - 1) * exit.y;", outputCoverage); - f->codeAppendf("}"); - - f->codeAppendf("%s *= %s;", outputCoverage, fWindTimesHalf.fsIn()); -} diff --git a/src/gpu/ccpr/GrCCTriangleShader.h b/src/gpu/ccpr/GrCCTriangleShader.h index 5f33b077cd..c7fbefd3b4 100644 --- a/src/gpu/ccpr/GrCCTriangleShader.h +++ b/src/gpu/ccpr/GrCCTriangleShader.h @@ -9,39 +9,28 @@ #define GrCCTriangleShader_DEFINED #include "ccpr/GrCCCoverageProcessor.h" +#include "glsl/GrGLSLFragmentShaderBuilder.h" /** - * Steps 1 & 2: Draw the triangle's conservative raster hull with a coverage of +1, then smooth the - * edges by drawing the conservative rasters of all 3 edges and interpolating from - * coverage=-1 on the outside to coverage=0 on the inside. The Impl may choose to - * implement these steps in either one or two actual render passes. + * This class renders AA triangles. It relies on the coverage processor to set up the geometry and + * provide coverage values at each vertex, then simply interpolates these values in the fragment + * shader. */ class GrCCTriangleShader : public GrCCCoverageProcessor::Shader { - void onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code, - const char* position, const char* inputCoverage, const char* wind) override; - void onEmitFragmentCode(GrGLSLFPFragmentBuilder*, const char* outputCoverage) const override; + void onEmitVaryings(GrGLSLVaryingHandler* varyingHandler, GrGLSLVarying::Scope scope, + SkString* code, const char* /*position*/, const char* inputCoverage, + const char* wind) override { + SkASSERT(inputCoverage); + fCoverageTimesWind.reset(kHalf_GrSLType, scope); + varyingHandler->addVarying("coverage_times_wind", &fCoverageTimesWind); + code->appendf("%s = %s * %s;", OutName(fCoverageTimesWind), inputCoverage, wind); + } - GrGLSLVarying fCoverageTimesWind; -}; - -/** - * Step 3: Touch up the corner pixels. Here we fix the simple distance-to-edge coverage analysis - * done previously so that it takes into account the region that is outside both edges at - * the same time. - */ -class GrCCTriangleCornerShader : public GrCCCoverageProcessor::Shader { - void emitSetupCode(GrGLSLVertexGeoBuilder*, const char* pts, const char* repetitionID, - const char* wind, GeometryVars*) const override; - void onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code, - const char* position, const char* inputCoverage, const char* wind) override; - void onEmitFragmentCode(GrGLSLFPFragmentBuilder* f, const char* outputCoverage) const override; + void onEmitFragmentCode(GrGLSLFPFragmentBuilder* f, const char* outputCoverage) const override { + f->codeAppendf("%s = %s;", outputCoverage, fCoverageTimesWind.fsIn()); + } - GrShaderVar fAABoxMatrices{"aa_box_matrices", kFloat2x2_GrSLType, 2}; - GrShaderVar fAABoxTranslates{"aa_box_translates", kFloat2_GrSLType, 2}; - GrShaderVar fGeoShaderBisects{"bisects", kFloat2_GrSLType, 2}; - GrGLSLVarying fCornerLocationInAABoxes; - GrGLSLVarying fBisectInAABoxes; - GrGLSLVarying fWindTimesHalf; + GrGLSLVarying fCoverageTimesWind; }; #endif |