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