diff --git a/src/duckstation-libretro/libretro_host_interface.cpp b/src/duckstation-libretro/libretro_host_interface.cpp index b4cdea1ec..6088cb2b5 100644 --- a/src/duckstation-libretro/libretro_host_interface.cpp +++ b/src/duckstation-libretro/libretro_host_interface.cpp @@ -50,7 +50,15 @@ static void LibretroLogCallback(void* pUserParam, const char* channelName, const LibretroHostInterface::LibretroHostInterface() = default; -LibretroHostInterface::~LibretroHostInterface() = default; +LibretroHostInterface::~LibretroHostInterface() +{ + // should be cleaned up by the context destroy, but just in case + if (m_hw_render_display) + { + m_hw_render_display->DestroyRenderDevice(); + m_hw_render_display.reset(); + } +} void LibretroHostInterface::InitLogging() { @@ -682,6 +690,27 @@ static std::optional RetroHwContextToRenderer(retro_hw_context_type } } +static std::optional RenderAPIToRenderer(HostDisplay::RenderAPI api) +{ + switch (api) + { + case HostDisplay::RenderAPI::OpenGL: + case HostDisplay::RenderAPI::OpenGLES: + return GPURenderer::HardwareOpenGL; + + case HostDisplay::RenderAPI::Vulkan: + return GPURenderer::HardwareVulkan; + +#ifdef WIN32 + case HostDisplay::RenderAPI::D3D11: + return GPURenderer::HardwareD3D11; +#endif + + default: + return std::nullopt; + } +} + bool LibretroHostInterface::RequestHardwareRendererContext() { GPURenderer renderer = Settings::DEFAULT_GPU_RENDERER; @@ -757,49 +786,59 @@ void LibretroHostInterface::HardwareRendererContextReset() void LibretroHostInterface::SwitchToHardwareRenderer() { - std::optional renderer = RetroHwContextToRenderer(m_hw_render_callback.context_type); - if (!renderer.has_value()) + // use the existing device if we just resized the window + std::optional renderer; + std::unique_ptr display = std::move(m_hw_render_display); + if (display) { - Log_ErrorPrintf("Unknown context type %u", static_cast(m_hw_render_callback.context_type)); - return; + Log_InfoPrintf("Using existing hardware display"); + renderer = RenderAPIToRenderer(display->GetRenderAPI()); } - - std::unique_ptr display = nullptr; - switch (renderer.value()) + else { - case GPURenderer::HardwareOpenGL: - display = std::make_unique(); - break; + renderer = RetroHwContextToRenderer(m_hw_render_callback.context_type); + if (!renderer.has_value()) + { + Log_ErrorPrintf("Unknown context type %u", static_cast(m_hw_render_callback.context_type)); + return; + } - case GPURenderer::HardwareVulkan: - display = std::make_unique(); - break; + switch (renderer.value()) + { + case GPURenderer::HardwareOpenGL: + display = std::make_unique(); + break; + + case GPURenderer::HardwareVulkan: + display = std::make_unique(); + break; #ifdef WIN32 - case GPURenderer::HardwareD3D11: - display = std::make_unique(); - break; + case GPURenderer::HardwareD3D11: + display = std::make_unique(); + break; #endif - default: - Log_ErrorPrintf("Unhandled renderer '%s'", Settings::GetRendererName(renderer.value())); + default: + Log_ErrorPrintf("Unhandled renderer '%s'", Settings::GetRendererName(renderer.value())); + return; + } + + struct retro_system_av_info avi; + g_libretro_host_interface.GetSystemAVInfo(&avi, true); + + WindowInfo wi; + wi.type = WindowInfo::Type::Libretro; + wi.display_connection = &g_libretro_host_interface.m_hw_render_callback; + wi.surface_width = avi.geometry.base_width; + wi.surface_height = avi.geometry.base_height; + wi.surface_scale = 1.0f; + if (!display || !display->CreateRenderDevice(wi, {}, g_libretro_host_interface.m_settings.gpu_use_debug_device) || + !display->InitializeRenderDevice({}, m_settings.gpu_use_debug_device)) + { + Log_ErrorPrintf("Failed to create hardware host display"); return; - } - - struct retro_system_av_info avi; - g_libretro_host_interface.GetSystemAVInfo(&avi, true); - - WindowInfo wi; - wi.type = WindowInfo::Type::Libretro; - wi.display_connection = &g_libretro_host_interface.m_hw_render_callback; - wi.surface_width = avi.geometry.base_width; - wi.surface_height = avi.geometry.base_height; - wi.surface_scale = 1.0f; - if (!display || !display->CreateRenderDevice(wi, {}, g_libretro_host_interface.m_settings.gpu_use_debug_device) || - !display->InitializeRenderDevice({}, m_settings.gpu_use_debug_device)) - { - Log_ErrorPrintf("Failed to create hardware host display"); - return; + } } std::swap(display, g_libretro_host_interface.m_display); @@ -810,21 +849,31 @@ void LibretroHostInterface::SwitchToHardwareRenderer() void LibretroHostInterface::HardwareRendererContextDestroy() { - g_libretro_host_interface.m_hw_render_callback_valid = false; - // switch back to software if (g_libretro_host_interface.m_using_hardware_renderer) { Log_InfoPrintf("Lost hardware renderer context, switching to software renderer"); g_libretro_host_interface.SwitchToSoftwareRenderer(); } + + if (g_libretro_host_interface.m_hw_render_display) + { + g_libretro_host_interface.m_hw_render_display->DestroyRenderDevice(); + g_libretro_host_interface.m_hw_render_display.reset(); + } + + g_libretro_host_interface.m_hw_render_callback_valid = false; } void LibretroHostInterface::SwitchToSoftwareRenderer() { - std::unique_ptr display = std::make_unique(); - std::swap(display, g_libretro_host_interface.m_display); - g_libretro_host_interface.m_system->RecreateGPU(GPURenderer::Software); - display->DestroyRenderDevice(); - m_using_hardware_renderer = false; + // keep the hw renderer around in case we need it later + if (m_using_hardware_renderer) + { + m_hw_render_display = std::move(m_display); + m_using_hardware_renderer = false; + } + + m_display = std::make_unique(); + m_system->RecreateGPU(GPURenderer::Software); } diff --git a/src/duckstation-libretro/libretro_host_interface.h b/src/duckstation-libretro/libretro_host_interface.h index 550027031..030704fb3 100644 --- a/src/duckstation-libretro/libretro_host_interface.h +++ b/src/duckstation-libretro/libretro_host_interface.h @@ -65,6 +65,7 @@ private: static void HardwareRendererContextDestroy(); retro_hw_render_callback m_hw_render_callback = {}; + std::unique_ptr m_hw_render_display; bool m_hw_render_callback_valid = false; bool m_using_hardware_renderer = false; };