blob: 7e4c5c3f1bbe96faec4d78ea9c9c0040846a4490 [file] [log] [blame]
/*
* Copyright 2022 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "src/base/SkFloatBits.h"
#include "src/base/SkHalf.h"
#include "src/core/SkSLTypeShared.h"
#include "src/gpu/graphite/PipelineData.h"
#include "src/gpu/graphite/Uniform.h"
#include "src/gpu/graphite/UniformManager.h"
#include "tests/Test.h"
using namespace skgpu::graphite;
static constexpr Layout kLayouts[] = {
Layout::kStd140,
Layout::kStd140_F16,
Layout::kStd430,
Layout::kStd430_F16,
Layout::kMetal,
};
// This list excludes SkSLTypes that we don't support in uniforms, like Bool, UInt or UShort.
static constexpr SkSLType kTypes[] = {
SkSLType::kFloat, SkSLType::kFloat2, SkSLType::kFloat3, SkSLType::kFloat4, //
SkSLType::kHalf, SkSLType::kHalf2, SkSLType::kHalf3, SkSLType::kHalf4, //
SkSLType::kInt, SkSLType::kInt2, SkSLType::kInt3, SkSLType::kInt4, //
SkSLType::kFloat2x2, SkSLType::kFloat3x3, SkSLType::kFloat4x4, //
SkSLType::kHalf2x2, SkSLType::kHalf3x3, SkSLType::kHalf4x4,
};
static constexpr float kFloats[16] = { 1.0f, 2.0f, 3.0f, 4.0f,
5.0f, 6.0f, 7.0f, 8.0f,
9.0f, 10.0f, 11.0f, 12.0f,
13.0f, 14.0f, 15.0f, 16.0f };
static constexpr SkHalf kHalfs[16] = { 0x3C00, 0x4000, 0x4200, 0x4400,
0x4500, 0x4600, 0x4700, 0x4800,
0x4880, 0x4900, 0x4980, 0x4A00,
0x4A80, 0x4B00, 0x4B80, 0x4C00 };
static constexpr int32_t kInts[16] = { 1, -2, 3, -4,
5, -6, 7, -8,
9, -10, 11, -12,
13, -14, 15, -16 };
static size_t element_size(Layout layout, SkSLType type) {
// Metal and the _F16 std extended layouts encodes half-precision uniforms in 16 bits.
// Other layouts are expected to encode uniforms in 32 bits.
const bool usesHalf = layout == Layout::kMetal ||
layout == Layout::kStd140_F16 ||
layout == Layout::kStd430_F16;
return (usesHalf && !SkSLTypeIsFullPrecisionNumericType(type)) ? 2 : 4;
}
DEF_GRAPHITE_TEST(UniformManagerCheckSingleUniform, r, CtsEnforcement::kApiLevel_202404) {
// Verify that the uniform manager can hold all the basic uniform types, in every layout.
for (Layout layout : kLayouts) {
UniformManager mgr(layout);
for (SkSLType type : kTypes) {
const Uniform expectations[] = {{"uniform", type}};
SkDEBUGCODE(mgr.setExpectedUniforms(SkSpan(expectations), /*isSubstruct=*/false);)
mgr.write(expectations[0], kFloats);
SkDEBUGCODE(mgr.doneWithExpectedUniforms();)
REPORTER_ASSERT(r, mgr.size() > 0, "Layout: %s - Type: %s",
LayoutString(layout), SkSLTypeString(type));
mgr.reset();
}
}
}
DEF_GRAPHITE_TEST(UniformManagerCheckFloatEncoding, r, CtsEnforcement::kApiLevel_202404) {
// Verify that the uniform manager encodes float data properly.
for (Layout layout : kLayouts) {
UniformManager mgr(layout);
for (SkSLType type : kTypes) {
// Only test scalar and vector floats. (Matrices can introduce padding between values.)
int vecLength = SkSLTypeVecLength(type);
if (!SkSLTypeIsFloatType(type) || vecLength < 1) {
continue;
}
// Write our uniform float scalar/vector.
const Uniform expectations[] = {{"uniform", type}};
SkDEBUGCODE(mgr.setExpectedUniforms(SkSpan(expectations), /*isSubstruct=*/false);)
mgr.write(expectations[0], kFloats);
SkDEBUGCODE(mgr.doneWithExpectedUniforms();)
// Read back the uniform data.
SkSpan<const char> uniformData = mgr.finish();
size_t elementSize = element_size(layout, type);
const void* validData = (elementSize == 4) ? (const void*)kFloats : (const void*)kHalfs;
REPORTER_ASSERT(r, uniformData.size() >= vecLength * elementSize);
REPORTER_ASSERT(r, 0 == memcmp(validData, uniformData.data(), vecLength * elementSize),
"Layout: %s - Type: %s float encoding failed",
LayoutString(layout), SkSLTypeString(type));
mgr.reset();
}
}
}
DEF_GRAPHITE_TEST(UniformManagerCheckIntEncoding, r, CtsEnforcement::kApiLevel_202404) {
// Verify that the uniform manager encodes int data properly.
for (Layout layout : kLayouts) {
UniformManager mgr(layout);
for (SkSLType type : kTypes) {
if (!SkSLTypeIsIntegralType(type)) {
continue;
}
// Write our uniform int scalar/vector.
const Uniform expectations[] = {{"uniform", type}};
SkDEBUGCODE(mgr.setExpectedUniforms(SkSpan(expectations), /*isSubstruct=*/false);)
mgr.write(expectations[0], kInts);
SkDEBUGCODE(mgr.doneWithExpectedUniforms();)
// Read back the uniform data.
SkSpan<const char> uniformData = mgr.finish();
int vecLength = SkSLTypeVecLength(type);
size_t elementSize = element_size(layout, type);
REPORTER_ASSERT(r, uniformData.size() >= vecLength * elementSize);
REPORTER_ASSERT(r, 0 == memcmp(kInts, uniformData.data(), vecLength * elementSize),
"Layout: %s - Type: %s int encoding failed",
LayoutString(layout), SkSLTypeString(type));
mgr.reset();
}
}
}
DEF_GRAPHITE_TEST(UniformManagerCheckScalarVectorPacking, r, CtsEnforcement::kApiLevel_202404) {
// Verify that the uniform manager can pack scalars and vectors of identical type correctly.
for (Layout layout : kLayouts) {
UniformManager mgr(layout);
for (SkSLType type : kTypes) {
int vecLength = SkSLTypeVecLength(type);
if (vecLength < 1) {
continue;
}
// Write three matching uniforms.
const Uniform expectations[] = {{"a", type}, {"b", type}, {"c", type}};
SkDEBUGCODE(mgr.setExpectedUniforms(SkSpan(expectations), /*isSubstruct=*/false);)
mgr.write(expectations[0], kFloats);
mgr.write(expectations[1], kFloats);
mgr.write(expectations[2], kFloats);
SkDEBUGCODE(mgr.doneWithExpectedUniforms();)
// Verify the uniform data packing.
SkSpan<const char> uniformData = mgr.finish();
size_t elementSize = element_size(layout, type);
// Vec3s must be laid out as if they were vec4s.
size_t effectiveVecLength = (vecLength == 3) ? 4 : vecLength;
REPORTER_ASSERT(r, uniformData.size() == elementSize * effectiveVecLength * 3,
"Layout: %s - Type: %s tight packing failed",
LayoutString(layout), SkSLTypeString(type));
mgr.reset();
}
}
}
DEF_GRAPHITE_TEST(UniformManagerCheckMatrixPacking, r, CtsEnforcement::kApiLevel_202404) {
// Verify that the uniform manager can pack matrices correctly.
for (Layout layout : kLayouts) {
UniformManager mgr(layout);
for (SkSLType type : kTypes) {
int matrixSize = SkSLTypeMatrixSize(type);
if (matrixSize < 2) {
continue;
}
// Write three matching uniforms.
const Uniform expectations[] = {{"a", type}, {"b", type}, {"c", type}};
SkDEBUGCODE(mgr.setExpectedUniforms(SkSpan(expectations), /*isSubstruct=*/false);)
mgr.write(expectations[0], kFloats);
mgr.write(expectations[1], kFloats);
mgr.write(expectations[2], kFloats);
SkDEBUGCODE(mgr.doneWithExpectedUniforms();)
// Verify the uniform data packing.
SkSpan<const char> uniformData = mgr.finish();
// In std140-f16, pretend the element size is 4 since the underlying column vectors
// are still forced to 16-byte alignment
size_t elementSize = layout == Layout::kStd140_F16 ? 4 : element_size(layout, type);
// In all layouts, mat3s burn 12 elements, not 9. In std140, mat2s burn 8 elements
// instead of 4.
size_t numElements;
if (matrixSize == 3) {
numElements = 12;
} else if (matrixSize == 2 && (layout == Layout::kStd140 ||
layout == Layout::kStd140_F16)) {
numElements = 8;
} else {
numElements = matrixSize * matrixSize;
}
REPORTER_ASSERT(r, uniformData.size() == elementSize * numElements * 3,
"Layout: %s - Type: %s matrix packing failed",
LayoutString(layout), SkSLTypeString(type));
mgr.reset();
}
}
}
DEF_GRAPHITE_TEST(UniformManagerCheckPaddingScalarVector, r, CtsEnforcement::kApiLevel_202404) {
// Verify that the uniform manager properly adds padding between pairs of scalar/vector.
for (Layout layout : kLayouts) {
UniformManager mgr(layout);
for (SkSLType type1 : kTypes) {
const int vecLength1 = SkSLTypeVecLength(type1);
if (vecLength1 < 1) {
continue;
}
for (SkSLType type2 : kTypes) {
const int vecLength2 = SkSLTypeVecLength(type2);
if (vecLength2 < 1) {
continue;
}
// Write two scalar/vector uniforms.
const Uniform expectations[] = {{"a", type1}, {"b", type2}};
SkDEBUGCODE(mgr.setExpectedUniforms(SkSpan(expectations), /*isSubstruct=*/false);)
mgr.write(expectations[0], kFloats);
mgr.write(expectations[1], kFloats);
SkDEBUGCODE(mgr.doneWithExpectedUniforms();)
// The expected packing varies depending on the bit-widths of each element.
const size_t elementSize1 = element_size(layout, type1);
const size_t elementSize2 = element_size(layout, type2);
const int layoutIdx = static_cast<int>(layout != Layout::kMetal);
if (elementSize1 == elementSize2) {
// Elements in the array correspond to the element size (either 16 or 32 bits).
// The expected uniform layout is listed as strings below.
// A/B: uniform values.
// a/b: padding as part of the uniform type (vec3 takes 4 slots)
// _ : padding between uniforms for alignment
static constexpr const char* kExpectedLayout[2][5][5] = {
// Metal (vec3 consumes vec4 size)
{{ "", "", "", "", "" },
{ "", "AB", "A_BB", "A___BBBb", "A___BBBB" },
{ "", "AAB_", "AABB", "AA__BBBb", "AA__BBBB" },
{ "", "AAAaB___", "AAAaBB__", "AAAaBBBb", "AAAaBBBB" },
{ "", "AAAAB___", "AAAABB__", "AAAABBBb", "AAAABBBB" }},
// std140 and std430 (vec3 aligns to vec4, but consumes only 3 elements)
{{ "", "", "", "", "" },
{ "", "AB", "A_BB", "A___BBBb", "A___BBBB" },
{ "", "AAB_", "AABB", "AA__BBBb", "AA__BBBB" },
{ "", "AAAB", "AAA_BB__", "AAA_BBBb", "AAA_BBBB" },
{ "", "AAAAB___", "AAAABB__", "AAAABBBb", "AAAABBBB" }},
};
const size_t size = strlen(kExpectedLayout[layoutIdx][vecLength1][vecLength2]) *
elementSize1;
SkSpan<const char> uniformData = mgr.finish();
REPORTER_ASSERT(r, uniformData.size() == size,
"Layout: %s - Types: %s, %s padding test failed",
LayoutString(layout),
SkSLTypeString(type1), SkSLTypeString(type2));
} else if (elementSize1 == 2 && elementSize2 == 4) {
// Elements in the array below correspond to 16 bits apiece.
// The expected uniform layout is listed as strings below.
// A/B: uniform values.
// a/b: padding as part of the uniform type (vec3 takes 4 slots in Metal)
// _ : padding between uniforms for alignment
static constexpr const char* kExpectedLayout[2][5][5] = {
// Metal (vec3 consumes vec4 size)
{{ "", "", "", "", "" },
{ "", "A_BB", "A___BBBB", "A_______BBBBBBbb", "A_______BBBBBBBB" },
{ "", "AABB", "AA__BBBB", "AA______BBBBBBbb", "AA______BBBBBBBB" },
{ "", "AAAaBB__", "AAAaBBBB", "AAAa____BBBBBBbb", "AAAa____BBBBBBBB" },
{ "", "AAAABB__", "AAAABBBB", "AAAA____BBBBBBbb", "AAAA____BBBBBBBB" }},
// std140-f16 and std430-f16 (vec3 aligns to vec4 but consumes only 3)
{{ "", "", "", "", "" },
{ "", "A_BB", "A___BBBB", "A_______BBBBBBbb", "A_______BBBBBBBB" },
{ "", "AABB", "AA__BBBB", "AA______BBBBBBbb", "AA______BBBBBBBB" },
{ "", "AAA_BB__", "AAA_BBBB", "AAA_____BBBBBBbb", "AAA_____BBBBBBBB" },
{ "", "AAAABB__", "AAAABBBB", "AAAA____BBBBBBbb", "AAAA____BBBBBBBB" }},
};
const size_t size =
strlen(kExpectedLayout[layoutIdx][vecLength1][vecLength2]) * 2;
SkSpan<const char> uniformData = mgr.finish();
REPORTER_ASSERT(r, uniformData.size() == size,
"Layout: %s - Types: %s, %s padding test failed",
LayoutString(layout),
SkSLTypeString(type1), SkSLTypeString(type2));
} else if (elementSize1 == 4 && elementSize2 == 2) {
// Elements in the array below correspond to 16 bits apiece.
// The expected uniform layout is listed as strings below.
// A/B: uniform values.
// a/b: padding as part of the uniform type (vec3 takes 4 slots)
// _ : padding between uniforms for alignment
static constexpr const char* kExpectedLayout[2][5][5] = {
// Metal (vec3 consumes vec4 size)
{{ "", "", "", "", "" },
{ "", "AAB_", "AABB", "AA__BBBb", "AA__BBBB" },
{ "", "AAAAB___", "AAAABB__", "AAAABBBb", "AAAABBBB" },
{ "", "AAAAAAaaB_______", "AAAAAAaaBB______", "AAAAAAaaBBBb____", "AAAAAAaaBBBB____" },
{ "", "AAAAAAAAB_______", "AAAAAAAABB______", "AAAAAAAABBBb____", "AAAAAAAABBBB____" }},
// std140-f16 and std430-f16 (vec3 aligns to vec4 but consumes only 3)
{{ "", "", "", "", "" },
{ "", "AAB_", "AABB", "AA__BBB_", "AA__BBBB" },
{ "", "AAAAB___", "AAAABB__", "AAAABBB_", "AAAABBBB" },
{ "", "AAAAAAB_", "AAAAAABB", "AAAAAA__BBB_____", "AAAAAA__BBBB____" },
{ "", "AAAAAAAAB_______", "AAAAAAAABB______", "AAAAAAAABBB_____", "AAAAAAAABBBB____" }},
};
const size_t size =
strlen(kExpectedLayout[layoutIdx][vecLength1][vecLength2]) * 2;
SkSpan<const char> uniformData = mgr.finish();
REPORTER_ASSERT(r, uniformData.size() == size,
"Layout: %s - Types: %s, %s padding test failed",
LayoutString(layout),
SkSLTypeString(type1), SkSLTypeString(type2));
} else {
ERRORF(r, "Unexpected element sizes: %zu %zu", elementSize1, elementSize2);
}
mgr.reset();
}
}
}
}
DEF_GRAPHITE_TEST(UniformManagerCheckPaddingVectorMatrix, r, CtsEnforcement::kApiLevel_202404) {
// Verify that the uniform manager properly adds padding between vectors and matrices.
for (Layout layout : kLayouts) {
UniformManager mgr(layout);
for (SkSLType type1 : kTypes) {
const int vecLength1 = SkSLTypeVecLength(type1);
if (vecLength1 < 1) {
continue;
}
for (SkSLType type2 : kTypes) {
const int matSize2 = SkSLTypeMatrixSize(type2);
if (matSize2 < 2) {
continue;
}
// Write the scalar/vector and matrix uniforms.
const Uniform expectations[] = {{"a", type1}, {"b", type2}};
SkDEBUGCODE(mgr.setExpectedUniforms(SkSpan(expectations), /*isSubstruct=*/false);)
mgr.write(expectations[0], kFloats);
mgr.write(expectations[1], kFloats);
SkDEBUGCODE(mgr.doneWithExpectedUniforms();)
// The expected packing varies depending on the bit-widths of each element.
const size_t elementSize1 = element_size(layout, type1);
const size_t elementSize2 = element_size(layout, type2);
int layoutIdx = static_cast<int>(layout != Layout::kStd140 &&
layout != Layout::kStd140_F16);
if (elementSize1 == elementSize2) {
// Elements in the array correspond to the element size (16 or 32 bits).
// The expected uniform layout is listed as strings below.
// A/B: uniform values.
// a/b: padding as part of the uniform type (vec3 takes 4 slots)
// _ : padding between uniforms for alignment
static constexpr const char* kExpectedLayout[3][5][5] = {
// std140-f16
{
{ "", "", "", "", "" },
{ "", "", "A_______BBbb____BBbb____", "A_______BBBb____BBBb____BBBb____", "A_______BBBB____BBBB____BBBB____BBBB____" },
{ "", "", "AA______BBbb____BBbb____", "AA______BBBb____BBBb____BBBb____", "AA______BBBB____BBBB____BBBB____BBBB____" },
{ "", "", "AAAa____BBbb____BBbb____", "AAAa____BBBb____BBBb____BBBb____", "AAAa____BBBB____BBBB____BBBB____BBBB____" },
{ "", "", "AAAA____BBbb____BBbb____", "AAAA____BBBb____BBBb____BBBb____", "AAAA____BBBB____BBBB____BBBB____BBBB____" },
},
// std140
{
{ "", "", "", "", "" },
{ "", "", "A___BBbbBBbb", "A___BBBbBBBbBBBb", "A___BBBBBBBBBBBBBBBB" },
{ "", "", "AA__BBbbBBbb", "AA__BBBbBBBbBBBb", "AA__BBBBBBBBBBBBBBBB" },
{ "", "", "AAAaBBbbBBbb", "AAAaBBBbBBBbBBBb", "AAAaBBBBBBBBBBBBBBBB" },
{ "", "", "AAAABBbbBBbb", "AAAABBBbBBBbBBBb", "AAAABBBBBBBBBBBBBBBB" },
},
// All other layouts
{
{ "", "", "", "", "" },
{ "", "", "A_BBBB", "A___BBBbBBBbBBBb", "A___BBBBBBBBBBBBBBBB" },
{ "", "", "AABBBB", "AA__BBBbBBBbBBBb", "AA__BBBBBBBBBBBBBBBB" },
{ "", "", "AAAaBBBB", "AAAaBBBbBBBbBBBb", "AAAaBBBBBBBBBBBBBBBB" },
{ "", "", "AAAABBBB", "AAAABBBbBBBbBBBb", "AAAABBBBBBBBBBBBBBBB" },
},
};
if (elementSize1 != 2 || layout != Layout::kStd140_F16) {
layoutIdx++;
}
// size_t elementSize = elementSize1 == 2 && layout == Layout::kStd140_F16 ? 4 : elementSize1;
const size_t size =
strlen(kExpectedLayout[layoutIdx][vecLength1][matSize2]) * elementSize1;
SkSpan<const char> uniformData = mgr.finish();
REPORTER_ASSERT(r, uniformData.size() == size,
"Layout: %s - Types: %s, %s vector-matrix padding test failed",
LayoutString(layout),
SkSLTypeString(type1), SkSLTypeString(type2));
} else if (elementSize1 == 2 && elementSize2 == 4) {
// Elements in the array below correspond to 16 bits apiece.
// The expected uniform layout is listed as strings below.
// A/B: uniform values.
// a/b: padding as part of the uniform type (vec3 takes 4 slots)
// _ : padding between uniforms for alignment
static constexpr const char* kExpectedLayout[2][5][5] = {
// std140-f16
{
{"", "", "", "", ""},
{"", "", "A_______BBBB____BBBB____", "A_______BBBBBBbbBBBBBBbbBBBBBBbb", "A_______BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"},
{"", "", "AA______BBBB____BBBB____", "AA______BBBBBBbbBBBBBBbbBBBBBBbb", "AA______BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"},
{"", "", "AAAa____BBBB____BBBB____", "AAAa____BBBBBBbbBBBBBBbbBBBBBBbb", "AAAa____BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"},
{"", "", "AAAA____BBBB____BBBB____", "AAAA____BBBBBBbbBBBBBBbbBBBBBBbb", "AAAA____BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"},
},
// Metal and std430-f16
{
{"", "", "", "", ""},
{"", "", "A___BBBBBBBB", "A_______BBBBBBbbBBBBBBbbBBBBBBbb", "A_______BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"},
{"", "", "AA__BBBBBBBB", "AA______BBBBBBbbBBBBBBbbBBBBBBbb", "AA______BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"},
{"", "", "AAAaBBBBBBBB", "AAAa____BBBBBBbbBBBBBBbbBBBBBBbb", "AAAa____BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"},
{"", "", "AAAABBBBBBBB", "AAAA____BBBBBBbbBBBBBBbbBBBBBBbb", "AAAA____BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"},
}
};
const size_t size =
strlen(kExpectedLayout[layoutIdx][vecLength1][matSize2]) * 2;
SkSpan<const char> uniformData = mgr.finish();
REPORTER_ASSERT(r, uniformData.size() == size,
"Layout: %s - Types: %s, %s vector-matrix padding test failed",
LayoutString(layout),
SkSLTypeString(type1), SkSLTypeString(type2));
} else if (elementSize1 == 4 && elementSize2 == 2) {
// Elements in the array below correspond to 16 bits apiece.
// The expected uniform layout is listed as strings below.
// A/B: uniform values.
// a/b: padding as part of the uniform type (vec3 takes 4 slots)
// _ : padding between uniforms for alignment
static constexpr const char* kExpectedLayout[2][5][5] = {
// std140-f16
{
{"", "", "", "", ""},
{"", "", "AA______BB______BB______", "AA______BBBb____BBBb____BBBb____", "AA______BBBB____BBBB____BBBB____BBBB____"},
{"", "", "AAAA____BB______BB______", "AAAA____BBBb____BBBb____BBBb____", "AAAA____BBBB____BBBB____BBBB____BBBB____"},
{"", "", "AAAAAAaaBB______BB______", "AAAAAAaaBBBb____BBBb____BBBb____", "AAAAAAaaBBBB____BBBB____BBBB____BBBB____"},
{"", "", "AAAAAAAABB______BB______", "AAAAAAAABBBb____BBBb____BBBb____", "AAAAAAAABBBB____BBBB____BBBB____BBBB____"},
},
// Metal and std430-f16
{
{"", "", "", "", ""},
{"", "", "AABBBB", "AA__BBBbBBBbBBBb", "AA__BBBBBBBBBBBBBBBB"},
{"", "", "AAAABBBB", "AAAABBBbBBBbBBBb", "AAAABBBBBBBBBBBBBBBB"},
{"", "", "AAAAAAaaBBBB____", "AAAAAAaaBBBbBBBbBBBb____", "AAAAAAaaBBBBBBBBBBBBBBBB"},
{"", "", "AAAAAAAABBBB____", "AAAAAAAABBBbBBBbBBBb____", "AAAAAAAABBBBBBBBBBBBBBBB"},
}
};
const size_t size =
strlen(kExpectedLayout[layoutIdx][vecLength1][matSize2]) * 2;
SkSpan<const char> uniformData = mgr.finish();
REPORTER_ASSERT(r, uniformData.size() == size,
"Layout: %s - Types: %s, %s vector-matrix padding test failed",
LayoutString(layout),
SkSLTypeString(type1), SkSLTypeString(type2));
}
mgr.reset();
}
}
}
}
DEF_GRAPHITE_TEST(UniformManagerCheckPaddingMatrixVector, r, CtsEnforcement::kApiLevel_202404) {
// Verify that the uniform manager properly adds padding between matrices and vectors.
for (Layout layout : kLayouts) {
UniformManager mgr(layout);
for (SkSLType type1 : kTypes) {
const int matSize1 = SkSLTypeMatrixSize(type1);
if (matSize1 < 2) {
continue;
}
for (SkSLType type2 : kTypes) {
const int vecLength2 = SkSLTypeVecLength(type2);
if (vecLength2 < 1) {
continue;
}
// Write the scalar/vector and matrix uniforms.
const Uniform expectations[] = {{"a", type1}, {"b", type2}};
SkDEBUGCODE(mgr.setExpectedUniforms(SkSpan(expectations), /*isSubstruct=*/false);)
mgr.write(expectations[0], kFloats);
mgr.write(expectations[1], kFloats);
SkDEBUGCODE(mgr.doneWithExpectedUniforms();)
// The expected packing varies depending on the bit-widths of each element.
const size_t elementSize1 = element_size(layout, type1);
const size_t elementSize2 = element_size(layout, type2);
int layoutIdx = static_cast<int>(layout != Layout::kStd140 &&
layout != Layout::kStd140_F16);
if (elementSize1 == elementSize2) {
// Elements in the array correspond to the element size (16 or 32 bits).
// The expected uniform layout is listed as strings below.
// A/B: uniform values.
// a/b: padding as part of the uniform type (vec3 takes 4 slots)
// _ : padding between uniforms for alignment
static constexpr const char* kExpectedLayout[3][5][5] = {
// std140-f16 layout
{
{ "", "", "", "", "" },
{ "", "", "", "", "" },
{ "", "AAaa____AAaa____B_______", "AAaa____AAaa____BB______", "AAaa____AAaa____BBBb____", "AAaa____AAaa____BBBB____" },
{ "", "AAAa____AAAa____AAAa____B_______", "AAAa____AAAa____AAAa____BB______", "AAAa____AAAa____AAAa____BBBb____", "AAAa____AAAa____AAAa____BBBB____" },
{ "", "AAAA____AAAA____AAAA____AAAA____B_______", "AAAA____AAAA____AAAA____AAAA____BB______", "AAAA____AAAA____AAAA____AAAA____BBBb____", "AAAA____AAAA____AAAA____AAAA____BBBB____" },
},
// std140 layout
{
{ "", "", "", "", "" },
{ "", "", "", "", "" },
{ "", "AAaaAAaaB___", "AAaaAAaaBB__", "AAaaAAaaBBBb", "AAaaAAaaBBBB" },
{ "", "AAAaAAAaAAAaB___", "AAAaAAAaAAAaBB__", "AAAaAAAaAAAaBBBb", "AAAaAAAaAAAaBBBB" },
{ "", "AAAAAAAAAAAAAAAAB___", "AAAAAAAAAAAAAAAABB__", "AAAAAAAAAAAAAAAABBBb", "AAAAAAAAAAAAAAAABBBB" },
},
// All other layouts
{
{ "", "", "", "", "" },
{ "", "", "", "", "" },
{ "", "AAAAB_", "AAAABB", "AAAABBBb", "AAAABBBB" },
{ "", "AAAaAAAaAAAaB___", "AAAaAAAaAAAaBB__", "AAAaAAAaAAAaBBBb", "AAAaAAAaAAAaBBBB" },
{ "", "AAAAAAAAAAAAAAAAB___", "AAAAAAAAAAAAAAAABB__", "AAAAAAAAAAAAAAAABBBb", "AAAAAAAAAAAAAAAABBBB" },
},
};
if (elementSize1 != 2 || layout != Layout::kStd140_F16) {
layoutIdx++;
}
const size_t size = strlen(kExpectedLayout[layoutIdx][matSize1][vecLength2]) *
elementSize1;
SkSpan<const char> uniformData = mgr.finish();
REPORTER_ASSERT(r, uniformData.size() == size,
"Layout: %s - Types: %s, %s matrix-vector padding test failed",
LayoutString(layout),
SkSLTypeString(type1), SkSLTypeString(type2));
} else if (elementSize1 == 2 && elementSize2 == 4) {
// Elements in the array below correspond to 16 bits apiece.
// The expected uniform layout is listed as strings below.
// A/B: uniform values.
// a/b: padding as part of the uniform type (vec3 takes 4 slots)
// _ : padding between uniforms for alignment
static constexpr const char* kExpectedLayout[2][5][5] = {
// std140-f16
{
{ "", "", "", "", "" },
{ "", "", "", "", "" },
{ "", "AA______AA______BB______", "AA______AA______BBBB____", "AA______AA______BBBBBBbb", "AA______AA______BBBBBBBB" },
{ "", "AAAa____AAAa____AAAa____BB______", "AAAa____AAAa____AAAa____BBBB____", "AAAa____AAAa____AAAa____BBBBBBbb", "AAAa____AAAa____AAAa____BBBBBBBB" },
{ "", "AAAA____AAAA____AAAA____AAAA____BB______", "AAAA____AAAA____AAAA____AAAA____BBBB____", "AAAA____AAAA____AAAA____AAAA____BBBBBBbb", "AAAA____AAAA____AAAA____AAAA____BBBBBBBB" },
},
// Metal and std430-f16
{
{ "", "", "", "", "" },
{ "", "", "", "", "" },
{ "", "AAAABB", "AAAABBBB", "AAAA____BBBBBBbb", "AAAA____BBBBBBBB" },
{ "", "AAAaAAAaAAAaBB__", "AAAaAAAaAAAaBBBB", "AAAaAAAaAAAa____BBBBBBbb", "AAAaAAAaAAAa____BBBBBBBB" },
{ "", "AAAAAAAAAAAAAAAABB__", "AAAAAAAAAAAAAAAABBBB", "AAAAAAAAAAAAAAAABBBBBBbb", "AAAAAAAAAAAAAAAABBBBBBBB" },
}
};
const size_t size =
strlen(kExpectedLayout[layoutIdx][matSize1][vecLength2]) * 2;
SkSpan<const char> uniformData = mgr.finish();
REPORTER_ASSERT(r, uniformData.size() == size,
"Layout: %s - Types: %s, %s matrix-vector padding test failed",
LayoutString(layout),
SkSLTypeString(type1), SkSLTypeString(type2));
} else if (elementSize1 == 4 && elementSize2 == 2) {
// Elements in the array below correspond to 16 bits apiece.
// The expected uniform layout is listed as strings below.
// A/B: uniform values.
// a/b: padding as part of the uniform type (vec3 takes 4 slots)
// _ : padding between uniforms for alignment
static constexpr const char* kExpectedLayout[2][5][5] = {
// std140-f16
{
{ "", "", "", "", "" },
{ "", "", "", "", "" },
{ "", "AAAA____AAAA____B_______", "AAAA____AAAA____BB______", "AAAA____AAAA____BBBb____", "AAAA____AAAA____BBBB____" },
{ "", "AAAAAAaaAAAAAAaaAAAAAAaaB_______", "AAAAAAaaAAAAAAaaAAAAAAaaBB______", "AAAAAAaaAAAAAAaaAAAAAAaaBBBb____", "AAAAAAaaAAAAAAaaAAAAAAaaBBBB____" },
{ "", "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB_______", "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABB______", "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBb____", "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB____" },
},
// Metal and std430-f16
{
{ "", "", "", "", "" },
{ "", "", "", "", "" },
{ "", "AAAAAAAAB___", "AAAAAAAABB__", "AAAAAAAABBBb", "AAAAAAAABBBB" },
{ "", "AAAAAAaaAAAAAAaaAAAAAAaaB_______", "AAAAAAaaAAAAAAaaAAAAAAaaBB______", "AAAAAAaaAAAAAAaaAAAAAAaaBBBb____", "AAAAAAaaAAAAAAaaAAAAAAaaBBBB____" },
{ "", "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB_______", "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABB______", "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBb____", "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB____" },
}
};
const size_t size = strlen(kExpectedLayout[layoutIdx][matSize1][vecLength2]) * 2;
SkSpan<const char> uniformData = mgr.finish();
REPORTER_ASSERT(r, uniformData.size() == size,
"Layout: %s - Types: %s, %s matrix-vector padding test failed",
LayoutString(layout),
SkSLTypeString(type1), SkSLTypeString(type2));
}
mgr.reset();
}
}
}
}
DEF_GRAPHITE_TEST(UniformManagerMetalArrayLayout, r, CtsEnforcement::kApiLevel_202404) {
UniformManager mgr(Layout::kMetal);
// Tests set up a uniform block with a single half (to force alignment) and an array of 3
// elements. Test every type that can appear in an array.
constexpr size_t kArraySize = 3;
// Buffer large enough to hold a float4x4[3] array.
static constexpr uint8_t kBuffer[192] = {};
static const char* kExpectedLayout[] = {
// Each letter (A/B/a/b) corresponds to a single byte.
// The expected uniform layout is listed as strings below.
// A/B: uniform values.
// a/b: padding as part of the uniform type.
// _ : padding between uniforms for alignment.
/* {half, float[3]} */ "AA__BBBBBBBBBBBB",
/* {half, float2[3]} */ "AA______BBBBBBBBBBBBBBBBBBBBBBBB",
/* {half, float3[3]} */ "AA______________BBBBBBBBBBBBbbbbBBBBBBBBBBBBbbbbBBBBBBBBBBBBbbbb",
/* {half, float4[3]} */ "AA______________BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
/* {half, half[3]} */ "AABBBBBB",
/* {half, half2[3]} */ "AA__BBBBBBBBBBBB",
/* {half, half3[3]} */ "AA______BBBBBBbbBBBBBBbbBBBBBBbb",
/* {half, half4[3]} */ "AA______BBBBBBBBBBBBBBBBBBBBBBBB",
/* {half, int[3]} */ "AA__BBBBBBBBBBBB",
/* {half, int2[3]} */ "AA______BBBBBBBBBBBBBBBBBBBBBBBB",
/* {half, int3[3]} */ "AA______________BBBBBBBBBBBBbbbbBBBBBBBBBBBBbbbbBBBBBBBBBBBBbbbb",
/* {half, int4[3]} */ "AA______________BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
/* {half, float2x2[3] */ "AA______BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
/* {half, float3x3[3] */ "AA______________"
"BBBBBBBBBBBBbbbbBBBBBBBBBBBBbbbbBBBBBBBBBBBBbbbb"
"BBBBBBBBBBBBbbbbBBBBBBBBBBBBbbbbBBBBBBBBBBBBbbbb"
"BBBBBBBBBBBBbbbbBBBBBBBBBBBBbbbbBBBBBBBBBBBBbbbb",
/* {half, float4x4[3] */ "AA______________"
"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
/* {half, half2x2[3] */ "AA__BBBBBBBBBBBBBBBBBBBBBBBB",
/* {half, half3x3[3] */ "AA______"
"BBBBBBbbBBBBBBbbBBBBBBbb"
"BBBBBBbbBBBBBBbbBBBBBBbb"
"BBBBBBbbBBBBBBbbBBBBBBbb",
/* {half, half4x4[3] */ "AA______"
"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
};
for (size_t i = 0; i < std::size(kExpectedLayout); i++) {
const SkSLType arrayType = kTypes[i];
const Uniform expectations[] = {{"a", SkSLType::kHalf}, {"b", arrayType, kArraySize}};
SkDEBUGCODE(mgr.setExpectedUniforms(SkSpan(expectations), /*isSubstruct=*/false);)
mgr.write(expectations[0], kHalfs);
mgr.write(expectations[1], kBuffer);
SkDEBUGCODE(mgr.doneWithExpectedUniforms();)
const size_t expectedSize = strlen(kExpectedLayout[i]);
SkSpan<const char> uniformData = mgr.finish();
REPORTER_ASSERT(r, uniformData.size() == expectedSize,
"array test %d for type %s failed - expected size: %zu, actual size: %zu",
(int)i, SkSLTypeString(arrayType), expectedSize, uniformData.size());
mgr.reset();
}
}
DEF_GRAPHITE_TEST(UniformManagerStd430ArrayLayout, r, CtsEnforcement::kApiLevel_202404) {
UniformManager mgr(Layout::kStd430);
// Tests set up a uniform block with a single half (to force alignment) and an array of 3
// elements. Test every type that can appear in an array.
constexpr size_t kArraySize = 3;
// Buffer large enough to hold a float4x4[3] array.
static constexpr uint8_t kBuffer[192] = {};
static const char* kExpectedLayout[] = {
// Each letter (A/B/a/b) corresponds to a single byte.
// The expected uniform layout is listed as strings below.
// A/B: uniform values.
// a/b: padding as part of the uniform type.
// _ : padding between uniforms for alignment.
/* {half, float[3]} */ "AA__BBBBBBBBBBBB",
/* {half, float2[3]} */ "AA______BBBBBBBBBBBBBBBBBBBBBBBB",
/* {half, float3[3]} */ "AA______________BBBBBBBBBBBBbbbbBBBBBBBBBBBBbbbbBBBBBBBBBBBBbbbb",
/* {half, float4[3]} */ "AA______________BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
/* {half, half[3]} */ "AA__BBBBBBBBBBBB",
/* {half, half2[3]} */ "AA______BBBBBBBBBBBBBBBBBBBBBBBB",
/* {half, half3[3]} */ "AA______________BBBBBBBBBBBBbbbbBBBBBBBBBBBBbbbbBBBBBBBBBBBBbbbb",
/* {half, half4[3]} */ "AA______________BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
/* {half, int[3]} */ "AA__BBBBBBBBBBBB",
/* {half, int2[3]} */ "AA______BBBBBBBBBBBBBBBBBBBBBBBB",
/* {half, int3[3]} */ "AA______________BBBBBBBBBBBBbbbbBBBBBBBBBBBBbbbbBBBBBBBBBBBBbbbb",
/* {half, int4[3]} */ "AA______________BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
/* {half, float2x2[3] */ "AA______BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
/* {half, float3x3[3] */ "AA______________"
"BBBBBBBBBBBBbbbbBBBBBBBBBBBBbbbbBBBBBBBBBBBBbbbb"
"BBBBBBBBBBBBbbbbBBBBBBBBBBBBbbbbBBBBBBBBBBBBbbbb"
"BBBBBBBBBBBBbbbbBBBBBBBBBBBBbbbbBBBBBBBBBBBBbbbb",
/* {half, float4x4[3] */ "AA______________"
"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
/* {half, half2x2[3] */ "AA______BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
/* {half, half3x3[3] */ "AA______________"
"BBBBBBBBBBBBbbbbBBBBBBBBBBBBbbbbBBBBBBBBBBBBbbbb"
"BBBBBBBBBBBBbbbbBBBBBBBBBBBBbbbbBBBBBBBBBBBBbbbb"
"BBBBBBBBBBBBbbbbBBBBBBBBBBBBbbbbBBBBBBBBBBBBbbbb",
/* {half, half4x4[3] */ "AA______________"
"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
};
for (size_t i = 0; i < std::size(kExpectedLayout); i++) {
const SkSLType arrayType = kTypes[i];
const Uniform expectations[] = {{"a", SkSLType::kHalf}, {"b", arrayType, kArraySize}};
SkDEBUGCODE(mgr.setExpectedUniforms(SkSpan(expectations), /*isSubstruct=*/false);)
mgr.write(expectations[0], kHalfs);
mgr.write(expectations[1], kBuffer);
SkDEBUGCODE(mgr.doneWithExpectedUniforms();)
const size_t expectedSize = strlen(kExpectedLayout[i]);
SkSpan<const char> uniformData = mgr.finish();
REPORTER_ASSERT(r, uniformData.size() == expectedSize,
"array test %d for type %s failed - expected size: %zu, actual size: %zu",
(int)i, SkSLTypeString(arrayType), expectedSize, uniformData.size());
mgr.reset();
}
}
DEF_GRAPHITE_TEST(UniformManagerStd430F16ArrayLayout, r, CtsEnforcement::kApiLevel_202404) {
UniformManager mgr(Layout::kStd430_F16);
// Tests set up a uniform block with a single half (to force alignment) and an array of 3
// elements. Test every type that can appear in an array.
constexpr size_t kArraySize = 3;
// Buffer large enough to hold a float4x4[3] array.
static constexpr uint8_t kBuffer[192] = {};
static const char* kExpectedLayout[] = {
// Each letter (A/B/a/b) corresponds to a single byte.
// The expected uniform layout is listed as strings below.
// A/B: uniform values.
// a/b: padding as part of the uniform type.
// _ : padding between uniforms for alignment.
/* {half, float[3]} */ "AA__BBBBBBBBBBBB",
/* {half, float2[3]} */ "AA______BBBBBBBBBBBBBBBBBBBBBBBB",
/* {half, float3[3]} */ "AA______________BBBBBBBBBBBBbbbbBBBBBBBBBBBBbbbbBBBBBBBBBBBBbbbb",
/* {half, float4[3]} */ "AA______________BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
/* {half, half[3]} */ "AABBBBBB",
/* {half, half2[3]} */ "AA__BBBBBBBBBBBB",
/* {half, half3[3]} */ "AA______BBBBBBbbBBBBBBbbBBBBBBbb",
/* {half, half4[3]} */ "AA______BBBBBBBBBBBBBBBBBBBBBBBB",
/* {half, int[3]} */ "AA__BBBBBBBBBBBB",
/* {half, int2[3]} */ "AA______BBBBBBBBBBBBBBBBBBBBBBBB",
/* {half, int3[3]} */ "AA______________BBBBBBBBBBBBbbbbBBBBBBBBBBBBbbbbBBBBBBBBBBBBbbbb",
/* {half, int4[3]} */ "AA______________BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
/* {half, float2x2[3] */ "AA______BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
/* {half, float3x3[3] */ "AA______________"
"BBBBBBBBBBBBbbbbBBBBBBBBBBBBbbbbBBBBBBBBBBBBbbbb"
"BBBBBBBBBBBBbbbbBBBBBBBBBBBBbbbbBBBBBBBBBBBBbbbb"
"BBBBBBBBBBBBbbbbBBBBBBBBBBBBbbbbBBBBBBBBBBBBbbbb",
/* {half, float4x4[3] */ "AA______________"
"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
/* {half, half2x2[3] */ "AA__BBBBBBBBBBBBBBBBBBBBBBBB",
/* {half, half3x3[3] */ "AA______"
"BBBBBBbbBBBBBBbbBBBBBBbb"
"BBBBBBbbBBBBBBbbBBBBBBbb"
"BBBBBBbbBBBBBBbbBBBBBBbb",
/* {half, half4x4[3] */ "AA______"
"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
};
for (size_t i = 0; i < std::size(kExpectedLayout); i++) {
const SkSLType arrayType = kTypes[i];
const Uniform expectations[] = {{"a", SkSLType::kHalf}, {"b", arrayType, kArraySize}};
SkDEBUGCODE(mgr.setExpectedUniforms(SkSpan(expectations), /*isSubstruct=*/false);)
mgr.write(expectations[0], kHalfs);
mgr.write(expectations[1], kBuffer);
SkDEBUGCODE(mgr.doneWithExpectedUniforms();)
const size_t expectedSize = strlen(kExpectedLayout[i]);
SkSpan<const char> uniformData = mgr.finish();
REPORTER_ASSERT(r, uniformData.size() == expectedSize,
"array test %d for type %s failed - expected size: %zu, actual size: %zu",
(int)i, SkSLTypeString(arrayType), expectedSize, uniformData.size());
mgr.reset();
}
}
DEF_GRAPHITE_TEST(UniformManagerStd140ArrayLayout, r, CtsEnforcement::kApiLevel_202404) {
UniformManager mgr(Layout::kStd140);
// Tests set up a uniform block with a single half (to force alignment) and an array of 3
// elements. Test every type that can appear in an array.
constexpr size_t kArraySize = 3;
// Buffer large enough to hold a float4x4[3] array.
static constexpr uint8_t kBuffer[192] = {};
static const char* kExpectedLayout[] = {
// Each letter (A/B/a/b) corresponds to a single byte.
// The expected uniform layout is listed as strings below.
// A/B: uniform values.
// a/b: padding as part of the uniform type.
// _ : padding between uniforms for alignment.
/* {half, float[3]} */ "AA______________BBBBbbbbbbbbbbbbBBBBbbbbbbbbbbbbBBBBbbbbbbbbbbbb",
/* {half, float2[3]} */ "AA______________BBBBBBBBbbbbbbbbBBBBBBBBbbbbbbbbBBBBBBBBbbbbbbbb",
/* {half, float3[3]} */ "AA______________BBBBBBBBBBBBbbbbBBBBBBBBBBBBbbbbBBBBBBBBBBBBbbbb",
/* {half, float4[3]} */ "AA______________BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
/* {half, half[3]} */ "AA______________BBbbbbbbbbbbbbbbBBbbbbbbbbbbbbbbBBbbbbbbbbbbbbbb",
/* {half, half2[3]} */ "AA______________BBBBbbbbbbbbbbbbBBBBbbbbbbbbbbbbBBBBbbbbbbbbbbbb",
/* {half, half3[3]} */ "AA______________BBBBBBbbbbbbbbbbBBBBBBbbbbbbbbbbBBBBBBbbbbbbbbbb",
/* {half, half4[3]} */ "AA______________BBBBBBBBbbbbbbbbBBBBBBBBbbbbbbbbBBBBBBBBbbbbbbbb",
/* {half, int[3]} */ "AA______________BBBBbbbbbbbbbbbbBBBBbbbbbbbbbbbbBBBBbbbbbbbbbbbb",
/* {half, int2[3]} */ "AA______________BBBBBBBBbbbbbbbbBBBBBBBBbbbbbbbbBBBBBBBBbbbbbbbb",
/* {half, int3[3]} */ "AA______________BBBBBBBBBBBBbbbbBBBBBBBBBBBBbbbbBBBBBBBBBBBBbbbb",
/* {half, int4[3]} */ "AA______________BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
/* {half, float2x2[3] */ "AA______________"
"BBBBBBBBbbbbbbbbBBBBBBBBbbbbbbbb"
"BBBBBBBBbbbbbbbbBBBBBBBBbbbbbbbb"
"BBBBBBBBbbbbbbbbBBBBBBBBbbbbbbbb",
/* {half, float3x3[3] */ "AA______________"
"BBBBBBBBBBBBbbbbBBBBBBBBBBBBbbbbBBBBBBBBBBBBbbbb"
"BBBBBBBBBBBBbbbbBBBBBBBBBBBBbbbbBBBBBBBBBBBBbbbb"
"BBBBBBBBBBBBbbbbBBBBBBBBBBBBbbbbBBBBBBBBBBBBbbbb",
/* {half, float4x4[3] */ "AA______________"
"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
/* {half, half2x2[3] */ "AA______________"
"BBBBBBBBbbbbbbbbBBBBBBBBbbbbbbbb"
"BBBBBBBBbbbbbbbbBBBBBBBBbbbbbbbb"
"BBBBBBBBbbbbbbbbBBBBBBBBbbbbbbbb",
/* {half, half3x3[3] */ "AA______________"
"BBBBBBBBBBBBbbbbBBBBBBBBBBBBbbbbBBBBBBBBBBBBbbbb"
"BBBBBBBBBBBBbbbbBBBBBBBBBBBBbbbbBBBBBBBBBBBBbbbb"
"BBBBBBBBBBBBbbbbBBBBBBBBBBBBbbbbBBBBBBBBBBBBbbbb",
/* {half, half4x4[3] */ "AA______________"
"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
};
for (size_t i = 0; i < std::size(kExpectedLayout); i++) {
const SkSLType arrayType = kTypes[i];
const Uniform expectations[] = {{"a", SkSLType::kHalf}, {"b", arrayType, kArraySize}};
SkDEBUGCODE(mgr.setExpectedUniforms(SkSpan(expectations), /*isSubstruct=*/false);)
mgr.write(expectations[0], kHalfs);
mgr.write(expectations[1], kBuffer);
SkDEBUGCODE(mgr.doneWithExpectedUniforms();)
const size_t expectedSize = strlen(kExpectedLayout[i]);
SkSpan<const char> uniformData = mgr.finish();
REPORTER_ASSERT(r, uniformData.size() == expectedSize,
"array test %d for type %s failed - expected size: %zu, actual size: %zu",
(int)i, SkSLTypeString(arrayType), expectedSize, uniformData.size());
mgr.reset();
}
}
// NOTE: Since arrays are aligned to 16 bytes, these cases end up matching kStd140 offsets
DEF_GRAPHITE_TEST(UniformManagerStd140F16ArrayLayout, r, CtsEnforcement::kApiLevel_202404) {
UniformManager mgr(Layout::kStd140_F16);
// Tests set up a uniform block with a single half (to force alignment) and an array of 3
// elements. Test every type that can appear in an array.
constexpr size_t kArraySize = 3;
// Buffer large enough to hold a float4x4[3] array.
static constexpr uint8_t kBuffer[192] = {};
static const char* kExpectedLayout[] = {
// Each letter (A/B/a/b) corresponds to a single byte.
// The expected uniform layout is listed as strings below.
// A/B: uniform values.
// a/b: padding as part of the uniform type.
// _ : padding between uniforms for alignment.
/* {half, float[3]} */ "AA______________BBBBbbbbbbbbbbbbBBBBbbbbbbbbbbbbBBBBbbbbbbbbbbbb",
/* {half, float2[3]} */ "AA______________BBBBBBBBbbbbbbbbBBBBBBBBbbbbbbbbBBBBBBBBbbbbbbbb",
/* {half, float3[3]} */ "AA______________BBBBBBBBBBBBbbbbBBBBBBBBBBBBbbbbBBBBBBBBBBBBbbbb",
/* {half, float4[3]} */ "AA______________BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
/* {half, half[3]} */ "AA______________BBbbbbbbbbbbbbbbBBbbbbbbbbbbbbbbBBbbbbbbbbbbbbbb",
/* {half, half2[3]} */ "AA______________BBBBbbbbbbbbbbbbBBBBbbbbbbbbbbbbBBBBbbbbbbbbbbbb",
/* {half, half3[3]} */ "AA______________BBBBBBbbbbbbbbbbBBBBBBbbbbbbbbbbBBBBBBbbbbbbbbbb",
/* {half, half4[3]} */ "AA______________BBBBBBBBbbbbbbbbBBBBBBBBbbbbbbbbBBBBBBBBbbbbbbbb",
/* {half, int[3]} */ "AA______________BBBBbbbbbbbbbbbbBBBBbbbbbbbbbbbbBBBBbbbbbbbbbbbb",
/* {half, int2[3]} */ "AA______________BBBBBBBBbbbbbbbbBBBBBBBBbbbbbbbbBBBBBBBBbbbbbbbb",
/* {half, int3[3]} */ "AA______________BBBBBBBBBBBBbbbbBBBBBBBBBBBBbbbbBBBBBBBBBBBBbbbb",
/* {half, int4[3]} */ "AA______________BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
/* {half, float2x2[3] */ "AA______________"
"BBBBBBBBbbbbbbbbBBBBBBBBbbbbbbbb"
"BBBBBBBBbbbbbbbbBBBBBBBBbbbbbbbb"
"BBBBBBBBbbbbbbbbBBBBBBBBbbbbbbbb",
/* {half, float3x3[3] */ "AA______________"
"BBBBBBBBBBBBbbbbBBBBBBBBBBBBbbbbBBBBBBBBBBBBbbbb"
"BBBBBBBBBBBBbbbbBBBBBBBBBBBBbbbbBBBBBBBBBBBBbbbb"
"BBBBBBBBBBBBbbbbBBBBBBBBBBBBbbbbBBBBBBBBBBBBbbbb",
/* {half, float4x4[3] */ "AA______________"
"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
/* {half, half2x2[3] */ "AA______________"
"BBBBBBBBbbbbbbbbBBBBBBBBbbbbbbbb"
"BBBBBBBBbbbbbbbbBBBBBBBBbbbbbbbb"
"BBBBBBBBbbbbbbbbBBBBBBBBbbbbbbbb",
/* {half, half3x3[3] */ "AA______________"
"BBBBBBBBBBBBbbbbBBBBBBBBBBBBbbbbBBBBBBBBBBBBbbbb"
"BBBBBBBBBBBBbbbbBBBBBBBBBBBBbbbbBBBBBBBBBBBBbbbb"
"BBBBBBBBBBBBbbbbBBBBBBBBBBBBbbbbBBBBBBBBBBBBbbbb",
/* {half, half4x4[3] */ "AA______________"
"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
};
for (size_t i = 0; i < std::size(kExpectedLayout); i++) {
const SkSLType arrayType = kTypes[i];
const Uniform expectations[] = {{"a", SkSLType::kHalf}, {"b", arrayType, kArraySize}};
SkDEBUGCODE(mgr.setExpectedUniforms(SkSpan(expectations), /*isSubstruct=*/false);)
mgr.write(expectations[0], kHalfs);
mgr.write(expectations[1], kBuffer);
SkDEBUGCODE(mgr.doneWithExpectedUniforms();)
const size_t expectedSize = strlen(kExpectedLayout[i]);
SkSpan<const char> uniformData = mgr.finish();
REPORTER_ASSERT(r, uniformData.size() == expectedSize,
"array test %d for type %s failed - expected size: %zu, actual size: %zu",
(int)i, SkSLTypeString(arrayType), expectedSize, uniformData.size());
mgr.reset();
}
}
// This test validates that the uniform data for matrix types get written out according to the
// layout expectations.
static void expect_matrix(skiatest::Reporter* reporter,
UniformManager& mgr,
SkSLType type,
bool isFullPrecision,
size_t expectedSizeInBytes,
SkSpan<const int> expectedOffsetsInPrimitives) {
const Uniform expectations[] = {{"m", type}};
SkDEBUGCODE(mgr.setExpectedUniforms(SkSpan(expectations), /*isSubstruct=*/false);)
mgr.write(expectations[0], kFloats);
SkDEBUGCODE(mgr.doneWithExpectedUniforms();)
SkSpan<const char> data = mgr.finish();
REPORTER_ASSERT(reporter, data.size() == expectedSizeInBytes,
"%s layout size expected %zu, got %zu",
SkSLTypeString(type), expectedSizeInBytes, data.size());
if (isFullPrecision) {
const float* elements = reinterpret_cast<const float*>(data.data());
for (size_t index = 0; index < expectedOffsetsInPrimitives.size(); ++index) {
float el = elements[expectedOffsetsInPrimitives[index]];
float expected = kFloats[index];
REPORTER_ASSERT(reporter, el == expected,
"Incorrect %s element %zu - expected %f, got %f",
SkSLTypeString(type), index, expected, el);
}
} else {
const SkHalf* elements = reinterpret_cast<const SkHalf*>(data.data());
for (size_t index = 0; index < expectedOffsetsInPrimitives.size(); ++index) {
SkHalf el = elements[expectedOffsetsInPrimitives[index]];
SkHalf expected = kHalfs[index];
REPORTER_ASSERT(reporter, el == expected,
"Incorrect %s element %zu - expected 0x%04x, got 0x%04x",
SkSLTypeString(type), index, expected, el);
}
}
mgr.reset();
}
DEF_GRAPHITE_TEST(UniformManagerStd140MatrixLayoutContents, r, CtsEnforcement::kApiLevel_202404) {
UniformManager mgr(Layout::kStd140);
// 2x2
for (SkSLType type : {SkSLType::kFloat2x2, SkSLType::kHalf2x2}) {
expect_matrix(r, mgr, type, /*isFullPrecision=*/true, /*expectedSizeInBytes=*/32,
/*expectedOffsetsInPrimitives=*/{0, 1, 4, 5});
}
// 3x3
for (SkSLType type : {SkSLType::kFloat3x3, SkSLType::kHalf3x3}) {
expect_matrix(r, mgr, type, /*isFullPrecision=*/true, /*expectedSizeInBytes=*/48,
/*expectedOffsetsInPrimitives=*/{0, 1, 2, 4, 5, 6, 8, 9, 10});
}
}
DEF_GRAPHITE_TEST(UniformManagerStd140F16MatrixLayoutContents, r, CtsEnforcement::kApiLevel_202404) {
UniformManager mgr(Layout::kStd140_F16);
// 2x2
{
expect_matrix(r, mgr, SkSLType::kFloat2x2, /*isFullPrecision=*/true,
/*expectedSizeInBytes=*/32, /*expectedOffsetsInPrimitives=*/{0, 1, 4, 5});
expect_matrix(r, mgr, SkSLType::kHalf2x2, /*isFullPrecision=*/false,
/*expectedSizeInBytes=*/32, /*expectedOffsetsInPrimitives=*/{0, 1, 8, 9});
}
// 3x3
{
expect_matrix(r, mgr, SkSLType::kFloat3x3, /*isFullPrecision=*/true,
/*expectedSizeInBytes=*/48,
/*expectedOffsetsInPrimitives=*/{0, 1, 2, 4, 5, 6, 8, 9, 10});
expect_matrix(r, mgr, SkSLType::kHalf3x3, /*isFullPrecision=*/false,
/*expectedSizeInBytes=*/48,
/*expectedOffsetsInPrimitives=*/{0, 1, 2, 8, 9, 10, 16, 17, 18});
}
// 4x4
{
expect_matrix(r, mgr, SkSLType::kFloat4x4, /*isFullPrecision=*/true,
/*expectedSizeInBytes=*/64,
/*expectedOffsetsInPrimitives=*/{0, 1, 2, 3,
4, 5, 6, 7,
8, 9, 10, 11,
12, 13, 14, 15});
expect_matrix(r, mgr, SkSLType::kHalf4x4, /*isFullPrecision=*/false,
/*expectedSizeInBytes=*/64,
/*expectedOffsetsInPrimitives=*/{0, 1, 2, 3,
8, 9, 10, 11,
16, 17, 18, 19,
24, 25, 26, 27});
}
}
DEF_GRAPHITE_TEST(UniformManagerStd430MatrixLayoutContents, r, CtsEnforcement::kApiLevel_202404) {
UniformManager mgr(Layout::kStd430);
// 2x2
for (SkSLType type : {SkSLType::kFloat2x2, SkSLType::kHalf2x2}) {
expect_matrix(r, mgr, type, /*isFullPrecision=*/true, /*expectedSizeInBytes=*/16,
/*expectedOffsetsInPrimitives=*/{0, 1, 2, 3});
}
// 3x3
for (SkSLType type : {SkSLType::kFloat3x3, SkSLType::kHalf3x3}) {
expect_matrix(r, mgr, type, /*isFullPrecision=*/true, /*expectedSizeInBytes=*/48,
/*expectedOffsetsInPrimitives=*/{0, 1, 2, 4, 5, 6, 8, 9, 10});
}
// 4x4
for (SkSLType type : {SkSLType::kFloat4x4, SkSLType::kHalf4x4}) {
expect_matrix(r, mgr, type, /*isFullPrecision=*/true, /*expectedSizeInBytes=*/64,
/*expectedOffsetsInPrimitives=*/{0, 1, 2, 3,
4, 5, 6, 7,
8, 9, 10, 11,
12, 13, 14, 15});
}
}
DEF_GRAPHITE_TEST(UniformManagerStd430F16MatrixLayoutContents, r, CtsEnforcement::kApiLevel_202404) {
UniformManager mgr(Layout::kStd430_F16);
// 2x2
{
expect_matrix(r, mgr, SkSLType::kFloat2x2, /*isFullPrecision=*/true,
/*expectedSizeInBytes=*/16, /*expectedOffsetsInPrimitives=*/{0, 1, 2, 3});
expect_matrix(r, mgr, SkSLType::kHalf2x2, /*isFullPrecision=*/false,
/*expectedSizeInBytes=*/8, /*expectedOffsetsInPrimitives=*/{0, 1, 2, 3});
}
// 3x3
{
expect_matrix(r, mgr, SkSLType::kFloat3x3, /*isFullPrecision=*/true,
/*expectedSizeInBytes=*/48,
/*expectedOffsetsInPrimitives=*/{0, 1, 2, 4, 5, 6, 8, 9, 10});
expect_matrix(r, mgr, SkSLType::kHalf3x3, /*isFullPrecision=*/false,
/*expectedSizeInBytes=*/24,
/*expectedOffsetsInPrimitives=*/{0, 1, 2, 4, 5, 6, 8, 9, 10});
}
// 4x4
{
expect_matrix(r, mgr, SkSLType::kFloat4x4, /*isFullPrecision=*/true,
/*expectedSizeInBytes=*/64,
/*expectedOffsetsInPrimitives=*/{0, 1, 2, 3,
4, 5, 6, 7,
8, 9, 10, 11,
12, 13, 14, 15});
expect_matrix(r, mgr, SkSLType::kHalf4x4, /*isFullPrecision=*/false,
/*expectedSizeInBytes=*/32,
/*expectedOffsetsInPrimitives=*/{0, 1, 2, 3,
4, 5, 6, 7,
8, 9, 10, 11,
12, 13, 14, 15});
}
}
DEF_GRAPHITE_TEST(UniformManagerMetalMatrixLayoutContents, r, CtsEnforcement::kApiLevel_202404) {
UniformManager mgr(Layout::kMetal);
// 2x2
{
expect_matrix(r, mgr, SkSLType::kFloat2x2, /*isFullPrecision=*/true,
/*expectedSizeInBytes=*/16, /*expectedOffsetsInPrimitives=*/{0, 1, 2, 3});
expect_matrix(r, mgr, SkSLType::kHalf2x2, /*isFullPrecision=*/false,
/*expectedSizeInBytes=*/8, /*expectedOffsetsInPrimitives=*/{0, 1, 2, 3});
}
// 3x3
{
expect_matrix(r, mgr, SkSLType::kFloat3x3, /*isFullPrecision=*/true,
/*expectedSizeInBytes=*/48,
/*expectedOffsetsInPrimitives=*/{0, 1, 2, 4, 5, 6, 8, 9, 10});
expect_matrix(r, mgr, SkSLType::kHalf3x3, /*isFullPrecision=*/false,
/*expectedSizeInBytes=*/24,
/*expectedOffsetsInPrimitives=*/{0, 1, 2, 4, 5, 6, 8, 9, 10});
}
// 4x4
{
expect_matrix(r, mgr, SkSLType::kFloat4x4, /*isFullPrecision=*/true,
/*expectedSizeInBytes=*/64,
/*expectedOffsetsInPrimitives=*/{0, 1, 2, 3,
4, 5, 6, 7,
8, 9, 10, 11,
12, 13, 14, 15});
expect_matrix(r, mgr, SkSLType::kHalf4x4, /*isFullPrecision=*/false,
/*expectedSizeInBytes=*/32,
/*expectedOffsetsInPrimitives=*/{0, 1, 2, 3,
4, 5, 6, 7,
8, 9, 10, 11,
12, 13, 14, 15});
}
}
// These tests validate that substructs are written and aligned appropriately.
DEF_GRAPHITE_TEST(UniformManagerStructLayout, r, CtsEnforcement::kNever) {
static constexpr uint16_t _ = 0; // 0s will only be written as padding
static const struct TestCase {
// For convenience, these should only have kFloat[2,3,4] or only kHalf[2,3,4] as their
// types. However the values written are integers and will be bit-punned to floats so that
// expectations are easy to define (UniformManager and Skia have no defined kInt3 object
// type). Written integers start at 1 and are incremented with each component across
// uniforms. 4-byte primitive types copy the expected integer value into the low and high
// 16-bits
//
// Expectations are written in 16-bit units.
std::vector<Uniform> fPreStruct; // before beginStruct(), top-level fields or struct
std::vector<Uniform> fSubstruct; // within beginStruct()/endStruct()
std::vector<Uniform> fPostStruct; // after endStruct(), top-level fields
std::pair<int, std::vector<uint16_t>> fExpectedAlignmentAndData[std::size(kLayouts)];
// If non-empty, holds base alignments for fPreStruct base alignment as a struct
std::vector<int> fPreStructAlignments = {};
} kCases[] = {
// Struct tests with no preceeding or following top-level fields
// NOTE: For kFloat types, the f16 layout variants are equivalent to the regular layout,
// and for kHalf types, the regular layouts are equivalent to their kFloat cases
{/*prestruct=*/ {},
/*substruct=*/ {{"u1", SkSLType::kFloat},
{"u2", SkSLType::kFloat},
{"u3", SkSLType::kFloat},
{"u4", SkSLType::kFloat}},
/*poststruct=*/{},
/*expect=*/{
/*std140=*/ {/*baseAlign=*/16, /*data=*/{1,1, 2,2, 3,3, 4,4}},
/*std140-f16=*/{/*baseAlign=*/16, /*data=*/{1,1, 2,2, 3,3, 4,4}},
/*std430=*/ {/*baseAlign=*/4, /*data=*/{1,1, 2,2, 3,3, 4,4}},
/*std430-f16=*/{/*baseAlign=*/4, /*data=*/{1,1, 2,2, 3,3, 4,4}},
/*metal=*/ {/*baseAlign=*/4, /*data=*/{1,1, 2,2, 3,3, 4,4}}
}},
{/*prestruct=*/ {},
/*substruct=*/ {{"u1", SkSLType::kHalf},
{"u2", SkSLType::kHalf},
{"u3", SkSLType::kHalf},
{"u4", SkSLType::kHalf}},
/*poststruct=*/{},
/*expect=*/{
/*std140=*/ {/*baseAlign=*/16, /*data=*/{1,_, 2,_, 3,_, 4,_}},
/*std140-f16=*/{/*baseAlign=*/16, /*data=*/{1, 2, 3, 4, _,_,_,_}},
/*std430=*/ {/*baseAlign=*/4, /*data=*/{1,_, 2,_, 3,_, 4,_}},
/*std430-f16=*/{/*baseAlign=*/2, /*data=*/{1, 2, 3, 4}},
/*metal=*/ {/*baseAlign=*/2, /*data=*/{1, 2, 3, 4}}
}},
{/*prestruct=*/ {},
/*substruct=*/ {{"u1", SkSLType::kFloat3},
{"u2", SkSLType::kFloat}},
/*poststruct=*/{},
/*expect=*/{
/*std140=*/ {/*baseAlign=*/16, /*data=*/{1,1,2,2,3,3, 4,4}},
/*std140-f16=*/{/*baseAlign=*/16, /*data=*/{1,1,2,2,3,3, 4,4}},
/*std430=*/ {/*baseAlign=*/16, /*data=*/{1,1,2,2,3,3, 4,4}},
/*std430-f16=*/{/*baseAlign=*/16, /*data=*/{1,1,2,2,3,3, 4,4}},
/*metal=*/ {/*baseAlign=*/16, /*data=*/{1,1,2,2,3,3,_,_, 4,4,_,_,_,_,_,_}}
}},
{/*prestruct=*/ {},
/*substruct=*/ {{"u1", SkSLType::kHalf3},
{"u2", SkSLType::kHalf}},
/*poststruct=*/{},
/*expect=*/{
/*std140=*/ {/*baseAlign=*/16, /*data=*/{1,_,2,_,3,_, 4,_}},
/*std140-f16=*/{/*baseAlign=*/16, /*data=*/{1,2,3, 4, _,_,_,_}},
/*std430=*/ {/*baseAlign=*/16, /*data=*/{1,_,2,_,3,_, 4,_}},
/*std430-f16=*/{/*baseAlign=*/8, /*data=*/{1,2,3, 4}},
/*metal=*/ {/*baseAlign=*/8, /*data=*/{1,2,3,_, 4,_,_,_}}
}},
{/*prestruct=*/ {},
/*substruct=*/ {{"u1", SkSLType::kFloat2},
{"u2", SkSLType::kFloat},
{"u3", SkSLType::kFloat},
{"u4", SkSLType::kFloat}},
/*poststruct=*/{},
/*expect=*/{
/*std140=*/ {/*baseAlign=*/16, /*data=*/{1,1,2,2, 3,3, 4,4, 5,5,_,_,_,_,_,_}},
/*std140-f16=*/{/*baseAlign=*/16, /*data=*/{1,1,2,2, 3,3, 4,4, 5,5,_,_,_,_,_,_}},
/*std430=*/ {/*baseAlign=*/8, /*data=*/{1,1,2,2, 3,3, 4,4, 5,5,_,_}},
/*std430-f16=*/{/*baseAlign=*/8, /*data=*/{1,1,2,2, 3,3, 4,4, 5,5,_,_}},
/*metal=*/ {/*baseAlign=*/8, /*data=*/{1,1,2,2, 3,3, 4,4, 5,5,_,_}}
}},
{/*prestruct=*/ {},
/*substruct=*/ {{"u1", SkSLType::kHalf2},
{"u2", SkSLType::kHalf},
{"u3", SkSLType::kHalf},
{"u4", SkSLType::kHalf}},
/*poststruct=*/{},
/*expect=*/{
/*std140=*/ {/*baseAlign=*/16, /*data=*/{1,_,2,_, 3,_, 4,_, 5,_,_,_,_,_,_,_}},
/*std140-f16=*/{/*baseAlign=*/16, /*data=*/{1,2, 3, 4, 5,_,_,_}},
/*std430=*/ {/*baseAlign=*/8, /*data=*/{1,_,2,_, 3,_, 4,_, 5,_,_,_}},
/*std430-f16=*/{/*baseAlign=*/4, /*data=*/{1,2, 3, 4, 5,_}},
/*metal=*/ {/*baseAlign=*/4, /*data=*/{1,2, 3, 4, 5,_}}
}},
{/*prestruct=*/ {},
/*substruct=*/ {{"u1", SkSLType::kFloat},
{"u2", SkSLType::kFloat4},
{"u3", SkSLType::kFloat2},
{"u4", SkSLType::kFloat3}},
/*poststruct=*/{},
/*expect=*/{
/*std140=*/ {/*baseAlign=*/16, /*data=*/{1,1,_,_,_,_,_,_, 2,2,3,3,4,4,5,5, 6,6,7,7,_,_,_,_, 8,8,9,9,10,10,_,_}},
/*std140-f16=*/{/*baseAlign=*/16, /*data=*/{1,1,_,_,_,_,_,_, 2,2,3,3,4,4,5,5, 6,6,7,7,_,_,_,_, 8,8,9,9,10,10,_,_}},
/*std430=*/ {/*baseAlign=*/16, /*data=*/{1,1,_,_,_,_,_,_, 2,2,3,3,4,4,5,5, 6,6,7,7,_,_,_,_, 8,8,9,9,10,10,_,_}},
/*std430-f16=*/{/*baseAlign=*/16, /*data=*/{1,1,_,_,_,_,_,_, 2,2,3,3,4,4,5,5, 6,6,7,7,_,_,_,_, 8,8,9,9,10,10,_,_}},
/*metal=*/ {/*baseAlign=*/16, /*data=*/{1,1,_,_,_,_,_,_, 2,2,3,3,4,4,5,5, 6,6,7,7,_,_,_,_, 8,8,9,9,10,10,_,_}}
}},
{/*prestruct=*/ {},
/*substruct=*/ {{"u1", SkSLType::kHalf},
{"u2", SkSLType::kHalf4},
{"u3", SkSLType::kHalf2},
{"u4", SkSLType::kHalf3}},
/*poststruct=*/{},
/*expect=*/{
/*std140=*/ {/*baseAlign=*/16, /*data=*/{1,_,_,_,_,_,_,_, 2,_,3,_,4,_,5,_, 6,_,7,_,_,_,_,_, 8,_,9,_,10,_,_,_}},
/*std140-f16=*/{/*baseAlign=*/16, /*data=*/{1,_,_,_, 2,3,4,5, 6,7,_,_, 8,9,10,_}},
/*std430=*/ {/*baseAlign=*/16, /*data=*/{1,_,_,_,_,_,_,_, 2,_,3,_,4,_,5,_, 6,_,7,_,_,_,_,_, 8,_,9,_,10,_,_,_}},
/*std430-f16=*/{/*baseAlign=*/8, /*data=*/{1,_,_,_, 2,3,4,5, 6,7,_,_, 8,9,10,_}},
/*metal=*/ {/*baseAlign=*/8, /*data=*/{1,_,_,_, 2,3,4,5, 6,7,_,_, 8,9,10,_}}
}},
// // Struct tests with a preceeding float to require padding to the struct's base alignment
{/*prestruct=*/ {{"p1", SkSLType::kFloat}},
/*substruct=*/ {{"u1", SkSLType::kFloat},
{"u2", SkSLType::kFloat},
{"u3", SkSLType::kFloat},
{"u4", SkSLType::kFloat}},
/*poststruct=*/{},
/*expect=*/{
/*std140=*/ {/*baseAlign=*/16, /*data=*/{1,1,_,_,_,_,_,_, 2,2, 3,3, 4,4, 5,5}},
/*std140-f16=*/{/*baseAlign=*/16, /*data=*/{1,1,_,_,_,_,_,_, 2,2, 3,3, 4,4, 5,5}},
/*std430=*/ {/*baseAlign=*/4, /*data=*/{1,1, 2,2, 3,3, 4,4, 5,5}},
/*std430-f16=*/{/*baseAlign=*/4, /*data=*/{1,1, 2,2, 3,3, 4,4, 5,5}},
/*metal=*/ {/*baseAlign=*/4, /*data=*/{1,1, 2,2, 3,3, 4,4, 5,5}}
}},
{/*prestruct=*/ {{"p1", SkSLType::kHalf}},
/*substruct=*/ {{"u1", SkSLType::kHalf},
{"u2", SkSLType::kHalf},
{"u3", SkSLType::kHalf},
{"u4", SkSLType::kHalf}},
/*poststruct=*/{},
/*expect=*/{
/*std140=*/ {/*baseAlign=*/16, /*data=*/{1,_,_,_,_,_,_,_, 2,_, 3,_, 4,_, 5,_}},
/*std140-f16=*/{/*baseAlign=*/16, /*data=*/{1,_,_,_,_,_,_,_, 2, 3, 4, 5,_,_,_,_}},
/*std430=*/ {/*baseAlign=*/4, /*data=*/{1,_, 2,_, 3,_, 4,_, 5,_}},
/*std430-f16=*/{/*baseAlign=*/2, /*data=*/{1, 2, 3, 4, 5}},
/*metal=*/ {/*baseAlign=*/2, /*data=*/{1, 2, 3, 4, 5}}
}},
{/*prestruct=*/ {{"p1", SkSLType::kFloat}},
/*substruct=*/ {{"u1", SkSLType::kFloat3},
{"u2", SkSLType::kFloat}},
/*poststruct=*/{},
/*expect=*/{
/*std140=*/ {/*baseAlign=*/16, /*data=*/{1,1,_,_,_,_,_,_, 2,2,3,3,4,4, 5,5}},
/*std140-f16=*/{/*baseAlign=*/16, /*data=*/{1,1,_,_,_,_,_,_, 2,2,3,3,4,4, 5,5}},
/*std430=*/ {/*baseAlign=*/16, /*data=*/{1,1,_,_,_,_,_,_, 2,2,3,3,4,4, 5,5}},
/*std430-f16=*/{/*baseAlign=*/16, /*data=*/{1,1,_,_,_,_,_,_, 2,2,3,3,4,4, 5,5}},
/*metal=*/ {/*baseAlign=*/16, /*data=*/{1,1,_,_,_,_,_,_, 2,2,3,3,4,4,_,_, 5,5,_,_,_,_,_,_}}
}},
{/*prestruct=*/ {{"p1", SkSLType::kHalf}},
/*substruct=*/ {{"u1", SkSLType::kHalf3},
{"u2", SkSLType::kHalf}},
/*poststruct=*/{},
/*expect=*/{
/*std140=*/ {/*baseAlign=*/16, /*data=*/{1,_,_,_,_,_,_,_, 2,_,3,_,4,_, 5,_}},
/*std140-f16=*/{/*baseAlign=*/16, /*data=*/{1,_,_,_,_,_,_,_, 2,3,4, 5,_,_,_,_}},
/*std430=*/ {/*baseAlign=*/16, /*data=*/{1,_,_,_,_,_,_,_, 2,_,3,_,4,_, 5,_}},
/*std430-f16=*/{/*baseAlign=*/8, /*data=*/{1,_,_,_, 2,3,4, 5}},
/*metal=*/ {/*baseAlign=*/8, /*data=*/{1,_,_,_, 2,3,4,_, 5,_,_,_}}
}},
{/*prestruct=*/ {{"p1", SkSLType::kFloat}},
/*substruct=*/ {{"u1", SkSLType::kFloat2},
{"u2", SkSLType::kFloat},
{"u3", SkSLType::kFloat},
{"u4", SkSLType::kFloat}},
/*poststruct=*/{},
/*expect=*/{
/*std140=*/ {/*baseAlign=*/16, /*data=*/{1,1,_,_,_,_,_,_, 2,2,3,3, 4,4, 5,5, 6,6,_,_,_,_,_,_}},
/*std140-f16=*/{/*baseAlign=*/16, /*data=*/{1,1,_,_,_,_,_,_, 2,2,3,3, 4,4, 5,5, 6,6,_,_,_,_,_,_}},
/*std430=*/ {/*baseAlign=*/8, /*data=*/{1,1,_,_, 2,2,3,3, 4,4, 5,5, 6,6,_,_}},
/*std430-f16=*/{/*baseAlign=*/8, /*data=*/{1,1,_,_, 2,2,3,3, 4,4, 5,5, 6,6,_,_}},
/*metal=*/ {/*baseAlign=*/8, /*data=*/{1,1,_,_, 2,2,3,3, 4,4, 5,5, 6,6,_,_}}
}},
{/*prestruct=*/ {{"p1", SkSLType::kHalf}},
/*substruct=*/ {{"u1", SkSLType::kHalf2},
{"u2", SkSLType::kHalf},
{"u3", SkSLType::kHalf},
{"u4", SkSLType::kHalf}},
/*poststruct=*/{},
/*expect=*/{
/*std140=*/ {/*baseAlign=*/16, /*data=*/{1,_,_,_,_,_,_,_, 2,_,3,_, 4,_, 5,_, 6,_,_,_,_,_,_,_}},
/*std140-f16=*/{/*baseAlign=*/16, /*data=*/{1,_,_,_,_,_,_,_, 2,3, 4, 5, 6,_,_,_,}},
/*std430=*/ {/*baseAlign=*/8, /*data=*/{1,_,_,_, 2,_,3,_, 4,_, 5,_, 6,_,_,_}},
/*std430-f16=*/{/*baseAlign=*/4, /*data=*/{1,_, 2,3, 4, 5, 6,_}},
/*metal=*/ {/*baseAlign=*/4, /*data=*/{1,_, 2,3, 4, 5, 6,_}}
}},
{/*prestruct=*/ {{"p1", SkSLType::kFloat}},
/*substruct=*/ {{"u1", SkSLType::kFloat},
{"u2", SkSLType::kFloat4},
{"u3", SkSLType::kFloat2},
{"u4", SkSLType::kFloat3}},
/*poststruct=*/{},
/*expect=*/{
/*std140=*/ {/*baseAlign=*/16, /*data=*/{1,1,_,_,_,_,_,_, 2,2,_,_,_,_,_,_, 3,3,4,4,5,5,6,6, 7,7,8,8,_,_,_,_, 9,9,10,10,11,11,_,_}},
/*std140-f16=*/{/*baseAlign=*/16, /*data=*/{1,1,_,_,_,_,_,_, 2,2,_,_,_,_,_,_, 3,3,4,4,5,5,6,6, 7,7,8,8,_,_,_,_, 9,9,10,10,11,11,_,_}},
/*std430=*/ {/*baseAlign=*/16, /*data=*/{1,1,_,_,_,_,_,_, 2,2,_,_,_,_,_,_, 3,3,4,4,5,5,6,6, 7,7,8,8,_,_,_,_, 9,9,10,10,11,11,_,_}},
/*std430-f16=*/{/*baseAlign=*/16, /*data=*/{1,1,_,_,_,_,_,_, 2,2,_,_,_,_,_,_, 3,3,4,4,5,5,6,6, 7,7,8,8,_,_,_,_, 9,9,10,10,11,11,_,_}},
/*metal=*/ {/*baseAlign=*/16, /*data=*/{1,1,_,_,_,_,_,_, 2,2,_,_,_,_,_,_, 3,3,4,4,5,5,6,6, 7,7,8,8,_,_,_,_, 9,9,10,10,11,11,_,_}}
}},
{/*prestruct=*/ {{"p1", SkSLType::kHalf}},
/*substruct=*/ {{"u1", SkSLType::kHalf},
{"u2", SkSLType::kHalf4},
{"u3", SkSLType::kHalf2},
{"u4", SkSLType::kHalf3}},
/*poststruct=*/{},
/*expect=*/{
/*std140=*/ {/*baseAlign=*/16, /*data=*/{1,_,_,_,_,_,_,_, 2,_,_,_,_,_,_,_, 3,_,4,_,5,_,6,_, 7,_,8,_,_,_,_,_, 9,_,10,_,11,_,_,_}},
/*std140-f16=*/{/*baseAlign=*/16, /*data=*/{1,_,_,_,_,_,_,_, 2,_,_,_, 3,4,5,6, 7,8,_,_, 9,10,11,_}},
/*std430=*/ {/*baseAlign=*/16, /*data=*/{1,_,_,_,_,_,_,_, 2,_,_,_,_,_,_,_, 3,_,4,_,5,_,6,_, 7,_,8,_,_,_,_,_, 9,_,10,_,11,_,_,_}},
/*std430-f16=*/{/*baseAlign=*/8, /*data=*/{1,_,_,_, 2,_,_,_, 3,4,5,6, 7,8,_,_, 9,10,11,_}},
/*metal=*/ {/*baseAlign=*/8, /*data=*/{1,_,_,_, 2,_,_,_, 3,4,5,6, 7,8,_,_, 9,10,11,_}}
}},
// Struct tests with a preceeding float to require padding to the struct's base alignment,
// and a following float4 to test alignment after a struct.
{/*prestruct=*/ {{"p1", SkSLType::kFloat}},
/*substruct=*/ {{"u1", SkSLType::kFloat},
{"u2", SkSLType::kFloat},
{"u3", SkSLType::kFloat},
{"u4", SkSLType::kFloat}},
/*poststruct=*/{{"p2", SkSLType::kFloat4}},
/*expect=*/{
/*std140=*/ {/*baseAlign=*/16, /*data=*/{1,1,_,_,_,_,_,_, 2,2, 3,3, 4,4, 5,5, 6,6,7,7,8,8,9,9}},
/*std140-f16=*/{/*baseAlign=*/16, /*data=*/{1,1,_,_,_,_,_,_, 2,2, 3,3, 4,4, 5,5, 6,6,7,7,8,8,9,9}},
/*std430=*/ {/*baseAlign=*/4, /*data=*/{1,1, 2,2, 3,3, 4,4, 5,5, _,_,_,_,_,_,6,6,7,7,8,8,9,9}},
/*std430-f16=*/{/*baseAlign=*/4, /*data=*/{1,1, 2,2, 3,3, 4,4, 5,5, _,_,_,_,_,_,6,6,7,7,8,8,9,9}},
/*metal=*/ {/*baseAlign=*/4, /*data=*/{1,1, 2,2, 3,3, 4,4, 5,5, _,_,_,_,_,_,6,6,7,7,8,8,9,9}}
}},
{/*prestruct=*/ {{"p1", SkSLType::kHalf}},
/*substruct=*/ {{"u1", SkSLType::kHalf},
{"u2", SkSLType::kHalf},
{"u3", SkSLType::kHalf},
{"u4", SkSLType::kHalf}},
/*poststruct=*/{{"p2", SkSLType::kHalf4}},
/*expect=*/{
/*std140=*/ {/*baseAlign=*/16, /*data=*/{1,_,_,_,_,_,_,_, 2,_, 3,_, 4,_, 5,_, 6,_,7,_,8,_,9,_}},
/*std140-f16=*/{/*baseAlign=*/16, /*data=*/{1,_,_,_,_,_,_,_, 2, 3, 4, 5,_,_,_,_, 6,7,8,9,_,_,_,_}},
/*std430=*/ {/*baseAlign=*/4, /*data=*/{1,_, 2,_, 3,_, 4,_, 5,_, _,_,_,_,_,_,6,_,7,_,8,_,9,_}},
/*std430-f16=*/{/*baseAlign=*/2, /*data=*/{1, 2, 3, 4, 5, _,_,_,6,7,8,9}},
/*metal=*/ {/*baseAlign=*/2, /*data=*/{1, 2, 3, 4, 5, _,_,_,6,7,8,9}}
}},
{/*prestruct=*/ {{"p1", SkSLType::kFloat}},
/*substruct=*/ {{"u1", SkSLType::kFloat3},
{"u2", SkSLType::kFloat}},
/*poststruct=*/{{"p2", SkSLType::kFloat4}},
/*expect=*/{
/*std140=*/ {/*baseAlign=*/16, /*data=*/{1,1,_,_,_,_,_,_, 2,2,3,3,4,4, 5,5, 6,6,7,7,8,8,9,9}},
/*std140-f16=*/{/*baseAlign=*/16, /*data=*/{1,1,_,_,_,_,_,_, 2,2,3,3,4,4, 5,5, 6,6,7,7,8,8,9,9}},
/*std430=*/ {/*baseAlign=*/16, /*data=*/{1,1,_,_,_,_,_,_, 2,2,3,3,4,4, 5,5, 6,6,7,7,8,8,9,9}},
/*std430-f16=*/{/*baseAlign=*/16, /*data=*/{1,1,_,_,_,_,_,_, 2,2,3,3,4,4, 5,5, 6,6,7,7,8,8,9,9}},
/*metal=*/ {/*baseAlign=*/16, /*data=*/{1,1,_,_,_,_,_,_, 2,2,3,3,4,4,_,_, 5,5,_,_,_,_,_,_, 6,6,7,7,8,8,9,9}}
}},
{/*prestruct=*/ {{"p1", SkSLType::kHalf}},
/*substruct=*/ {{"u1", SkSLType::kHalf3},
{"u2", SkSLType::kHalf}},
/*poststruct=*/{{"p2", SkSLType::kHalf4}},
/*expect=*/{
/*std140=*/ {/*baseAlign=*/16, /*data=*/{1,_,_,_,_,_,_,_, 2,_,3,_,4,_, 5,_, 6,_,7,_,8,_,9,_}},
/*std140-f16=*/{/*baseAlign=*/16, /*data=*/{1,_,_,_,_,_,_,_, 2,3,4, 5,_,_,_,_, 6,7,8,9,_,_,_,_}},
/*std430=*/ {/*baseAlign=*/16, /*data=*/{1,_,_,_,_,_,_,_, 2,_,3,_,4,_, 5,_, 6,_,7,_,8,_,9,_}},
/*std430-f16=*/{/*baseAlign=*/8, /*data=*/{1,_,_,_, 2,3,4, 5, 6,7,8,9}},
/*metal=*/ {/*baseAlign=*/8, /*data=*/{1,_,_,_, 2,3,4,_, 5,_,_,_, 6,7,8,9}}
}},
{/*prestruct=*/ {{"p1", SkSLType::kFloat}},
/*substruct=*/ {{"u1", SkSLType::kFloat2},
{"u2", SkSLType::kFloat},
{"u3", SkSLType::kFloat},
{"u4", SkSLType::kFloat}},
/*poststruct=*/{{"p2", SkSLType::kFloat4}},
/*expect=*/{
/*std140=*/ {/*baseAlign=*/16, /*data=*/{1,1,_,_,_,_,_,_, 2,2,3,3, 4,4, 5,5, 6,6,_,_,_,_,_,_, 7,7,8,8,9,9,10,10}},
/*std140-f16=*/{/*baseAlign=*/16, /*data=*/{1,1,_,_,_,_,_,_, 2,2,3,3, 4,4, 5,5, 6,6,_,_,_,_,_,_, 7,7,8,8,9,9,10,10}},
/*std430=*/ {/*baseAlign=*/8, /*data=*/{1,1,_,_, 2,2,3,3, 4,4, 5,5, 6,6,_,_, 7,7,8,8,9,9,10,10}},
/*std430-f16=*/{/*baseAlign=*/8, /*data=*/{1,1,_,_, 2,2,3,3, 4,4, 5,5, 6,6,_,_, 7,7,8,8,9,9,10,10}},
/*metal=*/ {/*baseAlign=*/8, /*data=*/{1,1,_,_, 2,2,3,3, 4,4, 5,5, 6,6,_,_, 7,7,8,8,9,9,10,10}}
}},
{/*prestruct=*/ {{"p1", SkSLType::kHalf}},
/*substruct=*/ {{"u1", SkSLType::kHalf2},
{"u2", SkSLType::kHalf},
{"u3", SkSLType::kHalf},
{"u4", SkSLType::kHalf}},
/*poststruct=*/{{"p2", SkSLType::kHalf4}},
/*expect=*/{
/*std140=*/ {/*baseAlign=*/16, /*data=*/{1,_,_,_,_,_,_,_, 2,_,3,_, 4,_, 5,_, 6,_,_,_,_,_,_,_, 7,_,8,_,9,_,10,_}},
/*std140-f16=*/{/*baseAlign=*/16, /*data=*/{1,_,_,_,_,_,_,_, 2,3, 4, 5, 6,_,_,_, 7,8,9,10,_,_,_,_}},
/*std430=*/ {/*baseAlign=*/8, /*data=*/{1,_,_,_, 2,_,3,_, 4,_, 5,_, 6,_,_,_, 7,_,8,_,9,_,10,_}},
/*std430-f16=*/{/*baseAlign=*/4, /*data=*/{1,_, 2,3, 4, 5, 6,_, 7,8,9,10}},
/*metal=*/ {/*baseAlign=*/4, /*data=*/{1,_, 2,3, 4, 5, 6,_, 7,8,9,10}}
}},
{/*prestruct=*/ {{"p1", SkSLType::kFloat}},
/*substruct=*/ {{"u1", SkSLType::kFloat},
{"u2", SkSLType::kFloat4},
{"u3", SkSLType::kFloat2},
{"u4", SkSLType::kFloat3}},
/*poststruct=*/{{"p2", SkSLType::kFloat4}},
/*expect=*/{
/*std140=*/ {/*baseAlign=*/16, /*data=*/{1,1,_,_,_,_,_,_, 2,2,_,_,_,_,_,_, 3,3,4,4,5,5,6,6, 7,7,8,8,_,_,_,_, 9,9,10,10,11,11,_,_, 12,12,13,13,14,14,15,15}},
/*std140-f16=*/{/*baseAlign=*/16, /*data=*/{1,1,_,_,_,_,_,_, 2,2,_,_,_,_,_,_, 3,3,4,4,5,5,6,6, 7,7,8,8,_,_,_,_, 9,9,10,10,11,11,_,_, 12,12,13,13,14,14,15,15}},
/*std430=*/ {/*baseAlign=*/16, /*data=*/{1,1,_,_,_,_,_,_, 2,2,_,_,_,_,_,_, 3,3,4,4,5,5,6,6, 7,7,8,8,_,_,_,_, 9,9,10,10,11,11,_,_, 12,12,13,13,14,14,15,15}},
/*std430-f16=*/{/*baseAlign=*/16, /*data=*/{1,1,_,_,_,_,_,_, 2,2,_,_,_,_,_,_, 3,3,4,4,5,5,6,6, 7,7,8,8,_,_,_,_, 9,9,10,10,11,11,_,_, 12,12,13,13,14,14,15,15}},
/*metal=*/ {/*baseAlign=*/16, /*data=*/{1,1,_,_,_,_,_,_, 2,2,_,_,_,_,_,_, 3,3,4,4,5,5,6,6, 7,7,8,8,_,_,_,_, 9,9,10,10,11,11,_,_, 12,12,13,13,14,14,15,15}}
}},
{/*prestruct=*/ {{"p1", SkSLType::kHalf}},
/*substruct=*/ {{"u1", SkSLType::kHalf},
{"u2", SkSLType::kHalf4},
{"u3", SkSLType::kHalf2},
{"u4", SkSLType::kHalf3}},
/*poststruct=*/{{"p2", SkSLType::kHalf4}},
/*expect=*/{
/*std140=*/ {/*baseAlign=*/16, /*data=*/{1,_,_,_,_,_,_,_, 2,_,_,_,_,_,_,_, 3,_,4,_,5,_,6,_, 7,_,8,_,_,_,_,_, 9,_,10,_,11,_,_,_, 12,_,13,_,14,_,15,_}},
/*std140-f16=*/{/*baseAlign=*/16, /*data=*/{1,_,_,_,_,_,_,_, 2,_,_,_, 3,4,5,6, 7,8,_,_, 9,10,11,_, 12,13,14,15,_,_,_,_}},
/*std430=*/ {/*baseAlign=*/16, /*data=*/{1,_,_,_,_,_,_,_, 2,_,_,_,_,_,_,_, 3,_,4,_,5,_,6,_, 7,_,8,_,_,_,_,_, 9,_,10,_,11,_,_,_, 12,_,13,_,14,_,15,_}},
/*std430-f16=*/{/*baseAlign=*/8, /*data=*/{1,_,_,_, 2,_,_,_, 3,4,5,6, 7,8,_,_, 9,10,11,_, 12,13,14,15}},
/*metal=*/ {/*baseAlign=*/8, /*data=*/{1,_,_,_, 2,_,_,_, 3,4,5,6, 7,8,_,_, 9,10,11,_, 12,13,14,15}}
}},
// Struct tests with two adjacent structs
{/*prestruct=*/ {{"p1", SkSLType::kFloat2},
{"p2", SkSLType::kFloat}},
/*substruct=*/ {{"u1", SkSLType::kFloat},
{"u2", SkSLType::kFloat}},
/*poststruct=*/{},
/*expect=*/{
/*std140=*/ {/*baseAlign=*/16, /*data=*/{1,1,2,2, 3,3,_,_, 4,4, 5,5,_,_,_,_}},
/*std140-f16=*/{/*baseAlign=*/16, /*data=*/{1,1,2,2, 3,3,_,_, 4,4, 5,5,_,_,_,_}},
/*std430=*/ {/*baseAlign=*/4, /*data=*/{1,1,2,2, 3,3,_,_, 4,4, 5,5}},
/*std430-f16=*/{/*baseAlign=*/4, /*data=*/{1,1,2,2, 3,3,_,_, 4,4, 5,5}},
/*metal=*/ {/*baseAlign=*/4, /*data=*/{1,1,2,2, 3,3,_,_, 4,4, 5,5}}
},
/*preStructAlignments=*/{
/*std140=*/16,
/*std140-f16=*/16,
/*std430=*/8,
/*std430-f16=*/8,
/*metal=*/ 8
}},
{/*prestruct=*/ {{"p1", SkSLType::kHalf2},
{"p2", SkSLType::kHalf}},
/*substruct=*/ {{"u1", SkSLType::kHalf},
{"u2", SkSLType::kHalf}},
/*poststruct=*/{},
/*expect=*/{
/*std140=*/ {/*baseAlign=*/16, /*data=*/{1,_,2,_, 3,_,_,_, 4,_, 5,_,_,_,_,_}},
/*std140-f16=*/{/*baseAlign=*/16, /*data=*/{1,2, 3,_,_,_,_,_, 4, 5,_,_,_,_,_,_}},
/*std430=*/ {/*baseAlign=*/4, /*data=*/{1,_,2,_, 3,_,_,_, 4,_, 5,_}},
/*std430-f16=*/{/*baseAlign=*/2, /*data=*/{1,2, 3,_, 4, 5}},
/*metal=*/ {/*baseAlign=*/2, /*data=*/{1,2, 3,_, 4, 5}}
},
/*preStructAlignments=*/{
/*std140=*/16,
/*std140-f16=*/16,
/*std430=*/8,
/*std430-f16=*/4,
/*metal=*/ 4
}}
};
auto writeFields = [](UniformManager* mgr, SkSpan<const Uniform> fields, uint32_t baseValue) {
auto gen16Bit = [mgr](int baseValue) {
if (LayoutRules::UseFullPrecision(mgr->layout())) {
return SkBits2Float(baseValue);
} else {
return SkHalfToFloat((SkHalf) baseValue);
}
};
auto gen32Bit = [](int baseValue) { return SkBits2Float(baseValue | (baseValue << 16)); };
for (const Uniform& f : fields) {
switch(f.type()) {
case SkSLType::kHalf:
mgr->writeHalf(gen16Bit(baseValue++));
break;
case SkSLType::kFloat:
mgr->write(gen32Bit(baseValue++));
break;
case SkSLType::kHalf2:
mgr->writeHalf(SkV2{gen16Bit(baseValue++),
gen16Bit(baseValue++)});
break;
case SkSLType::kFloat2:
mgr->write(SkV2{gen32Bit(baseValue++),
gen32Bit(baseValue++)});
break;
case SkSLType::kHalf3:
mgr->writeHalf(SkV3{gen16Bit(baseValue++),
gen16Bit(baseValue++),
gen16Bit(baseValue++)});
break;
case SkSLType::kFloat3:
mgr->write(SkV3{gen32Bit(baseValue++),
gen32Bit(baseValue++),
gen32Bit(baseValue++)});
break;
case SkSLType::kHalf4:
mgr->writeHalf(SkV4{gen16Bit(baseValue++),
gen16Bit(baseValue++),
gen16Bit(baseValue++),
gen16Bit(baseValue++)});
break;
case SkSLType::kFloat4:
mgr->write(SkV4{gen32Bit(baseValue++),
gen32Bit(baseValue++),
gen32Bit(baseValue++),
gen32Bit(baseValue++)});
break;
default:
SkUNREACHABLE;
}
}
return baseValue;
};
bool dataMatchFailureLogged = false;
for (size_t l = 0; l < std::size(kLayouts); ++l) {
const Layout layout = kLayouts[l];
skiatest::ReporterContext layoutLabel(r, LayoutString(layout));
for (size_t t = 0; t < std::size(kCases); ++t) {
const TestCase& test = kCases[t];
skiatest::ReporterContext testLabel(r, std::to_string(t));
auto [baseAlignment, expectedData] = test.fExpectedAlignmentAndData[l];
UniformManager mgr{layout};
int baseValue = 1;
if (!test.fPreStruct.empty()) {
// pre-struct fields
const bool preStructIsStruct = !test.fPreStructAlignments.empty();
SkDEBUGCODE(mgr.setExpectedUniforms(test.fPreStruct, preStructIsStruct);)
if (preStructIsStruct) {
mgr.beginStruct(test.fPreStructAlignments[l]);
}
baseValue = writeFields(&mgr, test.fPreStruct, baseValue);
if (preStructIsStruct) {
mgr.endStruct();
}
SkDEBUGCODE(mgr.doneWithExpectedUniforms();)
}
if (!test.fSubstruct.empty()) {
// substruct fields
SkDEBUGCODE(mgr.setExpectedUniforms(test.fSubstruct, /*isSubstruct=*/true);)
mgr.beginStruct(baseAlignment);
baseValue = writeFields(&mgr, test.fSubstruct, baseValue);
mgr.endStruct();
SkDEBUGCODE(mgr.doneWithExpectedUniforms();)
}
if (!test.fPostStruct.empty()) {
// post-struct fields
SkDEBUGCODE(mgr.setExpectedUniforms(test.fPostStruct, /*isSubstruct=*/false);)
baseValue = writeFields(&mgr, test.fPostStruct, baseValue);
SkDEBUGCODE(mgr.doneWithExpectedUniforms();)
}
SkSpan<const char> data = mgr.finish();
bool sizeMatch = data.size() == sizeof(uint16_t)*expectedData.size();
// To reduce logging/asserts, pretend contents "match" if the sizes differ since that
// will already be triggering test failures
bool contentsMatch = !sizeMatch ||
memcmp(data.data(), expectedData.data(), data.size()) == 0;
REPORTER_ASSERT(r, sizeMatch, "Size mismatch between written (%zu) and expected (%zu)",
data.size(), sizeof(uint16_t)*expectedData.size());
REPORTER_ASSERT(r, contentsMatch, "Contents differ between written and expected");
if (!contentsMatch && !dataMatchFailureLogged) {
// Print out actual and expected values once if it's only the contents that are
// incorrect (don't bother printing contents if their lengths differ).
SkDebugf("Expected contents:\n");
for (size_t i = 0; i < expectedData.size(); ++i) {
SkDebugf("%s%u", i % 8 == 0 ? " " : (i % 2 == 0 ? "," : ""), expectedData[i]);
}
SkDebugf("\nActual contents:\n");
SkASSERT(data.size() % 8 == 0);
const uint16_t* actualData = reinterpret_cast<const uint16_t*>(data.data());
for (size_t i = 0; i < expectedData.size(); ++i) {
SkDebugf("%s%u", i % 8 == 0 ? " " : (i % 2 == 0 ? "," : ""), actualData[i]);
}
SkDebugf("\n\n");
dataMatchFailureLogged = true;
}
}
}
}