xref: /aosp_15_r20/external/skia/src/shaders/SkRuntimeShader.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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