// // Copyright 2020 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. // // GenerateMipmapBenchmark: // Performance test for generating texture mipmaps. // #include "ANGLEPerfTest.h" #include #include #include #include "test_utils/gl_raii.h" #include "util/shader_utils.h" using namespace angle; namespace { constexpr unsigned int kIterationsPerStep = 5; struct GenerateMipmapParams final : public RenderTestParams { GenerateMipmapParams() { iterationsPerStep = kIterationsPerStep; trackGpuTime = true; textureWidth = 1920; textureHeight = 1080; internalFormat = GL_RGBA; webgl = false; } std::string story() const override; GLsizei textureWidth; GLsizei textureHeight; GLenum internalFormat; bool webgl; }; std::ostream &operator<<(std::ostream &os, const GenerateMipmapParams ¶ms) { return os << params.backendAndStory().substr(1); } std::string GenerateMipmapParams::story() const { std::stringstream strstr; strstr << RenderTestParams::story(); if (webgl) { strstr << "_webgl"; } if (internalFormat == GL_RGB) { strstr << "_rgb"; } return strstr.str(); } template void FillWithRandomData(T *storage) { for (uint8_t &u : *storage) { u = rand() & 0xFF; } } class GenerateMipmapBenchmarkBase : public ANGLERenderTest, public ::testing::WithParamInterface { public: GenerateMipmapBenchmarkBase(const char *benchmarkName); void initializeBenchmark() override; void destroyBenchmark() override; protected: void initShaders(); GLuint mProgram = 0; GLuint mTexture = 0; std::vector mTextureData; }; class GenerateMipmapBenchmark : public GenerateMipmapBenchmarkBase { public: GenerateMipmapBenchmark() : GenerateMipmapBenchmarkBase("GenerateMipmap") {} void initializeBenchmark() override; void drawBenchmark() override; }; class GenerateMipmapWithRedefineBenchmark : public GenerateMipmapBenchmarkBase { public: GenerateMipmapWithRedefineBenchmark() : GenerateMipmapBenchmarkBase("GenerateMipmapWithRedefine") {} void drawBenchmark() override; }; GenerateMipmapBenchmarkBase::GenerateMipmapBenchmarkBase(const char *benchmarkName) : ANGLERenderTest(benchmarkName, GetParam()) { setWebGLCompatibilityEnabled(GetParam().webgl); setRobustResourceInit(GetParam().webgl); if (GetParam().getRenderer() == EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE) { skipTest("http://crbug.com/945415 Crashes on nvidia+d3d11"); } if (IsWindows7() && IsNVIDIA() && GetParam().eglParameters.renderer == EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE) { skipTest( "http://crbug.com/1096510 Fails on Windows7 NVIDIA Vulkan, presumably due to old " "drivers"); } } void GenerateMipmapBenchmarkBase::initializeBenchmark() { const auto ¶ms = GetParam(); initShaders(); glClearColor(0.0f, 0.0f, 0.0f, 0.0f); glViewport(0, 0, getWindow()->getWidth(), getWindow()->getHeight()); glGenTextures(1, &mTexture); glBindTexture(GL_TEXTURE_2D, mTexture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); mTextureData.resize(params.textureWidth * params.textureHeight * 4); FillWithRandomData(&mTextureData); glTexImage2D(GL_TEXTURE_2D, 0, params.internalFormat, params.textureWidth, params.textureHeight, 0, params.internalFormat, GL_UNSIGNED_BYTE, mTextureData.data()); // Perform a draw so the image data is flushed. glDrawArrays(GL_TRIANGLES, 0, 3); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); ASSERT_GL_NO_ERROR(); } void GenerateMipmapBenchmarkBase::initShaders() { constexpr char kVS[] = R"(void main() { gl_Position = vec4(0, 0, 0, 1); })"; constexpr char kFS[] = R"(precision mediump float; void main() { gl_FragColor = vec4(0); })"; mProgram = CompileProgram(kVS, kFS); ASSERT_NE(0u, mProgram); glUseProgram(mProgram); glDisable(GL_DEPTH_TEST); ASSERT_GL_NO_ERROR(); } void GenerateMipmapBenchmarkBase::destroyBenchmark() { glDeleteTextures(1, &mTexture); glDeleteProgram(mProgram); } void GenerateMipmapBenchmark::initializeBenchmark() { GenerateMipmapBenchmarkBase::initializeBenchmark(); // Generate mipmaps once so the texture doesn't need to be redefined. glGenerateMipmap(GL_TEXTURE_2D); // Perform a draw so the image data is flushed. glDrawArrays(GL_TRIANGLES, 0, 3); } void GenerateMipmapBenchmark::drawBenchmark() { const auto ¶ms = GetParam(); startGpuTimer(); for (unsigned int iteration = 0; iteration < params.iterationsPerStep; ++iteration) { // Slightly modify the base texture so the mipmap is definitely regenerated. std::array randomData; FillWithRandomData(&randomData); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, params.internalFormat, GL_UNSIGNED_BYTE, randomData.data()); // Generate mipmaps glGenerateMipmap(GL_TEXTURE_2D); // Perform a draw just so the texture data is flushed. With the position attributes not // set, a constant default value is used, resulting in a very cheap draw. glDrawArrays(GL_TRIANGLES, 0, 3); } stopGpuTimer(); ASSERT_GL_NO_ERROR(); } void GenerateMipmapWithRedefineBenchmark::drawBenchmark() { const auto ¶ms = GetParam(); // Create a new texture every time, so image redefinition happens every time. GLTexture texture; glBindTexture(GL_TEXTURE_2D, texture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexImage2D(GL_TEXTURE_2D, 0, params.internalFormat, params.textureWidth, params.textureHeight, 0, params.internalFormat, GL_UNSIGNED_BYTE, mTextureData.data()); // Perform a draw so the image data is flushed. glDrawArrays(GL_TRIANGLES, 0, 3); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); startGpuTimer(); // Do a single iteration, otherwise the cost of redefinition is amortized. ASSERT_EQ(params.iterationsPerStep, 1u); // Generate mipmaps glGenerateMipmap(GL_TEXTURE_2D); // Perform a draw just so the texture data is flushed. With the position attributes not // set, a constant default value is used, resulting in a very cheap draw. glDrawArrays(GL_TRIANGLES, 0, 3); stopGpuTimer(); ASSERT_GL_NO_ERROR(); } GenerateMipmapParams D3D11Params(bool webglCompat, bool singleIteration) { GenerateMipmapParams params; params.eglParameters = egl_platform::D3D11(); params.majorVersion = 3; params.minorVersion = 0; params.webgl = webglCompat; if (singleIteration) { params.iterationsPerStep = 1; } return params; } GenerateMipmapParams MetalParams(bool webglCompat, bool singleIteration) { GenerateMipmapParams params; params.eglParameters = egl_platform::METAL(); params.majorVersion = 3; params.minorVersion = 0; params.webgl = webglCompat; if (singleIteration) { params.iterationsPerStep = 1; } return params; } GenerateMipmapParams OpenGLOrGLESParams(bool webglCompat, bool singleIteration) { GenerateMipmapParams params; params.eglParameters = egl_platform::OPENGL_OR_GLES(); params.majorVersion = 3; params.minorVersion = 0; params.webgl = webglCompat; if (singleIteration) { params.iterationsPerStep = 1; } return params; } GenerateMipmapParams VulkanParams(bool webglCompat, bool singleIteration, bool emulatedFormat) { GenerateMipmapParams params; params.eglParameters = egl_platform::VULKAN(); params.majorVersion = 3; params.minorVersion = 0; params.webgl = webglCompat; if (emulatedFormat) { params.internalFormat = GL_RGB; } if (singleIteration) { params.iterationsPerStep = 1; } return params; } } // anonymous namespace TEST_P(GenerateMipmapBenchmark, Run) { run(); } TEST_P(GenerateMipmapWithRedefineBenchmark, Run) { run(); } using namespace params; ANGLE_INSTANTIATE_TEST(GenerateMipmapBenchmark, D3D11Params(false, false), D3D11Params(true, false), MetalParams(false, false), MetalParams(true, false), OpenGLOrGLESParams(false, false), OpenGLOrGLESParams(true, false), VulkanParams(false, false, false), VulkanParams(true, false, false), VulkanParams(false, false, true), VulkanParams(true, false, true)); ANGLE_INSTANTIATE_TEST(GenerateMipmapWithRedefineBenchmark, D3D11Params(false, true), D3D11Params(true, true), MetalParams(false, true), MetalParams(true, true), OpenGLOrGLESParams(false, true), OpenGLOrGLESParams(true, true), VulkanParams(false, true, false), VulkanParams(true, true, false), VulkanParams(false, true, true), VulkanParams(true, true, true));