diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt index e47263bfb2..e482725196 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/BooleanSetting.kt @@ -17,6 +17,7 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting { USE_CUSTOM_CPU_TICKS("use_custom_cpu_ticks"), SKIP_CPU_INNER_INVALIDATION("skip_cpu_inner_invalidation"), FIX_BLOOM_EFFECTS("fix_bloom_effects"), + EMULATE_BGR565("emulate_bgr565"), CPUOPT_UNSAFE_HOST_MMU("cpuopt_unsafe_host_mmu"), USE_DOCKED_MODE("use_docked_mode"), USE_AUTO_STUB("use_auto_stub"), diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt index 8ca9533f83..daeee398d4 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.kt @@ -756,6 +756,13 @@ abstract class SettingsItem( descriptionId = R.string.fix_bloom_effects_description ) ) + put( + SwitchSetting( + BooleanSetting.EMULATE_BGR565, + titleId = R.string.emulate_bgr565, + descriptionId = R.string.emulate_bgr565_description + ) + ) put( SwitchSetting( BooleanSetting.CPUOPT_UNSAFE_HOST_MMU, diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt index 542215fa97..0487339f68 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.kt @@ -293,6 +293,7 @@ class SettingsFragmentPresenter( add(IntSetting.FAST_GPU_TIME.key) add(BooleanSetting.SKIP_CPU_INNER_INVALIDATION.key) add(BooleanSetting.FIX_BLOOM_EFFECTS.key) + add(BooleanSetting.EMULATE_BGR565.key) add(BooleanSetting.RENDERER_ASYNCHRONOUS_SHADERS.key) add(BooleanSetting.RENDERER_ASYNCHRONOUS_GPU_EMULATION.key) add(BooleanSetting.RENDERER_ASYNC_PRESENTATION.key) diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index d42fb37d58..02860364a9 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -501,7 +501,7 @@ Enable buffer history Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games. Optimized Vertex Buffers - Enables optimized vertex buffer binding for improved performance. Requires Mesa 26.0+ Turnip drivers. Will crash on older drivers. + Enables optimized vertex buffer binding for improved performance. Requires Mesa 26.0+ Turnip drivers/ QCOM drivers. Will crash on older Turnip drivers. Hacks @@ -510,7 +510,9 @@ Skip CPU Inner Invalidation Skips certain CPU-side cache invalidations during memory updates, reducing CPU usage and improving it\'s performance. This may cause glitches or crashes on some games. Fix Bloom Effects - Reduces bloom blur in LA/EOW (Adreno 700), removes bloom in Burnout. Warning: may cause graphical artifacts in other games. + Reduces bloom blur in LA/EOW (Adreno A6XX - A7XX/ Turnip), removes bloom in Burnout. Warning: may cause graphical artifacts in other games. + Emulate BGR565 + Fixes problems with inverted colors in games or strange artifacts or strange shadows. Use asynchronous shaders Compiles shaders asynchronously. This may reduce stutters but may also introduce glitches. GPU Unswizzle Settings diff --git a/src/common/settings.h b/src/common/settings.h index ca61ac906e..13ccf5a1d5 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -555,6 +555,9 @@ struct Values { SwitchableSetting fix_bloom_effects{linkage, false, "fix_bloom_effects", Category::RendererHacks}; + SwitchableSetting emulate_bgr565{linkage, false, "emulate_bgr565", + Category::RendererHacks}; + SwitchableSetting rescale_hack{linkage, false, "rescale_hack", Category::RendererHacks}; @@ -584,7 +587,7 @@ struct Values { SwitchableSetting dyna_state{linkage, #if defined(ANDROID) - ExtendedDynamicState::EDS1, + ExtendedDynamicState::Disabled, #elif defined(__APPLE__) ExtendedDynamicState::Disabled, #else diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index b9a24496c9..169e83d9fd 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -178,6 +178,9 @@ void DefineGenericOutput(EmitContext& ctx, size_t index, std::optional invo ctx.Decorate(id, spv::Decoration::XfbBuffer, xfb_varying->buffer); ctx.Decorate(id, spv::Decoration::XfbStride, xfb_varying->stride); ctx.Decorate(id, spv::Decoration::Offset, xfb_varying->offset); + if (ctx.stage == Stage::Geometry && xfb_varying->stream != 0) { + ctx.Decorate(id, spv::Decoration::Stream, xfb_varying->stream); + } } if (num_components < 4 || element > 0) { const std::string_view subswizzle{swizzle.substr(element, num_components)}; diff --git a/src/shader_recompiler/runtime_info.h b/src/shader_recompiler/runtime_info.h index be10a9bb08..e6e1284762 100644 --- a/src/shader_recompiler/runtime_info.h +++ b/src/shader_recompiler/runtime_info.h @@ -76,6 +76,7 @@ enum class TessSpacing { struct TransformFeedbackVarying { u32 buffer{}; + u32 stream{}; u32 stride{}; u32 offset{}; u32 components{}; diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h index 014b4a318e..c857e90e02 100644 --- a/src/video_core/buffer_cache/buffer_cache.h +++ b/src/video_core/buffer_cache/buffer_cache.h @@ -1067,26 +1067,29 @@ void BufferCache

::BindHostTransformFeedbackBuffers() { HostBindings host_bindings; for (u32 index = 0; index < NUM_TRANSFORM_FEEDBACK_BUFFERS; ++index) { const Binding& binding = channel_state->transform_feedback_buffers[index]; - if (maxwell3d->regs.transform_feedback.controls[index].varying_count == 0 && - maxwell3d->regs.transform_feedback.controls[index].stride == 0) { - break; + const auto& control = maxwell3d->regs.transform_feedback.controls[index]; + const bool has_layout = control.varying_count != 0 || control.stride != 0; + + Buffer* host_buffer = &slot_buffers[NULL_BUFFER_ID]; + u32 offset = 0; + u32 size = 0; + + if (has_layout && binding.buffer_id != NULL_BUFFER_ID && binding.size != 0) { + Buffer& buffer = slot_buffers[binding.buffer_id]; + TouchBuffer(buffer, binding.buffer_id); + size = binding.size; + SynchronizeBuffer(buffer, binding.device_addr, size); + MarkWrittenBuffer(binding.buffer_id, binding.device_addr, size); + offset = buffer.Offset(binding.device_addr); + buffer.MarkUsage(offset, size); + host_buffer = &buffer; } - Buffer& buffer = slot_buffers[binding.buffer_id]; - TouchBuffer(buffer, binding.buffer_id); - const u32 size = binding.size; - SynchronizeBuffer(buffer, binding.device_addr, size); - MarkWrittenBuffer(binding.buffer_id, binding.device_addr, size); - - const u32 offset = buffer.Offset(binding.device_addr); - buffer.MarkUsage(offset, size); - host_bindings.buffers.push_back(&buffer); + host_bindings.buffers.push_back(host_buffer); host_bindings.offsets.push_back(offset); host_bindings.sizes.push_back(size); } - if (host_bindings.buffers.size() > 0) { - runtime.BindTransformFeedbackBuffers(host_bindings); - } + runtime.BindTransformFeedbackBuffers(host_bindings); } template diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp index 8d8d857a02..63a2516399 100644 --- a/src/video_core/gpu_thread.cpp +++ b/src/video_core/gpu_thread.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 2019 yuzu Emulator Project @@ -92,7 +92,16 @@ void ThreadManager::InvalidateRegion(DAddr addr, u64 size) { } void ThreadManager::FlushAndInvalidateRegion(DAddr addr, u64 size) { - // Skip flush on asynch mode, as FlushAndInvalidateRegion is not used for anything too important + if (Settings::IsGPULevelHigh()) { + if (!is_async) { + PushCommand(FlushRegionCommand(addr, size)); + } else { + auto& gpu = system.GPU(); + const u64 fence = gpu.RequestFlush(addr, size); + TickGPU(); + gpu.WaitForSyncOperation(fence); + } + } rasterizer->OnCacheInvalidation(addr, size); } diff --git a/src/video_core/host_shaders/resolve_conditional_render.comp b/src/video_core/host_shaders/resolve_conditional_render.comp index 307e77d1ad..3bc92f94fa 100644 --- a/src/video_core/host_shaders/resolve_conditional_render.comp +++ b/src/video_core/host_shaders/resolve_conditional_render.comp @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later @@ -5,16 +8,22 @@ layout(local_size_x = 1) in; -layout(std430, binding = 0) buffer Query { - uvec2 initial; - uvec2 unknown; - uvec2 current; +layout(std430, binding = 0) readonly buffer Query { + uint data[]; }; -layout(std430, binding = 1) buffer Result { +layout(std430, binding = 1) writeonly buffer Result { uint result; }; +layout(push_constant) uniform PushConstants { + uint compare_to_zero; +}; + void main() { - result = all(equal(initial, current)) ? 1 : 0; + if (compare_to_zero != 0u) { + result = (data[0] != 0u && data[1] != 0u) ? 1u : 0u; + } else { + result = (data[0] == data[4] && data[1] == data[5]) ? 1u : 0u; + } } diff --git a/src/video_core/macro.cpp b/src/video_core/macro.cpp index 66cea5afbd..2cda78c459 100644 --- a/src/video_core/macro.cpp +++ b/src/video_core/macro.cpp @@ -285,11 +285,11 @@ void HLE_MultiDrawIndexedIndirectCount::Fallback(Engines::Maxwell3D& maxwell3d, } void HLE_DrawIndirectByteCount::Execute(Engines::Maxwell3D& maxwell3d, std::span parameters, [[maybe_unused]] u32 method) { const bool force = maxwell3d.Rasterizer().HasDrawTransformFeedback(); - auto topology = Maxwell3D::Regs::PrimitiveTopology(parameters[0] & 0xFFFFU); - if (!force && (!maxwell3d.AnyParametersDirty() || !IsTopologySafe(topology))) { + if (!force) { Fallback(maxwell3d, parameters); return; } + auto topology = Maxwell3D::Regs::PrimitiveTopology(parameters[0] & 0xFFFFU); auto& params = maxwell3d.draw_manager->GetIndirectParams(); params.is_byte_count = true; params.is_indexed = false; diff --git a/src/video_core/query_cache/query_cache.h b/src/video_core/query_cache/query_cache.h index 4ed42487aa..6bed91a53e 100644 --- a/src/video_core/query_cache/query_cache.h +++ b/src/video_core/query_cache/query_cache.h @@ -412,6 +412,7 @@ bool QueryCacheBase::AccelerateHostConditionalRendering() { .found_query = nullptr, }; } + it_current = it_current_2; } auto* query = impl->ObtainQuery(it_current->second); qc_dirty |= True(query->flags & QueryFlagBits::IsHostManaged) && diff --git a/src/video_core/query_cache/query_stream.h b/src/video_core/query_cache/query_stream.h index 1d11b12752..b4dc9d1815 100644 --- a/src/video_core/query_cache/query_stream.h +++ b/src/video_core/query_cache/query_stream.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later @@ -75,7 +78,7 @@ public: } u64 GetDependentMask() const { - return dependence_mask; + return dependent_mask; } u64 GetAmendValue() const { diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index e268c4d2c6..26826a8f78 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -629,6 +629,9 @@ void RasterizerOpenGL::ReleaseFences(bool force) { void RasterizerOpenGL::FlushAndInvalidateRegion(DAddr addr, u64 size, VideoCommon::CacheType which) { + if (Settings::IsGPULevelHigh()) { + FlushRegion(addr, size, which); + } InvalidateRegion(addr, size, which); } 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 #include #include +#include #include #include #include @@ -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 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 +#include #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, 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(query_index), 1); cmdbuf.BeginQuery(query_pool, static_cast(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(*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(bank_slot); new_query->size_banks = 1; new_query->start_slot = static_cast(data_slot); @@ -778,6 +803,9 @@ public: } std::optional> GetLastQueryStream(size_t stream) { + if (stream >= NUM_STREAMS) { + return std::nullopt; + } if (last_queries[stream] != 0) { std::pair 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(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(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(buffers_count, stream + 1); } }); } - std::pair ProduceCounterBuffer(size_t stream) { + std::pair ProduceCounterBuffer(size_t slot_index) { if (current_bank == nullptr || current_bank->IsClosed()) { current_bank_id = bank_pool.ReserveBank([this](std::deque& 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 offsets{}; std::array last_queries; std::array last_queries_stride; + std::array 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 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(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( - 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( + 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(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 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(regs.surface_clip.x); float y = static_cast(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(device.GetMaxViewports(), Maxwell::NumViewports); + std::array viewport_list{}; + viewport_list.fill(viewport); + const vk::Span 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(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(device.GetMaxViewports(), Maxwell::NumViewports); + std::array scissor_list{}; + scissor_list.fill(scissor); + const vk::Span 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 bindings; boost::container::static_vector 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(std::min(Maxwell::NumVertexAttributes, + device.GetMaxVertexInputAttributes())); + const u32 max_bindings = + static_cast(std::min(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(binding)] = true; - if (!attribute.constant) { - attributes.push_back({ - .sType = VK_STRUCTURE_TYPE_VERTEX_INPUT_ATTRIBUTE_DESCRIPTION_2_EXT, - .pNext = nullptr, - .location = static_cast(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(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& 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& 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(color), - .format = border_format, + .format = VK_FORMAT_UNDEFINED, }; const void* pnext = nullptr; if (has_custom_border_colors) { diff --git a/src/video_core/transform_feedback.cpp b/src/video_core/transform_feedback.cpp index a8f9da9853..53d29c08e2 100644 --- a/src/video_core/transform_feedback.cpp +++ b/src/video_core/transform_feedback.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 2021 yuzu Emulator Project @@ -88,13 +88,13 @@ std::pair, u32> MakeTransformF return 0; }; - UNIMPLEMENTED_IF_MSG(layout.stream != 0, "Stream is not zero: {}", layout.stream); Shader::TransformFeedbackVarying varying{ .buffer = static_cast(buffer), .stride = layout.stride, .offset = offset * 4, .components = 1, }; + varying.stream = layout.stream; const u32 base_offset = offset; const auto attribute{get_attribute(offset)}; if (std::ranges::find(VECTORS, Common::AlignDown(attribute, 4)) != VECTORS.end()) { diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index 5075a79bcd..6e55306079 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -869,6 +869,10 @@ bool Device::HasTimelineSemaphore() const { return features.timeline_semaphore.timelineSemaphore; } +bool Device::MustEmulateBGR565() const { + return Settings::values.emulate_bgr565.GetValue(); +} + bool Device::GetSuitability(bool requires_swapchain) { // Assume we will be suitable. bool suitable = true; @@ -919,6 +923,17 @@ bool Device::GetSuitability(bool requires_swapchain) { FOR_EACH_VK_FEATURE_EXT(FEATURE_EXTENSION); FOR_EACH_VK_EXTENSION(EXTENSION); + if (supported_extensions.contains(VK_KHR_ROBUSTNESS_2_EXTENSION_NAME)) { + loaded_extensions.erase(VK_EXT_ROBUSTNESS_2_EXTENSION_NAME); + loaded_extensions.insert(VK_KHR_ROBUSTNESS_2_EXTENSION_NAME); + extensions.robustness_2 = true; + } else if (supported_extensions.contains(VK_EXT_ROBUSTNESS_2_EXTENSION_NAME)) { + loaded_extensions.insert(VK_EXT_ROBUSTNESS_2_EXTENSION_NAME); + extensions.robustness_2 = true; + } else { + extensions.robustness_2 = false; + } + #undef FEATURE_EXTENSION #undef EXTENSION @@ -1131,8 +1146,6 @@ bool Device::GetSuitability(bool requires_swapchain) { if (u32(Settings::values.dyna_state.GetValue()) == 0) { LOG_INFO(Render_Vulkan, "Extended Dynamic State disabled by user setting, clearing all EDS features"); - features.custom_border_color.customBorderColors = false; - features.custom_border_color.customBorderColorWithoutFormat = false; features.extended_dynamic_state.extendedDynamicState = false; features.extended_dynamic_state2.extendedDynamicState2 = false; features.extended_dynamic_state3.extendedDynamicState3ColorBlendEnable = false; @@ -1148,24 +1161,13 @@ bool Device::GetSuitability(bool requires_swapchain) { void Device::RemoveUnsuitableExtensions() { // VK_EXT_custom_border_color - // Enable extension if driver supports it, then check individual features - // - customBorderColors: Required to use VK_BORDER_COLOR_FLOAT_CUSTOM_EXT - // - customBorderColorWithoutFormat: Optional, allows VK_FORMAT_UNDEFINED - // If only customBorderColors is available, we must provide a specific format if (extensions.custom_border_color) { - // Verify that at least customBorderColors is available - if (!features.custom_border_color.customBorderColors) { - LOG_WARNING(Render_Vulkan, - "VK_EXT_custom_border_color reported but customBorderColors feature not available, disabling"); - extensions.custom_border_color = false; - } + extensions.custom_border_color = + features.custom_border_color.customBorderColors && + features.custom_border_color.customBorderColorWithoutFormat; } RemoveExtensionFeatureIfUnsuitable(extensions.custom_border_color, features.custom_border_color, VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME); - // VK_KHR_unified_image_layouts - extensions.unified_image_layouts = features.unified_image_layouts.unifiedImageLayouts; - RemoveExtensionFeatureIfUnsuitable(extensions.unified_image_layouts, features.unified_image_layouts, - VK_KHR_UNIFIED_IMAGE_LAYOUTS_EXTENSION_NAME); // VK_EXT_depth_bias_control extensions.depth_bias_control = @@ -1251,16 +1253,22 @@ void Device::RemoveUnsuitableExtensions() { VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME); // VK_EXT_robustness2 - extensions.robustness_2 = features.robustness2.robustBufferAccess2 || - features.robustness2.robustImageAccess2 || - features.robustness2.nullDescriptor; + features.robustness2.robustBufferAccess2 = VK_FALSE; + features.robustness2.robustImageAccess2 = VK_FALSE; + extensions.robustness_2 = features.robustness2.nullDescriptor; + + const char* robustness2_extension_name = + loaded_extensions.contains(VK_KHR_ROBUSTNESS_2_EXTENSION_NAME) + ? VK_KHR_ROBUSTNESS_2_EXTENSION_NAME + : VK_EXT_ROBUSTNESS_2_EXTENSION_NAME; RemoveExtensionFeatureIfUnsuitable(extensions.robustness_2, features.robustness2, - VK_EXT_ROBUSTNESS_2_EXTENSION_NAME); + robustness2_extension_name); - // VK_EXT_image_robustness - extensions.image_robustness = features.image_robustness.robustImageAccess; - RemoveExtensionFeatureIfUnsuitable(extensions.image_robustness, features.image_robustness, + // Image robustness + extensions.robust_image_access = features.robust_image_access.robustImageAccess; + RemoveExtensionFeatureIfUnsuitable(extensions.robust_image_access, + features.robust_image_access, VK_EXT_IMAGE_ROBUSTNESS_EXTENSION_NAME); // VK_KHR_shader_atomic_int64 @@ -1288,8 +1296,7 @@ void Device::RemoveUnsuitableExtensions() { // VK_EXT_transform_feedback extensions.transform_feedback = features.transform_feedback.transformFeedback && - properties.transform_feedback.maxTransformFeedbackBuffers > 0 && - properties.transform_feedback.transformFeedbackQueries; + properties.transform_feedback.maxTransformFeedbackBuffers > 0; RemoveExtensionFeatureIfUnsuitable(extensions.transform_feedback, features.transform_feedback, VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME); diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index ad9d53ce16..a8a89aee89 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -38,7 +38,7 @@ VK_DEFINE_HANDLE(VmaAllocator) FEATURE(KHR, TimelineSemaphore, TIMELINE_SEMAPHORE, timeline_semaphore) #define FOR_EACH_VK_FEATURE_1_3(FEATURE) \ - FEATURE(EXT, ImageRobustness, IMAGE_ROBUSTNESS, image_robustness) \ + FEATURE(EXT, ImageRobustness, IMAGE_ROBUSTNESS, robust_image_access) \ FEATURE(EXT, ShaderDemoteToHelperInvocation, SHADER_DEMOTE_TO_HELPER_INVOCATION, \ shader_demote_to_helper_invocation) \ FEATURE(EXT, SubgroupSizeControl, SUBGROUP_SIZE_CONTROL, subgroup_size_control) \ @@ -68,8 +68,7 @@ VK_DEFINE_HANDLE(VmaAllocator) FEATURE(KHR, PipelineExecutableProperties, PIPELINE_EXECUTABLE_PROPERTIES, \ pipeline_executable_properties) \ FEATURE(KHR, WorkgroupMemoryExplicitLayout, WORKGROUP_MEMORY_EXPLICIT_LAYOUT, \ - workgroup_memory_explicit_layout) \ - FEATURE(KHR, UnifiedImageLayouts, UNIFIED_IMAGE_LAYOUTS, unified_image_layouts) + workgroup_memory_explicit_layout) // Define miscellaneous extensions which may be used by the implementation here. @@ -124,7 +123,6 @@ VK_DEFINE_HANDLE(VmaAllocator) EXTENSION_NAME(VK_EXT_EXTENDED_DYNAMIC_STATE_3_EXTENSION_NAME) \ EXTENSION_NAME(VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME) \ EXTENSION_NAME(VK_EXT_4444_FORMATS_EXTENSION_NAME) \ - EXTENSION_NAME(VK_EXT_IMAGE_ROBUSTNESS_EXTENSION_NAME) \ EXTENSION_NAME(VK_EXT_LINE_RASTERIZATION_EXTENSION_NAME) \ EXTENSION_NAME(VK_EXT_ROBUSTNESS_2_EXTENSION_NAME) \ EXTENSION_NAME(VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME) \ @@ -174,13 +172,11 @@ VK_DEFINE_HANDLE(VmaAllocator) FEATURE_NAME(depth_bias_control, depthBiasExact) \ FEATURE_NAME(extended_dynamic_state, extendedDynamicState) \ FEATURE_NAME(format_a4b4g4r4, formatA4B4G4R4) \ - FEATURE_NAME(image_robustness, robustImageAccess) \ + FEATURE_NAME(robust_image_access, robustImageAccess) \ FEATURE_NAME(index_type_uint8, indexTypeUint8) \ FEATURE_NAME(primitive_topology_list_restart, primitiveTopologyListRestart) \ FEATURE_NAME(provoking_vertex, provokingVertexLast) \ FEATURE_NAME(robustness2, nullDescriptor) \ - FEATURE_NAME(robustness2, robustBufferAccess2) \ - FEATURE_NAME(robustness2, robustImageAccess2) \ FEATURE_NAME(shader_float16_int8, shaderFloat16) \ FEATURE_NAME(shader_float16_int8, shaderInt8) \ FEATURE_NAME(timeline_semaphore, timelineSemaphore) \ @@ -542,6 +538,17 @@ public: return extensions.transform_feedback; } + /// Returns true if transform feedback draw commands are supported. + bool IsTransformFeedbackDrawSupported() const { + return extensions.transform_feedback && properties.transform_feedback.transformFeedbackDraw; + } + + /// Returns true if transform feedback query types are supported. + bool IsTransformFeedbackQueriesSupported() const { + return extensions.transform_feedback && + properties.transform_feedback.transformFeedbackQueries; + } + /// Returns true if the device supports VK_EXT_transform_feedback properly. bool AreTransformFeedbackGeometryStreamsSupported() const { return features.transform_feedback.geometryStreams; @@ -552,36 +559,6 @@ public: return extensions.custom_border_color; } - /// Returns true if the device supports VK_EXT_image_robustness. - bool IsExtImageRobustnessSupported() const { - return extensions.image_robustness; - } - - /// Returns true if robustImageAccess is supported. - bool IsRobustImageAccessSupported() const { - return features.image_robustness.robustImageAccess; - } - - /// Returns true if the device supports VK_EXT_robustness2. - bool IsExtRobustness2Supported() const { - return extensions.robustness_2; - } - - /// Returns true if robustBufferAccess2 is supported. - bool IsRobustBufferAccess2Supported() const { - return features.robustness2.robustBufferAccess2; - } - - /// Returns true if robustImageAccess2 is supported. - bool IsRobustImageAccess2Supported() const { - return features.robustness2.robustImageAccess2; - } - - /// Returns true if nullDescriptor is supported. - bool IsNullDescriptorSupported() const { - return features.robustness2.nullDescriptor; - } - /// Returns true if customBorderColors feature is available. bool IsCustomBorderColorsSupported() const { return features.custom_border_color.customBorderColors; @@ -805,6 +782,8 @@ public: return features.robustness2.nullDescriptor; } + bool MustEmulateBGR565() const; + bool HasExactDepthBiasControl() const { return features.depth_bias_control.depthBiasExact; } diff --git a/src/video_core/vulkan_common/vulkan_wrapper.cpp b/src/video_core/vulkan_common/vulkan_wrapper.cpp index f59ac7d6bc..871ce52678 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.cpp +++ b/src/video_core/vulkan_common/vulkan_wrapper.cpp @@ -123,6 +123,7 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept { X(vkCmdEndDebugUtilsLabelEXT); X(vkCmdFillBuffer); X(vkCmdPipelineBarrier); + X(vkCmdResetQueryPool); X(vkCmdPushConstants); X(vkCmdPushDescriptorSetWithTemplateKHR); X(vkCmdSetBlendConstants); diff --git a/src/video_core/vulkan_common/vulkan_wrapper.h b/src/video_core/vulkan_common/vulkan_wrapper.h index aaff66359e..4a3baad2c4 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.h +++ b/src/video_core/vulkan_common/vulkan_wrapper.h @@ -225,6 +225,7 @@ struct DeviceDispatch : InstanceDispatch { PFN_vkCmdEndTransformFeedbackEXT vkCmdEndTransformFeedbackEXT{}; PFN_vkCmdFillBuffer vkCmdFillBuffer{}; PFN_vkCmdPipelineBarrier vkCmdPipelineBarrier{}; + PFN_vkCmdResetQueryPool vkCmdResetQueryPool{}; PFN_vkCmdPushConstants vkCmdPushConstants{}; PFN_vkCmdPushDescriptorSetWithTemplateKHR vkCmdPushDescriptorSetWithTemplateKHR{}; PFN_vkCmdResolveImage vkCmdResolveImage{}; @@ -1168,6 +1169,10 @@ public: dld->vkCmdEndQuery(handle, query_pool, query); } + void ResetQueryPool(VkQueryPool query_pool, u32 first_query, u32 query_count) const noexcept { + dld->vkCmdResetQueryPool(handle, query_pool, first_query, query_count); + } + void BindDescriptorSets(VkPipelineBindPoint bind_point, VkPipelineLayout layout, u32 first, Span sets, Span dynamic_offsets) const noexcept { dld->vkCmdBindDescriptorSets(handle, bind_point, layout, first, sets.size(), sets.data(),