1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker * Copyright 2023 Google LLC
3*c8dee2aaSAndroid Build Coastguard Worker *
4*c8dee2aaSAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker */
7*c8dee2aaSAndroid Build Coastguard Worker #include "src/shaders/SkRuntimeShader.h"
8*c8dee2aaSAndroid Build Coastguard Worker
9*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkCapabilities.h"
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkData.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkMatrix.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkShader.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkString.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "include/effects/SkRuntimeEffect.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/SkSLSampleUsage.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkAssert.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkDebug.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTArray.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "include/sksl/SkSLDebugTrace.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkTLazy.h"
21*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkEffectPriv.h"
22*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkPicturePriv.h"
23*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkReadBuffer.h"
24*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkRuntimeEffectPriv.h"
25*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkWriteBuffer.h"
26*c8dee2aaSAndroid Build Coastguard Worker #include "src/shaders/SkShaderBase.h"
27*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/codegen/SkSLRasterPipelineBuilder.h"
28*c8dee2aaSAndroid Build Coastguard Worker #include "src/sksl/tracing/SkSLDebugTracePriv.h"
29*c8dee2aaSAndroid Build Coastguard Worker
30*c8dee2aaSAndroid Build Coastguard Worker #include <cstdint>
31*c8dee2aaSAndroid Build Coastguard Worker #include <optional>
32*c8dee2aaSAndroid Build Coastguard Worker #include <string>
33*c8dee2aaSAndroid Build Coastguard Worker #include <utility>
34*c8dee2aaSAndroid Build Coastguard Worker
35*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_BUILD_FOR_DEBUGGER)
36*c8dee2aaSAndroid Build Coastguard Worker constexpr bool kLenientSkSLDeserialization = true;
37*c8dee2aaSAndroid Build Coastguard Worker #else
38*c8dee2aaSAndroid Build Coastguard Worker constexpr bool kLenientSkSLDeserialization = false;
39*c8dee2aaSAndroid Build Coastguard Worker #endif
40*c8dee2aaSAndroid Build Coastguard Worker
41*c8dee2aaSAndroid Build Coastguard Worker class SkColorSpace;
42*c8dee2aaSAndroid Build Coastguard Worker struct SkIPoint;
43*c8dee2aaSAndroid Build Coastguard Worker
SkRuntimeShader(sk_sp<SkRuntimeEffect> effect,sk_sp<SkSL::DebugTracePriv> debugTrace,sk_sp<const SkData> uniforms,SkSpan<const SkRuntimeEffect::ChildPtr> children)44*c8dee2aaSAndroid Build Coastguard Worker SkRuntimeShader::SkRuntimeShader(sk_sp<SkRuntimeEffect> effect,
45*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkSL::DebugTracePriv> debugTrace,
46*c8dee2aaSAndroid Build Coastguard Worker sk_sp<const SkData> uniforms,
47*c8dee2aaSAndroid Build Coastguard Worker SkSpan<const SkRuntimeEffect::ChildPtr> children)
48*c8dee2aaSAndroid Build Coastguard Worker : fEffect(std::move(effect))
49*c8dee2aaSAndroid Build Coastguard Worker , fDebugTrace(std::move(debugTrace))
50*c8dee2aaSAndroid Build Coastguard Worker , fUniformData(std::move(uniforms))
51*c8dee2aaSAndroid Build Coastguard Worker , fChildren(children.begin(), children.end()) {}
52*c8dee2aaSAndroid Build Coastguard Worker
SkRuntimeShader(sk_sp<SkRuntimeEffect> effect,sk_sp<SkSL::DebugTracePriv> debugTrace,UniformsCallback uniformsCallback,SkSpan<const SkRuntimeEffect::ChildPtr> children)53*c8dee2aaSAndroid Build Coastguard Worker SkRuntimeShader::SkRuntimeShader(sk_sp<SkRuntimeEffect> effect,
54*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkSL::DebugTracePriv> debugTrace,
55*c8dee2aaSAndroid Build Coastguard Worker UniformsCallback uniformsCallback,
56*c8dee2aaSAndroid Build Coastguard Worker SkSpan<const SkRuntimeEffect::ChildPtr> children)
57*c8dee2aaSAndroid Build Coastguard Worker : fEffect(std::move(effect))
58*c8dee2aaSAndroid Build Coastguard Worker , fDebugTrace(std::move(debugTrace))
59*c8dee2aaSAndroid Build Coastguard Worker , fUniformsCallback(std::move(uniformsCallback))
60*c8dee2aaSAndroid Build Coastguard Worker , fChildren(children.begin(), children.end()) {}
61*c8dee2aaSAndroid Build Coastguard Worker
make_debug_trace(SkRuntimeEffect * effect,const SkIPoint & coord)62*c8dee2aaSAndroid Build Coastguard Worker static sk_sp<SkSL::DebugTracePriv> make_debug_trace(SkRuntimeEffect* effect,
63*c8dee2aaSAndroid Build Coastguard Worker const SkIPoint& coord) {
64*c8dee2aaSAndroid Build Coastguard Worker auto debugTrace = sk_make_sp<SkSL::DebugTracePriv>();
65*c8dee2aaSAndroid Build Coastguard Worker debugTrace->setSource(effect->source());
66*c8dee2aaSAndroid Build Coastguard Worker debugTrace->setTraceCoord(coord);
67*c8dee2aaSAndroid Build Coastguard Worker return debugTrace;
68*c8dee2aaSAndroid Build Coastguard Worker }
69*c8dee2aaSAndroid Build Coastguard Worker
makeTracedClone(const SkIPoint & coord)70*c8dee2aaSAndroid Build Coastguard Worker SkRuntimeEffect::TracedShader SkRuntimeShader::makeTracedClone(const SkIPoint& coord) {
71*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkRuntimeEffect> unoptimized = fEffect->makeUnoptimizedClone();
72*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkSL::DebugTracePriv> debugTrace = make_debug_trace(unoptimized.get(), coord);
73*c8dee2aaSAndroid Build Coastguard Worker auto debugShader = sk_make_sp<SkRuntimeShader>(
74*c8dee2aaSAndroid Build Coastguard Worker unoptimized, debugTrace, this->uniformData(nullptr), SkSpan(fChildren));
75*c8dee2aaSAndroid Build Coastguard Worker
76*c8dee2aaSAndroid Build Coastguard Worker return SkRuntimeEffect::TracedShader{std::move(debugShader), std::move(debugTrace)};
77*c8dee2aaSAndroid Build Coastguard Worker }
78*c8dee2aaSAndroid Build Coastguard Worker
appendStages(const SkStageRec & rec,const SkShaders::MatrixRec & mRec) const79*c8dee2aaSAndroid Build Coastguard Worker bool SkRuntimeShader::appendStages(const SkStageRec& rec, const SkShaders::MatrixRec& mRec) const {
80*c8dee2aaSAndroid Build Coastguard Worker if (!SkRuntimeEffectPriv::CanDraw(SkCapabilities::RasterBackend().get(), fEffect.get())) {
81*c8dee2aaSAndroid Build Coastguard Worker // SkRP has support for many parts of #version 300 already, but for now, we restrict its
82*c8dee2aaSAndroid Build Coastguard Worker // usage in runtime effects to just #version 100.
83*c8dee2aaSAndroid Build Coastguard Worker return false;
84*c8dee2aaSAndroid Build Coastguard Worker }
85*c8dee2aaSAndroid Build Coastguard Worker if (const SkSL::RP::Program* program = fEffect->getRPProgram(fDebugTrace.get())) {
86*c8dee2aaSAndroid Build Coastguard Worker std::optional<SkShaders::MatrixRec> newMRec = mRec.apply(rec);
87*c8dee2aaSAndroid Build Coastguard Worker if (!newMRec.has_value()) {
88*c8dee2aaSAndroid Build Coastguard Worker return false;
89*c8dee2aaSAndroid Build Coastguard Worker }
90*c8dee2aaSAndroid Build Coastguard Worker SkSpan<const float> uniforms =
91*c8dee2aaSAndroid Build Coastguard Worker SkRuntimeEffectPriv::UniformsAsSpan(fEffect->uniforms(),
92*c8dee2aaSAndroid Build Coastguard Worker this->uniformData(rec.fDstCS),
93*c8dee2aaSAndroid Build Coastguard Worker /*alwaysCopyIntoAlloc=*/fUniformData == nullptr,
94*c8dee2aaSAndroid Build Coastguard Worker rec.fDstCS,
95*c8dee2aaSAndroid Build Coastguard Worker rec.fAlloc);
96*c8dee2aaSAndroid Build Coastguard Worker RuntimeEffectRPCallbacks callbacks(rec, *newMRec, fChildren, fEffect->fSampleUsages);
97*c8dee2aaSAndroid Build Coastguard Worker bool success = program->appendStages(rec.fPipeline, rec.fAlloc, &callbacks, uniforms);
98*c8dee2aaSAndroid Build Coastguard Worker return success;
99*c8dee2aaSAndroid Build Coastguard Worker }
100*c8dee2aaSAndroid Build Coastguard Worker return false;
101*c8dee2aaSAndroid Build Coastguard Worker }
102*c8dee2aaSAndroid Build Coastguard Worker
flatten(SkWriteBuffer & buffer) const103*c8dee2aaSAndroid Build Coastguard Worker void SkRuntimeShader::flatten(SkWriteBuffer& buffer) const {
104*c8dee2aaSAndroid Build Coastguard Worker buffer.writeString(fEffect->source().c_str());
105*c8dee2aaSAndroid Build Coastguard Worker buffer.writeDataAsByteArray(this->uniformData(nullptr).get());
106*c8dee2aaSAndroid Build Coastguard Worker SkRuntimeEffectPriv::WriteChildEffects(buffer, fChildren);
107*c8dee2aaSAndroid Build Coastguard Worker }
108*c8dee2aaSAndroid Build Coastguard Worker
uniformData(const SkColorSpace * dstCS) const109*c8dee2aaSAndroid Build Coastguard Worker sk_sp<const SkData> SkRuntimeShader::uniformData(const SkColorSpace* dstCS) const {
110*c8dee2aaSAndroid Build Coastguard Worker if (fUniformData) {
111*c8dee2aaSAndroid Build Coastguard Worker return fUniformData;
112*c8dee2aaSAndroid Build Coastguard Worker }
113*c8dee2aaSAndroid Build Coastguard Worker
114*c8dee2aaSAndroid Build Coastguard Worker // We want to invoke the uniforms-callback each time a paint occurs.
115*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fUniformsCallback);
116*c8dee2aaSAndroid Build Coastguard Worker sk_sp<const SkData> uniforms = fUniformsCallback({dstCS});
117*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(uniforms && uniforms->size() == fEffect->uniformSize());
118*c8dee2aaSAndroid Build Coastguard Worker return uniforms;
119*c8dee2aaSAndroid Build Coastguard Worker }
120*c8dee2aaSAndroid Build Coastguard Worker
CreateProc(SkReadBuffer & buffer)121*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkFlattenable> SkRuntimeShader::CreateProc(SkReadBuffer& buffer) {
122*c8dee2aaSAndroid Build Coastguard Worker if (!buffer.validate(buffer.allowSkSL())) {
123*c8dee2aaSAndroid Build Coastguard Worker return nullptr;
124*c8dee2aaSAndroid Build Coastguard Worker }
125*c8dee2aaSAndroid Build Coastguard Worker
126*c8dee2aaSAndroid Build Coastguard Worker SkString sksl;
127*c8dee2aaSAndroid Build Coastguard Worker buffer.readString(&sksl);
128*c8dee2aaSAndroid Build Coastguard Worker sk_sp<SkData> uniforms = buffer.readByteArrayAsData();
129*c8dee2aaSAndroid Build Coastguard Worker
130*c8dee2aaSAndroid Build Coastguard Worker SkTLazy<SkMatrix> localM;
131*c8dee2aaSAndroid Build Coastguard Worker if (buffer.isVersionLT(SkPicturePriv::kNoShaderLocalMatrix)) {
132*c8dee2aaSAndroid Build Coastguard Worker uint32_t flags = buffer.read32();
133*c8dee2aaSAndroid Build Coastguard Worker if (flags & kHasLegacyLocalMatrix_Flag) {
134*c8dee2aaSAndroid Build Coastguard Worker buffer.readMatrix(localM.init());
135*c8dee2aaSAndroid Build Coastguard Worker }
136*c8dee2aaSAndroid Build Coastguard Worker }
137*c8dee2aaSAndroid Build Coastguard Worker
138*c8dee2aaSAndroid Build Coastguard Worker auto effect = SkMakeCachedRuntimeEffect(SkRuntimeEffect::MakeForShader, std::move(sksl));
139*c8dee2aaSAndroid Build Coastguard Worker if constexpr (!kLenientSkSLDeserialization) {
140*c8dee2aaSAndroid Build Coastguard Worker if (!buffer.validate(effect != nullptr)) {
141*c8dee2aaSAndroid Build Coastguard Worker return nullptr;
142*c8dee2aaSAndroid Build Coastguard Worker }
143*c8dee2aaSAndroid Build Coastguard Worker }
144*c8dee2aaSAndroid Build Coastguard Worker
145*c8dee2aaSAndroid Build Coastguard Worker skia_private::STArray<4, SkRuntimeEffect::ChildPtr> children;
146*c8dee2aaSAndroid Build Coastguard Worker if (!SkRuntimeEffectPriv::ReadChildEffects(buffer, effect.get(), &children)) {
147*c8dee2aaSAndroid Build Coastguard Worker return nullptr;
148*c8dee2aaSAndroid Build Coastguard Worker }
149*c8dee2aaSAndroid Build Coastguard Worker
150*c8dee2aaSAndroid Build Coastguard Worker if constexpr (kLenientSkSLDeserialization) {
151*c8dee2aaSAndroid Build Coastguard Worker if (!effect) {
152*c8dee2aaSAndroid Build Coastguard Worker // If any children were SkShaders, return the first one. This is a reasonable fallback.
153*c8dee2aaSAndroid Build Coastguard Worker for (int i = 0; i < children.size(); i++) {
154*c8dee2aaSAndroid Build Coastguard Worker if (children[i].shader()) {
155*c8dee2aaSAndroid Build Coastguard Worker SkDebugf("Serialized SkSL failed to compile. Replacing shader with child %d.\n",
156*c8dee2aaSAndroid Build Coastguard Worker i);
157*c8dee2aaSAndroid Build Coastguard Worker return sk_ref_sp(children[i].shader());
158*c8dee2aaSAndroid Build Coastguard Worker }
159*c8dee2aaSAndroid Build Coastguard Worker }
160*c8dee2aaSAndroid Build Coastguard Worker
161*c8dee2aaSAndroid Build Coastguard Worker // We don't know what to do, so just return nullptr (but *don't* poison the buffer).
162*c8dee2aaSAndroid Build Coastguard Worker SkDebugf("Serialized SkSL failed to compile. Ignoring/dropping SkSL shader.\n");
163*c8dee2aaSAndroid Build Coastguard Worker return nullptr;
164*c8dee2aaSAndroid Build Coastguard Worker }
165*c8dee2aaSAndroid Build Coastguard Worker }
166*c8dee2aaSAndroid Build Coastguard Worker
167*c8dee2aaSAndroid Build Coastguard Worker return effect->makeShader(std::move(uniforms), SkSpan(children), localM.getMaybeNull());
168*c8dee2aaSAndroid Build Coastguard Worker }
169