Edit

kc3-lang/angle/src/tests/gl_tests/InstancingTest.cpp

Branch :

  • Show log

    Commit

  • Author : Shao
    Date : 2017-05-22 14:13:27
    Hash : dde78e8c
    Message : ES31: Implement Vertex Attrib Binding on OpenGL This patch intends to implement Vertex Attrib Binding on OpenGL back-ends: 1. Add supports for updating vertex attributes by Vertex Attrib Binding APIs. 2. Refactor the process of updating vertex attribtues in class VertexArray to make it easier to implement this feature. BUG=angleproject:1593 TEST=dEQP-GLES31.functional.vertex_attribute_binding.* Change-Id: I800e61518c552b94b84c415895ad31668b0a84b2 Reviewed-on: https://chromium-review.googlesource.com/510251 Commit-Queue: Jamie Madill <jmadill@chromium.org> Reviewed-by: Jamie Madill <jmadill@chromium.org>

  • src/tests/gl_tests/InstancingTest.cpp
  • //
    // 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());