fbe28593e5
At this point, every created instance of GrCoordTransform is an identity so can be removed. Adding it manually using addCoordTransforms() is no different than relying on the (current) implicitly returned coord transform if the FP calls setUsesSampleCoordsDirectly(). Removing the addCoordTransform() lets us enforce that this remains the case and this CL also deletes all of those members that were previously used to provide access to the sample coordinates. As part of this, GrFragmentProcessor.h and many other files no longer need to include GrCoordTransform.h. This exposed a surprising popularity on SkMatrixPriv.h so I updated those files to include that header directly. Technically, a .fp file can still have an @coordTransform section and the sksl generator will try and call addCoordTransform, which will then fail to build. A follow up CL removes that support in .fp generation. Bug: skia:10416 Change-Id: I5e4d2bb49ee6d7e56ac75ca00be5631106fec20b Reviewed-on: https://skia-review.googlesource.com/c/skia/+/299291 Reviewed-by: Brian Osman <brianosman@google.com> Commit-Queue: Michael Ludwig <michaelludwig@google.com>
390 lines
16 KiB
C++
390 lines
16 KiB
C++
/*
|
|
* Copyright 2019 Google LLC.
|
|
*
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
* found in the LICENSE file.
|
|
*/
|
|
|
|
#include "gm/gm.h"
|
|
#include "include/core/SkFont.h"
|
|
#include "include/effects/SkRuntimeEffect.h"
|
|
#include "src/gpu/GrBitmapTextureMaker.h"
|
|
#include "src/gpu/GrContextPriv.h"
|
|
#include "src/gpu/GrRenderTargetContextPriv.h"
|
|
#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
|
|
#include "src/gpu/ops/GrFillRectOp.h"
|
|
#include "tools/ToolUtils.h"
|
|
|
|
// Samples child with a constant (literal) matrix
|
|
// Scales along X
|
|
class ConstantMatrixEffect : public GrFragmentProcessor {
|
|
public:
|
|
static constexpr GrProcessor::ClassID CLASS_ID = (GrProcessor::ClassID) 3;
|
|
|
|
ConstantMatrixEffect(std::unique_ptr<GrFragmentProcessor> child)
|
|
: GrFragmentProcessor(CLASS_ID, kNone_OptimizationFlags) {
|
|
this->registerChild(std::move(child),
|
|
SkSL::SampleMatrix::MakeConstUniform(
|
|
"float3x3(float3(0.5, 0.0, 0.0), "
|
|
"float3(0.0, 1.0, 0.0), "
|
|
"float3(0.0, 0.0, 1.0))"));
|
|
}
|
|
|
|
const char* name() const override { return "ConstantMatrixEffect"; }
|
|
void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override {}
|
|
bool onIsEqual(const GrFragmentProcessor& that) const override { return this == &that; }
|
|
std::unique_ptr<GrFragmentProcessor> clone() const override { return nullptr; }
|
|
|
|
GrGLSLFragmentProcessor* onCreateGLSLInstance() const override {
|
|
class Impl : public GrGLSLFragmentProcessor {
|
|
void emitCode(EmitArgs& args) override {
|
|
SkString sample = this->invokeChildWithMatrix(0, args);
|
|
args.fFragBuilder->codeAppendf("%s = %s;\n", args.fOutputColor, sample.c_str());
|
|
}
|
|
};
|
|
return new Impl;
|
|
}
|
|
};
|
|
|
|
// Samples child with a uniform matrix (functionally identical to GrMatrixEffect)
|
|
// Scales along Y
|
|
class UniformMatrixEffect : public GrFragmentProcessor {
|
|
public:
|
|
static constexpr GrProcessor::ClassID CLASS_ID = (GrProcessor::ClassID) 4;
|
|
|
|
UniformMatrixEffect(std::unique_ptr<GrFragmentProcessor> child)
|
|
: GrFragmentProcessor(CLASS_ID, kNone_OptimizationFlags) {
|
|
this->registerChild(std::move(child), SkSL::SampleMatrix::MakeConstUniform("matrix"));
|
|
}
|
|
|
|
const char* name() const override { return "UniformMatrixEffect"; }
|
|
void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override {}
|
|
bool onIsEqual(const GrFragmentProcessor& that) const override { return this == &that; }
|
|
std::unique_ptr<GrFragmentProcessor> clone() const override { return nullptr; }
|
|
|
|
GrGLSLFragmentProcessor* onCreateGLSLInstance() const override {
|
|
class Impl : public GrGLSLFragmentProcessor {
|
|
void emitCode(EmitArgs& args) override {
|
|
fMatrixVar = args.fUniformHandler->addUniform(&args.fFp, kFragment_GrShaderFlag,
|
|
kFloat3x3_GrSLType, "matrix");
|
|
SkString sample = this->invokeChildWithMatrix(0, args);
|
|
args.fFragBuilder->codeAppendf("%s = %s;\n", args.fOutputColor, sample.c_str());
|
|
}
|
|
void onSetData(const GrGLSLProgramDataManager& pdman,
|
|
const GrFragmentProcessor& proc) override {
|
|
pdman.setSkMatrix(fMatrixVar, SkMatrix::Scale(1, 0.5f));
|
|
}
|
|
UniformHandle fMatrixVar;
|
|
};
|
|
return new Impl;
|
|
}
|
|
};
|
|
|
|
// Samples child with a variable matrix
|
|
// Translates along X
|
|
// Typically, kVariable would be due to multiple sample(matrix) invocations, but this artificially
|
|
// uses kVariable with a single (constant) matrix.
|
|
class VariableMatrixEffect : public GrFragmentProcessor {
|
|
public:
|
|
static constexpr GrProcessor::ClassID CLASS_ID = (GrProcessor::ClassID) 5;
|
|
|
|
VariableMatrixEffect(std::unique_ptr<GrFragmentProcessor> child)
|
|
: GrFragmentProcessor(CLASS_ID, kNone_OptimizationFlags) {
|
|
this->registerChild(std::move(child), SkSL::SampleMatrix::MakeVariable());
|
|
}
|
|
|
|
const char* name() const override { return "VariableMatrixEffect"; }
|
|
void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override {}
|
|
bool onIsEqual(const GrFragmentProcessor& that) const override { return this == &that; }
|
|
std::unique_ptr<GrFragmentProcessor> clone() const override { return nullptr; }
|
|
|
|
GrGLSLFragmentProcessor* onCreateGLSLInstance() const override {
|
|
class Impl : public GrGLSLFragmentProcessor {
|
|
void emitCode(EmitArgs& args) override {
|
|
SkString sample = this->invokeChildWithMatrix(
|
|
0, args, "float3x3(1, 0, 0, 0, 1, 0, 8, 0, 1)");
|
|
args.fFragBuilder->codeAppendf("%s = %s;\n", args.fOutputColor, sample.c_str());
|
|
}
|
|
};
|
|
return new Impl;
|
|
}
|
|
};
|
|
|
|
// Samples child with explicit coords
|
|
// Translates along Y
|
|
class ExplicitCoordEffect : public GrFragmentProcessor {
|
|
public:
|
|
static constexpr GrProcessor::ClassID CLASS_ID = (GrProcessor::ClassID) 6;
|
|
|
|
ExplicitCoordEffect(std::unique_ptr<GrFragmentProcessor> child)
|
|
: GrFragmentProcessor(CLASS_ID, kNone_OptimizationFlags) {
|
|
this->registerExplicitlySampledChild(std::move(child));
|
|
this->setUsesSampleCoordsDirectly();
|
|
}
|
|
|
|
const char* name() const override { return "ExplicitCoordEffect"; }
|
|
void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override {}
|
|
bool onIsEqual(const GrFragmentProcessor& that) const override { return this == &that; }
|
|
std::unique_ptr<GrFragmentProcessor> clone() const override { return nullptr; }
|
|
|
|
GrGLSLFragmentProcessor* onCreateGLSLInstance() const override {
|
|
class Impl : public GrGLSLFragmentProcessor {
|
|
void emitCode(EmitArgs& args) override {
|
|
args.fFragBuilder->codeAppendf("float2 coord = %s + float2(0, 8);",
|
|
args.fSampleCoord);
|
|
SkString sample = this->invokeChild(0, args, "coord");
|
|
args.fFragBuilder->codeAppendf("%s = %s;\n", args.fOutputColor, sample.c_str());
|
|
}
|
|
};
|
|
return new Impl;
|
|
}
|
|
};
|
|
|
|
// Generates test pattern
|
|
class TestPatternEffect : public GrFragmentProcessor {
|
|
public:
|
|
static constexpr GrProcessor::ClassID CLASS_ID = (GrProcessor::ClassID) 7;
|
|
|
|
TestPatternEffect() : GrFragmentProcessor(CLASS_ID, kNone_OptimizationFlags) {
|
|
this->setUsesSampleCoordsDirectly();
|
|
}
|
|
|
|
const char* name() const override { return "TestPatternEffect"; }
|
|
void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override {}
|
|
bool onIsEqual(const GrFragmentProcessor& that) const override { return this == &that; }
|
|
std::unique_ptr<GrFragmentProcessor> clone() const override { return nullptr; }
|
|
|
|
GrGLSLFragmentProcessor* onCreateGLSLInstance() const override {
|
|
class Impl : public GrGLSLFragmentProcessor {
|
|
void emitCode(EmitArgs& args) override {
|
|
auto fb = args.fFragBuilder;
|
|
fb->codeAppendf("float2 coord = %s / 64.0;", args.fSampleCoord);
|
|
fb->codeAppendf("coord = floor(coord * 4) / 3;");
|
|
fb->codeAppendf("%s = half4(half2(coord.rg), 0, 1);\n", args.fOutputColor);
|
|
}
|
|
};
|
|
return new Impl;
|
|
}
|
|
};
|
|
|
|
SkBitmap make_test_bitmap() {
|
|
SkBitmap bitmap;
|
|
bitmap.allocN32Pixels(64, 64);
|
|
SkCanvas canvas(bitmap);
|
|
|
|
SkFont font(ToolUtils::create_portable_typeface());
|
|
const char* alpha = "ABCDEFGHIJKLMNOP";
|
|
|
|
for (int i = 0; i < 16; ++i) {
|
|
int tx = i % 4,
|
|
ty = i / 4;
|
|
int x = tx * 16,
|
|
y = ty * 16;
|
|
SkPaint paint;
|
|
paint.setColor4f({ tx / 3.0f, ty / 3.0f, 0.0f, 1.0f });
|
|
canvas.drawRect(SkRect::MakeXYWH(x, y, 16, 16), paint);
|
|
paint.setColor4f({ (3-tx) / 3.0f, (3-ty)/3.0f, 1.0f, 1.0f });
|
|
canvas.drawSimpleText(alpha + i, 1, SkTextEncoding::kUTF8, x + 3, y + 13, font, paint);
|
|
}
|
|
|
|
return bitmap;
|
|
}
|
|
|
|
enum EffectType {
|
|
kConstant,
|
|
kUniform,
|
|
kVariable,
|
|
kExplicit,
|
|
};
|
|
|
|
static std::unique_ptr<GrFragmentProcessor> wrap(std::unique_ptr<GrFragmentProcessor> fp,
|
|
EffectType effectType) {
|
|
switch (effectType) {
|
|
case kConstant:
|
|
return std::unique_ptr<GrFragmentProcessor>(new ConstantMatrixEffect(std::move(fp)));
|
|
case kUniform:
|
|
return std::unique_ptr<GrFragmentProcessor>(new UniformMatrixEffect(std::move(fp)));
|
|
case kVariable:
|
|
return std::unique_ptr<GrFragmentProcessor>(new VariableMatrixEffect(std::move(fp)));
|
|
case kExplicit:
|
|
return std::unique_ptr<GrFragmentProcessor>(new ExplicitCoordEffect(std::move(fp)));
|
|
}
|
|
SkUNREACHABLE;
|
|
}
|
|
|
|
DEF_SIMPLE_GPU_GM(fp_sample_chaining, ctx, rtCtx, canvas, 380, 306) {
|
|
SkBitmap bmp = make_test_bitmap();
|
|
|
|
GrBitmapTextureMaker maker(ctx, bmp, GrImageTexGenPolicy::kDraw);
|
|
int x = 10, y = 10;
|
|
|
|
auto nextCol = [&] { x += (64 + 10); };
|
|
auto nextRow = [&] { x = 10; y += (64 + 10); };
|
|
|
|
auto draw = [&](std::initializer_list<EffectType> effects) {
|
|
// Enable TestPatternEffect to get a fully procedural inner effect. It's not quite as nice
|
|
// visually (no text labels in each box), but it avoids the extra GrMatrixEffect.
|
|
// Switching it on actually triggers *more* shader compilation failures.
|
|
#if 0
|
|
auto fp = std::unique_ptr<GrFragmentProcessor>(new TestPatternEffect());
|
|
#else
|
|
auto view = maker.view(GrMipMapped::kNo);
|
|
auto fp = GrTextureEffect::Make(std::move(view), maker.alphaType());
|
|
#endif
|
|
for (EffectType effectType : effects) {
|
|
fp = wrap(std::move(fp), effectType);
|
|
}
|
|
GrPaint paint;
|
|
paint.addColorFragmentProcessor(std::move(fp));
|
|
rtCtx->drawRect(nullptr, std::move(paint), GrAA::kNo, SkMatrix::Translate(x, y),
|
|
SkRect::MakeIWH(64, 64));
|
|
nextCol();
|
|
};
|
|
|
|
// Reminder, in every case, the chain is more complicated than it seems, because the
|
|
// GrTextureEffect is wrapped in a GrMatrixEffect, which is subject to the same bugs that
|
|
// we're testing (particularly the bug about owner/base in UniformMatrixEffect).
|
|
|
|
// First row: no transform, then each one independently applied
|
|
draw({}); // Identity (4 rows and columns)
|
|
draw({ kConstant }); // Scale X axis by 2x (2 visible columns)
|
|
draw({ kUniform }); // Scale Y axis by 2x (2 visible rows)
|
|
draw({ kVariable }); // Translate left by 8px
|
|
draw({ kExplicit }); // Translate up by 8px
|
|
nextRow();
|
|
|
|
// Second row: transform duplicated
|
|
draw({ kConstant, kUniform }); // Scale XY by 2x (2 rows and columns)
|
|
draw({ kConstant, kConstant }); // Scale X axis by 4x (1 visible column)
|
|
draw({ kUniform, kUniform }); // Scale Y axis by 4x (1 visible row)
|
|
draw({ kVariable, kVariable }); // Translate left by 16px
|
|
draw({ kExplicit, kExplicit }); // Translate up by 16px
|
|
nextRow();
|
|
|
|
// Remember, these are applied inside out:
|
|
draw({ kConstant, kExplicit }); // Scale X by 2x and translate up by 8px
|
|
draw({ kConstant, kVariable }); // Scale X by 2x and translate left by 8px
|
|
draw({ kUniform, kVariable }); // Scale Y by 2x and translate left by 8px
|
|
draw({ kUniform, kExplicit }); // Scale Y by 2x and translate up by 8px
|
|
draw({ kVariable, kExplicit }); // Translate left and up by 8px
|
|
nextRow();
|
|
|
|
draw({ kExplicit, kExplicit, kConstant }); // Scale X by 2x and translate up by 16px
|
|
draw({ kVariable, kConstant }); // Scale X by 2x and translate left by 16px
|
|
draw({ kVariable, kVariable, kUniform }); // Scale Y by 2x and translate left by 16px
|
|
draw({ kExplicit, kUniform }); // Scale Y by 2x and translate up by 16px
|
|
draw({ kExplicit, kUniform, kVariable, kConstant }); // Scale XY by 2x and translate xy 16px
|
|
}
|
|
|
|
const char* gConstantMatrixSkSL = R"(
|
|
in shader child;
|
|
void main(float2 xy, inout half4 color) {
|
|
color = sample(child, float3x3(0.5, 0.0, 0.0,
|
|
0.0, 1.0, 0.0,
|
|
0.0, 0.0, 1.0));
|
|
}
|
|
)";
|
|
|
|
const char* gUniformMatrixSkSL = R"(
|
|
in shader child;
|
|
uniform float3x3 matrix;
|
|
void main(float2 xy, inout half4 color) {
|
|
color = sample(child, matrix);
|
|
}
|
|
)";
|
|
|
|
// This form (uniform * constant) is currently detected as variable, thanks to our limited analysis
|
|
// when scanning for sample matrices. If/when that improves, make this expression more complicated.
|
|
// NOTE: An easy fix would be to hoist the expression to a local matrix variable, but that triggers
|
|
// another bug where the resulting expression is used to compute the new local coords *before* the
|
|
// local variable is declared.
|
|
const char* gVariableMatrixSkSL = R"(
|
|
in shader child;
|
|
uniform float3x3 matrix;
|
|
void main(float2 xy, inout half4 color) {
|
|
color = sample(child, matrix * 0.5);
|
|
}
|
|
)";
|
|
|
|
const char* gExplicitCoordSkSL = R"(
|
|
in shader child;
|
|
void main(float2 xy, inout half4 color) {
|
|
color = sample(child, xy + float2(0, 8));
|
|
}
|
|
)";
|
|
|
|
// Version of fp_sample_chaining that uses SkRuntimeEffect
|
|
DEF_SIMPLE_GM(sksl_sample_chaining, canvas, 380, 306) {
|
|
SkBitmap bmp = make_test_bitmap();
|
|
|
|
sk_sp<SkRuntimeEffect> effects[4] = {
|
|
std::get<0>(SkRuntimeEffect::Make(SkString(gConstantMatrixSkSL))),
|
|
std::get<0>(SkRuntimeEffect::Make(SkString(gUniformMatrixSkSL))),
|
|
std::get<0>(SkRuntimeEffect::Make(SkString(gVariableMatrixSkSL))),
|
|
std::get<0>(SkRuntimeEffect::Make(SkString(gExplicitCoordSkSL))),
|
|
};
|
|
|
|
canvas->translate(10, 10);
|
|
canvas->save();
|
|
auto nextCol = [&] { canvas->translate(64 + 10, 0); };
|
|
auto nextRow = [&] { canvas->restore(); canvas->translate(0, 64 + 10); canvas->save(); };
|
|
|
|
auto draw = [&](std::initializer_list<EffectType> effectTypes) {
|
|
auto shader = bmp.makeShader();
|
|
|
|
for (EffectType effectType : effectTypes) {
|
|
SkRuntimeShaderBuilder builder(effects[effectType]);
|
|
builder.child("child") = shader;
|
|
switch (effectType) {
|
|
case kUniform:
|
|
builder.input("matrix") = SkMatrix::Scale(1.0f, 0.5f);
|
|
break;
|
|
case kVariable:
|
|
builder.input("matrix") = SkMatrix::Translate(8, 0);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
shader = builder.makeShader(nullptr, true);
|
|
}
|
|
SkPaint paint;
|
|
paint.setShader(shader);
|
|
canvas->drawRect(SkRect::MakeWH(64, 64), paint);
|
|
nextCol();
|
|
};
|
|
|
|
// Reminder, in every case, the chain is more complicated than it seems, because the
|
|
// GrTextureEffect is wrapped in a GrMatrixEffect, which is subject to the same bugs that
|
|
// we're testing (particularly the bug about owner/base in UniformMatrixEffect).
|
|
|
|
// First row: no transform, then each one independently applied
|
|
draw({}); // Identity (4 rows and columns)
|
|
draw({ kConstant }); // Scale X axis by 2x (2 visible columns)
|
|
draw({ kUniform }); // Scale Y axis by 2x (2 visible rows)
|
|
draw({ kVariable }); // Translate left by 8px
|
|
draw({ kExplicit }); // Translate up by 8px
|
|
nextRow();
|
|
|
|
// Second row: transform duplicated
|
|
draw({ kConstant, kUniform }); // Scale XY by 2x (2 rows and columns)
|
|
draw({ kConstant, kConstant }); // Scale X axis by 4x (1 visible column)
|
|
draw({ kUniform, kUniform }); // Scale Y axis by 4x (1 visible row)
|
|
draw({ kVariable, kVariable }); // Translate left by 16px
|
|
draw({ kExplicit, kExplicit }); // Translate up by 16px
|
|
nextRow();
|
|
|
|
// Remember, these are applied inside out:
|
|
draw({ kConstant, kExplicit }); // Scale X by 2x and translate up by 8px
|
|
draw({ kConstant, kVariable }); // Scale X by 2x and translate left by 8px
|
|
draw({ kUniform, kVariable }); // Scale Y by 2x and translate left by 8px
|
|
draw({ kUniform, kExplicit }); // Scale Y by 2x and translate up by 8px
|
|
draw({ kVariable, kExplicit }); // Translate left and up by 8px
|
|
nextRow();
|
|
|
|
draw({ kExplicit, kExplicit, kConstant }); // Scale X by 2x and translate up by 16px
|
|
draw({ kVariable, kConstant }); // Scale X by 2x and translate left by 16px
|
|
draw({ kVariable, kVariable, kUniform }); // Scale Y by 2x and translate left by 16px
|
|
draw({ kExplicit, kUniform }); // Scale Y by 2x and translate up by 16px
|
|
draw({ kExplicit, kUniform, kVariable, kConstant }); // Scale XY by 2x and translate xy 16px
|
|
}
|