From 5691046dcc2c1bd57f5b6f21fc5ced8735771f41 Mon Sep 17 00:00:00 2001 From: Vinicius Rangel Date: Thu, 13 Mar 2025 13:10:24 -0300 Subject: [PATCH] Renderer fixes (Splash + Aspect Ratio) (#2645) * rewrite splash removed Splash class rewrite using imgui texture manager fix crashes & old validation error * handle games with abnormal aspect ratios --- CMakeLists.txt | 2 - src/common/elf_info.h | 7 + src/core/devtools/layer.cpp | 4 +- src/core/file_format/splash.cpp | 38 ---- src/core/file_format/splash.h | 42 ----- src/core/libraries/videoout/driver.cpp | 10 +- src/emulator.cpp | 8 +- src/imgui/renderer/texture_manager.cpp | 1 + .../renderer_vulkan/vk_presenter.cpp | 171 +++++------------- src/video_core/renderer_vulkan/vk_presenter.h | 5 +- 10 files changed, 66 insertions(+), 222 deletions(-) delete mode 100644 src/core/file_format/splash.cpp delete mode 100644 src/core/file_format/splash.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 99620e7d3..f4c23b7c6 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -644,8 +644,6 @@ set(CORE src/core/aerolib/stubs.cpp src/core/file_format/playgo_chunk.h src/core/file_format/trp.cpp src/core/file_format/trp.h - src/core/file_format/splash.h - src/core/file_format/splash.cpp src/core/file_sys/fs.cpp src/core/file_sys/fs.h src/core/loader.cpp diff --git a/src/common/elf_info.h b/src/common/elf_info.h index d885709cd..062cee012 100644 --- a/src/common/elf_info.h +++ b/src/common/elf_info.h @@ -3,6 +3,7 @@ #pragma once +#include #include #include @@ -69,6 +70,8 @@ class ElfInfo { u32 raw_firmware_ver = 0; PSFAttributes psf_attributes{}; + std::filesystem::path splash_path{}; + public: static constexpr u32 FW_15 = 0x1500000; static constexpr u32 FW_16 = 0x1600000; @@ -116,6 +119,10 @@ public: ASSERT(initialized); return psf_attributes; } + + [[nodiscard]] const std::filesystem::path& GetSplashPath() const { + return splash_path; + } }; } // namespace Common diff --git a/src/core/devtools/layer.cpp b/src/core/devtools/layer.cpp index 5f0fd0c95..87fd9ffb3 100644 --- a/src/core/devtools/layer.cpp +++ b/src/core/devtools/layer.cpp @@ -21,8 +21,8 @@ extern std::unique_ptr presenter; using namespace ImGui; -using namespace Core::Devtools; -using L = Core::Devtools::Layer; +using namespace ::Core::Devtools; +using L = ::Core::Devtools::Layer; static bool show_simple_fps = false; static bool visibility_toggled = false; diff --git a/src/core/file_format/splash.cpp b/src/core/file_format/splash.cpp deleted file mode 100644 index 4eb701cf7..000000000 --- a/src/core/file_format/splash.cpp +++ /dev/null @@ -1,38 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include - -#include "common/assert.h" -#include "common/io_file.h" -#include "common/stb.h" -#include "splash.h" - -bool Splash::Open(const std::filesystem::path& filepath) { - ASSERT_MSG(filepath.extension().string() == ".png", "Unexpected file format passed"); - - Common::FS::IOFile file(filepath, Common::FS::FileAccessMode::Read); - if (!file.IsOpen()) { - return false; - } - - std::vector png_file{}; - const auto png_size = file.GetSize(); - png_file.resize(png_size); - file.Seek(0); - file.Read(png_file); - - auto* img_mem = stbi_load_from_memory(png_file.data(), png_file.size(), - reinterpret_cast(&img_info.width), - reinterpret_cast(&img_info.height), - reinterpret_cast(&img_info.num_channels), 4); - if (!img_mem) { - return false; - } - - const auto img_size = img_info.GetSizeBytes(); - img_data.resize(img_size); - std::memcpy(img_data.data(), img_mem, img_size); - stbi_image_free(img_mem); - return true; -} diff --git a/src/core/file_format/splash.h b/src/core/file_format/splash.h deleted file mode 100644 index 7c563f317..000000000 --- a/src/core/file_format/splash.h +++ /dev/null @@ -1,42 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include -#include -#include -#include "common/types.h" - -class Splash { -public: - struct ImageInfo { - u32 width; - u32 height; - u32 num_channels; - - u32 GetSizeBytes() const { - return width * height * 4; // we always forcing rgba8 for simplicity - } - }; - - Splash() = default; - ~Splash() = default; - - bool Open(const std::filesystem::path& filepath); - [[nodiscard]] bool IsLoaded() const { - return img_data.size(); - } - - const auto& GetImageData() const { - return img_data; - } - - ImageInfo GetImageInfo() const { - return img_info; - } - -private: - ImageInfo img_info{}; - std::vector img_data{}; -}; diff --git a/src/core/libraries/videoout/driver.cpp b/src/core/libraries/videoout/driver.cpp index 0f832910c..d2c980882 100644 --- a/src/core/libraries/videoout/driver.cpp +++ b/src/core/libraries/videoout/driver.cpp @@ -160,11 +160,8 @@ int VideoOutDriver::UnregisterBuffers(VideoOutPort* port, s32 attributeIndex) { } void VideoOutDriver::Flip(const Request& req) { - // Whatever the game is rendering show splash if it is active - if (!presenter->ShowSplash(req.frame)) { - // Present the frame. - presenter->Present(req.frame); - } + // Present the frame. + presenter->Present(req.frame); // Update flip status. auto* port = req.port; @@ -201,9 +198,6 @@ void VideoOutDriver::Flip(const Request& req) { } void VideoOutDriver::DrawBlankFrame() { - if (presenter->ShowSplash(nullptr)) { - return; - } const auto empty_frame = presenter->PrepareBlankFrame(false); presenter->Present(empty_frame); } diff --git a/src/emulator.cpp b/src/emulator.cpp index 758720325..b6586ecfd 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -24,7 +24,6 @@ #include "common/singleton.h" #include "common/version.h" #include "core/file_format/psf.h" -#include "core/file_format/splash.h" #include "core/file_format/trp.h" #include "core/file_sys/fs.h" #include "core/libraries/disc_map/disc_map.h" @@ -185,12 +184,7 @@ void Emulator::Run(const std::filesystem::path& file, const std::vectorGetHostPath("/app0/sce_sys/pic1.png"); if (std::filesystem::exists(pic1_path)) { - auto* splash = Common::Singleton::Instance(); - if (!splash->IsLoaded()) { - if (!splash->Open(pic1_path)) { - LOG_ERROR(Loader, "Game splash: unable to open file"); - } - } + game_info.splash_path = pic1_path; } game_info.initialized = true; diff --git a/src/imgui/renderer/texture_manager.cpp b/src/imgui/renderer/texture_manager.cpp index e217cd130..49f912a92 100644 --- a/src/imgui/renderer/texture_manager.cpp +++ b/src/imgui/renderer/texture_manager.cpp @@ -175,6 +175,7 @@ void WorkerLoop() { auto texture = Vulkan::UploadTexture(pixels, vk::Format::eR8G8B8A8Unorm, width, height, width * height * 4 * sizeof(stbi_uc)); + stbi_image_free((void*)pixels); core->upload_data = texture; core->width = width; diff --git a/src/video_core/renderer_vulkan/vk_presenter.cpp b/src/video_core/renderer_vulkan/vk_presenter.cpp index dc7fa8860..4a6a5c7c2 100644 --- a/src/video_core/renderer_vulkan/vk_presenter.cpp +++ b/src/video_core/renderer_vulkan/vk_presenter.cpp @@ -6,7 +6,6 @@ #include "common/singleton.h" #include "core/debug_state.h" #include "core/devtools/layer.h" -#include "core/file_format/splash.h" #include "core/libraries/system/systemservice.h" #include "imgui/renderer/imgui_core.h" #include "sdl_window.h" @@ -21,6 +20,8 @@ #include #include + +#include "common/elf_info.h" #include "imgui/renderer/imgui_impl_vulkan.h" namespace Vulkan { @@ -269,116 +270,10 @@ Frame* Presenter::PrepareLastFrame() { return frame; } -bool Presenter::ShowSplash(Frame* frame /*= nullptr*/) { - const auto* splash = Common::Singleton::Instance(); - if (splash->GetImageData().empty()) { - return false; - } - - if (!Libraries::SystemService::IsSplashVisible()) { - return false; - } - - draw_scheduler.EndRendering(); - const auto cmdbuf = draw_scheduler.CommandBuffer(); - - if (Config::getVkHostMarkersEnabled()) { - cmdbuf.beginDebugUtilsLabelEXT(vk::DebugUtilsLabelEXT{ - .pLabelName = "ShowSplash", - }); - } - - if (!frame) { - if (!splash_img.has_value()) { - VideoCore::ImageInfo info{}; - info.pixel_format = vk::Format::eR8G8B8A8Unorm; - info.type = vk::ImageType::e2D; - info.size = - VideoCore::Extent3D{splash->GetImageInfo().width, splash->GetImageInfo().height, 1}; - info.pitch = splash->GetImageInfo().width; - info.guest_address = VAddr(splash->GetImageData().data()); - info.guest_size = splash->GetImageData().size(); - info.mips_layout.emplace_back(splash->GetImageData().size(), - splash->GetImageInfo().width, - splash->GetImageInfo().height, 0); - splash_img.emplace(instance, present_scheduler, info); - splash_img->flags &= ~VideoCore::GpuDirty; - texture_cache.RefreshImage(*splash_img); - - splash_img->Transit(vk::ImageLayout::eTransferSrcOptimal, - vk::AccessFlagBits2::eTransferRead, {}, cmdbuf); - } - - frame = GetRenderFrame(); - } - - const auto frame_subresources = vk::ImageSubresourceRange{ - .aspectMask = vk::ImageAspectFlagBits::eColor, - .baseMipLevel = 0, - .levelCount = 1, - .baseArrayLayer = 0, - .layerCount = VK_REMAINING_ARRAY_LAYERS, - }; - - const auto pre_barrier = - vk::ImageMemoryBarrier2{.srcStageMask = vk::PipelineStageFlagBits2::eTransfer, - .srcAccessMask = vk::AccessFlagBits2::eTransferRead, - .dstStageMask = vk::PipelineStageFlagBits2::eTransfer, - .dstAccessMask = vk::AccessFlagBits2::eTransferWrite, - .oldLayout = vk::ImageLayout::eUndefined, - .newLayout = vk::ImageLayout::eTransferDstOptimal, - .image = frame->image, - .subresourceRange{frame_subresources}}; - - cmdbuf.pipelineBarrier2(vk::DependencyInfo{ - .imageMemoryBarrierCount = 1, - .pImageMemoryBarriers = &pre_barrier, - }); - - cmdbuf.blitImage(splash_img->image, vk::ImageLayout::eTransferSrcOptimal, frame->image, - vk::ImageLayout::eTransferDstOptimal, - MakeImageBlitFit(splash->GetImageInfo().width, splash->GetImageInfo().height, - frame->width, frame->height), - vk::Filter::eLinear); - - const auto post_barrier = - vk::ImageMemoryBarrier2{.srcStageMask = vk::PipelineStageFlagBits2::eTransfer, - .srcAccessMask = vk::AccessFlagBits2::eTransferWrite, - .dstStageMask = vk::PipelineStageFlagBits2::eColorAttachmentOutput, - .dstAccessMask = vk::AccessFlagBits2::eColorAttachmentWrite, - .oldLayout = vk::ImageLayout::eTransferDstOptimal, - .newLayout = vk::ImageLayout::eGeneral, - .image = frame->image, - .subresourceRange{frame_subresources}}; - - cmdbuf.pipelineBarrier2(vk::DependencyInfo{ - .imageMemoryBarrierCount = 1, - .pImageMemoryBarriers = &post_barrier, - }); - - if (Config::getVkHostMarkersEnabled()) { - cmdbuf.endDebugUtilsLabelEXT(); - } - - // Flush frame creation commands. - frame->ready_semaphore = draw_scheduler.GetMasterSemaphore()->Handle(); - frame->ready_tick = draw_scheduler.CurrentTick(); - SubmitInfo info{}; - draw_scheduler.Flush(info); - - Present(frame); - return true; -} - Frame* Presenter::PrepareFrameInternal(VideoCore::ImageId image_id, bool is_eop) { // Request a free presentation frame. Frame* frame = GetRenderFrame(); - if (frame->width != expected_frame_width || frame->height != expected_frame_height || - frame->is_hdr != swapchain.GetHDR()) { - RecreateFrame(frame, expected_frame_width, expected_frame_height); - } - // EOP flips are triggered from GPU thread so use the drawing scheduler to record // commands. Otherwise we are dealing with a CPU flip which could have arrived // from any guest thread. Use a separate scheduler for that. @@ -420,6 +315,11 @@ Frame* Presenter::PrepareFrameInternal(VideoCore::ImageId image_id, bool is_eop) if (image_id != VideoCore::NULL_IMAGE_ID) { auto& image = texture_cache.GetImage(image_id); vk::Extent2D image_size = {image.info.size.width, image.info.size.height}; + float ratio = (float)image_size.width / (float)image_size.height; + if (ratio != expected_ratio) { + expected_ratio = ratio; + } + image.Transit(vk::ImageLayout::eShaderReadOnlyOptimal, vk::AccessFlagBits2::eShaderRead, {}, cmdbuf); @@ -625,18 +525,43 @@ void Presenter::Present(Frame* frame, bool is_reusing_frame) { ImGui::SetNextWindowDockID(dockId, ImGuiCond_Once); ImGui::Begin("Display##game_display", nullptr, ImGuiWindowFlags_NoNav); + auto game_texture = frame->imgui_texture; + auto game_width = frame->width; + auto game_height = frame->height; + + if (Libraries::SystemService::IsSplashVisible()) { // draw splash + if (!splash_img.has_value()) { + splash_img.emplace(); + auto splash_path = Common::ElfInfo::Instance().GetSplashPath(); + if (!splash_path.empty()) { + splash_img = ImGui::RefCountedTexture::DecodePngFile(splash_path); + } + } + if (auto& splash_image = this->splash_img.value()) { + auto [im_id, width, height] = splash_image.GetTexture(); + game_texture = im_id; + game_width = width; + game_height = height; + } + } + ImVec2 contentArea = ImGui::GetContentRegionAvail(); - const vk::Rect2D imgRect = - FitImage(frame->width, frame->height, (s32)contentArea.x, (s32)contentArea.y); SetExpectedGameSize((s32)contentArea.x, (s32)contentArea.y); - ImGui::SetCursorPos(ImGui::GetCursorStartPos() + ImVec2{ - (float)imgRect.offset.x, - (float)imgRect.offset.y, - }); - ImGui::Image(frame->imgui_texture, { - static_cast(imgRect.extent.width), - static_cast(imgRect.extent.height), - }); + + const auto imgRect = + FitImage(game_width, game_height, (s32)contentArea.x, (s32)contentArea.y); + ImVec2 offset{ + static_cast(imgRect.offset.x), + static_cast(imgRect.offset.y), + }; + ImVec2 size{ + static_cast(imgRect.extent.width), + static_cast(imgRect.extent.height), + }; + + ImGui::SetCursorPos(ImGui::GetCursorStartPos() + offset); + ImGui::Image(game_texture, size); + ImGui::End(); ImGui::PopStyleVar(3); ImGui::PopStyleColor(); @@ -707,19 +632,23 @@ Frame* Presenter::GetRenderFrame() { } } + if (frame->width != expected_frame_width || frame->height != expected_frame_height || + frame->is_hdr != swapchain.GetHDR()) { + RecreateFrame(frame, expected_frame_width, expected_frame_height); + } + return frame; } void Presenter::SetExpectedGameSize(s32 width, s32 height) { - constexpr float expectedRatio = 1920.0 / 1080.0f; const float ratio = (float)width / (float)height; expected_frame_height = height; expected_frame_width = width; - if (ratio > expectedRatio) { - expected_frame_width = static_cast(height * expectedRatio); + if (ratio > expected_ratio) { + expected_frame_width = static_cast(height * expected_ratio); } else { - expected_frame_height = static_cast(width / expectedRatio); + expected_frame_height = static_cast(width / expected_ratio); } } diff --git a/src/video_core/renderer_vulkan/vk_presenter.h b/src/video_core/renderer_vulkan/vk_presenter.h index 892112397..ad2708474 100644 --- a/src/video_core/renderer_vulkan/vk_presenter.h +++ b/src/video_core/renderer_vulkan/vk_presenter.h @@ -6,6 +6,7 @@ #include #include "imgui/imgui_config.h" +#include "imgui/imgui_texture.h" #include "video_core/amdgpu/liverpool.h" #include "video_core/renderer_vulkan/host_passes/fsr_pass.h" #include "video_core/renderer_vulkan/host_passes/pp_pass.h" @@ -92,7 +93,6 @@ public: }) != vo_buffers_addr.cend(); } - bool ShowSplash(Frame* frame = nullptr); void Present(Frame* frame, bool is_reusing_frame = false); void RecreateFrame(Frame* frame, u32 width, u32 height); Frame* PrepareLastFrame(); @@ -125,6 +125,7 @@ private: void SetExpectedGameSize(s32 width, s32 height); private: + float expected_ratio{1920.0 / 1080.0f}; u32 expected_frame_width{1920}; u32 expected_frame_height{1080}; @@ -148,7 +149,7 @@ private: std::mutex free_mutex; std::condition_variable free_cv; std::condition_variable_any frame_cv; - std::optional splash_img; + std::optional splash_img; std::vector vo_buffers_addr; };