From c182b9470fe504b84cfe59bd4e2ebbd2edfba895 Mon Sep 17 00:00:00 2001 From: Michael Ludwig Date: Fri, 16 Nov 2018 10:27:51 -0500 Subject: [PATCH] Use vertex writer for per-edge aa; remove templated tessellate function Bug: skia: Change-Id: I5e1014cea7bb869636e6c706f54a2145e58214fd Reviewed-on: https://skia-review.googlesource.com/c/171229 Reviewed-by: Brian Osman Commit-Queue: Michael Ludwig --- src/gpu/GrQuad.cpp | 19 ++-- src/gpu/GrQuad.h | 10 +- src/gpu/ops/GrQuadPerEdgeAA.cpp | 191 ++++++++++++++------------------ src/gpu/ops/GrQuadPerEdgeAA.h | 119 ++++++++------------ src/gpu/ops/GrTextureOp.cpp | 71 ++++-------- 5 files changed, 166 insertions(+), 244 deletions(-) diff --git a/src/gpu/GrQuad.cpp b/src/gpu/GrQuad.cpp index 51fe6d4822..cce70dbee5 100644 --- a/src/gpu/GrQuad.cpp +++ b/src/gpu/GrQuad.cpp @@ -32,16 +32,15 @@ static bool coords_form_rect(const float xs[4], const float ys[4]) { GrQuadType GrQuad::quadType() const { // Since GrQuad applies any perspective information at construction time, there's only two // types to choose from. - return coords_form_rect(fX, fY) ? GrQuadType::kRect_QuadType : GrQuadType::kStandard_QuadType; + return coords_form_rect(fX, fY) ? GrQuadType::kRect : GrQuadType::kStandard; } GrQuadType GrPerspQuad::quadType() const { if (this->hasPerspective()) { - return GrQuadType::kPerspective_QuadType; + return GrQuadType::kPerspective; } else { // Rect or standard quad, can ignore w since they are all ones - return coords_form_rect(fX, fY) ? GrQuadType::kRect_QuadType - : GrQuadType::kStandard_QuadType; + return coords_form_rect(fX, fY) ? GrQuadType::kRect : GrQuadType::kStandard; } } #endif @@ -69,7 +68,7 @@ void GrResolveAATypeForQuad(GrAAType requestedAAType, GrQuadAAFlags requestedEdg } else { // For coverage AA, if the quad is a rect and it lines up with pixel boundaries // then overall aa and per-edge aa can be completely disabled - if (knownType == GrQuadType::kRect_QuadType && !quad.aaHasEffectOnRect()) { + if (knownType == GrQuadType::kRect && !quad.aaHasEffectOnRect()) { *outAAType = GrAAType::kNone; *outEdgeFlags = GrQuadAAFlags::kNone; } @@ -97,11 +96,11 @@ template void GrResolveAATypeForQuad(GrAAType, GrQuadAAFlags, const GrPerspQuad& GrQuadType GrQuadTypeForTransformedRect(const SkMatrix& matrix) { if (matrix.rectStaysRect()) { - return GrQuadType::kRect_QuadType; + return GrQuadType::kRect; } else if (matrix.hasPerspective()) { - return GrQuadType::kPerspective_QuadType; + return GrQuadType::kPerspective; } else { - return GrQuadType::kStandard_QuadType; + return GrQuadType::kStandard; } } @@ -143,7 +142,7 @@ GrQuad::GrQuad(const SkRect& rect, const SkMatrix& m) { } bool GrQuad::aaHasEffectOnRect() const { - SkASSERT(this->quadType() == GrQuadType::kRect_QuadType); + SkASSERT(this->quadType() == GrQuadType::kRect); return aa_affects_rect(fX[0], fY[0], fX[3], fY[3]); } @@ -188,7 +187,7 @@ GrPerspQuad::GrPerspQuad(const SkRect& rect, const SkMatrix& m) { } bool GrPerspQuad::aaHasEffectOnRect() const { - SkASSERT(this->quadType() == GrQuadType::kRect_QuadType); + SkASSERT(this->quadType() == GrQuadType::kRect); // If rect, ws must all be 1s so no need to divide return aa_affects_rect(fX[0], fY[0], fX[3], fY[3]); } diff --git a/src/gpu/GrQuad.h b/src/gpu/GrQuad.h index 107fe255f0..5f62682ef8 100644 --- a/src/gpu/GrQuad.h +++ b/src/gpu/GrQuad.h @@ -24,10 +24,12 @@ enum class GrQuadAAFlags; // ws() == all ones. // 3. Is a perspective quad - the matrix has perspective, subsuming all previous quad types. enum class GrQuadType { - kRect_QuadType, - kStandard_QuadType, - kPerspective_QuadType + kRect, + kStandard, + kPerspective, + kLast = kPerspective }; +static const int kGrQuadTypeCount = static_cast(GrQuadType::kLast) + 1; // If an SkRect is transformed by this matrix, what class of quad is required to represent it. Since // quadType() is only provided on Gr[Persp]Quad in debug builds, production code should use this @@ -35,7 +37,7 @@ enum class GrQuadType { GrQuadType GrQuadTypeForTransformedRect(const SkMatrix& matrix); // Resolve disagreements between the overall requested AA type and the per-edge quad AA flags. -// knownQuadType must have come from GrQuadtypeForTransformedRect with the matrix that created the +// knownQuadType must have come from GrQuadTypeForTransformedRect with the matrix that created the // provided quad. Both outAAType and outEdgeFlags will be updated. template void GrResolveAATypeForQuad(GrAAType requestedAAType, GrQuadAAFlags requestedEdgeFlags, diff --git a/src/gpu/ops/GrQuadPerEdgeAA.cpp b/src/gpu/ops/GrQuadPerEdgeAA.cpp index 8a3a1dd427..d27a5039c8 100644 --- a/src/gpu/ops/GrQuadPerEdgeAA.cpp +++ b/src/gpu/ops/GrQuadPerEdgeAA.cpp @@ -7,6 +7,7 @@ #include "GrQuadPerEdgeAA.h" #include "GrQuad.h" +#include "GrVertexWriter.h" #include "glsl/GrGLSLColorSpaceXformHelper.h" #include "glsl/GrGLSLPrimitiveProcessor.h" #include "glsl/GrGLSLFragmentShaderBuilder.h" @@ -234,69 +235,45 @@ static SkPoint3 compute_non_aa_persp_edge_coeffs(const Sk4f& w) { // When there's guaranteed no perspective, the edge coefficients for non-AA quads is constant static constexpr SkPoint3 kNonAANoPerspEdgeCoeffs = {0, 0, 1}; -// This packs the four quad vertices' values for a given channel (the data) into a block. Returns -// the offset for the next block to be written to localStorage -static int store(const Sk4f& data, float* localStorage, int offset) { - data.store(localStorage + offset); - return offset + 4; -} - -// This unpacks dimCt values from a series of channels. By initializing offset from 0 to 3 (plus -// any channels' offsets to skip over), the particular quad vertex can be accessed. Returns the -// offset for the next channel of data in localStorage. -static int load(const float* localStorage, int offset, float* coordOut, int dimCt) { - for (int i = 0; i < dimCt; i++) { - coordOut[i] = localStorage[offset]; - offset += 4; - } - return offset; -} - } // anonymous namespace -void GrQuadPerEdgeAA::TessellateImpl(void* vertices, size_t vertexSize, float* localStorage, - const GrPerspQuad& deviceQuad, int posDim, size_t posOffset, size_t posSize, - const void* color, size_t colorOffset, size_t colorSize, - const GrPerspQuad& srcQuad, int srcDim, size_t srcOffset, size_t srcSize, - const void* domain, size_t domainOffset, size_t domainSize, - GrQuadAAFlags aaFlags, size_t aaOffset, size_t aaSize) { - // Make sure the device and local positions are dimensions that are supported - SkASSERT(posDim == 2 || posDim == 3); - SkASSERT(srcDim == 0 || srcDim == 2 || srcDim == 3); - // Make sure that the position sizes are the proper multiples of sizeof(float) since we copy - // floats directly into the block without converting types - SkASSERT(posSize == posDim * sizeof(float)); - SkASSERT(srcSize == srcDim * sizeof(float)); - // Make sure the component sizes completely fill the vertex - SkASSERT(vertexSize == posSize + colorSize + srcSize + domainSize + aaSize); +namespace GrQuadPerEdgeAA { + +////////////////// Tessellate Implementation + +void* Tessellate(void* vertices, const VertexSpec& spec, const GrPerspQuad& deviceQuad, + const GrColor& color, const GrPerspQuad& localQuad, const SkRect& domain, + GrQuadAAFlags aaFlags) { + bool deviceHasPerspective = spec.deviceQuadType() == GrQuadType::kPerspective; + bool localHasPerspective = spec.localQuadType() == GrQuadType::kPerspective; // Load position data into Sk4fs (always x, y and maybe w) Sk4f x = deviceQuad.x4f(); Sk4f y = deviceQuad.y4f(); Sk4f w; - if (posDim == 3) { + if (deviceHasPerspective) { w = deviceQuad.w4f(); } // Load local position data into Sk4fs (either none, just u,v or all three) Sk4f u, v, r; - if (srcDim > 0) { - u = srcQuad.x4f(); - v = srcQuad.y4f(); + if (spec.hasLocalCoords()) { + u = localQuad.x4f(); + v = localQuad.y4f(); - if (srcDim == 3) { - r = srcQuad.w4f(); + if (localHasPerspective) { + r = localQuad.w4f(); } } Sk4f a, b, c; - if (aaSize) { + if (spec.usesCoverageAA()) { // Must calculate edges and possibly outside the positions if (aaFlags == GrQuadAAFlags::kNone) { // A non-AA quad that got batched into an AA group, so its edges will be the same for // all four vertices and it does not need to be outset SkPoint3 edgeCoeffs; - if (posDim == 3) { + if (deviceHasPerspective) { edgeCoeffs = compute_non_aa_persp_edge_coeffs(w); } else { edgeCoeffs = kNonAANoPerspEdgeCoeffs; @@ -306,99 +283,93 @@ void GrQuadPerEdgeAA::TessellateImpl(void* vertices, size_t vertexSize, float* l a = edgeCoeffs.fX; b = edgeCoeffs.fY; c = edgeCoeffs.fZ; - } else if (posDim == 2) { - // For simplicity, pointers to u, v, and r are always provided, but srcDim + } else if (deviceHasPerspective) { + // For simplicity, pointers to u, v, and r are always provided, but the local dim param // ensures that only loaded Sk4fs are modified in the compute functions. - compute_quad_edges_and_outset_vertices( - aaFlags, &x, &y, &a, &b, &c, &u, &v, &r, srcDim, /* outset */ true); + compute_quad_edges_and_outset_persp_vertices(aaFlags, &x, &y, &w, &a, &b, &c, + &u, &v, &r, spec.localDimensionality()); } else { - compute_quad_edges_and_outset_persp_vertices( - aaFlags, &x, &y, &w, &a, &b, &c, &u, &v, &r, srcDim); + compute_quad_edges_and_outset_vertices(aaFlags, &x, &y, &a, &b, &c, &u, &v, &r, + spec.localDimensionality(), /* outset */ true); } } - // It is faster to unpack the Sk4fs all at once than access their components out of order. - int offset = store(x, localStorage, 0); - offset = store(y, localStorage, offset); - if (posDim == 3) { - offset = store(w, localStorage, offset); - } - if (srcDim > 0) { - offset = store(u, localStorage, offset); - offset = store(v, localStorage, offset); - if (srcDim == 3) { - offset = store(r, localStorage, offset); - } - } - int edgeOffset = offset; // The 4 edges are separate from the 4 vertices - if (aaSize) { - offset = store(a, localStorage, offset); - offset = store(b, localStorage, offset); - offset = store(c, localStorage, offset); - } - // Now rearrange the unpacked buffer into the vertex layout - char* vb = reinterpret_cast(vertices); + // Now rearrange the Sk4fs into the interleaved vertex layout: + // i.e. x1x2x3x4 y1y2y3y4 -> x1y1 x2y2 x3y3 x4y + GrVertexWriter vb{vertices}; for (int i = 0; i < 4; ++i) { - // Starting the offset at i makes sure that all loads read the data for the i^th vertex - offset = i; - - // NOTE: while this code uses explicit offsets to make it independent of the actual - // vertex layout, it is a good idea to keep the writes in the same order as the fields - // save position - offset = load(localStorage, offset, reinterpret_cast(vb + posOffset), posDim); - // save color - if (colorSize) { - memcpy(vb + colorOffset, color, colorSize); - } - // save local position - if (srcDim) { - offset = load(localStorage, offset, reinterpret_cast(vb + srcOffset), srcDim); - } - // save the domain - if (domainSize) { - memcpy(vb + domainOffset, domain, domainSize); + if (deviceHasPerspective) { + vb.write({x[i], y[i], w[i]}); + } else { + vb.write({x[i], y[i]}); } - // save the edges - if (aaSize) { - float* edgeBuffer = reinterpret_cast(vb + aaOffset); - for (int j = 0; j < 4; j++) { - load(localStorage, edgeOffset + j, edgeBuffer, 3); - edgeBuffer += 3; + // save color + if (spec.hasVertexColors()) { + vb.write(color); + } + + // save local position + if (spec.hasLocalCoords()) { + if (localHasPerspective) { + vb.write({u[i], v[i], r[i]}); + } else { + vb.write({u[i], v[i]}); } } - vb += vertexSize; + // save the domain + if (spec.hasDomain()) { + vb.write(domain); + } + + // save the edges + if (spec.usesCoverageAA()) { + for (int j = 0; j < 4; j++) { + vb.write({a[j], b[j], c[j]}); + } + } } + + return vb.fPtr; } -GrQuadPerEdgeAA::GPAttributes::GPAttributes(int posDim, int localDim, bool hasColor, GrAAType aa, - Domain domain) { - SkASSERT(posDim == 2 || posDim == 3); - SkASSERT(localDim == 0 || localDim == 2 || localDim == 3); +////////////////// VertexSpec Implementation - if (posDim == 3) { +int VertexSpec::deviceDimensionality() const { + return this->deviceQuadType() == GrQuadType::kPerspective ? 3 : 2; +} + +int VertexSpec::localDimensionality() const { + return fHasLocalCoords ? (this->localQuadType() == GrQuadType::kPerspective ? 3 : 2) : 0; +} + +////////////////// GPAttributes Implementation + +GPAttributes::GPAttributes(const VertexSpec& spec) { + if (spec.deviceDimensionality() == 3) { fPositions = {"position", kFloat3_GrVertexAttribType, kFloat3_GrSLType}; } else { fPositions = {"position", kFloat2_GrVertexAttribType, kFloat2_GrSLType}; } + int localDim = spec.localDimensionality(); if (localDim == 3) { fLocalCoords = {"localCoord", kFloat3_GrVertexAttribType, kFloat3_GrSLType}; } else if (localDim == 2) { fLocalCoords = {"localCoord", kFloat2_GrVertexAttribType, kFloat2_GrSLType}; } // else localDim == 0 and attribute remains uninitialized - if (hasColor) { + if (spec.hasVertexColors()) { fColors = {"color", kUByte4_norm_GrVertexAttribType, kHalf4_GrSLType}; } - if (domain == Domain::kYes) { + if (spec.hasDomain()) { fDomain = {"domain", kFloat4_GrVertexAttribType, kFloat4_GrSLType}; } - if (aa == GrAAType::kCoverage) { + if (spec.usesCoverageAA()) { fAAEdges[0] = {"aaEdge0", kFloat3_GrVertexAttribType, kFloat3_GrSLType}; fAAEdges[1] = {"aaEdge1", kFloat3_GrVertexAttribType, kFloat3_GrSLType}; fAAEdges[2] = {"aaEdge2", kFloat3_GrVertexAttribType, kFloat3_GrSLType}; @@ -406,13 +377,13 @@ GrQuadPerEdgeAA::GPAttributes::GPAttributes(int posDim, int localDim, bool hasCo } } -bool GrQuadPerEdgeAA::GPAttributes::needsPerspectiveInterpolation() const { +bool GPAttributes::needsPerspectiveInterpolation() const { // This only needs to check the position; local position having perspective does not change // how the varyings are interpolated return fPositions.cpuType() == kFloat3_GrVertexAttribType; } -uint32_t GrQuadPerEdgeAA::GPAttributes::getKey() const { +uint32_t GPAttributes::getKey() const { // aa, color, domain are single bit flags uint32_t x = this->usesCoverageAA() ? 0 : 1; x |= this->hasVertexColors() ? 0 : 2; @@ -426,9 +397,9 @@ uint32_t GrQuadPerEdgeAA::GPAttributes::getKey() const { return x; } -void GrQuadPerEdgeAA::GPAttributes::emitColor(GrGLSLPrimitiveProcessor::EmitArgs& args, - GrGLSLColorSpaceXformHelper* csXformHelper, - const char* colorVarName) const { +void GPAttributes::emitColor(GrGLSLPrimitiveProcessor::EmitArgs& args, + GrGLSLColorSpaceXformHelper* csXformHelper, + const char* colorVarName) const { using Interpolation = GrGLSLVaryingHandler::Interpolation; if (!fColors.isInitialized()) { @@ -450,7 +421,7 @@ void GrQuadPerEdgeAA::GPAttributes::emitColor(GrGLSLPrimitiveProcessor::EmitArgs } } -void GrQuadPerEdgeAA::GPAttributes::emitExplicitLocalCoords( +void GPAttributes::emitExplicitLocalCoords( GrGLSLPrimitiveProcessor::EmitArgs& args, const char* localCoordName, const char* domainName) const { using Interpolation = GrGLSLVaryingHandler::Interpolation; @@ -480,8 +451,8 @@ void GrQuadPerEdgeAA::GPAttributes::emitExplicitLocalCoords( } } -void GrQuadPerEdgeAA::GPAttributes::emitCoverage(GrGLSLPrimitiveProcessor::EmitArgs& args, - const char* edgeDistName) const { +void GPAttributes::emitCoverage(GrGLSLPrimitiveProcessor::EmitArgs& args, + const char* edgeDistName) const { if (this->usesCoverageAA()) { bool mulByFragCoordW = false; GrGLSLVarying aaDistVarying(kFloat4_GrSLType, @@ -528,3 +499,5 @@ void GrQuadPerEdgeAA::GPAttributes::emitCoverage(GrGLSLPrimitiveProcessor::EmitA args.fFragBuilder->codeAppendf("%s = float4(1);", args.fOutputCoverage); } } + +} // namespace GrQuadPerEdgeAA diff --git a/src/gpu/ops/GrQuadPerEdgeAA.h b/src/gpu/ops/GrQuadPerEdgeAA.h index 18f6335702..578e431094 100644 --- a/src/gpu/ops/GrQuadPerEdgeAA.h +++ b/src/gpu/ops/GrQuadPerEdgeAA.h @@ -10,6 +10,7 @@ #include "GrColor.h" #include "GrPrimitiveProcessor.h" +#include "GrQuad.h" #include "GrSamplerState.h" #include "GrTypesPriv.h" #include "glsl/GrGLSLPrimitiveProcessor.h" @@ -17,47 +18,47 @@ #include "SkPoint3.h" class GrGLSLColorSpaceXformHelper; -class GrPerspQuad; -class GrQuadPerEdgeAA { -public: +namespace GrQuadPerEdgeAA { + enum class Domain : bool { kNo = false, kYes = true }; - // The vertex template provides a clean way of specifying the layout and components of a vertex - // for a per-edge aa quad. However, because there are so many permutations possible, the struct - // is defined this way to take away all layout control from the compiler and make - // sure that it matches what we need to send to the GPU. - // - // It is expected that most code using these vertices will only need to call the templated - // Tessellate() function with an appropriately sized vertex buffer and not need to modify or - // read the fields of a particular vertex. - template - struct Vertex { - using Color = C; - static constexpr GrAA kAA = AA; - static constexpr Domain kDomain = D; - static constexpr size_t kPositionDim = PosDim; - static constexpr size_t kLocalPositionDim = LocalPosDim; + // Specifies the vertex configuration for an op that renders per-edge AA quads. The vertex + // order (when enabled) is device position, color, local position, domain, aa edge equations. + // This order matches the constructor argument order of VertexSpec and is the order that + // GPAttributes maintains. If hasLocalCoords is false, then the local quad type can be ignored. + struct VertexSpec { + public: + VertexSpec(GrQuadType deviceQuadType, bool hasColor, GrQuadType localQuadType, + bool hasLocalCoords, Domain domain, GrAAType aa) + : fDeviceQuadType(static_cast(deviceQuadType)) + , fLocalQuadType(static_cast(localQuadType)) + , fHasLocalCoords(hasLocalCoords) + , fHasColor(hasColor) + , fHasDomain(static_cast(domain)) + , fUsesCoverageAA(aa == GrAAType::kCoverage) { } - static constexpr size_t kPositionOffset = 0; - static constexpr size_t kPositionSize = PosDim * sizeof(float); + GrQuadType deviceQuadType() const { return static_cast(fDeviceQuadType); } + GrQuadType localQuadType() const { return static_cast(fLocalQuadType); } + bool hasLocalCoords() const { return fHasLocalCoords; } + bool hasVertexColors() const { return fHasColor; } + bool hasDomain() const { return fHasDomain; } + bool usesCoverageAA() const { return fUsesCoverageAA; } - static constexpr size_t kColorOffset = kPositionOffset + kPositionSize; - static constexpr size_t kColorSize = sizeof(Color); + // Will always be 2 or 3 + int deviceDimensionality() const; + // Will always be 0 if hasLocalCoords is false, otherwise will be 2 or 3 + int localDimensionality() const; - static constexpr size_t kLocalPositionOffset = kColorOffset + kColorSize; - static constexpr size_t kLocalPositionSize = LocalPosDim * sizeof(float); + private: + static_assert(kGrQuadTypeCount <= 4, "GrQuadType doesn't fit in 2 bits"); - static constexpr size_t kDomainOffset = kLocalPositionOffset + kLocalPositionSize; - static constexpr size_t kDomainSize = D == Domain::kYes ? sizeof(SkRect) : 0; - - static constexpr size_t kAAOffset = kDomainOffset + kDomainSize; - static constexpr size_t kAASize = AA == GrAA::kYes ? 4 * sizeof(SkPoint3) : 0; - - static constexpr size_t kVertexSize = kAAOffset + kAASize; - - // Make sure sizeof(Vertex<...>) == kVertexSize - char fData[kVertexSize]; + unsigned fDeviceQuadType: 2; + unsigned fLocalQuadType: 2; + unsigned fHasLocalCoords: 1; + unsigned fHasColor: 1; + unsigned fHasDomain: 1; + unsigned fUsesCoverageAA: 1; }; // Utility class that manages the attribute state necessary to render a particular batch of @@ -77,7 +78,7 @@ public: public: using Attribute = GrPrimitiveProcessor::Attribute; - GPAttributes(int posDim, int localDim, bool hasColor, GrAAType aa, Domain domain); + GPAttributes(const VertexSpec& vertexSpec); const Attribute& positions() const { return fPositions; } const Attribute& colors() const { return fColors; } @@ -131,44 +132,18 @@ public: Attribute fAAEdges[4]; // named "aaEdgeX" for X = 0,1,2,3 }; - // Tessellate the given quad specification into the vertices buffer. If the specific vertex - // type does not use color, local positions, domain, etc. then the passed in values used for - // that field will be ignored. - template - static void Tessellate(V* vertices, const GrPerspQuad& deviceQuad, typename V::Color color, - const GrPerspQuad& srcQuad, const SkRect& domain, GrQuadAAFlags aa) { - static_assert(sizeof(V) == V::kVertexSize, "Incorrect vertex size"); - static constexpr bool useCoverageAA = V::kAA == GrAA::kYes; - float localStorage[4 * (V::kPositionDim + V::kLocalPositionDim + (useCoverageAA ? 3 : 0))]; - TessellateImpl(vertices, V::kVertexSize, localStorage, - deviceQuad, V::kPositionDim, V::kPositionOffset, V::kPositionSize, - &color, V::kColorOffset, V::kColorSize, - srcQuad, V::kLocalPositionDim, V::kLocalPositionOffset, V::kLocalPositionSize, - &domain, V::kDomainOffset, V::kDomainSize, - aa, V::kAAOffset, V::kAASize); - } -private: - // Don't let the "namespace" class be instantiated - GrQuadPerEdgeAA(); + // Fill vertices with the vertex data needed to represent the given quad. The device position, + // local coords, vertex color, domain, and edge coefficients will be written and/or computed + // based on the configuration in the vertex spec; if that attribute is disabled in the spec, + // then its corresponding function argument is ignored. + // + // Returns the advanced pointer in vertices. + // TODO4F: Switch GrColor to GrVertexColor + void* Tessellate(void* vertices, const VertexSpec& spec, const GrPerspQuad& deviceQuad, + const GrColor& color, const GrPerspQuad& localQuad, const SkRect& domain, + GrQuadAAFlags aa); - // Internal implementation that can handle all vertex template variations without being - // replicated by the template in order to keep code size down. - // - // This uses the field sizes to determine if particular data needs to be computed. The arguments - // are arranged so that the data and field specification match the field declaration order of - // the vertex type (pos, color, localPos, domain, aa). - // - // localStorage must be have a length > 4 * (devDimCt + srcDimCt + (aa ? 3 : 0)) and is assumed - // to be a pointer to a local variable in the wrapping template's stack. This is done instead of - // always allocating 36 floats in this function (36 is maximum needed). The minimum needed for a - // non-AA 2D quad with no local coordinates is just 8. - static void TessellateImpl(void* vertices, size_t vertexSize, float* localStorage, - const GrPerspQuad& deviceQuad, int posDim, size_t posOffset, size_t posSize, - const void* color, size_t colorOffset, size_t colorSize, - const GrPerspQuad& srcQuad, int srcDim, size_t srcOffset, size_t srcSize, - const void* domain, size_t domainOffset, size_t domainSize, - GrQuadAAFlags aaFlags, size_t aaOffset, size_t aaSize); -}; +} // namespace GrQuadPerEdgeAA #endif // GrQuadPerEdgeAA_DEFINED diff --git a/src/gpu/ops/GrTextureOp.cpp b/src/gpu/ops/GrTextureOp.cpp index 1d43282112..7f92a1c119 100644 --- a/src/gpu/ops/GrTextureOp.cpp +++ b/src/gpu/ops/GrTextureOp.cpp @@ -37,6 +37,7 @@ namespace { using Domain = GrQuadPerEdgeAA::Domain; +using VertexSpec = GrQuadPerEdgeAA::VertexSpec; /** * Geometry Processor that draws a texture modulated by a vertex color (though, this is meant to be @@ -50,11 +51,10 @@ public: const GrSamplerState::Filter filter, sk_sp textureColorSpaceXform, sk_sp paintColorSpaceXform, - int positionDim, GrAAType aa, Domain domain, - const GrShaderCaps& caps) { + const VertexSpec& vertexSpec, const GrShaderCaps& caps) { return sk_sp(new TextureGeometryProcessor( textureType, textureConfig, filter, std::move(textureColorSpaceXform), - std::move(paintColorSpaceXform), positionDim, aa, domain, caps)); + std::move(paintColorSpaceXform), vertexSpec, caps)); } const char* name() const override { return "TextureGeometryProcessor"; } @@ -117,13 +117,14 @@ private: TextureGeometryProcessor(GrTextureType textureType, GrPixelConfig textureConfig, GrSamplerState::Filter filter, sk_sp textureColorSpaceXform, - sk_sp paintColorSpaceXform, int positionDim, - GrAAType aa, Domain domain, const GrShaderCaps& caps) + sk_sp paintColorSpaceXform, + const VertexSpec& vertexSpec, const GrShaderCaps& caps) : INHERITED(kTextureGeometryProcessor_ClassID) - , fAttrs(positionDim, /* src dim */ 2, /* vertex color */ true, aa, domain) + , fAttrs(vertexSpec) , fTextureColorSpaceXform(std::move(textureColorSpaceXform)) , fPaintColorSpaceXform(std::move(paintColorSpaceXform)) , fSampler(textureType, textureConfig, filter) { + SkASSERT(vertexSpec.hasVertexColors() && vertexSpec.localDimensionality() == 2); this->setTextureSamplerCnt(1); this->setVertexAttributes(fAttrs.attributes(), fAttrs.attributeCount()); } @@ -139,7 +140,7 @@ private: }; static bool filter_has_effect_for_rect_stays_rect(const GrPerspQuad& quad, const SkRect& srcRect) { - SkASSERT(quad.quadType() == GrQuadType::kRect_QuadType); + SkASSERT(quad.quadType() == GrQuadType::kRect); float ql = quad.x(0); float qt = quad.y(0); float qr = quad.x(3); @@ -320,11 +321,11 @@ private: GrResolveAATypeForQuad(aaType, aaFlags, quad, quadType, &aaType, &aaFlags); fAAType = static_cast(aaType); - fPerspective = static_cast(quadType == GrQuadType::kPerspective_QuadType); + fPerspective = static_cast(quadType == GrQuadType::kPerspective); // We expect our caller to have already caught this optimization. SkASSERT(!srcRect.contains(proxy->getWorstCaseBoundsRect()) || constraint == SkCanvas::kFast_SrcRectConstraint); - if (quadType == GrQuadType::kRect_QuadType) { + if (quadType == GrQuadType::kRect) { // Disable filtering if possible (note AA optimizations for rects are automatically // handled above in GrResolveAATypeForQuad). if (this->filter() != GrSamplerState::Filter::kNearest && @@ -386,7 +387,7 @@ private: overallAAType = aaType; } if (!mustFilter && this->filter() != GrSamplerState::Filter::kNearest) { - mustFilter = quadType != GrQuadType::kRect_QuadType || + mustFilter = quadType != GrQuadType::kRect || filter_has_effect_for_rect_stays_rect(quad, set[p].fSrcRect); } fQuads.emplace_back(set[p].fSrcRect, quad, aaFlags, SkCanvas::kFast_SrcRectConstraint, @@ -401,13 +402,9 @@ private: fDomain = static_cast(false); } - template - void tess(void* v, const GrGeometryProcessor* gp, const GrTextureProxy* proxy, int start, - int cnt) const { + void tess(void* v, const VertexSpec& spec, const GrTextureProxy* proxy, + int start, int cnt) const { TRACE_EVENT0("skia", TRACE_FUNC); - using Vertex = GrQuadPerEdgeAA::Vertex; - SkASSERT(gp->vertexStride() == sizeof(Vertex)); - auto vertices = static_cast(v); auto origin = proxy->origin(); const auto* texture = proxy->peekTexture(); float iw = 1.f / texture->width(); @@ -417,9 +414,8 @@ private: const auto q = fQuads[i]; GrPerspQuad srcQuad = compute_src_quad(origin, q.srcRect(), iw, ih); SkRect domain = compute_domain(q.domain(), this->filter(), origin, q.srcRect(), iw, ih); - GrQuadPerEdgeAA::Tessellate( - vertices, q.quad(), q.color(), srcQuad, domain, q.aaFlags()); - vertices += 4; + v = GrQuadPerEdgeAA::Tessellate(v, spec, q.quad(), q.color(), srcQuad, domain, + q.aaFlags()); } } @@ -453,10 +449,13 @@ private: } } + VertexSpec vertexSpec(hasPerspective ? GrQuadType::kPerspective : GrQuadType::kStandard, + /* hasColor */ true, GrQuadType::kRect, /* hasLocal */ true, + domain, aaType); + sk_sp gp = TextureGeometryProcessor::Make( textureType, config, this->filter(), std::move(fTextureColorSpaceXform), - std::move(fPaintColorSpaceXform), hasPerspective ? 3 : 2, aaType, domain, - *target->caps().shaderCaps()); + std::move(fPaintColorSpaceXform), vertexSpec, *target->caps().shaderCaps()); GrPipeline::InitArgs args; args.fProxy = target->proxy(); args.fCaps = &target->caps(); @@ -480,33 +479,8 @@ private: } const auto* pipeline = target->allocPipeline(args, GrProcessorSet::MakeEmptySet(), std::move(clip)); - using TessFn = decltype(&TextureOp::tess<2, Domain::kNo, GrAA::kNo>); -#define TESS_FN_AND_VERTEX_SIZE(Point, Domain, AA) \ - { \ - &TextureOp::tess, \ - sizeof(GrQuadPerEdgeAA::Vertex) \ - } - static constexpr struct { - TessFn fTessFn; - size_t fVertexSize; - } kTessFnsAndVertexSizes[] = { - TESS_FN_AND_VERTEX_SIZE(2, Domain::kNo, GrAA::kNo), - TESS_FN_AND_VERTEX_SIZE(2, Domain::kNo, GrAA::kYes), - TESS_FN_AND_VERTEX_SIZE(2, Domain::kYes, GrAA::kNo), - TESS_FN_AND_VERTEX_SIZE(2, Domain::kYes, GrAA::kYes), - TESS_FN_AND_VERTEX_SIZE(3, Domain::kNo, GrAA::kNo), - TESS_FN_AND_VERTEX_SIZE(3, Domain::kNo, GrAA::kYes), - TESS_FN_AND_VERTEX_SIZE(3, Domain::kYes, GrAA::kNo), - TESS_FN_AND_VERTEX_SIZE(3, Domain::kYes, GrAA::kYes), - }; -#undef TESS_FN_AND_VERTEX_SIZE - int tessFnIdx = 0; - tessFnIdx |= (GrAAType::kCoverage == aaType) ? 0x1 : 0x0; - tessFnIdx |= (domain == Domain::kYes) ? 0x2 : 0x0; - tessFnIdx |= hasPerspective ? 0x4 : 0x0; - size_t vertexSize = kTessFnsAndVertexSizes[tessFnIdx].fVertexSize; - SkASSERT(vertexSize == gp->vertexStride()); + size_t vertexSize = gp->vertexStride(); GrMesh* meshes = target->allocMeshes(numProxies); const GrBuffer* vbuffer; @@ -534,8 +508,7 @@ private: } SkASSERT(numAllocatedVertices >= meshVertexCnt); - (op.*(kTessFnsAndVertexSizes[tessFnIdx].fTessFn))(vdata, gp.get(), proxy, q, - quadCnt); + op.tess(vdata, vertexSpec, proxy, q, quadCnt); if (quadCnt > 1) { meshes[m].setPrimitiveType(GrPrimitiveType::kTriangles);