| // |
| // Copyright 2022 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. |
| // |
| // Suballocation.cpp: |
| // Implements class methods for BufferBlock and Suballocation and other related classes |
| // |
| |
| // #include "libANGLE/renderer/vulkan/vk_utils.h" |
| |
| #include "libANGLE/renderer/vulkan/Suballocation.h" |
| #include "libANGLE/Context.h" |
| #include "libANGLE/renderer/vulkan/vk_mem_alloc_wrapper.h" |
| #include "libANGLE/renderer/vulkan/vk_renderer.h" |
| |
| namespace rx |
| { |
| namespace vk |
| { |
| // BufferBlock implementation. |
| BufferBlock::BufferBlock() |
| : mMemoryPropertyFlags(0), |
| mSize(0), |
| mAllocatedBufferSize(0), |
| mMemoryAllocationType(MemoryAllocationType::InvalidEnum), |
| mMemoryTypeIndex(kInvalidMemoryTypeIndex), |
| mMappedMemory(nullptr) |
| {} |
| |
| BufferBlock::BufferBlock(BufferBlock &&other) |
| : mVirtualBlock(std::move(other.mVirtualBlock)), |
| mBuffer(std::move(other.mBuffer)), |
| mDeviceMemory(std::move(other.mDeviceMemory)), |
| mMemoryPropertyFlags(other.mMemoryPropertyFlags), |
| mSize(other.mSize), |
| mAllocatedBufferSize(other.mAllocatedBufferSize), |
| mMemoryAllocationType(other.mMemoryAllocationType), |
| mMemoryTypeIndex(other.mMemoryTypeIndex), |
| mMappedMemory(other.mMappedMemory), |
| mSerial(other.mSerial), |
| mCountRemainsEmpty(0) |
| {} |
| |
| BufferBlock &BufferBlock::operator=(BufferBlock &&other) |
| { |
| std::swap(mVirtualBlock, other.mVirtualBlock); |
| std::swap(mBuffer, other.mBuffer); |
| std::swap(mDeviceMemory, other.mDeviceMemory); |
| std::swap(mMemoryPropertyFlags, other.mMemoryPropertyFlags); |
| std::swap(mSize, other.mSize); |
| std::swap(mAllocatedBufferSize, other.mAllocatedBufferSize); |
| std::swap(mMemoryAllocationType, other.mMemoryAllocationType); |
| std::swap(mMemoryTypeIndex, other.mMemoryTypeIndex); |
| std::swap(mMappedMemory, other.mMappedMemory); |
| std::swap(mSerial, other.mSerial); |
| std::swap(mCountRemainsEmpty, other.mCountRemainsEmpty); |
| return *this; |
| } |
| |
| BufferBlock::~BufferBlock() |
| { |
| ASSERT(!mVirtualBlock.valid()); |
| ASSERT(!mBuffer.valid()); |
| ASSERT(!mDeviceMemory.valid()); |
| } |
| |
| void BufferBlock::destroy(Renderer *renderer) |
| { |
| VkDevice device = renderer->getDevice(); |
| |
| if (mMappedMemory) |
| { |
| unmap(device); |
| } |
| |
| renderer->onMemoryDealloc(mMemoryAllocationType, mAllocatedBufferSize, mMemoryTypeIndex, |
| mDeviceMemory.getHandle()); |
| |
| mVirtualBlock.destroy(device); |
| mBuffer.destroy(device); |
| mDeviceMemory.destroy(device); |
| } |
| |
| VkResult BufferBlock::init(ErrorContext *context, |
| Buffer &buffer, |
| uint32_t memoryTypeIndex, |
| vma::VirtualBlockCreateFlags flags, |
| DeviceMemory &deviceMemory, |
| VkMemoryPropertyFlags memoryPropertyFlags, |
| VkDeviceSize size) |
| { |
| Renderer *renderer = context->getRenderer(); |
| ASSERT(!mVirtualBlock.valid()); |
| ASSERT(!mBuffer.valid()); |
| ASSERT(!mDeviceMemory.valid()); |
| |
| VK_RESULT_TRY(mVirtualBlock.init(renderer->getDevice(), flags, size)); |
| |
| mBuffer = std::move(buffer); |
| mDeviceMemory = std::move(deviceMemory); |
| mMemoryPropertyFlags = memoryPropertyFlags; |
| mSize = size; |
| mAllocatedBufferSize = size; |
| mMemoryAllocationType = MemoryAllocationType::Buffer; |
| mMemoryTypeIndex = memoryTypeIndex; |
| mMappedMemory = nullptr; |
| mSerial = renderer->getResourceSerialFactory().generateBufferSerial(); |
| |
| return VK_SUCCESS; |
| } |
| |
| void BufferBlock::initWithoutVirtualBlock(ErrorContext *context, |
| Buffer &buffer, |
| MemoryAllocationType memoryAllocationType, |
| uint32_t memoryTypeIndex, |
| DeviceMemory &deviceMemory, |
| VkMemoryPropertyFlags memoryPropertyFlags, |
| VkDeviceSize size, |
| VkDeviceSize allocatedBufferSize) |
| { |
| Renderer *renderer = context->getRenderer(); |
| ASSERT(!mVirtualBlock.valid()); |
| ASSERT(!mBuffer.valid()); |
| ASSERT(!mDeviceMemory.valid()); |
| |
| mBuffer = std::move(buffer); |
| mDeviceMemory = std::move(deviceMemory); |
| mMemoryPropertyFlags = memoryPropertyFlags; |
| mSize = size; |
| mAllocatedBufferSize = allocatedBufferSize; |
| mMemoryAllocationType = memoryAllocationType; |
| mMemoryTypeIndex = memoryTypeIndex; |
| mMappedMemory = nullptr; |
| mSerial = renderer->getResourceSerialFactory().generateBufferSerial(); |
| } |
| |
| VkResult BufferBlock::map(const VkDevice device) |
| { |
| ASSERT(mMappedMemory == nullptr); |
| return mDeviceMemory.map(device, 0, mAllocatedBufferSize, 0, &mMappedMemory); |
| } |
| |
| void BufferBlock::unmap(const VkDevice device) |
| { |
| mDeviceMemory.unmap(device); |
| mMappedMemory = nullptr; |
| } |
| |
| VkResult BufferBlock::allocate(VkDeviceSize size, |
| VkDeviceSize alignment, |
| VmaVirtualAllocation *allocationOut, |
| VkDeviceSize *offsetOut) |
| { |
| std::unique_lock<angle::SimpleMutex> lock(mVirtualBlockMutex); |
| mCountRemainsEmpty = 0; |
| return mVirtualBlock.allocate(size, alignment, allocationOut, offsetOut); |
| } |
| |
| void BufferBlock::free(VmaVirtualAllocation allocation, VkDeviceSize offset) |
| { |
| std::unique_lock<angle::SimpleMutex> lock(mVirtualBlockMutex); |
| mVirtualBlock.free(allocation, offset); |
| } |
| |
| int32_t BufferBlock::getAndIncrementEmptyCounter() |
| { |
| return ++mCountRemainsEmpty; |
| } |
| |
| void BufferBlock::calculateStats(vma::StatInfo *pStatInfo) const |
| { |
| std::unique_lock<angle::SimpleMutex> lock(mVirtualBlockMutex); |
| mVirtualBlock.calculateStats(pStatInfo); |
| } |
| |
| // BufferSuballocation implementation. |
| VkResult BufferSuballocation::map(ErrorContext *context) |
| { |
| return mBufferBlock->map(context->getDevice()); |
| } |
| |
| void BufferSuballocation::flush(Renderer *renderer) |
| { |
| if (!isCoherent()) |
| { |
| const VkDeviceSize nonCoherentAtomSize = |
| renderer->getPhysicalDeviceProperties().limits.nonCoherentAtomSize; |
| |
| VkMappedMemoryRange mappedRange = {}; |
| mappedRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; |
| mappedRange.memory = mBufferBlock->getDeviceMemory().getHandle(); |
| mappedRange.offset = getOffset(); |
| mappedRange.size = roundUp<VkDeviceSize>(mSize, nonCoherentAtomSize); |
| |
| ASSERT(mappedRange.size <= mBufferBlock->getAllocatedBufferSize()); |
| mBufferBlock->getDeviceMemory().flush(renderer->getDevice(), mappedRange); |
| } |
| } |
| |
| void BufferSuballocation::invalidate(Renderer *renderer) |
| { |
| if (!isCoherent()) |
| { |
| const VkDeviceSize nonCoherentAtomSize = |
| renderer->getPhysicalDeviceProperties().limits.nonCoherentAtomSize; |
| |
| VkMappedMemoryRange mappedRange = {}; |
| mappedRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; |
| mappedRange.memory = mBufferBlock->getDeviceMemory().getHandle(); |
| mappedRange.offset = getOffset(); |
| mappedRange.size = roundUp<VkDeviceSize>(mSize, nonCoherentAtomSize); |
| |
| ASSERT(mappedRange.size <= mBufferBlock->getAllocatedBufferSize()); |
| mBufferBlock->getDeviceMemory().invalidate(renderer->getDevice(), mappedRange); |
| } |
| } |
| |
| // BufferSuballocationGarbage implementation. |
| bool BufferSuballocationGarbage::destroyIfComplete(Renderer *renderer) |
| { |
| if (renderer->hasResourceUseFinished(mLifetime)) |
| { |
| mBuffer.destroy(renderer->getDevice()); |
| mSuballocation.destroy(renderer); |
| return true; |
| } |
| return false; |
| } |
| |
| bool BufferSuballocationGarbage::hasResourceUseSubmitted(Renderer *renderer) const |
| { |
| return renderer->hasResourceUseSubmitted(mLifetime); |
| } |
| } // namespace vk |
| } // namespace rx |