Merge pull request #135 from shadps4-emu/video_core/splash

Show title splash while the game is loading
This commit is contained in:
georgemoralis 2024-05-16 17:13:56 +03:00 committed by GitHub
commit f881753944
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 8167 additions and 22 deletions

View File

@ -27,5 +27,6 @@ Files: CMakeSettings.json
src/images/themes_icon.png src/images/themes_icon.png
src/shadps4.rc src/shadps4.rc
src/shadps4.qrc src/shadps4.qrc
externals/stb_image.h
Copyright: shadPS4 Emulator Project Copyright: shadPS4 Emulator Project
License: GPL-2.0-or-later License: GPL-2.0-or-later

View File

@ -252,6 +252,8 @@ set(CORE src/core/aerolib/stubs.cpp
src/core/file_format/pkg_type.h src/core/file_format/pkg_type.h
src/core/file_format/psf.cpp src/core/file_format/psf.cpp
src/core/file_format/psf.h src/core/file_format/psf.h
src/core/file_format/splash.h
src/core/file_format/splash.cpp
src/core/file_sys/fs.cpp src/core/file_sys/fs.cpp
src/core/file_sys/fs.h src/core/file_sys/fs.h
src/core/loader.cpp src/core/loader.cpp
@ -277,6 +279,8 @@ set(CORE src/core/aerolib/stubs.cpp
src/core/linker.h src/core/linker.h
src/core/memory.cpp src/core/memory.cpp
src/core/memory.h src/core/memory.h
src/core/platform.h
src/core/memory.h
src/core/tls.cpp src/core/tls.cpp
src/core/tls.h src/core/tls.h
src/core/virtual_memory.cpp src/core/virtual_memory.cpp

7985
externals/stb_image.h vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -17,6 +17,7 @@ std::string logFilter;
std::string logType = "sync"; std::string logType = "sync";
bool isDebugDump = false; bool isDebugDump = false;
bool isLibc = true; bool isLibc = true;
bool isShowSplash = false;
bool isLleLibc() { bool isLleLibc() {
return isLibc; return isLibc;
@ -49,6 +50,10 @@ bool debugDump() {
return isDebugDump; return isDebugDump;
} }
bool showSplash() {
return isShowSplash;
}
void load(const std::filesystem::path& path) { void load(const std::filesystem::path& path) {
// If the configuration file does not exist, create it and return // If the configuration file does not exist, create it and return
std::error_code error; std::error_code error;
@ -74,6 +79,7 @@ void load(const std::filesystem::path& path) {
isNeo = toml::find_or<toml::boolean>(general, "isPS4Pro", false); isNeo = toml::find_or<toml::boolean>(general, "isPS4Pro", false);
logFilter = toml::find_or<toml::string>(general, "logFilter", ""); logFilter = toml::find_or<toml::string>(general, "logFilter", "");
logType = toml::find_or<toml::string>(general, "logType", "sync"); logType = toml::find_or<toml::string>(general, "logType", "sync");
isShowSplash = toml::find_or<toml::boolean>(general, "showSplash", true);
} }
} }
if (data.contains("GPU")) { if (data.contains("GPU")) {
@ -125,6 +131,7 @@ void save(const std::filesystem::path& path) {
data["General"]["isPS4Pro"] = isNeo; data["General"]["isPS4Pro"] = isNeo;
data["General"]["logFilter"] = logFilter; data["General"]["logFilter"] = logFilter;
data["General"]["logType"] = logType; data["General"]["logType"] = logType;
data["General"]["showSplash"] = isShowSplash;
data["GPU"]["gpuId"] = gpuId; data["GPU"]["gpuId"] = gpuId;
data["GPU"]["screenWidth"] = screenWidth; data["GPU"]["screenWidth"] = screenWidth;
data["GPU"]["screenHeight"] = screenHeight; data["GPU"]["screenHeight"] = screenHeight;

View File

@ -20,5 +20,6 @@ s32 getGpuId();
bool debugDump(); bool debugDump();
bool isLleLibc(); bool isLleLibc();
bool showSplash();
}; // namespace Config }; // namespace Config

View File

@ -0,0 +1,42 @@
// 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 "splash.h"
#define STB_IMAGE_IMPLEMENTATION
#define STBI_ONLY_PNG
#define STBI_NO_STDIO
#include "externals/stb_image.h"
bool Splash::Open(const std::string& filepath) {
ASSERT_MSG(filepath.ends_with(".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;
}

View File

@ -0,0 +1,41 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#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::string& 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{};
};

View File

@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include "common/config.h"
#include "common/logging/log.h" #include "common/logging/log.h"
#include "core/libraries/error_codes.h" #include "core/libraries/error_codes.h"
#include "core/libraries/libs.h" #include "core/libraries/libs.h"
@ -8,6 +9,12 @@
namespace Libraries::SystemService { namespace Libraries::SystemService {
bool g_splash_status{true};
bool IsSplashVisible() {
return Config::showSplash() && g_splash_status;
}
int PS4_SYSV_ABI sceAppMessagingClearEventFlag() { int PS4_SYSV_ABI sceAppMessagingClearEventFlag() {
LOG_ERROR(Lib_SystemService, "(STUBBED) called"); LOG_ERROR(Lib_SystemService, "(STUBBED) called");
return ORBIS_OK; return ORBIS_OK;
@ -1787,7 +1794,8 @@ int PS4_SYSV_ABI sceSystemServiceGetVersionNumberOfCameraCalibrationData() {
} }
s32 PS4_SYSV_ABI sceSystemServiceHideSplashScreen() { s32 PS4_SYSV_ABI sceSystemServiceHideSplashScreen() {
LOG_WARNING(Lib_SystemService, "called"); LOG_INFO(Lib_SystemService, "called");
g_splash_status = false;
return ORBIS_OK; return ORBIS_OK;
} }

View File

@ -102,6 +102,8 @@ struct OrbisSystemServiceDisplaySafeAreaInfo {
uint8_t reserved[128]; uint8_t reserved[128];
}; };
bool IsSplashVisible();
int PS4_SYSV_ABI sceAppMessagingClearEventFlag(); int PS4_SYSV_ABI sceAppMessagingClearEventFlag();
int PS4_SYSV_ABI sceAppMessagingReceiveMsg(); int PS4_SYSV_ABI sceAppMessagingReceiveMsg();
int PS4_SYSV_ABI sceAppMessagingSendMsg(); int PS4_SYSV_ABI sceAppMessagingSendMsg();

View File

@ -167,6 +167,7 @@ void VideoOutDriver::Flip(std::chrono::microseconds timeout) {
std::unique_lock lock{mutex}; std::unique_lock lock{mutex};
submit_cond.wait_for(lock, timeout, [&] { return !requests.empty(); }); submit_cond.wait_for(lock, timeout, [&] { return !requests.empty(); });
if (requests.empty()) { if (requests.empty()) {
renderer->ShowSplash();
return; return;
} }
@ -175,8 +176,11 @@ void VideoOutDriver::Flip(std::chrono::microseconds timeout) {
requests.pop(); requests.pop();
} }
// Whatever the game is rendering show splash if it is active
if (!renderer->ShowSplash(req.frame)) {
// Present the frame. // Present the frame.
renderer->Present(req.frame); renderer->Present(req.frame);
}
std::scoped_lock lock{mutex}; std::scoped_lock lock{mutex};

View File

@ -13,6 +13,7 @@
#include "common/logging/log.h" #include "common/logging/log.h"
#include "common/path_util.h" #include "common/path_util.h"
#include "common/singleton.h" #include "common/singleton.h"
#include "core/file_format/splash.h"
#include "core/file_sys/fs.h" #include "core/file_sys/fs.h"
#include "core/libraries/kernel/thread_management.h" #include "core/libraries/kernel/thread_management.h"
#include "core/libraries/libc/libc.h" #include "core/libraries/libc/libc.h"
@ -62,6 +63,15 @@ int main(int argc, char* argv[]) {
u32 fw_version = param_sfo->GetInteger("SYSTEM_VER"); u32 fw_version = param_sfo->GetInteger("SYSTEM_VER");
std::string app_version = param_sfo->GetString("APP_VER"); std::string app_version = param_sfo->GetString("APP_VER");
LOG_INFO(Loader, "Fw: {:#x} App Version: {}", fw_version, app_version); LOG_INFO(Loader, "Fw: {:#x} App Version: {}", fw_version, app_version);
} else if (entry.path().filename() == "pic0.png" ||
entry.path().filename() == "pic1.png") {
auto* splash = Common::Singleton<Splash>::Instance();
if (splash->IsLoaded()) {
continue;
}
if (!splash->Open(entry.path().string())) {
LOG_ERROR(Loader, "Game splash: unable to open file");
}
} }
} }
} }

View File

@ -622,8 +622,10 @@ public:
void SubmitDone() { void SubmitDone() {
// This is wrong as `submitDone()` should never be blocking. The behavior will be // This is wrong as `submitDone()` should never be blocking. The behavior will be
// reworked with mutiple queues introduction // reworked with mutiple queues introduction
if (cp.valid()) {
cp.get(); cp.get();
} }
}
private: private:
void ProcessCmdList(u32* cmdbuf, u32 size_in_bytes); void ProcessCmdList(u32* cmdbuf, u32 size_in_bytes);

View File

@ -2,6 +2,9 @@
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include "common/config.h" #include "common/config.h"
#include "common/singleton.h"
#include "core/file_format/splash.h"
#include "core/libraries/system/systemservice.h"
#include "sdl_window.h" #include "sdl_window.h"
#include "video_core/renderer_vulkan/renderer_vulkan.h" #include "video_core/renderer_vulkan/renderer_vulkan.h"
@ -157,10 +160,44 @@ void RendererVulkan::RecreateFrame(Frame* frame, u32 width, u32 height) {
frame->height = height; frame->height = height;
} }
bool RendererVulkan::ShowSplash(Frame* frame /*= nullptr*/) {
const auto* splash = Common::Singleton<Splash>::Instance();
if (splash->GetImageData().empty()) {
return false;
}
if (!Libraries::SystemService::IsSplashVisible()) {
return false;
}
if (!frame) {
if (!splash_img.has_value()) {
VideoCore::ImageInfo info{};
info.pixel_format = vk::Format::eR8G8B8A8Srgb;
info.type = vk::ImageType::e2D;
info.size =
VideoCore::Extent3D{splash->GetImageInfo().width, splash->GetImageInfo().height, 1};
info.pitch = splash->GetImageInfo().width * 4;
info.guest_size_bytes = splash->GetImageData().size();
splash_img.emplace(instance, scheduler, info, VAddr(splash->GetImageData().data()));
texture_cache.RefreshImage(*splash_img);
}
frame = PrepareFrameInternal(*splash_img);
}
Present(frame);
return true;
}
Frame* RendererVulkan::PrepareFrame(const Libraries::VideoOut::BufferAttributeGroup& attribute, Frame* RendererVulkan::PrepareFrame(const Libraries::VideoOut::BufferAttributeGroup& attribute,
VAddr cpu_address) { VAddr cpu_address) {
// Request presentation image from the texture cache. // Request presentation image from the texture cache.
auto& image = texture_cache.FindDisplayBuffer(attribute, cpu_address); const auto info = VideoCore::ImageInfo{attribute};
auto& image = texture_cache.FindImage(info, cpu_address);
return PrepareFrameInternal(image);
}
Frame* RendererVulkan::PrepareFrameInternal(VideoCore::Image& image) {
// Request a free presentation frame. // Request a free presentation frame.
Frame* frame = GetRenderFrame(); Frame* frame = GetRenderFrame();

View File

@ -34,10 +34,12 @@ public:
Frame* PrepareFrame(const Libraries::VideoOut::BufferAttributeGroup& attribute, Frame* PrepareFrame(const Libraries::VideoOut::BufferAttributeGroup& attribute,
VAddr cpu_address); VAddr cpu_address);
bool ShowSplash(Frame* frame = nullptr);
void Present(Frame* frame); void Present(Frame* frame);
void RecreateFrame(Frame* frame, u32 width, u32 height); void RecreateFrame(Frame* frame, u32 width, u32 height);
private: private:
Frame* PrepareFrameInternal(VideoCore::Image& image);
Frame* GetRenderFrame(); Frame* GetRenderFrame();
private: private:
@ -52,6 +54,7 @@ private:
std::mutex free_mutex; std::mutex free_mutex;
std::condition_variable free_cv; std::condition_variable free_cv;
std::condition_variable_any frame_cv; std::condition_variable_any frame_cv;
std::optional<VideoCore::Image> splash_img;
}; };
} // namespace Vulkan } // namespace Vulkan

