summaryrefslogtreecommitdiffstats
path: root/gfx/angle/src/tests/gl_tests/InstancingTest.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/angle/src/tests/gl_tests/InstancingTest.cpp')
-rwxr-xr-xgfx/angle/src/tests/gl_tests/InstancingTest.cpp468
1 files changed, 468 insertions, 0 deletions
diff --git a/gfx/angle/src/tests/gl_tests/InstancingTest.cpp b/gfx/angle/src/tests/gl_tests/InstancingTest.cpp
new file mode 100755
index 000000000..633f96892
--- /dev/null
+++ b/gfx/angle/src/tests/gl_tests/InstancingTest.cpp
@@ -0,0 +1,468 @@
+//
+// 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"
+
+using namespace angle;
+
+class InstancingTest : public ANGLETest
+{
+ protected:
+ InstancingTest() : mProgram(0), mVertexBuffer(0)
+ {
+ setWindowWidth(256);
+ setWindowHeight(256);
+ setConfigRedBits(8);
+ setConfigGreenBits(8);
+ setConfigBlueBits(8);
+ setConfigAlphaBits(8);
+ }
+
+ ~InstancingTest() override
+ {
+ glDeleteBuffers(1, &mVertexBuffer);
+ glDeleteProgram(mProgram);
+ }
+
+ void SetUp() override
+ {
+ ANGLETest::SetUp();
+
+ mVertexAttribDivisorANGLE = NULL;
+ mDrawArraysInstancedANGLE = NULL;
+ mDrawElementsInstancedANGLE = NULL;
+
+ char *extensionString = (char*)glGetString(GL_EXTENSIONS);
+ if (strstr(extensionString, "GL_ANGLE_instanced_arrays"))
+ {
+ mVertexAttribDivisorANGLE = (PFNGLVERTEXATTRIBDIVISORANGLEPROC)eglGetProcAddress("glVertexAttribDivisorANGLE");
+ mDrawArraysInstancedANGLE = (PFNGLDRAWARRAYSINSTANCEDANGLEPROC)eglGetProcAddress("glDrawArraysInstancedANGLE");
+ mDrawElementsInstancedANGLE = (PFNGLDRAWELEMENTSINSTANCEDANGLEPROC)eglGetProcAddress("glDrawElementsInstancedANGLE");
+ }
+
+ ASSERT_TRUE(mVertexAttribDivisorANGLE != NULL);
+ ASSERT_TRUE(mDrawArraysInstancedANGLE != NULL);
+ ASSERT_TRUE(mDrawElementsInstancedANGLE != NULL);
+
+ // Initialize the vertex and index vectors
+ GLfloat qvertex1[3] = {-quadRadius, quadRadius, 0.0f};
+ GLfloat qvertex2[3] = {-quadRadius, -quadRadius, 0.0f};
+ GLfloat qvertex3[3] = { quadRadius, -quadRadius, 0.0f};
+ GLfloat qvertex4[3] = { quadRadius, quadRadius, 0.0f};
+ mQuadVertices.insert(mQuadVertices.end(), qvertex1, qvertex1 + 3);
+ mQuadVertices.insert(mQuadVertices.end(), qvertex2, qvertex2 + 3);
+ mQuadVertices.insert(mQuadVertices.end(), qvertex3, qvertex3 + 3);
+ mQuadVertices.insert(mQuadVertices.end(), qvertex4, qvertex4 + 3);
+
+ GLfloat coord1[2] = {0.0f, 0.0f};
+ GLfloat coord2[2] = {0.0f, 1.0f};
+ GLfloat coord3[2] = {1.0f, 1.0f};
+ GLfloat coord4[2] = {1.0f, 0.0f};
+ mTexcoords.insert(mTexcoords.end(), coord1, coord1 + 2);
+ mTexcoords.insert(mTexcoords.end(), coord2, coord2 + 2);
+ mTexcoords.insert(mTexcoords.end(), coord3, coord3 + 2);
+ mTexcoords.insert(mTexcoords.end(), coord4, coord4 + 2);
+
+ mIndices.push_back(0);
+ mIndices.push_back(1);
+ mIndices.push_back(2);
+ mIndices.push_back(0);
+ mIndices.push_back(2);
+ mIndices.push_back(3);
+
+ for (size_t vertexIndex = 0; vertexIndex < 6; ++vertexIndex)
+ {
+ mNonIndexedVertices.insert(mNonIndexedVertices.end(),
+ mQuadVertices.begin() + mIndices[vertexIndex] * 3,
+ mQuadVertices.begin() + mIndices[vertexIndex] * 3 + 3);
+ }
+
+ for (size_t vertexIndex = 0; vertexIndex < 6; ++vertexIndex)
+ {
+ mNonIndexedVertices.insert(mNonIndexedVertices.end(),
+ mQuadVertices.begin() + mIndices[vertexIndex] * 3,
+ mQuadVertices.begin() + mIndices[vertexIndex] * 3 + 3);
+ }
+
+ // Tile a 2x2 grid of the tiles
+ for (float y = -1.0f + quadRadius; y < 1.0f - quadRadius; y += quadRadius * 3)
+ {
+ for (float x = -1.0f + quadRadius; x < 1.0f - quadRadius; x += quadRadius * 3)
+ {
+ GLfloat instance[3] = {x + quadRadius, y + quadRadius, 0.0f};
+ mInstances.insert(mInstances.end(), instance, instance + 3);
+ }
+ }
+
+ glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+
+ glGenBuffers(1, &mVertexBuffer);
+
+ ASSERT_GL_NO_ERROR();
+ }
+
+ void setupDrawArraysTest(const std::string &vs)
+ {
+ const std::string fs = SHADER_SOURCE
+ (
+ precision mediump float;
+ void main()
+ {
+ gl_FragColor = vec4(1.0, 0, 0, 1.0);
+ }
+ );
+
+ mProgram = CompileProgram(vs, fs);
+ ASSERT_NE(0u, mProgram);
+
+ // Set the viewport
+ glViewport(0, 0, getWindowWidth(), getWindowHeight());
+
+ // Clear the color buffer
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ // Use the program object
+ glUseProgram(mProgram);
+ }
+
+ void setupInstancedPointsTest()
+ {
+ mIndices.clear();
+ mIndices.push_back(0);
+ mIndices.push_back(1);
+ mIndices.push_back(2);
+ mIndices.push_back(3);
+
+ // clang-format off
+ const std::string vs = SHADER_SOURCE
+ (
+ attribute vec3 a_position;
+ attribute vec3 a_instancePos;
+ void main()
+ {
+ gl_Position = vec4(a_position.xyz, 1.0);
+ gl_Position = vec4(a_instancePos.xyz, 1.0);
+ gl_PointSize = 6.0;
+ }
+ );
+
+ const std::string fs = SHADER_SOURCE
+ (
+ precision mediump float;
+ void main()
+ {
+ gl_FragColor = vec4(1.0, 0, 0, 1.0);
+ }
+ );
+ // clang-format on
+
+ mProgram = CompileProgram(vs, fs);
+ ASSERT_NE(0u, mProgram);
+
+ // Set the viewport
+ glViewport(0, 0, getWindowWidth(), getWindowHeight());
+
+ // Clear the color buffer
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ // Use the program object
+ glUseProgram(mProgram);
+ }
+
+ void runDrawArraysTest(GLint first, GLsizei count, GLsizei instanceCount, float *offset)
+ {
+ glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer);
+ glBufferData(GL_ARRAY_BUFFER, mInstances.size() * sizeof(mInstances[0]), &mInstances[0], GL_STATIC_DRAW);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+ // Get the attribute locations
+ GLint positionLoc = glGetAttribLocation(mProgram, "a_position");
+ GLint instancePosLoc = glGetAttribLocation(mProgram, "a_instancePos");
+
+ // Load the vertex position
+ glVertexAttribPointer(positionLoc, 3, GL_FLOAT, GL_FALSE, 0, mNonIndexedVertices.data());
+ glEnableVertexAttribArray(positionLoc);
+
+ // Load the instance position
+ glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer);
+ glVertexAttribPointer(instancePosLoc, 3, GL_FLOAT, GL_FALSE, 0, 0);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glEnableVertexAttribArray(instancePosLoc);
+
+ // Enable instancing
+ mVertexAttribDivisorANGLE(instancePosLoc, 1);
+
+ // Offset
+ GLint uniformLoc = glGetUniformLocation(mProgram, "u_offset");
+ ASSERT_NE(uniformLoc, -1);
+ glUniform3fv(uniformLoc, 1, offset);
+
+ // Do the instanced draw
+ mDrawArraysInstancedANGLE(GL_TRIANGLES, first, count, instanceCount);
+
+ ASSERT_GL_NO_ERROR();
+ }
+
+ virtual void runDrawElementsTest(std::string vs, bool shouldAttribZeroBeInstanced)
+ {
+ const std::string fs = SHADER_SOURCE
+ (
+ precision mediump float;
+ void main()
+ {
+ gl_FragColor = vec4(1.0, 0, 0, 1.0);
+ }
+ );
+
+ GLuint program = CompileProgram(vs, fs);
+ ASSERT_NE(program, 0u);
+
+ // Get the attribute locations
+ GLint positionLoc = glGetAttribLocation(program, "a_position");
+ GLint instancePosLoc = glGetAttribLocation(program, "a_instancePos");
+
+ // If this ASSERT fails then the vertex shader code should be refactored
+ ASSERT_EQ(shouldAttribZeroBeInstanced, (instancePosLoc == 0));
+
+ // Set the viewport
+ glViewport(0, 0, getWindowWidth(), getWindowHeight());
+
+ // Clear the color buffer
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ // Use the program object
+ glUseProgram(program);
+
+ // Load the vertex position
+ glVertexAttribPointer(positionLoc, 3, GL_FLOAT, GL_FALSE, 0, mQuadVertices.data());
+ glEnableVertexAttribArray(positionLoc);
+
+ // Load the instance position
+ glVertexAttribPointer(instancePosLoc, 3, GL_FLOAT, GL_FALSE, 0, mInstances.data());
+ glEnableVertexAttribArray(instancePosLoc);
+
+ // Enable instancing
+ mVertexAttribDivisorANGLE(instancePosLoc, 1);
+
+ // Do the instanced draw
+ mDrawElementsInstancedANGLE(GL_TRIANGLES, static_cast<GLsizei>(mIndices.size()),
+ GL_UNSIGNED_SHORT, mIndices.data(),
+ static_cast<GLsizei>(mInstances.size()) / 3);
+
+ ASSERT_GL_NO_ERROR();
+
+ checkQuads();
+ }
+
+ void checkQuads()
+ {
+ // Check that various pixels are the expected color.
+ for (unsigned int quadIndex = 0; quadIndex < 4; ++quadIndex)
+ {
+ unsigned int baseOffset = quadIndex * 3;
+
+ int quadx = static_cast<int>(((mInstances[baseOffset + 0]) * 0.5f + 0.5f) * getWindowWidth());
+ int quady = static_cast<int>(((mInstances[baseOffset + 1]) * 0.5f + 0.5f) * getWindowHeight());
+
+ EXPECT_PIXEL_EQ(quadx, quady, 255, 0, 0, 255);
+ }
+ }
+
+ // Loaded entry points
+ PFNGLVERTEXATTRIBDIVISORANGLEPROC mVertexAttribDivisorANGLE;
+ PFNGLDRAWARRAYSINSTANCEDANGLEPROC mDrawArraysInstancedANGLE;
+ PFNGLDRAWELEMENTSINSTANCEDANGLEPROC mDrawElementsInstancedANGLE;
+
+ // Vertex data
+ std::vector<GLfloat> mQuadVertices;
+ std::vector<GLfloat> mNonIndexedVertices;
+ std::vector<GLfloat> mTexcoords;
+ std::vector<GLfloat> mInstances;
+ std::vector<GLushort> mIndices;
+
+ const GLfloat quadRadius = 0.30f;
+
+ GLuint mProgram;
+ GLuint mVertexBuffer;
+};
+
+class InstancingTestAllConfigs : public InstancingTest
+{
+ protected:
+ InstancingTestAllConfigs() {}
+};
+
+class InstancingTestNo9_3 : public InstancingTest
+{
+ protected:
+ InstancingTestNo9_3() {}
+};
+
+class InstancingTestPoints : public InstancingTest
+{
+ protected:
+ InstancingTestPoints() {}
+};
+
+// This test uses a vertex shader with the first attribute (attribute zero) instanced.
+// On D3D9 and D3D11 FL9_3, this triggers a special codepath that rearranges the input layout sent to D3D,
+// to ensure that slot/stream zero of the input layout doesn't contain per-instance data.
+TEST_P(InstancingTestAllConfigs, AttributeZeroInstanced)
+{
+ const std::string vs = SHADER_SOURCE
+ (
+ attribute vec3 a_instancePos;
+ attribute vec3 a_position;
+ void main()
+ {
+ gl_Position = vec4(a_position.xyz + a_instancePos.xyz, 1.0);
+ }
+ );
+
+ runDrawElementsTest(vs, true);
+}
+
+// Same as AttributeZeroInstanced, but attribute zero is not instanced.
+// This ensures the general instancing codepath (i.e. without rearranging the input layout) works as expected.
+TEST_P(InstancingTestAllConfigs, AttributeZeroNotInstanced)
+{
+ const std::string vs = SHADER_SOURCE
+ (
+ attribute vec3 a_position;
+ attribute vec3 a_instancePos;
+ void main()
+ {
+ gl_Position = vec4(a_position.xyz + a_instancePos.xyz, 1.0);
+ }
+ );
+
+ runDrawElementsTest(vs, false);
+}
+
+// Tests that the "first" parameter to glDrawArraysInstancedANGLE is only an offset into
+// the non-instanced vertex attributes.
+TEST_P(InstancingTestNo9_3, DrawArraysWithOffset)
+{
+ const std::string vs = SHADER_SOURCE
+ (
+ attribute vec3 a_position;
+ attribute vec3 a_instancePos;
+ uniform vec3 u_offset;
+ void main()
+ {
+ gl_Position = vec4(a_position.xyz + a_instancePos.xyz + u_offset, 1.0);
+ }
+ );
+
+ setupDrawArraysTest(vs);
+
+ float offset1[3] = { 0, 0, 0 };
+ runDrawArraysTest(0, 6, 2, offset1);
+
+ float offset2[3] = { 0.0f, 1.0f, 0 };
+ runDrawArraysTest(6, 6, 2, offset2);
+
+ checkQuads();
+}
+
+// This test verifies instancing with GL_POINTS with glDrawArraysInstanced works.
+// On D3D11 FL9_3, this triggers a special codepath that emulates instanced points rendering.
+TEST_P(InstancingTestPoints, DrawArrays)
+{
+ // Disable D3D11 SDK Layers warnings checks, see ANGLE issue 667 for details
+ // On Win7, the D3D SDK Layers emits a false warning for these tests.
+ // This doesn't occur on Windows 10 (Version 1511) though.
+ ignoreD3D11SDKLayersWarnings();
+
+ setupInstancedPointsTest();
+
+ glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer);
+ glBufferData(GL_ARRAY_BUFFER, mInstances.size() * sizeof(mInstances[0]), &mInstances[0],
+ GL_STATIC_DRAW);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+ // Get the attribute locations
+ GLint positionLoc = glGetAttribLocation(mProgram, "a_position");
+ GLint instancePosLoc = glGetAttribLocation(mProgram, "a_instancePos");
+
+ // Load the vertex position
+ GLfloat pos[3] = {0, 0, 0};
+ glVertexAttribPointer(positionLoc, 3, GL_FLOAT, GL_FALSE, 0, pos);
+ glEnableVertexAttribArray(positionLoc);
+
+ // Load the instance position
+ glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer);
+ glVertexAttribPointer(instancePosLoc, 3, GL_FLOAT, GL_FALSE, 0, 0);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glEnableVertexAttribArray(instancePosLoc);
+
+ // Enable instancing
+ mVertexAttribDivisorANGLE(instancePosLoc, 1);
+
+ // Do the instanced draw
+ mDrawArraysInstancedANGLE(GL_POINTS, 0, 1, static_cast<GLsizei>(mInstances.size()) / 3);
+
+ ASSERT_GL_NO_ERROR();
+
+ checkQuads();
+}
+
+// This test verifies instancing with GL_POINTS with glDrawElementsInstanced works.
+// On D3D11 FL9_3, this triggers a special codepath that emulates instanced points rendering.
+TEST_P(InstancingTestPoints, DrawElements)
+{
+ // Disable D3D11 SDK Layers warnings checks, see ANGLE issue 667 for details
+ // On Win7, the D3D SDK Layers emits a false warning for these tests.
+ // This doesn't occur on Windows 10 (Version 1511) though.
+ ignoreD3D11SDKLayersWarnings();
+
+ setupInstancedPointsTest();
+
+ glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer);
+ glBufferData(GL_ARRAY_BUFFER, mInstances.size() * sizeof(mInstances[0]), &mInstances[0],
+ GL_STATIC_DRAW);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+ // Get the attribute locations
+ GLint positionLoc = glGetAttribLocation(mProgram, "a_position");
+ GLint instancePosLoc = glGetAttribLocation(mProgram, "a_instancePos");
+
+ // Load the vertex position
+ GLfloat pos[3] = {0, 0, 0};
+ glVertexAttribPointer(positionLoc, 3, GL_FLOAT, GL_FALSE, 0, pos);
+ glEnableVertexAttribArray(positionLoc);
+
+ // Load the instance position
+ glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer);
+ glVertexAttribPointer(instancePosLoc, 3, GL_FLOAT, GL_FALSE, 0, 0);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glEnableVertexAttribArray(instancePosLoc);
+
+ // Enable instancing
+ mVertexAttribDivisorANGLE(instancePosLoc, 1);
+
+ // Do the instanced draw
+ mDrawElementsInstancedANGLE(GL_POINTS, static_cast<GLsizei>(mIndices.size()), GL_UNSIGNED_SHORT,
+ mIndices.data(), static_cast<GLsizei>(mInstances.size()) / 3);
+
+ ASSERT_GL_NO_ERROR();
+
+ checkQuads();
+}
+
+// Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against.
+// We test on D3D9 and D3D11 9_3 because they use special codepaths when attribute zero is instanced, unlike D3D11.
+ANGLE_INSTANTIATE_TEST(InstancingTestAllConfigs,
+ ES2_D3D9(),
+ ES2_D3D11(),
+ ES2_D3D11_FL9_3(),
+ ES2_OPENGL(),
+ ES2_OPENGLES());
+
+// TODO(jmadill): Figure out the situation with DrawInstanced on FL 9_3
+ANGLE_INSTANTIATE_TEST(InstancingTestNo9_3, ES2_D3D9(), ES2_D3D11());
+
+ANGLE_INSTANTIATE_TEST(InstancingTestPoints, ES2_D3D11(), ES2_D3D11_FL9_3());