GPUDevice: Add recovery from lost device

This commit is contained in:
Stenzek 2024-09-07 12:48:44 +10:00
parent 3a4ac13c26
commit 6336c4ee1f
No known key found for this signature in database
22 changed files with 181 additions and 96 deletions

View file

@ -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);

View file

@ -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);

View file

@ -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);

View file

@ -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<u8> 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<std::mutex> 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()

View file

@ -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)

View file

@ -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;

View file

@ -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<unsigned>(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<unsigned>(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<unsigned>(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<UINT>(m_vsync_mode == GPUVSyncMode::FIFO);
const UINT flags = (m_vsync_mode == GPUVSyncMode::Disabled && m_using_allow_tearing) ? DXGI_PRESENT_ALLOW_TEARING : 0;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -2312,17 +2312,17 @@ id<MTLBlitCommandEncoder> 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;
}
}

View file

@ -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)

View file

@ -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;

View file

@ -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()

View file

@ -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);

View file

@ -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 <array>
#include <string>
@ -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<ShaderOption>;

View file

@ -1491,8 +1491,9 @@ 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,
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;
}

View file

@ -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;

View file

@ -167,8 +167,9 @@ 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,
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);
@ -176,8 +177,8 @@ bool PostProcessing::GLSLShader::Apply(GPUTexture* input_color, GPUTexture* inpu
// 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<float>(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)

View file

@ -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

View file

@ -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());
}

View file

@ -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<RenderPassCacheKey, VkRenderPass, RenderPassCacheKeyHash> m_render_pass_cache;
GPUFramebufferManager<VkFramebuffer, CreateFramebuffer, DestroyFramebuffer> m_framebuffer_manager;