| // |
| // 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; |
| |
| 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 = nullptr; |
| mDrawArraysInstancedANGLE = nullptr; |
| mDrawElementsInstancedANGLE = nullptr; |
| |
| const char *extensionString = reinterpret_cast<const char *>(glGetString(GL_EXTENSIONS)); |
| if (strstr(extensionString, "GL_ANGLE_instanced_arrays")) |
| { |
| mVertexAttribDivisorANGLE = |
| (PFNGLVERTEXATTRIBDIVISORANGLEPROC)eglGetProcAddress("glVertexAttribDivisorANGLE"); |
| mDrawArraysInstancedANGLE = |
| (PFNGLDRAWARRAYSINSTANCEDANGLEPROC)eglGetProcAddress("glDrawArraysInstancedANGLE"); |
| mDrawElementsInstancedANGLE = (PFNGLDRAWELEMENTSINSTANCEDANGLEPROC)eglGetProcAddress( |
| "glDrawElementsInstancedANGLE"); |
| } |
| |
| ASSERT_NE(nullptr, mVertexAttribDivisorANGLE); |
| ASSERT_NE(nullptr, mDrawArraysInstancedANGLE); |
| ASSERT_NE(nullptr, mDrawElementsInstancedANGLE); |
| |
| // Initialize the vertex and index vectors |
| constexpr GLfloat qvertex1[3] = {-quadRadius, quadRadius, 0.0f}; |
| constexpr GLfloat qvertex2[3] = {-quadRadius, -quadRadius, 0.0f}; |
| constexpr GLfloat qvertex3[3] = {quadRadius, -quadRadius, 0.0f}; |
| constexpr 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); |
| |
| constexpr GLfloat coord1[2] = {0.0f, 0.0f}; |
| constexpr GLfloat coord2[2] = {0.0f, 1.0f}; |
| constexpr GLfloat coord3[2] = {1.0f, 1.0f}; |
| constexpr 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) |
| { |
| const 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 = |
| "precision mediump float;\n" |
| "void main()\n" |
| "{\n" |
| " gl_FragColor = vec4(1.0, 0, 0, 1.0);\n" |
| "}\n"; |
| |
| 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 = |
| "attribute vec3 a_position;\n" |
| "attribute vec3 a_instancePos;\n" |
| "void main()\n" |
| "{\n" |
| " gl_Position = vec4(a_position.xyz, 1.0);\n" |
| " gl_Position = vec4(a_instancePos.xyz, 1.0);\n" |
| " gl_PointSize = 6.0;\n" |
| "}\n"; |
| |
| const std::string fs = |
| "precision mediump float;\n" |
| "void main()\n" |
| "{\n" |
| " gl_FragColor = vec4(1.0, 0, 0, 1.0);\n" |
| "}\n"; |
| // 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, const 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(-1, uniformLoc); |
| 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 = |
| "precision mediump float;\n" |
| "void main()\n" |
| "{\n" |
| " gl_FragColor = vec4(1.0, 0, 0, 1.0);\n" |
| "}\n"; |
| |
| ANGLE_GL_PROGRAM(program, vs, fs); |
| |
| // 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; |
| |
| static constexpr 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 = |
| "attribute vec3 a_instancePos;\n" |
| "attribute vec3 a_position;\n" |
| "void main()\n" |
| "{\n" |
| " gl_Position = vec4(a_position.xyz + a_instancePos.xyz, 1.0);\n" |
| "}\n"; |
| |
| 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 = |
| "attribute vec3 a_position;\n" |
| "attribute vec3 a_instancePos;\n" |
| "void main()\n" |
| "{\n" |
| " gl_Position = vec4(a_position.xyz + a_instancePos.xyz, 1.0);\n" |
| "}\n"; |
| |
| 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 = |
| "attribute vec3 a_position;\n" |
| "attribute vec3 a_instancePos;\n" |
| "uniform vec3 u_offset;\n" |
| "void main()\n" |
| "{\n" |
| " gl_Position = vec4(a_position.xyz + a_instancePos.xyz + u_offset, 1.0);\n" |
| "}\n"; |
| |
| setupDrawArraysTest(vs); |
| |
| constexpr float offset1[3] = {0, 0, 0}; |
| runDrawArraysTest(0, 6, 2, offset1); |
| |
| constexpr 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 |
| constexpr 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 |
| const Vector3 pos[] = {Vector3(0), Vector3(0), Vector3(0), Vector3(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(); |
| } |
| |
| class InstancingTestES31 : public InstancingTest |
| { |
| public: |
| InstancingTestES31() {} |
| }; |
| |
| // Verify that VertexAttribDivisor can update both binding divisor and attribBinding. |
| TEST_P(InstancingTestES31, UpdateAttribBindingByVertexAttribDivisor) |
| { |
| const std::string vs = |
| "attribute vec3 a_instancePos;\n" |
| "attribute vec3 a_position;\n" |
| "void main()\n" |
| "{\n" |
| " gl_Position = vec4(a_position.xyz + a_instancePos.xyz, 1.0);\n" |
| "}\n"; |
| |
| const std::string fs = |
| "precision mediump float;\n" |
| "void main()\n" |
| "{\n" |
| " gl_FragColor = vec4(1.0, 0, 0, 1.0);\n" |
| "}\n"; |
| |
| constexpr GLsizei kFloatStride = 4; |
| |
| ANGLE_GL_PROGRAM(program, vs, fs); |
| glUseProgram(program); |
| |
| // Get the attribute locations |
| GLint positionLoc = glGetAttribLocation(program, "a_position"); |
| GLint instancePosLoc = glGetAttribLocation(program, "a_instancePos"); |
| ASSERT_NE(-1, positionLoc); |
| ASSERT_NE(-1, instancePosLoc); |
| ASSERT_GL_NO_ERROR(); |
| |
| GLuint vao; |
| glGenVertexArrays(1, &vao); |
| glBindVertexArray(vao); |
| |
| GLBuffer quadBuffer; |
| glBindBuffer(GL_ARRAY_BUFFER, quadBuffer); |
| glBufferData(GL_ARRAY_BUFFER, mQuadVertices.size() * kFloatStride, mQuadVertices.data(), |
| GL_STATIC_DRAW); |
| GLBuffer instancesBuffer; |
| glBindBuffer(GL_ARRAY_BUFFER, instancesBuffer); |
| glBufferData(GL_ARRAY_BUFFER, mInstances.size() * kFloatStride, mInstances.data(), |
| GL_STATIC_DRAW); |
| |
| // Set the formats by VertexAttribFormat |
| glVertexAttribFormat(positionLoc, 3, GL_FLOAT, GL_FALSE, 0); |
| glVertexAttribFormat(instancePosLoc, 3, GL_FLOAT, GL_FALSE, 0); |
| glEnableVertexAttribArray(positionLoc); |
| glEnableVertexAttribArray(instancePosLoc); |
| |
| const GLint positionBinding = instancePosLoc; |
| const GLint instanceBinding = positionLoc; |
| |
| // Load the vertex position into the binding indexed positionBinding (== instancePosLoc) |
| // Load the instance position into the binding indexed instanceBinding (== positionLoc) |
| glBindVertexBuffer(positionBinding, quadBuffer, 0, kFloatStride * 3); |
| glBindVertexBuffer(instanceBinding, instancesBuffer, 0, kFloatStride * 3); |
| |
| // The attribute indexed positionLoc is using the binding indexed positionBinding |
| // The attribute indexed instancePosLoc is using the binding indexed instanceBinding |
| glVertexAttribBinding(positionLoc, positionBinding); |
| glVertexAttribBinding(instancePosLoc, instanceBinding); |
| |
| // Enable instancing on the binding indexed instanceBinding |
| glVertexBindingDivisor(instanceBinding, 1); |
| |
| // Do the first instanced draw |
| glDrawElementsInstanced(GL_TRIANGLES, static_cast<GLsizei>(mIndices.size()), GL_UNSIGNED_SHORT, |
| mIndices.data(), static_cast<GLsizei>(mInstances.size()) / 3); |
| checkQuads(); |
| |
| // Load the vertex position into the binding indexed positionLoc. |
| // Load the instance position into the binding indexed instancePosLoc. |
| glBindVertexBuffer(positionLoc, quadBuffer, 0, kFloatStride * 3); |
| glBindVertexBuffer(instancePosLoc, instancesBuffer, 0, kFloatStride * 3); |
| |
| // The attribute indexed positionLoc is using the binding indexed positionLoc. |
| glVertexAttribBinding(positionLoc, positionLoc); |
| |
| // Call VertexAttribDivisor to both enable instancing on instancePosLoc and set the attribute |
| // indexed instancePosLoc using the binding indexed instancePosLoc. |
| glVertexAttribDivisor(instancePosLoc, 1); |
| |
| // Do the second instanced draw |
| glDrawElementsInstanced(GL_TRIANGLES, static_cast<GLsizei>(mIndices.size()), GL_UNSIGNED_SHORT, |
| mIndices.data(), static_cast<GLsizei>(mInstances.size()) / 3); |
| checkQuads(); |
| |
| glDeleteVertexArrays(1, &vao); |
| } |
| |
| // 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()); |
| |
| // TODO(jiawei.shao@intel.com): Add D3D11 when Vertex Attrib Binding is supported on D3D11 |
| // back-ends. |
| ANGLE_INSTANTIATE_TEST(InstancingTestES31, ES31_OPENGL(), ES31_OPENGLES()); |