diff --git a/src/shader_recompiler/environment.h b/src/shader_recompiler/environment.h index 20381cf467..d46d1c890e 100644 --- a/src/shader_recompiler/environment.h +++ b/src/shader_recompiler/environment.h @@ -78,6 +78,11 @@ public: [[nodiscard]] virtual TextureType ReadTextureType(u32 raw_handle) = 0; + [[nodiscard]] virtual u32 ResolveBindlessHandle(u32 cbuf_index, u32 cbuf_offset, + u32 raw_handle) { + return raw_handle; + } + [[nodiscard]] virtual TexturePixelFormat ReadTexturePixelFormat(u32 raw_handle) = 0; [[nodiscard]] virtual bool IsTexturePixelFormatInteger(u32 raw_handle) = 0; diff --git a/src/shader_recompiler/ir_opt/texture_pass.cpp b/src/shader_recompiler/ir_opt/texture_pass.cpp index 0d0144fcab..cd306038ff 100644 --- a/src/shader_recompiler/ir_opt/texture_pass.cpp +++ b/src/shader_recompiler/ir_opt/texture_pass.cpp @@ -502,7 +502,7 @@ u32 GetTextureHandle(Environment& env, const ConstBufferAddr& cbuf) { const u32 lhs_raw{env.ReadCbufValue(cbuf.index, cbuf.offset) << cbuf.shift_left}; const u32 rhs_raw{env.ReadCbufValue(secondary_index, secondary_offset) << cbuf.secondary_shift_left}; - return lhs_raw | rhs_raw; + return env.ResolveBindlessHandle(cbuf.index, cbuf.offset, lhs_raw | rhs_raw); } [[maybe_unused]] TextureType ReadTextureType(Environment& env, const ConstBufferAddr& cbuf) { diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp index 006bfc0a09..7fd435438b 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp @@ -18,6 +18,7 @@ #include "video_core/renderer_vulkan/vk_pipeline_cache.h" #include "video_core/renderer_vulkan/vk_scheduler.h" #include "video_core/renderer_vulkan/vk_update_descriptor.h" +#include "video_core/shader_environment.h" #include "video_core/shader_notify.h" #include "video_core/vulkan_common/vulkan_device.h" #include "video_core/vulkan_common/vulkan_wrapper.h" @@ -148,6 +149,7 @@ void ComputePipeline::Configure(Tegra::Engines::KeplerCompute& kepler_compute, const auto& qmd{kepler_compute.launch_description}; const auto& cbufs{qmd.const_buffer_config}; const bool via_header_index{qmd.linked_tsc != 0}; + const u32 tic_limit{kepler_compute.regs.tic.limit}; const auto read_handle{[&](const auto& desc, u32 index) { ASSERT(((qmd.const_buffer_enable_mask >> desc.cbuf_index) & 1) != 0); const u32 index_offset{index << desc.size_shift}; @@ -162,10 +164,17 @@ void ComputePipeline::Configure(Tegra::Engines::KeplerCompute& kepler_compute, secondary_offset}; const u32 lhs_raw{gpu_memory.Read(addr) << desc.shift_left}; const u32 rhs_raw{gpu_memory.Read(separate_addr) << desc.secondary_shift_left}; - return TexturePair(lhs_raw | rhs_raw, via_header_index); + const u32 combined{VideoCommon::ResolveBindlessHandleTable( + gpu_memory, addr, lhs_raw | rhs_raw, + TexturePair(lhs_raw | rhs_raw, via_header_index).first, tic_limit)}; + return TexturePair(combined, via_header_index); } } - return TexturePair(gpu_memory.Read(addr), via_header_index); + const u32 single_raw{gpu_memory.Read(addr)}; + return TexturePair(VideoCommon::ResolveBindlessHandleTable( + gpu_memory, addr, single_raw, + TexturePair(single_raw, via_header_index).first, tic_limit), + via_header_index); }}; const auto add_image{[&](const auto& desc, bool blacklist) { for (u32 index = 0; index < desc.count; ++index) { diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index d08ba94e29..fdbe9bfdf4 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -203,7 +203,8 @@ RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra : gpu{gpu_}, device_memory{device_memory_}, device{device_}, memory_allocator{memory_allocator_}, state_tracker{state_tracker_}, scheduler{scheduler_}, staging_pool(device, memory_allocator, scheduler), descriptor_pool(device, scheduler), - guest_descriptor_queue(device), compute_pass_descriptor_queue(device), + guest_descriptor_queue(device, UpdateDescriptorQueue::GUEST_FRAME_PAYLOAD_SIZE), + compute_pass_descriptor_queue(device, UpdateDescriptorQueue::COMPUTE_FRAME_PAYLOAD_SIZE), blit_image(device, scheduler, state_tracker, descriptor_pool), render_pass_cache(device), texture_cache_runtime{ device, scheduler, memory_allocator, staging_pool, diff --git a/src/video_core/renderer_vulkan/vk_update_descriptor.cpp b/src/video_core/renderer_vulkan/vk_update_descriptor.cpp index b0227ac908..44920617ff 100644 --- a/src/video_core/renderer_vulkan/vk_update_descriptor.cpp +++ b/src/video_core/renderer_vulkan/vk_update_descriptor.cpp @@ -16,11 +16,12 @@ namespace Vulkan { -UpdateDescriptorQueue::UpdateDescriptorQueue(const Device& device_) - : device{device_} +UpdateDescriptorQueue::UpdateDescriptorQueue(const Device& device_, size_t frame_payload_size_) + : device{device_}, frame_payload_size{frame_payload_size_}, + payload{std::make_unique(frame_payload_size_ * FRAMES_IN_FLIGHT)} { - payload_start = payload.data(); - payload_cursor = payload.data(); + payload_start = payload.get(); + payload_cursor = payload.get(); } UpdateDescriptorQueue::~UpdateDescriptorQueue() = default; @@ -29,19 +30,19 @@ void UpdateDescriptorQueue::TickFrame() { if (++frame_index >= FRAMES_IN_FLIGHT) { frame_index = 0; } - payload_start = payload.data() + frame_index * FRAME_PAYLOAD_SIZE; + payload_start = payload.get() + frame_index * frame_payload_size; payload_cursor = payload_start; } void UpdateDescriptorQueue::Acquire(Scheduler& scheduler, size_t required_entries) { static constexpr size_t DEFAULT_REQUIRED_ENTRIES = 0x400; const size_t reserve = required_entries > 0 ? required_entries : DEFAULT_REQUIRED_ENTRIES; - ASSERT_MSG(reserve < FRAME_PAYLOAD_SIZE, "Descriptor reservation {} >= frame capacity {}", - reserve, FRAME_PAYLOAD_SIZE); + ASSERT_MSG(reserve < frame_payload_size, "Descriptor reservation {} >= frame capacity {}", + reserve, frame_payload_size); const size_t used = static_cast(std::distance(payload_start, payload_cursor)); - if (used + reserve >= FRAME_PAYLOAD_SIZE) { + if (used + reserve >= frame_payload_size) { LOG_WARNING(Render_Vulkan, "Payload overflow (used={}, reserve={}, capacity={})", - used, reserve, FRAME_PAYLOAD_SIZE); + used, reserve, frame_payload_size); scheduler.WaitWorker(); payload_cursor = payload_start; } diff --git a/src/video_core/renderer_vulkan/vk_update_descriptor.h b/src/video_core/renderer_vulkan/vk_update_descriptor.h index db7e52bc03..a1f053e6d6 100644 --- a/src/video_core/renderer_vulkan/vk_update_descriptor.h +++ b/src/video_core/renderer_vulkan/vk_update_descriptor.h @@ -6,7 +6,7 @@ #pragma once -#include +#include #include #include "video_core/vulkan_common/vulkan_wrapper.h" @@ -30,11 +30,12 @@ class UpdateDescriptorQueue final { // This should be plenty for the vast majority of cases. Most desktop platforms only // provide up to 3 swapchain images. static constexpr size_t FRAMES_IN_FLIGHT = 8; - static constexpr size_t FRAME_PAYLOAD_SIZE = 0x40000; - static constexpr size_t PAYLOAD_SIZE = FRAME_PAYLOAD_SIZE * FRAMES_IN_FLIGHT; public: - explicit UpdateDescriptorQueue(const Device& device_); + static constexpr size_t GUEST_FRAME_PAYLOAD_SIZE = 0x80000; + static constexpr size_t COMPUTE_FRAME_PAYLOAD_SIZE = 0x20000; + + explicit UpdateDescriptorQueue(const Device& device_, size_t frame_payload_size_); ~UpdateDescriptorQueue(); void TickFrame(); @@ -74,11 +75,12 @@ public: private: const Device& device; + const size_t frame_payload_size; size_t frame_index{0}; DescriptorUpdateEntry* payload_cursor = nullptr; DescriptorUpdateEntry* payload_start = nullptr; const DescriptorUpdateEntry* upload_start = nullptr; - std::array payload; + std::unique_ptr payload; }; // TODO: should these be separate classes instead? diff --git a/src/video_core/shader_environment.cpp b/src/video_core/shader_environment.cpp index 4d5db7e511..d22c37fb74 100644 --- a/src/video_core/shader_environment.cpp +++ b/src/video_core/shader_environment.cpp @@ -281,6 +281,34 @@ std::optional GenericEnvironment::TryFindSize() { return std::nullopt; } +u32 ResolveBindlessHandleTable(Tegra::MemoryManager& gpu_memory, GPUVAddr record_addr, + u32 raw_handle, u32 tic_index, u32 tic_limit) { + static constexpr u32 HandleRecordSize = 16; + static constexpr u32 MaxHandleTableBytes = 64 * 1024; + + if (tic_index <= tic_limit) { + return raw_handle; + } + const u32 addr_low{gpu_memory.Read(record_addr)}; + const u32 addr_high{gpu_memory.Read(record_addr + 4)}; + const u32 table_size{gpu_memory.Read(record_addr + 8)}; + const GPUVAddr table_addr{(static_cast(addr_high) << 32) | + static_cast(addr_low)}; + if (table_addr == 0 || table_size < HandleRecordSize || table_size > MaxHandleTableBytes) { + return raw_handle; + } + const u32 count{table_size / HandleRecordSize}; + for (u32 i = 0; i < count; ++i) { + const GPUVAddr entry_addr{table_addr + static_cast(i) * HandleRecordSize}; + const u32 handle{gpu_memory.Read(entry_addr)}; + const u32 live{gpu_memory.Read(entry_addr + 4)}; + if (live != 0 && handle != 0 && handle <= tic_limit) { + return handle; + } + } + return raw_handle; +} + Tegra::Texture::TICEntry GenericEnvironment::ReadTextureInfo(GPUVAddr tic_addr, u32 tic_limit, bool via_header_index, u32 raw) { const auto handle{Tegra::Texture::TexturePair(raw, via_header_index)}; @@ -431,6 +459,24 @@ u32 ComputeEnvironment::ReadCbufValue(u32 cbuf_index, u32 cbuf_offset) { return value; } +u32 ComputeEnvironment::ResolveBindlessHandle(u32 cbuf_index, u32 cbuf_offset, u32 raw_handle) { + const auto& regs{kepler_compute->regs}; + const auto& qmd{kepler_compute->launch_description}; + const auto pair{Tegra::Texture::TexturePair(raw_handle, qmd.linked_tsc != 0)}; + if (pair.first <= regs.tic.limit) { + return raw_handle; + } + if (((qmd.const_buffer_enable_mask.Value() >> cbuf_index) & 1) == 0) { + return raw_handle; + } + const auto& cbuf{qmd.const_buffer_config[cbuf_index]}; + if (cbuf_offset + 12 > cbuf.size) { + return raw_handle; + } + return ResolveBindlessHandleTable(*gpu_memory, cbuf.Address() + cbuf_offset, raw_handle, + pair.first, regs.tic.limit); +} + Shader::TextureType ComputeEnvironment::ReadTextureType(u32 handle) { const auto& regs{kepler_compute->regs}; const auto& qmd{kepler_compute->launch_description}; diff --git a/src/video_core/shader_environment.h b/src/video_core/shader_environment.h index 95c6d72f89..d0b897c1a4 100644 --- a/src/video_core/shader_environment.h +++ b/src/video_core/shader_environment.h @@ -29,6 +29,9 @@ class Memorymanager; namespace VideoCommon { +[[nodiscard]] u32 ResolveBindlessHandleTable(Tegra::MemoryManager& gpu_memory, GPUVAddr record_addr, + u32 raw_handle, u32 tic_index, u32 tic_limit); + class GenericEnvironment : public Shader::Environment { public: explicit GenericEnvironment() = default; @@ -140,6 +143,8 @@ public: u32 ReadCbufValue(u32 cbuf_index, u32 cbuf_offset) override; + u32 ResolveBindlessHandle(u32 cbuf_index, u32 cbuf_offset, u32 raw_handle) override; + Shader::TextureType ReadTextureType(u32 handle) override; Shader::TexturePixelFormat ReadTexturePixelFormat(u32 handle) override;