skia2/include/effects/SkRuntimeEffect.h
Brian Osman 871aa74797 Add support for children to runtime color filters
Like shaders, sampling a null child will return the input color (in
this case, the output of the skia shader stage). This gives us a path
to removing the implicit input color passed to main, which is the real
goal. Using this to create more interesting color filters is also
possible, although we need to add the versions of sample() that take a
color to really unlock the potential.

Change-Id: I6a7506055120756497d7583f14d6f928180825fc
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/308515
Commit-Queue: Brian Osman <brianosman@google.com>
Reviewed-by: Mike Klein <mtklein@google.com>
Reviewed-by: Michael Ludwig <michaelludwig@google.com>
2020-08-07 14:50:36 +00:00

271 lines
9.4 KiB
C++

/*
* Copyright 2019 Google LLC
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef SkRuntimeEffect_DEFINED
#define SkRuntimeEffect_DEFINED
#include "include/core/SkData.h"
#include "include/core/SkMatrix.h"
#include "include/core/SkString.h"
#include "include/private/GrTypesPriv.h"
#include "include/private/SkSLSampleUsage.h"
#include <string>
#include <vector>
#if SK_SUPPORT_GPU
#include "include/gpu/GrContextOptions.h"
#endif
class GrShaderCaps;
class SkColorFilter;
class SkShader;
namespace SkSL {
class ByteCode;
struct PipelineStageArgs;
struct Program;
class SharedCompiler;
} // namespace SkSL
/*
* SkRuntimeEffect supports creating custom SkShader and SkColorFilter objects using Skia's SkSL
* shading language.
*
* NOTE: This API is experimental and subject to change.
*/
class SK_API SkRuntimeEffect : public SkRefCnt {
public:
struct Variable {
enum class Qualifier {
kUniform,
kIn,
};
enum class Type {
kBool,
kInt,
kFloat,
kFloat2,
kFloat3,
kFloat4,
kFloat2x2,
kFloat3x3,
kFloat4x4,
};
enum Flags {
kArray_Flag = 0x1,
kMarker_Flag = 0x2,
kMarkerNormals_Flag = 0x4,
kSRGBUnpremul_Flag = 0x8,
};
SkString fName;
size_t fOffset;
Qualifier fQualifier;
Type fType;
GrSLType fGPUType;
int fCount;
uint32_t fFlags;
uint32_t fMarker;
bool isArray() const { return SkToBool(fFlags & kArray_Flag); }
size_t sizeInBytes() const;
};
struct Varying {
SkString fName;
int fWidth; // 1 - 4 (floats)
};
// [Effect, ErrorText]
// If successful, Effect != nullptr, otherwise, ErrorText contains the reason for failure.
using EffectResult = std::tuple<sk_sp<SkRuntimeEffect>, SkString>;
static EffectResult Make(SkString sksl);
sk_sp<SkShader> makeShader(sk_sp<SkData> inputs,
sk_sp<SkShader> children[],
size_t childCount,
const SkMatrix* localMatrix,
bool isOpaque);
sk_sp<SkColorFilter> makeColorFilter(sk_sp<SkData> inputs);
sk_sp<SkColorFilter> makeColorFilter(sk_sp<SkData> inputs,
sk_sp<SkColorFilter> children[],
size_t childCount);
const SkString& source() const { return fSkSL; }
uint32_t hash() const { return fHash; }
template <typename T>
class ConstIterable {
public:
ConstIterable(const std::vector<T>& vec) : fVec(vec) {}
using const_iterator = typename std::vector<T>::const_iterator;
const_iterator begin() const { return fVec.begin(); }
const_iterator end() const { return fVec.end(); }
size_t count() const { return fVec.size(); }
private:
const std::vector<T>& fVec;
};
// Combined size of all 'in' and 'uniform' variables. When calling makeColorFilter or
// makeShader, provide an SkData of this size, containing values for all of those variables.
size_t inputSize() const;
ConstIterable<Variable> inputs() const { return ConstIterable<Variable>(fInAndUniformVars); }
ConstIterable<SkString> children() const { return ConstIterable<SkString>(fChildren); }
ConstIterable<Varying> varyings() const { return ConstIterable<Varying>(fVaryings); }
// Returns pointer to the named in/uniform variable's description, or nullptr if not found
const Variable* findInput(const char* name) const;
// Returns index of the named child, or -1 if not found
int findChild(const char* name) const;
bool usesSampleCoords() const { return fUsesSampleCoords; }
static void RegisterFlattenables();
~SkRuntimeEffect() override;
private:
SkRuntimeEffect(SkString sksl,
std::unique_ptr<SkSL::Program> baseProgram,
std::vector<Variable>&& inAndUniformVars,
std::vector<SkString>&& children,
std::vector<SkSL::SampleUsage>&& sampleUsages,
std::vector<Varying>&& varyings,
size_t uniformSize,
bool usesSampleCoords,
bool allowColorFilter);
using SpecializeResult = std::tuple<std::unique_ptr<SkSL::Program>, SkString>;
SpecializeResult specialize(SkSL::Program& baseProgram, const void* inputs,
const SkSL::SharedCompiler&) const;
#if SK_SUPPORT_GPU
friend class GrSkSLFP; // toPipelineStage
friend class GrGLSLSkSLFP; // fSampleUsages
// This re-compiles the program from scratch, using the supplied shader caps.
// This is necessary to get the correct values of settings.
bool toPipelineStage(const void* inputs, const GrShaderCaps* shaderCaps,
GrContextOptions::ShaderErrorHandler* errorHandler,
SkSL::PipelineStageArgs* outArgs);
#endif
friend class SkRTShader; // toByteCode & uniformSize
friend class SkRuntimeColorFilter; //
// [ByteCode, ErrorText]
// If successful, ByteCode != nullptr, otherwise, ErrorText contains the reason for failure.
using ByteCodeResult = std::tuple<std::unique_ptr<SkSL::ByteCode>, SkString>;
ByteCodeResult toByteCode(const void* inputs) const;
// Combined size of just the 'uniform' variables.
size_t uniformSize() const { return fUniformSize; }
uint32_t fHash;
SkString fSkSL;
std::unique_ptr<SkSL::Program> fBaseProgram;
std::vector<Variable> fInAndUniformVars;
std::vector<SkString> fChildren;
std::vector<SkSL::SampleUsage> fSampleUsages;
std::vector<Varying> fVaryings;
size_t fUniformSize;
bool fUsesSampleCoords;
bool fAllowColorFilter;
};
/**
* SkRuntimeShaderBuilder is a utility to simplify creating SkShader objects from SkRuntimeEffects.
*
* NOTE: Like SkRuntimeEffect, this API is experimental and subject to change!
*
* Given an SkRuntimeEffect, the SkRuntimeShaderBuilder manages creating an input data block and
* provides named access to the 'in' and 'uniform' variables in that block, as well as named access
* to a list of child shader slots. Usage:
*
* sk_sp<SkRuntimeEffect> effect = ...;
* SkRuntimeShaderBuilder builder(effect);
* builder.input("some_uniform_float") = 3.14f;
* builder.input("some_uniform_matrix") = SkM44::Rotate(...);
* builder.child("some_child_effect") = mySkImage->makeShader(...);
* ...
* sk_sp<SkShader> shader = builder.makeShader(nullptr, false);
*
* Note that SkRuntimeShaderBuilder is built entirely on the public API of SkRuntimeEffect,
* so can be used as-is or serve as inspiration for other interfaces or binding techniques.
*/
struct SkRuntimeShaderBuilder {
SkRuntimeShaderBuilder(sk_sp<SkRuntimeEffect>);
~SkRuntimeShaderBuilder();
struct BuilderInput {
// Copy 'val' to this variable. No type conversion is performed - 'val' must be same
// size as expected by the effect. Information about the variable can be queried by
// looking at fVar. If the size is incorrect, no copy will be performed, and debug
// builds will abort. If this is the result of querying a missing variable, fVar will
// be nullptr, and assigning will also do nothing (and abort in debug builds).
template <typename T>
std::enable_if_t<std::is_trivially_copyable<T>::value, BuilderInput&> operator=(
const T& val) {
if (!fVar) {
SkDEBUGFAIL("Assigning to missing variable");
} else if (sizeof(val) != fVar->sizeInBytes()) {
SkDEBUGFAIL("Incorrect value size");
} else {
memcpy(SkTAddOffset<void>(fOwner->fInputs->writable_data(), fVar->fOffset),
&val, sizeof(val));
}
return *this;
}
BuilderInput& operator=(const SkMatrix& val) {
if (!fVar) {
SkDEBUGFAIL("Assigning to missing variable");
} else if (fVar->sizeInBytes() != 9 * sizeof(float)) {
SkDEBUGFAIL("Incorrect value size");
} else {
float* data = SkTAddOffset<float>(fOwner->fInputs->writable_data(), fVar->fOffset);
data[0] = val.get(0); data[1] = val.get(3); data[2] = val.get(6);
data[3] = val.get(1); data[4] = val.get(4); data[5] = val.get(7);
data[6] = val.get(2); data[7] = val.get(5); data[8] = val.get(8);
}
return *this;
}
SkRuntimeShaderBuilder* fOwner;
const SkRuntimeEffect::Variable* fVar; // nullptr if the variable was not found
};
struct BuilderChild {
BuilderChild& operator=(const sk_sp<SkShader>& val);
SkRuntimeShaderBuilder* fOwner;
int fIndex; // -1 if the child was not found
};
BuilderInput input(const char* name) { return { this, fEffect->findInput(name) }; }
BuilderChild child(const char* name) { return { this, fEffect->findChild(name) }; }
sk_sp<SkShader> makeShader(const SkMatrix* localMatrix, bool isOpaque);
sk_sp<SkRuntimeEffect> fEffect;
sk_sp<SkData> fInputs;
std::vector<sk_sp<SkShader>> fChildren;
};
#endif