diff --git a/src/d3d9/d3d9_bridge.cpp b/src/d3d9/d3d9_bridge.cpp index 47c691cb9ad..f2f471b0cd6 100644 --- a/src/d3d9/d3d9_bridge.cpp +++ b/src/d3d9/d3d9_bridge.cpp @@ -39,11 +39,11 @@ namespace dxvk { } HRESULT DxvkD3D8Bridge::SetColorKeyState(bool colorKeyState) { - return m_device->SetColorKeyState(colorKeyState); + return D3D_OK; //m_device->SetColorKeyState(colorKeyState); } HRESULT DxvkD3D8Bridge::SetColorKey(DWORD colorKeyLow, DWORD colorKeyHigh) { - return m_device->SetColorKey(colorKeyLow, colorKeyHigh); + return D3D_OK; //m_device->SetColorKey(colorKeyLow, colorKeyHigh); } HRESULT DxvkD3D8Bridge::SetLegacyLightsState(bool legacyLightsState, bool isD3DLight2) { diff --git a/src/d3d9/d3d9_device.cpp b/src/d3d9/d3d9_device.cpp index 61aa365b863..724d9f00730 100644 --- a/src/d3d9/d3d9_device.cpp +++ b/src/d3d9/d3d9_device.cpp @@ -6895,6 +6895,9 @@ namespace dxvk { key.Data.Contents.SpecularSource = m_state.renderStates[D3DRS_SPECULARMATERIALSOURCE] & mask; 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; if (key.Data.Contents.UseLighting) { diff --git a/src/d3d9/d3d9_device.h b/src/d3d9/d3d9_device.h index f0e58b3d185..b2b93e6b046 100644 --- a/src/d3d9/d3d9_device.h +++ b/src/d3d9/d3d9_device.h @@ -928,12 +928,6 @@ namespace dxvk { const D3D9ConstantLayout& GetPixelConstantLayout() { return m_psLayout; } HRESULT ResetState(D3DPRESENT_PARAMETERS* pPresentationParameters); - HRESULT SetColorKeyState(bool colorKeyState) { - if (likely(m_colorKeyEnabled != colorKeyState)) { - m_colorKeyEnabled = colorKeyState; - } - return D3D_OK; - } HRESULT SetLegacyLightsState(bool legacyLightState, bool isD3DLight2) { if (likely(m_useLegacyLights != legacyLightState)) { @@ -943,14 +937,6 @@ namespace dxvk { return D3D_OK; } - HRESULT SetColorKey(DWORD colorKeyLow, DWORD colorKeyHigh) { - if (likely(m_state.colorKeyLow != colorKeyLow || m_state.colorKeyHigh != colorKeyHigh)) { - m_state.colorKeyLow = colorKeyLow; - m_state.colorKeyHigh = colorKeyHigh; - } - return D3D_OK; - } - HRESULT ResetSwapChain(D3DPRESENT_PARAMETERS* pPresentationParameters, D3DDISPLAYMODEEX* pFullscreenDisplayMode); HRESULT InitialReset(D3DPRESENT_PARAMETERS* pPresentationParameters, D3DDISPLAYMODEEX* pFullscreenDisplayMode); @@ -1318,11 +1304,10 @@ namespace dxvk { bool m_isD3D6Compatible; bool m_isD3D7Compatible; - // D3D7 and earlier color key transparency state - bool m_colorKeyEnabled = false; // D3D6 and earlier legacy light model state bool m_useLegacyLights = false; bool m_isD3DLight2 = false; + bool m_isD3D8Compatible; bool m_amdATOC = false; bool m_nvATOC = false; diff --git a/src/d3d9/d3d9_fixed_function.cpp b/src/d3d9/d3d9_fixed_function.cpp index d45c70108e5..23bc15692f0 100644 --- a/src/d3d9/d3d9_fixed_function.cpp +++ b/src/d3d9/d3d9_fixed_function.cpp @@ -1082,16 +1082,25 @@ 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) { + d = m_module.opFSub(m_floatType, range, d); + d = m_module.opFDiv(m_floatType, d, range); + } uint32_t hitDir = m_module.opFNegate(m_vec3Type, direction); hitDir = m_module.opSelect(m_vec3Type, isDirectional3, hitDir, delta); hitDir = m_module.opNormalize(m_vec3Type, hitDir); uint32_t atten = m_module.opFFma (m_floatType, d, atten2, atten1); atten = m_module.opFFma (m_floatType, d, atten, atten0); - atten = m_module.opFDiv (m_floatType, m_module.constf32(1.0f), atten); + if (m_vsKey.Data.Contents.UseLegacyLights) + atten = m_module.opFDiv (m_floatType, m_module.constf32(1.0f), atten); atten = m_module.opNMin (m_floatType, atten, m_module.constf32(FLT_MAX)); - atten = m_module.opSelect(m_floatType, m_module.opFOrdGreaterThan(bool_t, d, range), m_module.constf32(0.0f), atten); + if (m_vsKey.Data.Contents.UseLegacyLights && m_vsKey.Data.Contents.IsD3DLight2) + 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); + atten = m_module.opSelect(m_floatType, isDirectional, m_module.constf32(1.0f), atten); // Spot Lighting @@ -1130,6 +1139,8 @@ namespace dxvk { uint32_t midDot = m_module.opDot(m_floatType, normal, mid); midDot = m_module.opFClamp(m_floatType, midDot, m_module.constf32(0.0f), m_module.constf32(1.0f)); uint32_t doSpec = m_module.opFOrdGreaterThan(bool_t, midDot, m_module.constf32(0.0f)); + if (m_vsKey.Data.Contents.UseLegacyLights) + doSpec = m_module.opLogicalAnd(bool_t, doSpec, m_module.opFOrdGreaterThan(bool_t, m_vs.constants.materialPower, m_module.constf32(0.0))); uint32_t specularness = m_module.opPow(m_floatType, midDot, m_vs.constants.materialPower); specularness = m_module.opFMul(m_floatType, specularness, atten); specularness = m_module.opSelect(m_floatType, doSpec, specularness, m_module.constf32(0.0f)); diff --git a/src/d3d9/d3d9_fixed_function.h b/src/d3d9/d3d9_fixed_function.h index 76883112ed6..c1c1ac190a9 100644 --- a/src/d3d9/d3d9_fixed_function.h +++ b/src/d3d9/d3d9_fixed_function.h @@ -113,6 +113,9 @@ 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/d3d9/d3d9_options.cpp b/src/d3d9/d3d9_options.cpp index 2bdc4c2facf..d9ae4c6db08 100644 --- a/src/d3d9/d3d9_options.cpp +++ b/src/d3d9/d3d9_options.cpp @@ -72,8 +72,6 @@ namespace dxvk { this->alphaTestWiggleRoom = config.getOption ("d3d9.alphaTestWiggleRoom", false); this->cachedDynamicBuffers = config.getOption ("d3d9.cachedDynamicBuffers", false); this->deviceLocalConstantBuffers = config.getOption ("d3d9.deviceLocalConstantBuffers", false); - // D3D7/6/5/DDraw options - this->colorKeyCompatibility = config.getOption ("ddraw.colorKeyCompatibility", false); this->allowDirectBufferMapping = config.getOption ("d3d9.allowDirectBufferMapping", true); this->seamlessCubes = config.getOption ("d3d9.seamlessCubes", false); diff --git a/src/d3d9/d3d9_options.h b/src/d3d9/d3d9_options.h index c14e1a9f44e..737e1822c31 100644 --- a/src/d3d9/d3d9_options.h +++ b/src/d3d9/d3d9_options.h @@ -74,11 +74,6 @@ namespace dxvk { /// for rendering hazards bool generalHazards; - /// Color key compatibility mode for DDraw - /// - /// Circumvents the texelFetch color key shader path. - bool colorKeyCompatibility; - /// Anisotropic filter override /// /// Enforces anisotropic filtering with the diff --git a/src/d3d9/d3d9_state.h b/src/d3d9/d3d9_state.h index 3d85862fc4f..3e51e099361 100644 --- a/src/d3d9/d3d9_state.h +++ b/src/d3d9/d3d9_state.h @@ -229,9 +229,6 @@ namespace dxvk { float nPatchSegments = 0.0f; - DWORD colorKeyLow = 0; - DWORD colorKeyHigh = 0; - bool IsLightEnabled(DWORD Index) { const auto& indices = enabledLightIndices; return std::find(indices.begin(), indices.end(), Index) != indices.end(); diff --git a/src/ddraw/d3d3/d3d3_device.cpp b/src/ddraw/d3d3/d3d3_device.cpp index 15cee3b96da..ca5f77dc0c3 100644 --- a/src/ddraw/d3d3/d3d3_device.cpp +++ b/src/ddraw/d3d3/d3d3_device.cpp @@ -1,21 +1,23 @@ #include "d3d3_device.h" #include "../d3d_common_texture.h" +#include "../ddraw_common_interface.h" #include "d3d3_execute_buffer.h" #include "../ddraw/ddraw_surface.h" +#include "../d3d6/d3d6_device.h" #include "../d3d5/d3d5_device.h" #include -#include namespace dxvk { uint32_t D3D3Device::s_deviceCount = 0; D3D3Device::D3D3Device( + D3DCommonDevice* commonD3DDevice, Com&& d3d3DeviceProxy, DDrawSurface* pParent, D3DDEVICEDESC3 Desc, @@ -24,27 +26,36 @@ namespace dxvk { Com&& pDevice9, DWORD CreationFlags9) : DDrawWrappedObject(pParent, std::move(d3d3DeviceProxy), std::move(pDevice9)) - , m_commonIntf ( pParent->GetCommonInterface() ) + , 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(); + } else if (m_commonD3DDevice != nullptr) { + m_commonIntf = m_commonD3DDevice->GetCommonInterface(); + } else { + 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!"); } - m_totalMemory = m_bridge->DetermineInitialTextureMemory(); + if (likely(m_commonD3DDevice == nullptr)) { + m_commonD3DDevice = new D3DCommonDevice(m_commonIntf, CreationFlags9, + m_bridge->DetermineInitialTextureMemory()); - const D3DOptions* d3dOptions = m_commonIntf->GetOptions(); + 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); - } + if (unlikely(d3dOptions->emulateFSAA == FSAAEmulation::Forced)) { + Logger::warn("D3D3Device: Force enabling AA"); + m_d3d9->SetRenderState(d3d9::D3DRS_MULTISAMPLEANTIALIAS, TRUE); + } - if (m_commonIntf->GetD3D5Device() == nullptr) { // 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); @@ -54,6 +65,11 @@ namespace dxvk { m_d3d9->SetTextureStageState(0, d3d9::D3DTSS_ALPHAARG2, D3DTA_DIFFUSE); } + if (m_commonD3DDevice->GetOrigin() == nullptr) + m_commonD3DDevice->SetOrigin(this); + + m_commonD3DDevice->SetD3D3Device(this); + m_deviceCount = ++s_deviceCount; Logger::debug(str::format("D3D3Device: Created a new device nr. ((1-", m_deviceCount, "))")); @@ -65,28 +81,63 @@ namespace dxvk { viewport->GetCommonViewport()->SetD3D3Device(nullptr); } - // Clear the common interface device pointer if it points to this device - if (m_commonIntf->GetD3D3Device() == this) - m_commonIntf->SetD3D3Device(nullptr); + if (m_commonD3DDevice->GetD3D3Device() == this) + m_commonD3DDevice->SetD3D3Device(nullptr); + + if (m_commonD3DDevice->GetOrigin() == this) + m_commonD3DDevice->SetOrigin(nullptr); Logger::debug(str::format("D3D3Device: Device nr. ((1-", m_deviceCount, ")) bites the dust")); } + // Interlocked refcount with the origin device + ULONG STDMETHODCALLTYPE D3D3Device::AddRef() { + IUnknown* origin = m_commonD3DDevice->GetOrigin(); + if (unlikely(origin != nullptr && origin != this)) { + return origin->AddRef(); + } else { + return ComObjectClamp::AddRef(); + } + } + + // Interlocked refcount with the origin device + ULONG STDMETHODCALLTYPE D3D3Device::Release() { + IUnknown* origin = m_commonD3DDevice->GetOrigin(); + if (unlikely(origin != nullptr && origin != this)) { + return origin->Release(); + } else { + return ComObjectClamp::Release(); + } + } + HRESULT STDMETHODCALLTYPE D3D3Device::QueryInterface(REFIID riid, void** ppvObject) { + Logger::debug(">>> D3D3Device::QueryInterface"); + if (unlikely(ppvObject == nullptr)) return E_POINTER; InitReturnPtr(ppvObject); if (unlikely(riid == __uuidof(IDirect3DDevice2))) { - if (likely(m_commonIntf->GetD3D5Device() != nullptr)) { + if (likely(m_commonD3DDevice->GetD3D5Device() != nullptr)) { Logger::debug("D3D3Device::QueryInterface: Query for existing IDirect3DDevice2"); - return m_commonIntf->GetD3D5Device()->QueryInterface(riid, ppvObject); + return m_commonD3DDevice->GetD3D5Device()->QueryInterface(riid, ppvObject); } // A D3D3 device shouldn't be able to create a D3D5 device // if it doesn't previously exist as a parent/origin device - Logger::warn("D3D3Device::QueryInterface: Query for IDirect3DDevice2"); + Logger::debug("D3D3Device::QueryInterface: Query for IDirect3DDevice2"); + return E_NOINTERFACE; + } + if (unlikely(riid == __uuidof(IDirect3DDevice3))) { + if (likely(m_commonD3DDevice->GetD3D6Device() != nullptr)) { + Logger::debug("D3D3Device::QueryInterface: Query for existing IDirect3DDevice3"); + return m_commonD3DDevice->GetD3D6Device()->QueryInterface(riid, ppvObject); + } + + // A D3D3 device shouldn't be able to create a D3D6 device + // if it doesn't previously exist as a parent/origin device + Logger::debug("D3D3Device::QueryInterface: Query for IDirect3DDevice3"); return E_NOINTERFACE; } @@ -369,7 +420,13 @@ namespace dxvk { if (unlikely(d3d == nullptr)) return DDERR_INVALIDPARAMS; - *d3d = ref(m_commonIntf->GetD3D3Interface()); + // D3D3 is "special", and we might not have a D3D interface + // to return on the device, as one can potentially not be created + D3D3Interface* d3d3Intf = m_commonIntf->GetD3D3Interface(); + if (unlikely(d3d3Intf == nullptr)) + return DDERR_NOTFOUND; + + *d3d = ref(d3d3Intf); return D3D_OK; } @@ -412,13 +469,19 @@ namespace dxvk { if (m_currentViewport != d3d3Viewport) m_currentViewport = d3d3Viewport; + m_currentViewport->GetCommonViewport()->SetIsCurrentViewport(true); + m_currentViewport->ApplyViewport(); + m_currentViewport->ApplyAndActivateLights(); + D3DEXECUTEDATA data = d3d3ExecuteBuffer->GetExecuteData(); std::vector executeBuffer = d3d3ExecuteBuffer->GetBuffer(); if (unlikely(executeBuffer.size() == 0)) return DDERR_INVALIDPARAMS; uint8_t* buf = executeBuffer.data(); - const D3DTLVERTEX* vertexBuffer = reinterpret_cast(&buf[data.dwVertexOffset]); + D3DVERTEX* vertexBuffer = reinterpret_cast(buf + data.dwVertexOffset); + D3DTLVERTEX* hVertexBuffer = reinterpret_cast(buf + data.dwHVertexOffset); + uint8_t* ptr = buf + data.dwInstructionOffset; uint8_t* end = ptr + data.dwInstructionLength; @@ -429,6 +492,7 @@ namespace dxvk { 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; @@ -452,7 +516,8 @@ namespace dxvk { } if (masked && b.dwOffset) { - ptr+= branch->dwOffset - instructionSize; + ptr+= branch->dwOffset; + skip = true; } } @@ -462,7 +527,7 @@ namespace dxvk { Logger::debug("D3D3Device::Execute: D3DOP_LINE"); D3DLINE* line = reinterpret_cast(operation); - DrawLineInternal(line, count, data.dwVertexCount, vertexBuffer); + DrawLineInternal(line, count, data.dwVertexCount, hVertexBuffer); break; } @@ -470,7 +535,7 @@ namespace dxvk { Logger::debug("D3D3Device::Execute: D3DOP_POINT"); D3DPOINT* point = reinterpret_cast(operation); - DrawPointInternal(point, count, data.dwVertexCount, vertexBuffer); + DrawPointInternal(point, count, data.dwVertexCount, hVertexBuffer); break; } @@ -478,20 +543,63 @@ namespace dxvk { Logger::debug("D3D3Device::Execute: D3DOP_TRIANGLE"); D3DTRIANGLE* triangle = reinterpret_cast(operation); - DrawTriangleInternal(triangle, count, data.dwVertexCount, vertexBuffer); + DrawTriangleInternal(triangle, count, data.dwVertexCount, hVertexBuffer); break; } case D3DOP_MATRIXLOAD: { - static bool s_matrixLoadErrorShown; - if (!std::exchange(s_matrixLoadErrorShown, true)) - Logger::warn("D3D3Device::Execute: D3DOP_MATRIXLOAD is not implemented"); + Logger::debug("D3D3Device::Execute: D3DOP_MATRIXLOAD"); + + D3DMATRIXLOAD* matrixLoad = reinterpret_cast(operation); + + for (uint16_t i = 0; i < count; i++) { + D3DMATRIXLOAD& ml = matrixLoad[i]; + + D3DMATRIX srcMatrix; + HRESULT hr = GetMatrix(ml.hSrcMatrix, &srcMatrix); + if (unlikely(FAILED(hr))) { + Logger::warn(str::format("D3D3Device::Execute: D3DOP_MATRIXLOAD failed to retrieve source matrix: ", ml.hSrcMatrix)); + continue; + } + + hr = SetMatrix(ml.hDestMatrix, &srcMatrix); + if (unlikely(FAILED(hr))) + Logger::warn(str::format("D3D3Device::Execute: D3DOP_MATRIXLOAD failed to set matrix to destination: ", ml.hDestMatrix)); + } + break; } case D3DOP_MATRIXMULTIPLY: { - static bool s_matrixMultiplyErrorShown; - if (!std::exchange(s_matrixMultiplyErrorShown, true)) - Logger::warn("D3D3Device::Execute: D3DOP_MATRIXMULTIPLY is not implemented"); + Logger::warn("D3D3Device::Execute: D3DOP_MATRIXMULTIPLY"); + + D3DMATRIXMULTIPLY* matrixMultiply = reinterpret_cast(operation); + + for (uint16_t i = 0; i < count; i++) { + D3DMATRIXMULTIPLY& mm = matrixMultiply[i]; + + D3DMATRIX srcMatrix1; + HRESULT hr = GetMatrix(mm.hSrcMatrix1, &srcMatrix1); + if (unlikely(FAILED(hr))) { + Logger::warn(str::format("D3D3Device::Execute: D3DOP_MATRIXMULTIPLY failed to retrieve first source matrix: ", mm.hSrcMatrix1)); + ZeroMemory(&srcMatrix1, sizeof(srcMatrix1)); + continue; + } + + D3DMATRIX srcMatrix2; + hr = GetMatrix(mm.hSrcMatrix2, &srcMatrix2); + if (unlikely(FAILED(hr))) { + Logger::warn(str::format("D3D3Device::Execute: D3DOP_MATRIXMULTIPLY failed to retrieve second source matrix: ", mm.hSrcMatrix2)); + continue; + } + + Matrix4 result = MatrixD3DTo4(&srcMatrix2) * MatrixD3DTo4(&srcMatrix1); + D3DMATRIX destMatrix = Matrix4ToD3D(&result); + + hr = SetMatrix(mm.hDestMatrix, &destMatrix); + if (unlikely(FAILED(hr))) + Logger::warn(str::format("D3D3Device::Execute: D3DOP_MATRIXMULTIPLY failed to set matrix to destination: ", mm.hDestMatrix)); + } + break; } case D3DOP_PROCESSVERTICES: { @@ -499,22 +607,61 @@ namespace dxvk { for (uint16_t i = 0; i < count; i++) { D3DPROCESSVERTICES& pv = processVertices[i]; - if (pv.dwFlags & D3DPROCESSVERTICES_COPY) { - static bool s_pvCopyErrorShown; - // Appears to be mostly harmless - if (!std::exchange(s_pvCopyErrorShown, true)) - Logger::debug("D3D3Device::Execute: D3DOP_PROCESSVERTICES COPY is not implemented"); - } - // D3DPROCESSVERTICES_NOCOLOR and D3DPROCESSVERTICES_UPDATEEXTENTS are additional flags for transforms - if (pv.dwFlags & D3DPROCESSVERTICES_TRANSFORM) { - static bool s_pvTransformErrorShown; - if (!std::exchange(s_pvTransformErrorShown, true)) - Logger::warn("D3D3Device::Execute: D3DOP_PROCESSVERTICES TRANSFORM is not implemented"); - } - if (pv.dwFlags & D3DPROCESSVERTICES_TRANSFORMLIGHT) { - static bool s_pvTransformLightErrorShown; - if (!std::exchange(s_pvTransformLightErrorShown, true)) - Logger::warn("D3D3Device::Execute: D3DOP_PROCESSVERTICES TRANSORMLIGHT is not implemented"); + const DWORD op = pv.dwFlags & D3DPROCESSVERTICES_OPMASK; + + switch (op) { + case D3DPROCESSVERTICES_COPY: { + Logger::debug("D3D3Device::Execute: D3DOP_PROCESSVERTICES COPY"); + if (pv.wDest != pv.wStart) + memcpy(&hVertexBuffer[pv.wDest], &vertexBuffer[pv.wStart], sizeof(D3DTLVERTEX) * pv.dwCount); + + break; + } + 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"); + } + 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]; + + //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))); + + 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; + } } } @@ -524,7 +671,7 @@ namespace dxvk { Logger::warn("D3D3Device::Execute: D3DOP_SPAN"); D3DSPAN* span = reinterpret_cast(operation); - DrawSpanInternal(span, count, data.dwVertexCount, vertexBuffer); + DrawSpanInternal(span, count, data.dwVertexCount, hVertexBuffer); break; } @@ -532,16 +679,19 @@ namespace dxvk { Logger::debug("D3D3Device::Execute: D3DOP_STATELIGHT"); D3DSTATE* state = reinterpret_cast(operation); + for (uint16_t i = 0; i < count; i++) { const D3DSTATE& s = state[i]; SetLightStateInternal(s.dlstLightStateType, s.dwArg[0]); } + break; } case D3DOP_STATERENDER: { Logger::debug("D3D3Device::Execute: D3DOP_STATERENDER"); D3DSTATE* state = reinterpret_cast(operation); + for (uint16_t i = 0; i < count; i++) { const D3DSTATE& s = state[i]; SetRenderStateInternal(s.drstRenderStateType, s.dwArg[0]); @@ -554,6 +704,7 @@ namespace dxvk { D3DSTATE* state = reinterpret_cast(operation); D3DMATRIX matrix; + for (uint16_t i = 0; i < count; i++) { const D3DSTATE& s = state[i]; @@ -603,7 +754,8 @@ namespace dxvk { break; } - ptr += instructionSize; + if (!skip) + ptr += instructionSize; } m_commonIntf->UpdateDrawTracking(); @@ -750,7 +902,8 @@ namespace dxvk { HRESULT hrDS = m_ds->InitializeD3D9DepthStencil(); if (unlikely(FAILED(hrDS))) { Logger::err("D3D3Device::InitializeDS: Failed to initialize D3D9 DS"); - } else if (m_commonIntf->GetD3D5Device() == nullptr) { + } else if (m_commonD3DDevice->GetD3D5Device() == nullptr && + m_commonD3DDevice->GetD3D6Device() == nullptr) { Logger::info("D3D3Device::InitializeDS: Got depth stencil from RT"); DDSURFACEDESC descDS; @@ -766,7 +919,8 @@ namespace dxvk { m_d3d9->SetRenderState(d3d9::D3DRS_ZENABLE, d3d9::D3DZB_TRUE); } } - } else if (m_commonIntf->GetD3D5Device() == nullptr) { + } else if (m_commonD3DDevice->GetD3D5Device() == nullptr && + m_commonD3DDevice->GetD3D6Device() == nullptr) { Logger::info("D3D3Device::InitializeDS: RT has no depth stencil attached"); m_d3d9->SetDepthStencilSurface(nullptr); // Should be superfluous, but play it safe @@ -803,34 +957,36 @@ namespace dxvk { switch (dwLightStateType) { case D3DLIGHTSTATE_MATERIAL: { - D3D5Device* device5 = m_commonIntf->GetD3D5Device(); + 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); return D3D_OK; } Logger::debug(str::format("D3D3Device::SetLightStateInternal: Applying material nr. ", dwLightState, " to D3D9")); - D3D3Interface* d3d3Intf = m_commonIntf->GetD3D3Interface(); - // consider pure D3D3 device use by default - if (likely(d3d3Intf != nullptr)) { - d3d9::D3DMATERIAL9* material9 = d3d3Intf->GetCommonD3DInterface()->GetD3D9MaterialFromHandle(dwLightState); + D3DCommonInterface* commonD3DIntf = m_commonD3DDevice->GetCommonD3DInterface(); + if (likely(commonD3DIntf != nullptr)) { + d3d9::D3DMATERIAL9* material9 = commonD3DIntf->GetD3D9MaterialFromHandle(dwLightState); if (unlikely(material9 == nullptr)) return DDERR_INVALIDPARAMS; - m_materialHandle = dwLightState; m_d3d9->SetMaterial(material9); - // fall back to using a D3D5 device otherwise - } else if (likely(device5 != nullptr)) { - d3d9::D3DMATERIAL9* material9 = device5->GetParent()->GetCommonD3DInterface()->GetD3D9MaterialFromHandle(dwLightState); - device5->SetCurrentMaterialHandle(dwLightState); - device5->GetD3D9()->SetMaterial(material9); + m_materialHandle = dwLightState; + if (device5 != nullptr) { + device5->SetCurrentMaterialHandle(dwLightState); + } else if (unlikely(device6 != nullptr)) { + device6->SetCurrentMaterialHandle(dwLightState); + } } else { Logger::warn("D3D3Device::SetLightStateInternal: Unable to set D3D9 material"); } @@ -1170,14 +1326,19 @@ namespace dxvk { return m_d3d9->SetRenderState(State9, dwRenderState); } - inline void D3D3Device::DrawTriangleInternal(D3DTRIANGLE* triangle, DWORD count, DWORD vertexCount, const D3DTLVERTEX* vertexBuffer) { + inline void D3D3Device::DrawTriangleInternal(D3DTRIANGLE* triangle, uint16_t count, DWORD vertexCount, const D3DTLVERTEX* vertexBuffer) { std::vector vertices; - for (DWORD i = 0; i < count; i++) { + for (uint16_t i = 0; i < count; i++) { const D3DTRIANGLE& t = triangle[i]; - if (t.v1 >= vertexCount || t.v2 >= vertexCount || t.v3 >= vertexCount) + if (t.v1 >= vertexCount || t.v2 >= vertexCount || t.v3 >= vertexCount) { + static bool s_skipErrorShown; + if (!std::exchange(s_skipErrorShown, true)) + Logger::warn(str::format("D3D3Device::Execute: D3DOP_TRIANGLE skipping triangles draw")); + continue; + } // TODO: Ignoring t.wFlags for now as they are relevant only for wireframe mode? // (D3DTRIFLAG_START, D3DTRIFLAG_STARTFLAT(1-29), D3DTRIFLAG_ODD(strip), @@ -1189,8 +1350,6 @@ namespace dxvk { } if (!vertices.empty() && m_d3d9 != nullptr) { - HandlePreDrawLegacyProjection(); - m_d3d9->SetFVF(D3DFVF_TLVERTEX); HRESULT hr = m_d3d9->DrawPrimitiveUP( d3d9::D3DPT_TRIANGLELIST, @@ -1198,8 +1357,6 @@ namespace dxvk { vertices.data(), GetFVFSize(D3DFVF_TLVERTEX)); - HandlePostDrawLegacyProjection(); - if (SUCCEEDED(hr)) { Logger::debug(str::format("D3D3Device::Execute: D3DOP_TRIANGLE drawn vertices: ", vertices.size())); m_stats.dwTrianglesDrawn += std::max(vertices.size() / 3, 0u); @@ -1211,10 +1368,10 @@ namespace dxvk { } } - inline void D3D3Device::DrawLineInternal(D3DLINE* line, DWORD count, DWORD vertexCount, const D3DTLVERTEX* vertexBuffer) { + inline void D3D3Device::DrawLineInternal(D3DLINE* line, uint16_t count, DWORD vertexCount, const D3DTLVERTEX* vertexBuffer) { std::vector vertices; - for (DWORD i = 0; i < count; i++) { + for (uint16_t i = 0; i < count; i++) { const D3DLINE& l = line[i]; if (l.v1 >= vertexCount || l.v2 >= vertexCount) @@ -1225,8 +1382,6 @@ namespace dxvk { } if (!vertices.empty() && m_d3d9 != nullptr) { - HandlePreDrawLegacyProjection(); - m_d3d9->SetFVF(D3DFVF_TLVERTEX); HRESULT hr = m_d3d9->DrawPrimitiveUP( d3d9::D3DPT_LINELIST, @@ -1234,8 +1389,6 @@ namespace dxvk { vertices.data(), GetFVFSize(D3DFVF_TLVERTEX)); - HandlePostDrawLegacyProjection(); - if (SUCCEEDED(hr)) { Logger::debug(str::format("D3D3Device::Execute: D3DOP_LINE drawn vertices: ", vertices.size())); m_stats.dwLinesDrawn += std::max(vertices.size() / 2, 0u); @@ -1247,10 +1400,10 @@ namespace dxvk { } } - inline void D3D3Device::DrawPointInternal(D3DPOINT* point, DWORD count, DWORD vertexCount, const D3DTLVERTEX* vertexBuffer) { + inline void D3D3Device::DrawPointInternal(D3DPOINT* point, uint16_t count, DWORD vertexCount, const D3DTLVERTEX* vertexBuffer) { std::vector vertices; - for (DWORD i = 0; i < count; i++) { + for (uint16_t i = 0; i < count; i++) { const D3DPOINT& p = point[i]; if (p.wFirst >= vertexCount) @@ -1262,8 +1415,6 @@ namespace dxvk { } if (!vertices.empty() && m_d3d9 != nullptr) { - HandlePreDrawLegacyProjection(); - m_d3d9->SetFVF(D3DFVF_TLVERTEX); HRESULT hr = m_d3d9->DrawPrimitiveUP( d3d9::D3DPT_POINTLIST, @@ -1271,8 +1422,6 @@ namespace dxvk { vertices.data(), GetFVFSize(D3DFVF_TLVERTEX)); - HandlePostDrawLegacyProjection(); - if (SUCCEEDED(hr)) { Logger::debug(str::format("D3D3Device::Execute: D3DOP_POINT drawn vertices: ", vertices.size())); m_stats.dwPointsDrawn += static_cast(vertices.size()); @@ -1283,10 +1432,10 @@ namespace dxvk { } } - inline void D3D3Device::DrawSpanInternal(D3DSPAN* span, DWORD count, DWORD vertexCount, const D3DTLVERTEX* vertexBuffer) { + inline void D3D3Device::DrawSpanInternal(D3DSPAN* span, uint16_t count, DWORD vertexCount, const D3DTLVERTEX* vertexBuffer) { std::vector vertices; - for (DWORD i = 0; i < count; i++) { + for (uint16_t i = 0; i < count; i++) { const D3DSPAN& s = span[i]; if (s.wFirst >= vertexCount) @@ -1298,8 +1447,6 @@ namespace dxvk { } if (!vertices.empty() && m_d3d9 != nullptr) { - HandlePreDrawLegacyProjection(); - m_d3d9->SetFVF(D3DFVF_TLVERTEX); HRESULT hr = m_d3d9->DrawPrimitiveUP( d3d9::D3DPT_LINESTRIP, @@ -1307,8 +1454,6 @@ namespace dxvk { vertices.data(), GetFVFSize(D3DFVF_TLVERTEX)); - HandlePostDrawLegacyProjection(); - if (SUCCEEDED(hr)) { Logger::debug(str::format("D3D3Device::Execute: D3DOP_SPAN drawn vertices: ", vertices.size())); m_stats.dwSpansDrawn += std::max(vertices.size() - 1, 0u); @@ -1320,8 +1465,8 @@ namespace dxvk { } } - inline void D3D3Device::TextureLoadInternal(D3DTEXTURELOAD* textureLoad, DWORD count) { - for (DWORD i = 0; i < count; i++) { + inline void D3D3Device::TextureLoadInternal(D3DTEXTURELOAD* textureLoad, uint16_t count) { + for (uint16_t i = 0; i < count; i++) { const D3DTEXTURELOAD& tl = textureLoad[i]; DDrawSurface* destSurf = m_commonIntf->GetSurfaceFromTextureHandle(tl.hDestTexture); @@ -1415,4 +1560,4 @@ namespace dxvk { return D3D_OK; } -} +} \ No newline at end of file diff --git a/src/ddraw/d3d3/d3d3_device.h b/src/ddraw/d3d3/d3d3_device.h index f0cba7e33ee..86312c8aaf8 100644 --- a/src/ddraw/d3d3/d3d3_device.h +++ b/src/ddraw/d3d3/d3d3_device.h @@ -4,8 +4,9 @@ #include "../ddraw_wrapped_object.h" #include "../ddraw_options.h" +#include "../d3d_common_device.h" + #include "../d3d_multithread.h" -#include "../ddraw_common_interface.h" #include "../../d3d9/d3d9_bridge.h" @@ -17,6 +18,8 @@ namespace dxvk { + class D3DCommonDevice; + class DDrawCommonInterface; class DDrawSurface; /** @@ -26,6 +29,7 @@ namespace dxvk { public: D3D3Device( + D3DCommonDevice* commonD3DDevice, Com&& d3d3DeviceProxy, DDrawSurface* pParent, D3DDEVICEDESC3 Desc, @@ -36,6 +40,10 @@ namespace dxvk { ~D3D3Device(); + ULONG STDMETHODCALLTYPE AddRef(); + + ULONG STDMETHODCALLTYPE Release(); + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject); HRESULT STDMETHODCALLTYPE GetCaps(D3DDEVICEDESC *hal_desc, D3DDEVICEDESC *hel_desc); @@ -78,6 +86,10 @@ namespace dxvk { void InitializeDS(); + D3DCommonDevice* GetCommonD3DDevice() { + return m_commonD3DDevice.ptr(); + } + D3DDeviceLock LockDevice() { return m_multithread.AcquireLock(); } @@ -86,10 +98,6 @@ namespace dxvk { m_bridge->SetLegacyLightsState(true, isD3DLight2); } - uint32_t GetTotalTextureMemory() const { - return m_totalMemory; - } - D3DSTATS GetStatsInternal() const { return m_stats; } @@ -121,8 +129,8 @@ namespace dxvk { private: inline void RefreshLastUsedDevice() { - if (unlikely(m_commonIntf->GetD3D3Device() != this)) - m_commonIntf->SetD3D3Device(this); + if (unlikely(m_commonIntf->GetCommonD3DDevice() != m_commonD3DDevice.ptr())) + m_commonIntf->SetCommonD3DDevice(m_commonD3DDevice.ptr()); } inline void AddViewportInternal(IDirect3DViewport* viewport); @@ -135,44 +143,25 @@ namespace dxvk { inline HRESULT STDMETHODCALLTYPE SetLightStateInternal(D3DLIGHTSTATETYPE dwLightStateType, DWORD dwLightState); - inline void DrawTriangleInternal(D3DTRIANGLE* triangle, DWORD count, DWORD vertexCount, const D3DTLVERTEX* vertexBuffer); + inline void DrawTriangleInternal(D3DTRIANGLE* triangle, uint16_t count, DWORD vertexCount, const D3DTLVERTEX* vertexBuffer); - inline void DrawLineInternal(D3DLINE* line, DWORD count, DWORD vertexCount, const D3DTLVERTEX* vertexBuffer); + inline void DrawLineInternal(D3DLINE* line, uint16_t count, DWORD vertexCount, const D3DTLVERTEX* vertexBuffer); - inline void DrawPointInternal(D3DPOINT* point, DWORD count, DWORD vertexCount, const D3DTLVERTEX* vertexBuffer); + inline void DrawPointInternal(D3DPOINT* point, uint16_t count, DWORD vertexCount, const D3DTLVERTEX* vertexBuffer); - inline void DrawSpanInternal(D3DSPAN* span, DWORD count, DWORD vertexCount, const D3DTLVERTEX* vertexBuffer); + inline void DrawSpanInternal(D3DSPAN* span, uint16_t count, DWORD vertexCount, const D3DTLVERTEX* vertexBuffer); - inline void TextureLoadInternal(D3DTEXTURELOAD* textureLoad, DWORD count); - - inline void HandlePreDrawLegacyProjection() { - if (likely(m_currentViewport != nullptr)) { - m_legacyProjection = m_currentViewport->GetCommonViewport()->GetLegacyProjectionMatrix(0); - - if (m_legacyProjection != nullptr) { - //Logger::debug("D3D3Device: Applying legacy projection"); - m_d3d9->GetTransform(d3d9::D3DTS_PROJECTION, &m_projectionMatrix); - m_d3d9->MultiplyTransform(d3d9::D3DTS_PROJECTION, m_legacyProjection); - } - } - } - - inline void HandlePostDrawLegacyProjection() { - if (m_legacyProjection != nullptr) { - //Logger::debug("D3D3Device: Reverting legacy projection"); - m_d3d9->SetTransform(d3d9::D3DTS_PROJECTION, &m_projectionMatrix); - } - } + inline void TextureLoadInternal(D3DTEXTURELOAD* textureLoad, uint16_t count); bool m_inScene = false; static uint32_t s_deviceCount; uint32_t m_deviceCount = 0; - uint32_t m_totalMemory = 0; - DDrawCommonInterface* m_commonIntf = nullptr; + Com m_commonD3DDevice; + Com m_bridge; D3DMultithread m_multithread; diff --git a/src/ddraw/d3d3/d3d3_interface.cpp b/src/ddraw/d3d3/d3d3_interface.cpp index 0376f554c84..77ca65d889e 100644 --- a/src/ddraw/d3d3/d3d3_interface.cpp +++ b/src/ddraw/d3d3/d3d3_interface.cpp @@ -76,6 +76,8 @@ namespace dxvk { } HRESULT STDMETHODCALLTYPE D3D3Interface::QueryInterface(REFIID riid, void** ppvObject) { + Logger::debug(">>> D3D3Interface::QueryInterface"); + if (unlikely(ppvObject == nullptr)) return E_POINTER; @@ -225,8 +227,15 @@ namespace dxvk { InitReturnPtr(lplpDirect3DMaterial); + Com ddrawMaterialProxied; + HRESULT hr = m_proxy->CreateMaterial(&ddrawMaterialProxied, pUnkOuter); + if (unlikely(FAILED(hr))) { + Logger::err("D3D3Interface::CreateMaterial: Failed to create proxied material"); + return hr; + } + D3DMATERIALHANDLE handle = m_commonD3DIntf->GetNextMaterialHandle(); - Com d3d3Material = new D3D3Material(nullptr, this, handle); + Com d3d3Material = new D3D3Material(std::move(ddrawMaterialProxied), this, handle); m_commonD3DIntf->EmplaceMaterial(d3d3Material->GetCommonMaterial(), handle); *lplpDirect3DMaterial = d3d3Material.ref(); @@ -258,6 +267,9 @@ namespace dxvk { if (unlikely(lpD3DFDS->dwSize != sizeof(D3DFINDDEVICESEARCH))) return DDERR_INVALIDPARAMS; + if (unlikely(!IsValidFindDeviceResultSize(lpD3DFDR->dwSize))) + return DDERR_INVALIDPARAMS; + const D3DOptions* d3dOptions = m_commonIntf->GetOptions(); // Software emulation, this is expected to be exposed @@ -325,6 +337,24 @@ namespace dxvk { lpD3DFRD3.ddSwDesc = descRGB_HEL; } + memcpy(lpD3DFDR, &lpD3DFRD3, sizeof(D3DFINDDEVICERESULT3)); + } else if (lpD3DFDS->dwFlags & D3DFDS_COLORMODEL) { + Logger::debug("D3D3Interface::FindDevice: Matching by color model"); + + Logger::debug("D3D3Interface::FindDevice: Matched IID_IDirect3DHALDevice"); + lpD3DFRD3.guid = IID_IDirect3DHALDevice; + lpD3DFRD3.ddHwDesc = descHAL_HAL; + lpD3DFRD3.ddSwDesc = descHAL_HEL; + + memcpy(lpD3DFDR, &lpD3DFRD3, sizeof(D3DFINDDEVICERESULT3)); + } else if (lpD3DFDS->dwFlags == 0) { + Logger::debug("D3D3Interface::FindDevice: No matching criteria specified"); + + Logger::debug("D3D3Interface::FindDevice: Matched IID_IDirect3DHALDevice"); + lpD3DFRD3.guid = IID_IDirect3DHALDevice; + lpD3DFRD3.ddHwDesc = descHAL_HAL; + lpD3DFRD3.ddSwDesc = descHAL_HEL; + memcpy(lpD3DFDR, &lpD3DFRD3, sizeof(D3DFINDDEVICERESULT3)); } else { Logger::err("D3D3Interface::FindDevice: Unhandled matching type"); diff --git a/src/ddraw/d3d3/d3d3_material.cpp b/src/ddraw/d3d3/d3d3_material.cpp index b754dfed548..86213af2215 100644 --- a/src/ddraw/d3d3/d3d3_material.cpp +++ b/src/ddraw/d3d3/d3d3_material.cpp @@ -17,6 +17,8 @@ namespace dxvk { : DDrawWrappedObject(pParent, std::move(proxyMaterial), nullptr) { m_commonMaterial = new D3DCommonMaterial(handle); + m_commonMaterial->SetD3D3Material(this); + m_materialCount = ++s_materialCount; Logger::debug(str::format("D3D3Material: Created a new material nr. [[1-", m_materialCount, "]]")); @@ -25,6 +27,8 @@ namespace dxvk { D3D3Material::~D3D3Material() { m_parent->GetCommonD3DInterface()->ReleaseMaterialHandle(m_commonMaterial->GetMaterialHandle()); + m_commonMaterial->SetD3D3Material(nullptr); + Logger::debug(str::format("D3D3Material: Material nr. [[1-", m_materialCount, "]] bites the dust")); } @@ -41,6 +45,12 @@ namespace dxvk { if (unlikely(data == nullptr)) 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); + if (unlikely(FAILED(hr))) + Logger::warn("D3D3Material::SetMaterial: Failed to set the proxied material"); + d3d9::D3DMATERIAL9* material9 = m_commonMaterial->GetD3D9Material(); material9->Diffuse = data->dcvDiffuse; @@ -59,7 +69,7 @@ namespace dxvk { Logger::debug(str::format(" Power: ", material9->Power)); // Update the D3D9 material directly if it's actively being used - D3D3Device* device3 = m_parent->GetCommonInterface()->GetD3D3Device(); + D3D3Device* device3 = m_parent->GetCommonInterface()->GetCommonD3DDevice()->GetD3D3Device(); if (likely(device3 != nullptr)) { D3DMATERIALHANDLE currentHandle = device3->GetCurrentMaterialHandle(); if (currentHandle == handle) { diff --git a/src/ddraw/d3d3/d3d3_viewport.cpp b/src/ddraw/d3d3/d3d3_viewport.cpp index 350c20bf4ed..b018899fc07 100644 --- a/src/ddraw/d3d3/d3d3_viewport.cpp +++ b/src/ddraw/d3d3/d3d3_viewport.cpp @@ -29,6 +29,9 @@ namespace dxvk { m_commonViewport->SetD3D3Viewport(this); + if (m_commonViewport->GetOrigin() == nullptr) + m_commonViewport->SetOrigin(this); + m_viewportCount = ++s_viewportCount; Logger::debug(str::format("D3D3Viewport: Created a new viewport nr. [[1-", m_viewportCount, "]]")); @@ -42,11 +45,34 @@ namespace dxvk { light->SetViewport3(nullptr); } + if (m_commonViewport->GetOrigin() == this) + m_commonViewport->SetOrigin(nullptr); + m_commonViewport->SetD3D3Viewport(nullptr); Logger::debug(str::format("D3D3Viewport: Viewport nr. [[1-", m_viewportCount, "]] bites the dust")); } + // Interlocked refcount with the origin viewport + ULONG STDMETHODCALLTYPE D3D3Viewport::AddRef() { + IUnknown* origin = m_commonViewport->GetOrigin(); + if (unlikely(origin != nullptr && origin != this)) { + return origin->AddRef(); + } else { + return ComObjectClamp::AddRef(); + } + } + + // Interlocked refcount with the origin viewport + ULONG STDMETHODCALLTYPE D3D3Viewport::Release() { + IUnknown* origin = m_commonViewport->GetOrigin(); + if (unlikely(origin != nullptr && origin != this)) { + return origin->Release(); + } else { + return ComObjectClamp::Release(); + } + } + HRESULT STDMETHODCALLTYPE D3D3Viewport::QueryInterface(REFIID riid, void** ppvObject) { Logger::debug(">>> D3D3Viewport::QueryInterface"); @@ -68,7 +94,10 @@ namespace dxvk { if (unlikely(FAILED(hr))) return hr; - *ppvObject = ref(new D3D5Viewport(m_commonViewport.ptr(), std::move(ppvProxyObject), nullptr)); + m_viewport5 = new D3D5Viewport(m_commonViewport.ptr(), std::move(ppvProxyObject), nullptr); + + // On native this is the same object, so no need to ref + *ppvObject = m_viewport5.ptr(); return S_OK; } @@ -85,7 +114,10 @@ namespace dxvk { if (unlikely(FAILED(hr))) return hr; - *ppvObject = ref(new D3D6Viewport(m_commonViewport.ptr(), std::move(ppvProxyObject), nullptr)); + m_viewport6 = new D3D6Viewport(m_commonViewport.ptr(), std::move(ppvProxyObject), nullptr); + + // On native this is the same object, so no need to ref + *ppvObject = m_viewport6.ptr(); return S_OK; } @@ -127,11 +159,13 @@ namespace dxvk { data->dvMinZ = viewport9->MinZ; data->dvMaxZ = viewport9->MaxZ; - data->dvMaxX = 1.0f; - data->dvMaxY = 1.0f; D3DVECTOR* legacyScale = m_commonViewport->GetLegacyScale(); data->dvScaleX = legacyScale->x * (float)data->dwWidth / 2.0f; data->dvScaleY = legacyScale->y * (float)data->dwHeight / 2.0f; + D3DVECTOR* legacyClip = m_commonViewport->GetLegacyClip(); + // Don't compact these because precission issues can affect the outcome + data->dvMaxX = 2.0f / legacyScale->x * (1.0f + (legacyClip->x + 1.0f) / -2.0f); // dvClipX + dvClipWidth + data->dvMaxY = 2.0f / legacyScale->y * (legacyClip->y - 1.0f) / -2.0f; // dvClipY return D3D_OK; } @@ -184,8 +218,35 @@ namespace dxvk { } HRESULT STDMETHODCALLTYPE D3D3Viewport::TransformVertices(DWORD vertex_count, D3DTRANSFORMDATA *data, DWORD flags, DWORD *offscreen) { - Logger::warn("<<< D3D3Viewport::TransformVertices: Proxy"); - return m_proxy->TransformVertices(vertex_count, data, flags, offscreen); + Logger::debug(">>> D3D3Viewport::TransformVertices"); + + if (unlikely(!m_commonViewport->HasDevice())) { + Logger::warn("D3D3Viewport::TransformVertices: Viewport isn't attached to a device"); + return D3DERR_VIEWPORTHASNODEVICE; + } + + d3d9::IDirect3DDevice9* d3d9Device = m_commonViewport->GetD3D9Device(); + + // Temporarily activate this viewport, if not already active + d3d9::D3DVIEWPORT9 currentViewport9; + if (!m_commonViewport->IsCurrentViewport()) { + D3D3Viewport* currentViewport = m_commonViewport->GetCurrentD3D3Viewport(); + if (currentViewport != nullptr) { + currentViewport9 = *currentViewport->GetCommonViewport()->GetD3D9Viewport(); + } else { + d3d9Device->GetViewport(¤tViewport9); + } + d3d9Device->SetViewport(m_commonViewport->GetD3D9Viewport()); + } + + HRESULT hr = m_commonViewport->TransformVertices(vertex_count, data, flags, offscreen); + + // Restore the previously active viewport + if (!m_commonViewport->IsCurrentViewport()) { + d3d9Device->SetViewport(¤tViewport9); + } + + return hr; } // Docs state: "The IDirect3DViewport::LightElements method is not currently implemented." @@ -211,6 +272,16 @@ namespace dxvk { if (unlikely(commonMaterial == nullptr)) return DDERR_INVALIDPARAMS; + // We still need to proxy this call to DDraw for + // proxied clear colors to be accurate + D3D3Device* device3 = m_commonViewport->GetD3D3Device(); + if (likely(device3 != nullptr)) { + D3DMATERIALHANDLE proxyHandle = commonMaterial->GetProxiedMaterialHandle(device3->GetProxied()); + HRESULT hr = m_proxy->SetBackground(proxyHandle); + if (unlikely(FAILED(hr))) + Logger::warn("D3D3Viewport::SetBackground: Failed to set the proxied viewport background"); + } + m_commonViewport->MarkMaterialAsSet(); // Cache only the set material handle, as its color can @@ -226,8 +297,7 @@ namespace dxvk { if (unlikely(material == nullptr || valid == nullptr)) return DDERR_INVALIDPARAMS; - if (likely(m_commonViewport->IsMaterialSet())) - *material = m_commonViewport->GetMaterialHandle(); + *material = m_commonViewport->GetMaterialHandle(); *valid = m_commonViewport->IsMaterialSet(); return D3D_OK; diff --git a/src/ddraw/d3d3/d3d3_viewport.h b/src/ddraw/d3d3/d3d3_viewport.h index 7d4161ccec8..ae3e8762baf 100644 --- a/src/ddraw/d3d3/d3d3_viewport.h +++ b/src/ddraw/d3d3/d3d3_viewport.h @@ -11,6 +11,9 @@ namespace dxvk { class D3DLight; + class D3D6Viewport; + class D3D5Viewport; + class D3D3Viewport final : public DDrawWrappedObject { public: @@ -22,6 +25,10 @@ namespace dxvk { ~D3D3Viewport(); + ULONG STDMETHODCALLTYPE AddRef(); + + ULONG STDMETHODCALLTYPE Release(); + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject); HRESULT STDMETHODCALLTYPE Initialize(LPDIRECT3D lpDirect3D); @@ -67,6 +74,9 @@ namespace dxvk { Com m_commonViewport; + Com m_viewport6; + Com m_viewport5; + }; } diff --git a/src/ddraw/d3d5/d3d5_device.cpp b/src/ddraw/d3d5/d3d5_device.cpp index fefae193d76..e4e056f4be4 100644 --- a/src/ddraw/d3d5/d3d5_device.cpp +++ b/src/ddraw/d3d5/d3d5_device.cpp @@ -3,15 +3,15 @@ #include "../ddraw_util.h" #include "../d3d_common_texture.h" +#include "../ddraw_common_interface.h" +#include "../d3d6/d3d6_device.h" #include "../d3d3/d3d3_device.h" #include "../ddraw/ddraw_surface.h" #include "../ddraw2/ddraw2_interface.h" #include -#include -#include "../../util/util_bit.h" // Supress warnings about D3DRENDERSTATE_ALPHABLENDENABLE_OLD // not being in the shipped D3D5 enum (thanks a lot, MS) @@ -24,6 +24,7 @@ namespace dxvk { uint32_t D3D5Device::s_deviceCount = 0; D3D5Device::D3D5Device( + D3DCommonDevice* commonD3DDevice, Com&& d3d5DeviceProxy, D3D5Interface* pParent, D3DDEVICEDESC2 Desc, @@ -33,34 +34,49 @@ namespace dxvk { DDrawSurface* pSurface, DWORD CreationFlags9) : DDrawWrappedObject(pParent, std::move(d3d5DeviceProxy), std::move(pDevice9)) - , m_commonIntf ( pParent->GetCommonInterface() ) - , m_creationFlags9 ( CreationFlags9 ) + , 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(); + } else if (m_commonD3DDevice != nullptr) { + m_commonIntf = m_commonD3DDevice->GetCommonInterface(); + } else { + 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!"); } - m_totalMemory = m_bridge->DetermineInitialTextureMemory(); + if (likely(m_commonD3DDevice == nullptr)) { + m_commonD3DDevice = new D3DCommonDevice(m_commonIntf, CreationFlags9, + m_bridge->DetermineInitialTextureMemory()); - const D3DOptions* d3dOptions = m_commonIntf->GetOptions(); + 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); + if (unlikely(d3dOptions->emulateFSAA == FSAAEmulation::Forced)) { + Logger::warn("D3D5Device: Force enabling AA"); + m_d3d9->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); } - // 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); + if (m_commonD3DDevice->GetOrigin() == nullptr) + m_commonD3DDevice->SetOrigin(this); + + m_commonD3DDevice->SetD3D5Device(this); m_deviceCount = ++s_deviceCount; @@ -73,23 +89,47 @@ namespace dxvk { viewport->GetCommonViewport()->SetD3D5Device(nullptr); } - // Clear the common interface device pointer if it points to this device - if (m_commonIntf->GetD3D5Device() == this) - m_commonIntf->SetD3D5Device(nullptr); + if (m_commonD3DDevice->GetD3D5Device() == this) + m_commonD3DDevice->SetD3D5Device(nullptr); + + if (m_commonD3DDevice->GetOrigin() == this) + m_commonD3DDevice->SetOrigin(nullptr); Logger::debug(str::format("D3D5Device: Device nr. ((2-", m_deviceCount, ")) bites the dust")); } + // Interlocked refcount with the origin device + ULONG STDMETHODCALLTYPE D3D5Device::AddRef() { + IUnknown* origin = m_commonD3DDevice->GetOrigin(); + if (unlikely(origin != nullptr && origin != this)) { + return origin->AddRef(); + } else { + return ComObjectClamp::AddRef(); + } + } + + // Interlocked refcount with the origin device + ULONG STDMETHODCALLTYPE D3D5Device::Release() { + IUnknown* origin = m_commonD3DDevice->GetOrigin(); + if (unlikely(origin != nullptr && origin != this)) { + return origin->Release(); + } else { + return ComObjectClamp::Release(); + } + } + HRESULT STDMETHODCALLTYPE D3D5Device::QueryInterface(REFIID riid, void** ppvObject) { + Logger::debug(">>> D3D5Device::QueryInterface"); + if (unlikely(ppvObject == nullptr)) return E_POINTER; InitReturnPtr(ppvObject); if (unlikely(riid == __uuidof(IDirect3DDevice))) { - if (m_commonIntf->GetD3D3Device() != nullptr) { + if (m_commonD3DDevice->GetD3D3Device() != nullptr) { Logger::debug("D3D3Device::QueryInterface: Query for existing IDirect3DDevice"); - return m_commonIntf->GetD3D3Device()->QueryInterface(riid, ppvObject); + return m_commonD3DDevice->GetD3D3Device()->QueryInterface(riid, ppvObject); } Logger::debug("D3D5Device::QueryInterface: Query for IDirect3DDevice"); @@ -104,11 +144,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(); - *ppvObject = ref(new D3D3Device(std::move(ppvProxyObject), m_rt.ptr(), GetD3D3Caps(d3dOptions), - m_deviceGUID, m_params9, std::move(device9), m_creationFlags9)); + 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()); + + // 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); + } + + Logger::err("D3D5Device::QueryInterface: Query for IDirect3DDevice3"); + return E_NOINTERFACE; + } try { *ppvObject = ref(this->GetInterface(riid)); @@ -197,8 +251,8 @@ namespace dxvk { D3DSTATS newStats = { }; - if (likely(m_commonIntf->GetD3D3Device() != nullptr)) - newStats = m_commonIntf->GetD3D3Device()->GetStatsInternal(); + if (likely(m_commonD3DDevice->GetD3D3Device() != nullptr)) + newStats = m_commonD3DDevice->GetD3D3Device()->GetStatsInternal(); const DWORD dwSize = stats->dwSize; @@ -438,14 +492,16 @@ namespace dxvk { Logger::debug(">>> D3D5Device::GetCurrentViewport"); + // This does indeed return D3DERR_NOCURRENTVIEWPORT... if (unlikely(viewport == nullptr)) return D3DERR_NOCURRENTVIEWPORT; - InitReturnPtr(viewport); - + // Current viewport is checked before initializing the return pointer if (unlikely(m_currentViewport == nullptr)) return D3DERR_NOCURRENTVIEWPORT; + InitReturnPtr(viewport); + *viewport = m_currentViewport.ref(); return D3D_OK; @@ -463,7 +519,7 @@ namespace dxvk { if (unlikely(!m_commonIntf->IsWrappedSurface(surface))) { Logger::err("D3D5Device::SetRenderTarget: Received an unwrapped RT"); - return DDERR_GENERIC; + return DDERR_UNSUPPORTED; } DDrawSurface* rt5 = static_cast(surface); @@ -1282,12 +1338,6 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE D3D5Device::SetTransform(D3DTRANSFORMSTATETYPE state, D3DMATRIX *matrix) { Logger::debug(">>> D3D5Device::SetTransform"); - - // Need to also proxy for viewport TransformVertices calls to work - HRESULT hr = m_proxy->SetTransform(state, matrix); - if (unlikely(FAILED(hr))) - return hr; - return m_d3d9->SetTransform(ConvertTransformState(state), matrix); } @@ -1298,12 +1348,6 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE D3D5Device::MultiplyTransform(D3DTRANSFORMSTATETYPE state, D3DMATRIX *matrix) { Logger::debug(">>> D3D5Device::MultiplyTransform"); - - // Need to also proxy for viewport TransformVertices calls to work - HRESULT hr = m_proxy->MultiplyTransform(state, matrix); - if (unlikely(FAILED(hr))) - return hr; - return m_d3d9->MultiplyTransform(ConvertTransformState(state), matrix); } @@ -1552,4 +1596,4 @@ namespace dxvk { return D3D_OK; } -} +} \ No newline at end of file diff --git a/src/ddraw/d3d5/d3d5_device.h b/src/ddraw/d3d5/d3d5_device.h index ab571706830..e453d180792 100644 --- a/src/ddraw/d3d5/d3d5_device.h +++ b/src/ddraw/d3d5/d3d5_device.h @@ -5,8 +5,9 @@ #include "../ddraw_options.h" #include "../ddraw_caps.h" +#include "../d3d_common_device.h" + #include "../d3d_multithread.h" -#include "../ddraw_common_interface.h" #include "../../d3d9/d3d9_bridge.h" @@ -17,9 +18,11 @@ namespace dxvk { + class D3DCommonDevice; class DDrawCommonInterface; class DDrawSurface; class DDrawInterface; + class D3D3Device; /** * \brief D3D5 device implementation @@ -28,6 +31,7 @@ namespace dxvk { public: D3D5Device( + D3DCommonDevice* commonD3DDevice, Com&& d3d5DeviceProxy, D3D5Interface* pParent, D3DDEVICEDESC2 Desc, @@ -39,6 +43,10 @@ namespace dxvk { ~D3D5Device(); + ULONG STDMETHODCALLTYPE AddRef(); + + ULONG STDMETHODCALLTYPE Release(); + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject); HRESULT STDMETHODCALLTYPE GetCaps(D3DDEVICEDESC *hal_desc, D3DDEVICEDESC *hel_desc); @@ -103,6 +111,10 @@ namespace dxvk { void InitializeDS(); + D3DCommonDevice* GetCommonD3DDevice() { + return m_commonD3DDevice.ptr(); + } + D3DDeviceLock LockDevice() { return m_multithread.AcquireLock(); } @@ -111,10 +123,6 @@ namespace dxvk { m_bridge->SetLegacyLightsState(true, isD3DLight2); } - uint32_t GetTotalTextureMemory() const { - return m_totalMemory; - } - d3d9::D3DPRESENT_PARAMETERS GetPresentParameters() const { return m_params9; } @@ -152,8 +160,8 @@ namespace dxvk { inline HRESULT SetTextureInternal(DDrawSurface* surface, DWORD textureHandle); inline void RefreshLastUsedDevice() { - if (unlikely(m_commonIntf->GetD3D5Device() != this)) - m_commonIntf->SetD3D5Device(this); + if (unlikely(m_commonIntf->GetCommonD3DDevice() != m_commonD3DDevice.ptr())) + m_commonIntf->SetCommonD3DDevice(m_commonD3DDevice.ptr()); } inline void HandlePreDrawFlags(DWORD drawFlags, DWORD vertexTypeDesc) { @@ -203,15 +211,16 @@ namespace dxvk { static uint32_t s_deviceCount; uint32_t m_deviceCount = 0; - uint32_t m_totalMemory = 0; - DWORD m_lighting = FALSE; DDrawCommonInterface* m_commonIntf = nullptr; + Com m_commonD3DDevice; + Com m_bridge; - DWORD m_creationFlags9 = 0; + Com m_device3; + D3DMultithread m_multithread; d3d9::D3DPRESENT_PARAMETERS m_params9; diff --git a/src/ddraw/d3d5/d3d5_interface.cpp b/src/ddraw/d3d5/d3d5_interface.cpp index ca477f82d89..01d6c479caf 100644 --- a/src/ddraw/d3d5/d3d5_interface.cpp +++ b/src/ddraw/d3d5/d3d5_interface.cpp @@ -76,6 +76,8 @@ namespace dxvk { } HRESULT STDMETHODCALLTYPE D3D5Interface::QueryInterface(REFIID riid, void** ppvObject) { + Logger::debug(">>> D3D5Interface::QueryInterface"); + if (unlikely(ppvObject == nullptr)) return E_POINTER; @@ -236,8 +238,15 @@ namespace dxvk { InitReturnPtr(lplpDirect3DMaterial); + Com ddrawMaterial2Proxied; + HRESULT hr = m_proxy->CreateMaterial(&ddrawMaterial2Proxied, pUnkOuter); + if (unlikely(FAILED(hr))) { + Logger::err("D3D5Interface::CreateMaterial: Failed to create proxied material"); + return hr; + } + D3DMATERIALHANDLE handle = m_commonD3DIntf->GetNextMaterialHandle(); - Com d3d5Material = new D3D5Material(nullptr, this, handle); + Com d3d5Material = new D3D5Material(std::move(ddrawMaterial2Proxied), this, handle); m_commonD3DIntf->EmplaceMaterial(d3d5Material->GetCommonMaterial(), handle); *lplpDirect3DMaterial = d3d5Material.ref(); @@ -270,6 +279,9 @@ namespace dxvk { if (unlikely(lpD3DFDS->dwSize != sizeof(D3DFINDDEVICESEARCH))) return DDERR_INVALIDPARAMS; + if (unlikely(!IsValidFindDeviceResultSize(lpD3DFDR->dwSize))) + return DDERR_INVALIDPARAMS; + const D3DOptions* d3dOptions = m_commonIntf->GetOptions(); // Software emulation, this is expected to be exposed @@ -337,6 +349,24 @@ namespace dxvk { lpD3DFRD2.ddSwDesc = descRGB_HEL; } + memcpy(lpD3DFDR, &lpD3DFRD2, sizeof(D3DFINDDEVICERESULT2)); + } else if (lpD3DFDS->dwFlags & D3DFDS_COLORMODEL) { + Logger::debug("D3D5Interface::FindDevice: Matching by color model"); + + Logger::debug("D3D5Interface::FindDevice: Matched IID_IDirect3DHALDevice"); + lpD3DFRD2.guid = IID_IDirect3DHALDevice; + lpD3DFRD2.ddHwDesc = descHAL_HAL; + lpD3DFRD2.ddSwDesc = descHAL_HEL; + + memcpy(lpD3DFDR, &lpD3DFRD2, sizeof(D3DFINDDEVICERESULT2)); + } else if (lpD3DFDS->dwFlags == 0) { + Logger::debug("D3D5Interface::FindDevice: No matching criteria specified"); + + Logger::debug("D3D5Interface::FindDevice: Matched IID_IDirect3DHALDevice"); + lpD3DFRD2.guid = IID_IDirect3DHALDevice; + lpD3DFRD2.ddHwDesc = descHAL_HAL; + lpD3DFRD2.ddSwDesc = descHAL_HEL; + memcpy(lpD3DFDR, &lpD3DFRD2, sizeof(D3DFINDDEVICERESULT2)); } else { Logger::err("D3D5Interface::FindDevice: Unhandled matching type"); @@ -411,7 +441,7 @@ namespace dxvk { } } else { Logger::err("D3D5Interface::CreateDevice: Unwrapped surface passed as RT"); - return DDERR_GENERIC; + return DDERR_UNSUPPORTED; } } else { rt = static_cast(lpDDS); @@ -538,12 +568,12 @@ namespace dxvk { D3DDEVICEDESC2 desc5 = GetD3D5Caps(rclsidOverride, d3dOptions); try{ - Com device5 = new D3D5Device(std::move(d3d5DeviceProxy), this, desc5, + Com device5 = new D3D5Device(nullptr, std::move(d3d5DeviceProxy), this, desc5, rclsidOverride, params, std::move(device9), rt.ptr(), deviceCreationFlags9); - // Set the newly created D3D5 device on the common interface - m_commonIntf->SetD3D5Device(device5.ptr()); + // Set the common device on the common interface + m_commonIntf->SetCommonD3DDevice(device5->GetCommonD3DDevice()); // Now that we have a valid D3D9 device pointer, we can initialize the depth stencil (if any) device5->InitializeDS(); diff --git a/src/ddraw/d3d5/d3d5_material.cpp b/src/ddraw/d3d5/d3d5_material.cpp index a9dc4279670..ce3ac3ae14a 100644 --- a/src/ddraw/d3d5/d3d5_material.cpp +++ b/src/ddraw/d3d5/d3d5_material.cpp @@ -17,6 +17,8 @@ namespace dxvk { : DDrawWrappedObject(pParent, std::move(proxyMaterial), nullptr) { m_commonMaterial = new D3DCommonMaterial(handle); + m_commonMaterial->SetD3D5Material(this); + m_materialCount = ++s_materialCount; Logger::debug(str::format("D3D5Material: Created a new material nr. [[2-", m_materialCount, "]]")); @@ -25,6 +27,8 @@ namespace dxvk { D3D5Material::~D3D5Material() { m_parent->GetCommonD3DInterface()->ReleaseMaterialHandle(m_commonMaterial->GetMaterialHandle()); + m_commonMaterial->SetD3D5Material(nullptr); + Logger::debug(str::format("D3D5Material: Material nr. [[2-", m_materialCount, "]] bites the dust")); } @@ -34,6 +38,12 @@ namespace dxvk { if (unlikely(data == nullptr)) 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); + if (unlikely(FAILED(hr))) + Logger::warn("D3D5Material::SetMaterial: Failed to set the proxied material"); + d3d9::D3DMATERIAL9* material9 = m_commonMaterial->GetD3D9Material(); material9->Diffuse = data->dcvDiffuse; @@ -52,7 +62,7 @@ namespace dxvk { Logger::debug(str::format(" Power: ", material9->Power)); // Update the D3D9 material directly if it's actively being used - D3D5Device* device5 = m_parent->GetCommonInterface()->GetD3D5Device(); + D3D5Device* device5 = m_parent->GetCommonInterface()->GetCommonD3DDevice()->GetD3D5Device(); if (likely(device5 != nullptr)) { D3DMATERIALHANDLE currentHandle = device5->GetCurrentMaterialHandle(); if (currentHandle == handle) { diff --git a/src/ddraw/d3d5/d3d5_viewport.cpp b/src/ddraw/d3d5/d3d5_viewport.cpp index 89e685d5517..863c27710d5 100644 --- a/src/ddraw/d3d5/d3d5_viewport.cpp +++ b/src/ddraw/d3d5/d3d5_viewport.cpp @@ -27,6 +27,9 @@ namespace dxvk { if (m_commonViewport == nullptr) m_commonViewport = new D3DCommonViewport(m_parent->GetCommonD3DInterface()); + if (m_commonViewport->GetOrigin() == nullptr) + m_commonViewport->SetOrigin(this); + m_commonViewport->SetD3D5Viewport(this); m_viewportCount = ++s_viewportCount; @@ -42,11 +45,34 @@ namespace dxvk { light->SetViewport5(nullptr); } + if (m_commonViewport->GetOrigin() == this) + m_commonViewport->SetOrigin(nullptr); + m_commonViewport->SetD3D5Viewport(nullptr); Logger::debug(str::format("D3D5Viewport: Viewport nr. [[2-", m_viewportCount, "]] bites the dust")); } + // Interlocked refcount with the origin viewport + ULONG STDMETHODCALLTYPE D3D5Viewport::AddRef() { + IUnknown* origin = m_commonViewport->GetOrigin(); + if (unlikely(origin != nullptr && origin != this)) { + return origin->AddRef(); + } else { + return ComObjectClamp::AddRef(); + } + } + + // Interlocked refcount with the origin viewport + ULONG STDMETHODCALLTYPE D3D5Viewport::Release() { + IUnknown* origin = m_commonViewport->GetOrigin(); + if (unlikely(origin != nullptr && origin != this)) { + return origin->Release(); + } else { + return ComObjectClamp::Release(); + } + } + HRESULT STDMETHODCALLTYPE D3D5Viewport::QueryInterface(REFIID riid, void** ppvObject) { Logger::debug(">>> D3D5Viewport::QueryInterface"); @@ -58,7 +84,7 @@ namespace dxvk { // Some games query for legacy viewport interfaces if (unlikely(riid == __uuidof(IDirect3DViewport))) { if (m_commonViewport->GetD3D3Viewport() != nullptr) { - Logger::debug("D3D6Viewport::QueryInterface: Query for existing IDirect3DViewport"); + Logger::debug("D3D5Viewport::QueryInterface: Query for existing IDirect3DViewport"); return m_commonViewport->GetD3D3Viewport()->QueryInterface(riid, ppvObject); } @@ -69,13 +95,16 @@ namespace dxvk { if (unlikely(FAILED(hr))) return hr; - *ppvObject = ref(new D3D3Viewport(m_commonViewport.ptr(), std::move(ppvProxyObject), nullptr)); + m_viewport3 = new D3D3Viewport(m_commonViewport.ptr(), std::move(ppvProxyObject), nullptr); + + // On native this is the same object, so no need to ref + *ppvObject = m_viewport3.ptr(); return S_OK; } if (unlikely(riid == __uuidof(IDirect3DViewport3))) { if (m_commonViewport->GetD3D6Viewport() != nullptr) { - Logger::debug("D3D6Viewport::QueryInterface: Query for existing IDirect3DViewport3"); + Logger::debug("D3D5Viewport::QueryInterface: Query for existing IDirect3DViewport3"); return m_commonViewport->GetD3D6Viewport()->QueryInterface(riid, ppvObject); } @@ -86,7 +115,10 @@ namespace dxvk { if (unlikely(FAILED(hr))) return hr; - *ppvObject = ref(new D3D6Viewport(m_commonViewport.ptr(), std::move(ppvProxyObject), nullptr)); + m_viewport6 = new D3D6Viewport(m_commonViewport.ptr(), std::move(ppvProxyObject), nullptr); + + // On native this is the same object, so no need to ref + *ppvObject = m_viewport6.ptr(); return S_OK; } @@ -128,11 +160,13 @@ namespace dxvk { data->dvMinZ = viewport9->MinZ; data->dvMaxZ = viewport9->MaxZ; - data->dvMaxX = 1.0f; - data->dvMaxY = 1.0f; D3DVECTOR* legacyScale = m_commonViewport->GetLegacyScale(); data->dvScaleX = legacyScale->x * (float)data->dwWidth / 2.0f; data->dvScaleY = legacyScale->y * (float)data->dwHeight / 2.0f; + D3DVECTOR* legacyClip = m_commonViewport->GetLegacyClip(); + // Don't compact these because precision issues can affect the outcome + data->dvMaxX = 2.0f / legacyScale->x * (1.0f + (legacyClip->x + 1.0f) / -2.0f); // dvClipX + dvClipWidth + data->dvMaxY = 2.0f / legacyScale->y * (legacyClip->y - 1.0f) / -2.0f; // dvClipY return D3D_OK; } @@ -185,8 +219,35 @@ namespace dxvk { } HRESULT STDMETHODCALLTYPE D3D5Viewport::TransformVertices(DWORD vertex_count, D3DTRANSFORMDATA *data, DWORD flags, DWORD *offscreen) { - Logger::debug("<<< D3D5Viewport::TransformVertices: Proxy"); - return m_proxy->TransformVertices(vertex_count, data, flags, offscreen); + Logger::debug(">>> D3D5Viewport::TransformVertices"); + + if (unlikely(!m_commonViewport->HasDevice())) { + Logger::warn("D3D5Viewport::TransformVertices: Viewport isn't attached to a device"); + return D3DERR_VIEWPORTHASNODEVICE; + } + + d3d9::IDirect3DDevice9* d3d9Device = m_commonViewport->GetD3D9Device(); + + // Temporarily activate this viewport, if not already active + d3d9::D3DVIEWPORT9 currentViewport9; + if (!m_commonViewport->IsCurrentViewport()) { + D3D5Viewport* currentViewport = m_commonViewport->GetCurrentD3D5Viewport(); + if (currentViewport != nullptr) { + currentViewport9 = *currentViewport->GetCommonViewport()->GetD3D9Viewport(); + } else { + d3d9Device->GetViewport(¤tViewport9); + } + d3d9Device->SetViewport(m_commonViewport->GetD3D9Viewport()); + } + + HRESULT hr = m_commonViewport->TransformVertices(vertex_count, data, flags, offscreen); + + // Restore the previously active viewport + if (!m_commonViewport->IsCurrentViewport()) { + d3d9Device->SetViewport(¤tViewport9); + } + + return hr; } // Docs state: "The IDirect3DViewport2::LightElements method is not currently implemented." @@ -206,6 +267,16 @@ namespace dxvk { if (unlikely(commonMaterial == nullptr)) return DDERR_INVALIDPARAMS; + // We still need to proxy this call to DDraw for + // proxied clear colors to be accurate + D3D5Device* device5 = m_commonViewport->GetD3D5Device(); + if (likely(device5 != nullptr)) { + D3DMATERIALHANDLE proxyHandle = commonMaterial->GetProxiedMaterialHandle(device5->GetProxied()); + HRESULT hr = m_proxy->SetBackground(proxyHandle); + if (unlikely(FAILED(hr))) + Logger::warn("D3D5Viewport::SetBackground: Failed to set the proxied viewport background"); + } + m_commonViewport->MarkMaterialAsSet(); // Cache only the set material handle, as its color can @@ -221,8 +292,7 @@ namespace dxvk { if (unlikely(material == nullptr || valid == nullptr)) return DDERR_INVALIDPARAMS; - if (likely(m_commonViewport->IsMaterialSet())) - *material = m_commonViewport->GetMaterialHandle(); + *material = m_commonViewport->GetMaterialHandle(); *valid = m_commonViewport->IsMaterialSet(); return D3D_OK; diff --git a/src/ddraw/d3d5/d3d5_viewport.h b/src/ddraw/d3d5/d3d5_viewport.h index 6b9248c82c5..4612dd40dc0 100644 --- a/src/ddraw/d3d5/d3d5_viewport.h +++ b/src/ddraw/d3d5/d3d5_viewport.h @@ -12,6 +12,9 @@ namespace dxvk { class D3DLight; + class D3D6Viewport; + class D3D3Viewport; + class D3D5Viewport final : public DDrawWrappedObject { public: @@ -23,6 +26,10 @@ namespace dxvk { ~D3D5Viewport(); + ULONG STDMETHODCALLTYPE AddRef(); + + ULONG STDMETHODCALLTYPE Release(); + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject); HRESULT STDMETHODCALLTYPE Initialize(LPDIRECT3D lpDirect3D); @@ -72,6 +79,9 @@ namespace dxvk { Com m_commonViewport; + Com m_viewport6; + Com m_viewport3; + }; } diff --git a/src/ddraw/d3d6/d3d6_buffer.cpp b/src/ddraw/d3d6/d3d6_buffer.cpp index 2c7f943ba48..661baee06e5 100644 --- a/src/ddraw/d3d6/d3d6_buffer.cpp +++ b/src/ddraw/d3d6/d3d6_buffer.cpp @@ -1,5 +1,7 @@ #include "d3d6_buffer.h" +#include "../d3d_common_device.h" + #include "../ddraw_util.h" #include "../d3d_multithread.h" @@ -191,4 +193,18 @@ namespace dxvk { return DD_OK; } + void D3D6VertexBuffer::RefreshD3D6Device() { + D3DCommonDevice* commonDevice = m_commonIntf->GetCommonD3DDevice(); + + D3D6Device* d3d6Device = commonDevice != nullptr ? commonDevice->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; + } + m_d3d6Device = d3d6Device; + } + } + } diff --git a/src/ddraw/d3d6/d3d6_buffer.h b/src/ddraw/d3d6/d3d6_buffer.h index fcd39a07ecf..9881e24205e 100644 --- a/src/ddraw/d3d6/d3d6_buffer.h +++ b/src/ddraw/d3d6/d3d6_buffer.h @@ -33,6 +33,10 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE Optimize(LPDIRECT3DDEVICE3 lpD3DDevice, DWORD dwFlags); + HRESULT InitializeD3D9(); + + void RefreshD3D6Device(); + DWORD GetFVF() const { return m_desc.dwFVF; } @@ -53,20 +57,6 @@ namespace dxvk { return m_d3d6Device; } - void RefreshD3D6Device() { - D3D6Device* d3d6Device = m_commonIntf->GetD3D6Device(); - 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; - } - m_d3d6Device = d3d6Device; - } - } - - HRESULT InitializeD3D9(); - private: inline bool IsOptimized() const { diff --git a/src/ddraw/d3d6/d3d6_device.cpp b/src/ddraw/d3d6/d3d6_device.cpp index b39f4fe34fb..0cfa60d3e34 100644 --- a/src/ddraw/d3d6/d3d6_device.cpp +++ b/src/ddraw/d3d6/d3d6_device.cpp @@ -1,19 +1,23 @@ #include "d3d6_device.h" +#include "../ddraw_common_interface.h" + #include "d3d6_buffer.h" #include "d3d6_texture.h" +#include "../d3d5/d3d5_device.h" +#include "../d3d3/d3d3_device.h" + #include "../ddraw4/ddraw4_surface.h" #include -#include -#include "../../util/util_bit.h" namespace dxvk { uint32_t D3D6Device::s_deviceCount = 0; D3D6Device::D3D6Device( + D3DCommonDevice* commonD3DDevice, Com&& d3d6DeviceProxy, D3D6Interface* pParent, D3DDEVICEDESC Desc, @@ -23,12 +27,20 @@ namespace dxvk { DDraw4Surface* pSurface, DWORD CreationFlags9) : DDrawWrappedObject(pParent, std::move(d3d6DeviceProxy), std::move(pDevice9)) - , m_commonIntf ( pParent->GetCommonInterface() ) + , 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(); + } else if (m_commonD3DDevice != nullptr) { + m_commonIntf = m_commonD3DDevice->GetCommonInterface(); + } else { + 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!"); @@ -39,24 +51,32 @@ namespace dxvk { throw DxvkError("D3D6Device: ERROR! Failed to initialize D3D9 index buffers."); } - m_totalMemory = m_bridge->DetermineInitialTextureMemory(); + if (likely(m_commonD3DDevice == nullptr)) { + m_commonD3DDevice = new D3DCommonDevice(m_commonIntf, CreationFlags9, + m_bridge->DetermineInitialTextureMemory()); - m_textures.fill(nullptr); + const D3DOptions* d3dOptions = m_commonIntf->GetOptions(); - 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); + } - if (unlikely(d3dOptions->emulateFSAA == FSAAEmulation::Forced)) { - Logger::warn("D3D6Device: Force enabling AA"); - m_d3d9->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); } - // 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); + if (m_commonD3DDevice->GetOrigin() == nullptr) + m_commonD3DDevice->SetOrigin(this); + + m_commonD3DDevice->SetD3D6Device(this); + + m_textures.fill(nullptr); m_deviceCount = ++s_deviceCount; @@ -80,13 +100,94 @@ namespace dxvk { viewport->GetCommonViewport()->SetD3D6Device(nullptr); } - // Clear the common interface device pointer if it points to this device - if (m_commonIntf->GetD3D6Device() == this) - m_commonIntf->SetD3D6Device(nullptr); + if (m_commonD3DDevice->GetD3D6Device() == this) + m_commonD3DDevice->SetD3D6Device(nullptr); + + if (m_commonD3DDevice->GetOrigin() == this) + m_commonD3DDevice->SetOrigin(nullptr); Logger::debug(str::format("D3D6Device: Device nr. ((3-", m_deviceCount, ")) bites the dust")); } + // Interlocked refcount with the origin device + ULONG STDMETHODCALLTYPE D3D6Device::AddRef() { + IUnknown* origin = m_commonD3DDevice->GetOrigin(); + if (unlikely(origin != nullptr && origin != this)) { + return origin->AddRef(); + } else { + return ComObjectClamp::AddRef(); + } + } + + // Interlocked refcount with the origin device + ULONG STDMETHODCALLTYPE D3D6Device::Release() { + IUnknown* origin = m_commonD3DDevice->GetOrigin(); + if (unlikely(origin != nullptr && origin != this)) { + return origin->Release(); + } else { + return ComObjectClamp::Release(); + } + } + + HRESULT STDMETHODCALLTYPE D3D6Device::QueryInterface(REFIID riid, void** ppvObject) { + Logger::debug(">>> D3D6Device::QueryInterface"); + + if (unlikely(ppvObject == nullptr)) + return E_POINTER; + + InitReturnPtr(ppvObject); + + if (riid == __uuidof(IDirect3DDevice)) { + if (m_commonD3DDevice->GetD3D3Device() != nullptr) { + Logger::debug("D3D6Device::QueryInterface: Query for existing IDirect3DDevice"); + return m_commonD3DDevice->GetD3D3Device()->QueryInterface(riid, ppvObject); + } + + Logger::debug("D3D6Device::QueryInterface: Query for IDirect3DDevice"); + + 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 + + // 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()); + + // 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(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; + } + + 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 D3D6Device::GetCaps(D3DDEVICEDESC *hal_desc, D3DDEVICEDESC *hel_desc) { Logger::debug(">>> D3D6Device::GetCaps"); @@ -396,14 +497,16 @@ namespace dxvk { Logger::debug(">>> D3D6Device::GetCurrentViewport"); + // This does indeed return D3DERR_NOCURRENTVIEWPORT... if (unlikely(viewport == nullptr)) return D3DERR_NOCURRENTVIEWPORT; - InitReturnPtr(viewport); - + // Current viewport is checked before initializing the return pointer if (unlikely(m_currentViewport == nullptr)) return D3DERR_NOCURRENTVIEWPORT; + InitReturnPtr(viewport); + *viewport = m_currentViewport.ref(); return D3D_OK; @@ -421,7 +524,7 @@ namespace dxvk { if (unlikely(!m_commonIntf->IsWrappedSurface(surface))) { Logger::err("D3D6Device::SetRenderTarget: Received an unwrapped RT"); - return DDERR_GENERIC; + return DDERR_UNSUPPORTED; } DDraw4Surface* rt6 = static_cast(surface); @@ -1226,12 +1329,6 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE D3D6Device::SetTransform(D3DTRANSFORMSTATETYPE state, D3DMATRIX *matrix) { Logger::debug(">>> D3D6Device::SetTransform"); - - // Need to also proxy for viewport TransformVertices calls to work - HRESULT hr = m_proxy->SetTransform(state, matrix); - if (unlikely(FAILED(hr))) - return hr; - return m_d3d9->SetTransform(ConvertTransformState(state), matrix); } @@ -1242,12 +1339,6 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE D3D6Device::MultiplyTransform(D3DTRANSFORMSTATETYPE state, D3DMATRIX *matrix) { Logger::debug(">>> D3D6Device::MultiplyTransform"); - - // Need to also proxy for viewport TransformVertices calls to work - HRESULT hr = m_proxy->MultiplyTransform(state, matrix); - if (unlikely(FAILED(hr))) - return hr; - return m_d3d9->MultiplyTransform(ConvertTransformState(state), matrix); } @@ -1848,4 +1939,4 @@ namespace dxvk { } } -} +} \ No newline at end of file diff --git a/src/ddraw/d3d6/d3d6_device.h b/src/ddraw/d3d6/d3d6_device.h index ea45dc85280..31f23a4b837 100644 --- a/src/ddraw/d3d6/d3d6_device.h +++ b/src/ddraw/d3d6/d3d6_device.h @@ -6,8 +6,9 @@ #include "../ddraw_util.h" #include "../ddraw_caps.h" +#include "../d3d_common_device.h" + #include "../d3d_multithread.h" -#include "../ddraw_common_interface.h" #include "../../d3d9/d3d9_bridge.h" @@ -19,10 +20,12 @@ namespace dxvk { + class D3DCommonDevice; class DDrawCommonInterface; class DDraw4Surface; class DDraw4Interface; class D3D6Texture; + class D3D3Device; /** * \brief D3D6 device implementation @@ -31,6 +34,7 @@ namespace dxvk { public: D3D6Device( + D3DCommonDevice* commonD3DDevice, Com&& d3d6DeviceProxy, D3D6Interface* pParent, D3DDEVICEDESC Desc, @@ -42,6 +46,12 @@ namespace dxvk { ~D3D6Device(); + ULONG STDMETHODCALLTYPE AddRef(); + + ULONG STDMETHODCALLTYPE Release(); + + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject); + HRESULT STDMETHODCALLTYPE GetCaps(D3DDEVICEDESC *hal_desc, D3DDEVICEDESC *hel_desc); HRESULT STDMETHODCALLTYPE GetStats(D3DSTATS *stats); @@ -124,6 +134,10 @@ namespace dxvk { HRESULT ResetD3D9Swapchain(d3d9::D3DPRESENT_PARAMETERS* params); + D3DCommonDevice* GetCommonD3DDevice() { + return m_commonD3DDevice.ptr(); + } + D3DDeviceLock LockDevice() { return m_multithread.AcquireLock(); } @@ -132,10 +146,6 @@ namespace dxvk { m_bridge->SetLegacyLightsState(true, isD3DLight2); } - uint32_t GetTotalTextureMemory() const { - return m_totalMemory; - } - d3d9::D3DPRESENT_PARAMETERS GetPresentParameters() const { return m_params9; } @@ -160,6 +170,10 @@ namespace dxvk { return m_materialHandle; } + void SetCurrentMaterialHandle(D3DMATERIALHANDLE handle) { + m_materialHandle = handle; + } + private: inline HRESULT InitializeIndexBuffers(); @@ -179,8 +193,8 @@ namespace dxvk { } inline void RefreshLastUsedDevice() { - if (unlikely(m_commonIntf->GetD3D6Device() != this)) - m_commonIntf->SetD3D6Device(this); + if (unlikely(m_commonIntf->GetCommonD3DDevice() != m_commonD3DDevice.ptr())) + m_commonIntf->SetCommonD3DDevice(m_commonD3DDevice.ptr()); } inline void HandlePreDrawFlags(DWORD drawFlags, DWORD vertexTypeDesc) { @@ -231,14 +245,16 @@ namespace dxvk { static uint32_t s_deviceCount; uint32_t m_deviceCount = 0; - uint32_t m_totalMemory = 0; - DWORD m_lighting = FALSE; DDrawCommonInterface* m_commonIntf = nullptr; + Com m_commonD3DDevice; + Com m_bridge; + Com m_device3; + D3DMultithread m_multithread; d3d9::D3DPRESENT_PARAMETERS m_params9; diff --git a/src/ddraw/d3d6/d3d6_interface.cpp b/src/ddraw/d3d6/d3d6_interface.cpp index 490d1f1372c..2f7e970c54f 100644 --- a/src/ddraw/d3d6/d3d6_interface.cpp +++ b/src/ddraw/d3d6/d3d6_interface.cpp @@ -74,6 +74,8 @@ namespace dxvk { } HRESULT STDMETHODCALLTYPE D3D6Interface::QueryInterface(REFIID riid, void** ppvObject) { + Logger::debug(">>> D3D6Interface::QueryInterface"); + if (unlikely(ppvObject == nullptr)) return E_POINTER; @@ -182,8 +184,15 @@ namespace dxvk { InitReturnPtr(lplpDirect3DMaterial); + Com ddrawMaterial3Proxied; + HRESULT hr = m_proxy->CreateMaterial(&ddrawMaterial3Proxied, pUnkOuter); + if (unlikely(FAILED(hr))) { + Logger::err("D3D6Interface::CreateMaterial: Failed to create proxied material"); + return hr; + } + D3DMATERIALHANDLE handle = m_commonD3DIntf->GetNextMaterialHandle(); - Com d3d6Material = new D3D6Material(nullptr, this, handle); + Com d3d6Material = new D3D6Material(std::move(ddrawMaterial3Proxied), this, handle); m_commonD3DIntf->EmplaceMaterial(d3d6Material->GetCommonMaterial(), handle); *lplpDirect3DMaterial = d3d6Material.ref(); @@ -216,6 +225,9 @@ namespace dxvk { if (unlikely(lpD3DFDS->dwSize != sizeof(D3DFINDDEVICESEARCH))) return DDERR_INVALIDPARAMS; + if (unlikely(!IsValidFindDeviceResultSize(lpD3DFDR->dwSize))) + return DDERR_INVALIDPARAMS; + const D3DOptions* d3dOptions = m_commonIntf->GetOptions(); // Software emulation, this is expected to be exposed @@ -253,9 +265,8 @@ namespace dxvk { if (lpD3DFDS->dwFlags & D3DFDS_GUID) { Logger::debug("D3D6Interface::FindDevice: Matching by device GUID"); - if (lpD3DFDS->guid == IID_IDirect3DRGBDevice || - lpD3DFDS->guid == IID_IDirect3DMMXDevice || - lpD3DFDS->guid == IID_IDirect3DRampDevice) { + // IID_IDirect3DRampDevice and IID_IDirect3DMMXDevice return DDERR_NOTFOUND in D3D6 + if (lpD3DFDS->guid == IID_IDirect3DRGBDevice) { Logger::debug("D3D6Interface::FindDevice: Matched IID_IDirect3DRGBDevice"); lpD3DFDR->guid = IID_IDirect3DRGBDevice; lpD3DFDR->ddHwDesc = descRGB_HAL; @@ -283,6 +294,20 @@ namespace dxvk { lpD3DFDR->ddHwDesc = descRGB_HAL; lpD3DFDR->ddSwDesc = descRGB_HEL; } + } else if (lpD3DFDS->dwFlags & D3DFDS_COLORMODEL) { + Logger::debug("D3D6Interface::FindDevice: Matching by color model"); + + Logger::debug("D3D6Interface::FindDevice: Matched IID_IDirect3DHALDevice"); + lpD3DFDR->guid = IID_IDirect3DHALDevice; + lpD3DFDR->ddHwDesc = descHAL_HAL; + lpD3DFDR->ddSwDesc = descHAL_HEL; + } else if (lpD3DFDS->dwFlags == 0) { + Logger::debug("D3D6Interface::FindDevice: No matching criteria specified"); + + Logger::debug("D3D6Interface::FindDevice: Matched IID_IDirect3DHALDevice"); + lpD3DFDR->guid = IID_IDirect3DHALDevice; + lpD3DFDR->ddHwDesc = descHAL_HAL; + lpD3DFDR->ddSwDesc = descHAL_HEL; } else { Logger::err("D3D6Interface::FindDevice: Unhandled matching type"); return DDERR_NOTFOUND; @@ -337,7 +362,7 @@ namespace dxvk { Com rt4; if (unlikely(!m_commonIntf->IsWrappedSurface(lpDDS))) { Logger::err("D3D6Interface::CreateDevice: Unwrapped surface passed as RT"); - return DDERR_GENERIC; + return DDERR_UNSUPPORTED; } else { rt4 = static_cast(lpDDS); } @@ -466,12 +491,12 @@ namespace dxvk { D3DDEVICEDESC desc6 = GetD3D6Caps(rclsidOverride, d3dOptions); try{ - Com device6 = new D3D6Device(std::move(d3d6DeviceProxy), this, desc6, + Com device6 = new D3D6Device(nullptr, std::move(d3d6DeviceProxy), this, desc6, rclsidOverride, params, std::move(device9), rt4.ptr(), deviceCreationFlags9); - // Set the newly created D3D6 device on the common interface - m_commonIntf->SetD3D6Device(device6.ptr()); + // Set the common device on the common interface + m_commonIntf->SetCommonD3DDevice(device6->GetCommonD3DDevice()); // Now that we have a valid D3D9 device pointer, we can initialize the depth stencil (if any) device6->InitializeDS(); @@ -549,11 +574,12 @@ namespace dxvk { if (unlikely(FAILED(hr))) return hr; - D3D6Device* d3d6Device = m_commonIntf->GetD3D6Device(); - if (likely(d3d6Device != nullptr)) { - D3DDeviceLock lock = d3d6Device->LockDevice(); + D3DCommonDevice* commonDevice = m_commonIntf->GetCommonD3DDevice(); + if (likely(commonDevice != nullptr)) { + d3d9::IDirect3DDevice9* d3d9Device = commonDevice->GetD3D9Device(); - HRESULT hr9 = d3d6Device->GetD3D9()->EvictManagedResources(); + // Note: This doesn't do anything in the D3D9 backend at the moment + HRESULT hr9 = d3d9Device->EvictManagedResources(); if (unlikely(FAILED(hr9))) { Logger::err("D3D6Interface::EvictManagedTextures: Failed D3D9 managed resource eviction"); return hr9; diff --git a/src/ddraw/d3d6/d3d6_material.cpp b/src/ddraw/d3d6/d3d6_material.cpp index 040318f43b3..3e5d8b0702d 100644 --- a/src/ddraw/d3d6/d3d6_material.cpp +++ b/src/ddraw/d3d6/d3d6_material.cpp @@ -17,6 +17,8 @@ namespace dxvk { : DDrawWrappedObject(pParent, std::move(proxyMaterial), nullptr) { m_commonMaterial = new D3DCommonMaterial(handle); + m_commonMaterial->SetD3D6Material(this); + m_materialCount = ++s_materialCount; Logger::debug(str::format("D3D6Material: Created a new material nr. [[3-", m_materialCount, "]]")); @@ -25,6 +27,8 @@ namespace dxvk { D3D6Material::~D3D6Material() { m_parent->GetCommonD3DInterface()->ReleaseMaterialHandle(m_commonMaterial->GetMaterialHandle()); + m_commonMaterial->SetD3D6Material(nullptr); + Logger::debug(str::format("D3D6Material: Material nr. [[3-", m_materialCount, "]] bites the dust")); } @@ -34,6 +38,12 @@ namespace dxvk { if (unlikely(data == nullptr)) 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); + if (unlikely(FAILED(hr))) + Logger::warn("D3D6Material::SetMaterial: Failed to set the proxied material"); + d3d9::D3DMATERIAL9* material9 = m_commonMaterial->GetD3D9Material(); material9->Diffuse = data->dcvDiffuse; @@ -52,7 +62,7 @@ namespace dxvk { Logger::debug(str::format(" Power: ", material9->Power)); // Update the D3D9 material directly if it's actively being used - D3D6Device* device6 = m_parent->GetCommonInterface()->GetD3D6Device(); + D3D6Device* device6 = m_parent->GetCommonInterface()->GetCommonD3DDevice()->GetD3D6Device(); if (likely(device6 != nullptr)) { D3DMATERIALHANDLE currentHandle = device6->GetCurrentMaterialHandle(); if (currentHandle == handle) { diff --git a/src/ddraw/d3d6/d3d6_viewport.cpp b/src/ddraw/d3d6/d3d6_viewport.cpp index 8dfdc592241..03e0c8076f9 100644 --- a/src/ddraw/d3d6/d3d6_viewport.cpp +++ b/src/ddraw/d3d6/d3d6_viewport.cpp @@ -27,6 +27,9 @@ namespace dxvk { if (m_commonViewport == nullptr) m_commonViewport = new D3DCommonViewport(m_parent->GetCommonD3DInterface()); + if (m_commonViewport->GetOrigin() == nullptr) + m_commonViewport->SetOrigin(this); + m_commonViewport->SetD3D6Viewport(this); m_viewportCount = ++s_viewportCount; @@ -42,11 +45,34 @@ namespace dxvk { light->SetViewport6(nullptr); } + if (m_commonViewport->GetOrigin() == this) + m_commonViewport->SetOrigin(nullptr); + m_commonViewport->SetD3D6Viewport(nullptr); Logger::debug(str::format("D3D6Viewport: Viewport nr. [[3-", m_viewportCount, "]] bites the dust")); } + // Interlocked refcount with the origin viewport + ULONG STDMETHODCALLTYPE D3D6Viewport::AddRef() { + IUnknown* origin = m_commonViewport->GetOrigin(); + if (unlikely(origin != nullptr && origin != this)) { + return origin->AddRef(); + } else { + return ComObjectClamp::AddRef(); + } + } + + // Interlocked refcount with the origin viewport + ULONG STDMETHODCALLTYPE D3D6Viewport::Release() { + IUnknown* origin = m_commonViewport->GetOrigin(); + if (unlikely(origin != nullptr && origin != this)) { + return origin->Release(); + } else { + return ComObjectClamp::Release(); + } + } + HRESULT STDMETHODCALLTYPE D3D6Viewport::QueryInterface(REFIID riid, void** ppvObject) { Logger::debug(">>> D3D6Viewport::QueryInterface"); @@ -69,7 +95,10 @@ namespace dxvk { if (unlikely(FAILED(hr))) return hr; - *ppvObject = ref(new D3D3Viewport(m_commonViewport.ptr(), std::move(ppvProxyObject), nullptr)); + m_viewport3 = new D3D3Viewport(m_commonViewport.ptr(), std::move(ppvProxyObject), nullptr); + + // On native this is the same object, so no need to ref + *ppvObject = m_viewport3.ptr(); return S_OK; } @@ -86,7 +115,10 @@ namespace dxvk { if (unlikely(FAILED(hr))) return hr; - *ppvObject = ref(new D3D5Viewport(m_commonViewport.ptr(), std::move(ppvProxyObject), nullptr)); + m_viewport5 = new D3D5Viewport(m_commonViewport.ptr(), std::move(ppvProxyObject), nullptr); + + // On native this is the same object, so no need to ref + *ppvObject = m_viewport5.ptr(); return S_OK; } @@ -127,12 +159,13 @@ namespace dxvk { data->dwHeight = viewport9->Height; data->dvMinZ = viewport9->MinZ; data->dvMaxZ = viewport9->MaxZ; - - data->dvMaxX = 1.0f; - data->dvMaxY = 1.0f; D3DVECTOR* legacyScale = m_commonViewport->GetLegacyScale(); data->dvScaleX = legacyScale->x * (float)data->dwWidth / 2.0f; data->dvScaleY = legacyScale->y * (float)data->dwHeight / 2.0f; + D3DVECTOR* legacyClip = m_commonViewport->GetLegacyClip(); + // Don't compact these because precision issues can affect the outcome + data->dvMaxX = 2.0f / legacyScale->x * (1.0f + (legacyClip->x + 1.0f) / -2.0f); // dvClipX + dvClipWidth + data->dvMaxY = 2.0f / legacyScale->y * (legacyClip->y - 1.0f) / -2.0f; // dvClipY return D3D_OK; } @@ -185,8 +218,35 @@ namespace dxvk { } HRESULT STDMETHODCALLTYPE D3D6Viewport::TransformVertices(DWORD vertex_count, D3DTRANSFORMDATA *data, DWORD flags, DWORD *offscreen) { - Logger::debug("<<< D3D6Viewport::TransformVertices: Proxy"); - return m_proxy->TransformVertices(vertex_count, data, flags, offscreen); + Logger::debug(">>> D3D6Viewport::TransformVertices"); + + if (unlikely(!m_commonViewport->HasDevice())) { + Logger::warn("D3D6Viewport::TransformVertices: Viewport isn't attached to a device"); + return D3DERR_VIEWPORTHASNODEVICE; + } + + d3d9::IDirect3DDevice9* d3d9Device = m_commonViewport->GetD3D9Device(); + + // Temporarily activate this viewport, if not already active + d3d9::D3DVIEWPORT9 currentViewport9; + if (!m_commonViewport->IsCurrentViewport()) { + D3D6Viewport* currentViewport = m_commonViewport->GetCurrentD3D6Viewport(); + if (currentViewport != nullptr) { + currentViewport9 = *currentViewport->GetCommonViewport()->GetD3D9Viewport(); + } else { + d3d9Device->GetViewport(¤tViewport9); + } + d3d9Device->SetViewport(m_commonViewport->GetD3D9Viewport()); + } + + HRESULT hr = m_commonViewport->TransformVertices(vertex_count, data, flags, offscreen); + + // Restore the previously active viewport + if (!m_commonViewport->IsCurrentViewport()) { + d3d9Device->SetViewport(¤tViewport9); + } + + return hr; } // Docs state: "The IDirect3DViewport3::LightElements method is not currently implemented." @@ -206,6 +266,16 @@ namespace dxvk { if (unlikely(commonMaterial == nullptr)) return DDERR_INVALIDPARAMS; + // We still need to proxy this call to DDraw for + // proxied clear colors to be accurate + D3D6Device* device6 = m_commonViewport->GetD3D6Device(); + if (likely(device6 != nullptr)) { + D3DMATERIALHANDLE proxyHandle = commonMaterial->GetProxiedMaterialHandle(device6->GetProxied()); + HRESULT hr = m_proxy->SetBackground(proxyHandle); + if (unlikely(FAILED(hr))) + Logger::warn("D3D6Viewport::SetBackground: Failed to set the proxied viewport background"); + } + m_commonViewport->MarkMaterialAsSet(); // Cache only the set material handle, as its color can @@ -221,8 +291,7 @@ namespace dxvk { if (unlikely(material == nullptr || valid == nullptr)) return DDERR_INVALIDPARAMS; - if (likely(m_commonViewport->IsMaterialSet())) - *material = m_commonViewport->GetMaterialHandle(); + *material = m_commonViewport->GetMaterialHandle(); *valid = m_commonViewport->IsMaterialSet(); return D3D_OK; diff --git a/src/ddraw/d3d6/d3d6_viewport.h b/src/ddraw/d3d6/d3d6_viewport.h index 750549cb9ff..35d033ead70 100644 --- a/src/ddraw/d3d6/d3d6_viewport.h +++ b/src/ddraw/d3d6/d3d6_viewport.h @@ -12,6 +12,9 @@ namespace dxvk { class D3DLight; + class D3D5Viewport; + class D3D3Viewport; + class D3D6Viewport final : public DDrawWrappedObject { public: @@ -23,6 +26,10 @@ namespace dxvk { ~D3D6Viewport(); + ULONG STDMETHODCALLTYPE AddRef(); + + ULONG STDMETHODCALLTYPE Release(); + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject); HRESULT STDMETHODCALLTYPE Initialize(IDirect3D *d3d); @@ -78,6 +85,9 @@ namespace dxvk { Com m_commonViewport; + Com m_viewport5; + Com m_viewport3; + }; } diff --git a/src/ddraw/d3d7/d3d7_buffer.cpp b/src/ddraw/d3d7/d3d7_buffer.cpp index 70ae307852c..45c431784d0 100644 --- a/src/ddraw/d3d7/d3d7_buffer.cpp +++ b/src/ddraw/d3d7/d3d7_buffer.cpp @@ -1,5 +1,7 @@ #include "d3d7_buffer.h" +#include "../d3d_common_device.h" + #include "../ddraw_util.h" #include "../d3d_multithread.h" @@ -230,4 +232,18 @@ namespace dxvk { return DD_OK; } + void D3D7VertexBuffer::RefreshD3D7Device() { + D3DCommonDevice* commonDevice = m_commonIntf->GetCommonD3DDevice(); + + D3D7Device* d3d7Device = commonDevice != nullptr ? commonDevice->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; + } + m_d3d7Device = d3d7Device; + } + } + } diff --git a/src/ddraw/d3d7/d3d7_buffer.h b/src/ddraw/d3d7/d3d7_buffer.h index 24a78145b65..c733df9ad0c 100644 --- a/src/ddraw/d3d7/d3d7_buffer.h +++ b/src/ddraw/d3d7/d3d7_buffer.h @@ -34,6 +34,10 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE Optimize(LPDIRECT3DDEVICE7 lpD3DDevice, DWORD dwFlags); + HRESULT InitializeD3D9(); + + void RefreshD3D7Device(); + DWORD GetFVF() const { return m_desc.dwFVF; } @@ -50,20 +54,6 @@ namespace dxvk { return m_d3d7Device; } - void RefreshD3D7Device() { - D3D7Device* d3d7Device = m_commonIntf->GetD3D7Device(); - 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; - } - m_d3d7Device = d3d7Device; - } - } - - HRESULT InitializeD3D9(); - private: inline bool IsOptimized() const { diff --git a/src/ddraw/d3d7/d3d7_device.cpp b/src/ddraw/d3d7/d3d7_device.cpp index 68baadc0f80..fdfbed00c87 100644 --- a/src/ddraw/d3d7/d3d7_device.cpp +++ b/src/ddraw/d3d7/d3d7_device.cpp @@ -1,8 +1,9 @@ #include "d3d7_device.h" +#include "../ddraw_common_interface.h" + #include "d3d7_buffer.h" #include "d3d7_state_block.h" -#include #include "../ddraw7/ddraw7_surface.h" @@ -11,6 +12,7 @@ namespace dxvk { uint32_t D3D7Device::s_deviceCount = 0; D3D7Device::D3D7Device( + D3DCommonDevice* commonD3DDevice, Com&& d3d7DeviceProxy, D3D7Interface* pParent, D3DDEVICEDESC7 Desc, @@ -19,11 +21,19 @@ namespace dxvk { DDraw7Surface* pSurface, DWORD CreationFlags9) : DDrawWrappedObject(pParent, std::move(d3d7DeviceProxy), std::move(pDevice9)) - , m_commonIntf ( pParent->GetCommonInterface() ) + , m_commonD3DDevice ( commonD3DDevice ) , m_multithread ( CreationFlags9 & D3DCREATE_MULTITHREADED ) , m_params9 ( Params9 ) , m_desc ( Desc ) , m_rt ( pSurface ) { + if (m_parent != nullptr) { + m_commonIntf = m_parent->GetCommonInterface(); + } else if (m_commonD3DDevice != nullptr) { + m_commonIntf = m_commonD3DDevice->GetCommonInterface(); + } else { + 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!"); @@ -34,17 +44,25 @@ namespace dxvk { throw DxvkError("D3D7Device: ERROR! Failed to initialize D3D9 index buffers."); } - m_totalMemory = m_bridge->DetermineInitialTextureMemory(); + if (likely(m_commonD3DDevice == nullptr)) { + m_commonD3DDevice = new D3DCommonDevice(m_commonIntf, CreationFlags9, + m_bridge->DetermineInitialTextureMemory()); - m_textures.fill(nullptr); - - const D3DOptions* d3dOptions = m_commonIntf->GetOptions(); + 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); + if (unlikely(d3dOptions->emulateFSAA == FSAAEmulation::Forced)) { + Logger::warn("D3D7Device: Force enabling AA"); + m_d3d9->SetRenderState(d3d9::D3DRS_MULTISAMPLEANTIALIAS, TRUE); + } } + if (m_commonD3DDevice->GetOrigin() == nullptr) + m_commonD3DDevice->SetOrigin(this); + + m_commonD3DDevice->SetD3D7Device(this); + + m_textures.fill(nullptr); + m_deviceCount = ++s_deviceCount; Logger::debug(str::format("D3D7Device: Created a new device nr. ((7-", m_deviceCount, "))")); @@ -62,9 +80,11 @@ namespace dxvk { Logger::info(str::format(" XXL: ", m_ib9_uploads[6])); } - // Clear the common interface device pointer if it points to this device - if (m_commonIntf->GetD3D7Device() == this) - m_commonIntf->SetD3D7Device(nullptr); + if (m_commonD3DDevice->GetD3D7Device() == this) + m_commonD3DDevice->SetD3D7Device(nullptr); + + if (m_commonD3DDevice->GetOrigin() == this) + m_commonD3DDevice->SetOrigin(nullptr); Logger::debug(str::format("D3D7Device: Device nr. ((7-", m_deviceCount, ")) bites the dust")); } @@ -255,7 +275,7 @@ namespace dxvk { if (unlikely(!m_commonIntf->IsWrappedSurface(surface))) { Logger::err("D3D7Device::SetRenderTarget: Received an unwrapped RT"); - return DDERR_GENERIC; + return DDERR_UNSUPPORTED; } DDraw7Surface* rt7 = static_cast(surface); @@ -831,7 +851,7 @@ namespace dxvk { if (unlikely(!m_commonIntf->IsWrappedSurface(surface))) { Logger::err("D3D7Device::PreLoad: Received an unwrapped surface"); - return DDERR_GENERIC; + return DDERR_UNSUPPORTED; } DDraw7Surface* surface7 = static_cast(surface); @@ -1185,7 +1205,7 @@ namespace dxvk { // Binding texture stages if (unlikely(!m_commonIntf->IsWrappedSurface(surface))) { Logger::err("D3D7Device::SetTexture: Received an unwrapped texture"); - return DDERR_GENERIC; + return DDERR_UNSUPPORTED; } Logger::debug("D3D7Device::SetTexture: Binding D3D9 texture"); @@ -1334,14 +1354,14 @@ namespace dxvk { ddraw7SurfaceSrc = static_cast(src_surface); } else { Logger::warn("D3D7Device::Load: Unwrapped surface source"); - return DDERR_GENERIC; + return DDERR_UNSUPPORTED; } if (likely(m_commonIntf->IsWrappedSurface(dst_surface))) { ddraw7SurfaceDst = static_cast(dst_surface); } else { Logger::warn("D3D7Device::Load: Unwrapped surface destination"); - return DDERR_GENERIC; + return DDERR_UNSUPPORTED; } HRESULT hr = m_proxy->Load(ddraw7SurfaceDst->GetProxied(), dst_point, @@ -1490,4 +1510,4 @@ namespace dxvk { ib9->Unlock(); } -} +} \ No newline at end of file diff --git a/src/ddraw/d3d7/d3d7_device.h b/src/ddraw/d3d7/d3d7_device.h index 101fd627636..82f36648479 100644 --- a/src/ddraw/d3d7/d3d7_device.h +++ b/src/ddraw/d3d7/d3d7_device.h @@ -6,8 +6,9 @@ #include "../ddraw_util.h" #include "../ddraw_caps.h" +#include "../d3d_common_device.h" + #include "../d3d_multithread.h" -#include "../ddraw_common_interface.h" #include "../../d3d9/d3d9_bridge.h" @@ -18,6 +19,7 @@ namespace dxvk { + class D3DCommonDevice; class DDrawCommonInterface; class DDraw7Surface; class D3D7StateBlock; @@ -31,6 +33,7 @@ namespace dxvk { public: D3D7Device( + D3DCommonDevice* commonD3DDevice, Com&& d3d7DeviceProxy, D3D7Interface* pParent, D3DDEVICEDESC7 Desc, @@ -137,12 +140,12 @@ namespace dxvk { HRESULT ResetD3D9Swapchain(d3d9::D3DPRESENT_PARAMETERS* params); - D3DDeviceLock LockDevice() { - return m_multithread.AcquireLock(); + D3DCommonDevice* GetCommonD3DDevice() { + return m_commonD3DDevice.ptr(); } - uint32_t GetTotalTextureMemory() const { - return m_totalMemory; + D3DDeviceLock LockDevice() { + return m_multithread.AcquireLock(); } d3d9::D3DPRESENT_PARAMETERS GetPresentParameters() const { @@ -178,8 +181,8 @@ namespace dxvk { inline bool ShouldRecord() const { return m_recorder != nullptr; } inline void RefreshLastUsedDevice() { - if (unlikely(m_commonIntf->GetD3D7Device() != this)) - m_commonIntf->SetD3D7Device(this); + if (unlikely(m_commonIntf->GetCommonD3DDevice() != m_commonD3DDevice.ptr())) + m_commonIntf->SetCommonD3DDevice(m_commonD3DDevice.ptr()); } bool m_inScene = false; @@ -187,10 +190,10 @@ namespace dxvk { static uint32_t s_deviceCount; uint32_t m_deviceCount = 0; - uint32_t m_totalMemory = 0; - DDrawCommonInterface* m_commonIntf = nullptr; + Com m_commonD3DDevice; + Com m_bridge; D3DMultithread m_multithread; diff --git a/src/ddraw/d3d7/d3d7_interface.cpp b/src/ddraw/d3d7/d3d7_interface.cpp index db7e65b5f5b..cfd120c742c 100644 --- a/src/ddraw/d3d7/d3d7_interface.cpp +++ b/src/ddraw/d3d7/d3d7_interface.cpp @@ -190,7 +190,7 @@ namespace dxvk { Com rt7; if (unlikely(!m_commonIntf->IsWrappedSurface(surface))) { Logger::err("D3D7Interface::CreateDevice: Unwrapped surface passed as RT"); - return DDERR_GENERIC; + return DDERR_UNSUPPORTED; } else { rt7 = static_cast(surface); } @@ -320,12 +320,12 @@ namespace dxvk { D3DDEVICEDESC7 desc7 = GetD3D7Caps(rclsidOverride, d3dOptions); try{ - Com device7 = new D3D7Device(std::move(d3d7DeviceProxy), this, desc7, - params, std::move(device9), + Com device7 = new D3D7Device(nullptr, std::move(d3d7DeviceProxy), this, + desc7, params, std::move(device9), rt7.ptr(), deviceCreationFlags9); - // Set the newly created D3D7 device on the common interface - m_commonIntf->SetD3D7Device(device7.ptr()); + // Set the common device on the common interface + m_commonIntf->SetCommonD3DDevice(device7->GetCommonD3DDevice()); // Now that we have a valid D3D9 device pointer, we can initialize the depth stencil (if any) device7->InitializeDS(); @@ -401,11 +401,12 @@ namespace dxvk { if (unlikely(FAILED(hr))) return hr; - D3D7Device* d3d7Device = m_commonIntf->GetD3D7Device(); - if (likely(d3d7Device != nullptr)) { - D3DDeviceLock lock = d3d7Device->LockDevice(); + D3DCommonDevice* commonDevice = m_commonIntf->GetCommonD3DDevice(); + if (likely(commonDevice != nullptr)) { + d3d9::IDirect3DDevice9* d3d9Device = commonDevice->GetD3D9Device(); - HRESULT hr9 = d3d7Device->GetD3D9()->EvictManagedResources(); + // Note: This doesn't do anything in the D3D9 backend at the moment + HRESULT hr9 = d3d9Device->EvictManagedResources(); if (unlikely(FAILED(hr9))) { Logger::err("D3D7Interface::EvictManagedTextures: Failed D3D9 managed resource eviction"); return hr9; diff --git a/src/ddraw/d3d7/d3d7_state_block.h b/src/ddraw/d3d7/d3d7_state_block.h index 294d74e7746..2db7a7abc5d 100644 --- a/src/ddraw/d3d7/d3d7_state_block.h +++ b/src/ddraw/d3d7/d3d7_state_block.h @@ -3,8 +3,8 @@ #include "../ddraw_include.h" #include "../ddraw_caps.h" -#include "../../util/util_bit.h" -#include "../../util/util_flags.h" +#include "../util/util_bit.h" +#include "../util/util_flags.h" #include "../ddraw7/ddraw7_surface.h" @@ -88,4 +88,4 @@ namespace dxvk { }; -} +} \ No newline at end of file diff --git a/src/ddraw/d3d_common_device.cpp b/src/ddraw/d3d_common_device.cpp new file mode 100644 index 00000000000..b07326e9f5e --- /dev/null +++ b/src/ddraw/d3d_common_device.cpp @@ -0,0 +1,158 @@ +#include "d3d_common_device.h" + +#include "d3d_common_interface.h" + +#include "ddraw/ddraw_surface.h" +#include "ddraw4/ddraw4_surface.h" +#include "ddraw7/ddraw7_surface.h" + +#include "d3d7/d3d7_device.h" +#include "d3d6/d3d6_device.h" +#include "d3d5/d3d5_device.h" +#include "d3d3/d3d3_device.h" + +namespace dxvk { + + D3DCommonDevice::D3DCommonDevice( + DDrawCommonInterface* commonIntf, + DWORD creationFlags9, + uint32_t totalMemory) + : m_commonIntf ( commonIntf ) + , m_totalMemory ( totalMemory ) + , m_creationFlags9 ( creationFlags9 ) { + } + + D3DCommonDevice::~D3DCommonDevice() { + if (m_commonIntf->GetCommonD3DDevice() == this) + 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; + } else if (m_device6 != nullptr) { + return m_device6->GetParent() != nullptr ? m_device6->GetParent()->GetCommonD3DInterface() : nullptr; + } else if (m_device5 != nullptr) { + return m_device5->GetParent() != nullptr ? m_device5->GetParent()->GetCommonD3DInterface() : nullptr; + } else if (m_device3 != nullptr) { + D3D3Interface* d3d3Intf = m_device3->GetParent()->GetCommonInterface()->GetD3D3Interface(); + return d3d3Intf != nullptr ? d3d3Intf->GetCommonD3DInterface() : nullptr; + } + + 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); + } else if (m_device6 != nullptr) { + return m_device6->ResetD3D9Swapchain(params); + } + // 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; + } + + bool D3DCommonDevice::IsCurrentDepthStencil(DDrawSurface* surface) const { + return m_device5 != nullptr ? m_device5->GetDepthStencil() == surface : + m_device3 != nullptr ? m_device3->GetDepthStencil() == surface : false; + } + + bool D3DCommonDevice::IsCurrentDepthStencil(DDraw4Surface* surface) const { + return m_device6 != nullptr ? m_device6->GetDepthStencil() == surface : false; + } + + bool D3DCommonDevice::IsCurrentDepthStencil(DDraw7Surface* surface) const { + return m_device7 != nullptr ? m_device7->GetDepthStencil() == surface : false; + } + + 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; + } + +} \ No newline at end of file diff --git a/src/ddraw/d3d_common_device.h b/src/ddraw/d3d_common_device.h new file mode 100644 index 00000000000..03dd4f128ed --- /dev/null +++ b/src/ddraw/d3d_common_device.h @@ -0,0 +1,135 @@ +#pragma once + +#include "ddraw_include.h" + +#include + +namespace dxvk { + + class D3DCommonInterface; + class DDrawCommonInterface; + + class DDraw7Surface; + class DDraw4Surface; + class DDrawSurface; + + class D3D7Device; + class D3D6Device; + class D3D5Device; + class D3D3Device; + + class D3DCommonDevice : public ComObjectClamp { + + public: + + D3DCommonDevice( + DDrawCommonInterface* commonIntf, + DWORD creationFlags9, + uint32_t totalMemory); + + ~D3DCommonDevice(); + + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppvObject) { + *ppvObject = this; + return S_OK; + } + + d3d9::IDirect3DDevice9* GetD3D9Device(); + + D3DCommonInterface* GetCommonD3DInterface() const; + + d3d9::D3DMULTISAMPLE_TYPE GetMultiSampleType(); + + d3d9::D3DPRESENT_PARAMETERS GetPresentParameters(); + + HRESULT ResetD3D9Swapchain(d3d9::D3DPRESENT_PARAMETERS* params); + + bool IsCurrentRenderTarget(DDrawSurface* surface) const; + + bool IsCurrentRenderTarget(DDraw4Surface* surface) const; + + bool IsCurrentRenderTarget(DDraw7Surface* surface) const; + + bool IsCurrentD3D9RenderTarget(d3d9::IDirect3DSurface9* surface) const; + + bool IsCurrentDepthStencil(DDrawSurface* surface) const; + + bool IsCurrentDepthStencil(DDraw4Surface* surface) const; + + bool IsCurrentDepthStencil(DDraw7Surface* surface) const; + + bool IsCurrentD3D9DepthStencil(d3d9::IDirect3DSurface9* surface) const; + + DDrawCommonInterface* GetCommonInterface() const { + return m_commonIntf; + } + + DWORD GetD3D9CreationFlags() const { + return m_creationFlags9; + } + + void SetD3D7Device(D3D7Device* device7) { + m_device7 = device7; + } + + D3D7Device* GetD3D7Device() const { + return m_device7; + } + + void SetD3D6Device(D3D6Device* device6) { + m_device6 = device6; + } + + D3D6Device* GetD3D6Device() const { + return m_device6; + } + + void SetD3D5Device(D3D5Device* device5) { + m_device5 = device5; + } + + D3D5Device* GetD3D5Device() const { + return m_device5; + } + + void SetD3D3Device(D3D3Device* device3) { + m_device3 = device3; + } + + D3D3Device* GetD3D3Device() const { + return m_device3; + } + + void SetOrigin(IUnknown* origin) { + m_origin = origin; + } + + IUnknown* GetOrigin() const { + return m_origin; + } + + uint32_t GetTotalTextureMemory() const { + return m_totalMemory; + } + + private: + + DDrawCommonInterface* m_commonIntf = nullptr; + + uint32_t m_totalMemory = 0; + + DWORD m_creationFlags9 = 0; + + // Track all possible last used D3D devices + 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; + + }; + +} \ No newline at end of file diff --git a/src/ddraw/d3d_common_material.cpp b/src/ddraw/d3d_common_material.cpp index d7524520710..3139b05af78 100644 --- a/src/ddraw/d3d_common_material.cpp +++ b/src/ddraw/d3d_common_material.cpp @@ -1,5 +1,13 @@ #include "d3d_common_material.h" +#include "d3d6/d3d6_material.h" +#include "d3d5/d3d5_material.h" +#include "d3d3/d3d3_material.h" + +#include "d3d6/d3d6_device.h" +#include "d3d5/d3d5_device.h" +#include "d3d3/d3d3_device.h" + namespace dxvk { D3DCommonMaterial::D3DCommonMaterial(D3DMATERIALHANDLE materialHandle) @@ -9,4 +17,22 @@ namespace dxvk { D3DCommonMaterial::~D3DCommonMaterial() { } + D3DMATERIALHANDLE D3DCommonMaterial::GetProxiedMaterialHandle(IUnknown* d3dDevice) const { + D3DMATERIALHANDLE proxiedHandle = D3DMATERIALHANDLE(0); + HRESULT hr = DDERR_GENERIC; + + if (m_material6 != nullptr) { + hr = m_material6->GetProxied()->GetHandle(reinterpret_cast(d3dDevice), &proxiedHandle); + } else if (m_material5 != nullptr) { + hr = m_material5->GetProxied()->GetHandle(reinterpret_cast(d3dDevice), &proxiedHandle); + } else if (m_material3 != nullptr) { + hr = m_material3->GetProxied()->GetHandle(reinterpret_cast(d3dDevice), &proxiedHandle); + } + + if (unlikely(FAILED(hr))) + Logger::warn("D3DCommonMaterial::GetProxiedMaterialHandle: Failed to retrieve proxied handle"); + + return proxiedHandle; + } + } \ No newline at end of file diff --git a/src/ddraw/d3d_common_material.h b/src/ddraw/d3d_common_material.h index 187208861f0..247b38eb0ce 100644 --- a/src/ddraw/d3d_common_material.h +++ b/src/ddraw/d3d_common_material.h @@ -4,6 +4,10 @@ namespace dxvk { + class D3D6Material; + class D3D5Material; + class D3D3Material; + class D3DCommonMaterial : public ComObjectClamp { public: @@ -17,6 +21,8 @@ namespace dxvk { return S_OK; } + D3DMATERIALHANDLE GetProxiedMaterialHandle(IUnknown* d3dDevice) const; + d3d9::D3DMATERIAL9* GetD3D9Material() { return &m_material9; } @@ -30,12 +36,41 @@ namespace dxvk { m_material9.Diffuse.b, m_material9.Diffuse.a); } + void SetD3D6Material(D3D6Material* material6) { + m_material6 = material6; + } + + D3D6Material* GetD3D6Material() const { + return m_material6; + } + + void SetD3D5Material(D3D5Material* material5) { + m_material5 = material5; + } + + D3D5Material* GetD3D5Material() const { + return m_material5; + } + + void SetD3D3Material(D3D3Material* material3) { + m_material3 = material3; + } + + D3D3Material* GetD3D3Material() const { + return m_material3; + } + private: D3DMATERIALHANDLE m_materialHandle = 0; d3d9::D3DMATERIAL9 m_material9 = { }; + // Track all possible material versions of the same object + D3D6Material* m_material6 = nullptr; + D3D5Material* m_material5 = nullptr; + D3D3Material* m_material3 = nullptr; + }; } \ No newline at end of file diff --git a/src/ddraw/d3d_common_viewport.cpp b/src/ddraw/d3d_common_viewport.cpp index ca401f64965..f94050ff12d 100644 --- a/src/ddraw/d3d_common_viewport.cpp +++ b/src/ddraw/d3d_common_viewport.cpp @@ -2,9 +2,9 @@ #include "d3d_common_interface.h" -#include "d3d3/d3d3_device.h" -#include "d3d5/d3d5_device.h" #include "d3d6/d3d6_device.h" +#include "d3d5/d3d5_device.h" +#include "d3d3/d3d3_device.h" namespace dxvk { @@ -58,4 +58,114 @@ namespace dxvk { return nullptr; } + HRESULT D3DCommonViewport::TransformVertices(DWORD vertex_count, D3DTRANSFORMDATA *data, DWORD flags, DWORD *offscreen) { + if (data == nullptr || data->dwSize != sizeof(D3DTRANSFORMDATA) || offscreen == nullptr) + return DDERR_INVALIDPARAMS; + + if ((flags & (D3DTRANSFORM_CLIPPED | D3DTRANSFORM_UNCLIPPED)) == 0) + return DDERR_INVALIDPARAMS; + + bool clipped = (flags & D3DTRANSFORM_CLIPPED) && !(flags & D3DTRANSFORM_UNCLIPPED); + if (clipped) + *offscreen = UINT_MAX; + else + *offscreen = 0; + + // When vertex_count = 0 native apparently returns success even when data->lpIn/data->lpOut are null, otherwise crash + if (vertex_count == 0) + return D3D_OK; + + if (data->dwInSize < sizeof(D3DLVERTEX) || data->dwOutSize < sizeof(D3DTLVERTEX)) + return DDERR_INVALIDPARAMS; + + if (data->lpIn == nullptr || data->lpOut == nullptr) + return DDERR_INVALIDPARAMS; + + // Ensure transform states aren't modified in flight + if (m_device6 != nullptr) + D3DDeviceLock lock6 = m_device6->LockDevice(); + if (m_device5 != nullptr) + D3DDeviceLock lock5 = m_device5->LockDevice(); + if (m_device3 != nullptr) + D3DDeviceLock lock3 = m_device3->LockDevice(); + + d3d9::IDirect3DDevice9* m_device9 = GetD3D9Device(); + + D3DMATRIX world{}, view{}, projection{}; + HRESULT hr; + hr = m_device9->GetTransform(ConvertTransformState(D3DTRANSFORMSTATE_WORLD), &world); + if (FAILED(hr)) { + Logger::err("D3DCommonViewport::TransformVertices: failed to get world transform"); + return DDERR_GENERIC; + } + hr = m_device9->GetTransform(d3d9::D3DTS_VIEW, &view); + if (FAILED(hr)) { + Logger::err("D3DCommonViewport::TransformVertices: failed to get view transform"); + return DDERR_GENERIC; + } + hr = m_device9->GetTransform(d3d9::D3DTS_PROJECTION, &projection); + if (FAILED(hr)) { + Logger::err("D3DCommonViewport::TransformVertices: failed to get projection transform"); + return DDERR_GENERIC; + } + + Matrix4 wv = MatrixD3DTo4(&view) * MatrixD3DTo4(&world); + Matrix4 wvp = MatrixD3DTo4(&projection) * wv; + const D3DMATRIX* correction = GetLegacyProjectionMatrix(0); + if (correction != nullptr) + wvp = MatrixD3DTo4(correction) * wvp; + + for (DWORD t = 0; t < vertex_count; t++) { + // Docs says input is always D3DLVERTEX and output D3DTLVERTEX. + // But they can have arbitrary stride set by application and defined via dwInSize/dwOutSize. + D3DLVERTEX& in = *(reinterpret_cast(reinterpret_cast(data->lpIn) + data->dwInSize * t)); + D3DTLVERTEX& out = *(reinterpret_cast(reinterpret_cast(data->lpOut) + data->dwOutSize * t)); + + Vector4 h = wvp * Vector4({in.x, in.y, in.z, 1.0f}); + + auto outH = data->lpHOut; + if (clipped) { + outH[t].dwFlags = 0; + if (h.x > h.w) + outH[t].dwFlags |= D3DCLIP_RIGHT; + if (h.x < -h.w) + outH[t].dwFlags |= D3DCLIP_LEFT; + if (h.y > h.w) + outH[t].dwFlags |= D3DCLIP_TOP; + if (h.y < -h.w) + outH[t].dwFlags |= D3DCLIP_BOTTOM; + if (h.z < 0.0f) + outH[t].dwFlags |= D3DCLIP_FRONT; + if (h.z > h.w) + outH[t].dwFlags |= D3DCLIP_BACK; + + *offscreen &= outH[t].dwFlags; + + outH[t].hx = (h.x - m_legacyClip.x * h.w) / m_legacyScale.x; + outH[t].hy = (h.y - m_legacyClip.y * h.w) / m_legacyScale.y; + outH[t].hz = (h.z - m_legacyClip.z * h.w) / m_legacyScale.z; + + if (outH[t].dwFlags) { + out.sx = h.x; + out.sy = h.y; + out.sz = h.z; + out.rhw = h.w; + continue; + } + } + + out.rhw = (h.w != 0.0f) ? (1.0f / h.w) : 0.0f; + 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); + + out.color = in.color; + out.specular = in.specular; + out.tu = in.tu; + out.tv = in.tv; + } + + return D3D_OK; + } + } \ No newline at end of file diff --git a/src/ddraw/d3d_common_viewport.h b/src/ddraw/d3d_common_viewport.h index ddc51538af5..11711e3fc43 100644 --- a/src/ddraw/d3d_common_viewport.h +++ b/src/ddraw/d3d_common_viewport.h @@ -1,6 +1,7 @@ #pragma once #include "ddraw_include.h" +#include "ddraw_util.h" #include "d3d_light.h" @@ -41,6 +42,8 @@ namespace dxvk { d3d9::IDirect3DDevice9* GetD3D9Device(); + HRESULT TransformVertices(DWORD vertex_count, D3DTRANSFORMDATA *data, DWORD flags, DWORD *offscreen); + D3DCommonInterface* GetCommonD3DInterface() const { return m_commonD3DIntf; } @@ -111,6 +114,14 @@ namespace dxvk { return m_d3d3Viewport; } + void SetOrigin(IUnknown* origin) { + m_origin = origin; + } + + IUnknown* GetOrigin() const { + return m_origin; + } + void SetD3D6Device(D3D6Device* device6) { m_device6 = device6; } @@ -206,6 +217,10 @@ namespace dxvk { D3D5Viewport* m_d3d5Viewport = nullptr; D3D3Viewport* m_d3d3Viewport = nullptr; + // Track the origin viewport, as in the viewport + // that gets created through a CreateViewport call + IUnknown* m_origin = nullptr; + // Track all devices this viewport is attached to D3D6Device* m_device6 = nullptr; D3D5Device* m_device5 = nullptr; diff --git a/src/ddraw/d3d_light.cpp b/src/ddraw/d3d_light.cpp index 0d6f972fd4e..df08b6c66a7 100644 --- a/src/ddraw/d3d_light.cpp +++ b/src/ddraw/d3d_light.cpp @@ -3,7 +3,6 @@ #include "d3d3/d3d3_viewport.h" #include "d3d5/d3d5_viewport.h" #include "d3d6/d3d6_viewport.h" -#include namespace dxvk { diff --git a/src/ddraw/ddraw/ddraw_interface.cpp b/src/ddraw/ddraw/ddraw_interface.cpp index 8eeb13c2823..5f4b050afc1 100644 --- a/src/ddraw/ddraw/ddraw_interface.cpp +++ b/src/ddraw/ddraw/ddraw_interface.cpp @@ -1,5 +1,7 @@ #include "ddraw_interface.h" +#include "../d3d_common_device.h" + #include "ddraw_surface.h" #include "../ddraw_clipper.h" @@ -12,7 +14,6 @@ #include "../d3d3/d3d3_interface.h" #include "../d3d5/d3d5_interface.h" #include "../d3d6/d3d6_interface.h" -#include namespace dxvk { @@ -24,9 +25,8 @@ namespace dxvk { : DDrawWrappedObject(nullptr, std::move(proxyIntf), nullptr) , m_commonIntf ( commonIntf ) { - if (m_commonIntf == nullptr) { - // We need a temporary D3D9 interface at this point to retrieve the options, - // even if we're only proxying and we don't yet have any child D3D interfaces + if (likely(m_commonIntf == nullptr)) { + // We need a temporary D3D9 interface to retrieve the options Com d3d9Intf = d3d9::Direct3DCreate9(D3D_SDK_VERSION); Com d3d9Bridge; @@ -330,7 +330,7 @@ namespace dxvk { } else { if (unlikely(lpDDSurface != nullptr)) { Logger::warn("DDrawInterface::DuplicateSurface: Received an unwrapped source surface"); - return DDERR_GENERIC; + return DDERR_UNSUPPORTED; } return m_proxy->DuplicateSurface(lpDDSurface, lplpDupDDSurface); } @@ -342,24 +342,26 @@ namespace dxvk { } HRESULT STDMETHODCALLTYPE DDrawInterface::EnumSurfaces(DWORD dwFlags, LPDDSURFACEDESC lpDDSD, LPVOID lpContext, LPDDENUMSURFACESCALLBACK lpEnumSurfacesCallback) { - Logger::debug(">>> DDrawInterface::EnumSurfaces: Proxy"); + Logger::debug("<<< DDrawInterface::EnumSurfaces: Proxy"); if (unlikely(lpEnumSurfacesCallback == nullptr)) return DDERR_INVALIDPARAMS; std::vector attachedSurfaces; // Enumerate all surfaces from the underlying DDraw implementation - m_proxy->EnumSurfaces(dwFlags, lpDDSD, reinterpret_cast(&attachedSurfaces), EnumAttachedSurfacesCallback); + HRESULT hr = m_proxy->EnumSurfaces(dwFlags, lpDDSD, reinterpret_cast(&attachedSurfaces), EnumAttachedSurfacesCallback); + if (unlikely(FAILED(hr))) + return hr; - HRESULT hr = DDENUMRET_OK; + HRESULT hrCB = DDENUMRET_OK; // Wrap surfaces as needed and perform the actual callback the application is requesting auto surfaceIt = attachedSurfaces.begin(); - while (surfaceIt != attachedSurfaces.end() && hr == DDENUMRET_OK) { + while (surfaceIt != attachedSurfaces.end() && hrCB == DDENUMRET_OK) { Com surface = surfaceIt->surface; Com ddrawSurface = new DDrawSurface(nullptr, std::move(surface), this, nullptr, false); - hr = lpEnumSurfacesCallback(ddrawSurface.ref(), &surfaceIt->desc, lpContext); + hrCB = lpEnumSurfacesCallback(ddrawSurface.ref(), &surfaceIt->desc, lpContext); ++surfaceIt; } @@ -368,7 +370,22 @@ 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(); + + // A primary surface must exist for a GDI flip to be possible + if (unlikely(ps == nullptr)) + return DDERR_NOTFOUND; + + if (unlikely(!ps->IsFlippable())) + return DDERR_NOTFLIPPABLE; + return DD_OK; } @@ -388,11 +405,13 @@ namespace dxvk { DWORD total9 = 0; DWORD free9 = 0; - d3d9::IDirect3DDevice9* d3d9Device = m_commonIntf->GetD3D9Device(); - if (likely(d3d9Device != nullptr)) { + D3DCommonDevice* commonDevice = m_commonIntf->GetCommonD3DDevice(); + if (likely(commonDevice != nullptr)) { Logger::debug("DDrawInterface::GetCaps: Getting memory stats from D3D9"); - total9 = static_cast(m_commonIntf->GetTotalTextureMemory()); + d3d9::IDirect3DDevice9* d3d9Device = commonDevice->GetD3D9Device(); + + total9 = static_cast(commonDevice->GetTotalTextureMemory()); free9 = static_cast(d3d9Device->GetAvailableTextureMem()); if (likely(total9 >= MaxMemory)) { @@ -603,13 +622,13 @@ namespace dxvk { // Switch to a default presentation interval when an application // tries to wait for vertical blank, if we're not already doing so - d3d9::IDirect3DDevice9* d3d9Device = m_commonIntf->GetD3D9Device(); - if (unlikely(d3d9Device != nullptr && !m_commonIntf->GetWaitForVBlank())) { + D3DCommonDevice* commonDevice = m_commonIntf->GetCommonD3DDevice(); + if (unlikely(commonDevice != nullptr && !m_commonIntf->GetWaitForVBlank())) { Logger::info("DDrawInterface::WaitForVerticalBlank: Switching to D3DPRESENT_INTERVAL_DEFAULT for presentation"); - d3d9::D3DPRESENT_PARAMETERS resetParams = m_commonIntf->GetPresentParameters(); + d3d9::D3DPRESENT_PARAMETERS resetParams = commonDevice->GetPresentParameters(); resetParams.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT; - HRESULT hrReset = m_commonIntf->ResetD3D9Swapchain(&resetParams); + HRESULT hrReset = commonDevice->ResetD3D9Swapchain(&resetParams); if (likely(SUCCEEDED(hrReset))) m_commonIntf->SetWaitForVBlank(true); } @@ -617,4 +636,4 @@ namespace dxvk { return DD_OK; } -} +} \ No newline at end of file diff --git a/src/ddraw/ddraw/ddraw_surface.cpp b/src/ddraw/ddraw/ddraw_surface.cpp index 25e085675fc..c7332b2e657 100644 --- a/src/ddraw/ddraw/ddraw_surface.cpp +++ b/src/ddraw/ddraw/ddraw_surface.cpp @@ -1,5 +1,7 @@ #include "ddraw_surface.h" +#include "../d3d_common_device.h" + #include "../ddraw_gamma.h" #include "../d3d3/d3d3_device.h" @@ -10,7 +12,6 @@ #include "../ddraw2/ddraw3_surface.h" #include "../ddraw4/ddraw4_surface.h" #include "../ddraw7/ddraw7_surface.h" -#include namespace dxvk { @@ -277,8 +278,14 @@ namespace dxvk { DDrawSurface* ddrawSurface = static_cast(lpDDSAttachedSurface); - if (unlikely(ddrawSurface->GetCommonSurface()->IsBackBufferOrFlippable())) - Logger::warn("DDrawSurface::AddAttachedSurface: Trying to attach a flippable surface"); + 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"); + } + } HRESULT hr = m_proxy->AddAttachedSurface(ddrawSurface->GetProxied()); if (unlikely(FAILED(hr))) @@ -340,7 +347,7 @@ namespace dxvk { if (unlikely(lpDDSrcSurface == nullptr && (dwFlags & DDBLT_DEPTHFILL) && lpDDBltFx != nullptr && - m_commonIntf->IsCurrentD3D9DepthStencil(m_d3d9.ptr()))) { + m_commonIntf->GetCommonD3DDevice()->IsCurrentD3D9DepthStencil(m_d3d9.ptr()))) { Logger::debug("DDrawSurface::Blt: Clearing d3d9 depth stencil"); HRESULT hrClear; @@ -358,7 +365,7 @@ namespace dxvk { if (unlikely(lpDDSrcSurface == nullptr && (dwFlags & DDBLT_COLORFILL) && lpDDBltFx != nullptr && - m_commonIntf->IsCurrentD3D9RenderTarget(m_d3d9.ptr()))) { + m_commonIntf->GetCommonD3DDevice()->IsCurrentD3D9RenderTarget(m_d3d9.ptr()))) { Logger::debug("DDrawSurface::Blt: Clearing d3d9 render target"); HRESULT hrClear; @@ -392,7 +399,7 @@ namespace dxvk { if (unlikely(!m_commonIntf->IsWrappedSurface(lpDDSrcSurface))) { if (unlikely(lpDDSrcSurface != nullptr)) { Logger::warn("DDrawSurface::Blt: Received an unwrapped source surface"); - return DDERR_GENERIC; + return DDERR_UNSUPPORTED; } hr = m_proxy->Blt(lpDestRect, lpDDSrcSurface, lpSrcRect, dwFlags, lpDDBltFx); } else { @@ -479,7 +486,7 @@ namespace dxvk { if (unlikely(!m_commonIntf->IsWrappedSurface(lpDDSrcSurface))) { if (unlikely(lpDDSrcSurface != nullptr)) { Logger::warn("DDrawSurface::BltFast: Received an unwrapped source surface"); - return DDERR_GENERIC; + return DDERR_UNSUPPORTED; } hr = m_proxy->BltFast(dwX, dwY, lpDDSrcSurface, lpSrcRect, dwTrans); } else { @@ -507,7 +514,7 @@ namespace dxvk { if (unlikely(!m_commonIntf->IsWrappedSurface(lpDDSAttachedSurface))) { if (unlikely(lpDDSAttachedSurface != nullptr)) { Logger::warn("DDrawSurface::DeleteAttachedSurface: Received an unwrapped surface"); - return DDERR_GENERIC; + return DDERR_UNSUPPORTED; } HRESULT hrProxy = m_proxy->DeleteAttachedSurface(dwFlags, lpDDSAttachedSurface); @@ -614,6 +621,9 @@ namespace dxvk { } } + DDrawSurface* rt = m_commonIntf->GetDDrawRenderTarget() != nullptr ? + m_commonIntf->GetDDrawRenderTarget()->GetDDSurface() : nullptr; + RefreshD3D9Device(); if (likely(m_d3d9Device != nullptr)) { Logger::debug("*** DDrawSurface::Flip: Presenting"); @@ -621,23 +631,40 @@ namespace dxvk { m_commonIntf->ResetDrawTracking(); if (unlikely(m_commonIntf->GetOptions()->forceProxiedPresent)) { + D3DCommonDevice* commonDevice = m_commonIntf->GetCommonD3DDevice(); + if (unlikely(!IsInitialized())) - InitializeD3D9(m_commonIntf->IsCurrentRenderTarget(this)); + InitializeD3D9(commonDevice->IsCurrentRenderTarget(this)); BlitToDDrawSurface(m_proxy.ptr(), m_d3d9.ptr()); if (unlikely(!m_commonIntf->IsWrappedSurface(lpDDSurfaceTargetOverride))) { if (unlikely(lpDDSurfaceTargetOverride != nullptr)) { Logger::warn("DDrawSurface::Flip: Received an unwrapped surface"); - return DDERR_GENERIC; + return DDERR_UNSUPPORTED; } - if (likely(m_commonIntf->IsCurrentRenderTarget(this))) + + if (likely(commonDevice->IsCurrentRenderTarget(this))) m_commonIntf->SetFlipRTSurfaceAndFlags(lpDDSurfaceTargetOverride, dwFlags); - return m_proxy->Flip(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(lpDDSurfaceTargetOverride, dwFlags); + } } else { - if (likely(m_commonIntf->IsCurrentRenderTarget(this))) + if (likely(commonDevice->IsCurrentRenderTarget(this))) m_commonIntf->SetFlipRTSurfaceAndFlags(lpDDSurfaceTargetOverride, dwFlags); - return m_proxy->Flip(surf->GetProxied(), 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); + } } } @@ -647,9 +674,15 @@ namespace dxvk { } else { Logger::debug("<<< DDrawSurface::Flip: Proxy"); if (unlikely(!m_commonIntf->IsWrappedSurface(lpDDSurfaceTargetOverride))) { - m_proxy->Flip(lpDDSurfaceTargetOverride, dwFlags); + 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); + } } else { - m_proxy->Flip(surf->GetProxied(), dwFlags); + return m_proxy->Flip(surf->GetProxied(), dwFlags); } } @@ -1041,9 +1074,12 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE DDrawSurface::UpdateOverlay(LPRECT lpSrcRect, LPDIRECTDRAWSURFACE lpDDDestSurface, LPRECT lpDestRect, DWORD dwFlags, LPDDOVERLAYFX lpDDOverlayFx) { Logger::debug("<<< DDrawSurface::UpdateOverlay: Proxy"); + if (unlikely(lpDDDestSurface == nullptr)) + return DDERR_INVALIDPARAMS; + if (unlikely(!m_commonIntf->IsWrappedSurface(lpDDDestSurface))) { Logger::warn("DDrawSurface::UpdateOverlay: Received an unwrapped surface"); - return DDERR_GENERIC; + return DDERR_UNSUPPORTED; } DDrawSurface* ddrawSurface = static_cast(lpDDDestSurface); @@ -1061,7 +1097,7 @@ namespace dxvk { if (unlikely(!m_commonIntf->IsWrappedSurface(lpDDSReference))) { Logger::warn("DDrawSurface::UpdateOverlayZOrder: Received an unwrapped surface"); - return DDERR_GENERIC; + return DDERR_UNSUPPORTED; } DDrawSurface* ddrawSurface = static_cast(lpDDSReference); @@ -1243,7 +1279,7 @@ namespace dxvk { 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->GetMultiSampleType(); + const d3d9::D3DMULTISAMPLE_TYPE multiSampleType = m_commonIntf->GetCommonD3DDevice()->GetMultiSampleType(); const uint32_t index = m_commonSurf->GetBackBufferIndex(); Com surf; @@ -1328,7 +1364,7 @@ namespace dxvk { // 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->IsCurrentRenderTarget(this) || 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); @@ -1503,20 +1539,34 @@ namespace dxvk { } } + 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. D3D3Interface* d3d3Intf = m_commonIntf->GetD3D3Interface(); if (unlikely(d3d3Intf == nullptr)) { - Logger::err("DDrawSurface::CreateDeviceInternal: Device creation failed due to null D3D3 interface"); - return DDERR_GENERIC; + Logger::debug("DDrawSurface::CreateDeviceInternal: Creating a temporary D3D9 interface"); + d3d9Intf = d3d9::Direct3DCreate9(D3D_SDK_VERSION); + + Com d3d9Bridge; + + if (unlikely(FAILED(d3d9Intf->QueryInterface(__uuidof(IDxvkD3D8InterfaceBridge), reinterpret_cast(&d3d9Bridge))))) { + Logger::err("DDrawSurface::CreateDeviceInternal: ERROR! Failed to get D3D9 Bridge. d3d9.dll might not be DXVK!"); + return DDERR_GENERIC; + } + + d3d9Bridge->EnableD3D3CompatibilityMode(); + } else { + d3d9Intf = d3d3Intf->GetD3D9(); } // 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 = d3d3Intf->GetD3D9()->CheckDeviceMultiSampleType(0, d3d9::D3DDEVTYPE_HAL, m_commonSurf->GetD3D9Format(), - TRUE, d3d9::D3DMULTISAMPLE_4_SAMPLES, NULL); + HRESULT hr4S = d3d9Intf->CheckDeviceMultiSampleType(0, d3d9::D3DDEVTYPE_HAL, m_commonSurf->GetD3D9Format(), + TRUE, d3d9::D3DMULTISAMPLE_4_SAMPLES, NULL); if (unlikely(FAILED(hr4S))) { - HRESULT hr2S = d3d3Intf->GetD3D9()->CheckDeviceMultiSampleType(0, d3d9::D3DDEVTYPE_HAL, m_commonSurf->GetD3D9Format(), - TRUE, d3d9::D3DMULTISAMPLE_2_SAMPLES, NULL); + 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 { @@ -1579,7 +1629,7 @@ namespace dxvk { params.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT; // A D3D3 device always uses VSync Com device9; - hr = d3d3Intf->GetD3D9()->CreateDevice( + hr = d3d9Intf->CreateDevice( D3DADAPTER_DEFAULT, d3d9::D3DDEVTYPE_HAL, hWnd, @@ -1593,11 +1643,12 @@ namespace dxvk { return hr; } - Com device3 = new D3D3Device(std::move(ppvProxyObject), this, GetD3D3Caps(d3dOptions), - rclsidOverride, params, std::move(device9), deviceCreationFlags9); + Com device3 = new D3D3Device(nullptr, std::move(ppvProxyObject), this, + GetD3D3Caps(d3dOptions), rclsidOverride, params, + std::move(device9), deviceCreationFlags9); - // Set the newly created D3D3 device on the common interface - m_commonIntf->SetD3D3Device(device3.ptr()); + // Set the common device on the common interface + m_commonIntf->SetCommonD3DDevice(device3->GetCommonD3DDevice()); // Now that we have a valid D3D9 device pointer, we can initialize the depth stencil (if any) device3->InitializeDS(); @@ -1606,4 +1657,19 @@ namespace dxvk { return DD_OK; } + inline void DDrawSurface::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("DDrawSurface: Device context has changed, clearing all D3D9 resources"); + m_d3d9 = nullptr; + } + + m_d3d9Device = d3d9Device; + } + } + } diff --git a/src/ddraw/ddraw/ddraw_surface.h b/src/ddraw/ddraw/ddraw_surface.h index 9b7ba74fac4..6100c51c9ae 100644 --- a/src/ddraw/ddraw/ddraw_surface.h +++ b/src/ddraw/ddraw/ddraw_surface.h @@ -179,18 +179,7 @@ namespace dxvk { inline HRESULT CreateDeviceInternal(REFIID riid, void** ppvObject); - inline void RefreshD3D9Device() { - d3d9::IDirect3DDevice9* d3d9Device = m_commonIntf->GetD3D9Device(); - 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_texture9 = nullptr; - m_d3d9 = nullptr; - } - m_d3d9Device = d3d9Device; - } - } + inline void RefreshD3D9Device(); bool m_isChildObject = true; diff --git a/src/ddraw/ddraw2/ddraw2_interface.cpp b/src/ddraw/ddraw2/ddraw2_interface.cpp index 3681fbf93a0..a33e13dfd12 100644 --- a/src/ddraw/ddraw2/ddraw2_interface.cpp +++ b/src/ddraw/ddraw2/ddraw2_interface.cpp @@ -1,17 +1,17 @@ #include "ddraw2_interface.h" +#include "../d3d_common_device.h" + #include "../ddraw_clipper.h" #include "../ddraw_palette.h" #include "../ddraw/ddraw_surface.h" -#include "../ddraw/ddraw_interface.h" #include "../ddraw4/ddraw4_interface.h" #include "../ddraw7/ddraw7_interface.h" #include "../d3d3/d3d3_interface.h" #include "../d3d5/d3d5_interface.h" #include "../d3d6/d3d6_interface.h" -#include namespace dxvk { @@ -22,9 +22,15 @@ namespace dxvk { Com&& proxyIntf) : DDrawWrappedObject(nullptr, std::move(proxyIntf), nullptr) , m_commonIntf ( commonIntf ) { + // Hold a reference to the parent IDirectDraw object, since + // it is needed to be able to create surfaces from this interface + if (likely(commonIntf->GetDDInterface() != nullptr)) { + m_parentIntf = commonIntf->GetDDInterface(); + } else { + Logger::warn("DDraw2Interface: Missing an IDirectDraw parent"); + } - if (m_commonIntf->GetOrigin() == nullptr) - m_commonIntf->SetOrigin(this); + // Note: IDirectDraw2 can never be the origin interface m_commonIntf->SetDD2Interface(this); @@ -40,9 +46,6 @@ namespace dxvk { } DDraw2Interface::~DDraw2Interface() { - if (m_commonIntf->GetOrigin() == this) - m_commonIntf->SetOrigin(nullptr); - m_commonIntf->SetDD2Interface(nullptr); Logger::debug(str::format("DDraw2Interface: Interface nr. <<2-", m_intfCount, ">> bites the dust")); @@ -323,7 +326,7 @@ namespace dxvk { } else { if (unlikely(lpDDSurface != nullptr)) { Logger::warn("DDraw2Interface::DuplicateSurface: Received an unwrapped source surface"); - return DDERR_GENERIC; + return DDERR_UNSUPPORTED; } return m_proxy->DuplicateSurface(lpDDSurface, lplpDupDDSurface); } @@ -337,12 +340,32 @@ namespace dxvk { } HRESULT STDMETHODCALLTYPE DDraw2Interface::EnumSurfaces(DWORD dwFlags, LPDDSURFACEDESC lpDDSD, LPVOID lpContext, LPDDENUMSURFACESCALLBACK lpEnumSurfacesCallback) { - Logger::warn("<<< DDraw2Interface::EnumSurfaces: Proxy"); - return m_proxy->EnumSurfaces(dwFlags, lpDDSD, lpContext, lpEnumSurfacesCallback); + if (unlikely(m_commonIntf->GetDDInterface() == nullptr)) { + Logger::warn("!!! DDraw2Interface::EnumSurfaces: Stub"); + return DDERR_UNSUPPORTED; + } + + Logger::warn(">>> DDraw2Interface::EnumSurfaces"); + return m_commonIntf->GetDDInterface()->EnumSurfaces(dwFlags, lpDDSD, lpContext, lpEnumSurfacesCallback); } 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(); + + // A primary surface must exist for a GDI flip to be possible + if (unlikely(ps == nullptr)) + return DDERR_NOTFOUND; + + if (unlikely(!ps->IsFlippable())) + return DDERR_NOTFLIPPABLE; + return DD_OK; } @@ -362,11 +385,13 @@ namespace dxvk { DWORD total9 = 0; DWORD free9 = 0; - d3d9::IDirect3DDevice9* d3d9Device = m_commonIntf->GetD3D9Device(); - if (likely(d3d9Device != nullptr)) { + D3DCommonDevice* commonDevice = m_commonIntf->GetCommonD3DDevice(); + if (likely(commonDevice != nullptr)) { Logger::debug("DDraw2Interface::GetCaps: Getting memory stats from D3D9"); - total9 = static_cast(m_commonIntf->GetTotalTextureMemory()); + d3d9::IDirect3DDevice9* d3d9Device = commonDevice->GetD3D9Device(); + + total9 = static_cast(commonDevice->GetTotalTextureMemory()); free9 = static_cast(d3d9Device->GetAvailableTextureMem()); if (likely(total9 >= MaxMemory)) { @@ -577,13 +602,13 @@ namespace dxvk { // Switch to a default presentation interval when an application // tries to wait for vertical blank, if we're not already doing so - d3d9::IDirect3DDevice9* d3d9Device = m_commonIntf->GetD3D9Device(); - if (unlikely(d3d9Device != nullptr && !m_commonIntf->GetWaitForVBlank())) { + D3DCommonDevice* commonDevice = m_commonIntf->GetCommonD3DDevice(); + if (unlikely(commonDevice != nullptr && !m_commonIntf->GetWaitForVBlank())) { Logger::info("DDraw2Interface::WaitForVerticalBlank: Switching to D3DPRESENT_INTERVAL_DEFAULT for presentation"); - d3d9::D3DPRESENT_PARAMETERS resetParams = m_commonIntf->GetPresentParameters(); + d3d9::D3DPRESENT_PARAMETERS resetParams = commonDevice->GetPresentParameters(); resetParams.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT; - HRESULT hrReset = m_commonIntf->ResetD3D9Swapchain(&resetParams); + HRESULT hrReset = commonDevice->ResetD3D9Swapchain(&resetParams); if (likely(SUCCEEDED(hrReset))) m_commonIntf->SetWaitForVBlank(true); } @@ -601,11 +626,13 @@ namespace dxvk { static constexpr DWORD MaxMemory = ddrawCaps::MaxTextureMemory * Megabytes; static constexpr DWORD ReservedMemory = ddrawCaps::ReservedTextureMemory * Megabytes; - d3d9::IDirect3DDevice9* d3d9Device = m_commonIntf->GetD3D9Device(); - if (likely(d3d9Device != nullptr)) { + D3DCommonDevice* commonDevice = m_commonIntf->GetCommonD3DDevice(); + if (likely(commonDevice != nullptr)) { Logger::debug("DDraw2Interface::GetAvailableVidMem: Getting memory stats from D3D9"); - DWORD total9 = static_cast(m_commonIntf->GetTotalTextureMemory()); + d3d9::IDirect3DDevice9* d3d9Device = commonDevice->GetD3D9Device(); + + DWORD total9 = static_cast(commonDevice->GetTotalTextureMemory()); DWORD free9 = static_cast(d3d9Device->GetAvailableTextureMem()); if (likely(total9 >= MaxMemory)) { @@ -665,4 +692,4 @@ namespace dxvk { return DD_OK; } -} +} \ No newline at end of file diff --git a/src/ddraw/ddraw2/ddraw2_interface.h b/src/ddraw/ddraw2/ddraw2_interface.h index c3264b3c657..8c7d1824f03 100644 --- a/src/ddraw/ddraw2/ddraw2_interface.h +++ b/src/ddraw/ddraw2/ddraw2_interface.h @@ -9,6 +9,8 @@ #include "../../d3d9/d3d9_bridge.h" +#include "../ddraw/ddraw_interface.h" + namespace dxvk { class DDrawSurface; @@ -80,6 +82,8 @@ namespace dxvk { Com m_commonIntf; + Com m_parentIntf; + }; } \ No newline at end of file diff --git a/src/ddraw/ddraw2/ddraw2_surface.cpp b/src/ddraw/ddraw2/ddraw2_surface.cpp index 4c60b5f42a3..41da66b9c6b 100644 --- a/src/ddraw/ddraw2/ddraw2_surface.cpp +++ b/src/ddraw/ddraw2/ddraw2_surface.cpp @@ -1,5 +1,7 @@ #include "ddraw2_surface.h" +#include "../d3d_common_device.h" + #include "../ddraw_gamma.h" #include "../ddraw/ddraw_interface.h" @@ -10,7 +12,6 @@ #include "../d3d3/d3d3_texture.h" #include "../d3d5/d3d5_device.h" -#include #include "../d3d5/d3d5_texture.h" namespace dxvk { @@ -241,8 +242,14 @@ namespace dxvk { DDraw2Surface* ddraw2Surface = static_cast(lpDDSAttachedSurface); - if (unlikely(ddraw2Surface->GetCommonSurface()->IsBackBufferOrFlippable())) - Logger::warn("DDraw2Surface::AddAttachedSurface: Trying to attach a flippable surface"); + 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"); + } + } HRESULT hr = m_proxy->AddAttachedSurface(ddraw2Surface->GetProxied()); if (unlikely(FAILED(hr))) @@ -304,7 +311,7 @@ namespace dxvk { if (unlikely(lpDDSrcSurface == nullptr && (dwFlags & DDBLT_DEPTHFILL) && lpDDBltFx != nullptr && - m_commonIntf->IsCurrentD3D9DepthStencil(m_d3d9.ptr()))) { + m_commonIntf->GetCommonD3DDevice()->IsCurrentD3D9DepthStencil(m_d3d9.ptr()))) { Logger::debug("DDraw2Surface::Blt: Clearing d3d9 depth stencil"); HRESULT hrClear; @@ -322,7 +329,7 @@ namespace dxvk { if (unlikely(lpDDSrcSurface == nullptr && (dwFlags & DDBLT_COLORFILL) && lpDDBltFx != nullptr && - m_commonIntf->IsCurrentD3D9RenderTarget(m_d3d9.ptr()))) { + m_commonIntf->GetCommonD3DDevice()->IsCurrentD3D9RenderTarget(m_d3d9.ptr()))) { Logger::debug("DDraw2Surface::Blt: Clearing d3d9 render target"); HRESULT hrClear; @@ -356,7 +363,7 @@ namespace dxvk { if (unlikely(!m_commonIntf->IsWrappedSurface(lpDDSrcSurface))) { if (unlikely(lpDDSrcSurface != nullptr)) { Logger::warn("DDraw2Surface::Blt: Received an unwrapped source surface"); - return DDERR_GENERIC; + return DDERR_UNSUPPORTED; } hr = m_proxy->Blt(lpDestRect, lpDDSrcSurface, lpSrcRect, dwFlags, lpDDBltFx); } else { @@ -443,7 +450,7 @@ namespace dxvk { if (unlikely(!m_commonIntf->IsWrappedSurface(lpDDSrcSurface))) { if (unlikely(lpDDSrcSurface != nullptr)) { Logger::warn("DDraw2Surface::BltFast: Received an unwrapped source surface"); - return DDERR_GENERIC; + return DDERR_UNSUPPORTED; } hr = m_proxy->BltFast(dwX, dwY, lpDDSrcSurface, lpSrcRect, dwTrans); } else { @@ -471,7 +478,7 @@ namespace dxvk { if (unlikely(!m_commonIntf->IsWrappedSurface(lpDDSAttachedSurface))) { if (unlikely(lpDDSAttachedSurface != nullptr)) { Logger::warn("DDraw2Surface::DeleteAttachedSurface: Received an unwrapped surface"); - return DDERR_GENERIC; + return DDERR_UNSUPPORTED; } HRESULT hrProxy = m_proxy->DeleteAttachedSurface(dwFlags, lpDDSAttachedSurface); @@ -542,6 +549,9 @@ namespace dxvk { } } + DDraw2Surface* rt = m_commonIntf->GetDDrawRenderTarget() != nullptr ? + m_commonIntf->GetDDrawRenderTarget()->GetDD2Surface() : nullptr; + RefreshD3D9Device(); if (likely(m_d3d9Device != nullptr)) { Logger::debug("*** DDraw2Surface::Flip: Presenting"); @@ -549,12 +559,14 @@ namespace dxvk { m_commonIntf->ResetDrawTracking(); if (unlikely(m_commonIntf->GetOptions()->forceProxiedPresent)) { + D3DCommonDevice* commonDevice = m_commonIntf->GetCommonD3DDevice(); + if (unlikely(!IsInitialized())) - m_parent->InitializeD3D9(m_commonIntf->IsCurrentRenderTarget(m_parent)); + m_parent->InitializeD3D9(commonDevice->IsCurrentRenderTarget(m_parent)); BlitToDDrawSurface(m_proxy.ptr(), m_parent->GetD3D9()); - if (likely(m_commonIntf->IsCurrentRenderTarget(m_parent))) { + if (likely(commonDevice->IsCurrentRenderTarget(m_parent))) { if (lpDDSurfaceTargetOverride != nullptr) { m_commonIntf->SetFlipRTSurfaceAndFlags(surf2->GetParent()->GetProxied(), dwFlags); } else { @@ -562,9 +574,21 @@ namespace dxvk { } } if (lpDDSurfaceTargetOverride != nullptr) { - return m_proxy->Flip(surf2->GetProxied(), dwFlags); + 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 { - return m_proxy->Flip(lpDDSurfaceTargetOverride, dwFlags); + 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); + } } } @@ -574,9 +598,15 @@ namespace dxvk { } else { Logger::debug("<<< DDraw2Surface::Flip: Proxy"); if (lpDDSurfaceTargetOverride == nullptr) { - m_proxy->Flip(lpDDSurfaceTargetOverride, dwFlags); + 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); + } } else { - m_proxy->Flip(surf2->GetProxied(), dwFlags); + return m_proxy->Flip(surf2->GetProxied(), dwFlags); } } @@ -946,9 +976,12 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE DDraw2Surface::UpdateOverlay(LPRECT lpSrcRect, LPDIRECTDRAWSURFACE2 lpDDDestSurface, LPRECT lpDestRect, DWORD dwFlags, LPDDOVERLAYFX lpDDOverlayFx) { Logger::debug("<<< DDraw2Surface::UpdateOverlay: Proxy"); + if (unlikely(lpDDDestSurface == nullptr)) + return DDERR_INVALIDPARAMS; + if (unlikely(!m_commonIntf->IsWrappedSurface(lpDDDestSurface))) { Logger::warn("DDraw2Surface::UpdateOverlay: Received an unwrapped surface"); - return DDERR_GENERIC; + return DDERR_UNSUPPORTED; } DDraw2Surface* ddraw2Surface = static_cast(lpDDDestSurface); @@ -966,7 +999,7 @@ namespace dxvk { if (unlikely(!m_commonIntf->IsWrappedSurface(lpDDSReference))) { Logger::warn("DDraw2Surface::UpdateOverlayZOrder: Received an unwrapped surface"); - return DDERR_GENERIC; + return DDERR_UNSUPPORTED; } DDraw2Surface* ddraw2Surface = static_cast(lpDDSReference); @@ -1000,4 +1033,19 @@ namespace dxvk { return m_proxy->PageUnlock(dwFlags); } + inline void DDraw2Surface::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("DDraw2Surface: Device context has changed, clearing all D3D9 resources"); + m_d3d9 = nullptr; + } + + m_d3d9Device = d3d9Device; + } + } + } diff --git a/src/ddraw/ddraw2/ddraw2_surface.h b/src/ddraw/ddraw2/ddraw2_surface.h index e856c5c4a13..481d10b4ac9 100644 --- a/src/ddraw/ddraw2/ddraw2_surface.h +++ b/src/ddraw/ddraw2/ddraw2_surface.h @@ -155,19 +155,7 @@ namespace dxvk { private: - inline void RefreshD3D9Device() { - if (likely(m_parent != nullptr)) { - d3d9::IDirect3DDevice9* d3d9Device = m_parent->GetCommonInterface()->GetD3D9Device(); - 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; - } - m_d3d9Device = d3d9Device; - } - } - } + inline void RefreshD3D9Device(); static uint32_t s_surfCount; uint32_t m_surfCount = 0; diff --git a/src/ddraw/ddraw2/ddraw3_surface.cpp b/src/ddraw/ddraw2/ddraw3_surface.cpp index 12fa820a587..40c3fb2dfb9 100644 --- a/src/ddraw/ddraw2/ddraw3_surface.cpp +++ b/src/ddraw/ddraw2/ddraw3_surface.cpp @@ -1,5 +1,7 @@ #include "ddraw3_surface.h" +#include "../d3d_common_device.h" + #include "../ddraw_gamma.h" #include "../ddraw/ddraw_interface.h" @@ -10,7 +12,6 @@ #include "../d3d3/d3d3_texture.h" #include "../d3d5/d3d5_device.h" -#include #include "../d3d5/d3d5_texture.h" namespace dxvk { @@ -250,8 +251,14 @@ namespace dxvk { DDraw3Surface* ddraw3Surface = static_cast(lpDDSAttachedSurface); - if (unlikely(ddraw3Surface->GetCommonSurface()->IsBackBufferOrFlippable())) - Logger::warn("DDraw3Surface::AddAttachedSurface: Trying to attach a flippable surface"); + 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"); + } + } HRESULT hr = m_proxy->AddAttachedSurface(ddraw3Surface->GetProxied()); if (unlikely(FAILED(hr))) @@ -314,7 +321,7 @@ namespace dxvk { if (unlikely(lpDDSrcSurface == nullptr && (dwFlags & DDBLT_DEPTHFILL) && lpDDBltFx != nullptr && - m_commonIntf->IsCurrentD3D9DepthStencil(m_d3d9.ptr()))) { + m_commonIntf->GetCommonD3DDevice()->IsCurrentD3D9DepthStencil(m_d3d9.ptr()))) { Logger::debug("DDraw3Surface::Blt: Clearing d3d9 depth stencil"); HRESULT hrClear; @@ -332,7 +339,7 @@ namespace dxvk { if (unlikely(lpDDSrcSurface == nullptr && (dwFlags & DDBLT_COLORFILL) && lpDDBltFx != nullptr && - m_commonIntf->IsCurrentD3D9RenderTarget(m_d3d9.ptr()))) { + m_commonIntf->GetCommonD3DDevice()->IsCurrentD3D9RenderTarget(m_d3d9.ptr()))) { Logger::debug("DDraw3Surface::Blt: Clearing d3d9 render target"); HRESULT hrClear; @@ -373,7 +380,7 @@ namespace dxvk { } else if (unlikely(!m_commonIntf->IsWrappedSurface(lpDDSrcSurface))) { if (unlikely(lpDDSrcSurface != nullptr)) { Logger::warn("DDraw3Surface::Blt: Received an unwrapped source surface"); - return DDERR_GENERIC; + return DDERR_UNSUPPORTED; } hr = m_proxy->Blt(lpDestRect, lpDDSrcSurface, lpSrcRect, dwFlags, lpDDBltFx); } else { @@ -460,7 +467,7 @@ namespace dxvk { if (unlikely(!m_commonIntf->IsWrappedSurface(lpDDSrcSurface))) { if (unlikely(lpDDSrcSurface != nullptr)) { Logger::warn("DDraw3Surface::BltFast: Received an unwrapped source surface"); - return DDERR_GENERIC; + return DDERR_UNSUPPORTED; } hr = m_proxy->BltFast(dwX, dwY, lpDDSrcSurface, lpSrcRect, dwTrans); } else { @@ -488,7 +495,7 @@ namespace dxvk { if (unlikely(!m_commonIntf->IsWrappedSurface(lpDDSAttachedSurface))) { if (unlikely(lpDDSAttachedSurface != nullptr)) { Logger::warn("DDraw3Surface::DeleteAttachedSurface: Received an unwrapped surface"); - return DDERR_GENERIC; + return DDERR_UNSUPPORTED; } HRESULT hrProxy = m_proxy->DeleteAttachedSurface(dwFlags, lpDDSAttachedSurface); @@ -559,6 +566,9 @@ namespace dxvk { } } + DDraw3Surface* rt = m_commonIntf->GetDDrawRenderTarget() != nullptr ? + m_commonIntf->GetDDrawRenderTarget()->GetDD3Surface() : nullptr; + RefreshD3D9Device(); if (likely(m_d3d9Device != nullptr)) { Logger::debug("*** DDraw3Surface::Flip: Presenting"); @@ -566,12 +576,14 @@ namespace dxvk { m_commonIntf->ResetDrawTracking(); if (unlikely(m_commonIntf->GetOptions()->forceProxiedPresent)) { + D3DCommonDevice* commonDevice = m_commonIntf->GetCommonD3DDevice(); + if (unlikely(!IsInitialized())) - m_parent->InitializeD3D9(m_commonIntf->IsCurrentRenderTarget(m_parent)); + m_parent->InitializeD3D9(commonDevice->IsCurrentRenderTarget(m_parent)); BlitToDDrawSurface(m_proxy.ptr(), m_parent->GetD3D9()); - if (likely(m_commonIntf->IsCurrentRenderTarget(m_parent))) { + if (likely(commonDevice->IsCurrentRenderTarget(m_parent))) { if (lpDDSurfaceTargetOverride != nullptr) { m_commonIntf->SetFlipRTSurfaceAndFlags(surf3->GetParent()->GetProxied(), dwFlags); } else { @@ -579,9 +591,21 @@ namespace dxvk { } } if (lpDDSurfaceTargetOverride != nullptr) { - return m_proxy->Flip(surf3->GetProxied(), dwFlags); + 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 { - return m_proxy->Flip(lpDDSurfaceTargetOverride, dwFlags); + 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); + } } } @@ -591,9 +615,15 @@ namespace dxvk { } else { Logger::debug("<<< DDraw3Surface::Flip: Proxy"); if (lpDDSurfaceTargetOverride == nullptr) { - m_proxy->Flip(lpDDSurfaceTargetOverride, dwFlags); + 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); + } } else { - m_proxy->Flip(surf3->GetProxied(), dwFlags); + return m_proxy->Flip(surf3->GetProxied(), dwFlags); } } @@ -963,9 +993,12 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE DDraw3Surface::UpdateOverlay(LPRECT lpSrcRect, LPDIRECTDRAWSURFACE3 lpDDDestSurface, LPRECT lpDestRect, DWORD dwFlags, LPDDOVERLAYFX lpDDOverlayFx) { Logger::debug("<<< DDraw3Surface::UpdateOverlay: Proxy"); + if (unlikely(lpDDDestSurface == nullptr)) + return DDERR_INVALIDPARAMS; + if (unlikely(!m_commonIntf->IsWrappedSurface(lpDDDestSurface))) { Logger::warn("DDraw3Surface::UpdateOverlay: Received an unwrapped surface"); - return DDERR_GENERIC; + return DDERR_UNSUPPORTED; } DDraw3Surface* ddraw3Surface = static_cast(lpDDDestSurface); @@ -983,7 +1016,7 @@ namespace dxvk { if (unlikely(!m_commonIntf->IsWrappedSurface(lpDDSReference))) { Logger::warn("DDraw3Surface::UpdateOverlayZOrder: Received an unwrapped surface"); - return DDERR_GENERIC; + return DDERR_UNSUPPORTED; } DDraw3Surface* ddraw3Surface = static_cast(lpDDSReference); @@ -1042,4 +1075,19 @@ namespace dxvk { return hr; } + inline void DDraw3Surface::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("DDraw3Surface: Device context has changed, clearing all D3D9 resources"); + m_d3d9 = nullptr; + } + + m_d3d9Device = d3d9Device; + } + } + } diff --git a/src/ddraw/ddraw2/ddraw3_surface.h b/src/ddraw/ddraw2/ddraw3_surface.h index 690324c63bb..5e6b4359f3c 100644 --- a/src/ddraw/ddraw2/ddraw3_surface.h +++ b/src/ddraw/ddraw2/ddraw3_surface.h @@ -158,19 +158,7 @@ namespace dxvk { private: - inline void RefreshD3D9Device() { - if (likely(m_parent != nullptr)) { - d3d9::IDirect3DDevice9* d3d9Device = m_parent->GetCommonInterface()->GetD3D9Device(); - 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; - } - m_d3d9Device = d3d9Device; - } - } - } + inline void RefreshD3D9Device(); static uint32_t s_surfCount; uint32_t m_surfCount = 0; diff --git a/src/ddraw/ddraw4/ddraw4_interface.cpp b/src/ddraw/ddraw4/ddraw4_interface.cpp index 6c778decc5f..988a8d0dd1d 100644 --- a/src/ddraw/ddraw4/ddraw4_interface.cpp +++ b/src/ddraw/ddraw4/ddraw4_interface.cpp @@ -1,5 +1,7 @@ #include "ddraw4_interface.h" +#include "../d3d_common_device.h" + #include "ddraw4_surface.h" #include "../ddraw_clipper.h" @@ -12,7 +14,6 @@ #include "../d3d3/d3d3_interface.h" #include "../d3d5/d3d5_interface.h" #include "../d3d6/d3d6_interface.h" -#include namespace dxvk { @@ -34,8 +35,7 @@ namespace dxvk { m_commonIntf->SetAdapterIdentifier(adapterIdentifier9); - if (m_commonIntf->GetOrigin() == nullptr) - m_commonIntf->SetOrigin(this); + // Note: IDirectDraw4 can never be the origin interface m_commonIntf->SetDD4Interface(this); @@ -51,9 +51,6 @@ namespace dxvk { } DDraw4Interface::~DDraw4Interface() { - if (m_commonIntf->GetOrigin() == this) - m_commonIntf->SetOrigin(nullptr); - m_commonIntf->SetDD4Interface(nullptr); Logger::debug(str::format("DDraw4Interface: Interface nr. <<4-", m_intfCount, ">> bites the dust")); @@ -329,7 +326,7 @@ namespace dxvk { } else { if (unlikely(lpDDSurface != nullptr)) { Logger::warn("DDraw7Interface::DuplicateSurface: Received an unwrapped source surface"); - return DDERR_GENERIC; + return DDERR_UNSUPPORTED; } return m_proxy->DuplicateSurface(lpDDSurface, lplpDupDDSurface); } @@ -341,24 +338,26 @@ namespace dxvk { } HRESULT STDMETHODCALLTYPE DDraw4Interface::EnumSurfaces(DWORD dwFlags, LPDDSURFACEDESC2 lpDDSD, LPVOID lpContext, LPDDENUMSURFACESCALLBACK2 lpEnumSurfacesCallback) { - Logger::debug(">>> DDraw4Interface::EnumSurfaces: Proxy"); + Logger::debug("<<< DDraw4Interface::EnumSurfaces: Proxy"); if (unlikely(lpEnumSurfacesCallback == nullptr)) return DDERR_INVALIDPARAMS; std::vector attachedSurfaces; // Enumerate all surfaces from the underlying DDraw implementation - m_proxy->EnumSurfaces(dwFlags, lpDDSD, reinterpret_cast(&attachedSurfaces), EnumAttachedSurfaces4Callback); + HRESULT hr = m_proxy->EnumSurfaces(dwFlags, lpDDSD, reinterpret_cast(&attachedSurfaces), EnumAttachedSurfaces4Callback); + if (unlikely(FAILED(hr))) + return hr; - HRESULT hr = DDENUMRET_OK; + HRESULT hrCB = DDENUMRET_OK; // Wrap surfaces as needed and perform the actual callback the application is requesting auto surfaceIt = attachedSurfaces.begin(); - while (surfaceIt != attachedSurfaces.end() && hr == DDENUMRET_OK) { + while (surfaceIt != attachedSurfaces.end() && hrCB == DDENUMRET_OK) { Com surface4 = surfaceIt->surface4; Com ddraw4Surface = new DDraw4Surface(nullptr, std::move(surface4), this, nullptr, false); - hr = lpEnumSurfacesCallback(ddraw4Surface.ref(), &surfaceIt->desc2, lpContext); + hrCB = lpEnumSurfacesCallback(ddraw4Surface.ref(), &surfaceIt->desc2, lpContext); ++surfaceIt; } @@ -367,10 +366,21 @@ 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"); - if (unlikely(m_commonIntf->GetOptions()->forceProxiedPresent)) - return m_proxy->FlipToGDISurface(); + DDrawCommonSurface* ps = m_commonIntf->GetPrimarySurface(); + + // A primary surface must exist for a GDI flip to be possible + if (unlikely(ps == nullptr)) + return DDERR_NOTFOUND; + + if (unlikely(!ps->IsFlippable())) + return DDERR_NOTFLIPPABLE; return DD_OK; } @@ -391,11 +401,13 @@ namespace dxvk { DWORD total9 = 0; DWORD free9 = 0; - d3d9::IDirect3DDevice9* d3d9Device = m_commonIntf->GetD3D9Device(); - if (likely(d3d9Device != nullptr)) { + D3DCommonDevice* commonDevice = m_commonIntf->GetCommonD3DDevice(); + if (likely(commonDevice != nullptr)) { Logger::debug("DDraw4Interface::GetCaps: Getting memory stats from D3D9"); - total9 = static_cast(m_commonIntf->GetTotalTextureMemory()); + d3d9::IDirect3DDevice9* d3d9Device = commonDevice->GetD3D9Device(); + + total9 = static_cast(commonDevice->GetTotalTextureMemory()); free9 = static_cast(d3d9Device->GetAvailableTextureMem()); if (likely(total9 >= MaxMemory)) { @@ -609,13 +621,13 @@ namespace dxvk { // Switch to a default presentation interval when an application // tries to wait for vertical blank, if we're not already doing so - d3d9::IDirect3DDevice9* d3d9Device = m_commonIntf->GetD3D9Device(); - if (unlikely(d3d9Device != nullptr && !m_commonIntf->GetWaitForVBlank())) { + D3DCommonDevice* commonDevice = m_commonIntf->GetCommonD3DDevice(); + if (unlikely(commonDevice != nullptr && !m_commonIntf->GetWaitForVBlank())) { Logger::info("DDraw4Interface::WaitForVerticalBlank: Switching to D3DPRESENT_INTERVAL_DEFAULT for presentation"); - d3d9::D3DPRESENT_PARAMETERS resetParams = m_commonIntf->GetPresentParameters(); + d3d9::D3DPRESENT_PARAMETERS resetParams = commonDevice->GetPresentParameters(); resetParams.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT; - HRESULT hrReset = m_commonIntf->ResetD3D9Swapchain(&resetParams); + HRESULT hrReset = commonDevice->ResetD3D9Swapchain(&resetParams); if (likely(SUCCEEDED(hrReset))) m_commonIntf->SetWaitForVBlank(true); } @@ -633,11 +645,13 @@ namespace dxvk { static constexpr DWORD MaxMemory = ddrawCaps::MaxTextureMemory * Megabytes; static constexpr DWORD ReservedMemory = ddrawCaps::ReservedTextureMemory * Megabytes; - d3d9::IDirect3DDevice9* d3d9Device = m_commonIntf->GetD3D9Device(); - if (likely(d3d9Device != nullptr)) { + D3DCommonDevice* commonDevice = m_commonIntf->GetCommonD3DDevice(); + if (likely(commonDevice != nullptr)) { Logger::debug("DDraw4Interface::GetAvailableVidMem: Getting memory stats from D3D9"); - DWORD total9 = static_cast(m_commonIntf->GetTotalTextureMemory()); + d3d9::IDirect3DDevice9* d3d9Device = commonDevice->GetD3D9Device(); + + DWORD total9 = static_cast(commonDevice->GetTotalTextureMemory()); DWORD free9 = static_cast(d3d9Device->GetAvailableTextureMem()); if (likely(total9 >= MaxMemory)) { @@ -727,15 +741,17 @@ namespace dxvk { } HRESULT STDMETHODCALLTYPE DDraw4Interface::TestCooperativeLevel() { - d3d9::IDirect3DDevice9* d3d9Device = m_commonIntf->GetD3D9Device(); + D3DCommonDevice* commonDevice = m_commonIntf->GetCommonD3DDevice(); - if (unlikely(d3d9Device == nullptr)) { + if (unlikely(commonDevice == nullptr)) { Logger::debug("<<< DDraw4Interface::TestCooperativeLevel: Proxy"); return m_proxy->TestCooperativeLevel(); } Logger::debug(">>> DDraw4Interface::TestCooperativeLevel"); + d3d9::IDirect3DDevice9* d3d9Device = commonDevice->GetD3D9Device(); + HRESULT hr = d3d9Device->TestCooperativeLevel(); if (unlikely(FAILED(hr))) return DDERR_NOEXCLUSIVEMODE; @@ -785,4 +801,4 @@ namespace dxvk { return DD_OK; } -} +} \ No newline at end of file diff --git a/src/ddraw/ddraw4/ddraw4_surface.cpp b/src/ddraw/ddraw4/ddraw4_surface.cpp index 4c11d05ff38..20007d3b9e6 100644 --- a/src/ddraw/ddraw4/ddraw4_surface.cpp +++ b/src/ddraw/ddraw4/ddraw4_surface.cpp @@ -1,5 +1,7 @@ #include "ddraw4_surface.h" +#include "../d3d_common_device.h" + #include "ddraw4_interface.h" #include "../ddraw_gamma.h" @@ -7,7 +9,6 @@ #include "../ddraw2/ddraw2_surface.h" #include "../ddraw2/ddraw3_surface.h" #include "../ddraw7/ddraw7_surface.h" -#include #include "../d3d3/d3d3_texture.h" #include "../d3d6/d3d6_texture.h" @@ -244,8 +245,14 @@ namespace dxvk { DDraw4Surface* ddraw4Surface = static_cast(lpDDSAttachedSurface); - if (unlikely(ddraw4Surface->GetCommonSurface()->IsBackBufferOrFlippable())) - Logger::warn("DDraw4Surface::AddAttachedSurface: Trying to attach a flippable surface"); + 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"); + } + } HRESULT hr = m_proxy->AddAttachedSurface(ddraw4Surface->GetProxied()); if (unlikely(FAILED(hr))) @@ -306,7 +313,7 @@ namespace dxvk { if (unlikely(lpDDSrcSurface == nullptr && (dwFlags & DDBLT_DEPTHFILL) && lpDDBltFx != nullptr && - m_commonIntf->IsCurrentD3D9DepthStencil(m_d3d9.ptr()))) { + m_commonIntf->GetCommonD3DDevice()->IsCurrentD3D9DepthStencil(m_d3d9.ptr()))) { Logger::debug("DDraw4Surface::Blt: Clearing d3d9 depth stencil"); HRESULT hrClear; @@ -324,7 +331,7 @@ namespace dxvk { if (unlikely(lpDDSrcSurface == nullptr && (dwFlags & DDBLT_COLORFILL) && lpDDBltFx != nullptr && - m_commonIntf->IsCurrentD3D9RenderTarget(m_d3d9.ptr()))) { + m_commonIntf->GetCommonD3DDevice()->IsCurrentD3D9RenderTarget(m_d3d9.ptr()))) { Logger::debug("DDraw4Surface::Blt: Clearing d3d9 render target"); HRESULT hrClear; @@ -358,7 +365,7 @@ namespace dxvk { if (unlikely(!m_commonIntf->IsWrappedSurface(lpDDSrcSurface))) { if (unlikely(lpDDSrcSurface != nullptr)) { Logger::warn("DDraw4Surface::Blt: Received an unwrapped source surface"); - return DDERR_GENERIC; + return DDERR_UNSUPPORTED; } hr = m_proxy->Blt(lpDestRect, lpDDSrcSurface, lpSrcRect, dwFlags, lpDDBltFx); } else { @@ -445,7 +452,7 @@ namespace dxvk { if (unlikely(!m_commonIntf->IsWrappedSurface(lpDDSrcSurface))) { if (unlikely(lpDDSrcSurface != nullptr)) { Logger::warn("DDraw4Surface::BltFast: Received an unwrapped source surface"); - return DDERR_GENERIC; + return DDERR_UNSUPPORTED; } hr = m_proxy->BltFast(dwX, dwY, lpDDSrcSurface, lpSrcRect, dwTrans); } else { @@ -474,7 +481,7 @@ namespace dxvk { if (unlikely(!m_commonIntf->IsWrappedSurface(lpDDSAttachedSurface))) { if (unlikely(lpDDSAttachedSurface != nullptr)) { Logger::warn("DDraw4Surface::DeleteAttachedSurface: Received an unwrapped surface"); - return DDERR_GENERIC; + return DDERR_UNSUPPORTED; } HRESULT hrProxy = m_proxy->DeleteAttachedSurface(dwFlags, lpDDSAttachedSurface); @@ -588,28 +595,48 @@ namespace dxvk { } } + 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(); + if (unlikely(!IsInitialized())) - InitializeD3D9(m_commonIntf->IsCurrentRenderTarget(this)); + InitializeD3D9(commonDevice->IsCurrentRenderTarget(this)); BlitToDDrawSurface(m_proxy.ptr(), m_d3d9.ptr()); if (unlikely(!m_commonIntf->IsWrappedSurface(lpDDSurfaceTargetOverride))) { if (unlikely(lpDDSurfaceTargetOverride != nullptr)) { Logger::warn("DDraw4Surface::Flip: Received an unwrapped surface"); - return DDERR_GENERIC; + return DDERR_UNSUPPORTED; } - if (likely(m_commonIntf->IsCurrentRenderTarget(this))) + + if (likely(commonDevice->IsCurrentRenderTarget(this))) m_commonIntf->SetFlipRTSurfaceAndFlags(lpDDSurfaceTargetOverride, dwFlags); - return m_proxy->Flip(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(lpDDSurfaceTargetOverride, dwFlags); + } } else { - if (likely(m_commonIntf->IsCurrentRenderTarget(this))) + if (likely(commonDevice->IsCurrentRenderTarget(this))) m_commonIntf->SetFlipRTSurfaceAndFlags(lpDDSurfaceTargetOverride, dwFlags); - return m_proxy->Flip(surf4->GetProxied(), 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); + } } } @@ -618,9 +645,11 @@ namespace dxvk { if (unlikely(m_commonIntf->GetWaitForVBlank() && (dwFlags & DDFLIP_NOVSYNC))) { Logger::info("DDraw4Surface::Flip: Switching to D3DPRESENT_INTERVAL_IMMEDIATE for presentation"); - d3d9::D3DPRESENT_PARAMETERS resetParams = m_commonIntf->GetPresentParameters(); + D3DCommonDevice* commonDevice = m_commonIntf->GetCommonD3DDevice(); + + d3d9::D3DPRESENT_PARAMETERS resetParams = commonDevice->GetPresentParameters(); resetParams.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; - HRESULT hrReset = m_commonIntf->ResetD3D9Swapchain(&resetParams); + HRESULT hrReset = commonDevice->ResetD3D9Swapchain(&resetParams); if (unlikely(FAILED(hrReset))) { Logger::warn("DDraw4Surface::Flip: Failed D3D9 swapchain reset"); } else { @@ -631,9 +660,11 @@ namespace dxvk { } else if (unlikely(!m_commonIntf->GetWaitForVBlank() && IsVSyncFlipFlag(dwFlags))) { Logger::info("DDraw4Surface::Flip: Switching to D3DPRESENT_INTERVAL_DEFAULT for presentation"); - d3d9::D3DPRESENT_PARAMETERS resetParams = m_commonIntf->GetPresentParameters(); + D3DCommonDevice* commonDevice = m_commonIntf->GetCommonD3DDevice(); + + d3d9::D3DPRESENT_PARAMETERS resetParams = commonDevice->GetPresentParameters(); resetParams.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT; - HRESULT hrReset = m_commonIntf->ResetD3D9Swapchain(&resetParams); + HRESULT hrReset = commonDevice->ResetD3D9Swapchain(&resetParams); if (unlikely(FAILED(hrReset))) { Logger::warn("DDraw4Surface::Flip: Failed D3D9 swapchain reset"); } else { @@ -651,9 +682,15 @@ namespace dxvk { m_commonIntf->SetWaitForVBlank(IsVSyncFlipFlag(dwFlags)); if (unlikely(!m_commonIntf->IsWrappedSurface(lpDDSurfaceTargetOverride))) { - m_proxy->Flip(lpDDSurfaceTargetOverride, dwFlags); + 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); + } } else { - m_proxy->Flip(surf4->GetProxied(), dwFlags); + return m_proxy->Flip(surf4->GetProxied(), dwFlags); } } @@ -1046,9 +1083,12 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE DDraw4Surface::UpdateOverlay(LPRECT lpSrcRect, LPDIRECTDRAWSURFACE4 lpDDDestSurface, LPRECT lpDestRect, DWORD dwFlags, LPDDOVERLAYFX lpDDOverlayFx) { Logger::debug("<<< DDraw4Surface::UpdateOverlay: Proxy"); + if (unlikely(lpDDDestSurface == nullptr)) + return DDERR_INVALIDPARAMS; + if (unlikely(!m_commonIntf->IsWrappedSurface(lpDDDestSurface))) { Logger::warn("DDraw4Surface::UpdateOverlay: Received an unwrapped surface"); - return DDERR_GENERIC; + return DDERR_UNSUPPORTED; } DDraw4Surface* ddraw4Surface = static_cast(lpDDDestSurface); @@ -1066,7 +1106,7 @@ namespace dxvk { if (unlikely(!m_commonIntf->IsWrappedSurface(lpDDSReference))) { Logger::warn("DDraw4Surface::UpdateOverlayZOrder: Received an unwrapped surface"); - return DDERR_GENERIC; + return DDERR_UNSUPPORTED; } DDraw4Surface* ddraw4Surface = static_cast(lpDDSReference); @@ -1333,7 +1373,7 @@ namespace dxvk { 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->GetMultiSampleType(); + const d3d9::D3DMULTISAMPLE_TYPE multiSampleType = m_commonIntf->GetCommonD3DDevice()->GetMultiSampleType(); const uint32_t index = m_commonSurf->GetBackBufferIndex(); Com surf; @@ -1418,7 +1458,7 @@ namespace dxvk { // 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->IsCurrentRenderTarget(this) || initRT)) { + if (unlikely(m_commonIntf->GetCommonD3DDevice()->IsCurrentRenderTarget(this) || initRT)) { Logger::debug("DDraw4Surface::InitializeD3D9: Offscreen plain surface is the RT"); m_d3d9Device->GetBackBuffer(0, index, d3d9::D3DBACKBUFFER_TYPE_MONO, &surf); @@ -1534,4 +1574,20 @@ namespace dxvk { return DD_OK; } + inline void DDraw4Surface::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("DDraw4Surface: Device context has changed, clearing all D3D9 resources"); + m_texture9 = nullptr; + m_d3d9 = nullptr; + } + + m_d3d9Device = d3d9Device; + } + } + } diff --git a/src/ddraw/ddraw4/ddraw4_surface.h b/src/ddraw/ddraw4/ddraw4_surface.h index 6bb28cc5072..a2899573043 100644 --- a/src/ddraw/ddraw4/ddraw4_surface.h +++ b/src/ddraw/ddraw4/ddraw4_surface.h @@ -178,18 +178,7 @@ namespace dxvk { inline HRESULT UploadSurfaceData(); - inline void RefreshD3D9Device() { - d3d9::IDirect3DDevice9* d3d9Device = m_commonIntf->GetD3D9Device(); - 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; - } - } + inline void RefreshD3D9Device(); bool m_isChildObject = true; diff --git a/src/ddraw/ddraw7/ddraw7_interface.cpp b/src/ddraw/ddraw7/ddraw7_interface.cpp index 9cc0beedb34..f46e3b9531d 100644 --- a/src/ddraw/ddraw7/ddraw7_interface.cpp +++ b/src/ddraw/ddraw7/ddraw7_interface.cpp @@ -1,5 +1,7 @@ #include "ddraw7_interface.h" +#include "../d3d_common_device.h" + #include "ddraw7_surface.h" #include "../ddraw_clipper.h" @@ -11,7 +13,6 @@ #include "../d3d7/d3d7_interface.h" #include "../d3d7/d3d7_device.h" -#include namespace dxvk { @@ -296,7 +297,7 @@ namespace dxvk { } else { if (unlikely(lpDDSurface != nullptr)) { Logger::warn("DDraw7Interface::DuplicateSurface: Received an unwrapped source surface"); - return DDERR_GENERIC; + return DDERR_UNSUPPORTED; } return m_proxy->DuplicateSurface(lpDDSurface, lplpDupDDSurface); } @@ -310,24 +311,26 @@ namespace dxvk { } HRESULT STDMETHODCALLTYPE DDraw7Interface::EnumSurfaces(DWORD dwFlags, LPDDSURFACEDESC2 lpDDSD, LPVOID lpContext, LPDDENUMSURFACESCALLBACK7 lpEnumSurfacesCallback) { - Logger::debug(">>> DDraw7Interface::EnumSurfaces: Proxy"); + Logger::debug("<<< DDraw7Interface::EnumSurfaces: Proxy"); if (unlikely(lpEnumSurfacesCallback == nullptr)) return DDERR_INVALIDPARAMS; std::vector attachedSurfaces; // Enumerate all surfaces from the underlying DDraw implementation - m_proxy->EnumSurfaces(dwFlags, lpDDSD, reinterpret_cast(&attachedSurfaces), EnumAttachedSurfaces7Callback); + HRESULT hr = m_proxy->EnumSurfaces(dwFlags, lpDDSD, reinterpret_cast(&attachedSurfaces), EnumAttachedSurfaces7Callback); + if (unlikely(FAILED(hr))) + return hr; - HRESULT hr = DDENUMRET_OK; + HRESULT hrCB = DDENUMRET_OK; // Wrap surfaces as needed and perform the actual callback the application is requesting auto surfaceIt = attachedSurfaces.begin(); - while (surfaceIt != attachedSurfaces.end() && hr == DDENUMRET_OK) { + while (surfaceIt != attachedSurfaces.end() && hrCB == DDENUMRET_OK) { Com surface7 = surfaceIt->surface7; Com ddraw7Surface = new DDraw7Surface(nullptr, std::move(surface7), this, nullptr, false); - hr = lpEnumSurfacesCallback(ddraw7Surface.ref(), &surfaceIt->desc2, lpContext); + hrCB = lpEnumSurfacesCallback(ddraw7Surface.ref(), &surfaceIt->desc2, lpContext); ++surfaceIt; } @@ -336,10 +339,21 @@ 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"); - if (unlikely(m_commonIntf->GetOptions()->forceProxiedPresent)) - return m_proxy->FlipToGDISurface(); + DDrawCommonSurface* ps = m_commonIntf->GetPrimarySurface(); + + // A primary surface must exist for a GDI flip to be possible + if (unlikely(ps == nullptr)) + return DDERR_NOTFOUND; + + if (unlikely(!ps->IsFlippable())) + return DDERR_NOTFLIPPABLE; return DD_OK; } @@ -360,11 +374,13 @@ namespace dxvk { DWORD total9 = 0; DWORD free9 = 0; - d3d9::IDirect3DDevice9* d3d9Device = m_commonIntf->GetD3D9Device(); - if (likely(d3d9Device != nullptr)) { + D3DCommonDevice* commonDevice = m_commonIntf->GetCommonD3DDevice(); + if (likely(commonDevice != nullptr)) { + d3d9::IDirect3DDevice9* d3d9Device = commonDevice->GetD3D9Device(); + Logger::debug("DDraw7Interface::GetCaps: Getting memory stats from D3D9"); - total9 = static_cast(m_commonIntf->GetTotalTextureMemory()); + total9 = static_cast(commonDevice->GetTotalTextureMemory()); free9 = static_cast(d3d9Device->GetAvailableTextureMem()); if (likely(total9 >= MaxMemory)) { @@ -578,13 +594,13 @@ namespace dxvk { // Switch to a default presentation interval when an application // tries to wait for vertical blank, if we're not already doing so - d3d9::IDirect3DDevice9* d3d9Device = m_commonIntf->GetD3D9Device(); - if (unlikely(d3d9Device != nullptr && !m_commonIntf->GetWaitForVBlank())) { + D3DCommonDevice* commonDevice = m_commonIntf->GetCommonD3DDevice(); + if (unlikely(commonDevice != nullptr && !m_commonIntf->GetWaitForVBlank())) { Logger::info("DDraw7Interface::WaitForVerticalBlank: Switching to D3DPRESENT_INTERVAL_DEFAULT for presentation"); - d3d9::D3DPRESENT_PARAMETERS resetParams = m_commonIntf->GetPresentParameters(); + d3d9::D3DPRESENT_PARAMETERS resetParams = commonDevice->GetPresentParameters(); resetParams.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT; - HRESULT hrReset = m_commonIntf->ResetD3D9Swapchain(&resetParams); + HRESULT hrReset = commonDevice->ResetD3D9Swapchain(&resetParams); if (likely(SUCCEEDED(hrReset))) m_commonIntf->SetWaitForVBlank(true); } @@ -602,11 +618,13 @@ namespace dxvk { static constexpr DWORD MaxMemory = ddrawCaps::MaxTextureMemory * Megabytes; static constexpr DWORD ReservedMemory = ddrawCaps::ReservedTextureMemory * Megabytes; - d3d9::IDirect3DDevice9* d3d9Device = m_commonIntf->GetD3D9Device(); - if (likely(d3d9Device != nullptr)) { + D3DCommonDevice* commonDevice = m_commonIntf->GetCommonD3DDevice(); + if (likely(commonDevice != nullptr)) { + d3d9::IDirect3DDevice9* d3d9Device = commonDevice->GetD3D9Device(); + Logger::debug("DDraw7Interface::GetAvailableVidMem: Getting memory stats from D3D9"); - DWORD total9 = static_cast(m_commonIntf->GetTotalTextureMemory()); + DWORD total9 = static_cast(commonDevice->GetTotalTextureMemory()); DWORD free9 = static_cast(d3d9Device->GetAvailableTextureMem()); if (likely(total9 >= MaxMemory)) { @@ -698,15 +716,17 @@ namespace dxvk { } HRESULT STDMETHODCALLTYPE DDraw7Interface::TestCooperativeLevel() { - d3d9::IDirect3DDevice9* d3d9Device = m_commonIntf->GetD3D9Device(); + D3DCommonDevice* commonDevice = m_commonIntf->GetCommonD3DDevice(); - if (unlikely(d3d9Device == nullptr)) { + if (unlikely(commonDevice == nullptr)) { Logger::debug("<<< DDraw7Interface::TestCooperativeLevel: Proxy"); return m_proxy->TestCooperativeLevel(); } Logger::debug(">>> DDraw7Interface::TestCooperativeLevel"); + d3d9::IDirect3DDevice9* d3d9Device = commonDevice->GetD3D9Device(); + HRESULT hr = d3d9Device->TestCooperativeLevel(); if (unlikely(FAILED(hr))) return DDERR_NOEXCLUSIVEMODE; @@ -775,4 +795,4 @@ namespace dxvk { return DD_OK; } -} +} \ No newline at end of file diff --git a/src/ddraw/ddraw7/ddraw7_surface.cpp b/src/ddraw/ddraw7/ddraw7_surface.cpp index 9b2d2e70dff..35f9069c8e5 100644 --- a/src/ddraw/ddraw7/ddraw7_surface.cpp +++ b/src/ddraw/ddraw7/ddraw7_surface.cpp @@ -1,11 +1,12 @@ #include "ddraw7_surface.h" +#include "../d3d_common_device.h" + #include "../ddraw_gamma.h" #include "../ddraw/ddraw_surface.h" #include "../ddraw2/ddraw2_surface.h" #include "../ddraw2/ddraw3_surface.h" #include "../ddraw4/ddraw4_surface.h" -#include #include "../d3d3/d3d3_texture.h" #include "../d3d6/d3d6_texture.h" @@ -286,7 +287,7 @@ namespace dxvk { if (unlikely(lpDDSrcSurface == nullptr && (dwFlags & DDBLT_DEPTHFILL) && lpDDBltFx != nullptr && - m_commonIntf->IsCurrentD3D9DepthStencil(m_d3d9.ptr()))) { + m_commonIntf->GetCommonD3DDevice()->IsCurrentD3D9DepthStencil(m_d3d9.ptr()))) { Logger::debug("DDraw7Surface::Blt: Clearing d3d9 depth stencil"); HRESULT hrClear; @@ -304,7 +305,7 @@ namespace dxvk { if (unlikely(lpDDSrcSurface == nullptr && (dwFlags & DDBLT_COLORFILL) && lpDDBltFx != nullptr && - m_commonIntf->IsCurrentD3D9RenderTarget(m_d3d9.ptr()))) { + m_commonIntf->GetCommonD3DDevice()->IsCurrentD3D9RenderTarget(m_d3d9.ptr()))) { Logger::debug("DDraw7Surface::Blt: Clearing d3d9 render target"); HRESULT hrClear; @@ -338,7 +339,7 @@ namespace dxvk { if (unlikely(!m_commonIntf->IsWrappedSurface(lpDDSrcSurface))) { if (unlikely(lpDDSrcSurface != nullptr)) { Logger::warn("DDraw7Surface::Blt: Received an unwrapped source surface"); - return DDERR_GENERIC; + return DDERR_UNSUPPORTED; } hr = m_proxy->Blt(lpDestRect, lpDDSrcSurface, lpSrcRect, dwFlags, lpDDBltFx); } else { @@ -425,7 +426,7 @@ namespace dxvk { if (unlikely(!m_commonIntf->IsWrappedSurface(lpDDSrcSurface))) { if (unlikely(lpDDSrcSurface != nullptr)) { Logger::warn("DDraw7Surface::BltFast: Received an unwrapped source surface"); - return DDERR_GENERIC; + return DDERR_UNSUPPORTED; } hr = m_proxy->BltFast(dwX, dwY, lpDDSrcSurface, lpSrcRect, dwTrans); } else { @@ -454,7 +455,7 @@ namespace dxvk { if (unlikely(!m_commonIntf->IsWrappedSurface(lpDDSAttachedSurface))) { if (unlikely(lpDDSAttachedSurface != nullptr)) { Logger::warn("DDraw7Surface::DeleteAttachedSurface: Received an unwrapped surface"); - return DDERR_GENERIC; + return DDERR_UNSUPPORTED; } HRESULT hrProxy = m_proxy->DeleteAttachedSurface(dwFlags, lpDDSAttachedSurface); @@ -570,21 +571,23 @@ namespace dxvk { m_commonIntf->ResetDrawTracking(); if (unlikely(m_commonIntf->GetOptions()->forceProxiedPresent)) { + D3DCommonDevice* commonDevice = m_commonIntf->GetCommonD3DDevice(); + if (unlikely(!IsInitialized())) - InitializeD3D9(m_commonIntf->IsCurrentRenderTarget(this)); + InitializeD3D9(commonDevice->IsCurrentRenderTarget(this)); BlitToDDrawSurface(m_proxy.ptr(), m_d3d9.ptr()); if (unlikely(!m_commonIntf->IsWrappedSurface(lpDDSurfaceTargetOverride))) { if (unlikely(lpDDSurfaceTargetOverride != nullptr)) { Logger::warn("DDraw7Surface::Flip: Received an unwrapped surface"); - return DDERR_GENERIC; + return DDERR_UNSUPPORTED; } - if (likely(m_commonIntf->IsCurrentRenderTarget(this))) + if (likely(commonDevice->IsCurrentRenderTarget(this))) m_commonIntf->SetFlipRTSurfaceAndFlags(lpDDSurfaceTargetOverride, dwFlags); return m_proxy->Flip(lpDDSurfaceTargetOverride, dwFlags); } else { - if (likely(m_commonIntf->IsCurrentRenderTarget(this))) + if (likely(commonDevice->IsCurrentRenderTarget(this))) m_commonIntf->SetFlipRTSurfaceAndFlags(lpDDSurfaceTargetOverride, dwFlags); return m_proxy->Flip(surf7->GetProxied(), dwFlags); } @@ -595,9 +598,11 @@ namespace dxvk { if (unlikely(m_commonIntf->GetWaitForVBlank() && (dwFlags & DDFLIP_NOVSYNC))) { Logger::info("DDraw7Surface::Flip: Switching to D3DPRESENT_INTERVAL_IMMEDIATE for presentation"); - d3d9::D3DPRESENT_PARAMETERS resetParams = m_commonIntf->GetPresentParameters(); + D3DCommonDevice* commonDevice = m_commonIntf->GetCommonD3DDevice(); + + d3d9::D3DPRESENT_PARAMETERS resetParams = commonDevice->GetPresentParameters(); resetParams.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; - HRESULT hrReset = m_commonIntf->ResetD3D9Swapchain(&resetParams); + HRESULT hrReset = commonDevice->ResetD3D9Swapchain(&resetParams); if (unlikely(FAILED(hrReset))) { Logger::warn("DDraw7Surface::Flip: Failed D3D9 swapchain reset"); } else { @@ -608,9 +613,11 @@ namespace dxvk { } else if (unlikely(!m_commonIntf->GetWaitForVBlank() && IsVSyncFlipFlag(dwFlags))) { Logger::info("DDraw7Surface::Flip: Switching to D3DPRESENT_INTERVAL_DEFAULT for presentation"); - d3d9::D3DPRESENT_PARAMETERS resetParams = m_commonIntf->GetPresentParameters(); + D3DCommonDevice* commonDevice = m_commonIntf->GetCommonD3DDevice(); + + d3d9::D3DPRESENT_PARAMETERS resetParams = commonDevice->GetPresentParameters(); resetParams.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT; - HRESULT hrReset = m_commonIntf->ResetD3D9Swapchain(&resetParams); + HRESULT hrReset = commonDevice->ResetD3D9Swapchain(&resetParams); if (unlikely(FAILED(hrReset))) { Logger::warn("DDraw7Surface::Flip: Failed D3D9 swapchain reset"); } else { @@ -628,9 +635,9 @@ namespace dxvk { m_commonIntf->SetWaitForVBlank(IsVSyncFlipFlag(dwFlags)); if (unlikely(!m_commonIntf->IsWrappedSurface(lpDDSurfaceTargetOverride))) { - m_proxy->Flip(lpDDSurfaceTargetOverride, dwFlags); + return m_proxy->Flip(lpDDSurfaceTargetOverride, dwFlags); } else { - m_proxy->Flip(surf7->GetProxied(), dwFlags); + return m_proxy->Flip(surf7->GetProxied(), dwFlags); } } @@ -1025,9 +1032,12 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE DDraw7Surface::UpdateOverlay(LPRECT lpSrcRect, LPDIRECTDRAWSURFACE7 lpDDDestSurface, LPRECT lpDestRect, DWORD dwFlags, LPDDOVERLAYFX lpDDOverlayFx) { Logger::debug("<<< DDraw7Surface::UpdateOverlay: Proxy"); + if (unlikely(lpDDDestSurface == nullptr)) + return DDERR_INVALIDPARAMS; + if (unlikely(!m_commonIntf->IsWrappedSurface(lpDDDestSurface))) { Logger::warn("DDraw7Surface::UpdateOverlay: Received an unwrapped surface"); - return DDERR_GENERIC; + return DDERR_UNSUPPORTED; } DDraw7Surface* ddraw7Surface = static_cast(lpDDDestSurface); @@ -1045,7 +1055,7 @@ namespace dxvk { if (unlikely(!m_commonIntf->IsWrappedSurface(lpDDSReference))) { Logger::warn("DDraw7Surface::UpdateOverlayZOrder: Received an unwrapped surface"); - return DDERR_GENERIC; + return DDERR_UNSUPPORTED; } DDraw7Surface* ddraw7Surface = static_cast(lpDDSReference); @@ -1432,7 +1442,7 @@ namespace dxvk { 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->GetMultiSampleType(); + const d3d9::D3DMULTISAMPLE_TYPE multiSampleType = m_commonIntf->GetCommonD3DDevice()->GetMultiSampleType(); const uint32_t index = m_commonSurf->GetBackBufferIndex(); Com surf; @@ -1587,7 +1597,7 @@ namespace dxvk { // 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->IsCurrentRenderTarget(this) || 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); @@ -1739,4 +1749,21 @@ namespace dxvk { return DD_OK; } + 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; + } + } + } diff --git a/src/ddraw/ddraw7/ddraw7_surface.h b/src/ddraw/ddraw7/ddraw7_surface.h index cf4558b5366..7514d214d2c 100644 --- a/src/ddraw/ddraw7/ddraw7_surface.h +++ b/src/ddraw/ddraw7/ddraw7_surface.h @@ -191,19 +191,7 @@ namespace dxvk { inline HRESULT UploadSurfaceData(); - inline void RefreshD3D9Device() { - d3d9::IDirect3DDevice9* d3d9Device = m_commonIntf->GetD3D9Device(); - 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; - } - } + inline void RefreshD3D9Device(); bool m_isChildObject = false; diff --git a/src/ddraw/ddraw_common_interface.cpp b/src/ddraw/ddraw_common_interface.cpp index eef3e2db713..262c93f54a4 100644 --- a/src/ddraw/ddraw_common_interface.cpp +++ b/src/ddraw/ddraw_common_interface.cpp @@ -1,15 +1,9 @@ #include "ddraw_common_interface.h" +#include "d3d_common_device.h" #include "d3d_common_texture.h" #include "ddraw/ddraw_surface.h" -#include "ddraw4/ddraw4_surface.h" -#include "ddraw7/ddraw7_surface.h" - -#include "d3d3/d3d3_device.h" -#include "d3d5/d3d5_device.h" -#include "d3d6/d3d6_device.h" -#include "d3d7/d3d7_device.h" #include @@ -187,133 +181,6 @@ namespace dxvk { } } - d3d9::IDirect3DDevice9* DDrawCommonInterface::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; - } - - uint32_t DDrawCommonInterface::GetTotalTextureMemory() { - if (m_device7 != nullptr) { - return m_device7->GetTotalTextureMemory(); - } else if (m_device6 != nullptr) { - return m_device6->GetTotalTextureMemory(); - } else if (m_device5 != nullptr) { - return m_device5->GetTotalTextureMemory(); - } else if (m_device3 != nullptr) { - return m_device3->GetTotalTextureMemory(); - } - - return 0; - } - - d3d9::D3DMULTISAMPLE_TYPE DDrawCommonInterface::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 DDrawCommonInterface::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 DDrawCommonInterface::ResetD3D9Swapchain(d3d9::D3DPRESENT_PARAMETERS* params) { - if (m_device7 != nullptr) { - return m_device7->ResetD3D9Swapchain(params); - } else if (m_device6 != nullptr) { - return m_device6->ResetD3D9Swapchain(params); - } - // D3D3/5 has no way of disabling/re-enabling VSync - - return DDERR_GENERIC; - } - - bool DDrawCommonInterface::IsCurrentRenderTarget(DDrawSurface* surface) const { - return m_device5 != nullptr ? m_device5->GetRenderTarget() == surface : - m_device3 != nullptr ? m_device3->GetRenderTarget() == surface : false; - } - - bool DDrawCommonInterface::IsCurrentRenderTarget(DDraw4Surface* surface) const { - return m_device6 != nullptr ? m_device6->GetRenderTarget() == surface : false; - } - - bool DDrawCommonInterface::IsCurrentRenderTarget(DDraw7Surface* surface) const { - return m_device7 != nullptr ? m_device7->GetRenderTarget() == surface : false; - } - - bool DDrawCommonInterface::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; - } - - bool DDrawCommonInterface::IsCurrentDepthStencil(DDrawSurface* surface) const { - return m_device5 != nullptr ? m_device5->GetDepthStencil() == surface : - m_device3 != nullptr ? m_device3->GetDepthStencil() == surface : false; - } - - bool DDrawCommonInterface::IsCurrentDepthStencil(DDraw4Surface* surface) const { - return m_device6 != nullptr ? m_device6->GetDepthStencil() == surface : false; - } - - bool DDrawCommonInterface::IsCurrentDepthStencil(DDraw7Surface* surface) const { - return m_device7 != nullptr ? m_device7->GetDepthStencil() == surface : false; - } - - bool DDrawCommonInterface::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; - } - DDrawSurface* DDrawCommonInterface::GetSurfaceFromTextureHandle(D3DTEXTUREHANDLE handle) const { auto texturesIter = m_textures.find(handle); diff --git a/src/ddraw/ddraw_common_interface.h b/src/ddraw/ddraw_common_interface.h index 1d178c8953f..fb1e3cd9f17 100644 --- a/src/ddraw/ddraw_common_interface.h +++ b/src/ddraw/ddraw_common_interface.h @@ -8,6 +8,7 @@ namespace dxvk { + class D3DCommonDevice; class D3DCommonTexture; class DDrawCommonSurface; @@ -20,13 +21,6 @@ namespace dxvk { class DDrawInterface; class DDrawSurface; - class DDraw4Surface; - class DDraw7Surface; - - class D3D7Device; - class D3D6Device; - class D3D5Device; - class D3D3Device; class DDrawCommonInterface : public ComObjectClamp { @@ -66,32 +60,6 @@ namespace dxvk { void RemoveWrappedSurface(IDirectDrawSurface7* surface); - d3d9::IDirect3DDevice9* GetD3D9Device(); - - uint32_t GetTotalTextureMemory(); - - d3d9::D3DMULTISAMPLE_TYPE GetMultiSampleType(); - - d3d9::D3DPRESENT_PARAMETERS GetPresentParameters(); - - HRESULT ResetD3D9Swapchain(d3d9::D3DPRESENT_PARAMETERS* params); - - bool IsCurrentRenderTarget(DDrawSurface* surface) const; - - bool IsCurrentRenderTarget(DDraw4Surface* surface) const; - - bool IsCurrentRenderTarget(DDraw7Surface* surface) const; - - bool IsCurrentD3D9RenderTarget(d3d9::IDirect3DSurface9* surface) const; - - bool IsCurrentDepthStencil(DDrawSurface* surface) const; - - bool IsCurrentDepthStencil(DDraw4Surface* surface) const; - - bool IsCurrentDepthStencil(DDraw7Surface* surface) const; - - bool IsCurrentD3D9DepthStencil(d3d9::IDirect3DSurface9* surface) const; - DDrawSurface* GetSurfaceFromTextureHandle(D3DTEXTUREHANDLE handle) const; void SetFlipRTSurfaceAndFlags(IUnknown* surf, DWORD flags) { @@ -165,6 +133,14 @@ namespace dxvk { return m_ps; } + void SetDDrawRenderTarget(DDrawCommonSurface* rt) { + m_rt = rt; + } + + DDrawCommonSurface* GetDDrawRenderTarget() { + return m_rt; + } + void SetCooperativeLevel(HWND hWnd, DWORD dwFlags) { m_hWnd = hWnd; m_cooperativeLevel = dwFlags; @@ -235,36 +211,12 @@ namespace dxvk { return m_origin; } - void SetD3D7Device(D3D7Device* device7) { - m_device7 = device7; - } - - D3D7Device* GetD3D7Device() const { - return m_device7; - } - - void SetD3D6Device(D3D6Device* device6) { - m_device6 = device6; - } - - D3D6Device* GetD3D6Device() const { - return m_device6; + void SetCommonD3DDevice(D3DCommonDevice* commonD3DDevice) { + m_commonD3DDevice = commonD3DDevice; } - void SetD3D5Device(D3D5Device* device5) { - m_device5 = device5; - } - - D3D5Device* GetD3D5Device() const { - return m_device5; - } - - void SetD3D3Device(D3D3Device* device3) { - m_device3 = device3; - } - - D3D3Device* GetD3D3Device() const { - return m_device3; + D3DCommonDevice* GetCommonD3DDevice() const { + return m_commonD3DDevice; } D3DTEXTUREHANDLE GetNextTextureHandle() { @@ -295,6 +247,7 @@ namespace dxvk { DWORD m_cooperativeLevel = 0; DDrawCommonSurface* m_ps = nullptr; + DDrawCommonSurface* m_rt = nullptr; HWND m_hWnd = nullptr; DDrawModeSize m_modeSize = { }; @@ -307,12 +260,6 @@ namespace dxvk { D3D3Interface* m_d3d3Intf = nullptr; - // Track all possible last used D3D devices - D3D7Device* m_device7 = nullptr; - D3D6Device* m_device6 = nullptr; - D3D5Device* m_device5 = nullptr; - D3D3Device* m_device3 = nullptr; - // Track all possible instance versions of the same object DDraw7Interface* m_intf7 = nullptr; DDraw4Interface* m_intf4 = nullptr; @@ -323,6 +270,8 @@ namespace dxvk { // that gets created through a DirectDrawCreate(Ex) call IUnknown* m_origin = nullptr; + D3DCommonDevice* m_commonD3DDevice = nullptr; + std::vector m_surfaces7; std::vector m_surfaces4; std::vector m_surfaces3; diff --git a/src/ddraw/ddraw_common_surface.h b/src/ddraw/ddraw_common_surface.h index b25ae1e7ff2..cb0dfd85724 100644 --- a/src/ddraw/ddraw_common_surface.h +++ b/src/ddraw/ddraw_common_surface.h @@ -45,12 +45,12 @@ namespace dxvk { m_format9 = ConvertFormat(m_desc2.ddpfPixelFormat); // determine and cache various frequently used flag combinations m_isTextureOrCubeMap = IsTexture() || IsCubeMap(); - m_isBackBufferOrFlippable = !IsFrontBuffer() && (IsBackBuffer() || IsFlippableSurface()); - m_isRenderTarget = IsFrontBuffer() || IsBackBuffer() || IsFlippableSurface() || Is3DSurface(); - m_isForwardableSurface = IsFrontBuffer() || IsBackBuffer() || IsFlippableSurface() + m_isBackBufferOrFlippable = !IsFrontBuffer() && (IsBackBuffer() || IsFlippable()); + m_isRenderTarget = IsFrontBuffer() || IsBackBuffer() || IsFlippable() || Is3DSurface(); + m_isForwardableSurface = IsFrontBuffer() || IsBackBuffer() || IsFlippable() || IsDepthStencil() || IsOffScreenPlainSurface(); m_isGuardableSurface = IsPrimarySurface() || IsFrontBuffer() - || IsBackBuffer() || IsFlippableSurface(); + || IsBackBuffer() || IsFlippable(); } const DDSURFACEDESC2* GetDesc2() const { @@ -66,12 +66,12 @@ namespace dxvk { m_isDescSet = true; m_format9 = ConvertFormat(m_desc.ddpfPixelFormat); // determine and cache various frequently used flag combinations - m_isBackBufferOrFlippable = !IsFrontBuffer() && (IsBackBuffer() || IsFlippableSurface()); - m_isRenderTarget = IsFrontBuffer() || IsBackBuffer() || IsFlippableSurface() || Is3DSurface(); - m_isForwardableSurface = IsFrontBuffer() || IsBackBuffer() || IsFlippableSurface() + m_isBackBufferOrFlippable = !IsFrontBuffer() && (IsBackBuffer() || IsFlippable()); + m_isRenderTarget = IsFrontBuffer() || IsBackBuffer() || IsFlippable() || Is3DSurface(); + m_isForwardableSurface = IsFrontBuffer() || IsBackBuffer() || IsFlippable() || IsDepthStencil() || IsOffScreenPlainSurface(); m_isGuardableSurface = IsPrimarySurface() || IsFrontBuffer() - || IsBackBuffer() || IsFlippableSurface(); + || IsBackBuffer() || IsFlippable(); } const DDSURFACEDESC* GetDesc() const { @@ -154,13 +154,15 @@ namespace dxvk { } void SetPalette(DDrawPalette* palette) { - if (palette == nullptr) - m_palette->SetCommonSurface(nullptr); + if (likely(m_palette != palette)) { + if (palette == nullptr) + m_palette->SetCommonSurface(nullptr); - m_palette = palette; + m_palette = palette; - if (m_palette != nullptr) - m_palette->SetCommonSurface(this); + if (m_palette != nullptr) + m_palette->SetCommonSurface(this); + } } DDrawPalette* GetPalette() const { @@ -270,7 +272,7 @@ namespace dxvk { return m_desc2.ddsCaps.dwCaps2 & DDSCAPS2_CUBEMAP; } - bool IsFlippableSurface() const { + bool IsFlippable() const { return m_desc2.ddsCaps.dwCaps & DDSCAPS_FLIP || m_desc.ddsCaps.dwCaps & DDSCAPS_FLIP; } diff --git a/src/ddraw/ddraw_format.h b/src/ddraw/ddraw_format.h index ad5cb268dd7..d865c8320de 100644 --- a/src/ddraw/ddraw_format.h +++ b/src/ddraw/ddraw_format.h @@ -4,6 +4,7 @@ #include #include +#include #include namespace dxvk { @@ -881,8 +882,8 @@ namespace dxvk { float maxRange = cvalue + half; DDCOLORKEY colorKey = { }; - colorKey.dwColorSpaceLowValue = (DWORD)std::max(0.0f, std::floor(minRange - 0.5f)); - colorKey.dwColorSpaceHighValue = (DWORD)std::min(255.0f, std::ceil(maxRange + 0.5f)); + colorKey.dwColorSpaceLowValue = std::floor(std::max(0.0f, floorf(minRange - 0.5))); + colorKey.dwColorSpaceHighValue = std::ceil(std::min(255.0f, floorf(maxRange + 0.5))); return colorKey; } @@ -907,4 +908,4 @@ namespace dxvk { return rgbColorKey; } -} +} \ No newline at end of file diff --git a/src/ddraw/ddraw_gamma.cpp b/src/ddraw/ddraw_gamma.cpp index 92a67f71034..ef3180e8773 100644 --- a/src/ddraw/ddraw_gamma.cpp +++ b/src/ddraw/ddraw_gamma.cpp @@ -1,5 +1,7 @@ #include "ddraw_gamma.h" +#include "d3d_common_device.h" + namespace dxvk { DDrawGammaControl::DDrawGammaControl( @@ -61,12 +63,18 @@ namespace dxvk { if (unlikely(lpRampData == nullptr)) return DDERR_INVALIDPARAMS; - d3d9::IDirect3DDevice9* d3d9Device = m_commonSurf->GetCommonInterface()->GetD3D9Device(); + DDrawCommonInterface* commonIntf = m_commonSurf->GetCommonInterface(); + + D3DCommonDevice* commonDevice = commonIntf->GetCommonD3DDevice(); // For proxied pesentation we need to rely on ddraw to handle gamma - if (likely(d3d9Device != nullptr && !m_commonSurf->GetCommonInterface()->GetOptions()->forceProxiedPresent)) { + if (likely(commonDevice != nullptr && !commonIntf->GetOptions()->forceProxiedPresent)) { Logger::debug("DDrawGammaControl::GetGammaRamp: Getting gamma ramp via D3D9"); + + d3d9::IDirect3DDevice9* d3d9Device = commonDevice->GetD3D9Device(); + d3d9::D3DGAMMARAMP rampData = { }; d3d9Device->GetGammaRamp(0, &rampData); + // Both gamma structs are identical in content/size memcpy(static_cast(lpRampData), static_cast(&rampData), sizeof(DDGAMMARAMP)); } else { @@ -83,11 +91,16 @@ namespace dxvk { if (unlikely(lpRampData == nullptr)) return DDERR_INVALIDPARAMS; - if (likely(!m_commonSurf->GetCommonInterface()->GetOptions()->ignoreGammaRamp)) { - d3d9::IDirect3DDevice9* d3d9Device = m_commonSurf->GetCommonInterface()->GetD3D9Device(); + DDrawCommonInterface* commonIntf = m_commonSurf->GetCommonInterface(); + + if (likely(!commonIntf->GetOptions()->ignoreGammaRamp)) { + D3DCommonDevice* commonDevice = commonIntf->GetCommonD3DDevice(); // For proxied pesentation we need to rely on ddraw to handle gamma - if (likely(d3d9Device != nullptr && !m_commonSurf->GetCommonInterface()->GetOptions()->forceProxiedPresent)) { + if (likely(commonDevice != nullptr && !commonIntf->GetOptions()->forceProxiedPresent)) { Logger::debug("DDrawGammaControl::SetGammaRamp: Setting gamma ramp via D3D9"); + + d3d9::IDirect3DDevice9* d3d9Device = commonDevice->GetD3D9Device(); + d3d9Device->SetGammaRamp(0, D3DSGR_NO_CALIBRATION, reinterpret_cast(lpRampData)); } else { diff --git a/src/ddraw/ddraw_options.h b/src/ddraw/ddraw_options.h index fd39f1372df..c9d7d1628e5 100644 --- a/src/ddraw/ddraw_options.h +++ b/src/ddraw/ddraw_options.h @@ -65,6 +65,9 @@ namespace dxvk { /// Blits back to the proxied flippable surface and presents with DDraw bool forceProxiedPresent; + /// Workaround that uses blits instead of flips for presentation + bool forceBlitOnFlip; + /// Ignore any application set gamma ramp bool ignoreGammaRamp; @@ -105,6 +108,7 @@ namespace dxvk { 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->ignoreGammaRamp = config.getOption ("ddraw.ignoreGammaRamp", false); this->ignoreExclusiveMode = config.getOption ("ddraw.ignoreExclusiveMode", false); this->autoGenMipMaps = config.getOption ("ddraw.autoGenMipMaps", false); diff --git a/src/ddraw/ddraw_util.h b/src/ddraw/ddraw_util.h index a792937c576..d151cd8733a 100644 --- a/src/ddraw/ddraw_util.h +++ b/src/ddraw/ddraw_util.h @@ -4,7 +4,10 @@ #include "ddraw_options.h" #include "ddraw_caps.h" +#include "../util/util_matrix.h" + #include +#include namespace dxvk { @@ -30,6 +33,14 @@ namespace dxvk { || size == sizeof(D3DDEVICEDESC3); } + // The structures used in FindDevice calls are also affected because + // of the above D3DDEVICEDESC jank, which is just lovely... + inline bool IsValidFindDeviceResultSize(DWORD size) { + return size == sizeof(D3DFINDDEVICERESULT) + || size == sizeof(D3DFINDDEVICERESULT2) + || size == sizeof(D3DFINDDEVICERESULT3); + } + inline bool IsVSyncFlipFlag(DWORD flag) { return !(flag & DDFLIP_NOVSYNC) || (flag & DDFLIP_INTERVAL2) || @@ -1636,4 +1647,28 @@ namespace dxvk { || rs == D3DRENDERSTATE_CLIPPLANEENABLE; } + inline Matrix4 MatrixD3DTo4(const D3DMATRIX *m) { + if (m == nullptr) + return nullptr; + + Matrix4 r; + r.data[0] = Vector4(m->_11, m->_12, m->_13, m->_14); + r.data[1] = Vector4(m->_21, m->_22, m->_23, m->_24); + r.data[2] = Vector4(m->_31, m->_32, m->_33, m->_34); + r.data[3] = Vector4(m->_41, m->_42, m->_43, m->_44); + + return r; + } + + inline D3DMATRIX Matrix4ToD3D(const Matrix4 *m) { + D3DMATRIX r; + + r._11 = m->data[0][0]; r._12 = m->data[0][1]; r._13 = m->data[0][2]; r._14 = m->data[0][3]; + r._21 = m->data[1][0]; r._22 = m->data[1][1]; r._23 = m->data[1][2]; r._24 = m->data[1][3]; + r._31 = m->data[2][0]; r._32 = m->data[2][1]; r._33 = m->data[2][2]; r._34 = m->data[2][3]; + r._41 = m->data[3][0]; r._42 = m->data[3][1]; r._43 = m->data[3][2]; r._44 = m->data[3][3]; + + return r; + } + } \ No newline at end of file diff --git a/src/ddraw/meson.build b/src/ddraw/meson.build index 7614d84778a..6ac612602f0 100644 --- a/src/ddraw/meson.build +++ b/src/ddraw/meson.build @@ -17,6 +17,7 @@ ddraw_src = [ 'ddraw4/ddraw4_surface.cpp', 'ddraw7/ddraw7_interface.cpp', 'ddraw7/ddraw7_surface.cpp', + 'd3d_common_device.cpp', 'd3d_common_interface.cpp', 'd3d_common_material.cpp', 'd3d_common_texture.cpp', diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index a31a5595291..f30f5d62669 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -1349,20 +1349,15 @@ namespace dxvk { * artifacting when using a T&L HAL device, * * and broken main menu animations. */ { R"(\\(1|I)nsane\\Game\.exe$)", {{ - { "d3d9.maxFrameRate", "-240" }, + { "d3d9.maxFrameRate", "240" }, { "ddraw.forceSWVP", "True" }, }} }, - /* Arx Fatalis */ - { R"(\\arx\.exe$)", {{ - { "ddraw.emulateFSAA", "True" }, - }} }, /* 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.maxFrameRate", "-60" }, - { "ddraw.emulateFSAA", "True" }, + { "d3d9.maxFrameRate", "60" }, { "ddraw.useD24X8forD32", "True" }, }} }, /* Battle Isle: The Andosia War - Performance * @@ -1370,7 +1365,7 @@ namespace dxvk { * also capped to prevent scroll speed issues */ { R"(\\bitaw\.exe$)", {{ { "d3d9.cachedWriteOnlyBuffers", "True" }, - { "d3d9.maxFrameRate", "-60" }, + { "d3d9.maxFrameRate", "60" }, { "ddraw.backBufferGuard", "Strict" }, }} }, /* Startopia */ @@ -1380,35 +1375,35 @@ namespace dxvk { /* Escape from Monkey Island * * Fixes broken physics, and flip logic */ { R"(\\Monkey4\.exe$)", {{ - { "d3d9.maxFrameRate", "-30" }, + { "d3d9.maxFrameRate", "30" }, { "ddraw.forceSingleBackBuffer", "True" }, }} }, /* Gothic 1 - broken physics and * * flickering on the loading screen */ { R"(\\GOTHIC(Mod)?\.EXE$)", {{ - { "d3d9.maxFrameRate", "-60" }, + { "d3d9.maxFrameRate", "60" }, { "ddraw.forceSingleBackBuffer", "True" }, }} }, /* Gothic 2 / Night of the Raven * * Broken physics and sliding speed, and * * flickering on the loading screen */ { R"(\\Gothic2\.exe)", {{ - { "d3d9.maxFrameRate", "-60" }, + { "d3d9.maxFrameRate", "60" }, { "ddraw.forceSingleBackBuffer", "True" }, }} }, /* Blade of Darkness - broken physics, main * * menu transitions, animations and GUI */ { R"(\\Blade\.exe$)", {{ - { "d3d9.maxFrameRate", "-60" }, + { "d3d9.maxFrameRate", "60" }, { "ddraw.forceSingleBackBuffer", "True" }, }} }, /* Hogs of War - Fixes animation speed */ { R"(\\warhogs_\.exe$)", {{ - { "d3d9.maxFrameRate", "-60" }, + { "d3d9.maxFrameRate", "60" }, }} }, /* Parkan: Iron Strategy - Performance */ { R"(\\iron_3d\.exe$)", {{ - { "d3d9.maxFrameRate", "-60" }, + { "d3d9.maxFrameRate", "60" }, { "d3d9.cachedWriteOnlyBuffers", "True" }, }} }, /* Dungeon Siege */ @@ -1444,13 +1439,12 @@ namespace dxvk { * Crashes without multithreading protection */ { R"(\\SCP - Containment Breach\.exe$)", {{ { "ddraw.forceProxiedPresent", "True" }, - { "ddraw.emulateFSAA", "True" }, { "ddraw.forceMultiThreaded", "True" }, }} }, /* Unreal * * Fixes missing mip map uploads and physics */ { R"(\\Unreal\.exe$)", {{ - { "d3d9.maxFrameRate", "-60" }, + { "d3d9.maxFrameRate", "60" }, { "ddraw.autoGenMipMaps", "True" }, }} }, /* Unreal Tournament * @@ -1461,56 +1455,55 @@ namespace dxvk { /* Rune * * Fixes missing mip map uploads and physics */ { R"(\\Rune\.exe$)", {{ - { "d3d9.maxFrameRate", "-60" }, + { "d3d9.maxFrameRate", "60" }, { "ddraw.autoGenMipMaps", "True" }, }} }, /* Deus Ex * * Fixes missing mip map uploads and physics */ { R"(\\DeusEx\.exe$)", {{ - { "d3d9.maxFrameRate", "-60" }, + { "d3d9.maxFrameRate", "60" }, { "ddraw.autoGenMipMaps", "True" }, }} }, /* Clive Barker's Undying * * Fixes missing mip map uploads, and broken * * cutscene playback at high frame rates */ { R"(\\Undying\.exe$)", {{ - { "d3d9.maxFrameRate", "-60" }, + { "d3d9.maxFrameRate", "60" }, { "ddraw.autoGenMipMaps", "True" }, }} }, /* X-COM: Enforcer * * Fixes missing mip map uploads and physics */ { R"(\\XCom\.exe$)", {{ - { "d3d9.maxFrameRate", "-60" }, + { "d3d9.maxFrameRate", "60" }, { "ddraw.autoGenMipMaps", "True" }, }} }, /* The Wheel of Time * * Fixes missing mip map uploads and physics */ { R"(\\WoT\.exe$)", {{ - { "d3d9.maxFrameRate", "-60" }, + { "d3d9.maxFrameRate", "60" }, { "ddraw.autoGenMipMaps", "True" }, }} }, /* Harry Potter and the Chamber of Secrets * * Fixes missing mip map uploads and physics */ { R"(\\Harry Potter.*\\system\\Game\.exe$)", {{ - { "d3d9.maxFrameRate", "-60" }, + { "d3d9.maxFrameRate", "60" }, { "ddraw.autoGenMipMaps", "True" }, }} }, /* Harry Potter and the Philosopher's Stone * * Fixes missing mip map uploads and physics */ { R"(\\HP\.exe$)", {{ - { "d3d9.maxFrameRate", "-60" }, + { "d3d9.maxFrameRate", "60" }, { "ddraw.autoGenMipMaps", "True" }, }} }, /* Messiah - Fixes missing mip map uploads * * and cutscene playback / physics */ { R"(\\MessiahD3D\.exe$)", {{ - { "d3d9.maxFrameRate", "-60" }, - { "ddraw.emulateFSAA", "True" }, + { "d3d9.maxFrameRate", "60" }, { "ddraw.autoGenMipMaps", "True" }, }} }, /* Might and Magic IX / No One Lives Forever */ { R"(\\lithtech\.exe$)", {{ - { "d3d9.maxFrameRate", "-60" }, + { "d3d9.maxFrameRate", "60" }, }} }, /* 3DMark2000 - Performance */ { R"(\\3DMark2000\.exe$)", {{ @@ -1518,37 +1511,35 @@ namespace dxvk { }} }, /* Carmageddon TDR 2000 - Main menu speed */ { R"(\\TDR2000\.exe$)", {{ - { "d3d9.maxFrameRate", "-120" }, + { "d3d9.maxFrameRate", "120" }, }} }, /* Disciples II - Excessive map scroll speed */ { R"(\\Discipl2\.exe$)", {{ - { "d3d9.maxFrameRate", "-60" }, + { "d3d9.maxFrameRate", "60" }, }} }, /* Hitman: Codename 47 - Broken physics and * * loading screens / menu transitions */ { R"(\\hitman\.exe$)", {{ - { "d3d9.maxFrameRate", "-60" }, - { "ddraw.emulateFSAA", "True" }, + { "d3d9.maxFrameRate", "60" }, { "ddraw.forceSingleBackBuffer", "True" }, }} }, /* Screamer 4x4 - Broken menu animation speed */ { R"(\\Screamer4x4_d3d\.exe$)", {{ - { "d3d9.maxFrameRate", "-60" }, + { "d3d9.maxFrameRate", "60" }, }} }, /* (The) Summoner - Accelerated game speed * * and fix for nonsensical viewport values */ { R"(\\Sum\.exe$)", {{ - { "d3d9.maxFrameRate", "-60" }, - { "ddraw.emulateFSAA", "True" }, + { "d3d9.maxFrameRate", "60" }, }} }, /* Wizardry 8 - Fixes broken input handling */ { R"(\\Wiz8\.exe$)", {{ - { "d3d9.maxFrameRate", "-60" }, + { "d3d9.maxFrameRate", "60" }, }} }, /* Giants: Citizen Kabuto * * Broken input handling at high framerates */ { R"(\\Giants\.exe$)", {{ - { "d3d9.maxFrameRate", "-60" }, + { "d3d9.maxFrameRate", "60" }, }} }, /* The Mystery of the Druids */ { R"(\\edd\.exe$)", {{ @@ -1556,7 +1547,7 @@ namespace dxvk { }} }, /* Silent Hunter II - Broken input handling */ { R"(\\Silent Hunter.*\\(Sim|Shell(1)?)\.exe$)", {{ - { "d3d9.maxFrameRate", "-60" }, + { "d3d9.maxFrameRate", "60" }, }} }, /* Enemy Engaged: Comanche vs Hokum */ { R"(\\cohokum\.exe$)", {{ @@ -1587,7 +1578,6 @@ namespace dxvk { }} }, /* Sacred - Fixes transition artifacting */ { R"(\\Sacred\.exe$)", {{ - { "ddraw.emulateFSAA", "True" }, { "ddraw.forceSingleBackBuffer", "True" }, }} }, /* StarLancer */ @@ -1616,7 +1606,6 @@ namespace dxvk { }} }, /* Hard Truck 2: King of the Road */ { R"(\\king\.exe$)", {{ - { "ddraw.colorKeyCompatibility", "True" }, { "ddraw.forceProxiedPresent", "True" }, }} }, /* Anno 1503 */ @@ -1659,7 +1648,7 @@ namespace dxvk { * DISCARD handling fixes missing geometry. */ { R"(\\Radeon'sArk1.3\.exe$)", {{ { "d3d9.customVendorId", "1002" }, - { "d3d9.maxFrameRate", "-500" }, + { "d3d9.maxFrameRate", "500" }, { "ddraw.forceLegacyDiscard", "True" }, }} }, /* Tribes 2 - fixes rendering and performance */ @@ -1674,7 +1663,6 @@ namespace dxvk { /* Will Rock * * Fixes missing save game screenshots */ { R"(\\WillRock\.exe$)", {{ - { "ddraw.emulateFSAA", "True" }, { "ddraw.backBufferWriteBack", "True" }, }} }, @@ -1690,7 +1678,7 @@ namespace dxvk { * enabled, because that causes depth stencil * * locks for each dynamic light source */ { R"(\\Drakan\.exe$)", {{ - { "d3d9.maxFrameRate", "-60" }, + { "d3d9.maxFrameRate", "60" }, { "ddraw.backBufferWriteBack", "True" }, { "ddraw.uploadDepthStencils", "False" }, }} }, @@ -1746,7 +1734,7 @@ namespace dxvk { }} }, /* Star Wars: Rogue Squadron 3D */ { R"(\\Rogue Squadron\.exe$)", {{ - { "d3d9.maxFrameRate", "-60" }, + { "d3d9.maxFrameRate", "60" }, }} }, /* Blood II: The Chosen */ { R"(\\Blood.*\\Client\.exe$)", {{ @@ -1769,17 +1757,12 @@ namespace dxvk { { "ddraw.forceMultiThreaded", "True" }, { "ddraw.forceProxiedPresent", "True" }, }} }, - /* Expendable */ - { R"(\\Expendable\\go_start\.exe$)", {{ - { "ddraw.emulateFSAA", "True" }, - }} }, /* F/A-18E Super Hornet */ { R"(\\F18\.exe$)", {{ { "ddraw.forceProxiedPresent", "True" }, }} }, /* Total Annihilation: Kingdoms */ { R"(\\KINGDOMS\.icd$)", {{ - { "ddraw.emulateFSAA", "True" }, { "ddraw.forcePOW2Textures", "True" }, }} }, /* Star Wars Episode I: Racer */ @@ -1795,14 +1778,6 @@ namespace dxvk { { R"(\\Revenant\.exe$)", {{ { "ddraw.forceProxiedPresent", "True" }, }} }, - /* Re-Volt */ - { R"(\\revolt\.exe$)", {{ - { "ddraw.emulateFSAA", "True" }, - }} }, - /* Sea Dogs */ - { R"(\\Sea Dogs\\ENGINE\.exe$)", {{ - { "ddraw.emulateFSAA", "True" }, - }} }, /* Empire of the Ants */ { R"(\\Empire of the Ants\\Game\.exe$)", {{ { "ddraw.forceProxiedPresent", "True" }, @@ -1826,7 +1801,6 @@ namespace dxvk { * Fixes unit and building transparency */ { R"(\\MFatigue\.exe$)", {{ { "ddraw.forceProxiedPresent", "True" }, - { "ddraw.colorKeyCompatibility", "True" }, }} }, /* Simon The Sorcerer 3D * * Fixes Z-fighting artifacts with D16 */ @@ -1841,7 +1815,6 @@ namespace dxvk { /* DethKarz - fixes crash post intro playback */ { R"(\\Dethkarz\.exe$)", {{ { "ddraw.mask8BitModes", "True" }, - { "ddraw.colorKeyCompatibility", "True" }, }} }, /* Tomb Raider Chronicles */ { R"(\\PCTomb5\.exe$)", {{ @@ -1862,7 +1835,7 @@ namespace dxvk { }} }, /* N.I.C.E 2 - Fixes main menu flickering */ { R"(\\n2_(std|arc)\.exe$)", {{ - { "d3d9.maxFrameRate", "-60" }, + { "d3d9.maxFrameRate", "60" }, { "ddraw.forceSingleBackBuffer", "True" }, }} }, /* Twisted Metal 2 */ @@ -1878,17 +1851,13 @@ namespace dxvk { * Fixes presentation and physics, which is * * tied to framerate in various situations */ { R"(\\NC(_V12)?\.exe$)", {{ - { "d3d9.maxFrameRate", "-30" }, + { "d3d9.maxFrameRate", "30" }, { "ddraw.ignoreExclusiveMode", "True" }, }} }, /* Deathtrap Dungeon * * Accelerated menu animations above 30 FPS */ { R"(\\DD_CD\.exe$)", {{ - { "d3d9.maxFrameRate", "-30" }, - }} }, - /* FIFA '99 */ - { R"(\\fifa99\.exe$)", {{ - { "ddraw.emulateFSAA", "True" }, + { "d3d9.maxFrameRate", "30" }, }} }, /* The Longest Journey */ { R"(\\The Longest Journey\\game\.exe$)", {{ @@ -1902,7 +1871,6 @@ namespace dxvk { * Fixes broken color key transparency */ { R"(\\RainbowSix\.exe$)", {{ { "ddraw.forceProxiedPresent", "True" }, - { "ddraw.colorKeyCompatibility", "True" }, }} }, /* Incoming - fixes load screen flickering */ { R"(\\incoming\.exe$)", {{ @@ -1926,6 +1894,13 @@ namespace dxvk { { R"(\\rr_dx5\.exe$)", {{ { "ddraw.forceProxiedPresent", "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" }, + }} }, /**********************************************/ /* D3D3 GAMES */ @@ -1951,6 +1926,10 @@ namespace dxvk { { R"(\\MONSTER\.EXE$)", {{ { "ddraw.forceProxiedPresent", "True" }, }} }, + /* Forsaken */ + { R"(\\ForsakenHW\.exe$)", {{ + { "ddraw.forceProxiedPresent", "True" }, + }} }, }};