From d21bb9a1b76831b239410b6de85a3b93b0d73042 Mon Sep 17 00:00:00 2001 From: CamilleLaVey Date: Mon, 27 Jul 2026 22:39:10 -0400 Subject: [PATCH] [TEST] Adjustments on memory collection --- src/video_core/CMakeLists.txt | 1 - src/video_core/buffer_cache/buffer_cache.h | 3 +- .../buffer_cache/buffer_cache_base.h | 1 + src/video_core/fence_manager.h | 13 +- .../renderer_vulkan/vk_buffer_cache.cpp | 1 - .../renderer_vulkan/vk_rasterizer.cpp | 5 +- .../vk_staging_buffer_pool.cpp | 53 ++++- .../renderer_vulkan/vk_staging_buffer_pool.h | 2 + src/video_core/texture_cache/texture_cache.h | 3 +- .../texture_cache/texture_cache_base.h | 1 + .../vulkan_common/vulkan_memory_allocator.cpp | 221 +----------------- .../vulkan_common/vulkan_memory_allocator.h | 74 ------ .../vulkan_common/vulkan_wrapper.cpp | 15 ++ src/video_core/vulkan_common/vulkan_wrapper.h | 4 + 14 files changed, 88 insertions(+), 309 deletions(-) diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 6c593eb3e0..4852237ca2 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -34,7 +34,6 @@ add_library(video_core STATIC control/scheduler.cpp control/scheduler.h deferred_destruction_queue.h - delayed_destruction_ring.h dirty_flags.cpp dirty_flags.h dma_pusher.cpp diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index d4d7204bfc..e6bc0d6da5 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -98,7 +98,8 @@ void BufferCache

::EnsureHeadroom(bool allow_download) { if (usage <= limit) { return; } - ReclaimMemory(usage - limit, allow_download); + const u64 target = (limit / 100) * RECLAIM_TARGET_PERCENT; + ReclaimMemory(usage - target, allow_download); } template diff --git a/src/video_core/buffer_cache/buffer_cache_base.h b/src/video_core/buffer_cache/buffer_cache_base.h index 7d618e51b1..caf9e3df47 100644 --- a/src/video_core/buffer_cache/buffer_cache_base.h +++ b/src/video_core/buffer_cache/buffer_cache_base.h @@ -190,6 +190,7 @@ class BufferCache : public VideoCommon::ChannelSetupCaches RETIRE_DELAY ? retire_tick - RETIRE_DELAY : 0); } // Unlike other fences, this one doesn't @@ -183,7 +184,7 @@ private: } { std::unique_lock lock(ring_guard); - delayed_destruction_ring.Push(std::move(current_fence)); + sentenced_fences.Push(std::move(current_fence), retire_tick); } fences.pop(); } @@ -216,7 +217,7 @@ private: } { std::unique_lock lock(ring_guard); - delayed_destruction_ring.Push(std::move(current_fence)); + sentenced_fences.Push(std::move(current_fence), retire_tick); } } } @@ -261,7 +262,9 @@ private: std::jthread fence_thread; - DelayedDestructionRing delayed_destruction_ring; + static constexpr u64 RETIRE_DELAY = 8; + u64 retire_tick = 1; + DeferredDestructionQueue sentenced_fences; }; } // namespace VideoCommon diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp index d197c32cca..4de5d3f7a5 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp @@ -246,7 +246,6 @@ protected: StagingBufferPool& staging_pool; vk::Buffer buffer{}; - MemoryCommit memory_commit{}; VkIndexType index_type{}; u32 num_indices = 0; }; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index db1a79d857..e940036d8c 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -222,7 +222,10 @@ RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra wfi_event(device.GetLogical().CreateEvent()) { scheduler.SetQueryCache(query_cache); memory_allocator.SetReclaimCallback([this](u64 bytes) -> u64 { - u64 freed = texture_cache.ReclaimMemory(bytes, false); + u64 freed = staging_pool.ReclaimMemory(bytes); + if (freed < bytes) { + freed += texture_cache.ReclaimMemory(bytes - freed, false); + } if (freed < bytes) { freed += buffer_cache.ReclaimMemory(bytes - freed, false); } diff --git a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp index b03449f9f7..d81a28eb3f 100644 --- a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp +++ b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp @@ -252,25 +252,62 @@ void StagingBufferPool::ReleaseLevel(StagingBuffersCache& cache, size_t log2) { constexpr size_t deletions_per_tick = 16; auto& staging = cache[log2]; auto& entries = staging.entries; - const size_t old_size = entries.size(); + if (entries.empty()) { + staging.delete_index = 0; + staging.iterate_index = 0; + return; + } const auto is_deletable = [this](const StagingBuffer& entry) { return scheduler.IsFree(entry.tick); }; - const size_t begin_offset = staging.delete_index; - const size_t end_offset = (std::min)(begin_offset + deletions_per_tick, old_size); + const size_t begin_offset = (std::min)(staging.delete_index, entries.size()); + const size_t end_offset = (std::min)(begin_offset + deletions_per_tick, entries.size()); const auto begin = entries.begin() + begin_offset; const auto end = entries.begin() + end_offset; - entries.erase(std::remove_if(begin, end, is_deletable), end); + const auto surviving_end = std::remove_if(begin, end, is_deletable); + const size_t removed = static_cast(std::distance(surviving_end, end)); + entries.erase(surviving_end, end); - const size_t new_size = entries.size(); - staging.delete_index += deletions_per_tick; - if (staging.delete_index >= new_size) { + staging.delete_index = end_offset - removed; + if (staging.delete_index >= entries.size()) { staging.delete_index = 0; } - if (staging.iterate_index > new_size) { + if (staging.iterate_index > entries.size()) { staging.iterate_index = 0; } } +u64 StagingBufferPool::ReclaimMemory(u64 target_bytes) { + u64 freed = 0; + const auto is_deletable = [this](const StagingBuffer& entry) { + return scheduler.IsFree(entry.tick); + }; + const auto reclaim_cache = [&](StagingBuffersCache& cache) { + for (size_t level = NUM_LEVELS; level-- > 0 && freed < target_bytes;) { + auto& staging = cache[level]; + auto& entries = staging.entries; + if (entries.empty()) { + continue; + } + const u64 entry_bytes = 1ULL << level; + auto it = entries.begin(); + while (it != entries.end() && freed < target_bytes) { + if (is_deletable(*it)) { + it = entries.erase(it); + freed += entry_bytes; + } else { + ++it; + } + } + staging.delete_index = 0; + staging.iterate_index = 0; + } + }; + reclaim_cache(device_local_cache); + reclaim_cache(upload_cache); + reclaim_cache(download_cache); + return freed; +} + } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h index f63a203272..6443392fd6 100644 --- a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h +++ b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h @@ -42,6 +42,8 @@ public: void TickFrame(); + u64 ReclaimMemory(u64 target_bytes); + private: struct StreamBufferCommit { size_t upper_bound; diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index ac7032fc86..297dc49ff5 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -221,7 +221,8 @@ void TextureCache

::EnsureHeadroom(bool allow_download) { if (usage <= limit) { return; } - ReclaimMemory(usage - limit, allow_download); + const u64 target = (limit / 100) * RECLAIM_TARGET_PERCENT; + ReclaimMemory(usage - target, allow_download); } template diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h index 5640c7f1cc..6bb0b57de0 100644 --- a/src/video_core/texture_cache/texture_cache_base.h +++ b/src/video_core/texture_cache/texture_cache_base.h @@ -121,6 +121,7 @@ class TextureCache : public VideoCommon::ChannelSetupCaches - static VkBuffer GetVkHandleFromBuffer(const T &buf) { - if constexpr (requires { static_cast(buf); }) { - return static_cast(buf); - } else if constexpr (requires {{ buf.GetHandle() } -> std::convertible_to; }) { - return buf.GetHandle(); - } else if constexpr (requires {{ buf.Handle() } -> std::convertible_to; }) { - return buf.Handle(); - } else if constexpr (requires {{ buf.vk_handle() } -> std::convertible_to; }) { - return buf.vk_handle(); - } else { - static_assert(sizeof(T) == 0, "Cannot extract VkBuffer handle from vk::Buffer"); - return VK_NULL_HANDLE; - } - } - } // namespace -//MemoryCommit is now VMA-backed - MemoryCommit::MemoryCommit(VmaAllocator alloc, VmaAllocation a, - const VmaAllocationInfo &info) noexcept - : allocator{alloc}, allocation{a}, memory{info.deviceMemory}, - offset{info.offset}, size{info.size}, mapped_ptr{info.pMappedData} { - // Log GPU memory allocation - if (GPU::Logging::IsActive() && - Settings::values.gpu_log_memory_tracking.GetValue()) { - GPU::Logging::GPULogger::GetInstance().LogMemoryAllocation( - reinterpret_cast(memory), - static_cast(size), - 0 // Memory property flags (not easily available from VMA) - ); - } - } - - MemoryCommit::~MemoryCommit() { Release(); } - - MemoryCommit::MemoryCommit(MemoryCommit &&rhs) noexcept - : allocator{std::exchange(rhs.allocator, nullptr)}, - allocation{std::exchange(rhs.allocation, nullptr)}, - memory{std::exchange(rhs.memory, VK_NULL_HANDLE)}, - offset{std::exchange(rhs.offset, 0)}, - size{std::exchange(rhs.size, 0)}, - mapped_ptr{std::exchange(rhs.mapped_ptr, nullptr)} {} - - MemoryCommit &MemoryCommit::operator=(MemoryCommit &&rhs) noexcept { - if (this != &rhs) { - Release(); - allocator = std::exchange(rhs.allocator, nullptr); - allocation = std::exchange(rhs.allocation, nullptr); - memory = std::exchange(rhs.memory, VK_NULL_HANDLE); - offset = std::exchange(rhs.offset, 0); - size = std::exchange(rhs.size, 0); - mapped_ptr = std::exchange(rhs.mapped_ptr, nullptr); - } - return *this; - } - - std::span MemoryCommit::Map() - { - if (!allocation) return {}; - if (!mapped_ptr) { - if (vmaMapMemory(allocator, allocation, &mapped_ptr) != VK_SUCCESS) return {}; - } - const size_t n = static_cast(std::min(size, - (std::numeric_limits::max)())); - return std::span{static_cast(mapped_ptr), n}; - } - - std::span MemoryCommit::Map() const - { - if (!allocation) return {}; - if (!mapped_ptr) { - void *p = nullptr; - if (vmaMapMemory(allocator, allocation, &p) != VK_SUCCESS) return {}; - const_cast(this)->mapped_ptr = p; - } - const size_t n = static_cast(std::min(size, - (std::numeric_limits::max)())); - return std::span{static_cast(mapped_ptr), n}; - } - - void MemoryCommit::Unmap() - { - if (allocation && mapped_ptr) { - vmaUnmapMemory(allocator, allocation); - mapped_ptr = nullptr; - } - } - - void MemoryCommit::Release() { - if (allocation && allocator) { - // Log GPU memory deallocation - if (GPU::Logging::IsActive() && - Settings::values.gpu_log_memory_tracking.GetValue() && - memory != VK_NULL_HANDLE) { - GPU::Logging::GPULogger::GetInstance().LogMemoryDeallocation( - reinterpret_cast(memory) - ); - } - - if (mapped_ptr) { - vmaUnmapMemory(allocator, allocation); - mapped_ptr = nullptr; - } - vmaFreeMemory(allocator, allocation); - } - allocation = nullptr; - allocator = nullptr; - memory = VK_NULL_HANDLE; - offset = 0; - size = 0; - } - MemoryAllocator::MemoryAllocator(const Device &device_) : device{device_}, allocator{device.GetAllocator()}, - properties{device_.GetPhysical().GetMemoryProperties().memoryProperties}, - buffer_image_granularity{ - device_.GetPhysical().GetProperties().limits.bufferImageGranularity} { + properties{device_.GetPhysical().GetMemoryProperties().memoryProperties} { // Preserve the previous "RenderDoc small heap" trimming behavior that we had in original vma minus the heap bug if (device.HasDebuggingToolAttached()) @@ -226,7 +92,7 @@ namespace Vulkan { void MemoryAllocator::SetReclaimCallback(ReclaimCallback callback) { reclaim_callback = std::move(callback); - owner_thread = std::this_thread::get_id(); + vk::SetAllocatorOwnerThread(); } bool MemoryAllocator::ReclaimAtLeast(u64 hint_bytes) const { @@ -239,12 +105,6 @@ namespace Vulkan { return freed > 0; } - void MemoryAllocator::AssertOwnerThread() const { - DEBUG_ASSERT_MSG(owner_thread == std::thread::id{} || - owner_thread == std::this_thread::get_id(), - "VmaAllocator is externally synchronized but was used off-thread"); - } - vk::Image MemoryAllocator::CreateImage(const VkImageCreateInfo &ci) const { const VmaAllocationCreateInfo alloc_ci = { @@ -261,7 +121,7 @@ namespace Vulkan { VkImage handle{}; VmaAllocation allocation{}; VmaAllocationInfo alloc_info{}; - AssertOwnerThread(); + DEBUG_ASSERT(vk::OnAllocatorOwnerThread()); VkResult res = vmaCreateImage(allocator, &ci, &alloc_ci, &handle, &allocation, &alloc_info); @@ -317,7 +177,7 @@ namespace Vulkan { VmaAllocation allocation{}; VkMemoryPropertyFlags property_flags{}; - AssertOwnerThread(); + DEBUG_ASSERT(vk::OnAllocatorOwnerThread()); VkResult res = vmaCreateBuffer(allocator, &ci, &alloc_ci, &handle, &allocation, &alloc_info); @@ -360,77 +220,4 @@ namespace Vulkan { device.GetDispatchLoader()); } - MemoryCommit MemoryAllocator::Commit(const VkMemoryRequirements &reqs, MemoryUsage usage) - { - const auto vma_usage = MemoryUsageVma(usage); - VmaAllocationCreateInfo ci{}; - ci.flags = VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT | MemoryUsageVmaFlags(usage); - ci.usage = vma_usage; - ci.memoryTypeBits = reqs.memoryTypeBits & valid_memory_types; - ci.requiredFlags = 0; - ci.preferredFlags = MemoryUsagePreferredVmaFlags(usage); - - VmaAllocation a{}; - VmaAllocationInfo info{}; - - VkResult res = vmaAllocateMemory(allocator, &reqs, &ci, &a, &info); - - if (res != VK_SUCCESS) { - // Relax 1: drop budget constraint - auto ci2 = ci; - ci2.flags &= ~VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT; - res = vmaAllocateMemory(allocator, &reqs, &ci2, &a, &info); - - // Relax 2: if we preferred DEVICE_LOCAL, drop that preference - if (res != VK_SUCCESS && (ci.preferredFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)) { - auto ci3 = ci2; - ci3.preferredFlags &= ~VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; - res = vmaAllocateMemory(allocator, &reqs, &ci3, &a, &info); - } - } - - vk::Check(res); - return MemoryCommit(allocator, a, info); - } - - MemoryCommit MemoryAllocator::Commit(const vk::Buffer &buffer, MemoryUsage usage) { - // Allocate memory appropriate for this buffer automatically - const auto vma_usage = MemoryUsageVma(usage); - - VmaAllocationCreateInfo ci{}; - ci.flags = VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT | MemoryUsageVmaFlags(usage); - ci.usage = vma_usage; - ci.requiredFlags = 0; - ci.preferredFlags = MemoryUsagePreferredVmaFlags(usage); - ci.pool = VK_NULL_HANDLE; - ci.pUserData = nullptr; - ci.priority = 0.0f; - - const VkBuffer raw = *buffer; - - VmaAllocation a{}; - VmaAllocationInfo info{}; - - // Let VMA infer memory requirements from the buffer - VkResult res = vmaAllocateMemoryForBuffer(allocator, raw, &ci, &a, &info); - - if (res != VK_SUCCESS) { - auto ci2 = ci; - ci2.flags &= ~VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT; - res = vmaAllocateMemoryForBuffer(allocator, raw, &ci2, &a, &info); - - if (res != VK_SUCCESS && (ci.preferredFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)) { - auto ci3 = ci2; - ci3.preferredFlags &= ~VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; - res = vmaAllocateMemoryForBuffer(allocator, raw, &ci3, &a, &info); - } - } - - vk::Check(res); - vk::Check(vmaBindBufferMemory2(allocator, a, 0, raw, nullptr)); - return MemoryCommit(allocator, a, info); - } - - - } // 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 10b18df43c..f50b351160 100644 --- a/src/video_core/vulkan_common/vulkan_memory_allocator.h +++ b/src/video_core/vulkan_common/vulkan_memory_allocator.h @@ -9,7 +9,6 @@ #include #include #include -#include #include #include "common/common_types.h" @@ -41,51 +40,6 @@ namespace Vulkan { } } -/// Ownership handle of a memory commitment (real VMA allocation). - class MemoryCommit { - public: - MemoryCommit() noexcept = default; - - MemoryCommit(VmaAllocator allocator, VmaAllocation allocation, - const VmaAllocationInfo &info) noexcept; - - ~MemoryCommit(); - - MemoryCommit(const MemoryCommit &) = delete; - - MemoryCommit &operator=(const MemoryCommit &) = delete; - - MemoryCommit(MemoryCommit &&) noexcept; - - MemoryCommit &operator=(MemoryCommit &&) noexcept; - - [[nodiscard]] std::span Map(); - - [[nodiscard]] std::span Map() const; - - void Unmap(); - - explicit operator bool() const noexcept { return allocation != nullptr; } - - VkDeviceMemory Memory() const noexcept { return memory; } - - VkDeviceSize Offset() const noexcept { return offset; } - - VkDeviceSize Size() const noexcept { return size; } - - VmaAllocation Allocation() const noexcept { return allocation; } - - private: - void Release(); - - VmaAllocator allocator{}; ///< VMA allocator - VmaAllocation allocation{}; ///< VMA allocation handle - VkDeviceMemory memory{}; ///< Underlying VkDeviceMemory chosen by VMA - VkDeviceSize offset{}; ///< Offset of this allocation inside VkDeviceMemory - VkDeviceSize size{}; ///< Size of the allocation - void *mapped_ptr{}; ///< Optional persistent mapped pointer - }; - /// Memory allocator container. /// Allocates and releases memory allocations on demand. class MemoryAllocator { @@ -109,19 +63,6 @@ namespace Vulkan { vk::Buffer CreateBuffer(const VkBufferCreateInfo &ci, MemoryUsage usage) const; - /** - * Commits a memory with the specified requirements. - * - * @param requirements Requirements returned from a Vulkan call. - * @param usage Indicates how the memory will be used. - * - * @returns A memory commit. - */ - MemoryCommit Commit(const VkMemoryRequirements &requirements, MemoryUsage usage); - - /// Commits memory required by the buffer and binds it (for buffers created outside VMA). - MemoryCommit Commit(const vk::Buffer &buffer, MemoryUsage usage); - using ReclaimCallback = std::function; void SetReclaimCallback(ReclaimCallback callback); @@ -129,29 +70,14 @@ namespace Vulkan { private: bool ReclaimAtLeast(u64 hint_bytes) const; - void AssertOwnerThread() const; - static constexpr u64 IMAGE_RECLAIM_HINT = 64ULL * 1024 * 1024; - static bool IsAutoUsage(VmaMemoryUsage u) noexcept { - switch (u) { - case VMA_MEMORY_USAGE_AUTO: - case VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE: - case VMA_MEMORY_USAGE_AUTO_PREFER_HOST: - return true; - default: - return false; - } - } - const Device &device; ///< Device handle. VmaAllocator allocator; ///< VMA allocator. const VkPhysicalDeviceMemoryProperties properties; ///< Physical device memory properties. - VkDeviceSize buffer_image_granularity; ///< Adjacent buffer/image granularity u32 valid_memory_types{~0u}; ReclaimCallback reclaim_callback; mutable bool in_reclaim{false}; - std::thread::id owner_thread; }; } // namespace Vulkan diff --git a/src/video_core/vulkan_common/vulkan_wrapper.cpp b/src/video_core/vulkan_common/vulkan_wrapper.cpp index ffb549da06..aecbf87e8f 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.cpp +++ b/src/video_core/vulkan_common/vulkan_wrapper.cpp @@ -7,9 +7,11 @@ #include #include #include +#include #include #include +#include "common/assert.h" #include "common/common_types.h" #include "common/logging.h" #include "video_core/vulkan_common/vk_enum_string_helper.h" @@ -20,6 +22,8 @@ namespace Vulkan::vk { namespace { +std::thread::id allocator_owner_thread; + template void SortPhysicalDevices(std::vector& devices, const InstanceDispatch& dld, Func&& func) { @@ -511,12 +515,22 @@ DebugReportCallback Instance::CreateDebugReportCallback( return DebugReportCallback(object, handle, *dld); } +void SetAllocatorOwnerThread() { + allocator_owner_thread = std::this_thread::get_id(); +} + +bool OnAllocatorOwnerThread() noexcept { + return allocator_owner_thread == std::thread::id{} || + allocator_owner_thread == std::this_thread::get_id(); +} + void Image::SetObjectNameEXT(const char* name) const { SetObjectName(dld, owner, handle, VK_OBJECT_TYPE_IMAGE, name); } void Image::Release() const noexcept { if (handle) { + DEBUG_ASSERT(OnAllocatorOwnerThread()); vmaDestroyImage(allocator, handle, allocation); } } @@ -539,6 +553,7 @@ void Buffer::SetObjectNameEXT(const char* name) const { void Buffer::Release() const noexcept { if (handle) { + DEBUG_ASSERT(OnAllocatorOwnerThread()); vmaDestroyBuffer(allocator, handle, allocation); } } diff --git a/src/video_core/vulkan_common/vulkan_wrapper.h b/src/video_core/vulkan_common/vulkan_wrapper.h index fb94d04c74..fd122aa27b 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.h +++ b/src/video_core/vulkan_common/vulkan_wrapper.h @@ -131,6 +131,10 @@ private: VkResult result; }; +void SetAllocatorOwnerThread(); + +[[nodiscard]] bool OnAllocatorOwnerThread() noexcept; + /// Throws a Vulkan exception if result is not success. inline void Check(VkResult result) { if (result != VK_SUCCESS) {