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
|
@ -48,4 +48,4 @@ jobs:
|
|||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: shadps4-sdl-appimage
|
||||
path: Shadps4-sdl.AppImage
|
||||
path: Shadps4-sdl.AppImage
|
||||
|
|
11
.reuse/dep5
11
.reuse/dep5
|
@ -8,9 +8,9 @@ Files: CMakeSettings.json
|
|||
documents/readme.txt
|
||||
.github/shadps4.desktop
|
||||
.github/shadps4.png
|
||||
.gitmodules
|
||||
src/images/shadps4.ico
|
||||
screenshots/screenshot.png
|
||||
.gitmodules
|
||||
src/images/shadps4.ico
|
||||
screenshots/screenshot.png
|
||||
src/images/controller_icon.png
|
||||
src/images/exit_icon.png
|
||||
src/images/file_icon.png
|
||||
|
@ -25,7 +25,8 @@ Files: CMakeSettings.json
|
|||
src/images/settings_icon.png
|
||||
src/images/stop_icon.png
|
||||
src/images/themes_icon.png
|
||||
src/shadps4.rc
|
||||
src/shadps4.qrc
|
||||
src/shadps4.rc
|
||||
src/shadps4.qrc
|
||||
externals/stb_image.h
|
||||
Copyright: shadPS4 Emulator Project
|
||||
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/psf.cpp
|
||||
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.h
|
||||
src/core/loader.cpp
|
||||
|
@ -277,8 +279,10 @@ set(CORE src/core/aerolib/stubs.cpp
|
|||
src/core/linker.h
|
||||
src/core/memory.cpp
|
||||
src/core/memory.h
|
||||
src/core/platform.h
|
||||
src/core/memory.h
|
||||
src/core/tls.cpp
|
||||
src/core/tls.h
|
||||
src/core/tls.h
|
||||
src/core/virtual_memory.cpp
|
||||
src/core/virtual_memory.h
|
||||
)
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -17,6 +17,7 @@ std::string logFilter;
|
|||
std::string logType = "sync";
|
||||
bool isDebugDump = false;
|
||||
bool isLibc = true;
|
||||
bool isShowSplash = false;
|
||||
|
||||
bool isLleLibc() {
|
||||
return isLibc;
|
||||
|
@ -49,6 +50,10 @@ bool debugDump() {
|
|||
return isDebugDump;
|
||||
}
|
||||
|
||||
bool showSplash() {
|
||||
return isShowSplash;
|
||||
}
|
||||
|
||||
void load(const std::filesystem::path& path) {
|
||||
// If the configuration file does not exist, create it and return
|
||||
std::error_code error;
|
||||
|
@ -74,6 +79,7 @@ void load(const std::filesystem::path& path) {
|
|||
isNeo = toml::find_or<toml::boolean>(general, "isPS4Pro", false);
|
||||
logFilter = toml::find_or<toml::string>(general, "logFilter", "");
|
||||
logType = toml::find_or<toml::string>(general, "logType", "sync");
|
||||
isShowSplash = toml::find_or<toml::boolean>(general, "showSplash", true);
|
||||
}
|
||||
}
|
||||
if (data.contains("GPU")) {
|
||||
|
@ -125,6 +131,7 @@ void save(const std::filesystem::path& path) {
|
|||
data["General"]["isPS4Pro"] = isNeo;
|
||||
data["General"]["logFilter"] = logFilter;
|
||||
data["General"]["logType"] = logType;
|
||||
data["General"]["showSplash"] = isShowSplash;
|
||||
data["GPU"]["gpuId"] = gpuId;
|
||||
data["GPU"]["screenWidth"] = screenWidth;
|
||||
data["GPU"]["screenHeight"] = screenHeight;
|
||||
|
|
|
@ -20,5 +20,6 @@ s32 getGpuId();
|
|||
|
||||
bool debugDump();
|
||||
bool isLleLibc();
|
||||
bool showSplash();
|
||||
|
||||
}; // 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-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/config.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/libraries/error_codes.h"
|
||||
#include "core/libraries/libs.h"
|
||||
|
@ -8,6 +9,12 @@
|
|||
|
||||
namespace Libraries::SystemService {
|
||||
|
||||
bool g_splash_status{true};
|
||||
|
||||
bool IsSplashVisible() {
|
||||
return Config::showSplash() && g_splash_status;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceAppMessagingClearEventFlag() {
|
||||
LOG_ERROR(Lib_SystemService, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
|
@ -1787,7 +1794,8 @@ int PS4_SYSV_ABI sceSystemServiceGetVersionNumberOfCameraCalibrationData() {
|
|||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceSystemServiceHideSplashScreen() {
|
||||
LOG_WARNING(Lib_SystemService, "called");
|
||||
LOG_INFO(Lib_SystemService, "called");
|
||||
g_splash_status = false;
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -102,6 +102,8 @@ struct OrbisSystemServiceDisplaySafeAreaInfo {
|
|||
uint8_t reserved[128];
|
||||
};
|
||||
|
||||
bool IsSplashVisible();
|
||||
|
||||
int PS4_SYSV_ABI sceAppMessagingClearEventFlag();
|
||||
int PS4_SYSV_ABI sceAppMessagingReceiveMsg();
|
||||
int PS4_SYSV_ABI sceAppMessagingSendMsg();
|
||||
|
|
|
@ -167,6 +167,7 @@ void VideoOutDriver::Flip(std::chrono::microseconds timeout) {
|
|||
std::unique_lock lock{mutex};
|
||||
submit_cond.wait_for(lock, timeout, [&] { return !requests.empty(); });
|
||||
if (requests.empty()) {
|
||||
renderer->ShowSplash();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -175,8 +176,11 @@ void VideoOutDriver::Flip(std::chrono::microseconds timeout) {
|
|||
requests.pop();
|
||||
}
|
||||
|
||||
// Present the frame.
|
||||
renderer->Present(req.frame);
|
||||
// Whatever the game is rendering show splash if it is active
|
||||
if (!renderer->ShowSplash(req.frame)) {
|
||||
// Present the frame.
|
||||
renderer->Present(req.frame);
|
||||
}
|
||||
|
||||
std::scoped_lock lock{mutex};
|
||||
|
||||
|
|
10
src/main.cpp
10
src/main.cpp
|
@ -13,6 +13,7 @@
|
|||
#include "common/logging/log.h"
|
||||
#include "common/path_util.h"
|
||||
#include "common/singleton.h"
|
||||
#include "core/file_format/splash.h"
|
||||
#include "core/file_sys/fs.h"
|
||||
#include "core/libraries/kernel/thread_management.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");
|
||||
std::string app_version = param_sfo->GetString("APP_VER");
|
||||
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,7 +622,9 @@ public:
|
|||
void SubmitDone() {
|
||||
// This is wrong as `submitDone()` should never be blocking. The behavior will be
|
||||
// reworked with mutiple queues introduction
|
||||
cp.get();
|
||||
if (cp.valid()) {
|
||||
cp.get();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#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 "video_core/renderer_vulkan/renderer_vulkan.h"
|
||||
|
||||
|
@ -157,10 +160,44 @@ void RendererVulkan::RecreateFrame(Frame* frame, u32 width, u32 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,
|
||||
VAddr cpu_address) {
|
||||
// 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.
|
||||
Frame* frame = GetRenderFrame();
|
||||
|
|
|
@ -34,10 +34,12 @@ public:
|
|||
Frame* PrepareFrame(const Libraries::VideoOut::BufferAttributeGroup& attribute,
|
||||
VAddr cpu_address);
|
||||
|
||||
bool ShowSplash(Frame* frame = nullptr);
|
||||
void Present(Frame* frame);
|
||||
void RecreateFrame(Frame* frame, u32 width, u32 height);
|
||||
|
||||
private:
|
||||
Frame* PrepareFrameInternal(VideoCore::Image& image);
|
||||
Frame* GetRenderFrame();
|
||||
|
||||
private:
|
||||
|
@ -52,6 +54,7 @@ private:
|
|||
std::mutex free_mutex;
|
||||
std::condition_variable free_cv;
|
||||
std::condition_variable_any frame_cv;
|
||||
std::optional<VideoCore::Image> splash_img;
|
||||
};
|
||||
|
||||
} // namespace Vulkan
|
||||
|
|
|
@ -101,20 +101,19 @@ void TextureCache::OnCpuWrite(VAddr address) {
|
|||
});
|
||||
}
|
||||
|
||||
Image& TextureCache::FindDisplayBuffer(const Libraries::VideoOut::BufferAttributeGroup& group,
|
||||
VAddr cpu_address) {
|
||||
Image& TextureCache::FindImage(const ImageInfo& info, VAddr cpu_address) {
|
||||
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) {
|
||||
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{};
|
||||
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);
|
||||
} else {
|
||||
image_id = image_ids[0];
|
||||
|
|
|
@ -33,8 +33,10 @@ public:
|
|||
void OnCpuWrite(VAddr address);
|
||||
|
||||
/// Retrieves the image handle of the image with the provided attributes and address.
|
||||
Image& FindDisplayBuffer(const Libraries::VideoOut::BufferAttributeGroup& attribute,
|
||||
VAddr cpu_address);
|
||||
Image& FindImage(const ImageInfo& info, VAddr cpu_address);
|
||||
|
||||
/// Reuploads image contents.
|
||||
void RefreshImage(Image& image);
|
||||
|
||||
private:
|
||||
/// Iterate over all page indices in a range
|
||||
|
@ -94,9 +96,6 @@ private:
|
|||
/// Create an image from the given parameters
|
||||
[[nodiscard]] ImageId InsertImage(const ImageInfo& info, VAddr cpu_addr);
|
||||
|
||||
/// Reuploads image contents.
|
||||
void RefreshImage(Image& image);
|
||||
|
||||
/// Register image in the page table
|
||||
void RegisterImage(ImageId image);
|
||||
|
||||
|
|
Loading…
Reference in New Issue