SkMeshSpecification supports uniforms.
Currently there is no way to specify the data that is bound to them. That will come in later changes. Bug: skia:12720 Change-Id: I3301a825486469396a13a4095b66d9e0b81f183b Reviewed-on: https://skia-review.googlesource.com/c/skia/+/543716 Reviewed-by: Brian Osman <brianosman@google.com> Commit-Queue: Brian Salomon <bsalomon@google.com>
This commit is contained in:
parent
479fa1c9ca
commit
586e6354aa
@ -16,6 +16,7 @@
|
|||||||
#include "include/core/SkRefCnt.h"
|
#include "include/core/SkRefCnt.h"
|
||||||
#include "include/core/SkSpan.h"
|
#include "include/core/SkSpan.h"
|
||||||
#include "include/core/SkString.h"
|
#include "include/core/SkString.h"
|
||||||
|
#include "include/effects/SkRuntimeEffect.h"
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@ -44,6 +45,10 @@ namespace SkSL { struct Program; }
|
|||||||
* return type is void then the interpolated position from vertex shader return is used as the local
|
* return type is void then the interpolated position from vertex shader return is used as the local
|
||||||
* coordinate. If the color variant is used it will be blended with SkShader (or SkPaint color in
|
* coordinate. If the color variant is used it will be blended with SkShader (or SkPaint color in
|
||||||
* absence of a shader) using the SkBlender provided to the SkCanvas draw call.
|
* absence of a shader) using the SkBlender provided to the SkCanvas draw call.
|
||||||
|
*
|
||||||
|
* The vertex and fragment programs may both contain uniforms. Uniforms with the same name are
|
||||||
|
* assumed to be shared between stages. It is an error to specify uniforms in the vertex and
|
||||||
|
* fragment program with the same name but different types, dimensionality, or layouts.
|
||||||
*/
|
*/
|
||||||
class SkMeshSpecification : public SkNVRefCnt<SkMeshSpecification> {
|
class SkMeshSpecification : public SkNVRefCnt<SkMeshSpecification> {
|
||||||
public:
|
public:
|
||||||
@ -86,6 +91,8 @@ public:
|
|||||||
SkString name;
|
SkString name;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
using Uniform = SkRuntimeEffect::Uniform;
|
||||||
|
|
||||||
~SkMeshSpecification();
|
~SkMeshSpecification();
|
||||||
|
|
||||||
struct Result {
|
struct Result {
|
||||||
@ -136,6 +143,13 @@ public:
|
|||||||
|
|
||||||
SkSpan<const Attribute> attributes() const { return SkMakeSpan(fAttributes); }
|
SkSpan<const Attribute> attributes() const { return SkMakeSpan(fAttributes); }
|
||||||
|
|
||||||
|
size_t uniformSize() const;
|
||||||
|
|
||||||
|
SkSpan<const Uniform> uniforms() const { return SkMakeSpan(fUniforms); }
|
||||||
|
|
||||||
|
/** Returns pointer to the named uniform variable's description, or nullptr if not found. */
|
||||||
|
const Uniform* findUniform(const char* name) const;
|
||||||
|
|
||||||
size_t stride() const { return fStride; }
|
size_t stride() const { return fStride; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -158,8 +172,9 @@ private:
|
|||||||
SkMeshSpecification(SkSpan<const Attribute>,
|
SkMeshSpecification(SkSpan<const Attribute>,
|
||||||
size_t,
|
size_t,
|
||||||
SkSpan<const Varying>,
|
SkSpan<const Varying>,
|
||||||
std::unique_ptr<SkSL::Program>,
|
std::vector<Uniform> uniforms,
|
||||||
std::unique_ptr<SkSL::Program>,
|
std::unique_ptr<const SkSL::Program>,
|
||||||
|
std::unique_ptr<const SkSL::Program>,
|
||||||
ColorType,
|
ColorType,
|
||||||
bool hasLocalCoords,
|
bool hasLocalCoords,
|
||||||
sk_sp<SkColorSpace>,
|
sk_sp<SkColorSpace>,
|
||||||
@ -171,16 +186,17 @@ private:
|
|||||||
SkMeshSpecification& operator=(const SkMeshSpecification&) = delete;
|
SkMeshSpecification& operator=(const SkMeshSpecification&) = delete;
|
||||||
SkMeshSpecification& operator=(SkMeshSpecification&&) = delete;
|
SkMeshSpecification& operator=(SkMeshSpecification&&) = delete;
|
||||||
|
|
||||||
const std::vector<Attribute> fAttributes;
|
const std::vector<Attribute> fAttributes;
|
||||||
const std::vector<Varying> fVaryings;
|
const std::vector<Varying> fVaryings;
|
||||||
std::unique_ptr<SkSL::Program> fVS;
|
const std::vector<Uniform> fUniforms;
|
||||||
std::unique_ptr<SkSL::Program> fFS;
|
const std::unique_ptr<const SkSL::Program> fVS;
|
||||||
size_t fStride;
|
const std::unique_ptr<const SkSL::Program> fFS;
|
||||||
uint32_t fHash;
|
const size_t fStride;
|
||||||
ColorType fColorType;
|
uint32_t fHash;
|
||||||
bool fHasLocalCoords;
|
const ColorType fColorType;
|
||||||
sk_sp<SkColorSpace> fColorSpace;
|
const bool fHasLocalCoords;
|
||||||
SkAlphaType fAlphaType;
|
const sk_sp<SkColorSpace> fColorSpace;
|
||||||
|
const SkAlphaType fAlphaType;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -68,13 +68,21 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
enum Flags {
|
enum Flags {
|
||||||
// Uniform is an declared as an array. 'count' contains array length.
|
// Uniform is declared as an array. 'count' contains array length.
|
||||||
kArray_Flag = 0x1,
|
kArray_Flag = 0x1,
|
||||||
|
|
||||||
// Uniform is declared with layout(color). Colors should be supplied as unpremultiplied,
|
// Uniform is declared with layout(color). Colors should be supplied as unpremultiplied,
|
||||||
// extended-range (unclamped) sRGB (ie SkColor4f). The uniform will be automatically
|
// extended-range (unclamped) sRGB (ie SkColor4f). The uniform will be automatically
|
||||||
// transformed to unpremultiplied extended-range working-space colors.
|
// transformed to unpremultiplied extended-range working-space colors.
|
||||||
kColor_Flag = 0x2,
|
kColor_Flag = 0x2,
|
||||||
|
|
||||||
|
// When used with SkMeshSpecification, indicates that the uniform is present in the
|
||||||
|
// vertex shader. Not used with SkRuntimeEffect.
|
||||||
|
kVertex_Flag = 0x4,
|
||||||
|
|
||||||
|
// When used with SkMeshSpecification, indicates that the uniform is present in the
|
||||||
|
// fragment shader. Not used with SkRuntimeEffect.
|
||||||
|
kFragment_Flag = 0x8,
|
||||||
};
|
};
|
||||||
|
|
||||||
SkString name;
|
SkString name;
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#include "include/private/SkSLProgramElement.h"
|
#include "include/private/SkSLProgramElement.h"
|
||||||
#include "include/private/SkSLProgramKind.h"
|
#include "include/private/SkSLProgramKind.h"
|
||||||
#include "src/core/SkMeshPriv.h"
|
#include "src/core/SkMeshPriv.h"
|
||||||
|
#include "src/core/SkRuntimeEffectPriv.h"
|
||||||
#include "src/core/SkSafeMath.h"
|
#include "src/core/SkSafeMath.h"
|
||||||
#include "src/sksl/SkSLAnalysis.h"
|
#include "src/sksl/SkSLAnalysis.h"
|
||||||
#include "src/sksl/SkSLBuiltinTypes.h"
|
#include "src/sksl/SkSLBuiltinTypes.h"
|
||||||
@ -24,6 +25,7 @@
|
|||||||
#include "src/sksl/ir/SkSLFunctionDefinition.h"
|
#include "src/sksl/ir/SkSLFunctionDefinition.h"
|
||||||
#include "src/sksl/ir/SkSLProgram.h"
|
#include "src/sksl/ir/SkSLProgram.h"
|
||||||
#include "src/sksl/ir/SkSLType.h"
|
#include "src/sksl/ir/SkSLType.h"
|
||||||
|
#include "src/sksl/ir/SkSLVarDeclarations.h"
|
||||||
#include "src/sksl/ir/SkSLVariable.h"
|
#include "src/sksl/ir/SkSLVariable.h"
|
||||||
|
|
||||||
#include <locale>
|
#include <locale>
|
||||||
@ -44,17 +46,64 @@ using VertexBuffer = SkMesh::VertexBuffer;
|
|||||||
|
|
||||||
#define RETURN_SUCCESS return std::make_tuple(true, SkString{})
|
#define RETURN_SUCCESS return std::make_tuple(true, SkString{})
|
||||||
|
|
||||||
static bool has_main(const SkSL::Program& p) {
|
using Uniform = SkMeshSpecification::Uniform;
|
||||||
for (const SkSL::ProgramElement* elem : p.elements()) {
|
|
||||||
|
static std::vector<Uniform>::iterator find_uniform(std::vector<Uniform>& uniforms,
|
||||||
|
std::string_view name) {
|
||||||
|
return std::find_if(uniforms.begin(), uniforms.end(),
|
||||||
|
[name](const SkMeshSpecification::Uniform& u) {
|
||||||
|
return u.name.equals(name.data(), name.size());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::tuple<bool, SkString>
|
||||||
|
gather_uniforms_and_check_for_main(const SkSL::Program& program,
|
||||||
|
std::vector<Uniform>* uniforms,
|
||||||
|
SkMeshSpecification::Uniform::Flags stage,
|
||||||
|
size_t* offset) {
|
||||||
|
bool foundMain = false;
|
||||||
|
for (const SkSL::ProgramElement* elem : program.elements()) {
|
||||||
if (elem->is<SkSL::FunctionDefinition>()) {
|
if (elem->is<SkSL::FunctionDefinition>()) {
|
||||||
const SkSL::FunctionDefinition& defn = elem->as<SkSL::FunctionDefinition>();
|
const SkSL::FunctionDefinition& defn = elem->as<SkSL::FunctionDefinition>();
|
||||||
const SkSL::FunctionDeclaration& decl = defn.declaration();
|
const SkSL::FunctionDeclaration& decl = defn.declaration();
|
||||||
if (decl.isMain()) {
|
if (decl.isMain()) {
|
||||||
return true;
|
foundMain = true;
|
||||||
|
}
|
||||||
|
} else if (elem->is<SkSL::GlobalVarDeclaration>()) {
|
||||||
|
const SkSL::GlobalVarDeclaration& global = elem->as<SkSL::GlobalVarDeclaration>();
|
||||||
|
const SkSL::VarDeclaration& varDecl = global.declaration()->as<SkSL::VarDeclaration>();
|
||||||
|
const SkSL::Variable& var = varDecl.var();
|
||||||
|
if (var.modifiers().fFlags & SkSL::Modifiers::kUniform_Flag) {
|
||||||
|
auto iter = find_uniform(*uniforms, var.name());
|
||||||
|
const auto& context = *program.fContext;
|
||||||
|
if (iter == uniforms->end()) {
|
||||||
|
uniforms->push_back(SkRuntimeEffectPriv::VarAsUniform(var, context, offset));
|
||||||
|
uniforms->back().flags |= stage;
|
||||||
|
} else {
|
||||||
|
// Check that the two declarations are equivalent
|
||||||
|
size_t ignoredOffset = 0;
|
||||||
|
auto uniform = SkRuntimeEffectPriv::VarAsUniform(var, context, &ignoredOffset);
|
||||||
|
if (uniform.isArray() != iter->isArray() ||
|
||||||
|
uniform.type != iter->type ||
|
||||||
|
uniform.count != iter->count) {
|
||||||
|
return {false, SkStringPrintf("Uniform %s declared with different types"
|
||||||
|
" in vertex and fragment shaders.",
|
||||||
|
iter->name.c_str())};
|
||||||
|
}
|
||||||
|
if (uniform.isColor() != iter->isColor()) {
|
||||||
|
return {false, SkStringPrintf("Uniform %s declared with different color"
|
||||||
|
" layout in vertex and fragment shaders.",
|
||||||
|
iter->name.c_str())};
|
||||||
|
}
|
||||||
|
(*iter).flags |= stage;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
if (!foundMain) {
|
||||||
|
return {false, SkString("No main function found.")};
|
||||||
|
}
|
||||||
|
return {true, {}};
|
||||||
}
|
}
|
||||||
|
|
||||||
using ColorType = SkMeshSpecificationPriv::ColorType;
|
using ColorType = SkMeshSpecificationPriv::ColorType;
|
||||||
@ -278,6 +327,9 @@ SkMeshSpecification::Result SkMeshSpecification::MakeFromSourceWithStructs(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<Uniform> uniforms;
|
||||||
|
size_t offset = 0;
|
||||||
|
|
||||||
SkSL::SharedCompiler compiler;
|
SkSL::SharedCompiler compiler;
|
||||||
SkSL::Program::Settings settings;
|
SkSL::Program::Settings settings;
|
||||||
// TODO(skia:11209): Add SkCapabilities to the API, check against required version.
|
// TODO(skia:11209): Add SkCapabilities to the API, check against required version.
|
||||||
@ -288,9 +340,16 @@ SkMeshSpecification::Result SkMeshSpecification::MakeFromSourceWithStructs(
|
|||||||
if (!vsProgram) {
|
if (!vsProgram) {
|
||||||
RETURN_FAILURE("VS: %s", compiler->errorText().c_str());
|
RETURN_FAILURE("VS: %s", compiler->errorText().c_str());
|
||||||
}
|
}
|
||||||
if (!has_main(*vsProgram)) {
|
|
||||||
RETURN_FAILURE("Vertex shader must have main function.");
|
if (auto [result, error] = gather_uniforms_and_check_for_main(
|
||||||
|
*vsProgram,
|
||||||
|
&uniforms,
|
||||||
|
SkMeshSpecification::Uniform::Flags::kVertex_Flag,
|
||||||
|
&offset);
|
||||||
|
!result) {
|
||||||
|
return {nullptr, std::move(error)};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (SkSL::Analysis::CallsColorTransformIntrinsics(*vsProgram)) {
|
if (SkSL::Analysis::CallsColorTransformIntrinsics(*vsProgram)) {
|
||||||
RETURN_FAILURE("Color transform intrinsics are not permitted in custom mesh shaders");
|
RETURN_FAILURE("Color transform intrinsics are not permitted in custom mesh shaders");
|
||||||
}
|
}
|
||||||
@ -303,9 +362,16 @@ SkMeshSpecification::Result SkMeshSpecification::MakeFromSourceWithStructs(
|
|||||||
if (!fsProgram) {
|
if (!fsProgram) {
|
||||||
RETURN_FAILURE("FS: %s", compiler->errorText().c_str());
|
RETURN_FAILURE("FS: %s", compiler->errorText().c_str());
|
||||||
}
|
}
|
||||||
if (!has_main(*fsProgram)) {
|
|
||||||
RETURN_FAILURE("Fragment shader must have main function.");
|
if (auto [result, error] = gather_uniforms_and_check_for_main(
|
||||||
|
*fsProgram,
|
||||||
|
&uniforms,
|
||||||
|
SkMeshSpecification::Uniform::Flags::kFragment_Flag,
|
||||||
|
&offset);
|
||||||
|
!result) {
|
||||||
|
return {nullptr, std::move(error)};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (SkSL::Analysis::CallsColorTransformIntrinsics(*fsProgram)) {
|
if (SkSL::Analysis::CallsColorTransformIntrinsics(*fsProgram)) {
|
||||||
RETURN_FAILURE("Color transform intrinsics are not permitted in custom mesh shaders");
|
RETURN_FAILURE("Color transform intrinsics are not permitted in custom mesh shaders");
|
||||||
}
|
}
|
||||||
@ -327,6 +393,7 @@ SkMeshSpecification::Result SkMeshSpecification::MakeFromSourceWithStructs(
|
|||||||
return {sk_sp<SkMeshSpecification>(new SkMeshSpecification(attributes,
|
return {sk_sp<SkMeshSpecification>(new SkMeshSpecification(attributes,
|
||||||
stride,
|
stride,
|
||||||
varyings,
|
varyings,
|
||||||
|
std::move(uniforms),
|
||||||
std::move(vsProgram),
|
std::move(vsProgram),
|
||||||
std::move(fsProgram),
|
std::move(fsProgram),
|
||||||
ct,
|
ct,
|
||||||
@ -338,17 +405,19 @@ SkMeshSpecification::Result SkMeshSpecification::MakeFromSourceWithStructs(
|
|||||||
|
|
||||||
SkMeshSpecification::~SkMeshSpecification() = default;
|
SkMeshSpecification::~SkMeshSpecification() = default;
|
||||||
|
|
||||||
SkMeshSpecification::SkMeshSpecification(SkSpan<const Attribute> attributes,
|
SkMeshSpecification::SkMeshSpecification(SkSpan<const Attribute> attributes,
|
||||||
size_t stride,
|
size_t stride,
|
||||||
SkSpan<const Varying> varyings,
|
SkSpan<const Varying> varyings,
|
||||||
std::unique_ptr<SkSL::Program> vs,
|
std::vector<Uniform> uniforms,
|
||||||
std::unique_ptr<SkSL::Program> fs,
|
std::unique_ptr<const SkSL::Program> vs,
|
||||||
ColorType ct,
|
std::unique_ptr<const SkSL::Program> fs,
|
||||||
bool hasLocalCoords,
|
ColorType ct,
|
||||||
sk_sp<SkColorSpace> cs,
|
bool hasLocalCoords,
|
||||||
SkAlphaType at)
|
sk_sp<SkColorSpace> cs,
|
||||||
|
SkAlphaType at)
|
||||||
: fAttributes(attributes.begin(), attributes.end())
|
: fAttributes(attributes.begin(), attributes.end())
|
||||||
, fVaryings(varyings.begin(), varyings.end())
|
, fVaryings(varyings.begin(), varyings.end())
|
||||||
|
, fUniforms(std::move(uniforms))
|
||||||
, fVS(std::move(vs))
|
, fVS(std::move(vs))
|
||||||
, fFS(std::move(fs))
|
, fFS(std::move(fs))
|
||||||
, fStride(stride)
|
, fStride(stride)
|
||||||
@ -376,6 +445,22 @@ SkMeshSpecification::SkMeshSpecification(SkSpan<const Attribute> attribut
|
|||||||
fHash = SkOpts::hash_fn(&atInt, sizeof(atInt), fHash);
|
fHash = SkOpts::hash_fn(&atInt, sizeof(atInt), fHash);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t SkMeshSpecification::uniformSize() const {
|
||||||
|
return fUniforms.empty() ? 0
|
||||||
|
: SkAlign4(fUniforms.back().offset + fUniforms.back().sizeInBytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
const Uniform* SkMeshSpecification::findUniform(const char* name) const {
|
||||||
|
SkASSERT(name);
|
||||||
|
size_t len = strlen(name);
|
||||||
|
auto iter = std::find_if(fUniforms.begin(), fUniforms.end(), [name, len] (const Uniform& u) {
|
||||||
|
return u.name.equals(name, len);
|
||||||
|
});
|
||||||
|
return iter == fUniforms.end() ? nullptr : &(*iter);
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
SkMesh::SkMesh() = default;
|
SkMesh::SkMesh() = default;
|
||||||
SkMesh::~SkMesh() = default;
|
SkMesh::~SkMesh() = default;
|
||||||
|
|
||||||
|
@ -59,30 +59,6 @@
|
|||||||
|
|
||||||
using ChildType = SkRuntimeEffect::ChildType;
|
using ChildType = SkRuntimeEffect::ChildType;
|
||||||
|
|
||||||
static bool flattenable_is_valid_as_child(const SkFlattenable* f) {
|
|
||||||
if (!f) { return true; }
|
|
||||||
switch (f->getFlattenableType()) {
|
|
||||||
case SkFlattenable::kSkShader_Type:
|
|
||||||
case SkFlattenable::kSkColorFilter_Type:
|
|
||||||
case SkFlattenable::kSkBlender_Type:
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SkRuntimeEffect::ChildPtr::ChildPtr(sk_sp<SkFlattenable> f) : fChild(std::move(f)) {
|
|
||||||
SkASSERT(flattenable_is_valid_as_child(fChild.get()));
|
|
||||||
}
|
|
||||||
|
|
||||||
static sk_sp<SkSL::SkVMDebugTrace> make_skvm_debug_trace(SkRuntimeEffect* effect,
|
|
||||||
const SkIPoint& coord) {
|
|
||||||
auto debugTrace = sk_make_sp<SkSL::SkVMDebugTrace>();
|
|
||||||
debugTrace->setSource(effect->source());
|
|
||||||
debugTrace->setTraceCoord(coord);
|
|
||||||
return debugTrace;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool init_uniform_type(const SkSL::Context& ctx,
|
static bool init_uniform_type(const SkSL::Context& ctx,
|
||||||
const SkSL::Type* type,
|
const SkSL::Type* type,
|
||||||
SkRuntimeEffect::Uniform* v) {
|
SkRuntimeEffect::Uniform* v) {
|
||||||
@ -110,6 +86,60 @@ static bool init_uniform_type(const SkSL::Context& ctx,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SkRuntimeEffect::Uniform SkRuntimeEffectPriv::VarAsUniform(const SkSL::Variable& var,
|
||||||
|
const SkSL::Context& context,
|
||||||
|
size_t* offset) {
|
||||||
|
using Uniform = SkRuntimeEffect::Uniform;
|
||||||
|
SkASSERT(var.modifiers().fFlags & SkSL::Modifiers::kUniform_Flag);
|
||||||
|
Uniform uni;
|
||||||
|
uni.name = SkString(var.name());
|
||||||
|
uni.flags = 0;
|
||||||
|
uni.count = 1;
|
||||||
|
|
||||||
|
const SkSL::Type* type = &var.type();
|
||||||
|
if (type->isArray()) {
|
||||||
|
uni.flags |= Uniform::kArray_Flag;
|
||||||
|
uni.count = type->columns();
|
||||||
|
type = &type->componentType();
|
||||||
|
}
|
||||||
|
|
||||||
|
SkAssertResult(init_uniform_type(context, type, &uni));
|
||||||
|
if (var.modifiers().fLayout.fFlags & SkSL::Layout::Flag::kColor_Flag) {
|
||||||
|
uni.flags |= Uniform::kColor_Flag;
|
||||||
|
}
|
||||||
|
|
||||||
|
uni.offset = *offset;
|
||||||
|
*offset += uni.sizeInBytes();
|
||||||
|
SkASSERT(SkIsAlign4(*offset));
|
||||||
|
return uni;
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
static bool flattenable_is_valid_as_child(const SkFlattenable* f) {
|
||||||
|
if (!f) { return true; }
|
||||||
|
switch (f->getFlattenableType()) {
|
||||||
|
case SkFlattenable::kSkShader_Type:
|
||||||
|
case SkFlattenable::kSkColorFilter_Type:
|
||||||
|
case SkFlattenable::kSkBlender_Type:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SkRuntimeEffect::ChildPtr::ChildPtr(sk_sp<SkFlattenable> f) : fChild(std::move(f)) {
|
||||||
|
SkASSERT(flattenable_is_valid_as_child(fChild.get()));
|
||||||
|
}
|
||||||
|
|
||||||
|
static sk_sp<SkSL::SkVMDebugTrace> make_skvm_debug_trace(SkRuntimeEffect* effect,
|
||||||
|
const SkIPoint& coord) {
|
||||||
|
auto debugTrace = sk_make_sp<SkSL::SkVMDebugTrace>();
|
||||||
|
debugTrace->setSource(effect->source());
|
||||||
|
debugTrace->setTraceCoord(coord);
|
||||||
|
return debugTrace;
|
||||||
|
}
|
||||||
|
|
||||||
static ChildType child_type(const SkSL::Type& type) {
|
static ChildType child_type(const SkSL::Type& type) {
|
||||||
switch (type.typeKind()) {
|
switch (type.typeKind()) {
|
||||||
case SkSL::Type::TypeKind::kBlender: return ChildType::kBlender;
|
case SkSL::Type::TypeKind::kBlender: return ChildType::kBlender;
|
||||||
@ -324,28 +354,7 @@ SkRuntimeEffect::Result SkRuntimeEffect::MakeInternal(std::unique_ptr<SkSL::Prog
|
|||||||
}
|
}
|
||||||
// 'uniform' variables
|
// 'uniform' variables
|
||||||
else if (var.modifiers().fFlags & SkSL::Modifiers::kUniform_Flag) {
|
else if (var.modifiers().fFlags & SkSL::Modifiers::kUniform_Flag) {
|
||||||
Uniform uni;
|
uniforms.push_back(SkRuntimeEffectPriv::VarAsUniform(var, ctx, &offset));
|
||||||
uni.name = SkString(var.name());
|
|
||||||
uni.flags = 0;
|
|
||||||
uni.count = 1;
|
|
||||||
|
|
||||||
const SkSL::Type* type = &var.type();
|
|
||||||
if (type->isArray()) {
|
|
||||||
uni.flags |= Uniform::kArray_Flag;
|
|
||||||
uni.count = type->columns();
|
|
||||||
type = &type->componentType();
|
|
||||||
}
|
|
||||||
|
|
||||||
SkAssertResult(init_uniform_type(ctx, type, &uni));
|
|
||||||
if (var.modifiers().fLayout.fFlags & SkSL::Layout::Flag::kColor_Flag) {
|
|
||||||
uni.flags |= Uniform::kColor_Flag;
|
|
||||||
}
|
|
||||||
|
|
||||||
uni.offset = offset;
|
|
||||||
offset += uni.sizeInBytes();
|
|
||||||
SkASSERT(SkIsAlign4(offset));
|
|
||||||
|
|
||||||
uniforms.push_back(uni);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,11 @@
|
|||||||
|
|
||||||
#ifdef SK_ENABLE_SKSL
|
#ifdef SK_ENABLE_SKSL
|
||||||
|
|
||||||
|
namespace SkSL {
|
||||||
|
class Variable;
|
||||||
|
class Context;
|
||||||
|
}
|
||||||
|
|
||||||
class SkRuntimeEffectPriv {
|
class SkRuntimeEffectPriv {
|
||||||
public:
|
public:
|
||||||
// Helper function when creating an effect for a GrSkSLFP that verifies an effect will
|
// Helper function when creating an effect for a GrSkSLFP that verifies an effect will
|
||||||
@ -33,6 +38,10 @@ public:
|
|||||||
static void UsePrivateRTShaderModule(SkRuntimeEffect::Options* options) {
|
static void UsePrivateRTShaderModule(SkRuntimeEffect::Options* options) {
|
||||||
options->usePrivateRTShaderModule = true;
|
options->usePrivateRTShaderModule = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static SkRuntimeEffect::Uniform VarAsUniform(const SkSL::Variable&,
|
||||||
|
const SkSL::Context&,
|
||||||
|
size_t* offset);
|
||||||
};
|
};
|
||||||
|
|
||||||
// These internal APIs for creating runtime effects vary from the public API in two ways:
|
// These internal APIs for creating runtime effects vary from the public API in two ways:
|
||||||
|
@ -147,10 +147,9 @@ void VarDeclaration::ErrorCheck(const Context& context,
|
|||||||
context.fErrors->error(pos,
|
context.fErrors->error(pos,
|
||||||
"variables of type '" + baseType->displayName() + "' must be uniform");
|
"variables of type '" + baseType->displayName() + "' must be uniform");
|
||||||
}
|
}
|
||||||
if (modifiers.fFlags & Modifiers::kUniform_Flag &&
|
if (baseType->isEffectChild() && (context.fConfig->fKind == ProgramKind::kMeshVertex ||
|
||||||
(context.fConfig->fKind == ProgramKind::kMeshVertex ||
|
context.fConfig->fKind == ProgramKind::kMeshFragment)) {
|
||||||
context.fConfig->fKind == ProgramKind::kMeshFragment)) {
|
context.fErrors->error(pos, "effects are not permitted in custom mesh shaders");
|
||||||
context.fErrors->error(pos, "uniforms are not permitted in custom mesh shaders");
|
|
||||||
}
|
}
|
||||||
if (modifiers.fLayout.fFlags & Layout::kColor_Flag) {
|
if (modifiers.fLayout.fFlags & Layout::kColor_Flag) {
|
||||||
if (!ProgramConfig::IsRuntimeEffect(context.fConfig->fKind)) {
|
if (!ProgramConfig::IsRuntimeEffect(context.fConfig->fKind)) {
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
#include "include/core/SkCanvas.h"
|
#include "include/core/SkCanvas.h"
|
||||||
#include "include/core/SkMesh.h"
|
#include "include/core/SkMesh.h"
|
||||||
|
#include "src/core/SkZip.h"
|
||||||
#include "tests/Test.h"
|
#include "tests/Test.h"
|
||||||
|
|
||||||
using Attribute = SkMeshSpecification::Attribute;
|
using Attribute = SkMeshSpecification::Attribute;
|
||||||
@ -84,15 +85,19 @@ static bool check_for_failure(skiatest::Reporter* r,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool check_for_success(skiatest::Reporter* r,
|
static bool check_for_success(skiatest::Reporter* r,
|
||||||
SkSpan<const Attribute> attributes,
|
SkSpan<const Attribute> attributes,
|
||||||
size_t stride,
|
size_t stride,
|
||||||
SkSpan<const Varying> varyings,
|
SkSpan<const Varying> varyings,
|
||||||
const SkString& vs,
|
const SkString& vs,
|
||||||
const SkString& fs) {
|
const SkString& fs,
|
||||||
auto [spec, error] = SkMeshSpecification::Make(attributes, stride, varyings, vs, fs);
|
sk_sp<SkMeshSpecification>* spec = nullptr) {
|
||||||
if (spec) {
|
auto [s, error] = SkMeshSpecification::Make(attributes, stride, varyings, vs, fs);
|
||||||
|
if (s) {
|
||||||
REPORTER_ASSERT(r, error.isEmpty());
|
REPORTER_ASSERT(r, error.isEmpty());
|
||||||
|
if (spec) {
|
||||||
|
*spec = std::move(s);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
ERRORF(r,
|
ERRORF(r,
|
||||||
@ -233,10 +238,9 @@ static void test_float4_color(skiatest::Reporter* r) {
|
|||||||
kFloat4FS);
|
kFloat4FS);
|
||||||
}
|
}
|
||||||
|
|
||||||
// We don't allow value or child uniforms in custom meshes currently.
|
// We don't allow child effects in custom meshes currently.
|
||||||
static void test_bad_globals(skiatest::Reporter* r) {
|
static void test_bad_globals(skiatest::Reporter* r) {
|
||||||
static constexpr const char* kBadGlobals[] {
|
static constexpr const char* kBadGlobals[] {
|
||||||
"uniform float3 uni;"
|
|
||||||
"uniform shader myshader;"
|
"uniform shader myshader;"
|
||||||
};
|
};
|
||||||
for (const auto& global : kBadGlobals) {
|
for (const auto& global : kBadGlobals) {
|
||||||
@ -265,6 +269,270 @@ static void test_bad_globals(skiatest::Reporter* r) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_good_uniforms(skiatest::Reporter* r) {
|
||||||
|
using Uniform = SkMeshSpecification::Uniform;
|
||||||
|
using Type = Uniform::Type;
|
||||||
|
using Flags = Uniform::Flags;
|
||||||
|
|
||||||
|
constexpr Flags kVS = Uniform::kVertex_Flag;
|
||||||
|
constexpr Flags kFS = Uniform::kFragment_Flag;
|
||||||
|
constexpr Flags kColor = Uniform::kColor_Flag;
|
||||||
|
|
||||||
|
auto make_uni = [](Type type, const char* name, size_t offset, uint32_t flags, int count = 0) {
|
||||||
|
if (count) {
|
||||||
|
return Uniform{SkString(name), offset, type, count, flags | Uniform::kArray_Flag};
|
||||||
|
} else {
|
||||||
|
SkASSERT(!(flags & Uniform::kArray_Flag));
|
||||||
|
return Uniform{SkString(name), offset, type, 1, flags};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Each test case is a set of VS and FS uniform declarations followed and the expected output
|
||||||
|
// of SkMeshSpecification::uniforms().
|
||||||
|
struct {
|
||||||
|
const std::vector<const char*> vsUniformDecls;
|
||||||
|
const std::vector<const char*> fsUniformDecls;
|
||||||
|
const std::vector<SkMeshSpecification::Uniform> expectations;
|
||||||
|
} static kTestCases[] {
|
||||||
|
// A single VS uniform.
|
||||||
|
{
|
||||||
|
{
|
||||||
|
"uniform float x;"
|
||||||
|
},
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
make_uni(Type::kFloat, "x", 0, kVS)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// A single FS uniform.
|
||||||
|
{
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
"uniform float2 v;"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
make_uni(Type::kFloat2, "v", 0, kFS)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// A single uniform in both that uses color layout.
|
||||||
|
{
|
||||||
|
{
|
||||||
|
"layout(color) uniform float4 color;",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"layout(color) uniform float4 color;",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
make_uni(Type::kFloat4, "color", 0, kVS|kFS|kColor)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// A shared uniform after an unshared vertex uniform
|
||||||
|
{
|
||||||
|
{
|
||||||
|
"layout(color) uniform float4 color;",
|
||||||
|
" uniform float x[5];",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uniform float x[5];",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
make_uni(Type::kFloat4, "color", 0, kVS|kColor, 0),
|
||||||
|
make_uni(Type::kFloat , "x" , 16, kVS|kFS , 5)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// A shared uniform before an unshared vertex uniform
|
||||||
|
{
|
||||||
|
{
|
||||||
|
"uniform half x[2];",
|
||||||
|
"uniform int y;",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uniform half x[2];",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
make_uni(Type::kFloat, "x", 0, kVS|kFS, 2),
|
||||||
|
make_uni(Type::kInt, "y", 8, kVS , 0)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// A shared uniform after an unshared fragment uniform
|
||||||
|
{
|
||||||
|
{
|
||||||
|
"uniform float3x3 m;",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uniform int2 i2;",
|
||||||
|
"uniform float3x3 m;",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
make_uni(Type::kFloat3x3, "m" , 0, kVS|kFS),
|
||||||
|
make_uni(Type::kInt2 , "i2", 36, kFS )
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// A shared uniform before an unshared fragment uniform
|
||||||
|
{
|
||||||
|
{
|
||||||
|
"uniform half4x4 m[4];",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uniform half4x4 m[4];",
|
||||||
|
"uniform int3 i3[1];",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
make_uni(Type::kFloat4x4, "m", 0, kVS|kFS, 4),
|
||||||
|
make_uni(Type::kInt3, "i3", 256, kFS , 1)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Complex case with 2 shared uniforms that are declared in the opposite order.
|
||||||
|
{
|
||||||
|
{
|
||||||
|
"uniform float x;"
|
||||||
|
"uniform half4x4 m[4];", // shared
|
||||||
|
"uniform int2 i2[2];"
|
||||||
|
"uniform float3 v[8];" // shared
|
||||||
|
"uniform int3 i3;"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uniform float y;"
|
||||||
|
"uniform float3 v[8];" // shared
|
||||||
|
"uniform int4 i4[2];"
|
||||||
|
"uniform half4x4 m[4];", // shared
|
||||||
|
"uniform int i;"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
make_uni(Type::kFloat, "x" , 0, kVS , 0),
|
||||||
|
make_uni(Type::kFloat4x4, "m" , 4, kVS|kFS, 4),
|
||||||
|
make_uni(Type::kInt2, "i2", 260, kVS , 2),
|
||||||
|
make_uni(Type::kFloat3, "v" , 276, kVS|kFS, 8),
|
||||||
|
make_uni(Type::kInt3, "i3", 372, kVS , 0),
|
||||||
|
make_uni(Type::kFloat, "y" , 384, kFS , 0),
|
||||||
|
make_uni(Type::kInt4, "i4", 388, kFS , 2),
|
||||||
|
make_uni(Type::kInt, "i" , 420, kFS , 0),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const auto& c : kTestCases) {
|
||||||
|
SkString vs = kValidVS;
|
||||||
|
SkString unis;
|
||||||
|
for (const auto u : c.vsUniformDecls) {
|
||||||
|
unis.append(u);
|
||||||
|
}
|
||||||
|
vs.prepend(unis);
|
||||||
|
|
||||||
|
SkString fs = kValidFSes[0];
|
||||||
|
unis = {};
|
||||||
|
for (const auto u : c.fsUniformDecls) {
|
||||||
|
unis.append(u);
|
||||||
|
}
|
||||||
|
fs.prepend(unis);
|
||||||
|
|
||||||
|
auto attrs = SkMakeSpan(kValidAttrs , SK_ARRAY_COUNT(kValidAttrs) );
|
||||||
|
auto varys = SkMakeSpan(kValidVaryings, SK_ARRAY_COUNT(kValidVaryings));
|
||||||
|
sk_sp<SkMeshSpecification> spec;
|
||||||
|
if (!check_for_success(r, attrs, kValidStride, varys, vs, fs, &spec)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SkString desc = make_description(attrs, kValidStride, varys, vs, fs);
|
||||||
|
auto uniforms = spec->uniforms();
|
||||||
|
if (uniforms.size() != c.expectations.size()) {
|
||||||
|
ERRORF(r,
|
||||||
|
"Expected %zu uniforms but actually %zu:\n%s",
|
||||||
|
c.expectations.size(),
|
||||||
|
uniforms.size(),
|
||||||
|
desc.c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (const auto& [actual, expected] : SkMakeZip(uniforms, c.expectations)) {
|
||||||
|
if (actual.name != expected.name) {
|
||||||
|
ERRORF(r,
|
||||||
|
"Actual uniform name (%s) does not match expected name (%s)",
|
||||||
|
actual.name.c_str(),
|
||||||
|
expected.name.c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const char* name = actual.name.c_str();
|
||||||
|
if (actual.type != expected.type) {
|
||||||
|
ERRORF(r,
|
||||||
|
"Uniform %s: Actual type (%d) does not match expected type (%d)",
|
||||||
|
name,
|
||||||
|
static_cast<int>(actual.type),
|
||||||
|
static_cast<int>(expected.type));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (actual.count != expected.count) {
|
||||||
|
ERRORF(r,
|
||||||
|
"Uniform %s: Actual count (%d) does not match expected count (%d)",
|
||||||
|
name,
|
||||||
|
actual.count,
|
||||||
|
expected.count);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (actual.flags != expected.flags) {
|
||||||
|
ERRORF(r,
|
||||||
|
"Uniform %s: Actual flags (0x%04x) do not match expected flags (0x%04x)",
|
||||||
|
name,
|
||||||
|
actual.flags,
|
||||||
|
expected.flags);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (actual.offset != expected.offset) {
|
||||||
|
ERRORF(r,
|
||||||
|
"Uniform %s: Actual offset (%zu) does not match expected offset (%zu)",
|
||||||
|
name,
|
||||||
|
actual.offset,
|
||||||
|
expected.offset);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_bad_uniforms(skiatest::Reporter* r) {
|
||||||
|
// We assume general uniform declarations are broadly tested generically in SkSL. Here we are
|
||||||
|
// concerned with agreement between VS and FS declarations, which is a unique aspect of
|
||||||
|
// SkMeshSpecification.
|
||||||
|
|
||||||
|
// Each test case is a fs and vs uniform declaration with the same name but some other
|
||||||
|
// difference that should make them incompatible.
|
||||||
|
static std::tuple<const char*, const char*> kTestCases[]{
|
||||||
|
// different types
|
||||||
|
{"uniform float x;", "uniform int x;"},
|
||||||
|
// array vs non-array
|
||||||
|
{"uniform float2x2 m[1];", "uniform float2x2 m;"},
|
||||||
|
// array count mismatch
|
||||||
|
{"uniform int3 i[1];", "uniform int3 i[2];"},
|
||||||
|
// layout difference
|
||||||
|
{"layout(color) uniform float4 color;", "uniform float4 color;"},
|
||||||
|
};
|
||||||
|
|
||||||
|
for (bool reverse : {false, true}) {
|
||||||
|
for (auto [u1, u2] : kTestCases) {
|
||||||
|
if (reverse) {
|
||||||
|
using std::swap;
|
||||||
|
swap(u1, u2);
|
||||||
|
}
|
||||||
|
SkString vs = kValidVS;
|
||||||
|
vs.prepend(u1);
|
||||||
|
|
||||||
|
SkString fs = kValidFSes[0];
|
||||||
|
fs.prepend(u2);
|
||||||
|
|
||||||
|
auto attrs = SkMakeSpan(kValidAttrs , SK_ARRAY_COUNT(kValidAttrs) );
|
||||||
|
auto varys = SkMakeSpan(kValidVaryings, SK_ARRAY_COUNT(kValidVaryings));
|
||||||
|
if (!check_for_failure(r, attrs, kValidStride, varys, vs, fs)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void test_no_main(skiatest::Reporter* r) {
|
static void test_no_main(skiatest::Reporter* r) {
|
||||||
static const SkString kHelper{"float2 swiz(float2 x) { return z.yx; }"};
|
static const SkString kHelper{"float2 swiz(float2 x) { return z.yx; }"};
|
||||||
|
|
||||||
@ -516,6 +784,8 @@ DEF_TEST(MeshSpec, reporter) {
|
|||||||
test_bad_sig(reporter);
|
test_bad_sig(reporter);
|
||||||
test_float4_color(reporter);
|
test_float4_color(reporter);
|
||||||
test_bad_globals(reporter);
|
test_bad_globals(reporter);
|
||||||
|
test_good_uniforms(reporter);
|
||||||
|
test_bad_uniforms(reporter);
|
||||||
test_no_main(reporter);
|
test_no_main(reporter);
|
||||||
test_zero_attrs(reporter);
|
test_zero_attrs(reporter);
|
||||||
test_zero_varyings(reporter);
|
test_zero_varyings(reporter);
|
||||||
|
Loading…
Reference in New Issue
Block a user