DeviceMemoryVk.h#
The Device Memory class, a class representing a memory block managed by Vulkan.
Includes#
PVRVk/DeviceVk.h
Included By#
Namespaces#
Classes#
Source Code#
#pragma once
#include "PVRVk/DeviceVk.h"
namespace pvrvk {
struct ExportMemoryAllocateInfoKHR
{
ExternalMemoryHandleTypeFlags handleTypes;
ExportMemoryAllocateInfoKHR() : handleTypes(ExternalMemoryHandleTypeFlags::e_NONE) {}
ExportMemoryAllocateInfoKHR(ExternalMemoryHandleTypeFlags handleTypes) : handleTypes(handleTypes) {}
};
struct MemoryAllocationInfo
{
public:
MemoryAllocationInfo() : _allocationSize(0), _memoryTypeIndex(uint32_t(-1)) {}
MemoryAllocationInfo(DeviceSize allocationSize, uint32_t memoryTypeIndex) : _allocationSize(allocationSize), _memoryTypeIndex(memoryTypeIndex) {}
DeviceSize getAllocationSize() const { return _allocationSize; }
void setAllocationSize(DeviceSize sizeInBytes) { _allocationSize = sizeInBytes; }
uint32_t getMemoryTypeIndex() const { return _memoryTypeIndex; }
void setMemoryTypeIndex(uint32_t memoryTypeIndex) { this->_memoryTypeIndex = memoryTypeIndex; }
void setExportMemoryAllocationInfoKHR(const ExportMemoryAllocateInfoKHR& info) { _exportMemoryAllocateInfoKHR = info; }
const ExportMemoryAllocateInfoKHR& getExportMemoryAllocateInfoKHR() const { return _exportMemoryAllocateInfoKHR; }
ExportMemoryAllocateInfoKHR& getExportMemoryAllocateInfoKHR() { return _exportMemoryAllocateInfoKHR; }
private:
DeviceSize _allocationSize;
uint32_t _memoryTypeIndex;
ExportMemoryAllocateInfoKHR _exportMemoryAllocateInfoKHR;
};
namespace impl {
class IDeviceMemory_ : public PVRVkDeviceObjectBase<VkDeviceMemory, ObjectType::e_DEVICE_MEMORY>
{
public:
IDeviceMemory_() : PVRVkDeviceObjectBase() {}
explicit IDeviceMemory_(DeviceWeakPtr device) : PVRVkDeviceObjectBase(device) {}
IDeviceMemory_(const DeviceWeakPtr& device, VkDeviceMemory memory) : PVRVkDeviceObjectBase(device, memory) {}
virtual ~IDeviceMemory_() {}
virtual bool isMappable() const = 0;
virtual uint32_t getMemoryType() const = 0;
virtual pvrvk::MemoryPropertyFlags getMemoryFlags() const = 0;
bool hasPropertyFlag(pvrvk::MemoryPropertyFlags flags) const { return (getMemoryFlags() & flags) == flags; }
virtual VkDeviceSize getMappedOffset() const = 0;
virtual void* getMappedData() = 0;
virtual VkDeviceSize getMappedSize() const = 0;
virtual VkDeviceSize getSize() const = 0;
virtual bool isMapped() const = 0;
virtual void* map(VkDeviceSize offset = 0, VkDeviceSize size = VK_WHOLE_SIZE, pvrvk::MemoryMapFlags memoryMapFlags = pvrvk::MemoryMapFlags::e_NONE) = 0;
virtual void unmap() = 0;
virtual void flushRange(VkDeviceSize offset = 0, VkDeviceSize size = VK_WHOLE_SIZE) = 0;
virtual void invalidateRange(VkDeviceSize offset = 0, VkDeviceSize size = VK_WHOLE_SIZE) = 0;
};
class DeviceMemory_ : public IDeviceMemory_, public DeviceObjectDebugUtils<DeviceMemory_>
{
protected:
friend class Device_;
class make_shared_enabler
{
protected:
make_shared_enabler() {}
friend class DeviceMemory_;
};
static DeviceMemory constructShared(const DeviceWeakPtr& device, const MemoryAllocationInfo& allocationInfo, pvrvk::MemoryPropertyFlags memPropFlags,
VkDeviceMemory vkMemoryHandle = VK_NULL_HANDLE, MemoryAllocateFlags memoryAllocateFlags = pvrvk::MemoryAllocateFlags::e_NONE)
{
return std::make_shared<DeviceMemory_>(make_shared_enabler{}, device, allocationInfo, memPropFlags, vkMemoryHandle, memoryAllocateFlags);
}
private:
void allocateDeviceMemory(Device device, const MemoryAllocationInfo& allocationInfo, VkDeviceMemory& outMemory, const pvrvk::MemoryAllocateFlags memoryAllocateFlags = pvrvk::MemoryAllocateFlags::e_NONE)
{
// allocate the memory
VkMemoryAllocateInfo memAllocInfo = {};
memAllocInfo.sType = static_cast<VkStructureType>(StructureType::e_MEMORY_ALLOCATE_INFO);
memAllocInfo.pNext = nullptr;
memAllocInfo.allocationSize = allocationInfo.getAllocationSize();
memAllocInfo.memoryTypeIndex = allocationInfo.getMemoryTypeIndex();
// handle extension
VkExportMemoryAllocateInfoKHR memAllocateInfoKHR = {};
if (allocationInfo.getExportMemoryAllocateInfoKHR().handleTypes != ExternalMemoryHandleTypeFlags::e_NONE)
{
memAllocateInfoKHR.sType = static_cast<VkStructureType>(StructureType::e_EXPORT_MEMORY_ALLOCATE_INFO_KHR);
memAllocateInfoKHR.handleTypes = static_cast<VkExternalMemoryHandleTypeFlags>(allocationInfo.getExportMemoryAllocateInfoKHR().handleTypes);
memAllocInfo.pNext = &memAllocateInfoKHR;
}
// handle VkMemoryAllocateFlagsInfo
VkMemoryAllocateFlagsInfo memoryAllocateFlagsInfo = {};
if (memoryAllocateFlags != pvrvk::MemoryAllocateFlags::e_NONE)
{
// VK_MEMORY_ALLOCATE_DEVICE_MASK_BIT not supported until we provide api for passing deviceMask to here
assert(!static_cast<bool>(memoryAllocateFlags & pvrvk::MemoryAllocateFlags::e_DEVICE_MASK_BIT));
memoryAllocateFlagsInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO;
memoryAllocateFlagsInfo.flags = static_cast<VkMemoryAllocateFlagBits>(memoryAllocateFlags);
memoryAllocateFlagsInfo.deviceMask = 0;
if (memAllocInfo.pNext == nullptr)
{
memAllocInfo.pNext = &memoryAllocateFlagsInfo;
}
else
{
memAllocateInfoKHR.pNext = &memoryAllocateFlagsInfo; // append to pNext chain
}
}
if (memAllocInfo.memoryTypeIndex == static_cast<uint32_t>(-1))
{
throw ErrorValidationFailedEXT("Device Memory allocation failed: Could not get a Memory Type Index for the specified combination specified memory bits, properties and "
"flags");
}
vkThrowIfFailed(getDevice()->getVkBindings().vkAllocateMemory(device->getVkHandle(), &memAllocInfo, nullptr, &outMemory), "Failed to allocate device memory");
}
pvrvk::MemoryPropertyFlags _flags;
VkDeviceSize _mappedOffset;
VkDeviceSize _mappedSize;
MemoryAllocationInfo _allocationInfo;
void* _mappedMemory;
public:
DECLARE_NO_COPY_SEMANTICS(DeviceMemory_)
virtual ~DeviceMemory_()
{
if (getVkHandle() != VK_NULL_HANDLE)
{
if (!_device.expired())
{
getDevice()->getVkBindings().vkFreeMemory(getDevice()->getVkHandle(), getVkHandle(), nullptr);
_vkHandle = VK_NULL_HANDLE;
}
else
{
reportDestroyedAfterDevice();
}
}
}
DeviceMemory_(make_shared_enabler, const DeviceWeakPtr& device, const MemoryAllocationInfo& allocationInfo, pvrvk::MemoryPropertyFlags memPropFlags,
VkDeviceMemory vkMemoryHandle, const pvrvk::MemoryAllocateFlags memoryAllocateFlags = pvrvk::MemoryAllocateFlags::e_NONE)
: IDeviceMemory_(device), DeviceObjectDebugUtils(), _flags(memPropFlags), _mappedOffset(0), _mappedSize(0), _mappedMemory(nullptr)
{
_vkHandle = vkMemoryHandle;
_allocationInfo = allocationInfo;
if (vkMemoryHandle == VK_NULL_HANDLE) { allocateDeviceMemory(getDevice(), allocationInfo, _vkHandle, memoryAllocateFlags); }
}
bool isMappable() const { return static_cast<uint32_t>(_flags & pvrvk::MemoryPropertyFlags::e_HOST_VISIBLE_BIT) != 0; }
pvrvk::MemoryPropertyFlags getMemoryFlags() const { return _flags; }
VkDeviceSize getMappedOffset() const { return _mappedOffset; }
virtual void* getMappedData() { return _mappedMemory; }
VkDeviceSize getMappedSize() const { return _mappedSize; }
VkDeviceSize getSize() const { return _allocationInfo.getAllocationSize(); }
bool isMapped() const { return _mappedSize > 0; }
virtual void* map(VkDeviceSize offset = 0, VkDeviceSize size = VK_WHOLE_SIZE, pvrvk::MemoryMapFlags memoryMapFlags = pvrvk::MemoryMapFlags::e_NONE)
{
if (!isMappable())
{
throw ErrorMemoryMapFailed("Cannot map memory block as the memory was created without "
"HOST_VISIBLE_BIT or HOST_COHERENT_BIT memory flags");
}
if (_mappedSize) { throw ErrorMemoryMapFailed("Cannot map memory block as the memory is already mapped"); }
if (size != VK_WHOLE_SIZE)
{
if (offset + size > getSize()) { throw ErrorMemoryMapFailed("Cannot map map memory block : offset + size range greater than the memory block size"); }
}
vkThrowIfFailed(getDevice()->getVkBindings().vkMapMemory(getDevice()->getVkHandle(), getVkHandle(), offset, size, static_cast<VkMemoryMapFlags>(memoryMapFlags), &_mappedMemory),
"Failed to map memory block");
if (_mappedMemory == nullptr) { throw ErrorMemoryMapFailed("Failed to map memory block"); }
// store the mapped offset and mapped size
_mappedOffset = offset;
_mappedSize = size;
return _mappedMemory;
}
virtual void unmap()
{
if (!_mappedSize) { throw ErrorMemoryMapFailed("Cannot unmap memory block as the memory is not mapped"); }
_mappedSize = 0;
_mappedOffset = 0;
getDevice()->getVkBindings().vkUnmapMemory(getDevice()->getVkHandle(), getVkHandle());
_mappedMemory = nullptr;
}
void flushRange(VkDeviceSize offset = 0, VkDeviceSize size = VK_WHOLE_SIZE)
{
if (static_cast<uint32_t>(_flags & pvrvk::MemoryPropertyFlags::e_HOST_COHERENT_BIT) != 0)
{ assert(false && "Flushing memory block created using HOST_COHERENT_BIT memory flags - this is unnecessary."); }
VkMappedMemoryRange range = {};
range.sType = static_cast<VkStructureType>(StructureType::e_MAPPED_MEMORY_RANGE);
range.memory = getVkHandle();
range.offset = offset;
range.size = size;
vkThrowIfFailed(getDevice()->getVkBindings().vkFlushMappedMemoryRanges(getDevice()->getVkHandle(), 1, &range), "Failed to flush range of memory block");
}
void invalidateRange(VkDeviceSize offset = 0, VkDeviceSize size = VK_WHOLE_SIZE)
{
if (static_cast<uint32_t>(_flags & MemoryPropertyFlags::e_HOST_COHERENT_BIT) != 0)
{ assert(false && "Invalidating range of memory block created using HOST_COHERENT_BIT memory flags - this is unnecessary."); }
VkMappedMemoryRange range = {};
range.sType = static_cast<VkStructureType>(StructureType::e_MAPPED_MEMORY_RANGE);
range.memory = getVkHandle();
range.offset = offset;
range.size = size;
vkThrowIfFailed(getDevice()->getVkBindings().vkInvalidateMappedMemoryRanges(getDevice()->getVkHandle(), 1, &range), "Failed to invalidate range of memory block");
}
uint32_t getMemoryType() const { return _allocationInfo.getMemoryTypeIndex(); }
};
} // namespace impl
} // namespace pvrvk