// // Copyright (c) 2016 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. // // TexturesPerf: // Performance test for setting texture state. // #include "ANGLEPerfTest.h" #include #include #include #include "shader_utils.h" namespace angle { struct TexturesParams final : public RenderTestParams { TexturesParams() { // Common default params majorVersion = 2; minorVersion = 0; windowWidth = 720; windowHeight = 720; iterations = 256; numTextures = 8; textureRebindFrequency = 5; textureStateUpdateFrequency = 3; textureMipCount = 8; } std::string suffix() const override; size_t numTextures; size_t textureRebindFrequency; size_t textureStateUpdateFrequency; size_t textureMipCount; // static parameters size_t iterations; }; std::ostream &operator<<(std::ostream &os, const TexturesParams ¶ms) { os << params.suffix().substr(1); return os; } std::string TexturesParams::suffix() const { std::stringstream strstr; strstr << RenderTestParams::suffix(); strstr << "_" << numTextures << "_textures"; strstr << "_" << textureRebindFrequency << "_rebind"; strstr << "_" << textureStateUpdateFrequency << "_state"; strstr << "_" << textureMipCount << "_mips"; return strstr.str(); } class TexturesBenchmark : public ANGLERenderTest, public ::testing::WithParamInterface { public: TexturesBenchmark(); void initializeBenchmark() override; void destroyBenchmark() override; void drawBenchmark() override; private: void initShaders(); void initTextures(); std::vector mTextures; GLuint mProgram; std::vector mUniformLocations; }; TexturesBenchmark::TexturesBenchmark() : ANGLERenderTest("Textures", GetParam()), mProgram(0u) { } void TexturesBenchmark::initializeBenchmark() { const auto ¶ms = GetParam(); ASSERT_GT(params.iterations, 0u); // Verify the uniform counts are within the limits GLint maxTextureUnits; glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &maxTextureUnits); if (params.numTextures > static_cast(maxTextureUnits)) { FAIL() << "Texture count (" << params.numTextures << ")" << " exceeds maximum texture unit count: " << maxTextureUnits << std::endl; } initShaders(); initTextures(); glClearColor(0.0f, 0.0f, 0.0f, 0.0f); glViewport(0, 0, getWindow()->getWidth(), getWindow()->getHeight()); ASSERT_GL_NO_ERROR(); } std::string GetUniformLocationName(size_t idx, bool vertexShader) { std::stringstream strstr; strstr << (vertexShader ? "vs" : "fs") << "_u_" << idx; return strstr.str(); } void TexturesBenchmark::initShaders() { const auto ¶ms = GetParam(); std::string vs = "void main()\n" "{\n" " gl_Position = vec4(0, 0, 0, 0);\n" "}\n"; std::stringstream fstrstr; for (size_t i = 0; i < params.numTextures; i++) { fstrstr << "uniform sampler2D tex" << i << ";"; } fstrstr << "void main()\n" "{\n" " gl_FragColor = vec4(0, 0, 0, 0)"; for (size_t i = 0; i < params.numTextures; i++) { fstrstr << "+ texture2D(tex" << i << ", vec2(0, 0))"; } fstrstr << ";\n" "}\n"; mProgram = CompileProgram(vs, fstrstr.str()); ASSERT_NE(0u, mProgram); for (size_t i = 0; i < params.numTextures; ++i) { std::stringstream uniformName; uniformName << "tex" << i; GLint location = glGetUniformLocation(mProgram, uniformName.str().c_str()); ASSERT_NE(-1, location); mUniformLocations.push_back(location); } // Use the program object glUseProgram(mProgram); } void TexturesBenchmark::initTextures() { const auto ¶ms = GetParam(); size_t textureSize = static_cast(1) << params.textureMipCount; std::vector textureData(textureSize * textureSize * 4); for (auto &byte : textureData) { byte = rand() % 255u; } for (size_t texIndex = 0; texIndex < params.numTextures; texIndex++) { GLuint tex = 0; glGenTextures(1, &tex); glActiveTexture(static_cast(GL_TEXTURE0 + texIndex)); glBindTexture(GL_TEXTURE_2D, tex); for (size_t mip = 0; mip < params.textureMipCount; mip++) { GLsizei levelSize = static_cast(textureSize >> mip); glTexImage2D(GL_TEXTURE_2D, static_cast(mip), GL_RGBA, levelSize, levelSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, textureData.data()); } mTextures.push_back(tex); glUniform1i(mUniformLocations[texIndex], static_cast(texIndex)); } } void TexturesBenchmark::destroyBenchmark() { glDeleteProgram(mProgram); } void TexturesBenchmark::drawBenchmark() { const auto ¶ms = GetParam(); for (size_t it = 0; it < params.iterations; ++it) { if (it % params.textureRebindFrequency == 0) { // Swap two textures size_t swapTexture = (it / params.textureRebindFrequency) % (params.numTextures - 1); glActiveTexture(static_cast(GL_TEXTURE0 + swapTexture)); glBindTexture(GL_TEXTURE_2D, mTextures[swapTexture]); glActiveTexture(static_cast(GL_TEXTURE0 + swapTexture + 1)); glBindTexture(GL_TEXTURE_2D, mTextures[swapTexture + 1]); std::swap(mTextures[swapTexture], mTextures[swapTexture + 1]); } if (it % params.textureStateUpdateFrequency == 0) { // Update a texture's state size_t stateUpdateCount = it / params.textureStateUpdateFrequency; const size_t numUpdateTextures = 4; ASSERT_LE(numUpdateTextures, params.numTextures); size_t firstTexture = stateUpdateCount % (params.numTextures - numUpdateTextures); for (size_t updateTextureIdx = 0; updateTextureIdx < numUpdateTextures; updateTextureIdx++) { size_t updateTexture = firstTexture + updateTextureIdx; glActiveTexture(static_cast(GL_TEXTURE0 + updateTexture)); const GLenum minFilters[] = { GL_NEAREST, GL_LINEAR, GL_NEAREST_MIPMAP_NEAREST, GL_LINEAR_MIPMAP_NEAREST, GL_NEAREST_MIPMAP_LINEAR, GL_LINEAR_MIPMAP_LINEAR, }; glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilters[stateUpdateCount % ArraySize(minFilters)]); const GLenum magFilters[] = { GL_NEAREST, GL_LINEAR, }; glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilters[stateUpdateCount % ArraySize(magFilters)]); const GLenum wrapParameters[] = { GL_CLAMP_TO_EDGE, GL_REPEAT, GL_MIRRORED_REPEAT, }; glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapParameters[stateUpdateCount % ArraySize(wrapParameters)]); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapParameters[stateUpdateCount % ArraySize(wrapParameters)]); } } glDrawArrays(GL_TRIANGLES, 0, 3); } ASSERT_GL_NO_ERROR(); } TexturesParams D3D11Params() { TexturesParams params; params.eglParameters = egl_platform::D3D11_NULL(); return params; } TexturesParams D3D9Params() { TexturesParams params; params.eglParameters = egl_platform::D3D9_NULL(); return params; } TexturesParams OpenGLParams() { TexturesParams params; params.eglParameters = egl_platform::OPENGL_NULL(); return params; } TEST_P(TexturesBenchmark, Run) { run(); } ANGLE_INSTANTIATE_TEST(TexturesBenchmark, D3D11Params(), D3D9Params(), OpenGLParams()); } // namespace angle