| /* |
| * 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 "GrCCCoverageProcessor.h" |
| |
| #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. |
| */ |
| class GrCCCoverageProcessor::VSImpl : public GrGLSLGeometryProcessor { |
| protected: |
| VSImpl(std::unique_ptr<Shader> shader) : fShader(std::move(shader)) {} |
| |
| void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor&, |
| FPCoordTransformIter&& transformIter) final { |
| this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter); |
| } |
| |
| 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.debugVisualizationsEnabled()) { |
| bloat *= proc.debugBloat(); |
| } |
| #endif |
| v->defineConstant("bloat", bloat); |
| |
| const char* coverage = this->emitVertexPosition(proc, v, gpArgs); |
| SkASSERT(kFloat2_GrSLType == gpArgs->fPositionVar.getType()); |
| |
| GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler; |
| SkString varyingCode; |
| fShader->emitVaryings(varyingHandler, GrGLSLVarying::Scope::kVertToFrag, &varyingCode, |
| gpArgs->fPositionVar.c_str(), coverage, "wind"); |
| v->codeAppend(varyingCode.c_str()); |
| |
| varyingHandler->emitAttributes(proc); |
| SkASSERT(!args.fFPCoordTransformHandler->nextCoordTransform()); |
| |
| // Fragment shader. |
| fShader->emitFragmentCode(proc, args.fFragBuilder, args.fOutputColor, args.fOutputCoverage); |
| } |
| |
| virtual const char* emitVertexPosition(const GrCCCoverageProcessor&, GrGLSLVertexBuilder*, |
| GrGPArgs*) const = 0; |
| |
| virtual ~VSImpl() {} |
| |
| const std::unique_ptr<Shader> fShader; |
| |
| typedef GrGLSLGeometryProcessor INHERITED; |
| }; |
| |
| /** |
| * 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 bloatIdx, int32_t edgeData, |
| int32_t cornerVertexID, int32_t cornerIdx) { |
| return (bloatIdx << 6) | (edgeData << 4) | (cornerVertexID << 2) | cornerIdx; |
| } |
| |
| static constexpr int32_t hull_vertex_data(int32_t cornerIdx, int32_t cornerVertexID, int n) { |
| return pack_vertex_data((cornerIdx + (2 == cornerVertexID ? 1 : n - 1)) % n, 0, cornerVertexID, |
| cornerIdx); |
| } |
| |
| static constexpr int32_t edge_vertex_data(int32_t edgeID, int32_t endptIdx, int32_t endptVertexID, |
| int n) { |
| return pack_vertex_data(0 == endptIdx ? (edgeID + 1) % n : edgeID, (endptIdx << 1) | 1, |
| endptVertexID, 0 == endptIdx ? edgeID : (edgeID + 1) % n); |
| } |
| |
| static constexpr int32_t kHull3AndEdgeVertices[] = { |
| hull_vertex_data(0, 0, 3), |
| hull_vertex_data(0, 1, 3), |
| hull_vertex_data(0, 2, 3), |
| hull_vertex_data(1, 0, 3), |
| hull_vertex_data(1, 1, 3), |
| hull_vertex_data(1, 2, 3), |
| hull_vertex_data(2, 0, 3), |
| hull_vertex_data(2, 1, 3), |
| hull_vertex_data(2, 2, 3), |
| |
| edge_vertex_data(0, 0, 0, 3), |
| edge_vertex_data(0, 0, 1, 3), |
| edge_vertex_data(0, 0, 2, 3), |
| edge_vertex_data(0, 1, 0, 3), |
| edge_vertex_data(0, 1, 1, 3), |
| edge_vertex_data(0, 1, 2, 3), |
| |
| edge_vertex_data(1, 0, 0, 3), |
| edge_vertex_data(1, 0, 1, 3), |
| edge_vertex_data(1, 0, 2, 3), |
| edge_vertex_data(1, 1, 0, 3), |
| edge_vertex_data(1, 1, 1, 3), |
| edge_vertex_data(1, 1, 2, 3), |
| |
| edge_vertex_data(2, 0, 0, 3), |
| edge_vertex_data(2, 0, 1, 3), |
| edge_vertex_data(2, 0, 2, 3), |
| edge_vertex_data(2, 1, 0, 3), |
| edge_vertex_data(2, 1, 1, 3), |
| edge_vertex_data(2, 1, 2, 3), |
| }; |
| |
| GR_DECLARE_STATIC_UNIQUE_KEY(gHull3AndEdgeVertexBufferKey); |
| |
| static constexpr uint16_t kRestartStrip = 0xffff; |
| |
| static constexpr uint16_t kHull3AndEdgeIndicesAsStrips[] = { |
| 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. |
| }; |
| |
| static constexpr uint16_t kHull3AndEdgeIndicesAsTris[] = { |
| // First corner and main body of the hull. |
| 1, 2, 0, |
| 2, 3, 0, |
| 0, 3, 8, // Main body. |
| |
| // Opposite side and corners of the hull. |
| 4, 5, 3, |
| 5, 6, 3, |
| 3, 6, 8, |
| 6, 7, 8, |
| |
| // First edge. |
| 10, 9, 11, |
| 9, 14, 11, |
| 11, 14, 12, |
| 14, 13, 12, |
| |
| // Second edge. |
| 16, 15, 17, |
| 15, 20, 17, |
| 17, 20, 18, |
| 20, 19, 18, |
| |
| // Third edge. |
| 22, 21, 23, |
| 21, 26, 23, |
| 23, 26, 24, |
| 26, 25, 24, |
| }; |
| |
| GR_DECLARE_STATIC_UNIQUE_KEY(gHull3AndEdgeIndexBufferKey); |
| |
| static constexpr int32_t kHull4Vertices[] = { |
| hull_vertex_data(0, 0, 4), |
| hull_vertex_data(0, 1, 4), |
| hull_vertex_data(0, 2, 4), |
| hull_vertex_data(1, 0, 4), |
| hull_vertex_data(1, 1, 4), |
| hull_vertex_data(1, 2, 4), |
| hull_vertex_data(2, 0, 4), |
| hull_vertex_data(2, 1, 4), |
| hull_vertex_data(2, 2, 4), |
| hull_vertex_data(3, 0, 4), |
| hull_vertex_data(3, 1, 4), |
| hull_vertex_data(3, 2, 4), |
| |
| // No edges for now (beziers don't use edges). |
| }; |
| |
| GR_DECLARE_STATIC_UNIQUE_KEY(gHull4VertexBufferKey); |
| |
| static constexpr uint16_t kHull4IndicesAsStrips[] = { |
| 1, 0, 2, 11, 3, 5, 4, kRestartStrip, // First half of the hull (split diagonally). |
| 7, 6, 8, 5, 9, 11, 10 // Second half of the hull. |
| }; |
| |
| static constexpr uint16_t kHull4IndicesAsTris[] = { |
| // First half of the hull (split diagonally). |
| 1, 0, 2, |
| 0, 11, 2, |
| 2, 11, 3, |
| 11, 5, 3, |
| 3, 5, 4, |
| |
| // Second half of the hull. |
| 7, 6, 8, |
| 6, 5, 8, |
| 8, 5, 9, |
| 5, 11, 9, |
| 9, 11, 10, |
| }; |
| |
| GR_DECLARE_STATIC_UNIQUE_KEY(gHull4IndexBufferKey); |
| |
| /** |
| * Generates a conservative raster hull around a convex polygon. For triangles, we also generate |
| * independent conservative rasters around each edge. (See comments for RenderPass) |
| */ |
| class VSHullAndEdgeImpl : public GrCCCoverageProcessor::VSImpl { |
| public: |
| VSHullAndEdgeImpl(std::unique_ptr<Shader> shader, int numSides) |
| : VSImpl(std::move(shader)), fNumSides(numSides) {} |
| |
| const char* emitVertexPosition(const GrCCCoverageProcessor& proc, GrGLSLVertexBuilder* v, |
| GrGPArgs* gpArgs) const override { |
| Shader::GeometryVars vars; |
| fShader->emitSetupCode(v, "pts", nullptr, "wind", &vars); |
| |
| const char* hullPts = vars.fHullVars.fAlternatePoints; |
| if (!hullPts) { |
| hullPts = "pts"; |
| } |
| |
| // 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) << 6) | (0xf << 2) | (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 are also handled here (see kHull3AndEdgeIndices). |
| // 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 bloatpoint = %s[clockwise_indices >> 6];", hullPts); |
| v->codeAppend ("float2 vertexbloat = float2(bloatpoint.y > corner.y ? -bloat : +bloat, " |
| "bloatpoint.x > corner.x ? +bloat : -bloat);"); |
| |
| v->codeAppendf("if ((1 << 2) == (%s & (3 << 2))) {", |
| proc.getAttrib(kAttribIdx_VertexData).fName); |
| // We are the corner's middle vertex (of 3). |
| v->codeAppend ( "vertexbloat = float2(-vertexbloat.y, vertexbloat.x);"); |
| v->codeAppend ("}"); |
| |
| v->codeAppendf("if ((2 << 2) == (%s & (3 << 2))) {", |
| proc.getAttrib(kAttribIdx_VertexData).fName); |
| // We are the corner's third vertex (of 3). |
| v->codeAppend ( "vertexbloat = -vertexbloat;"); |
| v->codeAppend ("}"); |
| |
| v->codeAppend ("float2 vertex = corner + vertexbloat;"); |
| gpArgs->fPositionVar.set(kFloat2_GrSLType, "vertex"); |
| |
| if (4 == fNumSides) { |
| // We don't generate edges around 4-sided polygons. |
| return nullptr; // Known hull vertices don't need an initial coverage value. |
| } |
| |
| // Find coverage for edge vertices. |
| Shader::EmitEdgeDistanceEquation(v, "bloatpoint", "corner", |
| "float3 edge_distance_equation"); |
| v->codeAppend ("half coverage = dot(edge_distance_equation.xy, vertex) + " |
| "edge_distance_equation.z;"); |
| v->codeAppendf("if (0 == (%s & (1 << 5))) {", proc.getAttrib(kAttribIdx_VertexData).fName); |
| // We are the opposite endpoint. Invert coverage. |
| v->codeAppend ( "coverage = -1 - coverage;"); |
| v->codeAppend ("}"); |
| v->codeAppendf("if (0 == (%s & (1 << 4))) {", proc.getAttrib(kAttribIdx_VertexData).fName); |
| // We are actually a hull vertex. Hull coverage is +1 all around. |
| v->codeAppend ( "coverage = +1;"); |
| v->codeAppend ("}"); |
| |
| return "coverage"; |
| } |
| |
| private: |
| const int fNumSides; |
| }; |
| |
| 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. |
| }; |
| |
| static constexpr uint16_t kCornerIndicesAsTris[] = { |
| // First corner. |
| 0, 1, 2, |
| 1, 3, 2, |
| |
| // Second corner. |
| 4, 5, 6, |
| 5, 7, 6, |
| |
| // Third corner. |
| 8, 9, 10, |
| 9, 11, 10, |
| }; |
| |
| GR_DECLARE_STATIC_UNIQUE_KEY(gCornerIndexBufferKey); |
| |
| /** |
| * Generates conservative rasters around corners. (See comments for RenderPass) |
| */ |
| class VSCornerImpl : public GrCCCoverageProcessor::VSImpl { |
| public: |
| VSCornerImpl(std::unique_ptr<Shader> shader) : VSImpl(std::move(shader)) {} |
| |
| const char* emitVertexPosition(const GrCCCoverageProcessor&, GrGLSLVertexBuilder* v, |
| GrGPArgs* gpArgs) const override { |
| Shader::GeometryVars vars; |
| v->codeAppend ("int corner_id = sk_VertexID / 4;"); |
| fShader->emitSetupCode(v, "pts", "corner_id", "wind", &vars); |
| |
| v->codeAppendf("float2 vertex = %s;", vars.fCornerVars.fPoint); |
| v->codeAppend ("vertex.x += (0 == (sk_VertexID & 2)) ? -bloat : +bloat;"); |
| v->codeAppend ("vertex.y += (0 == (sk_VertexID & 1)) ? -bloat : +bloat;"); |
| |
| gpArgs->fPositionVar.set(kFloat2_GrSLType, "vertex"); |
| return nullptr; // Corner vertices don't have an initial coverage value. |
| } |
| }; |
| |
| void GrCCCoverageProcessor::initVS(GrResourceProvider* rp) { |
| SkASSERT(Impl::kVertexShader == fImpl); |
| const GrCaps& caps = *rp->caps(); |
| |
| switch (fRenderPass) { |
| case RenderPass::kTriangleHulls: { |
| GR_DEFINE_STATIC_UNIQUE_KEY(gHull3AndEdgeVertexBufferKey); |
| fVertexBuffer = rp->findOrMakeStaticBuffer(kVertex_GrBufferType, |
| sizeof(kHull3AndEdgeVertices), |
| kHull3AndEdgeVertices, |
| gHull3AndEdgeVertexBufferKey); |
| GR_DEFINE_STATIC_UNIQUE_KEY(gHull3AndEdgeIndexBufferKey); |
| if (caps.usePrimitiveRestart()) { |
| fIndexBuffer = rp->findOrMakeStaticBuffer(kIndex_GrBufferType, |
| sizeof(kHull3AndEdgeIndicesAsStrips), |
| kHull3AndEdgeIndicesAsStrips, |
| gHull3AndEdgeIndexBufferKey); |
| fNumIndicesPerInstance = SK_ARRAY_COUNT(kHull3AndEdgeIndicesAsStrips); |
| } else { |
| fIndexBuffer = rp->findOrMakeStaticBuffer(kIndex_GrBufferType, |
| sizeof(kHull3AndEdgeIndicesAsTris), |
| kHull3AndEdgeIndicesAsTris, |
| gHull3AndEdgeIndexBufferKey); |
| fNumIndicesPerInstance = SK_ARRAY_COUNT(kHull3AndEdgeIndicesAsTris); |
| } |
| break; |
| } |
| case RenderPass::kQuadraticHulls: |
| case RenderPass::kCubicHulls: { |
| GR_DEFINE_STATIC_UNIQUE_KEY(gHull4VertexBufferKey); |
| fVertexBuffer = rp->findOrMakeStaticBuffer(kVertex_GrBufferType, sizeof(kHull4Vertices), |
| kHull4Vertices, gHull4VertexBufferKey); |
| GR_DEFINE_STATIC_UNIQUE_KEY(gHull4IndexBufferKey); |
| if (caps.usePrimitiveRestart()) { |
| fIndexBuffer = rp->findOrMakeStaticBuffer(kIndex_GrBufferType, |
| sizeof(kHull4IndicesAsStrips), |
| kHull4IndicesAsStrips, |
| gHull4IndexBufferKey); |
| fNumIndicesPerInstance = SK_ARRAY_COUNT(kHull4IndicesAsStrips); |
| } else { |
| fIndexBuffer = rp->findOrMakeStaticBuffer(kIndex_GrBufferType, |
| sizeof(kHull4IndicesAsTris), |
| kHull4IndicesAsTris, |
| gHull4IndexBufferKey); |
| fNumIndicesPerInstance = SK_ARRAY_COUNT(kHull4IndicesAsTris); |
| } |
| break; |
| } |
| case RenderPass::kTriangleEdges: |
| SK_ABORT("kTriangleEdges RenderPass is not used by VSImpl."); |
| break; |
| case RenderPass::kTriangleCorners: |
| case RenderPass::kQuadraticCorners: |
| case RenderPass::kCubicCorners: { |
| GR_DEFINE_STATIC_UNIQUE_KEY(gCornerIndexBufferKey); |
| if (caps.usePrimitiveRestart()) { |
| fIndexBuffer = rp->findOrMakeStaticBuffer(kIndex_GrBufferType, |
| sizeof(kCornerIndicesAsStrips), |
| kCornerIndicesAsStrips, |
| gCornerIndexBufferKey); |
| fNumIndicesPerInstance = SK_ARRAY_COUNT(kCornerIndicesAsStrips); |
| } else { |
| fIndexBuffer = rp->findOrMakeStaticBuffer(kIndex_GrBufferType, |
| sizeof(kCornerIndicesAsTris), |
| kCornerIndicesAsTris, |
| gCornerIndexBufferKey); |
| fNumIndicesPerInstance = SK_ARRAY_COUNT(kCornerIndicesAsTris); |
| } |
| if (RenderPass::kTriangleCorners != fRenderPass) { |
| fNumIndicesPerInstance = fNumIndicesPerInstance * 2/3; |
| } |
| break; |
| } |
| } |
| |
| if (RenderPassIsCubic(fRenderPass) || WindMethod::kInstanceData == fWindMethod) { |
| SkASSERT(WindMethod::kCrossProduct == fWindMethod || 3 == this->numInputPoints()); |
| |
| SkASSERT(kAttribIdx_X == this->numAttribs()); |
| this->addInstanceAttrib("X", kFloat4_GrVertexAttribType); |
| |
| SkASSERT(kAttribIdx_Y == this->numAttribs()); |
| this->addInstanceAttrib("Y", kFloat4_GrVertexAttribType); |
| |
| SkASSERT(offsetof(QuadPointInstance, fX) == this->getAttrib(kAttribIdx_X).fOffsetInRecord); |
| SkASSERT(offsetof(QuadPointInstance, fY) == this->getAttrib(kAttribIdx_Y).fOffsetInRecord); |
| SkASSERT(sizeof(QuadPointInstance) == this->getInstanceStride()); |
| } else { |
| SkASSERT(kAttribIdx_X == this->numAttribs()); |
| this->addInstanceAttrib("X", kFloat3_GrVertexAttribType); |
| |
| SkASSERT(kAttribIdx_Y == this->numAttribs()); |
| this->addInstanceAttrib("Y", kFloat3_GrVertexAttribType); |
| |
| SkASSERT(offsetof(TriPointInstance, fX) == this->getAttrib(kAttribIdx_X).fOffsetInRecord); |
| SkASSERT(offsetof(TriPointInstance, fY) == this->getAttrib(kAttribIdx_Y).fOffsetInRecord); |
| SkASSERT(sizeof(TriPointInstance) == this->getInstanceStride()); |
| } |
| |
| if (fVertexBuffer) { |
| SkASSERT(kAttribIdx_VertexData == this->numAttribs()); |
| this->addVertexAttrib("vertexdata", kInt_GrVertexAttribType); |
| |
| SkASSERT(sizeof(int32_t) == this->getVertexStride()); |
| } |
| |
| if (caps.usePrimitiveRestart()) { |
| this->setWillUsePrimitiveRestart(); |
| fPrimitiveType = GrPrimitiveType::kTriangleStrip; |
| } else { |
| fPrimitiveType = 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, |
| instanceCount, baseInstance); |
| if (fVertexBuffer) { |
| mesh.setVertexData(fVertexBuffer.get(), 0); |
| } |
| } |
| |
| GrGLSLPrimitiveProcessor* GrCCCoverageProcessor::createVSImpl(std::unique_ptr<Shader> shadr) const { |
| switch (fRenderPass) { |
| case RenderPass::kTriangleHulls: |
| return new VSHullAndEdgeImpl(std::move(shadr), 3); |
| case RenderPass::kQuadraticHulls: |
| case RenderPass::kCubicHulls: |
| return new VSHullAndEdgeImpl(std::move(shadr), 4); |
| case RenderPass::kTriangleEdges: |
| SK_ABORT("kTriangleEdges RenderPass is not used by VSImpl."); |
| return nullptr; |
| case RenderPass::kTriangleCorners: |
| case RenderPass::kQuadraticCorners: |
| case RenderPass::kCubicCorners: |
| return new VSCornerImpl(std::move(shadr)); |
| } |
| SK_ABORT("Invalid RenderPass"); |
| return nullptr; |
| } |