blob: b3456d0042cfb7fdeb957622d5525be5178c718b [file] [log] [blame]
//
// 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<const GLfloat *>(
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<const GLuint *>(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<const GLuint *>(reinterpret_cast<const GLbyte *>(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)
{
// Missing PBO support in Vulkan. http://anglebug.com/3210
ANGLE_SKIP_TEST_IF(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 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<const GLuint *>(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<const GLuint *>(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<GLColor *>(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<GLColor *>(
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 kBufferOffset = kBufferSize + (kBufferSize % bufferAlignOffset);
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<const GLuint *>(reinterpret_cast<const GLbyte *>(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<const GLuint *>(reinterpret_cast<const GLbyte *>(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<GLuint, 4> kUVData = {{
1u,
2u,
0u,
0u,
}};
std::array<GLuint, 8> 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<const GLuint *>(
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<GLuint, 4> kUVData = {{
1u,
2u,
0u,
0u,
}};
std::array<GLuint, 8> 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<const GLuint *>(
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<const GLuint *>(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<GLuint, 2> 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<const GLuint *>(
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<GLuint, 2> kU1Data = {{2u, 3u}};
std::array<GLuint, 3> 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<const GLuint *>(
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<const GLuint *>(reinterpret_cast<const GLbyte *>(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<const GLuint *>(
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<const GLuint *>(
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<const GLuint *>(
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<const GLuint *>(
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<const GLuint *>(
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 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<const GLuint *>(
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<const GLuint *>(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<const GLuint *>(
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<const GLuint *>(
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();
}
ANGLE_INSTANTIATE_TEST_ES31(ShaderStorageBufferTest31);
} // namespace