1
0
mirror of https://github.com/doitsujin/dxvk.git synced 2025-03-01 19:29:16 +01:00

[d3d9] Implement rudimentary device loss

This commit is contained in:
Robin Kertels 2023-03-27 17:46:41 +02:00 committed by Joshie
parent b5f43063b1
commit b4366db398
7 changed files with 114 additions and 19 deletions

View File

@ -211,8 +211,16 @@ namespace dxvk {
HRESULT STDMETHODCALLTYPE D3D9DeviceEx::TestCooperativeLevel() { HRESULT STDMETHODCALLTYPE D3D9DeviceEx::TestCooperativeLevel() {
D3D9DeviceLock lock = LockDevice();
// Equivelant of D3D11/DXGI present tests. We can always present. // Equivelant of D3D11/DXGI present tests. We can always present.
if (likely(m_deviceLostState == D3D9DeviceLostState::Ok)) {
return D3D_OK; return D3D_OK;
} else if (m_deviceLostState == D3D9DeviceLostState::NotReset) {
return D3DERR_DEVICENOTRESET;
} else {
return D3DERR_DEVICELOST;
}
} }
@ -382,13 +390,14 @@ namespace dxvk {
HRESULT STDMETHODCALLTYPE D3D9DeviceEx::Reset(D3DPRESENT_PARAMETERS* pPresentationParameters) { HRESULT STDMETHODCALLTYPE D3D9DeviceEx::Reset(D3DPRESENT_PARAMETERS* pPresentationParameters) {
D3D9DeviceLock lock = LockDevice(); D3D9DeviceLock lock = LockDevice();
Logger::info("Device reset");
m_deviceLostState = D3D9DeviceLostState::Ok;
HRESULT hr = ResetSwapChain(pPresentationParameters, nullptr); HRESULT hr = ResetSwapChain(pPresentationParameters, nullptr);
if (FAILED(hr)) if (FAILED(hr))
return hr; return hr;
hr = ResetState(pPresentationParameters); ResetState(pPresentationParameters);
if (FAILED(hr))
return hr;
Flush(); Flush();
SynchronizeCsThread(DxvkCsThread::SynchronizeAll); SynchronizeCsThread(DxvkCsThread::SynchronizeAll);
@ -911,6 +920,10 @@ namespace dxvk {
IDirect3DSurface9* pDestSurface) { IDirect3DSurface9* pDestSurface) {
D3D9DeviceLock lock = LockDevice(); D3D9DeviceLock lock = LockDevice();
if (unlikely(IsDeviceLost())) {
return D3DERR_DEVICELOST;
}
D3D9Surface* src = static_cast<D3D9Surface*>(pRenderTarget); D3D9Surface* src = static_cast<D3D9Surface*>(pRenderTarget);
D3D9Surface* dst = static_cast<D3D9Surface*>(pDestSurface); D3D9Surface* dst = static_cast<D3D9Surface*>(pDestSurface);
@ -2365,10 +2378,12 @@ namespace dxvk {
HRESULT STDMETHODCALLTYPE D3D9DeviceEx::ValidateDevice(DWORD* pNumPasses) { HRESULT STDMETHODCALLTYPE D3D9DeviceEx::ValidateDevice(DWORD* pNumPasses) {
D3D9DeviceLock lock = LockDevice();
if (pNumPasses != nullptr) if (pNumPasses != nullptr)
*pNumPasses = 1; *pNumPasses = 1;
return D3D_OK; return IsDeviceLost() ? D3DERR_DEVICELOST : D3D_OK;
} }
@ -3726,6 +3741,10 @@ namespace dxvk {
if (!m_implicitSwapchain->GetPresentParams()->Windowed) if (!m_implicitSwapchain->GetPresentParams()->Windowed)
return D3DERR_INVALIDCALL; return D3DERR_INVALIDCALL;
if (unlikely(IsDeviceLost())) {
return D3DERR_DEVICELOST;
}
m_implicitSwapchain->Invalidate(pPresentationParameters->hDeviceWindow); m_implicitSwapchain->Invalidate(pPresentationParameters->hDeviceWindow);
try { try {
@ -7094,7 +7113,7 @@ namespace dxvk {
} }
HRESULT D3D9DeviceEx::ResetState(D3DPRESENT_PARAMETERS* pPresentationParameters) { void D3D9DeviceEx::ResetState(D3DPRESENT_PARAMETERS* pPresentationParameters) {
if (!pPresentationParameters->EnableAutoDepthStencil) if (!pPresentationParameters->EnableAutoDepthStencil)
SetDepthStencilSurface(nullptr); SetDepthStencilSurface(nullptr);
@ -7335,8 +7354,6 @@ namespace dxvk {
UpdateVertexBoolSpec(0u); UpdateVertexBoolSpec(0u);
UpdatePixelBoolSpec(0u); UpdatePixelBoolSpec(0u);
UpdateCommonSamplerSpec(0u, 0u, 0u); UpdateCommonSamplerSpec(0u, 0u, 0u);
return D3D_OK;
} }
@ -7410,9 +7427,7 @@ namespace dxvk {
if (FAILED(hr)) if (FAILED(hr))
return hr; return hr;
hr = ResetState(pPresentationParameters); ResetState(pPresentationParameters);
if (FAILED(hr))
return hr;
Flush(); Flush();
SynchronizeCsThread(DxvkCsThread::SynchronizeAll); SynchronizeCsThread(DxvkCsThread::SynchronizeAll);

View File

@ -87,6 +87,12 @@ namespace dxvk {
using D3D9DeviceFlags = Flags<D3D9DeviceFlag>; using D3D9DeviceFlags = Flags<D3D9DeviceFlag>;
enum class D3D9DeviceLostState {
Ok = 0,
Lost = 1,
NotReset = 2,
};
struct D3D9DrawInfo { struct D3D9DrawInfo {
uint32_t vertexCount; uint32_t vertexCount;
uint32_t instanceCount; uint32_t instanceCount;
@ -933,7 +939,7 @@ namespace dxvk {
const D3D9ConstantLayout& GetVertexConstantLayout() { return m_vsLayout; } const D3D9ConstantLayout& GetVertexConstantLayout() { return m_vsLayout; }
const D3D9ConstantLayout& GetPixelConstantLayout() { return m_psLayout; } const D3D9ConstantLayout& GetPixelConstantLayout() { return m_psLayout; }
HRESULT ResetState(D3DPRESENT_PARAMETERS* pPresentationParameters); void ResetState(D3DPRESENT_PARAMETERS* pPresentationParameters);
HRESULT ResetSwapChain(D3DPRESENT_PARAMETERS* pPresentationParameters, D3DDISPLAYMODEEX* pFullscreenDisplayMode); HRESULT ResetSwapChain(D3DPRESENT_PARAMETERS* pPresentationParameters, D3DDISPLAYMODEEX* pFullscreenDisplayMode);
HRESULT InitialReset(D3DPRESENT_PARAMETERS* pPresentationParameters, D3DDISPLAYMODEEX* pFullscreenDisplayMode); HRESULT InitialReset(D3DPRESENT_PARAMETERS* pPresentationParameters, D3DDISPLAYMODEEX* pFullscreenDisplayMode);
@ -950,6 +956,43 @@ namespace dxvk {
void TouchMappedTexture(D3D9CommonTexture* pTexture); void TouchMappedTexture(D3D9CommonTexture* pTexture);
void RemoveMappedTexture(D3D9CommonTexture* pTexture); void RemoveMappedTexture(D3D9CommonTexture* pTexture);
bool IsDeviceLost() const {
return m_deviceLostState != D3D9DeviceLostState::Ok;
}
void NotifyFullscreen(HWND window, bool fullscreen) {
D3D9DeviceLock lock = LockDevice();
if (fullscreen) {
if (unlikely(window != m_fullscreenWindow && m_fullscreenWindow != NULL)) {
Logger::warn("Multiple fullscreen windows detected.");
}
m_fullscreenWindow = window;
} else {
if (unlikely(m_fullscreenWindow != window)) {
Logger::warn("Window was not fullscreen in the first place.");
} else {
m_fullscreenWindow = 0;
}
}
}
void NotifyWindowActivated(HWND window, bool activated) {
D3D9DeviceLock lock = LockDevice();
if (likely(!m_d3d9Options.deviceLost || IsExtended()))
return;
if (activated && m_deviceLostState == D3D9DeviceLostState::Lost) {
Logger::info("Device not reset");
m_deviceLostState = D3D9DeviceLostState::NotReset;
} else if (!activated && m_deviceLostState != D3D9DeviceLostState::Lost && m_fullscreenWindow == window) {
Logger::info("Device lost");
m_deviceLostState = D3D9DeviceLostState::Lost;
m_fullscreenWindow = NULL;
}
}
private: private:
DxvkCsChunkRef AllocCsChunk() { DxvkCsChunkRef AllocCsChunk() {
@ -1317,6 +1360,9 @@ namespace dxvk {
Direct3DState9 m_state; Direct3DState9 m_state;
D3D9DeviceLostState m_deviceLostState = D3D9DeviceLostState::Ok;
HWND m_fullscreenWindow = NULL;
#ifdef D3D9_ALLOW_UNMAPPING #ifdef D3D9_ALLOW_UNMAPPING
lru_list<D3D9CommonTexture*> m_mappedTextures; lru_list<D3D9CommonTexture*> m_mappedTextures;
#endif #endif

View File

@ -74,6 +74,7 @@ namespace dxvk {
this->allowDirectBufferMapping = config.getOption<bool> ("d3d9.allowDirectBufferMapping", true); this->allowDirectBufferMapping = config.getOption<bool> ("d3d9.allowDirectBufferMapping", true);
this->seamlessCubes = config.getOption<bool> ("d3d9.seamlessCubes", false); this->seamlessCubes = config.getOption<bool> ("d3d9.seamlessCubes", false);
this->textureMemory = config.getOption<int32_t> ("d3d9.textureMemory", 100) << 20; this->textureMemory = config.getOption<int32_t> ("d3d9.textureMemory", 100) << 20;
this->deviceLost = config.getOption<bool> ("d3d9.deviceLost", false);
std::string floatEmulation = Config::toLower(config.getOption<std::string>("d3d9.floatEmulation", "auto")); std::string floatEmulation = Config::toLower(config.getOption<std::string>("d3d9.floatEmulation", "auto"));
if (floatEmulation == "strict") { if (floatEmulation == "strict") {

View File

@ -158,6 +158,9 @@ namespace dxvk {
/// Shader dump path /// Shader dump path
std::string shaderDumpPath; std::string shaderDumpPath;
/// Enable emulation of device loss when a fullscreen app loses focus
bool deviceLost;
}; };
} }

View File

@ -132,6 +132,14 @@ namespace dxvk {
HRESULT STDMETHODCALLTYPE D3D9Query::GetData(void* pData, DWORD dwSize, DWORD dwGetDataFlags) { HRESULT STDMETHODCALLTYPE D3D9Query::GetData(void* pData, DWORD dwSize, DWORD dwGetDataFlags) {
D3D9DeviceLock lock = m_parent->LockDevice();
bool flush = dwGetDataFlags & D3DGETDATA_FLUSH;
if (unlikely(m_parent->IsDeviceLost())) {
return flush ? D3DERR_DEVICELOST : S_FALSE;
}
if (m_state == D3D9_VK_QUERY_CACHED) { if (m_state == D3D9_VK_QUERY_CACHED) {
// Query data was already retrieved once. // Query data was already retrieved once.
// Use cached query data to prevent having to check the VK event // Use cached query data to prevent having to check the VK event
@ -148,8 +156,6 @@ namespace dxvk {
HRESULT hr = this->GetQueryData(pData, dwSize); HRESULT hr = this->GetQueryData(pData, dwSize);
bool flush = dwGetDataFlags & D3DGETDATA_FLUSH;
// If we get S_FALSE and it's not from the fact // If we get S_FALSE and it's not from the fact
// they didn't call end, do some flushy stuff... // they didn't call end, do some flushy stuff...
if (flush && hr == S_FALSE && m_state != D3D9_VK_QUERY_BEGUN) { if (flush && hr == S_FALSE && m_state != D3D9_VK_QUERY_BEGUN) {

View File

@ -103,6 +103,9 @@ namespace dxvk {
DWORD dwFlags) { DWORD dwFlags) {
D3D9DeviceLock lock = m_parent->LockDevice(); D3D9DeviceLock lock = m_parent->LockDevice();
if (unlikely(m_parent->IsDeviceLost()))
return D3DERR_DEVICELOST;
// If we have no backbuffers, error out. // If we have no backbuffers, error out.
// This handles the case where a ::Reset failed due to OOM // This handles the case where a ::Reset failed due to OOM
// or whatever. // or whatever.
@ -238,6 +241,10 @@ namespace dxvk {
if (unlikely(dstTexInfo->Desc()->Pool != D3DPOOL_SYSTEMMEM && dstTexInfo->Desc()->Pool != D3DPOOL_SCRATCH)) if (unlikely(dstTexInfo->Desc()->Pool != D3DPOOL_SYSTEMMEM && dstTexInfo->Desc()->Pool != D3DPOOL_SCRATCH))
return D3DERR_INVALIDCALL; return D3DERR_INVALIDCALL;
if (unlikely(m_parent->IsDeviceLost())) {
return D3DERR_DEVICELOST;
}
VkExtent3D dstTexExtent = dstTexInfo->GetExtentMip(dst->GetMipLevel()); VkExtent3D dstTexExtent = dstTexInfo->GetExtentMip(dst->GetMipLevel());
VkExtent3D srcTexExtent = srcTexInfo->GetExtentMip(0); VkExtent3D srcTexExtent = srcTexInfo->GetExtentMip(0);
@ -540,6 +547,8 @@ namespace dxvk {
this->LeaveFullscreenMode(); this->LeaveFullscreenMode();
} }
else { else {
m_parent->NotifyFullscreen(m_window, true);
if (changeFullscreen) { if (changeFullscreen) {
hr = this->EnterFullscreenMode(pPresentParams, pFullscreenDisplayMode); hr = this->EnterFullscreenMode(pPresentParams, pFullscreenDisplayMode);
if (FAILED(hr)) if (FAILED(hr))
@ -1151,6 +1160,8 @@ namespace dxvk {
return D3DERR_INVALIDCALL; return D3DERR_INVALIDCALL;
} }
m_parent->NotifyFullscreen(m_window, true);
return D3D_OK; return D3D_OK;
} }
@ -1171,6 +1182,8 @@ namespace dxvk {
return D3DERR_NOTAVAILABLE; return D3DERR_NOTAVAILABLE;
} }
m_parent->NotifyFullscreen(m_window, false);
return D3D_OK; return D3D_OK;
} }

View File

@ -10,6 +10,7 @@ namespace dxvk
bool unicode; bool unicode;
bool filter; bool filter;
bool activateProcessed; bool activateProcessed;
bool deactivateProcessed;
WNDPROC proc; WNDPROC proc;
D3D9SwapChainEx* swapchain; D3D9SwapChainEx* swapchain;
}; };
@ -53,11 +54,14 @@ namespace dxvk
DefWindowProcW, DefWindowProcA, unicode, DefWindowProcW, DefWindowProcA, unicode,
window, message, wparam, lparam); window, message, wparam, lparam);
D3D9DeviceEx* device = windowData.swapchain->GetParent();
if (message == WM_DESTROY) if (message == WM_DESTROY)
ResetWindowProc(window); ResetWindowProc(window);
else if (message == WM_ACTIVATEAPP) { else if (message == WM_ACTIVATEAPP) {
D3DDEVICE_CREATION_PARAMETERS create_parms; D3DDEVICE_CREATION_PARAMETERS create_parms;
windowData.swapchain->GetDevice()->GetCreationParameters(&create_parms); device->GetCreationParameters(&create_parms);
if (!(create_parms.BehaviorFlags & D3DCREATE_NOWINDOWCHANGES)) { if (!(create_parms.BehaviorFlags & D3DCREATE_NOWINDOWCHANGES)) {
D3D9WindowMessageFilter filter(window); D3D9WindowMessageFilter filter(window);
@ -70,19 +74,24 @@ namespace dxvk
windowData.swapchain->GetPresentParameters(&params); windowData.swapchain->GetPresentParameters(&params);
SetWindowPos(window, nullptr, rect.left, rect.top, params.BackBufferWidth, params.BackBufferHeight, SetWindowPos(window, nullptr, rect.left, rect.top, params.BackBufferWidth, params.BackBufferHeight,
SWP_NOACTIVATE | SWP_NOZORDER); SWP_NOACTIVATE | SWP_NOZORDER);
SetActivateProcessed(window, true);
} }
else if (!wparam) { else if (!wparam) {
if (IsWindowVisible(window)) if (IsWindowVisible(window))
ShowWindow(window, SW_MINIMIZE); ShowWindow(window, SW_MINIMIZE);
SetActivateProcessed(window, false);
} }
} }
if ((wparam && !windowData.activateProcessed)
|| (!wparam && !windowData.deactivateProcessed)) {
device->NotifyWindowActivated(window, wparam);
}
SetActivateProcessed(window, !!wparam);
} }
else if (message == WM_SIZE) else if (message == WM_SIZE)
{ {
D3DDEVICE_CREATION_PARAMETERS create_parms; D3DDEVICE_CREATION_PARAMETERS create_parms;
windowData.swapchain->GetDevice()->GetCreationParameters(&create_parms); device->GetCreationParameters(&create_parms);
if (!(create_parms.BehaviorFlags & D3DCREATE_NOWINDOWCHANGES) && !IsIconic(window)) if (!(create_parms.BehaviorFlags & D3DCREATE_NOWINDOWCHANGES) && !IsIconic(window))
PostMessageW(window, WM_ACTIVATEAPP, 1, GetCurrentThreadId()); PostMessageW(window, WM_ACTIVATEAPP, 1, GetCurrentThreadId());
@ -137,8 +146,10 @@ namespace dxvk
{ {
std::lock_guard lock(g_windowProcMapMutex); std::lock_guard lock(g_windowProcMapMutex);
auto it = g_windowProcMap.find(window); auto it = g_windowProcMap.find(window);
if (it != g_windowProcMap.end()) if (it != g_windowProcMap.end()) {
it->second.activateProcessed = processed; it->second.activateProcessed = processed;
it->second.deactivateProcessed = !processed;
}
} }
#else #else
D3D9WindowMessageFilter::D3D9WindowMessageFilter(HWND window, bool filter) { D3D9WindowMessageFilter::D3D9WindowMessageFilter(HWND window, bool filter) {