diff --git a/src/video_core/renderer_vulkan/present/util.cpp b/src/video_core/renderer_vulkan/present/util.cpp index c85af50060..fe956a1af6 100644 --- a/src/video_core/renderer_vulkan/present/util.cpp +++ b/src/video_core/renderer_vulkan/present/util.cpp @@ -112,12 +112,15 @@ void UploadImage(const Device& device, MemoryAllocator& allocator, Scheduler& sc scheduler.RequestOutsideRenderPassOperationContext(); scheduler.Record([&](vk::CommandBuffer cmdbuf) { - TransitionImageLayout(cmdbuf, *image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + const VkImageLayout transfer_dst_layout = device.IsKhrUnifiedImageLayoutsSupported() + ? VK_IMAGE_LAYOUT_GENERAL + : VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + TransitionImageLayout(cmdbuf, *image, transfer_dst_layout, VK_IMAGE_LAYOUT_UNDEFINED); - cmdbuf.CopyBufferToImage(*upload_buffer, *image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + cmdbuf.CopyBufferToImage(*upload_buffer, *image, transfer_dst_layout, regions); TransitionImageLayout(cmdbuf, *image, VK_IMAGE_LAYOUT_GENERAL, - VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + transfer_dst_layout); }); scheduler.Finish(); } diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp index 0069f10f9c..d9a2c38508 100644 --- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp @@ -536,6 +536,7 @@ struct RangedBarrierRange { }; void CopyBufferToImage(vk::CommandBuffer cmdbuf, VkBuffer src_buffer, VkImage image, VkImageAspectFlags aspect_mask, bool is_initialized, + bool use_unified_layouts, std::span copies) { static constexpr VkAccessFlags WRITE_ACCESS_FLAGS = VK_ACCESS_SHADER_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | @@ -550,6 +551,8 @@ void CopyBufferToImage(vk::CommandBuffer cmdbuf, VkBuffer src_buffer, VkImage im range.AddLayers(region.imageSubresource); } const VkImageSubresourceRange subresource_range = range.SubresourceRange(aspect_mask); + const VkImageLayout transfer_dst_layout = + use_unified_layouts ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; const VkImageMemoryBarrier read_barrier{ .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, @@ -557,7 +560,7 @@ void CopyBufferToImage(vk::CommandBuffer cmdbuf, VkBuffer src_buffer, VkImage im .srcAccessMask = WRITE_ACCESS_FLAGS, .dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, .oldLayout = is_initialized ? VK_IMAGE_LAYOUT_GENERAL : VK_IMAGE_LAYOUT_UNDEFINED, - .newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + .newLayout = transfer_dst_layout, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .image = image, @@ -569,7 +572,7 @@ void CopyBufferToImage(vk::CommandBuffer cmdbuf, VkBuffer src_buffer, VkImage im .pNext = nullptr, .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, .dstAccessMask = WRITE_ACCESS_FLAGS | READ_ACCESS_FLAGS, - .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + .oldLayout = transfer_dst_layout, .newLayout = VK_IMAGE_LAYOUT_GENERAL, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, @@ -581,7 +584,7 @@ void CopyBufferToImage(vk::CommandBuffer cmdbuf, VkBuffer src_buffer, VkImage im VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, read_barrier); - cmdbuf.CopyBufferToImage(src_buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, copies); + cmdbuf.CopyBufferToImage(src_buffer, image, transfer_dst_layout, copies); // TODO: Move this to another API cmdbuf.PipelineBarrier( VK_PIPELINE_STAGE_TRANSFER_BIT, @@ -703,7 +706,7 @@ void TryTransformSwizzleIfNeeded(PixelFormat format, std::arrayoriginal_image; const VkImageAspectFlags vk_aspect_mask = temp_wrapper->aspect_mask; - scheduler->Record([src_buffer, temp_vk_image, vk_aspect_mask, vk_copies, + const bool use_unified_layouts = runtime->device.IsKhrUnifiedImageLayoutsSupported(); + scheduler->Record([src_buffer, temp_vk_image, vk_aspect_mask, vk_copies, use_unified_layouts, keep = temp_wrapper](vk::CommandBuffer cmdbuf) { - CopyBufferToImage(cmdbuf, src_buffer, temp_vk_image, vk_aspect_mask, false, VideoCommon::FixSmallVectorADL(vk_copies)); + CopyBufferToImage(cmdbuf, src_buffer, temp_vk_image, vk_aspect_mask, false, + use_unified_layouts, VideoCommon::FixSmallVectorADL(vk_copies)); }); // Use MSAACopyPass to convert from non-MSAA to MSAA @@ -1782,9 +1791,11 @@ void Image::UploadMemory(VkBuffer buffer, VkDeviceSize offset, const VkImageAspectFlags vk_aspect_mask = aspect_mask; const bool was_initialized = std::exchange(initialized, true); + const bool use_unified_layouts = runtime->device.IsKhrUnifiedImageLayoutsSupported(); scheduler->Record([src_buffer, vk_image, vk_aspect_mask, was_initialized, - vk_copies](vk::CommandBuffer cmdbuf) { - CopyBufferToImage(cmdbuf, src_buffer, vk_image, vk_aspect_mask, was_initialized, VideoCommon::FixSmallVectorADL(vk_copies)); + vk_copies, use_unified_layouts](vk::CommandBuffer cmdbuf) { + CopyBufferToImage(cmdbuf, src_buffer, vk_image, vk_aspect_mask, was_initialized, + use_unified_layouts, VideoCommon::FixSmallVectorADL(vk_copies)); }); if (is_rescaled) { @@ -2071,7 +2082,8 @@ bool Image::ScaleUp(bool ignore) { if (NeedsScaleHelper()) { return BlitScaleHelper(true); } else { - BlitScale(*scheduler, *original_image, *scaled_image, info, aspect_mask, resolution); + BlitScale(*scheduler, *original_image, *scaled_image, info, aspect_mask, resolution, + runtime->device.IsKhrUnifiedImageLayoutsSupported()); } return true; } @@ -2096,7 +2108,8 @@ bool Image::ScaleDown(bool ignore) { if (NeedsScaleHelper()) { return BlitScaleHelper(false); } else { - BlitScale(*scheduler, *scaled_image, *original_image, info, aspect_mask, resolution, false); + BlitScale(*scheduler, *scaled_image, *original_image, info, aspect_mask, resolution, + runtime->device.IsKhrUnifiedImageLayoutsSupported(), false); } return true; } diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index 74fef20a11..15081fe924 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -941,6 +941,9 @@ bool Device::GetSuitability(bool requires_swapchain) { VkPhysicalDeviceVulkan12Features features_1_2{}; VkPhysicalDeviceVulkan13Features features_1_3{}; +#ifdef VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_4_FEATURES + VkPhysicalDeviceVulkan14Features features_1_4{}; +#endif // Configure properties. properties.properties = physical.GetProperties(); @@ -980,6 +983,11 @@ bool Device::GetSuitability(bool requires_swapchain) { if (instance_version < VK_API_VERSION_1_3) { FOR_EACH_VK_FEATURE_1_3(FEATURE_EXTENSION); } +#ifdef VK_API_VERSION_1_4 + if (instance_version < VK_API_VERSION_1_4) { + FOR_EACH_VK_FEATURE_1_4(FEATURE_EXTENSION); + } +#endif FOR_EACH_VK_FEATURE_EXT(FEATURE_EXTENSION); FOR_EACH_VK_EXTENSION(EXTENSION); @@ -1015,11 +1023,16 @@ bool Device::GetSuitability(bool requires_swapchain) { // Set next pointer. void** next = &features2.pNext; - // Vulkan 1.2 and 1.3 features + // Vulkan 1.2, 1.3 and 1.4 features if (instance_version >= VK_API_VERSION_1_2) { features_1_2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES; features_1_3.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES; +#ifdef VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_4_FEATURES + features_1_4.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_4_FEATURES; + features_1_3.pNext = &features_1_4; +#endif + features_1_2.pNext = &features_1_3; *next = &features_1_2; @@ -1051,6 +1064,13 @@ bool Device::GetSuitability(bool requires_swapchain) { } else { FOR_EACH_VK_FEATURE_1_3(EXT_FEATURE); } +#ifdef VK_API_VERSION_1_4 + if (instance_version >= VK_API_VERSION_1_4) { + FOR_EACH_VK_FEATURE_1_4(FEATURE); + } else { + FOR_EACH_VK_FEATURE_1_4(EXT_FEATURE); + } +#endif #undef EXT_FEATURE #undef FEATURE @@ -1488,16 +1508,31 @@ void Device::RemoveUnsuitableExtensions() { RemoveExtensionFeatureIfUnsuitable(extensions.maintenance6, features.maintenance6, VK_KHR_MAINTENANCE_6_EXTENSION_NAME); - // VK_KHR_maintenance7 (proposed for Vulkan 1.4, no features) + // VK_KHR_maintenance7 +#ifdef VK_API_VERSION_1_4 + extensions.maintenance7 = instance_version >= VK_API_VERSION_1_4 || + loaded_extensions.contains(VK_KHR_MAINTENANCE_7_EXTENSION_NAME); +#else extensions.maintenance7 = loaded_extensions.contains(VK_KHR_MAINTENANCE_7_EXTENSION_NAME); +#endif RemoveExtensionIfUnsuitable(extensions.maintenance7, VK_KHR_MAINTENANCE_7_EXTENSION_NAME); - // VK_KHR_maintenance8 (proposed for Vulkan 1.4, no features) + // VK_KHR_maintenance8 +#ifdef VK_API_VERSION_1_4 + extensions.maintenance8 = instance_version >= VK_API_VERSION_1_4 || + loaded_extensions.contains(VK_KHR_MAINTENANCE_8_EXTENSION_NAME); +#else extensions.maintenance8 = loaded_extensions.contains(VK_KHR_MAINTENANCE_8_EXTENSION_NAME); +#endif RemoveExtensionIfUnsuitable(extensions.maintenance8, VK_KHR_MAINTENANCE_8_EXTENSION_NAME); - // VK_KHR_maintenance9 (proposed for Vulkan 1.4, no features) + // VK_KHR_maintenance9 +#ifdef VK_API_VERSION_1_4 + extensions.maintenance9 = instance_version >= VK_API_VERSION_1_4 || + loaded_extensions.contains(VK_KHR_MAINTENANCE_9_EXTENSION_NAME); +#else extensions.maintenance9 = loaded_extensions.contains(VK_KHR_MAINTENANCE_9_EXTENSION_NAME); +#endif RemoveExtensionIfUnsuitable(extensions.maintenance9, VK_KHR_MAINTENANCE_9_EXTENSION_NAME); } diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index 7d738a81df..fa16e6949e 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -487,6 +487,11 @@ public: return extensions.workgroup_memory_explicit_layout; } + /// Returns true if the device supports VK_KHR_unified_image_layouts. + bool IsKhrUnifiedImageLayoutsSupported() const { + return extensions.unified_image_layouts; + } + /// Returns true if the device supports VK_KHR_image_format_list. bool IsKhrImageFormatListSupported() const { return extensions.image_format_list || instance_version >= VK_API_VERSION_1_2; diff --git a/src/video_core/vulkan_common/vulkan_wrapper.cpp b/src/video_core/vulkan_common/vulkan_wrapper.cpp index 534a11edd4..0853aa4c0d 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.cpp +++ b/src/video_core/vulkan_common/vulkan_wrapper.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 @@ -47,6 +47,14 @@ bool IsMicrosoftDozen(const char* device_name) { return std::strstr(device_name, "Microsoft") != nullptr; } +constexpr u32 MaxInstanceApiVersion() { +#ifdef VK_API_VERSION_1_4 + return VK_API_VERSION_1_4; +#else + return VK_API_VERSION_1_3; +#endif +} + void SortPhysicalDevices(std::vector& devices, const InstanceDispatch& dld) { // Sort by name, this will set a base and make GPUs with higher numbers appear first // (e.g. GTX 1650 will intentionally be listed before a GTX 1080). @@ -437,8 +445,8 @@ Instance Instance::Create(u32 version, Span layers, Span layers, Span