| // |
| // 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 "libANGLE/renderer/d3d/d3d11/Buffer11.h" |
| |
| #include <memory> |
| |
| #include "common/MemoryBuffer.h" |
| #include "libANGLE/Context.h" |
| #include "libANGLE/renderer/d3d/IndexDataManager.h" |
| #include "libANGLE/renderer/d3d/VertexDataManager.h" |
| #include "libANGLE/renderer/d3d/d3d11/Context11.h" |
| #include "libANGLE/renderer/d3d/d3d11/RenderTarget11.h" |
| #include "libANGLE/renderer/d3d/d3d11/Renderer11.h" |
| #include "libANGLE/renderer/d3d/d3d11/formatutils11.h" |
| #include "libANGLE/renderer/d3d/d3d11/renderer11_utils.h" |
| #include "libANGLE/renderer/renderer_utils.h" |
| |
| namespace rx |
| { |
| |
| namespace |
| { |
| |
| template <typename T> |
| GLuint ReadIndexValueFromIndices(const uint8_t *data, size_t index) |
| { |
| return reinterpret_cast<const T *>(data)[index]; |
| } |
| typedef GLuint (*ReadIndexValueFunction)(const uint8_t *data, size_t index); |
| |
| enum class CopyResult |
| { |
| RECREATED, |
| NOT_RECREATED, |
| }; |
| |
| void CalculateConstantBufferParams(GLintptr offset, |
| GLsizeiptr size, |
| UINT *outFirstConstant, |
| UINT *outNumConstants) |
| { |
| // The offset must be aligned to 256 bytes (should have been enforced by glBindBufferRange). |
| ASSERT(offset % 256 == 0); |
| |
| // firstConstant and numConstants are expressed in constants of 16-bytes. Furthermore they must |
| // be a multiple of 16 constants. |
| *outFirstConstant = static_cast<UINT>(offset / 16); |
| |
| // The GL size is not required to be aligned to a 256 bytes boundary. |
| // Round the size up to a 256 bytes boundary then express the results in constants of 16-bytes. |
| *outNumConstants = static_cast<UINT>(rx::roundUp(size, static_cast<GLsizeiptr>(256)) / 16); |
| |
| // Since the size is rounded up, firstConstant + numConstants may be bigger than the actual size |
| // of the buffer. This behaviour is explictly allowed according to the documentation on |
| // ID3D11DeviceContext1::PSSetConstantBuffers1 |
| // https://msdn.microsoft.com/en-us/library/windows/desktop/hh404649%28v=vs.85%29.aspx |
| } |
| |
| } // anonymous namespace |
| |
| namespace gl_d3d11 |
| { |
| |
| D3D11_MAP GetD3DMapTypeFromBits(BufferUsage usage, 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) |
| { |
| // Special case for uniform storage - we only allow full buffer updates. |
| return usage == BUFFER_USAGE_UNIFORM ? D3D11_MAP_WRITE_DISCARD : D3D11_MAP_WRITE; |
| } |
| else if (writeBit && readBit) |
| { |
| return D3D11_MAP_READ_WRITE; |
| } |
| else |
| { |
| UNREACHABLE(); |
| return D3D11_MAP_READ; |
| } |
| } |
| } // namespace gl_d3d11 |
| |
| // Each instance of Buffer11::BufferStorage is specialized for a class of D3D binding points |
| // - vertex/transform feedback buffers |
| // - index buffers |
| // - pixel unpack buffers |
| // - uniform buffers |
| class Buffer11::BufferStorage : angle::NonCopyable |
| { |
| public: |
| virtual ~BufferStorage() {} |
| |
| DataRevision getDataRevision() const { return mRevision; } |
| BufferUsage getUsage() const { return mUsage; } |
| size_t getSize() const { return mBufferSize; } |
| void setDataRevision(DataRevision rev) { mRevision = rev; } |
| |
| virtual bool isCPUAccessible(GLbitfield access) const = 0; |
| |
| virtual bool isGPUAccessible() const = 0; |
| |
| virtual angle::Result copyFromStorage(const gl::Context *context, |
| BufferStorage *source, |
| size_t sourceOffset, |
| size_t size, |
| size_t destOffset, |
| CopyResult *resultOut) = 0; |
| virtual angle::Result resize(const gl::Context *context, size_t size, bool preserveData) = 0; |
| |
| virtual angle::Result map(const gl::Context *context, |
| size_t offset, |
| size_t length, |
| GLbitfield access, |
| uint8_t **mapPointerOut) = 0; |
| virtual void unmap() = 0; |
| |
| angle::Result setData(const gl::Context *context, |
| const uint8_t *data, |
| size_t offset, |
| size_t size); |
| |
| protected: |
| BufferStorage(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::NativeStorage : public Buffer11::BufferStorage |
| { |
| public: |
| NativeStorage(Renderer11 *renderer, BufferUsage usage, const angle::Subject *onStorageChanged); |
| ~NativeStorage() override; |
| |
| bool isCPUAccessible(GLbitfield access) const override; |
| |
| bool isGPUAccessible() const override { return true; } |
| |
| const d3d11::Buffer &getBuffer() const { return mBuffer; } |
| angle::Result copyFromStorage(const gl::Context *context, |
| BufferStorage *source, |
| size_t sourceOffset, |
| size_t size, |
| size_t destOffset, |
| CopyResult *resultOut) override; |
| angle::Result resize(const gl::Context *context, size_t size, bool preserveData) override; |
| |
| angle::Result map(const gl::Context *context, |
| size_t offset, |
| size_t length, |
| GLbitfield access, |
| uint8_t **mapPointerOut) override; |
| void unmap() override; |
| |
| angle::Result getSRVForFormat(const gl::Context *context, |
| DXGI_FORMAT srvFormat, |
| const d3d11::ShaderResourceView **srvOut); |
| angle::Result getRawUAV(const gl::Context *context, |
| unsigned int offset, |
| unsigned int size, |
| d3d11::UnorderedAccessView **uavOut); |
| |
| private: |
| static void FillBufferDesc(D3D11_BUFFER_DESC *bufferDesc, |
| Renderer11 *renderer, |
| BufferUsage usage, |
| unsigned int bufferSize); |
| void clearSRVs(); |
| void clearUAVs(); |
| |
| d3d11::Buffer mBuffer; |
| const angle::Subject *mOnStorageChanged; |
| std::map<DXGI_FORMAT, d3d11::ShaderResourceView> mBufferResourceViews; |
| std::map<std::pair<unsigned int, unsigned int>, d3d11::UnorderedAccessView> mBufferRawUAVs; |
| }; |
| |
| // A emulated indexed buffer storage represents an underlying D3D11 buffer for data |
| // that has been expanded to match the indices list used. This storage is only |
| // used for FL9_3 pointsprite rendering emulation. |
| class Buffer11::EmulatedIndexedStorage : public Buffer11::BufferStorage |
| { |
| public: |
| EmulatedIndexedStorage(Renderer11 *renderer); |
| ~EmulatedIndexedStorage() override; |
| |
| bool isCPUAccessible(GLbitfield access) const override { return true; } |
| |
| bool isGPUAccessible() const override { return false; } |
| |
| angle::Result getBuffer(const gl::Context *context, |
| SourceIndexData *indexInfo, |
| const TranslatedAttribute &attribute, |
| GLint startVertex, |
| const d3d11::Buffer **bufferOut); |
| |
| angle::Result copyFromStorage(const gl::Context *context, |
| BufferStorage *source, |
| size_t sourceOffset, |
| size_t size, |
| size_t destOffset, |
| CopyResult *resultOut) override; |
| |
| angle::Result resize(const gl::Context *context, size_t size, bool preserveData) override; |
| |
| angle::Result map(const gl::Context *context, |
| size_t offset, |
| size_t length, |
| GLbitfield access, |
| uint8_t **mapPointerOut) override; |
| void unmap() override; |
| |
| private: |
| d3d11::Buffer mBuffer; // contains expanded data for use by D3D |
| angle::MemoryBuffer mMemoryBuffer; // original data (not expanded) |
| angle::MemoryBuffer mIndicesMemoryBuffer; // indices data |
| }; |
| |
| // 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::PackStorage : public Buffer11::BufferStorage |
| { |
| public: |
| explicit PackStorage(Renderer11 *renderer); |
| ~PackStorage() override; |
| |
| bool isCPUAccessible(GLbitfield access) const override { return true; } |
| |
| bool isGPUAccessible() const override { return false; } |
| |
| angle::Result copyFromStorage(const gl::Context *context, |
| BufferStorage *source, |
| size_t sourceOffset, |
| size_t size, |
| size_t destOffset, |
| CopyResult *resultOut) override; |
| angle::Result resize(const gl::Context *context, size_t size, bool preserveData) override; |
| |
| angle::Result map(const gl::Context *context, |
| size_t offset, |
| size_t length, |
| GLbitfield access, |
| uint8_t **mapPointerOut) override; |
| void unmap() override; |
| |
| angle::Result packPixels(const gl::Context *context, |
| const gl::FramebufferAttachment &readAttachment, |
| const PackPixelsParams ¶ms); |
| |
| private: |
| angle::Result flushQueuedPackCommand(const gl::Context *context); |
| |
| TextureHelper11 mStagingTexture; |
| angle::MemoryBuffer mMemoryBuffer; |
| std::unique_ptr<PackPixelsParams> mQueuedPackCommand; |
| PackPixelsParams mPackParams; |
| 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 isCPUAccessible(GLbitfield access) const override { return true; } |
| |
| bool isGPUAccessible() const override { return false; } |
| |
| angle::Result copyFromStorage(const gl::Context *context, |
| BufferStorage *source, |
| size_t sourceOffset, |
| size_t size, |
| size_t destOffset, |
| CopyResult *resultOut) override; |
| angle::Result resize(const gl::Context *context, size_t size, bool preserveData) override; |
| |
| angle::Result map(const gl::Context *context, |
| size_t offset, |
| size_t length, |
| GLbitfield access, |
| uint8_t **mapPointerOut) override; |
| void unmap() override; |
| |
| angle::MemoryBuffer *getSystemCopy() { return &mSystemCopy; } |
| |
| protected: |
| angle::MemoryBuffer mSystemCopy; |
| }; |
| |
| Buffer11::Buffer11(const gl::BufferState &state, Renderer11 *renderer) |
| : BufferD3D(state, renderer), |
| mRenderer(renderer), |
| mSize(0), |
| mMappedStorage(nullptr), |
| mBufferStorages({}), |
| mLatestBufferStorage(nullptr), |
| mDeallocThresholds({}), |
| mIdleness({}), |
| mConstantBufferStorageAdditionalSize(0), |
| mMaxConstantBufferLruCount(0) |
| {} |
| |
| Buffer11::~Buffer11() |
| { |
| for (BufferStorage *&storage : mBufferStorages) |
| { |
| SafeDelete(storage); |
| } |
| |
| for (auto &p : mConstantBufferRangeStoragesCache) |
| { |
| SafeDelete(p.second.storage); |
| } |
| |
| mRenderer->onBufferDelete(this); |
| } |
| |
| angle::Result Buffer11::setData(const gl::Context *context, |
| gl::BufferBinding target, |
| const void *data, |
| size_t size, |
| gl::BufferUsage usage) |
| { |
| updateD3DBufferUsage(context, usage); |
| return setSubData(context, target, data, size, 0); |
| } |
| |
| angle::Result Buffer11::getData(const gl::Context *context, const uint8_t **outData) |
| { |
| if (mSize == 0) |
| { |
| // TODO(http://anglebug.com/2840): This ensures that we don't crash or assert in robust |
| // buffer access behavior mode if there are buffers without any data. However, technically |
| // it should still be possible to draw, with fetches from this buffer returning zero. |
| return angle::Result::Stop; |
| } |
| |
| SystemMemoryStorage *systemMemoryStorage = nullptr; |
| ANGLE_TRY(getBufferStorage(context, BUFFER_USAGE_SYSTEM_MEMORY, &systemMemoryStorage)); |
| |
| ASSERT(systemMemoryStorage->getSize() >= mSize); |
| |
| *outData = systemMemoryStorage->getSystemCopy()->data(); |
| return angle::Result::Continue; |
| } |
| |
| angle::Result Buffer11::setSubData(const gl::Context *context, |
| gl::BufferBinding target, |
| const void *data, |
| size_t size, |
| size_t offset) |
| { |
| size_t requiredSize = size + offset; |
| |
| if (data && size > 0) |
| { |
| // Use system memory storage for dynamic buffers. |
| // Try using a constant storage for constant buffers |
| BufferStorage *writeBuffer = nullptr; |
| if (target == gl::BufferBinding::Uniform) |
| { |
| // If we are a very large uniform buffer, keep system memory storage around so that we |
| // aren't forced to read back from a constant buffer. We also check the workaround for |
| // Intel - this requires us to use system memory so we don't end up having to copy from |
| // a constant buffer to a staging buffer. |
| // TODO(jmadill): Use Context caps. |
| if (offset == 0 && size >= mSize && |
| size <= static_cast<UINT>(mRenderer->getNativeCaps().maxUniformBlockSize) && |
| !mRenderer->getFeatures().useSystemMemoryForConstantBuffers.enabled) |
| { |
| ANGLE_TRY(getBufferStorage(context, BUFFER_USAGE_UNIFORM, &writeBuffer)); |
| } |
| else |
| { |
| ANGLE_TRY(getBufferStorage(context, BUFFER_USAGE_SYSTEM_MEMORY, &writeBuffer)); |
| } |
| } |
| else if (supportsDirectBinding()) |
| { |
| ANGLE_TRY(getStagingStorage(context, &writeBuffer)); |
| } |
| else |
| { |
| ANGLE_TRY(getBufferStorage(context, BUFFER_USAGE_SYSTEM_MEMORY, &writeBuffer)); |
| } |
| |
| ASSERT(writeBuffer); |
| |
| // Explicitly resize the staging buffer, preserving data if the new data will not |
| // completely fill the buffer |
| if (writeBuffer->getSize() < requiredSize) |
| { |
| bool preserveData = (offset > 0); |
| ANGLE_TRY(writeBuffer->resize(context, requiredSize, preserveData)); |
| } |
| |
| ANGLE_TRY(writeBuffer->setData(context, static_cast<const uint8_t *>(data), offset, size)); |
| onStorageUpdate(writeBuffer); |
| } |
| |
| mSize = std::max(mSize, requiredSize); |
| invalidateStaticData(context); |
| |
| return angle::Result::Continue; |
| } |
| |
| angle::Result Buffer11::copySubData(const gl::Context *context, |
| BufferImpl *source, |
| GLintptr sourceOffset, |
| GLintptr destOffset, |
| GLsizeiptr size) |
| { |
| Buffer11 *sourceBuffer = GetAs<Buffer11>(source); |
| ASSERT(sourceBuffer != nullptr); |
| |
| BufferStorage *copyDest = nullptr; |
| ANGLE_TRY(getLatestBufferStorage(context, ©Dest)); |
| |
| if (!copyDest) |
| { |
| ANGLE_TRY(getStagingStorage(context, ©Dest)); |
| } |
| |
| BufferStorage *copySource = nullptr; |
| ANGLE_TRY(sourceBuffer->getLatestBufferStorage(context, ©Source)); |
| |
| if (!copySource) |
| { |
| ANGLE_TRY(sourceBuffer->getStagingStorage(context, ©Source)); |
| } |
| |
| ASSERT(copySource && copyDest); |
| |
| // A staging buffer is needed if there is no cpu-cpu or gpu-gpu copy path avaiable. |
| if (!copyDest->isGPUAccessible() && !copySource->isCPUAccessible(GL_MAP_READ_BIT)) |
| { |
| ANGLE_TRY(sourceBuffer->getStagingStorage(context, ©Source)); |
| } |
| else if (!copySource->isGPUAccessible() && !copyDest->isCPUAccessible(GL_MAP_WRITE_BIT)) |
| { |
| ANGLE_TRY(getStagingStorage(context, ©Dest)); |
| } |
| |
| // 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) |
| { |
| ANGLE_TRY( |
| getBufferStorage(context, BUFFER_USAGE_VERTEX_OR_TRANSFORM_FEEDBACK, ©Source)); |
| } |
| else |
| { |
| ANGLE_TRY(getStagingStorage(context, ©Source)); |
| } |
| } |
| |
| CopyResult copyResult = CopyResult::NOT_RECREATED; |
| ANGLE_TRY(copyDest->copyFromStorage(context, copySource, sourceOffset, size, destOffset, |
| ©Result)); |
| onStorageUpdate(copyDest); |
| |
| mSize = std::max<size_t>(mSize, destOffset + size); |
| invalidateStaticData(context); |
| |
| return angle::Result::Continue; |
| } |
| |
| angle::Result Buffer11::map(const gl::Context *context, GLenum access, void **mapPtr) |
| { |
| // GL_OES_mapbuffer uses an enum instead of a bitfield for it's access, convert to a bitfield |
| // and call mapRange. |
| ASSERT(access == GL_WRITE_ONLY_OES); |
| return mapRange(context, 0, mSize, GL_MAP_WRITE_BIT, mapPtr); |
| } |
| |
| angle::Result Buffer11::mapRange(const gl::Context *context, |
| size_t offset, |
| size_t length, |
| GLbitfield access, |
| void **mapPtr) |
| { |
| ASSERT(!mMappedStorage); |
| |
| BufferStorage *latestStorage = nullptr; |
| ANGLE_TRY(getLatestBufferStorage(context, &latestStorage)); |
| |
| 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. |
| ANGLE_TRY(getStagingStorage(context, &mMappedStorage)); |
| } |
| |
| Context11 *context11 = GetImplAs<Context11>(context); |
| ANGLE_CHECK_GL_ALLOC(context11, mMappedStorage); |
| |
| if ((access & GL_MAP_WRITE_BIT) > 0) |
| { |
| // Update the data revision immediately, since the data might be changed at any time |
| onStorageUpdate(mMappedStorage); |
| invalidateStaticData(context); |
| } |
| |
| uint8_t *mappedBuffer = nullptr; |
| ANGLE_TRY(mMappedStorage->map(context, offset, length, access, &mappedBuffer)); |
| ASSERT(mappedBuffer); |
| |
| *mapPtr = static_cast<void *>(mappedBuffer); |
| return angle::Result::Continue; |
| } |
| |
| angle::Result Buffer11::unmap(const gl::Context *context, GLboolean *result) |
| { |
| ASSERT(mMappedStorage); |
| mMappedStorage->unmap(); |
| mMappedStorage = nullptr; |
| |
| // TODO: detect if we had corruption. if so, return false. |
| *result = GL_TRUE; |
| |
| return angle::Result::Continue; |
| } |
| |
| angle::Result Buffer11::markTransformFeedbackUsage(const gl::Context *context) |
| { |
| ANGLE_TRY(markBufferUsage(context, BUFFER_USAGE_VERTEX_OR_TRANSFORM_FEEDBACK)); |
| return angle::Result::Continue; |
| } |
| |
| void Buffer11::updateDeallocThreshold(BufferUsage usage) |
| { |
| // The following strategy was tuned on the Oort online benchmark (http://oortonline.gl/) |
| // as well as a custom microbenchmark (IndexConversionPerfTest.Run/index_range_d3d11) |
| |
| // First readback: 8 unmodified uses before we free buffer memory. |
| // After that, double the threshold each time until we reach the max. |
| if (mDeallocThresholds[usage] == 0) |
| { |
| mDeallocThresholds[usage] = 8; |
| } |
| else if (mDeallocThresholds[usage] < std::numeric_limits<unsigned int>::max() / 2u) |
| { |
| mDeallocThresholds[usage] *= 2u; |
| } |
| else |
| { |
| mDeallocThresholds[usage] = std::numeric_limits<unsigned int>::max(); |
| } |
| } |
| |
| // Free the storage if we decide it isn't being used very often. |
| angle::Result Buffer11::checkForDeallocation(const gl::Context *context, BufferUsage usage) |
| { |
| mIdleness[usage]++; |
| |
| BufferStorage *&storage = mBufferStorages[usage]; |
| if (storage != nullptr && mIdleness[usage] > mDeallocThresholds[usage]) |
| { |
| BufferStorage *latestStorage = nullptr; |
| ANGLE_TRY(getLatestBufferStorage(context, &latestStorage)); |
| if (latestStorage != storage) |
| { |
| SafeDelete(storage); |
| } |
| } |
| |
| return angle::Result::Continue; |
| } |
| |
| // Keep system memory when we are using it for the canonical version of data. |
| bool Buffer11::canDeallocateSystemMemory() const |
| { |
| // Must keep system memory on Intel. |
| if (mRenderer->getFeatures().useSystemMemoryForConstantBuffers.enabled) |
| { |
| return false; |
| } |
| |
| return (!mBufferStorages[BUFFER_USAGE_UNIFORM] || |
| mSize <= static_cast<size_t>(mRenderer->getNativeCaps().maxUniformBlockSize)); |
| } |
| |
| void Buffer11::markBufferUsage(BufferUsage usage) |
| { |
| mIdleness[usage] = 0; |
| } |
| |
| angle::Result Buffer11::markBufferUsage(const gl::Context *context, BufferUsage usage) |
| { |
| BufferStorage *bufferStorage = nullptr; |
| ANGLE_TRY(getBufferStorage(context, usage, &bufferStorage)); |
| |
| if (bufferStorage) |
| { |
| onStorageUpdate(bufferStorage); |
| } |
| |
| invalidateStaticData(context); |
| return angle::Result::Continue; |
| } |
| |
| angle::Result Buffer11::garbageCollection(const gl::Context *context, BufferUsage currentUsage) |
| { |
| if (currentUsage != BUFFER_USAGE_SYSTEM_MEMORY && canDeallocateSystemMemory()) |
| { |
| ANGLE_TRY(checkForDeallocation(context, BUFFER_USAGE_SYSTEM_MEMORY)); |
| } |
| |
| if (currentUsage != BUFFER_USAGE_STAGING) |
| { |
| ANGLE_TRY(checkForDeallocation(context, BUFFER_USAGE_STAGING)); |
| } |
| |
| return angle::Result::Continue; |
| } |
| |
| angle::Result Buffer11::getBuffer(const gl::Context *context, |
| BufferUsage usage, |
| ID3D11Buffer **bufferOut) |
| { |
| NativeStorage *storage = nullptr; |
| ANGLE_TRY(getBufferStorage(context, usage, &storage)); |
| *bufferOut = storage->getBuffer().get(); |
| return angle::Result::Continue; |
| } |
| |
| angle::Result Buffer11::getEmulatedIndexedBuffer(const gl::Context *context, |
| SourceIndexData *indexInfo, |
| const TranslatedAttribute &attribute, |
| GLint startVertex, |
| ID3D11Buffer **bufferOut) |
| { |
| ASSERT(indexInfo); |
| |
| EmulatedIndexedStorage *emulatedStorage = nullptr; |
| ANGLE_TRY(getBufferStorage(context, BUFFER_USAGE_EMULATED_INDEXED_VERTEX, &emulatedStorage)); |
| |
| const d3d11::Buffer *nativeBuffer = nullptr; |
| ANGLE_TRY( |
| emulatedStorage->getBuffer(context, indexInfo, attribute, startVertex, &nativeBuffer)); |
| *bufferOut = nativeBuffer->get(); |
| return angle::Result::Continue; |
| } |
| |
| angle::Result Buffer11::getConstantBufferRange(const gl::Context *context, |
| GLintptr offset, |
| GLsizeiptr size, |
| const d3d11::Buffer **bufferOut, |
| UINT *firstConstantOut, |
| UINT *numConstantsOut) |
| { |
| NativeStorage *bufferStorage = nullptr; |
| if ((offset == 0 && |
| size < static_cast<GLsizeiptr>(mRenderer->getNativeCaps().maxUniformBlockSize)) || |
| mRenderer->getRenderer11DeviceCaps().supportsConstantBufferOffsets) |
| { |
| ANGLE_TRY(getBufferStorage(context, BUFFER_USAGE_UNIFORM, &bufferStorage)); |
| CalculateConstantBufferParams(offset, size, firstConstantOut, numConstantsOut); |
| } |
| else |
| { |
| ANGLE_TRY(getConstantBufferRangeStorage(context, offset, size, &bufferStorage)); |
| *firstConstantOut = 0; |
| *numConstantsOut = 0; |
| } |
| |
| *bufferOut = &bufferStorage->getBuffer(); |
| return angle::Result::Continue; |
| } |
| |
| angle::Result Buffer11::markRawBufferUsage(const gl::Context *context) |
| { |
| ANGLE_TRY(markBufferUsage(context, BUFFER_USAGE_RAW_UAV)); |
| return angle::Result::Continue; |
| } |
| |
| angle::Result Buffer11::getRawUAVRange(const gl::Context *context, |
| GLintptr offset, |
| GLsizeiptr size, |
| d3d11::UnorderedAccessView **uavOut) |
| { |
| NativeStorage *nativeStorage = nullptr; |
| ANGLE_TRY(getBufferStorage(context, BUFFER_USAGE_RAW_UAV, &nativeStorage)); |
| |
| return nativeStorage->getRawUAV(context, static_cast<unsigned int>(offset), |
| static_cast<unsigned int>(size), uavOut); |
| } |
| |
| angle::Result Buffer11::getSRV(const gl::Context *context, |
| DXGI_FORMAT srvFormat, |
| const d3d11::ShaderResourceView **srvOut) |
| { |
| NativeStorage *nativeStorage = nullptr; |
| ANGLE_TRY(getBufferStorage(context, BUFFER_USAGE_PIXEL_UNPACK, &nativeStorage)); |
| return nativeStorage->getSRVForFormat(context, srvFormat, srvOut); |
| } |
| |
| angle::Result Buffer11::packPixels(const gl::Context *context, |
| const gl::FramebufferAttachment &readAttachment, |
| const PackPixelsParams ¶ms) |
| { |
| PackStorage *packStorage = nullptr; |
| ANGLE_TRY(getBufferStorage(context, BUFFER_USAGE_PIXEL_PACK, &packStorage)); |
| |
| ASSERT(packStorage); |
| ANGLE_TRY(packStorage->packPixels(context, readAttachment, params)); |
| onStorageUpdate(packStorage); |
| |
| return angle::Result::Continue; |
| } |
| |
| size_t Buffer11::getTotalCPUBufferMemoryBytes() const |
| { |
| size_t allocationSize = 0; |
| |
| BufferStorage *staging = mBufferStorages[BUFFER_USAGE_STAGING]; |
| allocationSize += staging ? staging->getSize() : 0; |
| |
| BufferStorage *sysMem = mBufferStorages[BUFFER_USAGE_SYSTEM_MEMORY]; |
| allocationSize += sysMem ? sysMem->getSize() : 0; |
| |
| return allocationSize; |
| } |
| |
| template <typename StorageOutT> |
| angle::Result Buffer11::getBufferStorage(const gl::Context *context, |
| BufferUsage usage, |
| StorageOutT **storageOut) |
| { |
| ASSERT(0 <= usage && usage < BUFFER_USAGE_COUNT); |
| BufferStorage *&newStorage = mBufferStorages[usage]; |
| |
| if (!newStorage) |
| { |
| newStorage = allocateStorage(usage); |
| } |
| |
| markBufferUsage(usage); |
| |
| // resize buffer |
| if (newStorage->getSize() < mSize) |
| { |
| ANGLE_TRY(newStorage->resize(context, mSize, true)); |
| } |
| |
| ASSERT(newStorage); |
| |
| ANGLE_TRY(updateBufferStorage(context, newStorage, 0, mSize)); |
| ANGLE_TRY(garbageCollection(context, usage)); |
| |
| *storageOut = GetAs<StorageOutT>(newStorage); |
| return angle::Result::Continue; |
| } |
| |
| Buffer11::BufferStorage *Buffer11::allocateStorage(BufferUsage usage) |
| { |
| updateDeallocThreshold(usage); |
| switch (usage) |
| { |
| case BUFFER_USAGE_PIXEL_PACK: |
| return new PackStorage(mRenderer); |
| case BUFFER_USAGE_SYSTEM_MEMORY: |
| return new SystemMemoryStorage(mRenderer); |
| case BUFFER_USAGE_EMULATED_INDEXED_VERTEX: |
| return new EmulatedIndexedStorage(mRenderer); |
| case BUFFER_USAGE_INDEX: |
| case BUFFER_USAGE_VERTEX_OR_TRANSFORM_FEEDBACK: |
| return new NativeStorage(mRenderer, usage, this); |
| default: |
| return new NativeStorage(mRenderer, usage, nullptr); |
| } |
| } |
| |
| angle::Result Buffer11::getConstantBufferRangeStorage(const gl::Context *context, |
| GLintptr offset, |
| GLsizeiptr size, |
| Buffer11::NativeStorage **storageOut) |
| { |
| BufferStorage *newStorage; |
| |
| { |
| // Keep the cacheEntry in a limited scope because it may be invalidated later in the code if |
| // we need to reclaim some space. |
| ConstantBufferCacheEntry *cacheEntry = &mConstantBufferRangeStoragesCache[offset]; |
| |
| if (!cacheEntry->storage) |
| { |
| cacheEntry->storage = allocateStorage(BUFFER_USAGE_UNIFORM); |
| cacheEntry->lruCount = ++mMaxConstantBufferLruCount; |
| } |
| |
| cacheEntry->lruCount = ++mMaxConstantBufferLruCount; |
| newStorage = cacheEntry->storage; |
| } |
| |
| markBufferUsage(BUFFER_USAGE_UNIFORM); |
| |
| if (newStorage->getSize() < static_cast<size_t>(size)) |
| { |
| size_t maximumAllowedAdditionalSize = 2 * getSize(); |
| |
| size_t sizeDelta = size - newStorage->getSize(); |
| |
| while (mConstantBufferStorageAdditionalSize + sizeDelta > maximumAllowedAdditionalSize) |
| { |
| auto iter = std::min_element(std::begin(mConstantBufferRangeStoragesCache), |
| std::end(mConstantBufferRangeStoragesCache), |
| [](const ConstantBufferCache::value_type &a, |
| const ConstantBufferCache::value_type &b) { |
| return a.second.lruCount < b.second.lruCount; |
| }); |
| |
| ASSERT(iter->second.storage != newStorage); |
| ASSERT(mConstantBufferStorageAdditionalSize >= iter->second.storage->getSize()); |
| |
| mConstantBufferStorageAdditionalSize -= iter->second.storage->getSize(); |
| SafeDelete(iter->second.storage); |
| mConstantBufferRangeStoragesCache.erase(iter); |
| } |
| |
| ANGLE_TRY(newStorage->resize(context, size, false)); |
| mConstantBufferStorageAdditionalSize += sizeDelta; |
| |
| // We don't copy the old data when resizing the constant buffer because the data may be |
| // out-of-date therefore we reset the data revision and let updateBufferStorage() handle the |
| // copy. |
| newStorage->setDataRevision(0); |
| } |
| |
| ANGLE_TRY(updateBufferStorage(context, newStorage, offset, size)); |
| ANGLE_TRY(garbageCollection(context, BUFFER_USAGE_UNIFORM)); |
| *storageOut = GetAs<NativeStorage>(newStorage); |
| return angle::Result::Continue; |
| } |
| |
| angle::Result Buffer11::updateBufferStorage(const gl::Context *context, |
| BufferStorage *storage, |
| size_t sourceOffset, |
| size_t storageSize) |
| { |
| BufferStorage *latestBuffer = nullptr; |
| ANGLE_TRY(getLatestBufferStorage(context, &latestBuffer)); |
| |
| ASSERT(storage); |
| |
| if (!latestBuffer) |
| { |
| onStorageUpdate(storage); |
| return angle::Result::Continue; |
| } |
| |
| if (latestBuffer->getDataRevision() <= storage->getDataRevision()) |
| { |
| return angle::Result::Continue; |
| } |
| |
| // Copy through a staging buffer if we're copying from or to a non-staging, mappable |
| // buffer storage. This is because we can't map a GPU buffer, and copy CPU |
| // data directly. If we're already using a staging buffer we're fine. |
| if (latestBuffer->getUsage() != BUFFER_USAGE_STAGING && |
| storage->getUsage() != BUFFER_USAGE_STAGING && |
| (!latestBuffer->isCPUAccessible(GL_MAP_READ_BIT) || |
| !storage->isCPUAccessible(GL_MAP_WRITE_BIT))) |
| { |
| NativeStorage *stagingBuffer = nullptr; |
| ANGLE_TRY(getStagingStorage(context, &stagingBuffer)); |
| |
| CopyResult copyResult = CopyResult::NOT_RECREATED; |
| ANGLE_TRY(stagingBuffer->copyFromStorage(context, latestBuffer, 0, latestBuffer->getSize(), |
| 0, ©Result)); |
| onCopyStorage(stagingBuffer, latestBuffer); |
| |
| latestBuffer = stagingBuffer; |
| } |
| |
| CopyResult copyResult = CopyResult::NOT_RECREATED; |
| ANGLE_TRY( |
| storage->copyFromStorage(context, latestBuffer, sourceOffset, storageSize, 0, ©Result)); |
| // If the D3D buffer has been recreated, we should update our serial. |
| if (copyResult == CopyResult::RECREATED) |
| { |
| updateSerial(); |
| } |
| onCopyStorage(storage, latestBuffer); |
| return angle::Result::Continue; |
| } |
| |
| angle::Result Buffer11::getLatestBufferStorage(const gl::Context *context, |
| Buffer11::BufferStorage **storageOut) const |
| { |
| // resize buffer |
| if (mLatestBufferStorage && mLatestBufferStorage->getSize() < mSize) |
| { |
| ANGLE_TRY(mLatestBufferStorage->resize(context, mSize, true)); |
| } |
| |
| *storageOut = mLatestBufferStorage; |
| return angle::Result::Continue; |
| } |
| |
| template <typename StorageOutT> |
| angle::Result Buffer11::getStagingStorage(const gl::Context *context, StorageOutT **storageOut) |
| { |
| return getBufferStorage(context, BUFFER_USAGE_STAGING, storageOut); |
| } |
| |
| size_t Buffer11::getSize() const |
| { |
| return mSize; |
| } |
| |
| bool Buffer11::supportsDirectBinding() const |
| { |
| // Do not support direct buffers for dynamic data. The streaming buffer |
| // offers better performance for data which changes every frame. |
| return (mUsage == D3DBufferUsage::STATIC); |
| } |
| |
| void Buffer11::initializeStaticData(const gl::Context *context) |
| { |
| BufferD3D::initializeStaticData(context); |
| onStateChange(angle::SubjectMessage::SubjectChanged); |
| } |
| |
| void Buffer11::invalidateStaticData(const gl::Context *context) |
| { |
| BufferD3D::invalidateStaticData(context); |
| onStateChange(angle::SubjectMessage::SubjectChanged); |
| } |
| |
| void Buffer11::onCopyStorage(BufferStorage *dest, BufferStorage *source) |
| { |
| ASSERT(source && mLatestBufferStorage); |
| dest->setDataRevision(source->getDataRevision()); |
| |
| // Only update the latest buffer storage if our usage index is lower. See comment in header. |
| if (dest->getUsage() < mLatestBufferStorage->getUsage()) |
| { |
| mLatestBufferStorage = dest; |
| } |
| } |
| |
| void Buffer11::onStorageUpdate(BufferStorage *updatedStorage) |
| { |
| updatedStorage->setDataRevision(updatedStorage->getDataRevision() + 1); |
| mLatestBufferStorage = updatedStorage; |
| } |
| |
| // Buffer11::BufferStorage implementation |
| |
| Buffer11::BufferStorage::BufferStorage(Renderer11 *renderer, BufferUsage usage) |
| : mRenderer(renderer), mRevision(0), mUsage(usage), mBufferSize(0) |
| {} |
| |
| angle::Result Buffer11::BufferStorage::setData(const gl::Context *context, |
| const uint8_t *data, |
| size_t offset, |
| size_t size) |
| { |
| ASSERT(isCPUAccessible(GL_MAP_WRITE_BIT)); |
| |
| // Uniform storage can have a different internal size than the buffer size. Ensure we don't |
| // overflow. |
| size_t mapSize = std::min(size, mBufferSize - offset); |
| |
| uint8_t *writePointer = nullptr; |
| ANGLE_TRY(map(context, offset, mapSize, GL_MAP_WRITE_BIT, &writePointer)); |
| |
| memcpy(writePointer, data, mapSize); |
| |
| unmap(); |
| |
| return angle::Result::Continue; |
| } |
| |
| // Buffer11::NativeStorage implementation |
| |
| Buffer11::NativeStorage::NativeStorage(Renderer11 *renderer, |
| BufferUsage usage, |
| const angle::Subject *onStorageChanged) |
| : BufferStorage(renderer, usage), mBuffer(), mOnStorageChanged(onStorageChanged) |
| {} |
| |
| Buffer11::NativeStorage::~NativeStorage() |
| { |
| clearSRVs(); |
| clearUAVs(); |
| } |
| |
| bool Buffer11::NativeStorage::isCPUAccessible(GLbitfield access) const |
| { |
| if ((access & GL_MAP_READ_BIT) != 0) |
| { |
| // Read is more exclusive than write mappability. |
| return (mUsage == BUFFER_USAGE_STAGING); |
| } |
| ASSERT((access & GL_MAP_WRITE_BIT) != 0); |
| return (mUsage == BUFFER_USAGE_STAGING || mUsage == BUFFER_USAGE_UNIFORM); |
| } |
| |
| // Returns true if it recreates the direct buffer |
| angle::Result Buffer11::NativeStorage::copyFromStorage(const gl::Context *context, |
| BufferStorage *source, |
| size_t sourceOffset, |
| size_t size, |
| size_t destOffset, |
| CopyResult *resultOut) |
| { |
| size_t requiredSize = destOffset + size; |
| |
| // (Re)initialize D3D buffer if needed |
| bool preserveData = (destOffset > 0); |
| if (!mBuffer.valid() || mBufferSize < requiredSize) |
| { |
| ANGLE_TRY(resize(context, requiredSize, preserveData)); |
| *resultOut = CopyResult::RECREATED; |
| } |
| else |
| { |
| *resultOut = CopyResult::NOT_RECREATED; |
| } |
| |
| size_t clampedSize = size; |
| if (mUsage == BUFFER_USAGE_UNIFORM) |
| { |
| clampedSize = std::min(clampedSize, mBufferSize - destOffset); |
| } |
| |
| if (clampedSize == 0) |
| { |
| return angle::Result::Continue; |
| } |
| |
| if (source->getUsage() == BUFFER_USAGE_PIXEL_PACK || |
| source->getUsage() == BUFFER_USAGE_SYSTEM_MEMORY) |
| { |
| ASSERT(source->isCPUAccessible(GL_MAP_READ_BIT) && isCPUAccessible(GL_MAP_WRITE_BIT)); |
| |
| // Uniform buffers must be mapped with write/discard. |
| ASSERT(!(preserveData && mUsage == BUFFER_USAGE_UNIFORM)); |
| |
| uint8_t *sourcePointer = nullptr; |
| ANGLE_TRY(source->map(context, sourceOffset, clampedSize, GL_MAP_READ_BIT, &sourcePointer)); |
| |
| auto err = setData(context, sourcePointer, destOffset, clampedSize); |
| source->unmap(); |
| ANGLE_TRY(err); |
| } |
| else |
| { |
| D3D11_BOX srcBox; |
| srcBox.left = static_cast<unsigned int>(sourceOffset); |
| srcBox.right = static_cast<unsigned int>(sourceOffset + clampedSize); |
| srcBox.top = 0; |
| srcBox.bottom = 1; |
| srcBox.front = 0; |
| srcBox.back = 1; |
| |
| const d3d11::Buffer *sourceBuffer = &GetAs<NativeStorage>(source)->getBuffer(); |
| |
| ID3D11DeviceContext *deviceContext = mRenderer->getDeviceContext(); |
| deviceContext->CopySubresourceRegion(mBuffer.get(), 0, |
| static_cast<unsigned int>(destOffset), 0, 0, |
| sourceBuffer->get(), 0, &srcBox); |
| } |
| |
| return angle::Result::Continue; |
| } |
| |
| angle::Result Buffer11::NativeStorage::resize(const gl::Context *context, |
| size_t size, |
| bool preserveData) |
| { |
| if (size == 0) |
| { |
| mBuffer.reset(); |
| mBufferSize = 0; |
| return angle::Result::Continue; |
| } |
| |
| D3D11_BUFFER_DESC bufferDesc; |
| FillBufferDesc(&bufferDesc, mRenderer, mUsage, static_cast<unsigned int>(size)); |
| |
| d3d11::Buffer newBuffer; |
| ANGLE_TRY( |
| mRenderer->allocateResource(SafeGetImplAs<Context11>(context), bufferDesc, &newBuffer)); |
| newBuffer.setDebugName("Buffer11::NativeStorage"); |
| |
| if (mBuffer.valid() && preserveData) |
| { |
| // We don't call resize if the buffer is big enough already. |
| ASSERT(mBufferSize <= size); |
| |
| D3D11_BOX srcBox; |
| srcBox.left = 0; |
| srcBox.right = static_cast<unsigned int>(mBufferSize); |
| srcBox.top = 0; |
| srcBox.bottom = 1; |
| srcBox.front = 0; |
| srcBox.back = 1; |
| |
| ID3D11DeviceContext *deviceContext = mRenderer->getDeviceContext(); |
| deviceContext->CopySubresourceRegion(newBuffer.get(), 0, 0, 0, 0, mBuffer.get(), 0, |
| &srcBox); |
| } |
| |
| // No longer need the old buffer |
| mBuffer = std::move(newBuffer); |
| |
| mBufferSize = bufferDesc.ByteWidth; |
| |
| // Free the SRVs. |
| clearSRVs(); |
| |
| // Free the UAVs. |
| clearUAVs(); |
| |
| // Notify that the storage has changed. |
| if (mOnStorageChanged) |
| { |
| mOnStorageChanged->onStateChange(angle::SubjectMessage::SubjectChanged); |
| } |
| |
| return angle::Result::Continue; |
| } |
| |
| // static |
| void Buffer11::NativeStorage::FillBufferDesc(D3D11_BUFFER_DESC *bufferDesc, |
| Renderer11 *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->isES3Capable()) |
| { |
| 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_INDIRECT: |
| bufferDesc->MiscFlags = D3D11_RESOURCE_MISC_DRAWINDIRECT_ARGS; |
| bufferDesc->Usage = D3D11_USAGE_DEFAULT; |
| bufferDesc->BindFlags = 0; |
| 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); |
| |
| // Note: it seems that D3D11 allows larger buffers on some platforms, but not all. |
| // (Windows 10 seems to allow larger constant buffers, but not Windows 7) |
| if (!renderer->getRenderer11DeviceCaps().supportsConstantBufferOffsets) |
| { |
| bufferDesc->ByteWidth = std::min<UINT>( |
| bufferDesc->ByteWidth, |
| static_cast<UINT>(renderer->getNativeCaps().maxUniformBlockSize)); |
| } |
| break; |
| |
| case BUFFER_USAGE_RAW_UAV: |
| bufferDesc->MiscFlags = D3D11_RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS; |
| bufferDesc->BindFlags = D3D11_BIND_UNORDERED_ACCESS; |
| bufferDesc->Usage = D3D11_USAGE_DEFAULT; |
| bufferDesc->CPUAccessFlags = 0; |
| break; |
| |
| default: |
| UNREACHABLE(); |
| } |
| } |
| |
| angle::Result Buffer11::NativeStorage::map(const gl::Context *context, |
| size_t offset, |
| size_t length, |
| GLbitfield access, |
| uint8_t **mapPointerOut) |
| { |
| ASSERT(isCPUAccessible(access)); |
| |
| D3D11_MAPPED_SUBRESOURCE mappedResource; |
| D3D11_MAP d3dMapType = gl_d3d11::GetD3DMapTypeFromBits(mUsage, access); |
| UINT d3dMapFlag = ((access & GL_MAP_UNSYNCHRONIZED_BIT) != 0 ? D3D11_MAP_FLAG_DO_NOT_WAIT : 0); |
| |
| ANGLE_TRY( |
| mRenderer->mapResource(context, mBuffer.get(), 0, d3dMapType, d3dMapFlag, &mappedResource)); |
| ASSERT(mappedResource.pData); |
| *mapPointerOut = static_cast<uint8_t *>(mappedResource.pData) + offset; |
| return angle::Result::Continue; |
| } |
| |
| void Buffer11::NativeStorage::unmap() |
| { |
| ASSERT(isCPUAccessible(GL_MAP_WRITE_BIT) || isCPUAccessible(GL_MAP_READ_BIT)); |
| ID3D11DeviceContext *context = mRenderer->getDeviceContext(); |
| context->Unmap(mBuffer.get(), 0); |
| } |
| |
| angle::Result Buffer11::NativeStorage::getSRVForFormat(const gl::Context *context, |
| DXGI_FORMAT srvFormat, |
| const d3d11::ShaderResourceView **srvOut) |
| { |
| auto bufferSRVIt = mBufferResourceViews.find(srvFormat); |
| |
| if (bufferSRVIt != mBufferResourceViews.end()) |
| { |
| *srvOut = &bufferSRVIt->second; |
| return angle::Result::Continue; |
| } |
| |
| const d3d11::DXGIFormatSize &dxgiFormatInfo = d3d11::GetDXGIFormatSizeInfo(srvFormat); |
| |
| D3D11_SHADER_RESOURCE_VIEW_DESC bufferSRVDesc; |
| bufferSRVDesc.Buffer.ElementOffset = 0; |
| bufferSRVDesc.Buffer.ElementWidth = static_cast<UINT>(mBufferSize) / dxgiFormatInfo.pixelBytes; |
| bufferSRVDesc.ViewDimension = D3D11_SRV_DIMENSION_BUFFER; |
| bufferSRVDesc.Format = srvFormat; |
| |
| ANGLE_TRY(mRenderer->allocateResource(GetImplAs<Context11>(context), bufferSRVDesc, |
| mBuffer.get(), &mBufferResourceViews[srvFormat])); |
| |
| *srvOut = &mBufferResourceViews[srvFormat]; |
| return angle::Result::Continue; |
| } |
| |
| angle::Result Buffer11::NativeStorage::getRawUAV(const gl::Context *context, |
| unsigned int offset, |
| unsigned int size, |
| d3d11::UnorderedAccessView **uavOut) |
| { |
| ASSERT(offset + size <= mBufferSize); |
| |
| auto bufferRawUAV = mBufferRawUAVs.find({offset, size}); |
| if (bufferRawUAV != mBufferRawUAVs.end()) |
| { |
| *uavOut = &bufferRawUAV->second; |
| return angle::Result::Continue; |
| } |
| |
| D3D11_UNORDERED_ACCESS_VIEW_DESC bufferUAVDesc; |
| |
| // DXGI_FORMAT_R32_TYPELESS uses 4 bytes per element |
| constexpr int kBytesToElement = 4; |
| bufferUAVDesc.Buffer.FirstElement = offset / kBytesToElement; |
| bufferUAVDesc.Buffer.NumElements = size / kBytesToElement; |
| bufferUAVDesc.Buffer.Flags = D3D11_BUFFER_UAV_FLAG_RAW; |
| bufferUAVDesc.Format = DXGI_FORMAT_R32_TYPELESS; // Format must be DXGI_FORMAT_R32_TYPELESS, |
| // when creating Raw Unordered Access View |
| bufferUAVDesc.ViewDimension = D3D11_UAV_DIMENSION_BUFFER; |
| |
| ANGLE_TRY(mRenderer->allocateResource(GetImplAs<Context11>(context), bufferUAVDesc, |
| mBuffer.get(), &mBufferRawUAVs[{offset, size}])); |
| *uavOut = &mBufferRawUAVs[{offset, size}]; |
| return angle::Result::Continue; |
| } |
| |
| void Buffer11::NativeStorage::clearSRVs() |
| { |
| mBufferResourceViews.clear(); |
| } |
| |
| void Buffer11::NativeStorage::clearUAVs() |
| { |
| mBufferRawUAVs.clear(); |
| } |
| |
| // Buffer11::EmulatedIndexStorage implementation |
| |
| Buffer11::EmulatedIndexedStorage::EmulatedIndexedStorage(Renderer11 *renderer) |
| : BufferStorage(renderer, BUFFER_USAGE_EMULATED_INDEXED_VERTEX), mBuffer() |
| {} |
| |
| Buffer11::EmulatedIndexedStorage::~EmulatedIndexedStorage() {} |
| |
| angle::Result Buffer11::EmulatedIndexedStorage::getBuffer(const gl::Context *context, |
| SourceIndexData *indexInfo, |
| const TranslatedAttribute &attribute, |
| GLint startVertex, |
| const d3d11::Buffer **bufferOut) |
| { |
| Context11 *context11 = GetImplAs<Context11>(context); |
| |
| // If a change in the indices applied from the last draw call is detected, then the emulated |
| // indexed buffer needs to be invalidated. After invalidation, the change detected flag should |
| // be cleared to avoid unnecessary recreation of the buffer. |
| if (!mBuffer.valid() || indexInfo->srcIndicesChanged) |
| { |
| mBuffer.reset(); |
| |
| // Copy the source index data. This ensures that the lifetime of the indices pointer |
| // stays with this storage until the next time we invalidate. |
| size_t indicesDataSize = 0; |
| switch (indexInfo->srcIndexType) |
| { |
| case gl::DrawElementsType::UnsignedInt: |
| indicesDataSize = sizeof(GLuint) * indexInfo->srcCount; |
| break; |
| case gl::DrawElementsType::UnsignedShort: |
| indicesDataSize = sizeof(GLushort) * indexInfo->srcCount; |
| break; |
| case gl::DrawElementsType::UnsignedByte: |
| indicesDataSize = sizeof(GLubyte) * indexInfo->srcCount; |
| break; |
| default: |
| indicesDataSize = sizeof(GLushort) * indexInfo->srcCount; |
| break; |
| } |
| |
| ANGLE_CHECK_GL_ALLOC(context11, mIndicesMemoryBuffer.resize(indicesDataSize)); |
| |
| memcpy(mIndicesMemoryBuffer.data(), indexInfo->srcIndices, indicesDataSize); |
| |
| indexInfo->srcIndicesChanged = false; |
| } |
| |
| if (!mBuffer.valid()) |
| { |
| unsigned int offset = 0; |
| ANGLE_TRY(attribute.computeOffset(context, startVertex, &offset)); |
| |
| // Expand the memory storage upon request and cache the results. |
| unsigned int expandedDataSize = |
| static_cast<unsigned int>((indexInfo->srcCount * attribute.stride) + offset); |
| angle::MemoryBuffer expandedData; |
| ANGLE_CHECK_GL_ALLOC(context11, expandedData.resize(expandedDataSize)); |
| |
| // Clear the contents of the allocated buffer |
| ZeroMemory(expandedData.data(), expandedDataSize); |
| |
| uint8_t *curr = expandedData.data(); |
| const uint8_t *ptr = static_cast<const uint8_t *>(indexInfo->srcIndices); |
| |
| // Ensure that we start in the correct place for the emulated data copy operation to |
| // maintain offset behaviors. |
| curr += offset; |
| |
| ReadIndexValueFunction readIndexValue = ReadIndexValueFromIndices<GLushort>; |
| |
| switch (indexInfo->srcIndexType) |
| { |
| case gl::DrawElementsType::UnsignedInt: |
| readIndexValue = ReadIndexValueFromIndices<GLuint>; |
| break; |
| case gl::DrawElementsType::UnsignedShort: |
| readIndexValue = ReadIndexValueFromIndices<GLushort>; |
| break; |
| case gl::DrawElementsType::UnsignedByte: |
| readIndexValue = ReadIndexValueFromIndices<GLubyte>; |
| break; |
| default: |
| UNREACHABLE(); |
| return angle::Result::Stop; |
| } |
| |
| // Iterate over the cached index data and copy entries indicated into the emulated buffer. |
| for (GLuint i = 0; i < indexInfo->srcCount; i++) |
| { |
| GLuint idx = readIndexValue(ptr, i); |
| memcpy(curr, mMemoryBuffer.data() + (attribute.stride * idx), attribute.stride); |
| curr += attribute.stride; |
| } |
| |
| // Finally, initialize the emulated indexed native storage object with the newly copied data |
| // and free the temporary buffers used. |
| D3D11_BUFFER_DESC bufferDesc; |
| bufferDesc.ByteWidth = expandedDataSize; |
| bufferDesc.MiscFlags = 0; |
| bufferDesc.StructureByteStride = 0; |
| bufferDesc.Usage = D3D11_USAGE_DEFAULT; |
| bufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER; |
| bufferDesc.CPUAccessFlags = 0; |
| |
| D3D11_SUBRESOURCE_DATA subResourceData = {expandedData.data(), 0, 0}; |
| |
| ANGLE_TRY(mRenderer->allocateResource(GetImplAs<Context11>(context), bufferDesc, |
| &subResourceData, &mBuffer)); |
| mBuffer.setDebugName("Buffer11::EmulatedIndexedStorage"); |
| } |
| |
| *bufferOut = &mBuffer; |
| return angle::Result::Continue; |
| } |
| |
| angle::Result Buffer11::EmulatedIndexedStorage::copyFromStorage(const gl::Context *context, |
| BufferStorage *source, |
| size_t sourceOffset, |
| size_t size, |
| size_t destOffset, |
| CopyResult *resultOut) |
| { |
| ASSERT(source->isCPUAccessible(GL_MAP_READ_BIT)); |
| uint8_t *sourceData = nullptr; |
| ANGLE_TRY(source->map(context, sourceOffset, size, GL_MAP_READ_BIT, &sourceData)); |
| ASSERT(destOffset + size <= mMemoryBuffer.size()); |
| memcpy(mMemoryBuffer.data() + destOffset, sourceData, size); |
| source->unmap(); |
| *resultOut = CopyResult::RECREATED; |
| return angle::Result::Continue; |
| } |
| |
| angle::Result Buffer11::EmulatedIndexedStorage::resize(const gl::Context *context, |
| size_t size, |
| bool preserveData) |
| { |
| if (mMemoryBuffer.size() < size) |
| { |
| Context11 *context11 = GetImplAs<Context11>(context); |
| ANGLE_CHECK_GL_ALLOC(context11, mMemoryBuffer.resize(size)); |
| mBufferSize = size; |
| } |
| |
| return angle::Result::Continue; |
| } |
| |
| angle::Result Buffer11::EmulatedIndexedStorage::map(const gl::Context *context, |
| size_t offset, |
| size_t length, |
| GLbitfield access, |
| uint8_t **mapPointerOut) |
| { |
| ASSERT(!mMemoryBuffer.empty() && offset + length <= mMemoryBuffer.size()); |
| *mapPointerOut = mMemoryBuffer.data() + offset; |
| return angle::Result::Continue; |
| } |
| |
| void Buffer11::EmulatedIndexedStorage::unmap() |
| { |
| // No-op |
| } |
| |
| // Buffer11::PackStorage implementation |
| |
| Buffer11::PackStorage::PackStorage(Renderer11 *renderer) |
| : BufferStorage(renderer, BUFFER_USAGE_PIXEL_PACK), mStagingTexture(), mDataModified(false) |
| {} |
| |
| Buffer11::PackStorage::~PackStorage() {} |
| |
| angle::Result Buffer11::PackStorage::copyFromStorage(const gl::Context *context, |
| BufferStorage *source, |
| size_t sourceOffset, |
| size_t size, |
| size_t destOffset, |
| CopyResult *resultOut) |
| { |
| ANGLE_TRY(flushQueuedPackCommand(context)); |
| |
| // For all use cases of pack buffers, we must copy through a readable buffer. |
| ASSERT(source->isCPUAccessible(GL_MAP_READ_BIT)); |
| uint8_t *sourceData = nullptr; |
| ANGLE_TRY(source->map(context, sourceOffset, size, GL_MAP_READ_BIT, &sourceData)); |
| ASSERT(destOffset + size <= mMemoryBuffer.size()); |
| memcpy(mMemoryBuffer.data() + destOffset, sourceData, size); |
| source->unmap(); |
| *resultOut = CopyResult::NOT_RECREATED; |
| return angle::Result::Continue; |
| } |
| |
| angle::Result Buffer11::PackStorage::resize(const gl::Context *context, |
| size_t size, |
| bool preserveData) |
| { |
| if (size != mBufferSize) |
| { |
| Context11 *context11 = GetImplAs<Context11>(context); |
| ANGLE_CHECK_GL_ALLOC(context11, mMemoryBuffer.resize(size)); |
| mBufferSize = size; |
| } |
| |
| return angle::Result::Continue; |
| } |
| |
| angle::Result Buffer11::PackStorage::map(const gl::Context *context, |
| size_t offset, |
| size_t length, |
| GLbitfield access, |
| uint8_t **mapPointerOut) |
| { |
| 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. |
| |
| ANGLE_TRY(flushQueuedPackCommand(context)); |
| |
| mDataModified = (mDataModified || (access & GL_MAP_WRITE_BIT) != 0); |
| |
| *mapPointerOut = mMemoryBuffer.data() + offset; |
| return angle::Result::Continue; |
| } |
| |
| void Buffer11::PackStorage::unmap() |
| { |
| // No-op |
| } |
| |
| angle::Result Buffer11::PackStorage::packPixels(const gl::Context *context, |
| const gl::FramebufferAttachment &readAttachment, |
| const PackPixelsParams ¶ms) |
| { |
| ANGLE_TRY(flushQueuedPackCommand(context)); |
| |
| RenderTarget11 *renderTarget = nullptr; |
| ANGLE_TRY(readAttachment.getRenderTarget(context, 0, &renderTarget)); |
| |
| const TextureHelper11 &srcTexture = renderTarget->getTexture(); |
| ASSERT(srcTexture.valid()); |
| unsigned int srcSubresource = renderTarget->getSubresourceIndex(); |
| |
| mQueuedPackCommand.reset(new PackPixelsParams(params)); |
| |
| gl::Extents srcTextureSize(params.area.width, params.area.height, 1); |
| if (!mStagingTexture.get() || mStagingTexture.getFormat() != srcTexture.getFormat() || |
| mStagingTexture.getExtents() != srcTextureSize) |
| { |
| ANGLE_TRY(mRenderer->createStagingTexture(context, srcTexture.getTextureType(), |
| srcTexture.getFormatSet(), srcTextureSize, |
| StagingAccess::READ, &mStagingTexture)); |
| } |
| |
| // ReadPixels from multisampled FBOs isn't supported in current GL |
| ASSERT(srcTexture.getSampleCount() <= 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; |
| |
| // Select the correct layer from a 3D attachment |
| srcBox.front = 0; |
| if (mStagingTexture.is3D()) |
| { |
| srcBox.front = static_cast<UINT>(readAttachment.layer()); |
| } |
| srcBox.back = srcBox.front + 1; |
| |
| // Asynchronous copy |
| immediateContext->CopySubresourceRegion(mStagingTexture.get(), 0, 0, 0, 0, srcTexture.get(), |
| srcSubresource, &srcBox); |
| |
| return angle::Result::Continue; |
| } |
| |
| angle::Result Buffer11::PackStorage::flushQueuedPackCommand(const gl::Context *context) |
| { |
| ASSERT(mMemoryBuffer.size() > 0); |
| |
| if (mQueuedPackCommand) |
| { |
| ANGLE_TRY(mRenderer->packPixels(context, mStagingTexture, *mQueuedPackCommand, |
| mMemoryBuffer.data())); |
| mQueuedPackCommand.reset(nullptr); |
| } |
| |
| return angle::Result::Continue; |
| } |
| |
| // Buffer11::SystemMemoryStorage implementation |
| |
| Buffer11::SystemMemoryStorage::SystemMemoryStorage(Renderer11 *renderer) |
| : Buffer11::BufferStorage(renderer, BUFFER_USAGE_SYSTEM_MEMORY) |
| {} |
| |
| angle::Result Buffer11::SystemMemoryStorage::copyFromStorage(const gl::Context *context, |
| BufferStorage *source, |
| size_t sourceOffset, |
| size_t size, |
| size_t destOffset, |
| CopyResult *resultOut) |
| { |
| ASSERT(source->isCPUAccessible(GL_MAP_READ_BIT)); |
| uint8_t *sourceData = nullptr; |
| ANGLE_TRY(source->map(context, sourceOffset, size, GL_MAP_READ_BIT, &sourceData)); |
| ASSERT(destOffset + size <= mSystemCopy.size()); |
| memcpy(mSystemCopy.data() + destOffset, sourceData, size); |
| source->unmap(); |
| *resultOut = CopyResult::RECREATED; |
| return angle::Result::Continue; |
| } |
| |
| angle::Result Buffer11::SystemMemoryStorage::resize(const gl::Context *context, |
| size_t size, |
| bool preserveData) |
| { |
| if (mSystemCopy.size() < size) |
| { |
| Context11 *context11 = GetImplAs<Context11>(context); |
| ANGLE_CHECK_GL_ALLOC(context11, mSystemCopy.resize(size)); |
| mBufferSize = size; |
| } |
| |
| return angle::Result::Continue; |
| } |
| |
| angle::Result Buffer11::SystemMemoryStorage::map(const gl::Context *context, |
| size_t offset, |
| size_t length, |
| GLbitfield access, |
| uint8_t **mapPointerOut) |
| { |
| ASSERT(!mSystemCopy.empty() && offset + length <= mSystemCopy.size()); |
| *mapPointerOut = mSystemCopy.data() + offset; |
| return angle::Result::Continue; |
| } |
| |
| void Buffer11::SystemMemoryStorage::unmap() |
| { |
| // No-op |
| } |
| } // namespace rx |