mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-12-22 21:51:52 +01:00
Show pipelines loading screen while GPU is loading them
This commit is contained in:
parent
3bb24a52ab
commit
3cae2e05ae
@ -414,6 +414,6 @@ namespace skyline::gpu {
|
||||
if (!*state.settings->disableShaderCache)
|
||||
graphicsPipelineCacheManager.emplace(state,
|
||||
state.os->publicAppFilesPath + "graphics_pipeline_cache/" + titleId);
|
||||
graphicsPipelineManager.emplace(*this);
|
||||
graphicsPipelineManager.emplace(*this, *state.jvm);
|
||||
}
|
||||
}
|
||||
|
@ -214,6 +214,9 @@ namespace skyline::gpu {
|
||||
|
||||
std::scoped_lock lock{mutex};
|
||||
compilePendingDescs.erase(pipelineDescIt);
|
||||
if (compilationCallback)
|
||||
compilationCallback();
|
||||
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
@ -252,4 +255,15 @@ namespace skyline::gpu {
|
||||
SerialisePipelineCache(gpu, pipelineCacheDir, rawData);
|
||||
});
|
||||
}
|
||||
|
||||
void GraphicsPipelineAssembler::RegisterCompilationCallback(std::function<void()> callback) {
|
||||
if (compilationCallback)
|
||||
throw exception("A compilation callback is already registered");
|
||||
|
||||
compilationCallback = std::move(callback);
|
||||
}
|
||||
|
||||
void GraphicsPipelineAssembler::UnregisterCompilationCallback() {
|
||||
compilationCallback = {};
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <future>
|
||||
#include <BS_thread_pool.hpp>
|
||||
#include <vulkan/vulkan_raii.hpp>
|
||||
@ -59,6 +60,7 @@ namespace skyline::gpu {
|
||||
vk::raii::PipelineCache vkPipelineCache; //!< A Vulkan Pipeline Cache which stores all unique graphics pipelines
|
||||
BS::thread_pool pool;
|
||||
std::string pipelineCacheDir;
|
||||
std::function<void()> compilationCallback;
|
||||
|
||||
/**
|
||||
* @brief All unique metadata in a single attachment for a compatible render pass according to Render Pass Compatibility clause in the Vulkan specification
|
||||
@ -158,5 +160,15 @@ namespace skyline::gpu {
|
||||
* @brief Saves the current Vulkan pipeline cache to the filesystem
|
||||
*/
|
||||
void SavePipelineCache();
|
||||
|
||||
/**
|
||||
* @brief Registers a callback that is called whenever a pipeline is compiled
|
||||
*/
|
||||
void RegisterCompilationCallback(std::function<void()> callback);
|
||||
|
||||
/**
|
||||
* @brief Unregisters the compilation callback
|
||||
*/
|
||||
void UnregisterCompilationCallback();
|
||||
};
|
||||
}
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include <gpu/graphics_pipeline_assembler.h>
|
||||
#include <gpu/shader_manager.h>
|
||||
#include <gpu.h>
|
||||
#include <jvm.h>
|
||||
#include <vulkan/vulkan_enums.hpp>
|
||||
#include "graphics_pipeline_state_accessor.h"
|
||||
#include "pipeline_manager.h"
|
||||
@ -942,12 +943,19 @@ namespace skyline::gpu::interconnect::maxwell3d {
|
||||
});
|
||||
}
|
||||
|
||||
PipelineManager::PipelineManager(GPU &gpu) {
|
||||
PipelineManager::PipelineManager(GPU &gpu, JvmManager &jvm) {
|
||||
if (!gpu.graphicsPipelineCacheManager)
|
||||
return;
|
||||
|
||||
std::ifstream stream{gpu.graphicsPipelineCacheManager->OpenReadStream()};
|
||||
std::atomic<u32> compiledCount{};
|
||||
auto [stream, totalPipelineCount]{gpu.graphicsPipelineCacheManager->OpenReadStream()};
|
||||
i64 lastKnownGoodOffset{stream.tellg()};
|
||||
|
||||
jvm.ShowPipelineLoadingScreen(totalPipelineCount);
|
||||
gpu.graphicsPipelineAssembler->RegisterCompilationCallback([&]() {
|
||||
jvm.UpdatePipelineLoadingProgress(++compiledCount);
|
||||
});
|
||||
|
||||
try {
|
||||
auto startTime{util::GetTimeNs()};
|
||||
PipelineStateBundle bundle;
|
||||
@ -986,8 +994,10 @@ namespace skyline::gpu::interconnect::maxwell3d {
|
||||
} catch (const exception &e) {
|
||||
Logger::Warn("Pipeline cache corrupted at: 0x{:X}, error: {}", lastKnownGoodOffset, e.what());
|
||||
gpu.graphicsPipelineCacheManager->InvalidateAllAfter(static_cast<u64>(lastKnownGoodOffset));
|
||||
return;
|
||||
}
|
||||
|
||||
gpu.graphicsPipelineAssembler->UnregisterCompilationCallback();
|
||||
jvm.HidePipelineLoadingScreen();
|
||||
}
|
||||
|
||||
Pipeline *PipelineManager::FindOrCreate(InterconnectContext &ctx, Textures &textures, ConstantBufferSet &constantBuffers, const PackedPipelineState &packedState, const std::array<ShaderBinary, engine::PipelineCount> &shaderBinaries) {
|
||||
|
@ -272,7 +272,7 @@ namespace skyline::gpu::interconnect::maxwell3d {
|
||||
#endif
|
||||
|
||||
public:
|
||||
PipelineManager(GPU &gpu);
|
||||
PipelineManager(GPU &gpu, JvmManager &jvm);
|
||||
|
||||
Pipeline *FindOrCreate(InterconnectContext &ctx, Textures &textures, ConstantBufferSet &constantBuffers, const PackedPipelineState &packedState, const std::array<ShaderBinary, engine::PipelineCount> &shaderBinaries);
|
||||
};
|
||||
|
@ -8,19 +8,26 @@
|
||||
namespace skyline::gpu {
|
||||
struct PipelineCacheFileHeader {
|
||||
static constexpr u32 Magic{util::MakeMagic<u32>("PCHE")}; //!< The magic value used to identify a pipeline cache file
|
||||
static constexpr u32 Version{2}; //!< The version of the pipeline cache file format, MUST be incremented for any format changes
|
||||
static constexpr u32 Version{3}; //!< The version of the pipeline cache file format, MUST be incremented for any format changes
|
||||
|
||||
u32 magic{Magic};
|
||||
u32 version{Version};
|
||||
u32 count{0}; //!< The total number of pipeline cache bundles in the file
|
||||
|
||||
auto operator<=>(const PipelineCacheFileHeader &) const = default;
|
||||
};
|
||||
|
||||
static constexpr PipelineCacheFileHeader ValidPipelineCacheFileHeader{};
|
||||
/**
|
||||
* @brief Checks if the header is valid
|
||||
*/
|
||||
bool IsValid() {
|
||||
return magic == Magic && version == Version;
|
||||
}
|
||||
};
|
||||
|
||||
void PipelineCacheManager::Run() {
|
||||
std::ofstream stream{stagingPath, std::ios::binary | std::ios::trunc};
|
||||
stream.write(reinterpret_cast<const char *>(&ValidPipelineCacheFileHeader), sizeof(PipelineCacheFileHeader));
|
||||
PipelineCacheFileHeader header{};
|
||||
stream.write(reinterpret_cast<const char *>(&header), sizeof(PipelineCacheFileHeader));
|
||||
|
||||
while (true) {
|
||||
std::unique_lock lock(writeMutex);
|
||||
@ -31,7 +38,15 @@ namespace skyline::gpu {
|
||||
auto bundle{std::move(writeQueue.front())};
|
||||
writeQueue.pop();
|
||||
lock.unlock();
|
||||
|
||||
bundle->Serialise(stream);
|
||||
|
||||
header.count++;
|
||||
// Rewrite the header with the updated count
|
||||
auto savedPosition{stream.tellp()};
|
||||
stream.seekp(0, std::ios_base::beg);
|
||||
stream.write(reinterpret_cast<const char *>(&header), sizeof(PipelineCacheFileHeader));
|
||||
stream.seekp(savedPosition);
|
||||
}
|
||||
}
|
||||
|
||||
@ -40,8 +55,8 @@ namespace skyline::gpu {
|
||||
return false;
|
||||
|
||||
PipelineCacheFileHeader header{};
|
||||
stream.read(reinterpret_cast<char *>(&header), sizeof(header));
|
||||
return header == ValidPipelineCacheFileHeader;
|
||||
stream.read(reinterpret_cast<char *>(&header), sizeof(PipelineCacheFileHeader));
|
||||
return header.IsValid();
|
||||
}
|
||||
|
||||
void PipelineCacheManager::MergeStaging() {
|
||||
@ -49,12 +64,24 @@ namespace skyline::gpu {
|
||||
if (stagingStream.fail())
|
||||
return; // If the staging file doesn't exist then there's nothing to merge
|
||||
|
||||
if (!ValidateHeader(stagingStream)) {
|
||||
PipelineCacheFileHeader stagingHeader{};
|
||||
stagingStream.read(reinterpret_cast<char *>(&stagingHeader), sizeof(PipelineCacheFileHeader));
|
||||
if (!stagingHeader.IsValid()) {
|
||||
Logger::Warn("Discarding invalid pipeline cache staging file");
|
||||
return;
|
||||
}
|
||||
|
||||
std::ofstream mainStream{mainPath, std::ios::binary | std::ios::app};
|
||||
std::fstream mainStream{mainPath, std::ios::binary | std::ios::in | std::ios::out};
|
||||
PipelineCacheFileHeader mainHeader{};
|
||||
mainStream.seekg(0, std::ios_base::beg);
|
||||
mainStream.read(reinterpret_cast<char *>(&mainHeader), sizeof(PipelineCacheFileHeader));
|
||||
|
||||
// Update the main header with the new count
|
||||
mainHeader.count += stagingHeader.count;
|
||||
mainStream.seekp(0, std::ios_base::beg);
|
||||
mainStream.write(reinterpret_cast<const char *>(&mainHeader), sizeof(PipelineCacheFileHeader));
|
||||
|
||||
mainStream.seekp(0, std::ios::end);
|
||||
mainStream << stagingStream.rdbuf();
|
||||
}
|
||||
|
||||
@ -73,7 +100,8 @@ namespace skyline::gpu {
|
||||
if (!didExist) { // If the main file didn't exist we need to write the header
|
||||
std::filesystem::create_directories(std::filesystem::path{mainPath}.parent_path());
|
||||
std::ofstream mainStream{mainPath, std::ios::binary | std::ios::app};
|
||||
mainStream.write(reinterpret_cast<const char *>(&ValidPipelineCacheFileHeader), sizeof(PipelineCacheFileHeader));
|
||||
PipelineCacheFileHeader header{};
|
||||
mainStream.write(reinterpret_cast<const char *>(&header), sizeof(PipelineCacheFileHeader));
|
||||
}
|
||||
|
||||
// Merge any staging changes into the main file before starting the writer thread
|
||||
@ -87,12 +115,17 @@ namespace skyline::gpu {
|
||||
writeCondition.notify_one();
|
||||
}
|
||||
|
||||
std::ifstream PipelineCacheManager::OpenReadStream() {
|
||||
std::pair<std::ifstream, u32> PipelineCacheManager::OpenReadStream() {
|
||||
auto mainStream{std::ifstream{mainPath, std::ios::binary}};
|
||||
if (!ValidateHeader(mainStream))
|
||||
if (mainStream.fail())
|
||||
throw exception("Pipeline cache main file missing at runtime!");
|
||||
|
||||
PipelineCacheFileHeader header{};
|
||||
mainStream.read(reinterpret_cast<char *>(&header), sizeof(PipelineCacheFileHeader));
|
||||
if (!header.IsValid())
|
||||
throw exception("Pipeline cache main file corrupted at runtime!");
|
||||
|
||||
return mainStream;
|
||||
return {std::move(mainStream), header.count};
|
||||
}
|
||||
|
||||
void PipelineCacheManager::InvalidateAllAfter(u64 offset) {
|
||||
|
@ -34,7 +34,11 @@ namespace skyline::gpu {
|
||||
*/
|
||||
void QueueWrite(std::unique_ptr<interconnect::PipelineStateBundle> bundle);
|
||||
|
||||
std::ifstream OpenReadStream();
|
||||
/**
|
||||
* @brief Opens the main pipeline cache file for reading
|
||||
* @return A pair containing the stream and the total pipeline count
|
||||
*/
|
||||
std::pair<std::ifstream, u32> OpenReadStream();
|
||||
|
||||
/**
|
||||
* @brief Shrinks the pipeline cache file to `offset` bytes, removing any (potentially invalid) data after that point
|
||||
|
Loading…
Reference in New Issue
Block a user