From c760409fbbb167dcf75b76ac4d6e274f6ac5359c Mon Sep 17 00:00:00 2001 From: pythonlover02 Date: Fri, 10 Oct 2025 00:26:12 -0300 Subject: [PATCH 1/5] [d3d9,dxso] Fix push constant validation errors --- src/d3d9/d3d9_fixed_function.cpp | 48 ++++++++++++++++---------------- src/dxso/dxso_compiler.cpp | 2 +- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/d3d9/d3d9_fixed_function.cpp b/src/d3d9/d3d9_fixed_function.cpp index c12507e4b3c..fd9216e6a1b 100644 --- a/src/d3d9/d3d9_fixed_function.cpp +++ b/src/d3d9/d3d9_fixed_function.cpp @@ -117,7 +117,7 @@ namespace dxvk { for (uint32_t i = 0; i < fogCaseLabels.size(); i++) { spvModule.opLabel(fogCaseLabels[i].labelId); - + fogVariables[i].labelId = fogCaseLabels[i].labelId; fogVariables[i].varId = [&] { auto mode = D3DFOGMODE(fogCaseLabels[i].literal); @@ -158,7 +158,7 @@ namespace dxvk { } } }(); - + spvModule.opBranch(applyFogFactor); } @@ -223,7 +223,7 @@ namespace dxvk { uint32_t rsBlock = spvModule.newVar( spvModule.defPointerType(rsStruct, spv::StorageClassPushConstant), spv::StorageClassPushConstant); - + spvModule.setDebugName (rsBlock, "render_state"); spvModule.setDebugName (rsStruct, "render_state_t"); @@ -431,7 +431,7 @@ namespace dxvk { NormalMatrix, InverseViewMatrix, ProjMatrix, - + Texcoord0, Texcoord1, Texcoord2, @@ -722,7 +722,7 @@ namespace dxvk { info.inputMask = m_inputMask; info.outputMask = m_outputMask; info.pushConstOffset = m_pushConstOffset; - info.pushConstSize = m_pushConstOffset; + info.pushConstSize = m_pushConstSize; return new DxvkShader(info, m_module.compile()); } @@ -894,7 +894,7 @@ namespace dxvk { normal = m_module.opNormalize(m_vec3Type, normal); normal = m_module.opSelect(m_vec3Type, isZeroNormal3, m_module.constvec3f32(0.0f, 0.0f, 0.0f), normal); } - + gl_Position = emitVectorTimesMatrix(4, 4, vtx, m_vs.constants.proj); } else { gl_Position = m_module.opFMul(m_vec4Type, gl_Position, m_vs.constants.invExtent); @@ -955,7 +955,7 @@ namespace dxvk { case (DXVK_TSS_TCI_CAMERASPACEREFLECTIONVECTOR >> TCIOffset): { uint32_t vtx3 = m_module.opVectorShuffle(m_vec3Type, vtx, vtx, 3, indices.data()); vtx3 = m_module.opNormalize(m_vec3Type, vtx3); - + uint32_t reflection = m_module.opReflect(m_vec3Type, vtx3, normal); std::array transformIndices; @@ -1146,7 +1146,7 @@ namespace dxvk { uint32_t mat_ambient = PickSource(m_vsKey.Data.Contents.AmbientSource, m_vs.constants.materialAmbient); uint32_t mat_emissive = PickSource(m_vsKey.Data.Contents.EmissiveSource, m_vs.constants.materialEmissive); uint32_t mat_specular = PickSource(m_vsKey.Data.Contents.SpecularSource, m_vs.constants.materialSpecular); - + std::array alphaSwizzle = {0, 1, 2, 7}; uint32_t finalColor0 = m_module.opFFma(m_vec4Type, mat_ambient, m_vs.constants.globalAmbient, mat_emissive); finalColor0 = m_module.opFFma(m_vec4Type, mat_ambient, ambientValue, finalColor0); @@ -1566,7 +1566,7 @@ namespace dxvk { uint32_t current = diffuse; // Temp starts off as equal to vec4(0) uint32_t temp = m_module.constvec4f32(0.0f, 0.0f, 0.0f, 0.0f); - + uint32_t texture = m_module.constvec4f32(0.0f, 0.0f, 0.0f, 1.0f); for (uint32_t i = 0; i < caps::TextureStageCount; i++) { @@ -1675,7 +1675,7 @@ namespace dxvk { uint32_t lOffset = m_module.opAccessChain(m_module.defPointerType(m_floatType, spv::StorageClassUniform), m_ps.sharedState, 1, &index); lOffset = m_module.opLoad(m_floatType, lOffset); - + uint32_t zIndex = 2; uint32_t scale = m_module.opCompositeExtract(m_floatType, texture, 1, &zIndex); scale = m_module.opFMul(m_floatType, scale, lScale); @@ -2165,40 +2165,40 @@ namespace dxvk { uint32_t worldPos = emitMatrixTimesVector(4, 4, m_vs.constants.inverseView, vtx); uint32_t clipPlaneCountId = m_module.constu32(caps::MaxClipPlanes); - + uint32_t floatType = m_module.defFloatType(32); uint32_t vec4Type = m_module.defVectorType(floatType, 4); - + // Declare uniform buffer containing clip planes uint32_t clipPlaneArray = m_module.defArrayTypeUnique(vec4Type, clipPlaneCountId); uint32_t clipPlaneStruct = m_module.defStructTypeUnique(1, &clipPlaneArray); uint32_t clipPlaneBlock = m_module.newVar( m_module.defPointerType(clipPlaneStruct, spv::StorageClassUniform), spv::StorageClassUniform); - + m_module.decorateArrayStride (clipPlaneArray, 16); - + m_module.setDebugName (clipPlaneStruct, "clip_info_t"); m_module.setDebugMemberName (clipPlaneStruct, 0, "clip_planes"); m_module.decorate (clipPlaneStruct, spv::DecorationBlock); m_module.memberDecorateOffset (clipPlaneStruct, 0, 0); - + uint32_t bindingId = computeResourceSlotId( DxsoProgramType::VertexShader, DxsoBindingType::ConstantBuffer, DxsoConstantBuffers::VSClipPlanes); - + m_module.setDebugName (clipPlaneBlock, "clip_info"); m_module.decorateDescriptorSet(clipPlaneBlock, 0); m_module.decorateBinding (clipPlaneBlock, bindingId); - + DxvkResourceSlot resource; resource.slot = bindingId; resource.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; resource.view = VK_IMAGE_VIEW_TYPE_MAX_ENUM; resource.access = VK_ACCESS_UNIFORM_READ_BIT; m_resourceSlots.push_back(resource); - + // Declare output array for clip distances uint32_t clipDistArray = m_module.newVar( m_module.defPointerType( @@ -2215,14 +2215,14 @@ namespace dxvk { m_module.constu32(0), m_module.constu32(i), }}; - + uint32_t planeId = m_module.opLoad(vec4Type, m_module.opAccessChain( m_module.defPointerType(vec4Type, spv::StorageClassUniform), clipPlaneBlock, blockMembers.size(), blockMembers.data())); - + uint32_t distId = m_module.opDot(floatType, worldPos, planeId); - + m_module.opStore( m_module.opAccessChain( m_module.defPointerType(floatType, spv::StorageClassOutput), @@ -2418,7 +2418,7 @@ namespace dxvk { std::ofstream dumpStream( str::tows(str::format(dumpPath, "/", Name, ".spv").c_str()).c_str(), std::ios_base::binary | std::ios_base::trunc); - + m_shader->dump(dumpStream); } } @@ -2431,7 +2431,7 @@ namespace dxvk { auto entry = m_vsModules.find(ShaderKey); if (entry != m_vsModules.end()) return entry->second; - + D3D9FFShader shader( pDevice, ShaderKey); @@ -2448,7 +2448,7 @@ namespace dxvk { auto entry = m_fsModules.find(ShaderKey); if (entry != m_fsModules.end()) return entry->second; - + D3D9FFShader shader( pDevice, ShaderKey); diff --git a/src/dxso/dxso_compiler.cpp b/src/dxso/dxso_compiler.cpp index 90dbec36e04..90d8f05c577 100644 --- a/src/dxso/dxso_compiler.cpp +++ b/src/dxso/dxso_compiler.cpp @@ -255,7 +255,7 @@ namespace dxvk { info.inputMask = m_inputMask; info.outputMask = m_outputMask; info.pushConstOffset = m_pushConstOffset; - info.pushConstSize = m_pushConstOffset; + info.pushConstSize = m_pushConstSize; return new DxvkShader(info, m_module.compile()); } From 680c1014ff0065fbc7f5e8967fbd4ca01f5df4f5 Mon Sep 17 00:00:00 2001 From: pythonlover02 Date: Tue, 14 Oct 2025 14:18:31 -0300 Subject: [PATCH 2/5] [d3d9] Fix barriers with staging buffers --- src/d3d9/d3d9_device.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/d3d9/d3d9_device.cpp b/src/d3d9/d3d9_device.cpp index 44222b58839..9cd528a44f2 100644 --- a/src/d3d9/d3d9_device.cpp +++ b/src/d3d9/d3d9_device.cpp @@ -4246,8 +4246,8 @@ namespace dxvk { info.stages = VK_PIPELINE_STAGE_VERTEX_INPUT_BIT; } else { info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT; - info.stages = VK_PIPELINE_STAGE_TRANSFER_BIT; - info.access = VK_ACCESS_TRANSFER_READ_BIT; + info.stages = VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT; + info.access = VK_ACCESS_TRANSFER_READ_BIT | VK_ACCESS_SHADER_READ_BIT; } D3D9BufferSlice result; From 8a0ec02fb4249122f8e3aed80c8a47587b010443 Mon Sep 17 00:00:00 2001 From: pythonlover02 Date: Tue, 14 Oct 2025 14:32:25 -0300 Subject: [PATCH 3/5] [d3d9] Fix spec constant derp --- src/d3d9/d3d9_format_helpers.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/d3d9/d3d9_format_helpers.cpp b/src/d3d9/d3d9_format_helpers.cpp index e810330c183..833b310e539 100644 --- a/src/d3d9/d3d9_format_helpers.cpp +++ b/src/d3d9/d3d9_format_helpers.cpp @@ -98,9 +98,7 @@ namespace dxvk { bufferViewInfo.rangeLength = srcSlice.length(); auto tmpBufferView = m_device->createBufferView(srcSlice.buffer(), bufferViewInfo); - if (specConstantValue) - m_context->setSpecConstant(VK_PIPELINE_BIND_POINT_COMPUTE, 0, specConstantValue); - + m_context->setSpecConstant(VK_PIPELINE_BIND_POINT_COMPUTE, 0, specConstantValue); m_context->bindResourceView(BindingIds::Image, tmpImageView, nullptr); m_context->bindResourceView(BindingIds::Buffer, nullptr, tmpBufferView); m_context->bindShader(VK_SHADER_STAGE_COMPUTE_BIT, m_shaders[videoFormat.FormatType]); @@ -113,7 +111,7 @@ namespace dxvk { // Reset the spec constants used... if (specConstantValue) m_context->setSpecConstant(VK_PIPELINE_BIND_POINT_COMPUTE, 0, 0); - + m_transferCommands += 1; } @@ -149,8 +147,8 @@ namespace dxvk { void D3D9FormatHelper::FlushInternal() { m_context->flushCommandList(); - + m_transferCommands = 0; } -} \ No newline at end of file +} From 235ab6134d0dd2b8e062cf5e1f8b5a07c60012a3 Mon Sep 17 00:00:00 2001 From: pythonlover02 Date: Thu, 23 Oct 2025 18:16:48 -0300 Subject: [PATCH 4/5] Merge pull request #29 from WinterSnowfall/d3d8-backport --- src/d3d8/d3d8_device.cpp | 69 +++++++++------- src/d3d8/d3d8_interface.cpp | 38 +++++---- src/d3d8/d3d8_main.cpp | 9 ++- src/d3d8/d3d8_state_block.cpp | 114 +++++++++++++++----------- src/d3d8/d3d8_state_block.h | 133 ++++++++++++++++++------------- src/d3d9/d3d9_device.cpp | 23 +++++- src/d3d9/d3d9_fixed_function.cpp | 4 +- src/d3d9/d3d9_stateblock.cpp | 4 + src/d3d9/d3d9_stateblock.h | 9 ++- src/util/config/config.cpp | 57 ++++++++----- 10 files changed, 282 insertions(+), 178 deletions(-) diff --git a/src/d3d8/d3d8_device.cpp b/src/d3d8/d3d8_device.cpp index d1908255d80..e529f5339ab 100644 --- a/src/d3d8/d3d8_device.cpp +++ b/src/d3d8/d3d8_device.cpp @@ -47,7 +47,7 @@ namespace dxvk { , m_behaviorFlags(BehaviorFlags) , m_multithread(BehaviorFlags & D3DCREATE_MULTITHREADED) { // Get the bridge interface to D3D9. - if (FAILED(GetD3D9()->QueryInterface(__uuidof(IDxvkD3D8Bridge), reinterpret_cast(&m_bridge)))) { + if (unlikely(FAILED(GetD3D9()->QueryInterface(__uuidof(IDxvkD3D8Bridge), reinterpret_cast(&m_bridge))))) { throw DxvkError("D3D8Device: ERROR! Failed to get D3D9 Bridge. d3d9.dll might not be DXVK!"); } @@ -1141,31 +1141,34 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE D3D8Device::SetViewport(const D3DVIEWPORT8* pViewport) { D3D8DeviceLock lock = LockDevice(); - if (likely(pViewport != nullptr)) { - // We need a valid render target to validate the viewport - if (unlikely(m_renderTarget == nullptr)) - return D3DERR_INVALIDCALL; + // Outright crashes on native, but let's be + // somewhat more elegant about it. + if (unlikely(pViewport == nullptr)) + return D3DERR_INVALIDCALL; - D3DSURFACE_DESC rtDesc; - HRESULT res = m_renderTarget->GetDesc(&rtDesc); - - // D3D8 will fail when setting a viewport that's outside of the - // current render target, although this apparently works in D3D9 - if (likely(SUCCEEDED(res)) && - unlikely(pViewport->X + pViewport->Width > rtDesc.Width || - pViewport->Y + pViewport->Height > rtDesc.Height)) { - // On Linux/Wine and in windowed mode, we can get in situations - // where the actual render target dimensions are off by one - // pixel to what the game sets them to. Allow this corner case - // to skip the validation, in order to prevent issues. - const bool isOnePixelWider = pViewport->X + pViewport->Width == rtDesc.Width + 1; - const bool isOnePixelTaller = pViewport->Y + pViewport->Height == rtDesc.Height + 1; - - if (m_presentParams.Windowed && (isOnePixelWider || isOnePixelTaller)) { - Logger::debug("D3D8Device::SetViewport: Viewport exceeds render target dimensions by one pixel"); - } else { - return D3DERR_INVALIDCALL; - } + // We need a valid render target to validate the viewport + if (unlikely(m_renderTarget == nullptr)) + return D3DERR_INVALIDCALL; + + D3DSURFACE_DESC rtDesc; + HRESULT res = m_renderTarget->GetDesc(&rtDesc); + + // D3D8 will fail when setting a viewport that's outside of the + // current render target, although this apparently works in D3D9 + if (likely(SUCCEEDED(res)) && + unlikely(pViewport->X + pViewport->Width > rtDesc.Width || + pViewport->Y + pViewport->Height > rtDesc.Height)) { + // On Linux/Wine and in windowed mode, we can get in situations + // where the actual render target dimensions are off by one + // pixel to what the game sets them to. Allow this corner case + // to skip the validation, in order to prevent issues. + const bool isOnePixelWider = pViewport->X + pViewport->Width == rtDesc.Width + 1; + const bool isOnePixelTaller = pViewport->Y + pViewport->Height == rtDesc.Height + 1; + + if (unlikely(m_presentParams.Windowed && (isOnePixelWider || isOnePixelTaller))) { + Logger::debug("D3D8Device::SetViewport: Viewport exceeds render target dimensions by one pixel"); + } else { + return D3DERR_INVALIDCALL; } } @@ -1226,18 +1229,22 @@ namespace dxvk { if (unlikely(ShouldRecord())) return D3DERR_INVALIDCALL; + D3D8StateBlockType stateBlockType = ConvertStateBlockType(Type); + + if (unlikely(stateBlockType == D3D8StateBlockType::Unknown)) { + Logger::warn(str::format("D3D8Device::CreateStateBlock: Invalid state block type: ", Type)); + return D3DERR_INVALIDCALL; + } + Com pStateBlock9; HRESULT res = GetD3D9()->CreateStateBlock(d3d9::D3DSTATEBLOCKTYPE(Type), &pStateBlock9); if (likely(SUCCEEDED(res))) { m_token++; - auto stateBlockIterPair = m_stateBlocks.emplace(std::piecewise_construct, - std::forward_as_tuple(m_token), - std::forward_as_tuple(this, Type, pStateBlock9.ptr())); + m_stateBlocks.emplace(std::piecewise_construct, + std::forward_as_tuple(m_token), + std::forward_as_tuple(this, stateBlockType, pStateBlock9.ptr())); *pToken = m_token; - - // D3D8 state blocks automatically capture state on creation. - stateBlockIterPair.first->second.Capture(); } return res; diff --git a/src/d3d8/d3d8_interface.cpp b/src/d3d8/d3d8_interface.cpp index 5cf366d6e75..2eccaba60f8 100644 --- a/src/d3d8/d3d8_interface.cpp +++ b/src/d3d8/d3d8_interface.cpp @@ -10,7 +10,7 @@ namespace dxvk { D3D8Interface::D3D8Interface() : m_d3d9(d3d9::Direct3DCreate9(D3D_SDK_VERSION)) { // Get the bridge interface to D3D9. - if (FAILED(m_d3d9->QueryInterface(__uuidof(IDxvkD3D8InterfaceBridge), reinterpret_cast(&m_bridge)))) { + if (unlikely(FAILED(m_d3d9->QueryInterface(__uuidof(IDxvkD3D8InterfaceBridge), reinterpret_cast(&m_bridge))))) { throw DxvkError("D3D8Interface: ERROR! Failed to get D3D9 Bridge. d3d9.dll might not be DXVK!"); } @@ -75,21 +75,22 @@ namespace dxvk { d3d9::D3DADAPTER_IDENTIFIER9 identifier9; HRESULT res = m_d3d9->GetAdapterIdentifier(Adapter, Flags, &identifier9); - if (likely(SUCCEEDED(res))) { - strncpy(pIdentifier->Driver, identifier9.Driver, MAX_DEVICE_IDENTIFIER_STRING); - strncpy(pIdentifier->Description, identifier9.Description, MAX_DEVICE_IDENTIFIER_STRING); + if (unlikely(FAILED(res))) + return res; - pIdentifier->DriverVersion = identifier9.DriverVersion; - pIdentifier->VendorId = identifier9.VendorId; - pIdentifier->DeviceId = identifier9.DeviceId; - pIdentifier->SubSysId = identifier9.SubSysId; - pIdentifier->Revision = identifier9.Revision; - pIdentifier->DeviceIdentifier = identifier9.DeviceIdentifier; + strncpy(pIdentifier->Driver, identifier9.Driver, MAX_DEVICE_IDENTIFIER_STRING); + strncpy(pIdentifier->Description, identifier9.Description, MAX_DEVICE_IDENTIFIER_STRING); - pIdentifier->WHQLLevel = identifier9.WHQLLevel; - } + pIdentifier->DriverVersion = identifier9.DriverVersion; + pIdentifier->VendorId = identifier9.VendorId; + pIdentifier->DeviceId = identifier9.DeviceId; + pIdentifier->SubSysId = identifier9.SubSysId; + pIdentifier->Revision = identifier9.Revision; + pIdentifier->DeviceIdentifier = identifier9.DeviceIdentifier; - return res; + pIdentifier->WHQLLevel = identifier9.WHQLLevel; + + return D3D_OK; } HRESULT __stdcall D3D8Interface::EnumAdapterModes( @@ -136,14 +137,21 @@ namespace dxvk { &pDevice9 ); - if (likely(SUCCEEDED(res))) + if (unlikely(FAILED(res))) + return res; + + try { *ppReturnedDeviceInterface = ref(new D3D8Device( this, std::move(pDevice9), DeviceType, hFocusWindow, BehaviorFlags, pPresentationParameters )); + } catch (const DxvkError& e) { + Logger::err(e.message()); + return D3DERR_NOTAVAILABLE; + } - return res; + return D3D_OK; } HRESULT D3D8Interface::ValidatePresentationParameters( diff --git a/src/d3d8/d3d8_main.cpp b/src/d3d8/d3d8_main.cpp index e4c5213e044..e752869f9cf 100644 --- a/src/d3d8/d3d8_main.cpp +++ b/src/d3d8/d3d8_main.cpp @@ -8,7 +8,14 @@ namespace dxvk { if (!ppDirect3D8) return D3DERR_INVALIDCALL; - *ppDirect3D8 = ref(new D3D8Interface()); + try { + *ppDirect3D8 = ref(new D3D8Interface()); + } catch (const DxvkError& e) { + Logger::err(e.message()); + *ppDirect3D8 = nullptr; + return D3DERR_NOTAVAILABLE; + } + return D3D_OK; } diff --git a/src/d3d8/d3d8_state_block.cpp b/src/d3d8/d3d8_state_block.cpp index c95b6b56da8..19f2470caaf 100644 --- a/src/d3d8/d3d8_state_block.cpp +++ b/src/d3d8/d3d8_state_block.cpp @@ -5,37 +5,43 @@ namespace dxvk { D3D8StateBlock::D3D8StateBlock( D3D8Device* pDevice, - D3DSTATEBLOCKTYPE Type, + D3D8StateBlockType Type, Com&& pStateBlock) : m_device(pDevice) - , m_stateBlock(std::move(pStateBlock)) - , m_type(Type) - , m_isSWVP(pDevice->GetD3D9()->GetSoftwareVertexProcessing()) { - if (Type == D3DSBT_VERTEXSTATE || Type == D3DSBT_ALL) { + , m_stateBlock(std::move(pStateBlock)) { + if (Type == D3D8StateBlockType::All) { + m_captures.flags.set(D3D8CapturedStateFlag::Indices); + m_captures.flags.set(D3D8CapturedStateFlag::SWVP); + + m_captures.flags.set(D3D8CapturedStateFlag::VertexBuffers); + m_captures.streams.setAll(); + + m_captures.flags.set(D3D8CapturedStateFlag::Textures); + m_captures.textures.setAll(); + } + + if (Type == D3D8StateBlockType::VertexState || Type == D3D8StateBlockType::All) { // Lights, D3DTSS_TEXCOORDINDEX and D3DTSS_TEXTURETRANSFORMFLAGS, // vertex shader, VS constants, and various render states. - m_capture.vs = true; + m_captures.flags.set(D3D8CapturedStateFlag::VertexShader); } - if (Type == D3DSBT_PIXELSTATE || Type == D3DSBT_ALL) { + if (Type == D3D8StateBlockType::PixelState || Type == D3D8StateBlockType::All) { // Pixel shader, PS constants, and various RS/TSS states. - m_capture.ps = true; + m_captures.flags.set(D3D8CapturedStateFlag::PixelShader); } - if (Type == D3DSBT_ALL) { - m_capture.indices = true; - m_capture.swvp = true; - m_capture.textures.setAll(); - m_capture.streams.setAll(); - } + m_state.textures.fill(nullptr); + m_state.streams.fill(D3D8VBOP()); - m_textures.fill(nullptr); - m_streams.fill(D3D8VBOP()); + // Automatically capture state on creation via D3D8Device::CreateStateBlock. + if (Type != D3D8StateBlockType::None) + Capture(); } // Construct a state block without a D3D9 object D3D8StateBlock::D3D8StateBlock(D3D8Device* pDevice) - : D3D8StateBlock(pDevice, D3DSTATEBLOCKTYPE(0), nullptr) { + : D3D8StateBlock(pDevice, D3D8StateBlockType::None, nullptr) { } // Attach a D3D9 object to a state block that doesn't have one yet @@ -51,31 +57,38 @@ namespace dxvk { if (unlikely(m_stateBlock == nullptr)) return D3DERR_INVALIDCALL; - if (m_capture.vs) m_device->GetVertexShader(&m_vertexShader); - if (m_capture.ps) m_device->GetPixelShader(&m_pixelShader); + if (m_captures.flags.test(D3D8CapturedStateFlag::Indices)) { + m_state.baseVertexIndex = m_device->m_baseVertexIndex; + m_state.indices = m_device->m_indices.ptr(); + } - for (DWORD stage = 0; stage < m_textures.size(); stage++) { - if (m_capture.textures.get(stage)) - m_textures[stage] = m_device->m_textures[stage].ptr(); + if (m_captures.flags.test(D3D8CapturedStateFlag::SWVP)) { + DWORD swvpState; + m_device->GetRenderState(D3DRS_SOFTWAREVERTEXPROCESSING, &swvpState); + m_state.isSWVP = static_cast(swvpState); } - for (DWORD stream = 0; stream < m_streams.size(); stream++) { - if (m_capture.streams.get(stream)) { - m_streams[stream].buffer = m_device->m_streams[stream].buffer.ptr(); - m_streams[stream].stride = m_device->m_streams[stream].stride; + if (m_captures.flags.test(D3D8CapturedStateFlag::VertexBuffers)) { + for (DWORD stream = 0; stream < m_state.streams.size(); stream++) { + if (m_captures.streams.get(stream)) { + m_state.streams[stream].buffer = m_device->m_streams[stream].buffer.ptr(); + m_state.streams[stream].stride = m_device->m_streams[stream].stride; + } } } - if (m_capture.indices) { - m_baseVertexIndex = m_device->m_baseVertexIndex; - m_indices = m_device->m_indices.ptr(); + if (m_captures.flags.test(D3D8CapturedStateFlag::Textures)) { + for (DWORD stage = 0; stage < m_state.textures.size(); stage++) { + if (m_captures.textures.get(stage)) + m_state.textures[stage] = m_device->m_textures[stage].ptr(); + } } - if (m_capture.swvp) { - DWORD swvpState; - m_device->GetRenderState(D3DRS_SOFTWAREVERTEXPROCESSING, &swvpState); - m_isSWVP = static_cast(swvpState); - } + if (m_captures.flags.test(D3D8CapturedStateFlag::VertexShader)) + m_device->GetVertexShader(&m_state.vertexShaderHandle); + + if (m_captures.flags.test(D3D8CapturedStateFlag::PixelShader)) + m_device->GetPixelShader(&m_state.pixelShaderHandle); return m_stateBlock->Capture(); } @@ -86,25 +99,32 @@ namespace dxvk { HRESULT res = m_stateBlock->Apply(); - if (m_capture.vs) m_device->SetVertexShader(m_vertexShader); - if (m_capture.ps) m_device->SetPixelShader(m_pixelShader); + if (m_captures.flags.test(D3D8CapturedStateFlag::Indices)) + m_device->SetIndices(m_state.indices, m_state.baseVertexIndex); - for (DWORD stage = 0; stage < m_textures.size(); stage++) { - if (m_capture.textures.get(stage)) - m_device->SetTexture(stage, m_textures[stage]); + // This was a very easy footgun for D3D8 applications. + if (m_captures.flags.test(D3D8CapturedStateFlag::SWVP)) + m_device->SetRenderState(D3DRS_SOFTWAREVERTEXPROCESSING, static_cast(m_state.isSWVP)); + + if (m_captures.flags.test(D3D8CapturedStateFlag::VertexBuffers)) { + for (DWORD stream = 0; stream < m_state.streams.size(); stream++) { + if (m_captures.streams.get(stream)) + m_device->SetStreamSource(stream, m_state.streams[stream].buffer, m_state.streams[stream].stride); + } } - for (DWORD stream = 0; stream < m_streams.size(); stream++) { - if (m_capture.streams.get(stream)) - m_device->SetStreamSource(stream, m_streams[stream].buffer, m_streams[stream].stride); + if (m_captures.flags.test(D3D8CapturedStateFlag::Textures)) { + for (DWORD stage = 0; stage < m_state.textures.size(); stage++) { + if (m_captures.textures.get(stage)) + m_device->SetTexture(stage, m_state.textures[stage]); + } } - if (m_capture.indices) - m_device->SetIndices(m_indices, m_baseVertexIndex); + if (m_captures.flags.test(D3D8CapturedStateFlag::VertexShader)) + m_device->SetVertexShader(m_state.vertexShaderHandle); - // This was a very easy footgun for D3D8 applications. - if (m_capture.swvp) - m_device->SetRenderState(D3DRS_SOFTWAREVERTEXPROCESSING, static_cast(m_isSWVP)); + if (m_captures.flags.test(D3D8CapturedStateFlag::PixelShader)) + m_device->SetPixelShader(m_state.pixelShaderHandle); return res; } diff --git a/src/d3d8/d3d8_state_block.h b/src/d3d8/d3d8_state_block.h index df55a74fbdf..73bb01f4da0 100644 --- a/src/d3d8/d3d8_state_block.h +++ b/src/d3d8/d3d8_state_block.h @@ -6,31 +6,70 @@ #include "d3d8_device_child.h" #include "../util/util_bit.h" +#include "../util/util_flags.h" #include namespace dxvk { - struct D3D8StateCapture { - bool vs : 1; - bool ps : 1; - bool indices : 1; - bool swvp : 1; + enum class D3D8CapturedStateFlag : uint8_t { + Indices, + SWVP, + VertexBuffers, + Textures, + VertexShader, + PixelShader + }; + + using D3D8CapturedStateFlags = Flags; + + struct D3D8StateCaptures { + D3D8CapturedStateFlags flags; - bit::bitset textures; bit::bitset streams; + bit::bitset textures; - D3D8StateCapture() - : vs(false) - , ps(false) - , indices(false) - , swvp(false) { + D3D8StateCaptures() { // Ensure all bits are initialized to false - textures.clearAll(); streams.clearAll(); + textures.clearAll(); } }; + struct D3D8VBOP { + IDirect3DVertexBuffer8* buffer = nullptr; + UINT stride = 0; + }; + + struct D3D8CapturableState { + std::array streams; + std::array textures; + + IDirect3DIndexBuffer8* indices = nullptr; + UINT baseVertexIndex = 0; + DWORD vertexShaderHandle = 0; + DWORD pixelShaderHandle = 0; + + bool isSWVP = false; // D3DRS_SOFTWAREVERTEXPROCESSING + }; + + enum class D3D8StateBlockType : uint8_t { + None, + All, + PixelState, + VertexState, + Unknown + }; + + inline D3D8StateBlockType ConvertStateBlockType(D3DSTATEBLOCKTYPE type) { + switch (type) { + case D3DSBT_ALL: return D3D8StateBlockType::All; + case D3DSBT_PIXELSTATE: return D3D8StateBlockType::PixelState; + case D3DSBT_VERTEXSTATE: return D3D8StateBlockType::VertexState; + default: return D3D8StateBlockType::Unknown; + } + } + // Wrapper class for D3D9 state blocks. Captures D3D8-specific state. class D3D8StateBlock { @@ -38,7 +77,7 @@ namespace dxvk { D3D8StateBlock( D3D8Device* pDevice, - D3DSTATEBLOCKTYPE Type, + D3D8StateBlockType Type, Com&& pStateBlock); D3D8StateBlock(D3D8Device* pDevice); @@ -49,43 +88,45 @@ namespace dxvk { HRESULT Apply(); - inline HRESULT SetVertexShader(DWORD Handle) { - m_vertexShader = Handle; - m_capture.vs = true; - return D3D_OK; - } - - inline HRESULT SetPixelShader(DWORD Handle) { - m_pixelShader = Handle; - m_capture.ps = true; + inline HRESULT SetIndices(IDirect3DIndexBuffer8* pIndexData, UINT BaseVertexIndex) { + m_state.indices = pIndexData; + m_state.baseVertexIndex = BaseVertexIndex; + m_captures.flags.set(D3D8CapturedStateFlag::Indices); return D3D_OK; } - inline HRESULT SetTexture(DWORD Stage, IDirect3DBaseTexture8* pTexture) { - m_textures[Stage] = pTexture; - m_capture.textures.set(Stage, true); + inline HRESULT SetSoftwareVertexProcessing(bool value) { + m_state.isSWVP = value; + m_captures.flags.set(D3D8CapturedStateFlag::SWVP); return D3D_OK; } inline HRESULT SetStreamSource(UINT StreamNumber, IDirect3DVertexBuffer8* pStreamData, UINT Stride) { - m_streams[StreamNumber].buffer = pStreamData; + m_state.streams[StreamNumber].buffer = pStreamData; // The previous stride is preserved if pStreamData is NULL if (likely(pStreamData != nullptr)) - m_streams[StreamNumber].stride = Stride; - m_capture.streams.set(StreamNumber, true); + m_state.streams[StreamNumber].stride = Stride; + m_captures.flags.set(D3D8CapturedStateFlag::VertexBuffers); + m_captures.streams.set(StreamNumber, true); return D3D_OK; } - inline HRESULT SetIndices(IDirect3DIndexBuffer8* pIndexData, UINT BaseVertexIndex) { - m_indices = pIndexData; - m_baseVertexIndex = BaseVertexIndex; - m_capture.indices = true; + inline HRESULT SetTexture(DWORD Stage, IDirect3DBaseTexture8* pTexture) { + m_state.textures[Stage] = pTexture; + m_captures.flags.set(D3D8CapturedStateFlag::Textures); + m_captures.textures.set(Stage, true); return D3D_OK; } - inline HRESULT SetSoftwareVertexProcessing(bool value) { - m_isSWVP = value; - m_capture.swvp = true; + inline HRESULT SetVertexShader(DWORD Handle) { + m_state.vertexShaderHandle = Handle; + m_captures.flags.set(D3D8CapturedStateFlag::VertexShader); + return D3D_OK; + } + + inline HRESULT SetPixelShader(DWORD Handle) { + m_state.pixelShaderHandle = Handle; + m_captures.flags.set(D3D8CapturedStateFlag::PixelShader); return D3D_OK; } @@ -93,27 +134,9 @@ namespace dxvk { D3D8Device* m_device; Com m_stateBlock; - D3DSTATEBLOCKTYPE m_type; - - struct D3D8VBOP { - IDirect3DVertexBuffer8* buffer = nullptr; - UINT stride = 0; - }; - - // State Data // - - D3D8StateCapture m_capture; - - DWORD m_vertexShader = 0; - DWORD m_pixelShader = 0; - - std::array m_textures; - std::array m_streams; - - IDirect3DIndexBuffer8* m_indices = nullptr; - UINT m_baseVertexIndex = 0; - bool m_isSWVP; // D3DRS_SOFTWAREVERTEXPROCESSING + D3D8CapturableState m_state; + D3D8StateCaptures m_captures; }; diff --git a/src/d3d9/d3d9_device.cpp b/src/d3d9/d3d9_device.cpp index 9cd528a44f2..c983d7ad240 100644 --- a/src/d3d9/d3d9_device.cpp +++ b/src/d3d9/d3d9_device.cpp @@ -1693,13 +1693,23 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE D3D9DeviceEx::SetViewport(const D3DVIEWPORT9* pViewport) { D3D9DeviceLock lock = LockDevice(); + // Outright crashes on native, but let's be + // somewhat more elegant about it. + if (unlikely(pViewport == nullptr)) + return D3DERR_INVALIDCALL; + if (unlikely(ShouldRecord())) return m_recorder->SetViewport(pViewport); if (m_state.viewport == *pViewport) return D3D_OK; - m_state.viewport = *pViewport; + m_state.viewport.X = pViewport->X; + m_state.viewport.Y = pViewport->Y; + m_state.viewport.Width = pViewport->Width; + m_state.viewport.Height = pViewport->Height; + m_state.viewport.MinZ = pViewport->MinZ; + m_state.viewport.MaxZ = pViewport->MinZ < pViewport->MaxZ ? pViewport->MaxZ : pViewport->MinZ + 0.001f; m_flags.set(D3D9DeviceFlag::DirtyViewportScissor); m_flags.set(D3D9DeviceFlag::DirtyFFViewport); @@ -1712,7 +1722,7 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE D3D9DeviceEx::GetViewport(D3DVIEWPORT9* pViewport) { D3D9DeviceLock lock = LockDevice(); - if (pViewport == nullptr) + if (unlikely(pViewport == nullptr)) return D3DERR_INVALIDCALL; *pViewport = m_state.viewport; @@ -2241,8 +2251,15 @@ namespace dxvk { if (unlikely(ppSB == nullptr)) return D3DERR_INVALIDCALL; + D3D9StateBlockType stateBlockType = ConvertStateBlockType(Type); + + if (unlikely(stateBlockType == D3D9StateBlockType::Unknown)) { + Logger::warn(str::format("D3D9DeviceEx::CreateStateBlock: Invalid state block type: ", Type)); + return D3DERR_INVALIDCALL; + } + try { - const Com sb = new D3D9StateBlock(this, ConvertStateBlockType(Type)); + const Com sb = new D3D9StateBlock(this, stateBlockType); *ppSB = sb.ref(); return D3D_OK; } diff --git a/src/d3d9/d3d9_fixed_function.cpp b/src/d3d9/d3d9_fixed_function.cpp index fd9216e6a1b..39078e61b33 100644 --- a/src/d3d9/d3d9_fixed_function.cpp +++ b/src/d3d9/d3d9_fixed_function.cpp @@ -1735,7 +1735,9 @@ namespace dxvk { reg = diffuse; break; case D3DTA_SPECULAR: - reg = specular; + // Specular highlights shouldn't be calculated at all if D3DRS_SPECULARENABLE + // is set to FALSE, so return vec4(0.0) to ensure correctness during texture blending. + reg = m_fsKey.Stages[0].Contents.GlobalSpecularEnable ? specular : m_module.constvec4f32(0.0f, 0.0f, 0.0f, 0.0f); break; case D3DTA_TEMP: reg = temp; diff --git a/src/d3d9/d3d9_stateblock.cpp b/src/d3d9/d3d9_stateblock.cpp index 72c85305dcb..d6d827320b0 100644 --- a/src/d3d9/d3d9_stateblock.cpp +++ b/src/d3d9/d3d9_stateblock.cpp @@ -39,6 +39,8 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE D3D9StateBlock::Capture() { + D3D9DeviceLock lock = m_parent->LockDevice(); + if (m_captures.flags.test(D3D9CapturedStateFlag::VertexDecl)) SetVertexDeclaration(m_deviceState->vertexDecl.ptr()); @@ -49,6 +51,8 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE D3D9StateBlock::Apply() { + D3D9DeviceLock lock = m_parent->LockDevice(); + m_applying = true; if (m_captures.flags.test(D3D9CapturedStateFlag::VertexDecl) && m_state.vertexDecl != nullptr) diff --git a/src/d3d9/d3d9_stateblock.h b/src/d3d9/d3d9_stateblock.h index 43a712ad6fa..5c9e039b5da 100644 --- a/src/d3d9/d3d9_stateblock.h +++ b/src/d3d9/d3d9_stateblock.h @@ -63,19 +63,20 @@ namespace dxvk { } psConsts; }; - enum class D3D9StateBlockType :uint32_t { + enum class D3D9StateBlockType : uint8_t { None, + All, VertexState, PixelState, - All + Unknown }; inline D3D9StateBlockType ConvertStateBlockType(D3DSTATEBLOCKTYPE type) { switch (type) { + case D3DSBT_ALL: return D3D9StateBlockType::All; case D3DSBT_PIXELSTATE: return D3D9StateBlockType::PixelState; case D3DSBT_VERTEXSTATE: return D3D9StateBlockType::VertexState; - default: - case D3DSBT_ALL: return D3D9StateBlockType::All; + default: return D3D9StateBlockType::Unknown; } } diff --git a/src/util/config/config.cpp b/src/util/config/config.cpp index b3ac3804237..5623f7328ae 100644 --- a/src/util/config/config.cpp +++ b/src/util/config/config.cpp @@ -1094,6 +1094,23 @@ namespace dxvk { { "d3d9.customDeviceId", "0330" }, { "d3d9.customDeviceDesc", "NVIDIA GeForce FX 5900 Ultra" }, }} }, + /* Psi-Ops: The Mindgate Conspiracy * + * Broken input and physics above 60 fps */ + { R"(\\PsiOps\.exe$)", {{ + { "d3d9.maxFrameRate", "60" }, + }} }, + /* Alone in the Dark (2008) * + * Crashes when selecting the graphics menu * + * option without memory tracking in place */ + { R"(\\Alone\.exe$)", {{ + { "d3d9.memoryTrackTest", "True" }, + }} }, + /* Heroes of Annihilated Empires * + * Cursor and other animations play back too * + * fast without a frame cap in place. */ + { R"(\\Heroes of Annihilated Empires.*\\engine\.exe$)", {{ + { "d3d9.maxFrameRate", "60" }, + }} }, /**********************************************/ /* D3D8 GAMES */ @@ -1229,12 +1246,18 @@ namespace dxvk { { R"(\\fifa2003(demo)?\.exe$)", {{ { "d3d9.cachedDynamicBuffers", "True" }, }} }, - /* Splinter Cell: Pandora Tomorrow * - * Broken inputs and physics above 60 FPS */ - { R"(\\SplinterCell2\.exe$)", {{ + /* Splinter Cell: Pandora Tomorrow (Retail) * + * Missing shadows without dref scaling and * + * broken inputs and physics above 60 FPS */ + { R"(\\offline\\system\\SplinterCell2\.exe$)", {{ { "d3d9.maxFrameRate", "60" }, { "d3d8.scaleDref", "24" }, }} }, + /* Splinter Cell: Pandora Tomorrow (Steam) * + * Broken inputs and physics above 60 FPS */ + { R"(\\Splinter Cell Pandora Tomorrow\\system\\SplinterCell2\.exe$)", {{ + { "d3d9.maxFrameRate", "60" }, + }} }, /* Chrome: Gold Edition * * Broken character model motion at high FPS */ { R"(\\Chrome(Single|Net)\.exe$)", {{ @@ -1253,27 +1276,9 @@ namespace dxvk { { R"(\\splintercell\.exe$)", {{ { "d3d9.customVendorId", "10de" }, { "d3d9.maxFrameRate", "60" }, - { "d3d9.deviceLossOnFocusLoss", "True" }, { "d3d8.scaleDref", "24" }, { "d3d8.shadowPerspectiveDivide", "True" }, }} }, - /* Trainz v1.3 (2001) * - * Fixes black screen after alt-tab */ - { R"(\\bin\\trainz\.exe$)", {{ - { "d3d9.deviceLossOnFocusLoss", "True" }, - }} }, - /* A.I.M.: Artificial Intelligence Machine * - * Fixes black screen after the options * - * window is closed or on alt-tab */ - { R"(\\AIM\.exe$)", {{ - { "d3d9.deviceLossOnFocusLoss", "True" }, - }} }, - /* Star Trek: Starfleet Command III * - * The GOG release ships with a D3D8 to D3D9 * - * wrapper that leaks several surfaces. */ - { R"(\\SFC3\.exe$)", {{ - { "d3d9.countLosableResources", "False" }, - }} }, /* GTR - FIA GT Racing Game * * Vram complaint & restricted resolutions * * Performance */ @@ -1295,6 +1300,16 @@ namespace dxvk { { R"(\\TopSpin\.exe$)", {{ { "d3d8.forceLegacyDiscard", "True" }, }} }, + /* Lego Racers 2 - Hits an incredible amount * + * of queue syncs with direct buffer mapping */ + { R"(\\LEGO Racers 2\.exe$)", {{ + { "d3d9.allowDirectBufferMapping", "False" }, + }} }, + /* Smash Up Derby - Poor performance on Intel * + * due to queue syncs on certain race tracks */ + { R"(\\Smash up Derby\\cars\.exe$)", {{ + { "d3d9.allowDirectBufferMapping", "False" }, + }} }, }}; From eeb773d14feba62cfc6ca463f33bb5e5824d7cba Mon Sep 17 00:00:00 2001 From: pythonlover02 Date: Mon, 22 Dec 2025 01:05:39 -0300 Subject: [PATCH 5/5] [d3d9] Fix recording MultiplyTransform --- src/d3d9/d3d9_device.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/d3d9/d3d9_device.cpp b/src/d3d9/d3d9_device.cpp index c983d7ad240..444f4f074e9 100644 --- a/src/d3d9/d3d9_device.cpp +++ b/src/d3d9/d3d9_device.cpp @@ -1674,10 +1674,10 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE D3D9DeviceEx::MultiplyTransform(D3DTRANSFORMSTATETYPE TransformState, const D3DMATRIX* pMatrix) { D3D9DeviceLock lock = LockDevice(); - if (unlikely(ShouldRecord())) - return m_recorder->MultiplyStateTransform(TransformState, pMatrix); + const uint32_t idx = GetTransformIndex(TransformState); - uint32_t idx = GetTransformIndex(TransformState); + if (unlikely(ShouldRecord())) + return m_recorder->MultiplyStateTransform(idx, pMatrix); m_state.transforms[idx] = m_state.transforms[idx] * ConvertMatrix(pMatrix);