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 <brianosman@google.com>
Commit-Queue: Michael Ludwig <michaelludwig@google.com>
This commit is contained in:
Michael Ludwig 2018-11-16 10:27:51 -05:00 committed by Skia Commit-Bot
parent 2c8e2bc682
commit c182b9470f
5 changed files with 166 additions and 244 deletions

View File

@ -32,16 +32,15 @@ static bool coords_form_rect(const float xs[4], const float ys[4]) {
GrQuadType GrQuad::quadType() const { GrQuadType GrQuad::quadType() const {
// Since GrQuad applies any perspective information at construction time, there's only two // Since GrQuad applies any perspective information at construction time, there's only two
// types to choose from. // 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 { GrQuadType GrPerspQuad::quadType() const {
if (this->hasPerspective()) { if (this->hasPerspective()) {
return GrQuadType::kPerspective_QuadType; return GrQuadType::kPerspective;
} else { } else {
// Rect or standard quad, can ignore w since they are all ones // Rect or standard quad, can ignore w since they are all ones
return coords_form_rect(fX, fY) ? GrQuadType::kRect_QuadType return coords_form_rect(fX, fY) ? GrQuadType::kRect : GrQuadType::kStandard;
: GrQuadType::kStandard_QuadType;
} }
} }
#endif #endif
@ -69,7 +68,7 @@ void GrResolveAATypeForQuad(GrAAType requestedAAType, GrQuadAAFlags requestedEdg
} else { } else {
// For coverage AA, if the quad is a rect and it lines up with pixel boundaries // 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 // 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; *outAAType = GrAAType::kNone;
*outEdgeFlags = GrQuadAAFlags::kNone; *outEdgeFlags = GrQuadAAFlags::kNone;
} }
@ -97,11 +96,11 @@ template void GrResolveAATypeForQuad(GrAAType, GrQuadAAFlags, const GrPerspQuad&
GrQuadType GrQuadTypeForTransformedRect(const SkMatrix& matrix) { GrQuadType GrQuadTypeForTransformedRect(const SkMatrix& matrix) {
if (matrix.rectStaysRect()) { if (matrix.rectStaysRect()) {
return GrQuadType::kRect_QuadType; return GrQuadType::kRect;
} else if (matrix.hasPerspective()) { } else if (matrix.hasPerspective()) {
return GrQuadType::kPerspective_QuadType; return GrQuadType::kPerspective;
} else { } else {
return GrQuadType::kStandard_QuadType; return GrQuadType::kStandard;
} }
} }
@ -143,7 +142,7 @@ GrQuad::GrQuad(const SkRect& rect, const SkMatrix& m) {
} }
bool GrQuad::aaHasEffectOnRect() const { 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]); 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 { 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 // If rect, ws must all be 1s so no need to divide
return aa_affects_rect(fX[0], fY[0], fX[3], fY[3]); return aa_affects_rect(fX[0], fY[0], fX[3], fY[3]);
} }

View File

