// // Copyright 2017 The ANGLE Project Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // // ShaderStorageBufferTest: // Various tests related for shader storage buffers. // #include "test_utils/ANGLETest.h" #include "test_utils/gl_raii.h" using namespace angle; namespace { struct MatrixCase { MatrixCase(unsigned cols, unsigned rows, unsigned matrixStride, const char *computeShaderSource, const float *inputData) : mColumns(cols), mRows(rows), mMatrixStride(matrixStride), mComputeShaderSource(computeShaderSource), mInputdata(inputData) {} unsigned int mColumns; unsigned int mRows; unsigned int mMatrixStride; const char *mComputeShaderSource; const float *mInputdata; const unsigned int kBytesPerComponent = sizeof(float); }; struct VectorCase { VectorCase(unsigned components, const char *computeShaderSource, const GLuint *inputData, const GLuint *expectedData) : mComponents(components), mComputeShaderSource(computeShaderSource), mInputdata(inputData), mExpectedData(expectedData) {} unsigned int mComponents; const char *mComputeShaderSource; const GLuint *mInputdata; const GLuint *mExpectedData; const unsigned int kBytesPerComponent = sizeof(GLuint); }; class ShaderStorageBufferTest31 : public ANGLETest { protected: ShaderStorageBufferTest31() { setWindowWidth(128); setWindowHeight(128); setConfigRedBits(8); setConfigGreenBits(8); setConfigBlueBits(8); setConfigAlphaBits(8); // Test flakiness was noticed when reusing displays. forceNewDisplay(); } void runMatrixTest(const MatrixCase &matrixCase) { ANGLE_GL_COMPUTE_PROGRAM(program, matrixCase.mComputeShaderSource); glUseProgram(program); // Create shader storage buffer GLBuffer shaderStorageBuffer[2]; glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[0]); glBufferData(GL_SHADER_STORAGE_BUFFER, matrixCase.mRows * matrixCase.mMatrixStride, matrixCase.mInputdata, GL_STATIC_DRAW); glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[1]); glBufferData(GL_SHADER_STORAGE_BUFFER, matrixCase.mRows * matrixCase.mMatrixStride, nullptr, GL_STATIC_DRAW); // Bind shader storage buffer glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, shaderStorageBuffer[0]); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, shaderStorageBuffer[1]); glDispatchCompute(1, 1, 1); glFinish(); // Read back shader storage buffer glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[1]); const GLfloat *ptr = reinterpret_cast( glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, matrixCase.mRows * matrixCase.mMatrixStride, GL_MAP_READ_BIT)); for (unsigned int row = 0; row < matrixCase.mRows; row++) { for (unsigned int col = 0; col < matrixCase.mColumns; col++) { GLfloat expected = matrixCase.mInputdata[row * (matrixCase.mMatrixStride / matrixCase.kBytesPerComponent) + col]; GLfloat actual = *(ptr + row * (matrixCase.mMatrixStride / matrixCase.kBytesPerComponent) + col); EXPECT_EQ(expected, actual) << " at row " << row << " and column " << col; } } EXPECT_GL_NO_ERROR(); } void runVectorTest(const VectorCase &vectorCase) { ANGLE_GL_COMPUTE_PROGRAM(program, vectorCase.mComputeShaderSource); glUseProgram(program); // Create shader storage buffer GLBuffer shaderStorageBuffer[2]; glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[0]); glBufferData(GL_SHADER_STORAGE_BUFFER, vectorCase.mComponents * vectorCase.kBytesPerComponent, vectorCase.mInputdata, GL_STATIC_DRAW); glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[1]); glBufferData(GL_SHADER_STORAGE_BUFFER, vectorCase.mComponents * vectorCase.kBytesPerComponent, nullptr, GL_STATIC_DRAW); // Bind shader storage buffer glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, shaderStorageBuffer[0]); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, shaderStorageBuffer[1]); glDispatchCompute(1, 1, 1); glFinish(); // Read back shader storage buffer glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[1]); const GLuint *ptr = reinterpret_cast(glMapBufferRange( GL_SHADER_STORAGE_BUFFER, 0, vectorCase.mComponents * vectorCase.kBytesPerComponent, GL_MAP_READ_BIT)); for (unsigned int idx = 0; idx < vectorCase.mComponents; idx++) { EXPECT_EQ(vectorCase.mExpectedData[idx], *(ptr + idx)); } EXPECT_GL_NO_ERROR(); } }; // Matched block names within a shader interface must match in terms of having the same number of // declarations with the same sequence of types. TEST_P(ShaderStorageBufferTest31, MatchedBlockNameWithDifferentMemberType) { constexpr char kVS[] = "#version 310 es\n" "buffer blockName {\n" " float data;\n" "};\n" "void main()\n" "{\n" "}\n"; constexpr char kFS[] = "#version 310 es\n" "buffer blockName {\n" " uint data;\n" "};\n" "void main()\n" "{\n" "}\n"; GLuint program = CompileProgram(kVS, kFS); EXPECT_EQ(0u, program); } // Linking should fail if blocks in vertex shader exceed GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS. TEST_P(ShaderStorageBufferTest31, ExceedMaxVertexShaderStorageBlocks) { std::ostringstream instanceCount; GLint maxVertexShaderStorageBlocks = 0; glGetIntegerv(GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS, &maxVertexShaderStorageBlocks); EXPECT_GL_NO_ERROR(); instanceCount << maxVertexShaderStorageBlocks; const std::string &vertexShaderSource = "#version 310 es\n" "layout(shared) buffer blockName {\n" " uint data;\n" "} instance[" + instanceCount.str() + " + 1];\n" "void main()\n" "{\n" "}\n"; constexpr char kFS[] = "#version 310 es\n" "void main()\n" "{\n" "}\n"; GLuint program = CompileProgram(vertexShaderSource.c_str(), kFS); EXPECT_EQ(0u, program); } // Linking should fail if the sum of the number of active shader storage blocks exceeds // MAX_COMBINED_SHADER_STORAGE_BLOCKS. TEST_P(ShaderStorageBufferTest31, ExceedMaxCombinedShaderStorageBlocks) { std::ostringstream vertexInstanceCount; GLint maxVertexShaderStorageBlocks = 0; glGetIntegerv(GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS, &maxVertexShaderStorageBlocks); vertexInstanceCount << maxVertexShaderStorageBlocks; GLint maxFragmentShaderStorageBlocks = 0; glGetIntegerv(GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS, &maxFragmentShaderStorageBlocks); GLint maxCombinedShaderStorageBlocks = 0; glGetIntegerv(GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS, &maxCombinedShaderStorageBlocks); EXPECT_GL_NO_ERROR(); ASSERT_GE(maxCombinedShaderStorageBlocks, maxVertexShaderStorageBlocks); ASSERT_GE(maxCombinedShaderStorageBlocks, maxFragmentShaderStorageBlocks); // As SPEC allows MAX_VERTEX_SHADER_STORAGE_BLOCKS and MAX_FRAGMENT_SHADER_STORAGE_BLOCKS to be // 0, in this situation we should skip this test to prevent these unexpected compile errors. ANGLE_SKIP_TEST_IF(maxVertexShaderStorageBlocks == 0 || maxFragmentShaderStorageBlocks == 0); GLint fragmentShaderStorageBlocks = maxCombinedShaderStorageBlocks - maxVertexShaderStorageBlocks + 1; ANGLE_SKIP_TEST_IF(fragmentShaderStorageBlocks > maxFragmentShaderStorageBlocks); std::ostringstream fragmentInstanceCount; fragmentInstanceCount << fragmentShaderStorageBlocks; const std::string &vertexShaderSource = "#version 310 es\n" "layout(shared) buffer blockName0 {\n" " uint data;\n" "} instance0[" + vertexInstanceCount.str() + "];\n" "void main()\n" "{\n" "}\n"; const std::string &fragmentShaderSource = "#version 310 es\n" "layout(shared) buffer blockName1 {\n" " uint data;\n" "} instance1[" + fragmentInstanceCount.str() + "];\n" "void main()\n" "{\n" "}\n"; GLuint program = CompileProgram(vertexShaderSource.c_str(), fragmentShaderSource.c_str()); EXPECT_EQ(0u, program); } // Test shader storage buffer read write. TEST_P(ShaderStorageBufferTest31, ShaderStorageBufferReadWrite) { constexpr char kCS[] = "#version 310 es\n" "layout(local_size_x=1, local_size_y=1, local_size_z=1) in;\n" "layout(std140, binding = 1) buffer blockName {\n" " uint data[2];\n" "} instanceName;\n" "void main()\n" "{\n" " instanceName.data[0] = 3u;\n" " instanceName.data[1] = 4u;\n" "}\n"; ANGLE_GL_COMPUTE_PROGRAM(program, kCS); glUseProgram(program.get()); constexpr unsigned int kElementCount = 2; // The array stride are rounded up to the base alignment of a vec4 for std140 layout. constexpr unsigned int kArrayStride = 16; // Create shader storage buffer GLBuffer shaderStorageBuffer; glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer); glBufferData(GL_SHADER_STORAGE_BUFFER, kElementCount * kArrayStride, nullptr, GL_STATIC_DRAW); // Bind shader storage buffer glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, shaderStorageBuffer); // Dispath compute glDispatchCompute(1, 1, 1); glFinish(); // Read back shader storage buffer constexpr unsigned int kExpectedValues[2] = {3u, 4u}; glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer); void *ptr = glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, kElementCount * kArrayStride, GL_MAP_READ_BIT); for (unsigned int idx = 0; idx < kElementCount; idx++) { EXPECT_EQ(kExpectedValues[idx], *(reinterpret_cast(reinterpret_cast(ptr) + idx * kArrayStride))); } glUnmapBuffer(GL_SHADER_STORAGE_BUFFER); glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); EXPECT_GL_NO_ERROR(); } // Tests modifying an existing shader storage buffer TEST_P(ShaderStorageBufferTest31, ShaderStorageBufferReadWriteSame) { constexpr char kComputeShaderSource[] = R"(#version 310 es layout(local_size_x=1, local_size_y=1, local_size_z=1) in; layout(std140, binding = 0) buffer block { uint data; } instance; void main() { uint temp = instance.data; instance.data = temp + 1u; } )"; ANGLE_GL_COMPUTE_PROGRAM(program, kComputeShaderSource); glUseProgram(program); constexpr unsigned int kBytesPerComponent = sizeof(GLuint); constexpr unsigned int kInitialData = 123u; // Create shader storage buffer GLBuffer shaderStorageBuffer; glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer); glBufferData(GL_SHADER_STORAGE_BUFFER, kBytesPerComponent, &kInitialData, GL_STATIC_DRAW); // Bind shader storage buffer glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, shaderStorageBuffer); glDispatchCompute(1, 1, 1); glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT | GL_SHADER_STORAGE_BARRIER_BIT); glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer); const void *bufferData = glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, kBytesPerComponent, GL_MAP_READ_BIT); constexpr unsigned int kExpectedData = 124u; EXPECT_EQ(kExpectedData, *static_cast(bufferData)); glUnmapBuffer(GL_SHADER_STORAGE_BUFFER); // Running shader twice to make sure that the buffer gets updated correctly 123->124->125 glDispatchCompute(1, 1, 1); glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT | GL_PIXEL_BUFFER_BARRIER_BIT); bufferData = glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, kBytesPerComponent, GL_MAP_READ_BIT); constexpr unsigned int kExpectedData2 = 125u; EXPECT_EQ(kExpectedData2, *static_cast(bufferData)); // Verify re-using the SSBO buffer with a PBO contains expected data. // This will read-back from FBO using a PBO into the same SSBO buffer. glUnmapBuffer(GL_SHADER_STORAGE_BUFFER); GLTexture texture; glBindTexture(GL_TEXTURE_2D, texture); glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, getWindowWidth(), getWindowHeight()); GLFramebuffer framebuffer; glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0); glClearColor(1.0f, 0.0f, 0.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); glBindBuffer(GL_PIXEL_PACK_BUFFER, shaderStorageBuffer); glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, 0); EXPECT_GL_NO_ERROR(); void *mappedPtr = glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, kBytesPerComponent, GL_MAP_READ_BIT); GLColor *dataColor = static_cast(mappedPtr); EXPECT_GL_NO_ERROR(); EXPECT_EQ(GLColor::red, dataColor[0]); glUnmapBuffer(GL_PIXEL_PACK_BUFFER); EXPECT_GL_NO_ERROR(); // Verify that binding the buffer back to the SSBO keeps the expected data. glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer); const GLColor *ptr = reinterpret_cast( glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, kBytesPerComponent, GL_MAP_READ_BIT)); EXPECT_EQ(GLColor::red, *ptr); EXPECT_GL_NO_ERROR(); } // Tests reading and writing to a shader storage buffer bound at an offset. TEST_P(ShaderStorageBufferTest31, ShaderStorageBufferReadWriteOffset) { // http://anglebug.com/4092 ANGLE_SKIP_TEST_IF(isSwiftshader()); constexpr char kCS[] = R"(#version 310 es layout(local_size_x=1, local_size_y=1, local_size_z=1) in; layout(std140, binding = 0) buffer block0 { uint data[2]; } instance0; void main() { instance0.data[0] = 3u; instance0.data[1] = 4u; } )"; ANGLE_GL_COMPUTE_PROGRAM(program, kCS); glUseProgram(program); constexpr unsigned int kElementCount = 2; // The array stride are rounded up to the base alignment of a vec4 for std140 layout. constexpr unsigned int kArrayStride = 16; // Create shader storage buffer GLBuffer shaderStorageBuffer; glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer); int bufferAlignOffset; glGetIntegerv(GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT, &bufferAlignOffset); constexpr int kBufferSize = kElementCount * kArrayStride; const int unalignedBytes = kBufferSize % bufferAlignOffset; const int alignCorrection = unalignedBytes == 0 ? 0 : bufferAlignOffset - unalignedBytes; const int kBufferOffset = kBufferSize + alignCorrection; glBufferData(GL_SHADER_STORAGE_BUFFER, kBufferOffset + kBufferSize, nullptr, GL_STATIC_DRAW); // Bind shader storage buffer at an offset glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 0, shaderStorageBuffer, kBufferOffset, kBufferSize); EXPECT_GL_NO_ERROR(); glDispatchCompute(1, 1, 1); glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT | GL_SHADER_STORAGE_BARRIER_BIT); glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer); // Bind the buffer at a separate location glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 0, shaderStorageBuffer, 0, kBufferSize); EXPECT_GL_NO_ERROR(); glDispatchCompute(1, 1, 1); glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT); // Read back shader storage buffer constexpr unsigned int kExpectedValues[2] = {3u, 4u}; glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer); void *ptr = glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, kBufferSize, GL_MAP_READ_BIT); for (unsigned int idx = 0; idx < kElementCount; idx++) { EXPECT_EQ(kExpectedValues[idx], *(reinterpret_cast(reinterpret_cast(ptr) + idx * kArrayStride))); } glUnmapBuffer(GL_SHADER_STORAGE_BUFFER); ptr = glMapBufferRange(GL_SHADER_STORAGE_BUFFER, kBufferOffset, kBufferSize, GL_MAP_READ_BIT); for (unsigned int idx = 0; idx < kElementCount; idx++) { EXPECT_EQ(kExpectedValues[idx], *(reinterpret_cast(reinterpret_cast(ptr) + idx * kArrayStride))); } glUnmapBuffer(GL_SHADER_STORAGE_BUFFER); EXPECT_GL_NO_ERROR(); } // Test that access/write to vector data in shader storage buffer. TEST_P(ShaderStorageBufferTest31, ShaderStorageBufferVector) { constexpr char kComputeShaderSource[] = R"(#version 310 es layout(local_size_x=1, local_size_y=1, local_size_z=1) in; layout(std140, binding = 0) buffer blockIn { uvec2 data; } instanceIn; layout(std140, binding = 1) buffer blockOut { uvec2 data; } instanceOut; void main() { instanceOut.data[0] = instanceIn.data[0]; instanceOut.data[1] = instanceIn.data[1]; } )"; constexpr unsigned int kComponentCount = 2; constexpr GLuint kInputValues[kComponentCount] = {3u, 4u}; VectorCase vectorCase(kComponentCount, kComputeShaderSource, kInputValues, kInputValues); runVectorTest(vectorCase); } // Test that the shader works well with an active SSBO but not statically used. TEST_P(ShaderStorageBufferTest31, ActiveSSBOButNotStaticallyUsed) { // http://anglebug.com/3725 ANGLE_SKIP_TEST_IF(IsAndroid() && IsPixel2() && IsVulkan()); constexpr char kComputeShaderSource[] = R"(#version 310 es layout(local_size_x=1, local_size_y=1, local_size_z=1) in; layout(std140, binding = 0) buffer blockIn { uvec2 data; } instanceIn; layout(std140, binding = 1) buffer blockOut { uvec2 data; } instanceOut; layout(std140, binding = 2) buffer blockC { uvec2 data; } instanceC; void main() { instanceOut.data[0] = instanceIn.data[0]; instanceOut.data[1] = instanceIn.data[1]; } )"; GLBuffer shaderStorageBufferC; glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBufferC); glBufferData(GL_SHADER_STORAGE_BUFFER, 32, nullptr, GL_STATIC_DRAW); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, shaderStorageBufferC); constexpr unsigned int kComponentCount = 2; constexpr GLuint kInputValues[kComponentCount] = {3u, 4u}; VectorCase vectorCase(kComponentCount, kComputeShaderSource, kInputValues, kInputValues); runVectorTest(vectorCase); } // Test that access/write to swizzle scalar data in shader storage block. TEST_P(ShaderStorageBufferTest31, ScalarSwizzleTest) { constexpr char kComputeShaderSource[] = R"(#version 310 es layout(local_size_x=1, local_size_y=1, local_size_z=1) in; layout(std140, binding = 0) buffer blockIn { uvec2 data; } instanceIn; layout(std140, binding = 1) buffer blockOut { uvec2 data; } instanceOut; void main() { instanceOut.data.x = instanceIn.data.y; instanceOut.data.y = instanceIn.data.x; } )"; constexpr unsigned int kComponentCount = 2; constexpr GLuint kInputValues[kComponentCount] = {3u, 4u}; constexpr GLuint kExpectedValues[kComponentCount] = {4u, 3u}; VectorCase vectorCase(kComponentCount, kComputeShaderSource, kInputValues, kExpectedValues); runVectorTest(vectorCase); } // Test that access/write to swizzle vector data in shader storage block. TEST_P(ShaderStorageBufferTest31, VectorSwizzleTest) { constexpr char kComputeShaderSource[] = R"(#version 310 es layout(local_size_x=1, local_size_y=1, local_size_z=1) in; layout(std140, binding = 0) buffer blockIn { uvec2 data; } instanceIn; layout(std140, binding = 1) buffer blockOut { uvec2 data; } instanceOut; void main() { instanceOut.data.yx = instanceIn.data.xy; } )"; constexpr unsigned int kComponentCount = 2; constexpr GLuint kInputValues[kComponentCount] = {3u, 4u}; constexpr GLuint kExpectedValues[kComponentCount] = {4u, 3u}; VectorCase vectorCase(kComponentCount, kComputeShaderSource, kInputValues, kExpectedValues); runVectorTest(vectorCase); } // Test that access/write to swizzle vector data in column_major matrix in shader storage block. TEST_P(ShaderStorageBufferTest31, VectorSwizzleInColumnMajorMatrixTest) { constexpr char kComputeShaderSource[] = R"(#version 310 es layout(local_size_x=1, local_size_y=1, local_size_z=1) in; layout(std140, binding = 0) buffer blockIn { layout(column_major) mat2x3 data; } instanceIn; layout(std140, binding = 1) buffer blockOut { layout(column_major) mat2x3 data; } instanceOut; void main() { instanceOut.data[0].xyz = instanceIn.data[0].xyz; instanceOut.data[1].xyz = instanceIn.data[1].xyz; } )"; constexpr unsigned int kColumns = 2; constexpr unsigned int kRows = 3; constexpr unsigned int kBytesPerComponent = sizeof(float); constexpr unsigned int kMatrixStride = 16; constexpr float kInputDada[kColumns * (kMatrixStride / kBytesPerComponent)] = { 0.1, 0.2, 0.3, 0.0, 0.4, 0.5, 0.6, 0.0}; MatrixCase matrixCase(kRows, kColumns, kMatrixStride, kComputeShaderSource, kInputDada); runMatrixTest(matrixCase); } // Test that access/write to swizzle vector data in row_major matrix in shader storage block. TEST_P(ShaderStorageBufferTest31, VectorSwizzleInRowMajorMatrixTest) { ANGLE_SKIP_TEST_IF(IsAndroid()); constexpr char kComputeShaderSource[] = R"(#version 310 es layout(local_size_x=1, local_size_y=1, local_size_z=1) in; layout(std140, binding = 0) buffer blockIn { layout(row_major) mat2x3 data; } instanceIn; layout(std140, binding = 1) buffer blockOut { layout(row_major) mat2x3 data; } instanceOut; void main() { instanceOut.data[0].xyz = instanceIn.data[0].xyz; instanceOut.data[1].xyz = instanceIn.data[1].xyz; } )"; constexpr unsigned int kColumns = 2; constexpr unsigned int kRows = 3; constexpr unsigned int kBytesPerComponent = sizeof(float); // std140 layout requires that base alignment and stride of arrays of scalars and vectors are // rounded up a multiple of the base alignment of a vec4. constexpr unsigned int kMatrixStride = 16; constexpr float kInputDada[kRows * (kMatrixStride / kBytesPerComponent)] = { 0.1, 0.2, 0.0, 0.0, 0.3, 0.4, 0.0, 0.0, 0.5, 0.6, 0.0, 0.0}; MatrixCase matrixCase(kColumns, kRows, kMatrixStride, kComputeShaderSource, kInputDada); runMatrixTest(matrixCase); } // Test that access/write to scalar data in matrix in shader storage block with row major. TEST_P(ShaderStorageBufferTest31, ScalarDataInMatrixInSSBOWithRowMajorQualifier) { // TODO(jiajia.qin@intel.com): Figure out why it fails on Intel Linux platform. // http://anglebug.com/1951 ANGLE_SKIP_TEST_IF(IsIntel() && IsLinux()); ANGLE_SKIP_TEST_IF(IsAndroid()); constexpr char kComputeShaderSource[] = R"(#version 310 es layout(local_size_x=1, local_size_y=1, local_size_z=1) in; layout(std140, binding = 0) buffer blockIn { layout(row_major) mat2x3 data; } instanceIn; layout(std140, binding = 1) buffer blockOut { layout(row_major) mat2x3 data; } instanceOut; void main() { instanceOut.data[0][0] = instanceIn.data[0][0]; instanceOut.data[0][1] = instanceIn.data[0][1]; instanceOut.data[0][2] = instanceIn.data[0][2]; instanceOut.data[1][0] = instanceIn.data[1][0]; instanceOut.data[1][1] = instanceIn.data[1][1]; instanceOut.data[1][2] = instanceIn.data[1][2]; } )"; constexpr unsigned int kColumns = 2; constexpr unsigned int kRows = 3; constexpr unsigned int kBytesPerComponent = sizeof(float); // std140 layout requires that base alignment and stride of arrays of scalars and vectors are // rounded up a multiple of the base alignment of a vec4. constexpr unsigned int kMatrixStride = 16; constexpr float kInputDada[kRows * (kMatrixStride / kBytesPerComponent)] = { 0.1, 0.2, 0.0, 0.0, 0.3, 0.4, 0.0, 0.0, 0.5, 0.6, 0.0, 0.0}; MatrixCase matrixCase(kColumns, kRows, kMatrixStride, kComputeShaderSource, kInputDada); runMatrixTest(matrixCase); } TEST_P(ShaderStorageBufferTest31, VectorDataInMatrixInSSBOWithRowMajorQualifier) { ANGLE_SKIP_TEST_IF(IsAndroid()); constexpr char kComputeShaderSource[] = R"(#version 310 es layout(local_size_x=1, local_size_y=1, local_size_z=1) in; layout(std140, binding = 0) buffer blockIn { layout(row_major) mat2x3 data; } instanceIn; layout(std140, binding = 1) buffer blockOut { layout(row_major) mat2x3 data; } instanceOut; void main() { instanceOut.data[0] = instanceIn.data[0]; instanceOut.data[1] = instanceIn.data[1]; } )"; constexpr unsigned int kColumns = 2; constexpr unsigned int kRows = 3; constexpr unsigned int kBytesPerComponent = sizeof(float); // std140 layout requires that base alignment and stride of arrays of scalars and vectors are // rounded up a multiple of the base alignment of a vec4. constexpr unsigned int kMatrixStride = 16; constexpr float kInputDada[kRows * (kMatrixStride / kBytesPerComponent)] = { 0.1, 0.2, 0.0, 0.0, 0.3, 0.4, 0.0, 0.0, 0.5, 0.6, 0.0, 0.0}; MatrixCase matrixCase(kColumns, kRows, kMatrixStride, kComputeShaderSource, kInputDada); runMatrixTest(matrixCase); } TEST_P(ShaderStorageBufferTest31, MatrixDataInSSBOWithRowMajorQualifier) { ANGLE_SKIP_TEST_IF(IsAMD() && IsWindows() && IsOpenGL()); constexpr char kComputeShaderSource[] = R"(#version 310 es layout(local_size_x=1, local_size_y=1, local_size_z=1) in; layout(std140, binding = 0) buffer blockIn { layout(row_major) mat2x3 data; } instanceIn; layout(std140, binding = 1) buffer blockOut { layout(row_major) mat2x3 data; } instanceOut; void main() { instanceOut.data = instanceIn.data; } )"; constexpr unsigned int kColumns = 2; constexpr unsigned int kRows = 3; constexpr unsigned int kBytesPerComponent = sizeof(float); // std140 layout requires that base alignment and stride of arrays of scalars and vectors are // rounded up a multiple of the base alignment of a vec4. constexpr unsigned int kMatrixStride = 16; constexpr float kInputDada[kRows * (kMatrixStride / kBytesPerComponent)] = { 0.1, 0.2, 0.0, 0.0, 0.3, 0.4, 0.0, 0.0, 0.5, 0.6, 0.0, 0.0}; MatrixCase matrixCase(kColumns, kRows, kMatrixStride, kComputeShaderSource, kInputDada); runMatrixTest(matrixCase); } // Test that access/write to scalar data in structure matrix in shader storage block with row major. TEST_P(ShaderStorageBufferTest31, ScalarDataInMatrixInStructureInSSBOWithRowMajorQualifier) { // TODO(jiajia.qin@intel.com): Figure out why it fails on Intel Linux platform. // http://anglebug.com/1951 ANGLE_SKIP_TEST_IF(IsIntel() && IsLinux()); ANGLE_SKIP_TEST_IF(IsAndroid()); constexpr char kComputeShaderSource[] = R"(#version 310 es layout(local_size_x=1, local_size_y=1, local_size_z=1) in; struct S { mat2x3 data; }; layout(std140, binding = 0) buffer blockIn { layout(row_major) S s; } instanceIn; layout(std140, binding = 1) buffer blockOut { layout(row_major) S s; } instanceOut; void main() { instanceOut.s.data[0][0] = instanceIn.s.data[0][0]; instanceOut.s.data[0][1] = instanceIn.s.data[0][1]; instanceOut.s.data[0][2] = instanceIn.s.data[0][2]; instanceOut.s.data[1][0] = instanceIn.s.data[1][0]; instanceOut.s.data[1][1] = instanceIn.s.data[1][1]; instanceOut.s.data[1][2] = instanceIn.s.data[1][2]; } )"; constexpr unsigned int kColumns = 2; constexpr unsigned int kRows = 3; constexpr unsigned int kBytesPerComponent = sizeof(float); // std140 layout requires that base alignment and stride of arrays of scalars and vectors are // rounded up a multiple of the base alignment of a vec4. constexpr unsigned int kMatrixStride = 16; constexpr float kInputDada[kRows * (kMatrixStride / kBytesPerComponent)] = { 0.1, 0.2, 0.0, 0.0, 0.3, 0.4, 0.0, 0.0, 0.5, 0.6, 0.0, 0.0}; MatrixCase matrixCase(kColumns, kRows, kMatrixStride, kComputeShaderSource, kInputDada); runMatrixTest(matrixCase); } // Test that access/write to column major matrix data in shader storage buffer. TEST_P(ShaderStorageBufferTest31, ScalarDataInMatrixInSSBO) { constexpr char kComputeShaderSource[] = R"(#version 310 es layout(local_size_x=1, local_size_y=1, local_size_z=1) in; layout(std140, binding = 0) buffer blockIn { mat2x3 data; } instanceIn; layout(std140, binding = 1) buffer blockOut { mat2x3 data; } instanceOut; void main() { instanceOut.data[0][0] = instanceIn.data[0][0]; instanceOut.data[0][1] = instanceIn.data[0][1]; instanceOut.data[0][2] = instanceIn.data[0][2]; instanceOut.data[1][0] = instanceIn.data[1][0]; instanceOut.data[1][1] = instanceIn.data[1][1]; instanceOut.data[1][2] = instanceIn.data[1][2]; } )"; constexpr unsigned int kColumns = 2; constexpr unsigned int kRows = 3; constexpr unsigned int kBytesPerComponent = sizeof(float); constexpr unsigned int kMatrixStride = 16; constexpr float kInputDada[kColumns * (kMatrixStride / kBytesPerComponent)] = { 0.1, 0.2, 0.3, 0.0, 0.4, 0.5, 0.6, 0.0}; MatrixCase matrixCase(kRows, kColumns, kMatrixStride, kComputeShaderSource, kInputDada); runMatrixTest(matrixCase); } TEST_P(ShaderStorageBufferTest31, VectorDataInMatrixInSSBOWithColumnMajorQualifier) { constexpr char kComputeShaderSource[] = R"(#version 310 es layout(local_size_x=1, local_size_y=1, local_size_z=1) in; layout(std140, binding = 0) buffer blockIn { layout(column_major) mat2x3 data; } instanceIn; layout(std140, binding = 1) buffer blockOut { layout(column_major) mat2x3 data; } instanceOut; void main() { instanceOut.data[0] = instanceIn.data[0]; instanceOut.data[1] = instanceIn.data[1]; } )"; constexpr unsigned int kColumns = 2; constexpr unsigned int kRows = 3; constexpr unsigned int kBytesPerComponent = sizeof(float); constexpr unsigned int kMatrixStride = 16; constexpr float kInputDada[kColumns * (kMatrixStride / kBytesPerComponent)] = { 0.1, 0.2, 0.3, 0.0, 0.4, 0.5, 0.6, 0.0}; MatrixCase matrixCase(kRows, kColumns, kMatrixStride, kComputeShaderSource, kInputDada); runMatrixTest(matrixCase); } TEST_P(ShaderStorageBufferTest31, MatrixDataInSSBOWithColumnMajorQualifier) { constexpr char kComputeShaderSource[] = R"(#version 310 es layout(local_size_x=1, local_size_y=1, local_size_z=1) in; layout(std140, binding = 0) buffer blockIn { layout(column_major) mat2x3 data; } instanceIn; layout(std140, binding = 1) buffer blockOut { layout(column_major) mat2x3 data; } instanceOut; void main() { instanceOut.data = instanceIn.data; } )"; constexpr unsigned int kColumns = 2; constexpr unsigned int kRows = 3; constexpr unsigned int kBytesPerComponent = sizeof(float); constexpr unsigned int kMatrixStride = 16; constexpr float kInputDada[kColumns * (kMatrixStride / kBytesPerComponent)] = { 0.1, 0.2, 0.3, 0.0, 0.4, 0.5, 0.6, 0.0}; MatrixCase matrixCase(kRows, kColumns, kMatrixStride, kComputeShaderSource, kInputDada); runMatrixTest(matrixCase); } // Test that access/write to structure data in shader storage buffer. TEST_P(ShaderStorageBufferTest31, ShaderStorageBufferStructureArray) { constexpr char kComputeShaderSource[] = R"(#version 310 es layout(local_size_x=1, local_size_y=1, local_size_z=1) in; struct S { uvec2 uvData; uint uiData[2]; }; layout(std140, binding = 0) buffer blockIn { S s[2]; uint lastData; } instanceIn; layout(std140, binding = 1) buffer blockOut { S s[2]; uint lastData; } instanceOut; void main() { instanceOut.s[0].uvData = instanceIn.s[0].uvData; instanceOut.s[0].uiData[0] = instanceIn.s[0].uiData[0]; instanceOut.s[0].uiData[1] = instanceIn.s[0].uiData[1]; instanceOut.s[1].uvData = instanceIn.s[1].uvData; instanceOut.s[1].uiData[0] = instanceIn.s[1].uiData[0]; instanceOut.s[1].uiData[1] = instanceIn.s[1].uiData[1]; instanceOut.lastData = instanceIn.lastData; } )"; ANGLE_GL_COMPUTE_PROGRAM(program, kComputeShaderSource); glUseProgram(program); std::array kUVData = {{ 1u, 2u, 0u, 0u, }}; std::array kUIData = {{ 3u, 0u, 0u, 0u, 4u, 0u, 0u, 0u, }}; GLuint kLastData = 5u; constexpr unsigned int kBytesPerComponent = sizeof(GLuint); constexpr unsigned int kStructureStride = 48; constexpr unsigned int totalSize = kStructureStride * 2 + sizeof(kLastData); // Create shader storage buffer GLBuffer shaderStorageBuffer[2]; glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[0]); glBufferData(GL_SHADER_STORAGE_BUFFER, totalSize, nullptr, GL_STATIC_DRAW); GLint offset = 0; // upload data to instanceIn.s[0] glBufferSubData(GL_SHADER_STORAGE_BUFFER, offset, kUVData.size() * kBytesPerComponent, kUVData.data()); offset += (kUVData.size() * kBytesPerComponent); glBufferSubData(GL_SHADER_STORAGE_BUFFER, offset, kUIData.size() * kBytesPerComponent, kUIData.data()); offset += (kUIData.size() * kBytesPerComponent); // upload data to instanceIn.s[1] glBufferSubData(GL_SHADER_STORAGE_BUFFER, offset, kUVData.size() * kBytesPerComponent, kUVData.data()); offset += (kUVData.size() * kBytesPerComponent); glBufferSubData(GL_SHADER_STORAGE_BUFFER, offset, kUIData.size() * kBytesPerComponent, kUIData.data()); offset += (kUIData.size() * kBytesPerComponent); // upload data to instanceIn.lastData glBufferSubData(GL_SHADER_STORAGE_BUFFER, offset, sizeof(kLastData), &kLastData); glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[1]); glBufferData(GL_SHADER_STORAGE_BUFFER, totalSize, nullptr, GL_STATIC_DRAW); // Bind shader storage buffer glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, shaderStorageBuffer[0]); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, shaderStorageBuffer[1]); glDispatchCompute(1, 1, 1); glFinish(); // Read back shader storage buffer glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[1]); constexpr float kExpectedValues[5] = {1u, 2u, 3u, 4u, 5u}; const GLuint *ptr = reinterpret_cast( glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, totalSize, GL_MAP_READ_BIT)); // instanceOut.s[0] EXPECT_EQ(kExpectedValues[0], *ptr); EXPECT_EQ(kExpectedValues[1], *(ptr + 1)); EXPECT_EQ(kExpectedValues[2], *(ptr + 4)); EXPECT_EQ(kExpectedValues[3], *(ptr + 8)); // instanceOut.s[1] ptr += kStructureStride / kBytesPerComponent; EXPECT_EQ(kExpectedValues[0], *ptr); EXPECT_EQ(kExpectedValues[1], *(ptr + 1)); EXPECT_EQ(kExpectedValues[2], *(ptr + 4)); EXPECT_EQ(kExpectedValues[3], *(ptr + 8)); // instanceOut.lastData ptr += kStructureStride / kBytesPerComponent; EXPECT_EQ(kExpectedValues[4], *(ptr)); EXPECT_GL_NO_ERROR(); } // Test that access/write to array of array structure data in shader storage buffer. TEST_P(ShaderStorageBufferTest31, ShaderStorageBufferStructureArrayOfArray) { constexpr char kComputeShaderSource[] = R"(#version 310 es layout(local_size_x=1, local_size_y=1, local_size_z=1) in; struct S { uvec2 uvData; uint uiData[2]; }; layout(std140, binding = 0) buffer blockIn { S s[3][2]; uint lastData; } instanceIn; layout(std140, binding = 1) buffer blockOut { S s[3][2]; uint lastData; } instanceOut; void main() { instanceOut.s[1][0].uvData = instanceIn.s[1][0].uvData; instanceOut.s[1][0].uiData[0] = instanceIn.s[1][0].uiData[0]; instanceOut.s[1][0].uiData[1] = instanceIn.s[1][0].uiData[1]; instanceOut.s[1][1].uvData = instanceIn.s[1][1].uvData; instanceOut.s[1][1].uiData[0] = instanceIn.s[1][1].uiData[0]; instanceOut.s[1][1].uiData[1] = instanceIn.s[1][1].uiData[1]; instanceOut.lastData = instanceIn.lastData; } )"; ANGLE_GL_COMPUTE_PROGRAM(program, kComputeShaderSource); glUseProgram(program); std::array kUVData = {{ 1u, 2u, 0u, 0u, }}; std::array kUIData = {{ 3u, 0u, 0u, 0u, 4u, 0u, 0u, 0u, }}; GLuint kLastData = 5u; constexpr unsigned int kBytesPerComponent = sizeof(GLuint); constexpr unsigned int kStructureStride = 48; constexpr unsigned int kStructureArrayDimension0 = 3; constexpr unsigned int kStructureArrayDimension1 = 2; constexpr unsigned int kLastDataOffset = kStructureStride * kStructureArrayDimension0 * kStructureArrayDimension1; constexpr unsigned int totalSize = kLastDataOffset + sizeof(kLastData); GLBuffer shaderStorageBuffer[2]; glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[0]); glBufferData(GL_SHADER_STORAGE_BUFFER, totalSize, nullptr, GL_STATIC_DRAW); // offset of instanceIn.s[1][0] GLint offset = kStructureStride * (kStructureArrayDimension1 * 1 + 0); GLuint uintOffset = offset / kBytesPerComponent; // upload data to instanceIn.s[1][0] glBufferSubData(GL_SHADER_STORAGE_BUFFER, offset, kUVData.size() * kBytesPerComponent, kUVData.data()); offset += (kUVData.size() * kBytesPerComponent); glBufferSubData(GL_SHADER_STORAGE_BUFFER, offset, kUIData.size() * kBytesPerComponent, kUIData.data()); offset += (kUIData.size() * kBytesPerComponent); // upload data to instanceIn.s[1][1] glBufferSubData(GL_SHADER_STORAGE_BUFFER, offset, kUVData.size() * kBytesPerComponent, kUVData.data()); offset += (kUVData.size() * kBytesPerComponent); glBufferSubData(GL_SHADER_STORAGE_BUFFER, offset, kUIData.size() * kBytesPerComponent, kUIData.data()); // upload data to instanceIn.lastData glBufferSubData(GL_SHADER_STORAGE_BUFFER, kLastDataOffset, sizeof(kLastData), &kLastData); glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[1]); glBufferData(GL_SHADER_STORAGE_BUFFER, totalSize, nullptr, GL_STATIC_DRAW); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, shaderStorageBuffer[0]); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, shaderStorageBuffer[1]); glDispatchCompute(1, 1, 1); glFinish(); // Read back shader storage buffer glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[1]); constexpr float kExpectedValues[5] = {1u, 2u, 3u, 4u, 5u}; const GLuint *ptr = reinterpret_cast( glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, totalSize, GL_MAP_READ_BIT)); // instanceOut.s[0][0] EXPECT_EQ(kExpectedValues[0], *(ptr + uintOffset)); EXPECT_EQ(kExpectedValues[1], *(ptr + uintOffset + 1)); EXPECT_EQ(kExpectedValues[2], *(ptr + uintOffset + 4)); EXPECT_EQ(kExpectedValues[3], *(ptr + uintOffset + 8)); // instanceOut.s[0][1] EXPECT_EQ(kExpectedValues[0], *(ptr + uintOffset + 12)); EXPECT_EQ(kExpectedValues[1], *(ptr + uintOffset + 13)); EXPECT_EQ(kExpectedValues[2], *(ptr + uintOffset + 16)); EXPECT_EQ(kExpectedValues[3], *(ptr + uintOffset + 20)); // instanceOut.lastData EXPECT_EQ(kExpectedValues[4], *(ptr + (kLastDataOffset / kBytesPerComponent))); EXPECT_GL_NO_ERROR(); } // Test that access/write to vector data in std430 shader storage block. TEST_P(ShaderStorageBufferTest31, VectorArrayInSSBOWithStd430Qualifier) { constexpr char kComputeShaderSource[] = R"(#version 310 es layout(local_size_x=1, local_size_y=1, local_size_z=1) in; layout(std430, binding = 0) buffer blockIn { uvec2 data[2]; } instanceIn; layout(std430, binding = 1) buffer blockOut { uvec2 data[2]; } instanceOut; void main() { instanceOut.data[0] = instanceIn.data[0]; instanceOut.data[1] = instanceIn.data[1]; } )"; ANGLE_GL_COMPUTE_PROGRAM(program, kComputeShaderSource); glUseProgram(program); constexpr unsigned int kElementCount = 2; constexpr unsigned int kBytesPerComponent = sizeof(unsigned int); constexpr unsigned int kArrayStride = 8; constexpr unsigned int kComponentCount = kArrayStride / kBytesPerComponent; constexpr unsigned int kExpectedValues[kElementCount][kComponentCount] = {{1u, 2u}, {3u, 4u}}; // Create shader storage buffer GLBuffer shaderStorageBuffer[2]; glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[0]); glBufferData(GL_SHADER_STORAGE_BUFFER, kElementCount * kArrayStride, kExpectedValues, GL_STATIC_DRAW); glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[1]); glBufferData(GL_SHADER_STORAGE_BUFFER, kElementCount * kArrayStride, nullptr, GL_STATIC_DRAW); // Bind shader storage buffer glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, shaderStorageBuffer[0]); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, shaderStorageBuffer[1]); glDispatchCompute(1, 1, 1); glFinish(); // Read back shader storage buffer glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[1]); const GLuint *ptr = reinterpret_cast(glMapBufferRange( GL_SHADER_STORAGE_BUFFER, 0, kElementCount * kArrayStride, GL_MAP_READ_BIT)); for (unsigned int idx = 0; idx < kElementCount; idx++) { for (unsigned int idy = 0; idy < kComponentCount; idy++) { EXPECT_EQ(kExpectedValues[idx][idy], *(ptr + idx * kComponentCount + idy)); } } EXPECT_GL_NO_ERROR(); } // Test that access/write to matrix data in std430 shader storage block. TEST_P(ShaderStorageBufferTest31, MatrixInSSBOWithStd430Qualifier) { constexpr char kComputeShaderSource[] = R"(#version 310 es layout(local_size_x=1, local_size_y=1, local_size_z=1) in; layout(std430, binding = 0) buffer blockIn { mat2 data; } instanceIn; layout(std430, binding = 1) buffer blockOut { mat2 data; } instanceOut; void main() { instanceOut.data = instanceIn.data; } )"; ANGLE_GL_COMPUTE_PROGRAM(program, kComputeShaderSource); glUseProgram(program); constexpr unsigned int kColumns = 2; constexpr unsigned int kRows = 2; constexpr unsigned int kBytesPerComponent = sizeof(float); constexpr unsigned int kMatrixStride = kRows * kBytesPerComponent; constexpr float kInputDada[kColumns * kRows] = {0.1, 0.2, 0.4, 0.5}; MatrixCase matrixCase(kRows, kColumns, kMatrixStride, kComputeShaderSource, kInputDada); runMatrixTest(matrixCase); } // Test that access/write to structure data in std430 shader storage block. TEST_P(ShaderStorageBufferTest31, StructureInSSBOWithStd430Qualifier) { constexpr char kComputeShaderSource[] = R"(#version 310 es layout(local_size_x=1, local_size_y=1, local_size_z=1) in; struct S { uvec2 u; }; layout(std430, binding = 0) buffer blockIn { uint i1; S s; uint i2; } instanceIn; layout(std430, binding = 1) buffer blockOut { uint i1; S s; uint i2; } instanceOut; void main() { instanceOut.i1 = instanceIn.i1; instanceOut.s.u = instanceIn.s.u; instanceOut.i2 = instanceIn.i2; } )"; ANGLE_GL_COMPUTE_PROGRAM(program, kComputeShaderSource); glUseProgram(program); GLuint kI1Data = 1u; std::array kUData = {{ 2u, 3u, }}; GLuint kI2Data = 4u; constexpr unsigned int kBytesPerComponent = sizeof(GLuint); constexpr unsigned int kStructureStartOffset = 8; constexpr unsigned int kStructureSize = 8; constexpr unsigned int kTotalSize = kStructureStartOffset + kStructureSize + kBytesPerComponent; // Create shader storage buffer GLBuffer shaderStorageBuffer[2]; glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[0]); glBufferData(GL_SHADER_STORAGE_BUFFER, kTotalSize, nullptr, GL_STATIC_DRAW); // upload data to instanceIn.i1 glBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, kBytesPerComponent, &kI1Data); // upload data to instanceIn.s.u glBufferSubData(GL_SHADER_STORAGE_BUFFER, kStructureStartOffset, kStructureSize, kUData.data()); // upload data to instanceIn.i2 glBufferSubData(GL_SHADER_STORAGE_BUFFER, kStructureStartOffset + kStructureSize, kBytesPerComponent, &kI2Data); glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[1]); glBufferData(GL_SHADER_STORAGE_BUFFER, kTotalSize, nullptr, GL_STATIC_DRAW); // Bind shader storage buffer glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, shaderStorageBuffer[0]); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, shaderStorageBuffer[1]); glDispatchCompute(1, 1, 1); glFinish(); // Read back shader storage buffer glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[1]); GLuint kExpectedValues[4] = {1u, 2u, 3u, 4u}; const GLuint *ptr = reinterpret_cast( glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, kTotalSize, GL_MAP_READ_BIT)); EXPECT_EQ(kExpectedValues[0], *ptr); ptr += (kStructureStartOffset / kBytesPerComponent); EXPECT_EQ(kExpectedValues[1], *ptr); EXPECT_EQ(kExpectedValues[2], *(ptr + 1)); ptr += (kStructureSize / kBytesPerComponent); EXPECT_EQ(kExpectedValues[3], *ptr); EXPECT_GL_NO_ERROR(); } // Test that access/write to structure of structure data in std430 shader storage block. TEST_P(ShaderStorageBufferTest31, StructureOfStructureInSSBOWithStd430Qualifier) { constexpr char kComputeShaderSource[] = R"(#version 310 es layout(local_size_x=1, local_size_y=1, local_size_z=1) in; struct S2 { uvec3 u2; }; struct S1 { uvec2 u1; S2 s2; }; layout(std430, binding = 0) buffer blockIn { uint i1; S1 s1; uint i2; } instanceIn; layout(std430, binding = 1) buffer blockOut { uint i1; S1 s1; uint i2; } instanceOut; void main() { instanceOut.i1 = instanceIn.i1; instanceOut.s1.u1 = instanceIn.s1.u1; instanceOut.s1.s2.u2 = instanceIn.s1.s2.u2; instanceOut.i2 = instanceIn.i2; } )"; ANGLE_GL_COMPUTE_PROGRAM(program, kComputeShaderSource); glUseProgram(program); constexpr unsigned int kBytesPerComponent = sizeof(GLuint); constexpr unsigned int kStructureS1StartOffset = 16; constexpr unsigned int kStructureS2StartOffset = 32; constexpr unsigned int kStructureS1Size = 32; constexpr unsigned int kTotalSize = kStructureS1StartOffset + kStructureS1Size + kBytesPerComponent; GLuint kI1Data = 1u; std::array kU1Data = {{2u, 3u}}; std::array kU2Data = {{4u, 5u, 6u}}; GLuint kI2Data = 7u; // Create shader storage buffer GLBuffer shaderStorageBuffer[2]; glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[0]); glBufferData(GL_SHADER_STORAGE_BUFFER, kTotalSize, nullptr, GL_STATIC_DRAW); // upload data to instanceIn.i1 glBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, kBytesPerComponent, &kI1Data); // upload data to instanceIn.s1.u1 glBufferSubData(GL_SHADER_STORAGE_BUFFER, kStructureS1StartOffset, kU1Data.size() * kBytesPerComponent, kU1Data.data()); // upload data to instanceIn.s1.s2.u2 glBufferSubData(GL_SHADER_STORAGE_BUFFER, kStructureS2StartOffset, kU2Data.size() * kBytesPerComponent, kU2Data.data()); // upload data to instanceIn.i2 glBufferSubData(GL_SHADER_STORAGE_BUFFER, kStructureS1StartOffset + kStructureS1Size, kBytesPerComponent, &kI2Data); glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[1]); glBufferData(GL_SHADER_STORAGE_BUFFER, kTotalSize, nullptr, GL_STATIC_DRAW); // Bind shader storage buffer glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, shaderStorageBuffer[0]); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, shaderStorageBuffer[1]); glDispatchCompute(1, 1, 1); glFinish(); // Read back shader storage buffer GLuint kExpectedValues[7] = {1u, 2u, 3u, 4u, 5u, 6u, 7u}; glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[1]); const GLuint *ptr = reinterpret_cast( glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, kTotalSize, GL_MAP_READ_BIT)); EXPECT_EQ(kExpectedValues[0], *ptr); ptr += (kStructureS1StartOffset / kBytesPerComponent); EXPECT_EQ(kExpectedValues[1], *ptr); EXPECT_EQ(kExpectedValues[2], *(ptr + 1)); ptr += ((kStructureS2StartOffset - kStructureS1StartOffset) / kBytesPerComponent); EXPECT_EQ(kExpectedValues[3], *ptr); EXPECT_EQ(kExpectedValues[4], *(ptr + 1)); EXPECT_EQ(kExpectedValues[5], *(ptr + 2)); ptr += ((kStructureS1Size - kStructureS2StartOffset) / kBytesPerComponent); EXPECT_EQ(kExpectedValues[6], *(ptr + 4)); EXPECT_GL_NO_ERROR(); } // Test atomic memory functions. TEST_P(ShaderStorageBufferTest31, AtomicMemoryFunctions) { constexpr char kCS[] = R"(#version 310 es layout(local_size_x=1, local_size_y=1, local_size_z=1) in; layout(std140, binding = 1) buffer blockName { uint data[2]; } instanceName; void main() { instanceName.data[0] = 0u; instanceName.data[1] = 0u; atomicAdd(instanceName.data[0], 5u); atomicMax(instanceName.data[1], 7u); })"; ANGLE_GL_COMPUTE_PROGRAM(program, kCS); glUseProgram(program.get()); constexpr unsigned int kElementCount = 2; // The array stride are rounded up to the base alignment of a vec4 for std140 layout. constexpr unsigned int kArrayStride = 16; // Create shader storage buffer GLBuffer shaderStorageBuffer; glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer); glBufferData(GL_SHADER_STORAGE_BUFFER, kElementCount * kArrayStride, nullptr, GL_STATIC_DRAW); // Bind shader storage buffer glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, shaderStorageBuffer); // Dispath compute glDispatchCompute(1, 1, 1); glFinish(); // Read back shader storage buffer constexpr unsigned int kExpectedValues[2] = {5u, 7u}; glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer); void *ptr = glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, kElementCount * kArrayStride, GL_MAP_READ_BIT); for (unsigned int idx = 0; idx < kElementCount; idx++) { EXPECT_EQ(kExpectedValues[idx], *(reinterpret_cast(reinterpret_cast(ptr) + idx * kArrayStride))); } glUnmapBuffer(GL_SHADER_STORAGE_BUFFER); glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); EXPECT_GL_NO_ERROR(); } // Test multiple storage buffers work correctly when program switching. In angle, storage buffer // bindings are updated accord to current program. If switch program, need to update storage buffer // bindings again. TEST_P(ShaderStorageBufferTest31, MultiStorageBuffersForMultiPrograms) { constexpr char kCS1[] = R"(#version 310 es layout(local_size_x=3, local_size_y=1, local_size_z=1) in; layout(binding = 1) buffer Output { uint result1[]; } sb_out1; void main() { highp uint offset = gl_LocalInvocationID.x; sb_out1.result1[gl_LocalInvocationIndex] = gl_LocalInvocationIndex + 1u; })"; constexpr char kCS2[] = R"(#version 310 es layout(local_size_x=3, local_size_y=1, local_size_z=1) in; layout(binding = 2) buffer Output { uint result2[]; } sb_out2; void main() { highp uint offset = gl_LocalInvocationID.x; sb_out2.result2[gl_LocalInvocationIndex] = gl_LocalInvocationIndex + 2u; })"; constexpr unsigned int numInvocations = 3; int arrayStride1 = 0, arrayStride2 = 0; GLenum props[] = {GL_ARRAY_STRIDE}; GLBuffer shaderStorageBuffer1, shaderStorageBuffer2; ANGLE_GL_COMPUTE_PROGRAM(program1, kCS1); ANGLE_GL_COMPUTE_PROGRAM(program2, kCS2); EXPECT_GL_NO_ERROR(); unsigned int outVarIndex1 = glGetProgramResourceIndex(program1.get(), GL_BUFFER_VARIABLE, "Output.result1"); glGetProgramResourceiv(program1.get(), GL_BUFFER_VARIABLE, outVarIndex1, 1, props, 1, 0, &arrayStride1); glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer1); glBufferData(GL_SHADER_STORAGE_BUFFER, numInvocations * arrayStride1, nullptr, GL_STREAM_READ); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, shaderStorageBuffer1); EXPECT_GL_NO_ERROR(); unsigned int outVarIndex2 = glGetProgramResourceIndex(program2.get(), GL_BUFFER_VARIABLE, "Output.result2"); glGetProgramResourceiv(program2.get(), GL_BUFFER_VARIABLE, outVarIndex2, 1, props, 1, 0, &arrayStride2); glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer2); glBufferData(GL_SHADER_STORAGE_BUFFER, numInvocations * arrayStride2, nullptr, GL_STREAM_READ); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, shaderStorageBuffer2); EXPECT_GL_NO_ERROR(); glUseProgram(program1.get()); glDispatchCompute(1, 1, 1); EXPECT_GL_NO_ERROR(); glUseProgram(program2.get()); glDispatchCompute(1, 1, 1); EXPECT_GL_NO_ERROR(); glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer1); const void *ptr1 = glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, 3 * arrayStride1, GL_MAP_READ_BIT); for (unsigned int idx = 0; idx < numInvocations; idx++) { EXPECT_EQ(idx + 1, *((const GLuint *)((const GLbyte *)ptr1 + idx * arrayStride1))); } glUnmapBuffer(GL_SHADER_STORAGE_BUFFER); EXPECT_GL_NO_ERROR(); glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer2); const void *ptr2 = glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, 3 * arrayStride2, GL_MAP_READ_BIT); EXPECT_GL_NO_ERROR(); for (unsigned int idx = 0; idx < numInvocations; idx++) { EXPECT_EQ(idx + 2, *((const GLuint *)((const GLbyte *)ptr2 + idx * arrayStride2))); } glUnmapBuffer(GL_SHADER_STORAGE_BUFFER); EXPECT_GL_NO_ERROR(); glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); EXPECT_GL_NO_ERROR(); } // Test that function calling is supported in SSBO access chain. TEST_P(ShaderStorageBufferTest31, FunctionCallInSSBOAccessChain) { constexpr char kComputeShaderSource[] = R"(#version 310 es layout (local_size_x=4) in; highp uint getIndex (in highp uvec2 localID, uint element) { return localID.x + element; } layout(binding=0, std430) buffer Storage { highp uint values[]; } sb_store; void main() { sb_store.values[getIndex(gl_LocalInvocationID.xy, 0u)] = gl_LocalInvocationIndex; } )"; ANGLE_GL_COMPUTE_PROGRAM(program, kComputeShaderSource); EXPECT_GL_NO_ERROR(); } // Test that unary operator is supported in SSBO access chain. TEST_P(ShaderStorageBufferTest31, UnaryOperatorInSSBOAccessChain) { constexpr char kComputeShaderSource[] = R"(#version 310 es layout (local_size_x=4) in; layout(binding=0, std430) buffer Storage { highp uint values[]; } sb_store; void main() { uint invocationNdx = gl_LocalInvocationIndex; sb_store.values[++invocationNdx] = invocationNdx; } )"; ANGLE_GL_COMPUTE_PROGRAM(program, kComputeShaderSource); EXPECT_GL_NO_ERROR(); } // Test that ternary operator is supported in SSBO access chain. TEST_P(ShaderStorageBufferTest31, TernaryOperatorInSSBOAccessChain) { constexpr char kComputeShaderSource[] = R"(#version 310 es layout (local_size_x=4) in; layout(binding=0, std430) buffer Storage { highp uint values[]; } sb_store; void main() { sb_store.values[gl_LocalInvocationIndex > 2u ? gl_NumWorkGroups.x : gl_NumWorkGroups.y] = gl_LocalInvocationIndex; } )"; ANGLE_GL_COMPUTE_PROGRAM(program, kComputeShaderSource); EXPECT_GL_NO_ERROR(); } // Tests that alignment is correct for bools inside a SSB and that the values // are written correctly by a trivial shader. Currently tests only the alignment // of the initial block. TEST_P(ShaderStorageBufferTest31, LoadAndStoreBooleanValue) { // TODO(jiajia.qin@intel.com): Figure out why it fails on Intel Linux platform. // http://anglebug.com/1951 ANGLE_SKIP_TEST_IF(IsIntel() && IsLinux()); constexpr char kComputeShaderSource[] = R"(#version 310 es layout (local_size_x=1) in; layout(binding=0, std140) buffer Storage0 { bool b1; bool b2; bool b3; } sb_load; layout(binding=1, std140) buffer Storage1 { bool b1; bool b2; bool b3; } sb_store; void main() { sb_store.b1 = sb_load.b1; sb_store.b2 = sb_load.b2; sb_store.b3 = sb_load.b3; } )"; ANGLE_GL_COMPUTE_PROGRAM(program, kComputeShaderSource); EXPECT_GL_NO_ERROR(); glUseProgram(program); constexpr GLuint kB1Value = 1u; constexpr GLuint kB2Value[2] = {0u, 1u}; constexpr unsigned int kBytesPerComponent = sizeof(GLuint); // Create shader storage buffer GLBuffer shaderStorageBuffer[2]; glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[0]); glBufferData(GL_SHADER_STORAGE_BUFFER, 3 * kBytesPerComponent, nullptr, GL_STATIC_DRAW); GLint offset = 0; // upload data to sb_load.b1 glBufferSubData(GL_SHADER_STORAGE_BUFFER, offset, kBytesPerComponent, &kB1Value); offset += kBytesPerComponent; // upload data to sb_load.b2 glBufferSubData(GL_SHADER_STORAGE_BUFFER, offset, 2 * kBytesPerComponent, kB2Value); constexpr GLuint kStoreBufferContents[3] = {0x1BCD1234, 0x2BCD1234, 0x3BCD1234}; glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[1]); glBufferData(GL_SHADER_STORAGE_BUFFER, 3 * kBytesPerComponent, kStoreBufferContents, GL_STATIC_DRAW); // Bind shader storage buffer glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, shaderStorageBuffer[0]); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, shaderStorageBuffer[1]); glDispatchCompute(1, 1, 1); glFinish(); // Read back shader storage buffer glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[1]); const GLuint *ptr = reinterpret_cast( glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, 3 * kBytesPerComponent, GL_MAP_READ_BIT)); EXPECT_EQ(kB1Value, ptr[0]); EXPECT_EQ(kB2Value[0], ptr[1]); EXPECT_EQ(kB2Value[1], ptr[2]); EXPECT_GL_NO_ERROR(); } // Tests that alignment is correct for bvecs3 inside a SSB and that the // values are written correctly by a trivial shader. Currently tests only the // alignment of the initial block. TEST_P(ShaderStorageBufferTest31, LoadAndStoreBooleanVec3) { // TODO(jiajia.qin@intel.com): Figure out why it fails on Intel Linux platform. // http://anglebug.com/1951 ANGLE_SKIP_TEST_IF(IsIntel() && IsLinux()); ANGLE_SKIP_TEST_IF(IsAMD() && IsWindows() && IsOpenGL()); constexpr char kComputeShaderSource[] = R"(#version 310 es layout (local_size_x=1) in; layout(binding=0, std140) buffer Storage0 { bvec3 b; } sb_load; layout(binding=1, std140) buffer Storage1 { bvec3 b; } sb_store; void main() { sb_store.b = sb_load.b; } )"; ANGLE_GL_COMPUTE_PROGRAM(program, kComputeShaderSource); EXPECT_GL_NO_ERROR(); glUseProgram(program); constexpr GLuint kBValues[3] = {1u, 0u, 1u}; constexpr unsigned int kBytesPerComponent = sizeof(GLuint); // Create shader storage buffer GLBuffer shaderStorageBuffer[2]; glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[0]); glBufferData(GL_SHADER_STORAGE_BUFFER, 3 * kBytesPerComponent, nullptr, GL_STATIC_DRAW); glBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, 3 * kBytesPerComponent, &kBValues); constexpr GLuint kStoreBufferContents[3] = {0x1BCD1234, 0x2BCD1234, 0x3BCD1234}; glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[1]); glBufferData(GL_SHADER_STORAGE_BUFFER, 3 * kBytesPerComponent, kStoreBufferContents, GL_STATIC_DRAW); // Bind shader storage buffer glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, shaderStorageBuffer[0]); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, shaderStorageBuffer[1]); glDispatchCompute(1, 1, 1); glFinish(); // Read back shader storage buffer glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[1]); const GLuint *ptr = reinterpret_cast( glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, 3 * kBytesPerComponent, GL_MAP_READ_BIT)); EXPECT_EQ(kBValues[0], ptr[0]); EXPECT_EQ(kBValues[1], ptr[1]); EXPECT_EQ(kBValues[2], ptr[2]); EXPECT_GL_NO_ERROR(); } // Tests that alignment is correct for bool + bvecs2 inside a SSB and that the // values are written correctly by a trivial shader. Currently tests only the // alignment of the initial block. Compare to LoadAndStoreBooleanVec3 to see how // the alignment rules affect the memory layout. TEST_P(ShaderStorageBufferTest31, LoadAndStoreBooleanVarAndVec2) { // TODO(jiajia.qin@intel.com): Figure out why it fails on Intel Linux platform. // http://anglebug.com/1951 ANGLE_SKIP_TEST_IF(IsIntel() && IsLinux()); ANGLE_SKIP_TEST_IF(IsAMD() && IsWindows() && IsOpenGL()); constexpr char kComputeShaderSource[] = R"(#version 310 es layout (local_size_x=1) in; layout(binding=0, std140) buffer Storage0 { bool b1; bvec2 b2; } sb_load; layout(binding=1, std140) buffer Storage1 { bool b1; bvec2 b2; } sb_store; void main() { sb_store.b1 = sb_load.b1; sb_store.b2 = sb_load.b2; } )"; // https://www.khronos.org/registry/OpenGL/specs/es/3.1/es_spec_3.1.pdf // 7.6.2.2 Standard Uniform Block Layout // ... A structure and each structure member have a base offset and a base // alignment, from which an aligned offset is computed by rounding the base // offset up to a multiple of the base alignment. The base offset of the // first member of a structure is taken from the aligned offset of the // structure itself. ... The members of a toplevel uniform block are laid // out in buffer storage by treating the uniform block as a structure with a // base offset of zero. // 1. If the member is a scalar consuming N basic machine units, the base // alignment is N. // 2. If the member is a two- or four-component vector with components // consuming N basic machine units, the base alignment is 2N or 4N, // respectively // b1 N == 4, basic offset 0, alignment 4, is at 0..3 // b2 N == 4, basic offset 4, alignment 2*4 = 8, is at 8..16. ANGLE_GL_COMPUTE_PROGRAM(program, kComputeShaderSource); EXPECT_GL_NO_ERROR(); glUseProgram(program); constexpr GLuint kAlignPadding = 0x1abcd789u; constexpr GLuint kBValues[] = {1u, kAlignPadding, 0u, 1u}; constexpr unsigned int kSsbSize = sizeof(kBValues); // Create shader storage buffer GLBuffer shaderStorageBuffer[2]; glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[0]); glBufferData(GL_SHADER_STORAGE_BUFFER, kSsbSize, nullptr, GL_STATIC_DRAW); glBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, kSsbSize, &kBValues); constexpr GLuint kStoreBufferContents[4] = {0x1BCD1234, 0x2BCD1234, 0x3BCD1234, 0x3BCD1277}; glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[1]); glBufferData(GL_SHADER_STORAGE_BUFFER, kSsbSize, kStoreBufferContents, GL_STATIC_DRAW); // Bind shader storage buffer glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, shaderStorageBuffer[0]); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, shaderStorageBuffer[1]); glDispatchCompute(1, 1, 1); glFinish(); // Read back shader storage buffer glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[1]); const GLuint *ptr = reinterpret_cast( glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, kSsbSize, GL_MAP_READ_BIT)); EXPECT_EQ(kBValues[0], ptr[0]); // Index 1 is padding. EXPECT_EQ(kBValues[2], ptr[2]); EXPECT_EQ(kBValues[3], ptr[3]); EXPECT_GL_NO_ERROR(); } // Test that non-structure array of arrays is supported in SSBO. TEST_P(ShaderStorageBufferTest31, SimpleArrayOfArrays) { constexpr char kComputeShaderSource[] = R"(#version 310 es layout (local_size_x=1) in; layout(binding=0, std140) buffer Storage0 { uint a[2][2][2]; uint b; } sb_load; layout(binding=1, std140) buffer Storage1 { uint a[2][2][2]; uint b; } sb_store; void main() { sb_store.a[0][0][0] = sb_load.a[0][0][0]; sb_store.a[0][0][1] = sb_load.a[0][0][1]; sb_store.a[0][1][0] = sb_load.a[0][1][0]; sb_store.a[0][1][1] = sb_load.a[0][1][1]; sb_store.a[1][0][0] = sb_load.a[1][0][0]; sb_store.a[1][0][1] = sb_load.a[1][0][1]; sb_store.a[1][1][0] = sb_load.a[1][1][0]; sb_store.a[1][1][1] = sb_load.a[1][1][1]; sb_store.b = sb_load.b; } )"; ANGLE_GL_COMPUTE_PROGRAM(program, kComputeShaderSource); glUseProgram(program); constexpr unsigned int kBytesPerComponent = sizeof(GLuint); // The array stride are rounded up to the base alignment of a vec4 for std140 layout. constexpr unsigned int kArrayStride = 16; constexpr unsigned int kDimension0 = 2; constexpr unsigned int kDimension1 = 2; constexpr unsigned int kDimension2 = 2; constexpr unsigned int kAElementCount = kDimension0 * kDimension1 * kDimension2; constexpr unsigned int kAComponentCountPerDimension = kArrayStride / kBytesPerComponent; constexpr unsigned int kTotalSize = kArrayStride * kAElementCount + kBytesPerComponent; constexpr GLuint kInputADatas[kAElementCount * kAComponentCountPerDimension] = { 1u, 0u, 0u, 0u, 2u, 0u, 0u, 0u, 3u, 0u, 0u, 0u, 4u, 0u, 0u, 0u, 5u, 0u, 0u, 0u, 6u, 0u, 0u, 0u, 7u, 0u, 0u, 0u, 8u, 0u, 0u, 0u}; constexpr GLuint kInputBData = 9u; // Create shader storage buffer GLBuffer shaderStorageBuffer[2]; glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[0]); glBufferData(GL_SHADER_STORAGE_BUFFER, kTotalSize, nullptr, GL_STATIC_DRAW); GLint offset = 0; // upload data to sb_load.a glBufferSubData(GL_SHADER_STORAGE_BUFFER, offset, kAElementCount * kArrayStride, kInputADatas); offset += (kAElementCount * kArrayStride); // upload data to sb_load.b glBufferSubData(GL_SHADER_STORAGE_BUFFER, offset, kBytesPerComponent, &kInputBData); glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[1]); glBufferData(GL_SHADER_STORAGE_BUFFER, kTotalSize, nullptr, GL_STATIC_DRAW); // Bind shader storage buffer glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, shaderStorageBuffer[0]); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, shaderStorageBuffer[1]); glDispatchCompute(1, 1, 1); glFinish(); // Read back shader storage buffer glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[1]); constexpr GLuint kExpectedADatas[kAElementCount] = {1u, 2u, 3u, 4u, 5u, 6u, 7u, 8u}; const GLuint *ptr = reinterpret_cast( glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, kTotalSize, GL_MAP_READ_BIT)); for (unsigned i = 0u; i < kDimension0; i++) { for (unsigned j = 0u; j < kDimension1; j++) { for (unsigned k = 0u; k < kDimension2; k++) { unsigned index = i * (kDimension1 * kDimension2) + j * kDimension2 + k; EXPECT_EQ(kExpectedADatas[index], *(ptr + index * (kArrayStride / kBytesPerComponent))); } } } ptr += (kAElementCount * (kArrayStride / kBytesPerComponent)); EXPECT_EQ(kInputBData, *ptr); EXPECT_GL_NO_ERROR(); } // Test that the length of unsized array is supported. TEST_P(ShaderStorageBufferTest31, UnsizedArrayLength) { constexpr char kComputeShaderSource[] = R"(#version 310 es layout (local_size_x=1) in; layout(std430, binding = 0) buffer Storage0 { uint buf1[2]; uint buf2[]; } sb_load; layout(std430, binding = 1) buffer Storage1 { int unsizedArrayLength; uint buf1[2]; uint buf2[]; } sb_store; void main() { sb_store.unsizedArrayLength = sb_store.buf2.length(); for (int i = 0; i < sb_load.buf1.length(); i++) { sb_store.buf1[i] = sb_load.buf1[i]; } for (int i = 0; i < sb_load.buf2.length(); i++) { sb_store.buf2[i] = sb_load.buf2[i]; } } )"; ANGLE_GL_COMPUTE_PROGRAM(program, kComputeShaderSource); glUseProgram(program); constexpr unsigned int kBytesPerComponent = sizeof(unsigned int); constexpr unsigned int kLoadBlockElementCount = 5; constexpr unsigned int kStoreBlockElementCount = 6; constexpr unsigned int kInputValues[kLoadBlockElementCount] = {1u, 2u, 3u, 4u, 5u}; constexpr unsigned int kExpectedValues[kStoreBlockElementCount] = {3u, 1u, 2u, 3u, 4u, 5u}; GLBuffer shaderStorageBuffer[2]; glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[0]); glBufferData(GL_SHADER_STORAGE_BUFFER, kLoadBlockElementCount * kBytesPerComponent, &kInputValues, GL_STATIC_DRAW); glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[1]); glBufferData(GL_SHADER_STORAGE_BUFFER, kStoreBlockElementCount * kBytesPerComponent, nullptr, GL_STATIC_DRAW); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, shaderStorageBuffer[0]); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, shaderStorageBuffer[1]); glDispatchCompute(1, 1, 1); glFinish(); glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[1]); const GLuint *ptr = reinterpret_cast( glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, kStoreBlockElementCount * kBytesPerComponent, GL_MAP_READ_BIT)); for (unsigned int i = 0; i < kStoreBlockElementCount; i++) { EXPECT_EQ(kExpectedValues[i], *(ptr + i)); } EXPECT_GL_NO_ERROR(); } // Test back to back that the length of unsized array is correct after respecifying the buffer // size to be smaller than the first TEST_P(ShaderStorageBufferTest31, UnsizedArrayLengthRespecifySize) { // http://anglebug.com/4566 ANGLE_SKIP_TEST_IF(IsD3D11() || (IsAndroid() && IsOpenGLES())); constexpr char kComputeShaderSource[] = R"(#version 310 es layout (local_size_x=1) in; layout(std430, binding = 0) buffer Storage0 { uint buf1[2]; uint buf2[]; } sb_load; layout(std430, binding = 1) buffer Storage1 { int unsizedArrayLength; uint buf1[2]; uint buf2[]; } sb_store; void main() { sb_store.unsizedArrayLength = sb_store.buf2.length(); for (int i = 0; i < sb_load.buf1.length(); i++) { sb_store.buf1[i] = sb_load.buf1[i]; } for (int i = 0; i < sb_load.buf2.length(); i++) { sb_store.buf2[i] = sb_load.buf2[i]; } } )"; ANGLE_GL_COMPUTE_PROGRAM(program, kComputeShaderSource); glUseProgram(program); constexpr unsigned int kBytesPerComponent = sizeof(unsigned int); constexpr unsigned int kLoadBlockElementCount = 5; constexpr unsigned int kStoreBlockElementCount = 6; constexpr unsigned int kInputValues[kLoadBlockElementCount] = {1u, 2u, 3u, 4u, 5u}; constexpr unsigned int kExpectedValues[kStoreBlockElementCount] = {3u, 1u, 2u, 3u, 4u, 5u}; GLBuffer shaderStorageBuffer[2]; glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[0]); glBufferData(GL_SHADER_STORAGE_BUFFER, kLoadBlockElementCount * kBytesPerComponent, &kInputValues, GL_STATIC_DRAW); glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[1]); glBufferData(GL_SHADER_STORAGE_BUFFER, kStoreBlockElementCount * kBytesPerComponent, nullptr, GL_STATIC_DRAW); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, shaderStorageBuffer[0]); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, shaderStorageBuffer[1]); glDispatchCompute(1, 1, 1); glFinish(); glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[1]); const GLuint *ptr = reinterpret_cast( glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, kStoreBlockElementCount * kBytesPerComponent, GL_MAP_READ_BIT)); for (unsigned int i = 0; i < kStoreBlockElementCount; i++) { EXPECT_EQ(kExpectedValues[i], *(ptr + i)); } glUnmapBuffer(GL_SHADER_STORAGE_BUFFER); EXPECT_GL_NO_ERROR(); // Respecify these SSBOs to be smaller constexpr unsigned int kSmallerLoadBlockElementCount = 3; constexpr unsigned int kSmallerStoreBlockElementCount = 4; constexpr unsigned int kSmallerInputValues[kSmallerLoadBlockElementCount] = {1u, 2u, 3u}; constexpr unsigned int kSmallerExpectedValues[kSmallerStoreBlockElementCount] = {1u, 1u, 2u, 3u}; glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[0]); glBufferData(GL_SHADER_STORAGE_BUFFER, kSmallerLoadBlockElementCount * kBytesPerComponent, &kSmallerInputValues, GL_STATIC_DRAW); glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[1]); glBufferData(GL_SHADER_STORAGE_BUFFER, kSmallerStoreBlockElementCount * kBytesPerComponent, nullptr, GL_STATIC_DRAW); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, shaderStorageBuffer[0]); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, shaderStorageBuffer[1]); glDispatchCompute(1, 1, 1); glFinish(); glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[1]); const GLuint *ptr2 = reinterpret_cast( glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, kSmallerStoreBlockElementCount * kBytesPerComponent, GL_MAP_READ_BIT)); for (unsigned int i = 0; i < kSmallerStoreBlockElementCount; i++) { EXPECT_EQ(kSmallerExpectedValues[i], *(ptr2 + i)); } EXPECT_GL_NO_ERROR(); } // Test that compond assignment operator for buffer variable is correctly handled. TEST_P(ShaderStorageBufferTest31, CompoundAssignmentOperator) { constexpr char kComputeShaderSource[] = R"(#version 310 es layout (local_size_x=1) in; layout(binding=0, std140) buffer Storage0 { uint b; } sb_load; layout(binding=1, std140) buffer Storage1 { uint b; } sb_store; void main() { uint temp = 2u; temp += sb_load.b; sb_store.b += temp; sb_store.b += sb_load.b; } )"; ANGLE_GL_COMPUTE_PROGRAM(program, kComputeShaderSource); glUseProgram(program); constexpr unsigned int kBytesPerComponent = sizeof(unsigned int); constexpr unsigned int kInputValue = 1u; constexpr unsigned int kExpectedValue = 5u; GLBuffer shaderStorageBuffer[2]; glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[0]); glBufferData(GL_SHADER_STORAGE_BUFFER, kBytesPerComponent, &kInputValue, GL_STATIC_DRAW); glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[1]); glBufferData(GL_SHADER_STORAGE_BUFFER, kBytesPerComponent, &kInputValue, GL_STATIC_DRAW); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, shaderStorageBuffer[0]); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, shaderStorageBuffer[1]); glDispatchCompute(1, 1, 1); glFinish(); glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[1]); const GLuint *ptr = reinterpret_cast( glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, kBytesPerComponent, GL_MAP_READ_BIT)); EXPECT_EQ(kExpectedValue, *ptr); EXPECT_GL_NO_ERROR(); } // Test that readonly binary operator for buffer variable is correctly handled. TEST_P(ShaderStorageBufferTest31, ReadonlyBinaryOperator) { constexpr char kComputeShaderSource[] = R"(#version 310 es layout(local_size_x=1, local_size_y=1, local_size_z=1) in; layout(std430, binding = 0) buffer blockIn1 { uvec2 data1; }; layout(std430, binding = 1) buffer blockIn2 { uvec2 data2; }; layout(std430, binding = 2) buffer blockIn3 { uvec2 data; } instanceIn3; layout(std430, binding = 3) buffer blockOut { uvec2 data; } instanceOut; void main() { instanceOut.data = data1 + data2 + instanceIn3.data; } )"; ANGLE_GL_COMPUTE_PROGRAM(program, kComputeShaderSource); glUseProgram(program); constexpr unsigned int kComponentCount = 2; constexpr unsigned int kBytesPerComponent = sizeof(unsigned int); constexpr unsigned int kInputValues1[kComponentCount] = {1u, 2u}; constexpr unsigned int kInputValues2[kComponentCount] = {3u, 4u}; constexpr unsigned int kInputValues3[kComponentCount] = {5u, 6u}; // Create shader storage buffer GLBuffer shaderStorageBuffer[4]; glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[0]); glBufferData(GL_SHADER_STORAGE_BUFFER, kComponentCount * kBytesPerComponent, kInputValues1, GL_STATIC_DRAW); glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[1]); glBufferData(GL_SHADER_STORAGE_BUFFER, kComponentCount * kBytesPerComponent, kInputValues2, GL_STATIC_DRAW); glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[2]); glBufferData(GL_SHADER_STORAGE_BUFFER, kComponentCount * kBytesPerComponent, kInputValues3, GL_STATIC_DRAW); glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[3]); glBufferData(GL_SHADER_STORAGE_BUFFER, kComponentCount * kBytesPerComponent, nullptr, GL_STATIC_DRAW); // Bind shader storage buffer glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, shaderStorageBuffer[0]); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, shaderStorageBuffer[1]); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, shaderStorageBuffer[2]); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3, shaderStorageBuffer[3]); glDispatchCompute(1, 1, 1); glFinish(); // Read back shader storage buffer constexpr unsigned int kExpectedValues[kComponentCount] = {9u, 12u}; glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[3]); const GLuint *ptr = reinterpret_cast(glMapBufferRange( GL_SHADER_STORAGE_BUFFER, 0, kComponentCount * kBytesPerComponent, GL_MAP_READ_BIT)); for (unsigned int idx = 0; idx < kComponentCount; idx++) { EXPECT_EQ(kExpectedValues[idx], *(ptr + idx)); } EXPECT_GL_NO_ERROR(); } // Test that ssbo as an argument of a function can be translated. TEST_P(ShaderStorageBufferTest31, SSBOAsFunctionArgument) { constexpr char kComputeShaderSource[] = R"(#version 310 es layout(local_size_x = 1) in; layout(std430, binding = 0) buffer Block { uint var1; uint var2; }; bool compare(uint a, uint b) { return a == b; } uint increase(inout uint a) { a++; return a; } void main(void) { bool isEqual = compare(var1, 2u); if (isEqual) { var2 += increase(var1); } } )"; ANGLE_GL_COMPUTE_PROGRAM(program, kComputeShaderSource); glUseProgram(program); constexpr unsigned int kBytesPerComponent = sizeof(unsigned int); constexpr unsigned int kInputValues[2] = {2u, 2u}; constexpr unsigned int kExpectedValues[2] = {3u, 5u}; GLBuffer shaderStorageBuffer; glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer); glBufferData(GL_SHADER_STORAGE_BUFFER, 2 * kBytesPerComponent, &kInputValues, GL_STATIC_DRAW); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, shaderStorageBuffer); glDispatchCompute(1, 1, 1); glFinish(); glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer); const GLuint *ptr = reinterpret_cast( glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, 2 * kBytesPerComponent, GL_MAP_READ_BIT)); EXPECT_EQ(kExpectedValues[0], *ptr); EXPECT_EQ(kExpectedValues[1], *(ptr + 1)); EXPECT_GL_NO_ERROR(); } // Test that ssbo as unary operand works well. TEST_P(ShaderStorageBufferTest31, SSBOAsUnaryOperand) { constexpr char kComputeShaderSource[] = R"(#version 310 es layout (local_size_x=1) in; layout(binding=0, std140) buffer Storage0 { uint b; } sb_load; layout(binding=1, std140) buffer Storage1 { uint i; } sb_store; void main() { sb_store.i = +sb_load.b; ++sb_store.i; } )"; ANGLE_GL_COMPUTE_PROGRAM(program, kComputeShaderSource); glUseProgram(program); constexpr unsigned int kBytesPerComponent = sizeof(unsigned int); constexpr unsigned kInputValue = 1u; constexpr unsigned int kExpectedValue = 2u; GLBuffer shaderStorageBuffer[2]; glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[0]); glBufferData(GL_SHADER_STORAGE_BUFFER, kBytesPerComponent, &kInputValue, GL_STATIC_DRAW); glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[1]); glBufferData(GL_SHADER_STORAGE_BUFFER, kBytesPerComponent, &kInputValue, GL_STATIC_DRAW); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, shaderStorageBuffer[0]); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, shaderStorageBuffer[1]); glDispatchCompute(1, 1, 1); glFinish(); glBindBuffer(GL_SHADER_STORAGE_BUFFER, shaderStorageBuffer[1]); const GLuint *ptr = reinterpret_cast( glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, kBytesPerComponent, GL_MAP_READ_BIT)); EXPECT_EQ(kExpectedValue, *ptr); EXPECT_GL_NO_ERROR(); } // Test that uniform can be used as the index of buffer variable. TEST_P(ShaderStorageBufferTest31, UniformUsedAsIndexOfBufferVariable) { constexpr char kComputeShaderSource[] = R"(#version 310 es layout (local_size_x=4) in; layout(std140, binding = 0) uniform CB { uint index; } cb; layout(binding=0, std140) buffer Storage0 { uint data[]; } sb_load; layout(binding=1, std140) buffer Storage1 { uint data[]; } sb_store; void main() { sb_store.data[gl_LocalInvocationIndex] = sb_load.data[gl_LocalInvocationID.x + cb.index]; } )"; ANGLE_GL_COMPUTE_PROGRAM(program, kComputeShaderSource); EXPECT_GL_NO_ERROR(); } // Test that inactive but statically used SSBOs with unsized array are handled correctly. // // Glslang wrapper used to replace the layout/qualifier of an inactive SSBO with |struct|, // effectively turning the interface block declaration into a struct definition. This generally // worked except for SSBOs with an unsized array. This test makes sure this special case is // now properly handled. TEST_P(ShaderStorageBufferTest31, InactiveButStaticallyUsedWithUnsizedArray) { constexpr char kComputeShaderSource[] = R"(#version 310 es layout (local_size_x=1) in; layout(binding=0, std140) buffer Storage { uint data[]; } sb; void main() { if (false) { sb.data[0] = 1u; } } )"; ANGLE_GL_COMPUTE_PROGRAM(program, kComputeShaderSource); EXPECT_GL_NO_ERROR(); glUseProgram(program); glDispatchCompute(1, 1, 1); EXPECT_GL_NO_ERROR(); } // Verify the size of the buffer with unsized struct array is calculated correctly TEST_P(ShaderStorageBufferTest31, BigStructUnsizedStructArraySize) { // TODO(http://anglebug.com/3596) ANGLE_SKIP_TEST_IF(IsAMD() && IsWindows() && IsOpenGL()); constexpr char kComputeShaderSource[] = R"(#version 310 es layout (local_size_x=1) in; struct S { mat4 m; // 4 vec4 = 16 floats vec4 a[10]; // 10 vec4 = 40 floats }; layout(binding=0) buffer B { vec4 precedingMember; // 4 floats S precedingMemberUnsizedArray[]; // 56 floats } b; void main() { if (false) { b.precedingMember = vec4(1.0, 1.0, 1.0, 1.0); } } )"; ANGLE_GL_COMPUTE_PROGRAM(program, kComputeShaderSource); EXPECT_GL_NO_ERROR(); glUseProgram(program); glDispatchCompute(1, 1, 1); EXPECT_GL_NO_ERROR(); GLuint resourceIndex = glGetProgramResourceIndex(program, GL_SHADER_STORAGE_BLOCK, "B"); EXPECT_GL_NO_ERROR(); EXPECT_NE(resourceIndex, 0xFFFFFFFF); GLenum property = GL_BUFFER_DATA_SIZE; GLint queryData = -1; glGetProgramResourceiv(program, GL_SHADER_STORAGE_BLOCK, resourceIndex, 1, &property, 1, nullptr, &queryData); EXPECT_GL_NO_ERROR(); // 60 * sizeof(float) = 240 // Vulkan rounds up to the required buffer alignment, so >= 240 EXPECT_GE(queryData, 240); } // Verify the size of the buffer with unsized float array is calculated correctly TEST_P(ShaderStorageBufferTest31, BigStructUnsizedFloatArraySize) { // TODO(http://anglebug.com/3596) ANGLE_SKIP_TEST_IF(IsAMD() && IsWindows() && IsOpenGL()); constexpr char kComputeShaderSource[] = R"(#version 310 es layout (local_size_x=1) in; layout(binding=0) buffer B { vec4 precedingMember; // 4 floats float precedingMemberUnsizedArray[]; // "1" float } b; void main() { if (false) { b.precedingMember = vec4(1.0, 1.0, 1.0, 1.0); } } )"; ANGLE_GL_COMPUTE_PROGRAM(program, kComputeShaderSource); EXPECT_GL_NO_ERROR(); glUseProgram(program); glDispatchCompute(1, 1, 1); EXPECT_GL_NO_ERROR(); GLuint resourceIndex = glGetProgramResourceIndex(program, GL_SHADER_STORAGE_BLOCK, "B"); EXPECT_GL_NO_ERROR(); EXPECT_NE(resourceIndex, 0xFFFFFFFF); GLenum property = GL_BUFFER_DATA_SIZE; GLint queryData = -1; glGetProgramResourceiv(program, GL_SHADER_STORAGE_BLOCK, resourceIndex, 1, &property, 1, nullptr, &queryData); EXPECT_GL_NO_ERROR(); // 5 * sizeof(float) = 20 // Vulkan rounds up to the required buffer alignment, so >= 20 EXPECT_GE(queryData, 20); } // Tests that shader write before pixel pack/unpack works TEST_P(ShaderStorageBufferTest31, ShaderStorageBufferWriteThenPixelPackUnpack) { // Create two textures and framebuffers and make sure they are initialized. GLTexture color1; glBindTexture(GL_TEXTURE_2D, color1); glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 1, 1); GLFramebuffer framebuffer1; glBindFramebuffer(GL_FRAMEBUFFER, framebuffer1); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color1, 0); glClearColor(255, 0, 255, 255); glClear(GL_COLOR_BUFFER_BIT); EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::magenta); GLTexture color2; glBindTexture(GL_TEXTURE_2D, color2); glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 1, 1); GLFramebuffer framebuffer2; glBindFramebuffer(GL_FRAMEBUFFER, framebuffer2); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color2, 0); glClearColor(0, 0, 255, 255); glClear(GL_COLOR_BUFFER_BIT); EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue); constexpr char kComputeShaderSource[] = R"(#version 310 es layout(local_size_x=1, local_size_y=1, local_size_z=1) in; layout(std430, binding = 0) buffer block { uint data[2]; } outBlock; void main() { // Output red to index 0, and green to index 1. outBlock.data[0] = 0xFF0000FFu; outBlock.data[1] = 0xFF00FF00u; } )"; ANGLE_GL_COMPUTE_PROGRAM(program, kComputeShaderSource); glUseProgram(program); constexpr GLsizei kBufferSize = sizeof(GLuint) * 2; constexpr std::array kBufferInitData = {0x01234567u, 0x89ABCDEFu}; // Create a shader storage buffer GLBuffer buffer; glBindBuffer(GL_SHADER_STORAGE_BUFFER, buffer); glBufferData(GL_SHADER_STORAGE_BUFFER, kBufferSize, kBufferInitData.data(), GL_STATIC_DRAW); // Bind shader storage buffer and write to it. glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, buffer); glDispatchCompute(1, 1, 1); EXPECT_GL_NO_ERROR(); // Issue a memory barrier for pixel pack/unpack operations. glMemoryBarrier(GL_PIXEL_BUFFER_BARRIER_BIT); EXPECT_GL_NO_ERROR(); // Use a pixel pack operation to overwrite the output of the compute shader at index 0. Uses // the second framebuffer which is blue. glBindBuffer(GL_PIXEL_PACK_BUFFER, buffer); glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, 0); EXPECT_GL_NO_ERROR(); // Use a pixel unpack operation to re-initialize the other framebuffer with the results from the // compute shader. glBindTexture(GL_TEXTURE_2D, color1); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, buffer); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, reinterpret_cast(sizeof(GLuint))); EXPECT_GL_NO_ERROR(); // Verify that the first framebuffer is now green glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffer1); EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); // Verify the contents of the buffer. It should have blue as the first index and green as the // second. glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT); GLColor *bufferContents = static_cast( glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, kBufferSize, GL_MAP_READ_BIT)); EXPECT_GL_NO_ERROR(); EXPECT_EQ(GLColor::blue, bufferContents[0]); EXPECT_EQ(GLColor::green, bufferContents[1]); glUnmapBuffer(GL_SHADER_STORAGE_BUFFER); EXPECT_GL_NO_ERROR(); } // Tests that shader write after pixel pack/unpack works TEST_P(ShaderStorageBufferTest31, PixelPackUnpackThenShaderStorageBufferWrite) { swapBuffers(); // Create two textures and framebuffers and make sure they are initialized. GLTexture color1; glBindTexture(GL_TEXTURE_2D, color1); glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 1, 1); GLFramebuffer framebuffer1; glBindFramebuffer(GL_FRAMEBUFFER, framebuffer1); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color1, 0); glClearColor(255, 0, 255, 255); glClear(GL_COLOR_BUFFER_BIT); EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::magenta); GLTexture color2; glBindTexture(GL_TEXTURE_2D, color2); glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 1, 1); GLFramebuffer framebuffer2; glBindFramebuffer(GL_FRAMEBUFFER, framebuffer2); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color2, 0); glClearColor(0, 0, 255, 255); glClear(GL_COLOR_BUFFER_BIT); EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue); constexpr GLsizei kBufferSize = sizeof(GLuint) * 2; constexpr std::array kBufferInitData = {0x01234567u, 0xFF00FF00u}; // Create a shader storage buffer GLBuffer buffer; glBindBuffer(GL_SHADER_STORAGE_BUFFER, buffer); glBufferData(GL_SHADER_STORAGE_BUFFER, kBufferSize, kBufferInitData.data(), GL_STATIC_DRAW); // Use a pixel pack operation to overwrite the buffer at index 0. Uses the second framebuffer // which is blue. glBindBuffer(GL_PIXEL_PACK_BUFFER, buffer); glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, 0); EXPECT_GL_NO_ERROR(); // Use a pixel unpack operation to re-initialize the other framebuffer with the contents of the // buffer, which is green. glBindTexture(GL_TEXTURE_2D, color1); glBindBuffer(GL_PIXEL_UNPACK_BUFFER, buffer); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, reinterpret_cast(sizeof(GLuint))); EXPECT_GL_NO_ERROR(); // Issue a memory barrier for pixel pack/unpack operations. glMemoryBarrier(GL_PIXEL_BUFFER_BARRIER_BIT); // Issue a dispatch call that overwrites the buffer, also verifying that the results of the pack // operation is correct. constexpr char kComputeShaderSource[] = R"(#version 310 es layout(local_size_x=1, local_size_y=1, local_size_z=1) in; layout(std430, binding = 0) buffer block { uint data[2]; } outBlock; void main() { if (outBlock.data[0] == 0xFFFF0000u) { outBlock.data[0] = 0x11223344u; } else { outBlock.data[0] = 0xABCDABCDu; } outBlock.data[1] = 0x55667788u; } )"; ANGLE_GL_COMPUTE_PROGRAM(program, kComputeShaderSource); glUseProgram(program); // Bind shader storage buffer and write to it. glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, buffer); glDispatchCompute(1, 1, 1); EXPECT_GL_NO_ERROR(); // Verify that the first framebuffer is now green glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffer1); EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); // Verify that the second framebuffer is still blue glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffer2); EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue); // Verify the contents of the buffer. glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT); uint32_t *bufferContents = static_cast( glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, kBufferSize, GL_MAP_READ_BIT)); EXPECT_GL_NO_ERROR(); EXPECT_EQ(bufferContents[0], 0x11223344u); EXPECT_EQ(bufferContents[1], 0x55667788u); glUnmapBuffer(GL_SHADER_STORAGE_BUFFER); EXPECT_GL_NO_ERROR(); } GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ShaderStorageBufferTest31); ANGLE_INSTANTIATE_TEST_ES31(ShaderStorageBufferTest31); } // namespace