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 &params)
-{
-    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 &params = 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 &params = 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 &params = 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 &params)
+{
+    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 &params = 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 &params = 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 &params = 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