Rework GPU initialization/teardown
This commit is contained in:
parent
1cf8413ec5
commit
7625385f27
|
@ -61,20 +61,20 @@ constexpr T PreviousPow2(T value)
|
|||
return value - (value >> 1);
|
||||
}
|
||||
|
||||
|
||||
ALWAYS_INLINE static void* AlignedMalloc(size_t size, size_t alignment)
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
return _aligned_malloc(size, alignment);
|
||||
#else
|
||||
// Unaligned sizes are slow on macOS.
|
||||
#ifdef __APPLE__
|
||||
#ifdef __APPLE__
|
||||
if (IsPow2(alignment))
|
||||
size = (size + alignment - 1) & ~(alignment - 1);
|
||||
#endif
|
||||
#endif
|
||||
void* ret = nullptr;
|
||||
posix_memalign(&ret, alignment, size);
|
||||
return ret;
|
||||
#endif
|
||||
}
|
||||
|
||||
ALWAYS_INLINE static void AlignedFree(void* ptr)
|
||||
|
@ -86,5 +86,4 @@ ALWAYS_INLINE static void AlignedFree(void* ptr)
|
|||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
} // namespace Common
|
||||
|
|
|
@ -159,29 +159,7 @@ public:
|
|||
float ComputeVerticalFrequency() const;
|
||||
float GetDisplayAspectRatio() const;
|
||||
|
||||
#ifdef _WIN32
|
||||
// gpu_hw_d3d11.cpp
|
||||
static std::unique_ptr<GPU> CreateHardwareD3D11Renderer();
|
||||
|
||||
// gpu_hw_d3d12.cpp
|
||||
static std::unique_ptr<GPU> CreateHardwareD3D12Renderer();
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
static std::unique_ptr<GPU> CreateHardwareMetalRenderer();
|
||||
#endif
|
||||
|
||||
#ifdef WITH_OPENGL
|
||||
// gpu_hw_opengl.cpp
|
||||
static std::unique_ptr<GPU> CreateHardwareOpenGLRenderer();
|
||||
#endif
|
||||
|
||||
#ifdef WITH_VULKAN
|
||||
// gpu_hw_vulkan.cpp
|
||||
static std::unique_ptr<GPU> CreateHardwareVulkanRenderer();
|
||||
#endif
|
||||
|
||||
// gpu_sw.cpp
|
||||
static std::unique_ptr<GPU> CreateHardwareRenderer();
|
||||
static std::unique_ptr<GPU> CreateSoftwareRenderer();
|
||||
|
||||
// Converts window coordinates into horizontal ticks and scanlines. Returns false if out of range. Used for lightguns.
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
|
||||
#include "d3d11_device.h"
|
||||
#include "../host_settings.h"
|
||||
#include "../settings.h"
|
||||
#include "../shader_cache_version.h"
|
||||
|
||||
#include "common/align.h"
|
||||
|
@ -11,6 +10,7 @@
|
|||
#include "common/file_system.h"
|
||||
#include "common/log.h"
|
||||
#include "common/path.h"
|
||||
#include "common/rectangle.h"
|
||||
#include "common/string_util.h"
|
||||
|
||||
#include "fmt/format.h"
|
||||
|
@ -48,6 +48,12 @@ static void SetD3DDebugObjectName(ID3D11DeviceChild* obj, const std::string_view
|
|||
#endif
|
||||
}
|
||||
|
||||
// TODO: FIXME
|
||||
namespace Host {
|
||||
extern bool IsFullscreen();
|
||||
extern void SetFullscreen(bool enabled);
|
||||
}
|
||||
|
||||
D3D11StreamBuffer::D3D11StreamBuffer() : m_size(0), m_position(0)
|
||||
{
|
||||
}
|
||||
|
@ -155,13 +161,8 @@ D3D11Device::D3D11Device() = default;
|
|||
|
||||
D3D11Device::~D3D11Device()
|
||||
{
|
||||
// TODO: Make virtual Destroy() method instead due to order of shit..
|
||||
DestroyStagingBuffer();
|
||||
DestroyResources();
|
||||
DestroyBuffers();
|
||||
DestroySurface();
|
||||
m_context.Reset();
|
||||
m_device.Reset();
|
||||
// Should all be torn down by now.
|
||||
Assert(!m_device);
|
||||
}
|
||||
|
||||
RenderAPI D3D11Device::GetRenderAPI() const
|
||||
|
@ -321,7 +322,7 @@ void D3D11Device::ResolveTextureRegion(GPUTexture* dst, u32 dst_x, u32 dst_y, u3
|
|||
|
||||
bool D3D11Device::GetHostRefreshRate(float* refresh_rate)
|
||||
{
|
||||
if (m_swap_chain && IsFullscreen())
|
||||
if (m_swap_chain && m_is_exclusive_fullscreen)
|
||||
{
|
||||
DXGI_SWAP_CHAIN_DESC desc;
|
||||
if (SUCCEEDED(m_swap_chain->GetDesc(&desc)) && desc.BufferDesc.RefreshRate.Numerator > 0 &&
|
||||
|
@ -343,10 +344,10 @@ void D3D11Device::SetVSync(bool enabled)
|
|||
m_vsync_enabled = enabled;
|
||||
}
|
||||
|
||||
bool D3D11Device::CreateDevice(const WindowInfo& wi, bool vsync)
|
||||
bool D3D11Device::CreateDevice(const std::string_view& adapter, bool debug_device)
|
||||
{
|
||||
UINT create_flags = 0;
|
||||
if (g_settings.gpu_use_debug_device)
|
||||
if (debug_device)
|
||||
create_flags |= D3D11_CREATE_DEVICE_DEBUG;
|
||||
|
||||
ComPtr<IDXGIFactory> temp_dxgi_factory;
|
||||
|
@ -358,18 +359,19 @@ bool D3D11Device::CreateDevice(const WindowInfo& wi, bool vsync)
|
|||
}
|
||||
|
||||
u32 adapter_index;
|
||||
if (!g_settings.gpu_adapter.empty())
|
||||
if (!adapter.empty())
|
||||
{
|
||||
AdapterAndModeList adapter_info(GetAdapterAndModeList(temp_dxgi_factory.Get()));
|
||||
for (adapter_index = 0; adapter_index < static_cast<u32>(adapter_info.adapter_names.size()); adapter_index++)
|
||||
{
|
||||
if (g_settings.gpu_adapter == adapter_info.adapter_names[adapter_index])
|
||||
if (adapter == adapter_info.adapter_names[adapter_index])
|
||||
break;
|
||||
}
|
||||
if (adapter_index == static_cast<u32>(adapter_info.adapter_names.size()))
|
||||
{
|
||||
Log_WarningPrintf("Could not find adapter '%s', using first (%s)", g_settings.gpu_adapter.c_str(),
|
||||
adapter_info.adapter_names[0].c_str());
|
||||
// TODO: Log_Fmt
|
||||
Log_WarningPrintf(
|
||||
fmt::format("Could not find adapter '{}', using first ({})", adapter, adapter_info.adapter_names[0]).c_str());
|
||||
adapter_index = 0;
|
||||
}
|
||||
}
|
||||
|
@ -408,7 +410,7 @@ bool D3D11Device::CreateDevice(const WindowInfo& wi, bool vsync)
|
|||
return false;
|
||||
}
|
||||
|
||||
if (g_settings.gpu_use_debug_device && IsDebuggerPresent())
|
||||
if (debug_device && IsDebuggerPresent())
|
||||
{
|
||||
ComPtr<ID3D11InfoQueue> info;
|
||||
hr = m_device.As(&info);
|
||||
|
@ -420,7 +422,7 @@ bool D3D11Device::CreateDevice(const WindowInfo& wi, bool vsync)
|
|||
}
|
||||
|
||||
#ifdef _DEBUG
|
||||
if (g_settings.gpu_use_debug_device)
|
||||
if (debug_device)
|
||||
m_context.As(&m_annotation);
|
||||
#endif
|
||||
|
||||
|
@ -452,27 +454,21 @@ bool D3D11Device::CreateDevice(const WindowInfo& wi, bool vsync)
|
|||
|
||||
SetFeatures();
|
||||
|
||||
m_window_info = wi;
|
||||
m_vsync_enabled = vsync;
|
||||
|
||||
if (m_window_info.type != WindowInfo::Type::Surfaceless && !CreateSwapChain(nullptr))
|
||||
{
|
||||
m_window_info = {};
|
||||
if (m_window_info.type != WindowInfo::Type::Surfaceless && !CreateSwapChain())
|
||||
return false;
|
||||
|
||||
if (!CreateBuffers())
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool D3D11Device::SetupDevice()
|
||||
void D3D11Device::DestroyDevice()
|
||||
{
|
||||
if (!GPUDevice::SetupDevice())
|
||||
return false;
|
||||
|
||||
if (!CreateBuffers() || !CreateResources())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
DestroyStagingBuffer();
|
||||
DestroyBuffers();
|
||||
m_context.Reset();
|
||||
m_device.Reset();
|
||||
}
|
||||
|
||||
void D3D11Device::SetFeatures()
|
||||
|
@ -512,58 +508,160 @@ void D3D11Device::SetFeatures()
|
|||
}
|
||||
}
|
||||
|
||||
bool D3D11Device::MakeCurrent()
|
||||
bool D3D11Device::GetRequestedExclusiveFullscreenModeDesc(IDXGIFactory5* factory, const RECT& window_rect, u32 width,
|
||||
u32 height, float refresh_rate, DXGI_FORMAT format,
|
||||
DXGI_MODE_DESC* fullscreen_mode, IDXGIOutput** output)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
// We need to find which monitor the window is located on.
|
||||
const Common::Rectangle<s32> client_rc_vec(window_rect.left, window_rect.top, window_rect.right, window_rect.bottom);
|
||||
|
||||
bool D3D11Device::DoneCurrent()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool D3D11Device::CreateSwapChain(const DXGI_MODE_DESC* fullscreen_mode)
|
||||
{
|
||||
// The window might be on a different adapter to which we are rendering.. so we have to enumerate them all.
|
||||
HRESULT hr;
|
||||
ComPtr<IDXGIOutput> first_output, intersecting_output;
|
||||
|
||||
for (u32 adapter_index = 0; !intersecting_output; adapter_index++)
|
||||
{
|
||||
ComPtr<IDXGIAdapter1> adapter;
|
||||
hr = factory->EnumAdapters1(adapter_index, adapter.GetAddressOf());
|
||||
if (hr == DXGI_ERROR_NOT_FOUND)
|
||||
break;
|
||||
else if (FAILED(hr))
|
||||
continue;
|
||||
|
||||
for (u32 output_index = 0;; output_index++)
|
||||
{
|
||||
ComPtr<IDXGIOutput> this_output;
|
||||
DXGI_OUTPUT_DESC output_desc;
|
||||
hr = adapter->EnumOutputs(output_index, this_output.GetAddressOf());
|
||||
if (hr == DXGI_ERROR_NOT_FOUND)
|
||||
break;
|
||||
else if (FAILED(hr) || FAILED(this_output->GetDesc(&output_desc)))
|
||||
continue;
|
||||
|
||||
const Common::Rectangle<s32> output_rc(output_desc.DesktopCoordinates.left, output_desc.DesktopCoordinates.top,
|
||||
output_desc.DesktopCoordinates.right,
|
||||
output_desc.DesktopCoordinates.bottom);
|
||||
if (!client_rc_vec.Intersects(output_rc))
|
||||
{
|
||||
intersecting_output = std::move(this_output);
|
||||
break;
|
||||
}
|
||||
|
||||
// Fallback to the first monitor.
|
||||
if (!first_output)
|
||||
first_output = std::move(this_output);
|
||||
}
|
||||
}
|
||||
|
||||
if (!intersecting_output)
|
||||
{
|
||||
if (!first_output)
|
||||
{
|
||||
Log_ErrorPrintf("No DXGI output found. Can't use exclusive fullscreen.");
|
||||
return false;
|
||||
}
|
||||
|
||||
Log_WarningPrint("No DXGI output found for window, using first.");
|
||||
intersecting_output = std::move(first_output);
|
||||
}
|
||||
|
||||
DXGI_MODE_DESC request_mode = {};
|
||||
request_mode.Width = width;
|
||||
request_mode.Height = height;
|
||||
request_mode.Format = format;
|
||||
request_mode.RefreshRate.Numerator = static_cast<UINT>(std::floor(refresh_rate * 1000.0f));
|
||||
request_mode.RefreshRate.Denominator = 1000u;
|
||||
|
||||
if (FAILED(hr = intersecting_output->FindClosestMatchingMode(&request_mode, fullscreen_mode, nullptr)) ||
|
||||
request_mode.Format != format)
|
||||
{
|
||||
Log_ErrorPrintf("Failed to find closest matching mode, hr=%08X", hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
*output = intersecting_output.Get();
|
||||
intersecting_output->AddRef();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool D3D11Device::CreateSwapChain()
|
||||
{
|
||||
constexpr DXGI_FORMAT swap_chain_format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
|
||||
if (m_window_info.type != WindowInfo::Type::Win32)
|
||||
return false;
|
||||
|
||||
m_using_flip_model_swap_chain = fullscreen_mode || !Host::GetBoolSettingValue("Display", "UseBlitSwapChain", false);
|
||||
|
||||
const HWND window_hwnd = reinterpret_cast<HWND>(m_window_info.window_handle);
|
||||
RECT client_rc{};
|
||||
GetClientRect(window_hwnd, &client_rc);
|
||||
const u32 width = static_cast<u32>(client_rc.right - client_rc.left);
|
||||
const u32 height = static_cast<u32>(client_rc.bottom - client_rc.top);
|
||||
|
||||
DXGI_SWAP_CHAIN_DESC swap_chain_desc = {};
|
||||
swap_chain_desc.BufferDesc.Width = width;
|
||||
swap_chain_desc.BufferDesc.Height = height;
|
||||
swap_chain_desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
DXGI_MODE_DESC fullscreen_mode = {};
|
||||
ComPtr<IDXGIOutput> fullscreen_output;
|
||||
if (Host::IsFullscreen())
|
||||
{
|
||||
u32 fullscreen_width, fullscreen_height;
|
||||
float fullscreen_refresh_rate;
|
||||
m_is_exclusive_fullscreen =
|
||||
GetRequestedExclusiveFullscreenMode(&fullscreen_width, &fullscreen_height, &fullscreen_refresh_rate) &&
|
||||
GetRequestedExclusiveFullscreenModeDesc(m_dxgi_factory.Get(), client_rc, fullscreen_width, fullscreen_height,
|
||||
fullscreen_refresh_rate, swap_chain_format, &fullscreen_mode,
|
||||
fullscreen_output.GetAddressOf());
|
||||
}
|
||||
else
|
||||
{
|
||||
m_is_exclusive_fullscreen = false;
|
||||
}
|
||||
|
||||
m_using_flip_model_swap_chain =
|
||||
!Host::GetBoolSettingValue("Display", "UseBlitSwapChain", false) || m_is_exclusive_fullscreen;
|
||||
|
||||
DXGI_SWAP_CHAIN_DESC1 swap_chain_desc = {};
|
||||
swap_chain_desc.Width = static_cast<u32>(client_rc.right - client_rc.left);
|
||||
swap_chain_desc.Height = static_cast<u32>(client_rc.bottom - client_rc.top);
|
||||
swap_chain_desc.Format = swap_chain_format;
|
||||
swap_chain_desc.SampleDesc.Count = 1;
|
||||
swap_chain_desc.BufferCount = 2;
|
||||
swap_chain_desc.BufferCount = 3;
|
||||
swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
|
||||
swap_chain_desc.OutputWindow = window_hwnd;
|
||||
swap_chain_desc.Windowed = TRUE;
|
||||
swap_chain_desc.SwapEffect = m_using_flip_model_swap_chain ? DXGI_SWAP_EFFECT_FLIP_DISCARD : DXGI_SWAP_EFFECT_DISCARD;
|
||||
|
||||
m_using_allow_tearing = (m_allow_tearing_supported && m_using_flip_model_swap_chain && !fullscreen_mode);
|
||||
m_using_allow_tearing = (m_allow_tearing_supported && m_using_flip_model_swap_chain && !m_is_exclusive_fullscreen);
|
||||
if (m_using_allow_tearing)
|
||||
swap_chain_desc.Flags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;
|
||||
|
||||
if (fullscreen_mode)
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
if (m_is_exclusive_fullscreen)
|
||||
{
|
||||
swap_chain_desc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
|
||||
swap_chain_desc.Windowed = FALSE;
|
||||
swap_chain_desc.BufferDesc = *fullscreen_mode;
|
||||
DXGI_SWAP_CHAIN_DESC1 fs_sd_desc = swap_chain_desc;
|
||||
DXGI_SWAP_CHAIN_FULLSCREEN_DESC fs_desc = {};
|
||||
|
||||
fs_sd_desc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
|
||||
fs_sd_desc.Width = fullscreen_mode.Width;
|
||||
fs_sd_desc.Height = fullscreen_mode.Height;
|
||||
fs_desc.RefreshRate = fullscreen_mode.RefreshRate;
|
||||
fs_desc.ScanlineOrdering = fullscreen_mode.ScanlineOrdering;
|
||||
fs_desc.Scaling = fullscreen_mode.Scaling;
|
||||
fs_desc.Windowed = FALSE;
|
||||
|
||||
Log_VerbosePrintf("Creating a %dx%d exclusive fullscreen swap chain", fs_sd_desc.Width, fs_sd_desc.Height);
|
||||
hr = m_dxgi_factory->CreateSwapChainForHwnd(m_device.Get(), window_hwnd, &fs_sd_desc, &fs_desc,
|
||||
fullscreen_output.Get(), m_swap_chain.ReleaseAndGetAddressOf());
|
||||
if (FAILED(hr))
|
||||
{
|
||||
Log_WarningPrintf("Failed to create fullscreen swap chain, trying windowed.");
|
||||
m_is_exclusive_fullscreen = false;
|
||||
m_using_allow_tearing = m_allow_tearing_supported && m_using_flip_model_swap_chain;
|
||||
}
|
||||
}
|
||||
|
||||
Log_InfoPrintf("Creating a %dx%d %s %s swap chain", swap_chain_desc.BufferDesc.Width,
|
||||
swap_chain_desc.BufferDesc.Height, m_using_flip_model_swap_chain ? "flip-discard" : "discard",
|
||||
swap_chain_desc.Windowed ? "windowed" : "full-screen");
|
||||
if (!m_is_exclusive_fullscreen)
|
||||
{
|
||||
Log_VerbosePrintf("Creating a %dx%d %s windowed swap chain", swap_chain_desc.Width, swap_chain_desc.Height,
|
||||
m_using_flip_model_swap_chain ? "flip-discard" : "discard");
|
||||
hr = m_dxgi_factory->CreateSwapChainForHwnd(m_device.Get(), window_hwnd, &swap_chain_desc, nullptr, nullptr,
|
||||
m_swap_chain.ReleaseAndGetAddressOf());
|
||||
}
|
||||
|
||||
hr = m_dxgi_factory->CreateSwapChain(m_device.Get(), &swap_chain_desc, m_swap_chain.GetAddressOf());
|
||||
if (FAILED(hr) && m_using_flip_model_swap_chain)
|
||||
{
|
||||
Log_WarningPrintf("Failed to create a flip-discard swap chain, trying discard.");
|
||||
|
@ -572,24 +670,29 @@ bool D3D11Device::CreateSwapChain(const DXGI_MODE_DESC* fullscreen_mode)
|
|||
m_using_flip_model_swap_chain = false;
|
||||
m_using_allow_tearing = false;
|
||||
|
||||
hr = m_dxgi_factory->CreateSwapChain(m_device.Get(), &swap_chain_desc, m_swap_chain.GetAddressOf());
|
||||
hr = m_dxgi_factory->CreateSwapChainForHwnd(m_device.Get(), window_hwnd, &swap_chain_desc, nullptr, nullptr,
|
||||
m_swap_chain.ReleaseAndGetAddressOf());
|
||||
if (FAILED(hr))
|
||||
{
|
||||
Log_ErrorPrintf("CreateSwapChain failed: 0x%08X", hr);
|
||||
Log_ErrorPrintf("CreateSwapChainForHwnd failed: 0x%08X", hr);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ComPtr<IDXGIFactory> dxgi_factory;
|
||||
hr = m_swap_chain->GetParent(IID_PPV_ARGS(dxgi_factory.GetAddressOf()));
|
||||
if (SUCCEEDED(hr))
|
||||
hr = m_dxgi_factory->MakeWindowAssociation(window_hwnd, DXGI_MWA_NO_WINDOW_CHANGES);
|
||||
if (FAILED(hr))
|
||||
Log_WarningPrintf("MakeWindowAssociation() to disable ALT+ENTER failed");
|
||||
|
||||
if (!CreateSwapChainRTV())
|
||||
{
|
||||
hr = dxgi_factory->MakeWindowAssociation(swap_chain_desc.OutputWindow, DXGI_MWA_NO_WINDOW_CHANGES);
|
||||
if (FAILED(hr))
|
||||
Log_WarningPrintf("MakeWindowAssociation() to disable ALT+ENTER failed");
|
||||
DestroySwapChain();
|
||||
return false;
|
||||
}
|
||||
|
||||
return CreateSwapChainRTV();
|
||||
// Render a frame as soon as possible to clear out whatever was previously being displayed.
|
||||
m_context->ClearRenderTargetView(m_swap_chain_rtv.Get(), s_clear_color.data());
|
||||
m_swap_chain->Present(0, m_using_allow_tearing ? DXGI_PRESENT_ALLOW_TEARING : 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool D3D11Device::CreateSwapChainRTV()
|
||||
|
@ -607,16 +710,17 @@ bool D3D11Device::CreateSwapChainRTV()
|
|||
|
||||
CD3D11_RENDER_TARGET_VIEW_DESC rtv_desc(D3D11_RTV_DIMENSION_TEXTURE2D, backbuffer_desc.Format, 0, 0,
|
||||
backbuffer_desc.ArraySize);
|
||||
hr = m_device->CreateRenderTargetView(backbuffer.Get(), &rtv_desc, m_swap_chain_rtv.GetAddressOf());
|
||||
hr = m_device->CreateRenderTargetView(backbuffer.Get(), &rtv_desc, m_swap_chain_rtv.ReleaseAndGetAddressOf());
|
||||
if (FAILED(hr))
|
||||
{
|
||||
Log_ErrorPrintf("CreateRenderTargetView for swap chain failed: 0x%08X", hr);
|
||||
m_swap_chain_rtv.Reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
m_window_info.surface_width = backbuffer_desc.Width;
|
||||
m_window_info.surface_height = backbuffer_desc.Height;
|
||||
Log_InfoPrintf("Swap chain buffer size: %ux%u", m_window_info.surface_width, m_window_info.surface_height);
|
||||
Log_VerbosePrintf("Swap chain buffer size: %ux%u", m_window_info.surface_width, m_window_info.surface_height);
|
||||
|
||||
if (m_window_info.type == WindowInfo::Type::Win32)
|
||||
{
|
||||
|
@ -637,22 +741,41 @@ bool D3D11Device::CreateSwapChainRTV()
|
|||
return true;
|
||||
}
|
||||
|
||||
bool D3D11Device::ChangeWindow(const WindowInfo& new_wi)
|
||||
void D3D11Device::DestroySwapChain()
|
||||
{
|
||||
DestroySurface();
|
||||
if (!m_swap_chain)
|
||||
return;
|
||||
|
||||
m_window_info = new_wi;
|
||||
return CreateSwapChain(nullptr);
|
||||
m_swap_chain_rtv.Reset();
|
||||
|
||||
// switch out of fullscreen before destroying
|
||||
BOOL is_fullscreen;
|
||||
if (SUCCEEDED(m_swap_chain->GetFullscreenState(&is_fullscreen, nullptr)) && is_fullscreen)
|
||||
m_swap_chain->SetFullscreenState(FALSE, nullptr);
|
||||
|
||||
m_swap_chain.Reset();
|
||||
m_is_exclusive_fullscreen = false;
|
||||
}
|
||||
|
||||
bool D3D11Device::UpdateWindow()
|
||||
{
|
||||
DestroySwapChain();
|
||||
|
||||
if (!AcquireWindow(false))
|
||||
return false;
|
||||
|
||||
if (m_window_info.type != WindowInfo::Type::Surfaceless && !CreateSwapChain())
|
||||
{
|
||||
Log_ErrorPrintf("Failed to create swap chain on updated window");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void D3D11Device::DestroySurface()
|
||||
{
|
||||
m_window_info.SetSurfaceless();
|
||||
if (IsFullscreen())
|
||||
SetFullscreen(false, 0, 0, 0.0f);
|
||||
|
||||
m_swap_chain_rtv.Reset();
|
||||
m_swap_chain.Reset();
|
||||
DestroySwapChain();
|
||||
}
|
||||
|
||||
std::string D3D11Device::GetShaderCacheBaseName(const std::string_view& type, bool debug) const
|
||||
|
@ -670,11 +793,19 @@ std::string D3D11Device::GetShaderCacheBaseName(const std::string_view& type, bo
|
|||
return fmt::format("d3d_{}_{}{}", type, flname, debug ? "_debug" : "");
|
||||
}
|
||||
|
||||
void D3D11Device::ResizeWindow(s32 new_window_width, s32 new_window_height)
|
||||
void D3D11Device::ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale)
|
||||
{
|
||||
if (!m_swap_chain)
|
||||
if (!m_swap_chain || m_is_exclusive_fullscreen)
|
||||
return;
|
||||
|
||||
m_window_info.surface_scale = new_window_scale;
|
||||
|
||||
if (m_window_info.surface_width == static_cast<u32>(new_window_width) &&
|
||||
m_window_info.surface_height == static_cast<u32>(new_window_height))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_swap_chain_rtv.Reset();
|
||||
|
||||
HRESULT hr = m_swap_chain->ResizeBuffers(0, 0, 0, DXGI_FORMAT_UNKNOWN,
|
||||
|
@ -686,79 +817,11 @@ void D3D11Device::ResizeWindow(s32 new_window_width, s32 new_window_height)
|
|||
Panic("Failed to recreate swap chain RTV after resize");
|
||||
}
|
||||
|
||||
bool D3D11Device::SupportsFullscreen() const
|
||||
bool D3D11Device::SupportsExclusiveFullscreen() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool D3D11Device::IsFullscreen()
|
||||
{
|
||||
BOOL is_fullscreen = FALSE;
|
||||
return (m_swap_chain && SUCCEEDED(m_swap_chain->GetFullscreenState(&is_fullscreen, nullptr)) && is_fullscreen);
|
||||
}
|
||||
|
||||
bool D3D11Device::SetFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate)
|
||||
{
|
||||
if (!m_swap_chain)
|
||||
return false;
|
||||
|
||||
BOOL is_fullscreen = FALSE;
|
||||
HRESULT hr = m_swap_chain->GetFullscreenState(&is_fullscreen, nullptr);
|
||||
if (!fullscreen)
|
||||
{
|
||||
// leaving fullscreen
|
||||
if (is_fullscreen)
|
||||
return SUCCEEDED(m_swap_chain->SetFullscreenState(FALSE, nullptr));
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
IDXGIOutput* output;
|
||||
if (FAILED(hr = m_swap_chain->GetContainingOutput(&output)))
|
||||
return false;
|
||||
|
||||
DXGI_SWAP_CHAIN_DESC current_desc;
|
||||
hr = m_swap_chain->GetDesc(¤t_desc);
|
||||
if (FAILED(hr))
|
||||
return false;
|
||||
|
||||
DXGI_MODE_DESC new_mode = current_desc.BufferDesc;
|
||||
new_mode.Width = width;
|
||||
new_mode.Height = height;
|
||||
new_mode.RefreshRate.Numerator = static_cast<UINT>(std::floor(refresh_rate * 1000.0f));
|
||||
new_mode.RefreshRate.Denominator = 1000u;
|
||||
|
||||
DXGI_MODE_DESC closest_mode;
|
||||
if (FAILED(hr = output->FindClosestMatchingMode(&new_mode, &closest_mode, nullptr)) ||
|
||||
new_mode.Format != current_desc.BufferDesc.Format)
|
||||
{
|
||||
Log_ErrorPrintf("Failed to find closest matching mode, hr=%08X", hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (new_mode.Width == current_desc.BufferDesc.Width && new_mode.Height == current_desc.BufferDesc.Height &&
|
||||
new_mode.RefreshRate.Numerator == current_desc.BufferDesc.RefreshRate.Numerator &&
|
||||
new_mode.RefreshRate.Denominator == current_desc.BufferDesc.RefreshRate.Denominator)
|
||||
{
|
||||
Log_InfoPrintf("Fullscreen mode already set");
|
||||
return true;
|
||||
}
|
||||
|
||||
m_swap_chain_rtv.Reset();
|
||||
m_swap_chain.Reset();
|
||||
|
||||
if (!CreateSwapChain(&closest_mode))
|
||||
{
|
||||
Log_ErrorPrintf("Failed to create a fullscreen swap chain");
|
||||
if (!CreateSwapChain(nullptr))
|
||||
Panic("Failed to recreate windowed swap chain");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool D3D11Device::CreateBuffers()
|
||||
{
|
||||
if (!m_vertex_buffer.Create(m_device.Get(), D3D11_BIND_VERTEX_BUFFER, VERTEX_BUFFER_SIZE) ||
|
||||
|
@ -784,6 +847,16 @@ bool D3D11Device::BeginPresent(bool skip_present)
|
|||
if (skip_present || !m_swap_chain)
|
||||
return false;
|
||||
|
||||
// Check if we lost exclusive fullscreen. If so, notify the host, so it can switch to windowed mode.
|
||||
// This might get called repeatedly if it takes a while to switch back, that's the host's problem.
|
||||
BOOL is_fullscreen;
|
||||
if (m_is_exclusive_fullscreen &&
|
||||
(FAILED(m_swap_chain->GetFullscreenState(&is_fullscreen, nullptr)) || !is_fullscreen))
|
||||
{
|
||||
Host::SetFullscreen(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
// When using vsync, the time here seems to include the time for the buffer to become available.
|
||||
// This blows our our GPU usage number considerably, so read the timestamp before the final blit
|
||||
// in this configuration. It does reduce accuracy a little, but better than seeing 100% all of
|
||||
|
@ -791,7 +864,7 @@ bool D3D11Device::BeginPresent(bool skip_present)
|
|||
if (m_vsync_enabled && m_gpu_timing_enabled)
|
||||
PopTimestampQuery();
|
||||
|
||||
static constexpr float clear_color[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
|
||||
static constexpr float clear_color[4] = {0.0f, 0.0f, 0.0f, 1.0f};
|
||||
m_context->ClearRenderTargetView(m_swap_chain_rtv.Get(), clear_color);
|
||||
m_context->OMSetRenderTargets(1, m_swap_chain_rtv.GetAddressOf(), nullptr);
|
||||
m_current_framebuffer = nullptr;
|
||||
|
@ -1367,7 +1440,7 @@ std::unique_ptr<GPUShader> D3D11Device::CreateShaderFromSource(GPUShaderStage st
|
|||
Log_ErrorPrintf("Failed to compile '%s':\n%s", target, error_string.c_str());
|
||||
|
||||
auto fp = FileSystem::OpenManagedCFile(
|
||||
Path::Combine(EmuFolders::DataRoot, fmt::format("bad_shader_{}.txt", s_next_bad_shader_id++)).c_str(), "wb");
|
||||
GetShaderDumpPath(fmt::format("bad_shader_{}.txt", s_next_bad_shader_id++)).c_str(), "wb");
|
||||
if (fp)
|
||||
{
|
||||
std::fwrite(source.data(), source.size(), 1, fp.get());
|
||||
|
@ -1734,7 +1807,7 @@ bool D3D11Texture::Map(void** map, u32* map_stride, u32 x, u32 y, u32 width, u32
|
|||
u32 level /*= 0*/)
|
||||
{
|
||||
if (!m_dynamic || (x + width) > GetMipWidth(level) || (y + height) > GetMipHeight(level) || layer > m_layers ||
|
||||
level > m_levels)
|
||||
level > m_levels)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -1,15 +1,17 @@
|
|||
// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gpu_device.h"
|
||||
#include "postprocessing_chain.h"
|
||||
|
||||
#include "common/timer.h"
|
||||
#include "common/window_info.h"
|
||||
#include "common/windows_headers.h"
|
||||
#include "gpu_device.h"
|
||||
#include "postprocessing_chain.h"
|
||||
|
||||
#include <d3d11_1.h>
|
||||
#include <dxgi.h>
|
||||
#include <dxgi1_5.h>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
@ -258,28 +260,25 @@ public:
|
|||
template<typename T>
|
||||
using ComPtr = Microsoft::WRL::ComPtr<T>;
|
||||
|
||||
D3D11Device();
|
||||
~D3D11Device();
|
||||
|
||||
ALWAYS_INLINE static D3D11Device& GetInstance() { return *static_cast<D3D11Device*>(g_host_display.get()); }
|
||||
ALWAYS_INLINE static ID3D11Device* GetD3DDevice() { return GetInstance().m_device.Get(); }
|
||||
ALWAYS_INLINE static ID3D11DeviceContext* GetD3DContext() { return GetInstance().m_context.Get(); }
|
||||
|
||||
D3D11Device();
|
||||
~D3D11Device();
|
||||
// returns the fullscreen mode to use for the specified dimensions
|
||||
static bool GetRequestedExclusiveFullscreenModeDesc(IDXGIFactory5* factory, const RECT& window_rect, u32 width,
|
||||
u32 height, float refresh_rate, DXGI_FORMAT format,
|
||||
DXGI_MODE_DESC* fullscreen_mode, IDXGIOutput** output);
|
||||
|
||||
RenderAPI GetRenderAPI() const override;
|
||||
|
||||
bool HasSurface() const override;
|
||||
|
||||
bool CreateDevice(const WindowInfo& wi, bool vsync) override;
|
||||
bool SetupDevice() override;
|
||||
|
||||
bool MakeCurrent() override;
|
||||
bool DoneCurrent() override;
|
||||
|
||||
bool ChangeWindow(const WindowInfo& new_wi) override;
|
||||
void ResizeWindow(s32 new_window_width, s32 new_window_height) override;
|
||||
bool SupportsFullscreen() const override;
|
||||
bool IsFullscreen() override;
|
||||
bool SetFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate) override;
|
||||
bool UpdateWindow() override;
|
||||
void ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale) override;
|
||||
bool SupportsExclusiveFullscreen() const override;
|
||||
AdapterAndModeList GetAdapterAndModeList() override;
|
||||
void DestroySurface() override;
|
||||
|
||||
|
@ -346,6 +345,10 @@ public:
|
|||
|
||||
static AdapterAndModeList StaticGetAdapterAndModeList();
|
||||
|
||||
protected:
|
||||
bool CreateDevice(const std::string_view& adapter, bool debug_device) override;
|
||||
void DestroyDevice() override;
|
||||
|
||||
private:
|
||||
using RasterizationStateMap = std::unordered_map<u8, ComPtr<ID3D11RasterizerState>>;
|
||||
using DepthStateMap = std::unordered_map<u8, ComPtr<ID3D11DepthStencilState>>;
|
||||
|
@ -368,8 +371,9 @@ private:
|
|||
bool CheckStagingBufferSize(u32 width, u32 height, DXGI_FORMAT format);
|
||||
void DestroyStagingBuffer();
|
||||
|
||||
bool CreateSwapChain(const DXGI_MODE_DESC* fullscreen_mode);
|
||||
bool CreateSwapChain();
|
||||
bool CreateSwapChainRTV();
|
||||
void DestroySwapChain();
|
||||
|
||||
bool CreateBuffers();
|
||||
void DestroyBuffers();
|
||||
|
@ -388,8 +392,8 @@ private:
|
|||
ComPtr<ID3D11DeviceContext1> m_context;
|
||||
ComPtr<ID3DUserDefinedAnnotation> m_annotation;
|
||||
|
||||
ComPtr<IDXGIFactory> m_dxgi_factory;
|
||||
ComPtr<IDXGISwapChain> m_swap_chain;
|
||||
ComPtr<IDXGIFactory5> m_dxgi_factory;
|
||||
ComPtr<IDXGISwapChain1> m_swap_chain;
|
||||
ComPtr<ID3D11RenderTargetView> m_swap_chain_rtv;
|
||||
|
||||
RasterizationStateMap m_rasterization_states;
|
||||
|
@ -405,6 +409,7 @@ private:
|
|||
bool m_allow_tearing_supported = false;
|
||||
bool m_using_flip_model_swap_chain = true;
|
||||
bool m_using_allow_tearing = false;
|
||||
bool m_is_exclusive_fullscreen = false;
|
||||
|
||||
D3D11StreamBuffer m_vertex_buffer;
|
||||
D3D11StreamBuffer m_index_buffer;
|
||||
|
|
|
@ -28,7 +28,7 @@ D3D12GPUDevice::~D3D12GPUDevice()
|
|||
|
||||
// DestroyRenderSurface() will exec the command list.
|
||||
DestroySurface();
|
||||
DestroyResources();
|
||||
//DestroyResources();
|
||||
g_d3d12_context->Destroy();
|
||||
}
|
||||
|
||||
|
@ -108,6 +108,7 @@ bool D3D12GPUDevice::SupportsTextureFormat(GPUTexture::Format format) const
|
|||
|
||||
bool D3D12GPUDevice::GetHostRefreshRate(float* refresh_rate)
|
||||
{
|
||||
#if 0
|
||||
if (m_swap_chain && IsFullscreen())
|
||||
{
|
||||
DXGI_SWAP_CHAIN_DESC desc;
|
||||
|
@ -121,6 +122,7 @@ bool D3D12GPUDevice::GetHostRefreshRate(float* refresh_rate)
|
|||
return true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return GPUDevice::GetHostRefreshRate(refresh_rate);
|
||||
}
|
||||
|
@ -130,6 +132,7 @@ void D3D12GPUDevice::SetVSync(bool enabled)
|
|||
m_vsync_enabled = enabled;
|
||||
}
|
||||
|
||||
#if 0
|
||||
bool D3D12GPUDevice::CreateDevice(const WindowInfo& wi, bool vsync)
|
||||
{
|
||||
ComPtr<IDXGIFactory> temp_dxgi_factory;
|
||||
|
@ -214,6 +217,7 @@ bool D3D12GPUDevice::DoneCurrent()
|
|||
{
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool D3D12GPUDevice::CreateSwapChain(const DXGI_MODE_DESC* fullscreen_mode)
|
||||
{
|
||||
|
@ -327,6 +331,8 @@ void D3D12GPUDevice::DestroySwapChainRTVs()
|
|||
m_current_swap_chain_buffer = 0;
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
||||
bool D3D12GPUDevice::ChangeWindow(const WindowInfo& new_wi)
|
||||
{
|
||||
DestroySurface();
|
||||
|
@ -334,6 +340,7 @@ bool D3D12GPUDevice::ChangeWindow(const WindowInfo& new_wi)
|
|||
m_window_info = new_wi;
|
||||
return CreateSwapChain(nullptr);
|
||||
}
|
||||
#endif
|
||||
|
||||
void D3D12GPUDevice::DestroySurface()
|
||||
{
|
||||
|
@ -342,12 +349,15 @@ void D3D12GPUDevice::DestroySurface()
|
|||
// For some reason if we don't execute the command list here, the swap chain is in use.. not sure where.
|
||||
g_d3d12_context->ExecuteCommandList(true);
|
||||
|
||||
#if 0
|
||||
if (IsFullscreen())
|
||||
SetFullscreen(false, 0, 0, 0.0f);
|
||||
#endif
|
||||
|
||||
DestroySwapChainRTVs();
|
||||
m_swap_chain.Reset();
|
||||
}
|
||||
#if 0
|
||||
|
||||
void D3D12GPUDevice::ResizeWindow(s32 new_window_width, s32 new_window_height)
|
||||
{
|
||||
|
@ -441,12 +451,14 @@ bool D3D12GPUDevice::SetFullscreen(bool fullscreen, u32 width, u32 height, float
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
GPUDevice::AdapterAndModeList D3D12GPUDevice::GetAdapterAndModeList()
|
||||
{
|
||||
return GetAdapterAndModeList(m_dxgi_factory.Get());
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
||||
bool D3D12GPUDevice::CreateResources()
|
||||
{
|
||||
D3D12::RootSignatureBuilder rsbuilder;
|
||||
|
@ -550,6 +562,7 @@ void D3D12GPUDevice::DestroyResources()
|
|||
m_display_pipeline.Reset();
|
||||
m_display_root_signature.Reset();
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
bool D3D12GPUDevice::CreateImGuiContext()
|
||||
|
@ -619,6 +632,8 @@ float D3D12GPUDevice::GetAndResetAccumulatedGPUTime()
|
|||
return g_d3d12_context->GetAndResetAccumulatedGPUTime();
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
||||
void D3D12GPUDevice::RenderImGui(ID3D12GraphicsCommandList* cmdlist)
|
||||
{
|
||||
ImGui::Render();
|
||||
|
@ -702,6 +717,8 @@ void D3D12GPUDevice::RenderSoftwareCursor(ID3D12GraphicsCommandList* cmdlist, s3
|
|||
cmdlist->DrawInstanced(3, 1, 0, 0);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
GPUDevice::AdapterAndModeList D3D12GPUDevice::StaticGetAdapterAndModeList()
|
||||
{
|
||||
ComPtr<IDXGIFactory> dxgi_factory;
|
||||
|
|
|
@ -32,17 +32,17 @@ public:
|
|||
|
||||
bool HasSurface() const override;
|
||||
|
||||
bool CreateDevice(const WindowInfo& wi, bool vsync) override;
|
||||
bool SetupDevice() override;
|
||||
//bool CreateDevice(const WindowInfo& wi, bool vsync) override;
|
||||
//bool SetupDevice() override;
|
||||
|
||||
bool MakeCurrent() override;
|
||||
bool DoneCurrent() override;
|
||||
//bool MakeCurrent() override;
|
||||
//bool DoneCurrent() override;
|
||||
|
||||
bool ChangeWindow(const WindowInfo& new_wi) override;
|
||||
void ResizeWindow(s32 new_window_width, s32 new_window_height) override;
|
||||
bool SupportsFullscreen() const override;
|
||||
bool IsFullscreen() override;
|
||||
bool SetFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate) override;
|
||||
//bool ChangeWindow(const WindowInfo& new_wi) override;
|
||||
//void ResizeWindow(s32 new_window_width, s32 new_window_height) override;
|
||||
//bool SupportsFullscreen() const override;
|
||||
//bool IsFullscreen() override;
|
||||
//bool SetFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate) override;
|
||||
AdapterAndModeList GetAdapterAndModeList() override;
|
||||
void DestroySurface() override;
|
||||
|
||||
|
@ -69,13 +69,14 @@ public:
|
|||
protected:
|
||||
static AdapterAndModeList GetAdapterAndModeList(IDXGIFactory* dxgi_factory);
|
||||
|
||||
virtual bool CreateResources() override;
|
||||
virtual void DestroyResources() override;
|
||||
//virtual bool CreateResources() override;
|
||||
//virtual void DestroyResources() override;
|
||||
|
||||
bool CreateSwapChain(const DXGI_MODE_DESC* fullscreen_mode);
|
||||
bool CreateSwapChainRTV();
|
||||
void DestroySwapChainRTVs();
|
||||
|
||||
#if 0
|
||||
void RenderDisplay(ID3D12GraphicsCommandList* cmdlist, D3D12::Texture* swap_chain_buf);
|
||||
void RenderSoftwareCursor(ID3D12GraphicsCommandList* cmdlist);
|
||||
void RenderImGui(ID3D12GraphicsCommandList* cmdlist);
|
||||
|
@ -85,6 +86,7 @@ protected:
|
|||
s32 texture_view_height, bool linear_filter);
|
||||
void RenderSoftwareCursor(ID3D12GraphicsCommandList* cmdlist, s32 left, s32 top, s32 width, s32 height,
|
||||
GPUTexture* texture_handle);
|
||||
#endif
|
||||
|
||||
ComPtr<IDXGIFactory> m_dxgi_factory;
|
||||
ComPtr<IDXGISwapChain> m_swap_chain;
|
||||
|
|
|
@ -2,8 +2,10 @@
|
|||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||
|
||||
#include "gpu_device.h"
|
||||
#include "../host_settings.h"
|
||||
#include "../settings.h"
|
||||
#include "../shadergen.h"
|
||||
#include "../system.h"
|
||||
#include "postprocessing_chain.h"
|
||||
|
||||
#include "common/align.h"
|
||||
|
@ -210,21 +212,95 @@ RenderAPI GPUDevice::GetPreferredAPI()
|
|||
#endif
|
||||
}
|
||||
|
||||
bool GPUDevice::SetupDevice()
|
||||
const char* GPUDevice::RenderAPIToString(RenderAPI api)
|
||||
{
|
||||
// TODO: option to disable shader cache
|
||||
if (true)
|
||||
// TODO: Combine ES
|
||||
switch (api)
|
||||
{
|
||||
const std::string basename = GetShaderCacheBaseName("shaders", g_settings.gpu_use_debug_device);
|
||||
const std::string filename = Path::Combine(EmuFolders::Cache, basename);
|
||||
if (!m_shader_cache.Open(filename.c_str()))
|
||||
Log_WarningPrintf("Failed to open shader cache.");
|
||||
// clang-format off
|
||||
#define CASE(x) case RenderAPI::x: return #x
|
||||
CASE(None);
|
||||
CASE(D3D11);
|
||||
CASE(D3D12);
|
||||
CASE(Metal);
|
||||
CASE(Vulkan);
|
||||
CASE(OpenGL);
|
||||
CASE(OpenGLES);
|
||||
#undef CASE
|
||||
// clang-format on
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
else
|
||||
}
|
||||
|
||||
bool GPUDevice::Create(const std::string_view& adapter, const std::string_view& shader_cache_path, bool debug_device,
|
||||
bool vsync)
|
||||
{
|
||||
m_vsync_enabled = vsync;
|
||||
|
||||
if (!AcquireWindow(true))
|
||||
{
|
||||
Log_WarningPrintf("Shader cache is disabled.");
|
||||
Log_ErrorPrintf("Failed to acquire window from host.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!CreateDevice(adapter, debug_device))
|
||||
{
|
||||
Log_ErrorPrintf("Failed to create device.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!shader_cache_path.empty())
|
||||
OpenShaderCache(shader_cache_path, debug_device);
|
||||
else
|
||||
Log_WarningPrintf("Shader cache is disabled.");
|
||||
|
||||
if (!CreateResources())
|
||||
{
|
||||
Log_ErrorPrintf("Failed to create base resources.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GPUDevice::Destroy()
|
||||
{
|
||||
if (HasSurface())
|
||||
DestroySurface();
|
||||
DestroyResources();
|
||||
DestroyDevice();
|
||||
}
|
||||
|
||||
bool GPUDevice::UpdateWindow()
|
||||
{
|
||||
// TODO: REMOVE ME
|
||||
UnreachableCode();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GPUDevice::SupportsExclusiveFullscreen() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void GPUDevice::OpenShaderCache(const std::string_view& base_path, bool debug)
|
||||
{
|
||||
// TODO: option to disable shader cache
|
||||
const std::string basename = GetShaderCacheBaseName("shaders", debug);
|
||||
const std::string filename = Path::Combine(base_path, basename);
|
||||
if (!m_shader_cache.Open(filename.c_str()))
|
||||
Log_WarningPrintf("Failed to open shader cache.");
|
||||
}
|
||||
|
||||
bool GPUDevice::AcquireWindow(bool recreate_window)
|
||||
{
|
||||
std::optional<WindowInfo> wi = Host::AcquireRenderWindow(recreate_window);
|
||||
if (!wi.has_value())
|
||||
return false;
|
||||
|
||||
Log_InfoPrintf("Render window is %ux%u.", wi->surface_width, wi->surface_height);
|
||||
m_window_info = wi.value();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -348,6 +424,12 @@ std::string GPUDevice::GetShaderCacheBaseName(const std::string_view& type, bool
|
|||
return {};
|
||||
}
|
||||
|
||||
void GPUDevice::ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale)
|
||||
{
|
||||
// TODO: REMOVE ME
|
||||
UnreachableCode();
|
||||
}
|
||||
|
||||
void GPUDevice::RenderImGui()
|
||||
{
|
||||
GL_SCOPE("RenderImGui");
|
||||
|
@ -566,6 +648,19 @@ void GPUDevice::InvalidateRenderTarget(GPUTexture* t)
|
|||
t->SetState(GPUTexture::State::Invalidated);
|
||||
}
|
||||
|
||||
bool GPUDevice::CreateDevice(const std::string_view& adapter, bool debug_device)
|
||||
{
|
||||
// TODO: REMOVE ME
|
||||
UnreachableCode();
|
||||
return false;
|
||||
}
|
||||
|
||||
void GPUDevice::DestroyDevice()
|
||||
{
|
||||
// TODO: REMOVE ME
|
||||
UnreachableCode();
|
||||
}
|
||||
|
||||
std::unique_ptr<GPUShader> GPUDevice::CreateShaderFromBinary(GPUShaderStage stage, gsl::span<const u8> data)
|
||||
{
|
||||
// TODO: REMOVE ME
|
||||
|
@ -658,14 +753,16 @@ std::unique_ptr<GPUShader> GPUDevice::CreateShader(GPUShaderStage stage, const s
|
|||
return shader;
|
||||
}
|
||||
|
||||
bool GPUDevice::ParseFullscreenMode(const std::string_view& mode, u32* width, u32* height, float* refresh_rate)
|
||||
bool GPUDevice::GetRequestedExclusiveFullscreenMode(u32* width, u32* height, float* refresh_rate)
|
||||
{
|
||||
const std::string mode = Host::GetBaseStringSettingValue("GPU", "FullscreenMode", "");
|
||||
if (!mode.empty())
|
||||
{
|
||||
const std::string_view mode_view = mode;
|
||||
std::string_view::size_type sep1 = mode.find('x');
|
||||
if (sep1 != std::string_view::npos)
|
||||
{
|
||||
std::optional<u32> owidth = StringUtil::FromChars<u32>(mode.substr(0, sep1));
|
||||
std::optional<u32> owidth = StringUtil::FromChars<u32>(mode_view.substr(0, sep1));
|
||||
sep1++;
|
||||
|
||||
while (sep1 < mode.length() && std::isspace(mode[sep1]))
|
||||
|
@ -676,7 +773,7 @@ bool GPUDevice::ParseFullscreenMode(const std::string_view& mode, u32* width, u3
|
|||
std::string_view::size_type sep2 = mode.find('@', sep1);
|
||||
if (sep2 != std::string_view::npos)
|
||||
{
|
||||
std::optional<u32> oheight = StringUtil::FromChars<u32>(mode.substr(sep1, sep2 - sep1));
|
||||
std::optional<u32> oheight = StringUtil::FromChars<u32>(mode_view.substr(sep1, sep2 - sep1));
|
||||
sep2++;
|
||||
|
||||
while (sep2 < mode.length() && std::isspace(mode[sep2]))
|
||||
|
@ -684,7 +781,7 @@ bool GPUDevice::ParseFullscreenMode(const std::string_view& mode, u32* width, u3
|
|||
|
||||
if (oheight.has_value() && sep2 < mode.length())
|
||||
{
|
||||
std::optional<float> orefresh_rate = StringUtil::FromChars<float>(mode.substr(sep2));
|
||||
std::optional<float> orefresh_rate = StringUtil::FromChars<float>(mode_view.substr(sep2));
|
||||
if (orefresh_rate.has_value())
|
||||
{
|
||||
*width = owidth.value();
|
||||
|
@ -709,6 +806,11 @@ std::string GPUDevice::GetFullscreenModeString(u32 width, u32 height, float refr
|
|||
return StringUtil::StdStringFromFormat("%u x %u @ %f hz", width, height, refresh_rate);
|
||||
}
|
||||
|
||||
std::string GPUDevice::GetShaderDumpPath(const std::string_view& name)
|
||||
{
|
||||
return Path::Combine(EmuFolders::Dumps, name);
|
||||
}
|
||||
|
||||
bool GPUDevice::UpdateImGuiFontTexture()
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
|
@ -1529,7 +1631,7 @@ bool GPUDevice::WriteScreenshotToFile(std::string filename, bool internal_resolu
|
|||
return true;
|
||||
}
|
||||
|
||||
std::unique_ptr<GPUDevice> Host::CreateDisplayForAPI(RenderAPI api)
|
||||
std::unique_ptr<GPUDevice> GPUDevice::CreateDeviceForAPI(RenderAPI api)
|
||||
{
|
||||
switch (api)
|
||||
{
|
||||
|
@ -1558,18 +1660,6 @@ std::unique_ptr<GPUDevice> Host::CreateDisplayForAPI(RenderAPI api)
|
|||
#endif
|
||||
|
||||
default:
|
||||
#if defined(_WIN32) && defined(_M_ARM64)
|
||||
return std::make_unique<D3D12GPUDevice>();
|
||||
#elif defined(_WIN32)
|
||||
return std::make_unique<D3D11Device>();
|
||||
#elif defined(__APPLE__)
|
||||
return WrapNewMetalDevice();
|
||||
#elif defined(WITH_OPENGL)
|
||||
return std::make_unique<OpenGLDevice>();
|
||||
#elif defined(WITH_VULKAN)
|
||||
return std::make_unique<VulkanGPUDevice>();
|
||||
#else
|
||||
return {};
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include "gsl/span"
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <tuple>
|
||||
|
@ -431,12 +432,21 @@ public:
|
|||
/// Returns the default/preferred API for the system.
|
||||
static RenderAPI GetPreferredAPI();
|
||||
|
||||
/// Returns a string representing the specified API.
|
||||
static const char* RenderAPIToString(RenderAPI api);
|
||||
|
||||
/// Returns a new device for the specified API.
|
||||
static std::unique_ptr<GPUDevice> CreateDeviceForAPI(RenderAPI api);
|
||||
|
||||
/// Parses a fullscreen mode into its components (width * height @ refresh hz)
|
||||
static bool ParseFullscreenMode(const std::string_view& mode, u32* width, u32* height, float* refresh_rate);
|
||||
static bool GetRequestedExclusiveFullscreenMode(u32* width, u32* height, float* refresh_rate);
|
||||
|
||||
/// Converts a fullscreen mode to a string.
|
||||
static std::string GetFullscreenModeString(u32 width, u32 height, float refresh_rate);
|
||||
|
||||
/// Returns the directory bad shaders are saved to.
|
||||
static std::string GetShaderDumpPath(const std::string_view& name);
|
||||
|
||||
ALWAYS_INLINE const Features& GetFeatures() const { return m_features; }
|
||||
ALWAYS_INLINE u32 GetMaxTextureSize() const { return m_max_texture_size; }
|
||||
ALWAYS_INLINE u32 GetMaxMultisamples() const { return m_max_multisamples; }
|
||||
|
@ -466,29 +476,23 @@ public:
|
|||
|
||||
virtual RenderAPI GetRenderAPI() const = 0;
|
||||
|
||||
virtual bool CreateDevice(const WindowInfo& wi, bool vsync) = 0;
|
||||
virtual bool SetupDevice();
|
||||
virtual bool MakeCurrent() = 0;
|
||||
virtual bool DoneCurrent() = 0;
|
||||
bool Create(const std::string_view& adapter, const std::string_view& shader_cache_path,
|
||||
bool debug_device, bool vsync);
|
||||
void Destroy();
|
||||
|
||||
virtual bool HasSurface() const = 0;
|
||||
virtual void DestroySurface() = 0;
|
||||
virtual bool ChangeWindow(const WindowInfo& wi) = 0;
|
||||
virtual bool UpdateWindow();
|
||||
|
||||
virtual bool SupportsFullscreen() const = 0;
|
||||
virtual bool IsFullscreen() = 0;
|
||||
virtual bool SetFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate) = 0;
|
||||
virtual bool SupportsExclusiveFullscreen() const;
|
||||
virtual AdapterAndModeList GetAdapterAndModeList() = 0;
|
||||
|
||||
virtual bool CreateResources();
|
||||
virtual void DestroyResources();
|
||||
|
||||
virtual bool SetPostProcessingChain(const std::string_view& config);
|
||||
|
||||
virtual std::string GetShaderCacheBaseName(const std::string_view& type, bool debug) const;
|
||||
|
||||
/// Call when the window size changes externally to recreate any resources.
|
||||
virtual void ResizeWindow(s32 new_window_width, s32 new_window_height) = 0;
|
||||
virtual void ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale);
|
||||
|
||||
/// Creates an abstracted RGBA8 texture. If dynamic, the texture can be updated with UpdateTexture() below.
|
||||
virtual std::unique_ptr<GPUTexture> CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples,
|
||||
|
@ -623,13 +627,37 @@ public:
|
|||
bool WriteScreenshotToFile(std::string filename, bool internal_resolution = false, bool compress_on_thread = false);
|
||||
|
||||
protected:
|
||||
virtual bool CreateDevice(const std::string_view& adapter, bool debug_device);
|
||||
virtual void DestroyDevice();
|
||||
|
||||
virtual std::unique_ptr<GPUShader> CreateShaderFromBinary(GPUShaderStage stage, gsl::span<const u8> data);
|
||||
virtual std::unique_ptr<GPUShader> CreateShaderFromSource(GPUShaderStage stage, const std::string_view& source,
|
||||
std::vector<u8>* out_binary = nullptr);
|
||||
|
||||
bool AcquireWindow(bool recreate_window);
|
||||
|
||||
Features m_features = {};
|
||||
u32 m_max_texture_size = 0;
|
||||
u32 m_max_multisamples = 0;
|
||||
|
||||
WindowInfo m_window_info;
|
||||
|
||||
GPUShaderCache m_shader_cache;
|
||||
|
||||
std::unique_ptr<GPUSampler> m_nearest_sampler;
|
||||
std::unique_ptr<GPUSampler> m_linear_sampler;
|
||||
|
||||
bool m_gpu_timing_enabled = false;
|
||||
bool m_vsync_enabled = false;
|
||||
|
||||
private:
|
||||
ALWAYS_INLINE bool HasSoftwareCursor() const { return static_cast<bool>(m_cursor_texture); }
|
||||
ALWAYS_INLINE bool HasDisplayTexture() const { return (m_display_texture != nullptr); }
|
||||
|
||||
void OpenShaderCache(const std::string_view& base_path, bool debug);
|
||||
bool CreateResources();
|
||||
void DestroyResources();
|
||||
|
||||
bool IsUsingLinearFiltering() const;
|
||||
|
||||
void CalculateDrawRect(s32 window_width, s32 window_height, float* out_left, float* out_top, float* out_width,
|
||||
|
@ -648,17 +676,6 @@ protected:
|
|||
bool linear_filter);
|
||||
void RenderSoftwareCursor(s32 left, s32 top, s32 width, s32 height, GPUTexture* texture);
|
||||
|
||||
Features m_features = {};
|
||||
u32 m_max_texture_size = 0;
|
||||
u32 m_max_multisamples = 0;
|
||||
|
||||
WindowInfo m_window_info;
|
||||
|
||||
GPUShaderCache m_shader_cache;
|
||||
|
||||
std::unique_ptr<GPUSampler> m_nearest_sampler;
|
||||
std::unique_ptr<GPUSampler> m_linear_sampler;
|
||||
|
||||
u64 m_last_frame_displayed_time = 0;
|
||||
|
||||
s32 m_mouse_position_x = 0;
|
||||
|
@ -688,8 +705,6 @@ protected:
|
|||
float m_cursor_texture_scale = 1.0f;
|
||||
|
||||
bool m_display_changed = false;
|
||||
bool m_gpu_timing_enabled = false;
|
||||
bool m_vsync_enabled = false;
|
||||
|
||||
std::unique_ptr<PostProcessingChain> m_post_processing_chain;
|
||||
};
|
||||
|
@ -698,20 +713,15 @@ protected:
|
|||
extern std::unique_ptr<GPUDevice> g_host_display;
|
||||
|
||||
namespace Host {
|
||||
std::unique_ptr<GPUDevice> CreateDisplayForAPI(RenderAPI api);
|
||||
/// Called when the core is creating a render device.
|
||||
/// This could also be fullscreen transition.
|
||||
std::optional<WindowInfo> AcquireRenderWindow(bool recreate_window);
|
||||
|
||||
/// Creates the host display. This may create a new window. The API used depends on the current configuration.
|
||||
bool AcquireHostDisplay(RenderAPI api);
|
||||
/// Called before drawing the OSD and other display elements.
|
||||
void BeginPresentFrame();
|
||||
|
||||
/// Destroys the host display. This may close the display window.
|
||||
void ReleaseHostDisplay();
|
||||
|
||||
/// Returns false if the window was completely occluded. If frame_skip is set, the frame won't be
|
||||
/// displayed, but the GPU command queue will still be flushed.
|
||||
// bool BeginPresentFrame(bool frame_skip);
|
||||
|
||||
/// Presents the frame to the display, and renders OSD elements.
|
||||
// void EndPresentFrame();
|
||||
/// Called when the core is finished with a render window.
|
||||
void ReleaseRenderWindow();
|
||||
|
||||
/// Provided by the host; renders the display.
|
||||
void RenderDisplay(bool skip_present);
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||
|
||||
#include "opengl_device.h"
|
||||
#include "../settings.h"
|
||||
#include "opengl_pipeline.h"
|
||||
#include "opengl_stream_buffer.h"
|
||||
#include "opengl_texture.h"
|
||||
|
@ -13,6 +12,8 @@
|
|||
#include "common/log.h"
|
||||
#include "common/string_util.h"
|
||||
|
||||
#include "fmt/format.h"
|
||||
|
||||
#include <array>
|
||||
#include <tuple>
|
||||
|
||||
|
@ -28,15 +29,7 @@ OpenGLDevice::OpenGLDevice()
|
|||
|
||||
OpenGLDevice::~OpenGLDevice()
|
||||
{
|
||||
// TODO: Destroy() function
|
||||
if (!m_gl_context)
|
||||
return;
|
||||
|
||||
DestroyResources();
|
||||
DestroyBuffers();
|
||||
|
||||
m_gl_context->DoneCurrent();
|
||||
m_gl_context.reset();
|
||||
Assert(!m_gl_context);
|
||||
}
|
||||
|
||||
void OpenGLDevice::BindUpdateTextureUnit()
|
||||
|
@ -201,7 +194,7 @@ void OpenGLDevice::ResolveTextureRegion(GPUTexture* dst, u32 dst_x, u32 dst_y, u
|
|||
void OpenGLDevice::PushDebugGroup(const char* fmt, ...)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
if (!glPushDebugGroup || !g_settings.gpu_use_debug_device)
|
||||
if (!glPushDebugGroup)
|
||||
return;
|
||||
|
||||
std::va_list ap;
|
||||
|
@ -216,7 +209,7 @@ void OpenGLDevice::PushDebugGroup(const char* fmt, ...)
|
|||
void OpenGLDevice::PopDebugGroup()
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
if (!glPopDebugGroup || !g_settings.gpu_use_debug_device)
|
||||
if (!glPopDebugGroup)
|
||||
return;
|
||||
|
||||
glPopDebugGroup();
|
||||
|
@ -226,7 +219,7 @@ void OpenGLDevice::PopDebugGroup()
|
|||
void OpenGLDevice::InsertDebugMessage(const char* fmt, ...)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
if (!glDebugMessageInsert || !g_settings.gpu_use_debug_device)
|
||||
if (!glDebugMessageInsert)
|
||||
return;
|
||||
|
||||
std::va_list ap;
|
||||
|
@ -275,9 +268,9 @@ bool OpenGLDevice::HasSurface() const
|
|||
return m_window_info.type != WindowInfo::Type::Surfaceless;
|
||||
}
|
||||
|
||||
bool OpenGLDevice::CreateDevice(const WindowInfo& wi, bool vsync)
|
||||
bool OpenGLDevice::CreateDevice(const std::string_view& adapter, bool debug_device)
|
||||
{
|
||||
m_gl_context = GL::Context::Create(wi);
|
||||
m_gl_context = GL::Context::Create(m_window_info);
|
||||
if (!m_gl_context)
|
||||
{
|
||||
Log_ErrorPrintf("Failed to create any GL context");
|
||||
|
@ -285,18 +278,26 @@ bool OpenGLDevice::CreateDevice(const WindowInfo& wi, bool vsync)
|
|||
return false;
|
||||
}
|
||||
|
||||
// Is this needed?
|
||||
m_window_info = m_gl_context->GetWindowInfo();
|
||||
m_vsync_enabled = vsync;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenGLDevice::SetupDevice()
|
||||
{
|
||||
if (!GPUDevice::SetupDevice())
|
||||
return false;
|
||||
#if 0
|
||||
// TODO: add these checks
|
||||
const bool opengl_is_available = ((g_host_display->GetRenderAPI() == RenderAPI::OpenGL &&
|
||||
(GLAD_GL_VERSION_3_0 || GLAD_GL_ARB_uniform_buffer_object)) ||
|
||||
(g_host_display->GetRenderAPI() == RenderAPI::OpenGLES && GLAD_GL_ES_VERSION_3_1));
|
||||
if (!opengl_is_available)
|
||||
{
|
||||
Host::AddOSDMessage(Host::TranslateStdString("OSDMessage",
|
||||
"OpenGL renderer unavailable, your driver or hardware is not "
|
||||
"recent enough. OpenGL 3.1 or OpenGL ES 3.1 is required."),
|
||||
20.0f);
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
OpenGLTexture::s_use_pbo_for_uploads = true;
|
||||
if (GetRenderAPI() == RenderAPI::OpenGLES)
|
||||
if (m_gl_context->IsGLES())
|
||||
{
|
||||
// Adreno seems to corrupt textures through PBOs... and Mali is slow.
|
||||
const char* gl_vendor = reinterpret_cast<const char*>(glGetString(GL_VENDOR));
|
||||
|
@ -306,9 +307,9 @@ bool OpenGLDevice::SetupDevice()
|
|||
|
||||
Log_VerbosePrintf("Using PBO for uploads: %s", OpenGLTexture::s_use_pbo_for_uploads ? "yes" : "no");
|
||||
|
||||
if (g_settings.gpu_use_debug_device && GLAD_GL_KHR_debug)
|
||||
if (debug_device && GLAD_GL_KHR_debug)
|
||||
{
|
||||
if (GetRenderAPI() == RenderAPI::OpenGLES)
|
||||
if (m_gl_context->IsGLES())
|
||||
glDebugMessageCallbackKHR(GLDebugCallback, nullptr);
|
||||
else
|
||||
glDebugMessageCallback(GLDebugCallback, nullptr);
|
||||
|
@ -316,11 +317,19 @@ bool OpenGLDevice::SetupDevice()
|
|||
glEnable(GL_DEBUG_OUTPUT);
|
||||
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Nail the function pointers so that we don't waste time calling them.
|
||||
glPushDebugGroup = nullptr;
|
||||
glPopDebugGroup = nullptr;
|
||||
glDebugMessageInsert = nullptr;
|
||||
glObjectLabel = nullptr;
|
||||
}
|
||||
|
||||
if (!CheckFeatures())
|
||||
return false;
|
||||
|
||||
if (!CreateBuffers() || !CreateResources())
|
||||
if (!CreateBuffers())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
@ -391,28 +400,27 @@ bool OpenGLDevice::CheckFeatures()
|
|||
return true;
|
||||
}
|
||||
|
||||
bool OpenGLDevice::MakeCurrent()
|
||||
void OpenGLDevice::DestroyDevice()
|
||||
{
|
||||
if (!m_gl_context->MakeCurrent())
|
||||
{
|
||||
Log_ErrorPrintf("Failed to make GL context current");
|
||||
return false;
|
||||
}
|
||||
if (!m_gl_context)
|
||||
return;
|
||||
|
||||
SetSwapInterval();
|
||||
return true;
|
||||
DestroyBuffers();
|
||||
|
||||
m_gl_context->DoneCurrent();
|
||||
m_gl_context.reset();
|
||||
}
|
||||
|
||||
bool OpenGLDevice::DoneCurrent()
|
||||
{
|
||||
return m_gl_context->DoneCurrent();
|
||||
}
|
||||
|
||||
bool OpenGLDevice::ChangeWindow(const WindowInfo& new_wi)
|
||||
bool OpenGLDevice::UpdateWindow()
|
||||
{
|
||||
Assert(m_gl_context);
|
||||
|
||||
if (!m_gl_context->ChangeSurface(new_wi))
|
||||
DestroySurface();
|
||||
|
||||
if (!AcquireWindow(false))
|
||||
return false;
|
||||
|
||||
if (!m_gl_context->ChangeSurface(m_window_info))
|
||||
{
|
||||
Log_ErrorPrintf("Failed to change surface");
|
||||
return false;
|
||||
|
@ -420,17 +428,24 @@ bool OpenGLDevice::ChangeWindow(const WindowInfo& new_wi)
|
|||
|
||||
m_window_info = m_gl_context->GetWindowInfo();
|
||||
|
||||
// Update swap interval for new surface.
|
||||
if (m_gl_context->IsCurrent())
|
||||
if (m_window_info.type != WindowInfo::Type::Surfaceless)
|
||||
{
|
||||
// reset vsync rate, since it (usually) gets lost
|
||||
SetSwapInterval();
|
||||
// TODO RenderBlankFrame();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void OpenGLDevice::ResizeWindow(s32 new_window_width, s32 new_window_height)
|
||||
void OpenGLDevice::ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale)
|
||||
{
|
||||
if (!m_gl_context)
|
||||
m_window_info.surface_scale = new_window_scale;
|
||||
if (m_window_info.surface_width == static_cast<u32>(new_window_width) &&
|
||||
m_window_info.surface_height == static_cast<u32>(new_window_height))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_gl_context->ResizeSurface(static_cast<u32>(new_window_width), static_cast<u32>(new_window_height));
|
||||
m_window_info = m_gl_context->GetWindowInfo();
|
||||
|
@ -453,21 +468,6 @@ void OpenGLDevice::SetSwapInterval()
|
|||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, current_fbo);
|
||||
}
|
||||
|
||||
bool OpenGLDevice::SupportsFullscreen() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool OpenGLDevice::IsFullscreen()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool OpenGLDevice::SetFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
GPUDevice::AdapterAndModeList OpenGLDevice::GetAdapterAndModeList()
|
||||
{
|
||||
AdapterAndModeList aml;
|
||||
|
|
|
@ -41,21 +41,13 @@ public:
|
|||
RenderAPI GetRenderAPI() const override;
|
||||
|
||||
bool HasSurface() const override;
|
||||
|
||||
bool CreateDevice(const WindowInfo& wi, bool vsync) override;
|
||||
bool SetupDevice() override;
|
||||
|
||||
bool MakeCurrent() override;
|
||||
bool DoneCurrent() override;
|
||||
|
||||
bool ChangeWindow(const WindowInfo& new_wi) override;
|
||||
void ResizeWindow(s32 new_window_width, s32 new_window_height) override;
|
||||
bool SupportsFullscreen() const override;
|
||||
bool IsFullscreen() override;
|
||||
bool SetFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate) override;
|
||||
AdapterAndModeList GetAdapterAndModeList() override;
|
||||
void DestroySurface() override;
|
||||
|
||||
bool UpdateWindow() override;
|
||||
void ResizeWindow(s32 new_window_width, s32 new_window_height, float new_window_scale) override;
|
||||
|
||||
AdapterAndModeList GetAdapterAndModeList() override;
|
||||
|
||||
std::string GetShaderCacheBaseName(const std::string_view& type, bool debug) const override;
|
||||
|
||||
std::unique_ptr<GPUTexture> CreateTexture(u32 width, u32 height, u32 layers, u32 levels, u32 samples,
|
||||
|
@ -142,6 +134,9 @@ protected:
|
|||
OpenGLPipeline::VertexArrayCache m_vao_cache;
|
||||
OpenGLPipeline::ProgramCache m_program_cache;
|
||||
|
||||
bool CreateDevice(const std::string_view& adapter, bool debug_device) override;
|
||||
void DestroyDevice() override;
|
||||
|
||||
bool CreateBuffers();
|
||||
void DestroyBuffers();
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ VulkanGPUDevice::~VulkanGPUDevice()
|
|||
g_vulkan_context->WaitForGPUIdle();
|
||||
|
||||
DestroyStagingBuffer();
|
||||
DestroyResources();
|
||||
//DestroyResources();
|
||||
|
||||
Vulkan::ShaderCache::Destroy();
|
||||
m_swap_chain.reset();
|
||||
|
@ -46,6 +46,8 @@ RenderAPI VulkanGPUDevice::GetRenderAPI() const
|
|||
return RenderAPI::Vulkan;
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
||||
bool VulkanGPUDevice::ChangeWindow(const WindowInfo& new_wi)
|
||||
{
|
||||
g_vulkan_context->WaitForGPUIdle();
|
||||
|
@ -118,6 +120,8 @@ bool VulkanGPUDevice::SetFullscreen(bool fullscreen, u32 width, u32 height, floa
|
|||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
GPUDevice::AdapterAndModeList VulkanGPUDevice::GetAdapterAndModeList()
|
||||
{
|
||||
return StaticGetAdapterAndModeList(m_window_info.type != WindowInfo::Type::Surfaceless ? &m_window_info : nullptr);
|
||||
|
@ -211,6 +215,8 @@ void VulkanGPUDevice::SetVSync(bool enabled)
|
|||
m_vsync_enabled = m_swap_chain->IsVSyncEnabled();
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
||||
bool VulkanGPUDevice::CreateDevice(const WindowInfo& wi, bool vsync)
|
||||
{
|
||||
WindowInfo local_wi(wi);
|
||||
|
@ -251,6 +257,8 @@ bool VulkanGPUDevice::SetupDevice()
|
|||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
bool VulkanGPUDevice::HasSurface() const
|
||||
{
|
||||
return static_cast<bool>(m_swap_chain);
|
||||
|
@ -385,6 +393,8 @@ bool VulkanGPUDevice::CheckStagingBufferSize(u32 required_size)
|
|||
return true;
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
||||
bool VulkanGPUDevice::CreateResources()
|
||||
{
|
||||
static constexpr char fullscreen_quad_vertex_shader[] = R"(
|
||||
|
@ -556,7 +566,6 @@ void VulkanGPUDevice::DestroyResources()
|
|||
Vulkan::Util::SafeDestroySampler(m_linear_sampler);
|
||||
}
|
||||
|
||||
#if 0
|
||||
bool VulkanGPUDevice::CreateImGuiContext()
|
||||
{
|
||||
const VkRenderPass render_pass =
|
||||
|
@ -581,7 +590,6 @@ bool VulkanGPUDevice::UpdateImGuiFontTexture()
|
|||
g_vulkan_context->ExecuteCommandBuffer(true);
|
||||
return ImGui_ImplVulkan_CreateFontsTexture();
|
||||
}
|
||||
#endif
|
||||
|
||||
bool VulkanGPUDevice::MakeCurrent()
|
||||
{
|
||||
|
@ -593,7 +601,6 @@ bool VulkanGPUDevice::DoneCurrent()
|
|||
return true;
|
||||
}
|
||||
|
||||
#if 0
|
||||
bool VulkanGPUDevice::Render(bool skip_present)
|
||||
{
|
||||
if (skip_present || !m_swap_chain)
|
||||
|
@ -686,6 +693,7 @@ void VulkanGPUDevice::BeginSwapChainRenderPass(VkFramebuffer framebuffer, u32 wi
|
|||
vkCmdBeginRenderPass(g_vulkan_context->GetCurrentCommandBuffer(), &rp, VK_SUBPASS_CONTENTS_INLINE);
|
||||
}
|
||||
|
||||
#if 0
|
||||
void VulkanGPUDevice::RenderDisplay()
|
||||
{
|
||||
const Vulkan::Util::DebugScope debugScope(g_vulkan_context->GetCurrentCommandBuffer(),
|
||||
|
@ -797,6 +805,7 @@ void VulkanGPUDevice::RenderSoftwareCursor(s32 left, s32 top, s32 width, s32 hei
|
|||
Vulkan::Util::SetViewportAndClampScissor(cmdbuffer, left, top, width, height);
|
||||
vkCmdDraw(cmdbuffer, 3, 1, 0, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool VulkanGPUDevice::SetGPUTimingEnabled(bool enabled)
|
||||
{
|
||||
|
|
|
@ -27,17 +27,17 @@ public:
|
|||
|
||||
bool HasSurface() const override;
|
||||
|
||||
bool CreateDevice(const WindowInfo& wi, bool vsync) override;
|
||||
bool SetupDevice() override;
|
||||
//bool CreateDevice(const WindowInfo& wi, bool vsync) override;
|
||||
//bool SetupDevice() override;
|
||||
|
||||
bool MakeCurrent() override;
|
||||
bool DoneCurrent() override;
|
||||
//bool MakeCurrent() override;
|
||||
//bool DoneCurrent() override;
|
||||
|
||||
bool ChangeWindow(const WindowInfo& new_wi) override;
|
||||
void ResizeWindow(s32 new_window_width, s32 new_window_height) override;
|
||||
bool SupportsFullscreen() const override;
|
||||
bool IsFullscreen() override;
|
||||
bool SetFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate) override;
|
||||
//bool ChangeWindow(const WindowInfo& new_wi) override;
|
||||
//void ResizeWindow(s32 new_window_width, s32 new_window_height) override;
|
||||
//bool SupportsFullscreen() const override;
|
||||
//bool IsFullscreen() override;
|
||||
//bool SetFullscreen(bool fullscreen, u32 width, u32 height, float refresh_rate) override;
|
||||
AdapterAndModeList GetAdapterAndModeList() override;
|
||||
void DestroySurface() override;
|
||||
|
||||
|
@ -73,10 +73,11 @@ protected:
|
|||
bool CheckStagingBufferSize(u32 required_size);
|
||||
void DestroyStagingBuffer();
|
||||
|
||||
bool CreateResources() override;
|
||||
void DestroyResources() override;
|
||||
//bool CreateResources() override;
|
||||
//void DestroyResources() override;
|
||||
|
||||
void BeginSwapChainRenderPass(VkFramebuffer framebuffer, u32 width, u32 height);
|
||||
#if 0
|
||||
void RenderDisplay();
|
||||
void RenderImGui();
|
||||
void RenderSoftwareCursor();
|
||||
|
@ -84,6 +85,7 @@ protected:
|
|||
void RenderDisplay(s32 left, s32 top, s32 width, s32 height, Vulkan::Texture* texture, s32 texture_view_x,
|
||||
s32 texture_view_y, s32 texture_view_width, s32 texture_view_height, bool linear_filter);
|
||||
void RenderSoftwareCursor(s32 left, s32 top, s32 width, s32 height, GPUTexture* texture_handle);
|
||||
#endif
|
||||
|
||||
std::unique_ptr<Vulkan::SwapChain> m_swap_chain;
|
||||
|
||||
|
|
|
@ -2633,71 +2633,8 @@ void GPU_HW::ShaderCompileProgressTracker::Increment()
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: Combine all these..
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
std::unique_ptr<GPU> GPU::CreateHardwareD3D11Renderer()
|
||||
std::unique_ptr<GPU> GPU::CreateHardwareRenderer()
|
||||
{
|
||||
if (!Host::AcquireHostDisplay(RenderAPI::D3D11))
|
||||
{
|
||||
Log_ErrorPrintf("Host render API is incompatible");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<GPU_HW> gpu(std::make_unique<GPU_HW>());
|
||||
if (!gpu->Initialize())
|
||||
return nullptr;
|
||||
|
||||
return gpu;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
|
||||
std::unique_ptr<GPU> GPU::CreateHardwareMetalRenderer()
|
||||
{
|
||||
if (!Host::AcquireHostDisplay(RenderAPI::Metal))
|
||||
{
|
||||
Log_ErrorPrintf("Host render API is incompatible");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<GPU_HW> gpu(std::make_unique<GPU_HW>());
|
||||
if (!gpu->Initialize())
|
||||
return nullptr;
|
||||
|
||||
return gpu;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
std::unique_ptr<GPU> GPU::CreateHardwareOpenGLRenderer()
|
||||
{
|
||||
// Don't re-request GL when we already have GLES here...
|
||||
const RenderAPI current_api = g_host_display ? g_host_display->GetRenderAPI() : RenderAPI::None;
|
||||
if (current_api != RenderAPI::OpenGL && current_api != RenderAPI::OpenGLES &&
|
||||
!Host::AcquireHostDisplay(RenderAPI::OpenGL))
|
||||
{
|
||||
Log_ErrorPrintf("Host render API type is incompatible");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#if 0
|
||||
const bool opengl_is_available = ((g_host_display->GetRenderAPI() == RenderAPI::OpenGL &&
|
||||
(GLAD_GL_VERSION_3_0 || GLAD_GL_ARB_uniform_buffer_object)) ||
|
||||
(g_host_display->GetRenderAPI() == RenderAPI::OpenGLES && GLAD_GL_ES_VERSION_3_1));
|
||||
if (!opengl_is_available)
|
||||
{
|
||||
Host::AddOSDMessage(Host::TranslateStdString("OSDMessage",
|
||||
"OpenGL renderer unavailable, your driver or hardware is not "
|
||||
"recent enough. OpenGL 3.1 or OpenGL ES 3.1 is required."),
|
||||
20.0f);
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
std::unique_ptr<GPU_HW> gpu(std::make_unique<GPU_HW>());
|
||||
if (!gpu->Initialize())
|
||||
return nullptr;
|
||||
|
|
|
@ -1149,7 +1149,6 @@ void GPU_HW_D3D12::ClearDepthBuffer()
|
|||
cmdlist->ClearDepthStencilView(m_vram_depth_texture.GetRTVOrDSVDescriptor(), D3D12_CLEAR_FLAG_DEPTH,
|
||||
m_pgxp_depth_buffer ? 1.0f : 0.0f, 0, 0, nullptr);
|
||||
}
|
||||
#endif
|
||||
|
||||
std::unique_ptr<GPU> GPU::CreateHardwareD3D12Renderer()
|
||||
{
|
||||
|
@ -1165,3 +1164,4 @@ std::unique_ptr<GPU> GPU::CreateHardwareD3D12Renderer()
|
|||
|
||||
return gpu;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -1858,19 +1858,3 @@ void GPU_HW_Vulkan::DownsampleFramebufferAdaptive(Vulkan::Texture& source, u32 l
|
|||
|
||||
g_host_display->SetDisplayTexture(&m_display_texture, left, top, width, height);
|
||||
}
|
||||
|
||||
std::unique_ptr<GPU> GPU::CreateHardwareVulkanRenderer()
|
||||
{
|
||||
if (!Host::AcquireHostDisplay(RenderAPI::Vulkan))
|
||||
{
|
||||
Log_ErrorPrintf("Host render API is incompatible");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Assert(g_vulkan_shader_cache);
|
||||
std::unique_ptr<GPU_HW_Vulkan> gpu(std::make_unique<GPU_HW_Vulkan>());
|
||||
if (!gpu->Initialize())
|
||||
return nullptr;
|
||||
|
||||
return gpu;
|
||||
}
|
||||
|
|
|
@ -895,10 +895,6 @@ void GPU_SW::CopyVRAM(u32 src_x, u32 src_y, u32 dst_x, u32 dst_y, u32 width, u32
|
|||
|
||||
std::unique_ptr<GPU> GPU::CreateSoftwareRenderer()
|
||||
{
|
||||
// we need something to draw in.. but keep the current api if we have one
|
||||
if (!g_host_display && !Host::AcquireHostDisplay(GPUDevice::GetPreferredAPI()))
|
||||
return nullptr;
|
||||
|
||||
std::unique_ptr<GPU_SW> gpu(std::make_unique<GPU_SW>());
|
||||
if (!gpu->Initialize())
|
||||
return nullptr;
|
||||
|
|
|
@ -942,6 +942,9 @@ RenderAPI Settings::GetRenderAPIForRenderer(GPURenderer renderer)
|
|||
case GPURenderer::HardwareD3D12:
|
||||
return RenderAPI::D3D12;
|
||||
#endif
|
||||
#ifdef __APPLE__
|
||||
return RenderAPI::Metal;
|
||||
#endif
|
||||
#ifdef WITH_VULKAN
|
||||
case GPURenderer::HardwareVulkan:
|
||||
return RenderAPI::Vulkan;
|
||||
|
|
|
@ -71,7 +71,9 @@ SystemBootParameters::SystemBootParameters(const SystemBootParameters&) = defaul
|
|||
|
||||
SystemBootParameters::SystemBootParameters(SystemBootParameters&& other) = default;
|
||||
|
||||
SystemBootParameters::SystemBootParameters(std::string filename_) : filename(std::move(filename_)) {}
|
||||
SystemBootParameters::SystemBootParameters(std::string filename_) : filename(std::move(filename_))
|
||||
{
|
||||
}
|
||||
|
||||
SystemBootParameters::~SystemBootParameters() = default;
|
||||
|
||||
|
@ -135,6 +137,7 @@ static std::string s_input_profile_name;
|
|||
|
||||
static System::State s_state = System::State::Shutdown;
|
||||
static std::atomic_bool s_startup_cancelled{false};
|
||||
static bool s_keep_gpu_device_on_shutdown = false;
|
||||
|
||||
static ConsoleRegion s_region = ConsoleRegion::NTSC_U;
|
||||
TickCount System::g_ticks_per_second = System::MASTER_CLOCK;
|
||||
|
@ -802,7 +805,7 @@ bool System::RecreateGPU(GPURenderer renderer, bool force_recreate_display, bool
|
|||
// create new renderer
|
||||
g_gpu.reset();
|
||||
if (force_recreate_display)
|
||||
Host::ReleaseHostDisplay();
|
||||
Host::ReleaseGPUDevice();
|
||||
|
||||
if (!CreateGPU(renderer))
|
||||
{
|
||||
|
@ -1131,6 +1134,7 @@ bool System::BootSystem(SystemBootParameters parameters)
|
|||
Assert(s_state == State::Shutdown);
|
||||
s_state = State::Starting;
|
||||
s_startup_cancelled.store(false);
|
||||
s_keep_gpu_device_on_shutdown = static_cast<bool>(g_host_display);
|
||||
s_region = g_settings.region;
|
||||
Host::OnSystemStarting();
|
||||
|
||||
|
@ -1421,7 +1425,11 @@ bool System::Initialize(bool force_software_renderer)
|
|||
if (IsStartupCancelled())
|
||||
{
|
||||
g_gpu.reset();
|
||||
Host::ReleaseHostDisplay();
|
||||
if (!s_keep_gpu_device_on_shutdown)
|
||||
{
|
||||
Host::ReleaseGPUDevice();
|
||||
Host::ReleaseRenderWindow();
|
||||
}
|
||||
if (g_settings.gpu_pgxp_enable)
|
||||
PGXP::Shutdown();
|
||||
CPU::Shutdown();
|
||||
|
@ -1515,11 +1523,15 @@ void System::DestroySystem()
|
|||
ClearRunningGame();
|
||||
|
||||
// Restore present-all-frames behavior.
|
||||
if (g_host_display)
|
||||
if (s_keep_gpu_device_on_shutdown && g_host_display)
|
||||
{
|
||||
g_host_display->SetDisplayMaxFPS(0.0f);
|
||||
UpdateSoftwareCursor();
|
||||
Host::ReleaseHostDisplay();
|
||||
}
|
||||
else
|
||||
{
|
||||
Host::ReleaseGPUDevice();
|
||||
Host::ReleaseRenderWindow();
|
||||
}
|
||||
|
||||
s_bios_hash = {};
|
||||
|
@ -1621,41 +1633,26 @@ void System::RecreateSystem()
|
|||
|
||||
bool System::CreateGPU(GPURenderer renderer)
|
||||
{
|
||||
switch (renderer)
|
||||
const RenderAPI api = Settings::GetRenderAPIForRenderer(renderer);
|
||||
|
||||
if (!g_host_display || g_host_display->GetRenderAPI() != api)
|
||||
{
|
||||
#ifdef WITH_OPENGL
|
||||
case GPURenderer::HardwareOpenGL:
|
||||
g_gpu = GPU::CreateHardwareOpenGLRenderer();
|
||||
break;
|
||||
#endif
|
||||
if (g_host_display)
|
||||
{
|
||||
Log_WarningPrintf("Recreating GPU device, expecting %s got %s", GPUDevice::RenderAPIToString(api),
|
||||
GPUDevice::RenderAPIToString(g_host_display->GetRenderAPI()));
|
||||
}
|
||||
|
||||
#ifdef WITH_VULKAN
|
||||
case GPURenderer::HardwareVulkan:
|
||||
g_gpu = GPU::CreateHardwareVulkanRenderer();
|
||||
break;
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
case GPURenderer::HardwareD3D11:
|
||||
g_gpu = GPU::CreateHardwareD3D11Renderer();
|
||||
break;
|
||||
case GPURenderer::HardwareD3D12:
|
||||
g_gpu = GPU::CreateHardwareD3D12Renderer();
|
||||
break;
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
case GPURenderer::HardwareMetal:
|
||||
g_gpu = GPU::CreateHardwareMetalRenderer();
|
||||
break;
|
||||
#endif
|
||||
|
||||
case GPURenderer::Software:
|
||||
default:
|
||||
g_gpu = GPU::CreateSoftwareRenderer();
|
||||
break;
|
||||
Host::ReleaseGPUDevice();
|
||||
if (!Host::CreateGPUDevice(api))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (renderer == GPURenderer::Software)
|
||||
g_gpu = GPU::CreateSoftwareRenderer();
|
||||
else
|
||||
g_gpu = GPU::CreateHardwareRenderer();
|
||||
|
||||
if (!g_gpu)
|
||||
{
|
||||
Log_ErrorPrintf("Failed to initialize %s renderer, falling back to software renderer",
|
||||
|
@ -1669,6 +1666,11 @@ bool System::CreateGPU(GPURenderer renderer)
|
|||
if (!g_gpu)
|
||||
{
|
||||
Log_ErrorPrintf("Failed to create fallback software renderer.");
|
||||
if (!s_keep_gpu_device_on_shutdown)
|
||||
{
|
||||
Host::ReleaseGPUDevice();
|
||||
Host::ReleaseRenderWindow();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -498,6 +498,18 @@ void RequestExit(bool allow_confirm);
|
|||
/// Requests shut down of the current virtual machine.
|
||||
void RequestSystemShutdown(bool allow_confirm, bool save_state);
|
||||
|
||||
/// Attempts to create the rendering device backend.
|
||||
bool CreateGPUDevice(RenderAPI api);
|
||||
|
||||
/// Handles fullscreen transitions and such.
|
||||
void UpdateDisplayWindow();
|
||||
|
||||
/// Called when the window is resized.
|
||||
void ResizeDisplayWindow(s32 width, s32 height, float scale);
|
||||
|
||||
/// Destroys any active rendering device.
|
||||
void ReleaseGPUDevice();
|
||||
|
||||
/// Returns true if the hosting application is currently fullscreen.
|
||||
bool IsFullscreen();
|
||||
|
||||
|
|
|
@ -82,8 +82,6 @@ static void UpdateWindowTitle(const std::string& game_title);
|
|||
static void CancelAsyncOp();
|
||||
static void StartAsyncOp(std::function<void(ProgressCallback*)> callback);
|
||||
static void AsyncOpThreadEntryPoint(std::function<void(ProgressCallback*)> callback);
|
||||
static bool AcquireHostDisplay(RenderAPI api);
|
||||
static void ReleaseHostDisplay();
|
||||
} // namespace NoGUIHost
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
@ -96,7 +94,7 @@ static bool s_save_state_on_shutdown = false;
|
|||
static bool s_was_paused_by_focus_loss = false;
|
||||
|
||||
static Threading::Thread s_cpu_thread;
|
||||
static Threading::KernelSemaphore s_host_display_created_or_destroyed;
|
||||
static Threading::KernelSemaphore s_platform_window_updated;
|
||||
static std::atomic_bool s_running{false};
|
||||
static std::mutex s_cpu_thread_events_mutex;
|
||||
static std::condition_variable s_cpu_thread_event_done;
|
||||
|
@ -415,8 +413,7 @@ void NoGUIHost::StartSystem(SystemBootParameters params)
|
|||
void NoGUIHost::ProcessPlatformWindowResize(s32 width, s32 height, float scale)
|
||||
{
|
||||
Host::RunOnCPUThread([width, height, scale]() {
|
||||
// TODO: Scale
|
||||
g_host_display->ResizeWindow(width, height);
|
||||
g_host_display->ResizeWindow(width, height, scale);
|
||||
ImGuiManager::WindowResized();
|
||||
System::HostDisplayResized();
|
||||
});
|
||||
|
@ -608,8 +605,8 @@ void NoGUIHost::CPUThreadEntryPoint()
|
|||
// input source setup must happen on emu thread
|
||||
CommonHost::Initialize();
|
||||
|
||||
// start the GS thread up and get it going
|
||||
if (AcquireHostDisplay(Settings::GetRenderAPIForRenderer(g_settings.gpu_renderer)))
|
||||
// start the fullscreen UI and get it going
|
||||
if (Host::CreateGPUDevice(Settings::GetRenderAPIForRenderer(g_settings.gpu_renderer)) && FullscreenUI::Initialize())
|
||||
{
|
||||
// kick a game list refresh if we're not in batch mode
|
||||
if (!InBatchMode())
|
||||
|
@ -629,7 +626,8 @@ void NoGUIHost::CPUThreadEntryPoint()
|
|||
|
||||
if (System::IsValid())
|
||||
System::ShutdownSystem(false);
|
||||
ReleaseHostDisplay();
|
||||
Host::ReleaseGPUDevice();
|
||||
Host::ReleaseRenderWindow();
|
||||
|
||||
CommonHost::Shutdown();
|
||||
g_nogui_window->QuitMessageLoop();
|
||||
|
@ -652,53 +650,30 @@ void NoGUIHost::CPUThreadMainLoop()
|
|||
}
|
||||
}
|
||||
|
||||
bool NoGUIHost::AcquireHostDisplay(RenderAPI api)
|
||||
std::optional<WindowInfo> Host::AcquireRenderWindow(bool recreate_window)
|
||||
{
|
||||
Assert(!g_host_display);
|
||||
std::optional<WindowInfo> wi;
|
||||
|
||||
g_nogui_window->ExecuteInMessageLoop([api]() {
|
||||
if (g_nogui_window->CreatePlatformWindow(GetWindowTitle(System::GetGameTitle())))
|
||||
g_nogui_window->ExecuteInMessageLoop([&wi, recreate_window]() {
|
||||
bool res = g_nogui_window->HasPlatformWindow();
|
||||
if (!res || recreate_window)
|
||||
{
|
||||
const std::optional<WindowInfo> wi(g_nogui_window->GetPlatformWindowInfo());
|
||||
if (wi.has_value())
|
||||
{
|
||||
g_host_display = Host::CreateDisplayForAPI(api);
|
||||
if (g_host_display && !g_host_display->CreateDevice(wi.value(), System::ShouldUseVSync()))
|
||||
g_host_display.reset();
|
||||
}
|
||||
|
||||
if (g_host_display)
|
||||
g_host_display->DoneCurrent();
|
||||
else
|
||||
if (res)
|
||||
g_nogui_window->DestroyPlatformWindow();
|
||||
}
|
||||
|
||||
s_host_display_created_or_destroyed.Post();
|
||||
res = g_nogui_window->CreatePlatformWindow(NoGUIHost::GetWindowTitle(System::GetGameTitle()));
|
||||
}
|
||||
if (res)
|
||||
wi = g_nogui_window->GetPlatformWindowInfo();
|
||||
s_platform_window_updated.Post();
|
||||
});
|
||||
|
||||
s_host_display_created_or_destroyed.Wait();
|
||||
s_platform_window_updated.Wait();
|
||||
|
||||
if (!g_host_display)
|
||||
if (!wi.has_value())
|
||||
{
|
||||
g_nogui_window->ReportError("Error", "Failed to create host display.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!g_host_display->MakeCurrent() || !g_host_display->SetupDevice() || !ImGuiManager::Initialize() ||
|
||||
!CommonHost::CreateHostDisplayResources())
|
||||
{
|
||||
ImGuiManager::Shutdown();
|
||||
CommonHost::ReleaseHostDisplayResources();
|
||||
g_host_display.reset();
|
||||
g_nogui_window->DestroyPlatformWindow();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!FullscreenUI::Initialize())
|
||||
{
|
||||
g_nogui_window->ReportError("Error", "Failed to initialize fullscreen UI");
|
||||
ReleaseHostDisplay();
|
||||
return false;
|
||||
g_nogui_window->ReportError("Error", "Failed to create render window.");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// reload input sources, since it might use the window handle
|
||||
|
@ -706,43 +681,18 @@ bool NoGUIHost::AcquireHostDisplay(RenderAPI api)
|
|||
auto lock = Host::GetSettingsLock();
|
||||
InputManager::ReloadSources(*Host::GetSettingsInterface(), lock);
|
||||
}
|
||||
return true;
|
||||
|
||||
return wi;
|
||||
}
|
||||
|
||||
bool Host::AcquireHostDisplay(RenderAPI api)
|
||||
void Host::ReleaseRenderWindow()
|
||||
{
|
||||
if (g_host_display && g_host_display->GetRenderAPI() == api)
|
||||
{
|
||||
// current is fine
|
||||
return true;
|
||||
}
|
||||
|
||||
// otherwise we need to switch
|
||||
NoGUIHost::ReleaseHostDisplay();
|
||||
return NoGUIHost::AcquireHostDisplay(api);
|
||||
}
|
||||
|
||||
void NoGUIHost::ReleaseHostDisplay()
|
||||
{
|
||||
if (!g_host_display)
|
||||
return;
|
||||
|
||||
// close input sources, since it might use the window handle
|
||||
InputManager::CloseSources();
|
||||
|
||||
CommonHost::ReleaseHostDisplayResources();
|
||||
ImGuiManager::Shutdown();
|
||||
g_host_display.reset();
|
||||
// Need to block here, otherwise the recreation message associates with the old window.
|
||||
g_nogui_window->ExecuteInMessageLoop([]() {
|
||||
g_nogui_window->DestroyPlatformWindow();
|
||||
s_host_display_created_or_destroyed.Post();
|
||||
s_platform_window_updated.Post();
|
||||
});
|
||||
s_host_display_created_or_destroyed.Wait();
|
||||
}
|
||||
|
||||
void Host::ReleaseHostDisplay()
|
||||
{
|
||||
// we keep the fsui going, so no need to do anything here
|
||||
s_platform_window_updated.Wait();
|
||||
}
|
||||
|
||||
void Host::OnSystemStarting()
|
||||
|
|
|
@ -24,6 +24,7 @@ public:
|
|||
virtual void SetDefaultConfig(SettingsInterface& si) = 0;
|
||||
|
||||
virtual bool CreatePlatformWindow(std::string title) = 0;
|
||||
virtual bool HasPlatformWindow() const = 0;
|
||||
virtual void DestroyPlatformWindow() = 0;
|
||||
|
||||
virtual std::optional<WindowInfo> GetPlatformWindowInfo() = 0;
|
||||
|
|
|
@ -150,6 +150,11 @@ bool WaylandNoGUIPlatform::CreatePlatformWindow(std::string title)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool WaylandNoGUIPlatform::HasPlatformWindow() const
|
||||
{
|
||||
return (m_surface != nullptr);
|
||||
}
|
||||
|
||||
void WaylandNoGUIPlatform::DestroyPlatformWindow()
|
||||
{
|
||||
m_window_info = {};
|
||||
|
|
|
@ -29,6 +29,7 @@ public:
|
|||
void SetDefaultConfig(SettingsInterface& si) override;
|
||||
|
||||
bool CreatePlatformWindow(std::string title) override;
|
||||
bool HasPlatformWindow() const override;
|
||||
void DestroyPlatformWindow() override;
|
||||
std::optional<WindowInfo> GetPlatformWindowInfo() override;
|
||||
void SetPlatformWindowTitle(std::string title) override;
|
||||
|
|
|
@ -134,6 +134,11 @@ bool Win32NoGUIPlatform::CreatePlatformWindow(std::string title)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool Win32NoGUIPlatform::HasPlatformWindow() const
|
||||
{
|
||||
return (m_hwnd != NULL);
|
||||
}
|
||||
|
||||
void Win32NoGUIPlatform::DestroyPlatformWindow()
|
||||
{
|
||||
if (!m_hwnd)
|
||||
|
|
|
@ -23,6 +23,7 @@ public:
|
|||
void SetDefaultConfig(SettingsInterface& si) override;
|
||||
|
||||
bool CreatePlatformWindow(std::string title) override;
|
||||
bool HasPlatformWindow() const override;
|
||||
void DestroyPlatformWindow() override;
|
||||
std::optional<WindowInfo> GetPlatformWindowInfo() override;
|
||||
void SetPlatformWindowTitle(std::string title) override;
|
||||
|
|
|
@ -99,6 +99,11 @@ bool X11NoGUIPlatform::CreatePlatformWindow(std::string title)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool X11NoGUIPlatform::HasPlatformWindow() const
|
||||
{
|
||||
return m_window != 0;
|
||||
}
|
||||
|
||||
void X11NoGUIPlatform::DestroyPlatformWindow()
|
||||
{
|
||||
m_window_info = {};
|
||||
|
|
|
@ -48,6 +48,7 @@ public:
|
|||
void SetDefaultConfig(SettingsInterface& si) override;
|
||||
|
||||
bool CreatePlatformWindow(std::string title) override;
|
||||
bool HasPlatformWindow() const override;
|
||||
void DestroyPlatformWindow() override;
|
||||
std::optional<WindowInfo> GetPlatformWindowInfo() override;
|
||||
void SetPlatformWindowTitle(std::string title) override;
|
||||
|
|
|
@ -9,8 +9,8 @@
|
|||
#include "common/file_system.h"
|
||||
#include "common/log.h"
|
||||
#include "core/achievements.h"
|
||||
#include "core/host.h"
|
||||
#include "core/gpu/gpu_device.h"
|
||||
#include "core/host.h"
|
||||
#include "core/memory_card.h"
|
||||
#include "core/settings.h"
|
||||
#include "core/system.h"
|
||||
|
@ -217,77 +217,33 @@ bool MainWindow::nativeEvent(const QByteArray& eventType, void* message, qintptr
|
|||
|
||||
#endif
|
||||
|
||||
bool MainWindow::createDisplay(bool fullscreen, bool render_to_main)
|
||||
std::optional<WindowInfo> MainWindow::acquireRenderWindow(bool recreate_window, bool fullscreen, bool render_to_main,
|
||||
bool surfaceless, bool use_main_window_pos)
|
||||
{
|
||||
Log_DevPrintf("createDisplay(%u, %u)", static_cast<u32>(fullscreen), static_cast<u32>(render_to_main));
|
||||
|
||||
const std::string fullscreen_mode(Host::GetBaseStringSettingValue("GPU", "FullscreenMode", ""));
|
||||
const bool is_exclusive_fullscreen = (fullscreen && !fullscreen_mode.empty() && g_host_display->SupportsFullscreen());
|
||||
|
||||
createDisplayWidget(fullscreen, render_to_main, is_exclusive_fullscreen);
|
||||
|
||||
std::optional<WindowInfo> wi = m_display_widget->getWindowInfo();
|
||||
if (!wi.has_value())
|
||||
{
|
||||
QMessageBox::critical(this, tr("Error"), tr("Failed to get window info from widget"));
|
||||
destroyDisplayWidget(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
g_emu_thread->connectDisplaySignals(m_display_widget);
|
||||
|
||||
if (!g_host_display->CreateDevice(wi.value(), System::ShouldUseVSync()))
|
||||
{
|
||||
QMessageBox::critical(this, tr("Error"), tr("Failed to create host display device context."));
|
||||
destroyDisplayWidget(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_display_created = true;
|
||||
|
||||
if (is_exclusive_fullscreen)
|
||||
setDisplayFullscreen(fullscreen_mode);
|
||||
|
||||
updateWindowTitle();
|
||||
updateWindowState();
|
||||
|
||||
m_ui.actionStartFullscreenUI->setEnabled(false);
|
||||
m_ui.actionStartFullscreenUI2->setEnabled(false);
|
||||
|
||||
updateDisplayWidgetCursor();
|
||||
updateDisplayRelatedActions(true, render_to_main, fullscreen);
|
||||
|
||||
m_display_widget->setFocus();
|
||||
|
||||
g_host_display->DoneCurrent();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MainWindow::updateDisplay(bool fullscreen, bool render_to_main, bool surfaceless)
|
||||
{
|
||||
Log_DevPrintf("updateDisplay() fullscreen=%s render_to_main=%s surfaceless=%s", fullscreen ? "true" : "false",
|
||||
render_to_main ? "true" : "false", surfaceless ? "true" : "false");
|
||||
Log_DevPrintf(
|
||||
"acquireRenderWindow() recreate=%s fullscreen=%s render_to_main=%s surfaceless=%s use_main_window_pos=%s",
|
||||
recreate_window ? "true" : "false", fullscreen ? "true" : "false", render_to_main ? "true" : "false",
|
||||
surfaceless ? "true" : "false", use_main_window_pos ? "true" : "false");
|
||||
|
||||
QWidget* container =
|
||||
m_display_container ? static_cast<QWidget*>(m_display_container) : static_cast<QWidget*>(m_display_widget);
|
||||
const bool is_fullscreen = isRenderingFullscreen();
|
||||
const bool is_rendering_to_main = isRenderingToMain();
|
||||
const std::string fullscreen_mode(Host::GetBaseStringSettingValue("GPU", "FullscreenMode", ""));
|
||||
const bool is_exclusive_fullscreen = (fullscreen && !fullscreen_mode.empty() && g_host_display->SupportsFullscreen());
|
||||
const bool changing_surfaceless = (!m_display_widget != surfaceless);
|
||||
if (fullscreen == is_fullscreen && is_rendering_to_main == render_to_main && !changing_surfaceless)
|
||||
return true;
|
||||
if (m_display_created && !recreate_window && fullscreen == is_fullscreen && is_rendering_to_main == render_to_main &&
|
||||
!changing_surfaceless)
|
||||
{
|
||||
return m_display_widget ? m_display_widget->getWindowInfo() : WindowInfo();
|
||||
}
|
||||
|
||||
// Skip recreating the surface if we're just transitioning between fullscreen and windowed with render-to-main off.
|
||||
// .. except on Wayland, where everything tends to break if you don't recreate.
|
||||
const bool has_container = (m_display_container != nullptr);
|
||||
const bool needs_container = DisplayContainer::isNeeded(fullscreen, render_to_main);
|
||||
if (!is_rendering_to_main && !render_to_main && !is_exclusive_fullscreen && has_container == needs_container &&
|
||||
!needs_container && !changing_surfaceless)
|
||||
if (m_display_created && !recreate_window && !is_rendering_to_main && !render_to_main &&
|
||||
has_container == needs_container && !needs_container && !changing_surfaceless)
|
||||
{
|
||||
Log_DevPrintf("Toggling to %s without recreating surface", (fullscreen ? "fullscreen" : "windowed"));
|
||||
if (g_host_display->IsFullscreen())
|
||||
g_host_display->SetFullscreen(false, 0, 0, 0.0f);
|
||||
|
||||
// since we don't destroy the display widget, we need to save it here
|
||||
if (!is_fullscreen && !is_rendering_to_main)
|
||||
|
@ -305,53 +261,48 @@ bool MainWindow::updateDisplay(bool fullscreen, bool render_to_main, bool surfac
|
|||
|
||||
updateDisplayWidgetCursor();
|
||||
m_display_widget->setFocus();
|
||||
updateWindowState();
|
||||
|
||||
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
|
||||
return true;
|
||||
return m_display_widget->getWindowInfo();
|
||||
}
|
||||
|
||||
g_host_display->DestroySurface();
|
||||
|
||||
destroyDisplayWidget(surfaceless || fullscreen);
|
||||
destroyDisplayWidget(surfaceless);
|
||||
m_display_created = true;
|
||||
|
||||
// if we're going to surfaceless, we're done here
|
||||
if (surfaceless)
|
||||
{
|
||||
updateWindowState();
|
||||
updateDisplayRelatedActions(false, render_to_main, fullscreen);
|
||||
return true;
|
||||
}
|
||||
return WindowInfo();
|
||||
|
||||
createDisplayWidget(fullscreen, render_to_main, is_exclusive_fullscreen);
|
||||
createDisplayWidget(fullscreen, render_to_main, use_main_window_pos);
|
||||
|
||||
// we need the surface visible.. this might be able to be replaced with something else
|
||||
QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
|
||||
|
||||
std::optional<WindowInfo> wi = m_display_widget->getWindowInfo();
|
||||
if (!wi.has_value())
|
||||
{
|
||||
QMessageBox::critical(this, tr("Error"), tr("Failed to get new window info from widget"));
|
||||
QMessageBox::critical(this, tr("Error"), tr("Failed to get window info from widget"));
|
||||
destroyDisplayWidget(true);
|
||||
return false;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
g_emu_thread->connectDisplaySignals(m_display_widget);
|
||||
|
||||
if (!g_host_display->ChangeWindow(wi.value()))
|
||||
Panic("Failed to recreate surface on new widget.");
|
||||
|
||||
if (is_exclusive_fullscreen)
|
||||
setDisplayFullscreen(fullscreen_mode);
|
||||
|
||||
updateWindowTitle();
|
||||
updateWindowState();
|
||||
|
||||
m_ui.actionStartFullscreenUI->setEnabled(false);
|
||||
m_ui.actionStartFullscreenUI2->setEnabled(false);
|
||||
|
||||
updateDisplayWidgetCursor();
|
||||
updateDisplayRelatedActions(true, render_to_main, fullscreen);
|
||||
m_display_widget->setFocus();
|
||||
|
||||
QSignalBlocker blocker(m_ui.actionFullscreen);
|
||||
m_ui.actionFullscreen->setChecked(fullscreen);
|
||||
return true;
|
||||
return wi;
|
||||
}
|
||||
|
||||
void MainWindow::createDisplayWidget(bool fullscreen, bool render_to_main, bool is_exclusive_fullscreen)
|
||||
void MainWindow::createDisplayWidget(bool fullscreen, bool render_to_main, bool use_main_window_pos)
|
||||
{
|
||||
// If we're rendering to main and were hidden (e.g. coming back from fullscreen),
|
||||
// make sure we're visible before trying to add ourselves. Otherwise Wayland breaks.
|
||||
|
@ -387,20 +338,21 @@ void MainWindow::createDisplayWidget(bool fullscreen, bool render_to_main, bool
|
|||
// and positioning has no effect anyway.
|
||||
if (!s_use_central_widget)
|
||||
{
|
||||
if (isVisible())
|
||||
if (isVisible() && g_emu_thread->shouldRenderToMain())
|
||||
container->move(pos());
|
||||
else
|
||||
restoreDisplayWindowGeometryFromConfig();
|
||||
}
|
||||
|
||||
if (!is_exclusive_fullscreen)
|
||||
container->showFullScreen();
|
||||
else
|
||||
container->showNormal();
|
||||
container->showFullScreen();
|
||||
}
|
||||
else if (!render_to_main)
|
||||
{
|
||||
restoreDisplayWindowGeometryFromConfig();
|
||||
// See lameland comment above.
|
||||
if (use_main_window_pos && !s_use_central_widget)
|
||||
container->move(pos());
|
||||
else
|
||||
restoreDisplayWindowGeometryFromConfig();
|
||||
container->showNormal();
|
||||
}
|
||||
else if (s_use_central_widget)
|
||||
|
@ -419,31 +371,13 @@ void MainWindow::createDisplayWidget(bool fullscreen, bool render_to_main, bool
|
|||
m_ui.mainContainer->setCurrentIndex(1);
|
||||
}
|
||||
|
||||
updateDisplayRelatedActions(true, render_to_main, fullscreen);
|
||||
|
||||
// We need the surface visible.
|
||||
QGuiApplication::sync();
|
||||
}
|
||||
|
||||
void MainWindow::setDisplayFullscreen(const std::string& fullscreen_mode)
|
||||
{
|
||||
u32 width, height;
|
||||
float refresh_rate;
|
||||
bool result = false;
|
||||
|
||||
if (GPUDevice::ParseFullscreenMode(fullscreen_mode, &width, &height, &refresh_rate))
|
||||
{
|
||||
result = g_host_display->SetFullscreen(true, width, height, refresh_rate);
|
||||
if (result)
|
||||
{
|
||||
Host::AddOSDMessage(Host::TranslateStdString("OSDMessage", "Acquired exclusive fullscreen."), 10.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
Host::AddOSDMessage(Host::TranslateStdString("OSDMessage", "Failed to acquire exclusive fullscreen."), 10.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::displaySizeRequested(qint32 width, qint32 height)
|
||||
void MainWindow::displayResizeRequested(qint32 width, qint32 height)
|
||||
{
|
||||
if (!m_display_widget)
|
||||
return;
|
||||
|
@ -465,7 +399,7 @@ void MainWindow::displaySizeRequested(qint32 width, qint32 height)
|
|||
QtUtils::ResizePotentiallyFixedSizeWindow(this, width, height + extra_height);
|
||||
}
|
||||
|
||||
void MainWindow::destroyDisplay()
|
||||
void MainWindow::releaseRenderWindow()
|
||||
{
|
||||
// Now we can safely destroy the display window.
|
||||
destroyDisplayWidget(true);
|
||||
|
@ -473,6 +407,8 @@ void MainWindow::destroyDisplay()
|
|||
|
||||
updateDisplayRelatedActions(false, false, false);
|
||||
|
||||
m_ui.actionViewSystemDisplay->setEnabled(false);
|
||||
m_ui.actionFullscreen->setEnabled(false);
|
||||
m_ui.actionStartFullscreenUI->setEnabled(true);
|
||||
m_ui.actionStartFullscreenUI2->setEnabled(true);
|
||||
}
|
||||
|
@ -1849,7 +1785,7 @@ bool MainWindow::isRenderingFullscreen() const
|
|||
if (!g_host_display || !m_display_widget)
|
||||
return false;
|
||||
|
||||
return getDisplayContainer()->isFullScreen() || g_host_display->IsFullscreen();
|
||||
return getDisplayContainer()->isFullScreen();
|
||||
}
|
||||
|
||||
bool MainWindow::isRenderingToMain() const
|
||||
|
@ -2003,12 +1939,11 @@ void MainWindow::connectSignals()
|
|||
Qt::QueuedConnection);
|
||||
connect(g_emu_thread, &EmuThread::errorReported, this, &MainWindow::reportError, Qt::BlockingQueuedConnection);
|
||||
connect(g_emu_thread, &EmuThread::messageConfirmed, this, &MainWindow::confirmMessage, Qt::BlockingQueuedConnection);
|
||||
connect(g_emu_thread, &EmuThread::createDisplayRequested, this, &MainWindow::createDisplay,
|
||||
connect(g_emu_thread, &EmuThread::onAcquireRenderWindowRequested, this, &MainWindow::acquireRenderWindow,
|
||||
Qt::BlockingQueuedConnection);
|
||||
connect(g_emu_thread, &EmuThread::destroyDisplayRequested, this, &MainWindow::destroyDisplay);
|
||||
connect(g_emu_thread, &EmuThread::updateDisplayRequested, this, &MainWindow::updateDisplay,
|
||||
connect(g_emu_thread, &EmuThread::onReleaseRenderWindowRequested, this, &MainWindow::releaseRenderWindow);
|
||||
connect(g_emu_thread, &EmuThread::onResizeRenderWindowRequested, this, &MainWindow::displayResizeRequested,
|
||||
Qt::BlockingQueuedConnection);
|
||||
connect(g_emu_thread, &EmuThread::displaySizeRequested, this, &MainWindow::displaySizeRequested);
|
||||
connect(g_emu_thread, &EmuThread::focusDisplayWidgetRequested, this, &MainWindow::focusDisplayWidget);
|
||||
connect(g_emu_thread, &EmuThread::systemStarting, this, &MainWindow::onSystemStarting);
|
||||
connect(g_emu_thread, &EmuThread::systemStarted, this, &MainWindow::onSystemStarted);
|
||||
|
|
|
@ -10,8 +10,8 @@
|
|||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
#include "controllersettingsdialog.h"
|
||||
#include "common/window_info.h"
|
||||
#include "controllersettingsdialog.h"
|
||||
#include "core/types.h"
|
||||
#include "displaywidget.h"
|
||||
#include "settingsdialog.h"
|
||||
|
@ -110,10 +110,11 @@ public Q_SLOTS:
|
|||
private Q_SLOTS:
|
||||
void reportError(const QString& title, const QString& message);
|
||||
bool confirmMessage(const QString& title, const QString& message);
|
||||
bool createDisplay(bool fullscreen, bool render_to_main);
|
||||
bool updateDisplay(bool fullscreen, bool render_to_main, bool surfaceless);
|
||||
void displaySizeRequested(qint32 width, qint32 height);
|
||||
void destroyDisplay();
|
||||
|
||||
std::optional<WindowInfo> acquireRenderWindow(bool recreate_window, bool fullscreen, bool render_to_main,
|
||||
bool surfaceless, bool use_main_window_pos);
|
||||
void displayResizeRequested(qint32 width, qint32 height);
|
||||
void releaseRenderWindow();
|
||||
void focusDisplayWidget();
|
||||
void onMouseModeRequested(bool relative_mode, bool hide_cursor);
|
||||
|
||||
|
@ -208,11 +209,10 @@ private:
|
|||
void restoreGeometryFromConfig();
|
||||
void saveDisplayWindowGeometryToConfig();
|
||||
void restoreDisplayWindowGeometryFromConfig();
|
||||
void createDisplayWidget(bool fullscreen, bool render_to_main, bool is_exclusive_fullscreen);
|
||||
void createDisplayWidget(bool fullscreen, bool render_to_main, bool use_main_window_pos);
|
||||
void destroyDisplayWidget(bool show_game_list);
|
||||
void updateDisplayWidgetCursor();
|
||||
void updateDisplayRelatedActions(bool has_surface, bool render_to_main, bool fullscreen);
|
||||
void setDisplayFullscreen(const std::string& fullscreen_mode);
|
||||
|
||||
SettingsDialog* getSettingsDialog();
|
||||
void doSettings(const char* category = nullptr);
|
||||
|
|
|
@ -100,7 +100,9 @@ static bool s_start_fullscreen_ui_fullscreen = false;
|
|||
EmuThread* g_emu_thread;
|
||||
GDBServer* g_gdb_server;
|
||||
|
||||
EmuThread::EmuThread(QThread* ui_thread) : QThread(), m_ui_thread(ui_thread) {}
|
||||
EmuThread::EmuThread(QThread* ui_thread) : QThread(), m_ui_thread(ui_thread)
|
||||
{
|
||||
}
|
||||
|
||||
EmuThread::~EmuThread() = default;
|
||||
|
||||
|
@ -329,24 +331,31 @@ void EmuThread::setInitialState(std::optional<bool> override_fullscreen)
|
|||
m_is_surfaceless = false;
|
||||
}
|
||||
|
||||
void EmuThread::checkForSettingsChanges(const Settings& old_settings)
|
||||
{
|
||||
if (g_main_window)
|
||||
{
|
||||
QMetaObject::invokeMethod(g_main_window, &MainWindow::checkForSettingChanges, Qt::QueuedConnection);
|
||||
updatePerformanceCounters();
|
||||
}
|
||||
|
||||
if (g_host_display)
|
||||
{
|
||||
const bool render_to_main = shouldRenderToMain();
|
||||
if (m_is_rendering_to_main != render_to_main)
|
||||
{
|
||||
m_is_rendering_to_main = render_to_main;
|
||||
g_host_display->UpdateWindow();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Host::CheckForSettingsChanges(const Settings& old_settings)
|
||||
{
|
||||
CommonHost::CheckForSettingsChanges(old_settings);
|
||||
g_emu_thread->checkForSettingsChanges(old_settings);
|
||||
}
|
||||
|
||||
void EmuThread::checkForSettingsChanges(const Settings& old_settings)
|
||||
{
|
||||
const bool render_to_main = shouldRenderToMain();
|
||||
if (m_is_rendering_to_main != render_to_main)
|
||||
{
|
||||
m_is_rendering_to_main = render_to_main;
|
||||
updateDisplayState();
|
||||
}
|
||||
|
||||
QMetaObject::invokeMethod(g_main_window, &MainWindow::checkForSettingChanges, Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
void EmuThread::setDefaultSettings(bool system /* = true */, bool controller /* = true */)
|
||||
{
|
||||
if (isOnThread())
|
||||
|
@ -392,7 +401,7 @@ void Host::RequestResizeHostDisplay(s32 new_window_width, s32 new_window_height)
|
|||
if (g_emu_thread->isFullscreen())
|
||||
return;
|
||||
|
||||
emit g_emu_thread->displaySizeRequested(new_window_width, new_window_height);
|
||||
emit g_emu_thread->onResizeRenderWindowRequested(new_window_width, new_window_height);
|
||||
}
|
||||
|
||||
void EmuThread::applySettings(bool display_osd_messages /* = false */)
|
||||
|
@ -449,7 +458,7 @@ void EmuThread::startFullscreenUI()
|
|||
setInitialState(s_start_fullscreen_ui_fullscreen ? std::optional<bool>(true) : std::optional<bool>());
|
||||
m_run_fullscreen_ui = true;
|
||||
|
||||
if (!acquireHostDisplay(Settings::GetRenderAPIForRenderer(g_settings.gpu_renderer)))
|
||||
if (!Host::CreateGPUDevice(Settings::GetRenderAPIForRenderer(g_settings.gpu_renderer)))
|
||||
{
|
||||
m_run_fullscreen_ui = false;
|
||||
return;
|
||||
|
@ -481,7 +490,8 @@ void EmuThread::stopFullscreenUI()
|
|||
return;
|
||||
|
||||
m_run_fullscreen_ui = false;
|
||||
releaseHostDisplay();
|
||||
Host::ReleaseGPUDevice();
|
||||
Host::ReleaseRenderWindow();
|
||||
}
|
||||
|
||||
void EmuThread::bootSystem(std::shared_ptr<SystemBootParameters> params)
|
||||
|
@ -605,33 +615,9 @@ void EmuThread::onDisplayWindowMouseWheelEvent(const QPoint& delta_angle)
|
|||
InputManager::UpdatePointerRelativeDelta(0, InputPointerAxis::WheelY, dy);
|
||||
}
|
||||
|
||||
void EmuThread::onDisplayWindowResized(int width, int height)
|
||||
void EmuThread::onDisplayWindowResized(int width, int height, float scale)
|
||||
{
|
||||
// this can be null if it was destroyed and the main thread is late catching up
|
||||
if (!g_host_display)
|
||||
return;
|
||||
|
||||
Log_DevPrintf("Display window resized to %dx%d", width, height);
|
||||
g_host_display->ResizeWindow(width, height);
|
||||
ImGuiManager::WindowResized();
|
||||
System::HostDisplayResized();
|
||||
|
||||
// re-render the display, since otherwise it will be out of date and stretched if paused
|
||||
if (System::IsValid())
|
||||
{
|
||||
if (m_is_exclusive_fullscreen && !g_host_display->IsFullscreen())
|
||||
{
|
||||
// we lost exclusive fullscreen, switch to borderless
|
||||
Host::AddOSDMessage(Host::TranslateStdString("OSDMessage", "Lost exclusive fullscreen."), 10.0f);
|
||||
m_is_exclusive_fullscreen = false;
|
||||
m_is_fullscreen = false;
|
||||
m_lost_exclusive_fullscreen = true;
|
||||
}
|
||||
|
||||
// force redraw if we're paused
|
||||
if (!System::IsRunning() && !FullscreenUI::HasActiveWindow())
|
||||
renderDisplay(false);
|
||||
}
|
||||
Host::ResizeDisplayWindow(width, height, scale);
|
||||
}
|
||||
|
||||
void EmuThread::redrawDisplayWindow()
|
||||
|
@ -656,14 +642,15 @@ void EmuThread::toggleFullscreen()
|
|||
return;
|
||||
}
|
||||
|
||||
setFullscreen(!m_is_fullscreen);
|
||||
setFullscreen(!m_is_fullscreen, true);
|
||||
}
|
||||
|
||||
void EmuThread::setFullscreen(bool fullscreen)
|
||||
void EmuThread::setFullscreen(bool fullscreen, bool allow_render_to_main)
|
||||
{
|
||||
if (!isOnThread())
|
||||
{
|
||||
QMetaObject::invokeMethod(this, "setFullscreen", Qt::QueuedConnection, Q_ARG(bool, fullscreen));
|
||||
QMetaObject::invokeMethod(this, "setFullscreen", Qt::QueuedConnection, Q_ARG(bool, fullscreen),
|
||||
Q_ARG(bool, allow_render_to_main));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -671,7 +658,8 @@ void EmuThread::setFullscreen(bool fullscreen)
|
|||
return;
|
||||
|
||||
m_is_fullscreen = fullscreen;
|
||||
updateDisplayState();
|
||||
m_is_rendering_to_main = allow_render_to_main && shouldRenderToMain();
|
||||
Host::UpdateDisplayWindow();
|
||||
}
|
||||
|
||||
bool Host::IsFullscreen()
|
||||
|
@ -681,7 +669,7 @@ bool Host::IsFullscreen()
|
|||
|
||||
void Host::SetFullscreen(bool enabled)
|
||||
{
|
||||
g_emu_thread->setFullscreen(enabled);
|
||||
g_emu_thread->setFullscreen(enabled, true);
|
||||
}
|
||||
|
||||
void EmuThread::setSurfaceless(bool surfaceless)
|
||||
|
@ -696,7 +684,7 @@ void EmuThread::setSurfaceless(bool surfaceless)
|
|||
return;
|
||||
|
||||
m_is_surfaceless = surfaceless;
|
||||
updateDisplayState();
|
||||
Host::UpdateDisplayWindow();
|
||||
}
|
||||
|
||||
void EmuThread::requestDisplaySize(float scale)
|
||||
|
@ -713,52 +701,25 @@ void EmuThread::requestDisplaySize(float scale)
|
|||
System::RequestDisplaySize(scale);
|
||||
}
|
||||
|
||||
bool EmuThread::acquireHostDisplay(RenderAPI api)
|
||||
std::optional<WindowInfo> EmuThread::acquireRenderWindow(bool recreate_window)
|
||||
{
|
||||
if (g_host_display)
|
||||
{
|
||||
if (g_host_display->GetRenderAPI() == api)
|
||||
{
|
||||
// current is fine
|
||||
return true;
|
||||
}
|
||||
DebugAssert(g_host_display);
|
||||
u32 fs_width, fs_height;
|
||||
float fs_refresh_rate;
|
||||
m_is_exclusive_fullscreen = (m_is_fullscreen && g_host_display->SupportsExclusiveFullscreen() &&
|
||||
GPUDevice::GetRequestedExclusiveFullscreenMode(&fs_width, &fs_height, &fs_refresh_rate));
|
||||
|
||||
// otherwise we need to switch
|
||||
releaseHostDisplay();
|
||||
}
|
||||
const bool window_fullscreen = m_is_fullscreen && !m_is_exclusive_fullscreen;
|
||||
const bool render_to_main = !m_is_exclusive_fullscreen && !window_fullscreen && m_is_rendering_to_main;
|
||||
const bool use_main_window_pos = m_is_exclusive_fullscreen && shouldRenderToMain();
|
||||
|
||||
g_host_display = Host::CreateDisplayForAPI(api);
|
||||
if (!g_host_display)
|
||||
return false;
|
||||
return emit onAcquireRenderWindowRequested(recreate_window, window_fullscreen, render_to_main, m_is_surfaceless,
|
||||
use_main_window_pos);
|
||||
}
|
||||
|
||||
if (!createDisplayRequested(m_is_fullscreen, m_is_rendering_to_main))
|
||||
{
|
||||
emit destroyDisplayRequested();
|
||||
g_host_display.reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!g_host_display->MakeCurrent() || !g_host_display->SetupDevice() || !ImGuiManager::Initialize() ||
|
||||
!CommonHost::CreateHostDisplayResources())
|
||||
{
|
||||
ImGuiManager::Shutdown();
|
||||
CommonHost::ReleaseHostDisplayResources();
|
||||
g_host_display.reset();
|
||||
emit destroyDisplayRequested();
|
||||
return false;
|
||||
}
|
||||
|
||||
m_is_exclusive_fullscreen = g_host_display->IsFullscreen();
|
||||
|
||||
if (m_run_fullscreen_ui && !FullscreenUI::Initialize())
|
||||
{
|
||||
Log_ErrorPrint("Failed to initialize fullscreen UI");
|
||||
releaseHostDisplay();
|
||||
m_run_fullscreen_ui = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
void EmuThread::releaseRenderWindow()
|
||||
{
|
||||
emit onReleaseRenderWindowRequested();
|
||||
}
|
||||
|
||||
void EmuThread::connectDisplaySignals(DisplayWidget* widget)
|
||||
|
@ -774,45 +735,6 @@ void EmuThread::connectDisplaySignals(DisplayWidget* widget)
|
|||
connect(widget, &DisplayWidget::windowMouseWheelEvent, this, &EmuThread::onDisplayWindowMouseWheelEvent);
|
||||
}
|
||||
|
||||
void EmuThread::updateDisplayState()
|
||||
{
|
||||
if (!g_host_display)
|
||||
return;
|
||||
|
||||
// this expects the context to get moved back to us afterwards
|
||||
g_host_display->DoneCurrent();
|
||||
|
||||
updateDisplayRequested(m_is_fullscreen, m_is_rendering_to_main && !m_is_fullscreen, m_is_surfaceless);
|
||||
if (!g_host_display->MakeCurrent())
|
||||
Panic("Failed to make device context current after updating");
|
||||
|
||||
m_is_exclusive_fullscreen = g_host_display->IsFullscreen();
|
||||
ImGuiManager::WindowResized();
|
||||
System::HostDisplayResized();
|
||||
|
||||
if (!System::IsShutdown())
|
||||
{
|
||||
System::UpdateSoftwareCursor();
|
||||
|
||||
if (!FullscreenUI::IsInitialized() || System::IsPaused())
|
||||
redrawDisplayWindow();
|
||||
}
|
||||
|
||||
System::UpdateSpeedLimiterState();
|
||||
}
|
||||
|
||||
void EmuThread::releaseHostDisplay()
|
||||
{
|
||||
if (!g_host_display)
|
||||
return;
|
||||
|
||||
CommonHost::ReleaseHostDisplayResources();
|
||||
ImGuiManager::Shutdown();
|
||||
g_host_display.reset();
|
||||
emit destroyDisplayRequested();
|
||||
m_is_fullscreen = false;
|
||||
}
|
||||
|
||||
void Host::OnSystemStarting()
|
||||
{
|
||||
CommonHost::OnSystemStarting();
|
||||
|
@ -1610,40 +1532,34 @@ void Host::CommitBaseSettingChanges()
|
|||
QtHost::QueueSettingsSave();
|
||||
}
|
||||
|
||||
bool Host::AcquireHostDisplay(RenderAPI api)
|
||||
std::optional<WindowInfo> Host::AcquireRenderWindow(bool recreate_window)
|
||||
{
|
||||
return g_emu_thread->acquireHostDisplay(api);
|
||||
return g_emu_thread->acquireRenderWindow(recreate_window);
|
||||
}
|
||||
|
||||
void Host::ReleaseHostDisplay()
|
||||
void Host::ReleaseRenderWindow()
|
||||
{
|
||||
if (g_emu_thread->isRunningFullscreenUI())
|
||||
{
|
||||
// keep display alive when running fsui
|
||||
return;
|
||||
}
|
||||
|
||||
g_emu_thread->releaseHostDisplay();
|
||||
g_emu_thread->releaseRenderWindow();
|
||||
}
|
||||
|
||||
void EmuThread::updatePerformanceCounters()
|
||||
{
|
||||
GPURenderer renderer = GPURenderer::Count;
|
||||
const RenderAPI render_api = g_host_display ? g_host_display->GetRenderAPI() : RenderAPI::None;
|
||||
const bool hardware_renderer = g_gpu && g_gpu->IsHardwareRenderer();
|
||||
u32 render_width = 0;
|
||||
u32 render_height = 0;
|
||||
|
||||
if (g_gpu)
|
||||
{
|
||||
// TODO: Fix renderer type
|
||||
renderer = g_gpu->IsHardwareRenderer() ? GPURenderer::HardwareOpenGL : GPURenderer::Software;
|
||||
std::tie(render_width, render_height) = g_gpu->GetEffectiveDisplayResolution();
|
||||
}
|
||||
|
||||
if (renderer != m_last_renderer)
|
||||
if (render_api != m_last_render_api || hardware_renderer != m_last_hardware_renderer)
|
||||
{
|
||||
const QString renderer_str = hardware_renderer ? QString::fromUtf8(GPUDevice::RenderAPIToString(render_api)) :
|
||||
qApp->translate("GPURenderer", "Software");
|
||||
QMetaObject::invokeMethod(g_main_window->getStatusRendererWidget(), "setText", Qt::QueuedConnection,
|
||||
Q_ARG(const QString&, QString::fromUtf8(Settings::GetRendererName(renderer))));
|
||||
m_last_renderer = renderer;
|
||||
Q_ARG(const QString&, renderer_str));
|
||||
m_last_render_api = render_api;
|
||||
m_last_hardware_renderer = hardware_renderer;
|
||||
}
|
||||
if (render_width != m_last_render_width || render_height != m_last_render_height)
|
||||
{
|
||||
|
@ -1680,7 +1596,8 @@ void EmuThread::resetPerformanceCounters()
|
|||
m_last_video_fps = std::numeric_limits<float>::infinity();
|
||||
m_last_render_width = std::numeric_limits<u32>::max();
|
||||
m_last_render_height = std::numeric_limits<u32>::max();
|
||||
m_last_renderer = GPURenderer::Count;
|
||||
m_last_render_api = RenderAPI::None;
|
||||
m_last_hardware_renderer = false;
|
||||
|
||||
QString blank;
|
||||
QMetaObject::invokeMethod(g_main_window->getStatusRendererWidget(), "setText", Qt::QueuedConnection,
|
||||
|
|
|
@ -95,9 +95,9 @@ public:
|
|||
ALWAYS_INLINE bool isSurfaceless() const { return m_is_surfaceless; }
|
||||
ALWAYS_INLINE bool isRunningFullscreenUI() const { return m_run_fullscreen_ui; }
|
||||
|
||||
bool acquireHostDisplay(RenderAPI api);
|
||||
std::optional<WindowInfo> acquireRenderWindow(bool recreate_window);
|
||||
void connectDisplaySignals(DisplayWidget* widget);
|
||||
void releaseHostDisplay();
|
||||
void releaseRenderWindow();
|
||||
void renderDisplay(bool skip_present);
|
||||
|
||||
void startBackgroundControllerPollTimer();
|
||||
|
@ -132,11 +132,11 @@ Q_SIGNALS:
|
|||
void systemPaused();
|
||||
void systemResumed();
|
||||
void gameListRefreshed();
|
||||
bool createDisplayRequested(bool fullscreen, bool render_to_main);
|
||||
bool updateDisplayRequested(bool fullscreen, bool render_to_main, bool surfaceless);
|
||||
void displaySizeRequested(qint32 width, qint32 height);
|
||||
std::optional<WindowInfo> onAcquireRenderWindowRequested(bool recreate_window, bool fullscreen, bool render_to_main,
|
||||
bool surfaceless, bool use_main_window_pos);
|
||||
void onResizeRenderWindowRequested(qint32 width, qint32 height);
|
||||
void onReleaseRenderWindowRequested();
|
||||
void focusDisplayWidgetRequested();
|
||||
void destroyDisplayRequested();
|
||||
void runningGameChanged(const QString& filename, const QString& game_serial, const QString& game_title);
|
||||
void inputProfileLoaded();
|
||||
void mouseModeRequested(bool relative, bool hide_cursor);
|
||||
|
@ -180,7 +180,7 @@ public Q_SLOTS:
|
|||
void saveScreenshot();
|
||||
void redrawDisplayWindow();
|
||||
void toggleFullscreen();
|
||||
void setFullscreen(bool fullscreen);
|
||||
void setFullscreen(bool fullscreen, bool allow_render_to_main);
|
||||
void setSurfaceless(bool surfaceless);
|
||||
void requestDisplaySize(float scale);
|
||||
void loadCheatList(const QString& filename);
|
||||
|
@ -194,7 +194,7 @@ private Q_SLOTS:
|
|||
void onDisplayWindowMouseMoveEvent(bool relative, float x, float y);
|
||||
void onDisplayWindowMouseButtonEvent(int button, bool pressed);
|
||||
void onDisplayWindowMouseWheelEvent(const QPoint& delta_angle);
|
||||
void onDisplayWindowResized(int width, int height);
|
||||
void onDisplayWindowResized(int width, int height, float scale);
|
||||
void onDisplayWindowKeyEvent(int key, bool pressed);
|
||||
void onDisplayWindowTextEntered(const QString& text);
|
||||
void doBackgroundControllerPoll();
|
||||
|
@ -210,7 +210,6 @@ private:
|
|||
void createBackgroundControllerPollTimer();
|
||||
void destroyBackgroundControllerPollTimer();
|
||||
void setInitialState(std::optional<bool> override_fullscreen);
|
||||
void updateDisplayState();
|
||||
|
||||
QThread* m_ui_thread;
|
||||
QSemaphore m_started_semaphore;
|
||||
|
@ -233,7 +232,8 @@ private:
|
|||
float m_last_video_fps = std::numeric_limits<float>::infinity();
|
||||
u32 m_last_render_width = std::numeric_limits<u32>::max();
|
||||
u32 m_last_render_height = std::numeric_limits<u32>::max();
|
||||
GPURenderer m_last_renderer = GPURenderer::Count;
|
||||
RenderAPI m_last_render_api = RenderAPI::None;
|
||||
bool m_last_hardware_renderer = false;
|
||||
};
|
||||
|
||||
extern EmuThread* g_emu_thread;
|
||||
|
|
|
@ -129,14 +129,86 @@ void CommonHost::PumpMessagesOnCPUThread()
|
|||
#endif
|
||||
}
|
||||
|
||||
bool CommonHost::CreateHostDisplayResources()
|
||||
bool Host::CreateGPUDevice(RenderAPI api)
|
||||
{
|
||||
DebugAssert(!g_host_display);
|
||||
|
||||
Log_InfoPrintf("Trying to create a %s GPU device...", GPUDevice::RenderAPIToString(api));
|
||||
g_host_display = GPUDevice::CreateDeviceForAPI(api);
|
||||
|
||||
// TODO: option to disable shader cache
|
||||
// TODO: FSUI should always use vsync..
|
||||
const bool vsync = System::IsValid() ? System::ShouldUseVSync() : g_settings.video_sync_enabled;
|
||||
if (!g_host_display ||
|
||||
!g_host_display->Create(g_settings.gpu_adapter, EmuFolders::Cache, g_settings.gpu_use_debug_device, vsync))
|
||||
{
|
||||
Log_ErrorPrintf("Failed to initialize GPU device.");
|
||||
if (g_host_display)
|
||||
g_host_display->Destroy();
|
||||
g_host_display.reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ImGuiManager::Initialize())
|
||||
{
|
||||
Log_ErrorPrintf("Failed to initialize ImGuiManager.");
|
||||
g_host_display->Destroy();
|
||||
g_host_display.reset();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CommonHost::ReleaseHostDisplayResources()
|
||||
void Host::UpdateDisplayWindow()
|
||||
{
|
||||
if (!g_host_display)
|
||||
return;
|
||||
|
||||
if (!g_host_display->UpdateWindow())
|
||||
{
|
||||
Host::ReportErrorAsync("Error", "Failed to change window after update. The log may contain more information.");
|
||||
return;
|
||||
}
|
||||
|
||||
ImGuiManager::WindowResized();
|
||||
|
||||
// If we're paused, re-present the current frame at the new window size.
|
||||
if (System::IsValid() && System::IsPaused())
|
||||
RenderDisplay(false);
|
||||
}
|
||||
|
||||
void Host::ResizeDisplayWindow(s32 width, s32 height, float scale)
|
||||
{
|
||||
if (!g_host_display)
|
||||
return;
|
||||
|
||||
Log_DevPrintf("Display window resized to %dx%d", width, height);
|
||||
|
||||
g_host_display->ResizeWindow(width, height, scale);
|
||||
ImGuiManager::WindowResized();
|
||||
|
||||
// If we're paused, re-present the current frame at the new window size.
|
||||
if (System::IsValid())
|
||||
{
|
||||
if (System::IsPaused())
|
||||
RenderDisplay(false);
|
||||
|
||||
System::HostDisplayResized();
|
||||
}
|
||||
}
|
||||
|
||||
void Host::ReleaseGPUDevice()
|
||||
{
|
||||
if (!g_host_display)
|
||||
return;
|
||||
|
||||
SaveStateSelectorUI::DestroyTextures();
|
||||
ImGuiManager::Shutdown();
|
||||
|
||||
Log_InfoPrintf("Destroying %s GPU device...", GPUDevice::RenderAPIToString(g_host_display->GetRenderAPI()));
|
||||
g_host_display->Destroy();
|
||||
g_host_display.reset();
|
||||
}
|
||||
|
||||
#ifndef __ANDROID__
|
||||
|
|
|
@ -33,8 +33,6 @@ void OnSystemPaused();
|
|||
void OnSystemResumed();
|
||||
void OnGameChanged(const std::string& disc_path, const std::string& game_serial, const std::string& game_name);
|
||||
void PumpMessagesOnCPUThread();
|
||||
bool CreateHostDisplayResources();
|
||||
void ReleaseHostDisplayResources();
|
||||
|
||||
/// Returns the time elapsed in the current play session.
|
||||
u64 GetSessionPlayedTime();
|
||||
|
|
Loading…
Reference in New Issue