1
0
mirror of https://github.com/doitsujin/dxvk.git synced 2024-12-12 13:08:50 +01:00

[d3d9] Reject Reset if there's any remaining DEFAULT resources

This commit is contained in:
Robin Kertels 2023-06-10 01:18:05 +02:00 committed by Joshie
parent a1a91dd766
commit 52ac271acb
13 changed files with 192 additions and 58 deletions

View File

@ -20,6 +20,11 @@ namespace dxvk {
m_dirtyRange = D3D9Range(0, m_desc.Size);
}
D3D9CommonBuffer::~D3D9CommonBuffer() {
if (m_desc.Pool == D3DPOOL_DEFAULT)
m_parent->DecrementLosableCounter();
}
HRESULT D3D9CommonBuffer::Lock(
UINT OffsetToLock,

View File

@ -78,6 +78,8 @@ namespace dxvk {
D3D9DeviceEx* pDevice,
const D3D9_BUFFER_DESC* pDesc);
~D3D9CommonBuffer();
HRESULT Lock(
UINT OffsetToLock,
UINT SizeToLock,

View File

@ -98,6 +98,9 @@ namespace dxvk {
m_device->ChangeReportedMemory(m_size);
m_device->RemoveMappedTexture(this);
if (m_desc.IsLosable)
m_device->DecrementLosableCounter();
}

View File

@ -48,6 +48,7 @@ namespace dxvk {
bool IsBackBuffer;
bool IsAttachmentOnly;
bool IsLockable;
bool IsLosable;
};
struct D3D9ColorView {

View File

@ -415,11 +415,42 @@ namespace dxvk {
Logger::info("Device reset");
m_deviceLostState = D3D9DeviceLostState::Ok;
HRESULT hr = ResetSwapChain(pPresentationParameters, nullptr);
if (FAILED(hr))
return hr;
if (!IsExtended()) {
// The internal references are always cleared, regardless of whether the Reset call succeeds.
ResetState(pPresentationParameters);
m_implicitSwapchain->DestroyBackBuffers();
m_autoDepthStencil = nullptr;
} else {
// Extended devices only reset the bound render targets
for (uint32_t i = 0; i < caps::MaxSimultaneousRenderTargets; i++) {
SetRenderTargetInternal(i, nullptr);
}
SetDepthStencilSurface(nullptr);
}
ResetState(pPresentationParameters);
/*
* Before calling the IDirect3DDevice9::Reset method for a device,
* an application should release any explicit render targets,
* depth stencil surfaces, additional swap chains, state blocks,
* and D3DPOOL_DEFAULT resources associated with the device.
*
* We have to check after ResetState clears the references held by SetTexture, etc.
* This matches what Windows D3D9 does.
*/
if (unlikely(m_losableResourceCounter.load() != 0 && !IsExtended())) {
Logger::warn(str::format("Device reset failed because device still has alive losable resources: Device not reset. Remaining resources: ", m_losableResourceCounter.load()));
m_deviceLostState = D3D9DeviceLostState::NotReset;
return D3DERR_INVALIDCALL;
}
HRESULT hr = ResetSwapChain(pPresentationParameters, nullptr);
if (FAILED(hr)) {
if (!IsExtended()) {
Logger::warn("Device reset failed: Device not reset");
m_deviceLostState = D3D9DeviceLostState::NotReset;
}
return hr;
}
Flush();
SynchronizeCsThread(DxvkCsThread::SynchronizeAll);
@ -516,6 +547,7 @@ namespace dxvk {
desc.MultisampleQuality = 0;
desc.IsBackBuffer = FALSE;
desc.IsAttachmentOnly = FALSE;
desc.IsLosable = Pool == D3DPOOL_DEFAULT;
// Docs:
// Textures placed in the D3DPOOL_DEFAULT pool cannot be locked
// unless they are dynamic textures or they are private, FOURCC, driver formats.
@ -542,6 +574,9 @@ namespace dxvk {
m_initializer->InitTexture(texture->GetCommonTexture(), initialData);
*ppTexture = texture.ref();
if (desc.IsLosable)
m_losableResourceCounter++;
return D3D_OK;
}
catch (const DxvkError& e) {
@ -583,6 +618,7 @@ namespace dxvk {
desc.MultisampleQuality = 0;
desc.IsBackBuffer = FALSE;
desc.IsAttachmentOnly = FALSE;
desc.IsLosable = Pool == D3DPOOL_DEFAULT;
// Docs:
// Textures placed in the D3DPOOL_DEFAULT pool cannot be locked
// unless they are dynamic textures or they are private, FOURCC, driver formats.
@ -597,6 +633,9 @@ namespace dxvk {
const Com<D3D9Texture3D> texture = new D3D9Texture3D(this, &desc);
m_initializer->InitTexture(texture->GetCommonTexture());
*ppVolumeTexture = texture.ref();
if (desc.IsLosable)
m_losableResourceCounter++;
return D3D_OK;
}
@ -637,6 +676,7 @@ namespace dxvk {
desc.MultisampleQuality = 0;
desc.IsBackBuffer = FALSE;
desc.IsAttachmentOnly = FALSE;
desc.IsLosable = Pool == D3DPOOL_DEFAULT;
// Docs:
// Textures placed in the D3DPOOL_DEFAULT pool cannot be locked
// unless they are dynamic textures or they are private, FOURCC, driver formats.
@ -651,6 +691,9 @@ namespace dxvk {
const Com<D3D9TextureCube> texture = new D3D9TextureCube(this, &desc);
m_initializer->InitTexture(texture->GetCommonTexture());
*ppCubeTexture = texture.ref();
if (desc.IsLosable)
m_losableResourceCounter++;
return D3D_OK;
}
@ -691,6 +734,9 @@ namespace dxvk {
const Com<D3D9VertexBuffer> buffer = new D3D9VertexBuffer(this, &desc);
m_initializer->InitBuffer(buffer->GetCommonBuffer());
*ppVertexBuffer = buffer.ref();
if (desc.Pool == D3DPOOL_DEFAULT)
m_losableResourceCounter++;
return D3D_OK;
}
catch (const DxvkError & e) {
@ -729,6 +775,9 @@ namespace dxvk {
const Com<D3D9IndexBuffer> buffer = new D3D9IndexBuffer(this, &desc);
m_initializer->InitBuffer(buffer->GetCommonBuffer());
*ppIndexBuffer = buffer.ref();
if (desc.Pool == D3DPOOL_DEFAULT)
m_losableResourceCounter++;
return D3D_OK;
}
catch (const DxvkError & e) {
@ -1311,14 +1360,22 @@ namespace dxvk {
0);
}
HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetRenderTarget(
DWORD RenderTargetIndex,
IDirect3DSurface9* pRenderTarget) {
D3D9DeviceLock lock = LockDevice();
if (unlikely(RenderTargetIndex >= caps::MaxSimultaneousRenderTargets
|| (pRenderTarget == nullptr && RenderTargetIndex == 0)))
if (unlikely((pRenderTarget == nullptr && RenderTargetIndex == 0)))
return D3DERR_INVALIDCALL;
return SetRenderTargetInternal(RenderTargetIndex, pRenderTarget);
}
HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetRenderTargetInternal(
DWORD RenderTargetIndex,
IDirect3DSurface9* pRenderTarget) {
if (unlikely(RenderTargetIndex >= caps::MaxSimultaneousRenderTargets))
return D3DERR_INVALIDCALL;
D3D9Surface* rt = static_cast<D3D9Surface*>(pRenderTarget);
@ -1330,21 +1387,28 @@ namespace dxvk {
return D3DERR_INVALIDCALL;
if (RenderTargetIndex == 0) {
auto rtSize = rt->GetSurfaceExtent();
D3DVIEWPORT9 viewport;
viewport.X = 0;
viewport.Y = 0;
viewport.Width = rtSize.width;
viewport.Height = rtSize.height;
viewport.MinZ = 0.0f;
viewport.MaxZ = 1.0f;
RECT scissorRect;
scissorRect.left = 0;
scissorRect.top = 0;
scissorRect.right = rtSize.width;
scissorRect.bottom = rtSize.height;
if (likely(rt != nullptr)) {
auto rtSize = rt->GetSurfaceExtent();
viewport.Width = rtSize.width;
viewport.Height = rtSize.height;
scissorRect.right = rtSize.width;
scissorRect.bottom = rtSize.height;
} else {
viewport.Width = 0;
viewport.Height = 0;
scissorRect.right = 0;
scissorRect.bottom = 0;
}
if (m_state.viewport != viewport) {
m_flags.set(D3D9DeviceFlag::DirtyFFViewport);
@ -1389,13 +1453,18 @@ namespace dxvk {
m_flags.set(D3D9DeviceFlag::DirtyBlendState);
if (RenderTargetIndex == 0) {
bool validSampleMask = texInfo->Desc()->MultiSample > D3DMULTISAMPLE_NONMASKABLE;
if (likely(texInfo != nullptr)) {
bool validSampleMask = texInfo->Desc()->MultiSample > D3DMULTISAMPLE_NONMASKABLE;
if (validSampleMask != m_flags.test(D3D9DeviceFlag::ValidSampleMask)) {
if (validSampleMask != m_flags.test(D3D9DeviceFlag::ValidSampleMask)) {
m_flags.clr(D3D9DeviceFlag::ValidSampleMask);
if (validSampleMask)
m_flags.set(D3D9DeviceFlag::ValidSampleMask);
m_flags.set(D3D9DeviceFlag::DirtyMultiSampleState);
}
} else {
m_flags.clr(D3D9DeviceFlag::ValidSampleMask);
if (validSampleMask)
m_flags.set(D3D9DeviceFlag::ValidSampleMask);
m_flags.set(D3D9DeviceFlag::DirtyMultiSampleState);
}
}
@ -2254,6 +2323,8 @@ namespace dxvk {
try {
const Com<D3D9StateBlock> sb = new D3D9StateBlock(this, ConvertStateBlockType(Type));
*ppSB = sb.ref();
m_losableResourceCounter++;
return D3D_OK;
}
catch (const DxvkError & e) {
@ -2284,6 +2355,7 @@ namespace dxvk {
return D3DERR_INVALIDCALL;
*ppSB = m_recorder.ref();
m_losableResourceCounter++;
m_recorder = nullptr;
return D3D_OK;
@ -3613,6 +3685,7 @@ namespace dxvk {
desc.IsBackBuffer = FALSE;
desc.IsAttachmentOnly = TRUE;
desc.IsLockable = Lockable;
desc.IsLosable = TRUE;
if (FAILED(D3D9CommonTexture::NormalizeTextureProperties(this, &desc)))
return D3DERR_INVALIDCALL;
@ -3621,6 +3694,8 @@ namespace dxvk {
const Com<D3D9Surface> surface = new D3D9Surface(this, &desc, nullptr, pSharedHandle);
m_initializer->InitTexture(surface->GetCommonTexture());
*ppSurface = surface.ref();
m_losableResourceCounter++;
return D3D_OK;
}
catch (const DxvkError& e) {
@ -3657,6 +3732,7 @@ namespace dxvk {
desc.MultisampleQuality = 0;
desc.IsBackBuffer = FALSE;
desc.IsAttachmentOnly = Pool == D3DPOOL_DEFAULT;
desc.IsLosable = Pool == D3DPOOL_DEFAULT;
// Docs: Off-screen plain surfaces are always lockable, regardless of their pool types.
desc.IsLockable = TRUE;
@ -3670,6 +3746,10 @@ namespace dxvk {
const Com<D3D9Surface> surface = new D3D9Surface(this, &desc, nullptr, pSharedHandle);
m_initializer->InitTexture(surface->GetCommonTexture());
*ppSurface = surface.ref();
if (desc.IsLosable)
m_losableResourceCounter++;
return D3D_OK;
}
catch (const DxvkError& e) {
@ -3708,6 +3788,7 @@ namespace dxvk {
desc.MultisampleQuality = MultisampleQuality;
desc.IsBackBuffer = FALSE;
desc.IsAttachmentOnly = TRUE;
desc.IsLosable = TRUE;
// Docs don't say anything, so just assume it's lockable.
desc.IsLockable = TRUE;
@ -3718,6 +3799,8 @@ namespace dxvk {
const Com<D3D9Surface> surface = new D3D9Surface(this, &desc, nullptr, pSharedHandle);
m_initializer->InitTexture(surface->GetCommonTexture());
*ppSurface = surface.ref();
m_losableResourceCounter++;
return D3D_OK;
}
catch (const DxvkError& e) {
@ -3779,6 +3862,7 @@ namespace dxvk {
try {
auto* swapchain = new D3D9SwapChainEx(this, pPresentationParameters, pFullscreenDisplayMode);
*ppSwapChain = ref(swapchain);
m_losableResourceCounter++;
}
catch (const DxvkError & e) {
Logger::err(e.message());
@ -7171,11 +7255,10 @@ namespace dxvk {
void D3D9DeviceEx::ResetState(D3DPRESENT_PARAMETERS* pPresentationParameters) {
if (!pPresentationParameters->EnableAutoDepthStencil)
SetDepthStencilSurface(nullptr);
SetDepthStencilSurface(nullptr);
for (uint32_t i = 1; i < caps::MaxSimultaneousRenderTargets; i++)
SetRenderTarget(i, nullptr);
for (uint32_t i = 0; i < caps::MaxSimultaneousRenderTargets; i++)
SetRenderTargetInternal(i, nullptr);
auto& rs = m_state.renderStates;
@ -7358,8 +7441,9 @@ namespace dxvk {
for (uint32_t i = 0; i < caps::MaxStreams; i++)
m_state.streamFreq[i] = 1;
for (uint32_t i = 0; i < m_state.textures->size(); i++)
for (uint32_t i = 0; i < m_state.textures->size(); i++) {
SetStateTexture(i, nullptr);
}
EmitCs([
cSize = m_state.textures->size()
@ -7465,6 +7549,7 @@ namespace dxvk {
desc.MultisampleQuality = pPresentationParameters->MultiSampleQuality;
desc.IsBackBuffer = FALSE;
desc.IsAttachmentOnly = TRUE;
desc.IsLosable = TRUE;
// Docs: Also note that - unlike textures - swap chain back buffers, render targets [..] can be locked
desc.IsLockable = TRUE;
@ -7474,6 +7559,7 @@ namespace dxvk {
m_autoDepthStencil = new D3D9Surface(this, &desc, nullptr, nullptr);
m_initializer->InitTexture(m_autoDepthStencil->GetCommonTexture());
SetDepthStencilSurface(m_autoDepthStencil.ptr());
m_losableResourceCounter++;
}
SetRenderTarget(0, m_implicitSwapchain->GetBackBuffer(0));
@ -7575,6 +7661,43 @@ namespace dxvk {
#endif
}
////////////////////////////////////
// D3D9 Device Lost
////////////////////////////////////
void D3D9DeviceEx::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 D3D9DeviceEx::NotifyWindowActivated(HWND window, bool activated) {
D3D9DeviceLock lock = LockDevice();
if (likely(!m_d3d9Options.deviceLossOnFocusLoss || 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;
}
}
////////////////////////////////////
// D3D9 Device Specialization State
////////////////////////////////////

View File

@ -879,6 +879,10 @@ namespace dxvk {
void MarkTextureBindingDirty(IDirect3DBaseTexture9* texture);
HRESULT STDMETHODCALLTYPE SetRenderTargetInternal(
DWORD RenderTargetIndex,
IDirect3DSurface9* pRenderTarget);
D3D9DrawInfo GenerateDrawInfo(
D3DPRIMITIVETYPE PrimitiveType,
UINT PrimitiveCount,
@ -961,41 +965,20 @@ namespace dxvk {
void TouchMappedTexture(D3D9CommonTexture* pTexture);
void RemoveMappedTexture(D3D9CommonTexture* pTexture);
// Device Lost
bool IsDeviceLost() const {
return m_deviceLostState != D3D9DeviceLostState::Ok;
}
void NotifyFullscreen(HWND window, bool fullscreen) {
D3D9DeviceLock lock = LockDevice();
void NotifyFullscreen(HWND window, bool fullscreen);
void NotifyWindowActivated(HWND window, bool activated);
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 IncrementLosableCounter() {
m_losableResourceCounter++;
}
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;
}
void DecrementLosableCounter() {
m_losableResourceCounter--;
}
bool CanOnlySWVP() const {
@ -1369,6 +1352,7 @@ namespace dxvk {
D3D9DeviceLostState m_deviceLostState = D3D9DeviceLostState::Ok;
HWND m_fullscreenWindow = NULL;
std::atomic<uint32_t> m_losableResourceCounter = { 0 };
#ifdef D3D9_ALLOW_UNMAPPING
lru_list<D3D9CommonTexture*> m_mappedTextures;

View File

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

View File

@ -143,7 +143,7 @@ namespace dxvk {
std::string shaderDumpPath;
/// Enable emulation of device loss when a fullscreen app loses focus
bool deviceLost;
bool deviceLossOnFocusLoss;
};
}

View File

@ -17,6 +17,9 @@ namespace dxvk {
CaptureType(Type);
}
D3D9StateBlock::~D3D9StateBlock() {
m_parent->DecrementLosableCounter();
}
HRESULT STDMETHODCALLTYPE D3D9StateBlock::QueryInterface(
REFIID riid,

View File

@ -89,6 +89,8 @@ namespace dxvk {
D3D9StateBlock(D3D9DeviceEx* pDevice, D3D9StateBlockType Type);
~D3D9StateBlock();
HRESULT STDMETHODCALLTYPE QueryInterface(
REFIID riid,
void** ppvObject) final;

View File

@ -71,6 +71,8 @@ namespace dxvk {
m_device->waitForSubmission(&m_presentStatus);
m_device->waitForIdle();
m_parent->DecrementLosableCounter();
}
@ -436,6 +438,13 @@ namespace dxvk {
return D3DERR_INVALIDCALL;
}
if (m_backBuffers.empty()) {
// The backbuffers were destroyed and not recreated.
// This can happen when a call to Reset fails.
*ppBackBuffer = nullptr;
return D3D_OK;
}
*ppBackBuffer = ref(m_backBuffers[iBackBuffer].ptr());
return D3D_OK;
}
@ -977,6 +986,7 @@ namespace dxvk {
desc.Discard = FALSE;
desc.IsBackBuffer = TRUE;
desc.IsAttachmentOnly = FALSE;
desc.IsLosable = TRUE;
// Docs: Also note that - unlike textures - swap chain back buffers, render targets [..] can be locked
desc.IsLockable = TRUE;
@ -984,6 +994,7 @@ namespace dxvk {
D3D9Surface* surface;
try {
surface = new D3D9Surface(m_parent, &desc, this, nullptr);
m_parent->IncrementLosableCounter();
} catch (const DxvkError& e) {
DestroyBackBuffers();
Logger::err(e.message());

View File

@ -120,6 +120,8 @@ namespace dxvk {
bool HasFormatsUnlocked() const { return m_unlockAdditionalFormats; }
void DestroyBackBuffers();
private:
enum BindingIds : uint32_t {
@ -189,8 +191,6 @@ namespace dxvk {
void CreateRenderTargetViews();
void DestroyBackBuffers();
HRESULT CreateBackBuffers(
uint32_t NumBackBuffers);

View File

@ -500,7 +500,7 @@ namespace dxvk {
{ "d3d9.customVendorId", "1002" },
{ "dxgi.emulateUMA", "True" },
{ "d3d9.supportDFFormats", "False" },
{ "d3d9.deviceLost", "True" },
{ "d3d9.deviceLostOnFocusLoss", "True" },
}} },
/* Battlefield 2 (bad z-pass) */
{ R"(\\BF2\.exe$)", {{
@ -754,7 +754,7 @@ namespace dxvk {
/* DC Universe Online *
* Freezes after alt tabbing */
{ R"(\\DCGAME\.EXE$)", {{
{ "d3d9.deviceLost", "True" },
{ "d3d9.deviceLostOnFocusLoss", "True" },
}} },
/* Halo Online *
* Black textures */