From bec4182e58d6260532958e5dca97607f5484049f Mon Sep 17 00:00:00 2001 From: WinterSnowfall Date: Sat, 9 May 2026 23:32:34 +0300 Subject: [PATCH] [d3d7] Update D7VK module to v1.9 --- src/d3d9/d3d9_bridge.cpp | 5 +- src/d3d9/d3d9_bridge.h | 4 +- src/d3d9/d3d9_device.cpp | 1 - src/d3d9/d3d9_device.h | 5 +- src/d3d9/d3d9_fixed_function.cpp | 4 +- src/d3d9/d3d9_fixed_function.h | 1 - src/ddraw/d3d3/d3d3_device.cpp | 552 ++++++----- src/ddraw/d3d3/d3d3_device.h | 42 +- src/ddraw/d3d3/d3d3_execute_buffer.cpp | 27 +- src/ddraw/d3d3/d3d3_execute_buffer.h | 18 +- src/ddraw/d3d3/d3d3_interface.cpp | 76 +- src/ddraw/d3d3/d3d3_interface.h | 2 +- src/ddraw/d3d3/d3d3_material.cpp | 44 +- src/ddraw/d3d3/d3d3_material.h | 4 +- src/ddraw/d3d3/d3d3_texture.cpp | 34 +- src/ddraw/d3d3/d3d3_texture.h | 2 +- src/ddraw/d3d3/d3d3_viewport.cpp | 38 +- src/ddraw/d3d3/d3d3_viewport.h | 4 +- src/ddraw/d3d5/d3d5_device.cpp | 455 ++++----- src/ddraw/d3d5/d3d5_device.h | 67 +- src/ddraw/d3d5/d3d5_interface.cpp | 154 ++-- src/ddraw/d3d5/d3d5_interface.h | 4 +- src/ddraw/d3d5/d3d5_material.cpp | 44 +- src/ddraw/d3d5/d3d5_material.h | 4 +- src/ddraw/d3d5/d3d5_texture.cpp | 18 +- src/ddraw/d3d5/d3d5_texture.h | 2 +- src/ddraw/d3d5/d3d5_viewport.cpp | 38 +- src/ddraw/d3d5/d3d5_viewport.h | 4 +- src/ddraw/d3d6/d3d6_buffer.cpp | 158 +++- src/ddraw/d3d6/d3d6_buffer.h | 60 +- src/ddraw/d3d6/d3d6_device.cpp | 597 ++++++------ src/ddraw/d3d6/d3d6_device.h | 90 +- src/ddraw/d3d6/d3d6_interface.cpp | 191 ++-- src/ddraw/d3d6/d3d6_interface.h | 4 +- src/ddraw/d3d6/d3d6_material.cpp | 44 +- src/ddraw/d3d6/d3d6_material.h | 4 +- src/ddraw/d3d6/d3d6_texture.cpp | 10 +- src/ddraw/d3d6/d3d6_texture.h | 2 +- src/ddraw/d3d6/d3d6_viewport.cpp | 43 +- src/ddraw/d3d6/d3d6_viewport.h | 4 +- src/ddraw/d3d7/d3d7_buffer.cpp | 142 ++- src/ddraw/d3d7/d3d7_buffer.h | 57 +- src/ddraw/d3d7/d3d7_device.cpp | 473 ++++++---- src/ddraw/d3d7/d3d7_device.h | 46 +- src/ddraw/d3d7/d3d7_interface.cpp | 188 ++-- src/ddraw/d3d7/d3d7_interface.h | 6 +- src/ddraw/d3d_common_device.cpp | 118 +-- src/ddraw/d3d_common_device.h | 154 +++- src/ddraw/d3d_common_interface.h | 18 +- src/ddraw/d3d_common_viewport.cpp | 24 +- src/ddraw/d3d_common_viewport.h | 4 +- src/ddraw/d3d_light.cpp | 56 +- src/ddraw/d3d_light.h | 14 +- src/ddraw/d3d_process_vertices.h | 681 ++++++++++++++ src/ddraw/ddraw/ddraw_interface.cpp | 67 +- src/ddraw/ddraw/ddraw_interface.h | 2 +- src/ddraw/ddraw/ddraw_surface.cpp | 1101 ++++++++-------------- src/ddraw/ddraw/ddraw_surface.h | 66 +- src/ddraw/ddraw2/ddraw2_interface.cpp | 67 +- src/ddraw/ddraw2/ddraw2_interface.h | 2 +- src/ddraw/ddraw2/ddraw2_surface.cpp | 594 ++++++------ src/ddraw/ddraw2/ddraw2_surface.h | 36 +- src/ddraw/ddraw2/ddraw3_surface.cpp | 607 ++++++------ src/ddraw/ddraw2/ddraw3_surface.h | 37 +- src/ddraw/ddraw4/ddraw4_interface.cpp | 71 +- src/ddraw/ddraw4/ddraw4_interface.h | 2 +- src/ddraw/ddraw4/ddraw4_surface.cpp | 1006 +++++++------------- src/ddraw/ddraw4/ddraw4_surface.h | 58 +- src/ddraw/ddraw7/ddraw7_interface.cpp | 71 +- src/ddraw/ddraw7/ddraw7_interface.h | 2 +- src/ddraw/ddraw7/ddraw7_surface.cpp | 1173 ++++++++---------------- src/ddraw/ddraw7/ddraw7_surface.h | 59 +- src/ddraw/ddraw_caps.h | 7 +- src/ddraw/ddraw_clipper.cpp | 20 +- src/ddraw/ddraw_clipper.h | 4 +- src/ddraw/ddraw_common_interface.h | 54 +- src/ddraw/ddraw_common_surface.cpp | 341 +++++++ src/ddraw/ddraw_common_surface.h | 203 ++-- src/ddraw/ddraw_format.h | 192 ++-- src/ddraw/ddraw_gamma.cpp | 14 +- src/ddraw/ddraw_gamma.h | 2 +- src/ddraw/ddraw_options.h | 94 +- src/ddraw/ddraw_palette.cpp | 20 +- src/ddraw/ddraw_palette.h | 4 +- src/ddraw/ddraw_util.h | 16 +- src/ddraw/ddraw_wrapped_object.h | 52 +- src/util/config/config.cpp | 479 +++++----- src/util/config/config.h | 5 + 88 files changed, 6122 insertions(+), 5247 deletions(-) create mode 100644 src/ddraw/d3d_process_vertices.h diff --git a/src/d3d9/d3d9_bridge.cpp b/src/d3d9/d3d9_bridge.cpp index f2f471b0cd6..c408275b5cd 100644 --- a/src/d3d9/d3d9_bridge.cpp +++ b/src/d3d9/d3d9_bridge.cpp @@ -46,9 +46,8 @@ namespace dxvk { return D3D_OK; //m_device->SetColorKey(colorKeyLow, colorKeyHigh); } - HRESULT DxvkD3D8Bridge::SetLegacyLightsState(bool legacyLightsState, bool isD3DLight2) { - m_device->SetRenderState(D3DRS_NORMALIZENORMALS, legacyLightsState); - return m_device->SetLegacyLightsState(legacyLightsState, isD3DLight2); + HRESULT DxvkD3D8Bridge::SetLegacyLightsState(bool legacyLightsState) { + return m_device->SetLegacyLightsState(legacyLightsState); } HRESULT DxvkD3D8Bridge::UpdateTextureFromBuffer( diff --git a/src/d3d9/d3d9_bridge.h b/src/d3d9/d3d9_bridge.h index 6eff01b3a06..64aa9ae9f23 100644 --- a/src/d3d9/d3d9_bridge.h +++ b/src/d3d9/d3d9_bridge.h @@ -36,7 +36,7 @@ IDxvkD3D8Bridge : public IUnknown { virtual HRESULT ResetSwapChain(D3DPRESENT_PARAMETERS* Params) = 0; virtual HRESULT SetColorKeyState(bool colorKeyState) = 0; virtual HRESULT SetColorKey(DWORD colorKeyLow, DWORD colorKeyHigh) = 0; - virtual HRESULT SetLegacyLightsState(bool legacyLightsState, bool isD3DLight2) = 0; + virtual HRESULT SetLegacyLightsState(bool legacyLightsState) = 0; virtual HRESULT UpdateTextureFromBuffer( IDirect3DSurface9* pDestSurface, @@ -102,7 +102,7 @@ namespace dxvk { HRESULT ResetSwapChain(D3DPRESENT_PARAMETERS* Params); HRESULT SetColorKeyState(bool colorKeyState); HRESULT SetColorKey(DWORD colorKeyLow, DWORD colorKeyHigh); - HRESULT SetLegacyLightsState(bool legacyLightsState, bool isD3DLight2); + HRESULT SetLegacyLightsState(bool legacyLightsState); HRESULT UpdateTextureFromBuffer( IDirect3DSurface9* pDestSurface, diff --git a/src/d3d9/d3d9_device.cpp b/src/d3d9/d3d9_device.cpp index 724d9f00730..1e73b898855 100644 --- a/src/d3d9/d3d9_device.cpp +++ b/src/d3d9/d3d9_device.cpp @@ -6896,7 +6896,6 @@ namespace dxvk { key.Data.Contents.EmissiveSource = m_state.renderStates[D3DRS_EMISSIVEMATERIALSOURCE] & mask; key.Data.Contents.UseLegacyLights = m_useLegacyLights; - key.Data.Contents.IsD3DLight2 = m_isD3DLight2; uint32_t lightCount = 0; diff --git a/src/d3d9/d3d9_device.h b/src/d3d9/d3d9_device.h index b2b93e6b046..99469fc33f2 100644 --- a/src/d3d9/d3d9_device.h +++ b/src/d3d9/d3d9_device.h @@ -929,11 +929,11 @@ namespace dxvk { HRESULT ResetState(D3DPRESENT_PARAMETERS* pPresentationParameters); - HRESULT SetLegacyLightsState(bool legacyLightState, bool isD3DLight2) { + HRESULT SetLegacyLightsState(bool legacyLightState) { if (likely(m_useLegacyLights != legacyLightState)) { m_useLegacyLights = legacyLightState; - m_isD3DLight2 = isD3DLight2; } + return D3D_OK; } @@ -1306,7 +1306,6 @@ namespace dxvk { // D3D6 and earlier legacy light model state bool m_useLegacyLights = false; - bool m_isD3DLight2 = false; bool m_isD3D8Compatible; bool m_amdATOC = false; diff --git a/src/d3d9/d3d9_fixed_function.cpp b/src/d3d9/d3d9_fixed_function.cpp index 23bc15692f0..465f486529b 100644 --- a/src/d3d9/d3d9_fixed_function.cpp +++ b/src/d3d9/d3d9_fixed_function.cpp @@ -1082,7 +1082,7 @@ namespace dxvk { uint32_t delta = m_module.opFSub(m_vec3Type, position, vtx3); uint32_t d = m_module.opLength(m_floatType, delta); - if (m_vsKey.Data.Contents.UseLegacyLights && m_vsKey.Data.Contents.IsD3DLight2) { + if (m_vsKey.Data.Contents.UseLegacyLights) { d = m_module.opFSub(m_floatType, range, d); d = m_module.opFDiv(m_floatType, d, range); } @@ -1096,7 +1096,7 @@ namespace dxvk { atten = m_module.opFDiv (m_floatType, m_module.constf32(1.0f), atten); atten = m_module.opNMin (m_floatType, atten, m_module.constf32(FLT_MAX)); - if (m_vsKey.Data.Contents.UseLegacyLights && m_vsKey.Data.Contents.IsD3DLight2) + if (m_vsKey.Data.Contents.UseLegacyLights) atten = m_module.opSelect(m_floatType, m_module.opFOrdLessThan(bool_t, d, m_module.constf32(0.0f)), m_module.constf32(0.0f), atten); else atten = m_module.opSelect(m_floatType, m_module.opFOrdGreaterThan(bool_t, d, range), m_module.constf32(0.0f), atten); diff --git a/src/d3d9/d3d9_fixed_function.h b/src/d3d9/d3d9_fixed_function.h index c1c1ac190a9..0d74ae938b2 100644 --- a/src/d3d9/d3d9_fixed_function.h +++ b/src/d3d9/d3d9_fixed_function.h @@ -114,7 +114,6 @@ namespace dxvk { uint32_t LightCount : 4; uint32_t UseLegacyLights : 1; - uint32_t IsD3DLight2 : 1; uint32_t TexcoordDeclMask : 24; uint32_t HasFog : 1; diff --git a/src/ddraw/d3d3/d3d3_device.cpp b/src/ddraw/d3d3/d3d3_device.cpp index ca5f77dc0c3..9775ae8ecc8 100644 --- a/src/ddraw/d3d3/d3d3_device.cpp +++ b/src/ddraw/d3d3/d3d3_device.cpp @@ -9,6 +9,7 @@ #include "../d3d6/d3d6_device.h" #include "../d3d5/d3d5_device.h" +#include "../d3d_process_vertices.h" #include @@ -25,12 +26,10 @@ namespace dxvk { d3d9::D3DPRESENT_PARAMETERS Params9, Com&& pDevice9, DWORD CreationFlags9) - : DDrawWrappedObject(pParent, std::move(d3d3DeviceProxy), std::move(pDevice9)) + : DDrawWrappedObject(pParent, std::move(d3d3DeviceProxy)) , m_commonD3DDevice ( commonD3DDevice ) , m_multithread ( CreationFlags9 & D3DCREATE_MULTITHREADED ) - , m_params9 ( Params9 ) , m_desc ( Desc ) - , m_deviceGUID ( deviceGUID ) , m_rt ( pParent ) { if (m_parent != nullptr) { m_commonIntf = m_parent->GetCommonInterface(); @@ -40,31 +39,45 @@ namespace dxvk { throw DxvkError("D3D3Device: ERROR! Failed to retrieve the common interface!"); } - // Get the bridge interface to D3D9 - if (unlikely(FAILED(m_d3d9->QueryInterface(__uuidof(IDxvkD3D8Bridge), reinterpret_cast(&m_bridge))))) { - throw DxvkError("D3D3Device: ERROR! Failed to get D3D9 Bridge. d3d9.dll might not be DXVK!"); - } + d3d9::IDirect3DDevice9* device9; if (likely(m_commonD3DDevice == nullptr)) { - m_commonD3DDevice = new D3DCommonDevice(m_commonIntf, CreationFlags9, - m_bridge->DetermineInitialTextureMemory()); + m_commonD3DDevice = new D3DCommonDevice(m_commonIntf, deviceGUID, Params9, CreationFlags9); + + m_commonD3DDevice->SetD3D9Device(std::move(pDevice9)); + device9 = m_commonD3DDevice->GetD3D9Device(); const D3DOptions* d3dOptions = m_commonIntf->GetOptions(); if (unlikely(d3dOptions->emulateFSAA == FSAAEmulation::Forced)) { Logger::warn("D3D3Device: Force enabling AA"); - m_d3d9->SetRenderState(d3d9::D3DRS_MULTISAMPLEANTIALIAS, TRUE); + device9->SetRenderState(d3d9::D3DRS_MULTISAMPLEANTIALIAS, TRUE); } // The default value of D3DRENDERSTATE_TEXTUREMAPBLEND in D3D3 is D3DTBLEND_MODULATE - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG1, D3DTA_TEXTURE); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG1, D3DTA_TEXTURE); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_COLOROP, D3DTOP_MODULATE); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAOP, D3DTOP_SELECTARG1); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG2, D3DTA_DIFFUSE); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG2, D3DTA_DIFFUSE); + device9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG1, D3DTA_TEXTURE); + device9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG1, D3DTA_TEXTURE); + device9->SetTextureStageState(0, d3d9::D3DTSS_COLOROP, D3DTOP_MODULATE); + device9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAOP, D3DTOP_SELECTARG1); + device9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG2, D3DTA_DIFFUSE); + device9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG2, D3DTA_DIFFUSE); + } else { + device9 = m_commonD3DDevice->GetD3D9Device(); + // Very important, otherwise the depth stencil isn't dirtied on draws + m_ds = m_rt->GetAttachedDepthStencil(); } + // Get the bridge interface to D3D9 + if (unlikely(FAILED(device9->QueryInterface(__uuidof(IDxvkD3D8Bridge), reinterpret_cast(&m_bridge))))) { + throw DxvkError("D3D3Device: ERROR! Failed to get D3D9 Bridge. d3d9.dll might not be DXVK!"); + } + + if (unlikely(!m_commonD3DDevice->GetTotalTextureMemory())) + m_commonD3DDevice->SetTotalTextureMemory(m_bridge->DetermineInitialTextureMemory()); + + // Update D3D9 legacy light state + m_bridge->SetLegacyLightsState(true); + if (m_commonD3DDevice->GetOrigin() == nullptr) m_commonD3DDevice->SetOrigin(this); @@ -118,6 +131,24 @@ namespace dxvk { InitReturnPtr(ppvObject); + // Apparently all of these should return a reference to the parent surface + if (riid == IID_IDirect3DHALDevice || riid == IID_IDirect3DRGBDevice || + riid == IID_IDirect3DMMXDevice || riid == IID_IDirect3DRampDevice || + riid == IID_WineD3DDevice) { + Logger::warn("D3D3Device::QueryInterface: Query with an IDirect3DDevice IID"); + + if (likely(m_parent != nullptr)) { + *ppvObject = ref(m_parent); + return S_OK; + } else { + return E_NOINTERFACE; + } + } + // Yes, this is indeed expected behavior... + if (unlikely(riid == __uuidof(IDirect3DDevice))) { + Logger::debug("D3D3Device::QueryInterface: Query for IDirect3DDevice"); + return E_NOINTERFACE; + } if (unlikely(riid == __uuidof(IDirect3DDevice2))) { if (likely(m_commonD3DDevice->GetD3D5Device() != nullptr)) { Logger::debug("D3D3Device::QueryInterface: Query for existing IDirect3DDevice2"); @@ -141,6 +172,14 @@ namespace dxvk { return E_NOINTERFACE; } + // IDirect3DDevice is quite different than any of the later device interfaces, + // because it will also expose what is available on its parent surface. An easy + // way to handle it, since the surface is its parent, is to forward the calls. + if (likely(m_parent != nullptr)) { + Logger::debug("D3D3Device::QueryInterface: Forwarding to parent surface"); + return m_parent->QueryInterface(riid, ppvObject); + } + try { *ppvObject = ref(this->GetInterface(riid)); return S_OK; @@ -164,7 +203,9 @@ namespace dxvk { D3DDEVICEDESC3 desc_HAL = m_desc; D3DDEVICEDESC3 desc_HEL = m_desc; - if (m_deviceGUID == IID_IDirect3DRGBDevice) { + const GUID deviceGUID = m_commonD3DDevice->GetDeviceGUID(); + + if (deviceGUID == IID_IDirect3DRGBDevice) { desc_HAL.dwFlags = 0; desc_HAL.dcmColorModel = 0; // Some applications apparently care about RGB texture caps @@ -174,7 +215,7 @@ namespace dxvk { & ~D3DPTEXTURECAPS_NONPOW2CONDITIONAL; desc_HEL.dpcLineCaps.dwTextureCaps |= D3DPTEXTURECAPS_POW2; desc_HEL.dpcTriCaps.dwTextureCaps |= D3DPTEXTURECAPS_POW2; - } else if (m_deviceGUID == IID_IDirect3DHALDevice) { + } else if (deviceGUID == IID_IDirect3DHALDevice) { desc_HEL.dcmColorModel = 0; } else { Logger::warn("D3D3Device::GetCaps: Unhandled device type"); @@ -189,7 +230,7 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE D3D3Device::SwapTextureHandles(IDirect3DTexture *tex1, IDirect3DTexture *tex2) { D3DDeviceLock lock = LockDevice(); - Logger::debug(">>> D3D5Device::SwapTextureHandles"); + Logger::debug(">>> D3D3Device::SwapTextureHandles"); D3D3Texture* texture1 = static_cast(tex1); D3D3Texture* texture2 = static_cast(tex2); @@ -200,8 +241,8 @@ namespace dxvk { const D3DTEXTUREHANDLE handle1 = commonTex1->GetTextureHandle(); const D3DTEXTUREHANDLE handle2 = commonTex2->GetTextureHandle(); - m_parent->GetCommonInterface()->ReleaseTextureHandle(handle1); - m_parent->GetCommonInterface()->ReleaseTextureHandle(handle2); + m_commonIntf->ReleaseTextureHandle(handle1); + m_commonIntf->ReleaseTextureHandle(handle2); commonTex1->SetTextureHandle(handle2); commonTex2->SetTextureHandle(handle1); @@ -369,13 +410,13 @@ namespace dxvk { RefreshLastUsedDevice(); - if (unlikely(m_inScene)) + if (unlikely(m_commonD3DDevice->IsInScene())) return D3DERR_SCENE_IN_SCENE; - HRESULT hr = m_d3d9->BeginScene(); + HRESULT hr = m_commonD3DDevice->GetD3D9Device()->BeginScene(); if (likely(SUCCEEDED(hr))) - m_inScene = true; + m_commonD3DDevice->SetInScene(true); return hr; } @@ -387,29 +428,13 @@ namespace dxvk { RefreshLastUsedDevice(); - if (unlikely(!m_inScene)) + if (unlikely(!m_commonD3DDevice->IsInScene())) return D3DERR_SCENE_NOT_IN_SCENE; - HRESULT hr = m_d3d9->EndScene(); - - if (likely(SUCCEEDED(hr))) { - const D3DOptions* d3dOptions = m_commonIntf->GetOptions(); - - if (d3dOptions->forceProxiedPresent) { - // If we have drawn anything, we need to make sure we blit back - // the results onto the D3D3 render target before we flip it - if (m_commonIntf->HasDrawn()) - BlitToDDrawSurface(m_rt->GetProxied(), m_rt->GetD3D9()); - - m_rt->GetProxied()->Flip(static_cast(m_commonIntf->GetFlipRTSurface()), - m_commonIntf->GetFlipRTFlags()); + HRESULT hr = m_commonD3DDevice->GetD3D9Device()->EndScene(); - if (likely(d3dOptions->backBufferGuard != D3DBackBufferGuard::Strict)) - m_commonIntf->ResetDrawTracking(); - } - - m_inScene = false; - } + if (likely(SUCCEEDED(hr))) + m_commonD3DDevice->SetInScene(false); return hr; } @@ -449,8 +474,7 @@ namespace dxvk { InitReturnPtr(buffer); - Com bufferProxy; - *buffer = ref(new D3D3ExecuteBuffer(std::move(bufferProxy), *desc, this)); + *buffer = ref(new D3D3ExecuteBuffer(*desc)); return D3D_OK; } @@ -463,15 +487,22 @@ namespace dxvk { if (unlikely(buffer == nullptr || viewport == nullptr)) return DDERR_INVALIDPARAMS; + d3d9::IDirect3DDevice9* device9 = m_commonD3DDevice->GetD3D9Device(); + D3D3ExecuteBuffer* d3d3ExecuteBuffer = static_cast(buffer); D3D3Viewport* d3d3Viewport = static_cast(viewport); - if (m_currentViewport != d3d3Viewport) + if (m_currentViewport != d3d3Viewport) { + if (likely(m_currentViewport != nullptr)) { + m_currentViewport->DeactivateLights(); + m_currentViewport->GetCommonViewport()->SetIsCurrentViewport(false); + } + m_currentViewport = d3d3Viewport; + } m_currentViewport->GetCommonViewport()->SetIsCurrentViewport(true); m_currentViewport->ApplyViewport(); - m_currentViewport->ApplyAndActivateLights(); D3DEXECUTEDATA data = d3d3ExecuteBuffer->GetExecuteData(); std::vector executeBuffer = d3d3ExecuteBuffer->GetBuffer(); @@ -483,31 +514,22 @@ namespace dxvk { D3DTLVERTEX* hVertexBuffer = reinterpret_cast(buf + data.dwHVertexOffset); uint8_t* ptr = buf + data.dwInstructionOffset; - uint8_t* end = ptr + data.dwInstructionLength; - while (ptr < end - sizeof(D3DINSTRUCTION)) { + // We can't rely on data.dwInstructionLength being correct. + while (true) { D3DINSTRUCTION* instruction = reinterpret_cast(ptr); - const uint8_t size = instruction->bSize; - const uint16_t count = instruction->wCount; - const uint32_t instructionSize = sizeof(D3DINSTRUCTION) + (count * size); - DWORD opcode = instruction->bOpcode; - uint8_t* operation = ptr + sizeof(D3DINSTRUCTION); - bool skip = false; - - if (opcode == D3DOP_EXIT) - break; + ptr += sizeof(D3DINSTRUCTION); + uint8_t* operation = ptr; - if (unlikely(ptr + instructionSize > end)) { - Logger::warn("D3D3Device::Execute: Reached the end but found no D3DOP_EXIT!"); + if (instruction->bOpcode == D3DOP_EXIT) break; - } - switch (opcode) { + switch (instruction->bOpcode) { case D3DOP_BRANCHFORWARD: { Logger::debug("D3D3Device::Execute: D3DOP_BRANCHFORWARD"); D3DBRANCH* branch = reinterpret_cast(operation); - for (uint16_t i = 0; i < count; i++) { + for (uint16_t i = 0; i < instruction->wCount; i++) { const D3DBRANCH& b = branch[i]; bool masked = (data.dsStatus.dwStatus & b.dwMask) == b.dwValue; @@ -516,9 +538,11 @@ namespace dxvk { } if (masked && b.dwOffset) { - ptr+= branch->dwOffset; - skip = true; + ptr = reinterpret_cast(instruction) + branch->dwOffset; + break; } + + ptr += instruction->bSize; } break; @@ -527,24 +551,27 @@ namespace dxvk { Logger::debug("D3D3Device::Execute: D3DOP_LINE"); D3DLINE* line = reinterpret_cast(operation); - DrawLineInternal(line, count, data.dwVertexCount, hVertexBuffer); + DrawLineInternal(line, instruction->wCount, data.dwVertexCount, hVertexBuffer); + ptr += instruction->bSize * instruction->wCount; break; } case D3DOP_POINT: { Logger::debug("D3D3Device::Execute: D3DOP_POINT"); D3DPOINT* point = reinterpret_cast(operation); - DrawPointInternal(point, count, data.dwVertexCount, hVertexBuffer); + DrawPointInternal(point, instruction->wCount, data.dwVertexCount, hVertexBuffer); + ptr += instruction->bSize * instruction->wCount; break; } case D3DOP_TRIANGLE: { Logger::debug("D3D3Device::Execute: D3DOP_TRIANGLE"); D3DTRIANGLE* triangle = reinterpret_cast(operation); - DrawTriangleInternal(triangle, count, data.dwVertexCount, hVertexBuffer); + DrawTriangleInternal(triangle, instruction->wCount, data.dwVertexCount, hVertexBuffer); + ptr += instruction->bSize * instruction->wCount; break; } case D3DOP_MATRIXLOAD: { @@ -552,7 +579,7 @@ namespace dxvk { D3DMATRIXLOAD* matrixLoad = reinterpret_cast(operation); - for (uint16_t i = 0; i < count; i++) { + for (uint16_t i = 0; i < instruction->wCount; i++) { D3DMATRIXLOAD& ml = matrixLoad[i]; D3DMATRIX srcMatrix; @@ -567,6 +594,7 @@ namespace dxvk { Logger::warn(str::format("D3D3Device::Execute: D3DOP_MATRIXLOAD failed to set matrix to destination: ", ml.hDestMatrix)); } + ptr += instruction->bSize * instruction->wCount; break; } case D3DOP_MATRIXMULTIPLY: { @@ -574,7 +602,7 @@ namespace dxvk { D3DMATRIXMULTIPLY* matrixMultiply = reinterpret_cast(operation); - for (uint16_t i = 0; i < count; i++) { + for (uint16_t i = 0; i < instruction->wCount; i++) { D3DMATRIXMULTIPLY& mm = matrixMultiply[i]; D3DMATRIX srcMatrix1; @@ -600,12 +628,13 @@ namespace dxvk { Logger::warn(str::format("D3D3Device::Execute: D3DOP_MATRIXMULTIPLY failed to set matrix to destination: ", mm.hDestMatrix)); } + ptr += instruction->bSize * instruction->wCount; break; } case D3DOP_PROCESSVERTICES: { D3DPROCESSVERTICES* processVertices = reinterpret_cast(operation); - for (uint16_t i = 0; i < count; i++) { + for (uint16_t i = 0; i < instruction->wCount; i++) { D3DPROCESSVERTICES& pv = processVertices[i]; const DWORD op = pv.dwFlags & D3DPROCESSVERTICES_OPMASK; @@ -619,60 +648,59 @@ namespace dxvk { } case D3DPROCESSVERTICES_TRANSFORM: case D3DPROCESSVERTICES_TRANSFORMLIGHT: { - Logger::debug("D3D3Device::Execute: D3DOP_PROCESSVERTICES TRANSFORM"); - D3DMATRIX world{}, view{}, projection{}; - HRESULT hr = m_d3d9->GetTransform(ConvertTransformState(D3DTRANSFORMSTATE_WORLD), &world); - if (FAILED(hr)) { - Logger::debug("D3D3Device::Execute: D3DOP_PROCESSVERTICES TRANSFORM failed to get world transform"); - } - hr = m_d3d9->GetTransform(d3d9::D3DTS_VIEW, &view); - if (FAILED(hr)) { - Logger::debug("D3D3Device::Execute: D3DOP_PROCESSVERTICES TRANSFORM failed to get view transform"); + const bool doLighting = op == D3DPROCESSVERTICES_TRANSFORMLIGHT; + + Logger::debug(str::format("D3D3Device::Execute: D3DOP_PROCESSVERTICES ", doLighting ? "TRANSFORMLIGHT" : "TRANSFORM")); + + D3DCommonViewport* commonViewport = m_currentViewport->GetCommonViewport(); + + ProcessVerticesData pvData; + pvData.inData = buf + data.dwVertexOffset + pv.wStart * sizeof(D3DVERTEX); + pvData.inFVF = doLighting ? D3DFVF_VERTEX : D3DFVF_LVERTEX; + pvData.inStride = sizeof(D3DVERTEX); + pvData.outData = buf + data.dwHVertexOffset + pv.wDest * sizeof(D3DTLVERTEX); + pvData.outFVF = D3DFVF_TLVERTEX; + pvData.outStride = sizeof(D3DTLVERTEX); + pvData.vertexCount = pv.dwCount; + pvData.correction = commonViewport->GetLegacyProjectionMatrix(0); + pvData.dsStatus = &data.dsStatus; + pvData.doLighting = doLighting; + pvData.doClipping = (flags & D3DEXECUTE_CLIPPED) && !(flags & D3DEXECUTE_UNCLIPPED); + pvData.doNotCopyData = pv.dwFlags & D3DPROCESSVERTICES_NOCOLOR; + pvData.doExtents = pv.dwFlags & D3DPROCESSVERTICES_UPDATEEXTENTS; + pvData.isLegacy = true; + + std::vector> lights = commonViewport->GetLights(); + std::vector lights9; + + for (auto light: lights) { + if (!light->IsActive()) + continue; + + const d3d9::D3DLIGHT9* light9 = light->GetD3D9Light(); + if (light9 != nullptr) + lights9.push_back(*light9); } - hr = m_d3d9->GetTransform(d3d9::D3DTS_PROJECTION, &projection); - if (FAILED(hr)) { - Logger::debug("D3D3Device::Execute: D3DOP_PROCESSVERTICES TRANSFORM failed to get projection transform"); - } - - Matrix4 wv = MatrixD3DTo4(&view) * MatrixD3DTo4(&world); - Matrix4 wvp = MatrixD3DTo4(&projection) * wv; - d3d9::D3DVIEWPORT9* m_viewport9 = m_currentViewport->GetCommonViewport()->GetD3D9Viewport(); - D3DVECTOR* m_legacyScale = m_currentViewport->GetCommonViewport()->GetLegacyScale(); - for (DWORD t = 0; t < pv.dwCount; t++) { - D3DVERTEX& in = (vertexBuffer + pv.wStart)[t]; - D3DTLVERTEX& out = (hVertexBuffer + pv.wDest)[t]; + pvData.lights = &lights9; - //Logger::debug(str::format("D3D3Device::Execute: D3DOP_PROCESSVERTICES TRANSFORM INPUT: in: ", in.x, ", ", in.y, ", ", in.z, ", start: ", pv.wStart + t * sizeof(D3DVERTEX), ", dest: ", pv.wDest + t * sizeof(D3DTLVERTEX))); + ProcessVerticesSW(device9, m_commonIntf->GetOptions(), &pvData); - Vector4 h = wvp * Vector4({in.x, in.y, in.z, 1.0f}); - out.rhw = (h.w != 0.0f) ? (1.0f / h.w) : 0.0f; - out.sx = (m_viewport9->X + (float)m_viewport9->Width * 0.5) + (h.x * out.rhw) * (m_legacyScale->x * (float)m_viewport9->Width * 0.5); - out.sy = (m_viewport9->Y + (float)m_viewport9->Height * 0.5) - (h.y * out.rhw) * (m_legacyScale->y * (float)m_viewport9->Height * 0.5); - out.sz = m_viewport9->MinZ + (h.z * out.rhw) * (m_viewport9->MaxZ - m_viewport9->MinZ); - - // TODO: D3DPROCESSVERTICES_TRANSFORMLIGHT, D3DPROCESSVERTICES_NOCOLOR, D3DPROCESSVERTICES_UPDATEEXTENTS - out.color = 0xFFFFFFFF; - out.specular = 0; - - out.tu = in.tu; - out.tv = in.tv; - - //Logger::debug(str::format("D3D3Device::Execute: D3DOP_PROCESSVERTICES TRANSFORM OUTPUT: out: ", out.sx, ", ", out.sy, ", ", out.sz, ", rhw: ", out.rhw)); - } break; } } } + ptr += instruction->bSize * instruction->wCount; break; } case D3DOP_SPAN: { Logger::warn("D3D3Device::Execute: D3DOP_SPAN"); D3DSPAN* span = reinterpret_cast(operation); - DrawSpanInternal(span, count, data.dwVertexCount, hVertexBuffer); + DrawSpanInternal(span, instruction->wCount, data.dwVertexCount, hVertexBuffer); + ptr += instruction->bSize * instruction->wCount; break; } case D3DOP_STATELIGHT: { @@ -680,11 +708,12 @@ namespace dxvk { D3DSTATE* state = reinterpret_cast(operation); - for (uint16_t i = 0; i < count; i++) { + for (uint16_t i = 0; i < instruction->wCount; i++) { const D3DSTATE& s = state[i]; SetLightStateInternal(s.dlstLightStateType, s.dwArg[0]); } + ptr += instruction->bSize * instruction->wCount; break; } case D3DOP_STATERENDER: { @@ -692,11 +721,12 @@ namespace dxvk { D3DSTATE* state = reinterpret_cast(operation); - for (uint16_t i = 0; i < count; i++) { + for (uint16_t i = 0; i < instruction->wCount; i++) { const D3DSTATE& s = state[i]; SetRenderStateInternal(s.drstRenderStateType, s.dwArg[0]); } + ptr += instruction->bSize * instruction->wCount; break; } case D3DOP_STATETRANSFORM: { @@ -705,7 +735,7 @@ namespace dxvk { D3DSTATE* state = reinterpret_cast(operation); D3DMATRIX matrix; - for (uint16_t i = 0; i < count; i++) { + for (uint16_t i = 0; i < instruction->wCount; i++) { const D3DSTATE& s = state[i]; if (unlikely(s.dwArg[0] == 0)) @@ -715,7 +745,7 @@ namespace dxvk { if (unlikely(FAILED(hr))) continue; - hr = m_d3d9->SetTransform(ConvertTransformState(s.dtstTransformStateType), &matrix); + hr = device9->SetTransform(ConvertTransformState(s.dtstTransformStateType), &matrix); if (likely(SUCCEEDED(hr))) { if (s.dtstTransformStateType == D3DTRANSFORMSTATE_WORLD) { m_worldHandle = s.dwArg[0]; @@ -729,36 +759,37 @@ namespace dxvk { } } + ptr += instruction->bSize * instruction->wCount; break; } case D3DOP_SETSTATUS: { Logger::debug("D3D3Device::Execute: D3DOP_SETSTATUS"); D3DSTATUS* status = reinterpret_cast(operation); - for (uint16_t i = 0; i < count; i++) { + for (uint16_t i = 0; i < instruction->wCount; i++) { data.dsStatus = status[i]; } + ptr += instruction->bSize * instruction->wCount; break; } case D3DOP_TEXTURELOAD: { Logger::debug("D3D3Device::Execute: D3DOP_TEXTURELOAD"); D3DTEXTURELOAD* textureLoad = reinterpret_cast(operation); - TextureLoadInternal(textureLoad, count); + TextureLoadInternal(textureLoad, instruction->wCount); + ptr += instruction->bSize * instruction->wCount; break; } default: - Logger::err(str::format("D3D3Device::Execute: Unknown opcode encountered: ", opcode)); + Logger::err(str::format("D3D3Device::Execute: Unknown opcode encountered: ", static_cast(instruction->bOpcode))); + ptr += instruction->bSize * instruction->wCount; break; } - - if (!skip) - ptr += instructionSize; } - m_commonIntf->UpdateDrawTracking(); + d3d3ExecuteBuffer->SetExecuteData(data); return D3D_OK; } @@ -837,7 +868,7 @@ namespace dxvk { } if (transformType) { - HRESULT hr = m_d3d9->SetTransform(ConvertTransformState(transformType), matrix); + HRESULT hr = m_commonD3DDevice->GetD3D9Device()->SetTransform(ConvertTransformState(transformType), matrix); if (unlikely(FAILED(hr))) Logger::warn("D3D3Device::SetMatrix: Failed to update D3D9 transform"); } @@ -890,9 +921,11 @@ namespace dxvk { return D3D_OK; } + // Not called when the device is created from a D3D5/6 device void D3D3Device::InitializeDS() { - if (!m_rt->IsInitialized()) - m_rt->InitializeD3D9RenderTarget(); + d3d9::IDirect3DDevice9* device9 = m_commonD3DDevice->GetD3D9Device(); + + m_rt->InitializeD3D9RenderTarget(); m_ds = m_rt->GetAttachedDepthStencil(); @@ -902,8 +935,7 @@ namespace dxvk { HRESULT hrDS = m_ds->InitializeD3D9DepthStencil(); if (unlikely(FAILED(hrDS))) { Logger::err("D3D3Device::InitializeDS: Failed to initialize D3D9 DS"); - } else if (m_commonD3DDevice->GetD3D5Device() == nullptr && - m_commonD3DDevice->GetD3D6Device() == nullptr) { + } else { Logger::info("D3D3Device::InitializeDS: Got depth stencil from RT"); DDSURFACEDESC descDS; @@ -911,23 +943,38 @@ namespace dxvk { m_ds->GetProxied()->GetSurfaceDesc(&descDS); Logger::debug(str::format("D3D3Device::InitializeDS: DepthStencil: ", descDS.dwWidth, "x", descDS.dwHeight)); - HRESULT hrDS9 = m_d3d9->SetDepthStencilSurface(m_ds->GetD3D9()); + HRESULT hrDS9 = device9->SetDepthStencilSurface(m_ds->GetCommonSurface()->GetD3D9Surface()); if(unlikely(FAILED(hrDS9))) { Logger::err("D3D3Device::InitializeDS: Failed to set D3D9 depth stencil"); } else { // This needs to act like an auto depth stencil of sorts, so manually enable z-buffering - m_d3d9->SetRenderState(d3d9::D3DRS_ZENABLE, d3d9::D3DZB_TRUE); + device9->SetRenderState(d3d9::D3DRS_ZENABLE, d3d9::D3DZB_TRUE); } } - } else if (m_commonD3DDevice->GetD3D5Device() == nullptr && - m_commonD3DDevice->GetD3D6Device() == nullptr) { + } else { Logger::info("D3D3Device::InitializeDS: RT has no depth stencil attached"); - m_d3d9->SetDepthStencilSurface(nullptr); + device9->SetDepthStencilSurface(nullptr); // Should be superfluous, but play it safe - m_d3d9->SetRenderState(d3d9::D3DRS_ZENABLE, d3d9::D3DZB_FALSE); + device9->SetRenderState(d3d9::D3DRS_ZENABLE, d3d9::D3DZB_FALSE); } } + void D3D3Device::UpdateSurfaceDirtyTracking(bool dirtyRenderTarget, bool dirtyDepthStencil, bool dirtyPrimarySurface) { + if(likely(dirtyRenderTarget)) + m_rt->GetCommonSurface()->DirtyD3D9Surface(); + + if (likely(dirtyPrimarySurface)) { + DDrawCommonSurface* primarySurface = m_commonIntf->GetPrimarySurface(); + // The primary surface can be bound as RT, in which case it will + // get dirtied twice, but we have no guarantees that will happen + if (likely(primarySurface != nullptr)) + primarySurface->DirtyD3D9Surface(); + } + + if (likely(dirtyDepthStencil && m_ds != nullptr)) + m_ds->GetCommonSurface()->DirtyD3D9Surface(); + } + inline void D3D3Device::AddViewportInternal(IDirect3DViewport* viewport) { D3D3Viewport* d3d3Viewport = static_cast(viewport); @@ -955,38 +1002,24 @@ namespace dxvk { inline HRESULT STDMETHODCALLTYPE D3D3Device::SetLightStateInternal(D3DLIGHTSTATETYPE dwLightStateType, DWORD dwLightState) { Logger::debug(">>> D3D3Device::SetLightStateInternal"); + d3d9::IDirect3DDevice9* device9 = m_commonD3DDevice->GetD3D9Device(); + switch (dwLightStateType) { case D3DLIGHTSTATE_MATERIAL: { - D3D5Device* device5 = m_commonD3DDevice->GetD3D5Device(); - D3D6Device* device6 = m_commonD3DDevice->GetD3D6Device(); - if (unlikely(!dwLightState)) { - m_materialHandle = dwLightState; - - if (device5 != nullptr) - device5->SetCurrentMaterialHandle(dwLightState); - else if (unlikely(device6 != nullptr)) - device6->SetCurrentMaterialHandle(dwLightState); - + m_commonD3DDevice->SetCurrentMaterialHandle(dwLightState); return D3D_OK; } - Logger::debug(str::format("D3D3Device::SetLightStateInternal: Applying material nr. ", dwLightState, " to D3D9")); - D3DCommonInterface* commonD3DIntf = m_commonD3DDevice->GetCommonD3DInterface(); if (likely(commonD3DIntf != nullptr)) { d3d9::D3DMATERIAL9* material9 = commonD3DIntf->GetD3D9MaterialFromHandle(dwLightState); if (unlikely(material9 == nullptr)) return DDERR_INVALIDPARAMS; - m_d3d9->SetMaterial(material9); - - m_materialHandle = dwLightState; - if (device5 != nullptr) { - device5->SetCurrentMaterialHandle(dwLightState); - } else if (unlikely(device6 != nullptr)) { - device6->SetCurrentMaterialHandle(dwLightState); - } + m_commonD3DDevice->SetCurrentMaterialHandle(dwLightState); + Logger::debug(str::format("D3D3Device::SetLightStateInternal: Applying material nr. ", dwLightState, " to D3D9")); + device9->SetMaterial(material9); } else { Logger::warn("D3D3Device::SetLightStateInternal: Unable to set D3D9 material"); } @@ -994,23 +1027,23 @@ namespace dxvk { break; } case D3DLIGHTSTATE_AMBIENT: - m_d3d9->SetRenderState(d3d9::D3DRS_AMBIENT, dwLightState); + device9->SetRenderState(d3d9::D3DRS_AMBIENT, dwLightState); break; case D3DLIGHTSTATE_COLORMODEL: if (unlikely(dwLightState != D3DCOLOR_RGB)) Logger::warn("D3D3Device::SetLightStateInternal: Unsupported D3DLIGHTSTATE_COLORMODEL"); break; case D3DLIGHTSTATE_FOGMODE: - m_d3d9->SetRenderState(d3d9::D3DRS_FOGVERTEXMODE, dwLightState); + device9->SetRenderState(d3d9::D3DRS_FOGVERTEXMODE, dwLightState); break; case D3DLIGHTSTATE_FOGSTART: - m_d3d9->SetRenderState(d3d9::D3DRS_FOGSTART, dwLightState); + device9->SetRenderState(d3d9::D3DRS_FOGSTART, dwLightState); break; case D3DLIGHTSTATE_FOGEND: - m_d3d9->SetRenderState(d3d9::D3DRS_FOGEND, dwLightState); + device9->SetRenderState(d3d9::D3DRS_FOGEND, dwLightState); break; case D3DLIGHTSTATE_FOGDENSITY: - m_d3d9->SetRenderState(d3d9::D3DRS_FOGDENSITY, dwLightState); + device9->SetRenderState(d3d9::D3DRS_FOGDENSITY, dwLightState); break; default: return DDERR_INVALIDPARAMS; @@ -1029,6 +1062,7 @@ namespace dxvk { return D3D_OK; } + d3d9::IDirect3DDevice9* device9 = m_commonD3DDevice->GetD3D9Device(); d3d9::D3DRENDERSTATETYPE State9 = d3d9::D3DRENDERSTATETYPE(dwRenderStateType); switch (dwRenderStateType) { @@ -1067,8 +1101,8 @@ namespace dxvk { } case D3DRENDERSTATE_TEXTUREADDRESS: - m_d3d9->SetSamplerState(0, d3d9::D3DSAMP_ADDRESSU, dwRenderState); - m_d3d9->SetSamplerState(0, d3d9::D3DSAMP_ADDRESSV, dwRenderState); + device9->SetSamplerState(0, d3d9::D3DSAMP_ADDRESSU, dwRenderState); + device9->SetSamplerState(0, d3d9::D3DSAMP_ADDRESSV, dwRenderState); return D3D_OK; // Always enabled on later APIs, though default FALSE in D3D3 @@ -1078,11 +1112,11 @@ namespace dxvk { // Not implemented in DXVK, but forward it anyway case D3DRENDERSTATE_WRAPU: { DWORD value9 = 0; - m_d3d9->GetRenderState(d3d9::D3DRS_WRAP0, &value9); + device9->GetRenderState(d3d9::D3DRS_WRAP0, &value9); if (dwRenderState == TRUE) { - m_d3d9->SetRenderState(d3d9::D3DRS_WRAP0, value9 & D3DWRAP_U); + device9->SetRenderState(d3d9::D3DRS_WRAP0, value9 & D3DWRAP_U); } else { - m_d3d9->SetRenderState(d3d9::D3DRS_WRAP0, value9 & ~D3DWRAP_U); + device9->SetRenderState(d3d9::D3DRS_WRAP0, value9 & ~D3DWRAP_U); } return D3D_OK; } @@ -1090,11 +1124,11 @@ namespace dxvk { // Not implemented in DXVK, but forward it anyway case D3DRENDERSTATE_WRAPV: { DWORD value9 = 0; - m_d3d9->GetRenderState(d3d9::D3DRS_WRAP0, &value9); + device9->GetRenderState(d3d9::D3DRS_WRAP0, &value9); if (dwRenderState == TRUE) { - m_d3d9->SetRenderState(d3d9::D3DRS_WRAP0, value9 & D3DWRAP_V); + device9->SetRenderState(d3d9::D3DRS_WRAP0, value9 & D3DWRAP_V); } else { - m_d3d9->SetRenderState(d3d9::D3DRS_WRAP0, value9 & ~D3DWRAP_V); + device9->SetRenderState(d3d9::D3DRS_WRAP0, value9 & ~D3DWRAP_V); } return D3D_OK; } @@ -1135,7 +1169,7 @@ namespace dxvk { switch (dwRenderState) { case D3DFILTER_NEAREST: case D3DFILTER_LINEAR: - m_d3d9->SetSamplerState(0, d3d9::D3DSAMP_MAGFILTER, dwRenderState); + device9->SetSamplerState(0, d3d9::D3DSAMP_MAGFILTER, dwRenderState); break; default: break; @@ -1147,29 +1181,29 @@ namespace dxvk { switch (dwRenderState) { case D3DFILTER_NEAREST: case D3DFILTER_LINEAR: - m_d3d9->SetSamplerState(0, d3d9::D3DSAMP_MINFILTER, dwRenderState); - m_d3d9->SetSamplerState(0, d3d9::D3DSAMP_MIPFILTER, d3d9::D3DTEXF_NONE); + device9->SetSamplerState(0, d3d9::D3DSAMP_MINFILTER, dwRenderState); + device9->SetSamplerState(0, d3d9::D3DSAMP_MIPFILTER, d3d9::D3DTEXF_NONE); break; // "The closest mipmap level is chosen and a point filter is applied." case D3DFILTER_MIPNEAREST: - m_d3d9->SetSamplerState(0, d3d9::D3DSAMP_MINFILTER, d3d9::D3DTEXF_POINT); - m_d3d9->SetSamplerState(0, d3d9::D3DSAMP_MIPFILTER, d3d9::D3DTEXF_POINT); + device9->SetSamplerState(0, d3d9::D3DSAMP_MINFILTER, d3d9::D3DTEXF_POINT); + device9->SetSamplerState(0, d3d9::D3DSAMP_MIPFILTER, d3d9::D3DTEXF_POINT); break; // "The closest mipmap level is chosen and a bilinear filter is applied within it." case D3DFILTER_MIPLINEAR: - m_d3d9->SetSamplerState(0, d3d9::D3DSAMP_MINFILTER, d3d9::D3DTEXF_LINEAR); - m_d3d9->SetSamplerState(0, d3d9::D3DSAMP_MIPFILTER, d3d9::D3DTEXF_POINT); + device9->SetSamplerState(0, d3d9::D3DSAMP_MINFILTER, d3d9::D3DTEXF_LINEAR); + device9->SetSamplerState(0, d3d9::D3DSAMP_MIPFILTER, d3d9::D3DTEXF_POINT); break; // "The two closest mipmap levels are chosen and then a linear // blend is used between point filtered samples of each level." case D3DFILTER_LINEARMIPNEAREST: - m_d3d9->SetSamplerState(0, d3d9::D3DSAMP_MINFILTER, d3d9::D3DTEXF_POINT); - m_d3d9->SetSamplerState(0, d3d9::D3DSAMP_MIPFILTER, d3d9::D3DTEXF_LINEAR); + device9->SetSamplerState(0, d3d9::D3DSAMP_MINFILTER, d3d9::D3DTEXF_POINT); + device9->SetSamplerState(0, d3d9::D3DSAMP_MIPFILTER, d3d9::D3DTEXF_LINEAR); break; // "The two closest mipmap levels are chosen and then combined using a bilinear filter." case D3DFILTER_LINEARMIPLINEAR: - m_d3d9->SetSamplerState(0, d3d9::D3DSAMP_MINFILTER, d3d9::D3DTEXF_LINEAR); - m_d3d9->SetSamplerState(0, d3d9::D3DSAMP_MIPFILTER, d3d9::D3DTEXF_LINEAR); + device9->SetSamplerState(0, d3d9::D3DSAMP_MINFILTER, d3d9::D3DTEXF_LINEAR); + device9->SetSamplerState(0, d3d9::D3DSAMP_MIPFILTER, d3d9::D3DTEXF_LINEAR); break; default: break; @@ -1178,19 +1212,19 @@ namespace dxvk { } case D3DRENDERSTATE_TEXTUREMAPBLEND: - m_textureMapBlend = dwRenderState; + m_commonD3DDevice->SetTextureMapBlend(dwRenderState); switch (dwRenderState) { // "In this mode, the RGB and alpha values of the texture replace // the colors that would have been used with no texturing." case D3DTBLEND_DECAL: case D3DTBLEND_COPY: - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG1, D3DTA_TEXTURE); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG1, D3DTA_TEXTURE); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_COLOROP, D3DTOP_SELECTARG1); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAOP, D3DTOP_SELECTARG1); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG2, D3DTA_CURRENT); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG2, D3DTA_CURRENT); + device9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG1, D3DTA_TEXTURE); + device9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG1, D3DTA_TEXTURE); + device9->SetTextureStageState(0, d3d9::D3DTSS_COLOROP, D3DTOP_SELECTARG1); + device9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAOP, D3DTOP_SELECTARG1); + device9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG2, D3DTA_CURRENT); + device9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG2, D3DTA_CURRENT); break; // "In this mode, the RGB values of the texture are multiplied with the RGB values // that would have been used with no texturing. Any alpha values in the texture @@ -1198,43 +1232,43 @@ namespace dxvk { // if the texture does not contain an alpha component, alpha values at the vertices // in the source are interpolated between vertices." case D3DTBLEND_MODULATE: - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG1, D3DTA_TEXTURE); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG1, D3DTA_TEXTURE); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_COLOROP, D3DTOP_MODULATE); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAOP, D3DTOP_SELECTARG1); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG2, D3DTA_DIFFUSE); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG2, D3DTA_DIFFUSE); + device9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG1, D3DTA_TEXTURE); + device9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG1, D3DTA_TEXTURE); + device9->SetTextureStageState(0, d3d9::D3DTSS_COLOROP, D3DTOP_MODULATE); + device9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAOP, D3DTOP_SELECTARG1); + device9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG2, D3DTA_DIFFUSE); + device9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG2, D3DTA_DIFFUSE); break; // "In this mode, the RGB and alpha values of the texture are blended with the colors // that would have been used with no texturing, according to the following formulas [...]" case D3DTBLEND_DECALALPHA: - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG1, D3DTA_TEXTURE); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG1, D3DTA_TEXTURE); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_COLOROP, D3DTOP_BLENDTEXTUREALPHA); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAOP, D3DTOP_SELECTARG2); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG2, D3DTA_DIFFUSE); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG2, D3DTA_DIFFUSE); + device9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG1, D3DTA_TEXTURE); + device9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG1, D3DTA_TEXTURE); + device9->SetTextureStageState(0, d3d9::D3DTSS_COLOROP, D3DTOP_BLENDTEXTUREALPHA); + device9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAOP, D3DTOP_SELECTARG2); + device9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG2, D3DTA_DIFFUSE); + device9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG2, D3DTA_DIFFUSE); break; // "In this mode, the RGB values of the texture are multiplied with the RGB values that // would have been used with no texturing, and the alpha values of the texture // are multiplied with the alpha values that would have been used with no texturing." case D3DTBLEND_MODULATEALPHA: - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG1, D3DTA_TEXTURE); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG1, D3DTA_TEXTURE); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_COLOROP, D3DTOP_MODULATE); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAOP, D3DTOP_MODULATE); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG2, D3DTA_DIFFUSE); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG2, D3DTA_DIFFUSE); + device9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG1, D3DTA_TEXTURE); + device9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG1, D3DTA_TEXTURE); + device9->SetTextureStageState(0, d3d9::D3DTSS_COLOROP, D3DTOP_MODULATE); + device9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAOP, D3DTOP_MODULATE); + device9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG2, D3DTA_DIFFUSE); + device9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG2, D3DTA_DIFFUSE); break; // "Add the Gouraud interpolants to the texture lookup with saturation semantics // (that is, if the color value overflows it is set to the maximum possible value)." case D3DTBLEND_ADD: - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG1, D3DTA_TEXTURE); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG1, D3DTA_TEXTURE); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_COLOROP, D3DTOP_ADD); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAOP, D3DTOP_SELECTARG2); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG2, D3DTA_DIFFUSE); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG2, D3DTA_DIFFUSE); + device9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG1, D3DTA_TEXTURE); + device9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG1, D3DTA_TEXTURE); + device9->SetTextureStageState(0, d3d9::D3DTSS_COLOROP, D3DTOP_ADD); + device9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAOP, D3DTOP_SELECTARG2); + device9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG2, D3DTA_DIFFUSE); + device9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG2, D3DTA_DIFFUSE); break; // Unsupported default: @@ -1323,10 +1357,16 @@ namespace dxvk { } // This call will never fail - return m_d3d9->SetRenderState(State9, dwRenderState); + return device9->SetRenderState(State9, dwRenderState); } inline void D3D3Device::DrawTriangleInternal(D3DTRIANGLE* triangle, uint16_t count, DWORD vertexCount, const D3DTLVERTEX* vertexBuffer) { + d3d9::IDirect3DDevice9* device9 = m_commonD3DDevice->GetD3D9Device(); + + m_rt->InitializeOrUploadD3D9(); + if (likely(m_ds != nullptr)) + m_ds->InitializeOrUploadD3D9(); + std::vector vertices; for (uint16_t i = 0; i < count; i++) { @@ -1349,15 +1389,17 @@ namespace dxvk { vertices.push_back(vertexBuffer[t.v3]); } - if (!vertices.empty() && m_d3d9 != nullptr) { - m_d3d9->SetFVF(D3DFVF_TLVERTEX); - HRESULT hr = m_d3d9->DrawPrimitiveUP( + if (likely(!vertices.empty())) { + device9->SetFVF(D3DFVF_TLVERTEX); + HRESULT hr = device9->DrawPrimitiveUP( d3d9::D3DPT_TRIANGLELIST, GetPrimitiveCount(D3DPT_TRIANGLELIST, vertices.size()), vertices.data(), GetFVFSize(D3DFVF_TLVERTEX)); if (SUCCEEDED(hr)) { + UpdateSurfaceDirtyTracking(true, true, true); + Logger::debug(str::format("D3D3Device::Execute: D3DOP_TRIANGLE drawn vertices: ", vertices.size())); m_stats.dwTrianglesDrawn += std::max(vertices.size() / 3, 0u); } else { @@ -1369,6 +1411,12 @@ namespace dxvk { } inline void D3D3Device::DrawLineInternal(D3DLINE* line, uint16_t count, DWORD vertexCount, const D3DTLVERTEX* vertexBuffer) { + d3d9::IDirect3DDevice9* device9 = m_commonD3DDevice->GetD3D9Device(); + + m_rt->InitializeOrUploadD3D9(); + if (likely(m_ds != nullptr)) + m_ds->InitializeOrUploadD3D9(); + std::vector vertices; for (uint16_t i = 0; i < count; i++) { @@ -1381,15 +1429,17 @@ namespace dxvk { vertices.push_back(vertexBuffer[l.v2]); } - if (!vertices.empty() && m_d3d9 != nullptr) { - m_d3d9->SetFVF(D3DFVF_TLVERTEX); - HRESULT hr = m_d3d9->DrawPrimitiveUP( + if (likely(!vertices.empty())) { + device9->SetFVF(D3DFVF_TLVERTEX); + HRESULT hr = device9->DrawPrimitiveUP( d3d9::D3DPT_LINELIST, GetPrimitiveCount(D3DPT_LINELIST, vertices.size()), vertices.data(), GetFVFSize(D3DFVF_TLVERTEX)); if (SUCCEEDED(hr)) { + UpdateSurfaceDirtyTracking(true, true, true); + Logger::debug(str::format("D3D3Device::Execute: D3DOP_LINE drawn vertices: ", vertices.size())); m_stats.dwLinesDrawn += std::max(vertices.size() / 2, 0u); } else { @@ -1401,6 +1451,12 @@ namespace dxvk { } inline void D3D3Device::DrawPointInternal(D3DPOINT* point, uint16_t count, DWORD vertexCount, const D3DTLVERTEX* vertexBuffer) { + d3d9::IDirect3DDevice9* device9 = m_commonD3DDevice->GetD3D9Device(); + + m_rt->InitializeOrUploadD3D9(); + if (likely(m_ds != nullptr)) + m_ds->InitializeOrUploadD3D9(); + std::vector vertices; for (uint16_t i = 0; i < count; i++) { @@ -1414,15 +1470,17 @@ namespace dxvk { } } - if (!vertices.empty() && m_d3d9 != nullptr) { - m_d3d9->SetFVF(D3DFVF_TLVERTEX); - HRESULT hr = m_d3d9->DrawPrimitiveUP( + if (likely(!vertices.empty())) { + device9->SetFVF(D3DFVF_TLVERTEX); + HRESULT hr = device9->DrawPrimitiveUP( d3d9::D3DPT_POINTLIST, GetPrimitiveCount(D3DPT_POINTLIST, vertices.size()), vertices.data(), GetFVFSize(D3DFVF_TLVERTEX)); if (SUCCEEDED(hr)) { + UpdateSurfaceDirtyTracking(true, true, true); + Logger::debug(str::format("D3D3Device::Execute: D3DOP_POINT drawn vertices: ", vertices.size())); m_stats.dwPointsDrawn += static_cast(vertices.size()); } else { @@ -1433,6 +1491,12 @@ namespace dxvk { } inline void D3D3Device::DrawSpanInternal(D3DSPAN* span, uint16_t count, DWORD vertexCount, const D3DTLVERTEX* vertexBuffer) { + d3d9::IDirect3DDevice9* device9 = m_commonD3DDevice->GetD3D9Device(); + + m_rt->InitializeOrUploadD3D9(); + if (likely(m_ds != nullptr)) + m_ds->InitializeOrUploadD3D9(); + std::vector vertices; for (uint16_t i = 0; i < count; i++) { @@ -1446,15 +1510,17 @@ namespace dxvk { } } - if (!vertices.empty() && m_d3d9 != nullptr) { - m_d3d9->SetFVF(D3DFVF_TLVERTEX); - HRESULT hr = m_d3d9->DrawPrimitiveUP( + if (likely(!vertices.empty())) { + device9->SetFVF(D3DFVF_TLVERTEX); + HRESULT hr = device9->DrawPrimitiveUP( d3d9::D3DPT_LINESTRIP, GetPrimitiveCount(D3DPT_LINESTRIP, vertices.size()), vertices.data(), GetFVFSize(D3DFVF_TLVERTEX)); if (SUCCEEDED(hr)) { + UpdateSurfaceDirtyTracking(true, true, true); + Logger::debug(str::format("D3D3Device::Execute: D3DOP_SPAN drawn vertices: ", vertices.size())); m_stats.dwSpansDrawn += std::max(vertices.size() - 1, 0u); } else { @@ -1484,16 +1550,18 @@ namespace dxvk { HRESULT hr; + d3d9::IDirect3DDevice9* device9 = m_commonD3DDevice->GetD3D9Device(); + // Unbinding texture stages if (surface == nullptr) { Logger::debug("D3D3Device::SetTextureInternal: Unbiding D3D9 texture"); - hr = m_d3d9->SetTexture(0, nullptr); + hr = device9->SetTexture(0, nullptr); if (likely(SUCCEEDED(hr))) { - if (m_textureHandle != 0) { + if (m_commonD3DDevice->GetCurrentTextureHandle() != 0) { Logger::debug("D3D3Device::SetTextureInternal: Unbinding local texture"); - m_textureHandle = 0; + m_commonD3DDevice->SetCurrentTextureHandle(0); } } else { Logger::err("D3D3Device::SetTextureInternal: Failed to unbind D3D9 texture"); @@ -1504,33 +1572,25 @@ namespace dxvk { Logger::debug("D3D3Device::SetTextureInternal: Binding D3D9 texture"); - // Only upload textures if any sort of blit/lock operation - // has been performed on them since the last SetTexture call, - // or textures which have been used on a different device, and - // need their D3D9 object to be reinitialized at this point - if (surface->GetCommonSurface()->HasDirtyMipMaps() || - unlikely(surface->GetD3D9Device() != m_d3d9.ptr())) { - hr = surface->InitializeOrUploadD3D9(); - if (unlikely(FAILED(hr))) { - Logger::err("D3D3Device::SetTextureInternal: Failed to initialize/upload D3D9 texture"); - return hr; - } + // If textures have been used on a different device, they + // will get their D3D9 object reinitialized at this point + if (unlikely(surface->GetCommonSurface()->GetCommonD3DDevice() != m_commonD3DDevice.ptr())) + surface->GetCommonSurface()->DirtyDDrawSurface(); - surface->GetCommonSurface()->UnDirtyMipMaps(); - } else { - Logger::debug("D3D3Device::SetTextureInternal: Skipping upload of texture and mip maps"); + hr = surface->InitializeOrUploadD3D9(); + if (unlikely(FAILED(hr))) { + Logger::err("D3D3Device::SetTexture: Failed to initialize/upload D3D9 texture"); + return hr; } - // Only fast skip on D3D9 side, since we want to ensure - // color keying is applied properly even in the case - // of the same texture being set again (color key may change) - //if (unlikely(m_textureHandle == textureHandle)) + // Don't fast skip, since color key might change + //if (unlikely(m_commonD3DDevice->GetCurrentTextureHandle() == textureHandle)) //return D3D_OK; - d3d9::IDirect3DTexture9* tex9 = surface->GetD3D9Texture(); + d3d9::IDirect3DTexture9* tex9 = surface->GetCommonSurface()->GetD3D9Texture(); if (likely(tex9 != nullptr)) { - hr = m_d3d9->SetTexture(0, tex9); + hr = device9->SetTexture(0, tex9); if (unlikely(FAILED(hr))) { Logger::warn("D3D3Device::SetTextureInternal: Failed to bind D3D9 texture"); return hr; @@ -1539,9 +1599,9 @@ namespace dxvk { // "Any alpha values in the texture replace the alpha values in the colors that would // have been used with no texturing; if the texture does not contain an alpha component, // alpha values at the vertices in the source are interpolated between vertices." - if (m_textureMapBlend == D3DTBLEND_MODULATE) { + if (m_commonD3DDevice->GetTextureMapBlend() == D3DTBLEND_MODULATE) { const DWORD textureOp = surface->GetCommonSurface()->IsAlphaFormat() ? D3DTOP_SELECTARG1 : D3DTOP_MODULATE; - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAOP, textureOp); + device9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAOP, textureOp); } // D3D3 enables color key transparency globally @@ -1555,7 +1615,7 @@ namespace dxvk { } } - m_textureHandle = textureHandle; + m_commonD3DDevice->SetCurrentTextureHandle(textureHandle); return D3D_OK; } diff --git a/src/ddraw/d3d3/d3d3_device.h b/src/ddraw/d3d3/d3d3_device.h index 86312c8aaf8..9fb44aefdb2 100644 --- a/src/ddraw/d3d3/d3d3_device.h +++ b/src/ddraw/d3d3/d3d3_device.h @@ -25,7 +25,7 @@ namespace dxvk { /** * \brief D3D3 device implementation */ - class D3D3Device final : public DDrawWrappedObject { + class D3D3Device final : public DDrawWrappedObject { public: D3D3Device( @@ -86,6 +86,8 @@ namespace dxvk { void InitializeDS(); + void UpdateSurfaceDirtyTracking(bool dirtyRenderTarget, bool dirtyDepthStencil, bool dirtyPrimarySurface); + D3DCommonDevice* GetCommonD3DDevice() { return m_commonD3DDevice.ptr(); } @@ -94,22 +96,10 @@ namespace dxvk { return m_multithread.AcquireLock(); } - void EnableLegacyLights(bool isD3DLight2) { - m_bridge->SetLegacyLightsState(true, isD3DLight2); - } - D3DSTATS GetStatsInternal() const { return m_stats; } - d3d9::D3DPRESENT_PARAMETERS GetPresentParameters() const { - return m_params9; - } - - d3d9::D3DMULTISAMPLE_TYPE GetMultiSampleType() const { - return m_params9.MultiSampleType; - } - DDrawSurface* GetRenderTarget() const { return m_rt.ptr(); } @@ -122,17 +112,8 @@ namespace dxvk { return m_currentViewport.ptr(); } - D3DMATERIALHANDLE GetCurrentMaterialHandle() const { - return m_materialHandle; - } - private: - inline void RefreshLastUsedDevice() { - if (unlikely(m_commonIntf->GetCommonD3DDevice() != m_commonD3DDevice.ptr())) - m_commonIntf->SetCommonD3DDevice(m_commonD3DDevice.ptr()); - } - inline void AddViewportInternal(IDirect3DViewport* viewport); inline void DeleteViewportInternal(IDirect3DViewport* viewport); @@ -153,7 +134,10 @@ namespace dxvk { inline void TextureLoadInternal(D3DTEXTURELOAD* textureLoad, uint16_t count); - bool m_inScene = false; + inline void RefreshLastUsedDevice() { + if (unlikely(m_commonIntf->GetCommonD3DDevice() != m_commonD3DDevice.ptr())) + m_commonIntf->SetCommonD3DDevice(m_commonD3DDevice.ptr()); + } static uint32_t s_deviceCount; uint32_t m_deviceCount = 0; @@ -166,13 +150,7 @@ namespace dxvk { D3DMultithread m_multithread; - d3d9::D3DPRESENT_PARAMETERS m_params9; - - D3DMATERIALHANDLE m_materialHandle = 0; - D3DTEXTUREHANDLE m_textureHandle = 0; - D3DDEVICEDESC3 m_desc; - GUID m_deviceGUID; Com m_rt; Com m_ds; @@ -180,12 +158,6 @@ namespace dxvk { Com m_currentViewport; std::vector> m_viewports; - // Value of D3DRENDERSTATE_TEXTUREMAPBLEND - DWORD m_textureMapBlend = D3DTBLEND_MODULATE; - - D3DMATRIX m_projectionMatrix = { }; - const D3DMATRIX* m_legacyProjection = nullptr; - D3DSTATS m_stats = { }; D3DMATRIXHANDLE m_worldHandle = 0; diff --git a/src/ddraw/d3d3/d3d3_execute_buffer.cpp b/src/ddraw/d3d3/d3d3_execute_buffer.cpp index fa64217a9ec..6542a9381f8 100644 --- a/src/ddraw/d3d3/d3d3_execute_buffer.cpp +++ b/src/ddraw/d3d3/d3d3_execute_buffer.cpp @@ -4,12 +4,8 @@ namespace dxvk { uint32_t D3D3ExecuteBuffer::s_buffCount = 0; - D3D3ExecuteBuffer::D3D3ExecuteBuffer( - Com&& buffProxy, - D3DEXECUTEBUFFERDESC desc, - D3D3Device* pParent) - : DDrawWrappedObject(pParent, std::move(buffProxy), nullptr) - , m_desc (desc) { + D3D3ExecuteBuffer::D3D3ExecuteBuffer(D3DEXECUTEBUFFERDESC desc) + : m_desc (desc) { if (likely(m_buffer.size() == 0 && (m_desc.dwFlags & D3DDEB_BUFSIZE))) { m_buffer.resize(m_desc.dwBufferSize); Logger::debug(str::format("D3D3ExecuteBuffer: Buffer is initialized with size ", m_desc.dwBufferSize)); @@ -24,6 +20,25 @@ namespace dxvk { Logger::debug(str::format("D3D3ExecuteBuffer: Execute buffer nr. {{1-", m_buffCount, "}} bites the dust")); } + HRESULT STDMETHODCALLTYPE D3D3ExecuteBuffer::QueryInterface(REFIID riid, void** ppvObject) { + Logger::debug(">> D3D3ExecuteBuffer::QueryInterface"); + + if (unlikely(ppvObject == nullptr)) + return E_POINTER; + + InitReturnPtr(ppvObject); + + if (likely(riid == __uuidof(IUnknown) || + riid == __uuidof(IDirect3DExecuteBuffer))) { + *ppvObject = ref(this); + return S_OK; + } + + Logger::warn("D3D3ExecuteBuffer::QueryInterface: Unknown interface query"); + Logger::warn(str::format(riid)); + return E_NOINTERFACE; + } + HRESULT STDMETHODCALLTYPE D3D3ExecuteBuffer::GetExecuteData(LPD3DEXECUTEDATA lpData) { Logger::debug(">>> D3D3ExecuteBuffer::GetExecuteData"); diff --git a/src/ddraw/d3d3/d3d3_execute_buffer.h b/src/ddraw/d3d3/d3d3_execute_buffer.h index 2b1c1dc41bb..b322d2f78eb 100644 --- a/src/ddraw/d3d3/d3d3_execute_buffer.h +++ b/src/ddraw/d3d3/d3d3_execute_buffer.h @@ -1,25 +1,21 @@ #pragma once #include "../ddraw_include.h" -#include "../ddraw_wrapped_object.h" - -#include "d3d3_device.h" #include namespace dxvk { - class D3D3ExecuteBuffer final : public DDrawWrappedObject { + class D3D3ExecuteBuffer final : public ComObjectClamp { public: - D3D3ExecuteBuffer( - Com&& buffProxy, - D3DEXECUTEBUFFERDESC desc, - D3D3Device* pParent); + D3D3ExecuteBuffer(D3DEXECUTEBUFFERDESC desc); ~D3D3ExecuteBuffer(); + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject); + HRESULT STDMETHODCALLTYPE GetExecuteData(LPD3DEXECUTEDATA lpData); HRESULT STDMETHODCALLTYPE Initialize(LPDIRECT3DDEVICE lpDirect3DDevice, LPD3DEXECUTEBUFFERDESC lpDesc); @@ -42,6 +38,10 @@ namespace dxvk { return m_data; } + void SetExecuteData(D3DEXECUTEDATA data) { + m_data = data; + } + private: bool m_locked = false; @@ -51,9 +51,9 @@ namespace dxvk { D3DEXECUTEBUFFERDESC m_desc; D3DEXECUTEDATA m_data; - D3D3Device* m_D3D3Device = nullptr; std::vector m_buffer; + }; } diff --git a/src/ddraw/d3d3/d3d3_interface.cpp b/src/ddraw/d3d3/d3d3_interface.cpp index 77ca65d889e..020571deeb1 100644 --- a/src/ddraw/d3d3/d3d3_interface.cpp +++ b/src/ddraw/d3d3/d3d3_interface.cpp @@ -18,20 +18,30 @@ namespace dxvk { D3DCommonInterface* commonD3DIntf, Com&& d3d3IntfProxy, IUnknown* pParent) - : DDrawWrappedObject(pParent, std::move(d3d3IntfProxy), std::move(d3d9::Direct3DCreate9(D3D_SDK_VERSION))) + : DDrawWrappedObject(pParent, std::move(d3d3IntfProxy)) , m_commonIntf ( commonIntf ) , m_commonD3DIntf ( commonD3DIntf ) { - // Get the bridge interface to D3D9. - if (unlikely(FAILED(m_d3d9->QueryInterface(__uuidof(IDxvkD3D8InterfaceBridge), reinterpret_cast(&m_bridge))))) { - throw DxvkError("D3D3Interface: ERROR! Failed to get D3D9 Bridge. d3d9.dll might not be DXVK!"); + if (m_commonD3DIntf == nullptr) { + m_commonD3DIntf = new D3DCommonInterface(); + + Com d3d9Intf = d3d9::Direct3DCreate9(D3D_SDK_VERSION); + m_commonD3DIntf->SetD3D9Interface(std::move(d3d9Intf)); } - if (m_commonD3DIntf == nullptr) - m_commonD3DIntf = new D3DCommonInterface(); + d3d9::IDirect3D9* d3d9Intf = m_commonD3DIntf->GetD3D9Interface(); + + // Get the bridge interface to D3D9 + if (unlikely(FAILED(d3d9Intf->QueryInterface(__uuidof(IDxvkD3D8InterfaceBridge), reinterpret_cast(&m_bridge))))) { + throw DxvkError("D3D3Interface: ERROR! Failed to get D3D9 Bridge. d3d9.dll might not be DXVK!"); + } m_commonD3DIntf->SetD3D3Interface(this); - m_bridge->EnableD3D3CompatibilityMode(); + // Don't enable D3D3 compatibility mode when coming from a higher interface + if (likely(m_commonD3DIntf->GetD3D5Interface() == nullptr + && m_commonD3DIntf->GetD3D6Interface() == nullptr)) { + m_bridge->EnableD3D3CompatibilityMode(); + } m_intfCount = ++s_intfCount; @@ -129,6 +139,8 @@ namespace dxvk { // and HAL last. A RAMP device also needs to be advertised in D3D3, // since some games like Resident Evil expect it to be present. + HRESULT hr; + // RAMP device (monochrome), this is expected to be exposed GUID guidRAMP = IID_IDirect3DRampDevice; D3DDEVICEDESC3 desc3RAMP_HAL = GetD3D3Caps(d3dOptions); @@ -148,11 +160,17 @@ namespace dxvk { desc3RAMP_HEL.dpcTriCaps.dwTextureCaps |= D3DPTEXTURECAPS_POW2; memcpy(&descRAMP_HAL, &desc3RAMP_HAL, sizeof(D3DDEVICEDESC3)); memcpy(&descRAMP_HEL, &desc3RAMP_HEL, sizeof(D3DDEVICEDESC3)); - static char deviceDescRAMP[100] = "D3VK RAMP"; - static char deviceNameRAMP[100] = "D3VK RAMP"; - - HRESULT hr = lpEnumDevicesCallback(&guidRAMP, &deviceDescRAMP[0], &deviceNameRAMP[0], - &descRAMP_HAL, &descRAMP_HEL, lpUserArg); + if (likely(!d3dOptions->legacyDeviceNames)) { + static char deviceDescRAMP[100] = "D3VK Ramp"; + static char deviceNameRAMP[100] = "D3VK Ramp"; + hr = lpEnumDevicesCallback(&guidRAMP, &deviceDescRAMP[0], &deviceNameRAMP[0], + &descRAMP_HAL, &descRAMP_HEL, lpUserArg); + } else { + static char legacyDeviceDescRAMP[100] = "Ramp Emulation"; + static char legacyDeviceNameRAMP[100] = "Ramp Emulation"; + hr = lpEnumDevicesCallback(&guidRAMP, &legacyDeviceDescRAMP[0], &legacyDeviceNameRAMP[0], + &descRAMP_HAL, &descRAMP_HEL, lpUserArg); + } if (hr != D3DENUMRET_OK) return D3D_OK; @@ -173,11 +191,17 @@ namespace dxvk { desc3RGB_HEL.dpcTriCaps.dwTextureCaps |= D3DPTEXTURECAPS_POW2; memcpy(&descRGB_HAL, &desc3RGB_HAL, sizeof(D3DDEVICEDESC3)); memcpy(&descRGB_HEL, &desc3RGB_HEL, sizeof(D3DDEVICEDESC3)); - static char deviceDescRGB[100] = "D3VK RGB"; - static char deviceNameRGB[100] = "D3VK RGB"; - - hr = lpEnumDevicesCallback(&guidRGB, &deviceDescRGB[0], &deviceNameRGB[0], - &descRGB_HAL, &descRGB_HEL, lpUserArg); + if (likely(!d3dOptions->legacyDeviceNames)) { + static char deviceDescRGB[100] = "D3VK RGB"; + static char deviceNameRGB[100] = "D3VK RGB"; + hr = lpEnumDevicesCallback(&guidRGB, &deviceDescRGB[0], &deviceNameRGB[0], + &descRGB_HAL, &descRGB_HEL, lpUserArg); + } else { + static char legacyDeviceDescRGB[100] = "RGB Emulation"; + static char legacyDeviceNameRGB[100] = "RGB Emulation"; + hr = lpEnumDevicesCallback(&guidRGB, &legacyDeviceDescRGB[0], &legacyDeviceNameRGB[0], + &descRGB_HAL, &descRGB_HEL, lpUserArg); + } if (hr != D3DENUMRET_OK) return D3D_OK; @@ -195,11 +219,17 @@ namespace dxvk { & ~D3DPTEXTURECAPS_POW2; memcpy(&descHAL_HAL, &desc3HAL_HAL, sizeof(D3DDEVICEDESC3)); memcpy(&descHAL_HEL, &desc3HAL_HEL, sizeof(D3DDEVICEDESC3)); - static char deviceDescHAL[100] = "D3VK HAL"; - static char deviceNameHAL[100] = "D3VK HAL"; - - hr = lpEnumDevicesCallback(&guidHAL, &deviceDescHAL[0], &deviceNameHAL[0], - &descHAL_HAL, &descHAL_HEL, lpUserArg); + if (likely(!d3dOptions->legacyDeviceNames)) { + static char deviceDescHAL[100] = "D3VK HAL"; + static char deviceNameHAL[100] = "D3VK HAL"; + hr = lpEnumDevicesCallback(&guidHAL, &deviceDescHAL[0], &deviceNameHAL[0], + &descHAL_HAL, &descHAL_HEL, lpUserArg); + } else { + static char legacyDeviceDescHAL[100] = "Direct3D HAL"; + static char legacyDeviceNameHAL[100] = "Direct3D HAL"; + hr = lpEnumDevicesCallback(&guidHAL, &legacyDeviceDescHAL[0], &legacyDeviceNameHAL[0], + &descHAL_HAL, &descHAL_HEL, lpUserArg); + } if (hr != D3DENUMRET_OK) return D3D_OK; @@ -214,7 +244,7 @@ namespace dxvk { InitReturnPtr(lplpDirect3DLight); - *lplpDirect3DLight = ref(new D3DLight(nullptr, this)); + *lplpDirect3DLight = ref(new D3DLight()); return D3D_OK; } diff --git a/src/ddraw/d3d3/d3d3_interface.h b/src/ddraw/d3d3/d3d3_interface.h index b74e852696d..cdf68dad39c 100644 --- a/src/ddraw/d3d3/d3d3_interface.h +++ b/src/ddraw/d3d3/d3d3_interface.h @@ -16,7 +16,7 @@ namespace dxvk { /** * \brief D3D3 interface implementation */ - class D3D3Interface final : public DDrawWrappedObject { + class D3D3Interface final : public DDrawWrappedObject { public: D3D3Interface( diff --git a/src/ddraw/d3d3/d3d3_material.cpp b/src/ddraw/d3d3/d3d3_material.cpp index 86213af2215..1c4ab22daef 100644 --- a/src/ddraw/d3d3/d3d3_material.cpp +++ b/src/ddraw/d3d3/d3d3_material.cpp @@ -1,9 +1,10 @@ #include "d3d3_material.h" -#include "d3d3_device.h" #include "d3d3_interface.h" #include "d3d3_viewport.h" +#include "../d3d_common_device.h" + #include "../ddraw/ddraw_interface.h" namespace dxvk { @@ -14,7 +15,7 @@ namespace dxvk { Com&& proxyMaterial, D3D3Interface* pParent, D3DMATERIALHANDLE handle) - : DDrawWrappedObject(pParent, std::move(proxyMaterial), nullptr) { + : DDrawWrappedObject(pParent, std::move(proxyMaterial)) { m_commonMaterial = new D3DCommonMaterial(handle); m_commonMaterial->SetD3D3Material(this); @@ -32,6 +33,24 @@ namespace dxvk { Logger::debug(str::format("D3D3Material: Material nr. [[1-", m_materialCount, "]] bites the dust")); } + HRESULT STDMETHODCALLTYPE D3D3Material::QueryInterface(REFIID riid, void** ppvObject) { + Logger::debug(">>> D3D3Material::QueryInterface"); + + if (unlikely(ppvObject == nullptr)) + return E_POINTER; + + InitReturnPtr(ppvObject); + + try { + *ppvObject = ref(this->GetInterface(riid)); + return S_OK; + } catch (const DxvkError& e) { + Logger::warn(e.message()); + Logger::warn(str::format(riid)); + return E_NOINTERFACE; + } + } + // Docs state: "Returns DDERR_ALREADYINITIALIZED because the // Direct3DMaterial object is initialized when it is created." HRESULT STDMETHODCALLTYPE D3D3Material::Initialize(LPDIRECT3D lpDirect3D) { @@ -45,6 +64,9 @@ namespace dxvk { if (unlikely(data == nullptr)) return DDERR_INVALIDPARAMS; + if (unlikely(!data->dwSize)) + return DDERR_INVALIDPARAMS; + // This call needs to be forwarded to the proxied material // too, in order to have a proper color used during proxied clears HRESULT hr = m_proxy->SetMaterial(data); @@ -59,22 +81,14 @@ namespace dxvk { material9->Emissive = data->dcvEmissive; material9->Power = data->dvPower; - D3DMATERIALHANDLE handle = m_commonMaterial->GetMaterialHandle(); - - Logger::debug(str::format(">>> D3D3Material::SetMaterial: Updated material nr. ", handle)); - Logger::debug(str::format(" Diffuse: ", material9->Diffuse.r, " ", material9->Diffuse.g, " ", material9->Diffuse.b)); - Logger::debug(str::format(" Ambient: ", material9->Ambient.r, " ", material9->Ambient.g, " ", material9->Ambient.b)); - Logger::debug(str::format(" Specular: ", material9->Specular.r, " ", material9->Specular.g, " ", material9->Specular.b)); - Logger::debug(str::format(" Emissive: ", material9->Emissive.r, " ", material9->Emissive.g, " ", material9->Emissive.b)); - Logger::debug(str::format(" Power: ", material9->Power)); - // Update the D3D9 material directly if it's actively being used - D3D3Device* device3 = m_parent->GetCommonInterface()->GetCommonD3DDevice()->GetD3D3Device(); - if (likely(device3 != nullptr)) { - D3DMATERIALHANDLE currentHandle = device3->GetCurrentMaterialHandle(); + D3DCommonDevice* commonDevice = m_parent->GetCommonInterface()->GetCommonD3DDevice(); + if (likely(commonDevice != nullptr)) { + const D3DMATERIALHANDLE handle = m_commonMaterial->GetMaterialHandle(); + const D3DMATERIALHANDLE currentHandle = commonDevice->GetCurrentMaterialHandle(); if (currentHandle == handle) { Logger::debug(str::format("D3D3Material::SetMaterial: Applying material nr. ", handle, " to D3D9")); - device3->GetD3D9()->SetMaterial(material9); + commonDevice->GetD3D9Device()->SetMaterial(material9); } } diff --git a/src/ddraw/d3d3/d3d3_material.h b/src/ddraw/d3d3/d3d3_material.h index 222a49acc72..b9fd0e00256 100644 --- a/src/ddraw/d3d3/d3d3_material.h +++ b/src/ddraw/d3d3/d3d3_material.h @@ -9,7 +9,7 @@ namespace dxvk { class D3D3Interface; - class D3D3Material final : public DDrawWrappedObject { + class D3D3Material final : public DDrawWrappedObject { public: @@ -20,6 +20,8 @@ namespace dxvk { ~D3D3Material(); + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject); + HRESULT STDMETHODCALLTYPE Initialize(LPDIRECT3D lpDirect3D); HRESULT STDMETHODCALLTYPE SetMaterial(D3DMATERIAL *data); diff --git a/src/ddraw/d3d3/d3d3_texture.cpp b/src/ddraw/d3d3/d3d3_texture.cpp index c5866734c77..de47aac667a 100644 --- a/src/ddraw/d3d3/d3d3_texture.cpp +++ b/src/ddraw/d3d3/d3d3_texture.cpp @@ -12,7 +12,7 @@ namespace dxvk { Com&& proxyTexture, DDrawSurface* pParent, D3DTEXTUREHANDLE handle) - : DDrawWrappedObject(pParent, std::move(proxyTexture), nullptr) { + : DDrawWrappedObject(pParent, std::move(proxyTexture)) { m_commonTex = new D3DCommonTexture(m_parent->GetCommonSurface(), handle); m_texCount = ++s_texCount; @@ -69,6 +69,14 @@ namespace dxvk { Logger::debug("D3D3Texture::QueryInterface: Query for IDirectDrawSurface3"); return m_parent->QueryInterface(riid, ppvObject); } + if (unlikely(riid == __uuidof(IDirectDrawSurface4))) { + Logger::debug("D3D3Texture::QueryInterface: Query for IDirectDrawSurface4"); + return m_parent->QueryInterface(riid, ppvObject); + } + if (unlikely(riid == __uuidof(IDirectDrawSurface7))) { + Logger::debug("D3D3Texture::QueryInterface: Query for IDirectDrawSurface7"); + return m_parent->QueryInterface(riid, ppvObject); + } try { *ppvObject = ref(this->GetInterface(riid)); @@ -92,9 +100,11 @@ namespace dxvk { return D3D_OK; } + // Docs state: "This method only affects the legacy ramp device. + // For all other devices, this method takes no action and returns D3D_OK." HRESULT STDMETHODCALLTYPE D3D3Texture::PaletteChanged(DWORD dwStart, DWORD dwCount) { - Logger::warn("<<< D3D3Texture::PaletteChanged: Proxy"); - return m_proxy->PaletteChanged(dwStart, dwCount); + Logger::debug(">>> D3D3Texture::PaletteChanged"); + return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D3Texture::Load(LPDIRECT3DTEXTURE lpD3DTexture) { @@ -117,25 +127,21 @@ namespace dxvk { m_parent->GetCommonSurface()->SetDesc(desc); } - m_parent->GetCommonSurface()->DirtyMipMaps(); + m_parent->GetCommonSurface()->DirtyDDrawSurface(); return hr; } + // Docs state: "Returns DDERR_ALREADYINITIALIZED because the Direct3DTexture object is initialized when it is created." HRESULT STDMETHODCALLTYPE D3D3Texture::Initialize(LPDIRECT3DDEVICE lpDirect3DDevice, LPDIRECTDRAWSURFACE lpDDSurface) { - Logger::debug("<<< D3D3Texture::Initialize: Proxy"); - - if(unlikely(lpDirect3DDevice == nullptr || lpDDSurface == nullptr)) - return DDERR_INVALIDPARAMS; - - D3D3Device* d3d3Device = static_cast(lpDirect3DDevice); - DDrawSurface* ddrawSurface = static_cast(lpDDSurface); - return m_proxy->Initialize(d3d3Device->GetProxied(), ddrawSurface->GetProxied()); + Logger::debug(">>> D3D3Texture::Initialize"); + return DDERR_ALREADYINITIALIZED; } + // Nothing to do here, this isn't managed texture unloading HRESULT STDMETHODCALLTYPE D3D3Texture::Unload() { - Logger::debug("<<< D3D3Texture::Unload: Proxy"); - return m_proxy->Unload(); + Logger::debug(">>> D3D3Texture::Unload"); + return D3D_OK; } } diff --git a/src/ddraw/d3d3/d3d3_texture.h b/src/ddraw/d3d3/d3d3_texture.h index c8bb8d96965..ee14271f2eb 100644 --- a/src/ddraw/d3d3/d3d3_texture.h +++ b/src/ddraw/d3d3/d3d3_texture.h @@ -9,7 +9,7 @@ namespace dxvk { class DDrawSurface; - class D3D3Texture final : public DDrawWrappedObject { + class D3D3Texture final : public DDrawWrappedObject { public: diff --git a/src/ddraw/d3d3/d3d3_viewport.cpp b/src/ddraw/d3d3/d3d3_viewport.cpp index b018899fc07..27ca299b78f 100644 --- a/src/ddraw/d3d3/d3d3_viewport.cpp +++ b/src/ddraw/d3d3/d3d3_viewport.cpp @@ -21,7 +21,7 @@ namespace dxvk { D3DCommonViewport* commonViewport, Com&& proxyViewport, D3D3Interface* pParent) - : DDrawWrappedObject(pParent, std::move(proxyViewport), nullptr) + : DDrawWrappedObject(pParent, std::move(proxyViewport)) , m_commonViewport ( commonViewport ) { if (m_commonViewport == nullptr) @@ -358,14 +358,18 @@ namespace dxvk { D3DCOLOR clearColor = commonMaterial != nullptr ? commonMaterial->GetMaterialColor() : defaultColor; HRESULT hr9 = d3d9Device->Clear(count, rects, flags, clearColor, 1.0f, 0u); - if (unlikely(FAILED(hr9))) - Logger::err("D3D3Viewport::Clear: Failed D3D9 Clear call"); // Restore the previously active viewport if (!m_commonViewport->IsCurrentViewport()) { d3d9Device->SetViewport(¤tViewport9); } + if (unlikely(FAILED(hr9))) { + Logger::warn("D3D3Viewport::Clear: Failed D3D9 Clear call"); + } else { + m_commonViewport->UpdateSurfaceDirtyTracking(true, false, false); + } + return D3D_OK; } @@ -380,11 +384,6 @@ namespace dxvk { if (unlikely(d3dLight->HasViewport())) return D3DERR_LIGHTHASVIEWPORT; - if (m_commonViewport->HasDevice()) { - Logger::debug("D3D3Viewport::AddLight: Enabling device legacy light model"); - m_commonViewport->EnableLegacyLights(d3dLight->IsD3DLight2()); - } - std::vector>& lights = m_commonViewport->GetLights(); // No need to check if the light is already attached, since // if that's the case it will have a set viewport above @@ -481,6 +480,25 @@ namespace dxvk { return D3D_OK; } + HRESULT D3D3Viewport::DeactivateLights() { + std::vector>& lights = m_commonViewport->GetLights(); + + if (!lights.size()) + return D3D_OK; + + Logger::debug("D3D3Viewport: Deactivating D3D9 lights"); + + for (auto light: lights) { + const DWORD lightIndex = light->GetIndex(); + if (m_commonViewport->HasDevice() && m_commonViewport->IsCurrentViewport() && light->IsActive()) { + Logger::debug(str::format("D3D3Viewport: Disabling light nr. ", lightIndex)); + m_commonViewport->GetD3D9Device()->LightEnable(lightIndex, FALSE); + } + } + + return D3D_OK; + } + HRESULT D3D3Viewport::ApplyAndActivateLight(DWORD index, D3DLight* light) { d3d9::IDirect3DDevice9* d3d9Device = m_commonViewport->GetD3D9Device(); @@ -490,12 +508,12 @@ namespace dxvk { } else { HRESULT hrLE; if (light->IsActive()) { - Logger::debug(str::format("D3D3Viewport: Enabling light nr. ", index)); + Logger::debug(str::format("D3D3Viewport: Enabling D3D9 light nr. ", index)); hrLE = d3d9Device->LightEnable(index, TRUE); if (unlikely(FAILED(hrLE))) Logger::err("D3D3Viewport: Failed D3D9 LightEnable call (TRUE)"); } else { - Logger::debug(str::format("D3D3Viewport: Disabling light nr. ", index)); + Logger::debug(str::format("D3D3Viewport: Disabling D3D9 light nr. ", index)); hrLE = d3d9Device->LightEnable(index, FALSE); if (unlikely(FAILED(hrLE))) Logger::err("D3D3Viewport: Failed D3D9 LightEnable call (FALSE)"); diff --git a/src/ddraw/d3d3/d3d3_viewport.h b/src/ddraw/d3d3/d3d3_viewport.h index ae3e8762baf..8a33b27e255 100644 --- a/src/ddraw/d3d3/d3d3_viewport.h +++ b/src/ddraw/d3d3/d3d3_viewport.h @@ -14,7 +14,7 @@ namespace dxvk { class D3D6Viewport; class D3D5Viewport; - class D3D3Viewport final : public DDrawWrappedObject { + class D3D3Viewport final : public DDrawWrappedObject { public: @@ -61,6 +61,8 @@ namespace dxvk { HRESULT ApplyAndActivateLights(); + HRESULT DeactivateLights(); + HRESULT ApplyAndActivateLight(DWORD index, D3DLight* light); D3DCommonViewport* GetCommonViewport() const { diff --git a/src/ddraw/d3d5/d3d5_device.cpp b/src/ddraw/d3d5/d3d5_device.cpp index e4e056f4be4..d6b20b950ff 100644 --- a/src/ddraw/d3d5/d3d5_device.cpp +++ b/src/ddraw/d3d5/d3d5_device.cpp @@ -33,12 +33,10 @@ namespace dxvk { Com&& pDevice9, DDrawSurface* pSurface, DWORD CreationFlags9) - : DDrawWrappedObject(pParent, std::move(d3d5DeviceProxy), std::move(pDevice9)) + : DDrawWrappedObject(pParent, std::move(d3d5DeviceProxy)) , m_commonD3DDevice ( commonD3DDevice ) , m_multithread ( CreationFlags9 & D3DCREATE_MULTITHREADED ) - , m_params9 ( Params9 ) , m_desc ( Desc ) - , m_deviceGUID ( deviceGUID ) , m_rt ( pSurface ) { if (m_parent != nullptr) { m_commonIntf = m_parent->GetCommonInterface(); @@ -48,31 +46,45 @@ namespace dxvk { throw DxvkError("D3D5Device: ERROR! Failed to retrieve the common interface!"); } - // Get the bridge interface to D3D9 - if (unlikely(FAILED(m_d3d9->QueryInterface(__uuidof(IDxvkD3D8Bridge), reinterpret_cast(&m_bridge))))) { - throw DxvkError("D3D5Device: ERROR! Failed to get D3D9 Bridge. d3d9.dll might not be DXVK!"); - } + d3d9::IDirect3DDevice9* device9; if (likely(m_commonD3DDevice == nullptr)) { - m_commonD3DDevice = new D3DCommonDevice(m_commonIntf, CreationFlags9, - m_bridge->DetermineInitialTextureMemory()); + m_commonD3DDevice = new D3DCommonDevice(m_commonIntf, deviceGUID, Params9, CreationFlags9); + + m_commonD3DDevice->SetD3D9Device(std::move(pDevice9)); + device9 = m_commonD3DDevice->GetD3D9Device(); const D3DOptions* d3dOptions = m_commonIntf->GetOptions(); if (unlikely(d3dOptions->emulateFSAA == FSAAEmulation::Forced)) { Logger::warn("D3D5Device: Force enabling AA"); - m_d3d9->SetRenderState(d3d9::D3DRS_MULTISAMPLEANTIALIAS, TRUE); + device9->SetRenderState(d3d9::D3DRS_MULTISAMPLEANTIALIAS, TRUE); } // The default value of D3DRENDERSTATE_TEXTUREMAPBLEND in D3D5 is D3DTBLEND_MODULATE - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG1, D3DTA_TEXTURE); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG1, D3DTA_TEXTURE); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_COLOROP, D3DTOP_MODULATE); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAOP, D3DTOP_SELECTARG1); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG2, D3DTA_DIFFUSE); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG2, D3DTA_DIFFUSE); + device9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG1, D3DTA_TEXTURE); + device9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG1, D3DTA_TEXTURE); + device9->SetTextureStageState(0, d3d9::D3DTSS_COLOROP, D3DTOP_MODULATE); + device9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAOP, D3DTOP_SELECTARG1); + device9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG2, D3DTA_DIFFUSE); + device9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG2, D3DTA_DIFFUSE); + } else { + device9 = m_commonD3DDevice->GetD3D9Device(); + // Very important, otherwise the depth stencil isn't dirtied on draws + m_ds = m_rt->GetAttachedDepthStencil(); + } + + // Get the bridge interface to D3D9 + if (unlikely(FAILED(device9->QueryInterface(__uuidof(IDxvkD3D8Bridge), reinterpret_cast(&m_bridge))))) { + throw DxvkError("D3D5Device: ERROR! Failed to get D3D9 Bridge. d3d9.dll might not be DXVK!"); } + if (unlikely(!m_commonD3DDevice->GetTotalTextureMemory())) + m_commonD3DDevice->SetTotalTextureMemory(m_bridge->DetermineInitialTextureMemory()); + + // Update D3D9 legacy light state + m_bridge->SetLegacyLightsState(true); + if (m_commonD3DDevice->GetOrigin() == nullptr) m_commonD3DDevice->SetOrigin(this); @@ -143,23 +155,25 @@ namespace dxvk { // Reuse the existing D3D9 device in situations where games want // to get access only to D3D3 execute buffers on a D3D5 device - Com device9 = m_d3d9.ptr(); m_device3 = new D3D3Device(m_commonD3DDevice.ptr(), std::move(ppvProxyObject), - m_rt.ptr(), GetD3D3Caps(d3dOptions), m_deviceGUID, - m_params9, std::move(device9), m_commonD3DDevice->GetD3D9CreationFlags()); + m_rt.ptr(), GetD3D3Caps(d3dOptions), m_commonD3DDevice->GetDeviceGUID(), + m_commonD3DDevice->GetPresentParameters(), nullptr, + m_commonD3DDevice->GetD3D9CreationFlags()); + m_commonD3DDevice->SetD3D3Device(m_device3.ptr()); // On native this is the same object, so no need to ref *ppvObject = m_device3.ptr(); return S_OK; } - // Technically possible, shouldn't ever be needed or make sense if (unlikely(riid == __uuidof(IDirect3DDevice3))) { if (m_commonD3DDevice->GetD3D6Device() != nullptr) { Logger::debug("D3D5Device::QueryInterface: Query for existing IDirect3DDevice3"); return m_commonD3DDevice->GetD3D6Device()->QueryInterface(riid, ppvObject); } + // A D3D5 device shouldn't be able to create a D3D6 device + // if it doesn't previously exist as a parent/origin device Logger::err("D3D5Device::QueryInterface: Query for IDirect3DDevice3"); return E_NOINTERFACE; } @@ -187,7 +201,9 @@ namespace dxvk { D3DDEVICEDESC2 desc_HAL = m_desc; D3DDEVICEDESC2 desc_HEL = m_desc; - if (m_deviceGUID == IID_IDirect3DRGBDevice) { + const GUID deviceGUID = m_commonD3DDevice->GetDeviceGUID(); + + if (deviceGUID == IID_IDirect3DRGBDevice) { desc_HAL.dwFlags = 0; desc_HAL.dcmColorModel = 0; // Some applications apparently care about RGB texture caps @@ -197,7 +213,7 @@ namespace dxvk { & ~D3DPTEXTURECAPS_NONPOW2CONDITIONAL; desc_HEL.dpcLineCaps.dwTextureCaps |= D3DPTEXTURECAPS_POW2; desc_HEL.dpcTriCaps.dwTextureCaps |= D3DPTEXTURECAPS_POW2; - } else if (m_deviceGUID == IID_IDirect3DHALDevice) { + } else if (deviceGUID == IID_IDirect3DHALDevice) { desc_HEL.dcmColorModel = 0; desc_HEL.dwDevCaps &= ~D3DDEVCAPS_HWTRANSFORMANDLIGHT & ~D3DDEVCAPS_DRAWPRIMITIVES2 @@ -226,8 +242,8 @@ namespace dxvk { const D3DTEXTUREHANDLE handle1 = commonTex1->GetTextureHandle(); const D3DTEXTUREHANDLE handle2 = commonTex2->GetTextureHandle(); - m_parent->GetCommonInterface()->ReleaseTextureHandle(handle1); - m_parent->GetCommonInterface()->ReleaseTextureHandle(handle2); + m_commonIntf->ReleaseTextureHandle(handle1); + m_commonIntf->ReleaseTextureHandle(handle2); commonTex1->SetTextureHandle(handle2); commonTex2->SetTextureHandle(handle1); @@ -401,13 +417,13 @@ namespace dxvk { RefreshLastUsedDevice(); - if (unlikely(m_inScene)) + if (unlikely(m_commonD3DDevice->IsInScene())) return D3DERR_SCENE_IN_SCENE; - HRESULT hr = m_d3d9->BeginScene(); + HRESULT hr = m_commonD3DDevice->GetD3D9Device()->BeginScene(); if (likely(SUCCEEDED(hr))) - m_inScene = true; + m_commonD3DDevice->SetInScene(true); return hr; } @@ -419,29 +435,13 @@ namespace dxvk { RefreshLastUsedDevice(); - if (unlikely(!m_inScene)) + if (unlikely(!m_commonD3DDevice->IsInScene())) return D3DERR_SCENE_NOT_IN_SCENE; - HRESULT hr = m_d3d9->EndScene(); - - if (likely(SUCCEEDED(hr))) { - const D3DOptions* d3dOptions = m_commonIntf->GetOptions(); - - if (d3dOptions->forceProxiedPresent) { - // If we have drawn anything, we need to make sure we blit back - // the results onto the D3D5 render target before we flip it - if (m_commonIntf->HasDrawn()) - BlitToDDrawSurface(m_rt->GetProxied(), m_rt->GetD3D9()); - - m_rt->GetProxied()->Flip(static_cast(m_commonIntf->GetFlipRTSurface()), - m_commonIntf->GetFlipRTFlags()); + HRESULT hr = m_commonD3DDevice->GetD3D9Device()->EndScene(); - if (likely(d3dOptions->backBufferGuard != D3DBackBufferGuard::Strict)) - m_commonIntf->ResetDrawTracking(); - } - - m_inScene = false; - } + if (likely(SUCCEEDED(hr))) + m_commonD3DDevice->SetInScene(false); return hr; } @@ -475,8 +475,10 @@ namespace dxvk { if (unlikely(m_currentViewport == d3d5Viewport)) return D3D_OK; - if (likely(m_currentViewport != nullptr)) + if (likely(m_currentViewport != nullptr)) { + m_currentViewport->DeactivateLights(); m_currentViewport->GetCommonViewport()->SetIsCurrentViewport(false); + } m_currentViewport = d3d5Viewport.ptr(); @@ -524,20 +526,10 @@ namespace dxvk { DDrawSurface* rt5 = static_cast(surface); - const D3DOptions* d3dOptions = m_commonIntf->GetOptions(); - - if (unlikely(d3dOptions->forceProxiedPresent)) { - HRESULT hrRT = m_proxy->SetRenderTarget(rt5->GetProxied(), flags); - if (unlikely(FAILED(hrRT))) { - Logger::warn("D3D5Device::SetRenderTarget: Failed to set RT"); - return hrRT; - } - } else { - // Needed to ensure proxied Z/Stencil viewport clears will work - HRESULT hrRT = m_proxy->SetRenderTarget(rt5->GetProxied(), flags); - if (unlikely(FAILED(hrRT))) - Logger::debug("D3D5Device::SetRenderTarget: Failed to set RT"); - } + // Needed to ensure proxied Z/Stencil viewport clears will work + HRESULT hrRT = m_proxy->SetRenderTarget(rt5->GetShadowOrProxied(), flags); + if (unlikely(FAILED(hrRT))) + Logger::debug("D3D5Device::SetRenderTarget: Failed to set RT"); HRESULT hr = rt5->GetCommonSurface()->ValidateRTUsage(); if (unlikely(FAILED(hr))) @@ -549,7 +541,9 @@ namespace dxvk { return hr; } - hr = m_d3d9->SetRenderTarget(0, rt5->GetD3D9()); + d3d9::IDirect3DDevice9* device9 = m_commonD3DDevice->GetD3D9Device(); + + hr = device9->SetRenderTarget(0, rt5->GetCommonSurface()->GetD3D9Surface()); if (likely(SUCCEEDED(hr))) { Logger::debug("D3D5Device::SetRenderTarget: Set a new D3D9 RT"); @@ -568,7 +562,7 @@ namespace dxvk { return hrDS; } - hrDS = m_d3d9->SetDepthStencilSurface(m_ds->GetD3D9()); + hrDS = device9->SetDepthStencilSurface(m_ds->GetCommonSurface()->GetD3D9Surface()); if (unlikely(FAILED(hrDS))) { Logger::err("D3D5Device::SetRenderTarget: Failed to set D3D9 DS"); return hrDS; @@ -578,7 +572,7 @@ namespace dxvk { } else { Logger::debug("D3D5Device::SetRenderTarget: RT has no depth stencil attached"); - hrDS = m_d3d9->SetDepthStencilSurface(nullptr); + hrDS = device9->SetDepthStencilSurface(nullptr); if (unlikely(FAILED(hrDS))) { Logger::err("D3D5Device::SetRenderTarget: Failed to clear the D3D9 DS"); return hrDS; @@ -692,12 +686,14 @@ namespace dxvk { // As opposed to D3D7, D3D5 does not error out on // unknown or invalid render states. - if (unlikely(!IsValidD3D5RenderStateType(dwRenderStateType))) { + if (unlikely(!IsValidD3D5RenderStateType(dwRenderStateType) + && !m_commonIntf->GetOptions()->apitraceMode)) { Logger::debug(str::format("D3D5Device::GetRenderState: Invalid render state ", dwRenderStateType)); *lpdwRenderState = 0; return D3D_OK; } + d3d9::IDirect3DDevice9* device9 = m_commonD3DDevice->GetD3D9Device(); d3d9::D3DRENDERSTATETYPE State9 = d3d9::D3DRENDERSTATETYPE(dwRenderStateType); switch (dwRenderStateType) { @@ -707,15 +703,15 @@ namespace dxvk { // Replacement for later implemented GetTexture calls case D3DRENDERSTATE_TEXTUREHANDLE: - *lpdwRenderState = m_textureHandle; + *lpdwRenderState = m_commonD3DDevice->GetCurrentTextureHandle(); return D3D_OK; case D3DRENDERSTATE_ANTIALIAS: - *lpdwRenderState = m_antialias; + *lpdwRenderState = m_commonD3DDevice->GetAntialias(); return D3D_OK; case D3DRENDERSTATE_TEXTUREADDRESS: - m_d3d9->GetSamplerState(0, d3d9::D3DSAMP_ADDRESSU, lpdwRenderState); + device9->GetSamplerState(0, d3d9::D3DSAMP_ADDRESSU, lpdwRenderState); return D3D_OK; // Always enabled on later APIs, though default FALSE in D3D5 @@ -726,7 +722,7 @@ namespace dxvk { // Not implemented in DXVK, but retrieve it as it were case D3DRENDERSTATE_WRAPU: { DWORD value9 = 0; - m_d3d9->GetRenderState(d3d9::D3DRS_WRAP0, &value9); + device9->GetRenderState(d3d9::D3DRS_WRAP0, &value9); *lpdwRenderState = value9 & D3DWRAP_U; return D3D_OK; } @@ -734,13 +730,13 @@ namespace dxvk { // Not implemented in DXVK, but retrieve it as it were case D3DRENDERSTATE_WRAPV: { DWORD value9 = 0; - m_d3d9->GetRenderState(d3d9::D3DRS_WRAP0, &value9); + device9->GetRenderState(d3d9::D3DRS_WRAP0, &value9); *lpdwRenderState = value9 & D3DWRAP_V; return D3D_OK; } case D3DRENDERSTATE_LINEPATTERN: - *lpdwRenderState = bit::cast(m_linePattern); + *lpdwRenderState = bit::cast(m_commonD3DDevice->GetLinePattern()); return D3D_OK; case D3DRENDERSTATE_MONOENABLE: @@ -756,20 +752,20 @@ namespace dxvk { return D3D_OK; case D3DRENDERSTATE_TEXTUREMAG: - m_d3d9->GetSamplerState(0, d3d9::D3DSAMP_MAGFILTER, lpdwRenderState); + device9->GetSamplerState(0, d3d9::D3DSAMP_MAGFILTER, lpdwRenderState); return D3D_OK; case D3DRENDERSTATE_TEXTUREMIN: { DWORD minFilter = 0; DWORD mipFilter = 0; - m_d3d9->GetSamplerState(0, d3d9::D3DSAMP_MINFILTER, &minFilter); - m_d3d9->GetSamplerState(0, d3d9::D3DSAMP_MIPFILTER, &mipFilter); + device9->GetSamplerState(0, d3d9::D3DSAMP_MINFILTER, &minFilter); + device9->GetSamplerState(0, d3d9::D3DSAMP_MIPFILTER, &mipFilter); *lpdwRenderState = DecodeTextureMinValues(minFilter, mipFilter); return D3D_OK; } case D3DRENDERSTATE_TEXTUREMAPBLEND: - *lpdwRenderState = m_textureMapBlend; + *lpdwRenderState = m_commonD3DDevice->GetTextureMapBlend(); return D3D_OK; // Replaced by D3DRENDERSTATE_ALPHABLENDENABLE @@ -799,7 +795,7 @@ namespace dxvk { break; case D3DRENDERSTATE_COLORKEYENABLE: - *lpdwRenderState = m_colorKeyEnabled; + *lpdwRenderState = m_commonD3DDevice->GetColorKeyEnable(); return D3D_OK; case D3DRENDERSTATE_ALPHABLENDENABLE_OLD: @@ -807,30 +803,30 @@ namespace dxvk { break; case D3DRENDERSTATE_BORDERCOLOR: - m_d3d9->GetSamplerState(0, d3d9::D3DSAMP_BORDERCOLOR, lpdwRenderState); + device9->GetSamplerState(0, d3d9::D3DSAMP_BORDERCOLOR, lpdwRenderState); return D3D_OK; case D3DRENDERSTATE_TEXTUREADDRESSU: - m_d3d9->GetSamplerState(0, d3d9::D3DSAMP_ADDRESSU, lpdwRenderState); + device9->GetSamplerState(0, d3d9::D3DSAMP_ADDRESSU, lpdwRenderState); return D3D_OK; case D3DRENDERSTATE_TEXTUREADDRESSV: - m_d3d9->GetSamplerState(0, d3d9::D3DSAMP_ADDRESSV, lpdwRenderState); + device9->GetSamplerState(0, d3d9::D3DSAMP_ADDRESSV, lpdwRenderState); return D3D_OK; case D3DRENDERSTATE_MIPMAPLODBIAS: - m_d3d9->GetSamplerState(0, d3d9::D3DSAMP_MIPMAPLODBIAS, lpdwRenderState); + device9->GetSamplerState(0, d3d9::D3DSAMP_MIPMAPLODBIAS, lpdwRenderState); return D3D_OK; case D3DRENDERSTATE_ZBIAS: { DWORD bias = 0; - m_d3d9->GetRenderState(d3d9::D3DRS_DEPTHBIAS, &bias); + device9->GetRenderState(d3d9::D3DRS_DEPTHBIAS, &bias); *lpdwRenderState = static_cast(bit::cast(bias) * ddrawCaps::ZBIAS_SCALE_INV); return D3D_OK; } case D3DRENDERSTATE_ANISOTROPY: - m_d3d9->GetSamplerState(0, d3d9::D3DSAMP_MAXANISOTROPY, lpdwRenderState); + device9->GetSamplerState(0, d3d9::D3DSAMP_MAXANISOTROPY, lpdwRenderState); return D3D_OK; // Not mentioned in the D3D5 docs, but seen in the wild. D3D6 docs state: @@ -878,7 +874,7 @@ namespace dxvk { } // This call will never fail - return m_d3d9->GetRenderState(State9, lpdwRenderState); + return device9->GetRenderState(State9, lpdwRenderState); } HRESULT STDMETHODCALLTYPE D3D5Device::SetRenderState(D3DRENDERSTATETYPE dwRenderStateType, DWORD dwRenderState) { @@ -893,6 +889,7 @@ namespace dxvk { return D3D_OK; } + d3d9::IDirect3DDevice9* device9 = m_commonD3DDevice->GetD3D9Device(); d3d9::D3DRENDERSTATETYPE State9 = d3d9::D3DRENDERSTATETYPE(dwRenderStateType); switch (dwRenderStateType) { @@ -931,16 +928,16 @@ namespace dxvk { } State9 = d3d9::D3DRS_MULTISAMPLEANTIALIAS; - m_antialias = dwRenderState; - dwRenderState = m_antialias == D3DANTIALIAS_SORTDEPENDENT - || m_antialias == D3DANTIALIAS_SORTINDEPENDENT + m_commonD3DDevice->SetAntialias(dwRenderState); + dwRenderState = dwRenderState == D3DANTIALIAS_SORTDEPENDENT + || dwRenderState == D3DANTIALIAS_SORTINDEPENDENT || d3dOptions->emulateFSAA == FSAAEmulation::Forced ? TRUE : FALSE; break; } case D3DRENDERSTATE_TEXTUREADDRESS: - m_d3d9->SetSamplerState(0, d3d9::D3DSAMP_ADDRESSU, dwRenderState); - m_d3d9->SetSamplerState(0, d3d9::D3DSAMP_ADDRESSV, dwRenderState); + device9->SetSamplerState(0, d3d9::D3DSAMP_ADDRESSU, dwRenderState); + device9->SetSamplerState(0, d3d9::D3DSAMP_ADDRESSV, dwRenderState); return D3D_OK; // Always enabled on later APIs, though default FALSE in D3D5 @@ -950,11 +947,11 @@ namespace dxvk { // Not implemented in DXVK, but forward it anyway case D3DRENDERSTATE_WRAPU: { DWORD value9 = 0; - m_d3d9->GetRenderState(d3d9::D3DRS_WRAP0, &value9); + device9->GetRenderState(d3d9::D3DRS_WRAP0, &value9); if (dwRenderState == TRUE) { - m_d3d9->SetRenderState(d3d9::D3DRS_WRAP0, value9 & D3DWRAP_U); + device9->SetRenderState(d3d9::D3DRS_WRAP0, value9 & D3DWRAP_U); } else { - m_d3d9->SetRenderState(d3d9::D3DRS_WRAP0, value9 & ~D3DWRAP_U); + device9->SetRenderState(d3d9::D3DRS_WRAP0, value9 & ~D3DWRAP_U); } return D3D_OK; } @@ -962,11 +959,11 @@ namespace dxvk { // Not implemented in DXVK, but forward it anyway case D3DRENDERSTATE_WRAPV: { DWORD value9 = 0; - m_d3d9->GetRenderState(d3d9::D3DRS_WRAP0, &value9); + device9->GetRenderState(d3d9::D3DRS_WRAP0, &value9); if (dwRenderState == TRUE) { - m_d3d9->SetRenderState(d3d9::D3DRS_WRAP0, value9 & D3DWRAP_V); + device9->SetRenderState(d3d9::D3DRS_WRAP0, value9 & D3DWRAP_V); } else { - m_d3d9->SetRenderState(d3d9::D3DRS_WRAP0, value9 & ~D3DWRAP_V); + device9->SetRenderState(d3d9::D3DRS_WRAP0, value9 & ~D3DWRAP_V); } return D3D_OK; } @@ -979,7 +976,7 @@ namespace dxvk { if (!std::exchange(s_linePatternErrorShown, true)) Logger::warn("D3D5Device::SetRenderState: Unimplemented render state D3DRS_LINEPATTERN"); - m_linePattern = bit::cast(dwRenderState); + m_commonD3DDevice->SetLinePattern(bit::cast(dwRenderState)); return D3D_OK; case D3DRENDERSTATE_MONOENABLE: @@ -1008,7 +1005,7 @@ namespace dxvk { switch (dwRenderState) { case D3DFILTER_NEAREST: case D3DFILTER_LINEAR: - m_d3d9->SetSamplerState(0, d3d9::D3DSAMP_MAGFILTER, dwRenderState); + device9->SetSamplerState(0, d3d9::D3DSAMP_MAGFILTER, dwRenderState); break; default: break; @@ -1020,29 +1017,29 @@ namespace dxvk { switch (dwRenderState) { case D3DFILTER_NEAREST: case D3DFILTER_LINEAR: - m_d3d9->SetSamplerState(0, d3d9::D3DSAMP_MINFILTER, dwRenderState); - m_d3d9->SetSamplerState(0, d3d9::D3DSAMP_MIPFILTER, d3d9::D3DTEXF_NONE); + device9->SetSamplerState(0, d3d9::D3DSAMP_MINFILTER, dwRenderState); + device9->SetSamplerState(0, d3d9::D3DSAMP_MIPFILTER, d3d9::D3DTEXF_NONE); break; // "The closest mipmap level is chosen and a point filter is applied." case D3DFILTER_MIPNEAREST: - m_d3d9->SetSamplerState(0, d3d9::D3DSAMP_MINFILTER, d3d9::D3DTEXF_POINT); - m_d3d9->SetSamplerState(0, d3d9::D3DSAMP_MIPFILTER, d3d9::D3DTEXF_POINT); + device9->SetSamplerState(0, d3d9::D3DSAMP_MINFILTER, d3d9::D3DTEXF_POINT); + device9->SetSamplerState(0, d3d9::D3DSAMP_MIPFILTER, d3d9::D3DTEXF_POINT); break; // "The closest mipmap level is chosen and a bilinear filter is applied within it." case D3DFILTER_MIPLINEAR: - m_d3d9->SetSamplerState(0, d3d9::D3DSAMP_MINFILTER, d3d9::D3DTEXF_LINEAR); - m_d3d9->SetSamplerState(0, d3d9::D3DSAMP_MIPFILTER, d3d9::D3DTEXF_POINT); + device9->SetSamplerState(0, d3d9::D3DSAMP_MINFILTER, d3d9::D3DTEXF_LINEAR); + device9->SetSamplerState(0, d3d9::D3DSAMP_MIPFILTER, d3d9::D3DTEXF_POINT); break; // "The two closest mipmap levels are chosen and then a linear // blend is used between point filtered samples of each level." case D3DFILTER_LINEARMIPNEAREST: - m_d3d9->SetSamplerState(0, d3d9::D3DSAMP_MINFILTER, d3d9::D3DTEXF_POINT); - m_d3d9->SetSamplerState(0, d3d9::D3DSAMP_MIPFILTER, d3d9::D3DTEXF_LINEAR); + device9->SetSamplerState(0, d3d9::D3DSAMP_MINFILTER, d3d9::D3DTEXF_POINT); + device9->SetSamplerState(0, d3d9::D3DSAMP_MIPFILTER, d3d9::D3DTEXF_LINEAR); break; // "The two closest mipmap levels are chosen and then combined using a bilinear filter." case D3DFILTER_LINEARMIPLINEAR: - m_d3d9->SetSamplerState(0, d3d9::D3DSAMP_MINFILTER, d3d9::D3DTEXF_LINEAR); - m_d3d9->SetSamplerState(0, d3d9::D3DSAMP_MIPFILTER, d3d9::D3DTEXF_LINEAR); + device9->SetSamplerState(0, d3d9::D3DSAMP_MINFILTER, d3d9::D3DTEXF_LINEAR); + device9->SetSamplerState(0, d3d9::D3DSAMP_MIPFILTER, d3d9::D3DTEXF_LINEAR); break; default: break; @@ -1051,19 +1048,19 @@ namespace dxvk { } case D3DRENDERSTATE_TEXTUREMAPBLEND: - m_textureMapBlend = dwRenderState; + m_commonD3DDevice->SetTextureMapBlend(dwRenderState); switch (dwRenderState) { // "In this mode, the RGB and alpha values of the texture replace // the colors that would have been used with no texturing." case D3DTBLEND_DECAL: case D3DTBLEND_COPY: - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG1, D3DTA_TEXTURE); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG1, D3DTA_TEXTURE); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_COLOROP, D3DTOP_SELECTARG1); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAOP, D3DTOP_SELECTARG1); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG2, D3DTA_CURRENT); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG2, D3DTA_CURRENT); + device9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG1, D3DTA_TEXTURE); + device9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG1, D3DTA_TEXTURE); + device9->SetTextureStageState(0, d3d9::D3DTSS_COLOROP, D3DTOP_SELECTARG1); + device9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAOP, D3DTOP_SELECTARG1); + device9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG2, D3DTA_CURRENT); + device9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG2, D3DTA_CURRENT); break; // "In this mode, the RGB values of the texture are multiplied with the RGB values // that would have been used with no texturing. Any alpha values in the texture @@ -1071,43 +1068,43 @@ namespace dxvk { // if the texture does not contain an alpha component, alpha values at the vertices // in the source are interpolated between vertices." case D3DTBLEND_MODULATE: - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG1, D3DTA_TEXTURE); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG1, D3DTA_TEXTURE); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_COLOROP, D3DTOP_MODULATE); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAOP, D3DTOP_SELECTARG1); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG2, D3DTA_DIFFUSE); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG2, D3DTA_DIFFUSE); + device9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG1, D3DTA_TEXTURE); + device9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG1, D3DTA_TEXTURE); + device9->SetTextureStageState(0, d3d9::D3DTSS_COLOROP, D3DTOP_MODULATE); + device9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAOP, D3DTOP_SELECTARG1); + device9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG2, D3DTA_DIFFUSE); + device9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG2, D3DTA_DIFFUSE); break; // "In this mode, the RGB and alpha values of the texture are blended with the colors // that would have been used with no texturing, according to the following formulas [...]" case D3DTBLEND_DECALALPHA: - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG1, D3DTA_TEXTURE); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG1, D3DTA_TEXTURE); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_COLOROP, D3DTOP_BLENDTEXTUREALPHA); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAOP, D3DTOP_SELECTARG2); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG2, D3DTA_DIFFUSE); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG2, D3DTA_DIFFUSE); + device9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG1, D3DTA_TEXTURE); + device9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG1, D3DTA_TEXTURE); + device9->SetTextureStageState(0, d3d9::D3DTSS_COLOROP, D3DTOP_BLENDTEXTUREALPHA); + device9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAOP, D3DTOP_SELECTARG2); + device9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG2, D3DTA_DIFFUSE); + device9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG2, D3DTA_DIFFUSE); break; // "In this mode, the RGB values of the texture are multiplied with the RGB values that // would have been used with no texturing, and the alpha values of the texture // are multiplied with the alpha values that would have been used with no texturing." case D3DTBLEND_MODULATEALPHA: - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG1, D3DTA_TEXTURE); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG1, D3DTA_TEXTURE); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_COLOROP, D3DTOP_MODULATE); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAOP, D3DTOP_MODULATE); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG2, D3DTA_DIFFUSE); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG2, D3DTA_DIFFUSE); + device9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG1, D3DTA_TEXTURE); + device9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG1, D3DTA_TEXTURE); + device9->SetTextureStageState(0, d3d9::D3DTSS_COLOROP, D3DTOP_MODULATE); + device9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAOP, D3DTOP_MODULATE); + device9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG2, D3DTA_DIFFUSE); + device9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG2, D3DTA_DIFFUSE); break; // "Add the Gouraud interpolants to the texture lookup with saturation semantics // (that is, if the color value overflows it is set to the maximum possible value)." case D3DTBLEND_ADD: - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG1, D3DTA_TEXTURE); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG1, D3DTA_TEXTURE); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_COLOROP, D3DTOP_ADD); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAOP, D3DTOP_SELECTARG2); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG2, D3DTA_DIFFUSE); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG2, D3DTA_DIFFUSE); + device9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG1, D3DTA_TEXTURE); + device9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG1, D3DTA_TEXTURE); + device9->SetTextureStageState(0, d3d9::D3DTSS_COLOROP, D3DTOP_ADD); + device9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAOP, D3DTOP_SELECTARG2); + device9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG2, D3DTA_DIFFUSE); + device9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG2, D3DTA_DIFFUSE); break; // Unsupported default: @@ -1159,12 +1156,14 @@ namespace dxvk { break; case D3DRENDERSTATE_COLORKEYENABLE: { - m_colorKeyEnabled = dwRenderState; + m_commonD3DDevice->SetColorKeyEnable(dwRenderState); - DDrawSurface* surface = m_textureHandle != 0 ? m_commonIntf->GetSurfaceFromTextureHandle(m_textureHandle) : nullptr; + const D3DTEXTUREHANDLE currentTextureHandle = m_commonD3DDevice->GetCurrentTextureHandle(); + DDrawSurface* surface = currentTextureHandle != 0 ? + m_commonIntf->GetSurfaceFromTextureHandle(currentTextureHandle) : nullptr; const bool validColorKey = surface != nullptr ? surface->GetCommonSurface()->HasValidColorKey() : false; - m_bridge->SetColorKeyState(m_colorKeyEnabled && validColorKey); - if (m_colorKeyEnabled && validColorKey) { + m_bridge->SetColorKeyState(dwRenderState && validColorKey); + if (dwRenderState && validColorKey) { DDCOLORKEY normalizedColorKey = surface->GetCommonSurface()->GetColorKeyNormalized(); m_bridge->SetColorKey(normalizedColorKey.dwColorSpaceLowValue, normalizedColorKey.dwColorSpaceHighValue); @@ -1178,19 +1177,19 @@ namespace dxvk { break; case D3DRENDERSTATE_BORDERCOLOR: - m_d3d9->SetSamplerState(0, d3d9::D3DSAMP_BORDERCOLOR, dwRenderState); + device9->SetSamplerState(0, d3d9::D3DSAMP_BORDERCOLOR, dwRenderState); return D3D_OK; case D3DRENDERSTATE_TEXTUREADDRESSU: - m_d3d9->SetSamplerState(0, d3d9::D3DSAMP_ADDRESSU, dwRenderState); + device9->SetSamplerState(0, d3d9::D3DSAMP_ADDRESSU, dwRenderState); return D3D_OK; case D3DRENDERSTATE_TEXTUREADDRESSV: - m_d3d9->SetSamplerState(0, d3d9::D3DSAMP_ADDRESSV, dwRenderState); + device9->SetSamplerState(0, d3d9::D3DSAMP_ADDRESSV, dwRenderState); return D3D_OK; case D3DRENDERSTATE_MIPMAPLODBIAS: - m_d3d9->SetSamplerState(0, d3d9::D3DSAMP_MIPMAPLODBIAS, dwRenderState); + device9->SetSamplerState(0, d3d9::D3DSAMP_MIPMAPLODBIAS, dwRenderState); return D3D_OK; case D3DRENDERSTATE_ZBIAS: @@ -1199,7 +1198,7 @@ namespace dxvk { break; case D3DRENDERSTATE_ANISOTROPY: - m_d3d9->SetSamplerState(0, d3d9::D3DSAMP_MAXANISOTROPY, dwRenderState); + device9->SetSamplerState(0, d3d9::D3DSAMP_MAXANISOTROPY, dwRenderState); return D3D_OK; // Not mentioned in the D3D5 docs, but seen in the wild. D3D6 docs state: @@ -1251,7 +1250,7 @@ namespace dxvk { } // This call will never fail - return m_d3d9->SetRenderState(State9, dwRenderState); + return device9->SetRenderState(State9, dwRenderState); } HRESULT STDMETHODCALLTYPE D3D5Device::GetLightState(D3DLIGHTSTATETYPE dwLightStateType, LPDWORD lpdwLightState) { @@ -1259,27 +1258,29 @@ namespace dxvk { Logger::debug(">>> D3D5Device::GetLightState"); + d3d9::IDirect3DDevice9* device9 = m_commonD3DDevice->GetD3D9Device(); + switch (dwLightStateType) { case D3DLIGHTSTATE_MATERIAL: - *lpdwLightState = m_materialHandle; + *lpdwLightState = m_commonD3DDevice->GetCurrentMaterialHandle(); break; case D3DLIGHTSTATE_AMBIENT: - m_d3d9->GetRenderState(d3d9::D3DRS_AMBIENT, lpdwLightState); + device9->GetRenderState(d3d9::D3DRS_AMBIENT, lpdwLightState); break; case D3DLIGHTSTATE_COLORMODEL: *lpdwLightState = D3DCOLOR_RGB; break; case D3DLIGHTSTATE_FOGMODE: - m_d3d9->GetRenderState(d3d9::D3DRS_FOGVERTEXMODE, lpdwLightState); + device9->GetRenderState(d3d9::D3DRS_FOGVERTEXMODE, lpdwLightState); break; case D3DLIGHTSTATE_FOGSTART: - m_d3d9->GetRenderState(d3d9::D3DRS_FOGSTART, lpdwLightState); + device9->GetRenderState(d3d9::D3DRS_FOGSTART, lpdwLightState); break; case D3DLIGHTSTATE_FOGEND: - m_d3d9->GetRenderState(d3d9::D3DRS_FOGEND, lpdwLightState); + device9->GetRenderState(d3d9::D3DRS_FOGEND, lpdwLightState); break; case D3DLIGHTSTATE_FOGDENSITY: - m_d3d9->GetRenderState(d3d9::D3DRS_FOGDENSITY, lpdwLightState); + device9->GetRenderState(d3d9::D3DRS_FOGDENSITY, lpdwLightState); break; default: return DDERR_INVALIDPARAMS; @@ -1293,10 +1294,12 @@ namespace dxvk { Logger::debug(">>> D3D5Device::SetLightState"); + d3d9::IDirect3DDevice9* device9 = m_commonD3DDevice->GetD3D9Device(); + switch (dwLightStateType) { case D3DLIGHTSTATE_MATERIAL: { if (unlikely(!dwLightState)) { - m_materialHandle = dwLightState; + m_commonD3DDevice->SetCurrentMaterialHandle(dwLightState); return D3D_OK; } @@ -1304,30 +1307,30 @@ namespace dxvk { if (unlikely(material9 == nullptr)) return DDERR_INVALIDPARAMS; - m_materialHandle = dwLightState; + m_commonD3DDevice->SetCurrentMaterialHandle(dwLightState); Logger::debug(str::format("D3D5Device::SetLightState: Applying material nr. ", dwLightState, " to D3D9")); - m_d3d9->SetMaterial(material9); + device9->SetMaterial(material9); break; } case D3DLIGHTSTATE_AMBIENT: - m_d3d9->SetRenderState(d3d9::D3DRS_AMBIENT, dwLightState); + device9->SetRenderState(d3d9::D3DRS_AMBIENT, dwLightState); break; case D3DLIGHTSTATE_COLORMODEL: if (unlikely(dwLightState != D3DCOLOR_RGB)) Logger::warn("D3D5Device::SetLightState: Unsupported D3DLIGHTSTATE_COLORMODEL"); break; case D3DLIGHTSTATE_FOGMODE: - m_d3d9->SetRenderState(d3d9::D3DRS_FOGVERTEXMODE, dwLightState); + device9->SetRenderState(d3d9::D3DRS_FOGVERTEXMODE, dwLightState); break; case D3DLIGHTSTATE_FOGSTART: - m_d3d9->SetRenderState(d3d9::D3DRS_FOGSTART, dwLightState); + device9->SetRenderState(d3d9::D3DRS_FOGSTART, dwLightState); break; case D3DLIGHTSTATE_FOGEND: - m_d3d9->SetRenderState(d3d9::D3DRS_FOGEND, dwLightState); + device9->SetRenderState(d3d9::D3DRS_FOGEND, dwLightState); break; case D3DLIGHTSTATE_FOGDENSITY: - m_d3d9->SetRenderState(d3d9::D3DRS_FOGDENSITY, dwLightState); + device9->SetRenderState(d3d9::D3DRS_FOGDENSITY, dwLightState); break; default: return DDERR_INVALIDPARAMS; @@ -1338,17 +1341,17 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE D3D5Device::SetTransform(D3DTRANSFORMSTATETYPE state, D3DMATRIX *matrix) { Logger::debug(">>> D3D5Device::SetTransform"); - return m_d3d9->SetTransform(ConvertTransformState(state), matrix); + return m_commonD3DDevice->GetD3D9Device()->SetTransform(ConvertTransformState(state), matrix); } HRESULT STDMETHODCALLTYPE D3D5Device::GetTransform(D3DTRANSFORMSTATETYPE state, D3DMATRIX *matrix) { Logger::debug(">>> D3D5Device::GetTransform"); - return m_d3d9->GetTransform(ConvertTransformState(state), matrix); + return m_commonD3DDevice->GetD3D9Device()->GetTransform(ConvertTransformState(state), matrix); } HRESULT STDMETHODCALLTYPE D3D5Device::MultiplyTransform(D3DTRANSFORMSTATETYPE state, D3DMATRIX *matrix) { Logger::debug(">>> D3D5Device::MultiplyTransform"); - return m_d3d9->MultiplyTransform(ConvertTransformState(state), matrix); + return m_commonD3DDevice->GetD3D9Device()->MultiplyTransform(ConvertTransformState(state), matrix); } HRESULT STDMETHODCALLTYPE D3D5Device::DrawPrimitive(D3DPRIMITIVETYPE primitive_type, D3DVERTEXTYPE vertex_type, void *vertices, DWORD vertex_count, DWORD flags) { @@ -1364,13 +1367,19 @@ namespace dxvk { if (unlikely(vertices == nullptr)) return DDERR_INVALIDPARAMS; + d3d9::IDirect3DDevice9* device9 = m_commonD3DDevice->GetD3D9Device(); + + m_rt->InitializeOrUploadD3D9(); + if (likely(m_ds != nullptr)) + m_ds->InitializeOrUploadD3D9(); + DWORD vertex_type5 = ConvertVertexType(vertex_type); HandlePreDrawFlags(flags, vertex_type5); HandlePreDrawLegacyProjection(flags); - m_d3d9->SetFVF(vertex_type5); - HRESULT hr = m_d3d9->DrawPrimitiveUP( + device9->SetFVF(vertex_type5); + HRESULT hr = device9->DrawPrimitiveUP( d3d9::D3DPRIMITIVETYPE(primitive_type), GetPrimitiveCount(primitive_type, vertex_count), vertices, @@ -1384,7 +1393,7 @@ namespace dxvk { return hr; } - m_commonIntf->UpdateDrawTracking(); + UpdateSurfaceDirtyTracking(true, true, true); return hr; } @@ -1402,13 +1411,19 @@ namespace dxvk { if (unlikely(vertices == nullptr || indices == nullptr)) return DDERR_INVALIDPARAMS; + d3d9::IDirect3DDevice9* device9 = m_commonD3DDevice->GetD3D9Device(); + + m_rt->InitializeOrUploadD3D9(); + if (likely(m_ds != nullptr)) + m_ds->InitializeOrUploadD3D9(); + const DWORD fvf5 = ConvertVertexType(fvf); HandlePreDrawFlags(flags, fvf5); HandlePreDrawLegacyProjection(flags); - m_d3d9->SetFVF(fvf5); - HRESULT hr = m_d3d9->DrawIndexedPrimitiveUP( + device9->SetFVF(fvf5); + HRESULT hr = device9->DrawIndexedPrimitiveUP( d3d9::D3DPRIMITIVETYPE(primitive_type), 0, vertex_count, @@ -1426,40 +1441,45 @@ namespace dxvk { return hr; } - m_commonIntf->UpdateDrawTracking(); + UpdateSurfaceDirtyTracking(true, true, true); return hr; } HRESULT STDMETHODCALLTYPE D3D5Device::SetClipStatus(D3DCLIPSTATUS *clip_status) { - D3DDeviceLock lock = LockDevice(); - Logger::debug(">>> D3D5Device::SetClipStatus"); if (unlikely(clip_status == nullptr)) return DDERR_INVALIDPARAMS; - m_clipStatus = *clip_status; - return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D5Device::GetClipStatus(D3DCLIPSTATUS *clip_status) { - D3DDeviceLock lock = LockDevice(); - Logger::debug(">>> D3D5Device::GetClipStatus"); if (unlikely(clip_status == nullptr)) return DDERR_INVALIDPARAMS; - *clip_status = m_clipStatus; + d3d9::D3DVIEWPORT9 viewport9; + if (SUCCEEDED(m_commonD3DDevice->GetD3D9Device()->GetViewport(&viewport9))) { + clip_status->dwFlags = D3DCLIPSTATUS_EXTENTS2; + clip_status->dwStatus = 0; + clip_status->minx = viewport9.X; + clip_status->maxx = viewport9.X + viewport9.Height; + clip_status->miny = viewport9.Y; + clip_status->maxy = viewport9.Y + viewport9.Width; + clip_status->minz = 0; + clip_status->maxz = 0; + } return D3D_OK; } void D3D5Device::InitializeDS() { - if (!m_rt->IsInitialized()) - m_rt->InitializeD3D9RenderTarget(); + d3d9::IDirect3DDevice9* device9 = m_commonD3DDevice->GetD3D9Device(); + + m_rt->InitializeD3D9RenderTarget(); m_ds = m_rt->GetAttachedDepthStencil(); @@ -1477,22 +1497,38 @@ namespace dxvk { m_ds->GetProxied()->GetSurfaceDesc(&descDS); Logger::debug(str::format("D3D5Device::InitializeDS: DepthStencil: ", descDS.dwWidth, "x", descDS.dwHeight)); - HRESULT hrDS9 = m_d3d9->SetDepthStencilSurface(m_ds->GetD3D9()); + HRESULT hrDS9 = device9->SetDepthStencilSurface(m_ds->GetCommonSurface()->GetD3D9Surface()); if(unlikely(FAILED(hrDS9))) { Logger::err("D3D5Device::InitializeDS: Failed to set D3D9 depth stencil"); } else { // This needs to act like an auto depth stencil of sorts, so manually enable z-buffering - m_d3d9->SetRenderState(d3d9::D3DRS_ZENABLE, d3d9::D3DZB_TRUE); + device9->SetRenderState(d3d9::D3DRS_ZENABLE, d3d9::D3DZB_TRUE); } } } else { Logger::info("D3D5Device::InitializeDS: RT has no depth stencil attached"); - m_d3d9->SetDepthStencilSurface(nullptr); + device9->SetDepthStencilSurface(nullptr); // Should be superfluous, but play it safe - m_d3d9->SetRenderState(d3d9::D3DRS_ZENABLE, d3d9::D3DZB_FALSE); + device9->SetRenderState(d3d9::D3DRS_ZENABLE, d3d9::D3DZB_FALSE); } } + void D3D5Device::UpdateSurfaceDirtyTracking(bool dirtyRenderTarget, bool dirtyDepthStencil, bool dirtyPrimarySurface) { + if(likely(dirtyRenderTarget)) + m_rt->GetCommonSurface()->DirtyD3D9Surface(); + + if (likely(dirtyPrimarySurface)) { + DDrawCommonSurface* primarySurface = m_commonIntf->GetPrimarySurface(); + // The primary surface can be bound as RT, in which case it will + // get dirtied twice, but we have no guarantees that will happen + if (likely(primarySurface != nullptr)) + primarySurface->DirtyD3D9Surface(); + } + + if (likely(dirtyDepthStencil && m_ds != nullptr)) + m_ds->GetCommonSurface()->DirtyD3D9Surface(); + } + inline void D3D5Device::AddViewportInternal(IDirect3DViewport2* viewport) { D3D5Viewport* d3d5Viewport = static_cast(viewport); @@ -1522,16 +1558,18 @@ namespace dxvk { HRESULT hr; + d3d9::IDirect3DDevice9* device9 = m_commonD3DDevice->GetD3D9Device(); + // Unbinding texture stages if (surface == nullptr) { Logger::debug("D3D5Device::SetTextureInternal: Unbiding D3D9 texture"); - hr = m_d3d9->SetTexture(0, nullptr); + hr = device9->SetTexture(0, nullptr); if (likely(SUCCEEDED(hr))) { - if (m_textureHandle != 0) { + if (m_commonD3DDevice->GetCurrentTextureHandle() != 0) { Logger::debug("D3D5Device::SetTextureInternal: Unbinding local texture"); - m_textureHandle = 0; + m_commonD3DDevice->SetCurrentTextureHandle(0); } } else { Logger::err("D3D5Device::SetTextureInternal: Failed to unbind D3D9 texture"); @@ -1542,33 +1580,25 @@ namespace dxvk { Logger::debug("D3D5Device::SetTextureInternal: Binding D3D9 texture"); - // Only upload textures if any sort of blit/lock operation - // has been performed on them since the last SetTexture call, - // or textures which have been used on a different device, and - // need their D3D9 object to be reinitialized at this point - if (surface->GetCommonSurface()->HasDirtyMipMaps() || - unlikely(surface->GetD3D9Device() != m_d3d9.ptr())) { - hr = surface->InitializeOrUploadD3D9(); - if (unlikely(FAILED(hr))) { - Logger::err("D3D5Device::SetTextureInternal: Failed to initialize/upload D3D9 texture"); - return hr; - } + // If textures have been used on a different device, they + // will get their D3D9 object reinitialized at this point + if (unlikely(surface->GetCommonSurface()->GetCommonD3DDevice() != m_commonD3DDevice.ptr())) + surface->GetCommonSurface()->DirtyDDrawSurface(); - surface->GetCommonSurface()->UnDirtyMipMaps(); - } else { - Logger::debug("D3D5Device::SetTextureInternal: Skipping upload of texture and mip maps"); + hr = surface->InitializeOrUploadD3D9(); + if (unlikely(FAILED(hr))) { + Logger::err("D3D6Device::D3D5Device: Failed to initialize/upload D3D9 texture"); + return hr; } - // Only fast skip on D3D9 side, since we want to ensure - // color keying is applied properly even in the case - // of the same texture being set again (color key may change) - //if (unlikely(m_textureHandle == textureHandle)) + // Don't fast skip, since color key might change + //if (unlikely(m_commonD3DDevice->GetCurrentTextureHandle() == textureHandle)) //return D3D_OK; - d3d9::IDirect3DTexture9* tex9 = surface->GetD3D9Texture(); + d3d9::IDirect3DTexture9* tex9 = surface->GetCommonSurface()->GetD3D9Texture(); if (likely(tex9 != nullptr)) { - hr = m_d3d9->SetTexture(0, tex9); + hr = device9->SetTexture(0, tex9); if (unlikely(FAILED(hr))) { Logger::warn("D3D5Device::SetTextureInternal: Failed to bind D3D9 texture"); return hr; @@ -1577,21 +1607,22 @@ namespace dxvk { // "Any alpha values in the texture replace the alpha values in the colors that would // have been used with no texturing; if the texture does not contain an alpha component, // alpha values at the vertices in the source are interpolated between vertices." - if (m_textureMapBlend == D3DTBLEND_MODULATE) { + if (m_commonD3DDevice->GetTextureMapBlend() == D3DTBLEND_MODULATE) { const DWORD textureOp = surface->GetCommonSurface()->IsAlphaFormat() ? D3DTOP_SELECTARG1 : D3DTOP_MODULATE; - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAOP, textureOp); + device9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAOP, textureOp); } + const bool colorKeyEnable = m_commonD3DDevice->GetColorKeyEnable(); const bool validColorKey = surface->GetCommonSurface()->HasValidColorKey(); - m_bridge->SetColorKeyState(m_colorKeyEnabled && validColorKey); - if (m_colorKeyEnabled && validColorKey) { + m_bridge->SetColorKeyState(colorKeyEnable && validColorKey); + if (colorKeyEnable && validColorKey) { DDCOLORKEY normalizedColorKey = surface->GetCommonSurface()->GetColorKeyNormalized(); m_bridge->SetColorKey(normalizedColorKey.dwColorSpaceLowValue, normalizedColorKey.dwColorSpaceHighValue); } } - m_textureHandle = textureHandle; + m_commonD3DDevice->SetCurrentTextureHandle(textureHandle); return D3D_OK; } diff --git a/src/ddraw/d3d5/d3d5_device.h b/src/ddraw/d3d5/d3d5_device.h index e453d180792..348a0e5528a 100644 --- a/src/ddraw/d3d5/d3d5_device.h +++ b/src/ddraw/d3d5/d3d5_device.h @@ -27,7 +27,7 @@ namespace dxvk { /** * \brief D3D5 device implementation */ - class D3D5Device final : public DDrawWrappedObject { + class D3D5Device final : public DDrawWrappedObject { public: D3D5Device( @@ -111,6 +111,8 @@ namespace dxvk { void InitializeDS(); + void UpdateSurfaceDirtyTracking(bool dirtyRenderTarget, bool dirtyDepthStencil, bool dirtyPrimarySurface); + D3DCommonDevice* GetCommonD3DDevice() { return m_commonD3DDevice.ptr(); } @@ -119,18 +121,6 @@ namespace dxvk { return m_multithread.AcquireLock(); } - void EnableLegacyLights(bool isD3DLight2) { - m_bridge->SetLegacyLightsState(true, isD3DLight2); - } - - d3d9::D3DPRESENT_PARAMETERS GetPresentParameters() const { - return m_params9; - } - - d3d9::D3DMULTISAMPLE_TYPE GetMultiSampleType() const { - return m_params9.MultiSampleType; - } - DDrawSurface* GetRenderTarget() const { return m_rt.ptr(); } @@ -143,14 +133,6 @@ namespace dxvk { return m_currentViewport.ptr(); } - D3DMATERIALHANDLE GetCurrentMaterialHandle() const { - return m_materialHandle; - } - - void SetCurrentMaterialHandle(D3DMATERIALHANDLE handle) { - m_materialHandle = handle; - } - private: inline void AddViewportInternal(IDirect3DViewport2* viewport); @@ -167,23 +149,27 @@ namespace dxvk { inline void HandlePreDrawFlags(DWORD drawFlags, DWORD vertexTypeDesc) { // Docs: "Direct3D normally performs lighting calculations // on any vertices that contain a vertex normal." - if (m_materialHandle == 0 || + if (m_commonD3DDevice->GetCurrentMaterialHandle() == 0 || (drawFlags & D3DDP_DONOTLIGHT) || !(vertexTypeDesc & D3DFVF_NORMAL)) { - m_d3d9->GetRenderState(d3d9::D3DRS_LIGHTING, &m_lighting); + d3d9::IDirect3DDevice9* device9 = m_commonD3DDevice->GetD3D9Device(); + + device9->GetRenderState(d3d9::D3DRS_LIGHTING, &m_lighting); if (m_lighting) { //Logger::debug("D3D5Device: Disabling lighting"); - m_d3d9->SetRenderState(d3d9::D3DRS_LIGHTING, FALSE); + device9->SetRenderState(d3d9::D3DRS_LIGHTING, FALSE); } } } inline void HandlePostDrawFlags(DWORD drawFlags, DWORD vertexTypeDesc) { - if ((m_materialHandle == 0 || + if ((m_commonD3DDevice->GetCurrentMaterialHandle() == 0 || (drawFlags & D3DDP_DONOTLIGHT) || !(vertexTypeDesc & D3DFVF_NORMAL)) && m_lighting) { + d3d9::IDirect3DDevice9* device9 = m_commonD3DDevice->GetD3D9Device(); + //Logger::debug("D3D5Device: Enabling lighting"); - m_d3d9->SetRenderState(d3d9::D3DRS_LIGHTING, TRUE); + device9->SetRenderState(d3d9::D3DRS_LIGHTING, TRUE); } } @@ -192,22 +178,24 @@ namespace dxvk { m_legacyProjection = m_currentViewport->GetCommonViewport()->GetLegacyProjectionMatrix(drawFlags); if (m_legacyProjection != nullptr) { + d3d9::IDirect3DDevice9* device9 = m_commonD3DDevice->GetD3D9Device(); + //Logger::debug("D3D5Device: Applying legacy projection"); - m_d3d9->GetTransform(d3d9::D3DTS_PROJECTION, &m_projectionMatrix); - m_d3d9->MultiplyTransform(d3d9::D3DTS_PROJECTION, m_legacyProjection); + device9->GetTransform(d3d9::D3DTS_PROJECTION, &m_projectionMatrix); + device9->MultiplyTransform(d3d9::D3DTS_PROJECTION, m_legacyProjection); } } } inline void HandlePostDrawLegacyProjection() { if (m_legacyProjection != nullptr) { + d3d9::IDirect3DDevice9* device9 = m_commonD3DDevice->GetD3D9Device(); + //Logger::debug("D3D5Device: Reverting legacy projection"); - m_d3d9->SetTransform(d3d9::D3DTS_PROJECTION, &m_projectionMatrix); + device9->SetTransform(d3d9::D3DTS_PROJECTION, &m_projectionMatrix); } } - bool m_inScene = false; - static uint32_t s_deviceCount; uint32_t m_deviceCount = 0; @@ -223,13 +211,7 @@ namespace dxvk { D3DMultithread m_multithread; - d3d9::D3DPRESENT_PARAMETERS m_params9; - - D3DMATERIALHANDLE m_materialHandle = 0; - D3DTEXTUREHANDLE m_textureHandle = 0; - D3DDEVICEDESC2 m_desc; - GUID m_deviceGUID; Com m_rt; Com m_ds; @@ -242,17 +224,6 @@ namespace dxvk { std::vector m_lvertexStream; std::vector m_tlvertexStream; - // Value of D3DRENDERSTATE_COLORKEYENABLE - DWORD m_colorKeyEnabled = 0; - // Value of D3DRENDERSTATE_ANTIALIAS - DWORD m_antialias = D3DANTIALIAS_NONE; - // Value of D3DRENDERSTATE_LINEPATTERN - D3DLINEPATTERN m_linePattern = { }; - // Value of D3DCLIPSTATUS - D3DCLIPSTATUS m_clipStatus = { }; - // Value of D3DRENDERSTATE_TEXTUREMAPBLEND - DWORD m_textureMapBlend = D3DTBLEND_MODULATE; - D3DMATRIX m_projectionMatrix = { }; const D3DMATRIX* m_legacyProjection = nullptr; diff --git a/src/ddraw/d3d5/d3d5_interface.cpp b/src/ddraw/d3d5/d3d5_interface.cpp index 01d6c479caf..7811fbc42df 100644 --- a/src/ddraw/d3d5/d3d5_interface.cpp +++ b/src/ddraw/d3d5/d3d5_interface.cpp @@ -22,16 +22,22 @@ namespace dxvk { D3DCommonInterface* commonD3DIntf, Com&& d3d5IntfProxy, IUnknown* pParent) - : DDrawWrappedObject(pParent, std::move(d3d5IntfProxy), std::move(d3d9::Direct3DCreate9(D3D_SDK_VERSION))) + : DDrawWrappedObject(pParent, std::move(d3d5IntfProxy)) , m_commonIntf ( m_commonIntf ) , m_commonD3DIntf ( commonD3DIntf ) { - // Get the bridge interface to D3D9. - if (unlikely(FAILED(m_d3d9->QueryInterface(__uuidof(IDxvkD3D8InterfaceBridge), reinterpret_cast(&m_bridge))))) { - throw DxvkError("D3D5Interface: ERROR! Failed to get D3D9 Bridge. d3d9.dll might not be DXVK!"); + if (m_commonD3DIntf == nullptr) { + m_commonD3DIntf = new D3DCommonInterface(); + + Com d3d9Intf = d3d9::Direct3DCreate9(D3D_SDK_VERSION); + m_commonD3DIntf->SetD3D9Interface(std::move(d3d9Intf)); } - if (m_commonD3DIntf == nullptr) - m_commonD3DIntf = new D3DCommonInterface(); + d3d9::IDirect3D9* d3d9Intf = m_commonD3DIntf->GetD3D9Interface(); + + // Get the bridge interface to D3D9 + if (unlikely(FAILED(d3d9Intf->QueryInterface(__uuidof(IDxvkD3D8InterfaceBridge), reinterpret_cast(&m_bridge))))) { + throw DxvkError("D3D5Interface: ERROR! Failed to get D3D9 Bridge. d3d9.dll might not be DXVK!"); + } m_commonD3DIntf->SetD3D5Interface(this); @@ -136,6 +142,8 @@ namespace dxvk { // and HAL last. A RAMP device also needs to be advertised in D3D5, // since some games like Resident Evil expect it to be present. + HRESULT hr; + // RAMP device (monochrome), this is expected to be exposed GUID guidRAMP = IID_IDirect3DRampDevice; // The caps of a RAMP device are mostly identical to an RGB device @@ -156,11 +164,17 @@ namespace dxvk { desc2RAMP_HEL.dpcTriCaps.dwTextureCaps |= D3DPTEXTURECAPS_POW2; memcpy(&descRAMP_HAL, &desc2RAMP_HAL, sizeof(D3DDEVICEDESC2)); memcpy(&descRAMP_HEL, &desc2RAMP_HEL, sizeof(D3DDEVICEDESC2)); - static char deviceDescRAMP[100] = "D5VK RAMP"; - static char deviceNameRAMP[100] = "D5VK RAMP"; - - HRESULT hr = lpEnumDevicesCallback(&guidRAMP, &deviceDescRAMP[0], &deviceNameRAMP[0], - &descRAMP_HAL, &descRAMP_HEL, lpUserArg); + if (likely(!d3dOptions->legacyDeviceNames)) { + static char deviceDescRAMP[100] = "D5VK Ramp"; + static char deviceNameRAMP[100] = "D5VK Ramp"; + hr = lpEnumDevicesCallback(&guidRAMP, &deviceDescRAMP[0], &deviceNameRAMP[0], + &descRAMP_HAL, &descRAMP_HEL, lpUserArg); + } else { + static char legacyDeviceDescRAMP[100] = "Ramp Emulation"; + static char legacyDeviceNameRAMP[100] = "Ramp Emulation"; + hr = lpEnumDevicesCallback(&guidRAMP, &legacyDeviceDescRAMP[0], &legacyDeviceNameRAMP[0], + &descRAMP_HAL, &descRAMP_HEL, lpUserArg); + } if (hr != D3DENUMRET_OK) return D3D_OK; @@ -181,11 +195,17 @@ namespace dxvk { desc2RGB_HEL.dpcTriCaps.dwTextureCaps |= D3DPTEXTURECAPS_POW2; memcpy(&descRGB_HAL, &desc2RGB_HAL, sizeof(D3DDEVICEDESC2)); memcpy(&descRGB_HEL, &desc2RGB_HEL, sizeof(D3DDEVICEDESC2)); - static char deviceDescRGB[100] = "D5VK RGB"; - static char deviceNameRGB[100] = "D5VK RGB"; - - hr = lpEnumDevicesCallback(&guidRGB, &deviceDescRGB[0], &deviceNameRGB[0], - &descRGB_HAL, &descRGB_HEL, lpUserArg); + if (likely(!d3dOptions->legacyDeviceNames)) { + static char deviceDescRGB[100] = "D5VK RGB"; + static char deviceNameRGB[100] = "D5VK RGB"; + hr = lpEnumDevicesCallback(&guidRGB, &deviceDescRGB[0], &deviceNameRGB[0], + &descRGB_HAL, &descRGB_HEL, lpUserArg); + } else { + static char legacyDeviceDescRGB[100] = "RGB Emulation"; + static char legacyDeviceNameRGB[100] = "RGB Emulation"; + hr = lpEnumDevicesCallback(&guidRGB, &legacyDeviceDescRGB[0], &legacyDeviceNameRGB[0], + &descRGB_HAL, &descRGB_HEL, lpUserArg); + } if (hr != D3DENUMRET_OK) return D3D_OK; @@ -206,11 +226,17 @@ namespace dxvk { & ~D3DDEVCAPS_DRAWPRIMITIVES2EX; memcpy(&descHAL_HAL, &desc2HAL_HAL, sizeof(D3DDEVICEDESC2)); memcpy(&descHAL_HEL, &desc2HAL_HEL, sizeof(D3DDEVICEDESC2)); - static char deviceDescHAL[100] = "D5VK HAL"; - static char deviceNameHAL[100] = "D5VK HAL"; - - hr = lpEnumDevicesCallback(&guidHAL, &deviceDescHAL[0], &deviceNameHAL[0], - &descHAL_HAL, &descHAL_HEL, lpUserArg); + if (likely(!d3dOptions->legacyDeviceNames)) { + static char deviceDescHAL[100] = "D5VK HAL"; + static char deviceNameHAL[100] = "D5VK HAL"; + hr = lpEnumDevicesCallback(&guidHAL, &deviceDescHAL[0], &deviceNameHAL[0], + &descHAL_HAL, &descHAL_HEL, lpUserArg); + } else { + static char legacyDeviceDescHAL[100] = "Direct3D HAL"; + static char legacyDeviceNameHAL[100] = "Direct3D HAL"; + hr = lpEnumDevicesCallback(&guidHAL, &legacyDeviceDescHAL[0], &legacyDeviceNameHAL[0], + &descHAL_HAL, &descHAL_HEL, lpUserArg); + } if (hr != D3DENUMRET_OK) return D3D_OK; @@ -225,7 +251,7 @@ namespace dxvk { InitReturnPtr(lplpDirect3DLight); - *lplpDirect3DLight = ref(new D3DLight(nullptr, this)); + *lplpDirect3DLight = ref(new D3DLight()); return D3D_OK; } @@ -395,7 +421,7 @@ namespace dxvk { bool rgbFallback = false; if (likely(!d3dOptions->forceSWVP)) { - if (rclsid == IID_IDirect3DHALDevice) { + if (rclsid == IID_IDirect3DHALDevice || rclsid == IID_WineD3DDevice) { Logger::info("D3D5Interface::CreateDevice: Creating an IID_IDirect3DHALDevice device"); deviceCreationFlags9 = D3DCREATE_MIXED_VERTEXPROCESSING; } else if (rclsid == IID_IDirect3DRGBDevice) { @@ -448,7 +474,7 @@ namespace dxvk { } Com d3d5DeviceProxy; - HRESULT hr = m_proxy->CreateDevice(rclsidOverride, rt->GetProxied(), &d3d5DeviceProxy); + HRESULT hr = m_proxy->CreateDevice(rclsidOverride, rt->GetShadowOrProxied(), &d3d5DeviceProxy); if (unlikely(FAILED(hr))) { Logger::warn("D3D5Interface::CreateDevice: Failed to create the proxy device"); return hr; @@ -461,8 +487,7 @@ namespace dxvk { DWORD backBufferWidth = desc.dwWidth; DWORD BackBufferHeight = desc.dwHeight; - if (likely(!d3dOptions->forceProxiedPresent && - d3dOptions->backBufferResize)) { + if (likely(d3dOptions->backBufferResize)) { const bool exclusiveMode = m_commonIntf->GetCooperativeLevel() & DDSCL_EXCLUSIVE; // Ignore any mode size dimensions when in windowed present mode @@ -479,29 +504,7 @@ namespace dxvk { } } - d3d9::D3DFORMAT backBufferFormat = ConvertFormat(desc.ddpfPixelFormat); - - // Determine the supported AA sample count by querying the D3D9 interface - d3d9::D3DMULTISAMPLE_TYPE multiSampleType = d3d9::D3DMULTISAMPLE_NONE; - if (likely(d3dOptions->emulateFSAA != FSAAEmulation::Disabled)) { - HRESULT hr4S = m_d3d9->CheckDeviceMultiSampleType(0, d3d9::D3DDEVTYPE_HAL, backBufferFormat, - TRUE, d3d9::D3DMULTISAMPLE_4_SAMPLES, NULL); - if (unlikely(FAILED(hr4S))) { - HRESULT hr2S = m_d3d9->CheckDeviceMultiSampleType(0, d3d9::D3DDEVTYPE_HAL, backBufferFormat, - TRUE, d3d9::D3DMULTISAMPLE_2_SAMPLES, NULL); - if (unlikely(FAILED(hr2S))) { - Logger::warn("D3D5Interface::CreateDevice: No MSAA support has been detected"); - } else { - Logger::info("D3D5Interface::CreateDevice: Using 2x MSAA for FSAA emulation"); - multiSampleType = d3d9::D3DMULTISAMPLE_2_SAMPLES; - } - } else { - Logger::info("D3D5Interface::CreateDevice: Using 4x MSAA for FSAA emulation"); - multiSampleType = d3d9::D3DMULTISAMPLE_4_SAMPLES; - } - } else { - Logger::info("D3D5Interface::CreateDevice: FSAA emulation is disabled"); - } + const d3d9::D3DFORMAT backBufferFormat = ConvertFormat(desc.ddpfPixelFormat); const DWORD cooperativeLevel = m_commonIntf->GetCooperativeLevel(); @@ -518,22 +521,17 @@ namespace dxvk { Logger::info(str::format("D3D5Interface::CreateDevice: Back buffer size: ", desc.dwWidth, "x", desc.dwHeight)); - DWORD backBufferCount = 0; - if (likely(!d3dOptions->forceSingleBackBuffer)) { - IDirectDrawSurface* backBuffer = rt->GetProxied(); - while (backBuffer != nullptr) { - IDirectDrawSurface* parentSurface = backBuffer; - backBuffer = nullptr; - parentSurface->EnumAttachedSurfaces(&backBuffer, ListBackBufferSurfacesCallback); - backBufferCount++; - // the swapchain will eventually return to its origin - if (backBuffer == rt->GetProxied()) - break; - } - } + const DWORD backBufferCount = DetermineBackBufferCount(rt->GetProxied()); // Consider the front buffer as well when reporting the overall count Logger::info(str::format("D3D5Interface::CreateDevice: Back buffer count: ", backBufferCount + 1)); + Com d3d9Intf = m_commonD3DIntf->GetD3D9Interface(); + + // Determine the supported AA sample count by querying the D3D9 interface + const d3d9::D3DMULTISAMPLE_TYPE multiSampleType = d3dOptions->emulateFSAA != FSAAEmulation::Disabled ? + GetSupportedMultiSampleType(d3d9Intf.ptr(), backBufferFormat) : + d3d9::D3DMULTISAMPLE_NONE; + d3d9::D3DPRESENT_PARAMETERS params; params.BackBufferWidth = backBufferWidth; params.BackBufferHeight = BackBufferHeight; @@ -551,7 +549,7 @@ namespace dxvk { params.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT; // A D3D5 device always uses VSync Com device9; - hr = m_d3d9->CreateDevice( + hr = d3d9Intf->CreateDevice( D3DADAPTER_DEFAULT, d3d9::D3DDEVTYPE_HAL, hWnd, @@ -586,4 +584,34 @@ namespace dxvk { return D3D_OK; } + inline DWORD D3D5Interface::DetermineBackBufferCount(IDirectDrawSurface* renderTarget) { + DWORD backBufferCount = 0; + + const D3DOptions* d3dOptions = m_commonIntf->GetOptions(); + + if (likely(!d3dOptions->forceSingleBackBuffer && !d3dOptions->forceLegacyPresent)) { + IDirectDrawSurface* backBuffer = renderTarget; + HRESULT hr; + + while (backBuffer != nullptr) { + IDirectDrawSurface* parentSurface = backBuffer; + backBuffer = nullptr; + + hr = parentSurface->EnumAttachedSurfaces(&backBuffer, ListBackBufferSurfacesCallback); + if (unlikely(FAILED(hr))) { + Logger::warn("D3D5Interface::DetermineBackBufferCount: Unable to enumerate attached surfaces"); + break; + } + + backBufferCount++; + + // the swapchain will eventually return to its origin + if (backBuffer == renderTarget) + break; + } + } + + return backBufferCount; + } + } \ No newline at end of file diff --git a/src/ddraw/d3d5/d3d5_interface.h b/src/ddraw/d3d5/d3d5_interface.h index 597b350ba4c..fc14768b437 100644 --- a/src/ddraw/d3d5/d3d5_interface.h +++ b/src/ddraw/d3d5/d3d5_interface.h @@ -16,7 +16,7 @@ namespace dxvk { /** * \brief D3D5 interface implementation */ - class D3D5Interface final : public DDrawWrappedObject { + class D3D5Interface final : public DDrawWrappedObject { public: D3D5Interface( @@ -55,6 +55,8 @@ namespace dxvk { private: + inline DWORD DetermineBackBufferCount(IDirectDrawSurface* renderTarget); + static uint32_t s_intfCount; uint32_t m_intfCount = 0; diff --git a/src/ddraw/d3d5/d3d5_material.cpp b/src/ddraw/d3d5/d3d5_material.cpp index ce3ac3ae14a..94c303b75c3 100644 --- a/src/ddraw/d3d5/d3d5_material.cpp +++ b/src/ddraw/d3d5/d3d5_material.cpp @@ -1,9 +1,10 @@ #include "d3d5_material.h" -#include "d3d5_device.h" #include "d3d5_interface.h" #include "d3d5_viewport.h" +#include "../d3d_common_device.h" + #include "../ddraw/ddraw_interface.h" namespace dxvk { @@ -14,7 +15,7 @@ namespace dxvk { Com&& proxyMaterial, D3D5Interface* pParent, D3DMATERIALHANDLE handle) - : DDrawWrappedObject(pParent, std::move(proxyMaterial), nullptr) { + : DDrawWrappedObject(pParent, std::move(proxyMaterial)) { m_commonMaterial = new D3DCommonMaterial(handle); m_commonMaterial->SetD3D5Material(this); @@ -32,12 +33,33 @@ namespace dxvk { Logger::debug(str::format("D3D5Material: Material nr. [[2-", m_materialCount, "]] bites the dust")); } + HRESULT STDMETHODCALLTYPE D3D5Material::QueryInterface(REFIID riid, void** ppvObject) { + Logger::debug(">>> D3D5Material::QueryInterface"); + + if (unlikely(ppvObject == nullptr)) + return E_POINTER; + + InitReturnPtr(ppvObject); + + try { + *ppvObject = ref(this->GetInterface(riid)); + return S_OK; + } catch (const DxvkError& e) { + Logger::warn(e.message()); + Logger::warn(str::format(riid)); + return E_NOINTERFACE; + } + } + HRESULT STDMETHODCALLTYPE D3D5Material::SetMaterial(D3DMATERIAL *data) { Logger::debug(">>> D3D5Material::SetMaterial"); if (unlikely(data == nullptr)) return DDERR_INVALIDPARAMS; + if (unlikely(!data->dwSize)) + return DDERR_INVALIDPARAMS; + // This call needs to be forwarded to the proxied material // too, in order to have a proper color used during proxied clears HRESULT hr = m_proxy->SetMaterial(data); @@ -52,22 +74,14 @@ namespace dxvk { material9->Emissive = data->dcvEmissive; material9->Power = data->dvPower; - D3DMATERIALHANDLE handle = m_commonMaterial->GetMaterialHandle(); - - Logger::debug(str::format(">>> D3D5Material::SetMaterial: Updated material nr. ", handle)); - Logger::debug(str::format(" Diffuse: ", material9->Diffuse.r, " ", material9->Diffuse.g, " ", material9->Diffuse.b)); - Logger::debug(str::format(" Ambient: ", material9->Ambient.r, " ", material9->Ambient.g, " ", material9->Ambient.b)); - Logger::debug(str::format(" Specular: ", material9->Specular.r, " ", material9->Specular.g, " ", material9->Specular.b)); - Logger::debug(str::format(" Emissive: ", material9->Emissive.r, " ", material9->Emissive.g, " ", material9->Emissive.b)); - Logger::debug(str::format(" Power: ", material9->Power)); - // Update the D3D9 material directly if it's actively being used - D3D5Device* device5 = m_parent->GetCommonInterface()->GetCommonD3DDevice()->GetD3D5Device(); - if (likely(device5 != nullptr)) { - D3DMATERIALHANDLE currentHandle = device5->GetCurrentMaterialHandle(); + D3DCommonDevice* commonDevice = m_parent->GetCommonInterface()->GetCommonD3DDevice(); + if (likely(commonDevice != nullptr)) { + const D3DMATERIALHANDLE handle = m_commonMaterial->GetMaterialHandle(); + const D3DMATERIALHANDLE currentHandle = commonDevice->GetCurrentMaterialHandle(); if (currentHandle == handle) { Logger::debug(str::format("D3D5Material::SetMaterial: Applying material nr. ", handle, " to D3D9")); - device5->GetD3D9()->SetMaterial(material9); + commonDevice->GetD3D9Device()->SetMaterial(material9); } } diff --git a/src/ddraw/d3d5/d3d5_material.h b/src/ddraw/d3d5/d3d5_material.h index cff7ceed7ce..dacfa1bdce5 100644 --- a/src/ddraw/d3d5/d3d5_material.h +++ b/src/ddraw/d3d5/d3d5_material.h @@ -9,7 +9,7 @@ namespace dxvk { class D3D5Interface; - class D3D5Material final : public DDrawWrappedObject { + class D3D5Material final : public DDrawWrappedObject { public: @@ -20,6 +20,8 @@ namespace dxvk { ~D3D5Material(); + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject); + HRESULT STDMETHODCALLTYPE SetMaterial(D3DMATERIAL *data); HRESULT STDMETHODCALLTYPE GetMaterial(D3DMATERIAL *data); diff --git a/src/ddraw/d3d5/d3d5_texture.cpp b/src/ddraw/d3d5/d3d5_texture.cpp index 31b901cd28f..752e3a8da3f 100644 --- a/src/ddraw/d3d5/d3d5_texture.cpp +++ b/src/ddraw/d3d5/d3d5_texture.cpp @@ -12,7 +12,7 @@ namespace dxvk { Com&& proxyTexture, DDrawSurface* pParent, D3DTEXTUREHANDLE handle) - : DDrawWrappedObject(pParent, std::move(proxyTexture), nullptr) { + : DDrawWrappedObject(pParent, std::move(proxyTexture)) { m_commonTex = new D3DCommonTexture(m_parent->GetCommonSurface(), handle); m_texCount = ++s_texCount; @@ -69,6 +69,14 @@ namespace dxvk { Logger::debug("D3D5Texture::QueryInterface: Query for IDirectDrawSurface3"); return m_parent->QueryInterface(riid, ppvObject); } + if (unlikely(riid == __uuidof(IDirectDrawSurface4))) { + Logger::debug("D3D5Texture::QueryInterface: Query for IDirectDrawSurface4"); + return m_parent->QueryInterface(riid, ppvObject); + } + if (unlikely(riid == __uuidof(IDirectDrawSurface7))) { + Logger::debug("D3D5Texture::QueryInterface: Query for IDirectDrawSurface7"); + return m_parent->QueryInterface(riid, ppvObject); + } try { *ppvObject = ref(this->GetInterface(riid)); @@ -91,9 +99,11 @@ namespace dxvk { return D3D_OK; } + // Docs state: "This method only affects the legacy ramp device. + // For all other devices, this method takes no action and returns D3D_OK." HRESULT STDMETHODCALLTYPE D3D5Texture::PaletteChanged(DWORD dwStart, DWORD dwCount) { - Logger::warn("<<< D3D5Texture::PaletteChanged: Proxy"); - return m_proxy->PaletteChanged(dwStart, dwCount); + Logger::debug(">>> D3D5Texture::PaletteChanged"); + return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D5Texture::Load(LPDIRECT3DTEXTURE2 lpD3DTexture2) { @@ -116,7 +126,7 @@ namespace dxvk { m_parent->GetCommonSurface()->SetDesc(desc); } - m_parent->GetCommonSurface()->DirtyMipMaps(); + m_parent->GetCommonSurface()->DirtyDDrawSurface(); return hr; } diff --git a/src/ddraw/d3d5/d3d5_texture.h b/src/ddraw/d3d5/d3d5_texture.h index 382fad268d6..e0b912099de 100644 --- a/src/ddraw/d3d5/d3d5_texture.h +++ b/src/ddraw/d3d5/d3d5_texture.h @@ -9,7 +9,7 @@ namespace dxvk { class DDrawSurface; - class D3D5Texture final : public DDrawWrappedObject { + class D3D5Texture final : public DDrawWrappedObject { public: diff --git a/src/ddraw/d3d5/d3d5_viewport.cpp b/src/ddraw/d3d5/d3d5_viewport.cpp index 863c27710d5..12cd1d72897 100644 --- a/src/ddraw/d3d5/d3d5_viewport.cpp +++ b/src/ddraw/d3d5/d3d5_viewport.cpp @@ -21,7 +21,7 @@ namespace dxvk { D3DCommonViewport* commonViewport, Com&& proxyViewport, D3D5Interface* pParent) - : DDrawWrappedObject(pParent, std::move(proxyViewport), nullptr) + : DDrawWrappedObject(pParent, std::move(proxyViewport)) , m_commonViewport ( commonViewport ) { if (m_commonViewport == nullptr) @@ -353,14 +353,18 @@ namespace dxvk { D3DCOLOR clearColor = commonMaterial != nullptr ? commonMaterial->GetMaterialColor() : defaultColor; HRESULT hr9 = d3d9Device->Clear(count, rects, flags, clearColor, 1.0f, 0u); - if (unlikely(FAILED(hr9))) - Logger::err("D3D5Viewport::Clear: Failed D3D9 Clear call"); // Restore the previously active viewport if (!m_commonViewport->IsCurrentViewport()) { d3d9Device->SetViewport(¤tViewport9); } + if (unlikely(FAILED(hr9))) { + Logger::warn("D3D5Viewport::Clear: Failed D3D9 Clear call"); + } else { + m_commonViewport->UpdateSurfaceDirtyTracking(true, false, false); + } + return D3D_OK; } @@ -375,11 +379,6 @@ namespace dxvk { if (unlikely(d3dLight->HasViewport())) return D3DERR_LIGHTHASVIEWPORT; - if (m_commonViewport->HasDevice()) { - Logger::debug("D3D5Viewport::AddLight: Enabling device legacy light model"); - m_commonViewport->EnableLegacyLights(d3dLight->IsD3DLight2()); - } - std::vector>& lights = m_commonViewport->GetLights(); // No need to check if the light is already attached, since // if that's the case it will have a set viewport above @@ -552,6 +551,25 @@ namespace dxvk { return D3D_OK; } + HRESULT D3D5Viewport::DeactivateLights() { + std::vector>& lights = m_commonViewport->GetLights(); + + if (!lights.size()) + return D3D_OK; + + Logger::debug("D3D5Viewport: Deactivating D3D9 lights"); + + for (auto light: lights) { + const DWORD lightIndex = light->GetIndex(); + if (m_commonViewport->HasDevice() && m_commonViewport->IsCurrentViewport() && light->IsActive()) { + Logger::debug(str::format("D3D5Viewport: Disabling light nr. ", lightIndex)); + m_commonViewport->GetD3D9Device()->LightEnable(lightIndex, FALSE); + } + } + + return D3D_OK; + } + HRESULT D3D5Viewport::ApplyAndActivateLight(DWORD index, D3DLight* light) { d3d9::IDirect3DDevice9* d3d9Device = m_commonViewport->GetD3D9Device(); @@ -561,12 +579,12 @@ namespace dxvk { } else { HRESULT hrLE; if (light->IsActive()) { - Logger::debug(str::format("D3D5Viewport: Enabling light nr. ", index)); + Logger::debug(str::format("D3D5Viewport: Enabling D3D9 light nr. ", index)); hrLE = d3d9Device->LightEnable(index, TRUE); if (unlikely(FAILED(hrLE))) Logger::err("D3D5Viewport: Failed D3D9 LightEnable call (TRUE)"); } else { - Logger::debug(str::format("D3D5Viewport: Disabling light nr. ", index)); + Logger::debug(str::format("D3D5Viewport: Disabling D3D9 light nr. ", index)); hrLE = d3d9Device->LightEnable(index, FALSE); if (unlikely(FAILED(hrLE))) Logger::err("D3D5Viewport: Failed D3D9 LightEnable call (FALSE)"); diff --git a/src/ddraw/d3d5/d3d5_viewport.h b/src/ddraw/d3d5/d3d5_viewport.h index 4612dd40dc0..f27f6f63a84 100644 --- a/src/ddraw/d3d5/d3d5_viewport.h +++ b/src/ddraw/d3d5/d3d5_viewport.h @@ -15,7 +15,7 @@ namespace dxvk { class D3D6Viewport; class D3D3Viewport; - class D3D5Viewport final : public DDrawWrappedObject { + class D3D5Viewport final : public DDrawWrappedObject { public: @@ -66,6 +66,8 @@ namespace dxvk { HRESULT ApplyAndActivateLights(); + HRESULT DeactivateLights(); + HRESULT ApplyAndActivateLight(DWORD index, D3DLight* light); D3DCommonViewport* GetCommonViewport() const { diff --git a/src/ddraw/d3d6/d3d6_buffer.cpp b/src/ddraw/d3d6/d3d6_buffer.cpp index 661baee06e5..5b027ceb6fd 100644 --- a/src/ddraw/d3d6/d3d6_buffer.cpp +++ b/src/ddraw/d3d6/d3d6_buffer.cpp @@ -4,6 +4,7 @@ #include "../ddraw_util.h" +#include "../d3d_process_vertices.h" #include "../d3d_multithread.h" #include "../ddraw4/ddraw4_interface.h" @@ -13,12 +14,10 @@ namespace dxvk { uint32_t D3D6VertexBuffer::s_buffCount = 0; D3D6VertexBuffer::D3D6VertexBuffer( - Com&& buffProxy, - Com&& pBuffer9, D3D6Interface* pParent, DWORD creationFlags, D3DVERTEXBUFFERDESC desc) - : DDrawWrappedObject(pParent, std::move(buffProxy), std::move(pBuffer9)) + : DDrawWrappedObject(pParent, nullptr) , m_commonIntf ( pParent->GetCommonInterface() ) , m_creationFlags ( creationFlags ) , m_desc ( desc ) @@ -33,6 +32,24 @@ namespace dxvk { Logger::debug(str::format("D3D6VertexBuffer: Buffer nr. {{1-", m_buffCount, "}} bites the dust")); } + HRESULT STDMETHODCALLTYPE D3D6VertexBuffer::QueryInterface(REFIID riid, void** ppvObject) { + Logger::debug(">>> D3D6VertexBuffer::QueryInterface"); + + if (unlikely(ppvObject == nullptr)) + return E_POINTER; + + InitReturnPtr(ppvObject); + + try { + *ppvObject = ref(this->GetInterface(riid)); + return S_OK; + } catch (const DxvkError& e) { + Logger::warn(e.message()); + Logger::warn(str::format(riid)); + return E_NOINTERFACE; + } + } + HRESULT STDMETHODCALLTYPE D3D6VertexBuffer::GetVertexBufferDesc(LPD3DVERTEXBUFFERDESC lpVBDesc) { Logger::debug(">>> D3D6VertexBuffer::GetVertexBufferDesc"); @@ -55,7 +72,7 @@ namespace dxvk { if (unlikely(IsOptimized())) return D3DERR_VERTEXBUFFEROPTIMIZED; - RefreshD3D6Device(); + RefreshD3DDevice(); if (unlikely(!IsInitialized())) { HRESULT hrInit = InitializeD3D9(); if (unlikely(FAILED(hrInit))) @@ -65,7 +82,7 @@ namespace dxvk { if (data_size != nullptr) *data_size = m_size; - HRESULT hr = m_d3d9->Lock(0, 0, data, ConvertD3D6LockFlags(flags, false)); + HRESULT hr = m_vb9->Lock(0, 0, data, ConvertD3D6LockFlags(flags, false)); if (likely(SUCCEEDED(hr))) m_locked = true; @@ -76,14 +93,14 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE D3D6VertexBuffer::Unlock() { Logger::debug(">>> D3D6VertexBuffer::Unlock"); - RefreshD3D6Device(); + RefreshD3DDevice(); if (unlikely(!IsInitialized())) { HRESULT hrInit = InitializeD3D9(); if (unlikely(FAILED(hrInit))) return hrInit; } - HRESULT hr = m_d3d9->Unlock(); + HRESULT hr = m_vb9->Unlock(); if (likely(SUCCEEDED(hr))) m_locked = false; @@ -105,11 +122,11 @@ namespace dxvk { if (unlikely(!(dwVertexOp & D3DVOP_TRANSFORM))) return DDERR_INVALIDPARAMS; - D3D6Device* device = static_cast(lpD3DDevice); + D3D6Device* device6 = static_cast(lpD3DDevice); D3D6VertexBuffer* vb = static_cast(lpSrcBuffer); - vb->RefreshD3D6Device(); - if (unlikely(vb->GetDevice() == nullptr || device != vb->GetDevice())) { + vb->RefreshD3DDevice(); + if (unlikely(vb->GetDevice() == nullptr || device6 != vb->GetDevice())) { Logger::err("D3D6VertexBuffer::ProcessVertices: Incompatible or null device"); return DDERR_GENERIC; } @@ -124,25 +141,86 @@ namespace dxvk { } // Check and initialize the destination buffer (this buffer) - RefreshD3D6Device(); + d3d9::IDirect3DDevice9* device9 = RefreshD3DDevice(); if (unlikely(!IsInitialized())) { hrInit = InitializeD3D9(); if (unlikely(FAILED(hrInit))) return hrInit; } - D3DDeviceLock lock = device->LockDevice(); + D3DDeviceLock lock = device6->LockDevice(); - HandlePreProcessVerticesFlags(dwVertexOp); + HRESULT hr = D3D_OK; - device->GetD3D9()->SetFVF(m_desc.dwFVF); - device->GetD3D9()->SetStreamSource(0, vb->GetD3D9(), 0, vb->GetStride()); - HRESULT hr = device->GetD3D9()->ProcessVertices(dwSrcIndex, dwDestIndex, dwCount, m_d3d9.ptr(), nullptr, dwFlags); - if (unlikely(FAILED(hr))) { - Logger::err("D3D6VertexBuffer::ProcessVertices: Failed call to D3D9 ProcessVertices"); - } + const D3DOptions* d3dOptions = m_commonIntf->GetOptions(); + + if (likely(d3dOptions->cpuProcessVertices)) { + uint8_t *inData = nullptr; + uint8_t *outData = nullptr; + + HRESULT hr = vb->GetD3D9VertexBuffer()->Lock(dwSrcIndex * vb->GetStride(), dwCount * vb->GetStride(), + reinterpret_cast(&inData), D3DLOCK_READONLY|D3DLOCK_NOSYSLOCK); + if (unlikely(FAILED(hr))) { + Logger::err("D3D6VertexBuffer::ProcessVertices: Failed to lock source buffer"); + return D3DERR_VERTEXBUFFERLOCKED; + } + + hr = m_vb9->Lock(dwDestIndex * m_stride, dwCount * m_stride, reinterpret_cast(&outData), D3DLOCK_NOSYSLOCK); + if (unlikely(FAILED(hr))) { + Logger::err("D3D6VertexBuffer::ProcessVertices: Failed to lock destination buffer"); + vb->Unlock(); + return D3DERR_VERTEXBUFFERLOCKED; + } + + D3DCommonViewport* commonViewport = device6->GetCurrentViewportInternal()->GetCommonViewport(); + + ProcessVerticesData pvData; + pvData.inData = inData; + pvData.inFVF = vb->GetFVF(); + pvData.inStride = vb->GetStride(); + pvData.outData = outData; + pvData.outFVF = m_desc.dwFVF; + pvData.outStride = m_stride; + pvData.vertexCount = dwCount; + pvData.correction = commonViewport->GetLegacyProjectionMatrix(0); + pvData.dsStatus = nullptr; + pvData.doLighting = dwVertexOp & D3DVOP_LIGHT; + pvData.doClipping = dwVertexOp & D3DVOP_CLIP; + pvData.doNotCopyData = dwFlags & D3DPV_DONOTCOPYDATA; + pvData.doExtents = true; + pvData.isLegacy = true; + + std::vector> lights = commonViewport->GetLights(); + std::vector lights9; + + for (auto light: lights) { + if (!light->IsActive()) + continue; + + const d3d9::D3DLIGHT9* light9 = light->GetD3D9Light(); + if (light9 != nullptr) + lights9.push_back(*light9); + } + + pvData.lights = &lights9; + + ProcessVerticesSW(device9, m_commonIntf->GetOptions(), &pvData); - HandlePostProcessVerticesFlags(dwVertexOp); + m_vb9->Unlock(); + vb->Unlock(); + + } else { + HandlePreProcessVerticesFlags(dwVertexOp); + + device9->SetFVF(vb->GetFVF()); + device9->SetStreamSource(0, vb->GetD3D9VertexBuffer(), 0, vb->GetStride()); + hr = device9->ProcessVertices(dwSrcIndex, dwDestIndex, dwCount, m_vb9.ptr(), nullptr, dwFlags); + if (unlikely(FAILED(hr))) { + Logger::err("D3D6VertexBuffer::ProcessVertices: Failed call to D3D9 ProcessVertices"); + } + + HandlePostProcessVerticesFlags(dwVertexOp); + } return hr; } @@ -171,6 +249,8 @@ namespace dxvk { return DDERR_GENERIC; } + d3d9::IDirect3DDevice9* device9 = m_d3d6Device->GetCommonD3DDevice()->GetD3D9Device(); + d3d9::D3DPOOL pool = d3d9::D3DPOOL_DEFAULT; if (m_desc.dwCaps & D3DVBCAPS_SYSTEMMEMORY) @@ -181,7 +261,7 @@ namespace dxvk { Logger::debug(str::format("D3D6VertexBuffer::InitializeD3D9: Placing in: ", poolPlacement)); const DWORD usage = ConvertD3D6UsageFlags(m_desc.dwCaps, m_creationFlags); - HRESULT hr = m_d3d6Device->GetD3D9()->CreateVertexBuffer(m_size, usage, m_desc.dwFVF, pool, &m_d3d9, nullptr); + HRESULT hr = device9->CreateVertexBuffer(m_size, usage, m_desc.dwFVF, pool, &m_vb9, nullptr); if (unlikely(FAILED(hr))) { Logger::err("D3D6VertexBuffer::InitializeD3D9: Failed to create D3D9 vertex buffer"); @@ -193,18 +273,44 @@ namespace dxvk { return DD_OK; } - void D3D6VertexBuffer::RefreshD3D6Device() { - D3DCommonDevice* commonDevice = m_commonIntf->GetCommonD3DDevice(); + d3d9::IDirect3DDevice9* D3D6VertexBuffer::RefreshD3DDevice() { + D3DCommonDevice* commonD3DDevice = m_commonIntf->GetCommonD3DDevice(); - D3D6Device* d3d6Device = commonDevice != nullptr ? commonDevice->GetD3D6Device() : nullptr; + D3D6Device* d3d6Device = commonD3DDevice != nullptr ? commonD3DDevice->GetD3D6Device() : nullptr; if (unlikely(m_d3d6Device != d3d6Device)) { // Check if the device has been recreated and reset all D3D9 resources if (unlikely(m_d3d6Device != nullptr)) { - Logger::debug("D3D6VertexBuffer::RefreshD3D6Device: Device context has changed, clearing D3D9 buffers"); - m_d3d9 = nullptr; + Logger::debug("D3D6VertexBuffer::RefreshD3DDevice: Device context has changed, clearing D3D9 buffers"); + m_vb9 = nullptr; } m_d3d6Device = d3d6Device; } + + return commonD3DDevice != nullptr ? commonD3DDevice->GetD3D9Device() : nullptr; + } + + inline void D3D6VertexBuffer::HandlePreProcessVerticesFlags(DWORD pvFlags) { + // Disable lighting if the D3DVOP_LIGHT isn't specified + if (!(pvFlags & D3DVOP_LIGHT)) { + d3d9::IDirect3DDevice9* device9 = m_d3d6Device->GetCommonD3DDevice()->GetD3D9Device(); + + device9->GetRenderState(d3d9::D3DRS_LIGHTING, &m_lighting); + if (m_lighting) { + //Logger::debug("D3D6VertexBuffer: Disabling lighting"); + device9->SetRenderState(d3d9::D3DRS_LIGHTING, FALSE); + } + } + m_d3d6Device->HandlePreDrawLegacyProjection(0); + } + + inline void D3D6VertexBuffer::HandlePostProcessVerticesFlags(DWORD pvFlags) { + if (!(pvFlags & D3DVOP_LIGHT) && m_lighting) { + d3d9::IDirect3DDevice9* device9 = m_d3d6Device->GetCommonD3DDevice()->GetD3D9Device(); + + //Logger::debug("D3D6VertexBuffer: Enabling lighting"); + device9->SetRenderState(d3d9::D3DRS_LIGHTING, TRUE); + } + m_d3d6Device->HandlePostDrawLegacyProjection(); } } diff --git a/src/ddraw/d3d6/d3d6_buffer.h b/src/ddraw/d3d6/d3d6_buffer.h index 9881e24205e..cc8a2bd9ca9 100644 --- a/src/ddraw/d3d6/d3d6_buffer.h +++ b/src/ddraw/d3d6/d3d6_buffer.h @@ -10,19 +10,19 @@ namespace dxvk { - class D3D6VertexBuffer final : public DDrawWrappedObject { + class D3D6VertexBuffer final : public DDrawWrappedObject { public: D3D6VertexBuffer( - Com&& buffProxy, - Com&& pBuffer9, D3D6Interface* pParent, DWORD creationFlags, D3DVERTEXBUFFERDESC desc); ~D3D6VertexBuffer(); + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject); + HRESULT STDMETHODCALLTYPE GetVertexBufferDesc(LPD3DVERTEXBUFFERDESC lpVBDesc); HRESULT STDMETHODCALLTYPE Lock(DWORD dwFlags, LPVOID* lplpData, LPDWORD lpdwSize); @@ -35,7 +35,15 @@ namespace dxvk { HRESULT InitializeD3D9(); - void RefreshD3D6Device(); + d3d9::IDirect3DDevice9* RefreshD3DDevice(); + + bool IsInitialized() const { + return m_vb9 != nullptr; + } + + d3d9::IDirect3DVertexBuffer9* GetD3D9VertexBuffer() const { + return m_vb9.ptr(); + } DWORD GetFVF() const { return m_desc.dwFVF; @@ -59,26 +67,12 @@ namespace dxvk { private: - inline bool IsOptimized() const { - return m_desc.dwCaps & D3DVBCAPS_OPTIMIZED; - } + inline void HandlePreProcessVerticesFlags(DWORD pvFlags); - inline void HandlePreProcessVerticesFlags(DWORD pvFlags) { - // Disable lighting if the D3DVOP_LIGHT isn't specified - if (!(pvFlags & D3DVOP_LIGHT)) { - m_d3d6Device->GetD3D9()->GetRenderState(d3d9::D3DRS_LIGHTING, &m_lighting); - if (m_lighting) { - //Logger::debug("D3D6VertexBuffer: Disabling lighting"); - m_d3d6Device->GetD3D9()->SetRenderState(d3d9::D3DRS_LIGHTING, FALSE); - } - } - } + inline void HandlePostProcessVerticesFlags(DWORD pvFlags); - inline void HandlePostProcessVerticesFlags(DWORD pvFlags) { - if (!(pvFlags & D3DVOP_LIGHT) && m_lighting) { - //Logger::debug("D3D6VertexBuffer: Enabling lighting"); - m_d3d6Device->GetD3D9()->SetRenderState(d3d9::D3DRS_LIGHTING, TRUE); - } + inline bool IsOptimized() const { + return m_desc.dwCaps & D3DVBCAPS_OPTIMIZED; } inline void ListBufferDetails() const { @@ -88,22 +82,24 @@ namespace dxvk { Logger::debug(str::format(" Vertices: ", m_size / m_stride)); } - bool m_locked = false; + bool m_locked = false; + + static uint32_t s_buffCount; + uint32_t m_buffCount = 0; - static uint32_t s_buffCount; - uint32_t m_buffCount = 0; + DDrawCommonInterface* m_commonIntf = nullptr; - DDrawCommonInterface* m_commonIntf = nullptr; + D3D6Device* m_d3d6Device = nullptr; - D3D6Device* m_d3d6Device = nullptr; + Com m_vb9; - DWORD m_lighting = FALSE; + DWORD m_lighting = FALSE; - DWORD m_creationFlags = 0; - D3DVERTEXBUFFERDESC m_desc; + DWORD m_creationFlags = 0; + D3DVERTEXBUFFERDESC m_desc; - UINT m_stride = 0; - UINT m_size = 0; + UINT m_stride = 0; + UINT m_size = 0; }; diff --git a/src/ddraw/d3d6/d3d6_device.cpp b/src/ddraw/d3d6/d3d6_device.cpp index 0cfa60d3e34..1156f202816 100644 --- a/src/ddraw/d3d6/d3d6_device.cpp +++ b/src/ddraw/d3d6/d3d6_device.cpp @@ -26,12 +26,10 @@ namespace dxvk { Com&& pDevice9, DDraw4Surface* pSurface, DWORD CreationFlags9) - : DDrawWrappedObject(pParent, std::move(d3d6DeviceProxy), std::move(pDevice9)) + : DDrawWrappedObject(pParent, std::move(d3d6DeviceProxy)) , m_commonD3DDevice ( commonD3DDevice ) , m_multithread ( CreationFlags9 & D3DCREATE_MULTITHREADED ) - , m_params9 ( Params9 ) , m_desc ( Desc ) - , m_deviceGUID ( deviceGUID ) , m_rt ( pSurface ) { if (m_parent != nullptr) { m_commonIntf = m_parent->GetCommonInterface(); @@ -41,36 +39,50 @@ namespace dxvk { throw DxvkError("D3D6Device: ERROR! Failed to retrieve the common interface!"); } - // Get the bridge interface to D3D9 - if (unlikely(FAILED(m_d3d9->QueryInterface(__uuidof(IDxvkD3D8Bridge), reinterpret_cast(&m_bridge))))) { - throw DxvkError("D3D6Device: ERROR! Failed to get D3D9 Bridge. d3d9.dll might not be DXVK!"); - } - - // Common D3D9 index buffers - if (unlikely(FAILED(InitializeIndexBuffers()))) { - throw DxvkError("D3D6Device: ERROR! Failed to initialize D3D9 index buffers."); - } + d3d9::IDirect3DDevice9* device9; if (likely(m_commonD3DDevice == nullptr)) { - m_commonD3DDevice = new D3DCommonDevice(m_commonIntf, CreationFlags9, - m_bridge->DetermineInitialTextureMemory()); + m_commonD3DDevice = new D3DCommonDevice(m_commonIntf, deviceGUID, Params9, CreationFlags9); + + m_commonD3DDevice->SetD3D9Device(std::move(pDevice9)); + device9 = m_commonD3DDevice->GetD3D9Device(); const D3DOptions* d3dOptions = m_commonIntf->GetOptions(); if (unlikely(d3dOptions->emulateFSAA == FSAAEmulation::Forced)) { Logger::warn("D3D6Device: Force enabling AA"); - m_d3d9->SetRenderState(d3d9::D3DRS_MULTISAMPLEANTIALIAS, TRUE); + device9->SetRenderState(d3d9::D3DRS_MULTISAMPLEANTIALIAS, TRUE); } // The default value of D3DRENDERSTATE_TEXTUREMAPBLEND in D3D6 is D3DTBLEND_MODULATE - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG1, D3DTA_TEXTURE); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG1, D3DTA_TEXTURE); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_COLOROP, D3DTOP_MODULATE); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAOP, D3DTOP_SELECTARG1); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG2, D3DTA_DIFFUSE); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG2, D3DTA_DIFFUSE); + device9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG1, D3DTA_TEXTURE); + device9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG1, D3DTA_TEXTURE); + device9->SetTextureStageState(0, d3d9::D3DTSS_COLOROP, D3DTOP_MODULATE); + device9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAOP, D3DTOP_SELECTARG1); + device9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG2, D3DTA_DIFFUSE); + device9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG2, D3DTA_DIFFUSE); + } else { + device9 = m_commonD3DDevice->GetD3D9Device(); + // Very important, otherwise the depth stencil isn't dirtied on draws + m_ds = m_rt->GetAttachedDepthStencil(); + } + + // Common D3D9 index buffers + if (unlikely(FAILED(InitializeIndexBuffers()))) { + throw DxvkError("D3D6Device: ERROR! Failed to initialize D3D9 index buffers."); + } + + // Get the bridge interface to D3D9 + if (unlikely(FAILED(device9->QueryInterface(__uuidof(IDxvkD3D8Bridge), reinterpret_cast(&m_bridge))))) { + throw DxvkError("D3D6Device: ERROR! Failed to get D3D9 Bridge. d3d9.dll might not be DXVK!"); } + if (unlikely(!m_commonD3DDevice->GetTotalTextureMemory())) + m_commonD3DDevice->SetTotalTextureMemory(m_bridge->DetermineInitialTextureMemory()); + + // Update D3D9 legacy light state + m_bridge->SetLegacyLightsState(true); + if (m_commonD3DDevice->GetOrigin() == nullptr) m_commonD3DDevice->SetOrigin(this); @@ -85,14 +97,16 @@ namespace dxvk { D3D6Device::~D3D6Device() { if (LogIndexBufferUsageStats()) { - Logger::info("D3D6Device: Index buffer upload statistics:"); - Logger::info(str::format(" XXS: ", m_ib9_uploads[0])); - Logger::info(str::format(" XS : ", m_ib9_uploads[1])); - Logger::info(str::format(" S : ", m_ib9_uploads[2])); - Logger::info(str::format(" M : ", m_ib9_uploads[3])); - Logger::info(str::format(" L : ", m_ib9_uploads[4])); - Logger::info(str::format(" XL : ", m_ib9_uploads[5])); - Logger::info(str::format(" XXL: ", m_ib9_uploads[6])); + Logger::debug("D3D6Device: Index buffer upload statistics:"); + Logger::debug(str::format(" 0.5 kb : ", m_ib9_uploads[0])); + Logger::debug(str::format(" 1 kb : ", m_ib9_uploads[1])); + Logger::debug(str::format(" 2 kb : ", m_ib9_uploads[2])); + Logger::debug(str::format(" 4 kb : ", m_ib9_uploads[3])); + Logger::debug(str::format(" 8 kb : ", m_ib9_uploads[4])); + Logger::debug(str::format(" 16 kb : ", m_ib9_uploads[5])); + Logger::debug(str::format(" 32 kb : ", m_ib9_uploads[6])); + Logger::debug(str::format(" 64 kb : ", m_ib9_uploads[7])); + Logger::debug(str::format(" 128 kb : ", m_ib9_uploads[8])); } // Dissasociate every bound viewport from this device @@ -157,25 +171,49 @@ namespace dxvk { // Reuse the existing D3D9 device in situations where games want // to get access only to D3D3 execute buffers on a D3D6 device - Com device9 = m_d3d9.ptr(); m_device3 = new D3D3Device(m_commonD3DDevice.ptr(), std::move(ppvProxyObject), - m_rt->GetCommonSurface()->GetDDSurface(), GetD3D3Caps(d3dOptions), m_deviceGUID, - m_params9, std::move(device9), m_commonD3DDevice->GetD3D9CreationFlags()); + m_rt->GetCommonSurface()->GetDDSurface(), GetD3D3Caps(d3dOptions), + m_commonD3DDevice->GetDeviceGUID(), m_commonD3DDevice->GetPresentParameters(), + nullptr, m_commonD3DDevice->GetD3D9CreationFlags()); + m_commonD3DDevice->SetD3D3Device(m_device3.ptr()); // On native this is the same object, so no need to ref *ppvObject = m_device3.ptr(); return S_OK; } - // Technically possible, shouldn't ever be needed or make sense + // Some games, such as Hype: The Time Quest, apparently query this + // as well, albeit without actually using the returned object... if (unlikely(riid == __uuidof(IDirect3DDevice2))) { if (m_commonD3DDevice->GetD3D5Device() != nullptr) { Logger::debug("D3D6Device::QueryInterface: Query for existing IDirect3DDevice2"); return m_commonD3DDevice->GetD3D5Device()->QueryInterface(riid, ppvObject); } - Logger::err("D3D6Device::QueryInterface: Query for IDirect3DDevice2"); - return E_NOINTERFACE; + Logger::debug("D3D6Device::QueryInterface: Query for IDirect3DDevice2"); + + Com ppvProxyObject; + HRESULT hr = m_proxy->QueryInterface(riid, reinterpret_cast(&ppvProxyObject)); + if (unlikely(FAILED(hr))) + return hr; + + const D3DOptions* d3dOptions = m_commonIntf->GetOptions(); + + // TODO: Make sure the RT has an existing DDrawSurface, + // and QueryInterface for one if that's not the case + + m_device5 = new D3D5Device(m_commonD3DDevice.ptr(), std::move(ppvProxyObject), + m_commonD3DDevice->GetCommonD3DInterface()->GetD3D5Interface(), + GetD3D5Caps(m_commonD3DDevice->GetDeviceGUID(), d3dOptions), + m_commonD3DDevice->GetDeviceGUID(), m_commonD3DDevice->GetPresentParameters(), + nullptr, m_rt->GetCommonSurface()->GetDDSurface(), + m_commonD3DDevice->GetD3D9CreationFlags()); + m_commonD3DDevice->SetD3D5Device(m_device5.ptr()); + + // On native this is the same object, so no need to ref + *ppvObject = m_device5.ptr(); + + return S_OK; } try { @@ -201,7 +239,9 @@ namespace dxvk { D3DDEVICEDESC desc_HAL = m_desc; D3DDEVICEDESC desc_HEL = m_desc; - if (m_deviceGUID == IID_IDirect3DRGBDevice) { + const GUID deviceGUID = m_commonD3DDevice->GetDeviceGUID(); + + if (deviceGUID == IID_IDirect3DRGBDevice) { desc_HAL.dwFlags = 0; desc_HAL.dcmColorModel = 0; // Some applications apparently care about RGB texture caps @@ -211,7 +251,7 @@ namespace dxvk { & ~D3DPTEXTURECAPS_NONPOW2CONDITIONAL; desc_HEL.dpcLineCaps.dwTextureCaps |= D3DPTEXTURECAPS_POW2; desc_HEL.dpcTriCaps.dwTextureCaps |= D3DPTEXTURECAPS_POW2; - } else if (m_deviceGUID == IID_IDirect3DHALDevice) { + } else if (deviceGUID == IID_IDirect3DHALDevice) { desc_HEL.dcmColorModel = 0; desc_HEL.dwDevCaps &= ~D3DDEVCAPS_HWTRANSFORMANDLIGHT & ~D3DDEVCAPS_DRAWPRIMITIVES2 @@ -406,13 +446,13 @@ namespace dxvk { RefreshLastUsedDevice(); - if (unlikely(m_inScene)) + if (unlikely(m_commonD3DDevice->IsInScene())) return D3DERR_SCENE_IN_SCENE; - HRESULT hr = m_d3d9->BeginScene(); + HRESULT hr = m_commonD3DDevice->GetD3D9Device()->BeginScene(); if (likely(SUCCEEDED(hr))) - m_inScene = true; + m_commonD3DDevice->SetInScene(true); return hr; } @@ -424,29 +464,13 @@ namespace dxvk { RefreshLastUsedDevice(); - if (unlikely(!m_inScene)) + if (unlikely(!m_commonD3DDevice->IsInScene())) return D3DERR_SCENE_NOT_IN_SCENE; - HRESULT hr = m_d3d9->EndScene(); - - if (likely(SUCCEEDED(hr))) { - const D3DOptions* d3dOptions = m_commonIntf->GetOptions(); - - if (d3dOptions->forceProxiedPresent) { - // If we have drawn anything, we need to make sure we blit back - // the results onto the D3D6 render target before we flip it - if (m_commonIntf->HasDrawn()) - BlitToDDrawSurface(m_rt->GetProxied(), m_rt->GetD3D9()); + HRESULT hr = m_commonD3DDevice->GetD3D9Device()->EndScene(); - m_rt->GetProxied()->Flip(static_cast(m_commonIntf->GetFlipRTSurface()), - m_commonIntf->GetFlipRTFlags()); - - if (likely(d3dOptions->backBufferGuard != D3DBackBufferGuard::Strict)) - m_commonIntf->ResetDrawTracking(); - } - - m_inScene = false; - } + if (likely(SUCCEEDED(hr))) + m_commonD3DDevice->SetInScene(false); return hr; } @@ -480,8 +504,10 @@ namespace dxvk { if (unlikely(m_currentViewport == d3d6Viewport)) return D3D_OK; - if (likely(m_currentViewport != nullptr)) + if (likely(m_currentViewport != nullptr)) { + m_currentViewport->DeactivateLights(); m_currentViewport->GetCommonViewport()->SetIsCurrentViewport(false); + } m_currentViewport = d3d6Viewport.ptr(); @@ -529,20 +555,10 @@ namespace dxvk { DDraw4Surface* rt6 = static_cast(surface); - const D3DOptions* d3dOptions = m_commonIntf->GetOptions(); - - if (unlikely(d3dOptions->forceProxiedPresent)) { - HRESULT hrRT = m_proxy->SetRenderTarget(rt6->GetProxied(), flags); - if (unlikely(FAILED(hrRT))) { - Logger::warn("D3D6Device::SetRenderTarget: Failed to set RT"); - return hrRT; - } - } else { - // Needed to ensure proxied Z/Stencil viewport clears will work - HRESULT hrRT = m_proxy->SetRenderTarget(rt6->GetProxied(), flags); - if (unlikely(FAILED(hrRT))) - Logger::debug("D3D6Device::SetRenderTarget: Failed to set RT"); - } + // Needed to ensure proxied Z/Stencil viewport clears will work + HRESULT hrRT = m_proxy->SetRenderTarget(rt6->GetShadowOrProxied(), flags); + if (unlikely(FAILED(hrRT))) + Logger::debug("D3D6Device::SetRenderTarget: Failed to set RT"); HRESULT hr = rt6->GetCommonSurface()->ValidateRTUsage(); if (unlikely(FAILED(hr))) @@ -554,7 +570,9 @@ namespace dxvk { return hr; } - hr = m_d3d9->SetRenderTarget(0, rt6->GetD3D9()); + d3d9::IDirect3DDevice9* device9 = m_commonD3DDevice->GetD3D9Device(); + + hr = device9->SetRenderTarget(0, rt6->GetCommonSurface()->GetD3D9Surface()); if (likely(SUCCEEDED(hr))) { Logger::debug("D3D6Device::SetRenderTarget: Set a new D3D9 RT"); @@ -573,7 +591,7 @@ namespace dxvk { return hrDS; } - hrDS = m_d3d9->SetDepthStencilSurface(m_ds->GetD3D9()); + hrDS = device9->SetDepthStencilSurface(m_ds->GetCommonSurface()->GetD3D9Surface()); if (unlikely(FAILED(hrDS))) { Logger::err("D3D6Device::SetRenderTarget: Failed to set D3D9 DS"); return hrDS; @@ -583,7 +601,7 @@ namespace dxvk { } else { Logger::debug("D3D6Device::SetRenderTarget: RT has no depth stencil attached"); - hrDS = m_d3d9->SetDepthStencilSurface(nullptr); + hrDS = device9->SetDepthStencilSurface(nullptr); if (unlikely(FAILED(hrDS))) { Logger::err("D3D6Device::SetRenderTarget: Failed to clear the D3D9 DS"); return hrDS; @@ -706,12 +724,14 @@ namespace dxvk { // As opposed to D3D7, D3D6 does not error out on // unknown or invalid render states. - if (unlikely(!IsValidD3D6RenderStateType(dwRenderStateType))) { + if (unlikely(!IsValidD3D6RenderStateType(dwRenderStateType) + && !m_commonIntf->GetOptions()->apitraceMode)) { Logger::debug(str::format("D3D6Device::GetRenderState: Invalid render state ", dwRenderStateType)); *lpdwRenderState = 0; return D3D_OK; } + d3d9::IDirect3DDevice9* device9 = m_commonD3DDevice->GetD3D9Device(); d3d9::D3DRENDERSTATETYPE State9 = d3d9::D3DRENDERSTATETYPE(dwRenderStateType); switch (dwRenderStateType) { @@ -725,11 +745,11 @@ namespace dxvk { return D3D_OK; case D3DRENDERSTATE_ANTIALIAS: - *lpdwRenderState = m_antialias; + *lpdwRenderState = m_commonD3DDevice->GetAntialias(); return D3D_OK; case D3DRENDERSTATE_TEXTUREADDRESS: - m_d3d9->GetSamplerState(0, d3d9::D3DSAMP_ADDRESSU, lpdwRenderState); + device9->GetSamplerState(0, d3d9::D3DSAMP_ADDRESSU, lpdwRenderState); return D3D_OK; // Always enabled on later APIs, default TRUE in D3D6 @@ -740,7 +760,7 @@ namespace dxvk { // Not implemented in DXVK, but retrieve it as it were case D3DRENDERSTATE_WRAPU: { DWORD value9 = 0; - m_d3d9->GetRenderState(d3d9::D3DRS_WRAP0, &value9); + device9->GetRenderState(d3d9::D3DRS_WRAP0, &value9); *lpdwRenderState = value9 & D3DWRAP_U; return D3D_OK; } @@ -748,13 +768,13 @@ namespace dxvk { // Not implemented in DXVK, but retrieve it as it were case D3DRENDERSTATE_WRAPV: { DWORD value9 = 0; - m_d3d9->GetRenderState(d3d9::D3DRS_WRAP0, &value9); + device9->GetRenderState(d3d9::D3DRS_WRAP0, &value9); *lpdwRenderState = value9 & D3DWRAP_V; return D3D_OK; } case D3DRENDERSTATE_LINEPATTERN: - *lpdwRenderState = bit::cast(m_linePattern); + *lpdwRenderState = bit::cast(m_commonD3DDevice->GetLinePattern()); return D3D_OK; case D3DRENDERSTATE_MONOENABLE: @@ -770,20 +790,20 @@ namespace dxvk { return D3D_OK; case D3DRENDERSTATE_TEXTUREMAG: - m_d3d9->GetSamplerState(0, d3d9::D3DSAMP_MAGFILTER, lpdwRenderState); + device9->GetSamplerState(0, d3d9::D3DSAMP_MAGFILTER, lpdwRenderState); return D3D_OK; case D3DRENDERSTATE_TEXTUREMIN: { DWORD minFilter = 0; DWORD mipFilter = 0; - m_d3d9->GetSamplerState(0, d3d9::D3DSAMP_MINFILTER, &minFilter); - m_d3d9->GetSamplerState(0, d3d9::D3DSAMP_MIPFILTER, &mipFilter); + device9->GetSamplerState(0, d3d9::D3DSAMP_MINFILTER, &minFilter); + device9->GetSamplerState(0, d3d9::D3DSAMP_MIPFILTER, &mipFilter); *lpdwRenderState = DecodeTextureMinValues(minFilter, mipFilter); return D3D_OK; } case D3DRENDERSTATE_TEXTUREMAPBLEND: - *lpdwRenderState = m_textureMapBlend; + *lpdwRenderState = m_commonD3DDevice->GetTextureMapBlend(); return D3D_OK; // Not supported by D3D6 @@ -809,34 +829,34 @@ namespace dxvk { break; case D3DRENDERSTATE_COLORKEYENABLE: - *lpdwRenderState = m_colorKeyEnabled; + *lpdwRenderState = m_commonD3DDevice->GetColorKeyEnable(); return D3D_OK; case D3DRENDERSTATE_BORDERCOLOR: - m_d3d9->GetSamplerState(0, d3d9::D3DSAMP_BORDERCOLOR, lpdwRenderState); + device9->GetSamplerState(0, d3d9::D3DSAMP_BORDERCOLOR, lpdwRenderState); return D3D_OK; case D3DRENDERSTATE_TEXTUREADDRESSU: - m_d3d9->GetSamplerState(0, d3d9::D3DSAMP_ADDRESSU, lpdwRenderState); + device9->GetSamplerState(0, d3d9::D3DSAMP_ADDRESSU, lpdwRenderState); return D3D_OK; case D3DRENDERSTATE_TEXTUREADDRESSV: - m_d3d9->GetSamplerState(0, d3d9::D3DSAMP_ADDRESSV, lpdwRenderState); + device9->GetSamplerState(0, d3d9::D3DSAMP_ADDRESSV, lpdwRenderState); return D3D_OK; case D3DRENDERSTATE_MIPMAPLODBIAS: - m_d3d9->GetSamplerState(0, d3d9::D3DSAMP_MIPMAPLODBIAS, lpdwRenderState); + device9->GetSamplerState(0, d3d9::D3DSAMP_MIPMAPLODBIAS, lpdwRenderState); return D3D_OK; case D3DRENDERSTATE_ZBIAS: { DWORD bias = 0; - m_d3d9->GetRenderState(d3d9::D3DRS_DEPTHBIAS, &bias); + device9->GetRenderState(d3d9::D3DRS_DEPTHBIAS, &bias); *lpdwRenderState = static_cast(bit::cast(bias) * ddrawCaps::ZBIAS_SCALE_INV); return D3D_OK; } case D3DRENDERSTATE_ANISOTROPY: - m_d3d9->GetSamplerState(0, d3d9::D3DSAMP_MAXANISOTROPY, lpdwRenderState); + device9->GetSamplerState(0, d3d9::D3DSAMP_MAXANISOTROPY, lpdwRenderState); return D3D_OK; // "Batched primitives are implicitly flushed when rendering with the @@ -886,7 +906,7 @@ namespace dxvk { } // This call will never fail - return m_d3d9->GetRenderState(State9, lpdwRenderState); + return device9->GetRenderState(State9, lpdwRenderState); } HRESULT STDMETHODCALLTYPE D3D6Device::SetRenderState(D3DRENDERSTATETYPE dwRenderStateType, DWORD dwRenderState) { @@ -901,6 +921,7 @@ namespace dxvk { return D3D_OK; } + d3d9::IDirect3DDevice9* device9 = m_commonD3DDevice->GetD3D9Device(); d3d9::D3DRENDERSTATETYPE State9 = d3d9::D3DRENDERSTATETYPE(dwRenderStateType); switch (dwRenderStateType) { @@ -923,16 +944,16 @@ namespace dxvk { } State9 = d3d9::D3DRS_MULTISAMPLEANTIALIAS; - m_antialias = dwRenderState; - dwRenderState = m_antialias == D3DANTIALIAS_SORTDEPENDENT - || m_antialias == D3DANTIALIAS_SORTINDEPENDENT + m_commonD3DDevice->SetAntialias(dwRenderState); + dwRenderState = dwRenderState == D3DANTIALIAS_SORTDEPENDENT + || dwRenderState == D3DANTIALIAS_SORTINDEPENDENT || d3dOptions->emulateFSAA == FSAAEmulation::Forced ? TRUE : FALSE; break; } case D3DRENDERSTATE_TEXTUREADDRESS: - m_d3d9->SetSamplerState(0, d3d9::D3DSAMP_ADDRESSU, dwRenderState); - m_d3d9->SetSamplerState(0, d3d9::D3DSAMP_ADDRESSV, dwRenderState); + device9->SetSamplerState(0, d3d9::D3DSAMP_ADDRESSU, dwRenderState); + device9->SetSamplerState(0, d3d9::D3DSAMP_ADDRESSV, dwRenderState); return D3D_OK; // Always enabled on later APIs, default TRUE in D3D6 @@ -942,11 +963,11 @@ namespace dxvk { // Not implemented in DXVK, but forward it anyway case D3DRENDERSTATE_WRAPU: { DWORD value9 = 0; - m_d3d9->GetRenderState(d3d9::D3DRS_WRAP0, &value9); + device9->GetRenderState(d3d9::D3DRS_WRAP0, &value9); if (dwRenderState == TRUE) { - m_d3d9->SetRenderState(d3d9::D3DRS_WRAP0, value9 & D3DWRAP_U); + device9->SetRenderState(d3d9::D3DRS_WRAP0, value9 & D3DWRAP_U); } else { - m_d3d9->SetRenderState(d3d9::D3DRS_WRAP0, value9 & ~D3DWRAP_U); + device9->SetRenderState(d3d9::D3DRS_WRAP0, value9 & ~D3DWRAP_U); } return D3D_OK; } @@ -954,11 +975,11 @@ namespace dxvk { // Not implemented in DXVK, but forward it anyway case D3DRENDERSTATE_WRAPV: { DWORD value9 = 0; - m_d3d9->GetRenderState(d3d9::D3DRS_WRAP0, &value9); + device9->GetRenderState(d3d9::D3DRS_WRAP0, &value9); if (dwRenderState == TRUE) { - m_d3d9->SetRenderState(d3d9::D3DRS_WRAP0, value9 & D3DWRAP_V); + device9->SetRenderState(d3d9::D3DRS_WRAP0, value9 & D3DWRAP_V); } else { - m_d3d9->SetRenderState(d3d9::D3DRS_WRAP0, value9 & ~D3DWRAP_V); + device9->SetRenderState(d3d9::D3DRS_WRAP0, value9 & ~D3DWRAP_V); } return D3D_OK; } @@ -971,7 +992,7 @@ namespace dxvk { if (!std::exchange(s_linePatternErrorShown, true)) Logger::warn("D3D6Device::SetRenderState: Unimplemented render state D3DRS_LINEPATTERN"); - m_linePattern = bit::cast(dwRenderState); + m_commonD3DDevice->SetLinePattern(bit::cast(dwRenderState)); return D3D_OK; case D3DRENDERSTATE_MONOENABLE: @@ -1001,7 +1022,7 @@ namespace dxvk { switch (dwRenderState) { case D3DFILTER_NEAREST: case D3DFILTER_LINEAR: - m_d3d9->SetSamplerState(0, d3d9::D3DSAMP_MAGFILTER, dwRenderState); + device9->SetSamplerState(0, d3d9::D3DSAMP_MAGFILTER, dwRenderState); break; default: break; @@ -1013,29 +1034,29 @@ namespace dxvk { switch (dwRenderState) { case D3DFILTER_NEAREST: case D3DFILTER_LINEAR: - m_d3d9->SetSamplerState(0, d3d9::D3DSAMP_MINFILTER, dwRenderState); - m_d3d9->SetSamplerState(0, d3d9::D3DSAMP_MIPFILTER, d3d9::D3DTEXF_NONE); + device9->SetSamplerState(0, d3d9::D3DSAMP_MINFILTER, dwRenderState); + device9->SetSamplerState(0, d3d9::D3DSAMP_MIPFILTER, d3d9::D3DTEXF_NONE); break; // "The closest mipmap level is chosen and a point filter is applied." case D3DFILTER_MIPNEAREST: - m_d3d9->SetSamplerState(0, d3d9::D3DSAMP_MINFILTER, d3d9::D3DTEXF_POINT); - m_d3d9->SetSamplerState(0, d3d9::D3DSAMP_MIPFILTER, d3d9::D3DTEXF_POINT); + device9->SetSamplerState(0, d3d9::D3DSAMP_MINFILTER, d3d9::D3DTEXF_POINT); + device9->SetSamplerState(0, d3d9::D3DSAMP_MIPFILTER, d3d9::D3DTEXF_POINT); break; // "The closest mipmap level is chosen and a bilinear filter is applied within it." case D3DFILTER_MIPLINEAR: - m_d3d9->SetSamplerState(0, d3d9::D3DSAMP_MINFILTER, d3d9::D3DTEXF_LINEAR); - m_d3d9->SetSamplerState(0, d3d9::D3DSAMP_MIPFILTER, d3d9::D3DTEXF_POINT); + device9->SetSamplerState(0, d3d9::D3DSAMP_MINFILTER, d3d9::D3DTEXF_LINEAR); + device9->SetSamplerState(0, d3d9::D3DSAMP_MIPFILTER, d3d9::D3DTEXF_POINT); break; // "The two closest mipmap levels are chosen and then a linear // blend is used between point filtered samples of each level." case D3DFILTER_LINEARMIPNEAREST: - m_d3d9->SetSamplerState(0, d3d9::D3DSAMP_MINFILTER, d3d9::D3DTEXF_POINT); - m_d3d9->SetSamplerState(0, d3d9::D3DSAMP_MIPFILTER, d3d9::D3DTEXF_LINEAR); + device9->SetSamplerState(0, d3d9::D3DSAMP_MINFILTER, d3d9::D3DTEXF_POINT); + device9->SetSamplerState(0, d3d9::D3DSAMP_MIPFILTER, d3d9::D3DTEXF_LINEAR); break; // "The two closest mipmap levels are chosen and then combined using a bilinear filter." case D3DFILTER_LINEARMIPLINEAR: - m_d3d9->SetSamplerState(0, d3d9::D3DSAMP_MINFILTER, d3d9::D3DTEXF_LINEAR); - m_d3d9->SetSamplerState(0, d3d9::D3DSAMP_MIPFILTER, d3d9::D3DTEXF_LINEAR); + device9->SetSamplerState(0, d3d9::D3DSAMP_MINFILTER, d3d9::D3DTEXF_LINEAR); + device9->SetSamplerState(0, d3d9::D3DSAMP_MIPFILTER, d3d9::D3DTEXF_LINEAR); break; default: break; @@ -1044,19 +1065,19 @@ namespace dxvk { } case D3DRENDERSTATE_TEXTUREMAPBLEND: - m_textureMapBlend = dwRenderState; + m_commonD3DDevice->SetTextureMapBlend(dwRenderState); switch (dwRenderState) { // "In this mode, the RGB and alpha values of the texture replace // the colors that would have been used with no texturing." case D3DTBLEND_DECAL: case D3DTBLEND_COPY: - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG1, D3DTA_TEXTURE); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG1, D3DTA_TEXTURE); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_COLOROP, D3DTOP_SELECTARG1); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAOP, D3DTOP_SELECTARG1); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG2, D3DTA_CURRENT); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG2, D3DTA_CURRENT); + device9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG1, D3DTA_TEXTURE); + device9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG1, D3DTA_TEXTURE); + device9->SetTextureStageState(0, d3d9::D3DTSS_COLOROP, D3DTOP_SELECTARG1); + device9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAOP, D3DTOP_SELECTARG1); + device9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG2, D3DTA_CURRENT); + device9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG2, D3DTA_CURRENT); break; // "In this mode, the RGB values of the texture are multiplied with the RGB values // that would have been used with no texturing. Any alpha values in the texture @@ -1064,43 +1085,43 @@ namespace dxvk { // if the texture does not contain an alpha component, alpha values at the vertices // in the source are interpolated between vertices." case D3DTBLEND_MODULATE: - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG1, D3DTA_TEXTURE); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG1, D3DTA_TEXTURE); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_COLOROP, D3DTOP_MODULATE); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAOP, D3DTOP_SELECTARG1); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG2, D3DTA_DIFFUSE); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG2, D3DTA_DIFFUSE); + device9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG1, D3DTA_TEXTURE); + device9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG1, D3DTA_TEXTURE); + device9->SetTextureStageState(0, d3d9::D3DTSS_COLOROP, D3DTOP_MODULATE); + device9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAOP, D3DTOP_SELECTARG1); + device9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG2, D3DTA_DIFFUSE); + device9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG2, D3DTA_DIFFUSE); break; // "In this mode, the RGB and alpha values of the texture are blended with the colors // that would have been used with no texturing, according to the following formulas [...]" case D3DTBLEND_DECALALPHA: - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG1, D3DTA_TEXTURE); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG1, D3DTA_TEXTURE); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_COLOROP, D3DTOP_BLENDTEXTUREALPHA); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAOP, D3DTOP_SELECTARG2); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG2, D3DTA_DIFFUSE); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG2, D3DTA_DIFFUSE); + device9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG1, D3DTA_TEXTURE); + device9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG1, D3DTA_TEXTURE); + device9->SetTextureStageState(0, d3d9::D3DTSS_COLOROP, D3DTOP_BLENDTEXTUREALPHA); + device9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAOP, D3DTOP_SELECTARG2); + device9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG2, D3DTA_DIFFUSE); + device9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG2, D3DTA_DIFFUSE); break; // "In this mode, the RGB values of the texture are multiplied with the RGB values that // would have been used with no texturing, and the alpha values of the texture // are multiplied with the alpha values that would have been used with no texturing." case D3DTBLEND_MODULATEALPHA: - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG1, D3DTA_TEXTURE); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG1, D3DTA_TEXTURE); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_COLOROP, D3DTOP_MODULATE); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAOP, D3DTOP_MODULATE); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG2, D3DTA_DIFFUSE); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG2, D3DTA_DIFFUSE); + device9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG1, D3DTA_TEXTURE); + device9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG1, D3DTA_TEXTURE); + device9->SetTextureStageState(0, d3d9::D3DTSS_COLOROP, D3DTOP_MODULATE); + device9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAOP, D3DTOP_MODULATE); + device9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG2, D3DTA_DIFFUSE); + device9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG2, D3DTA_DIFFUSE); break; // "Add the Gouraud interpolants to the texture lookup with saturation semantics // (that is, if the color value overflows it is set to the maximum possible value)." case D3DTBLEND_ADD: - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG1, D3DTA_TEXTURE); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG1, D3DTA_TEXTURE); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_COLOROP, D3DTOP_ADD); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAOP, D3DTOP_SELECTARG2); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG2, D3DTA_DIFFUSE); - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG2, D3DTA_DIFFUSE); + device9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG1, D3DTA_TEXTURE); + device9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG1, D3DTA_TEXTURE); + device9->SetTextureStageState(0, d3d9::D3DTSS_COLOROP, D3DTOP_ADD); + device9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAOP, D3DTOP_SELECTARG2); + device9->SetTextureStageState(0, d3d9::D3DTSS_COLORARG2, D3DTA_DIFFUSE); + device9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG2, D3DTA_DIFFUSE); break; // Unsupported default: @@ -1145,11 +1166,11 @@ namespace dxvk { break; case D3DRENDERSTATE_COLORKEYENABLE: { - m_colorKeyEnabled = dwRenderState; + m_commonD3DDevice->SetColorKeyEnable(dwRenderState); const bool validColorKey = m_textures[0] != nullptr ? m_textures[0]->GetParent()->GetCommonSurface()->HasValidColorKey() : false; - m_bridge->SetColorKeyState(m_colorKeyEnabled && validColorKey); - if (m_colorKeyEnabled && validColorKey) { + m_bridge->SetColorKeyState(dwRenderState && validColorKey); + if (dwRenderState && validColorKey) { DDCOLORKEY normalizedColorKey = m_textures[0]->GetParent()->GetCommonSurface()->GetColorKeyNormalized(); m_bridge->SetColorKey(normalizedColorKey.dwColorSpaceLowValue, normalizedColorKey.dwColorSpaceHighValue); @@ -1159,19 +1180,19 @@ namespace dxvk { } case D3DRENDERSTATE_BORDERCOLOR: - m_d3d9->SetSamplerState(0, d3d9::D3DSAMP_BORDERCOLOR, dwRenderState); + device9->SetSamplerState(0, d3d9::D3DSAMP_BORDERCOLOR, dwRenderState); return D3D_OK; case D3DRENDERSTATE_TEXTUREADDRESSU: - m_d3d9->SetSamplerState(0, d3d9::D3DSAMP_ADDRESSU, dwRenderState); + device9->SetSamplerState(0, d3d9::D3DSAMP_ADDRESSU, dwRenderState); return D3D_OK; case D3DRENDERSTATE_TEXTUREADDRESSV: - m_d3d9->SetSamplerState(0, d3d9::D3DSAMP_ADDRESSV, dwRenderState); + device9->SetSamplerState(0, d3d9::D3DSAMP_ADDRESSV, dwRenderState); return D3D_OK; case D3DRENDERSTATE_MIPMAPLODBIAS: - m_d3d9->SetSamplerState(0, d3d9::D3DSAMP_MIPMAPLODBIAS, dwRenderState); + device9->SetSamplerState(0, d3d9::D3DSAMP_MIPMAPLODBIAS, dwRenderState); return D3D_OK; case D3DRENDERSTATE_ZBIAS: @@ -1180,7 +1201,7 @@ namespace dxvk { break; case D3DRENDERSTATE_ANISOTROPY: - m_d3d9->SetSamplerState(0, d3d9::D3DSAMP_MAXANISOTROPY, dwRenderState); + device9->SetSamplerState(0, d3d9::D3DSAMP_MAXANISOTROPY, dwRenderState); return D3D_OK; // "Batched primitives are implicitly flushed when rendering with the @@ -1236,7 +1257,7 @@ namespace dxvk { } // This call will never fail - return m_d3d9->SetRenderState(State9, dwRenderState); + return device9->SetRenderState(State9, dwRenderState); } HRESULT STDMETHODCALLTYPE D3D6Device::GetLightState(D3DLIGHTSTATETYPE dwLightStateType, LPDWORD lpdwLightState) { @@ -1244,30 +1265,32 @@ namespace dxvk { Logger::debug(">>> D3D6Device::GetLightState"); + d3d9::IDirect3DDevice9* device9 = m_commonD3DDevice->GetD3D9Device(); + switch (dwLightStateType) { case D3DLIGHTSTATE_MATERIAL: - *lpdwLightState = m_materialHandle; + *lpdwLightState = m_commonD3DDevice->GetCurrentMaterialHandle(); break; case D3DLIGHTSTATE_AMBIENT: - m_d3d9->GetRenderState(d3d9::D3DRS_AMBIENT, lpdwLightState); + device9->GetRenderState(d3d9::D3DRS_AMBIENT, lpdwLightState); break; case D3DLIGHTSTATE_COLORMODEL: *lpdwLightState = D3DCOLOR_RGB; break; case D3DLIGHTSTATE_FOGMODE: - m_d3d9->GetRenderState(d3d9::D3DRS_FOGVERTEXMODE, lpdwLightState); + device9->GetRenderState(d3d9::D3DRS_FOGVERTEXMODE, lpdwLightState); break; case D3DLIGHTSTATE_FOGSTART: - m_d3d9->GetRenderState(d3d9::D3DRS_FOGSTART, lpdwLightState); + device9->GetRenderState(d3d9::D3DRS_FOGSTART, lpdwLightState); break; case D3DLIGHTSTATE_FOGEND: - m_d3d9->GetRenderState(d3d9::D3DRS_FOGEND, lpdwLightState); + device9->GetRenderState(d3d9::D3DRS_FOGEND, lpdwLightState); break; case D3DLIGHTSTATE_FOGDENSITY: - m_d3d9->GetRenderState(d3d9::D3DRS_FOGDENSITY, lpdwLightState); + device9->GetRenderState(d3d9::D3DRS_FOGDENSITY, lpdwLightState); break; case D3DLIGHTSTATE_COLORVERTEX: - m_d3d9->GetRenderState(d3d9::D3DRS_COLORVERTEX, lpdwLightState); + device9->GetRenderState(d3d9::D3DRS_COLORVERTEX, lpdwLightState); break; default: return DDERR_INVALIDPARAMS; @@ -1281,10 +1304,12 @@ namespace dxvk { Logger::debug(">>> D3D6Device::SetLightState"); + d3d9::IDirect3DDevice9* device9 = m_commonD3DDevice->GetD3D9Device(); + switch (dwLightStateType) { case D3DLIGHTSTATE_MATERIAL: { if (unlikely(!dwLightState)) { - m_materialHandle = dwLightState; + m_commonD3DDevice->SetCurrentMaterialHandle(dwLightState); return D3D_OK; } @@ -1292,33 +1317,33 @@ namespace dxvk { if (unlikely(material9 == nullptr)) return DDERR_INVALIDPARAMS; - m_materialHandle = dwLightState; + m_commonD3DDevice->SetCurrentMaterialHandle(dwLightState); Logger::debug(str::format("D3D6Device::SetLightState: Applying material nr. ", dwLightState, " to D3D9")); - m_d3d9->SetMaterial(material9); + device9->SetMaterial(material9); break; } case D3DLIGHTSTATE_AMBIENT: - m_d3d9->SetRenderState(d3d9::D3DRS_AMBIENT, dwLightState); + device9->SetRenderState(d3d9::D3DRS_AMBIENT, dwLightState); break; case D3DLIGHTSTATE_COLORMODEL: if (unlikely(dwLightState != D3DCOLOR_RGB)) Logger::warn("D3D6Device::SetLightState: Unsupported D3DLIGHTSTATE_COLORMODEL"); break; case D3DLIGHTSTATE_FOGMODE: - m_d3d9->SetRenderState(d3d9::D3DRS_FOGVERTEXMODE, dwLightState); + device9->SetRenderState(d3d9::D3DRS_FOGVERTEXMODE, dwLightState); break; case D3DLIGHTSTATE_FOGSTART: - m_d3d9->SetRenderState(d3d9::D3DRS_FOGSTART, dwLightState); + device9->SetRenderState(d3d9::D3DRS_FOGSTART, dwLightState); break; case D3DLIGHTSTATE_FOGEND: - m_d3d9->SetRenderState(d3d9::D3DRS_FOGEND, dwLightState); + device9->SetRenderState(d3d9::D3DRS_FOGEND, dwLightState); break; case D3DLIGHTSTATE_FOGDENSITY: - m_d3d9->SetRenderState(d3d9::D3DRS_FOGDENSITY, dwLightState); + device9->SetRenderState(d3d9::D3DRS_FOGDENSITY, dwLightState); break; case D3DLIGHTSTATE_COLORVERTEX: - m_d3d9->SetRenderState(d3d9::D3DRS_COLORVERTEX, dwLightState); + device9->SetRenderState(d3d9::D3DRS_COLORVERTEX, dwLightState); break; default: return DDERR_INVALIDPARAMS; @@ -1329,17 +1354,17 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE D3D6Device::SetTransform(D3DTRANSFORMSTATETYPE state, D3DMATRIX *matrix) { Logger::debug(">>> D3D6Device::SetTransform"); - return m_d3d9->SetTransform(ConvertTransformState(state), matrix); + return m_commonD3DDevice->GetD3D9Device()->SetTransform(ConvertTransformState(state), matrix); } HRESULT STDMETHODCALLTYPE D3D6Device::GetTransform(D3DTRANSFORMSTATETYPE state, D3DMATRIX *matrix) { Logger::debug(">>> D3D6Device::GetTransform"); - return m_d3d9->GetTransform(ConvertTransformState(state), matrix); + return m_commonD3DDevice->GetD3D9Device()->GetTransform(ConvertTransformState(state), matrix); } HRESULT STDMETHODCALLTYPE D3D6Device::MultiplyTransform(D3DTRANSFORMSTATETYPE state, D3DMATRIX *matrix) { Logger::debug(">>> D3D6Device::MultiplyTransform"); - return m_d3d9->MultiplyTransform(ConvertTransformState(state), matrix); + return m_commonD3DDevice->GetD3D9Device()->MultiplyTransform(ConvertTransformState(state), matrix); } HRESULT STDMETHODCALLTYPE D3D6Device::DrawPrimitive(D3DPRIMITIVETYPE primitive_type, DWORD vertex_type, void *vertices, DWORD vertex_count, DWORD flags) { @@ -1355,11 +1380,17 @@ namespace dxvk { if (unlikely(vertices == nullptr)) return DDERR_INVALIDPARAMS; + d3d9::IDirect3DDevice9* device9 = m_commonD3DDevice->GetD3D9Device(); + + m_rt->InitializeOrUploadD3D9(); + if (likely(m_ds != nullptr)) + m_ds->InitializeOrUploadD3D9(); + HandlePreDrawFlags(flags, vertex_type); HandlePreDrawLegacyProjection(flags); - m_d3d9->SetFVF(vertex_type); - HRESULT hr = m_d3d9->DrawPrimitiveUP( + device9->SetFVF(vertex_type); + HRESULT hr = device9->DrawPrimitiveUP( d3d9::D3DPRIMITIVETYPE(primitive_type), GetPrimitiveCount(primitive_type, vertex_count), vertices, @@ -1373,7 +1404,7 @@ namespace dxvk { return hr; } - m_commonIntf->UpdateDrawTracking(); + UpdateSurfaceDirtyTracking(true, true, true); return hr; } @@ -1391,11 +1422,17 @@ namespace dxvk { if (unlikely(vertices == nullptr || indices == nullptr)) return DDERR_INVALIDPARAMS; + d3d9::IDirect3DDevice9* device9 = m_commonD3DDevice->GetD3D9Device(); + + m_rt->InitializeOrUploadD3D9(); + if (likely(m_ds != nullptr)) + m_ds->InitializeOrUploadD3D9(); + HandlePreDrawFlags(flags, fvf); HandlePreDrawLegacyProjection(flags); - m_d3d9->SetFVF(fvf); - HRESULT hr = m_d3d9->DrawIndexedPrimitiveUP( + device9->SetFVF(fvf); + HRESULT hr = device9->DrawIndexedPrimitiveUP( d3d9::D3DPRIMITIVETYPE(primitive_type), 0, vertex_count, @@ -1413,33 +1450,37 @@ namespace dxvk { return hr; } - m_commonIntf->UpdateDrawTracking(); + UpdateSurfaceDirtyTracking(true, true, true); return hr; } HRESULT STDMETHODCALLTYPE D3D6Device::SetClipStatus(D3DCLIPSTATUS *clip_status) { - D3DDeviceLock lock = LockDevice(); - Logger::debug(">>> D3D6Device::SetClipStatus"); if (unlikely(clip_status == nullptr)) return DDERR_INVALIDPARAMS; - m_clipStatus = *clip_status; - return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D6Device::GetClipStatus(D3DCLIPSTATUS *clip_status) { - D3DDeviceLock lock = LockDevice(); - Logger::debug(">>> D3D6Device::GetClipStatus"); if (unlikely(clip_status == nullptr)) return DDERR_INVALIDPARAMS; - *clip_status = m_clipStatus; + d3d9::D3DVIEWPORT9 viewport9; + if (SUCCEEDED(m_commonD3DDevice->GetD3D9Device()->GetViewport(&viewport9))) { + clip_status->dwFlags = D3DCLIPSTATUS_EXTENTS2; + clip_status->dwStatus = 0; + clip_status->minx = viewport9.X; + clip_status->maxx = viewport9.X + viewport9.Height; + clip_status->miny = viewport9.Y; + clip_status->maxy = viewport9.Y + viewport9.Width; + clip_status->minz = 0; + clip_status->maxz = 0; + } return D3D_OK; } @@ -1457,14 +1498,20 @@ namespace dxvk { if (unlikely(strided_data == nullptr)) return DDERR_INVALIDPARAMS; + d3d9::IDirect3DDevice9* device9 = m_commonD3DDevice->GetD3D9Device(); + + m_rt->InitializeOrUploadD3D9(); + if (likely(m_ds != nullptr)) + m_ds->InitializeOrUploadD3D9(); + // Transform strided vertex data to a standard vertex buffer stream PackedVertexBuffer pvb = TransformStridedtoUP(fvf, strided_data, vertex_count); HandlePreDrawFlags(flags, fvf); HandlePreDrawLegacyProjection(flags); - m_d3d9->SetFVF(fvf); - HRESULT hr = m_d3d9->DrawPrimitiveUP( + device9->SetFVF(fvf); + HRESULT hr = device9->DrawPrimitiveUP( d3d9::D3DPRIMITIVETYPE(primitive_type), GetPrimitiveCount(primitive_type, vertex_count), pvb.vertexData.data(), @@ -1478,7 +1525,7 @@ namespace dxvk { return hr; } - m_commonIntf->UpdateDrawTracking(); + UpdateSurfaceDirtyTracking(true, true, true); return hr; } @@ -1496,14 +1543,20 @@ namespace dxvk { if (unlikely(strided_data == nullptr || indices == nullptr)) return DDERR_INVALIDPARAMS; + d3d9::IDirect3DDevice9* device9 = m_commonD3DDevice->GetD3D9Device(); + + m_rt->InitializeOrUploadD3D9(); + if (likely(m_ds != nullptr)) + m_ds->InitializeOrUploadD3D9(); + // Transform strided vertex data to a standard vertex buffer stream PackedVertexBuffer pvb = TransformStridedtoUP(fvf, strided_data, vertex_count); HandlePreDrawFlags(flags, fvf); HandlePreDrawLegacyProjection(flags); - m_d3d9->SetFVF(fvf); - HRESULT hr = m_d3d9->DrawIndexedPrimitiveUP( + device9->SetFVF(fvf); + HRESULT hr = device9->DrawIndexedPrimitiveUP( d3d9::D3DPRIMITIVETYPE(primitive_type), 0, vertex_count, @@ -1521,7 +1574,7 @@ namespace dxvk { return hr; } - m_commonIntf->UpdateDrawTracking(); + UpdateSurfaceDirtyTracking(true, true, true); return hr; } @@ -1546,12 +1599,18 @@ namespace dxvk { return D3DERR_VERTEXBUFFERLOCKED; } + d3d9::IDirect3DDevice9* device9 = m_commonD3DDevice->GetD3D9Device(); + + m_rt->InitializeOrUploadD3D9(); + if (likely(m_ds != nullptr)) + m_ds->InitializeOrUploadD3D9(); + HandlePreDrawFlags(flags, vb6->GetFVF()); HandlePreDrawLegacyProjection(flags); - m_d3d9->SetFVF(vb6->GetFVF()); - m_d3d9->SetStreamSource(0, vb6->GetD3D9(), 0, vb6->GetStride()); - HRESULT hr = m_d3d9->DrawPrimitive( + device9->SetFVF(vb6->GetFVF()); + device9->SetStreamSource(0, vb6->GetD3D9VertexBuffer(), 0, vb6->GetStride()); + HRESULT hr = device9->DrawPrimitive( d3d9::D3DPRIMITIVETYPE(primitive_type), start_vertex, GetPrimitiveCount(primitive_type, vertex_count)); @@ -1564,7 +1623,7 @@ namespace dxvk { return hr; } - m_commonIntf->UpdateDrawTracking(); + UpdateSurfaceDirtyTracking(true, true, true); return hr; } @@ -1596,10 +1655,16 @@ namespace dxvk { ibIndex++; if (unlikely(ibIndex > ddrawCaps::IndexBufferCount - 1)) { Logger::err("D3D6Device::DrawIndexedPrimitiveVB: Exceeded size of largest index buffer"); - return DDERR_GENERIC; + return DDERR_UNSUPPORTED; } } + d3d9::IDirect3DDevice9* device9 = m_commonD3DDevice->GetD3D9Device(); + + m_rt->InitializeOrUploadD3D9(); + if (likely(m_ds != nullptr)) + m_ds->InitializeOrUploadD3D9(); + HandlePreDrawFlags(flags, vb6->GetFVF()); HandlePreDrawLegacyProjection(flags); @@ -1607,10 +1672,10 @@ namespace dxvk { UploadIndices(ib9, indices, index_count); m_ib9_uploads[ibIndex]++; - m_d3d9->SetIndices(ib9); - m_d3d9->SetFVF(vb6->GetFVF()); - m_d3d9->SetStreamSource(0, vb6->GetD3D9(), 0, vb6->GetStride()); - HRESULT hr = m_d3d9->DrawIndexedPrimitive( + device9->SetIndices(ib9); + device9->SetFVF(vb6->GetFVF()); + device9->SetStreamSource(0, vb6->GetD3D9VertexBuffer(), 0, vb6->GetStride()); + HRESULT hr = device9->DrawIndexedPrimitive( d3d9::D3DPRIMITIVETYPE(primitive_type), 0, 0, @@ -1626,7 +1691,7 @@ namespace dxvk { return hr; } - m_commonIntf->UpdateDrawTracking(); + UpdateSurfaceDirtyTracking(true, true, true); return hr; } @@ -1679,13 +1744,15 @@ namespace dxvk { return DDERR_INVALIDPARAMS; } + d3d9::IDirect3DDevice9* device9 = m_commonD3DDevice->GetD3D9Device(); + HRESULT hr; // Unbinding texture stages if (texture == nullptr) { Logger::debug("D3D6Device::SetTexture: Unbiding D3D9 texture"); - hr = m_d3d9->SetTexture(stage, nullptr); + hr = device9->SetTexture(stage, nullptr); if (likely(SUCCEEDED(hr))) { if (m_textures[stage] != nullptr) { @@ -1702,38 +1769,30 @@ namespace dxvk { return hr; } - Logger::debug("D3D6Device::SetTexture: Binding D3D9 texture"); - D3D6Texture* texture6 = static_cast(texture); DDraw4Surface* surface6 = texture6->GetParent(); - // Only upload textures if any sort of blit/lock operation - // has been performed on them since the last SetTexture call, - // or textures which have been used on a different device, and - // need their D3D9 object to be reinitialized at this point - if (surface6->GetCommonSurface()->HasDirtyMipMaps() || - unlikely(surface6->GetD3D9Device() != m_d3d9.ptr())) { - hr = surface6->InitializeOrUploadD3D9(); - if (unlikely(FAILED(hr))) { - Logger::err("D3D6Device::SetTexture: Failed to initialize/upload D3D9 texture"); - return hr; - } + Logger::debug("D3D6Device::SetTexture: Binding D3D9 texture"); - surface6->GetCommonSurface()->UnDirtyMipMaps(); - } else { - Logger::debug("D3D6Device::SetTexture: Skipping upload of texture and mip maps"); + // If textures have been used on a different device, they + // will get their D3D9 object reinitialized at this point + if (unlikely(surface6->GetCommonSurface()->GetCommonD3DDevice() != m_commonD3DDevice.ptr())) + surface6->GetCommonSurface()->DirtyDDrawSurface(); + + hr = surface6->InitializeOrUploadD3D9(); + if (unlikely(FAILED(hr))) { + Logger::err("D3D6Device::SetTexture: Failed to initialize/upload D3D9 texture"); + return hr; } - // Only fast skip on D3D9 side, since we want to ensure - // color keying is applied properly even in the case - // of the same texture being set again (color key may change) + // Don't fast skip, since color key might change //if (unlikely(m_textures[stage] == texture6)) //return D3D_OK; - d3d9::IDirect3DTexture9* tex9 = surface6->GetD3D9Texture(); + d3d9::IDirect3DTexture9* tex9 = surface6->GetCommonSurface()->GetD3D9Texture(); if (likely(tex9 != nullptr)) { - hr = m_d3d9->SetTexture(stage, tex9); + hr = device9->SetTexture(stage, tex9); if (unlikely(FAILED(hr))) { Logger::warn("D3D6Device::SetTexture: Failed to bind D3D9 texture"); return hr; @@ -1743,14 +1802,15 @@ namespace dxvk { // "Any alpha values in the texture replace the alpha values in the colors that would // have been used with no texturing; if the texture does not contain an alpha component, // alpha values at the vertices in the source are interpolated between vertices." - if (m_textureMapBlend == D3DTBLEND_MODULATE && !m_alphaOpSet) { + if (m_commonD3DDevice->GetTextureMapBlend() == D3DTBLEND_MODULATE && !m_alphaOpSet) { const DWORD textureOp = surface6->GetCommonSurface()->IsAlphaFormat() ? D3DTOP_SELECTARG1 : D3DTOP_MODULATE; - m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAOP, textureOp); + device9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAOP, textureOp); } + const bool colorKeyEnable = m_commonD3DDevice->GetColorKeyEnable(); const bool validColorKey = surface6->GetCommonSurface()->HasValidColorKey(); - m_bridge->SetColorKeyState(m_colorKeyEnabled && validColorKey); - if (m_colorKeyEnabled && validColorKey) { + m_bridge->SetColorKeyState(colorKeyEnable && validColorKey); + if (colorKeyEnable && validColorKey) { DDCOLORKEY normalizedColorKey = surface6->GetCommonSurface()->GetColorKeyNormalized(); m_bridge->SetColorKey(normalizedColorKey.dwColorSpaceLowValue, normalizedColorKey.dwColorSpaceHighValue); @@ -1769,10 +1829,12 @@ namespace dxvk { if (unlikely(lpdwState == nullptr)) return DDERR_INVALIDPARAMS; - // In the case of D3DTSS_ADDRESS, which is D3D6 specific, - // simply return based on D3DTSS_ADDRESSU + d3d9::IDirect3DDevice9* device9 = m_commonD3DDevice->GetD3D9Device(); + + // In the case of D3DTSS_ADDRESS, which is exclusive to D3D7 + // and D3D6, simply return based on D3DTSS_ADDRESSU if (d3dTexStageStateType == D3DTSS_ADDRESS) { - return m_d3d9->GetSamplerState(dwStage, d3d9::D3DSAMP_ADDRESSU, lpdwState); + return device9->GetSamplerState(dwStage, d3d9::D3DSAMP_ADDRESSU, lpdwState); } d3d9::D3DSAMPLERSTATETYPE stateType = ConvertSamplerStateType(d3dTexStageStateType); @@ -1783,25 +1845,27 @@ namespace dxvk { if (stateType == d3d9::D3DSAMP_MAGFILTER || stateType == d3d9::D3DSAMP_MINFILTER || stateType == d3d9::D3DSAMP_MIPFILTER) { DWORD dwStateProxy9 = 0; - HRESULT hr = m_d3d9->GetSamplerState(dwStage, stateType, &dwStateProxy9); + HRESULT hr = device9->GetSamplerState(dwStage, stateType, &dwStateProxy9); *lpdwState = DecodeD3D9TexFilterValues(d3dTexStageStateType, dwStateProxy9); return hr; } else { - return m_d3d9->GetSamplerState(dwStage, stateType, lpdwState); + return device9->GetSamplerState(dwStage, stateType, lpdwState); } } else { - return m_d3d9->GetTextureStageState(dwStage, d3d9::D3DTEXTURESTAGESTATETYPE(d3dTexStageStateType), lpdwState); + return device9->GetTextureStageState(dwStage, d3d9::D3DTEXTURESTAGESTATETYPE(d3dTexStageStateType), lpdwState); } } HRESULT STDMETHODCALLTYPE D3D6Device::SetTextureStageState(DWORD dwStage, D3DTEXTURESTAGESTATETYPE d3dTexStageStateType, DWORD dwState) { Logger::debug(">>> D3D6Device::SetTextureStageState"); - // In the case of D3DTSS_ADDRESS, which is D3D6 specific, - // we need to set up both D3DTSS_ADDRESSU and D3DTSS_ADDRESSV + d3d9::IDirect3DDevice9* device9 = m_commonD3DDevice->GetD3D9Device(); + + // In the case of D3DTSS_ADDRESS, which is exclusive to D3D7 + // and D3D6, we need to set up both D3DTSS_ADDRESSU and D3DTSS_ADDRESSV if (d3dTexStageStateType == D3DTSS_ADDRESS) { - m_d3d9->SetSamplerState(dwStage, d3d9::D3DSAMP_ADDRESSU, dwState); - return m_d3d9->SetSamplerState(dwStage, d3d9::D3DSAMP_ADDRESSV, dwState); + device9->SetSamplerState(dwStage, d3d9::D3DSAMP_ADDRESSU, dwState); + return device9->SetSamplerState(dwStage, d3d9::D3DSAMP_ADDRESSV, dwState); } if (!m_alphaOpSet && d3dTexStageStateType == D3DTSS_ALPHAOP) @@ -1815,19 +1879,19 @@ namespace dxvk { if (stateType == d3d9::D3DSAMP_MAGFILTER || stateType == d3d9::D3DSAMP_MINFILTER || stateType == d3d9::D3DSAMP_MIPFILTER) { const DWORD dwState9 = DecodeD3D7TexFilterValues(d3dTexStageStateType, dwState); - return m_d3d9->SetSamplerState(dwStage, stateType, dwState9); + return device9->SetSamplerState(dwStage, stateType, dwState9); } else { - return m_d3d9->SetSamplerState(dwStage, stateType, dwState); + return device9->SetSamplerState(dwStage, stateType, dwState); } } else { - return m_d3d9->SetTextureStageState(dwStage, d3d9::D3DTEXTURESTAGESTATETYPE(d3dTexStageStateType), dwState); + return device9->SetTextureStageState(dwStage, d3d9::D3DTEXTURESTAGESTATETYPE(d3dTexStageStateType), dwState); } } HRESULT STDMETHODCALLTYPE D3D6Device::ValidateDevice(LPDWORD lpdwPasses) { Logger::debug(">>> D3D6Device::ValidateDevice"); - HRESULT hr = m_d3d9->ValidateDevice(lpdwPasses); + HRESULT hr = m_commonD3DDevice->GetD3D9Device()->ValidateDevice(lpdwPasses); if (unlikely(FAILED(hr))) return DDERR_INVALIDPARAMS; @@ -1835,8 +1899,9 @@ namespace dxvk { } void D3D6Device::InitializeDS() { - if (!m_rt->IsInitialized()) - m_rt->InitializeD3D9RenderTarget(); + d3d9::IDirect3DDevice9* device9 = m_commonD3DDevice->GetD3D9Device(); + + m_rt->InitializeD3D9RenderTarget(); m_ds = m_rt->GetAttachedDepthStencil(); @@ -1854,22 +1919,38 @@ namespace dxvk { m_ds->GetProxied()->GetSurfaceDesc(&descDS); Logger::debug(str::format("D3D6Device::InitializeDS: DepthStencil: ", descDS.dwWidth, "x", descDS.dwHeight)); - HRESULT hrDS9 = m_d3d9->SetDepthStencilSurface(m_ds->GetD3D9()); + HRESULT hrDS9 = device9->SetDepthStencilSurface(m_ds->GetCommonSurface()->GetD3D9Surface()); if(unlikely(FAILED(hrDS9))) { Logger::err("D3D6Device::InitializeDS: Failed to set D3D9 depth stencil"); } else { // This needs to act like an auto depth stencil of sorts, so manually enable z-buffering - m_d3d9->SetRenderState(d3d9::D3DRS_ZENABLE, d3d9::D3DZB_TRUE); + device9->SetRenderState(d3d9::D3DRS_ZENABLE, d3d9::D3DZB_TRUE); } } } else { Logger::info("D3D6Device::InitializeDS: RT has no depth stencil attached"); - m_d3d9->SetDepthStencilSurface(nullptr); + device9->SetDepthStencilSurface(nullptr); // Should be superfluous, but play it safe - m_d3d9->SetRenderState(d3d9::D3DRS_ZENABLE, d3d9::D3DZB_FALSE); + device9->SetRenderState(d3d9::D3DRS_ZENABLE, d3d9::D3DZB_FALSE); } } + void D3D6Device::UpdateSurfaceDirtyTracking(bool dirtyRenderTarget, bool dirtyDepthStencil, bool dirtyPrimarySurface) { + if(likely(dirtyRenderTarget)) + m_rt->GetCommonSurface()->DirtyD3D9Surface(); + + if (likely(dirtyPrimarySurface)) { + DDrawCommonSurface* primarySurface = m_commonIntf->GetPrimarySurface(); + // The primary surface can be bound as RT, in which case it will + // get dirtied twice, but we have no guarantees that will happen + if (likely(primarySurface != nullptr)) + primarySurface->DirtyD3D9Surface(); + } + + if (likely(dirtyDepthStencil && m_ds != nullptr)) + m_ds->GetCommonSurface()->DirtyD3D9Surface(); + } + HRESULT D3D6Device::ResetD3D9Swapchain(d3d9::D3DPRESENT_PARAMETERS* params) { Logger::info("D3D6Device::ResetD3D9Swapchain: Resetting the D3D9 swapchain"); @@ -1878,7 +1959,7 @@ namespace dxvk { Logger::err("D3D6Device::ResetD3D9Swapchain: Failed to reset the D3D9 swapchain"); } else { // TODO: Cache and reset all surfaces tied to the D3D9 backbuffers - m_rt->SetD3D9(nullptr); + m_rt->GetCommonSurface()->SetD3D9Surface(nullptr); // Note that the D3D9 depth stencil survives a swapchain reset, // so there's no need to worry about it in this case } @@ -1889,13 +1970,15 @@ namespace dxvk { inline HRESULT D3D6Device::InitializeIndexBuffers() { static constexpr DWORD Usage = D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY; + d3d9::IDirect3DDevice9* device9 = m_commonD3DDevice->GetD3D9Device(); + for (uint8_t ibIndex = 0; ibIndex < ddrawCaps::IndexBufferCount ; ibIndex++) { const UINT ibSize = ddrawCaps::IndexCount[ibIndex] * sizeof(WORD); - Logger::debug(str::format("D3D6Device::InitializeIndexBuffer: Creating index buffer, size: ", ibSize)); + Logger::debug(str::format("D3D6Device::InitializeIndexBuffer: Creating D3D9 index buffer, size: ", ibSize)); - HRESULT hr = m_d3d9->CreateIndexBuffer(ibSize, Usage, d3d9::D3DFMT_INDEX16, - d3d9::D3DPOOL_DEFAULT, &m_ib9[ibIndex], nullptr); + HRESULT hr = device9->CreateIndexBuffer(ibSize, Usage, d3d9::D3DFMT_INDEX16, + d3d9::D3DPOOL_DEFAULT, &m_ib9[ibIndex], nullptr); if (unlikely(FAILED(hr))) return hr; } diff --git a/src/ddraw/d3d6/d3d6_device.h b/src/ddraw/d3d6/d3d6_device.h index 31f23a4b837..6a3454bec68 100644 --- a/src/ddraw/d3d6/d3d6_device.h +++ b/src/ddraw/d3d6/d3d6_device.h @@ -25,12 +25,13 @@ namespace dxvk { class DDraw4Surface; class DDraw4Interface; class D3D6Texture; + class D3D5Device; class D3D3Device; /** * \brief D3D6 device implementation */ - class D3D6Device final : public DDrawWrappedObject { + class D3D6Device final : public DDrawWrappedObject { public: D3D6Device( @@ -132,6 +133,8 @@ namespace dxvk { void InitializeDS(); + void UpdateSurfaceDirtyTracking(bool dirtyRenderTarget, bool dirtyDepthStencil, bool dirtyPrimarySurface); + HRESULT ResetD3D9Swapchain(d3d9::D3DPRESENT_PARAMETERS* params); D3DCommonDevice* GetCommonD3DDevice() { @@ -142,18 +145,6 @@ namespace dxvk { return m_multithread.AcquireLock(); } - void EnableLegacyLights(bool isD3DLight2) { - m_bridge->SetLegacyLightsState(true, isD3DLight2); - } - - d3d9::D3DPRESENT_PARAMETERS GetPresentParameters() const { - return m_params9; - } - - d3d9::D3DMULTISAMPLE_TYPE GetMultiSampleType() const { - return m_params9.MultiSampleType; - } - DDraw4Surface* GetRenderTarget() const { return m_rt.ptr(); } @@ -166,12 +157,26 @@ namespace dxvk { return m_currentViewport.ptr(); } - D3DMATERIALHANDLE GetCurrentMaterialHandle() const { - return m_materialHandle; + void HandlePreDrawLegacyProjection(DWORD drawFlags) { + if (likely(m_currentViewport != nullptr)) { + m_legacyProjection = m_currentViewport->GetCommonViewport()->GetLegacyProjectionMatrix(drawFlags); + + if (m_legacyProjection != nullptr) { + d3d9::IDirect3DDevice9* device9 = m_commonD3DDevice->GetD3D9Device(); + //Logger::debug("D3D6Device: Applying legacy projection"); + device9->GetTransform(d3d9::D3DTS_PROJECTION, &m_projectionMatrix); + device9->MultiplyTransform(d3d9::D3DTS_PROJECTION, m_legacyProjection); + } + } } - void SetCurrentMaterialHandle(D3DMATERIALHANDLE handle) { - m_materialHandle = handle; + void HandlePostDrawLegacyProjection() { + if (m_legacyProjection != nullptr) { + d3d9::IDirect3DDevice9* device9 = m_commonD3DDevice->GetD3D9Device(); + + //Logger::debug("D3D6Device: Reverting legacy projection"); + device9->SetTransform(d3d9::D3DTS_PROJECTION, &m_projectionMatrix); + } } private: @@ -200,46 +205,30 @@ namespace dxvk { inline void HandlePreDrawFlags(DWORD drawFlags, DWORD vertexTypeDesc) { // Docs: "Direct3D normally performs lighting calculations // on any vertices that contain a vertex normal." - if (m_materialHandle == 0 || + if (m_commonD3DDevice->GetCurrentMaterialHandle() == 0 || (drawFlags & D3DDP_DONOTLIGHT) || !(vertexTypeDesc & D3DFVF_NORMAL)) { - m_d3d9->GetRenderState(d3d9::D3DRS_LIGHTING, &m_lighting); + d3d9::IDirect3DDevice9* device9 = m_commonD3DDevice->GetD3D9Device(); + + device9->GetRenderState(d3d9::D3DRS_LIGHTING, &m_lighting); if (m_lighting) { //Logger::debug("D3D6Device: Disabling lighting"); - m_d3d9->SetRenderState(d3d9::D3DRS_LIGHTING, FALSE); + device9->SetRenderState(d3d9::D3DRS_LIGHTING, FALSE); } } } inline void HandlePostDrawFlags(DWORD drawFlags, DWORD vertexTypeDesc) { - if ((m_materialHandle == 0 || + if ((m_commonD3DDevice->GetCurrentMaterialHandle() == 0 || (drawFlags & D3DDP_DONOTLIGHT) || !(vertexTypeDesc & D3DFVF_NORMAL)) && m_lighting) { - //Logger::debug("D3D6Device: Enabling lighting"); - m_d3d9->SetRenderState(d3d9::D3DRS_LIGHTING, TRUE); - } - } - - inline void HandlePreDrawLegacyProjection(DWORD drawFlags) { - if (likely(m_currentViewport != nullptr)) { - m_legacyProjection = m_currentViewport->GetCommonViewport()->GetLegacyProjectionMatrix(drawFlags); - - if (m_legacyProjection != nullptr) { - //Logger::debug("D3D6Device: Applying legacy projection"); - m_d3d9->GetTransform(d3d9::D3DTS_PROJECTION, &m_projectionMatrix); - m_d3d9->MultiplyTransform(d3d9::D3DTS_PROJECTION, m_legacyProjection); - } - } - } + d3d9::IDirect3DDevice9* device9 = m_commonD3DDevice->GetD3D9Device(); - inline void HandlePostDrawLegacyProjection() { - if (m_legacyProjection != nullptr) { - //Logger::debug("D3D6Device: Reverting legacy projection"); - m_d3d9->SetTransform(d3d9::D3DTS_PROJECTION, &m_projectionMatrix); + //Logger::debug("D3D6Device: Enabling lighting"); + device9->SetRenderState(d3d9::D3DRS_LIGHTING, TRUE); } } - bool m_inScene = false; bool m_alphaOpSet = false; static uint32_t s_deviceCount; @@ -253,16 +242,12 @@ namespace dxvk { Com m_bridge; + Com m_device5; Com m_device3; D3DMultithread m_multithread; - d3d9::D3DPRESENT_PARAMETERS m_params9; - - D3DMATERIALHANDLE m_materialHandle = 0; - D3DDEVICEDESC m_desc; - GUID m_deviceGUID; Com m_rt; Com m_ds; @@ -277,17 +262,6 @@ namespace dxvk { std::array, ddrawCaps::TextureStageCount> m_textures; - // Value of D3DRENDERSTATE_COLORKEYENABLE - DWORD m_colorKeyEnabled = 0; - // Value of D3DRENDERSTATE_ANTIALIAS - DWORD m_antialias = D3DANTIALIAS_NONE; - // Value of D3DRENDERSTATE_LINEPATTERN - D3DLINEPATTERN m_linePattern = { }; - // Value of D3DCLIPSTATUS - D3DCLIPSTATUS m_clipStatus = { }; - // Value of D3DRENDERSTATE_TEXTUREMAPBLEND - DWORD m_textureMapBlend = D3DTBLEND_MODULATE; - D3DMATRIX m_projectionMatrix = { }; const D3DMATRIX* m_legacyProjection = nullptr; diff --git a/src/ddraw/d3d6/d3d6_interface.cpp b/src/ddraw/d3d6/d3d6_interface.cpp index 2f7e970c54f..24daee7e06c 100644 --- a/src/ddraw/d3d6/d3d6_interface.cpp +++ b/src/ddraw/d3d6/d3d6_interface.cpp @@ -8,6 +8,8 @@ #include "../d3d_light.h" #include "../d3d_multithread.h" +#include "../d3d3/d3d3_interface.h" + #include "../ddraw4/ddraw4_interface.h" #include "../ddraw4/ddraw4_surface.h" @@ -20,16 +22,22 @@ namespace dxvk { D3DCommonInterface* commonD3DIntf, Com&& d3d6IntfProxy, IUnknown* pParent) - : DDrawWrappedObject(pParent, std::move(d3d6IntfProxy), std::move(d3d9::Direct3DCreate9(D3D_SDK_VERSION))) + : DDrawWrappedObject(pParent, std::move(d3d6IntfProxy)) , m_commonIntf ( commonIntf ) , m_commonD3DIntf ( commonD3DIntf ) { - // Get the bridge interface to D3D9. - if (unlikely(FAILED(m_d3d9->QueryInterface(__uuidof(IDxvkD3D8InterfaceBridge), reinterpret_cast(&m_bridge))))) { - throw DxvkError("D3D6Interface: ERROR! Failed to get D3D9 Bridge. d3d9.dll might not be DXVK!"); + if (m_commonD3DIntf == nullptr) { + m_commonD3DIntf = new D3DCommonInterface(); + + Com d3d9Intf = d3d9::Direct3DCreate9(D3D_SDK_VERSION); + m_commonD3DIntf->SetD3D9Interface(std::move(d3d9Intf)); } - if (m_commonD3DIntf == nullptr) - m_commonD3DIntf = new D3DCommonInterface(); + d3d9::IDirect3D9* d3d9Intf = m_commonD3DIntf->GetD3D9Interface(); + + // Get the bridge interface to D3D9 + if (unlikely(FAILED(d3d9Intf->QueryInterface(__uuidof(IDxvkD3D8InterfaceBridge), reinterpret_cast(&m_bridge))))) { + throw DxvkError("D3D6Interface: ERROR! Failed to get D3D9 Bridge. d3d9.dll might not be DXVK!"); + } m_commonD3DIntf->SetD3D6Interface(this); @@ -91,6 +99,26 @@ namespace dxvk { Logger::debug("D3D6Interface::QueryInterface: Query for legacy IDirectDraw"); return m_parent->QueryInterface(riid, ppvObject); } + // Some games query for legacy d3d interfaces + if (unlikely(riid == __uuidof(IDirect3D))) { + if (m_commonD3DIntf->GetD3D3Interface() != nullptr) { + Logger::debug("D3D6Interface::QueryInterface: Query for existing IDirect3D"); + return m_commonD3DIntf->GetD3D3Interface()->QueryInterface(riid, ppvObject); + } + + Logger::debug("D3D6Interface::QueryInterface: Query for IDirect3D"); + + Com ppvProxyObject; + HRESULT hr = m_proxy->QueryInterface(riid, reinterpret_cast(&ppvProxyObject)); + if (unlikely(FAILED(hr))) + return hr; + + Com d3d3Intf = new D3D3Interface(m_commonIntf, m_commonD3DIntf.ptr(), std::move(ppvProxyObject), m_parent); + m_commonIntf->SetD3D3Interface(d3d3Intf.ptr()); + *ppvObject = d3d3Intf.ref(); + + return S_OK; + } try { *ppvObject = ref(this->GetInterface(riid)); @@ -114,6 +142,8 @@ namespace dxvk { // with minor differences between the two. Note that the // device listing order matters, so list RGB first, HAL second. + HRESULT hr; + // Software emulation, this is expected to be exposed GUID guidRGB = IID_IDirect3DRGBDevice; D3DDEVICEDESC descRGB_HAL = GetD3D6Caps(IID_IDirect3DRGBDevice, d3dOptions); @@ -129,11 +159,18 @@ namespace dxvk { & ~D3DPTEXTURECAPS_POW2; descRGB_HEL.dpcLineCaps.dwTextureCaps |= D3DPTEXTURECAPS_POW2; descRGB_HEL.dpcTriCaps.dwTextureCaps |= D3DPTEXTURECAPS_POW2; - static char deviceDescRGB[100] = "D6VK RGB"; - static char deviceNameRGB[100] = "D6VK RGB"; - HRESULT hr = lpEnumDevicesCallback(&guidRGB, &deviceDescRGB[0], &deviceNameRGB[0], - &descRGB_HAL, &descRGB_HEL, lpUserArg); + if (likely(!d3dOptions->legacyDeviceNames)) { + static char deviceDescRGB[100] = "D6VK RGB"; + static char deviceNameRGB[100] = "D6VK RGB"; + hr = lpEnumDevicesCallback(&guidRGB, &deviceDescRGB[0], &deviceNameRGB[0], + &descRGB_HAL, &descRGB_HEL, lpUserArg); + } else { + static char legacyDeviceDescRGB[100] = "RGB Emulation"; + static char legacyDeviceNameRGB[100] = "RGB Emulation"; + hr = lpEnumDevicesCallback(&guidRGB, &legacyDeviceDescRGB[0], &legacyDeviceNameRGB[0], + &descRGB_HAL, &descRGB_HEL, lpUserArg); + } if (hr != D3DENUMRET_OK) return D3D_OK; @@ -152,11 +189,18 @@ namespace dxvk { descHAL_HEL.dwDevCaps &= ~D3DDEVCAPS_HWTRANSFORMANDLIGHT & ~D3DDEVCAPS_DRAWPRIMITIVES2 & ~D3DDEVCAPS_DRAWPRIMITIVES2EX; - static char deviceDescHAL[100] = "D6VK HAL"; - static char deviceNameHAL[100] = "D6VK HAL"; - hr = lpEnumDevicesCallback(&guidHAL, &deviceDescHAL[0], &deviceNameHAL[0], - &descHAL_HAL, &descHAL_HEL, lpUserArg); + if (likely(!d3dOptions->legacyDeviceNames)) { + static char deviceDescHAL[100] = "D6VK HAL"; + static char deviceNameHAL[100] = "D6VK HAL"; + hr = lpEnumDevicesCallback(&guidHAL, &deviceDescHAL[0], &deviceNameHAL[0], + &descHAL_HAL, &descHAL_HEL, lpUserArg); + } else { + static char legacyDeviceDescHAL[100] = "Direct3D HAL"; + static char legacyDeviceNameHAL[100] = "Direct3D HAL"; + hr = lpEnumDevicesCallback(&guidHAL, &legacyDeviceDescHAL[0], &legacyDeviceNameHAL[0], + &descHAL_HAL, &descHAL_HEL, lpUserArg); + } if (hr != D3DENUMRET_OK) return D3D_OK; @@ -171,7 +215,7 @@ namespace dxvk { InitReturnPtr(lplpDirect3DLight); - *lplpDirect3DLight = ref(new D3DLight(nullptr, this)); + *lplpDirect3DLight = ref(new D3DLight()); return D3D_OK; } @@ -335,7 +379,7 @@ namespace dxvk { bool rgbFallback = false; if (likely(!d3dOptions->forceSWVP)) { - if (rclsid == IID_IDirect3DHALDevice) { + if (rclsid == IID_IDirect3DHALDevice || rclsid == IID_WineD3DDevice) { Logger::info("D3D6Interface::CreateDevice: Creating an IID_IDirect3DHALDevice device"); deviceCreationFlags9 = D3DCREATE_MIXED_VERTEXPROCESSING; } else if (rclsid == IID_IDirect3DRGBDevice) { @@ -368,7 +412,7 @@ namespace dxvk { } Com d3d6DeviceProxy; - HRESULT hr = m_proxy->CreateDevice(rclsidOverride, rt4->GetProxied(), &d3d6DeviceProxy, nullptr); + HRESULT hr = m_proxy->CreateDevice(rclsidOverride, rt4->GetShadowOrProxied(), &d3d6DeviceProxy, nullptr); if (unlikely(FAILED(hr))) { Logger::warn("D3D6Interface::CreateDevice: Failed to create the proxy device"); return hr; @@ -381,8 +425,7 @@ namespace dxvk { DWORD backBufferWidth = desc.dwWidth; DWORD BackBufferHeight = desc.dwHeight; - if (likely(!d3dOptions->forceProxiedPresent && - d3dOptions->backBufferResize)) { + if (likely(d3dOptions->backBufferResize)) { const bool exclusiveMode = m_commonIntf->GetCooperativeLevel() & DDSCL_EXCLUSIVE; // Ignore any mode size dimensions when in windowed present mode @@ -399,29 +442,7 @@ namespace dxvk { } } - d3d9::D3DFORMAT backBufferFormat = ConvertFormat(desc.ddpfPixelFormat); - - // Determine the supported AA sample count by querying the D3D9 interface - d3d9::D3DMULTISAMPLE_TYPE multiSampleType = d3d9::D3DMULTISAMPLE_NONE; - if (likely(d3dOptions->emulateFSAA != FSAAEmulation::Disabled)) { - HRESULT hr4S = m_d3d9->CheckDeviceMultiSampleType(0, d3d9::D3DDEVTYPE_HAL, backBufferFormat, - TRUE, d3d9::D3DMULTISAMPLE_4_SAMPLES, NULL); - if (unlikely(FAILED(hr4S))) { - HRESULT hr2S = m_d3d9->CheckDeviceMultiSampleType(0, d3d9::D3DDEVTYPE_HAL, backBufferFormat, - TRUE, d3d9::D3DMULTISAMPLE_2_SAMPLES, NULL); - if (unlikely(FAILED(hr2S))) { - Logger::warn("D3D6Interface::CreateDevice: No MSAA support has been detected"); - } else { - Logger::info("D3D6Interface::CreateDevice: Using 2x MSAA for FSAA emulation"); - multiSampleType = d3d9::D3DMULTISAMPLE_2_SAMPLES; - } - } else { - Logger::info("D3D6Interface::CreateDevice: Using 4x MSAA for FSAA emulation"); - multiSampleType = d3d9::D3DMULTISAMPLE_4_SAMPLES; - } - } else { - Logger::info("D3D6Interface::CreateDevice: FSAA emulation is disabled"); - } + const d3d9::D3DFORMAT backBufferFormat = ConvertFormat(desc.ddpfPixelFormat); const DWORD cooperativeLevel = m_commonIntf->GetCooperativeLevel(); @@ -438,22 +459,17 @@ namespace dxvk { Logger::info(str::format("D3D6Interface::CreateDevice: Back buffer size: ", desc.dwWidth, "x", desc.dwHeight)); - DWORD backBufferCount = 0; - if (likely(!d3dOptions->forceSingleBackBuffer)) { - IDirectDrawSurface4* backBuffer = rt4->GetProxied(); - while (backBuffer != nullptr) { - IDirectDrawSurface4* parentSurface = backBuffer; - backBuffer = nullptr; - parentSurface->EnumAttachedSurfaces(&backBuffer, ListBackBufferSurfaces4Callback); - backBufferCount++; - // the swapchain will eventually return to its origin - if (backBuffer == rt4->GetProxied()) - break; - } - } + const DWORD backBufferCount = DetermineBackBufferCount(rt4->GetProxied()); // Consider the front buffer as well when reporting the overall count Logger::info(str::format("D3D6Interface::CreateDevice: Back buffer count: ", backBufferCount + 1)); + Com d3d9Intf = m_commonD3DIntf->GetD3D9Interface(); + + // Determine the supported AA sample count by querying the D3D9 interface + const d3d9::D3DMULTISAMPLE_TYPE multiSampleType = d3dOptions->emulateFSAA != FSAAEmulation::Disabled ? + GetSupportedMultiSampleType(d3d9Intf.ptr(), backBufferFormat) : + d3d9::D3DMULTISAMPLE_NONE; + // Always appears to be enabled when running in non-exclusive mode const bool vBlankStatus = m_commonIntf->GetWaitForVBlank(); @@ -474,7 +490,7 @@ namespace dxvk { params.PresentationInterval = vBlankStatus ? D3DPRESENT_INTERVAL_DEFAULT : D3DPRESENT_INTERVAL_IMMEDIATE; Com device9; - hr = m_d3d9->CreateDevice( + hr = d3d9Intf->CreateDevice( D3DADAPTER_DEFAULT, d3d9::D3DDEVTYPE_HAL, hWnd, @@ -517,17 +533,7 @@ namespace dxvk { InitReturnPtr(lpD3DVertexBuffer); - Com vertexBuffer; - // We don't really need a proxy buffer any longer - /*HRESULT hr = m_proxy->CreateVertexBuffer(desc, &vertexBuffer, usage); - if (unlikely(FAILED(hr))) { - Logger::warn("D3D6Interface::CreateVertexBuffer: Failed to create proxy vertex buffer"); - return hr; - }*/ - - // We need to delay the D3D9 vertex buffer creation as long as possible, to ensure - // that (ideally) we actually have a valid D3D6 device in place when that happens - *lpD3DVertexBuffer = ref(new D3D6VertexBuffer(std::move(vertexBuffer), nullptr, this, dwFlags, *lpVBDesc)); + *lpD3DVertexBuffer = ref(new D3D6VertexBuffer(this, dwFlags, *lpVBDesc)); return D3D_OK; } @@ -554,15 +560,28 @@ namespace dxvk { return D3D_OK; } + // Apparently some games expect D3DFMT_D24X8 to have a 24-bit + // dwZBufferBitDepth, so we have to enumerate both variants. + // According to Wine tests, Windows Vista and newer also enumerate both. depthFormat = GetZBufferFormat(d3d9::D3DFMT_D24X8); + depthFormat.dwZBufferBitDepth = 24; hr = lpEnumCallback(&depthFormat, lpContext); if (unlikely(hr != D3DENUMRET_OK)) return D3D_OK; - depthFormat = GetZBufferFormat(d3d9::D3DFMT_D24S8); - hr = lpEnumCallback(&depthFormat, lpContext); - if (unlikely(hr != D3DENUMRET_OK)) - return D3D_OK; + // Expendable relies on having only the 24-bit dwZBufferBitDepth variant + // of D3DFMT_D24X8 enumerated in order to have working projected shadows + if (likely(d3dOptions->support32BitDepth)) { + depthFormat = GetZBufferFormat(d3d9::D3DFMT_D24X8); + hr = lpEnumCallback(&depthFormat, lpContext); + if (unlikely(hr != D3DENUMRET_OK)) + return D3D_OK; + + depthFormat = GetZBufferFormat(d3d9::D3DFMT_D24S8); + hr = lpEnumCallback(&depthFormat, lpContext); + if (unlikely(hr != D3DENUMRET_OK)) + return D3D_OK; + } return D3D_OK; } @@ -589,4 +608,34 @@ namespace dxvk { return D3D_OK; } + inline DWORD D3D6Interface::DetermineBackBufferCount(IDirectDrawSurface4* renderTarget) { + DWORD backBufferCount = 0; + + const D3DOptions* d3dOptions = m_commonIntf->GetOptions(); + + if (likely(!d3dOptions->forceSingleBackBuffer && !d3dOptions->forceLegacyPresent)) { + IDirectDrawSurface4* backBuffer = renderTarget; + HRESULT hr; + + while (backBuffer != nullptr) { + IDirectDrawSurface4* parentSurface = backBuffer; + backBuffer = nullptr; + + hr = parentSurface->EnumAttachedSurfaces(&backBuffer, ListBackBufferSurfaces4Callback); + if (unlikely(FAILED(hr))) { + Logger::warn("D3D6Interface::DetermineBackBufferCount: Unable to enumerate attached surfaces"); + break; + } + + backBufferCount++; + + // the swapchain will eventually return to its origin + if (backBuffer == renderTarget) + break; + } + } + + return backBufferCount; + } + } \ No newline at end of file diff --git a/src/ddraw/d3d6/d3d6_interface.h b/src/ddraw/d3d6/d3d6_interface.h index b06940023c2..230f5bb4ec3 100644 --- a/src/ddraw/d3d6/d3d6_interface.h +++ b/src/ddraw/d3d6/d3d6_interface.h @@ -16,7 +16,7 @@ namespace dxvk { /** * \brief D3D6 interface implementation */ - class D3D6Interface final : public DDrawWrappedObject { + class D3D6Interface final : public DDrawWrappedObject { public: D3D6Interface( @@ -61,6 +61,8 @@ namespace dxvk { private: + inline DWORD DetermineBackBufferCount(IDirectDrawSurface4* renderTarget); + static uint32_t s_intfCount; uint32_t m_intfCount = 0; diff --git a/src/ddraw/d3d6/d3d6_material.cpp b/src/ddraw/d3d6/d3d6_material.cpp index 3e5d8b0702d..006737e1eb8 100644 --- a/src/ddraw/d3d6/d3d6_material.cpp +++ b/src/ddraw/d3d6/d3d6_material.cpp @@ -1,9 +1,10 @@ #include "d3d6_material.h" -#include "d3d6_device.h" #include "d3d6_interface.h" #include "d3d6_viewport.h" +#include "../d3d_common_device.h" + #include "../ddraw4/ddraw4_interface.h" namespace dxvk { @@ -14,7 +15,7 @@ namespace dxvk { Com&& proxyMaterial, D3D6Interface* pParent, D3DMATERIALHANDLE handle) - : DDrawWrappedObject(pParent, std::move(proxyMaterial), nullptr) { + : DDrawWrappedObject(pParent, std::move(proxyMaterial)) { m_commonMaterial = new D3DCommonMaterial(handle); m_commonMaterial->SetD3D6Material(this); @@ -32,12 +33,33 @@ namespace dxvk { Logger::debug(str::format("D3D6Material: Material nr. [[3-", m_materialCount, "]] bites the dust")); } + HRESULT STDMETHODCALLTYPE D3D6Material::QueryInterface(REFIID riid, void** ppvObject) { + Logger::debug(">>> D3D6Material::QueryInterface"); + + if (unlikely(ppvObject == nullptr)) + return E_POINTER; + + InitReturnPtr(ppvObject); + + try { + *ppvObject = ref(this->GetInterface(riid)); + return S_OK; + } catch (const DxvkError& e) { + Logger::warn(e.message()); + Logger::warn(str::format(riid)); + return E_NOINTERFACE; + } + } + HRESULT STDMETHODCALLTYPE D3D6Material::SetMaterial(D3DMATERIAL *data) { Logger::debug(">>> D3D6Material::SetMaterial"); if (unlikely(data == nullptr)) return DDERR_INVALIDPARAMS; + if (unlikely(!data->dwSize)) + return DDERR_INVALIDPARAMS; + // This call needs to be forwarded to the proxied material // too, in order to have a proper color used during proxied clears HRESULT hr = m_proxy->SetMaterial(data); @@ -52,22 +74,14 @@ namespace dxvk { material9->Emissive = data->dcvEmissive; material9->Power = data->dvPower; - D3DMATERIALHANDLE handle = m_commonMaterial->GetMaterialHandle(); - - Logger::debug(str::format(">>> D3D6Material::SetMaterial: Updated material nr. ", handle)); - Logger::debug(str::format(" Diffuse: ", material9->Diffuse.r, " ", material9->Diffuse.g, " ", material9->Diffuse.b)); - Logger::debug(str::format(" Ambient: ", material9->Ambient.r, " ", material9->Ambient.g, " ", material9->Ambient.b)); - Logger::debug(str::format(" Specular: ", material9->Specular.r, " ", material9->Specular.g, " ", material9->Specular.b)); - Logger::debug(str::format(" Emissive: ", material9->Emissive.r, " ", material9->Emissive.g, " ", material9->Emissive.b)); - Logger::debug(str::format(" Power: ", material9->Power)); - // Update the D3D9 material directly if it's actively being used - D3D6Device* device6 = m_parent->GetCommonInterface()->GetCommonD3DDevice()->GetD3D6Device(); - if (likely(device6 != nullptr)) { - D3DMATERIALHANDLE currentHandle = device6->GetCurrentMaterialHandle(); + D3DCommonDevice* commonDevice = m_parent->GetCommonInterface()->GetCommonD3DDevice(); + if (likely(commonDevice != nullptr)) { + const D3DMATERIALHANDLE handle = m_commonMaterial->GetMaterialHandle(); + const D3DMATERIALHANDLE currentHandle = commonDevice->GetCurrentMaterialHandle(); if (currentHandle == handle) { Logger::debug(str::format("D3D6Material::SetMaterial: Applying material nr. ", handle, " to D3D9")); - device6->GetD3D9()->SetMaterial(material9); + commonDevice->GetD3D9Device()->SetMaterial(material9); } } diff --git a/src/ddraw/d3d6/d3d6_material.h b/src/ddraw/d3d6/d3d6_material.h index 00687cbafd1..4601675a787 100644 --- a/src/ddraw/d3d6/d3d6_material.h +++ b/src/ddraw/d3d6/d3d6_material.h @@ -9,7 +9,7 @@ namespace dxvk { class D3D6Interface; - class D3D6Material final : public DDrawWrappedObject { + class D3D6Material final : public DDrawWrappedObject { public: @@ -20,6 +20,8 @@ namespace dxvk { ~D3D6Material(); + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject); + HRESULT STDMETHODCALLTYPE SetMaterial(D3DMATERIAL *data); HRESULT STDMETHODCALLTYPE GetMaterial(D3DMATERIAL *data); diff --git a/src/ddraw/d3d6/d3d6_texture.cpp b/src/ddraw/d3d6/d3d6_texture.cpp index a2f14f14003..439d464ce16 100644 --- a/src/ddraw/d3d6/d3d6_texture.cpp +++ b/src/ddraw/d3d6/d3d6_texture.cpp @@ -10,7 +10,7 @@ namespace dxvk { Com&& proxyTexture, DDraw4Surface* pParent, D3DTEXTUREHANDLE handle) - : DDrawWrappedObject(pParent, std::move(proxyTexture), nullptr) { + : DDrawWrappedObject(pParent, std::move(proxyTexture)) { m_commonTex = new D3DCommonTexture(m_parent->GetCommonSurface(), handle); m_texCount = ++s_texCount; @@ -95,9 +95,11 @@ namespace dxvk { return D3D_OK; } + // Docs state: "This method only affects the legacy ramp device. + // For all other devices, this method takes no action and returns D3D_OK." HRESULT STDMETHODCALLTYPE D3D6Texture::PaletteChanged(DWORD dwStart, DWORD dwCount) { - Logger::warn("<<< D3D6Texture::PaletteChanged: Proxy"); - return m_proxy->PaletteChanged(dwStart, dwCount); + Logger::debug(">>> D3D6Texture::PaletteChanged"); + return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D6Texture::Load(LPDIRECT3DTEXTURE2 lpD3DTexture2) { @@ -120,7 +122,7 @@ namespace dxvk { m_parent->GetCommonSurface()->SetDesc2(desc2); } - m_parent->GetCommonSurface()->DirtyMipMaps(); + m_parent->GetCommonSurface()->DirtyDDrawSurface(); return hr; } diff --git a/src/ddraw/d3d6/d3d6_texture.h b/src/ddraw/d3d6/d3d6_texture.h index 30b336ac430..b5ca54756f3 100644 --- a/src/ddraw/d3d6/d3d6_texture.h +++ b/src/ddraw/d3d6/d3d6_texture.h @@ -9,7 +9,7 @@ namespace dxvk { class DDraw4Surface; - class D3D6Texture final : public DDrawWrappedObject { + class D3D6Texture final : public DDrawWrappedObject { public: diff --git a/src/ddraw/d3d6/d3d6_viewport.cpp b/src/ddraw/d3d6/d3d6_viewport.cpp index 03e0c8076f9..68b6e42826a 100644 --- a/src/ddraw/d3d6/d3d6_viewport.cpp +++ b/src/ddraw/d3d6/d3d6_viewport.cpp @@ -21,7 +21,7 @@ namespace dxvk { D3DCommonViewport* commonViewport, Com&& proxyViewport, D3D6Interface* pParent) - : DDrawWrappedObject(pParent, std::move(proxyViewport), nullptr) + : DDrawWrappedObject(pParent, std::move(proxyViewport)) , m_commonViewport ( commonViewport ) { if (m_commonViewport == nullptr) @@ -352,14 +352,18 @@ namespace dxvk { D3DCOLOR clearColor = commonMaterial != nullptr ? commonMaterial->GetMaterialColor() : defaultColor; HRESULT hr9 = d3d9Device->Clear(count, rects, flags, clearColor, 1.0f, 0u); - if (unlikely(FAILED(hr9))) - Logger::err("D3D6Viewport::Clear: Failed D3D9 Clear call"); // Restore the previously active viewport if (!m_commonViewport->IsCurrentViewport()) { d3d9Device->SetViewport(¤tViewport9); } + if (unlikely(FAILED(hr9))) { + Logger::warn("D3D6Viewport::Clear: Failed D3D9 Clear call"); + } else { + m_commonViewport->UpdateSurfaceDirtyTracking(true, false, false); + } + return D3D_OK; } @@ -374,11 +378,6 @@ namespace dxvk { if (unlikely(d3dLight->HasViewport())) return D3DERR_LIGHTHASVIEWPORT; - if (m_commonViewport->HasDevice()) { - Logger::debug("D3D6Viewport::AddLight: Enabling device legacy light model"); - m_commonViewport->EnableLegacyLights(d3dLight->IsD3DLight2()); - } - std::vector>& lights = m_commonViewport->GetLights(); // No need to check if the light is already attached, since // if that's the case it will have a set viewport above @@ -587,6 +586,11 @@ namespace dxvk { return hr; } + const bool clearRenderTarget = flags & D3DCLEAR_TARGET; + const bool clearDepthStencil = (flags & D3DCLEAR_ZBUFFER) || (flags & D3DCLEAR_STENCIL); + + m_commonViewport->UpdateSurfaceDirtyTracking(clearRenderTarget, clearDepthStencil, false); + return D3D_OK; } @@ -617,6 +621,25 @@ namespace dxvk { return D3D_OK; } + HRESULT D3D6Viewport::DeactivateLights() { + std::vector>& lights = m_commonViewport->GetLights(); + + if (!lights.size()) + return D3D_OK; + + Logger::debug("D3D6Viewport: Deactivating D3D9 lights"); + + for (auto light: lights) { + const DWORD lightIndex = light->GetIndex(); + if (m_commonViewport->HasDevice() && m_commonViewport->IsCurrentViewport() && light->IsActive()) { + Logger::debug(str::format("D3D6Viewport: Disabling light nr. ", lightIndex)); + m_commonViewport->GetD3D9Device()->LightEnable(lightIndex, FALSE); + } + } + + return D3D_OK; + } + HRESULT D3D6Viewport::ApplyAndActivateLight(DWORD index, D3DLight* light) { d3d9::IDirect3DDevice9* d3d9Device = m_commonViewport->GetD3D9Device(); @@ -626,12 +649,12 @@ namespace dxvk { } else { HRESULT hrLE; if (light->IsActive()) { - Logger::debug(str::format("D3D6Viewport: Enabling light nr. ", index)); + Logger::debug(str::format("D3D6Viewport: Enabling D3D9 light nr. ", index)); hrLE = d3d9Device->LightEnable(index, TRUE); if (unlikely(FAILED(hrLE))) Logger::err("D3D6Viewport: Failed D3D9 LightEnable call (TRUE)"); } else { - Logger::debug(str::format("D3D6Viewport: Disabling light nr. ", index)); + Logger::debug(str::format("D3D6Viewport: Disabling D3D9 light nr. ", index)); hrLE = d3d9Device->LightEnable(index, FALSE); if (unlikely(FAILED(hrLE))) Logger::err("D3D6Viewport: Failed D3D9 LightEnable call (FALSE)"); diff --git a/src/ddraw/d3d6/d3d6_viewport.h b/src/ddraw/d3d6/d3d6_viewport.h index 35d033ead70..07375023ce9 100644 --- a/src/ddraw/d3d6/d3d6_viewport.h +++ b/src/ddraw/d3d6/d3d6_viewport.h @@ -15,7 +15,7 @@ namespace dxvk { class D3D5Viewport; class D3D3Viewport; - class D3D6Viewport final : public DDrawWrappedObject { + class D3D6Viewport final : public DDrawWrappedObject { public: @@ -72,6 +72,8 @@ namespace dxvk { HRESULT ApplyAndActivateLights(); + HRESULT DeactivateLights(); + HRESULT ApplyAndActivateLight(DWORD index, D3DLight* light); D3DCommonViewport* GetCommonViewport() const { diff --git a/src/ddraw/d3d7/d3d7_buffer.cpp b/src/ddraw/d3d7/d3d7_buffer.cpp index 45c431784d0..30fb4d18a09 100644 --- a/src/ddraw/d3d7/d3d7_buffer.cpp +++ b/src/ddraw/d3d7/d3d7_buffer.cpp @@ -4,6 +4,7 @@ #include "../ddraw_util.h" +#include "../d3d_process_vertices.h" #include "../d3d_multithread.h" #include "../ddraw7/ddraw7_interface.h" @@ -13,11 +14,9 @@ namespace dxvk { uint32_t D3D7VertexBuffer::s_buffCount = 0; D3D7VertexBuffer::D3D7VertexBuffer( - Com&& buffProxy, - Com&& pBuffer9, D3D7Interface* pParent, D3DVERTEXBUFFERDESC desc) - : DDrawWrappedObject(pParent, std::move(buffProxy), std::move(pBuffer9)) + : DDrawWrappedObject(pParent, nullptr) , m_commonIntf ( pParent->GetCommonInterface() ) , m_desc ( desc ) , m_stride ( GetFVFSize(desc.dwFVF) ) @@ -35,6 +34,24 @@ namespace dxvk { Logger::debug(str::format("D3D7VertexBuffer: Buffer nr. {{7-", m_buffCount, "}} bites the dust")); } + HRESULT STDMETHODCALLTYPE D3D7VertexBuffer::QueryInterface(REFIID riid, void** ppvObject) { + Logger::debug(">>> D3D7VertexBuffer::QueryInterface"); + + if (unlikely(ppvObject == nullptr)) + return E_POINTER; + + InitReturnPtr(ppvObject); + + try { + *ppvObject = ref(this->GetInterface(riid)); + return S_OK; + } catch (const DxvkError& e) { + Logger::warn(e.message()); + Logger::warn(str::format(riid)); + return E_NOINTERFACE; + } + } + HRESULT STDMETHODCALLTYPE D3D7VertexBuffer::GetVertexBufferDesc(LPD3DVERTEXBUFFERDESC lpVBDesc) { Logger::debug(">>> D3D7VertexBuffer::GetVertexBufferDesc"); @@ -57,7 +74,7 @@ namespace dxvk { if (unlikely(IsOptimized())) return D3DERR_VERTEXBUFFEROPTIMIZED; - RefreshD3D7Device(); + RefreshD3DDevice(); if (unlikely(!IsInitialized())) { HRESULT hrInit = InitializeD3D9(); if (unlikely(FAILED(hrInit))) @@ -67,7 +84,7 @@ namespace dxvk { if (data_size != nullptr) *data_size = m_size; - HRESULT hr = m_d3d9->Lock(0, 0, data, ConvertD3D7LockFlags(flags, m_legacyDiscard, false)); + HRESULT hr = m_vb9->Lock(0, 0, data, ConvertD3D7LockFlags(flags, m_legacyDiscard, false)); if (likely(SUCCEEDED(hr))) m_locked = true; @@ -78,14 +95,14 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE D3D7VertexBuffer::Unlock() { Logger::debug(">>> D3D7VertexBuffer::Unlock"); - RefreshD3D7Device(); + RefreshD3DDevice(); if (unlikely(!IsInitialized())) { HRESULT hrInit = InitializeD3D9(); if (unlikely(FAILED(hrInit))) return hrInit; } - HRESULT hr = m_d3d9->Unlock(); + HRESULT hr = m_vb9->Unlock(); if (likely(SUCCEEDED(hr))) m_locked = false; @@ -107,11 +124,11 @@ namespace dxvk { if (unlikely(!(dwVertexOp & D3DVOP_TRANSFORM))) return DDERR_INVALIDPARAMS; - D3D7Device* device = static_cast(lpD3DDevice); + D3D7Device* device7 = static_cast(lpD3DDevice); D3D7VertexBuffer* vb = static_cast(lpSrcBuffer); - vb->RefreshD3D7Device(); - if (unlikely(vb->GetDevice() == nullptr || device != vb->GetDevice())) { + vb->RefreshD3DDevice(); + if (unlikely(vb->GetDevice() == nullptr || device7 != vb->GetDevice())) { Logger::err("D3D7VertexBuffer::ProcessVertices: Incompatible or null device"); return DDERR_GENERIC; } @@ -126,25 +143,73 @@ namespace dxvk { } // Check and initialize the destination buffer (this buffer) - RefreshD3D7Device(); + d3d9::IDirect3DDevice9* device9 = RefreshD3DDevice(); if (unlikely(!IsInitialized())) { hrInit = InitializeD3D9(); if (unlikely(FAILED(hrInit))) return hrInit; } - D3DDeviceLock lock = device->LockDevice(); + D3DDeviceLock lock = device7->LockDevice(); - HandlePreProcessVerticesFlags(dwVertexOp); + HRESULT hr = D3D_OK; - device->GetD3D9()->SetFVF(m_desc.dwFVF); - device->GetD3D9()->SetStreamSource(0, vb->GetD3D9(), 0, vb->GetStride()); - HRESULT hr = device->GetD3D9()->ProcessVertices(dwSrcIndex, dwDestIndex, dwCount, m_d3d9.ptr(), nullptr, dwFlags); - if (unlikely(FAILED(hr))) { - Logger::err("D3D7VertexBuffer::ProcessVertices: Failed call to D3D9 ProcessVertices"); - } + const D3DOptions* d3dOptions = m_commonIntf->GetOptions(); - HandlePostProcessVerticesFlags(dwVertexOp); + if (likely(d3dOptions->cpuProcessVertices)) { + uint8_t *inData = nullptr; + uint8_t *outData = nullptr; + + hr = vb->GetD3D9VertexBuffer()->Lock(dwSrcIndex * vb->GetStride(), dwCount * vb->GetStride(), + reinterpret_cast(&inData), D3DLOCK_READONLY|D3DLOCK_NOSYSLOCK); + if (unlikely(FAILED(hr))) { + Logger::err("D3D7VertexBuffer::ProcessVertices: Failed to lock source buffer"); + return D3DERR_VERTEXBUFFERLOCKED; + } + + hr = m_vb9->Lock(dwDestIndex * m_stride, dwCount * m_stride, reinterpret_cast(&outData), D3DLOCK_NOSYSLOCK); + if (unlikely(FAILED(hr))) { + Logger::err("D3D7VertexBuffer::ProcessVertices: Failed to lock destination buffer"); + vb->Unlock(); + return D3DERR_VERTEXBUFFERLOCKED; + } + + ProcessVerticesData pvData; + pvData.inData = inData; + pvData.inFVF = vb->GetFVF(); + pvData.inStride = vb->GetStride(); + pvData.outData = outData; + pvData.outFVF = m_desc.dwFVF; + pvData.outStride = m_stride; + pvData.vertexCount = dwCount; + pvData.correction = nullptr; + pvData.dsStatus = nullptr; + pvData.doLighting = dwVertexOp & D3DVOP_LIGHT; + pvData.doClipping = dwVertexOp & D3DVOP_CLIP; + pvData.doNotCopyData = dwFlags & D3DPV_DONOTCOPYDATA; + pvData.doExtents = true; + pvData.isLegacy = false; + + std::vector lights9 = device7->GetLights(); + pvData.lights = &lights9; + + ProcessVerticesSW(device9, m_commonIntf->GetOptions(), &pvData); + + m_vb9->Unlock(); + vb->Unlock(); + + } else { + HandlePreProcessVerticesFlags(dwVertexOp); + + device9->SetFVF(vb->GetFVF()); + device9->SetStreamSource(0, vb->GetD3D9VertexBuffer(), 0, vb->GetStride()); + hr = device9->ProcessVertices(dwSrcIndex, dwDestIndex, dwCount, m_vb9.ptr(), nullptr, dwFlags); + if (unlikely(FAILED(hr))) { + Logger::err("D3D7VertexBuffer::ProcessVertices: Failed call to D3D9 ProcessVertices"); + } + + HandlePostProcessVerticesFlags(dwVertexOp); + } return hr; } @@ -160,7 +225,7 @@ namespace dxvk { D3D7Device* device = static_cast(lpD3DDevice); - RefreshD3D7Device(); + RefreshD3DDevice(); if(unlikely(m_d3d7Device == nullptr || device != m_d3d7Device)) { Logger::err(">>> D3D7VertexBuffer::ProcessVerticesStrided: Incompatible or null device"); return DDERR_GENERIC; @@ -208,6 +273,8 @@ namespace dxvk { return DDERR_GENERIC; } + d3d9::IDirect3DDevice9* device9 = m_d3d7Device->GetCommonD3DDevice()->GetD3D9Device(); + d3d9::D3DPOOL pool = d3d9::D3DPOOL_DEFAULT; if (m_desc.dwCaps & D3DVBCAPS_SYSTEMMEMORY) @@ -220,7 +287,7 @@ namespace dxvk { const DWORD usage = ConvertD3D7UsageFlags(m_desc.dwCaps); m_legacyDiscard = m_commonIntf->GetOptions()->forceLegacyDiscard && (usage & D3DUSAGE_DYNAMIC) && (usage & D3DUSAGE_WRITEONLY); - HRESULT hr = m_d3d7Device->GetD3D9()->CreateVertexBuffer(m_size, usage, m_desc.dwFVF, pool, &m_d3d9, nullptr); + HRESULT hr = device9->CreateVertexBuffer(m_size, usage, m_desc.dwFVF, pool, &m_vb9, nullptr); if (unlikely(FAILED(hr))) { Logger::err("D3D7VertexBuffer::InitializeD3D9: Failed to create D3D9 vertex buffer"); @@ -232,18 +299,39 @@ namespace dxvk { return DD_OK; } - void D3D7VertexBuffer::RefreshD3D7Device() { - D3DCommonDevice* commonDevice = m_commonIntf->GetCommonD3DDevice(); + d3d9::IDirect3DDevice9* D3D7VertexBuffer::RefreshD3DDevice() { + D3DCommonDevice* commonD3DDevice = m_commonIntf->GetCommonD3DDevice(); - D3D7Device* d3d7Device = commonDevice != nullptr ? commonDevice->GetD3D7Device() : nullptr; + D3D7Device* d3d7Device = commonD3DDevice != nullptr ? commonD3DDevice->GetD3D7Device() : nullptr; if (unlikely(m_d3d7Device != d3d7Device)) { // Check if the device has been recreated and reset all D3D9 resources if (unlikely(m_d3d7Device != nullptr)) { - Logger::debug("D3D7VertexBuffer::RefreshD3D7Device: Device context has changed, clearing D3D9 buffers"); - m_d3d9 = nullptr; + Logger::debug("D3D7VertexBuffer::RefreshD3DDevice: Device context has changed, clearing D3D9 buffers"); + m_vb9 = nullptr; } m_d3d7Device = d3d7Device; } + + return commonD3DDevice != nullptr ? commonD3DDevice->GetD3D9Device() : nullptr; + } + + inline void D3D7VertexBuffer::HandlePreProcessVerticesFlags(DWORD pvFlags) { + // Disable lighting if the D3DVOP_LIGHT isn't specified + if (!(pvFlags & D3DVOP_LIGHT)) { + d3d9::IDirect3DDevice9* device9 = m_d3d7Device->GetCommonD3DDevice()->GetD3D9Device(); + + device9->GetRenderState(d3d9::D3DRS_LIGHTING, &m_lighting); + if (m_lighting) + device9->SetRenderState(d3d9::D3DRS_LIGHTING, FALSE); + } + } + + inline void D3D7VertexBuffer::HandlePostProcessVerticesFlags(DWORD pvFlags) { + if (!(pvFlags & D3DVOP_LIGHT) && m_lighting) { + d3d9::IDirect3DDevice9* device9 = m_d3d7Device->GetCommonD3DDevice()->GetD3D9Device(); + + device9->SetRenderState(d3d9::D3DRS_LIGHTING, TRUE); + } } } diff --git a/src/ddraw/d3d7/d3d7_buffer.h b/src/ddraw/d3d7/d3d7_buffer.h index c733df9ad0c..b2d3805f010 100644 --- a/src/ddraw/d3d7/d3d7_buffer.h +++ b/src/ddraw/d3d7/d3d7_buffer.h @@ -10,18 +10,18 @@ namespace dxvk { - class D3D7VertexBuffer final : public DDrawWrappedObject { + class D3D7VertexBuffer final : public DDrawWrappedObject { public: D3D7VertexBuffer( - Com&& buffProxy, - Com&& pBuffer9, D3D7Interface* pParent, D3DVERTEXBUFFERDESC desc); ~D3D7VertexBuffer(); + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject); + HRESULT STDMETHODCALLTYPE GetVertexBufferDesc(LPD3DVERTEXBUFFERDESC lpVBDesc); HRESULT STDMETHODCALLTYPE Lock(DWORD flags, void **data, DWORD *data_size); @@ -36,7 +36,15 @@ namespace dxvk { HRESULT InitializeD3D9(); - void RefreshD3D7Device(); + d3d9::IDirect3DDevice9* RefreshD3DDevice(); + + bool IsInitialized() const { + return m_vb9 != nullptr; + } + + d3d9::IDirect3DVertexBuffer9* GetD3D9VertexBuffer() const { + return m_vb9.ptr(); + } DWORD GetFVF() const { return m_desc.dwFVF; @@ -56,23 +64,12 @@ namespace dxvk { private: - inline bool IsOptimized() const { - return m_desc.dwCaps & D3DVBCAPS_OPTIMIZED; - } + inline void HandlePreProcessVerticesFlags(DWORD pvFlags); - inline void HandlePreProcessVerticesFlags(DWORD pvFlags) { - // Disable lighting if the D3DVOP_LIGHT isn't specified - if (!(pvFlags & D3DVOP_LIGHT)) { - m_d3d7Device->GetD3D9()->GetRenderState(d3d9::D3DRS_LIGHTING, &m_lighting); - if (m_lighting) - m_d3d7Device->GetD3D9()->SetRenderState(d3d9::D3DRS_LIGHTING, FALSE); - } - } + inline void HandlePostProcessVerticesFlags(DWORD pvFlags); - inline void HandlePostProcessVerticesFlags(DWORD pvFlags) { - if (!(pvFlags & D3DVOP_LIGHT) && m_lighting) { - m_d3d7Device->GetD3D9()->SetRenderState(d3d9::D3DRS_LIGHTING, TRUE); - } + inline bool IsOptimized() const { + return m_desc.dwCaps & D3DVBCAPS_OPTIMIZED; } inline void ListBufferDetails() const { @@ -82,22 +79,24 @@ namespace dxvk { Logger::debug(str::format(" Vertices: ", m_size / m_stride)); } - bool m_locked = false; - bool m_legacyDiscard = false; + bool m_locked = false; + bool m_legacyDiscard = false; + + static uint32_t s_buffCount; + uint32_t m_buffCount = 0; - static uint32_t s_buffCount; - uint32_t m_buffCount = 0; + DDrawCommonInterface* m_commonIntf = nullptr; - DDrawCommonInterface* m_commonIntf = nullptr; + D3D7Device* m_d3d7Device = nullptr; - D3D7Device* m_d3d7Device = nullptr; + Com m_vb9; - DWORD m_lighting = FALSE; + DWORD m_lighting = FALSE; - D3DVERTEXBUFFERDESC m_desc; + D3DVERTEXBUFFERDESC m_desc; - UINT m_stride = 0; - UINT m_size = 0; + UINT m_stride = 0; + UINT m_size = 0; }; diff --git a/src/ddraw/d3d7/d3d7_device.cpp b/src/ddraw/d3d7/d3d7_device.cpp index fdfbed00c87..d8d5af65b03 100644 --- a/src/ddraw/d3d7/d3d7_device.cpp +++ b/src/ddraw/d3d7/d3d7_device.cpp @@ -16,14 +16,14 @@ namespace dxvk { Com&& d3d7DeviceProxy, D3D7Interface* pParent, D3DDEVICEDESC7 Desc, + GUID deviceGUID, d3d9::D3DPRESENT_PARAMETERS Params9, Com&& pDevice9, DDraw7Surface* pSurface, DWORD CreationFlags9) - : DDrawWrappedObject(pParent, std::move(d3d7DeviceProxy), std::move(pDevice9)) + : DDrawWrappedObject(pParent, std::move(d3d7DeviceProxy)) , m_commonD3DDevice ( commonD3DDevice ) , m_multithread ( CreationFlags9 & D3DCREATE_MULTITHREADED ) - , m_params9 ( Params9 ) , m_desc ( Desc ) , m_rt ( pSurface ) { if (m_parent != nullptr) { @@ -34,28 +34,42 @@ namespace dxvk { throw DxvkError("D3D7Device: ERROR! Failed to retrieve the common interface!"); } - // Get the bridge interface to D3D9 - if (unlikely(FAILED(m_d3d9->QueryInterface(__uuidof(IDxvkD3D8Bridge), reinterpret_cast(&m_bridge))))) { - throw DxvkError("D3D7Device: ERROR! Failed to get D3D9 Bridge. d3d9.dll might not be DXVK!"); - } - - // Common D3D9 index buffers - if (unlikely(FAILED(InitializeIndexBuffers()))) { - throw DxvkError("D3D7Device: ERROR! Failed to initialize D3D9 index buffers."); - } + d3d9::IDirect3DDevice9* device9; if (likely(m_commonD3DDevice == nullptr)) { - m_commonD3DDevice = new D3DCommonDevice(m_commonIntf, CreationFlags9, - m_bridge->DetermineInitialTextureMemory()); + m_commonD3DDevice = new D3DCommonDevice(m_commonIntf, deviceGUID, Params9, CreationFlags9); + + m_commonD3DDevice->SetD3D9Device(std::move(pDevice9)); + device9 = m_commonD3DDevice->GetD3D9Device(); const D3DOptions* d3dOptions = m_commonIntf->GetOptions(); if (unlikely(d3dOptions->emulateFSAA == FSAAEmulation::Forced)) { Logger::warn("D3D7Device: Force enabling AA"); - m_d3d9->SetRenderState(d3d9::D3DRS_MULTISAMPLEANTIALIAS, TRUE); + device9->SetRenderState(d3d9::D3DRS_MULTISAMPLEANTIALIAS, TRUE); } + } else { + device9 = m_commonD3DDevice->GetD3D9Device(); + // Very important, otherwise the depth stencil isn't dirtied on draws + m_ds = m_rt->GetAttachedDepthStencil(); } + // Common D3D9 index buffers + if (unlikely(FAILED(InitializeIndexBuffers()))) { + throw DxvkError("D3D7Device: ERROR! Failed to initialize D3D9 index buffers."); + } + + // Get the bridge interface to D3D9 + if (unlikely(FAILED(device9->QueryInterface(__uuidof(IDxvkD3D8Bridge), reinterpret_cast(&m_bridge))))) { + throw DxvkError("D3D7Device: ERROR! Failed to get D3D9 Bridge. d3d9.dll might not be DXVK!"); + } + + if (unlikely(!m_commonD3DDevice->GetTotalTextureMemory())) + m_commonD3DDevice->SetTotalTextureMemory(m_bridge->DetermineInitialTextureMemory()); + + // Update D3D9 legacy light state + m_bridge->SetLegacyLightsState(false); + if (m_commonD3DDevice->GetOrigin() == nullptr) m_commonD3DDevice->SetOrigin(this); @@ -70,14 +84,16 @@ namespace dxvk { D3D7Device::~D3D7Device() { if (LogIndexBufferUsageStats()) { - Logger::info("D3D7Device: Index buffer upload statistics:"); - Logger::info(str::format(" XXS: ", m_ib9_uploads[0])); - Logger::info(str::format(" XS : ", m_ib9_uploads[1])); - Logger::info(str::format(" S : ", m_ib9_uploads[2])); - Logger::info(str::format(" M : ", m_ib9_uploads[3])); - Logger::info(str::format(" L : ", m_ib9_uploads[4])); - Logger::info(str::format(" XL : ", m_ib9_uploads[5])); - Logger::info(str::format(" XXL: ", m_ib9_uploads[6])); + Logger::debug("D3D7Device: Index buffer upload statistics:"); + Logger::debug(str::format(" 0.5 kb : ", m_ib9_uploads[0])); + Logger::debug(str::format(" 1 kb : ", m_ib9_uploads[1])); + Logger::debug(str::format(" 2 kb : ", m_ib9_uploads[2])); + Logger::debug(str::format(" 4 kb : ", m_ib9_uploads[3])); + Logger::debug(str::format(" 8 kb : ", m_ib9_uploads[4])); + Logger::debug(str::format(" 16 kb : ", m_ib9_uploads[5])); + Logger::debug(str::format(" 32 kb : ", m_ib9_uploads[6])); + Logger::debug(str::format(" 64 kb : ", m_ib9_uploads[7])); + Logger::debug(str::format(" 128 kb : ", m_ib9_uploads[8])); } if (m_commonD3DDevice->GetD3D7Device() == this) @@ -89,6 +105,39 @@ namespace dxvk { Logger::debug(str::format("D3D7Device: Device nr. ((7-", m_deviceCount, ")) bites the dust")); } + HRESULT STDMETHODCALLTYPE D3D7Device::QueryInterface(REFIID riid, void** ppvObject) { + Logger::debug(">>> D3D7Device::QueryInterface"); + + if (unlikely(ppvObject == nullptr)) + return E_POINTER; + + InitReturnPtr(ppvObject); + + if (unlikely(riid == __uuidof(IDirect3DDevice))) { + Logger::debug("D3D7Device::QueryInterface: Query for IDirect3DDevice"); + return E_NOINTERFACE; + } + if (unlikely(riid == __uuidof(IDirect3DDevice2))) { + Logger::debug("D3D7Device::QueryInterface: Query for IDirect3DDevice2"); + return E_NOINTERFACE; + } + // Some games, like Conquest: Frontier Wars, query for + // IDirect3DDevice3, although that's not supported + if (unlikely(riid == __uuidof(IDirect3DDevice3))) { + Logger::debug("D3D7Device::QueryInterface: Query for IDirect3DDevice3"); + return E_NOINTERFACE; + } + + try { + *ppvObject = ref(this->GetInterface(riid)); + return S_OK; + } catch (const DxvkError& e) { + Logger::warn(e.message()); + Logger::warn(str::format(riid)); + return E_NOINTERFACE; + } + } + HRESULT STDMETHODCALLTYPE D3D7Device::GetCaps(D3DDEVICEDESC7 *desc) { Logger::debug(">>> D3D7Device::GetCaps"); @@ -207,13 +256,13 @@ namespace dxvk { RefreshLastUsedDevice(); - if (unlikely(m_inScene)) + if (unlikely(m_commonD3DDevice->IsInScene())) return D3DERR_SCENE_IN_SCENE; - HRESULT hr = m_d3d9->BeginScene(); + HRESULT hr = m_commonD3DDevice->GetD3D9Device()->BeginScene(); if (likely(SUCCEEDED(hr))) - m_inScene = true; + m_commonD3DDevice->SetInScene(true); return hr; } @@ -225,29 +274,13 @@ namespace dxvk { RefreshLastUsedDevice(); - if (unlikely(!m_inScene)) + if (unlikely(!m_commonD3DDevice->IsInScene())) return D3DERR_SCENE_NOT_IN_SCENE; - HRESULT hr = m_d3d9->EndScene(); - - if (likely(SUCCEEDED(hr))) { - const D3DOptions* d3dOptions = m_commonIntf->GetOptions(); - - if (d3dOptions->forceProxiedPresent) { - // If we have drawn anything, we need to make sure we blit back - // the results onto the D3D7 render target before we flip it - if (m_commonIntf->HasDrawn()) - BlitToDDrawSurface(m_rt->GetProxied(), m_rt->GetD3D9()); + HRESULT hr = m_commonD3DDevice->GetD3D9Device()->EndScene(); - m_rt->GetProxied()->Flip(static_cast(m_commonIntf->GetFlipRTSurface()), - m_commonIntf->GetFlipRTFlags()); - - if (likely(d3dOptions->backBufferGuard != D3DBackBufferGuard::Strict)) - m_commonIntf->ResetDrawTracking(); - } - - m_inScene = false; - } + if (likely(SUCCEEDED(hr))) + m_commonD3DDevice->SetInScene(false); return hr; } @@ -280,20 +313,10 @@ namespace dxvk { DDraw7Surface* rt7 = static_cast(surface); - const D3DOptions* d3dOptions = m_commonIntf->GetOptions(); - - if (unlikely(d3dOptions->forceProxiedPresent)) { - HRESULT hrRT = m_proxy->SetRenderTarget(rt7->GetProxied(), flags); - if (unlikely(FAILED(hrRT))) { - Logger::warn("D3D7Device::SetRenderTarget: Failed to set RT"); - return hrRT; - } - } else { - // Needed to ensure proxied Z/Stencil viewport clears will work - HRESULT hrRT = m_proxy->SetRenderTarget(rt7->GetProxied(), flags); - if (unlikely(FAILED(hrRT))) - Logger::debug("D3D7Device::SetRenderTarget: Failed to set RT"); - } + // Needed to ensure proxied Z/Stencil viewport clears will work + HRESULT hrRT = m_proxy->SetRenderTarget(rt7->GetShadowOrProxied(), flags); + if (unlikely(FAILED(hrRT))) + Logger::debug("D3D7Device::SetRenderTarget: Failed to set RT"); HRESULT hr = rt7->GetCommonSurface()->ValidateRTUsage(); if (unlikely(FAILED(hr))) @@ -305,7 +328,9 @@ namespace dxvk { return hr; } - hr = m_d3d9->SetRenderTarget(0, rt7->GetD3D9()); + d3d9::IDirect3DDevice9* device9 = m_commonD3DDevice->GetD3D9Device(); + + hr = device9->SetRenderTarget(0, rt7->GetCommonSurface()->GetD3D9Surface()); // TODO: Test suggest that the passed surface is saved as the // current RT even if the call fails, or it is an invalid surface... @@ -328,7 +353,7 @@ namespace dxvk { return hrDS; } - hrDS = m_d3d9->SetDepthStencilSurface(m_ds->GetD3D9()); + hrDS = device9->SetDepthStencilSurface(m_ds->GetCommonSurface()->GetD3D9Surface()); if (unlikely(FAILED(hrDS))) { Logger::err("D3D7Device::SetRenderTarget: Failed to set D3D9 DS"); return hrDS; @@ -338,7 +363,7 @@ namespace dxvk { } else { Logger::debug("D3D7Device::SetRenderTarget: RT has no depth stencil attached"); - hrDS = m_d3d9->SetDepthStencilSurface(nullptr); + hrDS = device9->SetDepthStencilSurface(nullptr); if (unlikely(FAILED(hrDS))) { Logger::err("D3D7Device::SetRenderTarget: Failed to clear the D3D9 DS"); return hrDS; @@ -372,28 +397,41 @@ namespace dxvk { Logger::debug(">>> D3D7Device::Clear"); + // Fast skip + if (unlikely(!count && rects)) + return D3D_OK; + // We are now allowing proxy back buffer blits in certain cases, so // we must also ensure the back buffer clear calls are proxied HRESULT hr = m_proxy->Clear(count, rects, flags, color, z, stencil); if (unlikely(FAILED(hr))) Logger::debug("D3D7Device::Clear: Failed proxied call"); - return m_d3d9->Clear(count, rects, flags, color, static_cast(z), stencil); + hr = m_commonD3DDevice->GetD3D9Device()->Clear(count, rects, flags, color, static_cast(z), stencil); + if (unlikely(FAILED(hr))) + return hr; + + const bool clearRenderTarget = flags & D3DCLEAR_TARGET; + const bool clearDepthStencil = (flags & D3DCLEAR_ZBUFFER) || (flags & D3DCLEAR_STENCIL); + + UpdateSurfaceDirtyTracking(clearRenderTarget, clearDepthStencil, false); + + return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D7Device::SetTransform(D3DTRANSFORMSTATETYPE state, D3DMATRIX *matrix) { Logger::debug(">>> D3D7Device::SetTransform"); - return m_d3d9->SetTransform(ConvertTransformState(state), matrix); + return m_commonD3DDevice->GetD3D9Device()->SetTransform(ConvertTransformState(state), matrix); } HRESULT STDMETHODCALLTYPE D3D7Device::GetTransform(D3DTRANSFORMSTATETYPE state, D3DMATRIX *matrix) { Logger::debug(">>> D3D7Device::GetTransform"); - return m_d3d9->GetTransform(ConvertTransformState(state), matrix); + return m_commonD3DDevice->GetD3D9Device()->GetTransform(ConvertTransformState(state), matrix); } HRESULT STDMETHODCALLTYPE D3D7Device::MultiplyTransform(D3DTRANSFORMSTATETYPE state, D3DMATRIX *matrix) { Logger::debug(">>> D3D7Device::MultiplyTransform"); - return m_d3d9->MultiplyTransform(ConvertTransformState(state), matrix); + return m_commonD3DDevice->GetD3D9Device()->MultiplyTransform(ConvertTransformState(state), matrix); } HRESULT STDMETHODCALLTYPE D3D7Device::SetViewport(D3DVIEWPORT7 *data) { @@ -442,7 +480,7 @@ namespace dxvk { if (unlikely(data->dvMinZ == 0.0f && data->dvMaxZ == 0.0f)) data->dvMaxZ = 1.0f; - return m_d3d9->SetViewport(reinterpret_cast(data)); + return m_commonD3DDevice->GetD3D9Device()->SetViewport(reinterpret_cast(data)); } HRESULT STDMETHODCALLTYPE D3D7Device::GetViewport(D3DVIEWPORT7 *data) { @@ -453,7 +491,7 @@ namespace dxvk { if (unlikely(data == nullptr)) return DDERR_INVALIDPARAMS; - return m_d3d9->GetViewport(reinterpret_cast(data)); + return m_commonD3DDevice->GetD3D9Device()->GetViewport(reinterpret_cast(data)); } HRESULT STDMETHODCALLTYPE D3D7Device::SetMaterial(D3DMATERIAL7 *data) { @@ -462,7 +500,7 @@ namespace dxvk { if (unlikely(data == nullptr)) return DDERR_INVALIDPARAMS; - return m_d3d9->SetMaterial(reinterpret_cast(data)); + return m_commonD3DDevice->GetD3D9Device()->SetMaterial(reinterpret_cast(data)); } HRESULT STDMETHODCALLTYPE D3D7Device::GetMaterial(D3DMATERIAL7 *data) { @@ -471,7 +509,7 @@ namespace dxvk { if (unlikely(data == nullptr)) return DDERR_INVALIDPARAMS; - return m_d3d9->GetMaterial(reinterpret_cast(data)); + return m_commonD3DDevice->GetD3D9Device()->GetMaterial(reinterpret_cast(data)); } HRESULT STDMETHODCALLTYPE D3D7Device::SetLight(DWORD idx, D3DLIGHT7 *data) { @@ -495,7 +533,10 @@ namespace dxvk { || data->dvAttenuation2 < 0.0f))) return DDERR_INVALIDPARAMS; - HRESULT hr = m_d3d9->SetLight(idx, reinterpret_cast(data)); + d3d9::D3DLIGHT9* light9 = reinterpret_cast(data); + m_lights[idx] = *light9; + + HRESULT hr = m_commonD3DDevice->GetD3D9Device()->SetLight(idx, light9); if (unlikely(FAILED(hr))) return DDERR_INVALIDPARAMS; @@ -508,7 +549,7 @@ namespace dxvk { if (unlikely(data == nullptr)) return DDERR_INVALIDPARAMS; - HRESULT hr = m_d3d9->GetLight(idx, reinterpret_cast(data)); + HRESULT hr = m_commonD3DDevice->GetD3D9Device()->GetLight(idx, reinterpret_cast(data)); if (unlikely(FAILED(hr))) return DDERR_INVALIDPARAMS; @@ -545,9 +586,9 @@ namespace dxvk { } State9 = d3d9::D3DRS_MULTISAMPLEANTIALIAS; - m_antialias = dwRenderState; - dwRenderState = m_antialias == D3DANTIALIAS_SORTDEPENDENT - || m_antialias == D3DANTIALIAS_SORTINDEPENDENT + m_commonD3DDevice->SetAntialias(dwRenderState); + dwRenderState = dwRenderState == D3DANTIALIAS_SORTDEPENDENT + || dwRenderState == D3DANTIALIAS_SORTINDEPENDENT || d3dOptions->emulateFSAA == FSAAEmulation::Forced ? TRUE : FALSE; break; } @@ -566,7 +607,7 @@ namespace dxvk { if (!std::exchange(s_linePatternErrorShown, true)) Logger::warn("D3D7Device::SetRenderState: Unimplemented render state D3DRS_LINEPATTERN"); - m_linePattern = bit::cast(dwRenderState); + m_commonD3DDevice->SetLinePattern(bit::cast(dwRenderState)); return D3D_OK; // Not supported by D3D7 @@ -587,11 +628,11 @@ namespace dxvk { break; case D3DRENDERSTATE_COLORKEYENABLE: { - m_colorKeyEnabled = dwRenderState; + m_commonD3DDevice->SetColorKeyEnable(dwRenderState); const bool validColorKey = m_textures[0] != nullptr ? m_textures[0]->GetCommonSurface()->HasValidColorKey() : false; - m_bridge->SetColorKeyState(m_colorKeyEnabled && validColorKey); - if (m_colorKeyEnabled && validColorKey) { + m_bridge->SetColorKeyState(dwRenderState && validColorKey); + if (dwRenderState && validColorKey) { DDCOLORKEY normalizedColorKey = m_textures[0]->GetCommonSurface()->GetColorKeyNormalized(); m_bridge->SetColorKey(normalizedColorKey.dwColorSpaceLowValue, normalizedColorKey.dwColorSpaceHighValue); @@ -616,12 +657,12 @@ namespace dxvk { // Used in conjunction with D3DRENDERSTATE_COLORKEYENABLE case D3DRENDERSTATE_COLORKEYBLENDENABLE: - m_colorKeyBlendEnabled = dwRenderState; + m_commonD3DDevice->SetColorKeyBlendEnable(dwRenderState); return D3D_OK; } // This call will never fail - return m_d3d9->SetRenderState(State9, dwRenderState); + return m_commonD3DDevice->GetD3D9Device()->SetRenderState(State9, dwRenderState); } HRESULT STDMETHODCALLTYPE D3D7Device::GetRenderState(D3DRENDERSTATETYPE dwRenderStateType, LPDWORD lpdwRenderState) { @@ -634,7 +675,8 @@ namespace dxvk { // As opposed to D3D8/9, D3D7 actually validates and // errors out in case of unknown/invalid render states - if (unlikely(!IsValidD3D7RenderStateType(dwRenderStateType))) { + if (unlikely(!IsValidD3D7RenderStateType(dwRenderStateType) + && !m_commonIntf->GetOptions()->apitraceMode)) { Logger::debug(str::format("D3D7Device::GetRenderState: Invalid render state ", dwRenderStateType)); return DDERR_INVALIDPARAMS; } @@ -647,7 +689,7 @@ namespace dxvk { break; case D3DRENDERSTATE_ANTIALIAS: - *lpdwRenderState = m_antialias; + *lpdwRenderState = m_commonD3DDevice->GetAntialias(); return D3D_OK; // Always enabled on later APIs, so it can't really be turned off @@ -658,7 +700,7 @@ namespace dxvk { return D3D_OK; case D3DRENDERSTATE_LINEPATTERN: - *lpdwRenderState = bit::cast(m_linePattern); + *lpdwRenderState = bit::cast(m_commonD3DDevice->GetLinePattern()); return D3D_OK; // Not supported by D3D7 @@ -675,12 +717,12 @@ namespace dxvk { break; case D3DRENDERSTATE_COLORKEYENABLE: - *lpdwRenderState = m_colorKeyEnabled; + *lpdwRenderState = m_commonD3DDevice->GetColorKeyEnable(); return D3D_OK; case D3DRENDERSTATE_ZBIAS: { DWORD bias = 0; - m_d3d9->GetRenderState(d3d9::D3DRS_DEPTHBIAS, &bias); + m_commonD3DDevice->GetD3D9Device()->GetRenderState(d3d9::D3DRS_DEPTHBIAS, &bias); *lpdwRenderState = static_cast(bit::cast(bias) * ddrawCaps::ZBIAS_SCALE_INV); return D3D_OK; } @@ -690,12 +732,12 @@ namespace dxvk { return D3D_OK; case D3DRENDERSTATE_COLORKEYBLENDENABLE: - *lpdwRenderState = m_colorKeyBlendEnabled; + *lpdwRenderState = m_commonD3DDevice->GetColorKeyBlendEnable(); return D3D_OK; } // This call will never fail - return m_d3d9->GetRenderState(State9, lpdwRenderState); + return m_commonD3DDevice->GetD3D9Device()->GetRenderState(State9, lpdwRenderState); } HRESULT STDMETHODCALLTYPE D3D7Device::BeginStateBlock() { @@ -706,7 +748,7 @@ namespace dxvk { if (unlikely(m_recorder != nullptr)) return D3DERR_INBEGINSTATEBLOCK; - HRESULT hr = m_d3d9->BeginStateBlock(); + HRESULT hr = m_commonD3DDevice->GetD3D9Device()->BeginStateBlock(); if (likely(SUCCEEDED(hr))) { m_handle++; @@ -732,7 +774,7 @@ namespace dxvk { return D3DERR_NOTINBEGINSTATEBLOCK; Com pStateBlock; - HRESULT hr = m_d3d9->EndStateBlock(&pStateBlock); + HRESULT hr = m_commonD3DDevice->GetD3D9Device()->EndStateBlock(&pStateBlock); if (likely(SUCCEEDED(hr))) { m_recorder->SetD3D9(std::move(pStateBlock)); @@ -831,7 +873,7 @@ namespace dxvk { } Com pStateBlock9; - HRESULT res = m_d3d9->CreateStateBlock(d3d9::D3DSTATEBLOCKTYPE(d3dsbType), &pStateBlock9); + HRESULT res = m_commonD3DDevice->GetD3D9Device()->CreateStateBlock(d3d9::D3DSTATEBLOCKTYPE(d3dsbType), &pStateBlock9); if (likely(SUCCEEDED(res))) { m_handle++; @@ -845,8 +887,6 @@ namespace dxvk { } HRESULT STDMETHODCALLTYPE D3D7Device::PreLoad(IDirectDrawSurface7 *surface) { - D3DDeviceLock lock = LockDevice(); - Logger::debug(">>> D3D7Device::PreLoad"); if (unlikely(!m_commonIntf->IsWrappedSurface(surface))) { @@ -856,22 +896,18 @@ namespace dxvk { DDraw7Surface* surface7 = static_cast(surface); - HRESULT hr = m_proxy->PreLoad(surface7->GetProxied()); - if (unlikely(FAILED(hr))) { - Logger::warn("D3D7Device::PreLoad: Failed to preload proxied surface"); - return hr; - } + if (unlikely(!surface7->GetCommonSurface()->IsManaged())) + return DDERR_INVALIDPARAMS; // Make sure the texture or surface is initialized and updated - hr = surface7->InitializeOrUploadD3D9(); - + HRESULT hr = surface7->InitializeOrUploadD3D9(); if (unlikely(FAILED(hr))) { Logger::err("D3D7Device::PreLoad: Failed to initialize/upload D3D9 surface"); return hr; } // Does not return an HRESULT - surface7->GetD3D9()->PreLoad(); + surface7->GetCommonSurface()->GetD3D9Surface()->PreLoad(); return D3D_OK; } @@ -889,8 +925,14 @@ namespace dxvk { if (unlikely(lpvVertices == nullptr)) return DDERR_INVALIDPARAMS; - m_d3d9->SetFVF(dwVertexTypeDesc); - HRESULT hr = m_d3d9->DrawPrimitiveUP( + d3d9::IDirect3DDevice9* device9 = m_commonD3DDevice->GetD3D9Device(); + + m_rt->InitializeOrUploadD3D9(); + if (likely(m_ds != nullptr)) + m_ds->InitializeOrUploadD3D9(); + + device9->SetFVF(dwVertexTypeDesc); + HRESULT hr = device9->DrawPrimitiveUP( d3d9::D3DPRIMITIVETYPE(d3dptPrimitiveType), GetPrimitiveCount(d3dptPrimitiveType, dwVertexCount), lpvVertices, @@ -901,7 +943,7 @@ namespace dxvk { return hr; } - m_commonIntf->UpdateDrawTracking(); + UpdateSurfaceDirtyTracking(true, true, true); return hr; } @@ -919,8 +961,14 @@ namespace dxvk { if (unlikely(lpvVertices == nullptr || lpwIndices == nullptr)) return DDERR_INVALIDPARAMS; - m_d3d9->SetFVF(dwVertexTypeDesc); - HRESULT hr = m_d3d9->DrawIndexedPrimitiveUP( + d3d9::IDirect3DDevice9* device9 = m_commonD3DDevice->GetD3D9Device(); + + m_rt->InitializeOrUploadD3D9(); + if (likely(m_ds != nullptr)) + m_ds->InitializeOrUploadD3D9(); + + device9->SetFVF(dwVertexTypeDesc); + HRESULT hr = device9->DrawIndexedPrimitiveUP( d3d9::D3DPRIMITIVETYPE(d3dptPrimitiveType), 0, dwVertexCount, @@ -935,33 +983,37 @@ namespace dxvk { return hr; } - m_commonIntf->UpdateDrawTracking(); + UpdateSurfaceDirtyTracking(true, true, true); return hr; } HRESULT STDMETHODCALLTYPE D3D7Device::SetClipStatus(D3DCLIPSTATUS *clip_status) { - D3DDeviceLock lock = LockDevice(); - Logger::debug(">>> D3D7Device::SetClipStatus"); if (unlikely(clip_status == nullptr)) return DDERR_INVALIDPARAMS; - m_clipStatus = *clip_status; - return D3D_OK; } HRESULT STDMETHODCALLTYPE D3D7Device::GetClipStatus(D3DCLIPSTATUS *clip_status) { - D3DDeviceLock lock = LockDevice(); - Logger::debug(">>> D3D7Device::GetClipStatus"); if (unlikely(clip_status == nullptr)) return DDERR_INVALIDPARAMS; - *clip_status = m_clipStatus; + d3d9::D3DVIEWPORT9 viewport9; + if (SUCCEEDED(m_commonD3DDevice->GetD3D9Device()->GetViewport(&viewport9))) { + clip_status->dwFlags = D3DCLIPSTATUS_EXTENTS2; + clip_status->dwStatus = 0; + clip_status->minx = viewport9.X; + clip_status->maxx = viewport9.X + viewport9.Height; + clip_status->miny = viewport9.Y; + clip_status->maxy = viewport9.Y + viewport9.Width; + clip_status->minz = 0; + clip_status->maxz = 0; + } return D3D_OK; } @@ -979,11 +1031,17 @@ namespace dxvk { if (unlikely(lpVertexArray == nullptr)) return DDERR_INVALIDPARAMS; + d3d9::IDirect3DDevice9* device9 = m_commonD3DDevice->GetD3D9Device(); + + m_rt->InitializeOrUploadD3D9(); + if (likely(m_ds != nullptr)) + m_ds->InitializeOrUploadD3D9(); + // Transform strided vertex data to a standard vertex buffer stream PackedVertexBuffer pvb = TransformStridedtoUP(dwVertexTypeDesc, lpVertexArray, dwVertexCount); - m_d3d9->SetFVF(dwVertexTypeDesc); - HRESULT hr = m_d3d9->DrawPrimitiveUP( + device9->SetFVF(dwVertexTypeDesc); + HRESULT hr = device9->DrawPrimitiveUP( d3d9::D3DPRIMITIVETYPE(d3dptPrimitiveType), GetPrimitiveCount(d3dptPrimitiveType, dwVertexCount), pvb.vertexData.data(), @@ -994,7 +1052,7 @@ namespace dxvk { return hr; } - m_commonIntf->UpdateDrawTracking(); + UpdateSurfaceDirtyTracking(true, true, true); return hr; } @@ -1012,11 +1070,17 @@ namespace dxvk { if (unlikely(lpVertexArray == nullptr || lpwIndices == nullptr)) return DDERR_INVALIDPARAMS; + d3d9::IDirect3DDevice9* device9 = m_commonD3DDevice->GetD3D9Device(); + + m_rt->InitializeOrUploadD3D9(); + if (likely(m_ds != nullptr)) + m_ds->InitializeOrUploadD3D9(); + // Transform strided vertex data to a standard vertex buffer stream PackedVertexBuffer pvb = TransformStridedtoUP(dwVertexTypeDesc, lpVertexArray, dwVertexCount); - m_d3d9->SetFVF(dwVertexTypeDesc); - HRESULT hr = m_d3d9->DrawIndexedPrimitiveUP( + device9->SetFVF(dwVertexTypeDesc); + HRESULT hr = device9->DrawIndexedPrimitiveUP( d3d9::D3DPRIMITIVETYPE(d3dptPrimitiveType), 0, dwVertexCount, @@ -1031,7 +1095,7 @@ namespace dxvk { return hr; } - m_commonIntf->UpdateDrawTracking(); + UpdateSurfaceDirtyTracking(true, true, true); return hr; } @@ -1056,9 +1120,15 @@ namespace dxvk { return D3DERR_VERTEXBUFFERLOCKED; } - m_d3d9->SetFVF(vb->GetFVF()); - m_d3d9->SetStreamSource(0, vb->GetD3D9(), 0, vb->GetStride()); - HRESULT hr = m_d3d9->DrawPrimitive( + d3d9::IDirect3DDevice9* device9 = m_commonD3DDevice->GetD3D9Device(); + + m_rt->InitializeOrUploadD3D9(); + if (likely(m_ds != nullptr)) + m_ds->InitializeOrUploadD3D9(); + + device9->SetFVF(vb->GetFVF()); + device9->SetStreamSource(0, vb->GetD3D9VertexBuffer(), 0, vb->GetStride()); + HRESULT hr = device9->DrawPrimitive( d3d9::D3DPRIMITIVETYPE(d3dptPrimitiveType), dwStartVertex, GetPrimitiveCount(d3dptPrimitiveType, dwNumVertices)); @@ -1068,7 +1138,7 @@ namespace dxvk { return hr; } - m_commonIntf->UpdateDrawTracking(); + UpdateSurfaceDirtyTracking(true, true, true); return hr; } @@ -1099,18 +1169,24 @@ namespace dxvk { ibIndex++; if (unlikely(ibIndex > ddrawCaps::IndexBufferCount - 1)) { Logger::err("D3D7Device::DrawIndexedPrimitiveVB: Exceeded size of largest index buffer"); - return DDERR_GENERIC; + return DDERR_UNSUPPORTED; } } + d3d9::IDirect3DDevice9* device9 = m_commonD3DDevice->GetD3D9Device(); + + m_rt->InitializeOrUploadD3D9(); + if (likely(m_ds != nullptr)) + m_ds->InitializeOrUploadD3D9(); + d3d9::IDirect3DIndexBuffer9* ib9 = m_ib9[ibIndex].ptr(); UploadIndices(ib9, lpwIndices, dwIndexCount); m_ib9_uploads[ibIndex]++; - m_d3d9->SetIndices(ib9); - m_d3d9->SetFVF(vb->GetFVF()); - m_d3d9->SetStreamSource(0, vb->GetD3D9(), 0, vb->GetStride()); - HRESULT hr = m_d3d9->DrawIndexedPrimitive( + device9->SetIndices(ib9); + device9->SetFVF(vb->GetFVF()); + device9->SetStreamSource(0, vb->GetD3D9VertexBuffer(), 0, vb->GetStride()); + HRESULT hr = device9->DrawIndexedPrimitive( d3d9::D3DPRIMITIVETYPE(d3dptPrimitiveType), dwStartVertex, 0, @@ -1123,7 +1199,7 @@ namespace dxvk { return hr; } - m_commonIntf->UpdateDrawTracking(); + UpdateSurfaceDirtyTracking(true, true, true); return hr; } @@ -1179,13 +1255,15 @@ namespace dxvk { if (unlikely(ShouldRecord())) return m_recorder->SetTexture(stage, surface); + d3d9::IDirect3DDevice9* device9 = m_commonD3DDevice->GetD3D9Device(); + HRESULT hr; // Unbinding texture stages if (surface == nullptr) { Logger::debug("D3D7Device::SetTexture: Unbiding D3D9 texture"); - hr = m_d3d9->SetTexture(stage, nullptr); + hr = device9->SetTexture(stage, nullptr); if (likely(SUCCEEDED(hr))) { if (m_textures[stage] != nullptr) { @@ -1212,51 +1290,44 @@ namespace dxvk { DDraw7Surface* surface7 = static_cast(surface); - // Only upload textures if any sort of blit/lock operation - // has been performed on them since the last SetTexture call, - // or textures which have been used on a different device, and - // need their D3D9 object to be reinitialized at this point - if (surface7->GetCommonSurface()->HasDirtyMipMaps() || - unlikely(surface7->GetD3D9Device() != m_d3d9.ptr())) { - hr = surface7->InitializeOrUploadD3D9(); - if (unlikely(FAILED(hr))) { - Logger::err("D3D7Device::SetTexture: Failed to initialize/upload D3D9 texture"); - return hr; - } + // If textures have been used on a different device, they + // will get their D3D9 object reinitialized at this point + if (unlikely(surface7->GetCommonSurface()->GetCommonD3DDevice() != m_commonD3DDevice.ptr())) + surface7->GetCommonSurface()->DirtyDDrawSurface(); - surface7->GetCommonSurface()->UnDirtyMipMaps(); - } else { - Logger::debug("D3D7Device::SetTexture: Skipping upload of texture and mip maps"); + hr = surface7->InitializeOrUploadD3D9(); + if (unlikely(FAILED(hr))) { + Logger::err("D3D7Device::SetTexture: Failed to initialize/upload D3D9 texture"); + return hr; } - // Only fast skip on D3D9 side, since we want to ensure - // color keying is applied properly even in the case - // of the same texture being set again (color key may change) + // Don't fast skip, since color key might change //if (unlikely(m_textures[stage] == surface7)) //return D3D_OK; - d3d9::IDirect3DTexture9* tex9 = surface7->GetD3D9Texture(); + d3d9::IDirect3DTexture9* tex9 = surface7->GetCommonSurface()->GetD3D9Texture(); if (likely(tex9 != nullptr)) { - hr = m_d3d9->SetTexture(stage, tex9); + hr = device9->SetTexture(stage, tex9); if (unlikely(FAILED(hr))) { Logger::warn("D3D7Device::SetTexture: Failed to bind D3D9 texture"); return hr; } if (likely(stage == 0)) { + const bool colorKeyEnable = m_commonD3DDevice->GetColorKeyEnable(); const bool validColorKey = surface7->GetCommonSurface()->HasValidColorKey(); - m_bridge->SetColorKeyState(m_colorKeyEnabled && validColorKey); - if (m_colorKeyEnabled && validColorKey) { + m_bridge->SetColorKeyState(colorKeyEnable && validColorKey); + if (colorKeyEnable && validColorKey) { DDCOLORKEY normalizedColorKey = surface7->GetCommonSurface()->GetColorKeyNormalized(); m_bridge->SetColorKey(normalizedColorKey.dwColorSpaceLowValue, normalizedColorKey.dwColorSpaceHighValue); } } } else { - d3d9::IDirect3DCubeTexture9* cube9 = surface7->GetD3D9CubeTexture(); + d3d9::IDirect3DCubeTexture9* cube9 = surface7->GetCommonSurface()->GetD3D9CubeTexture(); - hr = m_d3d9->SetTexture(stage, cube9); + hr = device9->SetTexture(stage, cube9); if (unlikely(FAILED(hr))) { Logger::warn("D3D7Device::SetTexture: Failed to bind D3D9 cube texture"); return hr; @@ -1274,10 +1345,12 @@ namespace dxvk { if (unlikely(lpdwState == nullptr)) return DDERR_INVALIDPARAMS; - // In the case of D3DTSS_ADDRESS, which is D3D7 specific, - // simply return based on D3DTSS_ADDRESSU + d3d9::IDirect3DDevice9* device9 = m_commonD3DDevice->GetD3D9Device(); + + // In the case of D3DTSS_ADDRESS, which is exclusive to D3D7 + // and D3D6, simply return based on D3DTSS_ADDRESSU if (d3dTexStageStateType == D3DTSS_ADDRESS) { - return m_d3d9->GetSamplerState(dwStage, d3d9::D3DSAMP_ADDRESSU, lpdwState); + return device9->GetSamplerState(dwStage, d3d9::D3DSAMP_ADDRESSU, lpdwState); } d3d9::D3DSAMPLERSTATETYPE stateType = ConvertSamplerStateType(d3dTexStageStateType); @@ -1288,25 +1361,27 @@ namespace dxvk { if (stateType == d3d9::D3DSAMP_MAGFILTER || stateType == d3d9::D3DSAMP_MINFILTER || stateType == d3d9::D3DSAMP_MIPFILTER) { DWORD dwStateProxy9 = 0; - HRESULT hr = m_d3d9->GetSamplerState(dwStage, stateType, &dwStateProxy9); + HRESULT hr = device9->GetSamplerState(dwStage, stateType, &dwStateProxy9); *lpdwState = DecodeD3D9TexFilterValues(d3dTexStageStateType, dwStateProxy9); return hr; } else { - return m_d3d9->GetSamplerState(dwStage, stateType, lpdwState); + return device9->GetSamplerState(dwStage, stateType, lpdwState); } } else { - return m_d3d9->GetTextureStageState(dwStage, d3d9::D3DTEXTURESTAGESTATETYPE(d3dTexStageStateType), lpdwState); + return device9->GetTextureStageState(dwStage, d3d9::D3DTEXTURESTAGESTATETYPE(d3dTexStageStateType), lpdwState); } } HRESULT STDMETHODCALLTYPE D3D7Device::SetTextureStageState(DWORD dwStage, D3DTEXTURESTAGESTATETYPE d3dTexStageStateType, DWORD dwState) { Logger::debug(">>> D3D7Device::SetTextureStageState"); - // In the case of D3DTSS_ADDRESS, which is D3D7 specific, - // we need to set up both D3DTSS_ADDRESSU and D3DTSS_ADDRESSV + d3d9::IDirect3DDevice9* device9 = m_commonD3DDevice->GetD3D9Device(); + + // In the case of D3DTSS_ADDRESS, which is exclusive to D3D7 + // and D3D6, we need to set up both D3DTSS_ADDRESSU and D3DTSS_ADDRESSV if (d3dTexStageStateType == D3DTSS_ADDRESS) { - m_d3d9->SetSamplerState(dwStage, d3d9::D3DSAMP_ADDRESSU, dwState); - return m_d3d9->SetSamplerState(dwStage, d3d9::D3DSAMP_ADDRESSV, dwState); + device9->SetSamplerState(dwStage, d3d9::D3DSAMP_ADDRESSU, dwState); + return device9->SetSamplerState(dwStage, d3d9::D3DSAMP_ADDRESSV, dwState); } d3d9::D3DSAMPLERSTATETYPE stateType = ConvertSamplerStateType(d3dTexStageStateType); @@ -1317,19 +1392,19 @@ namespace dxvk { if (stateType == d3d9::D3DSAMP_MAGFILTER || stateType == d3d9::D3DSAMP_MINFILTER || stateType == d3d9::D3DSAMP_MIPFILTER) { const DWORD dwState9 = DecodeD3D7TexFilterValues(d3dTexStageStateType, dwState); - return m_d3d9->SetSamplerState(dwStage, stateType, dwState9); + return device9->SetSamplerState(dwStage, stateType, dwState9); } else { - return m_d3d9->SetSamplerState(dwStage, stateType, dwState); + return device9->SetSamplerState(dwStage, stateType, dwState); } } else { - return m_d3d9->SetTextureStageState(dwStage, d3d9::D3DTEXTURESTAGESTATETYPE(d3dTexStageStateType), dwState); + return device9->SetTextureStageState(dwStage, d3d9::D3DTEXTURESTAGESTATETYPE(d3dTexStageStateType), dwState); } } HRESULT STDMETHODCALLTYPE D3D7Device::ValidateDevice(LPDWORD lpdwPasses) { Logger::debug(">>> D3D7Device::ValidateDevice"); - HRESULT hr = m_d3d9->ValidateDevice(lpdwPasses); + HRESULT hr = m_commonD3DDevice->GetD3D9Device()->ValidateDevice(lpdwPasses); if (unlikely(FAILED(hr))) return DDERR_INVALIDPARAMS; @@ -1338,8 +1413,6 @@ namespace dxvk { // This is a precursor of our ol' D3D8 pal CopyRects HRESULT STDMETHODCALLTYPE D3D7Device::Load(IDirectDrawSurface7 *dst_surface, POINT *dst_point, IDirectDrawSurface7 *src_surface, RECT *src_rect, DWORD flags) { - D3DDeviceLock lock = LockDevice(); - Logger::debug("<<< D3D7Device::Load: Proxy"); if (dst_surface == nullptr || src_surface == nullptr) { @@ -1382,22 +1455,17 @@ namespace dxvk { ddraw7SurfaceDst->GetCommonSurface()->SetDesc2(desc2); } - // Textures and cubemaps get uploaded during SetTexture calls - if (!ddraw7SurfaceDst->GetCommonSurface()->IsTextureOrCubeMap()) { - HRESULT hrInitDst = ddraw7SurfaceDst->InitializeOrUploadD3D9(); - if (unlikely(FAILED(hrInitDst))) { - Logger::err("D3D7Device::Load: Failed to upload D3D9 destination surface data"); - } - } else { - ddraw7SurfaceDst->GetCommonSurface()->DirtyMipMaps(); - } + ddraw7SurfaceDst->GetCommonSurface()->DirtyDDrawSurface(); return hr; } HRESULT STDMETHODCALLTYPE D3D7Device::LightEnable(DWORD dwLightIndex, BOOL bEnable) { Logger::debug(">>> D3D7Device::LightEnable"); - return m_d3d9->LightEnable(dwLightIndex, bEnable); + + m_lightsStates[dwLightIndex] = bEnable; + + return m_commonD3DDevice->GetD3D9Device()->LightEnable(dwLightIndex, bEnable); } HRESULT STDMETHODCALLTYPE D3D7Device::GetLightEnable(DWORD dwLightIndex, BOOL *pbEnable) { @@ -1406,7 +1474,7 @@ namespace dxvk { if (unlikely(pbEnable == nullptr)) return DDERR_INVALIDPARAMS; - HRESULT hr = m_d3d9->GetLightEnable(dwLightIndex, pbEnable); + HRESULT hr = m_commonD3DDevice->GetD3D9Device()->GetLightEnable(dwLightIndex, pbEnable); if (unlikely(FAILED(hr))) return DDERR_INVALIDPARAMS; @@ -1415,12 +1483,12 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE D3D7Device::SetClipPlane(DWORD dwIndex, D3DVALUE *pPlaneEquation) { Logger::debug(">>> D3D7Device::SetClipPlane"); - return m_d3d9->SetClipPlane(dwIndex, pPlaneEquation); + return m_commonD3DDevice->GetD3D9Device()->SetClipPlane(dwIndex, pPlaneEquation); } HRESULT STDMETHODCALLTYPE D3D7Device::GetClipPlane(DWORD dwIndex, D3DVALUE *pPlaneEquation) { Logger::debug(">>> D3D7Device::GetClipPlane"); - return m_d3d9->GetClipPlane(dwIndex, pPlaneEquation); + return m_commonD3DDevice->GetD3D9Device()->GetClipPlane(dwIndex, pPlaneEquation); } // Docs state: "This method returns S_FALSE on retail builds of DirectX." @@ -1430,8 +1498,9 @@ namespace dxvk { } void D3D7Device::InitializeDS() { - if (!m_rt->IsInitialized()) - m_rt->InitializeD3D9RenderTarget(); + d3d9::IDirect3DDevice9* device9 = m_commonD3DDevice->GetD3D9Device(); + + m_rt->InitializeD3D9RenderTarget(); m_ds = m_rt->GetAttachedDepthStencil(); @@ -1449,22 +1518,38 @@ namespace dxvk { m_ds->GetProxied()->GetSurfaceDesc(&descDS); Logger::debug(str::format("D3D7Device::InitializeDS: DepthStencil: ", descDS.dwWidth, "x", descDS.dwHeight)); - HRESULT hrDS9 = m_d3d9->SetDepthStencilSurface(m_ds->GetD3D9()); + HRESULT hrDS9 = device9->SetDepthStencilSurface(m_ds->GetCommonSurface()->GetD3D9Surface()); if(unlikely(FAILED(hrDS9))) { Logger::err("D3D7Device::InitializeDS: Failed to set D3D9 depth stencil"); } else { // This needs to act like an auto depth stencil of sorts, so manually enable z-buffering - m_d3d9->SetRenderState(d3d9::D3DRS_ZENABLE, d3d9::D3DZB_TRUE); + device9->SetRenderState(d3d9::D3DRS_ZENABLE, d3d9::D3DZB_TRUE); } } } else { Logger::info("D3D7Device::InitializeDS: RT has no depth stencil attached"); - m_d3d9->SetDepthStencilSurface(nullptr); + device9->SetDepthStencilSurface(nullptr); // Should be superfluous, but play it safe - m_d3d9->SetRenderState(d3d9::D3DRS_ZENABLE, d3d9::D3DZB_FALSE); + device9->SetRenderState(d3d9::D3DRS_ZENABLE, d3d9::D3DZB_FALSE); } } + void D3D7Device::UpdateSurfaceDirtyTracking(bool dirtyRenderTarget, bool dirtyDepthStencil, bool dirtyPrimarySurface) { + if(likely(dirtyRenderTarget)) + m_rt->GetCommonSurface()->DirtyD3D9Surface(); + + if (likely(dirtyPrimarySurface)) { + DDrawCommonSurface* primarySurface = m_commonIntf->GetPrimarySurface(); + // The primary surface can be bound as RT, in which case it will + // get dirtied twice, but we have no guarantees that will happen + if (likely(primarySurface != nullptr)) + primarySurface->DirtyD3D9Surface(); + } + + if (likely(dirtyDepthStencil && m_ds != nullptr)) + m_ds->GetCommonSurface()->DirtyD3D9Surface(); + } + HRESULT D3D7Device::ResetD3D9Swapchain(d3d9::D3DPRESENT_PARAMETERS* params) { Logger::info("D3D7Device::ResetD3D9Swapchain: Resetting the D3D9 swapchain"); @@ -1473,7 +1558,7 @@ namespace dxvk { Logger::err("D3D7Device::ResetD3D9Swapchain: Failed to reset the D3D9 swapchain"); } else { // TODO: Cache and reset all surfaces tied to the D3D9 backbuffers - m_rt->SetD3D9(nullptr); + m_rt->GetCommonSurface()->SetD3D9Surface(nullptr); // Note that the D3D9 depth stencil survives a swapchain reset, // so there's no need to worry about it in this case } @@ -1484,13 +1569,15 @@ namespace dxvk { inline HRESULT D3D7Device::InitializeIndexBuffers() { static constexpr DWORD Usage = D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY; + d3d9::IDirect3DDevice9* device9 = m_commonD3DDevice->GetD3D9Device(); + for (uint8_t ibIndex = 0; ibIndex < ddrawCaps::IndexBufferCount ; ibIndex++) { const UINT ibSize = ddrawCaps::IndexCount[ibIndex] * sizeof(WORD); - Logger::debug(str::format("D3D7Device::InitializeIndexBuffer: Creating index buffer, size: ", ibSize)); + Logger::debug(str::format("D3D7Device::InitializeIndexBuffer: Creating D3D9 index buffer, size: ", ibSize)); - HRESULT hr = m_d3d9->CreateIndexBuffer(ibSize, Usage, d3d9::D3DFMT_INDEX16, - d3d9::D3DPOOL_DEFAULT, &m_ib9[ibIndex], nullptr); + HRESULT hr = device9->CreateIndexBuffer(ibSize, Usage, d3d9::D3DFMT_INDEX16, + d3d9::D3DPOOL_DEFAULT, &m_ib9[ibIndex], nullptr); if (unlikely(FAILED(hr))) return hr; } diff --git a/src/ddraw/d3d7/d3d7_device.h b/src/ddraw/d3d7/d3d7_device.h index 82f36648479..f462fa65baf 100644 --- a/src/ddraw/d3d7/d3d7_device.h +++ b/src/ddraw/d3d7/d3d7_device.h @@ -7,6 +7,7 @@ #include "../ddraw_caps.h" #include "../d3d_common_device.h" +#include "../d3d_light.h" #include "../d3d_multithread.h" @@ -27,7 +28,7 @@ namespace dxvk { /** * \brief D3D7 device implementation */ - class D3D7Device final : public DDrawWrappedObject { + class D3D7Device final : public DDrawWrappedObject { friend class D3D7StateBlock; @@ -37,6 +38,7 @@ namespace dxvk { Com&& d3d7DeviceProxy, D3D7Interface* pParent, D3DDEVICEDESC7 Desc, + GUID deviceGUID, d3d9::D3DPRESENT_PARAMETERS Params9, Com&& pDevice9, DDraw7Surface* pRT, @@ -44,6 +46,8 @@ namespace dxvk { ~D3D7Device(); + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject); + HRESULT STDMETHODCALLTYPE GetCaps(D3DDEVICEDESC7 *desc); HRESULT STDMETHODCALLTYPE EnumTextureFormats(LPD3DENUMPIXELFORMATSCALLBACK cb, void *ctx); @@ -138,6 +142,8 @@ namespace dxvk { void InitializeDS(); + void UpdateSurfaceDirtyTracking(bool dirtyRenderTarget, bool dirtyDepthStencil, bool dirtyPrimarySurface); + HRESULT ResetD3D9Swapchain(d3d9::D3DPRESENT_PARAMETERS* params); D3DCommonDevice* GetCommonD3DDevice() { @@ -148,14 +154,6 @@ namespace dxvk { return m_multithread.AcquireLock(); } - d3d9::D3DPRESENT_PARAMETERS GetPresentParameters() const { - return m_params9; - } - - d3d9::D3DMULTISAMPLE_TYPE GetMultiSampleType() const { - return m_params9.MultiSampleType; - } - DDraw7Surface* GetRenderTarget() const { return m_rt.ptr(); } @@ -164,6 +162,17 @@ namespace dxvk { return m_ds.ptr(); } + std::vector GetLights() { + std::vector lights; + + for (const auto &[idx, light9] : m_lights) { + if (m_lightsStates[idx]) + lights.push_back(light9); + } + + return lights; + } + private: inline HRESULT InitializeIndexBuffers(); @@ -185,8 +194,6 @@ namespace dxvk { m_commonIntf->SetCommonD3DDevice(m_commonD3DDevice.ptr()); } - bool m_inScene = false; - static uint32_t s_deviceCount; uint32_t m_deviceCount = 0; @@ -198,30 +205,21 @@ namespace dxvk { D3DMultithread m_multithread; - d3d9::D3DPRESENT_PARAMETERS m_params9; - D3DDEVICEDESC7 m_desc; + Com m_rt; Com m_ds; std::array, ddrawCaps::TextureStageCount> m_textures; + std::unordered_map m_lights; + std::unordered_map m_lightsStates; + D3D7StateBlock* m_recorder = nullptr; DWORD m_recorderHandle = 0; DWORD m_handle = 0; std::unordered_map m_stateBlocks; - // Value of D3DRENDERSTATE_COLORKEYENABLE - DWORD m_colorKeyEnabled = 0; - // Value of D3DRENDERSTATE_COLORKEYBLENDENABLE - DWORD m_colorKeyBlendEnabled = 0; - // Value of D3DRENDERSTATE_ANTIALIAS - DWORD m_antialias = D3DANTIALIAS_NONE; - // Value of D3DRENDERSTATE_LINEPATTERN - D3DLINEPATTERN m_linePattern = { }; - // Value of D3DCLIPSTATUS - D3DCLIPSTATUS m_clipStatus = { }; - // Common index buffers used for indexed draws, split up into five sizes: // XS, S, M, L and XL, corresponding to 0.5 kb, 2 kb, 8 kb, 32 kb and 128 kb std::array, ddrawCaps::IndexBufferCount> m_ib9; diff --git a/src/ddraw/d3d7/d3d7_interface.cpp b/src/ddraw/d3d7/d3d7_interface.cpp index cfd120c742c..fe1bbb44469 100644 --- a/src/ddraw/d3d7/d3d7_interface.cpp +++ b/src/ddraw/d3d7/d3d7_interface.cpp @@ -17,16 +17,22 @@ namespace dxvk { D3DCommonInterface* commonD3DIntf, Com&& d3d7IntfProxy, IUnknown* pParent) - : DDrawWrappedObject(pParent, std::move(d3d7IntfProxy), std::move(d3d9::Direct3DCreate9(D3D_SDK_VERSION))) + : DDrawWrappedObject(pParent, std::move(d3d7IntfProxy)) , m_commonIntf ( commonIntf ) , m_commonD3DIntf ( commonD3DIntf ) { - // Get the bridge interface to D3D9. - if (unlikely(FAILED(m_d3d9->QueryInterface(__uuidof(IDxvkD3D8InterfaceBridge), reinterpret_cast(&m_bridge))))) { - throw DxvkError("D3D7Interface: ERROR! Failed to get D3D9 Bridge. d3d9.dll might not be DXVK!"); + if (m_commonD3DIntf == nullptr) { + m_commonD3DIntf = new D3DCommonInterface(); + + Com d3d9Intf = d3d9::Direct3DCreate9(D3D_SDK_VERSION); + m_commonD3DIntf->SetD3D9Interface(std::move(d3d9Intf)); } - if (m_commonD3DIntf == nullptr) - m_commonD3DIntf = new D3DCommonInterface(); + d3d9::IDirect3D9* d3d9Intf = m_commonD3DIntf->GetD3D9Interface(); + + // Get the bridge interface to D3D9 + if (unlikely(FAILED(d3d9Intf->QueryInterface(__uuidof(IDxvkD3D8InterfaceBridge), reinterpret_cast(&m_bridge))))) { + throw DxvkError("D3D7Interface: ERROR! Failed to get D3D9 Bridge. d3d9.dll might not be DXVK!"); + } m_commonD3DIntf->SetD3D7Interface(this); @@ -115,30 +121,47 @@ namespace dxvk { // Note: The enumeration order seems to matter for some applications, // such as (The) Summoner, so always report RGB first, then HAL, then T&L HAL + HRESULT hr; + // Software emulation, this is expected to be exposed D3DDEVICEDESC7 desc7RGB = GetD3D7Caps(IID_IDirect3DRGBDevice, d3dOptions); - static char deviceDescRGB[100] = "D7VK RGB"; - static char deviceNameRGB[100] = "D7VK RGB"; - - HRESULT hr = cb(&deviceDescRGB[0], &deviceNameRGB[0], &desc7RGB, ctx); + if (likely(!d3dOptions->legacyDeviceNames)) { + static char deviceDescRGB[100] = "D7VK RGB"; + static char deviceNameRGB[100] = "D7VK RGB"; + hr = cb(&deviceDescRGB[0], &deviceNameRGB[0], &desc7RGB, ctx); + } else { + static char legacyDeviceDescRGB[100] = "RGB Emulation"; + static char legacyDeviceNameRGB[100] = "RGB Emulation"; + hr = cb(&legacyDeviceDescRGB[0], &legacyDeviceNameRGB[0], &desc7RGB, ctx); + } if (hr != D3DENUMRET_OK) return D3D_OK; // Hardware acceleration (no T&L) D3DDEVICEDESC7 desc7HAL = GetD3D7Caps(IID_IDirect3DHALDevice, d3dOptions); - static char deviceDescHAL[100] = "D7VK HAL"; - static char deviceNameHAL[100] = "D7VK HAL"; - - hr = cb(&deviceDescHAL[0], &deviceNameHAL[0], &desc7HAL, ctx); + if (likely(!d3dOptions->legacyDeviceNames)) { + static char deviceDescHAL[100] = "D7VK HAL"; + static char deviceNameHAL[100] = "D7VK HAL"; + hr = cb(&deviceDescHAL[0], &deviceNameHAL[0], &desc7HAL, ctx); + } else { + static char legacyDeviceDescHAL[100] = "Direct3D HAL"; + static char legacyDeviceNameHAL[100] = "Direct3D HAL"; + hr = cb(&legacyDeviceDescHAL[0], &legacyDeviceNameHAL[0], &desc7HAL, ctx); + } if (hr != D3DENUMRET_OK) return D3D_OK; // Hardware acceleration with T&L D3DDEVICEDESC7 desc7TNL = GetD3D7Caps(IID_IDirect3DTnLHalDevice, d3dOptions); - static char deviceDescTNL[100] = "D7VK T&L HAL"; - static char deviceNameTNL[100] = "D7VK T&L HAL"; - - hr = cb(&deviceDescTNL[0], &deviceNameTNL[0], &desc7TNL, ctx); + if (likely(!d3dOptions->legacyDeviceNames)) { + static char deviceDescTNL[100] = "D7VK T&L HAL"; + static char deviceNameTNL[100] = "D7VK T&L HAL"; + hr = cb(&deviceDescTNL[0], &deviceNameTNL[0], &desc7TNL, ctx); + } else { + static char legacyDeviceDescTNL[100] = "Direct3D T&L HAL"; + static char legacyDeviceNameTNL[100] = "Direct3D T&L HAL"; + hr = cb(&legacyDeviceDescTNL[0], &legacyDeviceNameTNL[0], &desc7TNL, ctx); + } if (hr != D3DENUMRET_OK) return D3D_OK; @@ -167,7 +190,7 @@ namespace dxvk { if (rclsid == IID_IDirect3DTnLHalDevice) { Logger::info("D3D7Interface::CreateDevice: Creating an IID_IDirect3DTnLHalDevice device"); deviceCreationFlags9 = D3DCREATE_HARDWARE_VERTEXPROCESSING; - } else if (rclsid == IID_IDirect3DHALDevice) { + } else if (rclsid == IID_IDirect3DHALDevice || rclsid == IID_WineD3DDevice) { Logger::info("D3D7Interface::CreateDevice: Creating an IID_IDirect3DHALDevice device"); deviceCreationFlags9 = D3DCREATE_MIXED_VERTEXPROCESSING; } else if (rclsid == IID_IDirect3DRGBDevice) { @@ -196,7 +219,7 @@ namespace dxvk { } Com d3d7DeviceProxy; - HRESULT hr = m_proxy->CreateDevice(rclsidOverride, rt7->GetProxied(), &d3d7DeviceProxy); + HRESULT hr = m_proxy->CreateDevice(rclsidOverride, rt7->GetShadowOrProxied(), &d3d7DeviceProxy); if (unlikely(FAILED(hr))) { Logger::warn("D3D7Interface::CreateDevice: Failed to create the proxy device"); return hr; @@ -209,8 +232,7 @@ namespace dxvk { DWORD backBufferWidth = desc.dwWidth; DWORD BackBufferHeight = desc.dwHeight; - if (likely(!d3dOptions->forceProxiedPresent && - d3dOptions->backBufferResize)) { + if (likely(d3dOptions->backBufferResize)) { const bool exclusiveMode = m_commonIntf->GetCooperativeLevel() & DDSCL_EXCLUSIVE; // Ignore any mode size dimensions when in windowed present mode @@ -227,29 +249,7 @@ namespace dxvk { } } - d3d9::D3DFORMAT backBufferFormat = ConvertFormat(desc.ddpfPixelFormat); - - // Determine the supported AA sample count by querying the D3D9 interface - d3d9::D3DMULTISAMPLE_TYPE multiSampleType = d3d9::D3DMULTISAMPLE_NONE; - if (likely(d3dOptions->emulateFSAA != FSAAEmulation::Disabled)) { - HRESULT hr4S = m_d3d9->CheckDeviceMultiSampleType(0, d3d9::D3DDEVTYPE_HAL, backBufferFormat, - TRUE, d3d9::D3DMULTISAMPLE_4_SAMPLES, NULL); - if (unlikely(FAILED(hr4S))) { - HRESULT hr2S = m_d3d9->CheckDeviceMultiSampleType(0, d3d9::D3DDEVTYPE_HAL, backBufferFormat, - TRUE, d3d9::D3DMULTISAMPLE_2_SAMPLES, NULL); - if (unlikely(FAILED(hr2S))) { - Logger::warn("D3D7Interface::CreateDevice: No MSAA support has been detected"); - } else { - Logger::info("D3D7Interface::CreateDevice: Using 2x MSAA for FSAA emulation"); - multiSampleType = d3d9::D3DMULTISAMPLE_2_SAMPLES; - } - } else { - Logger::info("D3D7Interface::CreateDevice: Using 4x MSAA for FSAA emulation"); - multiSampleType = d3d9::D3DMULTISAMPLE_4_SAMPLES; - } - } else { - Logger::info("D3D7Interface::CreateDevice: FSAA emulation is disabled"); - } + const d3d9::D3DFORMAT backBufferFormat = ConvertFormat(desc.ddpfPixelFormat); const DWORD cooperativeLevel = m_commonIntf->GetCooperativeLevel(); @@ -267,22 +267,17 @@ namespace dxvk { Logger::info(str::format("D3D7Interface::CreateDevice: Back buffer size: ", desc.dwWidth, "x", desc.dwHeight)); - DWORD backBufferCount = 0; - if (likely(!d3dOptions->forceSingleBackBuffer)) { - IDirectDrawSurface7* backBuffer = rt7->GetProxied(); - while (backBuffer != nullptr) { - IDirectDrawSurface7* parentSurface = backBuffer; - backBuffer = nullptr; - parentSurface->EnumAttachedSurfaces(&backBuffer, ListBackBufferSurfaces7Callback); - backBufferCount++; - // the swapchain will eventually return to its origin - if (backBuffer == rt7->GetProxied()) - break; - } - } + const DWORD backBufferCount = DetermineBackBufferCount(rt7->GetProxied()); // Consider the front buffer as well when reporting the overall count Logger::info(str::format("D3D7Interface::CreateDevice: Back buffer count: ", backBufferCount + 1)); + Com d3d9Intf = m_commonD3DIntf->GetD3D9Interface(); + + // Determine the supported AA sample count by querying the D3D9 interface + const d3d9::D3DMULTISAMPLE_TYPE multiSampleType = d3dOptions->emulateFSAA != FSAAEmulation::Disabled ? + GetSupportedMultiSampleType(d3d9Intf.ptr(), backBufferFormat) : + d3d9::D3DMULTISAMPLE_NONE; + // Always appears to be enabled when running in non-exclusive mode const bool vBlankStatus = m_commonIntf->GetWaitForVBlank(); @@ -303,7 +298,7 @@ namespace dxvk { params.PresentationInterval = vBlankStatus ? D3DPRESENT_INTERVAL_DEFAULT : D3DPRESENT_INTERVAL_IMMEDIATE; Com device9; - hr = m_d3d9->CreateDevice( + hr = d3d9Intf->CreateDevice( D3DADAPTER_DEFAULT, d3d9::D3DDEVTYPE_HAL, hWnd, @@ -320,8 +315,8 @@ namespace dxvk { D3DDEVICEDESC7 desc7 = GetD3D7Caps(rclsidOverride, d3dOptions); try{ - Com device7 = new D3D7Device(nullptr, std::move(d3d7DeviceProxy), this, - desc7, params, std::move(device9), + Com device7 = new D3D7Device(nullptr, std::move(d3d7DeviceProxy), this, desc7, + rclsidOverride, params, std::move(device9), rt7.ptr(), deviceCreationFlags9); // Set the common device on the common interface @@ -346,25 +341,15 @@ namespace dxvk { InitReturnPtr(ppVertexBuffer); - Com vertexBuffer7; - // We don't really need a proxy buffer any longer - /*HRESULT hr = m_proxy->CreateVertexBuffer(desc, &vertexBuffer7, usage); - if (unlikely(FAILED(hr))) { - Logger::warn("D3D7Interface::CreateVertexBuffer: Failed to create proxy vertex buffer"); - return hr; - }*/ - - // We need to delay the D3D9 vertex buffer creation as long as possible, to ensure - // that (ideally) we actually have a valid D3D7 device in place when that happens - *ppVertexBuffer = ref(new D3D7VertexBuffer(std::move(vertexBuffer7), nullptr, this, *desc)); + *ppVertexBuffer = ref(new D3D7VertexBuffer(this, *desc)); return D3D_OK; } - HRESULT STDMETHODCALLTYPE D3D7Interface::EnumZBufferFormats(REFCLSID riidDevice, LPD3DENUMPIXELFORMATSCALLBACK cb, LPVOID ctx) { + HRESULT STDMETHODCALLTYPE D3D7Interface::EnumZBufferFormats(REFCLSID riidDevice, LPD3DENUMPIXELFORMATSCALLBACK lpEnumCallback, LPVOID lpContext) { Logger::debug(">>> D3D7Interface::EnumZBufferFormats"); - if (unlikely(cb == nullptr)) + if (unlikely(lpEnumCallback == nullptr)) return DDERR_INVALIDPARAMS; const D3DOptions* d3dOptions = m_commonIntf->GetOptions(); @@ -376,20 +361,33 @@ namespace dxvk { if (likely(d3dOptions->supportD16)) { depthFormat = GetZBufferFormat(d3d9::D3DFMT_D16); - hr = cb(&depthFormat, ctx); + hr = lpEnumCallback(&depthFormat, lpContext); if (unlikely(hr != D3DENUMRET_OK)) return D3D_OK; } + // Apparently some games expect D3DFMT_D24X8 to have a 24-bit + // dwZBufferBitDepth, so we have to enumerate both variants. + // According to Wine tests, Windows Vista and newer also enumerate both. depthFormat = GetZBufferFormat(d3d9::D3DFMT_D24X8); - hr = cb(&depthFormat, ctx); + depthFormat.dwZBufferBitDepth = 24; + hr = lpEnumCallback(&depthFormat, lpContext); if (unlikely(hr != D3DENUMRET_OK)) return D3D_OK; - depthFormat = GetZBufferFormat(d3d9::D3DFMT_D24S8); - hr = cb(&depthFormat, ctx); - if (unlikely(hr != D3DENUMRET_OK)) - return D3D_OK; + // Expendable relies on having only the 24-bit dwZBufferBitDepth variant + // of D3DFMT_D24X8 enumerated in order to have working projected shadows + if (likely(d3dOptions->support32BitDepth)) { + depthFormat = GetZBufferFormat(d3d9::D3DFMT_D24X8); + hr = lpEnumCallback(&depthFormat, lpContext); + if (unlikely(hr != D3DENUMRET_OK)) + return D3D_OK; + + depthFormat = GetZBufferFormat(d3d9::D3DFMT_D24S8); + hr = lpEnumCallback(&depthFormat, lpContext); + if (unlikely(hr != D3DENUMRET_OK)) + return D3D_OK; + } return D3D_OK; } @@ -416,4 +414,34 @@ namespace dxvk { return D3D_OK; } + inline DWORD D3D7Interface::DetermineBackBufferCount(IDirectDrawSurface7* renderTarget) { + DWORD backBufferCount = 0; + + const D3DOptions* d3dOptions = m_commonIntf->GetOptions(); + + if (likely(!d3dOptions->forceSingleBackBuffer && !d3dOptions->forceLegacyPresent)) { + IDirectDrawSurface7* backBuffer = renderTarget; + HRESULT hr; + + while (backBuffer != nullptr) { + IDirectDrawSurface7* parentSurface = backBuffer; + backBuffer = nullptr; + + hr = parentSurface->EnumAttachedSurfaces(&backBuffer, ListBackBufferSurfaces7Callback); + if (unlikely(FAILED(hr))) { + Logger::warn("D3D7Interface::DetermineBackBufferCount: Unable to enumerate attached surfaces"); + break; + } + + backBufferCount++; + + // the swapchain will eventually return to its origin + if (backBuffer == renderTarget) + break; + } + } + + return backBufferCount; + } + } \ No newline at end of file diff --git a/src/ddraw/d3d7/d3d7_interface.h b/src/ddraw/d3d7/d3d7_interface.h index 90b095bbdaa..cfca38f16c0 100644 --- a/src/ddraw/d3d7/d3d7_interface.h +++ b/src/ddraw/d3d7/d3d7_interface.h @@ -15,7 +15,7 @@ namespace dxvk { /** * \brief D3D7 interface implementation */ - class D3D7Interface final : public DDrawWrappedObject { + class D3D7Interface final : public DDrawWrappedObject { public: D3D7Interface( @@ -38,7 +38,7 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE CreateVertexBuffer(D3DVERTEXBUFFERDESC *desc, IDirect3DVertexBuffer7 **ppVertexBuffer, DWORD usage); - HRESULT STDMETHODCALLTYPE EnumZBufferFormats(REFCLSID device_iid, LPD3DENUMPIXELFORMATSCALLBACK cb, LPVOID ctx); + HRESULT STDMETHODCALLTYPE EnumZBufferFormats(REFCLSID riidDevice, LPD3DENUMPIXELFORMATSCALLBACK lpEnumCallback, LPVOID lpContext); HRESULT STDMETHODCALLTYPE EvictManagedTextures(); @@ -52,6 +52,8 @@ namespace dxvk { private: + inline DWORD DetermineBackBufferCount(IDirectDrawSurface7* renderTarget); + static uint32_t s_intfCount; uint32_t m_intfCount = 0; diff --git a/src/ddraw/d3d_common_device.cpp b/src/ddraw/d3d_common_device.cpp index b07326e9f5e..e077f473d8a 100644 --- a/src/ddraw/d3d_common_device.cpp +++ b/src/ddraw/d3d_common_device.cpp @@ -1,5 +1,7 @@ #include "d3d_common_device.h" +#include "ddraw_common_surface.h" + #include "d3d_common_interface.h" #include "ddraw/ddraw_surface.h" @@ -15,10 +17,12 @@ namespace dxvk { D3DCommonDevice::D3DCommonDevice( DDrawCommonInterface* commonIntf, - DWORD creationFlags9, - uint32_t totalMemory) + GUID deviceGUID, + d3d9::D3DPRESENT_PARAMETERS params9, + DWORD creationFlags9) : m_commonIntf ( commonIntf ) - , m_totalMemory ( totalMemory ) + , m_deviceGUID ( deviceGUID ) + , m_params9 ( params9 ) , m_creationFlags9 ( creationFlags9 ) { } @@ -27,20 +31,6 @@ namespace dxvk { m_commonIntf->SetCommonD3DDevice(nullptr); } - d3d9::IDirect3DDevice9* D3DCommonDevice::GetD3D9Device() { - if (m_device7 != nullptr) { - return m_device7->GetD3D9(); - } else if (m_device6 != nullptr) { - return m_device6->GetD3D9(); - } else if (m_device5 != nullptr) { - return m_device5->GetD3D9(); - } else if (m_device3 != nullptr) { - return m_device3->GetD3D9(); - } - - return nullptr; - } - D3DCommonInterface* D3DCommonDevice::GetCommonD3DInterface() const { if (m_device7 != nullptr) { return m_device7->GetParent() != nullptr ? m_device7->GetParent()->GetCommonD3DInterface() : nullptr; @@ -56,34 +46,6 @@ namespace dxvk { return nullptr; } - d3d9::D3DMULTISAMPLE_TYPE D3DCommonDevice::GetMultiSampleType() { - if (m_device7 != nullptr) { - return m_device7->GetMultiSampleType(); - } else if (m_device6 != nullptr) { - return m_device6->GetMultiSampleType(); - } else if (m_device5 != nullptr) { - return m_device5->GetMultiSampleType(); - } else if (m_device3 != nullptr) { - return m_device3->GetMultiSampleType(); - } - - return d3d9::D3DMULTISAMPLE_NONE; - } - - d3d9::D3DPRESENT_PARAMETERS D3DCommonDevice::GetPresentParameters() { - if (m_device7 != nullptr) { - return m_device7->GetPresentParameters(); - } else if (m_device6 != nullptr) { - return m_device6->GetPresentParameters(); - } else if (m_device5 != nullptr) { - return m_device5->GetPresentParameters(); - } else if (m_device3 != nullptr) { - return m_device3->GetPresentParameters(); - } - - return d3d9::D3DPRESENT_PARAMETERS(); - } - HRESULT D3DCommonDevice::ResetD3D9Swapchain(d3d9::D3DPRESENT_PARAMETERS* params) { if (m_device7 != nullptr) { return m_device7->ResetD3D9Swapchain(params); @@ -92,67 +54,27 @@ namespace dxvk { } // D3D5/3 has no way of disabling/re-enabling VSync - return DDERR_GENERIC; - } - - bool D3DCommonDevice::IsCurrentRenderTarget(DDrawSurface* surface) const { - return m_device5 != nullptr ? m_device5->GetRenderTarget() == surface : - m_device3 != nullptr ? m_device3->GetRenderTarget() == surface : false; - } - - bool D3DCommonDevice::IsCurrentRenderTarget(DDraw4Surface* surface) const { - return m_device6 != nullptr ? m_device6->GetRenderTarget() == surface : false; - } - - bool D3DCommonDevice::IsCurrentRenderTarget(DDraw7Surface* surface) const { - return m_device7 != nullptr ? m_device7->GetRenderTarget() == surface : false; - } - - bool D3DCommonDevice::IsCurrentD3D9RenderTarget(d3d9::IDirect3DSurface9* surface) const { - if (unlikely(surface == nullptr)) - return false; - - if (m_device7 != nullptr) { - return surface == m_device7->GetRenderTarget()->GetD3D9(); - } else if (m_device6 != nullptr) { - return surface == m_device6->GetRenderTarget()->GetD3D9(); - } else if (m_device5 != nullptr) { - return surface == m_device5->GetRenderTarget()->GetD3D9(); - } else if (m_device3 != nullptr) { - return surface == m_device3->GetRenderTarget()->GetD3D9(); - } - - return false; + return DDERR_UNSUPPORTED; } - bool D3DCommonDevice::IsCurrentDepthStencil(DDrawSurface* surface) const { - return m_device5 != nullptr ? m_device5->GetDepthStencil() == surface : - m_device3 != nullptr ? m_device3->GetDepthStencil() == surface : false; + DDrawSurface* D3DCommonDevice::GetCurrentRenderTarget() const { + return m_device5 != nullptr ? m_device5->GetRenderTarget() : + m_device3 != nullptr ? m_device3->GetRenderTarget() : nullptr; } - bool D3DCommonDevice::IsCurrentDepthStencil(DDraw4Surface* surface) const { - return m_device6 != nullptr ? m_device6->GetDepthStencil() == surface : false; + DDraw4Surface* D3DCommonDevice::GetCurrentRenderTarget4() const { + return m_device6 != nullptr ? m_device6->GetRenderTarget() : nullptr; } - bool D3DCommonDevice::IsCurrentDepthStencil(DDraw7Surface* surface) const { - return m_device7 != nullptr ? m_device7->GetDepthStencil() == surface : false; + DDraw7Surface* D3DCommonDevice::GetCurrentRenderTarget7() const { + return m_device7 != nullptr ? m_device7->GetRenderTarget() : nullptr; } - bool D3DCommonDevice::IsCurrentD3D9DepthStencil(d3d9::IDirect3DSurface9* surface) const { - if (unlikely(surface == nullptr)) - return false; - - if (m_device7 != nullptr) { - return surface == m_device7->GetDepthStencil()->GetD3D9(); - } else if (m_device6 != nullptr) { - return surface == m_device6->GetDepthStencil()->GetD3D9(); - } else if (m_device5 != nullptr) { - return surface == m_device5->GetDepthStencil()->GetD3D9(); - } else if (m_device3 != nullptr) { - return surface == m_device3->GetDepthStencil()->GetD3D9(); - } - - return false; + bool D3DCommonDevice::IsCurrentRenderTarget(DDrawCommonSurface* commonSurface) const { + return m_device7 != nullptr ? m_device7->GetRenderTarget()->GetCommonSurface() == commonSurface : + m_device6 != nullptr ? m_device6->GetRenderTarget()->GetCommonSurface() == commonSurface : + m_device5 != nullptr ? m_device5->GetRenderTarget()->GetCommonSurface() == commonSurface : + m_device3 != nullptr ? m_device3->GetRenderTarget()->GetCommonSurface() == commonSurface : false; } } \ No newline at end of file diff --git a/src/ddraw/d3d_common_device.h b/src/ddraw/d3d_common_device.h index 03dd4f128ed..826a2502c9c 100644 --- a/src/ddraw/d3d_common_device.h +++ b/src/ddraw/d3d_common_device.h @@ -6,6 +6,7 @@ namespace dxvk { + class DDrawCommonSurface; class D3DCommonInterface; class DDrawCommonInterface; @@ -24,8 +25,9 @@ namespace dxvk { D3DCommonDevice( DDrawCommonInterface* commonIntf, - DWORD creationFlags9, - uint32_t totalMemory); + GUID deviceGUID, + d3d9::D3DPRESENT_PARAMETERS params9, + DWORD creationFlags9); ~D3DCommonDevice(); @@ -34,40 +36,118 @@ namespace dxvk { return S_OK; } - d3d9::IDirect3DDevice9* GetD3D9Device(); - D3DCommonInterface* GetCommonD3DInterface() const; - d3d9::D3DMULTISAMPLE_TYPE GetMultiSampleType(); + HRESULT ResetD3D9Swapchain(d3d9::D3DPRESENT_PARAMETERS* params); - d3d9::D3DPRESENT_PARAMETERS GetPresentParameters(); + DDrawSurface* GetCurrentRenderTarget() const; - HRESULT ResetD3D9Swapchain(d3d9::D3DPRESENT_PARAMETERS* params); + DDraw4Surface* GetCurrentRenderTarget4() const; - bool IsCurrentRenderTarget(DDrawSurface* surface) const; + DDraw7Surface* GetCurrentRenderTarget7() const; - bool IsCurrentRenderTarget(DDraw4Surface* surface) const; + bool IsCurrentRenderTarget(DDrawCommonSurface* commonSurface) const; - bool IsCurrentRenderTarget(DDraw7Surface* surface) const; + void SetInScene(bool inScene) { + m_inScene = inScene; + } - bool IsCurrentD3D9RenderTarget(d3d9::IDirect3DSurface9* surface) const; + bool IsInScene() const { + return m_inScene; + } - bool IsCurrentDepthStencil(DDrawSurface* surface) const; + DDrawCommonInterface* GetCommonInterface() const { + return m_commonIntf; + } - bool IsCurrentDepthStencil(DDraw4Surface* surface) const; + void SetTotalTextureMemory(uint32_t totalMemory) { + m_totalMemory = totalMemory; + } - bool IsCurrentDepthStencil(DDraw7Surface* surface) const; + uint32_t GetTotalTextureMemory() const { + return m_totalMemory; + } - bool IsCurrentD3D9DepthStencil(d3d9::IDirect3DSurface9* surface) const; + GUID GetDeviceGUID() const { + return m_deviceGUID; + } - DDrawCommonInterface* GetCommonInterface() const { - return m_commonIntf; + d3d9::D3DPRESENT_PARAMETERS GetPresentParameters() const { + return m_params9; + } + + d3d9::D3DMULTISAMPLE_TYPE GetMultiSampleType() const { + return m_params9.MultiSampleType; } DWORD GetD3D9CreationFlags() const { return m_creationFlags9; } + D3DMATERIALHANDLE GetCurrentMaterialHandle() const { + return m_materialHandle; + } + + void SetCurrentMaterialHandle(D3DMATERIALHANDLE materialHandle) { + m_materialHandle = materialHandle; + } + + D3DTEXTUREHANDLE GetCurrentTextureHandle() const { + return m_textureHandle; + } + + void SetCurrentTextureHandle(D3DTEXTUREHANDLE textureHandle) { + m_textureHandle = textureHandle; + } + + DWORD GetColorKeyEnable() const { + return m_colorKeyEnable; + } + + void SetColorKeyEnable(DWORD colorKeyEnable) { + m_colorKeyEnable = colorKeyEnable; + } + + DWORD GetColorKeyBlendEnable() const { + return m_colorKeyBlendEnable; + } + + void SetColorKeyBlendEnable(DWORD colorKeyBlendEnable) { + m_colorKeyBlendEnable = colorKeyBlendEnable; + } + + DWORD GetAntialias() const { + return m_antialias; + } + + void SetAntialias(DWORD antialias) { + m_antialias = antialias; + } + + D3DLINEPATTERN GetLinePattern() const { + return m_linePattern; + } + + void SetLinePattern(D3DLINEPATTERN linePattern) { + m_linePattern = linePattern; + } + + DWORD GetTextureMapBlend() const { + return m_textureMapBlend; + } + + void SetTextureMapBlend(DWORD textureMapBlend) { + m_textureMapBlend = textureMapBlend; + } + + void SetD3D9Device(Com&& device9) { + m_device9 = device9; + } + + d3d9::IDirect3DDevice9* GetD3D9Device() const { + return m_device9.ptr(); + } + void SetD3D7Device(D3D7Device* device7) { m_device7 = device7; } @@ -108,27 +188,43 @@ namespace dxvk { return m_origin; } - uint32_t GetTotalTextureMemory() const { - return m_totalMemory; - } - private: - DDrawCommonInterface* m_commonIntf = nullptr; + bool m_inScene = false; + + DDrawCommonInterface* m_commonIntf = nullptr; + + uint32_t m_totalMemory = 0; + + GUID m_deviceGUID; + d3d9::D3DPRESENT_PARAMETERS m_params9; + DWORD m_creationFlags9 = 0; + + D3DMATERIALHANDLE m_materialHandle = 0; + D3DTEXTUREHANDLE m_textureHandle = 0; - uint32_t m_totalMemory = 0; + // Value of D3DRENDERSTATE_COLORKEYENABLE + DWORD m_colorKeyEnable = 0; + // Value of D3DRENDERSTATE_COLORKEYBLENDENABLE + DWORD m_colorKeyBlendEnable = 0; + // Value of D3DRENDERSTATE_ANTIALIAS + DWORD m_antialias = D3DANTIALIAS_NONE; + // Value of D3DRENDERSTATE_LINEPATTERN + D3DLINEPATTERN m_linePattern = { }; + // Value of D3DRENDERSTATE_TEXTUREMAPBLEND + DWORD m_textureMapBlend = D3DTBLEND_MODULATE; - DWORD m_creationFlags9 = 0; + Com m_device9; // Track all possible last used D3D devices - D3D7Device* m_device7 = nullptr; - D3D6Device* m_device6 = nullptr; - D3D5Device* m_device5 = nullptr; - D3D3Device* m_device3 = nullptr; + D3D7Device* m_device7 = nullptr; + D3D6Device* m_device6 = nullptr; + D3D5Device* m_device5 = nullptr; + D3D3Device* m_device3 = nullptr; // Track the origin device, as in the device // that gets created through a CreateDevice call - IUnknown* m_origin = nullptr; + IUnknown* m_origin = nullptr; }; diff --git a/src/ddraw/d3d_common_interface.h b/src/ddraw/d3d_common_interface.h index 653244ebe2e..6f36b7aabf8 100644 --- a/src/ddraw/d3d_common_interface.h +++ b/src/ddraw/d3d_common_interface.h @@ -38,6 +38,14 @@ namespace dxvk { return ++m_materialHandle; } + void SetD3D9Interface(Com&& d3d9Intf) { + m_d3d9Intf = d3d9Intf; + } + + d3d9::IDirect3D9* GetD3D9Interface() const { + return m_d3d9Intf.ptr(); + } + void SetD3D7Interface(D3D7Interface* d3d7Intf) { m_d3d7Intf = d3d7Intf; } @@ -72,11 +80,13 @@ namespace dxvk { private: + Com m_d3d9Intf = nullptr; + // Track all possible last used D3D interfaces - D3D7Interface* m_d3d7Intf = nullptr; - D3D6Interface* m_d3d6Intf = nullptr; - D3D5Interface* m_d3d5Intf = nullptr; - D3D3Interface* m_d3d3Intf = nullptr; + D3D7Interface* m_d3d7Intf = nullptr; + D3D6Interface* m_d3d6Intf = nullptr; + D3D5Interface* m_d3d5Intf = nullptr; + D3D3Interface* m_d3d3Intf = nullptr; std::atomic m_materialHandle = 0; std::unordered_map m_materials; diff --git a/src/ddraw/d3d_common_viewport.cpp b/src/ddraw/d3d_common_viewport.cpp index f94050ff12d..511fc852792 100644 --- a/src/ddraw/d3d_common_viewport.cpp +++ b/src/ddraw/d3d_common_viewport.cpp @@ -36,26 +36,26 @@ namespace dxvk { return nullptr; } - void D3DCommonViewport::EnableLegacyLights(bool isD3DLight2) { + d3d9::IDirect3DDevice9* D3DCommonViewport::GetD3D9Device() { if (m_device6 != nullptr) { - return m_device6->EnableLegacyLights(isD3DLight2); + return m_device6->GetCommonD3DDevice()->GetD3D9Device(); } else if (m_device5 != nullptr) { - return m_device5->EnableLegacyLights(isD3DLight2); + return m_device5->GetCommonD3DDevice()->GetD3D9Device(); } else if (m_device3 != nullptr) { - return m_device3->EnableLegacyLights(isD3DLight2); + return m_device3->GetCommonD3DDevice()->GetD3D9Device(); } + + return nullptr; } - d3d9::IDirect3DDevice9* D3DCommonViewport::GetD3D9Device() { + void D3DCommonViewport::UpdateSurfaceDirtyTracking(bool dirtyRenderTarget, bool dirtyDepthStencil, bool dirtyPrimarySurface) { if (m_device6 != nullptr) { - return m_device6->GetD3D9(); + m_device6->UpdateSurfaceDirtyTracking(dirtyRenderTarget, dirtyDepthStencil, dirtyPrimarySurface); } else if (m_device5 != nullptr) { - return m_device5->GetD3D9(); + m_device5->UpdateSurfaceDirtyTracking(dirtyRenderTarget, dirtyDepthStencil, dirtyPrimarySurface); } else if (m_device3 != nullptr) { - return m_device3->GetD3D9(); + m_device3->UpdateSurfaceDirtyTracking(dirtyRenderTarget, dirtyDepthStencil, dirtyPrimarySurface); } - - return nullptr; } HRESULT D3DCommonViewport::TransformVertices(DWORD vertex_count, D3DTRANSFORMDATA *data, DWORD flags, DWORD *offscreen) { @@ -154,7 +154,9 @@ namespace dxvk { } } - out.rhw = (h.w != 0.0f) ? (1.0f / h.w) : 0.0f; + // Hidden & Dangerous (D3D6) relies on NAN/INF output + // in ProcessVertices, so do the same here just in case + out.rhw = 1.0f / h.w; out.sx = m_viewport9.X + static_cast(m_viewport9.Width) * 0.5 * (h.x * out.rhw + 1.0f); out.sy = m_viewport9.Y + static_cast(m_viewport9.Height) * 0.5 * (1.0f - h.y * out.rhw); out.sz = m_viewport9.MinZ + h.z * out.rhw * (m_viewport9.MaxZ - m_viewport9.MinZ); diff --git a/src/ddraw/d3d_common_viewport.h b/src/ddraw/d3d_common_viewport.h index 11711e3fc43..218c054020e 100644 --- a/src/ddraw/d3d_common_viewport.h +++ b/src/ddraw/d3d_common_viewport.h @@ -38,10 +38,10 @@ namespace dxvk { D3D3Viewport* GetCurrentD3D3Viewport(); - void EnableLegacyLights(bool isD3DLight2); - d3d9::IDirect3DDevice9* GetD3D9Device(); + void UpdateSurfaceDirtyTracking(bool dirtyRenderTarget, bool dirtyDepthStencil, bool dirtyPrimarySurface); + HRESULT TransformVertices(DWORD vertex_count, D3DTRANSFORMDATA *data, DWORD flags, DWORD *offscreen); D3DCommonInterface* GetCommonD3DInterface() const { diff --git a/src/ddraw/d3d_light.cpp b/src/ddraw/d3d_light.cpp index df08b6c66a7..7543513b1cd 100644 --- a/src/ddraw/d3d_light.cpp +++ b/src/ddraw/d3d_light.cpp @@ -8,8 +8,7 @@ namespace dxvk { uint32_t D3DLight::s_lightCount = 0; - D3DLight::D3DLight(Com&& proxyLight, IUnknown* pParent) - : DDrawWrappedObject(pParent, std::move(proxyLight), nullptr) { + D3DLight::D3DLight() { m_lightCount = ++s_lightCount; Logger::debug(str::format("D3DLight: Created a new light nr. [[1-", m_lightCount, "]]")); @@ -19,6 +18,25 @@ namespace dxvk { Logger::debug(str::format("D3DLight: Light nr. [[1-", m_lightCount, "]] bites the dust")); } + HRESULT STDMETHODCALLTYPE D3DLight::QueryInterface(REFIID riid, void** ppvObject) { + Logger::debug(">> D3DLight::QueryInterface"); + + if (unlikely(ppvObject == nullptr)) + return E_POINTER; + + InitReturnPtr(ppvObject); + + if (likely(riid == __uuidof(IUnknown) || + riid == __uuidof(IDirect3DLight))) { + *ppvObject = ref(this); + return S_OK; + } + + Logger::warn("D3DLight::QueryInterface: Unknown interface query"); + Logger::warn(str::format(riid)); + return E_NOINTERFACE; + } + // Docs state: "The method returns DDERR_ALREADYINITIALIZED because // the Direct3DLight object is initialized when it is created." HRESULT STDMETHODCALLTYPE D3DLight::Initialize(IDirect3D *d3d) { @@ -56,9 +74,9 @@ namespace dxvk { return DDERR_INVALIDPARAMS; } - m_isD3DLight2 = data->dwSize == sizeof(D3DLIGHT2); - - const bool hasSpecular = m_isD3DLight2 ? !(reinterpret_cast(data)->dwFlags & D3DLIGHT_NO_SPECULAR) : true; + const bool isD3DLight2 = data->dwSize == sizeof(D3DLIGHT2); + m_flags = isD3DLight2 ? reinterpret_cast(data)->dwFlags : 0; + const bool hasSpecular = isD3DLight2 ? !(m_flags & D3DLIGHT_NO_SPECULAR) : true; // Docs: "Although this method's declaration specifies the lpLight parameter as being // the address of a D3DLIGHT structure, that structure is not normally used. Rather, @@ -66,8 +84,8 @@ namespace dxvk { m_light9.Type = d3d9::D3DLIGHTTYPE(data->dltType); m_light9.Diffuse = data->dcvColor; m_light9.Specular = hasSpecular ? data->dcvColor : noLight; - // Ambient light comes from the material - m_light9.Ambient = noLight; + // Ambient light always comes from the material + //m_light9.Ambient = noLight; m_light9.Position = data->dvPosition; m_light9.Direction = data->dvDirection; m_light9.Range = data->dvRange; @@ -78,22 +96,7 @@ namespace dxvk { m_light9.Theta = data->dvTheta; m_light9.Phi = data->dvPhi; // D3DLIGHT structure lights are, apparently, considered to be active by default - m_isActive = m_isD3DLight2 ? (reinterpret_cast(data)->dwFlags & D3DLIGHT_ACTIVE) : true; - - Logger::debug(str::format(">>> D3DLight::SetLight: Updated light nr. ", m_lightCount)); - Logger::debug(str::format(" Type: ", m_light9.Type)); - Logger::debug(str::format(" Diffuse: ", m_light9.Diffuse.r, " ", m_light9.Diffuse.g, " ", m_light9.Diffuse.b)); - Logger::debug(str::format(" Specular: ", m_light9.Specular.r, " ", m_light9.Specular.g, " ", m_light9.Specular.b)); - Logger::debug(str::format(" Ambient: ", m_light9.Ambient.r, " ", m_light9.Ambient.g, " ", m_light9.Ambient.b)); - Logger::debug(str::format(" Position: ", m_light9.Position.x, " ", m_light9.Position.y, " ", m_light9.Position.z)); - Logger::debug(str::format(" Direction: ", m_light9.Direction.x, " ", m_light9.Direction.y, " ", m_light9.Direction.z)); - Logger::debug(str::format(" Range: ", m_light9.Range)); - Logger::debug(str::format(" Falloff: ", m_light9.Falloff)); - Logger::debug(str::format(" Attenuation0: ", m_light9.Attenuation0)); - Logger::debug(str::format(" Attenuation1: ", m_light9.Attenuation1)); - Logger::debug(str::format(" Attenuation2: ", m_light9.Attenuation2)); - Logger::debug(str::format(" Theta: ", m_light9.Theta)); - Logger::debug(str::format(" Phi: ", m_light9.Phi)); + m_isActive = isD3DLight2 ? (m_flags & D3DLIGHT_ACTIVE) : true; // Update the D3D9 light directly if it's actively being used if (m_viewport6 != nullptr && m_viewport6->GetCommonViewport()->IsCurrentViewport()) @@ -112,6 +115,9 @@ namespace dxvk { if (unlikely(data == nullptr)) return DDERR_INVALIDPARAMS; + if (unlikely(!data->dwSize)) + return DDERR_INVALIDPARAMS; + data->dltType = D3DLIGHTTYPE(m_light9.Type); data->dcvColor = m_light9.Diffuse; //data->dcvColor = m_light9.Specular; @@ -125,8 +131,8 @@ namespace dxvk { data->dvTheta = m_light9.Theta; data->dvPhi = m_light9.Phi; - if (m_isD3DLight2) - reinterpret_cast(data)->dwFlags = m_isActive; + if (data->dwSize == sizeof(D3DLIGHT2)) + reinterpret_cast(data)->dwFlags = m_flags; return D3D_OK; } diff --git a/src/ddraw/d3d_light.h b/src/ddraw/d3d_light.h index ec0754d71e1..5aca2fdfd53 100644 --- a/src/ddraw/d3d_light.h +++ b/src/ddraw/d3d_light.h @@ -1,7 +1,6 @@ #pragma once #include "ddraw_include.h" -#include "ddraw_wrapped_object.h" namespace dxvk { @@ -9,14 +8,16 @@ namespace dxvk { class D3D5Viewport; class D3D6Viewport; - class D3DLight final : public DDrawWrappedObject { + class D3DLight final : public ComObjectClamp { public: - D3DLight(Com&& proxyLight, IUnknown* pParent); + D3DLight(); ~D3DLight(); + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject); + HRESULT STDMETHODCALLTYPE Initialize(IDirect3D *d3d); HRESULT STDMETHODCALLTYPE SetLight(D3DLIGHT *data); @@ -51,18 +52,15 @@ namespace dxvk { return m_isActive; } - bool IsD3DLight2() const { - return m_isD3DLight2; - } - private: bool m_isActive = false; - bool m_isD3DLight2 = false; static uint32_t s_lightCount; uint32_t m_lightCount = 0; + DWORD m_flags = 0; + D3D6Viewport* m_viewport6 = nullptr; D3D5Viewport* m_viewport5 = nullptr; D3D3Viewport* m_viewport3 = nullptr; diff --git a/src/ddraw/d3d_process_vertices.h b/src/ddraw/d3d_process_vertices.h new file mode 100644 index 00000000000..e996e91b725 --- /dev/null +++ b/src/ddraw/d3d_process_vertices.h @@ -0,0 +1,681 @@ +#pragma once + +#include "ddraw_include.h" +#include "ddraw_options.h" +#include "ddraw_caps.h" + +#include "d3d_common_viewport.h" + +#include +#include +#include + +namespace dxvk { + + struct ProcessVerticesData { + bool doLighting; + bool doClipping; + bool doExtents; + bool doNotCopyData; + bool isLegacy; + uint16_t vertexCount; + size_t inStride; + size_t outStride; + DWORD inFVF; + DWORD outFVF; + uint8_t* inData; + uint8_t* outData; + const D3DMATRIX* correction; + D3DSTATUS* dsStatus; + std::vector* lights; + }; + + // D3DCOLOR is DWORD packed ARGB, uint8_t per component + // D3DCOLORVALUE is struct with float per component normalized to 0.0f - 1.0f + inline D3DCOLOR ColorVToColor(const D3DCOLORVALUE& c) { + return D3DCOLOR_ARGB( + static_cast(c.a * 255.0f), + static_cast(c.r * 255.0f), + static_cast(c.g * 255.0f), + static_cast(c.b * 255.0f) + ); + } + + inline D3DCOLORVALUE ColorToColorV(D3DCOLOR c) { + D3DCOLORVALUE result; + + result.a = ((c >> 24) & 0xFF) / 255.0f; + result.r = ((c >> 16) & 0xFF) / 255.0f; + result.g = ((c >> 8) & 0xFF) / 255.0f; + result.b = ((c >> 0) & 0xFF) / 255.0f; + + return result; + } + + inline D3DCOLORVALUE ColorVClamp(D3DCOLORVALUE color, float min, float max) { + D3DCOLORVALUE result; + + result.r = std::clamp(color.r, min, max); + result.g = std::clamp(color.g, min, max); + result.b = std::clamp(color.b, min, max); + result.a = std::clamp(color.a, min, max); + + return result; + } + + inline void ColorVMultiplyAdd(D3DCOLORVALUE &out, D3DCOLORVALUE color, float value, bool alpha = false) { + out.r += color.r * value; + out.g += color.g * value; + out.b += color.b * value; + if (alpha) + out.a += color.r * value; + } + + inline Vector4 NormalizeVec3(Vector4 v) { + Vector4 result = normalize(Vector4(v.x, v.y, v.z, 0.0f)); + result.w = 0.0; + + return result; + } + + inline void MaterialColorSource( + d3d9::IDirect3DDevice9* d3d9Device, DWORD* diffuse, + DWORD* specular, DWORD* ambient, DWORD* emissive) { + DWORD state; + + d3d9Device->GetRenderState(d3d9::D3DRS_LIGHTING, &state); + if (state) { + *diffuse = d3d9::D3DMCS_COLOR1; + *specular = d3d9::D3DMCS_COLOR2; + *ambient = *emissive = d3d9::D3DMCS_MATERIAL; + return; + } + + d3d9Device->GetRenderState(d3d9::D3DRS_COLORVERTEX, &state); + if (state) { + *ambient = *diffuse = *emissive = *specular = d3d9::D3DMCS_MATERIAL; + return; + } + + d3d9Device->GetRenderState(d3d9::D3DRS_DIFFUSEMATERIALSOURCE, diffuse); + d3d9Device->GetRenderState(d3d9::D3DRS_SPECULARMATERIALSOURCE, specular); + d3d9Device->GetRenderState(d3d9::D3DRS_AMBIENTMATERIALSOURCE, ambient); + d3d9Device->GetRenderState(d3d9::D3DRS_EMISSIVEMATERIALSOURCE, emissive); + } + + inline D3DCOLORVALUE ColorFromMaterialSource( + D3DCOLOR* diffuse, D3DCOLOR* specular, DWORD colorSource, D3DCOLORVALUE materialColor) { + switch (colorSource) { + case d3d9::D3DMCS_COLOR1: + if (diffuse != nullptr) + return ColorToColorV(*diffuse); + else + return D3DCOLORVALUE{1.0, 1.0, 1.0, 1.0}; + case d3d9::D3DMCS_COLOR2: + if (specular != nullptr) + return ColorToColorV(*specular); + break; + case d3d9::D3DMCS_MATERIAL: + return materialColor; + } + + return D3DCOLORVALUE{0.0, 0.0, 0.0, 0.0}; + } + + inline void ApplyLight( + const d3d9::D3DLIGHT9* light9, bool localViewer, D3DCOLORVALUE& diffuse, D3DCOLORVALUE& specular, + Vector4 normals, Vector4 hitDirection, Vector4 position, float attenuation, float materialPower, bool isLegacy) { + const float direction_dot = dot(hitDirection, normals); + const float direction_clamped_dot = std::clamp(direction_dot, 0.0f, 1.0f); + ColorVMultiplyAdd(diffuse, light9->Diffuse, direction_clamped_dot * attenuation); + + Vector4 mid = hitDirection; + if (localViewer) { + mid.x -= position.x; + mid.y -= position.y; + mid.z -= position.z; + } else { + mid.z -= 1.0f; + } + + mid = normalize(mid); + const float direction_transformed_dot = dot(Vector4(normals.x, normals.y, normals.z, 0.0f), + Vector4(mid.x, mid.y, mid.z, 0.0f)); + if (direction_transformed_dot > 0.0f && (!isLegacy || materialPower > 0.0f) && direction_dot > 0.0f) + ColorVMultiplyAdd(specular, light9->Specular, powf(direction_transformed_dot, materialPower) * attenuation); + } + + inline void ApplyFog( + float *specularAlpha, D3DFOGMODE vertexMode, float density, + float start, float end, bool range, Vector4 position) { + if (vertexMode == D3DFOG_NONE) + return; + + const float coord = range ? sqrt(dot(position, position)) : fabsf(position.z); + + switch (vertexMode) { + case D3DFOG_EXP: + *specularAlpha = expf(-1.0f * coord * density); + break; + case D3DFOG_EXP2: + *specularAlpha = expf(-1.0f * coord * density * coord * density); + break; + case D3DFOG_LINEAR: + *specularAlpha = (end - coord) / (end - start); + break; + default: + break; + } + } + + inline void ProcessVerticesInput( + DWORD dwFVF, uint8_t *ptr, Vector4& position, Vector4& normals, + Vector4& texCoords, D3DCOLOR& diffuse, D3DCOLOR& specular) { + const DWORD dwNumTextures = (dwFVF & D3DFVF_TEXCOUNT_MASK) >> D3DFVF_TEXCOUNT_SHIFT; + if (uint8_t type = (dwFVF & D3DFVF_POSITION_MASK)) { + switch (type) { + case D3DFVF_XYZ: + position.x = *reinterpret_cast(ptr); + ptr += sizeof(FLOAT); + position.y = *reinterpret_cast(ptr); + ptr += sizeof(FLOAT); + position.z = *reinterpret_cast(ptr); + ptr += sizeof(FLOAT); + position.w = 1.0f; + break; + case D3DFVF_XYZRHW: + position.x = *reinterpret_cast(ptr); + ptr += sizeof(FLOAT); + position.y = *reinterpret_cast(ptr); + ptr += sizeof(FLOAT); + position.z = *reinterpret_cast(ptr); + ptr += sizeof(FLOAT); + position.w = *reinterpret_cast(ptr); + ptr += sizeof(FLOAT); + break; + case D3DFVF_XYZB1: + position.x = *reinterpret_cast(ptr); + ptr += sizeof(FLOAT); + position.y = *reinterpret_cast(ptr); + ptr += sizeof(FLOAT); + position.z = *reinterpret_cast(ptr); + ptr += sizeof(FLOAT); + position.w = 1.0f; + ptr += sizeof(FLOAT); + break; + case D3DFVF_XYZB2: + position.x = *reinterpret_cast(ptr); + ptr += sizeof(FLOAT); + position.y = *reinterpret_cast(ptr); + ptr += sizeof(FLOAT); + position.z = *reinterpret_cast(ptr); + ptr += sizeof(FLOAT); + position.w = 1.0f; + ptr += 2 * sizeof(FLOAT); + break; + case D3DFVF_XYZB3: + position.x = *reinterpret_cast(ptr); + ptr += sizeof(FLOAT); + position.y = *reinterpret_cast(ptr); + ptr += sizeof(FLOAT); + position.z = *reinterpret_cast(ptr); + ptr += sizeof(FLOAT); + position.w = 1.0f; + ptr += 3 * sizeof(FLOAT); + break; + case D3DFVF_XYZB4: + position.x = *reinterpret_cast(ptr); + ptr += sizeof(FLOAT); + position.y = *reinterpret_cast(ptr); + ptr += sizeof(FLOAT); + position.z = *reinterpret_cast(ptr); + ptr += sizeof(FLOAT); + position.w = 1.0f; + ptr += 4 * sizeof(FLOAT); + break; + case D3DFVF_XYZB5: + position.x = *reinterpret_cast(ptr); + ptr += sizeof(FLOAT); + position.y = *reinterpret_cast(ptr); + ptr += sizeof(FLOAT); + position.z = *reinterpret_cast(ptr); + ptr += sizeof(FLOAT); + position.w = 1.0f; + ptr += 5 * sizeof(FLOAT); + break; + } + } + + if (dwFVF & D3DFVF_NORMAL) { + normals.x = *reinterpret_cast(ptr); + ptr += sizeof(FLOAT); + normals.y = *reinterpret_cast(ptr); + ptr += sizeof(FLOAT); + normals.z = *reinterpret_cast(ptr); + ptr += sizeof(FLOAT); + normals.w = 0.0f; + } + + if (dwFVF & D3DFVF_RESERVED1) { + ptr += sizeof(DWORD); + } + + if (dwFVF & D3DFVF_DIFFUSE) { + diffuse = *reinterpret_cast(ptr); + ptr += sizeof(D3DCOLOR); + } + + if (dwFVF & D3DFVF_SPECULAR) { + specular = *reinterpret_cast(ptr); + ptr += sizeof(D3DCOLOR); + } + + for (DWORD tex = 0; tex < dwNumTextures; tex++) { + const DWORD texCoordSize = (dwFVF >> (tex * 2 + 16)) & 0x3; + switch (texCoordSize) { + case D3DFVF_TEXTUREFORMAT1: + texCoords.x = *reinterpret_cast(ptr); + ptr += sizeof(FLOAT); + break; + case D3DFVF_TEXTUREFORMAT2: + texCoords.x = *reinterpret_cast(ptr); + ptr += sizeof(FLOAT); + texCoords.y = *reinterpret_cast(ptr); + ptr += sizeof(FLOAT); + break; + case D3DFVF_TEXTUREFORMAT3: + texCoords.x = *reinterpret_cast(ptr); + ptr += sizeof(FLOAT); + texCoords.y = *reinterpret_cast(ptr); + ptr += sizeof(FLOAT); + texCoords.z = *reinterpret_cast(ptr); + ptr += sizeof(FLOAT); + break; + case D3DFVF_TEXTUREFORMAT4: + texCoords.x = *reinterpret_cast(ptr); + ptr += sizeof(FLOAT); + texCoords.y = *reinterpret_cast(ptr); + ptr += sizeof(FLOAT); + texCoords.z = *reinterpret_cast(ptr); + ptr += sizeof(FLOAT); + texCoords.w = *reinterpret_cast(ptr); + ptr += sizeof(FLOAT); + break; + } + } + } + + inline void ProcessVerticesOutput( + DWORD dwFVF, uint8_t *ptr, Vector4* position, Vector4* normals, + Vector4* texCoords, D3DCOLOR* diffuse, D3DCOLOR* specular) { + const DWORD dwNumTextures = (dwFVF & D3DFVF_TEXCOUNT_MASK) >> D3DFVF_TEXCOUNT_SHIFT; + if (uint8_t type = (dwFVF & D3DFVF_POSITION_MASK)) { + switch (type) { + case D3DFVF_XYZ: + memcpy(ptr, &position->x, sizeof(FLOAT)); + ptr += sizeof(FLOAT); + memcpy(ptr, &position->y, sizeof(FLOAT)); + ptr += sizeof(FLOAT); + memcpy(ptr, &position->z, sizeof(FLOAT)); + ptr += sizeof(FLOAT); + break; + case D3DFVF_XYZRHW: + memcpy(ptr, &position->x, sizeof(FLOAT)); + ptr += sizeof(FLOAT); + memcpy(ptr, &position->y, sizeof(FLOAT)); + ptr += sizeof(FLOAT); + memcpy(ptr, &position->z, sizeof(FLOAT)); + ptr += sizeof(FLOAT); + memcpy(ptr, &position->w, sizeof(FLOAT)); + ptr += sizeof(FLOAT); + break; + case D3DFVF_XYZB1: + memcpy(ptr, &position->x, sizeof(FLOAT)); + ptr += sizeof(FLOAT); + memcpy(ptr, &position->y, sizeof(FLOAT)); + ptr += sizeof(FLOAT); + memcpy(ptr, &position->z, sizeof(FLOAT)); + ptr += sizeof(FLOAT); + memset(ptr, 0x00, sizeof(FLOAT)); + ptr += sizeof(FLOAT); + break; + case D3DFVF_XYZB2: + memcpy(ptr, &position->x, sizeof(FLOAT)); + ptr += sizeof(FLOAT); + memcpy(ptr, &position->y, sizeof(FLOAT)); + ptr += sizeof(FLOAT); + memcpy(ptr, &position->z, sizeof(FLOAT)); + ptr += sizeof(FLOAT); + memset(ptr, 0x00, 2 * sizeof(FLOAT)); + ptr += 2 * sizeof(FLOAT); + break; + case D3DFVF_XYZB3: + memcpy(ptr, &position->x, sizeof(FLOAT)); + ptr += sizeof(FLOAT); + memcpy(ptr, &position->y, sizeof(FLOAT)); + ptr += sizeof(FLOAT); + memcpy(ptr, &position->z, sizeof(FLOAT)); + ptr += sizeof(FLOAT); + memset(ptr, 0x00, 3 * sizeof(FLOAT)); + ptr += 3 * sizeof(FLOAT); + break; + case D3DFVF_XYZB4: + memcpy(ptr, &position->x, sizeof(FLOAT)); + ptr += sizeof(FLOAT); + memcpy(ptr, &position->y, sizeof(FLOAT)); + ptr += sizeof(FLOAT); + memcpy(ptr, &position->z, sizeof(FLOAT)); + ptr += sizeof(FLOAT); + memset(ptr, 0x00, 4 * sizeof(FLOAT)); + ptr += 4 * sizeof(FLOAT); + break; + case D3DFVF_XYZB5: + memcpy(ptr, &position->x, sizeof(FLOAT)); + ptr += sizeof(FLOAT); + memcpy(ptr, &position->y, sizeof(FLOAT)); + ptr += sizeof(FLOAT); + memcpy(ptr, &position->z, sizeof(FLOAT)); + ptr += sizeof(FLOAT); + memset(ptr, 0x00, 5 * sizeof(FLOAT)); + ptr += 5 * sizeof(FLOAT); + break; + } + } + + if (dwFVF & D3DFVF_NORMAL) { + memcpy(ptr, &normals->x, sizeof(FLOAT)); + ptr += sizeof(FLOAT); + memcpy(ptr, &normals->y, sizeof(FLOAT)); + ptr += sizeof(FLOAT); + memcpy(ptr, &normals->z, sizeof(FLOAT)); + ptr += sizeof(FLOAT); + } + + if (dwFVF & D3DFVF_RESERVED1) { + ptr += sizeof(DWORD); + } + + if (dwFVF & D3DFVF_DIFFUSE) { + if (diffuse != nullptr) + memcpy(ptr, diffuse, sizeof(D3DCOLOR)); + ptr += sizeof(D3DCOLOR); + } + + if (dwFVF & D3DFVF_SPECULAR) { + if (specular != nullptr) + memcpy(ptr, specular, sizeof(D3DCOLOR)); + ptr += sizeof(D3DCOLOR); + } + + for (DWORD tex = 0; tex < dwNumTextures; tex++) { + const DWORD texCoordSize = (dwFVF >> (tex * 2 + 16)) & 0x3; + switch (texCoordSize) { + case D3DFVF_TEXTUREFORMAT1: + if (texCoords != nullptr) + memcpy(ptr, &texCoords->x, sizeof(FLOAT)); + ptr += sizeof(FLOAT); + break; + case D3DFVF_TEXTUREFORMAT2: + if (texCoords != nullptr) + memcpy(ptr, &texCoords->x, sizeof(FLOAT)); + ptr += sizeof(FLOAT); + if (texCoords != nullptr) + memcpy(ptr, &texCoords->y, sizeof(FLOAT)); + ptr += sizeof(FLOAT); + break; + case D3DFVF_TEXTUREFORMAT3: + if (texCoords != nullptr) + memcpy(ptr, &texCoords->x, sizeof(FLOAT)); + ptr += sizeof(FLOAT); + if (texCoords != nullptr) + memcpy(ptr, &texCoords->y, sizeof(FLOAT)); + ptr += sizeof(FLOAT); + if (texCoords != nullptr) + memcpy(ptr, &texCoords->z, sizeof(FLOAT)); + ptr += sizeof(FLOAT); + break; + case D3DFVF_TEXTUREFORMAT4: + if (texCoords != nullptr) + memcpy(ptr, &texCoords->x, sizeof(FLOAT)); + ptr += sizeof(FLOAT); + if (texCoords != nullptr) + memcpy(ptr, &texCoords->y, sizeof(FLOAT)); + ptr += sizeof(FLOAT); + if (texCoords != nullptr) + memcpy(ptr, &texCoords->z, sizeof(FLOAT)); + ptr += sizeof(FLOAT); + if (texCoords != nullptr) + memcpy(ptr, &texCoords->w, sizeof(FLOAT)); + ptr += sizeof(FLOAT); + break; + } + } + } + + inline void ProcessVerticesSW( + d3d9::IDirect3DDevice9* d3d9Device, const D3DOptions* options, ProcessVerticesData *pvData) { + Logger::debug(">>> ProcessVerticesSW"); + if (unlikely(pvData == nullptr)) { + Logger::err("ProcessVerticesSW: Missing processing data"); + return; + } + + D3DMATRIX world{}, view{}, projection{}; + d3d9::D3DVIEWPORT9 viewport9; + HRESULT hr = d3d9Device->GetViewport(&viewport9); + if (unlikely(FAILED(hr))) { + Logger::err("ProcessVerticesSW: Failed to get viewport"); + return; + } + hr = d3d9Device->GetTransform(ConvertTransformState(D3DTRANSFORMSTATE_WORLD), &world); + if (unlikely(FAILED(hr))) { + Logger::err("ProcessVerticesSW: Failed to get world transform"); + return; + } + hr = d3d9Device->GetTransform(d3d9::D3DTS_VIEW, &view); + if (unlikely(FAILED(hr))) { + Logger::err("ProcessVerticesSW: Failed to get view transform"); + return; + } + hr = d3d9Device->GetTransform(d3d9::D3DTS_PROJECTION, &projection); + if (unlikely(FAILED(hr))) { + Logger::err("ProcessVerticesSW: Failed to get projection transform"); + return; + } + + d3d9::D3DMATERIAL9 material{}; + d3d9Device->GetMaterial(&material); + + Matrix4 wv = MatrixD3DTo4(&view) * MatrixD3DTo4(&world); + Matrix4 wvp = MatrixD3DTo4(&projection) * wv; + if (pvData->isLegacy && pvData->correction != nullptr) + wvp = MatrixD3DTo4(pvData->correction) * wvp; + + BOOL isEnabledLighting, isEnabledFogRange, isEnabledNormalizeNormals, isEnabledSpecular, isEnabledLocalViewer; + D3DCOLOR stateAmbient; + D3DFOGMODE fogVertexMode; + float fogStart, fogEnd, fogDensity; + + d3d9Device->GetRenderState(d3d9::D3DRS_FOGVERTEXMODE, reinterpret_cast(&fogVertexMode)); + d3d9Device->GetRenderState(d3d9::D3DRS_FOGSTART, reinterpret_cast(&fogStart)); + d3d9Device->GetRenderState(d3d9::D3DRS_FOGEND, reinterpret_cast(&fogEnd)); + d3d9Device->GetRenderState(d3d9::D3DRS_FOGDENSITY, reinterpret_cast(&fogDensity)); + d3d9Device->GetRenderState(d3d9::D3DRS_RANGEFOGENABLE, reinterpret_cast(&isEnabledFogRange)); + d3d9Device->GetRenderState(d3d9::D3DRS_LIGHTING, reinterpret_cast(&isEnabledLighting)); + if (isEnabledLighting) { + d3d9Device->GetRenderState(d3d9::D3DRS_AMBIENT, reinterpret_cast(&stateAmbient)); + d3d9Device->GetRenderState(d3d9::D3DRS_SPECULARENABLE, reinterpret_cast(&isEnabledSpecular)); + d3d9Device->GetRenderState(d3d9::D3DRS_NORMALIZENORMALS, reinterpret_cast(&isEnabledNormalizeNormals)); + d3d9Device->GetRenderState(d3d9::D3DRS_LOCALVIEWER, reinterpret_cast(&isEnabledLocalViewer)); + } + + DWORD sourceDiffuse, sourceSpecular, sourceAmbient, sourceEmissive; + MaterialColorSource(d3d9Device, &sourceDiffuse, &sourceSpecular, &sourceAmbient, &sourceEmissive); + for (uint16_t t = 0; t < pvData->vertexCount; t++) { + uint8_t* inPtr = pvData->inData + t * pvData->inStride; + uint8_t* outPtr = pvData->outData + t * pvData->outStride; + + Vector4 inPosition{}; + Vector4 inNormals{}; + D3DCOLOR inDiffuse = 0, inSpecular = 0; + Vector4 inTexCoords{}; + const bool hasDiffUse = pvData->inFVF & D3DFVF_DIFFUSE; + const bool hasSpecular = pvData->inFVF & D3DFVF_SPECULAR; + ProcessVerticesInput(pvData->inFVF, inPtr, inPosition, inNormals, inTexCoords, inDiffuse, inSpecular); + + // Transform vertices + Vector4 h = wvp * inPosition; + Vector4 outPosition; + + // Hidden & Dangerous (D3D6) relies on division by zero and NAN/INF output + outPosition.w = 1.0f / h.w; + outPosition.x = viewport9.X + static_cast(viewport9.Width) * 0.5 * (h.x * outPosition.w + 1.0f); + outPosition.y = viewport9.Y + static_cast(viewport9.Height) * 0.5 * (1.0f - h.y * outPosition.w); + outPosition.z = viewport9.MinZ + h.z * outPosition.w * (viewport9.MaxZ - viewport9.MinZ); + + // Hack for Resident Evil quad alignment problem + if (unlikely(options->vertexOffset != 0.0f)) { + outPosition.x += options->vertexOffset; + outPosition.y += options->vertexOffset; + } + + if (unlikely(pvData->doClipping)) { + if (pvData->dsStatus != nullptr && (pvData->dsStatus->dwFlags & D3DSETSTATUS_STATUS)) { + pvData->dsStatus->dwFlags = 0; + pvData->dsStatus->dwStatus = 0; + if (pvData->doExtents) { + pvData->dsStatus->dwFlags |= D3DSETSTATUS_EXTENTS; + pvData->dsStatus->drExtent.x1 = viewport9.X; + pvData->dsStatus->drExtent.y1 = viewport9.Y; + pvData->dsStatus->drExtent.x2 = viewport9.X + viewport9.Width; + pvData->dsStatus->drExtent.y2 = viewport9.Y + viewport9.Height; + } + } + } + + D3DCOLORVALUE diffuse{0.0f, 0.0f, 0.0f, 0.0f}; + D3DCOLORVALUE specular{0.0f, 0.0f, 0.0f, 0.0f}; + + Vector4 VWPosition = wv * inPosition; + const float positionScale = 1.0f / VWPosition.w; + VWPosition.x *= positionScale; + VWPosition.y *= positionScale; + VWPosition.z *= positionScale; + + if (pvData->doLighting && isEnabledLighting && pvData->lights != nullptr + && pvData->outFVF & (D3DFVF_DIFFUSE | D3DFVF_SPECULAR)) { + const float materialPower = isEnabledSpecular ? material.Power : 0.0f; + Vector4 NVWPosition = NormalizeVec3(VWPosition); + Vector4 normals = wv * inNormals; + if (isEnabledNormalizeNormals) + normals = NormalizeVec3(normals); + + D3DCOLORVALUE ambient = ColorToColorV(stateAmbient); + + for (d3d9::D3DLIGHT9 light : *pvData->lights) { + Vector4 hitDirection; + float attenuation; + D3DLIGHTTYPE lightType = D3DLIGHTTYPE(light.Type); + switch (lightType) { + case D3DLIGHT_DIRECTIONAL: { + Vector4 lightDirection = NormalizeVec3(MatrixD3DTo4(&view) * Vector4(-light.Direction.x, -light.Direction.y, + -light.Direction.z, 0.0f)); + hitDirection = Vector4(lightDirection.x, lightDirection.y, lightDirection.z, 0.0f); + attenuation = 1.0f; + break; + } + case D3DLIGHT_POINT: + case D3DLIGHT_SPOT: { + Vector4 light_position = MatrixD3DTo4(&view) * Vector4(light.Position.x, light.Position.y, + light.Position.z, 1.0f); + + hitDirection = Vector4(light_position.x - VWPosition.x, light_position.y - VWPosition.y, + light_position.z - VWPosition.z, 0.0f); + Vector4 destination(1.0f, 0.0f, dot(hitDirection, hitDirection), 0.0f); + destination.y = sqrtf(destination.z); + if (pvData->isLegacy) { + destination.y = (light.Range - destination.y) / light.Range; + if (destination.y <= 0.0f) + continue; + destination.z = destination.y * destination.y; + } else { + if (destination.y > 0.0f) + continue; + } + + hitDirection = NormalizeVec3(hitDirection); + attenuation = destination.x * light.Attenuation0 + destination.y * + light.Attenuation1 + destination.z * light.Attenuation2; + if (!pvData->isLegacy) + attenuation = 1.0f / attenuation; + + if (lightType == D3DLIGHT_SPOT) { + Vector4 lightDirection = NormalizeVec3(MatrixD3DTo4(&view) * Vector4(light.Direction.x, light.Direction.y, + light.Direction.z, 0.0f)); + const float rho = dot(-hitDirection, lightDirection); + const float cosHalfPhi = cosf(light.Phi / 2.0f); + const float cosHalfTheta = cosf(light.Theta / 2.0f); + if (rho <= cosHalfPhi) + attenuation = 0.0f; + else if (rho <= cosHalfTheta) + attenuation *= powf((rho - cosHalfPhi) / (cosHalfTheta - cosHalfPhi), light.Falloff); + } + break; + } + // TODO: parallel point lights + case D3DLIGHT_PARALLELPOINT: + default: { + static bool s_warnShown; + if (!std::exchange(s_warnShown, true)) + Logger::warn(str::format("ProcessVerticesSW: Unsupported light type: ", light.Type)); + continue; + } + } + + ColorVMultiplyAdd(ambient, light.Ambient, attenuation); + ApplyLight(&light, isEnabledLocalViewer, diffuse, specular, normals, + hitDirection, NVWPosition, attenuation, materialPower, pvData->isLegacy); + } + + D3DCOLORVALUE materialDiffuse = ColorFromMaterialSource(hasDiffUse ? &inDiffuse : nullptr, + hasSpecular ? &inSpecular : nullptr, + sourceDiffuse, material.Diffuse); + D3DCOLORVALUE materialSpecular = ColorFromMaterialSource(hasDiffUse ? &inDiffuse : nullptr, + hasSpecular ? &inSpecular : nullptr, + sourceSpecular, material.Specular); + D3DCOLORVALUE materialAmbient = ColorFromMaterialSource(hasDiffUse ? &inDiffuse : nullptr, + hasSpecular ? &inSpecular : nullptr, + sourceAmbient, material.Ambient); + D3DCOLORVALUE materialEmissive = ColorFromMaterialSource(hasDiffUse ? &inDiffuse : nullptr, + hasSpecular ? &inSpecular : nullptr, + sourceEmissive, material.Emissive); + + diffuse.r = ambient.r * materialAmbient.r + diffuse.r * materialDiffuse.r + materialEmissive.r; + diffuse.g = ambient.g * materialAmbient.g + diffuse.g * materialDiffuse.g + materialEmissive.g; + diffuse.b = ambient.b * materialAmbient.b + diffuse.b * materialDiffuse.b + materialEmissive.b; + diffuse.a = material.Diffuse.a; + + specular.r *= materialSpecular.r; + specular.g *= materialSpecular.g; + specular.b *= materialSpecular.b; + specular.a *= pvData->isLegacy ? 0.0f : materialSpecular.a; + } else { + diffuse = hasDiffUse ? ColorToColorV(inDiffuse) : D3DCOLORVALUE{1.0, 1.0, 1.0, 1.0}; + specular = hasSpecular ? ColorToColorV(inSpecular) : D3DCOLORVALUE{0.0, 0.0, 0.0, 0.0}; + } + + ApplyFog(&specular.a, fogVertexMode, fogDensity, fogStart, fogEnd, isEnabledFogRange, VWPosition); + diffuse = ColorVClamp(diffuse, 0.0f, 1.0f); + specular = ColorVClamp(specular, 0.0f, 1.0f); + + D3DCOLOR outDiffuse = ColorVToColor(diffuse); + D3DCOLOR outSpecular = ColorVToColor(specular); + + ProcessVerticesOutput(pvData->outFVF, outPtr, &outPosition, &inNormals, + pvData->doNotCopyData ? nullptr : &inTexCoords, + pvData->doNotCopyData ? nullptr : &outDiffuse, + pvData->doNotCopyData ? nullptr : &outSpecular); + } + } + +} diff --git a/src/ddraw/ddraw/ddraw_interface.cpp b/src/ddraw/ddraw/ddraw_interface.cpp index 5f4b050afc1..2efeb8145ac 100644 --- a/src/ddraw/ddraw/ddraw_interface.cpp +++ b/src/ddraw/ddraw/ddraw_interface.cpp @@ -22,7 +22,7 @@ namespace dxvk { DDrawInterface::DDrawInterface( DDrawCommonInterface* commonIntf, Com&& proxyIntf) - : DDrawWrappedObject(nullptr, std::move(proxyIntf), nullptr) + : DDrawWrappedObject(nullptr, std::move(proxyIntf)) , m_commonIntf ( commonIntf ) { if (likely(m_commonIntf == nullptr)) { @@ -42,12 +42,6 @@ namespace dxvk { m_commonIntf->SetDDInterface(this); - static bool s_apitraceModeWarningShown; - - if (unlikely(m_commonIntf->GetOptions()->apitraceMode && - !std::exchange(s_apitraceModeWarningShown, true))) - Logger::warn("DDrawInterface: Apitrace mode is enabled. Performance will be suboptimal!"); - m_intfCount = ++s_intfCount; Logger::debug(str::format("DDrawInterface: Created a new interface nr. <<1-", m_intfCount, ">>")); @@ -294,8 +288,40 @@ namespace dxvk { try{ // Surfaces created from IDirectDraw and IDirectDraw2 do not ref their parent interfaces Com surface = new DDrawSurface(nullptr, std::move(ddrawSurfaceProxied), this, nullptr, false); - if (unlikely(lpDDSurfaceDesc->ddsCaps.dwCaps & DDSCAPS_PRIMARYSURFACE)) + + if (unlikely(lpDDSurfaceDesc->ddsCaps.dwCaps & DDSCAPS_PRIMARYSURFACE)) { m_commonIntf->SetPrimarySurface(surface->GetCommonSurface()); + + // Shadow surface creation for the primary surface + // (it needs to be based on the same incoming desc) + if (unlikely(!surface->GetCommonSurface()->Is8BitFormat() && + m_commonIntf->GetOptions()->forceLegacyPresent)) { + Logger::debug("DDrawInterface::CreateSurface: Creating shadow surface"); + + DDSURFACEDESC shadowDesc = *lpDDSurfaceDesc; + const DDSURFACEDESC* primaryDesc = surface->GetCommonSurface()->GetDesc(); + + shadowDesc.ddsCaps.dwCaps &= ~DDSCAPS_PRIMARYSURFACE & ~DDSCAPS_COMPLEX & ~DDSCAPS_FLIP; + shadowDesc.ddsCaps.dwCaps |= DDSCAPS_OFFSCREENPLAIN; + shadowDesc.dwFlags &= ~DDSD_BACKBUFFERCOUNT; + // Dimensions aren't specified in the incoming desc, + // but are explicitly needed for non-primary surfaces + shadowDesc.dwFlags |= DDSD_WIDTH | DDSD_HEIGHT; + shadowDesc.dwWidth = primaryDesc->dwWidth; + shadowDesc.dwHeight = primaryDesc->dwHeight; + + Com ddrawSurfaceShadow; + hr = m_proxy->CreateSurface(&shadowDesc, &ddrawSurfaceShadow, pUnkOuter); + if (unlikely(FAILED(hr))) { + Logger::warn("DDrawInterface::CreateSurface: Failed to create shadow surface"); + } else { + Com shadowSurf = new DDrawSurface(nullptr, std::move(ddrawSurfaceShadow), + this, nullptr, false); + surface->SetShadowSurface(std::move(shadowSurf)); + } + } + } + *lplpDDSurface = surface.ref(); } catch (const DxvkError& e) { Logger::err(e.message()); @@ -370,11 +396,6 @@ namespace dxvk { } HRESULT STDMETHODCALLTYPE DDrawInterface::FlipToGDISurface() { - if (unlikely(m_commonIntf->GetOptions()->forceProxiedPresent)) { - Logger::debug("<<< DDrawInterface::FlipToGDISurface: Proxy"); - return m_proxy->FlipToGDISurface(); - } - Logger::debug("*** DDrawInterface::FlipToGDISurface: Ignoring"); DDrawCommonSurface* ps = m_commonIntf->GetPrimarySurface(); @@ -392,6 +413,18 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE DDrawInterface::GetCaps(LPDDCAPS lpDDDriverCaps, LPDDCAPS lpDDHELCaps) { Logger::debug("<<< DDrawInterface::GetCaps: Proxy"); + if (unlikely(lpDDDriverCaps == nullptr && lpDDHELCaps == nullptr)) + return DDERR_INVALIDPARAMS; + + // Interstate '76 sends invalid dwSizes part of the structs, + // and that explodes in Wine, so validate it before proxying + + if (unlikely(lpDDDriverCaps != nullptr && !IsValidDDrawCapsSize(lpDDDriverCaps->dwSize))) + return DDERR_INVALIDPARAMS; + + if (unlikely(lpDDHELCaps != nullptr && !IsValidDDrawCapsSize(lpDDHELCaps->dwSize))) + return DDERR_INVALIDPARAMS; + HRESULT hr = m_proxy->GetCaps(lpDDDriverCaps, lpDDHELCaps); if (unlikely(FAILED(hr))) return hr; @@ -591,8 +624,7 @@ namespace dxvk { Logger::warn("DDrawInterface::SetDisplayMode: Failed to update primary surface desc"); } - if (likely(!m_commonIntf->GetOptions()->forceProxiedPresent && - m_commonIntf->GetOptions()->backBufferResize)) { + if (likely(m_commonIntf->GetOptions()->backBufferResize)) { const bool exclusiveMode = m_commonIntf->GetCooperativeLevel() & DDSCL_EXCLUSIVE; // Ignore any mode size dimensions when in windowed present mode @@ -610,11 +642,6 @@ namespace dxvk { } HRESULT STDMETHODCALLTYPE DDrawInterface::WaitForVerticalBlank(DWORD dwFlags, HANDLE hEvent) { - if (unlikely(m_commonIntf->GetOptions()->forceProxiedPresent)) { - Logger::debug("<<< DDrawInterface::WaitForVerticalBlank: Proxy"); - m_proxy->WaitForVerticalBlank(dwFlags, hEvent); - } - Logger::debug(">>> DDrawInterface::WaitForVerticalBlank"); if (unlikely(dwFlags & DDWAITVB_BLOCKBEGINEVENT)) diff --git a/src/ddraw/ddraw/ddraw_interface.h b/src/ddraw/ddraw/ddraw_interface.h index 05ac089aed5..1d351db37f2 100644 --- a/src/ddraw/ddraw/ddraw_interface.h +++ b/src/ddraw/ddraw/ddraw_interface.h @@ -17,7 +17,7 @@ namespace dxvk { /** * \brief DirectDraw interface implementation */ - class DDrawInterface final : public DDrawWrappedObject { + class DDrawInterface final : public DDrawWrappedObject { public: DDrawInterface( diff --git a/src/ddraw/ddraw/ddraw_surface.cpp b/src/ddraw/ddraw/ddraw_surface.cpp index c7332b2e657..b46f9b68bec 100644 --- a/src/ddraw/ddraw/ddraw_surface.cpp +++ b/src/ddraw/ddraw/ddraw_surface.cpp @@ -23,7 +23,7 @@ namespace dxvk { DDrawInterface* pParent, DDrawSurface* pParentSurf, bool isChildObject) - : DDrawWrappedObject(pParent, std::move(surfProxy), nullptr) + : DDrawWrappedObject(pParent, std::move(surfProxy)) , m_isChildObject ( isChildObject ) , m_commonSurf ( commonSurf ) , m_parentSurf ( pParentSurf ) { @@ -53,13 +53,23 @@ namespace dxvk { } } + // Retrieve and cache the next surface in a flippable chain + if (unlikely(m_commonSurf->IsFlippable() && !m_commonSurf->IsBackBuffer())) { + IDirectDrawSurface* nextFlippable = nullptr; + EnumAttachedSurfaces(&nextFlippable, ListBackBufferSurfacesCallback); + m_nextFlippable = reinterpret_cast(nextFlippable); + if (likely(m_nextFlippable != nullptr)) + Logger::debug("DDrawSurface: Retrieved the next swapchain surface"); + } + m_commonIntf->AddWrappedSurface(this); m_commonSurf->SetDDSurface(this); - if (m_parentSurf != nullptr && !m_commonSurf->IsFrontBuffer() + if (m_parentSurf != nullptr && m_parentSurf->GetCommonSurface()->IsBackBufferOrFlippable() - && !m_commonIntf->GetOptions()->forceSingleBackBuffer) { + && !m_commonIntf->GetOptions()->forceSingleBackBuffer + && !m_commonIntf->GetOptions()->forceLegacyPresent) { const uint32_t index = m_parentSurf->GetCommonSurface()->GetBackBufferIndex(); m_commonSurf->IncrementBackBufferIndex(index); } @@ -227,7 +237,24 @@ namespace dxvk { if (unlikely(FAILED(hr))) return hr; - *ppvObject = ref(new DDraw4Surface(m_commonSurf.ptr(), std::move(ppvProxyObject), m_commonIntf->GetDD4Interface(), nullptr, false)); + Com surface4 = new DDraw4Surface(m_commonSurf.ptr(), std::move(ppvProxyObject), + m_commonIntf->GetDD4Interface(), nullptr, false); + + // Dungeon Keeper 2 creates and attaches a IDirectDrawSurface4 depth stencil, + // but then keeps using clears from the IDirectDrawSurface object... + if (m_depthStencil != nullptr) { + Com dsProxyObject; + hr = m_depthStencil->GetProxied()->QueryInterface(riid, reinterpret_cast(&dsProxyObject)); + if (unlikely(FAILED(hr))) + return hr; + + Com depthStencil4 = new DDraw4Surface(m_depthStencil->GetCommonSurface(), std::move(dsProxyObject), + m_commonIntf->GetDD4Interface(), nullptr, false); + + surface4->SetAttachedDepthStencil(std::move(depthStencil4)); + } + + *ppvObject = surface4.ref(); return S_OK; } @@ -278,13 +305,11 @@ namespace dxvk { DDrawSurface* ddrawSurface = static_cast(lpDDSAttachedSurface); + // Unsupported by Wine's DDraw implementation, so we'll do our own present if (unlikely(ddrawSurface->GetCommonSurface()->IsBackBufferOrFlippable())) { - if (unlikely(m_commonIntf->GetOptions()->forceBlitOnFlip)) { - Logger::debug("DDrawSurface::AddAttachedSurface: Caching surface as RT"); - m_commonIntf->SetDDrawRenderTarget(ddrawSurface->GetCommonSurface()); - } else { - Logger::warn("DDrawSurface::AddAttachedSurface: Trying to attach a flippable surface"); - } + Logger::debug("DDrawSurface::AddAttachedSurface: Caching surface as DDraw RT"); + m_commonIntf->SetDDrawRenderTarget(ddrawSurface->GetCommonSurface()); + return DD_OK; } HRESULT hr = m_proxy->AddAttachedSurface(ddrawSurface->GetProxied()); @@ -308,89 +333,30 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE DDrawSurface::Blt(LPRECT lpDestRect, LPDIRECTDRAWSURFACE lpDDSrcSurface, LPRECT lpSrcRect, DWORD dwFlags, LPDDBLTFX lpDDBltFx) { Logger::debug("<<< DDrawSurface::Blt: Proxy"); - // Write back any flippable surfaces or depth stencils from D3D9 + // Write back any dirty surface data from bound D3D9 back buffers or + // depth stencils, for both the source surface and the current surface if (likely(lpDDSrcSurface != nullptr && m_commonIntf->IsWrappedSurface(lpDDSrcSurface))) { - DDrawSurface* sourceSurface = static_cast(lpDDSrcSurface); - if (unlikely(sourceSurface->GetCommonSurface()->IsGuardableSurface())) { - if (m_commonIntf->GetOptions()->backBufferWriteBack || m_commonIntf->GetOptions()->apitraceMode) { - Logger::debug("DDrawSurface::Blt: Source surface is a swapchain surface"); - - if (unlikely(m_commonIntf->GetOptions()->apitraceMode && !sourceSurface->IsInitialized())) - sourceSurface->InitializeOrUploadD3D9(); - - if (likely(sourceSurface->IsInitialized())) - BlitToDDrawSurface(sourceSurface->GetProxied(), sourceSurface->GetD3D9()); - } else { - static bool s_swapchainWarningShown; - - if (!std::exchange(s_swapchainWarningShown, true)) - Logger::warn("DDrawSurface::Blt: Source surface is a swapchain surface"); - } - } else if (unlikely(sourceSurface->GetCommonSurface()->IsDepthStencil())) { - if (m_commonIntf->GetOptions()->depthWriteBack || m_commonIntf->GetOptions()->apitraceMode) { - Logger::debug("DDrawSurface::Blt: Source surface is a depth stencil"); - - if (likely(sourceSurface->IsInitialized())) - BlitToDDrawSurface(sourceSurface->GetProxied(), sourceSurface->GetD3D9()); - } else { - static bool s_depthStencilWarningShown; - - if (!std::exchange(s_depthStencilWarningShown, true)) - Logger::warn("DDrawSurface::Blt: Source surface is a depth stencil"); - } - } + DDrawSurface* surface = static_cast(lpDDSrcSurface); + surface->DownloadSurfaceData(); } + DownloadSurfaceData(); - RefreshD3D9Device(); - if (likely(m_d3d9Device != nullptr)) { - // Forward DDBLT_DEPTHFILL clears to D3D9 if done on the current depth stencil - if (unlikely(lpDDSrcSurface == nullptr && - (dwFlags & DDBLT_DEPTHFILL) && - lpDDBltFx != nullptr && - m_commonIntf->GetCommonD3DDevice()->IsCurrentD3D9DepthStencil(m_d3d9.ptr()))) { - Logger::debug("DDrawSurface::Blt: Clearing d3d9 depth stencil"); - - HRESULT hrClear; - const float zClear = m_commonSurf->GetNormalizedFloatDepth(lpDDBltFx->dwFillDepth); - - if (lpDestRect == nullptr) { - hrClear = m_d3d9Device->Clear(0, NULL, D3DCLEAR_ZBUFFER, 0, zClear, 0); - } else { - hrClear = m_d3d9Device->Clear(1, reinterpret_cast(lpDestRect), D3DCLEAR_ZBUFFER, 0, zClear, 0); - } - if (unlikely(FAILED(hrClear))) - Logger::warn("DDrawSurface::Blt: Failed to clear d3d9 depth"); - } - // Forward DDBLT_COLORFILL clears to D3D9 if done on the current render target - if (unlikely(lpDDSrcSurface == nullptr && - (dwFlags & DDBLT_COLORFILL) && - lpDDBltFx != nullptr && - m_commonIntf->GetCommonD3DDevice()->IsCurrentD3D9RenderTarget(m_d3d9.ptr()))) { - Logger::debug("DDrawSurface::Blt: Clearing d3d9 render target"); - - HRESULT hrClear; - if (lpDestRect == nullptr) { - hrClear = m_d3d9Device->Clear(0, NULL, D3DCLEAR_TARGET, lpDDBltFx->dwFillColor, 0.0f, 0); - } else { - hrClear = m_d3d9Device->Clear(1, reinterpret_cast(lpDestRect), D3DCLEAR_TARGET, lpDDBltFx->dwFillColor, 0.0f, 0); - } - if (unlikely(FAILED(hrClear))) - Logger::warn("DDrawSurface::Blt: Failed to clear d3d9 render target"); - } - + d3d9::IDirect3DDevice9* d3d9Device = m_commonSurf->RefreshD3D9Device(); + if (likely(d3d9Device != nullptr)) { const bool exclusiveMode = (m_commonIntf->GetCooperativeLevel() & DDSCL_EXCLUSIVE) && !m_commonIntf->GetOptions()->ignoreExclusiveMode; - // Eclusive mode back buffer guard - if (exclusiveMode && m_commonIntf->HasDrawn() && - m_commonSurf->IsGuardableSurface() && - m_commonIntf->GetOptions()->backBufferGuard != D3DBackBufferGuard::Disabled) { - return DD_OK; // Windowed mode presentation path - } else if (!exclusiveMode && m_commonIntf->HasDrawn() && m_commonSurf->IsPrimarySurface()) { - m_commonIntf->ResetDrawTracking(); - m_d3d9Device->Present(NULL, NULL, NULL, NULL); - return DD_OK; + if (!exclusiveMode && lpDDSrcSurface != nullptr && m_commonSurf->IsPrimarySurface()) { + // TODO: Handle this properly, not by uploading the RT again + DDrawSurface* ddrawSurface = static_cast(lpDDSrcSurface); + DDrawSurface* renderTarget = m_commonSurf->GetCommonD3DDevice()->GetCurrentRenderTarget(); + + if (ddrawSurface == renderTarget) { + renderTarget->InitializeOrUploadD3D9(); + d3d9Device->Present(NULL, NULL, NULL, NULL); + return DD_OK; + } } } @@ -401,20 +367,24 @@ namespace dxvk { Logger::warn("DDrawSurface::Blt: Received an unwrapped source surface"); return DDERR_UNSUPPORTED; } - hr = m_proxy->Blt(lpDestRect, lpDDSrcSurface, lpSrcRect, dwFlags, lpDDBltFx); + hr = GetShadowOrProxied()->Blt(lpDestRect, lpDDSrcSurface, lpSrcRect, dwFlags, lpDDBltFx); } else { DDrawSurface* ddrawSurface = static_cast(lpDDSrcSurface); - hr = m_proxy->Blt(lpDestRect, ddrawSurface->GetProxied(), lpSrcRect, dwFlags, lpDDBltFx); + hr = GetShadowOrProxied()->Blt(lpDestRect, ddrawSurface->GetShadowOrProxied(), lpSrcRect, dwFlags, lpDDBltFx); } if (likely(SUCCEEDED(hr))) { - // Textures get uploaded during SetTexture calls - if (!m_commonSurf->IsTexture()) { - HRESULT hrUpload = InitializeOrUploadD3D9(); - if (unlikely(FAILED(hrUpload))) - Logger::warn("DDrawSurface::Blt: Failed upload to d3d9 surface"); - } else { - m_commonSurf->DirtyMipMaps(); + m_commonSurf->DirtyDDrawSurface(); + + if (unlikely(m_shadowSurf != nullptr && d3d9Device != nullptr)) { + const bool shouldPresent = m_commonIntf->GetOptions()->legacyPresentGuard == D3DLegacyPresentGuard::Auto ? + !m_commonSurf->GetCommonD3DDevice()->IsInScene() : + m_commonIntf->GetOptions()->legacyPresentGuard == D3DLegacyPresentGuard::Strict ? + false : true; + if (shouldPresent) { + InitializeOrUploadD3D9(); + d3d9Device->Present(NULL, NULL, NULL, NULL); + } } } @@ -430,54 +400,30 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE DDrawSurface::BltFast(DWORD dwX, DWORD dwY, LPDIRECTDRAWSURFACE lpDDSrcSurface, LPRECT lpSrcRect, DWORD dwTrans) { Logger::debug("<<< DDrawSurface::BltFast: Proxy"); - // Write back any flippable surfaces or depth stencils from D3D9 + // Write back any dirty surface data from bound D3D9 back buffers or + // depth stencils, for both the source surface and the current surface if (likely(lpDDSrcSurface != nullptr && m_commonIntf->IsWrappedSurface(lpDDSrcSurface))) { - DDrawSurface* sourceSurface = static_cast(lpDDSrcSurface); - if (unlikely(sourceSurface->GetCommonSurface()->IsGuardableSurface())) { - if (m_commonIntf->GetOptions()->backBufferWriteBack || m_commonIntf->GetOptions()->apitraceMode) { - Logger::debug("DDrawSurface::BltFast: Source surface is a swapchain surface"); - - if (unlikely(m_commonIntf->GetOptions()->apitraceMode && !sourceSurface->IsInitialized())) - sourceSurface->InitializeOrUploadD3D9(); - - if (likely(sourceSurface->IsInitialized())) - BlitToDDrawSurface(sourceSurface->GetProxied(), sourceSurface->GetD3D9()); - } else { - static bool s_swapchainWarningShown; - - if (!std::exchange(s_swapchainWarningShown, true)) - Logger::warn("DDrawSurface::BltFast: Source surface is a swapchain surface"); - } - } else if (unlikely(sourceSurface->GetCommonSurface()->IsDepthStencil())) { - if (m_commonIntf->GetOptions()->depthWriteBack || m_commonIntf->GetOptions()->apitraceMode) { - Logger::debug("DDrawSurface::BltFast: Source surface is a depth stencil"); - - if (likely(sourceSurface->IsInitialized())) - BlitToDDrawSurface(sourceSurface->GetProxied(), sourceSurface->GetD3D9()); - } else { - static bool s_depthStencilWarningShown; - - if (!std::exchange(s_depthStencilWarningShown, true)) - Logger::warn("DDrawSurface::BltFast: Source surface is a depth stencil"); - } - } + DDrawSurface* surface = static_cast(lpDDSrcSurface); + surface->DownloadSurfaceData(); } + DownloadSurfaceData(); - RefreshD3D9Device(); - if (likely(m_d3d9Device != nullptr)) { + d3d9::IDirect3DDevice9* d3d9Device = m_commonSurf->RefreshD3D9Device(); + if (likely(d3d9Device != nullptr)) { const bool exclusiveMode = (m_commonIntf->GetCooperativeLevel() & DDSCL_EXCLUSIVE) && !m_commonIntf->GetOptions()->ignoreExclusiveMode; - // Eclusive mode back buffer guard - if (exclusiveMode && m_commonIntf->HasDrawn() && - m_commonSurf->IsGuardableSurface() && - m_commonIntf->GetOptions()->backBufferGuard != D3DBackBufferGuard::Disabled) { - return DD_OK; // Windowed mode presentation path - } else if (!exclusiveMode && m_commonIntf->HasDrawn() && m_commonSurf->IsPrimarySurface()) { - m_commonIntf->ResetDrawTracking(); - m_d3d9Device->Present(NULL, NULL, NULL, NULL); - return DD_OK; + if (!exclusiveMode && lpDDSrcSurface != nullptr && m_commonSurf->IsPrimarySurface()) { + // TODO: Handle this properly, not by uploading the RT again + DDrawSurface* ddrawSurface = static_cast(lpDDSrcSurface); + DDrawSurface* renderTarget = m_commonSurf->GetCommonD3DDevice()->GetCurrentRenderTarget(); + + if (ddrawSurface == renderTarget) { + renderTarget->InitializeOrUploadD3D9(); + d3d9Device->Present(NULL, NULL, NULL, NULL); + return DD_OK; + } } } @@ -488,20 +434,24 @@ namespace dxvk { Logger::warn("DDrawSurface::BltFast: Received an unwrapped source surface"); return DDERR_UNSUPPORTED; } - hr = m_proxy->BltFast(dwX, dwY, lpDDSrcSurface, lpSrcRect, dwTrans); + hr = GetShadowOrProxied()->BltFast(dwX, dwY, lpDDSrcSurface, lpSrcRect, dwTrans); } else { DDrawSurface* ddrawSurface = static_cast(lpDDSrcSurface); - hr = m_proxy->BltFast(dwX, dwY, ddrawSurface->GetProxied(), lpSrcRect, dwTrans); + hr = GetShadowOrProxied()->BltFast(dwX, dwY, ddrawSurface->GetShadowOrProxied(), lpSrcRect, dwTrans); } if (likely(SUCCEEDED(hr))) { - // Textures get uploaded during SetTexture calls - if (!m_commonSurf->IsTexture()) { - HRESULT hrUpload = InitializeOrUploadD3D9(); - if (unlikely(FAILED(hrUpload))) - Logger::warn("DDrawSurface::BltFast: Failed upload to d3d9 surface"); - } else { - m_commonSurf->DirtyMipMaps(); + m_commonSurf->DirtyDDrawSurface(); + + if (unlikely(m_shadowSurf != nullptr && d3d9Device != nullptr)) { + const bool shouldPresent = m_commonIntf->GetOptions()->legacyPresentGuard == D3DLegacyPresentGuard::Auto ? + !m_commonSurf->GetCommonD3DDevice()->IsInScene() : + m_commonIntf->GetOptions()->legacyPresentGuard == D3DLegacyPresentGuard::Strict ? + false : true; + if (shouldPresent) { + InitializeOrUploadD3D9(); + d3d9Device->Present(NULL, NULL, NULL, NULL); + } } } @@ -528,6 +478,13 @@ namespace dxvk { DDrawSurface* ddrawSurface = static_cast(lpDDSAttachedSurface); + if (unlikely(ddrawSurface->GetCommonSurface()->IsBackBufferOrFlippable() && + ddrawSurface->GetCommonSurface() == m_commonIntf->GetDDrawRenderTarget())) { + Logger::debug("DDrawSurface::DeleteAttachedSurface: Removing cached DDraw RT surface"); + m_commonIntf->SetDDrawRenderTarget(nullptr); + return DD_OK; + } + HRESULT hr = m_proxy->DeleteAttachedSurface(dwFlags, ddrawSurface->GetProxied()); if (unlikely(FAILED(hr))) return hr; @@ -585,102 +542,68 @@ namespace dxvk { } HRESULT STDMETHODCALLTYPE DDrawSurface::Flip(LPDIRECTDRAWSURFACE lpDDSurfaceTargetOverride, DWORD dwFlags) { - // Lost surfaces are not flippable - HRESULT hr = m_proxy->IsLost(); - if (unlikely(FAILED(hr))) { - Logger::debug("DDrawSurface::Flip: Lost surface"); - return hr; - } - - if (unlikely(!(m_commonSurf->IsFrontBuffer() || m_commonSurf->IsBackBufferOrFlippable()))) { - Logger::debug("DDrawSurface::Flip: Unflippable surface"); - return DDERR_NOTFLIPPABLE; - } - - const bool exclusiveMode = m_commonIntf->GetCooperativeLevel() & DDSCL_EXCLUSIVE; - - // Non-exclusive mode validations - if (unlikely(m_commonSurf->IsPrimarySurface() && !exclusiveMode)) { - Logger::debug("DDrawSurface::Flip: Primary surface flip in non-exclusive mode"); - return DDERR_NOEXCLUSIVEMODE; - } - - // Exclusive mode validations - if (unlikely(m_commonSurf->IsBackBufferOrFlippable() && exclusiveMode)) { - Logger::debug("DDrawSurface::Flip: Back buffer flip in exclusive mode"); - return DDERR_NOTFLIPPABLE; - } Com surf; - if (m_commonIntf->IsWrappedSurface(lpDDSurfaceTargetOverride)) { + if (m_commonIntf->IsWrappedSurface(lpDDSurfaceTargetOverride)) surf = static_cast(lpDDSurfaceTargetOverride); - if (unlikely(!surf->GetCommonSurface()->IsBackBufferOrFlippable())) { - Logger::debug("DDrawSurface::Flip: Unflippable override surface"); - return DDERR_NOTFLIPPABLE; - } - } + d3d9::IDirect3DDevice9* d3d9Device = m_commonSurf->RefreshD3D9Device(); + if (likely(d3d9Device != nullptr)) { + Logger::debug("*** DDrawSurface::Flip: Presenting"); - DDrawSurface* rt = m_commonIntf->GetDDrawRenderTarget() != nullptr ? - m_commonIntf->GetDDrawRenderTarget()->GetDDSurface() : nullptr; + // Lost surfaces are not flippable + HRESULT hr = m_proxy->IsLost(); + if (unlikely(FAILED(hr))) { + Logger::debug("DDrawSurface::Flip: Lost surface"); + return hr; + } - RefreshD3D9Device(); - if (likely(m_d3d9Device != nullptr)) { - Logger::debug("*** DDrawSurface::Flip: Presenting"); + if (unlikely(!(m_commonSurf->IsFrontBuffer() || m_commonSurf->IsBackBufferOrFlippable()))) { + Logger::debug("DDrawSurface::Flip: Unflippable surface"); + return DDERR_NOTFLIPPABLE; + } - m_commonIntf->ResetDrawTracking(); + const bool exclusiveMode = m_commonIntf->GetCooperativeLevel() & DDSCL_EXCLUSIVE; - if (unlikely(m_commonIntf->GetOptions()->forceProxiedPresent)) { - D3DCommonDevice* commonDevice = m_commonIntf->GetCommonD3DDevice(); + // Non-exclusive mode validations + if (unlikely(m_commonSurf->IsPrimarySurface() && !exclusiveMode)) { + Logger::debug("DDrawSurface::Flip: Primary surface flip in non-exclusive mode"); + return DDERR_NOEXCLUSIVEMODE; + } - if (unlikely(!IsInitialized())) - InitializeD3D9(commonDevice->IsCurrentRenderTarget(this)); + // Exclusive mode validations + if (unlikely(m_commonSurf->IsBackBufferOrFlippable() && exclusiveMode)) { + Logger::debug("DDrawSurface::Flip: Back buffer flip in exclusive mode"); + return DDERR_NOTFLIPPABLE; + } - BlitToDDrawSurface(m_proxy.ptr(), m_d3d9.ptr()); + if (unlikely(surf != nullptr && !surf->GetCommonSurface()->IsBackBufferOrFlippable())) { + Logger::debug("DDrawSurface::Flip: Unflippable override surface"); + return DDERR_NOTFLIPPABLE; + } - if (unlikely(!m_commonIntf->IsWrappedSurface(lpDDSurfaceTargetOverride))) { - if (unlikely(lpDDSurfaceTargetOverride != nullptr)) { - Logger::warn("DDrawSurface::Flip: Received an unwrapped surface"); - return DDERR_UNSUPPORTED; - } + DDrawSurface* rt = m_commonIntf->GetDDrawRenderTarget() != nullptr ? + m_commonIntf->GetDDrawRenderTarget()->GetDDSurface() : nullptr; - if (likely(commonDevice->IsCurrentRenderTarget(this))) - m_commonIntf->SetFlipRTSurfaceAndFlags(lpDDSurfaceTargetOverride, dwFlags); + if (unlikely(rt != nullptr && m_commonSurf->IsPrimarySurface())) { + Logger::debug("DDrawSurface::Flip: Presenting from DDraw RT"); + rt->InitializeOrUploadD3D9(); + d3d9Device->Present(NULL, NULL, NULL, NULL); + return DD_OK; + } - if (unlikely(m_commonIntf->GetOptions()->forceBlitOnFlip && - rt != nullptr && m_commonSurf->IsPrimarySurface())) { - Logger::debug("DDrawSurface::Flip: Skipping flip"); - return DD_OK; - } else { - return m_proxy->Flip(lpDDSurfaceTargetOverride, dwFlags); - } - } else { - if (likely(commonDevice->IsCurrentRenderTarget(this))) - m_commonIntf->SetFlipRTSurfaceAndFlags(lpDDSurfaceTargetOverride, dwFlags); - - if (unlikely(m_commonIntf->GetOptions()->forceBlitOnFlip && - rt != nullptr && m_commonSurf->IsPrimarySurface())) { - Logger::debug("DDrawSurface::Flip: Skipping flip"); - return DD_OK; - } else { - return m_proxy->Flip(surf->GetProxied(), dwFlags); - } - } + if (likely(m_nextFlippable != nullptr)) { + m_nextFlippable->InitializeOrUploadD3D9(); + } else { + InitializeOrUploadD3D9(); } - m_d3d9Device->Present(NULL, NULL, NULL, NULL); - // If we don't have a valid D3D5 device, this means a D3D3 application - // is trying to flip the surface. Allow that for compatibility reasons. + d3d9Device->Present(NULL, NULL, NULL, NULL); + } else { Logger::debug("<<< DDrawSurface::Flip: Proxy"); if (unlikely(!m_commonIntf->IsWrappedSurface(lpDDSurfaceTargetOverride))) { - if (unlikely(m_commonIntf->GetOptions()->forceBlitOnFlip && - rt != nullptr && m_commonSurf->IsPrimarySurface())) { - Logger::debug("DDrawSurface::Flip: Blitting instead of flipping"); - return m_proxy->Blt(nullptr, rt->GetProxied(), nullptr, DDBLT_WAIT, nullptr); - } else { - return m_proxy->Flip(lpDDSurfaceTargetOverride, dwFlags); - } + return m_proxy->Flip(lpDDSurfaceTargetOverride, dwFlags); } else { return m_proxy->Flip(surf->GetProxied(), dwFlags); } @@ -752,11 +675,6 @@ namespace dxvk { // Blitting can be done at any time and completes within its call frame HRESULT STDMETHODCALLTYPE DDrawSurface::GetBltStatus(DWORD dwFlags) { - if (unlikely(m_commonIntf->GetOptions()->forceProxiedPresent)) { - Logger::debug("<<< DDrawSurface::GetBltStatus: Proxy"); - m_proxy->GetBltStatus(dwFlags); - } - Logger::debug(">>> DDrawSurface::GetBltStatus"); if (likely(dwFlags == DDGBS_CANBLT || dwFlags == DDGBS_ISBLTDONE)) @@ -800,42 +718,16 @@ namespace dxvk { } HRESULT STDMETHODCALLTYPE DDrawSurface::GetDC(HDC *lphDC) { - if (likely(!m_commonIntf->GetOptions()->forceProxiedPresent)) { - if (unlikely(lphDC == nullptr)) - return DDERR_INVALIDPARAMS; - - InitReturnPtr(lphDC); - - // Foward GetDC calls if we have drawn and the surface is flippable - RefreshD3D9Device(); - if (m_d3d9Device != nullptr && (m_commonIntf->HasDrawn() && - m_commonSurf->IsGuardableSurface())) { - Logger::debug(">>> DDrawSurface::GetDC"); - - if (unlikely(!IsInitialized())) { - HRESULT hrUpload = InitializeOrUploadD3D9(); - if (unlikely(FAILED(hrUpload))) - Logger::warn("DDrawSurface::GetDC: Failed to initialize d3d9 surface"); - } + Logger::debug("<<< DDrawSurface::GetDC: Proxy"); - HRESULT hr9 = m_d3d9->GetDC(lphDC); - if (unlikely(FAILED(hr9))) - Logger::warn("DDrawSurface::GetDC: Failed D3D9 call"); - return hr9; - } - } + // Write back any dirty surface data from bound D3D9 back buffers or depth stencils + DownloadSurfaceData(); - Logger::debug("<<< DDrawSurface::GetDC: Proxy"); - return m_proxy->GetDC(lphDC); + return GetShadowOrProxied()->GetDC(lphDC); } // Flipping can be done at any time and completes within its call frame HRESULT STDMETHODCALLTYPE DDrawSurface::GetFlipStatus(DWORD dwFlags) { - if (unlikely(m_commonIntf->GetOptions()->forceProxiedPresent)) { - Logger::debug("<<< DDrawSurface::GetFlipStatus: Proxy"); - m_proxy->GetFlipStatus(dwFlags); - } - Logger::debug(">>> DDrawSurface::GetFlipStatus"); if (likely(dwFlags == DDGFS_CANFLIP || dwFlags == DDGFS_ISFLIPDONE)) @@ -907,75 +799,30 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE DDrawSurface::Lock(LPRECT lpDestRect, LPDDSURFACEDESC lpDDSurfaceDesc, DWORD dwFlags, HANDLE hEvent) { Logger::debug("<<< DDrawSurface::Lock: Proxy"); - // Write back any flippable surfaces or depth stencils from D3D9 - if (unlikely(m_commonSurf->IsGuardableSurface())) { - if (m_commonIntf->GetOptions()->backBufferWriteBack || m_commonIntf->GetOptions()->apitraceMode) { - Logger::debug("DDrawSurface::Lock: Surface is a swapchain surface"); - - if (unlikely(m_commonIntf->GetOptions()->apitraceMode && !IsInitialized())) - InitializeOrUploadD3D9(); - - if (likely(IsInitialized())) - BlitToDDrawSurface(m_proxy.ptr(), m_d3d9.ptr()); - } else { - static bool s_swapchainWarningShown; - - if (!std::exchange(s_swapchainWarningShown, true)) - Logger::warn("DDrawSurface::Lock: Surface is a swapchain surface"); - } - } else if (unlikely(m_commonSurf->IsDepthStencil())) { - if (m_commonIntf->GetOptions()->depthWriteBack || m_commonIntf->GetOptions()->apitraceMode) { - Logger::debug("DDrawSurface::Lock: Surface is a depth stencil"); - - if (likely(IsInitialized())) - BlitToDDrawSurface(m_proxy.ptr(), m_d3d9.ptr()); - } else { - static bool s_depthStencilWarningShown; + // Write back any dirty surface data from bound D3D9 back buffers or depth stencils + DownloadSurfaceData(); - if (!std::exchange(s_depthStencilWarningShown, true)) - Logger::warn("DDrawSurface::Lock: Surface is a depth stencil"); - } - } - - return m_proxy->Lock(lpDestRect, lpDDSurfaceDesc, dwFlags, hEvent); + return GetShadowOrProxied()->Lock(lpDestRect, lpDDSurfaceDesc, dwFlags, hEvent); } HRESULT STDMETHODCALLTYPE DDrawSurface::ReleaseDC(HDC hDC) { - if (likely(!m_commonIntf->GetOptions()->forceProxiedPresent)) { - // Foward ReleaseDC calls if we have drawn and the surface is flippable - RefreshD3D9Device(); - if (m_d3d9Device != nullptr && (m_commonIntf->HasDrawn() && - m_commonSurf->IsGuardableSurface())) { - Logger::debug(">>> DDrawSurface::ReleaseDC"); - - if (unlikely(!IsInitialized())) { - HRESULT hrUpload = InitializeOrUploadD3D9(); - if (unlikely(FAILED(hrUpload))) - Logger::warn("DDrawSurface::ReleaseDC: Failed to initialize d3d9 surface"); - } - - HRESULT hr9 = m_d3d9->ReleaseDC(hDC); - if (unlikely(FAILED(hr9))) - Logger::warn("DDrawSurface::ReleaseDC: Failed D3D9 call"); - return hr9; - } - } - Logger::debug("<<< DDrawSurface::ReleaseDC: Proxy"); - HRESULT hr = m_proxy->ReleaseDC(hDC); + HRESULT hr = GetShadowOrProxied()->ReleaseDC(hDC); if (likely(SUCCEEDED(hr))) { - // Textures get uploaded during SetTexture calls - if (m_commonSurf->IsTexture()) { - m_commonSurf->DirtyMipMaps(); - } else if (unlikely(m_commonIntf->GetOptions()->apitraceMode)) { - // We should ideally upload the surface contents here at all times, - // however some games are amazing, and do hundreds of locks on the same - // surface per frame, so this would absolutely tank performance - HRESULT hrUpload = InitializeOrUploadD3D9(); - if (unlikely(FAILED(hrUpload))) - Logger::warn("DDrawSurface::ReleaseDC: Failed upload to d3d9 surface"); + m_commonSurf->DirtyDDrawSurface(); + + d3d9::IDirect3DDevice9* d3d9Device = m_commonSurf->RefreshD3D9Device(); + if (unlikely(m_shadowSurf != nullptr && d3d9Device != nullptr)) { + const bool shouldPresent = m_commonIntf->GetOptions()->legacyPresentGuard == D3DLegacyPresentGuard::Auto ? + !m_commonSurf->GetCommonD3DDevice()->IsInScene() : + m_commonIntf->GetOptions()->legacyPresentGuard == D3DLegacyPresentGuard::Strict ? + false : true; + if (shouldPresent) { + InitializeOrUploadD3D9(); + d3d9Device->Present(NULL, NULL, NULL, NULL); + } } } @@ -1005,6 +852,15 @@ namespace dxvk { return hr; m_commonSurf->SetClipper(ddrawClipper); + + // Retrieve a hWnd, if needed, during clipper attachment + HWND hWnd = nullptr; + hr = ddrawClipper->GetProxied()->GetHWnd(&hWnd); + if (unlikely(FAILED(hr))) { + Logger::debug("DDrawSurface::SetClipper: Failed to retrieve hWnd"); + } else { + m_commonIntf->SetHWND(hWnd); + } } return DD_OK; @@ -1013,6 +869,16 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE DDrawSurface::SetColorKey(DWORD dwFlags, LPDDCOLORKEY lpDDColorKey) { Logger::debug("<<< DDrawSurface::SetColorKey: Proxy"); + // The Combat Mission series of games set a color key which is + // outside the color range of the surface they are setting it on... + // clamp it to the surface color depth in that case. This doesn't + // appear to work well universally, however, so only apply when needed. + if (unlikely(m_commonIntf->GetOptions()->colorKeyMasking && lpDDColorKey != nullptr)) { + const uint8_t colorBitCount = m_commonSurf->GetColorBitCount(); + lpDDColorKey->dwColorSpaceLowValue &= (1 << colorBitCount) - 1; + lpDDColorKey->dwColorSpaceHighValue &= (1 << colorBitCount) - 1; + } + HRESULT hr = m_proxy->SetColorKey(dwFlags, lpDDColorKey); if (unlikely(FAILED(hr))) return hr; @@ -1021,6 +887,17 @@ namespace dxvk { if (unlikely(FAILED(hr))) Logger::err("DDrawSurface::SetColorKey: Failed to retrieve updated surface desc"); + if (unlikely(m_shadowSurf != nullptr)) { + hr = m_shadowSurf->GetProxied()->SetColorKey(dwFlags, lpDDColorKey); + if (unlikely(FAILED(hr))) { + Logger::warn("DDrawSurface::SetColorKey: Failed to set shadow surface color key"); + } else { + hr = m_shadowSurf->GetCommonSurface()->RefreshSurfaceDescripton(); + if (unlikely(FAILED(hr))) + Logger::warn("DDrawSurface::SetColorKey: Failed to retrieve updated shadow surface desc"); + } + } + return DD_OK; } @@ -1034,7 +911,7 @@ namespace dxvk { // A nullptr lpDDPalette gets the current palette detached if (lpDDPalette == nullptr) { - HRESULT hr = m_proxy->SetPalette(lpDDPalette); + HRESULT hr = GetShadowOrProxied()->SetPalette(lpDDPalette); if (unlikely(FAILED(hr))) return hr; @@ -1042,7 +919,7 @@ namespace dxvk { } else { DDrawPalette* ddrawPalette = static_cast(lpDDPalette); - HRESULT hr = m_proxy->SetPalette(ddrawPalette->GetProxied()); + HRESULT hr = GetShadowOrProxied()->SetPalette(ddrawPalette->GetProxied()); if (unlikely(FAILED(hr))) return hr; @@ -1055,16 +932,21 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE DDrawSurface::Unlock(LPVOID lpSurfaceData) { Logger::debug("<<< DDrawSurface::Unlock: Proxy"); - HRESULT hr = m_proxy->Unlock(lpSurfaceData); + HRESULT hr = GetShadowOrProxied()->Unlock(lpSurfaceData); if (likely(SUCCEEDED(hr))) { - // Textures and cubemaps get uploaded during SetTexture calls - if (!m_commonSurf->IsTexture()) { - HRESULT hrUpload = InitializeOrUploadD3D9(); - if (unlikely(FAILED(hrUpload))) - Logger::warn("DDrawSurface::Unlock: Failed upload to d3d9 surface"); - } else { - m_commonSurf->DirtyMipMaps(); + m_commonSurf->DirtyDDrawSurface(); + + d3d9::IDirect3DDevice9* d3d9Device = m_commonSurf->RefreshD3D9Device(); + if (unlikely(m_shadowSurf != nullptr && d3d9Device != nullptr)) { + const bool shouldPresent = m_commonIntf->GetOptions()->legacyPresentGuard == D3DLegacyPresentGuard::Auto ? + !m_commonSurf->GetCommonD3DDevice()->IsInScene() : + m_commonIntf->GetOptions()->legacyPresentGuard == D3DLegacyPresentGuard::Strict ? + false : true; + if (shouldPresent) { + InitializeOrUploadD3D9(); + d3d9Device->Present(NULL, NULL, NULL, NULL); + } } } @@ -1104,379 +986,177 @@ namespace dxvk { return m_proxy->UpdateOverlayZOrder(dwFlags, ddrawSurface->GetProxied()); } - HRESULT DDrawSurface::InitializeD3D9RenderTarget() { - HRESULT hr = DD_OK; + IDirectDrawSurface* DDrawSurface::GetShadowOrProxied() { + d3d9::IDirect3DDevice9* d3d9Device = m_commonSurf->RefreshD3D9Device(); - RefreshD3D9Device(); + if (unlikely(m_shadowSurf != nullptr && d3d9Device != nullptr)) + return m_shadowSurf->GetProxied(); - if (unlikely(!IsInitialized())) - hr = InitializeD3D9(true); - - return hr; + return m_proxy.ptr(); } - HRESULT DDrawSurface::InitializeD3D9DepthStencil() { - HRESULT hr = DD_OK; - - RefreshD3D9Device(); + HRESULT DDrawSurface::InitializeD3D9RenderTarget() { + m_commonSurf->RefreshD3D9Device(); - if (unlikely(!IsInitialized())) - hr = InitializeD3D9(false); + m_commonSurf->MarkAsD3D9BackBuffer(); - return hr; - } + if (unlikely(!m_commonSurf->IsInitialized())) { + if (m_commonSurf->IsTexture()) + UpdateMipMapCount(); - HRESULT DDrawSurface::InitializeOrUploadD3D9() { - HRESULT hr = DD_OK; + HRESULT hr = m_commonSurf->InitializeD3D9(true); + if (unlikely(FAILED(hr))) + return hr; - RefreshD3D9Device(); + Logger::debug(str::format("DDrawSurface::InitializeD3D9RenderTarget: Initialized surface nr. [[1-", m_surfCount, "]]")); - if (likely(IsInitialized())) { - hr = UploadSurfaceData(); - } else { - hr = InitializeD3D9(false); + return UploadSurfaceData(); } - return hr; + return DD_OK; } - HRESULT DDrawSurface::InitializeD3D9(const bool initRT) { - if (unlikely(m_d3d9Device == nullptr)) { - Logger::debug("DDrawSurface::InitializeD3D9: Null device, can't initialize right now"); - return DD_OK; - } + HRESULT DDrawSurface::InitializeD3D9DepthStencil() { + m_commonSurf->RefreshD3D9Device(); - Logger::debug(str::format("DDrawSurface::InitializeD3D9: Initializing nr. [[1-", m_surfCount, "]]")); + m_commonSurf->MarkAsD3D9DepthStencil(); - const DDSURFACEDESC* desc = m_commonSurf->GetDesc(); - const d3d9::D3DFORMAT format = m_commonSurf->GetD3D9Format(); + if (unlikely(!m_commonSurf->IsInitialized())) { + HRESULT hr = m_commonSurf->InitializeD3D9(false); + if (unlikely(FAILED(hr))) + return hr; - if (unlikely(desc->dwHeight == 0 || desc->dwWidth == 0)) { - Logger::err("DDrawSurface::InitializeD3D9: Surface has 0 height or width"); - return DDERR_GENERIC; - } + Logger::debug(str::format("DDrawSurface::InitializeD3D9DepthStencil: Initialized surface nr. [[1-", m_surfCount, "]]")); - if (unlikely(format == d3d9::D3DFMT_UNKNOWN)) { - Logger::err("DDrawSurface::InitializeD3D9: Surface has an unknown format"); - return DDERR_GENERIC; + return UploadSurfaceData(); } - // Don't initialize P8 textures/surfaces since we don't support them. - // Some applications do require them to be created by ddraw, otherwise - // they will simply fail to start, so just ignore them for now. - if (unlikely(format == d3d9::D3DFMT_P8)) { - static bool s_formatP8ErrorShown; - - if (!std::exchange(s_formatP8ErrorShown, true)) - Logger::warn("DDrawSurface::InitializeD3D9: Unsupported format D3DFMT_P8"); - - return DD_OK; - - // Similarly, D3DFMT_R3G3B2 isn't supported by D3D9 dxvk, however some - // applications require it to be supported by ddraw, even if they do not - // use it. Simply ignore any D3DFMT_R3G3B2 textures/surfaces for now. - } else if (unlikely(format == d3d9::D3DFMT_R3G3B2)) { - static bool s_formatR3G3B2ErrorShown; + return DD_OK; + } - if (!std::exchange(s_formatR3G3B2ErrorShown, true)) - Logger::warn("DDrawSurface::InitializeD3D9: Unsupported format D3DFMT_R3G3B2"); + HRESULT DDrawSurface::InitializeOrUploadD3D9() { + d3d9::IDirect3DDevice9* d3d9Device = m_commonSurf->RefreshD3D9Device(); + // Fast skip + if (unlikely(d3d9Device == nullptr)) { + Logger::debug("DDrawSurface::InitializeOrUploadD3D9: Null device, can't initialize right now"); return DD_OK; } - // We need to count the number of actual mips on initialization by going through - // the mip chain, since the dwMipMapCount number may or may not be accurate. I am - // guessing it was intended more as a hint, not neceesarily a set number. - if (m_commonSurf->IsTexture()) { - IDirectDrawSurface* mipMap = m_proxy.ptr(); - DDSURFACEDESC mipDesc; - uint16_t mipCount = 1; - - while (mipMap != nullptr) { - IDirectDrawSurface* parentSurface = mipMap; - mipMap = nullptr; - parentSurface->EnumAttachedSurfaces(&mipMap, ListMipChainSurfacesCallback); - if (mipMap != nullptr) { - mipCount++; - - mipDesc = { }; - mipDesc.dwSize = sizeof(DDSURFACEDESC2); - mipMap->GetSurfaceDesc(&mipDesc); - // Ignore multiple 1x1 mips, which apparently can get generated if the - // application gets the dwMipMapCount wrong vs surface dimensions. - if (unlikely(mipDesc.dwWidth == 1 && mipDesc.dwHeight == 1)) - break; - } - } - - // Do not worry about maximum supported mip map levels validation, - // because D3D9 will handle this for us and cap them appropriately - if (mipCount > 1) { - Logger::debug(str::format("DDrawSurface::InitializeD3D9: Found ", mipCount, " mip levels")); - - if (unlikely(mipCount != desc->dwMipMapCount)) - Logger::debug(str::format("DDrawSurface::InitializeD3D9: Mismatch with declared ", desc->dwMipMapCount, " mip levels")); - } - - if (unlikely(m_commonIntf->GetOptions()->autoGenMipMaps)) { - Logger::debug("DDrawSurface::InitializeD3D9: Using auto mip map generation"); - mipCount = 0; - } - - m_commonSurf->SetMipCount(mipCount); - } - - d3d9::D3DPOOL pool = d3d9::D3DPOOL_DEFAULT; - DWORD usage = 0; - - // General surface/texture pool placement - if (desc->ddsCaps.dwCaps & DDSCAPS_LOCALVIDMEM) - pool = d3d9::D3DPOOL_DEFAULT; - // There's no explicit non-local video memory placement - // per se, but D3DPOOL_MANAGED is close enough - else if (desc->ddsCaps.dwCaps & DDSCAPS_NONLOCALVIDMEM) - pool = d3d9::D3DPOOL_MANAGED; - else if (desc->ddsCaps.dwCaps & DDSCAPS_SYSTEMMEMORY) - // We can't know beforehand if a texture is or isn't going to be - // used in SetTexture() calls, and textures placed in D3DPOOL_SYSTEMMEM - // will not work in that context in dxvk, so revert to D3DPOOL_MANAGED. - pool = m_commonSurf->IsTexture() ? d3d9::D3DPOOL_MANAGED : d3d9::D3DPOOL_SYSTEMMEM; - - // Place all possible render targets in DEFAULT - // - // Note: This is somewhat problematic for textures and cube maps - // which will have D3DUSAGE_RENDERTARGET, but also need to have - // D3DUSAGE_DYNAMIC for locking/uploads to work. The flag combination - // isn't supported in D3D9, but we have a D3D7 exception in place. - // - if (m_commonSurf->IsRenderTarget() || initRT) { - Logger::debug("DDrawSurface::InitializeD3D9: Usage: D3DUSAGE_RENDERTARGET"); - pool = d3d9::D3DPOOL_DEFAULT; - usage |= D3DUSAGE_RENDERTARGET; - } - // All depth stencils will be created in DEFAULT - if (m_commonSurf->IsDepthStencil()) { - Logger::debug("DDrawSurface::InitializeD3D9: Usage: D3DUSAGE_DEPTHSTENCIL"); - pool = d3d9::D3DPOOL_DEFAULT; - usage |= D3DUSAGE_DEPTHSTENCIL; - } - - // General usage flags - if (m_commonSurf->IsTexture()) { - if (pool == d3d9::D3DPOOL_DEFAULT) { - Logger::debug("DDrawSurface::InitializeD3D9: Usage: D3DUSAGE_DYNAMIC"); - usage |= D3DUSAGE_DYNAMIC; - } - if (unlikely(m_commonIntf->GetOptions()->autoGenMipMaps)) { - Logger::debug("DDrawSurface::InitializeD3D9: Usage: D3DUSAGE_AUTOGENMIPMAP"); - usage |= D3DUSAGE_AUTOGENMIPMAP; - } - } - - const char* poolPlacement = pool == d3d9::D3DPOOL_DEFAULT ? "D3DPOOL_DEFAULT" : - pool == d3d9::D3DPOOL_SYSTEMMEM ? "D3DPOOL_SYSTEMMEM" : "D3DPOOL_MANAGED"; - - Logger::debug(str::format("DDrawSurface::InitializeD3D9: Placing in: ", poolPlacement)); - - // Use the MSAA type that was determined to be supported during device creation - const d3d9::D3DMULTISAMPLE_TYPE multiSampleType = m_commonIntf->GetCommonD3DDevice()->GetMultiSampleType(); - const uint32_t index = m_commonSurf->GetBackBufferIndex(); - - Com surf; - - HRESULT hr = DDERR_GENERIC; - - // Front Buffer - if (m_commonSurf->IsFrontBuffer()) { - Logger::debug("DDrawSurface::InitializeD3D9: Initializing front buffer..."); - - m_d3d9Device->GetBackBuffer(0, index, d3d9::D3DBACKBUFFER_TYPE_MONO, &surf); - - if (unlikely(surf == nullptr)) { - Logger::err("DDrawSurface::InitializeD3D9: Failed to retrieve front buffer"); - m_d3d9 = nullptr; - return hr; - } - - m_d3d9 = std::move(surf); - - // Back Buffer - } else if (m_commonSurf->IsBackBufferOrFlippable()) { - Logger::debug("DDrawSurface::InitializeD3D9: Initializing back buffer..."); - - m_d3d9Device->GetBackBuffer(0, index, d3d9::D3DBACKBUFFER_TYPE_MONO, &surf); - - if (unlikely(surf == nullptr)) { - Logger::err("DDrawSurface::InitializeD3D9: Failed to retrieve back buffer"); - m_d3d9 = nullptr; - return hr; - } - - m_d3d9 = std::move(surf); - - // Textures - } else if (m_commonSurf->IsTexture()) { - Logger::debug("DDrawSurface::InitializeD3D9: Initializing texture..."); - - Com tex; - - hr = m_d3d9Device->CreateTexture( - desc->dwWidth, desc->dwHeight, m_commonSurf->GetMipCount(), usage, - format, pool, &tex, nullptr); - - if (unlikely(FAILED(hr))) { - Logger::err("DDrawSurface::InitializeD3D9: Failed to create texture"); - m_texture9 = nullptr; - return hr; - } + if (unlikely(!m_commonSurf->IsInitialized())) { + if (m_commonSurf->IsTexture()) + UpdateMipMapCount(); - if (unlikely(m_commonIntf->GetOptions()->autoGenMipMaps)) - tex->SetAutoGenFilterType(d3d9::D3DTEXF_ANISOTROPIC); + const bool initRenderTarget = m_commonSurf->GetCommonD3DDevice()->IsCurrentRenderTarget(m_commonSurf.ptr()); - // Attach level 0 to this surface - tex->GetSurfaceLevel(0, &surf); - m_d3d9 = (std::move(surf)); - - Logger::debug("DDrawSurface::InitializeD3D9: Created texture"); - m_texture9 = std::move(tex); - - // Depth Stencil - } else if (m_commonSurf->IsDepthStencil()) { - Logger::debug("DDrawSurface::InitializeD3D9: Initializing depth stencil..."); - - hr = m_d3d9Device->CreateDepthStencilSurface( - desc->dwWidth, desc->dwHeight, format, - multiSampleType, 0, FALSE, &surf, nullptr); - - if (unlikely(FAILED(hr))) { - Logger::err("DDrawSurface::InitializeD3D9: Failed to create DS"); - m_d3d9 = nullptr; + HRESULT hr = m_commonSurf->InitializeD3D9(initRenderTarget); + if (unlikely(FAILED(hr))) return hr; - } - - Logger::debug("DDrawSurface::InitializeD3D9: Created depth stencil surface"); - - m_d3d9 = std::move(surf); - - // Offscreen Plain Surfaces - } else if (m_commonSurf->IsOffScreenPlainSurface()) { - Logger::debug("DDrawSurface::InitializeD3D9: Initializing offscreen plain surface..."); - - // Sometimes we get passed offscreen plain surfaces which should be tied to the back buffer, - // either as existing RTs or during SetRenderTarget() calls, which are tracked with initRT - if (unlikely(m_commonIntf->GetCommonD3DDevice()->IsCurrentRenderTarget(this) || initRT)) { - Logger::debug("DDrawSurface::InitializeD3D9: Offscreen plain surface is the RT"); - - m_d3d9Device->GetBackBuffer(0, index, d3d9::D3DBACKBUFFER_TYPE_MONO, &surf); - - if (unlikely(surf == nullptr)) { - Logger::err("DDrawSurface::InitializeD3D9: Failed to retrieve offscreen plain surface"); - m_d3d9 = nullptr; - return hr; - } - } else { - hr = m_d3d9Device->CreateOffscreenPlainSurface( - desc->dwWidth, desc->dwHeight, format, - pool, &surf, nullptr); - - if (unlikely(FAILED(hr))) { - Logger::err("DDrawSurface::InitializeD3D9: Failed to create offscreen plain surface"); - m_d3d9 = nullptr; - return hr; - } - } - m_d3d9 = std::move(surf); + Logger::debug(str::format("DDrawSurface::InitializeOrUploadD3D9: Initialized surface nr. [[1-", m_surfCount, "]]")); + } - // Overlays (haven't seen any actual use of overlays in the wild) - } else if (m_commonSurf->IsOverlay()) { - Logger::debug("DDrawSurface::InitializeD3D9: Initializing overlay..."); + if (likely(m_commonSurf->IsInitialized())) + return UploadSurfaceData(); - // Always link overlays to the back buffer - m_d3d9Device->GetBackBuffer(0, index, d3d9::D3DBACKBUFFER_TYPE_MONO, &surf); + return DD_OK; + } - if (unlikely(surf == nullptr)) { - Logger::err("DDrawSurface::InitializeD3D9: Failed to retrieve overlay surface"); - m_d3d9 = nullptr; - return hr; + void DDrawSurface::DownloadSurfaceData() { + // Some games, like The Settlers IV, use multiple devices for rendering, one to handle + // terrain and the overall 3D scene, and one to create textures/sprites to overlay on + // top of it. Since DXVK's D3D9 backend does not restrict cross-device surface/texture + // use, simply skip changing assigned surface devices during downloads. This is essentially + // a hack, which by some miracle works well enough in some cases, though may explode in others. + if (likely(!m_commonIntf->GetOptions()->deviceResourceSharing)) + m_commonSurf->RefreshD3D9Device(); + + if (unlikely(m_commonSurf->IsD3D9BackBuffer())) { + Logger::debug("DDrawSurface::DownloadSurfaceData: Surface is a bound swapchain surface"); + + if (m_commonSurf->IsInitialized() && m_commonSurf->IsD3D9SurfaceDirty()) { + Logger::debug(str::format("DDrawSurface::DownloadSurfaceData: Downloading nr. [[1-", m_surfCount, "]]")); + BlitToDDrawSurface(GetShadowOrProxied(), m_commonSurf->GetD3D9Surface(), + m_commonSurf->IsDXTFormat()); + m_commonSurf->UnDirtyD3D9Surface(); } - - m_d3d9 = std::move(surf); - - // Generic render target - } else if (m_commonSurf->IsRenderTarget()) { - Logger::debug("DDrawSurface::InitializeD3D9: Initializing render target..."); - - // Must be lockable for blitting to work. Note that - // D3D9 does not allow the creation of lockable RTs when - // using MSAA, but we have a D3D7 exception in place. - hr = m_d3d9Device->CreateRenderTarget( - desc->dwWidth, desc->dwHeight, format, - multiSampleType, usage, TRUE, &surf, nullptr); - - if (unlikely(FAILED(hr))) { - Logger::err("DDrawSurface::InitializeD3D9: Failed to create render target"); - m_d3d9 = nullptr; - return hr; + } else if (unlikely(m_commonSurf->IsD3D9DepthStencil())) { + Logger::debug("DDrawSurface::DownloadSurfaceData: Surface is a bound depth stencil"); + + if (m_commonSurf->IsInitialized() && m_commonSurf->IsD3D9SurfaceDirty()) { + Logger::debug(str::format("DDrawSurface::DownloadSurfaceData: Downloading nr. [[1-", m_surfCount, "]]")); + BlitToDDrawSurface(m_proxy.ptr(), m_commonSurf->GetD3D9Surface(), + m_commonSurf->IsDXTFormat()); + m_commonSurf->UnDirtyD3D9Surface(); } + } + } - m_d3d9 = std::move(surf); - - // We sometimes get generic surfaces, with only dimensions, format and placement info - } else if (!m_commonSurf->IsNotKnown()) { - Logger::debug("DDrawSurface::InitializeD3D9: Initializing generic surface..."); - - hr = m_d3d9Device->CreateOffscreenPlainSurface( - desc->dwWidth, desc->dwHeight, format, - pool, &surf, nullptr); - - if (unlikely(FAILED(hr))) { - Logger::err("DDrawSurface::InitializeD3D9: Failed to create offscreen plain surface"); - m_d3d9 = nullptr; - return hr; + void DDrawSurface::UpdateMipMapCount() { + // We need to count the number of actual mips on initialization by going through + // the mip chain, since the dwMipMapCount number may or may not be accurate. I am + // guessing it was intended more as a hint, not neceesarily a set number. + const DDSURFACEDESC* desc = m_commonSurf->GetDesc(); + + IDirectDrawSurface* mipMap = m_proxy.ptr(); + DDSURFACEDESC mipDesc; + uint16_t mipCount = 1; + + while (mipMap != nullptr) { + IDirectDrawSurface* parentSurface = mipMap; + mipMap = nullptr; + parentSurface->EnumAttachedSurfaces(&mipMap, ListMipChainSurfacesCallback); + if (mipMap != nullptr) { + mipCount++; + + mipDesc = { }; + mipDesc.dwSize = sizeof(DDSURFACEDESC2); + mipMap->GetSurfaceDesc(&mipDesc); + // Ignore multiple 1x1 mips, which apparently can get generated if the + // application gets the dwMipMapCount wrong vs surface dimensions. + if (unlikely(mipDesc.dwWidth == 1 && mipDesc.dwHeight == 1)) + break; } + } - Logger::debug("DDrawSurface::InitializeD3D9: Created offscreen plain surface"); + // Do not worry about maximum supported mip map levels validation, + // because D3D9 will handle this for us and cap them appropriately + if (mipCount > 1) { + Logger::debug(str::format("DDrawSurface::InitializeD3D9: Found ", mipCount, " mip levels")); - m_d3d9 = std::move(surf); - } else { - Logger::warn("DDrawSurface::InitializeD3D9: Skipping initialization of unknown surface"); + if (unlikely(mipCount != desc->dwMipMapCount)) + Logger::debug(str::format("DDrawSurface::InitializeD3D9: Mismatch with declared ", desc->dwMipMapCount, " mip levels")); } - // Depth stencils will not need uploads post initialization - if (likely(!m_commonSurf->IsDepthStencil())) - UploadSurfaceData(); + if (unlikely(m_commonIntf->GetOptions()->autoGenMipMaps)) { + Logger::debug("DDrawSurface::InitializeD3D9: Using auto mip map generation"); + mipCount = 0; + } - return DD_OK; + m_commonSurf->SetMipCount(mipCount); } inline HRESULT DDrawSurface::UploadSurfaceData() { - Logger::debug(str::format("DDrawSurface::UploadSurfaceData: Uploading nr. [[1-", m_surfCount, "]]")); - - if (unlikely(m_commonIntf->HasDrawn() && m_commonSurf->IsGuardableSurface())) { - Logger::debug("DDrawSurface::UploadSurfaceData: Skipping upload"); + // Fast skip + if (!m_commonSurf->IsDDrawSurfaceDirty()) return DD_OK; - } - const d3d9::D3DFORMAT format = m_commonSurf->GetD3D9Format(); + Logger::debug(str::format("DDrawSurface::UploadSurfaceData: Uploading nr. [[1-", m_surfCount, "]]")); if (m_commonSurf->IsTexture()) { - BlitToD3D9Texture(m_texture9.ptr(), format, - m_proxy.ptr(), m_commonSurf->GetMipCount()); + BlitToD3D9Texture(m_commonSurf->GetD3D9Texture(), m_proxy.ptr(), + m_commonSurf->GetMipCount(), m_commonSurf->IsDXTFormat()); // Blit surfaces directly } else { - if (unlikely(m_commonSurf->IsDepthStencil())) { - if (likely(m_commonIntf->GetOptions()->uploadDepthStencils)) { - Logger::debug("DDrawSurface::UploadSurfaceData: Uploading depth stencil"); - } else { - Logger::debug("DDrawSurface::UploadSurfaceData: Skipping upload of depth stencil"); - return DD_OK; - } - } + if (unlikely(m_commonSurf->IsDepthStencil())) + Logger::debug("DDrawSurface::UploadSurfaceData: Uploading depth stencil"); - BlitToD3D9Surface(m_d3d9.ptr(), format, m_proxy.ptr()); + BlitToD3D9Surface(m_commonSurf->GetD3D9Surface(), GetShadowOrProxied(), + m_commonSurf->IsDXTFormat()); } + m_commonSurf->UnDirtyDDrawSurface(); + return DD_OK; } @@ -1487,7 +1167,7 @@ namespace dxvk { bool rgbFallback = false; if (likely(!d3dOptions->forceSWVP)) { - if (riid == IID_IDirect3DHALDevice) { + if (riid == IID_IDirect3DHALDevice || riid == IID_WineD3DDevice) { Logger::info("DDrawSurface::CreateDeviceInternal: Creating an IID_IDirect3DHALDevice device"); deviceCreationFlags9 = D3DCREATE_MIXED_VERTEXPROCESSING; } else if (riid == IID_IDirect3DRGBDevice) { @@ -1514,15 +1194,14 @@ namespace dxvk { } Com ppvProxyObject; - HRESULT hr = m_proxy->QueryInterface(rclsidOverride, reinterpret_cast(&ppvProxyObject)); + HRESULT hr = GetShadowOrProxied()->QueryInterface(rclsidOverride, reinterpret_cast(&ppvProxyObject)); if (unlikely(FAILED(hr))) return hr; DWORD backBufferWidth = m_commonSurf->GetDesc()->dwWidth; DWORD BackBufferHeight = m_commonSurf->GetDesc()->dwHeight; - if (likely(!d3dOptions->forceProxiedPresent && - d3dOptions->backBufferResize)) { + if (likely(d3dOptions->backBufferResize)) { const bool exclusiveMode = m_commonIntf->GetCooperativeLevel() & DDSCL_EXCLUSIVE; // Ignore any mode size dimensions when in windowed present mode @@ -1539,6 +1218,27 @@ namespace dxvk { } } + const d3d9::D3DFORMAT backBufferFormat = m_commonSurf->GetD3D9Format(); + + const DWORD cooperativeLevel = m_commonIntf->GetCooperativeLevel(); + + if ((cooperativeLevel & DDSCL_MULTITHREADED) || d3dOptions->forceMultiThreaded) { + Logger::info("DDrawSurface::CreateDeviceInternal: Using thread safe runtime synchronization"); + deviceCreationFlags9 |= D3DCREATE_MULTITHREADED; + } + // DDSCL_FPUPRESERVE does not exist prior to DDraw7, + // and DDSCL_FPUSETUP is NOT the default state + if (!(cooperativeLevel & DDSCL_FPUSETUP)) + deviceCreationFlags9 |= D3DCREATE_FPU_PRESERVE; + if (cooperativeLevel & DDSCL_NOWINDOWCHANGES) + deviceCreationFlags9 |= D3DCREATE_NOWINDOWCHANGES; + + Logger::info(str::format("DDrawSurface::CreateDeviceInternal: Back buffer size: ", backBufferWidth, "x", BackBufferHeight)); + + const DWORD backBufferCount = DetermineBackBufferCount(m_proxy.ptr()); + // Consider the front buffer as well when reporting the overall count + Logger::info(str::format("DDrawSurface::CreateDeviceInternal: Back buffer count: ", backBufferCount + 1)); + Com d3d9Intf; // D3D3 is "special", so we might not have a valid D3D3 interface to work with // at this point. Create a temporary D3D9 interface should that ever happen. @@ -1556,66 +1256,18 @@ namespace dxvk { d3d9Bridge->EnableD3D3CompatibilityMode(); } else { - d3d9Intf = d3d3Intf->GetD3D9(); + d3d9Intf = d3d3Intf->GetCommonD3DInterface()->GetD3D9Interface(); } // Determine the supported AA sample count by querying the D3D9 interface - d3d9::D3DMULTISAMPLE_TYPE multiSampleType = d3d9::D3DMULTISAMPLE_NONE; - if (likely(d3dOptions->emulateFSAA != FSAAEmulation::Disabled)) { - HRESULT hr4S = d3d9Intf->CheckDeviceMultiSampleType(0, d3d9::D3DDEVTYPE_HAL, m_commonSurf->GetD3D9Format(), - TRUE, d3d9::D3DMULTISAMPLE_4_SAMPLES, NULL); - if (unlikely(FAILED(hr4S))) { - HRESULT hr2S = d3d9Intf->CheckDeviceMultiSampleType(0, d3d9::D3DDEVTYPE_HAL, m_commonSurf->GetD3D9Format(), - TRUE, d3d9::D3DMULTISAMPLE_2_SAMPLES, NULL); - if (unlikely(FAILED(hr2S))) { - Logger::warn("DDrawSurface::CreateDeviceInternal: No MSAA support has been detected"); - } else { - Logger::info("DDrawSurface::CreateDeviceInternal: Using 2x MSAA for FSAA emulation"); - multiSampleType = d3d9::D3DMULTISAMPLE_2_SAMPLES; - } - } else { - Logger::info("DDrawSurface::CreateDeviceInternal: Using 4x MSAA for FSAA emulation"); - multiSampleType = d3d9::D3DMULTISAMPLE_4_SAMPLES; - } - } else { - Logger::info("DDrawSurface::CreateDeviceInternal: FSAA emulation is disabled"); - } - - const DWORD cooperativeLevel = m_commonIntf->GetCooperativeLevel(); - - if ((cooperativeLevel & DDSCL_MULTITHREADED) || d3dOptions->forceMultiThreaded) { - Logger::info("DDrawSurface::CreateDeviceInternal: Using thread safe runtime synchronization"); - deviceCreationFlags9 |= D3DCREATE_MULTITHREADED; - } - // DDSCL_FPUPRESERVE does not exist prior to DDraw7, - // and DDSCL_FPUSETUP is NOT the default state - if (!(cooperativeLevel & DDSCL_FPUSETUP)) - deviceCreationFlags9 |= D3DCREATE_FPU_PRESERVE; - if (cooperativeLevel & DDSCL_NOWINDOWCHANGES) - deviceCreationFlags9 |= D3DCREATE_NOWINDOWCHANGES; - - Logger::info(str::format("DDrawSurface::CreateDeviceInternal: Back buffer size: ", backBufferWidth, "x", BackBufferHeight)); - - DWORD backBufferCount = 0; - if (likely(!d3dOptions->forceSingleBackBuffer)) { - IDirectDrawSurface* backBuffer = m_proxy.ptr(); - while (backBuffer != nullptr) { - IDirectDrawSurface* parentSurface = backBuffer; - backBuffer = nullptr; - parentSurface->EnumAttachedSurfaces(&backBuffer, ListBackBufferSurfacesCallback); - backBufferCount++; - // the swapchain will eventually return to its origin - if (backBuffer == m_proxy.ptr()) - break; - } - } - // Consider the front buffer as well when reporting the overall count - Logger::info(str::format("DDrawSurface::CreateDeviceInternal: Back buffer count: ", backBufferCount + 1)); + const d3d9::D3DMULTISAMPLE_TYPE multiSampleType = d3dOptions->emulateFSAA != FSAAEmulation::Disabled ? + GetSupportedMultiSampleType(d3d9Intf.ptr(), backBufferFormat) : + d3d9::D3DMULTISAMPLE_NONE; d3d9::D3DPRESENT_PARAMETERS params; params.BackBufferWidth = backBufferWidth; params.BackBufferHeight = BackBufferHeight; - params.BackBufferFormat = m_commonSurf->GetD3D9Format(); + params.BackBufferFormat = backBufferFormat; params.BackBufferCount = backBufferCount; params.MultiSampleType = multiSampleType; params.MultiSampleQuality = 0; @@ -1657,19 +1309,34 @@ namespace dxvk { return DD_OK; } - inline void DDrawSurface::RefreshD3D9Device() { - D3DCommonDevice* commonDevice = m_commonIntf->GetCommonD3DDevice(); + inline DWORD DDrawSurface::DetermineBackBufferCount(IDirectDrawSurface* renderTarget) { + DWORD backBufferCount = 0; - d3d9::IDirect3DDevice9* d3d9Device = commonDevice != nullptr ? commonDevice->GetD3D9Device() : nullptr; - if (unlikely(m_d3d9Device != d3d9Device)) { - // Check if the device has been recreated and reset all D3D9 resources - if (m_d3d9Device != nullptr) { - Logger::debug("DDrawSurface: Device context has changed, clearing all D3D9 resources"); - m_d3d9 = nullptr; - } + const D3DOptions* d3dOptions = m_commonIntf->GetOptions(); + + if (likely(!d3dOptions->forceSingleBackBuffer && !d3dOptions->forceLegacyPresent)) { + IDirectDrawSurface* backBuffer = renderTarget; + HRESULT hr; + + while (backBuffer != nullptr) { + IDirectDrawSurface* parentSurface = backBuffer; + backBuffer = nullptr; - m_d3d9Device = d3d9Device; + hr = parentSurface->EnumAttachedSurfaces(&backBuffer, ListBackBufferSurfacesCallback); + if (unlikely(FAILED(hr))) { + Logger::warn("DDrawSurface::DetermineBackBufferCount: Unable to enumerate attached surfaces"); + break; + } + + backBufferCount++; + + // the swapchain will eventually return to its origin + if (backBuffer == renderTarget) + break; + } } + + return backBufferCount; } } diff --git a/src/ddraw/ddraw/ddraw_surface.h b/src/ddraw/ddraw/ddraw_surface.h index 6100c51c9ae..5dd6d89443c 100644 --- a/src/ddraw/ddraw/ddraw_surface.h +++ b/src/ddraw/ddraw/ddraw_surface.h @@ -21,7 +21,7 @@ namespace dxvk { /** * \brief IDirectDrawSurface interface implementation */ - class DDrawSurface final : public DDrawWrappedObject { + class DDrawSurface final : public DDrawWrappedObject { public: @@ -102,6 +102,26 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE UpdateOverlayZOrder(DWORD dwFlags, LPDIRECTDRAWSURFACE lpDDSReference); + IDirectDrawSurface* GetShadowOrProxied(); + + HRESULT InitializeD3D9RenderTarget(); + + HRESULT InitializeD3D9DepthStencil(); + + HRESULT InitializeOrUploadD3D9(); + + void DownloadSurfaceData(); + + void UpdateMipMapCount(); + + void SetShadowSurface(Com&& shadowSurf) { + m_shadowSurf = shadowSurf; + } + + DDrawSurface* GetShadowSurface() const { + return m_shadowSurf.ptr(); + } + DDrawCommonSurface* GetCommonSurface() const { return m_commonSurf.ptr(); } @@ -110,12 +130,8 @@ namespace dxvk { return m_commonIntf; } - d3d9::IDirect3DDevice9* GetD3D9Device() const { - return m_d3d9Device; - } - - d3d9::IDirect3DTexture9* GetD3D9Texture() const { - return m_texture9.ptr(); + DDrawSurface* GetNextFlippable() const { + return m_nextFlippable; } D3D3Texture* GetD3D3Texture() const { @@ -134,6 +150,10 @@ namespace dxvk { m_texture5 = texture5; } + void SetAttachedDepthStencil(Com&& depthStencil) { + m_depthStencil = depthStencil; + } + DDrawSurface* GetAttachedDepthStencil() { // Fast path, since in most cases we already store the required surface if (likely(m_depthStencil.ptr() != nullptr)) @@ -165,42 +185,36 @@ namespace dxvk { m_commonSurf->SetIsAttached(false); } - HRESULT InitializeD3D9RenderTarget(); - - HRESULT InitializeD3D9DepthStencil(); - - HRESULT InitializeOrUploadD3D9(); - - HRESULT InitializeD3D9(const bool initRT); - private: inline HRESULT UploadSurfaceData(); inline HRESULT CreateDeviceInternal(REFIID riid, void** ppvObject); - inline void RefreshD3D9Device(); + inline DWORD DetermineBackBufferCount(IDirectDrawSurface* renderTarget); - bool m_isChildObject = true; + bool m_isChildObject = true; static uint32_t s_surfCount; - uint32_t m_surfCount = 0; + uint32_t m_surfCount = 0; - Com m_commonSurf; - DDrawCommonInterface* m_commonIntf = nullptr; + Com m_commonSurf; + DDrawCommonInterface* m_commonIntf = nullptr; - DDrawSurface* m_parentSurf = nullptr; + DDrawSurface* m_parentSurf = nullptr; - d3d9::IDirect3DDevice9* m_d3d9Device = nullptr; + Com m_texture3; + Com m_texture5; - Com m_texture3; - Com m_texture5; + DDrawSurface* m_nextFlippable = nullptr; - Com m_texture9; + // Offscreen plain surface we use to mask unwanted DDraw interactions, such + // as forced swapchain presents caused by blits/locks on primary surfaces + Com m_shadowSurf; // Back buffers will have depth stencil surfaces as attachments (in practice // I have never seen more than one depth stencil being attached at a time) - Com m_depthStencil; + Com m_depthStencil; // These are attached surfaces, which are typically mips or other types of generated // surfaces, which need to exist for the entire lifecycle of their parent surface. diff --git a/src/ddraw/ddraw2/ddraw2_interface.cpp b/src/ddraw/ddraw2/ddraw2_interface.cpp index a33e13dfd12..f677c914513 100644 --- a/src/ddraw/ddraw2/ddraw2_interface.cpp +++ b/src/ddraw/ddraw2/ddraw2_interface.cpp @@ -20,7 +20,7 @@ namespace dxvk { DDraw2Interface::DDraw2Interface( DDrawCommonInterface* commonIntf, Com&& proxyIntf) - : DDrawWrappedObject(nullptr, std::move(proxyIntf), nullptr) + : DDrawWrappedObject(nullptr, std::move(proxyIntf)) , m_commonIntf ( commonIntf ) { // Hold a reference to the parent IDirectDraw object, since // it is needed to be able to create surfaces from this interface @@ -34,12 +34,6 @@ namespace dxvk { m_commonIntf->SetDD2Interface(this); - static bool s_apitraceModeWarningShown; - - if (unlikely(m_commonIntf->GetOptions()->apitraceMode && - !std::exchange(s_apitraceModeWarningShown, true))) - Logger::warn("DDraw2Interface: Apitrace mode is enabled. Performance will be suboptimal!"); - m_intfCount = ++s_intfCount; Logger::debug(str::format("DDraw2Interface: Created a new interface nr. <<2-", m_intfCount, ">>")); @@ -289,8 +283,40 @@ namespace dxvk { // Surfaces created from IDirectDraw and IDirectDraw2 do not ref their parent interfaces Com surface = new DDrawSurface(nullptr, std::move(ddrawSurfaceProxied), m_commonIntf->GetDDInterface(), nullptr, false); - if (unlikely(lpDDSurfaceDesc->ddsCaps.dwCaps & DDSCAPS_PRIMARYSURFACE)) + + if (unlikely(lpDDSurfaceDesc->ddsCaps.dwCaps & DDSCAPS_PRIMARYSURFACE)) { m_commonIntf->SetPrimarySurface(surface->GetCommonSurface()); + + // Shadow surface creation for the primary surface + // (it needs to be based on the same incoming desc) + if (unlikely(!surface->GetCommonSurface()->Is8BitFormat() && + m_commonIntf->GetOptions()->forceLegacyPresent)) { + Logger::debug("DDraw2Interface::CreateSurface: Creating shadow surface"); + + DDSURFACEDESC shadowDesc = *lpDDSurfaceDesc; + const DDSURFACEDESC* primaryDesc = surface->GetCommonSurface()->GetDesc(); + + shadowDesc.ddsCaps.dwCaps &= ~DDSCAPS_PRIMARYSURFACE & ~DDSCAPS_FRONTBUFFER & ~DDSCAPS_COMPLEX & ~DDSCAPS_FLIP; + shadowDesc.ddsCaps.dwCaps |= DDSCAPS_OFFSCREENPLAIN; + shadowDesc.dwFlags &= ~DDSD_BACKBUFFERCOUNT; + // Dimensions aren't specified in the incoming desc, + // but are explicitly needed for non-primary surfaces + shadowDesc.dwFlags |= DDSD_WIDTH | DDSD_HEIGHT; + shadowDesc.dwWidth = primaryDesc->dwWidth; + shadowDesc.dwHeight = primaryDesc->dwHeight; + + Com ddrawSurfaceShadow; + hr = m_proxy->CreateSurface(&shadowDesc, &ddrawSurfaceShadow, pUnkOuter); + if (unlikely(FAILED(hr))) { + Logger::warn("DDraw2Interface::CreateSurface: Failed to create shadow surface"); + } else { + Com shadowSurf = new DDrawSurface(nullptr, std::move(ddrawSurfaceShadow), + m_commonIntf->GetDDInterface(), nullptr, false); + surface->SetShadowSurface(std::move(shadowSurf)); + } + } + } + *lplpDDSurface = surface.ref(); } catch (const DxvkError& e) { Logger::err(e.message()); @@ -350,11 +376,6 @@ namespace dxvk { } HRESULT STDMETHODCALLTYPE DDraw2Interface::FlipToGDISurface() { - if (unlikely(m_commonIntf->GetOptions()->forceProxiedPresent)) { - Logger::debug("<<< DDraw2Interface::FlipToGDISurface: Proxy"); - return m_proxy->FlipToGDISurface(); - } - Logger::debug("*** DDraw2Interface::FlipToGDISurface: Ignoring"); DDrawCommonSurface* ps = m_commonIntf->GetPrimarySurface(); @@ -372,6 +393,18 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE DDraw2Interface::GetCaps(LPDDCAPS lpDDDriverCaps, LPDDCAPS lpDDHELCaps) { Logger::debug("<<< DDraw2Interface::GetCaps: Proxy"); + if (unlikely(lpDDDriverCaps == nullptr && lpDDHELCaps == nullptr)) + return DDERR_INVALIDPARAMS; + + // Interstate '76 sends invalid dwSizes part of the structs, + // and that explodes in Wine, so validate it before proxying + + if (unlikely(lpDDDriverCaps != nullptr && !IsValidDDrawCapsSize(lpDDDriverCaps->dwSize))) + return DDERR_INVALIDPARAMS; + + if (unlikely(lpDDHELCaps != nullptr && !IsValidDDrawCapsSize(lpDDHELCaps->dwSize))) + return DDERR_INVALIDPARAMS; + HRESULT hr = m_proxy->GetCaps(lpDDDriverCaps, lpDDHELCaps); if (unlikely(FAILED(hr))) return hr; @@ -571,8 +604,7 @@ namespace dxvk { Logger::warn("DDraw2Interface::SetDisplayMode: Failed to update primary surface desc"); } - if (likely(!m_commonIntf->GetOptions()->forceProxiedPresent && - m_commonIntf->GetOptions()->backBufferResize)) { + if (likely(m_commonIntf->GetOptions()->backBufferResize)) { const bool exclusiveMode = m_commonIntf->GetCooperativeLevel() & DDSCL_EXCLUSIVE; // Ignore any mode size dimensions when in windowed present mode @@ -590,11 +622,6 @@ namespace dxvk { } HRESULT STDMETHODCALLTYPE DDraw2Interface::WaitForVerticalBlank(DWORD dwFlags, HANDLE hEvent) { - if (unlikely(m_commonIntf->GetOptions()->forceProxiedPresent)) { - Logger::debug("<<< DDraw2Interface::WaitForVerticalBlank: Proxy"); - m_proxy->WaitForVerticalBlank(dwFlags, hEvent); - } - Logger::debug(">>> DDraw2Interface::WaitForVerticalBlank"); if (unlikely(dwFlags & DDWAITVB_BLOCKBEGINEVENT)) diff --git a/src/ddraw/ddraw2/ddraw2_interface.h b/src/ddraw/ddraw2/ddraw2_interface.h index 8c7d1824f03..9b58742da69 100644 --- a/src/ddraw/ddraw2/ddraw2_interface.h +++ b/src/ddraw/ddraw2/ddraw2_interface.h @@ -18,7 +18,7 @@ namespace dxvk { /** * \brief DirectDraw2 interface implementation */ - class DDraw2Interface final : public DDrawWrappedObject { + class DDraw2Interface final : public DDrawWrappedObject { public: DDraw2Interface( diff --git a/src/ddraw/ddraw2/ddraw2_surface.cpp b/src/ddraw/ddraw2/ddraw2_surface.cpp index 41da66b9c6b..841df6763f3 100644 --- a/src/ddraw/ddraw2/ddraw2_surface.cpp +++ b/src/ddraw/ddraw2/ddraw2_surface.cpp @@ -5,7 +5,6 @@ #include "../ddraw_gamma.h" #include "../ddraw/ddraw_interface.h" -#include "../ddraw/ddraw_surface.h" #include "../ddraw2/ddraw3_surface.h" #include "../ddraw4/ddraw4_surface.h" #include "../ddraw7/ddraw7_surface.h" @@ -23,7 +22,7 @@ namespace dxvk { Com&& surfProxy, DDrawSurface* pParent, DDraw2Surface* pParentSurf) - : DDrawWrappedObject(pParent, std::move(surfProxy), nullptr) + : DDrawWrappedObject(pParent, std::move(surfProxy)) , m_commonSurf ( commonSurf ) , m_parentSurf ( pParentSurf ) { if (m_parent != nullptr) { @@ -66,6 +65,26 @@ namespace dxvk { m_originSurf = m_parent; } + // Retrieve and cache the shadow surface + if (likely(m_shadowSurf == nullptr)) { + IUnknown* shadowSurfaceProxiedOrigin = m_commonSurf->GetShadowSurfaceProxied(); + if (likely(shadowSurfaceProxiedOrigin != nullptr)) { + Logger::debug("DDraw2Surface: Retrieving shadow surface from origin"); + + Com shadowSurfProxy; + HRESULT hr = shadowSurfaceProxiedOrigin->QueryInterface(__uuidof(IDirectDrawSurface2), + reinterpret_cast(&shadowSurfProxy)); + if (unlikely(FAILED(hr))) { + throw DxvkError("DDraw2Surface: ERROR! Failed to retrieve the shadow surface!"); + } else { + if (shadowSurfProxy != nullptr) { + m_shadowSurf = new DDraw2Surface(m_commonSurf->GetShadowCommonSurface(), std::move(shadowSurfProxy), + m_commonSurf->GetDDSurface(), nullptr); + } + } + } + } + m_commonIntf->AddWrappedSurface(this); m_commonSurf->SetDD2Surface(this); @@ -242,13 +261,11 @@ namespace dxvk { DDraw2Surface* ddraw2Surface = static_cast(lpDDSAttachedSurface); + // Unsupported by Wine's DDraw implementation, so we'll do our own present if (unlikely(ddraw2Surface->GetCommonSurface()->IsBackBufferOrFlippable())) { - if (unlikely(m_commonIntf->GetOptions()->forceBlitOnFlip)) { - Logger::debug("DDraw2Surface::AddAttachedSurface: Caching surface as RT"); - m_commonIntf->SetDDrawRenderTarget(ddraw2Surface->GetCommonSurface()); - } else { - Logger::warn("DDraw2Surface::AddAttachedSurface: Trying to attach a flippable surface"); - } + Logger::debug("DDraw2Surface::AddAttachedSurface: Caching surface as DDraw RT"); + m_commonIntf->SetDDrawRenderTarget(ddraw2Surface->GetCommonSurface()); + return DD_OK; } HRESULT hr = m_proxy->AddAttachedSurface(ddraw2Surface->GetProxied()); @@ -272,89 +289,31 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE DDraw2Surface::Blt(LPRECT lpDestRect, LPDIRECTDRAWSURFACE2 lpDDSrcSurface, LPRECT lpSrcRect, DWORD dwFlags, LPDDBLTFX lpDDBltFx) { Logger::debug("<<< DDraw2Surface::Blt: Proxy"); - // Write back any flippable surfaces or depth stencils from D3D9 + // Write back any dirty surface data from bound D3D9 back buffers or + // depth stencils, for both the source surface and the current surface if (likely(lpDDSrcSurface != nullptr && m_commonIntf->IsWrappedSurface(lpDDSrcSurface))) { - DDraw2Surface* sourceSurface = static_cast(lpDDSrcSurface); - if (unlikely(sourceSurface->GetCommonSurface()->IsGuardableSurface())) { - if (m_commonIntf->GetOptions()->backBufferWriteBack || m_commonIntf->GetOptions()->apitraceMode) { - Logger::debug("DDraw2Surface::Blt: Source surface is a swapchain surface"); - - if (unlikely(m_commonIntf->GetOptions()->apitraceMode && !sourceSurface->IsInitialized())) - sourceSurface->InitializeOrUploadD3D9(); - - if (likely(sourceSurface->IsInitialized())) - BlitToDDrawSurface(m_parent->GetProxied(), m_parent->GetD3D9()); - } else { - static bool s_swapchainWarningShown; - - if (!std::exchange(s_swapchainWarningShown, true)) - Logger::warn("DDraw2Surface::Blt: Source surface is a swapchain surface"); - } - } else if (unlikely(sourceSurface->GetCommonSurface()->IsDepthStencil())) { - if (m_commonIntf->GetOptions()->depthWriteBack || m_commonIntf->GetOptions()->apitraceMode) { - Logger::debug("DDraw2Surface::Blt: Source surface is a depth stencil"); - - if (likely(sourceSurface->IsInitialized())) - BlitToDDrawSurface(m_parent->GetProxied(), m_parent->GetD3D9()); - } else { - static bool s_depthStencilWarningShown; - - if (!std::exchange(s_depthStencilWarningShown, true)) - Logger::warn("DDraw2Surface::Blt: Source surface is a depth stencil"); - } - } + DDraw2Surface* surface2 = static_cast(lpDDSrcSurface); + surface2->DownloadSurfaceData(); } + DownloadSurfaceData(); - RefreshD3D9Device(); - if (likely(m_d3d9Device != nullptr)) { - // Forward DDBLT_DEPTHFILL clears to D3D9 if done on the current depth stencil - if (unlikely(lpDDSrcSurface == nullptr && - (dwFlags & DDBLT_DEPTHFILL) && - lpDDBltFx != nullptr && - m_commonIntf->GetCommonD3DDevice()->IsCurrentD3D9DepthStencil(m_d3d9.ptr()))) { - Logger::debug("DDraw2Surface::Blt: Clearing d3d9 depth stencil"); - - HRESULT hrClear; - const float zClear = m_commonSurf->GetNormalizedFloatDepth(lpDDBltFx->dwFillDepth); - - if (lpDestRect == nullptr) { - hrClear = m_d3d9Device->Clear(0, NULL, D3DCLEAR_ZBUFFER, 0, zClear, 256); - } else { - hrClear = m_d3d9Device->Clear(1, reinterpret_cast(lpDestRect), D3DCLEAR_ZBUFFER, 0, zClear, 0); - } - if (unlikely(FAILED(hrClear))) - Logger::warn("DDraw2Surface::Blt: Failed to clear d3d9 depth"); - } - // Forward DDBLT_COLORFILL clears to D3D9 if done on the current render target - if (unlikely(lpDDSrcSurface == nullptr && - (dwFlags & DDBLT_COLORFILL) && - lpDDBltFx != nullptr && - m_commonIntf->GetCommonD3DDevice()->IsCurrentD3D9RenderTarget(m_d3d9.ptr()))) { - Logger::debug("DDraw2Surface::Blt: Clearing d3d9 render target"); - - HRESULT hrClear; - if (lpDestRect == nullptr) { - hrClear = m_d3d9Device->Clear(0, NULL, D3DCLEAR_TARGET, lpDDBltFx->dwFillColor, 0.0f, 0); - } else { - hrClear = m_d3d9Device->Clear(1, reinterpret_cast(lpDestRect), D3DCLEAR_TARGET, lpDDBltFx->dwFillColor, 0.0f, 0); - } - if (unlikely(FAILED(hrClear))) - Logger::warn("DDraw2Surface::Blt: Failed to clear d3d9 render target"); - } - + d3d9::IDirect3DDevice9* d3d9Device = m_commonSurf->RefreshD3D9Device(); + if (likely(d3d9Device != nullptr)) { const bool exclusiveMode = (m_commonIntf->GetCooperativeLevel() & DDSCL_EXCLUSIVE) && !m_commonIntf->GetOptions()->ignoreExclusiveMode; - // Eclusive mode back buffer guard - if (exclusiveMode && m_commonIntf->HasDrawn() && - m_commonSurf->IsGuardableSurface() && - m_commonIntf->GetOptions()->backBufferGuard != D3DBackBufferGuard::Disabled) { - return DD_OK; // Windowed mode presentation path - } else if (!exclusiveMode && m_commonIntf->HasDrawn() && m_commonSurf->IsPrimarySurface()) { - m_commonIntf->ResetDrawTracking(); - m_d3d9Device->Present(NULL, NULL, NULL, NULL); - return DD_OK; + if (!exclusiveMode && lpDDSrcSurface != nullptr && m_commonSurf->IsPrimarySurface()) { + // TODO: Handle this properly, not by uploading the RT again + DDraw2Surface* ddraw2Surface = static_cast(lpDDSrcSurface); + DDrawSurface* ddrawSurface = ddraw2Surface->GetCommonSurface()->GetDDSurface(); + DDrawSurface* renderTarget = m_commonSurf->GetCommonD3DDevice()->GetCurrentRenderTarget(); + + if (ddrawSurface == renderTarget) { + renderTarget->InitializeOrUploadD3D9(); + d3d9Device->Present(NULL, NULL, NULL, NULL); + return DD_OK; + } } } @@ -365,20 +324,24 @@ namespace dxvk { Logger::warn("DDraw2Surface::Blt: Received an unwrapped source surface"); return DDERR_UNSUPPORTED; } - hr = m_proxy->Blt(lpDestRect, lpDDSrcSurface, lpSrcRect, dwFlags, lpDDBltFx); + hr = GetShadowOrProxied()->Blt(lpDestRect, lpDDSrcSurface, lpSrcRect, dwFlags, lpDDBltFx); } else { DDraw2Surface* ddraw2Surface = static_cast(lpDDSrcSurface); - hr = m_proxy->Blt(lpDestRect, ddraw2Surface->GetProxied(), lpSrcRect, dwFlags, lpDDBltFx); + hr = GetShadowOrProxied()->Blt(lpDestRect, ddraw2Surface->GetShadowOrProxied(), lpSrcRect, dwFlags, lpDDBltFx); } if (likely(SUCCEEDED(hr))) { - // Textures get uploaded during SetTexture calls - if (m_commonSurf->IsTexture()) { - HRESULT hrUpload = InitializeOrUploadD3D9(); - if (unlikely(FAILED(hrUpload))) - Logger::warn("DDraw2Surface::Blt: Failed upload to d3d9 surface"); - } else { - m_commonSurf->DirtyMipMaps(); + m_commonSurf->DirtyDDrawSurface(); + + if (unlikely(m_shadowSurf != nullptr && d3d9Device != nullptr)) { + const bool shouldPresent = m_commonIntf->GetOptions()->legacyPresentGuard == D3DLegacyPresentGuard::Auto ? + !m_commonSurf->GetCommonD3DDevice()->IsInScene() : + m_commonIntf->GetOptions()->legacyPresentGuard == D3DLegacyPresentGuard::Strict ? + false : true; + if (shouldPresent) { + InitializeOrUploadD3D9(); + d3d9Device->Present(NULL, NULL, NULL, NULL); + } } } @@ -394,54 +357,31 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE DDraw2Surface::BltFast(DWORD dwX, DWORD dwY, LPDIRECTDRAWSURFACE2 lpDDSrcSurface, LPRECT lpSrcRect, DWORD dwTrans) { Logger::debug("<<< DDraw2Surface::BltFast: Proxy"); - // Write back any flippable surfaces or depth stencils from D3D9 + // Write back any dirty surface data from bound D3D9 back buffers or + // depth stencils, for both the source surface and the current surface if (likely(lpDDSrcSurface != nullptr && m_commonIntf->IsWrappedSurface(lpDDSrcSurface))) { - DDraw2Surface* sourceSurface = static_cast(lpDDSrcSurface); - if (unlikely(sourceSurface->GetCommonSurface()->IsGuardableSurface())) { - if (m_commonIntf->GetOptions()->backBufferWriteBack || m_commonIntf->GetOptions()->apitraceMode) { - Logger::debug("DDraw2Surface::BltFast: Source surface is a swapchain surface"); - - if (unlikely(m_commonIntf->GetOptions()->apitraceMode && !sourceSurface->IsInitialized())) - sourceSurface->InitializeOrUploadD3D9(); - - if (likely(sourceSurface->IsInitialized())) - BlitToDDrawSurface(m_parent->GetProxied(), m_parent->GetD3D9()); - } else { - static bool s_swapchainWarningShown; - - if (!std::exchange(s_swapchainWarningShown, true)) - Logger::warn("DDraw2Surface::BltFast: Source surface is a swapchain surface"); - } - } else if (unlikely(sourceSurface->GetCommonSurface()->IsDepthStencil())) { - if (m_commonIntf->GetOptions()->depthWriteBack || m_commonIntf->GetOptions()->apitraceMode) { - Logger::debug("DDraw2Surface::BltFast: Source surface is a depth stencil"); - - if (likely(sourceSurface->IsInitialized())) - BlitToDDrawSurface(m_parent->GetProxied(), m_parent->GetD3D9()); - } else { - static bool s_depthStencilWarningShown; - - if (!std::exchange(s_depthStencilWarningShown, true)) - Logger::warn("DDraw2Surface::BltFast: Source surface is a depth stencil"); - } - } + DDraw2Surface* surface2 = static_cast(lpDDSrcSurface); + surface2->DownloadSurfaceData(); } + DownloadSurfaceData(); - RefreshD3D9Device(); - if (likely(m_d3d9Device != nullptr)) { + d3d9::IDirect3DDevice9* d3d9Device = m_commonSurf->RefreshD3D9Device(); + if (likely(d3d9Device != nullptr)) { const bool exclusiveMode = (m_commonIntf->GetCooperativeLevel() & DDSCL_EXCLUSIVE) && !m_commonIntf->GetOptions()->ignoreExclusiveMode; - // Eclusive mode back buffer guard - if (exclusiveMode && m_commonIntf->HasDrawn() && - m_commonSurf->IsGuardableSurface() && - m_commonIntf->GetOptions()->backBufferGuard != D3DBackBufferGuard::Disabled) { - return DD_OK; // Windowed mode presentation path - } else if (!exclusiveMode && m_commonIntf->HasDrawn() && m_commonSurf->IsPrimarySurface()) { - m_commonIntf->ResetDrawTracking(); - m_d3d9Device->Present(NULL, NULL, NULL, NULL); - return DD_OK; + if (!exclusiveMode && lpDDSrcSurface != nullptr && m_commonSurf->IsPrimarySurface()) { + // TODO: Handle this properly, not by uploading the RT again + DDraw2Surface* ddraw2Surface = static_cast(lpDDSrcSurface); + DDrawSurface* ddrawSurface = ddraw2Surface->GetCommonSurface()->GetDDSurface(); + DDrawSurface* renderTarget = m_commonSurf->GetCommonD3DDevice()->GetCurrentRenderTarget(); + + if (ddrawSurface == renderTarget) { + renderTarget->InitializeOrUploadD3D9(); + d3d9Device->Present(NULL, NULL, NULL, NULL); + return DD_OK; + } } } @@ -452,20 +392,24 @@ namespace dxvk { Logger::warn("DDraw2Surface::BltFast: Received an unwrapped source surface"); return DDERR_UNSUPPORTED; } - hr = m_proxy->BltFast(dwX, dwY, lpDDSrcSurface, lpSrcRect, dwTrans); + hr = GetShadowOrProxied()->BltFast(dwX, dwY, lpDDSrcSurface, lpSrcRect, dwTrans); } else { DDraw2Surface* ddraw2Surface = static_cast(lpDDSrcSurface); - hr = m_proxy->BltFast(dwX, dwY, ddraw2Surface->GetProxied(), lpSrcRect, dwTrans); + hr = GetShadowOrProxied()->BltFast(dwX, dwY, ddraw2Surface->GetShadowOrProxied(), lpSrcRect, dwTrans); } if (likely(SUCCEEDED(hr))) { - // Textures get uploaded during SetTexture calls - if (m_commonSurf->IsTexture()) { - HRESULT hrUpload = InitializeOrUploadD3D9(); - if (unlikely(FAILED(hrUpload))) - Logger::warn("DDraw2Surface::BltFast: Failed upload to d3d9 surface"); - } else { - m_commonSurf->DirtyMipMaps(); + m_commonSurf->DirtyDDrawSurface(); + + if (unlikely(m_shadowSurf != nullptr && d3d9Device != nullptr)) { + const bool shouldPresent = m_commonIntf->GetOptions()->legacyPresentGuard == D3DLegacyPresentGuard::Auto ? + !m_commonSurf->GetCommonD3DDevice()->IsInScene() : + m_commonIntf->GetOptions()->legacyPresentGuard == D3DLegacyPresentGuard::Strict ? + false : true; + if (shouldPresent) { + InitializeOrUploadD3D9(); + d3d9Device->Present(NULL, NULL, NULL, NULL); + } } } @@ -492,6 +436,13 @@ namespace dxvk { DDraw2Surface* ddraw2Surface = static_cast(lpDDSAttachedSurface); + if (unlikely(ddraw2Surface->GetCommonSurface()->IsBackBufferOrFlippable() && + ddraw2Surface->GetCommonSurface() == m_commonIntf->GetDDrawRenderTarget())) { + Logger::debug("DDraw2Surface::DeleteAttachedSurface: Removing cached DDraw RT surface"); + m_commonIntf->SetDDrawRenderTarget(nullptr); + return DD_OK; + } + HRESULT hr = m_proxy->DeleteAttachedSurface(dwFlags, ddraw2Surface->GetProxied()); if (unlikely(FAILED(hr))) return hr; @@ -515,96 +466,66 @@ namespace dxvk { } HRESULT STDMETHODCALLTYPE DDraw2Surface::Flip(LPDIRECTDRAWSURFACE2 lpDDSurfaceTargetOverride, DWORD dwFlags) { - // Lost surfaces are not flippable - HRESULT hr = m_proxy->IsLost(); - if (unlikely(FAILED(hr))) { - Logger::debug("DDraw2Surface::Flip: Lost surface"); - return hr; - } - - if (unlikely(!(m_commonSurf->IsFrontBuffer() || m_commonSurf->IsBackBufferOrFlippable()))) { - Logger::debug("DDraw2Surface::Flip: Unflippable surface"); - return DDERR_NOTFLIPPABLE; - } - const bool exclusiveMode = m_commonIntf->GetCooperativeLevel() & DDSCL_EXCLUSIVE; + Com surf2 = static_cast(lpDDSurfaceTargetOverride); - // Non-exclusive mode validations - if (unlikely(m_commonSurf->IsPrimarySurface() && !exclusiveMode)) { - Logger::debug("DDraw2Surface::Flip: Primary surface flip in non-exclusive mode"); - return DDERR_NOEXCLUSIVEMODE; - } + d3d9::IDirect3DDevice9* d3d9Device = m_commonSurf->RefreshD3D9Device(); + if (likely(d3d9Device != nullptr)) { + Logger::debug("*** DDraw2Surface::Flip: Presenting"); - // Exclusive mode validations - if (unlikely(m_commonSurf->IsBackBufferOrFlippable() && exclusiveMode)) { - Logger::debug("DDraw2Surface::Flip: Back buffer flip in exclusive mode"); - return DDERR_NOTFLIPPABLE; - } + // Lost surfaces are not flippable + HRESULT hr = m_proxy->IsLost(); + if (unlikely(FAILED(hr))) { + Logger::debug("DDraw2Surface::Flip: Lost surface"); + return hr; + } - Com surf2 = static_cast(lpDDSurfaceTargetOverride); - if (lpDDSurfaceTargetOverride != nullptr) { - if (unlikely(!surf2->GetParent()->GetCommonSurface()->IsBackBufferOrFlippable())) { - Logger::debug("DDraw2Surface::Flip: Unflippable override surface"); + if (unlikely(!(m_commonSurf->IsFrontBuffer() || m_commonSurf->IsBackBufferOrFlippable()))) { + Logger::debug("DDraw2Surface::Flip: Unflippable surface"); return DDERR_NOTFLIPPABLE; } - } - DDraw2Surface* rt = m_commonIntf->GetDDrawRenderTarget() != nullptr ? - m_commonIntf->GetDDrawRenderTarget()->GetDD2Surface() : nullptr; + const bool exclusiveMode = m_commonIntf->GetCooperativeLevel() & DDSCL_EXCLUSIVE; - RefreshD3D9Device(); - if (likely(m_d3d9Device != nullptr)) { - Logger::debug("*** DDraw2Surface::Flip: Presenting"); + // Non-exclusive mode validations + if (unlikely(m_commonSurf->IsPrimarySurface() && !exclusiveMode)) { + Logger::debug("DDraw2Surface::Flip: Primary surface flip in non-exclusive mode"); + return DDERR_NOEXCLUSIVEMODE; + } - m_commonIntf->ResetDrawTracking(); + // Exclusive mode validations + if (unlikely(m_commonSurf->IsBackBufferOrFlippable() && exclusiveMode)) { + Logger::debug("DDraw2Surface::Flip: Back buffer flip in exclusive mode"); + return DDERR_NOTFLIPPABLE; + } - if (unlikely(m_commonIntf->GetOptions()->forceProxiedPresent)) { - D3DCommonDevice* commonDevice = m_commonIntf->GetCommonD3DDevice(); + if (unlikely(surf2 != nullptr && !surf2->GetParent()->GetCommonSurface()->IsBackBufferOrFlippable())) { + Logger::debug("DDraw2Surface::Flip: Unflippable override surface"); + return DDERR_NOTFLIPPABLE; + } - if (unlikely(!IsInitialized())) - m_parent->InitializeD3D9(commonDevice->IsCurrentRenderTarget(m_parent)); + DDraw2Surface* rt = m_commonIntf->GetDDrawRenderTarget() != nullptr ? + m_commonIntf->GetDDrawRenderTarget()->GetDD2Surface() : nullptr; - BlitToDDrawSurface(m_proxy.ptr(), m_parent->GetD3D9()); + if (unlikely(rt != nullptr && m_commonSurf->IsPrimarySurface())) { + Logger::debug("DDraw2Surface::Flip: Presenting from DDraw RT"); + rt->InitializeOrUploadD3D9(); + d3d9Device->Present(NULL, NULL, NULL, NULL); + return DD_OK; + } - if (likely(commonDevice->IsCurrentRenderTarget(m_parent))) { - if (lpDDSurfaceTargetOverride != nullptr) { - m_commonIntf->SetFlipRTSurfaceAndFlags(surf2->GetParent()->GetProxied(), dwFlags); - } else { - m_commonIntf->SetFlipRTSurfaceAndFlags(nullptr, dwFlags); - } - } - if (lpDDSurfaceTargetOverride != nullptr) { - if (unlikely(m_commonIntf->GetOptions()->forceBlitOnFlip && - rt != nullptr && m_commonSurf->IsPrimarySurface())) { - Logger::debug("DDraw2Surface::Flip: Skipping flip"); - return DD_OK; - } else { - return m_proxy->Flip(surf2->GetProxied(), dwFlags); - } - } else { - if (unlikely(m_commonIntf->GetOptions()->forceBlitOnFlip && - rt != nullptr && m_commonSurf->IsPrimarySurface())) { - Logger::debug("DDraw2Surface::Flip: Skipping flip"); - return DD_OK; - } else { - return m_proxy->Flip(lpDDSurfaceTargetOverride, dwFlags); - } - } + if (likely(m_parent->GetNextFlippable() != nullptr)) { + m_parent->GetNextFlippable()->InitializeOrUploadD3D9(); + } else { + InitializeOrUploadD3D9(); } - m_d3d9Device->Present(NULL, NULL, NULL, NULL); - // If we don't have a valid D3D5 device, this means a D3D3 application - // is trying to flip the surface. Allow that for compatibility reasons. + d3d9Device->Present(NULL, NULL, NULL, NULL); + } else { Logger::debug("<<< DDraw2Surface::Flip: Proxy"); if (lpDDSurfaceTargetOverride == nullptr) { - if (unlikely(m_commonIntf->GetOptions()->forceBlitOnFlip && - rt != nullptr && m_commonSurf->IsPrimarySurface())) { - Logger::debug("DDraw2Surface::Flip: Blitting instead of flipping"); - return m_proxy->Blt(nullptr, rt->GetProxied(), nullptr, DDBLT_WAIT, nullptr); - } else { - return m_proxy->Flip(lpDDSurfaceTargetOverride, dwFlags); - } + return m_proxy->Flip(lpDDSurfaceTargetOverride, dwFlags); } else { return m_proxy->Flip(surf2->GetProxied(), dwFlags); } @@ -676,11 +597,6 @@ namespace dxvk { // Blitting can be done at any time and completes within its call frame HRESULT STDMETHODCALLTYPE DDraw2Surface::GetBltStatus(DWORD dwFlags) { - if (unlikely(m_commonIntf->GetOptions()->forceProxiedPresent)) { - Logger::debug("<<< DDraw2Surface::GetBltStatus: Proxy"); - m_proxy->GetBltStatus(dwFlags); - } - Logger::debug(">>> DDraw2Surface::GetBltStatus"); if (likely(dwFlags == DDGBS_CANBLT || dwFlags == DDGBS_ISBLTDONE)) @@ -724,42 +640,16 @@ namespace dxvk { } HRESULT STDMETHODCALLTYPE DDraw2Surface::GetDC(HDC *lphDC) { - if (likely(!m_commonIntf->GetOptions()->forceProxiedPresent)) { - if (unlikely(lphDC == nullptr)) - return DDERR_INVALIDPARAMS; - - InitReturnPtr(lphDC); - - // Foward GetDC calls if we have drawn and the surface is flippable - RefreshD3D9Device(); - if (m_d3d9Device != nullptr && (m_commonIntf->HasDrawn() && - m_commonSurf->IsGuardableSurface())) { - Logger::debug(">>> DDraw2Surface::GetDC"); - - if (unlikely(!IsInitialized())) { - HRESULT hrUpload = InitializeOrUploadD3D9(); - if (unlikely(FAILED(hrUpload))) - Logger::warn("DDraw2Surface::GetDC: Failed to initialize d3d9 surface"); - } + Logger::debug("<<< DDraw2Surface::GetDC: Proxy"); - HRESULT hr9 = m_parent->GetD3D9()->GetDC(lphDC); - if (unlikely(FAILED(hr9))) - Logger::warn("DDraw2Surface::GetDC: Failed D3D9 call"); - return hr9; - } - } + // Write back any dirty surface data from bound D3D9 back buffers or depth stencils + DownloadSurfaceData(); - Logger::debug("<<< DDraw2Surface::GetDC: Proxy"); - return m_proxy->GetDC(lphDC); + return GetShadowOrProxied()->GetDC(lphDC); } // Flipping can be done at any time and completes within its call frame HRESULT STDMETHODCALLTYPE DDraw2Surface::GetFlipStatus(DWORD dwFlags) { - if (unlikely(m_commonIntf->GetOptions()->forceProxiedPresent)) { - Logger::debug("<<< DDraw2Surface::GetFlipStatus: Proxy"); - m_proxy->GetFlipStatus(dwFlags); - } - Logger::debug(">>> DDraw2Surface::GetFlipStatus"); if (likely(dwFlags == DDGFS_CANFLIP || dwFlags == DDGFS_ISFLIPDONE)) @@ -831,53 +721,30 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE DDraw2Surface::Lock(LPRECT lpDestRect, LPDDSURFACEDESC lpDDSurfaceDesc, DWORD dwFlags, HANDLE hEvent) { Logger::debug("<<< DDraw2Surface::Lock: Proxy"); - // It's highly unlikely anyone would do depth locks with IDirectDrawSurface2 - if (unlikely(m_commonSurf->IsDepthStencil())) { - static bool s_depthStencilWarningShown; - - if (!std::exchange(s_depthStencilWarningShown, true)) - Logger::warn("DDraw2Surface::Lock: Surface is a depth stencil"); - } + // Write back any dirty surface data from bound D3D9 back buffers or depth stencils + DownloadSurfaceData(); - return m_proxy->Lock(lpDestRect, lpDDSurfaceDesc, dwFlags, hEvent); + return GetShadowOrProxied()->Lock(lpDestRect, lpDDSurfaceDesc, dwFlags, hEvent); } HRESULT STDMETHODCALLTYPE DDraw2Surface::ReleaseDC(HDC hDC) { - if (likely(!m_commonIntf->GetOptions()->forceProxiedPresent)) { - // Foward ReleaseDC calls if we have drawn and the surface is flippable - RefreshD3D9Device(); - if (m_d3d9Device != nullptr && (m_commonIntf->HasDrawn() && - m_commonSurf->IsGuardableSurface())) { - Logger::debug(">>> DDraw2Surface::ReleaseDC"); - - if (unlikely(!IsInitialized())) { - HRESULT hrUpload = InitializeOrUploadD3D9(); - if (unlikely(FAILED(hrUpload))) - Logger::warn("DDraw2Surface::ReleaseDC: Failed to initialize d3d9 surface"); - } - - HRESULT hr9 = m_parent->GetD3D9()->ReleaseDC(hDC); - if (unlikely(FAILED(hr9))) - Logger::warn("DDraw2Surface::ReleaseDC: Failed D3D9 call"); - return hr9; - } - } - Logger::debug("<<< DDraw2Surface::ReleaseDC: Proxy"); - HRESULT hr = m_proxy->ReleaseDC(hDC); + HRESULT hr = GetShadowOrProxied()->ReleaseDC(hDC); if (likely(SUCCEEDED(hr))) { - // Textures and cubemaps get uploaded during SetTexture calls - if (m_commonSurf->IsTexture()) { - m_commonSurf->DirtyMipMaps(); - } else if (unlikely(m_commonIntf->GetOptions()->apitraceMode)) { - // We should ideally upload the surface contents here at all times, - // however some games are amazing, and do hundreds of locks on the same - // surface per frame, so this would absolutely tank performance - HRESULT hrUpload = InitializeOrUploadD3D9(); - if (unlikely(FAILED(hrUpload))) - Logger::warn("DDraw2Surface::ReleaseDC: Failed upload to d3d9 surface"); + m_commonSurf->DirtyDDrawSurface(); + + d3d9::IDirect3DDevice9* d3d9Device = m_commonSurf->RefreshD3D9Device(); + if (unlikely(m_shadowSurf != nullptr && d3d9Device != nullptr)) { + const bool shouldPresent = m_commonIntf->GetOptions()->legacyPresentGuard == D3DLegacyPresentGuard::Auto ? + !m_commonSurf->GetCommonD3DDevice()->IsInScene() : + m_commonIntf->GetOptions()->legacyPresentGuard == D3DLegacyPresentGuard::Strict ? + false : true; + if (shouldPresent) { + InitializeOrUploadD3D9(); + d3d9Device->Present(NULL, NULL, NULL, NULL); + } } } @@ -907,6 +774,15 @@ namespace dxvk { return hr; m_commonSurf->SetClipper(ddrawClipper); + + // Retrieve a hWnd, if needed, during clipper attachment + HWND hWnd = nullptr; + hr = ddrawClipper->GetProxied()->GetHWnd(&hWnd); + if (unlikely(FAILED(hr))) { + Logger::debug("DDraw2Surface::SetClipper: Failed to retrieve hWnd"); + } else { + m_commonIntf->SetHWND(hWnd); + } } return DD_OK; @@ -915,6 +791,16 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE DDraw2Surface::SetColorKey(DWORD dwFlags, LPDDCOLORKEY lpDDColorKey) { Logger::debug("<<< DDraw2Surface::SetColorKey: Proxy"); + // The Combat Mission series of games set a color key which is + // outside the color range of the surface they are setting it on... + // clamp it to the surface color depth in that case. This doesn't + // appear to work well universally, however, so only apply when needed. + if (unlikely(m_commonIntf->GetOptions()->colorKeyMasking && lpDDColorKey != nullptr)) { + const uint8_t colorBitCount = m_commonSurf->GetColorBitCount(); + lpDDColorKey->dwColorSpaceLowValue &= (1 << colorBitCount) - 1; + lpDDColorKey->dwColorSpaceHighValue &= (1 << colorBitCount) - 1; + } + HRESULT hr = m_proxy->SetColorKey(dwFlags, lpDDColorKey); if (unlikely(FAILED(hr))) return hr; @@ -923,6 +809,17 @@ namespace dxvk { if (unlikely(FAILED(hr))) Logger::err("DDraw2Surface::SetColorKey: Failed to retrieve updated surface desc"); + if (unlikely(m_shadowSurf != nullptr)) { + hr = m_shadowSurf->GetProxied()->SetColorKey(dwFlags, lpDDColorKey); + if (unlikely(FAILED(hr))) { + Logger::warn("DDraw2Surface::SetColorKey: Failed to set shadow surface color key"); + } else { + hr = m_shadowSurf->GetCommonSurface()->RefreshSurfaceDescripton(); + if (unlikely(FAILED(hr))) + Logger::warn("DDraw2Surface::SetColorKey: Failed to retrieve updated shadow surface desc"); + } + } + return DD_OK; } @@ -936,7 +833,7 @@ namespace dxvk { // A nullptr lpDDPalette gets the current palette detached if (lpDDPalette == nullptr) { - HRESULT hr = m_proxy->SetPalette(lpDDPalette); + HRESULT hr = GetShadowOrProxied()->SetPalette(lpDDPalette); if (unlikely(FAILED(hr))) return hr; @@ -944,7 +841,7 @@ namespace dxvk { } else { DDrawPalette* ddrawPalette = static_cast(lpDDPalette); - HRESULT hr = m_proxy->SetPalette(ddrawPalette->GetProxied()); + HRESULT hr = GetShadowOrProxied()->SetPalette(ddrawPalette->GetProxied()); if (unlikely(FAILED(hr))) return hr; @@ -957,16 +854,21 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE DDraw2Surface::Unlock(LPVOID lpSurfaceData) { Logger::debug("<<< DDraw2Surface::Unlock: Proxy"); - HRESULT hr = m_proxy->Unlock(lpSurfaceData); + HRESULT hr = GetShadowOrProxied()->Unlock(lpSurfaceData); if (likely(SUCCEEDED(hr))) { - // Textures and cubemaps get uploaded during SetTexture calls - if (!m_commonSurf->IsTexture()) { - HRESULT hrUpload = InitializeOrUploadD3D9(); - if (unlikely(FAILED(hrUpload))) - Logger::warn("DDraw2Surface::Unlock: Failed upload to d3d9 surface"); - } else { - m_commonSurf->DirtyMipMaps(); + m_commonSurf->DirtyDDrawSurface(); + + d3d9::IDirect3DDevice9* d3d9Device = m_commonSurf->RefreshD3D9Device(); + if (unlikely(m_shadowSurf != nullptr && d3d9Device != nullptr)) { + const bool shouldPresent = m_commonIntf->GetOptions()->legacyPresentGuard == D3DLegacyPresentGuard::Auto ? + !m_commonSurf->GetCommonD3DDevice()->IsInScene() : + m_commonIntf->GetOptions()->legacyPresentGuard == D3DLegacyPresentGuard::Strict ? + false : true; + if (shouldPresent) { + InitializeOrUploadD3D9(); + d3d9Device->Present(NULL, NULL, NULL, NULL); + } } } @@ -1033,19 +935,95 @@ namespace dxvk { return m_proxy->PageUnlock(dwFlags); } - inline void DDraw2Surface::RefreshD3D9Device() { - D3DCommonDevice* commonDevice = m_commonIntf->GetCommonD3DDevice(); + IDirectDrawSurface2* DDraw2Surface::GetShadowOrProxied() { + d3d9::IDirect3DDevice9* d3d9Device = m_commonSurf->RefreshD3D9Device(); + + if (unlikely(m_shadowSurf != nullptr && d3d9Device != nullptr)) + return m_shadowSurf->GetProxied(); + + return m_proxy.ptr(); + } + + HRESULT DDraw2Surface::InitializeOrUploadD3D9() { + d3d9::IDirect3DDevice9* d3d9Device = m_commonSurf->RefreshD3D9Device(); + + // Fast skip + if (unlikely(d3d9Device == nullptr)) { + Logger::debug("DDraw2Surface::InitializeOrUploadD3D9: Null device, can't initialize right now"); + return DD_OK; + } + + if (unlikely(!m_commonSurf->IsInitialized())) { + if (m_commonSurf->IsTexture()) + m_parent->UpdateMipMapCount(); + + const bool initRenderTarget = m_commonSurf->GetCommonD3DDevice()->IsCurrentRenderTarget(m_commonSurf.ptr()); + + HRESULT hr = m_commonSurf->InitializeD3D9(initRenderTarget); + if (unlikely(FAILED(hr))) + return hr; + + Logger::debug(str::format("DDraw2Surface::InitializeOrUploadD3D9: Initialized surface nr. [[2-", m_surfCount, "]]")); + } - d3d9::IDirect3DDevice9* d3d9Device = commonDevice != nullptr ? commonDevice->GetD3D9Device() : nullptr; - if (unlikely(m_d3d9Device != d3d9Device)) { - // Check if the device has been recreated and reset all D3D9 resources - if (m_d3d9Device != nullptr) { - Logger::debug("DDraw2Surface: Device context has changed, clearing all D3D9 resources"); - m_d3d9 = nullptr; + if (likely(m_commonSurf->IsInitialized())) + return UploadSurfaceData(); + + return DD_OK; + } + + void DDraw2Surface::DownloadSurfaceData() { + // Some games, like The Settlers IV, use multiple devices for rendering, one to handle + // terrain and the overall 3D scene, and one to create textures/sprites to overlay on + // top of it. Since DXVK's D3D9 backend does not restrict cross-device surface/texture + // use, simply skip changing assigned surface devices during downloads. This is essentially + // a hack, which by some miracle works well enough in some cases, though may explode in others. + if (likely(!m_commonIntf->GetOptions()->deviceResourceSharing)) + m_commonSurf->RefreshD3D9Device(); + + if (unlikely(m_commonSurf->IsD3D9BackBuffer())) { + Logger::debug("DDraw2Surface::DownloadSurfaceData: Surface is a bound swapchain surface"); + + if (m_commonSurf->IsInitialized() && m_commonSurf->IsD3D9SurfaceDirty()) { + Logger::debug(str::format("DDraw2Surface::DownloadSurfaceData: Downloading nr. [[2-", m_surfCount, "]]")); + BlitToDDrawSurface(GetShadowOrProxied(), m_commonSurf->GetD3D9Surface(), + m_commonSurf->IsDXTFormat()); + m_commonSurf->UnDirtyD3D9Surface(); } + } else if (unlikely(m_commonSurf->IsD3D9DepthStencil())) { + Logger::debug("DDraw2Surface::DownloadSurfaceData: Surface is a bound depth stencil"); + + if (m_commonSurf->IsInitialized() && m_commonSurf->IsD3D9SurfaceDirty()) { + Logger::debug(str::format("DDraw2Surface::DownloadSurfaceData: Downloading nr. [[2-", m_surfCount, "]]")); + BlitToDDrawSurface(m_proxy.ptr(), m_commonSurf->GetD3D9Surface(), + m_commonSurf->IsDXTFormat()); + m_commonSurf->UnDirtyD3D9Surface(); + } + } + } + + inline HRESULT DDraw2Surface::UploadSurfaceData() { + // Fast skip + if (!m_commonSurf->IsDDrawSurfaceDirty()) + return DD_OK; - m_d3d9Device = d3d9Device; + Logger::debug(str::format("DDraw2Surface::UploadSurfaceData: Uploading nr. [[2-", m_surfCount, "]]")); + + if (m_commonSurf->IsTexture()) { + BlitToD3D9Texture(m_commonSurf->GetD3D9Texture(), m_proxy.ptr(), + m_commonSurf->GetMipCount(), m_commonSurf->IsDXTFormat()); + // Blit surfaces directly + } else { + if (unlikely(m_commonSurf->IsDepthStencil())) + Logger::debug("DDraw2Surface::UploadSurfaceData: Uploading depth stencil"); + + BlitToD3D9Surface(m_commonSurf->GetD3D9Surface(), GetShadowOrProxied(), + m_commonSurf->IsDXTFormat()); } + + m_commonSurf->UnDirtyDDrawSurface(); + + return DD_OK; } } diff --git a/src/ddraw/ddraw2/ddraw2_surface.h b/src/ddraw/ddraw2/ddraw2_surface.h index 481d10b4ac9..bf99aa2c298 100644 --- a/src/ddraw/ddraw2/ddraw2_surface.h +++ b/src/ddraw/ddraw2/ddraw2_surface.h @@ -11,12 +11,12 @@ namespace dxvk { - class DDraw7Surface; + class D3DCommonDevice; /** * \brief IDirectDrawSurface2 interface implementation */ - class DDraw2Surface final : public DDrawWrappedObject { + class DDraw2Surface final : public DDrawWrappedObject { public: @@ -102,6 +102,20 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE PageUnlock(DWORD dwFlags); + IDirectDrawSurface2* GetShadowOrProxied(); + + HRESULT InitializeOrUploadD3D9(); + + void DownloadSurfaceData(); + + void SetShadowSurface(Com&& shadowSurf) { + m_shadowSurf = shadowSurf; + } + + DDraw2Surface* GetShadowSurface() const { + return m_shadowSurf.ptr(); + } + DDrawCommonSurface* GetCommonSurface() const { return m_commonSurf.ptr(); } @@ -110,8 +124,8 @@ namespace dxvk { return m_commonIntf; } - d3d9::IDirect3DDevice9* GetD3D9Device() const { - return m_d3d9Device; + void SetAttachedDepthStencil(Com&& depthStencil) { + m_depthStencil = depthStencil; } DDraw2Surface* GetAttachedDepthStencil() { @@ -145,17 +159,9 @@ namespace dxvk { m_commonSurf->SetIsAttached(false); } - HRESULT InitializeOrUploadD3D9() { - return m_parent->InitializeOrUploadD3D9(); - } - - bool IsInitialized() { - return m_parent->IsInitialized(); - } - private: - inline void RefreshD3D9Device(); + inline HRESULT UploadSurfaceData(); static uint32_t s_surfCount; uint32_t m_surfCount = 0; @@ -167,7 +173,9 @@ namespace dxvk { DDraw2Surface* m_parentSurf = nullptr; - d3d9::IDirect3DDevice9* m_d3d9Device = nullptr; + // Offscreen plain surface we use to mask unwanted DDraw interactions, such + // as forced swapchain presents caused by blits/locks on primary surfaces + Com m_shadowSurf; // Back buffers will have depth stencil surfaces as attachments (in practice // I have never seen more than one depth stencil being attached at a time) diff --git a/src/ddraw/ddraw2/ddraw3_surface.cpp b/src/ddraw/ddraw2/ddraw3_surface.cpp index 40c3fb2dfb9..9d5dc3fc69a 100644 --- a/src/ddraw/ddraw2/ddraw3_surface.cpp +++ b/src/ddraw/ddraw2/ddraw3_surface.cpp @@ -5,7 +5,6 @@ #include "../ddraw_gamma.h" #include "../ddraw/ddraw_interface.h" -#include "../ddraw/ddraw_surface.h" #include "../ddraw2/ddraw2_surface.h" #include "../ddraw4/ddraw4_surface.h" #include "../ddraw7/ddraw7_surface.h" @@ -23,7 +22,7 @@ namespace dxvk { Com&& surfProxy, DDrawSurface* pParent, DDraw3Surface* pParentSurf) - : DDrawWrappedObject(pParent, std::move(surfProxy), nullptr) + : DDrawWrappedObject(pParent, std::move(surfProxy)) , m_commonSurf ( commonSurf ) , m_parentSurf ( pParentSurf ) { if (m_parent != nullptr) { @@ -66,6 +65,26 @@ namespace dxvk { m_originSurf = m_parent; } + // Retrieve and cache the shadow surface + if (likely(m_shadowSurf == nullptr)) { + IUnknown* shadowSurfaceProxiedOrigin = m_commonSurf->GetShadowSurfaceProxied(); + if (likely(shadowSurfaceProxiedOrigin != nullptr)) { + Logger::debug("DDraw3Surface: Retrieving shadow surface from origin"); + + Com shadowSurfProxy; + HRESULT hr = shadowSurfaceProxiedOrigin->QueryInterface(__uuidof(IDirectDrawSurface3), + reinterpret_cast(&shadowSurfProxy)); + if (unlikely(FAILED(hr))) { + throw DxvkError("DDraw3Surface: ERROR! Failed to retrieve the shadow surface!"); + } else { + if (shadowSurfProxy != nullptr) { + m_shadowSurf = new DDraw3Surface(m_commonSurf->GetShadowCommonSurface(), std::move(shadowSurfProxy), + m_commonSurf->GetDDSurface(), nullptr); + } + } + } + } + m_commonIntf->AddWrappedSurface(this); m_commonSurf->SetDD3Surface(this); @@ -251,13 +270,11 @@ namespace dxvk { DDraw3Surface* ddraw3Surface = static_cast(lpDDSAttachedSurface); + // Unsupported by Wine's DDraw implementation, so we'll do our own present if (unlikely(ddraw3Surface->GetCommonSurface()->IsBackBufferOrFlippable())) { - if (unlikely(m_commonIntf->GetOptions()->forceBlitOnFlip)) { - Logger::debug("DDraw3Surface::AddAttachedSurface: Caching surface as RT"); - m_commonIntf->SetDDrawRenderTarget(ddraw3Surface->GetCommonSurface()); - } else { - Logger::warn("DDraw3Surface::AddAttachedSurface: Trying to attach a flippable surface"); - } + Logger::debug("DDraw3Surface::AddAttachedSurface: Caching surface as DDraw RT"); + m_commonIntf->SetDDrawRenderTarget(ddraw3Surface->GetCommonSurface()); + return DD_OK; } HRESULT hr = m_proxy->AddAttachedSurface(ddraw3Surface->GetProxied()); @@ -281,90 +298,31 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE DDraw3Surface::Blt(LPRECT lpDestRect, LPDIRECTDRAWSURFACE3 lpDDSrcSurface, LPRECT lpSrcRect, DWORD dwFlags, LPDDBLTFX lpDDBltFx) { Logger::debug("<<< DDraw3Surface::Blt: Proxy"); - // Write back any flippable surfaces or depth stencils from D3D9 + // Write back any dirty surface data from bound D3D9 back buffers or + // depth stencils, for both the source surface and the current surface if (likely(lpDDSrcSurface != nullptr && m_commonIntf->IsWrappedSurface(lpDDSrcSurface))) { - DDraw3Surface* sourceSurface = static_cast(lpDDSrcSurface); - if (unlikely(sourceSurface->GetCommonSurface()->IsGuardableSurface())) { - // Tomb Raider 3 relies on back buffer write backs using iDirectDrawSurface3 surfaces - if (m_commonIntf->GetOptions()->backBufferWriteBack || m_commonIntf->GetOptions()->apitraceMode) { - Logger::debug("DDraw3Surface::Blt: Source surface is a swapchain surface"); - - if (unlikely(m_commonIntf->GetOptions()->apitraceMode && !sourceSurface->IsInitialized())) - sourceSurface->InitializeOrUploadD3D9(); - - if (likely(sourceSurface->IsInitialized())) - BlitToDDrawSurface(m_parent->GetProxied(), m_parent->GetD3D9()); - } else { - static bool s_swapchainWarningShown; - - if (!std::exchange(s_swapchainWarningShown, true)) - Logger::warn("DDraw3Surface::Blt: Source surface is a swapchain surface"); - } - } else if (unlikely(sourceSurface->GetCommonSurface()->IsDepthStencil())) { - if (m_commonIntf->GetOptions()->depthWriteBack || m_commonIntf->GetOptions()->apitraceMode) { - Logger::debug("DDraw3Surface::Blt: Source surface is a depth stencil"); - - if (likely(sourceSurface->IsInitialized())) - BlitToDDrawSurface(m_parent->GetProxied(), m_parent->GetD3D9()); - } else { - static bool s_depthStencilWarningShown; - - if (!std::exchange(s_depthStencilWarningShown, true)) - Logger::warn("DDraw3Surface::Blt: Source surface is a depth stencil"); - } - } + DDraw3Surface* surface3 = static_cast(lpDDSrcSurface); + surface3->DownloadSurfaceData(); } + DownloadSurfaceData(); - RefreshD3D9Device(); - if (likely(m_d3d9Device != nullptr)) { - // Forward DDBLT_DEPTHFILL clears to D3D9 if done on the current depth stencil - if (unlikely(lpDDSrcSurface == nullptr && - (dwFlags & DDBLT_DEPTHFILL) && - lpDDBltFx != nullptr && - m_commonIntf->GetCommonD3DDevice()->IsCurrentD3D9DepthStencil(m_d3d9.ptr()))) { - Logger::debug("DDraw3Surface::Blt: Clearing d3d9 depth stencil"); - - HRESULT hrClear; - const float zClear = m_commonSurf->GetNormalizedFloatDepth(lpDDBltFx->dwFillDepth); - - if (lpDestRect == nullptr) { - hrClear = m_d3d9Device->Clear(0, NULL, D3DCLEAR_ZBUFFER, 0, zClear, 0); - } else { - hrClear = m_d3d9Device->Clear(1, reinterpret_cast(lpDestRect), D3DCLEAR_ZBUFFER, 0, zClear, 0); - } - if (unlikely(FAILED(hrClear))) - Logger::warn("DDraw3Surface::Blt: Failed to clear d3d9 depth"); - } - // Forward DDBLT_COLORFILL clears to D3D9 if done on the current render target - if (unlikely(lpDDSrcSurface == nullptr && - (dwFlags & DDBLT_COLORFILL) && - lpDDBltFx != nullptr && - m_commonIntf->GetCommonD3DDevice()->IsCurrentD3D9RenderTarget(m_d3d9.ptr()))) { - Logger::debug("DDraw3Surface::Blt: Clearing d3d9 render target"); - - HRESULT hrClear; - if (lpDestRect == nullptr) { - hrClear = m_d3d9Device->Clear(0, NULL, D3DCLEAR_TARGET, lpDDBltFx->dwFillColor, 0.0f, 0); - } else { - hrClear = m_d3d9Device->Clear(1, reinterpret_cast(lpDestRect), D3DCLEAR_TARGET, lpDDBltFx->dwFillColor, 0.0f, 0); - } - if (unlikely(FAILED(hrClear))) - Logger::warn("DDraw3Surface::Blt: Failed to clear d3d9 render target"); - } - + d3d9::IDirect3DDevice9* d3d9Device = m_commonSurf->RefreshD3D9Device(); + if (likely(d3d9Device != nullptr)) { const bool exclusiveMode = (m_commonIntf->GetCooperativeLevel() & DDSCL_EXCLUSIVE) && !m_commonIntf->GetOptions()->ignoreExclusiveMode; - // Eclusive mode back buffer guard - if (exclusiveMode && m_commonIntf->HasDrawn() && - m_commonSurf->IsGuardableSurface() && - m_commonIntf->GetOptions()->backBufferGuard != D3DBackBufferGuard::Disabled) { - return DD_OK; // Windowed mode presentation path - } else if (!exclusiveMode && m_commonIntf->HasDrawn() && m_commonSurf->IsPrimarySurface()) { - m_commonIntf->ResetDrawTracking(); - m_d3d9Device->Present(NULL, NULL, NULL, NULL); - return DD_OK; + if (!exclusiveMode && lpDDSrcSurface != nullptr && m_commonSurf->IsPrimarySurface()) { + // TODO: Handle this properly, not by uploading the RT again + DDraw3Surface* ddraw3Surface = static_cast(lpDDSrcSurface); + DDrawSurface* ddrawSurface = ddraw3Surface->GetCommonSurface()->GetDDSurface(); + DDrawSurface* renderTarget = m_commonSurf->GetCommonD3DDevice()->GetCurrentRenderTarget(); + + if (ddrawSurface == renderTarget) { + renderTarget->InitializeOrUploadD3D9(); + d3d9Device->Present(NULL, NULL, NULL, NULL); + return DD_OK; + } } } @@ -375,27 +333,31 @@ namespace dxvk { Logger::debug("DDraw3Surface::Blt: Received an IDirectDrawSurface source surface"); DDrawSurface* ddrawSurface = reinterpret_cast(lpDDSrcSurface); Com surface3; - ddrawSurface->GetProxied()->QueryInterface(__uuidof(IDirectDrawSurface3), reinterpret_cast(&surface3)); - hr = m_proxy->Blt(lpDestRect, surface3.ptr(), lpSrcRect, dwFlags, lpDDBltFx); + ddrawSurface->GetShadowOrProxied()->QueryInterface(__uuidof(IDirectDrawSurface3), reinterpret_cast(&surface3)); + hr = GetShadowOrProxied()->Blt(lpDestRect, surface3.ptr(), lpSrcRect, dwFlags, lpDDBltFx); } else if (unlikely(!m_commonIntf->IsWrappedSurface(lpDDSrcSurface))) { if (unlikely(lpDDSrcSurface != nullptr)) { Logger::warn("DDraw3Surface::Blt: Received an unwrapped source surface"); return DDERR_UNSUPPORTED; } - hr = m_proxy->Blt(lpDestRect, lpDDSrcSurface, lpSrcRect, dwFlags, lpDDBltFx); + hr = GetShadowOrProxied()->Blt(lpDestRect, lpDDSrcSurface, lpSrcRect, dwFlags, lpDDBltFx); } else { DDraw3Surface* ddraw3Surface = static_cast(lpDDSrcSurface); - hr = m_proxy->Blt(lpDestRect, ddraw3Surface->GetProxied(), lpSrcRect, dwFlags, lpDDBltFx); + hr = GetShadowOrProxied()->Blt(lpDestRect, ddraw3Surface->GetShadowOrProxied(), lpSrcRect, dwFlags, lpDDBltFx); } if (likely(SUCCEEDED(hr))) { - // Textures get uploaded during SetTexture calls - if (!m_commonSurf->IsTexture()) { - HRESULT hrUpload = InitializeOrUploadD3D9(); - if (unlikely(FAILED(hrUpload))) - Logger::warn("DDraw3Surface::Blt: Failed upload to d3d9 surface"); - } else { - m_commonSurf->DirtyMipMaps(); + m_commonSurf->DirtyDDrawSurface(); + + if (unlikely(m_shadowSurf != nullptr && d3d9Device != nullptr)) { + const bool shouldPresent = m_commonIntf->GetOptions()->legacyPresentGuard == D3DLegacyPresentGuard::Auto ? + !m_commonSurf->GetCommonD3DDevice()->IsInScene() : + m_commonIntf->GetOptions()->legacyPresentGuard == D3DLegacyPresentGuard::Strict ? + false : true; + if (shouldPresent) { + InitializeOrUploadD3D9(); + d3d9Device->Present(NULL, NULL, NULL, NULL); + } } } @@ -411,54 +373,31 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE DDraw3Surface::BltFast(DWORD dwX, DWORD dwY, LPDIRECTDRAWSURFACE3 lpDDSrcSurface, LPRECT lpSrcRect, DWORD dwTrans) { Logger::debug("<<< DDraw3Surface::BltFast: Proxy"); - // Write back any flippable surfaces or depth stencils from D3D9 + // Write back any dirty surface data from bound D3D9 back buffers or + // depth stencils, for both the source surface and the current surface if (likely(lpDDSrcSurface != nullptr && m_commonIntf->IsWrappedSurface(lpDDSrcSurface))) { - DDraw3Surface* sourceSurface = static_cast(lpDDSrcSurface); - if (unlikely(sourceSurface->GetCommonSurface()->IsGuardableSurface())) { - if (m_commonIntf->GetOptions()->backBufferWriteBack || m_commonIntf->GetOptions()->apitraceMode) { - Logger::debug("DDraw3Surface::BltFast: Source surface is a swapchain surface"); - - if (unlikely(m_commonIntf->GetOptions()->apitraceMode && !sourceSurface->IsInitialized())) - sourceSurface->InitializeOrUploadD3D9(); - - if (likely(sourceSurface->IsInitialized())) - BlitToDDrawSurface(m_parent->GetProxied(), m_parent->GetD3D9()); - } else { - static bool s_swapchainWarningShown; - - if (!std::exchange(s_swapchainWarningShown, true)) - Logger::warn("DDraw3Surface::BltFast: Source surface is a swapchain surface"); - } - } else if (unlikely(sourceSurface->GetCommonSurface()->IsDepthStencil())) { - if (m_commonIntf->GetOptions()->depthWriteBack || m_commonIntf->GetOptions()->apitraceMode) { - Logger::debug("DDraw3Surface::BltFast: Source surface is a depth stencil"); - - if (likely(sourceSurface->IsInitialized())) - BlitToDDrawSurface(m_parent->GetProxied(), m_parent->GetD3D9()); - } else { - static bool s_depthStencilWarningShown; - - if (!std::exchange(s_depthStencilWarningShown, true)) - Logger::warn("DDraw3Surface::BltFast: Source surface is a depth stencil"); - } - } + DDraw3Surface* surface3 = static_cast(lpDDSrcSurface); + surface3->DownloadSurfaceData(); } + DownloadSurfaceData(); - RefreshD3D9Device(); - if (likely(m_d3d9Device != nullptr)) { + d3d9::IDirect3DDevice9* d3d9Device = m_commonSurf->RefreshD3D9Device(); + if (likely(d3d9Device != nullptr)) { const bool exclusiveMode = (m_commonIntf->GetCooperativeLevel() & DDSCL_EXCLUSIVE) && !m_commonIntf->GetOptions()->ignoreExclusiveMode; - // Eclusive mode back buffer guard - if (exclusiveMode && m_commonIntf->HasDrawn() && - m_commonSurf->IsGuardableSurface() && - m_commonIntf->GetOptions()->backBufferGuard != D3DBackBufferGuard::Disabled) { - return DD_OK; // Windowed mode presentation path - } else if (!exclusiveMode && m_commonIntf->HasDrawn() && m_commonSurf->IsPrimarySurface()) { - m_commonIntf->ResetDrawTracking(); - m_d3d9Device->Present(NULL, NULL, NULL, NULL); - return DD_OK; + if (!exclusiveMode && lpDDSrcSurface != nullptr && m_commonSurf->IsPrimarySurface()) { + // TODO: Handle this properly, not by uploading the RT again + DDraw3Surface* ddraw3Surface = static_cast(lpDDSrcSurface); + DDrawSurface* ddrawSurface = ddraw3Surface->GetCommonSurface()->GetDDSurface(); + DDrawSurface* renderTarget = m_commonSurf->GetCommonD3DDevice()->GetCurrentRenderTarget(); + + if (ddrawSurface == renderTarget) { + renderTarget->InitializeOrUploadD3D9(); + d3d9Device->Present(NULL, NULL, NULL, NULL); + return DD_OK; + } } } @@ -469,20 +408,24 @@ namespace dxvk { Logger::warn("DDraw3Surface::BltFast: Received an unwrapped source surface"); return DDERR_UNSUPPORTED; } - hr = m_proxy->BltFast(dwX, dwY, lpDDSrcSurface, lpSrcRect, dwTrans); + hr = GetShadowOrProxied()->BltFast(dwX, dwY, lpDDSrcSurface, lpSrcRect, dwTrans); } else { DDraw3Surface* ddraw3Surface = static_cast(lpDDSrcSurface); - hr = m_proxy->BltFast(dwX, dwY, ddraw3Surface->GetProxied(), lpSrcRect, dwTrans); + hr = GetShadowOrProxied()->BltFast(dwX, dwY, ddraw3Surface->GetShadowOrProxied(), lpSrcRect, dwTrans); } if (likely(SUCCEEDED(hr))) { - // Textures get uploaded during SetTexture calls - if (!m_commonSurf->IsTexture()) { - HRESULT hrUpload = InitializeOrUploadD3D9(); - if (unlikely(FAILED(hrUpload))) - Logger::warn("DDraw3Surface::BltFast: Failed upload to d3d9 surface"); - } else { - m_commonSurf->DirtyMipMaps(); + m_commonSurf->DirtyDDrawSurface(); + + if (unlikely(m_shadowSurf != nullptr && d3d9Device != nullptr)) { + const bool shouldPresent = m_commonIntf->GetOptions()->legacyPresentGuard == D3DLegacyPresentGuard::Auto ? + !m_commonSurf->GetCommonD3DDevice()->IsInScene() : + m_commonIntf->GetOptions()->legacyPresentGuard == D3DLegacyPresentGuard::Strict ? + false : true; + if (shouldPresent) { + InitializeOrUploadD3D9(); + d3d9Device->Present(NULL, NULL, NULL, NULL); + } } } @@ -509,6 +452,13 @@ namespace dxvk { DDraw3Surface* ddraw3Surface = static_cast(lpDDSAttachedSurface); + if (unlikely(ddraw3Surface->GetCommonSurface()->IsBackBufferOrFlippable() && + ddraw3Surface->GetCommonSurface() == m_commonIntf->GetDDrawRenderTarget())) { + Logger::debug("DDraw3Surface::DeleteAttachedSurface: Removing cached DDraw RT surface"); + m_commonIntf->SetDDrawRenderTarget(nullptr); + return DD_OK; + } + HRESULT hr = m_proxy->DeleteAttachedSurface(dwFlags, ddraw3Surface->GetProxied()); if (unlikely(FAILED(hr))) return hr; @@ -532,96 +482,66 @@ namespace dxvk { } HRESULT STDMETHODCALLTYPE DDraw3Surface::Flip(LPDIRECTDRAWSURFACE3 lpDDSurfaceTargetOverride, DWORD dwFlags) { - // Lost surfaces are not flippable - HRESULT hr = m_proxy->IsLost(); - if (unlikely(FAILED(hr))) { - Logger::debug("DDraw3Surface::Flip: Lost surface"); - return hr; - } - - if (unlikely(!(m_commonSurf->IsFrontBuffer() || m_commonSurf->IsBackBufferOrFlippable()))) { - Logger::debug("DDraw3Surface::Flip: Unflippable surface"); - return DDERR_NOTFLIPPABLE; - } - const bool exclusiveMode = m_commonIntf->GetCooperativeLevel() & DDSCL_EXCLUSIVE; + Com surf3 = static_cast(lpDDSurfaceTargetOverride); - // Non-exclusive mode validations - if (unlikely(m_commonSurf->IsPrimarySurface() && !exclusiveMode)) { - Logger::debug("DDraw3Surface::Flip: Primary surface flip in non-exclusive mode"); - return DDERR_NOEXCLUSIVEMODE; - } + d3d9::IDirect3DDevice9* d3d9Device = m_commonSurf->RefreshD3D9Device(); + if (likely(d3d9Device != nullptr)) { + Logger::debug("*** DDraw3Surface::Flip: Presenting"); - // Exclusive mode validations - if (unlikely(m_commonSurf->IsBackBufferOrFlippable() && exclusiveMode)) { - Logger::debug("DDraw3Surface::Flip: Back buffer flip in exclusive mode"); - return DDERR_NOTFLIPPABLE; - } + // Lost surfaces are not flippable + HRESULT hr = m_proxy->IsLost(); + if (unlikely(FAILED(hr))) { + Logger::debug("DDraw3Surface::Flip: Lost surface"); + return hr; + } - Com surf3 = static_cast(lpDDSurfaceTargetOverride); - if (lpDDSurfaceTargetOverride != nullptr) { - if (unlikely(!surf3->GetParent()->GetCommonSurface()->IsBackBufferOrFlippable())) { - Logger::debug("DDraw3Surface::Flip: Unflippable override surface"); + if (unlikely(!(m_commonSurf->IsFrontBuffer() || m_commonSurf->IsBackBufferOrFlippable()))) { + Logger::debug("DDraw3Surface::Flip: Unflippable surface"); return DDERR_NOTFLIPPABLE; } - } - DDraw3Surface* rt = m_commonIntf->GetDDrawRenderTarget() != nullptr ? - m_commonIntf->GetDDrawRenderTarget()->GetDD3Surface() : nullptr; + const bool exclusiveMode = m_commonIntf->GetCooperativeLevel() & DDSCL_EXCLUSIVE; - RefreshD3D9Device(); - if (likely(m_d3d9Device != nullptr)) { - Logger::debug("*** DDraw3Surface::Flip: Presenting"); + // Non-exclusive mode validations + if (unlikely(m_commonSurf->IsPrimarySurface() && !exclusiveMode)) { + Logger::debug("DDraw3Surface::Flip: Primary surface flip in non-exclusive mode"); + return DDERR_NOEXCLUSIVEMODE; + } - m_commonIntf->ResetDrawTracking(); + // Exclusive mode validations + if (unlikely(m_commonSurf->IsBackBufferOrFlippable() && exclusiveMode)) { + Logger::debug("DDraw3Surface::Flip: Back buffer flip in exclusive mode"); + return DDERR_NOTFLIPPABLE; + } - if (unlikely(m_commonIntf->GetOptions()->forceProxiedPresent)) { - D3DCommonDevice* commonDevice = m_commonIntf->GetCommonD3DDevice(); + if (unlikely(surf3 != nullptr && !surf3->GetParent()->GetCommonSurface()->IsBackBufferOrFlippable())) { + Logger::debug("DDraw3Surface::Flip: Unflippable override surface"); + return DDERR_NOTFLIPPABLE; + } - if (unlikely(!IsInitialized())) - m_parent->InitializeD3D9(commonDevice->IsCurrentRenderTarget(m_parent)); + DDraw3Surface* rt = m_commonIntf->GetDDrawRenderTarget() != nullptr ? + m_commonIntf->GetDDrawRenderTarget()->GetDD3Surface() : nullptr; - BlitToDDrawSurface(m_proxy.ptr(), m_parent->GetD3D9()); + if (unlikely(rt != nullptr && m_commonSurf->IsPrimarySurface())) { + Logger::debug("DDraw3Surface::Flip: Presenting from DDraw RT"); + rt->InitializeOrUploadD3D9(); + d3d9Device->Present(NULL, NULL, NULL, NULL); + return DD_OK; + } - if (likely(commonDevice->IsCurrentRenderTarget(m_parent))) { - if (lpDDSurfaceTargetOverride != nullptr) { - m_commonIntf->SetFlipRTSurfaceAndFlags(surf3->GetParent()->GetProxied(), dwFlags); - } else { - m_commonIntf->SetFlipRTSurfaceAndFlags(nullptr, dwFlags); - } - } - if (lpDDSurfaceTargetOverride != nullptr) { - if (unlikely(m_commonIntf->GetOptions()->forceBlitOnFlip && - rt != nullptr && m_commonSurf->IsPrimarySurface())) { - Logger::debug("DDraw3Surface::Flip: Skipping flip"); - return DD_OK; - } else { - return m_proxy->Flip(surf3->GetProxied(), dwFlags); - } - } else { - if (unlikely(m_commonIntf->GetOptions()->forceBlitOnFlip && - rt != nullptr && m_commonSurf->IsPrimarySurface())) { - Logger::debug("DDraw3Surface::Flip: Skipping flip"); - return DD_OK; - } else { - return m_proxy->Flip(lpDDSurfaceTargetOverride, dwFlags); - } - } + if (likely(m_parent->GetNextFlippable() != nullptr)) { + m_parent->GetNextFlippable()->InitializeOrUploadD3D9(); + } else { + InitializeOrUploadD3D9(); } - m_d3d9Device->Present(NULL, NULL, NULL, NULL); - // If we don't have a valid D3D5 device, this means a D3D3 application - // is trying to flip the surface. Allow that for compatibility reasons. + d3d9Device->Present(NULL, NULL, NULL, NULL); + } else { Logger::debug("<<< DDraw3Surface::Flip: Proxy"); if (lpDDSurfaceTargetOverride == nullptr) { - if (unlikely(m_commonIntf->GetOptions()->forceBlitOnFlip && - rt != nullptr && m_commonSurf->IsPrimarySurface())) { - Logger::debug("DDraw3Surface::Flip: Blitting instead of flipping"); - return m_proxy->Blt(nullptr, rt->GetProxied(), nullptr, DDBLT_WAIT, nullptr); - } else { - return m_proxy->Flip(lpDDSurfaceTargetOverride, dwFlags); - } + return m_proxy->Flip(lpDDSurfaceTargetOverride, dwFlags); } else { return m_proxy->Flip(surf3->GetProxied(), dwFlags); } @@ -693,11 +613,6 @@ namespace dxvk { // Blitting can be done at any time and completes within its call frame HRESULT STDMETHODCALLTYPE DDraw3Surface::GetBltStatus(DWORD dwFlags) { - if (unlikely(m_commonIntf->GetOptions()->forceProxiedPresent)) { - Logger::debug("<<< DDraw3Surface::GetBltStatus: Proxy"); - m_proxy->GetBltStatus(dwFlags); - } - Logger::debug(">>> DDraw3Surface::GetBltStatus"); if (likely(dwFlags == DDGBS_CANBLT || dwFlags == DDGBS_ISBLTDONE)) @@ -741,42 +656,16 @@ namespace dxvk { } HRESULT STDMETHODCALLTYPE DDraw3Surface::GetDC(HDC *lphDC) { - if (likely(!m_commonIntf->GetOptions()->forceProxiedPresent)) { - if (unlikely(lphDC == nullptr)) - return DDERR_INVALIDPARAMS; - - InitReturnPtr(lphDC); - - // Foward GetDC calls if we have drawn and the surface is flippable - RefreshD3D9Device(); - if (m_d3d9Device != nullptr && (m_commonIntf->HasDrawn() && - m_commonSurf->IsGuardableSurface())) { - Logger::debug(">>> DDraw3Surface::GetDC"); - - if (unlikely(!IsInitialized())) { - HRESULT hrUpload = InitializeOrUploadD3D9(); - if (unlikely(FAILED(hrUpload))) - Logger::warn("DDraw3Surface::GetDC: Failed to initialize d3d9 surface"); - } + Logger::debug("<<< DDraw3Surface::GetDC: Proxy"); - HRESULT hr9 = m_parent->GetD3D9()->GetDC(lphDC); - if (unlikely(FAILED(hr9))) - Logger::warn("DDraw3Surface::GetDC: Failed D3D9 call"); - return hr9; - } - } + // Write back any dirty surface data from bound D3D9 back buffers or depth stencils + DownloadSurfaceData(); - Logger::debug("<<< DDraw3Surface::GetDC: Proxy"); - return m_proxy->GetDC(lphDC); + return GetShadowOrProxied()->GetDC(lphDC); } // Flipping can be done at any time and completes within its call frame HRESULT STDMETHODCALLTYPE DDraw3Surface::GetFlipStatus(DWORD dwFlags) { - if (unlikely(m_commonIntf->GetOptions()->forceProxiedPresent)) { - Logger::debug("<<< DDraw3Surface::GetFlipStatus: Proxy"); - m_proxy->GetFlipStatus(dwFlags); - } - Logger::debug(">>> DDraw3Surface::GetFlipStatus"); if (likely(dwFlags == DDGFS_CANFLIP || dwFlags == DDGFS_ISFLIPDONE)) @@ -848,53 +737,30 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE DDraw3Surface::Lock(LPRECT lpDestRect, LPDDSURFACEDESC lpDDSurfaceDesc, DWORD dwFlags, HANDLE hEvent) { Logger::debug("<<< DDraw3Surface::Lock: Proxy"); - // It's highly unlikely anyone would do depth locks with IDirectDrawSurface3 - if (unlikely(m_commonSurf->IsDepthStencil())) { - static bool s_depthStencilWarningShown; - - if (!std::exchange(s_depthStencilWarningShown, true)) - Logger::warn("DDraw3Surface::Lock: Surface is a depth stencil"); - } + // Write back any dirty surface data from bound D3D9 back buffers or depth stencils + DownloadSurfaceData(); - return m_proxy->Lock(lpDestRect, lpDDSurfaceDesc, dwFlags, hEvent); + return GetShadowOrProxied()->Lock(lpDestRect, lpDDSurfaceDesc, dwFlags, hEvent); } HRESULT STDMETHODCALLTYPE DDraw3Surface::ReleaseDC(HDC hDC) { - if (likely(!m_commonIntf->GetOptions()->forceProxiedPresent)) { - // Foward ReleaseDC calls if we have drawn and the surface is flippable - RefreshD3D9Device(); - if (m_d3d9Device != nullptr && (m_commonIntf->HasDrawn() && - m_commonSurf->IsGuardableSurface())) { - Logger::debug(">>> DDraw3Surface::ReleaseDC"); - - if (unlikely(!IsInitialized())) { - HRESULT hrUpload = InitializeOrUploadD3D9(); - if (unlikely(FAILED(hrUpload))) - Logger::warn("DDraw3Surface::ReleaseDC: Failed to initialize d3d9 surface"); - } - - HRESULT hr9 = m_parent->GetD3D9()->ReleaseDC(hDC); - if (unlikely(FAILED(hr9))) - Logger::warn("DDraw3Surface::ReleaseDC: Failed D3D9 call"); - return hr9; - } - } - Logger::debug("<<< DDraw3Surface::ReleaseDC: Proxy"); - HRESULT hr = m_proxy->ReleaseDC(hDC); + HRESULT hr = GetShadowOrProxied()->ReleaseDC(hDC); if (likely(SUCCEEDED(hr))) { - // Textures and cubemaps get uploaded during SetTexture calls - if (m_commonSurf->IsTexture()) { - m_commonSurf->DirtyMipMaps(); - } else if (unlikely(m_commonIntf->GetOptions()->apitraceMode)) { - // We should ideally upload the surface contents here at all times, - // however some games are amazing, and do hundreds of locks on the same - // surface per frame, so this would absolutely tank performance - HRESULT hrUpload = InitializeOrUploadD3D9(); - if (unlikely(FAILED(hrUpload))) - Logger::warn("DDraw3Surface::ReleaseDC: Failed upload to d3d9 surface"); + m_commonSurf->DirtyDDrawSurface(); + + d3d9::IDirect3DDevice9* d3d9Device = m_commonSurf->RefreshD3D9Device(); + if (unlikely(m_shadowSurf != nullptr && d3d9Device != nullptr)) { + const bool shouldPresent = m_commonIntf->GetOptions()->legacyPresentGuard == D3DLegacyPresentGuard::Auto ? + !m_commonSurf->GetCommonD3DDevice()->IsInScene() : + m_commonIntf->GetOptions()->legacyPresentGuard == D3DLegacyPresentGuard::Strict ? + false : true; + if (shouldPresent) { + InitializeOrUploadD3D9(); + d3d9Device->Present(NULL, NULL, NULL, NULL); + } } } @@ -924,6 +790,15 @@ namespace dxvk { return hr; m_commonSurf->SetClipper(ddrawClipper); + + // Retrieve a hWnd, if needed, during clipper attachment + HWND hWnd = nullptr; + hr = ddrawClipper->GetProxied()->GetHWnd(&hWnd); + if (unlikely(FAILED(hr))) { + Logger::debug("DDraw3Surface::SetClipper: Failed to retrieve hWnd"); + } else { + m_commonIntf->SetHWND(hWnd); + } } return DD_OK; @@ -932,6 +807,16 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE DDraw3Surface::SetColorKey(DWORD dwFlags, LPDDCOLORKEY lpDDColorKey) { Logger::debug("<<< DDraw3Surface::SetColorKey: Proxy"); + // The Combat Mission series of games set a color key which is + // outside the color range of the surface they are setting it on... + // clamp it to the surface color depth in that case. This doesn't + // appear to work well universally, however, so only apply when needed. + if (unlikely(m_commonIntf->GetOptions()->colorKeyMasking && lpDDColorKey != nullptr)) { + const uint8_t colorBitCount = m_commonSurf->GetColorBitCount(); + lpDDColorKey->dwColorSpaceLowValue &= (1 << colorBitCount) - 1; + lpDDColorKey->dwColorSpaceHighValue &= (1 << colorBitCount) - 1; + } + HRESULT hr = m_proxy->SetColorKey(dwFlags, lpDDColorKey); if (unlikely(FAILED(hr))) return hr; @@ -940,6 +825,17 @@ namespace dxvk { if (unlikely(FAILED(hr))) Logger::err("DDraw3Surface::SetColorKey: Failed to retrieve updated surface desc"); + if (unlikely(m_shadowSurf != nullptr)) { + hr = m_shadowSurf->GetProxied()->SetColorKey(dwFlags, lpDDColorKey); + if (unlikely(FAILED(hr))) { + Logger::warn("DDraw3Surface::SetColorKey: Failed to set shadow surface color key"); + } else { + hr = m_shadowSurf->GetCommonSurface()->RefreshSurfaceDescripton(); + if (unlikely(FAILED(hr))) + Logger::warn("DDraw3Surface::SetColorKey: Failed to retrieve updated shadow surface desc"); + } + } + return DD_OK; } @@ -953,7 +849,7 @@ namespace dxvk { // A nullptr lpDDPalette gets the current palette detached if (lpDDPalette == nullptr) { - HRESULT hr = m_proxy->SetPalette(lpDDPalette); + HRESULT hr = GetShadowOrProxied()->SetPalette(lpDDPalette); if (unlikely(FAILED(hr))) return hr; @@ -961,7 +857,7 @@ namespace dxvk { } else { DDrawPalette* ddrawPalette = static_cast(lpDDPalette); - HRESULT hr = m_proxy->SetPalette(ddrawPalette->GetProxied()); + HRESULT hr = GetShadowOrProxied()->SetPalette(ddrawPalette->GetProxied()); if (unlikely(FAILED(hr))) return hr; @@ -974,16 +870,21 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE DDraw3Surface::Unlock(LPVOID lpSurfaceData) { Logger::debug("<<< DDraw3Surface::Unlock: Proxy"); - HRESULT hr = m_proxy->Unlock(lpSurfaceData); + HRESULT hr = GetShadowOrProxied()->Unlock(lpSurfaceData); if (likely(SUCCEEDED(hr))) { - // Textures and cubemaps get uploaded during SetTexture calls - if (!m_commonSurf->IsTexture()) { - HRESULT hrUpload = InitializeOrUploadD3D9(); - if (unlikely(FAILED(hrUpload))) - Logger::warn("DDraw3Surface::Unlock: Failed upload to d3d9 surface"); - } else { - m_commonSurf->DirtyMipMaps(); + m_commonSurf->DirtyDDrawSurface(); + + d3d9::IDirect3DDevice9* d3d9Device = m_commonSurf->RefreshD3D9Device(); + if (unlikely(m_shadowSurf != nullptr && d3d9Device != nullptr)) { + const bool shouldPresent = m_commonIntf->GetOptions()->legacyPresentGuard == D3DLegacyPresentGuard::Auto ? + !m_commonSurf->GetCommonD3DDevice()->IsInScene() : + m_commonIntf->GetOptions()->legacyPresentGuard == D3DLegacyPresentGuard::Strict ? + false : true; + if (shouldPresent) { + InitializeOrUploadD3D9(); + d3d9Device->Present(NULL, NULL, NULL, NULL); + } } } @@ -1064,30 +965,100 @@ namespace dxvk { Logger::err("DDraw3Surface::SetSurfaceDesc: Failed to retrieve updated surface desc"); // We may need to recreate the d3d9 object based on the new desc - m_d3d9 = nullptr; - - if (!m_commonSurf->IsTexture()) { - InitializeOrUploadD3D9(); - } else { - m_commonSurf->DirtyMipMaps(); - } + m_commonSurf->SetD3D9Surface(nullptr); return hr; } - inline void DDraw3Surface::RefreshD3D9Device() { - D3DCommonDevice* commonDevice = m_commonIntf->GetCommonD3DDevice(); + IDirectDrawSurface3* DDraw3Surface::GetShadowOrProxied() { + d3d9::IDirect3DDevice9* d3d9Device = m_commonSurf->RefreshD3D9Device(); - d3d9::IDirect3DDevice9* d3d9Device = commonDevice != nullptr ? commonDevice->GetD3D9Device() : nullptr; - if (unlikely(m_d3d9Device != d3d9Device)) { - // Check if the device has been recreated and reset all D3D9 resources - if (m_d3d9Device != nullptr) { - Logger::debug("DDraw3Surface: Device context has changed, clearing all D3D9 resources"); - m_d3d9 = nullptr; + if (unlikely(m_shadowSurf != nullptr && d3d9Device != nullptr)) + return m_shadowSurf->GetProxied(); + + return m_proxy.ptr(); + } + + HRESULT DDraw3Surface::InitializeOrUploadD3D9() { + d3d9::IDirect3DDevice9* d3d9Device = m_commonSurf->RefreshD3D9Device(); + + // Fast skip + if (unlikely(d3d9Device == nullptr)) { + Logger::debug("DDraw3Surface::InitializeOrUploadD3D9: Null device, can't initialize right now"); + return DD_OK; + } + + if (unlikely(!m_commonSurf->IsInitialized())) { + if (m_commonSurf->IsTexture()) + m_parent->UpdateMipMapCount(); + + const bool initRenderTarget = m_commonSurf->GetCommonD3DDevice()->IsCurrentRenderTarget(m_commonSurf.ptr()); + + HRESULT hr = m_commonSurf->InitializeD3D9(initRenderTarget); + if (unlikely(FAILED(hr))) + return hr; + + Logger::debug(str::format("DDraw3Surface::InitializeOrUploadD3D9: Initialized surface nr. [[3-", m_surfCount, "]]")); + } + + if (likely(m_commonSurf->IsInitialized())) + return UploadSurfaceData(); + + return DD_OK; + } + + void DDraw3Surface::DownloadSurfaceData() { + // Some games, like The Settlers IV, use multiple devices for rendering, one to handle + // terrain and the overall 3D scene, and one to create textures/sprites to overlay on + // top of it. Since DXVK's D3D9 backend does not restrict cross-device surface/texture + // use, simply skip changing assigned surface devices during downloads. This is essentially + // a hack, which by some miracle works well enough in some cases, though may explode in others. + if (likely(!m_commonIntf->GetOptions()->deviceResourceSharing)) + m_commonSurf->RefreshD3D9Device(); + + if (unlikely(m_commonSurf->IsD3D9BackBuffer())) { + Logger::debug("DDraw3Surface::DownloadSurfaceData: Surface is a bound swapchain surface"); + + if (m_commonSurf->IsInitialized() && m_commonSurf->IsD3D9SurfaceDirty()) { + Logger::debug(str::format("DDraw3Surface::DownloadSurfaceData: Downloading nr. [[3-", m_surfCount, "]]")); + BlitToDDrawSurface(GetShadowOrProxied(), m_commonSurf->GetD3D9Surface(), + m_commonSurf->IsDXTFormat()); + m_commonSurf->UnDirtyD3D9Surface(); } + } else if (unlikely(m_commonSurf->IsD3D9DepthStencil())) { + Logger::debug("DDraw3Surface::DownloadSurfaceData: Surface is a bound depth stencil"); + + if (m_commonSurf->IsInitialized() && m_commonSurf->IsD3D9SurfaceDirty()) { + Logger::debug(str::format("DDraw3Surface::DownloadSurfaceData: Downloading nr. [[3-", m_surfCount, "]]")); + BlitToDDrawSurface(m_proxy.ptr(), m_commonSurf->GetD3D9Surface(), + m_commonSurf->IsDXTFormat()); + m_commonSurf->UnDirtyD3D9Surface(); + } + } + } + + inline HRESULT DDraw3Surface::UploadSurfaceData() { + // Fast skip + if (!m_commonSurf->IsDDrawSurfaceDirty()) + return DD_OK; - m_d3d9Device = d3d9Device; + Logger::debug(str::format("DDraw3Surface::UploadSurfaceData: Uploading nr. [[3-", m_surfCount, "]]")); + + if (m_commonSurf->IsTexture()) { + BlitToD3D9Texture(m_commonSurf->GetD3D9Texture(), m_proxy.ptr(), + m_commonSurf->GetMipCount(), m_commonSurf->IsDXTFormat()); + // Blit surfaces directly + } else { + if (unlikely(m_commonSurf->IsDepthStencil())) + Logger::debug("DDraw3Surface::UploadSurfaceData: Uploading depth stencil"); + + BlitToD3D9Surface(m_commonSurf->GetD3D9Surface(), GetShadowOrProxied(), + m_commonSurf->IsDXTFormat()); } + + m_commonSurf->UnDirtyDDrawSurface(); + + return DD_OK; } } diff --git a/src/ddraw/ddraw2/ddraw3_surface.h b/src/ddraw/ddraw2/ddraw3_surface.h index 5e6b4359f3c..121a43784ce 100644 --- a/src/ddraw/ddraw2/ddraw3_surface.h +++ b/src/ddraw/ddraw2/ddraw3_surface.h @@ -11,13 +11,12 @@ namespace dxvk { - class DDrawSurface; - class DDraw7Surface; + class D3DCommonDevice; /** * \brief IDirectDrawSurface3 interface implementation */ - class DDraw3Surface final : public DDrawWrappedObject { + class DDraw3Surface final : public DDrawWrappedObject { public: @@ -105,6 +104,20 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE SetSurfaceDesc(LPDDSURFACEDESC lpDDSD, DWORD dwFlags); + IDirectDrawSurface3* GetShadowOrProxied(); + + HRESULT InitializeOrUploadD3D9(); + + void DownloadSurfaceData(); + + void SetShadowSurface(Com&& shadowSurf) { + m_shadowSurf = shadowSurf; + } + + DDraw3Surface* GetShadowSurface() const { + return m_shadowSurf.ptr(); + } + DDrawCommonSurface* GetCommonSurface() const { return m_commonSurf.ptr(); } @@ -113,8 +126,8 @@ namespace dxvk { return m_commonIntf; } - d3d9::IDirect3DDevice9* GetD3D9Device() const { - return m_d3d9Device; + void SetAttachedDepthStencil(Com&& depthStencil) { + m_depthStencil = depthStencil; } DDraw3Surface* GetAttachedDepthStencil() { @@ -148,17 +161,9 @@ namespace dxvk { m_commonSurf->SetIsAttached(false); } - HRESULT InitializeOrUploadD3D9() { - return m_parent->InitializeOrUploadD3D9(); - } - - bool IsInitialized() { - return m_parent->IsInitialized(); - } - private: - inline void RefreshD3D9Device(); + inline HRESULT UploadSurfaceData(); static uint32_t s_surfCount; uint32_t m_surfCount = 0; @@ -170,7 +175,9 @@ namespace dxvk { DDraw3Surface* m_parentSurf = nullptr; - d3d9::IDirect3DDevice9* m_d3d9Device = nullptr; + // Offscreen plain surface we use to mask unwanted DDraw interactions, such + // as forced swapchain presents caused by blits/locks on primary surfaces + Com m_shadowSurf; // Back buffers will have depth stencil surfaces as attachments (in practice // I have never seen more than one depth stencil being attached at a time) diff --git a/src/ddraw/ddraw4/ddraw4_interface.cpp b/src/ddraw/ddraw4/ddraw4_interface.cpp index 988a8d0dd1d..4e518655587 100644 --- a/src/ddraw/ddraw4/ddraw4_interface.cpp +++ b/src/ddraw/ddraw4/ddraw4_interface.cpp @@ -22,7 +22,7 @@ namespace dxvk { DDraw4Interface::DDraw4Interface( DDrawCommonInterface* commonIntf, Com&& proxyIntf) - : DDrawWrappedObject(nullptr, std::move(proxyIntf), nullptr) + : DDrawWrappedObject(nullptr, std::move(proxyIntf)) , m_commonIntf ( commonIntf ) { // We need a temporary D3D9 interface at this point to retrieve the adapter identifier Com d3d9Intf = d3d9::Direct3DCreate9(D3D_SDK_VERSION); @@ -39,12 +39,6 @@ namespace dxvk { m_commonIntf->SetDD4Interface(this); - static bool s_apitraceModeWarningShown; - - if (unlikely(m_commonIntf->GetOptions()->apitraceMode && - !std::exchange(s_apitraceModeWarningShown, true))) - Logger::warn("DDraw4Interface: Apitrace mode is enabled. Performance will be suboptimal!"); - m_intfCount = ++s_intfCount; Logger::debug(str::format("DDraw4Interface: Created a new interface nr. <<4-", m_intfCount, ">>")); @@ -266,9 +260,7 @@ namespace dxvk { // D3D9, so always strip the DDSCAPS_WRITEONLY flag on creation lpDDSurfaceDesc->ddsCaps.dwCaps &= ~DDSCAPS_WRITEONLY; // Similarly strip the DDSCAPS2_OPAQUE flag on texture creation - if (lpDDSurfaceDesc->ddsCaps.dwCaps & DDSCAPS_TEXTURE) { - lpDDSurfaceDesc->ddsCaps.dwCaps2 &= ~DDSCAPS2_OPAQUE; - } + lpDDSurfaceDesc->ddsCaps.dwCaps2 &= ~DDSCAPS2_OPAQUE; if (unlikely((lpDDSurfaceDesc->ddsCaps.dwCaps & DDSCAPS_ZBUFFER) && (lpDDSurfaceDesc->ddpfPixelFormat.dwZBitMask == 0xFFFFFFFF))) { @@ -289,8 +281,40 @@ namespace dxvk { if (likely(SUCCEEDED(hr))) { try{ Com surface4 = new DDraw4Surface(nullptr, std::move(ddrawSurface4Proxied), this, nullptr, true); - if (unlikely(lpDDSurfaceDesc->ddsCaps.dwCaps & DDSCAPS_PRIMARYSURFACE)) + + if (unlikely(lpDDSurfaceDesc->ddsCaps.dwCaps & DDSCAPS_PRIMARYSURFACE)) { m_commonIntf->SetPrimarySurface(surface4->GetCommonSurface()); + + // Shadow surface creation for the primary surface + // (it needs to be based on the same incoming desc) + if (unlikely(!surface4->GetCommonSurface()->Is8BitFormat() && + m_commonIntf->GetOptions()->forceLegacyPresent)) { + Logger::debug("DDraw4Interface::CreateSurface: Creating shadow surface"); + + DDSURFACEDESC2 shadowDesc = *lpDDSurfaceDesc; + const DDSURFACEDESC2* primaryDesc = surface4->GetCommonSurface()->GetDesc2(); + + shadowDesc.ddsCaps.dwCaps &= ~DDSCAPS_PRIMARYSURFACE & ~DDSCAPS_COMPLEX & ~DDSCAPS_FLIP; + shadowDesc.ddsCaps.dwCaps |= DDSCAPS_OFFSCREENPLAIN; + shadowDesc.dwFlags &= ~DDSD_BACKBUFFERCOUNT; + // Dimensions aren't specified in the incoming desc, + // but are explicitly needed for non-primary surfaces + shadowDesc.dwFlags |= DDSD_WIDTH | DDSD_HEIGHT; + shadowDesc.dwWidth = primaryDesc->dwWidth; + shadowDesc.dwHeight = primaryDesc->dwHeight; + + Com ddraw4SurfaceShadow; + hr = m_proxy->CreateSurface(&shadowDesc, &ddraw4SurfaceShadow, pUnkOuter); + if (unlikely(FAILED(hr))) { + Logger::warn("DDraw4Interface::CreateSurface: Failed to create shadow surface"); + } else { + Com shadowSurf = new DDraw4Surface(nullptr, std::move(ddraw4SurfaceShadow), + this, nullptr, false); + surface4->SetShadowSurface(std::move(shadowSurf)); + } + } + } + *lplpDDSurface = surface4.ref(); } catch (const DxvkError& e) { Logger::err(e.message()); @@ -366,11 +390,6 @@ namespace dxvk { } HRESULT STDMETHODCALLTYPE DDraw4Interface::FlipToGDISurface() { - if (unlikely(m_commonIntf->GetOptions()->forceProxiedPresent)) { - Logger::debug("<<< DDraw4Interface::FlipToGDISurface: Proxy"); - return m_proxy->FlipToGDISurface(); - } - Logger::debug("*** DDraw4Interface::FlipToGDISurface: Ignoring"); DDrawCommonSurface* ps = m_commonIntf->GetPrimarySurface(); @@ -388,6 +407,18 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE DDraw4Interface::GetCaps(LPDDCAPS lpDDDriverCaps, LPDDCAPS lpDDHELCaps) { Logger::debug("<<< DDraw4Interface::GetCaps: Proxy"); + if (unlikely(lpDDDriverCaps == nullptr && lpDDHELCaps == nullptr)) + return DDERR_INVALIDPARAMS; + + // Interstate '76 sends invalid dwSizes part of the structs, + // and that explodes in Wine, so validate it before proxying + + if (unlikely(lpDDDriverCaps != nullptr && !IsValidDDrawCapsSize(lpDDDriverCaps->dwSize))) + return DDERR_INVALIDPARAMS; + + if (unlikely(lpDDHELCaps != nullptr && !IsValidDDrawCapsSize(lpDDHELCaps->dwSize))) + return DDERR_INVALIDPARAMS; + HRESULT hr = m_proxy->GetCaps(lpDDDriverCaps, lpDDHELCaps); if (unlikely(FAILED(hr))) return hr; @@ -590,8 +621,7 @@ namespace dxvk { Logger::warn("DDraw4Interface::SetDisplayMode: Failed to update primary surface desc"); } - if (likely(!m_commonIntf->GetOptions()->forceProxiedPresent && - m_commonIntf->GetOptions()->backBufferResize)) { + if (likely(m_commonIntf->GetOptions()->backBufferResize)) { const bool exclusiveMode = m_commonIntf->GetCooperativeLevel() & DDSCL_EXCLUSIVE; // Ignore any mode size dimensions when in windowed present mode @@ -609,11 +639,6 @@ namespace dxvk { } HRESULT STDMETHODCALLTYPE DDraw4Interface::WaitForVerticalBlank(DWORD dwFlags, HANDLE hEvent) { - if (unlikely(m_commonIntf->GetOptions()->forceProxiedPresent)) { - Logger::debug("<<< DDraw4Interface::WaitForVerticalBlank: Proxy"); - m_proxy->WaitForVerticalBlank(dwFlags, hEvent); - } - Logger::debug(">>> DDraw4Interface::WaitForVerticalBlank"); if (unlikely(dwFlags & DDWAITVB_BLOCKBEGINEVENT)) diff --git a/src/ddraw/ddraw4/ddraw4_interface.h b/src/ddraw/ddraw4/ddraw4_interface.h index 178d43e4e62..db2fabced3e 100644 --- a/src/ddraw/ddraw4/ddraw4_interface.h +++ b/src/ddraw/ddraw4/ddraw4_interface.h @@ -17,7 +17,7 @@ namespace dxvk { /** * \brief DirectDraw4 interface implementation */ - class DDraw4Interface final : public DDrawWrappedObject { + class DDraw4Interface final : public DDrawWrappedObject { public: DDraw4Interface( diff --git a/src/ddraw/ddraw4/ddraw4_surface.cpp b/src/ddraw/ddraw4/ddraw4_surface.cpp index 20007d3b9e6..acd5c1e2171 100644 --- a/src/ddraw/ddraw4/ddraw4_surface.cpp +++ b/src/ddraw/ddraw4/ddraw4_surface.cpp @@ -23,7 +23,7 @@ namespace dxvk { DDraw4Interface* pParent, DDraw4Surface* pParentSurf, bool isChildObject) - : DDrawWrappedObject(pParent, std::move(surfProxy), nullptr) + : DDrawWrappedObject(pParent, std::move(surfProxy)) , m_isChildObject ( isChildObject ) , m_commonSurf ( commonSurf ) , m_parentSurf ( pParentSurf ) { @@ -53,13 +53,23 @@ namespace dxvk { } } + // Retrieve and cache the next surface in a flippable chain + if (unlikely(m_commonSurf->IsFlippable() && !m_commonSurf->IsBackBuffer())) { + IDirectDrawSurface4* nextFlippable = nullptr; + EnumAttachedSurfaces(&nextFlippable, ListBackBufferSurfaces4Callback); + m_nextFlippable = reinterpret_cast(nextFlippable); + if (likely(m_nextFlippable != nullptr)) + Logger::debug("DDraw4Surface: Retrieved the next swapchain surface"); + } + m_commonIntf->AddWrappedSurface(this); m_commonSurf->SetDD4Surface(this); - if (m_parentSurf != nullptr && !m_commonSurf->IsFrontBuffer() + if (m_parentSurf != nullptr && m_parentSurf->GetCommonSurface()->IsBackBufferOrFlippable() - && !m_commonIntf->GetOptions()->forceSingleBackBuffer) { + && !m_commonIntf->GetOptions()->forceSingleBackBuffer + && !m_commonIntf->GetOptions()->forceLegacyPresent) { const uint32_t index = m_parentSurf->GetCommonSurface()->GetBackBufferIndex(); m_commonSurf->IncrementBackBufferIndex(index); } @@ -245,13 +255,11 @@ namespace dxvk { DDraw4Surface* ddraw4Surface = static_cast(lpDDSAttachedSurface); + // Unsupported by Wine's DDraw implementation, so we'll do our own present if (unlikely(ddraw4Surface->GetCommonSurface()->IsBackBufferOrFlippable())) { - if (unlikely(m_commonIntf->GetOptions()->forceBlitOnFlip)) { - Logger::debug("DDraw4Surface::AddAttachedSurface: Caching surface as RT"); - m_commonIntf->SetDDrawRenderTarget(ddraw4Surface->GetCommonSurface()); - } else { - Logger::warn("DDraw4Surface::AddAttachedSurface: Trying to attach a flippable surface"); - } + Logger::debug("ddraw4Surface::AddAttachedSurface: Caching surface as DDraw RT"); + m_commonIntf->SetDDrawRenderTarget(ddraw4Surface->GetCommonSurface()); + return DD_OK; } HRESULT hr = m_proxy->AddAttachedSurface(ddraw4Surface->GetProxied()); @@ -274,89 +282,30 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE DDraw4Surface::Blt(LPRECT lpDestRect, LPDIRECTDRAWSURFACE4 lpDDSrcSurface, LPRECT lpSrcRect, DWORD dwFlags, LPDDBLTFX lpDDBltFx) { Logger::debug("<<< DDraw4Surface::Blt: Proxy"); - // Write back any flippable surfaces or depth stencils from D3D9 + // Write back any dirty surface data from bound D3D9 back buffers or + // depth stencils, for both the source surface and the current surface if (likely(lpDDSrcSurface != nullptr && m_commonIntf->IsWrappedSurface(lpDDSrcSurface))) { - DDraw4Surface* sourceSurface = static_cast(lpDDSrcSurface); - if (unlikely(sourceSurface->GetCommonSurface()->IsGuardableSurface())) { - if (m_commonIntf->GetOptions()->backBufferWriteBack || m_commonIntf->GetOptions()->apitraceMode) { - Logger::debug("DDraw4Surface::Blt: Source surface is a swapchain surface"); - - if (unlikely(m_commonIntf->GetOptions()->apitraceMode && !sourceSurface->IsInitialized())) - sourceSurface->InitializeOrUploadD3D9(); - - if (likely(sourceSurface->IsInitialized())) - BlitToDDrawSurface(sourceSurface->GetProxied(), sourceSurface->GetD3D9()); - } else { - static bool s_swapchainWarningShown; - - if (!std::exchange(s_swapchainWarningShown, true)) - Logger::warn("DDraw4Surface::Blt: Source surface is a swapchain surface"); - } - } else if (unlikely(sourceSurface->GetCommonSurface()->IsDepthStencil())) { - if (m_commonIntf->GetOptions()->depthWriteBack || m_commonIntf->GetOptions()->apitraceMode) { - Logger::debug("DDraw4Surface::Blt: Source surface is a depth stencil"); - - if (likely(sourceSurface->IsInitialized())) - BlitToDDrawSurface(sourceSurface->GetProxied(), sourceSurface->GetD3D9()); - } else { - static bool s_depthStencilWarningShown; - - if (!std::exchange(s_depthStencilWarningShown, true)) - Logger::warn("DDraw4Surface::Blt: Source surface is a depth stencil"); - } - } + DDraw4Surface* surface4 = static_cast(lpDDSrcSurface); + surface4->DownloadSurfaceData(); } + DownloadSurfaceData(); - RefreshD3D9Device(); - if (likely(m_d3d9Device != nullptr)) { - // Forward DDBLT_DEPTHFILL clears to D3D9 if done on the current depth stencil - if (unlikely(lpDDSrcSurface == nullptr && - (dwFlags & DDBLT_DEPTHFILL) && - lpDDBltFx != nullptr && - m_commonIntf->GetCommonD3DDevice()->IsCurrentD3D9DepthStencil(m_d3d9.ptr()))) { - Logger::debug("DDraw4Surface::Blt: Clearing d3d9 depth stencil"); - - HRESULT hrClear; - const float zClear = m_commonSurf->GetNormalizedFloatDepth(lpDDBltFx->dwFillDepth); - - if (lpDestRect == nullptr) { - hrClear = m_d3d9Device->Clear(0, NULL, D3DCLEAR_ZBUFFER, 0, zClear, 0); - } else { - hrClear = m_d3d9Device->Clear(1, reinterpret_cast(lpDestRect), D3DCLEAR_ZBUFFER, 0, zClear, 0); - } - if (unlikely(FAILED(hrClear))) - Logger::warn("DDraw4Surface::Blt: Failed to clear d3d9 depth"); - } - // Forward DDBLT_COLORFILL clears to D3D9 if done on the current render target - if (unlikely(lpDDSrcSurface == nullptr && - (dwFlags & DDBLT_COLORFILL) && - lpDDBltFx != nullptr && - m_commonIntf->GetCommonD3DDevice()->IsCurrentD3D9RenderTarget(m_d3d9.ptr()))) { - Logger::debug("DDraw4Surface::Blt: Clearing d3d9 render target"); - - HRESULT hrClear; - if (lpDestRect == nullptr) { - hrClear = m_d3d9Device->Clear(0, NULL, D3DCLEAR_TARGET, lpDDBltFx->dwFillColor, 0.0f, 0); - } else { - hrClear = m_d3d9Device->Clear(1, reinterpret_cast(lpDestRect), D3DCLEAR_TARGET, lpDDBltFx->dwFillColor, 0.0f, 0); - } - if (unlikely(FAILED(hrClear))) - Logger::warn("DDraw4Surface::Blt: Failed to clear d3d9 render target"); - } - + d3d9::IDirect3DDevice9* d3d9Device = m_commonSurf->RefreshD3D9Device(); + if (likely(d3d9Device != nullptr)) { const bool exclusiveMode = (m_commonIntf->GetCooperativeLevel() & DDSCL_EXCLUSIVE) && !m_commonIntf->GetOptions()->ignoreExclusiveMode; - // Eclusive mode back buffer guard - if (exclusiveMode && m_commonIntf->HasDrawn() && - m_commonSurf->IsGuardableSurface() && - m_commonIntf->GetOptions()->backBufferGuard != D3DBackBufferGuard::Disabled) { - return DD_OK; // Windowed mode presentation path - } else if (!exclusiveMode && m_commonIntf->HasDrawn() && m_commonSurf->IsPrimarySurface()) { - m_commonIntf->ResetDrawTracking(); - m_d3d9Device->Present(NULL, NULL, NULL, NULL); - return DD_OK; + if (!exclusiveMode && lpDDSrcSurface != nullptr && m_commonSurf->IsPrimarySurface()) { + // TODO: Handle this properly, not by uploading the RT again + DDraw4Surface* ddraw4Surface = static_cast(lpDDSrcSurface); + DDraw4Surface* renderTarget = m_commonSurf->GetCommonD3DDevice()->GetCurrentRenderTarget4(); + + if (ddraw4Surface == renderTarget) { + renderTarget->InitializeOrUploadD3D9(); + d3d9Device->Present(NULL, NULL, NULL, NULL); + return DD_OK; + } } } @@ -367,20 +316,24 @@ namespace dxvk { Logger::warn("DDraw4Surface::Blt: Received an unwrapped source surface"); return DDERR_UNSUPPORTED; } - hr = m_proxy->Blt(lpDestRect, lpDDSrcSurface, lpSrcRect, dwFlags, lpDDBltFx); + hr = GetShadowOrProxied()->Blt(lpDestRect, lpDDSrcSurface, lpSrcRect, dwFlags, lpDDBltFx); } else { DDraw4Surface* ddraw4Surface = static_cast(lpDDSrcSurface); - hr = m_proxy->Blt(lpDestRect, ddraw4Surface->GetProxied(), lpSrcRect, dwFlags, lpDDBltFx); + hr = GetShadowOrProxied()->Blt(lpDestRect, ddraw4Surface->GetShadowOrProxied(), lpSrcRect, dwFlags, lpDDBltFx); } if (likely(SUCCEEDED(hr))) { - // Textures get uploaded during SetTexture calls - if (!m_commonSurf->IsTexture()) { - HRESULT hrUpload = InitializeOrUploadD3D9(); - if (unlikely(FAILED(hrUpload))) - Logger::warn("DDraw4Surface::Blt: Failed upload to d3d9 surface"); - } else { - m_commonSurf->DirtyMipMaps(); + m_commonSurf->DirtyDDrawSurface(); + + if (unlikely(m_shadowSurf != nullptr && d3d9Device != nullptr)) { + const bool shouldPresent = m_commonIntf->GetOptions()->legacyPresentGuard == D3DLegacyPresentGuard::Auto ? + !m_commonSurf->GetCommonD3DDevice()->IsInScene() : + m_commonIntf->GetOptions()->legacyPresentGuard == D3DLegacyPresentGuard::Strict ? + false : true; + if (shouldPresent) { + InitializeOrUploadD3D9(); + d3d9Device->Present(NULL, NULL, NULL, NULL); + } } } @@ -396,54 +349,30 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE DDraw4Surface::BltFast(DWORD dwX, DWORD dwY, LPDIRECTDRAWSURFACE4 lpDDSrcSurface, LPRECT lpSrcRect, DWORD dwTrans) { Logger::debug("<<< DDraw4Surface::BltFast: Proxy"); - // Write back any flippable surfaces or depth stencils from D3D9 + // Write back any dirty surface data from bound D3D9 back buffers or + // depth stencils, for both the source surface and the current surface if (likely(lpDDSrcSurface != nullptr && m_commonIntf->IsWrappedSurface(lpDDSrcSurface))) { - DDraw4Surface* sourceSurface = static_cast(lpDDSrcSurface); - if (unlikely(sourceSurface->GetCommonSurface()->IsGuardableSurface())) { - if (m_commonIntf->GetOptions()->backBufferWriteBack || m_commonIntf->GetOptions()->apitraceMode) { - Logger::debug("DDraw4Surface::BltFast: Source surface is a swapchain surface"); - - if (unlikely(m_commonIntf->GetOptions()->apitraceMode && !sourceSurface->IsInitialized())) - sourceSurface->InitializeOrUploadD3D9(); - - if (likely(sourceSurface->IsInitialized())) - BlitToDDrawSurface(sourceSurface->GetProxied(), sourceSurface->GetD3D9()); - } else { - static bool s_swapchainWarningShown; - - if (!std::exchange(s_swapchainWarningShown, true)) - Logger::warn("DDraw4Surface::BltFast: Source surface is a swapchain surface"); - } - } else if (unlikely(sourceSurface->GetCommonSurface()->IsDepthStencil())) { - if (m_commonIntf->GetOptions()->depthWriteBack || m_commonIntf->GetOptions()->apitraceMode) { - Logger::debug("DDraw4Surface::BltFast: Source surface is a depth stencil"); - - if (likely(sourceSurface->IsInitialized())) - BlitToDDrawSurface(sourceSurface->GetProxied(), sourceSurface->GetD3D9()); - } else { - static bool s_depthStencilWarningShown; - - if (!std::exchange(s_depthStencilWarningShown, true)) - Logger::warn("DDraw4Surface::BltFast: Source surface is a depth stencil"); - } - } + DDraw4Surface* surface4 = static_cast(lpDDSrcSurface); + surface4->DownloadSurfaceData(); } + DownloadSurfaceData(); - RefreshD3D9Device(); - if (likely(m_d3d9Device != nullptr)) { + d3d9::IDirect3DDevice9* d3d9Device = m_commonSurf->RefreshD3D9Device(); + if (likely(d3d9Device != nullptr)) { const bool exclusiveMode = (m_commonIntf->GetCooperativeLevel() & DDSCL_EXCLUSIVE) && !m_commonIntf->GetOptions()->ignoreExclusiveMode; - // Eclusive mode back buffer guard - if (exclusiveMode && m_commonIntf->HasDrawn() && - m_commonSurf->IsGuardableSurface() && - m_commonIntf->GetOptions()->backBufferGuard != D3DBackBufferGuard::Disabled) { - return DD_OK; // Windowed mode presentation path - } else if (!exclusiveMode && m_commonIntf->HasDrawn() && m_commonSurf->IsPrimarySurface()) { - m_commonIntf->ResetDrawTracking(); - m_d3d9Device->Present(NULL, NULL, NULL, NULL); - return DD_OK; + if (!exclusiveMode && lpDDSrcSurface != nullptr && m_commonSurf->IsPrimarySurface()) { + // TODO: Handle this properly, not by uploading the RT again + DDraw4Surface* ddraw4Surface = static_cast(lpDDSrcSurface); + DDraw4Surface* renderTarget = m_commonSurf->GetCommonD3DDevice()->GetCurrentRenderTarget4(); + + if (ddraw4Surface == renderTarget) { + renderTarget->InitializeOrUploadD3D9(); + d3d9Device->Present(NULL, NULL, NULL, NULL); + return DD_OK; + } } } @@ -454,20 +383,24 @@ namespace dxvk { Logger::warn("DDraw4Surface::BltFast: Received an unwrapped source surface"); return DDERR_UNSUPPORTED; } - hr = m_proxy->BltFast(dwX, dwY, lpDDSrcSurface, lpSrcRect, dwTrans); + hr = GetShadowOrProxied()->BltFast(dwX, dwY, lpDDSrcSurface, lpSrcRect, dwTrans); } else { DDraw4Surface* ddraw4Surface = static_cast(lpDDSrcSurface); - hr = m_proxy->BltFast(dwX, dwY, ddraw4Surface->GetProxied(), lpSrcRect, dwTrans); + hr = GetShadowOrProxied()->BltFast(dwX, dwY, ddraw4Surface->GetShadowOrProxied(), lpSrcRect, dwTrans); } if (likely(SUCCEEDED(hr))) { - // Textures get uploaded during SetTexture calls - if (!m_commonSurf->IsTexture()) { - HRESULT hrUpload = InitializeOrUploadD3D9(); - if (unlikely(FAILED(hrUpload))) - Logger::warn("DDraw4Surface::BltFast: Failed upload to d3d9 surface"); - } else { - m_commonSurf->DirtyMipMaps(); + m_commonSurf->DirtyDDrawSurface(); + + if (unlikely(m_shadowSurf != nullptr && d3d9Device != nullptr)) { + const bool shouldPresent = m_commonIntf->GetOptions()->legacyPresentGuard == D3DLegacyPresentGuard::Auto ? + !m_commonSurf->GetCommonD3DDevice()->IsInScene() : + m_commonIntf->GetOptions()->legacyPresentGuard == D3DLegacyPresentGuard::Strict ? + false : true; + if (shouldPresent) { + InitializeOrUploadD3D9(); + d3d9Device->Present(NULL, NULL, NULL, NULL); + } } } @@ -495,6 +428,13 @@ namespace dxvk { DDraw4Surface* ddraw4Surface = static_cast(lpDDSAttachedSurface); + if (unlikely(ddraw4Surface->GetCommonSurface()->IsBackBufferOrFlippable() && + ddraw4Surface->GetCommonSurface() == m_commonIntf->GetDDrawRenderTarget())) { + Logger::debug("DDraw4Surface::DeleteAttachedSurface: Removing cached DDraw RT surface"); + m_commonIntf->SetDDrawRenderTarget(nullptr); + return DD_OK; + } + HRESULT hr = m_proxy->DeleteAttachedSurface(dwFlags, ddraw4Surface->GetProxied()); if (unlikely(FAILED(hr))) return hr; @@ -552,92 +492,44 @@ namespace dxvk { } HRESULT STDMETHODCALLTYPE DDraw4Surface::Flip(LPDIRECTDRAWSURFACE4 lpDDSurfaceTargetOverride, DWORD dwFlags) { - if (m_parent == nullptr) { - Logger::debug("*** DDraw4Surface::Flip: Ignoring"); - return DD_OK; - } - - Logger::debug("*** DDraw4Surface::Flip: Presenting"); - - // Lost surfaces are not flippable - HRESULT hr = m_proxy->IsLost(); - if (unlikely(FAILED(hr))) { - Logger::debug("DDraw4Surface::Flip: Lost surface"); - return hr; - } - - if (unlikely(!(m_commonSurf->IsFrontBuffer() || m_commonSurf->IsBackBufferOrFlippable()))) { - Logger::debug("DDraw4Surface::Flip: Unflippable surface"); - return DDERR_NOTFLIPPABLE; - } - - const bool exclusiveMode = m_commonIntf->GetCooperativeLevel() & DDSCL_EXCLUSIVE; - - // Non-exclusive mode validations - if (unlikely(m_commonSurf->IsPrimarySurface() && !exclusiveMode)) { - Logger::debug("DDraw4Surface::Flip: Primary surface flip in non-exclusive mode"); - return DDERR_NOEXCLUSIVEMODE; - } - - // Exclusive mode validations - if (unlikely(m_commonSurf->IsBackBufferOrFlippable() && exclusiveMode)) { - Logger::debug("DDraw4Surface::Flip: Back buffer flip in exclusive mode"); - return DDERR_NOTFLIPPABLE; - } Com surf4; - if (m_commonIntf->IsWrappedSurface(lpDDSurfaceTargetOverride)) { + if (m_commonIntf->IsWrappedSurface(lpDDSurfaceTargetOverride)) surf4 = static_cast(lpDDSurfaceTargetOverride); - if (unlikely(!surf4->GetCommonSurface()->IsBackBufferOrFlippable())) { - Logger::debug("DDraw4Surface::Flip: Unflippable override surface"); - return DDERR_NOTFLIPPABLE; - } - } + d3d9::IDirect3DDevice9* d3d9Device = m_commonSurf->RefreshD3D9Device(); + if (likely(d3d9Device != nullptr)) { + Logger::debug("*** DDraw4Surface::Flip: Presenting"); - DDraw4Surface* rt = m_commonIntf->GetDDrawRenderTarget() != nullptr ? - m_commonIntf->GetDDrawRenderTarget()->GetDD4Surface() : nullptr; - - RefreshD3D9Device(); - if (likely(m_d3d9Device != nullptr)) { - m_commonIntf->ResetDrawTracking(); - - if (unlikely(m_commonIntf->GetOptions()->forceProxiedPresent)) { - D3DCommonDevice* commonDevice = m_commonIntf->GetCommonD3DDevice(); + // Lost surfaces are not flippable + HRESULT hr = m_proxy->IsLost(); + if (unlikely(FAILED(hr))) { + Logger::debug("DDraw4Surface::Flip: Lost surface"); + return hr; + } - if (unlikely(!IsInitialized())) - InitializeD3D9(commonDevice->IsCurrentRenderTarget(this)); + if (unlikely(!(m_commonSurf->IsFrontBuffer() || m_commonSurf->IsBackBufferOrFlippable()))) { + Logger::debug("DDraw4Surface::Flip: Unflippable surface"); + return DDERR_NOTFLIPPABLE; + } - BlitToDDrawSurface(m_proxy.ptr(), m_d3d9.ptr()); + const bool exclusiveMode = m_commonIntf->GetCooperativeLevel() & DDSCL_EXCLUSIVE; - if (unlikely(!m_commonIntf->IsWrappedSurface(lpDDSurfaceTargetOverride))) { - if (unlikely(lpDDSurfaceTargetOverride != nullptr)) { - Logger::warn("DDraw4Surface::Flip: Received an unwrapped surface"); - return DDERR_UNSUPPORTED; - } + // Non-exclusive mode validations + if (unlikely(m_commonSurf->IsPrimarySurface() && !exclusiveMode)) { + Logger::debug("DDraw4Surface::Flip: Primary surface flip in non-exclusive mode"); + return DDERR_NOEXCLUSIVEMODE; + } - if (likely(commonDevice->IsCurrentRenderTarget(this))) - m_commonIntf->SetFlipRTSurfaceAndFlags(lpDDSurfaceTargetOverride, dwFlags); + // Exclusive mode validations + if (unlikely(m_commonSurf->IsBackBufferOrFlippable() && exclusiveMode)) { + Logger::debug("DDraw4Surface::Flip: Back buffer flip in exclusive mode"); + return DDERR_NOTFLIPPABLE; + } - if (unlikely(m_commonIntf->GetOptions()->forceBlitOnFlip && - rt != nullptr && m_commonSurf->IsPrimarySurface())) { - Logger::debug("DDraw4Surface::Flip: Skipping flip"); - return DD_OK; - } else { - return m_proxy->Flip(lpDDSurfaceTargetOverride, dwFlags); - } - } else { - if (likely(commonDevice->IsCurrentRenderTarget(this))) - m_commonIntf->SetFlipRTSurfaceAndFlags(lpDDSurfaceTargetOverride, dwFlags); - - if (unlikely(m_commonIntf->GetOptions()->forceBlitOnFlip && - rt != nullptr && m_commonSurf->IsPrimarySurface())) { - Logger::debug("DDraw4Surface::Flip: Skipping flip"); - return DD_OK; - } else { - return m_proxy->Flip(surf4->GetProxied(), dwFlags); - } - } + if (unlikely(surf4 != nullptr && !surf4->GetCommonSurface()->IsBackBufferOrFlippable())) { + Logger::debug("DDraw4Surface::Flip: Unflippable override surface"); + return DDERR_NOTFLIPPABLE; } // If the interface is waiting for VBlank and we get a no VSync flip, switch @@ -645,11 +537,11 @@ namespace dxvk { if (unlikely(m_commonIntf->GetWaitForVBlank() && (dwFlags & DDFLIP_NOVSYNC))) { Logger::info("DDraw4Surface::Flip: Switching to D3DPRESENT_INTERVAL_IMMEDIATE for presentation"); - D3DCommonDevice* commonDevice = m_commonIntf->GetCommonD3DDevice(); + D3DCommonDevice* commonD3DDevice = m_commonSurf->GetCommonD3DDevice(); - d3d9::D3DPRESENT_PARAMETERS resetParams = commonDevice->GetPresentParameters(); + d3d9::D3DPRESENT_PARAMETERS resetParams = commonD3DDevice->GetPresentParameters(); resetParams.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; - HRESULT hrReset = commonDevice->ResetD3D9Swapchain(&resetParams); + HRESULT hrReset = commonD3DDevice->ResetD3D9Swapchain(&resetParams); if (unlikely(FAILED(hrReset))) { Logger::warn("DDraw4Surface::Flip: Failed D3D9 swapchain reset"); } else { @@ -660,11 +552,11 @@ namespace dxvk { } else if (unlikely(!m_commonIntf->GetWaitForVBlank() && IsVSyncFlipFlag(dwFlags))) { Logger::info("DDraw4Surface::Flip: Switching to D3DPRESENT_INTERVAL_DEFAULT for presentation"); - D3DCommonDevice* commonDevice = m_commonIntf->GetCommonD3DDevice(); + D3DCommonDevice* commonD3DDevice = m_commonSurf->GetCommonD3DDevice(); - d3d9::D3DPRESENT_PARAMETERS resetParams = commonDevice->GetPresentParameters(); + d3d9::D3DPRESENT_PARAMETERS resetParams = commonD3DDevice->GetPresentParameters(); resetParams.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT; - HRESULT hrReset = commonDevice->ResetD3D9Swapchain(&resetParams); + HRESULT hrReset = commonD3DDevice->ResetD3D9Swapchain(&resetParams); if (unlikely(FAILED(hrReset))) { Logger::warn("DDraw4Surface::Flip: Failed D3D9 swapchain reset"); } else { @@ -672,9 +564,24 @@ namespace dxvk { } } - m_d3d9Device->Present(NULL, NULL, NULL, NULL); - // If we don't have a valid D3D6 device, this means a D3D7 application - // is trying to flip the surface. Allow that for compatibility reasons. + DDraw4Surface* rt = m_commonIntf->GetDDrawRenderTarget() != nullptr ? + m_commonIntf->GetDDrawRenderTarget()->GetDD4Surface() : nullptr; + + if (unlikely(rt != nullptr && m_commonSurf->IsPrimarySurface())) { + Logger::debug("DDraw4Surface::Flip: Presenting from DDraw RT"); + rt->InitializeOrUploadD3D9(); + d3d9Device->Present(NULL, NULL, NULL, NULL); + return DD_OK; + } + + if (likely(m_nextFlippable != nullptr)) { + m_nextFlippable->InitializeOrUploadD3D9(); + } else { + InitializeOrUploadD3D9(); + } + + d3d9Device->Present(NULL, NULL, NULL, NULL); + } else { Logger::debug("<<< DDraw4Surface::Flip: Proxy"); @@ -682,13 +589,7 @@ namespace dxvk { m_commonIntf->SetWaitForVBlank(IsVSyncFlipFlag(dwFlags)); if (unlikely(!m_commonIntf->IsWrappedSurface(lpDDSurfaceTargetOverride))) { - if (unlikely(m_commonIntf->GetOptions()->forceBlitOnFlip && - rt != nullptr && m_commonSurf->IsPrimarySurface())) { - Logger::debug("DDrawSurface::Flip: Blitting instead of flipping"); - return m_proxy->Blt(nullptr, rt->GetProxied(), nullptr, DDBLT_WAIT, nullptr); - } else { - return m_proxy->Flip(lpDDSurfaceTargetOverride, dwFlags); - } + return m_proxy->Flip(lpDDSurfaceTargetOverride, dwFlags); } else { return m_proxy->Flip(surf4->GetProxied(), dwFlags); } @@ -761,11 +662,6 @@ namespace dxvk { // Blitting can be done at any time and completes within its call frame HRESULT STDMETHODCALLTYPE DDraw4Surface::GetBltStatus(DWORD dwFlags) { - if (unlikely(m_commonIntf->GetOptions()->forceProxiedPresent)) { - Logger::debug("<<< DDraw4Surface::GetBltStatus: Proxy"); - m_proxy->GetBltStatus(dwFlags); - } - Logger::debug(">>> DDraw4Surface::GetBltStatus"); if (likely(dwFlags == DDGBS_CANBLT || dwFlags == DDGBS_ISBLTDONE)) @@ -809,42 +705,16 @@ namespace dxvk { } HRESULT STDMETHODCALLTYPE DDraw4Surface::GetDC(HDC *lphDC) { - if (likely(!m_commonIntf->GetOptions()->forceProxiedPresent)) { - if (unlikely(lphDC == nullptr)) - return DDERR_INVALIDPARAMS; - - InitReturnPtr(lphDC); - - // Foward GetDC calls if we have drawn and the surface is flippable - RefreshD3D9Device(); - if (m_d3d9Device != nullptr && (m_commonIntf->HasDrawn() && - m_commonSurf->IsGuardableSurface())) { - Logger::debug(">>> DDraw4Surface::GetDC"); - - if (unlikely(!IsInitialized())) { - HRESULT hrUpload = InitializeOrUploadD3D9(); - if (unlikely(FAILED(hrUpload))) - Logger::warn("DDraw4Surface::GetDC: Failed to initialize d3d9 surface"); - } + Logger::debug("<<< DDraw4Surface::GetDC: Proxy"); - HRESULT hr9 = m_d3d9->GetDC(lphDC); - if (unlikely(FAILED(hr9))) - Logger::warn("DDraw4Surface::GetDC: Failed D3D9 call"); - return hr9; - } - } + // Write back any dirty surface data from bound D3D9 back buffers or depth stencils + DownloadSurfaceData(); - Logger::debug("<<< DDraw4Surface::GetDC: Proxy"); - return m_proxy->GetDC(lphDC); + return GetShadowOrProxied()->GetDC(lphDC); } // Flipping can be done at any time and completes within its call frame HRESULT STDMETHODCALLTYPE DDraw4Surface::GetFlipStatus(DWORD dwFlags) { - if (unlikely(m_commonIntf->GetOptions()->forceProxiedPresent)) { - Logger::debug("<<< DDraw4Surface::GetFlipStatus: Proxy"); - m_proxy->GetFlipStatus(dwFlags); - } - Logger::debug(">>> DDraw4Surface::GetFlipStatus"); if (likely(dwFlags == DDGFS_CANFLIP || dwFlags == DDGFS_ISFLIPDONE)) @@ -916,75 +786,28 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE DDraw4Surface::Lock(LPRECT lpDestRect, LPDDSURFACEDESC2 lpDDSurfaceDesc, DWORD dwFlags, HANDLE hEvent) { Logger::debug("<<< DDraw4Surface::Lock: Proxy"); - // Write back any flippable surfaces or depth stencils from D3D9 - if (unlikely(m_commonSurf->IsGuardableSurface())) { - if (m_commonIntf->GetOptions()->backBufferWriteBack || m_commonIntf->GetOptions()->apitraceMode) { - Logger::debug("DDraw4Surface::Lock: Surface is a swapchain surface"); + // Write back any dirty surface data from bound D3D9 back buffers or depth stencils + DownloadSurfaceData(); - if (unlikely(m_commonIntf->GetOptions()->apitraceMode && !IsInitialized())) - InitializeOrUploadD3D9(); - - if (likely(IsInitialized())) - BlitToDDrawSurface(m_proxy.ptr(), m_d3d9.ptr()); - } else { - static bool s_swapchainWarningShown; - - if (!std::exchange(s_swapchainWarningShown, true)) - Logger::warn("DDraw4Surface::Lock: Surface is a swapchain surface"); - } - } else if (unlikely(m_commonSurf->IsDepthStencil())) { - if (m_commonIntf->GetOptions()->depthWriteBack || m_commonIntf->GetOptions()->apitraceMode) { - Logger::debug("DDraw4Surface::Lock: Surface is a depth stencil"); - - if (likely(IsInitialized())) - BlitToDDrawSurface(m_proxy.ptr(), m_d3d9.ptr()); - } else { - static bool s_depthStencilWarningShown; - - if (!std::exchange(s_depthStencilWarningShown, true)) - Logger::warn("DDraw4Surface::Lock: Surface is a depth stencil"); - } - } - - return m_proxy->Lock(lpDestRect, lpDDSurfaceDesc, dwFlags, hEvent); + return GetShadowOrProxied()->Lock(lpDestRect, lpDDSurfaceDesc, dwFlags, hEvent); } HRESULT STDMETHODCALLTYPE DDraw4Surface::ReleaseDC(HDC hDC) { - if (likely(!m_commonIntf->GetOptions()->forceProxiedPresent)) { - // Foward ReleaseDC calls if we have drawn and the surface is flippable - RefreshD3D9Device(); - if (m_d3d9Device != nullptr && (m_commonIntf->HasDrawn() && - m_commonSurf->IsGuardableSurface())) { - Logger::debug(">>> DDraw4Surface::ReleaseDC"); - - if (unlikely(!IsInitialized())) { - HRESULT hrUpload = InitializeOrUploadD3D9(); - if (unlikely(FAILED(hrUpload))) - Logger::warn("DDraw4Surface::ReleaseDC: Failed to initialize d3d9 surface"); - } - - HRESULT hr9 = m_d3d9->ReleaseDC(hDC); - if (unlikely(FAILED(hr9))) - Logger::warn("DDraw4Surface::ReleaseDC: Failed D3D9 call"); - return hr9; - } - } - Logger::debug("<<< DDraw4Surface::ReleaseDC: Proxy"); - HRESULT hr = m_proxy->ReleaseDC(hDC); + HRESULT hr = GetShadowOrProxied()->ReleaseDC(hDC); if (likely(SUCCEEDED(hr))) { - // Textures and cubemaps get uploaded during SetTexture calls - if (m_commonSurf->IsTexture()) { - m_commonSurf->DirtyMipMaps(); - } else if (unlikely(m_commonIntf->GetOptions()->apitraceMode)) { - // We should ideally upload the surface contents here at all times, - // however some games are amazing, and do hundreds of locks on the same - // surface per frame, so this would absolutely tank performance - HRESULT hrUpload = InitializeOrUploadD3D9(); - if (unlikely(FAILED(hrUpload))) - Logger::warn("DDraw4Surface::ReleaseDC: Failed upload to d3d9 surface"); + m_commonSurf->DirtyDDrawSurface(); + + d3d9::IDirect3DDevice9* d3d9Device = m_commonSurf->RefreshD3D9Device(); + if (unlikely(m_shadowSurf != nullptr && d3d9Device != nullptr)) { + const bool shouldPresent = m_commonIntf->GetOptions()->legacyPresentGuard == D3DLegacyPresentGuard::Auto ? + !m_commonSurf->GetCommonD3DDevice()->IsInScene() : + m_commonIntf->GetOptions()->legacyPresentGuard == D3DLegacyPresentGuard::Strict ? + false : true; + if (shouldPresent) + d3d9Device->Present(NULL, NULL, NULL, NULL); } } @@ -1014,6 +837,15 @@ namespace dxvk { return hr; m_commonSurf->SetClipper(ddrawClipper); + + // Retrieve a hWnd, if needed, during clipper attachment + HWND hWnd = nullptr; + hr = ddrawClipper->GetProxied()->GetHWnd(&hWnd); + if (unlikely(FAILED(hr))) { + Logger::debug("DDraw4Surface::SetClipper: Failed to retrieve hWnd"); + } else { + m_commonIntf->SetHWND(hWnd); + } } return DD_OK; @@ -1022,6 +854,16 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE DDraw4Surface::SetColorKey(DWORD dwFlags, LPDDCOLORKEY lpDDColorKey) { Logger::debug("<<< DDraw4Surface::SetColorKey: Proxy"); + // The Combat Mission series of games set a color key which is + // outside the color range of the surface they are setting it on... + // clamp it to the surface color depth in that case. This doesn't + // appear to work well universally, however, so only apply when needed. + if (unlikely(m_commonIntf->GetOptions()->colorKeyMasking && lpDDColorKey != nullptr)) { + const uint8_t colorBitCount = m_commonSurf->GetColorBitCount(); + lpDDColorKey->dwColorSpaceLowValue &= (1 << colorBitCount) - 1; + lpDDColorKey->dwColorSpaceHighValue &= (1 << colorBitCount) - 1; + } + HRESULT hr = m_proxy->SetColorKey(dwFlags, lpDDColorKey); if (unlikely(FAILED(hr))) return hr; @@ -1030,6 +872,17 @@ namespace dxvk { if (unlikely(FAILED(hr))) Logger::err("DDraw4Surface::SetColorKey: Failed to retrieve updated surface desc"); + if (unlikely(m_shadowSurf != nullptr)) { + hr = m_shadowSurf->GetProxied()->SetColorKey(dwFlags, lpDDColorKey); + if (unlikely(FAILED(hr))) { + Logger::warn("DDraw4Surface::SetColorKey: Failed to set shadow surface color key"); + } else { + hr = m_shadowSurf->GetCommonSurface()->RefreshSurfaceDescripton(); + if (unlikely(FAILED(hr))) + Logger::warn("DDraw4Surface::SetColorKey: Failed to retrieve updated shadow surface desc"); + } + } + return DD_OK; } @@ -1043,7 +896,7 @@ namespace dxvk { // A nullptr lpDDPalette gets the current palette detached if (lpDDPalette == nullptr) { - HRESULT hr = m_proxy->SetPalette(lpDDPalette); + HRESULT hr = GetShadowOrProxied()->SetPalette(lpDDPalette); if (unlikely(FAILED(hr))) return hr; @@ -1051,7 +904,7 @@ namespace dxvk { } else { DDrawPalette* ddrawPalette = static_cast(lpDDPalette); - HRESULT hr = m_proxy->SetPalette(ddrawPalette->GetProxied()); + HRESULT hr = GetShadowOrProxied()->SetPalette(ddrawPalette->GetProxied()); if (unlikely(FAILED(hr))) return hr; @@ -1064,16 +917,21 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE DDraw4Surface::Unlock(LPRECT lpSurfaceData) { Logger::debug("<<< DDraw4Surface::Unlock: Proxy"); - HRESULT hr = m_proxy->Unlock(lpSurfaceData); + HRESULT hr = GetShadowOrProxied()->Unlock(lpSurfaceData); if (likely(SUCCEEDED(hr))) { - // Textures and cubemaps get uploaded during SetTexture calls - if (!m_commonSurf->IsTexture()) { - HRESULT hrUpload = InitializeOrUploadD3D9(); - if (unlikely(FAILED(hrUpload))) - Logger::warn("DDraw4Surface::Unlock: Failed upload to d3d9 surface"); - } else { - m_commonSurf->DirtyMipMaps(); + m_commonSurf->DirtyDDrawSurface(); + + d3d9::IDirect3DDevice9* d3d9Device = m_commonSurf->RefreshD3D9Device(); + if (unlikely(m_shadowSurf != nullptr && d3d9Device != nullptr)) { + const bool shouldPresent = m_commonIntf->GetOptions()->legacyPresentGuard == D3DLegacyPresentGuard::Auto ? + !m_commonSurf->GetCommonD3DDevice()->IsInScene() : + m_commonIntf->GetOptions()->legacyPresentGuard == D3DLegacyPresentGuard::Strict ? + false : true; + if (shouldPresent) { + InitializeOrUploadD3D9(); + d3d9Device->Present(NULL, NULL, NULL, NULL); + } } } @@ -1154,13 +1012,7 @@ namespace dxvk { Logger::err("DDraw4Surface::SetSurfaceDesc: Failed to retrieve updated surface desc"); // We may need to recreate the d3d9 object based on the new desc - m_d3d9 = nullptr; - - if (!m_commonSurf->IsTexture()) { - InitializeOrUploadD3D9(); - } else { - m_commonSurf->DirtyMipMaps(); - } + m_commonSurf->SetD3D9Surface(nullptr); return hr; } @@ -1198,396 +1050,178 @@ namespace dxvk { return DD_OK; } - HRESULT DDraw4Surface::InitializeD3D9RenderTarget() { - HRESULT hr = DD_OK; - - RefreshD3D9Device(); + IDirectDrawSurface4* DDraw4Surface::GetShadowOrProxied() { + d3d9::IDirect3DDevice9* d3d9Device = m_commonSurf->RefreshD3D9Device(); - if (unlikely(!IsInitialized())) - hr = InitializeD3D9(true); + if (unlikely(m_shadowSurf != nullptr && d3d9Device != nullptr)) + return m_shadowSurf->GetProxied(); - return hr; + return m_proxy.ptr(); } - HRESULT DDraw4Surface::InitializeD3D9DepthStencil() { - HRESULT hr = DD_OK; - - RefreshD3D9Device(); + HRESULT DDraw4Surface::InitializeD3D9RenderTarget() { + m_commonSurf->RefreshD3D9Device(); - if (unlikely(!IsInitialized())) - hr = InitializeD3D9(false); + m_commonSurf->MarkAsD3D9BackBuffer(); - return hr; - } + if (unlikely(!m_commonSurf->IsInitialized())) { + if (m_commonSurf->IsTexture()) + UpdateMipMapCount(); - HRESULT DDraw4Surface::InitializeOrUploadD3D9() { - HRESULT hr = DD_OK; + HRESULT hr = m_commonSurf->InitializeD3D9(true); + if (unlikely(FAILED(hr))) + return hr; - RefreshD3D9Device(); + Logger::debug(str::format("DDraw4Surface::InitializeD3D9RenderTarget: Initialized surface nr. [[4-", m_surfCount, "]]")); - if (likely(IsInitialized())) { - hr = UploadSurfaceData(); - } else { - hr = InitializeD3D9(false); + return UploadSurfaceData(); } - return hr; + return D3D_OK; } - inline HRESULT DDraw4Surface::InitializeD3D9(const bool initRT) { - if (unlikely(m_d3d9Device == nullptr)) { - Logger::debug("DDraw4Surface::InitializeD3D9: Null device, can't initialize right now"); - return DD_OK; - } - - Logger::debug(str::format("DDraw4Surface::InitializeD3D9: Initializing nr. [[4-", m_surfCount, "]]")); - - const DDSURFACEDESC2* desc2 = m_commonSurf->GetDesc2(); - const d3d9::D3DFORMAT format = m_commonSurf->GetD3D9Format(); - - if (unlikely(desc2->dwHeight == 0 || desc2->dwWidth == 0)) { - Logger::err("DDraw4Surface::InitializeD3D9: Surface has 0 height or width"); - return DDERR_GENERIC; - } - - if (unlikely(format == d3d9::D3DFMT_UNKNOWN)) { - Logger::err("DDraw4Surface::InitializeD3D9: Surface has an unknown format"); - return DDERR_GENERIC; - } - - // Don't initialize P8 textures/surfaces since we don't support them. - // Some applications do require them to be created by ddraw, otherwise - // they will simply fail to start, so just ignore them for now. - if (unlikely(format == d3d9::D3DFMT_P8)) { - static bool s_formatP8ErrorShown; - - if (!std::exchange(s_formatP8ErrorShown, true)) - Logger::warn("DDraw4Surface::InitializeD3D9: Unsupported format D3DFMT_P8"); + HRESULT DDraw4Surface::InitializeD3D9DepthStencil() { + m_commonSurf->RefreshD3D9Device(); - return DD_OK; + m_commonSurf->MarkAsD3D9DepthStencil(); - // Similarly, D3DFMT_R3G3B2 isn't supported by D3D9 dxvk, however some - // applications require it to be supported by ddraw, even if they do not - // use it. Simply ignore any D3DFMT_R3G3B2 textures/surfaces for now. - } else if (unlikely(format == d3d9::D3DFMT_R3G3B2)) { - static bool s_formatR3G3B2ErrorShown; + if (unlikely(!m_commonSurf->IsInitialized())) { + HRESULT hr = m_commonSurf->InitializeD3D9(false); + if (unlikely(FAILED(hr))) + return hr; - if (!std::exchange(s_formatR3G3B2ErrorShown, true)) - Logger::warn("DDraw4Surface::InitializeD3D9: Unsupported format D3DFMT_R3G3B2"); + Logger::debug(str::format("DDraw4Surface::InitializeD3D9DepthStencil: Initialized surface nr. [[4-", m_surfCount, "]]")); - return DD_OK; + return UploadSurfaceData(); } - // We need to count the number of actual mips on initialization by going through - // the mip chain, since the dwMipMapCount number may or may not be accurate. I am - // guessing it was intended more as a hint, not neceesarily a set number. - if (m_commonSurf->IsTexture()) { - IDirectDrawSurface4* mipMap = m_proxy.ptr(); - DDSURFACEDESC2 mipDesc2; - uint16_t mipCount = 1; - - while (mipMap != nullptr) { - IDirectDrawSurface4* parentSurface = mipMap; - mipMap = nullptr; - parentSurface->EnumAttachedSurfaces(&mipMap, ListMipChainSurfaces4Callback); - if (mipMap != nullptr) { - mipCount++; - - mipDesc2 = { }; - mipDesc2.dwSize = sizeof(DDSURFACEDESC2); - mipMap->GetSurfaceDesc(&mipDesc2); - // Ignore multiple 1x1 mips, which apparently can get generated if the - // application gets the dwMipMapCount wrong vs surface dimensions. - if (unlikely(mipDesc2.dwWidth == 1 && mipDesc2.dwHeight == 1)) - break; - } - } - - // Do not worry about maximum supported mip map levels validation, - // because D3D9 will handle this for us and cap them appropriately - if (mipCount > 1) { - Logger::debug(str::format("DDraw4Surface::InitializeD3D9: Found ", mipCount, " mip levels")); - - if (unlikely(mipCount != desc2->dwMipMapCount)) - Logger::debug(str::format("DDraw4Surface::InitializeD3D9: Mismatch with declared ", desc2->dwMipMapCount, " mip levels")); - } - - if (unlikely(m_commonIntf->GetOptions()->autoGenMipMaps)) { - Logger::debug("DDraw4Surface::InitializeD3D9: Using auto mip map generation"); - mipCount = 0; - } - - m_commonSurf->SetMipCount(mipCount); - } + return DD_OK; + } - d3d9::D3DPOOL pool = d3d9::D3DPOOL_DEFAULT; - DWORD usage = 0; - - // General surface/texture pool placement - if (desc2->ddsCaps.dwCaps & DDSCAPS_LOCALVIDMEM) - pool = d3d9::D3DPOOL_DEFAULT; - // There's no explicit non-local video memory placement - // per se, but D3DPOOL_MANAGED is close enough - else if ((desc2->ddsCaps.dwCaps & DDSCAPS_NONLOCALVIDMEM) || (desc2->ddsCaps.dwCaps2 & DDSCAPS2_TEXTUREMANAGE)) - pool = d3d9::D3DPOOL_MANAGED; - else if (desc2->ddsCaps.dwCaps & DDSCAPS_SYSTEMMEMORY) - // We can't know beforehand if a texture is or isn't going to be - // used in SetTexture() calls, and textures placed in D3DPOOL_SYSTEMMEM - // will not work in that context in dxvk, so revert to D3DPOOL_MANAGED. - pool = m_commonSurf->IsTexture() ? d3d9::D3DPOOL_MANAGED : d3d9::D3DPOOL_SYSTEMMEM; - - // Place all possible render targets in DEFAULT - // - // Note: This is somewhat problematic for textures and cube maps - // which will have D3DUSAGE_RENDERTARGET, but also need to have - // D3DUSAGE_DYNAMIC for locking/uploads to work. The flag combination - // isn't supported in D3D9, but we have a D3D7 exception in place. - // - if (m_commonSurf->IsRenderTarget() || initRT) { - Logger::debug("DDraw4Surface::InitializeD3D9: Usage: D3DUSAGE_RENDERTARGET"); - pool = d3d9::D3DPOOL_DEFAULT; - usage |= D3DUSAGE_RENDERTARGET; - } - // All depth stencils will be created in DEFAULT - if (m_commonSurf->IsDepthStencil()) { - Logger::debug("DDraw4Surface::InitializeD3D9: Usage: D3DUSAGE_DEPTHSTENCIL"); - pool = d3d9::D3DPOOL_DEFAULT; - usage |= D3DUSAGE_DEPTHSTENCIL; - } + HRESULT DDraw4Surface::InitializeOrUploadD3D9() { + d3d9::IDirect3DDevice9* d3d9Device = m_commonSurf->RefreshD3D9Device(); - // General usage flags - if (m_commonSurf->IsTexture()) { - if (pool == d3d9::D3DPOOL_DEFAULT) { - Logger::debug("DDraw4Surface::InitializeD3D9: Usage: D3DUSAGE_DYNAMIC"); - usage |= D3DUSAGE_DYNAMIC; - } - if (unlikely(m_commonIntf->GetOptions()->autoGenMipMaps)) { - Logger::debug("DDraw4Surface::InitializeD3D9: Usage: D3DUSAGE_AUTOGENMIPMAP"); - usage |= D3DUSAGE_AUTOGENMIPMAP; - } + // Fast skip + if (unlikely(d3d9Device == nullptr)) { + Logger::debug("DDraw4Surface::InitializeOrUploadD3D9: Null device, can't initialize right now"); + return D3D_OK; } - const char* poolPlacement = pool == d3d9::D3DPOOL_DEFAULT ? "D3DPOOL_DEFAULT" : - pool == d3d9::D3DPOOL_SYSTEMMEM ? "D3DPOOL_SYSTEMMEM" : "D3DPOOL_MANAGED"; - - Logger::debug(str::format("DDraw4Surface::InitializeD3D9: Placing in: ", poolPlacement)); - - // Use the MSAA type that was determined to be supported during device creation - const d3d9::D3DMULTISAMPLE_TYPE multiSampleType = m_commonIntf->GetCommonD3DDevice()->GetMultiSampleType(); - const uint32_t index = m_commonSurf->GetBackBufferIndex(); - - Com surf; - - HRESULT hr = DDERR_GENERIC; - - // Front Buffer - if (m_commonSurf->IsFrontBuffer()) { - Logger::debug("DDraw4Surface::InitializeD3D9: Initializing front buffer..."); - - m_d3d9Device->GetBackBuffer(0, index, d3d9::D3DBACKBUFFER_TYPE_MONO, &surf); - - if (unlikely(surf == nullptr)) { - Logger::err("DDraw4Surface::InitializeD3D9: Failed to retrieve front buffer"); - m_d3d9 = nullptr; - return hr; - } - - m_d3d9 = std::move(surf); - - // Back Buffer - } else if (m_commonSurf->IsBackBufferOrFlippable()) { - Logger::debug("DDraw4Surface::InitializeD3D9: Initializing back buffer..."); - - m_d3d9Device->GetBackBuffer(0, index, d3d9::D3DBACKBUFFER_TYPE_MONO, &surf); - - if (unlikely(surf == nullptr)) { - Logger::err("DDraw4Surface::InitializeD3D9: Failed to retrieve back buffer"); - m_d3d9 = nullptr; - return hr; - } + if (unlikely(!m_commonSurf->IsInitialized())) { + if (m_commonSurf->IsTexture()) + UpdateMipMapCount(); - m_d3d9 = std::move(surf); + const bool initRenderTarget = m_commonSurf->GetCommonD3DDevice()->IsCurrentRenderTarget(m_commonSurf.ptr()); - // Textures - } else if (m_commonSurf->IsTexture()) { - Logger::debug("DDraw4Surface::InitializeD3D9: Initializing texture..."); - - Com tex; - - hr = m_d3d9Device->CreateTexture( - desc2->dwWidth, desc2->dwHeight, m_commonSurf->GetMipCount(), usage, - format, pool, &tex, nullptr); - - if (unlikely(FAILED(hr))) { - Logger::err("DDraw4Surface::InitializeD3D9: Failed to create texture"); - m_texture9 = nullptr; - return hr; - } - - if (unlikely(m_commonIntf->GetOptions()->autoGenMipMaps)) - tex->SetAutoGenFilterType(d3d9::D3DTEXF_ANISOTROPIC); - - // Attach level 0 to this surface - tex->GetSurfaceLevel(0, &surf); - m_d3d9 = (std::move(surf)); - - Logger::debug("DDraw4Surface::InitializeD3D9: Created texture"); - m_texture9 = std::move(tex); - - // Depth Stencil - } else if (m_commonSurf->IsDepthStencil()) { - Logger::debug("DDraw4Surface::InitializeD3D9: Initializing depth stencil..."); - - hr = m_d3d9Device->CreateDepthStencilSurface( - desc2->dwWidth, desc2->dwHeight, format, - multiSampleType, 0, FALSE, &surf, nullptr); - - if (unlikely(FAILED(hr))) { - Logger::err("DDraw4Surface::InitializeD3D9: Failed to create DS"); - m_d3d9 = nullptr; + HRESULT hr = m_commonSurf->InitializeD3D9(initRenderTarget); + if (unlikely(FAILED(hr))) return hr; - } - - Logger::debug("DDraw4Surface::InitializeD3D9: Created depth stencil surface"); - - m_d3d9 = std::move(surf); - // Offscreen Plain Surfaces - } else if (m_commonSurf->IsOffScreenPlainSurface()) { - Logger::debug("DDraw4Surface::InitializeD3D9: Initializing offscreen plain surface..."); - - // Sometimes we get passed offscreen plain surfaces which should be tied to the back buffer, - // either as existing RTs or during SetRenderTarget() calls, which are tracked with initRT - if (unlikely(m_commonIntf->GetCommonD3DDevice()->IsCurrentRenderTarget(this) || initRT)) { - Logger::debug("DDraw4Surface::InitializeD3D9: Offscreen plain surface is the RT"); + Logger::debug(str::format("DDraw4Surface::InitializeOrUploadD3D9: Initialized surface nr. [[4-", m_surfCount, "]]")); + } - m_d3d9Device->GetBackBuffer(0, index, d3d9::D3DBACKBUFFER_TYPE_MONO, &surf); + if (likely(m_commonSurf->IsInitialized())) + return UploadSurfaceData(); - if (unlikely(surf == nullptr)) { - Logger::err("DDraw4Surface::InitializeD3D9: Failed to retrieve offscreen plain surface"); - m_d3d9 = nullptr; - return hr; - } - } else { - hr = m_d3d9Device->CreateOffscreenPlainSurface( - desc2->dwWidth, desc2->dwHeight, format, - pool, &surf, nullptr); + return DD_OK; + } - if (unlikely(FAILED(hr))) { - Logger::err("DDraw4Surface::InitializeD3D9: Failed to create offscreen plain surface"); - m_d3d9 = nullptr; - return hr; - } + void DDraw4Surface::DownloadSurfaceData() { + // Some games, like The Settlers IV, use multiple devices for rendering, one to handle + // terrain and the overall 3D scene, and one to create textures/sprites to overlay on + // top of it. Since DXVK's D3D9 backend does not restrict cross-device surface/texture + // use, simply skip changing assigned surface devices during downloads. This is essentially + // a hack, which by some miracle works well enough in some cases, though may explode in others. + if (likely(!m_commonIntf->GetOptions()->deviceResourceSharing)) + m_commonSurf->RefreshD3D9Device(); + + if (unlikely(m_commonSurf->IsD3D9BackBuffer())) { + Logger::debug("DDraw4Surface::DownloadSurfaceData: Surface is a bound swapchain surface"); + + if (m_commonSurf->IsInitialized() && m_commonSurf->IsD3D9SurfaceDirty()) { + Logger::debug(str::format("DDraw4Surface::DownloadSurfaceData: Downloading nr. [[4-", m_surfCount, "]]")); + BlitToDDrawSurface(GetShadowOrProxied(), m_commonSurf->GetD3D9Surface(), + m_commonSurf->IsDXTFormat()); + m_commonSurf->UnDirtyD3D9Surface(); } - - m_d3d9 = std::move(surf); - - // Overlays (haven't seen any actual use of overlays in the wild) - } else if (m_commonSurf->IsOverlay()) { - Logger::debug("DDraw4Surface::InitializeD3D9: Initializing overlay..."); - - // Always link overlays to the back buffer - m_d3d9Device->GetBackBuffer(0, index, d3d9::D3DBACKBUFFER_TYPE_MONO, &surf); - - if (unlikely(surf == nullptr)) { - Logger::err("DDraw4Surface::InitializeD3D9: Failed to retrieve overlay surface"); - m_d3d9 = nullptr; - return hr; + } else if (unlikely(m_commonSurf->IsD3D9DepthStencil())) { + Logger::debug("DDraw4Surface::DownloadSurfaceData: Surface is a bound depth stencil"); + + if (m_commonSurf->IsInitialized() && m_commonSurf->IsD3D9SurfaceDirty()) { + Logger::debug(str::format("DDraw4Surface::DownloadSurfaceData: Downloading nr. [[4-", m_surfCount, "]]")); + BlitToDDrawSurface(m_proxy.ptr(), m_commonSurf->GetD3D9Surface(), + m_commonSurf->IsDXTFormat()); + m_commonSurf->UnDirtyD3D9Surface(); } + } + } - m_d3d9 = std::move(surf); - - // Generic render target - } else if (m_commonSurf->IsRenderTarget()) { - Logger::debug("DDraw4Surface::InitializeD3D9: Initializing render target..."); - - // Must be lockable for blitting to work. Note that - // D3D9 does not allow the creation of lockable RTs when - // using MSAA, but we have a D3D7 exception in place. - hr = m_d3d9Device->CreateRenderTarget( - desc2->dwWidth, desc2->dwHeight, format, - multiSampleType, usage, TRUE, &surf, nullptr); - - if (unlikely(FAILED(hr))) { - Logger::err("DDraw4Surface::InitializeD3D9: Failed to create render target"); - m_d3d9 = nullptr; - return hr; - } - - m_d3d9 = std::move(surf); - - // We sometimes get generic surfaces, with only dimensions, format and placement info - } else if (!m_commonSurf->IsNotKnown()) { - Logger::debug("DDraw4Surface::InitializeD3D9: Initializing generic surface..."); - - hr = m_d3d9Device->CreateOffscreenPlainSurface( - desc2->dwWidth, desc2->dwHeight, format, - pool, &surf, nullptr); + inline void DDraw4Surface::UpdateMipMapCount() { + // We need to count the number of actual mips on initialization by going through + // the mip chain, since the dwMipMapCount number may or may not be accurate. I am + // guessing it was intended more as a hint, not neceesarily a set number. + const DDSURFACEDESC2* desc2 = m_commonSurf->GetDesc2(); - if (unlikely(FAILED(hr))) { - Logger::err("DDraw4Surface::InitializeD3D9: Failed to create offscreen plain surface"); - m_d3d9 = nullptr; - return hr; + IDirectDrawSurface4* mipMap = m_proxy.ptr(); + DDSURFACEDESC2 mipDesc2; + uint16_t mipCount = 1; + + while (mipMap != nullptr) { + IDirectDrawSurface4* parentSurface = mipMap; + mipMap = nullptr; + parentSurface->EnumAttachedSurfaces(&mipMap, ListMipChainSurfaces4Callback); + if (mipMap != nullptr) { + mipCount++; + + mipDesc2 = { }; + mipDesc2.dwSize = sizeof(DDSURFACEDESC2); + mipMap->GetSurfaceDesc(&mipDesc2); + // Ignore multiple 1x1 mips, which apparently can get generated if the + // application gets the dwMipMapCount wrong vs surface dimensions. + if (unlikely(mipDesc2.dwWidth == 1 && mipDesc2.dwHeight == 1)) + break; } + } - Logger::debug("DDraw4Surface::InitializeD3D9: Created offscreen plain surface"); + // Do not worry about maximum supported mip map levels validation, + // because D3D9 will handle this for us and cap them appropriately + if (mipCount > 1) { + Logger::debug(str::format("DDraw4Surface::UpdateMipMapCount: Found ", mipCount, " mip levels")); - m_d3d9 = std::move(surf); - } else { - Logger::warn("DDraw4Surface::InitializeD3D9: Skipping initialization of unknown surface"); + if (unlikely(mipCount != desc2->dwMipMapCount)) + Logger::debug(str::format("DDraw4Surface::UpdateMipMapCount: Mismatch with declared ", desc2->dwMipMapCount, " mip levels")); } - // Depth stencils will not need uploads post initialization - if (likely(!m_commonSurf->IsDepthStencil())) - UploadSurfaceData(); + if (unlikely(m_commonIntf->GetOptions()->autoGenMipMaps)) { + Logger::debug("DDraw4Surface::UpdateMipMapCount: Using auto mip map generation"); + mipCount = 0; + } - return DD_OK; + m_commonSurf->SetMipCount(mipCount); } inline HRESULT DDraw4Surface::UploadSurfaceData() { - Logger::debug(str::format("DDraw4Surface::UploadSurfaceData: Uploading nr. [[4-", m_surfCount, "]]")); - - if (unlikely(m_commonIntf->HasDrawn() && m_commonSurf->IsGuardableSurface())) { - Logger::debug("DDraw4Surface::UploadSurfaceData: Skipping upload"); + // Fast skip + if (!m_commonSurf->IsDDrawSurfaceDirty()) return DD_OK; - } - const d3d9::D3DFORMAT format = m_commonSurf->GetD3D9Format(); + Logger::debug(str::format("DDraw4Surface::UploadSurfaceData: Uploading nr. [[4-", m_surfCount, "]]")); if (m_commonSurf->IsTexture()) { - BlitToD3D9Texture(m_texture9.ptr(), format, - m_proxy.ptr(), m_commonSurf->GetMipCount()); + BlitToD3D9Texture(m_commonSurf->GetD3D9Texture(), m_proxy.ptr(), + m_commonSurf->GetMipCount(), m_commonSurf->IsDXTFormat()); // Blit surfaces directly } else { - if (unlikely(m_commonSurf->IsDepthStencil())) { - if (likely(m_commonIntf->GetOptions()->uploadDepthStencils)) { - Logger::debug("DDraw4Surface::UploadSurfaceData: Uploading depth stencil"); - } else { - Logger::debug("DDraw4Surface::UploadSurfaceData: Skipping upload of depth stencil"); - return DD_OK; - } - } + if (unlikely(m_commonSurf->IsDepthStencil())) + Logger::debug("DDrawSurface::UploadSurfaceData: Uploading depth stencil"); - BlitToD3D9Surface(m_d3d9.ptr(), format, m_proxy.ptr()); + BlitToD3D9Surface(m_commonSurf->GetD3D9Surface(), GetShadowOrProxied(), + m_commonSurf->IsDXTFormat()); } - return DD_OK; - } - - inline void DDraw4Surface::RefreshD3D9Device() { - D3DCommonDevice* commonDevice = m_commonIntf->GetCommonD3DDevice(); + m_commonSurf->UnDirtyDDrawSurface(); - d3d9::IDirect3DDevice9* d3d9Device = commonDevice != nullptr ? commonDevice->GetD3D9Device() : nullptr; - if (unlikely(m_d3d9Device != d3d9Device)) { - // Check if the device has been recreated and reset all D3D9 resources - if (m_d3d9Device != nullptr) { - Logger::debug("DDraw4Surface: Device context has changed, clearing all D3D9 resources"); - m_texture9 = nullptr; - m_d3d9 = nullptr; - } - - m_d3d9Device = d3d9Device; - } + return DD_OK; } } diff --git a/src/ddraw/ddraw4/ddraw4_surface.h b/src/ddraw/ddraw4/ddraw4_surface.h index a2899573043..c2ce5adb029 100644 --- a/src/ddraw/ddraw4/ddraw4_surface.h +++ b/src/ddraw/ddraw4/ddraw4_surface.h @@ -15,12 +15,12 @@ namespace dxvk { - class DDraw7Surface; + class D3DCommonDevice; /** * \brief IDirectDrawSurface4 interface implementation */ - class DDraw4Surface final : public DDrawWrappedObject { + class DDraw4Surface final : public DDrawWrappedObject { public: @@ -119,6 +119,24 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE ChangeUniquenessValue(); + IDirectDrawSurface4* GetShadowOrProxied(); + + HRESULT InitializeD3D9RenderTarget(); + + HRESULT InitializeD3D9DepthStencil(); + + HRESULT InitializeOrUploadD3D9(); + + void DownloadSurfaceData(); + + void SetShadowSurface(Com&& shadowSurf) { + m_shadowSurf = shadowSurf; + } + + DDraw4Surface* GetShadowSurface() const { + return m_shadowSurf.ptr(); + } + DDrawCommonSurface* GetCommonSurface() const { return m_commonSurf.ptr(); } @@ -127,12 +145,8 @@ namespace dxvk { return m_commonIntf; } - d3d9::IDirect3DDevice9* GetD3D9Device() const { - return m_d3d9Device; - } - - d3d9::IDirect3DTexture9* GetD3D9Texture() const { - return m_texture9.ptr(); + void SetAttachedDepthStencil(Com&& depthStencil) { + m_depthStencil = depthStencil; } DDraw4Surface* GetAttachedDepthStencil() { @@ -166,39 +180,33 @@ namespace dxvk { m_commonSurf->SetIsAttached(false); } - HRESULT InitializeD3D9RenderTarget(); - - HRESULT InitializeD3D9DepthStencil(); - - HRESULT InitializeOrUploadD3D9(); - private: - inline HRESULT InitializeD3D9(const bool initRT); + inline void UpdateMipMapCount(); inline HRESULT UploadSurfaceData(); - inline void RefreshD3D9Device(); - bool m_isChildObject = true; static uint32_t s_surfCount; - uint32_t m_surfCount = 0; + uint32_t m_surfCount = 0; - Com m_commonSurf; - DDrawCommonInterface* m_commonIntf = nullptr; + Com m_commonSurf; + DDrawCommonInterface* m_commonIntf = nullptr; - DDraw4Surface* m_parentSurf = nullptr; + DDraw4Surface* m_parentSurf = nullptr; - d3d9::IDirect3DDevice9* m_d3d9Device = nullptr; + Com m_texture6; - Com m_texture6; + DDraw4Surface* m_nextFlippable = nullptr; - Com m_texture9; + // Offscreen plain surface we use to mask unwanted DDraw interactions, such + // as forced swapchain presents caused by blits/locks on primary surfaces + Com m_shadowSurf; // Back buffers will have depth stencil surfaces as attachments (in practice // I have never seen more than one depth stencil being attached at a time) - Com m_depthStencil; + Com m_depthStencil; // These are attached surfaces, which are typically mips or other types of generated // surfaces, which need to exist for the entire lifecycle of their parent surface. diff --git a/src/ddraw/ddraw7/ddraw7_interface.cpp b/src/ddraw/ddraw7/ddraw7_interface.cpp index f46e3b9531d..1737ff6c864 100644 --- a/src/ddraw/ddraw7/ddraw7_interface.cpp +++ b/src/ddraw/ddraw7/ddraw7_interface.cpp @@ -21,7 +21,7 @@ namespace dxvk { DDraw7Interface::DDraw7Interface( DDrawCommonInterface* commonIntf, Com&& proxyIntf) - : DDrawWrappedObject(nullptr, std::move(proxyIntf), nullptr) + : DDrawWrappedObject(nullptr, std::move(proxyIntf)) , m_commonIntf ( commonIntf ) { // We need a temporary D3D9 interface at this point to retrieve the // adapter identifier, as well as (potentially) the options through a bridge @@ -48,12 +48,6 @@ namespace dxvk { m_commonIntf->SetDD7Interface(this); - static bool s_apitraceModeWarningShown; - - if (unlikely(m_commonIntf->GetOptions()->apitraceMode && - !std::exchange(s_apitraceModeWarningShown, true))) - Logger::warn("DDraw7Interface: Apitrace mode is enabled. Performance will be suboptimal!"); - m_intfCount = ++s_intfCount; Logger::debug(str::format("DDraw7Interface: Created a new interface nr. <<7-", m_intfCount, ">>")); @@ -237,9 +231,7 @@ namespace dxvk { // D3D9, so always strip the DDSCAPS_WRITEONLY flag on creation lpDDSurfaceDesc->ddsCaps.dwCaps &= ~DDSCAPS_WRITEONLY; // Similarly strip the DDSCAPS2_OPAQUE flag on texture creation - if (lpDDSurfaceDesc->ddsCaps.dwCaps & DDSCAPS_TEXTURE) { - lpDDSurfaceDesc->ddsCaps.dwCaps2 &= ~DDSCAPS2_OPAQUE; - } + lpDDSurfaceDesc->ddsCaps.dwCaps2 &= ~DDSCAPS2_OPAQUE; if (unlikely((lpDDSurfaceDesc->ddsCaps.dwCaps & DDSCAPS_ZBUFFER) && (lpDDSurfaceDesc->ddpfPixelFormat.dwZBitMask == 0xFFFFFFFF))) { @@ -260,8 +252,40 @@ namespace dxvk { if (likely(SUCCEEDED(hr))) { try{ Com surface7 = new DDraw7Surface(nullptr, std::move(ddraw7SurfaceProxied), this, nullptr, true); - if (unlikely(lpDDSurfaceDesc->ddsCaps.dwCaps & DDSCAPS_PRIMARYSURFACE)) + + if (unlikely(lpDDSurfaceDesc->ddsCaps.dwCaps & DDSCAPS_PRIMARYSURFACE)) { m_commonIntf->SetPrimarySurface(surface7->GetCommonSurface()); + + // Shadow surface creation for the primary surface + // (it needs to be based on the same incoming desc) + if (unlikely(!surface7->GetCommonSurface()->Is8BitFormat() && + m_commonIntf->GetOptions()->forceLegacyPresent)) { + Logger::debug("DDraw7Interface::CreateSurface: Creating shadow surface"); + + DDSURFACEDESC2 shadowDesc = *lpDDSurfaceDesc; + const DDSURFACEDESC2* primaryDesc = surface7->GetCommonSurface()->GetDesc2(); + + shadowDesc.ddsCaps.dwCaps &= ~DDSCAPS_PRIMARYSURFACE & ~DDSCAPS_COMPLEX & ~DDSCAPS_FLIP; + shadowDesc.ddsCaps.dwCaps |= DDSCAPS_OFFSCREENPLAIN; + shadowDesc.dwFlags &= ~DDSD_BACKBUFFERCOUNT; + // Dimensions aren't specified in the incoming desc, + // but are explicitly needed for non-primary surfaces + shadowDesc.dwFlags |= DDSD_WIDTH | DDSD_HEIGHT; + shadowDesc.dwWidth = primaryDesc->dwWidth; + shadowDesc.dwHeight = primaryDesc->dwHeight; + + Com ddraw7SurfaceShadow; + hr = m_proxy->CreateSurface(&shadowDesc, &ddraw7SurfaceShadow, pUnkOuter); + if (unlikely(FAILED(hr))) { + Logger::warn("DDraw7Interface::CreateSurface: Failed to create shadow surface"); + } else { + Com shadowSurf = new DDraw7Surface(nullptr, std::move(ddraw7SurfaceShadow), + this, nullptr, false); + surface7->SetShadowSurface(std::move(shadowSurf)); + } + } + } + *lplpDDSurface = surface7.ref(); } catch (const DxvkError& e) { Logger::err(e.message()); @@ -339,11 +363,6 @@ namespace dxvk { } HRESULT STDMETHODCALLTYPE DDraw7Interface::FlipToGDISurface() { - if (unlikely(m_commonIntf->GetOptions()->forceProxiedPresent)) { - Logger::debug("<<< DDraw7Interface::FlipToGDISurface: Proxy"); - return m_proxy->FlipToGDISurface(); - } - Logger::debug("*** DDraw7Interface::FlipToGDISurface: Ignoring"); DDrawCommonSurface* ps = m_commonIntf->GetPrimarySurface(); @@ -361,6 +380,18 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE DDraw7Interface::GetCaps(LPDDCAPS lpDDDriverCaps, LPDDCAPS lpDDHELCaps) { Logger::debug("<<< DDraw7Interface::GetCaps: Proxy"); + if (unlikely(lpDDDriverCaps == nullptr && lpDDHELCaps == nullptr)) + return DDERR_INVALIDPARAMS; + + // Interstate '76 sends invalid dwSizes part of the structs, + // and that explodes in Wine, so validate it before proxying + + if (unlikely(lpDDDriverCaps != nullptr && !IsValidDDrawCapsSize(lpDDDriverCaps->dwSize))) + return DDERR_INVALIDPARAMS; + + if (unlikely(lpDDHELCaps != nullptr && !IsValidDDrawCapsSize(lpDDHELCaps->dwSize))) + return DDERR_INVALIDPARAMS; + HRESULT hr = m_proxy->GetCaps(lpDDDriverCaps, lpDDHELCaps); if (unlikely(FAILED(hr))) return hr; @@ -563,8 +594,7 @@ namespace dxvk { Logger::warn("DDraw7Interface::SetDisplayMode: Failed to update primary surface desc"); } - if (likely(!m_commonIntf->GetOptions()->forceProxiedPresent && - m_commonIntf->GetOptions()->backBufferResize)) { + if (likely(m_commonIntf->GetOptions()->backBufferResize)) { const bool exclusiveMode = m_commonIntf->GetCooperativeLevel() & DDSCL_EXCLUSIVE; // Ignore any mode size dimensions when in windowed present mode @@ -582,11 +612,6 @@ namespace dxvk { } HRESULT STDMETHODCALLTYPE DDraw7Interface::WaitForVerticalBlank(DWORD dwFlags, HANDLE hEvent) { - if (unlikely(m_commonIntf->GetOptions()->forceProxiedPresent)) { - Logger::debug("<<< DDraw7Interface::WaitForVerticalBlank: Proxy"); - m_proxy->WaitForVerticalBlank(dwFlags, hEvent); - } - Logger::debug(">>> DDraw7Interface::WaitForVerticalBlank"); if (unlikely(dwFlags & DDWAITVB_BLOCKBEGINEVENT)) diff --git a/src/ddraw/ddraw7/ddraw7_interface.h b/src/ddraw/ddraw7/ddraw7_interface.h index ed659abbf95..f0a1b3e2d55 100644 --- a/src/ddraw/ddraw7/ddraw7_interface.h +++ b/src/ddraw/ddraw7/ddraw7_interface.h @@ -17,7 +17,7 @@ namespace dxvk { /** * \brief DirectDraw7 interface implementation */ - class DDraw7Interface final : public DDrawWrappedObject { + class DDraw7Interface final : public DDrawWrappedObject { public: DDraw7Interface( diff --git a/src/ddraw/ddraw7/ddraw7_surface.cpp b/src/ddraw/ddraw7/ddraw7_surface.cpp index 35f9069c8e5..8b9e91be525 100644 --- a/src/ddraw/ddraw7/ddraw7_surface.cpp +++ b/src/ddraw/ddraw7/ddraw7_surface.cpp @@ -21,7 +21,7 @@ namespace dxvk { DDraw7Interface* pParent, DDraw7Surface* pParentSurf, bool isChildObject) - : DDrawWrappedObject(pParent, std::move(surfProxy), nullptr) + : DDrawWrappedObject(pParent, std::move(surfProxy)) , m_isChildObject ( isChildObject ) , m_commonSurf ( commonSurf ) , m_parentSurf ( pParentSurf ) { @@ -51,13 +51,23 @@ namespace dxvk { } } + // Retrieve and cache the next surface in a flippable chain + if (unlikely(m_commonSurf->IsFlippable() && !m_commonSurf->IsBackBuffer())) { + IDirectDrawSurface7* nextFlippable = nullptr; + EnumAttachedSurfaces(&nextFlippable, ListBackBufferSurfaces7Callback); + m_nextFlippable = reinterpret_cast(nextFlippable); + if (likely(m_nextFlippable != nullptr)) + Logger::debug("DDraw7Surface: Retrieved the next swapchain surface"); + } + m_commonIntf->AddWrappedSurface(this); m_commonSurf->SetDD7Surface(this); - if (m_parentSurf != nullptr && !m_commonSurf->IsFrontBuffer() + if (m_parentSurf != nullptr && m_parentSurf->GetCommonSurface()->IsBackBufferOrFlippable() - && !m_commonIntf->GetOptions()->forceSingleBackBuffer) { + && !m_commonIntf->GetOptions()->forceSingleBackBuffer + && !m_commonIntf->GetOptions()->forceLegacyPresent) { const uint32_t index = m_parentSurf->GetCommonSurface()->GetBackBufferIndex(); m_commonSurf->IncrementBackBufferIndex(index); } @@ -120,6 +130,11 @@ namespace dxvk { Logger::debug("DDraw7Surface::QueryInterface: Query for IDirect3DTexture2"); return E_NOINTERFACE; } + // Shouldn't ever be called in practice + if (unlikely(riid == __uuidof(IDirect3DTexture))) { + Logger::debug("DDraw7Surface::QueryInterface: Query for IDirect3DTexture"); + return E_NOINTERFACE; + } // Wrap IDirectDrawGammaControl, to potentially ignore application set gamma ramps if (riid == __uuidof(IDirectDrawGammaControl)) { Logger::debug("DDraw7Surface::QueryInterface: Query for IDirectDrawGammaControl"); @@ -248,89 +263,30 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE DDraw7Surface::Blt(LPRECT lpDestRect, LPDIRECTDRAWSURFACE7 lpDDSrcSurface, LPRECT lpSrcRect, DWORD dwFlags, LPDDBLTFX lpDDBltFx) { Logger::debug("<<< DDraw7Surface::Blt: Proxy"); - // Write back any flippable surfaces or depth stencils from D3D9 + // Write back any dirty surface data from bound D3D9 back buffers or + // depth stencils, for both the source surface and the current surface if (likely(lpDDSrcSurface != nullptr && m_commonIntf->IsWrappedSurface(lpDDSrcSurface))) { - DDraw7Surface* sourceSurface = static_cast(lpDDSrcSurface); - if (unlikely(sourceSurface->GetCommonSurface()->IsGuardableSurface())) { - if (m_commonIntf->GetOptions()->backBufferWriteBack || m_commonIntf->GetOptions()->apitraceMode) { - Logger::debug("DDraw7Surface::Blt: Source surface is a swapchain surface"); - - if (unlikely(m_commonIntf->GetOptions()->apitraceMode && !sourceSurface->IsInitialized())) - sourceSurface->InitializeOrUploadD3D9(); - - if (likely(sourceSurface->IsInitialized())) - BlitToDDrawSurface(sourceSurface->GetProxied(), sourceSurface->GetD3D9()); - } else { - static bool s_swapchainWarningShown; - - if (!std::exchange(s_swapchainWarningShown, true)) - Logger::warn("DDraw7Surface::Blt: Source surface is a swapchain surface"); - } - } else if (unlikely(sourceSurface->GetCommonSurface()->IsDepthStencil())) { - if (m_commonIntf->GetOptions()->depthWriteBack || m_commonIntf->GetOptions()->apitraceMode) { - Logger::debug("DDraw7Surface::Blt: Source surface is a depth stencil"); - - if (likely(sourceSurface->IsInitialized())) - BlitToDDrawSurface(sourceSurface->GetProxied(), sourceSurface->GetD3D9()); - } else { - static bool s_depthStencilWarningShown; - - if (!std::exchange(s_depthStencilWarningShown, true)) - Logger::warn("DDraw7Surface::Blt: Source surface is a depth stencil"); - } - } + DDraw7Surface* surface7 = static_cast(lpDDSrcSurface); + surface7->DownloadSurfaceData(); } + DownloadSurfaceData(); - RefreshD3D9Device(); - if (likely(m_d3d9Device != nullptr)) { - // Forward DDBLT_DEPTHFILL clears to D3D9 if done on the current depth stencil - if (unlikely(lpDDSrcSurface == nullptr && - (dwFlags & DDBLT_DEPTHFILL) && - lpDDBltFx != nullptr && - m_commonIntf->GetCommonD3DDevice()->IsCurrentD3D9DepthStencil(m_d3d9.ptr()))) { - Logger::debug("DDraw7Surface::Blt: Clearing d3d9 depth stencil"); - - HRESULT hrClear; - const float zClear = m_commonSurf->GetNormalizedFloatDepth(lpDDBltFx->dwFillDepth); - - if (lpDestRect == nullptr) { - hrClear = m_d3d9Device->Clear(0, NULL, D3DCLEAR_ZBUFFER, 0, zClear, 0); - } else { - hrClear = m_d3d9Device->Clear(1, reinterpret_cast(lpDestRect), D3DCLEAR_ZBUFFER, 0, zClear, 0); - } - if (unlikely(FAILED(hrClear))) - Logger::warn("DDraw7Surface::Blt: Failed to clear d3d9 depth"); - } - // Forward DDBLT_COLORFILL clears to D3D9 if done on the current render target - if (unlikely(lpDDSrcSurface == nullptr && - (dwFlags & DDBLT_COLORFILL) && - lpDDBltFx != nullptr && - m_commonIntf->GetCommonD3DDevice()->IsCurrentD3D9RenderTarget(m_d3d9.ptr()))) { - Logger::debug("DDraw7Surface::Blt: Clearing d3d9 render target"); - - HRESULT hrClear; - if (lpDestRect == nullptr) { - hrClear = m_d3d9Device->Clear(0, NULL, D3DCLEAR_TARGET, lpDDBltFx->dwFillColor, 0.0f, 0); - } else { - hrClear = m_d3d9Device->Clear(1, reinterpret_cast(lpDestRect), D3DCLEAR_TARGET, lpDDBltFx->dwFillColor, 0.0f, 0); - } - if (unlikely(FAILED(hrClear))) - Logger::warn("DDraw7Surface::Blt: Failed to clear d3d9 render target"); - } - + d3d9::IDirect3DDevice9* d3d9Device = m_commonSurf->RefreshD3D9Device(); + if (likely(d3d9Device != nullptr)) { const bool exclusiveMode = (m_commonIntf->GetCooperativeLevel() & DDSCL_EXCLUSIVE) && !m_commonIntf->GetOptions()->ignoreExclusiveMode; - // Eclusive mode back buffer guard - if (exclusiveMode && m_commonIntf->HasDrawn() && - m_commonSurf->IsGuardableSurface() && - m_commonIntf->GetOptions()->backBufferGuard != D3DBackBufferGuard::Disabled) { - return DD_OK; // Windowed mode presentation path - } else if (!exclusiveMode && m_commonIntf->HasDrawn() && m_commonSurf->IsPrimarySurface()) { - m_commonIntf->ResetDrawTracking(); - m_d3d9Device->Present(NULL, NULL, NULL, NULL); - return DD_OK; + if (!exclusiveMode && lpDDSrcSurface != nullptr && m_commonSurf->IsPrimarySurface()) { + // TODO: Handle this properly, not by uploading the RT again + DDraw7Surface* ddraw7Surface = static_cast(lpDDSrcSurface); + DDraw7Surface* renderTarget = m_commonSurf->GetCommonD3DDevice()->GetCurrentRenderTarget7(); + + if (ddraw7Surface == renderTarget) { + renderTarget->InitializeOrUploadD3D9(); + d3d9Device->Present(NULL, NULL, NULL, NULL); + return DD_OK; + } } } @@ -341,20 +297,24 @@ namespace dxvk { Logger::warn("DDraw7Surface::Blt: Received an unwrapped source surface"); return DDERR_UNSUPPORTED; } - hr = m_proxy->Blt(lpDestRect, lpDDSrcSurface, lpSrcRect, dwFlags, lpDDBltFx); + hr = GetShadowOrProxied()->Blt(lpDestRect, lpDDSrcSurface, lpSrcRect, dwFlags, lpDDBltFx); } else { DDraw7Surface* ddraw7Surface = static_cast(lpDDSrcSurface); - hr = m_proxy->Blt(lpDestRect, ddraw7Surface->GetProxied(), lpSrcRect, dwFlags, lpDDBltFx); + hr = GetShadowOrProxied()->Blt(lpDestRect, ddraw7Surface->GetShadowOrProxied(), lpSrcRect, dwFlags, lpDDBltFx); } if (likely(SUCCEEDED(hr))) { - // Textures and cubemaps get uploaded during SetTexture calls - if (!m_commonSurf->IsTextureOrCubeMap()) { - HRESULT hrUpload = InitializeOrUploadD3D9(); - if (unlikely(FAILED(hrUpload))) - Logger::warn("DDraw7Surface::Blt: Failed upload to d3d9 surface"); - } else { - m_commonSurf->DirtyMipMaps(); + m_commonSurf->DirtyDDrawSurface(); + + if (unlikely(m_shadowSurf != nullptr && d3d9Device != nullptr)) { + const bool shouldPresent = m_commonIntf->GetOptions()->legacyPresentGuard == D3DLegacyPresentGuard::Auto ? + !m_commonSurf->GetCommonD3DDevice()->IsInScene() : + m_commonIntf->GetOptions()->legacyPresentGuard == D3DLegacyPresentGuard::Strict ? + false : true; + if (shouldPresent) { + InitializeOrUploadD3D9(); + d3d9Device->Present(NULL, NULL, NULL, NULL); + } } } @@ -370,54 +330,32 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE DDraw7Surface::BltFast(DWORD dwX, DWORD dwY, LPDIRECTDRAWSURFACE7 lpDDSrcSurface, LPRECT lpSrcRect, DWORD dwTrans) { Logger::debug("<<< DDraw7Surface::BltFast: Proxy"); - // Write back any flippable surfaces or depth stencils from D3D9 + // Write back any dirty surface data from bound D3D9 back buffers or + // depth stencils, for both the source surface and the current surface + // Write back any dirty surface data from bound D3D9 back buffers or + // depth stencils, for both the source surface and the current surface if (likely(lpDDSrcSurface != nullptr && m_commonIntf->IsWrappedSurface(lpDDSrcSurface))) { - DDraw7Surface* sourceSurface = static_cast(lpDDSrcSurface); - if (unlikely(sourceSurface->GetCommonSurface()->IsGuardableSurface())) { - if (m_commonIntf->GetOptions()->backBufferWriteBack || m_commonIntf->GetOptions()->apitraceMode) { - Logger::debug("DDraw7Surface::BltFast: Source surface is a swapchain surface"); - - if (unlikely(m_commonIntf->GetOptions()->apitraceMode && !sourceSurface->IsInitialized())) - sourceSurface->InitializeOrUploadD3D9(); - - if (likely(sourceSurface->IsInitialized())) - BlitToDDrawSurface(sourceSurface->GetProxied(), sourceSurface->GetD3D9()); - } else { - static bool s_swapchainWarningShown; - - if (!std::exchange(s_swapchainWarningShown, true)) - Logger::warn("DDraw7Surface::BltFast: Source surface is a swapchain surface"); - } - } else if (unlikely(sourceSurface->GetCommonSurface()->IsDepthStencil())) { - if (m_commonIntf->GetOptions()->depthWriteBack || m_commonIntf->GetOptions()->apitraceMode) { - Logger::debug("DDraw7Surface::BltFast: Source surface is a depth stencil"); - - if (likely(sourceSurface->IsInitialized())) - BlitToDDrawSurface(sourceSurface->GetProxied(), sourceSurface->GetD3D9()); - } else { - static bool s_depthStencilWarningShown; - - if (!std::exchange(s_depthStencilWarningShown, true)) - Logger::warn("DDraw7Surface::BltFast: Source surface is a depth stencil"); - } - } + DDraw7Surface* surface7 = static_cast(lpDDSrcSurface); + surface7->DownloadSurfaceData(); } + DownloadSurfaceData(); - RefreshD3D9Device(); - if (likely(m_d3d9Device != nullptr)) { + d3d9::IDirect3DDevice9* d3d9Device = m_commonSurf->RefreshD3D9Device(); + if (likely(d3d9Device != nullptr)) { const bool exclusiveMode = (m_commonIntf->GetCooperativeLevel() & DDSCL_EXCLUSIVE) && !m_commonIntf->GetOptions()->ignoreExclusiveMode; - // Eclusive mode back buffer guard - if (exclusiveMode && m_commonIntf->HasDrawn() && - m_commonSurf->IsGuardableSurface() && - m_commonIntf->GetOptions()->backBufferGuard != D3DBackBufferGuard::Disabled) { - return DD_OK; // Windowed mode presentation path - } else if (!exclusiveMode && m_commonIntf->HasDrawn() && m_commonSurf->IsPrimarySurface()) { - m_commonIntf->ResetDrawTracking(); - m_d3d9Device->Present(NULL, NULL, NULL, NULL); - return DD_OK; + if (!exclusiveMode && lpDDSrcSurface != nullptr && m_commonSurf->IsPrimarySurface()) { + // TODO: Handle this properly, not by uploading the RT again + DDraw7Surface* ddraw7Surface = static_cast(lpDDSrcSurface); + DDraw7Surface* renderTarget = m_commonSurf->GetCommonD3DDevice()->GetCurrentRenderTarget7(); + + if (ddraw7Surface == renderTarget) { + renderTarget->InitializeOrUploadD3D9(); + d3d9Device->Present(NULL, NULL, NULL, NULL); + return DD_OK; + } } } @@ -428,20 +366,24 @@ namespace dxvk { Logger::warn("DDraw7Surface::BltFast: Received an unwrapped source surface"); return DDERR_UNSUPPORTED; } - hr = m_proxy->BltFast(dwX, dwY, lpDDSrcSurface, lpSrcRect, dwTrans); + hr = GetShadowOrProxied()->BltFast(dwX, dwY, lpDDSrcSurface, lpSrcRect, dwTrans); } else { DDraw7Surface* ddraw7Surface = static_cast(lpDDSrcSurface); - hr = m_proxy->BltFast(dwX, dwY, ddraw7Surface->GetProxied(), lpSrcRect, dwTrans); + hr = GetShadowOrProxied()->BltFast(dwX, dwY, ddraw7Surface->GetShadowOrProxied(), lpSrcRect, dwTrans); } if (likely(SUCCEEDED(hr))) { - // Textures and cubemaps get uploaded during SetTexture calls - if (!m_commonSurf->IsTextureOrCubeMap()) { - HRESULT hrUpload = InitializeOrUploadD3D9(); - if (unlikely(FAILED(hrUpload))) - Logger::warn("DDraw7Surface::BltFast: Failed upload to d3d9 surface"); - } else { - m_commonSurf->DirtyMipMaps(); + m_commonSurf->DirtyDDrawSurface(); + + if (unlikely(m_shadowSurf != nullptr && d3d9Device != nullptr)) { + const bool shouldPresent = m_commonIntf->GetOptions()->legacyPresentGuard == D3DLegacyPresentGuard::Auto ? + !m_commonSurf->GetCommonD3DDevice()->IsInScene() : + m_commonIntf->GetOptions()->legacyPresentGuard == D3DLegacyPresentGuard::Strict ? + false : true; + if (shouldPresent) { + InitializeOrUploadD3D9(); + d3d9Device->Present(NULL, NULL, NULL, NULL); + } } } @@ -526,71 +468,44 @@ namespace dxvk { } HRESULT STDMETHODCALLTYPE DDraw7Surface::Flip(LPDIRECTDRAWSURFACE7 lpDDSurfaceTargetOverride, DWORD dwFlags) { - Logger::debug("*** DDraw7Surface::Flip: Presenting"); - - // Lost surfaces are not flippable - HRESULT hr = m_proxy->IsLost(); - if (unlikely(FAILED(hr))) { - Logger::debug("DDraw7Surface::Flip: Lost surface"); - return hr; - } - - if (unlikely(!(m_commonSurf->IsFrontBuffer() || m_commonSurf->IsBackBufferOrFlippable()))) { - Logger::debug("DDraw7Surface::Flip: Unflippable surface"); - return DDERR_NOTFLIPPABLE; - } - - const bool exclusiveMode = m_commonIntf->GetCooperativeLevel() & DDSCL_EXCLUSIVE; - - // Non-exclusive mode validations - if (unlikely(m_commonSurf->IsPrimarySurface() && !exclusiveMode)) { - Logger::debug("DDraw7Surface::Flip: Primary surface flip in non-exclusive mode"); - return DDERR_NOEXCLUSIVEMODE; - } - - // Exclusive mode validations - if (unlikely(m_commonSurf->IsBackBufferOrFlippable() && exclusiveMode)) { - Logger::debug("DDraw7Surface::Flip: Back buffer flip in exclusive mode"); - return DDERR_NOTFLIPPABLE; - } Com surf7; - if (m_commonIntf->IsWrappedSurface(lpDDSurfaceTargetOverride)) { + if (m_commonIntf->IsWrappedSurface(lpDDSurfaceTargetOverride)) surf7 = static_cast(lpDDSurfaceTargetOverride); - if (unlikely(!surf7->GetCommonSurface()->IsBackBufferOrFlippable())) { - Logger::debug("DDraw7Surface::Flip: Unflippable override surface"); - return DDERR_NOTFLIPPABLE; - } - } - - RefreshD3D9Device(); - if (likely(m_d3d9Device != nullptr)) { + d3d9::IDirect3DDevice9* d3d9Device = m_commonSurf->RefreshD3D9Device(); + if (likely(d3d9Device != nullptr)) { Logger::debug("*** DDraw7Surface::Flip: Presenting"); - m_commonIntf->ResetDrawTracking(); + // Lost surfaces are not flippable + HRESULT hr = m_proxy->IsLost(); + if (unlikely(FAILED(hr))) { + Logger::debug("DDraw7Surface::Flip: Lost surface"); + return hr; + } - if (unlikely(m_commonIntf->GetOptions()->forceProxiedPresent)) { - D3DCommonDevice* commonDevice = m_commonIntf->GetCommonD3DDevice(); + if (unlikely(!(m_commonSurf->IsFrontBuffer() || m_commonSurf->IsBackBufferOrFlippable()))) { + Logger::debug("DDraw7Surface::Flip: Unflippable surface"); + return DDERR_NOTFLIPPABLE; + } - if (unlikely(!IsInitialized())) - InitializeD3D9(commonDevice->IsCurrentRenderTarget(this)); + const bool exclusiveMode = m_commonIntf->GetCooperativeLevel() & DDSCL_EXCLUSIVE; - BlitToDDrawSurface(m_proxy.ptr(), m_d3d9.ptr()); + // Non-exclusive mode validations + if (unlikely(m_commonSurf->IsPrimarySurface() && !exclusiveMode)) { + Logger::debug("DDraw7Surface::Flip: Primary surface flip in non-exclusive mode"); + return DDERR_NOEXCLUSIVEMODE; + } - if (unlikely(!m_commonIntf->IsWrappedSurface(lpDDSurfaceTargetOverride))) { - if (unlikely(lpDDSurfaceTargetOverride != nullptr)) { - Logger::warn("DDraw7Surface::Flip: Received an unwrapped surface"); - return DDERR_UNSUPPORTED; - } - if (likely(commonDevice->IsCurrentRenderTarget(this))) - m_commonIntf->SetFlipRTSurfaceAndFlags(lpDDSurfaceTargetOverride, dwFlags); - return m_proxy->Flip(lpDDSurfaceTargetOverride, dwFlags); - } else { - if (likely(commonDevice->IsCurrentRenderTarget(this))) - m_commonIntf->SetFlipRTSurfaceAndFlags(lpDDSurfaceTargetOverride, dwFlags); - return m_proxy->Flip(surf7->GetProxied(), dwFlags); - } + // Exclusive mode validations + if (unlikely(m_commonSurf->IsBackBufferOrFlippable() && exclusiveMode)) { + Logger::debug("DDraw7Surface::Flip: Back buffer flip in exclusive mode"); + return DDERR_NOTFLIPPABLE; + } + + if (unlikely(surf7 != nullptr && !surf7->GetCommonSurface()->IsBackBufferOrFlippable())) { + Logger::debug("DDraw7Surface::Flip: Unflippable override surface"); + return DDERR_NOTFLIPPABLE; } // If the interface is waiting for VBlank and we get a no VSync flip, switch @@ -598,11 +513,11 @@ namespace dxvk { if (unlikely(m_commonIntf->GetWaitForVBlank() && (dwFlags & DDFLIP_NOVSYNC))) { Logger::info("DDraw7Surface::Flip: Switching to D3DPRESENT_INTERVAL_IMMEDIATE for presentation"); - D3DCommonDevice* commonDevice = m_commonIntf->GetCommonD3DDevice(); + D3DCommonDevice* commonD3DDevice = m_commonSurf->GetCommonD3DDevice(); - d3d9::D3DPRESENT_PARAMETERS resetParams = commonDevice->GetPresentParameters(); + d3d9::D3DPRESENT_PARAMETERS resetParams = commonD3DDevice->GetPresentParameters(); resetParams.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; - HRESULT hrReset = commonDevice->ResetD3D9Swapchain(&resetParams); + HRESULT hrReset = commonD3DDevice->ResetD3D9Swapchain(&resetParams); if (unlikely(FAILED(hrReset))) { Logger::warn("DDraw7Surface::Flip: Failed D3D9 swapchain reset"); } else { @@ -613,11 +528,11 @@ namespace dxvk { } else if (unlikely(!m_commonIntf->GetWaitForVBlank() && IsVSyncFlipFlag(dwFlags))) { Logger::info("DDraw7Surface::Flip: Switching to D3DPRESENT_INTERVAL_DEFAULT for presentation"); - D3DCommonDevice* commonDevice = m_commonIntf->GetCommonD3DDevice(); + D3DCommonDevice* commonD3DDevice = m_commonSurf->GetCommonD3DDevice(); - d3d9::D3DPRESENT_PARAMETERS resetParams = commonDevice->GetPresentParameters(); + d3d9::D3DPRESENT_PARAMETERS resetParams = commonD3DDevice->GetPresentParameters(); resetParams.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT; - HRESULT hrReset = commonDevice->ResetD3D9Swapchain(&resetParams); + HRESULT hrReset = commonD3DDevice->ResetD3D9Swapchain(&resetParams); if (unlikely(FAILED(hrReset))) { Logger::warn("DDraw7Surface::Flip: Failed D3D9 swapchain reset"); } else { @@ -625,9 +540,14 @@ namespace dxvk { } } - m_d3d9Device->Present(NULL, NULL, NULL, NULL); - // If we don't yet have a device, perhaps a D3D7 application is trying to - // present exclusively on DDraw (such as a main menu), so allow the flip + if (likely(m_nextFlippable != nullptr)) { + m_nextFlippable->InitializeOrUploadD3D9(); + } else { + InitializeOrUploadD3D9(); + } + + d3d9Device->Present(NULL, NULL, NULL, NULL); + } else { Logger::debug("<<< DDraw7Surface::Flip: Proxy"); @@ -710,11 +630,6 @@ namespace dxvk { // Blitting can be done at any time and completes within its call frame HRESULT STDMETHODCALLTYPE DDraw7Surface::GetBltStatus(DWORD dwFlags) { - if (unlikely(m_commonIntf->GetOptions()->forceProxiedPresent)) { - Logger::debug("<<< DDraw7Surface::GetBltStatus: Proxy"); - m_proxy->GetBltStatus(dwFlags); - } - Logger::debug(">>> DDraw7Surface::GetBltStatus"); if (likely(dwFlags == DDGBS_CANBLT || dwFlags == DDGBS_ISBLTDONE)) @@ -758,42 +673,16 @@ namespace dxvk { } HRESULT STDMETHODCALLTYPE DDraw7Surface::GetDC(HDC *lphDC) { - if (likely(!m_commonIntf->GetOptions()->forceProxiedPresent)) { - if (unlikely(lphDC == nullptr)) - return DDERR_INVALIDPARAMS; - - InitReturnPtr(lphDC); - - // Foward GetDC calls if we have drawn and the surface is flippable - RefreshD3D9Device(); - if (m_d3d9Device != nullptr && (m_commonIntf->HasDrawn() && - m_commonSurf->IsGuardableSurface())) { - Logger::debug(">>> DDraw7Surface::GetDC"); - - if (unlikely(!IsInitialized())) { - HRESULT hrUpload = InitializeOrUploadD3D9(); - if (unlikely(FAILED(hrUpload))) - Logger::warn("DDraw7Surface::GetDC: Failed to initialize d3d9 surface"); - } + Logger::debug("<<< DDraw7Surface::GetDC: Proxy"); - HRESULT hr9 = m_d3d9->GetDC(lphDC); - if (unlikely(FAILED(hr9))) - Logger::warn("DDraw7Surface::GetDC: Failed D3D9 call"); - return hr9; - } - } + // Write back any dirty surface data from bound D3D9 back buffers or depth stencils + DownloadSurfaceData(); - Logger::debug("<<< DDraw7Surface::GetDC: Proxy"); - return m_proxy->GetDC(lphDC); + return GetShadowOrProxied()->GetDC(lphDC); } // Flipping can be done at any time and completes within its call frame HRESULT STDMETHODCALLTYPE DDraw7Surface::GetFlipStatus(DWORD dwFlags) { - if (unlikely(m_commonIntf->GetOptions()->forceProxiedPresent)) { - Logger::debug("<<< DDraw7Surface::GetFlipStatus: Proxy"); - m_proxy->GetFlipStatus(dwFlags); - } - Logger::debug(">>> DDraw7Surface::GetFlipStatus"); if (likely(dwFlags == DDGFS_CANFLIP || dwFlags == DDGFS_ISFLIPDONE)) @@ -865,75 +754,30 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE DDraw7Surface::Lock(LPRECT lpDestRect, LPDDSURFACEDESC2 lpDDSurfaceDesc, DWORD dwFlags, HANDLE hEvent) { Logger::debug("<<< DDraw7Surface::Lock: Proxy"); - // Write back any flippable surfaces or depth stencils from D3D9 - if (unlikely(m_commonSurf->IsGuardableSurface())) { - if (m_commonIntf->GetOptions()->backBufferWriteBack || m_commonIntf->GetOptions()->apitraceMode) { - Logger::debug("DDraw7Surface::Lock: Surface is a swapchain surface"); - - if (unlikely(m_commonIntf->GetOptions()->apitraceMode && !IsInitialized())) - InitializeOrUploadD3D9(); - - if (likely(IsInitialized())) - BlitToDDrawSurface(m_proxy.ptr(), m_d3d9.ptr()); - } else { - static bool s_swapchainWarningShown; - - if (!std::exchange(s_swapchainWarningShown, true)) - Logger::warn("DDraw7Surface::Lock: Surface is a swapchain surface"); - } - } else if (unlikely(m_commonSurf->IsDepthStencil())) { - if (m_commonIntf->GetOptions()->depthWriteBack || m_commonIntf->GetOptions()->apitraceMode) { - Logger::debug("DDraw7Surface::Lock: Surface is a depth stencil"); - - if (likely(IsInitialized())) - BlitToDDrawSurface(m_proxy.ptr(), m_d3d9.ptr()); - } else { - static bool s_depthStencilWarningShown; - - if (!std::exchange(s_depthStencilWarningShown, true)) - Logger::warn("DDraw7Surface::Lock: Surface is a depth stencil"); - } - } + // Write back any dirty surface data from bound D3D9 back buffers or depth stencils + DownloadSurfaceData(); - return m_proxy->Lock(lpDestRect, lpDDSurfaceDesc, dwFlags, hEvent); + return GetShadowOrProxied()->Lock(lpDestRect, lpDDSurfaceDesc, dwFlags, hEvent); } HRESULT STDMETHODCALLTYPE DDraw7Surface::ReleaseDC(HDC hDC) { - if (likely(!m_commonIntf->GetOptions()->forceProxiedPresent)) { - // Foward ReleaseDC calls if we have drawn and the surface is flippable - RefreshD3D9Device(); - if (m_d3d9Device != nullptr && (m_commonIntf->HasDrawn() && - m_commonSurf->IsGuardableSurface())) { - Logger::debug(">>> DDraw7Surface::ReleaseDC"); - - if (unlikely(!IsInitialized())) { - HRESULT hrUpload = InitializeOrUploadD3D9(); - if (unlikely(FAILED(hrUpload))) - Logger::warn("DDraw7Surface::ReleaseDC: Failed to initialize d3d9 surface"); - } - - HRESULT hr9 = m_d3d9->ReleaseDC(hDC); - if (unlikely(FAILED(hr9))) - Logger::warn("DDraw7Surface::ReleaseDC: Failed D3D9 call"); - return hr9; - } - } - Logger::debug("<<< DDraw7Surface::ReleaseDC: Proxy"); - HRESULT hr = m_proxy->ReleaseDC(hDC); + HRESULT hr = GetShadowOrProxied()->ReleaseDC(hDC); if (likely(SUCCEEDED(hr))) { - // Textures and cubemaps get uploaded during SetTexture calls - if (m_commonSurf->IsTexture()) { - m_commonSurf->DirtyMipMaps(); - } else if (unlikely(m_commonIntf->GetOptions()->apitraceMode)) { - // We should ideally upload the surface contents here at all times, - // however some games are amazing, and do hundreds of locks on the same - // surface per frame, so this would absolutely tank performance - HRESULT hrUpload = InitializeOrUploadD3D9(); - if (unlikely(FAILED(hrUpload))) - Logger::warn("DDraw7Surface::ReleaseDC: Failed upload to d3d9 surface"); + m_commonSurf->DirtyDDrawSurface(); + + d3d9::IDirect3DDevice9* d3d9Device = m_commonSurf->RefreshD3D9Device(); + if (unlikely(m_shadowSurf != nullptr && d3d9Device != nullptr)) { + const bool shouldPresent = m_commonIntf->GetOptions()->legacyPresentGuard == D3DLegacyPresentGuard::Auto ? + !m_commonSurf->GetCommonD3DDevice()->IsInScene() : + m_commonIntf->GetOptions()->legacyPresentGuard == D3DLegacyPresentGuard::Strict ? + false : true; + if (shouldPresent) { + InitializeOrUploadD3D9(); + d3d9Device->Present(NULL, NULL, NULL, NULL); + } } } @@ -963,6 +807,15 @@ namespace dxvk { return hr; m_commonSurf->SetClipper(ddrawClipper); + + // Retrieve a hWnd, if needed, during clipper attachment + HWND hWnd = nullptr; + hr = ddrawClipper->GetProxied()->GetHWnd(&hWnd); + if (unlikely(FAILED(hr))) { + Logger::debug("DDraw7Surface::SetClipper: Failed to retrieve hWnd"); + } else { + m_commonIntf->SetHWND(hWnd); + } } return DD_OK; @@ -971,6 +824,16 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE DDraw7Surface::SetColorKey(DWORD dwFlags, LPDDCOLORKEY lpDDColorKey) { Logger::debug("<<< DDraw7Surface::SetColorKey: Proxy"); + // The Combat Mission series of games set a color key which is + // outside the color range of the surface they are setting it on... + // clamp it to the surface color depth in that case. This doesn't + // appear to work well universally, however, so only apply when needed. + if (unlikely(m_commonIntf->GetOptions()->colorKeyMasking && lpDDColorKey != nullptr)) { + const uint8_t colorBitCount = m_commonSurf->GetColorBitCount(); + lpDDColorKey->dwColorSpaceLowValue &= (1 << colorBitCount) - 1; + lpDDColorKey->dwColorSpaceHighValue &= (1 << colorBitCount) - 1; + } + HRESULT hr = m_proxy->SetColorKey(dwFlags, lpDDColorKey); if (unlikely(FAILED(hr))) return hr; @@ -979,6 +842,17 @@ namespace dxvk { if (unlikely(FAILED(hr))) Logger::err("DDraw7Surface::SetColorKey: Failed to retrieve updated surface desc"); + if (unlikely(m_shadowSurf != nullptr)) { + hr = m_shadowSurf->GetProxied()->SetColorKey(dwFlags, lpDDColorKey); + if (unlikely(FAILED(hr))) { + Logger::warn("DDraw7Surface::SetColorKey: Failed to set shadow surface color key"); + } else { + hr = m_shadowSurf->GetCommonSurface()->RefreshSurfaceDescripton(); + if (unlikely(FAILED(hr))) + Logger::warn("DDraw7Surface::SetColorKey: Failed to retrieve updated shadow surface desc"); + } + } + return DD_OK; } @@ -992,7 +866,7 @@ namespace dxvk { // A nullptr lpDDPalette gets the current palette detached if (lpDDPalette == nullptr) { - HRESULT hr = m_proxy->SetPalette(lpDDPalette); + HRESULT hr = GetShadowOrProxied()->SetPalette(lpDDPalette); if (unlikely(FAILED(hr))) return hr; @@ -1000,7 +874,7 @@ namespace dxvk { } else { DDrawPalette* ddrawPalette = static_cast(lpDDPalette); - HRESULT hr = m_proxy->SetPalette(ddrawPalette->GetProxied()); + HRESULT hr = GetShadowOrProxied()->SetPalette(ddrawPalette->GetProxied()); if (unlikely(FAILED(hr))) return hr; @@ -1013,16 +887,21 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE DDraw7Surface::Unlock(LPRECT lpSurfaceData) { Logger::debug("<<< DDraw7Surface::Unlock: Proxy"); - HRESULT hr = m_proxy->Unlock(lpSurfaceData); + HRESULT hr = GetShadowOrProxied()->Unlock(lpSurfaceData); if (likely(SUCCEEDED(hr))) { - // Textures and cubemaps get uploaded during SetTexture calls - if (!m_commonSurf->IsTextureOrCubeMap()) { - HRESULT hrUpload = InitializeOrUploadD3D9(); - if (unlikely(FAILED(hrUpload))) - Logger::warn("DDraw7Surface::Unlock: Failed upload to d3d9 surface"); - } else { - m_commonSurf->DirtyMipMaps(); + m_commonSurf->DirtyDDrawSurface(); + + d3d9::IDirect3DDevice9* d3d9Device = m_commonSurf->RefreshD3D9Device(); + if (unlikely(m_shadowSurf != nullptr && d3d9Device != nullptr)) { + const bool shouldPresent = m_commonIntf->GetOptions()->legacyPresentGuard == D3DLegacyPresentGuard::Auto ? + !m_commonSurf->GetCommonD3DDevice()->IsInScene() : + m_commonIntf->GetOptions()->legacyPresentGuard == D3DLegacyPresentGuard::Strict ? + false : true; + if (shouldPresent) { + InitializeOrUploadD3D9(); + d3d9Device->Present(NULL, NULL, NULL, NULL); + } } } @@ -1098,13 +977,7 @@ namespace dxvk { Logger::err("DDraw4Surface::SetSurfaceDesc: Failed to retrieve updated surface desc"); // We may need to recreate the d3d9 object based on the new desc - m_d3d9 = nullptr; - - if (!m_commonSurf->IsTextureOrCubeMap()) { - InitializeOrUploadD3D9(); - } else { - m_commonSurf->DirtyMipMaps(); - } + m_commonSurf->SetD3D9Surface(nullptr); return hr; } @@ -1147,8 +1020,8 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE DDraw7Surface::SetPriority(DWORD prio) { Logger::debug(">>> DDraw7Surface::SetPriority"); - RefreshD3D9Device(); - if (unlikely(!IsInitialized())) { + m_commonSurf->RefreshD3D9Device(); + if (unlikely(!m_commonSurf->IsInitialized())) { Logger::debug("DDraw7Surface::SetPriority: Not yet initialized"); return m_proxy->SetPriority(prio); } @@ -1160,7 +1033,7 @@ namespace dxvk { if (unlikely(FAILED(hr))) return hr; - m_d3d9->SetPriority(prio); + m_commonSurf->GetD3D9Surface()->SetPriority(prio); return DD_OK; } @@ -1168,8 +1041,8 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE DDraw7Surface::GetPriority(LPDWORD prio) { Logger::debug(">>> DDraw7Surface::GetPriority"); - RefreshD3D9Device(); - if (unlikely(!IsInitialized())) { + m_commonSurf->RefreshD3D9Device(); + if (unlikely(!m_commonSurf->IsInitialized())) { Logger::debug("DDraw7Surface::GetPriority: Not yet initialized"); return m_proxy->GetPriority(prio); } @@ -1180,7 +1053,7 @@ namespace dxvk { if (unlikely(!m_commonSurf->IsManaged())) return DDERR_INVALIDOBJECT; - *prio = m_d3d9->GetPriority(); + *prio = m_commonSurf->GetD3D9Surface()->GetPriority(); return DD_OK; } @@ -1188,8 +1061,8 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE DDraw7Surface::SetLOD(DWORD lod) { Logger::debug("<<< DDraw7Surface::SetLOD: Proxy"); - RefreshD3D9Device(); - if (unlikely(!IsInitialized())) { + m_commonSurf->RefreshD3D9Device(); + if (unlikely(!m_commonSurf->IsInitialized())) { Logger::debug("DDraw7Surface::SetLOD: Not yet initialized"); return m_proxy->SetLOD(lod); } @@ -1201,10 +1074,10 @@ namespace dxvk { if (unlikely(FAILED(hr))) return hr; - if (m_texture9 != nullptr) { - m_texture9->SetLOD(lod); - } else if (m_cubeMap9 != nullptr) { - m_cubeMap9->SetLOD(lod); + if (m_commonSurf->GetD3D9Texture() != nullptr) { + m_commonSurf->GetD3D9Texture()->SetLOD(lod); + } else if (m_commonSurf->GetD3D9CubeTexture() != nullptr) { + m_commonSurf->GetD3D9CubeTexture()->SetLOD(lod); } else { Logger::warn("DDraw7Surface::SetLOD: Failed to set D3D9 LOD"); return DDERR_INVALIDOBJECT; @@ -1216,8 +1089,8 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE DDraw7Surface::GetLOD(LPDWORD lod) { Logger::debug(">>> DDraw7Surface::GetLOD"); - RefreshD3D9Device(); - if (unlikely(!IsInitialized())) { + m_commonSurf->RefreshD3D9Device(); + if (unlikely(!m_commonSurf->IsInitialized())) { Logger::debug("DDraw7Surface::GetLOD: Not yet initialized"); return m_proxy->GetLOD(lod); } @@ -1228,10 +1101,10 @@ namespace dxvk { if (unlikely(!m_commonSurf->IsManaged())) return DDERR_INVALIDOBJECT; - if (likely(m_texture9 != nullptr)) { - *lod = m_texture9->GetLOD(); - } else if (m_cubeMap9 != nullptr) { - *lod = m_cubeMap9->GetLOD(); + if (likely(m_commonSurf->GetD3D9Texture() != nullptr)) { + *lod = m_commonSurf->GetD3D9Texture()->GetLOD(); + } else if (m_commonSurf->GetD3D9CubeTexture() != nullptr) { + *lod = m_commonSurf->GetD3D9CubeTexture()->GetLOD(); } else { Logger::warn("DDraw7Surface::GetLOD: Failed to get D3D9 LOD"); return DDERR_INVALIDOBJECT; @@ -1240,530 +1113,294 @@ namespace dxvk { return DD_OK; } - HRESULT DDraw7Surface::InitializeD3D9RenderTarget() { - HRESULT hr = DD_OK; + IDirectDrawSurface7* DDraw7Surface::GetShadowOrProxied() { + d3d9::IDirect3DDevice9* d3d9Device = m_commonSurf->RefreshD3D9Device(); - RefreshD3D9Device(); + if (unlikely(m_shadowSurf != nullptr && d3d9Device != nullptr)) + return m_shadowSurf->GetProxied(); - if (unlikely(!IsInitialized())) - hr = InitializeD3D9(true); - - return hr; + return m_proxy.ptr(); } - HRESULT DDraw7Surface::InitializeD3D9DepthStencil() { - HRESULT hr = DD_OK; + HRESULT DDraw7Surface::InitializeD3D9RenderTarget() { + m_commonSurf->RefreshD3D9Device(); - RefreshD3D9Device(); + m_commonSurf->MarkAsD3D9BackBuffer(); - if (unlikely(!IsInitialized())) - hr = InitializeD3D9(false); + if (unlikely(!m_commonSurf->IsInitialized())) { + if (m_commonSurf->IsTextureOrCubeMap()) + UpdateMipMapCount(); - return hr; - } + HRESULT hr = m_commonSurf->InitializeD3D9(true); + if (unlikely(FAILED(hr))) + return hr; - HRESULT DDraw7Surface::InitializeOrUploadD3D9() { - HRESULT hr = DD_OK; + if (unlikely(m_commonSurf->IsCubeMap())) + InitializeAllCubeMapSurfaces(); - RefreshD3D9Device(); + Logger::debug(str::format("DDraw7Surface::InitializeD3D9RenderTarget: Initialized surface nr. [[7-", m_surfCount, "]]")); - if (likely(IsInitialized())) { - hr = UploadSurfaceData(); - } else { - hr = InitializeD3D9(false); + return UploadSurfaceData(); } - return hr; - } - - inline void DDraw7Surface::InitializeAndAttachCubeFace( - IDirectDrawSurface7* surf, - d3d9::IDirect3DCubeTexture9* cubeTex9, - d3d9::D3DCUBEMAP_FACES face) { - Com face7; - if (likely(!m_commonIntf->IsWrappedSurface(surf))) { - Com wrappedFace = surf; - try { - face7 = new DDraw7Surface(nullptr, std::move(wrappedFace), m_parent, this, false); - } catch (const DxvkError& e) { - Logger::err("InitializeAndAttachCubeFace: Failed to create wrapped cube face surface"); - Logger::err(e.message()); - } - } else { - face7 = static_cast(surf); - } - - if (likely(face7 != nullptr)) { - Com face9; - cubeTex9->GetCubeMapSurface(face, 0, &face9); - face7->SetD3D9(std::move(face9)); - m_attachedSurfaces.emplace(std::piecewise_construct, - std::forward_as_tuple(face7->GetProxied()), - std::forward_as_tuple(face7.ref())); - } + return D3D_OK; } - inline HRESULT DDraw7Surface::InitializeD3D9(const bool initRT) { - if (unlikely(m_d3d9Device == nullptr)) { - Logger::debug("DDraw7Surface::InitializeD3D9: Null device, can't initialize right now"); - return DD_OK; - } - - Logger::debug(str::format("DDraw7Surface::InitializeD3D9: Initializing nr. [[7-", m_surfCount, "]]")); - - const DDSURFACEDESC2* desc2 = m_commonSurf->GetDesc2(); - const d3d9::D3DFORMAT format = m_commonSurf->GetD3D9Format(); - - if (unlikely(desc2->dwHeight == 0 || desc2->dwWidth == 0)) { - Logger::err("DDraw7Surface::InitializeD3D9: Surface has 0 height or width"); - return DDERR_GENERIC; - } - - if (unlikely(format == d3d9::D3DFMT_UNKNOWN)) { - Logger::err("DDraw7Surface::InitializeD3D9: Surface has an unknown format"); - return DDERR_GENERIC; - } - - // Don't initialize P8 textures/surfaces since we don't support them. - // Some applications do require them to be created by ddraw, otherwise - // they will simply fail to start, so just ignore them for now. - if (unlikely(format == d3d9::D3DFMT_P8)) { - static bool s_formatP8ErrorShown; - - if (!std::exchange(s_formatP8ErrorShown, true)) - Logger::warn("DDraw7Surface::InitializeD3D9: Unsupported format D3DFMT_P8"); + HRESULT DDraw7Surface::InitializeD3D9DepthStencil() { + m_commonSurf->RefreshD3D9Device(); - return DD_OK; + m_commonSurf->MarkAsD3D9DepthStencil(); - // Similarly, D3DFMT_R3G3B2 isn't supported by D3D9 dxvk, however some - // applications require it to be supported by ddraw, even if they do not - // use it. Simply ignore any D3DFMT_R3G3B2 textures/surfaces for now. - } else if (unlikely(format == d3d9::D3DFMT_R3G3B2)) { - static bool s_formatR3G3B2ErrorShown; + if (unlikely(!m_commonSurf->IsInitialized())) { + HRESULT hr = m_commonSurf->InitializeD3D9(false); + if (unlikely(FAILED(hr))) + return hr; - if (!std::exchange(s_formatR3G3B2ErrorShown, true)) - Logger::warn("DDraw7Surface::InitializeD3D9: Unsupported format D3DFMT_R3G3B2"); + Logger::debug(str::format("DDraw7Surface::InitializeD3D9DepthStencil: Initialized surface nr. [[7-", m_surfCount, "]]")); - return DD_OK; + return UploadSurfaceData(); } - // We need to count the number of actual mips on initialization by going through - // the mip chain, since the dwMipMapCount number may or may not be accurate. I am - // guessing it was intended more as a hint, not neceesarily a set number. - if (m_commonSurf->IsTextureOrCubeMap()) { - IDirectDrawSurface7* mipMap = m_proxy.ptr(); - DDSURFACEDESC2 mipDesc2; - uint16_t mipCount = 1; - - while (mipMap != nullptr) { - IDirectDrawSurface7* parentSurface = mipMap; - mipMap = nullptr; - parentSurface->EnumAttachedSurfaces(&mipMap, ListMipChainSurfaces7Callback); - if (mipMap != nullptr) { - mipCount++; - - mipDesc2 = { }; - mipDesc2.dwSize = sizeof(DDSURFACEDESC2); - mipMap->GetSurfaceDesc(&mipDesc2); - // Ignore multiple 1x1 mips, which apparently can get generated if the - // application gets the dwMipMapCount wrong vs surface dimensions. - if (unlikely(mipDesc2.dwWidth == 1 && mipDesc2.dwHeight == 1)) - break; - } - } - - // Do not worry about maximum supported mip map levels validation, - // because D3D9 will handle this for us and cap them appropriately - if (mipCount > 1) { - Logger::debug(str::format("DDraw7Surface::InitializeD3D9: Found ", mipCount, " mip levels")); - - if (unlikely(mipCount != desc2->dwMipMapCount)) - Logger::debug(str::format("DDraw7Surface::InitializeD3D9: Mismatch with declared ", desc2->dwMipMapCount, " mip levels")); - } - - if (unlikely(m_commonIntf->GetOptions()->autoGenMipMaps)) { - Logger::debug("DDraw7Surface::InitializeD3D9: Using auto mip map generation"); - mipCount = 0; - } - - m_commonSurf->SetMipCount(mipCount); - } + return DD_OK; + } - d3d9::D3DPOOL pool = d3d9::D3DPOOL_DEFAULT; - DWORD usage = 0; - - // General surface/texture pool placement - if (desc2->ddsCaps.dwCaps & DDSCAPS_LOCALVIDMEM) - pool = d3d9::D3DPOOL_DEFAULT; - // There's no explicit non-local video memory placement - // per se, but D3DPOOL_MANAGED is close enough - else if ((desc2->ddsCaps.dwCaps & DDSCAPS_NONLOCALVIDMEM) || (desc2->ddsCaps.dwCaps2 & DDSCAPS2_TEXTUREMANAGE)) - pool = d3d9::D3DPOOL_MANAGED; - else if (desc2->ddsCaps.dwCaps & DDSCAPS_SYSTEMMEMORY) - // We can't know beforehand if a texture is or isn't going to be - // used in SetTexture() calls, and textures placed in D3DPOOL_SYSTEMMEM - // will not work in that context in dxvk, so revert to D3DPOOL_MANAGED. - pool = m_commonSurf->IsTextureOrCubeMap() ? d3d9::D3DPOOL_MANAGED : d3d9::D3DPOOL_SYSTEMMEM; - - // Place all possible render targets in DEFAULT - // - // Note: This is somewhat problematic for textures and cube maps - // which will have D3DUSAGE_RENDERTARGET, but also need to have - // D3DUSAGE_DYNAMIC for locking/uploads to work. The flag combination - // isn't supported in D3D9, but we have a D3D7 exception in place. - // - if (m_commonSurf->IsRenderTarget() || initRT) { - Logger::debug("DDraw7Surface::InitializeD3D9: Usage: D3DUSAGE_RENDERTARGET"); - pool = d3d9::D3DPOOL_DEFAULT; - usage |= D3DUSAGE_RENDERTARGET; - } - // All depth stencils will be created in DEFAULT - if (m_commonSurf->IsDepthStencil()) { - Logger::debug("DDraw7Surface::InitializeD3D9: Usage: D3DUSAGE_DEPTHSTENCIL"); - pool = d3d9::D3DPOOL_DEFAULT; - usage |= D3DUSAGE_DEPTHSTENCIL; - } + HRESULT DDraw7Surface::InitializeOrUploadD3D9() { + d3d9::IDirect3DDevice9* d3d9Device = m_commonSurf->RefreshD3D9Device(); - // General usage flags - if (m_commonSurf->IsTextureOrCubeMap()) { - if (pool == d3d9::D3DPOOL_DEFAULT) { - Logger::debug("DDraw7Surface::InitializeD3D9: Usage: D3DUSAGE_DYNAMIC"); - usage |= D3DUSAGE_DYNAMIC; - } - if (unlikely(m_commonIntf->GetOptions()->autoGenMipMaps)) { - Logger::debug("DDraw7Surface::InitializeD3D9: Usage: D3DUSAGE_AUTOGENMIPMAP"); - usage |= D3DUSAGE_AUTOGENMIPMAP; - } + // Fast skip + if (unlikely(d3d9Device == nullptr)) { + Logger::debug("DDraw7Surface::InitializeOrUploadD3D9: Null device, can't initialize right now"); + return D3D_OK; } - const char* poolPlacement = pool == d3d9::D3DPOOL_DEFAULT ? "D3DPOOL_DEFAULT" : - pool == d3d9::D3DPOOL_SYSTEMMEM ? "D3DPOOL_SYSTEMMEM" : "D3DPOOL_MANAGED"; - - Logger::debug(str::format("DDraw7Surface::InitializeD3D9: Placing in: ", poolPlacement)); - - // Use the MSAA type that was determined to be supported during device creation - const d3d9::D3DMULTISAMPLE_TYPE multiSampleType = m_commonIntf->GetCommonD3DDevice()->GetMultiSampleType(); - const uint32_t index = m_commonSurf->GetBackBufferIndex(); - - Com surf; - - HRESULT hr = DDERR_GENERIC; - - // Front Buffer - if (m_commonSurf->IsFrontBuffer()) { - Logger::debug("DDraw7Surface::InitializeD3D9: Initializing front buffer..."); - - m_d3d9Device->GetBackBuffer(0, index, d3d9::D3DBACKBUFFER_TYPE_MONO, &surf); - - if (unlikely(surf == nullptr)) { - Logger::err("DDraw7Surface::InitializeD3D9: Failed to retrieve front buffer"); - m_d3d9 = nullptr; - return hr; - } - - m_d3d9 = std::move(surf); - - // Back Buffer - } else if (m_commonSurf->IsBackBufferOrFlippable()) { - Logger::debug("DDraw7Surface::InitializeD3D9: Initializing back buffer..."); - - m_d3d9Device->GetBackBuffer(0, index, d3d9::D3DBACKBUFFER_TYPE_MONO, &surf); - - if (unlikely(surf == nullptr)) { - Logger::err("DDraw7Surface::InitializeD3D9: Failed to retrieve back buffer"); - m_d3d9 = nullptr; - return hr; - } - - m_d3d9 = std::move(surf); - - // Cube maps - } else if (m_commonSurf->IsCubeMap()) { - Logger::debug("DDraw7Surface::InitializeD3D9: Initializing cube map..."); - - Com cubetex; - - hr = m_d3d9Device->CreateCubeTexture( - desc2->dwWidth, m_commonSurf->GetMipCount(), usage, - format, pool, &cubetex, nullptr); - - if (unlikely(FAILED(hr))) { - Logger::err("DDraw7Surface::InitializeD3D9: Failed to create cube map"); - m_cubeMap9 = nullptr; - return hr; - } - - if (unlikely(m_commonIntf->GetOptions()->autoGenMipMaps)) - cubetex->SetAutoGenFilterType(d3d9::D3DTEXF_ANISOTROPIC); - - // Always attach the positive X face to this surface - cubetex->GetCubeMapSurface(d3d9::D3DCUBEMAP_FACE_POSITIVE_X, 0, &surf); - m_d3d9 = (std::move(surf)); - - Logger::debug("DDraw7Surface::InitializeD3D9: Retrieving cube map faces"); - - CubeMapAttachedSurfaces cubeMapAttachedSurfaces; - m_proxy->EnumAttachedSurfaces(&cubeMapAttachedSurfaces, EnumAndAttachCubeMapFacesCallback); - - // The positive X surfaces is this surface, so nothing should be retrieved - if (unlikely(cubeMapAttachedSurfaces.positiveX != nullptr)) - Logger::warn("DDraw7Surface::InitializeD3D9: Non-null positive X cube map face"); - - m_cubeMapSurfaces[0] = m_proxy.ptr(); - - // We can't know in advance which faces have been generated, - // so check them one by one, initialize and bind as needed - if (cubeMapAttachedSurfaces.negativeX != nullptr) { - Logger::debug("DDraw7Surface::InitializeD3D9: Detected negative X cube map face"); - m_cubeMapSurfaces[1] = cubeMapAttachedSurfaces.negativeX; - InitializeAndAttachCubeFace(cubeMapAttachedSurfaces.negativeX, cubetex.ptr(), - d3d9::D3DCUBEMAP_FACE_NEGATIVE_X); - } - if (cubeMapAttachedSurfaces.positiveY != nullptr) { - Logger::debug("DDraw7Surface::InitializeD3D9: Detected positive Y cube map face"); - m_cubeMapSurfaces[2] = cubeMapAttachedSurfaces.positiveY; - InitializeAndAttachCubeFace(cubeMapAttachedSurfaces.positiveY, cubetex.ptr(), - d3d9::D3DCUBEMAP_FACE_POSITIVE_Y); - } - if (cubeMapAttachedSurfaces.negativeY != nullptr) { - Logger::debug("DDraw7Surface::InitializeD3D9: Detected negative Y cube map face"); - m_cubeMapSurfaces[3] = cubeMapAttachedSurfaces.negativeY; - InitializeAndAttachCubeFace(cubeMapAttachedSurfaces.negativeY, cubetex.ptr(), - d3d9::D3DCUBEMAP_FACE_NEGATIVE_Y); - } - if (cubeMapAttachedSurfaces.positiveZ != nullptr) { - Logger::debug("DDraw7Surface::InitializeD3D9: Detected positive Z cube map face"); - m_cubeMapSurfaces[4] = cubeMapAttachedSurfaces.positiveZ; - InitializeAndAttachCubeFace(cubeMapAttachedSurfaces.positiveZ, cubetex.ptr(), - d3d9::D3DCUBEMAP_FACE_POSITIVE_Z); - } - if (cubeMapAttachedSurfaces.negativeZ != nullptr) { - Logger::debug("DDraw7Surface::InitializeD3D9: Detected negative Z cube map face"); - m_cubeMapSurfaces[5] = cubeMapAttachedSurfaces.negativeZ; - InitializeAndAttachCubeFace(cubeMapAttachedSurfaces.negativeZ, cubetex.ptr(), - d3d9::D3DCUBEMAP_FACE_NEGATIVE_Z); - } - - Logger::debug("DDraw7Surface::InitializeD3D9: Created cube map"); - m_cubeMap9 = std::move(cubetex); - - // Textures - } else if (m_commonSurf->IsTexture()) { - Logger::debug("DDraw7Surface::InitializeD3D9: Initializing texture..."); - - Com tex; + if (unlikely(!m_commonSurf->IsInitialized())) { + if (m_commonSurf->IsTextureOrCubeMap()) + UpdateMipMapCount(); - hr = m_d3d9Device->CreateTexture( - desc2->dwWidth, desc2->dwHeight, m_commonSurf->GetMipCount(), usage, - format, pool, &tex, nullptr); + const bool initRenderTarget = m_commonSurf->GetCommonD3DDevice()->IsCurrentRenderTarget(m_commonSurf.ptr()); - if (unlikely(FAILED(hr))) { - Logger::err("DDraw7Surface::InitializeD3D9: Failed to create texture"); - m_texture9 = nullptr; + HRESULT hr = m_commonSurf->InitializeD3D9(initRenderTarget); + if (unlikely(FAILED(hr))) return hr; - } - if (unlikely(m_commonIntf->GetOptions()->autoGenMipMaps)) - tex->SetAutoGenFilterType(d3d9::D3DTEXF_ANISOTROPIC); + if (unlikely(m_commonSurf->IsCubeMap())) + InitializeAllCubeMapSurfaces(); - // Attach level 0 to this surface - tex->GetSurfaceLevel(0, &surf); - m_d3d9 = (std::move(surf)); - - Logger::debug("DDraw7Surface::InitializeD3D9: Created texture"); - m_texture9 = std::move(tex); + Logger::debug(str::format("DDraw7Surface::InitializeOrUploadD3D9: Initialized surface nr. [[7-", m_surfCount, "]]")); + } - // Depth Stencil - } else if (m_commonSurf->IsDepthStencil()) { - Logger::debug("DDraw7Surface::InitializeD3D9: Initializing depth stencil..."); + if (likely(m_commonSurf->IsInitialized())) + return UploadSurfaceData(); - hr = m_d3d9Device->CreateDepthStencilSurface( - desc2->dwWidth, desc2->dwHeight, format, - multiSampleType, 0, FALSE, &surf, nullptr); + return DD_OK; + } - if (unlikely(FAILED(hr))) { - Logger::err("DDraw7Surface::InitializeD3D9: Failed to create DS"); - m_d3d9 = nullptr; - return hr; + void DDraw7Surface::DownloadSurfaceData() { + // Some games, like The Settlers IV, use multiple devices for rendering, one to handle + // terrain and the overall 3D scene, and one to create textures/sprites to overlay on + // top of it. Since DXVK's D3D9 backend does not restrict cross-device surface/texture + // use, simply skip changing assigned surface devices during downloads. This is essentially + // a hack, which by some miracle works well enough in some cases, though may explode in others. + if (likely(!m_commonIntf->GetOptions()->deviceResourceSharing)) + m_commonSurf->RefreshD3D9Device(); + + if (unlikely(m_commonSurf->IsD3D9BackBuffer())) { + Logger::debug("DDraw7Surface::DownloadSurfaceData: Surface is a bound swapchain surface"); + + if (m_commonSurf->IsInitialized() && m_commonSurf->IsD3D9SurfaceDirty()) { + Logger::debug(str::format("DDraw7Surface::DownloadSurfaceData: Downloading nr. [[7-", m_surfCount, "]]")); + BlitToDDrawSurface(GetShadowOrProxied(), m_commonSurf->GetD3D9Surface(), + m_commonSurf->IsDXTFormat()); + m_commonSurf->UnDirtyD3D9Surface(); } - - Logger::debug("DDraw7Surface::InitializeD3D9: Created depth stencil surface"); - - m_d3d9 = std::move(surf); - - // Offscreen Plain Surfaces - } else if (m_commonSurf->IsOffScreenPlainSurface()) { - Logger::debug("DDraw7Surface::InitializeD3D9: Initializing offscreen plain surface..."); - - // Sometimes we get passed offscreen plain surfaces which should be tied to the back buffer, - // either as existing RTs or during SetRenderTarget() calls, which are tracked with initRT - if (unlikely(m_commonIntf->GetCommonD3DDevice()->IsCurrentRenderTarget(this) || initRT)) { - Logger::debug("DDraw7Surface::InitializeD3D9: Offscreen plain surface is the RT"); - - m_d3d9Device->GetBackBuffer(0, index, d3d9::D3DBACKBUFFER_TYPE_MONO, &surf); - - if (unlikely(surf == nullptr)) { - Logger::err("DDraw7Surface::InitializeD3D9: Failed to retrieve offscreen plain surface"); - m_d3d9 = nullptr; - return hr; - } - } else { - hr = m_d3d9Device->CreateOffscreenPlainSurface( - desc2->dwWidth, desc2->dwHeight, format, - pool, &surf, nullptr); - - if (unlikely(FAILED(hr))) { - Logger::err("DDraw7Surface::InitializeD3D9: Failed to create offscreen plain surface"); - m_d3d9 = nullptr; - return hr; - } + } else if (unlikely(m_commonSurf->IsD3D9DepthStencil())) { + Logger::debug("DDraw7Surface::DownloadSurfaceData: Surface is a bound depth stencil"); + + if (m_commonSurf->IsInitialized() && m_commonSurf->IsD3D9SurfaceDirty()) { + Logger::debug(str::format("DDraw7Surface::DownloadSurfaceData: Downloading nr. [[7-", m_surfCount, "]]")); + BlitToDDrawSurface(m_proxy.ptr(), m_commonSurf->GetD3D9Surface(), + m_commonSurf->IsDXTFormat()); + m_commonSurf->UnDirtyD3D9Surface(); } + } + } - m_d3d9 = std::move(surf); - - // Overlays (haven't seen any actual use of overlays in the wild) - } else if (m_commonSurf->IsOverlay()) { - Logger::debug("DDraw7Surface::InitializeD3D9: Initializing overlay..."); - - // Always link overlays to the back buffer - m_d3d9Device->GetBackBuffer(0, index, d3d9::D3DBACKBUFFER_TYPE_MONO, &surf); + inline void DDraw7Surface::UpdateMipMapCount() { + // We need to count the number of actual mips on initialization by going through + // the mip chain, since the dwMipMapCount number may or may not be accurate. I am + // guessing it was intended more as a hint, not neceesarily a set number. + const DDSURFACEDESC2* desc2 = m_commonSurf->GetDesc2(); - if (unlikely(surf == nullptr)) { - Logger::err("DDraw7Surface::InitializeD3D9: Failed to retrieve overlay surface"); - m_d3d9 = nullptr; - return hr; + IDirectDrawSurface7* mipMap = m_proxy.ptr(); + DDSURFACEDESC2 mipDesc2; + uint16_t mipCount = 1; + + while (mipMap != nullptr) { + IDirectDrawSurface7* parentSurface = mipMap; + mipMap = nullptr; + parentSurface->EnumAttachedSurfaces(&mipMap, ListMipChainSurfaces7Callback); + if (mipMap != nullptr) { + mipCount++; + + mipDesc2 = { }; + mipDesc2.dwSize = sizeof(DDSURFACEDESC2); + mipMap->GetSurfaceDesc(&mipDesc2); + // Ignore multiple 1x1 mips, which apparently can get generated if the + // application gets the dwMipMapCount wrong vs surface dimensions. + if (unlikely(mipDesc2.dwWidth == 1 && mipDesc2.dwHeight == 1)) + break; } + } - m_d3d9 = std::move(surf); - - // Generic render target - } else if (m_commonSurf->IsRenderTarget()) { - Logger::debug("DDraw7Surface::InitializeD3D9: Initializing render target..."); - - // Must be lockable for blitting to work. Note that - // D3D9 does not allow the creation of lockable RTs when - // using MSAA, but we have a D3D7 exception in place. - hr = m_d3d9Device->CreateRenderTarget( - desc2->dwWidth, desc2->dwHeight, format, - multiSampleType, usage, TRUE, &surf, nullptr); - - if (unlikely(FAILED(hr))) { - Logger::err("DDraw7Surface::InitializeD3D9: Failed to create render target"); - m_d3d9 = nullptr; - return hr; - } + // Do not worry about maximum supported mip map levels validation, + // because D3D9 will handle this for us and cap them appropriately + if (mipCount > 1) { + Logger::debug(str::format("DDraw7Surface::UpdateMipMapCount: Found ", mipCount, " mip levels")); - m_d3d9 = std::move(surf); + if (unlikely(mipCount != desc2->dwMipMapCount)) + Logger::debug(str::format("DDraw7Surface::UpdateMipMapCount: Mismatch with declared ", desc2->dwMipMapCount, " mip levels")); + } - // We sometimes get generic surfaces, with only dimensions, format and placement info - } else if (!m_commonSurf->IsNotKnown()) { - Logger::debug("DDraw7Surface::InitializeD3D9: Initializing generic surface..."); + if (unlikely(m_commonIntf->GetOptions()->autoGenMipMaps)) { + Logger::debug("DDraw7Surface::UpdateMipMapCount: Using auto mip map generation"); + mipCount = 0; + } - hr = m_d3d9Device->CreateOffscreenPlainSurface( - desc2->dwWidth, desc2->dwHeight, format, - pool, &surf, nullptr); + m_commonSurf->SetMipCount(mipCount); + } - if (unlikely(FAILED(hr))) { - Logger::err("DDraw7Surface::InitializeD3D9: Failed to create offscreen plain surface"); - m_d3d9 = nullptr; - return hr; + inline void DDraw7Surface::InitializeAndAttachCubeFace( + IDirectDrawSurface7* surf, + d3d9::IDirect3DCubeTexture9* cubeTex9, + d3d9::D3DCUBEMAP_FACES face) { + Com face7; + if (likely(!m_commonIntf->IsWrappedSurface(surf))) { + Com wrappedFace = surf; + try { + face7 = new DDraw7Surface(nullptr, std::move(wrappedFace), m_parent, this, false); + } catch (const DxvkError& e) { + Logger::err("InitializeAndAttachCubeFace: Failed to create wrapped cube face surface"); + Logger::err(e.message()); } - - Logger::debug("DDraw7Surface::InitializeD3D9: Created offscreen plain surface"); - - m_d3d9 = std::move(surf); } else { - Logger::warn("DDraw7Surface::InitializeD3D9: Skipping initialization of unknown surface"); + face7 = static_cast(surf); } - // Depth stencils will not need uploads post initialization - if (likely(!m_commonSurf->IsDepthStencil())) - UploadSurfaceData(); + if (likely(face7 != nullptr)) { + Com face9; + cubeTex9->GetCubeMapSurface(face, 0, &face9); + face7->GetCommonSurface()->SetD3D9Surface(std::move(face9)); + m_attachedSurfaces.emplace(std::piecewise_construct, + std::forward_as_tuple(face7->GetProxied()), + std::forward_as_tuple(face7.ref())); + } + } - return DD_OK; + inline void DDraw7Surface::InitializeAllCubeMapSurfaces() { + Logger::debug("DDraw7Surface::InitializeAllCubeMapSurfaces: Retrieving cube map faces"); + + CubeMapAttachedSurfaces cubeMapAttachedSurfaces; + m_proxy->EnumAttachedSurfaces(&cubeMapAttachedSurfaces, EnumAndAttachCubeMapFacesCallback); + + // The positive X surfaces is this surface, so nothing should be retrieved + if (unlikely(cubeMapAttachedSurfaces.positiveX != nullptr)) + Logger::warn("DDraw7Surface::InitializeAllCubeMapSurfaces: Non-null positive X cube map face"); + + m_cubeMapSurfaces[0] = m_proxy.ptr(); + + // We can't know in advance which faces have been generated, + // so check them one by one, initialize and bind as needed + if (cubeMapAttachedSurfaces.negativeX != nullptr) { + Logger::debug("DDraw7Surface::InitializeAllCubeMapSurfaces: Detected negative X cube map face"); + m_cubeMapSurfaces[1] = cubeMapAttachedSurfaces.negativeX; + InitializeAndAttachCubeFace(cubeMapAttachedSurfaces.negativeX, m_commonSurf->GetD3D9CubeTexture(), + d3d9::D3DCUBEMAP_FACE_NEGATIVE_X); + } + if (cubeMapAttachedSurfaces.positiveY != nullptr) { + Logger::debug("DDraw7Surface::InitializeAllCubeMapSurfaces: Detected positive Y cube map face"); + m_cubeMapSurfaces[2] = cubeMapAttachedSurfaces.positiveY; + InitializeAndAttachCubeFace(cubeMapAttachedSurfaces.positiveY, m_commonSurf->GetD3D9CubeTexture(), + d3d9::D3DCUBEMAP_FACE_POSITIVE_Y); + } + if (cubeMapAttachedSurfaces.negativeY != nullptr) { + Logger::debug("DDraw7Surface::InitializeAllCubeMapSurfaces: Detected negative Y cube map face"); + m_cubeMapSurfaces[3] = cubeMapAttachedSurfaces.negativeY; + InitializeAndAttachCubeFace(cubeMapAttachedSurfaces.negativeY, m_commonSurf->GetD3D9CubeTexture(), + d3d9::D3DCUBEMAP_FACE_NEGATIVE_Y); + } + if (cubeMapAttachedSurfaces.positiveZ != nullptr) { + Logger::debug("DDraw7Surface::InitializeAllCubeMapSurfaces: Detected positive Z cube map face"); + m_cubeMapSurfaces[4] = cubeMapAttachedSurfaces.positiveZ; + InitializeAndAttachCubeFace(cubeMapAttachedSurfaces.positiveZ, m_commonSurf->GetD3D9CubeTexture(), + d3d9::D3DCUBEMAP_FACE_POSITIVE_Z); + } + if (cubeMapAttachedSurfaces.negativeZ != nullptr) { + Logger::debug("DDraw7Surface::InitializeAllCubeMapSurfaces: Detected negative Z cube map face"); + m_cubeMapSurfaces[5] = cubeMapAttachedSurfaces.negativeZ; + InitializeAndAttachCubeFace(cubeMapAttachedSurfaces.negativeZ, m_commonSurf->GetD3D9CubeTexture(), + d3d9::D3DCUBEMAP_FACE_NEGATIVE_Z); + } } inline HRESULT DDraw7Surface::UploadSurfaceData() { - Logger::debug(str::format("DDraw7Surface::UploadSurfaceData: Uploading nr. [[7-", m_surfCount, "]]")); - - if (unlikely(m_commonIntf->HasDrawn() && m_commonSurf->IsGuardableSurface())) { - Logger::debug("DDraw7Surface::UploadSurfaceData: Skipping upload"); + //Fast skip + if (!m_commonSurf->IsDDrawSurfaceDirty()) return DD_OK; - } - const d3d9::D3DFORMAT format = m_commonSurf->GetD3D9Format(); + Logger::debug(str::format("DDraw7Surface::UploadSurfaceData: Uploading nr. [[7-", m_surfCount, "]]")); // Cube maps will also get marked as textures, so need to be handled first if (unlikely(m_commonSurf->IsCubeMap())) { // In theory we won't know which faces have been generated, // so check them one by one, and upload as needed - const uint16_t mipCount = m_commonSurf->GetMipCount(); + const uint16_t mipCount = m_commonSurf->GetMipCount(); + const bool isDXTFormat = m_commonSurf->IsDXTFormat(); if (likely(m_cubeMapSurfaces[0] != nullptr)) { - BlitToD3D9CubeMap(m_cubeMap9.ptr(), format, m_cubeMapSurfaces[0], mipCount); + BlitToD3D9CubeMap(m_commonSurf->GetD3D9CubeTexture(), m_cubeMapSurfaces[0], mipCount, isDXTFormat); } else { Logger::debug("DDraw7Surface::UploadSurfaceData: Positive X face is null, skpping"); } if (likely(m_cubeMapSurfaces[1] != nullptr)) { - BlitToD3D9CubeMap(m_cubeMap9.ptr(), format, m_cubeMapSurfaces[1], mipCount); + BlitToD3D9CubeMap(m_commonSurf->GetD3D9CubeTexture(), m_cubeMapSurfaces[1], mipCount, isDXTFormat); } else { Logger::debug("DDraw7Surface::UploadSurfaceData: Negative X face is null, skpping"); } if (likely(m_cubeMapSurfaces[2] != nullptr)) { - BlitToD3D9CubeMap(m_cubeMap9.ptr(), format, m_cubeMapSurfaces[2], mipCount); + BlitToD3D9CubeMap(m_commonSurf->GetD3D9CubeTexture(), m_cubeMapSurfaces[2], mipCount, isDXTFormat); } else { Logger::debug("DDraw7Surface::UploadSurfaceData: Positive Y face is null, skpping"); } if (likely(m_cubeMapSurfaces[3] != nullptr)) { - BlitToD3D9CubeMap(m_cubeMap9.ptr(), format, m_cubeMapSurfaces[3], mipCount); + BlitToD3D9CubeMap(m_commonSurf->GetD3D9CubeTexture(), m_cubeMapSurfaces[3], mipCount, isDXTFormat); } else { Logger::debug("DDraw7Surface::UploadSurfaceData: Negative Y face is null, skpping"); } if (likely(m_cubeMapSurfaces[4] != nullptr)) { - BlitToD3D9CubeMap(m_cubeMap9.ptr(), format, m_cubeMapSurfaces[4], mipCount); + BlitToD3D9CubeMap(m_commonSurf->GetD3D9CubeTexture(), m_cubeMapSurfaces[4], mipCount, isDXTFormat); } else { Logger::debug("DDraw7Surface::UploadSurfaceData: Positive Z face is null, skpping"); } if (likely(m_cubeMapSurfaces[5] != nullptr)) { - BlitToD3D9CubeMap(m_cubeMap9.ptr(), format, m_cubeMapSurfaces[5], mipCount); + BlitToD3D9CubeMap(m_commonSurf->GetD3D9CubeTexture(), m_cubeMapSurfaces[5], mipCount, isDXTFormat); } else { Logger::debug("DDraw7Surface::UploadSurfaceData: Negative Z face is null, skpping"); } // Blit all the mips for textures } else if (m_commonSurf->IsTexture()) { - BlitToD3D9Texture(m_texture9.ptr(), format, - m_proxy.ptr(), m_commonSurf->GetMipCount()); + BlitToD3D9Texture(m_commonSurf->GetD3D9Texture(), m_proxy.ptr(), + m_commonSurf->GetMipCount(), m_commonSurf->IsDXTFormat()); // Blit surfaces directly } else { - if (unlikely(m_commonSurf->IsDepthStencil())) { - if (likely(m_commonIntf->GetOptions()->uploadDepthStencils)) { - Logger::debug("DDraw7Surface::UploadSurfaceData: Uploading depth stencil"); - } else { - Logger::debug("DDraw7Surface::UploadSurfaceData: Skipping upload of depth stencil"); - return DD_OK; - } - } + if (unlikely(m_commonSurf->IsDepthStencil())) + Logger::debug("DDrawSurface::UploadSurfaceData: Uploading depth stencil"); - BlitToD3D9Surface(m_d3d9.ptr(), format, m_proxy.ptr()); + BlitToD3D9Surface(m_commonSurf->GetD3D9Surface(), GetShadowOrProxied(), + m_commonSurf->IsDXTFormat()); } - return DD_OK; - } + m_commonSurf->UnDirtyDDrawSurface(); - inline void DDraw7Surface::RefreshD3D9Device() { - D3DCommonDevice* commonDevice = m_commonIntf->GetCommonD3DDevice(); - - d3d9::IDirect3DDevice9* d3d9Device = commonDevice != nullptr ? commonDevice->GetD3D9Device() : nullptr; - if (unlikely(m_d3d9Device != d3d9Device)) { - // Check if the device has been recreated and reset all D3D9 resources - if (m_d3d9Device != nullptr) { - Logger::debug("DDraw7Surface: Device context has changed, clearing all D3D9 resources"); - m_cubeMap9 = nullptr; - m_texture9 = nullptr; - m_d3d9 = nullptr; - } - - m_d3d9Device = d3d9Device; - } + return DD_OK; } } diff --git a/src/ddraw/ddraw7/ddraw7_surface.h b/src/ddraw/ddraw7/ddraw7_surface.h index 7514d214d2c..8f7d0af179b 100644 --- a/src/ddraw/ddraw7/ddraw7_surface.h +++ b/src/ddraw/ddraw7/ddraw7_surface.h @@ -13,10 +13,12 @@ namespace dxvk { + class D3DCommonDevice; + /** * \brief IDirectDrawSurface7 interface implementation */ - class DDraw7Surface final : public DDrawWrappedObject { + class DDraw7Surface final : public DDrawWrappedObject { public: @@ -123,24 +125,34 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE GetLOD(LPDWORD lod); - DDrawCommonSurface* GetCommonSurface() const { - return m_commonSurf.ptr(); + IDirectDrawSurface7* GetShadowOrProxied(); + + HRESULT InitializeD3D9RenderTarget(); + + HRESULT InitializeD3D9DepthStencil(); + + HRESULT InitializeOrUploadD3D9(); + + void DownloadSurfaceData(); + + void SetShadowSurface(Com&& shadowSurf) { + m_shadowSurf = shadowSurf; } - DDrawCommonInterface* GetCommonInterface() const { - return m_commonIntf; + DDraw7Surface* GetShadowSurface() const { + return m_shadowSurf.ptr(); } - d3d9::IDirect3DDevice9* GetD3D9Device() const { - return m_d3d9Device; + DDrawCommonSurface* GetCommonSurface() const { + return m_commonSurf.ptr(); } - d3d9::IDirect3DTexture9* GetD3D9Texture() const { - return m_texture9.ptr(); + DDrawCommonInterface* GetCommonInterface() const { + return m_commonIntf; } - d3d9::IDirect3DCubeTexture9* GetD3D9CubeTexture() const { - return m_cubeMap9.ptr(); + void SetAttachedDepthStencil(Com&& depthStencil) { + m_depthStencil = depthStencil; } DDraw7Surface* GetAttachedDepthStencil() { @@ -174,41 +186,36 @@ namespace dxvk { m_commonSurf->SetIsAttached(false); } - HRESULT InitializeD3D9RenderTarget(); - - HRESULT InitializeD3D9DepthStencil(); - - HRESULT InitializeOrUploadD3D9(); - private: + inline void UpdateMipMapCount(); + inline void InitializeAndAttachCubeFace( IDirectDrawSurface7* surf, d3d9::IDirect3DCubeTexture9* cubeTex9, d3d9::D3DCUBEMAP_FACES face); - inline HRESULT InitializeD3D9(const bool initRT); + inline void InitializeAllCubeMapSurfaces(); inline HRESULT UploadSurfaceData(); - inline void RefreshD3D9Device(); - bool m_isChildObject = false; static uint32_t s_surfCount; uint32_t m_surfCount = 0; Com m_commonSurf; - DDrawCommonInterface* m_commonIntf = nullptr; - - DDraw7Surface* m_parentSurf = nullptr; + DDrawCommonInterface* m_commonIntf = nullptr; - d3d9::IDirect3DDevice9* m_d3d9Device = nullptr; + DDraw7Surface* m_parentSurf = nullptr; - Com m_cubeMap9; std::array m_cubeMapSurfaces; - Com m_texture9; + DDraw7Surface* m_nextFlippable = nullptr; + + // Offscreen plain surface we use to mask unwanted DDraw interactions, such + // as forced swapchain presents caused by blits/locks on primary surfaces + Com m_shadowSurf; // Back buffers will have depth stencil surfaces as attachments (in practice // I have never seen more than one depth stencil being attached at a time) diff --git a/src/ddraw/ddraw_caps.h b/src/ddraw/ddraw_caps.h index 2606b7c158c..8d4c9f25047 100644 --- a/src/ddraw/ddraw_caps.h +++ b/src/ddraw/ddraw_caps.h @@ -15,9 +15,10 @@ namespace dxvk::ddrawCaps { static constexpr uint32_t MaxEnabledLights = 8; - static constexpr uint8_t IndexBufferCount = 7; - // Index buffer sizes of XXS, XS, S, M, L, XL and XXL, corresponding to 0.1 kb, 0.5 kb, 2 kb, 8 kb, 32 kb, 64 kb and 128 kb - static constexpr UINT IndexCount[IndexBufferCount] = {64, 256, 1024, 4096, 16384, 32768, D3DMAXNUMVERTICES}; + static constexpr uint8_t IndexBufferCount = 9; + // Actual index buffer sizes are 2x, so 0.5 kb, 1kb, 2 kb, 4kb, 8 kb, 16kb, 32 kb, 64 kb and 128 kb + // Note: The DXVK backend gives us 256 byte chunks anyway as a minimum, so there's no point going below that + static constexpr UINT IndexCount[IndexBufferCount] = {256, 512, 1024, 2048, 4096, 8192, 16384, 32768, D3DMAXNUMVERTICES}; static constexpr uint8_t NumberOfFOURCCCodes = 6; static constexpr DWORD SupportedFourCCs[] = diff --git a/src/ddraw/ddraw_clipper.cpp b/src/ddraw/ddraw_clipper.cpp index 9be3b26739c..e49347d4183 100644 --- a/src/ddraw/ddraw_clipper.cpp +++ b/src/ddraw/ddraw_clipper.cpp @@ -5,7 +5,7 @@ namespace dxvk { DDrawClipper::DDrawClipper( Com&& clipperProxy, IUnknown* pParent) - : DDrawWrappedObject(pParent, std::move(clipperProxy), nullptr) { + : DDrawWrappedObject(pParent, std::move(clipperProxy)) { Logger::debug("DDrawClipper: Created a new clipper"); } @@ -24,6 +24,24 @@ namespace dxvk { return DD_OK; } + HRESULT STDMETHODCALLTYPE DDrawClipper::QueryInterface(REFIID riid, void** ppvObject) { + Logger::debug(">>> DDrawClipper::QueryInterface"); + + if (unlikely(ppvObject == nullptr)) + return E_POINTER; + + InitReturnPtr(ppvObject); + + try { + *ppvObject = ref(this->GetInterface(riid)); + return S_OK; + } catch (const DxvkError& e) { + Logger::warn(e.message()); + Logger::warn(str::format(riid)); + return E_NOINTERFACE; + } + } + HRESULT STDMETHODCALLTYPE DDrawClipper::GetClipList(LPRECT lpRect, LPRGNDATA lpClipList, LPDWORD lpdwSize) { Logger::debug("<<< DDrawClipper::GetClipList: Proxy"); return m_proxy->GetClipList(lpRect, lpClipList, lpdwSize); diff --git a/src/ddraw/ddraw_clipper.h b/src/ddraw/ddraw_clipper.h index bdaca83ed0c..78522bdbe5d 100644 --- a/src/ddraw/ddraw_clipper.h +++ b/src/ddraw/ddraw_clipper.h @@ -5,7 +5,7 @@ namespace dxvk { - class DDrawClipper final : public DDrawWrappedObject { + class DDrawClipper final : public DDrawWrappedObject { public: @@ -15,6 +15,8 @@ namespace dxvk { ~DDrawClipper(); + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject); + HRESULT STDMETHODCALLTYPE Initialize(LPDIRECTDRAW lpDD, DWORD dwFlags); HRESULT STDMETHODCALLTYPE GetClipList(LPRECT lpRect, LPRGNDATA lpClipList, LPDWORD lpdwSize); diff --git a/src/ddraw/ddraw_common_interface.h b/src/ddraw/ddraw_common_interface.h index fb1e3cd9f17..f80a4f3b9f4 100644 --- a/src/ddraw/ddraw_common_interface.h +++ b/src/ddraw/ddraw_common_interface.h @@ -30,6 +30,11 @@ namespace dxvk { ~DDrawCommonInterface(); + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject) { + *ppvObject = this; + return S_OK; + } + bool IsWrappedSurface(IDirectDrawSurface* surface) const; void AddWrappedSurface(IDirectDrawSurface* surface); @@ -62,26 +67,6 @@ namespace dxvk { DDrawSurface* GetSurfaceFromTextureHandle(D3DTEXTUREHANDLE handle) const; - void SetFlipRTSurfaceAndFlags(IUnknown* surf, DWORD flags) { - m_flipRTSurf = surf; - m_flipRTFlags = flags; - } - - IUnknown* GetFlipRTSurface() const { - return m_flipRTSurf; - } - - DWORD GetFlipRTFlags() const { - return m_flipRTFlags; - } - - bool HasDrawn() const { - // Returning true here means we skip all proxied back buffer blits, - // whereas returning false means we allow all proxied back buffer blits - return m_d3dOptions.backBufferGuard == D3DBackBufferGuard::Strict ? true : - m_d3dOptions.backBufferGuard == D3DBackBufferGuard::Disabled ? false : m_hasDrawn; - } - void MarkAsInitialized() { m_isInitialized = true; } @@ -90,21 +75,6 @@ namespace dxvk { return m_isInitialized; } - void UpdateDrawTracking() { - if (unlikely(!m_hasDrawn)) - m_hasDrawn = true; - } - - void ResetDrawTracking() { - if (likely(m_d3dOptions.backBufferGuard != D3DBackBufferGuard::Strict)) - m_hasDrawn = false; - } - - HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject) { - *ppvObject = this; - return S_OK; - } - void SetAdapterIdentifier(const d3d9::D3DADAPTER_IDENTIFIER9& adapterIdentifier9) { m_adapterIdentifier9 = adapterIdentifier9; } @@ -155,6 +125,14 @@ namespace dxvk { (m_cooperativeLevel & DDSCL_EXCLUSIVE); } + // Used to hook the hWnd during SetClipper calls, + // and use that on device creation if no other + // hWnd is specified through SetCooperativeLevel + void SetHWND(HWND hWnd) { + if (unlikely(m_hWnd == nullptr && hWnd != nullptr)) + m_hWnd = hWnd; + } + HWND GetHWND() const { return m_hWnd; } @@ -239,9 +217,6 @@ namespace dxvk { private: bool m_isInitialized = false; - // Track draw state on the common interface, since the back buffer - // guard should protect against global blits, not device specific ones - bool m_hasDrawn = false; bool m_waitForVBlank = true; DWORD m_cooperativeLevel = 0; @@ -251,9 +226,6 @@ namespace dxvk { HWND m_hWnd = nullptr; DDrawModeSize m_modeSize = { }; - IUnknown* m_flipRTSurf = nullptr; - DWORD m_flipRTFlags = 0; - d3d9::D3DADAPTER_IDENTIFIER9 m_adapterIdentifier9 = { }; D3DOptions m_d3dOptions; diff --git a/src/ddraw/ddraw_common_surface.cpp b/src/ddraw/ddraw_common_surface.cpp index 54da3685576..42601781e55 100644 --- a/src/ddraw/ddraw_common_surface.cpp +++ b/src/ddraw/ddraw_common_surface.cpp @@ -1,5 +1,7 @@ #include "ddraw_common_surface.h" +#include "d3d_common_device.h" + #include "ddraw7/ddraw7_surface.h" #include "ddraw4/ddraw4_surface.h" #include "ddraw2/ddraw3_surface.h" @@ -17,6 +19,66 @@ namespace dxvk { m_commonIntf->SetPrimarySurface(nullptr); } + IUnknown* DDrawCommonSurface::GetShadowSurfaceProxied() { + if (m_surf7 != nullptr) { + DDraw7Surface* shadow7 = m_surf7->GetShadowSurface(); + if (shadow7 != nullptr) + return shadow7->GetProxied(); + } + if (m_surf4 != nullptr) { + DDraw4Surface* shadow4 = m_surf4->GetShadowSurface(); + if (shadow4 != nullptr) + return shadow4->GetProxied(); + } + if (m_surf3 != nullptr) { + DDraw3Surface* shadow3 = m_surf3->GetShadowSurface(); + if (shadow3 != nullptr) + return shadow3->GetProxied(); + } + if (m_surf2 != nullptr) { + DDraw2Surface* shadow2 = m_surf2->GetShadowSurface(); + if (shadow2 != nullptr) + return shadow2->GetProxied(); + } + if (m_surf != nullptr) { + DDrawSurface* shadow = m_surf->GetShadowSurface(); + if (shadow != nullptr) + return shadow->GetProxied(); + } + + return nullptr; + } + + DDrawCommonSurface* DDrawCommonSurface::GetShadowCommonSurface() { + if (m_surf7 != nullptr) { + DDraw7Surface* shadow7 = m_surf7->GetShadowSurface(); + if (shadow7 != nullptr) + return shadow7->GetCommonSurface(); + } + if (m_surf4 != nullptr) { + DDraw4Surface* shadow4 = m_surf4->GetShadowSurface(); + if (shadow4 != nullptr) + return shadow4->GetCommonSurface(); + } + if (m_surf3 != nullptr) { + DDraw3Surface* shadow3 = m_surf3->GetShadowSurface(); + if (shadow3 != nullptr) + return shadow3->GetCommonSurface(); + } + if (m_surf2 != nullptr) { + DDraw2Surface* shadow2 = m_surf2->GetShadowSurface(); + if (shadow2 != nullptr) + return shadow2->GetCommonSurface(); + } + if (m_surf != nullptr) { + DDrawSurface* shadow = m_surf->GetShadowSurface(); + if (shadow != nullptr) + return shadow->GetCommonSurface(); + } + + return nullptr; + } + HRESULT DDrawCommonSurface::RefreshSurfaceDescripton() { HRESULT hr; @@ -58,4 +120,283 @@ namespace dxvk { return DD_OK; } + d3d9::IDirect3DDevice9* DDrawCommonSurface::RefreshD3D9Device() { + D3DCommonDevice* commonD3DDevice = m_commonIntf->GetCommonD3DDevice(); + + if (unlikely(m_commonD3DDevice != commonD3DDevice)) { + // Check if the device has been recreated and reset all D3D9 resources + if (m_commonD3DDevice != nullptr) { + Logger::debug("DDrawCommonSurface: Device context has changed, clearing all D3D9 resources"); + m_cubeMap9 = nullptr; + m_texture9 = nullptr; + m_surface9 = nullptr; + } + + m_commonD3DDevice = commonD3DDevice; + } + + return m_commonD3DDevice != nullptr ? m_commonD3DDevice->GetD3D9Device() : nullptr; + } + + HRESULT DDrawCommonSurface::InitializeD3D9(const bool initRenderTarget) { + const DWORD dwHeight = (m_desc2.dwFlags & DDSD_HEIGHT) ? m_desc2.dwHeight : m_desc.dwHeight; + const DWORD dwWidth = (m_desc2.dwFlags & DDSD_WIDTH) ? m_desc2.dwWidth : m_desc.dwWidth; + const DWORD dwCaps = (m_desc2.dwFlags & DDSD_CAPS) ? m_desc2.ddsCaps.dwCaps : m_desc.ddsCaps.dwCaps; + const DWORD dwCaps2 = (m_desc2.dwFlags & DDSD_CAPS) ? m_desc2.ddsCaps.dwCaps2 : 0; + + if (unlikely(dwHeight == 0 || dwWidth == 0)) { + Logger::err("DDrawCommonSurface::InitializeD3D9: Surface has 0 height or width"); + return DDERR_GENERIC; + } + + if (unlikely(m_format9 == d3d9::D3DFMT_UNKNOWN)) { + Logger::err("DDrawCommonSurface::InitializeD3D9: Surface has an unknown format"); + return DDERR_GENERIC; + } + + // Don't initialize P8 textures/surfaces since we don't support them. + // Some applications do require them to be created by ddraw, otherwise + // they will simply fail to start, so just ignore them for now. + if (unlikely(m_format9 == d3d9::D3DFMT_P8)) { + static bool s_formatP8ErrorShown; + + if (!std::exchange(s_formatP8ErrorShown, true)) + Logger::warn("DDrawCommonSurface::InitializeD3D9: Unsupported format D3DFMT_P8"); + + return DD_OK; + + // Similarly, D3DFMT_R3G3B2 isn't supported by D3D9 dxvk, however some + // applications require it to be supported by ddraw, even if they do not + // use it. Simply ignore any D3DFMT_R3G3B2 textures/surfaces for now. + } else if (unlikely(m_format9 == d3d9::D3DFMT_R3G3B2)) { + static bool s_formatR3G3B2ErrorShown; + + if (!std::exchange(s_formatR3G3B2ErrorShown, true)) + Logger::warn("DDrawCommonSurface::InitializeD3D9: Unsupported format D3DFMT_R3G3B2"); + + return DD_OK; + } + + d3d9::D3DPOOL pool = d3d9::D3DPOOL_DEFAULT; + DWORD usage = 0; + + // General surface/texture pool placement + if (dwCaps & DDSCAPS_LOCALVIDMEM) + pool = d3d9::D3DPOOL_DEFAULT; + // There's no explicit non-local video memory placement + // per se, but D3DPOOL_MANAGED is close enough + else if ((dwCaps & DDSCAPS_NONLOCALVIDMEM) || (dwCaps2 & DDSCAPS2_TEXTUREMANAGE)) + pool = d3d9::D3DPOOL_MANAGED; + else if (dwCaps & DDSCAPS_SYSTEMMEMORY) + // We can't know beforehand if a texture is or isn't going to be + // used in SetTexture() calls, and textures placed in D3DPOOL_SYSTEMMEM + // will not work in that context in dxvk, so revert to D3DPOOL_MANAGED. + pool = IsTextureOrCubeMap() ? d3d9::D3DPOOL_MANAGED : d3d9::D3DPOOL_SYSTEMMEM; + + // Place all possible render targets in DEFAULT + // + // Note: This is somewhat problematic for textures and cube maps + // which will have D3DUSAGE_RENDERTARGET, but also need to have + // D3DUSAGE_DYNAMIC for locking/uploads to work. The flag combination + // isn't supported in D3D9, but we have a D3D7 exception in place. + // + if (IsRenderTarget() || initRenderTarget) { + Logger::debug("DDrawCommonSurface::InitializeD3D9: Usage: D3DUSAGE_RENDERTARGET"); + pool = d3d9::D3DPOOL_DEFAULT; + usage |= D3DUSAGE_RENDERTARGET; + } + // All depth stencils will be created in DEFAULT + if (IsDepthStencil()) { + Logger::debug("DDrawCommonSurface::InitializeD3D9: Usage: D3DUSAGE_DEPTHSTENCIL"); + pool = d3d9::D3DPOOL_DEFAULT; + usage |= D3DUSAGE_DEPTHSTENCIL; + } + + // General usage flags + if (IsTextureOrCubeMap()) { + if (pool == d3d9::D3DPOOL_DEFAULT) { + Logger::debug("DDrawCommonSurface::InitializeD3D9: Usage: D3DUSAGE_DYNAMIC"); + usage |= D3DUSAGE_DYNAMIC; + } + if (unlikely(m_commonIntf->GetOptions()->autoGenMipMaps)) { + Logger::debug("DDrawCommonSurface::InitializeD3D9: Usage: D3DUSAGE_AUTOGENMIPMAP"); + usage |= D3DUSAGE_AUTOGENMIPMAP; + } + } + + const char* poolPlacement = pool == d3d9::D3DPOOL_DEFAULT ? "D3DPOOL_DEFAULT" : + pool == d3d9::D3DPOOL_SYSTEMMEM ? "D3DPOOL_SYSTEMMEM" : "D3DPOOL_MANAGED"; + + Logger::debug(str::format("DDrawCommonSurface::InitializeD3D9: Placing in: ", poolPlacement)); + + // Use the MSAA type that was determined to be supported during device creation + const d3d9::D3DMULTISAMPLE_TYPE multiSampleType = m_commonIntf->GetCommonD3DDevice()->GetMultiSampleType(); + d3d9::IDirect3DDevice9* d3d9Device = m_commonIntf->GetCommonD3DDevice()->GetD3D9Device(); + + HRESULT hr = DDERR_GENERIC; + + // Front Buffer + if (IsFrontBuffer()) { + Logger::debug("DDrawCommonSurface::InitializeD3D9: Initializing front buffer..."); + + hr = d3d9Device->GetBackBuffer(0, m_backBufferIndex, d3d9::D3DBACKBUFFER_TYPE_MONO, &m_surface9); + + if (unlikely(unlikely(FAILED(hr)))) { + Logger::err("DDrawCommonSurface::InitializeD3D9: Failed to retrieve front buffer"); + return hr; + } + + MarkAsD3D9BackBuffer(); + + // Back Buffer + } else if (IsBackBufferOrFlippable()) { + Logger::debug("DDrawCommonSurface::InitializeD3D9: Initializing back buffer..."); + + hr = d3d9Device->GetBackBuffer(0, m_backBufferIndex, d3d9::D3DBACKBUFFER_TYPE_MONO, &m_surface9); + + if (unlikely(FAILED(hr))) { + Logger::err("DDrawCommonSurface::InitializeD3D9: Failed to retrieve back buffer"); + return hr; + } + + MarkAsD3D9BackBuffer(); + + // Cube maps + } else if (IsCubeMap()) { + Logger::debug("DDrawCommonSurface::InitializeD3D9: Initializing cube map..."); + + hr = d3d9Device->CreateCubeTexture( + dwWidth, m_mipCount, usage, + m_format9, pool, &m_cubeMap9, nullptr); + + if (unlikely(FAILED(hr))) { + Logger::err("DDrawCommonSurface::InitializeD3D9: Failed to create cube map"); + return hr; + } + + if (unlikely(m_commonIntf->GetOptions()->autoGenMipMaps)) + m_cubeMap9->SetAutoGenFilterType(d3d9::D3DTEXF_ANISOTROPIC); + + // Always attach the positive X face to this surface + m_cubeMap9->GetCubeMapSurface(d3d9::D3DCUBEMAP_FACE_POSITIVE_X, 0, &m_surface9); + + Logger::debug("DDrawCommonSurface::InitializeD3D9: Created cube map"); + + // Textures + } else if (IsTexture()) { + Logger::debug("DDrawCommonSurface::InitializeD3D9: Initializing texture..."); + + hr = d3d9Device->CreateTexture( + dwWidth, dwHeight, m_mipCount, usage, + m_format9, pool, &m_texture9, nullptr); + + if (unlikely(FAILED(hr))) { + Logger::err("DDrawCommonSurface::InitializeD3D9: Failed to create texture"); + return hr; + } + + if (unlikely(m_commonIntf->GetOptions()->autoGenMipMaps)) + m_texture9->SetAutoGenFilterType(d3d9::D3DTEXF_ANISOTROPIC); + + // Attach level 0 to this surface + m_texture9->GetSurfaceLevel(0, &m_surface9); + + Logger::debug("DDrawCommonSurface::InitializeD3D9: Created texture"); + + // Depth Stencil + } else if (IsDepthStencil()) { + Logger::debug("DDrawCommonSurface::InitializeD3D9: Initializing depth stencil..."); + + hr = d3d9Device->CreateDepthStencilSurface( + dwWidth, dwHeight, m_format9, + multiSampleType, 0, FALSE, &m_surface9, nullptr); + + if (unlikely(FAILED(hr))) { + Logger::err("DDrawCommonSurface::InitializeD3D9: Failed to create depth stencil"); + return hr; + } + + Logger::debug("DDrawCommonSurface::InitializeD3D9: Created depth stencil surface"); + + // Offscreen Plain Surfaces + } else if (IsOffScreenPlainSurface()) { + Logger::debug("DDrawCommonSurface::InitializeD3D9: Initializing offscreen plain surface..."); + + if (unlikely(initRenderTarget)) { + Logger::debug("DDrawCommonSurface::InitializeD3D9: Offscreen plain surface is the render target"); + + hr = d3d9Device->GetBackBuffer(0, m_backBufferIndex, d3d9::D3DBACKBUFFER_TYPE_MONO, &m_surface9); + + if (unlikely(unlikely(FAILED(hr)))) { + Logger::err("DDrawCommonSurface::InitializeD3D9: Failed to retrieve offscreen plain surface"); + return hr; + } + + MarkAsD3D9BackBuffer(); + + } else { + hr = d3d9Device->CreateOffscreenPlainSurface( + dwWidth, dwHeight, m_format9, + pool, &m_surface9, nullptr); + + if (unlikely(FAILED(hr))) { + Logger::err("DDrawCommonSurface::InitializeD3D9: Failed to create offscreen plain surface"); + return hr; + } + + Logger::debug("DDrawCommonSurface::InitializeD3D9: Created offscreen plain surface"); + } + // Overlays (haven't seen any actual use of overlays in the wild) + } else if (IsOverlay()) { + Logger::debug("DDrawCommonSurface::InitializeD3D9: Initializing overlay..."); + + // Always link overlays to the back buffer + hr = d3d9Device->GetBackBuffer(0, m_backBufferIndex, d3d9::D3DBACKBUFFER_TYPE_MONO, &m_surface9); + + if (unlikely(FAILED(hr))) { + Logger::err("DDrawCommonSurface::InitializeD3D9: Failed to retrieve overlay surface"); + return hr; + } + + MarkAsD3D9BackBuffer(); + + // Generic render target + } else if (IsRenderTarget()) { + Logger::debug("DDrawCommonSurface::InitializeD3D9: Initializing render target..."); + + // Must be lockable for blitting to work. Note that + // D3D9 does not allow the creation of lockable RTs when + // using MSAA, but we have a D3D7 exception in place. + hr = d3d9Device->CreateRenderTarget( + dwWidth, dwHeight, m_format9, + multiSampleType, usage, TRUE, &m_surface9, nullptr); + + if (unlikely(FAILED(hr))) { + Logger::err("DDrawCommonSurface::InitializeD3D9: Failed to create render target"); + return hr; + } + + Logger::debug("DDrawCommonSurface::InitializeD3D9: Created render target"); + + // We sometimes get generic surfaces, with only dimensions, format and placement info + } else if (!IsNotKnown()) { + Logger::debug("DDrawCommonSurface::InitializeD3D9: Initializing generic surface..."); + + hr = d3d9Device->CreateOffscreenPlainSurface( + dwWidth, dwHeight, m_format9, + pool, &m_surface9, nullptr); + + if (unlikely(FAILED(hr))) { + Logger::err("DDrawCommonSurface::InitializeD3D9: Failed to create offscreen plain surface"); + return hr; + } + + Logger::debug("DDrawCommonSurface::InitializeD3D9: Created offscreen plain surface"); + } else { + Logger::warn("DDrawCommonSurface::InitializeD3D9: Skipping initialization of unknown surface"); + } + + return DD_OK; + } + } \ No newline at end of file diff --git a/src/ddraw/ddraw_common_surface.h b/src/ddraw/ddraw_common_surface.h index cb0dfd85724..d393082462e 100644 --- a/src/ddraw/ddraw_common_surface.h +++ b/src/ddraw/ddraw_common_surface.h @@ -10,6 +10,8 @@ namespace dxvk { + class D3DCommonDevice; + class DDraw7Surface; class DDraw4Surface; class DDraw3Surface; @@ -29,12 +31,56 @@ namespace dxvk { return S_OK; } + IUnknown* GetShadowSurfaceProxied(); + + DDrawCommonSurface* GetShadowCommonSurface(); + HRESULT RefreshSurfaceDescripton(); + d3d9::IDirect3DDevice9* RefreshD3D9Device(); + + HRESULT InitializeD3D9(const bool initRenderTarget); + + bool IsInitialized() const { + return m_surface9 != nullptr; + } + + void SetCommonD3DDevice(D3DCommonDevice* commonD3DDevice) { + m_commonD3DDevice = commonD3DDevice; + } + + D3DCommonDevice* GetCommonD3DDevice() const { + return m_commonD3DDevice; + } + DDrawCommonInterface* GetCommonInterface() const { return m_commonIntf.ptr(); } + void SetD3D9Surface(Com&& surface9) { + m_surface9 = surface9; + } + + d3d9::IDirect3DSurface9* GetD3D9Surface() const { + return m_surface9.ptr(); + } + + void SetD3D9Texture(Com&& texture9) { + m_texture9 = texture9; + } + + d3d9::IDirect3DTexture9* GetD3D9Texture() const { + return m_texture9.ptr(); + } + + void SetD3D9CubeTexture(Com&& cubeMap9) { + m_cubeMap9 = cubeMap9; + } + + d3d9::IDirect3DCubeTexture9* GetD3D9CubeTexture() const { + return m_cubeMap9.ptr(); + } + bool IsDesc2Set() const { return m_isDesc2Set; } @@ -44,13 +90,8 @@ namespace dxvk { m_isDesc2Set = true; m_format9 = ConvertFormat(m_desc2.ddpfPixelFormat); // determine and cache various frequently used flag combinations - m_isTextureOrCubeMap = IsTexture() || IsCubeMap(); - m_isBackBufferOrFlippable = !IsFrontBuffer() && (IsBackBuffer() || IsFlippable()); + m_isBackBufferOrFlippable = !IsPrimarySurface() && !IsFrontBuffer() && (IsBackBuffer() || IsFlippable()); m_isRenderTarget = IsFrontBuffer() || IsBackBuffer() || IsFlippable() || Is3DSurface(); - m_isForwardableSurface = IsFrontBuffer() || IsBackBuffer() || IsFlippable() - || IsDepthStencil() || IsOffScreenPlainSurface(); - m_isGuardableSurface = IsPrimarySurface() || IsFrontBuffer() - || IsBackBuffer() || IsFlippable(); } const DDSURFACEDESC2* GetDesc2() const { @@ -66,18 +107,19 @@ namespace dxvk { m_isDescSet = true; m_format9 = ConvertFormat(m_desc.ddpfPixelFormat); // determine and cache various frequently used flag combinations - m_isBackBufferOrFlippable = !IsFrontBuffer() && (IsBackBuffer() || IsFlippable()); + m_isBackBufferOrFlippable = !IsPrimarySurface() && !IsFrontBuffer() && (IsBackBuffer() || IsFlippable()); m_isRenderTarget = IsFrontBuffer() || IsBackBuffer() || IsFlippable() || Is3DSurface(); - m_isForwardableSurface = IsFrontBuffer() || IsBackBuffer() || IsFlippable() - || IsDepthStencil() || IsOffScreenPlainSurface(); - m_isGuardableSurface = IsPrimarySurface() || IsFrontBuffer() - || IsBackBuffer() || IsFlippable(); } const DDSURFACEDESC* GetDesc() const { return &m_desc; } + uint8_t GetColorBitCount() const { + return (m_desc2.dwFlags & DDSD_PIXELFORMAT) ? m_desc2.ddpfPixelFormat.dwRGBBitCount + : m_desc.ddpfPixelFormat.dwRGBBitCount; + } + bool IsAlphaFormat() const { return ((m_desc2.dwFlags & DDSD_PIXELFORMAT) && (m_desc2.ddpfPixelFormat.dwFlags & DDPF_ALPHAPIXELS)) || ((m_desc.dwFlags & DDSD_PIXELFORMAT) && (m_desc.ddpfPixelFormat.dwFlags & DDPF_ALPHAPIXELS)); @@ -96,7 +138,7 @@ namespace dxvk { const DDCOLORKEY* colorKey = (m_desc2.dwFlags & DDSD_CKSRCBLT) ? &m_desc2.ddckCKSrcBlt : &m_desc.ddckCKSrcBlt; // Empire of the Ants relies on us using the "Low" color space DWORD - return ColorKeyToRGB(pixelFormat, colorKey->dwColorSpaceLowValue); + return ColorKeyToRGB(pixelFormat, colorKey->dwColorSpaceLowValue, m_commonIntf->GetOptions()->colorKeyTolerance); } d3d9::D3DFORMAT GetD3D9Format() const { @@ -110,7 +152,8 @@ namespace dxvk { } uint16_t GetMipCount() const { - return m_mipCount; + // Properly handle textures with auto-generated mip maps + return std::max(1u, m_mipCount); } void SetMipCount(uint16_t mipCount) { @@ -125,16 +168,28 @@ namespace dxvk { m_backBufferIndex = index + 1; } - bool HasDirtyMipMaps() const { - return m_dirtyMipMaps; + bool IsDDrawSurfaceDirty() const { + return m_dirtyDDraw; } - void DirtyMipMaps() { - m_dirtyMipMaps = true; + void DirtyDDrawSurface() { + m_dirtyDDraw = true; } - void UnDirtyMipMaps() { - m_dirtyMipMaps = false; + void UnDirtyDDrawSurface() { + m_dirtyDDraw = false; + } + + bool IsD3D9SurfaceDirty() const { + return m_dirtyD3D9; + } + + void DirtyD3D9Surface() { + m_dirtyD3D9 = true; + } + + void UnDirtyD3D9Surface() { + m_dirtyD3D9 = false; } bool IsAttached() const { @@ -145,6 +200,22 @@ namespace dxvk { m_isAttached = isAttached; } + void MarkAsD3D9BackBuffer() { + m_isD3D9BackBuffer = true; + } + + bool IsD3D9BackBuffer() const { + return m_isD3D9BackBuffer; + } + + void MarkAsD3D9DepthStencil() { + m_isD3D9DepthStencil = true; + } + + bool IsD3D9DepthStencil() const { + return m_isD3D9DepthStencil; + } + void SetClipper(DDrawClipper* clipper) { m_clipper = clipper; } @@ -283,7 +354,8 @@ namespace dxvk { } bool IsManaged() const { - return m_desc2.ddsCaps.dwCaps2 & DDSCAPS2_TEXTUREMANAGE; + return m_desc2.ddsCaps.dwCaps2 & DDSCAPS2_TEXTUREMANAGE + || m_desc2.ddsCaps.dwCaps2 & DDSCAPS2_D3DTEXTUREMANAGE; } bool IsInSystemMemory() const { @@ -292,12 +364,14 @@ namespace dxvk { } bool HasColorKey() const { - return (m_desc2.dwFlags & DDSD_CKSRCBLT || - m_desc.dwFlags & DDSD_CKSRCBLT); + return m_desc2.dwFlags & DDSD_CKSRCBLT + || m_desc.dwFlags & DDSD_CKSRCBLT; } bool IsTextureOrCubeMap() const { - return m_isTextureOrCubeMap; + return m_desc2.ddsCaps.dwCaps & DDSCAPS_TEXTURE + || m_desc2.ddsCaps.dwCaps2 & DDSCAPS2_CUBEMAP + || m_desc.ddsCaps.dwCaps & DDSCAPS_TEXTURE; } bool IsBackBufferOrFlippable() const { @@ -308,12 +382,17 @@ namespace dxvk { return m_isRenderTarget; } - bool IsForwardableSurface() const { - return m_isForwardableSurface; + bool IsDXTFormat() const { + return m_format9 == d3d9::D3DFMT_DXT1 + || m_format9 == d3d9::D3DFMT_DXT2 + || m_format9 == d3d9::D3DFMT_DXT3 + || m_format9 == d3d9::D3DFMT_DXT4 + || m_format9 == d3d9::D3DFMT_DXT5; } - bool IsGuardableSurface() const { - return m_isGuardableSurface; + bool Is8BitFormat() const { + return m_format9 == d3d9::D3DFMT_R3G3B2 + || m_format9 == d3d9::D3DFMT_P8; } HRESULT ValidateRTUsage() const { @@ -341,64 +420,78 @@ namespace dxvk { void ListSurfaceDetails() const { const char* type = "generic surface"; - if (IsFrontBuffer()) type = "front buffer"; + if (IsPrimarySurface()) type = "primary surface"; + else if (IsFrontBuffer()) type = "front buffer"; else if (IsBackBuffer()) type = "back buffer"; + else if (IsCubeMap()) type = "cube texture"; else if (IsTextureMip()) type = "texture mipmap"; else if (IsTexture()) type = "texture"; else if (IsDepthStencil()) type = "depth stencil"; else if (IsOffScreenPlainSurface()) type = "offscreen plain surface"; else if (IsOverlay()) type = "overlay"; else if (Is3DSurface()) type = "render target"; - else if (IsPrimarySurface()) type = "primary surface"; else if (IsNotKnown()) type = "unknown"; + const DWORD width = IsDesc2Set() ? m_desc2.dwWidth : m_desc.dwWidth; + const DWORD height = IsDesc2Set() ? m_desc2.dwHeight : m_desc.dwHeight; + const DWORD mipMapCount = IsDesc2Set() ? m_desc2.dwMipMapCount : m_desc.dwMipMapCount; + const DWORD backBuferCount = IsDesc2Set() ? m_desc2.dwBackBufferCount : m_desc.dwBackBufferCount; + Logger::debug(str::format(" Type: ", type)); - Logger::debug(str::format(" Dimensions: ", m_desc.dwWidth, "x", m_desc.dwHeight)); + Logger::debug(str::format(" Dimensions: ", width, "x", height)); Logger::debug(str::format(" Format: ", GetD3D9Format())); Logger::debug(str::format(" IsComplex: ", IsComplex() ? "yes" : "no")); - Logger::debug(str::format(" HasMipMaps: ", m_desc.dwMipMapCount ? "yes" : "no")); + Logger::debug(str::format(" HasMipMaps: ", mipMapCount ? "yes" : "no")); Logger::debug(str::format(" IsAttached: ", IsAttached() ? "yes" : "no")); if (IsFrontBuffer()) - Logger::debug(str::format(" BackBuffers: ", m_desc.dwBackBufferCount)); + Logger::debug(str::format(" BackBuffers: ", backBuferCount)); if (HasColorKey()) Logger::debug(str::format(" ColorKey: ", GetColorKey()->dwColorSpaceLowValue)); } private: - bool m_dirtyMipMaps = false; - bool m_isAttached = false; - bool m_isDesc2Set = false; - bool m_isDescSet = false; + bool m_dirtyDDraw = false; + bool m_dirtyD3D9 = false; + + bool m_isAttached = false; + bool m_isD3D9BackBuffer = false; + bool m_isD3D9DepthStencil = false; + + bool m_isDesc2Set = false; + bool m_isDescSet = false; + + bool m_isBackBufferOrFlippable = false; + bool m_isRenderTarget = false; + + uint16_t m_mipCount = 1; + uint32_t m_backBufferIndex = 0; - bool m_isTextureOrCubeMap = false; - bool m_isBackBufferOrFlippable = false; - bool m_isRenderTarget = false; - bool m_isForwardableSurface = false; - bool m_isGuardableSurface = false; + DDSURFACEDESC m_desc = { }; + DDSURFACEDESC2 m_desc2 = { }; + d3d9::D3DFORMAT m_format9 = d3d9::D3DFMT_UNKNOWN; - uint16_t m_mipCount = 1; - uint32_t m_backBufferIndex = 0; + Com m_clipper; + Com m_palette; - DDSURFACEDESC m_desc = { }; - DDSURFACEDESC2 m_desc2 = { }; - d3d9::D3DFORMAT m_format9 = d3d9::D3DFMT_UNKNOWN; + Com m_commonIntf; - Com m_clipper; - Com m_palette; + D3DCommonDevice* m_commonD3DDevice = nullptr; - Com m_commonIntf; + Com m_surface9; + Com m_texture9; + Com m_cubeMap9; // Track all possible surface versions of the same object - DDraw7Surface* m_surf7 = nullptr; - DDraw4Surface* m_surf4 = nullptr; - DDraw3Surface* m_surf3 = nullptr; - DDraw2Surface* m_surf2 = nullptr; - DDrawSurface* m_surf = nullptr; + DDraw7Surface* m_surf7 = nullptr; + DDraw4Surface* m_surf4 = nullptr; + DDraw3Surface* m_surf3 = nullptr; + DDraw2Surface* m_surf2 = nullptr; + DDrawSurface* m_surf = nullptr; // Track the origin surface, as in the DDraw surface // that gets created through a CreateSurface call - IUnknown* m_origin = nullptr; + IUnknown* m_origin = nullptr; }; diff --git a/src/ddraw/ddraw_format.h b/src/ddraw/ddraw_format.h index d865c8320de..7ac1f1dc068 100644 --- a/src/ddraw/ddraw_format.h +++ b/src/ddraw/ddraw_format.h @@ -413,14 +413,8 @@ namespace dxvk { zformat.dwStencilBitMask = 0x0000; break; - case d3d9::D3DFMT_D24X4S4: - zformat.dwFlags = DDPF_ZBUFFER | DDPF_STENCILBUFFER; - zformat.dwZBufferBitDepth = 32; - zformat.dwZBitMask = 0x00ffffff; - zformat.dwStencilBitDepth = 4; - zformat.dwStencilBitMask = 0xf0000000; - break; - + // Historically has had dwZBufferBitDepth = 24 too in some cases, + // however we fix that up during format enumeration. Use 32 here. case d3d9::D3DFMT_D24X8: zformat.dwFlags = DDPF_ZBUFFER; zformat.dwZBufferBitDepth = 32; @@ -429,6 +423,14 @@ namespace dxvk { zformat.dwStencilBitMask = 0x00000000; break; + case d3d9::D3DFMT_D24X4S4: + zformat.dwFlags = DDPF_ZBUFFER | DDPF_STENCILBUFFER; + zformat.dwZBufferBitDepth = 32; + zformat.dwZBitMask = 0x00ffffff; + zformat.dwStencilBitDepth = 4; + zformat.dwStencilBitMask = 0xf0000000; + break; + case d3d9::D3DFMT_D24S8: zformat.dwFlags = DDPF_ZBUFFER | DDPF_STENCILBUFFER; zformat.dwZBufferBitDepth = 32; @@ -454,12 +456,22 @@ namespace dxvk { return zformat; } - inline bool IsDXTFormat(d3d9::D3DFORMAT fmt) { - return fmt == d3d9::D3DFMT_DXT1 - || fmt == d3d9::D3DFMT_DXT2 - || fmt == d3d9::D3DFMT_DXT3 - || fmt == d3d9::D3DFMT_DXT4 - || fmt == d3d9::D3DFMT_DXT5; + inline d3d9::D3DMULTISAMPLE_TYPE GetSupportedMultiSampleType(d3d9::IDirect3D9* d3d9Intf, d3d9::D3DFORMAT backBufferFormat) { + HRESULT hr4S = d3d9Intf->CheckDeviceMultiSampleType(0, d3d9::D3DDEVTYPE_HAL, backBufferFormat, + TRUE, d3d9::D3DMULTISAMPLE_4_SAMPLES, NULL); + if (likely(SUCCEEDED(hr4S))) { + Logger::info("GetSupportedMultiSampleType: Using 4x MSAA for FSAA emulation"); + return d3d9::D3DMULTISAMPLE_4_SAMPLES; + } else { + HRESULT hr2S = d3d9Intf->CheckDeviceMultiSampleType(0, d3d9::D3DDEVTYPE_HAL, backBufferFormat, + TRUE, d3d9::D3DMULTISAMPLE_2_SAMPLES, NULL); + if (likely(SUCCEEDED(hr2S))) { + Logger::info("GetSupportedMultiSampleType: Using 2x MSAA for FSAA emulation"); + return d3d9::D3DMULTISAMPLE_2_SAMPLES; + } + } + + return d3d9::D3DMULTISAMPLE_NONE; } template @@ -481,7 +493,6 @@ namespace dxvk { return DD_OK; } - // D3D5 Callback function used to navigate a flipable surface swapchain inline HRESULT STDMETHODCALLTYPE ListBackBufferSurfacesCallback(IDirectDrawSurface* subsurf, DDSURFACEDESC* desc, void* ctx) { IDirectDrawSurface** nextBackBuffer = static_cast(ctx); @@ -493,7 +504,6 @@ namespace dxvk { return DDENUMRET_OK; } - // D3D6 Callback function used to navigate a flipable surface swapchain inline HRESULT STDMETHODCALLTYPE ListBackBufferSurfaces4Callback(IDirectDrawSurface4* subsurf, DDSURFACEDESC2* desc, void* ctx) { IDirectDrawSurface4** nextBackBuffer = static_cast(ctx); @@ -505,7 +515,6 @@ namespace dxvk { return DDENUMRET_OK; } - // D3D7 callback function used to navigate a flipable surface swapchain inline HRESULT STDMETHODCALLTYPE ListBackBufferSurfaces7Callback(IDirectDrawSurface7* subsurf, DDSURFACEDESC2* desc, void* ctx) { IDirectDrawSurface7** nextBackBuffer = static_cast(ctx); @@ -517,7 +526,6 @@ namespace dxvk { return DDENUMRET_OK; } - // D3D5 callback function used to navigate the linked mip map chain inline HRESULT STDMETHODCALLTYPE ListMipChainSurfacesCallback(IDirectDrawSurface* subsurf, DDSURFACEDESC* desc, void* ctx) { IDirectDrawSurface** nextMip = static_cast(ctx); @@ -529,7 +537,6 @@ namespace dxvk { return DDENUMRET_OK; } - // D3D6 callback function used to navigate the linked mip map chain inline HRESULT STDMETHODCALLTYPE ListMipChainSurfaces4Callback(IDirectDrawSurface4* subsurf, DDSURFACEDESC2* desc, void* ctx) { IDirectDrawSurface4** nextMip = static_cast(ctx); @@ -542,7 +549,6 @@ namespace dxvk { return DDENUMRET_OK; } - // D3D7 callback function used to navigate the linked mip map chain inline HRESULT STDMETHODCALLTYPE ListMipChainSurfaces7Callback(IDirectDrawSurface7* subsurf, DDSURFACEDESC2* desc, void* ctx) { IDirectDrawSurface7** nextMip = static_cast(ctx); @@ -555,7 +561,6 @@ namespace dxvk { return DDENUMRET_OK; } - // D3D5 callback function used to enumerate attached surfaces inline HRESULT STDMETHODCALLTYPE EnumAttachedSurfacesCallback(IDirectDrawSurface* surface, DDSURFACEDESC* desc, void* ctx) { auto& attachedSurfaces = *static_cast*>(ctx); @@ -565,7 +570,6 @@ namespace dxvk { return DDENUMRET_OK; } - // D3D6 callback function used to enumerate attached surfaces inline HRESULT STDMETHODCALLTYPE EnumAttachedSurfaces4Callback(IDirectDrawSurface4* surface, DDSURFACEDESC2* desc, void* ctx) { auto& attachedSurfaces = *static_cast*>(ctx); @@ -575,7 +579,6 @@ namespace dxvk { return DDENUMRET_OK; } - // D3D7 callback function used to enumerate attached surfaces inline HRESULT STDMETHODCALLTYPE EnumAttachedSurfaces7Callback(IDirectDrawSurface7* surface, DDSURFACEDESC2* desc, void* ctx) { auto& attachedSurfaces = *static_cast*>(ctx); @@ -585,7 +588,6 @@ namespace dxvk { return DDENUMRET_OK; } - // D3D7 callback function used in cube map face/surface initialization inline HRESULT STDMETHODCALLTYPE EnumAndAttachCubeMapFacesCallback(IDirectDrawSurface7* subsurf, DDSURFACEDESC2* desc, void* ctx) { CubeMapAttachedSurfaces* cubeMapAttachedSurfaces = static_cast(ctx); @@ -617,14 +619,11 @@ namespace dxvk { } inline void BlitToD3D9CubeMap( - d3d9::IDirect3DCubeTexture9* cubeTex9, - d3d9::D3DFORMAT format9, - IDirectDrawSurface7* surface, - uint16_t mipLevels) { - // Properly handle cube textures with auto-generated mip maps - const uint16_t actualMipLevels = std::max(1u, mipLevels); - - DDSURFACEDESC2 desc = { }; + d3d9::IDirect3DCubeTexture9* cubeTex9, + IDirectDrawSurface7* surface, + const uint16_t mipLevels, + const bool isDXTFormat) { + DDSURFACEDESC2 desc; desc.dwSize = sizeof(DDSURFACEDESC2); surface->GetSurfaceDesc(&desc); const d3d9::D3DCUBEMAP_FACES face = GetCubemapFace(&desc); @@ -633,9 +632,9 @@ namespace dxvk { IDirectDrawSurface7* mipMap = surface; - Logger::debug(str::format("BlitToD3D9CubeMap: Blitting ", actualMipLevels, " mip map(s)")); + Logger::debug(str::format("BlitToD3D9CubeMap: Blitting ", mipLevels, " mip map(s)")); - for (uint16_t i = 0; i < actualMipLevels; i++) { + for (uint16_t i = 0; i < mipLevels; i++) { // Should never occur normally, but acts as a last ditch safety check if (unlikely(mipMap == nullptr)) { Logger::warn(str::format("BlitToD3D9CubeMap: Last found source mip ", i - 1)); @@ -644,22 +643,18 @@ namespace dxvk { d3d9::D3DLOCKED_RECT rect9mip; // D3DLOCK_DISCARD will get ignored for MANAGED/SYSTEMMEM, but will work on DEFAULT - HRESULT hr9 = cubeTex9->LockRect(face, i, &rect9mip, 0, D3DLOCK_DISCARD); + HRESULT hr9 = cubeTex9->LockRect(face, i, &rect9mip, NULL, D3DLOCK_DISCARD); if (likely(SUCCEEDED(hr9))) { DDSURFACEDESC2 descMip; descMip.dwSize = sizeof(DDSURFACEDESC2); - HRESULT hr = mipMap->Lock(0, &descMip, DDLOCK_READONLY, 0); + HRESULT hr = mipMap->Lock(NULL, &descMip, DDLOCK_READONLY, NULL); if (likely(SUCCEEDED(hr))) { - Logger::debug(str::format("descMip.dwWidth: ", descMip.dwWidth)); - Logger::debug(str::format("descMip.dwHeight: ", descMip.dwHeight)); - Logger::debug(str::format("descMip.lPitch: ", descMip.lPitch)); - Logger::debug(str::format("rect9mip.Pitch: ", rect9mip.Pitch)); // The lock pitch of a DXT surface represents its entire size, apparently - if (IsDXTFormat(format9)) { + if (isDXTFormat) { const size_t size = static_cast(descMip.lPitch); memcpy(rect9mip.pBits, descMip.lpSurface, size); Logger::debug(str::format("BlitToD3D9CubeMap: Done blitting DXT mip ", i)); - } else if (unlikely(descMip.lPitch != rect9mip.Pitch)) { + } else if (descMip.lPitch != rect9mip.Pitch) { Logger::debug(str::format("BlitToD3D9CubeMap: Incompatible mip map ", i, " pitch")); uint8_t* data9 = reinterpret_cast(rect9mip.pBits); @@ -675,7 +670,7 @@ namespace dxvk { memcpy(rect9mip.pBits, descMip.lpSurface, size); Logger::debug(str::format("BlitToD3D9CubeMap: Done blitting mip ", i)); } - mipMap->Unlock(0); + mipMap->Unlock(NULL); } else { Logger::warn(str::format("BlitToD3D9CubeMap: Failed to lock mip ", i)); } @@ -693,18 +688,15 @@ namespace dxvk { template inline void BlitToD3D9Texture( - d3d9::IDirect3DTexture9* texture9, - d3d9::D3DFORMAT format9, - SurfaceType* surface, - uint16_t mipLevels) { - // Properly handle textures with auto-generated mip maps - const uint16_t actualMipLevels = std::max(1u, mipLevels); - + d3d9::IDirect3DTexture9* texture9, + SurfaceType* surface, + const uint16_t mipLevels, + const bool isDXTFormat) { SurfaceType* mipMap = surface; - Logger::debug(str::format("BlitToD3D9Texture: Blitting ", actualMipLevels, " mip map(s)")); + Logger::debug(str::format("BlitToD3D9Texture: Blitting ", mipLevels, " mip map(s)")); - for (uint16_t i = 0; i < actualMipLevels; i++) { + for (uint16_t i = 0; i < mipLevels; i++) { // Should never occur normally, but acts as a last ditch safety check if (unlikely(mipMap == nullptr)) { Logger::warn(str::format("BlitToD3D9Texture: Last found source mip ", i - 1)); @@ -713,22 +705,18 @@ namespace dxvk { d3d9::D3DLOCKED_RECT rect9mip; // D3DLOCK_DISCARD will get ignored for MANAGED/SYSTEMMEM, but will work on DEFAULT - HRESULT hr9 = texture9->LockRect(i, &rect9mip, 0, D3DLOCK_DISCARD); + HRESULT hr9 = texture9->LockRect(i, &rect9mip, NULL, D3DLOCK_DISCARD); if (likely(SUCCEEDED(hr9))) { DescType descMip; descMip.dwSize = sizeof(DescType); - HRESULT hr = mipMap->Lock(0, &descMip, DDLOCK_READONLY, 0); + HRESULT hr = mipMap->Lock(NULL, &descMip, DDLOCK_READONLY, NULL); if (likely(SUCCEEDED(hr))) { - Logger::debug(str::format("descMip.dwWidth: ", descMip.dwWidth)); - Logger::debug(str::format("descMip.dwHeight: ", descMip.dwHeight)); - Logger::debug(str::format("descMip.lPitch: ", descMip.lPitch)); - Logger::debug(str::format("rect9mip.Pitch: ", rect9mip.Pitch)); // The lock pitch of a DXT surface represents its entire size, apparently - if (IsDXTFormat(format9)) { + if (isDXTFormat) { const size_t size = static_cast(descMip.lPitch); memcpy(rect9mip.pBits, descMip.lpSurface, size); Logger::debug(str::format("BlitToD3D9Texture: Done blitting DXT mip ", i)); - } else if (unlikely(descMip.lPitch != rect9mip.Pitch)) { + } else if (descMip.lPitch != rect9mip.Pitch) { Logger::debug(str::format("BlitToD3D9Texture: Incompatible mip map ", i, " pitch")); uint8_t* data9 = reinterpret_cast(rect9mip.pBits); @@ -744,7 +732,7 @@ namespace dxvk { memcpy(rect9mip.pBits, descMip.lpSurface, size); Logger::debug(str::format("BlitToD3D9Texture: Done blitting mip ", i)); } - mipMap->Unlock(0); + mipMap->Unlock(NULL); } else { Logger::warn(str::format("BlitToD3D9Texture: Failed to lock mip ", i)); } @@ -770,27 +758,23 @@ namespace dxvk { template inline void BlitToD3D9Surface( - d3d9::IDirect3DSurface9* surface9, - d3d9::D3DFORMAT format9, - SurfaceType* surface) { + d3d9::IDirect3DSurface9* surface9, + SurfaceType* surface, + const bool isDXTFormat) { d3d9::D3DLOCKED_RECT rect9; // D3DLOCK_DISCARD will get ignored for MANAGED/SYSTEMMEM, but will work on DEFAULT - HRESULT hr9 = surface9->LockRect(&rect9, 0, D3DLOCK_DISCARD); + HRESULT hr9 = surface9->LockRect(&rect9, NULL, D3DLOCK_DISCARD); if (likely(SUCCEEDED(hr9))) { DescType desc; desc.dwSize = sizeof(DescType); - HRESULT hr = surface->Lock(0, &desc, DDLOCK_READONLY, 0); + HRESULT hr = surface->Lock(NULL, &desc, DDLOCK_READONLY, NULL); if (likely(SUCCEEDED(hr))) { - Logger::debug(str::format("desc.dwWidth: ", desc.dwWidth)); - Logger::debug(str::format("desc.dwHeight: ", desc.dwHeight)); - Logger::debug(str::format("desc.lPitch: ", desc.lPitch)); - Logger::debug(str::format("rect.Pitch: ", rect9.Pitch)); // The lock pitch of a DXT surface represents its entire size, apparently - if (IsDXTFormat(format9)) { + if (isDXTFormat) { const size_t size = static_cast(desc.lPitch); memcpy(rect9.pBits, desc.lpSurface, size); Logger::debug("BlitToD3D9Texture: Done blitting DXT surface"); - } else if (unlikely(desc.lPitch != rect9.Pitch)) { + } else if (desc.lPitch != rect9.Pitch) { Logger::debug("BlitToD3D9Surface: Incompatible surface pitch"); uint8_t* data9 = reinterpret_cast(rect9.pBits); @@ -806,7 +790,7 @@ namespace dxvk { memcpy(rect9.pBits, desc.lpSurface, size); Logger::debug("BlitToD3D9Surface: Done blitting surface"); } - surface->Unlock(0); + surface->Unlock(NULL); } else { Logger::warn("BlitToD3D9Surface: Failed to lock surface"); } @@ -816,23 +800,24 @@ namespace dxvk { } } - // reverse blitter, used in the forceProxiedPresent logic template inline void BlitToDDrawSurface( - SurfaceType* surface, - d3d9::IDirect3DSurface9* surface9) { + SurfaceType* surface, + d3d9::IDirect3DSurface9* surface9, + const bool isDXTFormat) { DescType desc; desc.dwSize = sizeof(DescType); - HRESULT hr = surface->Lock(0, &desc, DDLOCK_WRITEONLY, 0); + HRESULT hr = surface->Lock(NULL, &desc, DDLOCK_WRITEONLY, NULL); if (likely(SUCCEEDED(hr))) { d3d9::D3DLOCKED_RECT rect9; - HRESULT hr9 = surface9->LockRect(&rect9, 0, D3DLOCK_READONLY); + HRESULT hr9 = surface9->LockRect(&rect9, NULL, D3DLOCK_READONLY); if (likely(SUCCEEDED(hr9))) { - Logger::debug(str::format("desc.dwWidth: ", desc.dwWidth)); - Logger::debug(str::format("desc.dwHeight: ", desc.dwHeight)); - Logger::debug(str::format("desc.lPitch: ", desc.lPitch)); - Logger::debug(str::format("rect.Pitch: ", rect9.Pitch)); - if (unlikely(desc.lPitch != rect9.Pitch)) { + // The lock pitch of a DXT surface represents its entire size, apparently + if (unlikely(isDXTFormat)) { + const size_t size = static_cast(desc.lPitch); + memcpy(desc.lpSurface, rect9.pBits, size); + Logger::debug("BlitToDDrawSurface: Done blitting DXT surface"); + } else if (desc.lPitch != rect9.Pitch) { Logger::debug("BlitToDDrawSurface: Incompatible surface pitch"); uint8_t* data7 = reinterpret_cast(desc.lpSurface); @@ -852,13 +837,13 @@ namespace dxvk { } else { Logger::warn("BlitToDDrawSurface: Failed to lock D3D9 surface"); } - surface->Unlock(0); + surface->Unlock(NULL); } else { Logger::warn("BlitToDDrawSurface: Failed to lock surface"); } } - inline DDCOLORKEY GetColorChannel(DWORD pixel, DWORD mask) { + inline DDCOLORKEY GetColorChannel(DWORD pixel, DWORD mask, bool withTolerance) { uint32_t shift = 0; DWORD cmask = mask; while ((cmask & 1) == 0) { @@ -874,36 +859,45 @@ namespace dxvk { cmask >>= 1; } - DWORD value = (pixel & mask) >> shift; - DWORD max = (1 << bits) - 1; - float cvalue = (float)value * 255.0 / (float)max; - float half = 255.0 / (2.0 * (float)max); - float minRange = cvalue - half; - float maxRange = cvalue + half; + const DWORD value = (pixel & mask) >> shift; + const DWORD max = (1 << bits) - 1; + const float cvalue = static_cast(value) * 255.0 / static_cast(max); DDCOLORKEY colorKey = { }; - colorKey.dwColorSpaceLowValue = std::floor(std::max(0.0f, floorf(minRange - 0.5))); - colorKey.dwColorSpaceHighValue = std::ceil(std::min(255.0f, floorf(maxRange + 0.5))); + if (unlikely(withTolerance)) { + const float half = 255.0 / (2.0 * static_cast(max)); + const float minRange = cvalue - half; + const float maxRange = cvalue + half; + colorKey.dwColorSpaceLowValue = std::floor(std::max(0.0f, floorf(minRange - 0.5))); + colorKey.dwColorSpaceHighValue = std::ceil(std::min(255.0f, floorf(maxRange + 0.5))); + } else { + colorKey.dwColorSpaceLowValue = cvalue; + colorKey.dwColorSpaceHighValue = cvalue; + } return colorKey; } - inline DDCOLORKEY ColorKeyToRGB(const DDPIXELFORMAT* fmt, DWORD colorKey) { + inline DDCOLORKEY ColorKeyToRGB(const DDPIXELFORMAT* fmt, DWORD colorKey, bool withTolerance) { DDCOLORKEY rgbColorKey = { }; if (unlikely(!(fmt->dwFlags & DDPF_RGB))) return rgbColorKey; - DDCOLORKEY r = GetColorChannel(colorKey, fmt->dwRBitMask); - DDCOLORKEY g = GetColorChannel(colorKey, fmt->dwGBitMask); - DDCOLORKEY b = GetColorChannel(colorKey, fmt->dwBBitMask); + DDCOLORKEY r = GetColorChannel(colorKey, fmt->dwRBitMask, withTolerance); + DDCOLORKEY g = GetColorChannel(colorKey, fmt->dwGBitMask, withTolerance); + DDCOLORKEY b = GetColorChannel(colorKey, fmt->dwBBitMask, withTolerance); + DDCOLORKEY a = (fmt->dwFlags & DDPF_ALPHAPIXELS) ? GetColorChannel(colorKey, fmt->dwRGBAlphaBitMask, + withTolerance) : DDCOLORKEY{255,255}; rgbColorKey.dwColorSpaceLowValue = r.dwColorSpaceLowValue | (g.dwColorSpaceLowValue << 8) | - (b.dwColorSpaceLowValue << 16); + (b.dwColorSpaceLowValue << 16)| + (a.dwColorSpaceLowValue << 24); rgbColorKey.dwColorSpaceHighValue = r.dwColorSpaceHighValue | (g.dwColorSpaceHighValue << 8) | - (b.dwColorSpaceHighValue << 16); + (b.dwColorSpaceHighValue << 16)| + (a.dwColorSpaceHighValue << 24); return rgbColorKey; } diff --git a/src/ddraw/ddraw_gamma.cpp b/src/ddraw/ddraw_gamma.cpp index ef3180e8773..72898cef523 100644 --- a/src/ddraw/ddraw_gamma.cpp +++ b/src/ddraw/ddraw_gamma.cpp @@ -8,7 +8,7 @@ namespace dxvk { DDrawCommonSurface* commonSurf, Com&& proxyGamma, IUnknown* pParent) - : DDrawWrappedObject(pParent, std::move(proxyGamma), nullptr) + : DDrawWrappedObject(pParent, std::move(proxyGamma)) , m_commonSurf ( commonSurf ) { Logger::debug("DDrawGammaControl: Created a new gamma control interface"); } @@ -46,6 +46,14 @@ namespace dxvk { Logger::debug("DDrawGammaControl::QueryInterface: Query for IDirectDrawSurface7"); return m_parent->QueryInterface(riid, ppvObject); } + if (unlikely(riid == __uuidof(IDirect3DTexture))) { + Logger::debug("DDrawGammaControl::QueryInterface: Query for IDirect3DTexture"); + return m_parent->QueryInterface(riid, ppvObject); + } + if (unlikely(riid == __uuidof(IDirect3DTexture2))) { + Logger::debug("DDrawGammaControl::QueryInterface: Query for IDirect3DTexture2"); + return m_parent->QueryInterface(riid, ppvObject); + } try { *ppvObject = ref(this->GetInterface(riid)); @@ -67,7 +75,7 @@ namespace dxvk { D3DCommonDevice* commonDevice = commonIntf->GetCommonD3DDevice(); // For proxied pesentation we need to rely on ddraw to handle gamma - if (likely(commonDevice != nullptr && !commonIntf->GetOptions()->forceProxiedPresent)) { + if (likely(commonDevice != nullptr)) { Logger::debug("DDrawGammaControl::GetGammaRamp: Getting gamma ramp via D3D9"); d3d9::IDirect3DDevice9* d3d9Device = commonDevice->GetD3D9Device(); @@ -96,7 +104,7 @@ namespace dxvk { if (likely(!commonIntf->GetOptions()->ignoreGammaRamp)) { D3DCommonDevice* commonDevice = commonIntf->GetCommonD3DDevice(); // For proxied pesentation we need to rely on ddraw to handle gamma - if (likely(commonDevice != nullptr && !commonIntf->GetOptions()->forceProxiedPresent)) { + if (likely(commonDevice != nullptr)) { Logger::debug("DDrawGammaControl::SetGammaRamp: Setting gamma ramp via D3D9"); d3d9::IDirect3DDevice9* d3d9Device = commonDevice->GetD3D9Device(); diff --git a/src/ddraw/ddraw_gamma.h b/src/ddraw/ddraw_gamma.h index 37ef63541d7..05832957197 100644 --- a/src/ddraw/ddraw_gamma.h +++ b/src/ddraw/ddraw_gamma.h @@ -7,7 +7,7 @@ namespace dxvk { - class DDrawGammaControl final : public DDrawWrappedObject { + class DDrawGammaControl final : public DDrawWrappedObject { public: diff --git a/src/ddraw/ddraw_options.h b/src/ddraw/ddraw_options.h index c9d7d1628e5..c583bbd2459 100644 --- a/src/ddraw/ddraw_options.h +++ b/src/ddraw/ddraw_options.h @@ -3,18 +3,18 @@ #include "ddraw_include.h" #include "../util/config/config.h" +#include "../util/util_math.h" namespace dxvk { enum class FSAAEmulation { Disabled, - Enabled, Forced }; - enum class D3DBackBufferGuard { + enum class D3DLegacyPresentGuard { + Auto, Disabled, - Enabled, Strict }; @@ -32,6 +32,9 @@ namespace dxvk { /// Advertise support for D16 bool supportD16; + /// Advertise support for any 32-bit bitmask depth foramts + bool support32BitDepth; + /// Replaces any use of D32 with D24X8 bool useD24X8forD32; @@ -44,8 +47,11 @@ namespace dxvk { /// Respect DISCARD only on DYNAMIC + WRITEONLY buffers bool forceLegacyDiscard; - /// Circumvents the texelFetch color key shader path - bool colorKeyCompatibility; + /// Process vertices on the CPU, instead of relaying to D3D9 + bool cpuProcessVertices; + + /// Correction offset for X/Y vertex position + float vertexOffset; /// Map all back buffers onto a single D3D9 back buffer bool forceSingleBackBuffer; @@ -53,20 +59,8 @@ namespace dxvk { /// Resize the back buffer size to screen size when needed bool backBufferResize; - /// Write back D3D9 back buffers during blits/locks on DDraw surfaces - bool backBufferWriteBack; - - /// Write back D3D9 depth stencils during blits/locks on DDraw surfaces - bool depthWriteBack; - - /// Upload or skip upload of depth stencils - bool uploadDepthStencils; - - /// Blits back to the proxied flippable surface and presents with DDraw - bool forceProxiedPresent; - - /// Workaround that uses blits instead of flips for presentation - bool forceBlitOnFlip; + /// Blits back to the proxied flippable surface and back again for presentation + bool forceLegacyPresent; /// Ignore any application set gamma ramp bool ignoreGammaRamp; @@ -77,17 +71,26 @@ namespace dxvk { /// Automatically generate all texture mip maps on the GPU bool autoGenMipMaps; - /// Enables all surface write-backs, but comes with performance penalties + /// Allow cross-device resource (surfaces/textures) use + bool deviceResourceSharing; + + /// Masks the color key values based on surface format color depth + bool colorKeyMasking; + + /// Uses a tolerance interval for color key inverval matching + bool colorKeyTolerance; + + /// Extends features and relaxes validations to enable apitrace debugging bool apitraceMode; - /// Uses supported MSAA up to 4x to emulate FSAA - FSAAEmulation emulateFSAA; + /// Enumerate with legacy/official implementation device names + bool legacyDeviceNames; - /// Determines how uploads for back buffer blits are handled - D3DBackBufferGuard backBufferGuard; + /// By default guards against legacy presents while inside of a scene + D3DLegacyPresentGuard legacyPresentGuard; - /// Max available memory override, shared with the D3D9 backend - uint32_t maxAvailableMemory; + /// Uses supported MSAA up to 4x to emulate FSAA + FSAAEmulation emulateFSAA; D3DOptions() {} @@ -97,39 +100,42 @@ namespace dxvk { this->forceSWVP = config.getOption ("ddraw.forceSWVP", false); this->supportR3G3B2 = config.getOption ("ddraw.supportR3G3B2", false); this->supportD16 = config.getOption ("ddraw.supportD16", true); + this->support32BitDepth = config.getOption ("ddraw.support32BitDepth", true); this->useD24X8forD32 = config.getOption ("ddraw.useD24X8forD32", false); this->mask8BitModes = config.getOption ("ddraw.mask8BitModes", false); this->forcePOW2Textures = config.getOption ("ddraw.forcePOW2Textures", false); this->forceLegacyDiscard = config.getOption ("ddraw.forceLegacyDiscard", false); - this->colorKeyCompatibility = config.getOption ("ddraw.colorKeyCompatibility", false); + this->cpuProcessVertices = config.getOption ("ddraw.cpuProcessVertices", true); + this->vertexOffset = config.getOption ("ddraw.vertexOffset", 0.0f); this->forceSingleBackBuffer = config.getOption ("ddraw.forceSingleBackBuffer", false); this->backBufferResize = config.getOption ("ddraw.backBufferResize", true); - this->backBufferWriteBack = config.getOption ("ddraw.backBufferWriteBack", false); - this->depthWriteBack = config.getOption ("ddraw.depthWriteBack", false); - this->uploadDepthStencils = config.getOption ("ddraw.uploadDepthStencils", true); - this->forceProxiedPresent = config.getOption ("ddraw.forceProxiedPresent", false); - this->forceBlitOnFlip = config.getOption ("ddraw.forceBlitOnFlip", false); + this->forceLegacyPresent = config.getOption ("ddraw.forceLegacyPresent", false); this->ignoreGammaRamp = config.getOption ("ddraw.ignoreGammaRamp", false); this->ignoreExclusiveMode = config.getOption ("ddraw.ignoreExclusiveMode", false); this->autoGenMipMaps = config.getOption ("ddraw.autoGenMipMaps", false); + this->deviceResourceSharing = config.getOption ("ddraw.deviceResourceSharing", false); + this->colorKeyMasking = config.getOption ("ddraw.colorKeyMasking", false); + this->colorKeyTolerance = config.getOption ("ddraw.colorKeyTolerance", false); + this->legacyDeviceNames = config.getOption ("ddraw.legacyDeviceNames", false); this->apitraceMode = config.getOption ("ddraw.apitraceMode", false); - std::string emulateFSAAStr = Config::toLower(config.getOption("ddraw.emulateFSAA", "auto")); - if (emulateFSAAStr == "true") { - this->emulateFSAA = FSAAEmulation::Enabled; - } else if (emulateFSAAStr == "forced") { - this->emulateFSAA = FSAAEmulation::Forced; + // Clamp the vertex offset in the (sensible) -1.0f/1.0f range + this->vertexOffset = dxvk::fclamp(this->vertexOffset, -1.0f, 1.0f); + + std::string legacyPresentGuardStr = Config::toLower(config.getOption("ddraw.legacyPresentGuard", "auto")); + if (legacyPresentGuardStr == "strict") { + this->legacyPresentGuard = D3DLegacyPresentGuard::Strict; + } else if (legacyPresentGuardStr == "disabled") { + this->legacyPresentGuard = D3DLegacyPresentGuard::Disabled; } else { - this->emulateFSAA = FSAAEmulation::Disabled; + this->legacyPresentGuard = D3DLegacyPresentGuard::Auto; } - std::string backBufferGuardStr = Config::toLower(config.getOption("ddraw.backBufferGuard", "auto")); - if (backBufferGuardStr == "strict") { - this->backBufferGuard = D3DBackBufferGuard::Strict; - } else if (backBufferGuardStr == "disabled") { - this->backBufferGuard = D3DBackBufferGuard::Disabled; + std::string emulateFSAAStr = Config::toLower(config.getOption("ddraw.emulateFSAA", "auto")); + if (emulateFSAAStr == "forced") { + this->emulateFSAA = FSAAEmulation::Forced; } else { - this->backBufferGuard = D3DBackBufferGuard::Enabled; + this->emulateFSAA = FSAAEmulation::Disabled; } } diff --git a/src/ddraw/ddraw_palette.cpp b/src/ddraw/ddraw_palette.cpp index ed3a1ca0e6d..3579b92aa1a 100644 --- a/src/ddraw/ddraw_palette.cpp +++ b/src/ddraw/ddraw_palette.cpp @@ -9,7 +9,7 @@ namespace dxvk { DDrawPalette::DDrawPalette( Com&& paletteProxy, IUnknown* pParent) - : DDrawWrappedObject(pParent, std::move(paletteProxy), nullptr) { + : DDrawWrappedObject(pParent, std::move(paletteProxy)) { if (m_parent != nullptr) m_parent->AddRef(); @@ -28,6 +28,24 @@ namespace dxvk { Logger::debug(str::format("DDrawPalette: Palette nr. [[1-", m_paletteCount, "]] bites the dust")); } + HRESULT STDMETHODCALLTYPE DDrawPalette::QueryInterface(REFIID riid, void** ppvObject) { + Logger::debug(">>> DDrawPalette::QueryInterface"); + + if (unlikely(ppvObject == nullptr)) + return E_POINTER; + + InitReturnPtr(ppvObject); + + try { + *ppvObject = ref(this->GetInterface(riid)); + return S_OK; + } catch (const DxvkError& e) { + Logger::warn(e.message()); + Logger::warn(str::format(riid)); + return E_NOINTERFACE; + } + } + // Docs state: "Because the DirectDrawPalette object is initialized when // it is created, this method always returns DDERR_ALREADYINITIALIZED." HRESULT STDMETHODCALLTYPE DDrawPalette::Initialize(LPDIRECTDRAW lpDD, DWORD dwFlags, LPPALETTEENTRY lpDDColorTable) { diff --git a/src/ddraw/ddraw_palette.h b/src/ddraw/ddraw_palette.h index 86a5118205c..4a598b34959 100644 --- a/src/ddraw/ddraw_palette.h +++ b/src/ddraw/ddraw_palette.h @@ -7,7 +7,7 @@ namespace dxvk { class DDrawCommonSurface; - class DDrawPalette final : public DDrawWrappedObject { + class DDrawPalette final : public DDrawWrappedObject { public: @@ -17,6 +17,8 @@ namespace dxvk { ~DDrawPalette(); + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject); + HRESULT STDMETHODCALLTYPE Initialize(LPDIRECTDRAW lpDD, DWORD dwFlags, LPPALETTEENTRY lpDDColorTable); HRESULT STDMETHODCALLTYPE GetCaps(LPDWORD lpdwCaps); diff --git a/src/ddraw/ddraw_util.h b/src/ddraw/ddraw_util.h index d151cd8733a..76e25ef280c 100644 --- a/src/ddraw/ddraw_util.h +++ b/src/ddraw/ddraw_util.h @@ -22,6 +22,13 @@ namespace dxvk { DWORD dwFlags = 0; }; + inline bool IsValidDDrawCapsSize(DWORD size) { + return size == sizeof(DDCAPS_DX7) + || size == sizeof(DDCAPS_DX6) + || size == sizeof(DDCAPS_DX5) + || size == sizeof(DDCAPS_DX3); + } + // MS, in their infinite wisdom, decided to have 3 distinct versions // of D3DDEVICEDESC, the first shipped with D3D2/3, the second with D3D5, // and the third (which is what we have in modern headers) with D3D6. @@ -464,8 +471,8 @@ namespace dxvk { D3DLIGHTINGCAPS lightingCaps; lightingCaps.dwSize = sizeof(D3DLIGHTINGCAPS); lightingCaps.dwCaps = D3DLIGHTCAPS_DIRECTIONAL - // | D3DLIGHTCAPS_GLSPOT // D3D3 specific - // | D3DLIGHTCAPS_PARALLELPOINT // Not supported by D3D9 + // | D3DLIGHTCAPS_GLSPOT + | D3DLIGHTCAPS_PARALLELPOINT // Not supported by D3D9 | D3DLIGHTCAPS_POINT | D3DLIGHTCAPS_SPOT; lightingCaps.dwLightingModel = D3DLIGHTINGMODEL_RGB; @@ -643,7 +650,8 @@ namespace dxvk { D3DLIGHTINGCAPS lightingCaps; lightingCaps.dwSize = sizeof(D3DLIGHTINGCAPS); lightingCaps.dwCaps = D3DLIGHTCAPS_DIRECTIONAL - // | D3DLIGHTCAPS_PARALLELPOINT // Not supported by D3D9 + // | D3DLIGHTCAPS_GLSPOT + | D3DLIGHTCAPS_PARALLELPOINT // Not supported by D3D9 | D3DLIGHTCAPS_POINT | D3DLIGHTCAPS_SPOT; lightingCaps.dwLightingModel = D3DLIGHTINGMODEL_RGB; @@ -850,7 +858,7 @@ namespace dxvk { lightingCaps.dwSize = sizeof(D3DLIGHTINGCAPS); lightingCaps.dwCaps = D3DLIGHTCAPS_DIRECTIONAL // | D3DLIGHTCAPS_GLSPOT - // | D3DLIGHTCAPS_PARALLELPOINT // Not supported by D3D9 + | D3DLIGHTCAPS_PARALLELPOINT // Not supported by D3D9 | D3DLIGHTCAPS_POINT | D3DLIGHTCAPS_SPOT; lightingCaps.dwLightingModel = D3DLIGHTINGMODEL_RGB; diff --git a/src/ddraw/ddraw_wrapped_object.h b/src/ddraw/ddraw_wrapped_object.h index cd2ded62ddb..a378c63e09e 100644 --- a/src/ddraw/ddraw_wrapped_object.h +++ b/src/ddraw/ddraw_wrapped_object.h @@ -4,19 +4,17 @@ namespace dxvk { - template + template class DDrawWrappedObject : public ComObjectClamp { public: using Parent = ParentType; using DDraw = DDrawType; - using D3D9 = D3D9Type; - DDrawWrappedObject(Parent* parent, Com&& proxiedIntf, Com&& object) + DDrawWrappedObject(Parent* parent, Com&& proxiedIntf) : m_parent ( parent ) - , m_proxy ( std::move(proxiedIntf) ) - , m_d3d9 ( std::move(object) ) { + , m_proxy ( std::move(proxiedIntf) ) { } void UpdateParent(Parent* parent) { @@ -31,31 +29,6 @@ namespace dxvk { return m_proxy.ptr(); } - D3D9* GetD3D9() const { - return m_d3d9.ptr(); - } - - void SetD3D9(Com&& object) { - m_d3d9 = std::move(object); - } - - bool IsInitialized() const { - return m_d3d9 != nullptr; - } - - // For cases where the object may be null. - static D3D9* GetD3D9Nullable(DDrawWrappedObject* self) { - if (unlikely(self == NULL)) { - return NULL; - } - return self->m_d3d9.ptr(); - } - - template - static D3D9* GetD3D9Nullable(Com& self) { - return GetD3D9Nullable(self.ptr()); - } - IUnknown* GetInterface(REFIID riid) { if (riid == __uuidof(IUnknown)) return this; @@ -65,30 +38,11 @@ namespace dxvk { throw DxvkError("DDrawWrappedObject::QueryInterface: Unknown interface query"); } - HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject) { - Logger::debug(">>> DDrawWrappedObject::QueryInterface"); - - if (unlikely(ppvObject == nullptr)) - return E_POINTER; - - InitReturnPtr(ppvObject); - - try { - *ppvObject = ref(this->GetInterface(riid)); - return S_OK; - } catch (const DxvkError& e) { - Logger::warn(e.message()); - Logger::warn(str::format(riid)); - return E_NOINTERFACE; - } - } - protected: Parent* m_parent = nullptr; Com m_proxy; - Com m_d3d9; }; diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index f30f5d62669..8a60d348de5 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -1352,11 +1352,18 @@ namespace dxvk { { "d3d9.maxFrameRate", "240" }, { "ddraw.forceSWVP", "True" }, }} }, + /* Arx Fatalis * + * Works around flickering introduced by GDI * + * blits, mainly during loading screens */ + { R"(\\arx\.exe$)", {{ + { "ddraw.forceLegacyPresent", "True" }, + { "ddraw.legacyPresentGuard", "Strict" }, + }} }, /* Sacrifice - Prevents hitching on asset * * loading and fixes broken AI above 60 FPS. * * Also support 32-bit modes, which need D32. */ { R"(\\Sacrifice\.exe$)", {{ - { "d3d9.cachedWriteOnlyBuffers", "True" }, + { "d3d9.cachedDynamicBuffers", "True" }, { "d3d9.maxFrameRate", "60" }, { "ddraw.useD24X8forD32", "True" }, }} }, @@ -1364,13 +1371,13 @@ namespace dxvk { * and black screen prevention on startup, * * also capped to prevent scroll speed issues */ { R"(\\bitaw\.exe$)", {{ - { "d3d9.cachedWriteOnlyBuffers", "True" }, + { "d3d9.cachedDynamicBuffers", "True" }, { "d3d9.maxFrameRate", "60" }, - { "ddraw.backBufferGuard", "Strict" }, + { "ddraw.forceLegacyPresent", "True" }, }} }, /* Startopia */ { R"(\\startopia\.exe$)", {{ - { "ddraw.forceProxiedPresent", "True" }, + { "ddraw.forceLegacyPresent", "True" }, }} }, /* Escape from Monkey Island * * Fixes broken physics, and flip logic */ @@ -1379,23 +1386,21 @@ namespace dxvk { { "ddraw.forceSingleBackBuffer", "True" }, }} }, /* Gothic 1 - broken physics and * - * flickering on the loading screen */ + * flickering during video playback */ { R"(\\GOTHIC(Mod)?\.EXE$)", {{ { "d3d9.maxFrameRate", "60" }, - { "ddraw.forceSingleBackBuffer", "True" }, + { "ddraw.forceLegacyPresent", "True" }, }} }, /* Gothic 2 / Night of the Raven * * Broken physics and sliding speed, and * - * flickering on the loading screen */ + * flickering during video playback */ { R"(\\Gothic2\.exe)", {{ { "d3d9.maxFrameRate", "60" }, - { "ddraw.forceSingleBackBuffer", "True" }, + { "ddraw.forceLegacyPresent", "True" }, }} }, - /* Blade of Darkness - broken physics, main * - * menu transitions, animations and GUI */ + /* Blade of Darkness - broken physics */ { R"(\\Blade\.exe$)", {{ { "d3d9.maxFrameRate", "60" }, - { "ddraw.forceSingleBackBuffer", "True" }, }} }, /* Hogs of War - Fixes animation speed */ { R"(\\warhogs_\.exe$)", {{ @@ -1404,41 +1409,50 @@ namespace dxvk { /* Parkan: Iron Strategy - Performance */ { R"(\\iron_3d\.exe$)", {{ { "d3d9.maxFrameRate", "60" }, - { "d3d9.cachedWriteOnlyBuffers", "True" }, + { "d3d9.cachedDynamicBuffers", "True" }, }} }, /* Dungeon Siege */ { R"(\\DungeonSiege\.exe$)", {{ - { "ddraw.forceProxiedPresent", "True" }, - { "ddraw.backBufferWriteBack", "True" }, + { "ddraw.forceLegacyPresent", "True" }, }} }, - /* Empire Earth / Art of Conquest */ + /* Empire Earth / Art of Conquest * + * Works around in-game flickering */ { R"(\\(Empire Earth|EE-AOC)\.exe$)", {{ - { "ddraw.forceProxiedPresent", "True" }, + { "ddraw.forceLegacyPresent", "True" }, + { "ddraw.legacyPresentGuard", "Strict" }, }} }, /* Etherlords * * Needs R3G3B2 support for text rendering */ { R"(\\Etherlords\.exe$)", {{ - { "ddraw.forceProxiedPresent", "True" }, + { "ddraw.forceLegacyPresent", "True" }, { "ddraw.supportR3G3B2", "True" }, }} }, /* Etherlords 2 * * Needs R3G3B2 support for text rendering */ { R"(\\Etherlords2\.exe$)", {{ - { "ddraw.forceProxiedPresent", "True" }, + { "ddraw.forceLegacyPresent", "True" }, { "ddraw.supportR3G3B2", "True" }, }} }, /* Evil Islands */ { R"(\\Evil Islands\\game\.exe$)", {{ - { "ddraw.forceProxiedPresent", "True" }, + { "ddraw.forceLegacyPresent", "True" }, }} }, - /* Star Trek: Armada */ + /* Star Trek: Armada * + * Works around in-game flickering */ { R"(\\Armada\.exe$)", {{ - { "ddraw.forceProxiedPresent", "True" }, + { "ddraw.forceLegacyPresent", "True" }, + { "ddraw.legacyPresentGuard", "Strict" }, }} }, /* SCP - Containment Breach * * Crashes without multithreading protection */ { R"(\\SCP - Containment Breach\.exe$)", {{ - { "ddraw.forceProxiedPresent", "True" }, + { "ddraw.forceLegacyPresent", "True" }, + { "ddraw.forceMultiThreaded", "True" }, + }} }, + /* SCP - Nine-Tailed Fox * + * Same engine as Containment Breach */ + { R"(\\SCP Nine-Tailed Fox\.exe$)", {{ + { "ddraw.forceLegacyPresent", "True" }, { "ddraw.forceMultiThreaded", "True" }, }} }, /* Unreal * @@ -1478,10 +1492,12 @@ namespace dxvk { { "ddraw.autoGenMipMaps", "True" }, }} }, /* The Wheel of Time * - * Fixes missing mip map uploads and physics */ + * Fixes missing mip map uploads, physics * + * and Z-fighting on distant objects */ { R"(\\WoT\.exe$)", {{ { "d3d9.maxFrameRate", "60" }, { "ddraw.autoGenMipMaps", "True" }, + { "ddraw.supportD16", "False" }, }} }, /* Harry Potter and the Chamber of Secrets * * Fixes missing mip map uploads and physics */ @@ -1496,7 +1512,7 @@ namespace dxvk { { "ddraw.autoGenMipMaps", "True" }, }} }, /* Messiah - Fixes missing mip map uploads * - * and cutscene playback / physics */ + * and broken cutscene playback / physics */ { R"(\\MessiahD3D\.exe$)", {{ { "d3d9.maxFrameRate", "60" }, { "ddraw.autoGenMipMaps", "True" }, @@ -1507,7 +1523,7 @@ namespace dxvk { }} }, /* 3DMark2000 - Performance */ { R"(\\3DMark2000\.exe$)", {{ - { "d3d9.cachedWriteOnlyBuffers", "True" }, + { "d3d9.cachedDynamicBuffers", "True" }, }} }, /* Carmageddon TDR 2000 - Main menu speed */ { R"(\\TDR2000\.exe$)", {{ @@ -1527,8 +1543,7 @@ namespace dxvk { { R"(\\Screamer4x4_d3d\.exe$)", {{ { "d3d9.maxFrameRate", "60" }, }} }, - /* (The) Summoner - Accelerated game speed * - * and fix for nonsensical viewport values */ + /* (The) Summoner - Accelerated game speed */ { R"(\\Sum\.exe$)", {{ { "d3d9.maxFrameRate", "60" }, }} }, @@ -1541,76 +1556,57 @@ namespace dxvk { { R"(\\Giants\.exe$)", {{ { "d3d9.maxFrameRate", "60" }, }} }, - /* The Mystery of the Druids */ - { R"(\\edd\.exe$)", {{ - { "ddraw.forceProxiedPresent", "True" }, - }} }, /* Silent Hunter II - Broken input handling */ { R"(\\Silent Hunter.*\\(Sim|Shell(1)?)\.exe$)", {{ { "d3d9.maxFrameRate", "60" }, }} }, /* Enemy Engaged: Comanche vs Hokum */ { R"(\\cohokum\.exe$)", {{ - { "ddraw.forceProxiedPresent", "True" }, + { "ddraw.forceLegacyPresent", "True" }, }} }, - /* The Nations (Gold Edition) */ + /* The Nations (Gold Edition) * + * Works around cursor flickering */ { R"(\\The Nations.*\\bin\\game\.exe$)", {{ - { "ddraw.forceProxiedPresent", "True" }, + { "ddraw.forceLegacyPresent", "True" }, + { "ddraw.legacyPresentGuard", "Strict" }, }} }, /* Need for Speed: Porsche Unleashed * * Fixes missing mip maps on car models */ { R"(\\(Porsche|nfs5)\.exe$)", {{ { "ddraw.autoGenMipMaps", "True" }, - { "ddraw.backBufferWriteBack", "True" }, - { "ddraw.backBufferGuard", "Disabled" }, - }} }, - /* Soulbringer - Uses legacy ddraw interfaces * - * and has broken rendering with direct * - * buffer mapping on T&L devices */ - { R"(\\SoulbringeVC(noeax)?\.exe$)", {{ - { "d3d9.allowDirectBufferMapping", "False" }, - { "ddraw.forceProxiedPresent", "True" }, }} }, /* Star Trek: Deep Space Nine - The Fallen * * Fixes missing mip map uploads */ { R"(\\DS9\.exe$)", {{ { "ddraw.autoGenMipMaps", "True" }, }} }, - /* Sacred - Fixes transition artifacting */ - { R"(\\Sacred\.exe$)", {{ - { "ddraw.forceSingleBackBuffer", "True" }, - }} }, /* StarLancer */ { R"(\\Lancer\.exe$)", {{ - { "ddraw.forceProxiedPresent", "True" }, + { "ddraw.forceLegacyPresent", "True" }, }} }, - /* The Settlers IV */ - { R"(\\S4_Main\.exe$)", {{ - { "ddraw.forceProxiedPresent", "True" }, + /* The Settlers IV * + * Works around the game's concurrent use * + * of two D3D devices for rendering */ + { R"(\\S4(_Main?)\.exe$)", {{ + { "ddraw.forceLegacyPresent", "True" }, + { "ddraw.deviceResourceSharing", "True" }, }} }, /* Spider-Man (2001) - broken cutscenes */ { R"(\\SpideyPC\.exe$)", {{ { "d3d9.maxFrameRate", "30" }, }} }, - /* Wizards & Warriors */ - { R"(\\deep6\.exe$)", {{ - { "ddraw.forceProxiedPresent", "True" }, - }} }, /* Age of Wonders: Shadow Magic */ { R"(\\AoWSM(Compat)?\.exe$)", {{ - { "ddraw.forceProxiedPresent", "True" }, + { "ddraw.forceLegacyPresent", "True" }, }} }, /* Age of Wonders II: The Wizard's Throne */ { R"(\\AoW2\.exe$)", {{ - { "ddraw.forceProxiedPresent", "True" }, - }} }, - /* Hard Truck 2: King of the Road */ - { R"(\\king\.exe$)", {{ - { "ddraw.forceProxiedPresent", "True" }, + { "ddraw.forceLegacyPresent", "True" }, }} }, - /* Anno 1503 */ + /* Anno 1503 * + * Fixes missing loading screens */ { R"(\\1503Startup\.exe$)", {{ - { "ddraw.forceProxiedPresent", "True" }, + { "ddraw.forceLegacyPresent", "True" }, }} }, /* Knight Rider: The Game * * Fixes in-game vehicle environment maps * @@ -1618,52 +1614,46 @@ namespace dxvk { { R"(\\(Knight Rider|KR( Demo)?)\.exe$)", {{ { "ddraw.supportD16", "False" }, { "ddraw.forceSingleBackBuffer", "True" }, - { "ddraw.backBufferWriteBack", "True" }, - }} }, - /* Knight Rider: The Game 2 * - * Fixes in-game vehicle environment maps */ - { R"(\\KR2\.exe$)", {{ - { "ddraw.backBufferWriteBack", "True" }, - }} }, - /* Real Myst * - * Fixes menu and save game backgrounds */ - { R"(\\RealMYST\.exe$)", {{ - { "ddraw.backBufferWriteBack", "True" }, - }} }, - /* Total Club Manager 2003 * - * Fixes in-game blur transition effects */ - { R"(\\TCM2003\.exe$)", {{ - { "ddraw.backBufferWriteBack", "True" }, - }} }, - /* Sim City 4 * - * Fixes broken overlays and 3D elements */ - { R"(\\SimCity 4\.exe$)", {{ - { "ddraw.depthWriteBack", "True" }, - { "ddraw.backBufferWriteBack", "True" }, }} }, /* Radeon's Ark (ATI Radeon 7000 Tech Demo) * * Needs custom vendor ID to run on anything * * outside AMD, and a frame cap to not freeze * - * or slow down upwards of 500 FPS. Legacy * + * or slow down upwards of 480 FPS. Legacy * * DISCARD handling fixes missing geometry. */ { R"(\\Radeon'sArk1.3\.exe$)", {{ { "d3d9.customVendorId", "1002" }, - { "d3d9.maxFrameRate", "500" }, + { "d3d9.maxFrameRate", "480" }, { "ddraw.forceLegacyDiscard", "True" }, }} }, - /* Tribes 2 - fixes rendering and performance */ + /* Tribes 2 - Fix odd buffer access patterns */ { R"(\\Tribes2\.exe$)", {{ { "ddraw.ignoreExclusiveMode", "True" }, { "ddraw.forceSWVP", "True" }, }} }, /* Space Empires V */ { R"(\\SE5\.exe$)", {{ - { "ddraw.forceProxiedPresent", "True" }, + { "ddraw.forceLegacyPresent", "True" }, + }} }, + /* FIFA 2001 */ + { R"(\\fifa2001\.exe$)", {{ + { "ddraw.forceLegacyPresent", "True" }, + }} }, + /* Nerf ArenaBlast * + * Fixes physics engine speed up at high FPS * + * and Z-fighting issues on some objects */ + { R"(\\nerf\.exe$)", {{ + { "d3d9.maxFrameRate", "60" }, + { "ddraw.autoGenMipMaps", "True" }, + { "ddraw.supportD16", "False" }, + }} }, + /* Space Pirates and Zombies */ + { R"(\\SpazGame\.exe$)", {{ + { "ddraw.ignoreExclusiveMode", "True" }, }} }, - /* Will Rock * - * Fixes missing save game screenshots */ - { R"(\\WillRock\.exe$)", {{ - { "ddraw.backBufferWriteBack", "True" }, + /* Hard Truck 2: King of the Road * + * Fixes top bar speed indicator flickering */ + { R"(\\king\.exe$)", {{ + { "ddraw.forceLegacyPresent", "True" }, }} }, /**********************************************/ @@ -1671,41 +1661,20 @@ namespace dxvk { /**********************************************/ /* Drakan: Order of the Flame * - * Fixes physics glitches at over 60 FPS and * - * missing pause / save game backgrounds. We * - * also prevent depth stencil uploads to fix * - * performance loss when lens flares are * - * enabled, because that causes depth stencil * - * locks for each dynamic light source */ + * Fixes physics glitches at over 60 FPS */ { R"(\\Drakan\.exe$)", {{ { "d3d9.maxFrameRate", "60" }, - { "ddraw.backBufferWriteBack", "True" }, - { "ddraw.uploadDepthStencils", "False" }, }} }, - /* O.R.B: Off-World Resource Base * - * Uses windowed present mode in full-screen */ + /* O.R.B: Off-World Resource Base */ { R"(\\orb\.exe$)", {{ { "ddraw.ignoreExclusiveMode", "True" }, }} }, - /* Might and Magic VII: For Blood and Honor */ - { R"(\\MM7(-Rel)?\.exe$)", {{ - { "ddraw.forceProxiedPresent", "True" }, - }} }, - /* Might and Magic VIII: Day of the Destroyer */ - { R"(\\MM8(-Rel)?\.exe$)", {{ - { "ddraw.forceProxiedPresent", "True" }, - }} }, /* Omikron: The Nomad Soul * - * Lights and other effects break over 30 FPS.* - * The pause menu and dialogue subtitles are * - * missing without proxy presentation. */ + * Lights and other effects break over 30 FPS */ { R"(\\Omikron.*\\Runtime\.exe$)", {{ { "d3d9.maxFrameRate", "30" }, - { "ddraw.forceProxiedPresent", "True" }, }} }, - /* Urban Chaos * - * Uses windowed present mode in full-screen * - * and mixes up VP MinZ / MaxZ values. */ + /* Urban Chaos */ { R"(\\fallen\.exe$)", {{ { "ddraw.ignoreExclusiveMode", "True" }, { "ddraw.forceSingleBackBuffer", "True" }, @@ -1713,24 +1682,17 @@ namespace dxvk { /* Redline - Fixes missing weapon mip maps */ { R"(\\Redline\.exe$)", {{ { "ddraw.autoGenMipMaps", "True" }, - { "ddraw.forceProxiedPresent", "True" }, }} }, /* 3DMark 99 (Max) - Enables VSync by default * * (probably due to hardware and/or driver * - * limitations of the time), and needs mixed * - * SWVP for performance reasons */ + * limitations at the time) + performance */ { R"(\\3dmark\.exe$)", {{ { "d3d9.presentInterval", "0" }, { "d3d9.allowDirectBufferMapping", "False" }, }} }, - /* Hidden & Dangerous (: Action Pack) * - * Prevents crashing on startup */ + /* Hidden & Dangerous (: Action Pack) */ { R"(\\h&d\.exe$)", {{ - { "d3d9.allowDirectBufferMapping", "False" }, - }} }, - /* Dungeon Keeper 2 */ - { R"(\\DKII(-DX)?\.exe$)", {{ - { "ddraw.forceProxiedPresent", "True" }, + { "ddraw.forceLegacyPresent", "True" }, }} }, /* Star Wars: Rogue Squadron 3D */ { R"(\\Rogue Squadron\.exe$)", {{ @@ -1738,49 +1700,47 @@ namespace dxvk { }} }, /* Blood II: The Chosen */ { R"(\\Blood.*\\Client\.exe$)", {{ - { "ddraw.forceProxiedPresent", "True" }, + { "d3d9.maxFrameRate", "60" }, }} }, /* Shogo: Mobile Armor Division */ { R"(\\Shogo.*\\Client\.exe$)", {{ - { "ddraw.forceProxiedPresent", "True" }, + { "d3d9.maxFrameRate", "60" }, + { "ddraw.forceSingleBackBuffer", "True" }, }} }, /* KISS: Psycho Circus - The Nightmare Child */ { R"(\\(KISS.*|Psycho.*)\\client\.exe$)", {{ - { "ddraw.forceProxiedPresent", "True" }, + { "d3d9.maxFrameRate", "60" }, }} }, /* Enemy Engaged: Apache vs Havoc */ { R"(\\aphavoc\.exe$)", {{ - { "ddraw.forceProxiedPresent", "True" }, + { "ddraw.forceLegacyPresent", "True" }, }} }, - /* Star Trek: Starfleet Command */ + /* Star Trek: Starfleet Command * + * Works around in-game flickering */ { R"(\\Starfleet\.exe$)", {{ { "ddraw.forceMultiThreaded", "True" }, - { "ddraw.forceProxiedPresent", "True" }, + { "ddraw.forceLegacyPresent", "True" }, + { "ddraw.legacyPresentGuard", "Strict" }, }} }, - /* F/A-18E Super Hornet */ - { R"(\\F18\.exe$)", {{ - { "ddraw.forceProxiedPresent", "True" }, + /* Expendable */ + { R"(\\Expendable\\go_start\.exe$)", {{ + { "ddraw.support32BitDepth", "False" }, }} }, /* Total Annihilation: Kingdoms */ { R"(\\KINGDOMS\.icd$)", {{ { "ddraw.forcePOW2Textures", "True" }, }} }, - /* Star Wars Episode I: Racer */ - { R"(\\SWEP1RCR\.exe$)", {{ - { "ddraw.depthWriteBack", "True" }, - }} }, - /* Gorky 17 - Fixes crash on game start */ + /* Gorky 17 */ { R"(\\gorky17\.exe$)", {{ - { "ddraw.depthWriteBack", "True" }, - { "ddraw.forceProxiedPresent", "True" }, + { "ddraw.forceLegacyPresent", "True" }, }} }, - /* Revenant */ + /* Revenant * + * Fixes blurry texture filtering and * + * too dark in-game gamma/brightness */ { R"(\\Revenant\.exe$)", {{ - { "ddraw.forceProxiedPresent", "True" }, - }} }, - /* Empire of the Ants */ - { R"(\\Empire of the Ants\\Game\.exe$)", {{ - { "ddraw.forceProxiedPresent", "True" }, + { "ddraw.forceLegacyPresent", "True" }, + { "ddraw.forcePOW2Textures", "True" }, + { "ddraw.ignoreGammaRamp", "True "}, }} }, /* Slave Zero - will not start in 32-bit * * color mode without D32 support */ @@ -1789,8 +1749,7 @@ namespace dxvk { }} }, /* Nocturne */ { R"(\\nocturne\.exe$)", {{ - { "ddraw.depthWriteBack", "True" }, - { "ddraw.forceProxiedPresent", "True" }, + { "d3d9.maxFrameRate", "60" }, }} }, /* Arabian Nights * * Fixes flickering during level load */ @@ -1800,25 +1759,86 @@ namespace dxvk { /* Metal Fatigue * * Fixes unit and building transparency */ { R"(\\MFatigue\.exe$)", {{ - { "ddraw.forceProxiedPresent", "True" }, + { "ddraw.forceLegacyPresent", "True" }, }} }, /* Simon The Sorcerer 3D * * Fixes Z-fighting artifacts with D16 */ { R"(\\Simon3D\.exe$)", {{ { "ddraw.supportD16", "False" }, }} }, - /* Crusaders of Might and Magic */ - { R"(\\crusaders\.exe$)", {{ - { "ddraw.backBufferWriteBack", "True" }, - { "ddraw.backBufferGuard", "Disabled" }, - }} }, /* DethKarz - fixes crash post intro playback */ { R"(\\Dethkarz\.exe$)", {{ { "ddraw.mask8BitModes", "True" }, }} }, + /* Tomb Raider: The Last Revelation */ + { R"(\\tomb4\.exe$)", {{ + { "ddraw.forceLegacyPresent", "True" }, + }} }, /* Tomb Raider Chronicles */ { R"(\\PCTomb5\.exe$)", {{ - { "ddraw.backBufferWriteBack", "True" }, + { "ddraw.forceLegacyPresent", "True" }, + }} }, + /* Prince of Persia 3D */ + { R"(\\POP3D(Demo)?\.exe$)", {{ + { "d3d9.cachedDynamicBuffers", "True" }, + }} }, + /* Jurassic Park: Trespasser */ + { R"(\\trespass\.exe$)", {{ + { "ddraw.forceLegacyPresent", "True" }, + }} }, + /* Wizards & Warriors */ + { R"(\\deep6\.exe$)", {{ + { "d3d9.maxFrameRate", "60" }, + }} }, + /* Star Wars: Episode I - The Phantom Menace */ + { R"(\\WMAIN\.EXE$)", {{ + { "ddraw.forceSingleBackBuffer", "True" }, + }} }, + /* Combat Mission: Afrika Korps * + * Fixes white backgrounds on text */ + { R"(\\CM Afrika Korps\.exe$)", {{ + { "ddraw.colorKeyMasking", "True" }, + }} }, + /* Combat Mission: Barbarossa to Berlin * + * Fixes white backgrounds on text */ + { R"(\\Barbarossa to Berlin\.exe$)", {{ + { "ddraw.colorKeyMasking", "True" }, + }} }, + /* Combat Mission: Beyond Overlord * + * Fixes white backgrounds on text */ + { R"(\\Combat Mission\.exe$)", {{ + { "ddraw.colorKeyMasking", "True" }, + }} }, + /* X: Beyond the Frontier */ + { R"(\\X\.exe$)", {{ + { "ddraw.forceSingleBackBuffer", "True" }, + }} }, + /* X: Tension */ + { R"(\\X-TENSION\.exe$)", {{ + { "ddraw.forceSingleBackBuffer", "True" }, + }} }, + /* Dungeon Keeper 2 * + * Fixes missing HW acceleration option */ + { R"(\\DKII(-DX)?\.exe$)", {{ + { "ddraw.legacyDeviceNames", "True" }, + }} }, + /* Earthworm Jim 3D */ + { R"(\\EarthwormJim3D\.exe$)", {{ + { "ddraw.forceSingleBackBuffer", "True" }, + }} }, + /* Homeworld: Cataclysm (Emergence) */ + { R"(\\cataclysm\.exe$)", {{ + { "ddraw.forceSingleBackBuffer", "True" }, + }} }, + /* Might and Magic VII: For Blood and Honor * + * Fixes missing loading screens */ + { R"(\\MM7(-Rel)?\.exe$)", {{ + { "ddraw.forceLegacyPresent", "True" }, + }} }, + /* Might and Magic VIII: Day of the Destroyer * + * Fixes missing loading screens */ + { R"(\\MM8(-Rel)?\.exe$)", {{ + { "ddraw.forceLegacyPresent", "True" }, }} }, /**********************************************/ @@ -1827,25 +1847,23 @@ namespace dxvk { /* Descent: FreeSpace - The Great War */ { R"(\\FS\.exe$)", {{ - { "ddraw.forceProxiedPresent", "True" }, + { "ddraw.forceLegacyPresent", "True" }, }} }, - /* Populous: The Beginning */ - { R"(\\D3DPopTB(UW)?\.exe$)", {{ - { "ddraw.forceProxiedPresent", "True" }, + /* Empire of the Ants */ + { R"(\\Empire of the Ants*\\Game\.exe$)", {{ + { "ddraw.forceLegacyPresent", "True" }, + { "ddraw.legacyPresentGuard", "Strict" }, }} }, /* N.I.C.E 2 - Fixes main menu flickering */ { R"(\\n2_(std|arc)\.exe$)", {{ { "d3d9.maxFrameRate", "60" }, { "ddraw.forceSingleBackBuffer", "True" }, }} }, - /* Twisted Metal 2 */ - { R"(\\tm2\.exe$)", {{ - { "ddraw.forceProxiedPresent", "True" }, - }} }, /* Mobil 1 Rally Championship * * Crashes on certain tracks above 30 FPS */ { R"(\\Ral\.exe$)", {{ { "d3d9.maxFrameRate", "30" }, + { "ddraw.forceSingleBackBuffer", "True" }, }} }, /* Nightmare Creatures * * Fixes presentation and physics, which is * @@ -1859,18 +1877,17 @@ namespace dxvk { { R"(\\DD_CD\.exe$)", {{ { "d3d9.maxFrameRate", "30" }, }} }, + /* FIFA '99 */ + { R"(\\fifa99\.exe$)", {{ + { "ddraw.forceLegacyPresent", "True" }, + }} }, /* The Longest Journey */ { R"(\\The Longest Journey\\game\.exe$)", {{ - { "ddraw.forceProxiedPresent", "True" }, - }} }, - /* Wing Commander: Prophecy */ - { R"(\\prophecy\.exe$)", {{ - { "ddraw.forceProxiedPresent", "True" }, + { "ddraw.forceLegacyPresent", "True" }, }} }, - /* Tom Clancy's Rainbow Six * - * Fixes broken color key transparency */ + /* Tom Clancy's Rainbow Six */ { R"(\\RainbowSix\.exe$)", {{ - { "ddraw.forceProxiedPresent", "True" }, + { "ddraw.forceSingleBackBuffer", "True" }, }} }, /* Incoming - fixes load screen flickering */ { R"(\\incoming\.exe$)", {{ @@ -1878,57 +1895,34 @@ namespace dxvk { }} }, /* Lands of Lore III */ { R"(\\LOL3\.dat$)", {{ - { "ddraw.forceProxiedPresent", "True" }, - }} }, - /* Virtua Fighter 2 */ - { R"(\\VF2\.exe$)", {{ - { "ddraw.backBufferWriteBack", "True" }, - { "ddraw.backBufferGuard", "Disabled" }, - }} }, - /* Return to Krondor */ - { R"(\\RtK\.exe$)", {{ - { "ddraw.backBufferWriteBack", "True" }, - { "ddraw.backBufferGuard", "Disabled" }, + { "ddraw.forceLegacyPresent", "True" }, }} }, /* RoBoRumble */ { R"(\\rr_dx5\.exe$)", {{ - { "ddraw.forceProxiedPresent", "True" }, + { "ddraw.forceLegacyPresent", "True" }, + }} }, + /* Tomb Raider III: Adventures of Lara Croft * + * Fixes missing pause screen background */ + { R"(\\tomb3\.exe$)", {{ + { "ddraw.forceLegacyPresent", "True" }, }} }, - /* Warhammer: Dark Omen * - * Works around the game trying to attach * - * a back buffer to the primary surface */ - { R"(\\DarkOmen\.exe$)", {{ - { "ddraw.forceProxiedPresent", "True" }, - { "ddraw.forceBlitOnFlip", "True" }, + /* Outwars */ + { R"(\\outwars\.exe$)", {{ + { "ddraw.forceLegacyPresent", "True" }, }} }, /**********************************************/ /* D3D3 GAMES */ /**********************************************/ - /* Outlaws - fixes pause menu backgrounds */ - { R"(\\olwin\.exe$)", {{ - { "ddraw.backBufferWriteBack", "True" }, - }} }, - /* Star Wars: Jedi Knight: Dark Forces II */ - { R"(\\JK\.exe$)", {{ - { "ddraw.forceProxiedPresent", "True" }, - }} }, - /* Star Wars: Jedi Knight: Mysteries of the Sith */ - { R"(\\JKM\.exe$)", {{ - { "ddraw.forceProxiedPresent", "True" }, - }} }, /* Moto Racer 2 - fixes menu flickering */ { R"(\\moto\.exe$)", {{ { "ddraw.forceSingleBackBuffer", "True" }, }} }, - /* Monster Truck Madness */ - { R"(\\MONSTER\.EXE$)", {{ - { "ddraw.forceProxiedPresent", "True" }, - }} }, - /* Forsaken */ - { R"(\\ForsakenHW\.exe$)", {{ - { "ddraw.forceProxiedPresent", "True" }, + /* Resident Evil * + * Fixes black lines in the background image */ + { R"(\\ResidentEvil\.exe$)", {{ + { "ddraw.vertexOffset", "-0.5" }, }} }, }}; @@ -2086,6 +2080,67 @@ namespace dxvk { return true; } + bool Config::parseOptionValue( + const std::string& value, + float& result) { + if (value.size() == 0) + return false; + + // Parse sign + size_t pos = 0; + bool negate = false; + + if (value[0] == '-') { + negate = true; + + if (++pos == value.size()) + return false; + } + + // Parse integer part + uint64_t intPart = 0; + + if (value[pos] == '.') + return false; + + while (pos < value.size()) { + if (value[pos] == '.') { + if (++pos == value.size()) + return false; + break; + } + + if (value[pos] < '0' || value[pos] > '9') + return false; + + intPart *= 10; + intPart += value[pos] - '0'; + pos += 1; + } + + // Parse fractional part + uint64_t fractPart = 0; + uint64_t fractDivisor = 1; + + while (pos < value.size()) { + if (value[pos] < '0' || value[pos] > '9') + return false; + + fractDivisor *= 10; + fractPart *= 10; + fractPart += value[pos] - '0'; + pos += 1; + } + + // Compute final number, not super accurate but this should do + result = float((double(fractPart) / double(fractDivisor)) + double(intPart)); + + if (negate) + result = -result; + + return std::isfinite(result); + } + bool Config::parseOptionValue( const std::string& value, diff --git a/src/util/config/config.h b/src/util/config/config.h index 51f209d7b35..bee0afe6dbd 100644 --- a/src/util/config/config.h +++ b/src/util/config/config.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -123,6 +124,10 @@ namespace dxvk { static bool parseOptionValue( const std::string& value, int32_t& result); + + static bool parseOptionValue( + const std::string& value, + float& result); static bool parseOptionValue( const std::string& value,