diff --git a/framework/CMakeLists.txt b/framework/CMakeLists.txt index 094dabee47..169cb5c24a 100644 --- a/framework/CMakeLists.txt +++ b/framework/CMakeLists.txt @@ -78,7 +78,7 @@ set(COMMON_FILES common/ktx_common.h common/vk_common.h common/vk_initializers.h - common/glm_common.h + common/glm_common.h common/resource_caching.h common/logging.h common/helpers.h @@ -240,6 +240,7 @@ set(CORE_FILES core/command_pool.h core/swapchain.h core/command_buffer.h + core/allocated.h core/buffer.h core/image.h core/image_view.h @@ -252,6 +253,7 @@ set(CORE_FILES core/scratch_buffer.h core/acceleration_structure.h core/shader_binding_table.h + core/hpp_allocated.h core/hpp_buffer.h core/hpp_command_buffer.h core/hpp_command_pool.h @@ -291,6 +293,7 @@ set(CORE_FILES core/command_pool.cpp core/swapchain.cpp core/command_buffer.cpp + core/allocated.cpp core/buffer.cpp core/image.cpp core/image_view.cpp @@ -387,7 +390,7 @@ set(LINUX_D2D_FILES platform/unix/direct_window.h # Source Files platform/unix/unix_d2d_platform.cpp - platform/unix/direct_window.cpp) + platform/unix/direct_window.cpp) source_group("\\" FILES ${FRAMEWORK_FILES}) source_group("common\\" FILES ${COMMON_FILES}) diff --git a/framework/core/allocated.cpp b/framework/core/allocated.cpp new file mode 100644 index 0000000000..258c574119 --- /dev/null +++ b/framework/core/allocated.cpp @@ -0,0 +1,235 @@ +/* Copyright (c) 2021-2024, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2024, Bradley Austin Davis. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 the "License"; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "allocated.h" + +namespace vkb +{ + +namespace allocated +{ + +VmaAllocator &get_memory_allocator() +{ + static VmaAllocator memory_allocator = VK_NULL_HANDLE; + return memory_allocator; +} + +void init(const VmaAllocatorCreateInfo &create_info) +{ + VkResult result = vmaCreateAllocator(&create_info, &get_memory_allocator()); + if (result != VK_SUCCESS) + { + throw VulkanException{result, "Cannot create allocator"}; + } +} + +void shutdown() +{ + auto &allocator = get_memory_allocator(); + if (allocator != VK_NULL_HANDLE) + { + VmaTotalStatistics stats; + vmaCalculateStatistics(allocator, &stats); + LOGI("Total device memory leaked: {} bytes.", stats.total.statistics.allocationBytes); + vmaDestroyAllocator(allocator); + allocator = VK_NULL_HANDLE; + } +} + +AllocatedBase::AllocatedBase(const VmaAllocationCreateInfo &alloc_create_info) : + alloc_create_info(alloc_create_info) +{ +} + +AllocatedBase::AllocatedBase(AllocatedBase &&other) noexcept : + alloc_create_info(std::exchange(other.alloc_create_info, {})), + allocation(std::exchange(other.allocation, {})), + mapped_data(std::exchange(other.mapped_data, {})), + coherent(std::exchange(other.coherent, {})), + persistent(std::exchange(other.persistent, {})) +{ + assert(other.allocation == VK_NULL_HANDLE); + assert(other.mapped_data == nullptr); +} + +const uint8_t *AllocatedBase::get_data() const +{ + return mapped_data; +} + +VkDeviceMemory AllocatedBase::get_memory() const +{ + VmaAllocationInfo alloc_info; + vmaGetAllocationInfo(get_memory_allocator(), allocation, &alloc_info); + return alloc_info.deviceMemory; +} + +void AllocatedBase::flush(VkDeviceSize offset, VkDeviceSize size) +{ + if (!coherent) + { + vmaFlushAllocation(get_memory_allocator(), allocation, offset, size); + } +} + +uint8_t *AllocatedBase::map() +{ + if (!persistent && !mapped()) + { + VK_CHECK(vmaMapMemory(get_memory_allocator(), allocation, reinterpret_cast(&mapped_data))); + assert(mapped_data); + } + return mapped_data; +} + +void AllocatedBase::unmap() +{ + if (!persistent && mapped()) + { + vmaUnmapMemory(get_memory_allocator(), allocation); + mapped_data = nullptr; + } +} + +size_t AllocatedBase::update(const uint8_t *data, size_t size, size_t offset) +{ + if (persistent) + { + std::copy(data, data + size, mapped_data + offset); + flush(); + } + else + { + map(); + std::copy(data, data + size, mapped_data + offset); + flush(); + unmap(); + } + return size; +} + +size_t AllocatedBase::update(void const *data, size_t size, size_t offset) +{ + return update(reinterpret_cast(data), size, offset); +} + +void AllocatedBase::post_create(VmaAllocationInfo const &allocation_info) +{ + VkMemoryPropertyFlags memory_properties; + vmaGetAllocationMemoryProperties(get_memory_allocator(), allocation, &memory_properties); + coherent = (memory_properties & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) == VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; + mapped_data = static_cast(allocation_info.pMappedData); + persistent = mapped(); +} + +[[nodiscard]] VkBuffer AllocatedBase::create_buffer(VkBufferCreateInfo const &create_info) +{ + VkBuffer handleResult = VK_NULL_HANDLE; + VmaAllocationInfo allocation_info{}; + + auto result = vmaCreateBuffer( + get_memory_allocator(), + &create_info, + &alloc_create_info, + &handleResult, + &allocation, + &allocation_info); + + if (result != VK_SUCCESS) + { + throw VulkanException{result, "Cannot create Buffer"}; + } + post_create(allocation_info); + return handleResult; +} + +[[nodiscard]] VkImage AllocatedBase::create_image(VkImageCreateInfo const &create_info) +{ + assert(0 < create_info.mipLevels && "Images should have at least one level"); + assert(0 < create_info.arrayLayers && "Images should have at least one layer"); + assert(0 < create_info.usage && "Images should have at least one usage type"); + + VkImage handleResult = VK_NULL_HANDLE; + VmaAllocationInfo allocation_info{}; + +#if 0 + // If the image is an attachment, prefer dedicated memory + constexpr VkImageUsageFlags attachment_only_flags = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT; + if (create_info.usage & attachment_only_flags) + { + alloc_create_info.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT; + } + + if (create_info.usage & VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT) + { + alloc_create_info.preferredFlags |= VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT; + } +#endif + + auto result = vmaCreateImage( + get_memory_allocator(), + &create_info, + &alloc_create_info, + &handleResult, + &allocation, + &allocation_info); + + if (result != VK_SUCCESS) + { + throw VulkanException{result, "Cannot create Image"}; + } + + post_create(allocation_info); + return handleResult; +} + +void AllocatedBase::destroy_buffer(VkBuffer handle) +{ + if (handle != VK_NULL_HANDLE && allocation != VK_NULL_HANDLE) + { + unmap(); + vmaDestroyBuffer(get_memory_allocator(), handle, allocation); + clear(); + } +} + +void AllocatedBase::destroy_image(VkImage image) +{ + if (image != VK_NULL_HANDLE && allocation != VK_NULL_HANDLE) + { + unmap(); + vmaDestroyImage(get_memory_allocator(), image, allocation); + clear(); + } +} + +bool AllocatedBase::mapped() const +{ + return mapped_data != nullptr; +} + +void AllocatedBase::clear() +{ + mapped_data = nullptr; + persistent = false; + alloc_create_info = {}; +} + +} // namespace allocated +} // namespace vkb diff --git a/framework/core/allocated.h b/framework/core/allocated.h new file mode 100644 index 0000000000..8f0e824f7a --- /dev/null +++ b/framework/core/allocated.h @@ -0,0 +1,310 @@ +/* Copyright (c) 2021-2024, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2024, Bradley Austin Davis. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 the "License"; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "common/error.h" +#include "core/vulkan_resource.h" +#include +#include + +namespace vkb +{ + +class Device; + +namespace allocated +{ + +template < + typename BuilderType, + typename CreateInfoType, + typename SharingModeType = VkSharingMode> +struct Builder +{ + VmaAllocationCreateInfo alloc_create_info{}; + std::string debug_name; + CreateInfoType create_info; + + protected: + Builder(const Builder &other) = delete; + Builder(const CreateInfoType &create_info) : + create_info(create_info) + { + alloc_create_info.usage = VMA_MEMORY_USAGE_AUTO; + }; + + public: + BuilderType &with_debug_name(const std::string &name) + { + debug_name = name; + return *static_cast(this); + } + + BuilderType &with_vma_usage(VmaMemoryUsage usage) + { + alloc_create_info.usage = usage; + return *static_cast(this); + } + + BuilderType &with_vma_flags(VmaAllocationCreateFlags flags) + { + alloc_create_info.flags = flags; + return *static_cast(this); + } + + BuilderType &with_vma_required_flags(VkMemoryPropertyFlags flags) + { + alloc_create_info.requiredFlags = flags; + return *static_cast(this); + } + + BuilderType &with_vma_preferred_flags(VkMemoryPropertyFlags flags) + { + alloc_create_info.preferredFlags = flags; + return *static_cast(this); + } + + BuilderType &with_memory_type_bits(uint32_t type_bits) + { + alloc_create_info.memoryTypeBits = type_bits; + return *static_cast(this); + } + + BuilderType &with_vma_pool(VmaPool pool) + { + alloc_create_info.pool = pool; + return *static_cast(this); + } + + BuilderType &with_queue_families(uint32_t count, const uint32_t *family_indices) + { + create_info.queueFamilyIndexCount = count; + create_info.pQueueFamilyIndices = family_indices; + return *static_cast(this); + } + + BuilderType &with_sharing(VkSharingMode sharing) + { + create_info.sharingMode = sharing; + return *static_cast(this); + } + + BuilderType &with_implicit_sharing_mode() + { + if (create_info.queueFamilyIndexCount != 0) + { + create_info.sharingMode = static_cast(VK_SHARING_MODE_CONCURRENT); + } + else + { + create_info.sharingMode = static_cast(VK_SHARING_MODE_EXCLUSIVE); + } + return *static_cast(this); + } + + BuilderType &with_queue_families(const std::vector &queue_families) + { + return with_queue_families(static_cast(queue_families.size()), queue_families.data()); + } +}; + +void init(const VmaAllocatorCreateInfo &create_info); + +template +void init(const DeviceType &device) +{ + VmaVulkanFunctions vma_vulkan_func{}; + vma_vulkan_func.vkGetInstanceProcAddr = vkGetInstanceProcAddr; + vma_vulkan_func.vkGetDeviceProcAddr = vkGetDeviceProcAddr; + + VmaAllocatorCreateInfo allocator_info{}; + allocator_info.pVulkanFunctions = &vma_vulkan_func; + allocator_info.physicalDevice = static_cast(device.get_gpu().get_handle()); + allocator_info.device = static_cast(device.get_handle()); + allocator_info.instance = static_cast(device.get_gpu().get_instance().get_handle()); + + bool can_get_memory_requirements = device.is_extension_supported(VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME); + bool has_dedicated_allocation = device.is_extension_supported(VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME); + if (can_get_memory_requirements && has_dedicated_allocation) + { + allocator_info.flags |= VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT; + } + + if (device.is_extension_supported(VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME) && device.is_enabled(VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME)) + { + allocator_info.flags |= VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT; + } + + if (device.is_extension_supported(VK_EXT_MEMORY_BUDGET_EXTENSION_NAME) && device.is_enabled(VK_EXT_MEMORY_BUDGET_EXTENSION_NAME)) + { + allocator_info.flags |= VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT; + } + + if (device.is_extension_supported(VK_EXT_MEMORY_PRIORITY_EXTENSION_NAME) && device.is_enabled(VK_EXT_MEMORY_PRIORITY_EXTENSION_NAME)) + { + allocator_info.flags |= VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT; + } + + if (device.is_extension_supported(VK_KHR_BIND_MEMORY_2_EXTENSION_NAME) && device.is_enabled(VK_KHR_BIND_MEMORY_2_EXTENSION_NAME)) + { + allocator_info.flags |= VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT; + } + + if (device.is_extension_supported(VK_AMD_DEVICE_COHERENT_MEMORY_EXTENSION_NAME) && device.is_enabled(VK_AMD_DEVICE_COHERENT_MEMORY_EXTENSION_NAME)) + { + allocator_info.flags |= VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT; + } + + init(allocator_info); +} + +VmaAllocator &get_memory_allocator(); + +void shutdown(); + +class AllocatedBase +{ + public: + AllocatedBase() = default; + AllocatedBase(const VmaAllocationCreateInfo &alloc_create_info); + AllocatedBase(AllocatedBase &&other) noexcept; + + AllocatedBase &operator=(const AllocatedBase &) = delete; + AllocatedBase &operator=(AllocatedBase &&) = delete; + + const uint8_t *get_data() const; + VkDeviceMemory get_memory() const; + + /** + * @brief Flushes memory if it is HOST_VISIBLE and not HOST_COHERENT + */ + void flush(VkDeviceSize offset = 0, VkDeviceSize size = VK_WHOLE_SIZE); + + /** + * @brief Returns true if the memory is mapped, false otherwise + * @return mapping status + */ + bool mapped() const; + + /** + * @brief Maps vulkan memory if it isn't already mapped to an host visible address + * @return Pointer to host visible memory + */ + uint8_t *map(); + + /** + * @brief Unmaps vulkan memory from the host visible address + */ + void unmap(); + + /** + * @brief Copies byte data into the buffer + * @param data The data to copy from + * @param size The amount of bytes to copy + * @param offset The offset to start the copying into the mapped data + */ + size_t update(const uint8_t *data, size_t size, size_t offset = 0); + /** + * @brief Converts any non byte data into bytes and then updates the buffer + * @param data The data to copy from + * @param size The amount of bytes to copy + * @param offset The offset to start the copying into the mapped data + */ + size_t update(void const *data, size_t size, size_t offset = 0); + + /** + * @todo Use the vk::ArrayBuffer class to collapse some of these templates + * @brief Copies a vector of items into the buffer + * @param data The data vector to upload + * @param offset The offset to start the copying into the mapped data + */ + template + size_t update(std::vector const &data, size_t offset = 0) + { + return update(data.data(), data.size() * sizeof(T), offset); + } + + template + size_t update(std::array const &data, size_t offset = 0) + { + return update(data.data(), data.size() * sizeof(T), offset); + } + + /** + * @brief Copies an object as byte data into the buffer + * @param object The object to convert into byte data + * @param offset The offset to start the copying into the mapped data + */ + template + size_t convert_and_update(const T &object, size_t offset = 0) + { + return update(reinterpret_cast(&object), sizeof(T), offset); + } + + protected: + virtual void post_create(VmaAllocationInfo const &allocation_info); + [[nodiscard]] VkBuffer create_buffer(VkBufferCreateInfo const &create_info); + [[nodiscard]] VkImage create_image(VkImageCreateInfo const &create_info); + void destroy_buffer(VkBuffer buffer); + void destroy_image(VkImage image); + void clear(); + + VmaAllocationCreateInfo alloc_create_info{}; + VmaAllocation allocation = VK_NULL_HANDLE; + uint8_t *mapped_data = nullptr; + bool coherent = false; + bool persistent = false; // Whether the buffer is persistently mapped or not +}; + +template < + typename HandleType, + typename MemoryType = VkDeviceMemory, + typename DeviceType = const vkb::Device, + typename ParentType = vkb::core::VulkanResource> +class Allocated : public ParentType, public AllocatedBase +{ + public: + using ParentType::ParentType; + + Allocated() = delete; + Allocated(const Allocated &) = delete; + + // Import the base class constructors + template + Allocated(const VmaAllocationCreateInfo &alloc_create_info, Args &&...args) : + ParentType(std::forward(args)...), + AllocatedBase(alloc_create_info) + { + } + + Allocated(Allocated &&other) noexcept + : + ParentType{static_cast(other)}, + AllocatedBase{static_cast(other)} + { + } + + const HandleType *get() const + { + return &ParentType::get_handle(); + } +}; + +} // namespace allocated +} // namespace vkb diff --git a/framework/core/buffer.cpp b/framework/core/buffer.cpp index defe63aed7..802d62179e 100644 --- a/framework/core/buffer.cpp +++ b/framework/core/buffer.cpp @@ -1,4 +1,4 @@ -/* Copyright (c) 2019-2023, Arm Limited and Contributors +/* Copyright (c) 2019-2024, Arm Limited and Contributors * * SPDX-License-Identifier: Apache-2.0 * @@ -23,123 +23,64 @@ namespace vkb { namespace core { -Buffer::Buffer(Device const &device, VkDeviceSize size, VkBufferUsageFlags buffer_usage, VmaMemoryUsage memory_usage, VmaAllocationCreateFlags flags, const std::vector &queue_family_indices) : - VulkanResource{VK_NULL_HANDLE, &device}, - size{size} -{ -#ifdef VK_USE_PLATFORM_METAL_EXT - // Workaround for Mac (MoltenVK requires unmapping https://github.com/KhronosGroup/MoltenVK/issues/175) - // Force cleares the flag VMA_ALLOCATION_CREATE_MAPPED_BIT - flags &= ~VMA_ALLOCATION_CREATE_MAPPED_BIT; -#endif - - persistent = (flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0; - - VkBufferCreateInfo buffer_info{VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO}; - buffer_info.usage = buffer_usage; - buffer_info.size = size; - if (queue_family_indices.size() >= 2) - { - buffer_info.sharingMode = VK_SHARING_MODE_CONCURRENT; - buffer_info.queueFamilyIndexCount = static_cast(queue_family_indices.size()); - buffer_info.pQueueFamilyIndices = queue_family_indices.data(); - } - - VmaAllocationCreateInfo memory_info{}; - memory_info.flags = flags; - memory_info.usage = memory_usage; - - VmaAllocationInfo allocation_info{}; - auto result = vmaCreateBuffer(device.get_memory_allocator(), - &buffer_info, &memory_info, - &handle, &allocation, - &allocation_info); - - if (result != VK_SUCCESS) - { - throw VulkanException{result, "Cannot create Buffer"}; - } - - memory = allocation_info.deviceMemory; - if (persistent) - { - mapped_data = static_cast(allocation_info.pMappedData); - } +Buffer BufferBuilder::build(Device const &device) const +{ + return Buffer(device, *this); } -Buffer::Buffer(Buffer &&other) : - VulkanResource{other.handle, other.device}, - allocation{other.allocation}, - memory{other.memory}, - size{other.size}, - mapped_data{other.mapped_data}, - mapped{other.mapped} +BufferPtr BufferBuilder::build_unique(Device const &device) const { - // Reset other handles to avoid releasing on destruction - other.allocation = VK_NULL_HANDLE; - other.memory = VK_NULL_HANDLE; - other.mapped_data = nullptr; - other.mapped = false; + return std::make_unique(device, *this); } -Buffer::~Buffer() +Buffer Buffer::create_staging_buffer(Device const &device, VkDeviceSize size, const void *data) { - if (handle != VK_NULL_HANDLE && allocation != VK_NULL_HANDLE) + BufferBuilder builder{size}; + builder.with_vma_flags(VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT); + builder.with_usage(VK_BUFFER_USAGE_TRANSFER_SRC_BIT); + Buffer result(device, builder); + if (data != nullptr) { - unmap(); - vmaDestroyBuffer(device->get_memory_allocator(), handle, allocation); + result.update(data, size); } + return result; } -const VkBuffer *Buffer::get() const -{ - return &handle; -} - -VmaAllocation Buffer::get_allocation() const -{ - return allocation; -} - -VkDeviceMemory Buffer::get_memory() const -{ - return memory; -} - -VkDeviceSize Buffer::get_size() const -{ - return size; -} - -uint8_t *Buffer::map() -{ - if (!mapped && !mapped_data) +Buffer::Buffer(Device const &device, VkDeviceSize size, VkBufferUsageFlags buffer_usage, VmaMemoryUsage memory_usage, VmaAllocationCreateFlags flags, const std::vector &queue_family_indices) : + Buffer(device, + BufferBuilder(size) + .with_usage(buffer_usage) + .with_vma_usage(memory_usage) + .with_vma_flags(flags) + .with_queue_families(queue_family_indices) + .with_implicit_sharing_mode()) +{} + +Buffer::Buffer(Device const &device, const BufferBuilder &builder) : + Allocated{builder.alloc_create_info, VK_NULL_HANDLE, &device}, size(builder.create_info.size) +{ + handle = create_buffer(builder.create_info); + if (!builder.debug_name.empty()) { - VK_CHECK(vmaMapMemory(device->get_memory_allocator(), allocation, reinterpret_cast(&mapped_data))); - mapped = true; + set_debug_name(builder.debug_name); } - return mapped_data; } -void Buffer::unmap() +Buffer::Buffer(Buffer &&other) noexcept : + Allocated{std::move(other)}, + size{std::exchange(other.size, {})} { - if (mapped) - { - vmaUnmapMemory(device->get_memory_allocator(), allocation); - mapped_data = nullptr; - mapped = false; - } } -void Buffer::flush() const +Buffer::~Buffer() { - vmaFlushAllocation(device->get_memory_allocator(), allocation, 0, size); + destroy_buffer(get_handle()); } -void Buffer::update(const std::vector &data, size_t offset) +VkDeviceSize Buffer::get_size() const { - update(data.data(), data.size(), offset); + return size; } uint64_t Buffer::get_device_address() @@ -150,26 +91,5 @@ uint64_t Buffer::get_device_address() return vkGetBufferDeviceAddressKHR(device->get_handle(), &buffer_device_address_info); } -void Buffer::update(void const *data, size_t size, size_t offset) -{ - update(reinterpret_cast(data), size, offset); -} - -void Buffer::update(const uint8_t *data, const size_t size, const size_t offset) -{ - if (persistent) - { - std::copy(data, data + size, mapped_data + offset); - flush(); - } - else - { - map(); - std::copy(data, data + size, mapped_data + offset); - flush(); - unmap(); - } -} - } // namespace core } // namespace vkb diff --git a/framework/core/buffer.h b/framework/core/buffer.h index 5ccf37d4fc..e035f04fb4 100644 --- a/framework/core/buffer.h +++ b/framework/core/buffer.h @@ -19,6 +19,7 @@ #include "common/helpers.h" #include "common/vk_common.h" +#include "core/allocated.h" #include "core/vulkan_resource.h" namespace vkb @@ -27,9 +28,65 @@ class Device; namespace core { -class Buffer : public VulkanResource + +class Buffer; +using BufferPtr = std::unique_ptr; + +struct BufferBuilder : public allocated::Builder +{ + BufferBuilder(VkDeviceSize size) : + Builder(VkBufferCreateInfo{VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, nullptr, 0, size}) + { + } + + BufferBuilder &with_flags(VkBufferCreateFlags flags) + { + create_info.flags = flags; + return *this; + } + + BufferBuilder &with_usage(VkBufferUsageFlags usage) + { + create_info.usage = usage; + return *this; + } + + BufferBuilder &with_sharing_mode(VkSharingMode sharing_mode) + { + create_info.sharingMode = sharing_mode; + return *this; + } + + BufferBuilder &with_implicit_sharing_mode() + { + if (create_info.queueFamilyIndexCount != 0) + { + create_info.sharingMode = VK_SHARING_MODE_CONCURRENT; + } + return *this; + } + + Buffer build(Device const &device) const; + BufferPtr build_unique(Device const &device) const; +}; + +class Buffer : public allocated::Allocated { public: + static Buffer create_staging_buffer(Device const &device, VkDeviceSize size, const void *data); + + template + static Buffer create_staging_buffer(Device const &device, const std::vector &data) + { + return create_staging_buffer(device, data.size() * sizeof(T), data.data()); + } + + template + static Buffer create_staging_buffer(Device const &device, const T &data) + { + return create_staging_buffer(device, sizeof(T), &data); + } + /** * @brief Creates a buffer using VMA * @param device A valid Vulkan device @@ -39,6 +96,7 @@ class Buffer : public VulkanResource &queue_family_indices = {}); + Buffer(Device const &device, const BufferBuilder &builder); + Buffer(const Buffer &) = delete; - Buffer(Buffer &&other); + Buffer(Buffer &&other) noexcept; ~Buffer(); @@ -56,6 +116,9 @@ class Buffer : public VulkanResource static std::vector copy(std::unordered_map &buffers, const char *buffer_name) { @@ -82,103 +145,18 @@ class Buffer : public VulkanResource &data, size_t offset = 0); - - template - void update(std::array const &data, size_t offset = 0) - { - update(data.data(), data.size() * sizeof(T), offset); - } - - template - void update(std::vector const &data, size_t offset = 0) - { - update(data.data(), data.size() * sizeof(T), offset); - } - - /** - * @brief Copies an object as byte data into the buffer - * @param object The object to convert into byte data - * @param offset The offset to start the copying into the mapped data - */ - template - void convert_and_update(const T &object, size_t offset = 0) - { - update(reinterpret_cast(&object), sizeof(T), offset); - } - /** * @return Return the buffer's device address (note: requires that the buffer has been created with the VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT usage fla) */ uint64_t get_device_address(); private: - VmaAllocation allocation{VK_NULL_HANDLE}; - - VkDeviceMemory memory{VK_NULL_HANDLE}; - VkDeviceSize size{0}; - - uint8_t *mapped_data{nullptr}; - - /// Whether the buffer is persistently mapped or not - bool persistent{false}; - - /// Whether the buffer has been mapped with vmaMapMemory - bool mapped{false}; }; } // namespace core } // namespace vkb diff --git a/framework/core/command_buffer.h b/framework/core/command_buffer.h index e8680f0195..25c0530d39 100644 --- a/framework/core/command_buffer.h +++ b/framework/core/command_buffer.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2019-2023, Arm Limited and Contributors +/* Copyright (c) 2019-2024, Arm Limited and Contributors * * SPDX-License-Identifier: Apache-2.0 * @@ -47,7 +47,7 @@ struct LightingState; * @brief Helper class to manage and record a command buffer, building and * keeping track of pipeline state and resource bindings */ -class CommandBuffer : public core::VulkanResource +class CommandBuffer : public core::VulkanResource { public: enum class ResetMode diff --git a/framework/core/device.cpp b/framework/core/device.cpp index 3faaba8d3a..5dd42f2b9b 100644 --- a/framework/core/device.cpp +++ b/framework/core/device.cpp @@ -197,53 +197,7 @@ Device::Device(PhysicalDevice &gpu, } } - VmaVulkanFunctions vma_vulkan_func{}; - vma_vulkan_func.vkGetInstanceProcAddr = vkGetInstanceProcAddr; - vma_vulkan_func.vkGetDeviceProcAddr = vkGetDeviceProcAddr; - - VmaAllocatorCreateInfo allocator_info{}; - allocator_info.physicalDevice = gpu.get_handle(); - allocator_info.device = handle; - allocator_info.instance = gpu.get_instance().get_handle(); - - if (can_get_memory_requirements && has_dedicated_allocation) - { - allocator_info.flags |= VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT; - } - - if (is_extension_supported(VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME) && is_enabled(VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME)) - { - allocator_info.flags |= VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT; - } - - if (is_extension_supported(VK_EXT_MEMORY_BUDGET_EXTENSION_NAME) && is_enabled(VK_EXT_MEMORY_BUDGET_EXTENSION_NAME)) - { - allocator_info.flags |= VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT; - } - - if (is_extension_supported(VK_EXT_MEMORY_PRIORITY_EXTENSION_NAME) && is_enabled(VK_EXT_MEMORY_PRIORITY_EXTENSION_NAME)) - { - allocator_info.flags |= VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT; - } - - if (is_extension_supported(VK_KHR_BIND_MEMORY_2_EXTENSION_NAME) && is_enabled(VK_KHR_BIND_MEMORY_2_EXTENSION_NAME)) - { - allocator_info.flags |= VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT; - } - - if (is_extension_supported(VK_AMD_DEVICE_COHERENT_MEMORY_EXTENSION_NAME) && is_enabled(VK_AMD_DEVICE_COHERENT_MEMORY_EXTENSION_NAME)) - { - allocator_info.flags |= VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT; - } - - allocator_info.pVulkanFunctions = &vma_vulkan_func; - - result = vmaCreateAllocator(&allocator_info, &memory_allocator); - - if (result != VK_SUCCESS) - { - throw VulkanException{result, "Cannot create allocator"}; - } + prepare_memory_allocator(); command_pool = std::make_unique(*this, get_queue_by_flags(VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT, 0).get_family_index()); fence_pool = std::make_unique(*this); @@ -264,15 +218,7 @@ Device::~Device() command_pool.reset(); fence_pool.reset(); - if (memory_allocator != VK_NULL_HANDLE) - { - VmaTotalStatistics stats; - vmaCalculateStatistics(memory_allocator, &stats); - - LOGI("Total device memory leaked: {} bytes.", stats.total.statistics.allocationBytes); - - vmaDestroyAllocator(memory_allocator); - } + vkb::allocated::shutdown(); if (handle != VK_NULL_HANDLE) { @@ -280,7 +226,7 @@ Device::~Device() } } -bool Device::is_extension_supported(const std::string &requested_extension) +bool Device::is_extension_supported(const std::string &requested_extension) const { return std::find_if(device_extensions.begin(), device_extensions.end(), [requested_extension](auto &device_extension) { @@ -288,7 +234,7 @@ bool Device::is_extension_supported(const std::string &requested_extension) }) != device_extensions.end(); } -bool Device::is_enabled(const char *extension) +bool Device::is_enabled(const char *extension) const { return std::find_if(enabled_extensions.begin(), enabled_extensions.end(), [extension](const char *enabled_extension) { return strcmp(extension, enabled_extension) == 0; }) != enabled_extensions.end(); } @@ -298,11 +244,6 @@ const PhysicalDevice &Device::get_gpu() const return gpu; } -VmaAllocator Device::get_memory_allocator() const -{ - return memory_allocator; -} - DriverVersion Device::get_driver_version() const { DriverVersion version; @@ -608,53 +549,7 @@ void Device::create_internal_command_pool() void Device::prepare_memory_allocator() { - bool can_get_memory_requirements = is_extension_supported("VK_KHR_get_memory_requirements2"); - bool has_dedicated_allocation = is_extension_supported("VK_KHR_dedicated_allocation"); - - VmaVulkanFunctions vma_vulkan_func{}; - vma_vulkan_func.vkAllocateMemory = vkAllocateMemory; - vma_vulkan_func.vkBindBufferMemory = vkBindBufferMemory; - vma_vulkan_func.vkBindImageMemory = vkBindImageMemory; - vma_vulkan_func.vkCreateBuffer = vkCreateBuffer; - vma_vulkan_func.vkCreateImage = vkCreateImage; - vma_vulkan_func.vkDestroyBuffer = vkDestroyBuffer; - vma_vulkan_func.vkDestroyImage = vkDestroyImage; - vma_vulkan_func.vkFlushMappedMemoryRanges = vkFlushMappedMemoryRanges; - vma_vulkan_func.vkFreeMemory = vkFreeMemory; - vma_vulkan_func.vkGetBufferMemoryRequirements = vkGetBufferMemoryRequirements; - vma_vulkan_func.vkGetImageMemoryRequirements = vkGetImageMemoryRequirements; - vma_vulkan_func.vkGetPhysicalDeviceMemoryProperties = vkGetPhysicalDeviceMemoryProperties; - vma_vulkan_func.vkGetPhysicalDeviceProperties = vkGetPhysicalDeviceProperties; - vma_vulkan_func.vkInvalidateMappedMemoryRanges = vkInvalidateMappedMemoryRanges; - vma_vulkan_func.vkMapMemory = vkMapMemory; - vma_vulkan_func.vkUnmapMemory = vkUnmapMemory; - vma_vulkan_func.vkCmdCopyBuffer = vkCmdCopyBuffer; - - VmaAllocatorCreateInfo allocator_info{}; - allocator_info.physicalDevice = gpu.get_handle(); - allocator_info.device = handle; - allocator_info.instance = gpu.get_instance().get_handle(); - - if (can_get_memory_requirements && has_dedicated_allocation) - { - allocator_info.flags |= VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT; - vma_vulkan_func.vkGetBufferMemoryRequirements2KHR = vkGetBufferMemoryRequirements2KHR; - vma_vulkan_func.vkGetImageMemoryRequirements2KHR = vkGetImageMemoryRequirements2KHR; - } - - if (is_extension_supported(VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME) && is_enabled(VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME)) - { - allocator_info.flags |= VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT; - } - - allocator_info.pVulkanFunctions = &vma_vulkan_func; - - VkResult result = vmaCreateAllocator(&allocator_info, &memory_allocator); - - if (result != VK_SUCCESS) - { - throw VulkanException{result, "Cannot create allocator"}; - } + vkb::allocated::init(*this); } CommandBuffer &Device::request_command_buffer() const diff --git a/framework/core/device.h b/framework/core/device.h index d4bb7b5903..58aa098479 100644 --- a/framework/core/device.h +++ b/framework/core/device.h @@ -1,5 +1,5 @@ -/* Copyright (c) 2019-2023, Arm Limited and Contributors - * Copyright (c) 2019-2023, Sascha Willems +/* Copyright (c) 2019-2024, Arm Limited and Contributors + * Copyright (c) 2019-2024, Sascha Willems * * SPDX-License-Identifier: Apache-2.0 * @@ -50,7 +50,7 @@ struct DriverVersion uint16_t patch; }; -class Device : public core::VulkanResource +class Device : public core::VulkanResource { public: /** @@ -87,8 +87,6 @@ class Device : public core::VulkanResource const PhysicalDevice &get_gpu() const; - VmaAllocator get_memory_allocator() const; - /** * @brief Returns the debug utils associated with this Device. */ @@ -128,9 +126,9 @@ class Device : public core::VulkanResource */ const Queue &get_suitable_graphics_queue() const; - bool is_extension_supported(const std::string &extension); + bool is_extension_supported(const std::string &extension) const; - bool is_enabled(const char *extension); + bool is_enabled(const char *extension) const; uint32_t get_queue_family_index(VkQueueFlagBits queue_flag); @@ -225,8 +223,6 @@ class Device : public core::VulkanResource std::vector enabled_extensions{}; - VmaAllocator memory_allocator{VK_NULL_HANDLE}; - std::vector> queues; /// A command pool associated to the primary queue diff --git a/framework/core/hpp_allocated.h b/framework/core/hpp_allocated.h new file mode 100644 index 0000000000..2b1e825011 --- /dev/null +++ b/framework/core/hpp_allocated.h @@ -0,0 +1,103 @@ +/* Copyright (c) 2021-2024, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2024, Bradley Austin Davis. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 the "License"; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "allocated.h" +#include "hpp_vulkan_resource.h" + +namespace vkb +{ +namespace allocated +{ + +template < + typename BuilderType, + typename CreateInfoType> +struct HPPBuilder : public Builder +{ + private: + using Parent = Builder; + + protected: + HPPBuilder(const HPPBuilder &other) = delete; + + HPPBuilder(const CreateInfoType &create_info) : + Parent(create_info) + {} + + public: + BuilderType &with_vma_required_flags(const vk::MemoryPropertyFlags &flags) + { + Parent::alloc_create_info.requiredFlags = flags.operator VkMemoryPropertyFlags(); + return *static_cast(this); + } + + BuilderType &with_vma_preferred_flags(const vk::MemoryPropertyFlags &flags) + { + Parent::alloc_create_info.preferredFlags = flags.operator VkMemoryPropertyFlags(); + return *static_cast(this); + } + + BuilderType &with_sharing(vk::SharingMode sharingMode) + { + Parent::create_info.sharingMode = sharingMode; + return *static_cast(this); + } +}; + +template +class HPPAllocated : public Allocated< + HandleType, + vk::DeviceMemory, + vkb::core::HPPDevice, + vkb::core::HPPVulkanResource> +{ + using Parent = Allocated>; + + public: + using Parent::get_handle; + using Parent::Parent; + using Parent::update; + + /** + * @brief Copies byte data into the buffer + * @param data The data to copy from + * @param offset The offset to start the copying into the mapped data + */ + template + vk::DeviceSize update(const vk::ArrayProxy &data, size_t offset = 0) + { + return Parent::update(static_cast(data.data()), data.size() * sizeof(T), offset); + } + + /** + * @brief Copies byte data into the buffer + * @param data The data to copy from + * @param count The number of array elements + * @param offset The offset to start the copying into the mapped data + */ + template + vk::DeviceSize update_from_array(const T *data, size_t count, size_t offset = 0) + { + return update(vk::ArrayProxy{data, count}, offset); + } +}; + +} // namespace allocated +} // namespace vkb diff --git a/framework/core/hpp_buffer.cpp b/framework/core/hpp_buffer.cpp index 6c8744fedd..c5f3338c01 100644 --- a/framework/core/hpp_buffer.cpp +++ b/framework/core/hpp_buffer.cpp @@ -1,4 +1,4 @@ -/* Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved. +/* Copyright (c) 2023-2024, NVIDIA CORPORATION. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 * @@ -23,119 +23,70 @@ namespace vkb { namespace core { -HPPBuffer::HPPBuffer(vkb::core::HPPDevice &device, - vk::DeviceSize size_, - vk::BufferUsageFlags buffer_usage, - VmaMemoryUsage memory_usage, - VmaAllocationCreateFlags flags, - const std::vector &queue_family_indices) : - HPPVulkanResource(nullptr, &device), size(size_) -{ -#ifdef VK_USE_PLATFORM_METAL_EXT - // Workaround for Mac (MoltenVK requires unmapping https://github.com/KhronosGroup/MoltenVK/issues/175) - // Force cleares the flag VMA_ALLOCATION_CREATE_MAPPED_BIT - flags &= ~VMA_ALLOCATION_CREATE_MAPPED_BIT; -#endif - - persistent = (flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0; - - vk::BufferCreateInfo buffer_create_info({}, size, buffer_usage); - if (queue_family_indices.size() >= 2) - { - buffer_create_info.sharingMode = vk::SharingMode::eConcurrent; - buffer_create_info.queueFamilyIndexCount = static_cast(queue_family_indices.size()); - buffer_create_info.pQueueFamilyIndices = queue_family_indices.data(); - } - - VmaAllocationCreateInfo memory_info{}; - memory_info.flags = flags; - memory_info.usage = memory_usage; - - VmaAllocationInfo allocation_info{}; - auto result = vmaCreateBuffer(device.get_memory_allocator(), - reinterpret_cast(&buffer_create_info), &memory_info, - reinterpret_cast(&get_handle()), &allocation, - &allocation_info); - - if (result != VK_SUCCESS) - { - throw VulkanException{result, "Cannot create HPPBuffer"}; - } - - memory = static_cast(allocation_info.deviceMemory); - - if (persistent) - { - mapped_data = static_cast(allocation_info.pMappedData); - } -} - -HPPBuffer::HPPBuffer(HPPBuffer &&other) : - HPPVulkanResource{other.get_handle(), &other.get_device()}, - allocation(std::exchange(other.allocation, {})), - memory(std::exchange(other.memory, {})), - size(std::exchange(other.size, {})), - mapped_data(std::exchange(other.mapped_data, {})), - mapped(std::exchange(other.mapped, {})) -{} - -HPPBuffer::~HPPBuffer() -{ - if (get_handle() && (allocation != VK_NULL_HANDLE)) - { - unmap(); - vmaDestroyBuffer(get_device().get_memory_allocator(), static_cast(get_handle()), allocation); - } -} - -VmaAllocation HPPBuffer::get_allocation() const -{ - return allocation; -} - -vk::DeviceMemory HPPBuffer::get_memory() const -{ - return memory; -} -vk::DeviceSize HPPBuffer::get_size() const +HPPBuffer HPPBufferBuilder::build(HPPDevice &device) const { - return size; + return HPPBuffer{device, *this}; } -const uint8_t *HPPBuffer::get_data() const +HPPBufferPtr HPPBufferBuilder::build_unique(HPPDevice &device) const { - return mapped_data; + return std::make_unique(device, *this); } -uint8_t *HPPBuffer::map() +HPPBuffer HPPBuffer::create_staging_buffer(HPPDevice &device, vk::DeviceSize size, const void *data) { - if (!mapped && !mapped_data) + HPPBuffer staging_buffer = HPPBufferBuilder{size} + .with_usage(vk::BufferUsageFlagBits::eTransferSrc) + .with_vma_flags(VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT) + .build(device); + if (data != nullptr) { - VK_CHECK(vmaMapMemory(get_device().get_memory_allocator(), allocation, reinterpret_cast(&mapped_data))); - mapped = true; + staging_buffer.update(static_cast(data), size); } - return mapped_data; + return staging_buffer; } -void HPPBuffer::unmap() +HPPBuffer::HPPBuffer(vkb::core::HPPDevice &device, + vk::DeviceSize size_, + vk::BufferUsageFlags buffer_usage, + VmaMemoryUsage memory_usage, + VmaAllocationCreateFlags flags, + const std::vector &queue_family_indices) : + HPPBuffer(device, + HPPBufferBuilder{size_} + .with_usage(buffer_usage) + .with_vma_flags(flags) + .with_vma_usage(memory_usage) + .with_queue_families(queue_family_indices) + .with_implicit_sharing_mode()) +{} + +HPPBuffer::HPPBuffer( + vkb::core::HPPDevice &device, + HPPBufferBuilder const &builder) : + Parent{builder.alloc_create_info, nullptr, &device}, size(builder.create_info.size) { - if (mapped) + get_handle() = create_buffer(builder.create_info.operator const VkBufferCreateInfo &()); + if (!builder.debug_name.empty()) { - vmaUnmapMemory(get_device().get_memory_allocator(), allocation); - mapped_data = nullptr; - mapped = false; + set_debug_name(builder.debug_name); } } -void HPPBuffer::flush() +HPPBuffer::HPPBuffer(HPPBuffer &&other) noexcept : + HPPAllocated{static_cast(other)}, + size(std::exchange(other.size, {})) +{} + +HPPBuffer::~HPPBuffer() { - vmaFlushAllocation(get_device().get_memory_allocator(), allocation, 0, size); + destroy_buffer(get_handle()); } -void HPPBuffer::update(const std::vector &data, size_t offset) +vk::DeviceSize HPPBuffer::get_size() const { - update(data.data(), data.size(), offset); + return size; } uint64_t HPPBuffer::get_device_address() const @@ -143,26 +94,5 @@ uint64_t HPPBuffer::get_device_address() const return get_device().get_handle().getBufferAddressKHR({get_handle()}); } -void HPPBuffer::update(void const *data, size_t size, size_t offset) -{ - update(reinterpret_cast(data), size, offset); -} - -void HPPBuffer::update(const uint8_t *data, const size_t size, const size_t offset) -{ - if (persistent) - { - std::copy(data, data + size, mapped_data + offset); - flush(); - } - else - { - map(); - std::copy(data, data + size, mapped_data + offset); - flush(); - unmap(); - } -} - } // namespace core } // namespace vkb diff --git a/framework/core/hpp_buffer.h b/framework/core/hpp_buffer.h index 4aa899c966..92aaba2148 100644 --- a/framework/core/hpp_buffer.h +++ b/framework/core/hpp_buffer.h @@ -17,18 +17,61 @@ #pragma once +#include "allocated.h" +#include "buffer.h" +#include "common/hpp_error.h" +#include "hpp_allocated.h" #include "hpp_vulkan_resource.h" - #include -#include namespace vkb { namespace core { -class HPPBuffer : public vkb::core::HPPVulkanResource + +class HPPBuffer; +using HPPBufferPtr = std::unique_ptr; + +struct HPPBufferBuilder : public allocated::HPPBuilder { + private: + using Parent = HPPBuilder; + public: + HPPBufferBuilder(vk::DeviceSize size) : + HPPBuilder(vk::BufferCreateInfo{{}, size}) + { + } + + HPPBufferBuilder &with_usage(vk::BufferUsageFlags usage) + { + create_info.usage = usage; + return *this; + } + + HPPBufferBuilder &with_flags(vk::BufferCreateFlags flags) + { + create_info.flags = flags; + return *this; + } + + HPPBuffer build(HPPDevice &device) const; + HPPBufferPtr build_unique(HPPDevice &device) const; +}; + +class HPPBuffer : public allocated::HPPAllocated +{ + using Parent = allocated::HPPAllocated; + + public: + static HPPBuffer create_staging_buffer(HPPDevice &device, vk::DeviceSize size, const void *data); + + template + static HPPBuffer create_staging_buffer(HPPDevice &device, std::vector const &data) + { + return create_staging_buffer(device, data.size() * sizeof(T), data.data()); + } + /** * @brief Creates a buffer using VMA * @param device A valid Vulkan device @@ -38,25 +81,25 @@ class HPPBuffer : public vkb::core::HPPVulkanResource * @param flags The allocation create flags * @param queue_family_indices optional queue family indices */ - HPPBuffer(vkb::core::HPPDevice &device, + // [[deprecated("Use the HPPBufferBuilder ctor instead")]] + HPPBuffer(HPPDevice &device, vk::DeviceSize size, vk::BufferUsageFlags buffer_usage, VmaMemoryUsage memory_usage, VmaAllocationCreateFlags flags = VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT, const std::vector &queue_family_indices = {}); + HPPBuffer(vkb::core::HPPDevice &device, + HPPBufferBuilder const &builder); + HPPBuffer(const HPPBuffer &) = delete; - HPPBuffer(HPPBuffer &&other); + HPPBuffer(HPPBuffer &&other) noexcept; ~HPPBuffer(); HPPBuffer &operator=(const HPPBuffer &) = delete; HPPBuffer &operator=(HPPBuffer &&) = delete; - VmaAllocation get_allocation() const; - const uint8_t *get_data() const; - vk::DeviceMemory get_memory() const; - /** * @return Return the buffer's device address (note: requires that the buffer has been created with the VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT usage fla) */ @@ -67,69 +110,9 @@ class HPPBuffer : public vkb::core::HPPVulkanResource */ vk::DeviceSize get_size() const; - /** - * @brief Flushes memory if it is HOST_VISIBLE and not HOST_COHERENT - */ - void flush(); - - /** - * @brief Maps vulkan memory if it isn't already mapped to an host visible address - * @return Pointer to host visible memory - */ - uint8_t *map(); - - /** - * @brief Unmaps vulkan memory from the host visible address - */ - void unmap(); - - /** - * @brief Copies byte data into the buffer - * @param data The data to copy from - * @param size The amount of bytes to copy - * @param offset The offset to start the copying into the mapped data - */ - void update(const uint8_t *data, size_t size, size_t offset = 0); - - /** - * @brief Converts any non byte data into bytes and then updates the buffer - * @param data The data to copy from - * @param size The amount of bytes to copy - * @param offset The offset to start the copying into the mapped data - */ - void update(void const *data, size_t size, size_t offset = 0); - - /** - * @brief Copies a vector of bytes into the buffer - * @param data The data vector to upload - * @param offset The offset to start the copying into the mapped data - */ - void update(const std::vector &data, size_t offset = 0); - - template - void update(std::vector const &data, size_t offset = 0) - { - update(data.data(), data.size() * sizeof(T), offset); - } - - /** - * @brief Copies an object as byte data into the buffer - * @param object The object to convert into byte data - * @param offset The offset to start the copying into the mapped data - */ - template - void convert_and_update(const T &object, size_t offset = 0) - { - update(reinterpret_cast(&object), sizeof(T), offset); - } - private: - VmaAllocation allocation = VK_NULL_HANDLE; - vk::DeviceMemory memory = nullptr; - vk::DeviceSize size = 0; - uint8_t *mapped_data = nullptr; - bool persistent = false; // Whether the buffer is persistently mapped or not - bool mapped = false; // Whether the buffer has been mapped with vmaMapMemory + vk::DeviceSize size = 0; }; + } // namespace core } // namespace vkb diff --git a/framework/core/hpp_device.cpp b/framework/core/hpp_device.cpp index 2e4c49b14d..7ce71f559c 100644 --- a/framework/core/hpp_device.cpp +++ b/framework/core/hpp_device.cpp @@ -180,53 +180,7 @@ HPPDevice::HPPDevice(vkb::core::HPPPhysicalDevice &gpu, } } - VmaVulkanFunctions vma_vulkan_func{}; - vma_vulkan_func.vkGetInstanceProcAddr = vkGetInstanceProcAddr; - vma_vulkan_func.vkGetDeviceProcAddr = reinterpret_cast(get_handle().getProcAddr("vkGetDeviceProcAddr")); - - VmaAllocatorCreateInfo allocator_info{}; - allocator_info.physicalDevice = static_cast(gpu.get_handle()); - allocator_info.device = static_cast(get_handle()); - allocator_info.instance = static_cast(gpu.get_instance().get_handle()); - - if (can_get_memory_requirements && has_dedicated_allocation) - { - allocator_info.flags |= VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT; - } - - if (is_extension_supported(VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME) && is_enabled(VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME)) - { - allocator_info.flags |= VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT; - } - - if (is_extension_supported(VK_EXT_MEMORY_BUDGET_EXTENSION_NAME) && is_enabled(VK_EXT_MEMORY_BUDGET_EXTENSION_NAME)) - { - allocator_info.flags |= VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT; - } - - if (is_extension_supported(VK_EXT_MEMORY_PRIORITY_EXTENSION_NAME) && is_enabled(VK_EXT_MEMORY_PRIORITY_EXTENSION_NAME)) - { - allocator_info.flags |= VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT; - } - - if (is_extension_supported(VK_KHR_BIND_MEMORY_2_EXTENSION_NAME) && is_enabled(VK_KHR_BIND_MEMORY_2_EXTENSION_NAME)) - { - allocator_info.flags |= VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT; - } - - if (is_extension_supported(VK_AMD_DEVICE_COHERENT_MEMORY_EXTENSION_NAME) && is_enabled(VK_AMD_DEVICE_COHERENT_MEMORY_EXTENSION_NAME)) - { - allocator_info.flags |= VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT; - } - - allocator_info.pVulkanFunctions = &vma_vulkan_func; - - VkResult result = vmaCreateAllocator(&allocator_info, &memory_allocator); - - if (result != VK_SUCCESS) - { - throw VulkanException{result, "Cannot create allocator"}; - } + vkb::allocated::init(*this); command_pool = std::make_unique( *this, get_queue_by_flags(vk::QueueFlagBits::eGraphics | vk::QueueFlagBits::eCompute, 0).get_family_index()); @@ -240,15 +194,7 @@ HPPDevice::~HPPDevice() command_pool.reset(); fence_pool.reset(); - if (memory_allocator != VK_NULL_HANDLE) - { - VmaTotalStatistics stats; - vmaCalculateStatistics(memory_allocator, &stats); - - LOGI("Total device memory leaked: {} bytes.", stats.total.statistics.allocationBytes); - - vmaDestroyAllocator(memory_allocator); - } + vkb::allocated::shutdown(); if (get_handle()) { @@ -275,11 +221,6 @@ vkb::core::HPPPhysicalDevice const &HPPDevice::get_gpu() const return gpu; } -VmaAllocator const &HPPDevice::get_memory_allocator() const -{ - return memory_allocator; -} - vkb::core::HPPDebugUtils const &HPPDevice::get_debug_utils() const { return *debug_utils; diff --git a/framework/core/hpp_device.h b/framework/core/hpp_device.h index 5bbfdf9d58..342a2f106e 100644 --- a/framework/core/hpp_device.h +++ b/framework/core/hpp_device.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2022-2023, NVIDIA CORPORATION. All rights reserved. +/* Copyright (c) 2022-2024, NVIDIA CORPORATION. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 * @@ -60,8 +60,6 @@ class HPPDevice : public vkb::core::HPPVulkanResource vkb::core::HPPPhysicalDevice const &get_gpu() const; - VmaAllocator const &get_memory_allocator() const; - /** * @brief Returns the debug utils associated with this HPPDevice. */ @@ -139,8 +137,6 @@ class HPPDevice : public vkb::core::HPPVulkanResource std::vector enabled_extensions{}; - VmaAllocator memory_allocator{VK_NULL_HANDLE}; - std::vector> queues; /// A command pool associated to the primary queue diff --git a/framework/core/hpp_image.cpp b/framework/core/hpp_image.cpp index 5e6f27363f..d03ea50e3b 100644 --- a/framework/core/hpp_image.cpp +++ b/framework/core/hpp_image.cpp @@ -1,4 +1,4 @@ -/* Copyright (c) 2022-2023, NVIDIA CORPORATION. All rights reserved. +/* Copyright (c) 2022-2024, NVIDIA CORPORATION. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 * @@ -43,6 +43,17 @@ inline vk::ImageType find_image_type(vk::Extent3D const &extent) namespace core { + +HPPImage HPPImageBuilder::build(HPPDevice &device) const +{ + return HPPImage(device, *this); +} + +HPPImagePtr HPPImageBuilder::build_unique(HPPDevice &device) const +{ + return std::make_unique(device, *this); +} + HPPImage::HPPImage(HPPDevice &device, const vk::Extent3D &extent, vk::Format format, @@ -55,48 +66,27 @@ HPPImage::HPPImage(HPPDevice &device, vk::ImageCreateFlags flags, uint32_t num_queue_families, const uint32_t *queue_families) : - HPPVulkanResource{nullptr, &device}, - type{find_image_type(extent)}, - extent{extent}, - format{format}, - sample_count{sample_count}, - usage{image_usage}, - array_layer_count{array_layers}, - tiling{tiling} -{ - assert(0 < mip_levels && "HPPImage should have at least one level"); - assert(0 < array_layers && "HPPImage should have at least one layer"); - - subresource.mipLevel = mip_levels; - subresource.arrayLayer = array_layers; - - vk::ImageCreateInfo image_info(flags, type, format, extent, mip_levels, array_layers, sample_count, tiling, image_usage); - - if (num_queue_families != 0) - { - image_info.sharingMode = vk::SharingMode::eConcurrent; - image_info.queueFamilyIndexCount = num_queue_families; - image_info.pQueueFamilyIndices = queue_families; - } - - VmaAllocationCreateInfo memory_info{}; - memory_info.usage = memory_usage; - - if (image_usage & vk::ImageUsageFlagBits::eTransientAttachment) + HPPImage{device, + HPPImageBuilder{extent} + .with_format(format) + .with_mip_levels(mip_levels) + .with_array_layers(array_layers) + .with_sample_count(sample_count) + .with_tiling(tiling) + .with_flags(flags) + .with_usage(image_usage) + .with_queue_families(num_queue_families, queue_families)} +{} + +HPPImage::HPPImage(HPPDevice &device, HPPImageBuilder const &builder) : + HPPAllocated{builder.alloc_create_info, nullptr, &device}, create_info{builder.create_info} +{ + get_handle() = create_image(create_info.operator const VkImageCreateInfo &()); + subresource.arrayLayer = create_info.arrayLayers; + subresource.mipLevel = create_info.mipLevels; + if (!builder.debug_name.empty()) { - memory_info.preferredFlags = VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT; - } - - auto result = vmaCreateImage(device.get_memory_allocator(), - reinterpret_cast(&image_info), - &memory_info, - const_cast(reinterpret_cast(&get_handle())), - &memory, - nullptr); - - if (result != VK_SUCCESS) - { - throw VulkanException{result, "Cannot create HPPImage"}; + set_debug_name(builder.debug_name); } } @@ -106,25 +96,23 @@ HPPImage::HPPImage(HPPDevice &device, vk::Format format, vk::ImageUsageFlags image_usage, vk::SampleCountFlagBits sample_count) : - HPPVulkanResource{handle, &device}, type{find_image_type(extent)}, extent{extent}, format{format}, sample_count{sample_count}, usage{image_usage} + HPPAllocated{handle, &device} { - subresource.mipLevel = 1; - subresource.arrayLayer = 1; + create_info.samples = sample_count; + create_info.format = format; + create_info.extent = extent; + create_info.imageType = find_image_type(extent); + create_info.arrayLayers = 1; + create_info.mipLevels = 1; + subresource.mipLevel = 1; + subresource.arrayLayer = 1; } -HPPImage::HPPImage(HPPImage &&other) : - HPPVulkanResource{std::move(other)}, - memory(std::exchange(other.memory, {})), - type(std::exchange(other.type, {})), - extent(std::exchange(other.extent, {})), - format(std::exchange(other.format, {})), - sample_count(std::exchange(other.sample_count, {})), - usage(std::exchange(other.usage, {})), - tiling(std::exchange(other.tiling, {})), +HPPImage::HPPImage(HPPImage &&other) noexcept : + HPPAllocated{std::move(other)}, + create_info(std::exchange(other.create_info, {})), subresource(std::exchange(other.subresource, {})), - views(std::exchange(other.views, {})), - mapped_data(std::exchange(other.mapped_data, {})), - mapped(std::exchange(other.mapped, {})) + views(std::exchange(other.views, {})) { // Update image views references to this image to avoid dangling pointers for (auto &view : views) @@ -135,70 +123,46 @@ HPPImage::HPPImage(HPPImage &&other) : HPPImage::~HPPImage() { - if (get_handle() && memory) - { - unmap(); - vmaDestroyImage(get_device().get_memory_allocator(), static_cast(get_handle()), memory); - } -} - -VmaAllocation HPPImage::get_memory() const -{ - return memory; + destroy_image(get_handle()); } uint8_t *HPPImage::map() { - if (!mapped_data) - { - if (tiling != vk::ImageTiling::eLinear) - { - LOGW("Mapping image memory that is not linear"); - } - VK_CHECK(vmaMapMemory(get_device().get_memory_allocator(), memory, reinterpret_cast(&mapped_data))); - mapped = true; - } - return mapped_data; -} - -void HPPImage::unmap() -{ - if (mapped) + if (create_info.tiling != vk::ImageTiling::eLinear) { - vmaUnmapMemory(get_device().get_memory_allocator(), memory); - mapped_data = nullptr; - mapped = false; + LOGW("Mapping image memory that is not linear"); } + return Allocated::map(); } vk::ImageType HPPImage::get_type() const { - return type; + return create_info.imageType; } const vk::Extent3D &HPPImage::get_extent() const { - return extent; + return create_info.extent; } vk::Format HPPImage::get_format() const { - return format; + return create_info.format; } vk::SampleCountFlagBits HPPImage::get_sample_count() const { - return sample_count; + return create_info.samples; } vk::ImageUsageFlags HPPImage::get_usage() const { - return usage; + return create_info.usage; } vk::ImageTiling HPPImage::get_tiling() const { - return tiling; + return create_info.tiling; } vk::ImageSubresource HPPImage::get_subresource() const @@ -208,7 +172,7 @@ vk::ImageSubresource HPPImage::get_subresource() const uint32_t HPPImage::get_array_layer_count() const { - return array_layer_count; + return create_info.arrayLayers; } std::unordered_set &HPPImage::get_views() diff --git a/framework/core/hpp_image.h b/framework/core/hpp_image.h index 39277d6a1c..36b6722049 100644 --- a/framework/core/hpp_image.h +++ b/framework/core/hpp_image.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2021-2023, NVIDIA CORPORATION. All rights reserved. +/* Copyright (c) 2021-2024, NVIDIA CORPORATION. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 * @@ -18,8 +18,8 @@ #pragma once #include "core/hpp_vulkan_resource.h" +#include "hpp_allocated.h" #include -#include namespace vkb { @@ -27,8 +27,93 @@ namespace core { class HPPDevice; class HPPImageView; +class HPPImage; +using HPPImagePtr = std::unique_ptr; -class HPPImage : public vkb::core::HPPVulkanResource +struct HPPImageBuilder : public allocated::HPPBuilder +{ + private: + using Parent = allocated::HPPBuilder; + + public: + HPPImageBuilder(vk::Extent3D const &extent) : + // Better reasonable defaults than vk::ImageCreateInfo default ctor + Parent(vk::ImageCreateInfo{{}, vk::ImageType::e2D, vk::Format::eR8G8B8A8Unorm, extent, 1, 1}) + { + } + + HPPImageBuilder(vk::Extent2D const &extent) : + HPPImageBuilder(vk::Extent3D{extent.width, extent.height, 1}) + { + } + + HPPImageBuilder(uint32_t width, uint32_t height = 1, uint32_t depth = 1) : + HPPImageBuilder(vk::Extent3D{width, height, depth}) + { + } + + HPPImageBuilder &with_format(vk::Format format) + { + create_info.format = format; + return *this; + } + + HPPImageBuilder &with_image_type(vk::ImageType type) + { + create_info.imageType = type; + return *this; + } + + HPPImageBuilder &with_array_layers(uint32_t layers) + { + create_info.arrayLayers = layers; + return *this; + } + + HPPImageBuilder &with_mip_levels(uint32_t levels) + { + create_info.mipLevels = levels; + return *this; + } + + HPPImageBuilder &with_sample_count(vk::SampleCountFlagBits sample_count) + { + create_info.samples = sample_count; + return *this; + } + + HPPImageBuilder &with_tiling(vk::ImageTiling tiling) + { + create_info.tiling = tiling; + return *this; + } + + HPPImageBuilder &with_usage(vk::ImageUsageFlags usage) + { + create_info.usage = usage; + return *this; + } + + HPPImageBuilder &with_flags(vk::ImageCreateFlags flags) + { + create_info.flags = flags; + return *this; + } + + HPPImageBuilder &with_implicit_sharing_mode() + { + if (create_info.queueFamilyIndexCount != 0) + { + create_info.sharingMode = vk::SharingMode::eConcurrent; + } + return *this; + } + + HPPImage build(HPPDevice &device) const; + HPPImagePtr build_unique(HPPDevice &device) const; +}; + +class HPPImage : public allocated::HPPAllocated { public: HPPImage(HPPDevice &device, @@ -38,11 +123,12 @@ class HPPImage : public vkb::core::HPPVulkanResource vk::ImageUsageFlags image_usage, vk::SampleCountFlagBits sample_count = vk::SampleCountFlagBits::e1); + //[[deprecated("Use the HPPImageBuilder ctor instead")]] HPPImage(HPPDevice &device, const vk::Extent3D &extent, vk::Format format, vk::ImageUsageFlags image_usage, - VmaMemoryUsage memory_usage, + VmaMemoryUsage memory_usage = VMA_MEMORY_USAGE_AUTO, vk::SampleCountFlagBits sample_count = vk::SampleCountFlagBits::e1, uint32_t mip_levels = 1, uint32_t array_layers = 1, @@ -51,29 +137,25 @@ class HPPImage : public vkb::core::HPPVulkanResource uint32_t num_queue_families = 0, const uint32_t *queue_families = nullptr); + HPPImage(HPPDevice &device, + HPPImageBuilder const &builder); + HPPImage(const HPPImage &) = delete; - HPPImage(HPPImage &&other); + HPPImage(HPPImage &&other) noexcept; - ~HPPImage() override; + ~HPPImage(); HPPImage &operator=(const HPPImage &) = delete; HPPImage &operator=(HPPImage &&) = delete; - VmaAllocation get_memory() const; - /** * @brief Maps vulkan memory to an host visible address * @return Pointer to host visible memory */ uint8_t *map(); - /** - * @brief Unmaps vulkan memory from the host visible address - */ - void unmap(); - vk::ImageType get_type() const; const vk::Extent3D &get_extent() const; vk::Format get_format() const; @@ -85,18 +167,9 @@ class HPPImage : public vkb::core::HPPVulkanResource std::unordered_set &get_views(); private: - VmaAllocation memory = VK_NULL_HANDLE; - vk::ImageType type; - vk::Extent3D extent; - vk::Format format; - vk::ImageUsageFlags usage; - vk::SampleCountFlagBits sample_count; - vk::ImageTiling tiling; + vk::ImageCreateInfo create_info; vk::ImageSubresource subresource; - uint32_t array_layer_count = 0; std::unordered_set views; /// HPPImage views referring to this image - uint8_t *mapped_data = nullptr; - bool mapped = false; /// Whether it was mapped with vmaMapMemory }; } // namespace core } // namespace vkb diff --git a/framework/core/image.cpp b/framework/core/image.cpp index 81878d06cf..68c97d77c8 100644 --- a/framework/core/image.cpp +++ b/framework/core/image.cpp @@ -1,4 +1,4 @@ -/* Copyright (c) 2019-2023, Arm Limited and Contributors +/* Copyright (c) 2019-2024, Arm Limited and Contributors * * SPDX-License-Identifier: Apache-2.0 * @@ -67,6 +67,17 @@ inline VkImageType find_image_type(VkExtent3D extent) namespace core { + +Image ImageBuilder::build(const Device &device) const +{ + return Image(device, *this); +} + +ImagePtr ImageBuilder::build_unique(const Device &device) const +{ + return std::make_unique(device, *this); +} + Image::Image(Device const &device, const VkExtent3D &extent, VkFormat format, @@ -79,88 +90,53 @@ Image::Image(Device const &device, VkImageCreateFlags flags, uint32_t num_queue_families, const uint32_t *queue_families) : - VulkanResource{VK_NULL_HANDLE, &device}, - type{find_image_type(extent)}, - extent{extent}, - format{format}, - sample_count{sample_count}, - usage{image_usage}, - array_layer_count{array_layers}, - tiling{tiling} -{ - assert(mip_levels > 0 && "Image should have at least one level"); - assert(array_layers > 0 && "Image should have at least one layer"); - - subresource.mipLevel = mip_levels; - subresource.arrayLayer = array_layers; - - VkImageCreateInfo image_info{VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO}; - image_info.flags = flags; - image_info.imageType = type; - image_info.format = format; - image_info.extent = extent; - image_info.mipLevels = mip_levels; - image_info.arrayLayers = array_layers; - image_info.samples = sample_count; - image_info.tiling = tiling; - image_info.usage = image_usage; - - if (num_queue_families != 0) + // Pass through to the ImageBuilder ctor + Image(device, + ImageBuilder(extent) + .with_format(format) + .with_image_type(find_image_type(extent)) + .with_usage(image_usage) + .with_mip_levels(mip_levels) + .with_array_layers(array_layers) + .with_tiling(tiling) + .with_flags(flags) + .with_vma_usage(memory_usage) + .with_sample_count(sample_count) + .with_queue_families(num_queue_families, queue_families) + .with_implicit_sharing_mode()) +{ +} + +Image::Image(Device const &device, ImageBuilder const &builder) : + Allocated{builder.alloc_create_info, VK_NULL_HANDLE, &device}, create_info(builder.create_info) +{ + handle = create_image(create_info); + subresource.arrayLayer = create_info.arrayLayers; + subresource.mipLevel = create_info.mipLevels; + if (!builder.debug_name.empty()) { - image_info.sharingMode = VK_SHARING_MODE_CONCURRENT; - image_info.queueFamilyIndexCount = num_queue_families; - image_info.pQueueFamilyIndices = queue_families; - } - - VmaAllocationCreateInfo memory_info{}; - memory_info.usage = memory_usage; - - if (image_usage & VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT) - { - memory_info.preferredFlags = VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT; - } - - auto result = vmaCreateImage(device.get_memory_allocator(), - &image_info, &memory_info, - &handle, &memory, - nullptr); - - if (result != VK_SUCCESS) - { - throw VulkanException{result, "Cannot create Image"}; + set_debug_name(builder.debug_name); } } Image::Image(Device const &device, VkImage handle, const VkExtent3D &extent, VkFormat format, VkImageUsageFlags image_usage, VkSampleCountFlagBits sample_count) : - VulkanResource{handle, &device}, - type{find_image_type(extent)}, - extent{extent}, - format{format}, - sample_count{sample_count}, - usage{image_usage} -{ - subresource.mipLevel = 1; - subresource.arrayLayer = 1; -} - -Image::Image(Image &&other) : - VulkanResource{std::move(other)}, - memory{other.memory}, - type{other.type}, - extent{other.extent}, - format{other.format}, - sample_count{other.sample_count}, - usage{other.usage}, - tiling{other.tiling}, - subresource{other.subresource}, - views(std::exchange(other.views, {})), - mapped_data{other.mapped_data}, - mapped{other.mapped} -{ - other.memory = VK_NULL_HANDLE; - other.mapped_data = nullptr; - other.mapped = false; + Allocated{handle, &device} +{ + create_info.extent = extent; + create_info.imageType = find_image_type(extent); + create_info.format = format; + create_info.samples = sample_count; + create_info.usage = image_usage; + subresource.arrayLayer = create_info.arrayLayers = 1; + subresource.mipLevel = create_info.mipLevels = 1; +} +Image::Image(Image &&other) noexcept : + Allocated{std::move(other)}, + create_info{std::exchange(other.create_info, {})}, + subresource{std::exchange(other.subresource, {})}, + views(std::exchange(other.views, {})) +{ // Update image views references to this image to avoid dangling pointers for (auto &view : views) { @@ -170,80 +146,47 @@ Image::Image(Image &&other) : Image::~Image() { - if (handle != VK_NULL_HANDLE && memory != VK_NULL_HANDLE) - { - unmap(); - vmaDestroyImage(device->get_memory_allocator(), handle, memory); - } -} - -VmaAllocation Image::get_memory() const -{ - return memory; -} - -uint8_t *Image::map() -{ - if (!mapped_data) - { - if (tiling != VK_IMAGE_TILING_LINEAR) - { - LOGW("Mapping image memory that is not linear"); - } - VK_CHECK(vmaMapMemory(device->get_memory_allocator(), memory, reinterpret_cast(&mapped_data))); - mapped = true; - } - return mapped_data; -} - -void Image::unmap() -{ - if (mapped) - { - vmaUnmapMemory(device->get_memory_allocator(), memory); - mapped_data = nullptr; - mapped = false; - } + destroy_image(get_handle()); } VkImageType Image::get_type() const { - return type; + return create_info.imageType; } const VkExtent3D &Image::get_extent() const { - return extent; + return create_info.extent; } VkFormat Image::get_format() const { - return format; + return create_info.format; } VkSampleCountFlagBits Image::get_sample_count() const { - return sample_count; + return create_info.samples; } VkImageUsageFlags Image::get_usage() const { - return usage; + return create_info.usage; } VkImageTiling Image::get_tiling() const { - return tiling; + return create_info.tiling; } -VkImageSubresource Image::get_subresource() const +const VkImageSubresource &Image::get_subresource() const { return subresource; } uint32_t Image::get_array_layer_count() const { - return array_layer_count; + return create_info.arrayLayers; } std::unordered_set &Image::get_views() diff --git a/framework/core/image.h b/framework/core/image.h index 53af4ce668..fe18b7b367 100644 --- a/framework/core/image.h +++ b/framework/core/image.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2019-2021, Arm Limited and Contributors +/* Copyright (c) 2019-2024, Arm Limited and Contributors * * SPDX-License-Identifier: Apache-2.0 * @@ -21,6 +21,7 @@ #include "common/helpers.h" #include "common/vk_common.h" +#include "core/allocated.h" #include "core/vulkan_resource.h" namespace vkb @@ -29,33 +30,132 @@ class Device; namespace core { + +class Image; +using ImagePtr = std::unique_ptr; + +struct ImageBuilder : public allocated::Builder +{ + private: + using Parent = allocated::Builder; + + public: + ImageBuilder(VkExtent3D const &extent) : + Parent(VkImageCreateInfo{VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, nullptr}) + { + create_info.extent = extent; + create_info.arrayLayers = 1; + create_info.mipLevels = 1; + create_info.imageType = VK_IMAGE_TYPE_2D; + create_info.format = VK_FORMAT_R8G8B8A8_UNORM; + create_info.samples = VK_SAMPLE_COUNT_1_BIT; + } + + ImageBuilder(uint32_t width, uint32_t height = 1, uint32_t depth = 1) : + ImageBuilder(VkExtent3D{width, height, depth}) + { + } + + ImageBuilder &with_format(VkFormat format) + { + create_info.format = format; + return *this; + } + + ImageBuilder &with_usage(VkImageUsageFlags usage) + { + create_info.usage = usage; + return *this; + } + + ImageBuilder &with_sharing_mode(VkSharingMode sharing_mode) + { + create_info.sharingMode = sharing_mode; + return *this; + } + + ImageBuilder &with_flags(VkImageCreateFlags flags) + { + create_info.flags = flags; + return *this; + } + + ImageBuilder &with_image_type(VkImageType type) + { + create_info.imageType = type; + return *this; + } + + ImageBuilder &with_array_layers(uint32_t layers) + { + create_info.arrayLayers = layers; + return *this; + } + + ImageBuilder &with_mip_levels(uint32_t levels) + { + create_info.mipLevels = levels; + return *this; + } + + ImageBuilder &with_sample_count(VkSampleCountFlagBits sample_count) + { + create_info.samples = sample_count; + return *this; + } + + ImageBuilder &with_tiling(VkImageTiling tiling) + { + create_info.tiling = tiling; + return *this; + } + + ImageBuilder &with_implicit_sharing_mode() + { + if (create_info.queueFamilyIndexCount != 0) + { + create_info.sharingMode = VK_SHARING_MODE_CONCURRENT; + } + return *this; + } + + Image build(const Device &device) const; + ImagePtr build_unique(const Device &device) const; +}; + class ImageView; -class Image : public VulkanResource +class Image : public allocated::Allocated { + VkImageCreateInfo create_info; + public: - Image(Device const & device, + Image(Device const &device, VkImage handle, - const VkExtent3D & extent, + const VkExtent3D &extent, VkFormat format, VkImageUsageFlags image_usage, VkSampleCountFlagBits sample_count = VK_SAMPLE_COUNT_1_BIT); - Image(Device const & device, - const VkExtent3D & extent, - VkFormat format, - VkImageUsageFlags image_usage, - VmaMemoryUsage memory_usage, - VkSampleCountFlagBits sample_count = VK_SAMPLE_COUNT_1_BIT, - uint32_t mip_levels = 1, - uint32_t array_layers = 1, - VkImageTiling tiling = VK_IMAGE_TILING_OPTIMAL, - VkImageCreateFlags flags = 0, - uint32_t num_queue_families = 0, - const uint32_t * queue_families = nullptr); + // [[deprecated("Use the ImageBuilder ctor instead")]] + Image( + Device const &device, + const VkExtent3D &extent, + VkFormat format, + VkImageUsageFlags image_usage, + VmaMemoryUsage memory_usage = VMA_MEMORY_USAGE_AUTO, + VkSampleCountFlagBits sample_count = VK_SAMPLE_COUNT_1_BIT, + uint32_t mip_levels = 1, + uint32_t array_layers = 1, + VkImageTiling tiling = VK_IMAGE_TILING_OPTIMAL, + VkImageCreateFlags flags = 0, + uint32_t num_queue_families = 0, + const uint32_t *queue_families = nullptr); + + Image(Device const &device, ImageBuilder const &builder); Image(const Image &) = delete; - Image(Image &&other); + Image(Image &&other) noexcept; ~Image() override; @@ -63,19 +163,6 @@ class Image : public VulkanResource Image &operator=(Image &&) = delete; - VmaAllocation get_memory() const; - - /** - * @brief Maps vulkan memory to an host visible address - * @return Pointer to host visible memory - */ - uint8_t *map(); - - /** - * @brief Unmaps vulkan memory from the host visible address - */ - void unmap(); - VkImageType get_type() const; const VkExtent3D &get_extent() const; @@ -88,38 +175,16 @@ class Image : public VulkanResource VkImageTiling get_tiling() const; - VkImageSubresource get_subresource() const; + const VkImageSubresource &get_subresource() const; uint32_t get_array_layer_count() const; std::unordered_set &get_views(); private: - VmaAllocation memory{VK_NULL_HANDLE}; - - VkImageType type{}; - - VkExtent3D extent{}; - - VkFormat format{}; - - VkImageUsageFlags usage{}; - - VkSampleCountFlagBits sample_count{}; - - VkImageTiling tiling{}; - - VkImageSubresource subresource{}; - - uint32_t array_layer_count{0}; - /// Image views referring to this image std::unordered_set views; - - uint8_t *mapped_data{nullptr}; - - /// Whether it was mapped with vmaMapMemory - bool mapped{false}; + VkImageSubresource subresource{}; }; } // namespace core } // namespace vkb diff --git a/framework/core/image_view.h b/framework/core/image_view.h index e6eb593eda..9b5a10e9fe 100644 --- a/framework/core/image_view.h +++ b/framework/core/image_view.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2019-2021, Arm Limited and Contributors +/* Copyright (c) 2019-2024, Arm Limited and Contributors * * SPDX-License-Identifier: Apache-2.0 * @@ -26,7 +26,7 @@ namespace vkb { namespace core { -class ImageView : public VulkanResource +class ImageView : public VulkanResource { public: ImageView(Image &image, VkImageViewType view_type, VkFormat format = VK_FORMAT_UNDEFINED, diff --git a/framework/core/render_pass.h b/framework/core/render_pass.h index f83290d50d..8c60321acb 100644 --- a/framework/core/render_pass.h +++ b/framework/core/render_pass.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2019-2021, Arm Limited and Contributors +/* Copyright (c) 2019-2024, Arm Limited and Contributors * * SPDX-License-Identifier: Apache-2.0 * @@ -43,13 +43,13 @@ struct SubpassInfo std::string debug_name; }; -class RenderPass : public core::VulkanResource +class RenderPass : public core::VulkanResource { public: - RenderPass(Device & device, - const std::vector & attachments, + RenderPass(Device &device, + const std::vector &attachments, const std::vector &load_store_infos, - const std::vector & subpasses); + const std::vector &subpasses); RenderPass(const RenderPass &) = delete; diff --git a/framework/core/sampler.h b/framework/core/sampler.h index e79c253954..66a099d64c 100644 --- a/framework/core/sampler.h +++ b/framework/core/sampler.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2019-2021, Arm Limited and Contributors +/* Copyright (c) 2019-2024, Arm Limited and Contributors * * SPDX-License-Identifier: Apache-2.0 * @@ -30,7 +30,7 @@ namespace core /** * @brief Represents a Vulkan Sampler */ -class Sampler : public VulkanResource +class Sampler : public VulkanResource { public: /** diff --git a/framework/core/vulkan_resource.h b/framework/core/vulkan_resource.h index e20f4b6a43..8d8b06a632 100644 --- a/framework/core/vulkan_resource.h +++ b/framework/core/vulkan_resource.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2021, Arm Limited and Contributors +/* Copyright (c) 2021-2024, Arm Limited and Contributors * * SPDX-License-Identifier: Apache-2.0 * @@ -33,11 +33,60 @@ namespace detail void set_debug_name(const Device *device, VkObjectType object_type, uint64_t handle, const char *debug_name); } +template +constexpr VkObjectType get_object_type(const HandleType &handle) +{ + throw std::runtime_error("Unknown handle type"); + return static_cast(-1); +} + +template <> +constexpr VkObjectType get_object_type(const VkImage &handle) +{ + return VK_OBJECT_TYPE_IMAGE; +} + +template <> +constexpr VkObjectType get_object_type(const VkImageView &handle) +{ + return VK_OBJECT_TYPE_IMAGE_VIEW; +} + +template <> +constexpr VkObjectType get_object_type(const VkRenderPass &handle) +{ + return VK_OBJECT_TYPE_RENDER_PASS; +} + +template <> +constexpr VkObjectType get_object_type(const VkSampler &handle) +{ + return VK_OBJECT_TYPE_SAMPLER; +} + +template <> +constexpr VkObjectType get_object_type(const VkBuffer &handle) +{ + return VK_OBJECT_TYPE_BUFFER; +} + +template <> +constexpr VkObjectType get_object_type(const VkDevice &handle) +{ + return VK_OBJECT_TYPE_DEVICE; +} + +template <> +constexpr VkObjectType get_object_type(const VkCommandBuffer &handle) +{ + return VK_OBJECT_TYPE_COMMAND_BUFFER; +} + /// Inherit this for any Vulkan object with a handle of type `THandle`. /// /// This allows the derived class to store a Vulkan handle, and also a pointer to the parent Device. /// It also allow for adding debug data to any Vulkan object. -template +template class VulkanResource { public: @@ -46,7 +95,7 @@ class VulkanResource { } - VulkanResource(const VulkanResource &) = delete; + VulkanResource(const VulkanResource &) = delete; VulkanResource &operator=(const VulkanResource &) = delete; VulkanResource(VulkanResource &&other) : @@ -70,9 +119,9 @@ class VulkanResource virtual ~VulkanResource() = default; - inline VkObjectType get_object_type() const + constexpr VkObjectType get_object_type() const { - return OBJECT_TYPE; + return vkb::core::get_object_type(handle); } inline Device &get_device() const @@ -104,12 +153,12 @@ class VulkanResource inline void set_debug_name(const std::string &name) { debug_name = name; - detail::set_debug_name(device, OBJECT_TYPE, get_handle_u64(), debug_name.c_str()); + detail::set_debug_name(device, get_object_type(), get_handle_u64(), debug_name.c_str()); } protected: THandle handle; - Device * device; + Device *device; std::string debug_name; };