diff --git a/src/graphics/guest_gpu/gpu_defs.h b/src/graphics/guest_gpu/gpu_defs.h index 06d1e790d..3aa363457 100644 --- a/src/graphics/guest_gpu/gpu_defs.h +++ b/src/graphics/guest_gpu/gpu_defs.h @@ -129,6 +129,7 @@ struct ColorComponentMapping { static_assert(sizeof(ColorComponentMapping) == sizeof(uint8_t)); inline constexpr ColorComponentMapping ColorMappingAbgr {0x1bu}; // 3, 2, 1, 0 +inline constexpr ColorComponentMapping ColorMappingBgra {0xc6u}; // 2, 1, 0, 3 enum class DepthFormat : uint32_t { kInvalid = 0, diff --git a/src/graphics/host_gpu/objects/textureCommon.cpp b/src/graphics/host_gpu/objects/textureCommon.cpp index 0ca4dda59..6bf79cb89 100644 --- a/src/graphics/host_gpu/objects/textureCommon.cpp +++ b/src/graphics/host_gpu/objects/textureCommon.cpp @@ -114,6 +114,10 @@ RenderTargetFormatInfo TextureGetRenderTargetFormat(uint32_t raw_layout, uint32_ Prospero::ChannelOrder::kStandard)) { return {VK_FORMAT_R16G16B16A16_SFLOAT, 8}; } + if (is(Prospero::ChannelLayout::k16_16_16_16, Prospero::ChannelType::kFloat, + Prospero::ChannelOrder::kAlt)) { + return {VK_FORMAT_R16G16B16A16_SFLOAT, 8, Prospero::ColorMappingBgra}; + } if (is(Prospero::ChannelLayout::k16_16_16_16, Prospero::ChannelType::kFloat, Prospero::ChannelOrder::kReversed)) { return {VK_FORMAT_R16G16B16A16_SFLOAT, 8, Prospero::ColorMappingAbgr}; diff --git a/src/graphics/host_gpu/renderer/colorRenderTarget.cpp b/src/graphics/host_gpu/renderer/colorRenderTarget.cpp index d886a8e63..b863bbba9 100644 --- a/src/graphics/host_gpu/renderer/colorRenderTarget.cpp +++ b/src/graphics/host_gpu/renderer/colorRenderTarget.cpp @@ -179,6 +179,10 @@ static float ColorClearF16(uint32_t value) { color.float32[1] = ColorClearF16((c0 >> 16u) & 0xffffu); color.float32[2] = ColorClearF16(c1 & 0xffffu); color.float32[3] = ColorClearF16((c1 >> 16u) & 0xffffu); + if (rt.info.channel_order == + Prospero::GpuEnumValue(Prospero::ChannelOrder::kAlt)) { + std::swap(color.float32[0], color.float32[2]); + } break; case Prospero::ChannelLayout::k32: case Prospero::ChannelLayout::k11_11_10: @@ -535,6 +539,10 @@ void ResolveRenderColorTarget(uint64_t submit_id, CommandBuffer* buffer, const H order == Prospero::ChannelOrder::kAlt)) || (layout == Prospero::ChannelLayout::k10_10_10_2 && type == Prospero::ChannelType::kUNorm && + (order == Prospero::ChannelOrder::kStandard || + order == Prospero::ChannelOrder::kAlt)) || + (layout == Prospero::ChannelLayout::k16_16_16_16 && + type == Prospero::ChannelType::kFloat && (order == Prospero::ChannelOrder::kStandard || order == Prospero::ChannelOrder::kAlt)); EXIT_NOT_IMPLEMENTED(!supported_display_format); @@ -555,6 +563,9 @@ void ResolveRenderColorTarget(uint64_t submit_id, CommandBuffer* buffer, const H r->extent = video_image.image->extent; r->base_mip_level = 0; r->buffer_size = video_image.size; + r->export_mapping = TextureGetRenderTargetFormat(rt.info.format, rt.info.channel_type, + rt.info.channel_order) + .export_mapping; } } diff --git a/src/graphics/host_gpu/renderer/descriptors.cpp b/src/graphics/host_gpu/renderer/descriptors.cpp index 3d23a86b8..cc5bfeefb 100644 --- a/src/graphics/host_gpu/renderer/descriptors.cpp +++ b/src/graphics/host_gpu/renderer/descriptors.cpp @@ -321,12 +321,20 @@ static void ValidateDepthTargetBinding(const ShaderRecompiler::IR::ImageResource TileGetTexturePitch(descriptor.Format(), static_cast(descriptor.Width5()) + 1u, 1, descriptor.TileMode()); EXIT("unsupported sampled depth target: resource=%d descriptor=%d encoding=%d format=%d " - "image_format=%d view_format=%d descriptor_pitch=%u target_pitch=%u " - "addr=0x%016" PRIx64 " size=0x%016" PRIx64 "\n", + "kind=%u dimension=%u mip=%u read=%d written=%d atomic=%d compare=%d " + "image_format=%d view_format=%d image_layers=%u descriptor_type=%u base_array=%u " + "depth=%u descriptor_pitch=%u target_pitch=%u fields=%08x/%08x/%08x/%08x/%08x/" + "%08x/%08x/%08x addr=0x%016" PRIx64 " size=0x%016" PRIx64 "\n", resource_ok, descriptor_ok, encoding_ok, format_ok, + static_cast(resource.kind), static_cast(resource.dimension), + static_cast(resource.mip_mode), resource.read, resource.written, resource.atomic, + resource.depth_compare, image == nullptr ? static_cast(VK_FORMAT_UNDEFINED) : static_cast(image->format), - static_cast(view_format), descriptor_pitch, - image == nullptr ? 0u : image->guest_pitch, descriptor.Base40(), size); + static_cast(view_format), image == nullptr ? 0u : image->layers, descriptor.Type(), + descriptor.BaseArray5(), descriptor.Depth(), descriptor_pitch, + image == nullptr ? 0u : image->guest_pitch, descriptor.fields[0], descriptor.fields[1], + descriptor.fields[2], descriptor.fields[3], descriptor.fields[4], descriptor.fields[5], + descriptor.fields[6], descriptor.fields[7], descriptor.Base40(), size); } static bool IsSupportedStorageTextureDescriptor(const ShaderRecompiler::IR::ImageResource& resource, diff --git a/src/graphics/host_gpu/renderer/imageInfo.h b/src/graphics/host_gpu/renderer/imageInfo.h index f2deb692b..72b19c162 100644 --- a/src/graphics/host_gpu/renderer/imageInfo.h +++ b/src/graphics/host_gpu/renderer/imageInfo.h @@ -240,6 +240,7 @@ struct VideoOutInfo { uint32_t tile_mode = 0; uint32_t dcc_control = 0; VideoOutCompression compression = VideoOutCompression::Unsupported; + bool bgra16 = false; }; [[nodiscard]] inline VideoOutCompression @@ -265,48 +266,61 @@ CanUseVideoOutNativeWithoutUpload(VideoOutCompression compression, bool render_t } struct VideoOutPixelFormatInfo { - VkFormat format = VK_FORMAT_UNDEFINED; - uint32_t guest_format = 0; + VkFormat format = VK_FORMAT_UNDEFINED; + uint32_t guest_format = 0; + uint32_t bytes_per_element = 0; + bool bgra16 = false; }; +struct VideoOutFormatPolicy { + uint64_t pixel_format; + VideoOutPixelFormatInfo info; +}; + +inline constexpr std::array VIDEO_OUT_FORMAT_POLICIES {{ + {0x8000000022000000ull, + {VK_FORMAT_R8G8B8A8_SRGB, Prospero::GpuEnumValue(Prospero::BufferFormat::k8_8_8_8Srgb), 4, + false}}, + {0x8000000000000000ull, + {VK_FORMAT_B8G8R8A8_SRGB, Prospero::GpuEnumValue(Prospero::BufferFormat::k8_8_8_8Srgb), 4, + false}}, + {0x8100000022000000ull, + {VK_FORMAT_A2B10G10R10_UNORM_PACK32, + Prospero::GpuEnumValue(Prospero::BufferFormat::k10_10_10_2UNorm), 4, false}}, + {0x8100000000000000ull, + {VK_FORMAT_A2R10G10B10_UNORM_PACK32, + Prospero::GpuEnumValue(Prospero::BufferFormat::k10_10_10_2UNorm), 4, false}}, + {0xc001000622000000ull, + {VK_FORMAT_R16G16B16A16_SFLOAT, + Prospero::GpuEnumValue(Prospero::BufferFormat::k16_16_16_16Float), 8, false}}, + {0xc001000600000000ull, + {VK_FORMAT_R16G16B16A16_SFLOAT, + Prospero::GpuEnumValue(Prospero::BufferFormat::k16_16_16_16Float), 8, true}}, +}}; + [[nodiscard]] inline bool DecodeVideoOutPixelFormat(uint64_t pixel_format, VideoOutPixelFormatInfo* info) { if (info == nullptr) { return false; } - VideoOutPixelFormatInfo next {}; - switch (pixel_format) { - case 0x8000000022000000ull: // SCE_VIDEO_OUT_PIXEL_FORMAT2_R8_G8_B8_A8_SRGB - next.format = VK_FORMAT_R8G8B8A8_SRGB; - next.guest_format = Prospero::GpuEnumValue(Prospero::BufferFormat::k8_8_8_8Srgb); - break; - case 0x8000000000000000ull: // SCE_VIDEO_OUT_PIXEL_FORMAT2_B8_G8_R8_A8_SRGB - next.format = VK_FORMAT_B8G8R8A8_SRGB; - next.guest_format = Prospero::GpuEnumValue(Prospero::BufferFormat::k8_8_8_8Srgb); - break; - case 0x8100000022000000ull: // SCE_VIDEO_OUT_PIXEL_FORMAT2_R10_G10_B10_A2_SRGB - next.format = VK_FORMAT_A2B10G10R10_UNORM_PACK32; - next.guest_format = Prospero::GpuEnumValue(Prospero::BufferFormat::k10_10_10_2UNorm); - break; - case 0x8100000000000000ull: // SCE_VIDEO_OUT_PIXEL_FORMAT2_B10_G10_R10_A2_SRGB - next.format = VK_FORMAT_A2R10G10B10_UNORM_PACK32; - next.guest_format = Prospero::GpuEnumValue(Prospero::BufferFormat::k10_10_10_2UNorm); - break; - default: return false; + for (const auto& policy: VIDEO_OUT_FORMAT_POLICIES) { + if (policy.pixel_format == pixel_format) { + *info = policy.info; + return true; + } } - *info = next; - return true; + return false; } -[[nodiscard]] inline bool IsSupportedVideoOutFormat(uint32_t guest_format, VkFormat format) { - const bool rgba8 = - guest_format == Prospero::GpuEnumValue(Prospero::BufferFormat::k8_8_8_8Srgb) && - (format == VK_FORMAT_R8G8B8A8_SRGB || format == VK_FORMAT_B8G8R8A8_SRGB); - const bool rgb10 = - guest_format == Prospero::GpuEnumValue(Prospero::BufferFormat::k10_10_10_2UNorm) && - (format == VK_FORMAT_A2B10G10R10_UNORM_PACK32 || - format == VK_FORMAT_A2R10G10B10_UNORM_PACK32); - return rgba8 || rgb10; +[[nodiscard]] inline bool IsSupportedVideoOutFormat(const VideoOutInfo& info) { + for (const auto& policy: VIDEO_OUT_FORMAT_POLICIES) { + if (info.format == policy.info.format && info.guest_format == policy.info.guest_format && + info.bytes_per_element == policy.info.bytes_per_element && + info.bgra16 == policy.info.bgra16) { + return true; + } + } + return false; } enum class DepthOverlap : uint8_t { None, RetireSampled, ExpandTarget, Unsupported }; diff --git a/src/graphics/host_gpu/renderer/textureCache.cpp b/src/graphics/host_gpu/renderer/textureCache.cpp index 62240a797..12d750cf0 100644 --- a/src/graphics/host_gpu/renderer/textureCache.cpp +++ b/src/graphics/host_gpu/renderer/textureCache.cpp @@ -1142,9 +1142,8 @@ void ValidateVideoOutInfo(GraphicContext* ctx, const VideoOutInfo& info) { (info.address & 0xffffu) != 0 || info.width == 0 || info.height == 0 || info.width > 16384 || info.height > 16384 || info.pitch < info.width || info.tile_mode != Prospero::GpuEnumValue(Prospero::TileMode::kRenderTarget) || - info.bytes_per_element != 4 || compression == VideoOutCompression::Unsupported || - compression != info.compression || metadata_invalid || - !IsSupportedVideoOutFormat(info.guest_format, info.format)) { + compression == VideoOutCompression::Unsupported || compression != info.compression || + metadata_invalid || !IsSupportedVideoOutFormat(info)) { EXIT("TextureCache: unsupported video-out surface, ctx=%p addr=0x%016" PRIx64 " size=0x%016" PRIx64 " metadata=0x%016" PRIx64 " dcc=0x%08" PRIx32 " extent=%ux%u pitch=%u tile=%u guest_format=%u bpe=%u vk_format=%d\n", @@ -1211,6 +1210,12 @@ void UploadVideoOut(GraphicContext* ctx, VideoOutVulkanImage* image, const Video TileConvertTiledToLinearRenderTarget( scratch.Data(), reinterpret_cast(info.address), info.width, info.height, info.pitch, info.bytes_per_element, info.size); + if (info.bgra16) { + auto* pixels = static_cast(scratch.Data()); + for (uint64_t i = 0; i < info.size / sizeof(uint16_t); i += 4) { + std::swap(pixels[i], pixels[i + 2]); + } + } UtilFillImage(ctx, image, scratch.Data(), info.size, info.pitch, static_cast(VK_IMAGE_LAYOUT_GENERAL)); } diff --git a/src/graphics/presentation/videoOut.cpp b/src/graphics/presentation/videoOut.cpp index 436bb7f92..96b5a5538 100644 --- a/src/graphics/presentation/videoOut.cpp +++ b/src/graphics/presentation/videoOut.cpp @@ -303,7 +303,8 @@ static Graphics::VideoOutInfo MakeVideoOutInfo(const VideoOutBufferAttribute2& a if (!Graphics::DecodeVideoOutPixelFormat(attribute.pixel_format, &pixel_format)) { EXIT("unsupported video-out pixel format: 0x%016" PRIx64 "\n", attribute.pixel_format); } - const auto tile_mode = Graphics::Prospero::GpuEnumValue(Graphics::Prospero::TileMode::kRenderTarget); + const auto tile_mode = + Graphics::Prospero::GpuEnumValue(Graphics::Prospero::TileMode::kRenderTarget); const auto pitch = Graphics::TileGetTexturePitch(pixel_format.guest_format, attribute.width, 1, tile_mode); Graphics::TileSizeAlign total {}; @@ -321,10 +322,11 @@ static Graphics::VideoOutInfo MakeVideoOutInfo(const VideoOutBufferAttribute2& a info.width = attribute.width; info.height = attribute.height; info.pitch = pitch; - info.bytes_per_element = 4; + info.bytes_per_element = pixel_format.bytes_per_element; info.tile_mode = tile_mode; info.dcc_control = attribute.dcc_control; info.compression = compression; + info.bgra16 = pixel_format.bgra16; return info; } diff --git a/tests/ShaderRecompilerComputeTests.cpp b/tests/ShaderRecompilerComputeTests.cpp index 1a15e7c28..78f9b228c 100644 --- a/tests/ShaderRecompilerComputeTests.cpp +++ b/tests/ShaderRecompilerComputeTests.cpp @@ -10706,62 +10706,97 @@ void CheckBufferImageWrites() { void CheckImageOverlapResolution() { CheckBufferImageWrites(); - VideoOutPixelFormatInfo video_format {}; - Require("ImageOverlapResolution", "existing video-out formats", - DecodeVideoOutPixelFormat(0x8000000022000000ull, &video_format) && - video_format.format == VK_FORMAT_R8G8B8A8_SRGB && - video_format.guest_format == Prospero::GpuEnumValue(Prospero::BufferFormat::k8_8_8_8Srgb) && - DecodeVideoOutPixelFormat(0x8000000000000000ull, &video_format) && - video_format.format == VK_FORMAT_B8G8R8A8_SRGB && - video_format.guest_format == Prospero::GpuEnumValue(Prospero::BufferFormat::k8_8_8_8Srgb) && - DecodeVideoOutPixelFormat(0x8100000022000000ull, &video_format) && - video_format.format == VK_FORMAT_A2B10G10R10_UNORM_PACK32 && - video_format.guest_format == Prospero::GpuEnumValue(Prospero::BufferFormat::k10_10_10_2UNorm), - "pre-existing PS5 video-out format mappings changed during decoder centralization"); - Require("ImageOverlapResolution", "B10G10R10A2 video-out format", - DecodeVideoOutPixelFormat(0x8100000000000000ull, &video_format) && - video_format.format == VK_FORMAT_A2R10G10B10_UNORM_PACK32 && - video_format.guest_format == - Prospero::GpuEnumValue(Prospero::BufferFormat::k10_10_10_2UNorm) && - IsSupportedVideoOutFormat(video_format.guest_format, video_format.format), - "PS5 B10G10R10A2 sRGB video-out format did not preserve alternate channel order"); - Require("ImageOverlapResolution", "video-out format guards", - !DecodeVideoOutPixelFormat(0x8100070400000000ull, &video_format) && - !DecodeVideoOutPixelFormat(0x8100000000000000ull, nullptr) && - !IsSupportedVideoOutFormat(Prospero::GpuEnumValue(Prospero::BufferFormat::k8_8_8_8Srgb), - VK_FORMAT_A2R10G10B10_UNORM_PACK32) && - !IsSupportedVideoOutFormat(Prospero::GpuEnumValue(Prospero::BufferFormat::k10_10_10_2UNorm), - VK_FORMAT_R8G8B8A8_SRGB), - "unsupported PS5 video-out formats or mismatched guest/host pairs were admitted"); + VideoOutPixelFormatInfo video_format{}; + Require( + "ImageOverlapResolution", "existing video-out formats", + DecodeVideoOutPixelFormat(0x8000000022000000ull, &video_format) && + video_format.format == VK_FORMAT_R8G8B8A8_SRGB && + video_format.guest_format == + Prospero::GpuEnumValue(Prospero::BufferFormat::k8_8_8_8Srgb) && + DecodeVideoOutPixelFormat(0x8000000000000000ull, &video_format) && + video_format.format == VK_FORMAT_B8G8R8A8_SRGB && + video_format.guest_format == + Prospero::GpuEnumValue(Prospero::BufferFormat::k8_8_8_8Srgb) && + DecodeVideoOutPixelFormat(0x8100000022000000ull, &video_format) && + video_format.format == VK_FORMAT_A2B10G10R10_UNORM_PACK32 && + video_format.guest_format == + Prospero::GpuEnumValue(Prospero::BufferFormat::k10_10_10_2UNorm), + "pre-existing PS5 video-out format mappings changed during decoder " + "centralization"); + Require( + "ImageOverlapResolution", "B10G10R10A2 video-out format", + DecodeVideoOutPixelFormat(0x8100000000000000ull, &video_format) && + video_format.format == VK_FORMAT_A2R10G10B10_UNORM_PACK32 && + video_format.guest_format == + Prospero::GpuEnumValue(Prospero::BufferFormat::k10_10_10_2UNorm), + "PS5 B10G10R10A2 sRGB video-out format did not preserve alternate " + "channel order"); + Require( + "ImageOverlapResolution", "RGBA16 float video-out formats", + DecodeVideoOutPixelFormat(0xc001000622000000ull, &video_format) && + video_format.format == VK_FORMAT_R16G16B16A16_SFLOAT && + video_format.guest_format == + Prospero::GpuEnumValue( + Prospero::BufferFormat::k16_16_16_16Float) && + video_format.bytes_per_element == 8 && + DecodeVideoOutPixelFormat(0xc001000600000000ull, &video_format) && + video_format.bgra16, + "PS5 RGBA16-float video-out formats lost their 64-bpp storage contract"); + VideoOutInfo supported_video_out{}; + supported_video_out.format = video_format.format; + supported_video_out.guest_format = video_format.guest_format; + supported_video_out.bytes_per_element = video_format.bytes_per_element; + supported_video_out.bgra16 = video_format.bgra16; + Require( + "ImageOverlapResolution", "video-out format policy", + IsSupportedVideoOutFormat(supported_video_out), + "decoded PS5 video-out format was rejected by the centralized policy"); + Require( + "ImageOverlapResolution", "video-out byte-size guards", ([&] { + auto mismatched = supported_video_out; + mismatched.bytes_per_element = 4; + return !IsSupportedVideoOutFormat(mismatched); + })(), + "video-out format validation admitted a mismatched storage element size"); + Require( + "ImageOverlapResolution", "video-out format guards", + !DecodeVideoOutPixelFormat(0x8100070400000000ull, &video_format) && + !DecodeVideoOutPixelFormat(0xc001070700000000ull, &video_format) && + !DecodeVideoOutPixelFormat(0x8100000000000000ull, nullptr) && !([&] { + auto mismatched = supported_video_out; + mismatched.guest_format = + Prospero::GpuEnumValue(Prospero::BufferFormat::k8_8_8_8Srgb); + return IsSupportedVideoOutFormat(mismatched); + })(), + "unsupported PS5 video-out formats or mismatched guest/host pairs were " + "admitted"); TileSizeAlign render_target_layout{}; Require("ImageOverlapResolution", "R8 render-target pitch", TileGetRenderTargetPitch(1920, 1) == 2048, "R8 target did not use native 256-pixel block width"); Require("ImageOverlapResolution", "R8 render-target size", - TileGetRenderTargetSize(1920, 1080, 2048, 1, - &render_target_layout) && + TileGetRenderTargetSize(1920, 1080, 2048, 1, &render_target_layout) && render_target_layout.size == 0x280000 && render_target_layout.align == 0x10000, "R8 target did not use native 64-KiB block geometry"); - Require("ImageOverlapResolution", "RGBA8 render-target size", - TileGetRenderTargetPitch(1920, 4) == 1920 && - TileGetRenderTargetSize(1920, 1080, 1920, 4, - &render_target_layout) && - render_target_layout.size == 0x870000, - "RGBA8 target layout regressed"); + Require( + "ImageOverlapResolution", "RGBA8 render-target size", + TileGetRenderTargetPitch(1920, 4) == 1920 && + TileGetRenderTargetSize(1920, 1080, 1920, 4, &render_target_layout) && + render_target_layout.size == 0x870000, + "RGBA8 target layout regressed"); Require("ImageOverlapResolution", "PPSA19268 render-target edge", TileGetRenderTargetPitch(120, 8) == 128 && - TileGetRenderTargetSize(120, 67, 128, 8, - &render_target_layout) && + TileGetRenderTargetSize(120, 67, 128, 8, &render_target_layout) && render_target_layout.size == 0x20000 && ImageRangeOverlaps(0x11029fff8ull, 1, 0x110280000ull, render_target_layout.size), "title write did not land in the final native render-target block"); - Require("ImageOverlapResolution", "render-target layout rejection", - TileGetRenderTargetPitch(1920, 3) == 0 && - !TileGetRenderTargetSize(1920, 1080, 1920, 1, - &render_target_layout), - "unsupported element size or mismatched pitch was admitted"); + Require( + "ImageOverlapResolution", "render-target layout rejection", + TileGetRenderTargetPitch(1920, 3) == 0 && + !TileGetRenderTargetSize(1920, 1080, 1920, 1, &render_target_layout), + "unsupported element size or mismatched pitch was admitted"); TileSizeOffset mip_levels[16]{}; TilePaddedSize mip_padded[16]{};