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 @@ ...@@ -12,6 +12,7 @@
#include "common/utilities.h" #include "common/utilities.h"
#include "libANGLE/renderer/d3d/IndexBuffer.h" #include "libANGLE/renderer/d3d/IndexBuffer.h"
#include "libANGLE/renderer/d3d/VertexBuffer.h" #include "libANGLE/renderer/d3d/VertexBuffer.h"
#include "libANGLE/renderer/d3d/RendererD3D.h"
namespace rx namespace rx
{ {
...@@ -21,9 +22,7 @@ unsigned int BufferD3D::mNextSerial = 1; ...@@ -21,9 +22,7 @@ unsigned int BufferD3D::mNextSerial = 1;
BufferD3D::BufferD3D(BufferFactoryD3D *factory) BufferD3D::BufferD3D(BufferFactoryD3D *factory)
: BufferImpl(), : BufferImpl(),
mFactory(factory), mFactory(factory),
mStaticVertexBuffer(nullptr),
mStaticIndexBuffer(nullptr), mStaticIndexBuffer(nullptr),
mStaticBufferCache(nullptr),
mStaticBufferCacheTotalSize(0), mStaticBufferCacheTotalSize(0),
mStaticVertexBufferOutOfDate(false), mStaticVertexBufferOutOfDate(false),
mUnmodifiedDataUse(0), mUnmodifiedDataUse(0),
...@@ -34,20 +33,12 @@ BufferD3D::BufferD3D(BufferFactoryD3D *factory) ...@@ -34,20 +33,12 @@ BufferD3D::BufferD3D(BufferFactoryD3D *factory)
BufferD3D::~BufferD3D() BufferD3D::~BufferD3D()
{ {
SafeDelete(mStaticVertexBuffer);
SafeDelete(mStaticIndexBuffer); SafeDelete(mStaticIndexBuffer);
emptyStaticBufferCache();
} }
void BufferD3D::emptyStaticBufferCache() void BufferD3D::emptyStaticBufferCache()
{ {
if (mStaticBufferCache != nullptr) mStaticVertexBuffers.clear();
{
SafeDeleteContainer(*mStaticBufferCache);
SafeDelete(mStaticBufferCache);
}
mStaticBufferCacheTotalSize = 0; mStaticBufferCacheTotalSize = 0;
} }
...@@ -82,9 +73,11 @@ void BufferD3D::updateD3DBufferUsage(GLenum usage) ...@@ -82,9 +73,11 @@ void BufferD3D::updateD3DBufferUsage(GLenum usage)
void BufferD3D::initializeStaticData() 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) if (!mStaticIndexBuffer)
{ {
...@@ -97,130 +90,69 @@ StaticIndexBufferInterface *BufferD3D::getStaticIndexBuffer() ...@@ -97,130 +90,69 @@ StaticIndexBufferInterface *BufferD3D::getStaticIndexBuffer()
return mStaticIndexBuffer; return mStaticIndexBuffer;
} }
StaticVertexBufferInterface *BufferD3D::getStaticVertexBuffer( StaticVertexBufferInterface *BufferD3D::getStaticVertexBuffer(const gl::VertexAttribute &attribute)
const gl::VertexAttribute &attribute,
D3DStaticBufferCreationType creationType)
{ {
if (!mStaticVertexBuffer) if (mStaticVertexBuffers.empty())
{ {
// Early out if there aren't any static buffers at all // Early out if there aren't any static buffers at all
ASSERT(mStaticBufferCache == nullptr);
return 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 mStaticVertexBuffers[0].get();
return mStaticVertexBuffer;
} }
// 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 // At this point, see if any of the existing static buffers contains the attribute data
if (mStaticVertexBuffer->lookupAttribute(attribute, nullptr)) // If there is a cached static buffer that already contains the attribute, then return it
{ for (const auto &staticBuffer : mStaticVertexBuffers)
return mStaticVertexBuffer;
}
if (mStaticBufferCache != nullptr)
{ {
// If there is a cached static buffer that already contains the attribute, then return it if (staticBuffer->matchesAttribute(attribute))
for (StaticVertexBufferInterface *staticBuffer : *mStaticBufferCache)
{ {
if (staticBuffer->lookupAttribute(attribute, nullptr)) return staticBuffer.get();
{
return staticBuffer;
}
} }
}
if (!mStaticVertexBuffer->isCommitted()) currentTotalSize += staticBuffer->getBufferSize();
{
// 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;
} }
// At this point, we must create a new static buffer for the attribute data // Cache size limiting: Clean-up threshold is four times the base buffer size, with a minimum.
if (creationType != D3D_BUFFER_CREATE_IF_NECESSARY) ASSERT(IsUnsignedMultiplicationSafe(getSize(), static_cast<size_t>(4u)));
{ size_t sizeThreshold = std::max(getSize() * 4u, static_cast<size_t>(0x1000u));
return nullptr;
}
ASSERT(mStaticVertexBuffer); // If we're past the threshold, clear the buffer cache. Note that this will release buffers
ASSERT(mStaticVertexBuffer->isCommitted()); // that are currenly bound, and in an edge case can even translate the same attribute twice
unsigned int staticVertexBufferSize = mStaticVertexBuffer->getBufferSize(); // in the same draw call. It will not delete currently bound buffers, however, because they
if (IsUnsignedAdditionSafe(staticVertexBufferSize, mStaticBufferCacheTotalSize)) // are ref counted.
if (currentTotalSize > sizeThreshold)
{ {
// Ensure that the total size of the static buffer cache remains less than 4x the emptyStaticBufferCache();
// 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;
}
} }
// At this point: // At this point, we must create a new static buffer for the attribute data.
// - mStaticVertexBuffer is committed and can't be altered auto newStaticBuffer = new StaticVertexBufferInterface(mFactory);
// - mStaticBufferCache is full (or nearly overflowing) newStaticBuffer->setAttribute(attribute);
// The inputted attribute should be put in some static buffer at some point, but it can't mStaticVertexBuffers.push_back(std::unique_ptr<StaticVertexBufferInterface>(newStaticBuffer));
// go in one right now, since mStaticBufferCache is full and we can't delete mStaticVertexBuffer return newStaticBuffer;
// 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;
} }
void BufferD3D::reinitOutOfDateStaticData() void BufferD3D::invalidateStaticData()
{ {
if (mStaticVertexBufferOutOfDate) emptyStaticBufferCache();
{
// 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;
}
}
void BufferD3D::invalidateStaticData(D3DBufferInvalidationType invalidationType) if (mStaticIndexBuffer && mStaticIndexBuffer->getBufferSize() != 0)
{
if (invalidationType == D3D_BUFFER_INVALIDATE_WHOLE_CACHE && mStaticBufferCache != nullptr)
{ {
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); initializeStaticData();
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();
}
} }
mUnmodifiedDataUse = 0; mUnmodifiedDataUse = 0;
...@@ -229,12 +161,8 @@ void BufferD3D::invalidateStaticData(D3DBufferInvalidationType invalidationType) ...@@ -229,12 +161,8 @@ void BufferD3D::invalidateStaticData(D3DBufferInvalidationType invalidationType)
// Creates static buffers if sufficient used data has been left unmodified // Creates static buffers if sufficient used data has been left unmodified
void BufferD3D::promoteStaticUsage(int dataSize) 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; mUnmodifiedDataUse += dataSize;
if (mUnmodifiedDataUse > 3 * getSize()) if (mUnmodifiedDataUse > 3 * getSize())
...@@ -261,4 +189,4 @@ gl::Error BufferD3D::getIndexRange(GLenum type, ...@@ -261,4 +189,4 @@ gl::Error BufferD3D::getIndexRange(GLenum type,
return gl::Error(GL_NO_ERROR); return gl::Error(GL_NO_ERROR);
} }
} } // namespace rx
...@@ -27,18 +27,6 @@ enum D3DBufferUsage ...@@ -27,18 +27,6 @@ enum D3DBufferUsage
D3D_BUFFER_USAGE_DYNAMIC, 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 class BufferD3D : public BufferImpl
{ {
public: public:
...@@ -52,13 +40,11 @@ class BufferD3D : public BufferImpl ...@@ -52,13 +40,11 @@ class BufferD3D : public BufferImpl
virtual void markTransformFeedbackUsage() = 0; virtual void markTransformFeedbackUsage() = 0;
virtual gl::Error getData(const uint8_t **outData) = 0; virtual gl::Error getData(const uint8_t **outData) = 0;
StaticVertexBufferInterface *getStaticVertexBuffer(const gl::VertexAttribute &attribute, StaticVertexBufferInterface *getStaticVertexBuffer(const gl::VertexAttribute &attribute);
D3DStaticBufferCreationType creationType);
StaticIndexBufferInterface *getStaticIndexBuffer(); StaticIndexBufferInterface *getStaticIndexBuffer();
void initializeStaticData(); void initializeStaticData();
void invalidateStaticData(D3DBufferInvalidationType invalidationType); void invalidateStaticData();
void reinitOutOfDateStaticData();
void promoteStaticUsage(int dataSize); void promoteStaticUsage(int dataSize);
...@@ -79,9 +65,8 @@ class BufferD3D : public BufferImpl ...@@ -79,9 +65,8 @@ class BufferD3D : public BufferImpl
unsigned int mSerial; unsigned int mSerial;
static unsigned int mNextSerial; static unsigned int mNextSerial;
StaticVertexBufferInterface *mStaticVertexBuffer; std::vector<std::unique_ptr<StaticVertexBufferInterface>> mStaticVertexBuffers;
StaticIndexBufferInterface *mStaticIndexBuffer; StaticIndexBufferInterface *mStaticIndexBuffer;
std::vector<StaticVertexBufferInterface *> *mStaticBufferCache;
unsigned int mStaticBufferCacheTotalSize; unsigned int mStaticBufferCacheTotalSize;
unsigned int mStaticVertexBufferOutOfDate; unsigned int mStaticVertexBufferOutOfDate;
unsigned int mUnmodifiedDataUse; unsigned int mUnmodifiedDataUse;
......
...@@ -227,7 +227,7 @@ gl::Error IndexDataManager::prepareIndexData(GLenum srcType, ...@@ -227,7 +227,7 @@ gl::Error IndexDataManager::prepareIndexData(GLenum srcType,
if (staticBufferInitialized && !staticBufferUsable) if (staticBufferInitialized && !staticBufferUsable)
{ {
buffer->invalidateStaticData(D3D_BUFFER_INVALIDATE_WHOLE_CACHE); buffer->invalidateStaticData();
staticBuffer = nullptr; staticBuffer = nullptr;
} }
......
...@@ -55,10 +55,11 @@ RendererD3D::~RendererD3D() ...@@ -55,10 +55,11 @@ RendererD3D::~RendererD3D()
void RendererD3D::cleanup() void RendererD3D::cleanup()
{ {
mTranslatedAttribCache.clear();
mScratchMemoryBuffer.resize(0); mScratchMemoryBuffer.resize(0);
for (auto &incompleteTexture : mIncompleteTextures) for (auto &incompleteTexture : mIncompleteTextures)
{ {
incompleteTexture.second.set(NULL); incompleteTexture.second.set(nullptr);
} }
mIncompleteTextures.clear(); mIncompleteTextures.clear();
......
...@@ -29,11 +29,12 @@ namespace rx ...@@ -29,11 +29,12 @@ namespace rx
{ {
class BufferFactoryD3D; 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 class VertexBuffer : angle::NonCopyable
{ {
public: public:
VertexBuffer(); VertexBuffer();
virtual ~VertexBuffer();
virtual gl::Error initialize(unsigned int size, bool dynamicUsage) = 0; virtual gl::Error initialize(unsigned int size, bool dynamicUsage) = 0;
...@@ -54,12 +55,18 @@ class VertexBuffer : angle::NonCopyable ...@@ -54,12 +55,18 @@ class VertexBuffer : angle::NonCopyable
// This may be overridden (e.g. by VertexBuffer11) if necessary. // This may be overridden (e.g. by VertexBuffer11) if necessary.
virtual void hintUnmapResource() { }; virtual void hintUnmapResource() { };
// Reference counting.
void addRef();
void release();
protected: protected:
void updateSerial(); void updateSerial();
virtual ~VertexBuffer();
private: private:
unsigned int mSerial; unsigned int mSerial;
static unsigned int mNextSerial; static unsigned int mNextSerial;
unsigned int mRefCount;
}; };
class VertexBufferInterface : angle::NonCopyable class VertexBufferInterface : angle::NonCopyable
...@@ -68,39 +75,22 @@ class VertexBufferInterface : angle::NonCopyable ...@@ -68,39 +75,22 @@ class VertexBufferInterface : angle::NonCopyable
VertexBufferInterface(BufferFactoryD3D *factory, bool dynamic); VertexBufferInterface(BufferFactoryD3D *factory, bool dynamic);
virtual ~VertexBufferInterface(); virtual ~VertexBufferInterface();
gl::Error reserveVertexSpace(const gl::VertexAttribute &attribute, GLsizei count, GLsizei instances);
unsigned int getBufferSize() const; unsigned int getBufferSize() const;
bool empty() const { return getBufferSize() == 0; }
unsigned int getSerial() const; 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; VertexBuffer* getVertexBuffer() const;
protected: protected:
virtual gl::Error reserveSpace(unsigned int size) = 0;
unsigned int getWritePosition() const;
void setWritePosition(unsigned int writePosition);
gl::Error discard(); gl::Error discard();
gl::Error setBufferSize(unsigned int size); gl::Error setBufferSize(unsigned int size);
gl::ErrorOrResult<unsigned int> getSpaceRequired(const gl::VertexAttribute &attrib,
private: GLsizei count,
GLsizei instances) const;
BufferFactoryD3D *const mFactory; BufferFactoryD3D *const mFactory;
VertexBuffer *mVertexBuffer;
VertexBuffer* mVertexBuffer;
unsigned int mWritePosition;
unsigned int mReservedSpace;
bool mDynamic; bool mDynamic;
}; };
...@@ -110,8 +100,23 @@ class StreamingVertexBufferInterface : public VertexBufferInterface ...@@ -110,8 +100,23 @@ class StreamingVertexBufferInterface : public VertexBufferInterface
StreamingVertexBufferInterface(BufferFactoryD3D *factory, std::size_t initialSize); StreamingVertexBufferInterface(BufferFactoryD3D *factory, std::size_t initialSize);
~StreamingVertexBufferInterface(); ~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); gl::Error reserveSpace(unsigned int size);
unsigned int mWritePosition;
unsigned int mReservedSpace;
}; };
class StaticVertexBufferInterface : public VertexBufferInterface class StaticVertexBufferInterface : public VertexBufferInterface
...@@ -120,41 +125,35 @@ class StaticVertexBufferInterface : public VertexBufferInterface ...@@ -120,41 +125,35 @@ class StaticVertexBufferInterface : public VertexBufferInterface
explicit StaticVertexBufferInterface(BufferFactoryD3D *factory); explicit StaticVertexBufferInterface(BufferFactoryD3D *factory);
~StaticVertexBufferInterface(); ~StaticVertexBufferInterface();
gl::Error storeVertexAttributes(const gl::VertexAttribute &attrib, gl::Error storeStaticAttribute(const gl::VertexAttribute &attrib,
GLenum currentValueType, GLint start,
GLint start, GLsizei count,
GLsizei count, GLsizei instances,
GLsizei instances, const uint8_t *sourceData);
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; }
protected: bool matchesAttribute(const gl::VertexAttribute &attribute) const;
gl::Error reserveSpace(unsigned int size); void setAttribute(const gl::VertexAttribute &attribute);
private: 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; GLenum type;
GLuint size; GLuint size;
GLuint stride; GLuint stride;
bool normalized; bool normalized;
bool pureInteger; bool pureInteger;
size_t attributeOffset; size_t offset;
unsigned int streamOffset;
}; };
bool mIsCommitted; AttributeSignature mSignature;
std::vector<VertexElement> mCache;
}; };
} } // namespace rx
#endif // LIBANGLE_RENDERER_D3D_VERTEXBUFFER_H_ #endif // LIBANGLE_RENDERER_D3D_VERTEXBUFFER_H_
...@@ -28,19 +28,24 @@ class BufferFactoryD3D; ...@@ -28,19 +28,24 @@ class BufferFactoryD3D;
class StreamingVertexBufferInterface; class StreamingVertexBufferInterface;
class VertexBuffer; 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 struct TranslatedAttribute
{ {
TranslatedAttribute() TranslatedAttribute();
: active(false),
attribute(NULL),
currentValueType(GL_NONE),
offset(0),
stride(0),
vertexBuffer(NULL),
storage(NULL),
serial(0),
divisor(0)
{}
bool active; bool active;
...@@ -49,7 +54,7 @@ struct TranslatedAttribute ...@@ -49,7 +54,7 @@ struct TranslatedAttribute
unsigned int offset; unsigned int offset;
unsigned int stride; // 0 means not to advance the read pointer at all unsigned int stride; // 0 means not to advance the read pointer at all
VertexBuffer *vertexBuffer; VertexBufferBinding vertexBuffer;
BufferD3D *storage; BufferD3D *storage;
unsigned int serial; unsigned int serial;
unsigned int divisor; unsigned int divisor;
...@@ -91,7 +96,7 @@ class VertexDataManager : angle::NonCopyable ...@@ -91,7 +96,7 @@ class VertexDataManager : angle::NonCopyable
TranslatedAttribute *translated, TranslatedAttribute *translated,
CurrentValueState *cachedState); CurrentValueState *cachedState);
void hintUnmapAllResources(const std::vector<gl::VertexAttribute> &vertexAttributes); void unmapStreamingBuffer();
BufferFactoryD3D *const mFactory; BufferFactoryD3D *const mFactory;
......
...@@ -269,13 +269,14 @@ Buffer11::~Buffer11() ...@@ -269,13 +269,14 @@ Buffer11::~Buffer11()
gl::Error Buffer11::setData(const void *data, size_t size, GLenum usage) gl::Error Buffer11::setData(const void *data, size_t size, GLenum usage)
{ {
updateD3DBufferUsage(usage);
gl::Error error = setSubData(data, size, 0); gl::Error error = setSubData(data, size, 0);
if (error.isError()) if (error.isError())
{ {
return error; return error;
} }
updateD3DBufferUsage(usage);
return error; return error;
} }
...@@ -361,7 +362,7 @@ gl::Error Buffer11::setSubData(const void *data, size_t size, size_t offset) ...@@ -361,7 +362,7 @@ gl::Error Buffer11::setSubData(const void *data, size_t size, size_t offset)
} }
mSize = std::max(mSize, requiredSize); mSize = std::max(mSize, requiredSize);
invalidateStaticData(D3D_BUFFER_INVALIDATE_WHOLE_CACHE); invalidateStaticData();
return gl::Error(GL_NO_ERROR); return gl::Error(GL_NO_ERROR);
} }
...@@ -417,7 +418,7 @@ gl::Error Buffer11::copySubData(BufferImpl *source, ...@@ -417,7 +418,7 @@ gl::Error Buffer11::copySubData(BufferImpl *source,
copyDest->setDataRevision(copyDest->getDataRevision() + 1); copyDest->setDataRevision(copyDest->getDataRevision() + 1);
mSize = std::max<size_t>(mSize, destOffset + size); mSize = std::max<size_t>(mSize, destOffset + size);
invalidateStaticData(D3D_BUFFER_INVALIDATE_WHOLE_CACHE); invalidateStaticData();
return gl::Error(GL_NO_ERROR); return gl::Error(GL_NO_ERROR);
} }
...@@ -457,7 +458,7 @@ gl::Error Buffer11::mapRange(size_t offset, size_t length, GLbitfield access, GL ...@@ -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 // Update the data revision immediately, since the data might be changed at any time
mMappedStorage->setDataRevision(mMappedStorage->getDataRevision() + 1); mMappedStorage->setDataRevision(mMappedStorage->getDataRevision() + 1);
invalidateStaticData(D3D_BUFFER_INVALIDATE_WHOLE_CACHE); invalidateStaticData();
} }
uint8_t *mappedBuffer = mMappedStorage->map(offset, length, access); uint8_t *mappedBuffer = mMappedStorage->map(offset, length, access);
...@@ -492,7 +493,7 @@ void Buffer11::markTransformFeedbackUsage() ...@@ -492,7 +493,7 @@ void Buffer11::markTransformFeedbackUsage()
transformFeedbackStorage->setDataRevision(transformFeedbackStorage->getDataRevision() + 1); transformFeedbackStorage->setDataRevision(transformFeedbackStorage->getDataRevision() + 1);
} }
invalidateStaticData(D3D_BUFFER_INVALIDATE_WHOLE_CACHE); invalidateStaticData();
} }
void Buffer11::markBufferUsage() void Buffer11::markBufferUsage()
...@@ -848,7 +849,7 @@ bool Buffer11::supportsDirectBinding() const ...@@ -848,7 +849,7 @@ bool Buffer11::supportsDirectBinding() const
// Do not support direct buffers for dynamic data. The streaming buffer // Do not support direct buffers for dynamic data. The streaming buffer
// offers better performance for data which changes every frame. // offers better performance for data which changes every frame.
// Check for absence of static buffer interfaces to detect dynamic data. // 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) Buffer11::BufferStorage::BufferStorage(Renderer11 *renderer, BufferUsage usage)
......
...@@ -252,8 +252,7 @@ gl::Error InputLayoutCache::applyVertexBuffers( ...@@ -252,8 +252,7 @@ gl::Error InputLayoutCache::applyVertexBuffers(
if (attribIndex < unsortedAttributes.size() && attrib.active) 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. // 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 // Emulated indexed pointsprite rendering requires that the vertex buffers match exactly to
...@@ -261,7 +260,8 @@ gl::Error InputLayoutCache::applyVertexBuffers( ...@@ -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. // on the number of points indicated by the index list or how many duplicates are found on the index list.
if (bufferStorage == nullptr) if (bufferStorage == nullptr)
{ {
buffer = vertexBuffer->getBuffer(); ASSERT(attrib.vertexBuffer.get());
buffer = GetAs<VertexBuffer11>(attrib.vertexBuffer.get())->getBuffer();
} }
else if (instancedPointSpritesActive && (indexInfo != nullptr)) else if (instancedPointSpritesActive && (indexInfo != nullptr))
{ {
......
...@@ -21,7 +21,6 @@ class VertexBuffer11 : public VertexBuffer ...@@ -21,7 +21,6 @@ class VertexBuffer11 : public VertexBuffer
{ {
public: public:
explicit VertexBuffer11(Renderer11 *const renderer); explicit VertexBuffer11(Renderer11 *const renderer);
~VertexBuffer11() override;
gl::Error initialize(unsigned int size, bool dynamicUsage) override; gl::Error initialize(unsigned int size, bool dynamicUsage) override;
...@@ -42,6 +41,7 @@ class VertexBuffer11 : public VertexBuffer ...@@ -42,6 +41,7 @@ class VertexBuffer11 : public VertexBuffer
ID3D11Buffer *getBuffer() const; ID3D11Buffer *getBuffer() const;
private: private:
~VertexBuffer11() override;
gl::Error mapResource(); gl::Error mapResource();
Renderer11 *const mRenderer; Renderer11 *const mRenderer;
......
...@@ -38,7 +38,7 @@ gl::Error Buffer9::setData(const void* data, size_t size, GLenum usage) ...@@ -38,7 +38,7 @@ gl::Error Buffer9::setData(const void* data, size_t size, GLenum usage)
memcpy(mMemory.data(), data, size); memcpy(mMemory.data(), data, size);
} }
invalidateStaticData(D3D_BUFFER_INVALIDATE_WHOLE_CACHE); invalidateStaticData();
updateD3DBufferUsage(usage); updateD3DBufferUsage(usage);
return gl::Error(GL_NO_ERROR); return gl::Error(GL_NO_ERROR);
...@@ -66,7 +66,7 @@ gl::Error Buffer9::setSubData(const void* data, size_t size, size_t offset) ...@@ -66,7 +66,7 @@ gl::Error Buffer9::setSubData(const void* data, size_t size, size_t offset)
memcpy(mMemory.data() + offset, data, size); memcpy(mMemory.data() + offset, data, size);
} }
invalidateStaticData(D3D_BUFFER_INVALIDATE_WHOLE_CACHE); invalidateStaticData();
return gl::Error(GL_NO_ERROR); return gl::Error(GL_NO_ERROR);
} }
...@@ -79,7 +79,7 @@ gl::Error Buffer9::copySubData(BufferImpl* source, GLintptr sourceOffset, GLintp ...@@ -79,7 +79,7 @@ gl::Error Buffer9::copySubData(BufferImpl* source, GLintptr sourceOffset, GLintp
memcpy(mMemory.data() + destOffset, sourceBuffer->mMemory.data() + sourceOffset, size); memcpy(mMemory.data() + destOffset, sourceBuffer->mMemory.data() + sourceOffset, size);
invalidateStaticData(D3D_BUFFER_INVALIDATE_WHOLE_CACHE); invalidateStaticData();
return gl::Error(GL_NO_ERROR); return gl::Error(GL_NO_ERROR);
} }
......
...@@ -19,7 +19,6 @@ class VertexBuffer9 : public VertexBuffer ...@@ -19,7 +19,6 @@ class VertexBuffer9 : public VertexBuffer
{ {
public: public:
explicit VertexBuffer9(Renderer9 *renderer); explicit VertexBuffer9(Renderer9 *renderer);
~VertexBuffer9() override;
gl::Error initialize(unsigned int size, bool dynamicUsage) override; gl::Error initialize(unsigned int size, bool dynamicUsage) override;
...@@ -38,6 +37,7 @@ class VertexBuffer9 : public VertexBuffer ...@@ -38,6 +37,7 @@ class VertexBuffer9 : public VertexBuffer
IDirect3DVertexBuffer9 *getBuffer() const; IDirect3DVertexBuffer9 *getBuffer() const;
private: private:
~VertexBuffer9() override;
Renderer9 *mRenderer; Renderer9 *mRenderer;
IDirect3DVertexBuffer9 *mVertexBuffer; IDirect3DVertexBuffer9 *mVertexBuffer;
......
...@@ -147,7 +147,7 @@ gl::Error VertexDeclarationCache::applyDeclaration(IDirect3DDevice9 *device, ...@@ -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 || if (mAppliedVBs[stream].serial != attributes[i].serial ||
mAppliedVBs[stream].stride != attributes[i].stride || mAppliedVBs[stream].stride != attributes[i].stride ||
......
...@@ -31,6 +31,22 @@ GLsizei TypeStride(GLenum attribType) ...@@ -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 class VertexAttributeTest : public ANGLETest
{ {
protected: protected:
...@@ -246,7 +262,7 @@ TEST_P(VertexAttributeTest, UnsignedByteNormalized) ...@@ -246,7 +262,7 @@ TEST_P(VertexAttributeTest, UnsignedByteNormalized)
GLfloat expectedData[mVertexCount]; GLfloat expectedData[mVertexCount];
for (size_t i = 0; i < mVertexCount; i++) 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}; TestData data = {GL_UNSIGNED_BYTE, GL_TRUE, Source::IMMEDIATE, inputData, expectedData};
...@@ -272,7 +288,7 @@ TEST_P(VertexAttributeTest, ByteNormalized) ...@@ -272,7 +288,7 @@ TEST_P(VertexAttributeTest, ByteNormalized)
GLfloat expectedData[mVertexCount]; GLfloat expectedData[mVertexCount];
for (size_t i = 0; i < mVertexCount; i++) 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}; TestData data = {GL_BYTE, GL_TRUE, Source::IMMEDIATE, inputData, expectedData};
...@@ -298,7 +314,7 @@ TEST_P(VertexAttributeTest, UnsignedShortNormalized) ...@@ -298,7 +314,7 @@ TEST_P(VertexAttributeTest, UnsignedShortNormalized)
GLfloat expectedData[mVertexCount]; GLfloat expectedData[mVertexCount];
for (size_t i = 0; i < mVertexCount; i++) 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}; TestData data = {GL_UNSIGNED_SHORT, GL_TRUE, Source::IMMEDIATE, inputData, expectedData};
...@@ -324,7 +340,7 @@ TEST_P(VertexAttributeTest, ShortNormalized) ...@@ -324,7 +340,7 @@ TEST_P(VertexAttributeTest, ShortNormalized)
GLfloat expectedData[mVertexCount]; GLfloat expectedData[mVertexCount];
for (size_t i = 0; i < mVertexCount; i++) 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}; TestData data = {GL_SHORT, GL_TRUE, Source::IMMEDIATE, inputData, expectedData};
...@@ -360,7 +376,7 @@ TEST_P(VertexAttributeTestES3, IntNormalized) ...@@ -360,7 +376,7 @@ TEST_P(VertexAttributeTestES3, IntNormalized)
GLfloat expectedData[mVertexCount]; GLfloat expectedData[mVertexCount];
for (size_t i = 0; i < mVertexCount; i++) 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}; TestData data = {GL_INT, GL_TRUE, Source::BUFFER, inputData, expectedData};
...@@ -392,7 +408,7 @@ TEST_P(VertexAttributeTestES3, UnsignedIntNormalized) ...@@ -392,7 +408,7 @@ TEST_P(VertexAttributeTestES3, UnsignedIntNormalized)
GLfloat expectedData[mVertexCount]; GLfloat expectedData[mVertexCount];
for (size_t i = 0; i < mVertexCount; i++) 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}; TestData data = {GL_UNSIGNED_INT, GL_TRUE, Source::BUFFER, inputData, expectedData};
...@@ -470,6 +486,164 @@ TEST_P(VertexAttributeTest, SimpleBindAttribLocation) ...@@ -470,6 +486,164 @@ TEST_P(VertexAttributeTest, SimpleBindAttribLocation)
EXPECT_PIXEL_NEAR(0, 0, 128, 0, 0, 255, 1); 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. // 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. // 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, ANGLE_INSTANTIATE_TEST(VertexAttributeTest,
...@@ -483,4 +657,10 @@ 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(VertexAttributeTestES3, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES());
ANGLE_INSTANTIATE_TEST(VertexAttributeCachingTest,
ES2_D3D9(),
ES2_D3D11(),
ES3_D3D11(),
ES3_OPENGL());
} // anonymous namespace } // 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