Add perf test for texture upload gpu time
This perf test measures the GPU time spent uploading texture data.
Bug: angleproject:2361
Change-Id: I62fcf0e893fdcc9826a083e23320051a69559fab
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1529922
Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
diff --git a/src/tests/angle_perftests.gni b/src/tests/angle_perftests.gni
index 547446c..c63b02c 100644
--- a/src/tests/angle_perftests.gni
+++ b/src/tests/angle_perftests.gni
@@ -24,8 +24,8 @@
"perf_tests/LinkProgramPerfTest.cpp",
"perf_tests/MultiviewPerf.cpp",
"perf_tests/PointSprites.cpp",
- "perf_tests/TexSubImage.cpp",
"perf_tests/TextureSampling.cpp",
+ "perf_tests/TextureUploadPerf.cpp",
"perf_tests/TexturesPerf.cpp",
"perf_tests/UniformsPerf.cpp",
"perf_tests/VulkanBarriersPerf.cpp",
diff --git a/src/tests/perf_tests/TexSubImage.cpp b/src/tests/perf_tests/TexSubImage.cpp
deleted file mode 100644
index 427b699..0000000
--- a/src/tests/perf_tests/TexSubImage.cpp
+++ /dev/null
@@ -1,300 +0,0 @@
-//
-// Copyright (c) 2014 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.
-//
-// TexSubImageBenchmark:
-// Performace test for ANGLE texture updates.
-//
-
-#include <sstream>
-
-#include "ANGLEPerfTest.h"
-#include "util/shader_utils.h"
-
-using namespace angle;
-
-namespace
-{
-constexpr unsigned int kIterationsPerStep = 9;
-
-struct TexSubImageParams final : public RenderTestParams
-{
- TexSubImageParams()
- {
- iterationsPerStep = kIterationsPerStep;
-
- // Common default parameters
- majorVersion = 2;
- minorVersion = 0;
- windowWidth = 512;
- windowHeight = 512;
-
- imageWidth = 1024;
- imageHeight = 1024;
- subImageWidth = 64;
- subImageHeight = 64;
- }
-
- std::string suffix() const override;
-
- // Static parameters
- int imageWidth;
- int imageHeight;
- int subImageWidth;
- int subImageHeight;
-};
-
-std::ostream &operator<<(std::ostream &os, const TexSubImageParams ¶ms)
-{
- os << params.suffix().substr(1);
- return os;
-}
-
-class TexSubImageBenchmark : public ANGLERenderTest,
- public ::testing::WithParamInterface<TexSubImageParams>
-{
- public:
- TexSubImageBenchmark();
-
- void initializeBenchmark() override;
- void destroyBenchmark() override;
- void drawBenchmark() override;
-
- private:
- GLuint createTexture();
-
- // Handle to a program object
- GLuint mProgram;
-
- // Attribute locations
- GLint mPositionLoc;
- GLint mTexCoordLoc;
-
- // Sampler location
- GLint mSamplerLoc;
-
- // Texture handle
- GLuint mTexture;
-
- // Buffer handle
- GLuint mVertexBuffer;
- GLuint mIndexBuffer;
-
- GLubyte *mPixels;
-};
-
-std::string TexSubImageParams::suffix() const
-{
- // TODO(jmadill)
- return RenderTestParams::suffix();
-}
-
-TexSubImageBenchmark::TexSubImageBenchmark()
- : ANGLERenderTest("TexSubImage", GetParam()),
- mProgram(0),
- mPositionLoc(-1),
- mTexCoordLoc(-1),
- mSamplerLoc(-1),
- mTexture(0),
- mVertexBuffer(0),
- mIndexBuffer(0),
- mPixels(nullptr)
-{
- addExtensionPrerequisite("GL_EXT_texture_storage");
-}
-
-GLuint TexSubImageBenchmark::createTexture()
-{
- const auto ¶ms = GetParam();
-
- // Use tightly packed data
- glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
-
- // Generate a texture object
- GLuint texture;
- glGenTextures(1, &texture);
-
- // Bind the texture object
- glBindTexture(GL_TEXTURE_2D, texture);
-
- glTexStorage2DEXT(GL_TEXTURE_2D, 1, GL_RGBA8, params.imageWidth, params.imageHeight);
-
- // Set the filtering mode
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-
- return texture;
-}
-
-void TexSubImageBenchmark::initializeBenchmark()
-{
- const auto ¶ms = GetParam();
-
- constexpr char kVS[] = R"(attribute vec4 a_position;
-attribute vec2 a_texCoord;
-varying vec2 v_texCoord;
-void main()
-{
- gl_Position = a_position;
- v_texCoord = a_texCoord;
-})";
-
- constexpr char kFS[] = R"(precision mediump float;
-varying vec2 v_texCoord;
-uniform sampler2D s_texture;
-void main()
-{
- gl_FragColor = texture2D(s_texture, v_texCoord);
-})";
-
- mProgram = CompileProgram(kVS, kFS);
- ASSERT_NE(0u, mProgram);
-
- // Get the attribute locations
- mPositionLoc = glGetAttribLocation(mProgram, "a_position");
- mTexCoordLoc = glGetAttribLocation(mProgram, "a_texCoord");
-
- // Get the sampler location
- mSamplerLoc = glGetUniformLocation(mProgram, "s_texture");
-
- // Build the vertex buffer
- GLfloat vertices[] = {
- -0.5f, 0.5f, 0.0f, // Position 0
- 0.0f, 0.0f, // TexCoord 0
- -0.5f, -0.5f, 0.0f, // Position 1
- 0.0f, 1.0f, // TexCoord 1
- 0.5f, -0.5f, 0.0f, // Position 2
- 1.0f, 1.0f, // TexCoord 2
- 0.5f, 0.5f, 0.0f, // Position 3
- 1.0f, 0.0f // TexCoord 3
- };
-
- glGenBuffers(1, &mVertexBuffer);
- glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer);
- glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
-
- GLushort indices[] = {0, 1, 2, 0, 2, 3};
- glGenBuffers(1, &mIndexBuffer);
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer);
- glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
-
- // Load the texture
- mTexture = createTexture();
-
- glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
-
- mPixels = new GLubyte[params.subImageWidth * params.subImageHeight * 4];
-
- // Fill the pixels structure with random data:
- for (int y = 0; y < params.subImageHeight; ++y)
- {
- for (int x = 0; x < params.subImageWidth; ++x)
- {
- int offset = (x + (y * params.subImageWidth)) * 4;
- mPixels[offset + 0] = rand() % 255; // Red
- mPixels[offset + 1] = rand() % 255; // Green
- mPixels[offset + 2] = rand() % 255; // Blue
- mPixels[offset + 3] = 255; // Alpha
- }
- }
-
- ASSERT_GL_NO_ERROR();
-}
-
-void TexSubImageBenchmark::destroyBenchmark()
-{
- glDeleteProgram(mProgram);
- glDeleteBuffers(1, &mVertexBuffer);
- glDeleteBuffers(1, &mIndexBuffer);
- glDeleteTextures(1, &mTexture);
- delete[] mPixels;
-}
-
-void TexSubImageBenchmark::drawBenchmark()
-{
- // Set the viewport
- glViewport(0, 0, getWindow()->getWidth(), getWindow()->getHeight());
-
- // Clear the color buffer
- glClear(GL_COLOR_BUFFER_BIT);
-
- // Use the program object
- glUseProgram(mProgram);
-
- // Bind the buffers
- glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer);
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer);
-
- // Load the vertex position
- glVertexAttribPointer(mPositionLoc, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), 0);
- // Load the texture coordinate
- glVertexAttribPointer(mTexCoordLoc, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat),
- reinterpret_cast<void *>(3 * sizeof(GLfloat)));
-
- glEnableVertexAttribArray(mPositionLoc);
- glEnableVertexAttribArray(mTexCoordLoc);
-
- // Bind the texture
- glActiveTexture(GL_TEXTURE0);
- glBindTexture(GL_TEXTURE_2D, mTexture);
-
- // Set the texture sampler to texture unit to 0
- glUniform1i(mSamplerLoc, 0);
-
- ASSERT_GL_NO_ERROR();
-
- const auto ¶ms = GetParam();
-
- for (unsigned int iteration = 0; iteration < params.iterationsPerStep; ++iteration)
- {
- glTexSubImage2D(GL_TEXTURE_2D, 0, rand() % (params.imageWidth - params.subImageWidth),
- rand() % (params.imageHeight - params.subImageHeight), params.subImageWidth,
- params.subImageHeight, GL_RGBA, GL_UNSIGNED_BYTE, mPixels);
-
- glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0);
- }
-
- ASSERT_GL_NO_ERROR();
-}
-
-TexSubImageParams D3D11Params()
-{
- TexSubImageParams params;
- params.eglParameters = egl_platform::D3D11();
- return params;
-}
-
-TexSubImageParams D3D9Params()
-{
- TexSubImageParams params;
- params.eglParameters = egl_platform::D3D9();
- return params;
-}
-
-TexSubImageParams OpenGLOrGLESParams()
-{
- TexSubImageParams params;
- params.eglParameters = egl_platform::OPENGL_OR_GLES(false);
- return params;
-}
-
-TexSubImageParams VulkanParams()
-{
- TexSubImageParams params;
- params.eglParameters = egl_platform::VULKAN();
- return params;
-}
-
-} // namespace
-
-TEST_P(TexSubImageBenchmark, Run)
-{
- run();
-}
-
-ANGLE_INSTANTIATE_TEST(TexSubImageBenchmark,
- D3D11Params(),
- D3D9Params(),
- OpenGLOrGLESParams(),
- VulkanParams());
diff --git a/src/tests/perf_tests/TextureUploadPerf.cpp b/src/tests/perf_tests/TextureUploadPerf.cpp
new file mode 100644
index 0000000..4046684
--- /dev/null
+++ b/src/tests/perf_tests/TextureUploadPerf.cpp
@@ -0,0 +1,277 @@
+//
+// Copyright 2019 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.
+//
+// TextureUploadBenchmark:
+// Performance test for uploading texture data.
+//
+
+#include "ANGLEPerfTest.h"
+
+#include <iostream>
+#include <random>
+#include <sstream>
+
+#include "test_utils/gl_raii.h"
+#include "util/shader_utils.h"
+
+namespace angle
+{
+constexpr unsigned int kIterationsPerStep = 64;
+
+struct TextureUploadParams final : public RenderTestParams
+{
+ TextureUploadParams()
+ {
+ iterationsPerStep = kIterationsPerStep;
+ trackGpuTime = true;
+
+ baseSize = 2048;
+ subImageSize = 64;
+
+ webgl = false;
+ }
+
+ std::string suffix() const override;
+
+ GLsizei baseSize;
+ GLsizei subImageSize;
+
+ bool webgl;
+};
+
+std::ostream &operator<<(std::ostream &os, const TextureUploadParams ¶ms)
+{
+ os << params.suffix().substr(1);
+ return os;
+}
+
+std::string TextureUploadParams::suffix() const
+{
+ std::stringstream strstr;
+
+ strstr << RenderTestParams::suffix();
+
+ if (webgl)
+ {
+ strstr << "_webgl";
+ }
+
+ return strstr.str();
+}
+
+class TextureUploadBenchmarkBase : public ANGLERenderTest,
+ public ::testing::WithParamInterface<TextureUploadParams>
+{
+ public:
+ TextureUploadBenchmarkBase(const char *benchmarkName);
+
+ void initializeBenchmark() override;
+ void destroyBenchmark() override;
+
+ protected:
+ void initShaders();
+
+ GLuint mProgram;
+ GLuint mPositionLoc;
+ GLuint mSamplerLoc;
+};
+
+class TextureUploadSubImageBenchmark : public TextureUploadBenchmarkBase
+{
+ public:
+ TextureUploadSubImageBenchmark() : TextureUploadBenchmarkBase("TexSubImage")
+ {
+ addExtensionPrerequisite("GL_EXT_texture_storage");
+ }
+
+ void drawBenchmark() override;
+};
+
+class TextureUploadFullMipBenchmark : public TextureUploadBenchmarkBase
+{
+ public:
+ TextureUploadFullMipBenchmark() : TextureUploadBenchmarkBase("TextureUpload") {}
+
+ void drawBenchmark() override;
+};
+
+TextureUploadBenchmarkBase::TextureUploadBenchmarkBase(const char *benchmarkName)
+ : ANGLERenderTest(benchmarkName, GetParam()), mProgram(0u), mPositionLoc(-1), mSamplerLoc(-1)
+{
+ setWebGLCompatibilityEnabled(GetParam().webgl);
+ setRobustResourceInit(GetParam().webgl);
+}
+
+void TextureUploadBenchmarkBase::initializeBenchmark()
+{
+ const auto ¶ms = GetParam();
+
+ initShaders();
+ glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+ glViewport(0, 0, getWindow()->getWidth(), getWindow()->getHeight());
+
+ if (params.webgl)
+ {
+ glRequestExtensionANGLE("GL_EXT_disjoint_timer_query");
+ }
+
+ ASSERT_GL_NO_ERROR();
+}
+
+void TextureUploadBenchmarkBase::initShaders()
+{
+ constexpr char kVS[] = R"(attribute vec4 a_position;
+void main()
+{
+ gl_Position = a_position;
+})";
+
+ constexpr char kFS[] = R"(precision mediump float;
+uniform sampler2D s_texture;
+void main()
+{
+ gl_FragColor = texture2D(s_texture, vec2(0, 0));
+})";
+
+ mProgram = CompileProgram(kVS, kFS);
+ ASSERT_NE(0u, mProgram);
+
+ mPositionLoc = glGetAttribLocation(mProgram, "a_position");
+ mSamplerLoc = glGetUniformLocation(mProgram, "s_texture");
+ glUseProgram(mProgram);
+
+ glDisable(GL_DEPTH_TEST);
+
+ ASSERT_GL_NO_ERROR();
+}
+
+void TextureUploadBenchmarkBase::destroyBenchmark()
+{
+ glDeleteProgram(mProgram);
+}
+
+void TextureUploadSubImageBenchmark::drawBenchmark()
+{
+ const auto ¶ms = GetParam();
+
+ std::vector<float> textureData(params.subImageSize * params.subImageSize * 4, 0.5);
+
+ GLTexture tex;
+
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, tex);
+ glTexStorage2DEXT(GL_TEXTURE_2D, 1, GL_RGBA8, params.baseSize, params.baseSize);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+
+ glUniform1i(mSamplerLoc, 0);
+
+ ASSERT_GL_NO_ERROR();
+
+ startGpuTimer();
+ for (unsigned int iteration = 0; iteration < params.iterationsPerStep; ++iteration)
+ {
+ glTexSubImage2D(GL_TEXTURE_2D, 0, rand() % (params.baseSize - params.subImageSize),
+ rand() % (params.baseSize - params.subImageSize), params.subImageSize,
+ params.subImageSize, GL_RGBA, GL_UNSIGNED_BYTE, textureData.data());
+
+ // 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 TextureUploadFullMipBenchmark::drawBenchmark()
+{
+ const auto ¶ms = GetParam();
+
+ std::vector<float> textureData(params.baseSize * params.baseSize * 4, 0.5);
+
+ startGpuTimer();
+ for (size_t it = 0; it < params.iterationsPerStep; ++it)
+ {
+ GLTexture tex;
+
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, tex);
+
+ // Stage data for all mips
+ GLint mip = 0;
+ for (GLsizei levelSize = params.baseSize; levelSize > 0; levelSize >>= 1)
+ {
+ glTexImage2D(GL_TEXTURE_2D, mip++, GL_RGBA, levelSize, levelSize, 0, GL_RGBA,
+ GL_UNSIGNED_BYTE, textureData.data());
+ }
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_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);
+
+ glUniform1i(mSamplerLoc, 0);
+
+ // 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();
+}
+
+TextureUploadParams TextureUploadD3D11Params(bool webglCompat)
+{
+ TextureUploadParams params;
+ params.eglParameters = egl_platform::D3D11();
+ params.webgl = webglCompat;
+ return params;
+}
+
+TextureUploadParams TextureUploadOpenGLOrGLESParams(bool webglCompat)
+{
+ TextureUploadParams params;
+ params.eglParameters = egl_platform::OPENGL_OR_GLES(false);
+ params.webgl = webglCompat;
+ return params;
+}
+
+TextureUploadParams TextureUploadVulkanParams(bool webglCompat)
+{
+ TextureUploadParams params;
+ params.eglParameters = egl_platform::VULKAN();
+ params.webgl = webglCompat;
+ return params;
+}
+
+TEST_P(TextureUploadSubImageBenchmark, Run)
+{
+ run();
+}
+
+TEST_P(TextureUploadFullMipBenchmark, Run)
+{
+ run();
+}
+
+ANGLE_INSTANTIATE_TEST(TextureUploadSubImageBenchmark,
+ TextureUploadD3D11Params(false),
+ TextureUploadD3D11Params(true),
+ TextureUploadOpenGLOrGLESParams(false),
+ TextureUploadOpenGLOrGLESParams(true),
+ TextureUploadVulkanParams(false),
+ TextureUploadVulkanParams(true));
+
+ANGLE_INSTANTIATE_TEST(TextureUploadFullMipBenchmark,
+ TextureUploadD3D11Params(false),
+ TextureUploadD3D11Params(true),
+ TextureUploadOpenGLOrGLESParams(false),
+ TextureUploadOpenGLOrGLESParams(true),
+ TextureUploadVulkanParams(false),
+ TextureUploadVulkanParams(true));
+} // namespace angle