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