blob: ddc68546a533cc0b6eecf623cc9be302d6978698 [file] [log] [blame]
//
// 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 <cassert>
#include <sstream>
#include "ANGLEPerfTest.h"
#include "shader_utils.h"
namespace
{
struct BufferSubDataParams final : public RenderTestParams
{
std::string suffix() const override;
GLboolean vertexNormalized;
GLenum vertexType;
GLint vertexComponentCount;
unsigned int updateRate;
// static parameters
GLsizeiptr updateSize;
GLsizeiptr bufferSize;
unsigned int iterations;
};
class BufferSubDataBenchmark : public ANGLERenderTest,
public ::testing::WithParamInterface<BufferSubDataParams>
{
public:
BufferSubDataBenchmark();
bool initializeBenchmark() override;
void destroyBenchmark() override;
void beginDrawBenchmark() 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 NULL;
}
}
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(NULL),
mNumTris(0)
{
}
bool BufferSubDataBenchmark::initializeBenchmark()
{
const auto &params = GetParam();
assert(params.vertexComponentCount > 1);
assert(params.iterations > 0);
mDrawIterations = 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);
if (!mProgram)
{
return false;
}
// 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 = GetVertexData(params.vertexType,
params.vertexComponentCount,
params.vertexNormalized, &data);
mNumTris = 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);
GLenum glErr = glGetError();
if (glErr != GL_NO_ERROR)
{
return false;
}
return true;
}
void BufferSubDataBenchmark::destroyBenchmark()
{
const auto &params = GetParam();
// print static parameters
printResult("update_size", static_cast<size_t>(params.updateSize), "b", false);
printResult("buffer_size", static_cast<size_t>(params.bufferSize), "b", false);
printResult("iterations", static_cast<size_t>(params.iterations), "updates", false);
glDeleteProgram(mProgram);
glDeleteBuffers(1, &mBuffer);
delete[] mUpdateData;
}
void BufferSubDataBenchmark::beginDrawBenchmark()
{
// Clear the color buffer
glClear(GL_COLOR_BUFFER_BIT);
}
void BufferSubDataBenchmark::drawBenchmark()
{
const auto &params = GetParam();
for (unsigned int it = 0; it < params.iterations; it++)
{
if (params.updateSize > 0 && ((mNumFrames % params.updateRate) == 0))
{
glBufferSubData(GL_ARRAY_BUFFER, 0, params.updateSize, mUpdateData);
}
glDrawArrays(GL_TRIANGLES, 0, 3 * mNumTris);
}
}
BufferSubDataParams BufferUpdateD3D11Params()
{
BufferSubDataParams params;
params.glesMajorVersion = 2;
params.widowWidth = 1280;
params.windowHeight = 720;
params.requestedRenderer = EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE;
params.deviceType = EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE;
params.vertexType = GL_FLOAT;
params.vertexComponentCount = 4;
params.vertexNormalized = GL_FALSE;
params.updateSize = 3000;
params.bufferSize = 67000000;
params.iterations = 10;
params.updateRate = 1;
return params;
}
BufferSubDataParams BufferUpdateD3D9Params()
{
BufferSubDataParams params;
params.glesMajorVersion = 2;
params.widowWidth = 1280;
params.windowHeight = 720;
params.requestedRenderer = EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE;
params.deviceType = EGL_PLATFORM_ANGLE_DEVICE_TYPE_HARDWARE_ANGLE;
params.vertexType = GL_FLOAT;
params.vertexComponentCount = 4;
params.vertexNormalized = GL_FALSE;
params.updateSize = 3000;
params.bufferSize = 67000000;
params.iterations = 10;
params.updateRate = 1;
return params;
}
TEST_P(BufferSubDataBenchmark, Run)
{
run();
}
INSTANTIATE_TEST_CASE_P(BufferUpdates,
BufferSubDataBenchmark,
::testing::Values(BufferUpdateD3D11Params(), BufferUpdateD3D9Params()));
} // namespace