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/SkSpan.h"
|
||||
#include "include/core/SkString.h"
|
||||
#include "include/effects/SkRuntimeEffect.h"
|
||||
|
||||
#include <memory>
|
||||
#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
|
||||
* 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.
|
||||
*
|
||||
* 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> {
|
||||
public:
|
||||
@ -86,6 +91,8 @@ public:
|
||||
SkString name;
|
||||
};
|
||||
|
||||
using Uniform = SkRuntimeEffect::Uniform;
|
||||
|
||||
~SkMeshSpecification();
|
||||
|
||||
struct Result {
|
||||
@ -136,6 +143,13 @@ public:
|
||||
|
||||
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; }
|
||||
|
||||
private:
|
||||
@ -158,8 +172,9 @@ private:
|
||||
SkMeshSpecification(SkSpan<const Attribute>,
|
||||
size_t,
|
||||
SkSpan<const Varying>,
|
||||
std::unique_ptr<SkSL::Program>,
|
||||
std::unique_ptr<SkSL::Program>,
|
||||
std::vector<Uniform> uniforms,
|
||||
std::unique_ptr<const SkSL::Program>,
|
||||
std::unique_ptr<const SkSL::Program>,
|
||||
ColorType,
|
||||
bool hasLocalCoords,
|
||||
sk_sp<SkColorSpace>,
|
||||
@ -171,16 +186,17 @@ private:
|
||||
SkMeshSpecification& operator=(const SkMeshSpecification&) = delete;
|
||||
SkMeshSpecification& operator=(SkMeshSpecification&&) = delete;
|
||||
|
||||
const std::vector<Attribute> fAttributes;
|
||||
const std::vector<Varying> fVaryings;
|
||||
std::unique_ptr<SkSL::Program> fVS;
|
||||
std::unique_ptr<SkSL::Program> fFS;
|
||||
size_t fStride;
|
||||
uint32_t fHash;
|
||||
ColorType fColorType;
|
||||
bool fHasLocalCoords;
|
||||
sk_sp<SkColorSpace> fColorSpace;
|
||||
SkAlphaType fAlphaType;
|
||||
const std::vector<Attribute> fAttributes;
|
||||
const std::vector<Varying> fVaryings;
|
||||
const std::vector<Uniform> fUniforms;
|
||||
const std::unique_ptr<const SkSL::Program> fVS;
|
||||
const std::unique_ptr<const SkSL::Program> fFS;
|
||||
const size_t fStride;
|
||||
uint32_t fHash;
|
||||
const ColorType fColorType;
|
||||
const bool fHasLocalCoords;
|
||||
const sk_sp<SkColorSpace> fColorSpace;
|
||||
const SkAlphaType fAlphaType;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -68,13 +68,21 @@ public:
|
||||
};
|
||||
|
||||
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,
|
||||
|
||||
// Uniform is declared with layout(color). Colors should be supplied as unpremultiplied,
|
||||
// extended-range (unclamped) sRGB (ie SkColor4f). The uniform will be automatically
|
||||
// transformed to unpremultiplied extended-range working-space colors.
|
||||
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;
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "include/private/SkSLProgramElement.h"
|
||||
#include "include/private/SkSLProgramKind.h"
|
||||
#include "src/core/SkMeshPriv.h"
|
||||
#include "src/core/SkRuntimeEffectPriv.h"
|
||||
#include "src/core/SkSafeMath.h"
|
||||
#include "src/sksl/SkSLAnalysis.h"
|
||||
#include "src/sksl/SkSLBuiltinTypes.h"
|
||||
@ -24,6 +25,7 @@
|
||||
#include "src/sksl/ir/SkSLFunctionDefinition.h"
|
||||
#include "src/sksl/ir/SkSLProgram.h"
|
||||
#include "src/sksl/ir/SkSLType.h"
|
||||
#include "src/sksl/ir/SkSLVarDeclarations.h"
|
||||
#include "src/sksl/ir/SkSLVariable.h"
|
||||
|
||||
#include <locale>
|
||||
@ -44,17 +46,64 @@ using VertexBuffer = SkMesh::VertexBuffer;
|
||||
|
||||
#define RETURN_SUCCESS return std::make_tuple(true, SkString{})
|
||||
|
||||
static bool has_main(const SkSL::Program& p) {
|
||||
for (const SkSL::ProgramElement* elem : p.elements()) {
|
||||
using Uniform = SkMeshSpecification::Uniform;
|
||||
|
||||
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>()) {
|
||||
const SkSL::FunctionDefinition& defn = elem->as<SkSL::FunctionDefinition>();
|
||||
const SkSL::FunctionDeclaration& decl = defn.declaration();
|
||||
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;
|
||||
@ -278,6 +327,9 @@ SkMeshSpecification::Result SkMeshSpecification::MakeFromSourceWithStructs(
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<Uniform> uniforms;
|
||||
size_t offset = 0;
|
||||
|
||||
SkSL::SharedCompiler compiler;
|
||||
SkSL::Program::Settings settings;
|
||||
// TODO(skia:11209): Add SkCapabilities to the API, check against required version.
|
||||
@ -288,9 +340,16 @@ SkMeshSpecification::Result SkMeshSpecification::MakeFromSourceWithStructs(
|
||||
if (!vsProgram) {
|
||||
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)) {
|
||||
RETURN_FAILURE("Color transform intrinsics are not permitted in custom mesh shaders");
|
||||
}
|
||||
@ -303,9 +362,16 @@ SkMeshSpecification::Result SkMeshSpecification::MakeFromSourceWithStructs(
|
||||
if (!fsProgram) {
|
||||
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)) {
|
||||
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,
|
||||
stride,
|
||||
varyings,
|
||||
std::move(uniforms),
|
||||
std::move(vsProgram),
|
||||
std::move(fsProgram),
|
||||
ct,
|
||||
@ -338,17 +405,19 @@ SkMeshSpecification::Result SkMeshSpecification::MakeFromSourceWithStructs(
|
||||
|
||||
SkMeshSpecification::~SkMeshSpecification() = default;
|
||||
|
||||
SkMeshSpecification::SkMeshSpecification(SkSpan<const Attribute> attributes,
|
||||
size_t stride,
|
||||
SkSpan<const Varying> varyings,
|
||||
std::unique_ptr<SkSL::Program> vs,
|
||||
std::unique_ptr<SkSL::Program> fs,
|
||||
ColorType ct,
|
||||
bool hasLocalCoords,
|
||||
sk_sp<SkColorSpace> cs,
|
||||
SkAlphaType at)
|
||||
SkMeshSpecification::SkMeshSpecification(SkSpan<const Attribute> attributes,
|
||||
size_t stride,
|
||||
SkSpan<const Varying> varyings,
|
||||
std::vector<Uniform> uniforms,
|
||||
std::unique_ptr<const SkSL::Program> vs,
|
||||
std::unique_ptr<const SkSL::Program> fs,
|
||||
ColorType ct,
|
||||
bool hasLocalCoords,
|
||||
sk_sp<SkColorSpace> cs,
|
||||
SkAlphaType at)
|
||||
: fAttributes(attributes.begin(), attributes.end())
|
||||
, fVaryings(varyings.begin(), varyings.end())
|
||||
, fUniforms(std::move(uniforms))
|
||||
, fVS(std::move(vs))
|
||||
, fFS(std::move(fs))
|
||||
, fStride(stride)
|
||||
@ -376,6 +445,22 @@ SkMeshSpecification::SkMeshSpecification(SkSpan<const Attribute> attribut
|
||||
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;
|
||||
|
||||
|
@ -59,30 +59,6 @@
|
||||
|
||||
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,
|
||||
const SkSL::Type* type,
|
||||
SkRuntimeEffect::Uniform* v) {
|
||||
@ -110,6 +86,60 @@ static bool init_uniform_type(const SkSL::Context& ctx,
|
||||
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) {
|
||||
switch (type.typeKind()) {
|
||||
case SkSL::Type::TypeKind::kBlender: return ChildType::kBlender;
|
||||
@ -324,28 +354,7 @@ SkRuntimeEffect::Result SkRuntimeEffect::MakeInternal(std::unique_ptr<SkSL::Prog
|
||||
}
|
||||
// 'uniform' variables
|
||||
else if (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(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);
|
||||
uniforms.push_back(SkRuntimeEffectPriv::VarAsUniform(var, ctx, &offset));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,6 +16,11 @@
|
||||
|
||||
#ifdef SK_ENABLE_SKSL
|
||||
|
||||
namespace SkSL {
|
||||
class Variable;
|
||||
class Context;
|
||||
}
|
||||
|
||||
class SkRuntimeEffectPriv {
|
||||
public:
|
||||
// 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) {
|
||||
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:
|
||||
|
@ -147,10 +147,9 @@ void VarDeclaration::ErrorCheck(const Context& context,
|
||||
context.fErrors->error(pos,
|
||||
"variables of type '" + baseType->displayName() + "' must be uniform");
|
||||
}
|
||||
if (modifiers.fFlags & Modifiers::kUniform_Flag &&
|
||||
(context.fConfig->fKind == ProgramKind::kMeshVertex ||
|
||||
context.fConfig->fKind == ProgramKind::kMeshFragment)) {
|
||||
context.fErrors->error(pos, "uniforms are not permitted in custom mesh shaders");
|
||||
if (baseType->isEffectChild() && (context.fConfig->fKind == ProgramKind::kMeshVertex ||
|
||||
context.fConfig->fKind == ProgramKind::kMeshFragment)) {
|
||||
context.fErrors->error(pos, "effects are not permitted in custom mesh shaders");
|
||||
}
|
||||
if (modifiers.fLayout.fFlags & Layout::kColor_Flag) {
|
||||
if (!ProgramConfig::IsRuntimeEffect(context.fConfig->fKind)) {
|
||||
|
@ -7,6 +7,7 @@
|
||||
|
||||
#include "include/core/SkCanvas.h"
|
||||
#include "include/core/SkMesh.h"
|
||||
#include "src/core/SkZip.h"
|
||||
#include "tests/Test.h"
|
||||
|
||||
using Attribute = SkMeshSpecification::Attribute;
|
||||
@ -84,15 +85,19 @@ static bool check_for_failure(skiatest::Reporter* r,
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool check_for_success(skiatest::Reporter* r,
|
||||
SkSpan<const Attribute> attributes,
|
||||
size_t stride,
|
||||
SkSpan<const Varying> varyings,
|
||||
const SkString& vs,
|
||||
const SkString& fs) {
|
||||
auto [spec, error] = SkMeshSpecification::Make(attributes, stride, varyings, vs, fs);
|
||||
if (spec) {
|
||||
static bool check_for_success(skiatest::Reporter* r,
|
||||
SkSpan<const Attribute> attributes,
|
||||
size_t stride,
|
||||
SkSpan<const Varying> varyings,
|
||||
const SkString& vs,
|
||||
const SkString& fs,
|
||||
sk_sp<SkMeshSpecification>* spec = nullptr) {
|
||||
auto [s, error] = SkMeshSpecification::Make(attributes, stride, varyings, vs, fs);
|
||||
if (s) {
|
||||
REPORTER_ASSERT(r, error.isEmpty());
|
||||
if (spec) {
|
||||
*spec = std::move(s);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
ERRORF(r,
|
||||
@ -233,10 +238,9 @@ static void test_float4_color(skiatest::Reporter* r) {
|
||||
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 constexpr const char* kBadGlobals[] {
|
||||
"uniform float3 uni;"
|
||||
"uniform shader myshader;"
|
||||
};
|
||||
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 const SkString kHelper{"float2 swiz(float2 x) { return z.yx; }"};
|
||||
|
||||
@ -516,6 +784,8 @@ DEF_TEST(MeshSpec, reporter) {
|
||||
test_bad_sig(reporter);
|
||||
test_float4_color(reporter);
|
||||
test_bad_globals(reporter);
|
||||
test_good_uniforms(reporter);
|
||||
test_bad_uniforms(reporter);
|
||||
test_no_main(reporter);
|
||||
test_zero_attrs(reporter);
|
||||
test_zero_varyings(reporter);
|
||||
|
Loading…
Reference in New Issue
Block a user