Add the concept of an "input" FP to GrSkSLFP
FPs are used to represent effects with varying semantics. In particular, shaders (that generate colors with no real input), and color filters (that always have a "primary" input color on which they operate). Even FPs that are not directly tied to a particular SkShader or SkColorFilter tend to fall into these two categories. GrSkSLFP was tailored to the shader semantics. It always supported having child FPs, but these weren't considered "special" in any way - there could be multiple, and each one could be sampled in whatever way the SkSL wanted. This CL adds a dedicated "input" FP slot, so that color filters (and FPs that resemble color filters) can naturally map better. Previously, we had to inject an additional FP to do composition of the input and the SkSL. That worked fine - this change is really about cutting down on allocations and extra CPU work for that extra FP in the tree. Change-Id: Ic37457b31f8ed7b4aa8d814a5e78806caa19e1c5 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/418739 Commit-Queue: Brian Osman <brianosman@google.com> Reviewed-by: John Stiles <johnstiles@google.com>
This commit is contained in:
parent
fba8a742cb
commit
b2cb817d23
@ -636,34 +636,37 @@ static sk_sp<SkData> get_xformed_uniforms(const SkRuntimeEffect* effect,
|
||||
}
|
||||
|
||||
#if SK_SUPPORT_GPU
|
||||
static std::unique_ptr<GrFragmentProcessor> make_effect_fp(
|
||||
sk_sp<SkRuntimeEffect> effect,
|
||||
const char* name,
|
||||
sk_sp<SkData> uniforms,
|
||||
SkSpan<const SkRuntimeEffect::ChildPtr> children,
|
||||
const GrFPArgs& childArgs) {
|
||||
auto fp = GrSkSLFP::Make(std::move(effect), name, std::move(uniforms));
|
||||
static GrFPResult make_effect_fp(sk_sp<SkRuntimeEffect> effect,
|
||||
const char* name,
|
||||
sk_sp<SkData> uniforms,
|
||||
std::unique_ptr<GrFragmentProcessor> inputFP,
|
||||
SkSpan<const SkRuntimeEffect::ChildPtr> children,
|
||||
const GrFPArgs& childArgs) {
|
||||
SkSTArray<8, std::unique_ptr<GrFragmentProcessor>> childFPs;
|
||||
for (const auto& child : children) {
|
||||
if (child.shader) {
|
||||
auto childFP = as_SB(child.shader)->asFragmentProcessor(childArgs);
|
||||
if (!childFP) {
|
||||
return nullptr;
|
||||
return GrFPFailure(std::move(inputFP));
|
||||
}
|
||||
fp->addChild(std::move(childFP));
|
||||
childFPs.push_back(std::move(childFP));
|
||||
} else if (child.colorFilter) {
|
||||
auto [success, childFP] = as_CFB(child.colorFilter)
|
||||
->asFragmentProcessor(/*inputFP=*/nullptr,
|
||||
childArgs.fContext,
|
||||
*childArgs.fDstColorInfo);
|
||||
if (!success) {
|
||||
return nullptr;
|
||||
return GrFPFailure(std::move(inputFP));
|
||||
}
|
||||
fp->addChild(std::move(childFP));
|
||||
childFPs.push_back(std::move(childFP));
|
||||
} else {
|
||||
fp->addChild(nullptr);
|
||||
childFPs.push_back(nullptr);
|
||||
}
|
||||
}
|
||||
return std::move(fp);
|
||||
auto fp = GrSkSLFP::MakeWithData(
|
||||
std::move(effect), name, std::move(inputFP), std::move(uniforms), SkMakeSpan(childFPs));
|
||||
SkASSERT(fp);
|
||||
return GrFPSuccess(std::move(fp));
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -685,20 +688,12 @@ public:
|
||||
SkASSERT(uniforms);
|
||||
|
||||
GrFPArgs childArgs(context, SkSimpleMatrixProvider(SkMatrix::I()), &colorInfo);
|
||||
auto fp = make_effect_fp(fEffect,
|
||||
"Runtime_Color_Filter",
|
||||
std::move(uniforms),
|
||||
SkMakeSpan(fChildren),
|
||||
childArgs);
|
||||
if (!fp) {
|
||||
return GrFPFailure(std::move(inputFP));
|
||||
}
|
||||
|
||||
// Runtime effect scripts are written to take an input color, not a fragment processor.
|
||||
// We need to pass the input to the runtime filter using Compose. This ensures that it will
|
||||
// be invoked exactly once, and the result will be returned when null children are sampled,
|
||||
// or as the (default) input color for non-null children.
|
||||
return GrFPSuccess(GrFragmentProcessor::Compose(std::move(fp), std::move(inputFP)));
|
||||
return make_effect_fp(fEffect,
|
||||
"runtime_color_filter",
|
||||
std::move(uniforms),
|
||||
std::move(inputFP),
|
||||
SkMakeSpan(fChildren),
|
||||
childArgs);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -853,18 +848,22 @@ public:
|
||||
GrFPArgs childArgs = args;
|
||||
childArgs.fInputColorIsOpaque = false;
|
||||
|
||||
auto result = make_effect_fp(
|
||||
fEffect, "runtime_shader", std::move(uniforms), SkMakeSpan(fChildren), childArgs);
|
||||
if (!result) {
|
||||
auto [success, fp] = make_effect_fp(fEffect,
|
||||
"runtime_shader",
|
||||
std::move(uniforms),
|
||||
/*inputFP=*/nullptr,
|
||||
SkMakeSpan(fChildren),
|
||||
childArgs);
|
||||
if (!success) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// If the shader was created with isOpaque = true, we *force* that result here.
|
||||
// CPU does the same thing (in SkShaderBase::program).
|
||||
if (fIsOpaque) {
|
||||
result = GrFragmentProcessor::SwizzleOutput(std::move(result), GrSwizzle::RGB1());
|
||||
fp = GrFragmentProcessor::SwizzleOutput(std::move(fp), GrSwizzle::RGB1());
|
||||
}
|
||||
result = GrMatrixEffect::Make(matrix, std::move(result));
|
||||
fp = GrMatrixEffect::Make(matrix, std::move(fp));
|
||||
// Three cases of GrClampType to think about:
|
||||
// kAuto - Normalized fixed-point. If fIsOpaque, then A is 1 (above), and the format's
|
||||
// range ensures RGB must be no larger. If !fIsOpaque, we clamp here.
|
||||
@ -873,9 +872,9 @@ public:
|
||||
// kNone - Unclamped floating point. No clamping is done, ever.
|
||||
GrClampType clampType = GrColorTypeClampType(args.fDstColorInfo->colorType());
|
||||
if (clampType == GrClampType::kManual || (clampType == GrClampType::kAuto && !fIsOpaque)) {
|
||||
return GrFragmentProcessor::ClampPremulOutput(std::move(result));
|
||||
return GrFragmentProcessor::ClampPremulOutput(std::move(fp));
|
||||
} else {
|
||||
return result;
|
||||
return std::move(fp);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -1128,19 +1127,22 @@ sk_sp<SkImage> SkRuntimeEffect::makeImage(GrRecordingContext* recordingContext,
|
||||
uniforms = get_xformed_uniforms(this, std::move(uniforms), resultInfo.colorSpace());
|
||||
SkASSERT(uniforms);
|
||||
|
||||
auto fp = GrSkSLFP::Make(sk_ref_sp(this),
|
||||
"runtime_image",
|
||||
std::move(uniforms));
|
||||
SkSimpleMatrixProvider matrixProvider(SkMatrix::I());
|
||||
GrColorInfo colorInfo(resultInfo.colorInfo());
|
||||
GrFPArgs args(recordingContext, matrixProvider, &colorInfo);
|
||||
SkSTArray<8, std::unique_ptr<GrFragmentProcessor>> childFPs;
|
||||
for (size_t i = 0; i < childCount; ++i) {
|
||||
if (!children[i]) {
|
||||
return nullptr;
|
||||
}
|
||||
auto childFP = as_SB(children[i])->asFragmentProcessor(args);
|
||||
fp->addChild(std::move(childFP));
|
||||
childFPs.push_back(as_SB(children[i])->asFragmentProcessor(args));
|
||||
}
|
||||
auto fp = GrSkSLFP::MakeWithData(sk_ref_sp(this),
|
||||
"runtime_image",
|
||||
/*inputFP=*/nullptr,
|
||||
std::move(uniforms),
|
||||
SkMakeSpan(childFPs));
|
||||
|
||||
if (localMatrix) {
|
||||
SkMatrix invLM;
|
||||
if (!localMatrix->invert(&invLM)) {
|
||||
|
@ -324,7 +324,7 @@ std::unique_ptr<GrFragmentProcessor> make_arithmetic_fp(
|
||||
return color;
|
||||
}
|
||||
)");
|
||||
return GrSkSLFP::Make(effect, "arithmetic_fp",
|
||||
return GrSkSLFP::Make(effect, "arithmetic_fp", /*inputFP=*/nullptr,
|
||||
"srcFP", std::move(srcFP),
|
||||
"dstFP", std::move(dstFP),
|
||||
"k", k,
|
||||
|
@ -135,7 +135,7 @@ static std::unique_ptr<GrFragmentProcessor> make_magnifier_fp(
|
||||
1.f / bounds.width(),
|
||||
1.f / bounds.height()};
|
||||
|
||||
return GrSkSLFP::Make(effect, "magnifier_fp",
|
||||
return GrSkSLFP::Make(effect, "magnifier_fp", /*inputFP=*/nullptr,
|
||||
"src", std::move(input),
|
||||
"boundsUniform", boundsUniform,
|
||||
"xInvZoom", xInvZoom,
|
||||
|
@ -214,7 +214,7 @@ std::unique_ptr<GrFragmentProcessor> GrFragmentProcessor::MakeColor(SkPMColor4f
|
||||
uniform half4 color;
|
||||
half4 main(half4 inColor) { return color; }
|
||||
)");
|
||||
return GrSkSLFP::Make(effect, "color_fp", "color", color);
|
||||
return GrSkSLFP::Make(effect, "color_fp", /*inputFP=*/nullptr, "color", color);
|
||||
}
|
||||
|
||||
std::unique_ptr<GrFragmentProcessor> GrFragmentProcessor::MulChildByInputAlpha(
|
||||
|
@ -159,6 +159,14 @@ public:
|
||||
int fUniformIndex = 0;
|
||||
};
|
||||
|
||||
// If we have an input child, we invoke it now, and make the result of that be the "input
|
||||
// color" for all other purposes later (eg, the default passed via sample calls, etc.)
|
||||
if (fp.fInputChildIndex >= 0) {
|
||||
args.fFragBuilder->codeAppendf("%s = %s;\n",
|
||||
args.fInputColor,
|
||||
this->invokeChild(fp.fInputChildIndex, args).c_str());
|
||||
}
|
||||
|
||||
// Snap off a global copy of the input color at the start of main. We need this when
|
||||
// we call child processors (particularly from helper functions, which can't "see" the
|
||||
// parameter to main). Even from within main, if the code mutates the parameter, calls to
|
||||
@ -225,9 +233,12 @@ public:
|
||||
std::vector<UniformHandle> fUniformHandles;
|
||||
};
|
||||
|
||||
std::unique_ptr<GrSkSLFP> GrSkSLFP::Make(sk_sp<SkRuntimeEffect> effect,
|
||||
const char* name,
|
||||
sk_sp<SkData> uniforms) {
|
||||
std::unique_ptr<GrSkSLFP> GrSkSLFP::MakeWithData(
|
||||
sk_sp<SkRuntimeEffect> effect,
|
||||
const char* name,
|
||||
std::unique_ptr<GrFragmentProcessor> inputFP,
|
||||
sk_sp<SkData> uniforms,
|
||||
SkSpan<std::unique_ptr<GrFragmentProcessor>> childFPs) {
|
||||
if (uniforms->size() != effect->uniformSize()) {
|
||||
return nullptr;
|
||||
}
|
||||
@ -236,6 +247,12 @@ std::unique_ptr<GrSkSLFP> GrSkSLFP::Make(sk_sp<SkRuntimeEffect> effect,
|
||||
std::unique_ptr<GrSkSLFP> fp(new (uniformSize + uniformFlagSize)
|
||||
GrSkSLFP(std::move(effect), name));
|
||||
sk_careful_memcpy(fp->uniformData(), uniforms->data(), uniformSize);
|
||||
for (auto& childFP : childFPs) {
|
||||
fp->addChild(std::move(childFP));
|
||||
}
|
||||
if (inputFP) {
|
||||
fp->setInput(std::move(inputFP));
|
||||
}
|
||||
return fp;
|
||||
}
|
||||
|
||||
@ -246,7 +263,7 @@ GrSkSLFP::GrSkSLFP(sk_sp<SkRuntimeEffect> effect, const char* name)
|
||||
: kNone_OptimizationFlags)
|
||||
, fEffect(std::move(effect))
|
||||
, fName(name)
|
||||
, fUniformSize(fEffect->uniformSize()) {
|
||||
, fUniformSize(SkToU32(fEffect->uniformSize())) {
|
||||
memset(this->uniformFlags(), 0, fEffect->uniforms().count() * sizeof(UniformFlags));
|
||||
if (fEffect->usesSampleCoords()) {
|
||||
this->setUsesSampleCoordsDirectly();
|
||||
@ -257,7 +274,8 @@ GrSkSLFP::GrSkSLFP(const GrSkSLFP& other)
|
||||
: INHERITED(kGrSkSLFP_ClassID, other.optimizationFlags())
|
||||
, fEffect(other.fEffect)
|
||||
, fName(other.fName)
|
||||
, fUniformSize(other.fUniformSize) {
|
||||
, fUniformSize(other.fUniformSize)
|
||||
, fInputChildIndex(other.fInputChildIndex) {
|
||||
sk_careful_memcpy(this->uniformFlags(),
|
||||
other.uniformFlags(),
|
||||
fEffect->uniforms().count() * sizeof(UniformFlags));
|
||||
@ -271,12 +289,21 @@ GrSkSLFP::GrSkSLFP(const GrSkSLFP& other)
|
||||
}
|
||||
|
||||
void GrSkSLFP::addChild(std::unique_ptr<GrFragmentProcessor> child) {
|
||||
SkASSERTF(fInputChildIndex == -1, "all addChild calls must happen before setInput");
|
||||
int childIndex = this->numChildProcessors();
|
||||
SkASSERT((size_t)childIndex < fEffect->fSampleUsages.size());
|
||||
this->mergeOptimizationFlags(ProcessorOptimizationFlags(child.get()));
|
||||
this->registerChild(std::move(child), fEffect->fSampleUsages[childIndex]);
|
||||
}
|
||||
|
||||
void GrSkSLFP::setInput(std::unique_ptr<GrFragmentProcessor> input) {
|
||||
SkASSERTF(fInputChildIndex == -1, "setInput should not be called more than once");
|
||||
fInputChildIndex = this->numChildProcessors();
|
||||
SkASSERT((size_t)fInputChildIndex == fEffect->fSampleUsages.size());
|
||||
this->mergeOptimizationFlags(ProcessorOptimizationFlags(input.get()));
|
||||
this->registerChild(std::move(input), SkSL::SampleUsage::PassThrough());
|
||||
}
|
||||
|
||||
std::unique_ptr<GrGLSLFragmentProcessor> GrSkSLFP::onMakeProgramImpl() const {
|
||||
return std::make_unique<GrGLSLSkSLFP>();
|
||||
}
|
||||
@ -286,7 +313,7 @@ void GrSkSLFP::onGetGLSLProcessorKey(const GrShaderCaps& caps, GrProcessorKeyBui
|
||||
// That ensures that we will (at worst) use the wrong program, but one that expects the same
|
||||
// amount of uniform data.
|
||||
b->add32(fEffect->hash());
|
||||
b->add32(SkToU32(fUniformSize));
|
||||
b->add32(fUniformSize);
|
||||
|
||||
const UniformFlags* flags = this->uniformFlags();
|
||||
const uint8_t* uniformData = this->uniformData();
|
||||
@ -325,7 +352,11 @@ SkPMColor4f GrSkSLFP::constantOutputForConstantInput(const SkPMColor4f& inputCol
|
||||
return ConstantOutputForConstantInput(this->childProcessor(index), color);
|
||||
};
|
||||
|
||||
return program->eval(inputColor, this->uniformData(), evalChild);
|
||||
SkPMColor4f color = (fInputChildIndex >= 0)
|
||||
? ConstantOutputForConstantInput(
|
||||
this->childProcessor(fInputChildIndex), inputColor)
|
||||
: inputColor;
|
||||
return program->eval(color, this->uniformData(), evalChild);
|
||||
}
|
||||
|
||||
/**************************************************************************************************/
|
||||
|
@ -60,20 +60,25 @@ public:
|
||||
kSpecialize_Flag = 0x1,
|
||||
};
|
||||
|
||||
/**
|
||||
* Both factories support a single 'input' FP, as well as a collection of other 'child' FPs.
|
||||
* The 'child' FPs correspond to the children declared in the effect's SkSL. The inputFP is
|
||||
* optional, and intended for instances that have color filter semantics. This is an implicit
|
||||
* child - if present, it's evaluated to produce the input color fed to the SkSL. Otherwise,
|
||||
* the SkSL receives this FP's input color directly.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Creates a new fragment processor from an SkRuntimeEffect and a data blob containing values
|
||||
* for all of the 'uniform' variables in the SkSL source. The layout of the uniforms blob is
|
||||
* dictated by the SkRuntimeEffect.
|
||||
*/
|
||||
static std::unique_ptr<GrSkSLFP> Make(sk_sp<SkRuntimeEffect> effect,
|
||||
const char* name,
|
||||
sk_sp<SkData> uniforms);
|
||||
|
||||
const char* name() const override { return fName; }
|
||||
|
||||
void addChild(std::unique_ptr<GrFragmentProcessor> child);
|
||||
|
||||
std::unique_ptr<GrFragmentProcessor> clone() const override;
|
||||
static std::unique_ptr<GrSkSLFP> MakeWithData(
|
||||
sk_sp<SkRuntimeEffect> effect,
|
||||
const char* name,
|
||||
std::unique_ptr<GrFragmentProcessor> inputFP,
|
||||
sk_sp<SkData> uniforms,
|
||||
SkSpan<std::unique_ptr<GrFragmentProcessor>> childFPs);
|
||||
|
||||
/*
|
||||
* Constructs a GrSkSLFP from a series of name-value pairs, corresponding to the children and
|
||||
@ -93,7 +98,7 @@ public:
|
||||
* std::unique_ptr<GrFragmentProcessor> child = ...;
|
||||
* float scaleVal = ...;
|
||||
* SkV2 ptVal = ...;
|
||||
* auto fp = GrSkSLFP::Make(effect, "my_effect",
|
||||
* auto fp = GrSkSLFP::Make(effect, "my_effect", nullptr,
|
||||
* "input", std::move(child),
|
||||
* "scale", scaleVal,
|
||||
* "pt", ptVal);
|
||||
@ -106,6 +111,7 @@ public:
|
||||
template <typename... Args>
|
||||
static std::unique_ptr<GrSkSLFP> Make(sk_sp<SkRuntimeEffect> effect,
|
||||
const char* name,
|
||||
std::unique_ptr<GrFragmentProcessor> inputFP,
|
||||
Args&&... args) {
|
||||
#ifdef SK_DEBUG
|
||||
checkArgs(effect->fUniforms.begin(),
|
||||
@ -118,13 +124,22 @@ public:
|
||||
size_t uniformPayloadSize = UniformPayloadSize(effect.get());
|
||||
std::unique_ptr<GrSkSLFP> fp(new (uniformPayloadSize) GrSkSLFP(std::move(effect), name));
|
||||
fp->appendArgs(fp->uniformData(), fp->uniformFlags(), std::forward<Args>(args)...);
|
||||
if (inputFP) {
|
||||
fp->setInput(std::move(inputFP));
|
||||
}
|
||||
return fp;
|
||||
}
|
||||
|
||||
const char* name() const override { return fName; }
|
||||
std::unique_ptr<GrFragmentProcessor> clone() const override;
|
||||
|
||||
private:
|
||||
GrSkSLFP(sk_sp<SkRuntimeEffect> effect, const char* name);
|
||||
GrSkSLFP(const GrSkSLFP& other);
|
||||
|
||||
void addChild(std::unique_ptr<GrFragmentProcessor> child);
|
||||
void setInput(std::unique_ptr<GrFragmentProcessor> input);
|
||||
|
||||
std::unique_ptr<GrGLSLFragmentProcessor> onMakeProgramImpl() const override;
|
||||
|
||||
void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override;
|
||||
@ -260,7 +275,8 @@ private:
|
||||
|
||||
sk_sp<SkRuntimeEffect> fEffect;
|
||||
const char* fName;
|
||||
size_t fUniformSize;
|
||||
uint32_t fUniformSize;
|
||||
int fInputChildIndex = -1;
|
||||
|
||||
GR_DECLARE_FRAGMENT_PROCESSOR_TEST
|
||||
|
||||
|
@ -95,8 +95,7 @@ GrFPResult SkGaussianColorFilter::asFragmentProcessor(std::unique_ptr<GrFragment
|
||||
return half4(factor);
|
||||
}
|
||||
)");
|
||||
auto fp = GrSkSLFP::Make(effect, "gaussian_fp");
|
||||
return GrFPSuccess(GrFragmentProcessor::Compose(std::move(fp), std::move(inputFP)));
|
||||
return GrFPSuccess(GrSkSLFP::Make(effect, "gaussian_fp", std::move(inputFP)));
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -662,7 +662,7 @@ DEF_TEST(SkRuntimeShaderSampleCoords, r) {
|
||||
REPORTER_ASSERT(r, effect);
|
||||
|
||||
auto child = GrFragmentProcessor::MakeColor({ 1, 1, 1, 1 });
|
||||
auto fp = GrSkSLFP::Make(effect, "test_fp", "child", std::move(child));
|
||||
auto fp = GrSkSLFP::Make(effect, "test_fp", /*inputFP=*/nullptr, "child", std::move(child));
|
||||
REPORTER_ASSERT(r, fp);
|
||||
|
||||
REPORTER_ASSERT(r, fp->childProcessor(0)->isSampledWithExplicitCoords() == expectExplicit);
|
||||
@ -715,8 +715,8 @@ DEF_GPUTEST_FOR_ALL_CONTEXTS(GrSkSLFP_Specialized, r, ctxInfo) {
|
||||
half4 main(float2 xy) { return color; }
|
||||
)");
|
||||
FpAndKey result;
|
||||
result.fp = GrSkSLFP::Make(
|
||||
std::move(effect), "color_fp", "color", GrSkSLFP::SpecializeIf(specialize, color));
|
||||
result.fp = GrSkSLFP::Make(std::move(effect), "color_fp", /*inputFP=*/nullptr,
|
||||
"color", GrSkSLFP::SpecializeIf(specialize, color));
|
||||
GrProcessorKeyBuilder builder(&result.key);
|
||||
result.fp->getGLSLProcessorKey(*ctxInfo.directContext()->priv().caps()->shaderCaps(),
|
||||
&builder);
|
||||
|
Loading…
Reference in New Issue
Block a user