Renderer: Handle resize events on-demand instead of polling
We now differentiate between a resize event and surface change/destroyed event, reducing the overhead for resizes in the Vulkan backend. It is also now now safe to change the surface multiple times if the video thread is lagging behind.
This commit is contained in:
parent
5baf3bbe2e
commit
de632fc9c8
|
@ -321,12 +321,8 @@ class PlatformX11 : public Platform
|
|||
{
|
||||
last_window_width = event.xconfigure.width;
|
||||
last_window_height = event.xconfigure.height;
|
||||
|
||||
// We call Renderer::ChangeSurface here to indicate the size has changed,
|
||||
// but pass the same window handle. This is needed for the Vulkan backend,
|
||||
// otherwise it cannot tell that the window has been resized on some drivers.
|
||||
if (g_renderer)
|
||||
g_renderer->ChangeSurface(s_window_handle);
|
||||
g_renderer->ResizeSurface(last_window_width, last_window_height);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -51,10 +51,10 @@ void Host::SetRenderFullscreen(bool fullscreen)
|
|||
m_render_fullscreen = fullscreen;
|
||||
}
|
||||
|
||||
void Host::UpdateSurface()
|
||||
void Host::ResizeSurface(int new_width, int new_height)
|
||||
{
|
||||
if (g_renderer)
|
||||
g_renderer->ChangeSurface(GetRenderHandle());
|
||||
g_renderer->ResizeSurface(new_width, new_height);
|
||||
}
|
||||
|
||||
void Host_Message(int id)
|
||||
|
|
|
@ -26,7 +26,7 @@ public:
|
|||
void SetRenderHandle(void* handle);
|
||||
void SetRenderFocus(bool focus);
|
||||
void SetRenderFullscreen(bool fullscreen);
|
||||
void UpdateSurface();
|
||||
void ResizeSurface(int new_width, int new_height);
|
||||
|
||||
signals:
|
||||
void RequestTitle(const QString& title);
|
||||
|
|
|
@ -22,7 +22,7 @@ RenderWidget::RenderWidget(QWidget* parent) : QWidget(parent)
|
|||
Qt::DirectConnection);
|
||||
connect(this, &RenderWidget::HandleChanged, Host::GetInstance(), &Host::SetRenderHandle,
|
||||
Qt::DirectConnection);
|
||||
connect(this, &RenderWidget::SizeChanged, Host::GetInstance(), &Host::UpdateSurface,
|
||||
connect(this, &RenderWidget::SizeChanged, Host::GetInstance(), &Host::ResizeSurface,
|
||||
Qt::DirectConnection);
|
||||
|
||||
emit HandleChanged((void*)winId());
|
||||
|
@ -84,8 +84,12 @@ bool RenderWidget::event(QEvent* event)
|
|||
Host::GetInstance()->SetRenderFocus(false);
|
||||
break;
|
||||
case QEvent::Resize:
|
||||
emit SizeChanged();
|
||||
{
|
||||
const QResizeEvent* se = static_cast<QResizeEvent*>(event);
|
||||
QSize new_size = se->size();
|
||||
emit SizeChanged(new_size.width(), new_size.height());
|
||||
break;
|
||||
}
|
||||
case QEvent::WindowStateChange:
|
||||
emit StateChanged(isFullScreen());
|
||||
break;
|
||||
|
|
|
@ -23,7 +23,7 @@ signals:
|
|||
void Closed();
|
||||
void HandleChanged(void* handle);
|
||||
void StateChanged(bool fullscreen);
|
||||
void SizeChanged();
|
||||
void SizeChanged(int new_width, int new_height);
|
||||
|
||||
private:
|
||||
void HandleCursorTimer();
|
||||
|
|
|
@ -603,21 +603,18 @@ void CFrame::OnRenderParentResize(wxSizeEvent& event)
|
|||
if (Core::GetState() != Core::State::Uninitialized)
|
||||
{
|
||||
int width, height;
|
||||
m_render_frame->GetClientSize(&width, &height);
|
||||
if (!SConfig::GetInstance().bRenderToMain && !RendererIsFullscreen() &&
|
||||
!m_render_frame->IsMaximized() && !m_render_frame->IsIconized())
|
||||
{
|
||||
m_render_frame->GetClientSize(&width, &height);
|
||||
SConfig::GetInstance().iRenderWindowWidth = width;
|
||||
SConfig::GetInstance().iRenderWindowHeight = height;
|
||||
}
|
||||
m_log_window->Refresh();
|
||||
m_log_window->Update();
|
||||
|
||||
// We call Renderer::ChangeSurface here to indicate the size has changed,
|
||||
// but pass the same window handle. This is needed for the Vulkan backend,
|
||||
// otherwise it cannot tell that the window has been resized on some drivers.
|
||||
if (g_renderer)
|
||||
g_renderer->ChangeSurface(GetRenderHandle());
|
||||
g_renderer->ResizeSurface(width, height);
|
||||
}
|
||||
event.Skip();
|
||||
}
|
||||
|
|
|
@ -37,9 +37,9 @@ namespace D3D
|
|||
ID3D11Device* device = nullptr;
|
||||
ID3D11Device1* device1 = nullptr;
|
||||
ID3D11DeviceContext* context = nullptr;
|
||||
HWND hWnd;
|
||||
IDXGISwapChain1* swapchain = nullptr;
|
||||
|
||||
static IDXGISwapChain1* s_swapchain;
|
||||
static IDXGIFactory2* s_dxgi_factory;
|
||||
static ID3D11Debug* s_debug;
|
||||
static D3D_FEATURE_LEVEL s_featlevel;
|
||||
static D3DTexture2D* s_backbuf;
|
||||
|
@ -49,9 +49,6 @@ static std::vector<DXGI_SAMPLE_DESC> s_aa_modes; // supported AA modes of the c
|
|||
static bool s_bgra_textures_supported;
|
||||
static bool s_allow_tearing_supported;
|
||||
|
||||
static unsigned int s_xres;
|
||||
static unsigned int s_yres;
|
||||
|
||||
constexpr UINT NUM_SUPPORTED_FEATURE_LEVELS = 3;
|
||||
constexpr D3D_FEATURE_LEVEL supported_feature_levels[NUM_SUPPORTED_FEATURE_LEVELS] = {
|
||||
D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0};
|
||||
|
@ -247,17 +244,74 @@ static bool SupportsBPTCTextures(ID3D11Device* dev)
|
|||
return (bc7_support & D3D11_FORMAT_SUPPORT_TEXTURE2D) != 0;
|
||||
}
|
||||
|
||||
static bool CreateSwapChainTextures()
|
||||
{
|
||||
ID3D11Texture2D* buf;
|
||||
HRESULT hr = swapchain->GetBuffer(0, IID_ID3D11Texture2D, (void**)&buf);
|
||||
CHECK(SUCCEEDED(hr), "GetBuffer for swap chain failed with HRESULT %08X", hr);
|
||||
if (FAILED(hr))
|
||||
return false;
|
||||
|
||||
s_backbuf = new D3DTexture2D(buf, D3D11_BIND_RENDER_TARGET);
|
||||
SAFE_RELEASE(buf);
|
||||
SetDebugObjectName(s_backbuf->GetTex(), "backbuffer texture");
|
||||
SetDebugObjectName(s_backbuf->GetRTV(), "backbuffer render target view");
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool CreateSwapChain(HWND hWnd)
|
||||
{
|
||||
DXGI_SWAP_CHAIN_DESC1 swap_chain_desc = {};
|
||||
swap_chain_desc.BufferCount = 2;
|
||||
swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
|
||||
swap_chain_desc.SampleDesc.Count = 1;
|
||||
swap_chain_desc.SampleDesc.Quality = 0;
|
||||
swap_chain_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
swap_chain_desc.Scaling = DXGI_SCALING_STRETCH;
|
||||
swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
|
||||
swap_chain_desc.Stereo = g_ActiveConfig.stereo_mode == StereoMode::QuadBuffer;
|
||||
|
||||
// This flag is necessary if we want to use a flip-model swapchain without locking the framerate
|
||||
swap_chain_desc.Flags = s_allow_tearing_supported ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0;
|
||||
|
||||
HRESULT hr = s_dxgi_factory->CreateSwapChainForHwnd(device, hWnd, &swap_chain_desc, nullptr,
|
||||
nullptr, &swapchain);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
// Flip-model discard swapchains aren't supported on Windows 8, so here we fall back to
|
||||
// a sequential swapchain
|
||||
swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
|
||||
hr = s_dxgi_factory->CreateSwapChainForHwnd(device, hWnd, &swap_chain_desc, nullptr, nullptr,
|
||||
&swapchain);
|
||||
}
|
||||
|
||||
if (FAILED(hr))
|
||||
{
|
||||
// Flip-model swapchains aren't supported on Windows 7, so here we fall back to a legacy
|
||||
// BitBlt-model swapchain
|
||||
swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
|
||||
hr = s_dxgi_factory->CreateSwapChainForHwnd(device, hWnd, &swap_chain_desc, nullptr, nullptr,
|
||||
&swapchain);
|
||||
}
|
||||
|
||||
if (FAILED(hr))
|
||||
{
|
||||
ERROR_LOG(VIDEO, "Failed to create swap chain with HRESULT %08X", hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!CreateSwapChainTextures())
|
||||
{
|
||||
SAFE_RELEASE(swapchain);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
HRESULT Create(HWND wnd)
|
||||
{
|
||||
hWnd = wnd;
|
||||
HRESULT hr;
|
||||
|
||||
RECT client;
|
||||
GetClientRect(hWnd, &client);
|
||||
s_xres = client.right - client.left;
|
||||
s_yres = client.bottom - client.top;
|
||||
|
||||
hr = LoadDXGI();
|
||||
HRESULT hr = LoadDXGI();
|
||||
if (SUCCEEDED(hr))
|
||||
hr = LoadD3D();
|
||||
if (SUCCEEDED(hr))
|
||||
|
@ -270,18 +324,17 @@ HRESULT Create(HWND wnd)
|
|||
return hr;
|
||||
}
|
||||
|
||||
IDXGIFactory2* factory;
|
||||
hr = PCreateDXGIFactory(__uuidof(IDXGIFactory2), (void**)&factory);
|
||||
hr = PCreateDXGIFactory(__uuidof(IDXGIFactory2), (void**)&s_dxgi_factory);
|
||||
if (FAILED(hr))
|
||||
MessageBox(wnd, _T("Failed to create IDXGIFactory object"), _T("Dolphin Direct3D 11 backend"),
|
||||
MB_OK | MB_ICONERROR);
|
||||
|
||||
IDXGIAdapter* adapter;
|
||||
hr = factory->EnumAdapters(g_ActiveConfig.iAdapter, &adapter);
|
||||
hr = s_dxgi_factory->EnumAdapters(g_ActiveConfig.iAdapter, &adapter);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
// try using the first one
|
||||
hr = factory->EnumAdapters(0, &adapter);
|
||||
hr = s_dxgi_factory->EnumAdapters(0, &adapter);
|
||||
if (FAILED(hr))
|
||||
MessageBox(wnd, _T("Failed to enumerate adapters"), _T("Dolphin Direct3D 11 backend"),
|
||||
MB_OK | MB_ICONERROR);
|
||||
|
@ -301,7 +354,7 @@ HRESULT Create(HWND wnd)
|
|||
// Check support for allow tearing, we query the interface for backwards compatibility
|
||||
UINT allow_tearing = FALSE;
|
||||
IDXGIFactory5* factory5;
|
||||
hr = factory->QueryInterface(&factory5);
|
||||
hr = s_dxgi_factory->QueryInterface(&factory5);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = factory5->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &allow_tearing,
|
||||
|
@ -310,21 +363,6 @@ HRESULT Create(HWND wnd)
|
|||
}
|
||||
s_allow_tearing_supported = SUCCEEDED(hr) && allow_tearing;
|
||||
|
||||
DXGI_SWAP_CHAIN_DESC1 swap_chain_desc = {};
|
||||
swap_chain_desc.BufferCount = 2;
|
||||
swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
|
||||
swap_chain_desc.SampleDesc.Count = 1;
|
||||
swap_chain_desc.SampleDesc.Quality = 0;
|
||||
swap_chain_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
|
||||
swap_chain_desc.Scaling = DXGI_SCALING_STRETCH;
|
||||
swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
|
||||
swap_chain_desc.Width = s_xres;
|
||||
swap_chain_desc.Height = s_yres;
|
||||
swap_chain_desc.Stereo = g_ActiveConfig.stereo_mode == StereoMode::QuadBuffer;
|
||||
|
||||
// This flag is necessary if we want to use a flip-model swapchain without locking the framerate
|
||||
swap_chain_desc.Flags = s_allow_tearing_supported ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0;
|
||||
|
||||
// Creating debug devices can sometimes fail if the user doesn't have the correct
|
||||
// version of the DirectX SDK. If it does, simply fallback to a non-debug device.
|
||||
if (g_Config.bEnableValidationLayer)
|
||||
|
@ -360,30 +398,9 @@ HRESULT Create(HWND wnd)
|
|||
D3D11_SDK_VERSION, &device, &s_featlevel, &context);
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = factory->CreateSwapChainForHwnd(device, hWnd, &swap_chain_desc, nullptr, nullptr,
|
||||
&s_swapchain);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
// Flip-model discard swapchains aren't supported on Windows 8, so here we fall back to
|
||||
// a sequential swapchain
|
||||
swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
|
||||
hr = factory->CreateSwapChainForHwnd(device, hWnd, &swap_chain_desc, nullptr, nullptr,
|
||||
&s_swapchain);
|
||||
}
|
||||
SAFE_RELEASE(adapter);
|
||||
|
||||
if (FAILED(hr))
|
||||
{
|
||||
// Flip-model swapchains aren't supported on Windows 7, so here we fall back to a legacy
|
||||
// BitBlt-model swapchain
|
||||
swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
|
||||
hr = factory->CreateSwapChainForHwnd(device, hWnd, &swap_chain_desc, nullptr, nullptr,
|
||||
&s_swapchain);
|
||||
}
|
||||
}
|
||||
|
||||
if (FAILED(hr))
|
||||
if (FAILED(hr) || (wnd && !CreateSwapChain(wnd)))
|
||||
{
|
||||
MessageBox(
|
||||
wnd,
|
||||
|
@ -391,7 +408,7 @@ HRESULT Create(HWND wnd)
|
|||
_T("Dolphin Direct3D 11 backend"), MB_OK | MB_ICONERROR);
|
||||
SAFE_RELEASE(device);
|
||||
SAFE_RELEASE(context);
|
||||
SAFE_RELEASE(s_swapchain);
|
||||
SAFE_RELEASE(s_dxgi_factory);
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
|
@ -399,52 +416,23 @@ HRESULT Create(HWND wnd)
|
|||
if (FAILED(hr))
|
||||
WARN_LOG(VIDEO, "Missing Direct3D 11.1 support. Logical operations will not be supported.");
|
||||
|
||||
// prevent DXGI from responding to Alt+Enter, unfortunately DXGI_MWA_NO_ALT_ENTER
|
||||
// does not work so we disable all monitoring of window messages. However this
|
||||
// may make it more difficult for DXGI to handle display mode changes.
|
||||
hr = factory->MakeWindowAssociation(wnd, DXGI_MWA_NO_WINDOW_CHANGES);
|
||||
if (FAILED(hr))
|
||||
MessageBox(wnd, _T("Failed to associate the window"), _T("Dolphin Direct3D 11 backend"),
|
||||
MB_OK | MB_ICONERROR);
|
||||
|
||||
SetDebugObjectName(context, "device context");
|
||||
SAFE_RELEASE(factory);
|
||||
SAFE_RELEASE(adapter);
|
||||
|
||||
if (SConfig::GetInstance().bFullscreen && !g_ActiveConfig.bBorderlessFullscreen)
|
||||
{
|
||||
s_swapchain->SetFullscreenState(true, nullptr);
|
||||
s_swapchain->ResizeBuffers(0, s_xres, s_yres, DXGI_FORMAT_R8G8B8A8_UNORM,
|
||||
swap_chain_desc.Flags);
|
||||
}
|
||||
|
||||
ID3D11Texture2D* buf;
|
||||
hr = s_swapchain->GetBuffer(0, IID_ID3D11Texture2D, (void**)&buf);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
MessageBox(wnd, _T("Failed to get swapchain buffer"), _T("Dolphin Direct3D 11 backend"),
|
||||
MB_OK | MB_ICONERROR);
|
||||
SAFE_RELEASE(device);
|
||||
SAFE_RELEASE(context);
|
||||
SAFE_RELEASE(s_swapchain);
|
||||
return E_FAIL;
|
||||
}
|
||||
s_backbuf = new D3DTexture2D(buf, D3D11_BIND_RENDER_TARGET);
|
||||
SAFE_RELEASE(buf);
|
||||
CHECK(s_backbuf != nullptr, "Create back buffer texture");
|
||||
SetDebugObjectName(s_backbuf->GetTex(), "backbuffer texture");
|
||||
SetDebugObjectName(s_backbuf->GetRTV(), "backbuffer render target view");
|
||||
|
||||
context->OMSetRenderTargets(1, &s_backbuf->GetRTV(), nullptr);
|
||||
|
||||
// BGRA textures are easier to deal with in TextureCache, but might not be supported by the
|
||||
// hardware
|
||||
// BGRA textures are easier to deal with in TextureCache, but might not be supported
|
||||
UINT format_support;
|
||||
device->CheckFormatSupport(DXGI_FORMAT_B8G8R8A8_UNORM, &format_support);
|
||||
s_bgra_textures_supported = (format_support & D3D11_FORMAT_SUPPORT_TEXTURE2D) != 0;
|
||||
g_Config.backend_info.bSupportsST3CTextures = SupportsS3TCTextures(device);
|
||||
g_Config.backend_info.bSupportsBPTCTextures = SupportsBPTCTextures(device);
|
||||
|
||||
// prevent DXGI from responding to Alt+Enter, unfortunately DXGI_MWA_NO_ALT_ENTER
|
||||
// does not work so we disable all monitoring of window messages. However this
|
||||
// may make it more difficult for DXGI to handle display mode changes.
|
||||
hr = s_dxgi_factory->MakeWindowAssociation(wnd, DXGI_MWA_NO_WINDOW_CHANGES);
|
||||
if (FAILED(hr))
|
||||
MessageBox(wnd, _T("Failed to associate the window"), _T("Dolphin Direct3D 11 backend"),
|
||||
MB_OK | MB_ICONERROR);
|
||||
|
||||
SetDebugObjectName(context, "device context");
|
||||
|
||||
stateman = new StateManager;
|
||||
return S_OK;
|
||||
}
|
||||
|
@ -452,12 +440,13 @@ HRESULT Create(HWND wnd)
|
|||
void Close()
|
||||
{
|
||||
// we can't release the swapchain while in fullscreen.
|
||||
s_swapchain->SetFullscreenState(false, nullptr);
|
||||
if (swapchain)
|
||||
swapchain->SetFullscreenState(false, nullptr);
|
||||
|
||||
// release all bound resources
|
||||
context->ClearState();
|
||||
SAFE_RELEASE(s_backbuf);
|
||||
SAFE_RELEASE(s_swapchain);
|
||||
SAFE_RELEASE(swapchain);
|
||||
SAFE_DELETE(stateman);
|
||||
context->Flush(); // immediately destroy device objects
|
||||
|
||||
|
@ -465,7 +454,6 @@ void Close()
|
|||
SAFE_RELEASE(device1);
|
||||
ULONG references = device->Release();
|
||||
|
||||
#if defined(_DEBUG) || defined(DEBUGFAST)
|
||||
if (s_debug)
|
||||
{
|
||||
--references; // the debug interface increases the refcount of the device, subtract that.
|
||||
|
@ -477,7 +465,6 @@ void Close()
|
|||
}
|
||||
SAFE_RELEASE(s_debug)
|
||||
}
|
||||
#endif
|
||||
|
||||
if (references)
|
||||
{
|
||||
|
@ -524,19 +511,10 @@ const char* PixelShaderVersionString()
|
|||
return "ps_4_0";
|
||||
}
|
||||
|
||||
D3DTexture2D*& GetBackBuffer()
|
||||
D3DTexture2D* GetBackBuffer()
|
||||
{
|
||||
return s_backbuf;
|
||||
}
|
||||
unsigned int GetBackBufferWidth()
|
||||
{
|
||||
return s_xres;
|
||||
}
|
||||
unsigned int GetBackBufferHeight()
|
||||
{
|
||||
return s_yres;
|
||||
}
|
||||
|
||||
bool BGRATexturesSupported()
|
||||
{
|
||||
return s_bgra_textures_supported;
|
||||
|
@ -572,37 +550,31 @@ u32 GetMaxTextureSize(D3D_FEATURE_LEVEL feature_level)
|
|||
}
|
||||
}
|
||||
|
||||
void Reset()
|
||||
void Reset(HWND new_wnd)
|
||||
{
|
||||
// release all back buffer references
|
||||
SAFE_RELEASE(s_backbuf);
|
||||
|
||||
UINT swap_chain_flags = AllowTearingSupported() ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0;
|
||||
|
||||
// resize swapchain buffers
|
||||
RECT client;
|
||||
GetClientRect(hWnd, &client);
|
||||
s_xres = client.right - client.left;
|
||||
s_yres = client.bottom - client.top;
|
||||
s_swapchain->ResizeBuffers(0, s_xres, s_yres, DXGI_FORMAT_R8G8B8A8_UNORM, swap_chain_flags);
|
||||
|
||||
// recreate back buffer texture
|
||||
ID3D11Texture2D* buf;
|
||||
HRESULT hr = s_swapchain->GetBuffer(0, IID_ID3D11Texture2D, (void**)&buf);
|
||||
if (FAILED(hr))
|
||||
if (swapchain)
|
||||
{
|
||||
MessageBox(hWnd, _T("Failed to get swapchain buffer"), _T("Dolphin Direct3D 11 backend"),
|
||||
MB_OK | MB_ICONERROR);
|
||||
SAFE_RELEASE(device);
|
||||
SAFE_RELEASE(context);
|
||||
SAFE_RELEASE(s_swapchain);
|
||||
return;
|
||||
if (GetFullscreenState())
|
||||
swapchain->SetFullscreenState(FALSE, nullptr);
|
||||
SAFE_RELEASE(swapchain);
|
||||
}
|
||||
|
||||
if (new_wnd)
|
||||
CreateSwapChain(new_wnd);
|
||||
}
|
||||
|
||||
void ResizeSwapChain()
|
||||
{
|
||||
SAFE_RELEASE(s_backbuf);
|
||||
const UINT swap_chain_flags = AllowTearingSupported() ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0;
|
||||
swapchain->ResizeBuffers(0, 0, 0, DXGI_FORMAT_R8G8B8A8_UNORM, swap_chain_flags);
|
||||
if (!CreateSwapChainTextures())
|
||||
{
|
||||
PanicAlert("Failed to get swap chain textures");
|
||||
SAFE_RELEASE(swapchain);
|
||||
}
|
||||
s_backbuf = new D3DTexture2D(buf, D3D11_BIND_RENDER_TARGET);
|
||||
SAFE_RELEASE(buf);
|
||||
CHECK(s_backbuf != nullptr, "Create back buffer texture");
|
||||
SetDebugObjectName(s_backbuf->GetTex(), "backbuffer texture");
|
||||
SetDebugObjectName(s_backbuf->GetRTV(), "backbuffer render target view");
|
||||
}
|
||||
|
||||
void Present()
|
||||
|
@ -616,25 +588,24 @@ void Present()
|
|||
if (AllowTearingSupported() && !g_ActiveConfig.IsVSync() && !GetFullscreenState())
|
||||
present_flags |= DXGI_PRESENT_ALLOW_TEARING;
|
||||
|
||||
if (s_swapchain->IsTemporaryMonoSupported() &&
|
||||
g_ActiveConfig.stereo_mode != StereoMode::QuadBuffer)
|
||||
if (swapchain->IsTemporaryMonoSupported() && g_ActiveConfig.stereo_mode != StereoMode::QuadBuffer)
|
||||
{
|
||||
present_flags |= DXGI_PRESENT_STEREO_TEMPORARY_MONO;
|
||||
}
|
||||
|
||||
// TODO: Is 1 the correct value for vsyncing?
|
||||
s_swapchain->Present((UINT)g_ActiveConfig.IsVSync(), present_flags);
|
||||
swapchain->Present(static_cast<UINT>(g_ActiveConfig.IsVSync()), present_flags);
|
||||
}
|
||||
|
||||
HRESULT SetFullscreenState(bool enable_fullscreen)
|
||||
{
|
||||
return s_swapchain->SetFullscreenState(enable_fullscreen, nullptr);
|
||||
return swapchain->SetFullscreenState(enable_fullscreen, nullptr);
|
||||
}
|
||||
|
||||
bool GetFullscreenState()
|
||||
{
|
||||
BOOL state = FALSE;
|
||||
s_swapchain->GetFullscreenState(&state, nullptr);
|
||||
swapchain->GetFullscreenState(&state, nullptr);
|
||||
return !!state;
|
||||
}
|
||||
|
||||
|
|
|
@ -59,14 +59,13 @@ void Close();
|
|||
extern ID3D11Device* device;
|
||||
extern ID3D11Device1* device1;
|
||||
extern ID3D11DeviceContext* context;
|
||||
extern HWND hWnd;
|
||||
extern IDXGISwapChain1* swapchain;
|
||||
|
||||
void Reset();
|
||||
void Reset(HWND new_wnd);
|
||||
void ResizeSwapChain();
|
||||
void Present();
|
||||
|
||||
unsigned int GetBackBufferWidth();
|
||||
unsigned int GetBackBufferHeight();
|
||||
D3DTexture2D*& GetBackBuffer();
|
||||
D3DTexture2D* GetBackBuffer();
|
||||
const char* PixelShaderVersionString();
|
||||
const char* GeometryShaderVersionString();
|
||||
const char* VertexShaderVersionString();
|
||||
|
|
|
@ -387,9 +387,9 @@ int CD3DFont::DrawTextScaled(float x, float y, float size, float spacing, u32 dw
|
|||
UINT stride = sizeof(FONT2DVERTEX);
|
||||
UINT bufoffset = 0;
|
||||
|
||||
float scalex = 1 / (float)D3D::GetBackBufferWidth() * 2.f;
|
||||
float scaley = 1 / (float)D3D::GetBackBufferHeight() * 2.f;
|
||||
float sizeratio = size / (float)m_LineHeight;
|
||||
float scalex = 1.0f / g_renderer->GetBackbufferWidth() * 2.f;
|
||||
float scaley = 1.0f / g_renderer->GetBackbufferHeight() * 2.f;
|
||||
float sizeratio = size / m_LineHeight;
|
||||
|
||||
// translate starting positions
|
||||
float sx = x * scalex - 1.f;
|
||||
|
|
|
@ -53,12 +53,12 @@ typedef struct _Nv_Stereo_Image_Header
|
|||
|
||||
#define NVSTEREO_IMAGE_SIGNATURE 0x4433564e
|
||||
|
||||
Renderer::Renderer() : ::Renderer(D3D::GetBackBufferWidth(), D3D::GetBackBufferHeight())
|
||||
Renderer::Renderer(int backbuffer_width, int backbuffer_height)
|
||||
: ::Renderer(backbuffer_width, backbuffer_height)
|
||||
{
|
||||
m_last_multisamples = g_ActiveConfig.iMultisamples;
|
||||
m_last_stereo_mode = g_ActiveConfig.stereo_mode != StereoMode::Off;
|
||||
m_last_fullscreen_mode = D3D::GetFullscreenState();
|
||||
|
||||
m_last_fullscreen_state = D3D::GetFullscreenState();
|
||||
g_framebuffer_manager = std::make_unique<FramebufferManager>(m_target_width, m_target_height);
|
||||
SetupDeviceObjects();
|
||||
|
||||
|
@ -239,25 +239,6 @@ TargetRectangle Renderer::ConvertEFBRectangle(const EFBRectangle& rc)
|
|||
return result;
|
||||
}
|
||||
|
||||
// With D3D, we have to resize the backbuffer if the window changed
|
||||
// size.
|
||||
bool Renderer::CheckForResize()
|
||||
{
|
||||
RECT rcWindow;
|
||||
GetClientRect(D3D::hWnd, &rcWindow);
|
||||
int client_width = rcWindow.right - rcWindow.left;
|
||||
int client_height = rcWindow.bottom - rcWindow.top;
|
||||
|
||||
// Sanity check
|
||||
if ((client_width != GetBackbufferWidth() || client_height != GetBackbufferHeight()) &&
|
||||
client_width >= 4 && client_height >= 4)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Renderer::SetScissorRect(const MathUtil::Rectangle<int>& rc)
|
||||
{
|
||||
const RECT rect = {rc.left, rc.top, rc.right, rc.bottom};
|
||||
|
@ -549,12 +530,13 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region
|
|||
ResetAPIState();
|
||||
|
||||
// Prepare to copy the XFBs to our backbuffer
|
||||
CheckForSurfaceChange();
|
||||
CheckForSurfaceResize();
|
||||
UpdateDrawRectangle();
|
||||
|
||||
TargetRectangle targetRc = GetTargetRectangle();
|
||||
|
||||
static constexpr std::array<float, 4> clear_color{{0.f, 0.f, 0.f, 1.f}};
|
||||
D3D::context->OMSetRenderTargets(1, &D3D::GetBackBuffer()->GetRTV(), nullptr);
|
||||
|
||||
constexpr std::array<float, 4> clear_color{{0.f, 0.f, 0.f, 1.f}};
|
||||
D3D::context->ClearRenderTargetView(D3D::GetBackBuffer()->GetRTV(), clear_color.data());
|
||||
|
||||
// activate linear filtering for the buffer copies
|
||||
|
@ -565,8 +547,8 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region
|
|||
xfb_texture->GetConfig().width, xfb_texture->GetConfig().height, Gamma);
|
||||
|
||||
// Reset viewport for drawing text
|
||||
D3D11_VIEWPORT vp =
|
||||
CD3D11_VIEWPORT(0.0f, 0.0f, (float)GetBackbufferWidth(), (float)GetBackbufferHeight());
|
||||
D3D11_VIEWPORT vp = CD3D11_VIEWPORT(0.0f, 0.0f, static_cast<float>(m_backbuffer_width),
|
||||
static_cast<float>(m_backbuffer_height));
|
||||
D3D::context->RSSetViewports(1, &vp);
|
||||
|
||||
Renderer::DrawDebugText();
|
||||
|
@ -580,41 +562,21 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region
|
|||
g_texture_cache->OnConfigChanged(g_ActiveConfig);
|
||||
VertexShaderCache::RetreiveAsyncShaders();
|
||||
|
||||
const bool window_resized = CheckForResize();
|
||||
const bool fullscreen = D3D::GetFullscreenState();
|
||||
const bool fs_changed = m_last_fullscreen_mode != fullscreen;
|
||||
|
||||
// Flip/present backbuffer to frontbuffer here
|
||||
if (D3D::swapchain)
|
||||
D3D::Present();
|
||||
|
||||
// Resize the back buffers NOW to avoid flickering
|
||||
if (CalculateTargetSize() || window_resized || fs_changed ||
|
||||
m_last_multisamples != g_ActiveConfig.iMultisamples ||
|
||||
if (CalculateTargetSize() || m_last_multisamples != g_ActiveConfig.iMultisamples ||
|
||||
m_last_stereo_mode != (g_ActiveConfig.stereo_mode != StereoMode::Off))
|
||||
{
|
||||
m_last_multisamples = g_ActiveConfig.iMultisamples;
|
||||
m_last_fullscreen_mode = fullscreen;
|
||||
PixelShaderCache::InvalidateMSAAShaders();
|
||||
|
||||
if (window_resized || fs_changed)
|
||||
{
|
||||
// TODO: Aren't we still holding a reference to the back buffer right now?
|
||||
D3D::Reset();
|
||||
SAFE_RELEASE(m_screenshot_texture);
|
||||
SAFE_RELEASE(m_3d_vision_texture);
|
||||
m_backbuffer_width = D3D::GetBackBufferWidth();
|
||||
m_backbuffer_height = D3D::GetBackBufferHeight();
|
||||
}
|
||||
|
||||
UpdateDrawRectangle();
|
||||
|
||||
m_last_stereo_mode = g_ActiveConfig.stereo_mode != StereoMode::Off;
|
||||
|
||||
D3D::context->OMSetRenderTargets(1, &D3D::GetBackBuffer()->GetRTV(), nullptr);
|
||||
PixelShaderCache::InvalidateMSAAShaders();
|
||||
UpdateDrawRectangle();
|
||||
|
||||
g_framebuffer_manager.reset();
|
||||
g_framebuffer_manager = std::make_unique<FramebufferManager>(m_target_width, m_target_height);
|
||||
|
||||
D3D::context->ClearRenderTargetView(FramebufferManager::GetEFBColorTexture()->GetRTV(),
|
||||
clear_color.data());
|
||||
D3D::context->ClearDepthStencilView(FramebufferManager::GetEFBDepthTexture()->GetDSV(),
|
||||
|
@ -633,6 +595,54 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region
|
|||
FramebufferManager::BindEFBRenderTarget();
|
||||
}
|
||||
|
||||
void Renderer::CheckForSurfaceChange()
|
||||
{
|
||||
if (!m_surface_changed.TestAndClear())
|
||||
return;
|
||||
|
||||
m_surface_handle = m_new_surface_handle;
|
||||
m_new_surface_handle = nullptr;
|
||||
|
||||
SAFE_RELEASE(m_screenshot_texture);
|
||||
SAFE_RELEASE(m_3d_vision_texture);
|
||||
D3D::Reset(reinterpret_cast<HWND>(m_new_surface_handle));
|
||||
UpdateBackbufferSize();
|
||||
}
|
||||
|
||||
void Renderer::CheckForSurfaceResize()
|
||||
{
|
||||
const bool fullscreen_state = D3D::GetFullscreenState();
|
||||
const bool exclusive_fullscreen_changed = fullscreen_state != m_last_fullscreen_state;
|
||||
if (!m_surface_resized.TestAndClear() && !exclusive_fullscreen_changed)
|
||||
return;
|
||||
|
||||
m_backbuffer_width = m_new_backbuffer_width;
|
||||
m_backbuffer_height = m_new_backbuffer_height;
|
||||
|
||||
SAFE_RELEASE(m_screenshot_texture);
|
||||
SAFE_RELEASE(m_3d_vision_texture);
|
||||
m_last_fullscreen_state = fullscreen_state;
|
||||
if (D3D::swapchain)
|
||||
D3D::ResizeSwapChain();
|
||||
UpdateBackbufferSize();
|
||||
}
|
||||
|
||||
void Renderer::UpdateBackbufferSize()
|
||||
{
|
||||
if (D3D::swapchain)
|
||||
{
|
||||
DXGI_SWAP_CHAIN_DESC1 desc = {};
|
||||
D3D::swapchain->GetDesc1(&desc);
|
||||
m_backbuffer_width = std::max(desc.Width, 1u);
|
||||
m_backbuffer_height = std::max(desc.Height, 1u);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_backbuffer_width = 1;
|
||||
m_backbuffer_height = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// ALWAYS call RestoreAPIState for each ResetAPIState call you're doing
|
||||
void Renderer::ResetAPIState()
|
||||
{
|
||||
|
|
|
@ -18,7 +18,7 @@ class D3DTexture2D;
|
|||
class Renderer : public ::Renderer
|
||||
{
|
||||
public:
|
||||
Renderer();
|
||||
Renderer(int backbuffer_width, int backbuffer_height);
|
||||
~Renderer() override;
|
||||
|
||||
StateCache& GetStateCache() { return m_state_cache; }
|
||||
|
@ -63,8 +63,6 @@ public:
|
|||
|
||||
void ReinterpretPixelData(unsigned int convtype) override;
|
||||
|
||||
bool CheckForResize();
|
||||
|
||||
private:
|
||||
struct GXPipelineState
|
||||
{
|
||||
|
@ -77,6 +75,9 @@ private:
|
|||
void SetupDeviceObjects();
|
||||
void TeardownDeviceObjects();
|
||||
void Create3DVisionTexture(int width, int height);
|
||||
void CheckForSurfaceChange();
|
||||
void CheckForSurfaceResize();
|
||||
void UpdateBackbufferSize();
|
||||
|
||||
void BlitScreen(TargetRectangle src, TargetRectangle dst, D3DTexture2D* src_texture,
|
||||
u32 src_width, u32 src_height, float Gamma);
|
||||
|
@ -95,6 +96,6 @@ private:
|
|||
|
||||
u32 m_last_multisamples = 1;
|
||||
bool m_last_stereo_mode = false;
|
||||
bool m_last_fullscreen_mode = false;
|
||||
bool m_last_fullscreen_state = false;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -137,7 +137,17 @@ bool VideoBackend::Initialize(void* window_handle)
|
|||
return false;
|
||||
}
|
||||
|
||||
g_renderer = std::make_unique<Renderer>();
|
||||
int backbuffer_width = 1, backbuffer_height = 1;
|
||||
if (D3D::swapchain)
|
||||
{
|
||||
DXGI_SWAP_CHAIN_DESC1 desc = {};
|
||||
D3D::swapchain->GetDesc1(&desc);
|
||||
backbuffer_width = std::max(desc.Width, 1u);
|
||||
backbuffer_height = std::max(desc.Height, 1u);
|
||||
}
|
||||
|
||||
// internal interfaces
|
||||
g_renderer = std::make_unique<Renderer>(backbuffer_width, backbuffer_height);
|
||||
g_texture_cache = std::make_unique<TextureCache>();
|
||||
g_vertex_manager = std::make_unique<VertexManager>();
|
||||
g_perf_query = std::make_unique<PerfQuery>();
|
||||
|
|
|
@ -845,12 +845,10 @@ std::unique_ptr<AbstractStagingTexture> Renderer::CreateStagingTexture(StagingTe
|
|||
|
||||
void Renderer::RenderText(const std::string& text, int left, int top, u32 color)
|
||||
{
|
||||
u32 backbuffer_width = std::max(GLInterface->GetBackBufferWidth(), 1u);
|
||||
u32 backbuffer_height = std::max(GLInterface->GetBackBufferHeight(), 1u);
|
||||
|
||||
s_raster_font->printMultilineText(text, left * 2.0f / static_cast<float>(backbuffer_width) - 1.0f,
|
||||
1.0f - top * 2.0f / static_cast<float>(backbuffer_height), 0,
|
||||
backbuffer_width, backbuffer_height, color);
|
||||
s_raster_font->printMultilineText(text,
|
||||
left * 2.0f / static_cast<float>(m_backbuffer_width) - 1.0f,
|
||||
1.0f - top * 2.0f / static_cast<float>(m_backbuffer_height), 0,
|
||||
m_backbuffer_width, m_backbuffer_height, color);
|
||||
}
|
||||
|
||||
TargetRectangle Renderer::ConvertEFBRectangle(const EFBRectangle& rc)
|
||||
|
@ -1319,15 +1317,16 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region
|
|||
|
||||
ResetAPIState();
|
||||
|
||||
UpdateDrawRectangle();
|
||||
TargetRectangle flipped_trc = GetTargetRectangle();
|
||||
|
||||
// Flip top and bottom for some reason; TODO: Fix the code to suck less?
|
||||
std::swap(flipped_trc.top, flipped_trc.bottom);
|
||||
|
||||
// Do our OSD callbacks
|
||||
OSD::DoCallbacks(OSD::CallbackType::OnFrame);
|
||||
|
||||
// Check if we need to render to a new surface.
|
||||
CheckForSurfaceChange();
|
||||
CheckForSurfaceResize();
|
||||
UpdateDrawRectangle();
|
||||
TargetRectangle flipped_trc = GetTargetRectangle();
|
||||
std::swap(flipped_trc.top, flipped_trc.bottom);
|
||||
|
||||
// Skip screen rendering when running in headless mode.
|
||||
if (!IsHeadless())
|
||||
{
|
||||
|
@ -1357,32 +1356,7 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region
|
|||
glFlush();
|
||||
}
|
||||
|
||||
#ifdef ANDROID
|
||||
// Handle surface changes on Android.
|
||||
if (m_surface_needs_change.IsSet())
|
||||
{
|
||||
GLInterface->UpdateHandle(m_new_surface_handle);
|
||||
GLInterface->UpdateSurface();
|
||||
m_surface_handle = m_new_surface_handle;
|
||||
m_new_surface_handle = nullptr;
|
||||
m_surface_needs_change.Clear();
|
||||
m_surface_changed.Set();
|
||||
}
|
||||
#endif
|
||||
|
||||
GLInterface->Update();
|
||||
|
||||
// Was the size changed since the last frame?
|
||||
bool window_resized = false;
|
||||
int window_width = static_cast<int>(std::max(GLInterface->GetBackBufferWidth(), 1u));
|
||||
int window_height = static_cast<int>(std::max(GLInterface->GetBackBufferHeight(), 1u));
|
||||
if (window_width != m_backbuffer_width || window_height != m_backbuffer_height)
|
||||
{
|
||||
window_resized = true;
|
||||
m_backbuffer_width = window_width;
|
||||
m_backbuffer_height = window_height;
|
||||
}
|
||||
|
||||
bool target_size_changed = CalculateTargetSize();
|
||||
bool stencil_buffer_enabled =
|
||||
static_cast<FramebufferManager*>(g_framebuffer_manager.get())->HasStencilBuffer();
|
||||
|
@ -1392,10 +1366,6 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region
|
|||
stencil_buffer_enabled != BoundingBox::NeedsStencilBuffer() ||
|
||||
s_last_stereo_mode != (g_ActiveConfig.stereo_mode != StereoMode::Off);
|
||||
|
||||
if (window_resized || fb_needs_update)
|
||||
{
|
||||
UpdateDrawRectangle();
|
||||
}
|
||||
if (fb_needs_update)
|
||||
{
|
||||
s_last_stereo_mode = g_ActiveConfig.stereo_mode != StereoMode::Off;
|
||||
|
@ -1415,6 +1385,7 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region
|
|||
g_framebuffer_manager = std::make_unique<FramebufferManager>(
|
||||
m_target_width, m_target_height, s_MSAASamples, BoundingBox::NeedsStencilBuffer());
|
||||
BoundingBox::SetTargetSizeChanged(m_target_width, m_target_height);
|
||||
UpdateDrawRectangle();
|
||||
}
|
||||
|
||||
if (s_vsync != g_ActiveConfig.IsVSync())
|
||||
|
@ -1455,6 +1426,30 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region
|
|||
ClearEFBCache();
|
||||
}
|
||||
|
||||
void Renderer::CheckForSurfaceChange()
|
||||
{
|
||||
if (!m_surface_changed.TestAndClear())
|
||||
return;
|
||||
|
||||
m_surface_handle = m_new_surface_handle;
|
||||
m_new_surface_handle = nullptr;
|
||||
GLInterface->UpdateHandle(m_surface_handle);
|
||||
GLInterface->UpdateSurface();
|
||||
|
||||
// With a surface change, the window likely has new dimensions.
|
||||
m_backbuffer_width = GLInterface->GetBackBufferWidth();
|
||||
m_backbuffer_height = GLInterface->GetBackBufferHeight();
|
||||
}
|
||||
|
||||
void Renderer::CheckForSurfaceResize()
|
||||
{
|
||||
if (!m_surface_resized.TestAndClear())
|
||||
return;
|
||||
|
||||
m_backbuffer_width = m_new_backbuffer_width;
|
||||
m_backbuffer_height = m_new_backbuffer_height;
|
||||
}
|
||||
|
||||
void Renderer::DrawEFB(GLuint framebuffer, const TargetRectangle& target_rc,
|
||||
const TargetRectangle& source_rc)
|
||||
{
|
||||
|
@ -1572,16 +1567,4 @@ void Renderer::SetInterlacingMode()
|
|||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
void Renderer::ChangeSurface(void* new_surface_handle)
|
||||
{
|
||||
// Win32 polls the window size when redrawing, X11 runs an event loop in another thread.
|
||||
// This is only necessary for Android at this point, although handling resizes here
|
||||
// would be more efficient than polling.
|
||||
#ifdef ANDROID
|
||||
m_new_surface_handle = new_surface_handle;
|
||||
m_surface_needs_change.Set();
|
||||
m_surface_changed.Wait();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
|
|
@ -121,8 +121,6 @@ public:
|
|||
|
||||
void ReinterpretPixelData(unsigned int convtype) override;
|
||||
|
||||
void ChangeSurface(void* new_surface_handle) override;
|
||||
|
||||
private:
|
||||
void UpdateEFBCache(EFBAccessType type, u32 cacheRectIdx, const EFBRectangle& efbPixelRc,
|
||||
const TargetRectangle& targetPixelRc, const void* data);
|
||||
|
@ -133,6 +131,9 @@ private:
|
|||
void BlitScreen(TargetRectangle src, TargetRectangle dst, GLuint src_texture, int src_width,
|
||||
int src_height);
|
||||
|
||||
void CheckForSurfaceChange();
|
||||
void CheckForSurfaceResize();
|
||||
|
||||
std::array<const AbstractTexture*, 8> m_bound_textures{};
|
||||
};
|
||||
}
|
||||
|
|
|
@ -500,6 +500,10 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region
|
|||
StateTracker::GetInstance()->EndRenderPass();
|
||||
StateTracker::GetInstance()->OnEndFrame();
|
||||
|
||||
// Handle host window resizes.
|
||||
CheckForSurfaceChange();
|
||||
CheckForSurfaceResize();
|
||||
|
||||
// There are a few variables which can alter the final window draw rectangle, and some of them
|
||||
// are determined by guest state. Currently, the only way to catch these is to update every frame.
|
||||
UpdateDrawRectangle();
|
||||
|
@ -543,9 +547,6 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region
|
|||
// Determine what (if anything) has changed in the config.
|
||||
CheckForConfigChanges();
|
||||
|
||||
// Handle host window resizes.
|
||||
CheckForSurfaceChange();
|
||||
|
||||
// Clean up stale textures.
|
||||
TextureCache::GetInstance()->Cleanup(frameCount);
|
||||
|
||||
|
@ -650,32 +651,23 @@ void Renderer::BlitScreen(VkRenderPass render_pass, const TargetRectangle& dst_r
|
|||
|
||||
void Renderer::CheckForSurfaceChange()
|
||||
{
|
||||
if (!m_surface_needs_change.IsSet())
|
||||
if (!m_surface_changed.TestAndClear())
|
||||
return;
|
||||
|
||||
// Wait for the GPU to catch up since we're going to destroy the swap chain.
|
||||
m_surface_handle = m_new_surface_handle;
|
||||
m_new_surface_handle = nullptr;
|
||||
|
||||
// Submit the current draws up until rendering the XFB.
|
||||
g_command_buffer_mgr->ExecuteCommandBuffer(false, false);
|
||||
g_command_buffer_mgr->WaitForGPUIdle();
|
||||
|
||||
// Clear the present failed flag, since we don't want to resize after recreating.
|
||||
g_command_buffer_mgr->CheckLastPresentFail();
|
||||
|
||||
// Fast path, if the surface handle is the same, the window has just been resized.
|
||||
if (m_swap_chain && m_new_surface_handle == m_swap_chain->GetNativeHandle())
|
||||
{
|
||||
INFO_LOG(VIDEO, "Detected window resize.");
|
||||
m_swap_chain->RecreateSwapChain();
|
||||
|
||||
// Notify the main thread we are done.
|
||||
m_surface_needs_change.Clear();
|
||||
m_new_surface_handle = nullptr;
|
||||
m_surface_changed.Set();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Did we previously have a swap chain?
|
||||
if (m_swap_chain)
|
||||
{
|
||||
if (!m_new_surface_handle)
|
||||
if (!m_surface_handle)
|
||||
{
|
||||
// If there is no surface now, destroy the swap chain.
|
||||
m_swap_chain.reset();
|
||||
|
@ -683,18 +675,18 @@ void Renderer::CheckForSurfaceChange()
|
|||
else
|
||||
{
|
||||
// Recreate the surface. If this fails we're in trouble.
|
||||
if (!m_swap_chain->RecreateSurface(m_new_surface_handle))
|
||||
if (!m_swap_chain->RecreateSurface(m_surface_handle))
|
||||
PanicAlert("Failed to recreate Vulkan surface. Cannot continue.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Previously had no swap chain. So create one.
|
||||
VkSurfaceKHR surface = SwapChain::CreateVulkanSurface(g_vulkan_context->GetVulkanInstance(),
|
||||
m_new_surface_handle);
|
||||
VkSurfaceKHR surface =
|
||||
SwapChain::CreateVulkanSurface(g_vulkan_context->GetVulkanInstance(), m_surface_handle);
|
||||
if (surface != VK_NULL_HANDLE)
|
||||
{
|
||||
m_swap_chain = SwapChain::Create(m_new_surface_handle, surface, g_ActiveConfig.IsVSync());
|
||||
m_swap_chain = SwapChain::Create(m_surface_handle, surface, g_ActiveConfig.IsVSync());
|
||||
if (!m_swap_chain)
|
||||
PanicAlert("Failed to create swap chain.");
|
||||
}
|
||||
|
@ -704,14 +696,35 @@ void Renderer::CheckForSurfaceChange()
|
|||
}
|
||||
}
|
||||
|
||||
// Notify calling thread.
|
||||
m_surface_needs_change.Clear();
|
||||
m_surface_handle = m_new_surface_handle;
|
||||
m_new_surface_handle = nullptr;
|
||||
m_surface_changed.Set();
|
||||
// Handle case where the dimensions are now different.
|
||||
OnSwapChainResized();
|
||||
}
|
||||
|
||||
// Handle case where the dimensions are now different.
|
||||
void Renderer::CheckForSurfaceResize()
|
||||
{
|
||||
if (!m_surface_resized.TestAndClear())
|
||||
return;
|
||||
|
||||
m_backbuffer_width = m_new_backbuffer_width;
|
||||
m_backbuffer_height = m_new_backbuffer_height;
|
||||
|
||||
// If we don't have a surface, how can we resize the swap chain?
|
||||
// CheckForSurfaceChange should handle this case.
|
||||
if (!m_swap_chain)
|
||||
{
|
||||
WARN_LOG(VIDEO, "Surface resize event received without active surface, ignoring");
|
||||
return;
|
||||
}
|
||||
|
||||
// Wait for the GPU to catch up since we're going to destroy the swap chain.
|
||||
g_command_buffer_mgr->ExecuteCommandBuffer(false, false);
|
||||
g_command_buffer_mgr->WaitForGPUIdle();
|
||||
|
||||
// Clear the present failed flag, since we don't want to resize after recreating.
|
||||
g_command_buffer_mgr->CheckLastPresentFail();
|
||||
|
||||
// Resize the swap chain.
|
||||
m_swap_chain->RecreateSwapChain();
|
||||
OnSwapChainResized();
|
||||
}
|
||||
|
||||
|
@ -782,9 +795,6 @@ void Renderer::OnSwapChainResized()
|
|||
{
|
||||
m_backbuffer_width = m_swap_chain->GetWidth();
|
||||
m_backbuffer_height = m_swap_chain->GetHeight();
|
||||
UpdateDrawRectangle();
|
||||
if (CalculateTargetSize())
|
||||
RecreateEFBFramebuffer();
|
||||
}
|
||||
|
||||
void Renderer::BindEFBToStateTracker()
|
||||
|
@ -914,14 +924,6 @@ void Renderer::SetViewport(float x, float y, float width, float height, float ne
|
|||
StateTracker::GetInstance()->SetViewport(viewport);
|
||||
}
|
||||
|
||||
void Renderer::ChangeSurface(void* new_surface_handle)
|
||||
{
|
||||
// Called by the main thread when the window is resized.
|
||||
m_new_surface_handle = new_surface_handle;
|
||||
m_surface_needs_change.Set();
|
||||
m_surface_changed.Set();
|
||||
}
|
||||
|
||||
void Renderer::RecompileShaders()
|
||||
{
|
||||
DestroyShaders();
|
||||
|
|
|
@ -70,8 +70,6 @@ public:
|
|||
void SetViewport(float x, float y, float width, float height, float near_depth,
|
||||
float far_depth) override;
|
||||
|
||||
void ChangeSurface(void* new_surface_handle) override;
|
||||
|
||||
private:
|
||||
bool CreateSemaphores();
|
||||
void DestroySemaphores();
|
||||
|
@ -79,6 +77,7 @@ private:
|
|||
void BeginFrame();
|
||||
|
||||
void CheckForSurfaceChange();
|
||||
void CheckForSurfaceResize();
|
||||
void CheckForConfigChanges();
|
||||
|
||||
void ResetSamplerStates();
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
#include "VideoBackends/Vulkan/CommandBufferManager.h"
|
||||
#include "VideoBackends/Vulkan/VulkanContext.h"
|
||||
#include "VideoCommon/RenderBase.h"
|
||||
|
||||
#if defined(VK_USE_PLATFORM_XLIB_KHR)
|
||||
#include <X11/Xlib.h>
|
||||
|
@ -261,11 +262,13 @@ bool SwapChain::CreateSwapChain()
|
|||
VkExtent2D size = surface_capabilities.currentExtent;
|
||||
if (size.width == UINT32_MAX)
|
||||
{
|
||||
size.width = std::min(std::max(surface_capabilities.minImageExtent.width, 640u),
|
||||
surface_capabilities.maxImageExtent.width);
|
||||
size.height = std::min(std::max(surface_capabilities.minImageExtent.height, 480u),
|
||||
surface_capabilities.maxImageExtent.height);
|
||||
size.width = std::max(g_renderer->GetBackbufferWidth(), 1);
|
||||
size.height = std::max(g_renderer->GetBackbufferHeight(), 1);
|
||||
}
|
||||
size.width = MathUtil::Clamp(size.width, surface_capabilities.minImageExtent.width,
|
||||
surface_capabilities.maxImageExtent.width);
|
||||
size.height = MathUtil::Clamp(size.height, surface_capabilities.minImageExtent.height,
|
||||
surface_capabilities.maxImageExtent.height);
|
||||
|
||||
// Prefer identity transform if possible
|
||||
VkSurfaceTransformFlagBitsKHR transform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
|
||||
|
@ -468,6 +471,22 @@ bool SwapChain::RecreateSurface(void* native_handle)
|
|||
if (m_surface == VK_NULL_HANDLE)
|
||||
return false;
|
||||
|
||||
// The validation layers get angry at us if we don't call this before creating the swapchain.
|
||||
VkBool32 present_supported = VK_TRUE;
|
||||
VkResult res = vkGetPhysicalDeviceSurfaceSupportKHR(
|
||||
g_vulkan_context->GetPhysicalDevice(), g_vulkan_context->GetPresentQueueFamilyIndex(),
|
||||
m_surface, &present_supported);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
LOG_VULKAN_ERROR(res, "vkGetPhysicalDeviceSurfaceSupportKHR failed: ");
|
||||
return false;
|
||||
}
|
||||
if (!present_supported)
|
||||
{
|
||||
PanicAlert("Recreated surface does not support presenting.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Finally re-create the swap chain
|
||||
if (!CreateSwapChain() || !SetupSwapChainImages() || !CreateRenderPass())
|
||||
return false;
|
||||
|
|
|
@ -400,6 +400,21 @@ bool Renderer::IsHeadless() const
|
|||
return !m_surface_handle;
|
||||
}
|
||||
|
||||
void Renderer::ChangeSurface(void* new_surface_handle)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_swap_mutex);
|
||||
m_new_surface_handle = new_surface_handle;
|
||||
m_surface_changed.Set();
|
||||
}
|
||||
|
||||
void Renderer::ResizeSurface(int new_width, int new_height)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_swap_mutex);
|
||||
m_new_backbuffer_width = new_width;
|
||||
m_new_backbuffer_height = new_height;
|
||||
m_surface_resized.Set();
|
||||
}
|
||||
|
||||
std::tuple<float, float> Renderer::ScaleToDisplayAspectRatio(const int width,
|
||||
const int height) const
|
||||
{
|
||||
|
@ -651,7 +666,10 @@ void Renderer::Swap(u32 xfbAddr, u32 fbWidth, u32 fbStride, u32 fbHeight, const
|
|||
m_last_xfb_region = xfb_rect;
|
||||
|
||||
// TODO: merge more generic parts into VideoCommon
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(m_swap_mutex);
|
||||
g_renderer->SwapImpl(xfb_entry->texture.get(), xfb_rect, ticks, xfb_entry->gamma);
|
||||
}
|
||||
|
||||
// Update the window size based on the frame that was just rendered.
|
||||
// Due to depending on guest state, we need to call this every frame.
|
||||
|
|
|
@ -154,7 +154,8 @@ public:
|
|||
PostProcessingShaderImplementation* GetPostProcessor() const { return m_post_processor.get(); }
|
||||
// Final surface changing
|
||||
// This is called when the surface is resized (WX) or the window changes (Android).
|
||||
virtual void ChangeSurface(void* new_surface_handle) {}
|
||||
void ChangeSurface(void* new_surface_handle);
|
||||
void ResizeSurface(int new_width, int new_height);
|
||||
bool UseVertexDepthRange() const;
|
||||
|
||||
virtual void Shutdown();
|
||||
|
@ -178,9 +179,11 @@ protected:
|
|||
int m_target_width = 0;
|
||||
int m_target_height = 0;
|
||||
|
||||
// TODO: Add functionality to reinit all the render targets when the window is resized.
|
||||
// Backbuffer (window) size and render area
|
||||
int m_backbuffer_width = 0;
|
||||
int m_backbuffer_height = 0;
|
||||
int m_new_backbuffer_width = 0;
|
||||
int m_new_backbuffer_height = 0;
|
||||
TargetRectangle m_target_rectangle = {};
|
||||
|
||||
FPSCounter m_fps_counter;
|
||||
|
@ -189,8 +192,9 @@ protected:
|
|||
|
||||
void* m_surface_handle = nullptr;
|
||||
void* m_new_surface_handle = nullptr;
|
||||
Common::Flag m_surface_needs_change;
|
||||
Common::Event m_surface_changed;
|
||||
Common::Flag m_surface_changed;
|
||||
Common::Flag m_surface_resized;
|
||||
std::mutex m_swap_mutex;
|
||||
|
||||
u32 m_last_host_config_bits = 0;
|
||||
|
||||
|
|
Loading…
Reference in New Issue