mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-28 22:06:59 +01:00
Add startup splash screen
This commit is contained in:
@@ -54,6 +54,7 @@ struct ConfigOptions {
|
|||||||
int32_t gpu_index = -1;
|
int32_t gpu_index = -1;
|
||||||
bool fullscreen_enabled = false;
|
bool fullscreen_enabled = false;
|
||||||
bool hide_cursor_enabled = false;
|
bool hide_cursor_enabled = false;
|
||||||
|
bool splash_enabled = true;
|
||||||
bool vr_enabled = false;
|
bool vr_enabled = false;
|
||||||
bool amd_cpu_enabled = false;
|
bool amd_cpu_enabled = false;
|
||||||
uint32_t vblank_frequency = 60;
|
uint32_t vblank_frequency = 60;
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
#include "common/subsystems.h"
|
#include "common/subsystems.h"
|
||||||
#include "common/systemInfo.h"
|
#include "common/systemInfo.h"
|
||||||
#include "common/threads.h"
|
#include "common/threads.h"
|
||||||
|
#include "graphics/presentation/systemOverlay.h"
|
||||||
#include "graphics/presentation/window.h"
|
#include "graphics/presentation/window.h"
|
||||||
#include "kernel/fileSystem.h"
|
#include "kernel/fileSystem.h"
|
||||||
#include "kernel/memory.h"
|
#include "kernel/memory.h"
|
||||||
@@ -138,6 +139,9 @@ static void Init(const Config::ConfigOptions& cfg, const std::filesystem::path&
|
|||||||
Libs::LibKernel::Memory::SetFlexibleMemorySize(flexible_memory_size);
|
Libs::LibKernel::Memory::SetFlexibleMemorySize(flexible_memory_size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (cfg.splash_enabled) {
|
||||||
|
Libs::Graphics::InitializeSplashScreen(param_json.parent_path());
|
||||||
|
}
|
||||||
|
|
||||||
// Initialization order is explicit; destruction is automatic and reversed.
|
// Initialization order is explicit; destruction is automatic and reversed.
|
||||||
subsystems.Initialize<Loader::Timer::Lifecycle>();
|
subsystems.Initialize<Loader::Timer::Lifecycle>();
|
||||||
|
|||||||
@@ -0,0 +1,137 @@
|
|||||||
|
#include "graphics/presentation/ddsImage.h"
|
||||||
|
|
||||||
|
#include "common/assert.h"
|
||||||
|
#include "common/file.h"
|
||||||
|
#include "common/logging/log.h"
|
||||||
|
#include "imgui_impl_vulkan.h"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
namespace Libs::Graphics {
|
||||||
|
|
||||||
|
DdsImage::~DdsImage() {
|
||||||
|
if (m_descriptor != VK_NULL_HANDLE) {
|
||||||
|
ImGui_ImplVulkan_RemoveTexture(m_descriptor);
|
||||||
|
}
|
||||||
|
m_graphics.device.destroyImageView(m_view, nullptr);
|
||||||
|
if (m_image.image != nullptr) {
|
||||||
|
m_graphics.DeleteImage(m_image);
|
||||||
|
}
|
||||||
|
if (m_staging != VK_NULL_HANDLE) {
|
||||||
|
vmaDestroyBuffer(m_graphics.allocator, m_staging, m_staging_allocation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DdsImage::Load(const std::filesystem::path& path) {
|
||||||
|
EXIT_IF(m_image.image != nullptr || m_staging != VK_NULL_HANDLE);
|
||||||
|
Common::File file(path, Common::File::Mode::Read);
|
||||||
|
if (file.IsInvalid()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::array<uint32_t, 37> header {};
|
||||||
|
uint32_t read = 0;
|
||||||
|
file.Read(header.data(), sizeof(header), &read);
|
||||||
|
const auto width = header[4];
|
||||||
|
const auto height = header[3];
|
||||||
|
if (read != sizeof(header) || header[0] != 0x20534444 || header[1] != 124 || header[19] != 32 ||
|
||||||
|
header[20] != 4 || header[21] != 0x30315844 || header[32] != 98 || header[33] != 3 ||
|
||||||
|
header[34] != 0 || header[35] != 1 || header[6] != 0 || header[7] != 1 || header[28] != 0 ||
|
||||||
|
width == 0 || height == 0 || width > 3840 || height > 2160) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const uint32_t size = ((width + 3) / 4) * ((height + 3) / 4) * 16;
|
||||||
|
if (file.Size() != sizeof(header) + size) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr auto format = vk::Format::eBc7UnormBlock;
|
||||||
|
constexpr auto features = vk::FormatFeatureFlagBits::eSampledImage |
|
||||||
|
vk::FormatFeatureFlagBits::eSampledImageFilterLinear |
|
||||||
|
vk::FormatFeatureFlagBits::eTransferDst;
|
||||||
|
if ((m_graphics.GetFormatProperties(format).optimalTilingFeatures & features) != features) {
|
||||||
|
LOGF("[Splash] host does not support BC7 textures\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
vk::BufferCreateInfo buffer {};
|
||||||
|
buffer.size = size;
|
||||||
|
buffer.usage = vk::BufferUsageFlagBits::eTransferSrc;
|
||||||
|
VmaAllocationCreateInfo allocation {};
|
||||||
|
allocation.usage = VMA_MEMORY_USAGE_AUTO_PREFER_HOST;
|
||||||
|
allocation.flags =
|
||||||
|
VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT;
|
||||||
|
VmaAllocationInfo mapped {};
|
||||||
|
if (vmaCreateBuffer(m_graphics.allocator, static_cast<const VkBufferCreateInfo*>(buffer),
|
||||||
|
&allocation, &m_staging, &m_staging_allocation, &mapped) != VK_SUCCESS) {
|
||||||
|
LOGF("[Splash] could not allocate DDS upload buffer\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
file.Read(mapped.pMappedData, size, &read);
|
||||||
|
if (read != size) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
RequireVulkanSuccess(static_cast<vk::Result>(vmaFlushAllocation(m_graphics.allocator,
|
||||||
|
m_staging_allocation, 0, size)),
|
||||||
|
"flush splash image upload");
|
||||||
|
|
||||||
|
vk::ImageCreateInfo image {};
|
||||||
|
image.imageType = vk::ImageType::e2D;
|
||||||
|
image.extent = {width, height, 1};
|
||||||
|
image.mipLevels = 1;
|
||||||
|
image.arrayLayers = 1;
|
||||||
|
image.format = format;
|
||||||
|
image.tiling = vk::ImageTiling::eOptimal;
|
||||||
|
image.usage = vk::ImageUsageFlagBits::eTransferDst | vk::ImageUsageFlagBits::eSampled;
|
||||||
|
image.samples = vk::SampleCountFlagBits::e1;
|
||||||
|
if (!m_graphics.CreateImage(image, m_image)) {
|
||||||
|
LOGF("[Splash] could not allocate DDS image\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
vk::ImageViewCreateInfo view {};
|
||||||
|
view.image = m_image.image;
|
||||||
|
view.viewType = vk::ImageViewType::e2D;
|
||||||
|
view.format = format;
|
||||||
|
view.subresourceRange = {vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1};
|
||||||
|
RequireVulkanSuccess(m_graphics.device.createImageView(&view, nullptr, &m_view),
|
||||||
|
"create splash image view");
|
||||||
|
m_descriptor = ImGui_ImplVulkan_AddTexture(static_cast<VkImageView>(m_view),
|
||||||
|
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DdsImage::Upload(vk::CommandBuffer command) {
|
||||||
|
if (m_image.state.layout == vk::ImageLayout::eShaderReadOnlyOptimal) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
vk::ImageMemoryBarrier barrier {};
|
||||||
|
barrier.dstAccessMask = vk::AccessFlagBits::eTransferWrite;
|
||||||
|
barrier.oldLayout = vk::ImageLayout::eUndefined;
|
||||||
|
barrier.newLayout = vk::ImageLayout::eTransferDstOptimal;
|
||||||
|
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||||
|
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||||
|
barrier.image = m_image.image;
|
||||||
|
barrier.subresourceRange = {vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1};
|
||||||
|
command.pipelineBarrier(vk::PipelineStageFlagBits::eTopOfPipe,
|
||||||
|
vk::PipelineStageFlagBits::eTransfer, {}, 0, nullptr, 0, nullptr, 1,
|
||||||
|
&barrier);
|
||||||
|
vk::BufferImageCopy copy {};
|
||||||
|
copy.imageSubresource = {vk::ImageAspectFlagBits::eColor, 0, 0, 1};
|
||||||
|
copy.imageExtent = m_image.extent;
|
||||||
|
command.copyBufferToImage(m_staging, m_image.image, vk::ImageLayout::eTransferDstOptimal, 1,
|
||||||
|
©);
|
||||||
|
barrier.srcAccessMask = vk::AccessFlagBits::eTransferWrite;
|
||||||
|
barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead;
|
||||||
|
barrier.oldLayout = vk::ImageLayout::eTransferDstOptimal;
|
||||||
|
barrier.newLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
|
||||||
|
command.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer,
|
||||||
|
vk::PipelineStageFlagBits::eFragmentShader, {}, 0, nullptr, 0, nullptr,
|
||||||
|
1, &barrier);
|
||||||
|
m_image.state.layout = vk::ImageLayout::eShaderReadOnlyOptimal;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImTextureRef DdsImage::Texture() const {
|
||||||
|
return ImTextureRef {static_cast<ImTextureID>(reinterpret_cast<uintptr_t>(m_descriptor))};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Libs::Graphics
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
#ifndef EMULATOR_SRC_GRAPHICS_PRESENTATION_DDSIMAGE_H_
|
||||||
|
#define EMULATOR_SRC_GRAPHICS_PRESENTATION_DDSIMAGE_H_
|
||||||
|
|
||||||
|
#include "graphics/host_gpu/graphicContext.h"
|
||||||
|
#include "imgui.h"
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
|
namespace Libs::Graphics {
|
||||||
|
|
||||||
|
class DdsImage final {
|
||||||
|
public:
|
||||||
|
explicit DdsImage(GraphicContext& graphics): m_graphics(graphics) {}
|
||||||
|
~DdsImage();
|
||||||
|
KYTY_CLASS_NO_COPY(DdsImage);
|
||||||
|
|
||||||
|
bool Load(const std::filesystem::path& path);
|
||||||
|
void Upload(vk::CommandBuffer command);
|
||||||
|
[[nodiscard]] ImTextureRef Texture() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
GraphicContext& m_graphics;
|
||||||
|
VulkanImage m_image;
|
||||||
|
vk::ImageView m_view = nullptr;
|
||||||
|
VkDescriptorSet m_descriptor = VK_NULL_HANDLE;
|
||||||
|
VkBuffer m_staging = VK_NULL_HANDLE;
|
||||||
|
VmaAllocation m_staging_allocation = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Libs::Graphics
|
||||||
|
|
||||||
|
#endif // EMULATOR_SRC_GRAPHICS_PRESENTATION_DDSIMAGE_H_
|
||||||
@@ -3,9 +3,12 @@
|
|||||||
#include <SDL3/SDL.h>
|
#include <SDL3/SDL.h>
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
|
#include "common/emulatorConfig.h"
|
||||||
|
#include "common/file.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "common/stringUtils.h"
|
#include "common/stringUtils.h"
|
||||||
#include "graphics/host_gpu/graphicContext.h"
|
#include "graphics/host_gpu/graphicContext.h"
|
||||||
|
#include "graphics/presentation/ddsImage.h"
|
||||||
#include "imgui.h"
|
#include "imgui.h"
|
||||||
#include "imgui_internal.h"
|
#include "imgui_internal.h"
|
||||||
#include "imgui_impl_vulkan.h"
|
#include "imgui_impl_vulkan.h"
|
||||||
@@ -13,6 +16,7 @@
|
|||||||
#include "libs/dialog.h"
|
#include "libs/dialog.h"
|
||||||
#include "libs/ime.h"
|
#include "libs/ime.h"
|
||||||
#include "libs/imeDialog.h"
|
#include "libs/imeDialog.h"
|
||||||
|
#include "loader/systemContent.h"
|
||||||
#include "stb_image.h"
|
#include "stb_image.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
@@ -23,6 +27,7 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
|
#include <fmt/format.h>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
@@ -37,6 +42,8 @@ namespace Libs::Graphics {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
using Clock = std::chrono::steady_clock;
|
||||||
|
|
||||||
namespace CoreIme = Libs::Ime;
|
namespace CoreIme = Libs::Ime;
|
||||||
namespace DialogIme = Libs::Dialog::ImeDialog;
|
namespace DialogIme = Libs::Dialog::ImeDialog;
|
||||||
namespace SystemDialog = Libs::Dialog::SystemDialog;
|
namespace SystemDialog = Libs::Dialog::SystemDialog;
|
||||||
@@ -194,6 +201,10 @@ struct TrophyNotification {
|
|||||||
std::mutex g_trophy_notification_mutex;
|
std::mutex g_trophy_notification_mutex;
|
||||||
std::deque<TrophyNotification> g_trophy_notifications;
|
std::deque<TrophyNotification> g_trophy_notifications;
|
||||||
std::atomic<bool> g_trophy_notification_active {false};
|
std::atomic<bool> g_trophy_notification_active {false};
|
||||||
|
std::array<std::filesystem::path, 2> g_splash_paths;
|
||||||
|
std::atomic<bool> g_splash_active {false};
|
||||||
|
std::atomic<Clock::duration::rep> g_splash_hidden_at {0};
|
||||||
|
bool g_splash_fast = false;
|
||||||
|
|
||||||
std::vector<std::string_view> WrapTrophyTitle(std::string_view text, float font_size, float width) {
|
std::vector<std::string_view> WrapTrophyTitle(std::string_view text, float font_size, float width) {
|
||||||
std::vector<std::string_view> lines;
|
std::vector<std::string_view> lines;
|
||||||
@@ -464,6 +475,29 @@ void CheckVulkanResult(VkResult result) {
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
void InitializeSplashScreen(const std::filesystem::path& sce_sys) {
|
||||||
|
const auto language = Config::GetConsoleLanguage();
|
||||||
|
for (size_t i = 0; i < g_splash_paths.size(); ++i) {
|
||||||
|
auto& path = g_splash_paths[i];
|
||||||
|
path = sce_sys / fmt::format("pic{}_{:02}.dds", i + 1, language);
|
||||||
|
if (!Common::File::IsFileExisting(path)) {
|
||||||
|
path = sce_sys / fmt::format("pic{}.dds", i + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int32_t attribute = 0;
|
||||||
|
Loader::SystemContentParamSfoGetInt("ATTRIBUTE3", &attribute);
|
||||||
|
g_splash_fast = (attribute & 0x1000) != 0;
|
||||||
|
g_splash_hidden_at.store(0, std::memory_order_release);
|
||||||
|
g_splash_active.store(Common::File::IsFileExisting(g_splash_paths[0]) &&
|
||||||
|
Common::File::IsFileExisting(g_splash_paths[1]),
|
||||||
|
std::memory_order_release);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HideSplashScreen() {
|
||||||
|
Clock::duration::rep expected = 0;
|
||||||
|
g_splash_hidden_at.compare_exchange_strong(expected, Clock::now().time_since_epoch().count());
|
||||||
|
}
|
||||||
|
|
||||||
void InitializeSystemOverlayInput(SDL_Window* window) {
|
void InitializeSystemOverlayInput(SDL_Window* window) {
|
||||||
EXIT_IF(window == nullptr);
|
EXIT_IF(window == nullptr);
|
||||||
const Uint32 type = SDL_RegisterEvents(1);
|
const Uint32 type = SDL_RegisterEvents(1);
|
||||||
@@ -481,6 +515,7 @@ void InitializeSystemOverlayInput(SDL_Window* window) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ShutdownSystemOverlayInput() {
|
void ShutdownSystemOverlayInput() {
|
||||||
|
g_splash_active.store(false, std::memory_order_release);
|
||||||
CoreIme::SetVisibilityCallback(nullptr);
|
CoreIme::SetVisibilityCallback(nullptr);
|
||||||
DialogIme::SetVisibilityCallback(nullptr);
|
DialogIme::SetVisibilityCallback(nullptr);
|
||||||
SystemDialog::SetVisibilityCallback(nullptr);
|
SystemDialog::SetVisibilityCallback(nullptr);
|
||||||
@@ -515,8 +550,10 @@ SystemOverlayVisualState GetSystemOverlayVisualState() noexcept {
|
|||||||
const auto dialog = DialogIme::GetVisualState();
|
const auto dialog = DialogIme::GetVisualState();
|
||||||
const auto system = SystemDialog::GetVisualState();
|
const auto system = SystemDialog::GetVisualState();
|
||||||
const bool trophy_active = g_trophy_notification_active.load(std::memory_order_acquire);
|
const bool trophy_active = g_trophy_notification_active.load(std::memory_order_acquire);
|
||||||
return {core.active || dialog.active || system.active || trophy_active,
|
const bool splash = g_splash_active.load(std::memory_order_acquire);
|
||||||
core.revision + dialog.revision + system.revision + (trophy_active ? 1u : 0u)};
|
return {core.active || dialog.active || system.active || trophy_active || splash,
|
||||||
|
core.revision + dialog.revision + system.revision + (trophy_active ? 1u : 0u) +
|
||||||
|
(splash ? 2u : 0u)};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ProcessSystemOverlayInput(const SDL_Event& event) {
|
bool ProcessSystemOverlayInput(const SDL_Event& event) {
|
||||||
@@ -656,8 +693,6 @@ void NotifyTrophyUnlocked(std::string_view name, int32_t grade,
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct SystemOverlay::Impl {
|
struct SystemOverlay::Impl {
|
||||||
using Clock = std::chrono::steady_clock;
|
|
||||||
|
|
||||||
explicit Impl(GraphicContext& context): graphics(context) {}
|
explicit Impl(GraphicContext& context): graphics(context) {}
|
||||||
|
|
||||||
~Impl() {
|
~Impl() {
|
||||||
@@ -770,6 +805,65 @@ struct SystemOverlay::Impl {
|
|||||||
return texture;
|
return texture;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FinishSplash() {
|
||||||
|
g_splash_active.store(false, std::memory_order_release);
|
||||||
|
if (splash_dds[0] || splash_dds[1]) {
|
||||||
|
Common::LockGuard lock(graphics.queue_mutex);
|
||||||
|
RequireVulkanSuccess(graphics.queue.waitIdle(), "retire splash images");
|
||||||
|
splash_dds = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrawSplash(vk::Extent2D frame_extent) {
|
||||||
|
if (!g_splash_active.load(std::memory_order_acquire)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (size_t i = 0; i < g_splash_paths.size(); ++i) {
|
||||||
|
if (splash_dds[i]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const auto& path = g_splash_paths[i];
|
||||||
|
auto image = std::make_unique<DdsImage>(graphics);
|
||||||
|
if (!image->Load(path)) {
|
||||||
|
LOGF("[Splash] could not load %s\n", path.string().c_str());
|
||||||
|
FinishSplash();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
splash_dds[i] = std::move(image);
|
||||||
|
}
|
||||||
|
const auto now = Clock::now();
|
||||||
|
if (splash_born == Clock::time_point {}) {
|
||||||
|
splash_born = now;
|
||||||
|
}
|
||||||
|
const auto hidden = g_splash_hidden_at.load(std::memory_order_acquire);
|
||||||
|
const float fade =
|
||||||
|
hidden == 0
|
||||||
|
? 0.0f
|
||||||
|
: std::chrono::duration<float>(now.time_since_epoch() - Clock::duration {hidden})
|
||||||
|
.count() /
|
||||||
|
0.3f;
|
||||||
|
const float alpha = 1.0f - std::clamp(fade, 0.0f, 1.0f);
|
||||||
|
if (alpha == 0.0f) {
|
||||||
|
FinishSplash();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const float age = std::chrono::duration<float>(now - splash_born).count();
|
||||||
|
const float logo = g_splash_fast ? 1.0f : std::clamp((age - 1.4f) / 2.2f, 0.0f, 1.0f);
|
||||||
|
const ImVec2 screen {static_cast<float>(frame_extent.width),
|
||||||
|
static_cast<float>(frame_extent.height)};
|
||||||
|
const float fit = std::min(screen.x / 3840.0f, screen.y / 2160.0f);
|
||||||
|
const ImVec2 size {3840.0f * fit, 2160.0f * fit};
|
||||||
|
const ImVec2 begin {(screen.x - size.x) * 0.5f, (screen.y - size.y) * 0.5f};
|
||||||
|
const ImVec2 end {begin.x + size.x, begin.y + size.y};
|
||||||
|
auto* draw = ImGui::GetBackgroundDrawList();
|
||||||
|
draw->AddRectFilled({0, 0}, screen, IM_COL32(0, 0, 0, static_cast<int>(255.0f * alpha)));
|
||||||
|
for (size_t i = 0; i < g_splash_paths.size(); ++i) {
|
||||||
|
draw->AddImage(
|
||||||
|
splash_dds[i]->Texture(), begin, end, {0, 0}, {1, 1},
|
||||||
|
IM_COL32(255, 255, 255, static_cast<int>(255.0f * alpha * (i == 0 ? 1.0f : logo))));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void EnsureVulkan(vk::Format format, uint32_t image_count) {
|
void EnsureVulkan(vk::Format format, uint32_t image_count) {
|
||||||
EnsureContext();
|
EnsureContext();
|
||||||
if (vulkan_initialized) {
|
if (vulkan_initialized) {
|
||||||
@@ -1215,7 +1309,8 @@ struct SystemOverlay::Impl {
|
|||||||
OverlaySnapshot snapshot;
|
OverlaySnapshot snapshot;
|
||||||
const bool has_overlay = GetOverlaySnapshot(&snapshot);
|
const bool has_overlay = GetOverlaySnapshot(&snapshot);
|
||||||
const auto now = Clock::now();
|
const auto now = Clock::now();
|
||||||
if (!has_overlay && !g_trophy_notification_active.load(std::memory_order_acquire)) {
|
if (!has_overlay && !g_trophy_notification_active.load(std::memory_order_acquire) &&
|
||||||
|
!g_splash_active.load(std::memory_order_acquire)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const auto prepared_session = snapshot.session;
|
const auto prepared_session = snapshot.session;
|
||||||
@@ -1245,12 +1340,9 @@ struct SystemOverlay::Impl {
|
|||||||
last_frame = now;
|
last_frame = now;
|
||||||
ImGui_ImplVulkan_NewFrame();
|
ImGui_ImplVulkan_NewFrame();
|
||||||
ImGui::NewFrame();
|
ImGui::NewFrame();
|
||||||
|
DrawSplash(frame_extent);
|
||||||
GetOverlaySnapshot(&snapshot);
|
GetOverlaySnapshot(&snapshot);
|
||||||
if (snapshot.session != prepared_session) {
|
if (has_overlay && snapshot.session == prepared_session) {
|
||||||
ImGui::EndFrame();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (has_overlay) {
|
|
||||||
if (snapshot.session.kind == OverlayKind::Dialog) {
|
if (snapshot.session.kind == OverlayKind::Dialog) {
|
||||||
DrawDialog(snapshot.dialog, frame_extent);
|
DrawDialog(snapshot.dialog, frame_extent);
|
||||||
} else {
|
} else {
|
||||||
@@ -1264,6 +1356,11 @@ struct SystemOverlay::Impl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Record(vk::CommandBuffer command, vk::ImageView target) {
|
void Record(vk::CommandBuffer command, vk::ImageView target) {
|
||||||
|
for (const auto& image: splash_dds) {
|
||||||
|
if (image) {
|
||||||
|
image->Upload(command);
|
||||||
|
}
|
||||||
|
}
|
||||||
vk::RenderingAttachmentInfo color {};
|
vk::RenderingAttachmentInfo color {};
|
||||||
color.sType = vk::StructureType::eRenderingAttachmentInfo;
|
color.sType = vk::StructureType::eRenderingAttachmentInfo;
|
||||||
color.imageView = target;
|
color.imageView = target;
|
||||||
@@ -1290,6 +1387,7 @@ struct SystemOverlay::Impl {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ImGui::SetCurrentContext(imgui_context);
|
ImGui::SetCurrentContext(imgui_context);
|
||||||
|
splash_dds = {};
|
||||||
ImGui_ImplVulkan_Shutdown();
|
ImGui_ImplVulkan_Shutdown();
|
||||||
vulkan_initialized = false;
|
vulkan_initialized = false;
|
||||||
}
|
}
|
||||||
@@ -1309,6 +1407,8 @@ struct SystemOverlay::Impl {
|
|||||||
std::optional<TrophyNotification> trophy_notification;
|
std::optional<TrophyNotification> trophy_notification;
|
||||||
Clock::time_point trophy_notification_born;
|
Clock::time_point trophy_notification_born;
|
||||||
std::vector<std::unique_ptr<ImTextureData>> retired_trophy_images;
|
std::vector<std::unique_ptr<ImTextureData>> retired_trophy_images;
|
||||||
|
std::array<std::unique_ptr<DdsImage>, 2> splash_dds;
|
||||||
|
Clock::time_point splash_born;
|
||||||
SDL_AudioStream* trophy_sound_stream = nullptr;
|
SDL_AudioStream* trophy_sound_stream = nullptr;
|
||||||
uint8_t* trophy_sound_data = nullptr;
|
uint8_t* trophy_sound_data = nullptr;
|
||||||
uint32_t trophy_sound_size = 0;
|
uint32_t trophy_sound_size = 0;
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include "graphics/host_gpu/vulkanCommon.h"
|
#include "graphics/host_gpu/vulkanCommon.h"
|
||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
#include <filesystem>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <span>
|
#include <span>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
@@ -21,6 +22,8 @@ struct SystemOverlayVisualState {
|
|||||||
uint64_t revision;
|
uint64_t revision;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void InitializeSplashScreen(const std::filesystem::path& sce_sys);
|
||||||
|
void HideSplashScreen();
|
||||||
void InitializeSystemOverlayInput(SDL_Window* window);
|
void InitializeSystemOverlayInput(SDL_Window* window);
|
||||||
void ShutdownSystemOverlayInput();
|
void ShutdownSystemOverlayInput();
|
||||||
bool ProcessSystemOverlayInput(const SDL_Event& event);
|
bool ProcessSystemOverlayInput(const SDL_Event& event);
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include "common/emulatorConfig.h"
|
#include "common/emulatorConfig.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "common/stringUtils.h"
|
#include "common/stringUtils.h"
|
||||||
|
#include "graphics/presentation/systemOverlay.h"
|
||||||
#include "libs/dialog.h"
|
#include "libs/dialog.h"
|
||||||
#include "libs/errno.h"
|
#include "libs/errno.h"
|
||||||
#include "libs/libs.h"
|
#include "libs/libs.h"
|
||||||
@@ -94,7 +95,7 @@ struct SystemServiceHdrToneMapLuminance {
|
|||||||
|
|
||||||
static int KYTY_SYSV_ABI SystemServiceHideSplashScreen() {
|
static int KYTY_SYSV_ABI SystemServiceHideSplashScreen() {
|
||||||
PRINT_NAME();
|
PRINT_NAME();
|
||||||
|
Graphics::HideSplashScreen();
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ static void PrintUsage() {
|
|||||||
::printf(" --fullscreen Run in borderless desktop fullscreen.\n");
|
::printf(" --fullscreen Run in borderless desktop fullscreen.\n");
|
||||||
::printf(
|
::printf(
|
||||||
" --hide-cursor Hide the cursor after 2 s idle. Default: off.\n");
|
" --hide-cursor Hide the cursor after 2 s idle. Default: off.\n");
|
||||||
|
::printf(" --no-splash Disable the startup splash screen.\n");
|
||||||
::printf(" --vr Enable the virtual VR headset.\n");
|
::printf(" --vr Enable the virtual VR headset.\n");
|
||||||
::printf(" --amd-cpu Apply AMD CPU instruction patches.\n");
|
::printf(" --amd-cpu Apply AMD CPU instruction patches.\n");
|
||||||
::printf(" --vblank-frequency <num> Virtual vblank frequency. Default: 60.\n");
|
::printf(" --vblank-frequency <num> Virtual vblank frequency. Default: 60.\n");
|
||||||
@@ -219,6 +220,11 @@ static bool ParseArgs(int argc, char* argv[], RunOptions& options, bool& show_he
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (arg == "--no-splash") {
|
||||||
|
options.config.splash_enabled = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (arg == "--vr") {
|
if (arg == "--vr") {
|
||||||
options.config.vr_enabled = true;
|
options.config.vr_enabled = true;
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
Reference in New Issue
Block a user