mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-04-20 03:24:49 +00:00
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
This commit is contained in:
parent
36927a7bbd
commit
5691046dcc
10 changed files with 66 additions and 222 deletions
|
@ -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
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
|
@ -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
|
||||
|
|
|
@ -21,8 +21,8 @@
|
|||
extern std::unique_ptr<Vulkan::Presenter> 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;
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <fstream>
|
||||
|
||||
#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<u8> 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<int*>(&img_info.width),
|
||||
reinterpret_cast<int*>(&img_info.height),
|
||||
reinterpret_cast<int*>(&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;
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#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<u8> img_data{};
|
||||
};
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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::vector<std::str
|
|||
|
||||
const auto pic1_path = mnt->GetHostPath("/app0/sce_sys/pic1.png");
|
||||
if (std::filesystem::exists(pic1_path)) {
|
||||
auto* splash = Common::Singleton<Splash>::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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 <vk_mem_alloc.h>
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
#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<Splash>::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<float>(imgRect.extent.width),
|
||||
static_cast<float>(imgRect.extent.height),
|
||||
});
|
||||
|
||||
const auto imgRect =
|
||||
FitImage(game_width, game_height, (s32)contentArea.x, (s32)contentArea.y);
|
||||
ImVec2 offset{
|
||||
static_cast<float>(imgRect.offset.x),
|
||||
static_cast<float>(imgRect.offset.y),
|
||||
};
|
||||
ImVec2 size{
|
||||
static_cast<float>(imgRect.extent.width),
|
||||
static_cast<float>(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<s32>(height * expectedRatio);
|
||||
if (ratio > expected_ratio) {
|
||||
expected_frame_width = static_cast<s32>(height * expected_ratio);
|
||||
} else {
|
||||
expected_frame_height = static_cast<s32>(width / expectedRatio);
|
||||
expected_frame_height = static_cast<s32>(width / expected_ratio);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include <condition_variable>
|
||||
|
||||
#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<VideoCore::Image> splash_img;
|
||||
std::optional<ImGui::RefCountedTexture> splash_img;
|
||||
std::vector<VAddr> vo_buffers_addr;
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue