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 b847b66e7c..5f04df2c9e 100644 --- a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp +++ b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp @@ -120,7 +120,14 @@ void StagingBufferPool::FreeDeferred(StagingBufferRef& ref) { } void StagingBufferPool::TickFrame() { - current_delete_level = (current_delete_level + 1) % NUM_LEVELS; + for (size_t step = 0; step < NUM_LEVELS; ++step) { + current_delete_level = (current_delete_level + 1) % NUM_LEVELS; + if (!device_local_cache[current_delete_level].entries.empty() || + !upload_cache[current_delete_level].entries.empty() || + !download_cache[current_delete_level].entries.empty()) { + break; + } + } ReleaseCache(MemoryUsage::DeviceLocal); ReleaseCache(MemoryUsage::Upload); @@ -128,10 +135,12 @@ void StagingBufferPool::TickFrame() { } StagingBufferRef StagingBufferPool::GetStreamBuffer(size_t size) { - if (AreRegionsActive(Region(free_iterator) + 1, - (std::min)(Region(iterator + size) + 1, NUM_SYNCS))) { - // Avoid waiting for the previous usages to be free - return GetStagingBuffer(size, MemoryUsage::Upload); + const size_t wanted_begin = Region(free_iterator) + 1; + const size_t wanted_end = (std::min)(Region(iterator + size) + 1, NUM_SYNCS); + if (AreRegionsActive(wanted_begin, wanted_end)) { + if (auto ref = OverflowStreamBuffer(size, wanted_begin, wanted_end)) { + return *ref; + } } const u64 current_tick = scheduler.CurrentTick(); std::fill(sync_ticks.begin() + Region(used_iterator), sync_ticks.begin() + Region(iterator), @@ -147,8 +156,9 @@ StagingBufferRef StagingBufferPool::GetStreamBuffer(size_t size) { free_iterator = size; if (AreRegionsActive(0, Region(size) + 1)) { - // Avoid waiting for the previous usages to be free - return GetStagingBuffer(size, MemoryUsage::Upload); + if (auto ref = OverflowStreamBuffer(size, 0, Region(size) + 1)) { + return *ref; + } } } const size_t offset = iterator; @@ -164,6 +174,27 @@ StagingBufferRef StagingBufferPool::GetStreamBuffer(size_t size) { }; } +u64 StagingBufferPool::MaxRegionTick(size_t region_begin, size_t region_end) const { + u64 tick = 0; + for (size_t region = region_begin; region < region_end; ++region) { + tick = (std::max)(tick, sync_ticks[region]); + } + return tick; +} + +std::optional StagingBufferPool::OverflowStreamBuffer(size_t size, + size_t region_begin, + size_t region_end) { + if (cache_bytes[static_cast(MemoryUsage::Upload)] < stream_buffer_size) { + return GetStagingBuffer(size, MemoryUsage::Upload); + } + if (auto ref = TryGetReservedBuffer(size, MemoryUsage::Upload, false)) { + return ref; + } + scheduler.Wait(MaxRegionTick(region_begin, region_end)); + return std::nullopt; +} + bool StagingBufferPool::AreRegionsActive(size_t region_begin, size_t region_end) const { const u64 gpu_tick = scheduler.GetMasterSemaphore().KnownGpuTick(); return std::any_of(sync_ticks.begin() + region_begin, sync_ticks.begin() + region_end, @@ -233,6 +264,7 @@ StagingBufferRef StagingBufferPool::CreateStagingBuffer(size_t size, MemoryUsage device.IsBufferDeviceAddressSupported() ? device.GetLogical().GetBufferDeviceAddress(*buffer) : VkDeviceAddress{}; + cache_bytes[static_cast(usage)] += size_t{1} << log2_size; StagingBuffer& entry = GetCache(usage)[log2_size].entries.emplace_back(StagingBuffer{ .buffer = std::move(buffer), .device_address = buffer_address, @@ -261,12 +293,12 @@ StagingBufferPool::StagingBuffersCache& StagingBufferPool::GetCache(MemoryUsage } void StagingBufferPool::ReleaseCache(MemoryUsage usage) { - ReleaseLevel(GetCache(usage), current_delete_level); + ReleaseLevel(usage, current_delete_level); } -void StagingBufferPool::ReleaseLevel(StagingBuffersCache& cache, size_t log2) { +void StagingBufferPool::ReleaseLevel(MemoryUsage usage, size_t log2) { constexpr size_t deletions_per_tick = 16; - auto& staging = cache[log2]; + auto& staging = GetCache(usage)[log2]; auto& entries = staging.entries; const size_t old_size = entries.size(); @@ -280,6 +312,7 @@ void StagingBufferPool::ReleaseLevel(StagingBuffersCache& cache, size_t log2) { entries.erase(std::remove_if(begin, end, is_deletable), end); const size_t new_size = entries.size(); + cache_bytes[static_cast(usage)] -= (old_size - new_size) << log2; staging.delete_index += deletions_per_tick; if (staging.delete_index >= new_size) { staging.delete_index = 0; 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 6d967abb41..d684a1289b 100644 --- a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h +++ b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h @@ -7,6 +7,7 @@ #pragma once #include +#include #include #include "common/common_types.h" @@ -88,6 +89,11 @@ private: bool AreRegionsActive(size_t region_begin, size_t region_end) const; + u64 MaxRegionTick(size_t region_begin, size_t region_end) const; + + std::optional OverflowStreamBuffer(size_t size, size_t region_begin, + size_t region_end); + StagingBufferRef GetStagingBuffer(size_t size, MemoryUsage usage, bool deferred = false); std::optional TryGetReservedBuffer(size_t size, MemoryUsage usage, @@ -99,7 +105,7 @@ private: void ReleaseCache(MemoryUsage usage); - void ReleaseLevel(StagingBuffersCache& cache, size_t log2); + void ReleaseLevel(MemoryUsage usage, size_t log2); size_t Region(size_t iter) const noexcept { return iter / region_size; } @@ -119,6 +125,8 @@ private: size_t free_iterator = 0; std::array sync_ticks{}; + std::array cache_bytes{}; + StagingBuffersCache device_local_cache; StagingBuffersCache upload_cache; StagingBuffersCache download_cache; diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 749a49c898..33ab5dd57f 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -1091,11 +1091,12 @@ bool TextureCacheRuntime::ShouldReinterpret(Image& dst, Image& src) { } VkBuffer TextureCacheRuntime::GetTemporaryBuffer(size_t needed_size) { - const auto level = (8 * sizeof(size_t)) - std::countl_zero(needed_size - 1ULL); + const size_t wanted_size = (std::max)(needed_size, size_t{1}); + const auto level = (8 * sizeof(size_t)) - std::countl_zero(wanted_size - 1ULL); if (buffers[level]) { return *buffers[level]; } - const auto new_size = Common::NextPow2(needed_size); + const auto new_size = Common::NextPow2(wanted_size); static constexpr VkBufferUsageFlags flags = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT; diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index 65194f647a..8fd72cb71f 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -741,7 +741,7 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR .pHeapSizeLimit = nullptr, .pVulkanFunctions = &functions, .instance = instance, - .vulkanApiVersion = ApiVersion(), + .vulkanApiVersion = VK_API_VERSION_1_1, .pTypeExternalMemoryHandleTypes = nullptr, }; diff --git a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp index 31a909ef2f..ef1143aeb6 100644 --- a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp +++ b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp @@ -26,6 +26,13 @@ namespace { : VkMemoryPropertyFlagBits{}; } + [[nodiscard]] VkMemoryPropertyFlags MemoryUsageRequiredVmaFlags(MemoryUsage usage) { + if (usage == MemoryUsage::Upload || usage == MemoryUsage::Download) { + return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; + } + return VkMemoryPropertyFlagBits{}; + } + [[nodiscard]] VmaAllocationCreateFlags MemoryUsageVmaFlags(MemoryUsage usage) { switch (usage) { case MemoryUsage::Upload: @@ -103,7 +110,7 @@ vk::Image MemoryAllocator::CreateImage(const VkImageCreateInfo &ci) const .usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE, .requiredFlags = 0, .preferredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, - .memoryTypeBits = 0, + .memoryTypeBits = valid_memory_types, .pool = VK_NULL_HANDLE, .pUserData = nullptr, .priority = 0.f, @@ -138,7 +145,7 @@ vk::Buffer MemoryAllocator::CreateBuffer(const VkBufferCreateInfo &ci, MemoryUsa VmaAllocationCreateInfo alloc_ci = { .flags = VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT | MemoryUsageVmaFlags(usage), .usage = MemoryUsageVma(usage), - .requiredFlags = 0, + .requiredFlags = MemoryUsageRequiredVmaFlags(usage), .preferredFlags = MemoryUsagePreferredVmaFlags(usage) | anv_flags, .memoryTypeBits = usage == MemoryUsage::Stream ? 0u : valid_memory_types, .pool = VK_NULL_HANDLE, @@ -196,7 +203,7 @@ vk::Buffer MemoryAllocator::CreateBuffer(const VkBufferCreateInfo &ci, MemoryUsa VmaAllocationCreateInfo alloc_ci = { .flags = VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT | MemoryUsageVmaFlags(usage), .usage = MemoryUsageVma(usage), - .requiredFlags = 0, + .requiredFlags = MemoryUsageRequiredVmaFlags(usage), .preferredFlags = MemoryUsagePreferredVmaFlags(usage) | anv_flags, .memoryTypeBits = memory_type_bits, .pool = VK_NULL_HANDLE,