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:
Brian Salomon 2022-05-31 14:15:46 -04:00 committed by SkCQ
parent 479fa1c9ca
commit 586e6354aa
7 changed files with 486 additions and 90 deletions

View File

@ -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;
};
/**

View File

@ -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;

View File

@ -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;

View File

@ -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));
}
}
}

View File

@ -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:

View File

@ -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)) {

View File

@ -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);