From b9b4a2aafd6b0b826da67dd9862517f8ccb44455 Mon Sep 17 00:00:00 2001 From: narzoul Date: Sun, 12 Jun 2016 14:21:09 +0200 Subject: [PATCH] Fixed incorrect z-buffer bit depths reported in D3DDEVICEDESC Legacy DirectDraw interfaces specify the z-buffer format as a single bit depth number instead of as a DDPIXELFORMAT struct. DirectDraw seems to convert a legacy z-buffer bit depth of N into a DDPIXELFORMAT with dwFlags = DDPF_ZBUFFER, dwZBufferBitDepth = N and dwZBitMask set to 1s in the lowest N bits. Some drivers (so far noticed with AMD only) report the list of supported z-buffer bit depths incorrectly, resulting in a game potentially selecting a bit depth that can't actually be created via the legacy interfaces. For example, the driver may report 16 and 32 bits as supported whereas all 32 bit z-buffer pixel formats use only 24 bits for z-buffer (with the remaining bits unused or used as stencil buffer). Meanwhile the same driver doesn't report 24 bits as supported when it's actually supported. This fix overrides the set of supported z-buffer bit depths in D3DDEVICEDESC structs for HAL devices to align with the actually supported pixel formats. Fixes a startup issue in Rainbow Six mentioned in issue #2. --- DDrawCompat/CompatDepthBuffer.cpp | 124 ++++++++++++++++++++++++ DDrawCompat/CompatDepthBuffer.h | 11 +++ DDrawCompat/CompatDirect3d.cpp | 62 +++++++++++- DDrawCompat/CompatDirect3dDevice.cpp | 38 +++++++- DDrawCompat/DDrawCompat.vcxproj | 3 + DDrawCompat/DDrawCompat.vcxproj.filters | 9 ++ DDrawCompat/Direct3dTypes.h | 64 ++++++++++++ 7 files changed, 309 insertions(+), 2 deletions(-) create mode 100644 DDrawCompat/CompatDepthBuffer.cpp create mode 100644 DDrawCompat/CompatDepthBuffer.h create mode 100644 DDrawCompat/Direct3dTypes.h diff --git a/DDrawCompat/CompatDepthBuffer.cpp b/DDrawCompat/CompatDepthBuffer.cpp new file mode 100644 index 0000000..a008cad --- /dev/null +++ b/DDrawCompat/CompatDepthBuffer.cpp @@ -0,0 +1,124 @@ +#include +#include + +#include "CompatDepthBuffer.h" + +namespace +{ + std::string bitDepthsToString(DWORD bitDepths) + { + std::string result; + if (bitDepths & DDBD_8) { result += ", 8"; } + if (bitDepths & DDBD_16) { result += ", 16"; } + if (bitDepths & DDBD_24) { result += ", 24"; } + if (bitDepths & DDBD_32) { result += ", 32"; } + result = '"' + result.substr(2) + '"'; + return result; + } + + HRESULT CALLBACK enumZBufferFormatsCallback(LPDDPIXELFORMAT lpDDPixFmt, LPVOID lpContext) + { + Compat::LogEnter("CompatDepthBuffer::enumZBufferFormatsCallback", lpDDPixFmt, lpContext); + if (DDPF_ZBUFFER == lpDDPixFmt->dwFlags && + (static_cast(1) << lpDDPixFmt->dwZBufferBitDepth) - 1 == lpDDPixFmt->dwZBitMask) + { + DWORD& supportedBitDepths = *reinterpret_cast(lpContext); + switch (lpDDPixFmt->dwZBufferBitDepth) + { + case 8: supportedBitDepths |= DDBD_8; break; + case 16: supportedBitDepths |= DDBD_16; break; + case 24: supportedBitDepths |= DDBD_24; break; + case 32: supportedBitDepths |= DDBD_32; break; + } + } + Compat::LogLeave("CompatDepthBuffer::enumZBufferFormatsCallback", + lpDDPixFmt, lpContext) << D3DENUMRET_OK; + return D3DENUMRET_OK; + } + + GUID getDeviceGuid(const D3DDEVICEDESC& /*desc*/) + { + return IID_IDirect3DHALDevice; + } + + GUID getDeviceGuid(const D3DDEVICEDESC7& desc) + { + return desc.deviceGUID; + } + + template + DWORD getSupportedZBufferBitDepths(CompatPtr d3d, const GUID& deviceGuid) + { + DWORD supportedBitDepths = 0; + if (d3d) + { + d3d->EnumZBufferFormats(d3d, deviceGuid, &enumZBufferFormatsCallback, &supportedBitDepths); + } + return supportedBitDepths; + } + + bool isHardwareZBufferSupported(const D3DDEVICEDESC& desc) + { + return (desc.dwFlags & D3DDD_DEVICEZBUFFERBITDEPTH) && 0 != desc.dwDeviceZBufferBitDepth; + } + + bool isHardwareZBufferSupported(const D3DDEVICEDESC7& desc) + { + return 0 != desc.dwDeviceZBufferBitDepth && + (IID_IDirect3DHALDevice == desc.deviceGUID || IID_IDirect3DTnLHalDevice == desc.deviceGUID); + } + + void logSupportedZBufferBitDepthsChanged( + CompatPtr dd, const GUID& d3dGuid, DWORD oldBitDepths, DWORD newBitDepths) + { + struct DeviceId + { + GUID directDrawGuid; + GUID direct3dGuid; + + bool operator==(const DeviceId& rhs) const + { + return directDrawGuid == rhs.directDrawGuid && direct3dGuid == rhs.direct3dGuid; + } + }; + + DDDEVICEIDENTIFIER2 deviceIdentifier = {}; + dd->GetDeviceIdentifier(dd, &deviceIdentifier, 0); + const DeviceId deviceId = { deviceIdentifier.guidDeviceIdentifier, d3dGuid }; + + static std::vector loggedDevices; + if (loggedDevices.end() != std::find(loggedDevices.begin(), loggedDevices.end(), deviceId)) + { + return; + } + loggedDevices.push_back(deviceId); + + Compat::Log() << "Incorrect z-buffer bit depth capabilities detected for \"" << + deviceIdentifier.szDescription << "\" / " << + (IID_IDirect3DTnLHalDevice == d3dGuid ? "Direct3D T&L HAL" : "Direct3D HAL") << ','; + Compat::Log() << " changed supported z-buffer bit depths from " << + bitDepthsToString(oldBitDepths) << " to " << bitDepthsToString(newBitDepths); + } +} + +namespace CompatDepthBuffer +{ + template + void fixSupportedZBufferBitDepths( + CompatPtr d3d, TD3dDeviceDesc& desc) + { + if (isHardwareZBufferSupported(desc)) + { + const DWORD supportedBitDepths = getSupportedZBufferBitDepths(d3d, getDeviceGuid(desc)); + if (0 != supportedBitDepths && supportedBitDepths != desc.dwDeviceZBufferBitDepth) + { + logSupportedZBufferBitDepthsChanged( + d3d, getDeviceGuid(desc), desc.dwDeviceZBufferBitDepth, supportedBitDepths); + desc.dwDeviceZBufferBitDepth = supportedBitDepths; + } + } + } + + template void fixSupportedZBufferBitDepths(CompatPtr, D3DDEVICEDESC&); + template void fixSupportedZBufferBitDepths(CompatPtr, D3DDEVICEDESC7&); +} diff --git a/DDrawCompat/CompatDepthBuffer.h b/DDrawCompat/CompatDepthBuffer.h new file mode 100644 index 0000000..bc596f1 --- /dev/null +++ b/DDrawCompat/CompatDepthBuffer.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +#include "CompatPtr.h" + +namespace CompatDepthBuffer +{ + template + void fixSupportedZBufferBitDepths(CompatPtr d3d, TD3dDeviceDesc& desc); +} diff --git a/DDrawCompat/CompatDirect3d.cpp b/DDrawCompat/CompatDirect3d.cpp index c15695f..d728c54 100644 --- a/DDrawCompat/CompatDirect3d.cpp +++ b/DDrawCompat/CompatDirect3d.cpp @@ -1,8 +1,68 @@ +#include "CompatDepthBuffer.h" #include "CompatDirect3d.h" +#include "CompatPtr.h" +#include "Direct3dTypes.h" + +namespace +{ + template + struct EnumDevicesParams + { + CompatPtr d3d; + typename Types::TD3dEnumDevicesCallback enumDevicesCallback; + void* userArg; + }; + + HRESULT CALLBACK d3dEnumDevicesCallback( + GUID* lpGuid, + LPSTR lpDeviceDescription, + LPSTR lpDeviceName, + LPD3DDEVICEDESC lpD3DHWDeviceDesc, + LPD3DDEVICEDESC lpD3DHELDeviceDesc, + LPVOID lpContext) + { + auto& params = *reinterpret_cast*>(lpContext); + CompatDepthBuffer::fixSupportedZBufferBitDepths(params.d3d, *lpD3DHWDeviceDesc); + return params.enumDevicesCallback(lpGuid, lpDeviceDescription, lpDeviceName, + lpD3DHWDeviceDesc, lpD3DHELDeviceDesc, params.userArg); + } + + HRESULT CALLBACK d3dEnumDevicesCallback( + LPSTR lpDeviceDescription, + LPSTR lpDeviceName, + LPD3DDEVICEDESC7 lpD3DDeviceDesc, + LPVOID lpContext) + { + auto& params = *reinterpret_cast*>(lpContext); + CompatDepthBuffer::fixSupportedZBufferBitDepths(params.d3d, *lpD3DDeviceDesc); + return params.enumDevicesCallback(lpDeviceDescription, lpDeviceName, + lpD3DDeviceDesc, params.userArg); + } + + template + HRESULT STDMETHODCALLTYPE enumDevices( + TDirect3d* This, TD3dEnumDevicesCallback lpEnumDevicesCallback, LPVOID lpUserArg) + { + if (!lpEnumDevicesCallback) + { + return CompatVtableBase::s_origVtable.EnumDevices( + This, lpEnumDevicesCallback, lpUserArg); + } + + typedef typename Types::TDirect3dHighest TDirect3dHighest; + CompatPtr d3d(Compat::queryInterface(This)); + + EnumDevicesParams params = { d3d, lpEnumDevicesCallback, lpUserArg }; + return CompatVtableBase::s_origVtable.EnumDevices( + This, &d3dEnumDevicesCallback, ¶ms); + } +} template -void CompatDirect3d::setCompatVtable(Vtable& /*vtable*/) +void CompatDirect3d::setCompatVtable(Vtable& vtable) { + vtable.EnumDevices = &enumDevices; + // No need to fix FindDevice since it uses EnumDevices } template CompatDirect3d; diff --git a/DDrawCompat/CompatDirect3dDevice.cpp b/DDrawCompat/CompatDirect3dDevice.cpp index 38299b4..bc39d24 100644 --- a/DDrawCompat/CompatDirect3dDevice.cpp +++ b/DDrawCompat/CompatDirect3dDevice.cpp @@ -1,8 +1,44 @@ +#include "CompatDepthBuffer.h" #include "CompatDirect3dDevice.h" +#include "CompatPtr.h" +#include "CompatRef.h" +#include "Direct3dTypes.h" + +namespace +{ + template + void fixSupportedZBufferBitDepths(CompatRef d3dDevice, TD3dDeviceDesc& desc) + { + typedef typename Types::TDirect3d TDirect3d; + CompatPtr d3d; + if (SUCCEEDED(CompatVtableBase::s_origVtable.GetDirect3D( + &d3dDevice, &d3d.getRef()))) + { + typedef typename Types::TDirect3dHighest TDirect3dHighest; + CompatDepthBuffer::fixSupportedZBufferBitDepths(d3d, desc); + } + } + + template + HRESULT STDMETHODCALLTYPE getCaps( + TDirect3dDevice* This, + TD3dDeviceDesc* lpD3DHWDevDesc, + Params... params) + { + HRESULT result = CompatVtableBase::s_origVtable.GetCaps( + This, lpD3DHWDevDesc, params...); + if (SUCCEEDED(result)) + { + fixSupportedZBufferBitDepths(*This, *lpD3DHWDevDesc); + } + return result; + } +} template -void CompatDirect3dDevice::setCompatVtable(Vtable& /*vtable*/) +void CompatDirect3dDevice::setCompatVtable(Vtable& vtable) { + vtable.GetCaps = &getCaps; } template CompatDirect3dDevice; diff --git a/DDrawCompat/DDrawCompat.vcxproj b/DDrawCompat/DDrawCompat.vcxproj index a64e8fe..3a4e8d9 100644 --- a/DDrawCompat/DDrawCompat.vcxproj +++ b/DDrawCompat/DDrawCompat.vcxproj @@ -145,6 +145,7 @@ + @@ -179,6 +180,7 @@ + @@ -192,6 +194,7 @@ + diff --git a/DDrawCompat/DDrawCompat.vcxproj.filters b/DDrawCompat/DDrawCompat.vcxproj.filters index a272343..37a3805 100644 --- a/DDrawCompat/DDrawCompat.vcxproj.filters +++ b/DDrawCompat/DDrawCompat.vcxproj.filters @@ -150,6 +150,12 @@ Header Files + + Header Files + + + Header Files + @@ -248,6 +254,9 @@ Source Files + + Source Files + diff --git a/DDrawCompat/Direct3dTypes.h b/DDrawCompat/Direct3dTypes.h new file mode 100644 index 0000000..a9be937 --- /dev/null +++ b/DDrawCompat/Direct3dTypes.h @@ -0,0 +1,64 @@ +#pragma once + +#define CINTERFACE + +#include + +struct Direct3dTypes +{ + typedef IDirect3D TDirect3d; + typedef IDirect3D3 TDirect3dHighest; + typedef IDirect3DDevice TDirect3dDevice; + typedef D3DDEVICEDESC TD3dDeviceDesc; + typedef LPD3DENUMDEVICESCALLBACK TD3dEnumDevicesCallback; +}; + +struct Direct3dTypes2 +{ + typedef IDirect3D2 TDirect3d; + typedef IDirect3D3 TDirect3dHighest; + typedef IDirect3DDevice2 TDirect3dDevice; + typedef D3DDEVICEDESC TD3dDeviceDesc; + typedef LPD3DENUMDEVICESCALLBACK TD3dEnumDevicesCallback; +}; + +struct Direct3dTypes3 +{ + typedef IDirect3D3 TDirect3d; + typedef IDirect3D3 TDirect3dHighest; + typedef IDirect3DDevice3 TDirect3dDevice; + typedef D3DDEVICEDESC TD3dDeviceDesc; + typedef LPD3DENUMDEVICESCALLBACK TD3dEnumDevicesCallback; +}; + +struct Direct3dTypes7 +{ + typedef IDirect3D7 TDirect3d; + typedef IDirect3D7 TDirect3dHighest; + typedef IDirect3DDevice7 TDirect3dDevice; + typedef D3DDEVICEDESC7 TD3dDeviceDesc; + typedef LPD3DENUMDEVICESCALLBACK7 TD3dEnumDevicesCallback; +}; + +template +struct Types; + +#define D3D_CONCAT(x, y, ...) x##y + +#define D3D_TYPES(Interface, ...) \ + template <> \ + struct Types : D3D_CONCAT(Direct3dTypes, __VA_ARGS__) \ + {} + +D3D_TYPES(IDirect3D); +D3D_TYPES(IDirect3D, 2); +D3D_TYPES(IDirect3D, 3); +D3D_TYPES(IDirect3D, 7); + +D3D_TYPES(IDirect3DDevice); +D3D_TYPES(IDirect3DDevice, 2); +D3D_TYPES(IDirect3DDevice, 3); +D3D_TYPES(IDirect3DDevice, 7); + +#undef D3D_TYPES +#undef D3D_CONCAT