diff --git a/src/video_core/renderer_vulkan/liverpool_to_vk.cpp b/src/video_core/renderer_vulkan/liverpool_to_vk.cpp index 651b863f..7adeebdc 100644 --- a/src/video_core/renderer_vulkan/liverpool_to_vk.cpp +++ b/src/video_core/renderer_vulkan/liverpool_to_vk.cpp @@ -281,6 +281,63 @@ vk::BorderColor BorderColor(AmdGpu::BorderColor color) { } } +const std::vector& GetAllFormats() { + static const std::vector formats{ + vk::Format::eR32G32B32A32Sfloat, + vk::Format::eR32G32B32Uint, + vk::Format::eR8G8B8A8Unorm, + vk::Format::eB8G8R8A8Unorm, + vk::Format::eR8G8B8A8Srgb, + vk::Format::eB8G8R8A8Srgb, + vk::Format::eR32G32B32Sfloat, + vk::Format::eR32G32Sfloat, + vk::Format::eB5G6R5UnormPack16, + vk::Format::eR5G6B5UnormPack16, + vk::Format::eR8Unorm, + vk::Format::eBc3SrgbBlock, + vk::Format::eBc3UnormBlock, + vk::Format::eBc4UnormBlock, + vk::Format::eBc5UnormBlock, + vk::Format::eR16G16B16A16Sint, + vk::Format::eR16G16Sfloat, + vk::Format::eB10G11R11UfloatPack32, + vk::Format::eA2B10G10R10UnormPack32, + vk::Format::eBc7SrgbBlock, + vk::Format::eBc1RgbaUnormBlock, + vk::Format::eR8G8B8A8Uint, + vk::Format::eR16Sfloat, + vk::Format::eR32Sfloat, + vk::Format::eR16G16B16A16Sfloat, + vk::Format::eR32Uint, + vk::Format::eR32Sint, + vk::Format::eR8G8Unorm, + vk::Format::eR8G8Snorm, + vk::Format::eBc7UnormBlock, + vk::Format::eBc2UnormBlock, + vk::Format::eR16G16Snorm, + vk::Format::eA2R10G10B10UnormPack32, + vk::Format::eA2R10G10B10SnormPack32, + vk::Format::eB10G11R11UfloatPack32, + vk::Format::eR16G16Sfloat, + vk::Format::eR16G16B16A16Snorm, + vk::Format::eR32G32Uint, + vk::Format::eR4G4B4A4UnormPack16, + vk::Format::eR16G16B16A16Uint, + vk::Format::eR32G32B32A32Uint, + vk::Format::eR8Sint, + vk::Format::eBc1RgbaSrgbBlock, + vk::Format::eR16G16Sint, + vk::Format::eR8G8B8A8Uscaled, + vk::Format::eR16Unorm, + vk::Format::eR16G16B16A16Unorm, + vk::Format::eD32SfloatS8Uint, + vk::Format::eD32Sfloat, + vk::Format::eD16Unorm, + vk::Format::eD16UnormS8Uint, + }; + return formats; +} + vk::Format SurfaceFormat(AmdGpu::DataFormat data_format, AmdGpu::NumberFormat num_format) { if (data_format == AmdGpu::DataFormat::Format32_32_32_32 && diff --git a/src/video_core/renderer_vulkan/liverpool_to_vk.h b/src/video_core/renderer_vulkan/liverpool_to_vk.h index aae39630..dbdaf088 100644 --- a/src/video_core/renderer_vulkan/liverpool_to_vk.h +++ b/src/video_core/renderer_vulkan/liverpool_to_vk.h @@ -3,6 +3,7 @@ #pragma once +#include #include "video_core/amdgpu/liverpool.h" #include "video_core/amdgpu/pixel_format.h" #include "video_core/amdgpu/resource.h" @@ -38,6 +39,8 @@ vk::SamplerMipmapMode MipFilter(AmdGpu::MipFilter filter); vk::BorderColor BorderColor(AmdGpu::BorderColor color); +const std::vector& GetAllFormats(); + vk::Format SurfaceFormat(AmdGpu::DataFormat data_format, AmdGpu::NumberFormat num_format); vk::Format AdjustColorBufferFormat(vk::Format base_format, diff --git a/src/video_core/renderer_vulkan/vk_instance.cpp b/src/video_core/renderer_vulkan/vk_instance.cpp index 7fc981ee..b11d950f 100644 --- a/src/video_core/renderer_vulkan/vk_instance.cpp +++ b/src/video_core/renderer_vulkan/vk_instance.cpp @@ -9,6 +9,7 @@ #include "common/assert.h" #include "sdl_window.h" +#include "video_core/renderer_vulkan/liverpool_to_vk.h" #include "video_core/renderer_vulkan/vk_instance.h" #include "video_core/renderer_vulkan/vk_platform.h" @@ -28,6 +29,15 @@ std::vector GetSupportedExtensions(vk::PhysicalDevice physical) { return supported_extensions; } +std::unordered_map GetFormatProperties( + vk::PhysicalDevice physical) { + std::unordered_map format_properties; + for (const auto& format : LiverpoolToVK::GetAllFormats()) { + format_properties.emplace(format, physical.getFormatProperties(format)); + } + return format_properties; +} + std::string GetReadableVersion(u32 version) { return fmt::format("{}.{}.{}", VK_VERSION_MAJOR(version), VK_VERSION_MINOR(version), VK_VERSION_PATCH(version)); @@ -93,6 +103,7 @@ Instance::Instance(Frontend::WindowSDL& window, s32 physical_device_index, } available_extensions = GetSupportedExtensions(physical_device); + format_properties = GetFormatProperties(physical_device); properties = physical_device.getProperties(); CollectDeviceParameters(); ASSERT_MSG(properties.apiVersion >= TargetVulkanApiVersion, @@ -102,6 +113,22 @@ Instance::Instance(Frontend::WindowSDL& window, s32 physical_device_index, CreateDevice(); CollectToolingInfo(); + + // Check and log format support details. + for (const auto& key : format_properties | std::views::keys) { + const auto format = key; + if (!IsFormatSupported(format)) { + const auto alternative = GetAlternativeFormat(format); + if (IsFormatSupported(alternative)) { + LOG_WARNING(Render_Vulkan, "Format {} is not supported, falling back to {}", + vk::to_string(format), vk::to_string(alternative)); + } else { + LOG_ERROR(Render_Vulkan, + "Format {} is not supported and no suitable alternative is supported.", + vk::to_string(format)); + } + } + } } Instance::~Instance() { @@ -303,7 +330,7 @@ bool Instance::CreateDevice() { vk::TimeDomainEXT::eClockMonotonicRaw) != time_domains.cend(); #else // Tracy limitation means only Windows and Linux can use host time domain. - // See https://github.com/shadps4-emu/tracy/blob/c6d779d78508514102fbe1b8eb28bda10d95bb2a/public/tracy/TracyVulkan.hpp#L384-L389 + // https://github.com/shadps4-emu/tracy/blob/c6d779d78508514102fbe1b8eb28bda10d95bb2a/public/tracy/TracyVulkan.hpp#L384-L389 const bool has_host_time_domain = false; #endif if (has_host_time_domain) { @@ -377,4 +404,52 @@ void Instance::CollectToolingInfo() { } } +bool Instance::IsFormatSupported(const vk::Format format) const { + if (format == vk::Format::eUndefined) [[unlikely]] { + return true; + } + + const auto it = format_properties.find(format); + if (it == format_properties.end()) { + UNIMPLEMENTED_MSG("Properties of format {} have not been queried.", vk::to_string(format)); + } + + constexpr vk::FormatFeatureFlags optimal_flags = vk::FormatFeatureFlagBits::eTransferSrc | + vk::FormatFeatureFlagBits::eTransferDst | + vk::FormatFeatureFlagBits::eSampledImage; + return (it->second.optimalTilingFeatures & optimal_flags) == optimal_flags; +} + +vk::Format Instance::GetAlternativeFormat(const vk::Format format) const { + if (format == vk::Format::eB5G6R5UnormPack16) { + return vk::Format::eR5G6B5UnormPack16; + } + return format; +} + +vk::Format Instance::GetSupportedFormat(const vk::Format format) const { + if (IsFormatSupported(format)) [[likely]] { + return format; + } + const vk::Format alternative = GetAlternativeFormat(format); + if (IsFormatSupported(alternative)) [[likely]] { + return alternative; + } + return format; +} + +vk::ComponentMapping Instance::GetSupportedComponentSwizzle(vk::Format format, + vk::ComponentMapping swizzle) const { + if (IsFormatSupported(format)) [[likely]] { + return swizzle; + } + + vk::ComponentMapping supported_swizzle = swizzle; + if (format == vk::Format::eB5G6R5UnormPack16) { + // B5G6R5 -> R5G6B5 + std::swap(supported_swizzle.r, supported_swizzle.b); + } + return supported_swizzle; +} + } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_instance.h b/src/video_core/renderer_vulkan/vk_instance.h index 85bd57e2..0f291d8e 100644 --- a/src/video_core/renderer_vulkan/vk_instance.h +++ b/src/video_core/renderer_vulkan/vk_instance.h @@ -4,6 +4,7 @@ #pragma once #include +#include #include "video_core/renderer_vulkan/vk_platform.h" @@ -34,6 +35,13 @@ public: /// Returns a formatted string for the driver version std::string GetDriverVersionName(); + /// Gets a compatibility format if the format is not supported. + [[nodiscard]] vk::Format GetSupportedFormat(vk::Format format) const; + + /// Re-orders a component swizzle for format compatibility, if needed. + [[nodiscard]] vk::ComponentMapping GetSupportedComponentSwizzle( + vk::Format format, vk::ComponentMapping swizzle) const; + /// Returns the Vulkan instance vk::Instance GetInstance() const { return *instance; @@ -211,6 +219,12 @@ private: void CollectDeviceParameters(); void CollectToolingInfo(); + /// Determines if a format is supported. + [[nodiscard]] bool IsFormatSupported(vk::Format format) const; + + /// Gets a commonly available alternative for an unsupported pixel format. + vk::Format GetAlternativeFormat(const vk::Format format) const; + private: vk::DynamicLoader dl{VULKAN_LIBRARY_NAME}; vk::UniqueInstance instance; @@ -226,6 +240,7 @@ private: vk::Queue graphics_queue; std::vector physical_devices; std::vector available_extensions; + std::unordered_map format_properties; TracyVkCtx profiler_context{}; u32 queue_family_index{0}; bool image_view_reinterpretation{true}; diff --git a/src/video_core/texture_cache/image.cpp b/src/video_core/texture_cache/image.cpp index 2b444774..b4b3f48a 100644 --- a/src/video_core/texture_cache/image.cpp +++ b/src/video_core/texture_cache/image.cpp @@ -142,7 +142,7 @@ Image::Image(const Vulkan::Instance& instance_, Vulkan::Scheduler& scheduler_, const vk::ImageCreateInfo image_ci = { .flags = flags, .imageType = info.type, - .format = info.pixel_format, + .format = instance->GetSupportedFormat(info.pixel_format), .extent{ .width = info.size.width, .height = info.size.height, diff --git a/src/video_core/texture_cache/image_view.cpp b/src/video_core/texture_cache/image_view.cpp index 4628a8e9..8f972253 100644 --- a/src/video_core/texture_cache/image_view.cpp +++ b/src/video_core/texture_cache/image_view.cpp @@ -89,8 +89,8 @@ ImageView::ImageView(const Vulkan::Instance& instance, const ImageViewInfo& info .pNext = usage_override ? &usage_ci : nullptr, .image = image.image, .viewType = info.type, - .format = format, - .components = info.mapping, + .format = instance.GetSupportedFormat(format), + .components = instance.GetSupportedComponentSwizzle(format, info.mapping), .subresourceRange{ .aspectMask = aspect, .baseMipLevel = 0U,