blob: 877b30344728f42e60d68588c4f64a056f39fac3 [file] [log] [blame]
//
// Copyright 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.
//
// Buffer11.cpp Defines the Buffer11 class.
#include "libGLESv2/renderer/d3d/d3d11/Buffer11.h"
#include "libGLESv2/renderer/d3d/d3d11/Renderer11.h"
#include "libGLESv2/renderer/d3d/d3d11/formatutils11.h"
#include "libGLESv2/main.h"
namespace rx
{
PackPixelsParams::PackPixelsParams()
: format(GL_NONE),
type(GL_NONE),
outputPitch(0),
packBuffer(NULL),
offset(0)
{}
PackPixelsParams::PackPixelsParams(const gl::Rectangle &areaIn, GLenum formatIn, GLenum typeIn, GLuint outputPitchIn,
const gl::PixelPackState &packIn, ptrdiff_t offsetIn)
: area(areaIn),
format(formatIn),
type(typeIn),
outputPitch(outputPitchIn),
packBuffer(packIn.pixelBuffer.get()),
pack(packIn.alignment, packIn.reverseRowOrder),
offset(offsetIn)
{}
namespace gl_d3d11
{
D3D11_MAP GetD3DMapTypeFromBits(GLbitfield access)
{
bool readBit = ((access & GL_MAP_READ_BIT) != 0);
bool writeBit = ((access & GL_MAP_WRITE_BIT) != 0);
ASSERT(readBit || writeBit);
// Note : we ignore the discard bit, because in D3D11, staging buffers
// don't accept the map-discard flag (discard only works for DYNAMIC usage)
if (readBit && !writeBit)
{
return D3D11_MAP_READ;
}
else if (writeBit && !readBit)
{
return D3D11_MAP_WRITE;
}
else if (writeBit && readBit)
{
return D3D11_MAP_READ_WRITE;
}
else
{
UNREACHABLE();
return D3D11_MAP_READ;
}
}
}
// Each instance of Buffer11::BufferStorage11 is specialized for a class of D3D binding points
// - vertex/transform feedback buffers
// - index buffers
// - pixel unpack buffers
// - uniform buffers
class Buffer11::BufferStorage11
{
public:
virtual ~BufferStorage11() {}
DataRevision getDataRevision() const { return mRevision; }
BufferUsage getUsage() const { return mUsage; }
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; }
virtual bool copyFromStorage(BufferStorage11 *source, size_t sourceOffset,
size_t size, size_t destOffset) = 0;
virtual bool resize(size_t size, bool preserveData) = 0;
virtual void *map(size_t offset, size_t length, GLbitfield access) = 0;
virtual void unmap() = 0;
protected:
BufferStorage11(Renderer11 *renderer, BufferUsage usage);
Renderer11 *mRenderer;
DataRevision mRevision;
const BufferUsage mUsage;
size_t mBufferSize;
};
// A native buffer storage represents an underlying D3D11 buffer for a particular
// type of storage.
class Buffer11::NativeBuffer11 : public Buffer11::BufferStorage11
{
public:
NativeBuffer11(Renderer11 *renderer, BufferUsage usage);
~NativeBuffer11();
ID3D11Buffer *getNativeBuffer() const { return mNativeBuffer; }
virtual bool copyFromStorage(BufferStorage11 *source, size_t sourceOffset,
size_t size, size_t destOffset);
virtual bool resize(size_t size, bool preserveData);
virtual void *map(size_t offset, size_t length, GLbitfield access);
virtual void unmap();
bool setData(D3D11_MAP mapMode, const uint8_t *data, size_t size, size_t offset);
private:
ID3D11Buffer *mNativeBuffer;
static void fillBufferDesc(D3D11_BUFFER_DESC* bufferDesc, Renderer *renderer, BufferUsage usage, unsigned int bufferSize);
};
// Pack storage represents internal storage for pack buffers. We implement pack buffers
// as CPU memory, tied to a staging texture, for asynchronous texture readback.
class Buffer11::PackStorage11 : public Buffer11::BufferStorage11
{
public:
PackStorage11(Renderer11 *renderer);
~PackStorage11();
virtual bool copyFromStorage(BufferStorage11 *source, size_t sourceOffset,
size_t size, size_t destOffset);
virtual bool resize(size_t size, bool preserveData);
virtual void *map(size_t offset, size_t length, GLbitfield access);
virtual void unmap();
gl::Error packPixels(ID3D11Texture2D *srcTexure, UINT srcSubresource, const PackPixelsParams &params);
private:
gl::Error flushQueuedPackCommand();
ID3D11Texture2D *mStagingTexture;
DXGI_FORMAT mTextureFormat;
gl::Extents mTextureSize;
MemoryBuffer mMemoryBuffer;
PackPixelsParams *mQueuedPackCommand;
PackPixelsParams mPackParams;
bool mDataModified;
};
Buffer11::Buffer11(Renderer11 *renderer)
: BufferD3D(),
mRenderer(renderer),
mSize(0),
mMappedStorage(NULL),
mResolvedDataRevision(0),
mReadUsageCount(0)
{}
Buffer11::~Buffer11()
{
for (auto it = mBufferStorages.begin(); it != mBufferStorages.end(); it++)
{
SafeDelete(it->second);
}
}
Buffer11 *Buffer11::makeBuffer11(BufferImpl *buffer)
{
ASSERT(HAS_DYNAMIC_TYPE(Buffer11*, buffer));
return static_cast<Buffer11*>(buffer);
}
gl::Error Buffer11::setData(const void *data, size_t size, GLenum usage)
{
gl::Error error = setSubData(data, size, 0);
if (error.isError())
{
return error;
}
if (usage == GL_STATIC_DRAW)
{
initializeStaticData();
}
return error;
}
void *Buffer11::getData()
{
NativeBuffer11 *stagingBuffer = getStagingBuffer();
if (!stagingBuffer)
{
// Out-of-memory
return NULL;
}
if (stagingBuffer->getDataRevision() > mResolvedDataRevision)
{
if (stagingBuffer->getSize() > mResolvedData.size())
{
if (!mResolvedData.resize(stagingBuffer->getSize()))
{
return gl::error(GL_OUT_OF_MEMORY, (void*)NULL);
}
}
ID3D11DeviceContext *context = mRenderer->getDeviceContext();
D3D11_MAPPED_SUBRESOURCE mappedResource;
HRESULT result = context->Map(stagingBuffer->getNativeBuffer(), 0, D3D11_MAP_READ, 0, &mappedResource);
if (FAILED(result))
{
return gl::error(GL_OUT_OF_MEMORY, (void*)NULL);
}
memcpy(mResolvedData.data(), mappedResource.pData, stagingBuffer->getSize());
context->Unmap(stagingBuffer->getNativeBuffer(), 0);
mResolvedDataRevision = stagingBuffer->getDataRevision();
}
mReadUsageCount = 0;
// Only happens if we initialized the buffer with no data (NULL)
if (mResolvedData.empty())
{
if (!mResolvedData.resize(mSize))
{
return gl::error(GL_OUT_OF_MEMORY, (void*)NULL);
}
}
ASSERT(mResolvedData.size() >= mSize);
return mResolvedData.data();
}
gl::Error Buffer11::setSubData(const void *data, size_t size, size_t offset)
{
size_t requiredSize = size + offset;
if (data && size > 0)
{
NativeBuffer11 *stagingBuffer = getStagingBuffer();
if (!stagingBuffer)
{
return gl::Error(GL_OUT_OF_MEMORY, "Failed to allocate internal staging buffer.");
}
// Explicitly resize the staging buffer, preserving data if the new data will not
// completely fill the buffer
if (stagingBuffer->getSize() < requiredSize)
{
bool preserveData = (offset > 0);
if (!stagingBuffer->resize(requiredSize, preserveData))
{
return gl::Error(GL_OUT_OF_MEMORY, "Failed to resize internal staging buffer.");
}
}
if (!stagingBuffer->setData(D3D11_MAP_WRITE, reinterpret_cast<const uint8_t *>(data), size, offset))
{
return gl::Error(GL_OUT_OF_MEMORY, "Failed to set data on internal staging buffer.");
}
stagingBuffer->setDataRevision(stagingBuffer->getDataRevision() + 1);
}
mSize = std::max(mSize, requiredSize);
invalidateStaticData();
return gl::Error(GL_NO_ERROR);
}
gl::Error Buffer11::copySubData(BufferImpl* source, GLintptr sourceOffset, GLintptr destOffset, GLsizeiptr size)
{
Buffer11 *sourceBuffer = makeBuffer11(source);
ASSERT(sourceBuffer != NULL);
BufferStorage11 *copyDest = getLatestBufferStorage();
if (!copyDest)
{
copyDest = getStagingBuffer();
}
BufferStorage11 *copySource = sourceBuffer->getLatestBufferStorage();
if (!copySource || !copyDest)
{
return gl::Error(GL_OUT_OF_MEMORY, "Failed to allocate internal staging buffer.");
}
// If copying to/from a pixel pack buffer, we must have a staging or
// pack buffer partner, because other native buffers can't be mapped
if (copyDest->getUsage() == BUFFER_USAGE_PIXEL_PACK && !copySource->isMappable())
{
copySource = sourceBuffer->getStagingBuffer();
}
else if (copySource->getUsage() == BUFFER_USAGE_PIXEL_PACK && !copyDest->isMappable())
{
copyDest = getStagingBuffer();
}
// D3D11 does not allow overlapped copies until 11.1, and only if the
// device supports D3D11_FEATURE_DATA_D3D11_OPTIONS::CopyWithOverlap
// Get around this via a different source buffer
if (copySource == copyDest)
{
if (copySource->getUsage() == BUFFER_USAGE_STAGING)
{
copySource = getBufferStorage(BUFFER_USAGE_VERTEX_OR_TRANSFORM_FEEDBACK);
}
else
{
copySource = getStagingBuffer();
}
}
copyDest->copyFromStorage(copySource, sourceOffset, size, destOffset);
copyDest->setDataRevision(copyDest->getDataRevision() + 1);
mSize = std::max<size_t>(mSize, destOffset + size);
invalidateStaticData();
return gl::Error(GL_NO_ERROR);
}
gl::Error Buffer11::map(size_t offset, size_t length, GLbitfield access, GLvoid **mapPtr)
{
ASSERT(!mMappedStorage);
BufferStorage11 *latestStorage = getLatestBufferStorage();
if (latestStorage &&
(latestStorage->getUsage() == BUFFER_USAGE_PIXEL_PACK ||
latestStorage->getUsage() == BUFFER_USAGE_STAGING))
{
// Latest storage is mappable.
mMappedStorage = latestStorage;
}
else
{
// Fall back to using the staging buffer if the latest storage does
// not exist or is not CPU-accessible.
mMappedStorage = getStagingBuffer();
}
if (!mMappedStorage)
{
return gl::Error(GL_OUT_OF_MEMORY, "Failed to allocate mappable internal buffer.");
}
if ((access & GL_MAP_WRITE_BIT) > 0)
{
// Update the data revision immediately, since the data might be changed at any time
mMappedStorage->setDataRevision(mMappedStorage->getDataRevision() + 1);
}
void *mappedBuffer = mMappedStorage->map(offset, length, access);
if (!mappedBuffer)
{
return gl::Error(GL_OUT_OF_MEMORY, "Failed to map internal buffer.");
}
*mapPtr = mappedBuffer;
return gl::Error(GL_NO_ERROR);
}
gl::Error Buffer11::unmap()
{
ASSERT(mMappedStorage);
mMappedStorage->unmap();
mMappedStorage = NULL;
return gl::Error(GL_NO_ERROR);
}
void Buffer11::markTransformFeedbackUsage()
{
BufferStorage11 *transformFeedbackStorage = getBufferStorage(BUFFER_USAGE_VERTEX_OR_TRANSFORM_FEEDBACK);
if (transformFeedbackStorage)
{
transformFeedbackStorage->setDataRevision(transformFeedbackStorage->getDataRevision() + 1);
}
invalidateStaticData();
}
void Buffer11::markBufferUsage()
{
mReadUsageCount++;
const unsigned int usageLimit = 5;
if (mReadUsageCount > usageLimit && mResolvedData.size() > 0)
{
mResolvedData.resize(0);
mResolvedDataRevision = 0;
}
}
Renderer* Buffer11::getRenderer()
{
return mRenderer;
}
ID3D11Buffer *Buffer11::getBuffer(BufferUsage usage)
{
markBufferUsage();
BufferStorage11 *bufferStorage = getBufferStorage(usage);
if (!bufferStorage)
{
// Storage out-of-memory
return NULL;
}
ASSERT(HAS_DYNAMIC_TYPE(NativeBuffer11*, bufferStorage));
return static_cast<NativeBuffer11*>(bufferStorage)->getNativeBuffer();
}
ID3D11ShaderResourceView *Buffer11::getSRV(DXGI_FORMAT srvFormat)
{
BufferStorage11 *storage = getBufferStorage(BUFFER_USAGE_PIXEL_UNPACK);
if (!storage)
{
// Storage out-of-memory
return NULL;
}
ASSERT(HAS_DYNAMIC_TYPE(NativeBuffer11*, storage));
ID3D11Buffer *buffer = static_cast<NativeBuffer11*>(storage)->getNativeBuffer();
auto bufferSRVIt = mBufferResourceViews.find(srvFormat);
if (bufferSRVIt != mBufferResourceViews.end())
{
if (bufferSRVIt->second.first == buffer)
{
return bufferSRVIt->second.second;
}
else
{
// The underlying buffer has changed since the SRV was created: recreate the SRV.
SafeRelease(bufferSRVIt->second.second);
}
}
ID3D11Device *device = mRenderer->getDevice();
ID3D11ShaderResourceView *bufferSRV = NULL;
const d3d11::DXGIFormat &dxgiFormatInfo = d3d11::GetDXGIFormatInfo(srvFormat);
D3D11_SHADER_RESOURCE_VIEW_DESC bufferSRVDesc;
bufferSRVDesc.Buffer.ElementOffset = 0;
bufferSRVDesc.Buffer.ElementWidth = mSize / dxgiFormatInfo.pixelBytes;
bufferSRVDesc.ViewDimension = D3D11_SRV_DIMENSION_BUFFER;
bufferSRVDesc.Format = srvFormat;
HRESULT result = device->CreateShaderResourceView(buffer, &bufferSRVDesc, &bufferSRV);
UNUSED_ASSERTION_VARIABLE(result);
ASSERT(SUCCEEDED(result));
mBufferResourceViews[srvFormat] = BufferSRVPair(buffer, bufferSRV);
return bufferSRV;
}
gl::Error Buffer11::packPixels(ID3D11Texture2D *srcTexture, UINT srcSubresource, const PackPixelsParams &params)
{
PackStorage11 *packStorage = getPackStorage();
BufferStorage11 *latestStorage = getLatestBufferStorage();
if (packStorage)
{
gl::Error error = packStorage->packPixels(srcTexture, srcSubresource, params);
if (error.isError())
{
return error;
}
packStorage->setDataRevision(latestStorage ? latestStorage->getDataRevision() + 1 : 1);
}
return gl::Error(GL_NO_ERROR);
}
Buffer11::BufferStorage11 *Buffer11::getBufferStorage(BufferUsage usage)
{
BufferStorage11 *directBuffer = NULL;
auto directBufferIt = mBufferStorages.find(usage);
if (directBufferIt != mBufferStorages.end())
{
directBuffer = directBufferIt->second;
}
if (!directBuffer)
{
if (usage == BUFFER_USAGE_PIXEL_PACK)
{
directBuffer = new PackStorage11(mRenderer);
}
else
{
// buffer is not allocated, create it
directBuffer = new NativeBuffer11(mRenderer, usage);
}
mBufferStorages.insert(std::make_pair(usage, directBuffer));
}
// resize buffer
if (directBuffer->getSize() < mSize)
{
if (!directBuffer->resize(mSize, true))
{
// Out of memory error
return NULL;
}
}
BufferStorage11 *latestBuffer = getLatestBufferStorage();
if (latestBuffer && latestBuffer->getDataRevision() > directBuffer->getDataRevision())
{
// if copying from a pack buffer to a non-staging native buffer, we must first
// copy through the staging buffer, because other native buffers can't be mapped
if (latestBuffer->getUsage() == BUFFER_USAGE_PIXEL_PACK && !directBuffer->isMappable())
{
NativeBuffer11 *stagingBuffer = getStagingBuffer();
stagingBuffer->copyFromStorage(latestBuffer, 0, latestBuffer->getSize(), 0);
directBuffer->setDataRevision(latestBuffer->getDataRevision());
latestBuffer = stagingBuffer;
}
// if copyFromStorage returns true, the D3D buffer has been recreated
// and we should update our serial
if (directBuffer->copyFromStorage(latestBuffer, 0, latestBuffer->getSize(), 0))
{
updateSerial();
}
directBuffer->setDataRevision(latestBuffer->getDataRevision());
}
return directBuffer;
}
Buffer11::BufferStorage11 *Buffer11::getLatestBufferStorage() const
{
// Even though we iterate over all the direct buffers, it is expected that only
// 1 or 2 will be present.
BufferStorage11 *latestStorage = NULL;
DataRevision latestRevision = 0;
for (auto it = mBufferStorages.begin(); it != mBufferStorages.end(); it++)
{
BufferStorage11 *storage = it->second;
if (!latestStorage || storage->getDataRevision() > latestRevision)
{
latestStorage = storage;
latestRevision = storage->getDataRevision();
}
}
return latestStorage;
}
Buffer11::NativeBuffer11 *Buffer11::getStagingBuffer()
{
BufferStorage11 *stagingStorage = getBufferStorage(BUFFER_USAGE_STAGING);
if (!stagingStorage)
{
// Out-of-memory
return NULL;
}
ASSERT(HAS_DYNAMIC_TYPE(NativeBuffer11*, stagingStorage));
return static_cast<NativeBuffer11*>(stagingStorage);
}
Buffer11::PackStorage11 *Buffer11::getPackStorage()
{
BufferStorage11 *packStorage = getBufferStorage(BUFFER_USAGE_PIXEL_PACK);
if (!packStorage)
{
// Out-of-memory
return NULL;
}
ASSERT(HAS_DYNAMIC_TYPE(PackStorage11*, packStorage));
return static_cast<PackStorage11*>(packStorage);
}
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);
}
Buffer11::BufferStorage11::BufferStorage11(Renderer11 *renderer, BufferUsage usage)
: mRenderer(renderer),
mUsage(usage),
mRevision(0),
mBufferSize(0)
{
}
Buffer11::NativeBuffer11::NativeBuffer11(Renderer11 *renderer, BufferUsage usage)
: BufferStorage11(renderer, usage),
mNativeBuffer(NULL)
{
}
Buffer11::NativeBuffer11::~NativeBuffer11()
{
SafeRelease(mNativeBuffer);
}
// Returns true if it recreates the direct buffer
bool Buffer11::NativeBuffer11::copyFromStorage(BufferStorage11 *source, size_t sourceOffset,
size_t size, size_t destOffset)
{
ID3D11DeviceContext *context = mRenderer->getDeviceContext();
size_t requiredSize = sourceOffset + size;
bool createBuffer = !mNativeBuffer || mBufferSize < requiredSize;
// (Re)initialize D3D buffer if needed
if (createBuffer)
{
bool preserveData = (destOffset > 0);
resize(source->getSize(), preserveData);
}
if (source->getUsage() == BUFFER_USAGE_PIXEL_PACK)
{
ASSERT(HAS_DYNAMIC_TYPE(PackStorage11*, source));
void *sourcePointer = source->map(sourceOffset, size, GL_MAP_READ_BIT);
D3D11_MAPPED_SUBRESOURCE mappedResource;
HRESULT hr = context->Map(mNativeBuffer, 0, D3D11_MAP_WRITE, 0, &mappedResource);
UNUSED_ASSERTION_VARIABLE(hr);
ASSERT(SUCCEEDED(hr));
unsigned char *destPointer = static_cast<unsigned char *>(mappedResource.pData) + destOffset;
// Offset bounds are validated at the API layer
ASSERT(sourceOffset + size <= destOffset + mBufferSize);
memcpy(destPointer, sourcePointer, size);
context->Unmap(mNativeBuffer, 0);
source->unmap();
}
else
{
ASSERT(HAS_DYNAMIC_TYPE(NativeBuffer11*, source));
D3D11_BOX srcBox;
srcBox.left = sourceOffset;
srcBox.right = sourceOffset + size;
srcBox.top = 0;
srcBox.bottom = 1;
srcBox.front = 0;
srcBox.back = 1;
ASSERT(HAS_DYNAMIC_TYPE(NativeBuffer11*, source));
ID3D11Buffer *sourceBuffer = static_cast<NativeBuffer11*>(source)->getNativeBuffer();
context->CopySubresourceRegion(mNativeBuffer, 0, destOffset, 0, 0, sourceBuffer, 0, &srcBox);
}
return createBuffer;
}
bool Buffer11::NativeBuffer11::resize(size_t size, bool preserveData)
{
ID3D11Device *device = mRenderer->getDevice();
ID3D11DeviceContext *context = mRenderer->getDeviceContext();
D3D11_BUFFER_DESC bufferDesc;
fillBufferDesc(&bufferDesc, mRenderer, mUsage, size);
ID3D11Buffer *newBuffer;
HRESULT result = device->CreateBuffer(&bufferDesc, NULL, &newBuffer);
if (FAILED(result))
{
return gl::error(GL_OUT_OF_MEMORY, false);
}
if (mNativeBuffer && preserveData)
{
// We don't call resize if the buffer is big enough already.
ASSERT(mBufferSize <= size);
D3D11_BOX srcBox;
srcBox.left = 0;
srcBox.right = mBufferSize;
srcBox.top = 0;
srcBox.bottom = 1;
srcBox.front = 0;
srcBox.back = 1;
context->CopySubresourceRegion(newBuffer, 0, 0, 0, 0, mNativeBuffer, 0, &srcBox);
}
// No longer need the old buffer
SafeRelease(mNativeBuffer);
mNativeBuffer = newBuffer;
mBufferSize = bufferDesc.ByteWidth;
return true;
}
void Buffer11::NativeBuffer11::fillBufferDesc(D3D11_BUFFER_DESC* bufferDesc, Renderer *renderer,
BufferUsage usage, unsigned int bufferSize)
{
bufferDesc->ByteWidth = bufferSize;
bufferDesc->MiscFlags = 0;
bufferDesc->StructureByteStride = 0;
switch (usage)
{
case BUFFER_USAGE_STAGING:
bufferDesc->Usage = D3D11_USAGE_STAGING;
bufferDesc->BindFlags = 0;
bufferDesc->CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE;
break;
case BUFFER_USAGE_VERTEX_OR_TRANSFORM_FEEDBACK:
bufferDesc->Usage = D3D11_USAGE_DEFAULT;
bufferDesc->BindFlags = D3D11_BIND_VERTEX_BUFFER | D3D11_BIND_STREAM_OUTPUT;
bufferDesc->CPUAccessFlags = 0;
break;
case BUFFER_USAGE_INDEX:
bufferDesc->Usage = D3D11_USAGE_DEFAULT;
bufferDesc->BindFlags = D3D11_BIND_INDEX_BUFFER;
bufferDesc->CPUAccessFlags = 0;
break;
case BUFFER_USAGE_PIXEL_UNPACK:
bufferDesc->Usage = D3D11_USAGE_DEFAULT;
bufferDesc->BindFlags = D3D11_BIND_SHADER_RESOURCE;
bufferDesc->CPUAccessFlags = 0;
break;
case BUFFER_USAGE_UNIFORM:
bufferDesc->Usage = D3D11_USAGE_DYNAMIC;
bufferDesc->BindFlags = D3D11_BIND_CONSTANT_BUFFER;
bufferDesc->CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
// Constant buffers must be of a limited size, and aligned to 16 byte boundaries
// For our purposes we ignore any buffer data past the maximum constant buffer size
bufferDesc->ByteWidth = roundUp(bufferDesc->ByteWidth, 16u);
bufferDesc->ByteWidth = std::min<UINT>(bufferDesc->ByteWidth, renderer->getRendererCaps().maxUniformBlockSize);
break;
default:
UNREACHABLE();
}
}
void *Buffer11::NativeBuffer11::map(size_t offset, size_t length, GLbitfield access)
{
ASSERT(mUsage == BUFFER_USAGE_STAGING);
D3D11_MAPPED_SUBRESOURCE mappedResource;
ID3D11DeviceContext *context = mRenderer->getDeviceContext();
D3D11_MAP d3dMapType = gl_d3d11::GetD3DMapTypeFromBits(access);
UINT d3dMapFlag = ((access & GL_MAP_UNSYNCHRONIZED_BIT) != 0 ? D3D11_MAP_FLAG_DO_NOT_WAIT : 0);
HRESULT result = context->Map(mNativeBuffer, 0, d3dMapType, d3dMapFlag, &mappedResource);
UNUSED_ASSERTION_VARIABLE(result);
ASSERT(SUCCEEDED(result));
return static_cast<GLubyte*>(mappedResource.pData) + offset;
}
bool Buffer11::NativeBuffer11::setData(D3D11_MAP mapMode, const uint8_t *data, size_t size, size_t offset)
{
ID3D11DeviceContext *context = mRenderer->getDeviceContext();
D3D11_MAPPED_SUBRESOURCE mappedResource;
HRESULT result = context->Map(mNativeBuffer, 0, mapMode, 0, &mappedResource);
if (FAILED(result))
{
return gl::error(GL_OUT_OF_MEMORY, false);
}
uint8_t *offsetBufferPointer = reinterpret_cast<uint8_t *>(mappedResource.pData) + offset;
memcpy(offsetBufferPointer, data, size);
context->Unmap(mNativeBuffer, 0);
return true;
}
void Buffer11::NativeBuffer11::unmap()
{
ASSERT(mUsage == BUFFER_USAGE_STAGING);
ID3D11DeviceContext *context = mRenderer->getDeviceContext();
context->Unmap(mNativeBuffer, 0);
}
Buffer11::PackStorage11::PackStorage11(Renderer11 *renderer)
: BufferStorage11(renderer, BUFFER_USAGE_PIXEL_PACK),
mStagingTexture(NULL),
mTextureFormat(DXGI_FORMAT_UNKNOWN),
mQueuedPackCommand(NULL),
mDataModified(false)
{
}
Buffer11::PackStorage11::~PackStorage11()
{
SafeRelease(mStagingTexture);
SafeDelete(mQueuedPackCommand);
}
bool Buffer11::PackStorage11::copyFromStorage(BufferStorage11 *source, size_t sourceOffset,
size_t size, size_t destOffset)
{
// We copy through a staging buffer when drawing with a pack buffer,
// or for other cases where we access the pack buffer
UNREACHABLE();
return false;
}
bool Buffer11::PackStorage11::resize(size_t size, bool preserveData)
{
if (size != mBufferSize)
{
if (!mMemoryBuffer.resize(size))
{
return false;
}
mBufferSize = size;
}
return true;
}
void *Buffer11::PackStorage11::map(size_t offset, size_t length, GLbitfield access)
{
ASSERT(offset + length <= getSize());
// TODO: fast path
// We might be able to optimize out one or more memcpy calls by detecting when
// and if D3D packs the staging texture memory identically to how we would fill
// the pack buffer according to the current pack state.
gl::Error error = flushQueuedPackCommand();
if (error.isError())
{
return NULL;
}
mDataModified = (mDataModified || (access & GL_MAP_WRITE_BIT) != 0);
return mMemoryBuffer.data() + offset;
}
void Buffer11::PackStorage11::unmap()
{
// No-op
}
gl::Error Buffer11::PackStorage11::packPixels(ID3D11Texture2D *srcTexure, UINT srcSubresource, const PackPixelsParams &params)
{
gl::Error error = flushQueuedPackCommand();
if (error.isError())
{
return error;
}
mQueuedPackCommand = new PackPixelsParams(params);
D3D11_TEXTURE2D_DESC textureDesc;
srcTexure->GetDesc(&textureDesc);
if (mStagingTexture != NULL &&
(mTextureFormat != textureDesc.Format ||
mTextureSize.width != params.area.width ||
mTextureSize.height != params.area.height))
{
SafeRelease(mStagingTexture);
mTextureSize.width = 0;
mTextureSize.height = 0;
mTextureFormat = DXGI_FORMAT_UNKNOWN;
}
if (mStagingTexture == NULL)
{
ID3D11Device *device = mRenderer->getDevice();
HRESULT hr;
mTextureSize.width = params.area.width;
mTextureSize.height = params.area.height;
mTextureFormat = textureDesc.Format;
D3D11_TEXTURE2D_DESC stagingDesc;
stagingDesc.Width = params.area.width;
stagingDesc.Height = params.area.height;
stagingDesc.MipLevels = 1;
stagingDesc.ArraySize = 1;
stagingDesc.Format = mTextureFormat;
stagingDesc.SampleDesc.Count = 1;
stagingDesc.SampleDesc.Quality = 0;
stagingDesc.Usage = D3D11_USAGE_STAGING;
stagingDesc.BindFlags = 0;
stagingDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
stagingDesc.MiscFlags = 0;
hr = device->CreateTexture2D(&stagingDesc, NULL, &mStagingTexture);
if (FAILED(hr))
{
ASSERT(hr == E_OUTOFMEMORY);
return gl::Error(GL_OUT_OF_MEMORY, "Failed to allocate internal staging texture.");
}
}
// ReadPixels from multisampled FBOs isn't supported in current GL
ASSERT(textureDesc.SampleDesc.Count <= 1);
ID3D11DeviceContext *immediateContext = mRenderer->getDeviceContext();
D3D11_BOX srcBox;
srcBox.left = params.area.x;
srcBox.right = params.area.x + params.area.width;
srcBox.top = params.area.y;
srcBox.bottom = params.area.y + params.area.height;
srcBox.front = 0;
srcBox.back = 1;
// Asynchronous copy
immediateContext->CopySubresourceRegion(mStagingTexture, 0, 0, 0, 0, srcTexure, srcSubresource, &srcBox);
return gl::Error(GL_NO_ERROR);
}
gl::Error Buffer11::PackStorage11::flushQueuedPackCommand()
{
ASSERT(mMemoryBuffer.size() > 0);
if (mQueuedPackCommand)
{
gl::Error error = mRenderer->packPixels(mStagingTexture, *mQueuedPackCommand, mMemoryBuffer.data());
SafeDelete(mQueuedPackCommand);
if (error.isError())
{
return error;
}
}
return gl::Error(GL_NO_ERROR);
}
}