2 * Copyright 2018 Google Inc.
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
8 #include "src/gpu/ganesh/effects/GrSkSLFP.h"
10 #include "include/effects/SkRuntimeEffect.h"
11 #include "include/private/SkSLString.h"
12 #include "include/private/gpu/ganesh/GrContext_Base.h"
13 #include "src/core/SkColorSpacePriv.h"
14 #include "src/core/SkRuntimeEffectPriv.h"
15 #include "src/core/SkVM.h"
16 #include "src/gpu/KeyBuilder.h"
17 #include "src/gpu/ganesh/GrBaseContextPriv.h"
18 #include "src/gpu/ganesh/GrColorInfo.h"
19 #include "src/gpu/ganesh/GrTexture.h"
20 #include "src/gpu/ganesh/glsl/GrGLSLFragmentShaderBuilder.h"
21 #include "src/gpu/ganesh/glsl/GrGLSLProgramBuilder.h"
22 #include "src/sksl/SkSLUtil.h"
23 #include "src/sksl/codegen/SkSLPipelineStageCodeGenerator.h"
24 #include "src/sksl/ir/SkSLVarDeclarations.h"
26 class GrSkSLFP::Impl : public ProgramImpl {
28 void emitCode(EmitArgs& args) override {
29 const GrSkSLFP& fp = args.fFp.cast<GrSkSLFP>();
30 const SkSL::Program& program = *fp.fEffect->fBaseProgram;
32 class FPCallbacks : public SkSL::PipelineStage::Callbacks {
34 FPCallbacks(Impl* self,
36 const char* inputColor,
37 const SkSL::Context& context,
38 const uint8_t* uniformData,
39 const GrSkSLFP::UniformFlags* uniformFlags)
42 , fInputColor(inputColor)
44 , fUniformData(uniformData)
45 , fUniformFlags(uniformFlags) {}
47 std::string declareUniform(const SkSL::VarDeclaration* decl) override {
48 const SkSL::Variable& var = decl->var();
49 if (var.type().isOpaque()) {
50 // Nothing to do. The only opaque types we should see are children, and those
51 // are handled specially, above.
52 SkASSERT(var.type().isEffectChild());
53 return std::string(var.name());
56 const SkSL::Type* type = &var.type();
57 size_t sizeInBytes = type->slotCount() * sizeof(float);
58 const float* floatData = reinterpret_cast<const float*>(fUniformData);
59 const int* intData = reinterpret_cast<const int*>(fUniformData);
60 fUniformData += sizeInBytes;
63 if (type->isArray()) {
64 type = &type->componentType();
69 SkAssertResult(SkSL::type_to_sksltype(fContext, *type, &gpuType));
71 if (*fUniformFlags++ & GrSkSLFP::kSpecialize_Flag) {
72 SkASSERTF(!isArray, "specializing array uniforms is not allowed");
73 std::string value = GrGLSLTypeString(gpuType);
76 bool isFloat = SkSLTypeIsFloatType(gpuType);
77 size_t slots = type->slotCount();
78 for (size_t i = 0; i < slots; ++i) {
79 value.append(isFloat ? skstd::to_string(floatData[i])
80 : std::to_string(intData[i]));
87 const char* uniformName = nullptr;
89 fArgs.fUniformHandler->addUniformArray(&fArgs.fFp.cast<GrSkSLFP>(),
90 kFragment_GrShaderFlag,
92 SkString(var.name()).c_str(),
93 isArray ? var.type().columns() : 0,
95 fSelf->fUniformHandles.push_back(handle);
96 return std::string(uniformName);
99 std::string getMangledName(const char* name) override {
100 return std::string(fArgs.fFragBuilder->getMangledFunctionName(name).c_str());
103 void defineFunction(const char* decl, const char* body, bool isMain) override {
105 fArgs.fFragBuilder->codeAppend(body);
107 fArgs.fFragBuilder->emitFunction(decl, body);
111 void declareFunction(const char* decl) override {
112 fArgs.fFragBuilder->emitFunctionPrototype(decl);
115 void defineStruct(const char* definition) override {
116 fArgs.fFragBuilder->definitionAppend(definition);
119 void declareGlobal(const char* declaration) override {
120 fArgs.fFragBuilder->definitionAppend(declaration);
123 std::string sampleShader(int index, std::string coords) override {
124 // If the child was sampled using the coords passed to main (and they are never
125 // modified), then we will have marked the child as PassThrough. The code generator
126 // doesn't know that, and still supplies coords. Inside invokeChild, we assert that
127 // any coords passed for a PassThrough child match args.fSampleCoords exactly.
129 // Normally, this is valid. Here, we *copied* the sample coords to a local variable
130 // (so that they're mutable in the runtime effect SkSL). Thus, the coords string we
131 // get here is the name of the local copy, and fSampleCoords still points to the
132 // unmodified original (which might be a varying, for example).
133 // To prevent the assert, we pass the empty string in this case. Note that for
134 // children sampled like this, invokeChild doesn't even use the coords parameter,
135 // except for that assert.
136 const GrFragmentProcessor* child = fArgs.fFp.childProcessor(index);
137 if (child && child->sampleUsage().isPassThrough()) {
140 return std::string(fSelf->invokeChild(index, fInputColor, fArgs, coords).c_str());
143 std::string sampleColorFilter(int index, std::string color) override {
144 return std::string(fSelf->invokeChild(index,
145 color.empty() ? fInputColor : color.c_str(),
150 std::string sampleBlender(int index, std::string src, std::string dst) override {
151 if (!fSelf->childProcessor(index)) {
152 return SkSL::String::printf("blend_src_over(%s, %s)", src.c_str(), dst.c_str());
155 fSelf->invokeChild(index, src.c_str(), dst.c_str(), fArgs).c_str());
158 // These intrinsics take and return 3-component vectors, but child FPs operate on
159 // 4-component vectors. We use swizzles here to paper over the difference.
160 std::string toLinearSrgb(std::string color) override {
161 const GrSkSLFP& fp = fArgs.fFp.cast<GrSkSLFP>();
162 if (fp.fToLinearSrgbChildIndex < 0) {
165 color = SkSL::String::printf("(%s).rgb1", color.c_str());
166 SkString xformedColor = fSelf->invokeChild(
167 fp.fToLinearSrgbChildIndex, color.c_str(), fArgs);
168 return SkSL::String::printf("(%s).rgb", xformedColor.c_str());
171 std::string fromLinearSrgb(std::string color) override {
172 const GrSkSLFP& fp = fArgs.fFp.cast<GrSkSLFP>();
173 if (fp.fFromLinearSrgbChildIndex < 0) {
176 color = SkSL::String::printf("(%s).rgb1", color.c_str());
177 SkString xformedColor = fSelf->invokeChild(
178 fp.fFromLinearSrgbChildIndex, color.c_str(), fArgs);
179 return SkSL::String::printf("(%s).rgb", xformedColor.c_str());
184 const char* fInputColor;
185 const SkSL::Context& fContext;
186 const uint8_t* fUniformData;
187 const GrSkSLFP::UniformFlags* fUniformFlags;
188 int fUniformIndex = 0;
191 // If we have an input child, we invoke it now, and make the result of that be the "input
192 // color" for all other purposes later (eg, the default passed via sample calls, etc.)
193 if (fp.fInputChildIndex >= 0) {
194 args.fFragBuilder->codeAppendf("%s = %s;\n",
196 this->invokeChild(fp.fInputChildIndex, args).c_str());
199 if (fp.fEffect->allowBlender()) {
200 // If we have an dest-color child, we invoke it now, and make the result of that be the
201 // "dest color" for all other purposes later.
202 if (fp.fDestColorChildIndex >= 0) {
203 args.fFragBuilder->codeAppendf(
206 this->invokeChild(fp.fDestColorChildIndex, args.fDestColor, args).c_str());
209 // We're not making a blender, so we don't expect a dest-color child FP to exist.
210 SkASSERT(fp.fDestColorChildIndex < 0);
213 // Snap off a global copy of the input color at the start of main. We need this when
214 // we call child processors (particularly from helper functions, which can't "see" the
215 // parameter to main). Even from within main, if the code mutates the parameter, calls to
216 // sample should still be passing the original color (by default).
217 SkString inputColorName;
218 if (fp.fEffect->samplesOutsideMain()) {
219 GrShaderVar inputColorCopy(args.fFragBuilder->getMangledFunctionName("inColor"),
221 args.fFragBuilder->declareGlobal(inputColorCopy);
222 inputColorName = inputColorCopy.getName();
223 args.fFragBuilder->codeAppendf("%s = %s;\n", inputColorName.c_str(), args.fInputColor);
225 inputColorName = args.fFragBuilder->newTmpVarName("inColor");
226 args.fFragBuilder->codeAppendf(
227 "half4 %s = %s;\n", inputColorName.c_str(), args.fInputColor);
230 // Copy the incoming coords to a local variable. Code in main might modify the coords
231 // parameter. fSampleCoord could be a varying, so writes to it would be illegal.
232 const char* coords = "float2(0)";
233 SkString coordsVarName;
234 if (fp.usesSampleCoordsDirectly()) {
235 coordsVarName = args.fFragBuilder->newTmpVarName("coords");
236 coords = coordsVarName.c_str();
237 args.fFragBuilder->codeAppendf("float2 %s = %s;\n", coords, args.fSampleCoord);
240 FPCallbacks callbacks(this,
242 inputColorName.c_str(),
246 SkSL::PipelineStage::ConvertProgram(
247 program, coords, args.fInputColor, args.fDestColor, &callbacks);
251 void onSetData(const GrGLSLProgramDataManager& pdman,
252 const GrFragmentProcessor& _proc) override {
253 using Type = SkRuntimeEffect::Uniform::Type;
255 const GrSkSLFP& outer = _proc.cast<GrSkSLFP>();
256 const uint8_t* uniformData = outer.uniformData();
257 const GrSkSLFP::UniformFlags* uniformFlags = outer.uniformFlags();
258 for (const auto& v : outer.fEffect->uniforms()) {
259 if (*uniformFlags++ & GrSkSLFP::kSpecialize_Flag) {
262 const UniformHandle handle = fUniformHandles[uniIndex++];
263 auto floatData = [=] { return SkTAddOffset<const float>(uniformData, v.offset); };
264 auto intData = [=] { return SkTAddOffset<const int>(uniformData, v.offset); };
266 case Type::kFloat: pdman.set1fv(handle, v.count, floatData()); break;
267 case Type::kFloat2: pdman.set2fv(handle, v.count, floatData()); break;
268 case Type::kFloat3: pdman.set3fv(handle, v.count, floatData()); break;
269 case Type::kFloat4: pdman.set4fv(handle, v.count, floatData()); break;
271 case Type::kFloat2x2: pdman.setMatrix2fv(handle, v.count, floatData()); break;
272 case Type::kFloat3x3: pdman.setMatrix3fv(handle, v.count, floatData()); break;
273 case Type::kFloat4x4: pdman.setMatrix4fv(handle, v.count, floatData()); break;
275 case Type::kInt: pdman.set1iv(handle, v.count, intData()); break;
276 case Type::kInt2: pdman.set2iv(handle, v.count, intData()); break;
277 case Type::kInt3: pdman.set3iv(handle, v.count, intData()); break;
278 case Type::kInt4: pdman.set4iv(handle, v.count, intData()); break;
281 SkDEBUGFAIL("Unsupported uniform type");
287 std::vector<UniformHandle> fUniformHandles;
290 std::unique_ptr<GrSkSLFP> GrSkSLFP::MakeWithData(
291 sk_sp<SkRuntimeEffect> effect,
293 sk_sp<SkColorSpace> dstColorSpace,
294 std::unique_ptr<GrFragmentProcessor> inputFP,
295 std::unique_ptr<GrFragmentProcessor> destColorFP,
296 sk_sp<SkData> uniforms,
297 SkSpan<std::unique_ptr<GrFragmentProcessor>> childFPs) {
298 if (uniforms->size() != effect->uniformSize()) {
301 size_t uniformSize = uniforms->size();
302 size_t uniformFlagSize = effect->uniforms().size() * sizeof(UniformFlags);
303 std::unique_ptr<GrSkSLFP> fp(new (uniformSize + uniformFlagSize)
304 GrSkSLFP(std::move(effect), name, OptFlags::kNone));
305 sk_careful_memcpy(fp->uniformData(), uniforms->data(), uniformSize);
306 for (auto& childFP : childFPs) {
307 fp->addChild(std::move(childFP), /*mergeOptFlags=*/true);
310 fp->setInput(std::move(inputFP));
313 fp->setDestColorFP(std::move(destColorFP));
315 if (fp->fEffect->usesColorTransform() && dstColorSpace) {
316 fp->addColorTransformChildren(std::move(dstColorSpace));
321 GrSkSLFP::GrSkSLFP(sk_sp<SkRuntimeEffect> effect, const char* name, OptFlags optFlags)
322 : INHERITED(kGrSkSLFP_ClassID,
323 static_cast<OptimizationFlags>(optFlags) |
324 (effect->getFilterColorProgram()
325 ? kConstantOutputForConstantInput_OptimizationFlag
326 : kNone_OptimizationFlags))
327 , fEffect(std::move(effect))
329 , fUniformSize(SkToU32(fEffect->uniformSize())) {
330 memset(this->uniformFlags(), 0, fEffect->uniforms().size() * sizeof(UniformFlags));
331 if (fEffect->usesSampleCoords()) {
332 this->setUsesSampleCoordsDirectly();
334 if (fEffect->allowBlender()) {
335 this->setIsBlendFunction();
339 GrSkSLFP::GrSkSLFP(const GrSkSLFP& other)
341 , fEffect(other.fEffect)
343 , fUniformSize(other.fUniformSize)
344 , fInputChildIndex(other.fInputChildIndex)
345 , fDestColorChildIndex(other.fDestColorChildIndex)
346 , fToLinearSrgbChildIndex(other.fToLinearSrgbChildIndex)
347 , fFromLinearSrgbChildIndex(other.fFromLinearSrgbChildIndex) {
348 sk_careful_memcpy(this->uniformFlags(),
349 other.uniformFlags(),
350 fEffect->uniforms().size() * sizeof(UniformFlags));
351 sk_careful_memcpy(this->uniformData(), other.uniformData(), fUniformSize);
354 void GrSkSLFP::addChild(std::unique_ptr<GrFragmentProcessor> child, bool mergeOptFlags) {
355 SkASSERTF(fInputChildIndex == -1, "all addChild calls must happen before setInput");
356 SkASSERTF(fDestColorChildIndex == -1, "all addChild calls must happen before setDestColorFP");
357 int childIndex = this->numChildProcessors();
358 SkASSERT((size_t)childIndex < fEffect->fSampleUsages.size());
360 this->mergeOptimizationFlags(ProcessorOptimizationFlags(child.get()));
362 this->registerChild(std::move(child), fEffect->fSampleUsages[childIndex]);
365 void GrSkSLFP::setInput(std::unique_ptr<GrFragmentProcessor> input) {
366 SkASSERTF(fInputChildIndex == -1, "setInput should not be called more than once");
367 fInputChildIndex = this->numChildProcessors();
368 SkASSERT((size_t)fInputChildIndex >= fEffect->fSampleUsages.size());
369 this->mergeOptimizationFlags(ProcessorOptimizationFlags(input.get()));
370 this->registerChild(std::move(input), SkSL::SampleUsage::PassThrough());
373 void GrSkSLFP::setDestColorFP(std::unique_ptr<GrFragmentProcessor> destColorFP) {
374 SkASSERTF(fEffect->allowBlender(), "dest colors are only used by blend effects");
375 SkASSERTF(fDestColorChildIndex == -1, "setDestColorFP should not be called more than once");
376 fDestColorChildIndex = this->numChildProcessors();
377 SkASSERT((size_t)fDestColorChildIndex >= fEffect->fSampleUsages.size());
378 this->mergeOptimizationFlags(ProcessorOptimizationFlags(destColorFP.get()));
379 this->registerChild(std::move(destColorFP), SkSL::SampleUsage::PassThrough());
382 void GrSkSLFP::addColorTransformChildren(sk_sp<SkColorSpace> dstColorSpace) {
383 SkASSERTF(fToLinearSrgbChildIndex == -1 && fFromLinearSrgbChildIndex == -1,
384 "addColorTransformChildren should not be called more than once");
386 // We use child FPs for the color transforms. They're really just code snippets that get
387 // invoked, but each one injects a collection of uniforms and helper functions. Doing it
388 // this way leverages per-FP name mangling to avoid conflicts.
389 auto workingToLinear = GrColorSpaceXformEffect::Make(nullptr,
391 kUnpremul_SkAlphaType,
392 sk_srgb_linear_singleton(),
393 kUnpremul_SkAlphaType);
394 auto linearToWorking = GrColorSpaceXformEffect::Make(nullptr,
395 sk_srgb_linear_singleton(),
396 kUnpremul_SkAlphaType,
398 kUnpremul_SkAlphaType);
400 fToLinearSrgbChildIndex = this->numChildProcessors();
401 SkASSERT((size_t)fToLinearSrgbChildIndex >= fEffect->fSampleUsages.size());
402 this->registerChild(std::move(workingToLinear), SkSL::SampleUsage::PassThrough());
404 fFromLinearSrgbChildIndex = this->numChildProcessors();
405 SkASSERT((size_t)fFromLinearSrgbChildIndex >= fEffect->fSampleUsages.size());
406 this->registerChild(std::move(linearToWorking), SkSL::SampleUsage::PassThrough());
409 std::unique_ptr<GrFragmentProcessor::ProgramImpl> GrSkSLFP::onMakeProgramImpl() const {
410 return std::make_unique<Impl>();
413 void GrSkSLFP::onAddToKey(const GrShaderCaps& caps, skgpu::KeyBuilder* b) const {
414 // In the unlikely event of a hash collision, we also include the uniform size in the key.
415 // That ensures that we will (at worst) use the wrong program, but one that expects the same
416 // amount of uniform data.
417 b->add32(fEffect->hash());
418 b->add32(fUniformSize);
420 const UniformFlags* flags = this->uniformFlags();
421 const uint8_t* uniformData = this->uniformData();
422 size_t uniformCount = fEffect->uniforms().size();
423 auto iter = fEffect->uniforms().begin();
425 for (size_t i = 0; i < uniformCount; ++i, ++iter) {
426 bool specialize = flags[i] & kSpecialize_Flag;
427 b->addBool(specialize, "specialize");
429 b->addBytes(iter->sizeInBytes(), uniformData + iter->offset, iter->name.c_str());
434 bool GrSkSLFP::onIsEqual(const GrFragmentProcessor& other) const {
435 const GrSkSLFP& sk = other.cast<GrSkSLFP>();
436 const size_t uniformFlagSize = fEffect->uniforms().size() * sizeof(UniformFlags);
437 return fEffect->hash() == sk.fEffect->hash() &&
438 fEffect->uniforms().size() == sk.fEffect->uniforms().size() &&
439 fUniformSize == sk.fUniformSize &&
441 this->uniformData(), sk.uniformData(), fUniformSize + uniformFlagSize);
444 std::unique_ptr<GrFragmentProcessor> GrSkSLFP::clone() const {
445 return std::unique_ptr<GrFragmentProcessor>(new (UniformPayloadSize(fEffect.get()))
449 SkPMColor4f GrSkSLFP::constantOutputForConstantInput(const SkPMColor4f& inputColor) const {
450 const SkFilterColorProgram* program = fEffect->getFilterColorProgram();
453 auto evalChild = [&](int index, SkPMColor4f color) {
454 return ConstantOutputForConstantInput(this->childProcessor(index), color);
457 SkPMColor4f color = (fInputChildIndex >= 0)
458 ? ConstantOutputForConstantInput(
459 this->childProcessor(fInputChildIndex), inputColor)
461 return program->eval(color, this->uniformData(), evalChild);
464 /**************************************************************************************************/
466 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(GrSkSLFP);
470 #include "include/effects/SkOverdrawColorFilter.h"
471 #include "src/core/SkColorFilterBase.h"
473 extern const char* SKSL_OVERDRAW_SRC;
475 std::unique_ptr<GrFragmentProcessor> GrSkSLFP::TestCreate(GrProcessorTestData* d) {
476 SkColor colors[SkOverdrawColorFilter::kNumColors];
477 for (SkColor& c : colors) {
478 c = d->fRandom->nextU();
480 auto filter = SkOverdrawColorFilter::MakeWithSkColors(colors);
481 auto [success, fp] = as_CFB(filter)->asFragmentProcessor(/*inputFP=*/nullptr, d->context(),
484 return std::move(fp);