| // |
| // Copyright (c) 2014 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. |
| // |
| // BufferSubDataBenchmark: |
| // Performance test for ANGLE buffer updates. |
| // |
| |
| #include <sstream> |
| |
| #include "ANGLEPerfTest.h" |
| #include "test_utils/draw_call_perf_utils.h" |
| |
| using namespace angle; |
| |
| namespace |
| { |
| |
| struct BufferSubDataParams final : public RenderTestParams |
| { |
| BufferSubDataParams() |
| { |
| // Common default values |
| majorVersion = 2; |
| minorVersion = 0; |
| windowWidth = 512; |
| windowHeight = 512; |
| updateSize = 3000; |
| bufferSize = 40000000; |
| iterations = 4; |
| updateRate = 1; |
| } |
| |
| std::string suffix() const override; |
| |
| GLboolean vertexNormalized; |
| GLenum vertexType; |
| GLint vertexComponentCount; |
| unsigned int updateRate; |
| |
| // static parameters |
| GLsizeiptr updateSize; |
| GLsizeiptr bufferSize; |
| unsigned int iterations; |
| }; |
| |
| std::ostream &operator<<(std::ostream &os, const BufferSubDataParams ¶ms) |
| { |
| os << params.suffix().substr(1); |
| return os; |
| } |
| |
| class BufferSubDataBenchmark : public ANGLERenderTest, |
| public ::testing::WithParamInterface<BufferSubDataParams> |
| { |
| public: |
| BufferSubDataBenchmark(); |
| |
| void initializeBenchmark() override; |
| void destroyBenchmark() override; |
| void drawBenchmark() override; |
| |
| private: |
| GLuint mProgram; |
| GLuint mBuffer; |
| uint8_t *mUpdateData; |
| int mNumTris; |
| }; |
| |
| GLfloat *GetFloatData(GLint componentCount) |
| { |
| static GLfloat vertices2[] = |
| { |
| 1, 2, |
| 0, 0, |
| 2, 0, |
| }; |
| |
| static GLfloat vertices3[] = |
| { |
| 1, 2, 1, |
| 0, 0, 1, |
| 2, 0, 1, |
| }; |
| |
| static GLfloat vertices4[] = |
| { |
| 1, 2, 1, 3, |
| 0, 0, 1, 3, |
| 2, 0, 1, 3, |
| }; |
| |
| switch (componentCount) |
| { |
| case 2: |
| return vertices2; |
| case 3: |
| return vertices3; |
| case 4: |
| return vertices4; |
| default: |
| return nullptr; |
| } |
| } |
| |
| template <class T> |
| GLsizeiptr GetNormalizedData(GLsizeiptr numElements, GLfloat *floatData, std::vector<uint8_t> *data) |
| { |
| GLsizeiptr triDataSize = sizeof(T) * numElements; |
| data->resize(triDataSize); |
| |
| T *destPtr = reinterpret_cast<T*>(data->data()); |
| |
| for (GLsizeiptr dataIndex = 0; dataIndex < numElements; dataIndex++) |
| { |
| GLfloat scaled = floatData[dataIndex] * 0.25f; |
| destPtr[dataIndex] = static_cast<T>(scaled * static_cast<GLfloat>(std::numeric_limits<T>::max())); |
| } |
| |
| return triDataSize; |
| } |
| |
| template <class T> |
| GLsizeiptr GetIntData(GLsizeiptr numElements, GLfloat *floatData, std::vector<uint8_t> *data) |
| { |
| GLsizeiptr triDataSize = sizeof(T) * numElements; |
| data->resize(triDataSize); |
| |
| T *destPtr = reinterpret_cast<T*>(data->data()); |
| |
| for (GLsizeiptr dataIndex = 0; dataIndex < numElements; dataIndex++) |
| { |
| destPtr[dataIndex] = static_cast<T>(floatData[dataIndex]); |
| } |
| |
| return triDataSize; |
| } |
| |
| GLsizeiptr GetVertexData(GLenum type, GLint componentCount, GLboolean normalized, std::vector<uint8_t> *data) |
| { |
| GLsizeiptr triDataSize = 0; |
| GLfloat *floatData = GetFloatData(componentCount); |
| |
| if (type == GL_FLOAT) |
| { |
| triDataSize = sizeof(GLfloat) * componentCount * 3; |
| data->resize(triDataSize); |
| memcpy(data->data(), floatData, triDataSize); |
| } |
| else if (normalized == GL_TRUE) |
| { |
| GLsizeiptr numElements = componentCount * 3; |
| |
| switch (type) |
| { |
| case GL_BYTE: |
| triDataSize = GetNormalizedData<GLbyte>(numElements, floatData, data); |
| break; |
| case GL_SHORT: |
| triDataSize = GetNormalizedData<GLshort>(numElements, floatData, data); |
| break; |
| case GL_INT: |
| triDataSize = GetNormalizedData<GLint>(numElements, floatData, data); |
| break; |
| case GL_UNSIGNED_BYTE: |
| triDataSize = GetNormalizedData<GLubyte>(numElements, floatData, data); |
| break; |
| case GL_UNSIGNED_SHORT: |
| triDataSize = GetNormalizedData<GLushort>(numElements, floatData, data); |
| break; |
| case GL_UNSIGNED_INT: |
| triDataSize = GetNormalizedData<GLuint>(numElements, floatData, data); |
| break; |
| default: |
| assert(0); |
| } |
| } |
| else |
| { |
| GLsizeiptr numElements = componentCount * 3; |
| |
| switch (type) |
| { |
| case GL_BYTE: |
| triDataSize = GetIntData<GLbyte>(numElements, floatData, data); |
| break; |
| case GL_SHORT: |
| triDataSize = GetIntData<GLshort>(numElements, floatData, data); |
| break; |
| case GL_INT: |
| triDataSize = GetIntData<GLint>(numElements, floatData, data); |
| break; |
| case GL_UNSIGNED_BYTE: |
| triDataSize = GetIntData<GLubyte>(numElements, floatData, data); |
| break; |
| case GL_UNSIGNED_SHORT: |
| triDataSize = GetIntData<GLushort>(numElements, floatData, data); |
| break; |
| case GL_UNSIGNED_INT: |
| triDataSize = GetIntData<GLuint>(numElements, floatData, data); |
| break; |
| default: |
| assert(0); |
| } |
| } |
| |
| return triDataSize; |
| } |
| |
| std::string BufferSubDataParams::suffix() const |
| { |
| std::stringstream strstr; |
| |
| strstr << RenderTestParams::suffix(); |
| |
| if (vertexNormalized) |
| { |
| strstr << "_norm"; |
| } |
| |
| switch (vertexType) |
| { |
| case GL_FLOAT: |
| strstr << "_float"; |
| break; |
| case GL_INT: |
| strstr << "_int"; |
| break; |
| case GL_BYTE: |
| strstr << "_byte"; |
| break; |
| case GL_SHORT: |
| strstr << "_short"; |
| break; |
| case GL_UNSIGNED_INT: |
| strstr << "_uint"; |
| break; |
| case GL_UNSIGNED_BYTE: |
| strstr << "_ubyte"; |
| break; |
| case GL_UNSIGNED_SHORT: |
| strstr << "_ushort"; |
| break; |
| default: |
| strstr << "_vunk_" << vertexType << "_"; |
| break; |
| } |
| |
| strstr << vertexComponentCount; |
| strstr << "_every" << updateRate; |
| |
| return strstr.str(); |
| } |
| |
| BufferSubDataBenchmark::BufferSubDataBenchmark() |
| : ANGLERenderTest("BufferSubData", GetParam()), |
| mProgram(0), |
| mBuffer(0), |
| mUpdateData(nullptr), |
| mNumTris(0) |
| { |
| } |
| |
| void BufferSubDataBenchmark::initializeBenchmark() |
| { |
| const auto ¶ms = GetParam(); |
| |
| ASSERT_LT(1, params.vertexComponentCount); |
| ASSERT_LT(0u, params.iterations); |
| |
| mProgram = SetupSimpleScaleAndOffsetProgram(); |
| ASSERT_NE(0u, mProgram); |
| |
| if (params.vertexNormalized == GL_TRUE) |
| { |
| GLfloat scale = 2.0f; |
| GLfloat offset = -0.5f; |
| glUniform1f(glGetUniformLocation(mProgram, "uScale"), scale); |
| glUniform1f(glGetUniformLocation(mProgram, "uOffset"), offset); |
| } |
| |
| glClearColor(0.0f, 0.0f, 0.0f, 0.0f); |
| |
| std::vector<uint8_t> zeroData(params.bufferSize); |
| memset(&zeroData[0], 0, zeroData.size()); |
| |
| glGenBuffers(1, &mBuffer); |
| glBindBuffer(GL_ARRAY_BUFFER, mBuffer); |
| glBufferData(GL_ARRAY_BUFFER, params.bufferSize, &zeroData[0], GL_DYNAMIC_DRAW); |
| |
| glVertexAttribPointer(0, params.vertexComponentCount, params.vertexType, |
| params.vertexNormalized, 0, 0); |
| glEnableVertexAttribArray(0); |
| |
| if (params.updateSize > 0) |
| { |
| mUpdateData = new uint8_t[params.updateSize]; |
| } |
| |
| std::vector<uint8_t> data; |
| GLsizei triDataSize = static_cast<GLsizei>(GetVertexData(params.vertexType, |
| params.vertexComponentCount, |
| params.vertexNormalized, &data)); |
| |
| mNumTris = static_cast<int>(params.updateSize / triDataSize); |
| for (int i = 0, offset = 0; i < mNumTris; ++i) |
| { |
| memcpy(mUpdateData + offset, &data[0], triDataSize); |
| offset += triDataSize; |
| } |
| |
| if (params.updateSize == 0) |
| { |
| mNumTris = 1; |
| glBufferSubData(GL_ARRAY_BUFFER, 0, data.size(), &data[0]); |
| } |
| |
| // Set the viewport |
| glViewport(0, 0, getWindow()->getWidth(), getWindow()->getHeight()); |
| |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| void BufferSubDataBenchmark::destroyBenchmark() |
| { |
| glDeleteProgram(mProgram); |
| glDeleteBuffers(1, &mBuffer); |
| SafeDeleteArray(mUpdateData); |
| } |
| |
| void BufferSubDataBenchmark::drawBenchmark() |
| { |
| glClear(GL_COLOR_BUFFER_BIT); |
| |
| const auto ¶ms = GetParam(); |
| |
| for (unsigned int it = 0; it < params.iterations; it++) |
| { |
| if (params.updateSize > 0 && ((getNumStepsPerformed() % params.updateRate) == 0)) |
| { |
| glBufferSubData(GL_ARRAY_BUFFER, 0, params.updateSize, mUpdateData); |
| } |
| |
| glDrawArrays(GL_TRIANGLES, 0, 3 * mNumTris); |
| } |
| |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| BufferSubDataParams BufferUpdateD3D11Params() |
| { |
| BufferSubDataParams params; |
| params.eglParameters = egl_platform::D3D11(); |
| params.vertexType = GL_FLOAT; |
| params.vertexComponentCount = 4; |
| params.vertexNormalized = GL_FALSE; |
| return params; |
| } |
| |
| BufferSubDataParams BufferUpdateD3D9Params() |
| { |
| BufferSubDataParams params; |
| params.eglParameters = egl_platform::D3D9(); |
| params.vertexType = GL_FLOAT; |
| params.vertexComponentCount = 4; |
| params.vertexNormalized = GL_FALSE; |
| return params; |
| } |
| |
| BufferSubDataParams BufferUpdateOpenGLParams() |
| { |
| BufferSubDataParams params; |
| params.eglParameters = egl_platform::OPENGL(); |
| params.vertexType = GL_FLOAT; |
| params.vertexComponentCount = 4; |
| params.vertexNormalized = GL_FALSE; |
| return params; |
| } |
| |
| TEST_P(BufferSubDataBenchmark, Run) |
| { |
| run(); |
| } |
| |
| ANGLE_INSTANTIATE_TEST(BufferSubDataBenchmark, |
| BufferUpdateD3D11Params(), BufferUpdateD3D9Params(), |
| BufferUpdateOpenGLParams()); |
| |
| } // namespace |