diff options
Diffstat (limited to 'gfx/angle/src/tests/gl_tests/UniformBufferTest.cpp')
-rwxr-xr-x | gfx/angle/src/tests/gl_tests/UniformBufferTest.cpp | 573 |
1 files changed, 573 insertions, 0 deletions
diff --git a/gfx/angle/src/tests/gl_tests/UniformBufferTest.cpp b/gfx/angle/src/tests/gl_tests/UniformBufferTest.cpp new file mode 100755 index 000000000..b3577bf30 --- /dev/null +++ b/gfx/angle/src/tests/gl_tests/UniformBufferTest.cpp @@ -0,0 +1,573 @@ +// +// Copyright 2015 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. +// + +#include "test_utils/ANGLETest.h" +#include "test_utils/gl_raii.h" + +using namespace angle; + +namespace +{ + +class UniformBufferTest : public ANGLETest +{ + protected: + UniformBufferTest() + { + setWindowWidth(128); + setWindowHeight(128); + setConfigRedBits(8); + setConfigGreenBits(8); + setConfigBlueBits(8); + setConfigAlphaBits(8); + } + + void SetUp() override + { + ANGLETest::SetUp(); + + const std::string vertexShaderSource = SHADER_SOURCE + ( #version 300 es\n + in vec4 position; + void main() + { + gl_Position = position; + } + ); + const std::string fragmentShaderSource = SHADER_SOURCE + ( #version 300 es\n + precision highp float; + uniform uni { + vec4 color; + }; + + out vec4 fragColor; + + void main() + { + fragColor = color; + } + ); + + mProgram = CompileProgram(vertexShaderSource, fragmentShaderSource); + ASSERT_NE(mProgram, 0u); + + mUniformBufferIndex = glGetUniformBlockIndex(mProgram, "uni"); + ASSERT_NE(mUniformBufferIndex, -1); + + glGenBuffers(1, &mUniformBuffer); + + ASSERT_GL_NO_ERROR(); + } + + void TearDown() override + { + glDeleteBuffers(1, &mUniformBuffer); + glDeleteProgram(mProgram); + ANGLETest::TearDown(); + } + + GLuint mProgram; + GLint mUniformBufferIndex; + GLuint mUniformBuffer; +}; + +// Basic UBO functionality. +TEST_P(UniformBufferTest, Simple) +{ + glClear(GL_COLOR_BUFFER_BIT); + float floatData[4] = {0.5f, 0.75f, 0.25f, 1.0f}; + + glBindBuffer(GL_UNIFORM_BUFFER, mUniformBuffer); + glBufferData(GL_UNIFORM_BUFFER, sizeof(float) * 4, floatData, GL_STATIC_DRAW); + + glBindBufferBase(GL_UNIFORM_BUFFER, 0, mUniformBuffer); + + glUniformBlockBinding(mProgram, mUniformBufferIndex, 0); + drawQuad(mProgram, "position", 0.5f); + + ASSERT_GL_NO_ERROR(); + EXPECT_PIXEL_NEAR(0, 0, 128, 191, 64, 255, 1); +} + +// Test that using a UBO with a non-zero offset and size actually works. +// The first step of this test renders a color from a UBO with a zero offset. +// The second step renders a color from a UBO with a non-zero offset. +TEST_P(UniformBufferTest, UniformBufferRange) +{ + int px = getWindowWidth() / 2; + int py = getWindowHeight() / 2; + + // Query the uniform buffer alignment requirement + GLint alignment; + glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &alignment); + + GLint64 maxUniformBlockSize; + glGetInteger64v(GL_MAX_UNIFORM_BLOCK_SIZE, &maxUniformBlockSize); + if (alignment >= maxUniformBlockSize) + { + // ANGLE doesn't implement UBO offsets for this platform. + // Ignore the test case. + return; + } + + ASSERT_GL_NO_ERROR(); + + // Let's create a buffer which contains two vec4. + GLuint vec4Size = 4 * sizeof(float); + GLuint stride = 0; + do + { + stride += alignment; + } + while (stride < vec4Size); + + std::vector<char> v(2 * stride); + float *first = reinterpret_cast<float*>(v.data()); + float *second = reinterpret_cast<float*>(v.data() + stride); + + first[0] = 10.f / 255.f; + first[1] = 20.f / 255.f; + first[2] = 30.f / 255.f; + first[3] = 40.f / 255.f; + + second[0] = 110.f / 255.f; + second[1] = 120.f / 255.f; + second[2] = 130.f / 255.f; + second[3] = 140.f / 255.f; + + glBindBuffer(GL_UNIFORM_BUFFER, mUniformBuffer); + // We use on purpose a size which is not a multiple of the alignment. + glBufferData(GL_UNIFORM_BUFFER, stride + vec4Size, v.data(), GL_STATIC_DRAW); + + glUniformBlockBinding(mProgram, mUniformBufferIndex, 0); + + EXPECT_GL_NO_ERROR(); + + // Bind the first part of the uniform buffer and draw + // Use a size which is smaller than the alignment to check + // to check that this case is handle correctly in the conversion to 11.1. + glBindBufferRange(GL_UNIFORM_BUFFER, 0, mUniformBuffer, 0, vec4Size); + drawQuad(mProgram, "position", 0.5f); + EXPECT_GL_NO_ERROR(); + EXPECT_PIXEL_EQ(px, py, 10, 20, 30, 40); + + // Bind the second part of the uniform buffer and draw + // Furthermore the D3D11.1 backend will internally round the vec4Size (16 bytes) to a stride (256 bytes) + // hence it will try to map the range [stride, 2 * stride] which is + // out-of-bound of the buffer bufferSize = stride + vec4Size < 2 * stride. + // Ensure that this behaviour works. + glBindBufferRange(GL_UNIFORM_BUFFER, 0, mUniformBuffer, stride, vec4Size); + drawQuad(mProgram, "position", 0.5f); + EXPECT_GL_NO_ERROR(); + EXPECT_PIXEL_EQ(px, py, 110, 120, 130, 140); +} + +// Test uniform block bindings. +TEST_P(UniformBufferTest, UniformBufferBindings) +{ + int px = getWindowWidth() / 2; + int py = getWindowHeight() / 2; + + ASSERT_GL_NO_ERROR(); + + // Let's create a buffer which contains one vec4. + GLuint vec4Size = 4 * sizeof(float); + std::vector<char> v(vec4Size); + float *first = reinterpret_cast<float*>(v.data()); + + first[0] = 10.f / 255.f; + first[1] = 20.f / 255.f; + first[2] = 30.f / 255.f; + first[3] = 40.f / 255.f; + + glBindBuffer(GL_UNIFORM_BUFFER, mUniformBuffer); + glBufferData(GL_UNIFORM_BUFFER, vec4Size, v.data(), GL_STATIC_DRAW); + + EXPECT_GL_NO_ERROR(); + + // Try to bind the buffer to binding point 2 + glUniformBlockBinding(mProgram, mUniformBufferIndex, 2); + glBindBufferBase(GL_UNIFORM_BUFFER, 2, mUniformBuffer); + drawQuad(mProgram, "position", 0.5f); + EXPECT_GL_NO_ERROR(); + EXPECT_PIXEL_EQ(px, py, 10, 20, 30, 40); + + // Clear the framebuffer + glClearColor(0.0, 0.0, 0.0, 0.0); + glClear(GL_COLOR_BUFFER_BIT); + EXPECT_PIXEL_EQ(px, py, 0, 0, 0, 0); + + // Try to bind the buffer to another binding point + glUniformBlockBinding(mProgram, mUniformBufferIndex, 5); + glBindBufferBase(GL_UNIFORM_BUFFER, 5, mUniformBuffer); + drawQuad(mProgram, "position", 0.5f); + EXPECT_GL_NO_ERROR(); + EXPECT_PIXEL_EQ(px, py, 10, 20, 30, 40); +} + +// Test that ANGLE handles used but unbound UBO. +// TODO: A test case shouldn't depend on the error code of an undefined behaviour. Move this to unit tests of the validation layer. +TEST_P(UniformBufferTest, UnboundUniformBuffer) +{ + glUniformBlockBinding(mProgram, mUniformBufferIndex, 0); + glBindBufferBase(GL_UNIFORM_BUFFER, 0, 0); + EXPECT_GL_NO_ERROR(); + + drawQuad(mProgram, "position", 0.5f); + EXPECT_GL_ERROR(GL_INVALID_OPERATION); +} + +// Update a UBO many time and verify that ANGLE uses the latest version of the data. +// https://code.google.com/p/angleproject/issues/detail?id=965 +TEST_P(UniformBufferTest, UniformBufferManyUpdates) +{ + // TODO(jmadill): Figure out why this fails on Intel OpenGL. + if (IsIntel() && IsOpenGL()) + { + std::cout << "Test skipped on Intel OpenGL." << std::endl; + return; + } + + int px = getWindowWidth() / 2; + int py = getWindowHeight() / 2; + + ASSERT_GL_NO_ERROR(); + + float data[4]; + + glBindBuffer(GL_UNIFORM_BUFFER, mUniformBuffer); + glBufferData(GL_UNIFORM_BUFFER, sizeof(data), NULL, GL_DYNAMIC_DRAW); + glUniformBlockBinding(mProgram, mUniformBufferIndex, 0); + glBindBufferBase(GL_UNIFORM_BUFFER, 0, mUniformBuffer); + + EXPECT_GL_NO_ERROR(); + + // Repeteadly update the data and draw + for (size_t i = 0; i < 10; ++i) + { + data[0] = (i + 10.f) / 255.f; + data[1] = (i + 20.f) / 255.f; + data[2] = (i + 30.f) / 255.f; + data[3] = (i + 40.f) / 255.f; + + glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(data), data); + + drawQuad(mProgram, "position", 0.5f); + EXPECT_GL_NO_ERROR(); + EXPECT_PIXEL_EQ(px, py, i + 10, i + 20, i + 30, i + 40); + } +} + +// Use a large number of buffer ranges (compared to the actual size of the UBO) +TEST_P(UniformBufferTest, ManyUniformBufferRange) +{ + int px = getWindowWidth() / 2; + int py = getWindowHeight() / 2; + + // Query the uniform buffer alignment requirement + GLint alignment; + glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &alignment); + + GLint64 maxUniformBlockSize; + glGetInteger64v(GL_MAX_UNIFORM_BLOCK_SIZE, &maxUniformBlockSize); + if (alignment >= maxUniformBlockSize) + { + // ANGLE doesn't implement UBO offsets for this platform. + // Ignore the test case. + return; + } + + ASSERT_GL_NO_ERROR(); + + // Let's create a buffer which contains eight vec4. + GLuint vec4Size = 4 * sizeof(float); + GLuint stride = 0; + do + { + stride += alignment; + } + while (stride < vec4Size); + + std::vector<char> v(8 * stride); + + for (size_t i = 0; i < 8; ++i) + { + float *data = reinterpret_cast<float*>(v.data() + i * stride); + + data[0] = (i + 10.f) / 255.f; + data[1] = (i + 20.f) / 255.f; + data[2] = (i + 30.f) / 255.f; + data[3] = (i + 40.f) / 255.f; + } + + glBindBuffer(GL_UNIFORM_BUFFER, mUniformBuffer); + glBufferData(GL_UNIFORM_BUFFER, v.size(), v.data(), GL_STATIC_DRAW); + + glUniformBlockBinding(mProgram, mUniformBufferIndex, 0); + + EXPECT_GL_NO_ERROR(); + + // Bind each possible offset + for (size_t i = 0; i < 8; ++i) + { + glBindBufferRange(GL_UNIFORM_BUFFER, 0, mUniformBuffer, i * stride, stride); + drawQuad(mProgram, "position", 0.5f); + EXPECT_GL_NO_ERROR(); + EXPECT_PIXEL_EQ(px, py, 10 + i, 20 + i, 30 + i, 40 + i); + } + + // Try to bind larger range + for (size_t i = 0; i < 7; ++i) + { + glBindBufferRange(GL_UNIFORM_BUFFER, 0, mUniformBuffer, i * stride, 2 * stride); + drawQuad(mProgram, "position", 0.5f); + EXPECT_GL_NO_ERROR(); + EXPECT_PIXEL_EQ(px, py, 10 + i, 20 + i, 30 + i, 40 + i); + } + + // Try to bind even larger range + for (size_t i = 0; i < 5; ++i) + { + glBindBufferRange(GL_UNIFORM_BUFFER, 0, mUniformBuffer, i * stride, 4 * stride); + drawQuad(mProgram, "position", 0.5f); + EXPECT_GL_NO_ERROR(); + EXPECT_PIXEL_EQ(px, py, 10 + i, 20 + i, 30 + i, 40 + i); + } +} + +// Tests that active uniforms have the right names. +TEST_P(UniformBufferTest, ActiveUniformNames) +{ + const std::string &vertexShaderSource = + "#version 300 es\n" + "in vec2 position;\n" + "out vec2 v;\n" + "uniform blockName1 {\n" + " float f1;\n" + "} instanceName1;\n" + "uniform blockName2 {\n" + " float f2;\n" + "} instanceName2[1];\n" + "void main() {\n" + " v = vec2(instanceName1.f1, instanceName2[0].f2);\n" + " gl_Position = vec4(position, 0, 1);\n" + "}"; + + const std::string &fragmentShaderSource = + "#version 300 es\n" + "precision highp float;\n" + "in vec2 v;\n" + "out vec4 color;\n" + "void main() {\n" + " color = vec4(v, 0, 1);\n" + "}"; + + GLuint program = CompileProgram(vertexShaderSource, fragmentShaderSource); + ASSERT_NE(0u, program); + + GLint activeUniformBlocks; + glGetProgramiv(program, GL_ACTIVE_UNIFORM_BLOCKS, &activeUniformBlocks); + ASSERT_EQ(2, activeUniformBlocks); + + GLint maxLength; + GLsizei length; + glGetProgramiv(program, GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH, &maxLength); + std::vector<GLchar> strBlockNameBuffer(maxLength + 1, 0); + glGetActiveUniformBlockName(program, 0, maxLength, &length, &strBlockNameBuffer[0]); + ASSERT_GL_NO_ERROR(); + EXPECT_EQ("blockName1", std::string(&strBlockNameBuffer[0])); + + glGetActiveUniformBlockName(program, 1, maxLength, &length, &strBlockNameBuffer[0]); + ASSERT_GL_NO_ERROR(); + EXPECT_EQ("blockName2[0]", std::string(&strBlockNameBuffer[0])); + + GLint activeUniforms; + glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &activeUniforms); + + ASSERT_EQ(2, activeUniforms); + + GLint size; + GLenum type; + glGetProgramiv(program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxLength); + std::vector<GLchar> strUniformNameBuffer(maxLength + 1, 0); + glGetActiveUniform(program, 0, maxLength, &length, &size, &type, &strUniformNameBuffer[0]); + + ASSERT_GL_NO_ERROR(); + EXPECT_EQ(1, size); + EXPECT_GLENUM_EQ(GL_FLOAT, type); + EXPECT_EQ("blockName1.f1", std::string(&strUniformNameBuffer[0])); + + glGetActiveUniform(program, 1, maxLength, &length, &size, &type, &strUniformNameBuffer[0]); + + ASSERT_GL_NO_ERROR(); + EXPECT_EQ(1, size); + EXPECT_GLENUM_EQ(GL_FLOAT, type); + EXPECT_EQ("blockName2.f2", std::string(&strUniformNameBuffer[0])); +} + +// Tests active uniforms and blocks when the layout is std140, shared and packed. +TEST_P(UniformBufferTest, ActiveUniformNumberAndName) +{ + // TODO(Jiajia): Figure out why this fails on Intel on Mac. + // This case can pass on Intel Mac-10.11/10.12. But it fails on Intel Mac-10.10. + if (IsIntel() && IsOSX()) + { + std::cout << "Test skipped on Intel on Mac." << std::endl; + return; + } + + // This case fails on all AMD platforms (Mac, Linux, Win). + // TODO(zmo): This actually passes on certain AMD cards, but we don't have + // a way to do device specific handling yet. + if (IsAMD()) + { + std::cout << "Test skipped on AMD." << std::endl; + return; + } + + const std::string &vertexShaderSource = + "#version 300 es\n" + "in vec2 position;\n" + "out float v;\n" + "struct S {\n" + " highp ivec3 a;\n" + " mediump ivec2 b[4];\n" + "};\n" + "layout(std140) uniform blockName0 {\n" + " S s0;\n" + " lowp vec2 v0;\n" + " S s1[2];\n" + " highp uint u0;\n" + "};\n" + "layout(std140) uniform blockName1 {\n" + " float f1;\n" + " bool b1;\n" + "} instanceName1;\n" + "layout(shared) uniform blockName2 {\n" + " float f2;\n" + "};\n" + "layout(packed) uniform blockName3 {\n" + " float f3;\n" + "};\n" + "void main() {\n" + " v = instanceName1.f1;\n" + " gl_Position = vec4(position, 0, 1);\n" + "}"; + + const std::string &fragmentShaderSource = + "#version 300 es\n" + "precision highp float;\n" + "in float v;\n" + "out vec4 color;\n" + "void main() {\n" + " color = vec4(v, 0, 0, 1);\n" + "}"; + + ANGLE_GL_PROGRAM(program, vertexShaderSource, fragmentShaderSource); + + // Note that the packed |blockName3| might (or might not) be optimized out. + GLint activeUniforms; + glGetProgramiv(program.get(), GL_ACTIVE_UNIFORMS, &activeUniforms); + EXPECT_GE(activeUniforms, 11); + + GLint activeUniformBlocks; + glGetProgramiv(program.get(), GL_ACTIVE_UNIFORM_BLOCKS, &activeUniformBlocks); + EXPECT_GE(activeUniformBlocks, 3); + + GLint maxLength, size; + GLenum type; + GLsizei length; + glGetProgramiv(program.get(), GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxLength); + std::vector<GLchar> strBuffer(maxLength + 1, 0); + + glGetActiveUniform(program.get(), 0, maxLength, &length, &size, &type, &strBuffer[0]); + ASSERT_GL_NO_ERROR(); + EXPECT_EQ(1, size); + EXPECT_EQ("s0.a", std::string(&strBuffer[0])); + + glGetActiveUniform(program.get(), 1, maxLength, &length, &size, &type, &strBuffer[0]); + ASSERT_GL_NO_ERROR(); + EXPECT_EQ(4, size); + EXPECT_EQ("s0.b[0]", std::string(&strBuffer[0])); + + glGetActiveUniform(program.get(), 2, maxLength, &length, &size, &type, &strBuffer[0]); + ASSERT_GL_NO_ERROR(); + EXPECT_EQ(1, size); + EXPECT_EQ("v0", std::string(&strBuffer[0])); + + glGetActiveUniform(program.get(), 3, maxLength, &length, &size, &type, &strBuffer[0]); + ASSERT_GL_NO_ERROR(); + EXPECT_EQ(1, size); + EXPECT_EQ("s1[0].a", std::string(&strBuffer[0])); + + glGetActiveUniform(program.get(), 4, maxLength, &length, &size, &type, &strBuffer[0]); + ASSERT_GL_NO_ERROR(); + EXPECT_EQ(4, size); + EXPECT_EQ("s1[0].b[0]", std::string(&strBuffer[0])); + + glGetActiveUniform(program.get(), 5, maxLength, &length, &size, &type, &strBuffer[0]); + ASSERT_GL_NO_ERROR(); + EXPECT_EQ(1, size); + EXPECT_EQ("s1[1].a", std::string(&strBuffer[0])); + + glGetActiveUniform(program.get(), 6, maxLength, &length, &size, &type, &strBuffer[0]); + ASSERT_GL_NO_ERROR(); + EXPECT_EQ(4, size); + EXPECT_EQ("s1[1].b[0]", std::string(&strBuffer[0])); + + glGetActiveUniform(program.get(), 7, maxLength, &length, &size, &type, &strBuffer[0]); + ASSERT_GL_NO_ERROR(); + EXPECT_EQ(1, size); + EXPECT_EQ("u0", std::string(&strBuffer[0])); + + glGetActiveUniform(program.get(), 8, maxLength, &length, &size, &type, &strBuffer[0]); + ASSERT_GL_NO_ERROR(); + EXPECT_EQ(1, size); + EXPECT_EQ("blockName1.f1", std::string(&strBuffer[0])); + + glGetActiveUniform(program.get(), 9, maxLength, &length, &size, &type, &strBuffer[0]); + ASSERT_GL_NO_ERROR(); + EXPECT_EQ(1, size); + EXPECT_EQ("blockName1.b1", std::string(&strBuffer[0])); + + glGetActiveUniform(program.get(), 10, maxLength, &length, &size, &type, &strBuffer[0]); + ASSERT_GL_NO_ERROR(); + EXPECT_EQ(1, size); + EXPECT_EQ("f2", std::string(&strBuffer[0])); +} + +// Test that using a very large buffer to back a small uniform block works OK. +TEST_P(UniformBufferTest, VeryLarge) +{ + glClear(GL_COLOR_BUFFER_BIT); + float floatData[4] = {0.5f, 0.75f, 0.25f, 1.0f}; + + GLsizei bigSize = 4096 * 64; + std::vector<GLubyte> zero(bigSize, 0); + + glBindBuffer(GL_UNIFORM_BUFFER, mUniformBuffer); + glBufferData(GL_UNIFORM_BUFFER, bigSize, zero.data(), GL_STATIC_DRAW); + glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(float) * 4, floatData); + + glBindBufferBase(GL_UNIFORM_BUFFER, 0, mUniformBuffer); + + glUniformBlockBinding(mProgram, mUniformBufferIndex, 0); + drawQuad(mProgram, "position", 0.5f); + + ASSERT_GL_NO_ERROR(); + EXPECT_PIXEL_NEAR(0, 0, 128, 191, 64, 255, 1); +} + +// Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against. +ANGLE_INSTANTIATE_TEST(UniformBufferTest, + ES3_D3D11(), + ES3_D3D11_FL11_1(), + ES3_D3D11_FL11_1_REFERENCE(), + ES3_OPENGL(), + ES3_OPENGLES()); + +} // namespace |