diff --git a/src/common/host_memory.cpp b/src/common/host_memory.cpp index 8940ee7243..340a7d5031 100644 --- a/src/common/host_memory.cpp +++ b/src/common/host_memory.cpp @@ -700,7 +700,11 @@ public: constexpr u64 MapSlotsPerWindow = 64; const u64 affordable_windows = permissible_maps / MapSlotsPerWindow; budget = (std::min)(budget, affordable_windows * window_size); - budget = (std::min)(budget, static_cast(backing_size) / 2); + const u64 available = Common::GetAvailablePhysicalMemory(); + if (available != 0) { + budget = (std::min)(budget, available / 2); + } + budget = (std::min)(budget, static_cast(backing_size)); budget = Common::AlignDown(budget, window_size); constexpr u64 MinimumBudget = 256ULL << 20; if (budget < MinimumBudget) { diff --git a/src/common/memory_detect.cpp b/src/common/memory_detect.cpp index e21e058486..ae303a4f1a 100644 --- a/src/common/memory_detect.cpp +++ b/src/common/memory_detect.cpp @@ -22,6 +22,7 @@ #include #include +#include #include "common/memory_detect.h" @@ -97,4 +98,39 @@ u64 GetPermissibleMapCount() { return count - ReservedMaps; } +u64 GetAvailablePhysicalMemory() { +#ifdef _WIN32 + MEMORYSTATUSEX memorystatus; + memorystatus.dwLength = sizeof(memorystatus); + if (GlobalMemoryStatusEx(&memorystatus) == 0) { + return 0; + } + return memorystatus.ullAvailPhys; +#elif defined(__linux__) + static constexpr char AvailableKey[] = "MemAvailable:"; + if (std::FILE* const file = std::fopen("/proc/meminfo", "re")) { + char line[256]; + u64 available = 0; + while (std::fgets(line, sizeof(line), file) != nullptr) { + if (std::strncmp(line, AvailableKey, sizeof(AvailableKey) - 1) != 0) { + continue; + } + available = std::strtoull(line + sizeof(AvailableKey) - 1, nullptr, 10) * 1024; + break; + } + std::fclose(file); + if (available != 0) { + return available; + } + } + struct sysinfo meminfo; + if (sysinfo(&meminfo) != 0) { + return 0; + } + return static_cast(meminfo.freeram) * static_cast(meminfo.mem_unit); +#else + return 0; +#endif +} + } // namespace Common diff --git a/src/common/memory_detect.h b/src/common/memory_detect.h index 9a7ce7ab1c..367e3f754d 100644 --- a/src/common/memory_detect.h +++ b/src/common/memory_detect.h @@ -23,4 +23,6 @@ struct MemoryInfo { [[nodiscard]] u64 GetPermissibleMapCount(); +[[nodiscard]] u64 GetAvailablePhysicalMemory(); + } // namespace Common diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.cpp b/src/video_core/renderer_vulkan/vk_compute_pass.cpp index cb9132b997..6aeb482788 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pass.cpp +++ b/src/video_core/renderer_vulkan/vk_compute_pass.cpp @@ -13,6 +13,7 @@ #include "video_core/renderer_vulkan/vk_texture_cache.h" +#include "common/alignment.h" #include "common/assert.h" #include "common/common_types.h" #include "common/div_ceil.h" @@ -992,6 +993,31 @@ void BlockLinearUnswizzle2DPass::UnswizzleFrom( const u32 layers = image.info.resources.layers; const VkImageAspectFlags aspect = image.AspectMask(); + const VkDeviceSize output_alignment = + (std::max)(device.GetStorageBufferAlignment(), VkDeviceSize{16}); + VkDeviceSize total_output = 0; + for (const VideoCommon::SwizzleParameters& sw : swizzles) { + const auto params = + VideoCommon::Accelerated::MakeBlockLinearSwizzle2DParams(sw, image.info); + const VkDeviceSize level_size = static_cast(sw.num_tiles.width) * + sw.num_tiles.height * layers * + (1ULL << params.bytes_per_block_log2); + if (level_size == 0) { + continue; + } + total_output = Common::AlignUp(total_output, output_alignment) + level_size; + } + if (total_output == 0) { + return; + } + const StagingBufferRef output = + staging_buffer_pool.Request(static_cast(total_output), MemoryUsage::DeviceLocal); + const VkBuffer out_buffer = output.buffer; + if (out_buffer == VK_NULL_HANDLE) { + return; + } + VkDeviceSize level_offset = 0; + scheduler.RequestOutsideRenderPassOperationContext(); VkAccessFlags pre_access = VK_ACCESS_NONE; @@ -1040,8 +1066,9 @@ void BlockLinearUnswizzle2DPass::UnswizzleFrom( const u32 texel_width = (std::max)(1u, image.info.size.width >> level); const u32 texel_height = (std::max)(1u, image.info.size.height >> level); - const StagingBufferRef output = - staging_buffer_pool.Request(static_cast(output_size), MemoryUsage::DeviceLocal); + level_offset = Common::AlignUp(level_offset, output_alignment); + const VkDeviceSize out_offset = output.offset + level_offset; + level_offset += output_size; BlockLinearUnswizzle2DPushConstants pc{}; pc.dim = {width, height, layers}; @@ -1056,22 +1083,17 @@ void BlockLinearUnswizzle2DPass::UnswizzleFrom( compute_pass_descriptor_queue.Acquire(scheduler, 2); compute_pass_descriptor_queue.AddBuffer(source_buffer, sw.buffer_offset + source_offset, image.guest_size_bytes - sw.buffer_offset); - compute_pass_descriptor_queue.AddBuffer(output.buffer, output.offset, output_size); + compute_pass_descriptor_queue.AddBuffer(out_buffer, out_offset, output_size); const void* descriptor_data = compute_pass_descriptor_queue.UpdateData(); const VkDescriptorSet set = descriptor_allocator.Commit(); const u32 gx = Common::DivCeil(width, 16u); const u32 gy = Common::DivCeil(height, 8u); - const VkBuffer out_buffer = output.buffer; - const VkDeviceSize out_offset = output.offset; scheduler.Record([this, set, descriptor_data, pc, gx, gy, layers, output_size, out_buffer, out_offset, dst_image, aspect, texel_width, texel_height, level](vk::CommandBuffer cmdbuf) { - if (out_buffer == VK_NULL_HANDLE) { - return; - } device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data); cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline); cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *layout, 0, set, {}); diff --git a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp index 2be71f0a7c..8061aa1336 100644 --- a/src/video_core/vulkan_common/vulkan_memory_allocator.cpp +++ b/src/video_core/vulkan_common/vulkan_memory_allocator.cpp @@ -490,13 +490,29 @@ namespace Vulkan { VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT; const bool want_address = device.IsBufferDeviceAddressSupported(); + VkBufferUsageFlags minimal_usage = TransferUsage | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; if (want_address) { shader_usage |= VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT; + minimal_usage |= VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT; } + const auto reset_windows = [&] { + for (Window &window : windows) { + if (window.buffer != VK_NULL_HANDLE) { + logical.DestroyBufferRaw(window.buffer); + } + } + windows.clear(); + imported_size = 0; + }; + bindable = import_all(shader_usage, want_address); if (!bindable) { - imported_size = 0; + reset_windows(); + bindable = import_all(minimal_usage, want_address); + } + if (!bindable) { + reset_windows(); import_all(TransferUsage, false); } if (windows.empty()) {