// // 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 #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 { 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 mIntIndexData; std::vector 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::max(), mCount); mShortIndexData.push_back(static_cast(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.surfaceType == SurfaceType::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(mIntIndexData.data()) : static_cast(mShortIndexData.data()); for (unsigned int it = 0; it < params.iterationsPerStep; it++) { glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, mBufferSize, bufferData); glDrawElements(GL_TRIANGLES, static_cast(mCount), params.type, 0); } } else { for (unsigned int it = 0; it < params.iterationsPerStep; it++) { glDrawElements(GL_TRIANGLES, static_cast(mCount), params.type, 0); } } ASSERT_GL_NO_ERROR(); } TEST_P(DrawElementsPerfBenchmark, Run) { run(); } using namespace angle; using namespace params; using P = DrawElementsPerfParams; P CombineIndexType(const P &in, GLenum indexType) { P out = in; out.type = indexType; return out; } P CombineIndexBufferChanged(const P &in, bool indexBufferChanged) { P out = in; out.indexBufferChanged = indexBufferChanged; // Scale down iterations for slower tests. if (indexBufferChanged) out.iterationsPerStep /= 100; return out; } std::vector gIndexTypes = {GL_UNSIGNED_INT, GL_UNSIGNED_SHORT}; std::vector

gWithIndexType = CombineWithValues({P()}, gIndexTypes, CombineIndexType); std::vector

gWithRenderer = CombineWithFuncs(gWithIndexType, {D3D11

, GL

, Metal

, Vulkan

, WGL

}); std::vector

gWithChange = CombineWithValues(gWithRenderer, {false, true}, CombineIndexBufferChanged); std::vector

gWithDevice = CombineWithFuncs(gWithChange, {Passthrough

, NullDevice

}); ANGLE_INSTANTIATE_TEST_ARRAY(DrawElementsPerfBenchmark, gWithDevice); } // anonymous namespace