Add 'isBlendFunction' as a FP flag and 'destColor' to EmitArgs.

Blend functions are fragment processors that take two arguments, a
source color and dest color.

Change-Id: Ia26fadae42610fd8849716ad68db867cfa082169
Bug: skia:12257
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/433361
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
Auto-Submit: John Stiles <johnstiles@google.com>
This commit is contained in:
John Stiles 2021-07-28 22:21:49 -04:00 committed by SkCQ
parent 109f54d200
commit 0ce9592410
5 changed files with 52 additions and 31 deletions

View File

@ -241,6 +241,11 @@ public:
return SkToBool(fFlags & kWillReadDstColor_Flag);
}
/** Does the SkSL for this FP take two colors as its input arguments? */
bool isBlendFunction() const {
return SkToBool(fFlags & kIsBlendFunction_Flag);
}
/**
* True if this FP refers directly to the sample coordinate parameter of its function
* (e.g. uses EmitArgs::fSampleCoord in emitCode()). This also returns true if the
@ -465,6 +470,12 @@ protected:
fFlags |= kWillReadDstColor_Flag;
}
// FP implementations must set this flag if their GrGLSLFragmentProcessor's emitCode() function
// emits a blend function (taking two color inputs instead of just one).
void setIsBlendFunction() {
fFlags |= kIsBlendFunction_Flag;
}
void mergeOptimizationFlags(OptimizationFlags flags) {
SkASSERT((flags & ~kAll_OptimizationFlags) == 0);
fFlags &= (flags | ~kAll_OptimizationFlags);
@ -498,13 +509,14 @@ private:
// Does not propagate at all
kUsesSampleCoordsDirectly_Flag = kFirstPrivateFlag << 1,
kIsBlendFunction_Flag = kFirstPrivateFlag << 2,
// Propagates down the FP to all its leaves
kSampledWithExplicitCoords_Flag = kFirstPrivateFlag << 2,
kNetTransformHasPerspective_Flag = kFirstPrivateFlag << 3,
kSampledWithExplicitCoords_Flag = kFirstPrivateFlag << 3,
kNetTransformHasPerspective_Flag = kFirstPrivateFlag << 4,
// Propagates up the FP tree to the root
kWillReadDstColor_Flag = kFirstPrivateFlag << 4,
kWillReadDstColor_Flag = kFirstPrivateFlag << 5,
};
void addAndPushFlagToChildren(PrivateFlags flag);

View File

@ -19,22 +19,24 @@ void GrGLSLFragmentProcessor::setData(const GrGLSLProgramDataManager& pdman,
void GrGLSLFragmentProcessor::emitChildFunction(int childIndex, EmitArgs& args) {
SkASSERT(childIndex >= 0);
SkASSERT(args.fFp.childProcessor(childIndex));
const GrFragmentProcessor* childFP = args.fFp.childProcessor(childIndex);
SkASSERT(childFP);
GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
while (childIndex >= (int) fFunctionNames.size()) {
fFunctionNames.emplace_back();
}
// Emit the child's helper function if this is the first time we've seen a call
if (fFunctionNames[childIndex].size() == 0) {
if (fFunctionNames[childIndex].isEmpty()) {
EmitArgs childArgs(fragBuilder,
args.fUniformHandler,
args.fShaderCaps,
*args.fFp.childProcessor(childIndex),
"_input",
*childFP,
childFP->isBlendFunction() ? "_src" : "_input",
"_dst",
"_coords");
fFunctionNames[childIndex] =
fragBuilder->writeProcessorFunction(this->childProcessor(childIndex), childArgs);
GrGLSLFragmentProcessor* childGLSLFP = this->childProcessor(childIndex);
fFunctionNames[childIndex] = fragBuilder->writeProcessorFunction(childGLSLFP, childArgs);
}
}

View File

@ -33,19 +33,20 @@ public:
stages.
@param fragBuilder Interface used to emit code in the shaders.
@param uniformHandler Interface used for accessing information about our uniforms
@param caps The capabilities of the GPU which will render this FP
@param fp The processor that generated this program stage.
@param key The key that was computed by GenKey() from the generating
GrProcessor.
@param outputColor A predefined half4 in the FS in which the stage should place its
output color (or coverage).
@param inputColor A half4 that holds the input color to the stage in the FS. This may
be nullptr in which case the fInputColor is set to "half4(1.0)"
(solid white) so this is guaranteed non-null.
@param inputColor A half4 that holds the input color to the stage in the FS (or the
source color, for blend processors). nullptr inputs are converted
to "half4(1.0)" (solid white) during construction.
TODO: Better system for communicating optimization info
(e.g. input color is solid white, trans black, known to be opaque,
etc.) that allows the processor to communicate back similar known
info about its output.
@param sampleCoord The name of a local coord reference to a float2 variable.
@param destColor A half4 that holds the dest color to the stage. Only meaningful
when the "is blend processor" FP flag is set.
@param sampleCoord The name of a local coord reference to a float2 variable. Only
meaningful when the "references sample coords" FP flag is set.
*/
struct EmitArgs {
EmitArgs(GrGLSLFPFragmentBuilder* fragBuilder,
@ -53,18 +54,21 @@ public:
const GrShaderCaps* caps,
const GrFragmentProcessor& fp,
const char* inputColor,
const char* destColor,
const char* sampleCoord)
: fFragBuilder(fragBuilder)
, fUniformHandler(uniformHandler)
, fShaderCaps(caps)
, fFp(fp)
, fInputColor(inputColor ? inputColor : "half4(1.0)")
, fDestColor(destColor)
, fSampleCoord(sampleCoord) {}
GrGLSLFPFragmentBuilder* fFragBuilder;
GrGLSLUniformHandler* fUniformHandler;
const GrShaderCaps* fShaderCaps;
const GrFragmentProcessor& fFp;
const char* fInputColor;
const char* fDestColor;
const char* fSampleCoord;
};

