Commit 868651d3 by Austin Kinross Committed by Jamie Madill

In D3D, cache static vertex buffers to prevent wasteful recreation

BUG=angleproject:197 Change-Id: I66cd10609b2edbcf12b99530eafe1727511fe515 Reviewed-on: https://chromium-review.googlesource.com/296503Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Tested-by: 's avatarAustin Kinross <aukinros@microsoft.com> Tested-by: 's avatarJamie Madill <jmadill@chromium.org>
parent 42590dd7
......@@ -8,6 +8,7 @@
#include "libANGLE/renderer/d3d/BufferD3D.h"
#include "common/mathutil.h"
#include "common/utilities.h"
#include "libANGLE/renderer/d3d/IndexBuffer.h"
#include "libANGLE/renderer/d3d/VertexBuffer.h"
......@@ -22,6 +23,7 @@ BufferD3D::BufferD3D(BufferFactoryD3D *factory)
mFactory(factory),
mStaticVertexBuffer(nullptr),
mStaticIndexBuffer(nullptr),
mStaticBufferCacheTotalSize(0),
mUnmodifiedDataUse(0),
mUsage(D3D_BUFFER_USAGE_STATIC)
{
......@@ -32,6 +34,11 @@ BufferD3D::~BufferD3D()
{
SafeDelete(mStaticVertexBuffer);
SafeDelete(mStaticIndexBuffer);
// Empty the cache of static vertex buffers too
SafeDeleteContainer(mStaticBufferCache);
mStaticBufferCacheTotalSize = 0;
}
void BufferD3D::updateSerial()
......@@ -75,8 +82,80 @@ void BufferD3D::initializeStaticData()
}
}
void BufferD3D::invalidateStaticData()
StaticIndexBufferInterface *BufferD3D::getStaticIndexBuffer()
{
return mStaticIndexBuffer;
}
StaticVertexBufferInterface *BufferD3D::getStaticVertexBuffer(const gl::VertexAttribute &attribute)
{
// If the default static vertex buffer contains the attribute, then return it
if (mStaticVertexBuffer && mStaticVertexBuffer->lookupAttribute(attribute, nullptr))
{
return mStaticVertexBuffer;
}
// If there is a cached static buffer that already contains the attribute, then return it
for (StaticVertexBufferInterface *staticBuffer : mStaticBufferCache)
{
if (staticBuffer->lookupAttribute(attribute, nullptr))
{
return staticBuffer;
}
}
if (mStaticVertexBuffer)
{
// If the default static vertex buffer hasn't been committed, then we can
// still use it
if (!mStaticVertexBuffer->isCommitted())
{
return mStaticVertexBuffer;
}
else
{
unsigned int staticVertexBufferSize = mStaticVertexBuffer->getBufferSize();
if (IsUnsignedAdditionSafe(staticVertexBufferSize, mStaticBufferCacheTotalSize))
{
// Ensure that the total size of the static buffer cache remains less than 4x the
// size of the original buffer
unsigned int maxStaticCacheSize =
IsUnsignedMultiplicationSafe(static_cast<unsigned int>(getSize()), 4u)
? 4u * static_cast<unsigned int>(getSize())
: std::numeric_limits<unsigned int>::max();
// We can't reuse the default static vertex buffer, so we add it to the cache
if (staticVertexBufferSize + mStaticBufferCacheTotalSize <= maxStaticCacheSize)
{
mStaticBufferCacheTotalSize += staticVertexBufferSize;
mStaticBufferCache.push_back(mStaticVertexBuffer);
mStaticVertexBuffer = nullptr;
// Then reinitialize the static buffers to create a new static vertex buffer
initializeStaticData();
}
}
}
}
// Return the default static vertex buffer
return mStaticVertexBuffer;
}
void BufferD3D::invalidateStaticData(bool invalidateWholeCache)
{
if (invalidateWholeCache)
{
// Empty the cache of static vertex buffers too
for (StaticVertexBufferInterface *staticBuffer : mStaticBufferCache)
{
SafeDelete(staticBuffer);
}
mStaticBufferCache.clear();
mStaticBufferCacheTotalSize = 0;
}
if ((mStaticVertexBuffer && mStaticVertexBuffer->getBufferSize() != 0) || (mStaticIndexBuffer && mStaticIndexBuffer->getBufferSize() != 0))
{
SafeDelete(mStaticVertexBuffer);
......@@ -98,6 +177,10 @@ void BufferD3D::promoteStaticUsage(int dataSize)
{
if (!mStaticVertexBuffer && !mStaticIndexBuffer)
{
// There isn't any scenario that involves promoting static usage and the static buffer cache
// being non-empty
ASSERT(mStaticBufferCache.empty());
mUnmodifiedDataUse += dataSize;
if (mUnmodifiedDataUse > 3 * getSize())
......
......@@ -13,6 +13,7 @@
#include "libANGLE/renderer/BufferImpl.h"
#include <stdint.h>
#include <vector>
namespace rx
{
......@@ -39,11 +40,11 @@ class BufferD3D : public BufferImpl
virtual void markTransformFeedbackUsage() = 0;
virtual gl::Error getData(const uint8_t **outData) = 0;
StaticVertexBufferInterface *getStaticVertexBuffer() { return mStaticVertexBuffer; }
StaticIndexBufferInterface *getStaticIndexBuffer() { return mStaticIndexBuffer; }
StaticVertexBufferInterface *getStaticVertexBuffer(const gl::VertexAttribute &attribute);
StaticIndexBufferInterface *getStaticIndexBuffer();
void initializeStaticData();
void invalidateStaticData();
void invalidateStaticData(bool invalidateWholeCache);
void promoteStaticUsage(int dataSize);
gl::Error getIndexRange(GLenum type,
......@@ -62,6 +63,9 @@ class BufferD3D : public BufferImpl
StaticVertexBufferInterface *mStaticVertexBuffer;
StaticIndexBufferInterface *mStaticIndexBuffer;
std::vector<StaticVertexBufferInterface *> mStaticBufferCache;
unsigned int mStaticBufferCacheTotalSize;
unsigned int mUnmodifiedDataUse;
D3DBufferUsage mUsage;
};
......
......@@ -193,7 +193,7 @@ gl::Error IndexDataManager::prepareIndexData(GLenum srcType, GLsizei count, gl::
if (staticBufferInitialized && !staticBufferUsable)
{
buffer->invalidateStaticData();
buffer->invalidateStaticData(true);
staticBuffer = nullptr;
}
......
......@@ -243,7 +243,7 @@ gl::Error StreamingVertexBufferInterface::reserveSpace(unsigned int size)
}
StaticVertexBufferInterface::StaticVertexBufferInterface(BufferFactoryD3D *factory)
: VertexBufferInterface(factory, false)
: VertexBufferInterface(factory, false), mIsCommitted(false)
{
}
......@@ -321,4 +321,11 @@ gl::Error StaticVertexBufferInterface::storeVertexAttributes(const gl::VertexAtt
return gl::Error(GL_NO_ERROR);
}
void StaticVertexBufferInterface::commit()
{
if (getBufferSize() > 0)
{
mIsCommitted = true;
}
}
}
......@@ -135,6 +135,11 @@ class StaticVertexBufferInterface : public VertexBufferInterface
bool lookupAttribute(const gl::VertexAttribute &attribute, unsigned int* outStreamFffset);
// If a static vertex buffer is committed then no more attribute data can be added to it
// A new static vertex buffer should be created instead
void commit();
bool isCommitted() { return mIsCommitted; }
protected:
gl::Error reserveSpace(unsigned int size);
......@@ -151,6 +156,7 @@ class StaticVertexBufferInterface : public VertexBufferInterface
unsigned int streamOffset;
};
bool mIsCommitted;
std::vector<VertexElement> mCache;
};
......
......@@ -88,7 +88,8 @@ void VertexDataManager::hintUnmapAllResources(const std::vector<gl::VertexAttrib
{
gl::Buffer *buffer = translated->attribute->buffer.get();
BufferD3D *storage = buffer ? GetImplAs<BufferD3D>(buffer) : nullptr;
StaticVertexBufferInterface *staticBuffer = storage ? storage->getStaticVertexBuffer() : nullptr;
StaticVertexBufferInterface *staticBuffer =
storage ? storage->getStaticVertexBuffer(*translated->attribute) : nullptr;
if (staticBuffer)
{
......@@ -197,6 +198,22 @@ gl::Error VertexDataManager::prepareVertexData(const gl::State &state,
}
}
// Commit all the static vertex buffers. This fixes them in size/contents, and forces ANGLE
// to use a new static buffer (or recreate the static buffers) next time
for (const TranslatedAttribute *activeAttrib : mActiveEnabledAttributes)
{
const gl::VertexAttribute &attrib = *activeAttrib->attribute;
gl::Buffer *buffer = attrib.buffer.get();
BufferD3D *storage = buffer ? GetImplAs<BufferD3D>(buffer) : nullptr;
StaticVertexBufferInterface *staticBuffer =
storage ? storage->getStaticVertexBuffer(attrib) : nullptr;
if (staticBuffer)
{
staticBuffer->commit();
}
}
// Hint to unmap all the resources
hintUnmapAllResources(vertexAttributes);
......@@ -223,14 +240,15 @@ void VertexDataManager::invalidateMatchingStaticData(const gl::VertexAttribute &
if (buffer)
{
BufferD3D *bufferImpl = GetImplAs<BufferD3D>(buffer);
StaticVertexBufferInterface *staticBuffer = bufferImpl->getStaticVertexBuffer();
StaticVertexBufferInterface *staticBuffer = bufferImpl->getStaticVertexBuffer(attrib);
if (staticBuffer &&
staticBuffer->getBufferSize() > 0 &&
!staticBuffer->lookupAttribute(attrib, NULL) &&
!staticBuffer->directStoragePossible(attrib, currentValue.Type))
{
bufferImpl->invalidateStaticData();
// This must be the default static vertex buffer, and we must invalidate it
bufferImpl->invalidateStaticData(false);
}
}
}
......@@ -242,7 +260,8 @@ gl::Error VertexDataManager::reserveSpaceForAttrib(const TranslatedAttribute &tr
const gl::VertexAttribute &attrib = *translatedAttrib.attribute;
gl::Buffer *buffer = attrib.buffer.get();
BufferD3D *bufferImpl = buffer ? GetImplAs<BufferD3D>(buffer) : NULL;
StaticVertexBufferInterface *staticBuffer = bufferImpl ? bufferImpl->getStaticVertexBuffer() : NULL;
StaticVertexBufferInterface *staticBuffer =
bufferImpl ? bufferImpl->getStaticVertexBuffer(attrib) : NULL;
VertexBufferInterface *vertexBuffer = staticBuffer ? staticBuffer : static_cast<VertexBufferInterface*>(mStreamingBuffer);
if (!vertexBuffer->directStoragePossible(attrib, translatedAttrib.currentValueType))
......@@ -291,7 +310,8 @@ gl::Error VertexDataManager::storeAttribute(TranslatedAttribute *translated,
ASSERT(attrib.enabled);
BufferD3D *storage = buffer ? GetImplAs<BufferD3D>(buffer) : NULL;
StaticVertexBufferInterface *staticBuffer = storage ? storage->getStaticVertexBuffer() : NULL;
StaticVertexBufferInterface *staticBuffer =
storage ? storage->getStaticVertexBuffer(attrib) : NULL;
VertexBufferInterface *vertexBuffer = staticBuffer ? staticBuffer : static_cast<VertexBufferInterface*>(mStreamingBuffer);
bool directStorage = vertexBuffer->directStoragePossible(attrib, translated->currentValueType);
......
......@@ -361,7 +361,7 @@ gl::Error Buffer11::setSubData(const void *data, size_t size, size_t offset)
}
mSize = std::max(mSize, requiredSize);
invalidateStaticData();
invalidateStaticData(true);
return gl::Error(GL_NO_ERROR);
}
......@@ -417,7 +417,7 @@ gl::Error Buffer11::copySubData(BufferImpl *source,
copyDest->setDataRevision(copyDest->getDataRevision() + 1);
mSize = std::max<size_t>(mSize, destOffset + size);
invalidateStaticData();
invalidateStaticData(true);
return gl::Error(GL_NO_ERROR);
}
......@@ -457,7 +457,7 @@ gl::Error Buffer11::mapRange(size_t offset, size_t length, GLbitfield access, GL
{
// Update the data revision immediately, since the data might be changed at any time
mMappedStorage->setDataRevision(mMappedStorage->getDataRevision() + 1);
invalidateStaticData();
invalidateStaticData(true);
}
uint8_t *mappedBuffer = mMappedStorage->map(offset, length, access);
......@@ -492,7 +492,7 @@ void Buffer11::markTransformFeedbackUsage()
transformFeedbackStorage->setDataRevision(transformFeedbackStorage->getDataRevision() + 1);
}
invalidateStaticData();
invalidateStaticData(true);
}
void Buffer11::markBufferUsage()
......
......@@ -38,7 +38,7 @@ gl::Error Buffer9::setData(const void* data, size_t size, GLenum usage)
memcpy(mMemory.data(), data, size);
}
invalidateStaticData();
invalidateStaticData(true);
updateD3DBufferUsage(usage);
return gl::Error(GL_NO_ERROR);
......@@ -66,7 +66,7 @@ gl::Error Buffer9::setSubData(const void* data, size_t size, size_t offset)
memcpy(mMemory.data() + offset, data, size);
}
invalidateStaticData();
invalidateStaticData(true);
return gl::Error(GL_NO_ERROR);
}
......@@ -79,7 +79,7 @@ gl::Error Buffer9::copySubData(BufferImpl* source, GLintptr sourceOffset, GLintp
memcpy(mMemory.data() + destOffset, sourceBuffer->mMemory.data() + sourceOffset, size);
invalidateStaticData();
invalidateStaticData(true);
return gl::Error(GL_NO_ERROR);
}
......
......@@ -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/InterleavedAttributeData.cpp',
'<(angle_path)/src/tests/perf_tests/PointSprites.cpp',
'<(angle_path)/src/tests/perf_tests/TexSubImage.cpp',
'<(angle_path)/src/tests/perf_tests/third_party/perf/perf_test.cc',
......
......@@ -45,7 +45,7 @@ struct BufferSubDataParams final : public RenderTestParams
unsigned int iterations;
};
inline std::ostream &operator<<(std::ostream &os, const BufferSubDataParams &params)
std::ostream &operator<<(std::ostream &os, const BufferSubDataParams &params)
{
os << params.suffix().substr(1);
return os;
......
......@@ -55,7 +55,7 @@ struct DrawCallPerfParams final : public RenderTestParams
int numTris;
};
inline std::ostream &operator<<(std::ostream &os, const DrawCallPerfParams &params)
std::ostream &operator<<(std::ostream &os, const DrawCallPerfParams &params)
{
os << params.suffix().substr(1);
return os;
......
//
// 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 beginDrawBenchmark() 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_TRUE(mPointSpriteProgram != 0);
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::beginDrawBenchmark()
{
// Clear the color buffer
glClear(GL_COLOR_BUFFER_BIT);
}
void InterleavedAttributeDataBenchmark::drawBenchmark()
{
for (size_t k = 0; k < 4; 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) % _countof(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());
} // namespace
......@@ -45,7 +45,7 @@ struct PointSpritesParams final : public RenderTestParams
unsigned int iterations;
};
inline std::ostream &operator<<(std::ostream &os, const PointSpritesParams &params)
std::ostream &operator<<(std::ostream &os, const PointSpritesParams &params)
{
os << params.suffix().substr(1);
return os;
......
......@@ -44,7 +44,7 @@ struct TexSubImageParams final : public RenderTestParams
unsigned int iterations;
};
inline std::ostream &operator<<(std::ostream &os, const TexSubImageParams &params)
std::ostream &operator<<(std::ostream &os, const TexSubImageParams &params)
{
os << params.suffix().substr(1);
return os;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment