// // Copyright 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 #include "ANGLEPerfTest.h" #include "test_utils/draw_call_perf_utils.h" using namespace angle; namespace { constexpr unsigned int kIterationsPerStep = 4; struct BufferSubDataParams final : public RenderTestParams { BufferSubDataParams() { // Common default values majorVersion = 2; minorVersion = 0; windowWidth = 512; windowHeight = 512; updateSize = 32000; bufferSize = 40000; iterationsPerStep = kIterationsPerStep; updateRate = 1; } std::string story() const override; GLboolean vertexNormalized; GLenum vertexType; GLint vertexComponentCount; unsigned int updateRate; // static parameters GLsizeiptr updateSize; GLsizeiptr bufferSize; }; std::ostream &operator<<(std::ostream &os, const BufferSubDataParams ¶ms) { os << params.backendAndStory().substr(1); return os; } class BufferSubDataBenchmark : public ANGLERenderTest, public ::testing::WithParamInterface { 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 GLsizeiptr GetNormalizedData(GLsizeiptr numElements, GLfloat *floatData, std::vector *data) { GLsizeiptr triDataSize = sizeof(T) * numElements; data->resize(triDataSize); T *destPtr = reinterpret_cast(data->data()); for (GLsizeiptr dataIndex = 0; dataIndex < numElements; dataIndex++) { GLfloat scaled = floatData[dataIndex] * 0.25f; destPtr[dataIndex] = static_cast(scaled * static_cast(std::numeric_limits::max())); } return triDataSize; } template GLsizeiptr GetIntData(GLsizeiptr numElements, GLfloat *floatData, std::vector *data) { GLsizeiptr triDataSize = sizeof(T) * numElements; data->resize(triDataSize); T *destPtr = reinterpret_cast(data->data()); for (GLsizeiptr dataIndex = 0; dataIndex < numElements; dataIndex++) { destPtr[dataIndex] = static_cast(floatData[dataIndex]); } return triDataSize; } GLsizeiptr GetVertexData(GLenum type, GLint componentCount, GLboolean normalized, std::vector *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(numElements, floatData, data); break; case GL_SHORT: triDataSize = GetNormalizedData(numElements, floatData, data); break; case GL_INT: triDataSize = GetNormalizedData(numElements, floatData, data); break; case GL_UNSIGNED_BYTE: triDataSize = GetNormalizedData(numElements, floatData, data); break; case GL_UNSIGNED_SHORT: triDataSize = GetNormalizedData(numElements, floatData, data); break; case GL_UNSIGNED_INT: triDataSize = GetNormalizedData(numElements, floatData, data); break; default: assert(0); } } else { GLsizeiptr numElements = componentCount * 3; switch (type) { case GL_BYTE: triDataSize = GetIntData(numElements, floatData, data); break; case GL_SHORT: triDataSize = GetIntData(numElements, floatData, data); break; case GL_INT: triDataSize = GetIntData(numElements, floatData, data); break; case GL_UNSIGNED_BYTE: triDataSize = GetIntData(numElements, floatData, data); break; case GL_UNSIGNED_SHORT: triDataSize = GetIntData(numElements, floatData, data); break; case GL_UNSIGNED_INT: triDataSize = GetIntData(numElements, floatData, data); break; default: assert(0); } } return triDataSize; } std::string BufferSubDataParams::story() const { std::stringstream strstr; strstr << RenderTestParams::story(); 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); 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 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 data; GLsizei triDataSize = static_cast(GetVertexData( params.vertexType, params.vertexComponentCount, params.vertexNormalized, &data)); mNumTris = static_cast(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.iterationsPerStep; 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 BufferUpdateMetalParams() { BufferSubDataParams params; params.eglParameters = egl_platform::METAL(); params.vertexType = GL_FLOAT; params.vertexComponentCount = 4; params.vertexNormalized = GL_FALSE; return params; } BufferSubDataParams BufferUpdateOpenGLOrGLESParams() { BufferSubDataParams params; params.eglParameters = egl_platform::OPENGL_OR_GLES(); params.vertexType = GL_FLOAT; params.vertexComponentCount = 4; params.vertexNormalized = GL_FALSE; return params; } BufferSubDataParams BufferUpdateVulkanParams() { BufferSubDataParams params; params.eglParameters = egl_platform::VULKAN(); params.vertexType = GL_FLOAT; params.vertexComponentCount = 4; params.vertexNormalized = GL_FALSE; return params; } TEST_P(BufferSubDataBenchmark, Run) { run(); } ANGLE_INSTANTIATE_TEST(BufferSubDataBenchmark, BufferUpdateD3D11Params(), BufferUpdateMetalParams(), BufferUpdateOpenGLOrGLESParams(), BufferUpdateVulkanParams()); } // namespace