diff --git a/CMakeLists.txt b/CMakeLists.txt index 66b8dd3ca..778e66096 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -225,6 +225,7 @@ file (GLOB CXBXR_SOURCE_COMMON "${CXBXR_ROOT_DIR}/src/common/xbe/XbePrinter.cpp" "${CXBXR_ROOT_DIR}/src/common/xdvdfs-tools/buffered_io.cpp" "${CXBXR_ROOT_DIR}/src/common/xdvdfs-tools/xdvdfs.cpp" + "${CXBXR_ROOT_DIR}/src/CxbxVersion.cpp" "${CXBXR_ROOT_DIR}/src/gui/DbgConsole.cpp" "${CXBXR_ROOT_DIR}/src/HighPerformanceGraphicsEnabler.c" ) diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 50d577767..43e98770b 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -1,19 +1,65 @@ -Developers: -Luke Usher [SoullessSentinel] -PatrickvL -StrikerX3 -blueshogun96 -donwayo -hrydgard -jagotu -jarupxx -phire -x1nixmzeng -RadWolfie -Luca D'Amico (Luca1991/Luca91) -ergo720 +Contributors +------------ + +Cxbx-Reloaded has a rich history, with lots of contributors in it's various stages of life. +Cxbx-Reloaded was initiated by Luke Usher, as a fork of the then-dormant Cxbx, and inspired by Dxbx. +Cxbx was initiated by Caustik. Dxbx was initiated by shadowtj. + +The following contributors are grouped per project and listed in alphabetical order, +based on sources like https://github.com/Cxbx-Reloaded/Cxbx-Reloaded/graphs/contributors , +http://www.caustik.com/cxbx/about.htm , http://emulation.gametechwiki.com/index.php/Cxbx , +and https://github.com/PatrickvL/Dxbx/graphs/contributors . + + +Cxbx-Reloaded Current Development Team: + +ergo720 +LukeUsher (Luke Usher) [SoullessSentinel] +NZJenkins +PatrickvL (Patrick van Logchem) +RadWolfie + + +Cxbx-Reloaded All-Time Contributors: + +aav7fl (Kyle Niewiada) +anita999 +BenNottelling (Benjamin Aerni) +blueshogun96 +CakeLancelot +darrena092 (Darren Anderson) +DiscoStarslayer (Darren Thompson) +donwayo (Wayo) +Ernegien (Mike Davis) +ergo720 +faha223 (Fred Hallock) +Fisherman166 +gandalfthewhite19890404 +gellis713 +ggKismet +GXTX (wutno) +hrydgard (Henrik Hydgard) +jackchentwkh +jagotu (JaGoTu) +jarupxx +JayFoxRox (Jannik Vogel) +literalmente-game +Luca1991 (Luca D'Amico) [Luca91] +LukeUsher (Luke Usher) [SoullessSentinel] +Margen67 +NZJenkins +PatrickvL (Patrick van Logchem) +phire (Scott Mansell) +RadWolfie +revel8n +StrikerX3 (Ivan Roberto de Oliveira) +TotalCaesar659 +Voxel9 (Voxel) +x1nixmzeng + + +Cxbx-Reloaded Supporters: -Supporters: Cedric Wilson Cisco Martinez Cody Dale Barton @@ -33,5 +79,30 @@ Taylor Stock Yldri Yuri Kunde Schlesner -Special Thanks: + +Cxbx-Reloaded Special Thanks: + All contributors to the original Cxbx and Dxbx projects, without which Cxbx-Reloaded would not exist. + + +Cxbx contributors: + +_SF_ +blueshogun96 +Caustic (Aaron Robinson) +donwayo (Wayo) +dstien (Daniel Stien) +Echelon9 (Rhys Kidd) +Kingofc +Koitsu +martin_sw (Martin) +sopskrutt (Sop Skrutt) +Zomby + + +Dxbx contributors: + +donwayo (Wayo) +PatrickvL (Patrick van Logchem) +revel8n +shadowtj (Shadow_tj) diff --git a/COPYING b/COPYING index b8306825e..960fe7469 100644 --- a/COPYING +++ b/COPYING @@ -278,64 +278,3 @@ PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - , 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Library General -Public License instead of this License. - diff --git a/resource/Cxbx.rc b/resource/Cxbx.rc index 3bcebfd55..5f6af33b0 100644 --- a/resource/Cxbx.rc +++ b/resource/Cxbx.rc @@ -13,11 +13,11 @@ #undef APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// -// English (United States) resources +// Language neutral resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US -#pragma code_page(1252) +LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL +#pragma code_page(65001) ///////////////////////////////////////////////////////////////////////////// // @@ -661,36 +661,6 @@ BEGIN MENUITEM " ", ID_LOG,MFT_STRING,MFS_ENABLED END -#endif // English (United States) resources -///////////////////////////////////////////////////////////////////////////// - - -///////////////////////////////////////////////////////////////////////////// -// English (United Kingdom) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENG) -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK -#pragma code_page(1252) - -///////////////////////////////////////////////////////////////////////////// -// -// DESIGNINFO -// - -#ifdef APSTUDIO_INVOKED -GUIDELINES DESIGNINFO -BEGIN - IDD_ABOUT, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 303 - TOPMARGIN, 7 - BOTTOMMARGIN, 170 - END -END -#endif // APSTUDIO_INVOKED - - ///////////////////////////////////////////////////////////////////////////// // // Dialog @@ -725,7 +695,8 @@ IDR_CONTRIBUTORS TXT "..\\CONTRIBUTORS" IDR_COPYING TXT "..\\COPYING" -#endif // English (United Kingdom) resources +#endif // Language neutral resources + ///////////////////////////////////////////////////////////////////////////// diff --git a/setup.bat b/setup.bat new file mode 100644 index 000000000..ddcfa78b8 --- /dev/null +++ b/setup.bat @@ -0,0 +1,26 @@ +@echo off + +REM CXbx-Reloaded setup script +REM +REM Depends on git, cmake and Visual Studio being installed. + +echo Pulling lastest version from git... +REM git clone --recurse-submodules https://github.com/Cxbx-Reloaded/Cxbx-Reloaded/ +git pull --recurse-submodules + +REM echo Synchronizing submodules... +REM git submodule update --init --recursive + +echo Initializing most recent Visual Studio build environment... +@call "%VS140COMNTOOLS%vsvars32.bat" + +echo Generating solution... +mkdir build +cd build +REM cmake .. -G "Visual Studio 16 2019" -A Win32 +cmake .. -A Win32 + +echo Building solution... +cmake --build . + +echo Done! Enjoy using Cxbx-Reloaded! \ No newline at end of file diff --git a/src/CxbxVersion.cpp b/src/CxbxVersion.cpp new file mode 100644 index 000000000..f3b0be0d3 --- /dev/null +++ b/src/CxbxVersion.cpp @@ -0,0 +1,11 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com +#include "Version.h" +#include "CxbxVersion.h" + +/*! version string dependent on trace flag */ +#ifndef _DEBUG_TRACE +const char* CxbxVersionStr = _GIT_VERSION " (" __DATE__ ")"; +#else +const char* CxbxVersionStr = _GIT_VERSION "-Trace (" __DATE__ ")"; +#endif diff --git a/src/CxbxVersion.h b/src/CxbxVersion.h index e04b32b3c..17983832b 100644 --- a/src/CxbxVersion.h +++ b/src/CxbxVersion.h @@ -2,9 +2,4 @@ #include "version.h" -/*! version string dependent on trace flag */ -#ifndef _DEBUG_TRACE -#define _CXBX_VERSION _GIT_VERSION " (" __DATE__ ")" -#else -#define _CXBX_VERSION _GIT_VERSION "-Trace (" __DATE__ ")" -#endif +extern const char* CxbxVersionStr; diff --git a/src/common/xbe/XbePrinter.cpp b/src/common/xbe/XbePrinter.cpp index a09d481c2..2d7e6ba62 100644 --- a/src/common/xbe/XbePrinter.cpp +++ b/src/common/xbe/XbePrinter.cpp @@ -26,7 +26,7 @@ // ****************************************************************** #include "common\xbe\XbePrinter.h" -#include "CxbxVersion.h" // For _CXBX_VERSION +#include "CxbxVersion.h" // For CxbxVersionStr #include // For ctime #include // For std::stringstream @@ -170,7 +170,9 @@ std::string XbePrinter::GameRatingToString() std::string XbePrinter::GenDumpHeader() { std::string text; - text.append("XBE information generated by Cxbx-Reloaded (Version " _CXBX_VERSION ")\n\n"); + text.append("XBE information generated by Cxbx-Reloaded (Version "); + text.append(CxbxVersionStr); + text.append(")\n\n"); text.append("Title identified as \""); text.append(Xbe_to_print->m_szAsciiTitle); text.append("\"\n\n"); diff --git a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp index 44fefdacc..a0ba71638 100644 --- a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp +++ b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp @@ -141,6 +141,13 @@ static bool g_bHack_DisableHostGPUQueries = false; // TO static IDirect3DQuery *g_pHostQueryWaitForIdle = nullptr; static IDirect3DQuery *g_pHostQueryCallbackEvent = nullptr; +// Vertex shader symbols, declared in XbVertexShader.cpp : +extern void CxbxImpl_SelectVertexShaderDirect(XTL::X_VERTEXATTRIBUTEFORMAT* pVAF, DWORD Address); +extern void CxbxImpl_SetVertexShaderInput(DWORD Handle, UINT StreamCount, XTL::X_STREAMINPUT* pStreamInputs); + +// Vertex buffer symbols, declared in XbVertexBuffer.cpp +extern void CxbxImpl_SetStreamSource(UINT StreamNumber, XTL::X_D3DVertexBuffer* pStreamData, UINT Stride); + static std::condition_variable g_VBConditionVariable; // Used in BlockUntilVerticalBlank static std::mutex g_VBConditionMutex; // Used in BlockUntilVerticalBlank static DWORD g_VBLastSwap = 0; @@ -173,9 +180,6 @@ static XTL::DWORD *g_Xbox_D3DDevice; // TODO: This should be a static DWORD g_dwVertexShaderUsage = 0; // Unused. If needed, move to XbVertexShader.cpp */ -// Active D3D Vertex Streams (and strides) - XTL::X_D3DVertexBuffer *g_D3DStreams[16]; - XTL::UINT g_D3DStreamStrides[16]; static XTL::DWORD g_VertexShaderSlots[X_VSH_MAX_INSTRUCTION_COUNT]; XTL::DWORD g_Xbox_VertexShader_Handle = 0; @@ -3420,7 +3424,7 @@ XTL::X_D3DSurface* WINAPI XTL::EMUPATCH(D3DDevice_GetBackBuffer2) /** unsafe, somehow HRESULT hRet = D3D_OK; - X_D3DSurface *pBackBuffer = EmuNewD3DSurface(); + X_D3DSurface *pXboxBackBuffer = EmuNewD3DSurface(); if(BackBuffer == -1) { static IDirect3DSurface *pCachedPrimarySurface = nullptr; @@ -3432,21 +3436,21 @@ XTL::X_D3DSurface* WINAPI XTL::EMUPATCH(D3DDevice_GetBackBuffer2) DEBUG_D3DRESULT(hRet, "g_pD3DDevice->CreateOffscreenPlainSurface"); } - SetHostSurface(pBackBuffer, pCachedPrimarySurface); + SetHostSurface(pXboxBackBuffer, pCachedPrimarySurface); hRet = g_pD3DDevice->GetFrontBuffer(pCachedPrimarySurface); DEBUG_D3DRESULT(hRet, "g_pD3DDevice->GetFrontBuffer"); if (FAILED(hRet)) { EmuLog(LOG_LEVEL::WARNING, "Could not retrieve primary surface, using backbuffer"); - SetHostSurface(pBackBuffer, nullptr); + SetHostSurface(pXboxBackBuffer, nullptr); pCachedPrimarySurface->Release(); pCachedPrimarySurface = nullptr; BackBuffer = 0; } // Debug: Save this image temporarily - //D3DXSaveSurfaceToFile("C:\\Aaron\\Textures\\FrontBuffer.bmp", D3DXIFF_BMP, GetHostSurface(pBackBuffer), nullptr, nullptr); + //D3DXSaveSurfaceToFile("C:\\Aaron\\Textures\\FrontBuffer.bmp", D3DXIFF_BMP, GetHostSurface(pXboxBackBuffer), nullptr, nullptr); } if(BackBuffer != -1) { @@ -3457,7 +3461,7 @@ XTL::X_D3DSurface* WINAPI XTL::EMUPATCH(D3DDevice_GetBackBuffer2) } //*/ - static X_D3DSurface *pBackBuffer = EmuNewD3DSurface(); + static X_D3DSurface *pXboxBackBuffer = EmuNewD3DSurface(); IDirect3DSurface *pCurrentHostBackBuffer = nullptr; STATUS_SUCCESS; @@ -3474,12 +3478,12 @@ XTL::X_D3DSurface* WINAPI XTL::EMUPATCH(D3DDevice_GetBackBuffer2) if (FAILED(hRet)) CxbxKrnlCleanup("Unable to retrieve back buffer"); - SetHostSurface(pBackBuffer, pCurrentHostBackBuffer); + SetHostSurface(pXboxBackBuffer, pCurrentHostBackBuffer); // Increment reference count - pBackBuffer->Common++; // EMUPATCH(D3DResource_AddRef)(pBackBuffer); + pXboxBackBuffer->Common++; // EMUPATCH(D3DResource_AddRef)(pXboxBackBuffer); - return pBackBuffer; + return pXboxBackBuffer; #else // COPY_BACKBUFFER_TO_XBOX_SURFACE // Rather than create a new surface, we should forward to the Xbox version of GetBackBuffer, // This gives us the correct Xbox surface to update. @@ -3626,8 +3630,8 @@ void UpdateViewPortOffsetAndScaleConstants() float vScale[] = { (2.0f / ViewPort.Width) * g_RenderScaleFactor, (-2.0f / ViewPort.Height) * g_RenderScaleFactor, 0.0f, 0.0f }; static float vOffset[] = { -1.0f, 1.0f, 0.0f, 1.0f }; - g_pD3DDevice->SetVertexShaderConstantF(58, vScale, 1); - g_pD3DDevice->SetVertexShaderConstantF(59, vOffset, 1); + g_pD3DDevice->SetVertexShaderConstantF(X_D3DVS_RESERVED_CONSTANT1_CORRECTED, vScale, 1); + g_pD3DDevice->SetVertexShaderConstantF(X_D3DVS_RESERVED_CONSTANT2_CORRECTED, vOffset, 1); } } @@ -4368,7 +4372,7 @@ VOID WINAPI XTL::EMUPATCH(D3DDevice_SetVertexData2s) float fa, fb; // Test case: Halo - // Note : XQEMU recently verified that the int16_t arguments + // Note : XQEMU verified that the int16_t arguments // must be mapped to floats in the range [-32768.0, 32767.0] // (See https://github.com/xqemu/xqemu/pull/176) fa = (float)a; @@ -4377,16 +4381,6 @@ VOID WINAPI XTL::EMUPATCH(D3DDevice_SetVertexData2s) EMUPATCH(D3DDevice_SetVertexData4f)(Register, fa, fb, 0.0f, 1.0f); } -DWORD FloatsToDWORD(FLOAT d, FLOAT a, FLOAT b, FLOAT c) -{ - DWORD ca = (FtoDW(d) << 24); - DWORD cr = (FtoDW(a) << 16) & 0x00FF0000; - DWORD cg = (FtoDW(b) << 8) & 0x0000FF00; - DWORD cb = (FtoDW(c) << 0) & 0x000000FF; - - return ca | cr | cg | cb; -} - extern uint32_t HLE_read_NV2A_pgraph_register(const int reg); // Declared in PushBuffer.cpp extern void HLE_write_NV2A_vertex_attribute_slot(unsigned slot, uint32_t parameter); // Declared in PushBuffer.cpp extern uint32_t HLE_read_NV2A_vertex_attribute_slot(unsigned VertexSlot); // Declared in PushBuffer.cpp @@ -4410,6 +4404,8 @@ VOID WINAPI XTL::EMUPATCH(D3DDevice_SetVertexData4f_16) mov Register, edi } + LOG_FORWARD("D3DDevice_SetVertexData4f"); + EMUPATCH(D3DDevice_SetVertexData4f)(Register, a, b, c, d); } @@ -4727,7 +4723,9 @@ VOID WINAPI XTL::EMUPATCH(D3DDevice_SetVertexData4s) float fa, fb, fc, fd; // Test case: Halo - // See comment note in D3DDevice_SetVertexData2s + // Note : XQEMU verified that the int16_t arguments + // must be mapped to floats in the range [-32768.0, 32767.0] + // (See https://github.com/xqemu/xqemu/pull/176) fa = (float)a; fb = (float)b; fc = (float)c; @@ -4747,12 +4745,9 @@ VOID WINAPI XTL::EMUPATCH(D3DDevice_SetVertexDataColor) { LOG_FORWARD("D3DDevice_SetVertexData4f"); - FLOAT a = ((Color & 0xFF000000) >> 24) / 255.0f; - FLOAT r = ((Color & 0x00FF0000) >> 16) / 255.0f; - FLOAT g = ((Color & 0x0000FF00) >> 8) / 255.0f; - FLOAT b = ((Color & 0x000000FF) >> 0) / 255.0f; + D3DXCOLOR XColor = Color; - EMUPATCH(D3DDevice_SetVertexData4f)(Register, r, g, b, a); + EMUPATCH(D3DDevice_SetVertexData4f)(Register, XColor.r, XColor.g, XColor.b, XColor.a); } // ****************************************************************** @@ -6201,8 +6196,8 @@ VOID __fastcall XTL::EMUPATCH(D3DDevice_SetRenderState_Simple) // Fetch the RenderState conversion info for the given input int XboxRenderStateIndex = -1; - for (int i = 0; i <= X_D3DRS_DONOTCULLUNCOMPRESSED; i++) { - if (DxbxRenderStateInfo[i].M == PUSH_METHOD(Method)) { + for (int i = X_D3DRS_FIRST; i <= X_D3DRS_LAST; i++) { + if (GetDxbxRenderStateInfo(i).M == PUSH_METHOD(Method)) { XboxRenderStateIndex = i; break; } @@ -6210,11 +6205,11 @@ VOID __fastcall XTL::EMUPATCH(D3DDevice_SetRenderState_Simple) // If we could not map it, log and return if (XboxRenderStateIndex == -1) { - EmuLog(LOG_LEVEL::WARNING, "RenderState_Simple(0x%.08X (%s), 0x%.08X) could not be found in RenderState table", Method, DxbxRenderStateInfo[XboxRenderStateIndex].S, Value); + EmuLog(LOG_LEVEL::WARNING, "RenderState_Simple(0x%.08X (%s), 0x%.08X) could not be found in RenderState table", Method, GetDxbxRenderStateInfo(XboxRenderStateIndex).S, Value); return; } - EmuLog(LOG_LEVEL::DEBUG, "RenderState_Simple: %s = 0x%08X", DxbxRenderStateInfo[XboxRenderStateIndex].S, Value); + EmuLog(LOG_LEVEL::DEBUG, "RenderState_Simple: %s = 0x%08X", GetDxbxRenderStateInfo(XboxRenderStateIndex).S, Value); XboxRenderStates.SetXboxRenderState(XboxRenderStateIndex, Value); } @@ -6405,15 +6400,12 @@ VOID WINAPI XTL::EMUPATCH(D3DDevice_SetStreamSource_4) // LOG_FUNC_END; EmuLog(LOG_LEVEL::DEBUG, "D3DDevice_SetStreamSource_4(StreamNumber : %08X pStreamData : %08X Stride : %08X);", StreamNumber, pStreamData, Stride); - // Forward to Xbox implementation + CxbxImpl_SetStreamSource(StreamNumber, pStreamData, Stride); + + // TODO : Forward to Xbox implementation // This should stop us having to patch GetStreamSource! //XB_trampoline(VOID, WINAPI, D3DDevice_SetStreamSource_4, (UINT, X_D3DVertexBuffer*, UINT)); //XB_D3DDevice_SetStreamSource_4(StreamNumber, pStreamData, Stride); - - if (StreamNumber < 16) { - g_D3DStreams[StreamNumber] = pStreamData; - g_D3DStreamStrides[StreamNumber] = Stride; - } } // This uses a custom calling convention where parameter is passed in EAX @@ -6438,15 +6430,12 @@ VOID WINAPI XTL::EMUPATCH(D3DDevice_SetStreamSource_8) // LOG_FUNC_END; EmuLog(LOG_LEVEL::DEBUG, "D3DDevice_SetStreamSource_8(StreamNumber : %08X pStreamData : %08X Stride : %08X);", StreamNumber, pStreamData, Stride); + CxbxImpl_SetStreamSource(StreamNumber, pStreamData, Stride); + // TODO : Forward to Xbox implementation // This should stop us having to patch GetStreamSource! //XB_trampoline(VOID, WINAPI, D3DDevice_SetStreamSource_8, (X_D3DVertexBuffer*, UINT)); //XB_D3DDevice_SetStreamSource_8(pStreamData, Stride); - - if (StreamNumber < 16) { - g_D3DStreams[StreamNumber] = pStreamData; - g_D3DStreamStrides[StreamNumber] = Stride; - } } // ****************************************************************** @@ -6465,19 +6454,12 @@ VOID WINAPI XTL::EMUPATCH(D3DDevice_SetStreamSource) LOG_FUNC_ARG(Stride) LOG_FUNC_END; + CxbxImpl_SetStreamSource(StreamNumber, pStreamData, Stride); + // Forward to Xbox implementation // This should stop us having to patch GetStreamSource! XB_trampoline(VOID, WINAPI, D3DDevice_SetStreamSource, (UINT, X_D3DVertexBuffer*, UINT)); XB_D3DDevice_SetStreamSource(StreamNumber, pStreamData, Stride); - - if(pStreamData != xbnullptr && Stride == 0){ - LOG_TEST_CASE("Stream stride set to 0"); - } - - if (StreamNumber < 16) { - g_D3DStreams[StreamNumber] = pStreamData; - g_D3DStreamStrides[StreamNumber] = Stride; - } } // ****************************************************************** @@ -7808,7 +7790,7 @@ VOID WINAPI XTL::EMUPATCH(D3DDevice_SelectVertexShaderDirect) LOG_FUNC_ARG(Address) LOG_FUNC_END; - LOG_UNIMPLEMENTED(); + CxbxImpl_SelectVertexShaderDirect(pVAF, Address); } // ****************************************************************** @@ -7933,16 +7915,19 @@ VOID WINAPI XTL::EMUPATCH(D3DDevice_SetVertexShaderInput) LOG_FUNC_ARG(pStreamInputs) LOG_FUNC_END; - // If Handle is NULL, all VertexShader input state is cleared. + // When this API is in effect, VertexBuffers as set by Xbox SetStreamSource are disregarded, + // instead, the pStreamInputs[].VertexBuffer streams are used. + + // If Handle is NULL, all VertexShader input state is cleared (after which the VertexBuffers as set by SetStreamSource are used once again). + // Otherwise, Handle is the address of an Xbox VertexShader struct, or-ed with 1 (X_D3DFVF_RESERVED0) + // The given pStreamInputs are stored in a global array, and the NV2A is programmed to read + // each vertex attribute (as defined in the given VertexShader.VertexAttribute.Slots[]) to read + // the attribute data from the pStreamInputs[slot].VertexBuffer + pStreamInputs[slot].Offset + VertexShader.VertexAttribute.Slots[slot].Offset - - LOG_UNIMPLEMENTED(); - - return; + CxbxImpl_SetVertexShaderInput(Handle, StreamCount, pStreamInputs); } - // ****************************************************************** // * patch: D3DDevice_RunVertexStateShader // ****************************************************************** @@ -7957,6 +7942,9 @@ VOID WINAPI XTL::EMUPATCH(D3DDevice_RunVertexStateShader) LOG_FUNC_ARG(pData) LOG_FUNC_END; + // If pData is assigned, pData[0..3] is pushed towards nv2a transform data registers + // then sends the nv2a a command to launch the vertex shader function located at Address + LOG_UNIMPLEMENTED(); } diff --git a/src/core/hle/D3D8/Direct3D9/RenderStates.cpp b/src/core/hle/D3D8/Direct3D9/RenderStates.cpp index 297080e6c..27ff00bff 100644 --- a/src/core/hle/D3D8/Direct3D9/RenderStates.cpp +++ b/src/core/hle/D3D8/Direct3D9/RenderStates.cpp @@ -57,6 +57,17 @@ bool XboxRenderStateConverter::Init() return true; } +bool IsRenderStateAvailableInCurrentXboxD3D8Lib(RenderStateInfo& aRenderStateInfo) +{ + bool bIsRenderStateAvailable = (aRenderStateInfo.V <= g_LibVersion_D3D8); + if (aRenderStateInfo.R > 0) { // Applies to XTL::X_D3DRS_MULTISAMPLETYPE + // Note : X_D3DRS_MULTISAMPLETYPE seems the only render state that got + // removed (from 4039 onwards), so we check that limitation here as well + bIsRenderStateAvailable &= (g_LibVersion_D3D8 < aRenderStateInfo.R); + } + return bIsRenderStateAvailable; +} + void XboxRenderStateConverter::VerifyAndFixDeferredRenderStateOffset() { DWORD CullModeOffset = g_SymbolAddresses["D3DRS_CULLMODE"]; @@ -68,8 +79,9 @@ void XboxRenderStateConverter::VerifyAndFixDeferredRenderStateOffset() // Calculate index of D3DRS_CULLMODE for this XDK. We start counting from the first deferred state (D3DRS_FOGENABLE) DWORD CullModeIndex = 0; - for (int i = XTL::X_D3DRS_FOGENABLE; i < XTL::X_D3DRS_CULLMODE; i++) { - if (DxbxRenderStateInfo[i].V <= g_LibVersion_D3D8) { + for (int i = XTL::X_D3DRS_DEFERRED_FIRST; i < XTL::X_D3DRS_CULLMODE; i++) { + auto RenderStateInfo = GetDxbxRenderStateInfo(i); + if (IsRenderStateAvailableInCurrentXboxD3D8Lib(RenderStateInfo)) { CullModeIndex++; } } @@ -85,22 +97,23 @@ void XboxRenderStateConverter::VerifyAndFixDeferredRenderStateOffset() void XboxRenderStateConverter::DeriveRenderStateOffsetFromDeferredRenderStateOffset() { // When this function is called. D3D__RenderState actually points to the first deferred render state - // this is D3DRS_FOGENABLE. We can count back from this using our RenderStateInfo table to find + // (this is X_D3DRS_FOGENABLE). We can count back from this using our RenderStateInfo table to find // the start of D3D__RenderStates. - // Count the number of render states (for this XDK) between 0 and D3DRS_FOGENABLE - int FogEnableOffset = 0; - for (unsigned int RenderState = XTL::X_D3DRS_PSALPHAINPUTS0; RenderState < XTL::X_D3DRS_FOGENABLE; RenderState++) { + // Count the number of render states (for this XDK) between 0 and the first deferred render state (D3DRS_FOGENABLE) + int FirstDeferredRenderStateOffset = 0; + for (unsigned int RenderState = XTL::X_D3DRS_FIRST; RenderState < XTL::X_D3DRS_DEFERRED_FIRST; RenderState++) { // if the current renderstate exists in this XDK version, count it - if (DxbxRenderStateInfo[RenderState].V <= g_LibVersion_D3D8) { - FogEnableOffset++; + auto RenderStateInfo = GetDxbxRenderStateInfo(RenderState); + if (IsRenderStateAvailableInCurrentXboxD3D8Lib(RenderStateInfo)) { + FirstDeferredRenderStateOffset++; } } - // At this point, FogEnableOffset should point to the index of D3DRS_FOGENABLE for the given XDK + // At this point, FirstDeferredRenderStateOffset should point to the index of D3DRS_FOGENABLE for the given XDK // This will be correct as long as our table DxbxRenderStateInfo is correct // We can get the correct 0 offset by using a negative index - D3D__RenderState = &D3D__RenderState[-FogEnableOffset]; + D3D__RenderState = &D3D__RenderState[-FirstDeferredRenderStateOffset]; } void XboxRenderStateConverter::BuildRenderStateMappingTable() @@ -110,15 +123,16 @@ void XboxRenderStateConverter::BuildRenderStateMappingTable() XboxRenderStateOffsets.fill(-1); int XboxIndex = 0; - for (unsigned int RenderState = XTL::X_D3DRS_PSALPHAINPUTS0; RenderState <= XTL::X_D3DRS_LAST; RenderState++) { - if (DxbxRenderStateInfo[RenderState].V <= g_LibVersion_D3D8) { + for (unsigned int RenderState = XTL::X_D3DRS_FIRST; RenderState <= XTL::X_D3DRS_LAST; RenderState++) { + auto RenderStateInfo = GetDxbxRenderStateInfo(RenderState); + if (IsRenderStateAvailableInCurrentXboxD3D8Lib(RenderStateInfo)) { XboxRenderStateOffsets[RenderState] = XboxIndex; - EmuLog(LOG_LEVEL::INFO, "%s = %d", DxbxRenderStateInfo[RenderState].S, XboxIndex); + EmuLog(LOG_LEVEL::INFO, "%s = %d", RenderStateInfo.S, XboxIndex); XboxIndex++; continue; } - EmuLog(LOG_LEVEL::INFO, "%s Not Present", DxbxRenderStateInfo[RenderState].S); + EmuLog(LOG_LEVEL::INFO, "%s Not Present", RenderStateInfo.S); } } @@ -153,7 +167,7 @@ bool XboxRenderStateConverter::XboxRenderStateValueChanged(uint32_t State) void XboxRenderStateConverter::SetXboxRenderState(uint32_t State, uint32_t Value) { if (!XboxRenderStateExists(State)) { - EmuLog(LOG_LEVEL::WARNING, "Attempt to write a Renderstate (%s) that does not exist in the current D3D8 XDK Version (%d)", DxbxRenderStateInfo[State].S, g_LibVersion_D3D8); + EmuLog(LOG_LEVEL::WARNING, "Attempt to write a Renderstate (%s) that does not exist in the current D3D8 XDK Version (%d)", GetDxbxRenderStateInfo(State).S, g_LibVersion_D3D8); return; } @@ -163,7 +177,7 @@ void XboxRenderStateConverter::SetXboxRenderState(uint32_t State, uint32_t Value uint32_t XboxRenderStateConverter::GetXboxRenderState(uint32_t State) { if (!XboxRenderStateExists(State)) { - EmuLog(LOG_LEVEL::WARNING, "Attempt to read a Renderstate (%s) that does not exist in the current D3D8 XDK Version (%d)", DxbxRenderStateInfo[State].S, g_LibVersion_D3D8); + EmuLog(LOG_LEVEL::WARNING, "Attempt to read a Renderstate (%s) that does not exist in the current D3D8 XDK Version (%d)", GetDxbxRenderStateInfo(State).S, g_LibVersion_D3D8); return 0; } @@ -172,7 +186,7 @@ uint32_t XboxRenderStateConverter::GetXboxRenderState(uint32_t State) void XboxRenderStateConverter::StoreInitialValues() { - for (unsigned int RenderState = XTL::X_D3DRS_PSALPHAINPUTS0; RenderState <= XTL::X_D3DRS_LAST; RenderState++) { + for (unsigned int RenderState = XTL::X_D3DRS_FIRST; RenderState <= XTL::X_D3DRS_LAST; RenderState++) { // Skip Render States that don't exist within this XDK if (!XboxRenderStateExists(RenderState)) { continue; @@ -203,7 +217,7 @@ void XboxRenderStateConverter::Apply() } auto Value = GetXboxRenderState(RenderState); - EmuLog(LOG_LEVEL::DEBUG, "XboxRenderStateConverter::Apply(%s, %X)\n", DxbxRenderStateInfo[RenderState].S, Value); + EmuLog(LOG_LEVEL::DEBUG, "XboxRenderStateConverter::Apply(%s, %X)\n", GetDxbxRenderStateInfo(RenderState).S, Value); if (RenderState <= XTL::X_D3DRS_SIMPLE_LAST) { ApplySimpleRenderState(RenderState, Value); @@ -219,6 +233,8 @@ void XboxRenderStateConverter::Apply() void XboxRenderStateConverter::ApplySimpleRenderState(uint32_t State, uint32_t Value) { + auto RenderStateInfo = GetDxbxRenderStateInfo(State); + switch (State) { case XTL::X_D3DRS_COLORWRITEENABLE: { DWORD OrigValue = Value; @@ -278,23 +294,25 @@ void XboxRenderStateConverter::ApplySimpleRenderState(uint32_t State, uint32_t V break; default: // Only log missing state if it has a PC counterpart - if (DxbxRenderStateInfo[State].PC != 0) { - EmuLog(LOG_LEVEL::WARNING, "ApplySimpleRenderState(%s, 0x%.08X) is unimplemented!", DxbxRenderStateInfo[State].S, Value); + if (RenderStateInfo.PC != 0) { + EmuLog(LOG_LEVEL::WARNING, "ApplySimpleRenderState(%s, 0x%.08X) is unimplemented!", RenderStateInfo.S, Value); } return; } // Skip RenderStates that don't have a defined PC counterpart - if (DxbxRenderStateInfo[State].PC == 0) { + if (RenderStateInfo.PC == 0) { return; } - g_pD3DDevice->SetRenderState((D3DRENDERSTATETYPE)(DxbxRenderStateInfo[State].PC), Value); + g_pD3DDevice->SetRenderState((D3DRENDERSTATETYPE)(RenderStateInfo.PC), Value); } void XboxRenderStateConverter::ApplyDeferredRenderState(uint32_t State, uint32_t Value) { - // Convert from Xbox Data Formats to PC + auto RenderStateInfo = GetDxbxRenderStateInfo(State); + + // Convert from Xbox Data Formats to PC switch (State) { case XTL::X_D3DRS_FOGSTART: case XTL::X_D3DRS_FOGEND: { @@ -373,23 +391,25 @@ void XboxRenderStateConverter::ApplyDeferredRenderState(uint32_t State, uint32_t } break; default: // Only log missing state if it has a PC counterpart - if (DxbxRenderStateInfo[State].PC != 0) { - EmuLog(LOG_LEVEL::WARNING, "ApplyDeferredRenderState(%s, 0x%.08X) is unimplemented!", DxbxRenderStateInfo[State].S, Value); + if (RenderStateInfo.PC != 0) { + EmuLog(LOG_LEVEL::WARNING, "ApplyDeferredRenderState(%s, 0x%.08X) is unimplemented!", RenderStateInfo.S, Value); } return; } // Skip RenderStates that don't have a defined PC counterpart - if (DxbxRenderStateInfo[State].PC == 0) { + if (RenderStateInfo.PC == 0) { return; } - g_pD3DDevice->SetRenderState(DxbxRenderStateInfo[State].PC, Value); + g_pD3DDevice->SetRenderState(RenderStateInfo.PC, Value); } void XboxRenderStateConverter::ApplyComplexRenderState(uint32_t State, uint32_t Value) { - switch (State) { + auto RenderStateInfo = GetDxbxRenderStateInfo(State); + + switch (State) { case XTL::X_D3DRS_VERTEXBLEND: // convert from Xbox direct3d to PC direct3d enumeration if (Value <= 1) { @@ -439,16 +459,16 @@ void XboxRenderStateConverter::ApplyComplexRenderState(uint32_t State, uint32_t break; default: // Only log missing state if it has a PC counterpart - if (DxbxRenderStateInfo[State].PC != 0) { - EmuLog(LOG_LEVEL::WARNING, "ApplyComplexRenderState(%s, 0x%.08X) is unimplemented!", DxbxRenderStateInfo[State].S, Value); + if (RenderStateInfo.PC != 0) { + EmuLog(LOG_LEVEL::WARNING, "ApplyComplexRenderState(%s, 0x%.08X) is unimplemented!", RenderStateInfo.S, Value); } return; } // Skip RenderStates that don't have a defined PC counterpart - if (DxbxRenderStateInfo[State].PC == 0) { + if (RenderStateInfo.PC == 0) { return; } - g_pD3DDevice->SetRenderState(DxbxRenderStateInfo[State].PC, Value); + g_pD3DDevice->SetRenderState(RenderStateInfo.PC, Value); } diff --git a/src/core/hle/D3D8/XbConvert.cpp b/src/core/hle/D3D8/XbConvert.cpp index fa3bfb111..ddd6aee47 100644 --- a/src/core/hle/D3D8/XbConvert.cpp +++ b/src/core/hle/D3D8/XbConvert.cpp @@ -28,7 +28,9 @@ #define LOG_PREFIX CXBXR_MODULE::D3DCVT -#include "core\kernel\support\Emu.h" +#include "common\Settings.hpp" // for g_LibVersion_D3D8 +#include "core\kernel\support\Emu.h" + #include "XbConvert.h" // About format color components: @@ -1330,10 +1332,18 @@ void EmuUnswizzleBox } } } // EmuUnswizzleBox NOPATCH - + +// Notes : +// * most renderstates were introduced in the (lowest known) XDK version : 3424 +// * additional renderstates were introduced between 3434 and 4627 +// * we MUST list exact versions for each of those, since their inserts impacts mapping! +// * renderstates were finalized in 4627 (so no change after that version) +// * renderstates after D3DRS_MULTISAMPLEMASK have no host mapping, thus no impact +// * D3DRS_MULTISAMPLETYPE seems the only renderstate that got removed (after 3944, before 4039) +// * all renderstates marked 3424 are also verified present in 3944 const RenderStateInfo DxbxRenderStateInfo[] = { - // String Ord Version Type Method Native + // String Ord Version Type Method Native { "D3DRS_PSALPHAINPUTS0" /*= 0*/, 3424, xtDWORD, NV2A_RC_IN_ALPHA(0) }, { "D3DRS_PSALPHAINPUTS1" /*= 1*/, 3424, xtDWORD, NV2A_RC_IN_ALPHA(1) }, { "D3DRS_PSALPHAINPUTS2" /*= 2*/, 3424, xtDWORD, NV2A_RC_IN_ALPHA(2) }, @@ -1413,69 +1423,70 @@ const RenderStateInfo DxbxRenderStateInfo[] = { { "D3DRS_BLENDCOLOR" /*= 75*/, 3424, xtD3DCOLOR, NV2A_BLEND_COLOR, D3DRS_BLENDFACTOR, "D3DCOLOR for D3DBLEND_CONSTANTCOLOR" }, // D3D9 D3DRS_BLENDFACTOR : D3DCOLOR used for a constant blend factor during alpha blending for devices that support D3DPBLENDCAPS_BLENDFACTOR { "D3DRS_SWATHWIDTH" /*= 76*/, 3424, xtD3DSWATH, NV2A_SWATH_WIDTH }, - { "D3DRS_POLYGONOFFSETZSLOPESCALE" /*= 77*/, 3424, xtFloat, NV2A_POLYGON_OFFSET_FACTOR, D3DRS_NONE, "float Z factor for shadow maps" }, + { "D3DRS_POLYGONOFFSETZSLOPESCALE" /*= 77*/, 3424, xtFloat, NV2A_POLYGON_OFFSET_FACTOR, D3DRS_UNSUPPORTED, "float Z factor for shadow maps" }, { "D3DRS_POLYGONOFFSETZOFFSET" /*= 78*/, 3424, xtFloat, NV2A_POLYGON_OFFSET_UNITS }, { "D3DRS_POINTOFFSETENABLE" /*= 79*/, 3424, xtBOOL, NV2A_POLYGON_OFFSET_POINT_ENABLE }, { "D3DRS_WIREFRAMEOFFSETENABLE" /*= 80*/, 3424, xtBOOL, NV2A_POLYGON_OFFSET_LINE_ENABLE }, { "D3DRS_SOLIDOFFSETENABLE" /*= 81*/, 3424, xtBOOL, NV2A_POLYGON_OFFSET_FILL_ENABLE }, - { "D3DRS_DEPTHCLIPCONTROL" /*= 82*/, 4432, xtD3DDCC, NV2A_DEPTHCLIPCONTROL }, - { "D3DRS_STIPPLEENABLE" /*= 83*/, 4627, xtBOOL, NV2A_POLYGON_STIPPLE_ENABLE }, - { "D3DRS_SIMPLE_UNUSED8" /*= 84*/, 4627, xtDWORD, 0 }, - { "D3DRS_SIMPLE_UNUSED7" /*= 85*/, 4627, xtDWORD, 0 }, - { "D3DRS_SIMPLE_UNUSED6" /*= 86*/, 4627, xtDWORD, 0 }, - { "D3DRS_SIMPLE_UNUSED5" /*= 87*/, 4627, xtDWORD, 0 }, - { "D3DRS_SIMPLE_UNUSED4" /*= 88*/, 4627, xtDWORD, 0 }, - { "D3DRS_SIMPLE_UNUSED3" /*= 89*/, 4627, xtDWORD, 0 }, - { "D3DRS_SIMPLE_UNUSED2" /*= 90*/, 4627, xtDWORD, 0 }, - { "D3DRS_SIMPLE_UNUSED1" /*= 91*/, 4627, xtDWORD, 0 }, + { "D3DRS_DEPTHCLIPCONTROL" /*= 82*/, 4432, xtD3DDCC, NV2A_DEPTHCLIPCONTROL }, // Verified absent in 4361, present in 4432 TODO : Might be introduced around 4400? + { "D3DRS_STIPPLEENABLE" /*= 83*/, 4627, xtBOOL, NV2A_POLYGON_STIPPLE_ENABLE }, // Verified absent in 4531, present in 4627 TODO : might be introduced in between? + { "D3DRS_SIMPLE_UNUSED8" /*= 84*/, 4627, xtDWORD, 0 }, // Verified absent in 4531, present in 4627 TODO : might be introduced in between? + { "D3DRS_SIMPLE_UNUSED7" /*= 85*/, 4627, xtDWORD, 0 }, // Verified absent in 4531, present in 4627 TODO : might be introduced in between? + { "D3DRS_SIMPLE_UNUSED6" /*= 86*/, 4627, xtDWORD, 0 }, // Verified absent in 4531, present in 4627 TODO : might be introduced in between? + { "D3DRS_SIMPLE_UNUSED5" /*= 87*/, 4627, xtDWORD, 0 }, // Verified absent in 4531, present in 4627 TODO : might be introduced in between? + { "D3DRS_SIMPLE_UNUSED4" /*= 88*/, 4627, xtDWORD, 0 }, // Verified absent in 4531, present in 4627 TODO : might be introduced in between? + { "D3DRS_SIMPLE_UNUSED3" /*= 89*/, 4627, xtDWORD, 0 }, // Verified absent in 4531, present in 4627 TODO : might be introduced in between? + { "D3DRS_SIMPLE_UNUSED2" /*= 90*/, 4627, xtDWORD, 0 }, // Verified absent in 4531, present in 4627 TODO : might be introduced in between? + { "D3DRS_SIMPLE_UNUSED1" /*= 91*/, 4627, xtDWORD, 0 }, // Verified absent in 4531, present in 4627 TODO : might be introduced in between? // End of "simple" render states, continuing with "deferred" render states : - { "D3DRS_FOGENABLE" /*= 92*/, 3424, xtBOOL, NV2A_FOG_ENABLE, D3DRS_FOGENABLE }, - { "D3DRS_FOGTABLEMODE" /*= 93*/, 3424, xtD3DFOGMODE, NV2A_FOG_MODE, D3DRS_FOGTABLEMODE }, - { "D3DRS_FOGSTART" /*= 94*/, 3424, xtFloat, NV2A_FOG_COORD_DIST, D3DRS_FOGSTART }, - { "D3DRS_FOGEND" /*= 95*/, 3424, xtFloat, NV2A_FOG_MODE, D3DRS_FOGEND }, - { "D3DRS_FOGDENSITY" /*= 96*/, 3424, xtFloat, NV2A_FOG_EQUATION_CONSTANT, D3DRS_FOGDENSITY }, // + NV2A_FOG_EQUATION_LINEAR + NV2A_FOG_EQUATION_QUADRATIC - { "D3DRS_RANGEFOGENABLE" /*= 97*/, 3424, xtBOOL, NV2A_FOG_COORD_DIST, D3DRS_RANGEFOGENABLE }, - { "D3DRS_WRAP0" /*= 98*/, 3424, xtD3DWRAP, NV2A_TX_WRAP(0), D3DRS_WRAP0 }, - { "D3DRS_WRAP1" /*= 99*/, 3424, xtD3DWRAP, NV2A_TX_WRAP(1), D3DRS_WRAP1 }, - { "D3DRS_WRAP2" /*= 100*/, 3424, xtD3DWRAP, NV2A_TX_WRAP(2), D3DRS_WRAP2 }, - { "D3DRS_WRAP3" /*= 101*/, 3424, xtD3DWRAP, NV2A_TX_WRAP(3), D3DRS_WRAP3 }, - { "D3DRS_LIGHTING" /*= 102*/, 3424, xtBOOL, NV2A_LIGHT_MODEL, D3DRS_LIGHTING }, // TODO : Needs push-buffer data conversion - { "D3DRS_SPECULARENABLE" /*= 103*/, 3424, xtBOOL, NV2A_RC_FINAL0, D3DRS_SPECULARENABLE }, - { "D3DRS_LOCALVIEWER" /*= 104*/, 3424, xtBOOL, 0, D3DRS_LOCALVIEWER }, - { "D3DRS_COLORVERTEX" /*= 105*/, 3424, xtBOOL, 0, D3DRS_COLORVERTEX }, - { "D3DRS_BACKSPECULARMATERIALSOURCE" /*= 106*/, 3424, xtD3DMCS, 0 }, // nsp. - { "D3DRS_BACKDIFFUSEMATERIALSOURCE" /*= 107*/, 3424, xtD3DMCS, 0 }, // nsp. - { "D3DRS_BACKAMBIENTMATERIALSOURCE" /*= 108*/, 3424, xtD3DMCS, 0 }, // nsp. - { "D3DRS_BACKEMISSIVEMATERIALSOURCE" /*= 109*/, 3424, xtD3DMCS, 0 }, // nsp. - { "D3DRS_SPECULARMATERIALSOURCE" /*= 110*/, 3424, xtD3DMCS, NV2A_COLOR_MATERIAL, D3DRS_SPECULARMATERIALSOURCE }, - { "D3DRS_DIFFUSEMATERIALSOURCE" /*= 111*/, 3424, xtD3DMCS, 0, D3DRS_DIFFUSEMATERIALSOURCE }, - { "D3DRS_AMBIENTMATERIALSOURCE" /*= 112*/, 3424, xtD3DMCS, 0, D3DRS_AMBIENTMATERIALSOURCE }, - { "D3DRS_EMISSIVEMATERIALSOURCE" /*= 113*/, 3424, xtD3DMCS, 0, D3DRS_EMISSIVEMATERIALSOURCE }, - { "D3DRS_BACKAMBIENT" /*= 114*/, 3424, xtD3DCOLOR, NV2A_LIGHT_MODEL_BACK_SIDE_PRODUCT_AMBIENT_PLUS_EMISSION_R }, // ..NV2A_MATERIAL_FACTOR_BACK_B nsp. Was NV2A_LIGHT_MODEL_BACK_AMBIENT_R - { "D3DRS_AMBIENT" /*= 115*/, 3424, xtD3DCOLOR, NV2A_LIGHT_MODEL_FRONT_SIDE_PRODUCT_AMBIENT_PLUS_EMISSION_R, D3DRS_AMBIENT }, // ..NV2A_LIGHT_MODEL_FRONT_AMBIENT_B + NV2A_MATERIAL_FACTOR_FRONT_R..NV2A_MATERIAL_FACTOR_FRONT_A Was NV2A_LIGHT_MODEL_FRONT_AMBIENT_R - { "D3DRS_POINTSIZE" /*= 116*/, 3424, xtFloat, NV2A_POINT_PARAMETER(0), D3DRS_POINTSIZE }, - { "D3DRS_POINTSIZE_MIN" /*= 117*/, 3424, xtFloat, 0, D3DRS_POINTSIZE_MIN }, - { "D3DRS_POINTSPRITEENABLE" /*= 118*/, 3424, xtBOOL, NV2A_POINT_SMOOTH_ENABLE, D3DRS_POINTSPRITEENABLE }, - { "D3DRS_POINTSCALEENABLE" /*= 119*/, 3424, xtBOOL, NV2A_POINT_PARAMETERS_ENABLE, D3DRS_POINTSCALEENABLE }, - { "D3DRS_POINTSCALE_A" /*= 120*/, 3424, xtFloat, 0, D3DRS_POINTSCALE_A }, - { "D3DRS_POINTSCALE_B" /*= 121*/, 3424, xtFloat, 0, D3DRS_POINTSCALE_B }, - { "D3DRS_POINTSCALE_C" /*= 122*/, 3424, xtFloat, 0, D3DRS_POINTSCALE_C }, - { "D3DRS_POINTSIZE_MAX" /*= 123*/, 3424, xtFloat, 0, D3DRS_POINTSIZE_MAX }, - { "D3DRS_PATCHEDGESTYLE" /*= 124*/, 3424, xtDWORD, 0, D3DRS_PATCHEDGESTYLE }, // D3DPATCHEDGESTYLE? - { "D3DRS_PATCHSEGMENTS" /*= 125*/, 3424, xtDWORD, 0 }, // nsp. // D3DRS_PATCHSEGMENTS exists in Direct3D 8, but not in 9 !? + // Verified as XDK 3911 Deferred RenderStates (3424 yet to do) + { "D3DRS_FOGENABLE" /*= 92*/, 3424, xtBOOL, NV2A_FOG_ENABLE, D3DRS_FOGENABLE }, // TRUE to enable fog blending + { "D3DRS_FOGTABLEMODE" /*= 93*/, 3424, xtD3DFOGMODE, NV2A_FOG_MODE, D3DRS_FOGTABLEMODE }, // D3DFOGMODE + { "D3DRS_FOGSTART" /*= 94*/, 3424, xtFloat, NV2A_FOG_COORD_DIST, D3DRS_FOGSTART }, // float fog start (for both vertex and pixel fog) + { "D3DRS_FOGEND" /*= 95*/, 3424, xtFloat, NV2A_FOG_MODE, D3DRS_FOGEND }, // float fog end + { "D3DRS_FOGDENSITY" /*= 96*/, 3424, xtFloat, NV2A_FOG_EQUATION_CONSTANT, D3DRS_FOGDENSITY }, // float fog density // + NV2A_FOG_EQUATION_LINEAR + NV2A_FOG_EQUATION_QUADRATIC + { "D3DRS_RANGEFOGENABLE" /*= 97*/, 3424, xtBOOL, NV2A_FOG_COORD_DIST, D3DRS_RANGEFOGENABLE }, // TRUE to enable range-based fog + { "D3DRS_WRAP0" /*= 98*/, 3424, xtD3DWRAP, NV2A_TX_WRAP(0), D3DRS_WRAP0 }, // D3DWRAP* flags (D3DWRAP_U, D3DWRAPCOORD_0, etc.) for 1st texture coord. + { "D3DRS_WRAP1" /*= 99*/, 3424, xtD3DWRAP, NV2A_TX_WRAP(1), D3DRS_WRAP1 }, // D3DWRAP* flags (D3DWRAP_U, D3DWRAPCOORD_0, etc.) for 2nd texture coord. + { "D3DRS_WRAP2" /*= 100*/, 3424, xtD3DWRAP, NV2A_TX_WRAP(2), D3DRS_WRAP2 }, // D3DWRAP* flags (D3DWRAP_U, D3DWRAPCOORD_0, etc.) for 3rd texture coord. + { "D3DRS_WRAP3" /*= 101*/, 3424, xtD3DWRAP, NV2A_TX_WRAP(3), D3DRS_WRAP3 }, // D3DWRAP* flags (D3DWRAP_U, D3DWRAPCOORD_0, etc.) for 4th texture coord. + { "D3DRS_LIGHTING" /*= 102*/, 3424, xtBOOL, NV2A_LIGHT_MODEL, D3DRS_LIGHTING }, // TRUE to enable lighting // TODO : Needs push-buffer data conversion + { "D3DRS_SPECULARENABLE" /*= 103*/, 3424, xtBOOL, NV2A_RC_FINAL0, D3DRS_SPECULARENABLE }, // TRUE to enable specular + { "D3DRS_LOCALVIEWER" /*= 104*/, 3424, xtBOOL, 0, D3DRS_LOCALVIEWER }, // TRUE to enable camera-relative specular highlights + { "D3DRS_COLORVERTEX" /*= 105*/, 3424, xtBOOL, 0, D3DRS_COLORVERTEX }, // TRUE to enable per-vertex color + { "D3DRS_BACKSPECULARMATERIALSOURCE" /*= 106*/, 3424, xtD3DMCS, 0 }, // D3DMATERIALCOLORSOURCE (Xbox extension) nsp. + { "D3DRS_BACKDIFFUSEMATERIALSOURCE" /*= 107*/, 3424, xtD3DMCS, 0 }, // D3DMATERIALCOLORSOURCE (Xbox extension) nsp. + { "D3DRS_BACKAMBIENTMATERIALSOURCE" /*= 108*/, 3424, xtD3DMCS, 0 }, // D3DMATERIALCOLORSOURCE (Xbox extension) nsp. + { "D3DRS_BACKEMISSIVEMATERIALSOURCE" /*= 109*/, 3424, xtD3DMCS, 0 }, // D3DMATERIALCOLORSOURCE (Xbox extension) nsp. + { "D3DRS_SPECULARMATERIALSOURCE" /*= 110*/, 3424, xtD3DMCS, NV2A_COLOR_MATERIAL, D3DRS_SPECULARMATERIALSOURCE }, // D3DMATERIALCOLORSOURCE + { "D3DRS_DIFFUSEMATERIALSOURCE" /*= 111*/, 3424, xtD3DMCS, 0, D3DRS_DIFFUSEMATERIALSOURCE }, // D3DMATERIALCOLORSOURCE + { "D3DRS_AMBIENTMATERIALSOURCE" /*= 112*/, 3424, xtD3DMCS, 0, D3DRS_AMBIENTMATERIALSOURCE }, // D3DMATERIALCOLORSOURCE + { "D3DRS_EMISSIVEMATERIALSOURCE" /*= 113*/, 3424, xtD3DMCS, 0, D3DRS_EMISSIVEMATERIALSOURCE }, // D3DMATERIALCOLORSOURCE + { "D3DRS_BACKAMBIENT" /*= 114*/, 3424, xtD3DCOLOR, NV2A_LIGHT_MODEL_BACK_SIDE_PRODUCT_AMBIENT_PLUS_EMISSION_R }, // D3DCOLOR (Xbox extension) // ..NV2A_MATERIAL_FACTOR_BACK_B nsp. Was NV2A_LIGHT_MODEL_BACK_AMBIENT_R + { "D3DRS_AMBIENT" /*= 115*/, 3424, xtD3DCOLOR, NV2A_LIGHT_MODEL_FRONT_SIDE_PRODUCT_AMBIENT_PLUS_EMISSION_R, D3DRS_AMBIENT }, // D3DCOLOR // ..NV2A_LIGHT_MODEL_FRONT_AMBIENT_B + NV2A_MATERIAL_FACTOR_FRONT_R..NV2A_MATERIAL_FACTOR_FRONT_A Was NV2A_LIGHT_MODEL_FRONT_AMBIENT_R + { "D3DRS_POINTSIZE" /*= 116*/, 3424, xtFloat, NV2A_POINT_PARAMETER(0), D3DRS_POINTSIZE }, // float point size + { "D3DRS_POINTSIZE_MIN" /*= 117*/, 3424, xtFloat, 0, D3DRS_POINTSIZE_MIN }, // float point size min threshold + { "D3DRS_POINTSPRITEENABLE" /*= 118*/, 3424, xtBOOL, NV2A_POINT_SMOOTH_ENABLE, D3DRS_POINTSPRITEENABLE }, // TRUE to enable point sprites + { "D3DRS_POINTSCALEENABLE" /*= 119*/, 3424, xtBOOL, NV2A_POINT_PARAMETERS_ENABLE, D3DRS_POINTSCALEENABLE }, // TRUE to enable point size scaling + { "D3DRS_POINTSCALE_A" /*= 120*/, 3424, xtFloat, 0, D3DRS_POINTSCALE_A }, // float point attenuation A value + { "D3DRS_POINTSCALE_B" /*= 121*/, 3424, xtFloat, 0, D3DRS_POINTSCALE_B }, // float point attenuation B value + { "D3DRS_POINTSCALE_C" /*= 122*/, 3424, xtFloat, 0, D3DRS_POINTSCALE_C }, // float point attenuation C value + { "D3DRS_POINTSIZE_MAX" /*= 123*/, 3424, xtFloat, 0, D3DRS_POINTSIZE_MAX }, // float point size max threshold + { "D3DRS_PATCHEDGESTYLE" /*= 124*/, 3424, xtDWORD, 0, D3DRS_PATCHEDGESTYLE }, // D3DPATCHEDGESTYLE + { "D3DRS_PATCHSEGMENTS" /*= 125*/, 3424, xtDWORD, 0 }, // DWORD number of segments per edge when drawing patches, nsp (D3DRS_PATCHSEGMENTS exists in Direct3D 8, but not in 9) // TODO -oDxbx : Is X_D3DRS_SWAPFILTER really a xtD3DMULTISAMPLE_TYPE? - { "D3DRS_SWAPFILTER" /*= 126*/, 4039, xtD3DMULTISAMPLE_TYPE, 0, D3DRS_NONE, "D3DTEXF_LINEAR etc. filter to use for Swap" }, // nsp. - { "D3DRS_PRESENTATIONINTERVAL" /*= 127*/, 4627, xtDWORD, 0 }, // nsp. - { "D3DRS_DEFERRED_UNUSED8" /*= 128*/, 4627, xtDWORD, 0 }, - { "D3DRS_DEFERRED_UNUSED7" /*= 129*/, 4627, xtDWORD, 0 }, - { "D3DRS_DEFERRED_UNUSED6" /*= 130*/, 4627, xtDWORD, 0 }, - { "D3DRS_DEFERRED_UNUSED5" /*= 131*/, 4627, xtDWORD, 0 }, - { "D3DRS_DEFERRED_UNUSED4" /*= 132*/, 4627, xtDWORD, 0 }, - { "D3DRS_DEFERRED_UNUSED3" /*= 133*/, 4627, xtDWORD, 0 }, - { "D3DRS_DEFERRED_UNUSED2" /*= 134*/, 4627, xtDWORD, 0 }, - { "D3DRS_DEFERRED_UNUSED1" /*= 135*/, 4627, xtDWORD, 0 }, + { "D3DRS_SWAPFILTER" /*= 126*/, 4039, xtD3DMULTISAMPLE_TYPE, 0, D3DRS_UNSUPPORTED, "D3DTEXF_LINEAR etc. filter to use for Swap" }, // nsp. Verified absent in 3944, present in 4039 TODO : Might be introduced in 4034? + { "D3DRS_PRESENTATIONINTERVAL" /*= 127*/, 4627, xtDWORD, 0 }, // nsp. Verified absent in 4531, present in 4627 TODO : might be introduced in between? + { "D3DRS_DEFERRED_UNUSED8" /*= 128*/, 4627, xtDWORD, 0 }, // Verified absent in 4531, present in 4627 TODO : might be introduced in between? + { "D3DRS_DEFERRED_UNUSED7" /*= 129*/, 4627, xtDWORD, 0 }, // Verified absent in 4531, present in 4627 TODO : might be introduced in between? + { "D3DRS_DEFERRED_UNUSED6" /*= 130*/, 4627, xtDWORD, 0 }, // Verified absent in 4531, present in 4627 TODO : might be introduced in between? + { "D3DRS_DEFERRED_UNUSED5" /*= 131*/, 4627, xtDWORD, 0 }, // Verified absent in 4531, present in 4627 TODO : might be introduced in between? + { "D3DRS_DEFERRED_UNUSED4" /*= 132*/, 4627, xtDWORD, 0 }, // Verified absent in 4531, present in 4627 TODO : might be introduced in between? + { "D3DRS_DEFERRED_UNUSED3" /*= 133*/, 4627, xtDWORD, 0 }, // Verified absent in 4531, present in 4627 TODO : might be introduced in between? + { "D3DRS_DEFERRED_UNUSED2" /*= 134*/, 4627, xtDWORD, 0 }, // Verified absent in 4531, present in 4627 TODO : might be introduced in between? + { "D3DRS_DEFERRED_UNUSED1" /*= 135*/, 4627, xtDWORD, 0 }, // Verified absent in 4531, present in 4627 TODO : might be introduced in between? // End of "deferred" render states, continuing with "complex" render states : - { "D3DRS_PSTEXTUREMODES" /*= 136*/, 3424, xtDWORD, 0 }, + { "D3DRS_PSTEXTUREMODES" /*= 136*/, 3424, xtDWORD, 0 }, // This is where pPSDef->PSTextureModes is stored (outside the pPSDEF - see DxbxUpdateActivePixelShader) { "D3DRS_VERTEXBLEND" /*= 137*/, 3424, xtD3DVERTEXBLENDFLAGS, NV2A_SKIN_MODE, D3DRS_VERTEXBLEND }, { "D3DRS_FOGCOLOR" /*= 138*/, 3424, xtD3DCOLOR, NV2A_FOG_COLOR, D3DRS_FOGCOLOR }, // SwapRgb { "D3DRS_FILLMODE" /*= 139*/, 3424, xtD3DFILLMODE, NV2A_POLYGON_MODE_FRONT, D3DRS_FILLMODE }, @@ -1493,20 +1504,25 @@ const RenderStateInfo DxbxRenderStateInfo[] = { { "D3DRS_EDGEANTIALIAS" /*= 151*/, 3424, xtBOOL, NV2A_LINE_SMOOTH_ENABLE, D3DRS_ANTIALIASEDLINEENABLE }, // Was D3DRS_EDGEANTIALIAS. Dxbx note : No Xbox ext. (according to Direct3D8) ! { "D3DRS_MULTISAMPLEANTIALIAS" /*= 152*/, 3424, xtBOOL, NV2A_MULTISAMPLE_CONTROL, D3DRS_MULTISAMPLEANTIALIAS }, { "D3DRS_MULTISAMPLEMASK" /*= 153*/, 3424, xtDWORD, NV2A_MULTISAMPLE_CONTROL, D3DRS_MULTISAMPLEMASK }, -// { "D3DRS_MULTISAMPLETYPE" /*= 154*/, 3424, xtD3DMULTISAMPLE_TYPE, 0 }, // [-3911] \_ aliasses D3DMULTISAMPLE_TYPE - { "D3DRS_MULTISAMPLEMODE" /*= 154*/, 3425 /* really 4361 but shares slot with previous entry */, xtD3DMULTISAMPLEMODE, 0 }, // [4361+] / D3DMULTISAMPLEMODE for the backbuffer - { "D3DRS_MULTISAMPLERENDERTARGETMODE" /*= 155*/, 4242, xtD3DMULTISAMPLEMODE, NV2A_RT_FORMAT }, - { "D3DRS_SHADOWFUNC" /*= 156*/, 3424, xtD3DCMPFUNC, NV2A_TX_RCOMP }, - { "D3DRS_LINEWIDTH" /*= 157*/, 3424, xtFloat, NV2A_LINE_WIDTH }, - { "D3DRS_SAMPLEALPHA" /*= 158*/, 4627, xtD3DSAMPLEALPHA, 0 }, // TODO : Later than 3424, but earlier then 4627? - { "D3DRS_DXT1NOISEENABLE" /*= 159*/, 3424, xtBOOL, NV2A_CLEAR_DEPTH_VALUE }, - { "D3DRS_YUVENABLE" /*= 160*/, 3911, xtBOOL, NV2A_CONTROL0 }, - { "D3DRS_OCCLUSIONCULLENABLE" /*= 161*/, 3911, xtBOOL, NV2A_OCCLUDE_ZSTENCIL_EN }, - { "D3DRS_STENCILCULLENABLE" /*= 162*/, 3911, xtBOOL, NV2A_OCCLUDE_ZSTENCIL_EN }, - { "D3DRS_ROPZCMPALWAYSREAD" /*= 163*/, 3911, xtBOOL, 0 }, - { "D3DRS_ROPZREAD" /*= 164*/, 3911, xtBOOL, 0 }, - { "D3DRS_DONOTCULLUNCOMPRESSED" /*= 165*/, 3911, xtBOOL, 0 } + { "D3DRS_MULTISAMPLETYPE" /*= 154*/, 3424, xtD3DMULTISAMPLE_TYPE, 0, D3DRS_UNSUPPORTED, "aliasses D3DMULTISAMPLE_TYPE, removed from 4039 onward", 4039 }, // Verified present in 3944, removed in 4039 TODO : Might be removed in 4034? + { "D3DRS_MULTISAMPLEMODE" /*= 155*/, 4039, xtD3DMULTISAMPLEMODE, 0 }, // D3DMULTISAMPLEMODE for the backbuffer. Verified absent in 3944, present in 4039 TODO : Might be introduced in 4034? + { "D3DRS_MULTISAMPLERENDERTARGETMODE" /*= 156*/, 4034, xtD3DMULTISAMPLEMODE, NV2A_RT_FORMAT }, // Verified absent in 3944, present in 4039. Presence in 4034 is based on test-case : The Simpsons Road Rage + { "D3DRS_SHADOWFUNC" /*= 157*/, 3424, xtD3DCMPFUNC, NV2A_TX_RCOMP }, + { "D3DRS_LINEWIDTH" /*= 158*/, 3424, xtFloat, NV2A_LINE_WIDTH }, + { "D3DRS_SAMPLEALPHA" /*= 159*/, 4627, xtD3DSAMPLEALPHA, 0 }, // Verified absent in 4531, present in 4627 TODO : might be introduced in between? + { "D3DRS_DXT1NOISEENABLE" /*= 160*/, 3424, xtBOOL, NV2A_CLEAR_DEPTH_VALUE }, + { "D3DRS_YUVENABLE" /*= 161*/, 3911, xtBOOL, NV2A_CONTROL0 }, // Verified present in 3944 + { "D3DRS_OCCLUSIONCULLENABLE" /*= 162*/, 3911, xtBOOL, NV2A_OCCLUDE_ZSTENCIL_EN }, // Verified present in 3944 + { "D3DRS_STENCILCULLENABLE" /*= 163*/, 3911, xtBOOL, NV2A_OCCLUDE_ZSTENCIL_EN }, // Verified present in 3944 + { "D3DRS_ROPZCMPALWAYSREAD" /*= 164*/, 3911, xtBOOL, 0 }, // Verified present in 3944 + { "D3DRS_ROPZREAD" /*= 165*/, 3911, xtBOOL, 0 }, // Verified present in 3944 + { "D3DRS_DONOTCULLUNCOMPRESSED" /*= 166*/, 3911, xtBOOL, 0 } // Verified present in 3944 }; + +const RenderStateInfo& GetDxbxRenderStateInfo(int State) +{ + return DxbxRenderStateInfo[State]; +} /*Direct3D8 states unused : D3DRS_LINEPATTERN diff --git a/src/core/hle/D3D8/XbConvert.h b/src/core/hle/D3D8/XbConvert.h index 8ee2c3cfe..a4fec49f7 100644 --- a/src/core/hle/D3D8/XbConvert.h +++ b/src/core/hle/D3D8/XbConvert.h @@ -1810,12 +1810,14 @@ typedef struct _RenderStateInfo { TXBType T = xt_Unknown; // The Xbox data type. Defaults to xt_Unknown. XTL::NV2AMETHOD M; // The related push buffer method. Not always a 1-to-1 mapping. Needs push-buffer interpretation & conversion code. D3DRENDERSTATETYPE PC = (D3DRENDERSTATETYPE)0; // Map XBox to PC render state - char *N; // XDK notes. Defaults to ''. + char *N; // XDK notes. Defaults to ''. + WORD R; // The XDK version since which a render state was removed } RenderStateInfo; -#define D3DRS_NONE ((D3DRENDERSTATETYPE)0) +#define D3DRS_UNSUPPORTED ((D3DRENDERSTATETYPE)0) + +extern const RenderStateInfo& GetDxbxRenderStateInfo(int State); -extern const RenderStateInfo DxbxRenderStateInfo[]; #endif diff --git a/src/core/hle/D3D8/XbD3D8Types.h b/src/core/hle/D3D8/XbD3D8Types.h index cdcf69b78..9e423135b 100644 --- a/src/core/hle/D3D8/XbD3D8Types.h +++ b/src/core/hle/D3D8/XbD3D8Types.h @@ -84,8 +84,8 @@ #define IDirect3DSwapChain IDirect3DSwapChain9 #define IDirect3DQuery IDirect3DQuery9 -namespace XTL { - +namespace XTL { + // TODO : Declare these aliasses as Xbox type typedef D3DLIGHT9 X_D3DLIGHT8; typedef D3DMATERIAL9 X_D3DMATERIAL8; @@ -649,12 +649,12 @@ typedef enum _X_D3DRENDERSTATETYPE { // Dxbx note : These declarations are from XDK version 5933, the most recent and complete version. // Older versions are slightly different (some members are missing), so we use a mapping table to - // cater for the differences (see DxbxBuildRenderStateMappingTable). This enables to ignore these - // version-differences in the rest of our code (unless it matters somehow); We write via indirection : - // *EmuMappedD3DRenderState[X_D3DRENDERSTATETYPE] = Value; + // cater for the differences (see XboxRenderStateConverter::BuildRenderStateMappingTable). This enables to ignore these + // version-differences in the rest of our code (unless it matters somehow); We write like this : + // XboxRenderStates.SetXboxRenderState(X_D3DRENDERSTATETYPE, Value); // - // And we read via the same mapping (do note, that missing elements all point to the same dummy) : - // Result = *EmuMappedD3DRenderState[X_D3DRENDERSTATETYPE]; + // And we read like this (do note, that missing elements all point to the same dummy) : + // Result = XboxRenderStates.GetXboxRenderState(X_D3DRENDERSTATETYPE); // Dxbx note : The PS* render states map 1-on-1 to the X_D3DPIXELSHADERDEF record, // SetPixelShader actually pushes the definition into these render state slots. @@ -737,14 +737,14 @@ typedef enum _X_D3DRENDERSTATETYPE { X_D3DRS_STENCILMASK = 72, // BYTE mask value used in stencil test X_D3DRS_STENCILWRITEMASK = 73, // BYTE write mask applied to values written to stencil buffer X_D3DRS_BLENDOP = 74, // D3DBLENDOP setting - X_D3DRS_BLENDCOLOR = 75, // D3DCOLOR for D3DBLEND_CONSTANTCOLOR (Xbox ext.) + X_D3DRS_BLENDCOLOR = 75, // D3DCOLOR for D3DBLEND_CONSTANTCOLOR X_D3DRS_SWATHWIDTH = 76, // D3DSWATHWIDTH (Xbox ext.) X_D3DRS_POLYGONOFFSETZSLOPESCALE = 77, // float Z factor for shadow maps (Xbox ext.) X_D3DRS_POLYGONOFFSETZOFFSET = 78, // Xbox ext. X_D3DRS_POINTOFFSETENABLE = 79, // Xbox ext. X_D3DRS_WIREFRAMEOFFSETENABLE = 80, // Xbox ext. X_D3DRS_SOLIDOFFSETENABLE = 81, // Xbox ext. - X_D3DRS_DEPTHCLIPCONTROL = 82, // [4627+] Xbox ext. + X_D3DRS_DEPTHCLIPCONTROL = 82, // [4432+] Xbox ext. X_D3DRS_STIPPLEENABLE = 83, // [4627+] Xbox ext. X_D3DRS_SIMPLE_UNUSED8 = 84, // [4627+] X_D3DRS_SIMPLE_UNUSED7 = 85, // [4627+] @@ -789,8 +789,8 @@ typedef enum _X_D3DRENDERSTATETYPE { X_D3DRS_POINTSIZE_MAX = 123, X_D3DRS_PATCHEDGESTYLE = 124, // Dxbx addition X_D3DRS_PATCHSEGMENTS = 125, - X_D3DRS_SWAPFILTER = 126, // [4361+] Xbox ext. nsp. D3DTEXF_LINEAR etc. filter to use for Swap - X_D3DRS_PRESENTATIONINTERVAL = 127, // [4627+] Xbox ext. nsp. + X_D3DRS_SWAPFILTER = 126, // [4039+] Xbox ext. nsp. D3DTEXF_LINEAR etc. filter to use for Swap + X_D3DRS_PRESENTATIONINTERVAL = 127, // [4627+] Xbox ext. nsp. TODO : Use 4361? X_D3DRS_DEFERRED_UNUSED8 = 128, // [4627+] X_D3DRS_DEFERRED_UNUSED7 = 129, // [4627+] X_D3DRS_DEFERRED_UNUSED6 = 130, // [4627+] @@ -814,23 +814,24 @@ typedef enum _X_D3DRENDERSTATETYPE { X_D3DRS_CULLMODE = 147, X_D3DRS_TEXTUREFACTOR = 148, X_D3DRS_ZBIAS = 149, - X_D3DRS_LOGICOP = 150, // Xbox ext. + X_D3DRS_LOGICOP = 150, // Xbox ext. nsp. X_D3DRS_EDGEANTIALIAS = 151, // Dxbx note : No Xbox ext. (according to Direct3D8) ! X_D3DRS_MULTISAMPLEANTIALIAS = 152, X_D3DRS_MULTISAMPLEMASK = 153, - X_D3DRS_MULTISAMPLETYPE = 154, // [-3911] Xbox ext. \_ aliasses D3DMULTISAMPLE_TYPE - X_D3DRS_MULTISAMPLEMODE = 154, // [4361+] Xbox ext. / D3DMULTISAMPLEMODE for the backbuffer - X_D3DRS_MULTISAMPLERENDERTARGETMODE = 155, // [4361+] Xbox ext. - X_D3DRS_SHADOWFUNC = 156, // D3DCMPFUNC (Xbox extension) - X_D3DRS_LINEWIDTH = 157, // Xbox ext. - X_D3DRS_SAMPLEALPHA = 158, // Xbox ext. - X_D3DRS_DXT1NOISEENABLE = 159, // Xbox ext. - X_D3DRS_YUVENABLE = 160, // [3911+] Xbox ext. - X_D3DRS_OCCLUSIONCULLENABLE = 161, // [3911+] Xbox ext. - X_D3DRS_STENCILCULLENABLE = 162, // [3911+] Xbox ext. - X_D3DRS_ROPZCMPALWAYSREAD = 163, // [3911+] Xbox ext. - X_D3DRS_ROPZREAD = 164, // [3911+] Xbox ext. - X_D3DRS_DONOTCULLUNCOMPRESSED = 165, // [3911+] Xbox ext. + X_D3DRS_MULTISAMPLETYPE = 154, // [-4039] Xbox ext. + // Note : X_D3DRS_MULTISAMPLETYPE seems the only one that got removed, but it does need a slot, so the rest is increased by 1 compared to 5933. + X_D3DRS_MULTISAMPLEMODE = 155, // [4361+] Xbox ext. // D3DMULTISAMPLEMODE for the backbuffer + X_D3DRS_MULTISAMPLERENDERTARGETMODE = 156, // [4039+] Xbox ext. + X_D3DRS_SHADOWFUNC = 157, // D3DCMPFUNC (Xbox extension) + X_D3DRS_LINEWIDTH = 158, // Xbox ext. + X_D3DRS_SAMPLEALPHA = 159, // Xbox ext. + X_D3DRS_DXT1NOISEENABLE = 160, // Xbox ext. + X_D3DRS_YUVENABLE = 161, // [3911+] Xbox ext. + X_D3DRS_OCCLUSIONCULLENABLE = 162, // [3911+] Xbox ext. + X_D3DRS_STENCILCULLENABLE = 163, // [3911+] Xbox ext. + X_D3DRS_ROPZCMPALWAYSREAD = 164, // [3911+] Xbox ext. + X_D3DRS_ROPZREAD = 165, // [3911+] Xbox ext. + X_D3DRS_DONOTCULLUNCOMPRESSED = 166, // [3911+] Xbox ext. // End of "complex" render states. X_D3DRS_UNK = 0x7fffffff // deferred render state "unknown" flag } X_D3DRENDERSTATETYPE; @@ -1211,6 +1212,6 @@ typedef DWORD NV2AMETHOD; // Host vertex shader counts #define CXBX_D3DVS_CONSTREG_VERTEXDATA4F_BASE X_D3DVS_CONSTREG_COUNT -} // end of namespace XTL +} // end of namespace XTL #endif diff --git a/src/core/hle/D3D8/XbPixelShader.cpp b/src/core/hle/D3D8/XbPixelShader.cpp index 9d1e7ea7a..547973acb 100644 --- a/src/core/hle/D3D8/XbPixelShader.cpp +++ b/src/core/hle/D3D8/XbPixelShader.cpp @@ -1210,7 +1210,7 @@ float PSH_IMD_ARGUMENT::GetConstValue() if (HasModifier(ARGMOD_NEGATE)) Result = -Result; // y = x-0.5 -> 0..1 > -0.5..0.5 - if (HasModifier (ARGMOD_BIAS)) Result = Result-0.5f; + if (HasModifier(ARGMOD_BIAS)) Result = Result-0.5f; // y = x*2 -> 0..1 > 0..2 if (HasModifier(ARGMOD_SCALE_X2)) Result = Result*2.0f; @@ -1650,7 +1650,7 @@ bool PSH_IMD_ARGUMENT::Decode(const DWORD Value, DWORD aMask, TArgumentType Argu Modifiers = (1 << ARGMOD_BIAS); break; // case PS_INPUTMAPPING_HALFBIAS_NEGATE: -// Modifiers = ARGMOD_IDENTITY; ??? +// Modifiers = (1 << ARGMOD_IDENTITY); ??? // break; case PS_INPUTMAPPING_SIGNED_IDENTITY: Modifiers = (1 << ARGMOD_IDENTITY); @@ -5982,11 +5982,9 @@ VOID DxbxUpdateActivePixelShader() // NOPATCH HRESULT Result = D3D_OK; - // TODO: Is this even right? The first RenderState is PSAlpha, + // The first RenderState is PSAlpha, // The pixel shader is stored in pDevice->m_pPixelShader // For now, we still patch SetPixelShader and read from there... - //DWORD *XTL_D3D__RenderState = EmuMappedD3DRenderState[0]; - //pPSDef = (XTL::X_D3DPIXELSHADERDEF*)(XTL_D3D__RenderState); // Use the pixel shader stored in D3D__RenderState rather than the set handle // This allows changes made via SetRenderState to actually take effect! @@ -5994,6 +5992,7 @@ VOID DxbxUpdateActivePixelShader() // NOPATCH // All other fields are the same. // We cast D3D__RenderState to a pPSDef for these fields, but // manually read from D3D__RenderState[X_D3DRS_PSTEXTUREMODES) for that one field. + // See D3DDevice_SetPixelShaderCommon which implements this pPSDef = g_pXbox_PixelShader != nullptr ? (XTL::X_D3DPIXELSHADERDEF*)(XboxRenderStates.GetPixelShaderRenderStatePointer()) : nullptr; @@ -6066,25 +6065,16 @@ VOID DxbxUpdateActivePixelShader() // NOPATCH if (RecompiledPixelShader->ConstInUse[i]) { // Read the color from the corresponding render state slot : - // TODO: These should read from EmuMappedD3DRenderState, but it doesn't exist yet - // The required code needs o be ported from Wip_LessVertexPatching or Dxbx switch (i) { case PSH_XBOX_CONSTANT_FOG: - //dwColor = *EmuMappedD3DRenderState[XTL::X_D3DRS_FOGCOLOR] | 0xFF000000; // Note : FOG.RGB is correct like this, but FOG.a should be coming // from the vertex shader (oFog) - however, D3D8 does not forward this... - g_pD3DDevice->GetRenderState(D3DRS_FOGCOLOR, &dwColor); - fColor.a = ((dwColor >> 24) & 0xFF) / 255.0f; - fColor.r = ((dwColor >> 16) & 0xFF) / 255.0f; - fColor.g = ((dwColor >> 8) & 0xFF) / 255.0f; - fColor.b = (dwColor & 0xFF) / 255.0f; + fColor = dwColor = XboxRenderStates.GetXboxRenderState(XTL::X_D3DRS_FOGCOLOR); break; case PSH_XBOX_CONSTANT_FC0: - //dwColor = *EmuMappedD3DRenderState[XTL::X_D3DRS_PSFINALCOMBINERCONSTANT0]; fColor = dwColor = XboxRenderStates.GetXboxRenderState(XTL::X_D3DRS_PSFINALCOMBINERCONSTANT0); break; case PSH_XBOX_CONSTANT_FC1: - //dwColor = *EmuMappedD3DRenderState[XTL::X_D3DRS_PSFINALCOMBINERCONSTANT1]; fColor = dwColor = XboxRenderStates.GetXboxRenderState(XTL::X_D3DRS_PSFINALCOMBINERCONSTANT1); break; case PSH_XBOX_CONSTANT_MUL0: @@ -6119,7 +6109,6 @@ VOID DxbxUpdateActivePixelShader() // NOPATCH break; } default: - //dwColor = *EmuMappedD3DRenderState[XTL::X_D3DRS_PSCONSTANT0_0 + i]; fColor = dwColor = XboxRenderStates.GetXboxRenderState(XTL::X_D3DRS_PSCONSTANT0_0 + i); break; } diff --git a/src/core/hle/D3D8/XbVertexBuffer.cpp b/src/core/hle/D3D8/XbVertexBuffer.cpp index 36ea192d6..d70fdb87f 100644 --- a/src/core/hle/D3D8/XbVertexBuffer.cpp +++ b/src/core/hle/D3D8/XbVertexBuffer.cpp @@ -55,8 +55,10 @@ FLOAT *g_InlineVertexBuffer_pData = nullptr; UINT g_InlineVertexBuffer_DataSize = 0; extern DWORD g_dwPrimPerFrame = 0; -extern XTL::X_D3DVertexBuffer*g_D3DStreams[X_VSH_MAX_STREAMS]; -extern UINT g_D3DStreamStrides[X_VSH_MAX_STREAMS]; + +// Copy of active Xbox D3D Vertex Streams (and strides), set by [D3DDevice|CxbxImpl]_SetStreamSource* +XTL::X_STREAMINPUT g_Xbox_SetStreamSource[X_VSH_MAX_STREAMS] = { 0 }; // Note : .Offset member is never set (so always 0) + extern XTL::X_D3DSurface* g_pXbox_RenderTarget; extern XTL::X_D3DSurface* g_pXbox_BackBufferSurface; void *GetDataFromXboxResource(XTL::X_D3DResource *pXboxResource); @@ -118,7 +120,7 @@ int CountActiveD3DStreams() { int lastStreamIndex = 0; for (int i = 0; i < X_VSH_MAX_STREAMS; i++) { - if (g_D3DStreams[i] != xbnullptr) { + if (g_Xbox_SetStreamSource[i].VertexBuffer != xbnullptr) { lastStreamIndex = i + 1; } } @@ -292,7 +294,7 @@ void CxbxVertexBufferConverter::ConvertStream uiHostVertexStride = (bNeedVertexPatching) ? pVertexShaderStreamInfo->HostVertexStride : uiXboxVertexStride; dwHostVertexDataSize = uiVertexCount * uiHostVertexStride; } else { - XTL::X_D3DVertexBuffer *pXboxVertexBuffer = g_D3DStreams[uiStream]; + XTL::X_D3DVertexBuffer *pXboxVertexBuffer = g_Xbox_SetStreamSource[uiStream].VertexBuffer; pXboxVertexData = (uint8_t*)GetDataFromXboxResource(pXboxVertexBuffer); if (pXboxVertexData == xbnullptr) { HRESULT hRet = g_pD3DDevice->SetStreamSource( @@ -308,7 +310,7 @@ void CxbxVertexBufferConverter::ConvertStream return; } - uiXboxVertexStride = g_D3DStreamStrides[uiStream]; + uiXboxVertexStride = g_Xbox_SetStreamSource[uiStream].Stride; // Set a new (exact) vertex count uiVertexCount = pDrawContext->VerticesInBuffer; // Dxbx note : Don't overwrite pDrawContext.dwVertexCount with uiVertexCount, because an indexed draw @@ -987,3 +989,15 @@ VOID EmuFlushIVB() } g_InlineVertexBuffer_TableOffset = 0; // Might not be needed (also cleared in D3DDevice_Begin) } + +void CxbxImpl_SetStreamSource(UINT StreamNumber, XTL::X_D3DVertexBuffer* pStreamData, UINT Stride) +{ + if (pStreamData != xbnullptr && Stride == 0) { + LOG_TEST_CASE("CxbxImpl_SetStreamSource : Stream assigned, and stride set to 0 (might be okay)"); + } + + assert(StreamNumber < X_VSH_MAX_STREAMS); + + g_Xbox_SetStreamSource[StreamNumber].VertexBuffer = pStreamData; + g_Xbox_SetStreamSource[StreamNumber].Stride = Stride; +} diff --git a/src/core/hle/D3D8/XbVertexShader.cpp b/src/core/hle/D3D8/XbVertexShader.cpp index 47adfe5c9..436a8d0b3 100644 --- a/src/core/hle/D3D8/XbVertexShader.cpp +++ b/src/core/hle/D3D8/XbVertexShader.cpp @@ -1,2831 +1,2861 @@ -// This is an open source non-commercial project. Dear PVS-Studio, please check it. -// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com -// ****************************************************************** -// * -// * This file is part of the Cxbx project. -// * -// * Cxbx and Cxbe are free software; you can redistribute them -// * and/or modify them under the terms of the GNU General Public -// * License as published by the Free Software Foundation; either -// * version 2 of the license, or (at your option) any later version. -// * -// * This program is distributed in the hope that it will be useful, -// * but WITHOUT ANY WARRANTY; without even the implied warranty of -// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// * GNU General Public License for more details. -// * -// * You should have recieved a copy of the GNU General Public License -// * along with this program; see the file COPYING. -// * If not, write to the Free Software Foundation, Inc., -// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. -// * -// * (c) 2002-2004 Aaron Robinson -// * Kingofc -// * -// * All rights reserved -// * -// ****************************************************************** -#define LOG_PREFIX CXBXR_MODULE::VTXSH - -#define _DEBUG_TRACK_VS - -#include "core\kernel\init\CxbxKrnl.h" -#include "core\kernel\support\Emu.h" -#include "core\hle\D3D8\Direct3D9\Direct3D9.h" // For g_Xbox_VertexShader_Handle -#include "core\hle\D3D8\XbVertexShader.h" - -#include "XbD3D8Types.h" // For X_D3DVSDE_* -#include -#include -#include -#include -#include - -//#define CXBX_USE_VS30 // Separate the port to Vertex Shader model 3.0 from the port to Direct3D9 -#ifdef CXBX_USE_VS30 - #define VSH_MAX_INSTRUCTION_COUNT VSH_VS30_MAX_INSTRUCTION_COUNT // == 512 -#else - #define VSH_MAX_INSTRUCTION_COUNT VSH_VS2X_MAX_INSTRUCTION_COUNT // == 256 -#endif - -// Internal Vertex Shader version (mustn't conflict with any VERSION_XVS*) -#define VERSION_CXBX 0x7863 // 'cx' Cxbx vertex shader, not an official value, used in VshConvertShader() and VshWriteShader() - -#define DbgVshPrintf \ - LOG_CHECK_ENABLED(LOG_LEVEL::DEBUG) \ - if(g_bPrintfOn) printf - -// **************************************************************************** -// * Vertex shader function recompiler -// **************************************************************************** - -typedef enum _VSH_SWIZZLE -{ - SWIZZLE_X = 0, - SWIZZLE_Y, - SWIZZLE_Z, - SWIZZLE_W -} -VSH_SWIZZLE; - -typedef struct DxbxSwizzles { VSH_SWIZZLE s[4]; } DxbxSwizzles; - -typedef DWORD DxbxMask, -*PDxbxMask; - -#define MASK_X 0x001 -#define MASK_Y 0x002 -#define MASK_Z 0x004 -#define MASK_W 0x008 -#define MASK_XYZ MASK_X | MASK_Y | MASK_Z -#define MASK_XYZW MASK_X | MASK_Y | MASK_Z | MASK_W - -// Local types -typedef enum _VSH_FIELD_NAME -{ - FLD_ILU = 0, - FLD_MAC, - FLD_CONST, - FLD_V, - // Input A - FLD_A_NEG, - FLD_A_SWZ_X, - FLD_A_SWZ_Y, - FLD_A_SWZ_Z, - FLD_A_SWZ_W, - FLD_A_R, - FLD_A_MUX, - // Input B - FLD_B_NEG, - FLD_B_SWZ_X, - FLD_B_SWZ_Y, - FLD_B_SWZ_Z, - FLD_B_SWZ_W, - FLD_B_R, - FLD_B_MUX, - // Input C - FLD_C_NEG, - FLD_C_SWZ_X, - FLD_C_SWZ_Y, - FLD_C_SWZ_Z, - FLD_C_SWZ_W, - FLD_C_R_HIGH, - FLD_C_R_LOW, - FLD_C_MUX, - // Output - FLD_OUT_MAC_MASK_X, - FLD_OUT_MAC_MASK_Y, - FLD_OUT_MAC_MASK_Z, - FLD_OUT_MAC_MASK_W, - FLD_OUT_R, - FLD_OUT_ILU_MASK_X, - FLD_OUT_ILU_MASK_Y, - FLD_OUT_ILU_MASK_Z, - FLD_OUT_ILU_MASK_W, - FLD_OUT_O_MASK_X, - FLD_OUT_O_MASK_Y, - FLD_OUT_O_MASK_Z, - FLD_OUT_O_MASK_W, - FLD_OUT_ORB, - FLD_OUT_ADDRESS, - FLD_OUT_MUX, - // Relative addressing - FLD_A0X, - // Final instruction - FLD_FINAL -} -VSH_FIELD_NAME; - -typedef enum _VSH_OREG_NAME -{ - OREG_OPOS, // 0 - OREG_UNUSED1, // 1 - OREG_UNUSED2, // 2 - OREG_OD0, // 3 - OREG_OD1, // 4 - OREG_OFOG, // 5 - OREG_OPTS, // 6 - OREG_OB0, // 7 - OREG_OB1, // 8 - OREG_OT0, // 9 - OREG_OT1, // 10 - OREG_OT2, // 11 - OREG_OT3, // 12 - OREG_UNUSED3, // 13 - OREG_UNUSED4, // 14 - OREG_A0X // 15 - all values of the 4 bits are used -} -VSH_OREG_NAME; - -typedef enum _VSH_OUTPUT_TYPE -{ - OUTPUT_C = 0, - OUTPUT_O -} -VSH_OUTPUT_TYPE; - -typedef enum _VSH_ARGUMENT_TYPE -{ - PARAM_UNKNOWN = 0, - PARAM_R, // Temporary registers - PARAM_V, // Vertex registers - PARAM_C, // Constant registers, set by SetVertexShaderConstant - PARAM_O -} -VSH_ARGUMENT_TYPE; - -typedef VSH_ARGUMENT_TYPE VSH_PARAMETER_TYPE; // Alias, to indicate difference between a parameter and a generic argument - -typedef enum _VSH_OUTPUT_MUX -{ - OMUX_MAC = 0, - OMUX_ILU -} -VSH_OUTPUT_MUX; - -typedef enum _VSH_IMD_OUTPUT_TYPE -{ - IMD_OUTPUT_C, - IMD_OUTPUT_R, - IMD_OUTPUT_O, - IMD_OUTPUT_A0X -} -VSH_IMD_OUTPUT_TYPE; - -// Dxbx note : ILU stands for 'Inverse Logic Unit' opcodes -typedef enum _VSH_ILU -{ - ILU_NOP = 0, - ILU_MOV, - ILU_RCP, - ILU_RCC, - ILU_RSQ, - ILU_EXP, - ILU_LOG, - ILU_LIT // = 7 - all values of the 3 bits are used -} -VSH_ILU; - -// Dxbx note : MAC stands for 'Multiply And Accumulate' opcodes -typedef enum _VSH_MAC -{ - MAC_NOP = 0, - MAC_MOV, - MAC_MUL, - MAC_ADD, - MAC_MAD, - MAC_DP3, - MAC_DPH, - MAC_DP4, - MAC_DST, - MAC_MIN, - MAC_MAX, - MAC_SLT, - MAC_SGE, - MAC_ARL - // ??? 14 - // ??? 15 - 2 values of the 4 bits are undefined -} -VSH_MAC; - -typedef struct _VSH_OPCODE_PARAMS -{ - // Dxbx Note : Since we split up g_OpCodeParams into g_OpCodeParams_ILU and g_OpCodeParams_MAC - // the following two members aren't needed anymore : - // VSH_ILU ILU; - // VSH_MAC MAC; - boolean A; - boolean B; - boolean C; -} -VSH_OPCODE_PARAMS; - -typedef struct _VSH_PARAMETER -{ - VSH_PARAMETER_TYPE ParameterType; // Parameter type, R, V or C - boolean Neg; // TRUE if negated, FALSE if not - VSH_SWIZZLE Swizzle[4]; // The four swizzles - int16_t Address; // Register address -} -VSH_PARAMETER; - -typedef struct _VSH_OUTPUT -{ - // Output register - VSH_OUTPUT_MUX OutputMux; // MAC or ILU used as output - VSH_OUTPUT_TYPE OutputType; // C or O - boolean OutputMask[4]; - int16_t OutputAddress; - // MAC output R register - boolean MACRMask[4]; - boolean MACRAddress; - // ILU output R register - boolean ILURMask[4]; - boolean ILURAddress; -} -VSH_OUTPUT; - -// The raw, parsed shader instruction (can be many combined [paired] instructions) -typedef struct _VSH_SHADER_INSTRUCTION -{ - VSH_ILU ILU; - VSH_MAC MAC; - VSH_OUTPUT Output; - VSH_PARAMETER A; - VSH_PARAMETER B; - VSH_PARAMETER C; - boolean a0x; -} -VSH_SHADER_INSTRUCTION; - -typedef enum _VSH_IMD_INSTRUCTION_TYPE -{ - IMD_MAC, - IMD_ILU -} -VSH_IMD_INSTRUCTION_TYPE; - -typedef struct _VSH_IMD_OUTPUT -{ - VSH_IMD_OUTPUT_TYPE Type; - boolean Mask[4]; - int16_t Address; -} -VSH_IMD_OUTPUT; - -typedef struct _VSH_IMD_PARAMETER -{ - boolean Active; - VSH_PARAMETER Parameter; - // There is only a single address register in Microsoft DirectX 8.0. - // The address register, designated as a0.x, may be used as signed - // integer offset in relative addressing into the constant register file. - // c[a0.x + n] - boolean IndexesWithA0_X; -} -VSH_IMD_PARAMETER; - -typedef struct _VSH_INTERMEDIATE_FORMAT -{ - - boolean IsCombined; - VSH_IMD_INSTRUCTION_TYPE InstructionType; - VSH_MAC MAC; - VSH_ILU ILU; - VSH_IMD_OUTPUT Output; - VSH_IMD_PARAMETER Parameters[3]; -} -VSH_INTERMEDIATE_FORMAT; - -// Used for xvu spec definition -typedef struct _VSH_FIELDMAPPING -{ - VSH_FIELD_NAME FieldName; - uint8_t SubToken; - uint8_t StartBit; - uint8_t BitLength; -} -VSH_FIELDMAPPING; - -typedef struct _VSH_XBOX_SHADER -{ - XTL::X_VSH_SHADER_HEADER ShaderHeader; - uint16_t IntermediateCount; - VSH_INTERMEDIATE_FORMAT Intermediate[VSH_MAX_INTERMEDIATE_COUNT]; -} -VSH_XBOX_SHADER; - -// Local constants -static const VSH_FIELDMAPPING g_FieldMapping[] = -{ - // Field Name DWORD BitPos BitSize - { FLD_ILU, 1, 25, 3 }, - { FLD_MAC, 1, 21, 4 }, - { FLD_CONST, 1, 13, 8 }, - { FLD_V, 1, 9, 4 }, - // Input A - { FLD_A_NEG, 1, 8, 1 }, - { FLD_A_SWZ_X, 1, 6, 2 }, - { FLD_A_SWZ_Y, 1, 4, 2 }, - { FLD_A_SWZ_Z, 1, 2, 2 }, - { FLD_A_SWZ_W, 1, 0, 2 }, - { FLD_A_R, 2, 28, 4 }, - { FLD_A_MUX, 2, 26, 2 }, - // Input B - { FLD_B_NEG, 2, 25, 1 }, - { FLD_B_SWZ_X, 2, 23, 2 }, - { FLD_B_SWZ_Y, 2, 21, 2 }, - { FLD_B_SWZ_Z, 2, 19, 2 }, - { FLD_B_SWZ_W, 2, 17, 2 }, - { FLD_B_R, 2, 13, 4 }, - { FLD_B_MUX, 2, 11, 2 }, - // Input C - { FLD_C_NEG, 2, 10, 1 }, - { FLD_C_SWZ_X, 2, 8, 2 }, - { FLD_C_SWZ_Y, 2, 6, 2 }, - { FLD_C_SWZ_Z, 2, 4, 2 }, - { FLD_C_SWZ_W, 2, 2, 2 }, - { FLD_C_R_HIGH, 2, 0, 2 }, - { FLD_C_R_LOW, 3, 30, 2 }, - { FLD_C_MUX, 3, 28, 2 }, - // Output - { FLD_OUT_MAC_MASK_X, 3, 27, 1 }, - { FLD_OUT_MAC_MASK_Y, 3, 26, 1 }, - { FLD_OUT_MAC_MASK_Z, 3, 25, 1 }, - { FLD_OUT_MAC_MASK_W, 3, 24, 1 }, - { FLD_OUT_R, 3, 20, 4 }, - { FLD_OUT_ILU_MASK_X, 3, 19, 1 }, - { FLD_OUT_ILU_MASK_Y, 3, 18, 1 }, - { FLD_OUT_ILU_MASK_Z, 3, 17, 1 }, - { FLD_OUT_ILU_MASK_W, 3, 16, 1 }, - { FLD_OUT_O_MASK_X, 3, 15, 1 }, - { FLD_OUT_O_MASK_Y, 3, 14, 1 }, - { FLD_OUT_O_MASK_Z, 3, 13, 1 }, - { FLD_OUT_O_MASK_W, 3, 12, 1 }, - { FLD_OUT_ORB, 3, 11, 1 }, - { FLD_OUT_ADDRESS, 3, 3, 8 }, - { FLD_OUT_MUX, 3, 2, 1 }, - // Relative addressing - { FLD_A0X, 3, 1, 1 }, - // Final instruction - { FLD_FINAL, 3, 0, 1 } -}; - -static const VSH_OPCODE_PARAMS g_OpCodeParams_ILU[] = -{ - // ILU OP MAC OP ParamA ParamB ParamC - { /*ILU_NOP, MAC_NOP, */ FALSE, FALSE, FALSE }, // Dxbx note : Unused - { /*ILU_MOV, MAC_NOP, */ FALSE, FALSE, TRUE }, - { /*ILU_RCP, MAC_NOP, */ FALSE, FALSE, TRUE }, - { /*ILU_RCC, MAC_NOP, */ FALSE, FALSE, TRUE }, - { /*ILU_RSQ, MAC_NOP, */ FALSE, FALSE, TRUE }, - { /*ILU_EXP, MAC_NOP, */ FALSE, FALSE, TRUE }, - { /*ILU_LOG, MAC_NOP, */ FALSE, FALSE, TRUE }, - { /*ILU_LIT, MAC_NOP, */ FALSE, FALSE, TRUE }, -}; - -static const VSH_OPCODE_PARAMS g_OpCodeParams_MAC[] = -{ - // ILU OP MAC OP ParamA ParamB ParamC - { /*ILU_NOP, MAC_NOP, */ FALSE, FALSE, FALSE }, // Dxbx note : Unused - { /*ILU_NOP, MAC_MOV, */ TRUE, FALSE, FALSE }, - { /*ILU_NOP, MAC_MUL, */ TRUE, TRUE, FALSE }, - { /*ILU_NOP, MAC_ADD, */ TRUE, FALSE, TRUE }, - { /*ILU_NOP, MAC_MAD, */ TRUE, TRUE, TRUE }, - { /*ILU_NOP, MAC_DP3, */ TRUE, TRUE, FALSE }, - { /*ILU_NOP, MAC_DPH, */ TRUE, TRUE, FALSE }, - { /*ILU_NOP, MAC_DP4, */ TRUE, TRUE, FALSE }, - { /*ILU_NOP, MAC_DST, */ TRUE, TRUE, FALSE }, - { /*ILU_NOP, MAC_MIN, */ TRUE, TRUE, FALSE }, - { /*ILU_NOP, MAC_MAX, */ TRUE, TRUE, FALSE }, - { /*ILU_NOP, MAC_SLT, */ TRUE, TRUE, FALSE }, - { /*ILU_NOP, MAC_SGE, */ TRUE, TRUE, FALSE }, - { /*ILU_NOP, MAC_ARL, */ TRUE, FALSE, FALSE } -}; - -static const char* MAC_OpCode[] = -{ - "nop", - "mov", - "mul", - "add", - "mad", - "dp3", - "dph", - "dp4", - "dst", - "min", - "max", - "slt", - "sge", - "mova", // really "arl" Dxbx note : Alias for 'mov a0.x' - "???", - "???" -}; - -static const char* ILU_OpCode[] = -{ - "nop", - "mov", - "rcp", - "rcc", - "rsq", - "expp", // The Xbox EXPP instruction behaves like vs_1_1 - "log", - "lit" -}; - -static const char* OReg_Name[] = -{ - "oPos", - "???", - "???", - "oD0", - "oD1", - "oFog", - "oPts", - "oB0", - "oB1", - "oT0", - "oT1", - "oT2", - "oT3", - "???", - "???", - "a0.x" -}; - -std::array RegVIsPresentInDeclaration; -std::array RegVIsUsedByShader; - -/* TODO : map non-FVF Xbox vertex shader handle to CxbxVertexShader (a struct containing a host Xbox vertex shader handle and the original members) -std::unordered_map g_CxbxVertexShaders; - -void CxbxUpdateVertexShader(DWORD XboxVertexShaderHandle) -{ - CxbxVertexShader &VertexShader = g_CxbxVertexShaders[XboxVertexShaderHandle]; -}*/ - -static inline int IsInUse(const boolean *pMask) -{ - return (pMask[0] || pMask[1] || pMask[2] || pMask[3]); -} - -static inline boolean HasMACR(VSH_SHADER_INSTRUCTION *pInstruction) -{ - return IsInUse(pInstruction->Output.MACRMask) && pInstruction->MAC != MAC_NOP; -} - -static inline boolean HasMACO(VSH_SHADER_INSTRUCTION *pInstruction) -{ - return IsInUse(pInstruction->Output.OutputMask) && - pInstruction->Output.OutputMux == OMUX_MAC && - pInstruction->MAC != MAC_NOP; -} - -static inline boolean HasMACARL(VSH_SHADER_INSTRUCTION *pInstruction) -{ - return /*!IsInUse(pInstruction->Output.OutputMask) && - pInstruction->Output.OutputMux == OMUX_MAC &&*/ - pInstruction->MAC == MAC_ARL; -} - -static inline boolean HasILUR(VSH_SHADER_INSTRUCTION *pInstruction) -{ - return IsInUse(pInstruction->Output.ILURMask) && pInstruction->ILU != ILU_NOP; -} - -static inline boolean HasILUO(VSH_SHADER_INSTRUCTION *pInstruction) -{ - return IsInUse(pInstruction->Output.OutputMask) && - pInstruction->Output.OutputMux == OMUX_ILU && - pInstruction->ILU != ILU_NOP; -} - -// Retrieves a number of bits in the instruction token -static inline int VshGetFromToken(uint32_t *pShaderToken, - uint8_t SubToken, - uint8_t StartBit, - uint8_t BitLength) -{ - return (pShaderToken[SubToken] >> StartBit) & ~(0xFFFFFFFF << BitLength); -} - -// Converts the C register address to disassembly format -static inline int16_t ConvertCRegister(const int16_t CReg) -{ - return ((((CReg >> 5) & 7) - 3) * 32) + (CReg & 31); -} - -uint8_t VshGetField(uint32_t *pShaderToken, - VSH_FIELD_NAME FieldName) -{ - return (uint8_t)(VshGetFromToken(pShaderToken, - g_FieldMapping[FieldName].SubToken, - g_FieldMapping[FieldName].StartBit, - g_FieldMapping[FieldName].BitLength)); -} - -static VSH_OPCODE_PARAMS* VshGetOpCodeParams(VSH_ILU ILU, - VSH_MAC MAC) -{ - if (ILU >= ILU_MOV && ILU <= ILU_LIT) - return (VSH_OPCODE_PARAMS*)&g_OpCodeParams_ILU[ILU]; - else - if (MAC >= MAC_MOV && MAC <= MAC_ARL) - return (VSH_OPCODE_PARAMS*)&g_OpCodeParams_MAC[MAC]; - else - return nullptr; -} - -static void VshParseInstruction(uint32_t *pShaderToken, - VSH_SHADER_INSTRUCTION *pInstruction) -{ - // First get the instruction(s). - pInstruction->ILU = (VSH_ILU)VshGetField(pShaderToken, FLD_ILU); - pInstruction->MAC = (VSH_MAC)VshGetField(pShaderToken, FLD_MAC); - - // Get parameter A - pInstruction->A.ParameterType = (VSH_PARAMETER_TYPE)VshGetField(pShaderToken, FLD_A_MUX); - switch(pInstruction->A.ParameterType) - { - case PARAM_R: - pInstruction->A.Address = VshGetField(pShaderToken, FLD_A_R); - break; - case PARAM_V: - pInstruction->A.Address = VshGetField(pShaderToken, FLD_V); - break; - case PARAM_C: - pInstruction->A.Address = ConvertCRegister(VshGetField(pShaderToken, FLD_CONST)); - break; - default: - EmuLog(LOG_LEVEL::WARNING, "Invalid instruction, parameter A type unknown %d", pInstruction->A.ParameterType); - return; - } - pInstruction->A.Neg = VshGetField(pShaderToken, FLD_A_NEG); - pInstruction->A.Swizzle[0] = (VSH_SWIZZLE)VshGetField(pShaderToken, FLD_A_SWZ_X); - pInstruction->A.Swizzle[1] = (VSH_SWIZZLE)VshGetField(pShaderToken, FLD_A_SWZ_Y); - pInstruction->A.Swizzle[2] = (VSH_SWIZZLE)VshGetField(pShaderToken, FLD_A_SWZ_Z); - pInstruction->A.Swizzle[3] = (VSH_SWIZZLE)VshGetField(pShaderToken, FLD_A_SWZ_W); - // Get parameter B - pInstruction->B.ParameterType = (VSH_PARAMETER_TYPE)VshGetField(pShaderToken, FLD_B_MUX); - switch(pInstruction->B.ParameterType) - { - case PARAM_R: - pInstruction->B.Address = VshGetField(pShaderToken, FLD_B_R); - break; - case PARAM_V: - pInstruction->B.Address = VshGetField(pShaderToken, FLD_V); - break; - case PARAM_C: - pInstruction->B.Address = ConvertCRegister(VshGetField(pShaderToken, FLD_CONST)); - break; - default: - DbgVshPrintf("Invalid instruction, parameter B type unknown %d\n", pInstruction->B.ParameterType); - return; - } - pInstruction->B.Neg = VshGetField(pShaderToken, FLD_B_NEG); - pInstruction->B.Swizzle[0] = (VSH_SWIZZLE)VshGetField(pShaderToken, FLD_B_SWZ_X); - pInstruction->B.Swizzle[1] = (VSH_SWIZZLE)VshGetField(pShaderToken, FLD_B_SWZ_Y); - pInstruction->B.Swizzle[2] = (VSH_SWIZZLE)VshGetField(pShaderToken, FLD_B_SWZ_Z); - pInstruction->B.Swizzle[3] = (VSH_SWIZZLE)VshGetField(pShaderToken, FLD_B_SWZ_W); - // Get parameter C - pInstruction->C.ParameterType = (VSH_PARAMETER_TYPE)VshGetField(pShaderToken, FLD_C_MUX); - switch(pInstruction->C.ParameterType) - { - case PARAM_R: - pInstruction->C.Address = VshGetField(pShaderToken, FLD_C_R_HIGH) << 2 | - VshGetField(pShaderToken, FLD_C_R_LOW); - break; - case PARAM_V: - pInstruction->C.Address = VshGetField(pShaderToken, FLD_V); - break; - case PARAM_C: - pInstruction->C.Address = ConvertCRegister(VshGetField(pShaderToken, FLD_CONST)); - break; - default: - DbgVshPrintf("Invalid instruction, parameter C type unknown %d\n", pInstruction->C.ParameterType); - return; - } - pInstruction->C.Neg = VshGetField(pShaderToken, FLD_C_NEG); - pInstruction->C.Swizzle[0] = (VSH_SWIZZLE)VshGetField(pShaderToken, FLD_C_SWZ_X); - pInstruction->C.Swizzle[1] = (VSH_SWIZZLE)VshGetField(pShaderToken, FLD_C_SWZ_Y); - pInstruction->C.Swizzle[2] = (VSH_SWIZZLE)VshGetField(pShaderToken, FLD_C_SWZ_Z); - pInstruction->C.Swizzle[3] = (VSH_SWIZZLE)VshGetField(pShaderToken, FLD_C_SWZ_W); - // Get output - // Output register - pInstruction->Output.OutputType = (VSH_OUTPUT_TYPE)VshGetField(pShaderToken, FLD_OUT_ORB); - switch(pInstruction->Output.OutputType) - { - case OUTPUT_C: - pInstruction->Output.OutputAddress = ConvertCRegister(VshGetField(pShaderToken, FLD_OUT_ADDRESS)); - break; - case OUTPUT_O: - pInstruction->Output.OutputAddress = VshGetField(pShaderToken, FLD_OUT_ADDRESS) & 0xF; - break; - } - pInstruction->Output.OutputMux = (VSH_OUTPUT_MUX)VshGetField(pShaderToken, FLD_OUT_MUX); - pInstruction->Output.OutputMask[0] = VshGetField(pShaderToken, FLD_OUT_O_MASK_X); - pInstruction->Output.OutputMask[1] = VshGetField(pShaderToken, FLD_OUT_O_MASK_Y); - pInstruction->Output.OutputMask[2] = VshGetField(pShaderToken, FLD_OUT_O_MASK_Z); - pInstruction->Output.OutputMask[3] = VshGetField(pShaderToken, FLD_OUT_O_MASK_W); - // MAC output - pInstruction->Output.MACRMask[0] = VshGetField(pShaderToken, FLD_OUT_MAC_MASK_X); - pInstruction->Output.MACRMask[1] = VshGetField(pShaderToken, FLD_OUT_MAC_MASK_Y); - pInstruction->Output.MACRMask[2] = VshGetField(pShaderToken, FLD_OUT_MAC_MASK_Z); - pInstruction->Output.MACRMask[3] = VshGetField(pShaderToken, FLD_OUT_MAC_MASK_W); - pInstruction->Output.MACRAddress = VshGetField(pShaderToken, FLD_OUT_R); - // ILU output - pInstruction->Output.ILURMask[0] = VshGetField(pShaderToken, FLD_OUT_ILU_MASK_X); - pInstruction->Output.ILURMask[1] = VshGetField(pShaderToken, FLD_OUT_ILU_MASK_Y); - pInstruction->Output.ILURMask[2] = VshGetField(pShaderToken, FLD_OUT_ILU_MASK_Z); - pInstruction->Output.ILURMask[3] = VshGetField(pShaderToken, FLD_OUT_ILU_MASK_W); - pInstruction->Output.ILURAddress = VshGetField(pShaderToken, FLD_OUT_R); - // Finally, get a0.x indirect constant addressing - pInstruction->a0x = VshGetField(pShaderToken, FLD_A0X); -} - -// Print functions -static char *VshGetRegisterName(VSH_PARAMETER_TYPE ParameterType) -{ - switch(ParameterType) - { - case PARAM_R: - return "r"; - case PARAM_V: - return "v"; - case PARAM_C: - return "c"; - case PARAM_O: - return "oPos"; - default: - return "?"; - } -} - -static void VshWriteOutputMask(boolean *OutputMask, - std::stringstream& pDisassembly) -{ - if(OutputMask[0] && OutputMask[1] && OutputMask[2] && OutputMask[3]) - { - // All components are there, no need to print the mask - return; - } - pDisassembly << "." << (OutputMask[0] ? "x" : "") - << (OutputMask[1] ? "y" : "") - << (OutputMask[2] ? "z" : "") - << (OutputMask[3] ? "w" : ""); -} - -static void VshWriteParameter(VSH_IMD_PARAMETER *pParameter, - std::stringstream& pDisassembly) -{ - pDisassembly << ", " << (pParameter->Parameter.Neg ? "-" : "") << VshGetRegisterName(pParameter->Parameter.ParameterType); - if(pParameter->Parameter.ParameterType == PARAM_C && pParameter->IndexesWithA0_X) - { - // Only display the offset if it's not 0. - if(pParameter->Parameter.Address) - { - pDisassembly << "[a0.x+" << pParameter->Parameter.Address << "]"; - } - else - { - pDisassembly << "[a0.x]"; - } - } - else - { - pDisassembly << pParameter->Parameter.Address; - } - // Only bother printing the swizzle if it is not .xyzw - if(!(pParameter->Parameter.Swizzle[0] == SWIZZLE_X && - pParameter->Parameter.Swizzle[1] == SWIZZLE_Y && - pParameter->Parameter.Swizzle[2] == SWIZZLE_Z && - pParameter->Parameter.Swizzle[3] == SWIZZLE_W)) - { - int i; - - pDisassembly << "."; - for (i = 0; i < 4; i++) - { - int j; - char Swizzle = '?'; - switch(pParameter->Parameter.Swizzle[i]) - { - case SWIZZLE_X: - Swizzle = 'x'; - break; - case SWIZZLE_Y: - Swizzle = 'y'; - break; - case SWIZZLE_Z: - Swizzle = 'z'; - break; - case SWIZZLE_W: - Swizzle = 'w'; - break; - } - pDisassembly << Swizzle; - for (j = i; j < 4; j++) - { - if(pParameter->Parameter.Swizzle[i] != pParameter->Parameter.Swizzle[j]) - { - break; - } - } - if(j == 4) - { - break; - } - } - } -} - - -char* XboxVertexRegisterAsString(DWORD VertexRegister) -{ - switch (VertexRegister) - { - case XTL::X_D3DVSDE_VERTEX: // -1 - return "D3DVSDE_VERTEX /* xbox ext. */"; - case XTL::X_D3DVSDE_POSITION: // 0 - return "D3DVSDE_POSITION"; - case XTL::X_D3DVSDE_BLENDWEIGHT: // 1 - return "D3DVSDE_BLENDWEIGHT"; - case XTL::X_D3DVSDE_NORMAL: // 2 - return "D3DVSDE_NORMAL"; - case XTL::X_D3DVSDE_DIFFUSE: // 3 - return "D3DVSDE_DIFFUSE"; - case XTL::X_D3DVSDE_SPECULAR: // 4 - return "D3DVSDE_SPECULAR"; - case XTL::X_D3DVSDE_FOG: // 5 - return "D3DVSDE_FOG"; - case XTL::X_D3DVSDE_POINTSIZE: // 6 - return "D3DVDSE_POINTSIZE"; - case XTL::X_D3DVSDE_BACKDIFFUSE: // 7 - return "D3DVSDE_BACKDIFFUSE /* xbox ext. */"; - case XTL::X_D3DVSDE_BACKSPECULAR: // 8 - return "D3DVSDE_BACKSPECULAR /* xbox ext. */"; - case XTL::X_D3DVSDE_TEXCOORD0: // 9 - return "D3DVSDE_TEXCOORD0"; - case XTL::X_D3DVSDE_TEXCOORD1: // 10 - return "D3DVSDE_TEXCOORD1"; - case XTL::X_D3DVSDE_TEXCOORD2: // 11 - return "D3DVSDE_TEXCOORD2"; - case XTL::X_D3DVSDE_TEXCOORD3: // 12 - return "D3DVSDE_TEXCOORD3"; - case 13: - return "13 /* unknown register */"; - case 14: - return "14 /* unknown register */"; - case 15: - return "15 /* unknown register */"; - default: - return "16 /* or higher, unknown register */"; - } -} - -#define D3DDECLUSAGE_UNSUPPORTED ((D3DDECLUSAGE)-1) - -D3DDECLUSAGE Xb2PCRegisterType -( - DWORD VertexRegister, - BYTE& PCUsageIndex -) -{ - D3DDECLUSAGE PCRegisterType; - PCUsageIndex = 0; - - switch (VertexRegister) - { - case XTL::X_D3DVSDE_VERTEX: // -1 - PCRegisterType = D3DDECLUSAGE_UNSUPPORTED; - break; - case XTL::X_D3DVSDE_POSITION: // 0 - PCRegisterType = D3DDECLUSAGE_POSITION; - break; - case XTL::X_D3DVSDE_BLENDWEIGHT: // 1 - PCRegisterType = D3DDECLUSAGE_BLENDWEIGHT; - break; - case XTL::X_D3DVSDE_NORMAL: // 2 - PCRegisterType = D3DDECLUSAGE_NORMAL; - break; - case XTL::X_D3DVSDE_DIFFUSE: // 3 - PCRegisterType = D3DDECLUSAGE_COLOR; PCUsageIndex = 0; - break; - case XTL::X_D3DVSDE_SPECULAR: // 4 - PCRegisterType = D3DDECLUSAGE_COLOR; PCUsageIndex = 1; - break; - case XTL::X_D3DVSDE_FOG: // 5 - PCRegisterType = D3DDECLUSAGE_FOG; - break; - case XTL::X_D3DVSDE_POINTSIZE: // 6 - PCRegisterType = D3DDECLUSAGE_PSIZE; - break; - case XTL::X_D3DVSDE_BACKDIFFUSE: // 7 - PCRegisterType = D3DDECLUSAGE_COLOR; PCUsageIndex = 2; - break; - case XTL::X_D3DVSDE_BACKSPECULAR: // 8 - PCRegisterType = D3DDECLUSAGE_COLOR; PCUsageIndex = 3; - break; - case XTL::X_D3DVSDE_TEXCOORD0: // 9 - PCRegisterType = D3DDECLUSAGE_TEXCOORD; PCUsageIndex = 0; - break; - case XTL::X_D3DVSDE_TEXCOORD1: // 10 - PCRegisterType = D3DDECLUSAGE_TEXCOORD; PCUsageIndex = 1; - break; - case XTL::X_D3DVSDE_TEXCOORD2: // 11 - PCRegisterType = D3DDECLUSAGE_TEXCOORD; PCUsageIndex = 2; - break; - case XTL::X_D3DVSDE_TEXCOORD3: // 12 - PCRegisterType = D3DDECLUSAGE_TEXCOORD; PCUsageIndex = 3; - break; - default: - PCRegisterType = D3DDECLUSAGE_UNSUPPORTED; - break; - } - - return PCRegisterType; -} - -extern D3DCAPS g_D3DCaps; - -enum { - X_VSH_TEMPORARY_REGISTER_COUNT = 12, // For Xbox temporary registers r0 to r11, mapped one-on-one to host - X_VSH_TEMP_OPOS = 12, // Used as intermediate storage for oPos (which Xbox can read through r12) - // X_VSH_TEMP_OFOG, // Enable once we treat oFog similar to oPos - // X_VSH_TEMP_OPTS, // Enable once we treat oPts similar to oPos - X_VSH_TEMP_SCRATCH = 13, // Used as intermediate storage in Xbox-to-host opcode conversion - X_VSH_TEMP_VERTEXREGBASE = 14 // Used for (1 up to 16) SetVertexData4f constants -}; - -static void VshWriteShader(VSH_XBOX_SHADER *pShader, - std::stringstream& pDisassembly, - D3DVERTEXELEMENT *pRecompiled, - boolean Truncate) -{ - switch(pShader->ShaderHeader.Version) - { - case VERSION_CXBX: -#ifdef CXBX_USE_VS30 - pDisassembly << "vs.3.0\n"; -#else - pDisassembly << "vs.2.x\n"; -#endif - break; - case VERSION_XVS: - pDisassembly << "xvs.1.1\n"; - break; - case VERSION_XVSS: - pDisassembly << "xvss.1.1\n"; - break; - case VERSION_XVSW: - pDisassembly << "xvsw.1.1\n"; - break; - default: - break; - } - - // Ensure extra temporary registers are assigned at the beginning, as stand-ins for undeclared v registers - // Abusing the truncate flag, which implies we're writing the final host shader - if (Truncate) { - std::stringstream moveConstantsToTemporaries; - - pDisassembly << "; Input usage declarations --\n"; - for(int i = 0; i < RegVIsUsedByShader.size(); i++){ - if (RegVIsUsedByShader[i]) { - if (!RegVIsPresentInDeclaration[i]) { - // Log test case and skip - // Any registers hitting this critera were already replaced with constant/temporary reads - // To correctly use the values given in SetVertexData4f. - // We need to move these constant values to temporaries so they can be used as input alongside other constants! - // We count down from the highest available on the host because Xbox titles don't use values that high, and we read from c192 (one above maximum Xbox c191 constant) and up - moveConstantsToTemporaries << "mov r" << (X_VSH_TEMP_VERTEXREGBASE + i) << ", c" << (CXBX_D3DVS_CONSTREG_VERTEXDATA4F_BASE + i) << "\n"; - // test-case : Blade II (before menu's) - // test-case : Namco Museum 50th Anniversary (at boot) - // test-case : Pac-Man World 2 (at boot) - // test-case : The Simpsons Road Rage (leaving menu's, before entering in-game) - // test-case : The SpongeBob SquarePants Movie (before menu's) - LOG_TEST_CASE("Shader uses undeclared Vertex Input Registers"); - continue; - } - - // dcl_texcoord can be useds for any user-defined data - // We need this because there is no reliable way to detect the real usage - // Xbox has no concept of 'usage types', it only requires a list of attribute register numbers. - // So we treat them all as 'user-defined' - pDisassembly << "dcl_texcoord" << i << " v" << i << "\n"; - } - } - - pDisassembly << moveConstantsToTemporaries.str(); - } - - for (int i = 0; i < pShader->IntermediateCount && (i < VSH_MAX_INSTRUCTION_COUNT || !Truncate); i++) - { - VSH_INTERMEDIATE_FORMAT *pIntermediate = &pShader->Intermediate[i]; - - if(i == VSH_MAX_INSTRUCTION_COUNT) - { - pDisassembly << "; -- Passing the truncation limit --\n"; - } - - // Writing combining sign if neccessary - if(pIntermediate->IsCombined) - { - pDisassembly << "+"; - } - - // Print the op code - if(pIntermediate->InstructionType == IMD_MAC) - { - // Dxbx addition : Safeguard against incorrect MAC opcodes : - if (pIntermediate->MAC > MAC_ARL) - pDisassembly << "??? "; - else - pDisassembly << MAC_OpCode[pIntermediate->MAC] << " "; - } - else // IMD_ILU - { - // Dxbx addition : Safeguard against incorrect ILU opcodes : - if (pIntermediate->ILU > ILU_LIT) - pDisassembly << "??? "; - else - pDisassembly << ILU_OpCode[pIntermediate->ILU] << " "; - } - - // Print the output parameter - if(pIntermediate->Output.Type == IMD_OUTPUT_A0X) - { - pDisassembly << "a0.x"; - } - else - { - switch(pIntermediate->Output.Type) - { - case IMD_OUTPUT_C: - pDisassembly << "c" << pIntermediate->Output.Address; - break; - case IMD_OUTPUT_R: - pDisassembly << "r" << pIntermediate->Output.Address; - break; - case IMD_OUTPUT_O: - // Dxbx addition : Safeguard against incorrect VSH_OREG_NAME values : - if ((int)pIntermediate->Output.Address > OREG_A0X) - ; // don't add anything - else - pDisassembly << OReg_Name[pIntermediate->Output.Address]; - break; - default: - CxbxKrnlCleanup("Invalid output register in vertex shader!"); - break; - } - VshWriteOutputMask(pIntermediate->Output.Mask, pDisassembly); - } - // Print the parameters - for (int p = 0; p < 3; p++) - { - VSH_IMD_PARAMETER *pParameter = &pIntermediate->Parameters[p]; - if(pParameter->Active) - { - VshWriteParameter(pParameter, pDisassembly); - } - } - pDisassembly << "\n"; - } -} - -static void VshAddParameter(VSH_PARAMETER *pParameter, - boolean a0x, - VSH_IMD_PARAMETER *pIntermediateParameter) -{ - pIntermediateParameter->Parameter = *pParameter; - pIntermediateParameter->Active = TRUE; - pIntermediateParameter->IndexesWithA0_X = a0x; -} - -static void VshAddParameters(VSH_SHADER_INSTRUCTION *pInstruction, - VSH_ILU ILU, - VSH_MAC MAC, - VSH_IMD_PARAMETER *pParameters) -{ - uint8_t ParamCount = 0; - VSH_OPCODE_PARAMS* pParams = VshGetOpCodeParams(ILU, MAC); - - // param A - if(pParams->A) - { - VshAddParameter(&pInstruction->A, pInstruction->a0x, &pParameters[ParamCount]); - ParamCount++; - } - - // param B - if(pParams->B) - { - VshAddParameter(&pInstruction->B, pInstruction->a0x, &pParameters[ParamCount]); - ParamCount++; - } - - // param C - if(pParams->C) - { - VshAddParameter(&pInstruction->C, pInstruction->a0x, &pParameters[ParamCount]); - ParamCount++; - } -} - -static void VshVerifyBufferBounds(VSH_XBOX_SHADER *pShader) -{ - if(pShader->IntermediateCount >= VSH_MAX_INTERMEDIATE_COUNT) - { - CxbxKrnlCleanup("Shader exceeds conversion buffer!"); - } -} - -static VSH_INTERMEDIATE_FORMAT *VshNewIntermediate(VSH_XBOX_SHADER *pShader) -{ - VshVerifyBufferBounds(pShader); - - ZeroMemory(&pShader->Intermediate[pShader->IntermediateCount], sizeof(VSH_INTERMEDIATE_FORMAT)); - - return &pShader->Intermediate[pShader->IntermediateCount++]; -} - -static void VshInsertIntermediate(VSH_XBOX_SHADER *pShader, - VSH_INTERMEDIATE_FORMAT *pIntermediate, - uint16_t Pos) -{ - VshVerifyBufferBounds(pShader); - - for (int i = pShader->IntermediateCount; i >= Pos; i--) - { - pShader->Intermediate[i + 1] = pShader->Intermediate[i]; - } - pShader->Intermediate[Pos] = *pIntermediate; - pShader->IntermediateCount++; -} - -static void VshDeleteIntermediate(VSH_XBOX_SHADER *pShader, - uint16_t Pos) -{ - for (int i = Pos; i < (pShader->IntermediateCount - 1); i++) - { - pShader->Intermediate[i] = pShader->Intermediate[i + 1]; - } - pShader->IntermediateCount--; -} - -static boolean VshAddInstructionMAC_R(VSH_SHADER_INSTRUCTION *pInstruction, - VSH_XBOX_SHADER *pShader, - boolean IsCombined) -{ - VSH_INTERMEDIATE_FORMAT *pIntermediate; - if(!HasMACR(pInstruction)) - { - return FALSE; - } - - pIntermediate = VshNewIntermediate(pShader); - pIntermediate->IsCombined = IsCombined; - - // Opcode - pIntermediate->InstructionType = IMD_MAC; - pIntermediate->MAC = pInstruction->MAC; - - // Output param - pIntermediate->Output.Type = IMD_OUTPUT_R; - pIntermediate->Output.Address = pInstruction->Output.MACRAddress; - memcpy(pIntermediate->Output.Mask, pInstruction->Output.MACRMask, sizeof(boolean) * 4); - - // Other parameters - VshAddParameters(pInstruction, ILU_NOP, pInstruction->MAC, pIntermediate->Parameters); - - return TRUE; -} - -static boolean VshAddInstructionMAC_O(VSH_SHADER_INSTRUCTION* pInstruction, - VSH_XBOX_SHADER *pShader, - boolean IsCombined) -{ - VSH_INTERMEDIATE_FORMAT *pIntermediate; - if(!HasMACO(pInstruction)) - { - return FALSE; - } - - pIntermediate = VshNewIntermediate(pShader); - pIntermediate->IsCombined = IsCombined; - - // Opcode - pIntermediate->InstructionType = IMD_MAC; - pIntermediate->MAC = pInstruction->MAC; - - // Output param - pIntermediate->Output.Type = pInstruction->Output.OutputType == OUTPUT_C ? IMD_OUTPUT_C : IMD_OUTPUT_O; - pIntermediate->Output.Address = pInstruction->Output.OutputAddress; - memcpy(pIntermediate->Output.Mask, pInstruction->Output.OutputMask, sizeof(boolean) * 4); - - // Other parameters - VshAddParameters(pInstruction, ILU_NOP, pInstruction->MAC, pIntermediate->Parameters); - - return TRUE; -} - -static boolean VshAddInstructionMAC_ARL(VSH_SHADER_INSTRUCTION *pInstruction, - VSH_XBOX_SHADER *pShader, - boolean IsCombined) -{ - VSH_INTERMEDIATE_FORMAT *pIntermediate; - if(!HasMACARL(pInstruction)) - { - return FALSE; - } - - pIntermediate = VshNewIntermediate(pShader); - pIntermediate->IsCombined = IsCombined; - - // Opcode - pIntermediate->InstructionType = IMD_MAC; - pIntermediate->MAC = pInstruction->MAC; - - // Output param - pIntermediate->Output.Type = IMD_OUTPUT_A0X; - pIntermediate->Output.Address = pInstruction->Output.OutputAddress; - - // Other parameters - VshAddParameters(pInstruction, ILU_NOP, pInstruction->MAC, pIntermediate->Parameters); - - return TRUE; -} - -// Dxbx addition : Scalar instructions reading from W should read from X instead -static boolean DxbxFixupScalarParameter(VSH_SHADER_INSTRUCTION *pInstruction, - VSH_XBOX_SHADER *pShader, - VSH_PARAMETER *pParameter); - -static boolean VshAddInstructionILU_R(VSH_SHADER_INSTRUCTION *pInstruction, - VSH_XBOX_SHADER *pShader, - boolean IsCombined) -{ - VSH_INTERMEDIATE_FORMAT *pIntermediate; - if(!HasILUR(pInstruction)) - { - return FALSE; - } - - pIntermediate = VshNewIntermediate(pShader); - pIntermediate->IsCombined = IsCombined; - - // Opcode - pIntermediate->InstructionType = IMD_ILU; - pIntermediate->ILU = pInstruction->ILU; - - // Output param - pIntermediate->Output.Type = IMD_OUTPUT_R; - // If this is a combined instruction, only r1 is allowed (R address should not be used) - pIntermediate->Output.Address = IsCombined ? 1 : pInstruction->Output.ILURAddress; - memcpy(pIntermediate->Output.Mask, pInstruction->Output.ILURMask, sizeof(boolean) * 4); - - // Other parameters - VshAddParameters(pInstruction, pInstruction->ILU, MAC_NOP, pIntermediate->Parameters); - - return TRUE; -} - -static boolean VshAddInstructionILU_O(VSH_SHADER_INSTRUCTION *pInstruction, - VSH_XBOX_SHADER *pShader, - boolean IsCombined) -{ - VSH_INTERMEDIATE_FORMAT *pIntermediate; - if(!HasILUO(pInstruction)) - { - return FALSE; - } - - pIntermediate = VshNewIntermediate(pShader); - pIntermediate->IsCombined = IsCombined; - - // Opcode - pIntermediate->InstructionType = IMD_ILU; - pIntermediate->ILU = pInstruction->ILU; - - // Output param - pIntermediate->Output.Type = pInstruction->Output.OutputType == OUTPUT_C ? IMD_OUTPUT_C : IMD_OUTPUT_O; - pIntermediate->Output.Address = pInstruction->Output.OutputAddress; - memcpy(pIntermediate->Output.Mask, pInstruction->Output.OutputMask, sizeof(boolean) * 4); - - // Other parameters - VshAddParameters(pInstruction, pInstruction->ILU, MAC_NOP, pIntermediate->Parameters); - - return TRUE; -} - -static void VshConvertToIntermediate(VSH_SHADER_INSTRUCTION *pInstruction, - VSH_XBOX_SHADER *pShader) -{ - // Five types of instructions: - // MAC - // - // ILU - // - // MAC - // +ILU - // - // MAC - // +MAC - // +ILU - // - // MAC - // +ILU - // +ILU - boolean IsCombined = FALSE; - - // Dxbx note : Scalar instructions read from C, but use X instead of W, fix that : - DxbxFixupScalarParameter(pInstruction, pShader, &pInstruction->C); - - if(VshAddInstructionMAC_R(pInstruction, pShader, IsCombined)) - { - if(HasMACO(pInstruction) || - HasILUR(pInstruction) || - HasILUO(pInstruction)) - { - IsCombined = TRUE; - } - } - if(VshAddInstructionMAC_O(pInstruction, pShader, IsCombined)) - { - if(HasILUR(pInstruction) || - HasILUO(pInstruction)) - { - IsCombined = TRUE; - } - } - // Special case, arl (mov a0.x, ...) - if(VshAddInstructionMAC_ARL(pInstruction, pShader, IsCombined)) - { - if(HasILUR(pInstruction) || - HasILUO(pInstruction)) - { - IsCombined = TRUE; - } - } - if(VshAddInstructionILU_R(pInstruction, pShader, IsCombined)) - { - if(HasILUO(pInstruction)) - { - IsCombined = TRUE; - } - } - (void)VshAddInstructionILU_O(pInstruction, pShader, IsCombined); -} - -static inline void VshSetSwizzle(VSH_PARAMETER *pParameter, - VSH_SWIZZLE x, - VSH_SWIZZLE y, - VSH_SWIZZLE z, - VSH_SWIZZLE w) -{ - pParameter->Swizzle[0] = x; - pParameter->Swizzle[1] = y; - pParameter->Swizzle[2] = z; - pParameter->Swizzle[3] = w; -} - -static inline void VshSetSwizzle(VSH_IMD_PARAMETER *pParameter, - VSH_SWIZZLE x, - VSH_SWIZZLE y, - VSH_SWIZZLE z, - VSH_SWIZZLE w) -{ - VshSetSwizzle(&pParameter->Parameter, x, y, z, w); -} - -static inline void VshSetOutputMask(VSH_IMD_OUTPUT* pOutput, - boolean MaskX, - boolean MaskY, - boolean MaskZ, - boolean MaskW) -{ - pOutput->Mask[0] = MaskX; - pOutput->Mask[1] = MaskY; - pOutput->Mask[2] = MaskZ; - pOutput->Mask[3] = MaskW; -} - -// Dxbx addition : Scalar instructions reading from W should read from X instead -static boolean DxbxFixupScalarParameter(VSH_SHADER_INSTRUCTION *pInstruction, - VSH_XBOX_SHADER *pShader, - VSH_PARAMETER *pParameter) -{ - boolean Result; - - // The DirectX vertex shader language specifies that the exp, log, rcc, rcp, and rsq instructions - // all operate on the "w" component of the input. But the microcode versions of these instructions - // actually operate on the "x" component of the input. - Result = false; - - // Test if this is a scalar instruction : - if (pInstruction->ILU == ILU_RCP || - pInstruction->ILU == ILU_RCC || - pInstruction->ILU == ILU_RSQ || - pInstruction->ILU == ILU_LOG) - { - // Test if this parameter reads all components, including W (TODO : Or should we fixup any W reading swizzle?) : - if ((pParameter->Swizzle[0] == SWIZZLE_X) - && (pParameter->Swizzle[1] == SWIZZLE_Y) - && (pParameter->Swizzle[2] == SWIZZLE_Z) - && (pParameter->Swizzle[3] == SWIZZLE_W)) - { - // Change the read from W into a read from X (this fixes the XDK VolumeLight sample) : - VshSetSwizzle(pParameter, SWIZZLE_X, SWIZZLE_X, SWIZZLE_X, SWIZZLE_X); - DbgVshPrintf("Dxbx fixup on scalar instruction applied; Changed read of uninitialized W into a read of X!\n"); - Result = true; - } - } - - return Result; -} - -/* - mul oPos.xyz, r12, c-38 - +rcc r1.x, r12.w - - mad oPos.xyz, r12, r1.x, c-37 -*/ -static void VshRemoveScreenSpaceInstructions(VSH_XBOX_SHADER *pShader) -{ - int16_t PosC38 = -1; - int deleted = 0; - - for (int i = 0; i < pShader->IntermediateCount; i++) - { - VSH_INTERMEDIATE_FORMAT* pIntermediate = &pShader->Intermediate[i]; - - for (int k = 0; k < 3; k++) - { - if(pIntermediate->Parameters[k].Active) - { - if(pIntermediate->Parameters[k].Parameter.ParameterType == PARAM_C && - !pIntermediate->Parameters[k].IndexesWithA0_X) - { - if(pIntermediate->Parameters[k].Parameter.Address == -37) - { - // Found c-37, remove the instruction - if(k == 2 && - pIntermediate->Parameters[1].Active && - pIntermediate->Parameters[1].Parameter.ParameterType == PARAM_R) - { - DbgVshPrintf("PosC38 = %d i = %d\n", PosC38, i); - for (int j = (i-1); j >= 0; j--) - { - VSH_INTERMEDIATE_FORMAT* pIntermediate1W = &pShader->Intermediate[j]; - // Time to start searching for +rcc r#.x, r12.w - if(pIntermediate1W->InstructionType == IMD_ILU && - pIntermediate1W->ILU == ILU_RCC && - pIntermediate1W->Output.Type == IMD_OUTPUT_R && - pIntermediate1W->Output.Address == - pIntermediate->Parameters[1].Parameter.Address) - { - DbgVshPrintf("Deleted +rcc r1.x, r12.w\n"); - VshDeleteIntermediate(pShader, j); - deleted++; - i--; - //j--; - break; - } - } - } - VshDeleteIntermediate(pShader, i); - deleted++; - i--; - DbgVshPrintf("Deleted mad oPos.xyz, r12, r1.x, c-37\n"); - break; - } - else if(pIntermediate->Parameters[k].Parameter.Address == -38) - { - VshDeleteIntermediate(pShader, i); - PosC38 = i; - deleted++; - i--; - DbgVshPrintf("Deleted mul oPos.xyz, r12, c-38\n"); - } - } - } - } - } - - // If we couldn't find the generic screen space transformation we're - // assuming that the shader writes direct screen coordinates that must be - // normalized. This hack will fail if (a) the shader uses custom screen - // space transformation, (b) reads r13 or r12 after we have written to - // them, or (c) doesn't reserve c-38 and c-37 for scale and offset. - if(deleted != 3) - { - EmuLog(LOG_LEVEL::WARNING, "Applying screen space vertex shader patching hack!"); - for (int i = 0; i < pShader->IntermediateCount; i++) - { - VSH_INTERMEDIATE_FORMAT* pIntermediate = &pShader->Intermediate[i]; - - // Find instructions outputting to oPos. - if( pIntermediate->Output.Type == IMD_OUTPUT_O && - pIntermediate->Output.Address == OREG_OPOS) - { - // Redirect output to r12. - pIntermediate->Output.Type = IMD_OUTPUT_R; - pIntermediate->Output.Address = X_VSH_TEMP_OPOS; - - // Scale r12 to r13. (mul r13.[mask], r12, c58) - VSH_INTERMEDIATE_FORMAT MulIntermediate; - MulIntermediate.IsCombined = FALSE; - MulIntermediate.InstructionType = IMD_MAC; - MulIntermediate.MAC = MAC_MUL; - MulIntermediate.Output.Type = IMD_OUTPUT_R; - MulIntermediate.Output.Address = X_VSH_TEMP_SCRATCH; - MulIntermediate.Output.Mask[0] = pIntermediate->Output.Mask[0]; - MulIntermediate.Output.Mask[1] = pIntermediate->Output.Mask[1]; - MulIntermediate.Output.Mask[2] = pIntermediate->Output.Mask[2]; - MulIntermediate.Output.Mask[3] = pIntermediate->Output.Mask[3]; - MulIntermediate.Parameters[0].Active = TRUE; - MulIntermediate.Parameters[0].IndexesWithA0_X = FALSE; - MulIntermediate.Parameters[0].Parameter.ParameterType = PARAM_R; - MulIntermediate.Parameters[0].Parameter.Address = X_VSH_TEMP_OPOS; - MulIntermediate.Parameters[0].Parameter.Neg = FALSE; - VshSetSwizzle(&MulIntermediate.Parameters[0], SWIZZLE_X, SWIZZLE_Y, SWIZZLE_Z, SWIZZLE_W); - MulIntermediate.Parameters[1].Active = TRUE; - MulIntermediate.Parameters[1].IndexesWithA0_X = FALSE; - MulIntermediate.Parameters[1].Parameter.ParameterType = PARAM_C; - MulIntermediate.Parameters[1].Parameter.Address = ConvertCRegister(58); - MulIntermediate.Parameters[1].Parameter.Neg = FALSE; - VshSetSwizzle(&MulIntermediate.Parameters[1], SWIZZLE_X, SWIZZLE_Y, SWIZZLE_Z, SWIZZLE_W); - MulIntermediate.Parameters[2].Active = FALSE; - VshInsertIntermediate(pShader, &MulIntermediate, ++i); - - // Add offset with r13 to oPos (add oPos.[mask], r13, c59) - VSH_INTERMEDIATE_FORMAT AddIntermediate = MulIntermediate; - AddIntermediate.MAC = MAC_ADD; - AddIntermediate.Output.Type = IMD_OUTPUT_O; - AddIntermediate.Output.Address = OREG_OPOS; - AddIntermediate.Parameters[0].Parameter.ParameterType = PARAM_R; - AddIntermediate.Parameters[0].Parameter.Address = X_VSH_TEMP_SCRATCH; - AddIntermediate.Parameters[1].Parameter.Address = ConvertCRegister(59); - VshInsertIntermediate(pShader, &AddIntermediate, ++i); - } - } - } -} - -static void VshRemoveUnsupportedObRegisters(VSH_XBOX_SHADER *pShader) -{ - int deleted = 0; - - for (int i = 0; i < pShader->IntermediateCount; i++) { - VSH_INTERMEDIATE_FORMAT* pIntermediate = &pShader->Intermediate[i]; - - if (pIntermediate->Output.Type == IMD_OUTPUT_O && (pIntermediate->Output.Address == OREG_OB0 || pIntermediate->Output.Address == OREG_OB1)) { - DbgVshPrintf("Deleted unsupported write to %s\n", OReg_Name[pIntermediate->Output.Address]); - VshDeleteIntermediate(pShader, i); - i--; - } - } -} - -// Converts the intermediate format vertex shader to DirectX 8/9 format -static boolean VshConvertShader(VSH_XBOX_SHADER *pShader, - boolean bNoReservedConstants -) -{ - // TODO: What about state shaders and such? - - pShader->ShaderHeader.Version = VERSION_CXBX; - - // Search for the screen space instructions, and remove them - if(!bNoReservedConstants) - { - VshRemoveScreenSpaceInstructions(pShader); - } - - // Windows does not support back-facing colours, so we remove them from the shaders - // Test Case: Panzer Dragoon Orta - VshRemoveUnsupportedObRegisters(pShader); - - // TODO: Add routine for compacting r register usage so that at least one is freed (two if dph and r12) - - for (int i = 0; i < pShader->IntermediateCount; i++) - { - VSH_INTERMEDIATE_FORMAT* pIntermediate = &pShader->Intermediate[i]; - // Combining not supported in vs.1.1 - pIntermediate->IsCombined = FALSE; - - if(pIntermediate->Output.Type == IMD_OUTPUT_O && (pIntermediate->Output.Address == OREG_OPTS || pIntermediate->Output.Address == OREG_OFOG)) - { - // The PC shader assembler doesn't like masks on scalar registers - VshSetOutputMask(&pIntermediate->Output, TRUE, TRUE, TRUE, TRUE); - - // Fix when mad or mov to a scaler input does not use a replicate swizzle - // MAD Test case: Panzer Dragoon Orta - // MOV Test case: DOA3, Mechassault (Const) - // MUL Test case: Amped - // TODO Previously we applied this fix for specified instructions - // When should we not apply the correction? - if (true) - { - // Clear all but the first swizzle for each parameter - // TODO: Is this sufficient? Perhaps we need to be smart about which swizzle to select - for (int param = 0; param < 3; param++) { - pIntermediate->Parameters[param].Parameter.Swizzle[1] = pIntermediate->Parameters[param].Parameter.Swizzle[0]; - pIntermediate->Parameters[param].Parameter.Swizzle[2] = pIntermediate->Parameters[param].Parameter.Swizzle[0]; - pIntermediate->Parameters[param].Parameter.Swizzle[3] = pIntermediate->Parameters[param].Parameter.Swizzle[0]; - } - } - } - - if(pIntermediate->InstructionType == IMD_ILU && pIntermediate->ILU == ILU_RCC) - { - // Convert rcc to rcp - pIntermediate->ILU = ILU_RCP; - } - - auto sw = pIntermediate->Parameters[0].Parameter.Swizzle; - bool singleSwizzle = sw[0] == sw[1] && sw[1] == sw[2] && sw[2] == sw[3]; - - if (!singleSwizzle) - { - // Fix when RSQ reads from unitialized components - if (pIntermediate->InstructionType == IMD_ILU && pIntermediate->ILU == ILU_RSQ) { - int swizzle = (pIntermediate->Output.Mask[0]) | (pIntermediate->Output.Mask[1] << 1) | (pIntermediate->Output.Mask[2] << 2) | (pIntermediate->Output.Mask[3] << 3); - switch (swizzle) - { - case 1: - VshSetSwizzle(&pIntermediate->Parameters[0], SWIZZLE_X, SWIZZLE_X, SWIZZLE_X, SWIZZLE_X); - break; - case 2: - VshSetSwizzle(&pIntermediate->Parameters[0], SWIZZLE_Y, SWIZZLE_Y, SWIZZLE_Y, SWIZZLE_Y); - break; - case 4: - VshSetSwizzle(&pIntermediate->Parameters[0], SWIZZLE_Z, SWIZZLE_Z, SWIZZLE_Z, SWIZZLE_Z); - break; - case 8: - VshSetSwizzle(&pIntermediate->Parameters[0], SWIZZLE_W, SWIZZLE_W, SWIZZLE_W, SWIZZLE_W); - break; - case 15: - default: - LOG_TEST_CASE("rsq instruction with invalid swizzle"); - break; - } - } - } - - for (int j = 0; j < 3; j++) - { - //if(pIntermediate->Parameters[j].Active) - { - // Make constant registers range from 0 to 191 instead of -96 to 95 - if (pIntermediate->Parameters[j].Parameter.ParameterType == PARAM_C) - { - //if(pIntermediate->Parameters[j].Parameter.Address < 0) - pIntermediate->Parameters[j].Parameter.Address += X_D3DVS_CONSTREG_BIAS; - } - - if (pIntermediate->Parameters[j].Parameter.ParameterType == PARAM_V) { - RegVIsUsedByShader[pIntermediate->Parameters[j].Parameter.Address] = TRUE; - - if (!RegVIsPresentInDeclaration[pIntermediate->Parameters[j].Parameter.Address]) { - // This vertex register was not declared and therefore is not present within the Vertex Data object - // We read from temporary registers instead, that are set based on constants, in-turn, set by SetVertexData4f - // We count down from the highest available on the host because Xbox titles don't use values that high, and we read from c192 (one above maximum Xbox c191 constant) and up - pIntermediate->Parameters[j].Parameter.ParameterType = PARAM_R; - pIntermediate->Parameters[j].Parameter.Address += X_VSH_TEMP_VERTEXREGBASE; - } - } - } - } - - // Make constant registers range from 0 to 191 instead of -96 to 95 - if(pIntermediate->Output.Type == IMD_OUTPUT_C) - { - //if(pIntermediate->Output.Address < 0) - pIntermediate->Output.Address += X_D3DVS_CONSTREG_BIAS; - } - - - - if(pIntermediate->InstructionType == IMD_MAC && pIntermediate->MAC == MAC_DPH) - { - // 2010/01/12 - revel8n - attempt to alleviate conversion issues relate to the dph instruction - - // Replace dph with dp3 and add - if(pIntermediate->Output.Type != IMD_OUTPUT_R) - { - // TODO: Complete dph support - EmuLog(LOG_LEVEL::WARNING, "Can't simulate dph for other than output r registers (yet)"); - - VSH_INTERMEDIATE_FORMAT TmpIntermediate = *pIntermediate; - - // modify the instructions - pIntermediate->MAC = MAC_DP3; - pIntermediate->Output.Type = IMD_OUTPUT_R; - pIntermediate->Output.Address = X_VSH_TEMP_SCRATCH; - VshSetOutputMask(&pIntermediate->Output, TRUE, TRUE, TRUE, TRUE); - - TmpIntermediate.MAC = MAC_ADD; - TmpIntermediate.Parameters[0].IndexesWithA0_X = FALSE; - TmpIntermediate.Parameters[0].Parameter.ParameterType = PARAM_R; - TmpIntermediate.Parameters[0].Parameter.Address = X_VSH_TEMP_SCRATCH; - TmpIntermediate.Parameters[0].Parameter.Neg = FALSE; - VshSetSwizzle(&TmpIntermediate.Parameters[1], SWIZZLE_W, SWIZZLE_W, SWIZZLE_W, SWIZZLE_W); - // Is this output register a scalar - if (TmpIntermediate.Output.Type == IMD_OUTPUT_O) { - if ((TmpIntermediate.Output.Address == OREG_OFOG) || (TmpIntermediate.Output.Address == OREG_OPTS)) { - // This fixes test case "Namco Museum 50th Anniversary" - // The PC shader assembler doesn't like masks on scalar registers - VshSetOutputMask(&TmpIntermediate.Output, TRUE, TRUE, TRUE, TRUE); - // Make the first source parameter use the w swizzle too - VshSetSwizzle(&TmpIntermediate.Parameters[0], SWIZZLE_W, SWIZZLE_W, SWIZZLE_W, SWIZZLE_W); - } - } - - VshInsertIntermediate(pShader, &TmpIntermediate, i + 1); - } - else - { - VSH_INTERMEDIATE_FORMAT TmpIntermediate = *pIntermediate; - pIntermediate->MAC = MAC_DP3; - TmpIntermediate.MAC = MAC_ADD; - TmpIntermediate.Parameters[0].IndexesWithA0_X = FALSE; - TmpIntermediate.Parameters[0].Parameter.ParameterType = PARAM_R; - TmpIntermediate.Parameters[0].Parameter.Address = TmpIntermediate.Output.Address; - TmpIntermediate.Parameters[0].Parameter.Neg = FALSE; - - int swizzle = (TmpIntermediate.Output.Mask[0]) | (TmpIntermediate.Output.Mask[1] << 1) | (TmpIntermediate.Output.Mask[2] << 2) | (TmpIntermediate.Output.Mask[3] << 3); - switch (swizzle) - { - case 1: - VshSetSwizzle(&TmpIntermediate.Parameters[0], SWIZZLE_X, SWIZZLE_X, SWIZZLE_X, SWIZZLE_X); - break; - case 2: - VshSetSwizzle(&TmpIntermediate.Parameters[0], SWIZZLE_Y, SWIZZLE_Y, SWIZZLE_Y, SWIZZLE_Y); - break; - case 4: - VshSetSwizzle(&TmpIntermediate.Parameters[0], SWIZZLE_Z, SWIZZLE_Z, SWIZZLE_Z, SWIZZLE_Z); - break; - case 8: - VshSetSwizzle(&TmpIntermediate.Parameters[0], SWIZZLE_W, SWIZZLE_W, SWIZZLE_W, SWIZZLE_W); - break; - case 15: - default: - VshSetSwizzle(&TmpIntermediate.Parameters[0], SWIZZLE_X, SWIZZLE_Y, SWIZZLE_Z, SWIZZLE_W); - break; - } - //VshSetSwizzle(&TmpIntermediate.Parameters[0], SWIZZLE_W, SWIZZLE_W, SWIZZLE_W, SWIZZLE_W); - VshSetSwizzle(&TmpIntermediate.Parameters[1], SWIZZLE_W, SWIZZLE_W, SWIZZLE_W, SWIZZLE_W); - //VshSetOutputMask(&TmpIntermediate.Output, FALSE, FALSE, FALSE, TRUE); - VshInsertIntermediate(pShader, &TmpIntermediate, i + 1); - } - i++; - } - } - - // Replace all writes to oPos with writes to r12. - // On Xbox, oPos is read/write, essentially a 13th temporary register - // In DX9 and vs_2_x, oPos is write-only, so we'll use r12 in its place - // And at the end of the shader, write r12 to oPos - for (int i = 0; i < pShader->IntermediateCount; i++) { - VSH_INTERMEDIATE_FORMAT* pIntermediate = &pShader->Intermediate[i]; - if (pIntermediate->Output.Type == IMD_OUTPUT_O && pIntermediate->Output.Address == OREG_OPOS) { - pIntermediate->Output.Type = IMD_OUTPUT_R; - pIntermediate->Output.Address = X_VSH_TEMP_OPOS; - } - } - - // We append one additional instruction to mov oPos, r12 - // TODO : *IF* r12 is not read after the final write to oPos, - // it'd be more efficient to not-replace this oPos write by r12, - // so that we don't have to do the following : - VSH_INTERMEDIATE_FORMAT MovIntermediate = {0}; - MovIntermediate.MAC = MAC_MOV; - MovIntermediate.Output.Type = IMD_OUTPUT_O; - MovIntermediate.Output.Address = OREG_OPOS; - MovIntermediate.Output.Mask[0] = true; - MovIntermediate.Output.Mask[1] = true; - MovIntermediate.Output.Mask[2] = true; - MovIntermediate.Output.Mask[3] = true; - MovIntermediate.Parameters[0].Active = true; - MovIntermediate.Parameters[0].Parameter.ParameterType = PARAM_R; - MovIntermediate.Parameters[0].Parameter.Address = X_VSH_TEMP_OPOS; - MovIntermediate.Parameters[0].Parameter.Swizzle[0] = SWIZZLE_X; - MovIntermediate.Parameters[0].Parameter.Swizzle[1] = SWIZZLE_Y; - MovIntermediate.Parameters[0].Parameter.Swizzle[2] = SWIZZLE_Z; - MovIntermediate.Parameters[0].Parameter.Swizzle[3] = SWIZZLE_W; - VshInsertIntermediate(pShader, &MovIntermediate, pShader->IntermediateCount); - - return TRUE; -} - -// **************************************************************************** -// * Vertex shader declaration recompiler -// **************************************************************************** - -class XboxVertexDeclarationConverter -{ -protected: - // Internal variables - CxbxVertexShaderInfo* pVertexShaderInfoToSet; - CxbxVertexShaderStreamInfo* pCurrentVertexShaderStreamInfo = nullptr; - DWORD hostTemporaryRegisterCount; - bool IsFixedFunction; - D3DVERTEXELEMENT* pRecompiled; - -public: - // Output - DWORD XboxDeclarationCount; - DWORD HostDeclarationSize; - -private: - // VERTEX SHADER - - static DWORD VshGetDeclarationCount(DWORD *pXboxDeclaration) - { - DWORD Pos = 0; - while (pXboxDeclaration[Pos] != X_D3DVSD_END()) - { - Pos++; - } - - return Pos + 1; - } - - static inline DWORD VshGetTokenType(DWORD XboxToken) - { - return (XboxToken & X_D3DVSD_TOKENTYPEMASK) >> X_D3DVSD_TOKENTYPESHIFT; - } - - static inline WORD VshGetVertexStream(DWORD XboxToken) - { - return (XboxToken & X_D3DVSD_STREAMNUMBERMASK) >> X_D3DVSD_STREAMNUMBERSHIFT; - } - - inline DWORD VshGetVertexRegister(DWORD XboxToken) - { - DWORD regNum = (XboxToken & X_D3DVSD_VERTEXREGMASK) >> X_D3DVSD_VERTEXREGSHIFT; - if (regNum >= hostTemporaryRegisterCount /*12 for D3D8, D3D9 value depends on host GPU */) { - // test-case : BLiNX: the time sweeper - // test-case : Lego Star Wars - LOG_TEST_CASE("RegNum > NumTemps"); - } - return regNum; - } - - inline DWORD VshGetVertexRegisterIn(DWORD XboxToken) - { - DWORD regNum = (XboxToken & X_D3DVSD_VERTEXREGINMASK) >> X_D3DVSD_VERTEXREGINSHIFT; - if (regNum >= hostTemporaryRegisterCount /*12 for D3D8, D3D9 value depends on host GPU */) { - // test-case : Lego Star Wars - LOG_TEST_CASE("RegNum > NumTemps"); - } - return regNum; - } - - void VshDumpXboxDeclaration(DWORD* pXboxDeclaration) - { - DbgVshPrintf("DWORD dwVSHDecl[] =\n{\n"); - unsigned iNumberOfVertexStreams = 0; - bool bStreamNeedsPatching = false; - auto pXboxToken = pXboxDeclaration; - while (*pXboxToken != X_D3DVSD_END()) // X_D3DVSD_TOKEN_END - { - DWORD Step = 1; - - switch (VshGetTokenType(*pXboxToken)) { - case XTL::X_D3DVSD_TOKEN_NOP: { - DbgVshPrintf("\tD3DVSD_NOP(),\n"); - break; - } - case XTL::X_D3DVSD_TOKEN_STREAM: { - if (*pXboxToken & X_D3DVSD_STREAMTESSMASK) { - DbgVshPrintf("\tD3DVSD_STREAM_TESS(),\n"); - } else { - if (iNumberOfVertexStreams > 0) { - DbgVshPrintf("\t// NeedPatching: %d\n", bStreamNeedsPatching); - } - DWORD StreamNumber = VshGetVertexStream(*pXboxToken); - DbgVshPrintf("\tD3DVSD_STREAM(%u),\n", StreamNumber); - iNumberOfVertexStreams++; - bStreamNeedsPatching = false; - } - break; - } - case XTL::X_D3DVSD_TOKEN_STREAMDATA: { - if (*pXboxToken & X_D3DVSD_MASK_SKIP) { - WORD SkipCount = (*pXboxToken & X_D3DVSD_SKIPCOUNTMASK) >> X_D3DVSD_SKIPCOUNTSHIFT; - if (*pXboxToken & X_D3DVSD_MASK_SKIPBYTES) { - DbgVshPrintf("\tD3DVSD_SKIPBYTES(%d), /* xbox ext. */\n", SkipCount); - } else { - DbgVshPrintf("\tD3DVSD_SKIP(%d),\n", SkipCount); - } - } else { - DWORD VertexRegister = VshGetVertexRegister(*pXboxToken); - if (IsFixedFunction) { - DbgVshPrintf("\t\tD3DVSD_REG(%s, ", XboxVertexRegisterAsString(VertexRegister)); - } else { - DbgVshPrintf("\t\tD3DVSD_REG(%d, ", (BYTE)VertexRegister); - } - - DWORD XboxVertexElementDataType = (*pXboxToken & X_D3DVSD_DATATYPEMASK) >> X_D3DVSD_DATATYPESHIFT; - switch (XboxVertexElementDataType) { - case XTL::X_D3DVSDT_FLOAT1: // 0x12: - DbgVshPrintf("D3DVSDT_FLOAT1"); - break; - case XTL::X_D3DVSDT_FLOAT2: // 0x22: - DbgVshPrintf("D3DVSDT_FLOAT2"); - break; - case XTL::X_D3DVSDT_FLOAT3: // 0x32: - DbgVshPrintf("D3DVSDT_FLOAT3"); - break; - case XTL::X_D3DVSDT_FLOAT4: // 0x42: - DbgVshPrintf("D3DVSDT_FLOAT4"); - break; - case XTL::X_D3DVSDT_D3DCOLOR: // 0x40: - DbgVshPrintf("D3DVSDT_D3DCOLOR"); - break; - case XTL::X_D3DVSDT_SHORT2: // 0x25: - DbgVshPrintf("D3DVSDT_SHORT2"); - break; - case XTL::X_D3DVSDT_SHORT4: // 0x45: - DbgVshPrintf("D3DVSDT_SHORT4"); - break; - case XTL::X_D3DVSDT_NORMSHORT1: // 0x11: - DbgVshPrintf("D3DVSDT_NORMSHORT1 /* xbox ext. */"); - bStreamNeedsPatching = true; - break; - case XTL::X_D3DVSDT_NORMSHORT2: // 0x21: - if (g_D3DCaps.DeclTypes & D3DDTCAPS_SHORT2N) { - DbgVshPrintf("D3DVSDT_NORMSHORT2"); - } else { - DbgVshPrintf("D3DVSDT_NORMSHORT2 /* xbox ext. */"); - bStreamNeedsPatching = true; - } - break; - case XTL::X_D3DVSDT_NORMSHORT3: // 0x31: - DbgVshPrintf("D3DVSDT_NORMSHORT3 /* xbox ext. */"); - bStreamNeedsPatching = true; - break; - case XTL::X_D3DVSDT_NORMSHORT4: // 0x41: - if (g_D3DCaps.DeclTypes & D3DDTCAPS_SHORT4N) { - DbgVshPrintf("D3DVSDT_NORMSHORT4"); - // No need for patching in D3D9 - } else { - DbgVshPrintf("D3DVSDT_NORMSHORT4 /* xbox ext. */"); - bStreamNeedsPatching = true; - } - break; - case XTL::X_D3DVSDT_NORMPACKED3: // 0x16: - DbgVshPrintf("D3DVSDT_NORMPACKED3 /* xbox ext. */"); - bStreamNeedsPatching = true; - break; - case XTL::X_D3DVSDT_SHORT1: // 0x15: - DbgVshPrintf("D3DVSDT_SHORT1 /* xbox ext. */"); - bStreamNeedsPatching = true; - break; - case XTL::X_D3DVSDT_SHORT3: // 0x35: - DbgVshPrintf("D3DVSDT_SHORT3 /* xbox ext. */"); - bStreamNeedsPatching = true; - break; - case XTL::X_D3DVSDT_PBYTE1: // 0x14: - DbgVshPrintf("D3DVSDT_PBYTE1 /* xbox ext. */"); - bStreamNeedsPatching = true; - break; - case XTL::X_D3DVSDT_PBYTE2: // 0x24: - DbgVshPrintf("D3DVSDT_PBYTE2 /* xbox ext. */"); - bStreamNeedsPatching = true; - break; - case XTL::X_D3DVSDT_PBYTE3: // 0x34: - DbgVshPrintf("D3DVSDT_PBYTE3 /* xbox ext. */"); - bStreamNeedsPatching = true; - break; - case XTL::X_D3DVSDT_PBYTE4: // 0x44: - if (g_D3DCaps.DeclTypes & D3DDTCAPS_UBYTE4N) { - DbgVshPrintf("D3DVSDT_PBYTE4"); - } else { - DbgVshPrintf("D3DVSDT_PBYTE4 /* xbox ext. */"); - bStreamNeedsPatching = true; - } - break; - case XTL::X_D3DVSDT_FLOAT2H: // 0x72: - DbgVshPrintf("D3DVSDT_FLOAT2H /* xbox ext. */"); - bStreamNeedsPatching = true; - break; - case XTL::X_D3DVSDT_NONE: // 0x02: - DbgVshPrintf("D3DVSDT_NONE /* xbox ext. */"); - break; - default: - DbgVshPrintf("Unknown data type for D3DVSD_REG: 0x%02X\n", XboxVertexElementDataType); - break; - } - - DbgVshPrintf("),\n"); - }; - break; - } - case XTL::X_D3DVSD_TOKEN_TESSELLATOR: { - DWORD VertexRegisterOut = VshGetVertexRegister(*pXboxToken); - if (*pXboxToken & X_D3DVSD_MASK_TESSUV) { - DbgVshPrintf("\tD3DVSD_TESSUV(%s),\n", XboxVertexRegisterAsString(VertexRegisterOut)); - } else { // D3DVSD_TESSNORMAL - DWORD VertexRegisterIn = VshGetVertexRegisterIn(*pXboxToken); - DbgVshPrintf("\tD3DVSD_TESSNORMAL(%s, %s),\n", - XboxVertexRegisterAsString(VertexRegisterIn), - XboxVertexRegisterAsString(VertexRegisterOut)); - } - break; - } - case XTL::X_D3DVSD_TOKEN_CONSTMEM: { - DWORD ConstantAddress = (*pXboxToken & X_D3DVSD_CONSTADDRESSMASK) >> X_D3DVSD_CONSTADDRESSSHIFT; - DWORD Count = (*pXboxToken & X_D3DVSD_CONSTCOUNTMASK) >> X_D3DVSD_CONSTCOUNTSHIFT; - DbgVshPrintf("\tD3DVSD_CONST(%d, %d),\n", ConstantAddress, Count); - LOG_TEST_CASE("X_D3DVSD_TOKEN_CONSTMEM"); - Step = Count * 4 + 1; - break; - } - case XTL::X_D3DVSD_TOKEN_EXT: { - DWORD ExtInfo = (*pXboxToken & X_D3DVSD_EXTINFOMASK) >> X_D3DVSD_EXTINFOSHIFT; - DWORD Count = (*pXboxToken & X_D3DVSD_EXTCOUNTMASK) >> X_D3DVSD_EXTCOUNTSHIFT; - DbgVshPrintf("\tD3DVSD_EXT(%d, %d),\n", ExtInfo, Count); - LOG_TEST_CASE("X_D3DVSD_TOKEN_EXT"); - Step = Count * 4 + 1; // TODO : Is this correct? - break; - } - default: - DbgVshPrintf("Unknown token type: %d\n", VshGetTokenType(*pXboxToken)); - break; - } - - pXboxToken += Step; - } - - if (iNumberOfVertexStreams > 0) { - DbgVshPrintf("\t// NeedPatching: %d\n", bStreamNeedsPatching); - } - - DbgVshPrintf("\tD3DVSD_END()\n};\n"); - - DbgVshPrintf("// NbrStreams: %d\n", iNumberOfVertexStreams); - } - - void VshConvertToken_NOP(DWORD *pXboxToken) - { - if(*pXboxToken != X_D3DVSD_NOP()) - { - LOG_TEST_CASE("Token NOP found, but extra parameters are given!"); - } - } - - DWORD VshConvertToken_CONSTMEM(DWORD *pXboxToken) - { - // DWORD ConstantAddress = (*pXboxToken & X_D3DVSD_CONSTADDRESSMASK) >> X_D3DVSD_CONSTADDRESSSHIFT; - DWORD Count = (*pXboxToken & X_D3DVSD_CONSTCOUNTMASK) >> X_D3DVSD_CONSTCOUNTSHIFT; - LOG_TEST_CASE("CONST"); // TODO : Implement - return Count * 4 + 1; - } - - void VshConvertToken_TESSELATOR(DWORD *pXboxToken) - { - BYTE Index; - - if(*pXboxToken & X_D3DVSD_MASK_TESSUV) - { - DWORD VertexRegister = VshGetVertexRegister(*pXboxToken); - DWORD NewVertexRegister = VertexRegister; - - NewVertexRegister = Xb2PCRegisterType(VertexRegister, Index); - // TODO : Expand on the setting of this TESSUV register element : - pRecompiled->Usage = D3DDECLUSAGE(NewVertexRegister); - pRecompiled->UsageIndex = Index; - } - else // D3DVSD_TESSNORMAL - { - DWORD VertexRegisterIn = VshGetVertexRegisterIn(*pXboxToken); - DWORD VertexRegisterOut = VshGetVertexRegister(*pXboxToken); - - DWORD NewVertexRegisterIn = VertexRegisterIn; - DWORD NewVertexRegisterOut = VertexRegisterOut; - - NewVertexRegisterIn = Xb2PCRegisterType(VertexRegisterIn, Index); - // TODO : Expand on the setting of this TESSNORMAL input register element : - pRecompiled->Usage = D3DDECLUSAGE(NewVertexRegisterIn); - pRecompiled->UsageIndex = Index; - - NewVertexRegisterOut = Xb2PCRegisterType(VertexRegisterOut, Index); - // TODO : Expand on the setting of this TESSNORMAL output register element : - pRecompiled++; - pRecompiled->Usage = D3DDECLUSAGE(NewVertexRegisterOut); - pRecompiled->UsageIndex = Index; - } - } - - void VshConvertToken_STREAM(DWORD *pXboxToken) - { - // D3DVSD_STREAM_TESS - if(*pXboxToken & X_D3DVSD_STREAMTESSMASK) - { - // TODO - } - else // D3DVSD_STREAM - { - DWORD StreamNumber = VshGetVertexStream(*pXboxToken); - - // new stream - pCurrentVertexShaderStreamInfo = &(pVertexShaderInfoToSet->VertexStreams[StreamNumber]); - pCurrentVertexShaderStreamInfo->NeedPatch = FALSE; - pCurrentVertexShaderStreamInfo->DeclPosition = FALSE; - pCurrentVertexShaderStreamInfo->CurrentStreamNumber = 0; - pCurrentVertexShaderStreamInfo->HostVertexStride = 0; - pCurrentVertexShaderStreamInfo->NumberOfVertexElements = 0; - - // Dxbx note : Use Dophin(s), FieldRender, MatrixPaletteSkinning and PersistDisplay as a testcase - - pCurrentVertexShaderStreamInfo->CurrentStreamNumber = VshGetVertexStream(*pXboxToken); - pVertexShaderInfoToSet->NumberOfVertexStreams++; - // TODO : Keep a bitmask for all StreamNumber's seen? - } - } - - void VshConvert_RegisterVertexElement( - UINT XboxVertexElementDataType, - UINT XboxVertexElementByteSize, - UINT HostVertexElementByteSize, - BOOL NeedPatching) - { - CxbxVertexShaderStreamElement* pCurrentElement = &(pCurrentVertexShaderStreamInfo->VertexElements[pCurrentVertexShaderStreamInfo->NumberOfVertexElements]); - pCurrentElement->XboxType = XboxVertexElementDataType; - pCurrentElement->XboxByteSize = XboxVertexElementByteSize; - pCurrentElement->HostByteSize = HostVertexElementByteSize; - pCurrentVertexShaderStreamInfo->NumberOfVertexElements++; - pCurrentVertexShaderStreamInfo->NeedPatch |= NeedPatching; - } - - void VshConvert_SkipBytes(int SkipBytesCount) - { - if (SkipBytesCount % sizeof(DWORD)) { - LOG_TEST_CASE("D3DVSD_SKIPBYTES not divisble by 4!"); - } -#if 0 // Potential optimization, for now disabled for simplicity : - else { - // Skip size is a whole multiple of 4 bytes; - // Is stream patching not needed up until this element? - if (!pCurrentVertexShaderStreamInfo->NeedPatch) { - // Then we can get away with increasing the host stride, - // which avoids otherwise needless vertex buffer patching : - pCurrentVertexShaderStreamInfo->HostVertexStride += SkipBytesCount; - return; - } - } -#endif - - // Register a 'skip' element, so that Xbox data will be skipped - // without increasing host stride - this does require patching : - VshConvert_RegisterVertexElement(XTL::X_D3DVSDT_NONE, SkipBytesCount, /*HostSize=*/0, /*NeedPatching=*/TRUE); - } - - void VshConvertToken_STREAMDATA_SKIP(DWORD *pXboxToken) - { - WORD SkipCount = (*pXboxToken & X_D3DVSD_SKIPCOUNTMASK) >> X_D3DVSD_SKIPCOUNTSHIFT; - VshConvert_SkipBytes(SkipCount * sizeof(DWORD)); - } - - void VshConvertToken_STREAMDATA_SKIPBYTES(DWORD* pXboxToken) - { - WORD SkipBytesCount = (*pXboxToken & X_D3DVSD_SKIPCOUNTMASK) >> X_D3DVSD_SKIPCOUNTSHIFT; - VshConvert_SkipBytes(SkipBytesCount); - } - - void VshConvertToken_STREAMDATA_REG(DWORD *pXboxToken) - { - DWORD VertexRegister = VshGetVertexRegister(*pXboxToken); - BOOL NeedPatching = FALSE; - BYTE Index; - BYTE HostVertexRegisterType; - - if (IsFixedFunction) { - HostVertexRegisterType = Xb2PCRegisterType(VertexRegister, Index); - } else { - // D3DDECLUSAGE_TEXCOORD can be useds for any user-defined data - // We need this because there is no reliable way to detect the real usage - // Xbox has no concept of 'usage types', it only requires a list of attribute register numbers. - // So we treat them all as 'user-defined' with an Index of the Vertex Register Index - // this prevents information loss in shaders due to non-matching dcl types! - HostVertexRegisterType = D3DDECLUSAGE_TEXCOORD; - Index = (BYTE)VertexRegister; - } - - // Add this register to the list of declared registers - RegVIsPresentInDeclaration[VertexRegister] = true; - - DWORD XboxVertexElementDataType = (*pXboxToken & X_D3DVSD_DATATYPEMASK) >> X_D3DVSD_DATATYPESHIFT; - WORD XboxVertexElementByteSize = 0; - BYTE HostVertexElementDataType = 0; - WORD HostVertexElementByteSize = 0; - - switch (XboxVertexElementDataType) - { - case XTL::X_D3DVSDT_FLOAT1: // 0x12: - HostVertexElementDataType = D3DDECLTYPE_FLOAT1; - HostVertexElementByteSize = 1 * sizeof(FLOAT); - break; - case XTL::X_D3DVSDT_FLOAT2: // 0x22: - HostVertexElementDataType = D3DDECLTYPE_FLOAT2; - HostVertexElementByteSize = 2 * sizeof(FLOAT); - break; - case XTL::X_D3DVSDT_FLOAT3: // 0x32: - HostVertexElementDataType = D3DDECLTYPE_FLOAT3; - HostVertexElementByteSize = 3 * sizeof(FLOAT); - break; - case XTL::X_D3DVSDT_FLOAT4: // 0x42: - HostVertexElementDataType = D3DDECLTYPE_FLOAT4; - HostVertexElementByteSize = 4 * sizeof(FLOAT); - break; - case XTL::X_D3DVSDT_D3DCOLOR: // 0x40: - HostVertexElementDataType = D3DDECLTYPE_D3DCOLOR; - HostVertexElementByteSize = 1 * sizeof(D3DCOLOR); - break; - case XTL::X_D3DVSDT_SHORT2: // 0x25: - HostVertexElementDataType = D3DDECLTYPE_SHORT2; - HostVertexElementByteSize = 2 * sizeof(SHORT); - break; - case XTL::X_D3DVSDT_SHORT4: // 0x45: - HostVertexElementDataType = D3DDECLTYPE_SHORT4; - HostVertexElementByteSize = 4 * sizeof(SHORT); - break; - case XTL::X_D3DVSDT_NORMSHORT1: // 0x11: - if (g_D3DCaps.DeclTypes & D3DDTCAPS_SHORT2N) { - HostVertexElementDataType = D3DDECLTYPE_SHORT2N; - HostVertexElementByteSize = 2 * sizeof(SHORT); - } - else - { - HostVertexElementDataType = D3DDECLTYPE_FLOAT1; - HostVertexElementByteSize = 1 * sizeof(FLOAT); - } - XboxVertexElementByteSize = 1 * sizeof(XTL::SHORT); - NeedPatching = TRUE; - break; - case XTL::X_D3DVSDT_NORMSHORT2: // 0x21: - if (g_D3DCaps.DeclTypes & D3DDTCAPS_SHORT2N) { - HostVertexElementDataType = D3DDECLTYPE_SHORT2N; - HostVertexElementByteSize = 2 * sizeof(SHORT); - // No need for patching in D3D9 - } - else - { - HostVertexElementDataType = D3DDECLTYPE_FLOAT2; - HostVertexElementByteSize = 2 * sizeof(FLOAT); - XboxVertexElementByteSize = 2 * sizeof(XTL::SHORT); - NeedPatching = TRUE; - } - break; - case XTL::X_D3DVSDT_NORMSHORT3: // 0x31: - if (g_D3DCaps.DeclTypes & D3DDTCAPS_SHORT4N) { - HostVertexElementDataType = D3DDECLTYPE_SHORT4N; - HostVertexElementByteSize = 4 * sizeof(SHORT); - } - else - { - HostVertexElementDataType = D3DDECLTYPE_FLOAT3; - HostVertexElementByteSize = 3 * sizeof(FLOAT); - } - XboxVertexElementByteSize = 3 * sizeof(XTL::SHORT); - NeedPatching = TRUE; - break; - case XTL::X_D3DVSDT_NORMSHORT4: // 0x41: - if (g_D3DCaps.DeclTypes & D3DDTCAPS_SHORT4N) { - HostVertexElementDataType = D3DDECLTYPE_SHORT4N; - HostVertexElementByteSize = 4 * sizeof(SHORT); - // No need for patching in D3D9 - } - else - { - HostVertexElementDataType = D3DDECLTYPE_FLOAT4; - HostVertexElementByteSize = 4 * sizeof(FLOAT); - XboxVertexElementByteSize = 4 * sizeof(XTL::SHORT); - NeedPatching = TRUE; - } - break; - case XTL::X_D3DVSDT_NORMPACKED3: // 0x16: - HostVertexElementDataType = D3DDECLTYPE_FLOAT3; - HostVertexElementByteSize = 3 * sizeof(FLOAT); - XboxVertexElementByteSize = 1 * sizeof(XTL::DWORD); - NeedPatching = TRUE; - break; - case XTL::X_D3DVSDT_SHORT1: // 0x15: - HostVertexElementDataType = D3DDECLTYPE_SHORT2; - HostVertexElementByteSize = 2 * sizeof(SHORT); - XboxVertexElementByteSize = 1 * sizeof(XTL::SHORT); - NeedPatching = TRUE; - break; - case XTL::X_D3DVSDT_SHORT3: // 0x35: - HostVertexElementDataType = D3DDECLTYPE_SHORT4; - HostVertexElementByteSize = 4 * sizeof(SHORT); - XboxVertexElementByteSize = 3 * sizeof(XTL::SHORT); - NeedPatching = TRUE; - break; - case XTL::X_D3DVSDT_PBYTE1: // 0x14: - if (g_D3DCaps.DeclTypes & D3DDTCAPS_UBYTE4N) { - HostVertexElementDataType = D3DDECLTYPE_UBYTE4N; - HostVertexElementByteSize = 4 * sizeof(BYTE); - } - else - { - HostVertexElementDataType = D3DDECLTYPE_FLOAT1; - HostVertexElementByteSize = 1 * sizeof(FLOAT); - } - XboxVertexElementByteSize = 1 * sizeof(XTL::BYTE); - NeedPatching = TRUE; - break; - case XTL::X_D3DVSDT_PBYTE2: // 0x24: - if (g_D3DCaps.DeclTypes & D3DDTCAPS_UBYTE4N) { - HostVertexElementDataType = D3DDECLTYPE_UBYTE4N; - HostVertexElementByteSize = 4 * sizeof(BYTE); - } - else - { - HostVertexElementDataType = D3DDECLTYPE_FLOAT2; - HostVertexElementByteSize = 2 * sizeof(FLOAT); - } - XboxVertexElementByteSize = 2 * sizeof(XTL::BYTE); - NeedPatching = TRUE; - break; - case XTL::X_D3DVSDT_PBYTE3: // 0x34: - if (g_D3DCaps.DeclTypes & D3DDTCAPS_UBYTE4N) { - HostVertexElementDataType = D3DDECLTYPE_UBYTE4N; - HostVertexElementByteSize = 4 * sizeof(BYTE); - } - else - { - HostVertexElementDataType = D3DDECLTYPE_FLOAT3; - HostVertexElementByteSize = 3 * sizeof(FLOAT); - } - XboxVertexElementByteSize = 3 * sizeof(XTL::BYTE); - NeedPatching = TRUE; - break; - case XTL::X_D3DVSDT_PBYTE4: // 0x44: - // Test-case : Panzer - if (g_D3DCaps.DeclTypes & D3DDTCAPS_UBYTE4N) { - HostVertexElementDataType = D3DDECLTYPE_UBYTE4N; - HostVertexElementByteSize = 4 * sizeof(BYTE); - // No need for patching when D3D9 supports D3DDECLTYPE_UBYTE4N - } - else - { - HostVertexElementDataType = D3DDECLTYPE_FLOAT4; - HostVertexElementByteSize = 4 * sizeof(FLOAT); - XboxVertexElementByteSize = 4 * sizeof(XTL::BYTE); - NeedPatching = TRUE; - } - break; - case XTL::X_D3DVSDT_FLOAT2H: // 0x72: - HostVertexElementDataType = D3DDECLTYPE_FLOAT4; - HostVertexElementByteSize = 4 * sizeof(FLOAT); - XboxVertexElementByteSize = 3 * sizeof(FLOAT); - NeedPatching = TRUE; - break; - case XTL::X_D3DVSDT_NONE: // 0x02: - // No host element data, so no patching - break; - default: - //LOG_TEST_CASE("Unknown data type for D3DVSD_REG: 0x%02X\n", XboxVertexElementDataType); - break; - } - - // On X_D3DVSDT_NONE skip this token - if (XboxVertexElementDataType == XTL::X_D3DVSDT_NONE) - { - // Xbox elements with X_D3DVSDT_NONE have size zero, so there's no need to register those. - // Note, that for skip tokens, we DO call VshConvert_RegisterVertexElement with a X_D3DVSDT_NONE! - return; - } - - // save patching information - VshConvert_RegisterVertexElement( - XboxVertexElementDataType, - NeedPatching ? XboxVertexElementByteSize : HostVertexElementByteSize, - HostVertexElementByteSize, - NeedPatching); - - pRecompiled->Stream = pCurrentVertexShaderStreamInfo->CurrentStreamNumber; - pRecompiled->Offset = pCurrentVertexShaderStreamInfo->HostVertexStride; - pRecompiled->Type = HostVertexElementDataType; - pRecompiled->Method = D3DDECLMETHOD_DEFAULT; - pRecompiled->Usage = HostVertexRegisterType; - pRecompiled->UsageIndex = Index; - - pRecompiled++; - - pCurrentVertexShaderStreamInfo->HostVertexStride += HostVertexElementByteSize; - } - - void VshConvertToken_STREAMDATA(DWORD *pXboxToken) - { - if (*pXboxToken & X_D3DVSD_MASK_SKIP) - { - // For D3D9, use D3DDECLTYPE_UNUSED ? - if (*pXboxToken & X_D3DVSD_MASK_SKIPBYTES) { - VshConvertToken_STREAMDATA_SKIPBYTES(pXboxToken); - } else { - VshConvertToken_STREAMDATA_SKIP(pXboxToken); - } - } - else // D3DVSD_REG - { - VshConvertToken_STREAMDATA_REG(pXboxToken); - } - } - - DWORD VshRecompileToken(DWORD *pXboxToken) - { - DWORD Step = 1; - - switch(VshGetTokenType(*pXboxToken)) - { - case XTL::X_D3DVSD_TOKEN_NOP: - VshConvertToken_NOP(pXboxToken); - break; - case XTL::X_D3DVSD_TOKEN_STREAM: - { - VshConvertToken_STREAM(pXboxToken); - break; - } - case XTL::X_D3DVSD_TOKEN_STREAMDATA: - { - VshConvertToken_STREAMDATA(pXboxToken); - break; - } - case XTL::X_D3DVSD_TOKEN_TESSELLATOR: - { - VshConvertToken_TESSELATOR(pXboxToken); - break; - } - case XTL::X_D3DVSD_TOKEN_CONSTMEM: - { - Step = VshConvertToken_CONSTMEM(pXboxToken); - break; - } - default: - //LOG_TEST_CASE("Unknown token type: %d\n", VshGetTokenType(*pXboxToken)); - break; - } - - return Step; - } - - DWORD* RemoveXboxDeclarationRedefinition(DWORD* pXboxDeclaration) - { - // Detect and remove register redefinitions by preprocessing the Xbox Vertex Declaration - // Test Case: King Kong - - // Find the last token - DWORD* pXboxToken = pXboxDeclaration; - while (*pXboxToken != X_D3DVSD_END()){ - pXboxToken++; - } - - // Operate on a copy of the Xbox declaration, rather than messing with the Xbox's memory - auto declarationBytes = sizeof(DWORD) * (pXboxToken - pXboxDeclaration + 1); - auto pXboxDeclarationCopy = (DWORD*)malloc(declarationBytes); - memcpy(pXboxDeclarationCopy, pXboxDeclaration, declarationBytes); - pXboxToken = pXboxDeclarationCopy + (pXboxToken - pXboxDeclaration); // Move to end of the copy - - // Remember if we've seen a given output register - std::bitset<16> seen; - - // We want to keep later definitions, and remove earlier ones - // Scan back from the end of the declaration, and replace redefinitions with nops - while (pXboxToken > pXboxDeclarationCopy) { - auto type = VshGetTokenType(*pXboxToken); - if (type == XTL::X_D3DVSD_TOKEN_STREAMDATA && !(*pXboxToken & X_D3DVSD_MASK_SKIP) || - type == XTL::X_D3DVSD_TOKEN_TESSELLATOR) - { - auto outputRegister = VshGetVertexRegister(*pXboxToken); - if (seen[outputRegister]) - { - // Blank out tokens for mapped registers - *pXboxToken = X_D3DVSD_NOP(); - EmuLog(LOG_LEVEL::DEBUG, "Replacing duplicate definition of register %d with D3DVSD_NOP", outputRegister); - } - else - { - // Mark register as seen - seen[outputRegister] = true; - } - } - - pXboxToken--; - } - - return pXboxDeclarationCopy; - } - -public: - D3DVERTEXELEMENT *Convert(DWORD* pXboxDeclaration, bool bIsFixedFunction, CxbxVertexShaderInfo* pCxbxVertexShaderInfo) - { - // Get a preprocessed copy of the original Xbox Vertex Declaration - auto pXboxVertexDeclarationCopy = RemoveXboxDeclarationRedefinition(pXboxDeclaration); - - pVertexShaderInfoToSet = pCxbxVertexShaderInfo; - hostTemporaryRegisterCount = g_D3DCaps.VS20Caps.NumTemps; - if (hostTemporaryRegisterCount < VSH_MIN_TEMPORARY_REGISTERS) { - LOG_TEST_CASE("g_D3DCaps.VS20Caps.NumTemps < 12 (Host minimal vertex shader temporary register count)"); - } - if (hostTemporaryRegisterCount < 12+1) { // TODO : Use a constant (see X_D3DVSD_REG) - LOG_TEST_CASE("g_D3DCaps.VS20Caps.NumTemps < 12+1 (Xbox vertex shader temporary register count + r12, reading oPos)"); - } - - // Note, that some Direct3D 9 drivers return only the required minimum temporary register count of 12, - // but regardless, shaders that use temporary register numbers above r12 still seem to work correctly. - // So it seems we can't rely on VS20Caps.NumTemps indicating accurately what host hardware supports. - // (Although it could be that the driver switches to software vertex processing when a shader exceeds hardware limits.) - - IsFixedFunction = bIsFixedFunction; - - RegVIsPresentInDeclaration.fill(false); - - // First of all some info: - // We have to figure out which flags are set and then - // we have to patch their params - - // some token values - // 0xFFFFFFFF - end of the declaration - // 0x00000000 - nop (means that this value is ignored) - - // Calculate size of declaration - XboxDeclarationCount = VshGetDeclarationCount(pXboxVertexDeclarationCopy); - // For Direct3D9, we need to reserve at least twice the number of elements, as one token can generate two registers (in and out) : - HostDeclarationSize = XboxDeclarationCount * sizeof(D3DVERTEXELEMENT) * 2; - - D3DVERTEXELEMENT *Result = (D3DVERTEXELEMENT *)calloc(1, HostDeclarationSize); - pRecompiled = Result; - uint8_t *pRecompiledBufferOverflow = ((uint8_t*)pRecompiled) + HostDeclarationSize; - - VshDumpXboxDeclaration(pXboxDeclaration); - - auto pXboxToken = pXboxVertexDeclarationCopy; - while (*pXboxToken != X_D3DVSD_END()) - { - if ((uint8_t*)pRecompiled >= pRecompiledBufferOverflow) { - DbgVshPrintf("Detected buffer-overflow, breaking out...\n"); - break; - } - - DWORD Step = VshRecompileToken(pXboxToken); - pXboxToken += Step; - } - - *pRecompiled = D3DDECL_END(); - - // Ensure valid ordering of the vertex declaration (http://doc.51windows.net/Directx9_SDK/graphics/programmingguide/gettingstarted/vertexdeclaration/vertexdeclaration.htm) - // In particular "All vertex elements for a stream must be consecutive and sorted by offset" - // Test case: King Kong (due to register redefinition) - std::sort(Result, pRecompiled, [] (const auto& x, const auto& y) - { return std::tie(x.Stream, x.Method, x.Offset) < std::tie(y.Stream, y.Method, y.Offset); }); - - // Free the preprocessed declaration copy - free(pXboxVertexDeclarationCopy); - - return Result; - } -}; - -D3DVERTEXELEMENT *EmuRecompileVshDeclaration -( - DWORD *pXboxDeclaration, - bool bIsFixedFunction, - DWORD *pXboxDeclarationCount, - DWORD *pHostDeclarationSize, - CxbxVertexShaderInfo *pCxbxVertexShaderInfo -) -{ - XboxVertexDeclarationConverter Converter; - - D3DVERTEXELEMENT* pHostVertexElements = Converter.Convert(pXboxDeclaration, bIsFixedFunction, pCxbxVertexShaderInfo); - - *pXboxDeclarationCount = Converter.XboxDeclarationCount; - *pHostDeclarationSize = Converter.HostDeclarationSize; - - return pHostVertexElements; -} - -std::string UsingScratch(std::string input) { - return std::regex_replace(input, std::regex("tmp"), "r" + std::to_string(X_VSH_TEMP_SCRATCH)); -} - -// Xbox expp seems to behave the as vs_1_1 -std::string VshPostProcess_Expp(std::string shader) { - // Find usages of exp with each swizzle - // If there's no swizzle, we should still match so we do the calculation - // for all components - const auto xbox_expp_x = std::regex("expp (\\w\\d\\d?)(\\.x)?, (.+)$"); - const auto xbox_expp_y = std::regex("expp (\\w\\d\\d?)(\\.y)?, (.+)$"); - const auto xbox_expp_z = std::regex("expp (\\w\\d\\d?)(\\.z)?, (.+)$"); - const auto xbox_expp_w = std::regex("expp (\\w\\d\\d?)(\\.w)?, (.+)$"); - - // We operate on a scalar so the input should have a swizzle? - - if (std::regex_search(shader, xbox_expp_x)) - LOG_TEST_CASE("Title uses the x component result of expp"); - if (std::regex_search(shader, xbox_expp_w)) - LOG_TEST_CASE("Title uses the w component result of expp"); - - // dest.x = 2 ^ floor(x) - // Test Case: ??? - static auto host_expp_x = UsingScratch( - "; patch expp: dest.x = 2 ^ floor(x)\n" - "frc tmp.x, $3\n" - "add tmp.x, $1$2, -tmp.x\n" - "exp $1.x, tmp.x"); - shader = std::regex_replace(shader, xbox_expp_x, host_expp_x); - - // dest.y = x - floor(x) - // Test Case: Tony Hawk Pro Skater 2X - const auto host_expp_y = - "; patch expp: dest.y = x - floor(x)\n" - "frc $1.y, $3"; - shader = std::regex_replace(shader, xbox_expp_y, host_expp_y); - - // dest.z = approximate 2 ^ x - // Test Case: Mechassault - const auto host_expp_z = - "; patch expp: dest.z = 2 ^ x\n" - "exp $1.z, $3"; - shader = std::regex_replace(shader, xbox_expp_z, host_expp_z); - - // dest.w = 1 - // Test Case: ??? - // TODO do a constant read here - const auto host_expp_w = UsingScratch( - "; patch expp: dest.w = 1\n" - "sub tmp.x, tmp.x, tmp.x\n" // Get 0 - "exp $1.w, tmp.x"); // 2 ^ 0 = 1 - shader = std::regex_replace(shader, xbox_expp_w, host_expp_w); - - return shader; -} - -// On Xbox, the special indexing register, a0.x, is truncated -// But on vs_2_x and up, it's rounded to the closest integer -// So we have to truncate it ourselves -// Test Case: Buffy the Vampire Slayer -std::string VshPostProcess_TruncateMovA(std::string shader) { - // find usages of mova - const auto movA = std::regex("mova a0\\.x, (.*)$"); - // The equivalent of floor() with a temp register - // and use the floored value - static auto truncate = UsingScratch( - "; patch mova: a = floor(x)\n" - "frc tmp, $1\n" - "add tmp, $1, -tmp\n" - "mova a0.x, tmp"); - return std::regex_replace(shader, movA, truncate); -} - -// Post process the shader as a string -std::string VshPostProcess(std::string shader) { - shader = VshPostProcess_Expp(shader); - return VshPostProcess_TruncateMovA(shader); -} - -// recompile xbox vertex shader function -extern HRESULT EmuRecompileVshFunction -( - DWORD *pXboxFunction, - bool bNoReservedConstants, - D3DVERTEXELEMENT *pRecompiledDeclaration, - bool *pbUseDeclarationOnly, - DWORD *pXboxFunctionSize, - LPD3DXBUFFER *ppRecompiledShader -) -{ - XTL::X_VSH_SHADER_HEADER *pXboxVertexShaderHeader = (XTL::X_VSH_SHADER_HEADER*)pXboxFunction; - DWORD *pToken; - boolean EOI = false; - VSH_XBOX_SHADER *pShader = (VSH_XBOX_SHADER*)calloc(1, sizeof(VSH_XBOX_SHADER)); - LPD3DXBUFFER pErrors = nullptr; - HRESULT hRet = 0; - - // TODO: support this situation.. - if(pXboxFunction == xbnullptr) - return E_FAIL; - - // Initialize output arguments to zero - *pbUseDeclarationOnly = 0; - *pXboxFunctionSize = 0; - *ppRecompiledShader = nullptr; - - if(!pShader) - { - EmuLog(LOG_LEVEL::WARNING, "Couldn't allocate memory for vertex shader conversion buffer"); - return E_OUTOFMEMORY; - } - pShader->ShaderHeader = *pXboxVertexShaderHeader; - switch(pXboxVertexShaderHeader->Version) - { - case VERSION_XVS: - break; - case VERSION_XVSS: - EmuLog(LOG_LEVEL::WARNING, "Might not support vertex state shaders?"); - hRet = E_FAIL; - break; - case VERSION_XVSW: - EmuLog(LOG_LEVEL::WARNING, "Might not support vertex read/write shaders?"); - hRet = E_FAIL; - break; - default: - EmuLog(LOG_LEVEL::WARNING, "Unknown vertex shader version 0x%02X", pXboxVertexShaderHeader->Version); - hRet = E_FAIL; - break; - } - - if(SUCCEEDED(hRet)) - { - RegVIsUsedByShader.fill(false); - - for (pToken = (DWORD*)((uint8_t*)pXboxFunction + sizeof(XTL::X_VSH_SHADER_HEADER)); !EOI; pToken += X_VSH_INSTRUCTION_SIZE) - { - VSH_SHADER_INSTRUCTION Inst; - - VshParseInstruction((uint32_t*)pToken, &Inst); - VshConvertToIntermediate(&Inst, pShader); - EOI = (boolean)VshGetField((uint32_t*)pToken, FLD_FINAL); - } - - // The size of the shader is - *pXboxFunctionSize = (intptr_t)pToken - (intptr_t)pXboxFunction; - - // Do not attempt to compile empty shaders - if (pShader->IntermediateCount == 0) { - // This is a declaration only shader, so there is no function to recompile - *pbUseDeclarationOnly = 1; - return D3D_OK; - } - - std::stringstream& pXboxShaderDisassembly = std::stringstream(); - std::stringstream& pHostShaderDisassembly = std::stringstream(); - - DbgVshPrintf("-- Before conversion --\n"); - VshWriteShader(pShader, pXboxShaderDisassembly, pRecompiledDeclaration, FALSE); - DbgVshPrintf("%s", pXboxShaderDisassembly.str().c_str()); - DbgVshPrintf("-----------------------\n"); - - VshConvertShader(pShader, bNoReservedConstants); - VshWriteShader(pShader, pHostShaderDisassembly, pRecompiledDeclaration, TRUE); - - // Post process the final shader - auto finalHostShader = VshPostProcess(pHostShaderDisassembly.str()); - - DbgVshPrintf("-- After conversion ---\n"); - DbgVshPrintf("%s", finalHostShader.c_str()); - DbgVshPrintf("-----------------------\n"); - - // HACK: Azurik. Prevent Direct3D from trying to assemble this. - if(finalHostShader == "vs.2.x\n") - { - EmuLog(LOG_LEVEL::WARNING, "Replacing empty vertex shader with fallback"); - - static const char dummy[] = - "vs.2.x\n" - "dcl_position v0\n" - "dp4 oPos.x, v0, c96\n" - "dp4 oPos.y, v0, c97\n" - "dp4 oPos.z, v0, c98\n" - "dp4 oPos.w, v0, c99\n"; - - hRet = D3DXAssembleShader( - dummy, - strlen(dummy), - /*pDefines=*/nullptr, - /*pInclude=*/nullptr, - /*Flags=*/0, // Was D3DXASM_SKIPVALIDATION, - /*ppCompiledShader=*/ppRecompiledShader, - /*ppCompilationErrors*/nullptr); - } - else - { - hRet = D3DXAssembleShader( - finalHostShader.c_str(), - finalHostShader.length(), - /*pDefines=*/nullptr, - /*pInclude=*/nullptr, - /*Flags=*/0, // Was D3DXASM_SKIPVALIDATION, - /*ppCompiledShader=*/ppRecompiledShader, - /*ppCompilationErrors*/&pErrors); - } - - if (FAILED(hRet)) - { - EmuLog(LOG_LEVEL::WARNING, "Couldn't assemble recompiled vertex shader"); - EmuLog(LOG_LEVEL::WARNING, "%s", pErrors->GetBufferPointer()); - } - - if( pErrors ) - pErrors->Release(); - } - - free(pShader); - - return hRet; -} - -extern void FreeVertexDynamicPatch(CxbxVertexShader *pVertexShader) -{ - pVertexShader->VertexShaderInfo.NumberOfVertexStreams = 0; -} - -// Checks for failed vertex shaders, and shaders that would need patching -boolean VshHandleIsValidShader(DWORD XboxVertexShaderHandle) -{ -#if 0 - //printf( "VS = 0x%.08X\n", XboxVertexShaderHandle ); - - CxbxVertexShader *pCxbxVertexShader = GetCxbxVertexShader(XboxVertexShaderHandle); - if (pCxbxVertexShader) { - if (pCxbxVertexShader->XboxStatus != 0) - { - return FALSE; - } - /* - for (uint32 i = 0; i < pCxbxVertexShader->VertexShaderInfo.NumberOfVertexStreams; i++) - { - if (pCxbxVertexShader->VertexShaderInfo.VertexStreams[i].NeedPatch) - { - // Just for caching purposes - pCxbxVertexShader->XboxStatus = 0x80000001; - return FALSE; - } - } - */ - } -#endif - return TRUE; -} - -extern boolean IsValidCurrentShader(void) -{ - // Dxbx addition : There's no need to call - // XTL_EmuIDirect3DDevice_GetVertexShader, just check g_Xbox_VertexShader_Handle : - return VshHandleIsValidShader(g_Xbox_VertexShader_Handle); -} - -CxbxVertexShaderInfo *GetCxbxVertexShaderInfo(DWORD XboxVertexShaderHandle) -{ - CxbxVertexShader *pCxbxVertexShader = GetCxbxVertexShader(XboxVertexShaderHandle); - - for (uint32_t i = 0; i < pCxbxVertexShader->VertexShaderInfo.NumberOfVertexStreams; i++) - { - if (pCxbxVertexShader->VertexShaderInfo.VertexStreams[i].NeedPatch) - { - return &pCxbxVertexShader->VertexShaderInfo; - } - } - return nullptr; -} - -std::unordered_map g_CxbxVertexShaders; - -CxbxVertexShader* GetCxbxVertexShader(DWORD XboxVertexShaderHandle) -{ - if (VshHandleIsVertexShader(XboxVertexShaderHandle)) { - auto it = g_CxbxVertexShaders.find(XboxVertexShaderHandle); - if (it != g_CxbxVertexShaders.end()) { - return it->second; - } - } - - return nullptr; -} - -void SetCxbxVertexShader(DWORD XboxVertexShaderHandle, CxbxVertexShader* shader) -{ - auto it = g_CxbxVertexShaders.find(XboxVertexShaderHandle); - if (it != g_CxbxVertexShaders.end() && it->second != nullptr && shader != nullptr) { - LOG_TEST_CASE("Overwriting existing Vertex Shader"); - } - - g_CxbxVertexShaders[XboxVertexShaderHandle] = shader; -} +// This is an open source non-commercial project. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com +// ****************************************************************** +// * +// * This file is part of the Cxbx project. +// * +// * Cxbx and Cxbe are free software; you can redistribute them +// * and/or modify them under the terms of the GNU General Public +// * License as published by the Free Software Foundation; either +// * version 2 of the license, or (at your option) any later version. +// * +// * This program is distributed in the hope that it will be useful, +// * but WITHOUT ANY WARRANTY; without even the implied warranty of +// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// * GNU General Public License for more details. +// * +// * You should have recieved a copy of the GNU General Public License +// * along with this program; see the file COPYING. +// * If not, write to the Free Software Foundation, Inc., +// * 59 Temple Place - Suite 330, Bostom, MA 02111-1307, USA. +// * +// * (c) 2002-2004 Aaron Robinson +// * Kingofc +// * +// * All rights reserved +// * +// ****************************************************************** +#define LOG_PREFIX CXBXR_MODULE::VTXSH + +#define _DEBUG_TRACK_VS + +#include "core\kernel\init\CxbxKrnl.h" +#include "core\kernel\support\Emu.h" +#include "core\hle\D3D8\Direct3D9\Direct3D9.h" // For g_Xbox_VertexShader_Handle +#include "core\hle\D3D8\XbVertexShader.h" + +#include "XbD3D8Types.h" // For X_D3DVSDE_* +#include +#include +#include +#include +#include + +//#define CXBX_USE_VS30 // Separate the port to Vertex Shader model 3.0 from the port to Direct3D9 +#ifdef CXBX_USE_VS30 + #define VSH_MAX_INSTRUCTION_COUNT VSH_VS30_MAX_INSTRUCTION_COUNT // == 512 +#else + #define VSH_MAX_INSTRUCTION_COUNT VSH_VS2X_MAX_INSTRUCTION_COUNT // == 256 +#endif + +// Internal Vertex Shader version (mustn't conflict with any VERSION_XVS*) +#define VERSION_CXBX 0x7863 // 'cx' Cxbx vertex shader, not an official value, used in VshConvertShader() and VshWriteShader() + +#define DbgVshPrintf \ + LOG_CHECK_ENABLED(LOG_LEVEL::DEBUG) \ + if(g_bPrintfOn) printf + +// **************************************************************************** +// * Vertex shader function recompiler +// **************************************************************************** + +typedef enum _VSH_SWIZZLE +{ + SWIZZLE_X = 0, + SWIZZLE_Y, + SWIZZLE_Z, + SWIZZLE_W +} +VSH_SWIZZLE; + +typedef struct DxbxSwizzles { VSH_SWIZZLE s[4]; } DxbxSwizzles; + +typedef DWORD DxbxMask, +*PDxbxMask; + +#define MASK_X 0x001 +#define MASK_Y 0x002 +#define MASK_Z 0x004 +#define MASK_W 0x008 +#define MASK_XYZ MASK_X | MASK_Y | MASK_Z +#define MASK_XYZW MASK_X | MASK_Y | MASK_Z | MASK_W + +// Local types +typedef enum _VSH_FIELD_NAME +{ + FLD_ILU = 0, + FLD_MAC, + FLD_CONST, + FLD_V, + // Input A + FLD_A_NEG, + FLD_A_SWZ_X, + FLD_A_SWZ_Y, + FLD_A_SWZ_Z, + FLD_A_SWZ_W, + FLD_A_R, + FLD_A_MUX, + // Input B + FLD_B_NEG, + FLD_B_SWZ_X, + FLD_B_SWZ_Y, + FLD_B_SWZ_Z, + FLD_B_SWZ_W, + FLD_B_R, + FLD_B_MUX, + // Input C + FLD_C_NEG, + FLD_C_SWZ_X, + FLD_C_SWZ_Y, + FLD_C_SWZ_Z, + FLD_C_SWZ_W, + FLD_C_R_HIGH, + FLD_C_R_LOW, + FLD_C_MUX, + // Output + FLD_OUT_MAC_MASK_X, + FLD_OUT_MAC_MASK_Y, + FLD_OUT_MAC_MASK_Z, + FLD_OUT_MAC_MASK_W, + FLD_OUT_R, + FLD_OUT_ILU_MASK_X, + FLD_OUT_ILU_MASK_Y, + FLD_OUT_ILU_MASK_Z, + FLD_OUT_ILU_MASK_W, + FLD_OUT_O_MASK_X, + FLD_OUT_O_MASK_Y, + FLD_OUT_O_MASK_Z, + FLD_OUT_O_MASK_W, + FLD_OUT_ORB, + FLD_OUT_ADDRESS, + FLD_OUT_MUX, + // Relative addressing + FLD_A0X, + // Final instruction + FLD_FINAL +} +VSH_FIELD_NAME; + +typedef enum _VSH_OREG_NAME +{ + OREG_OPOS, // 0 + OREG_UNUSED1, // 1 + OREG_UNUSED2, // 2 + OREG_OD0, // 3 + OREG_OD1, // 4 + OREG_OFOG, // 5 + OREG_OPTS, // 6 + OREG_OB0, // 7 + OREG_OB1, // 8 + OREG_OT0, // 9 + OREG_OT1, // 10 + OREG_OT2, // 11 + OREG_OT3, // 12 + OREG_UNUSED3, // 13 + OREG_UNUSED4, // 14 + OREG_A0X // 15 - all values of the 4 bits are used +} +VSH_OREG_NAME; + +typedef enum _VSH_OUTPUT_TYPE +{ + OUTPUT_C = 0, + OUTPUT_O +} +VSH_OUTPUT_TYPE; + +typedef enum _VSH_ARGUMENT_TYPE +{ + PARAM_UNKNOWN = 0, + PARAM_R, // Temporary registers + PARAM_V, // Vertex registers + PARAM_C, // Constant registers, set by SetVertexShaderConstant + PARAM_O +} +VSH_ARGUMENT_TYPE; + +typedef VSH_ARGUMENT_TYPE VSH_PARAMETER_TYPE; // Alias, to indicate difference between a parameter and a generic argument + +typedef enum _VSH_OUTPUT_MUX +{ + OMUX_MAC = 0, + OMUX_ILU +} +VSH_OUTPUT_MUX; + +typedef enum _VSH_IMD_OUTPUT_TYPE +{ + IMD_OUTPUT_C, + IMD_OUTPUT_R, + IMD_OUTPUT_O, + IMD_OUTPUT_A0X +} +VSH_IMD_OUTPUT_TYPE; + +// Dxbx note : ILU stands for 'Inverse Logic Unit' opcodes +typedef enum _VSH_ILU +{ + ILU_NOP = 0, + ILU_MOV, + ILU_RCP, + ILU_RCC, + ILU_RSQ, + ILU_EXP, + ILU_LOG, + ILU_LIT // = 7 - all values of the 3 bits are used +} +VSH_ILU; + +// Dxbx note : MAC stands for 'Multiply And Accumulate' opcodes +typedef enum _VSH_MAC +{ + MAC_NOP = 0, + MAC_MOV, + MAC_MUL, + MAC_ADD, + MAC_MAD, + MAC_DP3, + MAC_DPH, + MAC_DP4, + MAC_DST, + MAC_MIN, + MAC_MAX, + MAC_SLT, + MAC_SGE, + MAC_ARL + // ??? 14 + // ??? 15 - 2 values of the 4 bits are undefined +} +VSH_MAC; + +typedef struct _VSH_OPCODE_PARAMS +{ + // Dxbx Note : Since we split up g_OpCodeParams into g_OpCodeParams_ILU and g_OpCodeParams_MAC + // the following two members aren't needed anymore : + // VSH_ILU ILU; + // VSH_MAC MAC; + boolean A; + boolean B; + boolean C; +} +VSH_OPCODE_PARAMS; + +typedef struct _VSH_PARAMETER +{ + VSH_PARAMETER_TYPE ParameterType; // Parameter type, R, V or C + boolean Neg; // TRUE if negated, FALSE if not + VSH_SWIZZLE Swizzle[4]; // The four swizzles + int16_t Address; // Register address +} +VSH_PARAMETER; + +typedef struct _VSH_OUTPUT +{ + // Output register + VSH_OUTPUT_MUX OutputMux; // MAC or ILU used as output + VSH_OUTPUT_TYPE OutputType; // C or O + boolean OutputMask[4]; + int16_t OutputAddress; + // MAC output R register + boolean MACRMask[4]; + boolean MACRAddress; + // ILU output R register + boolean ILURMask[4]; + boolean ILURAddress; +} +VSH_OUTPUT; + +// The raw, parsed shader instruction (can be many combined [paired] instructions) +typedef struct _VSH_SHADER_INSTRUCTION +{ + VSH_ILU ILU; + VSH_MAC MAC; + VSH_OUTPUT Output; + VSH_PARAMETER A; + VSH_PARAMETER B; + VSH_PARAMETER C; + boolean a0x; +} +VSH_SHADER_INSTRUCTION; + +typedef enum _VSH_IMD_INSTRUCTION_TYPE +{ + IMD_MAC, + IMD_ILU +} +VSH_IMD_INSTRUCTION_TYPE; + +typedef struct _VSH_IMD_OUTPUT +{ + VSH_IMD_OUTPUT_TYPE Type; + boolean Mask[4]; + int16_t Address; +} +VSH_IMD_OUTPUT; + +typedef struct _VSH_IMD_PARAMETER +{ + boolean Active; + VSH_PARAMETER Parameter; + // There is only a single address register in Microsoft DirectX 8.0. + // The address register, designated as a0.x, may be used as signed + // integer offset in relative addressing into the constant register file. + // c[a0.x + n] + boolean IndexesWithA0_X; +} +VSH_IMD_PARAMETER; + +typedef struct _VSH_INTERMEDIATE_FORMAT +{ + + boolean IsCombined; + VSH_IMD_INSTRUCTION_TYPE InstructionType; + VSH_MAC MAC; + VSH_ILU ILU; + VSH_IMD_OUTPUT Output; + VSH_IMD_PARAMETER Parameters[3]; +} +VSH_INTERMEDIATE_FORMAT; + +// Used for xvu spec definition +typedef struct _VSH_FIELDMAPPING +{ + VSH_FIELD_NAME FieldName; + uint8_t SubToken; + uint8_t StartBit; + uint8_t BitLength; +} +VSH_FIELDMAPPING; + +typedef struct _VSH_XBOX_SHADER +{ + XTL::X_VSH_SHADER_HEADER ShaderHeader; + uint16_t IntermediateCount; + VSH_INTERMEDIATE_FORMAT Intermediate[VSH_MAX_INTERMEDIATE_COUNT]; +} +VSH_XBOX_SHADER; + +// Local constants +static const VSH_FIELDMAPPING g_FieldMapping[] = +{ + // Field Name DWORD BitPos BitSize + { FLD_ILU, 1, 25, 3 }, + { FLD_MAC, 1, 21, 4 }, + { FLD_CONST, 1, 13, 8 }, + { FLD_V, 1, 9, 4 }, + // Input A + { FLD_A_NEG, 1, 8, 1 }, + { FLD_A_SWZ_X, 1, 6, 2 }, + { FLD_A_SWZ_Y, 1, 4, 2 }, + { FLD_A_SWZ_Z, 1, 2, 2 }, + { FLD_A_SWZ_W, 1, 0, 2 }, + { FLD_A_R, 2, 28, 4 }, + { FLD_A_MUX, 2, 26, 2 }, + // Input B + { FLD_B_NEG, 2, 25, 1 }, + { FLD_B_SWZ_X, 2, 23, 2 }, + { FLD_B_SWZ_Y, 2, 21, 2 }, + { FLD_B_SWZ_Z, 2, 19, 2 }, + { FLD_B_SWZ_W, 2, 17, 2 }, + { FLD_B_R, 2, 13, 4 }, + { FLD_B_MUX, 2, 11, 2 }, + // Input C + { FLD_C_NEG, 2, 10, 1 }, + { FLD_C_SWZ_X, 2, 8, 2 }, + { FLD_C_SWZ_Y, 2, 6, 2 }, + { FLD_C_SWZ_Z, 2, 4, 2 }, + { FLD_C_SWZ_W, 2, 2, 2 }, + { FLD_C_R_HIGH, 2, 0, 2 }, + { FLD_C_R_LOW, 3, 30, 2 }, + { FLD_C_MUX, 3, 28, 2 }, + // Output + { FLD_OUT_MAC_MASK_X, 3, 27, 1 }, + { FLD_OUT_MAC_MASK_Y, 3, 26, 1 }, + { FLD_OUT_MAC_MASK_Z, 3, 25, 1 }, + { FLD_OUT_MAC_MASK_W, 3, 24, 1 }, + { FLD_OUT_R, 3, 20, 4 }, + { FLD_OUT_ILU_MASK_X, 3, 19, 1 }, + { FLD_OUT_ILU_MASK_Y, 3, 18, 1 }, + { FLD_OUT_ILU_MASK_Z, 3, 17, 1 }, + { FLD_OUT_ILU_MASK_W, 3, 16, 1 }, + { FLD_OUT_O_MASK_X, 3, 15, 1 }, + { FLD_OUT_O_MASK_Y, 3, 14, 1 }, + { FLD_OUT_O_MASK_Z, 3, 13, 1 }, + { FLD_OUT_O_MASK_W, 3, 12, 1 }, + { FLD_OUT_ORB, 3, 11, 1 }, + { FLD_OUT_ADDRESS, 3, 3, 8 }, + { FLD_OUT_MUX, 3, 2, 1 }, + // Relative addressing + { FLD_A0X, 3, 1, 1 }, + // Final instruction + { FLD_FINAL, 3, 0, 1 } +}; + +static const VSH_OPCODE_PARAMS g_OpCodeParams_ILU[] = +{ + // ILU OP MAC OP ParamA ParamB ParamC + { /*ILU_NOP, MAC_NOP, */ FALSE, FALSE, FALSE }, // Dxbx note : Unused + { /*ILU_MOV, MAC_NOP, */ FALSE, FALSE, TRUE }, + { /*ILU_RCP, MAC_NOP, */ FALSE, FALSE, TRUE }, + { /*ILU_RCC, MAC_NOP, */ FALSE, FALSE, TRUE }, + { /*ILU_RSQ, MAC_NOP, */ FALSE, FALSE, TRUE }, + { /*ILU_EXP, MAC_NOP, */ FALSE, FALSE, TRUE }, + { /*ILU_LOG, MAC_NOP, */ FALSE, FALSE, TRUE }, + { /*ILU_LIT, MAC_NOP, */ FALSE, FALSE, TRUE }, +}; + +static const VSH_OPCODE_PARAMS g_OpCodeParams_MAC[] = +{ + // ILU OP MAC OP ParamA ParamB ParamC + { /*ILU_NOP, MAC_NOP, */ FALSE, FALSE, FALSE }, // Dxbx note : Unused + { /*ILU_NOP, MAC_MOV, */ TRUE, FALSE, FALSE }, + { /*ILU_NOP, MAC_MUL, */ TRUE, TRUE, FALSE }, + { /*ILU_NOP, MAC_ADD, */ TRUE, FALSE, TRUE }, + { /*ILU_NOP, MAC_MAD, */ TRUE, TRUE, TRUE }, + { /*ILU_NOP, MAC_DP3, */ TRUE, TRUE, FALSE }, + { /*ILU_NOP, MAC_DPH, */ TRUE, TRUE, FALSE }, + { /*ILU_NOP, MAC_DP4, */ TRUE, TRUE, FALSE }, + { /*ILU_NOP, MAC_DST, */ TRUE, TRUE, FALSE }, + { /*ILU_NOP, MAC_MIN, */ TRUE, TRUE, FALSE }, + { /*ILU_NOP, MAC_MAX, */ TRUE, TRUE, FALSE }, + { /*ILU_NOP, MAC_SLT, */ TRUE, TRUE, FALSE }, + { /*ILU_NOP, MAC_SGE, */ TRUE, TRUE, FALSE }, + { /*ILU_NOP, MAC_ARL, */ TRUE, FALSE, FALSE } +}; + +static const char* MAC_OpCode[] = +{ + "nop", + "mov", + "mul", + "add", + "mad", + "dp3", + "dph", + "dp4", + "dst", + "min", + "max", + "slt", + "sge", + "mova", // really "arl" Dxbx note : Alias for 'mov a0.x' + "???", + "???" +}; + +static const char* ILU_OpCode[] = +{ + "nop", + "mov", + "rcp", + "rcc", + "rsq", + "expp", // The Xbox EXPP instruction behaves like vs_1_1 + "log", + "lit" +}; + +static const char* OReg_Name[] = +{ + "oPos", + "???", + "???", + "oD0", + "oD1", + "oFog", + "oPts", + "oB0", + "oB1", + "oT0", + "oT1", + "oT2", + "oT3", + "???", + "???", + "a0.x" +}; + +std::array RegVIsPresentInDeclaration; +std::array RegVIsUsedByShader; + +/* TODO : map non-FVF Xbox vertex shader handle to CxbxVertexShader (a struct containing a host Xbox vertex shader handle and the original members) +std::unordered_map g_CxbxVertexShaders; + +void CxbxUpdateVertexShader(DWORD XboxVertexShaderHandle) +{ + CxbxVertexShader &VertexShader = g_CxbxVertexShaders[XboxVertexShaderHandle]; +}*/ + +static inline int IsInUse(const boolean *pMask) +{ + return (pMask[0] || pMask[1] || pMask[2] || pMask[3]); +} + +static inline boolean HasMACR(VSH_SHADER_INSTRUCTION *pInstruction) +{ + return IsInUse(pInstruction->Output.MACRMask) && pInstruction->MAC != MAC_NOP; +} + +static inline boolean HasMACO(VSH_SHADER_INSTRUCTION *pInstruction) +{ + return IsInUse(pInstruction->Output.OutputMask) && + pInstruction->Output.OutputMux == OMUX_MAC && + pInstruction->MAC != MAC_NOP; +} + +static inline boolean HasMACARL(VSH_SHADER_INSTRUCTION *pInstruction) +{ + return /*!IsInUse(pInstruction->Output.OutputMask) && + pInstruction->Output.OutputMux == OMUX_MAC &&*/ + pInstruction->MAC == MAC_ARL; +} + +static inline boolean HasILUR(VSH_SHADER_INSTRUCTION *pInstruction) +{ + return IsInUse(pInstruction->Output.ILURMask) && pInstruction->ILU != ILU_NOP; +} + +static inline boolean HasILUO(VSH_SHADER_INSTRUCTION *pInstruction) +{ + return IsInUse(pInstruction->Output.OutputMask) && + pInstruction->Output.OutputMux == OMUX_ILU && + pInstruction->ILU != ILU_NOP; +} + +// Retrieves a number of bits in the instruction token +static inline int VshGetFromToken(uint32_t *pShaderToken, + uint8_t SubToken, + uint8_t StartBit, + uint8_t BitLength) +{ + return (pShaderToken[SubToken] >> StartBit) & ~(0xFFFFFFFF << BitLength); +} + +// Converts the C register address to disassembly format +static inline int16_t ConvertCRegister(const int16_t CReg) +{ + return ((((CReg >> 5) & 7) - 3) * 32) + (CReg & 31); +} + +uint8_t VshGetField(uint32_t *pShaderToken, + VSH_FIELD_NAME FieldName) +{ + return (uint8_t)(VshGetFromToken(pShaderToken, + g_FieldMapping[FieldName].SubToken, + g_FieldMapping[FieldName].StartBit, + g_FieldMapping[FieldName].BitLength)); +} + +static VSH_OPCODE_PARAMS* VshGetOpCodeParams(VSH_ILU ILU, + VSH_MAC MAC) +{ + if (ILU >= ILU_MOV && ILU <= ILU_LIT) + return (VSH_OPCODE_PARAMS*)&g_OpCodeParams_ILU[ILU]; + else + if (MAC >= MAC_MOV && MAC <= MAC_ARL) + return (VSH_OPCODE_PARAMS*)&g_OpCodeParams_MAC[MAC]; + else + return nullptr; +} + +static void VshParseInstruction(uint32_t *pShaderToken, + VSH_SHADER_INSTRUCTION *pInstruction) +{ + // First get the instruction(s). + pInstruction->ILU = (VSH_ILU)VshGetField(pShaderToken, FLD_ILU); + pInstruction->MAC = (VSH_MAC)VshGetField(pShaderToken, FLD_MAC); + + // Get parameter A + pInstruction->A.ParameterType = (VSH_PARAMETER_TYPE)VshGetField(pShaderToken, FLD_A_MUX); + switch(pInstruction->A.ParameterType) + { + case PARAM_R: + pInstruction->A.Address = VshGetField(pShaderToken, FLD_A_R); + break; + case PARAM_V: + pInstruction->A.Address = VshGetField(pShaderToken, FLD_V); + break; + case PARAM_C: + pInstruction->A.Address = ConvertCRegister(VshGetField(pShaderToken, FLD_CONST)); + break; + default: + EmuLog(LOG_LEVEL::WARNING, "Invalid instruction, parameter A type unknown %d", pInstruction->A.ParameterType); + return; + } + pInstruction->A.Neg = VshGetField(pShaderToken, FLD_A_NEG); + pInstruction->A.Swizzle[0] = (VSH_SWIZZLE)VshGetField(pShaderToken, FLD_A_SWZ_X); + pInstruction->A.Swizzle[1] = (VSH_SWIZZLE)VshGetField(pShaderToken, FLD_A_SWZ_Y); + pInstruction->A.Swizzle[2] = (VSH_SWIZZLE)VshGetField(pShaderToken, FLD_A_SWZ_Z); + pInstruction->A.Swizzle[3] = (VSH_SWIZZLE)VshGetField(pShaderToken, FLD_A_SWZ_W); + // Get parameter B + pInstruction->B.ParameterType = (VSH_PARAMETER_TYPE)VshGetField(pShaderToken, FLD_B_MUX); + switch(pInstruction->B.ParameterType) + { + case PARAM_R: + pInstruction->B.Address = VshGetField(pShaderToken, FLD_B_R); + break; + case PARAM_V: + pInstruction->B.Address = VshGetField(pShaderToken, FLD_V); + break; + case PARAM_C: + pInstruction->B.Address = ConvertCRegister(VshGetField(pShaderToken, FLD_CONST)); + break; + default: + DbgVshPrintf("Invalid instruction, parameter B type unknown %d\n", pInstruction->B.ParameterType); + return; + } + pInstruction->B.Neg = VshGetField(pShaderToken, FLD_B_NEG); + pInstruction->B.Swizzle[0] = (VSH_SWIZZLE)VshGetField(pShaderToken, FLD_B_SWZ_X); + pInstruction->B.Swizzle[1] = (VSH_SWIZZLE)VshGetField(pShaderToken, FLD_B_SWZ_Y); + pInstruction->B.Swizzle[2] = (VSH_SWIZZLE)VshGetField(pShaderToken, FLD_B_SWZ_Z); + pInstruction->B.Swizzle[3] = (VSH_SWIZZLE)VshGetField(pShaderToken, FLD_B_SWZ_W); + // Get parameter C + pInstruction->C.ParameterType = (VSH_PARAMETER_TYPE)VshGetField(pShaderToken, FLD_C_MUX); + switch(pInstruction->C.ParameterType) + { + case PARAM_R: + pInstruction->C.Address = VshGetField(pShaderToken, FLD_C_R_HIGH) << 2 | + VshGetField(pShaderToken, FLD_C_R_LOW); + break; + case PARAM_V: + pInstruction->C.Address = VshGetField(pShaderToken, FLD_V); + break; + case PARAM_C: + pInstruction->C.Address = ConvertCRegister(VshGetField(pShaderToken, FLD_CONST)); + break; + default: + DbgVshPrintf("Invalid instruction, parameter C type unknown %d\n", pInstruction->C.ParameterType); + return; + } + pInstruction->C.Neg = VshGetField(pShaderToken, FLD_C_NEG); + pInstruction->C.Swizzle[0] = (VSH_SWIZZLE)VshGetField(pShaderToken, FLD_C_SWZ_X); + pInstruction->C.Swizzle[1] = (VSH_SWIZZLE)VshGetField(pShaderToken, FLD_C_SWZ_Y); + pInstruction->C.Swizzle[2] = (VSH_SWIZZLE)VshGetField(pShaderToken, FLD_C_SWZ_Z); + pInstruction->C.Swizzle[3] = (VSH_SWIZZLE)VshGetField(pShaderToken, FLD_C_SWZ_W); + // Get output + // Output register + pInstruction->Output.OutputType = (VSH_OUTPUT_TYPE)VshGetField(pShaderToken, FLD_OUT_ORB); + switch(pInstruction->Output.OutputType) + { + case OUTPUT_C: + pInstruction->Output.OutputAddress = ConvertCRegister(VshGetField(pShaderToken, FLD_OUT_ADDRESS)); + break; + case OUTPUT_O: + pInstruction->Output.OutputAddress = VshGetField(pShaderToken, FLD_OUT_ADDRESS) & 0xF; + break; + } + pInstruction->Output.OutputMux = (VSH_OUTPUT_MUX)VshGetField(pShaderToken, FLD_OUT_MUX); + pInstruction->Output.OutputMask[0] = VshGetField(pShaderToken, FLD_OUT_O_MASK_X); + pInstruction->Output.OutputMask[1] = VshGetField(pShaderToken, FLD_OUT_O_MASK_Y); + pInstruction->Output.OutputMask[2] = VshGetField(pShaderToken, FLD_OUT_O_MASK_Z); + pInstruction->Output.OutputMask[3] = VshGetField(pShaderToken, FLD_OUT_O_MASK_W); + // MAC output + pInstruction->Output.MACRMask[0] = VshGetField(pShaderToken, FLD_OUT_MAC_MASK_X); + pInstruction->Output.MACRMask[1] = VshGetField(pShaderToken, FLD_OUT_MAC_MASK_Y); + pInstruction->Output.MACRMask[2] = VshGetField(pShaderToken, FLD_OUT_MAC_MASK_Z); + pInstruction->Output.MACRMask[3] = VshGetField(pShaderToken, FLD_OUT_MAC_MASK_W); + pInstruction->Output.MACRAddress = VshGetField(pShaderToken, FLD_OUT_R); + // ILU output + pInstruction->Output.ILURMask[0] = VshGetField(pShaderToken, FLD_OUT_ILU_MASK_X); + pInstruction->Output.ILURMask[1] = VshGetField(pShaderToken, FLD_OUT_ILU_MASK_Y); + pInstruction->Output.ILURMask[2] = VshGetField(pShaderToken, FLD_OUT_ILU_MASK_Z); + pInstruction->Output.ILURMask[3] = VshGetField(pShaderToken, FLD_OUT_ILU_MASK_W); + pInstruction->Output.ILURAddress = VshGetField(pShaderToken, FLD_OUT_R); + // Finally, get a0.x indirect constant addressing + pInstruction->a0x = VshGetField(pShaderToken, FLD_A0X); +} + +// Print functions +static char *VshGetRegisterName(VSH_PARAMETER_TYPE ParameterType) +{ + switch(ParameterType) + { + case PARAM_R: + return "r"; + case PARAM_V: + return "v"; + case PARAM_C: + return "c"; + case PARAM_O: + return "oPos"; + default: + return "?"; + } +} + +static void VshWriteOutputMask(boolean *OutputMask, + std::stringstream& pDisassembly) +{ + if(OutputMask[0] && OutputMask[1] && OutputMask[2] && OutputMask[3]) + { + // All components are there, no need to print the mask + return; + } + pDisassembly << "." << (OutputMask[0] ? "x" : "") + << (OutputMask[1] ? "y" : "") + << (OutputMask[2] ? "z" : "") + << (OutputMask[3] ? "w" : ""); +} + +static void VshWriteParameter(VSH_IMD_PARAMETER *pParameter, + std::stringstream& pDisassembly) +{ + pDisassembly << ", " << (pParameter->Parameter.Neg ? "-" : "") << VshGetRegisterName(pParameter->Parameter.ParameterType); + if(pParameter->Parameter.ParameterType == PARAM_C && pParameter->IndexesWithA0_X) + { + // Only display the offset if it's not 0. + if(pParameter->Parameter.Address) + { + pDisassembly << "[a0.x+" << pParameter->Parameter.Address << "]"; + } + else + { + pDisassembly << "[a0.x]"; + } + } + else + { + pDisassembly << pParameter->Parameter.Address; + } + // Only bother printing the swizzle if it is not .xyzw + if(!(pParameter->Parameter.Swizzle[0] == SWIZZLE_X && + pParameter->Parameter.Swizzle[1] == SWIZZLE_Y && + pParameter->Parameter.Swizzle[2] == SWIZZLE_Z && + pParameter->Parameter.Swizzle[3] == SWIZZLE_W)) + { + int i; + + pDisassembly << "."; + for (i = 0; i < 4; i++) + { + int j; + char Swizzle = '?'; + switch(pParameter->Parameter.Swizzle[i]) + { + case SWIZZLE_X: + Swizzle = 'x'; + break; + case SWIZZLE_Y: + Swizzle = 'y'; + break; + case SWIZZLE_Z: + Swizzle = 'z'; + break; + case SWIZZLE_W: + Swizzle = 'w'; + break; + } + pDisassembly << Swizzle; + for (j = i; j < 4; j++) + { + if(pParameter->Parameter.Swizzle[i] != pParameter->Parameter.Swizzle[j]) + { + break; + } + } + if(j == 4) + { + break; + } + } + } +} + + +char* XboxVertexRegisterAsString(DWORD VertexRegister) +{ + switch (VertexRegister) + { + case XTL::X_D3DVSDE_VERTEX: // -1 + return "D3DVSDE_VERTEX /* xbox ext. */"; + case XTL::X_D3DVSDE_POSITION: // 0 + return "D3DVSDE_POSITION"; + case XTL::X_D3DVSDE_BLENDWEIGHT: // 1 + return "D3DVSDE_BLENDWEIGHT"; + case XTL::X_D3DVSDE_NORMAL: // 2 + return "D3DVSDE_NORMAL"; + case XTL::X_D3DVSDE_DIFFUSE: // 3 + return "D3DVSDE_DIFFUSE"; + case XTL::X_D3DVSDE_SPECULAR: // 4 + return "D3DVSDE_SPECULAR"; + case XTL::X_D3DVSDE_FOG: // 5 + return "D3DVSDE_FOG"; + case XTL::X_D3DVSDE_POINTSIZE: // 6 + return "D3DVDSE_POINTSIZE"; + case XTL::X_D3DVSDE_BACKDIFFUSE: // 7 + return "D3DVSDE_BACKDIFFUSE /* xbox ext. */"; + case XTL::X_D3DVSDE_BACKSPECULAR: // 8 + return "D3DVSDE_BACKSPECULAR /* xbox ext. */"; + case XTL::X_D3DVSDE_TEXCOORD0: // 9 + return "D3DVSDE_TEXCOORD0"; + case XTL::X_D3DVSDE_TEXCOORD1: // 10 + return "D3DVSDE_TEXCOORD1"; + case XTL::X_D3DVSDE_TEXCOORD2: // 11 + return "D3DVSDE_TEXCOORD2"; + case XTL::X_D3DVSDE_TEXCOORD3: // 12 + return "D3DVSDE_TEXCOORD3"; + case 13: + return "13 /* unknown register */"; + case 14: + return "14 /* unknown register */"; + case 15: + return "15 /* unknown register */"; + default: + return "16 /* or higher, unknown register */"; + } +} + +#define D3DDECLUSAGE_UNSUPPORTED ((D3DDECLUSAGE)-1) + +D3DDECLUSAGE Xb2PCRegisterType +( + DWORD VertexRegister, + BYTE& PCUsageIndex +) +{ + D3DDECLUSAGE PCRegisterType; + PCUsageIndex = 0; + + switch (VertexRegister) + { + case XTL::X_D3DVSDE_VERTEX: // -1 + PCRegisterType = D3DDECLUSAGE_UNSUPPORTED; + break; + case XTL::X_D3DVSDE_POSITION: // 0 + PCRegisterType = D3DDECLUSAGE_POSITION; + break; + case XTL::X_D3DVSDE_BLENDWEIGHT: // 1 + PCRegisterType = D3DDECLUSAGE_BLENDWEIGHT; + break; + case XTL::X_D3DVSDE_NORMAL: // 2 + PCRegisterType = D3DDECLUSAGE_NORMAL; + break; + case XTL::X_D3DVSDE_DIFFUSE: // 3 + PCRegisterType = D3DDECLUSAGE_COLOR; PCUsageIndex = 0; + break; + case XTL::X_D3DVSDE_SPECULAR: // 4 + PCRegisterType = D3DDECLUSAGE_COLOR; PCUsageIndex = 1; + break; + case XTL::X_D3DVSDE_FOG: // 5 + PCRegisterType = D3DDECLUSAGE_FOG; + break; + case XTL::X_D3DVSDE_POINTSIZE: // 6 + PCRegisterType = D3DDECLUSAGE_PSIZE; + break; + case XTL::X_D3DVSDE_BACKDIFFUSE: // 7 + PCRegisterType = D3DDECLUSAGE_COLOR; PCUsageIndex = 2; + break; + case XTL::X_D3DVSDE_BACKSPECULAR: // 8 + PCRegisterType = D3DDECLUSAGE_COLOR; PCUsageIndex = 3; + break; + case XTL::X_D3DVSDE_TEXCOORD0: // 9 + PCRegisterType = D3DDECLUSAGE_TEXCOORD; PCUsageIndex = 0; + break; + case XTL::X_D3DVSDE_TEXCOORD1: // 10 + PCRegisterType = D3DDECLUSAGE_TEXCOORD; PCUsageIndex = 1; + break; + case XTL::X_D3DVSDE_TEXCOORD2: // 11 + PCRegisterType = D3DDECLUSAGE_TEXCOORD; PCUsageIndex = 2; + break; + case XTL::X_D3DVSDE_TEXCOORD3: // 12 + PCRegisterType = D3DDECLUSAGE_TEXCOORD; PCUsageIndex = 3; + break; + default: + PCRegisterType = D3DDECLUSAGE_UNSUPPORTED; + break; + } + + return PCRegisterType; +} + +extern D3DCAPS g_D3DCaps; + +enum { + X_VSH_TEMPORARY_REGISTER_COUNT = 12, // For Xbox temporary registers r0 to r11, mapped one-on-one to host + X_VSH_TEMP_OPOS = 12, // Used as intermediate storage for oPos (which Xbox can read through r12) + // X_VSH_TEMP_OFOG, // Enable once we treat oFog similar to oPos + // X_VSH_TEMP_OPTS, // Enable once we treat oPts similar to oPos + X_VSH_TEMP_SCRATCH = 13, // Used as intermediate storage in Xbox-to-host opcode conversion + X_VSH_TEMP_VERTEXREGBASE = 14 // Used for (1 up to 16) SetVertexData4f constants +}; + +static void VshWriteShader(VSH_XBOX_SHADER *pShader, + std::stringstream& pDisassembly, + D3DVERTEXELEMENT *pRecompiled, + boolean Truncate) +{ + switch(pShader->ShaderHeader.Version) + { + case VERSION_CXBX: +#ifdef CXBX_USE_VS30 + pDisassembly << "vs.3.0\n"; +#else + pDisassembly << "vs.2.x\n"; +#endif + break; + case VERSION_XVS: + pDisassembly << "xvs.1.1\n"; + break; + case VERSION_XVSS: + pDisassembly << "xvss.1.1\n"; + break; + case VERSION_XVSW: + pDisassembly << "xvsw.1.1\n"; + break; + default: + break; + } + + // Ensure extra temporary registers are assigned at the beginning, as stand-ins for undeclared v registers + // Abusing the truncate flag, which implies we're writing the final host shader + if (Truncate) { + std::stringstream moveConstantsToTemporaries; + + pDisassembly << "; Input usage declarations --\n"; + for(int i = 0; i < RegVIsUsedByShader.size(); i++){ + if (RegVIsUsedByShader[i]) { + if (!RegVIsPresentInDeclaration[i]) { + // Log test case and skip + // Any registers hitting this critera were already replaced with constant/temporary reads + // To correctly use the values given in SetVertexData4f. + // We need to move these constant values to temporaries so they can be used as input alongside other constants! + // We count down from the highest available on the host because Xbox titles don't use values that high, and we read from c192 (one above maximum Xbox c191 constant) and up + moveConstantsToTemporaries << "mov r" << (X_VSH_TEMP_VERTEXREGBASE + i) << ", c" << (CXBX_D3DVS_CONSTREG_VERTEXDATA4F_BASE + i) << "\n"; + // test-case : Blade II (before menu's) + // test-case : Namco Museum 50th Anniversary (at boot) + // test-case : Pac-Man World 2 (at boot) + // test-case : The Simpsons Road Rage (leaving menu's, before entering in-game) + // test-case : The SpongeBob SquarePants Movie (before menu's) + LOG_TEST_CASE("Shader uses undeclared Vertex Input Registers"); + continue; + } + + // dcl_texcoord can be useds for any user-defined data + // We need this because there is no reliable way to detect the real usage + // Xbox has no concept of 'usage types', it only requires a list of attribute register numbers. + // So we treat them all as 'user-defined' + pDisassembly << "dcl_texcoord" << i << " v" << i << "\n"; + } + } + + pDisassembly << moveConstantsToTemporaries.str(); + } + + for (int i = 0; i < pShader->IntermediateCount && (i < VSH_MAX_INSTRUCTION_COUNT || !Truncate); i++) + { + VSH_INTERMEDIATE_FORMAT *pIntermediate = &pShader->Intermediate[i]; + + if(i == VSH_MAX_INSTRUCTION_COUNT) + { + pDisassembly << "; -- Passing the truncation limit --\n"; + } + + // Writing combining sign if neccessary + if(pIntermediate->IsCombined) + { + pDisassembly << "+"; + } + + // Print the op code + if(pIntermediate->InstructionType == IMD_MAC) + { + // Dxbx addition : Safeguard against incorrect MAC opcodes : + if (pIntermediate->MAC > MAC_ARL) + pDisassembly << "??? "; + else + pDisassembly << MAC_OpCode[pIntermediate->MAC] << " "; + } + else // IMD_ILU + { + // Dxbx addition : Safeguard against incorrect ILU opcodes : + if (pIntermediate->ILU > ILU_LIT) + pDisassembly << "??? "; + else + pDisassembly << ILU_OpCode[pIntermediate->ILU] << " "; + } + + // Print the output parameter + if(pIntermediate->Output.Type == IMD_OUTPUT_A0X) + { + pDisassembly << "a0.x"; + } + else + { + switch(pIntermediate->Output.Type) + { + case IMD_OUTPUT_C: + pDisassembly << "c" << pIntermediate->Output.Address; + break; + case IMD_OUTPUT_R: + pDisassembly << "r" << pIntermediate->Output.Address; + break; + case IMD_OUTPUT_O: + // Dxbx addition : Safeguard against incorrect VSH_OREG_NAME values : + if ((int)pIntermediate->Output.Address > OREG_A0X) + ; // don't add anything + else + pDisassembly << OReg_Name[pIntermediate->Output.Address]; + break; + default: + CxbxKrnlCleanup("Invalid output register in vertex shader!"); + break; + } + VshWriteOutputMask(pIntermediate->Output.Mask, pDisassembly); + } + // Print the parameters + for (int p = 0; p < 3; p++) + { + VSH_IMD_PARAMETER *pParameter = &pIntermediate->Parameters[p]; + if(pParameter->Active) + { + VshWriteParameter(pParameter, pDisassembly); + } + } + pDisassembly << "\n"; + } +} + +static void VshAddParameter(VSH_PARAMETER *pParameter, + boolean a0x, + VSH_IMD_PARAMETER *pIntermediateParameter) +{ + pIntermediateParameter->Parameter = *pParameter; + pIntermediateParameter->Active = TRUE; + pIntermediateParameter->IndexesWithA0_X = a0x; +} + +static void VshAddParameters(VSH_SHADER_INSTRUCTION *pInstruction, + VSH_ILU ILU, + VSH_MAC MAC, + VSH_IMD_PARAMETER *pParameters) +{ + uint8_t ParamCount = 0; + VSH_OPCODE_PARAMS* pParams = VshGetOpCodeParams(ILU, MAC); + + // param A + if(pParams->A) + { + VshAddParameter(&pInstruction->A, pInstruction->a0x, &pParameters[ParamCount]); + ParamCount++; + } + + // param B + if(pParams->B) + { + VshAddParameter(&pInstruction->B, pInstruction->a0x, &pParameters[ParamCount]); + ParamCount++; + } + + // param C + if(pParams->C) + { + VshAddParameter(&pInstruction->C, pInstruction->a0x, &pParameters[ParamCount]); + ParamCount++; + } +} + +static void VshVerifyBufferBounds(VSH_XBOX_SHADER *pShader) +{ + if(pShader->IntermediateCount >= VSH_MAX_INTERMEDIATE_COUNT) + { + CxbxKrnlCleanup("Shader exceeds conversion buffer!"); + } +} + +static VSH_INTERMEDIATE_FORMAT *VshNewIntermediate(VSH_XBOX_SHADER *pShader) +{ + VshVerifyBufferBounds(pShader); + + ZeroMemory(&pShader->Intermediate[pShader->IntermediateCount], sizeof(VSH_INTERMEDIATE_FORMAT)); + + return &pShader->Intermediate[pShader->IntermediateCount++]; +} + +static void VshInsertIntermediate(VSH_XBOX_SHADER *pShader, + VSH_INTERMEDIATE_FORMAT *pIntermediate, + uint16_t Pos) +{ + VshVerifyBufferBounds(pShader); + + for (int i = pShader->IntermediateCount; i >= Pos; i--) + { + pShader->Intermediate[i + 1] = pShader->Intermediate[i]; + } + pShader->Intermediate[Pos] = *pIntermediate; + pShader->IntermediateCount++; +} + +static void VshDeleteIntermediate(VSH_XBOX_SHADER *pShader, + uint16_t Pos) +{ + for (int i = Pos; i < (pShader->IntermediateCount - 1); i++) + { + pShader->Intermediate[i] = pShader->Intermediate[i + 1]; + } + pShader->IntermediateCount--; +} + +static boolean VshAddInstructionMAC_R(VSH_SHADER_INSTRUCTION *pInstruction, + VSH_XBOX_SHADER *pShader, + boolean IsCombined) +{ + VSH_INTERMEDIATE_FORMAT *pIntermediate; + if(!HasMACR(pInstruction)) + { + return FALSE; + } + + pIntermediate = VshNewIntermediate(pShader); + pIntermediate->IsCombined = IsCombined; + + // Opcode + pIntermediate->InstructionType = IMD_MAC; + pIntermediate->MAC = pInstruction->MAC; + + // Output param + pIntermediate->Output.Type = IMD_OUTPUT_R; + pIntermediate->Output.Address = pInstruction->Output.MACRAddress; + memcpy(pIntermediate->Output.Mask, pInstruction->Output.MACRMask, sizeof(boolean) * 4); + + // Other parameters + VshAddParameters(pInstruction, ILU_NOP, pInstruction->MAC, pIntermediate->Parameters); + + return TRUE; +} + +static boolean VshAddInstructionMAC_O(VSH_SHADER_INSTRUCTION* pInstruction, + VSH_XBOX_SHADER *pShader, + boolean IsCombined) +{ + VSH_INTERMEDIATE_FORMAT *pIntermediate; + if(!HasMACO(pInstruction)) + { + return FALSE; + } + + pIntermediate = VshNewIntermediate(pShader); + pIntermediate->IsCombined = IsCombined; + + // Opcode + pIntermediate->InstructionType = IMD_MAC; + pIntermediate->MAC = pInstruction->MAC; + + // Output param + pIntermediate->Output.Type = pInstruction->Output.OutputType == OUTPUT_C ? IMD_OUTPUT_C : IMD_OUTPUT_O; + pIntermediate->Output.Address = pInstruction->Output.OutputAddress; + memcpy(pIntermediate->Output.Mask, pInstruction->Output.OutputMask, sizeof(boolean) * 4); + + // Other parameters + VshAddParameters(pInstruction, ILU_NOP, pInstruction->MAC, pIntermediate->Parameters); + + return TRUE; +} + +static boolean VshAddInstructionMAC_ARL(VSH_SHADER_INSTRUCTION *pInstruction, + VSH_XBOX_SHADER *pShader, + boolean IsCombined) +{ + VSH_INTERMEDIATE_FORMAT *pIntermediate; + if(!HasMACARL(pInstruction)) + { + return FALSE; + } + + pIntermediate = VshNewIntermediate(pShader); + pIntermediate->IsCombined = IsCombined; + + // Opcode + pIntermediate->InstructionType = IMD_MAC; + pIntermediate->MAC = pInstruction->MAC; + + // Output param + pIntermediate->Output.Type = IMD_OUTPUT_A0X; + pIntermediate->Output.Address = pInstruction->Output.OutputAddress; + + // Other parameters + VshAddParameters(pInstruction, ILU_NOP, pInstruction->MAC, pIntermediate->Parameters); + + return TRUE; +} + +// Dxbx addition : Scalar instructions reading from W should read from X instead +static boolean DxbxFixupScalarParameter(VSH_SHADER_INSTRUCTION *pInstruction, + VSH_XBOX_SHADER *pShader, + VSH_PARAMETER *pParameter); + +static boolean VshAddInstructionILU_R(VSH_SHADER_INSTRUCTION *pInstruction, + VSH_XBOX_SHADER *pShader, + boolean IsCombined) +{ + VSH_INTERMEDIATE_FORMAT *pIntermediate; + if(!HasILUR(pInstruction)) + { + return FALSE; + } + + pIntermediate = VshNewIntermediate(pShader); + pIntermediate->IsCombined = IsCombined; + + // Opcode + pIntermediate->InstructionType = IMD_ILU; + pIntermediate->ILU = pInstruction->ILU; + + // Output param + pIntermediate->Output.Type = IMD_OUTPUT_R; + // If this is a combined instruction, only r1 is allowed (R address should not be used) + pIntermediate->Output.Address = IsCombined ? 1 : pInstruction->Output.ILURAddress; + memcpy(pIntermediate->Output.Mask, pInstruction->Output.ILURMask, sizeof(boolean) * 4); + + // Other parameters + VshAddParameters(pInstruction, pInstruction->ILU, MAC_NOP, pIntermediate->Parameters); + + return TRUE; +} + +static boolean VshAddInstructionILU_O(VSH_SHADER_INSTRUCTION *pInstruction, + VSH_XBOX_SHADER *pShader, + boolean IsCombined) +{ + VSH_INTERMEDIATE_FORMAT *pIntermediate; + if(!HasILUO(pInstruction)) + { + return FALSE; + } + + pIntermediate = VshNewIntermediate(pShader); + pIntermediate->IsCombined = IsCombined; + + // Opcode + pIntermediate->InstructionType = IMD_ILU; + pIntermediate->ILU = pInstruction->ILU; + + // Output param + pIntermediate->Output.Type = pInstruction->Output.OutputType == OUTPUT_C ? IMD_OUTPUT_C : IMD_OUTPUT_O; + pIntermediate->Output.Address = pInstruction->Output.OutputAddress; + memcpy(pIntermediate->Output.Mask, pInstruction->Output.OutputMask, sizeof(boolean) * 4); + + // Other parameters + VshAddParameters(pInstruction, pInstruction->ILU, MAC_NOP, pIntermediate->Parameters); + + return TRUE; +} + +static void VshConvertToIntermediate(VSH_SHADER_INSTRUCTION *pInstruction, + VSH_XBOX_SHADER *pShader) +{ + // Five types of instructions: + // MAC + // + // ILU + // + // MAC + // +ILU + // + // MAC + // +MAC + // +ILU + // + // MAC + // +ILU + // +ILU + boolean IsCombined = FALSE; + + // Dxbx note : Scalar instructions read from C, but use X instead of W, fix that : + DxbxFixupScalarParameter(pInstruction, pShader, &pInstruction->C); + + if(VshAddInstructionMAC_R(pInstruction, pShader, IsCombined)) + { + if(HasMACO(pInstruction) || + HasILUR(pInstruction) || + HasILUO(pInstruction)) + { + IsCombined = TRUE; + } + } + if(VshAddInstructionMAC_O(pInstruction, pShader, IsCombined)) + { + if(HasILUR(pInstruction) || + HasILUO(pInstruction)) + { + IsCombined = TRUE; + } + } + // Special case, arl (mov a0.x, ...) + if(VshAddInstructionMAC_ARL(pInstruction, pShader, IsCombined)) + { + if(HasILUR(pInstruction) || + HasILUO(pInstruction)) + { + IsCombined = TRUE; + } + } + if(VshAddInstructionILU_R(pInstruction, pShader, IsCombined)) + { + if(HasILUO(pInstruction)) + { + IsCombined = TRUE; + } + } + (void)VshAddInstructionILU_O(pInstruction, pShader, IsCombined); +} + +static inline void VshSetSwizzle(VSH_PARAMETER *pParameter, + VSH_SWIZZLE x, + VSH_SWIZZLE y, + VSH_SWIZZLE z, + VSH_SWIZZLE w) +{ + pParameter->Swizzle[0] = x; + pParameter->Swizzle[1] = y; + pParameter->Swizzle[2] = z; + pParameter->Swizzle[3] = w; +} + +static inline void VshSetSwizzle(VSH_IMD_PARAMETER *pParameter, + VSH_SWIZZLE x, + VSH_SWIZZLE y, + VSH_SWIZZLE z, + VSH_SWIZZLE w) +{ + VshSetSwizzle(&pParameter->Parameter, x, y, z, w); +} + +static inline void VshSetOutputMask(VSH_IMD_OUTPUT* pOutput, + boolean MaskX, + boolean MaskY, + boolean MaskZ, + boolean MaskW) +{ + pOutput->Mask[0] = MaskX; + pOutput->Mask[1] = MaskY; + pOutput->Mask[2] = MaskZ; + pOutput->Mask[3] = MaskW; +} + +// Dxbx addition : Scalar instructions reading from W should read from X instead +static boolean DxbxFixupScalarParameter(VSH_SHADER_INSTRUCTION *pInstruction, + VSH_XBOX_SHADER *pShader, + VSH_PARAMETER *pParameter) +{ + boolean Result; + + // The DirectX vertex shader language specifies that the exp, log, rcc, rcp, and rsq instructions + // all operate on the "w" component of the input. But the microcode versions of these instructions + // actually operate on the "x" component of the input. + Result = false; + + // Test if this is a scalar instruction : + if (pInstruction->ILU == ILU_RCP || + pInstruction->ILU == ILU_RCC || + pInstruction->ILU == ILU_RSQ || + pInstruction->ILU == ILU_LOG) + { + // Test if this parameter reads all components, including W (TODO : Or should we fixup any W reading swizzle?) : + if ((pParameter->Swizzle[0] == SWIZZLE_X) + && (pParameter->Swizzle[1] == SWIZZLE_Y) + && (pParameter->Swizzle[2] == SWIZZLE_Z) + && (pParameter->Swizzle[3] == SWIZZLE_W)) + { + // Change the read from W into a read from X (this fixes the XDK VolumeLight sample) : + VshSetSwizzle(pParameter, SWIZZLE_X, SWIZZLE_X, SWIZZLE_X, SWIZZLE_X); + DbgVshPrintf("Dxbx fixup on scalar instruction applied; Changed read of uninitialized W into a read of X!\n"); + Result = true; + } + } + + return Result; +} + +/* + mul oPos.xyz, r12, c-38 + +rcc r1.x, r12.w + + mad oPos.xyz, r12, r1.x, c-37 +*/ +static void VshRemoveScreenSpaceInstructions(VSH_XBOX_SHADER *pShader) +{ + int16_t PosC38 = -1; + int deleted = 0; + + for (int i = 0; i < pShader->IntermediateCount; i++) + { + VSH_INTERMEDIATE_FORMAT* pIntermediate = &pShader->Intermediate[i]; + + for (int k = 0; k < 3; k++) + { + if(pIntermediate->Parameters[k].Active) + { + if(pIntermediate->Parameters[k].Parameter.ParameterType == PARAM_C && + !pIntermediate->Parameters[k].IndexesWithA0_X) + { + if(pIntermediate->Parameters[k].Parameter.Address == -37) + { + // Found c-37, remove the instruction + if(k == 2 && + pIntermediate->Parameters[1].Active && + pIntermediate->Parameters[1].Parameter.ParameterType == PARAM_R) + { + DbgVshPrintf("PosC38 = %d i = %d\n", PosC38, i); + for (int j = (i-1); j >= 0; j--) + { + VSH_INTERMEDIATE_FORMAT* pIntermediate1W = &pShader->Intermediate[j]; + // Time to start searching for +rcc r#.x, r12.w + if(pIntermediate1W->InstructionType == IMD_ILU && + pIntermediate1W->ILU == ILU_RCC && + pIntermediate1W->Output.Type == IMD_OUTPUT_R && + pIntermediate1W->Output.Address == + pIntermediate->Parameters[1].Parameter.Address) + { + DbgVshPrintf("Deleted +rcc r1.x, r12.w\n"); + VshDeleteIntermediate(pShader, j); + deleted++; + i--; + //j--; + break; + } + } + } + VshDeleteIntermediate(pShader, i); + deleted++; + i--; + DbgVshPrintf("Deleted mad oPos.xyz, r12, r1.x, c-37\n"); + break; + } + else if(pIntermediate->Parameters[k].Parameter.Address == -38) + { + VshDeleteIntermediate(pShader, i); + PosC38 = i; + deleted++; + i--; + DbgVshPrintf("Deleted mul oPos.xyz, r12, c-38\n"); + } + } + } + } + } + + // If we couldn't find the generic screen space transformation we're + // assuming that the shader writes direct screen coordinates that must be + // normalized. This hack will fail if (a) the shader uses custom screen + // space transformation, (b) reads r13 or r12 after we have written to + // them, or (c) doesn't reserve c-38 and c-37 for scale and offset. + if(deleted != 3) + { + EmuLog(LOG_LEVEL::WARNING, "Applying screen space vertex shader patching hack!"); + for (int i = 0; i < pShader->IntermediateCount; i++) + { + VSH_INTERMEDIATE_FORMAT* pIntermediate = &pShader->Intermediate[i]; + + // Find instructions outputting to oPos. + if( pIntermediate->Output.Type == IMD_OUTPUT_O && + pIntermediate->Output.Address == OREG_OPOS) + { + // Redirect output to r12. + pIntermediate->Output.Type = IMD_OUTPUT_R; + pIntermediate->Output.Address = X_VSH_TEMP_OPOS; + + // Scale r12 to r13. (mul r13.[mask], r12, c58) + VSH_INTERMEDIATE_FORMAT MulIntermediate; + MulIntermediate.IsCombined = FALSE; + MulIntermediate.InstructionType = IMD_MAC; + MulIntermediate.MAC = MAC_MUL; + MulIntermediate.Output.Type = IMD_OUTPUT_R; + MulIntermediate.Output.Address = X_VSH_TEMP_SCRATCH; + MulIntermediate.Output.Mask[0] = pIntermediate->Output.Mask[0]; + MulIntermediate.Output.Mask[1] = pIntermediate->Output.Mask[1]; + MulIntermediate.Output.Mask[2] = pIntermediate->Output.Mask[2]; + MulIntermediate.Output.Mask[3] = pIntermediate->Output.Mask[3]; + MulIntermediate.Parameters[0].Active = TRUE; + MulIntermediate.Parameters[0].IndexesWithA0_X = FALSE; + MulIntermediate.Parameters[0].Parameter.ParameterType = PARAM_R; + MulIntermediate.Parameters[0].Parameter.Address = X_VSH_TEMP_OPOS; + MulIntermediate.Parameters[0].Parameter.Neg = FALSE; + VshSetSwizzle(&MulIntermediate.Parameters[0], SWIZZLE_X, SWIZZLE_Y, SWIZZLE_Z, SWIZZLE_W); + MulIntermediate.Parameters[1].Active = TRUE; + MulIntermediate.Parameters[1].IndexesWithA0_X = FALSE; + MulIntermediate.Parameters[1].Parameter.ParameterType = PARAM_C; + MulIntermediate.Parameters[1].Parameter.Address = ConvertCRegister(X_D3DVS_RESERVED_CONSTANT1_CORRECTED); + MulIntermediate.Parameters[1].Parameter.Neg = FALSE; + VshSetSwizzle(&MulIntermediate.Parameters[1], SWIZZLE_X, SWIZZLE_Y, SWIZZLE_Z, SWIZZLE_W); + MulIntermediate.Parameters[2].Active = FALSE; + VshInsertIntermediate(pShader, &MulIntermediate, ++i); + + // Add offset with r13 to oPos (add oPos.[mask], r13, c59) + VSH_INTERMEDIATE_FORMAT AddIntermediate = MulIntermediate; + AddIntermediate.MAC = MAC_ADD; + AddIntermediate.Output.Type = IMD_OUTPUT_O; + AddIntermediate.Output.Address = OREG_OPOS; + AddIntermediate.Parameters[0].Parameter.ParameterType = PARAM_R; + AddIntermediate.Parameters[0].Parameter.Address = X_VSH_TEMP_SCRATCH; + AddIntermediate.Parameters[1].Parameter.Address = ConvertCRegister(X_D3DVS_RESERVED_CONSTANT2_CORRECTED); + VshInsertIntermediate(pShader, &AddIntermediate, ++i); + } + } + } +} + +static void VshRemoveUnsupportedObRegisters(VSH_XBOX_SHADER *pShader) +{ + int deleted = 0; + + for (int i = 0; i < pShader->IntermediateCount; i++) { + VSH_INTERMEDIATE_FORMAT* pIntermediate = &pShader->Intermediate[i]; + + if (pIntermediate->Output.Type == IMD_OUTPUT_O && (pIntermediate->Output.Address == OREG_OB0 || pIntermediate->Output.Address == OREG_OB1)) { + DbgVshPrintf("Deleted unsupported write to %s\n", OReg_Name[pIntermediate->Output.Address]); + VshDeleteIntermediate(pShader, i); + i--; + } + } +} + +// Converts the intermediate format vertex shader to DirectX 8/9 format +static boolean VshConvertShader(VSH_XBOX_SHADER *pShader, + boolean bNoReservedConstants +) +{ + // TODO: What about state shaders and such? + + pShader->ShaderHeader.Version = VERSION_CXBX; + + // Search for the screen space instructions, and remove them + if(!bNoReservedConstants) + { + VshRemoveScreenSpaceInstructions(pShader); + } + + // Windows does not support back-facing colours, so we remove them from the shaders + // Test Case: Panzer Dragoon Orta + VshRemoveUnsupportedObRegisters(pShader); + + // TODO: Add routine for compacting r register usage so that at least one is freed (two if dph and r12) + + for (int i = 0; i < pShader->IntermediateCount; i++) + { + VSH_INTERMEDIATE_FORMAT* pIntermediate = &pShader->Intermediate[i]; + // Combining not supported in vs.1.1 + pIntermediate->IsCombined = FALSE; + + if(pIntermediate->Output.Type == IMD_OUTPUT_O && (pIntermediate->Output.Address == OREG_OPTS || pIntermediate->Output.Address == OREG_OFOG)) + { + // The PC shader assembler doesn't like masks on scalar registers + VshSetOutputMask(&pIntermediate->Output, TRUE, TRUE, TRUE, TRUE); + + // Fix when mad or mov to a scaler input does not use a replicate swizzle + // MAD Test case: Panzer Dragoon Orta + // MOV Test case: DOA3, Mechassault (Const) + // MUL Test case: Amped + // TODO Previously we applied this fix for specified instructions + // When should we not apply the correction? + if (true) + { + // Clear all but the first swizzle for each parameter + // TODO: Is this sufficient? Perhaps we need to be smart about which swizzle to select + for (int param = 0; param < 3; param++) { + pIntermediate->Parameters[param].Parameter.Swizzle[1] = pIntermediate->Parameters[param].Parameter.Swizzle[0]; + pIntermediate->Parameters[param].Parameter.Swizzle[2] = pIntermediate->Parameters[param].Parameter.Swizzle[0]; + pIntermediate->Parameters[param].Parameter.Swizzle[3] = pIntermediate->Parameters[param].Parameter.Swizzle[0]; + } + } + } + + if(pIntermediate->InstructionType == IMD_ILU && pIntermediate->ILU == ILU_RCC) + { + // Convert rcc to rcp + pIntermediate->ILU = ILU_RCP; + } + + auto sw = pIntermediate->Parameters[0].Parameter.Swizzle; + bool singleSwizzle = sw[0] == sw[1] && sw[1] == sw[2] && sw[2] == sw[3]; + + if (!singleSwizzle) + { + // Fix when RSQ reads from unitialized components + if (pIntermediate->InstructionType == IMD_ILU && pIntermediate->ILU == ILU_RSQ) { + int swizzle = (pIntermediate->Output.Mask[0]) | (pIntermediate->Output.Mask[1] << 1) | (pIntermediate->Output.Mask[2] << 2) | (pIntermediate->Output.Mask[3] << 3); + switch (swizzle) + { + case 1: + VshSetSwizzle(&pIntermediate->Parameters[0], SWIZZLE_X, SWIZZLE_X, SWIZZLE_X, SWIZZLE_X); + break; + case 2: + VshSetSwizzle(&pIntermediate->Parameters[0], SWIZZLE_Y, SWIZZLE_Y, SWIZZLE_Y, SWIZZLE_Y); + break; + case 4: + VshSetSwizzle(&pIntermediate->Parameters[0], SWIZZLE_Z, SWIZZLE_Z, SWIZZLE_Z, SWIZZLE_Z); + break; + case 8: + VshSetSwizzle(&pIntermediate->Parameters[0], SWIZZLE_W, SWIZZLE_W, SWIZZLE_W, SWIZZLE_W); + break; + case 15: + default: + LOG_TEST_CASE("rsq instruction with invalid swizzle"); + break; + } + } + } + + for (int j = 0; j < 3; j++) + { + //if(pIntermediate->Parameters[j].Active) + { + // Make constant registers range from 0 to 191 instead of -96 to 95 + if (pIntermediate->Parameters[j].Parameter.ParameterType == PARAM_C) + { + //if(pIntermediate->Parameters[j].Parameter.Address < 0) + pIntermediate->Parameters[j].Parameter.Address += X_D3DVS_CONSTREG_BIAS; + } + + if (pIntermediate->Parameters[j].Parameter.ParameterType == PARAM_V) { + RegVIsUsedByShader[pIntermediate->Parameters[j].Parameter.Address] = TRUE; + + if (!RegVIsPresentInDeclaration[pIntermediate->Parameters[j].Parameter.Address]) { + // This vertex register was not declared and therefore is not present within the Vertex Data object + // We read from temporary registers instead, that are set based on constants, in-turn, set by SetVertexData4f + // We count down from the highest available on the host because Xbox titles don't use values that high, and we read from c192 (one above maximum Xbox c191 constant) and up + pIntermediate->Parameters[j].Parameter.ParameterType = PARAM_R; + pIntermediate->Parameters[j].Parameter.Address += X_VSH_TEMP_VERTEXREGBASE; + } + } + } + } + + // Make constant registers range from 0 to 191 instead of -96 to 95 + if(pIntermediate->Output.Type == IMD_OUTPUT_C) + { + //if(pIntermediate->Output.Address < 0) + pIntermediate->Output.Address += X_D3DVS_CONSTREG_BIAS; + } + + + + if(pIntermediate->InstructionType == IMD_MAC && pIntermediate->MAC == MAC_DPH) + { + // 2010/01/12 - revel8n - attempt to alleviate conversion issues relate to the dph instruction + + // Replace dph with dp3 and add + if(pIntermediate->Output.Type != IMD_OUTPUT_R) + { + // TODO: Complete dph support + EmuLog(LOG_LEVEL::WARNING, "Can't simulate dph for other than output r registers (yet)"); + + VSH_INTERMEDIATE_FORMAT TmpIntermediate = *pIntermediate; + + // modify the instructions + pIntermediate->MAC = MAC_DP3; + pIntermediate->Output.Type = IMD_OUTPUT_R; + pIntermediate->Output.Address = X_VSH_TEMP_SCRATCH; + VshSetOutputMask(&pIntermediate->Output, TRUE, TRUE, TRUE, TRUE); + + TmpIntermediate.MAC = MAC_ADD; + TmpIntermediate.Parameters[0].IndexesWithA0_X = FALSE; + TmpIntermediate.Parameters[0].Parameter.ParameterType = PARAM_R; + TmpIntermediate.Parameters[0].Parameter.Address = X_VSH_TEMP_SCRATCH; + TmpIntermediate.Parameters[0].Parameter.Neg = FALSE; + VshSetSwizzle(&TmpIntermediate.Parameters[1], SWIZZLE_W, SWIZZLE_W, SWIZZLE_W, SWIZZLE_W); + // Is this output register a scalar + if (TmpIntermediate.Output.Type == IMD_OUTPUT_O) { + if ((TmpIntermediate.Output.Address == OREG_OFOG) || (TmpIntermediate.Output.Address == OREG_OPTS)) { + // This fixes test case "Namco Museum 50th Anniversary" + // The PC shader assembler doesn't like masks on scalar registers + VshSetOutputMask(&TmpIntermediate.Output, TRUE, TRUE, TRUE, TRUE); + // Make the first source parameter use the w swizzle too + VshSetSwizzle(&TmpIntermediate.Parameters[0], SWIZZLE_W, SWIZZLE_W, SWIZZLE_W, SWIZZLE_W); + } + } + + VshInsertIntermediate(pShader, &TmpIntermediate, i + 1); + } + else + { + VSH_INTERMEDIATE_FORMAT TmpIntermediate = *pIntermediate; + pIntermediate->MAC = MAC_DP3; + TmpIntermediate.MAC = MAC_ADD; + TmpIntermediate.Parameters[0].IndexesWithA0_X = FALSE; + TmpIntermediate.Parameters[0].Parameter.ParameterType = PARAM_R; + TmpIntermediate.Parameters[0].Parameter.Address = TmpIntermediate.Output.Address; + TmpIntermediate.Parameters[0].Parameter.Neg = FALSE; + + int swizzle = (TmpIntermediate.Output.Mask[0]) | (TmpIntermediate.Output.Mask[1] << 1) | (TmpIntermediate.Output.Mask[2] << 2) | (TmpIntermediate.Output.Mask[3] << 3); + switch (swizzle) + { + case 1: + VshSetSwizzle(&TmpIntermediate.Parameters[0], SWIZZLE_X, SWIZZLE_X, SWIZZLE_X, SWIZZLE_X); + break; + case 2: + VshSetSwizzle(&TmpIntermediate.Parameters[0], SWIZZLE_Y, SWIZZLE_Y, SWIZZLE_Y, SWIZZLE_Y); + break; + case 4: + VshSetSwizzle(&TmpIntermediate.Parameters[0], SWIZZLE_Z, SWIZZLE_Z, SWIZZLE_Z, SWIZZLE_Z); + break; + case 8: + VshSetSwizzle(&TmpIntermediate.Parameters[0], SWIZZLE_W, SWIZZLE_W, SWIZZLE_W, SWIZZLE_W); + break; + case 15: + default: + VshSetSwizzle(&TmpIntermediate.Parameters[0], SWIZZLE_X, SWIZZLE_Y, SWIZZLE_Z, SWIZZLE_W); + break; + } + //VshSetSwizzle(&TmpIntermediate.Parameters[0], SWIZZLE_W, SWIZZLE_W, SWIZZLE_W, SWIZZLE_W); + VshSetSwizzle(&TmpIntermediate.Parameters[1], SWIZZLE_W, SWIZZLE_W, SWIZZLE_W, SWIZZLE_W); + //VshSetOutputMask(&TmpIntermediate.Output, FALSE, FALSE, FALSE, TRUE); + VshInsertIntermediate(pShader, &TmpIntermediate, i + 1); + } + i++; + } + } + + // Replace all writes to oPos with writes to r12. + // On Xbox, oPos is read/write, essentially a 13th temporary register + // In DX9 and vs_2_x, oPos is write-only, so we'll use r12 in its place + // And at the end of the shader, write r12 to oPos + for (int i = 0; i < pShader->IntermediateCount; i++) { + VSH_INTERMEDIATE_FORMAT* pIntermediate = &pShader->Intermediate[i]; + if (pIntermediate->Output.Type == IMD_OUTPUT_O && pIntermediate->Output.Address == OREG_OPOS) { + pIntermediate->Output.Type = IMD_OUTPUT_R; + pIntermediate->Output.Address = X_VSH_TEMP_OPOS; + } + } + + // We append one additional instruction to mov oPos, r12 + // TODO : *IF* r12 is not read after the final write to oPos, + // it'd be more efficient to not-replace this oPos write by r12, + // so that we don't have to do the following : + VSH_INTERMEDIATE_FORMAT MovIntermediate = {0}; + MovIntermediate.MAC = MAC_MOV; + MovIntermediate.Output.Type = IMD_OUTPUT_O; + MovIntermediate.Output.Address = OREG_OPOS; + MovIntermediate.Output.Mask[0] = true; + MovIntermediate.Output.Mask[1] = true; + MovIntermediate.Output.Mask[2] = true; + MovIntermediate.Output.Mask[3] = true; + MovIntermediate.Parameters[0].Active = true; + MovIntermediate.Parameters[0].Parameter.ParameterType = PARAM_R; + MovIntermediate.Parameters[0].Parameter.Address = X_VSH_TEMP_OPOS; + MovIntermediate.Parameters[0].Parameter.Swizzle[0] = SWIZZLE_X; + MovIntermediate.Parameters[0].Parameter.Swizzle[1] = SWIZZLE_Y; + MovIntermediate.Parameters[0].Parameter.Swizzle[2] = SWIZZLE_Z; + MovIntermediate.Parameters[0].Parameter.Swizzle[3] = SWIZZLE_W; + VshInsertIntermediate(pShader, &MovIntermediate, pShader->IntermediateCount); + + return TRUE; +} + +// **************************************************************************** +// * Vertex shader declaration recompiler +// **************************************************************************** + +class XboxVertexDeclarationConverter +{ +protected: + // Internal variables + CxbxVertexShaderInfo* pVertexShaderInfoToSet; + CxbxVertexShaderStreamInfo* pCurrentVertexShaderStreamInfo = nullptr; + DWORD hostTemporaryRegisterCount; + bool IsFixedFunction; + D3DVERTEXELEMENT* pRecompiled; + +public: + // Output + DWORD XboxDeclarationCount; + DWORD HostDeclarationSize; + +private: + // VERTEX SHADER + + static DWORD VshGetDeclarationCount(DWORD *pXboxDeclaration) + { + DWORD Pos = 0; + while (pXboxDeclaration[Pos] != X_D3DVSD_END()) + { + Pos++; + } + + return Pos + 1; + } + + static inline DWORD VshGetTokenType(DWORD XboxToken) + { + return (XboxToken & X_D3DVSD_TOKENTYPEMASK) >> X_D3DVSD_TOKENTYPESHIFT; + } + + static inline WORD VshGetVertexStream(DWORD XboxToken) + { + return (XboxToken & X_D3DVSD_STREAMNUMBERMASK) >> X_D3DVSD_STREAMNUMBERSHIFT; + } + + inline DWORD VshGetVertexRegister(DWORD XboxToken) + { + DWORD regNum = (XboxToken & X_D3DVSD_VERTEXREGMASK) >> X_D3DVSD_VERTEXREGSHIFT; + if (regNum >= hostTemporaryRegisterCount /*12 for D3D8, D3D9 value depends on host GPU */) { + // test-case : BLiNX: the time sweeper + // test-case : Lego Star Wars + LOG_TEST_CASE("RegNum > NumTemps"); + } + return regNum; + } + + inline DWORD VshGetVertexRegisterIn(DWORD XboxToken) + { + DWORD regNum = (XboxToken & X_D3DVSD_VERTEXREGINMASK) >> X_D3DVSD_VERTEXREGINSHIFT; + if (regNum >= hostTemporaryRegisterCount /*12 for D3D8, D3D9 value depends on host GPU */) { + // test-case : Lego Star Wars + LOG_TEST_CASE("RegNum > NumTemps"); + } + return regNum; + } + + void VshDumpXboxDeclaration(DWORD* pXboxDeclaration) + { + DbgVshPrintf("DWORD dwVSHDecl[] =\n{\n"); + unsigned iNumberOfVertexStreams = 0; + bool bStreamNeedsPatching = false; + auto pXboxToken = pXboxDeclaration; + while (*pXboxToken != X_D3DVSD_END()) // X_D3DVSD_TOKEN_END + { + DWORD Step = 1; + + switch (VshGetTokenType(*pXboxToken)) { + case XTL::X_D3DVSD_TOKEN_NOP: { + DbgVshPrintf("\tD3DVSD_NOP(),\n"); + break; + } + case XTL::X_D3DVSD_TOKEN_STREAM: { + if (*pXboxToken & X_D3DVSD_STREAMTESSMASK) { + DbgVshPrintf("\tD3DVSD_STREAM_TESS(),\n"); + } else { + if (iNumberOfVertexStreams > 0) { + DbgVshPrintf("\t// NeedPatching: %d\n", bStreamNeedsPatching); + } + DWORD StreamNumber = VshGetVertexStream(*pXboxToken); + DbgVshPrintf("\tD3DVSD_STREAM(%u),\n", StreamNumber); + iNumberOfVertexStreams++; + bStreamNeedsPatching = false; + } + break; + } + case XTL::X_D3DVSD_TOKEN_STREAMDATA: { + if (*pXboxToken & X_D3DVSD_MASK_SKIP) { + WORD SkipCount = (*pXboxToken & X_D3DVSD_SKIPCOUNTMASK) >> X_D3DVSD_SKIPCOUNTSHIFT; + if (*pXboxToken & X_D3DVSD_MASK_SKIPBYTES) { + DbgVshPrintf("\tD3DVSD_SKIPBYTES(%d), /* xbox ext. */\n", SkipCount); + } else { + DbgVshPrintf("\tD3DVSD_SKIP(%d),\n", SkipCount); + } + } else { + DWORD VertexRegister = VshGetVertexRegister(*pXboxToken); + if (IsFixedFunction) { + DbgVshPrintf("\t\tD3DVSD_REG(%s, ", XboxVertexRegisterAsString(VertexRegister)); + } else { + DbgVshPrintf("\t\tD3DVSD_REG(%d, ", (BYTE)VertexRegister); + } + + DWORD XboxVertexElementDataType = (*pXboxToken & X_D3DVSD_DATATYPEMASK) >> X_D3DVSD_DATATYPESHIFT; + switch (XboxVertexElementDataType) { + case XTL::X_D3DVSDT_FLOAT1: // 0x12: + DbgVshPrintf("D3DVSDT_FLOAT1"); + break; + case XTL::X_D3DVSDT_FLOAT2: // 0x22: + DbgVshPrintf("D3DVSDT_FLOAT2"); + break; + case XTL::X_D3DVSDT_FLOAT3: // 0x32: + DbgVshPrintf("D3DVSDT_FLOAT3"); + break; + case XTL::X_D3DVSDT_FLOAT4: // 0x42: + DbgVshPrintf("D3DVSDT_FLOAT4"); + break; + case XTL::X_D3DVSDT_D3DCOLOR: // 0x40: + DbgVshPrintf("D3DVSDT_D3DCOLOR"); + break; + case XTL::X_D3DVSDT_SHORT2: // 0x25: + DbgVshPrintf("D3DVSDT_SHORT2"); + break; + case XTL::X_D3DVSDT_SHORT4: // 0x45: + DbgVshPrintf("D3DVSDT_SHORT4"); + break; + case XTL::X_D3DVSDT_NORMSHORT1: // 0x11: + DbgVshPrintf("D3DVSDT_NORMSHORT1 /* xbox ext. */"); + bStreamNeedsPatching = true; + break; + case XTL::X_D3DVSDT_NORMSHORT2: // 0x21: + if (g_D3DCaps.DeclTypes & D3DDTCAPS_SHORT2N) { + DbgVshPrintf("D3DVSDT_NORMSHORT2"); + } else { + DbgVshPrintf("D3DVSDT_NORMSHORT2 /* xbox ext. */"); + bStreamNeedsPatching = true; + } + break; + case XTL::X_D3DVSDT_NORMSHORT3: // 0x31: + DbgVshPrintf("D3DVSDT_NORMSHORT3 /* xbox ext. */"); + bStreamNeedsPatching = true; + break; + case XTL::X_D3DVSDT_NORMSHORT4: // 0x41: + if (g_D3DCaps.DeclTypes & D3DDTCAPS_SHORT4N) { + DbgVshPrintf("D3DVSDT_NORMSHORT4"); + // No need for patching in D3D9 + } else { + DbgVshPrintf("D3DVSDT_NORMSHORT4 /* xbox ext. */"); + bStreamNeedsPatching = true; + } + break; + case XTL::X_D3DVSDT_NORMPACKED3: // 0x16: + DbgVshPrintf("D3DVSDT_NORMPACKED3 /* xbox ext. */"); + bStreamNeedsPatching = true; + break; + case XTL::X_D3DVSDT_SHORT1: // 0x15: + DbgVshPrintf("D3DVSDT_SHORT1 /* xbox ext. */"); + bStreamNeedsPatching = true; + break; + case XTL::X_D3DVSDT_SHORT3: // 0x35: + DbgVshPrintf("D3DVSDT_SHORT3 /* xbox ext. */"); + bStreamNeedsPatching = true; + break; + case XTL::X_D3DVSDT_PBYTE1: // 0x14: + DbgVshPrintf("D3DVSDT_PBYTE1 /* xbox ext. */"); + bStreamNeedsPatching = true; + break; + case XTL::X_D3DVSDT_PBYTE2: // 0x24: + DbgVshPrintf("D3DVSDT_PBYTE2 /* xbox ext. */"); + bStreamNeedsPatching = true; + break; + case XTL::X_D3DVSDT_PBYTE3: // 0x34: + DbgVshPrintf("D3DVSDT_PBYTE3 /* xbox ext. */"); + bStreamNeedsPatching = true; + break; + case XTL::X_D3DVSDT_PBYTE4: // 0x44: + if (g_D3DCaps.DeclTypes & D3DDTCAPS_UBYTE4N) { + DbgVshPrintf("D3DVSDT_PBYTE4"); + } else { + DbgVshPrintf("D3DVSDT_PBYTE4 /* xbox ext. */"); + bStreamNeedsPatching = true; + } + break; + case XTL::X_D3DVSDT_FLOAT2H: // 0x72: + DbgVshPrintf("D3DVSDT_FLOAT2H /* xbox ext. */"); + bStreamNeedsPatching = true; + break; + case XTL::X_D3DVSDT_NONE: // 0x02: + DbgVshPrintf("D3DVSDT_NONE /* xbox ext. */"); + break; + default: + DbgVshPrintf("Unknown data type for D3DVSD_REG: 0x%02X\n", XboxVertexElementDataType); + break; + } + + DbgVshPrintf("),\n"); + }; + break; + } + case XTL::X_D3DVSD_TOKEN_TESSELLATOR: { + DWORD VertexRegisterOut = VshGetVertexRegister(*pXboxToken); + if (*pXboxToken & X_D3DVSD_MASK_TESSUV) { + DbgVshPrintf("\tD3DVSD_TESSUV(%s),\n", XboxVertexRegisterAsString(VertexRegisterOut)); + } else { // D3DVSD_TESSNORMAL + DWORD VertexRegisterIn = VshGetVertexRegisterIn(*pXboxToken); + DbgVshPrintf("\tD3DVSD_TESSNORMAL(%s, %s),\n", + XboxVertexRegisterAsString(VertexRegisterIn), + XboxVertexRegisterAsString(VertexRegisterOut)); + } + break; + } + case XTL::X_D3DVSD_TOKEN_CONSTMEM: { + DWORD ConstantAddress = (*pXboxToken & X_D3DVSD_CONSTADDRESSMASK) >> X_D3DVSD_CONSTADDRESSSHIFT; + DWORD Count = (*pXboxToken & X_D3DVSD_CONSTCOUNTMASK) >> X_D3DVSD_CONSTCOUNTSHIFT; + DbgVshPrintf("\tD3DVSD_CONST(%d, %d),\n", ConstantAddress, Count); + LOG_TEST_CASE("X_D3DVSD_TOKEN_CONSTMEM"); + Step = Count * 4 + 1; + break; + } + case XTL::X_D3DVSD_TOKEN_EXT: { + DWORD ExtInfo = (*pXboxToken & X_D3DVSD_EXTINFOMASK) >> X_D3DVSD_EXTINFOSHIFT; + DWORD Count = (*pXboxToken & X_D3DVSD_EXTCOUNTMASK) >> X_D3DVSD_EXTCOUNTSHIFT; + DbgVshPrintf("\tD3DVSD_EXT(%d, %d),\n", ExtInfo, Count); + LOG_TEST_CASE("X_D3DVSD_TOKEN_EXT"); + Step = Count * 4 + 1; // TODO : Is this correct? + break; + } + default: + DbgVshPrintf("Unknown token type: %d\n", VshGetTokenType(*pXboxToken)); + break; + } + + pXboxToken += Step; + } + + if (iNumberOfVertexStreams > 0) { + DbgVshPrintf("\t// NeedPatching: %d\n", bStreamNeedsPatching); + } + + DbgVshPrintf("\tD3DVSD_END()\n};\n"); + + DbgVshPrintf("// NbrStreams: %d\n", iNumberOfVertexStreams); + } + + void VshConvertToken_NOP(DWORD *pXboxToken) + { + if(*pXboxToken != X_D3DVSD_NOP()) + { + LOG_TEST_CASE("Token NOP found, but extra parameters are given!"); + } + } + + DWORD VshConvertToken_CONSTMEM(DWORD *pXboxToken) + { + // DWORD ConstantAddress = (*pXboxToken & X_D3DVSD_CONSTADDRESSMASK) >> X_D3DVSD_CONSTADDRESSSHIFT; + DWORD Count = (*pXboxToken & X_D3DVSD_CONSTCOUNTMASK) >> X_D3DVSD_CONSTCOUNTSHIFT; + LOG_TEST_CASE("CONST"); // TODO : Implement + return Count * 4 + 1; + } + + void VshConvertToken_TESSELATOR(DWORD *pXboxToken) + { + BYTE Index; + + if(*pXboxToken & X_D3DVSD_MASK_TESSUV) + { + DWORD VertexRegister = VshGetVertexRegister(*pXboxToken); + DWORD NewVertexRegister = VertexRegister; + + NewVertexRegister = Xb2PCRegisterType(VertexRegister, Index); + // TODO : Expand on the setting of this TESSUV register element : + pRecompiled->Usage = D3DDECLUSAGE(NewVertexRegister); + pRecompiled->UsageIndex = Index; + } + else // D3DVSD_TESSNORMAL + { + DWORD VertexRegisterIn = VshGetVertexRegisterIn(*pXboxToken); + DWORD VertexRegisterOut = VshGetVertexRegister(*pXboxToken); + + DWORD NewVertexRegisterIn = VertexRegisterIn; + DWORD NewVertexRegisterOut = VertexRegisterOut; + + NewVertexRegisterIn = Xb2PCRegisterType(VertexRegisterIn, Index); + // TODO : Expand on the setting of this TESSNORMAL input register element : + pRecompiled->Usage = D3DDECLUSAGE(NewVertexRegisterIn); + pRecompiled->UsageIndex = Index; + + NewVertexRegisterOut = Xb2PCRegisterType(VertexRegisterOut, Index); + // TODO : Expand on the setting of this TESSNORMAL output register element : + pRecompiled++; + pRecompiled->Usage = D3DDECLUSAGE(NewVertexRegisterOut); + pRecompiled->UsageIndex = Index; + } + } + + void VshConvertToken_STREAM(DWORD *pXboxToken) + { + // D3DVSD_STREAM_TESS + if(*pXboxToken & X_D3DVSD_STREAMTESSMASK) + { + // TODO + } + else // D3DVSD_STREAM + { + DWORD StreamNumber = VshGetVertexStream(*pXboxToken); + + // new stream + pCurrentVertexShaderStreamInfo = &(pVertexShaderInfoToSet->VertexStreams[StreamNumber]); + pCurrentVertexShaderStreamInfo->NeedPatch = FALSE; + pCurrentVertexShaderStreamInfo->DeclPosition = FALSE; + pCurrentVertexShaderStreamInfo->CurrentStreamNumber = 0; + pCurrentVertexShaderStreamInfo->HostVertexStride = 0; + pCurrentVertexShaderStreamInfo->NumberOfVertexElements = 0; + + // Dxbx note : Use Dophin(s), FieldRender, MatrixPaletteSkinning and PersistDisplay as a testcase + + pCurrentVertexShaderStreamInfo->CurrentStreamNumber = VshGetVertexStream(*pXboxToken); + pVertexShaderInfoToSet->NumberOfVertexStreams++; + // TODO : Keep a bitmask for all StreamNumber's seen? + } + } + + void VshConvert_RegisterVertexElement( + UINT XboxVertexElementDataType, + UINT XboxVertexElementByteSize, + UINT HostVertexElementByteSize, + BOOL NeedPatching) + { + CxbxVertexShaderStreamElement* pCurrentElement = &(pCurrentVertexShaderStreamInfo->VertexElements[pCurrentVertexShaderStreamInfo->NumberOfVertexElements]); + pCurrentElement->XboxType = XboxVertexElementDataType; + pCurrentElement->XboxByteSize = XboxVertexElementByteSize; + pCurrentElement->HostByteSize = HostVertexElementByteSize; + pCurrentVertexShaderStreamInfo->NumberOfVertexElements++; + pCurrentVertexShaderStreamInfo->NeedPatch |= NeedPatching; + } + + void VshConvert_SkipBytes(int SkipBytesCount) + { + if (SkipBytesCount % sizeof(DWORD)) { + LOG_TEST_CASE("D3DVSD_SKIPBYTES not divisble by 4!"); + } +#if 0 // Potential optimization, for now disabled for simplicity : + else { + // Skip size is a whole multiple of 4 bytes; + // Is stream patching not needed up until this element? + if (!pCurrentVertexShaderStreamInfo->NeedPatch) { + // Then we can get away with increasing the host stride, + // which avoids otherwise needless vertex buffer patching : + pCurrentVertexShaderStreamInfo->HostVertexStride += SkipBytesCount; + return; + } + } +#endif + + // Register a 'skip' element, so that Xbox data will be skipped + // without increasing host stride - this does require patching : + VshConvert_RegisterVertexElement(XTL::X_D3DVSDT_NONE, SkipBytesCount, /*HostSize=*/0, /*NeedPatching=*/TRUE); + } + + void VshConvertToken_STREAMDATA_SKIP(DWORD *pXboxToken) + { + WORD SkipCount = (*pXboxToken & X_D3DVSD_SKIPCOUNTMASK) >> X_D3DVSD_SKIPCOUNTSHIFT; + VshConvert_SkipBytes(SkipCount * sizeof(DWORD)); + } + + void VshConvertToken_STREAMDATA_SKIPBYTES(DWORD* pXboxToken) + { + WORD SkipBytesCount = (*pXboxToken & X_D3DVSD_SKIPCOUNTMASK) >> X_D3DVSD_SKIPCOUNTSHIFT; + VshConvert_SkipBytes(SkipBytesCount); + } + + void VshConvertToken_STREAMDATA_REG(DWORD *pXboxToken) + { + DWORD VertexRegister = VshGetVertexRegister(*pXboxToken); + BOOL NeedPatching = FALSE; + BYTE Index; + BYTE HostVertexRegisterType; + + if (IsFixedFunction) { + HostVertexRegisterType = Xb2PCRegisterType(VertexRegister, Index); + } else { + // D3DDECLUSAGE_TEXCOORD can be useds for any user-defined data + // We need this because there is no reliable way to detect the real usage + // Xbox has no concept of 'usage types', it only requires a list of attribute register numbers. + // So we treat them all as 'user-defined' with an Index of the Vertex Register Index + // this prevents information loss in shaders due to non-matching dcl types! + HostVertexRegisterType = D3DDECLUSAGE_TEXCOORD; + Index = (BYTE)VertexRegister; + } + + // Add this register to the list of declared registers + RegVIsPresentInDeclaration[VertexRegister] = true; + + DWORD XboxVertexElementDataType = (*pXboxToken & X_D3DVSD_DATATYPEMASK) >> X_D3DVSD_DATATYPESHIFT; + WORD XboxVertexElementByteSize = 0; + BYTE HostVertexElementDataType = 0; + WORD HostVertexElementByteSize = 0; + + switch (XboxVertexElementDataType) + { + case XTL::X_D3DVSDT_FLOAT1: // 0x12: + HostVertexElementDataType = D3DDECLTYPE_FLOAT1; + HostVertexElementByteSize = 1 * sizeof(FLOAT); + break; + case XTL::X_D3DVSDT_FLOAT2: // 0x22: + HostVertexElementDataType = D3DDECLTYPE_FLOAT2; + HostVertexElementByteSize = 2 * sizeof(FLOAT); + break; + case XTL::X_D3DVSDT_FLOAT3: // 0x32: + HostVertexElementDataType = D3DDECLTYPE_FLOAT3; + HostVertexElementByteSize = 3 * sizeof(FLOAT); + break; + case XTL::X_D3DVSDT_FLOAT4: // 0x42: + HostVertexElementDataType = D3DDECLTYPE_FLOAT4; + HostVertexElementByteSize = 4 * sizeof(FLOAT); + break; + case XTL::X_D3DVSDT_D3DCOLOR: // 0x40: + HostVertexElementDataType = D3DDECLTYPE_D3DCOLOR; + HostVertexElementByteSize = 1 * sizeof(D3DCOLOR); + break; + case XTL::X_D3DVSDT_SHORT2: // 0x25: + HostVertexElementDataType = D3DDECLTYPE_SHORT2; + HostVertexElementByteSize = 2 * sizeof(SHORT); + break; + case XTL::X_D3DVSDT_SHORT4: // 0x45: + HostVertexElementDataType = D3DDECLTYPE_SHORT4; + HostVertexElementByteSize = 4 * sizeof(SHORT); + break; + case XTL::X_D3DVSDT_NORMSHORT1: // 0x11: + if (g_D3DCaps.DeclTypes & D3DDTCAPS_SHORT2N) { + HostVertexElementDataType = D3DDECLTYPE_SHORT2N; + HostVertexElementByteSize = 2 * sizeof(SHORT); + } + else + { + HostVertexElementDataType = D3DDECLTYPE_FLOAT1; + HostVertexElementByteSize = 1 * sizeof(FLOAT); + } + XboxVertexElementByteSize = 1 * sizeof(XTL::SHORT); + NeedPatching = TRUE; + break; + case XTL::X_D3DVSDT_NORMSHORT2: // 0x21: + if (g_D3DCaps.DeclTypes & D3DDTCAPS_SHORT2N) { + HostVertexElementDataType = D3DDECLTYPE_SHORT2N; + HostVertexElementByteSize = 2 * sizeof(SHORT); + // No need for patching in D3D9 + } + else + { + HostVertexElementDataType = D3DDECLTYPE_FLOAT2; + HostVertexElementByteSize = 2 * sizeof(FLOAT); + XboxVertexElementByteSize = 2 * sizeof(XTL::SHORT); + NeedPatching = TRUE; + } + break; + case XTL::X_D3DVSDT_NORMSHORT3: // 0x31: + if (g_D3DCaps.DeclTypes & D3DDTCAPS_SHORT4N) { + HostVertexElementDataType = D3DDECLTYPE_SHORT4N; + HostVertexElementByteSize = 4 * sizeof(SHORT); + } + else + { + HostVertexElementDataType = D3DDECLTYPE_FLOAT3; + HostVertexElementByteSize = 3 * sizeof(FLOAT); + } + XboxVertexElementByteSize = 3 * sizeof(XTL::SHORT); + NeedPatching = TRUE; + break; + case XTL::X_D3DVSDT_NORMSHORT4: // 0x41: + if (g_D3DCaps.DeclTypes & D3DDTCAPS_SHORT4N) { + HostVertexElementDataType = D3DDECLTYPE_SHORT4N; + HostVertexElementByteSize = 4 * sizeof(SHORT); + // No need for patching in D3D9 + } + else + { + HostVertexElementDataType = D3DDECLTYPE_FLOAT4; + HostVertexElementByteSize = 4 * sizeof(FLOAT); + XboxVertexElementByteSize = 4 * sizeof(XTL::SHORT); + NeedPatching = TRUE; + } + break; + case XTL::X_D3DVSDT_NORMPACKED3: // 0x16: + HostVertexElementDataType = D3DDECLTYPE_FLOAT3; + HostVertexElementByteSize = 3 * sizeof(FLOAT); + XboxVertexElementByteSize = 1 * sizeof(XTL::DWORD); + NeedPatching = TRUE; + break; + case XTL::X_D3DVSDT_SHORT1: // 0x15: + HostVertexElementDataType = D3DDECLTYPE_SHORT2; + HostVertexElementByteSize = 2 * sizeof(SHORT); + XboxVertexElementByteSize = 1 * sizeof(XTL::SHORT); + NeedPatching = TRUE; + break; + case XTL::X_D3DVSDT_SHORT3: // 0x35: + HostVertexElementDataType = D3DDECLTYPE_SHORT4; + HostVertexElementByteSize = 4 * sizeof(SHORT); + XboxVertexElementByteSize = 3 * sizeof(XTL::SHORT); + NeedPatching = TRUE; + break; + case XTL::X_D3DVSDT_PBYTE1: // 0x14: + if (g_D3DCaps.DeclTypes & D3DDTCAPS_UBYTE4N) { + HostVertexElementDataType = D3DDECLTYPE_UBYTE4N; + HostVertexElementByteSize = 4 * sizeof(BYTE); + } + else + { + HostVertexElementDataType = D3DDECLTYPE_FLOAT1; + HostVertexElementByteSize = 1 * sizeof(FLOAT); + } + XboxVertexElementByteSize = 1 * sizeof(XTL::BYTE); + NeedPatching = TRUE; + break; + case XTL::X_D3DVSDT_PBYTE2: // 0x24: + if (g_D3DCaps.DeclTypes & D3DDTCAPS_UBYTE4N) { + HostVertexElementDataType = D3DDECLTYPE_UBYTE4N; + HostVertexElementByteSize = 4 * sizeof(BYTE); + } + else + { + HostVertexElementDataType = D3DDECLTYPE_FLOAT2; + HostVertexElementByteSize = 2 * sizeof(FLOAT); + } + XboxVertexElementByteSize = 2 * sizeof(XTL::BYTE); + NeedPatching = TRUE; + break; + case XTL::X_D3DVSDT_PBYTE3: // 0x34: + if (g_D3DCaps.DeclTypes & D3DDTCAPS_UBYTE4N) { + HostVertexElementDataType = D3DDECLTYPE_UBYTE4N; + HostVertexElementByteSize = 4 * sizeof(BYTE); + } + else + { + HostVertexElementDataType = D3DDECLTYPE_FLOAT3; + HostVertexElementByteSize = 3 * sizeof(FLOAT); + } + XboxVertexElementByteSize = 3 * sizeof(XTL::BYTE); + NeedPatching = TRUE; + break; + case XTL::X_D3DVSDT_PBYTE4: // 0x44: + // Test-case : Panzer + if (g_D3DCaps.DeclTypes & D3DDTCAPS_UBYTE4N) { + HostVertexElementDataType = D3DDECLTYPE_UBYTE4N; + HostVertexElementByteSize = 4 * sizeof(BYTE); + // No need for patching when D3D9 supports D3DDECLTYPE_UBYTE4N + } + else + { + HostVertexElementDataType = D3DDECLTYPE_FLOAT4; + HostVertexElementByteSize = 4 * sizeof(FLOAT); + XboxVertexElementByteSize = 4 * sizeof(XTL::BYTE); + NeedPatching = TRUE; + } + break; + case XTL::X_D3DVSDT_FLOAT2H: // 0x72: + HostVertexElementDataType = D3DDECLTYPE_FLOAT4; + HostVertexElementByteSize = 4 * sizeof(FLOAT); + XboxVertexElementByteSize = 3 * sizeof(FLOAT); + NeedPatching = TRUE; + break; + case XTL::X_D3DVSDT_NONE: // 0x02: + // No host element data, so no patching + break; + default: + //LOG_TEST_CASE("Unknown data type for D3DVSD_REG: 0x%02X\n", XboxVertexElementDataType); + break; + } + + // On X_D3DVSDT_NONE skip this token + if (XboxVertexElementDataType == XTL::X_D3DVSDT_NONE) + { + // Xbox elements with X_D3DVSDT_NONE have size zero, so there's no need to register those. + // Note, that for skip tokens, we DO call VshConvert_RegisterVertexElement with a X_D3DVSDT_NONE! + return; + } + + // save patching information + VshConvert_RegisterVertexElement( + XboxVertexElementDataType, + NeedPatching ? XboxVertexElementByteSize : HostVertexElementByteSize, + HostVertexElementByteSize, + NeedPatching); + + pRecompiled->Stream = pCurrentVertexShaderStreamInfo->CurrentStreamNumber; + pRecompiled->Offset = pCurrentVertexShaderStreamInfo->HostVertexStride; + pRecompiled->Type = HostVertexElementDataType; + pRecompiled->Method = D3DDECLMETHOD_DEFAULT; + pRecompiled->Usage = HostVertexRegisterType; + pRecompiled->UsageIndex = Index; + + pRecompiled++; + + pCurrentVertexShaderStreamInfo->HostVertexStride += HostVertexElementByteSize; + } + + void VshConvertToken_STREAMDATA(DWORD *pXboxToken) + { + if (*pXboxToken & X_D3DVSD_MASK_SKIP) + { + // For D3D9, use D3DDECLTYPE_UNUSED ? + if (*pXboxToken & X_D3DVSD_MASK_SKIPBYTES) { + VshConvertToken_STREAMDATA_SKIPBYTES(pXboxToken); + } else { + VshConvertToken_STREAMDATA_SKIP(pXboxToken); + } + } + else // D3DVSD_REG + { + VshConvertToken_STREAMDATA_REG(pXboxToken); + } + } + + DWORD VshRecompileToken(DWORD *pXboxToken) + { + DWORD Step = 1; + + switch(VshGetTokenType(*pXboxToken)) + { + case XTL::X_D3DVSD_TOKEN_NOP: + VshConvertToken_NOP(pXboxToken); + break; + case XTL::X_D3DVSD_TOKEN_STREAM: + { + VshConvertToken_STREAM(pXboxToken); + break; + } + case XTL::X_D3DVSD_TOKEN_STREAMDATA: + { + VshConvertToken_STREAMDATA(pXboxToken); + break; + } + case XTL::X_D3DVSD_TOKEN_TESSELLATOR: + { + VshConvertToken_TESSELATOR(pXboxToken); + break; + } + case XTL::X_D3DVSD_TOKEN_CONSTMEM: + { + Step = VshConvertToken_CONSTMEM(pXboxToken); + break; + } + default: + //LOG_TEST_CASE("Unknown token type: %d\n", VshGetTokenType(*pXboxToken)); + break; + } + + return Step; + } + + DWORD* RemoveXboxDeclarationRedefinition(DWORD* pXboxDeclaration) + { + // Detect and remove register redefinitions by preprocessing the Xbox Vertex Declaration + // Test Case: King Kong + + // Find the last token + DWORD* pXboxToken = pXboxDeclaration; + while (*pXboxToken != X_D3DVSD_END()){ + pXboxToken++; + } + + // Operate on a copy of the Xbox declaration, rather than messing with the Xbox's memory + auto declarationBytes = sizeof(DWORD) * (pXboxToken - pXboxDeclaration + 1); + auto pXboxDeclarationCopy = (DWORD*)malloc(declarationBytes); + memcpy(pXboxDeclarationCopy, pXboxDeclaration, declarationBytes); + pXboxToken = pXboxDeclarationCopy + (pXboxToken - pXboxDeclaration); // Move to end of the copy + + // Remember if we've seen a given output register + std::bitset<16> seen; + + // We want to keep later definitions, and remove earlier ones + // Scan back from the end of the declaration, and replace redefinitions with nops + while (pXboxToken > pXboxDeclarationCopy) { + auto type = VshGetTokenType(*pXboxToken); + if (type == XTL::X_D3DVSD_TOKEN_STREAMDATA && !(*pXboxToken & X_D3DVSD_MASK_SKIP) || + type == XTL::X_D3DVSD_TOKEN_TESSELLATOR) + { + auto outputRegister = VshGetVertexRegister(*pXboxToken); + if (seen[outputRegister]) + { + // Blank out tokens for mapped registers + *pXboxToken = X_D3DVSD_NOP(); + EmuLog(LOG_LEVEL::DEBUG, "Replacing duplicate definition of register %d with D3DVSD_NOP", outputRegister); + } + else + { + // Mark register as seen + seen[outputRegister] = true; + } + } + + pXboxToken--; + } + + return pXboxDeclarationCopy; + } + +public: + D3DVERTEXELEMENT *Convert(DWORD* pXboxDeclaration, bool bIsFixedFunction, CxbxVertexShaderInfo* pCxbxVertexShaderInfo) + { + // Get a preprocessed copy of the original Xbox Vertex Declaration + auto pXboxVertexDeclarationCopy = RemoveXboxDeclarationRedefinition(pXboxDeclaration); + + pVertexShaderInfoToSet = pCxbxVertexShaderInfo; + hostTemporaryRegisterCount = g_D3DCaps.VS20Caps.NumTemps; + if (hostTemporaryRegisterCount < VSH_MIN_TEMPORARY_REGISTERS) { + LOG_TEST_CASE("g_D3DCaps.VS20Caps.NumTemps < 12 (Host minimal vertex shader temporary register count)"); + } + if (hostTemporaryRegisterCount < 12+1) { // TODO : Use a constant (see X_D3DVSD_REG) + LOG_TEST_CASE("g_D3DCaps.VS20Caps.NumTemps < 12+1 (Xbox vertex shader temporary register count + r12, reading oPos)"); + } + + // Note, that some Direct3D 9 drivers return only the required minimum temporary register count of 12, + // but regardless, shaders that use temporary register numbers above r12 still seem to work correctly. + // So it seems we can't rely on VS20Caps.NumTemps indicating accurately what host hardware supports. + // (Although it could be that the driver switches to software vertex processing when a shader exceeds hardware limits.) + + IsFixedFunction = bIsFixedFunction; + + RegVIsPresentInDeclaration.fill(false); + + // First of all some info: + // We have to figure out which flags are set and then + // we have to patch their params + + // some token values + // 0xFFFFFFFF - end of the declaration + // 0x00000000 - nop (means that this value is ignored) + + // Calculate size of declaration + XboxDeclarationCount = VshGetDeclarationCount(pXboxVertexDeclarationCopy); + // For Direct3D9, we need to reserve at least twice the number of elements, as one token can generate two registers (in and out) : + HostDeclarationSize = XboxDeclarationCount * sizeof(D3DVERTEXELEMENT) * 2; + + D3DVERTEXELEMENT *Result = (D3DVERTEXELEMENT *)calloc(1, HostDeclarationSize); + pRecompiled = Result; + uint8_t *pRecompiledBufferOverflow = ((uint8_t*)pRecompiled) + HostDeclarationSize; + + VshDumpXboxDeclaration(pXboxDeclaration); + + auto pXboxToken = pXboxVertexDeclarationCopy; + while (*pXboxToken != X_D3DVSD_END()) + { + if ((uint8_t*)pRecompiled >= pRecompiledBufferOverflow) { + DbgVshPrintf("Detected buffer-overflow, breaking out...\n"); + break; + } + + DWORD Step = VshRecompileToken(pXboxToken); + pXboxToken += Step; + } + + *pRecompiled = D3DDECL_END(); + + // Ensure valid ordering of the vertex declaration (http://doc.51windows.net/Directx9_SDK/graphics/programmingguide/gettingstarted/vertexdeclaration/vertexdeclaration.htm) + // In particular "All vertex elements for a stream must be consecutive and sorted by offset" + // Test case: King Kong (due to register redefinition) + std::sort(Result, pRecompiled, [] (const auto& x, const auto& y) + { return std::tie(x.Stream, x.Method, x.Offset) < std::tie(y.Stream, y.Method, y.Offset); }); + + // Free the preprocessed declaration copy + free(pXboxVertexDeclarationCopy); + + return Result; + } +}; + +D3DVERTEXELEMENT *EmuRecompileVshDeclaration +( + DWORD *pXboxDeclaration, + bool bIsFixedFunction, + DWORD *pXboxDeclarationCount, + DWORD *pHostDeclarationSize, + CxbxVertexShaderInfo *pCxbxVertexShaderInfo +) +{ + XboxVertexDeclarationConverter Converter; + + D3DVERTEXELEMENT* pHostVertexElements = Converter.Convert(pXboxDeclaration, bIsFixedFunction, pCxbxVertexShaderInfo); + + *pXboxDeclarationCount = Converter.XboxDeclarationCount; + *pHostDeclarationSize = Converter.HostDeclarationSize; + + return pHostVertexElements; +} + +std::string UsingScratch(std::string input) { + return std::regex_replace(input, std::regex("tmp"), "r" + std::to_string(X_VSH_TEMP_SCRATCH)); +} + +// Xbox expp seems to behave the as vs_1_1 +std::string VshPostProcess_Expp(std::string shader) { + // Find usages of exp with each swizzle + // If there's no swizzle, we should still match so we do the calculation + // for all components + const auto xbox_expp_x = std::regex("expp (\\w\\d\\d?)(\\.x)?, (.+)$"); + const auto xbox_expp_y = std::regex("expp (\\w\\d\\d?)(\\.y)?, (.+)$"); + const auto xbox_expp_z = std::regex("expp (\\w\\d\\d?)(\\.z)?, (.+)$"); + const auto xbox_expp_w = std::regex("expp (\\w\\d\\d?)(\\.w)?, (.+)$"); + + // We operate on a scalar so the input should have a swizzle? + + if (std::regex_search(shader, xbox_expp_x)) + LOG_TEST_CASE("Title uses the x component result of expp"); + if (std::regex_search(shader, xbox_expp_w)) + LOG_TEST_CASE("Title uses the w component result of expp"); + + // dest.x = 2 ^ floor(x) + // Test Case: ??? + static auto host_expp_x = UsingScratch( + "; patch expp: dest.x = 2 ^ floor(x)\n" + "frc tmp.x, $3\n" + "add tmp.x, $1$2, -tmp.x\n" + "exp $1.x, tmp.x"); + shader = std::regex_replace(shader, xbox_expp_x, host_expp_x); + + // dest.y = x - floor(x) + // Test Case: Tony Hawk Pro Skater 2X + const auto host_expp_y = + "; patch expp: dest.y = x - floor(x)\n" + "frc $1.y, $3"; + shader = std::regex_replace(shader, xbox_expp_y, host_expp_y); + + // dest.z = approximate 2 ^ x + // Test Case: Mechassault + const auto host_expp_z = + "; patch expp: dest.z = 2 ^ x\n" + "exp $1.z, $3"; + shader = std::regex_replace(shader, xbox_expp_z, host_expp_z); + + // dest.w = 1 + // Test Case: ??? + // TODO do a constant read here + const auto host_expp_w = UsingScratch( + "; patch expp: dest.w = 1\n" + "sub tmp.x, tmp.x, tmp.x\n" // Get 0 + "exp $1.w, tmp.x"); // 2 ^ 0 = 1 + shader = std::regex_replace(shader, xbox_expp_w, host_expp_w); + + return shader; +} + +// On Xbox, the special indexing register, a0.x, is truncated +// But on vs_2_x and up, it's rounded to the closest integer +// So we have to truncate it ourselves +// Test Case: Buffy the Vampire Slayer +std::string VshPostProcess_TruncateMovA(std::string shader) { + // find usages of mova + const auto movA = std::regex("mova a0\\.x, (.*)$"); + // The equivalent of floor() with a temp register + // and use the floored value + static auto truncate = UsingScratch( + "; patch mova: a = floor(x)\n" + "frc tmp, $1\n" + "add tmp, $1, -tmp\n" + "mova a0.x, tmp"); + return std::regex_replace(shader, movA, truncate); +} + +// Post process the shader as a string +std::string VshPostProcess(std::string shader) { + shader = VshPostProcess_Expp(shader); + return VshPostProcess_TruncateMovA(shader); +} + +// recompile xbox vertex shader function +extern HRESULT EmuRecompileVshFunction +( + DWORD *pXboxFunction, + bool bNoReservedConstants, + D3DVERTEXELEMENT *pRecompiledDeclaration, + bool *pbUseDeclarationOnly, + DWORD *pXboxFunctionSize, + LPD3DXBUFFER *ppRecompiledShader +) +{ + XTL::X_VSH_SHADER_HEADER *pXboxVertexShaderHeader = (XTL::X_VSH_SHADER_HEADER*)pXboxFunction; + DWORD *pToken; + boolean EOI = false; + VSH_XBOX_SHADER *pShader = (VSH_XBOX_SHADER*)calloc(1, sizeof(VSH_XBOX_SHADER)); + LPD3DXBUFFER pErrors = nullptr; + HRESULT hRet = 0; + + // TODO: support this situation.. + if(pXboxFunction == xbnullptr) + return E_FAIL; + + // Initialize output arguments to zero + *pbUseDeclarationOnly = 0; + *pXboxFunctionSize = 0; + *ppRecompiledShader = nullptr; + + if(!pShader) + { + EmuLog(LOG_LEVEL::WARNING, "Couldn't allocate memory for vertex shader conversion buffer"); + return E_OUTOFMEMORY; + } + pShader->ShaderHeader = *pXboxVertexShaderHeader; + switch(pXboxVertexShaderHeader->Version) + { + case VERSION_XVS: + break; + case VERSION_XVSS: + EmuLog(LOG_LEVEL::WARNING, "Might not support vertex state shaders?"); + hRet = E_FAIL; + break; + case VERSION_XVSW: + EmuLog(LOG_LEVEL::WARNING, "Might not support vertex read/write shaders?"); + hRet = E_FAIL; + break; + default: + EmuLog(LOG_LEVEL::WARNING, "Unknown vertex shader version 0x%02X", pXboxVertexShaderHeader->Version); + hRet = E_FAIL; + break; + } + + if(SUCCEEDED(hRet)) + { + RegVIsUsedByShader.fill(false); + + for (pToken = (DWORD*)((uint8_t*)pXboxFunction + sizeof(XTL::X_VSH_SHADER_HEADER)); !EOI; pToken += X_VSH_INSTRUCTION_SIZE) + { + VSH_SHADER_INSTRUCTION Inst; + + VshParseInstruction((uint32_t*)pToken, &Inst); + VshConvertToIntermediate(&Inst, pShader); + EOI = (boolean)VshGetField((uint32_t*)pToken, FLD_FINAL); + } + + // The size of the shader is + *pXboxFunctionSize = (intptr_t)pToken - (intptr_t)pXboxFunction; + + // Do not attempt to compile empty shaders + if (pShader->IntermediateCount == 0) { + // This is a declaration only shader, so there is no function to recompile + *pbUseDeclarationOnly = 1; + return D3D_OK; + } + + std::stringstream& pXboxShaderDisassembly = std::stringstream(); + std::stringstream& pHostShaderDisassembly = std::stringstream(); + + DbgVshPrintf("-- Before conversion --\n"); + VshWriteShader(pShader, pXboxShaderDisassembly, pRecompiledDeclaration, FALSE); + DbgVshPrintf("%s", pXboxShaderDisassembly.str().c_str()); + DbgVshPrintf("-----------------------\n"); + + VshConvertShader(pShader, bNoReservedConstants); + VshWriteShader(pShader, pHostShaderDisassembly, pRecompiledDeclaration, TRUE); + + // Post process the final shader + auto finalHostShader = VshPostProcess(pHostShaderDisassembly.str()); + + DbgVshPrintf("-- After conversion ---\n"); + DbgVshPrintf("%s", finalHostShader.c_str()); + DbgVshPrintf("-----------------------\n"); + + // HACK: Azurik. Prevent Direct3D from trying to assemble this. + if(finalHostShader == "vs.2.x\n") + { + EmuLog(LOG_LEVEL::WARNING, "Replacing empty vertex shader with fallback"); + + static const char dummy[] = + "vs.2.x\n" + "dcl_position v0\n" + "dp4 oPos.x, v0, c96\n" + "dp4 oPos.y, v0, c97\n" + "dp4 oPos.z, v0, c98\n" + "dp4 oPos.w, v0, c99\n"; + + hRet = D3DXAssembleShader( + dummy, + strlen(dummy), + /*pDefines=*/nullptr, + /*pInclude=*/nullptr, + /*Flags=*/0, // Was D3DXASM_SKIPVALIDATION, + /*ppCompiledShader=*/ppRecompiledShader, + /*ppCompilationErrors*/nullptr); + } + else + { + hRet = D3DXAssembleShader( + finalHostShader.c_str(), + finalHostShader.length(), + /*pDefines=*/nullptr, + /*pInclude=*/nullptr, + /*Flags=*/0, // Was D3DXASM_SKIPVALIDATION, + /*ppCompiledShader=*/ppRecompiledShader, + /*ppCompilationErrors*/&pErrors); + } + + if (FAILED(hRet)) + { + EmuLog(LOG_LEVEL::WARNING, "Couldn't assemble recompiled vertex shader"); + EmuLog(LOG_LEVEL::WARNING, "%s", pErrors->GetBufferPointer()); + } + + if( pErrors ) + pErrors->Release(); + } + + free(pShader); + + return hRet; +} + +extern void FreeVertexDynamicPatch(CxbxVertexShader *pVertexShader) +{ + pVertexShader->VertexShaderInfo.NumberOfVertexStreams = 0; +} + +// Checks for failed vertex shaders, and shaders that would need patching +boolean VshHandleIsValidShader(DWORD XboxVertexShaderHandle) +{ +#if 0 + //printf( "VS = 0x%.08X\n", XboxVertexShaderHandle ); + + CxbxVertexShader *pCxbxVertexShader = GetCxbxVertexShader(XboxVertexShaderHandle); + if (pCxbxVertexShader) { + if (pCxbxVertexShader->XboxStatus != 0) + { + return FALSE; + } + /* + for (uint32 i = 0; i < pCxbxVertexShader->VertexShaderInfo.NumberOfVertexStreams; i++) + { + if (pCxbxVertexShader->VertexShaderInfo.VertexStreams[i].NeedPatch) + { + // Just for caching purposes + pCxbxVertexShader->XboxStatus = 0x80000001; + return FALSE; + } + } + */ + } +#endif + return TRUE; +} + +extern boolean IsValidCurrentShader(void) +{ + // Dxbx addition : There's no need to call + // XTL_EmuIDirect3DDevice_GetVertexShader, just check g_Xbox_VertexShader_Handle : + return VshHandleIsValidShader(g_Xbox_VertexShader_Handle); +} + +CxbxVertexShaderInfo *GetCxbxVertexShaderInfo(DWORD XboxVertexShaderHandle) +{ + CxbxVertexShader *pCxbxVertexShader = GetCxbxVertexShader(XboxVertexShaderHandle); + + for (uint32_t i = 0; i < pCxbxVertexShader->VertexShaderInfo.NumberOfVertexStreams; i++) + { + if (pCxbxVertexShader->VertexShaderInfo.VertexStreams[i].NeedPatch) + { + return &pCxbxVertexShader->VertexShaderInfo; + } + } + return nullptr; +} + +std::unordered_map g_CxbxVertexShaders; + +CxbxVertexShader* GetCxbxVertexShader(DWORD XboxVertexShaderHandle) +{ + if (VshHandleIsVertexShader(XboxVertexShaderHandle)) { + auto it = g_CxbxVertexShaders.find(XboxVertexShaderHandle); + if (it != g_CxbxVertexShaders.end()) { + return it->second; + } + } + + return nullptr; +} + +void SetCxbxVertexShader(DWORD XboxVertexShaderHandle, CxbxVertexShader* shader) +{ + auto it = g_CxbxVertexShaders.find(XboxVertexShaderHandle); + if (it != g_CxbxVertexShaders.end() && it->second != nullptr && shader != nullptr) { + LOG_TEST_CASE("Overwriting existing Vertex Shader"); + } + + g_CxbxVertexShaders[XboxVertexShaderHandle] = shader; +} + +void CxbxImpl_SetVertexShaderInput +( + DWORD Handle, + UINT StreamCount, + XTL::X_STREAMINPUT* pStreamInputs +) +{ + LOG_INIT + + // If Handle is NULL, all VertexShader input state is cleared. + // Otherwise, Handle is the address of an Xbox VertexShader struct, or-ed with 1 (X_D3DFVF_RESERVED0) + // (Thus, a FVF handle is an invalid argument.) + // + + LOG_UNIMPLEMENTED(); +} + +void CxbxImpl_SelectVertexShaderDirect +( + XTL::X_VERTEXATTRIBUTEFORMAT* pVAF, + DWORD Address +) +{ + LOG_INIT; + + // When pVAF is non-null, this vertex attribute format takes precedence over the the one + LOG_UNIMPLEMENTED(); +} + diff --git a/src/core/kernel/init/CxbxKrnl.cpp b/src/core/kernel/init/CxbxKrnl.cpp index 9a71c2ba1..81d9af860 100644 --- a/src/core/kernel/init/CxbxKrnl.cpp +++ b/src/core/kernel/init/CxbxKrnl.cpp @@ -999,7 +999,7 @@ void CxbxKrnlMain(int argc, char* argv[]) // Write a header to the log { - printf("[0x%.4X] INIT: Cxbx-Reloaded Version %s\n", GetCurrentThreadId(), _CXBX_VERSION); + printf("[0x%.4X] INIT: Cxbx-Reloaded Version %s\n", GetCurrentThreadId(), CxbxVersionStr); time_t startTime = time(nullptr); struct tm* tm_info = localtime(&startTime); diff --git a/src/gui/DlgAbout.cpp b/src/gui/DlgAbout.cpp index a0e3def65..e8f9d572a 100644 --- a/src/gui/DlgAbout.cpp +++ b/src/gui/DlgAbout.cpp @@ -78,10 +78,15 @@ INT_PTR CALLBACK DlgAboutProc(HWND hWndDlg, UINT uMsg, WPARAM wParam, LPARAM lPa // Get tab pane dimensions RECT tabRect; GetClientRect(GetDlgItem(hWndDlg, IDC_TAB1), &tabRect); - SendMessage(GetDlgItem(hWndDlg, IDC_TAB1), TCM_ADJUSTRECT, FALSE, (LPARAM)&tabRect); + SendMessage(GetDlgItem(hWndDlg, IDC_TAB1), TCM_ADJUSTRECT, FALSE, (LPARAM)&tabRect); // Tab Pane 1 + char TabPane1Message[270]; + sprintf(TabPane1Message, "\nCxbx-Reloaded\nVersion %s\nŠ The Cxbx-Reloaded Team" + "\nThis software comes with ABSOLUTELY NO WARRANTY." + "\nThis is free software, and you are welcome to redistribute it" + "\nunder certain conditions; See our website for details.", CxbxVersionStr); HWND tab = CreateWindowEx - (NULL, "STATIC", "\nCxbx-Reloaded\nVersion " _CXBX_VERSION "\nŠ The Cxbx-Reloaded Team", + (NULL, "STATIC", TabPane1Message, WS_CHILD | WS_VISIBLE, tabRect.left + 10, tabRect.top + 10, tabRect.right - tabRect.left, @@ -145,7 +150,8 @@ INT_PTR CALLBACK DlgAboutProc(HWND hWndDlg, UINT uMsg, WPARAM wParam, LPARAM lPa aboutTabPanes.push_back(tab); aboutCurrentTab = 0; - UpdateWindow(hWndDlg); + UpdateWindow(hWndDlg); + } break; @@ -183,7 +189,9 @@ INT_PTR CALLBACK DlgAboutProc(HWND hWndDlg, UINT uMsg, WPARAM wParam, LPARAM lPa } // Show the selected tab pane - ShowWindow(aboutTabPanes[aboutCurrentTab], SW_HIDE); ShowWindow(aboutTabPanes[index], SW_SHOW); + ShowWindow(aboutTabPanes[aboutCurrentTab], SW_HIDE); + ShowWindow(aboutTabPanes[index], SW_SHOW); + aboutCurrentTab = index; } break; diff --git a/src/gui/Wnd.cpp b/src/gui/Wnd.cpp index fb893aed4..24186788b 100644 --- a/src/gui/Wnd.cpp +++ b/src/gui/Wnd.cpp @@ -83,7 +83,7 @@ bool Wnd::ProcessMessages() ( 0, m_classname, - m_wndname, + m_wndname.c_str(), m_wndstyle, m_x, m_y, diff --git a/src/gui/Wnd.h b/src/gui/Wnd.h index 075b0ca16..40b33c1ec 100644 --- a/src/gui/Wnd.h +++ b/src/gui/Wnd.h @@ -84,7 +84,7 @@ public: // ****************************************************************** ATOM m_class; const char *m_classname; - const char *m_wndname; + std::string m_wndname; UINT m_clsstyle; DWORD m_wndstyle; int m_x, m_y, m_w, m_h; diff --git a/src/gui/WndMain.cpp b/src/gui/WndMain.cpp index de2f61d0f..7fb596435 100644 --- a/src/gui/WndMain.cpp +++ b/src/gui/WndMain.cpp @@ -173,7 +173,7 @@ WndMain::WndMain(HINSTANCE x_hInstance) : // initialize members { m_classname = "WndMain"; - m_wndname = "Cxbx-Reloaded " _CXBX_VERSION; + m_wndname = "Cxbx-Reloaded " + std::string(CxbxVersionStr); } // load configuration from settings file @@ -1867,7 +1867,7 @@ void WndMain::UpdateCaption() { char AsciiTitle[MAX_PATH]; - int i = sprintf(AsciiTitle, "Cxbx-Reloaded %s", _CXBX_VERSION); + int i = sprintf(AsciiTitle, "Cxbx-Reloaded %s", CxbxVersionStr); if (m_Xbe != nullptr) { if (m_bIsStarted) { i += sprintf(AsciiTitle + i, " : Emulating ");