diff --git a/rpcs3/Emu/RSX/VK/VKGSRenderTypes.hpp b/rpcs3/Emu/RSX/VK/VKGSRenderTypes.hpp index 71bdfd82bb..e1893626ab 100644 --- a/rpcs3/Emu/RSX/VK/VKGSRenderTypes.hpp +++ b/rpcs3/Emu/RSX/VK/VKGSRenderTypes.hpp @@ -304,6 +304,7 @@ namespace vk u32 width; u32 height; u32 pitch; + u8 eye; }; struct draw_call_t diff --git a/rpcs3/Emu/RSX/VK/VKPresent.cpp b/rpcs3/Emu/RSX/VK/VKPresent.cpp index eadc094b7c..fe64e15ee4 100644 --- a/rpcs3/Emu/RSX/VK/VKPresent.cpp +++ b/rpcs3/Emu/RSX/VK/VKPresent.cpp @@ -15,6 +15,23 @@ extern atomic_t g_user_asked_for_screenshot; extern atomic_t g_recording_mode; +namespace +{ + VkFormat RSX_display_format_to_vk_format(u8 format) + { + switch (format) + { + default: + rsx_log.error("Unhandled video output format 0x%x", static_cast(format)); + [[fallthrough]]; + case CELL_VIDEO_OUT_BUFFER_COLOR_FORMAT_X8R8G8B8: + return VK_FORMAT_B8G8R8A8_UNORM; + case CELL_VIDEO_OUT_BUFFER_COLOR_FORMAT_X8B8G8R8: + return VK_FORMAT_R8G8B8A8_UNORM; + } + } +} + void VKGSRender::reinitialize_swapchain() { m_swapchain_dims.width = m_frame->client_width(); @@ -277,10 +294,13 @@ void VKGSRender::frame_context_cleanup(vk::frame_context_t *ctx) vk::advance_completed_frame_counter(); } -vk::viewable_image* VKGSRender::get_present_source(vk::present_surface_info* info, const rsx::avconf& avconfig) +vk::viewable_image* VKGSRender::get_present_source(/* inout */ vk::present_surface_info* info, const rsx::avconf& avconfig) { vk::viewable_image* image_to_flip = nullptr; + // @FIXME: This entire function needs to be rewritten to go through the texture cache's "upload_texture" routine. + // That method is not a 1:1 replacement due to handling of insets that is done differently here. + // Check the surface store first const auto format_bpp = rsx::get_format_block_size_in_bytes(info->format); const auto overlap_info = m_rtts.get_merged_texture_memory_region(*m_current_command_buffer, @@ -333,7 +353,12 @@ vk::viewable_image* VKGSRender::get_present_source(vk::present_surface_info* inf image_to_flip = dynamic_cast(surface->get_raw_texture()); } - if (!image_to_flip) + // The correct output format is determined by the AV configuration set in CellVideoOutConfigure by the game. + // 99.9% of the time, this will match the backbuffer fbo format used in rendering/compositing the output. + // But in some cases, let's just say some devs are creative. + const auto expected_format = RSX_display_format_to_vk_format(avconfig.format); + + if (!image_to_flip) [[ unlikely ]] { // Read from cell const auto range = utils::address_range::start_length(info->address, info->pitch * info->height); @@ -354,22 +379,35 @@ vk::viewable_image* VKGSRender::get_present_source(vk::present_surface_info* inf flush_command_queue(); } - VkFormat format; - switch (avconfig.format) - { - default: - rsx_log.error("Unhandled video output format 0x%x", avconfig.format); - [[fallthrough]]; - case CELL_VIDEO_OUT_BUFFER_COLOR_FORMAT_X8R8G8B8: - format = VK_FORMAT_B8G8R8A8_UNORM; - break; - case CELL_VIDEO_OUT_BUFFER_COLOR_FORMAT_X8B8G8R8: - format = VK_FORMAT_R8G8B8A8_UNORM; - break; - } - m_texture_cache.invalidate_range(*m_current_command_buffer, range, rsx::invalidation_cause::read); - image_to_flip = m_texture_cache.upload_image_simple(*m_current_command_buffer, format, info->address, info->width, info->height, info->pitch); + image_to_flip = m_texture_cache.upload_image_simple(*m_current_command_buffer, expected_format, info->address, info->width, info->height, info->pitch); + } + else if (image_to_flip->format() != expected_format) + { + // Devs are being creative. Force-cast this to the proper pixel layout. + auto dst_img = m_texture_cache.create_temporary_subresource_storage( + RSX_FORMAT_CLASS_COLOR, expected_format, info->width, info->height, 1, 1, 1, + VK_IMAGE_TYPE_2D, 0, VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT); + + if (dst_img) + { + const areai src_rect = { 0, 0, static_cast(info->width), static_cast(info->height) }; + const areai dst_rect = src_rect; + + dst_img->change_layout(*m_current_command_buffer, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + + if (vk::formats_are_bitcast_compatible(dst_img.get(), image_to_flip)) + { + vk::copy_image(*m_current_command_buffer, image_to_flip, dst_img.get(), src_rect, dst_rect, 1); + } + else + { + vk::copy_image_typeless(*m_current_command_buffer, image_to_flip, dst_img.get(), src_rect, dst_rect, 1); + } + + image_to_flip = dst_img.get(); + m_texture_cache.dispose_reusable_image(dst_img); + } } return image_to_flip; @@ -471,13 +509,15 @@ void VKGSRender::flip(const rsx::display_flip_info_t& info) vk::viewable_image *image_to_flip = nullptr, *image_to_flip2 = nullptr; if (info.buffer < display_buffers_count && buffer_width && buffer_height) { - vk::present_surface_info present_info; - present_info.width = buffer_width; - present_info.height = buffer_height; - present_info.pitch = buffer_pitch; - present_info.format = av_format; - present_info.address = rsx::get_address(display_buffers[info.buffer].offset, CELL_GCM_LOCATION_LOCAL); - + vk::present_surface_info present_info + { + .address = rsx::get_address(display_buffers[info.buffer].offset, CELL_GCM_LOCATION_LOCAL), + .format = av_format, + .width = buffer_width, + .height = buffer_height, + .pitch = buffer_pitch, + .eye = 0 + }; image_to_flip = get_present_source(&present_info, avconfig); if (avconfig.stereo_mode != stereo_render_mode_options::disabled) [[unlikely]] @@ -490,6 +530,7 @@ void VKGSRender::flip(const rsx::display_flip_info_t& info) present_info.width = buffer_width; present_info.height = buffer_height; present_info.address = rsx::get_address(image_offset, CELL_GCM_LOCATION_LOCAL); + present_info.eye = 1; image_to_flip2 = get_present_source(&present_info, avconfig); } diff --git a/rpcs3/Emu/RSX/VK/VKTextureCache.cpp b/rpcs3/Emu/RSX/VK/VKTextureCache.cpp index 9255e84237..bda591b6ee 100644 --- a/rpcs3/Emu/RSX/VK/VKTextureCache.cpp +++ b/rpcs3/Emu/RSX/VK/VKTextureCache.cpp @@ -576,6 +576,38 @@ namespace vk return {}; } + std::unique_ptr texture_cache::create_temporary_subresource_storage( + rsx::format_class format_class, VkFormat format, + u16 width, u16 height, u16 depth, u16 layers, u8 mips, + VkImageType image_type, VkFlags image_flags, VkFlags usage_flags) + { + auto image = find_cached_image(format, width, height, depth, mips, image_type, image_flags, usage_flags, VK_SHARING_MODE_EXCLUSIVE); + + if (!image) + { + image = std::make_unique(*vk::get_current_renderer(), m_memory_types.device_local, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, + image_type, + format, + width, height, depth, mips, layers, VK_SAMPLE_COUNT_1_BIT, VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, image_flags | VK_IMAGE_CREATE_ALLOW_NULL_RPCS3, + VMM_ALLOCATION_POOL_TEXTURE_CACHE, format_class); + + if (!image->value) + { + // OOM, bail + return nullptr; + } + } + + return image; + } + + void texture_cache::dispose_reusable_image(std::unique_ptr& image) + { + auto disposable = vk::disposable_t::make(new cached_image_reference_t(this, image)); + vk::get_resource_manager()->dispose(disposable); + } + vk::image_view* texture_cache::create_temporary_subresource_view_impl(vk::command_buffer& cmd, vk::image* source, VkImageType image_type, VkImageViewType view_type, u32 gcm_format, u16 x, u16 y, u16 w, u16 h, u16 d, u8 mips, const rsx::texture_channel_remap_t& remap_vector, bool copy) { @@ -584,22 +616,13 @@ namespace vk const VkFormat dst_format = vk::get_compatible_sampler_format(m_formats_support, gcm_format); const u16 layers = (view_type == VK_IMAGE_VIEW_TYPE_CUBE) ? 6 : 1; - auto image = find_cached_image(dst_format, w, h, d, mips, image_type, image_flags, usage_flags, VK_SHARING_MODE_EXCLUSIVE); + // Provision + auto image = create_temporary_subresource_storage(rsx::classify_format(gcm_format), dst_format, w, h, d, layers, mips, image_type, image_flags, usage_flags); + // OOM? if (!image) { - image = std::make_unique(*vk::get_current_renderer(), m_memory_types.device_local, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, - image_type, - dst_format, - w, h, d, mips, layers, VK_SAMPLE_COUNT_1_BIT, VK_IMAGE_LAYOUT_UNDEFINED, - VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, image_flags | VK_IMAGE_CREATE_ALLOW_NULL_RPCS3, - VMM_ALLOCATION_POOL_TEXTURE_CACHE, rsx::classify_format(gcm_format)); - - if (!image->value) - { - // OOM, bail - return nullptr; - } + return nullptr; } // This method is almost exclusively used to work on framebuffer resources @@ -1449,9 +1472,9 @@ namespace vk vk::change_image_layout(cmd, image.get(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); + // Fully dispose immediately. These immages aren't really reusable right now. auto result = image.get(); - auto disposable = vk::disposable_t::make(new cached_image_reference_t(this, image)); - vk::get_resource_manager()->dispose(disposable); + vk::get_resource_manager()->dispose(image); return result; } diff --git a/rpcs3/Emu/RSX/VK/VKTextureCache.h b/rpcs3/Emu/RSX/VK/VKTextureCache.h index 2ff0b5b447..c8d7a2fdc4 100644 --- a/rpcs3/Emu/RSX/VK/VKTextureCache.h +++ b/rpcs3/Emu/RSX/VK/VKTextureCache.h @@ -504,6 +504,13 @@ namespace vk void destroy() override; + std::unique_ptr create_temporary_subresource_storage( + rsx::format_class format_class, VkFormat format, + u16 width, u16 height, u16 depth, u16 layers, u8 mips, + VkImageType image_type, VkFlags image_flags, VkFlags usage_flags); + + void dispose_reusable_image(std::unique_ptr& tex); + bool is_depth_texture(u32 rsx_address, u32 rsx_size) override; void on_frame_end() override;