// // Copyright 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 "util/shader_utils.h" namespace angle { constexpr unsigned int kIterationsPerStep = 256; struct TexturesParams final : public RenderTestParams { TexturesParams() { iterationsPerStep = kIterationsPerStep; // Common default params majorVersion = 2; minorVersion = 0; windowWidth = 720; windowHeight = 720; numTextures = 8; textureRebindFrequency = 5; textureStateUpdateFrequency = 3; textureMipCount = 8; webgl = false; } std::string story() const override; size_t numTextures; size_t textureRebindFrequency; size_t textureStateUpdateFrequency; size_t textureMipCount; bool webgl; }; std::ostream &operator<<(std::ostream &os, const TexturesParams ¶ms) { os << params.backendAndStory().substr(1); return os; } std::string TexturesParams::story() const { std::stringstream strstr; strstr << RenderTestParams::story(); strstr << "_" << numTextures << "_textures"; strstr << "_" << textureRebindFrequency << "_rebind"; strstr << "_" << textureStateUpdateFrequency << "_state"; strstr << "_" << textureMipCount << "_mips"; if (webgl) { strstr << "_webgl"; } 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) { setWebGLCompatibilityEnabled(GetParam().webgl); setRobustResourceInit(GetParam().webgl); } void TexturesBenchmark::initializeBenchmark() { const auto ¶ms = GetParam(); // 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.c_str(), fstrstr.str().c_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.iterationsPerStep; ++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(bool webglCompat, bool frequentUpdate) { TexturesParams params; params.eglParameters = egl_platform::D3D11_NULL(); params.webgl = webglCompat; if (frequentUpdate) { params.textureRebindFrequency = 1; params.textureStateUpdateFrequency = 1; } return params; } TexturesParams OpenGLOrGLESParams(bool webglCompat, bool frequentUpdate) { TexturesParams params; params.eglParameters = egl_platform::OPENGL_OR_GLES_NULL(); params.webgl = webglCompat; if (frequentUpdate) { params.textureRebindFrequency = 1; params.textureStateUpdateFrequency = 1; } return params; } TexturesParams VulkanParams(bool webglCompat, bool frequentUpdate) { TexturesParams params; params.eglParameters = egl_platform::VULKAN_NULL(); params.webgl = webglCompat; if (frequentUpdate) { params.textureRebindFrequency = 1; params.textureStateUpdateFrequency = 1; } return params; } TEST_P(TexturesBenchmark, Run) { run(); } ANGLE_INSTANTIATE_TEST(TexturesBenchmark, D3D11Params(false, false), D3D11Params(true, false), D3D11Params(false, true), D3D11Params(true, true), OpenGLOrGLESParams(false, false), OpenGLOrGLESParams(true, false), OpenGLOrGLESParams(false, true), OpenGLOrGLESParams(true, true), VulkanParams(false, false), VulkanParams(true, false), VulkanParams(false, true), VulkanParams(true, true)); } // namespace angle