GPU: skip the draws of a pipeline the driver cannot create

"Failed to create graphics pipeline: ErrorUnknown" (some AMD and older
drivers) stopped the game with code 23. The pipeline now stays empty and is
logged; its draws and dispatches are skipped, on the GPU thread and when a
background compile finishes, and it is not saved to the cache, so the next
start tries it again. A cached one the driver rejects already rebuilt the
cache.

After Supermedo/bloodborne_pc 188c1f6, adapted to the background compiles.
This commit is contained in:
ayozetr committed 2026-10-13 11:26:21 +01:00
1 parent 25cdef7482
commit 49a37b4459
4 files changed
+38 -4

No files matched your search

@@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <cstdio>
#include <boost/container/small_vector.hpp>
#include "common/serdes.h"
@@ -115,8 +116,13 @@ ComputePipeline::ComputePipeline(const Instance& instance, Scheduler& scheduler,
throw Serialization::CorruptData{"cached compute pipeline rejected by the driver: " +
vk::to_string(pipeline_result)};
}
ASSERT_MSG(pipeline_result == vk::Result::eSuccess, "Failed to create compute pipeline: {}",
vk::to_string(pipeline_result));
// bbport: as for graphics pipelines, one the driver cannot build skips its dispatches.
if (pipeline_result != vk::Result::eSuccess) {
std::printf("GPU: the driver could not create compute pipeline %s (%s): its dispatches "
"are skipped\n",
debug_str.c_str(), vk::to_string(pipeline_result).c_str());
return;
}
pipeline = std::move(pipe);
SetObjectName(device, *pipeline, "Compute Pipeline {}", debug_str);
}
@@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <cstdio>
#include <algorithm>
#include <utility>
#include <boost/container/small_vector.hpp>
@@ -429,8 +430,15 @@ GraphicsPipeline::GraphicsPipeline(
throw Serialization::CorruptData{"cached graphics pipeline rejected by the driver: " +
vk::to_string(pipeline_result)};
}
ASSERT_MSG(pipeline_result == vk::Result::eSuccess, "Failed to create graphics pipeline: {}",
vk::to_string(pipeline_result));
// bbport: a driver that cannot build one pipeline ("ErrorUnknown" on some AMD and older
// drivers) stopped the game here (code 23). The pipeline stays empty instead, and the draws
// that use it are skipped (PipelineCache): a missing effect rather than a closed game.
if (pipeline_result != vk::Result::eSuccess) {
std::printf("GPU: the driver could not create graphics pipeline %s (%s): its draws are "
"skipped\n",
debug_str.c_str(), vk::to_string(pipeline_result).c_str());
return;
}
pipeline = std::move(pipe);
SetObjectName(device, *pipeline, "Graphics Pipeline {}", debug_str);
}
@@ -674,6 +674,13 @@ const GraphicsPipeline* PipelineCache::GetGraphicsPipeline(const DrawIndirectPar
it.value() = std::make_unique<GraphicsPipeline>(
instance, scheduler, desc_heap, profile, sel.graphics_key, *pipeline_cache, sel.infos,
sel.runtime_infos, sel.fetch_shader, sel.modules, sdata, false);
// bbport: the driver could not create it. The empty entry keeps the key from compiling
// again on every draw (they are skipped); it is not saved, so the next start tries anew.
if (!it.value()->IsValid()) {
it.value().reset();
sel.fetch_shader.reset();
return nullptr;
}
RegisterPipelineData(sel.graphics_key, pipeline_hash, sdata);
++num_new_pipelines;
@@ -693,6 +700,10 @@ const GraphicsPipeline* PipelineCache::GetGraphicsPipeline(const DrawIndirectPar
const GraphicsPipeline* PipelineCache::FinishPipeline(const GraphicsPipelineKey& key,
PendingPipeline& job) {
if (!job.pipeline || !job.pipeline->IsValid()) {
graphics_pipelines[key].reset(); // bbport: as GetGraphicsPipeline, skipped and not saved
return nullptr;
}
const auto pipeline_hash = std::hash<GraphicsPipelineKey>{}(key);
RegisterPipelineData(key, pipeline_hash, job.sdata);
++num_new_pipelines;
@@ -774,6 +785,10 @@ const ComputePipeline* PipelineCache::GetComputePipeline() {
it.value() = std::make_unique<ComputePipeline>(instance, scheduler, desc_heap, profile,
*pipeline_cache, compute_key, *sel.infos[0],
sel.modules[0], sdata, false);
if (!it.value()->IsValid()) { // bbport: as for graphics pipelines
it.value().reset();
return nullptr;
}
RegisterPipelineData(compute_key, sdata);
++num_new_pipelines;
@@ -36,6 +36,11 @@ public:
return *pipeline;
}
/// bbport: false when the driver could not create the pipeline (its draws are skipped).
bool IsValid() const noexcept {
return static_cast<bool>(pipeline);
}
vk::PipelineLayout GetLayout() const noexcept {
return *pipeline_layout;
}