diff --git a/src/video_core/renderer_vulkan/vk_swapchain.cpp b/src/video_core/renderer_vulkan/vk_swapchain.cpp index fdd2de2379..cb2b3d7520 100644 --- a/src/video_core/renderer_vulkan/vk_swapchain.cpp +++ b/src/video_core/renderer_vulkan/vk_swapchain.cpp @@ -171,6 +171,10 @@ void Swapchain::Create( resource_ticks.clear(); resource_ticks.resize(image_count); + + // Initialize incremental-present probe flags for this swapchain. + incremental_present_usable = device.IsKhrIncrementalPresentSupported(); + incremental_present_probed = false; } bool Swapchain::AcquireNextImage() { @@ -202,7 +206,13 @@ bool Swapchain::AcquireNextImage() { void Swapchain::Present(VkSemaphore render_semaphore) { const auto present_queue{device.GetPresentQueue()}; - const VkPresentInfoKHR present_info{ + // If the device advertises VK_KHR_incremental_present, we attempt a one-time probe + // on the first present to validate the driver/compositor accepts present-region info. + VkPresentRegionsKHR present_regions{}; + VkPresentRegionKHR region{}; + VkRect2D rect{}; + + VkPresentInfoKHR present_info{ .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, .pNext = nullptr, .waitSemaphoreCount = render_semaphore ? 1U : 0U, @@ -212,6 +222,20 @@ void Swapchain::Present(VkSemaphore render_semaphore) { .pImageIndices = &image_index, .pResults = nullptr, }; + + if (incremental_present_usable && !incremental_present_probed) { + // Build a minimal present-region describing a single 1x1 dirty rect at (0,0). + rect.offset = {0, 0}; + rect.extent = {1, 1}; + region.rectangleCount = 1; + region.pRectangles = ▭ + present_regions.sType = VK_STRUCTURE_TYPE_PRESENT_REGIONS_KHR; + present_regions.pNext = nullptr; + present_regions.swapchainCount = 1; + present_regions.pRegions = ®ion; + + present_info.pNext = &present_regions; + } std::scoped_lock lock{scheduler.submit_mutex}; switch (const VkResult result = present_queue.Present(present_info)) { case VK_SUCCESS: @@ -227,8 +251,18 @@ void Swapchain::Present(VkSemaphore render_semaphore) { break; default: LOG_CRITICAL(Render_Vulkan, "Failed to present with error {}", string_VkResult(result)); + // If the first present with incremental-present pNext failed, disable future use. + if (incremental_present_usable && !incremental_present_probed) { + incremental_present_usable = false; + LOG_WARNING(Render_Vulkan, "Disabling VK_KHR_incremental_present for this swapchain due to present failure: {}", string_VkResult(result)); + } break; } + if (incremental_present_usable && !incremental_present_probed) { + // Mark probe as completed if we reached here (success or handled failure above). + incremental_present_probed = true; + LOG_INFO(Render_Vulkan, "VK_KHR_incremental_present probe completed: usable={}", incremental_present_usable); + } ++frame_index; if (frame_index >= image_count) { frame_index = 0; diff --git a/src/video_core/renderer_vulkan/vk_swapchain.h b/src/video_core/renderer_vulkan/vk_swapchain.h index b3e1c4f025..2c6055fba3 100644 --- a/src/video_core/renderer_vulkan/vk_swapchain.h +++ b/src/video_core/renderer_vulkan/vk_swapchain.h @@ -158,6 +158,8 @@ private: bool is_outdated{}; bool is_suboptimal{}; + bool incremental_present_usable{}; + bool incremental_present_probed{}; }; } // namespace Vulkan diff --git a/src/video_core/vulkan_common/vulkan.h b/src/video_core/vulkan_common/vulkan.h index 13f679ff54..7092243f7f 100644 --- a/src/video_core/vulkan_common/vulkan.h +++ b/src/video_core/vulkan_common/vulkan.h @@ -22,6 +22,34 @@ #include +#ifndef VK_KHR_MAINTENANCE_1_EXTENSION_NAME +# define VK_KHR_MAINTENANCE_1_EXTENSION_NAME "VK_KHR_maintenance1" +#endif +#ifndef VK_KHR_MAINTENANCE_2_EXTENSION_NAME +# define VK_KHR_MAINTENANCE_2_EXTENSION_NAME "VK_KHR_maintenance2" +#endif +#ifndef VK_KHR_MAINTENANCE_3_EXTENSION_NAME +# define VK_KHR_MAINTENANCE_3_EXTENSION_NAME "VK_KHR_maintenance3" +#endif +#ifndef VK_KHR_MAINTENANCE_4_EXTENSION_NAME +# define VK_KHR_MAINTENANCE_4_EXTENSION_NAME "VK_KHR_maintenance4" +#endif +#ifndef VK_KHR_MAINTENANCE_5_EXTENSION_NAME +# define VK_KHR_MAINTENANCE_5_EXTENSION_NAME "VK_KHR_maintenance5" +#endif +#ifndef VK_KHR_MAINTENANCE_6_EXTENSION_NAME +# define VK_KHR_MAINTENANCE_6_EXTENSION_NAME "VK_KHR_maintenance6" +#endif +#ifndef VK_KHR_MAINTENANCE_7_EXTENSION_NAME +# define VK_KHR_MAINTENANCE_7_EXTENSION_NAME "VK_KHR_maintenance7" +#endif +#ifndef VK_KHR_MAINTENANCE_8_EXTENSION_NAME +# define VK_KHR_MAINTENANCE_8_EXTENSION_NAME "VK_KHR_maintenance8" +#endif +#ifndef VK_KHR_MAINTENANCE_9_EXTENSION_NAME +# define VK_KHR_MAINTENANCE_9_EXTENSION_NAME "VK_KHR_maintenance9" +#endif + // Sanitize macros #undef CreateEvent #undef CreateSemaphore diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index 81e60f1c6a..dd794af70f 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -416,7 +416,6 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR const bool is_suitable = GetSuitability(surface != nullptr); const VkDriverId driver_id = properties.driver.driverID; - const auto device_id = properties.properties.deviceID; const bool is_radv = driver_id == VK_DRIVER_ID_MESA_RADV; const bool is_amd_driver = driver_id == VK_DRIVER_ID_AMD_PROPRIETARY || driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE; @@ -681,9 +680,31 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR has_broken_compute = CheckBrokenCompute(properties.driver.driverID, properties.properties.driverVersion) && !Settings::values.enable_compute_pipelines.GetValue(); - if (is_intel_anv || (is_qualcomm && !is_s8gen2)) { - LOG_WARNING(Render_Vulkan, "Driver does not support native BGR format"); + must_emulate_bgr565 = false; // Default: assume emulation isn't required + + if (is_intel_anv) { + LOG_WARNING(Render_Vulkan, "Intel ANV driver does not support native BGR format"); must_emulate_bgr565 = true; + } else if (is_qualcomm) { + // Qualcomm driver version where VK_KHR_maintenance5 and A1B5G5R5 become reliable + constexpr uint32_t QUALCOMM_FIXED_DRIVER_VERSION = VK_MAKE_VERSION(512, 800, 1); + // Check if VK_KHR_maintenance5 is supported + if (extensions.maintenance5 && properties.properties.driverVersion >= QUALCOMM_FIXED_DRIVER_VERSION) { + LOG_INFO(Render_Vulkan, "Qualcomm driver supports VK_KHR_maintenance5, disabling BGR emulation"); + must_emulate_bgr565 = false; + } else { + LOG_WARNING(Render_Vulkan, "Qualcomm driver doesn't support native BGR, emulating formats"); + must_emulate_bgr565 = true; + } + } else if (is_turnip) { + // Mesa Turnip added support for maintenance5 in Mesa 25.0 + if (extensions.maintenance5) { + LOG_INFO(Render_Vulkan, "Turnip driver supports VK_KHR_maintenance5, disabling BGR emulation"); + must_emulate_bgr565 = false; + } else { + LOG_WARNING(Render_Vulkan, "Turnip driver doesn't support native BGR, emulating formats"); + must_emulate_bgr565 = true; + } } if (extensions.push_descriptor && is_intel_anv) { const u32 version = (properties.properties.driverVersion << 3) >> 3; @@ -1300,6 +1321,21 @@ void Device::RemoveUnsuitableExtensions() { RemoveExtensionFeatureIfUnsuitable(extensions.transform_feedback, features.transform_feedback, VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME); + // VK_EXT_robustness2 + extensions.robustness_2 = + features.robustness2.robustBufferAccess2 && features.robustness2.robustImageAccess2; + RemoveExtensionFeatureIfUnsuitable(extensions.robustness_2, features.robustness2, + VK_EXT_ROBUSTNESS_2_EXTENSION_NAME); + + // VK_EXT_image_robustness + extensions.image_robustness = features.image_robustness.robustImageAccess; + RemoveExtensionFeatureIfUnsuitable(extensions.image_robustness, features.image_robustness, + VK_EXT_IMAGE_ROBUSTNESS_EXTENSION_NAME); + + // VK_EXT_swapchain_maintenance1 + extensions.swapchain_maintenance1 = loaded_extensions.contains(VK_EXT_SWAPCHAIN_MAINTENANCE_1_EXTENSION_NAME); + RemoveExtensionIfUnsuitable(extensions.swapchain_maintenance1, VK_EXT_SWAPCHAIN_MAINTENANCE_1_EXTENSION_NAME); + // VK_EXT_vertex_input_dynamic_state extensions.vertex_input_dynamic_state = features.vertex_input_dynamic_state.vertexInputDynamicState; diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index cb13f28523..6f60741fea 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -52,6 +52,7 @@ VK_DEFINE_HANDLE(VmaAllocator) FEATURE(EXT, 4444Formats, 4444_FORMATS, format_a4b4g4r4) \ FEATURE(EXT, IndexTypeUint8, INDEX_TYPE_UINT8, index_type_uint8) \ FEATURE(EXT, LineRasterization, LINE_RASTERIZATION, line_rasterization) \ + FEATURE(EXT, ImageRobustness, IMAGE_ROBUSTNESS, image_robustness) \ FEATURE(EXT, PrimitiveTopologyListRestart, PRIMITIVE_TOPOLOGY_LIST_RESTART, \ primitive_topology_list_restart) \ FEATURE(EXT, ProvokingVertex, PROVOKING_VERTEX, provoking_vertex) \ @@ -82,7 +83,9 @@ VK_DEFINE_HANDLE(VmaAllocator) EXTENSION(KHR, SHADER_FLOAT_CONTROLS, shader_float_controls) \ EXTENSION(KHR, SPIRV_1_4, spirv_1_4) \ EXTENSION(KHR, SWAPCHAIN, swapchain) \ + EXTENSION(KHR, INCREMENTAL_PRESENT, incremental_present) \ EXTENSION(KHR, SWAPCHAIN_MUTABLE_FORMAT, swapchain_mutable_format) \ + EXTENSION(EXT, SWAPCHAIN_MAINTENANCE_1, swapchain_maintenance1) \ EXTENSION(KHR, IMAGE_FORMAT_LIST, image_format_list) \ EXTENSION(NV, DEVICE_DIAGNOSTICS_CONFIG, device_diagnostics_config) \ EXTENSION(NV, GEOMETRY_SHADER_PASSTHROUGH, geometry_shader_passthrough) \ @@ -91,6 +94,15 @@ VK_DEFINE_HANDLE(VmaAllocator) EXTENSION(EXT, DESCRIPTOR_INDEXING, descriptor_indexing) \ EXTENSION(EXT, FILTER_CUBIC, filter_cubic) \ EXTENSION(QCOM, FILTER_CUBIC_WEIGHTS, filter_cubic_weights) + EXTENSION(KHR, MAINTENANCE_1, maintenance1) \ + EXTENSION(KHR, MAINTENANCE_2, maintenance2) \ + EXTENSION(KHR, MAINTENANCE_3, maintenance3) \ + EXTENSION(KHR, MAINTENANCE_4, maintenance4) \ + EXTENSION(KHR, MAINTENANCE_5, maintenance5) \ + EXTENSION(KHR, MAINTENANCE_6, maintenance6) \ + EXTENSION(KHR, MAINTENANCE_7, maintenance7) \ + EXTENSION(KHR, MAINTENANCE_8, maintenance8) \ + EXTENSION(KHR, MAINTENANCE_9, maintenance9) \ // Define extensions which must be supported. #define FOR_EACH_VK_MANDATORY_EXTENSION(EXTENSION_NAME) \ @@ -455,6 +467,11 @@ public: return extensions.image_format_list || instance_version >= VK_API_VERSION_1_2; } + /// Returns true if the device supports VK_KHR_incremental_present. + bool IsKhrIncrementalPresentSupported() const { + return extensions.incremental_present; + } + /// Returns true if the device supports VK_EXT_primitive_topology_list_restart. bool IsTopologyListPrimitiveRestartSupported() const { return features.primitive_topology_list_restart.primitiveTopologyListRestart;