Merge pull request #135 from shadps4-emu/video_core/splash
Show title splash while the game is loading
This commit is contained in:
commit
f881753944
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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;
|
||||||
|
|
|
@ -20,5 +20,6 @@ s32 getGpuId();
|
||||||
|
|
||||||
bool debugDump();
|
bool debugDump();
|
||||||
bool isLleLibc();
|
bool isLleLibc();
|
||||||
|
bool showSplash();
|
||||||
|
|
||||||
}; // namespace Config
|
}; // namespace Config
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
|
@ -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{};
|
||||||
|
};
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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};
|
||||||
|
|
||||||
|
|
10
src/main.cpp
10
src/main.cpp
|
@ -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");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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];
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue