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) {