Use a uniform to drive Porter-Duff shader composition.

This lets most simple Porter-Duff based Compose operations share the
same shader text; the values in a uniform control which blend mode we
will use. This is only enabled in reduced-shader mode.

Best-case scenario I could find:
- Original: http://screen/EkXsnfNPC9CxiwE
- Uniforms: http://screen/9rJLe6JMrhteD24

Change-Id: I0edc7910a9a2ae7f4e5abbd57128d7b3b52971bf
Bug: skia:13109
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/525317
Commit-Queue: John Stiles <johnstiles@google.com>
Auto-Submit: John Stiles <johnstiles@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>
This commit is contained in:
John Stiles 2022-03-30 10:09:52 -04:00 committed by SkCQ
parent 2e9bc4700a
commit 9287beb9eb
2 changed files with 96 additions and 23 deletions

View File

@ -23,15 +23,35 @@ static inline bool does_cpu_blend_impl_match_gpu(SkBlendMode mode) {
mode != SkBlendMode::kColorBurn;
}
static bool supports_shared_blend_logic(SkBlendMode mode) {
switch (mode) {
case SkBlendMode::kSrcOver:
case SkBlendMode::kDstOver:
case SkBlendMode::kSrcIn:
case SkBlendMode::kDstIn:
case SkBlendMode::kSrcOut:
case SkBlendMode::kDstOut:
case SkBlendMode::kSrcATop:
case SkBlendMode::kDstATop:
case SkBlendMode::kXor:
case SkBlendMode::kPlus:
return true;
default:
return false;
}
}
//////////////////////////////////////////////////////////////////////////////
class BlendFragmentProcessor : public GrFragmentProcessor {
public:
static std::unique_ptr<GrFragmentProcessor> Make(std::unique_ptr<GrFragmentProcessor> src,
std::unique_ptr<GrFragmentProcessor> dst,
SkBlendMode mode) {
SkBlendMode mode,
bool shareBlendLogic) {
return std::unique_ptr<GrFragmentProcessor>(
new BlendFragmentProcessor(std::move(src), std::move(dst), mode));
new BlendFragmentProcessor(std::move(src), std::move(dst), mode, shareBlendLogic));
}
const char* name() const override { return "Blend"; }
@ -41,9 +61,11 @@ public:
private:
BlendFragmentProcessor(std::unique_ptr<GrFragmentProcessor> src,
std::unique_ptr<GrFragmentProcessor> dst,
SkBlendMode mode)
SkBlendMode mode,
bool shareBlendLogic)
: INHERITED(kBlendFragmentProcessor_ClassID, OptFlags(src.get(), dst.get(), mode))
, fMode(mode) {
, fMode(mode)
, fShareBlendLogic(shareBlendLogic && supports_shared_blend_logic(mode)) {
this->setIsBlendFunction();
this->registerChild(std::move(src));
this->registerChild(std::move(dst));
@ -51,7 +73,8 @@ private:
BlendFragmentProcessor(const BlendFragmentProcessor& that)
: INHERITED(that)
, fMode(that.fMode) {}
, fMode(that.fMode)
, fShareBlendLogic(that.fShareBlendLogic) {}
#if GR_TEST_UTILS
SkString onDumpInfo() const override {
@ -139,9 +162,13 @@ private:
return flags;
}
void onAddToKey(const GrShaderCaps&, skgpu::KeyBuilder* b) const override {
void onAddToKey(const GrShaderCaps& caps, skgpu::KeyBuilder* b) const override {
if (fShareBlendLogic) {
b->add32(-1);
} else {
b->add32((int)fMode);
}
}
bool onIsEqual(const GrFragmentProcessor& other) const override {
const BlendFragmentProcessor& cs = other.cast<BlendFragmentProcessor>();
@ -161,6 +188,7 @@ private:
std::unique_ptr<ProgramImpl> onMakeProgramImpl() const override;
SkBlendMode fMode;
bool fShareBlendLogic;
GR_DECLARE_FRAGMENT_PROCESSOR_TEST
@ -180,13 +208,14 @@ std::unique_ptr<GrFragmentProcessor> BlendFragmentProcessor::TestCreate(GrProces
if (d->fRandom->nextBool()) {
std::swap(src, dst);
}
bool shareLogic = d->fRandom->nextBool();
SkBlendMode mode;
do {
mode = static_cast<SkBlendMode>(d->fRandom->nextRangeU(0, (int)SkBlendMode::kLastMode));
} while (SkBlendMode::kClear == mode || SkBlendMode::kSrc == mode || SkBlendMode::kDst == mode);
return std::unique_ptr<GrFragmentProcessor>(
new BlendFragmentProcessor(std::move(src), std::move(dst), mode));
new BlendFragmentProcessor(std::move(src), std::move(dst), mode, shareLogic));
}
#endif
@ -200,20 +229,59 @@ std::unique_ptr<GrFragmentProcessor::ProgramImpl> BlendFragmentProcessor::onMake
void emitCode(EmitArgs& args) override {
GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
const BlendFragmentProcessor& bfp = args.fFp.cast<BlendFragmentProcessor>();
SkBlendMode mode = bfp.fMode;
fragBuilder->codeAppendf("// Blend mode: %s\n", SkBlendMode_Name(mode));
const SkBlendMode mode = bfp.fMode;
// Invoke src/dst with our input color (or substitute input color if no child FP)
SkString srcColor = this->invokeChild(0, args);
SkString dstColor = this->invokeChild(1, args);
// Blend src and dst colors together.
if (bfp.fShareBlendLogic) {
// Handle basic Porter-Duff blend ops by multiplying with uniforms.
const char* blendOp;
fBlendOpUniform = args.fUniformHandler->addUniform(&args.fFp,
kFragment_GrShaderFlag,
SkSLType::kHalf4,
"blendOp", &blendOp);
fragBuilder->codeAppendf(
"half4 src = %s, dst = %s;"
"return min(half4(1), "
"src * (%s.x + (%s.z * (dst.a + min(%s.z, 0)))) + "
"dst * (%s.y + (%s.w * (src.a + min(%s.w, 0)))));",
srcColor.c_str(), dstColor.c_str(),
blendOp, blendOp, blendOp,
blendOp, blendOp, blendOp);
} else {
// Blend src and dst colors together using a built-in blend function.
fragBuilder->codeAppendf("return %s(%s, %s);",
GrGLSLBlend::BlendFuncName(mode),
srcColor.c_str(),
dstColor.c_str());
}
}
void onSetData(const GrGLSLProgramDataManager& pdman,
const GrFragmentProcessor& fp) override {
const BlendFragmentProcessor& bfp = fp.cast<BlendFragmentProcessor>();
const SkBlendMode mode = bfp.fMode;
if (bfp.fShareBlendLogic) {
switch (mode) {
case SkBlendMode::kSrcOver: pdman.set4f(fBlendOpUniform, 1, 0, 0, -1); break;
case SkBlendMode::kDstOver: pdman.set4f(fBlendOpUniform, 0, 1, -1, 0); break;
case SkBlendMode::kSrcIn: pdman.set4f(fBlendOpUniform, 0, 0, 1, 0); break;
case SkBlendMode::kDstIn: pdman.set4f(fBlendOpUniform, 0, 0, 0, 1); break;
case SkBlendMode::kSrcOut: pdman.set4f(fBlendOpUniform, 0, 0, -1, 0); break;
case SkBlendMode::kDstOut: pdman.set4f(fBlendOpUniform, 0, 0, 0, -1); break;
case SkBlendMode::kSrcATop: pdman.set4f(fBlendOpUniform, 0, 0, 1, -1); break;
case SkBlendMode::kDstATop: pdman.set4f(fBlendOpUniform, 0, 0, -1, 1); break;
case SkBlendMode::kXor: pdman.set4f(fBlendOpUniform, 0, 0, -1, -1); break;
case SkBlendMode::kPlus: pdman.set4f(fBlendOpUniform, 1, 1, 0, 0); break;
default: SkDEBUGFAIL("unexpected blend mode"); break;
}
}
}
UniformHandle fBlendOpUniform;
};
return std::make_unique<Impl>();
@ -224,7 +292,8 @@ std::unique_ptr<GrFragmentProcessor::ProgramImpl> BlendFragmentProcessor::onMake
std::unique_ptr<GrFragmentProcessor> GrBlendFragmentProcessor::Make(
std::unique_ptr<GrFragmentProcessor> src,
std::unique_ptr<GrFragmentProcessor> dst,
SkBlendMode mode) {
SkBlendMode mode,
bool shareBlendLogic) {
switch (mode) {
case SkBlendMode::kClear:
return GrFragmentProcessor::MakeColor(SK_PMColor4fTRANSPARENT);
@ -233,6 +302,7 @@ std::unique_ptr<GrFragmentProcessor> GrBlendFragmentProcessor::Make(
case SkBlendMode::kDst:
return dst;
default:
return BlendFragmentProcessor::Make(std::move(src), std::move(dst), mode);
return BlendFragmentProcessor::Make(
std::move(src), std::move(dst), mode, shareBlendLogic);
}
}

View File

@ -17,20 +17,23 @@ namespace GrBlendFragmentProcessor {
/**
* Blends src and dst inputs according to the blend mode. If either input is null, fInputColor is
* used instead. TODO(johnstiles): Uses a uniform to specify the blend mode, reducing shader count.
* used instead.
* - When `shareBlendLogic` is false, the blend function logic is written directly into the code.
* - When `shareBlendLogic` is true, most Porter-Duff blends share the same code, and a uniform
* is used to pick the blend type. This can reduce our overall shader count.
*/
std::unique_ptr<GrFragmentProcessor> Make(std::unique_ptr<GrFragmentProcessor> src,
std::unique_ptr<GrFragmentProcessor> dst,
SkBlendMode mode);
SkBlendMode mode,
bool shareBlendLogic = true);
/**
* Blends src and dst inputs according to the blend mode. If either input is null, fInputColor is
* used instead. Bakes the blend function directly into the code.
* used instead. Hard-wires a single blend mode into the code (slightly reducing complexity).
*/
template <SkBlendMode mode>
std::unique_ptr<GrFragmentProcessor> Make(std::unique_ptr<GrFragmentProcessor> src,
std::unique_ptr<GrFragmentProcessor> dst) {
return Make(std::move(src), std::move(dst), mode);
return Make(std::move(src), std::move(dst), mode, /*shareBlendLogic=*/false);
}