// // Copyright 2021 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. // // MapBufferRangeBenchmark:: // Performance test for ANGLE GLES mapped buffers. // #include "ANGLEPerfTest.h" #include #include #include "common/debug.h" #include "test_utils/draw_call_perf_utils.h" using namespace angle; namespace { constexpr unsigned int kIterationsPerStep = 10; struct MapBufferRangeParams final : public RenderTestParams { MapBufferRangeParams() { // Common default values majorVersion = 3; minorVersion = 0; windowWidth = 512; windowHeight = 512; // Test intentionally small update versus buffer size to begin with. updateSize = 32768; updateOffset = 0; bufferSize = 1048576; iterationsPerStep = kIterationsPerStep; access = GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_RANGE_BIT; } std::string story() const override; GLboolean vertexNormalized; GLenum vertexType; GLint vertexComponentCount; // static parameters GLsizeiptr updateSize; GLsizeiptr updateOffset; GLsizeiptr bufferSize; GLbitfield access; }; std::ostream &operator<<(std::ostream &os, const MapBufferRangeParams ¶ms) { os << params.backendAndStory().substr(1); return os; } class MapBufferRangeBenchmark : public ANGLERenderTest, public ::testing::WithParamInterface { public: MapBufferRangeBenchmark(); void initializeBenchmark() override; void destroyBenchmark() override; void drawBenchmark() override; private: GLuint mProgram; GLuint mBuffer; std::vector mVertexData; int mTriSize; int mNumUpdateTris; }; const 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: UNREACHABLE(); } return 0; } template GLsizei GetNormalizedData(GLsizeiptr numElements, const GLfloat *floatData, std::vector *data) { GLsizei 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 GLsizei GetIntData(GLsizeiptr numElements, const GLfloat *floatData, std::vector *data) { GLsizei 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; } GLsizei GetVertexData(GLenum type, GLint componentCount, GLboolean normalized, std::vector *data) { GLsizei triDataSize = 0; const 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: UNREACHABLE(); } } 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 MapBufferRangeParams::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: UNREACHABLE(); } strstr << vertexComponentCount; strstr << "_updateOffset" << updateOffset; strstr << "_updateSize" << updateSize; strstr << "_bufferSize" << bufferSize; strstr << "_access0x" << std::hex << access; return strstr.str(); } MapBufferRangeBenchmark::MapBufferRangeBenchmark() : ANGLERenderTest("MapBufferRange", GetParam()), mProgram(0), mBuffer(0), mTriSize(0), mNumUpdateTris(0) {} void MapBufferRangeBenchmark::initializeBenchmark() { const auto ¶ms = GetParam(); ASSERT_LT(1, params.vertexComponentCount); ASSERT_LE(params.updateSize, params.bufferSize); ASSERT_LT(params.updateOffset, params.bufferSize); ASSERT_LE(params.updateOffset + params.updateSize, params.bufferSize); 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); glGenBuffers(1, &mBuffer); glBindBuffer(GL_ARRAY_BUFFER, mBuffer); glBufferData(GL_ARRAY_BUFFER, params.bufferSize, nullptr, GL_DYNAMIC_DRAW); glVertexAttribPointer(0, params.vertexComponentCount, params.vertexType, params.vertexNormalized, 0, 0); glEnableVertexAttribArray(0); mTriSize = GetVertexData(params.vertexType, params.vertexComponentCount, params.vertexNormalized, &mVertexData); mNumUpdateTris = static_cast(params.updateSize / mTriSize); int totalTris = static_cast(params.updateSize / mTriSize); mVertexData.resize(params.bufferSize); for (int i = 1; i < totalTris; ++i) { memcpy(mVertexData.data() + i * mTriSize, mVertexData.data(), mTriSize); } if (params.updateSize == 0) { mNumUpdateTris = 1; glBufferSubData(GL_ARRAY_BUFFER, 0, mVertexData.size(), mVertexData.data()); } // Set the viewport glViewport(0, 0, getWindow()->getWidth(), getWindow()->getHeight()); ASSERT_GL_NO_ERROR(); } void MapBufferRangeBenchmark::destroyBenchmark() { glDeleteProgram(mProgram); glDeleteBuffers(1, &mBuffer); } void MapBufferRangeBenchmark::drawBenchmark() { glClear(GL_COLOR_BUFFER_BIT); const auto ¶ms = GetParam(); for (unsigned int it = 0; it < params.iterationsPerStep; it++) { if (params.updateSize > 0) { void *mapPtr = glMapBufferRange(GL_ARRAY_BUFFER, params.updateOffset, params.updateSize, params.access); memcpy(mapPtr, mVertexData.data() + params.updateOffset, params.updateSize); glUnmapBuffer(GL_ARRAY_BUFFER); } glDrawArrays(GL_TRIANGLES, params.updateOffset / mTriSize, 3 * mNumUpdateTris); } ASSERT_GL_NO_ERROR(); } MapBufferRangeParams BufferUpdateD3D11Params() { MapBufferRangeParams params; params.eglParameters = egl_platform::D3D11(); params.vertexType = GL_FLOAT; params.vertexComponentCount = 4; params.vertexNormalized = GL_FALSE; return params; } MapBufferRangeParams BufferUpdateMetalParams() { MapBufferRangeParams params; params.eglParameters = egl_platform::METAL(); params.vertexType = GL_FLOAT; params.vertexComponentCount = 4; params.vertexNormalized = GL_FALSE; return params; } MapBufferRangeParams BufferUpdateMetalParamsLargeUpdate() { MapBufferRangeParams params; params.eglParameters = egl_platform::METAL(); params.vertexType = GL_FLOAT; params.vertexComponentCount = 4; params.vertexNormalized = GL_FALSE; params.updateSize = 524288; return params; } MapBufferRangeParams BufferUpdateOpenGLOrGLESParams() { MapBufferRangeParams params; params.eglParameters = egl_platform::OPENGL_OR_GLES(); params.vertexType = GL_FLOAT; params.vertexComponentCount = 4; params.vertexNormalized = GL_FALSE; return params; } MapBufferRangeParams BufferUpdateVulkanParams() { MapBufferRangeParams params; params.eglParameters = egl_platform::VULKAN(); params.vertexType = GL_FLOAT; params.vertexComponentCount = 4; params.vertexNormalized = GL_FALSE; return params; } MapBufferRangeParams BufferUpdateVulkanParamsMidBuffer() { MapBufferRangeParams params; params.eglParameters = egl_platform::VULKAN(); params.vertexType = GL_FLOAT; params.vertexComponentCount = 4; params.vertexNormalized = GL_FALSE; params.updateOffset = 524288; return params; } MapBufferRangeParams BufferUpdateVulkanParamsLargeUpdate() { MapBufferRangeParams params; params.eglParameters = egl_platform::VULKAN(); params.vertexType = GL_FLOAT; params.vertexComponentCount = 4; params.vertexNormalized = GL_FALSE; params.updateSize = 524288; return params; } MapBufferRangeParams BufferUpdateVulkanParamsFullBuffer() { MapBufferRangeParams params; params.eglParameters = egl_platform::VULKAN(); params.vertexType = GL_FLOAT; params.vertexComponentCount = 4; params.vertexNormalized = GL_FALSE; params.updateSize = 1048576; return params; } MapBufferRangeParams BufferUpdateVulkanParamsTinyUpdate() { MapBufferRangeParams params; params.eglParameters = egl_platform::VULKAN(); params.vertexType = GL_FLOAT; params.vertexComponentCount = 4; params.vertexNormalized = GL_FALSE; params.updateSize = 128; return params; } MapBufferRangeParams BufferUpdateVulkanParamsNonPowerOf2() { MapBufferRangeParams params; params.eglParameters = egl_platform::VULKAN(); params.vertexType = GL_FLOAT; params.vertexComponentCount = 4; params.vertexNormalized = GL_FALSE; params.updateSize = 32000; params.bufferSize = 800000; return params; } MapBufferRangeParams BufferUpdateVulkanParamsUnsynchronized() { MapBufferRangeParams params; params.eglParameters = egl_platform::VULKAN(); params.vertexType = GL_FLOAT; params.vertexComponentCount = 4; params.vertexNormalized = GL_FALSE; params.access = GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT; return params; } MapBufferRangeParams BufferUpdateVulkanParamsLargeUpdateUnsynchronized() { MapBufferRangeParams params; params.eglParameters = egl_platform::VULKAN(); params.vertexType = GL_FLOAT; params.vertexComponentCount = 4; params.vertexNormalized = GL_FALSE; params.updateSize = 524288; params.access = GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT; return params; } TEST_P(MapBufferRangeBenchmark, Run) { run(); } ANGLE_INSTANTIATE_TEST(MapBufferRangeBenchmark, BufferUpdateD3D11Params(), BufferUpdateMetalParams(), BufferUpdateMetalParamsLargeUpdate(), BufferUpdateOpenGLOrGLESParams(), BufferUpdateVulkanParams(), BufferUpdateVulkanParamsMidBuffer(), BufferUpdateVulkanParamsLargeUpdate(), BufferUpdateVulkanParamsFullBuffer(), BufferUpdateVulkanParamsTinyUpdate(), BufferUpdateVulkanParamsNonPowerOf2(), BufferUpdateVulkanParamsUnsynchronized(), BufferUpdateVulkanParamsLargeUpdateUnsynchronized()); } // namespace