View File

@ -101,20 +101,19 @@ void TextureCache::OnCpuWrite(VAddr address) {
}); });
} }
Image& TextureCache::FindDisplayBuffer(const Libraries::VideoOut::BufferAttributeGroup& group, Image& TextureCache::FindImage(const ImageInfo& info, VAddr cpu_address) {
VAddr cpu_address) {
boost::container::small_vector<ImageId, 2> image_ids; boost::container::small_vector<ImageId, 2> image_ids;
ForEachImageInRegion(cpu_address, group.size_in_bytes, [&](ImageId image_id, Image& image) { ForEachImageInRegion(cpu_address, info.guest_size_bytes, [&](ImageId image_id, Image& image) {
if (image.cpu_addr == cpu_address) { if (image.cpu_addr == cpu_address) {
image_ids.push_back(image_id); image_ids.push_back(image_id);
} }
}); });
ASSERT_MSG(image_ids.size() <= 1, "Overlapping framebuffers not allowed!"); ASSERT_MSG(image_ids.size() <= 1, "Overlapping images not allowed!");
ImageId image_id{}; ImageId image_id{};
if (image_ids.empty()) { if (image_ids.empty()) {
image_id = slot_images.insert(instance, scheduler, ImageInfo{group}, cpu_address); image_id = slot_images.insert(instance, scheduler, info, cpu_address);
RegisterImage(image_id); RegisterImage(image_id);
} else { } else {
image_id = image_ids[0]; image_id = image_ids[0];

View File

@ -33,8 +33,10 @@ public:
void OnCpuWrite(VAddr address); void OnCpuWrite(VAddr address);
/// Retrieves the image handle of the image with the provided attributes and address. /// Retrieves the image handle of the image with the provided attributes and address.
Image& FindDisplayBuffer(const Libraries::VideoOut::BufferAttributeGroup& attribute, Image& FindImage(const ImageInfo& info, VAddr cpu_address);
VAddr cpu_address);
/// Reuploads image contents.
void RefreshImage(Image& image);
private: private:
/// Iterate over all page indices in a range /// Iterate over all page indices in a range
@ -94,9 +96,6 @@ private:
/// Create an image from the given parameters /// Create an image from the given parameters
[[nodiscard]] ImageId InsertImage(const ImageInfo& info, VAddr cpu_addr); [[nodiscard]] ImageId InsertImage(const ImageInfo& info, VAddr cpu_addr);
/// Reuploads image contents.
void RefreshImage(Image& image);
/// Register image in the page table /// Register image in the page table
void RegisterImage(ImageId image); void RegisterImage(ImageId image);