Vulkan: Fix synchronization when shutting the GPU down in-game.

This commit is contained in:
Henrik Rydgård 2023-10-11 09:04:28 +02:00
parent e26bf611e0
commit 0ad2827e14
9 changed files with 39 additions and 27 deletions

View File

@ -1244,6 +1244,7 @@ void VulkanQueueRunner::PerformRenderPass(const VKRStep &step, VkCommandBuffer c
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
descSets = &c.graphics_pipeline.pipelineLayout->frameData[curFrame].descSets_;
pipelineLayout = c.graphics_pipeline.pipelineLayout->pipelineLayout;
_dbg_assert_(pipelineLayout != VK_NULL_HANDLE);
lastGraphicsPipeline = graphicsPipeline;
pipelineOK = true;
} else {

View File

@ -413,6 +413,8 @@ VulkanRenderManager::~VulkanRenderManager() {
vulkan_->WaitUntilQueueIdle();
_dbg_assert_(pipelineLayouts_.empty());
VkDevice device = vulkan_->GetDevice();
frameDataShared_.Destroy(vulkan_);
for (int i = 0; i < inflightFramesAtStart_; i++) {
@ -519,12 +521,14 @@ void VulkanRenderManager::CompileThreadFunc() {
}
void VulkanRenderManager::DrainAndBlockCompileQueue() {
EndCurRenderStep();
std::unique_lock<std::mutex> lock(compileMutex_);
compileBlocked_ = true;
compileCond_.notify_all();
while (!compileQueue_.empty()) {
queueRunner_.WaitForCompileNotification();
}
FlushSync();
}
void VulkanRenderManager::ReleaseCompileQueue() {
@ -1538,6 +1542,8 @@ void VulkanRenderManager::Run(VKRRenderThreadTask &task) {
// Called from main thread.
void VulkanRenderManager::FlushSync() {
_dbg_assert_(!curRenderStep_);
if (invalidationCallback_) {
invalidationCallback_(InvalidationCallbackFlags::COMMAND_BUFFER_STATE);
}
@ -1669,6 +1675,7 @@ void VulkanRenderManager::DestroyPipelineLayout(VKRPipelineLayout *layout) {
break;
}
}
delete layout;
}
void VulkanRenderManager::FlushDescriptors(int frame) {
@ -1687,6 +1694,11 @@ void VulkanRenderManager::ResetDescriptorLists(int frame) {
}
}
VKRPipelineLayout::~VKRPipelineLayout() {
_assert_(!pipelineLayout && !descriptorSetLayout);
_assert_(frameData[0].pool.IsDestroyed());
}
void VKRPipelineLayout::FlushDescSets(VulkanContext *vulkan, int frame, QueueProfileContext *profile) {
_dbg_assert_(frame < VulkanContext::MAX_INFLIGHT_FRAMES);

View File

@ -210,11 +210,7 @@ struct PackedDescriptor {
// Note that we only support a single descriptor set due to compatibility with some ancient devices.
// We should probably eventually give that up.
struct VKRPipelineLayout {
VKRPipelineLayout() {}
~VKRPipelineLayout() {
_assert_(!pipelineLayout && !descriptorSetLayout);
_assert_(frameData[0].pool.IsDestroyed());
}
~VKRPipelineLayout();
enum { MAX_DESC_SET_BINDINGS = 10 };
BindingType bindingTypes[MAX_DESC_SET_BINDINGS];

View File

@ -254,11 +254,8 @@ void DrawEngineVulkan::DoFlush() {
const bool forceIndexed = draw_->GetDeviceCaps().verySlowShaderCompiler;
if (useHWTransform) {
int vertexCount = 0;
bool useElements = true;
VkBuffer vbuf = VK_NULL_HANDLE;
VkBuffer ibuf = VK_NULL_HANDLE;
bool useIndexGen = true;
if (decOptions_.applySkinInDecode && (lastVType_ & GE_VTYPE_WEIGHT_MASK)) {
// If software skinning, we're predecoding into "decoded". So make sure we're done, then push that content.
DecodeVerts(decoded_);
@ -272,8 +269,8 @@ void DrawEngineVulkan::DoFlush() {
DecodeInds();
gpuStats.numUncachedVertsDrawn += indexGen.VertexCount();
if (useIndexGen) {
vertexCount = indexGen.VertexCount();
bool useElements;
int vertexCount = indexGen.VertexCount();
if (forceIndexed) {
useElements = true;
prim = indexGen.GeneralPrim();
@ -284,7 +281,6 @@ void DrawEngineVulkan::DoFlush() {
}
prim = indexGen.Prim();
}
}
bool hasColor = (lastVType_ & GE_VTYPE_COL_MASK) != GE_VTYPE_COL_NONE;
if (gstate.isModeThrough()) {

View File

@ -167,6 +167,7 @@ void GPU_Vulkan::SaveCache(const Path &filename) {
GPU_Vulkan::~GPU_Vulkan() {
if (draw_) {
VulkanRenderManager *rm = (VulkanRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);
// This now also does a hard sync with the render thread, so that we can safely delete our pipeline layout below.
rm->DrainAndBlockCompileQueue();
}
@ -423,6 +424,7 @@ void GPU_Vulkan::CheckRenderResized() {
void GPU_Vulkan::DeviceLost() {
// draw_ is normally actually still valid here in Vulkan. But we null it out in GPUCommonHW::DeviceLost so we don't try to use it again.
// So, we have to save it here to be able to call ReleaseCompileQueue().
Draw::DrawContext *draw = draw_;
if (draw) {
VulkanRenderManager *rm = (VulkanRenderManager *)draw->GetNativeObject(Draw::NativeObject::RENDER_MANAGER);

View File

@ -1177,7 +1177,7 @@ void EmuScreen::update() {
}
}
void EmuScreen::checkPowerDown() {
bool EmuScreen::checkPowerDown() {
if (PSP_IsRebooting()) {
bootPending_ = true;
invalid_ = true;
@ -1191,7 +1191,9 @@ void EmuScreen::checkPowerDown() {
screenManager()->switchScreen(new MainScreen());
bootPending_ = false;
invalid_ = true;
return true;
}
return false;
}
static const char *CPUCoreAsString(int core) {
@ -1454,6 +1456,7 @@ void EmuScreen::render() {
Core_UpdateDebugStats((DebugOverlay)g_Config.iDebugOverlay == DebugOverlay::DEBUG_STATS || g_Config.bLogFrameDrops);
bool blockedExecution = Achievements::IsBlockingExecution();
bool rebind = false;
if (!blockedExecution) {
PSP_BeginHostFrame();
PSP_RunLoopWhileState();
@ -1490,7 +1493,7 @@ void EmuScreen::render() {
// Didn't actually reach the end of the frame, ran out of the blockTicks cycles.
// In this case we need to bind and wipe the backbuffer, at least.
// It's possible we never ended up outputted anything - make sure we have the backbuffer cleared
thin3d->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::CLEAR, RPAction::CLEAR }, "EmuScreen_NoFrame");
rebind = true;
break;
}
@ -1498,10 +1501,10 @@ void EmuScreen::render() {
}
// This must happen after PSP_EndHostFrame so that things like push buffers are end-frame'd before we start destroying stuff.
checkPowerDown();
if (invalid_)
return;
if (checkPowerDown() || rebind) {
// Shutting down can end up ending the current render pass
thin3d->BindFramebufferAsRenderTarget(nullptr, { RPAction::CLEAR, RPAction::CLEAR, RPAction::CLEAR }, "EmuScreen_NoFrame");
}
if (hasVisibleUI()) {
// In most cases, this should already be bound and a no-op.

View File

@ -76,7 +76,7 @@ private:
void onVKeyAnalog(int virtualKeyCode, float value);
void autoLoad();
void checkPowerDown();
bool checkPowerDown();
UI::Event OnDevMenu;
UI::Event OnChatMenu;

View File

@ -335,6 +335,8 @@ shutdown:
g_graphicsContext->Shutdown();
delete g_graphicsContext;
UpdateConsolePosition();
NativeShutdown();

@ -1 +1 @@
Subproject commit 6f02e791aa202de5eaf3e86ed269dda6d456b779
Subproject commit 24dc84ca2dfbdbddebc9a26f4df225888e56a4f4