Commit f25eb1d6 by Jamie Madill

D3D11: Use system memory for dynamic buffer updates.

In our current code, we would use a staging texture as the working copy for buffer updates. This would trigger very large memcpy calls in some cases for large buffers with small updates. Instead, use a CPU memory buffer storage, and work with this storage when the user updates data. This plays much nicer with the VertexDataManager. BUG=angle:912 Change-Id: I8c32d3d9bb321a06534556ce05b4b99dc3d1e961 Reviewed-on: https://chromium-review.googlesource.com/249183Tested-by: 's avatarJamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarGeoff Lang <geofflang@chromium.org>
parent af1bdff6
...@@ -7,6 +7,8 @@ ...@@ -7,6 +7,8 @@
// Buffer11.cpp Defines the Buffer11 class. // Buffer11.cpp Defines the Buffer11 class.
#include "libANGLE/renderer/d3d/d3d11/Buffer11.h" #include "libANGLE/renderer/d3d/d3d11/Buffer11.h"
#include "common/MemoryBuffer.h"
#include "libANGLE/renderer/d3d/d3d11/Renderer11.h" #include "libANGLE/renderer/d3d/d3d11/Renderer11.h"
#include "libANGLE/renderer/d3d/d3d11/formatutils11.h" #include "libANGLE/renderer/d3d/d3d11/formatutils11.h"
...@@ -79,10 +81,10 @@ class Buffer11::BufferStorage ...@@ -79,10 +81,10 @@ class Buffer11::BufferStorage
DataRevision getDataRevision() const { return mRevision; } DataRevision getDataRevision() const { return mRevision; }
BufferUsage getUsage() const { return mUsage; } BufferUsage getUsage() const { return mUsage; }
size_t getSize() const { return mBufferSize; } size_t getSize() const { return mBufferSize; }
bool isMappable() const { return (mUsage == BUFFER_USAGE_STAGING || mUsage == BUFFER_USAGE_PIXEL_PACK); }
void setDataRevision(DataRevision rev) { mRevision = rev; } void setDataRevision(DataRevision rev) { mRevision = rev; }
virtual bool isMappable() const = 0;
virtual bool copyFromStorage(BufferStorage *source, size_t sourceOffset, virtual bool copyFromStorage(BufferStorage *source, size_t sourceOffset,
size_t size, size_t destOffset) = 0; size_t size, size_t destOffset) = 0;
virtual gl::Error resize(size_t size, bool preserveData) = 0; virtual gl::Error resize(size_t size, bool preserveData) = 0;
...@@ -111,6 +113,8 @@ class Buffer11::NativeStorage : public Buffer11::BufferStorage ...@@ -111,6 +113,8 @@ class Buffer11::NativeStorage : public Buffer11::BufferStorage
NativeStorage(Renderer11 *renderer, BufferUsage usage); NativeStorage(Renderer11 *renderer, BufferUsage usage);
~NativeStorage() override; ~NativeStorage() override;
bool isMappable() const override { return mUsage == BUFFER_USAGE_STAGING; }
ID3D11Buffer *getNativeStorage() const { return mNativeStorage; } ID3D11Buffer *getNativeStorage() const { return mNativeStorage; }
bool copyFromStorage(BufferStorage *source, size_t sourceOffset, bool copyFromStorage(BufferStorage *source, size_t sourceOffset,
...@@ -133,9 +137,11 @@ class Buffer11::NativeStorage : public Buffer11::BufferStorage ...@@ -133,9 +137,11 @@ class Buffer11::NativeStorage : public Buffer11::BufferStorage
class Buffer11::PackStorage : public Buffer11::BufferStorage class Buffer11::PackStorage : public Buffer11::BufferStorage
{ {
public: public:
PackStorage(Renderer11 *renderer); explicit PackStorage(Renderer11 *renderer);
~PackStorage() override; ~PackStorage() override;
bool isMappable() const override { return true; }
bool copyFromStorage(BufferStorage *source, size_t sourceOffset, bool copyFromStorage(BufferStorage *source, size_t sourceOffset,
size_t size, size_t destOffset) override; size_t size, size_t destOffset) override;
gl::Error resize(size_t size, bool preserveData) override; gl::Error resize(size_t size, bool preserveData) override;
...@@ -159,13 +165,39 @@ class Buffer11::PackStorage : public Buffer11::BufferStorage ...@@ -159,13 +165,39 @@ class Buffer11::PackStorage : public Buffer11::BufferStorage
bool mDataModified; bool mDataModified;
}; };
// System memory storage stores a CPU memory buffer with our buffer data.
// For dynamic data, it's much faster to update the CPU memory buffer than
// it is to update a D3D staging buffer and read it back later.
class Buffer11::SystemMemoryStorage : public Buffer11::BufferStorage
{
public:
explicit SystemMemoryStorage(Renderer11 *renderer);
~SystemMemoryStorage() override {}
bool isMappable() const override { return true; }
bool copyFromStorage(BufferStorage *source, size_t sourceOffset,
size_t size, size_t destOffset) override;
gl::Error resize(size_t size, bool preserveData) override;
uint8_t *map(size_t offset, size_t length, GLbitfield access) override;
void unmap() override;
MemoryBuffer *getSystemCopy() { return &mSystemCopy; }
protected:
DISALLOW_COPY_AND_ASSIGN(SystemMemoryStorage);
MemoryBuffer mSystemCopy;
};
Buffer11::Buffer11(Renderer11 *renderer) Buffer11::Buffer11(Renderer11 *renderer)
: BufferD3D(), : BufferD3D(),
mRenderer(renderer), mRenderer(renderer),
mSize(0), mSize(0),
mMappedStorage(NULL), mMappedStorage(NULL),
mResolvedDataRevision(0), mReadUsageCount(0),
mReadUsageCount(0) mHasSystemMemoryStorage(false)
{} {}
Buffer11::~Buffer11() Buffer11::~Buffer11()
...@@ -200,54 +232,34 @@ gl::Error Buffer11::setData(const void *data, size_t size, GLenum usage) ...@@ -200,54 +232,34 @@ gl::Error Buffer11::setData(const void *data, size_t size, GLenum usage)
gl::Error Buffer11::getData(const uint8_t **outData) gl::Error Buffer11::getData(const uint8_t **outData)
{ {
NativeStorage *stagingBuffer = getStagingStorage(); SystemMemoryStorage *systemMemoryStorage = nullptr;
gl::Error error = getSystemMemoryStorage(&systemMemoryStorage);
if (!stagingBuffer) if (error.isError())
{
// Out-of-memory
return gl::Error(GL_OUT_OF_MEMORY, "Failed to get internal staging buffer.");
}
if (stagingBuffer->getDataRevision() > mResolvedDataRevision)
{
if (stagingBuffer->getSize() > mResolvedData.size())
{
if (!mResolvedData.resize(stagingBuffer->getSize()))
{
return gl::Error(GL_OUT_OF_MEMORY, "Failed to resize data resolve buffer.");
}
}
ID3D11DeviceContext *context = mRenderer->getDeviceContext();
D3D11_MAPPED_SUBRESOURCE mappedResource;
HRESULT result = context->Map(stagingBuffer->getNativeStorage(), 0, D3D11_MAP_READ, 0, &mappedResource);
if (FAILED(result))
{ {
return gl::Error(GL_OUT_OF_MEMORY, "Failed to map internal buffer, result: 0x%X.", result); *outData = nullptr;
return error;
} }
memcpy(mResolvedData.data(), mappedResource.pData, stagingBuffer->getSize()); mReadUsageCount = 0;
context->Unmap(stagingBuffer->getNativeStorage(), 0); ASSERT(systemMemoryStorage->getSize() >= mSize);
mResolvedDataRevision = stagingBuffer->getDataRevision(); *outData = systemMemoryStorage->getSystemCopy()->data();
} return gl::Error(GL_NO_ERROR);
}
mReadUsageCount = 0; gl::Error Buffer11::getSystemMemoryStorage(SystemMemoryStorage **storageOut)
{
BufferStorage *memStorageUntyped = getBufferStorage(BUFFER_USAGE_SYSTEM_MEMORY);
// Only happens if we initialized the buffer with no data (NULL) if (memStorageUntyped == nullptr)
if (mResolvedData.empty())
{ {
if (!mResolvedData.resize(mSize)) // TODO(jmadill): convert all to errors
{ return gl::Error(GL_OUT_OF_MEMORY);
return gl::Error(GL_OUT_OF_MEMORY, "Failed to resize data resolve buffer.");
}
} }
ASSERT(mResolvedData.size() >= mSize); *storageOut = GetAs<SystemMemoryStorage>(memStorageUntyped);
*outData = mResolvedData.data();
return gl::Error(GL_NO_ERROR); return gl::Error(GL_NO_ERROR);
} }
...@@ -257,27 +269,46 @@ gl::Error Buffer11::setSubData(const void *data, size_t size, size_t offset) ...@@ -257,27 +269,46 @@ gl::Error Buffer11::setSubData(const void *data, size_t size, size_t offset)
if (data && size > 0) if (data && size > 0)
{ {
NativeStorage *stagingBuffer = getStagingStorage(); // Use system memory storage for dynamic buffers.
if (!stagingBuffer) BufferStorage *writeBuffer = nullptr;
if (supportsDirectBinding())
{ {
return gl::Error(GL_OUT_OF_MEMORY, "Failed to allocate internal staging buffer."); writeBuffer = getStagingStorage();
if (!writeBuffer)
{
return gl::Error(GL_OUT_OF_MEMORY, "Failed to allocate internal buffer.");
}
}
else
{
SystemMemoryStorage *systemMemoryStorage = nullptr;
gl::Error error = getSystemMemoryStorage(&systemMemoryStorage);
if (error.isError())
{
return error;
} }
writeBuffer = systemMemoryStorage;
}
ASSERT(writeBuffer);
// Explicitly resize the staging buffer, preserving data if the new data will not // Explicitly resize the staging buffer, preserving data if the new data will not
// completely fill the buffer // completely fill the buffer
if (stagingBuffer->getSize() < requiredSize) if (writeBuffer->getSize() < requiredSize)
{ {
bool preserveData = (offset > 0); bool preserveData = (offset > 0);
gl::Error error = stagingBuffer->resize(requiredSize, preserveData); gl::Error error = writeBuffer->resize(requiredSize, preserveData);
if (error.isError()) if (error.isError())
{ {
return error; return error;
} }
} }
stagingBuffer->setData(static_cast<const uint8_t *>(data), offset, size); writeBuffer->setData(static_cast<const uint8_t *>(data), offset, size);
stagingBuffer->setDataRevision(stagingBuffer->getDataRevision() + 1); writeBuffer->setDataRevision(writeBuffer->getDataRevision() + 1);
} }
mSize = std::max(mSize, requiredSize); mSize = std::max(mSize, requiredSize);
...@@ -403,12 +434,17 @@ void Buffer11::markBufferUsage() ...@@ -403,12 +434,17 @@ void Buffer11::markBufferUsage()
{ {
mReadUsageCount++; mReadUsageCount++;
// Free the system memory storage if we decide it isn't being used very often.
const unsigned int usageLimit = 5; const unsigned int usageLimit = 5;
if (mReadUsageCount > usageLimit && mResolvedData.size() > 0) if (mReadUsageCount > usageLimit && mHasSystemMemoryStorage)
{ {
mResolvedData.resize(0); auto systemMemoryStorageIt = mBufferStorages.find(BUFFER_USAGE_SYSTEM_MEMORY);
mResolvedDataRevision = 0; ASSERT(systemMemoryStorageIt != mBufferStorages.end());
SafeDelete(systemMemoryStorageIt->second);
mBufferStorages.erase(systemMemoryStorageIt);
mHasSystemMemoryStorage = false;
} }
} }
...@@ -515,6 +551,11 @@ Buffer11::BufferStorage *Buffer11::getBufferStorage(BufferUsage usage) ...@@ -515,6 +551,11 @@ Buffer11::BufferStorage *Buffer11::getBufferStorage(BufferUsage usage)
{ {
newStorage = new PackStorage(mRenderer); newStorage = new PackStorage(mRenderer);
} }
else if (usage == BUFFER_USAGE_SYSTEM_MEMORY)
{
newStorage = new SystemMemoryStorage(mRenderer);
mHasSystemMemoryStorage = true;
}
else else
{ {
// buffer is not allocated, create it // buffer is not allocated, create it
...@@ -537,9 +578,12 @@ Buffer11::BufferStorage *Buffer11::getBufferStorage(BufferUsage usage) ...@@ -537,9 +578,12 @@ Buffer11::BufferStorage *Buffer11::getBufferStorage(BufferUsage usage)
BufferStorage *latestBuffer = getLatestBufferStorage(); BufferStorage *latestBuffer = getLatestBufferStorage();
if (latestBuffer && latestBuffer->getDataRevision() > newStorage->getDataRevision()) if (latestBuffer && latestBuffer->getDataRevision() > newStorage->getDataRevision())
{ {
// if copying from a pack buffer to a non-staging native buffer, we must first // Copy through a staging buffer if we're copying from or to a non-staging, mappable
// copy through the staging buffer, because other native buffers can't be mapped // buffer storage. This is because we can't map a GPU buffer, and copy CPU
if (latestBuffer->getUsage() == BUFFER_USAGE_PIXEL_PACK && !newStorage->isMappable()) // data directly. If we're already using a staging buffer we're fine.
if (latestBuffer->getUsage() != BUFFER_USAGE_STAGING &&
newStorage->getUsage() != BUFFER_USAGE_STAGING &&
(!latestBuffer->isMappable() || !newStorage->isMappable()))
{ {
NativeStorage *stagingBuffer = getStagingStorage(); NativeStorage *stagingBuffer = getStagingStorage();
...@@ -678,9 +722,10 @@ bool Buffer11::NativeStorage::copyFromStorage(BufferStorage *source, size_t sour ...@@ -678,9 +722,10 @@ bool Buffer11::NativeStorage::copyFromStorage(BufferStorage *source, size_t sour
resize(source->getSize(), preserveData); resize(source->getSize(), preserveData);
} }
if (source->getUsage() == BUFFER_USAGE_PIXEL_PACK) if (source->getUsage() == BUFFER_USAGE_PIXEL_PACK ||
source->getUsage() == BUFFER_USAGE_SYSTEM_MEMORY)
{ {
ASSERT(HAS_DYNAMIC_TYPE(PackStorage*, source)); ASSERT(source->isMappable());
uint8_t *sourcePointer = source->map(sourceOffset, size, GL_MAP_READ_BIT); uint8_t *sourcePointer = source->map(sourceOffset, size, GL_MAP_READ_BIT);
...@@ -989,4 +1034,44 @@ gl::Error Buffer11::PackStorage::flushQueuedPackCommand() ...@@ -989,4 +1034,44 @@ gl::Error Buffer11::PackStorage::flushQueuedPackCommand()
return gl::Error(GL_NO_ERROR); return gl::Error(GL_NO_ERROR);
} }
Buffer11::SystemMemoryStorage::SystemMemoryStorage(Renderer11 *renderer)
: Buffer11::BufferStorage(renderer, BUFFER_USAGE_SYSTEM_MEMORY)
{}
bool Buffer11::SystemMemoryStorage::copyFromStorage(BufferStorage *source, size_t sourceOffset,
size_t size, size_t destOffset)
{
ASSERT(source->isMappable());
const uint8_t *sourceData = source->map(sourceOffset, size, GL_MAP_READ_BIT);
ASSERT(destOffset + size <= mSystemCopy.size());
memcpy(mSystemCopy.data() + destOffset, sourceData, size);
source->unmap();
return true;
}
gl::Error Buffer11::SystemMemoryStorage::resize(size_t size, bool preserveData)
{
if (mSystemCopy.size() < size)
{
if (!mSystemCopy.resize(size))
{
return gl::Error(GL_OUT_OF_MEMORY, "Failed to resize SystemMemoryStorage");
}
mBufferSize = size;
}
return gl::Error(GL_NO_ERROR);
}
uint8_t *Buffer11::SystemMemoryStorage::map(size_t offset, size_t length, GLbitfield access)
{
ASSERT(!mSystemCopy.empty() && offset + length <= mSystemCopy.size());
return mSystemCopy.data() + offset;
}
void Buffer11::SystemMemoryStorage::unmap()
{
// No-op
}
} }
...@@ -9,7 +9,6 @@ ...@@ -9,7 +9,6 @@
#ifndef LIBANGLE_RENDERER_D3D_D3D11_BUFFER11_H_ #ifndef LIBANGLE_RENDERER_D3D_D3D11_BUFFER11_H_
#define LIBANGLE_RENDERER_D3D_D3D11_BUFFER11_H_ #define LIBANGLE_RENDERER_D3D_D3D11_BUFFER11_H_
#include "common/MemoryBuffer.h"
#include "libANGLE/angletypes.h" #include "libANGLE/angletypes.h"
#include "libANGLE/renderer/d3d/BufferD3D.h" #include "libANGLE/renderer/d3d/BufferD3D.h"
...@@ -25,6 +24,7 @@ enum BufferUsage ...@@ -25,6 +24,7 @@ enum BufferUsage
BUFFER_USAGE_PIXEL_UNPACK, BUFFER_USAGE_PIXEL_UNPACK,
BUFFER_USAGE_PIXEL_PACK, BUFFER_USAGE_PIXEL_PACK,
BUFFER_USAGE_UNIFORM, BUFFER_USAGE_UNIFORM,
BUFFER_USAGE_SYSTEM_MEMORY,
}; };
struct PackPixelsParams struct PackPixelsParams
...@@ -77,6 +77,7 @@ class Buffer11 : public BufferD3D ...@@ -77,6 +77,7 @@ class Buffer11 : public BufferD3D
class BufferStorage; class BufferStorage;
class NativeStorage; class NativeStorage;
class PackStorage; class PackStorage;
class SystemMemoryStorage;
Renderer11 *mRenderer; Renderer11 *mRenderer;
size_t mSize; size_t mSize;
...@@ -88,13 +89,13 @@ class Buffer11 : public BufferD3D ...@@ -88,13 +89,13 @@ class Buffer11 : public BufferD3D
typedef std::pair<ID3D11Buffer *, ID3D11ShaderResourceView *> BufferSRVPair; typedef std::pair<ID3D11Buffer *, ID3D11ShaderResourceView *> BufferSRVPair;
std::map<DXGI_FORMAT, BufferSRVPair> mBufferResourceViews; std::map<DXGI_FORMAT, BufferSRVPair> mBufferResourceViews;
MemoryBuffer mResolvedData;
DataRevision mResolvedDataRevision;
unsigned int mReadUsageCount; unsigned int mReadUsageCount;
bool mHasSystemMemoryStorage;
void markBufferUsage(); void markBufferUsage();
NativeStorage *getStagingStorage(); NativeStorage *getStagingStorage();
PackStorage *getPackStorage(); PackStorage *getPackStorage();
gl::Error getSystemMemoryStorage(SystemMemoryStorage **storageOut);
BufferStorage *getBufferStorage(BufferUsage usage); BufferStorage *getBufferStorage(BufferUsage usage);
BufferStorage *getLatestBufferStorage() const; BufferStorage *getLatestBufferStorage() const;
......
...@@ -3,8 +3,8 @@ ...@@ -3,8 +3,8 @@
"type": "float", "type": "float",
"components": 4, "components": 4,
"normalized": false, "normalized": false,
"update_size": 300, "update_size": 3000,
"buffer_size": 1048576, "buffer_size": 67000000,
"iterations": 10, "iterations": 10,
"update_rate": 1 "update_rate": 1
} }
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