Merge pull request #6330 from stenzek/resizing
Improve handling of surface change/resize events in graphics backends
This commit is contained in:
commit
93c502fef8
|
@ -420,8 +420,8 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C
|
|||
else if (state == State.PAUSED)
|
||||
{
|
||||
Log.debug("[EmulationFragment] Resuming emulation.");
|
||||
NativeLibrary.UnPauseEmulation();
|
||||
NativeLibrary.SurfaceChanged(mSurface);
|
||||
NativeLibrary.UnPauseEmulation();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -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
|
||||
D3D::Present();
|
||||
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,33 +1317,36 @@ 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())
|
||||
{
|
||||
// Copy the framebuffer to screen.
|
||||
// Clear the framebuffer before drawing anything.
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
glClearColor(0, 0, 0, 0);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
// Copy the framebuffer to screen.
|
||||
BlitScreen(sourceRc, flipped_trc, xfb_texture->GetRawTexIdentifier(),
|
||||
xfb_texture->GetConfig().width, xfb_texture->GetConfig().height);
|
||||
|
||||
// Finish up the current frame, print some stats
|
||||
// Render OSD messages.
|
||||
glViewport(0, 0, m_backbuffer_width, m_backbuffer_height);
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
// Reset viewport for drawing text
|
||||
glViewport(0, 0, GLInterface->GetBackBufferWidth(), GLInterface->GetBackBufferHeight());
|
||||
DrawDebugText();
|
||||
OSD::DrawMessages();
|
||||
|
||||
// Copy the rendered frame to the real window.
|
||||
// Swap the back and front buffers, presenting the image.
|
||||
GLInterface->Swap();
|
||||
}
|
||||
else
|
||||
|
@ -1355,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();
|
||||
|
@ -1390,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;
|
||||
|
@ -1413,13 +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);
|
||||
}
|
||||
|
||||
// Clear framebuffer
|
||||
if (!IsHeadless())
|
||||
{
|
||||
glClearColor(0, 0, 0, 0);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
UpdateDrawRectangle();
|
||||
}
|
||||
|
||||
if (s_vsync != g_ActiveConfig.IsVSync())
|
||||
|
@ -1460,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)
|
||||
{
|
||||
|
@ -1577,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,71 +651,83 @@ 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())
|
||||
// Did we previously have a swap chain?
|
||||
if (m_swap_chain)
|
||||
{
|
||||
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_surface_handle)
|
||||
{
|
||||
if (!m_new_surface_handle)
|
||||
{
|
||||
// If there is no surface now, destroy the swap chain.
|
||||
m_swap_chain.reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Recreate the surface. If this fails we're in trouble.
|
||||
if (!m_swap_chain->RecreateSurface(m_new_surface_handle))
|
||||
PanicAlert("Failed to recreate Vulkan surface. Cannot continue.");
|
||||
}
|
||||
// If there is no surface now, destroy the swap chain.
|
||||
m_swap_chain.reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Previously had no swap chain. So create one.
|
||||
VkSurfaceKHR surface = SwapChain::CreateVulkanSurface(g_vulkan_context->GetVulkanInstance(),
|
||||
m_new_surface_handle);
|
||||
if (surface != VK_NULL_HANDLE)
|
||||
{
|
||||
m_swap_chain = SwapChain::Create(m_new_surface_handle, surface, g_ActiveConfig.IsVSync());
|
||||
if (!m_swap_chain)
|
||||
PanicAlert("Failed to create swap chain.");
|
||||
}
|
||||
else
|
||||
{
|
||||
PanicAlert("Failed to create surface.");
|
||||
}
|
||||
// Recreate the surface. If this fails we're in trouble.
|
||||
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_surface_handle);
|
||||
if (surface != VK_NULL_HANDLE)
|
||||
{
|
||||
m_swap_chain = SwapChain::Create(m_surface_handle, surface, g_ActiveConfig.IsVSync());
|
||||
if (!m_swap_chain)
|
||||
PanicAlert("Failed to create swap chain.");
|
||||
}
|
||||
else
|
||||
{
|
||||
PanicAlert("Failed to create surface.");
|
||||
}
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
void Renderer::CheckForConfigChanges()
|
||||
{
|
||||
// Save the video config so we can compare against to determine which settings have changed.
|
||||
|
@ -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
|
||||
g_renderer->SwapImpl(xfb_entry->texture.get(), xfb_rect, ticks, xfb_entry->gamma);
|
||||
{
|
||||
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