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)
|
else if (state == State.PAUSED)
|
||||||
{
|
{
|
||||||
Log.debug("[EmulationFragment] Resuming emulation.");
|
Log.debug("[EmulationFragment] Resuming emulation.");
|
||||||
NativeLibrary.UnPauseEmulation();
|
|
||||||
NativeLibrary.SurfaceChanged(mSurface);
|
NativeLibrary.SurfaceChanged(mSurface);
|
||||||
|
NativeLibrary.UnPauseEmulation();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -321,12 +321,8 @@ class PlatformX11 : public Platform
|
||||||
{
|
{
|
||||||
last_window_width = event.xconfigure.width;
|
last_window_width = event.xconfigure.width;
|
||||||
last_window_height = event.xconfigure.height;
|
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)
|
if (g_renderer)
|
||||||
g_renderer->ChangeSurface(s_window_handle);
|
g_renderer->ResizeSurface(last_window_width, last_window_height);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -51,10 +51,10 @@ void Host::SetRenderFullscreen(bool fullscreen)
|
||||||
m_render_fullscreen = fullscreen;
|
m_render_fullscreen = fullscreen;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Host::UpdateSurface()
|
void Host::ResizeSurface(int new_width, int new_height)
|
||||||
{
|
{
|
||||||
if (g_renderer)
|
if (g_renderer)
|
||||||
g_renderer->ChangeSurface(GetRenderHandle());
|
g_renderer->ResizeSurface(new_width, new_height);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Host_Message(int id)
|
void Host_Message(int id)
|
||||||
|
|
|
@ -26,7 +26,7 @@ public:
|
||||||
void SetRenderHandle(void* handle);
|
void SetRenderHandle(void* handle);
|
||||||
void SetRenderFocus(bool focus);
|
void SetRenderFocus(bool focus);
|
||||||
void SetRenderFullscreen(bool fullscreen);
|
void SetRenderFullscreen(bool fullscreen);
|
||||||
void UpdateSurface();
|
void ResizeSurface(int new_width, int new_height);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void RequestTitle(const QString& title);
|
void RequestTitle(const QString& title);
|
||||||
|
|
|
@ -22,7 +22,7 @@ RenderWidget::RenderWidget(QWidget* parent) : QWidget(parent)
|
||||||
Qt::DirectConnection);
|
Qt::DirectConnection);
|
||||||
connect(this, &RenderWidget::HandleChanged, Host::GetInstance(), &Host::SetRenderHandle,
|
connect(this, &RenderWidget::HandleChanged, Host::GetInstance(), &Host::SetRenderHandle,
|
||||||
Qt::DirectConnection);
|
Qt::DirectConnection);
|
||||||
connect(this, &RenderWidget::SizeChanged, Host::GetInstance(), &Host::UpdateSurface,
|
connect(this, &RenderWidget::SizeChanged, Host::GetInstance(), &Host::ResizeSurface,
|
||||||
Qt::DirectConnection);
|
Qt::DirectConnection);
|
||||||
|
|
||||||
emit HandleChanged((void*)winId());
|
emit HandleChanged((void*)winId());
|
||||||
|
@ -84,8 +84,12 @@ bool RenderWidget::event(QEvent* event)
|
||||||
Host::GetInstance()->SetRenderFocus(false);
|
Host::GetInstance()->SetRenderFocus(false);
|
||||||
break;
|
break;
|
||||||
case QEvent::Resize:
|
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;
|
break;
|
||||||
|
}
|
||||||
case QEvent::WindowStateChange:
|
case QEvent::WindowStateChange:
|
||||||
emit StateChanged(isFullScreen());
|
emit StateChanged(isFullScreen());
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -23,7 +23,7 @@ signals:
|
||||||
void Closed();
|
void Closed();
|
||||||
void HandleChanged(void* handle);
|
void HandleChanged(void* handle);
|
||||||
void StateChanged(bool fullscreen);
|
void StateChanged(bool fullscreen);
|
||||||
void SizeChanged();
|
void SizeChanged(int new_width, int new_height);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void HandleCursorTimer();
|
void HandleCursorTimer();
|
||||||
|
|
|
@ -603,21 +603,18 @@ void CFrame::OnRenderParentResize(wxSizeEvent& event)
|
||||||
if (Core::GetState() != Core::State::Uninitialized)
|
if (Core::GetState() != Core::State::Uninitialized)
|
||||||
{
|
{
|
||||||
int width, height;
|
int width, height;
|
||||||
|
m_render_frame->GetClientSize(&width, &height);
|
||||||
if (!SConfig::GetInstance().bRenderToMain && !RendererIsFullscreen() &&
|
if (!SConfig::GetInstance().bRenderToMain && !RendererIsFullscreen() &&
|
||||||
!m_render_frame->IsMaximized() && !m_render_frame->IsIconized())
|
!m_render_frame->IsMaximized() && !m_render_frame->IsIconized())
|
||||||
{
|
{
|
||||||
m_render_frame->GetClientSize(&width, &height);
|
|
||||||
SConfig::GetInstance().iRenderWindowWidth = width;
|
SConfig::GetInstance().iRenderWindowWidth = width;
|
||||||
SConfig::GetInstance().iRenderWindowHeight = height;
|
SConfig::GetInstance().iRenderWindowHeight = height;
|
||||||
}
|
}
|
||||||
m_log_window->Refresh();
|
m_log_window->Refresh();
|
||||||
m_log_window->Update();
|
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)
|
if (g_renderer)
|
||||||
g_renderer->ChangeSurface(GetRenderHandle());
|
g_renderer->ResizeSurface(width, height);
|
||||||
}
|
}
|
||||||
event.Skip();
|
event.Skip();
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,9 +37,9 @@ namespace D3D
|
||||||
ID3D11Device* device = nullptr;
|
ID3D11Device* device = nullptr;
|
||||||
ID3D11Device1* device1 = nullptr;
|
ID3D11Device1* device1 = nullptr;
|
||||||
ID3D11DeviceContext* context = nullptr;
|
ID3D11DeviceContext* context = nullptr;
|
||||||
HWND hWnd;
|
IDXGISwapChain1* swapchain = nullptr;
|
||||||
|
|
||||||
static IDXGISwapChain1* s_swapchain;
|
static IDXGIFactory2* s_dxgi_factory;
|
||||||
static ID3D11Debug* s_debug;
|
static ID3D11Debug* s_debug;
|
||||||
static D3D_FEATURE_LEVEL s_featlevel;
|
static D3D_FEATURE_LEVEL s_featlevel;
|
||||||
static D3DTexture2D* s_backbuf;
|
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_bgra_textures_supported;
|
||||||
static bool s_allow_tearing_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 UINT NUM_SUPPORTED_FEATURE_LEVELS = 3;
|
||||||
constexpr D3D_FEATURE_LEVEL supported_feature_levels[NUM_SUPPORTED_FEATURE_LEVELS] = {
|
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};
|
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;
|
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)
|
HRESULT Create(HWND wnd)
|
||||||
{
|
{
|
||||||
hWnd = wnd;
|
HRESULT hr = LoadDXGI();
|
||||||
HRESULT hr;
|
|
||||||
|
|
||||||
RECT client;
|
|
||||||
GetClientRect(hWnd, &client);
|
|
||||||
s_xres = client.right - client.left;
|
|
||||||
s_yres = client.bottom - client.top;
|
|
||||||
|
|
||||||
hr = LoadDXGI();
|
|
||||||
if (SUCCEEDED(hr))
|
if (SUCCEEDED(hr))
|
||||||
hr = LoadD3D();
|
hr = LoadD3D();
|
||||||
if (SUCCEEDED(hr))
|
if (SUCCEEDED(hr))
|
||||||
|
@ -270,18 +324,17 @@ HRESULT Create(HWND wnd)
|
||||||
return hr;
|
return hr;
|
||||||
}
|
}
|
||||||
|
|
||||||
IDXGIFactory2* factory;
|
hr = PCreateDXGIFactory(__uuidof(IDXGIFactory2), (void**)&s_dxgi_factory);
|
||||||
hr = PCreateDXGIFactory(__uuidof(IDXGIFactory2), (void**)&factory);
|
|
||||||
if (FAILED(hr))
|
if (FAILED(hr))
|
||||||
MessageBox(wnd, _T("Failed to create IDXGIFactory object"), _T("Dolphin Direct3D 11 backend"),
|
MessageBox(wnd, _T("Failed to create IDXGIFactory object"), _T("Dolphin Direct3D 11 backend"),
|
||||||
MB_OK | MB_ICONERROR);
|
MB_OK | MB_ICONERROR);
|
||||||
|
|
||||||
IDXGIAdapter* adapter;
|
IDXGIAdapter* adapter;
|
||||||
hr = factory->EnumAdapters(g_ActiveConfig.iAdapter, &adapter);
|
hr = s_dxgi_factory->EnumAdapters(g_ActiveConfig.iAdapter, &adapter);
|
||||||
if (FAILED(hr))
|
if (FAILED(hr))
|
||||||
{
|
{
|
||||||
// try using the first one
|
// try using the first one
|
||||||
hr = factory->EnumAdapters(0, &adapter);
|
hr = s_dxgi_factory->EnumAdapters(0, &adapter);
|
||||||
if (FAILED(hr))
|
if (FAILED(hr))
|
||||||
MessageBox(wnd, _T("Failed to enumerate adapters"), _T("Dolphin Direct3D 11 backend"),
|
MessageBox(wnd, _T("Failed to enumerate adapters"), _T("Dolphin Direct3D 11 backend"),
|
||||||
MB_OK | MB_ICONERROR);
|
MB_OK | MB_ICONERROR);
|
||||||
|
@ -301,7 +354,7 @@ HRESULT Create(HWND wnd)
|
||||||
// Check support for allow tearing, we query the interface for backwards compatibility
|
// Check support for allow tearing, we query the interface for backwards compatibility
|
||||||
UINT allow_tearing = FALSE;
|
UINT allow_tearing = FALSE;
|
||||||
IDXGIFactory5* factory5;
|
IDXGIFactory5* factory5;
|
||||||
hr = factory->QueryInterface(&factory5);
|
hr = s_dxgi_factory->QueryInterface(&factory5);
|
||||||
if (SUCCEEDED(hr))
|
if (SUCCEEDED(hr))
|
||||||
{
|
{
|
||||||
hr = factory5->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &allow_tearing,
|
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;
|
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
|
// 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.
|
// version of the DirectX SDK. If it does, simply fallback to a non-debug device.
|
||||||
if (g_Config.bEnableValidationLayer)
|
if (g_Config.bEnableValidationLayer)
|
||||||
|
@ -360,30 +398,9 @@ HRESULT Create(HWND wnd)
|
||||||
D3D11_SDK_VERSION, &device, &s_featlevel, &context);
|
D3D11_SDK_VERSION, &device, &s_featlevel, &context);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (SUCCEEDED(hr))
|
SAFE_RELEASE(adapter);
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (FAILED(hr))
|
if (FAILED(hr) || (wnd && !CreateSwapChain(wnd)))
|
||||||
{
|
|
||||||
// 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))
|
|
||||||
{
|
{
|
||||||
MessageBox(
|
MessageBox(
|
||||||
wnd,
|
wnd,
|
||||||
|
@ -391,7 +408,7 @@ HRESULT Create(HWND wnd)
|
||||||
_T("Dolphin Direct3D 11 backend"), MB_OK | MB_ICONERROR);
|
_T("Dolphin Direct3D 11 backend"), MB_OK | MB_ICONERROR);
|
||||||
SAFE_RELEASE(device);
|
SAFE_RELEASE(device);
|
||||||
SAFE_RELEASE(context);
|
SAFE_RELEASE(context);
|
||||||
SAFE_RELEASE(s_swapchain);
|
SAFE_RELEASE(s_dxgi_factory);
|
||||||
return E_FAIL;
|
return E_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -399,52 +416,23 @@ HRESULT Create(HWND wnd)
|
||||||
if (FAILED(hr))
|
if (FAILED(hr))
|
||||||
WARN_LOG(VIDEO, "Missing Direct3D 11.1 support. Logical operations will not be supported.");
|
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
|
// BGRA textures are easier to deal with in TextureCache, but might not be supported
|
||||||
// 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
|
|
||||||
UINT format_support;
|
UINT format_support;
|
||||||
device->CheckFormatSupport(DXGI_FORMAT_B8G8R8A8_UNORM, &format_support);
|
device->CheckFormatSupport(DXGI_FORMAT_B8G8R8A8_UNORM, &format_support);
|
||||||
s_bgra_textures_supported = (format_support & D3D11_FORMAT_SUPPORT_TEXTURE2D) != 0;
|
s_bgra_textures_supported = (format_support & D3D11_FORMAT_SUPPORT_TEXTURE2D) != 0;
|
||||||
g_Config.backend_info.bSupportsST3CTextures = SupportsS3TCTextures(device);
|
g_Config.backend_info.bSupportsST3CTextures = SupportsS3TCTextures(device);
|
||||||
g_Config.backend_info.bSupportsBPTCTextures = SupportsBPTCTextures(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;
|
stateman = new StateManager;
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
@ -452,12 +440,13 @@ HRESULT Create(HWND wnd)
|
||||||
void Close()
|
void Close()
|
||||||
{
|
{
|
||||||
// we can't release the swapchain while in fullscreen.
|
// we can't release the swapchain while in fullscreen.
|
||||||
s_swapchain->SetFullscreenState(false, nullptr);
|
if (swapchain)
|
||||||
|
swapchain->SetFullscreenState(false, nullptr);
|
||||||
|
|
||||||
// release all bound resources
|
// release all bound resources
|
||||||
context->ClearState();
|
context->ClearState();
|
||||||
SAFE_RELEASE(s_backbuf);
|
SAFE_RELEASE(s_backbuf);
|
||||||
SAFE_RELEASE(s_swapchain);
|
SAFE_RELEASE(swapchain);
|
||||||
SAFE_DELETE(stateman);
|
SAFE_DELETE(stateman);
|
||||||
context->Flush(); // immediately destroy device objects
|
context->Flush(); // immediately destroy device objects
|
||||||
|
|
||||||
|
@ -465,7 +454,6 @@ void Close()
|
||||||
SAFE_RELEASE(device1);
|
SAFE_RELEASE(device1);
|
||||||
ULONG references = device->Release();
|
ULONG references = device->Release();
|
||||||
|
|
||||||
#if defined(_DEBUG) || defined(DEBUGFAST)
|
|
||||||
if (s_debug)
|
if (s_debug)
|
||||||
{
|
{
|
||||||
--references; // the debug interface increases the refcount of the device, subtract that.
|
--references; // the debug interface increases the refcount of the device, subtract that.
|
||||||
|
@ -477,7 +465,6 @@ void Close()
|
||||||
}
|
}
|
||||||
SAFE_RELEASE(s_debug)
|
SAFE_RELEASE(s_debug)
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
if (references)
|
if (references)
|
||||||
{
|
{
|
||||||
|
@ -524,19 +511,10 @@ const char* PixelShaderVersionString()
|
||||||
return "ps_4_0";
|
return "ps_4_0";
|
||||||
}
|
}
|
||||||
|
|
||||||
D3DTexture2D*& GetBackBuffer()
|
D3DTexture2D* GetBackBuffer()
|
||||||
{
|
{
|
||||||
return s_backbuf;
|
return s_backbuf;
|
||||||
}
|
}
|
||||||
unsigned int GetBackBufferWidth()
|
|
||||||
{
|
|
||||||
return s_xres;
|
|
||||||
}
|
|
||||||
unsigned int GetBackBufferHeight()
|
|
||||||
{
|
|
||||||
return s_yres;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool BGRATexturesSupported()
|
bool BGRATexturesSupported()
|
||||||
{
|
{
|
||||||
return s_bgra_textures_supported;
|
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);
|
SAFE_RELEASE(s_backbuf);
|
||||||
|
|
||||||
UINT swap_chain_flags = AllowTearingSupported() ? DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING : 0;
|
if (swapchain)
|
||||||
|
|
||||||
// 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))
|
|
||||||
{
|
{
|
||||||
MessageBox(hWnd, _T("Failed to get swapchain buffer"), _T("Dolphin Direct3D 11 backend"),
|
if (GetFullscreenState())
|
||||||
MB_OK | MB_ICONERROR);
|
swapchain->SetFullscreenState(FALSE, nullptr);
|
||||||
SAFE_RELEASE(device);
|
SAFE_RELEASE(swapchain);
|
||||||
SAFE_RELEASE(context);
|
}
|
||||||
SAFE_RELEASE(s_swapchain);
|
|
||||||
return;
|
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()
|
void Present()
|
||||||
|
@ -616,25 +588,24 @@ void Present()
|
||||||
if (AllowTearingSupported() && !g_ActiveConfig.IsVSync() && !GetFullscreenState())
|
if (AllowTearingSupported() && !g_ActiveConfig.IsVSync() && !GetFullscreenState())
|
||||||
present_flags |= DXGI_PRESENT_ALLOW_TEARING;
|
present_flags |= DXGI_PRESENT_ALLOW_TEARING;
|
||||||
|
|
||||||
if (s_swapchain->IsTemporaryMonoSupported() &&
|
if (swapchain->IsTemporaryMonoSupported() && g_ActiveConfig.stereo_mode != StereoMode::QuadBuffer)
|
||||||
g_ActiveConfig.stereo_mode != StereoMode::QuadBuffer)
|
|
||||||
{
|
{
|
||||||
present_flags |= DXGI_PRESENT_STEREO_TEMPORARY_MONO;
|
present_flags |= DXGI_PRESENT_STEREO_TEMPORARY_MONO;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Is 1 the correct value for vsyncing?
|
// 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)
|
HRESULT SetFullscreenState(bool enable_fullscreen)
|
||||||
{
|
{
|
||||||
return s_swapchain->SetFullscreenState(enable_fullscreen, nullptr);
|
return swapchain->SetFullscreenState(enable_fullscreen, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GetFullscreenState()
|
bool GetFullscreenState()
|
||||||
{
|
{
|
||||||
BOOL state = FALSE;
|
BOOL state = FALSE;
|
||||||
s_swapchain->GetFullscreenState(&state, nullptr);
|
swapchain->GetFullscreenState(&state, nullptr);
|
||||||
return !!state;
|
return !!state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -59,14 +59,13 @@ void Close();
|
||||||
extern ID3D11Device* device;
|
extern ID3D11Device* device;
|
||||||
extern ID3D11Device1* device1;
|
extern ID3D11Device1* device1;
|
||||||
extern ID3D11DeviceContext* context;
|
extern ID3D11DeviceContext* context;
|
||||||
extern HWND hWnd;
|
extern IDXGISwapChain1* swapchain;
|
||||||
|
|
||||||
void Reset();
|
void Reset(HWND new_wnd);
|
||||||
|
void ResizeSwapChain();
|
||||||
void Present();
|
void Present();
|
||||||
|
|
||||||
unsigned int GetBackBufferWidth();
|
D3DTexture2D* GetBackBuffer();
|
||||||
unsigned int GetBackBufferHeight();
|
|
||||||
D3DTexture2D*& GetBackBuffer();
|
|
||||||
const char* PixelShaderVersionString();
|
const char* PixelShaderVersionString();
|
||||||
const char* GeometryShaderVersionString();
|
const char* GeometryShaderVersionString();
|
||||||
const char* VertexShaderVersionString();
|
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 stride = sizeof(FONT2DVERTEX);
|
||||||
UINT bufoffset = 0;
|
UINT bufoffset = 0;
|
||||||
|
|
||||||
float scalex = 1 / (float)D3D::GetBackBufferWidth() * 2.f;
|
float scalex = 1.0f / g_renderer->GetBackbufferWidth() * 2.f;
|
||||||
float scaley = 1 / (float)D3D::GetBackBufferHeight() * 2.f;
|
float scaley = 1.0f / g_renderer->GetBackbufferHeight() * 2.f;
|
||||||
float sizeratio = size / (float)m_LineHeight;
|
float sizeratio = size / m_LineHeight;
|
||||||
|
|
||||||
// translate starting positions
|
// translate starting positions
|
||||||
float sx = x * scalex - 1.f;
|
float sx = x * scalex - 1.f;
|
||||||
|
|
|
@ -53,12 +53,12 @@ typedef struct _Nv_Stereo_Image_Header
|
||||||
|
|
||||||
#define NVSTEREO_IMAGE_SIGNATURE 0x4433564e
|
#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_multisamples = g_ActiveConfig.iMultisamples;
|
||||||
m_last_stereo_mode = g_ActiveConfig.stereo_mode != StereoMode::Off;
|
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);
|
g_framebuffer_manager = std::make_unique<FramebufferManager>(m_target_width, m_target_height);
|
||||||
SetupDeviceObjects();
|
SetupDeviceObjects();
|
||||||
|
|
||||||
|
@ -239,25 +239,6 @@ TargetRectangle Renderer::ConvertEFBRectangle(const EFBRectangle& rc)
|
||||||
return result;
|
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)
|
void Renderer::SetScissorRect(const MathUtil::Rectangle<int>& rc)
|
||||||
{
|
{
|
||||||
const RECT rect = {rc.left, rc.top, rc.right, rc.bottom};
|
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();
|
ResetAPIState();
|
||||||
|
|
||||||
// Prepare to copy the XFBs to our backbuffer
|
// Prepare to copy the XFBs to our backbuffer
|
||||||
|
CheckForSurfaceChange();
|
||||||
|
CheckForSurfaceResize();
|
||||||
UpdateDrawRectangle();
|
UpdateDrawRectangle();
|
||||||
|
|
||||||
TargetRectangle targetRc = GetTargetRectangle();
|
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);
|
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());
|
D3D::context->ClearRenderTargetView(D3D::GetBackBuffer()->GetRTV(), clear_color.data());
|
||||||
|
|
||||||
// activate linear filtering for the buffer copies
|
// 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);
|
xfb_texture->GetConfig().width, xfb_texture->GetConfig().height, Gamma);
|
||||||
|
|
||||||
// Reset viewport for drawing text
|
// Reset viewport for drawing text
|
||||||
D3D11_VIEWPORT vp =
|
D3D11_VIEWPORT vp = CD3D11_VIEWPORT(0.0f, 0.0f, static_cast<float>(m_backbuffer_width),
|
||||||
CD3D11_VIEWPORT(0.0f, 0.0f, (float)GetBackbufferWidth(), (float)GetBackbufferHeight());
|
static_cast<float>(m_backbuffer_height));
|
||||||
D3D::context->RSSetViewports(1, &vp);
|
D3D::context->RSSetViewports(1, &vp);
|
||||||
|
|
||||||
Renderer::DrawDebugText();
|
Renderer::DrawDebugText();
|
||||||
|
@ -580,41 +562,21 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region
|
||||||
g_texture_cache->OnConfigChanged(g_ActiveConfig);
|
g_texture_cache->OnConfigChanged(g_ActiveConfig);
|
||||||
VertexShaderCache::RetreiveAsyncShaders();
|
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
|
// Flip/present backbuffer to frontbuffer here
|
||||||
D3D::Present();
|
if (D3D::swapchain)
|
||||||
|
D3D::Present();
|
||||||
|
|
||||||
// Resize the back buffers NOW to avoid flickering
|
// Resize the back buffers NOW to avoid flickering
|
||||||
if (CalculateTargetSize() || window_resized || fs_changed ||
|
if (CalculateTargetSize() || m_last_multisamples != g_ActiveConfig.iMultisamples ||
|
||||||
m_last_multisamples != g_ActiveConfig.iMultisamples ||
|
|
||||||
m_last_stereo_mode != (g_ActiveConfig.stereo_mode != StereoMode::Off))
|
m_last_stereo_mode != (g_ActiveConfig.stereo_mode != StereoMode::Off))
|
||||||
{
|
{
|
||||||
m_last_multisamples = g_ActiveConfig.iMultisamples;
|
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;
|
m_last_stereo_mode = g_ActiveConfig.stereo_mode != StereoMode::Off;
|
||||||
|
PixelShaderCache::InvalidateMSAAShaders();
|
||||||
D3D::context->OMSetRenderTargets(1, &D3D::GetBackBuffer()->GetRTV(), nullptr);
|
UpdateDrawRectangle();
|
||||||
|
|
||||||
g_framebuffer_manager.reset();
|
g_framebuffer_manager.reset();
|
||||||
g_framebuffer_manager = std::make_unique<FramebufferManager>(m_target_width, m_target_height);
|
g_framebuffer_manager = std::make_unique<FramebufferManager>(m_target_width, m_target_height);
|
||||||
|
|
||||||
D3D::context->ClearRenderTargetView(FramebufferManager::GetEFBColorTexture()->GetRTV(),
|
D3D::context->ClearRenderTargetView(FramebufferManager::GetEFBColorTexture()->GetRTV(),
|
||||||
clear_color.data());
|
clear_color.data());
|
||||||
D3D::context->ClearDepthStencilView(FramebufferManager::GetEFBDepthTexture()->GetDSV(),
|
D3D::context->ClearDepthStencilView(FramebufferManager::GetEFBDepthTexture()->GetDSV(),
|
||||||
|
@ -633,6 +595,54 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region
|
||||||
FramebufferManager::BindEFBRenderTarget();
|
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
|
// ALWAYS call RestoreAPIState for each ResetAPIState call you're doing
|
||||||
void Renderer::ResetAPIState()
|
void Renderer::ResetAPIState()
|
||||||
{
|
{
|
||||||
|
|
|
@ -18,7 +18,7 @@ class D3DTexture2D;
|
||||||
class Renderer : public ::Renderer
|
class Renderer : public ::Renderer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Renderer();
|
Renderer(int backbuffer_width, int backbuffer_height);
|
||||||
~Renderer() override;
|
~Renderer() override;
|
||||||
|
|
||||||
StateCache& GetStateCache() { return m_state_cache; }
|
StateCache& GetStateCache() { return m_state_cache; }
|
||||||
|
@ -63,8 +63,6 @@ public:
|
||||||
|
|
||||||
void ReinterpretPixelData(unsigned int convtype) override;
|
void ReinterpretPixelData(unsigned int convtype) override;
|
||||||
|
|
||||||
bool CheckForResize();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct GXPipelineState
|
struct GXPipelineState
|
||||||
{
|
{
|
||||||
|
@ -77,6 +75,9 @@ private:
|
||||||
void SetupDeviceObjects();
|
void SetupDeviceObjects();
|
||||||
void TeardownDeviceObjects();
|
void TeardownDeviceObjects();
|
||||||
void Create3DVisionTexture(int width, int height);
|
void Create3DVisionTexture(int width, int height);
|
||||||
|
void CheckForSurfaceChange();
|
||||||
|
void CheckForSurfaceResize();
|
||||||
|
void UpdateBackbufferSize();
|
||||||
|
|
||||||
void BlitScreen(TargetRectangle src, TargetRectangle dst, D3DTexture2D* src_texture,
|
void BlitScreen(TargetRectangle src, TargetRectangle dst, D3DTexture2D* src_texture,
|
||||||
u32 src_width, u32 src_height, float Gamma);
|
u32 src_width, u32 src_height, float Gamma);
|
||||||
|
@ -95,6 +96,6 @@ private:
|
||||||
|
|
||||||
u32 m_last_multisamples = 1;
|
u32 m_last_multisamples = 1;
|
||||||
bool m_last_stereo_mode = false;
|
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;
|
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_texture_cache = std::make_unique<TextureCache>();
|
||||||
g_vertex_manager = std::make_unique<VertexManager>();
|
g_vertex_manager = std::make_unique<VertexManager>();
|
||||||
g_perf_query = std::make_unique<PerfQuery>();
|
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)
|
void Renderer::RenderText(const std::string& text, int left, int top, u32 color)
|
||||||
{
|
{
|
||||||
u32 backbuffer_width = std::max(GLInterface->GetBackBufferWidth(), 1u);
|
s_raster_font->printMultilineText(text,
|
||||||
u32 backbuffer_height = std::max(GLInterface->GetBackBufferHeight(), 1u);
|
left * 2.0f / static_cast<float>(m_backbuffer_width) - 1.0f,
|
||||||
|
1.0f - top * 2.0f / static_cast<float>(m_backbuffer_height), 0,
|
||||||
s_raster_font->printMultilineText(text, left * 2.0f / static_cast<float>(backbuffer_width) - 1.0f,
|
m_backbuffer_width, m_backbuffer_height, color);
|
||||||
1.0f - top * 2.0f / static_cast<float>(backbuffer_height), 0,
|
|
||||||
backbuffer_width, backbuffer_height, color);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TargetRectangle Renderer::ConvertEFBRectangle(const EFBRectangle& rc)
|
TargetRectangle Renderer::ConvertEFBRectangle(const EFBRectangle& rc)
|
||||||
|
@ -1319,33 +1317,36 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region
|
||||||
|
|
||||||
ResetAPIState();
|
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
|
// Do our OSD callbacks
|
||||||
OSD::DoCallbacks(OSD::CallbackType::OnFrame);
|
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.
|
// Skip screen rendering when running in headless mode.
|
||||||
if (!IsHeadless())
|
if (!IsHeadless())
|
||||||
{
|
{
|
||||||
// Copy the framebuffer to screen.
|
// Clear the framebuffer before drawing anything.
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
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(),
|
BlitScreen(sourceRc, flipped_trc, xfb_texture->GetRawTexIdentifier(),
|
||||||
xfb_texture->GetConfig().width, xfb_texture->GetConfig().height);
|
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);
|
glEnable(GL_BLEND);
|
||||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||||
|
|
||||||
// Reset viewport for drawing text
|
|
||||||
glViewport(0, 0, GLInterface->GetBackBufferWidth(), GLInterface->GetBackBufferHeight());
|
|
||||||
DrawDebugText();
|
DrawDebugText();
|
||||||
OSD::DrawMessages();
|
OSD::DrawMessages();
|
||||||
|
|
||||||
// Copy the rendered frame to the real window.
|
// Swap the back and front buffers, presenting the image.
|
||||||
GLInterface->Swap();
|
GLInterface->Swap();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -1355,32 +1356,7 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region
|
||||||
glFlush();
|
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?
|
// 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 target_size_changed = CalculateTargetSize();
|
||||||
bool stencil_buffer_enabled =
|
bool stencil_buffer_enabled =
|
||||||
static_cast<FramebufferManager*>(g_framebuffer_manager.get())->HasStencilBuffer();
|
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() ||
|
stencil_buffer_enabled != BoundingBox::NeedsStencilBuffer() ||
|
||||||
s_last_stereo_mode != (g_ActiveConfig.stereo_mode != StereoMode::Off);
|
s_last_stereo_mode != (g_ActiveConfig.stereo_mode != StereoMode::Off);
|
||||||
|
|
||||||
if (window_resized || fb_needs_update)
|
|
||||||
{
|
|
||||||
UpdateDrawRectangle();
|
|
||||||
}
|
|
||||||
if (fb_needs_update)
|
if (fb_needs_update)
|
||||||
{
|
{
|
||||||
s_last_stereo_mode = g_ActiveConfig.stereo_mode != StereoMode::Off;
|
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>(
|
g_framebuffer_manager = std::make_unique<FramebufferManager>(
|
||||||
m_target_width, m_target_height, s_MSAASamples, BoundingBox::NeedsStencilBuffer());
|
m_target_width, m_target_height, s_MSAASamples, BoundingBox::NeedsStencilBuffer());
|
||||||
BoundingBox::SetTargetSizeChanged(m_target_width, m_target_height);
|
BoundingBox::SetTargetSizeChanged(m_target_width, m_target_height);
|
||||||
}
|
UpdateDrawRectangle();
|
||||||
|
|
||||||
// Clear framebuffer
|
|
||||||
if (!IsHeadless())
|
|
||||||
{
|
|
||||||
glClearColor(0, 0, 0, 0);
|
|
||||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (s_vsync != g_ActiveConfig.IsVSync())
|
if (s_vsync != g_ActiveConfig.IsVSync())
|
||||||
|
@ -1460,6 +1426,30 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region
|
||||||
ClearEFBCache();
|
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,
|
void Renderer::DrawEFB(GLuint framebuffer, const TargetRectangle& target_rc,
|
||||||
const TargetRectangle& source_rc)
|
const TargetRectangle& source_rc)
|
||||||
{
|
{
|
||||||
|
@ -1577,16 +1567,4 @@ void Renderer::SetInterlacingMode()
|
||||||
{
|
{
|
||||||
// TODO
|
// 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 ReinterpretPixelData(unsigned int convtype) override;
|
||||||
|
|
||||||
void ChangeSurface(void* new_surface_handle) override;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void UpdateEFBCache(EFBAccessType type, u32 cacheRectIdx, const EFBRectangle& efbPixelRc,
|
void UpdateEFBCache(EFBAccessType type, u32 cacheRectIdx, const EFBRectangle& efbPixelRc,
|
||||||
const TargetRectangle& targetPixelRc, const void* data);
|
const TargetRectangle& targetPixelRc, const void* data);
|
||||||
|
@ -133,6 +131,9 @@ private:
|
||||||
void BlitScreen(TargetRectangle src, TargetRectangle dst, GLuint src_texture, int src_width,
|
void BlitScreen(TargetRectangle src, TargetRectangle dst, GLuint src_texture, int src_width,
|
||||||
int src_height);
|
int src_height);
|
||||||
|
|
||||||
|
void CheckForSurfaceChange();
|
||||||
|
void CheckForSurfaceResize();
|
||||||
|
|
||||||
std::array<const AbstractTexture*, 8> m_bound_textures{};
|
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()->EndRenderPass();
|
||||||
StateTracker::GetInstance()->OnEndFrame();
|
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
|
// 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.
|
// are determined by guest state. Currently, the only way to catch these is to update every frame.
|
||||||
UpdateDrawRectangle();
|
UpdateDrawRectangle();
|
||||||
|
@ -543,9 +547,6 @@ void Renderer::SwapImpl(AbstractTexture* texture, const EFBRectangle& xfb_region
|
||||||
// Determine what (if anything) has changed in the config.
|
// Determine what (if anything) has changed in the config.
|
||||||
CheckForConfigChanges();
|
CheckForConfigChanges();
|
||||||
|
|
||||||
// Handle host window resizes.
|
|
||||||
CheckForSurfaceChange();
|
|
||||||
|
|
||||||
// Clean up stale textures.
|
// Clean up stale textures.
|
||||||
TextureCache::GetInstance()->Cleanup(frameCount);
|
TextureCache::GetInstance()->Cleanup(frameCount);
|
||||||
|
|
||||||
|
@ -650,71 +651,83 @@ void Renderer::BlitScreen(VkRenderPass render_pass, const TargetRectangle& dst_r
|
||||||
|
|
||||||
void Renderer::CheckForSurfaceChange()
|
void Renderer::CheckForSurfaceChange()
|
||||||
{
|
{
|
||||||
if (!m_surface_needs_change.IsSet())
|
if (!m_surface_changed.TestAndClear())
|
||||||
return;
|
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();
|
g_command_buffer_mgr->WaitForGPUIdle();
|
||||||
|
|
||||||
// Clear the present failed flag, since we don't want to resize after recreating.
|
// Clear the present failed flag, since we don't want to resize after recreating.
|
||||||
g_command_buffer_mgr->CheckLastPresentFail();
|
g_command_buffer_mgr->CheckLastPresentFail();
|
||||||
|
|
||||||
// Fast path, if the surface handle is the same, the window has just been resized.
|
// Did we previously have a swap chain?
|
||||||
if (m_swap_chain && m_new_surface_handle == m_swap_chain->GetNativeHandle())
|
if (m_swap_chain)
|
||||||
{
|
{
|
||||||
INFO_LOG(VIDEO, "Detected window resize.");
|
if (!m_surface_handle)
|
||||||
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 there is no surface now, destroy the swap chain.
|
||||||
{
|
m_swap_chain.reset();
|
||||||
// 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.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Previously had no swap chain. So create one.
|
// Recreate the surface. If this fails we're in trouble.
|
||||||
VkSurfaceKHR surface = SwapChain::CreateVulkanSurface(g_vulkan_context->GetVulkanInstance(),
|
if (!m_swap_chain->RecreateSurface(m_surface_handle))
|
||||||
m_new_surface_handle);
|
PanicAlert("Failed to recreate Vulkan surface. Cannot continue.");
|
||||||
if (surface != VK_NULL_HANDLE)
|
}
|
||||||
{
|
}
|
||||||
m_swap_chain = SwapChain::Create(m_new_surface_handle, surface, g_ActiveConfig.IsVSync());
|
else
|
||||||
if (!m_swap_chain)
|
{
|
||||||
PanicAlert("Failed to create swap chain.");
|
// Previously had no swap chain. So create one.
|
||||||
}
|
VkSurfaceKHR surface =
|
||||||
else
|
SwapChain::CreateVulkanSurface(g_vulkan_context->GetVulkanInstance(), m_surface_handle);
|
||||||
{
|
if (surface != VK_NULL_HANDLE)
|
||||||
PanicAlert("Failed to create surface.");
|
{
|
||||||
}
|
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.
|
// Handle case where the dimensions are now different.
|
||||||
OnSwapChainResized();
|
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()
|
void Renderer::CheckForConfigChanges()
|
||||||
{
|
{
|
||||||
// Save the video config so we can compare against to determine which settings have changed.
|
// 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_width = m_swap_chain->GetWidth();
|
||||||
m_backbuffer_height = m_swap_chain->GetHeight();
|
m_backbuffer_height = m_swap_chain->GetHeight();
|
||||||
UpdateDrawRectangle();
|
|
||||||
if (CalculateTargetSize())
|
|
||||||
RecreateEFBFramebuffer();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Renderer::BindEFBToStateTracker()
|
void Renderer::BindEFBToStateTracker()
|
||||||
|
@ -914,14 +924,6 @@ void Renderer::SetViewport(float x, float y, float width, float height, float ne
|
||||||
StateTracker::GetInstance()->SetViewport(viewport);
|
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()
|
void Renderer::RecompileShaders()
|
||||||
{
|
{
|
||||||
DestroyShaders();
|
DestroyShaders();
|
||||||
|
|
|
@ -70,8 +70,6 @@ public:
|
||||||
void SetViewport(float x, float y, float width, float height, float near_depth,
|
void SetViewport(float x, float y, float width, float height, float near_depth,
|
||||||
float far_depth) override;
|
float far_depth) override;
|
||||||
|
|
||||||
void ChangeSurface(void* new_surface_handle) override;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool CreateSemaphores();
|
bool CreateSemaphores();
|
||||||
void DestroySemaphores();
|
void DestroySemaphores();
|
||||||
|
@ -79,6 +77,7 @@ private:
|
||||||
void BeginFrame();
|
void BeginFrame();
|
||||||
|
|
||||||
void CheckForSurfaceChange();
|
void CheckForSurfaceChange();
|
||||||
|
void CheckForSurfaceResize();
|
||||||
void CheckForConfigChanges();
|
void CheckForConfigChanges();
|
||||||
|
|
||||||
void ResetSamplerStates();
|
void ResetSamplerStates();
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
|
|
||||||
#include "VideoBackends/Vulkan/CommandBufferManager.h"
|
#include "VideoBackends/Vulkan/CommandBufferManager.h"
|
||||||
#include "VideoBackends/Vulkan/VulkanContext.h"
|
#include "VideoBackends/Vulkan/VulkanContext.h"
|
||||||
|
#include "VideoCommon/RenderBase.h"
|
||||||
|
|
||||||
#if defined(VK_USE_PLATFORM_XLIB_KHR)
|
#if defined(VK_USE_PLATFORM_XLIB_KHR)
|
||||||
#include <X11/Xlib.h>
|
#include <X11/Xlib.h>
|
||||||
|
@ -261,11 +262,13 @@ bool SwapChain::CreateSwapChain()
|
||||||
VkExtent2D size = surface_capabilities.currentExtent;
|
VkExtent2D size = surface_capabilities.currentExtent;
|
||||||
if (size.width == UINT32_MAX)
|
if (size.width == UINT32_MAX)
|
||||||
{
|
{
|
||||||
size.width = std::min(std::max(surface_capabilities.minImageExtent.width, 640u),
|
size.width = std::max(g_renderer->GetBackbufferWidth(), 1);
|
||||||
surface_capabilities.maxImageExtent.width);
|
size.height = std::max(g_renderer->GetBackbufferHeight(), 1);
|
||||||
size.height = std::min(std::max(surface_capabilities.minImageExtent.height, 480u),
|
|
||||||
surface_capabilities.maxImageExtent.height);
|
|
||||||
}
|
}
|
||||||
|
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
|
// Prefer identity transform if possible
|
||||||
VkSurfaceTransformFlagBitsKHR transform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
|
VkSurfaceTransformFlagBitsKHR transform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
|
||||||
|
@ -468,6 +471,22 @@ bool SwapChain::RecreateSurface(void* native_handle)
|
||||||
if (m_surface == VK_NULL_HANDLE)
|
if (m_surface == VK_NULL_HANDLE)
|
||||||
return false;
|
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
|
// Finally re-create the swap chain
|
||||||
if (!CreateSwapChain() || !SetupSwapChainImages() || !CreateRenderPass())
|
if (!CreateSwapChain() || !SetupSwapChainImages() || !CreateRenderPass())
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -400,6 +400,21 @@ bool Renderer::IsHeadless() const
|
||||||
return !m_surface_handle;
|
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,
|
std::tuple<float, float> Renderer::ScaleToDisplayAspectRatio(const int width,
|
||||||
const int height) const
|
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;
|
m_last_xfb_region = xfb_rect;
|
||||||
|
|
||||||
// TODO: merge more generic parts into VideoCommon
|
// 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.
|
// 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.
|
// 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(); }
|
PostProcessingShaderImplementation* GetPostProcessor() const { return m_post_processor.get(); }
|
||||||
// Final surface changing
|
// Final surface changing
|
||||||
// This is called when the surface is resized (WX) or the window changes (Android).
|
// 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;
|
bool UseVertexDepthRange() const;
|
||||||
|
|
||||||
virtual void Shutdown();
|
virtual void Shutdown();
|
||||||
|
@ -178,9 +179,11 @@ protected:
|
||||||
int m_target_width = 0;
|
int m_target_width = 0;
|
||||||
int m_target_height = 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_width = 0;
|
||||||
int m_backbuffer_height = 0;
|
int m_backbuffer_height = 0;
|
||||||
|
int m_new_backbuffer_width = 0;
|
||||||
|
int m_new_backbuffer_height = 0;
|
||||||
TargetRectangle m_target_rectangle = {};
|
TargetRectangle m_target_rectangle = {};
|
||||||
|
|
||||||
FPSCounter m_fps_counter;
|
FPSCounter m_fps_counter;
|
||||||
|
@ -189,8 +192,9 @@ protected:
|
||||||
|
|
||||||
void* m_surface_handle = nullptr;
|
void* m_surface_handle = nullptr;
|
||||||
void* m_new_surface_handle = nullptr;
|
void* m_new_surface_handle = nullptr;
|
||||||
Common::Flag m_surface_needs_change;
|
Common::Flag m_surface_changed;
|
||||||
Common::Event m_surface_changed;
|
Common::Flag m_surface_resized;
|
||||||
|
std::mutex m_swap_mutex;
|
||||||
|
|
||||||
u32 m_last_host_config_bits = 0;
|
u32 m_last_host_config_bits = 0;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue