From 6336c4ee1f8f12b7fa49c125710b082ed8c3f130 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sat, 7 Sep 2024 12:48:44 +1000 Subject: [PATCH] GPUDevice: Add recovery from lost device --- src/core/gpu.cpp | 29 +++++++++----- src/core/gpu.h | 6 ++- src/core/imgui_overlays.cpp | 2 +- src/core/system.cpp | 53 ++++++++++++++++++++++--- src/util/d3d11_device.cpp | 10 ++--- src/util/d3d11_device.h | 2 +- src/util/d3d12_device.cpp | 34 ++++++++++++---- src/util/d3d12_device.h | 3 +- src/util/gpu_device.h | 9 ++++- src/util/metal_device.h | 2 +- src/util/metal_device.mm | 10 ++--- src/util/opengl_device.cpp | 6 +-- src/util/opengl_device.h | 2 +- src/util/postprocessing.cpp | 16 ++++---- src/util/postprocessing.h | 5 ++- src/util/postprocessing_shader.h | 11 +++-- src/util/postprocessing_shader_fx.cpp | 13 +++--- src/util/postprocessing_shader_fx.h | 6 +-- src/util/postprocessing_shader_glsl.cpp | 13 +++--- src/util/postprocessing_shader_glsl.h | 6 +-- src/util/vulkan_device.cpp | 35 ++++++++-------- src/util/vulkan_device.h | 4 +- 22 files changed, 181 insertions(+), 96 deletions(-) diff --git a/src/core/gpu.cpp b/src/core/gpu.cpp index 13fe5516b..d81e1465f 100644 --- a/src/core/gpu.cpp +++ b/src/core/gpu.cpp @@ -1993,7 +1993,7 @@ void GPU::SetDisplayTexture(GPUTexture* texture, GPUTexture* depth_buffer, s32 v m_display_texture_view_height = view_height; } -bool GPU::PresentDisplay() +GPUDevice::PresentResult GPU::PresentDisplay() { FlushRender(); @@ -2004,7 +2004,8 @@ bool GPU::PresentDisplay() return RenderDisplay(nullptr, display_rect, draw_rect, !g_settings.debugging.show_vram); } -bool GPU::RenderDisplay(GPUTexture* target, const GSVector4i display_rect, const GSVector4i draw_rect, bool postfx) +GPUDevice::PresentResult GPU::RenderDisplay(GPUTexture* target, const GSVector4i display_rect, + const GSVector4i draw_rect, bool postfx) { GL_SCOPE_FMT("RenderDisplay: {}", draw_rect); @@ -2027,10 +2028,15 @@ bool GPU::RenderDisplay(GPUTexture* target, const GSVector4i display_rect, const // Now we can apply the post chain. GPUTexture* post_output_texture = PostProcessing::InternalChain.GetOutputTexture(); - if (PostProcessing::InternalChain.Apply(display_texture, m_display_depth_buffer, post_output_texture, - GSVector4i(0, 0, display_texture_view_width, display_texture_view_height), - display_texture_view_width, display_texture_view_height, - m_crtc_state.display_width, m_crtc_state.display_height)) + if (const GPUDevice::PresentResult pres = PostProcessing::InternalChain.Apply( + display_texture, m_display_depth_buffer, post_output_texture, + GSVector4i(0, 0, display_texture_view_width, display_texture_view_height), display_texture_view_width, + display_texture_view_height, m_crtc_state.display_width, m_crtc_state.display_height); + pres != GPUDevice::PresentResult::OK) + { + return pres; + } + else { display_texture_view_x = 0; display_texture_view_y = 0; @@ -2057,8 +2063,9 @@ bool GPU::RenderDisplay(GPUTexture* target, const GSVector4i display_rect, const { if (target) g_gpu_device->SetRenderTarget(target); - else if (!g_gpu_device->BeginPresent(false)) - return false; + else if (const GPUDevice::PresentResult pres = g_gpu_device->BeginPresent(false); + pres != GPUDevice::PresentResult::OK) + return pres; } if (display_texture) @@ -2167,7 +2174,9 @@ bool GPU::RenderDisplay(GPUTexture* target, const GSVector4i display_rect, const m_crtc_state.display_height); } else - return true; + { + return GPUDevice::PresentResult::OK; + } } bool GPU::SendDisplayToMediaCapture(MediaCapture* cap) @@ -2186,7 +2195,7 @@ bool GPU::SendDisplayToMediaCapture(MediaCapture* cap) // Not cleared by RenderDisplay(). g_gpu_device->ClearRenderTarget(target, GPUDevice::DEFAULT_CLEAR_COLOR); - if (!RenderDisplay(target, display_rect, draw_rect, postfx)) [[unlikely]] + if (RenderDisplay(target, display_rect, draw_rect, postfx) != GPUDevice::PresentResult::OK) [[unlikely]] return false; return cap->DeliverVideoFrame(target); diff --git a/src/core/gpu.h b/src/core/gpu.h index 063e64714..99d47e8ec 100644 --- a/src/core/gpu.h +++ b/src/core/gpu.h @@ -8,6 +8,7 @@ #include "timing_event.h" #include "types.h" +#include "util/gpu_device.h" #include "util/gpu_texture.h" #include "common/bitfield.h" @@ -233,7 +234,7 @@ public: bool show_osd_message); /// Draws the current display texture, with any post-processing. - bool PresentDisplay(); + GPUDevice::PresentResult PresentDisplay(); /// Sends the current frame to media capture. bool SendDisplayToMediaCapture(MediaCapture* cap); @@ -630,7 +631,8 @@ protected: void SetDisplayTexture(GPUTexture* texture, GPUTexture* depth_texture, s32 view_x, s32 view_y, s32 view_width, s32 view_height); - bool RenderDisplay(GPUTexture* target, const GSVector4i display_rect, const GSVector4i draw_rect, bool postfx); + GPUDevice::PresentResult RenderDisplay(GPUTexture* target, const GSVector4i display_rect, const GSVector4i draw_rect, + bool postfx); bool Deinterlace(u32 field, u32 line_skip); bool DeinterlaceExtractField(u32 dst_bufidx, GPUTexture* src, u32 x, u32 y, u32 width, u32 height, u32 line_skip); diff --git a/src/core/imgui_overlays.cpp b/src/core/imgui_overlays.cpp index ed1950e3f..22ba420bb 100644 --- a/src/core/imgui_overlays.cpp +++ b/src/core/imgui_overlays.cpp @@ -160,7 +160,7 @@ void Host::DisplayLoadingScreen(const char* message, int progress_min /*= -1*/, // TODO: Glass effect or something. - if (g_gpu_device->BeginPresent(false)) + if (g_gpu_device->BeginPresent(false) == GPUDevice::PresentResult::OK) { g_gpu_device->RenderImGui(); g_gpu_device->EndPresent(false); diff --git a/src/core/system.cpp b/src/core/system.cpp index 627c81ba1..b9d4fc097 100644 --- a/src/core/system.cpp +++ b/src/core/system.cpp @@ -162,6 +162,7 @@ static void DestroySystem(); static bool CreateGPU(GPURenderer renderer, bool is_switching, Error* error); static bool RecreateGPU(GPURenderer renderer, bool force_recreate_device = false, bool update_display = true); +static void HandleHostGPUDeviceLost(); /// Updates the throttle period, call when target emulation speed changes. static void UpdateThrottlePeriod(); @@ -1202,6 +1203,45 @@ bool System::RecreateGPU(GPURenderer renderer, bool force_recreate_device, bool return true; } +void System::HandleHostGPUDeviceLost() +{ + static Common::Timer::Value s_last_gpu_reset_time = 0; + static constexpr float MIN_TIME_BETWEEN_RESETS = 15.0f; + + // If we're constantly crashing on something in particular, we don't want to end up in an + // endless reset loop.. that'd probably end up leaking memory and/or crashing us for other + // reasons. So just abort in such case. + const Common::Timer::Value current_time = Common::Timer::GetCurrentValue(); + if (s_last_gpu_reset_time != 0 && + Common::Timer::ConvertValueToSeconds(current_time - s_last_gpu_reset_time) < MIN_TIME_BETWEEN_RESETS) + { + Panic("Host GPU lost too many times, device is probably completely wedged."); + } + s_last_gpu_reset_time = current_time; + + // Little bit janky, but because the device is lost, the VRAM readback is going to give us garbage. + // So back up what we have, it's probably missing bits, but whatever... + DynamicHeapArray vram_backup(VRAM_SIZE); + std::memcpy(vram_backup.data(), g_vram, VRAM_SIZE); + + // Device lost, something went really bad. + // Let's just toss out everything, and try to hobble on. + if (!RecreateGPU(g_gpu->IsHardwareRenderer() ? g_settings.gpu_renderer : GPURenderer::Software, true, false)) + { + Panic("Failed to recreate GS device after loss."); + return; + } + + // Restore backed-up VRAM. + std::memcpy(g_vram, vram_backup.data(), VRAM_SIZE); + + // First frame after reopening is definitely going to be trash, so skip it. + Host::AddIconOSDMessage( + "HostGPUDeviceLost", ICON_EMOJI_WARNING, + TRANSLATE_STR("System", "Host GPU device encountered an error and has recovered. This may cause broken rendering."), + Host::OSD_CRITICAL_ERROR_DURATION); +} + void System::LoadSettings(bool display_osd_messages) { std::unique_lock lock = Host::GetSettingsLock(); @@ -5713,13 +5753,13 @@ bool System::PresentDisplay(bool skip_present, bool explicit_present) ImGuiManager::RenderOverlayWindows(); ImGuiManager::RenderDebugWindows(); - bool do_present; + GPUDevice::PresentResult pres; if (g_gpu && !skip_present) - do_present = g_gpu->PresentDisplay(); + pres = g_gpu->PresentDisplay(); else - do_present = g_gpu_device->BeginPresent(skip_present); + pres = g_gpu_device->BeginPresent(skip_present); - if (do_present) + if (pres == GPUDevice::PresentResult::OK) { g_gpu_device->RenderImGui(); g_gpu_device->EndPresent(explicit_present); @@ -5732,13 +5772,16 @@ bool System::PresentDisplay(bool skip_present, bool explicit_present) } else { + if (pres == GPUDevice::PresentResult::DeviceLost) [[unlikely]] + HandleHostGPUDeviceLost(); + // Still need to kick ImGui or it gets cranky. ImGui::Render(); } ImGuiManager::NewFrame(); - return do_present; + return (pres == GPUDevice::PresentResult::OK); } void System::InvalidateDisplay() diff --git a/src/util/d3d11_device.cpp b/src/util/d3d11_device.cpp index 43f13a50d..b40a87833 100644 --- a/src/util/d3d11_device.cpp +++ b/src/util/d3d11_device.cpp @@ -639,17 +639,17 @@ void D3D11Device::SetVSyncMode(GPUVSyncMode mode, bool allow_present_throttle) } } -bool D3D11Device::BeginPresent(bool skip_present, u32 clear_color) +GPUDevice::PresentResult D3D11Device::BeginPresent(bool skip_present, u32 clear_color) { if (skip_present) - return false; + return PresentResult::SkipPresent; if (!m_swap_chain) { // Note: Really slow on Intel... m_context->Flush(); TrimTexturePool(); - return false; + return PresentResult::SkipPresent; } // Check if we lost exclusive fullscreen. If so, notify the host, so it can switch to windowed mode. @@ -660,7 +660,7 @@ bool D3D11Device::BeginPresent(bool skip_present, u32 clear_color) { Host::SetFullscreen(false); TrimTexturePool(); - return false; + return PresentResult::SkipPresent; } // When using vsync, the time here seems to include the time for the buffer to become available. @@ -677,7 +677,7 @@ bool D3D11Device::BeginPresent(bool skip_present, u32 clear_color) m_current_render_pass_flags = GPUPipeline::NoRenderPassFlags; std::memset(m_current_render_targets.data(), 0, sizeof(m_current_render_targets)); m_current_depth_target = nullptr; - return true; + return PresentResult::OK; } void D3D11Device::EndPresent(bool explicit_present) diff --git a/src/util/d3d11_device.h b/src/util/d3d11_device.h index 50751b478..afba73bf4 100644 --- a/src/util/d3d11_device.h +++ b/src/util/d3d11_device.h @@ -104,7 +104,7 @@ public: bool SetGPUTimingEnabled(bool enabled) override; float GetAndResetAccumulatedGPUTime() override; - bool BeginPresent(bool skip_present, u32 clear_color) override; + PresentResult BeginPresent(bool skip_present, u32 clear_color) override; void EndPresent(bool explicit_present) override; void SubmitPresent() override; diff --git a/src/util/d3d12_device.cpp b/src/util/d3d12_device.cpp index f0339c38f..892aedcaf 100644 --- a/src/util/d3d12_device.cpp +++ b/src/util/d3d12_device.cpp @@ -532,6 +532,9 @@ ID3D12GraphicsCommandList4* D3D12Device::GetInitCommandList() void D3D12Device::SubmitCommandList(bool wait_for_completion) { + if (m_device_was_lost) [[unlikely]] + return; + CommandList& res = m_command_lists[m_current_command_list]; HRESULT hr; @@ -553,7 +556,8 @@ void D3D12Device::SubmitCommandList(bool wait_for_completion) if (FAILED(hr)) [[unlikely]] { ERROR_LOG("Closing init command list failed with HRESULT {:08X}", static_cast(hr)); - Panic("TODO cannot continue"); + m_device_was_lost = true; + return; } } @@ -562,7 +566,8 @@ void D3D12Device::SubmitCommandList(bool wait_for_completion) if (FAILED(hr)) [[unlikely]] { ERROR_LOG("Closing main command list failed with HRESULT {:08X}", static_cast(hr)); - Panic("TODO cannot continue"); + m_device_was_lost = true; + return; } if (res.init_list_used) @@ -578,7 +583,12 @@ void D3D12Device::SubmitCommandList(bool wait_for_completion) // Update fence when GPU has completed. hr = m_command_queue->Signal(m_fence.Get(), res.fence_counter); - DebugAssertMsg(SUCCEEDED(hr), "Signal fence"); + if (FAILED(hr)) + { + ERROR_LOG("Signal command queue fence failed with HRESULT {:08X}", static_cast(hr)); + m_device_was_lost = true; + return; + } MoveToNextCommandList(); @@ -606,6 +616,9 @@ void D3D12Device::SubmitCommandListAndRestartRenderPass(const std::string_view r void D3D12Device::WaitForFence(u64 fence) { + if (m_device_was_lost) [[unlikely]] + return; + if (m_completed_fence_value >= fence) return; @@ -1110,20 +1123,23 @@ void D3D12Device::SetVSyncMode(GPUVSyncMode mode, bool allow_present_throttle) } } -bool D3D12Device::BeginPresent(bool frame_skip, u32 clear_color) +GPUDevice::PresentResult D3D12Device::BeginPresent(bool frame_skip, u32 clear_color) { if (InRenderPass()) EndRenderPass(); + if (m_device_was_lost) [[unlikely]] + return PresentResult::DeviceLost; + if (frame_skip) - return false; + return PresentResult::SkipPresent; // If we're running surfaceless, kick the command buffer so we don't run out of descriptors. if (!m_swap_chain) { SubmitCommandList(false); TrimTexturePool(); - return false; + return PresentResult::SkipPresent; } // TODO: Check if the device was lost. @@ -1136,11 +1152,11 @@ bool D3D12Device::BeginPresent(bool frame_skip, u32 clear_color) { Host::RunOnCPUThread([]() { Host::SetFullscreen(false); }); TrimTexturePool(); - return false; + return PresentResult::SkipPresent; } BeginSwapChainRenderPass(clear_color); - return true; + return PresentResult::OK; } void D3D12Device::EndPresent(bool explicit_present) @@ -1165,6 +1181,8 @@ void D3D12Device::EndPresent(bool explicit_present) void D3D12Device::SubmitPresent() { DebugAssert(m_swap_chain); + if (m_device_was_lost) [[unlikely]] + return; const UINT sync_interval = static_cast(m_vsync_mode == GPUVSyncMode::FIFO); const UINT flags = (m_vsync_mode == GPUVSyncMode::Disabled && m_using_allow_tearing) ? DXGI_PRESENT_ALLOW_TEARING : 0; diff --git a/src/util/d3d12_device.h b/src/util/d3d12_device.h index 90c0b090b..22e05fe49 100644 --- a/src/util/d3d12_device.h +++ b/src/util/d3d12_device.h @@ -126,7 +126,7 @@ public: bool SetGPUTimingEnabled(bool enabled) override; float GetAndResetAccumulatedGPUTime() override; - bool BeginPresent(bool skip_present, u32 clear_color) override; + PresentResult BeginPresent(bool skip_present, u32 clear_color) override; void EndPresent(bool explicit_present) override; void SubmitPresent() override; @@ -300,6 +300,7 @@ private: bool m_allow_tearing_supported = false; bool m_using_allow_tearing = false; bool m_is_exclusive_fullscreen = false; + bool m_device_was_lost = false; D3D12DescriptorHeapManager m_descriptor_heap_manager; D3D12DescriptorHeapManager m_rtv_heap_manager; diff --git a/src/util/gpu_device.h b/src/util/gpu_device.h index aab33a12b..6c6898b11 100644 --- a/src/util/gpu_device.h +++ b/src/util/gpu_device.h @@ -481,6 +481,13 @@ public: Full }; + enum class PresentResult : u32 + { + OK, + SkipPresent, + DeviceLost, + }; + struct Features { bool dual_source_blend : 1; @@ -702,7 +709,7 @@ public: virtual void DrawIndexedWithBarrier(u32 index_count, u32 base_index, u32 base_vertex, DrawBarrier type) = 0; /// Returns false if the window was completely occluded. - virtual bool BeginPresent(bool skip_present, u32 clear_color = DEFAULT_CLEAR_COLOR) = 0; + virtual PresentResult BeginPresent(bool skip_present, u32 clear_color = DEFAULT_CLEAR_COLOR) = 0; virtual void EndPresent(bool explicit_submit) = 0; virtual void SubmitPresent() = 0; diff --git a/src/util/metal_device.h b/src/util/metal_device.h index 97740575f..07216e645 100644 --- a/src/util/metal_device.h +++ b/src/util/metal_device.h @@ -265,7 +265,7 @@ public: void SetVSyncMode(GPUVSyncMode mode, bool allow_present_throttle) override; - bool BeginPresent(bool skip_present, u32 clear_color) override; + PresentResult BeginPresent(bool skip_present, u32 clear_color) override; void EndPresent(bool explicit_submit) override; void SubmitPresent() override; diff --git a/src/util/metal_device.mm b/src/util/metal_device.mm index a0d7c8aad..8e81b01df 100644 --- a/src/util/metal_device.mm +++ b/src/util/metal_device.mm @@ -2312,17 +2312,17 @@ id MetalDevice::GetBlitEncoder(bool is_inline) } } -bool MetalDevice::BeginPresent(bool skip_present, u32 clear_color) +GPUDevice::PresentResult MetalDevice::BeginPresent(bool skip_present, u32 clear_color) { @autoreleasepool { if (skip_present) - return false; + return PresentResult::SkipPresent; if (m_layer == nil) { TrimTexturePool(); - return false; + return PresentResult::SkipPresent; } EndAnyEncoding(); @@ -2331,7 +2331,7 @@ bool MetalDevice::BeginPresent(bool skip_present, u32 clear_color) if (m_layer_drawable == nil) { TrimTexturePool(); - return false; + return PresentResult::SkipPresent; } SetViewportAndScissor(0, 0, m_window_info.surface_width, m_window_info.surface_height); @@ -2351,7 +2351,7 @@ bool MetalDevice::BeginPresent(bool skip_present, u32 clear_color) m_current_pipeline = nullptr; m_current_depth_state = nil; SetInitialEncoderState(); - return true; + return PresentResult::OK; } } diff --git a/src/util/opengl_device.cpp b/src/util/opengl_device.cpp index 4fcc28720..a48605b9e 100644 --- a/src/util/opengl_device.cpp +++ b/src/util/opengl_device.cpp @@ -740,7 +740,7 @@ void OpenGLDevice::DestroyBuffers() m_vertex_buffer.reset(); } -bool OpenGLDevice::BeginPresent(bool skip_present, u32 clear_color) +GPUDevice::PresentResult OpenGLDevice::BeginPresent(bool skip_present, u32 clear_color) { if (skip_present || m_window_info.type == WindowInfo::Type::Surfaceless) { @@ -750,7 +750,7 @@ bool OpenGLDevice::BeginPresent(bool skip_present, u32 clear_color) TrimTexturePool(); } - return false; + return PresentResult::SkipPresent; } glBindFramebuffer(GL_FRAMEBUFFER, 0); @@ -771,7 +771,7 @@ bool OpenGLDevice::BeginPresent(bool skip_present, u32 clear_color) m_last_scissor = window_rc; UpdateViewport(); UpdateScissor(); - return true; + return PresentResult::OK; } void OpenGLDevice::EndPresent(bool explicit_present) diff --git a/src/util/opengl_device.h b/src/util/opengl_device.h index 947d76ffc..dedd37083 100644 --- a/src/util/opengl_device.h +++ b/src/util/opengl_device.h @@ -104,7 +104,7 @@ public: void SetVSyncMode(GPUVSyncMode mode, bool allow_present_throttle) override; - bool BeginPresent(bool skip_present, u32 clear_color) override; + PresentResult BeginPresent(bool skip_present, u32 clear_color) override; void EndPresent(bool explicit_present) override; void SubmitPresent() override; diff --git a/src/util/postprocessing.cpp b/src/util/postprocessing.cpp index edc686079..fc7bad384 100644 --- a/src/util/postprocessing.cpp +++ b/src/util/postprocessing.cpp @@ -619,9 +619,9 @@ void PostProcessing::Chain::DestroyTextures() g_gpu_device->RecycleTexture(std::move(m_input_texture)); } -bool PostProcessing::Chain::Apply(GPUTexture* input_color, GPUTexture* input_depth, GPUTexture* final_target, - GSVector4i final_rect, s32 orig_width, s32 orig_height, s32 native_width, - s32 native_height) +GPUDevice::PresentResult PostProcessing::Chain::Apply(GPUTexture* input_color, GPUTexture* input_depth, + GPUTexture* final_target, GSVector4i final_rect, s32 orig_width, + s32 orig_height, s32 native_width, s32 native_height) { GL_SCOPE_FMT("{} Apply", m_section); @@ -634,10 +634,12 @@ bool PostProcessing::Chain::Apply(GPUTexture* input_color, GPUTexture* input_dep { const bool is_final = (stage.get() == m_stages.back().get()); - if (!stage->Apply(input_color, input_depth, is_final ? final_target : output, final_rect, orig_width, orig_height, - native_width, native_height, m_target_width, m_target_height)) + if (const GPUDevice::PresentResult pres = + stage->Apply(input_color, input_depth, is_final ? final_target : output, final_rect, orig_width, orig_height, + native_width, native_height, m_target_width, m_target_height); + pres != GPUDevice::PresentResult::OK) { - return false; + return pres; } if (!is_final) @@ -648,7 +650,7 @@ bool PostProcessing::Chain::Apply(GPUTexture* input_color, GPUTexture* input_dep } } - return true; + return GPUDevice::PresentResult::OK; } void PostProcessing::Initialize() diff --git a/src/util/postprocessing.h b/src/util/postprocessing.h index f91d7e504..662ec083a 100644 --- a/src/util/postprocessing.h +++ b/src/util/postprocessing.h @@ -134,8 +134,9 @@ public: bool CheckTargets(GPUTexture::Format target_format, u32 target_width, u32 target_height, ProgressCallback* progress = nullptr); - bool Apply(GPUTexture* input_color, GPUTexture* input_depth, GPUTexture* final_target, const GSVector4i final_rect, - s32 orig_width, s32 orig_height, s32 native_width, s32 native_height); + GPUDevice::PresentResult Apply(GPUTexture* input_color, GPUTexture* input_depth, GPUTexture* final_target, + const GSVector4i final_rect, s32 orig_width, s32 orig_height, s32 native_width, + s32 native_height); private: void ClearStagesWithError(const Error& error); diff --git a/src/util/postprocessing_shader.h b/src/util/postprocessing_shader.h index d0a0150b5..7cc266744 100644 --- a/src/util/postprocessing_shader.h +++ b/src/util/postprocessing_shader.h @@ -3,15 +3,14 @@ #pragma once -#include "postprocessing.h" - +#include "gpu_device.h" #include "gpu_texture.h" +#include "postprocessing.h" #include "common/gsvector.h" #include "common/settings_interface.h" #include "common/timer.h" #include "common/types.h" -#include "gpu_device.h" #include #include @@ -49,9 +48,9 @@ public: virtual bool CompilePipeline(GPUTexture::Format format, u32 width, u32 height, ProgressCallback* progress) = 0; - virtual bool Apply(GPUTexture* input_color, GPUTexture* input_depth, GPUTexture* final_target, GSVector4i final_rect, - s32 orig_width, s32 orig_height, s32 native_width, s32 native_height, u32 target_width, - u32 target_height) = 0; + virtual GPUDevice::PresentResult Apply(GPUTexture* input_color, GPUTexture* input_depth, GPUTexture* final_target, + GSVector4i final_rect, s32 orig_width, s32 orig_height, s32 native_width, + s32 native_height, u32 target_width, u32 target_height) = 0; protected: using OptionList = std::vector; diff --git a/src/util/postprocessing_shader_fx.cpp b/src/util/postprocessing_shader_fx.cpp index 164155765..9e95e530c 100644 --- a/src/util/postprocessing_shader_fx.cpp +++ b/src/util/postprocessing_shader_fx.cpp @@ -1491,9 +1491,10 @@ bool PostProcessing::ReShadeFXShader::ResizeOutput(GPUTexture::Format format, u3 return true; } -bool PostProcessing::ReShadeFXShader::Apply(GPUTexture* input_color, GPUTexture* input_depth, GPUTexture* final_target, - GSVector4i final_rect, s32 orig_width, s32 orig_height, s32 native_width, - s32 native_height, u32 target_width, u32 target_height) +GPUDevice::PresentResult PostProcessing::ReShadeFXShader::Apply(GPUTexture* input_color, GPUTexture* input_depth, + GPUTexture* final_target, GSVector4i final_rect, + s32 orig_width, s32 orig_height, s32 native_width, + s32 native_height, u32 target_width, u32 target_height) { GL_PUSH_FMT("PostProcessingShaderFX {}", m_name); @@ -1783,10 +1784,10 @@ bool PostProcessing::ReShadeFXShader::Apply(GPUTexture* input_color, GPUTexture* if (pass.render_targets.size() == 1 && pass.render_targets[0] == OUTPUT_COLOR_TEXTURE && !final_target) { // Special case: drawing to final buffer. - if (!g_gpu_device->BeginPresent(false)) + if (const GPUDevice::PresentResult pres = g_gpu_device->BeginPresent(false); pres != GPUDevice::PresentResult::OK) { GL_POP(); - return false; + return pres; } } else @@ -1842,5 +1843,5 @@ bool PostProcessing::ReShadeFXShader::Apply(GPUTexture* input_color, GPUTexture* GL_POP(); m_frame_timer.Reset(); - return true; + return GPUDevice::PresentResult::OK; } diff --git a/src/util/postprocessing_shader_fx.h b/src/util/postprocessing_shader_fx.h index af3909ff4..e265e4d40 100644 --- a/src/util/postprocessing_shader_fx.h +++ b/src/util/postprocessing_shader_fx.h @@ -31,9 +31,9 @@ public: bool ResizeOutput(GPUTexture::Format format, u32 width, u32 height) override; bool CompilePipeline(GPUTexture::Format format, u32 width, u32 height, ProgressCallback* progress) override; - bool Apply(GPUTexture* input_color, GPUTexture* input_depth, GPUTexture* final_target, GSVector4i final_rect, - s32 orig_width, s32 orig_height, s32 native_width, s32 native_height, u32 target_width, - u32 target_height) override; + GPUDevice::PresentResult Apply(GPUTexture* input_color, GPUTexture* input_depth, GPUTexture* final_target, + GSVector4i final_rect, s32 orig_width, s32 orig_height, s32 native_width, + s32 native_height, u32 target_width, u32 target_height) override; private: using TextureID = s32; diff --git a/src/util/postprocessing_shader_glsl.cpp b/src/util/postprocessing_shader_glsl.cpp index 51d45ca80..a6bf097cf 100644 --- a/src/util/postprocessing_shader_glsl.cpp +++ b/src/util/postprocessing_shader_glsl.cpp @@ -167,17 +167,18 @@ bool PostProcessing::GLSLShader::CompilePipeline(GPUTexture::Format format, u32 return true; } -bool PostProcessing::GLSLShader::Apply(GPUTexture* input_color, GPUTexture* input_depth, GPUTexture* final_target, - GSVector4i final_rect, s32 orig_width, s32 orig_height, s32 native_width, - s32 native_height, u32 target_width, u32 target_height) +GPUDevice::PresentResult PostProcessing::GLSLShader::Apply(GPUTexture* input_color, GPUTexture* input_depth, + GPUTexture* final_target, GSVector4i final_rect, + s32 orig_width, s32 orig_height, s32 native_width, + s32 native_height, u32 target_width, u32 target_height) { GL_SCOPE_FMT("GLSL Shader {}", m_name); // Assumes final stage has been cleared already. if (!final_target) { - if (!g_gpu_device->BeginPresent(false)) - return false; + if (const GPUDevice::PresentResult pres = g_gpu_device->BeginPresent(false); pres != GPUDevice::PresentResult::OK) + return pres; } else { @@ -196,7 +197,7 @@ bool PostProcessing::GLSLShader::Apply(GPUTexture* input_color, GPUTexture* inpu static_cast(PostProcessing::GetTimer().GetTimeSeconds())); g_gpu_device->UnmapUniformBuffer(uniforms_size); g_gpu_device->Draw(3, 0); - return true; + return GPUDevice::PresentResult::OK; } bool PostProcessing::GLSLShader::ResizeOutput(GPUTexture::Format format, u32 width, u32 height) diff --git a/src/util/postprocessing_shader_glsl.h b/src/util/postprocessing_shader_glsl.h index b1714ee3a..af58407a6 100644 --- a/src/util/postprocessing_shader_glsl.h +++ b/src/util/postprocessing_shader_glsl.h @@ -24,9 +24,9 @@ public: bool ResizeOutput(GPUTexture::Format format, u32 width, u32 height) override; bool CompilePipeline(GPUTexture::Format format, u32 width, u32 height, ProgressCallback* progress) override; - bool Apply(GPUTexture* input_color, GPUTexture* input_depth, GPUTexture* final_target, GSVector4i final_rect, - s32 orig_width, s32 orig_height, s32 native_width, s32 native_height, u32 target_width, - u32 target_height) override; + GPUDevice::PresentResult Apply(GPUTexture* input_color, GPUTexture* input_depth, GPUTexture* final_target, + GSVector4i final_rect, s32 orig_width, s32 orig_height, s32 native_width, + s32 native_height, u32 target_width, u32 target_height) override; private: struct CommonUniforms diff --git a/src/util/vulkan_device.cpp b/src/util/vulkan_device.cpp index 6afc64464..a020b1580 100644 --- a/src/util/vulkan_device.cpp +++ b/src/util/vulkan_device.cpp @@ -1286,6 +1286,9 @@ bool VulkanDevice::SetGPUTimingEnabled(bool enabled) void VulkanDevice::WaitForCommandBufferCompletion(u32 index) { + if (m_device_was_lost) + return; + // Wait for this command buffer to be completed. static constexpr u32 MAX_TIMEOUTS = 10; u32 timeouts = 0; @@ -1303,7 +1306,7 @@ void VulkanDevice::WaitForCommandBufferCompletion(u32 index) else if (res != VK_SUCCESS) { LOG_VULKAN_ERROR(res, TinyString::from_format("vkWaitForFences() for cmdbuffer {} failed: ", index)); - m_device_is_lost = true; + m_device_was_lost = true; return; } } @@ -1357,7 +1360,7 @@ void VulkanDevice::WaitForCommandBufferCompletion(u32 index) void VulkanDevice::EndAndSubmitCommandBuffer(VulkanSwapChain* present_swap_chain, bool explicit_present) { - if (m_device_is_lost) + if (m_device_was_lost) [[unlikely]] return; CommandBuffer& resources = m_frame_resources[m_current_frame]; @@ -1416,7 +1419,7 @@ void VulkanDevice::EndAndSubmitCommandBuffer(VulkanSwapChain* present_swap_chain if (res != VK_SUCCESS) { LOG_VULKAN_ERROR(res, "vkQueueSubmit failed: "); - m_device_is_lost = true; + m_device_was_lost = true; return; } @@ -2339,28 +2342,23 @@ void VulkanDevice::SetVSyncMode(GPUVSyncMode mode, bool allow_present_throttle) } } -bool VulkanDevice::BeginPresent(bool frame_skip, u32 clear_color) +GPUDevice::PresentResult VulkanDevice::BeginPresent(bool frame_skip, u32 clear_color) { if (InRenderPass()) EndRenderPass(); + if (m_device_was_lost) [[unlikely]] + return PresentResult::DeviceLost; + if (frame_skip) - return false; + return PresentResult::SkipPresent; // If we're running surfaceless, kick the command buffer so we don't run out of descriptors. if (!m_swap_chain) { SubmitCommandBuffer(false); TrimTexturePool(); - return false; - } - - // Check if the device was lost. - if (m_device_is_lost) - { - Panic("Fixme"); // TODO - TrimTexturePool(); - return false; + return PresentResult::SkipPresent; } VkResult res = m_swap_chain->AcquireNextImage(); @@ -2382,7 +2380,7 @@ bool VulkanDevice::BeginPresent(bool frame_skip, u32 clear_color) ERROR_LOG("Failed to recreate surface after loss"); SubmitCommandBuffer(false); TrimTexturePool(); - return false; + return PresentResult::SkipPresent; } res = m_swap_chain->AcquireNextImage(); @@ -2395,12 +2393,12 @@ bool VulkanDevice::BeginPresent(bool frame_skip, u32 clear_color) // Still submit the command buffer, otherwise we'll end up with several frames waiting. SubmitCommandBuffer(false); TrimTexturePool(); - return false; + return PresentResult::SkipPresent; } } BeginSwapChainRenderPass(clear_color); - return true; + return PresentResult::OK; } void VulkanDevice::EndPresent(bool explicit_present) @@ -2421,6 +2419,9 @@ void VulkanDevice::EndPresent(bool explicit_present) void VulkanDevice::SubmitPresent() { DebugAssert(m_swap_chain); + if (m_device_was_lost) [[unlikely]] + return; + QueuePresent(m_swap_chain.get()); } diff --git a/src/util/vulkan_device.h b/src/util/vulkan_device.h index 9f0f683d2..7d1545631 100644 --- a/src/util/vulkan_device.h +++ b/src/util/vulkan_device.h @@ -142,7 +142,7 @@ public: void SetVSyncMode(GPUVSyncMode mode, bool allow_present_throttle) override; - bool BeginPresent(bool skip_present, u32 clear_color) override; + PresentResult BeginPresent(bool skip_present, u32 clear_color) override; void EndPresent(bool explicit_present) override; void SubmitPresent() override; @@ -414,7 +414,7 @@ private: u64 m_completed_fence_counter = 0; u32 m_current_frame = 0; - bool m_device_is_lost = false; + bool m_device_was_lost = false; std::unordered_map m_render_pass_cache; GPUFramebufferManager m_framebuffer_manager;