@ -24,10 +24,12 @@ enum class GrQuadAAFlags;
// ws() == all ones. // ws() == all ones.
// 3. Is a perspective quad - the matrix has perspective, subsuming all previous quad types. // 3. Is a perspective quad - the matrix has perspective, subsuming all previous quad types.
enum class GrQuadType { enum class GrQuadType {
kRect_QuadType, kRect,
kStandard_QuadType, kStandard,
kPerspective_QuadType kPerspective,
kLast = kPerspective
}; };
static const int kGrQuadTypeCount = static_cast<int>(GrQuadType::kLast) + 1;
// If an SkRect is transformed by this matrix, what class of quad is required to represent it. Since // 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 // 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); GrQuadType GrQuadTypeForTransformedRect(const SkMatrix& matrix);
// Resolve disagreements between the overall requested AA type and the per-edge quad AA flags. // 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. // provided quad. Both outAAType and outEdgeFlags will be updated.
template <typename Q> template <typename Q>
void GrResolveAATypeForQuad(GrAAType requestedAAType, GrQuadAAFlags requestedEdgeFlags, void GrResolveAATypeForQuad(GrAAType requestedAAType, GrQuadAAFlags requestedEdgeFlags,

View File

@ -7,6 +7,7 @@
#include "GrQuadPerEdgeAA.h" #include "GrQuadPerEdgeAA.h"
#include "GrQuad.h" #include "GrQuad.h"
#include "GrVertexWriter.h"
#include "glsl/GrGLSLColorSpaceXformHelper.h" #include "glsl/GrGLSLColorSpaceXformHelper.h"
#include "glsl/GrGLSLPrimitiveProcessor.h" #include "glsl/GrGLSLPrimitiveProcessor.h"
#include "glsl/GrGLSLFragmentShaderBuilder.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 // When there's guaranteed no perspective, the edge coefficients for non-AA quads is constant
static constexpr SkPoint3 kNonAANoPerspEdgeCoeffs = {0, 0, 1}; 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 } // anonymous namespace
void GrQuadPerEdgeAA::TessellateImpl(void* vertices, size_t vertexSize, float* localStorage, namespace GrQuadPerEdgeAA {
const GrPerspQuad& deviceQuad, int posDim, size_t posOffset, size_t posSize,
const void* color, size_t colorOffset, size_t colorSize, ////////////////// Tessellate Implementation
const GrPerspQuad& srcQuad, int srcDim, size_t srcOffset, size_t srcSize,
const void* domain, size_t domainOffset, size_t domainSize, void* Tessellate(void* vertices, const VertexSpec& spec, const GrPerspQuad& deviceQuad,
GrQuadAAFlags aaFlags, size_t aaOffset, size_t aaSize) { const GrColor& color, const GrPerspQuad& localQuad, const SkRect& domain,
// Make sure the device and local positions are dimensions that are supported GrQuadAAFlags aaFlags) {
SkASSERT(posDim == 2 || posDim == 3); bool deviceHasPerspective = spec.deviceQuadType() == GrQuadType::kPerspective;
SkASSERT(srcDim == 0 || srcDim == 2 || srcDim == 3); bool localHasPerspective = spec.localQuadType() == GrQuadType::kPerspective;
// 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);
// Load position data into Sk4fs (always x, y and maybe w) // Load position data into Sk4fs (always x, y and maybe w)
Sk4f x = deviceQuad.x4f(); Sk4f x = deviceQuad.x4f();
Sk4f y = deviceQuad.y4f(); Sk4f y = deviceQuad.y4f();
Sk4f w; Sk4f w;
if (posDim == 3) { if (deviceHasPerspective) {
w = deviceQuad.w4f(); w = deviceQuad.w4f();
} }
// Load local position data into Sk4fs (either none, just u,v or all three) // Load local position data into Sk4fs (either none, just u,v or all three)
Sk4f u, v, r; Sk4f u, v, r;
if (srcDim > 0) { if (spec.hasLocalCoords()) {
u = srcQuad.x4f(); u = localQuad.x4f();
v = srcQuad.y4f(); v = localQuad.y4f();
if (srcDim == 3) { if (localHasPerspective) {
r = srcQuad.w4f(); r = localQuad.w4f();
} }
} }
Sk4f a, b, c; Sk4f a, b, c;
if (aaSize) { if (spec.usesCoverageAA()) {
// Must calculate edges and possibly outside the positions // Must calculate edges and possibly outside the positions
if (aaFlags == GrQuadAAFlags::kNone) { if (aaFlags == GrQuadAAFlags::kNone) {
// A non-AA quad that got batched into an AA group, so its edges will be the same for // 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 // all four vertices and it does not need to be outset
SkPoint3 edgeCoeffs; SkPoint3 edgeCoeffs;
if (posDim == 3) { if (deviceHasPerspective) {
edgeCoeffs = compute_non_aa_persp_edge_coeffs(w); edgeCoeffs = compute_non_aa_persp_edge_coeffs(w);
} else { } else {
edgeCoeffs = kNonAANoPerspEdgeCoeffs; edgeCoeffs = kNonAANoPerspEdgeCoeffs;
@ -306,99 +283,93 @@ void GrQuadPerEdgeAA::TessellateImpl(void* vertices, size_t vertexSize, float* l
a = edgeCoeffs.fX; a = edgeCoeffs.fX;
b = edgeCoeffs.fY; b = edgeCoeffs.fY;
c = edgeCoeffs.fZ; c = edgeCoeffs.fZ;
} else if (posDim == 2) { } else if (deviceHasPerspective) {
// For simplicity, pointers to u, v, and r are always provided, but srcDim // 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. // ensures that only loaded Sk4fs are modified in the compute functions.
compute_quad_edges_and_outset_vertices( compute_quad_edges_and_outset_persp_vertices(aaFlags, &x, &y, &w, &a, &b, &c,
aaFlags, &x, &y, &a, &b, &c, &u, &v, &r, srcDim, /* outset */ true); &u, &v, &r, spec.localDimensionality());
} else { } else {
compute_quad_edges_and_outset_persp_vertices( compute_quad_edges_and_outset_vertices(aaFlags, &x, &y, &a, &b, &c, &u, &v, &r,
aaFlags, &x, &y, &w, &a, &b, &c, &u, &v, &r, srcDim); spec.localDimensionality(), /* outset */ true);
} }
} }
// It is faster to unpack the Sk4fs all at once than access their components out of order. // Now rearrange the Sk4fs into the interleaved vertex layout:
int offset = store(x, localStorage, 0); // i.e. x1x2x3x4 y1y2y3y4 -> x1y1 x2y2 x3y3 x4y
offset = store(y, localStorage, offset); GrVertexWriter vb{vertices};
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<char*>(vertices);
for (int i = 0; i < 4; ++i) { 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 // save position
offset = load(localStorage, offset, reinterpret_cast<float*>(vb + posOffset), posDim); if (deviceHasPerspective) {
vb.write<SkPoint3>({x[i], y[i], w[i]});
} else {
vb.write<SkPoint>({x[i], y[i]});
}
// save color // save color
if (colorSize) { if (spec.hasVertexColors()) {
memcpy(vb + colorOffset, color, colorSize); vb.write<GrColor>(color);
} }
// save local position // save local position
if (srcDim) { if (spec.hasLocalCoords()) {
offset = load(localStorage, offset, reinterpret_cast<float*>(vb + srcOffset), srcDim); if (localHasPerspective) {
vb.write<SkPoint3>({u[i], v[i], r[i]});
} else {
vb.write<SkPoint>({u[i], v[i]});
} }
}
// save the domain // save the domain
if (domainSize) { if (spec.hasDomain()) {
memcpy(vb + domainOffset, domain, domainSize); vb.write<SkRect>(domain);
} }
// save the edges // save the edges
if (aaSize) { if (spec.usesCoverageAA()) {
float* edgeBuffer = reinterpret_cast<float*>(vb + aaOffset);
for (int j = 0; j < 4; j++) { for (int j = 0; j < 4; j++) {
load(localStorage, edgeOffset + j, edgeBuffer, 3); vb.write<SkPoint3>({a[j], b[j], c[j]});
edgeBuffer += 3; }
} }
} }
vb += vertexSize; return vb.fPtr;
}
} }
GrQuadPerEdgeAA::GPAttributes::GPAttributes(int posDim, int localDim, bool hasColor, GrAAType aa, ////////////////// VertexSpec Implementation
Domain domain) {
SkASSERT(posDim == 2 || posDim == 3);
SkASSERT(localDim == 0 || localDim == 2 || localDim == 3);
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}; fPositions = {"position", kFloat3_GrVertexAttribType, kFloat3_GrSLType};
} else { } else {
fPositions = {"position", kFloat2_GrVertexAttribType, kFloat2_GrSLType}; fPositions = {"position", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
} }
int localDim = spec.localDimensionality();
if (localDim == 3) { if (localDim == 3) {
fLocalCoords = {"localCoord", kFloat3_GrVertexAttribType, kFloat3_GrSLType}; fLocalCoords = {"localCoord", kFloat3_GrVertexAttribType, kFloat3_GrSLType};
} else if (localDim == 2) { } else if (localDim == 2) {
fLocalCoords = {"localCoord", kFloat2_GrVertexAttribType, kFloat2_GrSLType}; fLocalCoords = {"localCoord", kFloat2_GrVertexAttribType, kFloat2_GrSLType};
} // else localDim == 0 and attribute remains uninitialized } // else localDim == 0 and attribute remains uninitialized
if (hasColor) { if (spec.hasVertexColors()) {
fColors = {"color", kUByte4_norm_GrVertexAttribType, kHalf4_GrSLType}; fColors = {"color", kUByte4_norm_GrVertexAttribType, kHalf4_GrSLType};
} }
if (domain == Domain::kYes) { if (spec.hasDomain()) {
fDomain = {"domain", kFloat4_GrVertexAttribType, kFloat4_GrSLType}; fDomain = {"domain", kFloat4_GrVertexAttribType, kFloat4_GrSLType};
} }
if (aa == GrAAType::kCoverage) { if (spec.usesCoverageAA()) {
fAAEdges[0] = {"aaEdge0", kFloat3_GrVertexAttribType, kFloat3_GrSLType}; fAAEdges[0] = {"aaEdge0", kFloat3_GrVertexAttribType, kFloat3_GrSLType};
fAAEdges[1] = {"aaEdge1", kFloat3_GrVertexAttribType, kFloat3_GrSLType}; fAAEdges[1] = {"aaEdge1", kFloat3_GrVertexAttribType, kFloat3_GrSLType};
fAAEdges[2] = {"aaEdge2", 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 // This only needs to check the position; local position having perspective does not change
// how the varyings are interpolated // how the varyings are interpolated
return fPositions.cpuType() == kFloat3_GrVertexAttribType; return fPositions.cpuType() == kFloat3_GrVertexAttribType;
} }
uint32_t GrQuadPerEdgeAA::GPAttributes::getKey() const { uint32_t GPAttributes::getKey() const {
// aa, color, domain are single bit flags // aa, color, domain are single bit flags
uint32_t x = this->usesCoverageAA() ? 0 : 1; uint32_t x = this->usesCoverageAA() ? 0 : 1;
x |= this->hasVertexColors() ? 0 : 2; x |= this->hasVertexColors() ? 0 : 2;
@ -426,7 +397,7 @@ uint32_t GrQuadPerEdgeAA::GPAttributes::getKey() const {
return x; return x;
} }
void GrQuadPerEdgeAA::GPAttributes::emitColor(GrGLSLPrimitiveProcessor::EmitArgs& args, void GPAttributes::emitColor(GrGLSLPrimitiveProcessor::EmitArgs& args,
GrGLSLColorSpaceXformHelper* csXformHelper, GrGLSLColorSpaceXformHelper* csXformHelper,
const char* colorVarName) const { const char* colorVarName) const {
using Interpolation = GrGLSLVaryingHandler::Interpolation; using Interpolation = GrGLSLVaryingHandler::Interpolation;
@ -450,7 +421,7 @@ void GrQuadPerEdgeAA::GPAttributes::emitColor(GrGLSLPrimitiveProcessor::EmitArgs
} }
} }
void GrQuadPerEdgeAA::GPAttributes::emitExplicitLocalCoords( void GPAttributes::emitExplicitLocalCoords(
GrGLSLPrimitiveProcessor::EmitArgs& args, const char* localCoordName, GrGLSLPrimitiveProcessor::EmitArgs& args, const char* localCoordName,
const char* domainName) const { const char* domainName) const {
using Interpolation = GrGLSLVaryingHandler::Interpolation; using Interpolation = GrGLSLVaryingHandler::Interpolation;
@ -480,7 +451,7 @@ void GrQuadPerEdgeAA::GPAttributes::emitExplicitLocalCoords(
} }
} }
void GrQuadPerEdgeAA::GPAttributes::emitCoverage(GrGLSLPrimitiveProcessor::EmitArgs& args, void GPAttributes::emitCoverage(GrGLSLPrimitiveProcessor::EmitArgs& args,
const char* edgeDistName) const { const char* edgeDistName) const {
if (this->usesCoverageAA()) { if (this->usesCoverageAA()) {
bool mulByFragCoordW = false; bool mulByFragCoordW = false;
@ -528,3 +499,5 @@ void GrQuadPerEdgeAA::GPAttributes::emitCoverage(GrGLSLPrimitiveProcessor::EmitA
args.fFragBuilder->codeAppendf("%s = float4(1);", args.fOutputCoverage); args.fFragBuilder->codeAppendf("%s = float4(1);", args.fOutputCoverage);
} }
} }
} // namespace GrQuadPerEdgeAA

View File

@ -10,6 +10,7 @@
#include "GrColor.h" #include "GrColor.h"
#include "GrPrimitiveProcessor.h" #include "GrPrimitiveProcessor.h"
#include "GrQuad.h"
#include "GrSamplerState.h" #include "GrSamplerState.h"
#include "GrTypesPriv.h" #include "GrTypesPriv.h"
#include "glsl/GrGLSLPrimitiveProcessor.h" #include "glsl/GrGLSLPrimitiveProcessor.h"
@ -17,47 +18,47 @@
#include "SkPoint3.h" #include "SkPoint3.h"
class GrGLSLColorSpaceXformHelper; class GrGLSLColorSpaceXformHelper;
class GrPerspQuad;
class GrQuadPerEdgeAA { namespace GrQuadPerEdgeAA {
public:
enum class Domain : bool { kNo = false, kYes = true }; enum class Domain : bool { kNo = false, kYes = true };
// The vertex template provides a clean way of specifying the layout and components of a vertex // Specifies the vertex configuration for an op that renders per-edge AA quads. The vertex
// for a per-edge aa quad. However, because there are so many permutations possible, the struct // order (when enabled) is device position, color, local position, domain, aa edge equations.
// is defined this way to take away all layout control from the compiler and make // This order matches the constructor argument order of VertexSpec and is the order that
// sure that it matches what we need to send to the GPU. // GPAttributes maintains. If hasLocalCoords is false, then the local quad type can be ignored.
// struct VertexSpec {
// It is expected that most code using these vertices will only need to call the templated public:
// Tessellate() function with an appropriately sized vertex buffer and not need to modify or VertexSpec(GrQuadType deviceQuadType, bool hasColor, GrQuadType localQuadType,
// read the fields of a particular vertex. bool hasLocalCoords, Domain domain, GrAAType aa)
template <int PosDim, typename C, int LocalPosDim, Domain D, GrAA AA> : fDeviceQuadType(static_cast<unsigned>(deviceQuadType))
struct Vertex { , fLocalQuadType(static_cast<unsigned>(localQuadType))
using Color = C; , fHasLocalCoords(hasLocalCoords)
static constexpr GrAA kAA = AA; , fHasColor(hasColor)
static constexpr Domain kDomain = D; , fHasDomain(static_cast<unsigned>(domain))
static constexpr size_t kPositionDim = PosDim; , fUsesCoverageAA(aa == GrAAType::kCoverage) { }
static constexpr size_t kLocalPositionDim = LocalPosDim;
static constexpr size_t kPositionOffset = 0; GrQuadType deviceQuadType() const { return static_cast<GrQuadType>(fDeviceQuadType); }
static constexpr size_t kPositionSize = PosDim * sizeof(float); GrQuadType localQuadType() const { return static_cast<GrQuadType>(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; // Will always be 2 or 3
static constexpr size_t kColorSize = sizeof(Color); 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; private:
static constexpr size_t kLocalPositionSize = LocalPosDim * sizeof(float); static_assert(kGrQuadTypeCount <= 4, "GrQuadType doesn't fit in 2 bits");
static constexpr size_t kDomainOffset = kLocalPositionOffset + kLocalPositionSize; unsigned fDeviceQuadType: 2;
static constexpr size_t kDomainSize = D == Domain::kYes ? sizeof(SkRect) : 0; unsigned fLocalQuadType: 2;
unsigned fHasLocalCoords: 1;
static constexpr size_t kAAOffset = kDomainOffset + kDomainSize; unsigned fHasColor: 1;
static constexpr size_t kAASize = AA == GrAA::kYes ? 4 * sizeof(SkPoint3) : 0; unsigned fHasDomain: 1;
unsigned fUsesCoverageAA: 1;
static constexpr size_t kVertexSize = kAAOffset + kAASize;
// Make sure sizeof(Vertex<...>) == kVertexSize
char fData[kVertexSize];
}; };
// Utility class that manages the attribute state necessary to render a particular batch of // Utility class that manages the attribute state necessary to render a particular batch of
@ -77,7 +78,7 @@ public:
public: public:
using Attribute = GrPrimitiveProcessor::Attribute; 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& positions() const { return fPositions; }
const Attribute& colors() const { return fColors; } const Attribute& colors() const { return fColors; }
@ -131,44 +132,18 @@ public:
Attribute fAAEdges[4]; // named "aaEdgeX" for X = 0,1,2,3 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<typename V>
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: // Fill vertices with the vertex data needed to represent the given quad. The device position,
// Don't let the "namespace" class be instantiated // local coords, vertex color, domain, and edge coefficients will be written and/or computed
GrQuadPerEdgeAA(); // 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 } // namespace GrQuadPerEdgeAA
// 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);
};
#endif // GrQuadPerEdgeAA_DEFINED #endif // GrQuadPerEdgeAA_DEFINED

View File

@ -37,6 +37,7 @@
namespace { namespace {
using Domain = GrQuadPerEdgeAA::Domain; 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 * 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, const GrSamplerState::Filter filter,
sk_sp<GrColorSpaceXform> textureColorSpaceXform, sk_sp<GrColorSpaceXform> textureColorSpaceXform,
sk_sp<GrColorSpaceXform> paintColorSpaceXform, sk_sp<GrColorSpaceXform> paintColorSpaceXform,
int positionDim, GrAAType aa, Domain domain, const VertexSpec& vertexSpec, const GrShaderCaps& caps) {
const GrShaderCaps& caps) {
return sk_sp<TextureGeometryProcessor>(new TextureGeometryProcessor( return sk_sp<TextureGeometryProcessor>(new TextureGeometryProcessor(
textureType, textureConfig, filter, std::move(textureColorSpaceXform), 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"; } const char* name() const override { return "TextureGeometryProcessor"; }
@ -117,13 +117,14 @@ private:
TextureGeometryProcessor(GrTextureType textureType, GrPixelConfig textureConfig, TextureGeometryProcessor(GrTextureType textureType, GrPixelConfig textureConfig,
GrSamplerState::Filter filter, GrSamplerState::Filter filter,
sk_sp<GrColorSpaceXform> textureColorSpaceXform, sk_sp<GrColorSpaceXform> textureColorSpaceXform,
sk_sp<GrColorSpaceXform> paintColorSpaceXform, int positionDim, sk_sp<GrColorSpaceXform> paintColorSpaceXform,
GrAAType aa, Domain domain, const GrShaderCaps& caps) const VertexSpec& vertexSpec, const GrShaderCaps& caps)
: INHERITED(kTextureGeometryProcessor_ClassID) : INHERITED(kTextureGeometryProcessor_ClassID)
, fAttrs(positionDim, /* src dim */ 2, /* vertex color */ true, aa, domain) , fAttrs(vertexSpec)
, fTextureColorSpaceXform(std::move(textureColorSpaceXform)) , fTextureColorSpaceXform(std::move(textureColorSpaceXform))
, fPaintColorSpaceXform(std::move(paintColorSpaceXform)) , fPaintColorSpaceXform(std::move(paintColorSpaceXform))
, fSampler(textureType, textureConfig, filter) { , fSampler(textureType, textureConfig, filter) {
SkASSERT(vertexSpec.hasVertexColors() && vertexSpec.localDimensionality() == 2);
this->setTextureSamplerCnt(1); this->setTextureSamplerCnt(1);
this->setVertexAttributes(fAttrs.attributes(), fAttrs.attributeCount()); 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) { 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 ql = quad.x(0);
float qt = quad.y(0); float qt = quad.y(0);
float qr = quad.x(3); float qr = quad.x(3);
@ -320,11 +321,11 @@ private:
GrResolveAATypeForQuad(aaType, aaFlags, quad, quadType, &aaType, &aaFlags); GrResolveAATypeForQuad(aaType, aaFlags, quad, quadType, &aaType, &aaFlags);
fAAType = static_cast<unsigned>(aaType); fAAType = static_cast<unsigned>(aaType);
fPerspective = static_cast<unsigned>(quadType == GrQuadType::kPerspective_QuadType); fPerspective = static_cast<unsigned>(quadType == GrQuadType::kPerspective);
// We expect our caller to have already caught this optimization. // We expect our caller to have already caught this optimization.
SkASSERT(!srcRect.contains(proxy->getWorstCaseBoundsRect()) || SkASSERT(!srcRect.contains(proxy->getWorstCaseBoundsRect()) ||
constraint == SkCanvas::kFast_SrcRectConstraint); constraint == SkCanvas::kFast_SrcRectConstraint);
if (quadType == GrQuadType::kRect_QuadType) { if (quadType == GrQuadType::kRect) {
// Disable filtering if possible (note AA optimizations for rects are automatically // Disable filtering if possible (note AA optimizations for rects are automatically
// handled above in GrResolveAATypeForQuad). // handled above in GrResolveAATypeForQuad).
if (this->filter() != GrSamplerState::Filter::kNearest && if (this->filter() != GrSamplerState::Filter::kNearest &&
@ -386,7 +387,7 @@ private:
overallAAType = aaType; overallAAType = aaType;
} }
if (!mustFilter && this->filter() != GrSamplerState::Filter::kNearest) { 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); filter_has_effect_for_rect_stays_rect(quad, set[p].fSrcRect);
} }
fQuads.emplace_back(set[p].fSrcRect, quad, aaFlags, SkCanvas::kFast_SrcRectConstraint, fQuads.emplace_back(set[p].fSrcRect, quad, aaFlags, SkCanvas::kFast_SrcRectConstraint,
@ -401,13 +402,9 @@ private:
fDomain = static_cast<unsigned>(false); fDomain = static_cast<unsigned>(false);
} }
template <int PosDim, Domain D, GrAA AA> void tess(void* v, const VertexSpec& spec, const GrTextureProxy* proxy,
void tess(void* v, const GrGeometryProcessor* gp, const GrTextureProxy* proxy, int start, int start, int cnt) const {
int cnt) const {
TRACE_EVENT0("skia", TRACE_FUNC); TRACE_EVENT0("skia", TRACE_FUNC);
using Vertex = GrQuadPerEdgeAA::Vertex<PosDim, GrColor, 2, D, AA>;
SkASSERT(gp->vertexStride() == sizeof(Vertex));
auto vertices = static_cast<Vertex*>(v);
auto origin = proxy->origin(); auto origin = proxy->origin();
const auto* texture = proxy->peekTexture(); const auto* texture = proxy->peekTexture();
float iw = 1.f / texture->width(); float iw = 1.f / texture->width();
@ -417,9 +414,8 @@ private:
const auto q = fQuads[i]; const auto q = fQuads[i];
GrPerspQuad srcQuad = compute_src_quad(origin, q.srcRect(), iw, ih); GrPerspQuad srcQuad = compute_src_quad(origin, q.srcRect(), iw, ih);
SkRect domain = compute_domain(q.domain(), this->filter(), origin, q.srcRect(), iw, ih); SkRect domain = compute_domain(q.domain(), this->filter(), origin, q.srcRect(), iw, ih);
GrQuadPerEdgeAA::Tessellate<Vertex>( v = GrQuadPerEdgeAA::Tessellate(v, spec, q.quad(), q.color(), srcQuad, domain,
vertices, q.quad(), q.color(), srcQuad, domain, q.aaFlags()); q.aaFlags());
vertices += 4;
} }
} }
@ -453,10 +449,13 @@ private:
} }
} }
VertexSpec vertexSpec(hasPerspective ? GrQuadType::kPerspective : GrQuadType::kStandard,
/* hasColor */ true, GrQuadType::kRect, /* hasLocal */ true,
domain, aaType);
sk_sp<GrGeometryProcessor> gp = TextureGeometryProcessor::Make( sk_sp<GrGeometryProcessor> gp = TextureGeometryProcessor::Make(
textureType, config, this->filter(), std::move(fTextureColorSpaceXform), textureType, config, this->filter(), std::move(fTextureColorSpaceXform),
std::move(fPaintColorSpaceXform), hasPerspective ? 3 : 2, aaType, domain, std::move(fPaintColorSpaceXform), vertexSpec, *target->caps().shaderCaps());
*target->caps().shaderCaps());
GrPipeline::InitArgs args; GrPipeline::InitArgs args;
args.fProxy = target->proxy(); args.fProxy = target->proxy();
args.fCaps = &target->caps(); args.fCaps = &target->caps();
@ -480,33 +479,8 @@ private:
} }
const auto* pipeline = const auto* pipeline =
target->allocPipeline(args, GrProcessorSet::MakeEmptySet(), std::move(clip)); 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<Point, Domain, AA>, \
sizeof(GrQuadPerEdgeAA::Vertex<Point, GrColor, 2, Domain, AA>) \
}
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; size_t vertexSize = gp->vertexStride();
SkASSERT(vertexSize == gp->vertexStride());
GrMesh* meshes = target->allocMeshes(numProxies); GrMesh* meshes = target->allocMeshes(numProxies);
const GrBuffer* vbuffer; const GrBuffer* vbuffer;
@ -534,8 +508,7 @@ private:
} }
SkASSERT(numAllocatedVertices >= meshVertexCnt); SkASSERT(numAllocatedVertices >= meshVertexCnt);
(op.*(kTessFnsAndVertexSizes[tessFnIdx].fTessFn))(vdata, gp.get(), proxy, q, op.tess(vdata, vertexSpec, proxy, q, quadCnt);
quadCnt);
if (quadCnt > 1) { if (quadCnt > 1) {
meshes[m].setPrimitiveType(GrPrimitiveType::kTriangles); meshes[m].setPrimitiveType(GrPrimitiveType::kTriangles);