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 {
// 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]);
}

View File

@ -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<int>(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 <typename Q>
void GrResolveAATypeForQuad(GrAAType requestedAAType, GrQuadAAFlags requestedEdgeFlags,

View File

@ -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<char*>(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<float*>(vb + posOffset), posDim);
// save color
if (colorSize) {
memcpy(vb + colorOffset, color, colorSize);
}
// save local position
if (srcDim) {
offset = load(localStorage, offset, reinterpret_cast<float*>(vb + srcOffset), srcDim);
}
// save the domain
if (domainSize) {
memcpy(vb + domainOffset, domain, domainSize);
if (deviceHasPerspective) {
vb.write<SkPoint3>({x[i], y[i], w[i]});
} else {
vb.write<SkPoint>({x[i], y[i]});
}
// save the edges
if (aaSize) {
float* edgeBuffer = reinterpret_cast<float*>(vb + aaOffset);
for (int j = 0; j < 4; j++) {
load(localStorage, edgeOffset + j, edgeBuffer, 3);
edgeBuffer += 3;
// save color
if (spec.hasVertexColors()) {
vb.write<GrColor>(color);
}
// save local position
if (spec.hasLocalCoords()) {
if (localHasPerspective) {
vb.write<SkPoint3>({u[i], v[i], r[i]});
} else {
vb.write<SkPoint>({u[i], v[i]});
}
}
vb += vertexSize;
// save the domain
if (spec.hasDomain()) {
vb.write<SkRect>(domain);
}
// save the edges
if (spec.usesCoverageAA()) {
for (int j = 0; j < 4; j++) {
vb.write<SkPoint3>({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

View File

@ -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 <int PosDim, typename C, int LocalPosDim, Domain D, GrAA AA>
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<unsigned>(deviceQuadType))
, fLocalQuadType(static_cast<unsigned>(localQuadType))
, fHasLocalCoords(hasLocalCoords)
, fHasColor(hasColor)
, fHasDomain(static_cast<unsigned>(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<GrQuadType>(fDeviceQuadType); }
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;
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<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:
// 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

View File

@ -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<GrColorSpaceXform> textureColorSpaceXform,
sk_sp<GrColorSpaceXform> paintColorSpaceXform,
int positionDim, GrAAType aa, Domain domain,
const GrShaderCaps& caps) {
const VertexSpec& vertexSpec, const GrShaderCaps& caps) {
return sk_sp<TextureGeometryProcessor>(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<GrColorSpaceXform> textureColorSpaceXform,
sk_sp<GrColorSpaceXform> paintColorSpaceXform, int positionDim,
GrAAType aa, Domain domain, const GrShaderCaps& caps)
sk_sp<GrColorSpaceXform> 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<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.
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<unsigned>(false);
}
template <int PosDim, Domain D, GrAA AA>
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<PosDim, GrColor, 2, D, AA>;
SkASSERT(gp->vertexStride() == sizeof(Vertex));
auto vertices = static_cast<Vertex*>(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<Vertex>(
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<GrGeometryProcessor> 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<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;
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);