Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1839,7 +1839,7 @@ VulkanDevice::VulkanDevice(const PresentationWindow* window, bool compileOnly) :
Graphics::PrepareImportWatch(graphicsContext());
state->bufferPool = std::make_shared<Graphics::BufferPool>(graphicsContext());
state->emptyBuffer = std::make_unique<Graphics::Buffer>(graphicsContext(), Graphics::EmptyBufferBytes, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT);
state->pipelineCache = std::make_unique<Graphics::PipelineCache>(graphicsContext(), state->properties);
state->pipelineCache = std::make_unique<Graphics::PipelineCache>(graphicsContext(), state->properties, window != nullptr || compileOnly);
state->detiler = std::make_unique<Graphics::TextureDetiler>(graphicsContext());
state->textureCache = std::make_unique<Graphics::TextureCache>(graphicsContext());
state->colorTransfer = std::make_unique<Graphics::GpuColorTransfer>(graphicsContext());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,14 @@ class PipelineCache {
public:
static constexpr int SaveIntervalSeconds = 10;

PipelineCache(const Context& context, const VkPhysicalDeviceProperties& properties);
PipelineCache(const Context& context, const VkPhysicalDeviceProperties& properties, bool loadFromFile = true);
~PipelineCache();

PipelineCache(const PipelineCache&) = delete;
PipelineCache& operator=(const PipelineCache&) = delete;
VkPipelineCache Handle() const { return cache; }
PipelineCacheSynchronization& Synchronization() { return synchronization; }
void SaveNow();

private:
struct FileHeader {
Expand Down Expand Up @@ -54,6 +55,7 @@ class PipelineCache {
VkPipelineCache cache = VK_NULL_HANDLE;
PipelineCacheSynchronization synchronization;
std::filesystem::path path;
bool holdsFile = false;
std::size_t loadedBytes = 0;
std::size_t settledBytes = 0;
std::optional<FileHeader> settledFile;
Expand Down
25 changes: 16 additions & 9 deletions core/libs/prx/libSceAgcDriver/Graphics/src/PipelineCache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ std::mutex& pathMutex(const std::filesystem::path& path) {

}

PipelineCache::PipelineCache(const Context& context, const VkPhysicalDeviceProperties& properties) : context(context), properties(properties) {
PipelineCache::PipelineCache(const Context& context, const VkPhysicalDeviceProperties& properties, bool loadFromFile) : context(context), properties(properties) {
static_assert(sizeof(FileHeader) == 24);
const auto directory = ShaderRecompiler::ShaderCacheDirectory();
if (!directory.empty()) {
Expand All @@ -52,7 +52,7 @@ PipelineCache::PipelineCache(const Context& context, const VkPhysicalDevicePrope
std::vector<std::byte> initialData;
if (!path.empty()) {
std::lock_guard pathLock(pathMutex(path));
load(initialData);
if (loadFromFile) load(initialData);
}
VkPipelineCacheCreateInfo info{VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO};
info.initialDataSize = initialData.size();
Expand Down Expand Up @@ -145,7 +145,7 @@ std::optional<PipelineCache::FileHeader> PipelineCache::readHeader() const {
}

void PipelineCache::load(std::vector<std::byte>& initialData) {
const auto file = readFile();
auto file = readFile();
if (file.state == FileState::Missing) return;
if (file.state == FileState::Unreadable) {
std::fprintf(stderr, "[pipeline-cache] cannot read %s; starting empty\n", path.string().c_str());
Expand All @@ -155,11 +155,12 @@ void PipelineCache::load(std::vector<std::byte>& initialData) {
std::fprintf(stderr, "[pipeline-cache] ignoring %s: %s\n", path.string().c_str(), file.invalidReason);
return;
}
const auto data = file.Payload();
initialData.assign(data.begin(), data.end());
initialData = std::move(file.contents);
initialData.erase(initialData.begin(), initialData.begin() + sizeof(FileHeader));
loadedBytes = initialData.size();
settledBytes = initialData.size();
settledFile = file.header;
holdsFile = true;
std::fprintf(stderr, "[pipeline-cache] loaded %.1f KiB from %s\n", static_cast<double>(initialData.size()) / 1024.0, path.string().c_str());
}

Expand All @@ -171,20 +172,22 @@ void PipelineCache::save(bool final) {
auto synchronizationLock = synchronization.LockForSnapshot();
if (getData(context.device, cache, &liveBytes, nullptr) != VK_SUCCESS || liveBytes == 0) return;
}
if (liveBytes == settledBytes && readHeader() == settledFile) return;
const auto onDisk = readHeader();
if (liveBytes == settledBytes && onDisk == settledFile) return;
const bool fileUnchanged = holdsFile && settledFile.has_value() && onDisk == settledFile;
const auto started = std::chrono::steady_clock::now();
const auto settle = [&](const std::optional<FileHeader>& file) {
settledBytes = liveBytes;
settledFile = file;
};
const auto disk = readFile();
const auto disk = fileUnchanged ? File{} : readFile();
if (disk.state == FileState::Unreadable) {
std::fprintf(stderr, "[pipeline-cache] cannot read %s; not saving over it\n", path.string().c_str());
settle(readHeader());
return;
}
if (disk.state == FileState::Invalid) std::fprintf(stderr, "[pipeline-cache] replacing %s: %s\n", path.string().c_str(), disk.invalidReason);
std::size_t floor = 0;
std::size_t floor = fileUnchanged ? std::max<std::size_t>(loadedBytes, settledFile->dataBytes) : 0;
VkPipelineCache merged = VK_NULL_HANDLE;
struct DestroyMerged {
const Context& context;
Expand Down Expand Up @@ -231,7 +234,7 @@ void PipelineCache::save(bool final) {
}
if (size < floor) {
std::fprintf(stderr, "[pipeline-cache] keeping %s: the merged cache has %.1f KiB, less than the %.1f KiB on disk or loaded at start\n", path.string().c_str(), static_cast<double>(size) / 1024.0, static_cast<double>(floor) / 1024.0);
settle(disk.header);
settle(fileUnchanged ? *settledFile : disk.header);
return;
}
const FileHeader header{FileMagic, FileFormat, size, ShaderRecompiler::HashBytes(std::span(file).subspan(sizeof(FileHeader)))};
Expand All @@ -244,6 +247,10 @@ void PipelineCache::save(bool final) {
if (final || profiling()) std::fprintf(stderr, "[pipeline-cache] saved %.1f KiB to %s in %.1f ms%s%s\n", static_cast<double>(size) / 1024.0, path.string().c_str(), std::chrono::duration<double, std::milli>(std::chrono::steady_clock::now() - started).count(), merged != VK_NULL_HANDLE ? " (merged with the file)" : "", final ? " (teardown)" : "");
}

void PipelineCache::SaveNow() {
save(false);
}

void PipelineCache::run() {
std::unique_lock lock(mutex);
while (!wake.wait_for(lock, std::chrono::seconds(SaveIntervalSeconds), [&] { return stopping; })) {
Expand Down
37 changes: 37 additions & 0 deletions core/libs/prx/libSceAgcDriver/tests/Recorder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7429,6 +7429,43 @@ int pipelineCacheFileTests(const Device& device) {
PipelineCache idle(context, properties);
}
Require(contents(path) == stored, "an object that added no pipelines rewrote the cache file");
{
PipelineCache unseeded(context, properties, false);
Require(!holds(unseeded.Handle(), 0), "a cache created without the file already held a pipeline of the file");
}
{
PipelineCache reloaded(context, properties);
Require(holds(reloaded.Handle(), 0) && holds(reloaded.Handle(), 1), "an unseeded cache dropped pipelines from the cache file when it saved");
}
std::filesystem::remove_all(directory);
{
PipelineCache writer(context, properties);
build(writer.Handle(), 0);
}
{
PipelineCache unseeded(context, properties, false);
unseeded.SaveNow();
build(unseeded.Handle(), 1);
unseeded.SaveNow();
}
{
PipelineCache reloaded(context, properties);
Require(holds(reloaded.Handle(), 0) && holds(reloaded.Handle(), 1), "an unseeded cache that saved twice over an unchanged file lost pipelines of the file");
}
std::filesystem::remove_all(directory);
{
PipelineCache writer(context, properties);
build(writer.Handle(), 0);
}
{
PipelineCache extender(context, properties);
Require(holds(extender.Handle(), 0), "the cache file lost a pipeline");
build(extender.Handle(), 1);
}
{
PipelineCache reloaded(context, properties);
Require(holds(reloaded.Handle(), 0) && holds(reloaded.Handle(), 1), "a cache that saved over an unchanged file lost pipelines");
}
{
std::ofstream corrupt(path, std::ios::binary | std::ios::trunc);
corrupt << "not a pipeline cache";
Expand Down
Loading