Added BufferSubData benchmark.

BUG=angle:705

Change-Id: I65d557f35e4c9f1d94853a775330a92b7d428847
Reviewed-on: https://chromium-review.googlesource.com/213810
Tested-by: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
diff --git a/src/common/angleutils.h b/src/common/angleutils.h
index 95f1b60..e85237e 100644
--- a/src/common/angleutils.h
+++ b/src/common/angleutils.h
@@ -24,8 +24,8 @@
   TypeName(const TypeName&);               \
   void operator=(const TypeName&)
 
-template <typename T, unsigned int N>
-inline unsigned int ArraySize(T(&)[N])
+template <typename T, size_t N>
+inline size_t ArraySize(T(&)[N])
 {
     return N;
 }
diff --git a/tests/perf_tests/BufferSubData.cpp b/tests/perf_tests/BufferSubData.cpp
new file mode 100644
index 0000000..db614cf
--- /dev/null
+++ b/tests/perf_tests/BufferSubData.cpp
@@ -0,0 +1,174 @@
+//
+// 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.
+//
+
+#include "BufferSubData.h"
+
+#include <cassert>
+#include <sstream>
+
+std::string BufferSubDataParams::name() const
+{
+    std::stringstream strstr;
+
+    strstr << "BufferSubData - ";
+
+    switch (requestedRenderer)
+    {
+      case EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE: strstr << "D3D11"; break;
+      case EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE: strstr << "D3D9"; break;
+      default: strstr << "UNKNOWN RENDERER (" << requestedRenderer << ")"; break;
+    }
+
+    strstr << " - ";
+
+    switch (vertexType)
+    {
+      case GL_FLOAT: strstr << "Float"; break;
+      case GL_INT: strstr << "Int"; break;
+      case GL_BYTE: strstr << "Byte"; break;
+      case GL_SHORT: strstr << "Short"; break;
+      case GL_UNSIGNED_INT: strstr << "UInt"; break;
+      case GL_UNSIGNED_BYTE: strstr << "UByte"; break;
+      case GL_UNSIGNED_SHORT: strstr << "UShort"; break;
+      default: strstr << "UNKNOWN FORMAT (" << vertexType << ")"; break;
+    }
+
+    strstr << vertexComponentCount;
+
+    strstr << " - " << updateSize << "b updates - ";
+    strstr << (bufferSize >> 10) << "k buffer - ";
+    strstr << iterations << " updates";
+
+    return strstr.str();
+}
+
+BufferSubDataBenchmark::BufferSubDataBenchmark(const BufferSubDataParams &params)
+    : SimpleBenchmark(params.name(), 1280, 720, 2, params.requestedRenderer),
+      mParams(params)
+{
+    mDrawIterations = mParams.iterations;
+
+    assert(mParams.vertexComponentCount > 1);
+    assert(mParams.iterations > 0);
+}
+
+bool BufferSubDataBenchmark::initializeBenchmark()
+{
+    const std::string vs = SHADER_SOURCE
+    (
+        attribute vec4 vPosition;
+        void main()
+        {
+            gl_Position = vPosition;
+        }
+    );
+
+    const std::string fs = SHADER_SOURCE
+    (
+        precision mediump float;
+        void main()
+        {
+            gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
+        }
+    );
+
+    mProgram = CompileProgram(vs, fs);
+    if (!mProgram)
+    {
+        return false;
+    }
+
+    // Use the program object
+    glUseProgram(mProgram);
+
+    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+
+    glGenBuffers(1, &mBuffer);
+    glBindBuffer(GL_ARRAY_BUFFER, mBuffer);
+    glBufferData(GL_ARRAY_BUFFER, mParams.bufferSize, 0, GL_DYNAMIC_DRAW);
+
+    glVertexAttribPointer(0, mParams.vertexComponentCount, mParams.vertexType,
+                          mParams.vertexNormalized, 0, 0);
+    glEnableVertexAttribArray(0);
+
+    mUpdateData = new uint8_t[mParams.updateSize];
+
+    GLfloat vertices2[] =
+    {
+        0.0f, 0.5f,
+        -0.5f, -0.5f,
+        0.5f, -0.5f,
+    };
+
+    GLfloat vertices3[] =
+    {
+        0.0f, 0.5f, 0.0f,
+        -0.5f, -0.5f, 0.0f,
+        0.5f, -0.5f, 0.0f,
+    };
+
+    GLfloat vertices4[] =
+    {
+        0.0f, 0.5f, 0.0f, 1.0f,
+        -0.5f, -0.5f, 0.0f, 1.0f,
+        0.5f, -0.5f, 0.0f, 1.0f,
+    };
+
+    float *vertexData = NULL;
+
+    switch (mParams.vertexComponentCount)
+    {
+      case 2: vertexData = vertices2; break;
+      case 3: vertexData = vertices3; break;
+      case 4: vertexData = vertices4; break;
+      default: break;
+    }
+
+    assert(vertexData != NULL);
+
+    GLsizeiptr vertexDataSize = sizeof(GLfloat) * mParams.vertexComponentCount;
+    GLsizeiptr triDataSize = vertexDataSize * 3;
+    mNumTris = mParams.updateSize / triDataSize;
+    int offset = 0;
+    for (int i = 0; i < mNumTris; ++i)
+    {
+        memcpy(mUpdateData + offset, vertexData, triDataSize);
+        offset += triDataSize;
+    }
+
+    // Set the viewport
+    glViewport(0, 0, getWindow()->getWidth(), getWindow()->getHeight());
+
+    GLenum glErr = glGetError();
+    if (glErr != GL_NO_ERROR)
+    {
+        return false;
+    }
+
+    return true;
+}
+
+void BufferSubDataBenchmark::destroyBenchmark()
+{
+    glDeleteProgram(mProgram);
+    glDeleteBuffers(1, &mBuffer);
+    delete[] mUpdateData;
+}
+
+void BufferSubDataBenchmark::beginDrawBenchmark()
+{
+    // Clear the color buffer
+    glClear(GL_COLOR_BUFFER_BIT);
+}
+
+void BufferSubDataBenchmark::drawBenchmark()
+{
+    for (unsigned int it = 0; it < mParams.iterations; it++)
+    {
+        glBufferSubData(GL_ARRAY_BUFFER, 0, mParams.updateSize, mUpdateData);
+        glDrawArrays(GL_TRIANGLES, 0, 3 * mNumTris);
+    }
+}
diff --git a/tests/perf_tests/BufferSubData.h b/tests/perf_tests/BufferSubData.h
new file mode 100644
index 0000000..f138bcc
--- /dev/null
+++ b/tests/perf_tests/BufferSubData.h
@@ -0,0 +1,47 @@
+//
+// 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.
+//
+
+#include "SimpleBenchmark.h"
+
+const int BUFFER_SIZE = 1024 * 1024;
+const int UPDATE_SIZE = 300;
+
+#include "shader_utils.h"
+
+struct BufferSubDataParams : public BenchmarkParams
+{
+    EGLint requestedRenderer;
+    GLenum vertexType;
+    GLint vertexComponentCount;
+    GLboolean vertexNormalized;
+    GLsizeiptr updateSize;
+    GLsizeiptr bufferSize;
+    unsigned int iterations;
+
+    virtual std::string name() const;
+};
+
+class BufferSubDataBenchmark : public SimpleBenchmark
+{
+  public:
+    BufferSubDataBenchmark(const BufferSubDataParams &params);
+
+    virtual bool initializeBenchmark();
+    virtual void destroyBenchmark();
+    virtual void beginDrawBenchmark();
+    virtual void drawBenchmark();
+
+  private:
+    DISALLOW_COPY_AND_ASSIGN(BufferSubDataBenchmark);
+
+    GLuint mProgram;
+    GLuint mBuffer;
+    uint8_t *mUpdateData;
+    int mNumTris;
+
+    const BufferSubDataParams mParams;
+};
+
diff --git a/tests/perf_tests/SimpleBenchmark.cpp b/tests/perf_tests/SimpleBenchmark.cpp
index b47daf0..b1e83a3 100644
--- a/tests/perf_tests/SimpleBenchmark.cpp
+++ b/tests/perf_tests/SimpleBenchmark.cpp
@@ -10,7 +10,9 @@
 SimpleBenchmark::SimpleBenchmark(const std::string &name, size_t width, size_t height, EGLint glesMajorVersion, EGLint requestedRenderer)
     : mNumFrames(0),
       mName(name),
