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:
parent
2e9bc4700a
commit
9287beb9eb
@ -23,15 +23,35 @@ static inline bool does_cpu_blend_impl_match_gpu(SkBlendMode mode) {
|
|||||||
mode != SkBlendMode::kColorBurn;
|
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 {
|
class BlendFragmentProcessor : public GrFragmentProcessor {
|
||||||
public:
|
public:
|
||||||
static std::unique_ptr<GrFragmentProcessor> Make(std::unique_ptr<GrFragmentProcessor> src,
|
static std::unique_ptr<GrFragmentProcessor> Make(std::unique_ptr<GrFragmentProcessor> src,
|
||||||
std::unique_ptr<GrFragmentProcessor> dst,
|
std::unique_ptr<GrFragmentProcessor> dst,
|
||||||
SkBlendMode mode) {
|
SkBlendMode mode,
|
||||||
|
bool shareBlendLogic) {
|
||||||
return std::unique_ptr<GrFragmentProcessor>(
|
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"; }
|
const char* name() const override { return "Blend"; }
|
||||||
@ -41,9 +61,11 @@ public:
|
|||||||
private:
|
private:
|
||||||
BlendFragmentProcessor(std::unique_ptr<GrFragmentProcessor> src,
|
BlendFragmentProcessor(std::unique_ptr<GrFragmentProcessor> src,
|
||||||
std::unique_ptr<GrFragmentProcessor> dst,
|
std::unique_ptr<GrFragmentProcessor> dst,
|
||||||
SkBlendMode mode)
|
SkBlendMode mode,
|
||||||
|
bool shareBlendLogic)
|
||||||
: INHERITED(kBlendFragmentProcessor_ClassID, OptFlags(src.get(), dst.get(), mode))
|
: INHERITED(kBlendFragmentProcessor_ClassID, OptFlags(src.get(), dst.get(), mode))
|
||||||
, fMode(mode) {
|
, fMode(mode)
|
||||||
|
, fShareBlendLogic(shareBlendLogic && supports_shared_blend_logic(mode)) {
|
||||||
this->setIsBlendFunction();
|
this->setIsBlendFunction();
|
||||||
this->registerChild(std::move(src));
|
this->registerChild(std::move(src));
|
||||||
this->registerChild(std::move(dst));
|
this->registerChild(std::move(dst));
|
||||||
@ -51,7 +73,8 @@ private:
|
|||||||
|
|
||||||
BlendFragmentProcessor(const BlendFragmentProcessor& that)
|
BlendFragmentProcessor(const BlendFragmentProcessor& that)
|
||||||
: INHERITED(that)
|
: INHERITED(that)
|
||||||
, fMode(that.fMode) {}
|
, fMode(that.fMode)
|
||||||
|
, fShareBlendLogic(that.fShareBlendLogic) {}
|
||||||
|
|
||||||
#if GR_TEST_UTILS
|
#if GR_TEST_UTILS
|
||||||
SkString onDumpInfo() const override {
|
SkString onDumpInfo() const override {
|
||||||
@ -139,9 +162,13 @@ private:
|
|||||||
return flags;
|
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);
|
b->add32((int)fMode);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool onIsEqual(const GrFragmentProcessor& other) const override {
|
bool onIsEqual(const GrFragmentProcessor& other) const override {
|
||||||
const BlendFragmentProcessor& cs = other.cast<BlendFragmentProcessor>();
|
const BlendFragmentProcessor& cs = other.cast<BlendFragmentProcessor>();
|
||||||
@ -161,6 +188,7 @@ private:
|
|||||||
std::unique_ptr<ProgramImpl> onMakeProgramImpl() const override;
|
std::unique_ptr<ProgramImpl> onMakeProgramImpl() const override;
|
||||||
|
|
||||||
SkBlendMode fMode;
|
SkBlendMode fMode;
|
||||||
|
bool fShareBlendLogic;
|
||||||
|
|
||||||
GR_DECLARE_FRAGMENT_PROCESSOR_TEST
|
GR_DECLARE_FRAGMENT_PROCESSOR_TEST
|
||||||
|
|
||||||
@ -180,13 +208,14 @@ std::unique_ptr<GrFragmentProcessor> BlendFragmentProcessor::TestCreate(GrProces
|
|||||||
if (d->fRandom->nextBool()) {
|
if (d->fRandom->nextBool()) {
|
||||||
std::swap(src, dst);
|
std::swap(src, dst);
|
||||||
}
|
}
|
||||||
|
bool shareLogic = d->fRandom->nextBool();
|
||||||
|
|
||||||
SkBlendMode mode;
|
SkBlendMode mode;
|
||||||
do {
|
do {
|
||||||
mode = static_cast<SkBlendMode>(d->fRandom->nextRangeU(0, (int)SkBlendMode::kLastMode));
|
mode = static_cast<SkBlendMode>(d->fRandom->nextRangeU(0, (int)SkBlendMode::kLastMode));
|
||||||
} while (SkBlendMode::kClear == mode || SkBlendMode::kSrc == mode || SkBlendMode::kDst == mode);
|
} while (SkBlendMode::kClear == mode || SkBlendMode::kSrc == mode || SkBlendMode::kDst == mode);
|
||||||
return std::unique_ptr<GrFragmentProcessor>(
|
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
|
#endif
|
||||||
|
|
||||||
@ -200,20 +229,59 @@ std::unique_ptr<GrFragmentProcessor::ProgramImpl> BlendFragmentProcessor::onMake
|
|||||||
void emitCode(EmitArgs& args) override {
|
void emitCode(EmitArgs& args) override {
|
||||||
GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
|
GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
|
||||||
const BlendFragmentProcessor& bfp = args.fFp.cast<BlendFragmentProcessor>();
|
const BlendFragmentProcessor& bfp = args.fFp.cast<BlendFragmentProcessor>();
|
||||||
SkBlendMode mode = bfp.fMode;
|
const SkBlendMode mode = bfp.fMode;
|
||||||
|
|
||||||
fragBuilder->codeAppendf("// Blend mode: %s\n", SkBlendMode_Name(mode));
|
|
||||||
|
|
||||||
// Invoke src/dst with our input color (or substitute input color if no child FP)
|
// Invoke src/dst with our input color (or substitute input color if no child FP)
|
||||||
SkString srcColor = this->invokeChild(0, args);
|
SkString srcColor = this->invokeChild(0, args);
|
||||||
SkString dstColor = this->invokeChild(1, 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);",
|
fragBuilder->codeAppendf("return %s(%s, %s);",
|
||||||
GrGLSLBlend::BlendFuncName(mode),
|
GrGLSLBlend::BlendFuncName(mode),
|
||||||
srcColor.c_str(),
|
srcColor.c_str(),
|
||||||
dstColor.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>();
|
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> GrBlendFragmentProcessor::Make(
|
||||||
std::unique_ptr<GrFragmentProcessor> src,
|
std::unique_ptr<GrFragmentProcessor> src,
|
||||||
std::unique_ptr<GrFragmentProcessor> dst,
|
std::unique_ptr<GrFragmentProcessor> dst,
|
||||||
SkBlendMode mode) {
|
SkBlendMode mode,
|
||||||
|
bool shareBlendLogic) {
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case SkBlendMode::kClear:
|
case SkBlendMode::kClear:
|
||||||
return GrFragmentProcessor::MakeColor(SK_PMColor4fTRANSPARENT);
|
return GrFragmentProcessor::MakeColor(SK_PMColor4fTRANSPARENT);
|
||||||
@ -233,6 +302,7 @@ std::unique_ptr<GrFragmentProcessor> GrBlendFragmentProcessor::Make(
|
|||||||
case SkBlendMode::kDst:
|
case SkBlendMode::kDst:
|
||||||
return dst;
|
return dst;
|
||||||
default:
|
default:
|
||||||
return BlendFragmentProcessor::Make(std::move(src), std::move(dst), mode);
|
return BlendFragmentProcessor::Make(
|
||||||
|
std::move(src), std::move(dst), mode, shareBlendLogic);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,20 +17,23 @@ namespace GrBlendFragmentProcessor {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Blends src and dst inputs according to the blend mode. If either input is null, fInputColor is
|
* 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> Make(std::unique_ptr<GrFragmentProcessor> src,
|
||||||
std::unique_ptr<GrFragmentProcessor> dst,
|
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
|
* 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>
|
template <SkBlendMode mode>
|
||||||
std::unique_ptr<GrFragmentProcessor> Make(std::unique_ptr<GrFragmentProcessor> src,
|
std::unique_ptr<GrFragmentProcessor> Make(std::unique_ptr<GrFragmentProcessor> src,
|
||||||
std::unique_ptr<GrFragmentProcessor> dst) {
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user