| // |
| // Copyright 2017 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. |
| // |
| // DrawElementsPerf: |
| // Performance tests for ANGLE DrawElements call overhead. |
| // |
| |
| #include <sstream> |
| |
| #include "ANGLEPerfTest.h" |
| #include "DrawCallPerfParams.h" |
| #include "test_utils/draw_call_perf_utils.h" |
| |
| namespace |
| { |
| |
| GLuint CreateElementArrayBuffer(size_t count, GLenum type, GLenum usage) |
| { |
| GLuint buffer = 0u; |
| glGenBuffers(1, &buffer); |
| glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer); |
| glBufferData(GL_ELEMENT_ARRAY_BUFFER, count * sizeof(type), nullptr, usage); |
| |
| return buffer; |
| } |
| |
| struct DrawElementsPerfParams final : public DrawCallPerfParams |
| { |
| // Common default options |
| DrawElementsPerfParams() |
| { |
| runTimeSeconds = 5.0; |
| numTris = 2; |
| } |
| |
| std::string story() const override |
| { |
| std::stringstream strstr; |
| |
| strstr << DrawCallPerfParams::story(); |
| |
| if (indexBufferChanged) |
| { |
| strstr << "_index_buffer_changed"; |
| } |
| |
| if (type == GL_UNSIGNED_SHORT) |
| { |
| strstr << "_ushort"; |
| } |
| |
| return strstr.str(); |
| } |
| |
| GLenum type = GL_UNSIGNED_INT; |
| bool indexBufferChanged = false; |
| }; |
| |
| std::ostream &operator<<(std::ostream &os, const DrawElementsPerfParams ¶ms) |
| { |
| os << params.backendAndStory().substr(1); |
| return os; |
| } |
| |
| class DrawElementsPerfBenchmark : public ANGLERenderTest, |
| public ::testing::WithParamInterface<DrawElementsPerfParams> |
| { |
| public: |
| DrawElementsPerfBenchmark(); |
| |
| void initializeBenchmark() override; |
| void destroyBenchmark() override; |
| void drawBenchmark() override; |
| |
| private: |
| GLuint mProgram = 0; |
| GLuint mBuffer = 0; |
| GLuint mIndexBuffer = 0; |
| GLuint mFBO = 0; |
| GLuint mTexture = 0; |
| GLsizei mBufferSize = 0; |
| int mCount = 3 * GetParam().numTris; |
| std::vector<GLuint> mIntIndexData; |
| std::vector<GLushort> mShortIndexData; |
| }; |
| |
| DrawElementsPerfBenchmark::DrawElementsPerfBenchmark() |
| : ANGLERenderTest("DrawElementsPerf", GetParam()) |
| { |
| if (GetParam().type == GL_UNSIGNED_INT) |
| { |
| addExtensionPrerequisite("GL_OES_element_index_uint"); |
| } |
| } |
| |
| GLsizei ElementTypeSize(GLenum elementType) |
| { |
| switch (elementType) |
| { |
| case GL_UNSIGNED_BYTE: |
| return sizeof(GLubyte); |
| case GL_UNSIGNED_SHORT: |
| return sizeof(GLushort); |
| case GL_UNSIGNED_INT: |
| return sizeof(GLuint); |
| default: |
| return 0; |
| } |
| } |
| |
| void DrawElementsPerfBenchmark::initializeBenchmark() |
| { |
| const auto ¶ms = GetParam(); |
| |
| mProgram = SetupSimpleDrawProgram(); |
| ASSERT_NE(0u, mProgram); |
| |
| glClearColor(0.0f, 0.0f, 0.0f, 0.0f); |
| |
| mBuffer = Create2DTriangleBuffer(params.numTris, GL_STATIC_DRAW); |
| mIndexBuffer = CreateElementArrayBuffer(mCount, params.type, GL_STATIC_DRAW); |
| |
| for (int i = 0; i < mCount; i++) |
| { |
| ASSERT_GE(std::numeric_limits<GLushort>::max(), mCount); |
| mShortIndexData.push_back(static_cast<GLushort>(rand() % mCount)); |
| mIntIndexData.push_back(rand() % mCount); |
| } |
| |
| glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer); |
| |
| mBufferSize = ElementTypeSize(params.type) * mCount; |
| |
| if (params.type == GL_UNSIGNED_INT) |
| { |
| glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, mBufferSize, mIntIndexData.data()); |
| } |
| else |
| { |
| glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, mBufferSize, mShortIndexData.data()); |
| } |
| |
| glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0); |
| glEnableVertexAttribArray(0); |
| |
| // Set the viewport |
| glViewport(0, 0, getWindow()->getWidth(), getWindow()->getHeight()); |
| |
| if (params.offscreen) |
| { |
| CreateColorFBO(getWindow()->getWidth(), getWindow()->getHeight(), &mTexture, &mFBO); |
| } |
| |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| void DrawElementsPerfBenchmark::destroyBenchmark() |
| { |
| glDeleteProgram(mProgram); |
| glDeleteBuffers(1, &mBuffer); |
| glDeleteBuffers(1, &mIndexBuffer); |
| glDeleteTextures(1, &mTexture); |
| glDeleteFramebuffers(1, &mFBO); |
| } |
| |
| void DrawElementsPerfBenchmark::drawBenchmark() |
| { |
| // This workaround fixes a huge queue of graphics commands accumulating on the GL |
| // back-end. The GL back-end doesn't have a proper NULL device at the moment. |
| // TODO(jmadill): Remove this when/if we ever get a proper OpenGL NULL device. |
| const auto &eglParams = GetParam().eglParameters; |
| if (eglParams.deviceType != EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE || |
| (eglParams.renderer != EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE && |
| eglParams.renderer != EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE)) |
| { |
| glClear(GL_COLOR_BUFFER_BIT); |
| } |
| |
| const DrawElementsPerfParams ¶ms = GetParam(); |
| |
| if (params.indexBufferChanged) |
| { |
| const void *bufferData = (params.type == GL_UNSIGNED_INT) |
| ? static_cast<GLvoid *>(mIntIndexData.data()) |
| : static_cast<GLvoid *>(mShortIndexData.data()); |
| for (unsigned int it = 0; it < params.iterationsPerStep; it++) |
| { |
| glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, mBufferSize, bufferData); |
| glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(mCount), params.type, 0); |
| } |
| } |
| else |
| { |
| for (unsigned int it = 0; it < params.iterationsPerStep; it++) |
| { |
| glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(mCount), params.type, 0); |
| } |
| } |
| |
| ASSERT_GL_NO_ERROR(); |
| } |
| |
| DrawElementsPerfParams DrawElementsPerfD3D11Params(bool indexBufferChanged, |
| bool useNullDevice, |
| GLenum indexType) |
| { |
| DrawElementsPerfParams params; |
| params.eglParameters = |
| useNullDevice ? angle::egl_platform::D3D11_NULL() : angle::egl_platform::D3D11(); |
| params.indexBufferChanged = indexBufferChanged; |
| params.type = indexType; |
| |
| // Scale down iterations for slower tests. |
| if (indexBufferChanged) |
| params.iterationsPerStep /= 100; |
| |
| return params; |
| } |
| |
| DrawElementsPerfParams DrawElementsPerfD3D9Params(bool indexBufferChanged) |
| { |
| DrawElementsPerfParams params; |
| params.eglParameters = angle::egl_platform::D3D9(); |
| params.indexBufferChanged = indexBufferChanged; |
| |
| // Scale down iterations for slower tests. |
| if (indexBufferChanged) |
| params.iterationsPerStep /= 100; |
| |
| return params; |
| } |
| |
| DrawElementsPerfParams DrawElementsPerfOpenGLOrGLESParams(bool indexBufferChanged, |
| bool useNullDevice, |
| GLenum indexType) |
| { |
| DrawElementsPerfParams params; |
| params.eglParameters = useNullDevice ? angle::egl_platform::OPENGL_OR_GLES_NULL() |
| : angle::egl_platform::OPENGL_OR_GLES(); |
| params.indexBufferChanged = indexBufferChanged; |
| params.type = indexType; |
| |
| // Scale down iterations for slower tests. |
| if (indexBufferChanged) |
| params.iterationsPerStep /= 100; |
| |
| return params; |
| } |
| |
| DrawElementsPerfParams DrawElementsPerfVulkanParams(bool indexBufferChanged, |
| bool useNullDevice, |
| GLenum indexType) |
| { |
| DrawElementsPerfParams params; |
| params.eglParameters = |
| useNullDevice ? angle::egl_platform::VULKAN_NULL() : angle::egl_platform::VULKAN(); |
| params.indexBufferChanged = indexBufferChanged; |
| params.type = indexType; |
| |
| // Scale down iterations for slower tests. |
| if (indexBufferChanged) |
| params.iterationsPerStep /= 100; |
| |
| return params; |
| } |
| |
| DrawElementsPerfParams DrawElementsPerfWGLParams(bool indexBufferChanged, GLenum indexType) |
| { |
| DrawElementsPerfParams params; |
| params.driver = angle::GLESDriverType::SystemWGL; |
| |
| params.indexBufferChanged = indexBufferChanged; |
| params.type = indexType; |
| |
| // Scale down iterations for slower tests. |
| if (indexBufferChanged) |
| params.iterationsPerStep /= 100; |
| |
| return params; |
| } |
| |
| TEST_P(DrawElementsPerfBenchmark, Run) |
| { |
| run(); |
| } |
| |
| ANGLE_INSTANTIATE_TEST(DrawElementsPerfBenchmark, |
| DrawElementsPerfD3D9Params(false), |
| DrawElementsPerfD3D9Params(true), |
| DrawElementsPerfD3D11Params(false, true, GL_UNSIGNED_INT), |
| DrawElementsPerfD3D11Params(true, true, GL_UNSIGNED_INT), |
| DrawElementsPerfD3D11Params(false, false, GL_UNSIGNED_INT), |
| DrawElementsPerfD3D11Params(true, false, GL_UNSIGNED_INT), |
| DrawElementsPerfD3D11Params(false, false, GL_UNSIGNED_SHORT), |
| DrawElementsPerfD3D11Params(false, true, GL_UNSIGNED_SHORT), |
| DrawElementsPerfOpenGLOrGLESParams(false, false, GL_UNSIGNED_SHORT), |
| DrawElementsPerfOpenGLOrGLESParams(false, true, GL_UNSIGNED_SHORT), |
| DrawElementsPerfOpenGLOrGLESParams(true, false, GL_UNSIGNED_SHORT), |
| DrawElementsPerfOpenGLOrGLESParams(false, false, GL_UNSIGNED_INT), |
| DrawElementsPerfOpenGLOrGLESParams(true, false, GL_UNSIGNED_INT), |
| DrawElementsPerfVulkanParams(false, false, GL_UNSIGNED_SHORT), |
| DrawElementsPerfVulkanParams(false, true, GL_UNSIGNED_SHORT), |
| DrawElementsPerfVulkanParams(false, false, GL_UNSIGNED_INT), |
| DrawElementsPerfVulkanParams(false, true, GL_UNSIGNED_INT), |
| DrawElementsPerfWGLParams(false, GL_UNSIGNED_SHORT)); |
| |
| } // anonymous namespace |