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:
parent
2c8e2bc682
commit
c182b9470f
@ -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]);
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user