mirror of
https://github.com/ApfelTeeSaft/azahar.git
synced 2026-08-26 19:23:31 +00:00
video_core: Fixes to vulkan disk cache (#1748)
This commit is contained in:
@@ -1254,20 +1254,19 @@ static std::size_t pread(int fd, void* buf, std::size_t count, uint64_t offset)
|
|||||||
#define pread ::pread
|
#define pread ::pread
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
std::size_t IOFile::ReadAtImpl(void* data, std::size_t length, std::size_t data_size,
|
std::size_t IOFile::ReadAtImpl(void* data, std::size_t byte_count, std::size_t offset) {
|
||||||
std::size_t offset) {
|
|
||||||
if (!IsOpen()) {
|
if (!IsOpen()) {
|
||||||
m_good = false;
|
m_good = false;
|
||||||
return std::numeric_limits<std::size_t>::max();
|
return std::numeric_limits<std::size_t>::max();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (length == 0) {
|
if (byte_count == 0) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
DEBUG_ASSERT(data != nullptr);
|
DEBUG_ASSERT(data != nullptr);
|
||||||
|
|
||||||
return pread(fileno(m_file), data, data_size * length, offset);
|
return pread(fileno(m_file), data, byte_count, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t IOFile::WriteImpl(const void* data, std::size_t length, std::size_t data_size) {
|
std::size_t IOFile::WriteImpl(const void* data, std::size_t length, std::size_t data_size) {
|
||||||
@@ -1315,19 +1314,19 @@ struct CryptoIOFileImpl {
|
|||||||
std::size_t res = f.IOFile::ReadImpl(data, length, data_size);
|
std::size_t res = f.IOFile::ReadImpl(data, length, data_size);
|
||||||
if (res != std::numeric_limits<std::size_t>::max() && res != 0) {
|
if (res != std::numeric_limits<std::size_t>::max() && res != 0) {
|
||||||
d.ProcessData(reinterpret_cast<CryptoPP::byte*>(data),
|
d.ProcessData(reinterpret_cast<CryptoPP::byte*>(data),
|
||||||
reinterpret_cast<CryptoPP::byte*>(data), length * data_size);
|
reinterpret_cast<CryptoPP::byte*>(data), res * data_size);
|
||||||
e.Seek(f.IOFile::Tell());
|
e.Seek(f.IOFile::Tell());
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t ReadAtImpl(CryptoIOFile& f, void* data, std::size_t length, std::size_t data_size,
|
std::size_t ReadAtImpl(CryptoIOFile& f, void* data, std::size_t byte_count,
|
||||||
std::size_t offset) {
|
std::size_t offset) {
|
||||||
std::size_t res = f.IOFile::ReadAtImpl(data, length, data_size, offset);
|
std::size_t res = f.IOFile::ReadAtImpl(data, byte_count, offset);
|
||||||
if (res != std::numeric_limits<std::size_t>::max() && res != 0) {
|
if (res != std::numeric_limits<std::size_t>::max() && res != 0) {
|
||||||
d.Seek(offset);
|
d.Seek(offset);
|
||||||
d.ProcessData(reinterpret_cast<CryptoPP::byte*>(data),
|
d.ProcessData(reinterpret_cast<CryptoPP::byte*>(data),
|
||||||
reinterpret_cast<CryptoPP::byte*>(data), length * data_size);
|
reinterpret_cast<CryptoPP::byte*>(data), res);
|
||||||
e.Seek(f.IOFile::Tell());
|
e.Seek(f.IOFile::Tell());
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
@@ -1378,9 +1377,8 @@ std::size_t CryptoIOFile::ReadImpl(void* data, std::size_t length, std::size_t d
|
|||||||
return impl->ReadImpl(*this, data, length, data_size);
|
return impl->ReadImpl(*this, data, length, data_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t CryptoIOFile::ReadAtImpl(void* data, std::size_t length, std::size_t data_size,
|
std::size_t CryptoIOFile::ReadAtImpl(void* data, std::size_t byte_count, std::size_t offset) {
|
||||||
std::size_t offset) {
|
return impl->ReadAtImpl(*this, data, byte_count, offset);
|
||||||
return impl->ReadAtImpl(*this, data, length, data_size, offset);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t CryptoIOFile::WriteImpl(const void* data, std::size_t length, std::size_t data_size) {
|
std::size_t CryptoIOFile::WriteImpl(const void* data, std::size_t length, std::size_t data_size) {
|
||||||
|
|||||||
@@ -302,6 +302,7 @@ public:
|
|||||||
|
|
||||||
virtual bool Close();
|
virtual bool Close();
|
||||||
|
|
||||||
|
/// Returns the amount of T items read
|
||||||
template <typename T>
|
template <typename T>
|
||||||
std::size_t ReadArray(T* data, std::size_t length) {
|
std::size_t ReadArray(T* data, std::size_t length) {
|
||||||
static_assert(std::is_trivially_copyable_v<T>,
|
static_assert(std::is_trivially_copyable_v<T>,
|
||||||
@@ -314,16 +315,18 @@ public:
|
|||||||
return items_read;
|
return items_read;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the amount of bytes read
|
||||||
template <typename T>
|
template <typename T>
|
||||||
std::size_t ReadAtArray(T* data, std::size_t length, std::size_t offset) {
|
std::size_t ReadAtArray(T* data, std::size_t length, std::size_t offset) {
|
||||||
static_assert(std::is_trivially_copyable_v<T>,
|
static_assert(std::is_trivially_copyable_v<T>,
|
||||||
"Given array does not consist of trivially copyable objects");
|
"Given array does not consist of trivially copyable objects");
|
||||||
|
|
||||||
std::size_t items_read = ReadAtImpl(data, length, sizeof(T), offset);
|
const size_t bytes = length * sizeof(T);
|
||||||
if (items_read != length)
|
std::size_t size_read = ReadAtImpl(data, bytes, offset);
|
||||||
|
if (size_read != bytes)
|
||||||
m_good = false;
|
m_good = false;
|
||||||
|
|
||||||
return items_read;
|
return size_read;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
@@ -466,8 +469,7 @@ protected:
|
|||||||
virtual bool Open();
|
virtual bool Open();
|
||||||
|
|
||||||
virtual std::size_t ReadImpl(void* data, std::size_t length, std::size_t data_size);
|
virtual std::size_t ReadImpl(void* data, std::size_t length, std::size_t data_size);
|
||||||
virtual std::size_t ReadAtImpl(void* data, std::size_t length, std::size_t data_size,
|
virtual std::size_t ReadAtImpl(void* data, std::size_t byte_count, std::size_t offset);
|
||||||
std::size_t offset);
|
|
||||||
virtual std::size_t WriteImpl(const void* data, std::size_t length, std::size_t data_size);
|
virtual std::size_t WriteImpl(const void* data, std::size_t length, std::size_t data_size);
|
||||||
|
|
||||||
virtual bool SeekImpl(s64 off, int origin);
|
virtual bool SeekImpl(s64 off, int origin);
|
||||||
@@ -520,8 +522,7 @@ private:
|
|||||||
std::unique_ptr<CryptoIOFileImpl> impl;
|
std::unique_ptr<CryptoIOFileImpl> impl;
|
||||||
|
|
||||||
std::size_t ReadImpl(void* data, std::size_t length, std::size_t data_size) override;
|
std::size_t ReadImpl(void* data, std::size_t length, std::size_t data_size) override;
|
||||||
std::size_t ReadAtImpl(void* data, std::size_t length, std::size_t data_size,
|
std::size_t ReadAtImpl(void* data, std::size_t byte_count, std::size_t offset) override;
|
||||||
std::size_t offset) override;
|
|
||||||
std::size_t WriteImpl(const void* data, std::size_t length, std::size_t data_size) override;
|
std::size_t WriteImpl(const void* data, std::size_t length, std::size_t data_size) override;
|
||||||
|
|
||||||
bool SeekImpl(s64 off, int origin) override;
|
bool SeekImpl(s64 off, int origin) override;
|
||||||
|
|||||||
@@ -357,8 +357,7 @@ std::size_t Z3DSWriteIOFile::ReadImpl(void* data, std::size_t length, std::size_
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t Z3DSWriteIOFile::ReadAtImpl(void* data, std::size_t length, std::size_t data_size,
|
std::size_t Z3DSWriteIOFile::ReadAtImpl(void* data, std::size_t byte_count, std::size_t offset) {
|
||||||
std::size_t offset) {
|
|
||||||
// Stubbed
|
// Stubbed
|
||||||
UNIMPLEMENTED();
|
UNIMPLEMENTED();
|
||||||
return 0;
|
return 0;
|
||||||
@@ -619,12 +618,12 @@ bool Z3DSReadIOFile::Open() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::size_t Z3DSReadIOFile::ReadImpl(void* data, std::size_t length, std::size_t data_size) {
|
std::size_t Z3DSReadIOFile::ReadImpl(void* data, std::size_t length, std::size_t data_size) {
|
||||||
return impl->Read(data, length * data_size);
|
size_t res = impl->Read(data, length * data_size);
|
||||||
|
return res == std::numeric_limits<size_t>::max() ? res : (res / data_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t Z3DSReadIOFile::ReadAtImpl(void* data, std::size_t length, std::size_t data_size,
|
std::size_t Z3DSReadIOFile::ReadAtImpl(void* data, std::size_t byte_count, std::size_t offset) {
|
||||||
std::size_t offset) {
|
return impl->ReadAt(data, byte_count, offset);
|
||||||
return impl->ReadAt(data, length * data_size, offset);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t Z3DSReadIOFile::WriteImpl(const void* data, std::size_t length, std::size_t data_size) {
|
std::size_t Z3DSReadIOFile::WriteImpl(const void* data, std::size_t length, std::size_t data_size) {
|
||||||
|
|||||||
@@ -183,8 +183,7 @@ private:
|
|||||||
bool Open() override;
|
bool Open() override;
|
||||||
|
|
||||||
std::size_t ReadImpl(void* data, std::size_t length, std::size_t data_size) override;
|
std::size_t ReadImpl(void* data, std::size_t length, std::size_t data_size) override;
|
||||||
std::size_t ReadAtImpl(void* data, std::size_t length, std::size_t data_size,
|
std::size_t ReadAtImpl(void* data, std::size_t byte_count, std::size_t offset) override;
|
||||||
std::size_t offset) override;
|
|
||||||
std::size_t WriteImpl(const void* data, std::size_t length, std::size_t data_size) override;
|
std::size_t WriteImpl(const void* data, std::size_t length, std::size_t data_size) override;
|
||||||
|
|
||||||
bool SeekImpl(s64 off, int origin) override;
|
bool SeekImpl(s64 off, int origin) override;
|
||||||
@@ -250,8 +249,7 @@ private:
|
|||||||
bool Open() override;
|
bool Open() override;
|
||||||
|
|
||||||
std::size_t ReadImpl(void* data, std::size_t length, std::size_t data_size) override;
|
std::size_t ReadImpl(void* data, std::size_t length, std::size_t data_size) override;
|
||||||
std::size_t ReadAtImpl(void* data, std::size_t length, std::size_t data_size,
|
std::size_t ReadAtImpl(void* data, std::size_t byte_count, std::size_t offset) override;
|
||||||
std::size_t offset) override;
|
|
||||||
std::size_t WriteImpl(const void* data, std::size_t length, std::size_t data_size) override;
|
std::size_t WriteImpl(const void* data, std::size_t length, std::size_t data_size) override;
|
||||||
|
|
||||||
bool SeekImpl(s64 off, int origin) override;
|
bool SeekImpl(s64 off, int origin) override;
|
||||||
|
|||||||
@@ -168,7 +168,6 @@ Loader::ResultStatus NCCHContainer::LoadHeader() {
|
|||||||
ASSERT(Loader::MakeMagic('N', 'C', 'S', 'D') == ncsd_header.magic);
|
ASSERT(Loader::MakeMagic('N', 'C', 'S', 'D') == ncsd_header.magic);
|
||||||
ASSERT(partition < 8);
|
ASSERT(partition < 8);
|
||||||
ncch_offset = ncsd_header.partitions[partition].offset * kBlockSize;
|
ncch_offset = ncsd_header.partitions[partition].offset * kBlockSize;
|
||||||
LOG_ERROR(Service_FS, "{}", ncch_offset);
|
|
||||||
file->Seek(ncch_offset, SEEK_SET);
|
file->Seek(ncch_offset, SEEK_SET);
|
||||||
file->ReadBytes(&ncch_header, sizeof(NCCH_Header));
|
file->ReadBytes(&ncch_header, sizeof(NCCH_Header));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -144,24 +144,59 @@ void PipelineCache::BuildLayout() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
PipelineCache::~PipelineCache() {
|
PipelineCache::~PipelineCache() {
|
||||||
SaveDiskCache();
|
workers.WaitForRequests();
|
||||||
|
SaveDriverPipelineDiskCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PipelineCache::LoadPipelineDiskCache(const std::atomic_bool& stop_loading,
|
void PipelineCache::LoadCache(const std::atomic_bool& stop_loading,
|
||||||
const VideoCore::DiskResourceLoadCallback& callback) {
|
const VideoCore::DiskResourceLoadCallback& callback) {
|
||||||
|
LoadDriverPipelineDiskCache(stop_loading, callback);
|
||||||
|
LoadDiskCache(stop_loading, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PipelineCache::SwitchCache(u64 title_id, const std::atomic_bool& stop_loading,
|
||||||
|
const VideoCore::DiskResourceLoadCallback& callback) {
|
||||||
|
if (GetProgramID() == title_id) {
|
||||||
|
LOG_DEBUG(Render_Vulkan,
|
||||||
|
"Skipping pipeline cache switch - already using cache for title_id={:016X}",
|
||||||
|
title_id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure we have a valid pipeline cache before switching
|
||||||
|
if (!driver_pipeline_cache) {
|
||||||
|
vk::PipelineCacheCreateInfo cache_info{};
|
||||||
|
try {
|
||||||
|
driver_pipeline_cache = instance.GetDevice().createPipelineCacheUnique(cache_info);
|
||||||
|
} catch (const vk::SystemError& err) {
|
||||||
|
LOG_ERROR(Render_Vulkan, "Failed to create pipeline cache: {}", err.what());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_INFO(Render_Vulkan, "Switching pipeline cache to title_id={:016X}", title_id);
|
||||||
|
|
||||||
|
// Save current driver cache, update program ID and load the new driver cache
|
||||||
|
SaveDriverPipelineDiskCache();
|
||||||
|
SetProgramID(title_id);
|
||||||
|
LoadDriverPipelineDiskCache(stop_loading, nullptr);
|
||||||
|
|
||||||
|
// Switch the disk shader cache after driver cache is switched
|
||||||
|
SwitchDiskCache(title_id, stop_loading, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PipelineCache::LoadDriverPipelineDiskCache(
|
||||||
|
const std::atomic_bool& stop_loading, const VideoCore::DiskResourceLoadCallback& callback) {
|
||||||
vk::PipelineCacheCreateInfo cache_info{};
|
vk::PipelineCacheCreateInfo cache_info{};
|
||||||
|
|
||||||
if (callback) {
|
|
||||||
callback(VideoCore::LoadCallbackStage::Prepare, 0, 0, "");
|
|
||||||
}
|
|
||||||
if (callback) {
|
if (callback) {
|
||||||
callback(VideoCore::LoadCallbackStage::Build, 0, 1, "Driver Pipeline Cache");
|
callback(VideoCore::LoadCallbackStage::Build, 0, 1, "Driver Pipeline Cache");
|
||||||
}
|
}
|
||||||
|
|
||||||
auto load_cache = [this, &cache_info](bool allow_fallback) {
|
auto load_cache = [this, &cache_info, &callback](bool allow_fallback) {
|
||||||
const vk::Device device = instance.GetDevice();
|
const vk::Device device = instance.GetDevice();
|
||||||
try {
|
try {
|
||||||
pipeline_cache = device.createPipelineCacheUnique(cache_info);
|
driver_pipeline_cache = device.createPipelineCacheUnique(cache_info);
|
||||||
} catch (const vk::SystemError& err) {
|
} catch (const vk::SystemError& err) {
|
||||||
LOG_ERROR(Render_Vulkan, "Failed to create pipeline cache: {}", err.what());
|
LOG_ERROR(Render_Vulkan, "Failed to create pipeline cache: {}", err.what());
|
||||||
if (allow_fallback) {
|
if (allow_fallback) {
|
||||||
@@ -169,13 +204,16 @@ void PipelineCache::LoadPipelineDiskCache(const std::atomic_bool& stop_loading,
|
|||||||
cache_info.initialDataSize = 0;
|
cache_info.initialDataSize = 0;
|
||||||
cache_info.pInitialData = nullptr;
|
cache_info.pInitialData = nullptr;
|
||||||
try {
|
try {
|
||||||
pipeline_cache = device.createPipelineCacheUnique(cache_info);
|
driver_pipeline_cache = device.createPipelineCacheUnique(cache_info);
|
||||||
} catch (const vk::SystemError& err) {
|
} catch (const vk::SystemError& err) {
|
||||||
LOG_ERROR(Render_Vulkan, "Failed to create fallback pipeline cache: {}",
|
LOG_ERROR(Render_Vulkan, "Failed to create fallback pipeline cache: {}",
|
||||||
err.what());
|
err.what());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (callback) {
|
||||||
|
callback(VideoCore::LoadCallbackStage::Build, 1, 1, "Driver Pipeline Cache");
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Try to load existing pipeline cache if disk cache is enabled and directories exist
|
// Try to load existing pipeline cache if disk cache is enabled and directories exist
|
||||||
@@ -226,19 +264,9 @@ void PipelineCache::LoadPipelineDiskCache(const std::atomic_bool& stop_loading,
|
|||||||
load_cache(true);
|
load_cache(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PipelineCache::LoadDiskCache(const std::atomic_bool& stop_loading,
|
void PipelineCache::SaveDriverPipelineDiskCache() {
|
||||||
const VideoCore::DiskResourceLoadCallback& callback) {
|
|
||||||
|
|
||||||
disk_caches.clear();
|
|
||||||
curr_disk_cache =
|
|
||||||
disk_caches.emplace_back(std::make_shared<ShaderDiskCache>(*this, GetProgramID()));
|
|
||||||
|
|
||||||
curr_disk_cache->Init(stop_loading, callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PipelineCache::SaveDiskCache() {
|
|
||||||
// Save Vulkan pipeline cache
|
// Save Vulkan pipeline cache
|
||||||
if (!Settings::values.use_disk_shader_cache || !pipeline_cache) {
|
if (!Settings::values.use_disk_shader_cache || !driver_pipeline_cache) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -258,13 +286,81 @@ void PipelineCache::SaveDiskCache() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const vk::Device device = instance.GetDevice();
|
const vk::Device device = instance.GetDevice();
|
||||||
const auto cache_data = device.getPipelineCacheData(*pipeline_cache);
|
const auto cache_data = device.getPipelineCacheData(*driver_pipeline_cache);
|
||||||
if (cache_file.WriteBytes(cache_data.data(), cache_data.size()) != cache_data.size()) {
|
if (cache_file.WriteBytes(cache_data.data(), cache_data.size()) != cache_data.size()) {
|
||||||
LOG_ERROR(Render_Vulkan, "Error during pipeline cache write");
|
LOG_ERROR(Render_Vulkan, "Error during pipeline cache write");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PipelineCache::LoadDiskCache(const std::atomic_bool& stop_loading,
|
||||||
|
const VideoCore::DiskResourceLoadCallback& callback) {
|
||||||
|
|
||||||
|
disk_caches.clear();
|
||||||
|
curr_disk_cache =
|
||||||
|
disk_caches.emplace_back(std::make_shared<ShaderDiskCache>(*this, GetProgramID()));
|
||||||
|
|
||||||
|
curr_disk_cache->Init(stop_loading, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PipelineCache::SwitchDiskCache(u64 title_id, const std::atomic_bool& stop_loading,
|
||||||
|
const VideoCore::DiskResourceLoadCallback& callback) {
|
||||||
|
// NOTE: curr_disk_cache can be null if emulation restarted without calling
|
||||||
|
// LoadDefaultDiskResources
|
||||||
|
|
||||||
|
// Check if the current cache is for the specified TID.
|
||||||
|
if (curr_disk_cache && curr_disk_cache->GetProgramID() == title_id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search for an existing manager
|
||||||
|
size_t new_pos = 0;
|
||||||
|
for (new_pos = 0; new_pos < disk_caches.size(); new_pos++) {
|
||||||
|
if (disk_caches[new_pos]->GetProgramID() == title_id) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Manager does not exist, create it and append to the end
|
||||||
|
if (new_pos >= disk_caches.size()) {
|
||||||
|
new_pos = disk_caches.size();
|
||||||
|
auto& new_manager =
|
||||||
|
disk_caches.emplace_back(std::make_shared<ShaderDiskCache>(*this, title_id));
|
||||||
|
|
||||||
|
new_manager->Init(stop_loading, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto is_applet = [](u64 tid) {
|
||||||
|
constexpr u32 APPLET_TID_HIGH = 0x00040030;
|
||||||
|
return static_cast<u32>(tid >> 32) == APPLET_TID_HIGH;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool prev_applet = curr_disk_cache ? is_applet(curr_disk_cache->GetProgramID()) : false;
|
||||||
|
bool new_applet = is_applet(disk_caches[new_pos]->GetProgramID());
|
||||||
|
curr_disk_cache = disk_caches[new_pos];
|
||||||
|
|
||||||
|
if (prev_applet) {
|
||||||
|
// If we came from an applet, clean up all other applets
|
||||||
|
for (auto it = disk_caches.begin(); it != disk_caches.end();) {
|
||||||
|
if (it == disk_caches.begin() || *it == curr_disk_cache ||
|
||||||
|
!is_applet((*it)->GetProgramID())) {
|
||||||
|
it++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
it = disk_caches.erase(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!new_applet) {
|
||||||
|
// If we are going into a non-applet, clean up everything
|
||||||
|
for (auto it = disk_caches.begin(); it != disk_caches.end();) {
|
||||||
|
if (it == disk_caches.begin() || *it == curr_disk_cache) {
|
||||||
|
it++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
it = disk_caches.erase(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool PipelineCache::BindPipeline(PipelineInfo& info, bool wait_built) {
|
bool PipelineCache::BindPipeline(PipelineInfo& info, bool wait_built) {
|
||||||
MICROPROFILE_SCOPE(Vulkan_Bind);
|
MICROPROFILE_SCOPE(Vulkan_Bind);
|
||||||
|
|
||||||
@@ -551,112 +647,4 @@ std::string PipelineCache::GetTransferableDir() const {
|
|||||||
return GetVulkanDir() + DIR_SEP + "transferable";
|
return GetVulkanDir() + DIR_SEP + "transferable";
|
||||||
}
|
}
|
||||||
|
|
||||||
void PipelineCache::SwitchPipelineCache(u64 title_id, const std::atomic_bool& stop_loading,
|
|
||||||
const VideoCore::DiskResourceLoadCallback& callback) {
|
|
||||||
if (!Settings::values.use_disk_shader_cache || GetProgramID() == title_id) {
|
|
||||||
LOG_DEBUG(Render_Vulkan,
|
|
||||||
"Skipping pipeline cache switch - already using cache for title_id={:016X}",
|
|
||||||
title_id);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (callback) {
|
|
||||||
callback(VideoCore::LoadCallbackStage::Prepare, 0, 0, "");
|
|
||||||
}
|
|
||||||
if (callback) {
|
|
||||||
callback(VideoCore::LoadCallbackStage::Build, 0, 1, "Driver Pipeline Cache");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure we have a valid pipeline cache before switching
|
|
||||||
if (!pipeline_cache) {
|
|
||||||
vk::PipelineCacheCreateInfo cache_info{};
|
|
||||||
try {
|
|
||||||
pipeline_cache = instance.GetDevice().createPipelineCacheUnique(cache_info);
|
|
||||||
} catch (const vk::SystemError& err) {
|
|
||||||
LOG_ERROR(Render_Vulkan, "Failed to create pipeline cache: {}", err.what());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG_INFO(Render_Vulkan, "Switching pipeline cache to title_id={:016X}", title_id);
|
|
||||||
|
|
||||||
// Save current cache before switching
|
|
||||||
SaveDiskCache();
|
|
||||||
|
|
||||||
// Update program ID and load the new pipeline cache
|
|
||||||
SetProgramID(title_id);
|
|
||||||
LoadPipelineDiskCache(stop_loading, nullptr);
|
|
||||||
SwitchDiskCache(title_id, stop_loading, callback);
|
|
||||||
|
|
||||||
if (callback) {
|
|
||||||
callback(VideoCore::LoadCallbackStage::Complete, 0, 0, "");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PipelineCache::SwitchDiskCache(u64 title_id, const std::atomic_bool& stop_loading,
|
|
||||||
const VideoCore::DiskResourceLoadCallback& callback) {
|
|
||||||
// NOTE: curr_disk_cache can be null if emulation restarted without calling
|
|
||||||
// LoadDefaultDiskResources
|
|
||||||
|
|
||||||
// Check if the current cache is for the specified TID.
|
|
||||||
if (curr_disk_cache && curr_disk_cache->GetProgramID() == title_id) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Search for an existing manager
|
|
||||||
size_t new_pos = 0;
|
|
||||||
for (new_pos = 0; new_pos < disk_caches.size(); new_pos++) {
|
|
||||||
if (disk_caches[new_pos]->GetProgramID() == title_id) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Manager does not exist, create it and append to the end
|
|
||||||
if (new_pos >= disk_caches.size()) {
|
|
||||||
new_pos = disk_caches.size();
|
|
||||||
auto& new_manager =
|
|
||||||
disk_caches.emplace_back(std::make_shared<ShaderDiskCache>(*this, title_id));
|
|
||||||
|
|
||||||
if (callback) {
|
|
||||||
callback(VideoCore::LoadCallbackStage::Prepare, 0, 0, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
new_manager->Init(stop_loading, callback);
|
|
||||||
|
|
||||||
if (callback) {
|
|
||||||
callback(VideoCore::LoadCallbackStage::Complete, 0, 0, "");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto is_applet = [](u64 tid) {
|
|
||||||
constexpr u32 APPLET_TID_HIGH = 0x00040030;
|
|
||||||
return static_cast<u32>(tid >> 32) == APPLET_TID_HIGH;
|
|
||||||
};
|
|
||||||
|
|
||||||
bool prev_applet = curr_disk_cache ? is_applet(curr_disk_cache->GetProgramID()) : false;
|
|
||||||
bool new_applet = is_applet(disk_caches[new_pos]->GetProgramID());
|
|
||||||
curr_disk_cache = disk_caches[new_pos];
|
|
||||||
|
|
||||||
if (prev_applet) {
|
|
||||||
// If we came from an applet, clean up all other applets
|
|
||||||
for (auto it = disk_caches.begin(); it != disk_caches.end();) {
|
|
||||||
if (it == disk_caches.begin() || *it == curr_disk_cache ||
|
|
||||||
!is_applet((*it)->GetProgramID())) {
|
|
||||||
it++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
it = disk_caches.erase(it);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!new_applet) {
|
|
||||||
// If we are going into a non-applet, clean up everything
|
|
||||||
for (auto it = disk_caches.begin(); it != disk_caches.end();) {
|
|
||||||
if (it == disk_caches.begin() || *it == curr_disk_cache) {
|
|
||||||
it++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
it = disk_caches.erase(it);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace Vulkan
|
} // namespace Vulkan
|
||||||
|
|||||||
@@ -58,15 +58,13 @@ public:
|
|||||||
offsets[binding] = offset;
|
offsets[binding] = offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Loads the pipeline cache stored to disk
|
/// Loads the driver pipeline cache and the disk shader cache
|
||||||
void LoadPipelineDiskCache(const std::atomic_bool& stop_loading = std::atomic_bool{false},
|
void LoadCache(const std::atomic_bool& stop_loading = std::atomic_bool{false},
|
||||||
const VideoCore::DiskResourceLoadCallback& callback = {});
|
const VideoCore::DiskResourceLoadCallback& callback = {});
|
||||||
|
|
||||||
void LoadDiskCache(const std::atomic_bool& stop_loading = std::atomic_bool{false},
|
/// Switches the driver pipeline cache and the shader disk cache to the specified title
|
||||||
const VideoCore::DiskResourceLoadCallback& callback = {});
|
void SwitchCache(u64 title_id, const std::atomic_bool& stop_loading = std::atomic_bool{false},
|
||||||
|
const VideoCore::DiskResourceLoadCallback& callback = {});
|
||||||
/// Stores the generated pipeline cache to disk
|
|
||||||
void SaveDiskCache();
|
|
||||||
|
|
||||||
/// Binds a pipeline using the provided information
|
/// Binds a pipeline using the provided information
|
||||||
bool BindPipeline(PipelineInfo& info, bool wait_built = false);
|
bool BindPipeline(PipelineInfo& info, bool wait_built = false);
|
||||||
@@ -90,11 +88,6 @@ public:
|
|||||||
/// Binds a fragment shader generated from PICA state
|
/// Binds a fragment shader generated from PICA state
|
||||||
void UseFragmentShader(const Pica::RegsInternal& regs, const Pica::Shader::UserConfig& user);
|
void UseFragmentShader(const Pica::RegsInternal& regs, const Pica::Shader::UserConfig& user);
|
||||||
|
|
||||||
/// Switches the shader disk cache to the specified title
|
|
||||||
void SwitchPipelineCache(u64 title_id,
|
|
||||||
const std::atomic_bool& stop_loading = std::atomic_bool{false},
|
|
||||||
const VideoCore::DiskResourceLoadCallback& callback = {});
|
|
||||||
|
|
||||||
/// Gets the current program ID
|
/// Gets the current program ID
|
||||||
u64 GetProgramID() const {
|
u64 GetProgramID() const {
|
||||||
return current_program_id;
|
return current_program_id;
|
||||||
@@ -111,6 +104,17 @@ public:
|
|||||||
private:
|
private:
|
||||||
friend ShaderDiskCache;
|
friend ShaderDiskCache;
|
||||||
|
|
||||||
|
/// Loads the driver pipeline cache
|
||||||
|
void LoadDriverPipelineDiskCache(const std::atomic_bool& stop_loading = std::atomic_bool{false},
|
||||||
|
const VideoCore::DiskResourceLoadCallback& callback = {});
|
||||||
|
|
||||||
|
/// Stores the generated pipeline cache
|
||||||
|
void SaveDriverPipelineDiskCache();
|
||||||
|
|
||||||
|
/// Loads the shader disk cache
|
||||||
|
void LoadDiskCache(const std::atomic_bool& stop_loading = std::atomic_bool{false},
|
||||||
|
const VideoCore::DiskResourceLoadCallback& callback = {});
|
||||||
|
|
||||||
/// Switches the disk cache at runtime to use a different title ID
|
/// Switches the disk cache at runtime to use a different title ID
|
||||||
void SwitchDiskCache(u64 title_id, const std::atomic_bool& stop_loading,
|
void SwitchDiskCache(u64 title_id, const std::atomic_bool& stop_loading,
|
||||||
const VideoCore::DiskResourceLoadCallback& callback);
|
const VideoCore::DiskResourceLoadCallback& callback);
|
||||||
@@ -140,7 +144,7 @@ private:
|
|||||||
DescriptorUpdateQueue& update_queue;
|
DescriptorUpdateQueue& update_queue;
|
||||||
|
|
||||||
Pica::Shader::Profile profile{};
|
Pica::Shader::Profile profile{};
|
||||||
vk::UniquePipelineCache pipeline_cache;
|
vk::UniquePipelineCache driver_pipeline_cache;
|
||||||
vk::UniquePipelineLayout pipeline_layout;
|
vk::UniquePipelineLayout pipeline_layout;
|
||||||
std::size_t num_worker_threads;
|
std::size_t num_worker_threads;
|
||||||
Common::ThreadWorker workers;
|
Common::ThreadWorker workers;
|
||||||
|
|||||||
@@ -152,10 +152,13 @@ void RasterizerVulkan::LoadDefaultDiskResources(
|
|||||||
program_id = 0;
|
program_id = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (callback) {
|
||||||
|
callback(VideoCore::LoadCallbackStage::Prepare, 0, 0, "");
|
||||||
|
}
|
||||||
|
|
||||||
pipeline_cache.SetProgramID(program_id);
|
pipeline_cache.SetProgramID(program_id);
|
||||||
pipeline_cache.SetAccurateMul(accurate_mul);
|
pipeline_cache.SetAccurateMul(accurate_mul);
|
||||||
pipeline_cache.LoadPipelineDiskCache(stop_loading, callback);
|
pipeline_cache.LoadCache(stop_loading, callback);
|
||||||
pipeline_cache.LoadDiskCache(stop_loading, callback);
|
|
||||||
|
|
||||||
if (callback) {
|
if (callback) {
|
||||||
callback(VideoCore::LoadCallbackStage::Complete, 0, 0, "");
|
callback(VideoCore::LoadCallbackStage::Complete, 0, 0, "");
|
||||||
@@ -985,8 +988,17 @@ void RasterizerVulkan::UploadUniforms(bool accelerate_draw) {
|
|||||||
|
|
||||||
void RasterizerVulkan::SwitchDiskResources(u64 title_id) {
|
void RasterizerVulkan::SwitchDiskResources(u64 title_id) {
|
||||||
std::atomic_bool stop_loading = false;
|
std::atomic_bool stop_loading = false;
|
||||||
|
|
||||||
|
if (switch_disk_resources_callback) {
|
||||||
|
switch_disk_resources_callback(VideoCore::LoadCallbackStage::Prepare, 0, 0, "");
|
||||||
|
}
|
||||||
|
|
||||||
pipeline_cache.SetAccurateMul(accurate_mul);
|
pipeline_cache.SetAccurateMul(accurate_mul);
|
||||||
pipeline_cache.SwitchPipelineCache(title_id, stop_loading, switch_disk_resources_callback);
|
pipeline_cache.SwitchCache(title_id, stop_loading, switch_disk_resources_callback);
|
||||||
|
|
||||||
|
if (switch_disk_resources_callback) {
|
||||||
|
switch_disk_resources_callback(VideoCore::LoadCallbackStage::Complete, 0, 0, "");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Vulkan
|
} // namespace Vulkan
|
||||||
|
|||||||
@@ -59,6 +59,9 @@ static VideoCore::DiskResourceLoadCallback MakeThrottledCallback(
|
|||||||
void ShaderDiskCache::Init(const std::atomic_bool& stop_loading,
|
void ShaderDiskCache::Init(const std::atomic_bool& stop_loading,
|
||||||
const VideoCore::DiskResourceLoadCallback& callback) {
|
const VideoCore::DiskResourceLoadCallback& callback) {
|
||||||
|
|
||||||
|
if (!Settings::values.use_disk_shader_cache)
|
||||||
|
return;
|
||||||
|
|
||||||
auto new_callback = MakeThrottledCallback(callback);
|
auto new_callback = MakeThrottledCallback(callback);
|
||||||
|
|
||||||
if (!stop_loading && !InitVSCache(stop_loading, new_callback)) {
|
if (!stop_loading && !InitVSCache(stop_loading, new_callback)) {
|
||||||
@@ -248,7 +251,7 @@ GraphicsPipeline* ShaderDiskCache::GetPipeline(const PipelineInfo& info) {
|
|||||||
parent.UseTrivialGeometryShader();
|
parent.UseTrivialGeometryShader();
|
||||||
}
|
}
|
||||||
it.value() = std::make_unique<GraphicsPipeline>(
|
it.value() = std::make_unique<GraphicsPipeline>(
|
||||||
parent.instance, parent.renderpass_cache, info, *parent.pipeline_cache,
|
parent.instance, parent.renderpass_cache, info, *parent.driver_pipeline_cache,
|
||||||
*parent.pipeline_layout, parent.current_shaders, &parent.workers);
|
*parent.pipeline_layout, parent.current_shaders, &parent.workers);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -339,86 +342,129 @@ ShaderDiskCache::CacheEntry ShaderDiskCache::CacheFile::ReadAt(size_t position)
|
|||||||
}
|
}
|
||||||
|
|
||||||
size_t ShaderDiskCache::CacheFile::GetTotalEntries() {
|
size_t ShaderDiskCache::CacheFile::GetTotalEntries() {
|
||||||
if (biggest_entry_id != SIZE_MAX) {
|
if (!file.IsGood()) {
|
||||||
return biggest_entry_id + 1;
|
next_entry_id = SIZE_MAX;
|
||||||
|
return next_entry_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (next_entry_id != SIZE_MAX) {
|
||||||
|
return next_entry_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
const size_t file_size = file.GetSize();
|
const size_t file_size = file.GetSize();
|
||||||
|
if (file_size == 0) {
|
||||||
|
next_entry_id = 0;
|
||||||
|
return next_entry_id;
|
||||||
|
}
|
||||||
|
|
||||||
CacheEntry::CacheEntryFooter footer{};
|
CacheEntry::CacheEntryFooter footer{};
|
||||||
|
|
||||||
if (file.ReadAtArray(&footer, 1, file_size - sizeof(footer)) == sizeof(footer) &&
|
if (file.ReadAtArray(&footer, 1, file_size - sizeof(footer)) == sizeof(footer) &&
|
||||||
footer.version == CacheEntry::CacheEntryFooter::ENTRY_VERSION) {
|
footer.version == CacheEntry::CacheEntryFooter::ENTRY_VERSION) {
|
||||||
biggest_entry_id = footer.entry_id;
|
next_entry_id = footer.entry_id + 1;
|
||||||
|
} else {
|
||||||
|
return SIZE_MAX;
|
||||||
}
|
}
|
||||||
|
|
||||||
return biggest_entry_id + 1;
|
return next_entry_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ShaderDiskCache::CacheFile::Append(CacheEntryType type, u64 id, std::span<const u8> data,
|
void ShaderDiskCache::CacheFile::Append(CacheEntryType type, u64 id, std::span<const u8> data,
|
||||||
bool compress) {
|
bool compress) {
|
||||||
std::scoped_lock lock(mutex);
|
if (curr_mode != CacheOpMode::APPEND) {
|
||||||
|
return;
|
||||||
std::span<const u8> data_final;
|
|
||||||
std::vector<u8> data_compress;
|
|
||||||
|
|
||||||
CacheEntry::CacheEntryHeader header{};
|
|
||||||
CacheEntry::CacheEntryFooter footer{};
|
|
||||||
|
|
||||||
constexpr u32 headers_size =
|
|
||||||
sizeof(CacheEntry::CacheEntryHeader) + sizeof(CacheEntry::CacheEntryFooter);
|
|
||||||
|
|
||||||
if (compress) {
|
|
||||||
data_compress = Common::Compression::CompressDataZSTDDefault(data);
|
|
||||||
data_final = data_compress;
|
|
||||||
header.zstd_compressed.Assign(true);
|
|
||||||
} else {
|
|
||||||
data_final = data;
|
|
||||||
}
|
}
|
||||||
header.entry_version = CacheEntry::CacheEntryHeader::ENTRY_VERSION;
|
|
||||||
footer.version.Assign(CacheEntry::CacheEntryFooter::ENTRY_VERSION);
|
|
||||||
header.entry_size = footer.entry_size = data_final.size() + headers_size;
|
|
||||||
footer.entry_id.Assign(biggest_entry_id++);
|
|
||||||
|
|
||||||
header.type = type;
|
std::vector<u8> copy_data(data.begin(), data.end());
|
||||||
header.id = id;
|
|
||||||
|
|
||||||
std::vector<u8> out_data(data_final.size() + headers_size);
|
append_worker.QueueWork([this, type, id, compress, data = std::move(copy_data)]() {
|
||||||
memcpy(out_data.data(), &header, sizeof(header));
|
if (next_entry_id == SIZE_MAX || !file.IsGood()) {
|
||||||
memcpy(out_data.data() + sizeof(header), data_final.data(), data_final.size());
|
return;
|
||||||
memcpy(out_data.data() + sizeof(header) + data_final.size(), &footer, sizeof(footer));
|
}
|
||||||
|
|
||||||
return file.WriteBytes(out_data.data(), out_data.size()) == out_data.size();
|
std::span<const u8> data_final;
|
||||||
|
std::vector<u8> data_compress;
|
||||||
|
|
||||||
|
CacheEntry::CacheEntryHeader header{};
|
||||||
|
CacheEntry::CacheEntryFooter footer{};
|
||||||
|
|
||||||
|
constexpr u32 headers_size =
|
||||||
|
sizeof(CacheEntry::CacheEntryHeader) + sizeof(CacheEntry::CacheEntryFooter);
|
||||||
|
|
||||||
|
if (compress) {
|
||||||
|
data_compress = Common::Compression::CompressDataZSTDDefault(data);
|
||||||
|
data_final = data_compress;
|
||||||
|
header.zstd_compressed.Assign(true);
|
||||||
|
} else {
|
||||||
|
data_final = data;
|
||||||
|
}
|
||||||
|
header.entry_version = CacheEntry::CacheEntryHeader::ENTRY_VERSION;
|
||||||
|
footer.version.Assign(CacheEntry::CacheEntryFooter::ENTRY_VERSION);
|
||||||
|
header.entry_size = footer.entry_size = data_final.size() + headers_size;
|
||||||
|
footer.entry_id.Assign(next_entry_id++);
|
||||||
|
|
||||||
|
header.type = type;
|
||||||
|
header.id = id;
|
||||||
|
|
||||||
|
std::vector<u8> out_data(data_final.size() + headers_size);
|
||||||
|
memcpy(out_data.data(), &header, sizeof(header));
|
||||||
|
memcpy(out_data.data() + sizeof(header), data_final.data(), data_final.size());
|
||||||
|
memcpy(out_data.data() + sizeof(header) + data_final.size(), &footer, sizeof(footer));
|
||||||
|
|
||||||
|
file.WriteBytes(out_data.data(), out_data.size());
|
||||||
|
if (file.IsGood()) {
|
||||||
|
file.Flush();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ShaderDiskCache::CacheFile::SwitchMode(CacheOpMode mode) {
|
bool ShaderDiskCache::CacheFile::SwitchMode(CacheOpMode mode) {
|
||||||
|
if (curr_mode == mode) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (curr_mode == CacheOpMode::APPEND) {
|
||||||
|
append_worker.WaitForRequests();
|
||||||
|
}
|
||||||
|
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case CacheOpMode::READ: {
|
case CacheOpMode::READ: {
|
||||||
|
next_entry_id = SIZE_MAX; // Force reading entries agains
|
||||||
file = FileUtil::IOFile(filepath, "rb");
|
file = FileUtil::IOFile(filepath, "rb");
|
||||||
bool is_open = file.IsOpen();
|
bool is_open = file.IsGood();
|
||||||
if (is_open) {
|
if (is_open) {
|
||||||
GetTotalEntries();
|
GetTotalEntries();
|
||||||
}
|
}
|
||||||
|
curr_mode = mode;
|
||||||
return is_open;
|
return is_open;
|
||||||
}
|
}
|
||||||
case CacheOpMode::APPEND: {
|
case CacheOpMode::APPEND: {
|
||||||
GetTotalEntries();
|
if (!SwitchMode(CacheOpMode::READ)) {
|
||||||
|
curr_mode = mode;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
file.Close();
|
file.Close();
|
||||||
if (biggest_entry_id == SIZE_MAX) {
|
curr_mode = mode;
|
||||||
|
if (next_entry_id == SIZE_MAX) {
|
||||||
// Cannot append if getting total items fails
|
// Cannot append if getting total items fails
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
file = FileUtil::IOFile(filepath, "ab");
|
file = FileUtil::IOFile(filepath, "ab");
|
||||||
return file.IsOpen();
|
return file.IsGood();
|
||||||
}
|
}
|
||||||
case CacheOpMode::DELETE: {
|
case CacheOpMode::DELETE: {
|
||||||
biggest_entry_id = 0;
|
next_entry_id = SIZE_MAX;
|
||||||
file.Close();
|
file.Close();
|
||||||
FileUtil::Delete(filepath);
|
curr_mode = mode;
|
||||||
return true;
|
return FileUtil::Delete(filepath);
|
||||||
}
|
}
|
||||||
case CacheOpMode::RECREATE: {
|
case CacheOpMode::RECREATE: {
|
||||||
SwitchMode(CacheOpMode::DELETE);
|
if (!SwitchMode(CacheOpMode::DELETE)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!FileUtil::CreateEmptyFile(filepath)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return SwitchMode(CacheOpMode::APPEND);
|
return SwitchMode(CacheOpMode::APPEND);
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@@ -827,8 +873,7 @@ bool ShaderDiskCache::InitVSCache(const std::atomic_bool& stop_loading,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Switch to append mode to receive new entries.
|
// Switch to append mode to receive new entries.
|
||||||
vs_cache.SwitchMode(CacheFile::CacheOpMode::APPEND);
|
return vs_cache.SwitchMode(CacheFile::CacheOpMode::APPEND);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ShaderDiskCache::InitFSCache(const std::atomic_bool& stop_loading,
|
bool ShaderDiskCache::InitFSCache(const std::atomic_bool& stop_loading,
|
||||||
@@ -1045,8 +1090,7 @@ bool ShaderDiskCache::InitFSCache(const std::atomic_bool& stop_loading,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Switch to append mode to receive new entries.
|
// Switch to append mode to receive new entries.
|
||||||
fs_cache.SwitchMode(CacheFile::CacheOpMode::APPEND);
|
return fs_cache.SwitchMode(CacheFile::CacheOpMode::APPEND);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ShaderDiskCache::InitGSCache(const std::atomic_bool& stop_loading,
|
bool ShaderDiskCache::InitGSCache(const std::atomic_bool& stop_loading,
|
||||||
@@ -1272,8 +1316,7 @@ bool ShaderDiskCache::InitGSCache(const std::atomic_bool& stop_loading,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Switch to append mode to receive new entries.
|
// Switch to append mode to receive new entries.
|
||||||
gs_cache.SwitchMode(CacheFile::CacheOpMode::APPEND);
|
return gs_cache.SwitchMode(CacheFile::CacheOpMode::APPEND);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ShaderDiskCache::InitPLCache(const std::atomic_bool& stop_loading,
|
bool ShaderDiskCache::InitPLCache(const std::atomic_bool& stop_loading,
|
||||||
@@ -1400,7 +1443,7 @@ bool ShaderDiskCache::InitPLCache(const std::atomic_bool& stop_loading,
|
|||||||
|
|
||||||
auto [it_pl, _] = graphics_pipelines.try_emplace(pl_hash_opt);
|
auto [it_pl, _] = graphics_pipelines.try_emplace(pl_hash_opt);
|
||||||
it_pl.value() = std::make_unique<GraphicsPipeline>(
|
it_pl.value() = std::make_unique<GraphicsPipeline>(
|
||||||
parent.instance, parent.renderpass_cache, info, *parent.pipeline_cache,
|
parent.instance, parent.renderpass_cache, info, *parent.driver_pipeline_cache,
|
||||||
*parent.pipeline_layout, shaders, &parent.workers);
|
*parent.pipeline_layout, shaders, &parent.workers);
|
||||||
|
|
||||||
it_pl.value()->TryBuild(false);
|
it_pl.value()->TryBuild(false);
|
||||||
@@ -1413,11 +1456,10 @@ bool ShaderDiskCache::InitPLCache(const std::atomic_bool& stop_loading,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Switch to append mode to receive new entries.
|
// Switch to append mode to receive new entries.
|
||||||
pl_cache.SwitchMode(CacheFile::CacheOpMode::APPEND);
|
return pl_cache.SwitchMode(CacheFile::CacheOpMode::APPEND);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ShaderDiskCache::AppendVSConfigProgram(CacheFile& file,
|
void ShaderDiskCache::AppendVSConfigProgram(CacheFile& file,
|
||||||
const Pica::Shader::Generator::PicaVSConfig& config,
|
const Pica::Shader::Generator::PicaVSConfig& config,
|
||||||
const Pica::ShaderSetup& setup, u64 config_id,
|
const Pica::ShaderSetup& setup, u64 config_id,
|
||||||
u64 spirv_id) {
|
u64 spirv_id) {
|
||||||
@@ -1430,7 +1472,6 @@ bool ShaderDiskCache::AppendVSConfigProgram(CacheFile& file,
|
|||||||
Common::HashCombine(config.state.program_hash, config.state.swizzle_hash);
|
Common::HashCombine(config.state.program_hash, config.state.swizzle_hash);
|
||||||
|
|
||||||
bool new_entry = known_vertex_programs.emplace(entry.program_entry_id).second;
|
bool new_entry = known_vertex_programs.emplace(entry.program_entry_id).second;
|
||||||
bool prog_res = true;
|
|
||||||
if (new_entry) {
|
if (new_entry) {
|
||||||
std::unique_ptr<VSProgramEntry> prog_entry = std::make_unique<VSProgramEntry>();
|
std::unique_ptr<VSProgramEntry> prog_entry = std::make_unique<VSProgramEntry>();
|
||||||
prog_entry->version = VSProgramEntry::EXPECTED_VERSION;
|
prog_entry->version = VSProgramEntry::EXPECTED_VERSION;
|
||||||
@@ -1439,49 +1480,46 @@ bool ShaderDiskCache::AppendVSConfigProgram(CacheFile& file,
|
|||||||
prog_entry->swizzle_len = setup.GetBiggestSwizzleSize();
|
prog_entry->swizzle_len = setup.GetBiggestSwizzleSize();
|
||||||
prog_entry->swizzle_code = setup.GetSwizzleData();
|
prog_entry->swizzle_code = setup.GetSwizzleData();
|
||||||
|
|
||||||
prog_res = AppendVSProgram(file, *prog_entry, entry.program_entry_id);
|
AppendVSProgram(file, *prog_entry, entry.program_entry_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
return AppendVSConfig(file, entry, config_id) && prog_res;
|
AppendVSConfig(file, entry, config_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ShaderDiskCache::AppendVSProgram(CacheFile& file, const VSProgramEntry& entry,
|
void ShaderDiskCache::AppendVSProgram(CacheFile& file, const VSProgramEntry& entry,
|
||||||
u64 program_id) {
|
u64 program_id) {
|
||||||
return file.Append(CacheEntryType::VS_PROGRAM, program_id, entry, true);
|
file.Append(CacheEntryType::VS_PROGRAM, program_id, entry, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ShaderDiskCache::AppendVSConfig(CacheFile& file, const VSConfigEntry& entry, u64 config_id) {
|
void ShaderDiskCache::AppendVSConfig(CacheFile& file, const VSConfigEntry& entry, u64 config_id) {
|
||||||
return file.Append(CacheEntryType::VS_CONFIG, config_id, entry, true);
|
file.Append(CacheEntryType::VS_CONFIG, config_id, entry, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ShaderDiskCache::AppendVSSPIRV(CacheFile& file, std::span<const u32> program, u64 program_id) {
|
void ShaderDiskCache::AppendVSSPIRV(CacheFile& file, std::span<const u32> program, u64 program_id) {
|
||||||
return file.Append(CacheEntryType::VS_SPIRV, program_id,
|
file.Append(CacheEntryType::VS_SPIRV, program_id,
|
||||||
{reinterpret_cast<const u8*>(program.data()), program.size() * sizeof(u32)},
|
{reinterpret_cast<const u8*>(program.data()), program.size() * sizeof(u32)}, true);
|
||||||
true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ShaderDiskCache::AppendFSConfig(CacheFile& file, const FSConfigEntry& entry, u64 config_id) {
|
void ShaderDiskCache::AppendFSConfig(CacheFile& file, const FSConfigEntry& entry, u64 config_id) {
|
||||||
return file.Append(CacheEntryType::FS_CONFIG, config_id, entry, true);
|
file.Append(CacheEntryType::FS_CONFIG, config_id, entry, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ShaderDiskCache::AppendFSSPIRV(CacheFile& file, std::span<const u32> program, u64 program_id) {
|
void ShaderDiskCache::AppendFSSPIRV(CacheFile& file, std::span<const u32> program, u64 program_id) {
|
||||||
return file.Append(CacheEntryType::FS_SPIRV, program_id,
|
file.Append(CacheEntryType::FS_SPIRV, program_id,
|
||||||
{reinterpret_cast<const u8*>(program.data()), program.size() * sizeof(u32)},
|
{reinterpret_cast<const u8*>(program.data()), program.size() * sizeof(u32)}, true);
|
||||||
true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ShaderDiskCache::AppendGSConfig(CacheFile& file, const GSConfigEntry& entry, u64 config_id) {
|
void ShaderDiskCache::AppendGSConfig(CacheFile& file, const GSConfigEntry& entry, u64 config_id) {
|
||||||
return file.Append(CacheEntryType::GS_CONFIG, config_id, entry, true);
|
file.Append(CacheEntryType::GS_CONFIG, config_id, entry, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ShaderDiskCache::AppendGSSPIRV(CacheFile& file, std::span<const u32> program, u64 program_id) {
|
void ShaderDiskCache::AppendGSSPIRV(CacheFile& file, std::span<const u32> program, u64 program_id) {
|
||||||
return file.Append(CacheEntryType::GS_SPIRV, program_id,
|
file.Append(CacheEntryType::GS_SPIRV, program_id,
|
||||||
{reinterpret_cast<const u8*>(program.data()), program.size() * sizeof(u32)},
|
{reinterpret_cast<const u8*>(program.data()), program.size() * sizeof(u32)}, true);
|
||||||
true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ShaderDiskCache::AppendPLConfig(CacheFile& file, const PLConfigEntry& entry, u64 config_id) {
|
void ShaderDiskCache::AppendPLConfig(CacheFile& file, const PLConfigEntry& entry, u64 config_id) {
|
||||||
return file.Append(CacheEntryType::PL_CONFIG, config_id, entry, true);
|
file.Append(CacheEntryType::PL_CONFIG, config_id, entry, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Vulkan
|
} // namespace Vulkan
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/file_util.h"
|
#include "common/file_util.h"
|
||||||
|
#include "common/thread_worker.h"
|
||||||
#include "video_core/pica/shader_setup.h"
|
#include "video_core/pica/shader_setup.h"
|
||||||
#include "video_core/rasterizer_interface.h"
|
#include "video_core/rasterizer_interface.h"
|
||||||
#include "video_core/renderer_vulkan/vk_graphics_pipeline.h"
|
#include "video_core/renderer_vulkan/vk_graphics_pipeline.h"
|
||||||
@@ -241,6 +242,7 @@ private:
|
|||||||
class CacheFile {
|
class CacheFile {
|
||||||
public:
|
public:
|
||||||
enum class CacheOpMode {
|
enum class CacheOpMode {
|
||||||
|
NONE = 0,
|
||||||
READ,
|
READ,
|
||||||
APPEND,
|
APPEND,
|
||||||
DELETE,
|
DELETE,
|
||||||
@@ -249,6 +251,9 @@ private:
|
|||||||
|
|
||||||
CacheFile() = default;
|
CacheFile() = default;
|
||||||
CacheFile(const std::string& _filepath) : filepath(_filepath) {}
|
CacheFile(const std::string& _filepath) : filepath(_filepath) {}
|
||||||
|
~CacheFile() {
|
||||||
|
append_worker.WaitForRequests();
|
||||||
|
}
|
||||||
|
|
||||||
void SetFilePath(const std::string& path) {
|
void SetFilePath(const std::string& path) {
|
||||||
filepath = path;
|
filepath = path;
|
||||||
@@ -268,24 +273,25 @@ private:
|
|||||||
size_t GetTotalEntries();
|
size_t GetTotalEntries();
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
bool Append(CacheEntryType type, u64 id, const T& object, bool compress) {
|
void Append(CacheEntryType type, u64 id, const T& object, bool compress) {
|
||||||
static_assert(std::is_trivially_copyable_v<T>);
|
static_assert(std::is_trivially_copyable_v<T>);
|
||||||
|
|
||||||
auto bytes = std::as_bytes(std::span{&object, 1});
|
auto bytes = std::as_bytes(std::span{&object, 1});
|
||||||
auto u8_span =
|
auto u8_span =
|
||||||
std::span<const u8>(reinterpret_cast<const u8*>(bytes.data()), bytes.size());
|
std::span<const u8>(reinterpret_cast<const u8*>(bytes.data()), bytes.size());
|
||||||
return Append(type, id, u8_span, compress);
|
Append(type, id, u8_span, compress);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Append(CacheEntryType type, u64 id, std::span<const u8> data, bool compress);
|
void Append(CacheEntryType type, u64 id, std::span<const u8> data, bool compress);
|
||||||
|
|
||||||
bool SwitchMode(CacheOpMode mode);
|
bool SwitchMode(CacheOpMode mode);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
CacheOpMode curr_mode = CacheOpMode::NONE;
|
||||||
std::string filepath;
|
std::string filepath;
|
||||||
std::mutex mutex;
|
|
||||||
FileUtil::IOFile file{};
|
FileUtil::IOFile file{};
|
||||||
size_t biggest_entry_id = SIZE_MAX;
|
std::atomic<size_t> next_entry_id = SIZE_MAX;
|
||||||
|
Common::ThreadWorker append_worker{1, "Disk Shader Cache Append Worker"};
|
||||||
};
|
};
|
||||||
|
|
||||||
std::string GetVSFile(u64 title_id, bool is_temp) const;
|
std::string GetVSFile(u64 title_id, bool is_temp) const;
|
||||||
@@ -307,19 +313,19 @@ private:
|
|||||||
bool InitPLCache(const std::atomic_bool& stop_loading,
|
bool InitPLCache(const std::atomic_bool& stop_loading,
|
||||||
const VideoCore::DiskResourceLoadCallback& callback);
|
const VideoCore::DiskResourceLoadCallback& callback);
|
||||||
|
|
||||||
bool AppendVSConfigProgram(CacheFile& file, const Pica::Shader::Generator::PicaVSConfig& config,
|
void AppendVSConfigProgram(CacheFile& file, const Pica::Shader::Generator::PicaVSConfig& config,
|
||||||
const Pica::ShaderSetup& setup, u64 config_id, u64 program_id);
|
const Pica::ShaderSetup& setup, u64 config_id, u64 program_id);
|
||||||
bool AppendVSProgram(CacheFile& file, const VSProgramEntry& entry, u64 program_id);
|
void AppendVSProgram(CacheFile& file, const VSProgramEntry& entry, u64 program_id);
|
||||||
bool AppendVSConfig(CacheFile& file, const VSConfigEntry& entry, u64 config_id);
|
void AppendVSConfig(CacheFile& file, const VSConfigEntry& entry, u64 config_id);
|
||||||
bool AppendVSSPIRV(CacheFile& file, std::span<const u32> program, u64 program_id);
|
void AppendVSSPIRV(CacheFile& file, std::span<const u32> program, u64 program_id);
|
||||||
|
|
||||||
bool AppendFSConfig(CacheFile& file, const FSConfigEntry& entry, u64 config_id);
|
void AppendFSConfig(CacheFile& file, const FSConfigEntry& entry, u64 config_id);
|
||||||
bool AppendFSSPIRV(CacheFile& file, std::span<const u32> program, u64 program_id);
|
void AppendFSSPIRV(CacheFile& file, std::span<const u32> program, u64 program_id);
|
||||||
|
|
||||||
bool AppendGSConfig(CacheFile& file, const GSConfigEntry& entry, u64 config_id);
|
void AppendGSConfig(CacheFile& file, const GSConfigEntry& entry, u64 config_id);
|
||||||
bool AppendGSSPIRV(CacheFile& file, std::span<const u32> program, u64 program_id);
|
void AppendGSSPIRV(CacheFile& file, std::span<const u32> program, u64 program_id);
|
||||||
|
|
||||||
bool AppendPLConfig(CacheFile& file, const PLConfigEntry& entry, u64 config_id);
|
void AppendPLConfig(CacheFile& file, const PLConfigEntry& entry, u64 config_id);
|
||||||
|
|
||||||
CacheFile vs_cache;
|
CacheFile vs_cache;
|
||||||
CacheFile fs_cache;
|
CacheFile fs_cache;
|
||||||
|
|||||||
Reference in New Issue
Block a user