Qt: Refactor render widget state transitions

Recreate widget each time. Fixes fullscreen mode switches on D3D11 and
hopefully Wayland.
This commit is contained in:
Connor McLaughlin 2020-04-22 21:13:51 +10:00
parent d7aa514f14
commit ea3c0b65cf
16 changed files with 550 additions and 459 deletions

View File

@ -29,10 +29,12 @@ add_executable(duckstation-qt
mainwindow.cpp mainwindow.cpp
mainwindow.h mainwindow.h
mainwindow.ui mainwindow.ui
opengldisplaywidget.cpp openglhostdisplay.cpp
opengldisplaywidget.h openglhostdisplay.h
portsettingswidget.cpp portsettingswidget.cpp
portsettingswidget.h portsettingswidget.h
qthostdisplay.cpp
qthostdisplay.h
qtdisplaywidget.cpp qtdisplaywidget.cpp
qtdisplaywidget.h qtdisplaywidget.h
qthostinterface.cpp qthostinterface.cpp
@ -52,8 +54,8 @@ target_link_libraries(duckstation-qt PRIVATE frontend-common core common imgui g
if(WIN32) if(WIN32)
target_sources(duckstation-qt PRIVATE target_sources(duckstation-qt PRIVATE
d3d11displaywidget.cpp d3d11hostdisplay.cpp
d3d11displaywidget.h d3d11hostdisplay.h
) )
target_link_libraries(duckstation PRIVATE d3d11.lib dxgi.lib) target_link_libraries(duckstation PRIVATE d3d11.lib dxgi.lib)
endif() endif()

View File

@ -1,14 +1,15 @@
#include "d3d11displaywidget.h" #include "d3d11hostdisplay.h"
#include "common/assert.h" #include "common/assert.h"
#include "common/d3d11/shader_compiler.h" #include "common/d3d11/shader_compiler.h"
#include "common/log.h" #include "common/log.h"
#include "frontend-common/display_ps.hlsl.h" #include "frontend-common/display_ps.hlsl.h"
#include "frontend-common/display_vs.hlsl.h" #include "frontend-common/display_vs.hlsl.h"
#include "qtdisplaywidget.h"
#include <array> #include <array>
#include <dxgi1_5.h> #include <dxgi1_5.h>
#include <imgui.h> #include <imgui.h>
#include <imgui_impl_dx11.h> #include <imgui_impl_dx11.h>
Log_SetChannel(D3D11DisplayWidget); Log_SetChannel(D3D11HostDisplay);
class D3D11DisplayWidgetTexture : public HostDisplayTexture class D3D11DisplayWidgetTexture : public HostDisplayTexture
{ {
@ -61,50 +62,32 @@ private:
bool m_dynamic; bool m_dynamic;
}; };
D3D11DisplayWidget::D3D11DisplayWidget(QtHostInterface* host_interface, QWidget* parent) D3D11HostDisplay::D3D11HostDisplay(QtHostInterface* host_interface) : QtHostDisplay(host_interface) {}
: QtDisplayWidget(host_interface, parent)
{
}
D3D11DisplayWidget::~D3D11DisplayWidget() = default; D3D11HostDisplay::~D3D11HostDisplay() = default;
HostDisplay* D3D11DisplayWidget::getHostDisplayInterface() HostDisplay::RenderAPI D3D11HostDisplay::GetRenderAPI() const
{
return this;
}
HostDisplay::RenderAPI D3D11DisplayWidget::GetRenderAPI() const
{ {
return HostDisplay::RenderAPI::D3D11; return HostDisplay::RenderAPI::D3D11;
} }
void* D3D11DisplayWidget::GetRenderDevice() const void* D3D11HostDisplay::GetRenderDevice() const
{ {
return m_device.Get(); return m_device.Get();
} }
void* D3D11DisplayWidget::GetRenderContext() const void* D3D11HostDisplay::GetRenderContext() const
{ {
return m_context.Get(); return m_context.Get();
} }
void* D3D11DisplayWidget::GetRenderWindow() const std::unique_ptr<HostDisplayTexture> D3D11HostDisplay::CreateTexture(u32 width, u32 height, const void* initial_data,
{
return const_cast<QWidget*>(static_cast<const QWidget*>(this));
}
void D3D11DisplayWidget::ChangeRenderWindow(void* new_window)
{
Panic("Not supported");
}
std::unique_ptr<HostDisplayTexture> D3D11DisplayWidget::CreateTexture(u32 width, u32 height, const void* initial_data,
u32 initial_data_stride, bool dynamic) u32 initial_data_stride, bool dynamic)
{ {
return D3D11DisplayWidgetTexture::Create(m_device.Get(), width, height, initial_data, initial_data_stride, dynamic); return D3D11DisplayWidgetTexture::Create(m_device.Get(), width, height, initial_data, initial_data_stride, dynamic);
} }
void D3D11DisplayWidget::UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height, void D3D11HostDisplay::UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height,
const void* texture_data, u32 texture_data_stride) const void* texture_data, u32 texture_data_stride)
{ {
D3D11DisplayWidgetTexture* d3d11_texture = static_cast<D3D11DisplayWidgetTexture*>(texture); D3D11DisplayWidgetTexture* d3d11_texture = static_cast<D3D11DisplayWidgetTexture*>(texture);
@ -141,8 +124,8 @@ void D3D11DisplayWidget::UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y
} }
} }
bool D3D11DisplayWidget::DownloadTexture(const void* texture_handle, u32 x, u32 y, u32 width, u32 height, bool D3D11HostDisplay::DownloadTexture(const void* texture_handle, u32 x, u32 y, u32 width, u32 height, void* out_data,
void* out_data, u32 out_data_stride) u32 out_data_stride)
{ {
ID3D11ShaderResourceView* srv = ID3D11ShaderResourceView* srv =
const_cast<ID3D11ShaderResourceView*>(static_cast<const ID3D11ShaderResourceView*>(texture_handle)); const_cast<ID3D11ShaderResourceView*>(static_cast<const ID3D11ShaderResourceView*>(texture_handle));
@ -159,17 +142,17 @@ bool D3D11DisplayWidget::DownloadTexture(const void* texture_handle, u32 x, u32
static_cast<u32*>(out_data)); static_cast<u32*>(out_data));
} }
void D3D11DisplayWidget::SetVSync(bool enabled) void D3D11HostDisplay::SetVSync(bool enabled)
{ {
m_vsync = enabled; m_vsync = enabled;
} }
bool D3D11DisplayWidget::hasDeviceContext() const bool D3D11HostDisplay::hasDeviceContext() const
{ {
return static_cast<bool>(m_device); return static_cast<bool>(m_device);
} }
bool D3D11DisplayWidget::createDeviceContext(QThread* worker_thread, bool debug_device) bool D3D11HostDisplay::createDeviceContext(bool debug_device)
{ {
UINT create_flags = 0; UINT create_flags = 0;
if (debug_device) if (debug_device)
@ -231,49 +214,34 @@ bool D3D11DisplayWidget::createDeviceContext(QThread* worker_thread, bool debug_
m_allow_tearing_supported = (allow_tearing_supported == TRUE); m_allow_tearing_supported = (allow_tearing_supported == TRUE);
} }
if (!createSwapChain()) return true;
return false; }
if (!QtDisplayWidget::createDeviceContext(worker_thread, debug_device)) void D3D11HostDisplay::destroyDeviceContext()
{ {
QtHostDisplay::destroyDeviceContext();
m_swap_chain.Reset(); m_swap_chain.Reset();
m_context.Reset(); m_context.Reset();
m_device.Reset(); m_device.Reset();
} }
return true; bool D3D11HostDisplay::shouldUseFlipModelSwapChain() const
}
bool D3D11DisplayWidget::initializeDeviceContext(bool debug_device)
{
if (!createSwapChainRTV())
return false;
if (!QtDisplayWidget::initializeDeviceContext(debug_device))
return false;
return true;
}
void D3D11DisplayWidget::destroyDeviceContext()
{
QtDisplayWidget::destroyDeviceContext();
m_swap_chain.Reset();
m_context.Reset();
m_device.Reset();
}
bool D3D11DisplayWidget::shouldUseFlipModelSwapChain() const
{ {
// For some reason DXGI gets stuck waiting for some kernel object when the Qt window has a parent (render-to-main) on // For some reason DXGI gets stuck waiting for some kernel object when the Qt window has a parent (render-to-main) on
// some computers, unless the window is completely occluded. The legacy swap chain mode does not have this problem. // some computers, unless the window is completely occluded. The legacy swap chain mode does not have this problem.
return parent() == nullptr; return m_widget->parent() == nullptr;
} }
bool D3D11DisplayWidget::createSwapChain() bool D3D11HostDisplay::createSurface()
{ {
m_using_flip_model_swap_chain = shouldUseFlipModelSwapChain(); m_using_flip_model_swap_chain = shouldUseFlipModelSwapChain();
const HWND window_hwnd = reinterpret_cast<HWND>(m_widget->winId());
RECT client_rc{};
GetClientRect(window_hwnd, &client_rc);
m_window_width = client_rc.right - client_rc.left;
m_window_height = client_rc.bottom - client_rc.top;
DXGI_SWAP_CHAIN_DESC swap_chain_desc = {}; DXGI_SWAP_CHAIN_DESC swap_chain_desc = {};
swap_chain_desc.BufferDesc.Width = m_window_width; swap_chain_desc.BufferDesc.Width = m_window_width;
swap_chain_desc.BufferDesc.Height = m_window_height; swap_chain_desc.BufferDesc.Height = m_window_height;
@ -281,7 +249,7 @@ bool D3D11DisplayWidget::createSwapChain()
swap_chain_desc.SampleDesc.Count = 1; swap_chain_desc.SampleDesc.Count = 1;
swap_chain_desc.BufferCount = 3; swap_chain_desc.BufferCount = 3;
swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swap_chain_desc.OutputWindow = reinterpret_cast<HWND>(winId()); swap_chain_desc.OutputWindow = window_hwnd;
swap_chain_desc.Windowed = TRUE; swap_chain_desc.Windowed = TRUE;
swap_chain_desc.SwapEffect = m_using_flip_model_swap_chain ? DXGI_SWAP_EFFECT_FLIP_DISCARD : DXGI_SWAP_EFFECT_DISCARD; swap_chain_desc.SwapEffect = m_using_flip_model_swap_chain ? DXGI_SWAP_EFFECT_FLIP_DISCARD : DXGI_SWAP_EFFECT_DISCARD;
@ -289,6 +257,10 @@ bool D3D11DisplayWidget::createSwapChain()
if (m_using_allow_tearing) if (m_using_allow_tearing)
swap_chain_desc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING; swap_chain_desc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;
Log_InfoPrintf("Creating a %dx%d %s %s swap chain", m_window_width, m_window_height,
m_using_flip_model_swap_chain ? "flip-discard" : "discard",
swap_chain_desc.Windowed ? "windowed" : "full-screen");
HRESULT hr = m_dxgi_factory->CreateSwapChain(m_device.Get(), &swap_chain_desc, m_swap_chain.GetAddressOf()); HRESULT hr = m_dxgi_factory->CreateSwapChain(m_device.Get(), &swap_chain_desc, m_swap_chain.GetAddressOf());
if (FAILED(hr) && m_using_flip_model_swap_chain) if (FAILED(hr) && m_using_flip_model_swap_chain)
{ {
@ -310,44 +282,14 @@ bool D3D11DisplayWidget::createSwapChain()
if (FAILED(hr)) if (FAILED(hr))
Log_WarningPrintf("MakeWindowAssociation() to disable ALT+ENTER failed"); Log_WarningPrintf("MakeWindowAssociation() to disable ALT+ENTER failed");
if (!createSwapChainRTV())
return false;
emit m_widget->windowResizedEvent(m_window_width, m_window_height);
return true; return true;
} }
void D3D11DisplayWidget::recreateSwapChain() bool D3D11HostDisplay::createSwapChainRTV()
{
m_swap_chain_rtv.Reset();
m_swap_chain.Reset();
if (!createSwapChain() || !createSwapChainRTV())
Panic("Failed to recreate swap chain");
}
void D3D11DisplayWidget::windowResized(s32 new_window_width, s32 new_window_height)
{
QtDisplayWidget::windowResized(new_window_width, new_window_height);
HostDisplay::WindowResized(new_window_width, new_window_height);
if (!m_swap_chain)
return;
if (m_using_flip_model_swap_chain != shouldUseFlipModelSwapChain())
{
recreateSwapChain();
return;
}
m_swap_chain_rtv.Reset();
HRESULT hr = m_swap_chain->ResizeBuffers(0, new_window_width, new_window_height, DXGI_FORMAT_UNKNOWN,
m_using_allow_tearing ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0);
if (FAILED(hr))
Log_ErrorPrintf("ResizeBuffers() failed: 0x%08X", hr);
if (!createSwapChainRTV())
Panic("Failed to recreate swap chain RTV after resize");
}
bool D3D11DisplayWidget::createSwapChainRTV()
{ {
ComPtr<ID3D11Texture2D> backbuffer; ComPtr<ID3D11Texture2D> backbuffer;
HRESULT hr = m_swap_chain->GetBuffer(0, IID_PPV_ARGS(backbuffer.GetAddressOf())); HRESULT hr = m_swap_chain->GetBuffer(0, IID_PPV_ARGS(backbuffer.GetAddressOf()));
@ -372,7 +314,32 @@ bool D3D11DisplayWidget::createSwapChainRTV()
return true; return true;
} }
bool D3D11DisplayWidget::createDeviceResources() void D3D11HostDisplay::destroySurface()
{
m_swap_chain_rtv.Reset();
m_swap_chain.Reset();
QtHostDisplay::destroySurface();
}
void D3D11HostDisplay::WindowResized(s32 new_window_width, s32 new_window_height)
{
QtHostDisplay::WindowResized(new_window_width, new_window_height);
if (!m_swap_chain)
return;
m_swap_chain_rtv.Reset();
HRESULT hr = m_swap_chain->ResizeBuffers(0, new_window_width, new_window_height, DXGI_FORMAT_UNKNOWN,
m_using_allow_tearing ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0);
if (FAILED(hr))
Log_ErrorPrintf("ResizeBuffers() failed: 0x%08X", hr);
if (!createSwapChainRTV())
Panic("Failed to recreate swap chain RTV after resize");
}
bool D3D11HostDisplay::createDeviceResources()
{ {
HRESULT hr; HRESULT hr;
@ -418,9 +385,9 @@ bool D3D11DisplayWidget::createDeviceResources()
return true; return true;
} }
void D3D11DisplayWidget::destroyDeviceResources() void D3D11HostDisplay::destroyDeviceResources()
{ {
QtDisplayWidget::destroyDeviceResources(); QtHostDisplay::destroyDeviceResources();
m_display_uniform_buffer.Release(); m_display_uniform_buffer.Release();
m_swap_chain_rtv.Reset(); m_swap_chain_rtv.Reset();
@ -433,9 +400,9 @@ void D3D11DisplayWidget::destroyDeviceResources()
m_display_rasterizer_state.Reset(); m_display_rasterizer_state.Reset();
} }
bool D3D11DisplayWidget::createImGuiContext() bool D3D11HostDisplay::createImGuiContext()
{ {
if (!QtDisplayWidget::createImGuiContext()) if (!QtHostDisplay::createImGuiContext())
return false; return false;
if (!ImGui_ImplDX11_Init(m_device.Get(), m_context.Get())) if (!ImGui_ImplDX11_Init(m_device.Get(), m_context.Get()))
@ -446,13 +413,13 @@ bool D3D11DisplayWidget::createImGuiContext()
return true; return true;
} }
void D3D11DisplayWidget::destroyImGuiContext() void D3D11HostDisplay::destroyImGuiContext()
{ {
ImGui_ImplDX11_Shutdown(); ImGui_ImplDX11_Shutdown();
QtDisplayWidget::destroyImGuiContext(); QtHostDisplay::destroyImGuiContext();
} }
void D3D11DisplayWidget::Render() void D3D11HostDisplay::Render()
{ {
static constexpr std::array<float, 4> clear_color = {}; static constexpr std::array<float, 4> clear_color = {};
m_context->ClearRenderTargetView(m_swap_chain_rtv.Get(), clear_color.data()); m_context->ClearRenderTargetView(m_swap_chain_rtv.Get(), clear_color.data());
@ -472,12 +439,13 @@ void D3D11DisplayWidget::Render()
ImGui_ImplDX11_NewFrame(); ImGui_ImplDX11_NewFrame();
} }
void D3D11DisplayWidget::renderDisplay() void D3D11HostDisplay::renderDisplay()
{ {
if (!m_display_texture_handle) if (!m_display_texture_handle)
return; return;
auto [vp_left, vp_top, vp_width, vp_height] = CalculateDrawRect(m_window_width, m_window_height, m_display_top_margin); auto [vp_left, vp_top, vp_width, vp_height] =
CalculateDrawRect(m_window_width, m_window_height, m_display_top_margin);
m_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); m_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
m_context->VSSetShader(m_display_vertex_shader.Get(), nullptr, 0); m_context->VSSetShader(m_display_vertex_shader.Get(), nullptr, 0);

View File

@ -4,37 +4,31 @@
#include "common/d3d11/texture.h" #include "common/d3d11/texture.h"
#include "common/windows_headers.h" #include "common/windows_headers.h"
#include "core/host_display.h" #include "core/host_display.h"
#include "qtdisplaywidget.h" #include "qthostdisplay.h"
#include <d3d11.h> #include <d3d11.h>
#include <dxgi.h> #include <dxgi.h>
#include <memory> #include <memory>
#include <wrl/client.h> #include <wrl/client.h>
class D3D11DisplayWidget final : public QtDisplayWidget, private HostDisplay class D3D11HostDisplay final : public QtHostDisplay
{ {
Q_OBJECT
public: public:
template<typename T> template<typename T>
using ComPtr = Microsoft::WRL::ComPtr<T>; using ComPtr = Microsoft::WRL::ComPtr<T>;
D3D11DisplayWidget(QtHostInterface* host_interface, QWidget* parent); D3D11HostDisplay(QtHostInterface* host_interface);
~D3D11DisplayWidget(); ~D3D11HostDisplay();
HostDisplay* getHostDisplayInterface() override;
bool hasDeviceContext() const override; bool hasDeviceContext() const override;
bool createDeviceContext(QThread* worker_thread, bool debug_device) override; bool createDeviceContext(bool debug_device) override;
bool initializeDeviceContext(bool debug_device) override;
void destroyDeviceContext() override; void destroyDeviceContext() override;
bool createSurface() override;
void destroySurface() override;
RenderAPI GetRenderAPI() const override; RenderAPI GetRenderAPI() const override;
void* GetRenderDevice() const override; void* GetRenderDevice() const override;
void* GetRenderContext() const override; void* GetRenderContext() const override;
void* GetRenderWindow() const override; void WindowResized(s32 new_window_width, s32 new_window_height) override;
void ChangeRenderWindow(void* new_window) override;
void windowResized(s32 new_window_width, s32 new_window_height) override;
std::unique_ptr<HostDisplayTexture> CreateTexture(u32 width, u32 height, const void* initial_data, std::unique_ptr<HostDisplayTexture> CreateTexture(u32 width, u32 height, const void* initial_data,
u32 initial_data_stride, bool dynamic) override; u32 initial_data_stride, bool dynamic) override;
@ -56,9 +50,7 @@ private:
void destroyDeviceResources() override; void destroyDeviceResources() override;
bool shouldUseFlipModelSwapChain() const; bool shouldUseFlipModelSwapChain() const;
bool createSwapChain();
bool createSwapChainRTV(); bool createSwapChainRTV();
void recreateSwapChain();
void renderDisplay(); void renderDisplay();

View File

@ -37,7 +37,7 @@
<ItemGroup> <ItemGroup>
<ClCompile Include="audiosettingswidget.cpp" /> <ClCompile Include="audiosettingswidget.cpp" />
<ClCompile Include="consolesettingswidget.cpp" /> <ClCompile Include="consolesettingswidget.cpp" />
<ClCompile Include="d3d11displaywidget.cpp" /> <ClCompile Include="d3d11hostdisplay.cpp" />
<ClCompile Include="generalsettingswidget.cpp" /> <ClCompile Include="generalsettingswidget.cpp" />
<ClCompile Include="gpusettingswidget.cpp" /> <ClCompile Include="gpusettingswidget.cpp" />
<ClCompile Include="hotkeysettingswidget.cpp" /> <ClCompile Include="hotkeysettingswidget.cpp" />
@ -47,8 +47,9 @@
<ClCompile Include="gamelistwidget.cpp" /> <ClCompile Include="gamelistwidget.cpp" />
<ClCompile Include="main.cpp" /> <ClCompile Include="main.cpp" />
<ClCompile Include="mainwindow.cpp" /> <ClCompile Include="mainwindow.cpp" />
<ClCompile Include="opengldisplaywidget.cpp" /> <ClCompile Include="openglhostdisplay.cpp" />
<ClCompile Include="portsettingswidget.cpp" /> <ClCompile Include="portsettingswidget.cpp" />
<ClCompile Include="qthostdisplay.cpp" />
<ClCompile Include="qthostinterface.cpp" /> <ClCompile Include="qthostinterface.cpp" />
<ClCompile Include="qtprogresscallback.cpp" /> <ClCompile Include="qtprogresscallback.cpp" />
<ClCompile Include="qtsettingsinterface.cpp" /> <ClCompile Include="qtsettingsinterface.cpp" />
@ -63,14 +64,15 @@
<QtMoc Include="gpusettingswidget.h" /> <QtMoc Include="gpusettingswidget.h" />
<QtMoc Include="hotkeysettingswidget.h" /> <QtMoc Include="hotkeysettingswidget.h" />
<QtMoc Include="inputbindingwidgets.h" /> <QtMoc Include="inputbindingwidgets.h" />
<QtMoc Include="d3d11displaywidget.h" /> <ClInclude Include="d3d11hostdisplay.h" />
<QtMoc Include="qtprogresscallback.h" /> <QtMoc Include="qtprogresscallback.h" />
<ClInclude Include="qthostdisplay.h" />
<ClInclude Include="settingwidgetbinder.h" /> <ClInclude Include="settingwidgetbinder.h" />
<QtMoc Include="consolesettingswidget.h" /> <QtMoc Include="consolesettingswidget.h" />
<QtMoc Include="gamelistsettingswidget.h" /> <QtMoc Include="gamelistsettingswidget.h" />
<QtMoc Include="gamelistwidget.h" /> <QtMoc Include="gamelistwidget.h" />
<QtMoc Include="mainwindow.h" /> <QtMoc Include="mainwindow.h" />
<QtMoc Include="opengldisplaywidget.h" /> <ClInclude Include="openglhostdisplay.h" />
<QtMoc Include="qthostinterface.h" /> <QtMoc Include="qthostinterface.h" />
<ClInclude Include="qtsettingsinterface.h" /> <ClInclude Include="qtsettingsinterface.h" />
<ClInclude Include="qtutils.h" /> <ClInclude Include="qtutils.h" />
@ -127,7 +129,6 @@
<ItemGroup> <ItemGroup>
<ClCompile Include="$(IntDir)moc_audiosettingswidget.cpp" /> <ClCompile Include="$(IntDir)moc_audiosettingswidget.cpp" />
<ClCompile Include="$(IntDir)moc_consolesettingswidget.cpp" /> <ClCompile Include="$(IntDir)moc_consolesettingswidget.cpp" />
<ClCompile Include="$(IntDir)moc_d3d11displaywidget.cpp" />
<ClCompile Include="$(IntDir)moc_gamelistsettingswidget.cpp" /> <ClCompile Include="$(IntDir)moc_gamelistsettingswidget.cpp" />
<ClCompile Include="$(IntDir)moc_gamelistwidget.cpp" /> <ClCompile Include="$(IntDir)moc_gamelistwidget.cpp" />
<ClCompile Include="$(IntDir)moc_generalsettingswidget.cpp" /> <ClCompile Include="$(IntDir)moc_generalsettingswidget.cpp" />
@ -135,7 +136,6 @@
<ClCompile Include="$(IntDir)moc_hotkeysettingswidget.cpp" /> <ClCompile Include="$(IntDir)moc_hotkeysettingswidget.cpp" />
<ClCompile Include="$(IntDir)moc_inputbindingwidgets.cpp" /> <ClCompile Include="$(IntDir)moc_inputbindingwidgets.cpp" />
<ClCompile Include="$(IntDir)moc_mainwindow.cpp" /> <ClCompile Include="$(IntDir)moc_mainwindow.cpp" />
<ClCompile Include="$(IntDir)moc_opengldisplaywidget.cpp" />
<ClCompile Include="$(IntDir)moc_portsettingswidget.cpp" /> <ClCompile Include="$(IntDir)moc_portsettingswidget.cpp" />
<ClCompile Include="$(IntDir)moc_qtdisplaywidget.cpp" /> <ClCompile Include="$(IntDir)moc_qtdisplaywidget.cpp" />
<ClCompile Include="$(IntDir)moc_qthostinterface.cpp" /> <ClCompile Include="$(IntDir)moc_qthostinterface.cpp" />

View File

@ -27,8 +27,6 @@
<ClCompile Include="$(IntDir)moc_inputbindingwidgets.cpp" /> <ClCompile Include="$(IntDir)moc_inputbindingwidgets.cpp" />
<ClCompile Include="audiosettingswidget.cpp" /> <ClCompile Include="audiosettingswidget.cpp" />
<ClCompile Include="$(IntDir)moc_audiosettingswidget.cpp" /> <ClCompile Include="$(IntDir)moc_audiosettingswidget.cpp" />
<ClCompile Include="opengldisplaywidget.cpp" />
<ClCompile Include="d3d11displaywidget.cpp" />
<ClCompile Include="$(IntDir)moc_d3d11displaywidget.cpp" /> <ClCompile Include="$(IntDir)moc_d3d11displaywidget.cpp" />
<ClCompile Include="$(IntDir)moc_opengldisplaywidget.cpp" /> <ClCompile Include="$(IntDir)moc_opengldisplaywidget.cpp" />
<ClCompile Include="$(IntDir)moc_qtdisplaywidget.cpp" /> <ClCompile Include="$(IntDir)moc_qtdisplaywidget.cpp" />
@ -37,11 +35,15 @@
<ClCompile Include="$(IntDir)moc_qtprogresscallback.cpp" /> <ClCompile Include="$(IntDir)moc_qtprogresscallback.cpp" />
<ClCompile Include="generalsettingswidget.cpp" /> <ClCompile Include="generalsettingswidget.cpp" />
<ClCompile Include="$(IntDir)moc_generalsettingswidget.cpp" /> <ClCompile Include="$(IntDir)moc_generalsettingswidget.cpp" />
<ClCompile Include="qthostdisplay.cpp" />
<ClCompile Include="openglhostdisplay.cpp" />
<ClCompile Include="d3d11hostdisplay.cpp" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="qtsettingsinterface.h" /> <ClInclude Include="qtsettingsinterface.h" />
<ClInclude Include="qtutils.h" /> <ClInclude Include="qtutils.h" />
<ClInclude Include="settingwidgetbinder.h" /> <ClInclude Include="settingwidgetbinder.h" />
<ClInclude Include="qthostdisplay.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Filter Include="resources"> <Filter Include="resources">
@ -63,11 +65,11 @@
<QtMoc Include="hotkeysettingswidget.h" /> <QtMoc Include="hotkeysettingswidget.h" />
<QtMoc Include="inputbindingwidgets.h" /> <QtMoc Include="inputbindingwidgets.h" />
<QtMoc Include="audiosettingswidget.h" /> <QtMoc Include="audiosettingswidget.h" />
<QtMoc Include="opengldisplaywidget.h" />
<QtMoc Include="d3d11displaywidget.h" />
<QtMoc Include="qtdisplaywidget.h" /> <QtMoc Include="qtdisplaywidget.h" />
<QtMoc Include="generalsettingswidget.h" /> <QtMoc Include="generalsettingswidget.h" />
<QtMoc Include="qtprogresscallback.h" /> <QtMoc Include="qtprogresscallback.h" />
<QtMoc Include="openglhostdisplay.h" />
<QtMoc Include="d3d11hostdisplay.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<QtUi Include="consolesettingswidget.ui" /> <QtUi Include="consolesettingswidget.ui" />

View File

@ -9,7 +9,7 @@
static void InitLogging() static void InitLogging()
{ {
// set log flags // set log flags
#ifdef _DEBUG #ifdef _DEBUGA
Log::SetConsoleOutputParams(true, nullptr, LOGLEVEL_DEBUG); Log::SetConsoleOutputParams(true, nullptr, LOGLEVEL_DEBUG);
Log::SetFilterLevel(LOGLEVEL_DEBUG); Log::SetFilterLevel(LOGLEVEL_DEBUG);
#else #else

View File

@ -6,6 +6,7 @@
#include "gamelistsettingswidget.h" #include "gamelistsettingswidget.h"
#include "gamelistwidget.h" #include "gamelistwidget.h"
#include "qtdisplaywidget.h" #include "qtdisplaywidget.h"
#include "qthostdisplay.h"
#include "qthostinterface.h" #include "qthostinterface.h"
#include "qtsettingsinterface.h" #include "qtsettingsinterface.h"
#include "scmversion/scmversion.h" #include "scmversion/scmversion.h"
@ -66,17 +67,15 @@ bool MainWindow::confirmMessage(const QString& message)
return (result == QMessageBox::Yes); return (result == QMessageBox::Yes);
} }
void MainWindow::createDisplayWindow(QThread* worker_thread, bool use_debug_device, bool fullscreen, void MainWindow::createDisplay(QThread* worker_thread, bool use_debug_device, bool fullscreen, bool render_to_main)
bool render_to_main)
{ {
DebugAssert(!m_display_widget); Assert(!m_host_display && !m_display_widget);
Assert(!fullscreen || !render_to_main);
m_display_widget = m_host_interface->createDisplayWidget(); m_host_display = m_host_interface->createHostDisplay();
m_display_widget = m_host_display->createWidget((!fullscreen && render_to_main) ? m_ui.mainContainer : nullptr);
m_display_widget->setWindowTitle(windowTitle()); m_display_widget->setWindowTitle(windowTitle());
m_display_widget->setWindowIcon(windowIcon()); m_display_widget->setWindowIcon(windowIcon());
DebugAssert(m_display_widget);
m_display_widget->setFocusPolicy(Qt::StrongFocus);
if (fullscreen) if (fullscreen)
{ {
@ -96,74 +95,89 @@ void MainWindow::createDisplayWindow(QThread* worker_thread, bool use_debug_devi
// we need the surface visible.. this might be able to be replaced with something else // we need the surface visible.. this might be able to be replaced with something else
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents); QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
m_display_widget->createDeviceContext(worker_thread, use_debug_device); if (!m_host_display->createDeviceContext(use_debug_device))
}
void MainWindow::destroyDisplayWindow()
{ {
DebugAssert(m_display_widget); reportError(tr("Failed to create host display device context."));
return;
}
if (m_display_widget->isFullScreen()) if (!m_host_display->createSurface() || !m_host_display->makeDeviceContextCurrent())
m_display_widget->showNormal();
if (m_display_widget->parent())
{ {
m_ui.mainContainer->removeWidget(m_display_widget); reportError(tr("Failed to create host display surface."));
switchToGameListView(); m_host_display->destroyDeviceContext();
return;
} }
// recreate the display widget using the potentially-new renderer m_host_display->moveContextToThread(worker_thread);
delete m_display_widget;
m_display_widget = nullptr;
} }
void MainWindow::updateDisplayWindow(bool fullscreen, bool render_to_main) void MainWindow::updateDisplay(QThread* worker_thread, bool fullscreen, bool render_to_main)
{ {
const bool is_fullscreen = m_display_widget->isFullScreen(); const bool is_fullscreen = m_display_widget->isFullScreen();
const bool is_rendering_to_main = (!is_fullscreen && m_display_widget->parent()); const bool is_rendering_to_main = (!is_fullscreen && m_display_widget->parent());
if (fullscreen == is_fullscreen && is_rendering_to_main == render_to_main) if (fullscreen == is_fullscreen && is_rendering_to_main == render_to_main)
{
m_host_display->moveContextToThread(worker_thread);
return; return;
if (fullscreen || !render_to_main)
{
if (m_display_widget->parent())
{
m_ui.mainContainer->setCurrentIndex(0);
m_ui.mainContainer->removeWidget(m_display_widget);
m_display_widget->setParent(nullptr);
switchToGameListView();
} }
m_host_display->destroySurface();
if (is_rendering_to_main)
{
switchToGameListView();
m_ui.mainContainer->removeWidget(m_display_widget);
}
m_host_display->destroyWidget();
m_display_widget = m_host_display->createWidget((!fullscreen && render_to_main) ? m_ui.mainContainer : nullptr);
m_display_widget->setWindowTitle(windowTitle());
m_display_widget->setWindowIcon(windowIcon());
if (fullscreen) if (fullscreen)
{ {
m_display_widget->showFullScreen(); m_display_widget->showFullScreen();
m_display_widget->setCursor(Qt::BlankCursor); m_display_widget->setCursor(Qt::BlankCursor);
} }
else else if (!render_to_main)
{ {
// if we don't position it, it ends up in the top-left corner with the title bar obscured
m_display_widget->setCursor(QCursor());
m_display_widget->showNormal(); m_display_widget->showNormal();
m_display_widget->move(pos());
}
} }
else else
{
// render-to-main
if (!m_display_widget->parent())
{ {
m_ui.mainContainer->insertWidget(1, m_display_widget); m_ui.mainContainer->insertWidget(1, m_display_widget);
m_ui.mainContainer->setCurrentIndex(1); switchToEmulationView();
} }
m_display_widget->setCursor(QCursor()); // we need the surface visible.. this might be able to be replaced with something else
} QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
if (!m_host_display->createSurface())
Panic("Failed to recreate surface on new widget.");
m_display_widget->windowResizedEvent(m_display_widget->scaledWindowWidth(), m_display_widget->scaledWindowHeight());
m_display_widget->setFocus(); m_display_widget->setFocus();
QSignalBlocker blocker(m_ui.actionFullscreen); QSignalBlocker blocker(m_ui.actionFullscreen);
m_ui.actionFullscreen->setChecked(fullscreen); m_ui.actionFullscreen->setChecked(fullscreen);
m_host_display->moveContextToThread(worker_thread);
}
void MainWindow::destroyDisplay()
{
DebugAssert(m_host_display && m_display_widget);
if (m_display_widget->parent())
{
m_ui.mainContainer->removeWidget(m_display_widget);
switchToGameListView();
}
m_host_display->destroyWidget();
m_display_widget = nullptr;
delete m_host_display;
m_host_display = nullptr;
} }
void MainWindow::focusDisplayWidget() void MainWindow::focusDisplayWidget()
@ -523,10 +537,10 @@ void MainWindow::connectSignals()
connect(m_host_interface, &QtHostInterface::messageReported, this, &MainWindow::reportMessage); connect(m_host_interface, &QtHostInterface::messageReported, this, &MainWindow::reportMessage);
connect(m_host_interface, &QtHostInterface::messageConfirmed, this, &MainWindow::confirmMessage, connect(m_host_interface, &QtHostInterface::messageConfirmed, this, &MainWindow::confirmMessage,
Qt::BlockingQueuedConnection); Qt::BlockingQueuedConnection);
connect(m_host_interface, &QtHostInterface::createDisplayWindowRequested, this, &MainWindow::createDisplayWindow, connect(m_host_interface, &QtHostInterface::createDisplayRequested, this, &MainWindow::createDisplay,
Qt::BlockingQueuedConnection); Qt::BlockingQueuedConnection);
connect(m_host_interface, &QtHostInterface::destroyDisplayWindowRequested, this, &MainWindow::destroyDisplayWindow); connect(m_host_interface, &QtHostInterface::destroyDisplayRequested, this, &MainWindow::destroyDisplay);
connect(m_host_interface, &QtHostInterface::updateDisplayWindowRequested, this, &MainWindow::updateDisplayWindow, connect(m_host_interface, &QtHostInterface::updateDisplayRequested, this, &MainWindow::updateDisplay,
Qt::BlockingQueuedConnection); Qt::BlockingQueuedConnection);
connect(m_host_interface, &QtHostInterface::focusDisplayWidgetRequested, this, &MainWindow::focusDisplayWidget); connect(m_host_interface, &QtHostInterface::focusDisplayWidgetRequested, this, &MainWindow::focusDisplayWidget);
connect(m_host_interface, &QtHostInterface::emulationStarted, this, &MainWindow::onEmulationStarted); connect(m_host_interface, &QtHostInterface::emulationStarted, this, &MainWindow::onEmulationStarted);

View File

@ -12,6 +12,7 @@ class QThread;
class GameListWidget; class GameListWidget;
class QtHostInterface; class QtHostInterface;
class QtHostDisplay;
class QtDisplayWidget; class QtDisplayWidget;
struct GameListEntry; struct GameListEntry;
@ -28,9 +29,9 @@ private Q_SLOTS:
void reportError(const QString& message); void reportError(const QString& message);
void reportMessage(const QString& message); void reportMessage(const QString& message);
bool confirmMessage(const QString& message); bool confirmMessage(const QString& message);
void createDisplayWindow(QThread* worker_thread, bool use_debug_device, bool fullscreen, bool render_to_main); void createDisplay(QThread* worker_thread, bool use_debug_device, bool fullscreen, bool render_to_main);
void destroyDisplayWindow(); void updateDisplay(QThread* worker_thread, bool fullscreen, bool render_to_main);
void updateDisplayWindow(bool fullscreen, bool render_to_main); void destroyDisplay();
void focusDisplayWidget(); void focusDisplayWidget();
void onEmulationStarted(); void onEmulationStarted();
void onEmulationStopped(); void onEmulationStopped();
@ -73,6 +74,8 @@ private:
QtHostInterface* m_host_interface = nullptr; QtHostInterface* m_host_interface = nullptr;
GameListWidget* m_game_list_widget = nullptr; GameListWidget* m_game_list_widget = nullptr;
QtHostDisplay* m_host_display = nullptr;
QtDisplayWidget* m_display_widget = nullptr; QtDisplayWidget* m_display_widget = nullptr;
QLabel* m_status_speed_widget = nullptr; QLabel* m_status_speed_widget = nullptr;

View File

@ -1,14 +1,15 @@
#include "opengldisplaywidget.h" #include "openglhostdisplay.h"
#include "common/assert.h" #include "common/assert.h"
#include "common/log.h" #include "common/log.h"
#include "imgui.h" #include "imgui.h"
#include "qtdisplaywidget.h"
#include "qthostinterface.h" #include "qthostinterface.h"
#include <QtGui/QKeyEvent> #include <QtGui/QKeyEvent>
#include <QtGui/QWindow> #include <QtGui/QWindow>
#include <array> #include <array>
#include <imgui_impl_opengl3.h> #include <imgui_impl_opengl3.h>
#include <tuple> #include <tuple>
Log_SetChannel(OpenGLDisplayWidget); Log_SetChannel(OpenGLHostDisplay);
static thread_local QOpenGLContext* s_thread_gl_context; static thread_local QOpenGLContext* s_thread_gl_context;
@ -27,7 +28,7 @@ static void* GetProcAddressCallback(const char* name)
/// Changes the swap interval on a window. Since Qt doesn't expose this functionality, we need to change it manually /// Changes the swap interval on a window. Since Qt doesn't expose this functionality, we need to change it manually
/// ourselves it by calling system-specific functions. Assumes the context is current. /// ourselves it by calling system-specific functions. Assumes the context is current.
static void SetSwapInterval(QWindow* window, QOpenGLContext* context, int interval) static void SetSwapInterval(QOpenGLContext* context, int interval)
{ {
static QOpenGLContext* last_context = nullptr; static QOpenGLContext* last_context = nullptr;
@ -97,59 +98,43 @@ private:
u32 m_height; u32 m_height;
}; };
OpenGLDisplayWidget::OpenGLDisplayWidget(QtHostInterface* host_interface, QWidget* parent) OpenGLHostDisplay::OpenGLHostDisplay(QtHostInterface* host_interface) : QtHostDisplay(host_interface) {}
: QtDisplayWidget(host_interface, parent)
OpenGLHostDisplay::~OpenGLHostDisplay() = default;
QtDisplayWidget* OpenGLHostDisplay::createWidget(QWidget* parent)
{ {
QWindow* native_window = windowHandle(); QtDisplayWidget* widget = QtHostDisplay::createWidget(parent);
QWindow* native_window = widget->windowHandle();
Assert(native_window); Assert(native_window);
native_window->setSurfaceType(QWindow::OpenGLSurface); native_window->setSurfaceType(QWindow::OpenGLSurface);
return widget;
} }
OpenGLDisplayWidget::~OpenGLDisplayWidget() = default; HostDisplay::RenderAPI OpenGLHostDisplay::GetRenderAPI() const
HostDisplay* OpenGLDisplayWidget::getHostDisplayInterface()
{
return this;
}
HostDisplay::RenderAPI OpenGLDisplayWidget::GetRenderAPI() const
{ {
return m_gl_context->isOpenGLES() ? HostDisplay::RenderAPI::OpenGLES : HostDisplay::RenderAPI::OpenGL; return m_gl_context->isOpenGLES() ? HostDisplay::RenderAPI::OpenGLES : HostDisplay::RenderAPI::OpenGL;
} }
void* OpenGLDisplayWidget::GetRenderDevice() const void* OpenGLHostDisplay::GetRenderDevice() const
{ {
return nullptr; return nullptr;
} }
void* OpenGLDisplayWidget::GetRenderContext() const void* OpenGLHostDisplay::GetRenderContext() const
{ {
return m_gl_context.get(); return m_gl_context.get();
} }
void* OpenGLDisplayWidget::GetRenderWindow() const std::unique_ptr<HostDisplayTexture> OpenGLHostDisplay::CreateTexture(u32 width, u32 height, const void* initial_data,
{
return const_cast<QWidget*>(static_cast<const QWidget*>(this));
}
void OpenGLDisplayWidget::ChangeRenderWindow(void* new_window)
{
Panic("Not implemented");
}
void OpenGLDisplayWidget::windowResized(s32 new_window_width, s32 new_window_height)
{
QtDisplayWidget::windowResized(new_window_width, new_window_height);
HostDisplay::WindowResized(new_window_width, new_window_height);
}
std::unique_ptr<HostDisplayTexture> OpenGLDisplayWidget::CreateTexture(u32 width, u32 height, const void* initial_data,
u32 initial_data_stride, bool dynamic) u32 initial_data_stride, bool dynamic)
{ {
return OpenGLDisplayWidgetTexture::Create(width, height, initial_data, initial_data_stride); return OpenGLDisplayWidgetTexture::Create(width, height, initial_data, initial_data_stride);
} }
void OpenGLDisplayWidget::UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height, void OpenGLHostDisplay::UpdateTexture(HostDisplayTexture* texture, u32 x, u32 y, u32 width, u32 height,
const void* texture_data, u32 texture_data_stride) const void* texture_data, u32 texture_data_stride)
{ {
OpenGLDisplayWidgetTexture* tex = static_cast<OpenGLDisplayWidgetTexture*>(texture); OpenGLDisplayWidgetTexture* tex = static_cast<OpenGLDisplayWidgetTexture*>(texture);
@ -171,8 +156,8 @@ void OpenGLDisplayWidget::UpdateTexture(HostDisplayTexture* texture, u32 x, u32
glBindTexture(GL_TEXTURE_2D, old_texture_binding); glBindTexture(GL_TEXTURE_2D, old_texture_binding);
} }
bool OpenGLDisplayWidget::DownloadTexture(const void* texture_handle, u32 x, u32 y, u32 width, u32 height, bool OpenGLHostDisplay::DownloadTexture(const void* texture_handle, u32 x, u32 y, u32 width, u32 height, void* out_data,
void* out_data, u32 out_data_stride) u32 out_data_stride)
{ {
GLint old_alignment = 0, old_row_length = 0; GLint old_alignment = 0, old_row_length = 0;
glGetIntegerv(GL_PACK_ALIGNMENT, &old_alignment); glGetIntegerv(GL_PACK_ALIGNMENT, &old_alignment);
@ -189,17 +174,17 @@ bool OpenGLDisplayWidget::DownloadTexture(const void* texture_handle, u32 x, u32
return true; return true;
} }
void OpenGLDisplayWidget::SetVSync(bool enabled) void OpenGLHostDisplay::SetVSync(bool enabled)
{ {
// Window framebuffer has to be bound to call SetSwapInterval. // Window framebuffer has to be bound to call SetSwapInterval.
GLint current_fbo = 0; GLint current_fbo = 0;
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &current_fbo); glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &current_fbo);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
SetSwapInterval(windowHandle(), m_gl_context.get(), enabled ? 1 : 0); SetSwapInterval(m_gl_context.get(), enabled ? 1 : 0);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, current_fbo); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, current_fbo);
} }
const char* OpenGLDisplayWidget::GetGLSLVersionString() const const char* OpenGLHostDisplay::GetGLSLVersionString() const
{ {
if (m_gl_context->isOpenGLES()) if (m_gl_context->isOpenGLES())
{ {
@ -217,7 +202,7 @@ const char* OpenGLDisplayWidget::GetGLSLVersionString() const
} }
} }
std::string OpenGLDisplayWidget::GetGLSLVersionHeader() const std::string OpenGLHostDisplay::GetGLSLVersionHeader() const
{ {
std::string header = GetGLSLVersionString(); std::string header = GetGLSLVersionString();
header += "\n\n"; header += "\n\n";
@ -250,12 +235,12 @@ static void APIENTRY GLDebugCallback(GLenum source, GLenum type, GLuint id, GLen
} }
} }
bool OpenGLDisplayWidget::hasDeviceContext() const bool OpenGLHostDisplay::hasDeviceContext() const
{ {
return static_cast<bool>(m_gl_context); return static_cast<bool>(m_gl_context);
} }
bool OpenGLDisplayWidget::createDeviceContext(QThread* worker_thread, bool debug_device) bool OpenGLHostDisplay::createDeviceContext(bool debug_device)
{ {
m_gl_context = std::make_unique<QOpenGLContext>(); m_gl_context = std::make_unique<QOpenGLContext>();
@ -308,28 +293,12 @@ bool OpenGLDisplayWidget::createDeviceContext(QThread* worker_thread, bool debug
Log_InfoPrintf("Got a %s %d.%d context", (m_gl_context->isOpenGLES() ? "OpenGL ES" : "desktop OpenGL"), Log_InfoPrintf("Got a %s %d.%d context", (m_gl_context->isOpenGLES() ? "OpenGL ES" : "desktop OpenGL"),
surface_format.majorVersion(), surface_format.minorVersion()); surface_format.majorVersion(), surface_format.minorVersion());
if (!m_gl_context->makeCurrent(windowHandle()))
{
Log_ErrorPrintf("Failed to make GL context current on UI thread");
m_gl_context.reset();
return false;
}
if (!QtDisplayWidget::createDeviceContext(worker_thread, debug_device))
{
m_gl_context->doneCurrent();
m_gl_context.reset();
return false;
}
m_gl_context->doneCurrent();
m_gl_context->moveToThread(worker_thread);
return true; return true;
} }
bool OpenGLDisplayWidget::initializeDeviceContext(bool debug_device) bool OpenGLHostDisplay::initializeDeviceContext(bool debug_device)
{ {
if (!m_gl_context->makeCurrent(windowHandle())) if (!m_gl_context->makeCurrent(m_widget->windowHandle()))
return false; return false;
s_thread_gl_context = m_gl_context.get(); s_thread_gl_context = m_gl_context.get();
@ -352,7 +321,7 @@ bool OpenGLDisplayWidget::initializeDeviceContext(bool debug_device)
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
} }
if (!QtDisplayWidget::initializeDeviceContext(debug_device)) if (!QtHostDisplay::initializeDeviceContext(debug_device))
{ {
s_thread_gl_context = nullptr; s_thread_gl_context = nullptr;
m_gl_context->doneCurrent(); m_gl_context->doneCurrent();
@ -362,20 +331,47 @@ bool OpenGLDisplayWidget::initializeDeviceContext(bool debug_device)
return true; return true;
} }
void OpenGLDisplayWidget::destroyDeviceContext() bool OpenGLHostDisplay::makeDeviceContextCurrent()
{
if (!m_gl_context->makeCurrent(m_widget->windowHandle()))
{
Log_ErrorPrintf("Failed to make GL context current");
return false;
}
return true;
}
void OpenGLHostDisplay::moveContextToThread(QThread* new_thread)
{
m_gl_context->doneCurrent();
m_gl_context->moveToThread(new_thread);
}
void OpenGLHostDisplay::destroyDeviceContext()
{ {
Assert(m_gl_context && s_thread_gl_context == m_gl_context.get()); Assert(m_gl_context && s_thread_gl_context == m_gl_context.get());
QtDisplayWidget::destroyDeviceContext(); QtHostDisplay::destroyDeviceContext();
s_thread_gl_context = nullptr; s_thread_gl_context = nullptr;
m_gl_context->doneCurrent(); m_gl_context->doneCurrent();
m_gl_context.reset(); m_gl_context.reset();
} }
bool OpenGLDisplayWidget::createImGuiContext() bool OpenGLHostDisplay::createSurface()
{ {
if (!QtDisplayWidget::createImGuiContext()) m_window_width = m_widget->scaledWindowWidth();
m_window_height = m_widget->scaledWindowHeight();
emit m_widget->windowResizedEvent(m_window_width, m_window_height);
return true;
}
void OpenGLHostDisplay::destroySurface() {}
bool OpenGLHostDisplay::createImGuiContext()
{
if (!QtHostDisplay::createImGuiContext())
return false; return false;
if (!ImGui_ImplOpenGL3_Init(GetGLSLVersionString())) if (!ImGui_ImplOpenGL3_Init(GetGLSLVersionString()))
@ -386,14 +382,14 @@ bool OpenGLDisplayWidget::createImGuiContext()
return true; return true;
} }
void OpenGLDisplayWidget::destroyImGuiContext() void OpenGLHostDisplay::destroyImGuiContext()
{ {
ImGui_ImplOpenGL3_Shutdown(); ImGui_ImplOpenGL3_Shutdown();
QtDisplayWidget::destroyImGuiContext(); QtHostDisplay::destroyImGuiContext();
} }
bool OpenGLDisplayWidget::createDeviceResources() bool OpenGLHostDisplay::createDeviceResources()
{ {
static constexpr char fullscreen_quad_vertex_shader[] = R"( static constexpr char fullscreen_quad_vertex_shader[] = R"(
uniform vec4 u_src_rect; uniform vec4 u_src_rect;
@ -453,9 +449,9 @@ void main()
return true; return true;
} }
void OpenGLDisplayWidget::destroyDeviceResources() void OpenGLHostDisplay::destroyDeviceResources()
{ {
QtDisplayWidget::destroyDeviceResources(); QtHostDisplay::destroyDeviceResources();
if (m_display_vao != 0) if (m_display_vao != 0)
glDeleteVertexArrays(1, &m_display_vao); glDeleteVertexArrays(1, &m_display_vao);
@ -467,7 +463,7 @@ void OpenGLDisplayWidget::destroyDeviceResources()
m_display_program.Destroy(); m_display_program.Destroy();
} }
void OpenGLDisplayWidget::Render() void OpenGLHostDisplay::Render()
{ {
glDisable(GL_SCISSOR_TEST); glDisable(GL_SCISSOR_TEST);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
@ -479,7 +475,7 @@ void OpenGLDisplayWidget::Render()
ImGui::Render(); ImGui::Render();
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
QWindow* window_handle = windowHandle(); QWindow* window_handle = m_widget->windowHandle();
m_gl_context->makeCurrent(window_handle); m_gl_context->makeCurrent(window_handle);
m_gl_context->swapBuffers(window_handle); m_gl_context->swapBuffers(window_handle);
@ -489,12 +485,13 @@ void OpenGLDisplayWidget::Render()
GL::Program::ResetLastProgram(); GL::Program::ResetLastProgram();
} }
void OpenGLDisplayWidget::renderDisplay() void OpenGLHostDisplay::renderDisplay()
{ {
if (!m_display_texture_handle) if (!m_display_texture_handle)
return; return;
const auto [vp_left, vp_top, vp_width, vp_height] = CalculateDrawRect(m_window_width, m_window_height, m_display_top_margin); const auto [vp_left, vp_top, vp_width, vp_height] =
CalculateDrawRect(m_window_width, m_window_height, m_display_top_margin);
glViewport(vp_left, m_window_height - (m_display_top_margin + vp_top) - vp_height, vp_width, vp_height); glViewport(vp_left, m_window_height - (m_display_top_margin + vp_top) - vp_height, vp_width, vp_height);
glDisable(GL_BLEND); glDisable(GL_BLEND);

View File

@ -12,33 +12,32 @@
#include "common/gl/texture.h" #include "common/gl/texture.h"
#include "core/host_display.h" #include "core/host_display.h"
#include "qtdisplaywidget.h" #include "qtdisplaywidget.h"
#include "qthostdisplay.h"
#include <QtGui/QOpenGLContext> #include <QtGui/QOpenGLContext>
#include <memory> #include <memory>
class QtHostInterface; class QtHostInterface;
class OpenGLDisplayWidget final : public QtDisplayWidget, public HostDisplay class OpenGLHostDisplay final : public QtHostDisplay
{ {
Q_OBJECT
public: public:
OpenGLDisplayWidget(QtHostInterface* host_interface, QWidget* parent); OpenGLHostDisplay(QtHostInterface* host_interface);
~OpenGLDisplayWidget(); ~OpenGLHostDisplay();
HostDisplay* getHostDisplayInterface() override; QtDisplayWidget* createWidget(QWidget* parent) override;
bool hasDeviceContext() const override; bool hasDeviceContext() const override;
bool createDeviceContext(QThread* worker_thread, bool debug_device) override; bool createDeviceContext(bool debug_device) override;
bool initializeDeviceContext(bool debug_device) override; bool initializeDeviceContext(bool debug_device) override;
bool makeDeviceContextCurrent() override;
void moveContextToThread(QThread* new_thread) override;
void destroyDeviceContext() override; void destroyDeviceContext() override;
bool createSurface() override;
void destroySurface();
RenderAPI GetRenderAPI() const override; RenderAPI GetRenderAPI() const override;
void* GetRenderDevice() const override; void* GetRenderDevice() const override;
void* GetRenderContext() const override; void* GetRenderContext() const override;
void* GetRenderWindow() const override;
void ChangeRenderWindow(void* new_window) override;
void windowResized(s32 new_window_width, s32 new_window_height) override;
std::unique_ptr<HostDisplayTexture> CreateTexture(u32 width, u32 height, const void* initial_data, std::unique_ptr<HostDisplayTexture> CreateTexture(u32 width, u32 height, const void* initial_data,
u32 initial_data_stride, bool dynamic) override; u32 initial_data_stride, bool dynamic) override;

View File

@ -1,6 +1,5 @@
#include "qtdisplaywidget.h" #include "qtdisplaywidget.h"
#include "frontend-common/imgui_styles.h" #include "qthostdisplay.h"
#include "imgui.h"
#include "qthostinterface.h" #include "qthostinterface.h"
#include "qtutils.h" #include "qtutils.h"
#include <QtGui/QGuiApplication> #include <QtGui/QGuiApplication>
@ -10,47 +9,18 @@
#include <QtGui/QWindowStateChangeEvent> #include <QtGui/QWindowStateChangeEvent>
#include <cmath> #include <cmath>
QtDisplayWidget::QtDisplayWidget(QtHostInterface* host_interface, QWidget* parent) QtDisplayWidget::QtDisplayWidget(QWidget* parent) : QWidget(parent)
: QWidget(parent), m_host_interface(host_interface)
{ {
// We want a native window for both D3D and OpenGL. // We want a native window for both D3D and OpenGL.
setAutoFillBackground(false); setAutoFillBackground(false);
setAttribute(Qt::WA_NativeWindow, true); setAttribute(Qt::WA_NativeWindow, true);
setAttribute(Qt::WA_NoSystemBackground, true); setAttribute(Qt::WA_NoSystemBackground, true);
setAttribute(Qt::WA_PaintOnScreen, true); setAttribute(Qt::WA_PaintOnScreen, true);
setFocusPolicy(Qt::StrongFocus);
} }
QtDisplayWidget::~QtDisplayWidget() = default; QtDisplayWidget::~QtDisplayWidget() = default;
HostDisplay* QtDisplayWidget::getHostDisplayInterface()
{
return nullptr;
}
bool QtDisplayWidget::hasDeviceContext() const
{
return true;
}
bool QtDisplayWidget::createDeviceContext(QThread* worker_thread, bool debug_device)
{
return true;
}
bool QtDisplayWidget::initializeDeviceContext(bool debug_device)
{
if (!createImGuiContext() || !createDeviceResources())
return false;
return true;
}
void QtDisplayWidget::destroyDeviceContext()
{
destroyImGuiContext();
destroyDeviceResources();
}
qreal QtDisplayWidget::devicePixelRatioFromScreen() const qreal QtDisplayWidget::devicePixelRatioFromScreen() const
{ {
QScreen* screen_for_ratio; QScreen* screen_for_ratio;
@ -75,49 +45,6 @@ int QtDisplayWidget::scaledWindowHeight() const
return static_cast<int>(std::ceil(static_cast<qreal>(height()) * devicePixelRatioFromScreen())); return static_cast<int>(std::ceil(static_cast<qreal>(height()) * devicePixelRatioFromScreen()));
} }
bool QtDisplayWidget::createImGuiContext()
{
ImGui::CreateContext();
auto& io = ImGui::GetIO();
io.IniFilename = nullptr;
io.DisplaySize.x = static_cast<float>(scaledWindowWidth());
io.DisplaySize.y = static_cast<float>(scaledWindowHeight());
const float framebuffer_scale = static_cast<float>(devicePixelRatioFromScreen());
io.DisplayFramebufferScale.x = framebuffer_scale;
io.DisplayFramebufferScale.y = framebuffer_scale;
ImGui::GetStyle().ScaleAllSizes(framebuffer_scale);
ImGui::StyleColorsDarker();
ImGui::AddRobotoRegularFont(15.0f * framebuffer_scale);
return true;
}
void QtDisplayWidget::destroyImGuiContext()
{
ImGui::DestroyContext();
}
bool QtDisplayWidget::createDeviceResources()
{
return true;
}
void QtDisplayWidget::destroyDeviceResources() {}
void QtDisplayWidget::windowResized(s32 new_window_width, s32 new_window_height)
{
// imgui may not have been initialized yet
if (!ImGui::GetCurrentContext())
return;
auto& io = ImGui::GetIO();
io.DisplaySize.x = static_cast<float>(new_window_width);
io.DisplaySize.y = static_cast<float>(new_window_height);
}
QPaintEngine* QtDisplayWidget::paintEngine() const QPaintEngine* QtDisplayWidget::paintEngine() const
{ {
return nullptr; return nullptr;
@ -132,7 +59,7 @@ bool QtDisplayWidget::event(QEvent* event)
{ {
QKeyEvent* key_event = static_cast<QKeyEvent*>(event); QKeyEvent* key_event = static_cast<QKeyEvent*>(event);
if (!key_event->isAutoRepeat()) if (!key_event->isAutoRepeat())
m_host_interface->handleKeyEvent(QtUtils::KeyEventToInt(key_event), event->type() == QEvent::KeyPress); emit windowKeyEvent(QtUtils::KeyEventToInt(key_event), event->type() == QEvent::KeyPress);
return true; return true;
} }
@ -147,7 +74,7 @@ bool QtDisplayWidget::event(QEvent* event)
case QEvent::Close: case QEvent::Close:
{ {
m_host_interface->synchronousPowerOffSystem(); emit windowClosedEvent();
QWidget::event(event); QWidget::event(event);
return true; return true;
} }

View File

@ -2,49 +2,26 @@
#include "common/types.h" #include "common/types.h"
#include <QtWidgets/QWidget> #include <QtWidgets/QWidget>
class QKeyEvent; class QtDisplayWidget final : public QWidget
class QResizeEvent;
class HostDisplay;
class QtHostInterface;
class QtDisplayWidget : public QWidget
{ {
Q_OBJECT Q_OBJECT
public: public:
QtDisplayWidget(QtHostInterface* host_interface, QWidget* parent); QtDisplayWidget(QWidget* parent);
virtual ~QtDisplayWidget(); ~QtDisplayWidget();
virtual HostDisplay* getHostDisplayInterface(); QPaintEngine* paintEngine() const override;
virtual bool hasDeviceContext() const;
virtual bool createDeviceContext(QThread* worker_thread, bool debug_device);
virtual bool initializeDeviceContext(bool debug_device);
virtual void destroyDeviceContext();
// this comes back on the emu thread
virtual void windowResized(s32 new_window_width, s32 new_window_height);
virtual QPaintEngine* paintEngine() const override;
int scaledWindowWidth() const; int scaledWindowWidth() const;
int scaledWindowHeight() const; int scaledWindowHeight() const;
qreal devicePixelRatioFromScreen() const;
Q_SIGNALS: Q_SIGNALS:
void windowResizedEvent(int width, int height); void windowResizedEvent(int width, int height);
void windowRestoredEvent(); void windowRestoredEvent();
void windowClosedEvent();
void windowKeyEvent(int key_code, bool pressed);
protected: protected:
qreal devicePixelRatioFromScreen() const; bool event(QEvent* event) override;
virtual bool createImGuiContext();
virtual void destroyImGuiContext();
virtual bool createDeviceResources();
virtual void destroyDeviceResources();
virtual bool event(QEvent* event) override;
QtHostInterface* m_host_interface;
}; };

View File

@ -0,0 +1,130 @@
#include "qthostdisplay.h"
#include "common/assert.h"
#include "frontend-common/imgui_styles.h"
#include "imgui.h"
#include "qtdisplaywidget.h"
#include "qthostinterface.h"
#include <cmath>
QtHostDisplay::QtHostDisplay(QtHostInterface* host_interface) : m_host_interface(host_interface) {}
QtHostDisplay::~QtHostDisplay() = default;
QtDisplayWidget* QtHostDisplay::createWidget(QWidget* parent)
{
Assert(!m_widget);
m_widget = new QtDisplayWidget(parent);
// We want a native window for both D3D and OpenGL.
m_widget->setAutoFillBackground(false);
m_widget->setAttribute(Qt::WA_NativeWindow, true);
m_widget->setAttribute(Qt::WA_NoSystemBackground, true);
m_widget->setAttribute(Qt::WA_PaintOnScreen, true);
return m_widget;
}
void QtHostDisplay::destroyWidget()
{
Assert(m_widget);
delete m_widget;
m_widget = nullptr;
}
bool QtHostDisplay::hasDeviceContext() const
{
return false;
}
bool QtHostDisplay::createDeviceContext(bool debug_device)
{
return false;
}
bool QtHostDisplay::initializeDeviceContext(bool debug_device)
{
if (!createImGuiContext() || !createDeviceResources())
return false;
return true;
}
bool QtHostDisplay::makeDeviceContextCurrent()
{
return true;
}
void QtHostDisplay::moveContextToThread(QThread* new_thread) {}
void QtHostDisplay::destroyDeviceContext()
{
destroyImGuiContext();
destroyDeviceResources();
}
bool QtHostDisplay::createSurface()
{
return false;
}
void QtHostDisplay::destroySurface() {}
void* QtHostDisplay::GetRenderWindow() const
{
return m_widget;
}
void QtHostDisplay::ChangeRenderWindow(void* new_window)
{
Panic("Not implemented");
}
bool QtHostDisplay::createImGuiContext()
{
ImGui::CreateContext();
auto& io = ImGui::GetIO();
io.IniFilename = nullptr;
io.DisplaySize.x = static_cast<float>(m_window_width);
io.DisplaySize.y = static_cast<float>(m_window_height);
const float framebuffer_scale = static_cast<float>(m_widget->devicePixelRatioFromScreen());
io.DisplayFramebufferScale.x = framebuffer_scale;
io.DisplayFramebufferScale.y = framebuffer_scale;
ImGui::GetStyle().ScaleAllSizes(framebuffer_scale);
ImGui::StyleColorsDarker();
ImGui::AddRobotoRegularFont(15.0f * framebuffer_scale);
return true;
}
void QtHostDisplay::destroyImGuiContext()
{
ImGui::DestroyContext();
}
bool QtHostDisplay::createDeviceResources()
{
return true;
}
void QtHostDisplay::destroyDeviceResources() {}
void QtHostDisplay::WindowResized(s32 new_window_width, s32 new_window_height)
{
HostDisplay::WindowResized(new_window_width, new_window_height);
updateImGuiDisplaySize();
}
void QtHostDisplay::updateImGuiDisplaySize()
{
// imgui may not have been initialized yet
if (!ImGui::GetCurrentContext())
return;
auto& io = ImGui::GetIO();
io.DisplaySize.x = static_cast<float>(m_window_width);
io.DisplaySize.y = static_cast<float>(m_window_height);
}

View File

@ -0,0 +1,46 @@
#pragma once
#include "common/types.h"
#include "core/host_display.h"
class QThread;
class QWidget;
class QtHostInterface;
class QtDisplayWidget;
class QtHostDisplay : public HostDisplay
{
public:
QtHostDisplay(QtHostInterface* host_interface);
virtual ~QtHostDisplay();
ALWAYS_INLINE bool hasWidget() const { return (m_widget != nullptr); }
ALWAYS_INLINE QtDisplayWidget* getWidget() const { return m_widget; }
virtual QtDisplayWidget* createWidget(QWidget* parent);
virtual void destroyWidget();
virtual bool hasDeviceContext() const;
virtual bool createDeviceContext(bool debug_device);
virtual bool initializeDeviceContext(bool debug_device);
virtual bool makeDeviceContextCurrent();
virtual void moveContextToThread(QThread* new_thread);
virtual void destroyDeviceContext();
virtual bool createSurface();
virtual void destroySurface();
virtual void* GetRenderWindow() const override;
virtual void ChangeRenderWindow(void* new_window) override;
virtual void WindowResized(s32 new_window_width, s32 new_window_height) override;
void updateImGuiDisplaySize();
protected:
virtual bool createImGuiContext();
virtual void destroyImGuiContext();
virtual bool createDeviceResources();
virtual void destroyDeviceResources();
QtHostInterface* m_host_interface;
QtDisplayWidget* m_widget = nullptr;
};

View File

@ -11,7 +11,7 @@
#include "frontend-common/sdl_audio_stream.h" #include "frontend-common/sdl_audio_stream.h"
#include "frontend-common/sdl_controller_interface.h" #include "frontend-common/sdl_controller_interface.h"
#include "mainwindow.h" #include "mainwindow.h"
#include "opengldisplaywidget.h" #include "openglhostdisplay.h"
#include "qtprogresscallback.h" #include "qtprogresscallback.h"
#include "qtsettingsinterface.h" #include "qtsettingsinterface.h"
#include "qtutils.h" #include "qtutils.h"
@ -26,7 +26,7 @@
Log_SetChannel(QtHostInterface); Log_SetChannel(QtHostInterface);
#ifdef WIN32 #ifdef WIN32
#include "d3d11displaywidget.h" #include "d3d11hostdisplay.h"
#endif #endif
QtHostInterface::QtHostInterface(QObject* parent) : QObject(parent), CommonHostInterface() QtHostInterface::QtHostInterface(QObject* parent) : QObject(parent), CommonHostInterface()
@ -36,7 +36,7 @@ QtHostInterface::QtHostInterface(QObject* parent) : QObject(parent), CommonHostI
QtHostInterface::~QtHostInterface() QtHostInterface::~QtHostInterface()
{ {
Assert(!m_display_widget); Assert(!getHostDisplay());
} }
const char* QtHostInterface::GetFrontendName() const const char* QtHostInterface::GetFrontendName() const
@ -162,10 +162,10 @@ void QtHostInterface::applySettings()
// detect when render-to-main flag changes // detect when render-to-main flag changes
const bool render_to_main = m_qsettings->value("Main/RenderToMainWindow", true).toBool(); const bool render_to_main = m_qsettings->value("Main/RenderToMainWindow", true).toBool();
if (m_system && m_display_widget && !m_is_fullscreen && render_to_main != m_is_rendering_to_main) if (m_system && getHostDisplay() && !m_is_fullscreen && render_to_main != m_is_rendering_to_main)
{ {
m_is_rendering_to_main = render_to_main; m_is_rendering_to_main = render_to_main;
emit updateDisplayWindowRequested(false, render_to_main); updateDisplayState();
} }
} }
@ -188,23 +188,6 @@ void QtHostInterface::setMainWindow(MainWindow* window)
m_main_window = window; m_main_window = window;
} }
QtDisplayWidget* QtHostInterface::createDisplayWidget()
{
Assert(!m_display_widget);
#ifdef WIN32
if (m_settings.gpu_renderer == GPURenderer::HardwareOpenGL)
m_display_widget = new OpenGLDisplayWidget(this, nullptr);
else
m_display_widget = new D3D11DisplayWidget(this, nullptr);
#else
m_display_widget = new OpenGLDisplayWidget(this, nullptr);
#endif
connect(m_display_widget, &QtDisplayWidget::windowResizedEvent, this, &QtHostInterface::onDisplayWidgetResized);
connect(m_display_widget, &QtDisplayWidget::windowRestoredEvent, this, &QtHostInterface::redrawDisplayWindow);
return m_display_widget;
}
void QtHostInterface::bootSystem(const SystemBootParameters& params) void QtHostInterface::bootSystem(const SystemBootParameters& params)
{ {
if (!isOnWorkerThread()) if (!isOnWorkerThread())
@ -231,24 +214,19 @@ void QtHostInterface::resumeSystemFromState(const QString& filename, bool boot_o
HostInterface::ResumeSystemFromState(filename.toStdString().c_str(), boot_on_failure); HostInterface::ResumeSystemFromState(filename.toStdString().c_str(), boot_on_failure);
} }
void QtHostInterface::handleKeyEvent(int key, bool pressed) void QtHostInterface::onDisplayWindowKeyEvent(int key, bool pressed)
{ {
if (!isOnWorkerThread()) DebugAssert(isOnWorkerThread());
{
QMetaObject::invokeMethod(this, "handleKeyEvent", Qt::QueuedConnection, Q_ARG(int, key), Q_ARG(bool, pressed));
return;
}
HandleHostKeyEvent(key, pressed); HandleHostKeyEvent(key, pressed);
} }
void QtHostInterface::onDisplayWidgetResized(int width, int height) void QtHostInterface::onHostDisplayWindowResized(int width, int height)
{ {
// this can be null if it was destroyed and the main thread is late catching up // this can be null if it was destroyed and the main thread is late catching up
if (!m_display_widget) if (!getHostDisplay())
return; return;
m_display_widget->windowResized(width, height); getHostDisplay()->WindowResized(width, height);
// re-render the display, since otherwise it will be out of date and stretched if paused // re-render the display, since otherwise it will be out of date and stretched if paused
if (m_system) if (m_system)
@ -263,7 +241,7 @@ void QtHostInterface::redrawDisplayWindow()
return; return;
} }
if (!m_display_widget || !m_system) if (!getHostDisplay() || !m_system)
return; return;
renderDisplay(); renderDisplay();
@ -280,39 +258,91 @@ void QtHostInterface::toggleFullscreen()
SetFullscreen(!m_is_fullscreen); SetFullscreen(!m_is_fullscreen);
} }
QtHostDisplay* QtHostInterface::getHostDisplay()
{
return static_cast<QtHostDisplay*>(m_display);
}
bool QtHostInterface::AcquireHostDisplay() bool QtHostInterface::AcquireHostDisplay()
{ {
DebugAssert(!m_display_widget); Assert(!m_display);
m_is_rendering_to_main = getSettingValue("Main/RenderToMainWindow", true).toBool(); m_is_rendering_to_main = getSettingValue("Main/RenderToMainWindow", true).toBool();
emit createDisplayWindowRequested(m_worker_thread, m_settings.gpu_use_debug_device, m_is_fullscreen, emit createDisplayRequested(m_worker_thread, m_settings.gpu_use_debug_device, m_is_fullscreen,
m_is_rendering_to_main); m_is_rendering_to_main);
if (!m_display_widget->hasDeviceContext()) Assert(m_display);
if (!getHostDisplay()->hasDeviceContext())
{ {
m_display_widget = nullptr; emit destroyDisplayRequested();
emit destroyDisplayWindowRequested(); m_display = nullptr;
return false; return false;
} }
if (!m_display_widget->initializeDeviceContext(m_settings.gpu_use_debug_device)) if (!getHostDisplay()->makeDeviceContextCurrent() ||
!getHostDisplay()->initializeDeviceContext(m_settings.gpu_use_debug_device))
{ {
m_display_widget->destroyDeviceContext(); getHostDisplay()->destroyDeviceContext();
m_display_widget = nullptr; emit destroyDisplayRequested();
emit destroyDisplayWindowRequested(); m_display = nullptr;
return false; return false;
} }
m_display = m_display_widget->getHostDisplayInterface(); connectDisplaySignals();
return true; return true;
} }
QtHostDisplay* QtHostInterface::createHostDisplay()
{
Assert(!getHostDisplay());
#ifdef WIN32
if (m_settings.gpu_renderer == GPURenderer::HardwareOpenGL)
m_display = new OpenGLHostDisplay(this);
else
m_display = new D3D11HostDisplay(this);
#else
m_display = new OpenGLHostDisplay(this);
#endif
return getHostDisplay();
}
void QtHostInterface::connectDisplaySignals()
{
QtDisplayWidget* widget = getHostDisplay()->getWidget();
connect(widget, &QtDisplayWidget::windowResizedEvent, this, &QtHostInterface::onHostDisplayWindowResized);
connect(widget, &QtDisplayWidget::windowRestoredEvent, this, &QtHostInterface::redrawDisplayWindow);
connect(widget, &QtDisplayWidget::windowClosedEvent, this, &QtHostInterface::powerOffSystem,
Qt::BlockingQueuedConnection);
connect(widget, &QtDisplayWidget::windowKeyEvent, this, &QtHostInterface::onDisplayWindowKeyEvent);
}
void QtHostInterface::disconnectDisplaySignals()
{
getHostDisplay()->getWidget()->disconnect(this);
}
void QtHostInterface::updateDisplayState()
{
// this expects the context to get moved back to us afterwards
getHostDisplay()->moveContextToThread(m_original_thread);
emit updateDisplayRequested(m_worker_thread, m_is_fullscreen, m_is_rendering_to_main);
if (!getHostDisplay()->makeDeviceContextCurrent())
Panic("Failed to make device context current after updating");
getHostDisplay()->updateImGuiDisplaySize();
connectDisplaySignals();
UpdateSpeedLimiterState();
}
void QtHostInterface::ReleaseHostDisplay() void QtHostInterface::ReleaseHostDisplay()
{ {
DebugAssert(m_display_widget && m_display == m_display_widget->getHostDisplayInterface()); Assert(m_display);
getHostDisplay()->destroyDeviceContext();
emit destroyDisplayRequested();
m_display = nullptr; m_display = nullptr;
m_display_widget->destroyDeviceContext();
m_display_widget = nullptr;
emit destroyDisplayWindowRequested();
} }
bool QtHostInterface::IsFullscreen() const bool QtHostInterface::IsFullscreen() const
@ -326,7 +356,7 @@ bool QtHostInterface::SetFullscreen(bool enabled)
return true; return true;
m_is_fullscreen = enabled; m_is_fullscreen = enabled;
emit updateDisplayWindowRequested(m_is_fullscreen, m_is_rendering_to_main); updateDisplayState();
return true; return true;
} }

View File

@ -25,7 +25,8 @@ class QTimer;
class GameList; class GameList;
class MainWindow; class MainWindow;
class QtDisplayWidget;
class QtHostDisplay;
Q_DECLARE_METATYPE(SystemBootParameters); Q_DECLARE_METATYPE(SystemBootParameters);
@ -65,7 +66,7 @@ public:
ALWAYS_INLINE MainWindow* getMainWindow() const { return m_main_window; } ALWAYS_INLINE MainWindow* getMainWindow() const { return m_main_window; }
void setMainWindow(MainWindow* window); void setMainWindow(MainWindow* window);
QtDisplayWidget* createDisplayWidget(); QtHostDisplay* createHostDisplay();
void populateSaveStateMenus(const char* game_code, QMenu* load_menu, QMenu* save_menu); void populateSaveStateMenus(const char* game_code, QMenu* load_menu, QMenu* save_menu);
@ -87,11 +88,10 @@ Q_SIGNALS:
void emulationPaused(bool paused); void emulationPaused(bool paused);
void stateSaved(const QString& game_code, bool global, qint32 slot); void stateSaved(const QString& game_code, bool global, qint32 slot);
void gameListRefreshed(); void gameListRefreshed();
void createDisplayWindowRequested(QThread* worker_thread, bool use_debug_device, bool fullscreen, void createDisplayRequested(QThread* worker_thread, bool use_debug_device, bool fullscreen, bool render_to_main);
bool render_to_main); void updateDisplayRequested(QThread* worker_thread, bool fullscreen, bool render_to_main);
void destroyDisplayWindowRequested();
void updateDisplayWindowRequested(bool fullscreen, bool render_to_main);
void focusDisplayWidgetRequested(); void focusDisplayWidgetRequested();
void destroyDisplayRequested();
void systemPerformanceCountersUpdated(float speed, float fps, float vps, float avg_frame_time, void systemPerformanceCountersUpdated(float speed, float fps, float vps, float avg_frame_time,
float worst_frame_time); float worst_frame_time);
void runningGameChanged(const QString& filename, const QString& game_code, const QString& game_title); void runningGameChanged(const QString& filename, const QString& game_code, const QString& game_title);
@ -103,7 +103,7 @@ public Q_SLOTS:
void applySettings(); void applySettings();
void updateInputMap(); void updateInputMap();
void applyInputProfile(const QString& profile_path); void applyInputProfile(const QString& profile_path);
void handleKeyEvent(int key, bool pressed); void onDisplayWindowKeyEvent(int key, bool pressed);
void bootSystem(const SystemBootParameters& params); void bootSystem(const SystemBootParameters& params);
void resumeSystemFromState(const QString& filename, bool boot_on_failure); void resumeSystemFromState(const QString& filename, bool boot_on_failure);
void powerOffSystem(); void powerOffSystem();
@ -129,7 +129,7 @@ public Q_SLOTS:
private Q_SLOTS: private Q_SLOTS:
void doStopThread(); void doStopThread();
void onDisplayWidgetResized(int width, int height); void onHostDisplayWindowResized(int width, int height);
void doBackgroundControllerPoll(); void doBackgroundControllerPoll();
protected: protected:
@ -180,6 +180,8 @@ private:
Common::Event m_init_event; Common::Event m_init_event;
}; };
QtHostDisplay* getHostDisplay();
void createBackgroundControllerPollTimer(); void createBackgroundControllerPollTimer();
void destroyBackgroundControllerPollTimer(); void destroyBackgroundControllerPollTimer();
@ -189,13 +191,15 @@ private:
bool initializeOnThread(); bool initializeOnThread();
void shutdownOnThread(); void shutdownOnThread();
void renderDisplay(); void renderDisplay();
void connectDisplaySignals();
void disconnectDisplaySignals();
void updateDisplayState();
void wakeThread(); void wakeThread();
std::unique_ptr<QSettings> m_qsettings; std::unique_ptr<QSettings> m_qsettings;
std::recursive_mutex m_qsettings_mutex; std::recursive_mutex m_qsettings_mutex;
MainWindow* m_main_window = nullptr; MainWindow* m_main_window = nullptr;
QtDisplayWidget* m_display_widget = nullptr;
QThread* m_original_thread = nullptr; QThread* m_original_thread = nullptr;
Thread* m_worker_thread = nullptr; Thread* m_worker_thread = nullptr;
QEventLoop* m_worker_thread_event_loop = nullptr; QEventLoop* m_worker_thread_event_loop = nullptr;