Qt: Refactor render widget state transitions
Recreate widget each time. Fixes fullscreen mode switches on D3D11 and hopefully Wayland.
This commit is contained in:
parent
d7aa514f14
commit
ea3c0b65cf
|
@ -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()
|
||||||
|
|
|
@ -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);
|
|
@ -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();
|
||||||
|
|
|
@ -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" />
|
||||||
|
|
|
@ -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" />
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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, ¤t_fbo);
|
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, ¤t_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);
|
|
@ -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;
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
|
@ -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;
|
||||||
|
};
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue