* Copyright 2017 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "src/gpu/ccpr/GrVSCoverageProcessor.h"
#include "src/gpu/GrMesh.h"
#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
class GrVSCoverageProcessor::Impl : public GrGLSLGeometryProcessor {
public:
Impl(std::unique_ptr<Shader> shader, int numSides)
: fShader(std::move(shader)), fNumSides(numSides) {}
private:
void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor&,
FPCoordTransformIter&& transformIter) final {
this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
}
void onEmitCode(EmitArgs&, GrGPArgs*) override;
const std::unique_ptr<Shader> fShader;
const int fNumSides;
};
static constexpr int kInstanceAttribIdx_X = 0;
static constexpr int kInstanceAttribIdx_Y = 1;
static constexpr int kVertexData_LeftNeighborIdShift = 10;
static constexpr int kVertexData_RightNeighborIdShift = 8;
static constexpr int kVertexData_BloatIdxShift = 6;
static constexpr int kVertexData_InvertNegativeCoverageBit = 1 << 5;
static constexpr int kVertexData_IsCornerBit = 1 << 4;
static constexpr int kVertexData_IsEdgeBit = 1 << 3;
static constexpr int kVertexData_IsHullBit = 1 << 2;
static constexpr int32_t pack_vertex_data(int32_t leftNeighborID, int32_t rightNeighborID,
int32_t bloatIdx, int32_t cornerID,
int32_t extraData = 0) {
return (leftNeighborID << kVertexData_LeftNeighborIdShift) |
(rightNeighborID << kVertexData_RightNeighborIdShift) |
(bloatIdx << kVertexData_BloatIdxShift) |
cornerID | extraData;
}
static constexpr int32_t hull_vertex_data(int32_t cornerID, int32_t bloatIdx, int n) {
return pack_vertex_data((cornerID + n - 1) % n, (cornerID + 1) % n, bloatIdx, cornerID,
kVertexData_IsHullBit);
}
static constexpr int32_t edge_vertex_data(int32_t edgeID, int32_t endptIdx, int32_t bloatIdx,
int n) {
return pack_vertex_data(0 == endptIdx ? (edgeID + 1) % n : edgeID,
0 == endptIdx ? (edgeID + 1) % n : edgeID,
bloatIdx, 0 == endptIdx ? edgeID : (edgeID + 1) % n,
kVertexData_IsEdgeBit |
(!endptIdx ? kVertexData_InvertNegativeCoverageBit : 0));
}
static constexpr int32_t corner_vertex_data(int32_t leftID, int32_t cornerID, int32_t rightID,
int32_t bloatIdx) {
return pack_vertex_data(leftID, rightID, bloatIdx, cornerID, kVertexData_IsCornerBit);
}
static constexpr int32_t kTriangleVertices[] = {
hull_vertex_data(0, 0, 3),
hull_vertex_data(0, 1, 3),
hull_vertex_data(0, 2, 3),
hull_vertex_data(1, 0, 3),
hull_vertex_data(1, 1, 3),
hull_vertex_data(1, 2, 3),
hull_vertex_data(2, 0, 3),
hull_vertex_data(2, 1, 3),
hull_vertex_data(2, 2, 3),
edge_vertex_data(0, 0, 0, 3),
edge_vertex_data(0, 0, 1, 3),
edge_vertex_data(0, 0, 2, 3),
edge_vertex_data(0, 1, 0, 3),
edge_vertex_data(0, 1, 1, 3),
edge_vertex_data(0, 1, 2, 3),
edge_vertex_data(1, 0, 0, 3),
edge_vertex_data(1, 0, 1, 3),
edge_vertex_data(1, 0, 2, 3),
edge_vertex_data(1, 1, 0, 3),
edge_vertex_data(1, 1, 1, 3),
edge_vertex_data(1, 1, 2, 3),
edge_vertex_data(2, 0, 0, 3),
edge_vertex_data(2, 0, 1, 3),
edge_vertex_data(2, 0, 2, 3),
edge_vertex_data(2, 1, 0, 3),
edge_vertex_data(2, 1, 1, 3),
edge_vertex_data(2, 1, 2, 3),
corner_vertex_data(2, 0, 1, 0),
corner_vertex_data(2, 0, 1, 1),
corner_vertex_data(2, 0, 1, 2),
corner_vertex_data(2, 0, 1, 3),
corner_vertex_data(0, 1, 2, 0),
corner_vertex_data(0, 1, 2, 1),
corner_vertex_data(0, 1, 2, 2),
corner_vertex_data(0, 1, 2, 3),
corner_vertex_data(1, 2, 0, 0),
corner_vertex_data(1, 2, 0, 1),
corner_vertex_data(1, 2, 0, 2),
corner_vertex_data(1, 2, 0, 3),
};
GR_DECLARE_STATIC_UNIQUE_KEY(gTriangleVertexBufferKey);
static constexpr uint16_t kRestartStrip = 0xffff;
static constexpr uint16_t kTriangleIndicesAsStrips[] = {
1, 2, 0, 3, 8, kRestartStrip,
4, 5, 3, 6, 8, 7, kRestartStrip,
10, 9, 11, 14, 12, 13, kRestartStrip,
16, 15, 17, 20, 18, 19, kRestartStrip,
22, 21, 23, 26, 24, 25, kRestartStrip,
28, 27, 29, 30, kRestartStrip,
32, 31, 33, 34, kRestartStrip,
36, 35, 37, 38
};
static constexpr uint16_t kTriangleIndicesAsTris[] = {
1, 2, 0,
2, 3, 0,
0, 3, 8,
4, 5, 3,
5, 6, 3,
3, 6, 8,
6, 7, 8,
10, 9, 11,
9, 14, 11,
11, 14, 12,
14, 13, 12,
16, 15, 17,
15, 20, 17,
17, 20, 18,
20, 19, 18,
22, 21, 23,
21, 26, 23,
23, 26, 24,
26, 25, 24,
28, 27, 29,
27, 30, 29,
32, 31, 33,
31, 34, 33,
36, 35, 37,
35, 38, 37,
};
GR_DECLARE_STATIC_UNIQUE_KEY(gTriangleIndexBufferKey);
static constexpr int32_t kCurveVertices[] = {
hull_vertex_data(0, 0, 4),
hull_vertex_data(0, 1, 4),
hull_vertex_data(0, 2, 4),
hull_vertex_data(1, 0, 4),
hull_vertex_data(1, 1, 4),
hull_vertex_data(1, 2, 4),
hull_vertex_data(2, 0, 4),
hull_vertex_data(2, 1, 4),
hull_vertex_data(2, 2, 4),
hull_vertex_data(3, 0, 4),
hull_vertex_data(3, 1, 4),
hull_vertex_data(3, 2, 4),
corner_vertex_data(3, 0, 1, 0),
corner_vertex_data(3, 0, 1, 1),
corner_vertex_data(3, 0, 1, 2),
corner_vertex_data(3, 0, 1, 3),
corner_vertex_data(2, 3, 0, 0),
corner_vertex_data(2, 3, 0, 1),
corner_vertex_data(2, 3, 0, 2),
corner_vertex_data(2, 3, 0, 3),
};
GR_DECLARE_STATIC_UNIQUE_KEY(gCurveVertexBufferKey);
static constexpr uint16_t kCurveIndicesAsStrips[] = {
1, 0, 2, 11, 3, 5, 4, kRestartStrip,
7, 6, 8, 5, 9, 11, 10, kRestartStrip,
13, 12, 14, 15, kRestartStrip,
17, 16, 18, 19
};
static constexpr uint16_t kCurveIndicesAsTris[] = {
1, 0, 2,
0, 11, 2,
2, 11, 3,
11, 5, 3,
3, 5, 4,
7, 6, 8,
6, 5, 8,
8, 5, 9,
5, 11, 9,
9, 11, 10,
13, 12, 14,
12, 15, 14,
17, 16, 18,
16, 19, 18,
};
GR_DECLARE_STATIC_UNIQUE_KEY(gCurveIndexBufferKey);
void GrVSCoverageProcessor::Impl::onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) {
const GrVSCoverageProcessor& proc = args.fGP.cast<GrVSCoverageProcessor>();
GrGLSLVertexBuilder* v = args.fVertBuilder;
int numInputPoints = proc.numInputPoints();
int inputWidth = (4 == numInputPoints || proc.hasInputWeight()) ? 4 : 3;
const char* swizzle = (4 == inputWidth) ? "xyzw" : "xyz";
v->codeAppendf("float%ix2 pts = transpose(float2x%i(%s.%s, %s.%s));", inputWidth, inputWidth,
proc.fInputXAndYValues[kInstanceAttribIdx_X].name(), swizzle,
proc.fInputXAndYValues[kInstanceAttribIdx_Y].name(), swizzle);
v->codeAppend ("half wind;");
Shader::CalcWind(proc, v, "pts", "wind");
if (PrimitiveType::kWeightedTriangles == proc.fPrimitiveType) {
SkASSERT(3 == numInputPoints);
SkASSERT(kFloat4_GrVertexAttribType ==
proc.fInputXAndYValues[kInstanceAttribIdx_X].cpuType());
v->codeAppendf("wind *= half(%s.w);",
proc.fInputXAndYValues[kInstanceAttribIdx_X].name());
}
float bloat = kAABloatRadius;
#ifdef SK_DEBUG
if (proc.debugBloatEnabled()) {
bloat *= proc.debugBloat();
}
#endif
v->defineConstant("bloat", bloat);
const char* hullPts = "pts";
fShader->emitSetupCode(v, "pts", (4 == fNumSides) ? &hullPts : nullptr);
v->codeAppendf("int clockwise_indices = wind > 0 ? %s : 0x%x - %s;",
proc.fPerVertexData.name(),
((fNumSides - 1) << kVertexData_LeftNeighborIdShift) |
((fNumSides - 1) << kVertexData_RightNeighborIdShift) |
(((1 << kVertexData_RightNeighborIdShift) - 1) ^ 3) |
(fNumSides - 1),
proc.fPerVertexData.name());
v->codeAppendf("float2 corner = %s[clockwise_indices & 3];", hullPts);
v->codeAppendf("float2 left = %s[clockwise_indices >> %i];",
hullPts, kVertexData_LeftNeighborIdShift);
v->codeAppendf("float2 right = %s[(clockwise_indices >> %i) & 3];",
hullPts, kVertexData_RightNeighborIdShift);
v->codeAppend ("float2 leftbloat = sign(corner - left);");
v->codeAppend ("leftbloat = float2(0 != leftbloat.y ? leftbloat.y : leftbloat.x, "
"0 != leftbloat.x ? -leftbloat.x : -leftbloat.y);");
v->codeAppend ("float2 rightbloat = sign(right - corner);");
v->codeAppend ("rightbloat = float2(0 != rightbloat.y ? rightbloat.y : rightbloat.x, "
"0 != rightbloat.x ? -rightbloat.x : -rightbloat.y);");
v->codeAppend ("bool2 left_right_notequal = notEqual(leftbloat, rightbloat);");
v->codeAppend ("float2 bloatdir = leftbloat;");
v->codeAppend ("float2 leftdir = corner - left;");
v->codeAppend ("leftdir = (float2(0) != leftdir) ? normalize(leftdir) : float2(1, 0);");
v->codeAppend ("float2 rightdir = right - corner;");
v->codeAppend ("rightdir = (float2(0) != rightdir) ? normalize(rightdir) : float2(1, 0);");
v->codeAppendf("if (0 != (%s & %i)) {",
proc.fPerVertexData.name(), kVertexData_IsCornerBit);
v->codeAppend ( "bloatdir = float2(leftdir.x > rightdir.x ? +1 : -1, "
"leftdir.y > rightdir.y ? +1 : -1);");
v->codeAppendf( "left_right_notequal = bool2(true);");
v->codeAppend ("}");
v->codeAppendf("int bloatidx = (%s >> %i) & 3;", proc.fPerVertexData.name(),
kVertexData_BloatIdxShift);
v->codeAppend ("switch (bloatidx) {");
v->codeAppend ( "case 3:");
v->codeAppend ( "bloatdir = float2(-bloatdir.y, +bloatdir.x);");
v->codeAppend ( "case 2:");
v->codeAppendf( "if (all(left_right_notequal)) {");
v->codeAppend ( "bloatdir = float2(-bloatdir.y, +bloatdir.x);");
v->codeAppend ( "}");
v->codeAppend ( "case 1:");
v->codeAppendf( "if (any(left_right_notequal)) {");
v->codeAppend ( "bloatdir = float2(-bloatdir.y, +bloatdir.x);");
v->codeAppend ( "}");
v->codeAppend ("}");
v->codeAppend ("float2 vertexpos = fma(bloatdir, float2(bloat), corner);");
gpArgs->fPositionVar.set(kFloat2_GrSLType, "vertexpos");
v->codeAppend ("half coverage = +1;");
if (3 == fNumSides) {
v->codeAppend ("half left_coverage; {");
Shader::CalcEdgeCoverageAtBloatVertex(v, "left", "corner", "bloatdir", "left_coverage");
v->codeAppend ("}");
v->codeAppend ("half right_coverage; {");
Shader::CalcEdgeCoverageAtBloatVertex(v, "corner", "right", "bloatdir", "right_coverage");
v->codeAppend ("}");
v->codeAppendf("if (0 != (%s & %i)) {",
proc.fPerVertexData.name(), kVertexData_IsEdgeBit);
v->codeAppend ( "coverage = left_coverage;");
v->codeAppend ("}");
v->codeAppendf("if (0 != (%s & %i)) {",
proc.fPerVertexData.name(),
kVertexData_InvertNegativeCoverageBit);
v->codeAppend ( "coverage = -1 - coverage;");
v->codeAppend ("}");
} else if (!fShader->calculatesOwnEdgeCoverage()) {
v->codeAppendf("float2 p0 = pts[0], p1 = pts[%i];", numInputPoints - 1);
v->codeAppendf("float2 n = float2(p0.y - p1.y, p1.x - p0.x);");
v->codeAppend ("float nwidth = bloat*2 * (abs(n.x) + abs(n.y));");
v->codeAppend ("float d = dot(p0 - vertexpos, n);");
v->codeAppend ("d /= (0 != nwidth) ? nwidth : 1;");
v->codeAppend ("coverage = half(d) - .5*sign(wind);");
}
v->codeAppend ("half2 corner_coverage = half2(0);");
v->codeAppendf("if (0 != (%s & %i)) {",
proc.fPerVertexData.name(), kVertexData_IsCornerBit);
v->codeAppend ( "wind = -wind;");
if (3 == fNumSides) {
v->codeAppend ("coverage = 1 + left_coverage + right_coverage;");
} else if (!fShader->calculatesOwnEdgeCoverage()) {
v->codeAppend ("coverage = -coverage;");
}
v->codeAppend ( "half attenuation; {");
Shader::CalcCornerAttenuation(v, "leftdir", "rightdir", "attenuation");
v->codeAppend ( "}");
v->codeAppend ( "corner_coverage = (0 == bloatidx) ? half2(0, attenuation) : half2(-1,+1);");
if (3 == fNumSides) {
v->codeAppend ("if (1 == bloatidx || 2 == bloatidx) {");
v->codeAppend ( "corner_coverage.x -= right_coverage;");
v->codeAppend ("}");
v->codeAppend ("if (bloatidx >= 2) {");
v->codeAppend ( "corner_coverage.x -= left_coverage;");
v->codeAppend ("}");
}
v->codeAppend ("}");
GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
v->codeAppend ("coverage *= wind;");
v->codeAppend ("corner_coverage.x *= wind;");
fShader->emitVaryings(varyingHandler, GrGLSLVarying::Scope::kVertToFrag, &AccessCodeString(v),
"vertexpos", "coverage", "corner_coverage", "wind");
varyingHandler->emitAttributes(proc);
SkASSERT(!args.fFPCoordTransformHandler->nextCoordTransform());
GrGLSLFPFragmentBuilder* f = args.fFragBuilder;
f->codeAppendf("half coverage;");
fShader->emitFragmentCoverageCode(f, "coverage");
f->codeAppendf("%s = half4(coverage);", args.fOutputColor);
f->codeAppendf("%s = half4(1);", args.fOutputCoverage);
}
void GrVSCoverageProcessor::reset(PrimitiveType primitiveType, GrResourceProvider* rp) {
const GrCaps& caps = *rp->caps();
fPrimitiveType = primitiveType;
switch (fPrimitiveType) {
case PrimitiveType::kTriangles:
case PrimitiveType::kWeightedTriangles: {
GR_DEFINE_STATIC_UNIQUE_KEY(gTriangleVertexBufferKey);
fVertexBuffer = rp->findOrMakeStaticBuffer(
GrGpuBufferType::kVertex, sizeof(kTriangleVertices), kTriangleVertices,
gTriangleVertexBufferKey);
GR_DEFINE_STATIC_UNIQUE_KEY(gTriangleIndexBufferKey);
if (caps.usePrimitiveRestart()) {
fIndexBuffer = rp->findOrMakeStaticBuffer(
GrGpuBufferType::kIndex, sizeof(kTriangleIndicesAsStrips),
kTriangleIndicesAsStrips, gTriangleIndexBufferKey);
fNumIndicesPerInstance = SK_ARRAY_COUNT(kTriangleIndicesAsStrips);
} else {
fIndexBuffer = rp->findOrMakeStaticBuffer(
GrGpuBufferType::kIndex, sizeof(kTriangleIndicesAsTris),
kTriangleIndicesAsTris, gTriangleIndexBufferKey);
fNumIndicesPerInstance = SK_ARRAY_COUNT(kTriangleIndicesAsTris);
}
break;
}
case PrimitiveType::kQuadratics:
case PrimitiveType::kCubics:
case PrimitiveType::kConics: {
GR_DEFINE_STATIC_UNIQUE_KEY(gCurveVertexBufferKey);
fVertexBuffer = rp->findOrMakeStaticBuffer(
GrGpuBufferType::kVertex, sizeof(kCurveVertices), kCurveVertices,
gCurveVertexBufferKey);
GR_DEFINE_STATIC_UNIQUE_KEY(gCurveIndexBufferKey);
if (caps.usePrimitiveRestart()) {
fIndexBuffer = rp->findOrMakeStaticBuffer(
GrGpuBufferType::kIndex, sizeof(kCurveIndicesAsStrips),
kCurveIndicesAsStrips, gCurveIndexBufferKey);
fNumIndicesPerInstance = SK_ARRAY_COUNT(kCurveIndicesAsStrips);
} else {
fIndexBuffer = rp->findOrMakeStaticBuffer(
GrGpuBufferType::kIndex, sizeof(kCurveIndicesAsTris), kCurveIndicesAsTris,
gCurveIndexBufferKey);
fNumIndicesPerInstance = SK_ARRAY_COUNT(kCurveIndicesAsTris);
}
break;
}
}
GrVertexAttribType xyAttribType;
GrSLType xySLType;
if (4 == this->numInputPoints() || this->hasInputWeight()) {
GR_STATIC_ASSERT(offsetof(QuadPointInstance, fX) == 0);
GR_STATIC_ASSERT(sizeof(QuadPointInstance::fX) ==
GrVertexAttribTypeSize(kFloat4_GrVertexAttribType));
GR_STATIC_ASSERT(sizeof(QuadPointInstance::fY) ==
GrVertexAttribTypeSize(kFloat4_GrVertexAttribType));
xyAttribType = kFloat4_GrVertexAttribType;
xySLType = kFloat4_GrSLType;
} else {
GR_STATIC_ASSERT(sizeof(TriPointInstance) ==
2 * GrVertexAttribTypeSize(kFloat3_GrVertexAttribType));
xyAttribType = kFloat3_GrVertexAttribType;
xySLType = kFloat3_GrSLType;
}
fInputXAndYValues[kInstanceAttribIdx_X] = {"X", xyAttribType, xySLType};
fInputXAndYValues[kInstanceAttribIdx_Y] = {"Y", xyAttribType, xySLType};
this->setInstanceAttributes(fInputXAndYValues, 2);
fPerVertexData = {"vertexdata", kInt_GrVertexAttribType, kInt_GrSLType};
this->setVertexAttributes(&fPerVertexData, 1);
if (caps.usePrimitiveRestart()) {
fTriangleType = GrPrimitiveType::kTriangleStrip;
} else {
fTriangleType = GrPrimitiveType::kTriangles;
}
}
void GrVSCoverageProcessor::appendMesh(sk_sp<const GrGpuBuffer> instanceBuffer, int instanceCount,
int baseInstance, SkTArray<GrMesh>* out) const {
GrMesh& mesh = out->emplace_back(fTriangleType);
auto primitiveRestart = GrPrimitiveRestart(GrPrimitiveType::kTriangleStrip == fTriangleType);
mesh.setIndexedInstanced(fIndexBuffer, fNumIndicesPerInstance, std::move(instanceBuffer),
instanceCount, baseInstance, primitiveRestart);
mesh.setVertexData(fVertexBuffer, 0);
}
GrGLSLPrimitiveProcessor* GrVSCoverageProcessor::onCreateGLSLInstance(
std::unique_ptr<Shader> shader) const {
switch (fPrimitiveType) {
case PrimitiveType::kTriangles:
case PrimitiveType::kWeightedTriangles:
return new Impl(std::move(shader), 3);
case PrimitiveType::kQuadratics:
case PrimitiveType::kCubics:
case PrimitiveType::kConics:
return new Impl(std::move(shader), 4);
}
SK_ABORT("Invalid PrimitiveType");
}