-      mRunning(false)
+      mRunning(false),
+      mDrawIterations(10),
+      mRunTimeSeconds(5.0)
 {
     mOSWindow.reset(CreateOSWindow());
     mEGLWindow.reset(new EGLWindow(width, height, glesMajorVersion, requestedRenderer));
@@ -41,7 +43,7 @@
 
 void SimpleBenchmark::draw()
 {
-    if (mTimer->getElapsedTime() > runTimeSeconds()) {
+    if (mTimer->getElapsedTime() > mRunTimeSeconds) {
         mRunning = false;
         return;
     }
@@ -50,7 +52,7 @@
 
     beginDrawBenchmark();
 
-    for (int i = 0; i < drawIterations(); ++i)
+    for (unsigned int iteration = 0; iteration < mDrawIterations; ++iteration)
     {
         drawBenchmark();
     }
diff --git a/tests/perf_tests/SimpleBenchmark.h b/tests/perf_tests/SimpleBenchmark.h
index 76ffd88..e128366 100644
--- a/tests/perf_tests/SimpleBenchmark.h
+++ b/tests/perf_tests/SimpleBenchmark.h
@@ -30,9 +30,6 @@
 
     virtual ~SimpleBenchmark() { };
 
-    virtual int drawIterations() const { return 10; }
-    virtual double runTimeSeconds() const { return 10.0; }
-
     virtual bool initializeBenchmark() { return true; }
     virtual void destroyBenchmark() { }
 
@@ -47,6 +44,10 @@
 
     OSWindow *getWindow();
 
+  protected:
+    unsigned int mDrawIterations;
+    double mRunTimeSeconds;
+
   private:
     DISALLOW_COPY_AND_ASSIGN(SimpleBenchmark);
 
diff --git a/tests/perf_tests/SimpleBenchmarks.cpp b/tests/perf_tests/SimpleBenchmarks.cpp
index d538fd7..dc4734b 100644
--- a/tests/perf_tests/SimpleBenchmarks.cpp
+++ b/tests/perf_tests/SimpleBenchmarks.cpp
@@ -5,8 +5,63 @@
 //
 
 #include "SimpleBenchmark.h"
+#include "BufferSubData.h"
+
+EGLint platforms[] =
+{
+    EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE,
+    EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE
+};
+
+GLenum vertexTypes[] = { GL_FLOAT };
+GLint componentCounts[] = { 2, 3, 4 };
+GLboolean vertexNorms[] = { GL_FALSE };
+GLsizeiptr updateSizes[] = { 300 };
+GLsizeiptr bufferSizes[] = { 1024 * 1024 };
+unsigned int iterationCounts[] = { 10 };
 
 int main(int argc, char **argv)
 {
-    // TODO
+    std::vector<BufferSubDataParams> benchmarks;
+
+    for (size_t platIt = 0; platIt < ArraySize(platforms); platIt++)
+    {
+        for (size_t typeIt = 0; typeIt < ArraySize(vertexTypes); typeIt++)
+        {
+            for (size_t compIt = 0; compIt < ArraySize(componentCounts); compIt++)
+            {
+                for (size_t normIt = 0; normIt < ArraySize(vertexNorms); normIt++)
+                {
+                    // No normalized float data
+                    if (vertexTypes[typeIt] == GL_FLOAT && vertexNorms[normIt] == GL_TRUE)
+                    {
+                        continue;
+                    }
+
+                    for (size_t updateIt = 0; updateIt < ArraySize(updateSizes); updateIt++)
+                    {
+                        for (size_t bufszIt = 0; bufszIt < ArraySize(bufferSizes); bufszIt++)
+                        {
+                            for (size_t itIt = 0; itIt < ArraySize(iterationCounts); itIt++)
+                            {
+                                BufferSubDataParams params;
+                                params.requestedRenderer = platforms[platIt];
+                                params.vertexType = vertexTypes[typeIt];
+                                params.vertexComponentCount = componentCounts[compIt];
+                                params.vertexNormalized = vertexNorms[normIt];
+                                params.updateSize = updateSizes[updateIt];
+                                params.bufferSize = bufferSizes[bufszIt];
+                                params.iterations = iterationCounts[itIt];
+
+                                benchmarks.push_back(params);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    // Enumerates permutations
+    RunBenchmarks<BufferSubDataBenchmark>(benchmarks);
 }
diff --git a/tests/tests.gyp b/tests/tests.gyp
index ef0f7a6..1af7f21 100644
--- a/tests/tests.gyp
+++ b/tests/tests.gyp
@@ -209,6 +209,8 @@
                     ],
                     'sources':
                     [
+                        'perf_tests/BufferSubData.cpp',
+                        'perf_tests/BufferSubData.h',
                         'perf_tests/SimpleBenchmark.cpp',
                         'perf_tests/SimpleBenchmark.h',
                         'perf_tests/SimpleBenchmarks.cpp',
diff --git a/util/shared_utils.h b/util/shared_utils.h
index 9a23048..375aff8 100644
--- a/util/shared_utils.h
+++ b/util/shared_utils.h
@@ -15,4 +15,10 @@
     TypeName(const TypeName&);             \
     void operator=(const TypeName&)
 
+template <typename T, size_t N>
+inline size_t ArraySize(T(&)[N])
+{
+    return N;
+}
+
 #endif // UTIL_SHARED_UTILS_H