diff options
Diffstat (limited to 'src/3rdparty/angle/src/libGLESv2/renderer/d3d/d3d11/Buffer11.cpp')
-rw-r--r-- | src/3rdparty/angle/src/libGLESv2/renderer/d3d/d3d11/Buffer11.cpp | 900 |
1 files changed, 900 insertions, 0 deletions
diff --git a/src/3rdparty/angle/src/libGLESv2/renderer/d3d/d3d11/Buffer11.cpp b/src/3rdparty/angle/src/libGLESv2/renderer/d3d/d3d11/Buffer11.cpp new file mode 100644 index 0000000000..352da9654a --- /dev/null +++ b/src/3rdparty/angle/src/libGLESv2/renderer/d3d/d3d11/Buffer11.cpp @@ -0,0 +1,900 @@ +#include "precompiled.h" +// +// 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/main.h" +#include "libGLESv2/renderer/d3d/d3d11/Renderer11.h" +#include "libGLESv2/renderer/d3d/d3d11/formatutils11.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(); + + 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(); + + void packPixels(ID3D11Texture2D *srcTexure, UINT srcSubresource, const PackPixelsParams ¶ms); + + private: + + void 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() +{ + clear(); +} + +Buffer11 *Buffer11::makeBuffer11(BufferImpl *buffer) +{ + ASSERT(HAS_DYNAMIC_TYPE(Buffer11*, buffer)); + return static_cast<Buffer11*>(buffer); +} + +void Buffer11::clear() +{ + for (auto it = mBufferStorages.begin(); it != mBufferStorages.end(); it++) + { + SafeDelete(it->second); + } + + mBufferStorages.clear(); + + mSize = 0; + mResolvedDataRevision = 0; +} + +void Buffer11::setData(const void* data, size_t size, GLenum usage) +{ + mIndexRangeCache.clear(); + + setSubData(data, size, 0); + + if (usage == GL_STATIC_DRAW) + { + initializeStaticData(); + } +} + +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; + + return mResolvedData.data(); +} + +void Buffer11::setSubData(const void* data, size_t size, size_t offset) +{ + size_t requiredSize = size + offset; + mSize = std::max(mSize, requiredSize); + + mIndexRangeCache.invalidateRange(offset, size); + invalidateStaticData(); + + if (data && size > 0) + { + NativeBuffer11 *stagingBuffer = getStagingBuffer(); + + if (!stagingBuffer) + { + // Out-of-memory + return; + } + + // 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)) + { + // Out-of-memory + return; + } + } + + ID3D11DeviceContext *context = mRenderer->getDeviceContext(); + + D3D11_MAPPED_SUBRESOURCE mappedResource; + HRESULT result = context->Map(stagingBuffer->getNativeBuffer(), 0, D3D11_MAP_WRITE, 0, &mappedResource); + if (FAILED(result)) + { + return gl::error(GL_OUT_OF_MEMORY); + } + + unsigned char *offsetBufferPointer = reinterpret_cast<unsigned char *>(mappedResource.pData) + offset; + memcpy(offsetBufferPointer, data, size); + + context->Unmap(stagingBuffer->getNativeBuffer(), 0); + + stagingBuffer->setDataRevision(stagingBuffer->getDataRevision() + 1); + } +} + +void Buffer11::copySubData(BufferImpl* source, GLintptr sourceOffset, GLintptr destOffset, GLsizeiptr size) +{ + Buffer11 *sourceBuffer = makeBuffer11(source); + if (sourceBuffer) + { + BufferStorage11 *dest = getLatestBufferStorage(); + if (!dest) + { + dest = getStagingBuffer(); + } + + BufferStorage11 *source = sourceBuffer->getLatestBufferStorage(); + if (source && dest) + { + // 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 (dest->getUsage() == BUFFER_USAGE_PIXEL_PACK && !source->isMappable()) + { + source = sourceBuffer->getStagingBuffer(); + } + else if (source->getUsage() == BUFFER_USAGE_PIXEL_PACK && !dest->isMappable()) + { + dest = getStagingBuffer(); + } + + dest->copyFromStorage(source, sourceOffset, size, destOffset); + dest->setDataRevision(dest->getDataRevision() + 1); + } + + mSize = std::max<size_t>(mSize, destOffset + size); + } + + invalidateStaticData(); +} + +GLvoid *Buffer11::map(size_t offset, size_t length, GLbitfield access) +{ + 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) + { + // Out-of-memory + return NULL; + } + + 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); + } + + return mMappedStorage->map(offset, length, access); +} + +void Buffer11::unmap() +{ + ASSERT(mMappedStorage); + mMappedStorage->unmap(); + mMappedStorage = NULL; +} + +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; + + D3D11_SHADER_RESOURCE_VIEW_DESC bufferSRVDesc; + bufferSRVDesc.Buffer.ElementOffset = 0; + bufferSRVDesc.Buffer.ElementWidth = mSize / d3d11::GetFormatPixelBytes(srvFormat); + 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; +} + +void Buffer11::packPixels(ID3D11Texture2D *srcTexture, UINT srcSubresource, const PackPixelsParams ¶ms) +{ + PackStorage11 *packStorage = getPackStorage(); + + BufferStorage11 *latestStorage = getLatestBufferStorage(); + + if (packStorage) + { + packStorage->packPixels(srcTexture, srcSubresource, params); + packStorage->setDataRevision(latestStorage ? latestStorage->getDataRevision() + 1 : 1); + } +} + +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); +} + +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); + } + 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; + if (renderer->getMaxTransformFeedbackBuffers() > 0) + bufferDesc->BindFlags |= 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(bufferDesc->ByteWidth, renderer->getMaxUniformBufferSize()); + 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; +} + +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) +{ + UNIMPLEMENTED(); + 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. + + flushQueuedPackCommand(); + mDataModified = (mDataModified || (access & GL_MAP_WRITE_BIT) != 0); + + return mMemoryBuffer.data() + offset; +} + +void Buffer11::PackStorage11::unmap() +{ + // No-op +} + +void Buffer11::PackStorage11::packPixels(ID3D11Texture2D *srcTexure, UINT srcSubresource, const PackPixelsParams ¶ms) +{ + flushQueuedPackCommand(); + 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); + ASSERT(SUCCEEDED(hr)); + } + + if (textureDesc.SampleDesc.Count > 1) + { + UNIMPLEMENTED(); + } + + 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); +} + +void Buffer11::PackStorage11::flushQueuedPackCommand() +{ + ASSERT(mMemoryBuffer.size() > 0); + + if (mQueuedPackCommand) + { + mRenderer->packPixels(mStagingTexture, *mQueuedPackCommand, mMemoryBuffer.data()); + SafeDelete(mQueuedPackCommand); + } +} + +} |