/* * 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::kStd430, 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 encodes half-precision uniforms in 16 bits. // Other layouts are expected to encode uniforms in 32 bits. return (layout == Layout::kMetal && !SkSLTypeIsFullPrecisionNumericType(type)) ? 2 : 4; } DEF_GRAPHITE_TEST(UniformManagerCheckSingleUniform, r, CtsEnforcement::kApiLevel_V) { // 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_V) { // 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 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_V) { // 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 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_V) { // 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 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_V) { // 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 uniformData = mgr.finish(); size_t elementSize = 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) { 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_V) { // 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); 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" }}, }; int layoutIdx = static_cast(layout != Layout::kMetal); const size_t size = strlen(kExpectedLayout[layoutIdx][vecLength1][vecLength2]) * elementSize1; SkSpan 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) // _ : padding between uniforms for alignment static constexpr const char* kExpectedLayout[5][5] = { { "", "", "", "", "" }, { "", "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" }, }; const size_t size = strlen(kExpectedLayout[vecLength1][vecLength2]) * 2; SkSpan 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[5][5] = { { "", "", "", "", "" }, { "", "AAB_", "AABB", "AA__BBBb", "AA__BBBB" }, { "", "AAAAB___", "AAAABB__", "AAAABBBb", "AAAABBBB" }, { "", "AAAAAAaaB_______", "AAAAAAaaBB______", "AAAAAAaaBBBb____", "AAAAAAaaBBBB____" }, { "", "AAAAAAAAB_______", "AAAAAAAABB______", "AAAAAAAABBBb____", "AAAAAAAABBBB____" }, }; const size_t size = strlen(kExpectedLayout[vecLength1][vecLength2]) * 2; SkSpan 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_V) { // 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); if (elementSize1 == elementSize2) { // Elements in the array correspond to the element size (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] = { // std140 layout { { "", "", "", "", "" }, { "", "", "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" }, }, }; int layoutIdx = static_cast(layout != Layout::kStd140); const size_t size = strlen(kExpectedLayout[layoutIdx][vecLength1][matSize2]) * elementSize1; SkSpan 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[5][5] = { {"", "", "", "", ""}, {"", "", "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[vecLength1][matSize2]) * 2; SkSpan 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[5][5] = { {"", "", "", "", ""}, {"", "", "AABBBB", "AA__BBBbBBBbBBBb", "AA__BBBBBBBBBBBBBBBB"}, {"", "", "AAAABBBB", "AAAABBBbBBBbBBBb", "AAAABBBBBBBBBBBBBBBB"}, {"", "", "AAAAAAaaBBBB____", "AAAAAAaaBBBbBBBbBBBb____", "AAAAAAaaBBBBBBBBBBBBBBBB"}, {"", "", "AAAAAAAABBBB____", "AAAAAAAABBBbBBBbBBBb____", "AAAAAAAABBBBBBBBBBBBBBBB"}, }; const size_t size = strlen(kExpectedLayout[vecLength1][matSize2]) * 2; SkSpan 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_V) { // 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); if (elementSize1 == elementSize2) { // Elements in the array correspond to the element size (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] = { // 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" }, }, }; int layoutIdx = static_cast(layout != Layout::kStd140); const size_t size = strlen(kExpectedLayout[layoutIdx][matSize1][vecLength2]) * elementSize1; SkSpan 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[5][5] = { { "", "", "", "", "" }, { "", "", "", "", "" }, { "", "AAAABB", "AAAABBBB", "AAAA____BBBBBBbb", "AAAA____BBBBBBBB" }, { "", "AAAaAAAaAAAaBB__", "AAAaAAAaAAAaBBBB", "AAAaAAAaAAAa____BBBBBBbb", "AAAaAAAaAAAa____BBBBBBBB" }, { "", "AAAAAAAAAAAAAAAABB__", "AAAAAAAAAAAAAAAABBBB", "AAAAAAAAAAAAAAAABBBBBBbb", "AAAAAAAAAAAAAAAABBBBBBBB" }, }; const size_t size = strlen(kExpectedLayout[matSize1][vecLength2]) * 2; SkSpan 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[5][5] = { { "", "", "", "", "" }, { "", "", "", "", "" }, { "", "AAAAAAAAB___", "AAAAAAAABB__", "AAAAAAAABBBb", "AAAAAAAABBBB" }, { "", "AAAAAAaaAAAAAAaaAAAAAAaaB_______", "AAAAAAaaAAAAAAaaAAAAAAaaBB______", "AAAAAAaaAAAAAAaaAAAAAAaaBBBb____", "AAAAAAaaAAAAAAaaAAAAAAaaBBBB____" }, { "", "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB_______", "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABB______", "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBb____", "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB____" }, }; const size_t size = strlen(kExpectedLayout[matSize1][vecLength2]) * 2; SkSpan 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_V) { 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 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_V) { 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 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_V) { 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 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. DEF_GRAPHITE_TEST(UniformManagerStd140MatrixLayoutContents, r, CtsEnforcement::kApiLevel_V) { UniformManager mgr(Layout::kStd140); // float2x2, half2x2 for (SkSLType type : {SkSLType::kFloat2x2, SkSLType::kHalf2x2}) { const Uniform expectations[] = {{"m", type}}; SkDEBUGCODE(mgr.setExpectedUniforms(SkSpan(expectations), /*isSubstruct=*/false);) mgr.write(expectations[0], kFloats); SkDEBUGCODE(mgr.doneWithExpectedUniforms();) SkSpan uniformData = mgr.finish(); REPORTER_ASSERT(r, uniformData.size() == 32, "%s layout size expected 32, got %zu", SkSLTypeString(type), uniformData.size()); // The expected offsets of the 4 matrix elements. const int kOffsets[4] = {0, 1, 4, 5}; const float* elements = reinterpret_cast(uniformData.data()); for (size_t i = 0u; i < std::size(kOffsets); ++i) { float expected = kFloats[i]; float el = elements[kOffsets[i]]; REPORTER_ASSERT(r, el == expected, "Incorrect %s element %zu - expected %f, got %f", SkSLTypeString(type), i, expected, el); } mgr.reset(); } // float3x3, half3x3 for (SkSLType type : {SkSLType::kFloat3x3, SkSLType::kHalf3x3}) { const Uniform expectations[] = {{"m", type}}; SkDEBUGCODE(mgr.setExpectedUniforms(SkSpan(expectations), /*isSubstruct=*/false);) mgr.write(expectations[0], kFloats); SkDEBUGCODE(mgr.doneWithExpectedUniforms();) SkSpan uniformData = mgr.finish(); REPORTER_ASSERT(r, uniformData.size() == 48, "%s layout size expected 48, got %zu", SkSLTypeString(type), uniformData.size()); // The expected offsets of the 9 matrix elements. const int kOffsets[9] = {0, 1, 2, 4, 5, 6, 8, 9, 10}; const float* elements = reinterpret_cast(uniformData.data()); for (size_t i = 0u; i < std::size(kOffsets); ++i) { float expected = kFloats[i]; float el = elements[kOffsets[i]]; REPORTER_ASSERT(r, el == expected, "Incorrect %s element %zu - expected %f, got %f", SkSLTypeString(type), i, expected, el); } mgr.reset(); } } // This test validates that the uniform data for matrix types get written out according to the // layout expectations. DEF_GRAPHITE_TEST(UniformManagerStd430MatrixLayoutContents, r, CtsEnforcement::kApiLevel_V) { UniformManager mgr(Layout::kStd430); // float2x2, half2x2 for (SkSLType type : {SkSLType::kFloat2x2, SkSLType::kHalf2x2}) { const Uniform expectations[] = {{"m", type}}; SkDEBUGCODE(mgr.setExpectedUniforms(SkSpan(expectations), /*isSubstruct=*/false);) mgr.write(expectations[0], kFloats); SkDEBUGCODE(mgr.doneWithExpectedUniforms();) SkSpan uniformData = mgr.finish(); REPORTER_ASSERT(r, uniformData.size() == 16, "%s layout size expected 16, got %zu", SkSLTypeString(type), uniformData.size()); // The expected offsets of the 4 matrix elements. This uses a tighter packing than std140 // layout. const int kOffsets[4] = {0, 1, 2, 3}; const float* elements = reinterpret_cast(uniformData.data()); for (size_t i = 0u; i < std::size(kOffsets); ++i) { float expected = kFloats[i]; float el = elements[kOffsets[i]]; REPORTER_ASSERT(r, el == expected, "Incorrect %s element %zu - expected %f, got %f", SkSLTypeString(type), i, expected, el); } mgr.reset(); } // float3x3, half3x3 for (SkSLType type : {SkSLType::kFloat3x3, SkSLType::kHalf3x3}) { const Uniform expectations[] = {{"m", type}}; SkDEBUGCODE(mgr.setExpectedUniforms(SkSpan(expectations), /*isSubstruct=*/false);) mgr.write(expectations[0], kFloats); SkDEBUGCODE(mgr.doneWithExpectedUniforms();) SkSpan uniformData = mgr.finish(); REPORTER_ASSERT(r, uniformData.size() == 48, "%s layout size expected 48, got %zu", SkSLTypeString(type), uniformData.size()); // The expected offsets of the 9 matrix elements. This is the same as std140 layout. const int kOffsets[9] = {0, 1, 2, 4, 5, 6, 8, 9, 10}; const float* elements = reinterpret_cast(uniformData.data()); for (size_t i = 0u; i < std::size(kOffsets); ++i) { float expected = kFloats[i]; float el = elements[kOffsets[i]]; REPORTER_ASSERT(r, el == expected, "Incorrect %s element %zu - expected %f, got %f", SkSLTypeString(type), i, expected, el); } mgr.reset(); } } // These tests validate that substructs are written and aligned appropriately. DEF_GRAPHITE_TEST(UniformManagerStructLayout, r, CtsEnforcement::kNextRelease) { static constexpr uint32_t _ = 0; // 0s will only be written as padding static const struct TestCase { // For convenience, these should only have kFloat[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. std::vector fPreStruct; // before beginStruct(), top-level fields or struct std::vector fSubstruct; // within beginStruct()/endStruct() std::vector fPostStruct; // after endStruct(), top-level fields std::pair> fExpectedAlignmentAndData[std::size(kLayouts)]; // If non-empty, holds base alignments for fPreStruct base alignment as a struct std::vector fPreStructAlignments = {}; } kCases[] = { // Struct tests with no preceeding or following top-level fields {/*prestruct=*/ {}, /*substruct=*/ {{"u1", SkSLType::kFloat}, {"u2", SkSLType::kFloat}, {"u3", SkSLType::kFloat}, {"u4", SkSLType::kFloat}}, /*poststruct=*/{}, /*expect=*/{ /*std140=*/{/*baseAlign=*/16, /*data=*/{1, 2, 3, 4}}, /*std430=*/{/*baseAlign=*/4, /*data=*/{1, 2, 3, 4}}, /*metal=*/ {/*baseAlign=*/4, /*data=*/{1, 2, 3, 4}} }}, {/*prestruct=*/ {}, /*substruct=*/ {{"u1", SkSLType::kFloat3}, {"u2", SkSLType::kFloat}}, /*poststruct=*/{}, /*expect=*/{ /*std140=*/{/*baseAlign=*/16, /*data=*/{1,2,3, 4}}, /*std430=*/{/*baseAlign=*/16, /*data=*/{1,2,3, 4}}, /*metal=*/ {/*baseAlign=*/16, /*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,2, 3, 4, 5,_,_,_}}, /*std430=*/{/*baseAlign=*/8, /*data=*/{1,2, 3, 4, 5,_}}, /*metal=*/ {/*baseAlign=*/8, /*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,_,_,_, 2,3,4,5, 6,7,_,_, 8,9,10,_}}, /*std430=*/{/*baseAlign=*/16, /*data=*/{1,_,_,_, 2,3,4,5, 6,7,_,_, 8,9,10,_}}, /*metal=*/ {/*baseAlign=*/16, /*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,_,_,_, 2, 3, 4, 5}}, /*std430=*/{/*baseAlign=*/4, /*data=*/{1, 2, 3, 4, 5}}, /*metal=*/ {/*baseAlign=*/4, /*data=*/{1, 2, 3, 4, 5}} }}, {/*prestruct=*/ {{"p1", SkSLType::kFloat}}, /*substruct=*/ {{"u1", SkSLType::kFloat3}, {"u2", SkSLType::kFloat}}, /*poststruct=*/{}, /*expect=*/{ /*std140=*/{/*baseAlign=*/16, /*data=*/{1,_,_,_, 2,3,4, 5}}, /*std430=*/{/*baseAlign=*/16, /*data=*/{1,_,_,_, 2,3,4, 5}}, /*metal=*/ {/*baseAlign=*/16, /*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,_,_,_, 2,3, 4, 5, 6,_,_,_}}, /*std430=*/{/*baseAlign=*/8, /*data=*/{1,_, 2,3, 4, 5, 6,_}}, /*metal=*/ {/*baseAlign=*/8, /*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,_,_,_, 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,_}}, /*metal=*/ {/*baseAlign=*/16, /*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,_,_,_, 2, 3, 4, 5, 6,7,8,9}}, /*std430=*/{/*baseAlign=*/4, /*data=*/{1, 2, 3, 4, 5, _,_,_,6,7,8,9}}, /*metal=*/ {/*baseAlign=*/4, /*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,_,_,_, 2,3,4, 5, 6,7,8,9}}, /*std430=*/{/*baseAlign=*/16, /*data=*/{1,_,_,_, 2,3,4, 5, 6,7,8,9}}, /*metal=*/ {/*baseAlign=*/16, /*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,_,_,_, 2,3, 4, 5, 6,_,_,_, 7,8,9,10}}, /*std430=*/{/*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}} }}, {/*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,_,_,_, 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}}, /*metal=*/ {/*baseAlign=*/16, /*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,2, 3,_, 4, 5,_,_}}, /*std430=*/{/*baseAlign=*/4, /*data=*/{1,2, 3,_, 4, 5}}, /*metal=*/ {/*baseAlign=*/4, /*data=*/{1,2, 3,_, 4, 5}} }, /*preStructAlignments=*/{ /*std140=*/16, /*std430=*/8, /*metal=*/ 8 }} }; auto writeFields = [](UniformManager* mgr, SkSpan fields, uint32_t baseValue) { for (const Uniform& f : fields) { switch(f.type()) { case SkSLType::kFloat: mgr->write(SkBits2Float(baseValue++)); break; case SkSLType::kFloat2: mgr->write(SkV2{SkBits2Float(baseValue++), SkBits2Float(baseValue++)}); break; case SkSLType::kFloat3: mgr->write(SkV3{SkBits2Float(baseValue++), SkBits2Float(baseValue++), SkBits2Float(baseValue++)}); break; case SkSLType::kFloat4: mgr->write(SkV4{SkBits2Float(baseValue++), SkBits2Float(baseValue++), SkBits2Float(baseValue++), SkBits2Float(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 data = mgr.finish(); bool sizeMatch = data.size() == sizeof(uint32_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(), 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 % 4 == 0 ? " " : ",", expectedData[i]); } SkDebugf("\nActual contents:\n"); SkASSERT(data.size() % 4 == 0); const uint32_t* actualData = reinterpret_cast(data.begin()); for (size_t i = 0; i < expectedData.size(); ++i) { SkDebugf("%s%u", i % 4 == 0 ? " " : ",", actualData[i]); } SkDebugf("\n\n"); dataMatchFailureLogged = true; } } } }