mirror of
https://github.com/izzy2lost/xenia-edge.git
synced 2026-07-06 00:20:26 -07:00
[Vulkan] Add shader disk storage / startup loading
Big refactor of the shader storage to allow both backends to share code
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -18,6 +18,7 @@
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
@@ -36,6 +37,7 @@
|
||||
#include "xenia/gpu/primitive_processor.h"
|
||||
#include "xenia/gpu/register_file.h"
|
||||
#include "xenia/gpu/registers.h"
|
||||
#include "xenia/gpu/shader_storage.h"
|
||||
#include "xenia/gpu/xenos.h"
|
||||
#include "xenia/ui/d3d12/d3d12_api.h"
|
||||
|
||||
@@ -114,15 +116,6 @@ class PipelineCache {
|
||||
}
|
||||
|
||||
private:
|
||||
XEPACKEDSTRUCT(ShaderStoredHeader, {
|
||||
uint64_t ucode_data_hash;
|
||||
|
||||
uint32_t ucode_dword_count : 31;
|
||||
xenos::ShaderType type : 1;
|
||||
|
||||
static constexpr uint32_t kVersion = 0x20201219;
|
||||
});
|
||||
|
||||
// Update PipelineDescription::kVersion if any of the Pipeline* enums are
|
||||
// changed!
|
||||
|
||||
@@ -290,6 +283,11 @@ class PipelineCache {
|
||||
IDxcUtils* dxc_utils = nullptr,
|
||||
IDxcCompiler* dxc_compiler = nullptr);
|
||||
|
||||
// Translates shaders in parallel for storage loading.
|
||||
void TranslateShadersForStorage(
|
||||
const std::set<std::pair<uint64_t, uint64_t>>& translations_needed,
|
||||
bool edram_rov_used);
|
||||
|
||||
// If draw_util::IsRasterizationPotentiallyDone is false, the pixel shader
|
||||
// MUST be made nullptr BEFORE calling this! The shaders must be translated
|
||||
// and valid, unless for_placeholder is true.
|
||||
@@ -416,33 +414,14 @@ class PipelineCache {
|
||||
// changed.
|
||||
Pipeline* current_pipeline_ = nullptr;
|
||||
|
||||
// Currently open shader storage path.
|
||||
std::filesystem::path shader_storage_cache_root_;
|
||||
// Currently open shader storage state.
|
||||
uint32_t shader_storage_title_id_ = 0;
|
||||
std::atomic<bool> shader_storage_file_flush_needed_{false};
|
||||
std::atomic<bool> pipeline_storage_file_flush_needed_{false};
|
||||
|
||||
// Shader storage output stream, for preload in the next emulator runs.
|
||||
FILE* shader_storage_file_ = nullptr;
|
||||
// For only writing shaders to the currently open storage once, incremented
|
||||
// when switching the storage.
|
||||
uint32_t shader_storage_index_ = 0;
|
||||
bool shader_storage_file_flush_needed_ = false;
|
||||
|
||||
// Pipeline storage output stream, for preload in the next emulator runs.
|
||||
FILE* pipeline_storage_file_ = nullptr;
|
||||
bool pipeline_storage_file_flush_needed_ = false;
|
||||
|
||||
// Thread for asynchronous writing to the storage streams.
|
||||
void StorageWriteThread();
|
||||
std::mutex storage_write_request_lock_;
|
||||
std::condition_variable storage_write_request_cond_;
|
||||
// Storage thread input is protected with storage_write_request_lock_, and the
|
||||
// thread is notified about its change via storage_write_request_cond_.
|
||||
std::deque<const Shader*> storage_write_shader_queue_;
|
||||
std::deque<PipelineStoredDescription> storage_write_pipeline_queue_;
|
||||
bool storage_write_flush_shaders_ = false;
|
||||
bool storage_write_flush_pipelines_ = false;
|
||||
bool storage_write_thread_shutdown_ = false;
|
||||
std::unique_ptr<xe::threading::Thread> storage_write_thread_;
|
||||
// Storage writer for shaders and pipelines (owns file handles and storage
|
||||
// index).
|
||||
ShaderStorageWriter<PipelineStoredDescription> storage_writer_;
|
||||
|
||||
// Pipeline creation threads.
|
||||
void CreationThread(size_t thread_index);
|
||||
|
||||
+15
-3
@@ -11,6 +11,7 @@
|
||||
#define XENIA_GPU_SHADER_H_
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <filesystem>
|
||||
#include <set>
|
||||
@@ -1007,9 +1008,20 @@ class Shader {
|
||||
// An externally managed identifier of the shader storage the microcode of the
|
||||
// shader was last written to, or was loaded from, to only write the shader
|
||||
// microcode to the storage once. UINT32_MAX by default.
|
||||
uint32_t ucode_storage_index() const { return ucode_storage_index_; }
|
||||
uint32_t ucode_storage_index() const {
|
||||
return ucode_storage_index_.load(std::memory_order_relaxed);
|
||||
}
|
||||
void set_ucode_storage_index(uint32_t storage_index) {
|
||||
ucode_storage_index_ = storage_index;
|
||||
ucode_storage_index_.store(storage_index, std::memory_order_relaxed);
|
||||
}
|
||||
// Atomically set storage index if changed. Returns true if updated.
|
||||
bool try_set_ucode_storage_index(uint32_t new_index) {
|
||||
uint32_t expected = ucode_storage_index_.load(std::memory_order_relaxed);
|
||||
if (expected == new_index) {
|
||||
return false;
|
||||
}
|
||||
return ucode_storage_index_.compare_exchange_strong(
|
||||
expected, new_index, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
// Dumps the shader's microcode binary and, if analyzed, disassembly, to files
|
||||
@@ -1074,7 +1086,7 @@ class Shader {
|
||||
// Modification bits -> translation.
|
||||
std::unordered_map<uint64_t, Translation*> translations_;
|
||||
|
||||
uint32_t ucode_storage_index_ = UINT32_MAX;
|
||||
std::atomic<uint32_t> ucode_storage_index_{UINT32_MAX};
|
||||
|
||||
private:
|
||||
void GatherExecInformation(
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -167,6 +167,15 @@ void VulkanCommandProcessor::TracePlaybackWroteMemory(uint32_t base_ptr,
|
||||
primitive_processor_->MemoryInvalidationCallback(base_ptr, length, true);
|
||||
}
|
||||
|
||||
void VulkanCommandProcessor::InitializeShaderStorage(
|
||||
const std::filesystem::path& cache_root, uint32_t title_id, bool blocking,
|
||||
std::function<void()> completion_callback) {
|
||||
CommandProcessor::InitializeShaderStorage(cache_root, title_id, blocking,
|
||||
nullptr);
|
||||
pipeline_cache_->InitializeShaderStorage(cache_root, title_id, blocking,
|
||||
std::move(completion_callback));
|
||||
}
|
||||
|
||||
void VulkanCommandProcessor::RestoreEdramSnapshot(const void* snapshot) {}
|
||||
|
||||
void VulkanCommandProcessor::PrepareForWait() {
|
||||
|
||||
@@ -148,6 +148,10 @@ class VulkanCommandProcessor final : public CommandProcessor {
|
||||
|
||||
void TracePlaybackWroteMemory(uint32_t base_ptr, uint32_t length) override;
|
||||
|
||||
void InitializeShaderStorage(
|
||||
const std::filesystem::path& cache_root, uint32_t title_id, bool blocking,
|
||||
std::function<void()> completion_callback = nullptr) override;
|
||||
|
||||
void RestoreEdramSnapshot(const void* snapshot) override;
|
||||
|
||||
void PrepareForWait() override;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -13,11 +13,15 @@
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <cstddef>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <deque>
|
||||
#include <filesystem>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
@@ -29,6 +33,7 @@
|
||||
#include "xenia/gpu/primitive_processor.h"
|
||||
#include "xenia/gpu/register_file.h"
|
||||
#include "xenia/gpu/registers.h"
|
||||
#include "xenia/gpu/shader_storage.h"
|
||||
#include "xenia/gpu/spirv_shader_translator.h"
|
||||
#include "xenia/gpu/vulkan/vulkan_render_target_cache.h"
|
||||
#include "xenia/gpu/vulkan/vulkan_shader.h"
|
||||
@@ -105,6 +110,12 @@ class VulkanPipelineCache {
|
||||
bool Initialize();
|
||||
void Shutdown();
|
||||
|
||||
// Shader and pipeline storage.
|
||||
void InitializeShaderStorage(
|
||||
const std::filesystem::path& cache_root, uint32_t title_id, bool blocking,
|
||||
std::function<void()> completion_callback = nullptr);
|
||||
void ShutdownShaderStorage();
|
||||
|
||||
void EndSubmission();
|
||||
bool IsCreatingPipelines();
|
||||
|
||||
@@ -263,6 +274,18 @@ class VulkanPipelineCache {
|
||||
return size_t(description.GetHash());
|
||||
}
|
||||
};
|
||||
|
||||
static constexpr uint32_t kVersion = 0x20250118;
|
||||
});
|
||||
|
||||
// Pipeline storage constants.
|
||||
static constexpr uint32_t kPipelineStorageVersionWithoutAPI = 0x20201219;
|
||||
static constexpr uint32_t kPipelineStorageAPIMagicVulkan = 'VLKN';
|
||||
|
||||
// Pipeline storage description.
|
||||
XEPACKEDSTRUCT(PipelineStoredDescription, {
|
||||
uint64_t description_hash;
|
||||
PipelineDescription description;
|
||||
});
|
||||
|
||||
// creation threads, with everything needed from caches pre-looked-up.
|
||||
@@ -320,6 +343,11 @@ class VulkanPipelineCache {
|
||||
bool TranslateAnalyzedShader(SpirvShaderTranslator& translator,
|
||||
VulkanShader::VulkanTranslation& translation);
|
||||
|
||||
// Translates shaders in parallel for storage loading.
|
||||
void TranslateShadersForStorage(
|
||||
const std::set<std::pair<uint64_t, uint64_t>>& translations_needed,
|
||||
bool edram_fsi_used);
|
||||
|
||||
void WritePipelineRenderTargetDescription(
|
||||
reg::RB_BLENDCONTROL blend_control, uint32_t write_mask,
|
||||
PipelineRenderTarget& render_target_out) const;
|
||||
@@ -468,6 +496,10 @@ class VulkanPipelineCache {
|
||||
std::condition_variable creation_request_cond_;
|
||||
std::unique_ptr<xe::threading::Event> creation_completion_event_ = nullptr;
|
||||
std::atomic<bool> creation_completion_set_event_{false};
|
||||
std::function<void()> creation_completion_callback_;
|
||||
// During startup loading, don't block on pipeline creation to allow game
|
||||
// boot.
|
||||
bool startup_loading_ = false;
|
||||
|
||||
// Deferred destruction of pipelines.
|
||||
// Pipelines are only destroyed after the GPU submission that might reference
|
||||
@@ -475,6 +507,18 @@ class VulkanPipelineCache {
|
||||
void ProcessDeferredDestructions();
|
||||
std::vector<std::pair<VkPipeline, uint64_t>> deferred_destroy_pipelines_;
|
||||
std::mutex deferred_destroy_mutex_;
|
||||
|
||||
// Shader and pipeline storage.
|
||||
uint32_t shader_storage_title_id_ = 0;
|
||||
std::atomic<bool> shader_storage_file_flush_needed_{false};
|
||||
std::atomic<bool> pipeline_storage_file_flush_needed_{false};
|
||||
|
||||
// Storage writer for shaders and pipelines (owns file handles and storage
|
||||
// index).
|
||||
ShaderStorageWriter<PipelineStoredDescription> storage_writer_;
|
||||
|
||||
// VkPipelineCache persistence path.
|
||||
std::filesystem::path vk_pipeline_cache_path_;
|
||||
};
|
||||
|
||||
} // namespace vulkan
|
||||
|
||||
@@ -75,6 +75,7 @@ XE_UI_VULKAN_FUNCTION(vkGetBufferMemoryRequirements)
|
||||
XE_UI_VULKAN_FUNCTION(vkGetDeviceQueue)
|
||||
XE_UI_VULKAN_FUNCTION(vkGetFenceStatus)
|
||||
XE_UI_VULKAN_FUNCTION(vkGetImageMemoryRequirements)
|
||||
XE_UI_VULKAN_FUNCTION(vkGetPipelineCacheData)
|
||||
XE_UI_VULKAN_FUNCTION(vkInvalidateMappedMemoryRanges)
|
||||
XE_UI_VULKAN_FUNCTION(vkMapMemory)
|
||||
XE_UI_VULKAN_FUNCTION(vkResetCommandPool)
|
||||
|
||||
Reference in New Issue
Block a user