From ddc62ad00e6f02d6c51d2010cc815c1dc9f8f40b Mon Sep 17 00:00:00 2001 From: Anthony Miles Date: Tue, 27 Oct 2020 18:23:33 +1300 Subject: [PATCH 01/21] Extract HLSL compilation to a method --- src/core/hle/D3D8/Direct3D9/VertexShader.cpp | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/core/hle/D3D8/Direct3D9/VertexShader.cpp b/src/core/hle/D3D8/Direct3D9/VertexShader.cpp index b14b4a27b..caaabef8d 100644 --- a/src/core/hle/D3D8/Direct3D9/VertexShader.cpp +++ b/src/core/hle/D3D8/Direct3D9/VertexShader.cpp @@ -237,25 +237,26 @@ HRESULT CompileHlsl(const std::string& hlsl, ID3DBlob** ppHostShader, const char if (FAILED(hRet)) { LOG_TEST_CASE("Couldn't assemble vertex shader"); - //EmuLog(LOG_LEVEL::WARNING, "Couldn't assemble recompiled vertex shader"); } } // Determine the log level auto hlslErrorLogLevel = FAILED(hRet) ? LOG_LEVEL::ERROR2 : LOG_LEVEL::DEBUG; if (pErrors) { - // Log HLSL compiler errors + // Log errors from the initial compilation EmuLog(hlslErrorLogLevel, "%s", (char*)(pErrors->GetBufferPointer())); pErrors->Release(); pErrors = nullptr; - if (pErrorsCompatibility != nullptr) { - pErrorsCompatibility->Release(); - pErrorsCompatibility = nullptr; - } } - LOG_CHECK_ENABLED(LOG_LEVEL::DEBUG) - if (g_bPrintfOn) + // Failure to recompile in compatibility mode ignored for now + if (pErrorsCompatibility != nullptr) { + pErrorsCompatibility->Release(); + pErrorsCompatibility = nullptr; + } + + LOG_CHECK_ENABLED(LOG_LEVEL::DEBUG) { + if (g_bPrintfOn) { if (!FAILED(hRet)) { // Log disassembly hRet = D3DDisassemble( @@ -270,6 +271,8 @@ HRESULT CompileHlsl(const std::string& hlsl, ID3DBlob** ppHostShader, const char pErrors->Release(); } } + } + } return hRet; } From 99a96e675df676b1052784992b1b6eeb73139a13 Mon Sep 17 00:00:00 2001 From: Anthony Miles Date: Tue, 27 Oct 2020 20:43:19 +1300 Subject: [PATCH 02/21] Add FixedFunctionState to keep track of D3D fixed function lights --- CMakeLists.txt | 2 + src/core/hle/D3D8/FixedFunctionState.cpp | 87 ++++++++++++++++++++++++ src/core/hle/D3D8/FixedFunctionState.h | 26 +++++++ 3 files changed, 115 insertions(+) create mode 100644 src/core/hle/D3D8/FixedFunctionState.cpp create mode 100644 src/core/hle/D3D8/FixedFunctionState.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 64b50be5e..295102ab3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -121,6 +121,7 @@ file (GLOB CXBXR_HEADER_EMU "${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/VertexShader.h" "${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/VertexShaderSource.h" "${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/WalkIndexBuffer.h" + "${CXBXR_ROOT_DIR}/src/core/hle/D3D8/FixedFunctionState.h" "${CXBXR_ROOT_DIR}/src/core/hle/D3D8/ResourceTracker.h" "${CXBXR_ROOT_DIR}/src/core/hle/D3D8/XbConvert.h" "${CXBXR_ROOT_DIR}/src/core/hle/D3D8/XbD3D8Logging.h" @@ -266,6 +267,7 @@ file (GLOB CXBXR_SOURCE_EMU "${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/VertexShader.cpp" "${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/VertexShaderSource.cpp" "${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/WalkIndexBuffer.cpp" + "${CXBXR_ROOT_DIR}/src/core/hle/D3D8/FixedFunctionState.cpp" "${CXBXR_ROOT_DIR}/src/core/hle/D3D8/ResourceTracker.cpp" "${CXBXR_ROOT_DIR}/src/core/hle/D3D8/XbConvert.cpp" "${CXBXR_ROOT_DIR}/src/core/hle/D3D8/XbD3D8Logging.cpp" diff --git a/src/core/hle/D3D8/FixedFunctionState.cpp b/src/core/hle/D3D8/FixedFunctionState.cpp new file mode 100644 index 000000000..6eb6064d8 --- /dev/null +++ b/src/core/hle/D3D8/FixedFunctionState.cpp @@ -0,0 +1,87 @@ +#define LOG_PREFIX CXBXR_MODULE::D3D8 + +#include "FixedFunctionState.h" +#include "Logging.h" + +D3DCOLORVALUE colorValue(float r, float g, float b, float a) { + auto value = D3DCOLORVALUE(); + value.r = r; + value.g = g; + value.b = b; + value.a = a; + return value; +} + +D3DVECTOR toVector(float x, float y, float z) { + auto value = D3DVECTOR(); + value.x = x; + value.y = y; + value.z = z; + return value; +} + +D3D8LightState::D3D8LightState() { + // Define the default light + // When unset lights are enabled, they're set to the default light + auto defaultLight = xbox::X_D3DLIGHT8(); + defaultLight.Type = D3DLIGHT_DIRECTIONAL; + defaultLight.Diffuse = colorValue(1, 1, 1, 0); + defaultLight.Specular = colorValue(0, 0, 0, 0); + defaultLight.Ambient = colorValue(0, 0, 0, 0); + defaultLight.Position = toVector(0, 0, 0); + defaultLight.Direction = toVector(0, 0, 1); + defaultLight.Range = 0; + defaultLight.Falloff = 0; + defaultLight.Attenuation0 = 0; + defaultLight.Attenuation1 = 0; + defaultLight.Attenuation2 = 0; + defaultLight.Theta = 0; + defaultLight.Phi = 0; + + // We'll just preset every light to the default light + Lights.fill(defaultLight); + EnabledLights.fill(-1); +} + +void D3D8LightState::EnableLight(uint32_t index, bool enable) { + // Since Xbox only supports 8 lights, we keep track of the 8 most recently enabled lights + // Lights are ordered oldest to newest, with disabled lights at the end + + // Check to see if the light is already enabled + for (size_t i = 0; i < EnabledLightCount; i++) { + + // If the light is already in the enabled lights + if (EnabledLights[i] == index) { + // Either way we move this light to the end + std::rotate(std::begin(EnabledLights) + i, std::begin(EnabledLights) + i + 1, std::begin(EnabledLights) + EnabledLightCount); + + if (enable) { + // Don't need to do anything + EmuLog(LOG_LEVEL::INFO, "Enabled light %d but it was already enabled", index); + } + else { + // Disable the light + EnabledLights[EnabledLightCount - 1] = -1; + EnabledLightCount--; + } + return; + } + } + + if (enable) { + // The light was not in the enabled lights. Let's add it + if (EnabledLightCount < EnabledLights.size()) { + EnabledLights[EnabledLightCount] = index; // add it to the end + EnabledLightCount++; + } + else { + // Replace the oldest element and move to end + EmuLog(LOG_LEVEL::INFO, "Can't enable any more lights. Replacing the oldest light %i", EnabledLights[0]); + EnabledLights[0] = index; + std::rotate(std::begin(EnabledLights), std::begin(EnabledLights) + 1, std::end(EnabledLights)); + } + } + else { + EmuLog(LOG_LEVEL::INFO, "Could not disable light %d because it wasn't enabled", index); + } +} diff --git a/src/core/hle/D3D8/FixedFunctionState.h b/src/core/hle/D3D8/FixedFunctionState.h new file mode 100644 index 000000000..d7a4c3fd1 --- /dev/null +++ b/src/core/hle/D3D8/FixedFunctionState.h @@ -0,0 +1,26 @@ +#ifndef FIXEDFUNCTIONSTATE_H +#define FIXEDFUNCTIONSTATE_H + +#include "XbD3D8Types.h" +#include + +class D3D8LightState { +public: + std::array Lights; + + // The indices of last 8 enabled lights + // From least recently to most recently enabled + // -1 represents empty light slots + // which always appear after enabled lights + std::array EnabledLights; + + // The number of enabled lights + uint32_t EnabledLightCount = 0; + + D3D8LightState(); + + // Enable a light + void EnableLight(uint32_t index, bool enable); +}; + +#endif From 3bfad0327f7a1bf906de94c7cf7a1930116bd3df Mon Sep 17 00:00:00 2001 From: Anthony Miles Date: Tue, 27 Oct 2020 22:07:12 +1300 Subject: [PATCH 03/21] Xbox fixed function vertex shader (not yet enabled) The shader should behave similarly to D3D9's fixed function pipeline But supports Xbox extensions and helps to move off D3D9 Co-authored with PatrickVL --- CMakeLists.txt | 3 + projects/cxbx/CMakeLists.txt | 11 + projects/cxbxr-emu/CMakeLists.txt | 11 + projects/misc/batch.cmake | 9 + src/core/hle/D3D8/Direct3D9/Direct3D9.cpp | 197 +++++- .../Direct3D9/FixedFunctionVertexShader.hlsl | 635 ++++++++++++++++++ .../FixedFunctionVertexShaderState.hlsli | 145 ++++ src/core/hle/D3D8/Direct3D9/VertexShader.cpp | 26 + src/core/hle/D3D8/Direct3D9/VertexShader.h | 3 + src/core/hle/D3D8/XbVertexShader.cpp | 17 + src/core/hle/D3D8/XbVertexShader.h | 3 + 11 files changed, 1058 insertions(+), 2 deletions(-) create mode 100644 src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShader.hlsl create mode 100644 src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShaderState.hlsli diff --git a/CMakeLists.txt b/CMakeLists.txt index 295102ab3..8ca950a86 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -117,7 +117,10 @@ file (GLOB CXBXR_HEADER_EMU "${CXBXR_ROOT_DIR}/src/common/util/gloffscreen/gloffscreen.h" "${CXBXR_ROOT_DIR}/src/common/XADPCM.h" "${CXBXR_ROOT_DIR}/src/common/xbox/Logging.hpp" + "${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/CxbxVertexShaderTemplate.hlsl" "${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/Direct3D9.h" + "${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShader.hlsl" + "${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShaderState.hlsli" "${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/VertexShader.h" "${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/VertexShaderSource.h" "${CXBXR_ROOT_DIR}/src/core/hle/D3D8/Direct3D9/WalkIndexBuffer.h" diff --git a/projects/cxbx/CMakeLists.txt b/projects/cxbx/CMakeLists.txt index 49c9b8884..731de24b4 100644 --- a/projects/cxbx/CMakeLists.txt +++ b/projects/cxbx/CMakeLists.txt @@ -194,3 +194,14 @@ if(${CMAKE_GENERATOR} MATCHES "Visual Studio ([^9]|[9][0-9])") endif() add_dependencies(cxbx cxbxr-ldr cxbxr-emu misc-batch) + +# Try to stop cmake from building hlsl files +# Which are all currently loaded at runtime only +set(HlslHeaders ${CXBXR_HEADER_EMU}) +list(FILTER HlslHeaders INCLUDE REGEX ".*\\.hlsl$") +set_source_files_properties( + ${HlslHeaders} + PROPERTIES + HEADER_FILE_ONLY TRUE + VS_TOOL_OVERRIDE "None" +) \ No newline at end of file diff --git a/projects/cxbxr-emu/CMakeLists.txt b/projects/cxbxr-emu/CMakeLists.txt index 569b0d4b2..06f66f0b0 100644 --- a/projects/cxbxr-emu/CMakeLists.txt +++ b/projects/cxbxr-emu/CMakeLists.txt @@ -166,3 +166,14 @@ install(TARGETS ${PROJECT_NAME} ) add_dependencies(cxbxr-emu cxbxr-ldr misc-batch) + +# Try to stop cmake from building hlsl files +# Which are all currently loaded at runtime only +set(HlslHeaders ${CXBXR_HEADER_EMU}) +list(FILTER HlslHeaders INCLUDE REGEX ".*\\.hlsl$") +set_source_files_properties( + ${HlslHeaders} + PROPERTIES + HEADER_FILE_ONLY TRUE + VS_TOOL_OVERRIDE "None" +) \ No newline at end of file diff --git a/projects/misc/batch.cmake b/projects/misc/batch.cmake index e3678c490..e29fafe0d 100644 --- a/projects/misc/batch.cmake +++ b/projects/misc/batch.cmake @@ -27,3 +27,12 @@ message("Runtime Build Directory: ${TargetRunTimeDir}") # Copy glew32.dll to build type's folder. set(CXBXR_GLEW_DLL "${CMAKE_SOURCE_DIR}/import/glew-2.0.0/bin/Release/Win32/glew32.dll") file(COPY ${CXBXR_GLEW_DLL} DESTINATION ${TargetRunTimeDir}) + +# Copy certain HLSL files to the output directory, which we will load at runtime +set(CXBXR_HLSL_FILES +"${CMAKE_SOURCE_DIR}/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShaderState.hlsli" +"${CMAKE_SOURCE_DIR}/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShader.hlsl" +) +set(HlslOutputDir ${TargetRunTimeDir}/hlsl) +file(MAKE_DIRECTORY ${HlslOutputDir}) +file(COPY ${CXBXR_HLSL_FILES} DESTINATION ${HlslOutputDir}) diff --git a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp index facb4944e..bc63cd09b 100644 --- a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp +++ b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp @@ -37,6 +37,7 @@ #include "core\kernel\init\CxbxKrnl.h" #include "core\kernel\support\Emu.h" #include "EmuShared.h" +#include "..\FixedFunctionState.h" #include "core\hle\D3D8\ResourceTracker.h" #include "core\hle\D3D8\Direct3D9\Direct3D9.h" // For LPDIRECTDRAWSURFACE7 #include "core\hle\D3D8\XbVertexBuffer.h" @@ -70,6 +71,9 @@ XboxRenderStateConverter XboxRenderStates; XboxTextureStateConverter XboxTextureStates; +D3D8LightState d3d8LightState = D3D8LightState(); +FixedFunctionVertexShaderState ffShaderState = {0}; // TODO find a home for this and associated code + // Allow use of time duration literals (making 16ms, etc possible) using namespace std::literals::chrono_literals; @@ -6263,6 +6267,179 @@ void CreateHostResource(xbox::X_D3DResource *pResource, DWORD D3DUsage, int iTex } // switch XboxResourceType } +D3DXVECTOR4 toVector(D3DCOLOR color) { + D3DXVECTOR4 v; + // ARGB to XYZW + v.w = (color >> 24 & 0xFF) / 255.f; + v.x = (color >> 16 & 0xFF) / 255.f; + v.y = (color >> 8 & 0xFF) / 255.f; + v.z = (color >> 0 & 0xFF) / 255.f; + return v; +} + +D3DXVECTOR4 toVector(D3DCOLORVALUE val) { + return D3DXVECTOR4(val.r, val.g, val.b, val.a); +} + +void UpdateFixedFunctionShaderLight(int d3dLightIndex, Light* pShaderLight, D3DXVECTOR4* pLightAmbient, D3DXMATRIX viewTransform) { + if (d3dLightIndex == -1) { + pShaderLight->Type = 0; // Disable the light + return; + } + + auto d3dLight = &d3d8LightState.Lights[d3dLightIndex]; + + // TODO remove D3DX usage + // Pre-transform light position to viewspace + D3DXVECTOR4 positionV; + D3DXVec3Transform(&positionV, (D3DXVECTOR3*)&d3dLight->Position, &viewTransform); + pShaderLight->PositionV = (D3DXVECTOR3)positionV; + + // Pre-transform light direction to viewspace and normalize + D3DXVECTOR4 directionV; + D3DXMATRIX viewTransform3x3; + D3DXMatrixIdentity(&viewTransform3x3); + for (int y = 0; y < 3; y++) { + for (int x = 0; x < 3; x++) { + viewTransform3x3.m[x][y] = viewTransform.m[x][y]; + } + } + + D3DXVec3Transform(&directionV, (D3DXVECTOR3*)&d3dLight->Direction, &viewTransform3x3); + D3DXVec3Normalize((D3DXVECTOR3*)&pShaderLight->DirectionVN, (D3DXVECTOR3*)&directionV); + + // Map D3D light to state struct + pShaderLight->Type = (float)((int)d3dLight->Type); + pShaderLight->Diffuse = toVector(d3dLight->Diffuse); + pShaderLight->Specular = toVector(d3dLight->Specular); + pShaderLight->Range = d3dLight->Range; + pShaderLight->Falloff = d3dLight->Falloff; + pShaderLight->Attenuation.x = d3dLight->Attenuation0; + pShaderLight->Attenuation.y = d3dLight->Attenuation1; + pShaderLight->Attenuation.z = d3dLight->Attenuation2; + + pLightAmbient->x += d3dLight->Ambient.r; + pLightAmbient->y += d3dLight->Ambient.g; + pLightAmbient->z += d3dLight->Ambient.b; + + auto cosHalfPhi = cos(d3dLight->Phi / 2); + pShaderLight->CosHalfPhi = cosHalfPhi; + pShaderLight->SpotIntensityDivisor = cos(d3dLight->Theta / 2) - cos(d3dLight->Phi / 2); +} + +float AsFloat(uint32_t value) { + auto v = value; + return *(float*)&v; +} + +void UpdateFixedFunctionVertexShaderState() +{ + using namespace xbox; + + // Lighting + ffShaderState.Modes.Lighting = (float)XboxRenderStates.GetXboxRenderState(X_D3DRS_LIGHTING); + ffShaderState.Modes.TwoSidedLighting = (float)XboxRenderStates.GetXboxRenderState(X_D3DRS_TWOSIDEDLIGHTING); + ffShaderState.Modes.SpecularEnable = (float)XboxRenderStates.GetXboxRenderState(X_D3DRS_SPECULARENABLE); + ffShaderState.Modes.LocalViewer = (float)XboxRenderStates.GetXboxRenderState(X_D3DRS_LOCALVIEWER); + ffShaderState.Modes.ColorVertex = (float)XboxRenderStates.GetXboxRenderState(X_D3DRS_COLORVERTEX); + + D3DXVECTOR4 Ambient = toVector(XboxRenderStates.GetXboxRenderState(X_D3DRS_AMBIENT)); + D3DXVECTOR4 BackAmbient = toVector(XboxRenderStates.GetXboxRenderState(X_D3DRS_BACKAMBIENT)); + + // Material sources + ffShaderState.Modes.AmbientMaterialSource = (float)XboxRenderStates.GetXboxRenderState(X_D3DRS_AMBIENTMATERIALSOURCE); + ffShaderState.Modes.DiffuseMaterialSource = (float)XboxRenderStates.GetXboxRenderState(X_D3DRS_DIFFUSEMATERIALSOURCE); + ffShaderState.Modes.SpecularMaterialSource = (float)XboxRenderStates.GetXboxRenderState(X_D3DRS_SPECULARMATERIALSOURCE); + ffShaderState.Modes.EmissiveMaterialSource = (float)XboxRenderStates.GetXboxRenderState(X_D3DRS_EMISSIVEMATERIALSOURCE); + ffShaderState.Modes.BackAmbientMaterialSource = (float)XboxRenderStates.GetXboxRenderState(X_D3DRS_BACKAMBIENTMATERIALSOURCE); + ffShaderState.Modes.BackDiffuseMaterialSource = (float)XboxRenderStates.GetXboxRenderState(X_D3DRS_BACKDIFFUSEMATERIALSOURCE); + ffShaderState.Modes.BackSpecularMaterialSource = (float)XboxRenderStates.GetXboxRenderState(X_D3DRS_BACKSPECULARMATERIALSOURCE); + ffShaderState.Modes.BackEmissiveMaterialSource = (float)XboxRenderStates.GetXboxRenderState(X_D3DRS_BACKEMISSIVEMATERIALSOURCE); + + // Point sprites + auto pointSize = XboxRenderStates.GetXboxRenderState(X_D3DRS_POINTSIZE); + ffShaderState.PointSprite.PointSize = *reinterpret_cast(&pointSize); + ffShaderState.PointSprite.PointScaleEnable = (float)XboxRenderStates.GetXboxRenderState(X_D3DRS_POINTSCALEENABLE); + ffShaderState.PointSprite.RenderTargetHeight = GetPixelContainerHeight(g_pXbox_RenderTarget); + auto scaleA = XboxRenderStates.GetXboxRenderState(X_D3DRS_POINTSCALE_A); + ffShaderState.PointSprite.ScaleA = *reinterpret_cast(&scaleA); + auto scaleB = XboxRenderStates.GetXboxRenderState(X_D3DRS_POINTSCALE_B); + ffShaderState.PointSprite.ScaleB = *reinterpret_cast(&scaleB); + auto scaleC = XboxRenderStates.GetXboxRenderState(X_D3DRS_POINTSCALE_C); + ffShaderState.PointSprite.ScaleC = *reinterpret_cast(&scaleC); + + // Fog + // Determine how fog depth is calculated + if (XboxRenderStates.GetXboxRenderState(X_D3DRS_FOGENABLE) && + XboxRenderStates.GetXboxRenderState(X_D3DRS_FOGTABLEMODE) != D3DFOG_NONE) { + auto proj = &ffShaderState.Transforms.Projection; + + if (XboxRenderStates.GetXboxRenderState(X_D3DRS_RANGEFOGENABLE)) { + LOG_TEST_CASE("Using RANGE fog"); + ffShaderState.Fog.DepthMode = FixedFunctionVertexShader::FOG_DEPTH_RANGE; + } + else if (proj->_14 == 0 && + proj->_24 == 0 && + proj->_34 == 0 && + proj->_44 == 1) { + LOG_TEST_CASE("Using Z fog"); + ffShaderState.Fog.DepthMode = FixedFunctionVertexShader::FOG_DEPTH_Z; + } + else { + // Test case: + // Fog sample + // JSRF (non-compliant projection matrix) + ffShaderState.Fog.DepthMode = FixedFunctionVertexShader::FOG_DEPTH_W; + } + } + else { + ffShaderState.Fog.DepthMode = FixedFunctionVertexShader::FOG_DEPTH_NONE; + } + + // Texture state + for (int i = 0; i < 4; i++) { + auto transformFlags = XboxTextureStates.Get(i, X_D3DTSS_TEXTURETRANSFORMFLAGS); + ffShaderState.TextureStates[i].TextureTransformFlagsCount = (float)(transformFlags & ~D3DTTFF_PROJECTED); + ffShaderState.TextureStates[i].TextureTransformFlagsProjected = (float)(transformFlags & D3DTTFF_PROJECTED); + + auto texCoordIndex = XboxTextureStates.Get(i, X_D3DTSS_TEXCOORDINDEX); + ffShaderState.TextureStates[i].TexCoordIndex = (float)(texCoordIndex & 0x7); // 8 coords + ffShaderState.TextureStates[i].TexCoordIndexGen = (float)(texCoordIndex >> 16); // D3DTSS_TCI flags + } + + // TexCoord component counts + extern xbox::X_VERTEXATTRIBUTEFORMAT* GetXboxVertexAttributeFormat(); // TMP glue + xbox::X_VERTEXATTRIBUTEFORMAT* pXboxVertexAttributeFormat = GetXboxVertexAttributeFormat(); + for (int i = 0; i < xbox::X_D3DTS_STAGECOUNT; i++) { + auto vertexDataFormat = pXboxVertexAttributeFormat->Slots[xbox::X_D3DVSDE_TEXCOORD0 + i].Format; + ffShaderState.TexCoordComponentCount[i] = (float)GetXboxVertexDataComponentCount(vertexDataFormat); + } + + // Misc flags + ffShaderState.Modes.VertexBlend = (float)XboxRenderStates.GetXboxRenderState(X_D3DRS_VERTEXBLEND); + ffShaderState.Modes.NormalizeNormals = (float)XboxRenderStates.GetXboxRenderState(X_D3DRS_NORMALIZENORMALS); + + // Update lights + auto LightAmbient = D3DXVECTOR4(0.f, 0.f, 0.f, 0.f); + D3DXMATRIX rowMajorViewTransform; + D3DXMatrixTranspose(&rowMajorViewTransform, (D3DXMATRIX*)&ffShaderState.Transforms.View); + for (size_t i = 0; i < ffShaderState.Lights.size(); i++) { + UpdateFixedFunctionShaderLight(d3d8LightState.EnabledLights[i], &ffShaderState.Lights[i], &LightAmbient, rowMajorViewTransform); + } + + ffShaderState.AmbientPlusLightAmbient = Ambient + LightAmbient; + ffShaderState.BackAmbientPlusLightAmbient = BackAmbient + LightAmbient; + + // Write fixed function state to shader constants + const int slotSize = 16; + const int fixedFunctionStateSize = (sizeof(FixedFunctionVertexShaderState) + slotSize - 1) / slotSize; + auto hRet = g_pD3DDevice->SetVertexShaderConstantF(0, (float*)&ffShaderState, fixedFunctionStateSize); + + if (FAILED(hRet)) { + CxbxKrnlCleanup("Failed to write fixed-function HLSL state"); + } +} + // ****************************************************************** // * patch: D3DDevice_EnableOverlay // ****************************************************************** @@ -6405,9 +6582,15 @@ void CxbxImpl_SetTransform { LOG_INIT - State = EmuXB2PC_D3DTS(State); + // Transpose row major to column major for HLSL + D3DXMATRIX hlslMatrix; + D3DXMatrixTranspose(&hlslMatrix, (D3DXMATRIX*)pMatrix); + // Save to vertex shader state + ((D3DXMATRIX*)&ffShaderState.Transforms)[State] = hlslMatrix; - HRESULT hRet = g_pD3DDevice->SetTransform(State, pMatrix); + auto d3d9State = EmuXB2PC_D3DTS(State); + + HRESULT hRet = g_pD3DDevice->SetTransform(d3d9State, pMatrix); DEBUG_D3DRESULT(hRet, "g_pD3DDevice->SetTransform"); } @@ -7897,6 +8080,8 @@ xbox::hresult_xt WINAPI xbox::EMUPATCH(D3DDevice_SetLight) XB_TRMP(D3DDevice_SetLight)(Index, pLight); + d3d8LightState.Lights[Index] = *pLight; + HRESULT hRet = g_pD3DDevice->SetLight(Index, pLight); DEBUG_D3DRESULT(hRet, "g_pD3DDevice->SetLight"); @@ -7913,6 +8098,12 @@ xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_SetMaterial) { LOG_FUNC_ONE_ARG(pMaterial); + ffShaderState.Materials[0].Ambient = toVector(pMaterial->Ambient); + ffShaderState.Materials[0].Diffuse = toVector(pMaterial->Diffuse); + ffShaderState.Materials[0].Specular = toVector(pMaterial->Specular); + ffShaderState.Materials[0].Emissive = toVector(pMaterial->Emissive); + ffShaderState.Materials[0].Power = pMaterial->Power; + HRESULT hRet = g_pD3DDevice->SetMaterial(pMaterial); DEBUG_D3DRESULT(hRet, "g_pD3DDevice->SetMaterial"); } @@ -7933,6 +8124,8 @@ xbox::hresult_xt WINAPI xbox::EMUPATCH(D3DDevice_LightEnable) XB_TRMP(D3DDevice_LightEnable)(Index, bEnable); + d3d8LightState.EnableLight(Index, bEnable); + HRESULT hRet = g_pD3DDevice->LightEnable(Index, bEnable); DEBUG_D3DRESULT(hRet, "g_pD3DDevice->LightEnable"); diff --git a/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShader.hlsl b/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShader.hlsl new file mode 100644 index 000000000..a5c848610 --- /dev/null +++ b/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShader.hlsl @@ -0,0 +1,635 @@ +#include "FixedFunctionVertexShaderState.hlsli" + +// Default values for vertex registers, and whether to use them +uniform float4 vRegisterDefaultValues[16] : register(c192); +uniform float4 vRegisterDefaultFlagsPacked[4] : register(c208); +static bool vRegisterDefaultFlags[16]; + +uniform FixedFunctionVertexShaderState state : register(c0); + +uniform float4 xboxTextureScale[4] : register(c214); + +#undef CXBX_ALL_TEXCOORD_INPUTS // Enable this to disable semantics in VS_INPUT (instead, we'll use an array of generic TEXCOORD's) + +// Input registers +struct VS_INPUT +{ +#ifdef CXBX_ALL_TEXCOORD_INPUTS + float4 v[16] : TEXCOORD; +#else + float4 pos : POSITION; + float4 bw : BLENDWEIGHT; + float4 color[2] : COLOR; + float4 backColor[2] : TEXCOORD4; + float4 normal : NORMAL; + float4 texcoord[4] : TEXCOORD; +#endif +}; + +// Input register indices (also known as attributes, as given in VS_INPUT.v array) +// TODO : Convert FVF codes on CPU to a vertex declaration with these standardized register indices: +// NOTE : Converting FVF vertex indices must also consider NV2A vertex attribute 'slot mapping', +// as set in NV2A_VTXFMT/NV097_SET_VERTEX_DATA_ARRAY_FORMAT! +// TODO : Rename these into SLOT_POSITION, SLOT_WEIGHT, SLOT_TEXTURE0, SLOT_TEXTURE3, etc : +static const uint position = 0; // See X_D3DFVF_XYZ / X_D3DVSDE_POSITION was float4 pos : POSITION; +static const uint weight = 1; // See X_D3DFVF_XYZB1-4 / X_D3DVSDE_BLENDWEIGHT was float4 bw : BLENDWEIGHT; +static const uint normal = 2; // See X_D3DFVF_NORMAL / X_D3DVSDE_NORMAL was float4 normal : NORMAL; // Note : Only normal.xyz is used. +static const uint diffuse = 3; // See X_D3DFVF_DIFFUSE / X_D3DVSDE_DIFFUSE was float4 color[2] : COLOR; +static const uint specular = 4; // See X_D3DFVF_SPECULAR / X_D3DVSDE_SPECULAR +static const uint fogCoord = 5; // Has no X_D3DFVF_* ! See X_D3DVSDE_FOG Note : Only fog.x is used. +static const uint pointSize = 6; // Has no X_D3DFVF_* ! See X_D3DVSDE_POINTSIZE +static const uint backDiffuse = 7; // Has no X_D3DFVF_* ! See X_D3DVSDE_BACKDIFFUSE was float4 backColor[2] : TEXCOORD4; +static const uint backSpecular = 8; // Has no X_D3DFVF_* ! See X_D3DVSDE_BACKSPECULAR +static const uint texcoord0 = 9; // See X_D3DFVF_TEX1 / X_D3DVSDE_TEXCOORD0 was float4 texcoord[4] : TEXCOORD; +static const uint texcoord1 = 10; // See X_D3DFVF_TEX2 / X_D3DVSDE_TEXCOORD1 +static const uint texcoord2 = 11; // See X_D3DFVF_TEX3 / X_D3DVSDE_TEXCOORD2 +static const uint texcoord3 = 12; // See X_D3DFVF_TEX4 / X_D3DVSDE_TEXCOORD3 +static const uint reserved0 = 13; // Has no X_D3DFVF_* / X_D3DVSDE_* +static const uint reserved1 = 14; // Has no X_D3DFVF_* / X_D3DVSDE_* +static const uint reserved2 = 15; // Has no X_D3DFVF_* / X_D3DVSDE_* + +float4 Get(const VS_INPUT xIn, const uint index) +{ +#ifdef CXBX_ALL_TEXCOORD_INPUTS + return xIn.v[index]; +#else + // switch statements inexplicably don't work here + if(index == position) return xIn.pos; + if(index == weight) return xIn.bw; + if(index == normal) return xIn.normal; + if(index == diffuse) return xIn.color[0]; + if(index == specular) return xIn.color[1]; + if(index == backDiffuse) return xIn.backColor[0]; + if(index == backSpecular) return xIn.backColor[1]; + if(index == texcoord0) return xIn.texcoord[0]; + if(index == texcoord1) return xIn.texcoord[1]; + if(index == texcoord2) return xIn.texcoord[2]; + if(index == texcoord3) return xIn.texcoord[3]; + return 1; +#endif +} + +// Output registers +struct VS_OUTPUT +{ + float4 oPos : POSITION; // Homogeneous clip space position + float4 oD0 : COLOR0; // Primary color (front-facing) + float4 oD1 : COLOR1; // Secondary color (front-facing) + float oFog : FOG; // Fog coordinate + float oPts : PSIZE; // Point size + float4 oB0 : TEXCOORD4; // Back-facing primary color + float4 oB1 : TEXCOORD5; // Back-facing secondary color + float4 oT0 : TEXCOORD0; // Texture coordinate set 0 + float4 oT1 : TEXCOORD1; // Texture coordinate set 1 + float4 oT2 : TEXCOORD2; // Texture coordinate set 2 + float4 oT3 : TEXCOORD3; // Texture coordinate set 3 +}; + +struct TransformInfo +{ + float4 Position; + float3 Normal; +}; + +static TransformInfo World; // Vertex worldspace transform +static TransformInfo View; // Vertex viewspace/cameraspace transform +static TransformInfo Projection; // Vertex projection transform + +// Vertex lighting +// Both frontface and backface lighting can be calculated +struct LightingInfo +{ + float3 Front; + float3 Back; +}; + +// Final lighting output +struct LightingOutput +{ + LightingInfo Diffuse; + LightingInfo Specular; +}; + +LightingInfo DoSpecular(const float3 toLightVN, const float3 toViewerVN, const float2 powers, const float4 lightSpecular) +{ + LightingInfo o; + o.Front = o.Back = float3(0, 0, 0); + + // Specular + if (state.Modes.SpecularEnable) + { + // Blinn-Phong + // https://learnopengl.com/Advanced-Lighting/Advanced-Lighting + float3 halfway = normalize(toViewerVN + toLightVN); + float NdotH = dot(View.Normal, halfway); + + float3 frontSpecular = pow(abs(NdotH), powers[0]) * lightSpecular.rgb; + float3 backSpecular = pow(abs(NdotH), powers[1]) * lightSpecular.rgb; + + if (NdotH >= 0) + o.Front = frontSpecular; + else + o.Back = backSpecular; + } + + return o; +} + +// useful reference https://drivers.amd.com/misc/samples/dx9/FixedFuncShader.pdf + +LightingOutput DoPointLight(const Light l, const float3 toViewerVN, const float2 powers) +{ + LightingOutput o; + o.Diffuse.Front = o.Diffuse.Back = float3(0, 0, 0); + o.Specular.Front = o.Specular.Back = float3(0, 0, 0); + + // Diffuse + float3 toLightV = l.PositionV - View.Position.xyz; + float lightDist = length(toLightV); + float3 toLightVN = normalize(toLightV); + + // A(Constant) + A(Linear) * dist + A(Exp) * dist^2 + float attenuation = + 1 / (l.Attenuation[0] + + l.Attenuation[1] * lightDist + + l.Attenuation[2] * lightDist * lightDist); + + // Range cutoff + if (lightDist > l.Range) + attenuation = 0; + + float NdotL = dot(View.Normal, toLightVN); + float3 lightDiffuse = abs(NdotL) * attenuation * l.Diffuse.rgb; + + if (NdotL >= 0) + o.Diffuse.Front = lightDiffuse; + else + o.Diffuse.Back = lightDiffuse; + + // Specular + o.Specular = DoSpecular(toLightVN, toViewerVN, powers, l.Specular); + o.Specular.Front *= attenuation; + o.Specular.Back *= attenuation; + + return o; +} + +LightingOutput DoSpotLight(const Light l, const float3 toViewerVN, const float2 powers) +{ + LightingOutput o; + o.Diffuse.Front = o.Diffuse.Back = float3(0, 0, 0); + o.Specular.Front = o.Specular.Back = float3(0, 0, 0); + + // Diffuse + float3 toLightV = l.PositionV - View.Position.xyz; + float lightDist = length(toLightV); + float3 toLightVN = normalize(toLightV); + float3 toVertexVN = -toLightVN; + + // https://docs.microsoft.com/en-us/windows/win32/direct3d9/light-types + float cosAlpha = dot(l.DirectionVN, toVertexVN); + // I = ( cos(a) - cos(phi/2) ) / ( cos(theta/2) - cos(phi/2) ) + float spotBase = saturate((cosAlpha - l.CosHalfPhi) / l.SpotIntensityDivisor); + float spotIntensity = pow(spotBase, l.Falloff); + + // A(Constant) + A(Linear) * dist + A(Exp) * dist^2 + float attenuation = + 1 / (l.Attenuation[0] + + l.Attenuation[1] * lightDist + + l.Attenuation[2] * lightDist * lightDist); + + // Range cutoff + if (lightDist > l.Range) + attenuation = 0; + + float NdotL = dot(View.Normal, toLightVN); + float3 lightDiffuse = abs(NdotL) * attenuation * l.Diffuse.rgb * spotIntensity; + + if (NdotL >= 0) + o.Diffuse.Front = lightDiffuse; + else + o.Diffuse.Back = lightDiffuse; + + // Specular + o.Specular = DoSpecular(toLightVN, toViewerVN, powers, l.Specular); + o.Specular.Front *= attenuation; + o.Specular.Back *= attenuation; + + return o; +} + +LightingOutput DoDirectionalLight(const Light l, const float3 toViewerVN, const float2 powers) +{ + LightingOutput o; + o.Diffuse.Front = o.Diffuse.Back = float3(0, 0, 0); + o.Specular.Front = o.Specular.Back = float3(0, 0, 0); + + // Diffuse + + // Intensity from N . L + float3 toLightVN = -l.DirectionVN; + float NdotL = dot(View.Normal, toLightVN); + float3 lightDiffuse = abs(NdotL * l.Diffuse.rgb); + + // Apply light contribution to front or back face + // as the case may be + if (NdotL >= 0) + o.Diffuse.Front = lightDiffuse; + else + o.Diffuse.Back = lightDiffuse; + + // Specular + o.Specular = DoSpecular(toLightVN, toViewerVN, powers, l.Specular); + + return o; +} + + +LightingOutput CalcLighting(const float2 powers) +{ + const int LIGHT_TYPE_NONE = 0; + const int LIGHT_TYPE_POINT = 1; + const int LIGHT_TYPE_SPOT = 2; + const int LIGHT_TYPE_DIRECTIONAL = 3; + + LightingOutput totalLightOutput; + totalLightOutput.Diffuse.Front = float3(0, 0, 0); + totalLightOutput.Diffuse.Back = float3(0, 0, 0); + totalLightOutput.Specular.Front = float3(0, 0, 0); + totalLightOutput.Specular.Back = float3(0, 0, 0); + + float3 toViewerVN = state.Modes.LocalViewer + ? float3(0, 0, 1) + : normalize(-View.Position.xyz); + + for (uint i = 0; i < 8; i++) + { + const Light currentLight = state.Lights[i]; + LightingOutput currentLightOutput; + + if(currentLight.Type == LIGHT_TYPE_POINT) + currentLightOutput = DoPointLight(currentLight, toViewerVN, powers); + else if(currentLight.Type == LIGHT_TYPE_SPOT) + currentLightOutput = DoSpotLight(currentLight, toViewerVN, powers); + else if (currentLight.Type == LIGHT_TYPE_DIRECTIONAL) + currentLightOutput = DoDirectionalLight(currentLight, toViewerVN, powers); + else + continue; + + totalLightOutput.Diffuse.Front += currentLightOutput.Diffuse.Front; + totalLightOutput.Diffuse.Back += currentLightOutput.Diffuse.Back; + totalLightOutput.Specular.Front += currentLightOutput.Specular.Front; + totalLightOutput.Specular.Back += currentLightOutput.Specular.Back; + } + + return totalLightOutput; +} + +TransformInfo DoWorldTransform(const float4 position, const float3 normal, const float4 blendWeights) +{ + TransformInfo output; + output.Position = float4(0, 0, 0, 0); + output.Normal = float3(0, 0, 0); + + // D3D + const int _BLEND_OFF = 0; + const int _1WEIGHT_2MAT = 1; + const int _2WEIGHT_3MAT = 3; + const int _3WEIGHT_4MAT = 5; + // Xbox + const int _2WEIGHT_2MAT = 2; + const int _3WEIGHT_3MAT = 4; + const int _4WEIGHT_4MAT = 6; + + if (state.Modes.VertexBlend == _BLEND_OFF) { + output.Position = mul(position, state.Transforms.World[0]); + output.Normal = mul(normal, (float3x3)state.Transforms.World[0]); + return output; + } + + // The number of matrices to blend + int mats = floor((state.Modes.VertexBlend - 1) / 2 + 2); + // If we have to calculate the last blend value + bool calcLastBlend = fmod(state.Modes.VertexBlend, 2) == 1; + + float lastBlend = 1; + for (int i = 0; i < mats - 1; i++) + { + output.Position += mul(position, state.Transforms.World[i]) * blendWeights[i]; + output.Normal += mul(normal, (float3x3) state.Transforms.World[i]) * blendWeights[i]; + lastBlend -= blendWeights[i]; + } + + if (calcLastBlend) + { + output.Position += mul(position, state.Transforms.World[mats-1]) * lastBlend; + output.Normal += mul(normal, (float3x3) state.Transforms.World[mats-1]) * lastBlend; + } + else + { + output.Position += mul(position, state.Transforms.World[mats-1]) * blendWeights[mats-1]; + output.Normal += mul(normal, (float3x3) state.Transforms.World[mats-1]) * blendWeights[mats-1]; + } + + return output; +} + +Material DoMaterial(const uint index, const uint diffuseReg, const uint specularReg, const VS_INPUT xIn) +{ + // Get the material from material state + Material material = state.Materials[index]; + + if (state.Modes.ColorVertex) + { + // https://docs.microsoft.com/en-us/windows/win32/direct3d9/d3dmaterialcolorsource + const int D3DMCS_MATERIAL = 0; + const int D3DMCS_COLOR1 = 1; + const int D3DMCS_COLOR2 = 2; + + // TODO preprocess on the CPU + // If COLORVERTEX mode, AND the desired diffuse or specular colour is defined in the vertex declaration + // Then use the vertex colour instead of the material + + if (!vRegisterDefaultFlags[diffuseReg]) { + float4 diffuseVertexColour = Get(xIn, diffuseReg); + if (state.Modes.AmbientMaterialSource == D3DMCS_COLOR1) material.Ambient = diffuseVertexColour; + if (state.Modes.DiffuseMaterialSource == D3DMCS_COLOR1) material.Diffuse = diffuseVertexColour; + if (state.Modes.SpecularMaterialSource == D3DMCS_COLOR1) material.Specular = diffuseVertexColour; + if (state.Modes.EmissiveMaterialSource == D3DMCS_COLOR1) material.Emissive = diffuseVertexColour; + } + + if (!vRegisterDefaultFlags[specularReg]) { + float4 specularVertexColour = Get(xIn, specularReg); + if (state.Modes.AmbientMaterialSource == D3DMCS_COLOR2) material.Ambient = specularVertexColour; + if (state.Modes.DiffuseMaterialSource == D3DMCS_COLOR2) material.Diffuse = specularVertexColour; + if (state.Modes.SpecularMaterialSource == D3DMCS_COLOR2) material.Specular = specularVertexColour; + if (state.Modes.EmissiveMaterialSource == D3DMCS_COLOR2) material.Emissive = specularVertexColour; + } + } + + return material; +} + +float DoFog(VS_INPUT xIn) +{ + // TODO implement properly + // Until we have pixel shader HLSL we are still leaning on D3D renderstates for fogging + // So we are not doing any fog density calculations here + // http://developer.download.nvidia.com/assets/gamedev/docs/Fog2.pdf + + float fogDepth; + + if (state.Fog.DepthMode == FixedFunctionVertexShader::FOG_DEPTH_NONE) + fogDepth = xIn.color[1].a; // In fixed-function mode, fog is passed in the specular alpha + if (state.Fog.DepthMode == FixedFunctionVertexShader::FOG_DEPTH_RANGE) + fogDepth = length(View.Position.xyz); + if (state.Fog.DepthMode == FixedFunctionVertexShader::FOG_DEPTH_Z) + fogDepth = abs(Projection.Position.z); + if (state.Fog.DepthMode == FixedFunctionVertexShader::FOG_DEPTH_W) + fogDepth = Projection.Position.w; + + return fogDepth; +} + +float4 DoTexCoord(const uint stage, const VS_INPUT xIn) +{ + // Texture transform flags + // https://docs.microsoft.com/en-gb/windows/win32/direct3d9/d3dtexturetransformflags + const int D3DTTFF_DISABLE = 0; + const int D3DTTFF_COUNT1 = 1; + const int D3DTTFF_COUNT2 = 2; + const int D3DTTFF_COUNT3 = 3; + const int D3DTTFF_COUNT4 = 4; + const int D3DTTFF_PROJECTED = 256; // This is the only real flag + + // https://docs.microsoft.com/en-us/windows/win32/direct3d9/d3dtss-tci + // Pre-shifted + const int TCI_PASSTHRU = 0; + const int TCI_CAMERASPACENORMAL = 1; + const int TCI_CAMERASPACEPOSITION = 2; + const int TCI_CAMERASPACEREFLECTIONVECTOR = 3; + const int TCI_OBJECT = 4; // Xbox + const int TCI_SPHERE = 5; // Xbox + + const TextureState tState = state.TextureStates[stage]; + + // Extract transform flags + int countFlag = tState.TextureTransformFlagsCount; + bool projected = tState.TextureTransformFlagsProjected; + + // Get texture coordinates + // Coordinates are either from the vertex texcoord data + // Or generated + float4 texCoord = float4(0, 0, 0, 0); + if (tState.TexCoordIndexGen == TCI_PASSTHRU) + { + // Get from vertex data + uint texCoordIndex = abs(tState.TexCoordIndex); // Note : abs() avoids error X3548 : in vs_3_0 uints can only be used with known - positive values, use int if possible + texCoord = Get(xIn, texcoord0+texCoordIndex); + + // Make coordinates homogenous + // For example, if a title supplies (u, v) + // We need to make transform (u, v, 1) to allow translation with a 3x3 matrix + // We'll need to get this from the current FVF or VertexDeclaration + // Test case: JSRF scrolling texture effect. + // Test case: Madagascar shadows + // Test case: Modify pixel shader sample + + // TODO move alongside the texture transformation when it stops angering the HLSL compiler + float componentCount = state.TexCoordComponentCount[texCoordIndex]; + if (componentCount == 1) + texCoord.yzw = float3(1, 0, 0); + if (componentCount == 2) + texCoord.zw = float2(1, 0); + if (componentCount == 3) + texCoord.w = 1; + } + else + { + // Generate texture coordinates + float3 reflected = reflect(normalize(View.Position.xyz), View.Normal); + + if (tState.TexCoordIndexGen == TCI_CAMERASPACENORMAL) + texCoord = float4(View.Normal, 1); + else if (tState.TexCoordIndexGen == TCI_CAMERASPACEPOSITION) + texCoord = View.Position; + else if (tState.TexCoordIndexGen == TCI_CAMERASPACEREFLECTIONVECTOR) + texCoord.xyz = reflected; + // else if TCI_OBJECT TODO is this just model position? + else if (tState.TexCoordIndexGen == TCI_SPHERE) + { + // TODO verify + // http://www.bluevoid.com/opengl/sig99/advanced99/notes/node177.html + float3 R = reflected; + float p = sqrt(pow(R.x, 2) + pow(R.y, 2) + pow(R.z + 1, 2)); + texCoord.x = R.x / 2 * p + 0.5f; + texCoord.y = R.y / 2 * p + 0.5f; + } + } + + // Transform the texture coordinates if requested + if (countFlag != D3DTTFF_DISABLE) + texCoord = mul(texCoord, state.Transforms.Texture[stage]); + + // We always send four coordinates + // If we are supposed to send less than four + // then copy the last coordinate to the remaining coordinates + // For D3DTTFF_PROJECTED, the value of the *last* coordinate is important + // Test case: ProjectedTexture sample, which uses 3 coordinates + // We'll need to implement the divide when D3D stops handling it for us? + // https://docs.microsoft.com/en-us/windows/win32/direct3d9/d3dtexturetransformflags + if (projected) + { + if (countFlag == 1) + texCoord.yzw = texCoord.x; + if (countFlag == 2) + texCoord.zw = texCoord.y; + if (countFlag == 3) + texCoord.w = texCoord.z; + } + + return texCoord; +} + +// Point size for Point Sprites +// https://docs.microsoft.com/en-us/windows/win32/direct3d9/point-sprites +// Test case: Point sprite sample +float DoPointSpriteSize() +{ + PointSprite ps = state.PointSprite; + float pointSize = ps.PointSize; + if (ps.PointScaleEnable) + { + float eyeDistance = length(View.Position); + float factor = ps.ScaleA + ps.ScaleB * eyeDistance + ps.ScaleC * (eyeDistance * eyeDistance); + pointSize *= ps.RenderTargetHeight * sqrt(1 / factor); + } + + return pointSize; +} + +VS_INPUT InitializeInputRegisters(const VS_INPUT xInput) +{ + VS_INPUT xIn; + + // Initialize input registers from the vertex buffer data + // Or use the register's default value (which can be changed by the title) + for (uint i = 0; i < 16; i++) { + float4 value = lerp(Get(xInput, i), vRegisterDefaultValues[i], vRegisterDefaultFlags[i]); + #ifdef CXBX_ALL_TEXCOORD_INPUTS + xIn.v[i] = value; + #else + // switch statements inexplicably don't work here + if(i == position) xIn.pos = value; + if(i == weight) xIn.bw = value; + if(i == normal) xIn.normal = value; + if(i == diffuse) xIn.color[0] = value; + if(i == specular) xIn.color[1] = value; + if(i == backDiffuse) xIn.backColor[0] = value; + if(i == backSpecular) xIn.backColor[1] = value; + if(i == texcoord0) xIn.texcoord[0] = value; + if(i == texcoord1) xIn.texcoord[1] = value; + if(i == texcoord2) xIn.texcoord[2] = value; + if(i == texcoord3) xIn.texcoord[3] = value; + #endif + } + + return xIn; +} + +VS_OUTPUT main(const VS_INPUT xInput) +{ + VS_OUTPUT xOut; + + // Unpack 16 bool flags from 4 float4 constant registers + vRegisterDefaultFlags = (bool[16]) vRegisterDefaultFlagsPacked; + + // TODO make sure this goes fast + + // Map default values + VS_INPUT xIn = InitializeInputRegisters(xInput); + + // World transform with vertex blending + World = DoWorldTransform(Get(xIn, position), Get(xIn, normal).xyz, Get(xIn, weight)); + + // View transform + View.Position = mul(World.Position, state.Transforms.View); + View.Normal = mul(World.Normal, (float3x3) state.Transforms.View); + + // Optionally normalize camera-space normals + if (state.Modes.NormalizeNormals) + View.Normal = normalize(View.Normal); + + // Projection transform + Projection.Position = mul(View.Position, state.Transforms.Projection); + // Normal unused... + + // Projection transform - final position + xOut.oPos = Projection.Position; + + // Vertex lighting + if (state.Modes.Lighting || state.Modes.TwoSidedLighting) + { + // Materials + Material material = DoMaterial(0, diffuse, specular, xIn); + Material backMaterial = DoMaterial(1, backDiffuse, backSpecular, xIn); + + float2 powers = float2(material.Power, backMaterial.Power); + + LightingOutput lighting = CalcLighting(powers); + + // Compute each lighting component + float3 ambient = material.Ambient.rgb * state.AmbientPlusLightAmbient.rgb; + float3 backAmbient = backMaterial.Ambient.rgb * state.BackAmbientPlusLightAmbient.rgb; + + float3 diffuse = material.Diffuse.rgb * lighting.Diffuse.Front; + float3 backDiffuse = backMaterial.Diffuse.rgb * lighting.Diffuse.Back; + + float3 specular = material.Specular.rgb * lighting.Specular.Front; + float3 backSpecular = backMaterial.Specular.rgb * lighting.Specular.Back; + + float3 emissive = material.Emissive.rgb; + float3 backEmissive = backMaterial.Emissive.rgb; + + // Frontface + xOut.oD0 = float4(ambient + diffuse + emissive, material.Diffuse.a); + xOut.oD1 = float4(specular, 0); + // Backface + xOut.oB0 = float4(backAmbient + backDiffuse + backEmissive, backMaterial.Diffuse.a); + xOut.oB1 = float4(backSpecular, 0); + } + + // TODO does TwoSidedLighting imply Lighting? Verify if TwoSidedLighting can be enabled independently of Lighting + // Diffuse and specular for when lighting is disabled + if (!state.Modes.Lighting) + { + xOut.oD0 = Get(xIn, diffuse); + xOut.oD1 = Get(xIn, specular); + } + + if(!state.Modes.TwoSidedLighting) + { + xOut.oB0 = Get(xIn, backDiffuse); + xOut.oB1 = Get(xIn, backSpecular); + } + + // Colour + xOut.oD0 = saturate(xOut.oD0); + xOut.oD1 = saturate(xOut.oD1); + xOut.oB0 = saturate(xOut.oB0); + xOut.oB1 = saturate(xOut.oB1); + + // Fog + xOut.oFog = DoFog(xIn); + + // Point Sprite + xOut.oPts = DoPointSpriteSize(); + + // Texture coordinates + xOut.oT0 = DoTexCoord(0, xIn) / xboxTextureScale[0];; + xOut.oT1 = DoTexCoord(1, xIn) / xboxTextureScale[1];; + xOut.oT2 = DoTexCoord(2, xIn) / xboxTextureScale[2];; + xOut.oT3 = DoTexCoord(3, xIn) / xboxTextureScale[3];; + + return xOut; +} diff --git a/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShaderState.hlsli b/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShaderState.hlsli new file mode 100644 index 000000000..b684a04e9 --- /dev/null +++ b/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShaderState.hlsli @@ -0,0 +1,145 @@ +// C++ / HLSL shared state block for fixed function support +#ifdef __cplusplus +#pragma once + +#include +#include // for D3DFORMAT, D3DLIGHT9, etc +#include // for D3DXVECTOR4, etc +#include + +#define float4x4 D3DMATRIX +#define float4 D3DXVECTOR4 +#define float3 D3DVECTOR +#define float2 D3DXVECTOR2 +#define arr(name, type, length) std::array name + +#else +// HLSL +#define arr(name, type, length) type name[length] +#define alignas(x) +#define const static +#endif // __cplusplus + +namespace FixedFunctionVertexShader { + // Fog depth is taken from the vertex, rather than generated + const float FOG_DEPTH_NONE = 0; + // Fog depth is the output Z coordinate + const float FOG_DEPTH_Z = 1; + // Fog depth is based on the output W coordinate (1 / W) + const float FOG_DEPTH_W = 2; + // Fog depth is based distance of the vertex from the eye position + const float FOG_DEPTH_RANGE = 3; +} + +// Shared HLSL structures +// Taking care with packing rules +// In VS_3_0, packing works in mysterious ways +// * Structs inside arrays are not packed +// * Floats can't be packed at all (?) +// We don't get documented packing until vs_4_0 + +struct Transforms { + float4x4 View; // 0 + float4x4 Projection; // 1 + arr(Texture, float4x4, 4); // 2, 3, 4, 5 + arr(World, float4x4, 4); // 6, 7, 8, 9 +}; + +// See D3DLIGHT +struct Light { + // TODO in vs_4_0+ when floats are packable + // Change colour values to float3 + // And put something useful in the alpha slot + float4 Diffuse; + float4 Specular; + + // Viewspace light position + alignas(16) float3 PositionV; + alignas(16) float Range; + + // Viewspace light direction (normalized) + alignas(16) float3 DirectionVN; + alignas(16) float Type; // 1=Point, 2=Spot, 3=Directional + + alignas(16) float3 Attenuation; + alignas(16) float Falloff; + + alignas(16) float CosHalfPhi; + // cos(theta/2) - cos(phi/2) + alignas(16) float SpotIntensityDivisor; +}; + +struct Material { + float4 Diffuse; + float4 Ambient; + float4 Specular; + float4 Emissive; + + alignas(16) float Power; +}; + +struct Modes { + alignas(16) float AmbientMaterialSource; + alignas(16) float DiffuseMaterialSource; + alignas(16) float SpecularMaterialSource; + alignas(16) float EmissiveMaterialSource; + + alignas(16) float BackAmbientMaterialSource; + alignas(16) float BackDiffuseMaterialSource; + alignas(16) float BackSpecularMaterialSource; + alignas(16) float BackEmissiveMaterialSource; + + alignas(16) float Lighting; + alignas(16) float TwoSidedLighting; + alignas(16) float SpecularEnable; + alignas(16) float LocalViewer; + + alignas(16) float ColorVertex; + alignas(16) float VertexBlend; + alignas(16) float NormalizeNormals; +}; + +struct PointSprite { + alignas(16) float PointSize; + alignas(16) float PointScaleEnable; + alignas(16) float RenderTargetHeight; + alignas(16) float ScaleA; + alignas(16) float ScaleB; + alignas(16) float ScaleC; +}; + +struct TextureState { + alignas(16) float TextureTransformFlagsCount; + alignas(16) float TextureTransformFlagsProjected; + alignas(16) float TexCoordIndex; + alignas(16) float TexCoordIndexGen; +}; + +struct Fog { + alignas(16) float DepthMode; +}; + +struct FixedFunctionVertexShaderState { + alignas(16) Transforms Transforms; + alignas(16) arr(Lights, Light, 8); + alignas(16) float4 AmbientPlusLightAmbient; + alignas(16) float4 BackAmbientPlusLightAmbient; + alignas(16) arr(Materials, Material, 2); + alignas(16) Modes Modes; + alignas(16) Fog Fog; + alignas(16) arr(TextureStates, TextureState, 4); + alignas(16) PointSprite PointSprite; + alignas(16) float4 TexCoordComponentCount; +}; + +#ifdef __cplusplus +#undef float4x4 +#undef float4 +#undef float3 +#undef float2 +#undef arr +#else // HLSL +#undef arr +#undef alignas +#undef const +#endif // __cplusplus diff --git a/src/core/hle/D3D8/Direct3D9/VertexShader.cpp b/src/core/hle/D3D8/Direct3D9/VertexShader.cpp index caaabef8d..10353ca5b 100644 --- a/src/core/hle/D3D8/Direct3D9/VertexShader.cpp +++ b/src/core/hle/D3D8/Direct3D9/VertexShader.cpp @@ -4,6 +4,7 @@ #include "core\kernel\init\CxbxKrnl.h" #include "core\kernel\support\Emu.h" +#include #include extern const char* g_vs_model = vs_model_3_0; @@ -304,6 +305,31 @@ extern HRESULT EmuCompileShader return CompileHlsl(hlsl_str, ppHostShader, "CxbxVertexShaderTemplate.hlsl"); } +extern void EmuCompileFixedFunction(ID3DBlob** ppHostShader) +{ + static ID3DBlob* pShader = nullptr; + + // TODO does this need to be thread safe? + if (pShader == nullptr) { + // Determine the filename and directory for the fixed function shader + auto hlslDir = std::filesystem::path(szFilePath_CxbxReloaded_Exe) + .parent_path() + .append("hlsl"); + + auto sourceFile = hlslDir.append("FixedFunctionVertexShader.hlsl").string(); + + // Load the shader into a string + std::ifstream hlslStream(sourceFile); + std::stringstream hlsl; + hlsl << hlslStream.rdbuf(); + + // Compile the shader + CompileHlsl(hlsl.str(), &pShader, sourceFile.c_str()); + } + + *ppHostShader = pShader; +}; + static ID3DBlob* pPassthroughShader = nullptr; extern HRESULT EmuCompileXboxPassthrough(ID3DBlob** ppHostShader) diff --git a/src/core/hle/D3D8/Direct3D9/VertexShader.h b/src/core/hle/D3D8/Direct3D9/VertexShader.h index e2f6ca24b..29d8cc57c 100644 --- a/src/core/hle/D3D8/Direct3D9/VertexShader.h +++ b/src/core/hle/D3D8/Direct3D9/VertexShader.h @@ -3,6 +3,7 @@ #define DIRECT3D9VERTEXSHADER_H #include "core\hle\D3D8\XbVertexShader.h" +#include "FixedFunctionVertexShaderState.hlsli" enum class ShaderType { Empty = 0, @@ -20,6 +21,8 @@ extern HRESULT EmuCompileShader ID3DBlob** ppHostShader ); +extern void EmuCompileFixedFunction(ID3DBlob** ppHostShader); + extern HRESULT EmuCompileXboxPassthrough(ID3DBlob** ppHostShader); #endif diff --git a/src/core/hle/D3D8/XbVertexShader.cpp b/src/core/hle/D3D8/XbVertexShader.cpp index b884a9ffa..db62e5df5 100644 --- a/src/core/hle/D3D8/XbVertexShader.cpp +++ b/src/core/hle/D3D8/XbVertexShader.cpp @@ -323,6 +323,23 @@ static DWORD* CxbxGetVertexShaderTokens(xbox::X_D3DVertexShader* pXboxVertexShad return &pXboxVertexShader->ProgramAndConstants[0]; } +int GetXboxVertexDataComponentCount(int d3dvsdt) { + using namespace xbox; + switch (d3dvsdt) { + case X_D3DVSDT_NORMPACKED3: + return 3; + case X_D3DVSDT_FLOAT2H: + LOG_TEST_CASE("Attempting to use component count for X_D3DVSDT_FLOAT2H, which uses an odd (value, value, 0, value) layout"); + // This is a bit of an odd case. Will call it 4 since it writes a value to the 4th component... + return 4; + default: + // Most data types have a representation consistent with the number of components + const int countMask = 0x7; + const int countShift = 4; + return (d3dvsdt >> countShift) & countMask; + } +} + extern bool g_InlineVertexBuffer_DeclarationOverride; // TMP glue extern xbox::X_VERTEXATTRIBUTEFORMAT g_InlineVertexBuffer_AttributeFormat; // TMP glue diff --git a/src/core/hle/D3D8/XbVertexShader.h b/src/core/hle/D3D8/XbVertexShader.h index e25ab0a65..29a5ade50 100644 --- a/src/core/hle/D3D8/XbVertexShader.h +++ b/src/core/hle/D3D8/XbVertexShader.h @@ -200,6 +200,9 @@ extern size_t GetVshFunctionSize(const xbox::dword_xt* pXboxFunction); inline boolean VshHandleIsVertexShader(DWORD Handle) { return (Handle & X_D3DFVF_RESERVED0) ? TRUE : FALSE; } inline xbox::X_D3DVertexShader *VshHandleToXboxVertexShader(DWORD Handle) { return (xbox::X_D3DVertexShader *)(Handle & ~X_D3DFVF_RESERVED0);} +// Get the number of components represented by the given xbox vertex data type +extern int GetXboxVertexDataComponentCount(int d3dvsdt); + extern bool g_Xbox_VertexShader_IsFixedFunction; extern CxbxVertexDeclaration* CxbxGetVertexDeclaration(); From 5720444c6b506cf03621e722cbe044077521d679 Mon Sep 17 00:00:00 2001 From: Anthony Date: Mon, 7 Dec 2020 21:40:37 +1300 Subject: [PATCH 04/21] Fix normal transformations - Keep track of transforms set by the title - Transform normals by the inverse transpose of the position transform - X_D3DTRANSFORMSTATETYPE enum --- src/core/hle/D3D8/Direct3D9/Direct3D9.cpp | 36 +++++++++------- src/core/hle/D3D8/Direct3D9/Direct3D9.h | 4 +- .../Direct3D9/FixedFunctionVertexShader.hlsl | 31 ++++++-------- .../FixedFunctionVertexShaderState.hlsli | 5 ++- src/core/hle/D3D8/FixedFunctionState.cpp | 41 +++++++++++++++++++ src/core/hle/D3D8/FixedFunctionState.h | 14 +++++++ src/core/hle/D3D8/XbConvert.h | 24 +++++------ src/core/hle/D3D8/XbD3D8Types.h | 16 ++++++++ 8 files changed, 124 insertions(+), 47 deletions(-) diff --git a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp index bc63cd09b..477c4e523 100644 --- a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp +++ b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp @@ -72,6 +72,7 @@ XboxRenderStateConverter XboxRenderStates; XboxTextureStateConverter XboxTextureStates; D3D8LightState d3d8LightState = D3D8LightState(); +D3D8TransformState d3d8TransformState = D3D8TransformState(); FixedFunctionVertexShaderState ffShaderState = {0}; // TODO find a home for this and associated code // Allow use of time duration literals (making 16ms, etc possible) @@ -306,9 +307,9 @@ g_EmuCDPD; XB_MACRO(xbox::void_xt, WINAPI, D3DDevice_SetVertexShader_0, () ); \ XB_MACRO(xbox::void_xt, WINAPI, D3DDevice_SetVertexShaderInput, (xbox::dword_xt, xbox::uint_xt, xbox::X_STREAMINPUT*) ); \ XB_MACRO(xbox::void_xt, WINAPI, D3DDevice_SetViewport, (CONST xbox::X_D3DVIEWPORT8*) ); \ - XB_MACRO(xbox::void_xt, WINAPI, D3DDevice_SetTransform, (D3DTRANSFORMSTATETYPE, CONST D3DMATRIX*) ); \ + XB_MACRO(xbox::void_xt, WINAPI, D3DDevice_SetTransform, (xbox::X_D3DTRANSFORMSTATETYPE, CONST D3DMATRIX*) ); \ XB_MACRO(xbox::void_xt, WINAPI, D3DDevice_SetTransform_0, () ); \ - XB_MACRO(xbox::void_xt, WINAPI, D3DDevice_MultiplyTransform, (D3DTRANSFORMSTATETYPE, CONST D3DMATRIX*) ); \ + XB_MACRO(xbox::void_xt, WINAPI, D3DDevice_MultiplyTransform, (xbox::X_D3DTRANSFORMSTATETYPE, CONST D3DMATRIX*) ); \ XB_MACRO(xbox::void_xt, WINAPI, D3D_DestroyResource, (xbox::X_D3DResource*) ); \ XB_MACRO(xbox::void_xt, WINAPI, D3D_DestroyResource__LTCG, (xbox::void_xt) ); \ XB_MACRO(xbox::hresult_xt, WINAPI, Direct3D_CreateDevice, (xbox::uint_xt, D3DDEVTYPE, HWND, xbox::dword_xt, xbox::X_D3DPRESENT_PARAMETERS*, xbox::X_D3DDevice**)); \ @@ -6336,6 +6337,17 @@ void UpdateFixedFunctionVertexShaderState() { using namespace xbox; + // Transforms + // Transpose row major to column major for HLSL + D3DXMatrixTranspose((D3DXMATRIX*)&ffShaderState.Transforms.Projection, (D3DXMATRIX*)&d3d8TransformState.Transforms[X_D3DTS_PROJECTION]); + D3DXMatrixTranspose((D3DXMATRIX*)&ffShaderState.Transforms.View, (D3DXMATRIX*)&d3d8TransformState.Transforms[X_D3DTS_VIEW]); + + for (int i = 0; i < 4; i++) { + D3DXMatrixTranspose((D3DXMATRIX*)&ffShaderState.Transforms.Texture[i], (D3DXMATRIX*)&d3d8TransformState.Transforms[X_D3DTS_TEXTURE0 + i]); + D3DXMatrixTranspose((D3DXMATRIX*)&ffShaderState.Transforms.WorldView[i], (D3DXMATRIX*)&d3d8TransformState.WorldView[i]); + D3DXMatrixTranspose((D3DXMATRIX*)&ffShaderState.Transforms.WorldViewInverseTranspose[i], (D3DXMATRIX*)&d3d8TransformState.WorldViewInverseTranspose[i]); + } + // Lighting ffShaderState.Modes.Lighting = (float)XboxRenderStates.GetXboxRenderState(X_D3DRS_LIGHTING); ffShaderState.Modes.TwoSidedLighting = (float)XboxRenderStates.GetXboxRenderState(X_D3DRS_TWOSIDEDLIGHTING); @@ -6576,17 +6588,13 @@ xbox::void_xt __fastcall xbox::EMUPATCH(D3DDevice_SetRenderState_Simple) void CxbxImpl_SetTransform ( - D3DTRANSFORMSTATETYPE State, + xbox::X_D3DTRANSFORMSTATETYPE State, CONST D3DMATRIX *pMatrix ) { LOG_INIT - // Transpose row major to column major for HLSL - D3DXMATRIX hlslMatrix; - D3DXMatrixTranspose(&hlslMatrix, (D3DXMATRIX*)pMatrix); - // Save to vertex shader state - ((D3DXMATRIX*)&ffShaderState.Transforms)[State] = hlslMatrix; + d3d8TransformState.SetTransform(State, pMatrix); auto d3d9State = EmuXB2PC_D3DTS(State); @@ -6605,7 +6613,7 @@ static thread_local uint32_t setTransformCount = 0; // so we cheat a bit by stashing the function body in a separate function static void D3DDevice_SetTransform_0 ( - D3DTRANSFORMSTATETYPE State, + xbox::X_D3DTRANSFORMSTATETYPE State, CONST D3DMATRIX *pMatrix ) { @@ -6633,7 +6641,7 @@ __declspec(naked) xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_SetTransform_0) ( ) { - D3DTRANSFORMSTATETYPE State; + xbox::X_D3DTRANSFORMSTATETYPE State; CONST D3DMATRIX *pMatrix; __asm { LTCG_PROLOGUE @@ -6655,7 +6663,7 @@ __declspec(naked) xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_SetTransform_0) // ****************************************************************** xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_SetTransform) ( - D3DTRANSFORMSTATETYPE State, + xbox::X_D3DTRANSFORMSTATETYPE State, CONST D3DMATRIX *pMatrix ) { @@ -6679,7 +6687,7 @@ xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_SetTransform) // ****************************************************************** xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_MultiplyTransform) ( - D3DTRANSFORMSTATETYPE State, + xbox::X_D3DTRANSFORMSTATETYPE State, CONST D3DMATRIX *pMatrix ) { @@ -6693,9 +6701,9 @@ xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_MultiplyTransform) // Trampoline to guest code to remove the need for a GetTransform patch XB_TRMP(D3DDevice_MultiplyTransform)(State, pMatrix); - State = EmuXB2PC_D3DTS(State); + auto pcState = EmuXB2PC_D3DTS(State); - HRESULT hRet = g_pD3DDevice->MultiplyTransform(State, pMatrix); + HRESULT hRet = g_pD3DDevice->MultiplyTransform(pcState, pMatrix); DEBUG_D3DRESULT(hRet, "g_pD3DDevice->MultiplyTransform"); } diff --git a/src/core/hle/D3D8/Direct3D9/Direct3D9.h b/src/core/hle/D3D8/Direct3D9/Direct3D9.h index 8f3c92459..6874a8a5f 100644 --- a/src/core/hle/D3D8/Direct3D9/Direct3D9.h +++ b/src/core/hle/D3D8/Direct3D9/Direct3D9.h @@ -1306,7 +1306,7 @@ xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetRenderState_ShadowFunc) // ****************************************************************** xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetTransform) ( - D3DTRANSFORMSTATETYPE State, + xbox::X_D3DTRANSFORMSTATETYPE State, CONST D3DMATRIX *pMatrix ); @@ -1317,7 +1317,7 @@ xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetTransform_0)(); // ****************************************************************** xbox::void_xt WINAPI EMUPATCH(D3DDevice_MultiplyTransform) ( - D3DTRANSFORMSTATETYPE State, + xbox::X_D3DTRANSFORMSTATETYPE State, CONST D3DMATRIX *pMatrix ); diff --git a/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShader.hlsl b/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShader.hlsl index a5c848610..e377836d3 100644 --- a/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShader.hlsl +++ b/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShader.hlsl @@ -91,9 +91,8 @@ struct TransformInfo float3 Normal; }; -static TransformInfo World; // Vertex worldspace transform -static TransformInfo View; // Vertex viewspace/cameraspace transform -static TransformInfo Projection; // Vertex projection transform +static TransformInfo View; // Vertex transformed to viewspace +static TransformInfo Projection; // Vertex transformed to projection space // Vertex lighting // Both frontface and backface lighting can be calculated @@ -285,7 +284,7 @@ LightingOutput CalcLighting(const float2 powers) return totalLightOutput; } -TransformInfo DoWorldTransform(const float4 position, const float3 normal, const float4 blendWeights) +TransformInfo DoTransform(const float4 position, const float3 normal, const float4 blendWeights) { TransformInfo output; output.Position = float4(0, 0, 0, 0); @@ -302,8 +301,8 @@ TransformInfo DoWorldTransform(const float4 position, const float3 normal, const const int _4WEIGHT_4MAT = 6; if (state.Modes.VertexBlend == _BLEND_OFF) { - output.Position = mul(position, state.Transforms.World[0]); - output.Normal = mul(normal, (float3x3)state.Transforms.World[0]); + output.Position = mul(position, state.Transforms.WorldView[0]); + output.Normal = mul(normal, (float3x3)state.Transforms.WorldViewInverseTranspose[0]); return output; } @@ -315,20 +314,20 @@ TransformInfo DoWorldTransform(const float4 position, const float3 normal, const float lastBlend = 1; for (int i = 0; i < mats - 1; i++) { - output.Position += mul(position, state.Transforms.World[i]) * blendWeights[i]; - output.Normal += mul(normal, (float3x3) state.Transforms.World[i]) * blendWeights[i]; + output.Position += mul(position, state.Transforms.WorldView[i]) * blendWeights[i]; + output.Normal += mul(normal, (float3x3) state.Transforms.WorldViewInverseTranspose[i]) * blendWeights[i]; lastBlend -= blendWeights[i]; } if (calcLastBlend) { - output.Position += mul(position, state.Transforms.World[mats-1]) * lastBlend; - output.Normal += mul(normal, (float3x3) state.Transforms.World[mats-1]) * lastBlend; + output.Position += mul(position, state.Transforms.WorldView[mats-1]) * lastBlend; + output.Normal += mul(normal, (float3x3) state.Transforms.WorldViewInverseTranspose[mats-1]) * lastBlend; } else { - output.Position += mul(position, state.Transforms.World[mats-1]) * blendWeights[mats-1]; - output.Normal += mul(normal, (float3x3) state.Transforms.World[mats-1]) * blendWeights[mats-1]; + output.Position += mul(position, state.Transforms.WorldView[mats-1]) * blendWeights[mats-1]; + output.Normal += mul(normal, (float3x3) state.Transforms.WorldViewInverseTranspose[mats-1]) * blendWeights[mats-1]; } return output; @@ -549,12 +548,8 @@ VS_OUTPUT main(const VS_INPUT xInput) // Map default values VS_INPUT xIn = InitializeInputRegisters(xInput); - // World transform with vertex blending - World = DoWorldTransform(Get(xIn, position), Get(xIn, normal).xyz, Get(xIn, weight)); - - // View transform - View.Position = mul(World.Position, state.Transforms.View); - View.Normal = mul(World.Normal, (float3x3) state.Transforms.View); + // World + View transform with vertex blending + View = DoTransform(Get(xIn, position), Get(xIn, normal).xyz, Get(xIn, weight)); // Optionally normalize camera-space normals if (state.Modes.NormalizeNormals) diff --git a/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShaderState.hlsli b/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShaderState.hlsli index b684a04e9..31c223d1b 100644 --- a/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShaderState.hlsli +++ b/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShaderState.hlsli @@ -42,7 +42,10 @@ struct Transforms { float4x4 View; // 0 float4x4 Projection; // 1 arr(Texture, float4x4, 4); // 2, 3, 4, 5 - arr(World, float4x4, 4); // 6, 7, 8, 9 + // World matrices are 6, 7, 8, 9 + // But we use combined WorldView matrices in the shader + arr(WorldView, float4x4, 4); + arr(WorldViewInverseTranspose, float4x4, 4); }; // See D3DLIGHT diff --git a/src/core/hle/D3D8/FixedFunctionState.cpp b/src/core/hle/D3D8/FixedFunctionState.cpp index 6eb6064d8..57b719376 100644 --- a/src/core/hle/D3D8/FixedFunctionState.cpp +++ b/src/core/hle/D3D8/FixedFunctionState.cpp @@ -2,6 +2,7 @@ #include "FixedFunctionState.h" #include "Logging.h" +#include "core/kernel/init/CxbxKrnl.h" D3DCOLORVALUE colorValue(float r, float g, float b, float a) { auto value = D3DCOLORVALUE(); @@ -85,3 +86,43 @@ void D3D8LightState::EnableLight(uint32_t index, bool enable) { EmuLog(LOG_LEVEL::INFO, "Could not disable light %d because it wasn't enabled", index); } } + +D3D8TransformState::D3D8TransformState() { + D3DMATRIX identity; + D3DXMatrixIdentity((D3DXMATRIX*)&identity); + + this->Transforms.fill(identity); + this->WorldView.fill(identity); + this->WorldViewInverseTranspose.fill(identity); +} + +void D3D8TransformState::SetTransform(xbox::X_D3DTRANSFORMSTATETYPE state, const D3DMATRIX* pMatrix) +{ + using namespace xbox; + + LOG_INIT + + if (state >= this->Transforms.size()) { + LOG_TEST_CASE("Transform state was not in expected range"); + return; + } + + // Update transform state + this->Transforms[state] = *pMatrix; + + // Recalculate dependent matrices + for (int i = 0; i < 4; i++) { + auto worldState = X_D3DTS_WORLD + i; + if (state == X_D3DTS_VIEW || state == worldState) { + D3DXMATRIX worldView; + D3DXMatrixMultiply(&worldView, (D3DXMATRIX*)&Transforms[worldState], (D3DXMATRIX*)&Transforms[X_D3DTS_VIEW]); + this->WorldView[i] = worldView; + + D3DXMATRIX worldViewInverseTranspose; + D3DXMatrixInverse(&worldViewInverseTranspose, nullptr, &worldView); + D3DXMatrixTranspose(&worldViewInverseTranspose, &worldViewInverseTranspose); + + this->WorldViewInverseTranspose[i] = worldViewInverseTranspose; + } + } +} diff --git a/src/core/hle/D3D8/FixedFunctionState.h b/src/core/hle/D3D8/FixedFunctionState.h index d7a4c3fd1..105b32b8e 100644 --- a/src/core/hle/D3D8/FixedFunctionState.h +++ b/src/core/hle/D3D8/FixedFunctionState.h @@ -23,4 +23,18 @@ public: void EnableLight(uint32_t index, bool enable); }; +class D3D8TransformState { +public: + D3D8TransformState(); + void SetTransform(xbox::X_D3DTRANSFORMSTATETYPE state, const D3DMATRIX* pMatrix); + + // The transforms set by the Xbox title + std::array Transforms; + + // Combines world/view matrices + std::array WorldView; + // World/view inverse transpose for lighting calculations + std::array WorldViewInverseTranspose; +}; + #endif diff --git a/src/core/hle/D3D8/XbConvert.h b/src/core/hle/D3D8/XbConvert.h index 08deee031..1bf14114c 100644 --- a/src/core/hle/D3D8/XbConvert.h +++ b/src/core/hle/D3D8/XbConvert.h @@ -94,25 +94,25 @@ else //*/ // convert from xbox to pc texture transform state types -inline D3DTRANSFORMSTATETYPE EmuXB2PC_D3DTS(D3DTRANSFORMSTATETYPE State) +inline D3DTRANSFORMSTATETYPE EmuXB2PC_D3DTS(xbox::X_D3DTRANSFORMSTATETYPE State) { // Handle Xbox -> D3D State mapping switch (State) { - case 0: return (D3DTRANSFORMSTATETYPE)D3DTS_VIEW; - case 1: return (D3DTRANSFORMSTATETYPE)D3DTS_PROJECTION; - case 2: return (D3DTRANSFORMSTATETYPE)D3DTS_TEXTURE0; - case 3: return (D3DTRANSFORMSTATETYPE)D3DTS_TEXTURE1; - case 4: return (D3DTRANSFORMSTATETYPE)D3DTS_TEXTURE2; - case 5: return (D3DTRANSFORMSTATETYPE)D3DTS_TEXTURE3; - case 6: return (D3DTRANSFORMSTATETYPE)D3DTS_WORLD; - case 7: return (D3DTRANSFORMSTATETYPE)D3DTS_WORLD1; - case 8: return (D3DTRANSFORMSTATETYPE)D3DTS_WORLD2; - case 9: return (D3DTRANSFORMSTATETYPE)D3DTS_WORLD3; + case xbox::X_D3DTS_VIEW: return D3DTS_VIEW; + case xbox::X_D3DTS_PROJECTION: return D3DTS_PROJECTION; + case xbox::X_D3DTS_TEXTURE0: return D3DTS_TEXTURE0; + case xbox::X_D3DTS_TEXTURE1: return D3DTS_TEXTURE1; + case xbox::X_D3DTS_TEXTURE2: return D3DTS_TEXTURE2; + case xbox::X_D3DTS_TEXTURE3: return D3DTS_TEXTURE3; + case xbox::X_D3DTS_WORLD: return D3DTS_WORLD; + case xbox::X_D3DTS_WORLD1: return D3DTS_WORLD1; + case xbox::X_D3DTS_WORLD2: return D3DTS_WORLD2; + case xbox::X_D3DTS_WORLD3: return D3DTS_WORLD3; } // Handle World Matrix offsets if (State >= 256 && State <= 511) { - return D3DTS_WORLDMATRIX(State - 256); + return D3DTS_WORLDMATRIX(State - 256); } CxbxKrnlCleanupEx(LOG_PREFIX_D3DCVT, "Unknown Transform State Type (%d)", State); diff --git a/src/core/hle/D3D8/XbD3D8Types.h b/src/core/hle/D3D8/XbD3D8Types.h index 6c062f7c0..75e026008 100644 --- a/src/core/hle/D3D8/XbD3D8Types.h +++ b/src/core/hle/D3D8/XbD3D8Types.h @@ -1305,6 +1305,22 @@ typedef enum _X_D3DVSD_TOKENTYPE #define X_D3DTSS_TCI_OBJECT 0x00040000 // Warning! Collides with host Direct3D 9 D3DTSS_TCI_SPHEREMAP #define X_D3DTSS_TCI_SPHEREMAP 0x00050000 +enum X_D3DTRANSFORMSTATETYPE { + X_D3DTS_VIEW = 0, + X_D3DTS_PROJECTION = 1, + X_D3DTS_TEXTURE0 = 2, + X_D3DTS_TEXTURE1 = 3, + X_D3DTS_TEXTURE2 = 4, + X_D3DTS_TEXTURE3 = 5, + X_D3DTS_WORLD = 6, + X_D3DTS_WORLD1 = 7, + X_D3DTS_WORLD2 = 8, + X_D3DTS_WORLD3 = 9, + + X_D3DTS_MAX = 10, + X_D3DTS_FORCE_DWORD = 0x7FFFFFFF, +}; + typedef DWORD NV2AMETHOD; // From f98d040a655178642ecfe43a582c9aef7b405347 Mon Sep 17 00:00:00 2001 From: Anthony Miles Date: Tue, 27 Oct 2020 23:06:27 +1300 Subject: [PATCH 05/21] Enable fixed function vertex shader - Various bits of glue code - Toggle Shader/D3D9 with F2 - Replace vertex shader mode flags with enum --- src/core/hle/D3D8/Direct3D9/Direct3D9.cpp | 21 ++++---- src/core/hle/D3D8/XbVertexBuffer.cpp | 2 +- src/core/hle/D3D8/XbVertexShader.cpp | 58 +++++++++++++---------- src/core/hle/D3D8/XbVertexShader.h | 12 +++++ 4 files changed, 59 insertions(+), 34 deletions(-) diff --git a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp index 477c4e523..a433b2f60 100644 --- a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp +++ b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp @@ -1908,6 +1908,10 @@ static LRESULT WINAPI EmuMsgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lPar extern void DSound_PrintStats(); //TODO: move into plugin class usage. DSound_PrintStats(); } + else if (wParam == VK_F2) + { + g_UseFixedFunctionVertexShader = !g_UseFixedFunctionVertexShader; + } else if (wParam == VK_F3) { g_bClipCursor = !g_bClipCursor; @@ -2885,8 +2889,6 @@ void GetMultiSampleScaleRaw(float& xScale, float& yScale) { // Titles can render pre-transformed vertices to screen space (using XYZRHW vertex position data or otherwise) // so we need to know the space they are in to interpret it correctly void GetScreenScaleFactors(float& scaleX, float& scaleY) { - extern bool g_Xbox_VertexShader_IsPassthrough; - // Example: // NFS HP2 renders in-game at 640*480 // The title uses MSAA, which increases the rendertarget size, but leaves the screen scale unaffected @@ -2916,7 +2918,7 @@ void GetScreenScaleFactors(float& scaleX, float& scaleY) { // - Antialias sample (background gradient) // Vertex program passthrough equivalent (title does apply backbuffer scale): // - NFS:HP2 (car speed and other in-game UI elements) - if (!g_Xbox_VertexShader_IsPassthrough) { + if (g_Xbox_VertexShaderMode != VertexShaderMode::Passthrough) { scaleX *= g_Xbox_BackbufferScaleX; scaleY *= g_Xbox_BackbufferScaleY; } @@ -4169,8 +4171,6 @@ void GetXboxViewportOffsetAndScale(float (&vOffset)[4], float(&vScale)[4]) void CxbxUpdateHostViewPortOffsetAndScaleConstants() { - extern bool g_Xbox_VertexShader_IsPassthrough; - float vScaleOffset[2][4]; // 0 - scale 1 - offset GetXboxViewportOffsetAndScale(vScaleOffset[1], vScaleOffset[0]); @@ -4196,7 +4196,7 @@ void CxbxUpdateHostViewPortOffsetAndScaleConstants() // Passthrough should range 0 to 1, instead of 0 to zbuffer depth // Test case: DoA3 character select - float zOutputScale = g_Xbox_VertexShader_IsPassthrough ? 1 : g_ZScale; + float zOutputScale = g_Xbox_VertexShaderMode == VertexShaderMode::Passthrough ? 1 : g_ZScale; float screenspaceScale[4] = { xboxScreenspaceWidth / 2, -xboxScreenspaceHeight / 2, zOutputScale, 1 }; float screenspaceOffset[4] = { xboxScreenspaceWidth / 2 + aaOffsetX, xboxScreenspaceHeight / 2 + aaOffsetY, 0, 0 }; @@ -7343,7 +7343,7 @@ void CxbxUpdateHostTextureScaling() // Texcoord index. Just the texture stage unless fixed function mode int texCoordIndex = stage; - if (g_Xbox_VertexShader_IsFixedFunction) { + if (g_Xbox_VertexShaderMode == VertexShaderMode::FixedFunction) { // Get TEXCOORDINDEX for the current texture stage's state // Stores both the texture stage index and information for generating coordinates // See D3DTSS_TEXCOORDINDEX @@ -7434,7 +7434,7 @@ void CxbxUpdateHostViewport() { aaScaleX *= g_RenderTargetUpscaleFactor; aaScaleY *= g_RenderTargetUpscaleFactor; - if (g_Xbox_VertexShader_IsFixedFunction) { + if (g_Xbox_VertexShaderMode == VertexShaderMode::FixedFunction) { // Set viewport D3DVIEWPORT hostViewport = g_Xbox_Viewport; hostViewport.X *= aaScaleX; @@ -7495,6 +7495,11 @@ void CxbxUpdateNativeD3DResources() CxbxUpdateHostVertexShaderConstants(); CxbxUpdateHostViewport(); + + // Update fixed function vertex shader state + if (g_Xbox_VertexShaderMode == VertexShaderMode::FixedFunction && g_UseFixedFunctionVertexShader) { + UpdateFixedFunctionVertexShaderState(); + } // NOTE: Order is important here // Some Texture States depend on RenderState values (Point Sprites) diff --git a/src/core/hle/D3D8/XbVertexBuffer.cpp b/src/core/hle/D3D8/XbVertexBuffer.cpp index e67fdf6ca..23ed872d7 100644 --- a/src/core/hle/D3D8/XbVertexBuffer.cpp +++ b/src/core/hle/D3D8/XbVertexBuffer.cpp @@ -844,7 +844,7 @@ void CxbxImpl_End() // Arrange for g_InlineVertexBuffer_AttributeFormat to be returned in CxbxGetVertexDeclaration, // so that our above composed declaration will be used for the next draw : g_InlineVertexBuffer_DeclarationOverride = true; - // Note, that g_Xbox_VertexShader_IsFixedFunction should be left untouched, + // Note, that g_Xbox_VertexShaderMode should be left untouched, // because except for the declaration override, the Xbox shader (either FVF // or a program, or even passthrough shaders) should still be in effect! diff --git a/src/core/hle/D3D8/XbVertexShader.cpp b/src/core/hle/D3D8/XbVertexShader.cpp index db62e5df5..90e92412a 100644 --- a/src/core/hle/D3D8/XbVertexShader.cpp +++ b/src/core/hle/D3D8/XbVertexShader.cpp @@ -58,9 +58,9 @@ extern XboxRenderStateConverter XboxRenderStates; // Declared in Direct3D9.cpp xbox::X_STREAMINPUT g_Xbox_SetVertexShaderInput_Data[X_VSH_MAX_STREAMS] = { 0 }; // Active when g_Xbox_SetVertexShaderInput_Count > 0 xbox::X_VERTEXATTRIBUTEFORMAT g_Xbox_SetVertexShaderInput_Attributes = { 0 }; // Read by GetXboxVertexAttributes when g_Xbox_SetVertexShaderInput_Count > 0 -// Variables set by [D3DDevice|CxbxImpl]_SetVertexShader() and [D3DDevice|CxbxImpl]_SelectVertexShader() : - bool g_Xbox_VertexShader_IsFixedFunction = true; - bool g_Xbox_VertexShader_IsPassthrough = false; +VertexShaderMode g_Xbox_VertexShaderMode = VertexShaderMode::FixedFunction; +bool g_UseFixedFunctionVertexShader = true; + xbox::dword_xt g_Xbox_VertexShader_Handle = 0; #ifdef CXBX_USE_GLOBAL_VERTEXSHADER_POINTER // TODO : Would this be more accurate / simpler? xbox::X_D3DVertexShader *g_Xbox_VertexShader_Ptr = nullptr; @@ -1092,7 +1092,9 @@ VertexDeclarationKey GetXboxVertexAttributesKey(xbox::X_VERTEXATTRIBUTEFORMAT* p auto attributeHash = ComputeHash((void*)pXboxVertexAttributeFormat, sizeof(xbox::X_VERTEXATTRIBUTEFORMAT)); // For now, we use different declarations depending on if the fixed function pipeline // is in use, even if the attributes are the same - return attributeHash ^ (VertexDeclarationKey)g_Xbox_VertexShader_IsFixedFunction; + return g_Xbox_VertexShaderMode == VertexShaderMode::FixedFunction + ? attributeHash + : attributeHash ^ 1; } std::unordered_map g_CxbxVertexDeclarations; @@ -1139,18 +1141,27 @@ void CxbxUpdateHostVertexShader() LOG_INIT; // Allows use of DEBUG_D3DRESULT - if (g_Xbox_VertexShader_IsFixedFunction) { - HRESULT hRet = g_pD3DDevice->SetVertexShader(nullptr); - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->SetVertexShader"); - // TODO : Once available, start using host Fixed Function HLSL shader - // instead of using deprecated host fixed function (by setting a null - // vertex shader). - // As for the required host vertex declaration : - // CxbxUpdateHostVertexDeclaration already been - // called, which sets host vertex declaration based on the - // declaration that XboxVertexShaderFromFVF generated. + if (g_Xbox_VertexShaderMode == VertexShaderMode::FixedFunction) { + IDirect3DVertexShader* fixedFunctionShader = nullptr; + HRESULT hRet; + + if (g_UseFixedFunctionVertexShader) { + static IDirect3DVertexShader* ffHlsl = nullptr; + if (ffHlsl == nullptr) { + ID3DBlob* pBlob = nullptr; + EmuCompileFixedFunction(&pBlob); + if (pBlob) { + hRet = g_pD3DDevice->CreateVertexShader((DWORD*)pBlob->GetBufferPointer(), &ffHlsl); + if (FAILED(hRet)) CxbxKrnlCleanup("Failed to create fixed-function shader"); + } + } + fixedFunctionShader = ffHlsl; + } + + hRet = g_pD3DDevice->SetVertexShader(fixedFunctionShader); + if (FAILED(hRet)) CxbxKrnlCleanup("Failed to set fixed-function shader"); } - else if (g_Xbox_VertexShader_IsPassthrough && g_bUsePassthroughHLSL) { + else if (g_Xbox_VertexShaderMode == VertexShaderMode::Passthrough && g_bUsePassthroughHLSL) { if (passthroughshader == nullptr) { ID3DBlob* pBlob = nullptr; EmuCompileXboxPassthrough(&pBlob); @@ -1264,7 +1275,7 @@ CxbxVertexDeclaration* CxbxGetVertexDeclaration() // Convert Xbox vertex attributes towards host Direct3D 9 vertex element D3DVERTEXELEMENT* pRecompiledVertexElements = EmuRecompileVshDeclaration( pXboxVertexAttributeFormat, - g_Xbox_VertexShader_IsFixedFunction, + g_Xbox_VertexShaderMode == VertexShaderMode::FixedFunction, pCxbxVertexDeclaration); // Create the vertex declaration @@ -1354,6 +1365,8 @@ void CxbxImpl_SelectVertexShader(DWORD Handle, DWORD Address) // Either way, the given address slot is selected as the start of the current vertex shader program g_Xbox_VertexShader_FunctionSlots_StartAddress = Address; + g_Xbox_VertexShaderMode = VertexShaderMode::ShaderProgram; + if (Handle) { if (!VshHandleIsVertexShader(Handle)) LOG_TEST_CASE("Non-zero handle must be a VertexShader!"); @@ -1362,8 +1375,6 @@ void CxbxImpl_SelectVertexShader(DWORD Handle, DWORD Address) g_Xbox_VertexShader_Ptr = VshHandleToXboxVertexShader(Handle); #endif g_Xbox_VertexShader_Handle = Handle; - g_Xbox_VertexShader_IsFixedFunction = false; - g_Xbox_VertexShader_IsPassthrough = false; } } @@ -1450,7 +1461,6 @@ void CxbxImpl_SetVertexShader(DWORD Handle) HRESULT hRet = D3D_OK; xbox::X_D3DVertexShader* pXboxVertexShader = CxbxGetXboxVertexShaderForHandle(Handle); - g_Xbox_VertexShader_IsPassthrough = false; if ((pXboxVertexShader->Flags & g_X_VERTEXSHADER_FLAG_VALID_MASK) != pXboxVertexShader->Flags) { LOG_TEST_CASE("Unknown vertex shader flag"); @@ -1470,8 +1480,8 @@ void CxbxImpl_SetVertexShader(DWORD Handle) LOG_TEST_CASE("g_Xbox_VertexShader_FunctionSlots_StartAddress != 0"); bHackCallSelectAgain = true; } - if (g_Xbox_VertexShader_IsFixedFunction != false) { - LOG_TEST_CASE("g_Xbox_VertexShader_IsFixedFunction != false"); + if (g_Xbox_VertexShaderMode != VertexShaderMode::ShaderProgram) { + LOG_TEST_CASE("Not in shader program mode after SetVertexShader trampoline"); bHackCallSelectAgain = true; } @@ -1480,7 +1490,6 @@ void CxbxImpl_SetVertexShader(DWORD Handle) // _SelectVertexShader isn't applied; // 'solve' that by calling it here instead. CxbxImpl_SelectVertexShader(Handle, 0); - g_Xbox_VertexShader_IsFixedFunction = false; } #endif } else { @@ -1511,12 +1520,11 @@ void CxbxImpl_SetVertexShader(DWORD Handle) // Switch to passthrough program, if so required if (pXboxVertexShader->Flags & X_VERTEXSHADER_FLAG_PASSTHROUGH) { CxbxSetVertexShaderPassthroughProgram(); - g_Xbox_VertexShader_IsFixedFunction = false; - g_Xbox_VertexShader_IsPassthrough = true; + g_Xbox_VertexShaderMode = VertexShaderMode::Passthrough; } else { // Test-case : Many XDK samples, Crazy taxi 3 //LOG_TEST_CASE("Other or no vertex shader flags"); - g_Xbox_VertexShader_IsFixedFunction = true; + g_Xbox_VertexShaderMode = VertexShaderMode::FixedFunction; } } } diff --git a/src/core/hle/D3D8/XbVertexShader.h b/src/core/hle/D3D8/XbVertexShader.h index 29a5ade50..5b82c28a3 100644 --- a/src/core/hle/D3D8/XbVertexShader.h +++ b/src/core/hle/D3D8/XbVertexShader.h @@ -80,6 +80,17 @@ typedef struct _CxbxVertexDeclaration } CxbxVertexDeclaration; +enum class VertexShaderMode { + FixedFunction, + // When titles use Xbox fixed function with pre-transformed vertices + // it actually uses a special "passthrough" shader program + Passthrough, + ShaderProgram +}; + +extern VertexShaderMode g_Xbox_VertexShaderMode; +extern bool g_UseFixedFunctionVertexShader; + // Intermediate vertex shader structures enum VSH_OREG_NAME { @@ -217,4 +228,5 @@ extern void CxbxImpl_SetVertexShaderInput(DWORD Handle, UINT StreamCount, xbox:: extern void CxbxImpl_SetVertexShaderConstant(INT Register, PVOID pConstantData, DWORD ConstantCount); extern void CxbxImpl_DeleteVertexShader(DWORD Handle); extern void CxbxVertexShaderSetFlags(); +extern HRESULT SetVertexShader(IDirect3DVertexShader* pShader); #endif From b4685c1dc26f2fc071a905d17350d4ca15830966 Mon Sep 17 00:00:00 2001 From: Anthony Miles Date: Wed, 28 Oct 2020 00:36:03 +1300 Subject: [PATCH 06/21] Initialize all registers for fixed function mode --- src/core/hle/D3D8/XbVertexShader.cpp | 47 +++++++++++++++++++--------- 1 file changed, 32 insertions(+), 15 deletions(-) diff --git a/src/core/hle/D3D8/XbVertexShader.cpp b/src/core/hle/D3D8/XbVertexShader.cpp index 90e92412a..d9918f1ae 100644 --- a/src/core/hle/D3D8/XbVertexShader.cpp +++ b/src/core/hle/D3D8/XbVertexShader.cpp @@ -1445,6 +1445,37 @@ void CxbxImpl_LoadVertexShader(DWORD Handle, DWORD Address) } } +// Set default values for attributes missing from vertex declaration +void SetFixedFunctionDefaultVertexAttributes(DWORD vshFlags) { + // Test case: Mechassault (skybox) + // Test case: KOTOR (overlay) + auto decl = CxbxGetVertexDeclaration(); + for (int i = 0; i < xbox::X_D3DVSDE_TEXCOORD3; i++) { + if (decl->vRegisterInDeclaration[i]) { + continue; // only reset missing attributes + } + + const float white[4] = { 1, 1, 1, 1 }; + const float black[4] = { 0, 0, 0, 0 }; + const float unset[4] = { 0, 0, 0, 1 }; + const float* value = unset; + + // Account for flags that override this reset behaviour + if (i == xbox::X_D3DVSDE_DIFFUSE && !(vshFlags & X_VERTEXSHADER_FLAG_HASDIFFUSE) || + i == xbox::X_D3DVSDE_BACKDIFFUSE && !(vshFlags & X_VERTEXSHADER_FLAG_HASBACKDIFFUSE)) { + value = white; + } + else if (i == xbox::X_D3DVSDE_SPECULAR && !(vshFlags & X_VERTEXSHADER_FLAG_HASSPECULAR) || + i == xbox::X_D3DVSDE_BACKSPECULAR && !(vshFlags & X_VERTEXSHADER_FLAG_HASBACKSPECULAR)) { + value = black; + } + + // Note : We avoid calling CxbxImpl_SetVertexData4f here, as that would + // start populating g_InlineVertexBuffer_Table, which is not our intent here. + CxbxSetVertexAttribute(i, value[0], value[1], value[2], value[3]); + } +} + void CxbxImpl_SetVertexShader(DWORD Handle) { LOG_INIT; // Allows use of DEBUG_D3DRESULT @@ -1501,21 +1532,7 @@ void CxbxImpl_SetVertexShader(DWORD Handle) g_Xbox_VertexShader_Handle = Handle; g_Xbox_VertexShader_FunctionSlots_StartAddress = 0; - // Only when there's no program, set default values for attributes missing from vertex shader - // Note : We avoid calling CxbxImpl_SetVertexData4f here, as that would - // start populating g_InlineVertexBuffer_Table, which is not our intend here. - if (!(pXboxVertexShader->Flags & X_VERTEXSHADER_FLAG_HASDIFFUSE)) { - CxbxSetVertexAttribute(xbox::X_D3DVSDE_DIFFUSE, 1, 1, 1, 1); - } - if (!(pXboxVertexShader->Flags & X_VERTEXSHADER_FLAG_HASSPECULAR)) { - CxbxSetVertexAttribute(xbox::X_D3DVSDE_SPECULAR, 0, 0, 0, 0); - } - if (!(pXboxVertexShader->Flags & X_VERTEXSHADER_FLAG_HASBACKDIFFUSE)) { - CxbxSetVertexAttribute(xbox::X_D3DVSDE_BACKDIFFUSE, 1, 1, 1, 1); - } - if (!(pXboxVertexShader->Flags & X_VERTEXSHADER_FLAG_HASBACKSPECULAR)) { - CxbxSetVertexAttribute(xbox::X_D3DVSDE_BACKSPECULAR, 0, 0, 0, 0); - } + SetFixedFunctionDefaultVertexAttributes(pXboxVertexShader->Flags); // Switch to passthrough program, if so required if (pXboxVertexShader->Flags & X_VERTEXSHADER_FLAG_PASSTHROUGH) { From 43be445c3762476efa68cc14a355cb5ef04c50bd Mon Sep 17 00:00:00 2001 From: Anthony Date: Mon, 30 Nov 2020 23:47:49 +1300 Subject: [PATCH 07/21] Copy constants regardless of dirty state --- src/core/hle/D3D8/Direct3D9/Direct3D9.cpp | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp index a433b2f60..53f68c76b 100644 --- a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp +++ b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp @@ -7393,18 +7393,10 @@ extern float* HLE_get_NV2A_vertex_constant_float4_ptr(unsigned const_index); // // remove our patches on D3DDevice_SetVertexShaderConstant (and CxbxImpl_SetVertexShaderConstant) void CxbxUpdateHostVertexShaderConstants() { - // Transfer all constants that have been flagged dirty to host - auto nv2a = g_NV2A->GetDeviceState(); - for (int i = 0; i < X_D3DVS_CONSTREG_COUNT; i++) { - if (nv2a->pgraph.vsh_constants_dirty[i]) { - nv2a->pgraph.vsh_constants_dirty[i] = false; - - float *constant_floats = HLE_get_NV2A_vertex_constant_float4_ptr(i); - // Note : If host SetVertexShaderConstantF has high overhead (unlikely), - // we could combine multiple adjacent constants into one call. - g_pD3DDevice->SetVertexShaderConstantF(i, constant_floats, 1); - } - } + // Copy all constants (as they may have been overwritten with fixed-function mode) + // Though we should only have to copy overwritten or dirty constants + float* constant_floats = HLE_get_NV2A_vertex_constant_float4_ptr(0); + g_pD3DDevice->SetVertexShaderConstantF(0, constant_floats, X_D3DVS_CONSTREG_COUNT); // FIXME our viewport constants don't match Xbox values // If we write them to pgraph constants, like we do with constants set by the title, From 8b8d1e0d3935db2775948c4756a67044cb5fbb4f Mon Sep 17 00:00:00 2001 From: PatrickvL Date: Wed, 9 Dec 2020 22:13:52 +0100 Subject: [PATCH 08/21] Fixed Function : Fixed CXBX_ALL_TEXCOORD_INPUTS build, avoid ambiguous symbol redeclaration --- .../Direct3D9/FixedFunctionVertexShader.hlsl | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShader.hlsl b/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShader.hlsl index e377836d3..a57f61429 100644 --- a/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShader.hlsl +++ b/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShader.hlsl @@ -369,7 +369,7 @@ Material DoMaterial(const uint index, const uint diffuseReg, const uint specular return material; } -float DoFog(VS_INPUT xIn) +float DoFog(const VS_INPUT xIn) { // TODO implement properly // Until we have pixel shader HLSL we are still leaning on D3D renderstates for fogging @@ -379,7 +379,7 @@ float DoFog(VS_INPUT xIn) float fogDepth; if (state.Fog.DepthMode == FixedFunctionVertexShader::FOG_DEPTH_NONE) - fogDepth = xIn.color[1].a; // In fixed-function mode, fog is passed in the specular alpha + fogDepth = Get(xIn, specular).a; // In fixed-function mode, fog is passed in the specular alpha if (state.Fog.DepthMode == FixedFunctionVertexShader::FOG_DEPTH_RANGE) fogDepth = length(View.Position.xyz); if (state.Fog.DepthMode == FixedFunctionVertexShader::FOG_DEPTH_Z) @@ -574,24 +574,24 @@ VS_OUTPUT main(const VS_INPUT xInput) LightingOutput lighting = CalcLighting(powers); // Compute each lighting component - float3 ambient = material.Ambient.rgb * state.AmbientPlusLightAmbient.rgb; - float3 backAmbient = backMaterial.Ambient.rgb * state.BackAmbientPlusLightAmbient.rgb; + float3 _ambient = material.Ambient.rgb * state.AmbientPlusLightAmbient.rgb; + float3 _backAmbient = backMaterial.Ambient.rgb * state.BackAmbientPlusLightAmbient.rgb; - float3 diffuse = material.Diffuse.rgb * lighting.Diffuse.Front; - float3 backDiffuse = backMaterial.Diffuse.rgb * lighting.Diffuse.Back; + float3 _diffuse = material.Diffuse.rgb * lighting.Diffuse.Front; + float3 _backDiffuse = backMaterial.Diffuse.rgb * lighting.Diffuse.Back; - float3 specular = material.Specular.rgb * lighting.Specular.Front; - float3 backSpecular = backMaterial.Specular.rgb * lighting.Specular.Back; + float3 _specular = material.Specular.rgb * lighting.Specular.Front; + float3 _backSpecular = backMaterial.Specular.rgb * lighting.Specular.Back; - float3 emissive = material.Emissive.rgb; - float3 backEmissive = backMaterial.Emissive.rgb; + float3 _emissive = material.Emissive.rgb; + float3 _backEmissive = backMaterial.Emissive.rgb; // Frontface - xOut.oD0 = float4(ambient + diffuse + emissive, material.Diffuse.a); - xOut.oD1 = float4(specular, 0); + xOut.oD0 = float4(_ambient + _diffuse + _emissive, material.Diffuse.a); + xOut.oD1 = float4(_specular, 0); // Backface - xOut.oB0 = float4(backAmbient + backDiffuse + backEmissive, backMaterial.Diffuse.a); - xOut.oB1 = float4(backSpecular, 0); + xOut.oB0 = float4(_backAmbient + _backDiffuse + _backEmissive, backMaterial.Diffuse.a); + xOut.oB1 = float4(_backSpecular, 0); } // TODO does TwoSidedLighting imply Lighting? Verify if TwoSidedLighting can be enabled independently of Lighting @@ -621,10 +621,10 @@ VS_OUTPUT main(const VS_INPUT xInput) xOut.oPts = DoPointSpriteSize(); // Texture coordinates - xOut.oT0 = DoTexCoord(0, xIn) / xboxTextureScale[0];; - xOut.oT1 = DoTexCoord(1, xIn) / xboxTextureScale[1];; - xOut.oT2 = DoTexCoord(2, xIn) / xboxTextureScale[2];; - xOut.oT3 = DoTexCoord(3, xIn) / xboxTextureScale[3];; + xOut.oT0 = DoTexCoord(0, xIn) / xboxTextureScale[0]; + xOut.oT1 = DoTexCoord(1, xIn) / xboxTextureScale[1]; + xOut.oT2 = DoTexCoord(2, xIn) / xboxTextureScale[2]; + xOut.oT3 = DoTexCoord(3, xIn) / xboxTextureScale[3]; return xOut; } From 8200cd8e43f22945023474cab6614752ffa740c6 Mon Sep 17 00:00:00 2001 From: PatrickvL Date: Wed, 9 Dec 2020 22:34:05 +0100 Subject: [PATCH 09/21] Fixed Function : Extract method D3D8TransformState::RecalculateDependentMatrices, called via two getters using a dirty boolean, preventing needless recalculations. --- src/core/hle/D3D8/Direct3D9/Direct3D9.cpp | 6 +-- src/core/hle/D3D8/FixedFunctionState.cpp | 56 +++++++++++++++++------ src/core/hle/D3D8/FixedFunctionState.h | 6 +++ 3 files changed, 51 insertions(+), 17 deletions(-) diff --git a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp index 53f68c76b..b4777107a 100644 --- a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp +++ b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp @@ -6342,10 +6342,10 @@ void UpdateFixedFunctionVertexShaderState() D3DXMatrixTranspose((D3DXMATRIX*)&ffShaderState.Transforms.Projection, (D3DXMATRIX*)&d3d8TransformState.Transforms[X_D3DTS_PROJECTION]); D3DXMatrixTranspose((D3DXMATRIX*)&ffShaderState.Transforms.View, (D3DXMATRIX*)&d3d8TransformState.Transforms[X_D3DTS_VIEW]); - for (int i = 0; i < 4; i++) { + for (unsigned i = 0; i < 4; i++) { D3DXMatrixTranspose((D3DXMATRIX*)&ffShaderState.Transforms.Texture[i], (D3DXMATRIX*)&d3d8TransformState.Transforms[X_D3DTS_TEXTURE0 + i]); - D3DXMatrixTranspose((D3DXMATRIX*)&ffShaderState.Transforms.WorldView[i], (D3DXMATRIX*)&d3d8TransformState.WorldView[i]); - D3DXMatrixTranspose((D3DXMATRIX*)&ffShaderState.Transforms.WorldViewInverseTranspose[i], (D3DXMATRIX*)&d3d8TransformState.WorldViewInverseTranspose[i]); + D3DXMatrixTranspose((D3DXMATRIX*)&ffShaderState.Transforms.WorldView[i], (D3DXMATRIX*)d3d8TransformState.GetWorldView(i)); + D3DXMatrixTranspose((D3DXMATRIX*)&ffShaderState.Transforms.WorldViewInverseTranspose[i], (D3DXMATRIX*)d3d8TransformState.GetWorldViewInverseTranspose(i)); } // Lighting diff --git a/src/core/hle/D3D8/FixedFunctionState.cpp b/src/core/hle/D3D8/FixedFunctionState.cpp index 57b719376..ac33d1752 100644 --- a/src/core/hle/D3D8/FixedFunctionState.cpp +++ b/src/core/hle/D3D8/FixedFunctionState.cpp @@ -94,6 +94,7 @@ D3D8TransformState::D3D8TransformState() { this->Transforms.fill(identity); this->WorldView.fill(identity); this->WorldViewInverseTranspose.fill(identity); + bWorldViewDirty = true; } void D3D8TransformState::SetTransform(xbox::X_D3DTRANSFORMSTATETYPE state, const D3DMATRIX* pMatrix) @@ -110,19 +111,46 @@ void D3D8TransformState::SetTransform(xbox::X_D3DTRANSFORMSTATETYPE state, const // Update transform state this->Transforms[state] = *pMatrix; - // Recalculate dependent matrices - for (int i = 0; i < 4; i++) { - auto worldState = X_D3DTS_WORLD + i; - if (state == X_D3DTS_VIEW || state == worldState) { - D3DXMATRIX worldView; - D3DXMatrixMultiply(&worldView, (D3DXMATRIX*)&Transforms[worldState], (D3DXMATRIX*)&Transforms[X_D3DTS_VIEW]); - this->WorldView[i] = worldView; - - D3DXMATRIX worldViewInverseTranspose; - D3DXMatrixInverse(&worldViewInverseTranspose, nullptr, &worldView); - D3DXMatrixTranspose(&worldViewInverseTranspose, &worldViewInverseTranspose); - - this->WorldViewInverseTranspose[i] = worldViewInverseTranspose; - } + if ((state == X_D3DTS_VIEW) || ((X_D3DTS_WORLD <= state) && (state <= X_D3DTS_WORLD3))) { + bWorldViewDirty = true; } } + +void D3D8TransformState::RecalculateDependentMatrices() +{ + for (unsigned i = 0; i < 4; i++) { + auto worldState = xbox::X_D3DTS_WORLD + i; + D3DXMATRIX worldView; + D3DXMatrixMultiply(&worldView, (D3DXMATRIX*)&Transforms[worldState], (D3DXMATRIX*)&Transforms[xbox::X_D3DTS_VIEW]); + this->WorldView[i] = worldView; + + D3DXMATRIX worldViewInverseTranspose; + D3DXMatrixInverse(&worldViewInverseTranspose, nullptr, &worldView); + D3DXMatrixTranspose(&worldViewInverseTranspose, &worldViewInverseTranspose); + this->WorldViewInverseTranspose[i] = worldViewInverseTranspose; + } +} + +D3DMATRIX* D3D8TransformState::GetWorldView(unsigned i) +{ + assert(i < 4); + + if (bWorldViewDirty) { + RecalculateDependentMatrices(); + bWorldViewDirty = false; + } + + return &WorldView[i]; +} + +D3DMATRIX* D3D8TransformState::GetWorldViewInverseTranspose(unsigned i) +{ + assert(i < 4); + + if (bWorldViewDirty) { + RecalculateDependentMatrices(); + bWorldViewDirty = false; + } + + return &WorldViewInverseTranspose[i]; +} diff --git a/src/core/hle/D3D8/FixedFunctionState.h b/src/core/hle/D3D8/FixedFunctionState.h index 105b32b8e..0b36971a9 100644 --- a/src/core/hle/D3D8/FixedFunctionState.h +++ b/src/core/hle/D3D8/FixedFunctionState.h @@ -27,10 +27,16 @@ class D3D8TransformState { public: D3D8TransformState(); void SetTransform(xbox::X_D3DTRANSFORMSTATETYPE state, const D3DMATRIX* pMatrix); + D3DMATRIX* GetWorldView(unsigned i); + D3DMATRIX* GetWorldViewInverseTranspose(unsigned i); // The transforms set by the Xbox title std::array Transforms; +private: + void RecalculateDependentMatrices(); + + bool bWorldViewDirty; // Combines world/view matrices std::array WorldView; // World/view inverse transpose for lighting calculations From 0245cc6ee8566e309fb96815b74054875fe47404 Mon Sep 17 00:00:00 2001 From: PatrickvL Date: Thu, 10 Dec 2020 12:21:03 +0100 Subject: [PATCH 10/21] Optimized vertex blending by only recalculating the required world/view matrices, and a simplified HLSL implementation --- src/core/hle/D3D8/Direct3D9/Direct3D9.cpp | 30 ++++++++++-- .../Direct3D9/FixedFunctionVertexShader.hlsl | 47 +++++-------------- .../FixedFunctionVertexShaderState.hlsli | 3 +- src/core/hle/D3D8/Direct3D9/RenderStates.cpp | 15 +++--- src/core/hle/D3D8/FixedFunctionState.cpp | 41 ++++++++-------- src/core/hle/D3D8/FixedFunctionState.h | 4 +- src/core/hle/D3D8/XbConvert.h | 2 +- src/core/hle/D3D8/XbD3D8Types.h | 12 +++++ 8 files changed, 85 insertions(+), 69 deletions(-) diff --git a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp index b4777107a..5c0b72c4b 100644 --- a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp +++ b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp @@ -6337,13 +6337,38 @@ void UpdateFixedFunctionVertexShaderState() { using namespace xbox; + // Preprocessing + // Prepare vertex blending mode variables used in transforms, below + auto VertexBlend = XboxRenderStates.GetXboxRenderState(X_D3DRS_VERTEXBLEND); + // Xbox and host D3DVERTEXBLENDFLAGS : + // D3DVBF_DISABLE = 0 : 1 matrix, 0 weights => final weight 1 + // D3DVBF_1WEIGHTS = 1 : 2 matrices, 1 weights => final weight calculated + // D3DVBF_2WEIGHTS = 3 : 3 matrices, 2 weights => final weight calculated + // D3DVBF_3WEIGHTS = 5 : 4 matrices, 3 weights => final weight calculated + // Xbox X_D3DVERTEXBLENDFLAGS : + // X_D3DVBF_2WEIGHTS2MATRICES = 2 : 2 matrices, 2 weights + // X_D3DVBF_3WEIGHTS3MATRICES = 4 : 3 matrices, 3 weights + // X_D3DVBF_4WEIGHTS4MATRICES = 6 : 4 matrices, 4 weights + // + if (VertexBlend > xbox::X_D3DVBF_4WEIGHTS4MATRICES) LOG_TEST_CASE("X_D3DRS_VERTEXBLEND out of range"); + // Calculate the number of matrices, by adding the LSB to turn (0,1,3,5) and (0,2,4,6) into (0,2,4,6); Then divide by 2 to get (0,1,2,3), and add 1 to get 1, 2, 3 or 4 matrices : + auto NrBlendMatrices = ((VertexBlend + (VertexBlend & 1)) / 2) + 1; + // Looking at the above values, 0 or the LSB of VertexBlend signals that the final weight needs to be calculated from all previous weigths (deducting them all from an initial 1) : + auto CalcLastBlendWeight = (VertexBlend == xbox::X_D3DVBF_DISABLE) || (VertexBlend & 1); + // Copy the resulting values over to shader state : + ffShaderState.Modes.VertexBlend_NrOfMatrices = (float)NrBlendMatrices; + ffShaderState.Modes.VertexBlend_CalcLastWeight = (float)CalcLastBlendWeight; + // Transforms // Transpose row major to column major for HLSL D3DXMatrixTranspose((D3DXMATRIX*)&ffShaderState.Transforms.Projection, (D3DXMATRIX*)&d3d8TransformState.Transforms[X_D3DTS_PROJECTION]); D3DXMatrixTranspose((D3DXMATRIX*)&ffShaderState.Transforms.View, (D3DXMATRIX*)&d3d8TransformState.Transforms[X_D3DTS_VIEW]); - for (unsigned i = 0; i < 4; i++) { + for (unsigned i = 0; i < 4; i++) { // TODO : Would it help to limit this to just the active texture channels? D3DXMatrixTranspose((D3DXMATRIX*)&ffShaderState.Transforms.Texture[i], (D3DXMATRIX*)&d3d8TransformState.Transforms[X_D3DTS_TEXTURE0 + i]); + } + + for (unsigned i = 0; i < ffShaderState.Modes.VertexBlend_NrOfMatrices; i++) { D3DXMatrixTranspose((D3DXMATRIX*)&ffShaderState.Transforms.WorldView[i], (D3DXMATRIX*)d3d8TransformState.GetWorldView(i)); D3DXMatrixTranspose((D3DXMATRIX*)&ffShaderState.Transforms.WorldViewInverseTranspose[i], (D3DXMATRIX*)d3d8TransformState.GetWorldViewInverseTranspose(i)); } @@ -6372,7 +6397,7 @@ void UpdateFixedFunctionVertexShaderState() auto pointSize = XboxRenderStates.GetXboxRenderState(X_D3DRS_POINTSIZE); ffShaderState.PointSprite.PointSize = *reinterpret_cast(&pointSize); ffShaderState.PointSprite.PointScaleEnable = (float)XboxRenderStates.GetXboxRenderState(X_D3DRS_POINTSCALEENABLE); - ffShaderState.PointSprite.RenderTargetHeight = GetPixelContainerHeight(g_pXbox_RenderTarget); + ffShaderState.PointSprite.RenderTargetHeight = (float)GetPixelContainerHeight(g_pXbox_RenderTarget); auto scaleA = XboxRenderStates.GetXboxRenderState(X_D3DRS_POINTSCALE_A); ffShaderState.PointSprite.ScaleA = *reinterpret_cast(&scaleA); auto scaleB = XboxRenderStates.GetXboxRenderState(X_D3DRS_POINTSCALE_B); @@ -6428,7 +6453,6 @@ void UpdateFixedFunctionVertexShaderState() } // Misc flags - ffShaderState.Modes.VertexBlend = (float)XboxRenderStates.GetXboxRenderState(X_D3DRS_VERTEXBLEND); ffShaderState.Modes.NormalizeNormals = (float)XboxRenderStates.GetXboxRenderState(X_D3DRS_NORMALIZENORMALS); // Update lights diff --git a/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShader.hlsl b/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShader.hlsl index a57f61429..3214c96de 100644 --- a/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShader.hlsl +++ b/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShader.hlsl @@ -290,44 +290,23 @@ TransformInfo DoTransform(const float4 position, const float3 normal, const floa output.Position = float4(0, 0, 0, 0); output.Normal = float3(0, 0, 0); - // D3D - const int _BLEND_OFF = 0; - const int _1WEIGHT_2MAT = 1; - const int _2WEIGHT_3MAT = 3; - const int _3WEIGHT_4MAT = 5; - // Xbox - const int _2WEIGHT_2MAT = 2; - const int _3WEIGHT_3MAT = 4; - const int _4WEIGHT_4MAT = 6; - - if (state.Modes.VertexBlend == _BLEND_OFF) { - output.Position = mul(position, state.Transforms.WorldView[0]); - output.Normal = mul(normal, (float3x3)state.Transforms.WorldViewInverseTranspose[0]); - return output; - } - - // The number of matrices to blend - int mats = floor((state.Modes.VertexBlend - 1) / 2 + 2); - // If we have to calculate the last blend value - bool calcLastBlend = fmod(state.Modes.VertexBlend, 2) == 1; + // The number of matrices to blend (always in the range [1..4]) + int matrices = state.Modes.VertexBlend_NrOfMatrices; + // Initialize the final matrix its blend weight at 1, from which all preceding blend weights will be deducted : float lastBlend = 1; - for (int i = 0; i < mats - 1; i++) + for (int i = 0; i < matrices; i++) { - output.Position += mul(position, state.Transforms.WorldView[i]) * blendWeights[i]; - output.Normal += mul(normal, (float3x3) state.Transforms.WorldViewInverseTranspose[i]) * blendWeights[i]; + // Do we have to calculate the last blend value (never happens when there's already 4 matrices) ? + bool bCalcFinalWeight = (state.Modes.VertexBlend_CalcLastWeight > 0) && (i == (matrices - 1)); + // Note : In case of X_D3DVBF_DISABLE, no prior weights have been deducted from lastBlend, so it will still be 1. + // The number of matrices will also be 1, which effectively turns this into non-weighted single-matrix multiplications : + float blendWeight = bCalcFinalWeight ? lastBlend : blendWeights[i]; + // Reduce the blend weight for the final matrix : lastBlend -= blendWeights[i]; - } - - if (calcLastBlend) - { - output.Position += mul(position, state.Transforms.WorldView[mats-1]) * lastBlend; - output.Normal += mul(normal, (float3x3) state.Transforms.WorldViewInverseTranspose[mats-1]) * lastBlend; - } - else - { - output.Position += mul(position, state.Transforms.WorldView[mats-1]) * blendWeights[mats-1]; - output.Normal += mul(normal, (float3x3) state.Transforms.WorldViewInverseTranspose[mats-1]) * blendWeights[mats-1]; + // Add this matrix (multiplied by its blend weight) to the output : + output.Position += mul(position, state.Transforms.WorldView[i]) * blendWeight; + output.Normal += mul(normal, (float3x3) state.Transforms.WorldViewInverseTranspose[i]) * blendWeight; } return output; diff --git a/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShaderState.hlsli b/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShaderState.hlsli index 31c223d1b..98b512bfb 100644 --- a/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShaderState.hlsli +++ b/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShaderState.hlsli @@ -98,7 +98,8 @@ struct Modes { alignas(16) float LocalViewer; alignas(16) float ColorVertex; - alignas(16) float VertexBlend; + alignas(16) float VertexBlend_NrOfMatrices; + alignas(16) float VertexBlend_CalcLastWeight; // Could be a bool in higer shader models alignas(16) float NormalizeNormals; }; diff --git a/src/core/hle/D3D8/Direct3D9/RenderStates.cpp b/src/core/hle/D3D8/Direct3D9/RenderStates.cpp index 9183e7df1..9ecd3f4d9 100644 --- a/src/core/hle/D3D8/Direct3D9/RenderStates.cpp +++ b/src/core/hle/D3D8/Direct3D9/RenderStates.cpp @@ -396,14 +396,13 @@ void XboxRenderStateConverter::ApplyComplexRenderState(uint32_t State, uint32_t switch (State) { case xbox::X_D3DRS_VERTEXBLEND: - // convert from Xbox direct3d to PC direct3d enumeration - if (Value <= 1) { - Value = Value; - } else if (Value == 3) { - Value = 2; - } else if (Value == 5) { - Value = 3; - } else { + // convert from Xbox X_D3DVERTEXBLENDFLAGS to PC D3DVERTEXBLENDFLAGS enumeration + switch (Value) { + case xbox::X_D3DVBF_DISABLE: Value = D3DVBF_DISABLE; break; + case xbox::X_D3DVBF_1WEIGHTS: Value = D3DVBF_1WEIGHTS; break; + case xbox::X_D3DVBF_2WEIGHTS: Value = D3DVBF_2WEIGHTS; break; + case xbox::X_D3DVBF_3WEIGHTS: Value = D3DVBF_3WEIGHTS; break; + default: LOG_TEST_CASE("Unsupported D3DVERTEXBLENDFLAGS (%d)"); return; } diff --git a/src/core/hle/D3D8/FixedFunctionState.cpp b/src/core/hle/D3D8/FixedFunctionState.cpp index ac33d1752..c11f76fa2 100644 --- a/src/core/hle/D3D8/FixedFunctionState.cpp +++ b/src/core/hle/D3D8/FixedFunctionState.cpp @@ -94,7 +94,7 @@ D3D8TransformState::D3D8TransformState() { this->Transforms.fill(identity); this->WorldView.fill(identity); this->WorldViewInverseTranspose.fill(identity); - bWorldViewDirty = true; + bWorldViewDirty.fill(true); } void D3D8TransformState::SetTransform(xbox::X_D3DTRANSFORMSTATETYPE state, const D3DMATRIX* pMatrix) @@ -111,33 +111,34 @@ void D3D8TransformState::SetTransform(xbox::X_D3DTRANSFORMSTATETYPE state, const // Update transform state this->Transforms[state] = *pMatrix; - if ((state == X_D3DTS_VIEW) || ((X_D3DTS_WORLD <= state) && (state <= X_D3DTS_WORLD3))) { - bWorldViewDirty = true; + if (state == X_D3DTS_VIEW) { + bWorldViewDirty.fill(true); + } + if ((X_D3DTS_WORLD <= state) && (state <= X_D3DTS_WORLD3)) { + bWorldViewDirty[state - X_D3DTS_WORLD] = true; } } -void D3D8TransformState::RecalculateDependentMatrices() +void D3D8TransformState::RecalculateDependentMatrices(unsigned i) { - for (unsigned i = 0; i < 4; i++) { - auto worldState = xbox::X_D3DTS_WORLD + i; - D3DXMATRIX worldView; - D3DXMatrixMultiply(&worldView, (D3DXMATRIX*)&Transforms[worldState], (D3DXMATRIX*)&Transforms[xbox::X_D3DTS_VIEW]); - this->WorldView[i] = worldView; + auto worldState = xbox::X_D3DTS_WORLD + i; + D3DXMATRIX worldView; + D3DXMatrixMultiply(&worldView, (D3DXMATRIX*)&Transforms[worldState], (D3DXMATRIX*)&Transforms[xbox::X_D3DTS_VIEW]); + this->WorldView[i] = worldView; - D3DXMATRIX worldViewInverseTranspose; - D3DXMatrixInverse(&worldViewInverseTranspose, nullptr, &worldView); - D3DXMatrixTranspose(&worldViewInverseTranspose, &worldViewInverseTranspose); - this->WorldViewInverseTranspose[i] = worldViewInverseTranspose; - } + D3DXMATRIX worldViewInverseTranspose; + D3DXMatrixInverse(&worldViewInverseTranspose, nullptr, &worldView); + D3DXMatrixTranspose(&worldViewInverseTranspose, &worldViewInverseTranspose); + this->WorldViewInverseTranspose[i] = worldViewInverseTranspose; } D3DMATRIX* D3D8TransformState::GetWorldView(unsigned i) { assert(i < 4); - if (bWorldViewDirty) { - RecalculateDependentMatrices(); - bWorldViewDirty = false; + if (bWorldViewDirty[i]) { + RecalculateDependentMatrices(i); + bWorldViewDirty[i] = false; } return &WorldView[i]; @@ -147,9 +148,9 @@ D3DMATRIX* D3D8TransformState::GetWorldViewInverseTranspose(unsigned i) { assert(i < 4); - if (bWorldViewDirty) { - RecalculateDependentMatrices(); - bWorldViewDirty = false; + if (bWorldViewDirty[i]) { + RecalculateDependentMatrices(i); + bWorldViewDirty[i] = false; } return &WorldViewInverseTranspose[i]; diff --git a/src/core/hle/D3D8/FixedFunctionState.h b/src/core/hle/D3D8/FixedFunctionState.h index 0b36971a9..d2dfee9e2 100644 --- a/src/core/hle/D3D8/FixedFunctionState.h +++ b/src/core/hle/D3D8/FixedFunctionState.h @@ -34,9 +34,9 @@ public: std::array Transforms; private: - void RecalculateDependentMatrices(); + void RecalculateDependentMatrices(unsigned i); - bool bWorldViewDirty; + std::array bWorldViewDirty; // Combines world/view matrices std::array WorldView; // World/view inverse transpose for lighting calculations diff --git a/src/core/hle/D3D8/XbConvert.h b/src/core/hle/D3D8/XbConvert.h index 1bf14114c..57cd45c72 100644 --- a/src/core/hle/D3D8/XbConvert.h +++ b/src/core/hle/D3D8/XbConvert.h @@ -1796,7 +1796,7 @@ typedef enum _TXBType { xtD3DTEXTUREOP, // Used for TextureStageState X_D3DTSS_COLOROP and X_D3DTSS_ALPHAOP xtD3DTEXTURESTAGESTATETYPE, xtD3DTRANSFORMSTATETYPE, - xtD3DVERTEXBLENDFLAGS, + xtD3DVERTEXBLENDFLAGS, // Used for X_D3DRS_VERTEXBLEND xtD3DVSDE, xtD3DWRAP, xtDWORD, diff --git a/src/core/hle/D3D8/XbD3D8Types.h b/src/core/hle/D3D8/XbD3D8Types.h index 75e026008..c00a1fbbe 100644 --- a/src/core/hle/D3D8/XbD3D8Types.h +++ b/src/core/hle/D3D8/XbD3D8Types.h @@ -1007,6 +1007,18 @@ constexpr DWORD X_D3DCOLORWRITEENABLE_ALL = 0x01010101; // Xbox ext. // deferred texture stage state "unknown" flag #define X_D3DTSS_UNK 0x7fffffff +typedef enum _D3DVERTEXBLENDFLAGS +{ + X_D3DVBF_DISABLE = 0, // 1 matrix, 0 weights => final weight effectively 1 (Disable vertex blending) + X_D3DVBF_1WEIGHTS = 1, // 2 matrices, 1 weights => final weight calculated + X_D3DVBF_2WEIGHTS = 3, // 3 matrices, 2 weights => final weight calculated + X_D3DVBF_3WEIGHTS = 5, // 4 matrices, 3 weights => final weight calculated + X_D3DVBF_2WEIGHTS2MATRICES = 2, // 2 matrices, 2 weights (Xbox ext.) + X_D3DVBF_3WEIGHTS3MATRICES = 4, // 3 matrices, 3 weights (Xbox ext.) + X_D3DVBF_4WEIGHTS4MATRICES = 6, // 4 matrices, 4 weights (Xbox ext.) + X_D3DVBF_FORCE_DWORD = 0x7fffffff +} X_D3DVERTEXBLENDFLAGS; + typedef DWORD X_VERTEXSHADERCONSTANTMODE; // Xbox vertex shader constant modes From 39e91ffec69ceb801c57f40e04ebb6d5f77a6918 Mon Sep 17 00:00:00 2001 From: PatrickvL Date: Fri, 11 Dec 2020 13:12:27 +0100 Subject: [PATCH 11/21] Fixed Function HLSL : Optimized ColorVertex handling in DoMaterial, by removing it's check froM HLSL, and replace that by CPU replacing all MaterialSource's with D3DMCS_MATERIAL when X_D3DRS_COLORVERTEX is disabled. This shaves off 13 vertex shader instructions. --- src/core/hle/D3D8/Direct3D9/Direct3D9.cpp | 18 ++++---- .../Direct3D9/FixedFunctionVertexShader.hlsl | 44 +++++++++---------- .../FixedFunctionVertexShaderState.hlsli | 1 - 3 files changed, 30 insertions(+), 33 deletions(-) diff --git a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp index 5c0b72c4b..5c21ab15b 100644 --- a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp +++ b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp @@ -6378,20 +6378,20 @@ void UpdateFixedFunctionVertexShaderState() ffShaderState.Modes.TwoSidedLighting = (float)XboxRenderStates.GetXboxRenderState(X_D3DRS_TWOSIDEDLIGHTING); ffShaderState.Modes.SpecularEnable = (float)XboxRenderStates.GetXboxRenderState(X_D3DRS_SPECULARENABLE); ffShaderState.Modes.LocalViewer = (float)XboxRenderStates.GetXboxRenderState(X_D3DRS_LOCALVIEWER); - ffShaderState.Modes.ColorVertex = (float)XboxRenderStates.GetXboxRenderState(X_D3DRS_COLORVERTEX); + bool ColorVertex = XboxRenderStates.GetXboxRenderState(X_D3DRS_COLORVERTEX) != FALSE; D3DXVECTOR4 Ambient = toVector(XboxRenderStates.GetXboxRenderState(X_D3DRS_AMBIENT)); D3DXVECTOR4 BackAmbient = toVector(XboxRenderStates.GetXboxRenderState(X_D3DRS_BACKAMBIENT)); // Material sources - ffShaderState.Modes.AmbientMaterialSource = (float)XboxRenderStates.GetXboxRenderState(X_D3DRS_AMBIENTMATERIALSOURCE); - ffShaderState.Modes.DiffuseMaterialSource = (float)XboxRenderStates.GetXboxRenderState(X_D3DRS_DIFFUSEMATERIALSOURCE); - ffShaderState.Modes.SpecularMaterialSource = (float)XboxRenderStates.GetXboxRenderState(X_D3DRS_SPECULARMATERIALSOURCE); - ffShaderState.Modes.EmissiveMaterialSource = (float)XboxRenderStates.GetXboxRenderState(X_D3DRS_EMISSIVEMATERIALSOURCE); - ffShaderState.Modes.BackAmbientMaterialSource = (float)XboxRenderStates.GetXboxRenderState(X_D3DRS_BACKAMBIENTMATERIALSOURCE); - ffShaderState.Modes.BackDiffuseMaterialSource = (float)XboxRenderStates.GetXboxRenderState(X_D3DRS_BACKDIFFUSEMATERIALSOURCE); - ffShaderState.Modes.BackSpecularMaterialSource = (float)XboxRenderStates.GetXboxRenderState(X_D3DRS_BACKSPECULARMATERIALSOURCE); - ffShaderState.Modes.BackEmissiveMaterialSource = (float)XboxRenderStates.GetXboxRenderState(X_D3DRS_BACKEMISSIVEMATERIALSOURCE); + ffShaderState.Modes.AmbientMaterialSource = (float)(ColorVertex ? XboxRenderStates.GetXboxRenderState(X_D3DRS_AMBIENTMATERIALSOURCE) : D3DMCS_MATERIAL); + ffShaderState.Modes.DiffuseMaterialSource = (float)(ColorVertex ? XboxRenderStates.GetXboxRenderState(X_D3DRS_DIFFUSEMATERIALSOURCE) : D3DMCS_MATERIAL); + ffShaderState.Modes.SpecularMaterialSource = (float)(ColorVertex ? XboxRenderStates.GetXboxRenderState(X_D3DRS_SPECULARMATERIALSOURCE) : D3DMCS_MATERIAL); + ffShaderState.Modes.EmissiveMaterialSource = (float)(ColorVertex ? XboxRenderStates.GetXboxRenderState(X_D3DRS_EMISSIVEMATERIALSOURCE) : D3DMCS_MATERIAL); + ffShaderState.Modes.BackAmbientMaterialSource = (float)(ColorVertex ? XboxRenderStates.GetXboxRenderState(X_D3DRS_BACKAMBIENTMATERIALSOURCE) : D3DMCS_MATERIAL); + ffShaderState.Modes.BackDiffuseMaterialSource = (float)(ColorVertex ? XboxRenderStates.GetXboxRenderState(X_D3DRS_BACKDIFFUSEMATERIALSOURCE) : D3DMCS_MATERIAL); + ffShaderState.Modes.BackSpecularMaterialSource = (float)(ColorVertex ? XboxRenderStates.GetXboxRenderState(X_D3DRS_BACKSPECULARMATERIALSOURCE) : D3DMCS_MATERIAL); + ffShaderState.Modes.BackEmissiveMaterialSource = (float)(ColorVertex ? XboxRenderStates.GetXboxRenderState(X_D3DRS_BACKEMISSIVEMATERIALSOURCE) : D3DMCS_MATERIAL); // Point sprites auto pointSize = XboxRenderStates.GetXboxRenderState(X_D3DRS_POINTSIZE); diff --git a/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShader.hlsl b/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShader.hlsl index 3214c96de..032373f96 100644 --- a/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShader.hlsl +++ b/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShader.hlsl @@ -317,32 +317,30 @@ Material DoMaterial(const uint index, const uint diffuseReg, const uint specular // Get the material from material state Material material = state.Materials[index]; - if (state.Modes.ColorVertex) - { - // https://docs.microsoft.com/en-us/windows/win32/direct3d9/d3dmaterialcolorsource - const int D3DMCS_MATERIAL = 0; - const int D3DMCS_COLOR1 = 1; - const int D3DMCS_COLOR2 = 2; + // Note : If X_D3DRS_COLORVERTEX is FALSE, UpdateFixedFunctionVertexShaderState has already changed all MaterialSource's into D3DMCS_MATERIAL - // TODO preprocess on the CPU - // If COLORVERTEX mode, AND the desired diffuse or specular colour is defined in the vertex declaration - // Then use the vertex colour instead of the material + // https://docs.microsoft.com/en-us/windows/win32/direct3d9/d3dmaterialcolorsource + const int D3DMCS_MATERIAL = 0; + const int D3DMCS_COLOR1 = 1; + const int D3DMCS_COLOR2 = 2; - if (!vRegisterDefaultFlags[diffuseReg]) { - float4 diffuseVertexColour = Get(xIn, diffuseReg); - if (state.Modes.AmbientMaterialSource == D3DMCS_COLOR1) material.Ambient = diffuseVertexColour; - if (state.Modes.DiffuseMaterialSource == D3DMCS_COLOR1) material.Diffuse = diffuseVertexColour; - if (state.Modes.SpecularMaterialSource == D3DMCS_COLOR1) material.Specular = diffuseVertexColour; - if (state.Modes.EmissiveMaterialSource == D3DMCS_COLOR1) material.Emissive = diffuseVertexColour; - } + // If COLORVERTEX mode, AND the desired diffuse or specular colour is defined in the vertex declaration + // Then use the vertex colour instead of the material - if (!vRegisterDefaultFlags[specularReg]) { - float4 specularVertexColour = Get(xIn, specularReg); - if (state.Modes.AmbientMaterialSource == D3DMCS_COLOR2) material.Ambient = specularVertexColour; - if (state.Modes.DiffuseMaterialSource == D3DMCS_COLOR2) material.Diffuse = specularVertexColour; - if (state.Modes.SpecularMaterialSource == D3DMCS_COLOR2) material.Specular = specularVertexColour; - if (state.Modes.EmissiveMaterialSource == D3DMCS_COLOR2) material.Emissive = specularVertexColour; - } + if (!vRegisterDefaultFlags[diffuseReg]) { + float4 diffuseVertexColour = Get(xIn, diffuseReg); + if (state.Modes.AmbientMaterialSource == D3DMCS_COLOR1) material.Ambient = diffuseVertexColour; + if (state.Modes.DiffuseMaterialSource == D3DMCS_COLOR1) material.Diffuse = diffuseVertexColour; + if (state.Modes.SpecularMaterialSource == D3DMCS_COLOR1) material.Specular = diffuseVertexColour; + if (state.Modes.EmissiveMaterialSource == D3DMCS_COLOR1) material.Emissive = diffuseVertexColour; + } + + if (!vRegisterDefaultFlags[specularReg]) { + float4 specularVertexColour = Get(xIn, specularReg); + if (state.Modes.AmbientMaterialSource == D3DMCS_COLOR2) material.Ambient = specularVertexColour; + if (state.Modes.DiffuseMaterialSource == D3DMCS_COLOR2) material.Diffuse = specularVertexColour; + if (state.Modes.SpecularMaterialSource == D3DMCS_COLOR2) material.Specular = specularVertexColour; + if (state.Modes.EmissiveMaterialSource == D3DMCS_COLOR2) material.Emissive = specularVertexColour; } return material; diff --git a/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShaderState.hlsli b/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShaderState.hlsli index 98b512bfb..e8ab2bb64 100644 --- a/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShaderState.hlsli +++ b/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShaderState.hlsli @@ -97,7 +97,6 @@ struct Modes { alignas(16) float SpecularEnable; alignas(16) float LocalViewer; - alignas(16) float ColorVertex; alignas(16) float VertexBlend_NrOfMatrices; alignas(16) float VertexBlend_CalcLastWeight; // Could be a bool in higer shader models alignas(16) float NormalizeNormals; From bd76d67c78a8228f30b2fcea79faf7112e788222 Mon Sep 17 00:00:00 2001 From: PatrickvL Date: Fri, 11 Dec 2020 13:53:41 +0100 Subject: [PATCH 12/21] Indentation fixes & add a 'const' prefix to all single-assigned variables, and 'static' for all literal assignments. --- .../Direct3D9/FixedFunctionVertexShader.hlsl | 326 +++++++++--------- 1 file changed, 164 insertions(+), 162 deletions(-) diff --git a/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShader.hlsl b/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShader.hlsl index 032373f96..0e52a1b23 100644 --- a/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShader.hlsl +++ b/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShader.hlsl @@ -15,14 +15,14 @@ uniform float4 xboxTextureScale[4] : register(c214); struct VS_INPUT { #ifdef CXBX_ALL_TEXCOORD_INPUTS - float4 v[16] : TEXCOORD; + float4 v[16] : TEXCOORD; #else - float4 pos : POSITION; - float4 bw : BLENDWEIGHT; - float4 color[2] : COLOR; - float4 backColor[2] : TEXCOORD4; - float4 normal : NORMAL; - float4 texcoord[4] : TEXCOORD; + float4 pos : POSITION; + float4 bw : BLENDWEIGHT; + float4 color[2] : COLOR; + float4 backColor[2] : TEXCOORD4; + float4 normal : NORMAL; + float4 texcoord[4] : TEXCOORD; #endif }; @@ -51,7 +51,7 @@ static const uint reserved2 = 15; // Has no X_D3DFVF_* / X_D3DVSDE_* float4 Get(const VS_INPUT xIn, const uint index) { #ifdef CXBX_ALL_TEXCOORD_INPUTS - return xIn.v[index]; + return xIn.v[index]; #else // switch statements inexplicably don't work here if(index == position) return xIn.pos; @@ -72,17 +72,17 @@ float4 Get(const VS_INPUT xIn, const uint index) // Output registers struct VS_OUTPUT { - float4 oPos : POSITION; // Homogeneous clip space position - float4 oD0 : COLOR0; // Primary color (front-facing) - float4 oD1 : COLOR1; // Secondary color (front-facing) - float oFog : FOG; // Fog coordinate - float oPts : PSIZE; // Point size - float4 oB0 : TEXCOORD4; // Back-facing primary color - float4 oB1 : TEXCOORD5; // Back-facing secondary color - float4 oT0 : TEXCOORD0; // Texture coordinate set 0 - float4 oT1 : TEXCOORD1; // Texture coordinate set 1 - float4 oT2 : TEXCOORD2; // Texture coordinate set 2 - float4 oT3 : TEXCOORD3; // Texture coordinate set 3 + float4 oPos : POSITION; // Homogeneous clip space position + float4 oD0 : COLOR0; // Primary color (front-facing) + float4 oD1 : COLOR1; // Secondary color (front-facing) + float oFog : FOG; // Fog coordinate + float oPts : PSIZE; // Point size + float4 oB0 : TEXCOORD4; // Back-facing primary color + float4 oB1 : TEXCOORD5; // Back-facing secondary color + float4 oT0 : TEXCOORD0; // Texture coordinate set 0 + float4 oT1 : TEXCOORD1; // Texture coordinate set 1 + float4 oT2 : TEXCOORD2; // Texture coordinate set 2 + float4 oT3 : TEXCOORD3; // Texture coordinate set 3 }; struct TransformInfo @@ -113,18 +113,18 @@ LightingInfo DoSpecular(const float3 toLightVN, const float3 toViewerVN, const f { LightingInfo o; o.Front = o.Back = float3(0, 0, 0); - + // Specular if (state.Modes.SpecularEnable) { // Blinn-Phong // https://learnopengl.com/Advanced-Lighting/Advanced-Lighting - float3 halfway = normalize(toViewerVN + toLightVN); - float NdotH = dot(View.Normal, halfway); + const float3 halfway = normalize(toViewerVN + toLightVN); + const float NdotH = dot(View.Normal, halfway); + + const float3 frontSpecular = pow(abs(NdotH), powers[0]) * lightSpecular.rgb; + const float3 backSpecular = pow(abs(NdotH), powers[1]) * lightSpecular.rgb; - float3 frontSpecular = pow(abs(NdotH), powers[0]) * lightSpecular.rgb; - float3 backSpecular = pow(abs(NdotH), powers[1]) * lightSpecular.rgb; - if (NdotH >= 0) o.Front = frontSpecular; else @@ -143,9 +143,9 @@ LightingOutput DoPointLight(const Light l, const float3 toViewerVN, const float2 o.Specular.Front = o.Specular.Back = float3(0, 0, 0); // Diffuse - float3 toLightV = l.PositionV - View.Position.xyz; - float lightDist = length(toLightV); - float3 toLightVN = normalize(toLightV); + const float3 toLightV = l.PositionV - View.Position.xyz; + const float lightDist = length(toLightV); + const float3 toLightVN = normalize(toLightV); // A(Constant) + A(Linear) * dist + A(Exp) * dist^2 float attenuation = @@ -157,8 +157,8 @@ LightingOutput DoPointLight(const Light l, const float3 toViewerVN, const float2 if (lightDist > l.Range) attenuation = 0; - float NdotL = dot(View.Normal, toLightVN); - float3 lightDiffuse = abs(NdotL) * attenuation * l.Diffuse.rgb; + const float NdotL = dot(View.Normal, toLightVN); + const float3 lightDiffuse = abs(NdotL) * attenuation * l.Diffuse.rgb; if (NdotL >= 0) o.Diffuse.Front = lightDiffuse; @@ -180,16 +180,16 @@ LightingOutput DoSpotLight(const Light l, const float3 toViewerVN, const float2 o.Specular.Front = o.Specular.Back = float3(0, 0, 0); // Diffuse - float3 toLightV = l.PositionV - View.Position.xyz; - float lightDist = length(toLightV); - float3 toLightVN = normalize(toLightV); - float3 toVertexVN = -toLightVN; + const float3 toLightV = l.PositionV - View.Position.xyz; + const float lightDist = length(toLightV); + const float3 toLightVN = normalize(toLightV); + const float3 toVertexVN = -toLightVN; // https://docs.microsoft.com/en-us/windows/win32/direct3d9/light-types - float cosAlpha = dot(l.DirectionVN, toVertexVN); + const float cosAlpha = dot(l.DirectionVN, toVertexVN); // I = ( cos(a) - cos(phi/2) ) / ( cos(theta/2) - cos(phi/2) ) - float spotBase = saturate((cosAlpha - l.CosHalfPhi) / l.SpotIntensityDivisor); - float spotIntensity = pow(spotBase, l.Falloff); + const float spotBase = saturate((cosAlpha - l.CosHalfPhi) / l.SpotIntensityDivisor); + const float spotIntensity = pow(spotBase, l.Falloff); // A(Constant) + A(Linear) * dist + A(Exp) * dist^2 float attenuation = @@ -201,8 +201,8 @@ LightingOutput DoSpotLight(const Light l, const float3 toViewerVN, const float2 if (lightDist > l.Range) attenuation = 0; - float NdotL = dot(View.Normal, toLightVN); - float3 lightDiffuse = abs(NdotL) * attenuation * l.Diffuse.rgb * spotIntensity; + const float NdotL = dot(View.Normal, toLightVN); + const float3 lightDiffuse = abs(NdotL) * attenuation * l.Diffuse.rgb * spotIntensity; if (NdotL >= 0) o.Diffuse.Front = lightDiffuse; @@ -226,9 +226,9 @@ LightingOutput DoDirectionalLight(const Light l, const float3 toViewerVN, const // Diffuse // Intensity from N . L - float3 toLightVN = -l.DirectionVN; - float NdotL = dot(View.Normal, toLightVN); - float3 lightDiffuse = abs(NdotL * l.Diffuse.rgb); + const float3 toLightVN = -l.DirectionVN; + const float NdotL = dot(View.Normal, toLightVN); + const float3 lightDiffuse = abs(NdotL * l.Diffuse.rgb); // Apply light contribution to front or back face // as the case may be @@ -246,10 +246,10 @@ LightingOutput DoDirectionalLight(const Light l, const float3 toViewerVN, const LightingOutput CalcLighting(const float2 powers) { - const int LIGHT_TYPE_NONE = 0; - const int LIGHT_TYPE_POINT = 1; - const int LIGHT_TYPE_SPOT = 2; - const int LIGHT_TYPE_DIRECTIONAL = 3; + static const int LIGHT_TYPE_NONE = 0; + static const int LIGHT_TYPE_POINT = 1; + static const int LIGHT_TYPE_SPOT = 2; + static const int LIGHT_TYPE_DIRECTIONAL = 3; LightingOutput totalLightOutput; totalLightOutput.Diffuse.Front = float3(0, 0, 0); @@ -257,10 +257,10 @@ LightingOutput CalcLighting(const float2 powers) totalLightOutput.Specular.Front = float3(0, 0, 0); totalLightOutput.Specular.Back = float3(0, 0, 0); - float3 toViewerVN = state.Modes.LocalViewer + const float3 toViewerVN = state.Modes.LocalViewer ? float3(0, 0, 1) : normalize(-View.Position.xyz); - + for (uint i = 0; i < 8; i++) { const Light currentLight = state.Lights[i]; @@ -291,17 +291,17 @@ TransformInfo DoTransform(const float4 position, const float3 normal, const floa output.Normal = float3(0, 0, 0); // The number of matrices to blend (always in the range [1..4]) - int matrices = state.Modes.VertexBlend_NrOfMatrices; + const int matrices = state.Modes.VertexBlend_NrOfMatrices; // Initialize the final matrix its blend weight at 1, from which all preceding blend weights will be deducted : float lastBlend = 1; for (int i = 0; i < matrices; i++) { // Do we have to calculate the last blend value (never happens when there's already 4 matrices) ? - bool bCalcFinalWeight = (state.Modes.VertexBlend_CalcLastWeight > 0) && (i == (matrices - 1)); + const bool bCalcFinalWeight = (state.Modes.VertexBlend_CalcLastWeight > 0) && (i == (matrices - 1)); // Note : In case of X_D3DVBF_DISABLE, no prior weights have been deducted from lastBlend, so it will still be 1. // The number of matrices will also be 1, which effectively turns this into non-weighted single-matrix multiplications : - float blendWeight = bCalcFinalWeight ? lastBlend : blendWeights[i]; + const float blendWeight = bCalcFinalWeight ? lastBlend : blendWeights[i]; // Reduce the blend weight for the final matrix : lastBlend -= blendWeights[i]; // Add this matrix (multiplied by its blend weight) to the output : @@ -320,15 +320,15 @@ Material DoMaterial(const uint index, const uint diffuseReg, const uint specular // Note : If X_D3DRS_COLORVERTEX is FALSE, UpdateFixedFunctionVertexShaderState has already changed all MaterialSource's into D3DMCS_MATERIAL // https://docs.microsoft.com/en-us/windows/win32/direct3d9/d3dmaterialcolorsource - const int D3DMCS_MATERIAL = 0; - const int D3DMCS_COLOR1 = 1; - const int D3DMCS_COLOR2 = 2; + static const int D3DMCS_MATERIAL = 0; + static const int D3DMCS_COLOR1 = 1; + static const int D3DMCS_COLOR2 = 2; // If COLORVERTEX mode, AND the desired diffuse or specular colour is defined in the vertex declaration // Then use the vertex colour instead of the material if (!vRegisterDefaultFlags[diffuseReg]) { - float4 diffuseVertexColour = Get(xIn, diffuseReg); + const float4 diffuseVertexColour = Get(xIn, diffuseReg); if (state.Modes.AmbientMaterialSource == D3DMCS_COLOR1) material.Ambient = diffuseVertexColour; if (state.Modes.DiffuseMaterialSource == D3DMCS_COLOR1) material.Diffuse = diffuseVertexColour; if (state.Modes.SpecularMaterialSource == D3DMCS_COLOR1) material.Specular = diffuseVertexColour; @@ -336,7 +336,7 @@ Material DoMaterial(const uint index, const uint diffuseReg, const uint specular } if (!vRegisterDefaultFlags[specularReg]) { - float4 specularVertexColour = Get(xIn, specularReg); + const float4 specularVertexColour = Get(xIn, specularReg); if (state.Modes.AmbientMaterialSource == D3DMCS_COLOR2) material.Ambient = specularVertexColour; if (state.Modes.DiffuseMaterialSource == D3DMCS_COLOR2) material.Diffuse = specularVertexColour; if (state.Modes.SpecularMaterialSource == D3DMCS_COLOR2) material.Specular = specularVertexColour; @@ -348,50 +348,50 @@ Material DoMaterial(const uint index, const uint diffuseReg, const uint specular float DoFog(const VS_INPUT xIn) { - // TODO implement properly - // Until we have pixel shader HLSL we are still leaning on D3D renderstates for fogging - // So we are not doing any fog density calculations here - // http://developer.download.nvidia.com/assets/gamedev/docs/Fog2.pdf + // TODO implement properly + // Until we have pixel shader HLSL we are still leaning on D3D renderstates for fogging + // So we are not doing any fog density calculations here + // http://developer.download.nvidia.com/assets/gamedev/docs/Fog2.pdf - float fogDepth; + float fogDepth; - if (state.Fog.DepthMode == FixedFunctionVertexShader::FOG_DEPTH_NONE) - fogDepth = Get(xIn, specular).a; // In fixed-function mode, fog is passed in the specular alpha - if (state.Fog.DepthMode == FixedFunctionVertexShader::FOG_DEPTH_RANGE) - fogDepth = length(View.Position.xyz); - if (state.Fog.DepthMode == FixedFunctionVertexShader::FOG_DEPTH_Z) - fogDepth = abs(Projection.Position.z); - if (state.Fog.DepthMode == FixedFunctionVertexShader::FOG_DEPTH_W) - fogDepth = Projection.Position.w; + if (state.Fog.DepthMode == FixedFunctionVertexShader::FOG_DEPTH_NONE) + fogDepth = Get(xIn, specular).a; // In fixed-function mode, fog is passed in the specular alpha + if (state.Fog.DepthMode == FixedFunctionVertexShader::FOG_DEPTH_RANGE) + fogDepth = length(View.Position.xyz); + if (state.Fog.DepthMode == FixedFunctionVertexShader::FOG_DEPTH_Z) + fogDepth = abs(Projection.Position.z); + if (state.Fog.DepthMode == FixedFunctionVertexShader::FOG_DEPTH_W) + fogDepth = Projection.Position.w; - return fogDepth; + return fogDepth; } float4 DoTexCoord(const uint stage, const VS_INPUT xIn) { // Texture transform flags // https://docs.microsoft.com/en-gb/windows/win32/direct3d9/d3dtexturetransformflags - const int D3DTTFF_DISABLE = 0; - const int D3DTTFF_COUNT1 = 1; - const int D3DTTFF_COUNT2 = 2; - const int D3DTTFF_COUNT3 = 3; - const int D3DTTFF_COUNT4 = 4; - const int D3DTTFF_PROJECTED = 256; // This is the only real flag + static const int D3DTTFF_DISABLE = 0; + static const int D3DTTFF_COUNT1 = 1; + static const int D3DTTFF_COUNT2 = 2; + static const int D3DTTFF_COUNT3 = 3; + static const int D3DTTFF_COUNT4 = 4; + static const int D3DTTFF_PROJECTED = 256; // This is the only real flag // https://docs.microsoft.com/en-us/windows/win32/direct3d9/d3dtss-tci // Pre-shifted - const int TCI_PASSTHRU = 0; - const int TCI_CAMERASPACENORMAL = 1; - const int TCI_CAMERASPACEPOSITION = 2; - const int TCI_CAMERASPACEREFLECTIONVECTOR = 3; - const int TCI_OBJECT = 4; // Xbox - const int TCI_SPHERE = 5; // Xbox + static const int TCI_PASSTHRU = 0; + static const int TCI_CAMERASPACENORMAL = 1; + static const int TCI_CAMERASPACEPOSITION = 2; + static const int TCI_CAMERASPACEREFLECTIONVECTOR = 3; + static const int TCI_OBJECT = 4; // Xbox + static const int TCI_SPHERE = 5; // Xbox const TextureState tState = state.TextureStates[stage]; - + // Extract transform flags - int countFlag = tState.TextureTransformFlagsCount; - bool projected = tState.TextureTransformFlagsProjected; + const int countFlag = tState.TextureTransformFlagsCount; + const bool projected = tState.TextureTransformFlagsProjected; // Get texture coordinates // Coordinates are either from the vertex texcoord data @@ -400,52 +400,51 @@ float4 DoTexCoord(const uint stage, const VS_INPUT xIn) if (tState.TexCoordIndexGen == TCI_PASSTHRU) { // Get from vertex data - uint texCoordIndex = abs(tState.TexCoordIndex); // Note : abs() avoids error X3548 : in vs_3_0 uints can only be used with known - positive values, use int if possible + const uint texCoordIndex = abs(tState.TexCoordIndex); // Note : abs() avoids error X3548 : in vs_3_0 uints can only be used with known - positive values, use int if possible texCoord = Get(xIn, texcoord0+texCoordIndex); - // Make coordinates homogenous - // For example, if a title supplies (u, v) - // We need to make transform (u, v, 1) to allow translation with a 3x3 matrix - // We'll need to get this from the current FVF or VertexDeclaration - // Test case: JSRF scrolling texture effect. - // Test case: Madagascar shadows - // Test case: Modify pixel shader sample + // Make coordinates homogenous + // For example, if a title supplies (u, v) + // We need to make transform (u, v, 1) to allow translation with a 3x3 matrix + // We'll need to get this from the current FVF or VertexDeclaration + // Test case: JSRF scrolling texture effect. + // Test case: Madagascar shadows + // Test case: Modify pixel shader sample - // TODO move alongside the texture transformation when it stops angering the HLSL compiler - float componentCount = state.TexCoordComponentCount[texCoordIndex]; - if (componentCount == 1) - texCoord.yzw = float3(1, 0, 0); - if (componentCount == 2) - texCoord.zw = float2(1, 0); - if (componentCount == 3) - texCoord.w = 1; - } - else - { - // Generate texture coordinates - float3 reflected = reflect(normalize(View.Position.xyz), View.Normal); - - if (tState.TexCoordIndexGen == TCI_CAMERASPACENORMAL) - texCoord = float4(View.Normal, 1); - else if (tState.TexCoordIndexGen == TCI_CAMERASPACEPOSITION) - texCoord = View.Position; - else if (tState.TexCoordIndexGen == TCI_CAMERASPACEREFLECTIONVECTOR) - texCoord.xyz = reflected; + // TODO move alongside the texture transformation when it stops angering the HLSL compiler + const float componentCount = state.TexCoordComponentCount[texCoordIndex]; + if (componentCount == 1) + texCoord.yzw = float3(1, 0, 0); + if (componentCount == 2) + texCoord.zw = float2(1, 0); + if (componentCount == 3) + texCoord.w = 1; + } // Generate texture coordinates + else if (tState.TexCoordIndexGen == TCI_CAMERASPACENORMAL) + texCoord = float4(View.Normal, 1); + else if (tState.TexCoordIndexGen == TCI_CAMERASPACEPOSITION) + texCoord = View.Position; + else + { + const float3 reflected = reflect(normalize(View.Position.xyz), View.Normal); + + if (tState.TexCoordIndexGen == TCI_CAMERASPACEREFLECTIONVECTOR) + texCoord.xyz = reflected; // else if TCI_OBJECT TODO is this just model position? - else if (tState.TexCoordIndexGen == TCI_SPHERE) - { + else if (tState.TexCoordIndexGen == TCI_SPHERE) + { // TODO verify // http://www.bluevoid.com/opengl/sig99/advanced99/notes/node177.html - float3 R = reflected; - float p = sqrt(pow(R.x, 2) + pow(R.y, 2) + pow(R.z + 1, 2)); - texCoord.x = R.x / 2 * p + 0.5f; - texCoord.y = R.y / 2 * p + 0.5f; - } - } + const float3 R = reflected; + const float p = sqrt(pow(R.x, 2) + pow(R.y, 2) + pow(R.z + 1, 2)); + texCoord.x = R.x / 2 * p + 0.5f; + texCoord.y = R.y / 2 * p + 0.5f; + } + } // Transform the texture coordinates if requested if (countFlag != D3DTTFF_DISABLE) - texCoord = mul(texCoord, state.Transforms.Texture[stage]); + texCoord = mul(texCoord, state.Transforms.Texture[stage]); // We always send four coordinates // If we are supposed to send less than four @@ -463,8 +462,8 @@ float4 DoTexCoord(const uint stage, const VS_INPUT xIn) if (countFlag == 3) texCoord.w = texCoord.z; } - - return texCoord; + + return texCoord; } // Point size for Point Sprites @@ -472,12 +471,14 @@ float4 DoTexCoord(const uint stage, const VS_INPUT xIn) // Test case: Point sprite sample float DoPointSpriteSize() { - PointSprite ps = state.PointSprite; + const PointSprite ps = state.PointSprite; float pointSize = ps.PointSize; + if (ps.PointScaleEnable) { - float eyeDistance = length(View.Position); - float factor = ps.ScaleA + ps.ScaleB * eyeDistance + ps.ScaleC * (eyeDistance * eyeDistance); + const float eyeDistance = length(View.Position); + const float factor = ps.ScaleA + ps.ScaleB * eyeDistance + ps.ScaleC * (eyeDistance * eyeDistance); + pointSize *= ps.RenderTargetHeight * sqrt(1 / factor); } @@ -490,23 +491,24 @@ VS_INPUT InitializeInputRegisters(const VS_INPUT xInput) // Initialize input registers from the vertex buffer data // Or use the register's default value (which can be changed by the title) - for (uint i = 0; i < 16; i++) { - float4 value = lerp(Get(xInput, i), vRegisterDefaultValues[i], vRegisterDefaultFlags[i]); + for (uint i = 0; i < 16; i++) + { + const float4 value = lerp(Get(xInput, i), vRegisterDefaultValues[i], vRegisterDefaultFlags[i]); #ifdef CXBX_ALL_TEXCOORD_INPUTS xIn.v[i] = value; #else - // switch statements inexplicably don't work here - if(i == position) xIn.pos = value; - if(i == weight) xIn.bw = value; - if(i == normal) xIn.normal = value; - if(i == diffuse) xIn.color[0] = value; - if(i == specular) xIn.color[1] = value; - if(i == backDiffuse) xIn.backColor[0] = value; - if(i == backSpecular) xIn.backColor[1] = value; - if(i == texcoord0) xIn.texcoord[0] = value; - if(i == texcoord1) xIn.texcoord[1] = value; - if(i == texcoord2) xIn.texcoord[2] = value; - if(i == texcoord3) xIn.texcoord[3] = value; + // switch statements inexplicably don't work here + if(i == position) xIn.pos = value; + if(i == weight) xIn.bw = value; + if(i == normal) xIn.normal = value; + if(i == diffuse) xIn.color[0] = value; + if(i == specular) xIn.color[1] = value; + if(i == backDiffuse) xIn.backColor[0] = value; + if(i == backSpecular) xIn.backColor[1] = value; + if(i == texcoord0) xIn.texcoord[0] = value; + if(i == texcoord1) xIn.texcoord[1] = value; + if(i == texcoord2) xIn.texcoord[2] = value; + if(i == texcoord3) xIn.texcoord[3] = value; #endif } @@ -518,7 +520,7 @@ VS_OUTPUT main(const VS_INPUT xInput) VS_OUTPUT xOut; // Unpack 16 bool flags from 4 float4 constant registers - vRegisterDefaultFlags = (bool[16]) vRegisterDefaultFlagsPacked; + vRegisterDefaultFlags = (bool[16]) vRegisterDefaultFlagsPacked; // TODO make sure this goes fast @@ -532,9 +534,9 @@ VS_OUTPUT main(const VS_INPUT xInput) if (state.Modes.NormalizeNormals) View.Normal = normalize(View.Normal); - // Projection transform - Projection.Position = mul(View.Position, state.Transforms.Projection); - // Normal unused... + // Projection transform + Projection.Position = mul(View.Position, state.Transforms.Projection); + // Normal unused... // Projection transform - final position xOut.oPos = Projection.Position; @@ -543,25 +545,25 @@ VS_OUTPUT main(const VS_INPUT xInput) if (state.Modes.Lighting || state.Modes.TwoSidedLighting) { // Materials - Material material = DoMaterial(0, diffuse, specular, xIn); - Material backMaterial = DoMaterial(1, backDiffuse, backSpecular, xIn); - - float2 powers = float2(material.Power, backMaterial.Power); + const Material material = DoMaterial(0, diffuse, specular, xIn); + const Material backMaterial = DoMaterial(1, backDiffuse, backSpecular, xIn); + + const float2 powers = float2(material.Power, backMaterial.Power); LightingOutput lighting = CalcLighting(powers); // Compute each lighting component - float3 _ambient = material.Ambient.rgb * state.AmbientPlusLightAmbient.rgb; - float3 _backAmbient = backMaterial.Ambient.rgb * state.BackAmbientPlusLightAmbient.rgb; - - float3 _diffuse = material.Diffuse.rgb * lighting.Diffuse.Front; - float3 _backDiffuse = backMaterial.Diffuse.rgb * lighting.Diffuse.Back; - - float3 _specular = material.Specular.rgb * lighting.Specular.Front; - float3 _backSpecular = backMaterial.Specular.rgb * lighting.Specular.Back; - - float3 _emissive = material.Emissive.rgb; - float3 _backEmissive = backMaterial.Emissive.rgb; + const float3 _ambient = material.Ambient.rgb * state.AmbientPlusLightAmbient.rgb; + const float3 _backAmbient = backMaterial.Ambient.rgb * state.BackAmbientPlusLightAmbient.rgb; + + const float3 _diffuse = material.Diffuse.rgb * lighting.Diffuse.Front; + const float3 _backDiffuse = backMaterial.Diffuse.rgb * lighting.Diffuse.Back; + + const float3 _specular = material.Specular.rgb * lighting.Specular.Front; + const float3 _backSpecular = backMaterial.Specular.rgb * lighting.Specular.Back; + + const float3 _emissive = material.Emissive.rgb; + const float3 _backEmissive = backMaterial.Emissive.rgb; // Frontface xOut.oD0 = float4(_ambient + _diffuse + _emissive, material.Diffuse.a); @@ -592,7 +594,7 @@ VS_OUTPUT main(const VS_INPUT xInput) xOut.oB1 = saturate(xOut.oB1); // Fog - xOut.oFog = DoFog(xIn); + xOut.oFog = DoFog(xIn); // Point Sprite xOut.oPts = DoPointSpriteSize(); @@ -603,5 +605,5 @@ VS_OUTPUT main(const VS_INPUT xInput) xOut.oT2 = DoTexCoord(2, xIn) / xboxTextureScale[2]; xOut.oT3 = DoTexCoord(3, xIn) / xboxTextureScale[3]; - return xOut; + return xOut; } From 134dd7011744b5cfd33807e621a58774622584d8 Mon Sep 17 00:00:00 2001 From: PatrickvL Date: Fri, 11 Dec 2020 18:00:43 +0100 Subject: [PATCH 13/21] Optimize away SpecularEnable and PointScaleEnable, by resetting the related variables on CPU. This shaves off almost 50 vertex shader instructions (down to 705 with this). --- src/core/hle/D3D8/Direct3D9/Direct3D9.cpp | 37 +++-- .../Direct3D9/FixedFunctionVertexShader.hlsl | 132 ++++++++---------- .../FixedFunctionVertexShaderState.hlsli | 16 ++- 3 files changed, 87 insertions(+), 98 deletions(-) diff --git a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp index 5c21ab15b..a236eb2f2 100644 --- a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp +++ b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp @@ -6309,10 +6309,12 @@ void UpdateFixedFunctionShaderLight(int d3dLightIndex, Light* pShaderLight, D3DX D3DXVec3Transform(&directionV, (D3DXVECTOR3*)&d3dLight->Direction, &viewTransform3x3); D3DXVec3Normalize((D3DXVECTOR3*)&pShaderLight->DirectionVN, (D3DXVECTOR3*)&directionV); + bool SpecularEnable = XboxRenderStates.GetXboxRenderState(xbox::X_D3DRS_SPECULARENABLE) != FALSE; + // Map D3D light to state struct pShaderLight->Type = (float)((int)d3dLight->Type); pShaderLight->Diffuse = toVector(d3dLight->Diffuse); - pShaderLight->Specular = toVector(d3dLight->Specular); + pShaderLight->Specular = SpecularEnable ? toVector(d3dLight->Specular) : toVector(0); pShaderLight->Range = d3dLight->Range; pShaderLight->Falloff = d3dLight->Falloff; pShaderLight->Attenuation.x = d3dLight->Attenuation0; @@ -6337,7 +6339,7 @@ void UpdateFixedFunctionVertexShaderState() { using namespace xbox; - // Preprocessing + // Vertex blending // Prepare vertex blending mode variables used in transforms, below auto VertexBlend = XboxRenderStates.GetXboxRenderState(X_D3DRS_VERTEXBLEND); // Xbox and host D3DVERTEXBLENDFLAGS : @@ -6376,14 +6378,10 @@ void UpdateFixedFunctionVertexShaderState() // Lighting ffShaderState.Modes.Lighting = (float)XboxRenderStates.GetXboxRenderState(X_D3DRS_LIGHTING); ffShaderState.Modes.TwoSidedLighting = (float)XboxRenderStates.GetXboxRenderState(X_D3DRS_TWOSIDEDLIGHTING); - ffShaderState.Modes.SpecularEnable = (float)XboxRenderStates.GetXboxRenderState(X_D3DRS_SPECULARENABLE); ffShaderState.Modes.LocalViewer = (float)XboxRenderStates.GetXboxRenderState(X_D3DRS_LOCALVIEWER); - bool ColorVertex = XboxRenderStates.GetXboxRenderState(X_D3DRS_COLORVERTEX) != FALSE; - - D3DXVECTOR4 Ambient = toVector(XboxRenderStates.GetXboxRenderState(X_D3DRS_AMBIENT)); - D3DXVECTOR4 BackAmbient = toVector(XboxRenderStates.GetXboxRenderState(X_D3DRS_BACKAMBIENT)); // Material sources + bool ColorVertex = XboxRenderStates.GetXboxRenderState(X_D3DRS_COLORVERTEX) != FALSE; ffShaderState.Modes.AmbientMaterialSource = (float)(ColorVertex ? XboxRenderStates.GetXboxRenderState(X_D3DRS_AMBIENTMATERIALSOURCE) : D3DMCS_MATERIAL); ffShaderState.Modes.DiffuseMaterialSource = (float)(ColorVertex ? XboxRenderStates.GetXboxRenderState(X_D3DRS_DIFFUSEMATERIALSOURCE) : D3DMCS_MATERIAL); ffShaderState.Modes.SpecularMaterialSource = (float)(ColorVertex ? XboxRenderStates.GetXboxRenderState(X_D3DRS_SPECULARMATERIALSOURCE) : D3DMCS_MATERIAL); @@ -6396,14 +6394,14 @@ void UpdateFixedFunctionVertexShaderState() // Point sprites auto pointSize = XboxRenderStates.GetXboxRenderState(X_D3DRS_POINTSIZE); ffShaderState.PointSprite.PointSize = *reinterpret_cast(&pointSize); - ffShaderState.PointSprite.PointScaleEnable = (float)XboxRenderStates.GetXboxRenderState(X_D3DRS_POINTSCALEENABLE); - ffShaderState.PointSprite.RenderTargetHeight = (float)GetPixelContainerHeight(g_pXbox_RenderTarget); + bool PointScaleEnable = XboxRenderStates.GetXboxRenderState(X_D3DRS_POINTSCALEENABLE) != FALSE; + ffShaderState.PointSprite.RenderTargetHeight = PointScaleEnable ? (float)GetPixelContainerHeight(g_pXbox_RenderTarget) : 1.0f; auto scaleA = XboxRenderStates.GetXboxRenderState(X_D3DRS_POINTSCALE_A); - ffShaderState.PointSprite.ScaleA = *reinterpret_cast(&scaleA); + ffShaderState.PointSprite.ScaleA = PointScaleEnable ? *reinterpret_cast(&scaleA) : 1.0f; auto scaleB = XboxRenderStates.GetXboxRenderState(X_D3DRS_POINTSCALE_B); - ffShaderState.PointSprite.ScaleB = *reinterpret_cast(&scaleB); + ffShaderState.PointSprite.ScaleB = PointScaleEnable ? *reinterpret_cast(&scaleB) : 0.0f; auto scaleC = XboxRenderStates.GetXboxRenderState(X_D3DRS_POINTSCALE_C); - ffShaderState.PointSprite.ScaleC = *reinterpret_cast(&scaleC); + ffShaderState.PointSprite.ScaleC = PointScaleEnable ? *reinterpret_cast(&scaleC) : 0.0f; // Fog // Determine how fog depth is calculated @@ -6452,9 +6450,6 @@ void UpdateFixedFunctionVertexShaderState() ffShaderState.TexCoordComponentCount[i] = (float)GetXboxVertexDataComponentCount(vertexDataFormat); } - // Misc flags - ffShaderState.Modes.NormalizeNormals = (float)XboxRenderStates.GetXboxRenderState(X_D3DRS_NORMALIZENORMALS); - // Update lights auto LightAmbient = D3DXVECTOR4(0.f, 0.f, 0.f, 0.f); D3DXMATRIX rowMajorViewTransform; @@ -6463,8 +6458,14 @@ void UpdateFixedFunctionVertexShaderState() UpdateFixedFunctionShaderLight(d3d8LightState.EnabledLights[i], &ffShaderState.Lights[i], &LightAmbient, rowMajorViewTransform); } - ffShaderState.AmbientPlusLightAmbient = Ambient + LightAmbient; - ffShaderState.BackAmbientPlusLightAmbient = BackAmbient + LightAmbient; + D3DXVECTOR4 Ambient = toVector(XboxRenderStates.GetXboxRenderState(X_D3DRS_AMBIENT)); + D3DXVECTOR4 BackAmbient = toVector(XboxRenderStates.GetXboxRenderState(X_D3DRS_BACKAMBIENT)); + + ffShaderState.TotalLightsAmbient.Front = (D3DXVECTOR3)(LightAmbient + Ambient); + ffShaderState.TotalLightsAmbient.Back = (D3DXVECTOR3)(LightAmbient + BackAmbient); + + // Misc flags + ffShaderState.Modes.NormalizeNormals = (float)XboxRenderStates.GetXboxRenderState(X_D3DRS_NORMALIZENORMALS); // Write fixed function state to shader constants const int slotSize = 16; @@ -7344,8 +7345,6 @@ void CxbxUpdateHostTextures() void CxbxUpdateHostTextureScaling() { - extern xbox::X_VERTEXATTRIBUTEFORMAT* GetXboxVertexAttributeFormat(); // TMP glue - // Xbox works with "Linear" and "Swizzled" texture formats // Linear formats are not addressed with normalized coordinates (similar to https://www.khronos.org/opengl/wiki/Rectangle_Texture?) // We want to use normalized coordinates in our shaders, so need to be able to scale the coordinates back diff --git a/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShader.hlsl b/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShader.hlsl index 0e52a1b23..93b3ca62e 100644 --- a/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShader.hlsl +++ b/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShader.hlsl @@ -94,28 +94,20 @@ struct TransformInfo static TransformInfo View; // Vertex transformed to viewspace static TransformInfo Projection; // Vertex transformed to projection space -// Vertex lighting -// Both frontface and backface lighting can be calculated -struct LightingInfo -{ - float3 Front; - float3 Back; -}; - // Final lighting output struct LightingOutput { - LightingInfo Diffuse; - LightingInfo Specular; + TwoSidedColor Diffuse; + TwoSidedColor Specular; }; -LightingInfo DoSpecular(const float3 toLightVN, const float3 toViewerVN, const float2 powers, const float4 lightSpecular) +TwoSidedColor DoSpecular(const float3 toLightVN, const float3 toViewerVN, const float2 powers, const float4 lightSpecular) { - LightingInfo o; - o.Front = o.Back = float3(0, 0, 0); + TwoSidedColor Specular; + Specular.Front = Specular.Back = float3(0, 0, 0); // Specular - if (state.Modes.SpecularEnable) + // Note : if (state.Modes.SpecularEnable) no longer required because when disabled, CPU sets all lightSpecular.rgb inputs to 0 { // Blinn-Phong // https://learnopengl.com/Advanced-Lighting/Advanced-Lighting @@ -126,12 +118,12 @@ LightingInfo DoSpecular(const float3 toLightVN, const float3 toViewerVN, const f const float3 backSpecular = pow(abs(NdotH), powers[1]) * lightSpecular.rgb; if (NdotH >= 0) - o.Front = frontSpecular; + Specular.Front = frontSpecular; else - o.Back = backSpecular; + Specular.Back = backSpecular; } - return o; + return Specular; } // useful reference https://drivers.amd.com/misc/samples/dx9/FixedFuncShader.pdf @@ -317,30 +309,31 @@ Material DoMaterial(const uint index, const uint diffuseReg, const uint specular // Get the material from material state Material material = state.Materials[index]; - // Note : If X_D3DRS_COLORVERTEX is FALSE, UpdateFixedFunctionVertexShaderState has already changed all MaterialSource's into D3DMCS_MATERIAL + // Note : if (state.Modes.ColorVertex) no longer required because when disabled, CPU sets all MaterialSource's to D3DMCS_MATERIAL + { + // https://docs.microsoft.com/en-us/windows/win32/direct3d9/d3dmaterialcolorsource + static const int D3DMCS_MATERIAL = 0; + static const int D3DMCS_COLOR1 = 1; + static const int D3DMCS_COLOR2 = 2; - // https://docs.microsoft.com/en-us/windows/win32/direct3d9/d3dmaterialcolorsource - static const int D3DMCS_MATERIAL = 0; - static const int D3DMCS_COLOR1 = 1; - static const int D3DMCS_COLOR2 = 2; + // If COLORVERTEX mode, AND the desired diffuse or specular colour is defined in the vertex declaration + // Then use the vertex colour instead of the material - // If COLORVERTEX mode, AND the desired diffuse or specular colour is defined in the vertex declaration - // Then use the vertex colour instead of the material + if (!vRegisterDefaultFlags[diffuseReg]) { + const float4 diffuseVertexColour = Get(xIn, diffuseReg); + if (state.Modes.AmbientMaterialSource == D3DMCS_COLOR1) material.Ambient = diffuseVertexColour; + if (state.Modes.DiffuseMaterialSource == D3DMCS_COLOR1) material.Diffuse = diffuseVertexColour; + if (state.Modes.SpecularMaterialSource == D3DMCS_COLOR1) material.Specular = diffuseVertexColour; + if (state.Modes.EmissiveMaterialSource == D3DMCS_COLOR1) material.Emissive = diffuseVertexColour; + } - if (!vRegisterDefaultFlags[diffuseReg]) { - const float4 diffuseVertexColour = Get(xIn, diffuseReg); - if (state.Modes.AmbientMaterialSource == D3DMCS_COLOR1) material.Ambient = diffuseVertexColour; - if (state.Modes.DiffuseMaterialSource == D3DMCS_COLOR1) material.Diffuse = diffuseVertexColour; - if (state.Modes.SpecularMaterialSource == D3DMCS_COLOR1) material.Specular = diffuseVertexColour; - if (state.Modes.EmissiveMaterialSource == D3DMCS_COLOR1) material.Emissive = diffuseVertexColour; - } - - if (!vRegisterDefaultFlags[specularReg]) { - const float4 specularVertexColour = Get(xIn, specularReg); - if (state.Modes.AmbientMaterialSource == D3DMCS_COLOR2) material.Ambient = specularVertexColour; - if (state.Modes.DiffuseMaterialSource == D3DMCS_COLOR2) material.Diffuse = specularVertexColour; - if (state.Modes.SpecularMaterialSource == D3DMCS_COLOR2) material.Specular = specularVertexColour; - if (state.Modes.EmissiveMaterialSource == D3DMCS_COLOR2) material.Emissive = specularVertexColour; + if (!vRegisterDefaultFlags[specularReg]) { + const float4 specularVertexColour = Get(xIn, specularReg); + if (state.Modes.AmbientMaterialSource == D3DMCS_COLOR2) material.Ambient = specularVertexColour; + if (state.Modes.DiffuseMaterialSource == D3DMCS_COLOR2) material.Diffuse = specularVertexColour; + if (state.Modes.SpecularMaterialSource == D3DMCS_COLOR2) material.Specular = specularVertexColour; + if (state.Modes.EmissiveMaterialSource == D3DMCS_COLOR2) material.Emissive = specularVertexColour; + } } return material; @@ -474,7 +467,7 @@ float DoPointSpriteSize() const PointSprite ps = state.PointSprite; float pointSize = ps.PointSize; - if (ps.PointScaleEnable) + // Note : if (ps.PointScaleEnable) not required because when disabled, CPU sets RenderTargetHeight and ScaleA to 1, and ScaleB and ScaleC to 0 { const float eyeDistance = length(View.Position); const float factor = ps.ScaleA + ps.ScaleB * eyeDistance + ps.ScaleC * (eyeDistance * eyeDistance); @@ -541,50 +534,39 @@ VS_OUTPUT main(const VS_INPUT xInput) // Projection transform - final position xOut.oPos = Projection.Position; + // Diffuse and specular for when lighting is disabled + xOut.oD0 = Get(xIn, diffuse); + xOut.oD1 = Get(xIn, specular); + xOut.oB0 = Get(xIn, backDiffuse); + xOut.oB1 = Get(xIn, backSpecular); + // Vertex lighting - if (state.Modes.Lighting || state.Modes.TwoSidedLighting) + if (state.Modes.Lighting) // TODO : Remove this check by incorporating this boolean into the variables used below (set DiffuseMaterialSource to D3DMCS_COLOR1, SpecularMaterialSource to D3DMCS_COLOR2, all other to D3DMCS_MATERIAL and their colors and TotalLightsAmbient to zero, etc) { // Materials - const Material material = DoMaterial(0, diffuse, specular, xIn); - const Material backMaterial = DoMaterial(1, backDiffuse, backSpecular, xIn); - - const float2 powers = float2(material.Power, backMaterial.Power); - - LightingOutput lighting = CalcLighting(powers); + Material material = DoMaterial(0, diffuse, specular, xIn); + Material backMaterial = DoMaterial(1, backDiffuse, backSpecular, xIn); // Compute each lighting component - const float3 _ambient = material.Ambient.rgb * state.AmbientPlusLightAmbient.rgb; - const float3 _backAmbient = backMaterial.Ambient.rgb * state.BackAmbientPlusLightAmbient.rgb; - - const float3 _diffuse = material.Diffuse.rgb * lighting.Diffuse.Front; - const float3 _backDiffuse = backMaterial.Diffuse.rgb * lighting.Diffuse.Back; - - const float3 _specular = material.Specular.rgb * lighting.Specular.Front; - const float3 _backSpecular = backMaterial.Specular.rgb * lighting.Specular.Back; - - const float3 _emissive = material.Emissive.rgb; - const float3 _backEmissive = backMaterial.Emissive.rgb; + const float2 powers = float2(material.Power, backMaterial.Power); + const LightingOutput lighting = CalcLighting(powers); // Frontface - xOut.oD0 = float4(_ambient + _diffuse + _emissive, material.Diffuse.a); - xOut.oD1 = float4(_specular, 0); - // Backface - xOut.oB0 = float4(_backAmbient + _backDiffuse + _backEmissive, backMaterial.Diffuse.a); - xOut.oB1 = float4(_backSpecular, 0); - } + material.Specular.rgb *= lighting.Specular.Front; + material.Diffuse.rgb *= lighting.Diffuse.Front; + material.Ambient.rgb *= state.TotalLightsAmbient.Front; + xOut.oD0 = float4(material.Diffuse.rgb + material.Ambient.rgb + material.Emissive.rgb, material.Diffuse.a); + xOut.oD1 = float4(material.Specular.rgb, 0); - // TODO does TwoSidedLighting imply Lighting? Verify if TwoSidedLighting can be enabled independently of Lighting - // Diffuse and specular for when lighting is disabled - if (!state.Modes.Lighting) - { - xOut.oD0 = Get(xIn, diffuse); - xOut.oD1 = Get(xIn, specular); - } - - if(!state.Modes.TwoSidedLighting) - { - xOut.oB0 = Get(xIn, backDiffuse); - xOut.oB1 = Get(xIn, backSpecular); + if(state.Modes.TwoSidedLighting) // TODO : Same as above, for backface lighting variables + { + // Backface + backMaterial.Specular.rgb *= lighting.Specular.Back; + backMaterial.Diffuse.rgb *= lighting.Diffuse.Back; + backMaterial.Ambient.rgb *= state.TotalLightsAmbient.Back; + xOut.oB0 = float4(backMaterial.Diffuse.rgb + backMaterial.Ambient.rgb + backMaterial.Emissive.rgb, backMaterial.Diffuse.a); + xOut.oB1 = float4(backMaterial.Specular.rgb, 0); + } } // Colour diff --git a/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShaderState.hlsli b/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShaderState.hlsli index e8ab2bb64..77fb72a1a 100644 --- a/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShaderState.hlsli +++ b/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShaderState.hlsli @@ -94,9 +94,10 @@ struct Modes { alignas(16) float Lighting; alignas(16) float TwoSidedLighting; - alignas(16) float SpecularEnable; +// alignas(16) float SpecularEnable; alignas(16) float LocalViewer; +/// alignas(16) float ColorVertex; alignas(16) float VertexBlend_NrOfMatrices; alignas(16) float VertexBlend_CalcLastWeight; // Could be a bool in higer shader models alignas(16) float NormalizeNormals; @@ -104,7 +105,7 @@ struct Modes { struct PointSprite { alignas(16) float PointSize; - alignas(16) float PointScaleEnable; +// alignas(16) float PointScaleEnable; alignas(16) float RenderTargetHeight; alignas(16) float ScaleA; alignas(16) float ScaleB; @@ -122,11 +123,18 @@ struct Fog { alignas(16) float DepthMode; }; +// Vertex lighting +// Both frontface and backface lighting can be calculated +struct TwoSidedColor +{ + alignas(16) float3 Front; + alignas(16) float3 Back; +}; + struct FixedFunctionVertexShaderState { alignas(16) Transforms Transforms; alignas(16) arr(Lights, Light, 8); - alignas(16) float4 AmbientPlusLightAmbient; - alignas(16) float4 BackAmbientPlusLightAmbient; + alignas(16) TwoSidedColor TotalLightsAmbient; alignas(16) arr(Materials, Material, 2); alignas(16) Modes Modes; alignas(16) Fog Fog; From c77582524db0efa80c4670256d66c33432485cba Mon Sep 17 00:00:00 2001 From: Anthony Date: Tue, 15 Dec 2020 18:08:20 +1300 Subject: [PATCH 14/21] Fix incorrect toViewer vector when LocalViewer is enabled --- src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShader.hlsl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShader.hlsl b/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShader.hlsl index 93b3ca62e..b55c2d15a 100644 --- a/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShader.hlsl +++ b/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShader.hlsl @@ -250,8 +250,8 @@ LightingOutput CalcLighting(const float2 powers) totalLightOutput.Specular.Back = float3(0, 0, 0); const float3 toViewerVN = state.Modes.LocalViewer - ? float3(0, 0, 1) - : normalize(-View.Position.xyz); + ? float3(0, 0, -1) // Strip sample + : normalize(-View.Position.xyz); // DoA3 character select for (uint i = 0; i < 8; i++) { From 54e0c08ef1495e1ea13bc03a9faf225558822886 Mon Sep 17 00:00:00 2001 From: Anthony Date: Tue, 15 Dec 2020 18:32:36 +1300 Subject: [PATCH 15/21] Let MultiplyTransform call SetTransform Detect if it didn't --- src/core/hle/D3D8/Direct3D9/Direct3D9.cpp | 28 ++++++++++------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp index a236eb2f2..72482b764 100644 --- a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp +++ b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp @@ -6627,7 +6627,7 @@ void CxbxImpl_SetTransform DEBUG_D3DRESULT(hRet, "g_pD3DDevice->SetTransform"); } -// MultiplyTransform can call SetTransform, nested call detection is required +// MultiplyTransform should call SetTransform, we'd like to know if it didn't // Test case: 25 to Life static thread_local uint32_t setTransformCount = 0; @@ -6647,7 +6647,7 @@ static void D3DDevice_SetTransform_0 LOG_FUNC_ARG(pMatrix) LOG_FUNC_END; - NestedPatchCounter call(setTransformCount); + setTransformCount++; __asm { // Trampoline to guest code to remove the need for a GetTransform patch @@ -6656,10 +6656,7 @@ static void D3DDevice_SetTransform_0 call XB_TRMP(D3DDevice_SetTransform_0) } - if (call.GetLevel() == 0) { - // Skip if this patch is called from MultiplyTransform - CxbxImpl_SetTransform(State, pMatrix); - } + CxbxImpl_SetTransform(State, pMatrix); } __declspec(naked) xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_SetTransform_0) @@ -6697,14 +6694,11 @@ xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_SetTransform) LOG_FUNC_ARG(pMatrix) LOG_FUNC_END; - NestedPatchCounter call(setTransformCount); + setTransformCount++; // Trampoline to guest code to remove the need for a GetTransform patch XB_TRMP(D3DDevice_SetTransform)(State, pMatrix); - if (call.GetLevel() == 0) { - // Skip if this patch is called from MultiplyTransform - CxbxImpl_SetTransform(State, pMatrix); - } + CxbxImpl_SetTransform(State, pMatrix); } // ****************************************************************** @@ -6721,15 +6715,17 @@ xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_MultiplyTransform) LOG_FUNC_ARG(pMatrix) LOG_FUNC_END; - NestedPatchCounter call(setTransformCount); + setTransformCount = 0; - // Trampoline to guest code to remove the need for a GetTransform patch + // Trampoline to guest code, which we expect to call SetTransform + // If we find a case where the trampoline doesn't call SetTransform + // (or we can't detect the call) we will need to implement this XB_TRMP(D3DDevice_MultiplyTransform)(State, pMatrix); - auto pcState = EmuXB2PC_D3DTS(State); + if (setTransformCount == 0) { + LOG_TEST_CASE("MultiplyTransform did not appear to call SetTransform"); + } - HRESULT hRet = g_pD3DDevice->MultiplyTransform(pcState, pMatrix); - DEBUG_D3DRESULT(hRet, "g_pD3DDevice->MultiplyTransform"); } // ****************************************************************** From c722d805031a7eb85b2921429e0890714c5aee51 Mon Sep 17 00:00:00 2001 From: Anthony Date: Fri, 18 Dec 2020 22:50:06 +1300 Subject: [PATCH 16/21] Get view transform from transform state --- src/core/hle/D3D8/Direct3D9/Direct3D9.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp index 72482b764..9fbb22d84 100644 --- a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp +++ b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp @@ -6282,13 +6282,14 @@ D3DXVECTOR4 toVector(D3DCOLORVALUE val) { return D3DXVECTOR4(val.r, val.g, val.b, val.a); } -void UpdateFixedFunctionShaderLight(int d3dLightIndex, Light* pShaderLight, D3DXVECTOR4* pLightAmbient, D3DXMATRIX viewTransform) { +void UpdateFixedFunctionShaderLight(int d3dLightIndex, Light* pShaderLight, D3DXVECTOR4* pLightAmbient) { if (d3dLightIndex == -1) { pShaderLight->Type = 0; // Disable the light return; } auto d3dLight = &d3d8LightState.Lights[d3dLightIndex]; + auto viewTransform = (D3DXMATRIX)d3d8TransformState.Transforms[xbox::X_D3DTS_VIEW]; // TODO remove D3DX usage // Pre-transform light position to viewspace @@ -6452,10 +6453,8 @@ void UpdateFixedFunctionVertexShaderState() // Update lights auto LightAmbient = D3DXVECTOR4(0.f, 0.f, 0.f, 0.f); - D3DXMATRIX rowMajorViewTransform; - D3DXMatrixTranspose(&rowMajorViewTransform, (D3DXMATRIX*)&ffShaderState.Transforms.View); for (size_t i = 0; i < ffShaderState.Lights.size(); i++) { - UpdateFixedFunctionShaderLight(d3d8LightState.EnabledLights[i], &ffShaderState.Lights[i], &LightAmbient, rowMajorViewTransform); + UpdateFixedFunctionShaderLight(d3d8LightState.EnabledLights[i], &ffShaderState.Lights[i], &LightAmbient); } D3DXVECTOR4 Ambient = toVector(XboxRenderStates.GetXboxRenderState(X_D3DRS_AMBIENT)); From 8a5c9a6445c4a60b30caba13d4099c6483db79dc Mon Sep 17 00:00:00 2001 From: Anthony Date: Fri, 18 Dec 2020 23:37:28 +1300 Subject: [PATCH 17/21] Consolidate lighting calculations --- .../Direct3D9/FixedFunctionVertexShader.hlsl | 192 +++++------------- 1 file changed, 54 insertions(+), 138 deletions(-) diff --git a/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShader.hlsl b/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShader.hlsl index b55c2d15a..1d6099bb4 100644 --- a/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShader.hlsl +++ b/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShader.hlsl @@ -94,6 +94,11 @@ struct TransformInfo static TransformInfo View; // Vertex transformed to viewspace static TransformInfo Projection; // Vertex transformed to projection space +static const int LIGHT_TYPE_NONE = 0; +static const int LIGHT_TYPE_POINT = 1; +static const int LIGHT_TYPE_SPOT = 2; +static const int LIGHT_TYPE_DIRECTIONAL = 3; + // Final lighting output struct LightingOutput { @@ -101,155 +106,71 @@ struct LightingOutput TwoSidedColor Specular; }; -TwoSidedColor DoSpecular(const float3 toLightVN, const float3 toViewerVN, const float2 powers, const float4 lightSpecular) -{ - TwoSidedColor Specular; - Specular.Front = Specular.Back = float3(0, 0, 0); - - // Specular - // Note : if (state.Modes.SpecularEnable) no longer required because when disabled, CPU sets all lightSpecular.rgb inputs to 0 - { - // Blinn-Phong - // https://learnopengl.com/Advanced-Lighting/Advanced-Lighting - const float3 halfway = normalize(toViewerVN + toLightVN); - const float NdotH = dot(View.Normal, halfway); - - const float3 frontSpecular = pow(abs(NdotH), powers[0]) * lightSpecular.rgb; - const float3 backSpecular = pow(abs(NdotH), powers[1]) * lightSpecular.rgb; - - if (NdotH >= 0) - Specular.Front = frontSpecular; - else - Specular.Back = backSpecular; - } - - return Specular; -} - // useful reference https://drivers.amd.com/misc/samples/dx9/FixedFuncShader.pdf - -LightingOutput DoPointLight(const Light l, const float3 toViewerVN, const float2 powers) +LightingOutput DoLight(const Light l, const float3 toViewerN, const float2 powers) { LightingOutput o; o.Diffuse.Front = o.Diffuse.Back = float3(0, 0, 0); o.Specular.Front = o.Specular.Back = float3(0, 0, 0); - // Diffuse - const float3 toLightV = l.PositionV - View.Position.xyz; - const float lightDist = length(toLightV); - const float3 toLightVN = normalize(toLightV); + const float3 toLight = l.PositionV - View.Position.xyz; + const float3 toLightN = normalize(toLight); + float attenuation = 1; + float spotIntensity = 1; - // A(Constant) + A(Linear) * dist + A(Exp) * dist^2 - float attenuation = - 1 / (l.Attenuation[0] - + l.Attenuation[1] * lightDist - + l.Attenuation[2] * lightDist * lightDist); + if (l.Type == LIGHT_TYPE_SPOT) { + // Spotlight factors + // https://docs.microsoft.com/en-us/windows/win32/direct3d9/light-types + float3 toVertexN = -toLightN; + float cosAlpha = dot(l.DirectionVN, toVertexN); + // I = ( cos(a) - cos(phi/2) ) / ( cos(theta/2) - cos(phi/2) ) + float spotBase = saturate((cosAlpha - l.CosHalfPhi) / l.SpotIntensityDivisor); + float spotIntensity = pow(spotBase, l.Falloff); + } - // Range cutoff - if (lightDist > l.Range) - attenuation = 0; + if (l.Type == LIGHT_TYPE_POINT || l.Type == LIGHT_TYPE_SPOT) { + float lightDist = length(toLight); - const float NdotL = dot(View.Normal, toLightVN); - const float3 lightDiffuse = abs(NdotL) * attenuation * l.Diffuse.rgb; + // A(Constant) + A(Linear) * dist + A(Exp) * dist^2 + attenuation = + 1 / (l.Attenuation[0] + + l.Attenuation[1] * lightDist + + l.Attenuation[2] * lightDist * lightDist); - if (NdotL >= 0) - o.Diffuse.Front = lightDiffuse; - else - o.Diffuse.Back = lightDiffuse; + // Range cutoff + if (lightDist > l.Range) + attenuation = 0; + } - // Specular - o.Specular = DoSpecular(toLightVN, toViewerVN, powers, l.Specular); - o.Specular.Front *= attenuation; - o.Specular.Back *= attenuation; + // Diffuse lighting calculation + const float NdotLFront = dot(View.Normal, toLightN); + const float NdotLBack = dot(-View.Normal, toLightN); + o.Diffuse.Front = max(NdotLFront, 0) * l.Diffuse.rgb * attenuation * spotIntensity; + o.Diffuse.Back = max(NdotLBack, 0) * l.Diffuse.rgb * attenuation * spotIntensity; + + // Specular lighting calculation + // Note : if (state.Modes.SpecularEnable) no longer required because when disabled, CPU sets all lightSpecular.rgb inputs to 0 + // Blinn-Phong + // https://learnopengl.com/Advanced-Lighting/Advanced-Lighting + const float3 halfway = normalize(toViewerN + toLightN); + const float NdotHFront = dot(View.Normal, halfway); + const float NdotHBack = dot(-View.Normal, halfway); + + o.Specular.Front = pow(max(NdotHFront, 0), powers[0]) * l.Specular.rgb * attenuation * spotIntensity; + o.Specular.Back = pow(max(NdotHBack, 0), powers[1]) * l.Specular.rgb * attenuation * spotIntensity; return o; } -LightingOutput DoSpotLight(const Light l, const float3 toViewerVN, const float2 powers) -{ - LightingOutput o; - o.Diffuse.Front = o.Diffuse.Back = float3(0, 0, 0); - o.Specular.Front = o.Specular.Back = float3(0, 0, 0); - - // Diffuse - const float3 toLightV = l.PositionV - View.Position.xyz; - const float lightDist = length(toLightV); - const float3 toLightVN = normalize(toLightV); - const float3 toVertexVN = -toLightVN; - - // https://docs.microsoft.com/en-us/windows/win32/direct3d9/light-types - const float cosAlpha = dot(l.DirectionVN, toVertexVN); - // I = ( cos(a) - cos(phi/2) ) / ( cos(theta/2) - cos(phi/2) ) - const float spotBase = saturate((cosAlpha - l.CosHalfPhi) / l.SpotIntensityDivisor); - const float spotIntensity = pow(spotBase, l.Falloff); - - // A(Constant) + A(Linear) * dist + A(Exp) * dist^2 - float attenuation = - 1 / (l.Attenuation[0] - + l.Attenuation[1] * lightDist - + l.Attenuation[2] * lightDist * lightDist); - - // Range cutoff - if (lightDist > l.Range) - attenuation = 0; - - const float NdotL = dot(View.Normal, toLightVN); - const float3 lightDiffuse = abs(NdotL) * attenuation * l.Diffuse.rgb * spotIntensity; - - if (NdotL >= 0) - o.Diffuse.Front = lightDiffuse; - else - o.Diffuse.Back = lightDiffuse; - - // Specular - o.Specular = DoSpecular(toLightVN, toViewerVN, powers, l.Specular); - o.Specular.Front *= attenuation; - o.Specular.Back *= attenuation; - - return o; -} - -LightingOutput DoDirectionalLight(const Light l, const float3 toViewerVN, const float2 powers) -{ - LightingOutput o; - o.Diffuse.Front = o.Diffuse.Back = float3(0, 0, 0); - o.Specular.Front = o.Specular.Back = float3(0, 0, 0); - - // Diffuse - - // Intensity from N . L - const float3 toLightVN = -l.DirectionVN; - const float NdotL = dot(View.Normal, toLightVN); - const float3 lightDiffuse = abs(NdotL * l.Diffuse.rgb); - - // Apply light contribution to front or back face - // as the case may be - if (NdotL >= 0) - o.Diffuse.Front = lightDiffuse; - else - o.Diffuse.Back = lightDiffuse; - - // Specular - o.Specular = DoSpecular(toLightVN, toViewerVN, powers, l.Specular); - - return o; -} - - LightingOutput CalcLighting(const float2 powers) { - static const int LIGHT_TYPE_NONE = 0; - static const int LIGHT_TYPE_POINT = 1; - static const int LIGHT_TYPE_SPOT = 2; - static const int LIGHT_TYPE_DIRECTIONAL = 3; - LightingOutput totalLightOutput; totalLightOutput.Diffuse.Front = float3(0, 0, 0); totalLightOutput.Diffuse.Back = float3(0, 0, 0); totalLightOutput.Specular.Front = float3(0, 0, 0); totalLightOutput.Specular.Back = float3(0, 0, 0); - const float3 toViewerVN = state.Modes.LocalViewer + const float3 toViewerN = state.Modes.LocalViewer ? float3(0, 0, -1) // Strip sample : normalize(-View.Position.xyz); // DoA3 character select @@ -258,19 +179,14 @@ LightingOutput CalcLighting(const float2 powers) const Light currentLight = state.Lights[i]; LightingOutput currentLightOutput; - if(currentLight.Type == LIGHT_TYPE_POINT) - currentLightOutput = DoPointLight(currentLight, toViewerVN, powers); - else if(currentLight.Type == LIGHT_TYPE_SPOT) - currentLightOutput = DoSpotLight(currentLight, toViewerVN, powers); - else if (currentLight.Type == LIGHT_TYPE_DIRECTIONAL) - currentLightOutput = DoDirectionalLight(currentLight, toViewerVN, powers); - else - continue; + if (currentLight.Type != LIGHT_TYPE_NONE) { + currentLightOutput = DoLight(currentLight, toViewerN, powers); - totalLightOutput.Diffuse.Front += currentLightOutput.Diffuse.Front; - totalLightOutput.Diffuse.Back += currentLightOutput.Diffuse.Back; - totalLightOutput.Specular.Front += currentLightOutput.Specular.Front; - totalLightOutput.Specular.Back += currentLightOutput.Specular.Back; + totalLightOutput.Diffuse.Front += currentLightOutput.Diffuse.Front; + totalLightOutput.Diffuse.Back += currentLightOutput.Diffuse.Back; + totalLightOutput.Specular.Front += currentLightOutput.Specular.Front; + totalLightOutput.Specular.Back += currentLightOutput.Specular.Back; + } } return totalLightOutput; From 5441dbff1cb2f64f6007cc0829e257b725080232 Mon Sep 17 00:00:00 2001 From: Anthony Date: Sat, 19 Dec 2020 00:37:21 +1300 Subject: [PATCH 18/21] Fix LocalViewer again, and move it to where it's used --- .../Direct3D9/FixedFunctionVertexShader.hlsl | 36 +++++++++++-------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShader.hlsl b/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShader.hlsl index 1d6099bb4..0cf4619af 100644 --- a/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShader.hlsl +++ b/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShader.hlsl @@ -107,16 +107,24 @@ struct LightingOutput }; // useful reference https://drivers.amd.com/misc/samples/dx9/FixedFuncShader.pdf -LightingOutput DoLight(const Light l, const float3 toViewerN, const float2 powers) +LightingOutput DoLight(const Light l, const float2 powers) { LightingOutput o; o.Diffuse.Front = o.Diffuse.Back = float3(0, 0, 0); o.Specular.Front = o.Specular.Back = float3(0, 0, 0); - const float3 toLight = l.PositionV - View.Position.xyz; - const float3 toLightN = normalize(toLight); + float3 toLight; + float3 toLightN; float attenuation = 1; float spotIntensity = 1; + + if (l.Type == LIGHT_TYPE_DIRECTIONAL) { + toLight = toLightN = -l.DirectionVN; + } + else { + toLight = l.PositionV - View.Position.xyz; + toLightN = normalize(toLight); + } if (l.Type == LIGHT_TYPE_SPOT) { // Spotlight factors @@ -133,9 +141,9 @@ LightingOutput DoLight(const Light l, const float3 toViewerN, const float2 power // A(Constant) + A(Linear) * dist + A(Exp) * dist^2 attenuation = - 1 / (l.Attenuation[0] - + l.Attenuation[1] * lightDist - + l.Attenuation[2] * lightDist * lightDist); + 1 / (l.Attenuation[0] + + l.Attenuation[1] * lightDist + + l.Attenuation[2] * lightDist * lightDist); // Range cutoff if (lightDist > l.Range) @@ -143,13 +151,17 @@ LightingOutput DoLight(const Light l, const float3 toViewerN, const float2 power } // Diffuse lighting calculation - const float NdotLFront = dot(View.Normal, toLightN); + const float NdotLFront = dot(View.Normal, toLightN); const float NdotLBack = dot(-View.Normal, toLightN); o.Diffuse.Front = max(NdotLFront, 0) * l.Diffuse.rgb * attenuation * spotIntensity; o.Diffuse.Back = max(NdotLBack, 0) * l.Diffuse.rgb * attenuation * spotIntensity; // Specular lighting calculation - // Note : if (state.Modes.SpecularEnable) no longer required because when disabled, CPU sets all lightSpecular.rgb inputs to 0 + float3 toViewerN = state.Modes.LocalViewer + ? normalize(-View.Position.xyz) // Strip sample + : float3(0, 0, -1); // DoA 3 character select + + // Note : if X_D3DRS_SPECULARENABLE is false then all light specular colours should have been zeroed out // Blinn-Phong // https://learnopengl.com/Advanced-Lighting/Advanced-Lighting const float3 halfway = normalize(toViewerN + toLightN); @@ -159,7 +171,7 @@ LightingOutput DoLight(const Light l, const float3 toViewerN, const float2 power o.Specular.Front = pow(max(NdotHFront, 0), powers[0]) * l.Specular.rgb * attenuation * spotIntensity; o.Specular.Back = pow(max(NdotHBack, 0), powers[1]) * l.Specular.rgb * attenuation * spotIntensity; - return o; + return o; } LightingOutput CalcLighting(const float2 powers) @@ -170,17 +182,13 @@ LightingOutput CalcLighting(const float2 powers) totalLightOutput.Specular.Front = float3(0, 0, 0); totalLightOutput.Specular.Back = float3(0, 0, 0); - const float3 toViewerN = state.Modes.LocalViewer - ? float3(0, 0, -1) // Strip sample - : normalize(-View.Position.xyz); // DoA3 character select - for (uint i = 0; i < 8; i++) { const Light currentLight = state.Lights[i]; LightingOutput currentLightOutput; if (currentLight.Type != LIGHT_TYPE_NONE) { - currentLightOutput = DoLight(currentLight, toViewerN, powers); + currentLightOutput = DoLight(currentLight, powers); totalLightOutput.Diffuse.Front += currentLightOutput.Diffuse.Front; totalLightOutput.Diffuse.Back += currentLightOutput.Diffuse.Back; From 24b38ae3b3de98ad89e34c1e32102ce353f74bee Mon Sep 17 00:00:00 2001 From: Anthony Date: Sat, 19 Dec 2020 02:04:29 +1300 Subject: [PATCH 19/21] Point sprite fixes * Implement Min and Max * Account for upscaling --- src/core/hle/D3D8/Direct3D9/Direct3D9.cpp | 16 +++++++++++----- .../Direct3D9/FixedFunctionVertexShader.hlsl | 9 ++++++--- .../FixedFunctionVertexShaderState.hlsli | 9 +++++---- 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp index 9fbb22d84..e36268f50 100644 --- a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp +++ b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp @@ -6394,15 +6394,21 @@ void UpdateFixedFunctionVertexShaderState() // Point sprites auto pointSize = XboxRenderStates.GetXboxRenderState(X_D3DRS_POINTSIZE); + auto pointSizeMin = XboxRenderStates.GetXboxRenderState(X_D3DRS_POINTSIZE_MIN); + auto pointSizeMax = XboxRenderStates.GetXboxRenderState(X_D3DRS_POINTSIZE_MAX); ffShaderState.PointSprite.PointSize = *reinterpret_cast(&pointSize); - bool PointScaleEnable = XboxRenderStates.GetXboxRenderState(X_D3DRS_POINTSCALEENABLE) != FALSE; - ffShaderState.PointSprite.RenderTargetHeight = PointScaleEnable ? (float)GetPixelContainerHeight(g_pXbox_RenderTarget) : 1.0f; + ffShaderState.PointSprite.PointSizeMin = *reinterpret_cast(&pointSizeMin); + ffShaderState.PointSprite.PointSizeMax = *reinterpret_cast(&pointSizeMax); + + bool PointScaleEnable = XboxRenderStates.GetXboxRenderState(X_D3DRS_POINTSCALEENABLE); auto scaleA = XboxRenderStates.GetXboxRenderState(X_D3DRS_POINTSCALE_A); - ffShaderState.PointSprite.ScaleA = PointScaleEnable ? *reinterpret_cast(&scaleA) : 1.0f; auto scaleB = XboxRenderStates.GetXboxRenderState(X_D3DRS_POINTSCALE_B); - ffShaderState.PointSprite.ScaleB = PointScaleEnable ? *reinterpret_cast(&scaleB) : 0.0f; auto scaleC = XboxRenderStates.GetXboxRenderState(X_D3DRS_POINTSCALE_C); - ffShaderState.PointSprite.ScaleC = PointScaleEnable ? *reinterpret_cast(&scaleC) : 0.0f; + ffShaderState.PointSprite.ScaleABC.x = PointScaleEnable ? *reinterpret_cast(&scaleA) : 1.0f; + ffShaderState.PointSprite.ScaleABC.y = PointScaleEnable ? *reinterpret_cast(&scaleB) : 0.0f; + ffShaderState.PointSprite.ScaleABC.z = PointScaleEnable ? *reinterpret_cast(&scaleC) : 0.0f; + ffShaderState.PointSprite.XboxRenderTargetHeight = PointScaleEnable ? (float)GetPixelContainerHeight(g_pXbox_RenderTarget) : 1.0f; + ffShaderState.PointSprite.RenderUpscaleFactor = g_RenderUpscaleFactor; // Fog // Determine how fog depth is calculated diff --git a/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShader.hlsl b/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShader.hlsl index 0cf4619af..cdab93f0f 100644 --- a/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShader.hlsl +++ b/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShader.hlsl @@ -390,16 +390,19 @@ float DoPointSpriteSize() { const PointSprite ps = state.PointSprite; float pointSize = ps.PointSize; + float A = ps.ScaleABC.x; + float B = ps.ScaleABC.y; + float C = ps.ScaleABC.z; // Note : if (ps.PointScaleEnable) not required because when disabled, CPU sets RenderTargetHeight and ScaleA to 1, and ScaleB and ScaleC to 0 { const float eyeDistance = length(View.Position); - const float factor = ps.ScaleA + ps.ScaleB * eyeDistance + ps.ScaleC * (eyeDistance * eyeDistance); + const float factor = A + (B * eyeDistance) + (C * (eyeDistance * eyeDistance)); - pointSize *= ps.RenderTargetHeight * sqrt(1 / factor); + pointSize *= ps.XboxRenderTargetHeight * sqrt(1 / factor); } - return pointSize; + return clamp(pointSize, ps.PointSizeMin, ps.PointSizeMax) * ps.RenderUpscaleFactor; } VS_INPUT InitializeInputRegisters(const VS_INPUT xInput) diff --git a/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShaderState.hlsli b/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShaderState.hlsli index 77fb72a1a..b909b97e4 100644 --- a/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShaderState.hlsli +++ b/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShaderState.hlsli @@ -105,11 +105,12 @@ struct Modes { struct PointSprite { alignas(16) float PointSize; + alignas(16) float PointSizeMin; + alignas(16) float PointSizeMax; // alignas(16) float PointScaleEnable; - alignas(16) float RenderTargetHeight; - alignas(16) float ScaleA; - alignas(16) float ScaleB; - alignas(16) float ScaleC; + alignas(16) float XboxRenderTargetHeight; + alignas(16) float3 ScaleABC; + alignas(16) float RenderUpscaleFactor; }; struct TextureState { From b6db19e353e812f3f46a9fc0f754ab8d8ae1bbbe Mon Sep 17 00:00:00 2001 From: Anthony Date: Thu, 28 Jan 2021 19:24:09 +1300 Subject: [PATCH 20/21] Add hlsl files to install --- CMakeLists.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8ca950a86..1a468388c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -412,6 +412,12 @@ install(FILES ${cxbxr_INSTALL_files} DESTINATION bin ) +install(FILES + "${CMAKE_SOURCE_DIR}/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShaderState.hlsli" + "${CMAKE_SOURCE_DIR}/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShader.hlsl" + DESTINATION bin/hlsl +) + set(cxbxr_GLEW_DLL "${CMAKE_SOURCE_DIR}/import/glew-2.0.0/bin/Release/Win32/glew32.dll") install(PROGRAMS ${cxbxr_GLEW_DLL} From 2331816a993bf469f68ce9d62a6ae44a1926cc08 Mon Sep 17 00:00:00 2001 From: RadWolfie Date: Thu, 28 Jan 2021 10:30:31 +0000 Subject: [PATCH 21/21] review remarks --- CMakeLists.txt | 11 ++++++++--- projects/cxbx/CMakeLists.txt | 6 ++---- projects/cxbxr-emu/CMakeLists.txt | 6 ++---- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1a468388c..8ad9094d8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -195,6 +195,11 @@ file (GLOB CXBXR_HEADER_EMU "${CXBXR_ROOT_DIR}/src/devices/Xbox.h" ) +# filter hlsl files into its own list +# excluding hlsli file(s) +set(CXBXR_HEADER_HLSL ${CXBXR_HEADER_EMU}) +list(FILTER CXBXR_HEADER_HLSL INCLUDE REGEX ".*\\.hlsl$") + # Common (GUI and Emulator) file (GLOB CXBXR_SOURCE_COMMON "${CXBXR_ROOT_DIR}/src/common/crypto/EmuDes.cpp" @@ -413,9 +418,9 @@ install(FILES ${cxbxr_INSTALL_files} ) install(FILES - "${CMAKE_SOURCE_DIR}/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShaderState.hlsli" - "${CMAKE_SOURCE_DIR}/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShader.hlsl" - DESTINATION bin/hlsl + "${CMAKE_SOURCE_DIR}/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShaderState.hlsli" + "${CMAKE_SOURCE_DIR}/src/core/hle/D3D8/Direct3D9/FixedFunctionVertexShader.hlsl" + DESTINATION bin/hlsl ) set(cxbxr_GLEW_DLL "${CMAKE_SOURCE_DIR}/import/glew-2.0.0/bin/Release/Win32/glew32.dll") diff --git a/projects/cxbx/CMakeLists.txt b/projects/cxbx/CMakeLists.txt index 731de24b4..ad9dd6e11 100644 --- a/projects/cxbx/CMakeLists.txt +++ b/projects/cxbx/CMakeLists.txt @@ -197,11 +197,9 @@ add_dependencies(cxbx cxbxr-ldr cxbxr-emu misc-batch) # Try to stop cmake from building hlsl files # Which are all currently loaded at runtime only -set(HlslHeaders ${CXBXR_HEADER_EMU}) -list(FILTER HlslHeaders INCLUDE REGEX ".*\\.hlsl$") set_source_files_properties( - ${HlslHeaders} + ${CXBXR_HEADER_HLSL} PROPERTIES HEADER_FILE_ONLY TRUE VS_TOOL_OVERRIDE "None" -) \ No newline at end of file +) diff --git a/projects/cxbxr-emu/CMakeLists.txt b/projects/cxbxr-emu/CMakeLists.txt index 06f66f0b0..656554ff1 100644 --- a/projects/cxbxr-emu/CMakeLists.txt +++ b/projects/cxbxr-emu/CMakeLists.txt @@ -169,11 +169,9 @@ add_dependencies(cxbxr-emu cxbxr-ldr misc-batch) # Try to stop cmake from building hlsl files # Which are all currently loaded at runtime only -set(HlslHeaders ${CXBXR_HEADER_EMU}) -list(FILTER HlslHeaders INCLUDE REGEX ".*\\.hlsl$") set_source_files_properties( - ${HlslHeaders} + ${CXBXR_HEADER_HLSL} PROPERTIES HEADER_FILE_ONLY TRUE VS_TOOL_OVERRIDE "None" -) \ No newline at end of file +)