diff options
| author | 2023-06-27 11:21:36 -0400 | |
|---|---|---|
| committer | 2023-06-27 11:21:36 -0400 | |
| commit | c6959449d1e28f16d6eaf3a215f2bce5fab5ed0f (patch) | |
| tree | 1ceac7222976e40bb12c3919cd3412d36b7ec93b /src/video_core/vulkan_common | |
| parent | Merge pull request #10495 from bm01/master (diff) | |
| parent | externals: Use cmake subdirectory (diff) | |
| download | yuzu-c6959449d1e28f16d6eaf3a215f2bce5fab5ed0f.tar.gz yuzu-c6959449d1e28f16d6eaf3a215f2bce5fab5ed0f.tar.xz yuzu-c6959449d1e28f16d6eaf3a215f2bce5fab5ed0f.zip | |
Merge pull request #10473 from GPUCode/vma
Use vulkan memory allocator
Diffstat (limited to 'src/video_core/vulkan_common')
| -rw-r--r-- | src/video_core/vulkan_common/vulkan_device.cpp | 26 | ||||
| -rw-r--r-- | src/video_core/vulkan_common/vulkan_device.h | 8 | ||||
| -rw-r--r-- | src/video_core/vulkan_common/vulkan_memory_allocator.cpp | 173 | ||||
| -rw-r--r-- | src/video_core/vulkan_common/vulkan_memory_allocator.h | 28 | ||||
| -rw-r--r-- | src/video_core/vulkan_common/vulkan_wrapper.cpp | 50 | ||||
| -rw-r--r-- | src/video_core/vulkan_common/vulkan_wrapper.h | 161 |
6 files changed, 299 insertions, 147 deletions
diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index b11abe311..e4ca65b58 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp | |||
| @@ -22,6 +22,8 @@ | |||
| 22 | #include <adrenotools/bcenabler.h> | 22 | #include <adrenotools/bcenabler.h> |
| 23 | #endif | 23 | #endif |
| 24 | 24 | ||
| 25 | #include <vk_mem_alloc.h> | ||
| 26 | |||
| 25 | namespace Vulkan { | 27 | namespace Vulkan { |
| 26 | using namespace Common::Literals; | 28 | using namespace Common::Literals; |
| 27 | namespace { | 29 | namespace { |
| @@ -596,9 +598,31 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR | |||
| 596 | 598 | ||
| 597 | graphics_queue = logical.GetQueue(graphics_family); | 599 | graphics_queue = logical.GetQueue(graphics_family); |
| 598 | present_queue = logical.GetQueue(present_family); | 600 | present_queue = logical.GetQueue(present_family); |
| 601 | |||
| 602 | VmaVulkanFunctions functions{}; | ||
| 603 | functions.vkGetInstanceProcAddr = dld.vkGetInstanceProcAddr; | ||
| 604 | functions.vkGetDeviceProcAddr = dld.vkGetDeviceProcAddr; | ||
| 605 | |||
| 606 | const VmaAllocatorCreateInfo allocator_info = { | ||
| 607 | .flags = VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT, | ||
| 608 | .physicalDevice = physical, | ||
| 609 | .device = *logical, | ||
| 610 | .preferredLargeHeapBlockSize = 0, | ||
| 611 | .pAllocationCallbacks = nullptr, | ||
| 612 | .pDeviceMemoryCallbacks = nullptr, | ||
| 613 | .pHeapSizeLimit = nullptr, | ||
| 614 | .pVulkanFunctions = &functions, | ||
| 615 | .instance = instance, | ||
| 616 | .vulkanApiVersion = VK_API_VERSION_1_1, | ||
| 617 | .pTypeExternalMemoryHandleTypes = nullptr, | ||
| 618 | }; | ||
| 619 | |||
| 620 | vk::Check(vmaCreateAllocator(&allocator_info, &allocator)); | ||
| 599 | } | 621 | } |
| 600 | 622 | ||
| 601 | Device::~Device() = default; | 623 | Device::~Device() { |
| 624 | vmaDestroyAllocator(allocator); | ||
| 625 | } | ||
| 602 | 626 | ||
| 603 | VkFormat Device::GetSupportedFormat(VkFormat wanted_format, VkFormatFeatureFlags wanted_usage, | 627 | VkFormat Device::GetSupportedFormat(VkFormat wanted_format, VkFormatFeatureFlags wanted_usage, |
| 604 | FormatType format_type) const { | 628 | FormatType format_type) const { |
diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index 0b634a876..b84af3dfb 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h | |||
| @@ -14,6 +14,8 @@ | |||
| 14 | #include "common/settings.h" | 14 | #include "common/settings.h" |
| 15 | #include "video_core/vulkan_common/vulkan_wrapper.h" | 15 | #include "video_core/vulkan_common/vulkan_wrapper.h" |
| 16 | 16 | ||
| 17 | VK_DEFINE_HANDLE(VmaAllocator) | ||
| 18 | |||
| 17 | // Define all features which may be used by the implementation here. | 19 | // Define all features which may be used by the implementation here. |
| 18 | // Vulkan version in the macro describes the minimum version required for feature availability. | 20 | // Vulkan version in the macro describes the minimum version required for feature availability. |
| 19 | // If the Vulkan version is lower than the required version, the named extension is required. | 21 | // If the Vulkan version is lower than the required version, the named extension is required. |
| @@ -199,6 +201,11 @@ public: | |||
| 199 | return dld; | 201 | return dld; |
| 200 | } | 202 | } |
| 201 | 203 | ||
| 204 | /// Returns the VMA allocator. | ||
| 205 | VmaAllocator GetAllocator() const { | ||
| 206 | return allocator; | ||
| 207 | } | ||
| 208 | |||
| 202 | /// Returns the logical device. | 209 | /// Returns the logical device. |
| 203 | const vk::Device& GetLogical() const { | 210 | const vk::Device& GetLogical() const { |
| 204 | return logical; | 211 | return logical; |
| @@ -630,6 +637,7 @@ private: | |||
| 630 | 637 | ||
| 631 | private: | 638 | private: |
| 632 | VkInstance instance; ///< Vulkan instance. | 639 | VkInstance instance; ///< Vulkan instance. |
| 640 | VmaAllocator allocator; ///< VMA allocator. | ||
| 633 | vk::DeviceDispatch dld; ///< Device function pointers. | 641 | vk::DeviceDispatch dld; ///< Device function pointers. |
| 634 | vk::PhysicalDevice physical; ///< Physical device. | 642 | vk::PhysicalDevice physical; ///< Physical device. |
| 635 | vk::Device logical; ///< Logical device. | 643 | vk::Device logical; ///< Logical device. |
diff --git a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp index e28a556f8..a2ef0efa4 100644 --- a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp +++ b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp | |||
| @@ -6,8 +6,6 @@ | |||
| 6 | #include <optional> | 6 | #include <optional> |
| 7 | #include <vector> | 7 | #include <vector> |
| 8 | 8 | ||
| 9 | #include <glad/glad.h> | ||
| 10 | |||
| 11 | #include "common/alignment.h" | 9 | #include "common/alignment.h" |
| 12 | #include "common/assert.h" | 10 | #include "common/assert.h" |
| 13 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| @@ -17,6 +15,8 @@ | |||
| 17 | #include "video_core/vulkan_common/vulkan_memory_allocator.h" | 15 | #include "video_core/vulkan_common/vulkan_memory_allocator.h" |
| 18 | #include "video_core/vulkan_common/vulkan_wrapper.h" | 16 | #include "video_core/vulkan_common/vulkan_wrapper.h" |
| 19 | 17 | ||
| 18 | #include <vk_mem_alloc.h> | ||
| 19 | |||
| 20 | namespace Vulkan { | 20 | namespace Vulkan { |
| 21 | namespace { | 21 | namespace { |
| 22 | struct Range { | 22 | struct Range { |
| @@ -49,22 +49,45 @@ struct Range { | |||
| 49 | case MemoryUsage::Download: | 49 | case MemoryUsage::Download: |
| 50 | return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | | 50 | return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | |
| 51 | VK_MEMORY_PROPERTY_HOST_CACHED_BIT; | 51 | VK_MEMORY_PROPERTY_HOST_CACHED_BIT; |
| 52 | case MemoryUsage::Stream: | ||
| 53 | return VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | | ||
| 54 | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; | ||
| 52 | } | 55 | } |
| 53 | ASSERT_MSG(false, "Invalid memory usage={}", usage); | 56 | ASSERT_MSG(false, "Invalid memory usage={}", usage); |
| 54 | return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; | 57 | return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; |
| 55 | } | 58 | } |
| 56 | 59 | ||
| 57 | constexpr VkExportMemoryAllocateInfo EXPORT_ALLOCATE_INFO{ | 60 | [[nodiscard]] VkMemoryPropertyFlags MemoryUsagePreferedVmaFlags(MemoryUsage usage) { |
| 58 | .sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO, | 61 | return usage != MemoryUsage::DeviceLocal ? VK_MEMORY_PROPERTY_HOST_COHERENT_BIT |
| 59 | .pNext = nullptr, | 62 | : VkMemoryPropertyFlagBits{}; |
| 60 | #ifdef _WIN32 | 63 | } |
| 61 | .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT, | 64 | |
| 62 | #elif __unix__ | 65 | [[nodiscard]] VmaAllocationCreateFlags MemoryUsageVmaFlags(MemoryUsage usage) { |
| 63 | .handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT, | 66 | switch (usage) { |
| 64 | #else | 67 | case MemoryUsage::Upload: |
| 65 | .handleTypes = 0, | 68 | case MemoryUsage::Stream: |
| 66 | #endif | 69 | return VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT; |
| 67 | }; | 70 | case MemoryUsage::Download: |
| 71 | return VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT; | ||
| 72 | case MemoryUsage::DeviceLocal: | ||
| 73 | return VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | | ||
| 74 | VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT; | ||
| 75 | } | ||
| 76 | return {}; | ||
| 77 | } | ||
| 78 | |||
| 79 | [[nodiscard]] VmaMemoryUsage MemoryUsageVma(MemoryUsage usage) { | ||
| 80 | switch (usage) { | ||
| 81 | case MemoryUsage::DeviceLocal: | ||
| 82 | case MemoryUsage::Stream: | ||
| 83 | return VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE; | ||
| 84 | case MemoryUsage::Upload: | ||
| 85 | case MemoryUsage::Download: | ||
| 86 | return VMA_MEMORY_USAGE_AUTO_PREFER_HOST; | ||
| 87 | } | ||
| 88 | return VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE; | ||
| 89 | } | ||
| 90 | |||
| 68 | } // Anonymous namespace | 91 | } // Anonymous namespace |
| 69 | 92 | ||
| 70 | class MemoryAllocation { | 93 | class MemoryAllocation { |
| @@ -74,14 +97,6 @@ public: | |||
| 74 | : allocator{allocator_}, memory{std::move(memory_)}, allocation_size{allocation_size_}, | 97 | : allocator{allocator_}, memory{std::move(memory_)}, allocation_size{allocation_size_}, |
| 75 | property_flags{properties}, shifted_memory_type{1U << type} {} | 98 | property_flags{properties}, shifted_memory_type{1U << type} {} |
| 76 | 99 | ||
| 77 | #if defined(_WIN32) || defined(__unix__) | ||
| 78 | ~MemoryAllocation() { | ||
| 79 | if (owning_opengl_handle != 0) { | ||
| 80 | glDeleteMemoryObjectsEXT(1, &owning_opengl_handle); | ||
| 81 | } | ||
| 82 | } | ||
| 83 | #endif | ||
| 84 | |||
| 85 | MemoryAllocation& operator=(const MemoryAllocation&) = delete; | 100 | MemoryAllocation& operator=(const MemoryAllocation&) = delete; |
| 86 | MemoryAllocation(const MemoryAllocation&) = delete; | 101 | MemoryAllocation(const MemoryAllocation&) = delete; |
| 87 | 102 | ||
| @@ -120,31 +135,6 @@ public: | |||
| 120 | return memory_mapped_span; | 135 | return memory_mapped_span; |
| 121 | } | 136 | } |
| 122 | 137 | ||
| 123 | #ifdef _WIN32 | ||
| 124 | [[nodiscard]] u32 ExportOpenGLHandle() { | ||
| 125 | if (!owning_opengl_handle) { | ||
| 126 | glCreateMemoryObjectsEXT(1, &owning_opengl_handle); | ||
| 127 | glImportMemoryWin32HandleEXT(owning_opengl_handle, allocation_size, | ||
| 128 | GL_HANDLE_TYPE_OPAQUE_WIN32_EXT, | ||
| 129 | memory.GetMemoryWin32HandleKHR()); | ||
| 130 | } | ||
| 131 | return owning_opengl_handle; | ||
| 132 | } | ||
| 133 | #elif __unix__ | ||
| 134 | [[nodiscard]] u32 ExportOpenGLHandle() { | ||
| 135 | if (!owning_opengl_handle) { | ||
| 136 | glCreateMemoryObjectsEXT(1, &owning_opengl_handle); | ||
| 137 | glImportMemoryFdEXT(owning_opengl_handle, allocation_size, GL_HANDLE_TYPE_OPAQUE_FD_EXT, | ||
| 138 | memory.GetMemoryFdKHR()); | ||
| 139 | } | ||
| 140 | return owning_opengl_handle; | ||
| 141 | } | ||
| 142 | #else | ||
| 143 | [[nodiscard]] u32 ExportOpenGLHandle() { | ||
| 144 | return 0; | ||
| 145 | } | ||
| 146 | #endif | ||
| 147 | |||
| 148 | /// Returns whether this allocation is compatible with the arguments. | 138 | /// Returns whether this allocation is compatible with the arguments. |
| 149 | [[nodiscard]] bool IsCompatible(VkMemoryPropertyFlags flags, u32 type_mask) const { | 139 | [[nodiscard]] bool IsCompatible(VkMemoryPropertyFlags flags, u32 type_mask) const { |
| 150 | return (flags & property_flags) == flags && (type_mask & shifted_memory_type) != 0; | 140 | return (flags & property_flags) == flags && (type_mask & shifted_memory_type) != 0; |
| @@ -182,9 +172,6 @@ private: | |||
| 182 | const u32 shifted_memory_type; ///< Shifted Vulkan memory type. | 172 | const u32 shifted_memory_type; ///< Shifted Vulkan memory type. |
| 183 | std::vector<Range> commits; ///< All commit ranges done from this allocation. | 173 | std::vector<Range> commits; ///< All commit ranges done from this allocation. |
| 184 | std::span<u8> memory_mapped_span; ///< Memory mapped span. Empty if not queried before. | 174 | std::span<u8> memory_mapped_span; ///< Memory mapped span. Empty if not queried before. |
| 185 | #if defined(_WIN32) || defined(__unix__) | ||
| 186 | u32 owning_opengl_handle{}; ///< Owning OpenGL memory object handle. | ||
| 187 | #endif | ||
| 188 | }; | 175 | }; |
| 189 | 176 | ||
| 190 | MemoryCommit::MemoryCommit(MemoryAllocation* allocation_, VkDeviceMemory memory_, u64 begin_, | 177 | MemoryCommit::MemoryCommit(MemoryAllocation* allocation_, VkDeviceMemory memory_, u64 begin_, |
| @@ -216,24 +203,70 @@ std::span<u8> MemoryCommit::Map() { | |||
| 216 | return span; | 203 | return span; |
| 217 | } | 204 | } |
| 218 | 205 | ||
| 219 | u32 MemoryCommit::ExportOpenGLHandle() const { | ||
| 220 | return allocation->ExportOpenGLHandle(); | ||
| 221 | } | ||
| 222 | |||
| 223 | void MemoryCommit::Release() { | 206 | void MemoryCommit::Release() { |
| 224 | if (allocation) { | 207 | if (allocation) { |
| 225 | allocation->Free(begin); | 208 | allocation->Free(begin); |
| 226 | } | 209 | } |
| 227 | } | 210 | } |
| 228 | 211 | ||
| 229 | MemoryAllocator::MemoryAllocator(const Device& device_, bool export_allocations_) | 212 | MemoryAllocator::MemoryAllocator(const Device& device_) |
| 230 | : device{device_}, properties{device_.GetPhysical().GetMemoryProperties().memoryProperties}, | 213 | : device{device_}, allocator{device.GetAllocator()}, |
| 231 | export_allocations{export_allocations_}, | 214 | properties{device_.GetPhysical().GetMemoryProperties().memoryProperties}, |
| 232 | buffer_image_granularity{ | 215 | buffer_image_granularity{ |
| 233 | device_.GetPhysical().GetProperties().limits.bufferImageGranularity} {} | 216 | device_.GetPhysical().GetProperties().limits.bufferImageGranularity} {} |
| 234 | 217 | ||
| 235 | MemoryAllocator::~MemoryAllocator() = default; | 218 | MemoryAllocator::~MemoryAllocator() = default; |
| 236 | 219 | ||
| 220 | vk::Image MemoryAllocator::CreateImage(const VkImageCreateInfo& ci) const { | ||
| 221 | const VmaAllocationCreateInfo alloc_ci = { | ||
| 222 | .flags = VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT, | ||
| 223 | .usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE, | ||
| 224 | .requiredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, | ||
| 225 | .preferredFlags = 0, | ||
| 226 | .memoryTypeBits = 0, | ||
| 227 | .pool = VK_NULL_HANDLE, | ||
| 228 | .pUserData = nullptr, | ||
| 229 | .priority = 0.f, | ||
| 230 | }; | ||
| 231 | |||
| 232 | VkImage handle{}; | ||
| 233 | VmaAllocation allocation{}; | ||
| 234 | |||
| 235 | vk::Check(vmaCreateImage(allocator, &ci, &alloc_ci, &handle, &allocation, nullptr)); | ||
| 236 | |||
| 237 | return vk::Image(handle, *device.GetLogical(), allocator, allocation, | ||
| 238 | device.GetDispatchLoader()); | ||
| 239 | } | ||
| 240 | |||
| 241 | vk::Buffer MemoryAllocator::CreateBuffer(const VkBufferCreateInfo& ci, MemoryUsage usage) const { | ||
| 242 | const VmaAllocationCreateInfo alloc_ci = { | ||
| 243 | .flags = VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT | | ||
| 244 | MemoryUsageVmaFlags(usage), | ||
| 245 | .usage = MemoryUsageVma(usage), | ||
| 246 | .requiredFlags = 0, | ||
| 247 | .preferredFlags = MemoryUsagePreferedVmaFlags(usage), | ||
| 248 | .memoryTypeBits = 0, | ||
| 249 | .pool = VK_NULL_HANDLE, | ||
| 250 | .pUserData = nullptr, | ||
| 251 | .priority = 0.f, | ||
| 252 | }; | ||
| 253 | |||
| 254 | VkBuffer handle{}; | ||
| 255 | VmaAllocationInfo alloc_info{}; | ||
| 256 | VmaAllocation allocation{}; | ||
| 257 | VkMemoryPropertyFlags property_flags{}; | ||
| 258 | |||
| 259 | vk::Check(vmaCreateBuffer(allocator, &ci, &alloc_ci, &handle, &allocation, &alloc_info)); | ||
| 260 | vmaGetAllocationMemoryProperties(allocator, allocation, &property_flags); | ||
| 261 | |||
| 262 | u8* data = reinterpret_cast<u8*>(alloc_info.pMappedData); | ||
| 263 | const std::span<u8> mapped_data = data ? std::span<u8>{data, ci.size} : std::span<u8>{}; | ||
| 264 | const bool is_coherent = property_flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; | ||
| 265 | |||
| 266 | return vk::Buffer(handle, *device.GetLogical(), allocator, allocation, mapped_data, is_coherent, | ||
| 267 | device.GetDispatchLoader()); | ||
| 268 | } | ||
| 269 | |||
| 237 | MemoryCommit MemoryAllocator::Commit(const VkMemoryRequirements& requirements, MemoryUsage usage) { | 270 | MemoryCommit MemoryAllocator::Commit(const VkMemoryRequirements& requirements, MemoryUsage usage) { |
| 238 | // Find the fastest memory flags we can afford with the current requirements | 271 | // Find the fastest memory flags we can afford with the current requirements |
| 239 | const u32 type_mask = requirements.memoryTypeBits; | 272 | const u32 type_mask = requirements.memoryTypeBits; |
| @@ -253,25 +286,11 @@ MemoryCommit MemoryAllocator::Commit(const VkMemoryRequirements& requirements, M | |||
| 253 | return TryCommit(requirements, flags).value(); | 286 | return TryCommit(requirements, flags).value(); |
| 254 | } | 287 | } |
| 255 | 288 | ||
| 256 | MemoryCommit MemoryAllocator::Commit(const vk::Buffer& buffer, MemoryUsage usage) { | ||
| 257 | auto commit = Commit(device.GetLogical().GetBufferMemoryRequirements(*buffer), usage); | ||
| 258 | buffer.BindMemory(commit.Memory(), commit.Offset()); | ||
| 259 | return commit; | ||
| 260 | } | ||
| 261 | |||
| 262 | MemoryCommit MemoryAllocator::Commit(const vk::Image& image, MemoryUsage usage) { | ||
| 263 | VkMemoryRequirements requirements = device.GetLogical().GetImageMemoryRequirements(*image); | ||
| 264 | requirements.size = Common::AlignUp(requirements.size, buffer_image_granularity); | ||
| 265 | auto commit = Commit(requirements, usage); | ||
| 266 | image.BindMemory(commit.Memory(), commit.Offset()); | ||
| 267 | return commit; | ||
| 268 | } | ||
| 269 | |||
| 270 | bool MemoryAllocator::TryAllocMemory(VkMemoryPropertyFlags flags, u32 type_mask, u64 size) { | 289 | bool MemoryAllocator::TryAllocMemory(VkMemoryPropertyFlags flags, u32 type_mask, u64 size) { |
| 271 | const u32 type = FindType(flags, type_mask).value(); | 290 | const u32 type = FindType(flags, type_mask).value(); |
| 272 | vk::DeviceMemory memory = device.GetLogical().TryAllocateMemory({ | 291 | vk::DeviceMemory memory = device.GetLogical().TryAllocateMemory({ |
| 273 | .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, | 292 | .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, |
| 274 | .pNext = export_allocations ? &EXPORT_ALLOCATE_INFO : nullptr, | 293 | .pNext = nullptr, |
| 275 | .allocationSize = size, | 294 | .allocationSize = size, |
| 276 | .memoryTypeIndex = type, | 295 | .memoryTypeIndex = type, |
| 277 | }); | 296 | }); |
| @@ -342,16 +361,4 @@ std::optional<u32> MemoryAllocator::FindType(VkMemoryPropertyFlags flags, u32 ty | |||
| 342 | return std::nullopt; | 361 | return std::nullopt; |
| 343 | } | 362 | } |
| 344 | 363 | ||
| 345 | bool IsHostVisible(MemoryUsage usage) noexcept { | ||
| 346 | switch (usage) { | ||
| 347 | case MemoryUsage::DeviceLocal: | ||
| 348 | return false; | ||
| 349 | case MemoryUsage::Upload: | ||
| 350 | case MemoryUsage::Download: | ||
| 351 | return true; | ||
| 352 | } | ||
| 353 | ASSERT_MSG(false, "Invalid memory usage={}", usage); | ||
| 354 | return false; | ||
| 355 | } | ||
| 356 | |||
| 357 | } // namespace Vulkan | 364 | } // namespace Vulkan |
diff --git a/src/video_core/vulkan_common/vulkan_memory_allocator.h b/src/video_core/vulkan_common/vulkan_memory_allocator.h index a5bff03fe..f449bc8d0 100644 --- a/src/video_core/vulkan_common/vulkan_memory_allocator.h +++ b/src/video_core/vulkan_common/vulkan_memory_allocator.h | |||
| @@ -9,6 +9,8 @@ | |||
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | #include "video_core/vulkan_common/vulkan_wrapper.h" | 10 | #include "video_core/vulkan_common/vulkan_wrapper.h" |
| 11 | 11 | ||
| 12 | VK_DEFINE_HANDLE(VmaAllocator) | ||
| 13 | |||
| 12 | namespace Vulkan { | 14 | namespace Vulkan { |
| 13 | 15 | ||
| 14 | class Device; | 16 | class Device; |
| @@ -17,9 +19,11 @@ class MemoryAllocation; | |||
| 17 | 19 | ||
| 18 | /// Hints and requirements for the backing memory type of a commit | 20 | /// Hints and requirements for the backing memory type of a commit |
| 19 | enum class MemoryUsage { | 21 | enum class MemoryUsage { |
| 20 | DeviceLocal, ///< Hints device local usages, fastest memory type to read and write from the GPU | 22 | DeviceLocal, ///< Requests device local host visible buffer, falling back to device local |
| 23 | ///< memory. | ||
| 21 | Upload, ///< Requires a host visible memory type optimized for CPU to GPU uploads | 24 | Upload, ///< Requires a host visible memory type optimized for CPU to GPU uploads |
| 22 | Download, ///< Requires a host visible memory type optimized for GPU to CPU readbacks | 25 | Download, ///< Requires a host visible memory type optimized for GPU to CPU readbacks |
| 26 | Stream, ///< Requests device local host visible buffer, falling back host memory. | ||
| 23 | }; | 27 | }; |
| 24 | 28 | ||
| 25 | /// Ownership handle of a memory commitment. | 29 | /// Ownership handle of a memory commitment. |
| @@ -41,9 +45,6 @@ public: | |||
| 41 | /// It will map the backing allocation if it hasn't been mapped before. | 45 | /// It will map the backing allocation if it hasn't been mapped before. |
| 42 | std::span<u8> Map(); | 46 | std::span<u8> Map(); |
| 43 | 47 | ||
| 44 | /// Returns an non-owning OpenGL handle, creating one if it doesn't exist. | ||
| 45 | u32 ExportOpenGLHandle() const; | ||
| 46 | |||
| 47 | /// Returns the Vulkan memory handler. | 48 | /// Returns the Vulkan memory handler. |
| 48 | VkDeviceMemory Memory() const { | 49 | VkDeviceMemory Memory() const { |
| 49 | return memory; | 50 | return memory; |
| @@ -74,16 +75,19 @@ public: | |||
| 74 | * Construct memory allocator | 75 | * Construct memory allocator |
| 75 | * | 76 | * |
| 76 | * @param device_ Device to allocate from | 77 | * @param device_ Device to allocate from |
| 77 | * @param export_allocations_ True when allocations have to be exported | ||
| 78 | * | 78 | * |
| 79 | * @throw vk::Exception on failure | 79 | * @throw vk::Exception on failure |
| 80 | */ | 80 | */ |
| 81 | explicit MemoryAllocator(const Device& device_, bool export_allocations_); | 81 | explicit MemoryAllocator(const Device& device_); |
| 82 | ~MemoryAllocator(); | 82 | ~MemoryAllocator(); |
| 83 | 83 | ||
| 84 | MemoryAllocator& operator=(const MemoryAllocator&) = delete; | 84 | MemoryAllocator& operator=(const MemoryAllocator&) = delete; |
| 85 | MemoryAllocator(const MemoryAllocator&) = delete; | 85 | MemoryAllocator(const MemoryAllocator&) = delete; |
| 86 | 86 | ||
| 87 | vk::Image CreateImage(const VkImageCreateInfo& ci) const; | ||
| 88 | |||
| 89 | vk::Buffer CreateBuffer(const VkBufferCreateInfo& ci, MemoryUsage usage) const; | ||
| 90 | |||
| 87 | /** | 91 | /** |
| 88 | * Commits a memory with the specified requirements. | 92 | * Commits a memory with the specified requirements. |
| 89 | * | 93 | * |
| @@ -97,9 +101,6 @@ public: | |||
| 97 | /// Commits memory required by the buffer and binds it. | 101 | /// Commits memory required by the buffer and binds it. |
| 98 | MemoryCommit Commit(const vk::Buffer& buffer, MemoryUsage usage); | 102 | MemoryCommit Commit(const vk::Buffer& buffer, MemoryUsage usage); |
| 99 | 103 | ||
| 100 | /// Commits memory required by the image and binds it. | ||
| 101 | MemoryCommit Commit(const vk::Image& image, MemoryUsage usage); | ||
| 102 | |||
| 103 | private: | 104 | private: |
| 104 | /// Tries to allocate a chunk of memory. | 105 | /// Tries to allocate a chunk of memory. |
| 105 | bool TryAllocMemory(VkMemoryPropertyFlags flags, u32 type_mask, u64 size); | 106 | bool TryAllocMemory(VkMemoryPropertyFlags flags, u32 type_mask, u64 size); |
| @@ -117,15 +118,12 @@ private: | |||
| 117 | /// Returns index to the fastest memory type compatible with the passed requirements. | 118 | /// Returns index to the fastest memory type compatible with the passed requirements. |
| 118 | std::optional<u32> FindType(VkMemoryPropertyFlags flags, u32 type_mask) const; | 119 | std::optional<u32> FindType(VkMemoryPropertyFlags flags, u32 type_mask) const; |
| 119 | 120 | ||
| 120 | const Device& device; ///< Device handle. | 121 | const Device& device; ///< Device handle. |
| 121 | const VkPhysicalDeviceMemoryProperties properties; ///< Physical device properties. | 122 | VmaAllocator allocator; ///< Vma allocator. |
| 122 | const bool export_allocations; ///< True when memory allocations have to be exported. | 123 | const VkPhysicalDeviceMemoryProperties properties; ///< Physical device properties. |
| 123 | std::vector<std::unique_ptr<MemoryAllocation>> allocations; ///< Current allocations. | 124 | std::vector<std::unique_ptr<MemoryAllocation>> allocations; ///< Current allocations. |
| 124 | VkDeviceSize buffer_image_granularity; // The granularity for adjacent offsets between buffers | 125 | VkDeviceSize buffer_image_granularity; // The granularity for adjacent offsets between buffers |
| 125 | // and optimal images | 126 | // and optimal images |
| 126 | }; | 127 | }; |
| 127 | 128 | ||
| 128 | /// Returns true when a memory usage is guaranteed to be host visible. | ||
| 129 | bool IsHostVisible(MemoryUsage usage) noexcept; | ||
| 130 | |||
| 131 | } // namespace Vulkan | 129 | } // namespace Vulkan |
diff --git a/src/video_core/vulkan_common/vulkan_wrapper.cpp b/src/video_core/vulkan_common/vulkan_wrapper.cpp index 336f53700..28fcb21a0 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.cpp +++ b/src/video_core/vulkan_common/vulkan_wrapper.cpp | |||
| @@ -12,6 +12,8 @@ | |||
| 12 | 12 | ||
| 13 | #include "video_core/vulkan_common/vulkan_wrapper.h" | 13 | #include "video_core/vulkan_common/vulkan_wrapper.h" |
| 14 | 14 | ||
| 15 | #include <vk_mem_alloc.h> | ||
| 16 | |||
| 15 | namespace Vulkan::vk { | 17 | namespace Vulkan::vk { |
| 16 | 18 | ||
| 17 | namespace { | 19 | namespace { |
| @@ -547,24 +549,40 @@ DebugUtilsMessenger Instance::CreateDebugUtilsMessenger( | |||
| 547 | return DebugUtilsMessenger(object, handle, *dld); | 549 | return DebugUtilsMessenger(object, handle, *dld); |
| 548 | } | 550 | } |
| 549 | 551 | ||
| 550 | void Buffer::BindMemory(VkDeviceMemory memory, VkDeviceSize offset) const { | 552 | void Image::SetObjectNameEXT(const char* name) const { |
| 551 | Check(dld->vkBindBufferMemory(owner, handle, memory, offset)); | 553 | SetObjectName(dld, owner, handle, VK_OBJECT_TYPE_IMAGE, name); |
| 552 | } | 554 | } |
| 553 | 555 | ||
| 554 | void Buffer::SetObjectNameEXT(const char* name) const { | 556 | void Image::Release() const noexcept { |
| 555 | SetObjectName(dld, owner, handle, VK_OBJECT_TYPE_BUFFER, name); | 557 | if (handle) { |
| 558 | vmaDestroyImage(allocator, handle, allocation); | ||
| 559 | } | ||
| 556 | } | 560 | } |
| 557 | 561 | ||
| 558 | void BufferView::SetObjectNameEXT(const char* name) const { | 562 | void Buffer::Flush() const { |
| 559 | SetObjectName(dld, owner, handle, VK_OBJECT_TYPE_BUFFER_VIEW, name); | 563 | if (!is_coherent) { |
| 564 | vmaFlushAllocation(allocator, allocation, 0, VK_WHOLE_SIZE); | ||
| 565 | } | ||
| 560 | } | 566 | } |
| 561 | 567 | ||
| 562 | void Image::BindMemory(VkDeviceMemory memory, VkDeviceSize offset) const { | 568 | void Buffer::Invalidate() const { |
| 563 | Check(dld->vkBindImageMemory(owner, handle, memory, offset)); | 569 | if (!is_coherent) { |
| 570 | vmaInvalidateAllocation(allocator, allocation, 0, VK_WHOLE_SIZE); | ||
| 571 | } | ||
| 564 | } | 572 | } |
| 565 | 573 | ||
| 566 | void Image::SetObjectNameEXT(const char* name) const { | 574 | void Buffer::SetObjectNameEXT(const char* name) const { |
| 567 | SetObjectName(dld, owner, handle, VK_OBJECT_TYPE_IMAGE, name); | 575 | SetObjectName(dld, owner, handle, VK_OBJECT_TYPE_BUFFER, name); |
| 576 | } | ||
| 577 | |||
| 578 | void Buffer::Release() const noexcept { | ||
| 579 | if (handle) { | ||
| 580 | vmaDestroyBuffer(allocator, handle, allocation); | ||
| 581 | } | ||
| 582 | } | ||
| 583 | |||
| 584 | void BufferView::SetObjectNameEXT(const char* name) const { | ||
| 585 | SetObjectName(dld, owner, handle, VK_OBJECT_TYPE_BUFFER_VIEW, name); | ||
| 568 | } | 586 | } |
| 569 | 587 | ||
| 570 | void ImageView::SetObjectNameEXT(const char* name) const { | 588 | void ImageView::SetObjectNameEXT(const char* name) const { |
| @@ -701,24 +719,12 @@ Queue Device::GetQueue(u32 family_index) const noexcept { | |||
| 701 | return Queue(queue, *dld); | 719 | return Queue(queue, *dld); |
| 702 | } | 720 | } |
| 703 | 721 | ||
| 704 | Buffer Device::CreateBuffer(const VkBufferCreateInfo& ci) const { | ||
| 705 | VkBuffer object; | ||
| 706 | Check(dld->vkCreateBuffer(handle, &ci, nullptr, &object)); | ||
| 707 | return Buffer(object, handle, *dld); | ||
| 708 | } | ||
| 709 | |||
| 710 | BufferView Device::CreateBufferView(const VkBufferViewCreateInfo& ci) const { | 722 | BufferView Device::CreateBufferView(const VkBufferViewCreateInfo& ci) const { |
| 711 | VkBufferView object; | 723 | VkBufferView object; |
| 712 | Check(dld->vkCreateBufferView(handle, &ci, nullptr, &object)); | 724 | Check(dld->vkCreateBufferView(handle, &ci, nullptr, &object)); |
| 713 | return BufferView(object, handle, *dld); | 725 | return BufferView(object, handle, *dld); |
| 714 | } | 726 | } |
| 715 | 727 | ||
| 716 | Image Device::CreateImage(const VkImageCreateInfo& ci) const { | ||
| 717 | VkImage object; | ||
| 718 | Check(dld->vkCreateImage(handle, &ci, nullptr, &object)); | ||
| 719 | return Image(object, handle, *dld); | ||
| 720 | } | ||
| 721 | |||
| 722 | ImageView Device::CreateImageView(const VkImageViewCreateInfo& ci) const { | 728 | ImageView Device::CreateImageView(const VkImageViewCreateInfo& ci) const { |
| 723 | VkImageView object; | 729 | VkImageView object; |
| 724 | Check(dld->vkCreateImageView(handle, &ci, nullptr, &object)); | 730 | Check(dld->vkCreateImageView(handle, &ci, nullptr, &object)); |
diff --git a/src/video_core/vulkan_common/vulkan_wrapper.h b/src/video_core/vulkan_common/vulkan_wrapper.h index 4ff328a21..44fce47a5 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.h +++ b/src/video_core/vulkan_common/vulkan_wrapper.h | |||
| @@ -32,6 +32,9 @@ | |||
| 32 | #pragma warning(disable : 26812) // Disable prefer enum class over enum | 32 | #pragma warning(disable : 26812) // Disable prefer enum class over enum |
| 33 | #endif | 33 | #endif |
| 34 | 34 | ||
| 35 | VK_DEFINE_HANDLE(VmaAllocator) | ||
| 36 | VK_DEFINE_HANDLE(VmaAllocation) | ||
| 37 | |||
| 35 | namespace Vulkan::vk { | 38 | namespace Vulkan::vk { |
| 36 | 39 | ||
| 37 | /** | 40 | /** |
| @@ -616,6 +619,138 @@ public: | |||
| 616 | } | 619 | } |
| 617 | }; | 620 | }; |
| 618 | 621 | ||
| 622 | class Image { | ||
| 623 | public: | ||
| 624 | explicit Image(VkImage handle_, VkDevice owner_, VmaAllocator allocator_, | ||
| 625 | VmaAllocation allocation_, const DeviceDispatch& dld_) noexcept | ||
| 626 | : handle{handle_}, owner{owner_}, allocator{allocator_}, | ||
| 627 | allocation{allocation_}, dld{&dld_} {} | ||
| 628 | Image() = default; | ||
| 629 | |||
| 630 | Image(const Image&) = delete; | ||
| 631 | Image& operator=(const Image&) = delete; | ||
| 632 | |||
| 633 | Image(Image&& rhs) noexcept | ||
| 634 | : handle{std::exchange(rhs.handle, nullptr)}, owner{rhs.owner}, allocator{rhs.allocator}, | ||
| 635 | allocation{rhs.allocation}, dld{rhs.dld} {} | ||
| 636 | |||
| 637 | Image& operator=(Image&& rhs) noexcept { | ||
| 638 | Release(); | ||
| 639 | handle = std::exchange(rhs.handle, nullptr); | ||
| 640 | owner = rhs.owner; | ||
| 641 | allocator = rhs.allocator; | ||
| 642 | allocation = rhs.allocation; | ||
| 643 | dld = rhs.dld; | ||
| 644 | return *this; | ||
| 645 | } | ||
| 646 | |||
| 647 | ~Image() noexcept { | ||
| 648 | Release(); | ||
| 649 | } | ||
| 650 | |||
| 651 | VkImage operator*() const noexcept { | ||
| 652 | return handle; | ||
| 653 | } | ||
| 654 | |||
| 655 | void reset() noexcept { | ||
| 656 | Release(); | ||
| 657 | handle = nullptr; | ||
| 658 | } | ||
| 659 | |||
| 660 | explicit operator bool() const noexcept { | ||
| 661 | return handle != nullptr; | ||
| 662 | } | ||
| 663 | |||
| 664 | void SetObjectNameEXT(const char* name) const; | ||
| 665 | |||
| 666 | private: | ||
| 667 | void Release() const noexcept; | ||
| 668 | |||
| 669 | VkImage handle = nullptr; | ||
| 670 | VkDevice owner = nullptr; | ||
| 671 | VmaAllocator allocator = nullptr; | ||
| 672 | VmaAllocation allocation = nullptr; | ||
| 673 | const DeviceDispatch* dld = nullptr; | ||
| 674 | }; | ||
| 675 | |||
| 676 | class Buffer { | ||
| 677 | public: | ||
| 678 | explicit Buffer(VkBuffer handle_, VkDevice owner_, VmaAllocator allocator_, | ||
| 679 | VmaAllocation allocation_, std::span<u8> mapped_, bool is_coherent_, | ||
| 680 | const DeviceDispatch& dld_) noexcept | ||
| 681 | : handle{handle_}, owner{owner_}, allocator{allocator_}, | ||
| 682 | allocation{allocation_}, mapped{mapped_}, is_coherent{is_coherent_}, dld{&dld_} {} | ||
| 683 | Buffer() = default; | ||
| 684 | |||
| 685 | Buffer(const Buffer&) = delete; | ||
| 686 | Buffer& operator=(const Buffer&) = delete; | ||
| 687 | |||
| 688 | Buffer(Buffer&& rhs) noexcept | ||
| 689 | : handle{std::exchange(rhs.handle, nullptr)}, owner{rhs.owner}, allocator{rhs.allocator}, | ||
| 690 | allocation{rhs.allocation}, mapped{rhs.mapped}, | ||
| 691 | is_coherent{rhs.is_coherent}, dld{rhs.dld} {} | ||
| 692 | |||
| 693 | Buffer& operator=(Buffer&& rhs) noexcept { | ||
| 694 | Release(); | ||
| 695 | handle = std::exchange(rhs.handle, nullptr); | ||
| 696 | owner = rhs.owner; | ||
| 697 | allocator = rhs.allocator; | ||
| 698 | allocation = rhs.allocation; | ||
| 699 | mapped = rhs.mapped; | ||
| 700 | is_coherent = rhs.is_coherent; | ||
| 701 | dld = rhs.dld; | ||
| 702 | return *this; | ||
| 703 | } | ||
| 704 | |||
| 705 | ~Buffer() noexcept { | ||
| 706 | Release(); | ||
| 707 | } | ||
| 708 | |||
| 709 | VkBuffer operator*() const noexcept { | ||
| 710 | return handle; | ||
| 711 | } | ||
| 712 | |||
| 713 | void reset() noexcept { | ||
| 714 | Release(); | ||
| 715 | handle = nullptr; | ||
| 716 | } | ||
| 717 | |||
| 718 | explicit operator bool() const noexcept { | ||
| 719 | return handle != nullptr; | ||
| 720 | } | ||
| 721 | |||
| 722 | /// Returns the host mapped memory, an empty span otherwise. | ||
| 723 | std::span<u8> Mapped() noexcept { | ||
| 724 | return mapped; | ||
| 725 | } | ||
| 726 | |||
| 727 | std::span<const u8> Mapped() const noexcept { | ||
| 728 | return mapped; | ||
| 729 | } | ||
| 730 | |||
| 731 | /// Returns true if the buffer is mapped to the host. | ||
| 732 | bool IsHostVisible() const noexcept { | ||
| 733 | return !mapped.empty(); | ||
| 734 | } | ||
| 735 | |||
| 736 | void Flush() const; | ||
| 737 | |||
| 738 | void Invalidate() const; | ||
| 739 | |||
| 740 | void SetObjectNameEXT(const char* name) const; | ||
| 741 | |||
| 742 | private: | ||
| 743 | void Release() const noexcept; | ||
| 744 | |||
| 745 | VkBuffer handle = nullptr; | ||
| 746 | VkDevice owner = nullptr; | ||
| 747 | VmaAllocator allocator = nullptr; | ||
| 748 | VmaAllocation allocation = nullptr; | ||
| 749 | std::span<u8> mapped = {}; | ||
| 750 | bool is_coherent = false; | ||
| 751 | const DeviceDispatch* dld = nullptr; | ||
| 752 | }; | ||
| 753 | |||
| 619 | class Queue { | 754 | class Queue { |
| 620 | public: | 755 | public: |
| 621 | /// Construct an empty queue handle. | 756 | /// Construct an empty queue handle. |
| @@ -639,17 +774,6 @@ private: | |||
| 639 | const DeviceDispatch* dld = nullptr; | 774 | const DeviceDispatch* dld = nullptr; |
| 640 | }; | 775 | }; |
| 641 | 776 | ||
| 642 | class Buffer : public Handle<VkBuffer, VkDevice, DeviceDispatch> { | ||
| 643 | using Handle<VkBuffer, VkDevice, DeviceDispatch>::Handle; | ||
| 644 | |||
| 645 | public: | ||
| 646 | /// Attaches a memory allocation. | ||
| 647 | void BindMemory(VkDeviceMemory memory, VkDeviceSize offset) const; | ||
| 648 | |||
| 649 | /// Set object name. | ||
| 650 | void SetObjectNameEXT(const char* name) const; | ||
| 651 | }; | ||
| 652 | |||
| 653 | class BufferView : public Handle<VkBufferView, VkDevice, DeviceDispatch> { | 777 | class BufferView : public Handle<VkBufferView, VkDevice, DeviceDispatch> { |
| 654 | using Handle<VkBufferView, VkDevice, DeviceDispatch>::Handle; | 778 | using Handle<VkBufferView, VkDevice, DeviceDispatch>::Handle; |
| 655 | 779 | ||
| @@ -658,17 +782,6 @@ public: | |||
| 658 | void SetObjectNameEXT(const char* name) const; | 782 | void SetObjectNameEXT(const char* name) const; |
| 659 | }; | 783 | }; |
| 660 | 784 | ||
| 661 | class Image : public Handle<VkImage, VkDevice, DeviceDispatch> { | ||
| 662 | using Handle<VkImage, VkDevice, DeviceDispatch>::Handle; | ||
| 663 | |||
| 664 | public: | ||
| 665 | /// Attaches a memory allocation. | ||
| 666 | void BindMemory(VkDeviceMemory memory, VkDeviceSize offset) const; | ||
| 667 | |||
| 668 | /// Set object name. | ||
| 669 | void SetObjectNameEXT(const char* name) const; | ||
| 670 | }; | ||
| 671 | |||
| 672 | class ImageView : public Handle<VkImageView, VkDevice, DeviceDispatch> { | 785 | class ImageView : public Handle<VkImageView, VkDevice, DeviceDispatch> { |
| 673 | using Handle<VkImageView, VkDevice, DeviceDispatch>::Handle; | 786 | using Handle<VkImageView, VkDevice, DeviceDispatch>::Handle; |
| 674 | 787 | ||
| @@ -840,12 +953,8 @@ public: | |||
| 840 | 953 | ||
| 841 | Queue GetQueue(u32 family_index) const noexcept; | 954 | Queue GetQueue(u32 family_index) const noexcept; |
| 842 | 955 | ||
| 843 | Buffer CreateBuffer(const VkBufferCreateInfo& ci) const; | ||
| 844 | |||
| 845 | BufferView CreateBufferView(const VkBufferViewCreateInfo& ci) const; | 956 | BufferView CreateBufferView(const VkBufferViewCreateInfo& ci) const; |
| 846 | 957 | ||
| 847 | Image CreateImage(const VkImageCreateInfo& ci) const; | ||
| 848 | |||
| 849 | ImageView CreateImageView(const VkImageViewCreateInfo& ci) const; | 958 | ImageView CreateImageView(const VkImageViewCreateInfo& ci) const; |
| 850 | 959 | ||
| 851 | Semaphore CreateSemaphore() const; | 960 | Semaphore CreateSemaphore() const; |