mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-10-31 09:28:29 +00:00
pipeline: create shader-store pipelines at boot on half the hardware threads
This commit is contained in:
1 parent
14837b0479
commit
ec746622c2
3 files changed
+58
-16
No files matched your search
@@ -0,0 +1,35 @@
|
||||
#ifndef KYTY_COMMON_PARALLELFOR_H_
|
||||
#define KYTY_COMMON_PARALLELFOR_H_
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <cstddef>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
namespace Common {
|
||||
|
||||
// Half the hardware threads, at least one.
|
||||
inline size_t ParallelThreads() {
|
||||
return std::max(1u, std::thread::hardware_concurrency() / 2);
|
||||
}
|
||||
|
||||
// Calls job(i) for every i below count on up to ParallelThreads() threads, the caller included.
|
||||
template <typename Job>
|
||||
void ParallelFor(size_t count, const Job& job) {
|
||||
std::atomic_size_t next = 0;
|
||||
const auto work = [&] {
|
||||
for (auto i = next++; i < count; i = next++) {
|
||||
job(i);
|
||||
}
|
||||
};
|
||||
std::vector<std::jthread> helpers;
|
||||
for (auto i = std::min(count, ParallelThreads()); i > 1; i--) {
|
||||
helpers.emplace_back(work);
|
||||
}
|
||||
work();
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
|
||||
#endif // KYTY_COMMON_PARALLELFOR_H_
|
||||
@@ -262,7 +262,7 @@ private:
|
||||
|
||||
// pipelineSerialization.cpp
|
||||
void WarmUp(const std::filesystem::path& path);
|
||||
bool LoadRecord(std::span<const uint8_t>& records, std::vector<GraphicsPipelineKey>& pipelines);
|
||||
bool LoadRecord(std::span<const uint8_t>& records);
|
||||
void StoreModule(const ProgramKey& key, const Program& program, const ShaderModule& module,
|
||||
const std::vector<uint32_t>& spirv);
|
||||
void StorePipeline(const GraphicsPipelineKey& key);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "graphics/host_gpu/renderer/pipeline/pipelineCache.h"
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "common/parallelFor.h"
|
||||
#include "common/stringUtils.h"
|
||||
#include "graphics/host_gpu/vulkanCommon.h"
|
||||
#include "graphics/shader/recompiler/ShaderRecompiler.h"
|
||||
@@ -153,35 +154,41 @@ void PipelineCache::WarmUp(const std::filesystem::path& path) {
|
||||
file.Read(bytes.data(), static_cast<uint32_t>(bytes.size()), &read);
|
||||
bytes.resize(read);
|
||||
}
|
||||
Log::WriteToConsoleAndLog(
|
||||
fmt::format("Shader cache: compiling shaders and pipelines from {} ({:.1f} MB)\n",
|
||||
Common::PathToString(path), static_cast<double>(bytes.size()) / 1'000'000.0));
|
||||
std::vector<GraphicsPipelineKey> pipelines;
|
||||
Log::WriteToConsoleAndLog(fmt::format(
|
||||
"Shader cache: compiling shaders and pipelines from {} ({:.1f} MB) with {} threads\n",
|
||||
Common::PathToString(path), static_cast<double>(bytes.size()) / 1'000'000.0,
|
||||
Common::ParallelThreads()));
|
||||
auto records = std::span<const uint8_t>(bytes);
|
||||
const bool reusable =
|
||||
records.size() >= header.size() && std::ranges::equal(records.first(header.size()), header);
|
||||
if (reusable) {
|
||||
records = records.subspan(header.size());
|
||||
while (LoadRecord(records, pipelines)) {
|
||||
while (LoadRecord(records)) {
|
||||
}
|
||||
}
|
||||
// A damaged record ends the store; the records before it stay.
|
||||
m_store.Open(path, header, reusable ? bytes.size() - records.size() : 0);
|
||||
|
||||
const auto modules = MapModules();
|
||||
for (const auto& key: pipelines) {
|
||||
Pipeline pipeline;
|
||||
if (CreateGraphicsPipeline(pipeline, key, modules)) {
|
||||
m_graphics_pipelines.emplace(key, pipeline);
|
||||
}
|
||||
std::vector<Common::UniqueFunction<void>> jobs;
|
||||
jobs.reserve(m_graphics_pipelines.size() + modules.size());
|
||||
for (auto& node: m_graphics_pipelines) {
|
||||
jobs.emplace_back([this, &modules, &node] {
|
||||
CreateGraphicsPipeline(node.second, node.first, modules);
|
||||
});
|
||||
}
|
||||
// A compute pipeline depends only on its module.
|
||||
for (const auto* module: modules | std::views::values) {
|
||||
if (module != nullptr && module->info.stage == ShaderType::Compute) {
|
||||
CreatePipelineInternal(m_graphics, m_compute_pipelines[module->id], *module,
|
||||
m_driver_cache);
|
||||
jobs.emplace_back([this, module, &pipeline = m_compute_pipelines[module->id]] {
|
||||
CreatePipelineInternal(m_graphics, pipeline, *module, m_driver_cache);
|
||||
});
|
||||
}
|
||||
}
|
||||
Common::ParallelFor(jobs.size(), [&](size_t i) { jobs[i](); });
|
||||
// A key with a stage missing from the store keeps no pipeline.
|
||||
std::erase_if(m_graphics_pipelines,
|
||||
[](const auto& entry) { return entry.second.pipeline == nullptr; });
|
||||
if (!m_graphics_pipelines.empty() || !m_compute_pipelines.empty()) {
|
||||
CheckpointDriverCache();
|
||||
}
|
||||
@@ -194,8 +201,7 @@ void PipelineCache::WarmUp(const std::filesystem::path& path) {
|
||||
.count()));
|
||||
}
|
||||
|
||||
bool PipelineCache::LoadRecord(std::span<const uint8_t>& records,
|
||||
std::vector<GraphicsPipelineKey>& pipelines) {
|
||||
bool PipelineCache::LoadRecord(std::span<const uint8_t>& records) {
|
||||
Frame frame;
|
||||
if (records.size() < sizeof(frame)) {
|
||||
return false;
|
||||
@@ -249,7 +255,8 @@ bool PipelineCache::LoadRecord(std::span<const uint8_t>& records,
|
||||
if (!complete()) {
|
||||
return false;
|
||||
}
|
||||
pipelines.push_back(key);
|
||||
// WarmUp creates the pipeline once every record is loaded.
|
||||
m_graphics_pipelines.try_emplace(key);
|
||||
break;
|
||||
}
|
||||
case Record::TessellationLayout: {
|
||||
|
||||
Reference in new issue
Block a user