blob: c42a8d0e511f802b3897ceb51e041e1ba38bf3f3 [file] [log] [blame]
/*
* Copyright 2019 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "include/core/SkData.h"
#include "src/core/SkArenaAlloc.h"
#include "src/core/SkRasterPipeline.h"
#include "src/core/SkReadBuffer.h"
#include "src/core/SkWriteBuffer.h"
#include "src/shaders/SkRTShader.h"
#include "src/sksl/SkSLByteCode.h"
#include "src/sksl/SkSLCompiler.h"
#if SK_SUPPORT_GPU
#include "include/private/GrRecordingContext.h"
#include "src/gpu/GrCaps.h"
#include "src/gpu/GrColorInfo.h"
#include "src/gpu/GrRecordingContextPriv.h"
#include "src/gpu/SkGr.h"
#include "src/gpu/GrFragmentProcessor.h"
#include "src/gpu/effects/GrSkSLFP.h"
#include "src/gpu/effects/generated/GrMixerEffect.h"
static inline uint32_t new_sksl_unique_id() {
return GrSkSLFP::NewIndex();
}
#else
static inline uint32_t new_sksl_unique_id() {
return 0; // not used w/o GPU
}
#endif
SkRTShader::SkRTShader(int index, SkString sksl, sk_sp<SkData> inputs, const SkMatrix* localMatrix,
bool isOpaque)
: SkShaderBase(localMatrix)
, fSkSL(std::move(sksl))
, fInputs(std::move(inputs))
, fUniqueID(index)
, fIsOpaque(isOpaque)
{}
bool SkRTShader::onAppendStages(const SkStageRec& rec) const {
SkMatrix inverse;
if (!this->computeTotalInverse(rec.fCTM, rec.fLocalM, &inverse)) {
return false;
}
auto ctx = rec.fAlloc->make<SkRasterPipeline_InterpreterCtx>();
ctx->paintColor = rec.fPaint.getColor4f();
ctx->inputs = fInputs->data();
ctx->ninputs = fInputs->size() / 4;
ctx->shaderConvention = true;
SkAutoMutexExclusive ama(fByteCodeMutex);
if (!fByteCode) {
SkSL::Compiler c;
auto prog = c.convertProgram(SkSL::Program::kPipelineStage_Kind,
SkSL::String(fSkSL.c_str()),
SkSL::Program::Settings());
if (c.errorCount()) {
SkDebugf("%s\n", c.errorText().c_str());
return false;
}
fByteCode = c.toByteCode(*prog);
if (c.errorCount()) {
SkDebugf("%s\n", c.errorText().c_str());
return false;
}
SkASSERT(fByteCode);
if (!fByteCode->getFunction("main")) {
return false;
}
}
ctx->byteCode = fByteCode.get();
ctx->fn = ctx->byteCode->getFunction("main");
rec.fPipeline->append(SkRasterPipeline::seed_shader);
rec.fPipeline->append_matrix(rec.fAlloc, inverse);
rec.fPipeline->append(SkRasterPipeline::interpreter, ctx);
return true;
}
enum Flags {
kIsOpaque_Flag = 1 << 0,
kHasLocalMatrix_Flag = 1 << 1,
};
void SkRTShader::flatten(SkWriteBuffer& buffer) const {
uint32_t flags = 0;
if (fIsOpaque) {
flags |= kIsOpaque_Flag;
}
if (!this->getLocalMatrix().isIdentity()) {
flags |= kHasLocalMatrix_Flag;
}
buffer.writeString(fSkSL.c_str());
if (fInputs) {
buffer.writeDataAsByteArray(fInputs.get());
} else {
buffer.writeByteArray(nullptr, 0);
}
buffer.write32(flags);
if (flags & kHasLocalMatrix_Flag) {
buffer.writeMatrix(this->getLocalMatrix());
}
}
sk_sp<SkFlattenable> SkRTShader::CreateProc(SkReadBuffer& buffer) {
// We don't have a way to ensure that indices are consistent and correct when deserializing.
// Perhaps we should have a hash table to map strings to indices? For now, all shaders get a
// new unique ID after serialization.
int index = new_sksl_unique_id();
SkString sksl;
buffer.readString(&sksl);
sk_sp<SkData> inputs = buffer.readByteArrayAsData();
uint32_t flags = buffer.read32();
bool isOpaque = SkToBool(flags & kIsOpaque_Flag);
SkMatrix localM, *localMPtr = nullptr;
if (flags & kHasLocalMatrix_Flag) {
buffer.readMatrix(&localM);
localMPtr = &localM;
}
return sk_sp<SkFlattenable>(new SkRTShader(index, std::move(sksl), std::move(inputs),
localMPtr, isOpaque));
}
#if SK_SUPPORT_GPU
std::unique_ptr<GrFragmentProcessor> SkRTShader::asFragmentProcessor(const GrFPArgs& args) const {
SkMatrix matrix;
if (!this->totalLocalMatrix(args.fPreLocalMatrix, args.fPostLocalMatrix)->invert(&matrix)) {
return nullptr;
}
return GrSkSLFP::Make(args.fContext, fUniqueID, "runtime-shader", fSkSL,
fInputs->data(), fInputs->size(), SkSL::Program::kPipelineStage_Kind,
&matrix);
}
#endif
SkRuntimeShaderFactory::SkRuntimeShaderFactory(SkString sksl, bool isOpaque)
: fIndex(new_sksl_unique_id())
, fSkSL(std::move(sksl))
, fIsOpaque(isOpaque) {}
sk_sp<SkShader> SkRuntimeShaderFactory::make(sk_sp<SkData> inputs, const SkMatrix* localMatrix) {
return sk_sp<SkShader>(
new SkRTShader(fIndex, fSkSL, std::move(inputs), localMatrix, fIsOpaque));
}