blob: 398f1b537efbc3557b9d2e641ff020a60c54509f [file]
//
// 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
{
class ShaderStorageBufferTest31 : public ANGLETest
{
protected:
ShaderStorageBufferTest31()
{
setWindowWidth(128);
setWindowHeight(128);
setConfigRedBits(8);
setConfigGreenBits(8);
setConfigBlueBits(8);
setConfigAlphaBits(8);
}
};
// 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)
{
const std::string &vertexShaderSource =
"#version 310 es\n"
"buffer blockName {\n"
" float data;\n"
"};\n"
"void main()\n"
"{\n"
"}\n";
const std::string &fragmentShaderSource =
"#version 310 es\n"
"buffer blockName {\n"
" uint data;\n"
"};\n"
"void main()\n"
"{\n"
"}\n";
GLuint program = CompileProgram(vertexShaderSource, fragmentShaderSource);
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";
const std::string &fragmentShaderSource =
"#version 310 es\n"
"void main()\n"
"{\n"
"}\n";
GLuint program = CompileProgram(vertexShaderSource, fragmentShaderSource);
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, fragmentShaderSource);
EXPECT_EQ(0u, program);
}
// Test shader storage buffer read write.
TEST_P(ShaderStorageBufferTest31, ShaderStorageBufferReadWrite)
{
const std::string &csSource =
"#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"
" if (instanceName.data[0] == 3u)\n"
" instanceName.data[1] = 4u;\n"
" else\n"
" instanceName.data[1] = 5u;\n"
"}\n";
ANGLE_GL_COMPUTE_PROGRAM(program, csSource);
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();
}
// Test atomic memory functions.
TEST_P(ShaderStorageBufferTest31, AtomicMemoryFunctions)
{
const std::string &csSource =
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, csSource);
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)
{
const std::string &csSource1 =
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;
})";
const std::string &csSource2 =
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, csSource1);
ANGLE_GL_COMPUTE_PROGRAM(program2, csSource2);
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();
}
ANGLE_INSTANTIATE_TEST(ShaderStorageBufferTest31, ES31_OPENGL(), ES31_OPENGLES());
} // namespace