Revert "ccpr: Simplify triangle corners"
This reverts commit 622650a194
.
Reason for revert: Going to try to improve AAA quality on curve corners
Original change's description:
> ccpr: Simplify triangle corners
>
> Modifies triangle corner shaders to just approximate their coverage with
> linear values that ramp to zero at bloat vertices outside the triangle.
>
> For the vertex backend, since corners now have the same fragment shader
> as the rest of the triangle, we fold them in with the other steps and
> draw triangles in a single pass.
>
> The geometry backend still draws triangles in two passes, as there is
> not an apparent performance advantage in combining them.
>
> Bug: skia:
> Change-Id: Ib4a89d793a3c706f734d0271875c8a3e5c87c49b
> Reviewed-on: https://skia-review.googlesource.com/112632
> Commit-Queue: Chris Dalton <csmartdalton@google.com>
> Reviewed-by: Brian Salomon <bsalomon@google.com>
TBR=bsalomon@google.com,csmartdalton@google.com
Change-Id: I45e7b9d7d7f8452b28bd54ca1e90a1f046cb2462
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Bug: skia:
Reviewed-on: https://skia-review.googlesource.com/113180
Reviewed-by: Chris Dalton <csmartdalton@google.com>
Commit-Queue: Chris Dalton <csmartdalton@google.com>
This commit is contained in:
parent
4997e14c80
commit
fe462efbcb
@ -314,6 +314,8 @@ skia_gpu_sources = [
|
||||
"$_src/gpu/ccpr/GrCCPathProcessor.h",
|
||||
"$_src/gpu/ccpr/GrCCQuadraticShader.cpp",
|
||||
"$_src/gpu/ccpr/GrCCQuadraticShader.h",
|
||||
"$_src/gpu/ccpr/GrCCTriangleShader.cpp",
|
||||
"$_src/gpu/ccpr/GrCCTriangleShader.h",
|
||||
"$_src/gpu/ccpr/GrCoverageCountingPathRenderer.cpp",
|
||||
"$_src/gpu/ccpr/GrCoverageCountingPathRenderer.h",
|
||||
|
||||
|
@ -280,7 +280,7 @@ void CCPRGeometryView::Op::onExecute(GrOpFlushState* state) {
|
||||
|
||||
if (!mesh.empty()) {
|
||||
SkASSERT(1 == mesh.count());
|
||||
proc.draw(state, pipeline, mesh.begin(), nullptr, 1, this->bounds());
|
||||
state->rtCommandBuffer()->draw(pipeline, proc, mesh.begin(), nullptr, 1, this->bounds());
|
||||
}
|
||||
|
||||
if (glGpu) {
|
||||
@ -337,7 +337,7 @@ bool CCPRGeometryView::onQuery(SkEvent* evt) {
|
||||
}
|
||||
SkUnichar unichar;
|
||||
if (SampleCode::CharQ(*evt, &unichar)) {
|
||||
if (unichar >= '1' && unichar <= '3') {
|
||||
if (unichar >= '1' && unichar <= '4') {
|
||||
fRenderPass = RenderPass(unichar - '1');
|
||||
this->updateAndInval();
|
||||
return true;
|
||||
|
@ -7,21 +7,17 @@
|
||||
|
||||
#include "GrCCCoverageProcessor.h"
|
||||
|
||||
#include "GrGpuCommandBuffer.h"
|
||||
#include "GrOpFlushState.h"
|
||||
#include "SkMakeUnique.h"
|
||||
#include "ccpr/GrCCCubicShader.h"
|
||||
#include "ccpr/GrCCQuadraticShader.h"
|
||||
#include "ccpr/GrCCTriangleShader.h"
|
||||
#include "glsl/GrGLSLVertexGeoBuilder.h"
|
||||
#include "glsl/GrGLSLFragmentShaderBuilder.h"
|
||||
#include "glsl/GrGLSLVertexGeoBuilder.h"
|
||||
|
||||
void GrCCCoverageProcessor::getGLSLProcessorKey(const GrShaderCaps&,
|
||||
GrProcessorKeyBuilder* b) const {
|
||||
int key = ((int)fRenderPass << 3);
|
||||
if (GSTriangleSubpass::kCorners == fGSTriangleSubpass) {
|
||||
key |= 4;
|
||||
}
|
||||
int key = (int)fRenderPass << 2;
|
||||
if (WindMethod::kInstanceData == fWindMethod) {
|
||||
key |= 2;
|
||||
}
|
||||
@ -40,7 +36,10 @@ GrGLSLPrimitiveProcessor* GrCCCoverageProcessor::createGLSLInstance(const GrShad
|
||||
std::unique_ptr<Shader> shader;
|
||||
switch (fRenderPass) {
|
||||
case RenderPass::kTriangles:
|
||||
shader = skstd::make_unique<Shader>();
|
||||
shader = skstd::make_unique<GrCCTriangleShader>();
|
||||
break;
|
||||
case RenderPass::kTriangleCorners:
|
||||
shader = skstd::make_unique<GrCCTriangleCornerShader>();
|
||||
break;
|
||||
case RenderPass::kQuadratics:
|
||||
shader = skstd::make_unique<GrCCQuadraticShader>();
|
||||
@ -53,45 +52,12 @@ GrGLSLPrimitiveProcessor* GrCCCoverageProcessor::createGLSLInstance(const GrShad
|
||||
: this->createVSImpl(std::move(shader));
|
||||
}
|
||||
|
||||
void GrCCCoverageProcessor::draw(GrOpFlushState* flushState, const GrPipeline& pipeline,
|
||||
const GrMesh meshes[],
|
||||
const GrPipeline::DynamicState dynamicStates[], int meshCount,
|
||||
const SkRect& drawBounds) const {
|
||||
GrGpuRTCommandBuffer* cmdBuff = flushState->rtCommandBuffer();
|
||||
cmdBuff->draw(pipeline, *this, meshes, dynamicStates, meshCount, drawBounds);
|
||||
|
||||
// Geometry shader backend draws triangles in two subpasses.
|
||||
if (RenderPass::kTriangles == fRenderPass && Impl::kGeometryShader == fImpl) {
|
||||
SkASSERT(GSTriangleSubpass::kHullsAndEdges == fGSTriangleSubpass);
|
||||
GrCCCoverageProcessor cornerProc(*this, GSTriangleSubpass::kCorners);
|
||||
cmdBuff->draw(pipeline, cornerProc, meshes, dynamicStates, meshCount, drawBounds);
|
||||
}
|
||||
}
|
||||
|
||||
void GrCCCoverageProcessor::Shader::emitVaryings(GrGLSLVaryingHandler* varyingHandler,
|
||||
GrGLSLVarying::Scope scope, SkString* code,
|
||||
const char* position, const char* coverage,
|
||||
const char* wind) {
|
||||
SkASSERT(GrGLSLVarying::Scope::kVertToGeo != scope);
|
||||
code->appendf("half coverageTimesWind = %s * %s;", coverage, wind);
|
||||
CoverageHandling coverageHandling = this->onEmitVaryings(varyingHandler, scope, code, position,
|
||||
"coverageTimesWind");
|
||||
if (CoverageHandling::kNotHandled == coverageHandling) {
|
||||
fCoverageTimesWind.reset(kHalf_GrSLType, scope);
|
||||
varyingHandler->addVarying("coverage_times_wind", &fCoverageTimesWind);
|
||||
code->appendf("%s = coverageTimesWind;", OutName(fCoverageTimesWind));
|
||||
}
|
||||
}
|
||||
|
||||
void GrCCCoverageProcessor::Shader::emitFragmentCode(const GrCCCoverageProcessor& proc,
|
||||
GrGLSLFPFragmentBuilder* f,
|
||||
const char* skOutputColor,
|
||||
const char* skOutputCoverage) const {
|
||||
f->codeAppendf("half coverage = +1;");
|
||||
f->codeAppendf("half coverage = 0;");
|
||||
this->onEmitFragmentCode(proc, f, "coverage");
|
||||
if (fCoverageTimesWind.fsIn()) {
|
||||
f->codeAppendf("coverage *= %s;", fCoverageTimesWind.fsIn());
|
||||
}
|
||||
f->codeAppendf("%s.a = coverage;", skOutputColor);
|
||||
f->codeAppendf("%s = half4(1);", skOutputCoverage);
|
||||
#ifdef SK_DEBUG
|
||||
@ -136,20 +102,3 @@ void GrCCCoverageProcessor::Shader::CalcEdgeCoverageAtBloatVertex(GrGLSLVertexGe
|
||||
// GPU divides by multiplying by the reciprocal?) It also guards against NaN when nwidth=0.
|
||||
s->codeAppendf("%s = (abs(t) != nwidth ? t / nwidth : sign(t)) * -.5 - .5;", outputCoverage);
|
||||
}
|
||||
|
||||
void GrCCCoverageProcessor::Shader::CalcEdgeCoveragesAtBloatVertices(GrGLSLVertexGeoBuilder* s,
|
||||
const char* leftPt,
|
||||
const char* rightPt,
|
||||
const char* bloatDir1,
|
||||
const char* bloatDir2,
|
||||
const char* outputCoverages) {
|
||||
// See comments in CalcEdgeCoverageAtBloatVertex.
|
||||
s->codeAppendf("float2 n = float2(%s.y - %s.y, %s.x - %s.x);",
|
||||
rightPt, leftPt, leftPt, rightPt);
|
||||
s->codeAppend ("float nwidth = abs(n.x) + abs(n.y);");
|
||||
s->codeAppendf("float2 t = n * float2x2(%s, %s);", bloatDir1, bloatDir2);
|
||||
s->codeAppendf("for (int i = 0; i < 2; ++i) {");
|
||||
s->codeAppendf( "%s[i] = (abs(t[i]) != nwidth ? t[i] / nwidth : sign(t[i])) * -.5 - .5;",
|
||||
outputCoverages);
|
||||
s->codeAppendf("}");
|
||||
}
|
||||
|
@ -10,7 +10,6 @@
|
||||
|
||||
#include "GrCaps.h"
|
||||
#include "GrGeometryProcessor.h"
|
||||
#include "GrPipeline.h"
|
||||
#include "GrShaderCaps.h"
|
||||
#include "SkNx.h"
|
||||
#include "glsl/GrGLSLGeometryProcessor.h"
|
||||
@ -19,7 +18,6 @@
|
||||
class GrGLSLFPFragmentBuilder;
|
||||
class GrGLSLVertexGeoBuilder;
|
||||
class GrMesh;
|
||||
class GrOpFlushState;
|
||||
|
||||
/**
|
||||
* This is the geometry processor for the simple convex primitive shapes (triangles and closed,
|
||||
@ -56,9 +54,11 @@ public:
|
||||
void set(const SkPoint&, const SkPoint&, const SkPoint&, const Sk2f& trans, float w);
|
||||
};
|
||||
// Here we enumerate every render pass needed in order to produce a complete coverage count
|
||||
// mask. This is an exhaustive list of all ccpr coverage shaders.
|
||||
// mask. Triangles require two render passes: One to draw a rough outline, and a second pass to
|
||||
// touch up the corners. This is an exhaustive list of all ccpr coverage shaders.
|
||||
enum class RenderPass {
|
||||
kTriangles,
|
||||
kTriangleCorners,
|
||||
kQuadratics,
|
||||
kCubics,
|
||||
};
|
||||
@ -83,6 +83,18 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
// Appends a GrMesh that will draw the provided instances. The instanceBuffer must be an array
|
||||
// of either TriPointInstance or QuadPointInstance, depending on this processor's RendererPass,
|
||||
// with coordinates in the desired shape's final atlas-space position.
|
||||
void appendMesh(GrBuffer* instanceBuffer, int instanceCount, int baseInstance,
|
||||
SkTArray<GrMesh>* out) {
|
||||
if (Impl::kGeometryShader == fImpl) {
|
||||
this->appendGSMesh(instanceBuffer, instanceCount, baseInstance, out);
|
||||
} else {
|
||||
this->appendVSMesh(instanceBuffer, instanceCount, baseInstance, out);
|
||||
}
|
||||
}
|
||||
|
||||
// GrPrimitiveProcessor overrides.
|
||||
const char* name() const override { return RenderPassName(fRenderPass); }
|
||||
SkString dumpInfo() const override {
|
||||
@ -99,38 +111,38 @@ public:
|
||||
float debugBloat() const { SkASSERT(this->debugVisualizationsEnabled()); return fDebugBloat; }
|
||||
#endif
|
||||
|
||||
// Appends a GrMesh that will draw the provided instances. The instanceBuffer must be an array
|
||||
// of either TriPointInstance or QuadPointInstance, depending on this processor's RendererPass,
|
||||
// with coordinates in the desired shape's final atlas-space position.
|
||||
void appendMesh(GrBuffer* instanceBuffer, int instanceCount, int baseInstance,
|
||||
SkTArray<GrMesh>* out) const {
|
||||
if (Impl::kGeometryShader == fImpl) {
|
||||
this->appendGSMesh(instanceBuffer, instanceCount, baseInstance, out);
|
||||
} else {
|
||||
this->appendVSMesh(instanceBuffer, instanceCount, baseInstance, out);
|
||||
}
|
||||
}
|
||||
|
||||
void draw(GrOpFlushState*, const GrPipeline&, const GrMesh[], const GrPipeline::DynamicState[],
|
||||
int meshCount, const SkRect& drawBounds) const;
|
||||
|
||||
// The Shader provides code to calculate a pixel's coverage.
|
||||
// The Shader provides code to calculate each pixel's coverage in a RenderPass. It also
|
||||
// provides details about shape-specific geometry.
|
||||
class Shader {
|
||||
public:
|
||||
// Called before generating geometry. Subclasses may use this opportunity to setup internal
|
||||
// member variables that will be needed during onEmitVaryings (e.g. transformation
|
||||
// matrices).
|
||||
//
|
||||
// Returns the name of a newly defined list of points around which the Impl should generate
|
||||
// its geometry, or null if it should just use the input points. (Regardless, the size of
|
||||
// whatever list of points indicated should match the size expected by the Impl: 3 points
|
||||
// for triangles, and 4 for quadratics and cubics.)
|
||||
virtual const char* emitSetupCode(GrGLSLVertexGeoBuilder*, const char* pts) const {
|
||||
return nullptr;
|
||||
}
|
||||
union GeometryVars {
|
||||
struct {
|
||||
const char* fAlternatePoints; // floatNx2 (if left null, will use input points).
|
||||
} fHullVars;
|
||||
|
||||
void emitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code,
|
||||
const char* position, const char* coverage, const char* wind);
|
||||
struct {
|
||||
const char* fPoint; // float2
|
||||
} fCornerVars;
|
||||
|
||||
GeometryVars() { memset(this, 0, sizeof(*this)); }
|
||||
};
|
||||
|
||||
// Called before generating geometry. Subclasses must fill out the applicable fields in
|
||||
// GeometryVars (if any), and may also use this opportunity to setup internal member
|
||||
// variables that will be needed during onEmitVaryings (e.g. transformation matrices).
|
||||
//
|
||||
// repetitionID is a 0-based index and indicates which edge or corner is being generated.
|
||||
// It will be null when generating a hull.
|
||||
virtual void emitSetupCode(GrGLSLVertexGeoBuilder*, const char* pts,
|
||||
const char* repetitionID, const char* wind,
|
||||
GeometryVars*) const {}
|
||||
|
||||
void emitVaryings(GrGLSLVaryingHandler* varyingHandler, GrGLSLVarying::Scope scope,
|
||||
SkString* code, const char* position, const char* inputCoverage,
|
||||
const char* wind) {
|
||||
SkASSERT(GrGLSLVarying::Scope::kVertToGeo != scope);
|
||||
this->onEmitVaryings(varyingHandler, scope, code, position, inputCoverage, wind);
|
||||
}
|
||||
|
||||
void emitFragmentCode(const GrCCCoverageProcessor&, GrGLSLFPFragmentBuilder*,
|
||||
const char* skOutputColor, const char* skOutputCoverage) const;
|
||||
@ -145,37 +157,20 @@ public:
|
||||
const char* rightPt, const char* rasterVertexDir,
|
||||
const char* outputCoverage);
|
||||
|
||||
// Calculates an edge's coverage at two conservative raster vertices.
|
||||
// (See CalcEdgeCoverageAtBloatVertex).
|
||||
static void CalcEdgeCoveragesAtBloatVertices(GrGLSLVertexGeoBuilder*, const char* leftPt,
|
||||
const char* rightPt, const char* bloatDir1,
|
||||
const char* bloatDir2,
|
||||
const char* outputCoverages);
|
||||
|
||||
virtual ~Shader() {}
|
||||
|
||||
protected:
|
||||
enum class CoverageHandling : bool {
|
||||
kHandled,
|
||||
kNotHandled
|
||||
};
|
||||
|
||||
// Here the subclass adds its internal varyings to the handler and produces code to
|
||||
// initialize those varyings from a given position and coverage/wind.
|
||||
// initialize those varyings from a given position, input coverage value, and wind.
|
||||
//
|
||||
// Returns whether the subclass will handle coverage modulation or if this base class should
|
||||
// take charge of multiplying the final coverage output by 'coverageTimesWind'.
|
||||
virtual CoverageHandling onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope,
|
||||
SkString* code, const char* position,
|
||||
const char* coverageTimesWind) {
|
||||
return CoverageHandling::kNotHandled;
|
||||
}
|
||||
// NOTE: the coverage input is only relevant for triangles. Otherwise it is null.
|
||||
virtual void onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code,
|
||||
const char* position, const char* inputCoverage,
|
||||
const char* wind) = 0;
|
||||
|
||||
// Emits the fragment code that calculates a pixel's coverage value. If using
|
||||
// CoverageHandling::kHandled, this value must be signed and modulated appropriately by
|
||||
// coverage.
|
||||
// Emits the fragment code that calculates a pixel's signed coverage value.
|
||||
virtual void onEmitFragmentCode(const GrCCCoverageProcessor&, GrGLSLFPFragmentBuilder*,
|
||||
const char* outputCoverage) const {}
|
||||
const char* outputCoverage) const = 0;
|
||||
|
||||
// Returns the name of a Shader's internal varying at the point where where its value is
|
||||
// assigned. This is intended to work whether called for a vertex or a geometry shader.
|
||||
@ -184,9 +179,6 @@ public:
|
||||
SkASSERT(Scope::kVertToGeo != varying.scope());
|
||||
return Scope::kGeoToFrag == varying.scope() ? varying.gsOut() : varying.vsOut();
|
||||
}
|
||||
|
||||
private:
|
||||
GrGLSLVarying fCoverageTimesWind;
|
||||
};
|
||||
|
||||
class GSImpl;
|
||||
@ -205,24 +197,6 @@ private:
|
||||
kVertexShader
|
||||
};
|
||||
|
||||
// Geometry shader backend draws triangles in two subpasses.
|
||||
enum class GSTriangleSubpass : bool {
|
||||
kHullsAndEdges,
|
||||
kCorners
|
||||
};
|
||||
|
||||
GrCCCoverageProcessor(const GrCCCoverageProcessor& proc, GSTriangleSubpass subpass)
|
||||
: INHERITED(kGrCCCoverageProcessor_ClassID)
|
||||
, fRenderPass(RenderPass::kTriangles)
|
||||
, fWindMethod(proc.fWindMethod)
|
||||
, fImpl(Impl::kGeometryShader)
|
||||
SkDEBUGCODE(, fDebugBloat(proc.fDebugBloat))
|
||||
, fGSTriangleSubpass(subpass) {
|
||||
SkASSERT(RenderPass::kTriangles == proc.fRenderPass);
|
||||
SkASSERT(Impl::kGeometryShader == proc.fImpl);
|
||||
this->initGS();
|
||||
}
|
||||
|
||||
void initGS();
|
||||
void initVS(GrResourceProvider*);
|
||||
|
||||
@ -239,9 +213,6 @@ private:
|
||||
const Impl fImpl;
|
||||
SkDEBUGCODE(float fDebugBloat = 0);
|
||||
|
||||
// Used by GSImpl.
|
||||
const GSTriangleSubpass fGSTriangleSubpass = GSTriangleSubpass::kHullsAndEdges;
|
||||
|
||||
// Used by VSImpl.
|
||||
sk_sp<const GrBuffer> fVertexBuffer;
|
||||
sk_sp<const GrBuffer> fIndexBuffer;
|
||||
@ -283,6 +254,7 @@ inline void GrCCCoverageProcessor::QuadPointInstance::set(const SkPoint& p0, con
|
||||
inline const char* GrCCCoverageProcessor::RenderPassName(RenderPass pass) {
|
||||
switch (pass) {
|
||||
case RenderPass::kTriangles: return "kTriangles";
|
||||
case RenderPass::kTriangleCorners: return "kTriangleCorners";
|
||||
case RenderPass::kQuadratics: return "kQuadratics";
|
||||
case RenderPass::kCubics: return "kCubics";
|
||||
}
|
||||
|
@ -75,7 +75,10 @@ protected:
|
||||
SkString emitVertexFn;
|
||||
SkSTArray<2, GrShaderVar> emitArgs;
|
||||
const char* position = emitArgs.emplace_back("position", kFloat2_GrSLType).c_str();
|
||||
const char* coverage = emitArgs.emplace_back("coverage", kHalf_GrSLType).c_str();
|
||||
const char* coverage = nullptr;
|
||||
if (RenderPass::kTriangleCorners != proc.fRenderPass) {
|
||||
coverage = emitArgs.emplace_back("coverage", kHalf_GrSLType).c_str();
|
||||
}
|
||||
g->emitFunction(kVoid_GrSLType, "emitVertex", emitArgs.count(), emitArgs.begin(), [&]() {
|
||||
SkString fnBody;
|
||||
fShader->emitVaryings(varyingHandler, GrGLSLVarying::Scope::kGeoToFrag, &fnBody,
|
||||
@ -113,7 +116,7 @@ protected:
|
||||
* coverage ramp from -1 to 0. These edge coverage values convert jagged conservative raster edges
|
||||
* into smooth, antialiased ones.
|
||||
*
|
||||
* The final corners get touched up in a later step by GSTriangleCornerImpl.
|
||||
* The final corners get touched up in a later step by GSCornerImpl.
|
||||
*/
|
||||
class GSTriangleImpl : public GrCCCoverageProcessor::GSImpl {
|
||||
public:
|
||||
@ -121,8 +124,6 @@ public:
|
||||
|
||||
void onEmitGeometryShader(GrGLSLGeometryBuilder* g, const GrShaderVar& wind,
|
||||
const char* emitVertexFn) const override {
|
||||
SkAssertResult(!fShader->emitSetupCode(g, "pts"));
|
||||
|
||||
// Visualize the input triangle as upright and equilateral, with a flat base. Paying special
|
||||
// attention to wind, we can identify the points as top, bottom-left, and bottom-right.
|
||||
//
|
||||
@ -210,70 +211,20 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Generates conservative rasters around triangle corners (aka pixel-size boxes) and calculates
|
||||
* coverage ramps that fix up the coverage values written by GSTriangleImpl.
|
||||
*/
|
||||
class GSTriangleCornerImpl : public GrCCCoverageProcessor::GSImpl {
|
||||
public:
|
||||
GSTriangleCornerImpl(std::unique_ptr<Shader> shader) : GSImpl(std::move(shader)) {}
|
||||
|
||||
void onEmitGeometryShader(GrGLSLGeometryBuilder* g, const GrShaderVar& wind,
|
||||
const char* emitVertexFn) const override {
|
||||
SkAssertResult(!fShader->emitSetupCode(g, "pts"));
|
||||
|
||||
g->codeAppendf("float2 corner = pts[sk_InvocationID];");
|
||||
g->codeAppendf("float2 left = pts[(sk_InvocationID + (%s > 0 ? 2 : 1)) %% 3];",
|
||||
wind.c_str());
|
||||
g->codeAppendf("float2 right = pts[(sk_InvocationID + (%s > 0 ? 1 : 2)) %% 3];",
|
||||
wind.c_str());
|
||||
|
||||
// Find "outbloat" and "crossbloat" at our corner. The outbloat points diagonally out of the
|
||||
// triangle, in the direction that should ramp to zero coverage. The crossbloat runs
|
||||
// perpindicular to outbloat, and ramps from left-edge coverage to right-edge coverage.
|
||||
g->codeAppend ("float2 leftdir = normalize(corner - left);");
|
||||
g->codeAppend ("float2 rightdir = normalize(right - corner);");
|
||||
g->codeAppend ("float2 outbloat = float2(leftdir.x > rightdir.x ? +1 : -1, "
|
||||
"leftdir.y > rightdir.y ? +1 : -1);");
|
||||
g->codeAppend ("float2 crossbloat = float2(-outbloat.y, +outbloat.x);");
|
||||
|
||||
g->codeAppend ("half2 left_coverages; {");
|
||||
Shader::CalcEdgeCoveragesAtBloatVertices(g, "left", "corner", "outbloat", "crossbloat",
|
||||
"left_coverages");
|
||||
g->codeAppend ("}");
|
||||
|
||||
g->codeAppend ("half2 right_coverages; {");
|
||||
Shader::CalcEdgeCoveragesAtBloatVertices(g, "corner", "right", "outbloat", "-crossbloat",
|
||||
"right_coverages");
|
||||
g->codeAppend ("}");
|
||||
|
||||
// Emit a corner box that erases whatever coverage was written previously, and replaces it
|
||||
// using linearly-interpolated values that ramp to zero in bloat vertices that fall outside
|
||||
// the triangle.
|
||||
//
|
||||
// NOTE: Since this is not a linear mapping, it is important that the box's diagonal shared
|
||||
// edge points out of the triangle as much as possible.
|
||||
g->codeAppendf("%s(corner - crossbloat * bloat, -right_coverages[1]);", emitVertexFn);
|
||||
g->codeAppendf("%s(corner + outbloat * bloat, "
|
||||
"-1 - left_coverages[0] - right_coverages[0]);", emitVertexFn);
|
||||
g->codeAppendf("%s(corner - outbloat * bloat, 0);", emitVertexFn);
|
||||
g->codeAppendf("%s(corner + crossbloat * bloat, -left_coverages[1]);", emitVertexFn);
|
||||
|
||||
g->configure(InputType::kLines, OutputType::kTriangleStrip, 4, 3);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Generates a conservative raster hull around a convex quadrilateral that encloses a cubic or
|
||||
* quadratic, as well as its shared edge.
|
||||
*/
|
||||
class GSCurveImpl : public GrCCCoverageProcessor::GSImpl {
|
||||
class GSHull4Impl : public GrCCCoverageProcessor::GSImpl {
|
||||
public:
|
||||
GSCurveImpl(std::unique_ptr<Shader> shader) : GSImpl(std::move(shader)) {}
|
||||
GSHull4Impl(std::unique_ptr<Shader> shader) : GSImpl(std::move(shader)) {}
|
||||
|
||||
void onEmitGeometryShader(GrGLSLGeometryBuilder* g, const GrShaderVar& wind,
|
||||
const char* emitVertexFn) const override {
|
||||
const char* hullPts = fShader->emitSetupCode(g, "pts");
|
||||
Shader::GeometryVars vars;
|
||||
fShader->emitSetupCode(g, "pts", nullptr, wind.c_str(), &vars);
|
||||
|
||||
const char* hullPts = vars.fHullVars.fAlternatePoints;
|
||||
if (!hullPts) {
|
||||
hullPts = "pts";
|
||||
}
|
||||
@ -363,6 +314,34 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Generates conservative rasters around corners. (See comments for RenderPass)
|
||||
*/
|
||||
class GSCornerImpl : public GrCCCoverageProcessor::GSImpl {
|
||||
public:
|
||||
GSCornerImpl(std::unique_ptr<Shader> shader, int numCorners)
|
||||
: GSImpl(std::move(shader)), fNumCorners(numCorners) {}
|
||||
|
||||
void onEmitGeometryShader(GrGLSLGeometryBuilder* g, const GrShaderVar& wind,
|
||||
const char* emitVertexFn) const override {
|
||||
Shader::GeometryVars vars;
|
||||
fShader->emitSetupCode(g, "pts", "sk_InvocationID", wind.c_str(), &vars);
|
||||
|
||||
const char* corner = vars.fCornerVars.fPoint;
|
||||
SkASSERT(corner);
|
||||
|
||||
g->codeAppendf("%s(%s + float2(-bloat, -bloat));", emitVertexFn, corner);
|
||||
g->codeAppendf("%s(%s + float2(-bloat, +bloat));", emitVertexFn, corner);
|
||||
g->codeAppendf("%s(%s + float2(+bloat, -bloat));", emitVertexFn, corner);
|
||||
g->codeAppendf("%s(%s + float2(+bloat, +bloat));", emitVertexFn, corner);
|
||||
|
||||
g->configure(InputType::kLines, OutputType::kTriangleStrip, 4, fNumCorners);
|
||||
}
|
||||
|
||||
private:
|
||||
const int fNumCorners;
|
||||
};
|
||||
|
||||
void GrCCCoverageProcessor::initGS() {
|
||||
SkASSERT(Impl::kGeometryShader == fImpl);
|
||||
if (RenderPass::kCubics == fRenderPass || WindMethod::kInstanceData == fWindMethod) {
|
||||
@ -393,12 +372,12 @@ void GrCCCoverageProcessor::appendGSMesh(GrBuffer* instanceBuffer, int instanceC
|
||||
GrGLSLPrimitiveProcessor* GrCCCoverageProcessor::createGSImpl(std::unique_ptr<Shader> shadr) const {
|
||||
switch (fRenderPass) {
|
||||
case RenderPass::kTriangles:
|
||||
return (GSTriangleSubpass::kHullsAndEdges == fGSTriangleSubpass)
|
||||
? (GSImpl*) new GSTriangleImpl(std::move(shadr))
|
||||
: (GSImpl*) new GSTriangleCornerImpl(std::move(shadr));
|
||||
return new GSTriangleImpl(std::move(shadr));
|
||||
case RenderPass::kTriangleCorners:
|
||||
return new GSCornerImpl(std::move(shadr), 3);
|
||||
case RenderPass::kQuadratics:
|
||||
case RenderPass::kCubics:
|
||||
return new GSCurveImpl(std::move(shadr));
|
||||
return new GSHull4Impl(std::move(shadr));
|
||||
}
|
||||
SK_ABORT("Invalid RenderPass");
|
||||
return nullptr;
|
||||
|
@ -10,15 +10,89 @@
|
||||
#include "GrMesh.h"
|
||||
#include "glsl/GrGLSLVertexGeoBuilder.h"
|
||||
|
||||
using Shader = GrCCCoverageProcessor::Shader;
|
||||
|
||||
static constexpr int kAttribIdx_X = 0;
|
||||
static constexpr int kAttribIdx_Y = 1;
|
||||
static constexpr int kAttribIdx_VertexData = 2;
|
||||
|
||||
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;
|
||||
/**
|
||||
* This class and its subclasses implement the coverage processor with vertex shaders.
|
||||
*/
|
||||
class GrCCCoverageProcessor::VSImpl : public GrGLSLGeometryProcessor {
|
||||
protected:
|
||||
VSImpl(std::unique_ptr<Shader> shader) : fShader(std::move(shader)) {}
|
||||
|
||||
void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor&,
|
||||
FPCoordTransformIter&& transformIter) final {
|
||||
this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
|
||||
}
|
||||
|
||||
void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) final {
|
||||
const GrCCCoverageProcessor& proc = args.fGP.cast<GrCCCoverageProcessor>();
|
||||
|
||||
// Vertex shader.
|
||||
GrGLSLVertexBuilder* v = args.fVertBuilder;
|
||||
int numInputPoints = proc.numInputPoints();
|
||||
|
||||
const char* swizzle = (4 == numInputPoints) ? "xyzw" : "xyz";
|
||||
v->codeAppendf("float%ix2 pts = transpose(float2x%i(%s.%s, %s.%s));",
|
||||
numInputPoints, numInputPoints, proc.getAttrib(kAttribIdx_X).fName, swizzle,
|
||||
proc.getAttrib(kAttribIdx_Y).fName, swizzle);
|
||||
|
||||
if (WindMethod::kCrossProduct == proc.fWindMethod) {
|
||||
v->codeAppend ("float area_x2 = determinant(float2x2(pts[0] - pts[1], "
|
||||
"pts[0] - pts[2]));");
|
||||
if (4 == numInputPoints) {
|
||||
v->codeAppend ("area_x2 += determinant(float2x2(pts[0] - pts[2], "
|
||||
"pts[0] - pts[3]));");
|
||||
}
|
||||
v->codeAppend ("half wind = sign(area_x2);");
|
||||
} else {
|
||||
SkASSERT(WindMethod::kInstanceData == proc.fWindMethod);
|
||||
SkASSERT(3 == numInputPoints);
|
||||
SkASSERT(kFloat4_GrVertexAttribType == proc.getAttrib(kAttribIdx_X).fType);
|
||||
v->codeAppendf("half wind = %s.w;", proc.getAttrib(kAttribIdx_X).fName);
|
||||
}
|
||||
|
||||
float bloat = kAABloatRadius;
|
||||
#ifdef SK_DEBUG
|
||||
if (proc.debugVisualizationsEnabled()) {
|
||||
bloat *= proc.debugBloat();
|
||||
}
|
||||
#endif
|
||||
v->defineConstant("bloat", bloat);
|
||||
|
||||
const char* coverage = this->emitVertexPosition(proc, v, gpArgs);
|
||||
SkASSERT(kFloat2_GrSLType == gpArgs->fPositionVar.getType());
|
||||
|
||||
GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
|
||||
SkString varyingCode;
|
||||
fShader->emitVaryings(varyingHandler, GrGLSLVarying::Scope::kVertToFrag, &varyingCode,
|
||||
gpArgs->fPositionVar.c_str(), coverage, "wind");
|
||||
v->codeAppend(varyingCode.c_str());
|
||||
|
||||
varyingHandler->emitAttributes(proc);
|
||||
SkASSERT(!args.fFPCoordTransformHandler->nextCoordTransform());
|
||||
|
||||
// Fragment shader.
|
||||
fShader->emitFragmentCode(proc, args.fFragBuilder, args.fOutputColor, args.fOutputCoverage);
|
||||
}
|
||||
|
||||
virtual const char* emitVertexPosition(const GrCCCoverageProcessor&, GrGLSLVertexBuilder*,
|
||||
GrGPArgs*) const = 0;
|
||||
|
||||
virtual ~VSImpl() {}
|
||||
|
||||
const std::unique_ptr<Shader> fShader;
|
||||
|
||||
typedef GrGLSLGeometryProcessor INHERITED;
|
||||
};
|
||||
|
||||
static constexpr int kVertexData_LeftNeighborIdShift = 9;
|
||||
static constexpr int kVertexData_RightNeighborIdShift = 7;
|
||||
static constexpr int kVertexData_BloatIdxShift = 5;
|
||||
static constexpr int kVertexData_InvertNegativeCoverageBit = 1 << 4;
|
||||
static constexpr int kVertexData_IsEdgeBit = 1 << 3;
|
||||
static constexpr int kVertexData_IsHullBit = 1 << 2;
|
||||
|
||||
@ -45,12 +119,8 @@ static constexpr int32_t edge_vertex_data(int32_t leftID, int rightID, int32_t b
|
||||
return pack_vertex_data(leftID, leftID, bloatIdx, rightID, kVertexData_IsEdgeBit | extraData);
|
||||
}
|
||||
|
||||
static constexpr int32_t triangle_corner_vertex_data(int32_t cornerID, int32_t bloatIdx) {
|
||||
return pack_vertex_data((cornerID + 2) % 3, (cornerID + 1) % 3, bloatIdx, cornerID,
|
||||
kVertexData_IsCornerBit);
|
||||
}
|
||||
|
||||
static constexpr int32_t kTriangleVertices[] = {
|
||||
static constexpr int32_t kHull3AndEdgeVertices[] = {
|
||||
hull_vertex_data(0, 0, 3),
|
||||
hull_vertex_data(0, 1, 3),
|
||||
hull_vertex_data(0, 2, 3),
|
||||
@ -81,39 +151,21 @@ static constexpr int32_t kTriangleVertices[] = {
|
||||
edge_vertex_data(0, 2, 0, kVertexData_InvertNegativeCoverageBit),
|
||||
edge_vertex_data(0, 2, 1, kVertexData_InvertNegativeCoverageBit),
|
||||
edge_vertex_data(0, 2, 2, kVertexData_InvertNegativeCoverageBit),
|
||||
|
||||
triangle_corner_vertex_data(0, 0),
|
||||
triangle_corner_vertex_data(0, 1),
|
||||
triangle_corner_vertex_data(0, 2),
|
||||
triangle_corner_vertex_data(0, 3),
|
||||
|
||||
triangle_corner_vertex_data(1, 0),
|
||||
triangle_corner_vertex_data(1, 1),
|
||||
triangle_corner_vertex_data(1, 2),
|
||||
triangle_corner_vertex_data(1, 3),
|
||||
|
||||
triangle_corner_vertex_data(2, 0),
|
||||
triangle_corner_vertex_data(2, 1),
|
||||
triangle_corner_vertex_data(2, 2),
|
||||
triangle_corner_vertex_data(2, 3),
|
||||
};
|
||||
|
||||
GR_DECLARE_STATIC_UNIQUE_KEY(gTriangleVertexBufferKey);
|
||||
GR_DECLARE_STATIC_UNIQUE_KEY(gHull3AndEdgeVertexBufferKey);
|
||||
|
||||
static constexpr uint16_t kRestartStrip = 0xffff;
|
||||
|
||||
static constexpr uint16_t kTriangleIndicesAsStrips[] = {
|
||||
static constexpr uint16_t kHull3AndEdgeIndicesAsStrips[] = {
|
||||
1, 2, 0, 3, 8, kRestartStrip, // First corner and main body of the hull.
|
||||
4, 5, 3, 6, 8, 7, kRestartStrip, // Opposite side and corners of the hull.
|
||||
10, 9, 11, 14, 12, 13, kRestartStrip, // First edge.
|
||||
16, 15, 17, 20, 18, 19, kRestartStrip, // Second edge.
|
||||
22, 21, 23, 26, 24, 25, kRestartStrip, // Third edge.
|
||||
27, 28, 30, 29, kRestartStrip, // First corner.
|
||||
31, 32, 34, 33, kRestartStrip, // Second corner.
|
||||
35, 36, 38, 37 // Third corner.
|
||||
22, 21, 23, 26, 24, 25 // Third edge.
|
||||
};
|
||||
|
||||
static constexpr uint16_t kTriangleIndicesAsTris[] = {
|
||||
static constexpr uint16_t kHull3AndEdgeIndicesAsTris[] = {
|
||||
// First corner and main body of the hull.
|
||||
1, 2, 0,
|
||||
2, 3, 0,
|
||||
@ -142,21 +194,9 @@ static constexpr uint16_t kTriangleIndicesAsTris[] = {
|
||||
21, 26, 23,
|
||||
23, 26, 24,
|
||||
26, 25, 24,
|
||||
|
||||
// First corner.
|
||||
27, 28, 30,
|
||||
28, 29, 30,
|
||||
|
||||
// Second corner.
|
||||
31, 32, 34,
|
||||
32, 33, 34,
|
||||
|
||||
// Third corner.
|
||||
35, 36, 38,
|
||||
36, 37, 38,
|
||||
};
|
||||
|
||||
GR_DECLARE_STATIC_UNIQUE_KEY(gTriangleIndexBufferKey);
|
||||
GR_DECLARE_STATIC_UNIQUE_KEY(gHull3AndEdgeIndexBufferKey);
|
||||
|
||||
static constexpr int32_t kHull4AndEdgeVertices[] = {
|
||||
hull_vertex_data(0, 0, 4),
|
||||
@ -212,120 +252,51 @@ static constexpr uint16_t kHull4AndEdgeIndicesAsTris[] = {
|
||||
|
||||
GR_DECLARE_STATIC_UNIQUE_KEY(gHull4AndEdgeIndexBufferKey);
|
||||
|
||||
|
||||
/**
|
||||
* This class and its subclasses implement the coverage processor with vertex shaders.
|
||||
*/
|
||||
class GrCCCoverageProcessor::VSImpl : public GrGLSLGeometryProcessor {
|
||||
public:
|
||||
VSImpl(std::unique_ptr<Shader> shader) : fShader(std::move(shader)) {}
|
||||
|
||||
private:
|
||||
void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor&,
|
||||
FPCoordTransformIter&& transformIter) override {
|
||||
this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
|
||||
}
|
||||
|
||||
void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) override;
|
||||
|
||||
const char* emitVertexPosition(const GrCCCoverageProcessor&, GrGLSLVertexBuilder*,
|
||||
GrGPArgs*) const;
|
||||
|
||||
const std::unique_ptr<Shader> fShader;
|
||||
};
|
||||
|
||||
void GrCCCoverageProcessor::VSImpl::onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) {
|
||||
const GrCCCoverageProcessor& proc = args.fGP.cast<GrCCCoverageProcessor>();
|
||||
|
||||
// Vertex shader.
|
||||
GrGLSLVertexBuilder* v = args.fVertBuilder;
|
||||
int numInputPoints = proc.numInputPoints();
|
||||
|
||||
const char* swizzle = (4 == numInputPoints) ? "xyzw" : "xyz";
|
||||
v->codeAppendf("float%ix2 pts = transpose(float2x%i(%s.%s, %s.%s));",
|
||||
numInputPoints, numInputPoints, proc.getAttrib(kAttribIdx_X).fName, swizzle,
|
||||
proc.getAttrib(kAttribIdx_Y).fName, swizzle);
|
||||
|
||||
if (WindMethod::kCrossProduct == proc.fWindMethod) {
|
||||
v->codeAppend ("float area_x2 = determinant(float2x2(pts[0] - pts[1], "
|
||||
"pts[0] - pts[2]));");
|
||||
if (4 == numInputPoints) {
|
||||
v->codeAppend ("area_x2 += determinant(float2x2(pts[0] - pts[2], "
|
||||
"pts[0] - pts[3]));");
|
||||
}
|
||||
v->codeAppend ("half wind = sign(area_x2);");
|
||||
} else {
|
||||
SkASSERT(WindMethod::kInstanceData == proc.fWindMethod);
|
||||
SkASSERT(3 == numInputPoints);
|
||||
SkASSERT(kFloat4_GrVertexAttribType == proc.getAttrib(kAttribIdx_X).fType);
|
||||
v->codeAppendf("half wind = %s.w;", proc.getAttrib(kAttribIdx_X).fName);
|
||||
}
|
||||
|
||||
float bloat = kAABloatRadius;
|
||||
#ifdef SK_DEBUG
|
||||
if (proc.debugVisualizationsEnabled()) {
|
||||
bloat *= proc.debugBloat();
|
||||
}
|
||||
#endif
|
||||
v->defineConstant("bloat", bloat);
|
||||
|
||||
const char* coverage = this->emitVertexPosition(proc, v, gpArgs);
|
||||
SkASSERT(kFloat2_GrSLType == gpArgs->fPositionVar.getType());
|
||||
|
||||
GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
|
||||
SkString varyingCode;
|
||||
fShader->emitVaryings(varyingHandler, GrGLSLVarying::Scope::kVertToFrag, &varyingCode,
|
||||
gpArgs->fPositionVar.c_str(), coverage, "wind");
|
||||
v->codeAppend(varyingCode.c_str());
|
||||
|
||||
varyingHandler->emitAttributes(proc);
|
||||
SkASSERT(!args.fFPCoordTransformHandler->nextCoordTransform());
|
||||
|
||||
// Fragment shader.
|
||||
fShader->emitFragmentCode(proc, args.fFragBuilder, args.fOutputColor, args.fOutputCoverage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a conservative raster hull around a triangle or curve. For triangles we generate
|
||||
* additional conservative rasters with coverage ramps around the edges. For curves we generate an
|
||||
* additional raster with coverage ramps around its shared edge.
|
||||
* additional conservative rasters with coverage ramps around the edges. For curves we
|
||||
* generate an additional raster with coverage ramps around its shared edge.
|
||||
*
|
||||
* Triangles are drawn in three steps: (1) Draw a conservative raster of the entire triangle, with a
|
||||
* coverage of +1. (2) Draw conservative rasters around each edge, with a coverage ramp from -1 to
|
||||
* 0. These edge coverage values convert jagged conservative raster edges into smooth, antialiased
|
||||
* ones. (3) Draw conservative rasters (aka pixel-size boxes) around each corner, replacing the
|
||||
* previous coverage values with ones that ramp to zero in the bloat vertices that fall outside the
|
||||
* triangle.
|
||||
* Triangle rough outlines are drawn in two steps: (1) Draw a conservative raster of the entire
|
||||
* triangle, with a coverage of +1. (2) Draw conservative rasters around each edge, with a
|
||||
* coverage ramp from -1 to 0. These edge coverage values convert jagged conservative raster edges
|
||||
* into smooth, antialiased ones. The final corners get touched up in a later step by VSCornerImpl.
|
||||
*
|
||||
* Curves are drawn in two steps: (1) Draw a conservative raster around the input points, passing
|
||||
* coverage=+1 to the Shader. (2) Draw an additional conservative raster around the curve's shared
|
||||
* edge, using coverage=-1 at bloat vertices that fall outside the input points. This erases what
|
||||
* the hull just wrote and ramps coverage to zero.
|
||||
*/
|
||||
const char* GrCCCoverageProcessor::VSImpl::emitVertexPosition(const GrCCCoverageProcessor& proc,
|
||||
GrGLSLVertexBuilder* v,
|
||||
GrGPArgs* gpArgs) const {
|
||||
int numSides = (RenderPass::kTriangles == proc.fRenderPass) ? 3 : 4;
|
||||
const char* hullPts = fShader->emitSetupCode(v, "pts");
|
||||
class VSHullAndEdgeImpl : public GrCCCoverageProcessor::VSImpl {
|
||||
public:
|
||||
VSHullAndEdgeImpl(std::unique_ptr<Shader> shader, int numSides)
|
||||
: VSImpl(std::move(shader)), fNumSides(numSides) {}
|
||||
|
||||
const char* emitVertexPosition(const GrCCCoverageProcessor& proc, GrGLSLVertexBuilder* v,
|
||||
GrGPArgs* gpArgs) const override {
|
||||
Shader::GeometryVars vars;
|
||||
fShader->emitSetupCode(v, "pts", nullptr, "wind", &vars);
|
||||
|
||||
const char* hullPts = vars.fHullVars.fAlternatePoints;
|
||||
if (!hullPts) {
|
||||
SkASSERT(numSides == proc.numInputPoints());
|
||||
hullPts = "pts";
|
||||
}
|
||||
|
||||
// Reverse all indices if the wind is counter-clockwise: [0, 1, 2] -> [2, 1, 0].
|
||||
v->codeAppendf("int clockwise_indices = wind > 0 ? %s : 0x%x - %s;",
|
||||
proc.getAttrib(kAttribIdx_VertexData).fName,
|
||||
((numSides - 1) << kVertexData_LeftNeighborIdShift) |
|
||||
((numSides - 1) << kVertexData_RightNeighborIdShift) |
|
||||
((fNumSides - 1) << kVertexData_LeftNeighborIdShift) |
|
||||
((fNumSides - 1) << kVertexData_RightNeighborIdShift) |
|
||||
(((1 << kVertexData_RightNeighborIdShift) - 1) ^ 3) |
|
||||
(numSides - 1),
|
||||
(fNumSides - 1),
|
||||
proc.getAttrib(kAttribIdx_VertexData).fName);
|
||||
|
||||
// Here we generate conservative raster geometry for the input polygon. It is the convex hull of
|
||||
// N pixel-size boxes, one centered on each the input points. Each corner has three vertices,
|
||||
// where one or two may cause degenerate triangles. The vertex data tells us how to offset each
|
||||
// vertex. Edges are also handled here using the same concept. For more details on conservative
|
||||
// raster, see: https://developer.nvidia.com/gpugems/GPUGems2/gpugems2_chapter42.html
|
||||
// Here we generate conservative raster geometry for the input polygon. It is the convex
|
||||
// hull of N pixel-size boxes, one centered on each the input points. Each corner has three
|
||||
// vertices, where one or two may cause degenerate triangles. The vertex data tells us how
|
||||
// to offset each vertex. Edges are also handled here using the same concept. For more
|
||||
// details on conservative raster, see:
|
||||
// https://developer.nvidia.com/gpugems/GPUGems2/gpugems2_chapter42.html
|
||||
v->codeAppendf("float2 corner = %s[clockwise_indices & 3];", hullPts);
|
||||
v->codeAppendf("float2 left = %s[clockwise_indices >> %i];",
|
||||
hullPts, kVertexData_LeftNeighborIdShift);
|
||||
@ -342,49 +313,22 @@ const char* GrCCCoverageProcessor::VSImpl::emitVertexPosition(const GrCCCoverage
|
||||
|
||||
v->codeAppend ("bool2 left_right_notequal = notEqual(leftbloat, rightbloat);");
|
||||
|
||||
// At each corner of the polygon, our hull will have either 1, 2, or 3 vertices. We begin
|
||||
// with the first hull vertex (leftbloat), then continue rotating 90 degrees clockwise until
|
||||
// we reach the desired vertex for this invocation. Corners with less than 3 corresponding
|
||||
// hull vertices will result in redundant vertices and degenerate triangles.
|
||||
v->codeAppend ("float2 bloatdir = leftbloat;");
|
||||
|
||||
if (RenderPass::kTriangles == proc.fRenderPass) { // Only triangles emit corner boxes.
|
||||
v->codeAppendf("if (0 != (%s & %i)) {", // Are we a corner?
|
||||
proc.getAttrib(kAttribIdx_VertexData).fName, kVertexData_IsCornerBit);
|
||||
|
||||
// For corner boxes, we hack 'left_right_notequal' to [true, true]. This
|
||||
// causes the upcoming code to always rotate, which is the right thing
|
||||
// for corners.
|
||||
v->codeAppendf( "left_right_notequal = bool2(true, true);");
|
||||
|
||||
// In corner boxes, all 4 coverage values will not map linearly, so it is
|
||||
// important to rotate the box so its diagonal shared edge points out of
|
||||
// the triangle, in the direction that ramps to zero.
|
||||
v->codeAppend ( "float2 bisect = normalize(corner - right) + normalize(corner - left);");
|
||||
v->codeAppend ( "if (sign(bisect) == sign(leftbloat)) {");
|
||||
v->codeAppend ( "bloatdir = float2(+bloatdir.y, -bloatdir.x);");
|
||||
v->codeAppend ( "}");
|
||||
v->codeAppend ("}");
|
||||
}
|
||||
|
||||
// At each corner of the polygon, our hull will have either 1, 2, or 3 vertices (or 4 if it's a
|
||||
// corner box). We begin with the first hull vertex (leftbloat), then continue rotating 90
|
||||
// degrees clockwise until we reach the desired vertex for this invocation. Corners with less
|
||||
// than 3 corresponding hull vertices will result in redundant vertices and degenerate
|
||||
// triangles.
|
||||
v->codeAppendf("int bloatidx = (%s >> %i) & 3;",
|
||||
proc.getAttrib(kAttribIdx_VertexData).fName, kVertexData_BloatIdxShift);
|
||||
v->codeAppend ("switch (bloatidx) {");
|
||||
if (RenderPass::kTriangles == proc.fRenderPass) { // Only triangles emit corner boxes.
|
||||
v->codeAppend ( "case 3:");
|
||||
// Only corners will have bloatidx=3, and corners always rotate.
|
||||
v->codeAppend ( "bloatdir = float2(-bloatdir.y, +bloatdir.x);"); // 90 deg CW.
|
||||
// fallthru.
|
||||
}
|
||||
v->codeAppend ( "case 2:");
|
||||
v->codeAppendf( "if (all(left_right_notequal)) {");
|
||||
v->codeAppend ( "bloatdir = float2(-bloatdir.y, +bloatdir.x);"); // 90 deg CW.
|
||||
v->codeAppend ( "bloatdir = float2(-bloatdir.y, +bloatdir.x);");
|
||||
v->codeAppend ( "}");
|
||||
// fallthru.
|
||||
v->codeAppend ( "case 1:");
|
||||
v->codeAppendf( "if (any(left_right_notequal)) {");
|
||||
v->codeAppend ( "bloatdir = float2(-bloatdir.y, +bloatdir.x);"); // 90 deg CW.
|
||||
v->codeAppend ( "bloatdir = float2(-bloatdir.y, +bloatdir.x);");
|
||||
v->codeAppend ( "}");
|
||||
// fallthru.
|
||||
v->codeAppend ("}");
|
||||
@ -395,32 +339,13 @@ const char* GrCCCoverageProcessor::VSImpl::emitVertexPosition(const GrCCCoverage
|
||||
// The hull has a coverage of +1 all around.
|
||||
v->codeAppend ("half coverage = +1;");
|
||||
|
||||
if (RenderPass::kTriangles == proc.fRenderPass) {
|
||||
v->codeAppendf("if (0 != (%s & %i)) {", // Are we an edge OR corner?
|
||||
proc.getAttrib(kAttribIdx_VertexData).fName,
|
||||
kVertexData_IsEdgeBit | kVertexData_IsCornerBit);
|
||||
if (3 == fNumSides) {
|
||||
v->codeAppendf("if (0 != (%s & %i)) {", // Are we an edge?
|
||||
proc.getAttrib(kAttribIdx_VertexData).fName, kVertexData_IsEdgeBit);
|
||||
Shader::CalcEdgeCoverageAtBloatVertex(v, "left", "corner", "bloatdir", "coverage");
|
||||
v->codeAppend ("}");
|
||||
|
||||
v->codeAppendf("if (0 != (%s & %i)) {", // Are we a corner?
|
||||
proc.getAttrib(kAttribIdx_VertexData).fName, kVertexData_IsCornerBit);
|
||||
// Corner boxes erase whatever coverage was written previously, and
|
||||
// replace it with linearly-interpolated values that ramp to zero in the
|
||||
// diagonal that points out of the triangle, and ramp from left-edge
|
||||
// coverage to right-edge coverage in the other diagonal.
|
||||
v->codeAppend ( "half left_coverage = coverage;");
|
||||
v->codeAppend ( "half right_coverage;");
|
||||
Shader::CalcEdgeCoverageAtBloatVertex(v, "corner", "right", "bloatdir",
|
||||
"right_coverage");
|
||||
v->codeAppend ( "coverage = (1 == bloatidx) ? -1 : 0;");
|
||||
v->codeAppend ( "if (((bloatidx + 3) & 3) < 2) {");
|
||||
v->codeAppend ( "coverage -= left_coverage;");
|
||||
v->codeAppend ( "}");
|
||||
v->codeAppend ( "if (bloatidx < 2) {");
|
||||
v->codeAppend ( "coverage -= right_coverage;");
|
||||
v->codeAppend ( "}");
|
||||
v->codeAppend ("}");
|
||||
} else {
|
||||
SkASSERT(4 == fNumSides);
|
||||
v->codeAppendf("if (0 != (%s & %i)) {", // Are we an edge?
|
||||
proc.getAttrib(kAttribIdx_VertexData).fName, kVertexData_IsEdgeBit);
|
||||
v->codeAppend ( "coverage = -1;");
|
||||
@ -434,7 +359,55 @@ const char* GrCCCoverageProcessor::VSImpl::emitVertexPosition(const GrCCCoverage
|
||||
v->codeAppend ("}");
|
||||
|
||||
return "coverage";
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
const int fNumSides;
|
||||
};
|
||||
|
||||
static constexpr uint16_t kCornerIndicesAsStrips[] = {
|
||||
0, 1, 2, 3, kRestartStrip, // First corner.
|
||||
4, 5, 6, 7, kRestartStrip, // Second corner.
|
||||
8, 9, 10, 11 // Third corner.
|
||||
};
|
||||
|
||||
static constexpr uint16_t kCornerIndicesAsTris[] = {
|
||||
// First corner.
|
||||
0, 1, 2,
|
||||
1, 3, 2,
|
||||
|
||||
// Second corner.
|
||||
4, 5, 6,
|
||||
5, 7, 6,
|
||||
|
||||
// Third corner.
|
||||
8, 9, 10,
|
||||
9, 11, 10,
|
||||
};
|
||||
|
||||
GR_DECLARE_STATIC_UNIQUE_KEY(gCornerIndexBufferKey);
|
||||
|
||||
/**
|
||||
* Generates conservative rasters around corners. (See comments for RenderPass)
|
||||
*/
|
||||
class VSCornerImpl : public GrCCCoverageProcessor::VSImpl {
|
||||
public:
|
||||
VSCornerImpl(std::unique_ptr<Shader> shader) : VSImpl(std::move(shader)) {}
|
||||
|
||||
const char* emitVertexPosition(const GrCCCoverageProcessor&, GrGLSLVertexBuilder* v,
|
||||
GrGPArgs* gpArgs) const override {
|
||||
Shader::GeometryVars vars;
|
||||
v->codeAppend ("int corner_id = sk_VertexID / 4;");
|
||||
fShader->emitSetupCode(v, "pts", "corner_id", "wind", &vars);
|
||||
|
||||
v->codeAppendf("float2 vertex = %s;", vars.fCornerVars.fPoint);
|
||||
v->codeAppend ("vertex.x += (0 == (sk_VertexID & 2)) ? -bloat : +bloat;");
|
||||
v->codeAppend ("vertex.y += (0 == (sk_VertexID & 1)) ? -bloat : +bloat;");
|
||||
|
||||
gpArgs->fPositionVar.set(kFloat2_GrSLType, "vertex");
|
||||
return nullptr; // Corner vertices don't have an initial coverage value.
|
||||
}
|
||||
};
|
||||
|
||||
void GrCCCoverageProcessor::initVS(GrResourceProvider* rp) {
|
||||
SkASSERT(Impl::kVertexShader == fImpl);
|
||||
@ -442,24 +415,42 @@ void GrCCCoverageProcessor::initVS(GrResourceProvider* rp) {
|
||||
|
||||
switch (fRenderPass) {
|
||||
case RenderPass::kTriangles: {
|
||||
GR_DEFINE_STATIC_UNIQUE_KEY(gTriangleVertexBufferKey);
|
||||
GR_DEFINE_STATIC_UNIQUE_KEY(gHull3AndEdgeVertexBufferKey);
|
||||
fVertexBuffer = rp->findOrMakeStaticBuffer(kVertex_GrBufferType,
|
||||
sizeof(kTriangleVertices),
|
||||
kTriangleVertices,
|
||||
gTriangleVertexBufferKey);
|
||||
GR_DEFINE_STATIC_UNIQUE_KEY(gTriangleIndexBufferKey);
|
||||
sizeof(kHull3AndEdgeVertices),
|
||||
kHull3AndEdgeVertices,
|
||||
gHull3AndEdgeVertexBufferKey);
|
||||
GR_DEFINE_STATIC_UNIQUE_KEY(gHull3AndEdgeIndexBufferKey);
|
||||
if (caps.usePrimitiveRestart()) {
|
||||
fIndexBuffer = rp->findOrMakeStaticBuffer(kIndex_GrBufferType,
|
||||
sizeof(kTriangleIndicesAsStrips),
|
||||
kTriangleIndicesAsStrips,
|
||||
gTriangleIndexBufferKey);
|
||||
fNumIndicesPerInstance = SK_ARRAY_COUNT(kTriangleIndicesAsStrips);
|
||||
sizeof(kHull3AndEdgeIndicesAsStrips),
|
||||
kHull3AndEdgeIndicesAsStrips,
|
||||
gHull3AndEdgeIndexBufferKey);
|
||||
fNumIndicesPerInstance = SK_ARRAY_COUNT(kHull3AndEdgeIndicesAsStrips);
|
||||
} else {
|
||||
fIndexBuffer = rp->findOrMakeStaticBuffer(kIndex_GrBufferType,
|
||||
sizeof(kTriangleIndicesAsTris),
|
||||
kTriangleIndicesAsTris,
|
||||
gTriangleIndexBufferKey);
|
||||
fNumIndicesPerInstance = SK_ARRAY_COUNT(kTriangleIndicesAsTris);
|
||||
sizeof(kHull3AndEdgeIndicesAsTris),
|
||||
kHull3AndEdgeIndicesAsTris,
|
||||
gHull3AndEdgeIndexBufferKey);
|
||||
fNumIndicesPerInstance = SK_ARRAY_COUNT(kHull3AndEdgeIndicesAsTris);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case RenderPass::kTriangleCorners: {
|
||||
GR_DEFINE_STATIC_UNIQUE_KEY(gCornerIndexBufferKey);
|
||||
if (caps.usePrimitiveRestart()) {
|
||||
fIndexBuffer = rp->findOrMakeStaticBuffer(kIndex_GrBufferType,
|
||||
sizeof(kCornerIndicesAsStrips),
|
||||
kCornerIndicesAsStrips,
|
||||
gCornerIndexBufferKey);
|
||||
fNumIndicesPerInstance = SK_ARRAY_COUNT(kCornerIndicesAsStrips);
|
||||
} else {
|
||||
fIndexBuffer = rp->findOrMakeStaticBuffer(kIndex_GrBufferType,
|
||||
sizeof(kCornerIndicesAsTris),
|
||||
kCornerIndicesAsTris,
|
||||
gCornerIndexBufferKey);
|
||||
fNumIndicesPerInstance = SK_ARRAY_COUNT(kCornerIndicesAsTris);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -540,5 +531,15 @@ void GrCCCoverageProcessor::appendVSMesh(GrBuffer* instanceBuffer, int instanceC
|
||||
}
|
||||
|
||||
GrGLSLPrimitiveProcessor* GrCCCoverageProcessor::createVSImpl(std::unique_ptr<Shader> shadr) const {
|
||||
return new VSImpl(std::move(shadr));
|
||||
switch (fRenderPass) {
|
||||
case RenderPass::kTriangles:
|
||||
return new VSHullAndEdgeImpl(std::move(shadr), 3);
|
||||
case RenderPass::kTriangleCorners:
|
||||
return new VSCornerImpl(std::move(shadr));
|
||||
case RenderPass::kQuadratics:
|
||||
case RenderPass::kCubics:
|
||||
return new VSHullAndEdgeImpl(std::move(shadr), 4);
|
||||
}
|
||||
SK_ABORT("Invalid RenderPass");
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -12,7 +12,9 @@
|
||||
|
||||
using Shader = GrCCCoverageProcessor::Shader;
|
||||
|
||||
const char* GrCCCubicShader::emitSetupCode(GrGLSLVertexGeoBuilder* s, const char* pts) const {
|
||||
void GrCCCubicShader::emitSetupCode(GrGLSLVertexGeoBuilder* s, const char* pts,
|
||||
const char* /*repetitionID*/, const char* /*wind*/,
|
||||
GeometryVars*) const {
|
||||
// Find the cubic's power basis coefficients.
|
||||
s->codeAppendf("float2x4 C = float4x4(-1, 3, -3, 1, "
|
||||
" 3, -6, 3, 0, "
|
||||
@ -62,28 +64,24 @@ const char* GrCCCubicShader::emitSetupCode(GrGLSLVertexGeoBuilder* s, const char
|
||||
s->codeAppendf("%s *= float3x3(orientation[0] * orientation[1], 0, 0, "
|
||||
"0, orientation[0], 0, "
|
||||
"0, 0, orientation[1]);", fKLMMatrix.c_str());
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Shader::CoverageHandling GrCCCubicShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler,
|
||||
void GrCCCubicShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler,
|
||||
GrGLSLVarying::Scope scope, SkString* code,
|
||||
const char* position,
|
||||
const char* coverageTimesWind) {
|
||||
const char* position, const char* inputCoverage,
|
||||
const char* wind) {
|
||||
code->appendf("float3 klm = float3(%s, 1) * %s;", position, fKLMMatrix.c_str());
|
||||
|
||||
fKLMW.reset(kFloat4_GrSLType, scope);
|
||||
varyingHandler->addVarying("klmw", &fKLMW);
|
||||
code->appendf("%s.xyz = klm;", OutName(fKLMW));
|
||||
code->appendf("%s.w = %s;", OutName(fKLMW), coverageTimesWind);
|
||||
code->appendf("%s.w = %s * %s;", OutName(fKLMW), inputCoverage, wind);
|
||||
|
||||
fGradMatrix.reset(kFloat2x2_GrSLType, scope);
|
||||
varyingHandler->addVarying("grad_matrix", &fGradMatrix);
|
||||
code->appendf("%s[0] = 3 * klm[0] * %s[0].xy;", OutName(fGradMatrix), fKLMMatrix.c_str());
|
||||
code->appendf("%s[1] = -klm[1] * %s[2].xy - klm[2] * %s[1].xy;",
|
||||
OutName(fGradMatrix), fKLMMatrix.c_str(), fKLMMatrix.c_str());
|
||||
|
||||
return CoverageHandling::kHandled;
|
||||
}
|
||||
|
||||
void GrCCCubicShader::onEmitFragmentCode(const GrCCCoverageProcessor& proc,
|
||||
|
@ -23,10 +23,11 @@
|
||||
*/
|
||||
class GrCCCubicShader : public GrCCCoverageProcessor::Shader {
|
||||
protected:
|
||||
const char* emitSetupCode(GrGLSLVertexGeoBuilder*, const char* pts) const override;
|
||||
void emitSetupCode(GrGLSLVertexGeoBuilder*, const char* pts, const char* repetitionID,
|
||||
const char* wind, GeometryVars*) const override;
|
||||
|
||||
CoverageHandling onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code,
|
||||
const char* position, const char* coverageTimesWind) override;
|
||||
void onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code,
|
||||
const char* position, const char* inputCoverage, const char* wind) override;
|
||||
|
||||
void onEmitFragmentCode(const GrCCCoverageProcessor&, GrGLSLFPFragmentBuilder*,
|
||||
const char* outputCoverage) const override;
|
||||
|
@ -514,12 +514,17 @@ void GrCCPathParser::drawCoverageCount(GrOpFlushState* flushState, CoverageCount
|
||||
if (batchTotalCounts.fTriangles) {
|
||||
this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kTriangles,
|
||||
WindMethod::kCrossProduct, &PrimitiveTallies::fTriangles, drawBounds);
|
||||
this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kTriangleCorners,
|
||||
WindMethod::kCrossProduct, &PrimitiveTallies::fTriangles, drawBounds);
|
||||
}
|
||||
|
||||
if (batchTotalCounts.fWoundTriangles) {
|
||||
this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kTriangles,
|
||||
WindMethod::kInstanceData, &PrimitiveTallies::fWoundTriangles,
|
||||
drawBounds);
|
||||
this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kTriangleCorners,
|
||||
WindMethod::kInstanceData, &PrimitiveTallies::fWoundTriangles,
|
||||
drawBounds);
|
||||
}
|
||||
|
||||
if (batchTotalCounts.fQuadratics) {
|
||||
@ -587,8 +592,9 @@ void GrCCPathParser::drawRenderPass(GrOpFlushState* flushState, const GrPipeline
|
||||
SkASSERT(totalInstanceCount == batch.fTotalPrimitiveCounts.*instanceType);
|
||||
|
||||
if (!fMeshesScratchBuffer.empty()) {
|
||||
proc.draw(flushState, pipeline, fMeshesScratchBuffer.begin(),
|
||||
fDynamicStatesScratchBuffer.begin(), fMeshesScratchBuffer.count(),
|
||||
SkRect::Make(drawBounds));
|
||||
SkASSERT(flushState->rtCommandBuffer());
|
||||
flushState->rtCommandBuffer()->draw(pipeline, proc, fMeshesScratchBuffer.begin(),
|
||||
fDynamicStatesScratchBuffer.begin(),
|
||||
fMeshesScratchBuffer.count(), SkRect::Make(drawBounds));
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,9 @@
|
||||
|
||||
using Shader = GrCCCoverageProcessor::Shader;
|
||||
|
||||
const char* GrCCQuadraticShader::emitSetupCode(GrGLSLVertexGeoBuilder* s, const char* pts) const {
|
||||
void GrCCQuadraticShader::emitSetupCode(GrGLSLVertexGeoBuilder* s, const char* pts,
|
||||
const char* /*repetitionID*/, const char* /*wind*/,
|
||||
GeometryVars* vars) const {
|
||||
s->declareGlobal(fCanonicalMatrix);
|
||||
s->codeAppendf("%s = float3x3(0.0, 0, 1, "
|
||||
"0.5, 0, 1, "
|
||||
@ -36,20 +38,23 @@ const char* GrCCQuadraticShader::emitSetupCode(GrGLSLVertexGeoBuilder* s, const
|
||||
"%s[0] + tan0 * t, "
|
||||
"%s[1] + tan1 * t, "
|
||||
"%s[2]);", pts, pts, pts, pts);
|
||||
return "quadratic_hull";
|
||||
vars->fHullVars.fAlternatePoints = "quadratic_hull";
|
||||
}
|
||||
|
||||
Shader::CoverageHandling GrCCQuadraticShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler,
|
||||
GrGLSLVarying::Scope scope,
|
||||
SkString* code, const char* position,
|
||||
const char* coverageTimesWind) {
|
||||
void GrCCQuadraticShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler,
|
||||
GrGLSLVarying::Scope scope, SkString* code,
|
||||
const char* position, const char* inputCoverage,
|
||||
const char* wind) {
|
||||
fCoords.reset(kFloat4_GrSLType, scope);
|
||||
varyingHandler->addVarying("coords", &fCoords);
|
||||
code->appendf("%s.xy = (%s * float3(%s, 1)).xy;",
|
||||
OutName(fCoords), fCanonicalMatrix.c_str(), position);
|
||||
code->appendf("%s.zw = float2(2 * %s.x, -1) * float2x2(%s);",
|
||||
OutName(fCoords), OutName(fCoords), fCanonicalMatrix.c_str());
|
||||
return CoverageHandling::kNotHandled;
|
||||
|
||||
fCoverageTimesWind.reset(kHalf_GrSLType, scope);
|
||||
varyingHandler->addVarying("coverage_times_wind", &fCoverageTimesWind);
|
||||
code->appendf("%s = %s * %s;", OutName(fCoverageTimesWind), inputCoverage, wind);
|
||||
}
|
||||
|
||||
void GrCCQuadraticShader::onEmitFragmentCode(const GrCCCoverageProcessor& proc,
|
||||
@ -62,5 +67,5 @@ void GrCCQuadraticShader::onEmitFragmentCode(const GrCCCoverageProcessor& proc,
|
||||
f->codeAppendf("d /= %f;", proc.debugBloat());
|
||||
}
|
||||
#endif
|
||||
f->codeAppendf("%s = clamp(0.5 - d, 0, 1);", outputCoverage);
|
||||
f->codeAppendf("%s = clamp(0.5 - d, 0, 1) * %s;", outputCoverage, fCoverageTimesWind.fsIn());
|
||||
}
|
||||
|
@ -22,16 +22,18 @@
|
||||
*/
|
||||
class GrCCQuadraticShader : public GrCCCoverageProcessor::Shader {
|
||||
protected:
|
||||
const char* emitSetupCode(GrGLSLVertexGeoBuilder*, const char* pts) const override;
|
||||
void emitSetupCode(GrGLSLVertexGeoBuilder*, const char* pts, const char* repetitionID,
|
||||
const char* wind, GeometryVars*) const override;
|
||||
|
||||
CoverageHandling onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code,
|
||||
const char* position, const char* coverageTimesWind) override;
|
||||
void onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code,
|
||||
const char* position, const char* inputCoverage, const char* wind) override;
|
||||
|
||||
void onEmitFragmentCode(const GrCCCoverageProcessor&, GrGLSLFPFragmentBuilder*,
|
||||
const char* outputCoverage) const override;
|
||||
|
||||
const GrShaderVar fCanonicalMatrix{"canonical_matrix", kFloat3x3_GrSLType};
|
||||
GrGLSLVarying fCoords;
|
||||
GrGLSLVarying fCoverageTimesWind;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
151
src/gpu/ccpr/GrCCTriangleShader.cpp
Normal file
151
src/gpu/ccpr/GrCCTriangleShader.cpp
Normal file
@ -0,0 +1,151 @@
|
||||
/*
|
||||
* 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 "GrCCTriangleShader.h"
|
||||
|
||||
#include "glsl/GrGLSLFragmentShaderBuilder.h"
|
||||
#include "glsl/GrGLSLVertexGeoBuilder.h"
|
||||
|
||||
using Shader = GrCCCoverageProcessor::Shader;
|
||||
|
||||
void GrCCTriangleShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler,
|
||||
GrGLSLVarying::Scope scope, SkString* code,
|
||||
const char* /*position*/, const char* inputCoverage,
|
||||
const char* wind) {
|
||||
SkASSERT(inputCoverage);
|
||||
fCoverageTimesWind.reset(kHalf_GrSLType, scope);
|
||||
varyingHandler->addVarying("coverage_times_wind", &fCoverageTimesWind);
|
||||
code->appendf("%s = %s * %s;", OutName(fCoverageTimesWind), inputCoverage, wind);
|
||||
}
|
||||
|
||||
void GrCCTriangleShader::onEmitFragmentCode(const GrCCCoverageProcessor&,
|
||||
GrGLSLFPFragmentBuilder* f,
|
||||
const char* outputCoverage) const {
|
||||
f->codeAppendf("%s = %s;", outputCoverage, fCoverageTimesWind.fsIn());
|
||||
}
|
||||
|
||||
void GrCCTriangleCornerShader::emitSetupCode(GrGLSLVertexGeoBuilder* s, const char* pts,
|
||||
const char* repetitionID, const char* wind,
|
||||
GeometryVars* vars) const {
|
||||
s->codeAppendf("float2 corner = %s[%s];", pts, repetitionID);
|
||||
vars->fCornerVars.fPoint = "corner";
|
||||
|
||||
s->codeAppendf("float2x2 vectors = float2x2(corner - %s[0 != %s ? %s - 1 : 2], "
|
||||
"corner - %s[2 != %s ? %s + 1 : 0]);",
|
||||
pts, repetitionID, repetitionID, pts, repetitionID,
|
||||
repetitionID);
|
||||
|
||||
// Make sure neither vector is 0 to avoid a divide-by-zero. Wind will be zero anyway if this
|
||||
// is the case, so whatever we output won't have any effect as long it isn't NaN or Inf.
|
||||
s->codeAppend ("for (int i = 0; i < 2; ++i) {");
|
||||
s->codeAppend ( "vectors[i] = (vectors[i] != float2(0)) ? vectors[i] : float2(1);");
|
||||
s->codeAppend ("}");
|
||||
|
||||
// Find the vector that bisects the region outside the incoming edges. Each edge is
|
||||
// responsible to subtract the outside region on its own the side of the bisector.
|
||||
s->codeAppendf("float2 leftdir = normalize(vectors[%s > 0 ? 0 : 1]);", wind);
|
||||
s->codeAppendf("float2 rightdir = normalize(vectors[%s > 0 ? 1 : 0]);", wind);
|
||||
s->codeAppend ("float2 bisect = dot(leftdir, rightdir) >= 0 ? "
|
||||
"leftdir + rightdir : "
|
||||
"float2(leftdir.y - rightdir.y, rightdir.x - leftdir.x);");
|
||||
|
||||
// In ccpr we don't calculate exact geometric pixel coverage. What the distance-to-edge
|
||||
// method actually finds is coverage inside a logical "AA box", one that is rotated inline
|
||||
// with the edge, and in our case, up-scaled to circumscribe the actual pixel. Below we set
|
||||
// up transformations into normalized logical AA box space for both incoming edges. These
|
||||
// will tell the fragment shader where the corner is located within each edge's AA box.
|
||||
s->declareGlobal(fAABoxMatrices);
|
||||
s->declareGlobal(fAABoxTranslates);
|
||||
s->declareGlobal(fGeoShaderBisects);
|
||||
s->codeAppendf("for (int i = 0; i < 2; ++i) {");
|
||||
// The X component runs parallel to the edge (i.e. distance to the corner).
|
||||
s->codeAppendf( "float2 n = -vectors[%s > 0 ? i : 1 - i];", wind);
|
||||
s->codeAppend ( "float nwidth = (abs(n.x) + abs(n.y)) * (bloat * 2);");
|
||||
s->codeAppend ( "n /= nwidth;"); // nwidth != 0 because both vectors != 0.
|
||||
s->codeAppendf( "%s[i][0] = n;", fAABoxMatrices.c_str());
|
||||
s->codeAppendf( "%s[i][0] = -dot(n, corner) + .5;", fAABoxTranslates.c_str());
|
||||
|
||||
// The Y component runs perpendicular to the edge (i.e. distance-to-edge).
|
||||
s->codeAppend ( "n = (i == 0) ? float2(-n.y, n.x) : float2(n.y, -n.x);");
|
||||
s->codeAppendf( "%s[i][1] = n;", fAABoxMatrices.c_str());
|
||||
s->codeAppendf( "%s[i][1] = -dot(n, corner) + .5;", fAABoxTranslates.c_str());
|
||||
|
||||
// Translate the bisector into logical AA box space.
|
||||
// NOTE: Since the region outside two edges of a convex shape is in [180 deg, 360 deg], the
|
||||
// bisector will therefore be in [90 deg, 180 deg]. Or, x >= 0 and y <= 0 in AA box space.
|
||||
s->codeAppendf( "%s[i] = -bisect * %s[i];",
|
||||
fGeoShaderBisects.c_str(), fAABoxMatrices.c_str());
|
||||
s->codeAppend ("}");
|
||||
}
|
||||
|
||||
void GrCCTriangleCornerShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler,
|
||||
GrGLSLVarying::Scope scope, SkString* code,
|
||||
const char* position, const char* inputCoverage,
|
||||
const char* wind) {
|
||||
using Interpolation = GrGLSLVaryingHandler::Interpolation;
|
||||
SkASSERT(!inputCoverage);
|
||||
|
||||
fCornerLocationInAABoxes.reset(kFloat2x2_GrSLType, scope);
|
||||
varyingHandler->addVarying("corner_location_in_aa_boxes", &fCornerLocationInAABoxes);
|
||||
|
||||
fBisectInAABoxes.reset(kFloat2x2_GrSLType, scope);
|
||||
varyingHandler->addVarying("bisect_in_aa_boxes", &fBisectInAABoxes, Interpolation::kCanBeFlat);
|
||||
|
||||
code->appendf("for (int i = 0; i < 2; ++i) {");
|
||||
code->appendf( "%s[i] = %s * %s[i] + %s[i];",
|
||||
OutName(fCornerLocationInAABoxes), position, fAABoxMatrices.c_str(),
|
||||
fAABoxTranslates.c_str());
|
||||
code->appendf( "%s[i] = %s[i];", OutName(fBisectInAABoxes), fGeoShaderBisects.c_str());
|
||||
code->appendf("}");
|
||||
|
||||
fWindTimesHalf.reset(kHalf_GrSLType, scope);
|
||||
varyingHandler->addVarying("wind_times_half", &fWindTimesHalf, Interpolation::kCanBeFlat);
|
||||
code->appendf("%s = %s * .5;", OutName(fWindTimesHalf), wind);
|
||||
}
|
||||
|
||||
void GrCCTriangleCornerShader::onEmitFragmentCode(const GrCCCoverageProcessor&,
|
||||
GrGLSLFPFragmentBuilder* f,
|
||||
const char* outputCoverage) const {
|
||||
// By the time we reach this shader, the pixel is in the following state:
|
||||
//
|
||||
// 1. The hull shader has emitted a coverage of 1.
|
||||
// 2. Both edges have subtracted the area on their outside.
|
||||
//
|
||||
// This generally works, but it is a problem for corner pixels. There is a region within
|
||||
// corner pixels that is outside both edges at the same time. This means the region has been
|
||||
// double subtracted (once by each edge). The purpose of this shader is to fix these corner
|
||||
// pixels.
|
||||
//
|
||||
// More specifically, each edge redoes its coverage analysis so that it only subtracts the
|
||||
// outside area that falls on its own side of the bisector line.
|
||||
//
|
||||
// NOTE: unless the edges fall on multiples of 90 deg from one another, they will have
|
||||
// different AA boxes. (For an explanation of AA boxes, see comments in
|
||||
// onEmitGeometryShader.) This means the coverage analysis will only be approximate. It
|
||||
// seems acceptable, but if we want exact coverage we will need to switch to a more
|
||||
// expensive model.
|
||||
f->codeAppendf("for (int i = 0; i < 2; ++i) {"); // Loop through both edges.
|
||||
f->codeAppendf( "half2 corner = %s[i];", fCornerLocationInAABoxes.fsIn());
|
||||
f->codeAppendf( "half2 bisect = %s[i];", fBisectInAABoxes.fsIn());
|
||||
|
||||
// Find the point at which the bisector exits the logical AA box.
|
||||
// (The inequality works because bisect.x is known >= 0 and bisect.y is known <= 0.)
|
||||
f->codeAppendf( "half2 d = half2(1 - corner.x, -corner.y);");
|
||||
f->codeAppendf( "half T = d.y * bisect.x >= d.x * bisect.y ? d.y / bisect.y "
|
||||
": d.x / bisect.x;");
|
||||
f->codeAppendf( "half2 exit = corner + bisect * T;");
|
||||
|
||||
// These lines combined (and the final multiply by .5) accomplish the following:
|
||||
// 1. Add back the area beyond the corner that was subtracted out previously.
|
||||
// 2. Subtract out the area beyond the corner, but under the bisector.
|
||||
// The other edge will take care of the area on its own side of the bisector.
|
||||
f->codeAppendf( "%s += (2 - corner.x - exit.x) * corner.y;", outputCoverage);
|
||||
f->codeAppendf( "%s += (corner.x - 1) * exit.y;", outputCoverage);
|
||||
f->codeAppendf("}");
|
||||
|
||||
f->codeAppendf("%s *= %s;", outputCoverage, fWindTimesHalf.fsIn());
|
||||
}
|
49
src/gpu/ccpr/GrCCTriangleShader.h
Normal file
49
src/gpu/ccpr/GrCCTriangleShader.h
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2017 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#ifndef GrCCTriangleShader_DEFINED
|
||||
#define GrCCTriangleShader_DEFINED
|
||||
|
||||
#include "ccpr/GrCCCoverageProcessor.h"
|
||||
|
||||
/**
|
||||
* Steps 1 & 2: Draw the triangle's conservative raster hull with a coverage of +1, then smooth the
|
||||
* edges by drawing the conservative rasters of all 3 edges and interpolating from
|
||||
* coverage=-1 on the outside to coverage=0 on the inside. The Impl may choose to
|
||||
* implement these steps in either one or two actual render passes.
|
||||
*/
|
||||
class GrCCTriangleShader : public GrCCCoverageProcessor::Shader {
|
||||
void onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code,
|
||||
const char* position, const char* inputCoverage, const char* wind) override;
|
||||
void onEmitFragmentCode(const GrCCCoverageProcessor&, GrGLSLFPFragmentBuilder*,
|
||||
const char* outputCoverage) const override;
|
||||
|
||||
GrGLSLVarying fCoverageTimesWind;
|
||||
};
|
||||
|
||||
/**
|
||||
* Step 3: Touch up the corner pixels. Here we fix the simple distance-to-edge coverage analysis
|
||||
* done previously so that it takes into account the region that is outside both edges at
|
||||
* the same time.
|
||||
*/
|
||||
class GrCCTriangleCornerShader : public GrCCCoverageProcessor::Shader {
|
||||
void emitSetupCode(GrGLSLVertexGeoBuilder*, const char* pts, const char* repetitionID,
|
||||
const char* wind, GeometryVars*) const override;
|
||||
void onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code,
|
||||
const char* position, const char* inputCoverage, const char* wind) override;
|
||||
void onEmitFragmentCode(const GrCCCoverageProcessor&, GrGLSLFPFragmentBuilder*,
|
||||
const char* outputCoverage) const override;
|
||||
|
||||
GrShaderVar fAABoxMatrices{"aa_box_matrices", kFloat2x2_GrSLType, 2};
|
||||
GrShaderVar fAABoxTranslates{"aa_box_translates", kFloat2_GrSLType, 2};
|
||||
GrShaderVar fGeoShaderBisects{"bisects", kFloat2_GrSLType, 2};
|
||||
GrGLSLVarying fCornerLocationInAABoxes;
|
||||
GrGLSLVarying fBisectInAABoxes;
|
||||
GrGLSLVarying fWindTimesHalf;
|
||||
};
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user