xref: /aosp_15_r20/external/skia/src/gpu/ganesh/effects/GrSkSLFP.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2018 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "src/gpu/ganesh/effects/GrSkSLFP.h"
9 
10 #include "include/core/SkAlphaType.h"
11 #include "include/core/SkColor.h"
12 #include "include/core/SkData.h"
13 #include "include/core/SkString.h"
14 #include "include/core/SkSurfaceProps.h"
15 #include "include/effects/SkOverdrawColorFilter.h"
16 #include "include/effects/SkRuntimeEffect.h"
17 #include "include/private/SkSLSampleUsage.h"
18 #include "include/private/base/SkMalloc.h"
19 #include "include/private/base/SkTo.h"
20 #include "include/private/gpu/ganesh/GrTypesPriv.h"
21 #include "src/base/SkArenaAlloc.h"
22 #include "src/base/SkRandom.h"
23 #include "src/core/SkColorSpacePriv.h"
24 #include "src/core/SkRasterPipeline.h"
25 #include "src/core/SkRasterPipelineOpContexts.h"
26 #include "src/core/SkRasterPipelineOpList.h"
27 #include "src/core/SkRuntimeEffectPriv.h"
28 #include "src/core/SkSLTypeShared.h"
29 #include "src/gpu/KeyBuilder.h"
30 #include "src/gpu/ganesh/GrColorInfo.h"
31 #include "src/gpu/ganesh/GrColorSpaceXform.h"
32 #include "src/gpu/ganesh/GrFragmentProcessors.h"
33 #include "src/gpu/ganesh/GrShaderVar.h"
34 #include "src/gpu/ganesh/glsl/GrGLSLFragmentShaderBuilder.h"
35 #include "src/gpu/ganesh/glsl/GrGLSLUniformHandler.h"
36 #include "src/sksl/SkSLString.h"
37 #include "src/sksl/SkSLUtil.h"
38 #include "src/sksl/codegen/SkSLPipelineStageCodeGenerator.h"
39 #include "src/sksl/codegen/SkSLRasterPipelineBuilder.h"
40 #include "src/sksl/ir/SkSLProgram.h"
41 #include "src/sksl/ir/SkSLType.h"
42 #include "src/sksl/ir/SkSLVarDeclarations.h"
43 #include "src/sksl/ir/SkSLVariable.h"
44 
45 #include <algorithm>
46 
47 namespace SkSL { class Context; }
48 struct GrShaderCaps;
49 
50 class GrSkSLFP::Impl : public ProgramImpl {
51 public:
emitCode(EmitArgs & args)52     void emitCode(EmitArgs& args) override {
53         const GrSkSLFP& fp            = args.fFp.cast<GrSkSLFP>();
54         const SkSL::Program& program  = *fp.fEffect->fBaseProgram;
55 
56         class FPCallbacks : public SkSL::PipelineStage::Callbacks {
57         public:
58             FPCallbacks(Impl* self,
59                         EmitArgs& args,
60                         const char* inputColor,
61                         const SkSL::Context& context,
62                         const uint8_t* uniformData,
63                         const Specialized* specialized)
64                     : fSelf(self)
65                     , fArgs(args)
66                     , fInputColor(inputColor)
67                     , fContext(context)
68                     , fUniformData(uniformData)
69                     , fSpecialized(specialized) {}
70 
71             std::string declareUniform(const SkSL::VarDeclaration* decl) override {
72                 const SkSL::Variable* var = decl->var();
73                 if (var->type().isOpaque()) {
74                     // Nothing to do. The only opaque types we should see are children, and those
75                     // are handled specially.
76                     SkASSERT(var->type().isEffectChild());
77                     return std::string(var->name());
78                 }
79 
80                 const SkSL::Type* type = &var->type();
81                 size_t sizeInBytes = type->slotCount() * sizeof(float);
82                 const float* floatData = reinterpret_cast<const float*>(fUniformData);
83                 const int* intData = reinterpret_cast<const int*>(fUniformData);
84                 fUniformData += sizeInBytes;
85 
86                 bool isArray = false;
87                 if (type->isArray()) {
88                     type = &type->componentType();
89                     isArray = true;
90                 }
91 
92                 SkSLType gpuType;
93                 SkAssertResult(SkSL::type_to_sksltype(fContext, *type, &gpuType));
94 
95                 if (*fSpecialized++ == Specialized::kYes) {
96                     SkASSERTF(!isArray, "specializing array uniforms is not allowed");
97                     std::string value = SkSLTypeString(gpuType);
98                     value.append("(");
99 
100                     bool isFloat = SkSLTypeIsFloatType(gpuType);
101                     size_t slots = type->slotCount();
102                     for (size_t i = 0; i < slots; ++i) {
103                         value.append(isFloat ? skstd::to_string(floatData[i])
104                                              : std::to_string(intData[i]));
105                         value.append(",");
106                     }
107                     value.back() = ')';
108                     return value;
109                 }
110 
111                 const char* uniformName = nullptr;
112                 auto handle =
113                         fArgs.fUniformHandler->addUniformArray(&fArgs.fFp.cast<GrSkSLFP>(),
114                                                                kFragment_GrShaderFlag,
115                                                                gpuType,
116                                                                SkString(var->name()).c_str(),
117                                                                isArray ? var->type().columns() : 0,
118                                                                &uniformName);
119                 fSelf->fUniformHandles.push_back(handle);
120                 return std::string(uniformName);
121             }
122 
123             std::string getMangledName(const char* name) override {
124                 return std::string(fArgs.fFragBuilder->getMangledFunctionName(name).c_str());
125             }
126 
127             void defineFunction(const char* decl, const char* body, bool isMain) override {
128                 if (isMain) {
129                     fArgs.fFragBuilder->codeAppend(body);
130                 } else {
131                     fArgs.fFragBuilder->emitFunction(decl, body);
132                 }
133             }
134 
135             void declareFunction(const char* decl) override {
136                 fArgs.fFragBuilder->emitFunctionPrototype(decl);
137             }
138 
139             void defineStruct(const char* definition) override {
140                 fArgs.fFragBuilder->definitionAppend(definition);
141             }
142 
143             void declareGlobal(const char* declaration) override {
144                 fArgs.fFragBuilder->definitionAppend(declaration);
145             }
146 
147             std::string sampleShader(int index, std::string coords) override {
148                 // If the child was sampled using the coords passed to main (and they are never
149                 // modified), then we will have marked the child as PassThrough. The code generator
150                 // doesn't know that, and still supplies coords. Inside invokeChild, we assert that
151                 // any coords passed for a PassThrough child match args.fSampleCoords exactly.
152                 //
153                 // Normally, this is valid. Here, we *copied* the sample coords to a local variable
154                 // (so that they're mutable in the runtime effect SkSL). Thus, the coords string we
155                 // get here is the name of the local copy, and fSampleCoords still points to the
156                 // unmodified original (which might be a varying, for example).
157                 // To prevent the assert, we pass the empty string in this case. Note that for
158                 // children sampled like this, invokeChild doesn't even use the coords parameter,
159                 // except for that assert.
160                 const GrFragmentProcessor* child = fArgs.fFp.childProcessor(index);
161                 if (child && child->sampleUsage().isPassThrough()) {
162                     coords.clear();
163                 }
164                 return child ? std::string(fSelf->invokeChild(index, fInputColor, fArgs, coords)
165                                                    .c_str())
166                              : std::string("half4(0)");
167             }
168 
169             std::string sampleColorFilter(int index, std::string color) override {
170                 return std::string(fSelf->invokeChild(index,
171                                                       color.empty() ? fInputColor : color.c_str(),
172                                                       fArgs)
173                                            .c_str());
174             }
175 
176             std::string sampleBlender(int index, std::string src, std::string dst) override {
177                 if (!fSelf->childProcessor(index)) {
178                     return SkSL::String::printf("blend_src_over(%s, %s)", src.c_str(), dst.c_str());
179                 }
180                 return std::string(
181                         fSelf->invokeChild(index, src.c_str(), dst.c_str(), fArgs).c_str());
182             }
183 
184             // These intrinsics take and return 3-component vectors, but child FPs operate on
185             // 4-component vectors. We use swizzles here to paper over the difference.
186             std::string toLinearSrgb(std::string color) override {
187                 const GrSkSLFP& fp = fArgs.fFp.cast<GrSkSLFP>();
188                 if (fp.fToLinearSrgbChildIndex < 0) {
189                     return color;
190                 }
191                 color = SkSL::String::printf("(%s).rgb1", color.c_str());
192                 SkString xformedColor = fSelf->invokeChild(
193                         fp.fToLinearSrgbChildIndex, color.c_str(), fArgs);
194                 return SkSL::String::printf("(%s).rgb", xformedColor.c_str());
195             }
196 
197             std::string fromLinearSrgb(std::string color) override {
198                 const GrSkSLFP& fp = fArgs.fFp.cast<GrSkSLFP>();
199                 if (fp.fFromLinearSrgbChildIndex < 0) {
200                     return color;
201                 }
202                 color = SkSL::String::printf("(%s).rgb1", color.c_str());
203                 SkString xformedColor = fSelf->invokeChild(
204                         fp.fFromLinearSrgbChildIndex, color.c_str(), fArgs);
205                 return SkSL::String::printf("(%s).rgb", xformedColor.c_str());
206             }
207 
208             Impl*                         fSelf;
209             EmitArgs&                     fArgs;
210             const char*                   fInputColor;
211             const SkSL::Context&          fContext;
212             const uint8_t*                fUniformData;
213             const Specialized*            fSpecialized;
214             int                           fUniformIndex = 0;
215         };
216 
217         // If we have an input child, we invoke it now, and make the result of that be the "input
218         // color" for all other purposes later (eg, the default passed via sample calls, etc.)
219         if (fp.fInputChildIndex >= 0) {
220             args.fFragBuilder->codeAppendf("%s = %s;\n",
221                                            args.fInputColor,
222                                            this->invokeChild(fp.fInputChildIndex, args).c_str());
223         }
224 
225         if (fp.fEffect->allowBlender()) {
226             // If we have an dest-color child, we invoke it now, and make the result of that be the
227             // "dest color" for all other purposes later.
228             if (fp.fDestColorChildIndex >= 0) {
229                 args.fFragBuilder->codeAppendf(
230                         "%s = %s;\n",
231                         args.fDestColor,
232                         this->invokeChild(fp.fDestColorChildIndex, args.fDestColor, args).c_str());
233             }
234         } else {
235             // We're not making a blender, so we don't expect a dest-color child FP to exist.
236             SkASSERT(fp.fDestColorChildIndex < 0);
237         }
238 
239         // Snap off a global copy of the input color at the start of main. We need this when
240         // we call child processors (particularly from helper functions, which can't "see" the
241         // parameter to main). Even from within main, if the code mutates the parameter, calls to
242         // sample should still be passing the original color (by default).
243         SkString inputColorName;
244         if (fp.fEffect->samplesOutsideMain()) {
245             GrShaderVar inputColorCopy(args.fFragBuilder->getMangledFunctionName("inColor"),
246                                        SkSLType::kHalf4);
247             args.fFragBuilder->declareGlobal(inputColorCopy);
248             inputColorName = inputColorCopy.getName();
249             args.fFragBuilder->codeAppendf("%s = %s;\n", inputColorName.c_str(), args.fInputColor);
250         } else {
251             inputColorName = args.fFragBuilder->newTmpVarName("inColor");
252             args.fFragBuilder->codeAppendf(
253                     "half4 %s = %s;\n", inputColorName.c_str(), args.fInputColor);
254         }
255 
256         // Copy the incoming coords to a local variable. Code in main might modify the coords
257         // parameter. fSampleCoord could be a varying, so writes to it would be illegal.
258         const char* coords = "float2(0)";
259         SkString coordsVarName;
260         if (fp.usesSampleCoordsDirectly()) {
261             coordsVarName = args.fFragBuilder->newTmpVarName("coords");
262             coords = coordsVarName.c_str();
263             args.fFragBuilder->codeAppendf("float2 %s = %s;\n", coords, args.fSampleCoord);
264         }
265 
266         FPCallbacks callbacks(this,
267                               args,
268                               inputColorName.c_str(),
269                               *program.fContext,
270                               fp.uniformData(),
271                               fp.specialized());
272         SkSL::PipelineStage::ConvertProgram(
273                 program, coords, args.fInputColor, args.fDestColor, &callbacks);
274     }
275 
276 private:
onSetData(const GrGLSLProgramDataManager & pdman,const GrFragmentProcessor & _proc)277     void onSetData(const GrGLSLProgramDataManager& pdman,
278                    const GrFragmentProcessor& _proc) override {
279         const GrSkSLFP& outer = _proc.cast<GrSkSLFP>();
280         pdman.setRuntimeEffectUniforms(outer.fEffect->uniforms(),
281                                        SkSpan(fUniformHandles),
282                                        SkSpan(outer.specialized(), outer.uniformCount()),
283                                        outer.uniformData());
284     }
285 
286     std::vector<UniformHandle> fUniformHandles;
287 };
288 
MakeWithData(sk_sp<SkRuntimeEffect> effect,const char * name,sk_sp<SkColorSpace> dstColorSpace,std::unique_ptr<GrFragmentProcessor> inputFP,std::unique_ptr<GrFragmentProcessor> destColorFP,const sk_sp<const SkData> & uniforms,SkSpan<std::unique_ptr<GrFragmentProcessor>> childFPs)289 std::unique_ptr<GrSkSLFP> GrSkSLFP::MakeWithData(
290         sk_sp<SkRuntimeEffect> effect,
291         const char* name,
292         sk_sp<SkColorSpace> dstColorSpace,
293         std::unique_ptr<GrFragmentProcessor> inputFP,
294         std::unique_ptr<GrFragmentProcessor> destColorFP,
295         const sk_sp<const SkData>& uniforms,
296         SkSpan<std::unique_ptr<GrFragmentProcessor>> childFPs) {
297     if (uniforms->size() != effect->uniformSize()) {
298         return nullptr;
299     }
300     size_t uniformSize = uniforms->size();
301     size_t specializedSize = effect->uniforms().size() * sizeof(Specialized);
302     std::unique_ptr<GrSkSLFP> fp(new (uniformSize + specializedSize)
303                                          GrSkSLFP(std::move(effect), name, OptFlags::kNone));
304     sk_careful_memcpy(fp->uniformData(), uniforms->data(), uniformSize);
305     for (auto& childFP : childFPs) {
306         fp->addChild(std::move(childFP), /*mergeOptFlags=*/true);
307     }
308     if (inputFP) {
309         fp->setInput(std::move(inputFP));
310     }
311     if (destColorFP) {
312         fp->setDestColorFP(std::move(destColorFP));
313     }
314     if (fp->fEffect->usesColorTransform() && dstColorSpace) {
315         fp->addColorTransformChildren(dstColorSpace.get());
316     }
317     return fp;
318 }
319 
DetermineOptimizationFlags(OptFlags of,SkRuntimeEffect * effect)320 GrFragmentProcessor::OptimizationFlags GrSkSLFP::DetermineOptimizationFlags(
321         OptFlags of, SkRuntimeEffect* effect) {
322     OptimizationFlags optFlags = static_cast<OptimizationFlags>(of);
323     if (SkRuntimeEffectPriv::SupportsConstantOutputForConstantInput(effect)) {
324         optFlags |= kConstantOutputForConstantInput_OptimizationFlag;
325     }
326     return optFlags;
327 }
328 
GrSkSLFP(sk_sp<SkRuntimeEffect> effect,const char * name,OptFlags optFlags)329 GrSkSLFP::GrSkSLFP(sk_sp<SkRuntimeEffect> effect, const char* name, OptFlags optFlags)
330         : INHERITED(kGrSkSLFP_ClassID, DetermineOptimizationFlags(optFlags, effect.get()))
331         , fEffect(std::move(effect))
332         , fName(name)
333         , fUniformSize(SkToU32(fEffect->uniformSize())) {
334     std::fill_n(this->specialized(), this->uniformCount(), Specialized::kNo);
335     if (fEffect->usesSampleCoords()) {
336         this->setUsesSampleCoordsDirectly();
337     }
338     if (fEffect->allowBlender()) {
339         this->setIsBlendFunction();
340     }
341 }
342 
GrSkSLFP(const GrSkSLFP & other)343 GrSkSLFP::GrSkSLFP(const GrSkSLFP& other)
344         : INHERITED(other)
345         , fEffect(other.fEffect)
346         , fName(other.fName)
347         , fUniformSize(other.fUniformSize)
348         , fInputChildIndex(other.fInputChildIndex)
349         , fDestColorChildIndex(other.fDestColorChildIndex)
350         , fToLinearSrgbChildIndex(other.fToLinearSrgbChildIndex)
351         , fFromLinearSrgbChildIndex(other.fFromLinearSrgbChildIndex) {
352     std::copy_n(other.specialized(), this->uniformCount(), this->specialized());
353     sk_careful_memcpy(this->uniformData(), other.uniformData(), fUniformSize);
354 }
355 
addChild(std::unique_ptr<GrFragmentProcessor> child,bool mergeOptFlags)356 void GrSkSLFP::addChild(std::unique_ptr<GrFragmentProcessor> child, bool mergeOptFlags) {
357     SkASSERTF(fInputChildIndex == -1, "all addChild calls must happen before setInput");
358     SkASSERTF(fDestColorChildIndex == -1, "all addChild calls must happen before setDestColorFP");
359     int childIndex = this->numChildProcessors();
360     SkASSERT((size_t)childIndex < fEffect->fSampleUsages.size());
361     if (mergeOptFlags) {
362         this->mergeOptimizationFlags(ProcessorOptimizationFlags(child.get()));
363     }
364     this->clearConstantOutputForConstantInputFlag();
365     this->registerChild(std::move(child), fEffect->fSampleUsages[childIndex]);
366 }
367 
setInput(std::unique_ptr<GrFragmentProcessor> input)368 void GrSkSLFP::setInput(std::unique_ptr<GrFragmentProcessor> input) {
369     SkASSERTF(fInputChildIndex == -1, "setInput should not be called more than once");
370     fInputChildIndex = this->numChildProcessors();
371     SkASSERT((size_t)fInputChildIndex >= fEffect->fSampleUsages.size());
372     this->mergeOptimizationFlags(ProcessorOptimizationFlags(input.get()));
373     this->registerChild(std::move(input), SkSL::SampleUsage::PassThrough());
374 }
375 
setDestColorFP(std::unique_ptr<GrFragmentProcessor> destColorFP)376 void GrSkSLFP::setDestColorFP(std::unique_ptr<GrFragmentProcessor> destColorFP) {
377     SkASSERTF(fEffect->allowBlender(), "dest colors are only used by blend effects");
378     SkASSERTF(fDestColorChildIndex == -1, "setDestColorFP should not be called more than once");
379     fDestColorChildIndex = this->numChildProcessors();
380     SkASSERT((size_t)fDestColorChildIndex >= fEffect->fSampleUsages.size());
381     this->mergeOptimizationFlags(ProcessorOptimizationFlags(destColorFP.get()));
382     this->registerChild(std::move(destColorFP), SkSL::SampleUsage::PassThrough());
383 }
384 
addColorTransformChildren(SkColorSpace * dstColorSpace)385 void GrSkSLFP::addColorTransformChildren(SkColorSpace* dstColorSpace) {
386     SkASSERTF(fToLinearSrgbChildIndex == -1 && fFromLinearSrgbChildIndex == -1,
387               "addColorTransformChildren should not be called more than once");
388 
389     // We use child FPs for the color transforms. They're really just code snippets that get
390     // invoked, but each one injects a collection of uniforms and helper functions. Doing it
391     // this way leverages per-FP name mangling to avoid conflicts.
392     auto workingToLinear = GrColorSpaceXformEffect::Make(nullptr,
393                                                          dstColorSpace,
394                                                          kUnpremul_SkAlphaType,
395                                                          sk_srgb_linear_singleton(),
396                                                          kUnpremul_SkAlphaType);
397     auto linearToWorking = GrColorSpaceXformEffect::Make(nullptr,
398                                                          sk_srgb_linear_singleton(),
399                                                          kUnpremul_SkAlphaType,
400                                                          dstColorSpace,
401                                                          kUnpremul_SkAlphaType);
402 
403     fToLinearSrgbChildIndex = this->numChildProcessors();
404     SkASSERT((size_t)fToLinearSrgbChildIndex >= fEffect->fSampleUsages.size());
405     this->registerChild(std::move(workingToLinear), SkSL::SampleUsage::PassThrough());
406 
407     fFromLinearSrgbChildIndex = this->numChildProcessors();
408     SkASSERT((size_t)fFromLinearSrgbChildIndex >= fEffect->fSampleUsages.size());
409     this->registerChild(std::move(linearToWorking), SkSL::SampleUsage::PassThrough());
410 }
411 
onMakeProgramImpl() const412 std::unique_ptr<GrFragmentProcessor::ProgramImpl> GrSkSLFP::onMakeProgramImpl() const {
413     return std::make_unique<Impl>();
414 }
415 
onAddToKey(const GrShaderCaps & caps,skgpu::KeyBuilder * b) const416 void GrSkSLFP::onAddToKey(const GrShaderCaps& caps, skgpu::KeyBuilder* b) const {
417     // In the unlikely event of a hash collision, we also include the uniform size in the key.
418     // That ensures that we will (at worst) use the wrong program, but one that expects the same
419     // amount of uniform data.
420     b->add32(fEffect->hash());
421     b->add32(fUniformSize);
422 
423     const Specialized* specialized = this->specialized();
424     const uint8_t* uniformData = this->uniformData();
425     size_t uniformCount = this->uniformCount();
426     auto iter = fEffect->uniforms().begin();
427 
428     for (size_t i = 0; i < uniformCount; ++i, ++iter) {
429         bool specialize = specialized[i] == Specialized::kYes;
430         b->addBool(specialize, "specialize");
431         if (specialize) {
432             b->addBytes(iter->sizeInBytes(), uniformData + iter->offset, iter->name);
433         }
434     }
435 }
436 
onIsEqual(const GrFragmentProcessor & other) const437 bool GrSkSLFP::onIsEqual(const GrFragmentProcessor& other) const {
438     const GrSkSLFP& sk = other.cast<GrSkSLFP>();
439     const size_t specializedSize = this->uniformCount() * sizeof(Specialized);
440     return fEffect->hash() == sk.fEffect->hash() &&
441            this->uniformCount() == sk.uniformCount() &&
442            fUniformSize == sk.fUniformSize &&
443            !sk_careful_memcmp(this->uniformData(),
444                               sk.uniformData(),
445                               fUniformSize + specializedSize);
446 }
447 
clone() const448 std::unique_ptr<GrFragmentProcessor> GrSkSLFP::clone() const {
449     return std::unique_ptr<GrFragmentProcessor>(new (UniformPayloadSize(fEffect.get()))
450                                                         GrSkSLFP(*this));
451 }
452 
constantOutputForConstantInput(const SkPMColor4f & inputColor) const453 SkPMColor4f GrSkSLFP::constantOutputForConstantInput(const SkPMColor4f& inputColor) const {
454     SkPMColor4f color = (fInputChildIndex >= 0)
455             ? ConstantOutputForConstantInput(this->childProcessor(fInputChildIndex), inputColor)
456             : inputColor;
457 
458     class ConstantOutputForConstantInput_SkRPCallbacks : public SkSL::RP::Callbacks {
459     public:
460         bool appendShader(int index) override {
461            SkDEBUGFAIL("constant-output-for-constant-input unsupported when child shaders present");
462            return false;
463         }
464         bool appendColorFilter(int index) override {
465            SkDEBUGFAIL("constant-output-for-constant-input unsupported when child shaders present");
466            return false;
467         }
468         bool appendBlender(int index) override {
469            SkDEBUGFAIL("constant-output-for-constant-input unsupported when child shaders present");
470            return false;
471         }
472         void toLinearSrgb(const void* color) override { /* identity color conversion */ }
473         void fromLinearSrgb(const void* color) override { /* identity color conversion */ }
474     };
475 
476     if (const SkSL::RP::Program* program = fEffect->getRPProgram(/*debugTrace=*/nullptr)) {
477         // No color conversion is happening here, so we can use untransformed uniforms.
478         SkSpan<const float> uniforms{reinterpret_cast<const float*>(this->uniformData()),
479                                      fUniformSize / sizeof(float)};
480         SkSTArenaAlloc<2048> alloc;  // sufficient for a tiny SkSL program
481         SkRasterPipeline pipeline(&alloc);
482         pipeline.appendConstantColor(&alloc, color.vec());
483         ConstantOutputForConstantInput_SkRPCallbacks callbacks;
484         if (program->appendStages(&pipeline, &alloc, &callbacks, uniforms)) {
485             SkPMColor4f outputColor;
486             SkRasterPipeline_MemoryCtx outputCtx = {&outputColor, 0};
487             pipeline.append(SkRasterPipelineOp::store_f32, &outputCtx);
488             pipeline.run(0, 0, 1, 1);
489             return outputColor;
490         }
491     }
492 
493     // We weren't able to run the Raster Pipeline program.
494     return color;
495 }
496 
497 /**************************************************************************************************/
498 
GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrSkSLFP)499 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrSkSLFP)
500 
501 #if defined(GPU_TEST_UTILS)
502 
503 std::unique_ptr<GrFragmentProcessor> GrSkSLFP::TestCreate(GrProcessorTestData* d) {
504     SkColor colors[SkOverdrawColorFilter::kNumColors];
505     for (SkColor& c : colors) {
506         c = d->fRandom->nextU();
507     }
508     auto filter = SkOverdrawColorFilter::MakeWithSkColors(colors);
509     SkSurfaceProps props; // default props for testing
510     auto [success, fp] = GrFragmentProcessors::Make(
511             d->context(), filter.get(), /*inputFP=*/nullptr, GrColorInfo{}, props);
512     SkASSERT(success);
513     return std::move(fp);
514 }
515 
516 #endif
517