Fix when 3DDevice_LoadVertexShaderProgram is called while an FVF shader is set

This commit is contained in:
Luke Usher 2019-04-09 16:53:04 +01:00
parent 43e147777c
commit f8af77d0e1
2 changed files with 136 additions and 21 deletions

View File

@ -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());
}
// ******************************************************************

View File

@ -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