summaryrefslogtreecommitdiffstats
path: root/gfx/angle/src/tests/perf_tests
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/angle/src/tests/perf_tests')
-rwxr-xr-xgfx/angle/src/tests/perf_tests/ANGLEPerfTest.cpp194
-rwxr-xr-xgfx/angle/src/tests/perf_tests/ANGLEPerfTest.h107
-rwxr-xr-xgfx/angle/src/tests/perf_tests/BufferSubData.cpp418
-rwxr-xr-xgfx/angle/src/tests/perf_tests/DrawCallPerf.cpp256
-rwxr-xr-xgfx/angle/src/tests/perf_tests/DynamicPromotionPerfTest.cpp189
-rwxr-xr-xgfx/angle/src/tests/perf_tests/EGLInitializePerf.cpp161
-rwxr-xr-xgfx/angle/src/tests/perf_tests/IndexConversionPerf.cpp271
-rwxr-xr-xgfx/angle/src/tests/perf_tests/IndexDataManagerTest.cpp186
-rwxr-xr-xgfx/angle/src/tests/perf_tests/InstancingPerf.cpp366
-rwxr-xr-xgfx/angle/src/tests/perf_tests/InterleavedAttributeData.cpp231
-rwxr-xr-xgfx/angle/src/tests/perf_tests/PointSprites.cpp230
-rwxr-xr-xgfx/angle/src/tests/perf_tests/TexSubImage.cpp298
-rwxr-xr-xgfx/angle/src/tests/perf_tests/TextureSampling.cpp288
-rw-r--r--gfx/angle/src/tests/perf_tests/TexturesPerf.cpp293
-rwxr-xr-xgfx/angle/src/tests/perf_tests/UniformsPerf.cpp228
-rwxr-xr-xgfx/angle/src/tests/perf_tests/third_party/perf/angle-mods.patch61
-rwxr-xr-xgfx/angle/src/tests/perf_tests/third_party/perf/perf_test.cc239
-rwxr-xr-xgfx/angle/src/tests/perf_tests/third_party/perf/perf_test.h116
18 files changed, 4132 insertions, 0 deletions
diff --git a/gfx/angle/src/tests/perf_tests/ANGLEPerfTest.cpp b/gfx/angle/src/tests/perf_tests/ANGLEPerfTest.cpp
new file mode 100755
index 000000000..e7c74370d
--- /dev/null
+++ b/gfx/angle/src/tests/perf_tests/ANGLEPerfTest.cpp
@@ -0,0 +1,194 @@
+//
+// 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.
+//
+// ANGLEPerfTests:
+// Base class for google test performance tests
+//
+
+#include "ANGLEPerfTest.h"
+
+#include "third_party/perf/perf_test.h"
+
+#include <cassert>
+#include <cmath>
+#include <iostream>
+
+ANGLEPerfTest::ANGLEPerfTest(const std::string &name, const std::string &suffix)
+ : mName(name),
+ mSuffix(suffix),
+ mTimer(nullptr),
+ mRunTimeSeconds(5.0),
+ mNumStepsPerformed(0),
+ mRunning(true)
+{
+ mTimer = CreateTimer();
+}
+
+ANGLEPerfTest::~ANGLEPerfTest()
+{
+ SafeDelete(mTimer);
+}
+
+void ANGLEPerfTest::run()
+{
+ mTimer->start();
+ while (mRunning)
+ {
+ step();
+ if (mRunning)
+ {
+ ++mNumStepsPerformed;
+ }
+ if (mTimer->getElapsedTime() > mRunTimeSeconds)
+ {
+ mRunning = false;
+ }
+ }
+ finishTest();
+ mTimer->stop();
+}
+
+void ANGLEPerfTest::printResult(const std::string &trace, double value, const std::string &units, bool important) const
+{
+ perf_test::PrintResult(mName, mSuffix, trace, value, units, important);
+}
+
+void ANGLEPerfTest::printResult(const std::string &trace, size_t value, const std::string &units, bool important) const
+{
+ perf_test::PrintResult(mName, mSuffix, trace, value, units, important);
+}
+
+void ANGLEPerfTest::SetUp()
+{
+}
+
+void ANGLEPerfTest::TearDown()
+{
+ double relativeScore = static_cast<double>(mNumStepsPerformed) / mTimer->getElapsedTime();
+ printResult("score", static_cast<size_t>(std::round(relativeScore)), "score", true);
+}
+
+double ANGLEPerfTest::normalizedTime(size_t value) const
+{
+ return static_cast<double>(value) / static_cast<double>(mNumStepsPerformed);
+}
+
+std::string RenderTestParams::suffix() const
+{
+ switch (getRenderer())
+ {
+ case EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE:
+ return "_d3d11";
+ case EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE:
+ return "_d3d9";
+ case EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE:
+ return "_gl";
+ case EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE:
+ return "_gles";
+ case EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE:
+ return "_default";
+ default:
+ assert(0);
+ return "_unk";
+ }
+}
+
+ANGLERenderTest::ANGLERenderTest(const std::string &name, const RenderTestParams &testParams)
+ : ANGLEPerfTest(name, testParams.suffix()),
+ mTestParams(testParams),
+ mEGLWindow(nullptr),
+ mOSWindow(nullptr)
+{
+}
+
+ANGLERenderTest::~ANGLERenderTest()
+{
+ SafeDelete(mOSWindow);
+ SafeDelete(mEGLWindow);
+}
+
+void ANGLERenderTest::SetUp()
+{
+ mOSWindow = CreateOSWindow();
+ mEGLWindow = new EGLWindow(mTestParams.majorVersion, mTestParams.minorVersion,
+ mTestParams.eglParameters);
+ mEGLWindow->setSwapInterval(0);
+
+ if (!mOSWindow->initialize(mName, mTestParams.windowWidth, mTestParams.windowHeight))
+ {
+ FAIL() << "Failed initializing OSWindow";
+ return;
+ }
+
+ if (!mEGLWindow->initializeGL(mOSWindow))
+ {
+ FAIL() << "Failed initializing EGLWindow";
+ return;
+ }
+
+ initializeBenchmark();
+
+ ANGLEPerfTest::SetUp();
+}
+
+void ANGLERenderTest::TearDown()
+{
+ ANGLEPerfTest::TearDown();
+
+ destroyBenchmark();
+
+ mEGLWindow->destroyGL();
+ mOSWindow->destroy();
+}
+
+void ANGLERenderTest::step()
+{
+ // Clear events that the application did not process from this frame
+ Event event;
+ bool closed = false;
+ while (popEvent(&event))
+ {
+ // If the application did not catch a close event, close now
+ if (event.Type == Event::EVENT_CLOSED)
+ {
+ closed = true;
+ }
+ }
+
+ if (closed)
+ {
+ abortTest();
+ }
+ else
+ {
+ drawBenchmark();
+ // Swap is needed so that the GPU driver will occasionally flush its internal command queue
+ // to the GPU. The null device benchmarks are only testing CPU overhead, so they don't need
+ // to swap.
+ if (mTestParams.eglParameters.deviceType != EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE)
+ {
+ mEGLWindow->swap();
+ }
+ mOSWindow->messageLoop();
+ }
+}
+
+void ANGLERenderTest::finishTest()
+{
+ if (mTestParams.eglParameters.deviceType != EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE)
+ {
+ glFinish();
+ }
+}
+
+bool ANGLERenderTest::popEvent(Event *event)
+{
+ return mOSWindow->popEvent(event);
+}
+
+OSWindow *ANGLERenderTest::getWindow()
+{
+ return mOSWindow;
+}
diff --git a/gfx/angle/src/tests/perf_tests/ANGLEPerfTest.h b/gfx/angle/src/tests/perf_tests/ANGLEPerfTest.h
new file mode 100755
index 000000000..62f2cb444
--- /dev/null
+++ b/gfx/angle/src/tests/perf_tests/ANGLEPerfTest.h
@@ -0,0 +1,107 @@
+//
+// 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.
+//
+// ANGLEPerfTests:
+// Base class for google test performance tests
+//
+
+#ifndef PERF_TESTS_ANGLE_PERF_TEST_H_
+#define PERF_TESTS_ANGLE_PERF_TEST_H_
+
+#include <string>
+#include <vector>
+
+#include <gtest/gtest.h>
+#include <EGL/egl.h>
+#include <EGL/eglext.h>
+
+#include "common/angleutils.h"
+#include "common/debug.h"
+#include "EGLWindow.h"
+#include "OSWindow.h"
+#include "test_utils/angle_test_configs.h"
+#include "test_utils/angle_test_instantiate.h"
+#include "Timer.h"
+
+class Event;
+
+#ifndef ASSERT_GL_NO_ERROR
+#define ASSERT_GL_NO_ERROR() ASSERT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError())
+#endif
+
+class ANGLEPerfTest : public testing::Test, angle::NonCopyable
+{
+ public:
+ ANGLEPerfTest(const std::string &name, const std::string &suffix);
+ virtual ~ANGLEPerfTest();
+
+ virtual void step() = 0;
+
+ // Called right before timer is stopped to let the test wait for asynchronous operations.
+ virtual void finishTest() {}
+
+ protected:
+ void run();
+ void printResult(const std::string &trace, double value, const std::string &units, bool important) const;
+ void printResult(const std::string &trace, size_t value, const std::string &units, bool important) const;
+ void SetUp() override;
+ void TearDown() override;
+
+ // Normalize a time value according to the number of test loop iterations (mFrameCount)
+ double normalizedTime(size_t value) const;
+
+ // Call if the test step was aborted and the test should stop running.
+ void abortTest() { mRunning = false; }
+
+ unsigned int getNumStepsPerformed() const { return mNumStepsPerformed; }
+
+ std::string mName;
+ std::string mSuffix;
+ Timer *mTimer;
+ double mRunTimeSeconds;
+
+ private:
+ unsigned int mNumStepsPerformed;
+ bool mRunning;
+};
+
+struct RenderTestParams : public angle::PlatformParameters
+{
+ virtual std::string suffix() const;
+
+ EGLint windowWidth;
+ EGLint windowHeight;
+};
+
+class ANGLERenderTest : public ANGLEPerfTest
+{
+ public:
+ ANGLERenderTest(const std::string &name, const RenderTestParams &testParams);
+ ~ANGLERenderTest();
+
+ virtual void initializeBenchmark() { }
+ virtual void destroyBenchmark() { }
+
+ virtual void drawBenchmark() = 0;
+
+ bool popEvent(Event *event);
+
+ OSWindow *getWindow();
+
+ protected:
+ const RenderTestParams &mTestParams;
+
+ private:
+ void SetUp() override;
+ void TearDown() override;
+
+ void step() override;
+ void finishTest() override;
+
+ EGLWindow *mEGLWindow;
+ OSWindow *mOSWindow;
+};
+
+#endif // PERF_TESTS_ANGLE_PERF_TEST_H_
diff --git a/gfx/angle/src/tests/perf_tests/BufferSubData.cpp b/gfx/angle/src/tests/perf_tests/BufferSubData.cpp
new file mode 100755
index 000000000..5818d52ee
--- /dev/null
+++ b/gfx/angle/src/tests/perf_tests/BufferSubData.cpp
@@ -0,0 +1,418 @@
+//
+// 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.
+//
+// BufferSubDataBenchmark:
+// Performance test for ANGLE buffer updates.
+//
+
+#include <sstream>
+
+#include "ANGLEPerfTest.h"
+#include "shader_utils.h"
+
+using namespace angle;
+
+namespace
+{
+
+struct BufferSubDataParams final : public RenderTestParams
+{
+ BufferSubDataParams()
+ {
+ // Common default values
+ majorVersion = 2;
+ minorVersion = 0;
+ windowWidth = 512;
+ windowHeight = 512;
+ updateSize = 3000;
+ bufferSize = 40000000;
+ iterations = 4;
+ updateRate = 1;
+ }
+
+ std::string suffix() const override;
+
+ GLboolean vertexNormalized;
+ GLenum vertexType;
+ GLint vertexComponentCount;
+ unsigned int updateRate;
+
+ // static parameters
+ GLsizeiptr updateSize;
+ GLsizeiptr bufferSize;
+ unsigned int iterations;
+};
+
+std::ostream &operator<<(std::ostream &os, const BufferSubDataParams &params)
+{
+ os << params.suffix().substr(1);
+ return os;
+}
+
+class BufferSubDataBenchmark : public ANGLERenderTest,
+ public ::testing::WithParamInterface<BufferSubDataParams>
+{
+ public:
+ BufferSubDataBenchmark();
+
+ void initializeBenchmark() override;
+ void destroyBenchmark() override;
+ void drawBenchmark() override;
+
+ private:
+ GLuint mProgram;
+ GLuint mBuffer;
+ uint8_t *mUpdateData;
+ int mNumTris;
+};
+
+GLfloat *GetFloatData(GLint componentCount)
+{
+ static GLfloat vertices2[] =
+ {
+ 1, 2,
+ 0, 0,
+ 2, 0,
+ };
+
+ static GLfloat vertices3[] =
+ {
+ 1, 2, 1,
+ 0, 0, 1,
+ 2, 0, 1,
+ };
+
+ static GLfloat vertices4[] =
+ {
+ 1, 2, 1, 3,
+ 0, 0, 1, 3,
+ 2, 0, 1, 3,
+ };
+
+ switch (componentCount)
+ {
+ case 2:
+ return vertices2;
+ case 3:
+ return vertices3;
+ case 4:
+ return vertices4;
+ default:
+ return nullptr;
+ }
+}
+
+template <class T>
+GLsizeiptr GetNormalizedData(GLsizeiptr numElements, GLfloat *floatData, std::vector<uint8_t> *data)
+{
+ GLsizeiptr triDataSize = sizeof(T) * numElements;
+ data->resize(triDataSize);
+
+ T *destPtr = reinterpret_cast<T*>(data->data());
+
+ for (GLsizeiptr dataIndex = 0; dataIndex < numElements; dataIndex++)
+ {
+ GLfloat scaled = floatData[dataIndex] * 0.25f;
+ destPtr[dataIndex] = static_cast<T>(scaled * static_cast<GLfloat>(std::numeric_limits<T>::max()));
+ }
+
+ return triDataSize;
+}
+
+template <class T>
+GLsizeiptr GetIntData(GLsizeiptr numElements, GLfloat *floatData, std::vector<uint8_t> *data)
+{
+ GLsizeiptr triDataSize = sizeof(T) * numElements;
+ data->resize(triDataSize);
+
+ T *destPtr = reinterpret_cast<T*>(data->data());
+
+ for (GLsizeiptr dataIndex = 0; dataIndex < numElements; dataIndex++)
+ {
+ destPtr[dataIndex] = static_cast<T>(floatData[dataIndex]);
+ }
+
+ return triDataSize;
+}
+
+GLsizeiptr GetVertexData(GLenum type, GLint componentCount, GLboolean normalized, std::vector<uint8_t> *data)
+{
+ GLsizeiptr triDataSize = 0;
+ GLfloat *floatData = GetFloatData(componentCount);
+
+ if (type == GL_FLOAT)
+ {
+ triDataSize = sizeof(GLfloat) * componentCount * 3;
+ data->resize(triDataSize);
+ memcpy(data->data(), floatData, triDataSize);
+ }
+ else if (normalized == GL_TRUE)
+ {
+ GLsizeiptr numElements = componentCount * 3;
+
+ switch (type)
+ {
+ case GL_BYTE:
+ triDataSize = GetNormalizedData<GLbyte>(numElements, floatData, data);
+ break;
+ case GL_SHORT:
+ triDataSize = GetNormalizedData<GLshort>(numElements, floatData, data);
+ break;
+ case GL_INT:
+ triDataSize = GetNormalizedData<GLint>(numElements, floatData, data);
+ break;
+ case GL_UNSIGNED_BYTE:
+ triDataSize = GetNormalizedData<GLubyte>(numElements, floatData, data);
+ break;
+ case GL_UNSIGNED_SHORT:
+ triDataSize = GetNormalizedData<GLushort>(numElements, floatData, data);
+ break;
+ case GL_UNSIGNED_INT:
+ triDataSize = GetNormalizedData<GLuint>(numElements, floatData, data);
+ break;
+ default:
+ assert(0);
+ }
+ }
+ else
+ {
+ GLsizeiptr numElements = componentCount * 3;
+
+ switch (type)
+ {
+ case GL_BYTE:
+ triDataSize = GetIntData<GLbyte>(numElements, floatData, data);
+ break;
+ case GL_SHORT:
+ triDataSize = GetIntData<GLshort>(numElements, floatData, data);
+ break;
+ case GL_INT:
+ triDataSize = GetIntData<GLint>(numElements, floatData, data);
+ break;
+ case GL_UNSIGNED_BYTE:
+ triDataSize = GetIntData<GLubyte>(numElements, floatData, data);
+ break;
+ case GL_UNSIGNED_SHORT:
+ triDataSize = GetIntData<GLushort>(numElements, floatData, data);
+ break;
+ case GL_UNSIGNED_INT:
+ triDataSize = GetIntData<GLuint>(numElements, floatData, data);
+ break;
+ default:
+ assert(0);
+ }
+ }
+
+ return triDataSize;
+}
+
+std::string BufferSubDataParams::suffix() const
+{
+ std::stringstream strstr;
+
+ strstr << RenderTestParams::suffix();
+
+ if (vertexNormalized)
+ {
+ strstr << "_norm";
+ }
+
+ 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 << "_vunk_" << vertexType << "_";
+ break;
+ }
+
+ strstr << vertexComponentCount;
+ strstr << "_every" << updateRate;
+
+ return strstr.str();
+}
+
+BufferSubDataBenchmark::BufferSubDataBenchmark()
+ : ANGLERenderTest("BufferSubData", GetParam()),
+ mProgram(0),
+ mBuffer(0),
+ mUpdateData(nullptr),
+ mNumTris(0)
+{
+}
+
+void BufferSubDataBenchmark::initializeBenchmark()
+{
+ const auto &params = GetParam();
+
+ ASSERT_LT(1, params.vertexComponentCount);
+ ASSERT_LT(0u, params.iterations);
+
+ const std::string vs = SHADER_SOURCE
+ (
+ attribute vec2 vPosition;
+ uniform float uScale;
+ uniform float uOffset;
+ void main()
+ {
+ gl_Position = vec4(vPosition * vec2(uScale) - vec2(uOffset), 0, 1);
+ }
+ );
+
+ 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);
+ ASSERT_NE(0u, mProgram);
+
+ // Use the program object
+ glUseProgram(mProgram);
+
+ glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+
+
+ std::vector<uint8_t> zeroData(params.bufferSize);
+ memset(&zeroData[0], 0, zeroData.size());
+
+ glGenBuffers(1, &mBuffer);
+ glBindBuffer(GL_ARRAY_BUFFER, mBuffer);
+ glBufferData(GL_ARRAY_BUFFER, params.bufferSize, &zeroData[0], GL_DYNAMIC_DRAW);
+
+ glVertexAttribPointer(0, params.vertexComponentCount, params.vertexType,
+ params.vertexNormalized, 0, 0);
+ glEnableVertexAttribArray(0);
+
+ if (params.updateSize > 0)
+ {
+ mUpdateData = new uint8_t[params.updateSize];
+ }
+
+ std::vector<uint8_t> data;
+ GLsizei triDataSize = static_cast<GLsizei>(GetVertexData(params.vertexType,
+ params.vertexComponentCount,
+ params.vertexNormalized, &data));
+
+ mNumTris = static_cast<int>(params.updateSize / triDataSize);
+ for (int i = 0, offset = 0; i < mNumTris; ++i)
+ {
+ memcpy(mUpdateData + offset, &data[0], triDataSize);
+ offset += triDataSize;
+ }
+
+ if (params.updateSize == 0)
+ {
+ mNumTris = 1;
+ glBufferSubData(GL_ARRAY_BUFFER, 0, data.size(), &data[0]);
+ }
+
+ // Set the viewport
+ glViewport(0, 0, getWindow()->getWidth(), getWindow()->getHeight());
+
+ GLfloat scale = 0.5f;
+ GLfloat offset = 0.5f;
+
+ if (params.vertexNormalized == GL_TRUE)
+ {
+ scale = 2.0f;
+ offset = 0.5f;
+ }
+
+ glUniform1f(glGetUniformLocation(mProgram, "uScale"), scale);
+ glUniform1f(glGetUniformLocation(mProgram, "uOffset"), offset);
+
+ ASSERT_GL_NO_ERROR();
+}
+
+void BufferSubDataBenchmark::destroyBenchmark()
+{
+ glDeleteProgram(mProgram);
+ glDeleteBuffers(1, &mBuffer);
+ SafeDeleteArray(mUpdateData);
+}
+
+void BufferSubDataBenchmark::drawBenchmark()
+{
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ const auto &params = GetParam();
+
+ for (unsigned int it = 0; it < params.iterations; it++)
+ {
+ if (params.updateSize > 0 && ((getNumStepsPerformed() % params.updateRate) == 0))
+ {
+ glBufferSubData(GL_ARRAY_BUFFER, 0, params.updateSize, mUpdateData);
+ }
+
+ glDrawArrays(GL_TRIANGLES, 0, 3 * mNumTris);
+ }
+
+ ASSERT_GL_NO_ERROR();
+}
+
+BufferSubDataParams BufferUpdateD3D11Params()
+{
+ BufferSubDataParams params;
+ params.eglParameters = egl_platform::D3D11();
+ params.vertexType = GL_FLOAT;
+ params.vertexComponentCount = 4;
+ params.vertexNormalized = GL_FALSE;
+ return params;
+}
+
+BufferSubDataParams BufferUpdateD3D9Params()
+{
+ BufferSubDataParams params;
+ params.eglParameters = egl_platform::D3D9();
+ params.vertexType = GL_FLOAT;
+ params.vertexComponentCount = 4;
+ params.vertexNormalized = GL_FALSE;
+ return params;
+}
+
+BufferSubDataParams BufferUpdateOpenGLParams()
+{
+ BufferSubDataParams params;
+ params.eglParameters = egl_platform::OPENGL();
+ params.vertexType = GL_FLOAT;
+ params.vertexComponentCount = 4;
+ params.vertexNormalized = GL_FALSE;
+ return params;
+}
+
+TEST_P(BufferSubDataBenchmark, Run)
+{
+ run();
+}
+
+ANGLE_INSTANTIATE_TEST(BufferSubDataBenchmark,
+ BufferUpdateD3D11Params(), BufferUpdateD3D9Params(),
+ BufferUpdateOpenGLParams());
+
+} // namespace
diff --git a/gfx/angle/src/tests/perf_tests/DrawCallPerf.cpp b/gfx/angle/src/tests/perf_tests/DrawCallPerf.cpp
new file mode 100755
index 000000000..e5007e19e
--- /dev/null
+++ b/gfx/angle/src/tests/perf_tests/DrawCallPerf.cpp
@@ -0,0 +1,256 @@
+//
+// 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.
+//
+// DrawCallPerf:
+// Performance tests for ANGLE draw call overhead.
+//
+
+#include <sstream>
+
+#include "ANGLEPerfTest.h"
+#include "shader_utils.h"
+
+using namespace angle;
+
+namespace
+{
+
+struct DrawCallPerfParams final : public RenderTestParams
+{
+ // Common default options
+ DrawCallPerfParams()
+ {
+ majorVersion = 2;
+ minorVersion = 0;
+ windowWidth = 256;
+ windowHeight = 256;
+ }
+
+ std::string suffix() const override
+ {
+ std::stringstream strstr;
+
+ strstr << RenderTestParams::suffix();
+
+ if (numTris == 0)
+ {
+ strstr << "_validation_only";
+ }
+
+ if (useFBO)
+ {
+ strstr << "_render_to_texture";
+ }
+
+ if (eglParameters.deviceType == EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE)
+ {
+ strstr << "_null";
+ }
+
+ return strstr.str();
+ }
+
+ unsigned int iterations = 50;
+ double runTimeSeconds = 10.0;
+ int numTris = 1;
+ bool useFBO = false;
+};
+
+std::ostream &operator<<(std::ostream &os, const DrawCallPerfParams &params)
+{
+ os << params.suffix().substr(1);
+ return os;
+}
+
+class DrawCallPerfBenchmark : public ANGLERenderTest,
+ public ::testing::WithParamInterface<DrawCallPerfParams>
+{
+ public:
+ DrawCallPerfBenchmark();
+
+ void initializeBenchmark() override;
+ void destroyBenchmark() override;
+ void drawBenchmark() override;
+
+ private:
+ GLuint mProgram = 0;
+ GLuint mBuffer = 0;
+ GLuint mFBO = 0;
+ GLuint mTexture = 0;
+ int mNumTris = GetParam().numTris;
+};
+
+DrawCallPerfBenchmark::DrawCallPerfBenchmark() : ANGLERenderTest("DrawCallPerf", GetParam())
+{
+ mRunTimeSeconds = GetParam().runTimeSeconds;
+}
+
+void DrawCallPerfBenchmark::initializeBenchmark()
+{
+ const auto &params = GetParam();
+
+ ASSERT_LT(0u, params.iterations);
+
+ const std::string vs = SHADER_SOURCE
+ (
+ attribute vec2 vPosition;
+ uniform float uScale;
+ uniform float uOffset;
+ void main()
+ {
+ gl_Position = vec4(vPosition * vec2(uScale) - vec2(uOffset), 0, 1);
+ }
+ );
+
+ 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);
+ ASSERT_NE(0u, mProgram);
+
+ // Use the program object
+ glUseProgram(mProgram);
+
+ glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+
+ std::vector<GLfloat> floatData;
+
+ for (int quadIndex = 0; quadIndex < mNumTris; ++quadIndex)
+ {
+ floatData.push_back(1);
+ floatData.push_back(2);
+ floatData.push_back(0);
+ floatData.push_back(0);
+ floatData.push_back(2);
+ floatData.push_back(0);
+ }
+
+ glGenBuffers(1, &mBuffer);
+ glBindBuffer(GL_ARRAY_BUFFER, mBuffer);
+
+ // To avoid generating GL errors when testing validation-only
+ if (floatData.empty())
+ {
+ floatData.push_back(0.0f);
+ }
+
+ glBufferData(GL_ARRAY_BUFFER, floatData.size() * sizeof(GLfloat), &floatData[0], GL_STATIC_DRAW);
+
+ glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0);
+ glEnableVertexAttribArray(0);
+
+ // Set the viewport
+ glViewport(0, 0, getWindow()->getWidth(), getWindow()->getHeight());
+
+ GLfloat scale = 0.5f;
+ GLfloat offset = 0.5f;
+
+ glUniform1f(glGetUniformLocation(mProgram, "uScale"), scale);
+ glUniform1f(glGetUniformLocation(mProgram, "uOffset"), offset);
+
+ if (params.useFBO)
+ {
+ glGenFramebuffers(1, &mFBO);
+ glBindFramebuffer(GL_FRAMEBUFFER, mFBO);
+ glGenTextures(1, &mTexture);
+ glBindTexture(GL_TEXTURE_2D, mTexture);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, getWindow()->getWidth(), getWindow()->getHeight(),
+ 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTexture, 0);
+ }
+
+ ASSERT_GL_NO_ERROR();
+}
+
+void DrawCallPerfBenchmark::destroyBenchmark()
+{
+ glDeleteProgram(mProgram);
+ glDeleteBuffers(1, &mBuffer);
+ glDeleteTextures(1, &mTexture);
+ glDeleteFramebuffers(1, &mFBO);
+}
+
+void DrawCallPerfBenchmark::drawBenchmark()
+{
+ // This workaround fixes a huge queue of graphics commands accumulating on the GL
+ // back-end. The GL back-end doesn't have a proper NULL device at the moment.
+ // TODO(jmadill): Remove this when/if we ever get a proper OpenGL NULL device.
+ const auto &eglParams = GetParam().eglParameters;
+ if (eglParams.deviceType != EGL_PLATFORM_ANGLE_DEVICE_TYPE_NULL_ANGLE ||
+ (eglParams.renderer != EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE &&
+ eglParams.renderer != EGL_PLATFORM_ANGLE_TYPE_OPENGLES_ANGLE))
+ {
+ glClear(GL_COLOR_BUFFER_BIT);
+ }
+
+ const auto &params = GetParam();
+
+ for (unsigned int it = 0; it < params.iterations; it++)
+ {
+ glDrawArrays(GL_TRIANGLES, 0, static_cast<GLsizei>(3 * mNumTris));
+ }
+
+ ASSERT_GL_NO_ERROR();
+}
+
+using namespace egl_platform;
+
+DrawCallPerfParams DrawCallPerfD3D11Params(bool useNullDevice, bool renderToTexture)
+{
+ DrawCallPerfParams params;
+ params.eglParameters = useNullDevice ? D3D11_NULL() : D3D11();
+ params.useFBO = renderToTexture;
+ return params;
+}
+
+DrawCallPerfParams DrawCallPerfD3D9Params(bool useNullDevice, bool renderToTexture)
+{
+ DrawCallPerfParams params;
+ params.eglParameters = useNullDevice ? D3D9_NULL() : D3D9();
+ params.useFBO = renderToTexture;
+ return params;
+}
+
+DrawCallPerfParams DrawCallPerfOpenGLParams(bool useNullDevice, bool renderToTexture)
+{
+ DrawCallPerfParams params;
+ params.eglParameters = useNullDevice ? OPENGL_NULL() : OPENGL();
+ params.useFBO = renderToTexture;
+ return params;
+}
+
+DrawCallPerfParams DrawCallPerfValidationOnly()
+{
+ DrawCallPerfParams params;
+ params.eglParameters = DEFAULT();
+ params.iterations = 10000;
+ params.numTris = 0;
+ params.runTimeSeconds = 5.0;
+ return params;
+}
+
+TEST_P(DrawCallPerfBenchmark, Run)
+{
+ run();
+}
+
+ANGLE_INSTANTIATE_TEST(DrawCallPerfBenchmark,
+ DrawCallPerfD3D9Params(false, false),
+ DrawCallPerfD3D9Params(true, false),
+ DrawCallPerfD3D11Params(false, false),
+ DrawCallPerfD3D11Params(true, false),
+ DrawCallPerfD3D11Params(true, true),
+ DrawCallPerfOpenGLParams(false, false),
+ DrawCallPerfOpenGLParams(true, false),
+ DrawCallPerfOpenGLParams(true, true),
+ DrawCallPerfValidationOnly());
+
+} // namespace
diff --git a/gfx/angle/src/tests/perf_tests/DynamicPromotionPerfTest.cpp b/gfx/angle/src/tests/perf_tests/DynamicPromotionPerfTest.cpp
new file mode 100755
index 000000000..738314354
--- /dev/null
+++ b/gfx/angle/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
diff --git a/gfx/angle/src/tests/perf_tests/EGLInitializePerf.cpp b/gfx/angle/src/tests/perf_tests/EGLInitializePerf.cpp
new file mode 100755
index 000000000..210ba2745
--- /dev/null
+++ b/gfx/angle/src/tests/perf_tests/EGLInitializePerf.cpp
@@ -0,0 +1,161 @@
+//
+// Copyright 2015 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.
+//
+// EGLInitializePerfTest:
+// Performance test for device creation.
+//
+
+#include "ANGLEPerfTest.h"
+#include "Timer.h"
+#include "test_utils/angle_test_configs.h"
+#include "test_utils/angle_test_instantiate.h"
+#include "platform/Platform.h"
+
+using namespace testing;
+
+namespace
+{
+
+// Only applies to D3D11
+class CapturePlatform : public angle::Platform
+{
+ public:
+ CapturePlatform()
+ : mTimer(CreateTimer()),
+ mLoadDLLsMS(0),
+ mCreateDeviceMS(0),
+ mInitResourcesMS(0)
+ {
+ mTimer->start();
+ }
+
+ double currentTime() override;
+ void histogramCustomCounts(
+ const char *name, int sample, int min, int max, int bucketCount) override;
+
+ size_t getLoadDLLsMS() const { return mLoadDLLsMS; }
+ size_t getCreateDeviceMS() const { return mCreateDeviceMS; }
+ size_t getInitResourcesMS() const { return mInitResourcesMS; }
+
+ private:
+ Timer *mTimer;
+ size_t mLoadDLLsMS;
+ size_t mCreateDeviceMS;
+ size_t mInitResourcesMS;
+};
+
+double CapturePlatform::currentTime()
+{
+ return mTimer->getElapsedTime();
+}
+
+void CapturePlatform::histogramCustomCounts(
+ const char *name, int sample, int /*min*/, int /*max*/, int /*bucketCount*/)
+{
+ // These must match the names of the histograms.
+ if (strcmp(name, "GPU.ANGLE.Renderer11InitializeDLLsMS") == 0)
+ {
+ mLoadDLLsMS += static_cast<size_t>(sample);
+ }
+ // Note: not captured in debug, due to creating a debug device
+ else if (strcmp(name, "GPU.ANGLE.D3D11CreateDeviceMS") == 0)
+ {
+ mCreateDeviceMS += static_cast<size_t>(sample);
+ }
+ else if (strcmp(name, "GPU.ANGLE.Renderer11InitializeDeviceMS") == 0)
+ {
+ mInitResourcesMS += static_cast<size_t>(sample);
+ }
+}
+
+class EGLInitializePerfTest : public ANGLEPerfTest,
+ public WithParamInterface<angle::PlatformParameters>
+{
+ public:
+ EGLInitializePerfTest();
+ ~EGLInitializePerfTest();
+
+ void step() override;
+ void TearDown() override;
+
+ private:
+ OSWindow *mOSWindow;
+ EGLDisplay mDisplay;
+ CapturePlatform mCapturePlatform;
+};
+
+EGLInitializePerfTest::EGLInitializePerfTest()
+ : ANGLEPerfTest("EGLInitialize", "_run"),
+ mOSWindow(nullptr),
+ mDisplay(EGL_NO_DISPLAY)
+{
+ auto platform = GetParam().eglParameters;
+
+ std::vector<EGLint> displayAttributes;
+ displayAttributes.push_back(EGL_PLATFORM_ANGLE_TYPE_ANGLE);
+ displayAttributes.push_back(platform.renderer);
+ displayAttributes.push_back(EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE);
+ displayAttributes.push_back(platform.majorVersion);
+ displayAttributes.push_back(EGL_PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE);
+ displayAttributes.push_back(platform.minorVersion);
+
+ if (platform.renderer == EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE ||
+ platform.renderer == EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE)
+ {
+ displayAttributes.push_back(EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE);
+ displayAttributes.push_back(platform.deviceType);
+ }
+ displayAttributes.push_back(EGL_NONE);
+
+ mOSWindow = CreateOSWindow();
+ mOSWindow->initialize("EGLInitialize Test", 64, 64);
+
+ auto eglGetPlatformDisplayEXT =
+ reinterpret_cast<PFNEGLGETPLATFORMDISPLAYEXTPROC>(eglGetProcAddress("eglGetPlatformDisplayEXT"));
+ if (eglGetPlatformDisplayEXT == nullptr)
+ {
+ std::cerr << "Error getting platform display!" << std::endl;
+ return;
+ }
+
+ mDisplay = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE,
+ reinterpret_cast<void *>(mOSWindow->getNativeDisplay()),
+ &displayAttributes[0]);
+
+ ANGLEPlatformInitialize(&mCapturePlatform);
+}
+
+EGLInitializePerfTest::~EGLInitializePerfTest()
+{
+ SafeDelete(mOSWindow);
+}
+
+void EGLInitializePerfTest::step()
+{
+ ASSERT_NE(EGL_NO_DISPLAY, mDisplay);
+
+ EGLint majorVersion, minorVersion;
+ ASSERT_EQ(static_cast<EGLBoolean>(EGL_TRUE), eglInitialize(mDisplay, &majorVersion, &minorVersion));
+ ASSERT_EQ(static_cast<EGLBoolean>(EGL_TRUE), eglTerminate(mDisplay));
+}
+
+void EGLInitializePerfTest::TearDown()
+{
+ ANGLEPerfTest::TearDown();
+ printResult("LoadDLLs", normalizedTime(mCapturePlatform.getLoadDLLsMS()), "ms", true);
+ printResult("D3D11CreateDevice", normalizedTime(mCapturePlatform.getCreateDeviceMS()), "ms", true);
+ printResult("InitResources", normalizedTime(mCapturePlatform.getInitResourcesMS()), "ms", true);
+
+ ANGLEPlatformShutdown();
+}
+
+TEST_P(EGLInitializePerfTest, Run)
+{
+ run();
+}
+
+ANGLE_INSTANTIATE_TEST(EGLInitializePerfTest, angle::ES2_D3D11());
+
+} // namespace
diff --git a/gfx/angle/src/tests/perf_tests/IndexConversionPerf.cpp b/gfx/angle/src/tests/perf_tests/IndexConversionPerf.cpp
new file mode 100755
index 000000000..d05e79d09
--- /dev/null
+++ b/gfx/angle/src/tests/perf_tests/IndexConversionPerf.cpp
@@ -0,0 +1,271 @@
+//
+// Copyright 2015 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.
+//
+// IndexConversionPerf:
+// Performance tests for ANGLE index conversion in D3D11.
+//
+
+#include <sstream>
+
+#include "ANGLEPerfTest.h"
+#include "shader_utils.h"
+
+using namespace angle;
+
+namespace
+{
+
+struct IndexConversionPerfParams final : public RenderTestParams
+{
+ std::string suffix() const override
+ {
+ std::stringstream strstr;
+
+ if (indexRangeOffset > 0)
+ {
+ strstr << "_index_range";
+ }
+
+ strstr << RenderTestParams::suffix();
+
+ return strstr.str();
+ }
+
+ unsigned int iterations;
+ unsigned int numIndexTris;
+
+ // A second test, which covers using index ranges with an offset.
+ unsigned int indexRangeOffset;
+};
+
+// Provide a custom gtest parameter name function for IndexConversionPerfParams.
+std::ostream &operator<<(std::ostream &stream, const IndexConversionPerfParams &param)
+{
+ stream << param.suffix().substr(1);
+ return stream;
+}
+
+class IndexConversionPerfTest : public ANGLERenderTest,
+ public ::testing::WithParamInterface<IndexConversionPerfParams>
+{
+ public:
+ IndexConversionPerfTest();
+
+ void initializeBenchmark() override;
+ void destroyBenchmark() override;
+ void drawBenchmark() override;
+
+ private:
+ void updateBufferData();
+ void drawConversion();
+ void drawIndexRange();
+
+ GLuint mProgram;
+ GLuint mVertexBuffer;
+ GLuint mIndexBuffer;
+ std::vector<GLushort> mIndexData;
+};
+
+IndexConversionPerfTest::IndexConversionPerfTest()
+ : ANGLERenderTest("IndexConversionPerfTest", GetParam()),
+ mProgram(0),
+ mVertexBuffer(0),
+ mIndexBuffer(0)
+{
+ mRunTimeSeconds = 3.0;
+}
+
+void IndexConversionPerfTest::initializeBenchmark()
+{
+ const auto &params = GetParam();
+
+ ASSERT_LT(0u, params.iterations);
+ ASSERT_LT(0u, params.numIndexTris);
+
+ const std::string vs = SHADER_SOURCE
+ (
+ attribute vec2 vPosition;
+ uniform float uScale;
+ uniform float uOffset;
+ void main()
+ {
+ gl_Position = vec4(vPosition * vec2(uScale) - vec2(uOffset), 0, 1);
+ }
+ );
+
+ 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);
+ ASSERT_NE(0u, mProgram);
+
+ // Use the program object
+ glUseProgram(mProgram);
+
+ glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+
+ // Initialize the vertex data
+ std::vector<GLfloat> floatData;
+
+ size_t numTris = std::numeric_limits<GLushort>::max() / 3 + 1;
+ for (size_t triIndex = 0; triIndex < numTris; ++triIndex)
+ {
+ floatData.push_back(1);
+ floatData.push_back(2);
+ floatData.push_back(0);
+ floatData.push_back(0);
+ floatData.push_back(2);
+ floatData.push_back(0);
+ }
+
+ glGenBuffers(1, &mVertexBuffer);
+ glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer);
+ glBufferData(GL_ARRAY_BUFFER, floatData.size() * sizeof(GLfloat), &floatData[0], GL_STATIC_DRAW);
+
+ glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0);
+ glEnableVertexAttribArray(0);
+
+ // Initialize the index buffer
+ for (unsigned int triIndex = 0; triIndex < params.numIndexTris; ++triIndex)
+ {
+ // Handle two different types of tests, one with index conversion triggered by a -1 index.
+ if (params.indexRangeOffset == 0)
+ {
+ mIndexData.push_back(std::numeric_limits<GLushort>::max());
+ }
+ else
+ {
+ mIndexData.push_back(0);
+ }
+
+ mIndexData.push_back(1);
+ mIndexData.push_back(2);
+ }
+
+ glGenBuffers(1, &mIndexBuffer);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuffer);
+ updateBufferData();
+
+ // Set the viewport
+ glViewport(0, 0, getWindow()->getWidth(), getWindow()->getHeight());
+
+ GLfloat scale = 0.5f;
+ GLfloat offset = 0.5f;
+
+ glUniform1f(glGetUniformLocation(mProgram, "uScale"), scale);
+ glUniform1f(glGetUniformLocation(mProgram, "uOffset"), offset);
+
+ ASSERT_GL_NO_ERROR();
+}
+
+void IndexConversionPerfTest::updateBufferData()
+{
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, mIndexData.size() * sizeof(mIndexData[0]), &mIndexData[0], GL_STATIC_DRAW);
+}
+
+void IndexConversionPerfTest::destroyBenchmark()
+{
+ glDeleteProgram(mProgram);
+ glDeleteBuffers(1, &mVertexBuffer);
+ glDeleteBuffers(1, &mIndexBuffer);
+}
+
+void IndexConversionPerfTest::drawBenchmark()
+{
+ const auto &params = GetParam();
+
+ if (params.indexRangeOffset == 0)
+ {
+ drawConversion();
+ }
+ else
+ {
+ drawIndexRange();
+ }
+}
+
+void IndexConversionPerfTest::drawConversion()
+{
+ const auto &params = GetParam();
+
+ // Trigger an update to ensure we convert once a frame
+ updateBufferData();
+
+ for (unsigned int it = 0; it < params.iterations; it++)
+ {
+ glDrawElements(GL_TRIANGLES,
+ static_cast<GLsizei>(params.numIndexTris * 3 - 1),
+ GL_UNSIGNED_SHORT,
+ reinterpret_cast<GLvoid*>(0));
+ }
+
+ ASSERT_GL_NO_ERROR();
+}
+
+void IndexConversionPerfTest::drawIndexRange()
+{
+ const auto &params = GetParam();
+
+ unsigned int indexCount = 3;
+ size_t offset = static_cast<size_t>(indexCount * getNumStepsPerformed());
+
+ offset %= (params.numIndexTris * 3);
+
+ // This test increments an offset each step. Drawing repeatedly may cause the system memory
+ // to release. Then, using a fresh offset will require index range validation, which pages
+ // it back in. The performance should be good even if the data is was used quite a bit.
+ for (unsigned int it = 0; it < params.iterations; it++)
+ {
+ glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(indexCount), GL_UNSIGNED_SHORT,
+ reinterpret_cast<GLvoid *>(offset));
+ }
+
+ ASSERT_GL_NO_ERROR();
+}
+
+IndexConversionPerfParams IndexConversionPerfD3D11Params()
+{
+ IndexConversionPerfParams params;
+ params.eglParameters = egl_platform::D3D11_NULL();
+ params.majorVersion = 2;
+ params.minorVersion = 0;
+ params.windowWidth = 256;
+ params.windowHeight = 256;
+ params.iterations = 225;
+ params.numIndexTris = 3000;
+ params.indexRangeOffset = 0;
+ return params;
+}
+
+IndexConversionPerfParams IndexRangeOffsetPerfD3D11Params()
+{
+ IndexConversionPerfParams params;
+ params.eglParameters = egl_platform::D3D11_NULL();
+ params.majorVersion = 2;
+ params.minorVersion = 0;
+ params.windowWidth = 256;
+ params.windowHeight = 256;
+ params.iterations = 16;
+ params.numIndexTris = 50000;
+ params.indexRangeOffset = 64;
+ return params;
+}
+
+TEST_P(IndexConversionPerfTest, Run)
+{
+ run();
+}
+
+ANGLE_INSTANTIATE_TEST(IndexConversionPerfTest,
+ IndexConversionPerfD3D11Params(),
+ IndexRangeOffsetPerfD3D11Params());
+
+} // namespace
diff --git a/gfx/angle/src/tests/perf_tests/IndexDataManagerTest.cpp b/gfx/angle/src/tests/perf_tests/IndexDataManagerTest.cpp
new file mode 100755
index 000000000..696b81bb5
--- /dev/null
+++ b/gfx/angle/src/tests/perf_tests/IndexDataManagerTest.cpp
@@ -0,0 +1,186 @@
+//
+// Copyright 2015 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.
+//
+// IndexDataManagerPerfTest:
+// Performance test for index buffer management.
+//
+
+#include "ANGLEPerfTest.h"
+
+#include <gmock/gmock.h>
+
+#include "angle_unittests_utils.h"
+#include "libANGLE/renderer/d3d/BufferD3D.h"
+#include "libANGLE/renderer/d3d/IndexBuffer.h"
+#include "libANGLE/renderer/d3d/IndexDataManager.h"
+
+using namespace testing;
+
+namespace
+{
+
+class MockIndexBuffer : public rx::IndexBuffer
+{
+ public:
+ MockIndexBuffer(unsigned int bufferSize, GLenum indexType)
+ : mBufferSize(bufferSize),
+ mIndexType(indexType)
+ {
+ }
+
+ MOCK_METHOD3(initialize, gl::Error(unsigned int, GLenum, bool));
+ MOCK_METHOD3(mapBuffer, gl::Error(unsigned int, unsigned int, void**));
+ MOCK_METHOD0(unmapBuffer, gl::Error());
+ MOCK_METHOD0(discard, gl::Error());
+ MOCK_METHOD2(setSize, gl::Error(unsigned int, GLenum));
+
+ // inlined for speed
+ GLenum getIndexType() const override { return mIndexType; }
+ unsigned int getBufferSize() const override { return mBufferSize; }
+
+ private:
+ unsigned int mBufferSize;
+ GLenum mIndexType;
+};
+
+class MockBufferFactoryD3D : public rx::BufferFactoryD3D
+{
+ public:
+ MockBufferFactoryD3D(unsigned int bufferSize, GLenum indexType)
+ : mBufferSize(bufferSize),
+ mIndexType(indexType)
+ {
+ }
+
+ MOCK_METHOD0(createVertexBuffer, rx::VertexBuffer*());
+ MOCK_CONST_METHOD1(getVertexConversionType, rx::VertexConversionType(gl::VertexFormatType));
+ MOCK_CONST_METHOD1(getVertexComponentType, GLenum(gl::VertexFormatType));
+ MOCK_CONST_METHOD3(getVertexSpaceRequired,
+ gl::ErrorOrResult<unsigned int>(const gl::VertexAttribute &,
+ GLsizei,
+ GLsizei));
+
+ // Dependency injection
+ rx::IndexBuffer* createIndexBuffer() override
+ {
+ return new MockIndexBuffer(mBufferSize, mIndexType);
+ }
+
+ private:
+ unsigned int mBufferSize;
+ GLenum mIndexType;
+};
+
+class MockBufferD3D : public rx::BufferD3D
+{
+ public:
+ MockBufferD3D(rx::BufferFactoryD3D *factory) : BufferD3D(mockState, factory), mData() {}
+
+ // BufferImpl
+ gl::Error setData(GLenum target, const void *data, size_t size, GLenum) override
+ {
+ mData.resize(size);
+ if (data && size > 0)
+ {
+ memcpy(&mData[0], data, size);
+ }
+ return gl::Error(GL_NO_ERROR);
+ }
+
+ MOCK_METHOD4(setSubData, gl::Error(GLenum, const void *, size_t, size_t));
+ MOCK_METHOD4(copySubData, gl::Error(BufferImpl*, GLintptr, GLintptr, GLsizeiptr));
+ MOCK_METHOD2(map, gl::Error(GLenum, GLvoid **));
+ MOCK_METHOD4(mapRange, gl::Error(size_t, size_t, GLbitfield, GLvoid **));
+ MOCK_METHOD1(unmap, gl::Error(GLboolean *));
+
+ // BufferD3D
+ MOCK_METHOD0(markTransformFeedbackUsage, gl::Error());
+
+ // inlined for speed
+ bool supportsDirectBinding() const override { return false; }
+ size_t getSize() const override { return mData.size(); }
+
+ gl::Error getData(const uint8_t **outData) override
+ {
+ *outData = &mData[0];
+ return gl::Error(GL_NO_ERROR);
+ }
+
+ private:
+ gl::BufferState mockState;
+ std::vector<uint8_t> mData;
+};
+
+class MockGLFactoryD3D : public rx::MockGLFactory
+{
+ public:
+ MockGLFactoryD3D(MockBufferFactoryD3D *bufferFactory) : mBufferFactory(bufferFactory) {}
+
+ rx::BufferImpl *createBuffer(const gl::BufferState &state) override
+ {
+ MockBufferD3D *mockBufferD3D = new MockBufferD3D(mBufferFactory);
+
+ EXPECT_CALL(*mBufferFactory, createVertexBuffer())
+ .WillOnce(Return(nullptr))
+ .RetiresOnSaturation();
+ mockBufferD3D->initializeStaticData();
+
+ return mockBufferD3D;
+ }
+
+ MockBufferFactoryD3D *mBufferFactory;
+};
+
+class IndexDataManagerPerfTest : public ANGLEPerfTest
+{
+ public:
+ IndexDataManagerPerfTest();
+
+ void step() override;
+
+ rx::IndexDataManager mIndexDataManager;
+ GLsizei mIndexCount;
+ unsigned int mBufferSize;
+ MockBufferFactoryD3D mMockBufferFactory;
+ MockGLFactoryD3D mMockGLFactory;
+ gl::Buffer mIndexBuffer;
+};
+
+IndexDataManagerPerfTest::IndexDataManagerPerfTest()
+ : ANGLEPerfTest("IndexDataManger", "_run"),
+ mIndexDataManager(&mMockBufferFactory, rx::RENDERER_D3D11),
+ mIndexCount(4000),
+ mBufferSize(mIndexCount * sizeof(GLushort)),
+ mMockBufferFactory(mBufferSize, GL_UNSIGNED_SHORT),
+ mMockGLFactory(&mMockBufferFactory),
+ mIndexBuffer(&mMockGLFactory, 1)
+{
+ std::vector<GLushort> indexData(mIndexCount);
+ for (GLsizei index = 0; index < mIndexCount; ++index)
+ {
+ indexData[index] = static_cast<GLushort>(index);
+ }
+ mIndexBuffer.bufferData(GL_ARRAY_BUFFER, &indexData[0], indexData.size() * sizeof(GLushort),
+ GL_STATIC_DRAW);
+}
+
+void IndexDataManagerPerfTest::step()
+{
+ rx::TranslatedIndexData translatedIndexData;
+ for (unsigned int iteration = 0; iteration < 100; ++iteration)
+ {
+ mIndexBuffer.getIndexRange(GL_UNSIGNED_SHORT, 0, mIndexCount, false,
+ &translatedIndexData.indexRange);
+ mIndexDataManager.prepareIndexData(GL_UNSIGNED_SHORT, mIndexCount, &mIndexBuffer, nullptr,
+ &translatedIndexData, false);
+ }
+}
+
+TEST_F(IndexDataManagerPerfTest, Run)
+{
+ run();
+}
+
+} // anonymous namespace
diff --git a/gfx/angle/src/tests/perf_tests/InstancingPerf.cpp b/gfx/angle/src/tests/perf_tests/InstancingPerf.cpp
new file mode 100755
index 000000000..eb02c10e9
--- /dev/null
+++ b/gfx/angle/src/tests/perf_tests/InstancingPerf.cpp
@@ -0,0 +1,366 @@
+//
+// Copyright 2015 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.
+//
+// InstancingPerf:
+// Performance tests for ANGLE instanced draw calls.
+//
+
+#include <cmath>
+#include <sstream>
+
+#include "ANGLEPerfTest.h"
+#include "Matrix.h"
+#include "random_utils.h"
+#include "shader_utils.h"
+#include "Vector.h"
+
+using namespace angle;
+using namespace egl_platform;
+
+namespace
+{
+
+float AnimationSignal(float t)
+{
+ float l = t / 2.0f;
+ float f = l - std::floor(l);
+ return (f > 0.5f ? 1.0f - f : f) * 4.0f - 1.0f;
+}
+
+template <typename T>
+size_t VectorSizeBytes(const std::vector<T> &vec)
+{
+ return sizeof(T) * vec.size();
+}
+
+Vector3 RandomVector3(RNG *rng)
+{
+ return Vector3(rng->randomNegativeOneToOne(), rng->randomNegativeOneToOne(),
+ rng->randomNegativeOneToOne());
+}
+
+struct InstancingPerfParams final : public RenderTestParams
+{
+ // Common default options
+ InstancingPerfParams()
+ {
+ majorVersion = 2;
+ minorVersion = 0;
+ windowWidth = 256;
+ windowHeight = 256;
+ iterations = 1;
+ runTimeSeconds = 10.0;
+ animationEnabled = false;
+ instancingEnabled = true;
+ }
+
+ std::string suffix() const override
+ {
+ std::stringstream strstr;
+
+ strstr << RenderTestParams::suffix();
+
+ if (!instancingEnabled)
+ {
+ strstr << "_billboards";
+ }
+
+ return strstr.str();
+ }
+
+ unsigned int iterations;
+ double runTimeSeconds;
+ bool animationEnabled;
+ bool instancingEnabled;
+};
+
+std::ostream &operator<<(std::ostream &os, const InstancingPerfParams &params)
+{
+ os << params.suffix().substr(1);
+ return os;
+}
+
+class InstancingPerfBenchmark : public ANGLERenderTest,
+ public ::testing::WithParamInterface<InstancingPerfParams>
+{
+ public:
+ InstancingPerfBenchmark();
+
+ void initializeBenchmark() override;
+ void destroyBenchmark() override;
+ void drawBenchmark() override;
+
+ private:
+ GLuint mProgram;
+ std::vector<GLuint> mBuffers;
+ GLuint mNumPoints;
+ std::vector<Vector3> mTranslateData;
+ std::vector<float> mSizeData;
+ std::vector<Vector3> mColorData;
+ angle::RNG mRNG;
+};
+
+InstancingPerfBenchmark::InstancingPerfBenchmark()
+ : ANGLERenderTest("InstancingPerf", GetParam()), mProgram(0), mNumPoints(75000)
+{
+ mRunTimeSeconds = GetParam().runTimeSeconds;
+}
+
+void InstancingPerfBenchmark::initializeBenchmark()
+{
+ const auto &params = GetParam();
+
+ ASSERT_LT(0u, params.iterations);
+
+ const std::string vs =
+ "attribute vec2 aPosition;\n"
+ "attribute vec3 aTranslate;\n"
+ "attribute float aScale;\n"
+ "attribute vec3 aColor;\n"
+ "uniform mat4 uWorldMatrix;\n"
+ "uniform mat4 uProjectionMatrix;\n"
+ "varying vec3 vColor;\n"
+ "void main()\n"
+ "{\n"
+ " vec4 position = uWorldMatrix * vec4(aTranslate, 1.0);\n"
+ " position.xy += aPosition * aScale;\n"
+ " gl_Position = uProjectionMatrix * position;\n"
+ " vColor = aColor;\n"
+ "}\n";
+
+ const std::string fs =
+ "precision mediump float;\n"
+ "varying vec3 vColor;\n"
+ "void main()\n"
+ "{\n"
+ " gl_FragColor = vec4(vColor, 1.0);\n"
+ "}\n";
+
+ mProgram = CompileProgram(vs, fs);
+ ASSERT_NE(0u, mProgram);
+
+ glUseProgram(mProgram);
+
+ glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+
+ GLuint baseIndexData[6] = {0, 1, 2, 1, 3, 2};
+ Vector2 basePositionData[4] = {Vector2(-1.0f, 1.0f), Vector2(1.0f, 1.0f), Vector2(-1.0f, -1.0f),
+ Vector2(1.0f, -1.0f)};
+
+ std::vector<GLuint> indexData;
+ std::vector<Vector2> positionData;
+
+ if (!params.instancingEnabled)
+ {
+ GLuint pointVertexStride = 4;
+ for (GLuint pointIndex = 0; pointIndex < mNumPoints; ++pointIndex)
+ {
+ for (GLuint indexIndex = 0; indexIndex < 6; ++indexIndex)
+ {
+ indexData.push_back(baseIndexData[indexIndex] + pointIndex * pointVertexStride);
+ }
+
+ Vector3 randVec = RandomVector3(&mRNG);
+ for (GLuint vertexIndex = 0; vertexIndex < 4; ++vertexIndex)
+ {
+ positionData.push_back(basePositionData[vertexIndex]);
+ mTranslateData.push_back(randVec);
+ }
+ }
+
+ mSizeData.resize(mNumPoints * 4, 0.012f);
+ mColorData.resize(mNumPoints * 4, Vector3(1.0f, 0.0f, 0.0f));
+ }
+ else
+ {
+ for (GLuint index : baseIndexData)
+ {
+ indexData.push_back(index);
+ }
+
+ for (const Vector2 &position : basePositionData)
+ {
+ positionData.push_back(position);
+ }
+
+ for (GLuint pointIndex = 0; pointIndex < mNumPoints; ++pointIndex)
+ {
+ Vector3 randVec = RandomVector3(&mRNG);
+ mTranslateData.push_back(randVec);
+ }
+
+ mSizeData.resize(mNumPoints, 0.012f);
+ mColorData.resize(mNumPoints, Vector3(1.0f, 0.0f, 0.0f));
+ }
+
+ mBuffers.resize(5, 0);
+ glGenBuffers(static_cast<GLsizei>(mBuffers.size()), &mBuffers[0]);
+
+ // Index Data
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mBuffers[0]);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, VectorSizeBytes(indexData), &indexData[0],
+ GL_STATIC_DRAW);
+
+ // Position Data
+ glBindBuffer(GL_ARRAY_BUFFER, mBuffers[1]);
+ glBufferData(GL_ARRAY_BUFFER, VectorSizeBytes(positionData), &positionData[0], GL_STATIC_DRAW);
+ GLint positionLocation = glGetAttribLocation(mProgram, "aPosition");
+ ASSERT_NE(-1, positionLocation);
+ glVertexAttribPointer(positionLocation, 2, GL_FLOAT, GL_FALSE, 8, nullptr);
+ glEnableVertexAttribArray(positionLocation);
+
+ // Translate Data
+ glBindBuffer(GL_ARRAY_BUFFER, mBuffers[2]);
+ glBufferData(GL_ARRAY_BUFFER, VectorSizeBytes(mTranslateData), &mTranslateData[0],
+ GL_STATIC_DRAW);
+ GLint translateLocation = glGetAttribLocation(mProgram, "aTranslate");
+ ASSERT_NE(-1, translateLocation);
+ glVertexAttribPointer(translateLocation, 3, GL_FLOAT, GL_FALSE, 12, nullptr);
+ glEnableVertexAttribArray(translateLocation);
+ glVertexAttribDivisorANGLE(translateLocation, 1);
+
+ // Scale Data
+ glBindBuffer(GL_ARRAY_BUFFER, mBuffers[3]);
+ glBufferData(GL_ARRAY_BUFFER, VectorSizeBytes(mSizeData), nullptr, GL_DYNAMIC_DRAW);
+ GLint scaleLocation = glGetAttribLocation(mProgram, "aScale");
+ ASSERT_NE(-1, scaleLocation);
+ glVertexAttribPointer(scaleLocation, 1, GL_FLOAT, GL_FALSE, 4, nullptr);
+ glEnableVertexAttribArray(scaleLocation);
+ glVertexAttribDivisorANGLE(scaleLocation, 1);
+
+ // Color Data
+ glBindBuffer(GL_ARRAY_BUFFER, mBuffers[4]);
+ glBufferData(GL_ARRAY_BUFFER, VectorSizeBytes(mColorData), nullptr, GL_DYNAMIC_DRAW);
+ GLint colorLocation = glGetAttribLocation(mProgram, "aColor");
+ ASSERT_NE(-1, colorLocation);
+ glVertexAttribPointer(colorLocation, 3, GL_FLOAT, GL_FALSE, 12, nullptr);
+ glEnableVertexAttribArray(colorLocation);
+ glVertexAttribDivisorANGLE(colorLocation, 1);
+
+ // Set the viewport
+ glViewport(0, 0, getWindow()->getWidth(), getWindow()->getHeight());
+
+ // Init matrices
+ GLint worldMatrixLocation = glGetUniformLocation(mProgram, "uWorldMatrix");
+ ASSERT_NE(-1, worldMatrixLocation);
+ Matrix4 worldMatrix = Matrix4::translate(Vector3(0, 0, -3.0f));
+ worldMatrix *= Matrix4::rotate(25.0f, Vector3(0.6f, 1.0f, 0.0f));
+ glUniformMatrix4fv(worldMatrixLocation, 1, GL_FALSE, &worldMatrix.data[0]);
+
+ GLint projectionMatrixLocation = glGetUniformLocation(mProgram, "uProjectionMatrix");
+ ASSERT_NE(-1, projectionMatrixLocation);
+ float fov =
+ static_cast<float>(getWindow()->getWidth()) / static_cast<float>(getWindow()->getHeight());
+ Matrix4 projectionMatrix = Matrix4::perspective(60.0f, fov, 1.0f, 300.0f);
+ glUniformMatrix4fv(projectionMatrixLocation, 1, GL_FALSE, &projectionMatrix.data[0]);
+
+ getWindow()->setVisible(true);
+
+ ASSERT_GL_NO_ERROR();
+}
+
+void InstancingPerfBenchmark::destroyBenchmark()
+{
+ glDeleteProgram(mProgram);
+
+ if (!mBuffers.empty())
+ {
+ glDeleteBuffers(static_cast<GLsizei>(mBuffers.size()), &mBuffers[0]);
+ mBuffers.clear();
+ }
+}
+
+void InstancingPerfBenchmark::drawBenchmark()
+{
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ const auto &params = GetParam();
+
+ // Animatino makes the test more interesting visually, but also eats up many CPU cycles.
+ if (params.animationEnabled)
+ {
+ // Not implemented for billboards.
+ ASSERT(params.instancingEnabled);
+
+ float time = static_cast<float>(mTimer->getElapsedTime());
+
+ for (size_t pointIndex = 0; pointIndex < mTranslateData.size(); ++pointIndex)
+ {
+ const Vector3 &translate = mTranslateData[pointIndex];
+
+ float tx = translate.x + time;
+ float ty = translate.y + time;
+ float tz = translate.z + time;
+
+ float scale = AnimationSignal(tx) * 0.01f + 0.01f;
+ mSizeData[pointIndex] = scale;
+
+ Vector3 color;
+ color.x = AnimationSignal(tx) * 0.5f + 0.5f;
+ color.y = AnimationSignal(ty) * 0.5f + 0.5f;
+ color.z = AnimationSignal(tz) * 0.5f + 0.5f;
+
+ mColorData[pointIndex] = color;
+ }
+ }
+
+ // Update scales and colors.
+ glBindBuffer(GL_ARRAY_BUFFER, mBuffers[3]);
+ glBufferSubData(GL_ARRAY_BUFFER, 0, VectorSizeBytes(mSizeData), &mSizeData[0]);
+
+ glBindBuffer(GL_ARRAY_BUFFER, mBuffers[4]);
+ glBufferSubData(GL_ARRAY_BUFFER, 0, VectorSizeBytes(mColorData), &mColorData[0]);
+
+ // Render the instances/billboards.
+ if (params.instancingEnabled)
+ {
+ for (unsigned int it = 0; it < params.iterations; it++)
+ {
+ glDrawElementsInstancedANGLE(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr, mNumPoints);
+ }
+ }
+ else
+ {
+ for (unsigned int it = 0; it < params.iterations; it++)
+ {
+ glDrawElements(GL_TRIANGLES, 6 * mNumPoints, GL_UNSIGNED_INT, nullptr);
+ }
+ }
+
+ ASSERT_GL_NO_ERROR();
+}
+
+InstancingPerfParams InstancingPerfD3D11Params()
+{
+ InstancingPerfParams params;
+ params.eglParameters = D3D11();
+ return params;
+}
+
+InstancingPerfParams InstancingPerfD3D9Params()
+{
+ InstancingPerfParams params;
+ params.eglParameters = D3D9();
+ return params;
+}
+
+InstancingPerfParams InstancingPerfOpenGLParams()
+{
+ InstancingPerfParams params;
+ params.eglParameters = OPENGL();
+ return params;
+}
+
+TEST_P(InstancingPerfBenchmark, Run)
+{
+ run();
+}
+
+ANGLE_INSTANTIATE_TEST(InstancingPerfBenchmark,
+ InstancingPerfD3D11Params(),
+ InstancingPerfD3D9Params(),
+ InstancingPerfOpenGLParams());
+
+} // anonymous namespace
diff --git a/gfx/angle/src/tests/perf_tests/InterleavedAttributeData.cpp b/gfx/angle/src/tests/perf_tests/InterleavedAttributeData.cpp
new file mode 100755
index 000000000..15e2a5b14
--- /dev/null
+++ b/gfx/angle/src/tests/perf_tests/InterleavedAttributeData.cpp
@@ -0,0 +1,231 @@
+//
+// 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.
+//
+// InterleavedAttributeData:
+// Performance test for draws using interleaved attribute data in vertex buffers.
+//
+
+#include <sstream>
+
+#include "ANGLEPerfTest.h"
+#include "shader_utils.h"
+
+using namespace angle;
+
+namespace
+{
+
+struct InterleavedAttributeDataParams final : public RenderTestParams
+{
+ InterleavedAttributeDataParams()
+ {
+ // Common default values
+ majorVersion = 2;
+ minorVersion = 0;
+ windowWidth = 512;
+ windowHeight = 512;
+ numSprites = 3000;
+ }
+
+ // static parameters
+ unsigned int numSprites;
+};
+
+std::ostream &operator<<(std::ostream &os, const InterleavedAttributeDataParams &params)
+{
+ os << params.suffix().substr(1);
+
+ if (params.eglParameters.majorVersion != EGL_DONT_CARE)
+ {
+ os << "_" << params.eglParameters.majorVersion << "_" << params.eglParameters.minorVersion;
+ }
+
+ return os;
+}
+
+class InterleavedAttributeDataBenchmark
+ : public ANGLERenderTest,
+ public ::testing::WithParamInterface<InterleavedAttributeDataParams>
+{
+ public:
+ InterleavedAttributeDataBenchmark();
+
+ void initializeBenchmark() override;
+ void destroyBenchmark() override;
+ void drawBenchmark() override;
+
+ private:
+ GLuint mPointSpriteProgram;
+ GLuint mPositionColorBuffer[2];
+
+ // The buffers contain two floats and 3 unsigned bytes per point sprite
+ const size_t mBytesPerSprite = 2 * sizeof(float) + 3;
+};
+
+InterleavedAttributeDataBenchmark::InterleavedAttributeDataBenchmark()
+ : ANGLERenderTest("InterleavedAttributeData", GetParam()), mPointSpriteProgram(0)
+{
+}
+
+void InterleavedAttributeDataBenchmark::initializeBenchmark()
+{
+ const auto &params = GetParam();
+
+ // Compile point sprite shaders
+ const std::string vs =
+ "attribute vec4 aPosition;"
+ "attribute vec4 aColor;"
+ "varying vec4 vColor;"
+ "void main()"
+ "{"
+ " gl_PointSize = 25.0;"
+ " gl_Position = aPosition;"
+ " vColor = aColor;"
+ "}";
+
+ const std::string fs =
+ "precision mediump float;"
+ "varying vec4 vColor;"
+ "void main()"
+ "{"
+ " gl_FragColor = vColor;"
+ "}";
+
+ mPointSpriteProgram = CompileProgram(vs, fs);
+ ASSERT_NE(0u, mPointSpriteProgram);
+
+ glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
+
+ for (size_t i = 0; i < ArraySize(mPositionColorBuffer); i++)
+ {
+ // Set up initial data for pointsprite positions and colors
+ std::vector<uint8_t> positionColorData(mBytesPerSprite * params.numSprites);
+ for (unsigned int j = 0; j < params.numSprites; j++)
+ {
+ float pointSpriteX =
+ (static_cast<float>(rand() % getWindow()->getWidth()) / getWindow()->getWidth()) *
+ 2.0f - 1.0f;
+ float pointSpriteY =
+ (static_cast<float>(rand() % getWindow()->getHeight()) / getWindow()->getHeight()) *
+ 2.0f - 1.0f;
+ GLubyte pointSpriteRed = static_cast<GLubyte>(rand() % 255);
+ GLubyte pointSpriteGreen = static_cast<GLubyte>(rand() % 255);
+ GLubyte pointSpriteBlue = static_cast<GLubyte>(rand() % 255);
+
+ // Add position data for the pointsprite
+ *reinterpret_cast<float *>(
+ &(positionColorData[j * mBytesPerSprite + 0 * sizeof(float) + 0])) =
+ pointSpriteX; // X
+ *reinterpret_cast<float *>(
+ &(positionColorData[j * mBytesPerSprite + 1 * sizeof(float) + 0])) =
+ pointSpriteY; // Y
+
+ // Add color data for the pointsprite
+ positionColorData[j * mBytesPerSprite + 2 * sizeof(float) + 0] = pointSpriteRed; // R
+ positionColorData[j * mBytesPerSprite + 2 * sizeof(float) + 1] = pointSpriteGreen; // G
+ positionColorData[j * mBytesPerSprite + 2 * sizeof(float) + 2] = pointSpriteBlue; // B
+ }
+
+ // Generate the GL buffer with the position/color data
+ glGenBuffers(1, &mPositionColorBuffer[i]);
+ glBindBuffer(GL_ARRAY_BUFFER, mPositionColorBuffer[i]);
+ glBufferData(GL_ARRAY_BUFFER, params.numSprites * mBytesPerSprite, &(positionColorData[0]),
+ GL_STATIC_DRAW);
+ }
+
+ ASSERT_GL_NO_ERROR();
+}
+
+void InterleavedAttributeDataBenchmark::destroyBenchmark()
+{
+ glDeleteProgram(mPointSpriteProgram);
+
+ for (size_t i = 0; i < ArraySize(mPositionColorBuffer); i++)
+ {
+ glDeleteBuffers(1, &mPositionColorBuffer[i]);
+ }
+}
+
+void InterleavedAttributeDataBenchmark::drawBenchmark()
+{
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ for (size_t k = 0; k < 20; k++)
+ {
+ for (size_t i = 0; i < ArraySize(mPositionColorBuffer); i++)
+ {
+ // Firstly get the attribute locations for the program
+ glUseProgram(mPointSpriteProgram);
+ GLint positionLocation = glGetAttribLocation(mPointSpriteProgram, "aPosition");
+ ASSERT_NE(positionLocation, -1);
+ GLint colorLocation = glGetAttribLocation(mPointSpriteProgram, "aColor");
+ ASSERT_NE(colorLocation, -1);
+
+ // Bind the position data from one buffer
+ glBindBuffer(GL_ARRAY_BUFFER, mPositionColorBuffer[i]);
+ glEnableVertexAttribArray(positionLocation);
+ glVertexAttribPointer(positionLocation, 2, GL_FLOAT, GL_FALSE,
+ static_cast<GLsizei>(mBytesPerSprite), 0);
+
+ // But bind the color data from the other buffer.
+ glBindBuffer(GL_ARRAY_BUFFER,
+ mPositionColorBuffer[(i + 1) % ArraySize(mPositionColorBuffer)]);
+ glEnableVertexAttribArray(colorLocation);
+ glVertexAttribPointer(colorLocation, 3, GL_UNSIGNED_BYTE, GL_TRUE,
+ static_cast<GLsizei>(mBytesPerSprite),
+ reinterpret_cast<void *>(2 * sizeof(float)));
+
+ // Then draw the colored pointsprites
+ glDrawArrays(GL_POINTS, 0, GetParam().numSprites);
+ glFlush();
+
+ glDisableVertexAttribArray(positionLocation);
+ glDisableVertexAttribArray(colorLocation);
+ }
+ }
+
+ ASSERT_GL_NO_ERROR();
+}
+
+TEST_P(InterleavedAttributeDataBenchmark, Run)
+{
+ run();
+}
+
+InterleavedAttributeDataParams D3D11Params()
+{
+ InterleavedAttributeDataParams params;
+ params.eglParameters = egl_platform::D3D11();
+ return params;
+}
+
+InterleavedAttributeDataParams D3D11_9_3Params()
+{
+ InterleavedAttributeDataParams params;
+ params.eglParameters = egl_platform::D3D11_FL9_3();
+ return params;
+}
+
+InterleavedAttributeDataParams D3D9Params()
+{
+ InterleavedAttributeDataParams params;
+ params.eglParameters = egl_platform::D3D9();
+ return params;
+}
+
+InterleavedAttributeDataParams OpenGLParams()
+{
+ InterleavedAttributeDataParams params;
+ params.eglParameters = egl_platform::OPENGL();
+ return params;
+}
+
+ANGLE_INSTANTIATE_TEST(InterleavedAttributeDataBenchmark,
+ D3D11Params(),
+ D3D11_9_3Params(),
+ D3D9Params(),
+ OpenGLParams());
+
+} // anonymous namespace
diff --git a/gfx/angle/src/tests/perf_tests/PointSprites.cpp b/gfx/angle/src/tests/perf_tests/PointSprites.cpp
new file mode 100755
index 000000000..b0b23109b
--- /dev/null
+++ b/gfx/angle/src/tests/perf_tests/PointSprites.cpp
@@ -0,0 +1,230 @@
+//
+// 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.
+//
+// PointSpritesBenchmark:
+// Performance test for ANGLE point sprites.
+//
+//
+#include "ANGLEPerfTest.h"
+
+#include <iostream>
+#include <sstream>
+
+#include "shader_utils.h"
+#include "random_utils.h"
+
+using namespace angle;
+
+namespace
+{
+
+struct PointSpritesParams final : public RenderTestParams
+{
+ PointSpritesParams()
+ {
+ // Common default params
+ majorVersion = 2;
+ minorVersion = 0;
+ windowWidth = 1280;
+ windowHeight = 720;
+ iterations = 100;
+ count = 10;
+ size = 3.0f;
+ numVaryings = 3;
+ }
+
+ std::string suffix() const override;
+
+ unsigned int count;
+ float size;
+ unsigned int numVaryings;
+
+ // static parameters
+ unsigned int iterations;
+};
+
+std::ostream &operator<<(std::ostream &os, const PointSpritesParams &params)
+{
+ os << params.suffix().substr(1);
+ return os;
+}
+
+class PointSpritesBenchmark : public ANGLERenderTest,
+ public ::testing::WithParamInterface<PointSpritesParams>
+{
+ public:
+ PointSpritesBenchmark();
+
+ void initializeBenchmark() override;
+ void destroyBenchmark() override;
+ void drawBenchmark() override;
+
+ private:
+ GLuint mProgram;
+ GLuint mBuffer;
+ RNG mRNG;
+};
+
+std::string PointSpritesParams::suffix() const
+{
+ std::stringstream strstr;
+
+ strstr << RenderTestParams::suffix()
+ << "_" << count << "_" << size << "px"
+ << "_" << numVaryings << "vars";
+
+ return strstr.str();
+}
+
+PointSpritesBenchmark::PointSpritesBenchmark()
+ : ANGLERenderTest("PointSprites", GetParam()), mRNG(1)
+{
+}
+
+void PointSpritesBenchmark::initializeBenchmark()
+{
+ const auto &params = GetParam();
+
+ ASSERT_LT(0u, params.iterations);
+
+ std::stringstream vstrstr;
+
+ // Verify "numVaryings" is within MAX_VARYINGS limit
+ GLint maxVaryings;
+ glGetIntegerv(GL_MAX_VARYING_VECTORS, &maxVaryings);
+
+ if (params.numVaryings > static_cast<unsigned int>(maxVaryings))
+ {
+ FAIL() << "Varying count (" << params.numVaryings << ")"
+ << " exceeds maximum varyings: " << maxVaryings << std::endl;
+ }
+
+ vstrstr << "attribute vec2 vPosition;\n"
+ "uniform float uPointSize;\n";
+
+ for (unsigned int varCount = 0; varCount < params.numVaryings; varCount++)
+ {
+ vstrstr << "varying vec4 v" << varCount << ";\n";
+ }
+
+ vstrstr << "void main()\n"
+ "{\n";
+
+ for (unsigned int varCount = 0; varCount < params.numVaryings; varCount++)
+ {
+ vstrstr << " v" << varCount << " = vec4(1.0);\n";
+ }
+
+ vstrstr << " gl_Position = vec4(vPosition, 0, 1.0);\n"
+ " gl_PointSize = uPointSize;\n"
+ "}";
+
+ std::stringstream fstrstr;
+
+ fstrstr << "precision mediump float;\n";
+
+ for (unsigned int varCount = 0; varCount < params.numVaryings; varCount++)
+ {
+ fstrstr << "varying vec4 v" << varCount << ";\n";
+ }
+
+ fstrstr << "void main()\n"
+ "{\n"
+ " vec4 colorOut = vec4(1.0, 0.0, 0.0, 1.0);\n";
+
+ for (unsigned int varCount = 0; varCount < params.numVaryings; varCount++)
+ {
+ fstrstr << " colorOut.r += v" << varCount << ".r;\n";
+ }
+
+ fstrstr << " gl_FragColor = colorOut;\n"
+ "}\n";
+
+ mProgram = CompileProgram(vstrstr.str(), fstrstr.str());
+ ASSERT_NE(0u, mProgram);
+
+ // Use the program object
+ glUseProgram(mProgram);
+
+ glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+
+ std::vector<float> vertexPositions(params.count * 2);
+ for (size_t pointIndex = 0; pointIndex < vertexPositions.size(); ++pointIndex)
+ {
+ vertexPositions[pointIndex] = mRNG.randomNegativeOneToOne();
+ }
+
+ glGenBuffers(1, &mBuffer);
+ glBindBuffer(GL_ARRAY_BUFFER, mBuffer);
+ glBufferData(GL_ARRAY_BUFFER, vertexPositions.size() * sizeof(float), &vertexPositions[0], GL_STATIC_DRAW);
+
+ GLint positionLocation = glGetAttribLocation(mProgram, "vPosition");
+ ASSERT_NE(-1, positionLocation);
+
+ glVertexAttribPointer(positionLocation, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
+ glEnableVertexAttribArray(positionLocation);
+
+ // Set the viewport
+ glViewport(0, 0, getWindow()->getWidth(), getWindow()->getHeight());
+
+ GLint pointSizeLocation = glGetUniformLocation(mProgram, "uPointSize");
+ ASSERT_NE(-1, pointSizeLocation);
+
+ glUniform1f(pointSizeLocation, params.size);
+
+ ASSERT_GL_NO_ERROR();
+}
+
+void PointSpritesBenchmark::destroyBenchmark()
+{
+ glDeleteProgram(mProgram);
+ glDeleteBuffers(1, &mBuffer);
+}
+
+void PointSpritesBenchmark::drawBenchmark()
+{
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ const auto &params = GetParam();
+
+ for (unsigned int it = 0; it < params.iterations; it++)
+ {
+ //TODO(jmadill): Indexed point rendering. ANGLE is bad at this.
+ glDrawArrays(GL_POINTS, 0, params.count);
+ }
+
+ ASSERT_GL_NO_ERROR();
+}
+
+PointSpritesParams D3D11Params()
+{
+ PointSpritesParams params;
+ params.eglParameters = egl_platform::D3D11();
+ return params;
+}
+
+PointSpritesParams D3D9Params()
+{
+ PointSpritesParams params;
+ params.eglParameters = egl_platform::D3D9();
+ return params;
+}
+
+PointSpritesParams OpenGLParams()
+{
+ PointSpritesParams params;
+ params.eglParameters = egl_platform::OPENGL();
+ return params;
+}
+
+} // namespace
+
+TEST_P(PointSpritesBenchmark, Run)
+{
+ run();
+}
+
+ANGLE_INSTANTIATE_TEST(PointSpritesBenchmark,
+ D3D11Params(), D3D9Params(), OpenGLParams());
diff --git a/gfx/angle/src/tests/perf_tests/TexSubImage.cpp b/gfx/angle/src/tests/perf_tests/TexSubImage.cpp
new file mode 100755
index 000000000..3e4b5cf68
--- /dev/null
+++ b/gfx/angle/src/tests/perf_tests/TexSubImage.cpp
@@ -0,0 +1,298 @@
+//
+// 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 "shader_utils.h"
+
+using namespace angle;
+
+namespace
+{
+
+struct TexSubImageParams final : public RenderTestParams
+{
+ TexSubImageParams()
+ {
+ // Common default parameters
+ majorVersion = 2;
+ minorVersion = 0;
+ windowWidth = 512;
+ windowHeight = 512;
+
+ imageWidth = 1024;
+ imageHeight = 1024;
+ subImageWidth = 64;
+ subImageHeight = 64;
+ iterations = 9;
+ }
+
+ std::string suffix() const override;
+
+ // Static parameters
+ int imageWidth;
+ int imageHeight;
+ int subImageWidth;
+ int subImageHeight;
+ unsigned int iterations;
+};
+
+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)
+{
+}
+
+GLuint TexSubImageBenchmark::createTexture()
+{
+ const auto &params = GetParam();
+
+ assert(params.iterations > 0);
+
+ // 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();
+
+ const std::string vs = SHADER_SOURCE
+ (
+ attribute vec4 a_position;
+ attribute vec2 a_texCoord;
+ varying vec2 v_texCoord;
+ void main()
+ {
+ gl_Position = a_position;
+ v_texCoord = a_texCoord;
+ }
+ );
+
+ const std::string fs = SHADER_SOURCE
+ (
+ precision mediump float;
+ varying vec2 v_texCoord;
+ uniform sampler2D s_texture;
+ void main()
+ {
+ gl_FragColor = texture2D(s_texture, v_texCoord);
+ }
+ );
+
+ mProgram = CompileProgram(vs, fs);
+ 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), (GLvoid*)(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.iterations; ++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 OpenGLParams()
+{
+ TexSubImageParams params;
+ params.eglParameters = egl_platform::OPENGL();
+ return params;
+}
+
+} // namespace
+
+TEST_P(TexSubImageBenchmark, Run)
+{
+ run();
+}
+
+ANGLE_INSTANTIATE_TEST(TexSubImageBenchmark,
+ D3D11Params(), D3D9Params(), OpenGLParams());
diff --git a/gfx/angle/src/tests/perf_tests/TextureSampling.cpp b/gfx/angle/src/tests/perf_tests/TextureSampling.cpp
new file mode 100755
index 000000000..bc00acddf
--- /dev/null
+++ b/gfx/angle/src/tests/perf_tests/TextureSampling.cpp
@@ -0,0 +1,288 @@
+//
+// Copyright (c) 2015 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.
+//
+// TextureSamplingBenchmark:
+// Performance test for texture sampling. The test generates a texture containing random data
+// and then blurs it in a fragment shader using nearest neighbor sampling. The test is
+// specifically designed to test overhead of GLSL's builtin texture*() functions that may result
+// from how ANGLE translates them on each backend.
+//
+
+#include "ANGLEPerfTest.h"
+
+#include <iostream>
+#include <random>
+#include <sstream>
+
+#include "shader_utils.h"
+
+using namespace angle;
+
+namespace
+{
+
+struct TextureSamplingParams final : public RenderTestParams
+{
+ TextureSamplingParams()
+ {
+ // Common default params
+ majorVersion = 2;
+ minorVersion = 0;
+ windowWidth = 720;
+ windowHeight = 720;
+ iterations = 4;
+
+ numSamplers = 2;
+ textureSize = 32;
+ kernelSize = 3;
+ }
+
+ std::string suffix() const override;
+ unsigned int numSamplers;
+ unsigned int textureSize;
+ unsigned int kernelSize;
+
+ // static parameters
+ unsigned int iterations;
+};
+
+std::ostream &operator<<(std::ostream &os, const TextureSamplingParams &params)
+{
+ os << params.suffix().substr(1);
+ return os;
+}
+
+std::string TextureSamplingParams::suffix() const
+{
+ std::stringstream strstr;
+
+ strstr << RenderTestParams::suffix() << "_" << numSamplers << "samplers";
+
+ return strstr.str();
+}
+
+class TextureSamplingBenchmark : public ANGLERenderTest,
+ public ::testing::WithParamInterface<TextureSamplingParams>
+{
+ public:
+ TextureSamplingBenchmark();
+
+ void initializeBenchmark() override;
+ void destroyBenchmark() override;
+ void drawBenchmark() override;
+
+ private:
+ void initShaders();
+ void initVertexBuffer();
+ void initTextures();
+
+ GLuint mProgram;
+ GLuint mBuffer;
+ std::vector<GLuint> mTextures;
+};
+
+TextureSamplingBenchmark::TextureSamplingBenchmark()
+ : ANGLERenderTest("TextureSampling", GetParam()), mProgram(0u), mBuffer(0u)
+{
+}
+
+void TextureSamplingBenchmark::initializeBenchmark()
+{
+ const auto &params = GetParam();
+
+ ASSERT_LT(0u, params.iterations);
+
+ // Verify "numSamplers" is within MAX_TEXTURE_IMAGE_UNITS limit
+ GLint maxTextureImageUnits;
+ glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &maxTextureImageUnits);
+
+ if (params.numSamplers > static_cast<unsigned int>(maxTextureImageUnits))
+ {
+ FAIL() << "Sampler count (" << params.numSamplers << ")"
+ << " exceeds maximum texture count: " << maxTextureImageUnits << std::endl;
+ }
+ initShaders();
+ initVertexBuffer();
+ initTextures();
+ glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+ glViewport(0, 0, getWindow()->getWidth(), getWindow()->getHeight());
+
+ ASSERT_GL_NO_ERROR();
+}
+
+void TextureSamplingBenchmark::initShaders()
+{
+ const auto &params = GetParam();
+
+ std::stringstream vstrstr;
+ vstrstr << "attribute vec2 aPosition;\n"
+ "varying vec2 vTextureCoordinates;\n"
+ "void main()\n"
+ "{\n"
+ " vTextureCoordinates = (aPosition + vec2(1.0)) * 0.5;\n"
+ " gl_Position = vec4(aPosition, 0, 1.0);\n"
+ "}";
+
+ std::stringstream fstrstr;
+ fstrstr << "precision mediump float;\n"
+ "varying vec2 vTextureCoordinates;\n";
+ for (unsigned int count = 0; count < params.numSamplers; count++)
+ {
+ fstrstr << "uniform sampler2D uSampler" << count << ";\n";
+ }
+ fstrstr << "void main()\n"
+ "{\n"
+ " const float inverseTextureSize = 1.0 / "
+ << params.textureSize << ".0;\n"
+ " vec4 colorOut = vec4(0.0, 0.0, 0.0, 1.0);\n";
+ for (unsigned int count = 0; count < params.numSamplers; count++)
+ {
+ fstrstr << " for (int x = 0; x < " << params.kernelSize << "; ++x)\n"
+ " {\n"
+ " for (int y = 0; y < " << params.kernelSize << "; ++y)\n"
+ " {\n"
+ " colorOut += texture2D(uSampler" << count
+ << ", vTextureCoordinates + vec2(x, y) * inverseTextureSize) * 0.1;\n"
+ " }\n"
+ " }\n";
+ }
+ fstrstr << " gl_FragColor = colorOut;\n"
+ "}\n";
+
+ mProgram = CompileProgram(vstrstr.str(), fstrstr.str());
+ ASSERT_NE(0u, mProgram);
+
+ // Use the program object
+ glUseProgram(mProgram);
+}
+
+void TextureSamplingBenchmark::initVertexBuffer()
+{
+ std::vector<float> vertexPositions(12);
+ {
+ // Bottom left triangle
+ vertexPositions[0] = -1.0f;
+ vertexPositions[1] = -1.0f;
+ vertexPositions[2] = 1.0f;
+ vertexPositions[3] = -1.0f;
+ vertexPositions[4] = -1.0f;
+ vertexPositions[5] = 1.0f;
+
+ // Top right triangle
+ vertexPositions[6] = -1.0f;
+ vertexPositions[7] = 1.0f;
+ vertexPositions[8] = 1.0f;
+ vertexPositions[9] = -1.0f;
+ vertexPositions[10] = 1.0f;
+ vertexPositions[11] = 1.0f;
+ }
+
+ glGenBuffers(1, &mBuffer);
+ glBindBuffer(GL_ARRAY_BUFFER, mBuffer);
+ glBufferData(GL_ARRAY_BUFFER, vertexPositions.size() * sizeof(float), &vertexPositions[0],
+ GL_STATIC_DRAW);
+
+ GLint positionLocation = glGetAttribLocation(mProgram, "aPosition");
+ ASSERT_NE(-1, positionLocation);
+
+ glVertexAttribPointer(positionLocation, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
+ glEnableVertexAttribArray(positionLocation);
+}
+
+void TextureSamplingBenchmark::initTextures()
+{
+ const auto &params = GetParam();
+
+ unsigned int dataSize = params.textureSize * params.textureSize;
+ std::vector<unsigned int> randomTextureData;
+ randomTextureData.resize(dataSize);
+
+ unsigned int pseudoRandom = 1u;
+ for (unsigned int i = 0; i < dataSize; ++i)
+ {
+ pseudoRandom = pseudoRandom * 1664525u + 1013904223u;
+ randomTextureData[i] = pseudoRandom;
+ }
+
+ mTextures.resize(params.numSamplers);
+ glGenTextures(params.numSamplers, mTextures.data());
+ for (unsigned int i = 0; i < params.numSamplers; ++i)
+ {
+ glActiveTexture(GL_TEXTURE0 + i);
+ glBindTexture(GL_TEXTURE_2D, mTextures[i]);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, params.textureSize, params.textureSize, 0, GL_RGBA,
+ GL_UNSIGNED_BYTE, randomTextureData.data());
+ }
+
+ for (unsigned int count = 0; count < params.numSamplers; count++)
+ {
+ std::stringstream samplerstrstr;
+ samplerstrstr << "uSampler" << count;
+ GLint samplerLocation = glGetUniformLocation(mProgram, samplerstrstr.str().c_str());
+ ASSERT_NE(-1, samplerLocation);
+
+ glUniform1i(samplerLocation, count);
+ }
+}
+
+void TextureSamplingBenchmark::destroyBenchmark()
+{
+ const auto &params = GetParam();
+
+ glDeleteProgram(mProgram);
+ glDeleteBuffers(1, &mBuffer);
+ if (!mTextures.empty())
+ {
+ glDeleteTextures(params.numSamplers, mTextures.data());
+ }
+}
+
+void TextureSamplingBenchmark::drawBenchmark()
+{
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ const auto &params = GetParam();
+
+ for (unsigned int it = 0; it < params.iterations; ++it)
+ {
+ glDrawArrays(GL_TRIANGLES, 0, 6);
+ }
+
+ ASSERT_GL_NO_ERROR();
+}
+
+TextureSamplingParams D3D11Params()
+{
+ TextureSamplingParams params;
+ params.eglParameters = egl_platform::D3D11();
+ return params;
+}
+
+TextureSamplingParams D3D9Params()
+{
+ TextureSamplingParams params;
+ params.eglParameters = egl_platform::D3D9();
+ return params;
+}
+
+TextureSamplingParams OpenGLParams()
+{
+ TextureSamplingParams params;
+ params.eglParameters = egl_platform::OPENGL();
+ return params;
+}
+
+} // anonymous namespace
+
+TEST_P(TextureSamplingBenchmark, Run)
+{
+ run();
+}
+
+ANGLE_INSTANTIATE_TEST(TextureSamplingBenchmark, D3D11Params(), D3D9Params(), OpenGLParams());
diff --git a/gfx/angle/src/tests/perf_tests/TexturesPerf.cpp b/gfx/angle/src/tests/perf_tests/TexturesPerf.cpp
new file mode 100644
index 000000000..58e1a9ba6
--- /dev/null
+++ b/gfx/angle/src/tests/perf_tests/TexturesPerf.cpp
@@ -0,0 +1,293 @@
+//
+// Copyright (c) 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.
+//
+// TexturesPerf:
+// Performance test for setting texture state.
+//
+
+#include "ANGLEPerfTest.h"
+
+#include <iostream>
+#include <random>
+#include <sstream>
+
+#include "shader_utils.h"
+
+namespace angle
+{
+
+struct TexturesParams final : public RenderTestParams
+{
+ TexturesParams()
+ {
+ // Common default params
+ majorVersion = 2;
+ minorVersion = 0;
+ windowWidth = 720;
+ windowHeight = 720;
+ iterations = 256;
+
+ numTextures = 8;
+ textureRebindFrequency = 5;
+ textureStateUpdateFrequency = 3;
+ textureMipCount = 8;
+ }
+
+ std::string suffix() const override;
+ size_t numTextures;
+ size_t textureRebindFrequency;
+ size_t textureStateUpdateFrequency;
+ size_t textureMipCount;
+
+ // static parameters
+ size_t iterations;
+};
+
+std::ostream &operator<<(std::ostream &os, const TexturesParams &params)
+{
+ os << params.suffix().substr(1);
+ return os;
+}
+
+std::string TexturesParams::suffix() const
+{
+ std::stringstream strstr;
+
+ strstr << RenderTestParams::suffix();
+ strstr << "_" << numTextures << "_textures";
+ strstr << "_" << textureRebindFrequency << "_rebind";
+ strstr << "_" << textureStateUpdateFrequency << "_state";
+ strstr << "_" << textureMipCount << "_mips";
+
+ return strstr.str();
+}
+
+class TexturesBenchmark : public ANGLERenderTest,
+ public ::testing::WithParamInterface<TexturesParams>
+{
+ public:
+ TexturesBenchmark();
+
+ void initializeBenchmark() override;
+ void destroyBenchmark() override;
+ void drawBenchmark() override;
+
+ private:
+ void initShaders();
+ void initTextures();
+
+ std::vector<GLuint> mTextures;
+
+ GLuint mProgram;
+ std::vector<GLuint> mUniformLocations;
+};
+
+TexturesBenchmark::TexturesBenchmark() : ANGLERenderTest("Textures", GetParam()), mProgram(0u)
+{
+}
+
+void TexturesBenchmark::initializeBenchmark()
+{
+ const auto &params = GetParam();
+
+ ASSERT_GT(params.iterations, 0u);
+
+ // Verify the uniform counts are within the limits
+ GLint maxTextureUnits;
+ glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &maxTextureUnits);
+ if (params.numTextures > static_cast<size_t>(maxTextureUnits))
+ {
+ FAIL() << "Texture count (" << params.numTextures << ")"
+ << " exceeds maximum texture unit count: " << maxTextureUnits << std::endl;
+ }
+
+ initShaders();
+ initTextures();
+ glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+ glViewport(0, 0, getWindow()->getWidth(), getWindow()->getHeight());
+
+ ASSERT_GL_NO_ERROR();
+}
+
+std::string GetUniformLocationName(size_t idx, bool vertexShader)
+{
+ std::stringstream strstr;
+ strstr << (vertexShader ? "vs" : "fs") << "_u_" << idx;
+ return strstr.str();
+}
+
+void TexturesBenchmark::initShaders()
+{
+ const auto &params = GetParam();
+
+ std::string vs =
+ "void main()\n"
+ "{\n"
+ " gl_Position = vec4(0, 0, 0, 0);\n"
+ "}\n";
+
+ std::stringstream fstrstr;
+ for (size_t i = 0; i < params.numTextures; i++)
+ {
+ fstrstr << "uniform sampler2D tex" << i << ";";
+ }
+ fstrstr << "void main()\n"
+ "{\n"
+ " gl_FragColor = vec4(0, 0, 0, 0)";
+ for (size_t i = 0; i < params.numTextures; i++)
+ {
+ fstrstr << "+ texture2D(tex" << i << ", vec2(0, 0))";
+ }
+ fstrstr << ";\n"
+ "}\n";
+
+ mProgram = CompileProgram(vs, fstrstr.str());
+ ASSERT_NE(0u, mProgram);
+
+ for (size_t i = 0; i < params.numTextures; ++i)
+ {
+ std::stringstream uniformName;
+ uniformName << "tex" << i;
+
+ GLint location = glGetUniformLocation(mProgram, uniformName.str().c_str());
+ ASSERT_NE(-1, location);
+ mUniformLocations.push_back(location);
+ }
+
+ // Use the program object
+ glUseProgram(mProgram);
+}
+
+void TexturesBenchmark::initTextures()
+{
+ const auto &params = GetParam();
+
+ size_t textureSize = static_cast<size_t>(1) << params.textureMipCount;
+ std::vector<GLubyte> textureData(textureSize * textureSize * 4);
+ for (auto &byte : textureData)
+ {
+ byte = rand() % 255u;
+ }
+
+ for (size_t texIndex = 0; texIndex < params.numTextures; texIndex++)
+ {
+ GLuint tex = 0;
+ glGenTextures(1, &tex);
+
+ glActiveTexture(static_cast<GLenum>(GL_TEXTURE0 + texIndex));
+ glBindTexture(GL_TEXTURE_2D, tex);
+ for (size_t mip = 0; mip < params.textureMipCount; mip++)
+ {
+ GLsizei levelSize = static_cast<GLsizei>(textureSize >> mip);
+ glTexImage2D(GL_TEXTURE_2D, static_cast<GLint>(mip), GL_RGBA, levelSize, levelSize, 0,
+ GL_RGBA, GL_UNSIGNED_BYTE, textureData.data());
+ }
+ mTextures.push_back(tex);
+
+ glUniform1i(mUniformLocations[texIndex], static_cast<GLint>(texIndex));
+ }
+}
+
+void TexturesBenchmark::destroyBenchmark()
+{
+ glDeleteProgram(mProgram);
+}
+
+void TexturesBenchmark::drawBenchmark()
+{
+ const auto &params = GetParam();
+
+ for (size_t it = 0; it < params.iterations; ++it)
+ {
+ if (it % params.textureRebindFrequency == 0)
+ {
+ // Swap two textures
+ size_t swapTexture = (it / params.textureRebindFrequency) % (params.numTextures - 1);
+
+ glActiveTexture(static_cast<GLenum>(GL_TEXTURE0 + swapTexture));
+ glBindTexture(GL_TEXTURE_2D, mTextures[swapTexture]);
+ glActiveTexture(static_cast<GLenum>(GL_TEXTURE0 + swapTexture + 1));
+ glBindTexture(GL_TEXTURE_2D, mTextures[swapTexture + 1]);
+ std::swap(mTextures[swapTexture], mTextures[swapTexture + 1]);
+ }
+
+ if (it % params.textureStateUpdateFrequency == 0)
+ {
+ // Update a texture's state
+ size_t stateUpdateCount = it / params.textureStateUpdateFrequency;
+
+ const size_t numUpdateTextures = 4;
+ ASSERT_LE(numUpdateTextures, params.numTextures);
+
+ size_t firstTexture = stateUpdateCount % (params.numTextures - numUpdateTextures);
+
+ for (size_t updateTextureIdx = 0; updateTextureIdx < numUpdateTextures;
+ updateTextureIdx++)
+ {
+ size_t updateTexture = firstTexture + updateTextureIdx;
+ glActiveTexture(static_cast<GLenum>(GL_TEXTURE0 + updateTexture));
+
+ const GLenum minFilters[] = {
+ GL_NEAREST,
+ GL_LINEAR,
+ GL_NEAREST_MIPMAP_NEAREST,
+ GL_LINEAR_MIPMAP_NEAREST,
+ GL_NEAREST_MIPMAP_LINEAR,
+ GL_LINEAR_MIPMAP_LINEAR,
+ };
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
+ minFilters[stateUpdateCount % ArraySize(minFilters)]);
+
+ const GLenum magFilters[] = {
+ GL_NEAREST, GL_LINEAR,
+ };
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
+ magFilters[stateUpdateCount % ArraySize(magFilters)]);
+
+ const GLenum wrapParameters[] = {
+ GL_CLAMP_TO_EDGE, GL_REPEAT, GL_MIRRORED_REPEAT,
+ };
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
+ wrapParameters[stateUpdateCount % ArraySize(wrapParameters)]);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,
+ wrapParameters[stateUpdateCount % ArraySize(wrapParameters)]);
+ }
+ }
+
+ glDrawArrays(GL_TRIANGLES, 0, 3);
+ }
+
+ ASSERT_GL_NO_ERROR();
+}
+
+TexturesParams D3D11Params()
+{
+ TexturesParams params;
+ params.eglParameters = egl_platform::D3D11_NULL();
+ return params;
+}
+
+TexturesParams D3D9Params()
+{
+ TexturesParams params;
+ params.eglParameters = egl_platform::D3D9_NULL();
+ return params;
+}
+
+TexturesParams OpenGLParams()
+{
+ TexturesParams params;
+ params.eglParameters = egl_platform::OPENGL_NULL();
+ return params;
+}
+
+TEST_P(TexturesBenchmark, Run)
+{
+ run();
+}
+
+ANGLE_INSTANTIATE_TEST(TexturesBenchmark, D3D11Params(), D3D9Params(), OpenGLParams());
+
+} // namespace angle
diff --git a/gfx/angle/src/tests/perf_tests/UniformsPerf.cpp b/gfx/angle/src/tests/perf_tests/UniformsPerf.cpp
new file mode 100755
index 000000000..276b1ab9b
--- /dev/null
+++ b/gfx/angle/src/tests/perf_tests/UniformsPerf.cpp
@@ -0,0 +1,228 @@
+//
+// Copyright (c) 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.
+//
+// UniformsBenchmark:
+// Performance test for setting uniform data.
+//
+
+#include "ANGLEPerfTest.h"
+
+#include <iostream>
+#include <random>
+#include <sstream>
+
+#include "shader_utils.h"
+
+using namespace angle;
+
+namespace
+{
+
+struct UniformsParams final : public RenderTestParams
+{
+ UniformsParams()
+ {
+ // Common default params
+ majorVersion = 2;
+ minorVersion = 0;
+ windowWidth = 720;
+ windowHeight = 720;
+ iterations = 4;
+
+ numVertexUniforms = 200;
+ numFragmentUniforms = 200;
+ }
+
+ std::string suffix() const override;
+ size_t numVertexUniforms;
+ size_t numFragmentUniforms;
+
+ // static parameters
+ size_t iterations;
+};
+
+std::ostream &operator<<(std::ostream &os, const UniformsParams &params)
+{
+ os << params.suffix().substr(1);
+ return os;
+}
+
+std::string UniformsParams::suffix() const
+{
+ std::stringstream strstr;
+
+ strstr << RenderTestParams::suffix();
+ strstr << "_" << numVertexUniforms << "_vertex_uniforms";
+ strstr << "_" << numFragmentUniforms << "_fragment_uniforms";
+
+ return strstr.str();
+}
+
+class UniformsBenchmark : public ANGLERenderTest,
+ public ::testing::WithParamInterface<UniformsParams>
+{
+ public:
+ UniformsBenchmark();
+
+ void initializeBenchmark() override;
+ void destroyBenchmark() override;
+ void drawBenchmark() override;
+
+ private:
+ void initShaders();
+ void initVertexBuffer();
+ void initTextures();
+
+ GLuint mProgram;
+ std::vector<GLuint> mUniformLocations;
+};
+
+UniformsBenchmark::UniformsBenchmark() : ANGLERenderTest("Uniforms", GetParam()), mProgram(0u)
+{
+}
+
+void UniformsBenchmark::initializeBenchmark()
+{
+ const auto &params = GetParam();
+
+ ASSERT_GT(params.iterations, 0u);
+
+ // Verify the uniform counts are within the limits
+ GLint maxVertexUniforms, maxFragmentUniforms;
+ glGetIntegerv(GL_MAX_VERTEX_UNIFORM_VECTORS, &maxVertexUniforms);
+ glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_VECTORS, &maxFragmentUniforms);
+
+ if (params.numVertexUniforms > static_cast<size_t>(maxVertexUniforms))
+ {
+ FAIL() << "Vertex uniform count (" << params.numVertexUniforms << ")"
+ << " exceeds maximum vertex uniform count: " << maxVertexUniforms << std::endl;
+ }
+ if (params.numFragmentUniforms > static_cast<size_t>(maxFragmentUniforms))
+ {
+ FAIL() << "Fragment uniform count (" << params.numFragmentUniforms << ")"
+ << " exceeds maximum fragment uniform count: " << maxFragmentUniforms << std::endl;
+ }
+
+ initShaders();
+ glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+ glViewport(0, 0, getWindow()->getWidth(), getWindow()->getHeight());
+
+ ASSERT_GL_NO_ERROR();
+}
+
+std::string GetUniformLocationName(size_t idx, bool vertexShader)
+{
+ std::stringstream strstr;
+ strstr << (vertexShader ? "vs" : "fs") << "_u_" << idx;
+ return strstr.str();
+}
+
+void UniformsBenchmark::initShaders()
+{
+ const auto &params = GetParam();
+
+ std::stringstream vstrstr;
+ vstrstr << "precision mediump float;\n";
+ for (size_t i = 0; i < params.numVertexUniforms; i++)
+ {
+ vstrstr << "uniform vec4 " << GetUniformLocationName(i, true) << ";\n";
+ }
+ vstrstr << "void main()\n"
+ "{\n"
+ " gl_Position = vec4(0, 0, 0, 0);\n";
+ for (size_t i = 0; i < params.numVertexUniforms; i++)
+ {
+ vstrstr << " gl_Position = gl_Position + " << GetUniformLocationName(i, true) << ";\n";
+ }
+ vstrstr << "}";
+
+ std::stringstream fstrstr;
+ fstrstr << "precision mediump float;\n";
+ for (size_t i = 0; i < params.numFragmentUniforms; i++)
+ {
+ fstrstr << "uniform vec4 " << GetUniformLocationName(i, false) << ";\n";
+ }
+ fstrstr << "void main()\n"
+ "{\n"
+ " gl_FragColor = vec4(0, 0, 0, 0);\n";
+ for (size_t i = 0; i < params.numFragmentUniforms; i++)
+ {
+ fstrstr << " gl_FragColor = gl_FragColor + " << GetUniformLocationName(i, false)
+ << ";\n";
+ }
+ fstrstr << "}";
+
+ mProgram = CompileProgram(vstrstr.str(), fstrstr.str());
+ ASSERT_NE(0u, mProgram);
+
+ for (size_t i = 0; i < params.numVertexUniforms; ++i)
+ {
+ GLint location = glGetUniformLocation(mProgram, GetUniformLocationName(i, true).c_str());
+ ASSERT_NE(-1, location);
+ mUniformLocations.push_back(location);
+ }
+ for (size_t i = 0; i < params.numFragmentUniforms; ++i)
+ {
+ GLint location = glGetUniformLocation(mProgram, GetUniformLocationName(i, false).c_str());
+ ASSERT_NE(-1, location);
+ mUniformLocations.push_back(location);
+ }
+
+ // Use the program object
+ glUseProgram(mProgram);
+}
+
+void UniformsBenchmark::destroyBenchmark()
+{
+ glDeleteProgram(mProgram);
+}
+
+void UniformsBenchmark::drawBenchmark()
+{
+ const auto &params = GetParam();
+
+ for (size_t it = 0; it < params.iterations; ++it)
+ {
+ for (size_t uniform = 0; uniform < mUniformLocations.size(); ++uniform)
+ {
+ float value = static_cast<float>(uniform);
+ glUniform4f(mUniformLocations[uniform], value, value, value, value);
+ }
+
+ glDrawArrays(GL_TRIANGLES, 0, 3);
+ }
+
+ ASSERT_GL_NO_ERROR();
+}
+
+UniformsParams D3D11Params()
+{
+ UniformsParams params;
+ params.eglParameters = egl_platform::D3D11();
+ return params;
+}
+
+UniformsParams D3D9Params()
+{
+ UniformsParams params;
+ params.eglParameters = egl_platform::D3D9();
+ return params;
+}
+
+UniformsParams OpenGLParams()
+{
+ UniformsParams params;
+ params.eglParameters = egl_platform::OPENGL();
+ return params;
+}
+
+} // anonymous namespace
+
+TEST_P(UniformsBenchmark, Run)
+{
+ run();
+}
+
+ANGLE_INSTANTIATE_TEST(UniformsBenchmark, D3D11Params(), D3D9Params(), OpenGLParams());
diff --git a/gfx/angle/src/tests/perf_tests/third_party/perf/angle-mods.patch b/gfx/angle/src/tests/perf_tests/third_party/perf/angle-mods.patch
new file mode 100755
index 000000000..d0b640289
--- /dev/null
+++ b/gfx/angle/src/tests/perf_tests/third_party/perf/angle-mods.patch
@@ -0,0 +1,61 @@
+diff --git a/tests/perf_tests/third_party/perf/perf_test.cc b/tests/perf_tests/third_party/perf/perf_test.cc
+index 0d5abc0..7364330 100644
+--- a/tests/perf_tests/third_party/perf/perf_test.cc
++++ b/tests/perf_tests/third_party/perf/perf_test.cc
+@@ -2,16 +2,51 @@
+ // Use of this source code is governed by a BSD-style license that can be
+ // found in the LICENSE file.
+
+-#include "testing/perf/perf_test.h"
++#include "perf_test.h"
+
+ #include <stdio.h>
+-
+-#include "base/logging.h"
+-#include "base/strings/string_number_conversions.h"
+-#include "base/strings/stringprintf.h"
++#include <stdarg.h>
++#include <vector>
+
+ namespace {
+
++namespace base {
++
++std::string FormatString(const char *fmt, va_list vararg) {
++ static std::vector<char> buffer(512);
++
++ // Attempt to just print to the current buffer
++ int len = vsnprintf(&buffer[0], buffer.size(), fmt, vararg);
++ if (len < 0 || static_cast<size_t>(len) >= buffer.size()) {
++ // Buffer was not large enough, calculate the required size and resize the buffer
++ len = vsnprintf(NULL, 0, fmt, vararg);
++ buffer.resize(len + 1);
++
++ // Print again
++ vsnprintf(&buffer[0], buffer.size(), fmt, vararg);
++ }
++
++ return std::string(buffer.data(), len);
++}
++
++std::string StringPrintf(const char *fmt, ...) {
++ va_list vararg;
++ va_start(vararg, fmt);
++ std::string result = FormatString(fmt, vararg);
++ va_end(vararg);
++ return result;
++}
++
++std::string UintToString(unsigned int value) {
++ return StringPrintf("%u", value);
++}
++
++std::string DoubleToString(double value) {
++ return StringPrintf("%.10lf", value);
++}
++
++}
++
+ std::string ResultsToString(const std::string& measurement,
+ const std::string& modifier,
+ const std::string& trace,
diff --git a/gfx/angle/src/tests/perf_tests/third_party/perf/perf_test.cc b/gfx/angle/src/tests/perf_tests/third_party/perf/perf_test.cc
new file mode 100755
index 000000000..73643306c
--- /dev/null
+++ b/gfx/angle/src/tests/perf_tests/third_party/perf/perf_test.cc
@@ -0,0 +1,239 @@
+// Copyright 2013 The Chromium 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 "perf_test.h"
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <vector>
+
+namespace {
+
+namespace base {
+
+std::string FormatString(const char *fmt, va_list vararg) {
+ static std::vector<char> buffer(512);
+
+ // Attempt to just print to the current buffer
+ int len = vsnprintf(&buffer[0], buffer.size(), fmt, vararg);
+ if (len < 0 || static_cast<size_t>(len) >= buffer.size()) {
+ // Buffer was not large enough, calculate the required size and resize the buffer
+ len = vsnprintf(NULL, 0, fmt, vararg);
+ buffer.resize(len + 1);
+
+ // Print again
+ vsnprintf(&buffer[0], buffer.size(), fmt, vararg);
+ }
+
+ return std::string(buffer.data(), len);
+}
+
+std::string StringPrintf(const char *fmt, ...) {
+ va_list vararg;
+ va_start(vararg, fmt);
+ std::string result = FormatString(fmt, vararg);
+ va_end(vararg);
+ return result;
+}
+
+std::string UintToString(unsigned int value) {
+ return StringPrintf("%u", value);
+}
+
+std::string DoubleToString(double value) {
+ return StringPrintf("%.10lf", value);
+}
+
+}
+
+std::string ResultsToString(const std::string& measurement,
+ const std::string& modifier,
+ const std::string& trace,
+ const std::string& values,
+ const std::string& prefix,
+ const std::string& suffix,
+ const std::string& units,
+ bool important) {
+ // <*>RESULT <graph_name>: <trace_name>= <value> <units>
+ // <*>RESULT <graph_name>: <trace_name>= {<mean>, <std deviation>} <units>
+ // <*>RESULT <graph_name>: <trace_name>= [<value>,value,value,...,] <units>
+ return base::StringPrintf("%sRESULT %s%s: %s= %s%s%s %s\n",
+ important ? "*" : "", measurement.c_str(), modifier.c_str(),
+ trace.c_str(), prefix.c_str(), values.c_str(), suffix.c_str(),
+ units.c_str());
+}
+
+void PrintResultsImpl(const std::string& measurement,
+ const std::string& modifier,
+ const std::string& trace,
+ const std::string& values,
+ const std::string& prefix,
+ const std::string& suffix,
+ const std::string& units,
+ bool important) {
+ fflush(stdout);
+ printf("%s", ResultsToString(measurement, modifier, trace, values,
+ prefix, suffix, units, important).c_str());
+ fflush(stdout);
+}
+
+} // namespace
+
+namespace perf_test {
+
+void PrintResult(const std::string& measurement,
+ const std::string& modifier,
+ const std::string& trace,
+ size_t value,
+ const std::string& units,
+ bool important) {
+ PrintResultsImpl(measurement,
+ modifier,
+ trace,
+ base::UintToString(static_cast<unsigned int>(value)),
+ std::string(),
+ std::string(),
+ units,
+ important);
+}
+
+void PrintResult(const std::string& measurement,
+ const std::string& modifier,
+ const std::string& trace,
+ double value,
+ const std::string& units,
+ bool important) {
+ PrintResultsImpl(measurement,
+ modifier,
+ trace,
+ base::DoubleToString(value),
+ std::string(),
+ std::string(),
+ units,
+ important);
+}
+
+void AppendResult(std::string& output,
+ const std::string& measurement,
+ const std::string& modifier,
+ const std::string& trace,
+ size_t value,
+ const std::string& units,
+ bool important) {
+ output += ResultsToString(
+ measurement,
+ modifier,
+ trace,
+ base::UintToString(static_cast<unsigned int>(value)),
+ std::string(),
+ std::string(),
+ units,
+ important);
+}
+
+void PrintResult(const std::string& measurement,
+ const std::string& modifier,
+ const std::string& trace,
+ const std::string& value,
+ const std::string& units,
+ bool important) {
+ PrintResultsImpl(measurement,
+ modifier,
+ trace,
+ value,
+ std::string(),
+ std::string(),
+ units,
+ important);
+}
+
+void AppendResult(std::string& output,
+ const std::string& measurement,
+ const std::string& modifier,
+ const std::string& trace,
+ const std::string& value,
+ const std::string& units,
+ bool important) {
+ output += ResultsToString(measurement,
+ modifier,
+ trace,
+ value,
+ std::string(),
+ std::string(),
+ units,
+ important);
+}
+
+void PrintResultMeanAndError(const std::string& measurement,
+ const std::string& modifier,
+ const std::string& trace,
+ const std::string& mean_and_error,
+ const std::string& units,
+ bool important) {
+ PrintResultsImpl(measurement, modifier, trace, mean_and_error,
+ "{", "}", units, important);
+}
+
+void AppendResultMeanAndError(std::string& output,
+ const std::string& measurement,
+ const std::string& modifier,
+ const std::string& trace,
+ const std::string& mean_and_error,
+ const std::string& units,
+ bool important) {
+ output += ResultsToString(measurement, modifier, trace, mean_and_error,
+ "{", "}", units, important);
+}
+
+void PrintResultList(const std::string& measurement,
+ const std::string& modifier,
+ const std::string& trace,
+ const std::string& values,
+ const std::string& units,
+ bool important) {
+ PrintResultsImpl(measurement, modifier, trace, values,
+ "[", "]", units, important);
+}
+
+void AppendResultList(std::string& output,
+ const std::string& measurement,
+ const std::string& modifier,
+ const std::string& trace,
+ const std::string& values,
+ const std::string& units,
+ bool important) {
+ output += ResultsToString(measurement, modifier, trace, values,
+ "[", "]", units, important);
+}
+
+void PrintSystemCommitCharge(const std::string& test_name,
+ size_t charge,
+ bool important) {
+ PrintSystemCommitCharge(stdout, test_name, charge, important);
+}
+
+void PrintSystemCommitCharge(FILE* target,
+ const std::string& test_name,
+ size_t charge,
+ bool important) {
+ fprintf(target, "%s", SystemCommitChargeToString(test_name, charge,
+ important).c_str());
+}
+
+std::string SystemCommitChargeToString(const std::string& test_name,
+ size_t charge,
+ bool important) {
+ std::string trace_name(test_name);
+ std::string output;
+ AppendResult(output,
+ "commit_charge",
+ std::string(),
+ "cc" + trace_name,
+ charge,
+ "kb",
+ important);
+ return output;
+}
+
+} // namespace perf_test
diff --git a/gfx/angle/src/tests/perf_tests/third_party/perf/perf_test.h b/gfx/angle/src/tests/perf_tests/third_party/perf/perf_test.h
new file mode 100755
index 000000000..36e2916c5
--- /dev/null
+++ b/gfx/angle/src/tests/perf_tests/third_party/perf/perf_test.h
@@ -0,0 +1,116 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef TESTING_PERF_PERF_TEST_H_
+#define TESTING_PERF_PERF_TEST_H_
+
+#include <string>
+
+namespace perf_test {
+
+// Prints numerical information to stdout in a controlled format, for
+// post-processing. |measurement| is a description of the quantity being
+// measured, e.g. "vm_peak"; |modifier| is provided as a convenience and
+// will be appended directly to the name of the |measurement|, e.g.
+// "_browser"; |trace| is a description of the particular data point, e.g.
+// "reference"; |value| is the measured value; and |units| is a description
+// of the units of measure, e.g. "bytes". If |important| is true, the output
+// line will be specially marked, to notify the post-processor. The strings
+// may be empty. They should not contain any colons (:) or equals signs (=).
+// A typical post-processing step would be to produce graphs of the data
+// produced for various builds, using the combined |measurement| + |modifier|
+// string to specify a particular graph and the |trace| to identify a trace
+// (i.e., data series) on that graph.
+void PrintResult(const std::string& measurement,
+ const std::string& modifier,
+ const std::string& trace,
+ size_t value,
+ const std::string& units,
+ bool important);
+void PrintResult(const std::string& measurement,
+ const std::string& modifier,
+ const std::string& trace,
+ double value,
+ const std::string& units,
+ bool important);
+
+void AppendResult(std::string& output,
+ const std::string& measurement,
+ const std::string& modifier,
+ const std::string& trace,
+ size_t value,
+ const std::string& units,
+ bool important);
+
+// Like the above version of PrintResult(), but takes a std::string value
+// instead of a size_t.
+void PrintResult(const std::string& measurement,
+ const std::string& modifier,
+ const std::string& trace,
+ const std::string& value,
+ const std::string& units,
+ bool important);
+
+void AppendResult(std::string& output,
+ const std::string& measurement,
+ const std::string& modifier,
+ const std::string& trace,
+ const std::string& value,
+ const std::string& units,
+ bool important);
+
+// Like PrintResult(), but prints a (mean, standard deviation) result pair.
+// The |<values>| should be two comma-separated numbers, the mean and
+// standard deviation (or other error metric) of the measurement.
+void PrintResultMeanAndError(const std::string& measurement,
+ const std::string& modifier,
+ const std::string& trace,
+ const std::string& mean_and_error,
+ const std::string& units,
+ bool important);
+
+void AppendResultMeanAndError(std::string& output,
+ const std::string& measurement,
+ const std::string& modifier,
+ const std::string& trace,
+ const std::string& mean_and_error,
+ const std::string& units,
+ bool important);
+
+// Like PrintResult(), but prints an entire list of results. The |values|
+// will generally be a list of comma-separated numbers. A typical
+// post-processing step might produce plots of their mean and standard
+// deviation.
+void PrintResultList(const std::string& measurement,
+ const std::string& modifier,
+ const std::string& trace,
+ const std::string& values,
+ const std::string& units,
+ bool important);
+
+void AppendResultList(std::string& output,
+ const std::string& measurement,
+ const std::string& modifier,
+ const std::string& trace,
+ const std::string& values,
+ const std::string& units,
+ bool important);
+
+// Prints memory commit charge stats for use by perf graphs.
+void PrintSystemCommitCharge(const std::string& test_name,
+ size_t charge,
+ bool important);
+
+void PrintSystemCommitCharge(FILE* target,
+ const std::string& test_name,
+ size_t charge,
+ bool important);
+
+std::string SystemCommitChargeToString(const std::string& test_name,
+ size_t charge,
+ bool important);
+
+} // namespace perf_test
+
+#endif // TESTING_PERF_PERF_TEST_H_