| // | |
| // 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 "common/unsafe_buffers.h" | |
| #include <sstream> | |
| #include <vector> | |
| #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<MapBufferRangeParams> | |
| { | |
| public: | |
| MapBufferRangeBenchmark(); | |
| void initializeBenchmark() override; | |
| void destroyBenchmark() override; | |
| void drawBenchmark() override; | |
| private: | |
| GLuint mProgram; | |
| GLuint mBuffer; | |
| std::vector<uint8_t> 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 <class T> | |
| GLsizei GetNormalizedData(GLsizeiptr numElements, | |
| const GLfloat *floatData, | |
| std::vector<uint8_t> *data) | |
| { | |
| GLsizei triDataSize = sizeof(T) * numElements; | |
| data->resize(triDataSize); | |
| T *destPtr = ANGLE_UNSAFE_TODO(reinterpret_cast<T *>(data->data())); | |
| for (GLsizeiptr dataIndex = 0; dataIndex < numElements; dataIndex++) | |
| { | |
| GLfloat scaled = ANGLE_UNSAFE_TODO(floatData[dataIndex]) * 0.25f; | |
| ANGLE_UNSAFE_TODO(destPtr[dataIndex]) = | |
| static_cast<T>(scaled * static_cast<GLfloat>(std::numeric_limits<T>::max())); | |
| } | |
| return triDataSize; | |
| } | |
| template <class T> | |
| GLsizei GetIntData(GLsizeiptr numElements, const GLfloat *floatData, std::vector<uint8_t> *data) | |
| { | |
| GLsizei triDataSize = sizeof(T) * numElements; | |
| data->resize(triDataSize); | |
| T *destPtr = ANGLE_UNSAFE_TODO(reinterpret_cast<T *>(data->data())); | |
| for (GLsizeiptr dataIndex = 0; dataIndex < numElements; dataIndex++) | |
| { | |
| ANGLE_UNSAFE_TODO(destPtr[dataIndex]) = | |
| static_cast<T>(ANGLE_UNSAFE_TODO(floatData[dataIndex])); | |
| } | |
| return triDataSize; | |
| } | |
| GLsizei GetVertexData(GLenum type, | |
| GLint componentCount, | |
| GLboolean normalized, | |
| std::vector<uint8_t> *data) | |
| { | |
| GLsizei triDataSize = 0; | |
| const GLfloat *floatData = GetFloatData(componentCount); | |
| if (type == GL_FLOAT) | |
| { | |
| triDataSize = sizeof(GLfloat) * componentCount * 3; | |
| data->resize(triDataSize); | |
| ANGLE_UNSAFE_TODO(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: | |
| UNREACHABLE(); | |
| } | |
| } | |
| 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 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<int>(params.updateSize / mTriSize); | |
| int totalTris = static_cast<int>(params.updateSize / mTriSize); | |
| mVertexData.resize(params.bufferSize); | |
| for (int i = 1; i < totalTris; ++i) | |
| { | |
| ANGLE_UNSAFE_TODO(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); | |
| ANGLE_UNSAFE_TODO( | |
| 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 |