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/effects/GrSkSLFP.h"
9
10 #include "include/effects/SkRuntimeEffect.h"
11 #include "include/private/GrContext_Base.h"
12 #include "include/private/SkSLString.h"
13 #include "src/core/SkRuntimeEffectPriv.h"
14 #include "src/core/SkVM.h"
15 #include "src/gpu/GrBaseContextPriv.h"
16 #include "src/gpu/GrColorInfo.h"
17 #include "src/gpu/GrTexture.h"
18 #include "src/gpu/KeyBuilder.h"
19 #include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
20 #include "src/gpu/glsl/GrGLSLProgramBuilder.h"
21 #include "src/sksl/SkSLUtil.h"
22 #include "src/sksl/codegen/SkSLPipelineStageCodeGenerator.h"
23 #include "src/sksl/ir/SkSLVarDeclarations.h"
24
25 class GrSkSLFP::Impl : public ProgramImpl {
26 public:
emitCode(EmitArgs & args)27 void emitCode(EmitArgs& args) override {
28 const GrSkSLFP& fp = args.fFp.cast<GrSkSLFP>();
29 const SkSL::Program& program = *fp.fEffect->fBaseProgram;
30
31 class FPCallbacks : public SkSL::PipelineStage::Callbacks {
32 public:
33 FPCallbacks(Impl* self,
34 EmitArgs& args,
35 const char* inputColor,
36 const SkSL::Context& context,
37 const uint8_t* uniformData,
38 const GrSkSLFP::UniformFlags* uniformFlags)
39 : fSelf(self)
40 , fArgs(args)
41 , fInputColor(inputColor)
42 , fContext(context)
43 , fUniformData(uniformData)
44 , fUniformFlags(uniformFlags) {}
45
46 std::string declareUniform(const SkSL::VarDeclaration* decl) override {
47 const SkSL::Variable& var = decl->var();
48 if (var.type().isOpaque()) {
49 // Nothing to do. The only opaque types we should see are children, and those
50 // are handled specially, above.
51 SkASSERT(var.type().isEffectChild());
52 return std::string(var.name());
53 }
54
55 const SkSL::Type* type = &var.type();
56 size_t sizeInBytes = type->slotCount() * sizeof(float);
57 const float* floatData = reinterpret_cast<const float*>(fUniformData);
58 const int* intData = reinterpret_cast<const int*>(fUniformData);
59 fUniformData += sizeInBytes;
60
61 bool isArray = false;
62 if (type->isArray()) {
63 type = &type->componentType();
64 isArray = true;
65 }
66
67 SkSLType gpuType;
68 SkAssertResult(SkSL::type_to_sksltype(fContext, *type, &gpuType));
69
70 if (*fUniformFlags++ & GrSkSLFP::kSpecialize_Flag) {
71 SkASSERTF(!isArray, "specializing array uniforms is not allowed");
72 std::string value = GrGLSLTypeString(gpuType);
73 value.append("(");
74
75 bool isFloat = SkSLTypeIsFloatType(gpuType);
76 size_t slots = type->slotCount();
77 for (size_t i = 0; i < slots; ++i) {
78 value.append(isFloat ? skstd::to_string(floatData[i])
79 : std::to_string(intData[i]));
80 value.append(",");
81 }
82 value.back() = ')';
83 return value;
84 }
85
86 const char* uniformName = nullptr;
87 auto handle =
88 fArgs.fUniformHandler->addUniformArray(&fArgs.fFp.cast<GrSkSLFP>(),
89 kFragment_GrShaderFlag,
90 gpuType,
91 SkString(var.name()).c_str(),
92 isArray ? var.type().columns() : 0,
93 &uniformName);
94 fSelf->fUniformHandles.push_back(handle);
95 return std::string(uniformName);
96 }
97
98 std::string getMangledName(const char* name) override {
99 return std::string(fArgs.fFragBuilder->getMangledFunctionName(name).c_str());
100 }
101
102 void defineFunction(const char* decl, const char* body, bool isMain) override {
103 if (isMain) {
104 fArgs.fFragBuilder->codeAppend(body);
105 } else {
106 fArgs.fFragBuilder->emitFunction(decl, body);
107 }
108 }
109
110 void declareFunction(const char* decl) override {
111 fArgs.fFragBuilder->emitFunctionPrototype(decl);
112 }
113
114 void defineStruct(const char* definition) override {
115 fArgs.fFragBuilder->definitionAppend(definition);
116 }
117
118 void declareGlobal(const char* declaration) override {
119 fArgs.fFragBuilder->definitionAppend(declaration);
120 }
121
122 std::string sampleShader(int index, std::string coords) override {
123 // If the child was sampled using the coords passed to main (and they are never
124 // modified), then we will have marked the child as PassThrough. The code generator
125 // doesn't know that, and still supplies coords. Inside invokeChild, we assert that
126 // any coords passed for a PassThrough child match args.fSampleCoords exactly.
127 //
128 // Normally, this is valid. Here, we *copied* the sample coords to a local variable
129 // (so that they're mutable in the runtime effect SkSL). Thus, the coords string we
130 // get here is the name of the local copy, and fSampleCoords still points to the
131 // unmodified original (which might be a varying, for example).
132 // To prevent the assert, we pass the empty string in this case. Note that for
133 // children sampled like this, invokeChild doesn't even use the coords parameter,
134 // except for that assert.
135 const GrFragmentProcessor* child = fArgs.fFp.childProcessor(index);
136 if (child && child->sampleUsage().isPassThrough()) {
137 coords.clear();
138 }
139 return std::string(fSelf->invokeChild(index, fInputColor, fArgs, coords).c_str());
140 }
141
142 std::string sampleColorFilter(int index, std::string color) override {
143 return std::string(fSelf->invokeChild(index,
144 color.empty() ? fInputColor : color.c_str(),
145 fArgs)
146 .c_str());
147 }
148
149 std::string sampleBlender(int index, std::string src, std::string dst) override {
150 if (!fSelf->childProcessor(index)) {
151 return SkSL::String::printf("blend_src_over(%s, %s)", src.c_str(), dst.c_str());
152 }
153 return std::string(
154 fSelf->invokeChild(index, src.c_str(), dst.c_str(), fArgs).c_str());
155 }
156
157 // These intrinsics take and return 3-component vectors, but child FPs operate on
158 // 4-component vectors. We use swizzles here to paper over the difference.
159 std::string toLinearSrgb(std::string color) override {
160 const GrSkSLFP& fp = fArgs.fFp.cast<GrSkSLFP>();
161 if (fp.fToLinearSrgbChildIndex < 0) {
162 return color;
163 }
164 color = SkSL::String::printf("(%s).rgb1", color.c_str());
165 SkString xformedColor = fSelf->invokeChild(
166 fp.fToLinearSrgbChildIndex, color.c_str(), fArgs);
167 return SkSL::String::printf("(%s).rgb", xformedColor.c_str());
168 }
169
170 std::string fromLinearSrgb(std::string color) override {
171 const GrSkSLFP& fp = fArgs.fFp.cast<GrSkSLFP>();
172 if (fp.fFromLinearSrgbChildIndex < 0) {
173 return color;
174 }
175 color = SkSL::String::printf("(%s).rgb1", color.c_str());
176 SkString xformedColor = fSelf->invokeChild(
177 fp.fFromLinearSrgbChildIndex, color.c_str(), fArgs);
178 return SkSL::String::printf("(%s).rgb", xformedColor.c_str());
179 }
180
181 Impl* fSelf;
182 EmitArgs& fArgs;
183 const char* fInputColor;
184 const SkSL::Context& fContext;
185 const uint8_t* fUniformData;
186 const GrSkSLFP::UniformFlags* fUniformFlags;
187 int fUniformIndex = 0;
188 };
189
190 // If we have an input child, we invoke it now, and make the result of that be the "input
191 // color" for all other purposes later (eg, the default passed via sample calls, etc.)
192 if (fp.fInputChildIndex >= 0) {
193 args.fFragBuilder->codeAppendf("%s = %s;\n",
194 args.fInputColor,
195 this->invokeChild(fp.fInputChildIndex, args).c_str());
196 }
197
198 if (fp.fEffect->allowBlender()) {
199 // If we have an dest-color child, we invoke it now, and make the result of that be the
200 // "dest color" for all other purposes later.
201 if (fp.fDestColorChildIndex >= 0) {
202 args.fFragBuilder->codeAppendf(
203 "%s = %s;\n",
204 args.fDestColor,
205 this->invokeChild(fp.fDestColorChildIndex, args.fDestColor, args).c_str());
206 }
207 } else {
208 // We're not making a blender, so we don't expect a dest-color child FP to exist.
209 SkASSERT(fp.fDestColorChildIndex < 0);
210 }
211
212 // Snap off a global copy of the input color at the start of main. We need this when
213 // we call child processors (particularly from helper functions, which can't "see" the
214 // parameter to main). Even from within main, if the code mutates the parameter, calls to
215 // sample should still be passing the original color (by default).
216 SkString inputColorName;
217 if (fp.fEffect->samplesOutsideMain()) {
218 GrShaderVar inputColorCopy(args.fFragBuilder->getMangledFunctionName("inColor"),
219 SkSLType::kHalf4);
220 args.fFragBuilder->declareGlobal(inputColorCopy);
221 inputColorName = inputColorCopy.getName();
222 args.fFragBuilder->codeAppendf("%s = %s;\n", inputColorName.c_str(), args.fInputColor);
223 } else {
224 inputColorName = args.fFragBuilder->newTmpVarName("inColor");
225 args.fFragBuilder->codeAppendf(
226 "half4 %s = %s;\n", inputColorName.c_str(), args.fInputColor);
227 }
228
229 // Copy the incoming coords to a local variable. Code in main might modify the coords
230 // parameter. fSampleCoord could be a varying, so writes to it would be illegal.
231 const char* coords = "float2(0)";
232 SkString coordsVarName;
233 if (fp.usesSampleCoordsDirectly()) {
234 coordsVarName = args.fFragBuilder->newTmpVarName("coords");
235 coords = coordsVarName.c_str();
236 args.fFragBuilder->codeAppendf("float2 %s = %s;\n", coords, args.fSampleCoord);
237 }
238
239 FPCallbacks callbacks(this,
240 args,
241 inputColorName.c_str(),
242 *program.fContext,
243 fp.uniformData(),
244 fp.uniformFlags());
245 SkSL::PipelineStage::ConvertProgram(
246 program, coords, args.fInputColor, args.fDestColor, &callbacks);
247 }
248
249 private:
onSetData(const GrGLSLProgramDataManager & pdman,const GrFragmentProcessor & _proc)250 void onSetData(const GrGLSLProgramDataManager& pdman,
251 const GrFragmentProcessor& _proc) override {
252 using Type = SkRuntimeEffect::Uniform::Type;
253 size_t uniIndex = 0;
254 const GrSkSLFP& outer = _proc.cast<GrSkSLFP>();
255 const uint8_t* uniformData = outer.uniformData();
256 const GrSkSLFP::UniformFlags* uniformFlags = outer.uniformFlags();
257 for (const auto& v : outer.fEffect->uniforms()) {
258 if (*uniformFlags++ & GrSkSLFP::kSpecialize_Flag) {
259 continue;
260 }
261 const UniformHandle handle = fUniformHandles[uniIndex++];
262 auto floatData = [=] { return SkTAddOffset<const float>(uniformData, v.offset); };
263 auto intData = [=] { return SkTAddOffset<const int>(uniformData, v.offset); };
264 switch (v.type) {
265 case Type::kFloat: pdman.set1fv(handle, v.count, floatData()); break;
266 case Type::kFloat2: pdman.set2fv(handle, v.count, floatData()); break;
267 case Type::kFloat3: pdman.set3fv(handle, v.count, floatData()); break;
268 case Type::kFloat4: pdman.set4fv(handle, v.count, floatData()); break;
269
270 case Type::kFloat2x2: pdman.setMatrix2fv(handle, v.count, floatData()); break;
271 case Type::kFloat3x3: pdman.setMatrix3fv(handle, v.count, floatData()); break;
272 case Type::kFloat4x4: pdman.setMatrix4fv(handle, v.count, floatData()); break;
273
274 case Type::kInt: pdman.set1iv(handle, v.count, intData()); break;
275 case Type::kInt2: pdman.set2iv(handle, v.count, intData()); break;
276 case Type::kInt3: pdman.set3iv(handle, v.count, intData()); break;
277 case Type::kInt4: pdman.set4iv(handle, v.count, intData()); break;
278
279 default:
280 SkDEBUGFAIL("Unsupported uniform type");
281 break;
282 }
283 }
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,sk_sp<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 sk_sp<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 uniformFlagSize = effect->uniforms().size() * sizeof(UniformFlags);
302 std::unique_ptr<GrSkSLFP> fp(new (uniformSize + uniformFlagSize)
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(std::move(dstColorSpace));
316 }
317 return fp;
318 }
319
GrSkSLFP(sk_sp<SkRuntimeEffect> effect,const char * name,OptFlags optFlags)320 GrSkSLFP::GrSkSLFP(sk_sp<SkRuntimeEffect> effect, const char* name, OptFlags optFlags)
321 : INHERITED(kGrSkSLFP_ClassID,
322 static_cast<OptimizationFlags>(optFlags) |
323 (effect->getFilterColorProgram()
324 ? kConstantOutputForConstantInput_OptimizationFlag
325 : kNone_OptimizationFlags))
326 , fEffect(std::move(effect))
327 , fName(name)
328 , fUniformSize(SkToU32(fEffect->uniformSize())) {
329 memset(this->uniformFlags(), 0, fEffect->uniforms().size() * sizeof(UniformFlags));
330 if (fEffect->usesSampleCoords()) {
331 this->setUsesSampleCoordsDirectly();
332 }
333 if (fEffect->allowBlender()) {
334 this->setIsBlendFunction();
335 }
336 }
337
GrSkSLFP(const GrSkSLFP & other)338 GrSkSLFP::GrSkSLFP(const GrSkSLFP& other)
339 : INHERITED(other)
340 , fEffect(other.fEffect)
341 , fName(other.fName)
342 , fUniformSize(other.fUniformSize)
343 , fInputChildIndex(other.fInputChildIndex)
344 , fDestColorChildIndex(other.fDestColorChildIndex)
345 , fToLinearSrgbChildIndex(other.fToLinearSrgbChildIndex)
346 , fFromLinearSrgbChildIndex(other.fFromLinearSrgbChildIndex) {
347 sk_careful_memcpy(this->uniformFlags(),
348 other.uniformFlags(),
349 fEffect->uniforms().size() * sizeof(UniformFlags));
350 sk_careful_memcpy(this->uniformData(), other.uniformData(), fUniformSize);
351 }
352
addChild(std::unique_ptr<GrFragmentProcessor> child,bool mergeOptFlags)353 void GrSkSLFP::addChild(std::unique_ptr<GrFragmentProcessor> child, bool mergeOptFlags) {
354 SkASSERTF(fInputChildIndex == -1, "all addChild calls must happen before setInput");
355 SkASSERTF(fDestColorChildIndex == -1, "all addChild calls must happen before setDestColorFP");
356 int childIndex = this->numChildProcessors();
357 SkASSERT((size_t)childIndex < fEffect->fSampleUsages.size());
358 if (mergeOptFlags) {
359 this->mergeOptimizationFlags(ProcessorOptimizationFlags(child.get()));
360 }
361 this->registerChild(std::move(child), fEffect->fSampleUsages[childIndex]);
362 }
363
setInput(std::unique_ptr<GrFragmentProcessor> input)364 void GrSkSLFP::setInput(std::unique_ptr<GrFragmentProcessor> input) {
365 SkASSERTF(fInputChildIndex == -1, "setInput should not be called more than once");
366 fInputChildIndex = this->numChildProcessors();
367 SkASSERT((size_t)fInputChildIndex >= fEffect->fSampleUsages.size());
368 this->mergeOptimizationFlags(ProcessorOptimizationFlags(input.get()));
369 this->registerChild(std::move(input), SkSL::SampleUsage::PassThrough());
370 }
371
setDestColorFP(std::unique_ptr<GrFragmentProcessor> destColorFP)372 void GrSkSLFP::setDestColorFP(std::unique_ptr<GrFragmentProcessor> destColorFP) {
373 SkASSERTF(fEffect->allowBlender(), "dest colors are only used by blend effects");
374 SkASSERTF(fDestColorChildIndex == -1, "setDestColorFP should not be called more than once");
375 fDestColorChildIndex = this->numChildProcessors();
376 SkASSERT((size_t)fDestColorChildIndex >= fEffect->fSampleUsages.size());
377 this->mergeOptimizationFlags(ProcessorOptimizationFlags(destColorFP.get()));
378 this->registerChild(std::move(destColorFP), SkSL::SampleUsage::PassThrough());
379 }
380
addColorTransformChildren(sk_sp<SkColorSpace> dstColorSpace)381 void GrSkSLFP::addColorTransformChildren(sk_sp<SkColorSpace> dstColorSpace) {
382 SkASSERTF(fToLinearSrgbChildIndex == -1 && fFromLinearSrgbChildIndex == -1,
383 "addColorTransformChildren should not be called more than once");
384
385 // We use child FPs for the color transforms. They're really just code snippets that get
386 // invoked, but each one injects a collection of uniforms and helper functions. Doing it
387 // this way leverages per-FP name mangling to avoid conflicts.
388 auto workingToLinear = GrColorSpaceXformEffect::Make(nullptr,
389 dstColorSpace.get(),
390 kUnpremul_SkAlphaType,
391 sk_srgb_linear_singleton(),
392 kUnpremul_SkAlphaType);
393 auto linearToWorking = GrColorSpaceXformEffect::Make(nullptr,
394 sk_srgb_linear_singleton(),
395 kUnpremul_SkAlphaType,
396 dstColorSpace.get(),
397 kUnpremul_SkAlphaType);
398
399 fToLinearSrgbChildIndex = this->numChildProcessors();
400 SkASSERT((size_t)fToLinearSrgbChildIndex >= fEffect->fSampleUsages.size());
401 this->registerChild(std::move(workingToLinear), SkSL::SampleUsage::PassThrough());
402
403 fFromLinearSrgbChildIndex = this->numChildProcessors();
404 SkASSERT((size_t)fFromLinearSrgbChildIndex >= fEffect->fSampleUsages.size());
405 this->registerChild(std::move(linearToWorking), SkSL::SampleUsage::PassThrough());
406 }
407
onMakeProgramImpl() const408 std::unique_ptr<GrFragmentProcessor::ProgramImpl> GrSkSLFP::onMakeProgramImpl() const {
409 return std::make_unique<Impl>();
410 }
411
onAddToKey(const GrShaderCaps & caps,skgpu::KeyBuilder * b) const412 void GrSkSLFP::onAddToKey(const GrShaderCaps& caps, skgpu::KeyBuilder* b) const {
413 // In the unlikely event of a hash collision, we also include the uniform size in the key.
414 // That ensures that we will (at worst) use the wrong program, but one that expects the same
415 // amount of uniform data.
416 b->add32(fEffect->hash());
417 b->add32(fUniformSize);
418
419 const UniformFlags* flags = this->uniformFlags();
420 const uint8_t* uniformData = this->uniformData();
421 size_t uniformCount = fEffect->uniforms().size();
422 auto iter = fEffect->uniforms().begin();
423
424 for (size_t i = 0; i < uniformCount; ++i, ++iter) {
425 bool specialize = flags[i] & kSpecialize_Flag;
426 b->addBool(specialize, "specialize");
427 if (specialize) {
428 b->addBytes(iter->sizeInBytes(), uniformData + iter->offset, iter->name.c_str());
429 }
430 }
431 }
432
onIsEqual(const GrFragmentProcessor & other) const433 bool GrSkSLFP::onIsEqual(const GrFragmentProcessor& other) const {
434 const GrSkSLFP& sk = other.cast<GrSkSLFP>();
435 const size_t uniformFlagSize = fEffect->uniforms().size() * sizeof(UniformFlags);
436 return fEffect->hash() == sk.fEffect->hash() &&
437 fEffect->uniforms().size() == sk.fEffect->uniforms().size() &&
438 fUniformSize == sk.fUniformSize &&
439 !sk_careful_memcmp(
440 this->uniformData(), sk.uniformData(), fUniformSize + uniformFlagSize);
441 }
442
clone() const443 std::unique_ptr<GrFragmentProcessor> GrSkSLFP::clone() const {
444 return std::unique_ptr<GrFragmentProcessor>(new (UniformPayloadSize(fEffect.get()))
445 GrSkSLFP(*this));
446 }
447
constantOutputForConstantInput(const SkPMColor4f & inputColor) const448 SkPMColor4f GrSkSLFP::constantOutputForConstantInput(const SkPMColor4f& inputColor) const {
449 const SkFilterColorProgram* program = fEffect->getFilterColorProgram();
450 SkASSERT(program);
451
452 auto evalChild = [&](int index, SkPMColor4f color) {
453 return ConstantOutputForConstantInput(this->childProcessor(index), color);
454 };
455
456 SkPMColor4f color = (fInputChildIndex >= 0)
457 ? ConstantOutputForConstantInput(
458 this->childProcessor(fInputChildIndex), inputColor)
459 : inputColor;
460 return program->eval(color, this->uniformData(), evalChild);
461 }
462
463 /**************************************************************************************************/
464
465 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrSkSLFP);
466
467 #if GR_TEST_UTILS
468
469 #include "include/effects/SkOverdrawColorFilter.h"
470 #include "src/core/SkColorFilterBase.h"
471
472 extern const char* SKSL_OVERDRAW_SRC;
473
TestCreate(GrProcessorTestData * d)474 std::unique_ptr<GrFragmentProcessor> GrSkSLFP::TestCreate(GrProcessorTestData* d) {
475 SkColor colors[SkOverdrawColorFilter::kNumColors];
476 for (SkColor& c : colors) {
477 c = d->fRandom->nextU();
478 }
479 auto filter = SkOverdrawColorFilter::MakeWithSkColors(colors);
480 auto [success, fp] = as_CFB(filter)->asFragmentProcessor(/*inputFP=*/nullptr, d->context(),
481 GrColorInfo{});
482 SkASSERT(success);
483 return std::move(fp);
484 }
485
486 #endif
487