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