D3D: Add perf test for dynamic buffer promotion.

Drawing with a DYNAMIC usage array or element array buffer should
eventually lead to internally switching to a static usage, if the app
doesn't modify the data very often. This perf test simply renders a
bunch of random indexed triangles where both buffers are specified as
DYNAMIC. It should perform just as well as the static usage after a
warm-up period.

BUG=angleproject:1334

Change-Id: Ibe432d2122feaefc82d3c11cdf227f93ada82eda
Reviewed-on: https://chromium-review.googlesource.com/332578
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Kenneth Russell <kbr@chromium.org>
Commit-Queue: Jamie Madill <jmadill@chromium.org>
diff --git a/src/tests/perf_tests/DynamicPromotionPerfTest.cpp b/src/tests/perf_tests/DynamicPromotionPerfTest.cpp
new file mode 100644
index 0000000..7383143
--- /dev/null
+++ b/src/tests/perf_tests/DynamicPromotionPerfTest.cpp
@@ -0,0 +1,189 @@
+//
+// 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.
+//
+// DynamicPromotionPerfTest:
+//   Tests that ANGLE will promote buffer specfied with DYNAMIC usage to static after a number of
+//   iterations without changing the data. It specifically affects the D3D back-end, which treats
+//   dynamic and static buffers quite differently.
+//
+
+#include "ANGLEPerfTest.h"
+#include "random_utils.h"
+#include "shader_utils.h"
+#include "Vector.h"
+
+using namespace angle;
+
+namespace
+{
+
+struct DynamicPromotionParams final : public RenderTestParams
+{
+    DynamicPromotionParams();
+    std::string suffix() const override;
+
+    size_t vertexCount;
+    unsigned int iterations;
+};
+
+DynamicPromotionParams::DynamicPromotionParams() : vertexCount(1024), iterations(4)
+{
+}
+
+std::string DynamicPromotionParams::suffix() const
+{
+    return RenderTestParams::suffix();
+}
+
+std::ostream &operator<<(std::ostream &os, const DynamicPromotionParams &params)
+{
+    os << params.suffix().substr(1);
+    return os;
+}
+
+class DynamicPromotionPerfTest : public ANGLERenderTest,
+                                 public testing::WithParamInterface<DynamicPromotionParams>
+{
+  public:
+    DynamicPromotionPerfTest();
+
+    void initializeBenchmark() override;
+    void destroyBenchmark() override;
+    void drawBenchmark() override;
+
+  private:
+    GLuint mProgram;
+    GLuint mElementArrayBuffer;
+    GLuint mArrayBuffer;
+};
+
+DynamicPromotionPerfTest::DynamicPromotionPerfTest()
+    : ANGLERenderTest("DynamicPromotion", GetParam()),
+      mProgram(0),
+      mElementArrayBuffer(0),
+      mArrayBuffer(0)
+{
+}
+
+void DynamicPromotionPerfTest::initializeBenchmark()
+{
+    const std::string &vertexShaderSource =
+        "attribute vec2 position;\n"
+        "attribute vec3 color;\n"
+        "varying vec3 vColor;\n"
+        "void main()\n"
+        "{\n"
+        "    vColor = color;\n"
+        "    gl_Position = vec4(position, 0, 1);\n"
+        "}";
+
+    const std::string &fragmentShaderSource =
+        "varying mediump vec3 vColor;\n"
+        "void main()\n"
+        "{\n"
+        "    gl_FragColor = vec4(vColor, 1);\n"
+        "}";
+
+    mProgram = CompileProgram(vertexShaderSource, fragmentShaderSource);
+    ASSERT_NE(0u, mProgram);
+
+    const size_t vertexCount = GetParam().vertexCount;
+
+    std::vector<GLushort> indexData;
+    std::vector<Vector2> positionData;
+    std::vector<Vector3> colorData;
+
+    ASSERT_GE(static_cast<size_t>(std::numeric_limits<GLushort>::max()), vertexCount);
+
+    RNG rng(1);
+
+    for (size_t index = 0; index < vertexCount; ++index)
+    {
+        indexData.push_back(static_cast<GLushort>(index));
+
+        Vector2 position(rng.randomNegativeOneToOne(), rng.randomNegativeOneToOne());
+        positionData.push_back(position);
+
+        Vector3 color(rng.randomFloat(), rng.randomFloat(), rng.randomFloat());
+        colorData.push_back(color);
+    }
+
+    glGenBuffers(1, &mElementArrayBuffer);
+    glGenBuffers(1, &mArrayBuffer);
+
+    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mElementArrayBuffer);
+    glBindBuffer(GL_ARRAY_BUFFER, mArrayBuffer);
+
+    GLsizeiptr elementArraySize  = sizeof(GLushort) * vertexCount;
+    GLsizeiptr positionArraySize = sizeof(Vector2) * vertexCount;
+    GLsizeiptr colorArraySize    = sizeof(Vector3) * vertexCount;
+
+    // The DYNAMIC_DRAW usage is the key to the test.
+    glBufferData(GL_ELEMENT_ARRAY_BUFFER, elementArraySize, indexData.data(), GL_DYNAMIC_DRAW);
+    glBufferData(GL_ARRAY_BUFFER, positionArraySize + colorArraySize, nullptr, GL_DYNAMIC_DRAW);
+    glBufferSubData(GL_ARRAY_BUFFER, 0, positionArraySize, positionData.data());
+    glBufferSubData(GL_ARRAY_BUFFER, positionArraySize, colorArraySize, colorData.data());
+
+    glUseProgram(mProgram);
+    GLint positionLocation = glGetAttribLocation(mProgram, "position");
+    ASSERT_NE(-1, positionLocation);
+    GLint colorLocation = glGetAttribLocation(mProgram, "color");
+    ASSERT_NE(-1, colorLocation);
+
+    glVertexAttribPointer(positionLocation, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
+    glVertexAttribPointer(colorLocation, 3, GL_FLOAT, GL_FALSE, 0,
+                          reinterpret_cast<const GLvoid *>(positionArraySize));
+
+    glEnableVertexAttribArray(positionLocation);
+    glEnableVertexAttribArray(colorLocation);
+
+    ASSERT_GL_NO_ERROR();
+}
+
+void DynamicPromotionPerfTest::destroyBenchmark()
+{
+    glDeleteProgram(mProgram);
+    glDeleteBuffers(1, &mElementArrayBuffer);
+    glDeleteBuffers(1, &mArrayBuffer);
+}
+
+void DynamicPromotionPerfTest::drawBenchmark()
+{
+    unsigned int iterations = GetParam().iterations;
+    size_t vertexCount      = GetParam().vertexCount;
+
+    glClear(GL_COLOR_BUFFER_BIT);
+    for (unsigned int count = 0; count < iterations; ++count)
+    {
+        glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(vertexCount), GL_UNSIGNED_SHORT, nullptr);
+    }
+
+    ASSERT_GL_NO_ERROR();
+}
+
+DynamicPromotionParams DynamicPromotionD3D11Params()
+{
+    DynamicPromotionParams params;
+    params.eglParameters = egl_platform::D3D11();
+    return params;
+}
+
+DynamicPromotionParams DynamicPromotionD3D9Params()
+{
+    DynamicPromotionParams params;
+    params.eglParameters = egl_platform::D3D9();
+    return params;
+}
+
+TEST_P(DynamicPromotionPerfTest, Run)
+{
+    run();
+}
+
+ANGLE_INSTANTIATE_TEST(DynamicPromotionPerfTest,
+                       DynamicPromotionD3D11Params(),
+                       DynamicPromotionD3D9Params());
+
+}  // anonymous namespace