diff options
Diffstat (limited to 'src/video_core/renderer_vulkan')
18 files changed, 336 insertions, 113 deletions
diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp index 866b721a84..d53cf70b37 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp @@ -190,9 +190,7 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe } } } - if (!extended_dynamic_state_3_enables) { - dynamic_state.Refresh3(regs); - } + dynamic_state.Refresh3(regs, features); if (xfb_enabled) { RefreshXfbState(xfb_state, regs); } @@ -295,16 +293,22 @@ void FixedPipelineState::DynamicState::Refresh2(const Maxwell& regs, depth_bias_enable.Assign(enabled_lut[POLYGON_OFFSET_ENABLE_LUT[topology_index]] != 0 ? 1 : 0); } -void FixedPipelineState::DynamicState::Refresh3(const Maxwell& regs) { - logic_op_enable.Assign(regs.logic_op.enable != 0 ? 1 : 0); - depth_clamp_disabled.Assign(regs.viewport_clip_control.geometry_clip == - Maxwell::ViewportClipControl::GeometryClip::Passthrough || - regs.viewport_clip_control.geometry_clip == - Maxwell::ViewportClipControl::GeometryClip::FrustumXYZ || - regs.viewport_clip_control.geometry_clip == - Maxwell::ViewportClipControl::GeometryClip::FrustumZ); - - line_stipple_enable.Assign(regs.line_stipple_enable); +void FixedPipelineState::DynamicState::Refresh3(const Maxwell& regs, + const DynamicFeatures& features) { + if (!features.has_dynamic_state3_logic_op_enable) { + logic_op_enable.Assign(regs.logic_op.enable != 0 ? 1 : 0); + } + if (!features.has_dynamic_state3_depth_clamp_enable) { + depth_clamp_disabled.Assign(regs.viewport_clip_control.geometry_clip == + Maxwell::ViewportClipControl::GeometryClip::Passthrough || + regs.viewport_clip_control.geometry_clip == + Maxwell::ViewportClipControl::GeometryClip::FrustumXYZ || + regs.viewport_clip_control.geometry_clip == + Maxwell::ViewportClipControl::GeometryClip::FrustumZ); + } + if (!features.has_dynamic_state3_line_stipple_enable) { + line_stipple_enable.Assign(regs.line_stipple_enable); + } } size_t FixedPipelineState::Hash() const noexcept { diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.h b/src/video_core/renderer_vulkan/fixed_pipeline_state.h index ffc91e9a55..44157d686d 100644 --- a/src/video_core/renderer_vulkan/fixed_pipeline_state.h +++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.h @@ -27,6 +27,9 @@ struct DynamicFeatures { bool has_extended_dynamic_state_2_patch_control_points; bool has_extended_dynamic_state_3_blend; bool has_extended_dynamic_state_3_enables; + bool has_dynamic_state3_depth_clamp_enable; + bool has_dynamic_state3_logic_op_enable; + bool has_dynamic_state3_line_stipple_enable; bool has_dynamic_vertex_input; bool has_provoking_vertex; bool has_provoking_vertex_first_mode; @@ -175,7 +178,7 @@ struct FixedPipelineState { void Refresh(const Maxwell& regs); void Refresh2(const Maxwell& regs, Maxwell::PrimitiveTopology topology, bool base_features_supported); - void Refresh3(const Maxwell& regs); + void Refresh3(const Maxwell& regs, const DynamicFeatures& features); Maxwell::ComparisonOp DepthTestFunc() const noexcept { return UnpackComparisonOp(depth_test_func); @@ -265,8 +268,7 @@ struct FixedPipelineState { return sizeof(*this); } if (dynamic_vertex_input && extended_dynamic_state_3_blend) { - // Exclude dynamic state and attributes - return offsetof(FixedPipelineState, dynamic_state); + return offsetof(FixedPipelineState, attachments); } if (dynamic_vertex_input) { // Exclude dynamic state diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index 010cfd225d..2deec13ace 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp @@ -8,6 +8,7 @@ #include <array> #include <cstring> #include <memory> +#include <mutex> #include <optional> #include <string> #include <vector> @@ -171,7 +172,11 @@ try RendererVulkan::~RendererVulkan() { scheduler.RegisterOnSubmit([] {}); - void(device.GetLogical().WaitIdle()); + scheduler.Finish(); + { + std::scoped_lock lock{scheduler.submit_mutex}; + void(device.GetLogical().WaitIdle()); + } } void RendererVulkan::Composite(std::span<const Tegra::FramebufferConfig> framebuffers) { diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp index bb7eb9bdaa..75a8c3bf91 100644 --- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp +++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp @@ -8,6 +8,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include <vulkan/vulkan_core.h> +#include <mutex> #include "video_core/framebuffer_config.h" #include "video_core/present.h" #include "video_core/renderer_vulkan/present/filters.h" @@ -31,7 +32,10 @@ BlitScreen::~BlitScreen() = default; void BlitScreen::WaitIdle() { present_manager.WaitPresent(); scheduler.Finish(); - device.GetLogical().WaitIdle(); + { + std::scoped_lock lock{scheduler.submit_mutex}; + device.GetLogical().WaitIdle(); + } } void BlitScreen::SetWindowAdaptPass() { diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp index 74f06427dd..a359502046 100644 --- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp @@ -637,12 +637,10 @@ void BufferCacheRuntime::BindTransformFeedbackBuffers(VideoCommon::HostBindings< for (u32 i = 0; i < bindings.buffers.size(); ++i) { auto handle = bindings.buffers[i]->Handle(); if (handle == VK_NULL_HANDLE) { + ReserveNullBuffer(); + handle = *null_buffer; bindings.offsets[i] = 0; - bindings.sizes[i] = VK_WHOLE_SIZE; - if (!device.HasNullDescriptor()) { - ReserveNullBuffer(); - handle = *null_buffer; - } + bindings.sizes[i] = 0; } buffer_handles[i] = handle; } diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.cpp b/src/video_core/renderer_vulkan/vk_compute_pass.cpp index d45a57f7bb..f198b65d69 100644 --- a/src/video_core/renderer_vulkan/vk_compute_pass.cpp +++ b/src/video_core/renderer_vulkan/vk_compute_pass.cpp @@ -228,6 +228,10 @@ struct QueriesPrefixScanPushConstants { u32 accumulation_limit; u32 buffer_offset; }; + +struct ConditionalRenderingResolvePushConstants { + u32 compare_to_zero; +}; } // Anonymous namespace ComputePass::ComputePass(const Device& device_, DescriptorPool& descriptor_pool, @@ -413,7 +417,8 @@ ConditionalRenderingResolvePass::ConditionalRenderingResolvePass( const Device& device_, Scheduler& scheduler_, DescriptorPool& descriptor_pool_, ComputePassDescriptorQueue& compute_pass_descriptor_queue_) : ComputePass(device_, descriptor_pool_, INPUT_OUTPUT_DESCRIPTOR_SET_BINDINGS, - INPUT_OUTPUT_DESCRIPTOR_UPDATE_TEMPLATE, INPUT_OUTPUT_BANK_INFO, nullptr, + INPUT_OUTPUT_DESCRIPTOR_UPDATE_TEMPLATE, INPUT_OUTPUT_BANK_INFO, + COMPUTE_PUSH_CONSTANT_RANGE<sizeof(ConditionalRenderingResolvePushConstants)>, RESOLVE_CONDITIONAL_RENDER_COMP_SPV), scheduler{scheduler_}, compute_pass_descriptor_queue{compute_pass_descriptor_queue_} {} @@ -430,7 +435,7 @@ void ConditionalRenderingResolvePass::Resolve(VkBuffer dst_buffer, VkBuffer src_ const void* const descriptor_data{compute_pass_descriptor_queue.UpdateData()}; scheduler.RequestOutsideRenderPassOperationContext(); - scheduler.Record([this, descriptor_data](vk::CommandBuffer cmdbuf) { + scheduler.Record([this, descriptor_data, compare_to_zero](vk::CommandBuffer cmdbuf) { static constexpr VkMemoryBarrier read_barrier{ .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, .pNext = nullptr, @@ -443,6 +448,9 @@ void ConditionalRenderingResolvePass::Resolve(VkBuffer dst_buffer, VkBuffer src_ .srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT, .dstAccessMask = VK_ACCESS_CONDITIONAL_RENDERING_READ_BIT_EXT, }; + const ConditionalRenderingResolvePushConstants uniforms{ + .compare_to_zero = compare_to_zero ? 1U : 0U, + }; const VkDescriptorSet set = descriptor_allocator.Commit(); device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data); @@ -450,9 +458,11 @@ void ConditionalRenderingResolvePass::Resolve(VkBuffer dst_buffer, VkBuffer src_ VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, read_barrier); cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline); cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *layout, 0, set, {}); + cmdbuf.PushConstants(*layout, VK_SHADER_STAGE_COMPUTE_BIT, uniforms); cmdbuf.Dispatch(1, 1, 1); cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, - VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, write_barrier); + VK_PIPELINE_STAGE_CONDITIONAL_RENDERING_BIT_EXT, 0, + write_barrier); }); } @@ -520,7 +530,7 @@ void QueriesPrefixScanPass::Run(VkBuffer accumulation_buffer, VkBuffer dst_buffe const VkDescriptorSet set = descriptor_allocator.Commit(); device.GetLogical().UpdateDescriptorSet(set, *descriptor_template, descriptor_data); - cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, read_barrier); cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline); cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *layout, 0, set, {}); diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp index 8f706a02c8..43fbefe425 100644 --- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp +++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp @@ -467,6 +467,10 @@ bool GraphicsPipeline::ConfigureImpl(bool is_indexed) { bind_stage_info(4); } + if (regs.transform_feedback_enabled != 0) { + scheduler.RequestOutsideRenderPassOperationContext(); + } + buffer_cache.UpdateGraphicsBuffers(is_indexed); buffer_cache.BindHostGeometryBuffers(is_indexed); diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 5f86506961..7b5ac7fac0 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -485,6 +485,12 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, device.IsExtExtendedDynamicState3BlendingSupported(); dynamic_features.has_extended_dynamic_state_3_enables = device.IsExtExtendedDynamicState3EnablesSupported(); + dynamic_features.has_dynamic_state3_depth_clamp_enable = + device.SupportsDynamicState3DepthClampEnable(); + dynamic_features.has_dynamic_state3_logic_op_enable = + device.SupportsDynamicState3LogicOpEnable(); + dynamic_features.has_dynamic_state3_line_stipple_enable = + device.SupportsDynamicState3LineStippleEnable(); // VIDS: Independent toggle (not affected by dyna_state levels) dynamic_features.has_dynamic_vertex_input = diff --git a/src/video_core/renderer_vulkan/vk_present_manager.cpp b/src/video_core/renderer_vulkan/vk_present_manager.cpp index 80853362ad..de854554c7 100644 --- a/src/video_core/renderer_vulkan/vk_present_manager.cpp +++ b/src/video_core/renderer_vulkan/vk_present_manager.cpp @@ -189,7 +189,7 @@ void PresentManager::RecreateFrame(Frame* frame, u32 width, u32 height, VkFormat frame->image = memory_allocator.CreateImage({ .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, .pNext = nullptr, - .flags = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT, + .flags = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT | VK_IMAGE_CREATE_EXTENDED_USAGE_BIT, .imageType = VK_IMAGE_TYPE_2D, .format = swapchain.GetImageFormat(), .extent = diff --git a/src/video_core/renderer_vulkan/vk_query_cache.cpp b/src/video_core/renderer_vulkan/vk_query_cache.cpp index 8518d89eee..6aea5d18a8 100644 --- a/src/video_core/renderer_vulkan/vk_query_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_query_cache.cpp @@ -157,8 +157,9 @@ public: ReserveHostQuery(); scheduler.Record([query_pool = current_query_pool, - query_index = current_bank_slot](vk::CommandBuffer cmdbuf) { + query_index = current_bank_slot](vk::CommandBuffer cmdbuf) { const bool use_precise = Settings::IsGPULevelHigh(); + cmdbuf.ResetQueryPool(query_pool, static_cast<u32>(query_index), 1); cmdbuf.BeginQuery(query_pool, static_cast<u32>(query_index), use_precise ? VK_QUERY_CONTROL_PRECISE_BIT : 0); }); @@ -220,8 +221,7 @@ public: } PauseCounter(); const auto driver_id = device.GetDriverID(); - if (driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY || - driver_id == VK_DRIVER_ID_ARM_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_TURNIP) { + if (driver_id == VK_DRIVER_ID_ARM_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_TURNIP) { pending_sync.clear(); sync_values_stash.clear(); return; @@ -666,13 +666,18 @@ public: offsets.fill(0); last_queries.fill(0); last_queries_stride.fill(1); + stream_to_slot.fill(INVALID_SLOT); + VkBufferUsageFlags counter_buffer_usage = + VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; + if (device.IsExtTransformFeedbackSupported()) { + counter_buffer_usage |= VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_COUNTER_BUFFER_BIT_EXT; + } const VkBufferCreateInfo buffer_ci = { .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, .pNext = nullptr, .flags = 0, .size = TFBQueryBank::QUERY_SIZE * NUM_STREAMS, - .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | - VK_BUFFER_USAGE_TRANSFORM_FEEDBACK_COUNTER_BUFFER_BIT_EXT, + .usage = counter_buffer_usage, .sharingMode = VK_SHARING_MODE_EXCLUSIVE, .queueFamilyIndexCount = 0, .pQueueFamilyIndices = nullptr, @@ -692,6 +697,9 @@ public: ~TFBCounterStreamer() = default; void StartCounter() override { + if (!device.IsExtTransformFeedbackSupported()) { + return; + } FlushBeginTFB(); has_started = true; } @@ -706,7 +714,9 @@ public: void CloseCounter() override { if (has_flushed_end_pending) { - FlushEndTFB(); + if (scheduler.IsRenderPassActive()) { + FlushEndTFB(); + } } runtime.View3DRegs([this](Maxwell3D& maxwell3d) { if (maxwell3d.regs.transform_feedback_enabled == 0) { @@ -756,18 +766,33 @@ public: if (has_timestamp) { new_query->flags |= VideoCommon::QueryFlagBits::HasTimestamp; } + if (!device.IsExtTransformFeedbackSupported()) { + new_query->flags |= VideoCommon::QueryFlagBits::IsFinalValueSynced; + return index; + } if (!subreport_) { new_query->flags |= VideoCommon::QueryFlagBits::IsFinalValueSynced; return index; } const size_t subreport = static_cast<size_t>(*subreport_); + if (subreport >= NUM_STREAMS) { + new_query->flags |= VideoCommon::QueryFlagBits::IsFinalValueSynced; + return index; + } last_queries[subreport] = address; if ((streams_mask & (1ULL << subreport)) == 0) { new_query->flags |= VideoCommon::QueryFlagBits::IsFinalValueSynced; return index; } + const size_t slot = stream_to_slot[subreport]; + if (slot >= NUM_STREAMS) { + new_query->flags |= VideoCommon::QueryFlagBits::IsFinalValueSynced; + return index; + } + + scheduler.RequestOutsideRenderPassOperationContext(); CloseCounter(); - auto [bank_slot, data_slot] = ProduceCounterBuffer(subreport); + auto [bank_slot, data_slot] = ProduceCounterBuffer(slot); new_query->start_bank_id = static_cast<u32>(bank_slot); new_query->size_banks = 1; new_query->start_slot = static_cast<u32>(data_slot); @@ -778,6 +803,9 @@ public: } std::optional<std::pair<DAddr, size_t>> GetLastQueryStream(size_t stream) { + if (stream >= NUM_STREAMS) { + return std::nullopt; + } if (last_queries[stream] != 0) { std::pair<DAddr, size_t> result(last_queries[stream], last_queries_stride[stream]); return result; @@ -789,6 +817,10 @@ public: return out_topology; } + u32 GetPatchVertices() const { + return patch_vertices; + } + bool HasUnsyncedQueries() const override { return !pending_flush_queries.empty(); } @@ -855,6 +887,9 @@ public: private: void FlushBeginTFB() { + if (!device.IsExtTransformFeedbackSupported()) [[unlikely]] { + return; + } if (has_flushed_end_pending) [[unlikely]] { return; } @@ -868,12 +903,24 @@ private: }); return; } + static constexpr VkMemoryBarrier COUNTER_RESUME_BARRIER{ + .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_WRITE_BIT_EXT, + .dstAccessMask = VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_READ_BIT_EXT, + }; scheduler.Record([this, total = static_cast<u32>(buffers_count)](vk::CommandBuffer cmdbuf) { + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFORM_FEEDBACK_BIT_EXT, + VK_PIPELINE_STAGE_TRANSFORM_FEEDBACK_BIT_EXT, 0, + COUNTER_RESUME_BARRIER); cmdbuf.BeginTransformFeedbackEXT(0, total, counter_buffers.data(), offsets.data()); }); } void FlushEndTFB() { + if (!device.IsExtTransformFeedbackSupported()) [[unlikely]] { + return; + } if (!has_flushed_end_pending) [[unlikely]] { UNREACHABLE(); return; @@ -899,28 +946,48 @@ private: void UpdateBuffers() { last_queries.fill(0); last_queries_stride.fill(1); + stream_to_slot.fill(INVALID_SLOT); streams_mask = 0; // reset previously recorded streams runtime.View3DRegs([this](Maxwell3D& maxwell3d) { buffers_count = 0; out_topology = maxwell3d.draw_manager->GetDrawState().topology; + patch_vertices = std::max(maxwell3d.regs.patch_vertices, 1U); + if (out_topology == Maxwell3D::Regs::PrimitiveTopology::Patches) { + switch (maxwell3d.regs.tessellation.params.output_primitives.Value()) { + case Maxwell3D::Regs::Tessellation::OutputPrimitives::Points: + out_topology = Maxwell3D::Regs::PrimitiveTopology::Points; + break; + case Maxwell3D::Regs::Tessellation::OutputPrimitives::Lines: + out_topology = Maxwell3D::Regs::PrimitiveTopology::LineStrip; + break; + case Maxwell3D::Regs::Tessellation::OutputPrimitives::Triangles_CW: + case Maxwell3D::Regs::Tessellation::OutputPrimitives::Triangles_CCW: + out_topology = Maxwell3D::Regs::PrimitiveTopology::TriangleStrip; + break; + } + } for (size_t i = 0; i < Maxwell3D::Regs::NumTransformFeedbackBuffers; i++) { const auto& tf = maxwell3d.regs.transform_feedback; if (tf.buffers[i].enable == 0) { continue; } + buffers_count = std::max<size_t>(buffers_count, i + 1); const size_t stream = tf.controls[i].stream; if (stream >= last_queries_stride.size()) { LOG_WARNING(Render_Vulkan, "TransformFeedback stream {} out of range", stream); continue; } + if ((streams_mask & (1ULL << stream)) != 0) { + continue; + } last_queries_stride[stream] = tf.controls[i].stride; + stream_to_slot[stream] = i; streams_mask |= 1ULL << stream; - buffers_count = std::max<size_t>(buffers_count, stream + 1); } }); } - std::pair<size_t, size_t> ProduceCounterBuffer(size_t stream) { + std::pair<size_t, size_t> ProduceCounterBuffer(size_t slot_index) { if (current_bank == nullptr || current_bank->IsClosed()) { current_bank_id = bank_pool.ReserveBank([this](std::deque<TFBQueryBank>& queue, size_t index) { @@ -946,7 +1013,8 @@ private: }; scheduler.RequestOutsideRenderPassOperationContext(); scheduler.Record([dst_buffer = current_bank->GetBuffer(), - src_buffer = counter_buffers[stream], src_offset = offsets[stream], + src_buffer = counter_buffers[slot_index], + src_offset = offsets[slot_index], slot](vk::CommandBuffer cmdbuf) { cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFORM_FEEDBACK_BIT_EXT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, READ_BARRIER); @@ -965,6 +1033,7 @@ private: friend class PrimitivesSucceededStreamer; static constexpr size_t NUM_STREAMS = 4; + static constexpr size_t INVALID_SLOT = NUM_STREAMS; QueryCacheRuntime& runtime; const Device& device; @@ -994,7 +1063,9 @@ private: std::array<VkDeviceSize, NUM_STREAMS> offsets{}; std::array<DAddr, NUM_STREAMS> last_queries; std::array<size_t, NUM_STREAMS> last_queries_stride; + std::array<size_t, NUM_STREAMS> stream_to_slot; Maxwell3D::Regs::PrimitiveTopology out_topology; + u32 patch_vertices{1}; u64 streams_mask; }; @@ -1015,6 +1086,7 @@ public: u64 stride{}; DAddr dependant_address{}; Maxwell3D::Regs::PrimitiveTopology topology{Maxwell3D::Regs::PrimitiveTopology::Points}; + u32 patch_vertices{1}; size_t dependant_index{}; bool dependant_manage{}; }; @@ -1031,6 +1103,10 @@ public: ~PrimitivesSucceededStreamer() = default; + void ResetCounter() override { + tfb_streamer.ResetCounter(); + } + size_t WriteCounter(DAddr address, bool has_timestamp, u32 value, std::optional<u32> subreport_) override { auto index = BuildQuery(); @@ -1048,6 +1124,7 @@ public: auto dependant_address_opt = tfb_streamer.GetLastQueryStream(subreport); bool must_manage_dependance = false; new_query->topology = tfb_streamer.GetOutputTopology(); + new_query->patch_vertices = tfb_streamer.GetPatchVertices(); if (dependant_address_opt) { auto [dep_address, stride] = *dependant_address_opt; new_query->dependant_address = dep_address; @@ -1068,6 +1145,7 @@ public: } new_query->stride = 1; runtime.View3DRegs([new_query, subreport](Maxwell3D& maxwell3d) { + new_query->patch_vertices = std::max(maxwell3d.regs.patch_vertices, 1U); for (size_t i = 0; i < Maxwell3D::Regs::NumTransformFeedbackBuffers; i++) { const auto& tf = maxwell3d.regs.transform_feedback; if (tf.buffers[i].enable == 0) { @@ -1131,27 +1209,39 @@ public: } } query->value = [&]() -> u64 { + const auto saturating_subtract = [](u64 value, u64 amount) { + return value > amount ? value - amount : 0; + }; switch (query->topology) { case Maxwell3D::Regs::PrimitiveTopology::Points: return num_vertices; case Maxwell3D::Regs::PrimitiveTopology::Lines: return num_vertices / 2; case Maxwell3D::Regs::PrimitiveTopology::LineLoop: - return (num_vertices / 2) + 1; + return num_vertices > 1 ? num_vertices : 0; case Maxwell3D::Regs::PrimitiveTopology::LineStrip: - return num_vertices - 1; - case Maxwell3D::Regs::PrimitiveTopology::Patches: + return saturating_subtract(num_vertices, 1); + case Maxwell3D::Regs::PrimitiveTopology::LinesAdjacency: + return num_vertices / 4; + case Maxwell3D::Regs::PrimitiveTopology::LineStripAdjacency: + return saturating_subtract(num_vertices, 3); case Maxwell3D::Regs::PrimitiveTopology::Triangles: - case Maxwell3D::Regs::PrimitiveTopology::TrianglesAdjacency: return num_vertices / 3; + case Maxwell3D::Regs::PrimitiveTopology::TrianglesAdjacency: + return num_vertices / 6; case Maxwell3D::Regs::PrimitiveTopology::TriangleFan: case Maxwell3D::Regs::PrimitiveTopology::TriangleStrip: + return saturating_subtract(num_vertices, 2); case Maxwell3D::Regs::PrimitiveTopology::TriangleStripAdjacency: - return num_vertices - 2; + return num_vertices > 4 ? (num_vertices - 4) / 2 : 0; case Maxwell3D::Regs::PrimitiveTopology::Quads: return num_vertices / 4; + case Maxwell3D::Regs::PrimitiveTopology::QuadStrip: + return num_vertices > 2 ? (num_vertices - 2) / 2 : 0; case Maxwell3D::Regs::PrimitiveTopology::Polygon: - return 1U; + return num_vertices >= 3 ? 1U : 0U; + case Maxwell3D::Regs::PrimitiveTopology::Patches: + return num_vertices / std::max<u64>(query->patch_vertices, 1U); default: return num_vertices; } @@ -1202,16 +1292,24 @@ struct QueryCacheRuntimeImpl { hcr_setup.pNext = nullptr; hcr_setup.flags = 0; - conditional_resolve_pass = std::make_unique<ConditionalRenderingResolvePass>( - device, scheduler, descriptor_pool, compute_pass_descriptor_queue); + const bool has_conditional_rendering = device.IsExtConditionalRendering(); + if (has_conditional_rendering) { + conditional_resolve_pass = std::make_unique<ConditionalRenderingResolvePass>( + device, scheduler, descriptor_pool, compute_pass_descriptor_queue); + } + + VkBufferUsageFlags hcr_buffer_usage = + VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; + if (has_conditional_rendering) { + hcr_buffer_usage |= VK_BUFFER_USAGE_CONDITIONAL_RENDERING_BIT_EXT; + } const VkBufferCreateInfo buffer_ci = { .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, .pNext = nullptr, .flags = 0, .size = sizeof(u32), - .usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | - VK_BUFFER_USAGE_CONDITIONAL_RENDERING_BIT_EXT, + .usage = hcr_buffer_usage, .sharingMode = VK_SHARING_MODE_EXCLUSIVE, .queueFamilyIndexCount = 0, .pQueueFamilyIndices = nullptr, @@ -1338,15 +1436,17 @@ void QueryCacheRuntime::HostConditionalRenderingCompareValueImpl(VideoCommon::Lo } } -void QueryCacheRuntime::HostConditionalRenderingCompareBCImpl(DAddr address, bool is_equal) { +void QueryCacheRuntime::HostConditionalRenderingCompareBCImpl(DAddr address, bool is_equal, + bool compare_to_zero) { VkBuffer to_resolve; u32 to_resolve_offset; + const u32 resolve_size = compare_to_zero ? 8 : 24; { std::scoped_lock lk(impl->buffer_cache.mutex); - static constexpr auto sync_info = VideoCommon::ObtainBufferSynchronize::NoSynchronize; + const auto sync_info = VideoCommon::ObtainBufferSynchronize::FullSynchronize; const auto post_op = VideoCommon::ObtainBufferOperation::DoNothing; const auto [buffer, offset] = - impl->buffer_cache.ObtainCPUBuffer(address, 24, sync_info, post_op); + impl->buffer_cache.ObtainCPUBuffer(address, resolve_size, sync_info, post_op); to_resolve = buffer->Handle(); to_resolve_offset = static_cast<u32>(offset); } @@ -1355,7 +1455,7 @@ void QueryCacheRuntime::HostConditionalRenderingCompareBCImpl(DAddr address, boo PauseHostConditionalRendering(); } impl->conditional_resolve_pass->Resolve(*impl->hcr_resolve_buffer, to_resolve, - to_resolve_offset, false); + to_resolve_offset, compare_to_zero); impl->hcr_setup.buffer = *impl->hcr_resolve_buffer; impl->hcr_setup.offset = 0; impl->hcr_setup.flags = is_equal ? 0 : VK_CONDITIONAL_RENDERING_INVERTED_BIT_EXT; @@ -1371,7 +1471,7 @@ bool QueryCacheRuntime::HostConditionalRenderingCompareValue(VideoCommon::Lookup if (!impl->device.IsExtConditionalRendering()) { return false; } - HostConditionalRenderingCompareValueImpl(object_1, false); + HostConditionalRenderingCompareBCImpl(object_1.address, true, true); return true; } @@ -1420,7 +1520,8 @@ bool QueryCacheRuntime::HostConditionalRenderingCompareValues(VideoCommon::Looku auto driver_id = impl->device.GetDriverID(); const bool is_gpu_high = Settings::IsGPULevelHigh(); - if ((!is_gpu_high && driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS) || driver_id == VK_DRIVER_ID_QUALCOMM_PROPRIETARY || driver_id == VK_DRIVER_ID_ARM_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_TURNIP) { + if ((!is_gpu_high && driver_id == VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS) || driver_id == VK_DRIVER_ID_ARM_PROPRIETARY || driver_id == VK_DRIVER_ID_MESA_TURNIP) { + EndHostConditionalRendering(); return true; } @@ -1437,10 +1538,12 @@ bool QueryCacheRuntime::HostConditionalRenderingCompareValues(VideoCommon::Looku } if (!is_gpu_high) { + EndHostConditionalRendering(); return true; } if (!is_in_bc[0] && !is_in_bc[1]) { + EndHostConditionalRendering(); return true; } HostConditionalRenderingCompareBCImpl(object_1.address, equal_check); diff --git a/src/video_core/renderer_vulkan/vk_query_cache.h b/src/video_core/renderer_vulkan/vk_query_cache.h index e2aa4d991e..bbb5234e11 100644 --- a/src/video_core/renderer_vulkan/vk_query_cache.h +++ b/src/video_core/renderer_vulkan/vk_query_cache.h @@ -63,7 +63,8 @@ public: private: void HostConditionalRenderingCompareValueImpl(VideoCommon::LookupData object, bool is_equal); - void HostConditionalRenderingCompareBCImpl(DAddr address, bool is_equal); + void HostConditionalRenderingCompareBCImpl(DAddr address, bool is_equal, + bool compare_to_zero = false); friend struct QueryCacheRuntimeImpl; std::unique_ptr<QueryCacheRuntimeImpl> impl; }; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index ba7b5d8c1b..78337b3ebe 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -173,6 +173,28 @@ DrawParams MakeDrawParams(const MaxwellDrawState& draw_state, u32 num_instances, } return params; } + +bool SupportsPrimitiveRestart(VkPrimitiveTopology topology) { + switch (topology) { + case VK_PRIMITIVE_TOPOLOGY_POINT_LIST: + case VK_PRIMITIVE_TOPOLOGY_LINE_LIST: + case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST: + case VK_PRIMITIVE_TOPOLOGY_LINE_LIST_WITH_ADJACENCY: + case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST_WITH_ADJACENCY: + case VK_PRIMITIVE_TOPOLOGY_PATCH_LIST: + return false; + default: + return true; + } +} + +bool IsPrimitiveRestartSupported(const Device& device, VkPrimitiveTopology topology) { + return ((topology != VK_PRIMITIVE_TOPOLOGY_PATCH_LIST && + device.IsTopologyListPrimitiveRestartSupported()) || + SupportsPrimitiveRestart(topology) || + (topology == VK_PRIMITIVE_TOPOLOGY_PATCH_LIST && + device.IsPatchListPrimitiveRestartSupported())); +} } // Anonymous namespace RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_, @@ -225,6 +247,7 @@ void RasterizerVulkan::PrepareDraw(bool is_indexed, Func&& draw_func) { UpdateDynamicStates(); + query_cache.NotifySegment(true); HandleTransformFeedback(); query_cache.CounterEnable(VideoCommon::QueryType::ZPassPixelCount64, maxwell3d->regs.zpass_pixel_count_enable); @@ -336,6 +359,7 @@ void RasterizerVulkan::DrawTexture() { UpdateDynamicStates(); + query_cache.NotifySegment(true); query_cache.CounterEnable(VideoCommon::QueryType::ZPassPixelCount64, maxwell3d->regs.zpass_pixel_count_enable); const auto& draw_texture_state = maxwell3d->draw_manager->GetDrawTextureState(); @@ -575,11 +599,17 @@ void RasterizerVulkan::DispatchCompute() { } void RasterizerVulkan::ResetCounter(VideoCommon::QueryType type) { - if (type != VideoCommon::QueryType::ZPassPixelCount64) { + switch (type) { + case VideoCommon::QueryType::ZPassPixelCount64: + case VideoCommon::QueryType::StreamingByteCount: + case VideoCommon::QueryType::StreamingPrimitivesSucceeded: + case VideoCommon::QueryType::VtgPrimitivesOut: + query_cache.CounterReset(type); + return; + default: LOG_DEBUG(Render_Vulkan, "Unimplemented counter reset={}", type); return; } - query_cache.CounterReset(type); } void RasterizerVulkan::Query(GPUVAddr gpu_addr, VideoCommon::QueryType type, @@ -766,6 +796,9 @@ void RasterizerVulkan::ReleaseFences(bool force) { void RasterizerVulkan::FlushAndInvalidateRegion(DAddr addr, u64 size, VideoCommon::CacheType which) { + if (Settings::IsGPULevelHigh()) { + FlushRegion(addr, size, which); + } InvalidateRegion(addr, size, which); } @@ -830,6 +863,10 @@ bool RasterizerVulkan::AccelerateConditionalRendering() { return query_cache.AccelerateHostConditionalRendering(); } +bool RasterizerVulkan::HasDrawTransformFeedback() { + return device.IsTransformFeedbackDrawSupported(); +} + bool RasterizerVulkan::AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surface& src, const Tegra::Engines::Fermi2D::Surface& dst, const Tegra::Engines::Fermi2D::Config& copy_config) { @@ -976,6 +1013,12 @@ bool AccelerateDMA::BufferToImage(const Tegra::DMA::ImageCopy& copy_info, void RasterizerVulkan::UpdateDynamicStates() { auto& regs = maxwell3d->regs; + auto& flags = maxwell3d->dirty.flags; + const auto topology = maxwell3d->draw_manager->GetDrawState().topology; + if (state_tracker.ChangePrimitiveTopology(topology)) { + flags[Dirty::DepthBiasEnable] = true; + flags[Dirty::PrimitiveRestartEnable] = true; + } // Core Dynamic States (Vulkan 1.0) - Always active regardless of dyna_state setting UpdateViewportsState(regs); @@ -1084,6 +1127,9 @@ void RasterizerVulkan::UpdateViewportsState(Tegra::Engines::Maxwell3D::Regs& reg if (!state_tracker.TouchViewports()) { return; } + + maxwell3d->dirty.flags[Dirty::Scissors] = true; + if (!regs.viewport_scale_offset_enabled) { float x = static_cast<float>(regs.surface_clip.x); float y = static_cast<float>(regs.surface_clip.y); @@ -1101,8 +1147,12 @@ void RasterizerVulkan::UpdateViewportsState(Tegra::Engines::Maxwell3D::Regs& reg .minDepth = 0.0f, .maxDepth = 1.0f, }; - scheduler.Record([viewport](vk::CommandBuffer cmdbuf) { - cmdbuf.SetViewport(0, viewport); + scheduler.Record([this, viewport](vk::CommandBuffer cmdbuf) { + const u32 num_viewports = std::min<u32>(device.GetMaxViewports(), Maxwell::NumViewports); + std::array<VkViewport, Maxwell::NumViewports> viewport_list{}; + viewport_list.fill(viewport); + const vk::Span<VkViewport> viewports(viewport_list.data(), num_viewports); + cmdbuf.SetViewport(0, viewports); }); return; } @@ -1142,8 +1192,12 @@ void RasterizerVulkan::UpdateScissorsState(Tegra::Engines::Maxwell3D::Regs& regs scissor.offset.y = static_cast<int32_t>(y); scissor.extent.width = width; scissor.extent.height = height; - scheduler.Record([scissor](vk::CommandBuffer cmdbuf) { - cmdbuf.SetScissor(0, scissor); + scheduler.Record([this, scissor](vk::CommandBuffer cmdbuf) { + const u32 num_scissors = std::min<u32>(device.GetMaxViewports(), Maxwell::NumViewports); + std::array<VkRect2D, Maxwell::NumViewports> scissor_list{}; + scissor_list.fill(scissor); + const vk::Span<VkRect2D> scissors(scissor_list.data(), num_scissors); + cmdbuf.SetScissor(0, scissors); }); return; } @@ -1388,7 +1442,17 @@ void RasterizerVulkan::UpdatePrimitiveRestartEnable(Tegra::Engines::Maxwell3D::R if (!state_tracker.TouchPrimitiveRestartEnable()) { return; } - scheduler.Record([enable = regs.primitive_restart.enabled](vk::CommandBuffer cmdbuf) { + + bool enable = regs.primitive_restart.enabled != 0; + if (device.IsMoltenVK()) { + enable = true; + } else if (enable) { + const auto topology = + MaxwellToVK::PrimitiveTopology(device, maxwell3d->draw_manager->GetDrawState().topology); + enable = IsPrimitiveRestartSupported(device, topology); + } + + scheduler.Record([enable](vk::CommandBuffer cmdbuf) { cmdbuf.SetPrimitiveRestartEnableEXT(enable); }); } @@ -1727,7 +1791,9 @@ void RasterizerVulkan::UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs& void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs) { auto& dirty{maxwell3d->dirty.flags}; - if (!dirty[Dirty::VertexInput]) { + const bool vertex_input_dirty = dirty[Dirty::VertexInput]; + const bool vertex_buffers_dirty = dirty[VideoCommon::Dirty::VertexBuffers]; + if (!vertex_input_dirty && !vertex_buffers_dirty) { return; } dirty[Dirty::VertexInput] = false; @@ -1735,38 +1801,31 @@ void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs) boost::container::static_vector<VkVertexInputBindingDescription2EXT, 32> bindings; boost::container::static_vector<VkVertexInputAttributeDescription2EXT, 32> attributes; - // There seems to be a bug on Nvidia's driver where updating only higher attributes ends up - // generating dirty state. Track the highest dirty attribute and update all attributes until - // that one. - size_t highest_dirty_attr{}; - for (size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) { - if (dirty[Dirty::VertexAttribute0 + index]) { - highest_dirty_attr = index; - } - } - for (size_t index = 0; index < highest_dirty_attr; ++index) { + const u32 max_attributes = + static_cast<u32>(std::min<size_t>(Maxwell::NumVertexAttributes, + device.GetMaxVertexInputAttributes())); + const u32 max_bindings = + static_cast<u32>(std::min<size_t>(Maxwell::NumVertexArrays, + device.GetMaxVertexInputBindings())); + + + for (u32 index = 0; index < max_attributes; ++index) { const Maxwell::VertexAttribute attribute{regs.vertex_attrib_format[index]}; const u32 binding{attribute.buffer}; - dirty[Dirty::VertexAttribute0 + index] = false; - dirty[Dirty::VertexBinding0 + static_cast<size_t>(binding)] = true; - if (!attribute.constant) { - attributes.push_back({ - .sType = VK_STRUCTURE_TYPE_VERTEX_INPUT_ATTRIBUTE_DESCRIPTION_2_EXT, - .pNext = nullptr, - .location = static_cast<u32>(index), - .binding = binding, - .format = MaxwellToVK::VertexFormat(device, attribute.type, attribute.size), - .offset = attribute.offset, - }); - } - } - for (size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) { - if (!dirty[Dirty::VertexBinding0 + index]) { + if (attribute.constant || binding >= max_bindings) { continue; } - dirty[Dirty::VertexBinding0 + index] = false; + attributes.push_back({ + .sType = VK_STRUCTURE_TYPE_VERTEX_INPUT_ATTRIBUTE_DESCRIPTION_2_EXT, + .pNext = nullptr, + .location = index, + .binding = binding, + .format = MaxwellToVK::VertexFormat(device, attribute.type, attribute.size), + .offset = attribute.offset, + }); + } - const u32 binding{static_cast<u32>(index)}; + for (u32 binding = 0; binding < max_bindings; ++binding) { const auto& input_binding{regs.vertex_streams[binding]}; const bool is_instanced{regs.vertex_stream_instances.IsInstancingEnabled(binding)}; bindings.push_back({ @@ -1778,6 +1837,14 @@ void RasterizerVulkan::UpdateVertexInput(Tegra::Engines::Maxwell3D::Regs& regs) .divisor = is_instanced ? input_binding.frequency : 1, }); } + + for (size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) { + dirty[Dirty::VertexAttribute0 + index] = false; + } + for (size_t index = 0; index < Maxwell::NumVertexArrays; ++index) { + dirty[Dirty::VertexBinding0 + index] = false; + } + scheduler.Record([bindings, attributes](vk::CommandBuffer cmdbuf) { cmdbuf.SetVertexInputEXT(bindings, attributes); }); diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index b689c6b660..841933d31d 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project @@ -122,6 +122,7 @@ public: void FlushCommands() override; void TickFrame() override; bool AccelerateConditionalRendering() override; + bool HasDrawTransformFeedback() override; bool AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surface& src, const Tegra::Engines::Fermi2D::Surface& dst, const Tegra::Engines::Fermi2D::Config& copy_config) override; diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp index 226619d8d6..fdaf9baacc 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.cpp +++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp @@ -324,6 +324,8 @@ void Scheduler::EndRenderPass() return; } + query_cache->CounterClose(VideoCommon::QueryType::StreamingByteCount); + // Log render pass end if (Settings::values.gpu_logging_enabled.GetValue() && Settings::values.gpu_log_vulkan_calls.GetValue()) { diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h index 00a912f2cd..0709c3a370 100644 --- a/src/video_core/renderer_vulkan/vk_scheduler.h +++ b/src/video_core/renderer_vulkan/vk_scheduler.h @@ -63,6 +63,11 @@ public: /// of a renderpass. void RequestOutsideRenderPassOperationContext(); + /// Returns true when a render pass is currently active in the scheduler state. + bool IsRenderPassActive() const { + return state.renderpass != VK_NULL_HANDLE; + } + /// Update the pipeline to the current execution context. bool UpdateGraphicsPipeline(GraphicsPipeline* pipeline); diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.cpp b/src/video_core/renderer_vulkan/vk_state_tracker.cpp index 79967d540a..3f4dd89c7e 100644 --- a/src/video_core/renderer_vulkan/vk_state_tracker.cpp +++ b/src/video_core/renderer_vulkan/vk_state_tracker.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project @@ -87,6 +87,7 @@ Flags MakeInvalidationFlags() { void SetupDirtyViewports(Tables& tables) { FillBlock(tables[0], OFF(viewport_transform), NUM(viewport_transform), Viewports); FillBlock(tables[0], OFF(viewports), NUM(viewports), Viewports); + FillBlock(tables[1], OFF(surface_clip), NUM(surface_clip), Viewports); tables[0][OFF(viewport_scale_offset_enabled)] = Viewports; tables[1][OFF(window_origin)] = Viewports; } diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.h b/src/video_core/renderer_vulkan/vk_state_tracker.h index 74bae9e181..6b47ba4176 100644 --- a/src/video_core/renderer_vulkan/vk_state_tracker.h +++ b/src/video_core/renderer_vulkan/vk_state_tracker.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 48aa5ec476..f099db74cb 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -176,7 +176,18 @@ constexpr VkBorderColor ConvertBorderColor(const std::array<float, 4>& color) { .pViewFormats = view_formats.data(), }; if (view_formats.size() > 1) { - image_ci.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT; + image_ci.flags |= + VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT | VK_IMAGE_CREATE_EXTENDED_USAGE_BIT; + + const bool has_storage_compatible_view = + std::any_of(view_formats.begin(), view_formats.end(), [&device](VkFormat view_format) { + return device.IsFormatSupported(view_format, VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT, + FormatType::Optimal); + }); + if (has_storage_compatible_view) { + image_ci.usage |= VK_IMAGE_USAGE_STORAGE_BIT; + } + if (device.IsKhrImageFormatListSupported()) { image_ci.pNext = &image_format_list; } @@ -668,11 +679,16 @@ void CopyBufferToImage(vk::CommandBuffer cmdbuf, VkBuffer src_buffer, VkImage im } void TryTransformSwizzleIfNeeded(PixelFormat format, std::array<SwizzleSource, 4>& swizzle, - bool emulate_a4b4g4r4) { + bool emulate_bgr565, bool emulate_a4b4g4r4) { switch (format) { case PixelFormat::A1B5G5R5_UNORM: std::ranges::transform(swizzle, swizzle.begin(), SwapBlueRed); break; + case PixelFormat::B5G6R5_UNORM: + if (emulate_bgr565) { + std::ranges::transform(swizzle, swizzle.begin(), SwapBlueRed); + } + break; case PixelFormat::A5B5G5R1_UNORM: std::ranges::transform(swizzle, swizzle.begin(), SwapSpecial); break; @@ -2119,22 +2135,21 @@ ImageView::ImageView(TextureCacheRuntime& runtime, const VideoCommon::ImageViewI if (!info.IsRenderTarget()) { swizzle = info.Swizzle(); TryTransformSwizzleIfNeeded(format, swizzle, - !device->IsExt4444FormatsSupported()); + device->MustEmulateBGR565(), + !device->IsExt4444FormatsSupported()); if ((aspect_mask & (VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT)) != 0) { std::ranges::transform(swizzle, swizzle.begin(), ConvertGreenRed); SanitizeDepthStencilSwizzle(swizzle, device->SupportsDepthStencilSwizzleOne()); } } const auto format_info = MaxwellToVK::SurfaceFormat(*device, FormatType::Optimal, true, format); - if (ImageUsageFlags(format_info, format) != image.UsageFlags()) { - LOG_WARNING(Render_Vulkan, - "Image view format {} has different usage flags than image format {}", format, - image.info.format); - } + const VkImageUsageFlags requested_view_usage = ImageUsageFlags(format_info, format); + const VkImageUsageFlags image_usage = image.UsageFlags(); + const VkImageUsageFlags clamped_view_usage = requested_view_usage & image_usage; const VkImageViewUsageCreateInfo image_view_usage{ .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO, .pNext = nullptr, - .usage = ImageUsageFlags(format_info, format), + .usage = clamped_view_usage, }; const VkImageViewCreateInfo create_info{ .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, @@ -2300,23 +2315,18 @@ vk::ImageView ImageView::MakeView(VkFormat vk_format, VkImageAspectFlags aspect_ Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& tsc) { const auto& device = runtime.device; - // Check if custom border colors are supported - const bool has_custom_border_colors = runtime.device.IsCustomBorderColorsSupported(); - const bool has_format_undefined = runtime.device.IsCustomBorderColorWithoutFormatSupported(); + const bool has_custom_border_extension = runtime.device.IsExtCustomBorderColorSupported(); + const bool has_format_undefined = + has_custom_border_extension && runtime.device.IsCustomBorderColorWithoutFormatSupported(); + const bool has_custom_border_colors = + has_format_undefined && runtime.device.IsCustomBorderColorsSupported(); const auto color = tsc.BorderColor(); - // Determine border format based on available features: - // - If customBorderColorWithoutFormat is available: use VK_FORMAT_UNDEFINED (most flexible) - // - If only customBorderColors is available: use concrete format (R8G8B8A8_UNORM) - // - If neither is available: use standard border colors (handled by ConvertBorderColor) - const VkFormat border_format = has_format_undefined ? VK_FORMAT_UNDEFINED - : VK_FORMAT_R8G8B8A8_UNORM; - const VkSamplerCustomBorderColorCreateInfoEXT border_ci{ .sType = VK_STRUCTURE_TYPE_SAMPLER_CUSTOM_BORDER_COLOR_CREATE_INFO_EXT, .pNext = nullptr, .customBorderColor = std::bit_cast<VkClearColorValue>(color), - .format = border_format, + .format = VK_FORMAT_UNDEFINED, }; const void* pnext = nullptr; if (has_custom_border_colors) { |
