diff --git a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp index a22fb6e97..c81b93288 100644 --- a/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp +++ b/src/core/hle/D3D8/Direct3D9/Direct3D9.cpp @@ -8264,14 +8264,10 @@ VOID WINAPI XTL::EMUPATCH(D3DDevice_LoadVertexShaderProgram) DWORD hCurrentShader = g_CurrentXboxVertexShaderHandle; - if (!VshHandleIsVertexShader(hCurrentShader)) { - LOG_TEST_CASE("D3DDevice_LoadVertexShaderProgram called with FVF Shader"); - } - // D3DDevice_LoadVertexShaderProgram splits the given function buffer into batch-wise pushes to the NV2A load_shader_program_key_t shaderCacheKey = ((load_shader_program_key_t)hCurrentShader << 32) | (DWORD)pFunction; - + // If the shader key was located in the cache, use the cached shader // TODO: When do we clear the cache? In this approach, shaders are // never freed... @@ -8282,28 +8278,107 @@ VOID WINAPI XTL::EMUPATCH(D3DDevice_LoadVertexShaderProgram) return; } - CxbxVertexShader *pVertexShader = GetCxbxVertexShader(hCurrentShader); - if (pVertexShader != nullptr) - { - DWORD hNewShader = 0; + DWORD hNewShader = 0; + DWORD* pDeclaration = nullptr; - // Save the contents of the existing vertex shader program - DWORD* pDeclaration = (DWORD*) malloc( pVertexShader->OriginalDeclarationCount * sizeof(DWORD) ); - memmove( pDeclaration, pVertexShader->pDeclaration, pVertexShader->OriginalDeclarationCount * sizeof(DWORD)); + if (VshHandleIsVertexShader(hCurrentShader)) { + CxbxVertexShader *pVertexShader = GetCxbxVertexShader(hCurrentShader); - // Create a vertex shader with the new vertex program data - HRESULT hr = EMUPATCH(D3DDevice_CreateVertexShader)( pDeclaration, pFunction, &hNewShader, 0 ); - free(pDeclaration); - if( FAILED( hr ) ) - CxbxKrnlCleanup("Error creating new vertex shader!" ); + // If we failed to fetch an active pixel shader, log and do nothing + if (pVertexShader == nullptr) { + LOG_TEST_CASE("D3DDevice_LoadVertexShaderProgram: Failed to locate original shader"); + return; + } - EMUPATCH(D3DDevice_LoadVertexShader)(hNewShader, Address); - EMUPATCH(D3DDevice_SelectVertexShader)(hNewShader, Address); + // Simply retrieve the contents of the existing vertex shader program + pDeclaration = (DWORD*)malloc(pVertexShader->OriginalDeclarationCount * sizeof(DWORD)); + memcpy(pDeclaration, pVertexShader->pDeclaration, pVertexShader->OriginalDeclarationCount * sizeof(DWORD)); + } else { + // This is an unusual scenario in which an FVF shader is being replaced with an actual shader + // But without calling CreateVertexShader: This means we need to parse the current FVF and generate + // a declaration to use when converting/setting this new shader - g_LoadVertexShaderProgramCache[shaderCacheKey] = hNewShader; + // Allocate a large enough definition to contain all possible FVF types + // 20 is maximum possible size + pDeclaration = (DWORD*)malloc(20 * sizeof(DWORD)); + memset(pDeclaration, 0, 20 * sizeof(DWORD)); + int index = 0; - EmuLog(LOG_LEVEL::WARNING, "Vertex Shader Cache Size: %d", g_LoadVertexShaderProgramCache.size()); + // Write the Stream Number (always 0 for FVF) + pDeclaration[index++] = X_D3DVSD_STREAM(0); + + // Write Position + DWORD position = (g_CurrentXboxVertexShaderHandle & X_D3DFVF_POSITION_MASK); + if (position == D3DFVF_XYZRHW) { + pDeclaration[index++] = X_D3DVSD_REG(X_D3DVSDE_POSITION, X_D3DVSDT_FLOAT4); + } else { + pDeclaration[index++] = X_D3DVSD_REG(X_D3DVSDE_POSITION, X_D3DVSDT_FLOAT3); + } + + // Write Blend Weights + if (position == D3DFVF_XYZB1) { + pDeclaration[index++] = X_D3DVSD_REG(X_D3DVSDE_BLENDWEIGHT, X_D3DVSDT_FLOAT1); + } + if (position == D3DFVF_XYZB2) { + pDeclaration[index++] = X_D3DVSD_REG(X_D3DVSDE_BLENDWEIGHT, X_D3DVSDT_FLOAT2); + } + if (position == D3DFVF_XYZB3) { + pDeclaration[index++] = X_D3DVSD_REG(X_D3DVSDE_BLENDWEIGHT, X_D3DVSDT_FLOAT3); + } + if (position == D3DFVF_XYZB4) { + pDeclaration[index++] = X_D3DVSD_REG(X_D3DVSDE_BLENDWEIGHT, X_D3DVSDT_FLOAT4); + } + + // Write Normal, Diffuse, and Specular + if (g_CurrentXboxVertexShaderHandle & D3DFVF_NORMAL) { + pDeclaration[index++] = X_D3DVSD_REG(X_D3DVSDE_NORMAL, X_D3DVSDT_FLOAT3); + } + if (g_CurrentXboxVertexShaderHandle & D3DFVF_DIFFUSE) { + pDeclaration[index++] = X_D3DVSD_REG(X_D3DVSDE_DIFFUSE, X_D3DVSDT_D3DCOLOR); + } + if (g_CurrentXboxVertexShaderHandle & D3DFVF_SPECULAR) { + pDeclaration[index++] = X_D3DVSD_REG(X_D3DVSDE_SPECULAR, X_D3DVSDT_D3DCOLOR); + } + + // Write Texture Coordinates + int textureCount = (g_CurrentXboxVertexShaderHandle & X_D3DFVF_TEXCOUNT_MASK) >> X_D3DFVF_TEXCOUNT_SHIFT; + for (int i = 0; i < textureCount; i++) { + int numberOfCoordinates = 0; + + if ((g_CurrentXboxVertexShaderHandle & X_D3DFVF_TEXCOORDSIZE1(i)) == (DWORD)X_D3DFVF_TEXCOORDSIZE1(i)) { + numberOfCoordinates = X_D3DVSDT_FLOAT1; + } + if ((g_CurrentXboxVertexShaderHandle & X_D3DFVF_TEXCOORDSIZE2(i)) == (DWORD)X_D3DFVF_TEXCOORDSIZE2(i)) { + numberOfCoordinates = X_D3DVSDT_FLOAT2; + } + if ((g_CurrentXboxVertexShaderHandle & X_D3DFVF_TEXCOORDSIZE3(i)) == (DWORD)X_D3DFVF_TEXCOORDSIZE3(i)) { + numberOfCoordinates = X_D3DVSDT_FLOAT3; + } + if ((g_CurrentXboxVertexShaderHandle & X_D3DFVF_TEXCOORDSIZE4(i)) == (DWORD)X_D3DFVF_TEXCOORDSIZE4(i)) { + numberOfCoordinates = X_D3DVSDT_FLOAT4; + } + + pDeclaration[index++] = X_D3DVSD_REG(X_D3DVSDE_TEXCOORD0 + i, numberOfCoordinates); + } + + // Write Declaration End + pDeclaration[index++] = X_D3DVSD_END(); + + // Now we can fall through and create a new vertex shader } + + // Create a vertex shader with the new vertex program data + HRESULT hr = EMUPATCH(D3DDevice_CreateVertexShader)( pDeclaration, pFunction, &hNewShader, 0 ); + free(pDeclaration); + if( FAILED( hr ) ) + CxbxKrnlCleanup("Error creating new vertex shader!" ); + + EMUPATCH(D3DDevice_LoadVertexShader)(hNewShader, Address); + EMUPATCH(D3DDevice_SelectVertexShader)(hNewShader, Address); + + g_LoadVertexShaderProgramCache[shaderCacheKey] = hNewShader; + + EmuLog(LOG_LEVEL::WARNING, "Vertex Shader Cache Size: %d", g_LoadVertexShaderProgramCache.size()); } // ****************************************************************** diff --git a/src/core/hle/D3D8/XbD3D8Types.h b/src/core/hle/D3D8/XbD3D8Types.h index 4abc57430..83bec1d45 100644 --- a/src/core/hle/D3D8/XbD3D8Types.h +++ b/src/core/hle/D3D8/XbD3D8Types.h @@ -1215,8 +1215,48 @@ typedef enum _X_D3DVSD_TOKENTYPE #define X_D3DVSD_EXTINFOSHIFT 0 #define X_D3DVSD_EXTINFOMASK (0xFFFFFF << X_D3DVSD_EXTINFOSHIFT) +#define X_D3DVSD_MAKETOKENTYPE(Type) ((Type << X_D3DVSD_TOKENTYPESHIFT) & X_D3DVSD_TOKENTYPEMASK) + +#define X_D3DVSD_STREAM(Stream) (X_D3DVSD_MAKETOKENTYPE(X_D3DVSD_TOKEN_STREAM) | (Stream)) +#define X_D3DVSD_REG(Reg, Type) (X_D3DVSD_MAKETOKENTYPE(X_D3DVSD_TOKEN_STREAMDATA) | ((Type) << X_D3DVSD_DATATYPESHIFT) | (Reg)) + +#define X_D3DVSD_NOP() 0x00000000 #define X_D3DVSD_END() 0xFFFFFFFF +// FVF Definitions +#define X_D3DFVF_POSITION_MASK 0x00E +#define X_D3DFVF_XYZ 0x002 +#define X_D3DFVF_XYZRHW 0x004 +#define X_D3DFVF_XYZB1 0x006 +#define X_D3DFVF_XYZB2 0x008 +#define X_D3DFVF_XYZB3 0x00a +#define X_D3DFVF_XYZB4 0x00c +#define X_D3DFVF_NORMAL 0x010 +#define X_D3DFVF_RESERVED1 0x020 +#define X_D3DFVF_DIFFUSE 0x040 +#define X_D3DFVF_SPECULAR 0x080 +#define X_D3DFVF_TEXCOUNT_MASK 0xf00 +#define X_D3DFVF_TEXCOUNT_SHIFT 8 +#define X_D3DFVF_TEX0 0x000 +#define X_D3DFVF_TEX1 0x100 +#define X_D3DFVF_TEX2 0x200 +#define X_D3DFVF_TEX3 0x300 +#define X_D3DFVF_TEX4 0x400 +#define X_D3DFVF_TEX5 0x500 +#define X_D3DFVF_TEX6 0x600 +#define X_D3DFVF_TEX7 0x700 +#define X_D3DFVF_TEX8 0x800 +#define X_D3DFVF_TEXTUREFORMAT1 0x003 +#define X_D3DFVF_TEXTUREFORMAT2 0x000 +#define X_D3DFVF_TEXTUREFORMAT3 0x001 +#define X_D3DFVF_TEXTUREFORMAT4 0x002 + +#define X_D3DFVF_TEXCOORDSIZE1(Index) (X_D3DFVF_TEXTUREFORMAT1 << (Index * 2 + 16)) +#define X_D3DFVF_TEXCOORDSIZE2(Index) (X_D3DFVF_TEXTUREFORMAT2) +#define X_D3DFVF_TEXCOORDSIZE3(Index) (X_D3DFVF_TEXTUREFORMAT3 << (Index * 2 + 16)) +#define X_D3DFVF_TEXCOORDSIZE4(Index) (X_D3DFVF_TEXTUREFORMAT4 << (Index * 2 + 16)) + + typedef DWORD NV2AMETHOD; #endif