ccpr: Add an attenuation parameter to triangle corners
Adds an attenuation parameter to corners that corrects the over-coverage from linear interpolation. Adds a GM for shared corners that ensures we're doing this right. Bug: skia: Change-Id: Iff8bd40554f9fda2e7e03faa3c9fbefe65f27568 Reviewed-on: https://skia-review.googlesource.com/114272 Reviewed-by: Greg Daniel <egdaniel@google.com> Commit-Queue: Chris Dalton <csmartdalton@google.com>
This commit is contained in:
parent
c2d0dd658b
commit
04a1de5545
158
gm/sharedcorners.cpp
Normal file
158
gm/sharedcorners.cpp
Normal file
@ -0,0 +1,158 @@
|
||||
/*
|
||||
* Copyright 2018 Google Inc.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license that can be
|
||||
* found in the LICENSE file.
|
||||
*/
|
||||
|
||||
#include "gm.h"
|
||||
#include "sk_tool_utils.h"
|
||||
#include "SkPaint.h"
|
||||
#include "SkPath.h"
|
||||
#include "SkPoint.h"
|
||||
#include <array>
|
||||
#include <vector>
|
||||
|
||||
namespace skiagm {
|
||||
|
||||
static constexpr int kPadSize = 20;
|
||||
static constexpr int kBoxSize = 100;
|
||||
static constexpr SkPoint kJitters[] = {{0, 0}, {.5f, .5f}, {2/3.f, 1/3.f}};
|
||||
|
||||
// Tests various corners of different angles falling on the same pixel, particularly to ensure
|
||||
// analytic AA is working properly.
|
||||
class SharedCornersGM : public GM {
|
||||
public:
|
||||
SharedCornersGM() {
|
||||
this->setBGColor(sk_tool_utils::color_to_565(0xFF1A65D7));
|
||||
}
|
||||
|
||||
protected:
|
||||
SkString onShortName() override {
|
||||
return SkString("sharedcorners");
|
||||
}
|
||||
|
||||
SkISize onISize() override {
|
||||
constexpr int numRows = 3 * 2;
|
||||
constexpr int numCols = (1 + SK_ARRAY_COUNT(kJitters)) * 2;
|
||||
return SkISize::Make(numCols * (kBoxSize + kPadSize) + kPadSize,
|
||||
numRows * (kBoxSize + kPadSize) + kPadSize);
|
||||
}
|
||||
|
||||
void onOnceBeforeDraw() override {
|
||||
fFillPaint.setColor(SK_ColorWHITE);
|
||||
fFillPaint.setAntiAlias(true);
|
||||
|
||||
fWireFramePaint = fFillPaint;
|
||||
fWireFramePaint.setStyle(SkPaint::kStroke_Style);
|
||||
|
||||
}
|
||||
|
||||
void onDraw(SkCanvas* canvas) override {
|
||||
canvas->translate(kPadSize, kPadSize);
|
||||
canvas->save();
|
||||
|
||||
// Adjacent rects.
|
||||
this->drawTriangleBoxes(canvas,
|
||||
{{0, 0}, {40, 0}, {80, 0}, {120, 0},
|
||||
{0, 20}, {40, 20}, {80, 20}, {120, 20},
|
||||
{40, 40}, {80, 40},
|
||||
{40, 60}, {80, 60}},
|
||||
{{{0, 1, 4}}, {{1, 5, 4}},
|
||||
{{5, 1, 6}}, {{1, 2, 6}},
|
||||
{{2, 3, 6}}, {{3, 7, 6}},
|
||||
{{8, 5, 9}}, {{5, 6, 9}},
|
||||
{{10, 8, 11}}, {{8, 9, 11}}});
|
||||
|
||||
// Obtuse angles.
|
||||
this->drawTriangleBoxes(canvas,
|
||||
{{ 0, 0}, {10, 0}, {20, 0},
|
||||
{ 0, 2}, {20, 2},
|
||||
{10, 4},
|
||||
{ 0, 6}, {20, 6},
|
||||
{ 0, 8}, {10, 8}, {20, 8}},
|
||||
{{{3, 1, 4}}, {{4, 5, 3}}, {{6, 5, 7}}, {{7, 9, 6}},
|
||||
{{0, 1, 3}}, {{1, 2, 4}},
|
||||
{{3, 5, 6}}, {{5, 4, 7}},
|
||||
{{6, 9, 8}}, {{9, 7, 10}}});
|
||||
|
||||
canvas->restore();
|
||||
canvas->translate((kBoxSize + kPadSize) * 4, 0);
|
||||
|
||||
// Right angles.
|
||||
this->drawTriangleBoxes(canvas,
|
||||
{{0, 0}, {-1, 0}, {0, -1}, {1, 0}, {0, 1}},
|
||||
{{{0, 1, 2}}, {{0, 2, 3}}, {{0, 3, 4}}, {{0, 4, 1}}});
|
||||
|
||||
// Acute angles.
|
||||
SkRandom rand;
|
||||
std::vector<SkPoint> pts;
|
||||
std::vector<std::array<int, 3>> indices;
|
||||
SkScalar theta = 0;
|
||||
pts.push_back({0, 0});
|
||||
while (theta < 2*SK_ScalarPI) {
|
||||
pts.push_back({SkScalarCos(theta), SkScalarSin(theta)});
|
||||
if (pts.size() > 2) {
|
||||
indices.push_back({{0, (int)pts.size() - 2, (int)pts.size() - 1}});
|
||||
}
|
||||
theta += rand.nextRangeF(0, SK_ScalarPI/3);
|
||||
}
|
||||
indices.push_back({{0, (int)pts.size() - 1, 1}});
|
||||
this->drawTriangleBoxes(canvas, pts, indices);
|
||||
}
|
||||
|
||||
void drawTriangleBoxes(SkCanvas* canvas, const std::vector<SkPoint>& points,
|
||||
const std::vector<std::array<int, 3>>& triangles) {
|
||||
SkPath path;
|
||||
path.setFillType(SkPath::kEvenOdd_FillType);
|
||||
path.setIsVolatile(true);
|
||||
for (const std::array<int, 3>& triangle : triangles) {
|
||||
path.moveTo(points[triangle[0]]);
|
||||
path.lineTo(points[triangle[1]]);
|
||||
path.lineTo(points[triangle[2]]);
|
||||
path.close();
|
||||
}
|
||||
SkScalar scale = kBoxSize / SkTMax(path.getBounds().height(), path.getBounds().width());
|
||||
path.transform(SkMatrix::MakeScale(scale, scale));
|
||||
|
||||
this->drawRow(canvas, path);
|
||||
canvas->translate(0, kBoxSize + kPadSize);
|
||||
|
||||
SkMatrix rot;
|
||||
rot.setRotate(45, path.getBounds().centerX(), path.getBounds().centerY());
|
||||
path.transform(rot);
|
||||
this->drawRow(canvas, path);
|
||||
canvas->translate(0, kBoxSize + kPadSize);
|
||||
|
||||
rot.setRotate(-45 - 69.38111f, path.getBounds().centerX(), path.getBounds().centerY());
|
||||
path.transform(rot);
|
||||
this->drawRow(canvas, path);
|
||||
canvas->translate(0, kBoxSize + kPadSize);
|
||||
}
|
||||
|
||||
void drawRow(SkCanvas* canvas, const SkPath& path) {
|
||||
SkAutoCanvasRestore acr(canvas, true);
|
||||
const SkRect& bounds = path.getBounds();
|
||||
canvas->translate((kBoxSize - bounds.width()) / 2 - bounds.left(),
|
||||
(kBoxSize - bounds.height()) / 2 - bounds.top());
|
||||
|
||||
canvas->drawPath(path, fWireFramePaint);
|
||||
canvas->translate(kBoxSize + kPadSize, 0);
|
||||
|
||||
for (SkPoint jitter : kJitters) {
|
||||
{
|
||||
SkAutoCanvasRestore acr(canvas, true);
|
||||
canvas->translate(jitter.x(), jitter.y());
|
||||
canvas->drawPath(path, fFillPaint);
|
||||
}
|
||||
canvas->translate(kBoxSize + kPadSize, 0);
|
||||
}
|
||||
}
|
||||
|
||||
SkPaint fWireFramePaint;
|
||||
SkPaint fFillPaint;
|
||||
};
|
||||
|
||||
DEF_GM(return new SharedCornersGM;)
|
||||
|
||||
}
|
@ -272,6 +272,7 @@ gm_sources = [
|
||||
"$_gm/shallowgradient.cpp",
|
||||
"$_gm/shapes.cpp",
|
||||
"$_gm/shapes_as_paths.cpp",
|
||||
"$_gm/sharedcorners.cpp",
|
||||
"$_gm/showmiplevels.cpp",
|
||||
"$_gm/simpleaaclip.cpp",
|
||||
"$_gm/simple_magnification.cpp",
|
||||
|
@ -96,6 +96,43 @@ void GrCCCoverageProcessor::Shader::CalcEdgeCoveragesAtBloatVertices(GrGLSLVerte
|
||||
s->codeAppendf("}");
|
||||
}
|
||||
|
||||
void GrCCCoverageProcessor::Shader::CalcCornerCoverageAttenuation(GrGLSLVertexGeoBuilder* s,
|
||||
const char* leftDir,
|
||||
const char* rightDir,
|
||||
const char* outputAttenuation) {
|
||||
// obtuseness = cos(corner_angle) if corner_angle > 90 degrees
|
||||
// 0 if corner_angle <= 90 degrees
|
||||
s->codeAppendf("half obtuseness = max(dot(%s, %s), 0);", leftDir, rightDir);
|
||||
|
||||
// axis_alignedness = 1 when the leftDir/rightDir bisector is aligned with the x- or y-axis
|
||||
// 0 when the bisector falls on a 45 degree angle
|
||||
// (i.e. 1 - tan(angle_to_nearest_axis))
|
||||
s->codeAppendf("half2 abs_bisect = abs(%s - %s);", leftDir, rightDir);
|
||||
s->codeAppend ("half axis_alignedness = 1 - min(abs_bisect.y, abs_bisect.x) / "
|
||||
"max(abs_bisect.x, abs_bisect.y);");
|
||||
|
||||
// ninety_degreesness = sin^2(corner_angle)
|
||||
// sin^2 just because... it's always positive and the results looked better than plain sine... ?
|
||||
s->codeAppendf("half ninety_degreesness = determinant(half2x2(%s, %s));", leftDir, rightDir);
|
||||
s->codeAppend ("ninety_degreesness = ninety_degreesness * ninety_degreesness;");
|
||||
|
||||
// The below formula is not smart. It was just arrived at by considering the following
|
||||
// observations:
|
||||
//
|
||||
// 1. 90-degree, axis-aligned corners have full attenuation along the bisector.
|
||||
// (i.e. coverage = 1 - distance_to_corner^2)
|
||||
// (i.e. outputAttenuation = 0)
|
||||
//
|
||||
// 2. 180-degree corners always have zero attenuation.
|
||||
// (i.e. coverage = 1 - distance_to_corner)
|
||||
// (i.e. outputAttenuation = 1)
|
||||
//
|
||||
// 3. 90-degree corners whose bisector falls on a 45 degree angle also do not attenuate.
|
||||
// (i.e. outputAttenuation = 1)
|
||||
s->codeAppendf("%s = max(obtuseness, axis_alignedness * ninety_degreesness);",
|
||||
outputAttenuation);
|
||||
}
|
||||
|
||||
int GrCCCoverageProcessor::Shader::DefineSoftSampleLocations(GrGLSLFPFragmentBuilder* f,
|
||||
const char* samplesName) {
|
||||
// Standard DX11 sample locations.
|
||||
|
@ -150,10 +150,11 @@ public:
|
||||
GeometryVars*) const {}
|
||||
|
||||
void emitVaryings(GrGLSLVaryingHandler* varyingHandler, GrGLSLVarying::Scope scope,
|
||||
SkString* code, const char* position, const char* inputCoverage,
|
||||
const char* wind) {
|
||||
SkString* code, const char* position, const char* coverage,
|
||||
const char* attenuatedCoverage, const char* wind) {
|
||||
SkASSERT(GrGLSLVarying::Scope::kVertToGeo != scope);
|
||||
this->onEmitVaryings(varyingHandler, scope, code, position, inputCoverage, wind);
|
||||
this->onEmitVaryings(varyingHandler, scope, code, position, coverage,
|
||||
attenuatedCoverage, wind);
|
||||
}
|
||||
|
||||
void emitFragmentCode(const GrCCCoverageProcessor&, GrGLSLFPFragmentBuilder*,
|
||||
@ -183,16 +184,24 @@ public:
|
||||
const char* bloatDir2,
|
||||
const char* outputCoverages);
|
||||
|
||||
// Corner boxes require an additional "attenuation" varying that is multiplied by the
|
||||
// regular (linearly-interpolated) coverage. This function calculates the attenuation value
|
||||
// to use in the single, outermost vertex. The remaining three vertices of the corner box
|
||||
// all use an attenuation value of 1.
|
||||
static void CalcCornerCoverageAttenuation(GrGLSLVertexGeoBuilder*, const char* leftDir,
|
||||
const char* rightDir,
|
||||
const char* outputAttenuation);
|
||||
|
||||
virtual ~Shader() {}
|
||||
|
||||
protected:
|
||||
// Here the subclass adds its internal varyings to the handler and produces code to
|
||||
// initialize those varyings from a given position, input coverage value, and wind.
|
||||
//
|
||||
// NOTE: the coverage input is only relevant for triangles. Otherwise it is null.
|
||||
// NOTE: the coverage inputs are only relevant for triangles. Otherwise they are null.
|
||||
virtual void onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code,
|
||||
const char* position, const char* inputCoverage,
|
||||
const char* wind) = 0;
|
||||
const char* position, const char* coverage,
|
||||
const char* attenuatedCoverage, const char* wind) = 0;
|
||||
|
||||
// Emits the fragment code that calculates a pixel's signed coverage value.
|
||||
virtual void onEmitFragmentCode(GrGLSLFPFragmentBuilder*,
|
||||
|
@ -80,10 +80,15 @@ protected:
|
||||
RenderPass::kTriangleCorners == proc.fRenderPass) {
|
||||
coverage = emitArgs.emplace_back("coverage", kHalf_GrSLType).c_str();
|
||||
}
|
||||
const char* attenuatedCoverage = nullptr;
|
||||
if (RenderPass::kTriangleCorners == proc.fRenderPass) {
|
||||
attenuatedCoverage = emitArgs.emplace_back("attenuated_coverage",
|
||||
kHalf2_GrSLType).c_str();
|
||||
}
|
||||
g->emitFunction(kVoid_GrSLType, "emitVertex", emitArgs.count(), emitArgs.begin(), [&]() {
|
||||
SkString fnBody;
|
||||
fShader->emitVaryings(varyingHandler, GrGLSLVarying::Scope::kGeoToFrag, &fnBody,
|
||||
position, coverage, wind.c_str());
|
||||
position, coverage, attenuatedCoverage, wind.c_str());
|
||||
g->emitVertex(&fnBody, position, rtAdjust);
|
||||
return fnBody;
|
||||
}().c_str(), &emitVertexFn);
|
||||
@ -218,7 +223,7 @@ public:
|
||||
|
||||
/**
|
||||
* Generates conservative rasters around triangle corners (aka pixel-size boxes) and calculates
|
||||
* coverage ramps that fix up the coverage values written by GSTriangleImpl.
|
||||
* coverage and attenuation ramps to fix up the coverage values written by GSTriangleImpl.
|
||||
*/
|
||||
class GSTriangleCornerImpl : public GrCCCoverageProcessor::GSImpl {
|
||||
public:
|
||||
@ -236,36 +241,55 @@ public:
|
||||
g->codeAppendf("float2 right = pts[(sk_InvocationID + (%s > 0 ? 1 : 2)) %% 3];",
|
||||
wind.c_str());
|
||||
|
||||
g->codeAppend ("float2 leftdir = corner - left;");
|
||||
g->codeAppend ("leftdir = (float2(0) != leftdir) ? normalize(leftdir) : float2(1, 0);");
|
||||
|
||||
g->codeAppend ("float2 rightdir = right - corner;");
|
||||
g->codeAppend ("rightdir = (float2(0) != rightdir) ? normalize(rightdir) : float2(1, 0);");
|
||||
|
||||
// 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);");
|
||||
// triangle, in the direction that should ramp to zero coverage with attenuation. The
|
||||
// crossbloat runs perpindicular to outbloat.
|
||||
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",
|
||||
Shader::CalcEdgeCoveragesAtBloatVertices(g, "left", "corner", "-outbloat", "-crossbloat",
|
||||
"left_coverages");
|
||||
g->codeAppend ("}");
|
||||
|
||||
g->codeAppend ("half2 right_coverages; {");
|
||||
Shader::CalcEdgeCoveragesAtBloatVertices(g, "corner", "right", "outbloat", "-crossbloat",
|
||||
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.
|
||||
g->codeAppend ("half attenuation; {");
|
||||
Shader::CalcCornerCoverageAttenuation(g, "leftdir", "rightdir", "attenuation");
|
||||
g->codeAppend ("}");
|
||||
|
||||
// Emit a corner box. The first coverage argument erases the values that were written
|
||||
// previously by the hull and edge geometry. The second pair are multiplied together by the
|
||||
// fragment shader. They ramp to 0 with attenuation in the direction of outbloat, and
|
||||
// linearly from left-edge coverage to right-edge coverage in the direction of crossbloat.
|
||||
//
|
||||
// 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);
|
||||
// edge points in the direction of outbloat.
|
||||
g->codeAppendf("%s(corner - crossbloat * bloat, "
|
||||
"right_coverages[1] - left_coverages[1],"
|
||||
"half2(1 + left_coverages[1], 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);
|
||||
"1 + left_coverages[0] + right_coverages[0],"
|
||||
"half2(0, attenuation));", emitVertexFn);
|
||||
|
||||
g->codeAppendf("%s(corner - outbloat * bloat, "
|
||||
"-1 - left_coverages[0] - right_coverages[0],"
|
||||
"half2(1 + left_coverages[0] + right_coverages[0], 1));", emitVertexFn);
|
||||
|
||||
g->codeAppendf("%s(corner + crossbloat * bloat, "
|
||||
"left_coverages[1] - right_coverages[1],"
|
||||
"half2(1 + right_coverages[1], 1));", emitVertexFn);
|
||||
|
||||
g->configure(InputType::kLines, OutputType::kTriangleStrip, 4, 3);
|
||||
}
|
||||
|
@ -28,6 +28,11 @@ protected:
|
||||
this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
|
||||
}
|
||||
|
||||
struct Coverages {
|
||||
const char* fCoverage = nullptr; // half
|
||||
const char* fAttenuatedCoverage = nullptr; // half2
|
||||
};
|
||||
|
||||
void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) final {
|
||||
const GrCCCoverageProcessor& proc = args.fGP.cast<GrCCCoverageProcessor>();
|
||||
|
||||
@ -63,13 +68,15 @@ protected:
|
||||
#endif
|
||||
v->defineConstant("bloat", bloat);
|
||||
|
||||
const char* coverage = this->emitVertexPosition(proc, v, gpArgs);
|
||||
Coverages coverages;
|
||||
this->emitVertexPosition(proc, v, gpArgs, &coverages);
|
||||
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");
|
||||
gpArgs->fPositionVar.c_str(), coverages.fCoverage,
|
||||
coverages.fAttenuatedCoverage, "wind");
|
||||
v->codeAppend(varyingCode.c_str());
|
||||
|
||||
varyingHandler->emitAttributes(proc);
|
||||
@ -79,8 +86,8 @@ protected:
|
||||
fShader->emitFragmentCode(proc, args.fFragBuilder, args.fOutputColor, args.fOutputCoverage);
|
||||
}
|
||||
|
||||
virtual const char* emitVertexPosition(const GrCCCoverageProcessor&, GrGLSLVertexBuilder*,
|
||||
GrGPArgs*) const = 0;
|
||||
virtual void emitVertexPosition(const GrCCCoverageProcessor&, GrGLSLVertexBuilder*, GrGPArgs*,
|
||||
Coverages* outCoverages) const = 0;
|
||||
|
||||
virtual ~VSImpl() {}
|
||||
|
||||
@ -187,9 +194,9 @@ static constexpr uint16_t kTriangleIndicesAsStrips[] = {
|
||||
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.
|
||||
28, 27, 29, 30, kRestartStrip, // First corner.
|
||||
32, 31, 33, 34, kRestartStrip, // Second corner.
|
||||
36, 35, 37, 38 // Third corner.
|
||||
};
|
||||
|
||||
static constexpr uint16_t kTriangleIndicesAsTris[] = {
|
||||
@ -223,16 +230,16 @@ static constexpr uint16_t kTriangleIndicesAsTris[] = {
|
||||
26, 25, 24,
|
||||
|
||||
// First corner.
|
||||
27, 28, 30,
|
||||
28, 29, 30,
|
||||
28, 27, 29,
|
||||
27, 30, 29,
|
||||
|
||||
// Second corner.
|
||||
31, 32, 34,
|
||||
32, 33, 34,
|
||||
32, 31, 33,
|
||||
31, 34, 33,
|
||||
|
||||
// Third corner.
|
||||
35, 36, 38,
|
||||
36, 37, 38,
|
||||
36, 35, 37,
|
||||
35, 38, 37,
|
||||
};
|
||||
|
||||
GR_DECLARE_STATIC_UNIQUE_KEY(gTriangleIndexBufferKey);
|
||||
@ -300,8 +307,8 @@ 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 {
|
||||
void emitVertexPosition(const GrCCCoverageProcessor& proc, GrGLSLVertexBuilder* v,
|
||||
GrGPArgs* gpArgs, Coverages* outCoverages) const override {
|
||||
Shader::GeometryVars vars;
|
||||
fShader->emitSetupCode(v, "pts", nullptr, "wind", &vars);
|
||||
|
||||
@ -344,22 +351,26 @@ public:
|
||||
v->codeAppend ("float2 bloatdir = leftbloat;");
|
||||
|
||||
if (3 == fNumSides) { // Only triangles emit corner boxes.
|
||||
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)) {", // 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.
|
||||
// Therefore it is important to align the box so its diagonal shared
|
||||
// edge points out of the triangle, in the direction that ramps to 0.
|
||||
v->codeAppend ( "bloatdir = float2(leftdir.x > rightdir.x ? +1 : -1, "
|
||||
"leftdir.y > rightdir.y ? +1 : -1);");
|
||||
|
||||
// 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 ( "}");
|
||||
// For corner boxes, we hack left_right_notequal to always true. This
|
||||
// in turn causes the upcoming code to always rotate, generating all
|
||||
// 4 vertices of the corner box.
|
||||
v->codeAppendf( "left_right_notequal = bool2(true);");
|
||||
v->codeAppend ("}");
|
||||
}
|
||||
|
||||
@ -392,12 +403,14 @@ public:
|
||||
v->codeAppend ("float2 vertex = corner + bloatdir * bloat;");
|
||||
gpArgs->fPositionVar.set(kFloat2_GrSLType, "vertex");
|
||||
|
||||
// For triangles, we also emit coverage in order to handle edges and corners.
|
||||
const char* coverage = nullptr;
|
||||
// For triangles, we also emit coverage and attenuation.
|
||||
if (3 == fNumSides) {
|
||||
// The hull has a coverage of +1 all around.
|
||||
v->codeAppend ("half coverage = +1;");
|
||||
|
||||
// Corner boxes require attenuation.
|
||||
v->codeAppend ("half2 attenuated_coverage = half2(0);");
|
||||
|
||||
v->codeAppendf("if (0 != (%s & %i)) {", // Are we an edge OR corner?
|
||||
proc.getAttrib(kAttribIdx_VertexData).fName,
|
||||
kVertexData_IsEdgeBit | kVertexData_IsCornerBit);
|
||||
@ -406,20 +419,31 @@ public:
|
||||
|
||||
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 ( "half attenuation;");
|
||||
Shader::CalcCornerCoverageAttenuation(v, "leftdir", "rightdir", "attenuation");
|
||||
|
||||
// For corners, "coverage" erases the values that were written
|
||||
// previously by the hull and edge geometry.
|
||||
v->codeAppend ( "coverage = -1 - left_coverage - right_coverage;");
|
||||
|
||||
// The x and y components of "attenuated_coverage" are multiplied
|
||||
// together by the fragment shader. They ramp to 0 with attenuation
|
||||
// in the diagonal that points out of the triangle, and linearly from
|
||||
// left-edge coverage to right in the opposite diagonal. bloatidx=0
|
||||
// is the outermost vertex; the one that has attenuation.
|
||||
v->codeAppend ( "attenuated_coverage = (0 == bloatidx)"
|
||||
"? half2(0, attenuation) : half2(1);");
|
||||
v->codeAppend ( "if (1 == bloatidx || 2 == bloatidx) {");
|
||||
v->codeAppend ( "attenuated_coverage.x += right_coverage;");
|
||||
v->codeAppend ( "}");
|
||||
v->codeAppend ( "if (bloatidx < 2) {");
|
||||
v->codeAppend ( "coverage -= right_coverage;");
|
||||
v->codeAppend ( "if (bloatidx >= 2) {");
|
||||
v->codeAppend ( "attenuated_coverage.x += left_coverage;");
|
||||
v->codeAppend ( "}");
|
||||
v->codeAppend ("}");
|
||||
|
||||
@ -429,10 +453,9 @@ public:
|
||||
v->codeAppend ( "coverage = -1 - coverage;");
|
||||
v->codeAppend ("}");
|
||||
|
||||
coverage = "coverage";
|
||||
outCoverages->fCoverage = "coverage";
|
||||
outCoverages->fAttenuatedCoverage = "attenuated_coverage";
|
||||
}
|
||||
|
||||
return coverage;
|
||||
}
|
||||
|
||||
private:
|
||||
@ -463,8 +486,8 @@ 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 {
|
||||
void emitVertexPosition(const GrCCCoverageProcessor&, GrGLSLVertexBuilder* v, GrGPArgs* gpArgs,
|
||||
Coverages* /*outCoverages*/) const override {
|
||||
Shader::GeometryVars vars;
|
||||
v->codeAppend ("int corner_id = sk_VertexID / 4;");
|
||||
fShader->emitSetupCode(v, "pts", "corner_id", "wind", &vars);
|
||||
@ -474,7 +497,6 @@ public:
|
||||
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.
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -79,9 +79,10 @@ void GrCCCubicShader::emitSetupCode(GrGLSLVertexGeoBuilder* s, const char* pts,
|
||||
|
||||
void GrCCCubicShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler,
|
||||
GrGLSLVarying::Scope scope, SkString* code,
|
||||
const char* position, const char* inputCoverage,
|
||||
const char* /*wind*/) {
|
||||
SkASSERT(!inputCoverage);
|
||||
const char* position, const char* coverage,
|
||||
const char* attenuatedCoverage, const char* /*wind*/) {
|
||||
SkASSERT(!coverage);
|
||||
SkASSERT(!attenuatedCoverage);
|
||||
|
||||
fKLMD.reset(kFloat4_GrSLType, scope);
|
||||
varyingHandler->addVarying("klmd", &fKLMD);
|
||||
|
@ -29,7 +29,8 @@ protected:
|
||||
GeometryVars*) const {}
|
||||
|
||||
void onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code,
|
||||
const char* position, const char* inputCoverage, const char* wind) final;
|
||||
const char* position, const char* coverage, const char* attenuatedCoverage,
|
||||
const char* wind) final;
|
||||
virtual void onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code) = 0;
|
||||
|
||||
void onEmitFragmentCode(GrGLSLFPFragmentBuilder*, const char* outputCoverage) const final;
|
||||
|
@ -35,9 +35,10 @@ void GrCCQuadraticShader::emitSetupCode(GrGLSLVertexGeoBuilder* s, const char* p
|
||||
|
||||
void GrCCQuadraticShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler,
|
||||
GrGLSLVarying::Scope scope, SkString* code,
|
||||
const char* position, const char* inputCoverage,
|
||||
const char* wind) {
|
||||
SkASSERT(!inputCoverage);
|
||||
const char* position, const char* coverage,
|
||||
const char* attenuatedCoverage, const char* wind) {
|
||||
SkASSERT(!coverage);
|
||||
SkASSERT(!attenuatedCoverage);
|
||||
|
||||
fXYDW.reset(kFloat4_GrSLType, scope);
|
||||
varyingHandler->addVarying("xydw", &fXYDW);
|
||||
|
@ -28,7 +28,8 @@ protected:
|
||||
GeometryVars*) const = 0;
|
||||
|
||||
void onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code,
|
||||
const char* position, const char* inputCoverage, const char* wind) final;
|
||||
const char* position, const char* coverage, const char* attenuatedCoverage,
|
||||
const char* wind) final;
|
||||
virtual void onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code) {}
|
||||
|
||||
void onEmitFragmentCode(GrGLSLFPFragmentBuilder*, const char* outputCoverage) const final;
|
||||
|
@ -18,16 +18,29 @@
|
||||
*/
|
||||
class GrCCTriangleShader : public GrCCCoverageProcessor::Shader {
|
||||
void onEmitVaryings(GrGLSLVaryingHandler* varyingHandler, GrGLSLVarying::Scope scope,
|
||||
SkString* code, const char* /*position*/, const char* inputCoverage,
|
||||
const char* wind) override {
|
||||
SkASSERT(inputCoverage);
|
||||
fCoverageTimesWind.reset(kHalf_GrSLType, scope);
|
||||
varyingHandler->addVarying("coverage_times_wind", &fCoverageTimesWind);
|
||||
code->appendf("%s = %s * %s;", OutName(fCoverageTimesWind), inputCoverage, wind);
|
||||
SkString* code, const char* position, const char* coverage,
|
||||
const char* attenuatedCoverage, const char* wind) override {
|
||||
if (!attenuatedCoverage) {
|
||||
fCoverageTimesWind.reset(kHalf_GrSLType, scope);
|
||||
varyingHandler->addVarying("coverage_times_wind", &fCoverageTimesWind);
|
||||
code->appendf("%s = %s * %s;", OutName(fCoverageTimesWind), coverage, wind);
|
||||
} else {
|
||||
fCoverageTimesWind.reset(kHalf3_GrSLType, scope);
|
||||
varyingHandler->addVarying("coverage_times_wind", &fCoverageTimesWind);
|
||||
code->appendf("%s = half3(%s, %s);",
|
||||
OutName(fCoverageTimesWind), attenuatedCoverage, coverage);
|
||||
code->appendf("%s.yz *= %s;", OutName(fCoverageTimesWind), wind);
|
||||
}
|
||||
}
|
||||
|
||||
void onEmitFragmentCode(GrGLSLFPFragmentBuilder* f, const char* outputCoverage) const override {
|
||||
f->codeAppendf("%s = %s;", outputCoverage, fCoverageTimesWind.fsIn());
|
||||
if (kHalf_GrSLType == fCoverageTimesWind.type()) {
|
||||
f->codeAppendf("%s = %s;", outputCoverage, fCoverageTimesWind.fsIn());
|
||||
} else {
|
||||
f->codeAppendf("%s = %s.x * %s.y + %s.z;",
|
||||
outputCoverage, fCoverageTimesWind.fsIn(), fCoverageTimesWind.fsIn(),
|
||||
fCoverageTimesWind.fsIn());
|
||||
}
|
||||
}
|
||||
|
||||
GrGLSLVarying fCoverageTimesWind;
|
||||
|
Loading…
Reference in New Issue
Block a user