View File

@ -23,23 +23,26 @@ SkString GrGLSLFPFragmentBuilder::writeProcessorFunction(GrGLSLFragmentProcessor
this->onBeforeChildProcEmitCode();
this->nextStage();
// An FP's function signature is theoretically always main(half4 color, float2 _coords).
// However, if it is only sampled by a chain of uniform matrix expressions (or legacy coord
// transforms), the value that would have been passed to _coords is lifted to the vertex shader
// and stored in a unique varying. In that case it uses that variable and does not have a
// second actual argument for _coords.
// FIXME: An alternative would be to have all FP functions have a float2 argument, and the
// parent FP invokes it with the varying reference when it's been lifted to the vertex shader.
size_t paramCount = 2;
// Conceptually, an FP is always sampled at a particular coordinate. However, if it is only
// sampled by a chain of uniform matrix expressions (or legacy coord transforms), the value that
// would have been passed to _coords is lifted to the vertex shader and stored in a unique
// varying. In that case it uses that variable and we do not pass a second argument for _coords.
GrShaderVar params[] = { GrShaderVar(args.fInputColor, kHalf4_GrSLType),
GrShaderVar(args.fSampleCoord, kFloat2_GrSLType) };
SkSpan<GrShaderVar> paramSpan = SkMakeSpan(params, SK_ARRAY_COUNT(params));
if (!args.fFp.isSampledWithExplicitCoords()) {
if (args.fFp.isBlendFunction()) {
// Blend functions take two colors as input, and never take coordinates.
// Replace the sampleCoord parameter with destination color.
params[1] = GrShaderVar(args.fDestColor, kHalf4_GrSLType);
} else if (args.fFp.isSampledWithExplicitCoords()) {
// We are passing explicit coords; the function keeps its two arguments as-is.
} else {
// Sampled with a uniform matrix expression and/or a legacy coord transform. The actual
// transformation code is emitted in the vertex shader, so this only has to access it.
// Add a float2 _coords variable that maps to the associated varying and replaces the
// absent 2nd argument to the fp's function.
paramCount = 1;
paramSpan = paramSpan.first(1);
if (args.fFp.referencesSampleCoords()) {
GrShaderVar varying = fProgramBuilder->varyingCoordsForFragmentProcessor(&args.fFp);
@ -61,13 +64,12 @@ SkString GrGLSLFPFragmentBuilder::writeProcessorFunction(GrGLSLFragmentProcessor
break;
}
}
} // else the function keeps its two arguments
}
fp->emitCode(args);
SkString funcName = this->getMangledFunctionName(args.fFp.name());
this->emitFunction(kHalf4_GrSLType, funcName.c_str(), {params, paramCount},
this->code().c_str());
this->emitFunction(kHalf4_GrSLType, funcName.c_str(), paramSpan, this->code().c_str());
this->deleteStage();
this->onAfterChildProcEmitCode();
return funcName;

View File

@ -185,7 +185,8 @@ SkString GrGLSLProgramBuilder::emitFragProc(const GrFragmentProcessor& fp,
this->uniformHandler(),
this->shaderCaps(),
fp,
"_input",
fp.isBlendFunction() ? "_src" : "_input",
"_dst",
"_coords");
auto name = fFS.writeProcessorFunction(&glslFP, args);
fFS.codeAppendf("%s = %s(%s);", output.c_str(), name.c_str(), input.c_str());