diff --git a/samples/multi_window/MultiWindow.cpp b/samples/multi_window/MultiWindow.cpp index 0d80c717f..87a20622e 100644 --- a/samples/multi_window/MultiWindow.cpp +++ b/samples/multi_window/MultiWindow.cpp @@ -14,6 +14,8 @@ #include "random_utils.h" #include "shader_utils.h" +using namespace angle; + class MultiWindowSample : public SampleApplication { public: diff --git a/samples/particle_system/ParticleSystem.cpp b/samples/particle_system/ParticleSystem.cpp index 4ecb9b333..5b6bdfdfa 100644 --- a/samples/particle_system/ParticleSystem.cpp +++ b/samples/particle_system/ParticleSystem.cpp @@ -23,6 +23,8 @@ #define _USE_MATH_DEFINES #include +using namespace angle; + class ParticleSystemSample : public SampleApplication { public: diff --git a/samples/sample_util/SampleApplication.cpp b/samples/sample_util/SampleApplication.cpp index ddf5a116a..386e20bfd 100644 --- a/samples/sample_util/SampleApplication.cpp +++ b/samples/sample_util/SampleApplication.cpp @@ -6,6 +6,7 @@ #include "SampleApplication.h" #include "EGLWindow.h" +#include "random_utils.h" SampleApplication::SampleApplication(const std::string &name, size_t width, @@ -29,6 +30,8 @@ SampleApplication::SampleApplication(const std::string &name, // Disable vsync mEGLWindow->setSwapInterval(0); + + angle::RandomInitFromTime(); } SampleApplication::~SampleApplication() diff --git a/src/tests/angle_perftests.gypi b/src/tests/angle_perftests.gypi index 16bc99069..587930724 100644 --- a/src/tests/angle_perftests.gypi +++ b/src/tests/angle_perftests.gypi @@ -21,6 +21,7 @@ '<(angle_path)/src/tests/perf_tests/DrawCallPerf.cpp', '<(angle_path)/src/tests/perf_tests/EGLInitializePerf.cpp', '<(angle_path)/src/tests/perf_tests/IndexConversionPerf.cpp', + '<(angle_path)/src/tests/perf_tests/InstancingPerf.cpp', '<(angle_path)/src/tests/perf_tests/InterleavedAttributeData.cpp', '<(angle_path)/src/tests/perf_tests/PointSprites.cpp', '<(angle_path)/src/tests/perf_tests/TexSubImage.cpp', diff --git a/src/tests/perf_tests/InstancingPerf.cpp b/src/tests/perf_tests/InstancingPerf.cpp new file mode 100644 index 000000000..a0bbecf72 --- /dev/null +++ b/src/tests/perf_tests/InstancingPerf.cpp @@ -0,0 +1,365 @@ +// +// 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 + +#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 - floor(l); + return (f > 0.5f ? 1.0f - f : f) * 4.0f - 1.0f; +} + +template +size_t VectorSizeBytes(const std::vector &vec) +{ + return sizeof(T) * vec.size(); +} + +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 ¶ms) +{ + os << params.suffix().substr(1); + return os; +} + +class InstancingPerfBenchmark : public ANGLERenderTest, + public ::testing::WithParamInterface +{ + public: + InstancingPerfBenchmark(); + + void initializeBenchmark() override; + void destroyBenchmark() override; + void beginDrawBenchmark() override; + void drawBenchmark() override; + + private: + GLuint mProgram; + std::vector mBuffers; + GLuint mNumPoints; + std::vector mTranslateData; + std::vector mSizeData; + std::vector mColorData; +}; + +InstancingPerfBenchmark::InstancingPerfBenchmark() + : ANGLERenderTest("InstancingPerf", GetParam()), mProgram(0), mNumPoints(75000) +{ + mRunTimeSeconds = GetParam().runTimeSeconds; +} + +void InstancingPerfBenchmark::initializeBenchmark() +{ + const auto ¶ms = GetParam(); + + ASSERT_TRUE(params.iterations > 0); + mDrawIterations = 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_TRUE(mProgram != 0); + + 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 indexData; + std::vector 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(RandomNegativeOneToOne(), RandomNegativeOneToOne(), + RandomNegativeOneToOne()); + 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(RandomNegativeOneToOne(), RandomNegativeOneToOne(), + RandomNegativeOneToOne()); + 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(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(getWindow()->getWidth()) / static_cast(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(mBuffers.size()), &mBuffers[0]); + mBuffers.clear(); + } +} + +void InstancingPerfBenchmark::beginDrawBenchmark() +{ + glClear(GL_COLOR_BUFFER_BIT); +} + +void InstancingPerfBenchmark::drawBenchmark() +{ + const auto ¶ms = 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(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/util/random_utils.cpp b/util/random_utils.cpp index e95ae274c..f03692797 100644 --- a/util/random_utils.cpp +++ b/util/random_utils.cpp @@ -3,20 +3,35 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // +// random_utils: +// Helper functions for random number generation. +// #include "random_utils.h" #include #include +namespace angle +{ + +void RandomInitFromTime() +{ + srand(static_cast(time(NULL))); +} + +float RandomFloat() +{ + return static_cast(rand()) / static_cast(RAND_MAX); +} + float RandomBetween(float min, float max) { - static bool randInitialized = false; - if (!randInitialized) - { - srand(static_cast(time(NULL))); - randInitialized = true; - } - - const size_t divisor = 10000; - return min + ((rand() % divisor) / static_cast(divisor)) * (max - min); + return min + RandomFloat() * (max - min); } + +float RandomNegativeOneToOne() +{ + return RandomBetween(0.0f, 1.0f); +} + +} // namespace angle diff --git a/util/random_utils.h b/util/random_utils.h index ea0c8537e..8f8c7836c 100644 --- a/util/random_utils.h +++ b/util/random_utils.h @@ -3,10 +3,22 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // +// random_utils: +// Helper functions for random number generation. +// #ifndef UTIL_RANDOM_UTILS_H #define UTIL_RANDOM_UTILS_H +namespace angle +{ + +// TODO(jmadill): Should make this a class +void RandomInitFromTime(); +float RandomFloat(); float RandomBetween(float min, float max); +float RandomNegativeOneToOne(); + +} // namespace angle #endif // UTIL_RANDOM_UTILS_H diff --git a/util/shader_utils.cpp b/util/shader_utils.cpp index 33f8bc69d..150d067e4 100644 --- a/util/shader_utils.cpp +++ b/util/shader_utils.cpp @@ -46,10 +46,16 @@ GLuint CompileShader(GLenum type, const std::string &source) GLint infoLogLength; glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLogLength); - std::vector infoLog(infoLogLength); - glGetShaderInfoLog(shader, static_cast(infoLog.size()), NULL, &infoLog[0]); - - std::cerr << "shader compilation failed: " << &infoLog[0]; + if (infoLogLength > 0) + { + std::vector infoLog(infoLogLength); + glGetShaderInfoLog(shader, static_cast(infoLog.size()), NULL, &infoLog[0]); + std::cerr << "shader compilation failed: " << &infoLog[0]; + } + else + { + std::cerr << "shader compilation failed. "; + } glDeleteShader(shader); shader = 0;