Commit e36b92d4 by Jamie Madill Committed by Commit Bot

D3D: Use a single D3D buffer per static vertex attribute.

The current caching logic stores multiple static attributes in a single buffer if the attributes share a D3D buffer - sometimes. If a buffer is 'committed' (has been used in a draw) then we would make a new D3D buffer for the next set of static attributes. Instead use a simpler scheme of a single D3D buffer for each static attribute. Also change rx::VertexBuffer to a reference counted class. This simplifies the caching logic for static vertex buffers (translated attributes) considerably. We only need to release the buffers when the ref count is zero, and ensure we track the ref count correctly when bound to D3D. This leads the way towards using a simpler dirty bit scheme for intelligent state updates, and less overhead doing work with buffer state updates. BUG=angleproject:1327 Change-Id: I99461d50b9663024eaa654cd56b42a63f1416d08 Reviewed-on: https://chromium-review.googlesource.com/330170Reviewed-by: 's avatarZhenyao Mo <zmo@chromium.org> Commit-Queue: Jamie Madill <jmadill@chromium.org>
parent c52a4026
......@@ -12,6 +12,7 @@
#include "common/utilities.h"
#include "libANGLE/renderer/d3d/IndexBuffer.h"
#include "libANGLE/renderer/d3d/VertexBuffer.h"
#include "libANGLE/renderer/d3d/RendererD3D.h"
namespace rx
{
......@@ -21,9 +22,7 @@ unsigned int BufferD3D::mNextSerial = 1;
BufferD3D::BufferD3D(BufferFactoryD3D *factory)
: BufferImpl(),
mFactory(factory),
mStaticVertexBuffer(nullptr),
mStaticIndexBuffer(nullptr),
mStaticBufferCache(nullptr),
mStaticBufferCacheTotalSize(0),
mStaticVertexBufferOutOfDate(false),
mUnmodifiedDataUse(0),
......@@ -34,20 +33,12 @@ BufferD3D::BufferD3D(BufferFactoryD3D *factory)
BufferD3D::~BufferD3D()
{
SafeDelete(mStaticVertexBuffer);
SafeDelete(mStaticIndexBuffer);
emptyStaticBufferCache();
}
void BufferD3D::emptyStaticBufferCache()
{
if (mStaticBufferCache != nullptr)
{
SafeDeleteContainer(*mStaticBufferCache);
SafeDelete(mStaticBufferCache);
}
mStaticVertexBuffers.clear();
mStaticBufferCacheTotalSize = 0;
}
......@@ -82,9 +73,11 @@ void BufferD3D::updateD3DBufferUsage(GLenum usage)
void BufferD3D::initializeStaticData()
{
if (!mStaticVertexBuffer)
if (mStaticVertexBuffers.empty())
{
mStaticVertexBuffer = new StaticVertexBufferInterface(mFactory);
auto newStaticBuffer = new StaticVertexBufferInterface(mFactory);
mStaticVertexBuffers.push_back(
std::unique_ptr<StaticVertexBufferInterface>(newStaticBuffer));
}
if (!mStaticIndexBuffer)
{
......@@ -97,130 +90,69 @@ StaticIndexBufferInterface *BufferD3D::getStaticIndexBuffer()
return mStaticIndexBuffer;
}
StaticVertexBufferInterface *BufferD3D::getStaticVertexBuffer(
const gl::VertexAttribute &attribute,
D3DStaticBufferCreationType creationType)
StaticVertexBufferInterface *BufferD3D::getStaticVertexBuffer(const gl::VertexAttribute &attribute)
{
if (!mStaticVertexBuffer)
if (mStaticVertexBuffers.empty())
{
// Early out if there aren't any static buffers at all
ASSERT(mStaticBufferCache == nullptr);
return nullptr;
}
if (mStaticBufferCache == nullptr && !mStaticVertexBuffer->isCommitted())
// Early out, the attribute can be added to mStaticVertexBuffer.
if (mStaticVertexBuffers.size() == 1 && mStaticVertexBuffers[0]->empty())
{
// Early out, the attribute can be added to mStaticVertexBuffer or is already in there
return mStaticVertexBuffer;
return mStaticVertexBuffers[0].get();
}
// At this point, see if any of the existing static buffers contains the attribute data
// Cache size limiting: track the total allocated buffer sizes.
size_t currentTotalSize = 0;
// If the default static vertex buffer contains the attribute, then return it
if (mStaticVertexBuffer->lookupAttribute(attribute, nullptr))
{
return mStaticVertexBuffer;
}
if (mStaticBufferCache != nullptr)
// At this point, see if any of the existing static buffers contains the attribute data
// If there is a cached static buffer that already contains the attribute, then return it
for (const auto &staticBuffer : mStaticVertexBuffers)
{
// If there is a cached static buffer that already contains the attribute, then return it
for (StaticVertexBufferInterface *staticBuffer : *mStaticBufferCache)
if (staticBuffer->matchesAttribute(attribute))
{
if (staticBuffer->lookupAttribute(attribute, nullptr))
{
return staticBuffer;
}
return staticBuffer.get();
}
}
if (!mStaticVertexBuffer->isCommitted())
{
// None of the existing static buffers contain the attribute data and we are able to add
// the data to mStaticVertexBuffer, so we should just do so
return mStaticVertexBuffer;
currentTotalSize += staticBuffer->getBufferSize();
}
// At this point, we must create a new static buffer for the attribute data
if (creationType != D3D_BUFFER_CREATE_IF_NECESSARY)
{
return nullptr;
}
// Cache size limiting: Clean-up threshold is four times the base buffer size, with a minimum.
ASSERT(IsUnsignedMultiplicationSafe(getSize(), static_cast<size_t>(4u)));
size_t sizeThreshold = std::max(getSize() * 4u, static_cast<size_t>(0x1000u));
ASSERT(mStaticVertexBuffer);
ASSERT(mStaticVertexBuffer->isCommitted());
unsigned int staticVertexBufferSize = mStaticVertexBuffer->getBufferSize();
if (IsUnsignedAdditionSafe(staticVertexBufferSize, mStaticBufferCacheTotalSize))
// If we're past the threshold, clear the buffer cache. Note that this will release buffers
// that are currenly bound, and in an edge case can even translate the same attribute twice
// in the same draw call. It will not delete currently bound buffers, however, because they
// are ref counted.
if (currentTotalSize > sizeThreshold)
{
// 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)
{
if (mStaticBufferCache == nullptr)
{
mStaticBufferCache = new std::vector<StaticVertexBufferInterface *>();
}
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;
}
emptyStaticBufferCache();
}
// At this point:
// - mStaticVertexBuffer is committed and can't be altered
// - mStaticBufferCache is full (or nearly overflowing)
// The inputted attribute should be put in some static buffer at some point, but it can't
// go in one right now, since mStaticBufferCache is full and we can't delete mStaticVertexBuffer
// in case another attribute is relying upon it for the current draw.
// We therefore mark mStaticVertexBuffer for deletion at the next possible time.
mStaticVertexBufferOutOfDate = true;
return nullptr;
// At this point, we must create a new static buffer for the attribute data.
auto newStaticBuffer = new StaticVertexBufferInterface(mFactory);
newStaticBuffer->setAttribute(attribute);
mStaticVertexBuffers.push_back(std::unique_ptr<StaticVertexBufferInterface>(newStaticBuffer));
return newStaticBuffer;
}
void BufferD3D::reinitOutOfDateStaticData()
void BufferD3D::invalidateStaticData()
{
if (mStaticVertexBufferOutOfDate)
{
// During the last draw the caller tried to use some attribute with static data, but neither
// the static buffer cache nor mStaticVertexBuffer contained that data.
// Therefore, invalidate mStaticVertexBuffer so that if the caller tries to use that
// attribute in the next draw, it'll successfully get put into mStaticVertexBuffer.
invalidateStaticData(D3D_BUFFER_INVALIDATE_DEFAULT_BUFFER_ONLY);
mStaticVertexBufferOutOfDate = false;
}
}
emptyStaticBufferCache();
void BufferD3D::invalidateStaticData(D3DBufferInvalidationType invalidationType)
{
if (invalidationType == D3D_BUFFER_INVALIDATE_WHOLE_CACHE && mStaticBufferCache != nullptr)
if (mStaticIndexBuffer && mStaticIndexBuffer->getBufferSize() != 0)
{
emptyStaticBufferCache();
SafeDelete(mStaticIndexBuffer);
}
if ((mStaticVertexBuffer && mStaticVertexBuffer->getBufferSize() != 0) || (mStaticIndexBuffer && mStaticIndexBuffer->getBufferSize() != 0))
// If the buffer was created with a static usage then we recreate the static
// buffers so that they are populated the next time we use this buffer.
if (mUsage == D3D_BUFFER_USAGE_STATIC)
{
SafeDelete(mStaticVertexBuffer);
SafeDelete(mStaticIndexBuffer);
// If the buffer was created with a static usage then we recreate the static
// buffers so that they are populated the next time we use this buffer.
if (mUsage == D3D_BUFFER_USAGE_STATIC)
{
initializeStaticData();
}
initializeStaticData();
}
mUnmodifiedDataUse = 0;
......@@ -229,12 +161,8 @@ void BufferD3D::invalidateStaticData(D3DBufferInvalidationType invalidationType)
// Creates static buffers if sufficient used data has been left unmodified
void BufferD3D::promoteStaticUsage(int dataSize)
{
if (!mStaticVertexBuffer && !mStaticIndexBuffer)
if (mStaticVertexBuffers.empty() && !mStaticIndexBuffer)
{
// There isn't any scenario that involves promoting static usage and the static buffer cache
// being non-empty
ASSERT(mStaticBufferCache == nullptr);
mUnmodifiedDataUse += dataSize;
if (mUnmodifiedDataUse > 3 * getSize())
......@@ -261,4 +189,4 @@ gl::Error BufferD3D::getIndexRange(GLenum type,
return gl::Error(GL_NO_ERROR);
}
}
} // namespace rx
......@@ -27,18 +27,6 @@ enum D3DBufferUsage
D3D_BUFFER_USAGE_DYNAMIC,
};
enum D3DBufferInvalidationType
{
D3D_BUFFER_INVALIDATE_WHOLE_CACHE,
D3D_BUFFER_INVALIDATE_DEFAULT_BUFFER_ONLY,
};
enum D3DStaticBufferCreationType
{
D3D_BUFFER_CREATE_IF_NECESSARY,
D3D_BUFFER_DO_NOT_CREATE,
};
class BufferD3D : public BufferImpl
{
public:
......@@ -52,13 +40,11 @@ class BufferD3D : public BufferImpl
virtual void markTransformFeedbackUsage() = 0;
virtual gl::Error getData(const uint8_t **outData) = 0;
StaticVertexBufferInterface *getStaticVertexBuffer(const gl::VertexAttribute &attribute,
D3DStaticBufferCreationType creationType);
StaticVertexBufferInterface *getStaticVertexBuffer(const gl::VertexAttribute &attribute);
StaticIndexBufferInterface *getStaticIndexBuffer();
void initializeStaticData();
void invalidateStaticData(D3DBufferInvalidationType invalidationType);
void reinitOutOfDateStaticData();
void invalidateStaticData();
void promoteStaticUsage(int dataSize);
......@@ -79,9 +65,8 @@ class BufferD3D : public BufferImpl
unsigned int mSerial;
static unsigned int mNextSerial;
StaticVertexBufferInterface *mStaticVertexBuffer;
std::vector<std::unique_ptr<StaticVertexBufferInterface>> mStaticVertexBuffers;
StaticIndexBufferInterface *mStaticIndexBuffer;
std::vector<StaticVertexBufferInterface *> *mStaticBufferCache;
unsigned int mStaticBufferCacheTotalSize;
unsigned int mStaticVertexBufferOutOfDate;
unsigned int mUnmodifiedDataUse;
......
......@@ -227,7 +227,7 @@ gl::Error IndexDataManager::prepareIndexData(GLenum srcType,
if (staticBufferInitialized && !staticBufferUsable)
{
buffer->invalidateStaticData(D3D_BUFFER_INVALIDATE_WHOLE_CACHE);
buffer->invalidateStaticData();
staticBuffer = nullptr;
}
......
......@@ -55,10 +55,11 @@ RendererD3D::~RendererD3D()
void RendererD3D::cleanup()
{
mTranslatedAttribCache.clear();
mScratchMemoryBuffer.resize(0);
for (auto &incompleteTexture : mIncompleteTextures)
{
incompleteTexture.second.set(NULL);
incompleteTexture.second.set(nullptr);
}
mIncompleteTextures.clear();
......
......@@ -29,11 +29,12 @@ namespace rx
{
class BufferFactoryD3D;
// Use a ref-counting scheme with self-deletion on release. We do this so that we can more
// easily manage the static buffer cache, without deleting currently bound buffers.
class VertexBuffer : angle::NonCopyable
{
public:
VertexBuffer();
virtual ~VertexBuffer();
virtual gl::Error initialize(unsigned int size, bool dynamicUsage) = 0;
......@@ -54,12 +55,18 @@ class VertexBuffer : angle::NonCopyable
// This may be overridden (e.g. by VertexBuffer11) if necessary.
virtual void hintUnmapResource() { };
// Reference counting.
void addRef();
void release();
protected:
void updateSerial();
virtual ~VertexBuffer();
private:
unsigned int mSerial;
static unsigned int mNextSerial;
unsigned int mRefCount;
};
class VertexBufferInterface : angle::NonCopyable
......@@ -68,39 +75,22 @@ class VertexBufferInterface : angle::NonCopyable
VertexBufferInterface(BufferFactoryD3D *factory, bool dynamic);
virtual ~VertexBufferInterface();
gl::Error reserveVertexSpace(const gl::VertexAttribute &attribute, GLsizei count, GLsizei instances);
unsigned int getBufferSize() const;
bool empty() const { return getBufferSize() == 0; }
unsigned int getSerial() const;
virtual gl::Error storeVertexAttributes(const gl::VertexAttribute &attrib,
GLenum currentValueType,
GLint start,
GLsizei count,
GLsizei instances,
unsigned int *outStreamOffset,
const uint8_t *sourceData);
VertexBuffer* getVertexBuffer() const;
protected:
virtual gl::Error reserveSpace(unsigned int size) = 0;
unsigned int getWritePosition() const;
void setWritePosition(unsigned int writePosition);
gl::Error discard();
gl::Error setBufferSize(unsigned int size);
private:
gl::ErrorOrResult<unsigned int> getSpaceRequired(const gl::VertexAttribute &attrib,
GLsizei count,
GLsizei instances) const;
BufferFactoryD3D *const mFactory;
VertexBuffer* mVertexBuffer;
unsigned int mWritePosition;
unsigned int mReservedSpace;
VertexBuffer *mVertexBuffer;
bool mDynamic;
};
......@@ -110,8 +100,23 @@ class StreamingVertexBufferInterface : public VertexBufferInterface
StreamingVertexBufferInterface(BufferFactoryD3D *factory, std::size_t initialSize);
~StreamingVertexBufferInterface();
protected:
gl::Error storeDynamicAttribute(const gl::VertexAttribute &attrib,
GLenum currentValueType,
GLint start,
GLsizei count,
GLsizei instances,
unsigned int *outStreamOffset,
const uint8_t *sourceData);
gl::Error reserveVertexSpace(const gl::VertexAttribute &attribute,
GLsizei count,
GLsizei instances);
private:
gl::Error reserveSpace(unsigned int size);
unsigned int mWritePosition;
unsigned int mReservedSpace;
};
class StaticVertexBufferInterface : public VertexBufferInterface
......@@ -120,41 +125,35 @@ class StaticVertexBufferInterface : public VertexBufferInterface
explicit StaticVertexBufferInterface(BufferFactoryD3D *factory);
~StaticVertexBufferInterface();
gl::Error storeVertexAttributes(const gl::VertexAttribute &attrib,
GLenum currentValueType,
GLint start,
GLsizei count,
GLsizei instances,
unsigned int *outStreamOffset,
const uint8_t *sourceData) override;
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; }
gl::Error storeStaticAttribute(const gl::VertexAttribute &attrib,
GLint start,
GLsizei count,
GLsizei instances,
const uint8_t *sourceData);
protected:
gl::Error reserveSpace(unsigned int size);
bool matchesAttribute(const gl::VertexAttribute &attribute) const;
void setAttribute(const gl::VertexAttribute &attribute);
private:
struct VertexElement
class AttributeSignature final : angle::NonCopyable
{
public:
AttributeSignature();
bool matchesAttribute(const gl::VertexAttribute &attrib) const;
void set(const gl::VertexAttribute &attrib);
private:
GLenum type;
GLuint size;
GLuint stride;
bool normalized;
bool pureInteger;
size_t attributeOffset;
unsigned int streamOffset;
size_t offset;
};
bool mIsCommitted;
std::vector<VertexElement> mCache;
AttributeSignature mSignature;
};
}
} // namespace rx
#endif // LIBANGLE_RENDERER_D3D_VERTEXBUFFER_H_
......@@ -28,19 +28,24 @@ class BufferFactoryD3D;
class StreamingVertexBufferInterface;
class VertexBuffer;
class VertexBufferBinding final
{
public:
VertexBufferBinding();
VertexBufferBinding(const VertexBufferBinding &other);
~VertexBufferBinding();
void set(VertexBuffer *vertexBuffer);
VertexBuffer *get() const;
VertexBufferBinding &operator=(const VertexBufferBinding &other);
private:
VertexBuffer *mBoundVertexBuffer;
};
struct TranslatedAttribute
{
TranslatedAttribute()
: active(false),
attribute(NULL),
currentValueType(GL_NONE),
offset(0),
stride(0),
vertexBuffer(NULL),
storage(NULL),
serial(0),
divisor(0)
{}
TranslatedAttribute();
bool active;
......@@ -49,7 +54,7 @@ struct TranslatedAttribute
unsigned int offset;
unsigned int stride; // 0 means not to advance the read pointer at all
VertexBuffer *vertexBuffer;
VertexBufferBinding vertexBuffer;
BufferD3D *storage;
unsigned int serial;
unsigned int divisor;
......@@ -91,7 +96,7 @@ class VertexDataManager : angle::NonCopyable
TranslatedAttribute *translated,
CurrentValueState *cachedState);
void hintUnmapAllResources(const std::vector<gl::VertexAttribute> &vertexAttributes);
void unmapStreamingBuffer();
BufferFactoryD3D *const mFactory;
......
......@@ -269,13 +269,14 @@ Buffer11::~Buffer11()
gl::Error Buffer11::setData(const void *data, size_t size, GLenum usage)
{
updateD3DBufferUsage(usage);
gl::Error error = setSubData(data, size, 0);
if (error.isError())
{
return error;
}
updateD3DBufferUsage(usage);
return error;
}
......@@ -361,7 +362,7 @@ gl::Error Buffer11::setSubData(const void *data, size_t size, size_t offset)
}
mSize = std::max(mSize, requiredSize);
invalidateStaticData(D3D_BUFFER_INVALIDATE_WHOLE_CACHE);
invalidateStaticData();
return gl::Error(GL_NO_ERROR);
}
......@@ -417,7 +418,7 @@ gl::Error Buffer11::copySubData(BufferImpl *source,
copyDest->setDataRevision(copyDest->getDataRevision() + 1);
mSize = std::max<size_t>(mSize, destOffset + size);
invalidateStaticData(D3D_BUFFER_INVALIDATE_WHOLE_CACHE);
invalidateStaticData();
return gl::Error(GL_NO_ERROR);
}
......@@ -457,7 +458,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(D3D_BUFFER_INVALIDATE_WHOLE_CACHE);
invalidateStaticData();
}
uint8_t *mappedBuffer = mMappedStorage->map(offset, length, access);
......@@ -492,7 +493,7 @@ void Buffer11::markTransformFeedbackUsage()
transformFeedbackStorage->setDataRevision(transformFeedbackStorage->getDataRevision() + 1);
}
invalidateStaticData(D3D_BUFFER_INVALIDATE_WHOLE_CACHE);
invalidateStaticData();
}
void Buffer11::markBufferUsage()
......@@ -848,7 +849,7 @@ bool Buffer11::supportsDirectBinding() const
// Do not support direct buffers for dynamic data. The streaming buffer
// offers better performance for data which changes every frame.
// Check for absence of static buffer interfaces to detect dynamic data.
return (mStaticVertexBuffer && mStaticIndexBuffer);
return (!mStaticVertexBuffers.empty() && mStaticIndexBuffer);
}
Buffer11::BufferStorage::BufferStorage(Renderer11 *renderer, BufferUsage usage)
......
......@@ -252,8 +252,7 @@ gl::Error InputLayoutCache::applyVertexBuffers(
if (attribIndex < unsortedAttributes.size() && attrib.active)
{
VertexBuffer11 *vertexBuffer = GetAs<VertexBuffer11>(attrib.vertexBuffer);
Buffer11 *bufferStorage = attrib.storage ? GetAs<Buffer11>(attrib.storage) : nullptr;
Buffer11 *bufferStorage = attrib.storage ? GetAs<Buffer11>(attrib.storage) : nullptr;
// If indexed pointsprite emulation is active, then we need to take a less efficent code path.
// Emulated indexed pointsprite rendering requires that the vertex buffers match exactly to
......@@ -261,7 +260,8 @@ gl::Error InputLayoutCache::applyVertexBuffers(
// on the number of points indicated by the index list or how many duplicates are found on the index list.
if (bufferStorage == nullptr)
{
buffer = vertexBuffer->getBuffer();
ASSERT(attrib.vertexBuffer.get());
buffer = GetAs<VertexBuffer11>(attrib.vertexBuffer.get())->getBuffer();
}
else if (instancedPointSpritesActive && (indexInfo != nullptr))
{
......
......@@ -21,7 +21,6 @@ class VertexBuffer11 : public VertexBuffer
{
public:
explicit VertexBuffer11(Renderer11 *const renderer);
~VertexBuffer11() override;
gl::Error initialize(unsigned int size, bool dynamicUsage) override;
......@@ -42,6 +41,7 @@ class VertexBuffer11 : public VertexBuffer
ID3D11Buffer *getBuffer() const;
private:
~VertexBuffer11() override;
gl::Error mapResource();
Renderer11 *const mRenderer;
......
......@@ -38,7 +38,7 @@ gl::Error Buffer9::setData(const void* data, size_t size, GLenum usage)
memcpy(mMemory.data(), data, size);
}
invalidateStaticData(D3D_BUFFER_INVALIDATE_WHOLE_CACHE);
invalidateStaticData();
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(D3D_BUFFER_INVALIDATE_WHOLE_CACHE);
invalidateStaticData();
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(D3D_BUFFER_INVALIDATE_WHOLE_CACHE);
invalidateStaticData();
return gl::Error(GL_NO_ERROR);
}
......
......@@ -19,7 +19,6 @@ class VertexBuffer9 : public VertexBuffer
{
public:
explicit VertexBuffer9(Renderer9 *renderer);
~VertexBuffer9() override;
gl::Error initialize(unsigned int size, bool dynamicUsage) override;
......@@ -38,6 +37,7 @@ class VertexBuffer9 : public VertexBuffer
IDirect3DVertexBuffer9 *getBuffer() const;
private:
~VertexBuffer9() override;
Renderer9 *mRenderer;
IDirect3DVertexBuffer9 *mVertexBuffer;
......
......@@ -147,7 +147,7 @@ gl::Error VertexDeclarationCache::applyDeclaration(IDirect3DDevice9 *device,
}
}
VertexBuffer9 *vertexBuffer = GetAs<VertexBuffer9>(attributes[i].vertexBuffer);
VertexBuffer9 *vertexBuffer = GetAs<VertexBuffer9>(attributes[i].vertexBuffer.get());
if (mAppliedVBs[stream].serial != attributes[i].serial ||
mAppliedVBs[stream].stride != attributes[i].stride ||
......
......@@ -31,6 +31,22 @@ GLsizei TypeStride(GLenum attribType)
}
}
template <typename T>
GLfloat Normalize(T value)
{
static_assert(std::is_integral<T>::value, "Integer required.");
if (std::is_signed<T>::value)
{
typedef typename std::make_unsigned<T>::type unsigned_type;
return (2.0f * static_cast<GLfloat>(value) + 1.0f) /
static_cast<GLfloat>(std::numeric_limits<unsigned_type>::max());
}
else
{
return static_cast<GLfloat>(value) / static_cast<GLfloat>(std::numeric_limits<T>::max());
}
}
class VertexAttributeTest : public ANGLETest
{
protected:
......@@ -246,7 +262,7 @@ TEST_P(VertexAttributeTest, UnsignedByteNormalized)
GLfloat expectedData[mVertexCount];
for (size_t i = 0; i < mVertexCount; i++)
{
expectedData[i] = inputData[i] / 255.0f;
expectedData[i] = Normalize(inputData[i]);
}
TestData data = {GL_UNSIGNED_BYTE, GL_TRUE, Source::IMMEDIATE, inputData, expectedData};
......@@ -272,7 +288,7 @@ TEST_P(VertexAttributeTest, ByteNormalized)
GLfloat expectedData[mVertexCount];
for (size_t i = 0; i < mVertexCount; i++)
{
expectedData[i] = ((2.0f * inputData[i]) + 1.0f) / 255.0f;
expectedData[i] = Normalize(inputData[i]);
}
TestData data = {GL_BYTE, GL_TRUE, Source::IMMEDIATE, inputData, expectedData};
......@@ -298,7 +314,7 @@ TEST_P(VertexAttributeTest, UnsignedShortNormalized)
GLfloat expectedData[mVertexCount];
for (size_t i = 0; i < mVertexCount; i++)
{
expectedData[i] = inputData[i] / 65535.0f;
expectedData[i] = Normalize(inputData[i]);
}
TestData data = {GL_UNSIGNED_SHORT, GL_TRUE, Source::IMMEDIATE, inputData, expectedData};
......@@ -324,7 +340,7 @@ TEST_P(VertexAttributeTest, ShortNormalized)
GLfloat expectedData[mVertexCount];
for (size_t i = 0; i < mVertexCount; i++)
{
expectedData[i] = ((2.0f * inputData[i]) + 1.0f) / 65535.0f;
expectedData[i] = Normalize(inputData[i]);
}
TestData data = {GL_SHORT, GL_TRUE, Source::IMMEDIATE, inputData, expectedData};
......@@ -360,7 +376,7 @@ TEST_P(VertexAttributeTestES3, IntNormalized)
GLfloat expectedData[mVertexCount];
for (size_t i = 0; i < mVertexCount; i++)
{
expectedData[i] = ((2.0f * inputData[i]) + 1.0f) / static_cast<GLfloat>(0xFFFFFFFFu);
expectedData[i] = Normalize(inputData[i]);
}
TestData data = {GL_INT, GL_TRUE, Source::BUFFER, inputData, expectedData};
......@@ -392,7 +408,7 @@ TEST_P(VertexAttributeTestES3, UnsignedIntNormalized)
GLfloat expectedData[mVertexCount];
for (size_t i = 0; i < mVertexCount; i++)
{
expectedData[i] = static_cast<GLfloat>(inputData[i]) / static_cast<GLfloat>(hi);
expectedData[i] = Normalize(inputData[i]);
}
TestData data = {GL_UNSIGNED_INT, GL_TRUE, Source::BUFFER, inputData, expectedData};
......@@ -470,6 +486,164 @@ TEST_P(VertexAttributeTest, SimpleBindAttribLocation)
EXPECT_PIXEL_NEAR(0, 0, 128, 0, 0, 255, 1);
}
class VertexAttributeCachingTest : public VertexAttributeTest
{
protected:
VertexAttributeCachingTest() {}
template <typename DestT>
static std::vector<GLfloat> GetExpectedData(const std::vector<GLubyte> &srcData,
GLenum attribType,
GLboolean normalized);
};
// static
template <typename DestT>
std::vector<GLfloat> VertexAttributeCachingTest::GetExpectedData(
const std::vector<GLubyte> &srcData,
GLenum attribType,
GLboolean normalized)
{
std::vector<GLfloat> expectedData;
const DestT *typedSrcPtr = reinterpret_cast<const DestT *>(srcData.data());
size_t iterations = srcData.size() / TypeStride(attribType);
if (normalized)
{
for (size_t index = 0; index < iterations; ++index)
{
expectedData.push_back(Normalize(typedSrcPtr[index]));
}
}
else
{
for (size_t index = 0; index < iterations; ++index)
{
expectedData.push_back(static_cast<GLfloat>(typedSrcPtr[index]));
}
}
return expectedData;
}
// In D3D11, we must sometimes translate buffer data into static attribute caches. We also use a
// cache management scheme which garbage collects old attributes after we start using too much
// cache data. This test tries to make as many attribute caches from a single buffer as possible
// to stress-test the caching code.
TEST_P(VertexAttributeCachingTest, BufferMulticaching)
{
if (IsAMD() && isOpenGL())
{
std::cout << "Test skipped on AMD OpenGL." << std::endl;
return;
}
glBindBuffer(GL_ARRAY_BUFFER, mBuffer);
std::vector<GLubyte> srcData;
for (size_t count = 0; count < 4; ++count)
{
for (GLubyte i = 0; i < std::numeric_limits<GLubyte>::max(); ++i)
{
srcData.push_back(i);
}
}
glBufferData(GL_ARRAY_BUFFER, srcData.size(), srcData.data(), GL_STATIC_DRAW);
GLint viewportSize[4];
glGetIntegerv(GL_VIEWPORT, viewportSize);
struct AttribData
{
AttribData(GLenum typeIn, GLint sizeIn, GLboolean normalizedIn, GLsizei strideIn)
: type(typeIn), size(sizeIn), normalized(normalizedIn), stride(strideIn)
{
}
GLenum type;
GLint size;
GLboolean normalized;
GLsizei stride;
};
std::vector<GLenum> attribTypes;
attribTypes.push_back(GL_BYTE);
attribTypes.push_back(GL_UNSIGNED_BYTE);
attribTypes.push_back(GL_SHORT);
attribTypes.push_back(GL_UNSIGNED_SHORT);
if (getClientVersion() >= 3)
{
attribTypes.push_back(GL_INT);
attribTypes.push_back(GL_UNSIGNED_INT);
}
std::vector<AttribData> datas;
const GLint maxSize = 4;
const GLsizei maxStride = 4;
for (GLenum attribType : attribTypes)
{
for (GLint attribSize = 1; attribSize <= maxSize; ++attribSize)
{
for (GLsizei stride = 1; stride <= maxStride; ++stride)
{
datas.push_back(AttribData(attribType, attribSize, GL_FALSE, stride));
if (attribType != GL_FLOAT)
{
datas.push_back(AttribData(attribType, attribSize, GL_TRUE, stride));
}
}
}
}
std::map<GLenum, std::vector<GLfloat>> expectedData;
std::map<GLenum, std::vector<GLfloat>> normExpectedData;
expectedData[GL_BYTE] = GetExpectedData<GLbyte>(srcData, GL_BYTE, GL_FALSE);
expectedData[GL_UNSIGNED_BYTE] = GetExpectedData<GLubyte>(srcData, GL_UNSIGNED_BYTE, GL_FALSE);
expectedData[GL_SHORT] = GetExpectedData<GLshort>(srcData, GL_SHORT, GL_FALSE);
expectedData[GL_UNSIGNED_SHORT] =
GetExpectedData<GLushort>(srcData, GL_UNSIGNED_SHORT, GL_FALSE);
expectedData[GL_INT] = GetExpectedData<GLint>(srcData, GL_INT, GL_FALSE);
expectedData[GL_UNSIGNED_INT] = GetExpectedData<GLuint>(srcData, GL_UNSIGNED_INT, GL_FALSE);
normExpectedData[GL_BYTE] = GetExpectedData<GLbyte>(srcData, GL_BYTE, GL_TRUE);
normExpectedData[GL_UNSIGNED_BYTE] =
GetExpectedData<GLubyte>(srcData, GL_UNSIGNED_BYTE, GL_TRUE);
normExpectedData[GL_SHORT] = GetExpectedData<GLshort>(srcData, GL_SHORT, GL_TRUE);
normExpectedData[GL_UNSIGNED_SHORT] =
GetExpectedData<GLushort>(srcData, GL_UNSIGNED_SHORT, GL_TRUE);
normExpectedData[GL_INT] = GetExpectedData<GLint>(srcData, GL_INT, GL_TRUE);
normExpectedData[GL_UNSIGNED_INT] = GetExpectedData<GLuint>(srcData, GL_UNSIGNED_INT, GL_TRUE);
glEnableVertexAttribArray(mTestAttrib);
glEnableVertexAttribArray(mExpectedAttrib);
ASSERT_GL_NO_ERROR();
for (const auto &data : datas)
{
const auto &expected =
(data.normalized) ? normExpectedData[data.type] : expectedData[data.type];
GLsizei baseStride = static_cast<GLsizei>(data.size) * data.stride;
GLsizei stride = TypeStride(data.type) * baseStride;
glBindBuffer(GL_ARRAY_BUFFER, mBuffer);
glVertexAttribPointer(mTestAttrib, data.size, data.type, data.normalized, stride, nullptr);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glVertexAttribPointer(mExpectedAttrib, data.size, GL_FLOAT, GL_FALSE,
sizeof(GLfloat) * baseStride, expected.data());
drawQuad(mProgram, "position", 0.5f);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_EQ(getWindowWidth() / 2, getWindowHeight() / 2, 255, 255, 255, 255);
}
}
// Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against.
// D3D11 Feature Level 9_3 uses different D3D formats for vertex attribs compared to Feature Levels 10_0+, so we should test them separately.
ANGLE_INSTANTIATE_TEST(VertexAttributeTest,
......@@ -483,4 +657,10 @@ ANGLE_INSTANTIATE_TEST(VertexAttributeTest,
ANGLE_INSTANTIATE_TEST(VertexAttributeTestES3, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES());
ANGLE_INSTANTIATE_TEST(VertexAttributeCachingTest,
ES2_D3D9(),
ES2_D3D11(),
ES3_D3D11(),
ES3_OPENGL());
} // anonymous namespace
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