mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-22 08:33:12 -05:00
fix(agc): drain library builds before device replacement
This commit is contained in:
1 parent
82ddae75b6
commit
81e3db853c
6 files changed
+90
-1
No files matched your search
@@ -1565,7 +1565,9 @@ if(BUILD_TESTING)
|
||||
endif()
|
||||
set_tests_properties(agc_sparse_buffer_holes PROPERTIES TIMEOUT 30)
|
||||
add_test(NAME agc_pipeline_journal COMMAND agc_driver_graphics_tests --pipeline-journal)
|
||||
add_test(NAME agc_pipeline_library_clear_waits_for_build COMMAND agc_driver_graphics_tests --pipeline-library-clear-waits-for-build)
|
||||
set_tests_properties(agc_pipeline_journal PROPERTIES TIMEOUT 30 WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}")
|
||||
set_tests_properties(agc_pipeline_library_clear_waits_for_build PROPERTIES TIMEOUT 30 WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}")
|
||||
if(WIN32)
|
||||
set_tests_properties(agc_pipeline_journal PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$<TARGET_FILE_DIR:libc>")
|
||||
endif()
|
||||
|
||||
@@ -117,6 +117,7 @@ void SubmitRegistrationPreparation(std::function<void()> task, const PreparedSha
|
||||
void PrioritizeRegistrationPreparation(PreparedShaders& prepared);
|
||||
void AwaitRegistrationPreparation();
|
||||
void SubmitLibraryPrebuild(std::function<void()> task);
|
||||
void AwaitLibraryPrebuilds();
|
||||
void CloseRegistrationPreparation();
|
||||
|
||||
void ResolvePreparedGraphics(const ShaderSnapshot& front, const std::shared_ptr<const ShaderSnapshot>& fragment, std::uint32_t primitiveType, const ShaderRecompiler::SpirvTarget& target);
|
||||
|
||||
@@ -1119,6 +1119,10 @@ void SubmitLibraryPrebuild(std::function<void()> task) {
|
||||
RegistrationPreparePool::Prebuild().Submit(std::move(task));
|
||||
}
|
||||
|
||||
void AwaitLibraryPrebuilds() {
|
||||
if (auto* pool = RegistrationPreparePool::ExistingPrebuild()) pool->AwaitSubmitted();
|
||||
}
|
||||
|
||||
void CloseRegistrationPreparation() {
|
||||
RegistrationPreparePool::Instance().Close();
|
||||
if (auto* prebuild = RegistrationPreparePool::ExistingPrebuild()) prebuild->Close();
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "prx/libSceAgcDriver/Graphics/include/PipelineJournal.hpp"
|
||||
#include "prx/libSceAgcDriver/Execution/include/VulkanDevice.hpp"
|
||||
#include "prx/libSceAgcDriver/Execution/include/Driver.hpp"
|
||||
#include "prx/libSceAgcDriver/Execution/include/Driver/Shaders/ShaderRegistry.hpp"
|
||||
#include "prx/libSceAgcDriver/Execution/include/PerformanceTimer.hpp"
|
||||
#include "prx/libSceAgcDriver/Execution/include/BdaFeatures.hpp"
|
||||
#include "prx/libSceAgcDriver/Execution/include/DeviceFault.hpp"
|
||||
@@ -2027,6 +2028,7 @@ void VulkanDevice::PrepareForReplacement() {
|
||||
state->journalReplay->Wait();
|
||||
state->journalReplay.reset();
|
||||
}
|
||||
DriverDetail::AwaitLibraryPrebuilds();
|
||||
Graphics::FlushCachedTextures(state->device);
|
||||
Graphics::PublishAllShadows(state->context, Graphics::PublishReason::Teardown);
|
||||
WaitIdle();
|
||||
|
||||
@@ -53,6 +53,7 @@ struct LibraryStore {
|
||||
std::condition_variable work;
|
||||
std::deque<std::pair<VkDevice, std::function<void()>>> jobs;
|
||||
std::map<VkDevice, std::size_t> pending;
|
||||
std::map<VkDevice, std::size_t> activeBuilds;
|
||||
bool started = false;
|
||||
};
|
||||
|
||||
@@ -109,6 +110,16 @@ VkPipeline LinkPipelineFromLibraries(const Context& context, const VkGraphicsPip
|
||||
Require(!split || (layout.setLayoutCount == 2u && layout.pSetLayouts != nullptr && ((keys.preRasterizationSets | keys.fragmentSets) & ~3u) == 0u), "split pipeline library layout requires two descriptor set slots and a valid mask");
|
||||
auto& store = Libraries();
|
||||
std::unique_lock lock(store.mutex);
|
||||
++store.activeBuilds[context.device];
|
||||
struct ActiveBuild {
|
||||
LibraryStore& store;
|
||||
VkDevice device;
|
||||
std::unique_lock<std::mutex>& lock;
|
||||
~ActiveBuild() {
|
||||
if (!lock.owns_lock()) lock.lock();
|
||||
if (--store.activeBuilds[device] == 0) store.idle.notify_all();
|
||||
}
|
||||
} activeBuild{store, context.device, lock};
|
||||
auto& device = store.devices[context.device];
|
||||
if (device.destroyPipeline == nullptr) {
|
||||
device.destroyPipeline = context.Function<PFN_vkDestroyPipeline>("vkDestroyPipeline");
|
||||
@@ -312,7 +323,7 @@ void WaitForOptimizedPipelines(VkDevice device) {
|
||||
void ClearPipelineLibraries(VkDevice device) {
|
||||
auto& store = Libraries();
|
||||
std::unique_lock lock(store.mutex);
|
||||
store.idle.wait(lock, [&] { return store.pending[device] == 0; });
|
||||
store.idle.wait(lock, [&] { return store.pending[device] == 0 && store.activeBuilds[device] == 0; });
|
||||
const auto found = store.devices.find(device);
|
||||
if (found == store.devices.end()) return;
|
||||
auto& libraries = found->second;
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include <array>
|
||||
#include <bit>
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
@@ -3488,6 +3489,11 @@ struct MockVulkan {
|
||||
};
|
||||
|
||||
MockVulkan mock;
|
||||
std::mutex pipelineCreateMutex;
|
||||
std::condition_variable pipelineCreateCondition;
|
||||
bool blockLibraryCreate = false;
|
||||
bool libraryCreateEntered = false;
|
||||
bool releaseLibraryCreate = false;
|
||||
|
||||
template<typename THandle>
|
||||
THandle makeHandle() {
|
||||
@@ -3672,6 +3678,14 @@ VKAPI_ATTR VkResult VKAPI_CALL mockCreatePipelineLayout(VkDevice, const VkPipeli
|
||||
}
|
||||
|
||||
VKAPI_ATTR VkResult VKAPI_CALL mockCreateGraphicsPipelines(VkDevice, VkPipelineCache, std::uint32_t count, const VkGraphicsPipelineCreateInfo* infos, const VkAllocationCallbacks*, VkPipeline* pipelines) {
|
||||
{
|
||||
std::unique_lock lock(pipelineCreateMutex);
|
||||
if (blockLibraryCreate) {
|
||||
libraryCreateEntered = true;
|
||||
pipelineCreateCondition.notify_all();
|
||||
pipelineCreateCondition.wait(lock, [] { return releaseLibraryCreate; });
|
||||
}
|
||||
}
|
||||
Require(count == 1, "mock expects exactly one graphics pipeline per call");
|
||||
if ((infos[0].flags & VK_PIPELINE_CREATE_LIBRARY_BIT_KHR) == 0) {
|
||||
const auto* linked = static_cast<const VkPipelineLibraryCreateInfoKHR*>(infos[0].pNext);
|
||||
@@ -5140,6 +5154,55 @@ void pipelineLibrarySetTests() {
|
||||
}
|
||||
}
|
||||
|
||||
void pipelineLibraryClearWaitsForBuildTests() {
|
||||
using namespace AgcDriver::Graphics;
|
||||
mock = MockVulkan{};
|
||||
auto context = mockContext();
|
||||
context.graphicsPipelineLibrary = true;
|
||||
context.limits.framebufferNoAttachmentsSampleCounts = VK_SAMPLE_COUNT_1_BIT;
|
||||
State state{};
|
||||
ShaderRecompiler::RecompileResult vertex, fragment;
|
||||
vertex.spirv = makeModule({});
|
||||
fragment.spirv = makeModule({.fragment = true});
|
||||
vertex.variantId = 101;
|
||||
fragment.variantId = 102;
|
||||
const std::array<CompiledShader, 2> shaders{{{ShaderRecompiler::ShaderStage::Vertex, &vertex, 0}, {ShaderRecompiler::ShaderStage::Fragment, &fragment, PipelinePushSlotBytes}}};
|
||||
std::array<std::uint32_t, 1> mainKey{1}, fragmentKey{2};
|
||||
PipelineSetLayouts resources{};
|
||||
resources.main = makeHandle<VkDescriptorSetLayout>();
|
||||
resources.fragment = makeHandle<VkDescriptorSetLayout>();
|
||||
resources.mainKey = mainKey;
|
||||
resources.fragmentKey = fragmentKey;
|
||||
{
|
||||
std::lock_guard lock(pipelineCreateMutex);
|
||||
blockLibraryCreate = true;
|
||||
libraryCreateEntered = false;
|
||||
releaseLibraryCreate = false;
|
||||
}
|
||||
std::thread build([&] { Require(Pipeline::Prebuild(context, state, VertexInputLayout{}, resources, shaders), "graphics pipeline library prebuild was skipped"); });
|
||||
{
|
||||
std::unique_lock lock(pipelineCreateMutex);
|
||||
pipelineCreateCondition.wait(lock, [] { return libraryCreateEntered; });
|
||||
}
|
||||
std::atomic<bool> cleared{false};
|
||||
std::thread clear([&] {
|
||||
ClearCachedPipelines(context.device);
|
||||
cleared.store(true, std::memory_order_release);
|
||||
});
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(20));
|
||||
const bool waited = !cleared.load(std::memory_order_acquire);
|
||||
{
|
||||
std::lock_guard lock(pipelineCreateMutex);
|
||||
releaseLibraryCreate = true;
|
||||
}
|
||||
pipelineCreateCondition.notify_all();
|
||||
build.join();
|
||||
clear.join();
|
||||
Require(waited, "clearing pipeline libraries did not wait for an active part build");
|
||||
Require(cleared.load(std::memory_order_acquire), "clearing pipeline libraries did not complete after the part build");
|
||||
Require(mock.live == 0, "clearing during a library build leaked Vulkan objects");
|
||||
}
|
||||
|
||||
void barycentricEmulationTests() {
|
||||
using namespace ShaderRecompiler;
|
||||
using namespace AgcDriver::Graphics;
|
||||
@@ -7694,6 +7757,11 @@ int main(int argc, char** argv) {
|
||||
std::cout << "pipeline journal recipe, record and replay tests passed\n";
|
||||
return 0;
|
||||
}
|
||||
if (argc == 2 && std::string_view(argv[1]) == "--pipeline-library-clear-waits-for-build") {
|
||||
pipelineLibraryClearWaitsForBuildTests();
|
||||
std::cout << "pipeline library clear waits for active part builds\n";
|
||||
return 0;
|
||||
}
|
||||
if (argc == 2 && std::string_view(argv[1]) == "--sparse-buffer-holes") {
|
||||
bool passed = false;
|
||||
{
|
||||
@@ -7806,6 +7874,7 @@ int main(int argc, char** argv) {
|
||||
stateTests();
|
||||
retainedPixelStageTests();
|
||||
pipelineLibrarySetTests();
|
||||
pipelineLibraryClearWaitsForBuildTests();
|
||||
hdrComponentSwapTests();
|
||||
depthMaintenanceTests();
|
||||
hardwareScreenOffsetTests();
|
||||
|
||||
Reference in new issue
Block a user