diff --git a/dxvk.conf b/dxvk.conf index a3ddf3ea6..54f2ac0ab 100644 --- a/dxvk.conf +++ b/dxvk.conf @@ -464,6 +464,7 @@ # capabilities that the applicatation queries. # # Supported values: +# - 0: Fixed-function only # - 1: Shader Model 1 # - 2: Shader Model 2 # - 3: Shader Model 3 diff --git a/src/d3d8/d3d8_d3d9_util.h b/src/d3d8/d3d8_d3d9_util.h index 5be366ee9..69aeb9c07 100644 --- a/src/d3d8/d3d8_d3d9_util.h +++ b/src/d3d8/d3d8_d3d9_util.h @@ -19,7 +19,10 @@ namespace dxvk { // Max supported shader model is PS 1.4 and VS 1.1 pCaps8->VertexShaderVersion = D3DVS_VERSION(1, 1); - pCaps8->PixelShaderVersion = D3DPS_VERSION(1, 4); + // Late fixed-function capable hardware will advertise VS 1.1 + // support, but will not advertise any support for PS + if (likely(caps9.PixelShaderVersion != D3DPS_VERSION(0, 0))) + pCaps8->PixelShaderVersion = D3DPS_VERSION(1, 4); // Remove D3D9-specific caps: diff --git a/src/d3d8/d3d8_device.cpp b/src/d3d8/d3d8_device.cpp index b6128008b..0a5ab0f0d 100644 --- a/src/d3d8/d3d8_device.cpp +++ b/src/d3d8/d3d8_device.cpp @@ -58,6 +58,12 @@ namespace dxvk { if (m_d3d8Options.batching) m_batcher = new D3D8Batcher(this, GetD3D9()); + + d3d9::D3DCAPS9 caps9; + HRESULT res = GetD3D9()->GetDeviceCaps(&caps9); + + if (unlikely(SUCCEEDED(res) && caps9.PixelShaderVersion == D3DPS_VERSION(0, 0))) + m_isFixedFunctionOnly = true; } D3D8Device::~D3D8Device() { @@ -1784,8 +1790,8 @@ namespace dxvk { // Validate VS version for non-FF shaders if (pFunction != nullptr) { - uint32_t majorVersion = (pFunction[0] >> 8) & 0xff; - uint32_t minorVersion = pFunction[0] & 0xff; + const uint32_t majorVersion = (pFunction[0] >> 8) & 0xff; + const uint32_t minorVersion = pFunction[0] & 0xff; if (unlikely(majorVersion != 1 || minorVersion > 1)) { Logger::err(str::format("D3D8Device::CreateVertexShader: Unsupported VS version ", majorVersion, ".", minorVersion)); @@ -2023,10 +2029,10 @@ namespace dxvk { if (unlikely(pFunction == nullptr || pHandle == nullptr)) return D3DERR_INVALIDCALL; - uint32_t majorVersion = (pFunction[0] >> 8) & 0xff; - uint32_t minorVersion = pFunction[0] & 0xff; + const uint32_t majorVersion = (pFunction[0] >> 8) & 0xff; + const uint32_t minorVersion = pFunction[0] & 0xff; - if (unlikely(majorVersion != 1 || minorVersion > 4)) { + if (unlikely(m_isFixedFunctionOnly || majorVersion != 1 || minorVersion > 4)) { Logger::err(str::format("D3D8Device::CreatePixelShader: Unsupported PS version ", majorVersion, ".", minorVersion)); return D3DERR_INVALIDCALL; } diff --git a/src/d3d8/d3d8_device.h b/src/d3d8/d3d8_device.h index dc83a3eb7..61ce40b8d 100644 --- a/src/d3d8/d3d8_device.h +++ b/src/d3d8/d3d8_device.h @@ -429,6 +429,9 @@ namespace dxvk { // Value of D3DRS_PATCHSEGMENTS float m_patchSegments = 1.0f; + // Controls fixed-function exclusive mode (no PS support) + bool m_isFixedFunctionOnly = false; + D3D8StateBlock* m_recorder = nullptr; DWORD m_recorderToken = 0; DWORD m_token = 0; diff --git a/src/d3d8/d3d8_interface.cpp b/src/d3d8/d3d8_interface.cpp index 502f3cfb8..1fd9933eb 100644 --- a/src/d3d8/d3d8_interface.cpp +++ b/src/d3d8/d3d8_interface.cpp @@ -12,7 +12,7 @@ namespace dxvk { // Get the bridge interface to D3D9. if (FAILED(m_d3d9->QueryInterface(__uuidof(IDxvkD3D8InterfaceBridge), (void**)&m_bridge))) { - throw DxvkError("D3D8Device: ERROR! Failed to get D3D9 Bridge. d3d9.dll might not be DXVK!"); + throw DxvkError("D3D8Interface: ERROR! Failed to get D3D9 Bridge. d3d9.dll might not be DXVK!"); } m_bridge->SetD3D8CompatibilityMode(true); diff --git a/src/d3d8/d3d8_main.cpp b/src/d3d8/d3d8_main.cpp index 54109773a..86ca6118c 100644 --- a/src/d3d8/d3d8_main.cpp +++ b/src/d3d8/d3d8_main.cpp @@ -26,8 +26,8 @@ extern "C" { if (unlikely(pPixelShader == nullptr)) { errorMessage = "D3D8: ValidatePixelShader: Null pPixelShader"; } else { - uint32_t majorVersion = (pPixelShader[0] >> 8) & 0xff; - uint32_t minorVersion = pPixelShader[0] & 0xff; + const uint32_t majorVersion = (pPixelShader[0] >> 8) & 0xff; + const uint32_t minorVersion = pPixelShader[0] & 0xff; if (unlikely(majorVersion != 1 || minorVersion > 4)) { errorMessage = dxvk::str::format("D3D8: ValidatePixelShader: Unsupported PS version ", @@ -69,8 +69,8 @@ extern "C" { if (unlikely(pVertexShader == nullptr)) { errorMessage = "D3D8: ValidateVertexShader: Null pVertexShader"; } else { - uint32_t majorVersion = (pVertexShader[0] >> 8) & 0xff; - uint32_t minorVersion = pVertexShader[0] & 0xff; + const uint32_t majorVersion = (pVertexShader[0] >> 8) & 0xff; + const uint32_t minorVersion = pVertexShader[0] & 0xff; if (unlikely(majorVersion != 1 || minorVersion > 1)) { errorMessage = dxvk::str::format("D3D8: ValidateVertexShader: Unsupported VS version ", diff --git a/src/d3d9/d3d9_adapter.cpp b/src/d3d9/d3d9_adapter.cpp index b40ef2454..e1bc320a4 100644 --- a/src/d3d9/d3d9_adapter.cpp +++ b/src/d3d9/d3d9_adapter.cpp @@ -573,17 +573,22 @@ namespace dxvk { // Max Stream Stride pCaps->MaxStreamStride = 508; // bytes - const uint32_t majorVersion = options.shaderModel; - const uint32_t minorVersion = options.shaderModel != 1 ? 0 : 4; + // Late fixed-function capable cards, such as the GeForce 4 MX series, + // expose support for VS 1.1, while not advertising any PS support + const uint32_t majorVersionVS = options.shaderModel == 0 ? 1 : options.shaderModel; + const uint32_t majorVersionPS = options.shaderModel; + // Max supported SM1 is VS 1.1 and PS 1.4 + const uint32_t minorVersionVS = majorVersionVS != 1 ? 0 : 1; + const uint32_t minorVersionPS = majorVersionPS != 1 ? 0 : 4; // Shader Versions - pCaps->VertexShaderVersion = D3DVS_VERSION(majorVersion, minorVersion); - pCaps->PixelShaderVersion = D3DPS_VERSION(majorVersion, minorVersion); + pCaps->VertexShaderVersion = D3DVS_VERSION(majorVersionVS, minorVersionVS); + pCaps->PixelShaderVersion = D3DPS_VERSION(majorVersionPS, minorVersionPS); // Max Vertex Shader Const pCaps->MaxVertexShaderConst = MaxFloatConstantsVS; // Max PS1 Value - pCaps->PixelShader1xMaxValue = FLT_MAX; + pCaps->PixelShader1xMaxValue = options.shaderModel > 0 ? FLT_MAX : 0.0f; // Dev Caps 2 pCaps->DevCaps2 = D3DDEVCAPS2_STREAMOFFSET /* | D3DDEVCAPS2_DMAPNPATCH */ @@ -646,8 +651,8 @@ namespace dxvk { pCaps->PS20Caps.NumInstructionSlots = options.shaderModel >= 2 ? 512 : 256; pCaps->VertexTextureFilterCaps = 50332416; - pCaps->MaxVShaderInstructionsExecuted = 4294967295; - pCaps->MaxPShaderInstructionsExecuted = 4294967295; + pCaps->MaxVShaderInstructionsExecuted = options.shaderModel >= 2 ? 4294967295 : 0; + pCaps->MaxPShaderInstructionsExecuted = options.shaderModel >= 2 ? 4294967295 : 0; pCaps->MaxVertexShader30InstructionSlots = options.shaderModel == 3 ? 32768 : 0; pCaps->MaxPixelShader30InstructionSlots = options.shaderModel == 3 ? 32768 : 0; diff --git a/src/d3d9/d3d9_device.cpp b/src/d3d9/d3d9_device.cpp index 502625556..415afebf4 100644 --- a/src/d3d9/d3d9_device.cpp +++ b/src/d3d9/d3d9_device.cpp @@ -3314,6 +3314,19 @@ namespace dxvk { if (unlikely(ppShader == nullptr)) return D3DERR_INVALIDCALL; + const uint32_t majorVersion = (pFunction[0] >> 8) & 0xff; + const uint32_t minorVersion = pFunction[0] & 0xff; + + // Late fixed-function capable hardware exposed support for VS 1.1 + const uint32_t shaderModelVS = m_d3d9Options.shaderModel == 0 ? 1 : m_d3d9Options.shaderModel; + + if (unlikely(majorVersion > shaderModelVS + || (majorVersion == 1 && minorVersion > 1) + || (majorVersion > 1 && minorVersion != 0))) { + Logger::err(str::format("D3D9DeviceEx::CreateVertexShader: Unsupported VS version ", majorVersion, ".", minorVersion)); + return D3DERR_INVALIDCALL; + } + DxsoModuleInfo moduleInfo; moduleInfo.options = m_dxsoOptions; @@ -3678,6 +3691,16 @@ namespace dxvk { if (unlikely(ppShader == nullptr)) return D3DERR_INVALIDCALL; + const uint32_t majorVersion = (pFunction[0] >> 8) & 0xff; + const uint32_t minorVersion = pFunction[0] & 0xff; + + if (unlikely(majorVersion > m_d3d9Options.shaderModel + || (majorVersion == 1 && minorVersion > 4) + || (majorVersion > 1 && minorVersion != 0))) { + Logger::err(str::format("D3D9DeviceEx::CreatePixelShader: Unsupported PS version ", majorVersion, ".", minorVersion)); + return D3DERR_INVALIDCALL; + } + DxsoModuleInfo moduleInfo; moduleInfo.options = m_dxsoOptions; diff --git a/src/d3d9/d3d9_interface.cpp b/src/d3d9/d3d9_interface.cpp index 4ed2dfdf2..f0c0921c4 100644 --- a/src/d3d9/d3d9_interface.cpp +++ b/src/d3d9/d3d9_interface.cpp @@ -67,6 +67,9 @@ namespace dxvk { SetProcessDPIAware(); } #endif + + if (unlikely(m_d3d9Options.shaderModel == 0)) + Logger::warn("D3D9InterfaceEx: WARNING! Fixed-function exclusive mode is enabled."); } diff --git a/src/d3d9/d3d9_options.h b/src/d3d9/d3d9_options.h index 4d733880f..8fee48361 100644 --- a/src/d3d9/d3d9_options.h +++ b/src/d3d9/d3d9_options.h @@ -34,7 +34,7 @@ namespace dxvk { int32_t maxFrameRate; /// Set the max shader model the device can support in the caps. - int32_t shaderModel; + uint32_t shaderModel; /// Whether or not to set the process as DPI aware in Windows when the API interface is created. bool dpiAware; diff --git a/src/d3d9/d3d9_shader.cpp b/src/d3d9/d3d9_shader.cpp index b17ab47c0..6ab5df3d7 100644 --- a/src/d3d9/d3d9_shader.cpp +++ b/src/d3d9/d3d9_shader.cpp @@ -98,9 +98,6 @@ namespace dxvk { DxsoModule module(reader); - if (module.info().majorVersion() > pDxbcModuleInfo->options.shaderModel) - throw DxvkError("GetShaderModule: Out of range of supported shader model"); - if (module.info().shaderStage() != ShaderStage) throw DxvkError("GetShaderModule: Bytecode does not match shader stage"); diff --git a/src/dxso/dxso_options.cpp b/src/dxso/dxso_options.cpp index 30ea3b331..28400af72 100644 --- a/src/dxso/dxso_options.cpp +++ b/src/dxso/dxso_options.cpp @@ -20,8 +20,6 @@ namespace dxvk { strictPow = options.strictPow; d3d9FloatEmulation = options.d3d9FloatEmulation; - shaderModel = options.shaderModel; - invariantPosition = options.invariantPosition; forceSamplerTypeSpecConstants = options.forceSamplerTypeSpecConstants; diff --git a/src/dxso/dxso_options.h b/src/dxso/dxso_options.h index 3145e2c61..132b99ac3 100644 --- a/src/dxso/dxso_options.h +++ b/src/dxso/dxso_options.h @@ -25,9 +25,6 @@ namespace dxvk { /// Whether or not we should care about pow(0, 0) = 1 bool strictPow; - /// Max version of shader to support - uint32_t shaderModel; - /// Work around a NV driver quirk /// Fixes flickering/z-fighting in some games. bool invariantPosition;