| // |
| // 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 <cassert> |
| #include <sstream> |
| |
| #include "ANGLEPerfTest.h" |
| #include "shader_utils.h" |
| |
| namespace |
| { |
| |
| struct BufferSubDataParams final : public RenderTestParams |
| { |
| std::string suffix() const override; |
| |
| GLboolean vertexNormalized; |
| GLenum vertexType; |
| GLint vertexComponentCount; |
| unsigned int updateRate; |
| |
| // static parameters |
| GLsizeiptr updateSize; |
| GLsizeiptr bufferSize; |
| unsigned int iterations; |
| }; |
| |
| class BufferSubDataBenchmark : public ANGLERenderTest, |
| public ::testing::WithParamInterface<BufferSubDataParams> |
| { |
| public: |
| BufferSubDataBenchmark(); |
| |
| bool initializeBenchmark() override; |
| void destroyBenchmark() override; |
| void beginDrawBenchmark() 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 NULL; |
| } |
| } |
| |
| 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(NULL), |
| mNumTris(0) |
| { |
| } |
| |
| bool BufferSubDataBenchmark::initializeBenchmark() |
| { |
| const auto ¶ms = GetParam(); |
| |
| assert(params.vertexComponentCount > 1); |
| assert(params.iterations > 0); |
| mDrawIterations = params.iterations; |
| |
| const std::string vs = SHADER_SOURCE |
| ( |
| attribute vec2 vPosition; |
| uniform float uScale; |
| uniform float uOffset; |
| void main() |
| { |
| gl_Position = vec4(vPosition * vec2(uScale) - vec2(uOffset), 0, 1); |
| } |
| ); |
| |
| const std::string fs = SHADER_SOURCE |
| ( |
| precision mediump float; |
| void main() |
| { |
| gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); |
| } |
| ); |
| |
| mProgram = CompileProgram(vs, fs); |
| if (!mProgram) |
| { |
| return false; |
| } |
| |
| // Use the program object |
| glUseProgram(mProgram); |
| |
| 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 = GetVertexData(params.vertexType, |
| params.vertexComponentCount, |
| params.vertexNormalized, &data); |
| |
| mNumTris = 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()); |
| |
| GLfloat scale = 0.5f; |
| GLfloat offset = 0.5f; |
| |
| if (params.vertexNormalized == GL_TRUE) |
| { |
| scale = 2.0f; |
| offset = 0.5f; |
| } |
| |
| glUniform1f(glGetUniformLocation(mProgram, "uScale"), scale); |
| glUniform1f(glGetUniformLocation(mProgram, "uOffset"), offset); |
| |
| GLenum glErr = glGetError(); |
| if (glErr != GL_NO_ERROR) |
| { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| void BufferSubDataBenchmark::destroyBenchmark() |
| { |
| const auto ¶ms = GetParam(); |
| |
| // print static parameters |
| printResult("update_size", static_cast<size_t>(params.updateSize), "b", false); |
| printResult("buffer_size", static_cast<size_t>(params.bufferSize), "b", false); |
| printResult("iterations", static_cast<size_t>(params.iterations), "updates", false); |
| |
| glDeleteProgram(mProgram); |
| glDeleteBuffers(1, &mBuffer); |
| delete[] mUpdateData; |
| } |
| |
| void BufferSubDataBenchmark::beginDrawBenchmark() |
| { |
| // Clear the color buffer |
| glClear(GL_COLOR_BUFFER_BIT); |
| } |
| |
| void BufferSubDataBenchmark::drawBenchmark() |
| { |
| const auto ¶ms = GetParam(); |
| |
| for (unsigned int it = 0; it < params.iterations; it++) |
| { |
| if (params.updateSize > 0 && ((mNumFrames % params.updateRate) == 0)) |
| { |
| glBufferSubData(GL_ARRAY_BUFFER, 0, params.updateSize, mUpdateData); |
| } |
| |
| glDrawArrays(GL_TRIANGLES, 0, 3 * mNumTris); |
| } |
| } |
| |
| BufferSubDataParams BufferUpdateD3D11Params() |
| { |
| BufferSubDataParams params; |
| params.glesMajorVersion = 2; |
| params.widowWidth = 1280; |
| params.windowHeight = 720; |
| params.requestedRenderer = EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE; |
| params.deviceType = EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE; |
| params.vertexType = GL_FLOAT; |
| params.vertexComponentCount = 4; |
| params.vertexNormalized = GL_FALSE; |
| params.updateSize = 3000; |
| params.bufferSize = 67000000; |
| params.iterations = 10; |
| params.updateRate = 1; |
| return params; |
| } |
| |
| BufferSubDataParams BufferUpdateD3D9Params() |
| { |
| BufferSubDataParams params; |
| params.glesMajorVersion = 2; |
| params.widowWidth = 1280; |
| params.windowHeight = 720; |
| params.requestedRenderer = EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE; |
| params.deviceType = EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE; |
| params.vertexType = GL_FLOAT; |
| params.vertexComponentCount = 4; |
| params.vertexNormalized = GL_FALSE; |
| params.updateSize = 3000; |
| params.bufferSize = 67000000; |
| params.iterations = 10; |
| params.updateRate = 1; |
| return params; |
| } |
| |
| TEST_P(BufferSubDataBenchmark, Run) |
| { |
| run(); |
| } |
| |
| INSTANTIATE_TEST_CASE_P(BufferUpdates, |
| BufferSubDataBenchmark, |
| ::testing::Values(BufferUpdateD3D11Params(), BufferUpdateD3D9Params())); |
| |
| } // namespace |