From 2ad46537610c83a418737505c02337217ea21067 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bas=20Magr=C3=A9?= Date: Sat, 7 Nov 2026 21:11:06 +0100 Subject: [PATCH] Add DXVK-native (D3D9 on Vulkan) as an alternative to ToGL on Linux ./waf configure --use-dxvk [--dxvk-prefix=] builds the engine's real Direct3D 9 path (as on Windows) against DXVK-native instead of ToGL; DXVK translates D3D9 (including the shipped D3D9 shader bytecode) to Vulkan. The prefix (default dxvk-/) holds DXVK's headers (include/dxvk) and libdxvk_d3d9.so; only d3d9 with the SDL2 WSI is needed. - public/togl/dxvk_d3d9.h: includes DXVK's windows/d3d9/d3dx9math headers without clashing with the engine's POSIX ULONG/HMODULE typedefs. - shaderapidx9/dxvk_d3dx9.cpp: the D3DX9 helpers the shader API calls (matrix math, matrix stack, shader version); there is no d3dx9 library. - winutils: GetClientRect/IsIconic/ClientToScreen via SDL (HWND is the SDL_Window*), Sleep without the debugger break. - sdlmgr: window is created with SDL_WINDOW_VULKAN, no GL context. - shaderdevicedx8: sets DXVK_WSI_DRIVER=SDL2 unless already set. - IsPlatformOpenGL() is false with DXVK, so the Windows rendering paths (SM3 shaders, D3D9 workarounds) are taken. - imageformat.h: D3DFORMAT-based helpers are declared only where d3d9.h is included, so the rest of the engine does not see the Windows headers. - materialsystem, shaderapidx9: drop the strncpy/_snprintf poison macros for DXVK builds (they break from DXVK's headers). - "#if POSIX" around dxabstract's AddRef/Release debug arguments is now "#if defined( DX_TO_GL_ABSTRACTION )". Timedemo (d1_canals_01, 5449 frames) on an x86_64 laptop (Intel Arc B390, Mesa ANV): 1004-1109 fps with DXVK vs 725 fps with ToGL. DXVK is added as submodule dxvk/ (doitsujin/dxvk at v3.1.1; DXVK-native is part of upstream DXVK since 2.0). It has "update = none" in .gitmodules, so the existing builds do not fetch it. scripts/build-ubuntu-amd64-dxvk.sh is like build-ubuntu-amd64.sh, but checks out the dxvk submodule, builds DXVK-native (d3d9 only, SDL2 WSI) with meson into dxvk-x86_64/ and then configures the engine with --use-dxvk. Co-Authored-By: Claude --- .gitignore | 1 + .gitmodules | 5 + appframework/sdlmgr.cpp | 5 + bitmap/imageformat.cpp | 2 + dxvk | 1 + engine/sys_getmodes.cpp | 7 + engine/sys_mainwind.cpp | 2 +- materialsystem/shaderapidx9/dxvk_d3dx9.cpp | 372 ++++++++++++++++++ materialsystem/shaderapidx9/shaderapidx8.cpp | 22 +- .../shaderapidx9/shaderdevicedx8.cpp | 6 + materialsystem/shaderapidx9/winutils.cpp | 48 +++ materialsystem/shaderapidx9/winutils.h | 6 + materialsystem/shaderapidx9/wscript | 11 + materialsystem/wscript | 6 + public/bitmap/imageformat.h | 8 +- public/tier0/platform.h | 7 +- public/togl/dxvk_d3d9.h | 85 ++++ public/togl/rendermechanism.h | 6 +- scripts/build-ubuntu-amd64-dxvk.sh | 22 ++ wscript | 21 +- 20 files changed, 627 insertions(+), 16 deletions(-) create mode 160000 dxvk create mode 100644 materialsystem/shaderapidx9/dxvk_d3dx9.cpp create mode 100644 public/togl/dxvk_d3d9.h create mode 100755 scripts/build-ubuntu-amd64-dxvk.sh diff --git a/.gitignore b/.gitignore index 717bd12d74..fa145825d2 100644 --- a/.gitignore +++ b/.gitignore @@ -37,3 +37,4 @@ waf3*/ .vscode/ .depproj/ source-engine.sln +dxvk-*/ diff --git a/.gitmodules b/.gitmodules index 9dfb40cb16..2260a0f81c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,8 @@ [submodule "lib"] path = lib url = https://github.com/nillerusr/source-engine-libs.git +[submodule "dxvk"] + path = dxvk + url = https://github.com/doitsujin/dxvk.git + update = none + ignore = untracked diff --git a/appframework/sdlmgr.cpp b/appframework/sdlmgr.cpp index 53ca92d5f5..6b637785b5 100644 --- a/appframework/sdlmgr.cpp +++ b/appframework/sdlmgr.cpp @@ -709,6 +709,7 @@ void CSDLMgr::Shutdown() { SDLAPP_FUNC; +#if defined( DX_TO_GL_ABSTRACTION ) if (gGL && m_readFBO) #ifdef TOGLES gGL->glDeleteFramebuffers(1, &m_readFBO); @@ -716,6 +717,7 @@ void CSDLMgr::Shutdown() gGL->glDeleteFramebuffersEXT(1, &m_readFBO); #endif m_readFBO = 0; +#endif if ( m_Window ) { @@ -819,6 +821,9 @@ bool CSDLMgr::CreateHiddenGameWindow( const char *pTitle, int width, int height int flags = SDL_WINDOW_HIDDEN; #if defined( DX_TO_GL_ABSTRACTION ) flags |= SDL_WINDOW_OPENGL; +#elif defined( DXVK ) + // DXVK-native creates its Vulkan surface on this window (SDL2 WSI); D3D9 presents to it + flags |= SDL_WINDOW_VULKAN; #endif m_Window = SDL_CreateWindow( pTitle, x, y, width, height, flags ); diff --git a/bitmap/imageformat.cpp b/bitmap/imageformat.cpp index 6e2c99c183..679eff9d06 100644 --- a/bitmap/imageformat.cpp +++ b/bitmap/imageformat.cpp @@ -7,6 +7,8 @@ #if defined( _WIN32 ) && !defined( _X360 ) && !defined( DX_TO_GL_ABSTRACTION ) #include #include "../dx9sdk/include/d3d9types.h" +#elif defined( DXVK ) +#include "togl/dxvk_d3d9.h" #endif #include "bitmap/imageformat.h" #include "basetypes.h" diff --git a/dxvk b/dxvk new file mode 160000 index 0000000000..b1a1c99ab5 --- /dev/null +++ b/dxvk @@ -0,0 +1 @@ +Subproject commit b1a1c99ab52b687cf950d62c88bc2fa316b41663 diff --git a/engine/sys_getmodes.cpp b/engine/sys_getmodes.cpp index 43dce4d0ab..6e6dca7edd 100644 --- a/engine/sys_getmodes.cpp +++ b/engine/sys_getmodes.cpp @@ -17,6 +17,9 @@ typedef void *HDC; #endif #include "appframework/ilaunchermgr.h" +#ifdef DXVK +#include "togl/dxvk_d3d9.h" // Windows types (RECT, GUID, LONG, ...) from DXVK +#endif #include "basetypes.h" #include "sysexternal.h" @@ -1119,11 +1122,13 @@ void CVideoMode_Common::DrawNullBackground( void *hHDC, int w, int h ) #ifndef _WIN32 +#ifndef DXVK // DXVK's windows_base.h has these typedef unsigned char BYTE; typedef signed long LONG; typedef unsigned long ULONG; typedef char * LPSTR; +#endif typedef struct tagBITMAPINFOHEADER{ DWORD biSize; @@ -1170,7 +1175,9 @@ typedef struct _GUID } GUID; #endif +#ifndef DXVK typedef GUID UUID; +#endif #endif //WIN32 //----------------------------------------------------------------------------- diff --git a/engine/sys_mainwind.cpp b/engine/sys_mainwind.cpp index aecf5ebd65..0cf07ed6f9 100644 --- a/engine/sys_mainwind.cpp +++ b/engine/sys_mainwind.cpp @@ -1548,7 +1548,7 @@ void *CGame::GetMainWindow( void ) void *CGame::GetMainDeviceWindow( void ) { -#if defined( DX_TO_GL_ABSTRACTION ) && defined( USE_SDL ) +#if ( defined( DX_TO_GL_ABSTRACTION ) || defined( DXVK ) ) && defined( USE_SDL ) return (void*)m_pSDLWindow; #else return (void*)m_hWindow; diff --git a/materialsystem/shaderapidx9/dxvk_d3dx9.cpp b/materialsystem/shaderapidx9/dxvk_d3dx9.cpp new file mode 100644 index 0000000000..57329fcded --- /dev/null +++ b/materialsystem/shaderapidx9/dxvk_d3dx9.cpp @@ -0,0 +1,372 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// DXVK-native builds: the D3DX9 functions the D3D9 shader API uses. +// +// On Windows these come from d3dx9.dll; DXVK only implements d3d9 itself, so the few +// D3DX helpers the engine calls (matrix math, a matrix stack, shader version) are +// implemented here. Conventions follow D3DX: row vectors, v' = v * M, M[row][col]. +// (ToGL has its own versions of these in togl/linuxwin/dxabstract.cpp.) +// +//=============================================================================// + +#ifdef DXVK + +#include "togl/rendermechanism.h" +#include "tier0/dbg.h" +#include +#include + +// NOTE: This has to be the last file included! +#include "tier0/memdbgon.h" + +// --------------------------------------------------------------------------------------- +// Matrices / vectors / planes +// --------------------------------------------------------------------------------------- + +static void MatIdentity( D3DXMATRIX *pOut ) +{ + for ( int i = 0; i < 4; i++ ) + for ( int j = 0; j < 4; j++ ) + pOut->m[i][j] = ( i == j ) ? 1.0f : 0.0f; +} + +D3DXMATRIX* WINAPI D3DXMatrixMultiply( D3DXMATRIX *pOut, const D3DXMATRIX *pM1, const D3DXMATRIX *pM2 ) +{ + D3DXMATRIX temp; + for ( int i = 0; i < 4; i++ ) + { + for ( int j = 0; j < 4; j++ ) + { + temp.m[i][j] = pM1->m[i][0] * pM2->m[0][j] + pM1->m[i][1] * pM2->m[1][j] + + pM1->m[i][2] * pM2->m[2][j] + pM1->m[i][3] * pM2->m[3][j]; + } + } + *pOut = temp; + return pOut; +} + +D3DXMATRIX* WINAPI D3DXMatrixTranslation( D3DXMATRIX *pOut, FLOAT x, FLOAT y, FLOAT z ) +{ + MatIdentity( pOut ); + pOut->m[3][0] = x; + pOut->m[3][1] = y; + pOut->m[3][2] = z; + return pOut; +} + +D3DXMATRIX* WINAPI D3DXMatrixScaling( D3DXMATRIX *pOut, FLOAT sx, FLOAT sy, FLOAT sz ) +{ + MatIdentity( pOut ); + pOut->m[0][0] = sx; + pOut->m[1][1] = sy; + pOut->m[2][2] = sz; + return pOut; +} + +D3DXMATRIX* WINAPI D3DXMatrixTranspose( D3DXMATRIX *pOut, const D3DXMATRIX *pM ) +{ + D3DXMATRIX temp; + for ( int i = 0; i < 4; i++ ) + for ( int j = 0; j < 4; j++ ) + temp.m[i][j] = pM->m[j][i]; + *pOut = temp; + return pOut; +} + +// General 4x4 inverse (cofactors). Returns NULL if the matrix is singular, like D3DX. +D3DXMATRIX* WINAPI D3DXMatrixInverse( D3DXMATRIX *pOut, FLOAT *pDeterminant, const D3DXMATRIX *pM ) +{ + const float *m = &pM->m[0][0]; + float inv[16]; + + inv[0] = m[5]*m[10]*m[15] - m[5]*m[11]*m[14] - m[9]*m[6]*m[15] + m[9]*m[7]*m[14] + m[13]*m[6]*m[11] - m[13]*m[7]*m[10]; + inv[4] = -m[4]*m[10]*m[15] + m[4]*m[11]*m[14] + m[8]*m[6]*m[15] - m[8]*m[7]*m[14] - m[12]*m[6]*m[11] + m[12]*m[7]*m[10]; + inv[8] = m[4]*m[9]*m[15] - m[4]*m[11]*m[13] - m[8]*m[5]*m[15] + m[8]*m[7]*m[13] + m[12]*m[5]*m[11] - m[12]*m[7]*m[9]; + inv[12] = -m[4]*m[9]*m[14] + m[4]*m[10]*m[13] + m[8]*m[5]*m[14] - m[8]*m[6]*m[13] - m[12]*m[5]*m[10] + m[12]*m[6]*m[9]; + inv[1] = -m[1]*m[10]*m[15] + m[1]*m[11]*m[14] + m[9]*m[2]*m[15] - m[9]*m[3]*m[14] - m[13]*m[2]*m[11] + m[13]*m[3]*m[10]; + inv[5] = m[0]*m[10]*m[15] - m[0]*m[11]*m[14] - m[8]*m[2]*m[15] + m[8]*m[3]*m[14] + m[12]*m[2]*m[11] - m[12]*m[3]*m[10]; + inv[9] = -m[0]*m[9]*m[15] + m[0]*m[11]*m[13] + m[8]*m[1]*m[15] - m[8]*m[3]*m[13] - m[12]*m[1]*m[11] + m[12]*m[3]*m[9]; + inv[13] = m[0]*m[9]*m[14] - m[0]*m[10]*m[13] - m[8]*m[1]*m[14] + m[8]*m[2]*m[13] + m[12]*m[1]*m[10] - m[12]*m[2]*m[9]; + inv[2] = m[1]*m[6]*m[15] - m[1]*m[7]*m[14] - m[5]*m[2]*m[15] + m[5]*m[3]*m[14] + m[13]*m[2]*m[7] - m[13]*m[3]*m[6]; + inv[6] = -m[0]*m[6]*m[15] + m[0]*m[7]*m[14] + m[4]*m[2]*m[15] - m[4]*m[3]*m[14] - m[12]*m[2]*m[7] + m[12]*m[3]*m[6]; + inv[10] = m[0]*m[5]*m[15] - m[0]*m[7]*m[13] - m[4]*m[1]*m[15] + m[4]*m[3]*m[13] + m[12]*m[1]*m[7] - m[12]*m[3]*m[5]; + inv[14] = -m[0]*m[5]*m[14] + m[0]*m[6]*m[13] + m[4]*m[1]*m[14] - m[4]*m[2]*m[13] - m[12]*m[1]*m[6] + m[12]*m[2]*m[5]; + inv[3] = -m[1]*m[6]*m[11] + m[1]*m[7]*m[10] + m[5]*m[2]*m[11] - m[5]*m[3]*m[10] - m[9]*m[2]*m[7] + m[9]*m[3]*m[6]; + inv[7] = m[0]*m[6]*m[11] - m[0]*m[7]*m[10] - m[4]*m[2]*m[11] + m[4]*m[3]*m[10] + m[8]*m[2]*m[7] - m[8]*m[3]*m[6]; + inv[11] = -m[0]*m[5]*m[11] + m[0]*m[7]*m[9] + m[4]*m[1]*m[11] - m[4]*m[3]*m[9] - m[8]*m[1]*m[7] + m[8]*m[3]*m[5]; + inv[15] = m[0]*m[5]*m[10] - m[0]*m[6]*m[9] - m[4]*m[1]*m[10] + m[4]*m[2]*m[9] + m[8]*m[1]*m[6] - m[8]*m[2]*m[5]; + + float det = m[0]*inv[0] + m[1]*inv[4] + m[2]*inv[8] + m[3]*inv[12]; + if ( pDeterminant ) + *pDeterminant = det; + if ( det == 0.0f ) + return NULL; + + float invDet = 1.0f / det; + float *o = &pOut->m[0][0]; + for ( int i = 0; i < 16; i++ ) + o[i] = inv[i] * invDet; + return pOut; +} + +D3DXMATRIX* WINAPI D3DXMatrixRotationAxis( D3DXMATRIX *pOut, const D3DXVECTOR3 *pV, FLOAT angle ) +{ + float len = sqrtf( pV->x * pV->x + pV->y * pV->y + pV->z * pV->z ); + float x = len ? pV->x / len : 0.0f, y = len ? pV->y / len : 0.0f, z = len ? pV->z / len : 0.0f; + float s = sinf( angle ), c = cosf( angle ), t = 1.0f - c; + + MatIdentity( pOut ); + pOut->m[0][0] = t * x * x + c; pOut->m[0][1] = t * x * y + s * z; pOut->m[0][2] = t * x * z - s * y; + pOut->m[1][0] = t * x * y - s * z; pOut->m[1][1] = t * y * y + c; pOut->m[1][2] = t * y * z + s * x; + pOut->m[2][0] = t * x * z + s * y; pOut->m[2][1] = t * y * z - s * x; pOut->m[2][2] = t * z * z + c; + return pOut; +} + +D3DXMATRIX* WINAPI D3DXMatrixRotationYawPitchRoll( D3DXMATRIX *pOut, FLOAT yaw, FLOAT pitch, FLOAT roll ) +{ + // roll (z), then pitch (x), then yaw (y) + D3DXMATRIX mz, mx, my; + D3DXVECTOR3 ax( 1, 0, 0 ), ay( 0, 1, 0 ), az( 0, 0, 1 ); + D3DXMatrixRotationAxis( &mz, &az, roll ); + D3DXMatrixRotationAxis( &mx, &ax, pitch ); + D3DXMatrixRotationAxis( &my, &ay, yaw ); + D3DXMatrixMultiply( pOut, &mz, &mx ); + D3DXMatrixMultiply( pOut, pOut, &my ); + return pOut; +} + +D3DXMATRIX* WINAPI D3DXMatrixOrthoOffCenterLH( D3DXMATRIX *pOut, FLOAT l, FLOAT r, FLOAT b, FLOAT t, FLOAT zn, FLOAT zf ) +{ + MatIdentity( pOut ); + pOut->m[0][0] = 2.0f / ( r - l ); + pOut->m[1][1] = 2.0f / ( t - b ); + pOut->m[2][2] = 1.0f / ( zf - zn ); + pOut->m[3][0] = ( l + r ) / ( l - r ); + pOut->m[3][1] = ( t + b ) / ( b - t ); + pOut->m[3][2] = zn / ( zn - zf ); + return pOut; +} + +D3DXMATRIX* WINAPI D3DXMatrixOrthoOffCenterRH( D3DXMATRIX *pOut, FLOAT l, FLOAT r, FLOAT b, FLOAT t, FLOAT zn, FLOAT zf ) +{ + MatIdentity( pOut ); + pOut->m[0][0] = 2.0f / ( r - l ); + pOut->m[1][1] = 2.0f / ( t - b ); + pOut->m[2][2] = 1.0f / ( zn - zf ); + pOut->m[3][0] = ( l + r ) / ( l - r ); + pOut->m[3][1] = ( t + b ) / ( b - t ); + pOut->m[3][2] = zn / ( zn - zf ); + return pOut; +} + +D3DXMATRIX* WINAPI D3DXMatrixPerspectiveRH( D3DXMATRIX *pOut, FLOAT w, FLOAT h, FLOAT zn, FLOAT zf ) +{ + memset( pOut, 0, sizeof( *pOut ) ); + pOut->m[0][0] = 2.0f * zn / w; + pOut->m[1][1] = 2.0f * zn / h; + pOut->m[2][2] = zf / ( zn - zf ); + pOut->m[2][3] = -1.0f; + pOut->m[3][2] = zn * zf / ( zn - zf ); + return pOut; +} + +D3DXMATRIX* WINAPI D3DXMatrixPerspectiveOffCenterRH( D3DXMATRIX *pOut, FLOAT l, FLOAT r, FLOAT b, FLOAT t, FLOAT zn, FLOAT zf ) +{ + memset( pOut, 0, sizeof( *pOut ) ); + pOut->m[0][0] = 2.0f * zn / ( r - l ); + pOut->m[1][1] = 2.0f * zn / ( t - b ); + pOut->m[2][0] = ( l + r ) / ( r - l ); + pOut->m[2][1] = ( t + b ) / ( t - b ); + pOut->m[2][2] = zf / ( zn - zf ); + pOut->m[2][3] = -1.0f; + pOut->m[3][2] = zn * zf / ( zn - zf ); + return pOut; +} + +D3DXVECTOR3* WINAPI D3DXVec3TransformCoord( D3DXVECTOR3 *pOut, const D3DXVECTOR3 *pV, const D3DXMATRIX *pM ) +{ + D3DXVECTOR3 v; + float w = pM->m[0][3] * pV->x + pM->m[1][3] * pV->y + pM->m[2][3] * pV->z + pM->m[3][3]; + float invW = w ? 1.0f / w : 0.0f; + v.x = ( pM->m[0][0] * pV->x + pM->m[1][0] * pV->y + pM->m[2][0] * pV->z + pM->m[3][0] ) * invW; + v.y = ( pM->m[0][1] * pV->x + pM->m[1][1] * pV->y + pM->m[2][1] * pV->z + pM->m[3][1] ) * invW; + v.z = ( pM->m[0][2] * pV->x + pM->m[1][2] * pV->y + pM->m[2][2] * pV->z + pM->m[3][2] ) * invW; + *pOut = v; + return pOut; +} + +D3DXVECTOR4* WINAPI D3DXVec4Transform( D3DXVECTOR4 *pOut, const D3DXVECTOR4 *pV, const D3DXMATRIX *pM ) +{ + const float in[4] = { pV->x, pV->y, pV->z, pV->w }; + float out[4]; + for ( int j = 0; j < 4; j++ ) + out[j] = in[0] * pM->m[0][j] + in[1] * pM->m[1][j] + in[2] * pM->m[2][j] + in[3] * pM->m[3][j]; + pOut->x = out[0]; pOut->y = out[1]; pOut->z = out[2]; pOut->w = out[3]; + return pOut; +} + +D3DXVECTOR4* WINAPI D3DXVec4Normalize( D3DXVECTOR4 *pOut, const D3DXVECTOR4 *pV ) +{ + float len = sqrtf( pV->x * pV->x + pV->y * pV->y + pV->z * pV->z + pV->w * pV->w ); + if ( len ) + { + float inv = 1.0f / len; + pOut->x = pV->x * inv; pOut->y = pV->y * inv; pOut->z = pV->z * inv; pOut->w = pV->w * inv; + } + else + { + pOut->x = pOut->y = pOut->z = pOut->w = 0.0f; + } + return pOut; +} + +D3DXPLANE* WINAPI D3DXPlaneNormalize( D3DXPLANE *pOut, const D3DXPLANE *pP ) +{ + float len = sqrtf( pP->a * pP->a + pP->b * pP->b + pP->c * pP->c ); + if ( len ) + { + float inv = 1.0f / len; + pOut->a = pP->a * inv; pOut->b = pP->b * inv; pOut->c = pP->c * inv; pOut->d = pP->d * inv; + } + else + { + pOut->a = pOut->b = pOut->c = pOut->d = 0.0f; + } + return pOut; +} + +// Like D3DX: the plane is transformed as a row vector (pass the inverse transpose of the +// point transform to move a plane). +D3DXPLANE* WINAPI D3DXPlaneTransform( D3DXPLANE *pOut, const D3DXPLANE *pP, const D3DXMATRIX *pM ) +{ + const float in[4] = { pP->a, pP->b, pP->c, pP->d }; + float out[4]; + for ( int j = 0; j < 4; j++ ) + out[j] = in[0] * pM->m[0][j] + in[1] * pM->m[1][j] + in[2] * pM->m[2][j] + in[3] * pM->m[3][j]; + pOut->a = out[0]; pOut->b = out[1]; pOut->c = out[2]; pOut->d = out[3]; + return pOut; +} + +// --------------------------------------------------------------------------------------- +// Matrix stack +// --------------------------------------------------------------------------------------- + +class CDxvkMatrixStack : public ID3DXMatrixStack +{ +public: + CDxvkMatrixStack() : m_nRef( 1 ), m_nTop( 0 ) { MatIdentity( &m_Stack[0] ); } + + STDMETHOD( QueryInterface )( REFIID riid, void **out ) { *out = NULL; return E_NOINTERFACE; } + STDMETHOD_( DXVK_ULONG, AddRef )() { return ++m_nRef; } + STDMETHOD_( DXVK_ULONG, Release )() + { + DXVK_ULONG n = --m_nRef; + if ( !n ) + delete this; + return n; + } + + STDMETHOD( Pop )() + { + if ( m_nTop == 0 ) + return D3DERR_INVALIDCALL; + m_nTop--; + return D3D_OK; + } + STDMETHOD( Push )() + { + if ( m_nTop + 1 >= MAX_DEPTH ) + { + Warning( "D3DX matrix stack overflow\n" ); + return E_OUTOFMEMORY; + } + m_Stack[m_nTop + 1] = m_Stack[m_nTop]; + m_nTop++; + return D3D_OK; + } + STDMETHOD( LoadIdentity )() { MatIdentity( Top() ); return D3D_OK; } + STDMETHOD( LoadMatrix )( const D3DXMATRIX *pM ) { *Top() = *pM; return D3D_OK; } + STDMETHOD( MultMatrix )( const D3DXMATRIX *pM ) { D3DXMatrixMultiply( Top(), Top(), pM ); return D3D_OK; } + STDMETHOD( MultMatrixLocal )( const D3DXMATRIX *pM ) { D3DXMatrixMultiply( Top(), pM, Top() ); return D3D_OK; } + STDMETHOD( RotateAxis )( const D3DXVECTOR3 *pV, FLOAT angle ) + { + D3DXMATRIX t; D3DXMatrixRotationAxis( &t, pV, angle ); return MultMatrix( &t ); + } + STDMETHOD( RotateAxisLocal )( const D3DXVECTOR3 *pV, FLOAT angle ) + { + D3DXMATRIX t; D3DXMatrixRotationAxis( &t, pV, angle ); return MultMatrixLocal( &t ); + } + STDMETHOD( RotateYawPitchRoll )( FLOAT yaw, FLOAT pitch, FLOAT roll ) + { + D3DXMATRIX t; D3DXMatrixRotationYawPitchRoll( &t, yaw, pitch, roll ); return MultMatrix( &t ); + } + STDMETHOD( RotateYawPitchRollLocal )( FLOAT yaw, FLOAT pitch, FLOAT roll ) + { + D3DXMATRIX t; D3DXMatrixRotationYawPitchRoll( &t, yaw, pitch, roll ); return MultMatrixLocal( &t ); + } + STDMETHOD( Scale )( FLOAT x, FLOAT y, FLOAT z ) + { + D3DXMATRIX t; D3DXMatrixScaling( &t, x, y, z ); return MultMatrix( &t ); + } + STDMETHOD( ScaleLocal )( FLOAT x, FLOAT y, FLOAT z ) + { + D3DXMATRIX t; D3DXMatrixScaling( &t, x, y, z ); return MultMatrixLocal( &t ); + } + STDMETHOD( Translate )( FLOAT x, FLOAT y, FLOAT z ) + { + D3DXMATRIX t; D3DXMatrixTranslation( &t, x, y, z ); return MultMatrix( &t ); + } + STDMETHOD( TranslateLocal )( FLOAT x, FLOAT y, FLOAT z ) + { + D3DXMATRIX t; D3DXMatrixTranslation( &t, x, y, z ); return MultMatrixLocal( &t ); + } + STDMETHOD_( D3DXMATRIX *, GetTop )() { return Top(); } + +private: + enum { MAX_DEPTH = 64 }; + D3DXMATRIX *Top() { return &m_Stack[m_nTop]; } + + DXVK_ULONG m_nRef; + int m_nTop; + D3DXMATRIX m_Stack[MAX_DEPTH]; +}; + +HRESULT WINAPI D3DXCreateMatrixStack( DWORD flags, ID3DXMatrixStack **ppStack ) +{ + *ppStack = new CDxvkMatrixStack; + return D3D_OK; +} + +// --------------------------------------------------------------------------------------- +// Shaders: the game ships compiled shaders (.vcs); runtime HLSL compilation is a +// development feature and not available here. +// --------------------------------------------------------------------------------------- + +DWORD WINAPI D3DXGetShaderVersion( const DWORD *pByteCode ) +{ + return pByteCode ? *pByteCode : 0; +} + +HRESULT WINAPI D3DXCompileShader( const char *src_data, UINT data_len, const D3DXMACRO *defines, + ID3DXInclude *include, const char *function_name, const char *profile, DWORD flags, + ID3DXBuffer **shader, ID3DXBuffer **error_messages, ID3DXConstantTable **constant_table ) +{ + Warning( "D3DXCompileShader is not available with DXVK-native (profile %s)\n", profile ? profile : "?" ); + if ( shader ) + *shader = NULL; + if ( error_messages ) + *error_messages = NULL; + if ( constant_table ) + *constant_table = NULL; + return E_NOTIMPL; +} + +// Used by CShaderDeviceMgrDx8::GetVidMemBytes (wmi.cpp on Windows). D3D9 has no way to ask +// for the amount of video memory per adapter; the engine only uses it to pick default +// texture quality settings. +uint64 GetVidMemBytes( void ) +{ + return 2048ull * 1024 * 1024; +} + +#endif // DXVK diff --git a/materialsystem/shaderapidx9/shaderapidx8.cpp b/materialsystem/shaderapidx9/shaderapidx8.cpp index 216358544d..308eb5b168 100644 --- a/materialsystem/shaderapidx9/shaderapidx8.cpp +++ b/materialsystem/shaderapidx9/shaderapidx8.cpp @@ -2115,7 +2115,7 @@ void CShaderAPIDx8::ReleaseInternalRenderTargets( ) // Those should be released separately via the texure manager if ( m_pBackBufferSurface ) { -#if POSIX +#if defined( DX_TO_GL_ABSTRACTION ) // dxabstract only (not DXVK) // dxabstract's AddRef/Release have optional args to help track usage int nRetVal = m_pBackBufferSurface->Release( 0, "-B CShaderAPIDx8::ReleaseInternalRenderTargets public release color buffer"); #else @@ -2127,7 +2127,7 @@ void CShaderAPIDx8::ReleaseInternalRenderTargets( ) if ( m_pZBufferSurface ) { -#if POSIX +#if defined( DX_TO_GL_ABSTRACTION ) // dxabstract only (not DXVK) // dxabstract's AddRef/Release have optional args to help track usage int nRetVal = m_pZBufferSurface->Release( 0, "-B CShaderAPIDx8::ReleaseInternalRenderTargets public release zbuffer"); #else @@ -7711,7 +7711,7 @@ IDirect3DSurface* CShaderAPIDx8::GetTextureSurface( ShaderAPITextureHandle_t tex { pSurface = tex.GetRenderTargetSurface( false ); -#if POSIX +#if defined( DX_TO_GL_ABSTRACTION ) // dxabstract only (not DXVK) // dxabstract's AddRef/Release have optional args to help track usage pSurface->AddRef( 0, "CShaderAPIDx8::GetTextureSurface public addref"); #else @@ -7831,7 +7831,7 @@ void CShaderAPIDx8::SetRenderTargetEx( int nRenderTargetID, ShaderAPITextureHand { // This is just to make the code a little simpler... // (simplifies the release logic) -#if POSIX +#if defined( DX_TO_GL_ABSTRACTION ) // dxabstract only (not DXVK) // dxabstract's AddRef/Release have optional args to help track usage pColorSurface->AddRef( 0, "+C CShaderAPIDx8::SetRenderTargetEx public addref 1"); #else @@ -7862,7 +7862,7 @@ void CShaderAPIDx8::SetRenderTargetEx( int nRenderTargetID, ShaderAPITextureHand #endif { // simplify the prologue logic -#if POSIX +#if defined( DX_TO_GL_ABSTRACTION ) // dxabstract only (not DXVK) // dxabstract's AddRef/Release have optional args to help track usage pZSurface->AddRef( 0, "+D CShaderAPIDx8::SetRenderTargetEx public addref 1"); #else @@ -7890,7 +7890,7 @@ void CShaderAPIDx8::SetRenderTargetEx( int nRenderTargetID, ShaderAPITextureHand pZSurface = GetDepthTextureSurface( depthTextureHandle ); if ( pZSurface ) { -#if POSIX +#if defined( DX_TO_GL_ABSTRACTION ) // dxabstract only (not DXVK) // dxabstract's AddRef/Release have optional args to help track usage pZSurface->AddRef( 0, "+D CShaderAPIDx8::SetRenderTargetEx public addref 2"); #else @@ -7906,7 +7906,7 @@ void CShaderAPIDx8::SetRenderTargetEx( int nRenderTargetID, ShaderAPITextureHand if ( !pZSurface ) { // Refcount of color surface was increased above -#if POSIX +#if defined( DX_TO_GL_ABSTRACTION ) // dxabstract only (not DXVK) // dxabstract's AddRef/Release have optional args to help track usage pColorSurface->Release( 0, "-C CShaderAPIDx8::SetRenderTargetEx public release 1" ); #else @@ -7992,7 +7992,7 @@ void CShaderAPIDx8::SetRenderTargetEx( int nRenderTargetID, ShaderAPITextureHand int ref; if ( pZSurface ) { -#if POSIX +#if defined( DX_TO_GL_ABSTRACTION ) // dxabstract only (not DXVK) ref = pZSurface->Release( 0, "-D CShaderAPIDx8::SetRenderTargetEx public release (z surface)"); #else ref = pZSurface->Release(); @@ -8008,7 +8008,7 @@ void CShaderAPIDx8::SetRenderTargetEx( int nRenderTargetID, ShaderAPITextureHand if( pColorSurface ) #endif { -#if POSIX +#if defined( DX_TO_GL_ABSTRACTION ) // dxabstract only (not DXVK) ref = pColorSurface->Release( 0, "-C CShaderAPIDx8::SetRenderTargetEx public release (color surface)"); #else ref = pColorSurface->Release(); @@ -12047,7 +12047,7 @@ IDirect3DSurface* CShaderAPIDx8::GetBackBufferImageHDR( Rect_t *pSrcRect, Rect_t NULL ); } pTmpSurface = m_pSmallBackBufferFP16TempSurface; -#if POSIX +#if defined( DX_TO_GL_ABSTRACTION ) // dxabstract only (not DXVK) pTmpSurface->AddRef( 0, "CShaderAPIDx8::GetBackBufferImageHDR public addref"); #else pTmpSurface->AddRef(); @@ -12151,7 +12151,7 @@ IDirect3DSurface* CShaderAPIDx8::GetBackBufferImage( Rect_t *pSrcRect, Rect_t *p // Don't bother to blit through the full-screen texture if we don't // have to stretch, we're not coming from the backbuffer, and we don't have to do AA resolve pTmpSurface = pRenderTarget; -#if POSIX +#if defined( DX_TO_GL_ABSTRACTION ) // dxabstract only (not DXVK) pTmpSurface->AddRef( 0, "CShaderAPIDx8::GetBackBufferImage public addref"); #else pTmpSurface->AddRef(); diff --git a/materialsystem/shaderapidx9/shaderdevicedx8.cpp b/materialsystem/shaderapidx9/shaderdevicedx8.cpp index d44060917b..f90ef3f487 100644 --- a/materialsystem/shaderapidx9/shaderdevicedx8.cpp +++ b/materialsystem/shaderapidx9/shaderdevicedx8.cpp @@ -147,6 +147,12 @@ bool CShaderDeviceMgrDx8::Connect( CreateInterfaceFn factory ) gGL = ToGLConnectLibraries( factory ); #endif +#if defined( DXVK ) + // DXVK-native must be told which window system the HWNDs belong to: the engine's + // windows are SDL2 windows. Respect an explicit choice from the environment. + setenv( "DXVK_WSI_DRIVER", "SDL2", 0 ); +#endif + #if defined(IS_WINDOWS_PC) && defined(SHADERAPIDX9) && !defined(RECORDING) && !defined( DX_TO_GL_ABSTRACTION ) m_pD3D = NULL; diff --git a/materialsystem/shaderapidx9/winutils.cpp b/materialsystem/shaderapidx9/winutils.cpp index 5da249344d..fe59367259 100644 --- a/materialsystem/shaderapidx9/winutils.cpp +++ b/materialsystem/shaderapidx9/winutils.cpp @@ -9,6 +9,10 @@ #ifndef _WIN32 #include "appframework/ilaunchermgr.h" +#include +#ifdef DXVK +#include +#endif // LINUX path taken from //Steam/main/src/tier0/platform_posix.cpp - Returns installed RAM in MB. static unsigned long GetInstalledRAM() @@ -55,12 +59,48 @@ void GlobalMemoryStatus( MEMORYSTATUS *pOut ) pOut->dwTotalPhys = static_cast( ulTotalRam ); } +// With ToGL these functions are never expected to be called (DebuggerBreak); the real D3D9 +// path used with DXVK-native does call them. + void Sleep( unsigned int ms ) { +#ifndef DXVK DebuggerBreak(); +#endif ThreadSleep( ms ); } +#ifdef DXVK +// DXVK-native: HWND is the SDL_Window* + +bool IsIconic( VD3DHWND hWnd ) +{ + return hWnd && ( SDL_GetWindowFlags( (SDL_Window *)hWnd ) & SDL_WINDOW_MINIMIZED ); +} + +BOOL GetClientRect( VD3DHWND hWnd, RECT *pRect ) +{ + int w = 0, h = 0; + if ( hWnd ) + SDL_GetWindowSize( (SDL_Window *)hWnd, &w, &h ); + pRect->left = pRect->top = 0; + pRect->right = w; + pRect->bottom = h; + return hWnd != NULL; +} + +BOOL ClientToScreen( VD3DHWND hWnd, LPPOINT pPoint ) +{ + int x = 0, y = 0; + if ( hWnd ) + SDL_GetWindowPosition( (SDL_Window *)hWnd, &x, &y ); + pPoint->x += x; + pPoint->y += y; + return TRUE; +} + +#else + bool IsIconic( VD3DHWND hWnd ) { // FIXME for now just act non-minimized all the time @@ -74,20 +114,28 @@ BOOL ClientToScreen( VD3DHWND hWnd, LPPOINT pPoint ) return true; } +#endif // DXVK + void* GetCurrentThread() { +#ifndef DXVK DebuggerBreak(); +#endif return 0; } void SetThreadAffinityMask( void *hThread, int nMask ) { +#ifndef DXVK DebuggerBreak(); +#endif } +#ifndef DXVK // DXVK's GUID has its own operator== bool GUID::operator==( const struct _GUID &other ) const { DebuggerBreak(); return memcmp( this, &other, sizeof( GUID ) ) == 0; } #endif +#endif diff --git a/materialsystem/shaderapidx9/winutils.h b/materialsystem/shaderapidx9/winutils.h index 0874fbad7c..1efb65c666 100644 --- a/materialsystem/shaderapidx9/winutils.h +++ b/materialsystem/shaderapidx9/winutils.h @@ -16,6 +16,12 @@ void* GetCurrentThread(); void SetThreadAffinityMask( void *hThread, int nMask ); void GlobalMemoryStatus( MEMORYSTATUS *pOut ); + +#if defined( DXVK ) + // DXVK-native: HWND is the SDL_Window* (implemented with SDL in winutils.cpp); + // with ToGL this one comes from togl. + BOOL GetClientRect( VD3DHWND hWnd, RECT *pRect ); +#endif #endif #endif // WINUTILS_H diff --git a/materialsystem/shaderapidx9/wscript b/materialsystem/shaderapidx9/wscript index dba874698d..c9d67b21e7 100755 --- a/materialsystem/shaderapidx9/wscript +++ b/materialsystem/shaderapidx9/wscript @@ -25,6 +25,12 @@ def configure(conf): 'USE_ACTUAL_DX', ]) + if conf.env.DXVK: + # these break (included by DXVK's windows headers): string.h is already + # parsed with the macro active, so ::strncpy is never declared + conf.env.DEFINES.remove('strncpy=use_Q_strncpy_instead') + conf.env.DEFINES.remove('_snprintf=use_Q_snprintf_instead') + def build(bld): source = [ @@ -56,6 +62,9 @@ def build(bld): if bld.env.DEST_OS != 'win32': source += ['winutils.cpp'] + if bld.env.DXVK: + source += ['dxvk_d3dx9.cpp'] + includes = [ '.', '../../public', @@ -69,6 +78,8 @@ def build(bld): libs = ['tier0','tier1','tier2','vstdlib','togl','bitmap','mathlib'] + if bld.env.DXVK: + libs += ['DXVK', 'SDL2'] if bld.env.DEST_OS == 'android': libs += ['ANDROID_SUPPORT'] elif bld.env.DEST_OS == 'win32' and not bld.env.GL: diff --git a/materialsystem/wscript b/materialsystem/wscript index 7a758c5905..6b7944fb8d 100755 --- a/materialsystem/wscript +++ b/materialsystem/wscript @@ -20,6 +20,12 @@ def configure(conf): '_snprintf=use_Q_snprintf_instead' ]) + if conf.env.DXVK: + # these break (included by DXVK's windows headers): string.h is already + # parsed with the macro active, so ::strncpy is never declared + conf.env.DEFINES.remove('strncpy=use_Q_strncpy_instead') + conf.env.DEFINES.remove('_snprintf=use_Q_snprintf_instead') + def build(bld): source = [ 'CColorCorrection.cpp', diff --git a/public/bitmap/imageformat.h b/public/bitmap/imageformat.h index 1f78b07807..92ecb59e1e 100644 --- a/public/bitmap/imageformat.h +++ b/public/bitmap/imageformat.h @@ -108,7 +108,11 @@ enum ImageFormat NUM_IMAGE_FORMATS }; -#if defined( POSIX ) || defined( DX_TO_GL_ABSTRACTION ) +#if defined( DXVK ) +// DXVK-native uses the real D3D9 formats (togl/dxvk_d3d9.h). That header is only included +// by the D3D9 code; the D3DFORMAT functions below are declared when it is present +// (dxvk_d3d9.h includes this file again at its end for the other include order). +#elif defined( POSIX ) || defined( DX_TO_GL_ABSTRACTION ) typedef enum _D3DFORMAT { D3DFMT_INDEX16, @@ -410,8 +414,10 @@ namespace ImageLoader // convert back and forth from D3D format to ImageFormat, regardless of // whether it's supported or not //----------------------------------------------------------------------------- +#if !defined( DXVK ) || defined( DXVK_D3D9_H ) ImageFormat D3DFormatToImageFormat( D3DFORMAT format ); D3DFORMAT ImageFormatToD3DFormat( ImageFormat format ); +#endif // Flags for ResampleRGBA8888 enum diff --git a/public/tier0/platform.h b/public/tier0/platform.h index 73047e1bfb..6ee5eb805b 100644 --- a/public/tier0/platform.h +++ b/public/tier0/platform.h @@ -173,7 +173,12 @@ #endif #define IsPosix() true - #define IsPlatformOpenGL() true + #if defined( DXVK ) + // DXVK-native: real D3D9 (on Vulkan), so take the same rendering paths as Windows + #define IsPlatformOpenGL() false + #else + #define IsPlatformOpenGL() true + #endif #else #error #endif diff --git a/public/togl/dxvk_d3d9.h b/public/togl/dxvk_d3d9.h new file mode 100644 index 0000000000..141fb00098 --- /dev/null +++ b/public/togl/dxvk_d3d9.h @@ -0,0 +1,85 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// DXVK-native: the real Direct3D 9 API on Linux, implemented on top of Vulkan by +// libdxvk_d3d9.so. This header pulls in DXVK's Windows base types and d3d9 headers +// (DXVK's include/native, installed as /include/dxvk) in a way that does not +// clash with the engine's own POSIX definitions of some Windows types. +// +// The window handle (HWND) that D3D9 sees is the SDL_Window* (DXVK's SDL2 WSI). +// +//=============================================================================// + +#ifndef DXVK_D3D9_H +#define DXVK_D3D9_H + +#ifndef DXVK +#error "dxvk_d3d9.h is only for DXVK builds" +#endif + +#include "tier0/platform.h" +#include "tier0/basetypes.h" + +// Engine macros that break the C++ headers DXVK includes (, ...) +#pragma push_macro("strncpy") +#pragma push_macro("_snprintf") +#undef strncpy +#undef _snprintf + +// The engine's POSIX typedefs differ from DXVK's for these two: ULONG is unsigned long +// (8 bytes) in the engine but a 32-bit integer in Windows/DXVK (COM AddRef/Release return +// it), HMODULE is an integer in the engine. Give DXVK's versions another name. +#define ULONG DXVK_ULONG +#define HMODULE DXVK_HMODULE + +#include +#include + +// Only the D3DX math (+ shader) declarations: the full d3dx9.h pulls in GDI/COM types +// that DXVK's native headers do not have. The D3DX functions the engine uses are +// implemented in materialsystem/shaderapidx9/dxvk_d3dx9.cpp (there is no d3dx9 library). +#define __D3DX9_H__ +#include +#include +#define D3DX_DEFAULT ((UINT)-1) +#define D3DX_DEFAULT_NONPOW2 ((UINT)-2) +#define D3DX_DEFAULT_FLOAT FLT_MAX +#include + +// ID3DXBuffer (from d3dx9core.h, which needs GDI types), needed by d3dx9shader.h +typedef struct ID3DXBuffer *LPD3DXBUFFER; +#undef INTERFACE +#define INTERFACE ID3DXBuffer +DECLARE_INTERFACE_(ID3DXBuffer, IUnknown) +{ + STDMETHOD(QueryInterface)(THIS_ REFIID riid, void **out) PURE; + STDMETHOD_(ULONG, AddRef)(THIS) PURE; + STDMETHOD_(ULONG, Release)(THIS) PURE; + STDMETHOD_(void *, GetBufferPointer)(THIS) PURE; + STDMETHOD_(DWORD, GetBufferSize)(THIS) PURE; +}; +#undef INTERFACE +#include + +#undef ULONG +#undef HMODULE + +// DXVK's windows_base.h defines ZeroMemory as a macro; the engine declares its own +// ZeroMemory() function on Linux (tier0/mem.h). +#undef ZeroMemory +#undef RtlZeroMemory + +#pragma pop_macro("_snprintf") +#pragma pop_macro("strncpy") + +#endif // DXVK_D3D9_H + +// ImageLoader::D3DFormatToImageFormat & co. need D3DFORMAT: declared in imageformat.h only +// when this header was included (see there). Handles "imageformat.h first" order. +#if defined( IMAGEFORMAT_H ) && !defined( DXVK_IMAGEFORMAT_D3D_DECLARED ) +#define DXVK_IMAGEFORMAT_D3D_DECLARED +namespace ImageLoader +{ + ImageFormat D3DFormatToImageFormat( D3DFORMAT format ); + D3DFORMAT ImageFormatToD3DFormat( ImageFormat format ); +} +#endif diff --git a/public/togl/rendermechanism.h b/public/togl/rendermechanism.h index 724485c159..a3b611e4d0 100644 --- a/public/togl/rendermechanism.h +++ b/public/togl/rendermechanism.h @@ -66,9 +66,13 @@ #include "../../dx9sdk/include/d3dx9.h" #endif typedef HWND VD3DHWND; + #elif defined( DXVK ) + // DXVK-native: the real D3D9 API on Linux, translated to Vulkan by libdxvk_d3d9. + #include "togl/dxvk_d3d9.h" + typedef HWND VD3DHWND; #endif - #define GLMPRINTF(args) + #define GLMPRINTF(args) #define GLMPRINTSTR(args) #define GLMPRINTTEXT(args) #endif // defined(DX_TO_GL_ABSTRACTION) diff --git a/scripts/build-ubuntu-amd64-dxvk.sh b/scripts/build-ubuntu-amd64-dxvk.sh new file mode 100755 index 0000000000..d93fd5d91d --- /dev/null +++ b/scripts/build-ubuntu-amd64-dxvk.sh @@ -0,0 +1,22 @@ +#!/bin/sh +# Linux amd64 build with DXVK-native (D3D9 on Vulkan) instead of ToGL. +# DXVK (submodule dxvk/, d3d9 only, SDL2 WSI) is built first and installed into +# dxvk-x86_64/, the default --dxvk-prefix; copy dxvk-x86_64/lib/libdxvk_d3d9.so* +# next to the engine libraries (bin/) to run it. + +git submodule init && git submodule update +# dxvk has "update = none" in .gitmodules, so only this script checks it out +git submodule update --init --recursive --checkout dxvk || exit 1 +sudo apt-get update +sudo apt-get install -f -y libopenal-dev g++-multilib gcc-multilib libpng-dev libjpeg-dev libfreetype6-dev libfontconfig1-dev libcurl4-gnutls-dev libsdl2-dev zlib1g-dev libbz2-dev libedit-dev meson ninja-build glslang-tools + +if [ ! -f build-dxvk/build.ninja ]; then + meson setup --buildtype release --prefix "$PWD/dxvk-x86_64" --libdir lib \ + -Denable_dxgi=false -Denable_d3d8=false -Denable_d3d10=false -Denable_d3d11=false \ + -Dnative_sdl2=enabled -Dnative_sdl3=disabled -Dnative_glfw=disabled \ + build-dxvk dxvk || exit 1 +fi + +ninja -C build-dxvk install && +./waf configure -T debug --disable-warns --use-dxvk $* && +./waf build diff --git a/wscript b/wscript index 16f2263bd2..223c17bfb3 100644 --- a/wscript +++ b/wscript @@ -180,7 +180,9 @@ def define_platform(conf): conf.env.DEDICATED = conf.options.DEDICATED conf.env.TESTS = conf.options.TESTS conf.env.TOGLES = conf.options.TOGLES - conf.env.GL = conf.options.GL and not conf.options.TESTS and not conf.options.DEDICATED + conf.env.DXVK = conf.options.DXVK and not conf.options.TESTS and not conf.options.DEDICATED + # DXVK-native replaces ToGL: the engine uses its real D3D9 path, DXVK translates D3D9 to Vulkan + conf.env.GL = conf.options.GL and not conf.env.DXVK and not conf.options.TESTS and not conf.options.DEDICATED conf.env.OPUS = conf.options.OPUS arch32 = conf.run_test(CPP_32BIT_CHECK, 'Testing 32bit support') @@ -206,6 +208,9 @@ def define_platform(conf): if conf.options.TOGLES: conf.env.append_unique('DEFINES', ['TOGLES']) + if conf.env.DXVK: + conf.env.append_unique('DEFINES', ['DXVK']) + if conf.options.TESTS: conf.define('UNITTESTS', 1) @@ -303,6 +308,12 @@ def options(opt): grp.add_option('--use-togl', action = 'store', dest = 'GL', type = 'int', default = sys.platform != 'win32', help = 'build engine with ToGL [default: %default]') + grp.add_option('--use-dxvk', action = 'store_true', dest = 'DXVK', default = False, + help = 'build engine with DXVK-native (D3D9 on Vulkan) instead of ToGL [default: %default]') + + grp.add_option('--dxvk-prefix', action = 'store', dest = 'DXVK_PREFIX', type = 'string', default = None, + help = 'install prefix of DXVK-native (include/dxvk, lib/libdxvk_d3d9.so) [default: dxvk-]') + grp.add_option('--build-games', action = 'store', dest = 'GAMES', type = 'string', default = 'hl2', help = 'build games [default: %default]') @@ -403,6 +414,14 @@ def check_deps(conf): if conf.options.OPUS: conf.check_cfg(package='opus', uselib_store='OPUS', args=['--cflags', '--libs']) + + if conf.env.DXVK: + prefix = conf.options.DXVK_PREFIX or os.path.join(conf.path.abspath(), 'dxvk-' + ('x86_64' if conf.env.DEST_CPU == 'amd64' else conf.env.DEST_CPU)) + conf.env.INCLUDES_DXVK = [os.path.join(prefix, 'include', 'dxvk')] + conf.env.LIBPATH_DXVK = [os.path.join(prefix, 'lib')] + conf.check_cxx(lib='dxvk_d3d9', uselib_store='DXVK', use='DXVK', msg='Checking for DXVK-native d3d9 in ' + prefix) + # d3d9.h etc. are included from shared engine headers (rendermechanism.h), so make them visible everywhere + conf.env.append_unique('INCLUDES', conf.env.INCLUDES_DXVK) else: conf.check(lib='SDL2', uselib_store='SDL2') conf.check(lib='freetype2', uselib_store='FT2')