Merge pull request #1894 from PatrickvL/vertex_declaration_refactoring

Vertex declaration refactoring
This commit is contained in:
Luke Usher 2020-11-14 14:04:58 +00:00 committed by GitHub
commit 213dd2f86f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 2252 additions and 2325 deletions

View File

@ -32,9 +32,13 @@ uniform float4 C[X_D3DVS_CONSTREG_COUNT] : register(c0);
uniform float4 vRegisterDefaultValues[16] : register(c192);
uniform float4 vRegisterDefaultFlagsPacked[4] : register(c208);
uniform float4 xboxViewportScale : register(c212);
uniform float4 xboxViewportScaleInverse : register(c212);
uniform float4 xboxViewportOffset : register(c213);
uniform float4 xboxTextureScale[4] : register(c214);
uniform float4 xboxIsRHWTransformedPosition : register(c218);
// Overloaded casts, assuring all inputs are treated as float4
float4 _tof4(float src) { return float4(src, src, src, src); }
float4 _tof4(float2 src) { return src.xyyy; }
@ -149,17 +153,6 @@ float _dph(float4 src0, float4 src1)
// Xbox ILU Functions
// 2.14.1.10.6 RCP: Reciprocal
#define x_rcp(dest, mask, src0) dest.mask = _ssss(_rcp(_scalar(src0))).mask
float _rcp(float src)
{
#if 0 // TODO : Enable
if (src == 1) return 1;
if (src == 0) return 1.#INF;
#endif
return 1/ src;
}
// 2.14.1.10.7 RSQ: Reciprocal Square Root
#define x_rsq(dest, mask, src0) dest.mask = _ssss(_rsq(_scalar(src0))).mask
float _rsq(float src)
@ -251,6 +244,23 @@ float _rcc(float src)
: clamp(r, -1.84467e+019f, -5.42101e-020f); // the IEEE 32-bit binary values 0xDF800000 and 0x9F800000
}
// 2.14.1.10.6 RCP: Reciprocal
#define x_rcp(dest, mask, src0) dest.mask = _ssss(_rcp(_scalar(src0))).mask
float _rcp(float src)
{
// OpenGL/NVidia extension definition
#if 0 // TODO : Enable?
if (src == 1) return 1;
if (src == 0) return 1.#INF;
return 1 / src;
#endif
// Forward to Xbox clamped reciprocal
// So we have defined behaviour with rcp(0)
// This prevents issues with XYZRHW modes
// where the w component may be 0
return _rcc(src);
}
float4 reverseScreenspaceTransform(float4 oPos)
{
// On Xbox, oPos should contain the vertex position in screenspace
@ -261,13 +271,19 @@ float4 reverseScreenspaceTransform(float4 oPos)
// mad oPos.xyz, r12, r1.x, c-37
// where c-37 and c-38 are reserved transform values
if (xboxIsRHWTransformedPosition.x) {
// Detect 0 w and avoid 0 division
if (oPos.w == 0) oPos.w = 1; // if else doesn't seem to work here
oPos.w = 1 / oPos.w; // flip rhw to w
}
// oPos.w and xboxViewportScale.z might be VERY big when a D24 depth buffer is used
// and multiplying oPos.xyz by oPos.w may cause precision issues.
// Pre-divide them to help keep the values reasonably small.
// Test case: Burnout 3
float3 divisor = xboxViewportScale.xyz / oPos.w;
oPos.xyz -= xboxViewportOffset.xyz; // reverse offset
oPos.xyz /= divisor; // reverse scale and perspective divide
oPos.xyz *= oPos.w; // reverse perspective divide
oPos.xyz *= xboxViewportScaleInverse.xyz; // reverse scale
return oPos;
}
@ -315,14 +331,15 @@ R"DELIMITER(
xOut.oPos = reverseScreenspaceTransform(oPos);
xOut.oD0 = saturate(oD0);
xOut.oD1 = saturate(oD1);
xOut.oFog = oFog.x;
xOut.oFog = oFog.x; // Note : Xbox clamps fog in pixel shader
xOut.oPts = oPts.x;
xOut.oB0 = saturate(oB0);
xOut.oB1 = saturate(oB1);
xOut.oT0 = oT0;
xOut.oT1 = oT1;
xOut.oT2 = oT2;
xOut.oT3 = oT3;
// Scale textures (TODO : or should we apply this to the input register values?)
xOut.oT0 = oT0 / xboxTextureScale[0];
xOut.oT1 = oT1 / xboxTextureScale[1];
xOut.oT2 = oT2 / xboxTextureScale[2];
xOut.oT3 = oT3 / xboxTextureScale[3];
return xOut;
}

File diff suppressed because it is too large Load Diff

View File

@ -4195,3 +4195,64 @@ xbox::void_xt WINAPI xbox::EMUPATCH(D3DDevice_SetPixelShaderConstant_4)
hRet = D3D_OK;
}
}
// ******************************************************************
// * patch: D3DDevice_SelectVertexShaderDirect
// ******************************************************************
VOID WINAPI XTL::EMUPATCH(D3DDevice_SelectVertexShaderDirect)
(
X_VERTEXATTRIBUTEFORMAT *pVAF,
DWORD Address
)
{
LOG_FUNC_BEGIN
LOG_FUNC_ARG(pVAF)
LOG_FUNC_ARG(Address)
LOG_FUNC_END;
CxbxImpl_SelectVertexShaderDirect(pVAF, Address);
}
// ******************************************************************
// * patch: D3DDevice_SetVertexShaderInputDirect
// ******************************************************************
VOID WINAPI XTL::EMUPATCH(D3DDevice_SetVertexShaderInputDirect)
(
X_VERTEXATTRIBUTEFORMAT *pVAF,
UINT StreamCount,
X_STREAMINPUT *pStreamInputs
)
{
LOG_FUNC_BEGIN
LOG_FUNC_ARG(pVAF)
LOG_FUNC_ARG(StreamCount)
LOG_FUNC_ARG(pStreamInputs)
LOG_FUNC_END;
// If pVAF is given, it's copied into a global Xbox VertexBuffer struct and
// D3DDevice_SetVertexShaderInput is called with Handle set to that address, or-ed with 1 (X_D3DFVF_RESERVED0)
// Otherwise, D3DDevice_SetVertexShaderInput is called with Handle 0.
LOG_UNIMPLEMENTED();
}
// ******************************************************************
// * patch: D3DDevice_GetVertexShaderInput
// ******************************************************************
HRESULT WINAPI XTL::EMUPATCH(D3DDevice_GetVertexShaderInput)
(
DWORD *pHandle,
UINT *pStreamCount,
X_STREAMINPUT *pStreamInputs
)
{
LOG_FUNC_BEGIN
LOG_FUNC_ARG(pHandle)
LOG_FUNC_ARG(pStreamCount)
LOG_FUNC_ARG(pStreamInputs)
LOG_FUNC_END;
LOG_UNIMPLEMENTED();
return 0;
}

View File

@ -63,6 +63,9 @@ extern uint8_t *ConvertD3DTextureToARGB(
void CxbxUpdateNativeD3DResources();
void CxbxImpl_SetRenderTarget(xbox::X_D3DSurface* pRenderTarget, xbox::X_D3DSurface* pNewZStencil);
void CxbxImpl_SetViewPort(xbox::X_D3DVIEWPORT8* pViewport);
// initialize direct3d
extern void EmuD3DInit();
@ -214,7 +217,7 @@ xbox::void_xt WINAPI EMUPATCH(D3DDevice_LoadVertexShader)
);
xbox::void_xt __stdcall EMUPATCH(D3DDevice_LoadVertexShader_0)();
xbox::void_xt WINAPI EMUPATCH(D3DDevice_LoadVertexShader_4)
xbox::void_xt EMUPATCH(D3DDevice_LoadVertexShader_4)
(
dword_xt Address
);
@ -332,7 +335,7 @@ xbox::void_xt WINAPI EMUPATCH(D3DDevice_GetBackBuffer)
// ******************************************************************
xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetViewport)
(
CONST X_D3DVIEWPORT8 *pViewport
X_D3DVIEWPORT8 *pViewport
);
// ******************************************************************
@ -1602,15 +1605,6 @@ xbox::void_xt WINAPI EMUPATCH(D3DDevice_DeleteVertexShader)
xbox::void_xt WINAPI EMUPATCH(D3DDevice_DeleteVertexShader_0)();
// ******************************************************************
// * patch: D3DDevice_SelectVertexShaderDirect
// ******************************************************************
xbox::void_xt WINAPI EMUPATCH(D3DDevice_SelectVertexShaderDirect)
(
X_VERTEXATTRIBUTEFORMAT *pVAF,
dword_xt Address
);
// ******************************************************************
// * patch: D3DDevice_GetShaderConstantMode
// ******************************************************************
@ -1637,16 +1631,6 @@ xbox::void_xt WINAPI EMUPATCH(D3DDevice_GetVertexShaderConstant)
dword_xt ConstantCount
);
// ******************************************************************
// * patch: D3DDevice_SetVertexShaderInputDirect
// ******************************************************************
xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetVertexShaderInputDirect)
(
X_VERTEXATTRIBUTEFORMAT *pVAF,
uint_xt StreamCount,
X_STREAMINPUT *pStreamInputs
);
// ******************************************************************
// * patch: D3DDevice_GetVertexShaderInput
// ******************************************************************
@ -1658,7 +1642,7 @@ xbox::hresult_xt WINAPI EMUPATCH(D3DDevice_GetVertexShaderInput)
);
// ******************************************************************
// * patch: D3DDevice_GetVertexShaderInput
// * patch: D3DDevice_SetVertexShaderInput
// ******************************************************************
xbox::void_xt WINAPI EMUPATCH(D3DDevice_SetVertexShaderInput)
(

View File

@ -77,7 +77,7 @@ TextureStateInfo CxbxTextureStateInfo[] = {
bool XboxTextureStateConverter::Init(XboxRenderStateConverter* pState)
{
// Deferred states start at 0, this menas that D3DDeferredTextureState IS D3D__TextureState
// Deferred states start at 0, this means that D3DDeferredTextureState IS D3D__TextureState
// No further works is required to derive the offset
if (g_SymbolAddresses.find("D3DDeferredTextureState") != g_SymbolAddresses.end()) {
D3D__TextureState = (uint32_t*)g_SymbolAddresses["D3DDeferredTextureState"];
@ -184,36 +184,84 @@ void XboxTextureStateConverter::Apply()
switch (State) {
// These types map 1:1 but have some unsupported values
case xbox::X_D3DTSS_ADDRESSU: case xbox::X_D3DTSS_ADDRESSV: case xbox::X_D3DTSS_ADDRESSW:
if (Value == xbox::X_D3DTADDRESS_CLAMPTOEDGE) {
EmuLog(LOG_LEVEL::WARNING, "D3DTADDRESS_CLAMPTOEDGE is unsupported");
switch (Value) {
case 0: // Let's ignore zero (its no known X_D3DTADDRESS_ mode, but logging this seems useless)
case xbox::X_D3DTADDRESS_WRAP: // = 1 = D3DTADDRESS_WRAP = 1,
case xbox::X_D3DTADDRESS_MIRROR: // = 2 = D3DTADDRESS_MIRROR = 2,
case xbox::X_D3DTADDRESS_CLAMP: // = 3 = D3DTADDRESS_CLAMP = 3,
case xbox::X_D3DTADDRESS_BORDER: // = 4 = D3DTADDRESS_BORDER = 4,
// These match host Direct3D 9 values, so no update necessary
break;
case xbox::X_D3DTADDRESS_CLAMPTOEDGE: // = 5
LOG_TEST_CASE("X_D3DTADDRESS_CLAMPTOEDGE unsupported, falling back to D3DTADDRESS_BORDER");
// D3DTADDRESS_BORDER is the closest host match, CLAMPTOEDGE is identical
// Except it has additional restrictions.
Value = D3DTADDRESS_BORDER;
break;
default:
EmuLog(LOG_LEVEL::WARNING, "Unsupported X_D3DTSS_ADDRESS? value %x", Value);
Value = D3DTADDRESS_WRAP;
break;
}
break;
case xbox::X_D3DTSS_MAGFILTER: case xbox::X_D3DTSS_MINFILTER: case xbox::X_D3DTSS_MIPFILTER:
if (Value == xbox::X_D3DTEXF_QUINCUNX) {
EmuLog(LOG_LEVEL::WARNING, "D3DTEXF_QUINCUNX is unsupported");
// Fallback to D3DTEXF_ANISOTROPIC
switch (Value) {
case xbox::X_D3DTEXF_NONE: // = 0 = D3DTEXF_NONE = 0, // filtering disabled (valid for mip filter only)
case xbox::X_D3DTEXF_POINT: // = 1 = D3DTEXF_POINT = 1, // nearest
case xbox::X_D3DTEXF_LINEAR: // = 2 = D3DTEXF_LINEAR = 2, // linear interpolation
case xbox::X_D3DTEXF_ANISOTROPIC: // = 3 = D3DTEXF_ANISOTROPIC = 3, // anisotropic
// These match host Direct3D 9 values, so no update necessary
break;
case xbox::X_D3DTEXF_QUINCUNX: // = 4; // quincunx kernel (Xbox extension), also known as "flat cubic"
LOG_TEST_CASE("X_D3DTEXF_QUINCUNX unsupported, falling back to D3DTEXF_ANISOTROPIC");
Value = D3DTEXF_ANISOTROPIC;
break;
}
case xbox::X_D3DTEXF_GAUSSIANCUBIC: // = 5 // Xbox extension, different cubic kernel
// Direct3D 9 alternatives :
// D3DTEXF_PYRAMIDALQUAD = 6, // 4-sample tent
// D3DTEXF_GAUSSIANQUAD = 7, // 4-sample gaussian
// D3DTEXF_CONVOLUTIONMONO = 8, // Convolution filter for monochrome textures
LOG_TEST_CASE("X_D3DTEXF_QUINCUNX unsupported, falling back to D3DTEXF_GAUSSIANQUAD");
Value = D3DTEXF_GAUSSIANQUAD;
break;
case xbox::X_D3DTSS_TEXCOORDINDEX:
switch (Value) {
case 0x00040000:
// This value is TCI_OBJECT on Xbox,which is not supported by the host
// In this case, we reset to 0.
EmuLog(LOG_LEVEL::WARNING, "EmuD3DDevice_SetTextureState_TexCoordIndex: D3DTSS_TCI_OBJECT is unsupported", Value);
Value = 0;
break;
case 0x00050000:
// This value is TCI_SPHERE on Xbox, let's map it to D3DTSS_TCI_SPHEREMAP for the host
Value = D3DTSS_TCI_SPHEREMAP;
default:
EmuLog(LOG_LEVEL::WARNING, "Unsupported X_D3DTSS_M??FILTER value %x", Value);
Value = D3DTEXF_NONE;
break;
}
break;
case xbox::X_D3DTSS_TEXCOORDINDEX: {
int texCoordIndex = Value & 0x0000FFFF;
if (texCoordIndex > 3) {
LOG_TEST_CASE("TEXCOORDINDEX out of bounds, masking to lowest 2 bits");
texCoordIndex = Value & 3;
}
switch (Value & 0xFFFF0000) {
case X_D3DTSS_TCI_PASSTHRU: // = 0x00000000
case X_D3DTSS_TCI_CAMERASPACENORMAL: // = 0x00010000
case X_D3DTSS_TCI_CAMERASPACEPOSITION: // = 0x00020000
case X_D3DTSS_TCI_CAMERASPACEREFLECTIONVECTOR: // = 0x00030000
// These match host Direct3D 9 values, so no update necessary
break;
case X_D3DTSS_TCI_OBJECT: // = 0x00040000
// Collides with host Direct3D 9 D3DTSS_TCI_SPHEREMAP
// This value is not supported on host in Direct3D 9
// It probably means "TexGen ObjectLinear", or '(untransformed) object space identity mapping'
LOG_TEST_CASE("Xbox D3DTSS_TCI_OBJECT unsupported on host");
// Test-case : Terrain XDK sample
Value = texCoordIndex;
break;
case X_D3DTSS_TCI_SPHEREMAP: // = 0x00050000
// Convert Xbox sphere mapping bit to host Direct3D 9 (which uses a different bit)
Value = D3DTSS_TCI_SPHEREMAP | texCoordIndex;
break;
default:
EmuLog(LOG_LEVEL::WARNING, "Unsupported X_D3DTSS_TEXCOORDINDEX value %x", Value);
Value = texCoordIndex;
break;
}
break;
}
// These types require value remapping for all supported values
case xbox::X_D3DTSS_COLOROP: case xbox::X_D3DTSS_ALPHAOP:
Value = GetHostTextureOpValue(Value);
@ -273,3 +321,12 @@ void XboxTextureStateConverter::Apply()
// no need to actually copy here, since it was handled in the loop above
}
}
uint32_t XboxTextureStateConverter::Get(int textureStage, DWORD xboxState) {
if (textureStage < 0 || textureStage > 3)
CxbxKrnlCleanup("Requested texture stage was out of range: %d", textureStage);
if (xboxState < xbox::X_D3DTSS_FIRST || xboxState > xbox::X_D3DTSS_LAST)
CxbxKrnlCleanup("Requested texture state was out of range: %d", xboxState);
return D3D__TextureState[(textureStage * xbox::X_D3DTS_STAGESIZE) + xboxState];
}

View File

@ -38,6 +38,7 @@ class XboxTextureStateConverter
public:
bool Init(XboxRenderStateConverter* state);
void Apply();
uint32_t Get(int textureStage, DWORD xboxState);
private:
void BuildTextureStateMappingTable();

View File

@ -220,44 +220,21 @@ extern ShaderType EmuGetShaderInfo(IntermediateVertexShader* pIntermediateShader
return ShaderType::Compilable;
}
// recompile xbox vertex shader function
extern HRESULT EmuCompileShader
(
IntermediateVertexShader* pIntermediateShader,
ID3DBlob** ppHostShader
)
HRESULT CompileHlsl(const std::string& hlsl, ID3DBlob** ppHostShader, const char* pSourceName)
{
// TODO include header in vertex shader
//xbox::X_VSH_SHADER_HEADER* pXboxVertexShaderHeader = (xbox::X_VSH_SHADER_HEADER*)pXboxFunction;
ID3DBlob* pErrors = nullptr;
ID3DBlob* pErrorsCompatibility = nullptr;
HRESULT hRet = 0;
// Include HLSL header and footer as raw strings :
static std::string hlsl_template[2] = {
#include "core\hle\D3D8\Direct3D9\CxbxVertexShaderTemplate.hlsl"
};
auto hlsl_stream = std::stringstream();
hlsl_stream << hlsl_template[0]; // Start with the HLSL template header
assert(pIntermediateShader->Instructions.size() > 0);
BuildShader(pIntermediateShader, hlsl_stream);
hlsl_stream << hlsl_template[1]; // Finish with the HLSL template footer
std::string hlsl_str = hlsl_stream.str();
EmuLog(LOG_LEVEL::DEBUG, "--- HLSL conversion ---");
EmuLog(LOG_LEVEL::DEBUG, DebugPrependLineNumbers(hlsl_str).c_str());
EmuLog(LOG_LEVEL::DEBUG, "-----------------------");
UINT flags1 = D3DCOMPILE_OPTIMIZATION_LEVEL3 | D3DCOMPILE_AVOID_FLOW_CONTROL;
UINT flags1 = D3DCOMPILE_OPTIMIZATION_LEVEL3;
hRet = D3DCompile(
hlsl_str.c_str(),
hlsl_str.length(),
nullptr, // pSourceName
hlsl.c_str(),
hlsl.length(),
pSourceName, // pSourceName
nullptr, // pDefines
nullptr, // pInclude // TODO precompile x_* HLSL functions?
D3D_COMPILE_STANDARD_FILE_INCLUDE, // pInclude // TODO precompile x_* HLSL functions?
"main", // shader entry poiint
g_vs_model, // shader profile
flags1, // flags1
@ -266,25 +243,26 @@ extern HRESULT EmuCompileShader
&pErrors // ppErrorMsgs out
);
if (FAILED(hRet)) {
EmuLog(LOG_LEVEL::WARNING, "Shader compile failed. Recompiling in compatibility mode");
// Attempt to retry in compatibility mode, this allows some vertex-state shaders to compile
// Test Case: Spy vs Spy
flags1 |= D3DCOMPILE_ENABLE_BACKWARDS_COMPATIBILITY;
flags1 |= D3DCOMPILE_ENABLE_BACKWARDS_COMPATIBILITY | D3DCOMPILE_AVOID_FLOW_CONTROL;
hRet = D3DCompile(
hlsl_str.c_str(),
hlsl_str.length(),
nullptr, // pSourceName
hlsl.c_str(),
hlsl.length(),
pSourceName, // pSourceName
nullptr, // pDefines
nullptr, // pInclude // TODO precompile x_* HLSL functions?
D3D_COMPILE_STANDARD_FILE_INCLUDE, // pInclude // TODO precompile x_* HLSL functions?
"main", // shader entry poiint
g_vs_model, // shader profile
flags1, // flags1
0, // flags2
ppHostShader, // out
&pErrors // ppErrorMsgs out
&pErrorsCompatibility // ppErrorMsgs out
);
if (FAILED(hRet)) {
LOG_TEST_CASE("Couldn't assemble recompiled vertex shader");
LOG_TEST_CASE("Couldn't assemble vertex shader");
//EmuLog(LOG_LEVEL::WARNING, "Couldn't assemble recompiled vertex shader");
}
}
@ -296,6 +274,10 @@ extern HRESULT EmuCompileShader
EmuLog(hlslErrorLogLevel, "%s", (char*)(pErrors->GetBufferPointer()));
pErrors->Release();
pErrors = nullptr;
if (pErrorsCompatibility != nullptr) {
pErrorsCompatibility->Release();
pErrorsCompatibility = nullptr;
}
}
LOG_CHECK_ENABLED(LOG_LEVEL::DEBUG)
@ -317,3 +299,156 @@ extern HRESULT EmuCompileShader
return hRet;
}
// recompile xbox vertex shader function
extern HRESULT EmuCompileShader
(
IntermediateVertexShader* pIntermediateShader,
ID3DBlob** ppHostShader
)
{
// Include HLSL header and footer as raw strings :
static std::string hlsl_template[2] = {
#include "core\hle\D3D8\Direct3D9\CxbxVertexShaderTemplate.hlsl"
};
auto hlsl_stream = std::stringstream();
hlsl_stream << hlsl_template[0]; // Start with the HLSL template header
assert(pIntermediateShader->Instructions.size() > 0);
BuildShader(pIntermediateShader, hlsl_stream);
hlsl_stream << hlsl_template[1]; // Finish with the HLSL template footer
std::string hlsl_str = hlsl_stream.str();
EmuLog(LOG_LEVEL::DEBUG, "--- HLSL conversion ---");
EmuLog(LOG_LEVEL::DEBUG, DebugPrependLineNumbers(hlsl_str).c_str());
EmuLog(LOG_LEVEL::DEBUG, "-----------------------");
return CompileHlsl(hlsl_str, ppHostShader, "CxbxVertexShaderTemplate.hlsl");
}
static ID3DBlob* pPassthroughShader = nullptr;
extern HRESULT EmuCompileXboxPassthrough(ID3DBlob** ppHostShader)
{
// TODO does this need to be thread safe?
if (pPassthroughShader == nullptr) {
auto hlsl =
R"(
// Xbox HLSL pretransformed vertex shader
// Default values for vertex registers, and whether to use them
uniform float4 vRegisterDefaultValues[16] : register(c192);
uniform float4 vRegisterDefaultFlagsPacked[4] : register(c208);
uniform float4 xboxViewportScaleInverse : register(c212);
uniform float4 xboxViewportOffset : register(c213);
uniform float4 xboxTextureScale[4] : register(c214);
uniform float4 xboxIsRHWTransformedPosition : register(c218);
struct VS_INPUT
{
float4 v[16] : TEXCOORD;
};
// Output registers
struct VS_OUTPUT
{
float4 oPos : POSITION; // Homogeneous clip space position
float4 oD0 : COLOR0; // Primary color (front-facing)
float4 oD1 : COLOR1; // Secondary color (front-facing)
float oFog : FOG; // Fog coordinate
float oPts : PSIZE; // Point size
float4 oB0 : TEXCOORD4; // Back-facing primary color
float4 oB1 : TEXCOORD5; // Back-facing secondary color
float4 oT0 : TEXCOORD0; // Texture coordinate set 0
float4 oT1 : TEXCOORD1; // Texture coordinate set 1
float4 oT2 : TEXCOORD2; // Texture coordinate set 2
float4 oT3 : TEXCOORD3; // Texture coordinate set 3
};
float4 reverseScreenspaceTransform(float4 oPos)
{
// Scale screenspace coordinates (0 to viewport width/height) to -1 to +1 range
// On Xbox, oPos should contain the vertex position in screenspace
// We need to reverse this transformation
// Conventionally, each Xbox Vertex Shader includes instructions like this
// mul oPos.xyz, r12, c-38
// +rcc r1.x, r12.w
// mad oPos.xyz, r12, r1.x, c-37
// where c-37 and c-38 are reserved transform values
if (xboxIsRHWTransformedPosition.x) {
// Detect 0 w and avoid 0 division
if (oPos.w == 0) oPos.w = 1; // if else doesn't seem to work here
oPos.w = 1 / oPos.w; // flip rhw to w
}
oPos.xyz -= xboxViewportOffset.xyz; // reverse offset
oPos.xyz *= oPos.w; // reverse perspective divide
oPos.xyz *= xboxViewportScaleInverse.xyz; // reverse scale
return oPos;
}
VS_OUTPUT main(const VS_INPUT xIn)
{
// Input registers
float4 v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15;
// Unpack 16 flags from 4 float4 constant registers
float vRegisterDefaultFlags[16] = (float[16])vRegisterDefaultFlagsPacked;
// Initialize input registers from the vertex buffer data
// Or use the register's default value (which can be changed by the title)
#define init_v(i) v##i = lerp(xIn.v[i], vRegisterDefaultValues[i], vRegisterDefaultFlags[i]);
// Note : unroll manually instead of for-loop, because of the ## concatenation
init_v( 0); init_v( 1); init_v( 2); init_v( 3);
init_v( 4); init_v( 5); init_v( 6); init_v( 7);
init_v( 8); init_v( 9); init_v(10); init_v(11);
init_v(12); init_v(13); init_v(14); init_v(15);
// For passthrough, map output variables to their corresponding input registers
float4 oPos = v0;
float4 oD0 = v3;
float4 oD1 = v4;
float4 oFog = v5;
float4 oPts = v6;
float4 oB0 = v7;
float4 oB1 = v8;
float4 oT0 = v9;
float4 oT1 = v10;
float4 oT2 = v11;
float4 oT3 = v12;
// Copy variables to output struct
VS_OUTPUT xOut;
xOut.oPos = reverseScreenspaceTransform(oPos);
xOut.oD0 = saturate(oD0);
xOut.oD1 = saturate(oD1);
xOut.oFog = oFog.x; // Note : Xbox clamps fog in pixel shader
xOut.oPts = oPts.x;
xOut.oB0 = saturate(oB0);
xOut.oB1 = saturate(oB1);
// Scale textures (TODO : or should we apply this to the input register values?)
xOut.oT0 = oT0 / xboxTextureScale[0];
xOut.oT1 = oT1 / xboxTextureScale[1];
xOut.oT2 = oT2 / xboxTextureScale[2];
xOut.oT3 = oT3 / xboxTextureScale[3];
return xOut;
}
)";
CompileHlsl(hlsl, &pPassthroughShader, "passthrough.hlsl");
}
*ppHostShader = pPassthroughShader;
return 0;
}

View File

@ -22,4 +22,6 @@ extern HRESULT EmuCompileShader
ID3DBlob** ppHostShader
);
extern HRESULT EmuCompileXboxPassthrough(ID3DBlob** ppHostShader);
#endif

View File

@ -43,7 +43,7 @@ ID3DBlob* AsyncCreateVertexShader(IntermediateVertexShader intermediateShader, S
// Create a new shader
// If the shader was already created, just increase its reference count
ShaderKey VertexShaderSource::CreateShader(const DWORD* pXboxFunction, DWORD *pXboxFunctionSize) {
ShaderKey VertexShaderSource::CreateShader(const xbox::dword_xt* pXboxFunction, DWORD *pXboxFunctionSize) {
IntermediateVertexShader intermediateShader;
// Parse into intermediate format

View File

@ -11,7 +11,7 @@ typedef uint64_t ShaderKey;
class VertexShaderSource {
public:
ShaderKey CreateShader(const DWORD* pXboxFunction, DWORD* pXboxFunctionSize);
ShaderKey CreateShader(const xbox::dword_xt* pXboxFunction, DWORD* pXboxFunctionSize);
IDirect3DVertexShader *GetShader(ShaderKey key);
void ReleaseShader(ShaderKey key);

View File

@ -929,7 +929,7 @@ static const FormatInfo FormatInfos[] = {
/* 0x33 X_D3DFMT_V16U16 */ { 32, Swzzld, NoCmpnts, D3DFMT_V16U16 },
/* 0x34 undefined */ {},
/* 0x35 X_D3DFMT_LIN_L16 */ { 16, Linear, _____L16, D3DFMT_L16 },
/* 0x36 X_D3DFMT_LIN_V16U16 */ { 32, Linear, NoCmpnts, D3DFMT_V16U16 }, // Note : Seems ununsed on Xbox
/* 0x36 X_D3DFMT_LIN_V16U16 */ { 32, Linear, NoCmpnts, D3DFMT_V16U16 }, // Note : Seems unused on Xbox
/* 0x37 X_D3DFMT_LIN_L6V5U5 */ { 16, Linear, __R6G5B5, D3DFMT_L6V5U5 }, // Alias : X_D3DFMT_LIN_R6G5B5
/* 0x38 X_D3DFMT_R5G5B5A1 */ { 16, Swzzld, R5G5B5A1, D3DFMT_A1R5G5B5 , Texture, "X_D3DFMT_R5G5B5A1 -> D3DFMT_A1R5G5B5" },
/* 0x39 X_D3DFMT_R4G4B4A4 */ { 16, Swzzld, R4G4B4A4, D3DFMT_A4R4G4B4 , Texture, "X_D3DFMT_R4G4B4A4 -> D3DFMT_A4R4G4B4" },

View File

@ -1020,9 +1020,22 @@ typedef DWORD X_VERTEXSHADERCONSTANTMODE;
// Special Registers, used to pass additional information to the shaders
// TODO co-locate shader workaround constants with shader code
#define CXBX_D3DVS_CONSTREG_VREGDEFAULTS_BASE (X_D3DVS_CONSTREG_COUNT)
#define CXBX_D3DVS_CONSTREG_VREGDEFAULTS_FLAG_BASE (CXBX_D3DVS_CONSTREG_VREGDEFAULTS_BASE + 16)
#define CXBX_D3DVS_VIEWPORT_SCALE_MIRROR (CXBX_D3DVS_CONSTREG_VREGDEFAULTS_FLAG_BASE + 4)
#define CXBX_D3DVS_VIEWPORT_OFFSET_MIRROR (CXBX_D3DVS_VIEWPORT_SCALE_MIRROR + 1)
#define CXBX_D3DVS_CONSTREG_VREGDEFAULTS_SIZE 16
#define CXBX_D3DVS_CONSTREG_VREGDEFAULTS_FLAG_BASE (CXBX_D3DVS_CONSTREG_VREGDEFAULTS_BASE + CXBX_D3DVS_CONSTREG_VREGDEFAULTS_SIZE)
#define CXBX_D3DVS_CONSTREG_VREGDEFAULTS_FLAG_SIZE 4
#define CXBX_D3DVS_VIEWPORT_SCALE_INVERSE_BASE (CXBX_D3DVS_CONSTREG_VREGDEFAULTS_FLAG_BASE + CXBX_D3DVS_CONSTREG_VREGDEFAULTS_FLAG_SIZE)
#define CXBX_D3DVS_VIEWPORT_SCALE_INVERSE_SIZE 1
#define CXBX_D3DVS_VIEWPORT_OFFSET_MIRROR_BASE (CXBX_D3DVS_VIEWPORT_SCALE_INVERSE_BASE + CXBX_D3DVS_VIEWPORT_SCALE_INVERSE_SIZE)
#define CXBX_D3DVS_VIEWPORT_OFFSET_MIRROR_SIZE 1
#define CXBX_D3DVS_TEXTURES_SCALE_BASE (CXBX_D3DVS_VIEWPORT_OFFSET_MIRROR_BASE + CXBX_D3DVS_VIEWPORT_OFFSET_MIRROR_SIZE)
#define CXBX_D3DVS_TEXTURES_SCALE_SIZE 4
#define CXBX_D3DVS_IS_RHW_TRANSFORMED_POSITION_BASE (CXBX_D3DVS_TEXTURES_SCALE_BASE + CXBX_D3DVS_TEXTURES_SCALE_SIZE)
#define CXBX_D3DVS_IS_RHW_TRANSFORMED_POSITION_SIZE 1
#define X_D3DSCM_RESERVED_CONSTANT_SCALE_CORRECTED (X_D3DSCM_RESERVED_CONSTANT_SCALE + X_D3DSCM_CORRECTION)
#define X_D3DSCM_RESERVED_CONSTANT_OFFSET_CORRECTED (X_D3DSCM_RESERVED_CONSTANT_OFFSET + X_D3DSCM_CORRECTION)
@ -1042,11 +1055,13 @@ typedef DWORD X_VERTEXSHADERCONSTANTMODE;
#define X_VSH_MAX_ATTRIBUTES 16
#define X_VSH_MAX_STREAMS 16
#define X_VSH_MAX_INSTRUCTION_COUNT 136 // The maximum Xbox shader instruction count
#define X_VSH_INSTRUCTION_SIZE 4
#define X_VSH_INSTRUCTION_SIZE_BYTES (X_VSH_INSTRUCTION_SIZE * sizeof(DWORD))
// Xbox Vertex Shader versions
#define VERSION_XVS 0x2078 // 'x ' Xbox vertex shader
#define VERSION_XVSS 0x7378 // 'xs' Xbox vertex state shader
#define VERSION_XVSW 0x7778 // 'xw' Xbox vertex read/write shader
#define VERSION_XVS 0x2078 // 'x ' Xbox vertex shader. Corresponds to X_VST_NORMAL
#define VERSION_XVSS 0x7378 // 'xs' Xbox vertex state shader. Corresponds to X_VST_STATE
#define VERSION_XVSW 0x7778 // 'xw' Xbox vertex read/write shader. Corresponds to X_VST_READWRITE
/// nv2a microcode header
typedef struct
@ -1056,22 +1071,30 @@ typedef struct
}
X_VSH_SHADER_HEADER;
#define X_VSH_INSTRUCTION_SIZE 4
#define X_VSH_INSTRUCTION_SIZE_BYTES (X_VSH_INSTRUCTION_SIZE * sizeof(DWORD))
// ******************************************************************
// * X_VERTEXSHADERINPUT
// ******************************************************************
typedef struct _X_VERTEXSHADERINPUT
{
DWORD IndexOfStream;
DWORD StreamIndex;
DWORD Offset;
DWORD Format;
BYTE TesselationType;
BYTE TesselationSource;
BYTE TessellationType;
BYTE TessellationSource;
BYTE Padding0;
BYTE Padding1;
}
X_VERTEXSHADERINPUT;
typedef struct {
DWORD StreamIndex;
DWORD Offset;
DWORD SizeAndType;
BYTE Flags;
BYTE Source;
}
X_VertexShaderSlot;
// ******************************************************************
// * X_VERTEXATTRIBUTEFORMAT
// ******************************************************************
@ -1092,34 +1115,64 @@ typedef struct _X_STREAMINPUT
UINT Offset;
} X_STREAMINPUT;
struct X_D3DVertexShader3948
{
#if 0
DWORD Signature; // Note : Debug XBE's have a 'Vshd' DWORD signature prefix
#endif
DWORD RefCount; // Based on the observation this member is set to 1 in D3DDevice_CreateVertexShader and decreased in D3DDevice_DeleteVertexShader
DWORD Flags; // Seems to contain at solely the four X_D3DUSAGE_PERSISTENT* flags
DWORD MaxSlot;
DWORD TextureCount;
DWORD ProgramSize;
DWORD ProgramAndConstantsDwords; // Sum of ProgramSize + constant count, expressed in instruction slots, taking 4 DWORD's per slot (see X_VSH_INSTRUCTION_SIZE)
DWORD Dimensionality[4] ; // Guesswork, since all 4 bytes (for all 4 textures) are most often set to 0 (or 2 when a texture isn't used) and 1, 3 and 4 also occur (and nothing else)
X_VERTEXATTRIBUTEFORMAT VertexAttribute;
X_VertexShaderSlot Slot[4]; // Four more (for a total of 20)
DWORD ProgramAndConstants[1 /*declare more for debugging purposes */+ X_VSH_MAX_INSTRUCTION_COUNT]; // The binary function data and constants (contents continues futher outside this struct, up to ProgramAndConstantsDwords * 4 (=X_VSH_INSTRUCTION_SIZE) DWORD's)
};
struct X_D3DVertexShader
{
// Note : Debug XBE's have a 'Vshd' DWORD signature prefixing this!
#if 0
DWORD Signature; // Note : Debug XBE's have a 'Vshd' DWORD signature prefix
#endif
DWORD RefCount; // Based on the observation this member is set to 1 in D3DDevice_CreateVertexShader and decreased in D3DDevice_DeleteVertexShader
DWORD Flags;
DWORD FunctionSize; // ?Also known as ProgramSize?
DWORD TotalSize; // seems to include both the function and ?constants?
DWORD NumberOfDimensionsPerTexture; // Guesswork, since all 4 bytes (for all 4 textures) are most often set to 0 (or 2 when a texture isn't used) and 1, 3 and 4 also occur (and nothing else)
DWORD Flags; // Contains X_VERTEXSHADER_FLAG_* bits
DWORD ProgramSize;
DWORD ProgramAndConstantsDwords; // Sum of ProgramSize + constant count, expressed in instruction slots, taking 4 DWORD's per slot (see X_VSH_INSTRUCTION_SIZE)
BYTE Dimensionality[4] ; // Guesswork, since all 4 bytes (for all 4 textures) are most often set to 0 (or 2 when a texture isn't used) and 1, 3 and 4 also occur (and nothing else)
X_VERTEXATTRIBUTEFORMAT VertexAttribute;
DWORD FunctionData[X_VSH_MAX_INSTRUCTION_COUNT]; // probably the binary function data and ?constants? (data continues futher outside this struct, up to TotalSize DWORD's)
DWORD ProgramAndConstants[X_VSH_MAX_INSTRUCTION_COUNT]; // The binary function data and constants (contents continues futher outside this struct, up to ProgramAndConstantsDwords * 4 (=X_VSH_INSTRUCTION_SIZE) DWORD's)
};
// X_D3DVertexShader.Flags values :
#define X_VERTEXSHADER_FLAG_WRITE (1 << 0) // = 0x0001 // Set for Xbox ShaderType != X_VST_NORMAL
#define X_VERTEXSHADER_FLAG_PASSTHROUGH (1 << 1) // = 0x0002
#define X_VERTEXSHADER_FLAG_UNKNOWN (1 << 2) // = 0x0004 // Test case: Amped
#define X_VERTEXSHADER_FLAG_STATE (1 << 3) // = 0x0008 // Set for Xbox ShaderType == X_VST_STATE
#define X_VERTEXSHADER_FLAG_PROGRAM (1 << 4) // = 0x0010 // Set when X_D3DVertexShader was created with assigned function data
#define X_VERTEXSHADER_FLAG_HASDIFFUSE (1 << 10) // = 0x0400 Corresponds to X_D3DUSAGE_PERSISTENTDIFFUSE
#define X_VERTEXSHADER_FLAG_HASSPECULAR (1 << 11) // = 0x0800 Corresponds to X_D3DUSAGE_PERSISTENTSPECULAR
#define X_VERTEXSHADER_FLAG_HASBACKDIFFUSE (1 << 12) // = 0x1000 Corresponds to X_D3DUSAGE_PERSISTENTBACKDIFFUSE
#define X_VERTEXSHADER_FLAG_HASBACKSPECULAR (1 << 13) // = 0x2000 Corresponds to X_D3DUSAGE_PERSISTENTBACKSPECULAR
// vertex shader input registers for fixed function vertex shader
// Name Register number D3DFVF
const int X_D3DVSDE_POSITION = 0; // Corresponds to D3DFVF_XYZ
const int X_D3DVSDE_BLENDWEIGHT = 1; // Corresponds to D3DFVF_XYZRHW
const int X_D3DVSDE_NORMAL = 2; // Corresponds to D3DFVF_NORMAL
const int X_D3DVSDE_DIFFUSE = 3; // Corresponds to D3DFVF_DIFFUSE
const int X_D3DVSDE_SPECULAR = 4; // Corresponds to D3DFVF_SPECULAR
const int X_D3DVSDE_POSITION = 0; // Corresponds to X_D3DFVF_XYZ
const int X_D3DVSDE_BLENDWEIGHT = 1; // Corresponds to X_D3DFVF_XYZB1? (was X_D3DFVF_XYZRHW?)
const int X_D3DVSDE_NORMAL = 2; // Corresponds to X_D3DFVF_NORMAL
const int X_D3DVSDE_DIFFUSE = 3; // Corresponds to X_D3DFVF_DIFFUSE
const int X_D3DVSDE_SPECULAR = 4; // Corresponds to X_D3DFVF_SPECULAR
const int X_D3DVSDE_FOG = 5; // Xbox extension
const int X_D3DVSDE_POINTSIZE = 6; // Dxbx addition
const int X_D3DVSDE_BACKDIFFUSE = 7; // Xbox extension
const int X_D3DVSDE_BACKSPECULAR = 8; // Xbox extension
const int X_D3DVSDE_TEXCOORD0 = 9; // Corresponds to D3DFVF_TEX1 (not D3DFVF_TEX0, which means no textures are present)
const int X_D3DVSDE_TEXCOORD1 = 10; // Corresponds to D3DFVF_TEX2
const int X_D3DVSDE_TEXCOORD2 = 11; // Corresponds to D3DFVF_TEX3
const int X_D3DVSDE_TEXCOORD3 = 12; // Corresponds to D3DFVF_TEX4
const int X_D3DVSDE_TEXCOORD0 = 9; // Corresponds to X_D3DFVF_TEX1 (not X_D3DFVF_TEX0, which means no textures are present)
const int X_D3DVSDE_TEXCOORD1 = 10; // Corresponds to X_D3DFVF_TEX2
const int X_D3DVSDE_TEXCOORD2 = 11; // Corresponds to X_D3DFVF_TEX3
const int X_D3DVSDE_TEXCOORD3 = 12; // Corresponds to X_D3DFVF_TEX4
const int X_D3DVSDE_VERTEX = 0xFFFFFFFF; // Xbox extension for Begin/End drawing (data is a D3DVSDT_FLOAT4)
//typedef X_D3DVSDE = X_D3DVSDE_POSITION..High(DWORD)-2; // Unique declaration to make overloads possible;
@ -1233,12 +1286,22 @@ typedef enum _X_D3DVSD_TOKENTYPE
#define X_D3DFVF_TEXTUREFORMAT2 0x000
#define X_D3DFVF_TEXTUREFORMAT3 0x001
#define X_D3DFVF_TEXTUREFORMAT4 0x002
#define X_D3DFVF_TEXCOORDSIZE_SHIFT(Index) ((Index) * 2 + 16)
#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))
// Values, used with D3DTSS_TEXCOORDINDEX, to specify that the vertex data (position
// and normal in the camera space) should be taken as texture coordinates.
// Low 16 bits are used to specify texture coordinate index, to take the WRAP mode from.
#define X_D3DTSS_TCI_PASSTHRU 0x00000000
#define X_D3DTSS_TCI_CAMERASPACENORMAL 0x00010000
#define X_D3DTSS_TCI_CAMERASPACEPOSITION 0x00020000
#define X_D3DTSS_TCI_CAMERASPACEREFLECTIONVECTOR 0x00030000
#define X_D3DTSS_TCI_OBJECT 0x00040000 // Warning! Collides with host Direct3D 9 D3DTSS_TCI_SPHEREMAP
#define X_D3DTSS_TCI_SPHEREMAP 0x00050000
typedef DWORD NV2AMETHOD;
//

View File

@ -66,8 +66,9 @@
#include <process.h>
#include <locale.h>
#include "Direct3D9\RenderStates.h"
extern XboxRenderStateConverter XboxRenderStates;
#include "Direct3D9\RenderStates.h" // For XboxRenderStateConverter
extern XboxRenderStateConverter XboxRenderStates; // Declared in Direct3D9.cpp
#define DbgPshPrintf \
LOG_CHECK_ENABLED(LOG_LEVEL::DEBUG) \

View File

@ -40,82 +40,12 @@
#include "Logging.h"
// TODO: Find somewhere to put this that doesn't conflict with xbox::
extern void EmuUpdateActiveTextureStages();
extern void CxbxUpdateHostTextures();
const char *NV2AMethodToString(DWORD dwMethod); // forward
static void DbgDumpMesh(WORD *pIndexData, DWORD dwCount);
// Determine the size (in number of floating point texture coordinates) of the texture format (indexed 0 .. 3).
// This is the reverse of the D3DFVF_TEXCOORDSIZE[0..3] macros.
int DxbxFVF_GetNumberOfTextureCoordinates(DWORD dwFVF, int aTextureIndex)
{
// See D3DFVF_TEXCOORDSIZE1()
switch ((dwFVF >> ((aTextureIndex * 2) + 16)) & 3) {
case D3DFVF_TEXTUREFORMAT1: return 1; // One floating point value
case D3DFVF_TEXTUREFORMAT2: return 2; // Two floating point values
case D3DFVF_TEXTUREFORMAT3: return 3; // Three floating point values
case D3DFVF_TEXTUREFORMAT4: return 4; // Four floating point values
default:
//assert(false || "DxbxFVF_GetNumberOfTextureCoordinates : Unhandled case");
return 0;
}
}
// Dxbx Note: This code appeared in EmuExecutePushBufferRaw and occured
// in EmuFlushIVB too, so it's generalize in this single implementation.
UINT DxbxFVFToVertexSizeInBytes(DWORD dwFVF, BOOL bIncludeTextures)
{
/*
X_D3DFVF_POSITION_MASK = $00E; // Dec /2 #fl
X_D3DFVF_XYZ = $002; // 2 > 1 > 3
X_D3DFVF_XYZRHW = $004; // 4 > 2 > 4
X_D3DFVF_XYZB1 = $006; // 6 > 3 > 4
X_D3DFVF_XYZB2 = $008; // 8 > 4 > 5
X_D3DFVF_XYZB3 = $00a; // 10 > 5 > 6
X_D3DFVF_XYZB4 = $00c; // 12 > 6 > 7
*/
// Divide the D3DFVF by two, this gives almost the number of floats needed for the format :
UINT Result = (dwFVF & D3DFVF_POSITION_MASK) >> 1;
if (Result >= (D3DFVF_XYZB1 >> 1)) {
// Any format from D3DFVF_XYZB1 and above need 1 extra float :
Result++;
}
else {
// The other formats (XYZ and XYZRHW) need 2 extra floats :
Result += 2;
}
// Express the size in bytes, instead of floats :
Result *= sizeof(FLOAT);
// D3DFVF_NORMAL cannot be combined with D3DFVF_XYZRHW :
if ((dwFVF & D3DFVF_POSITION_MASK) != D3DFVF_XYZRHW) {
if (dwFVF & D3DFVF_NORMAL) {
Result += sizeof(FLOAT) * 3;
}
}
if (dwFVF & D3DFVF_DIFFUSE) {
Result += sizeof(D3DCOLOR);
}
if (dwFVF & D3DFVF_SPECULAR) {
Result += sizeof(D3DCOLOR);
}
if (bIncludeTextures) {
int NrTextures = ((dwFVF & D3DFVF_TEXCOUNT_MASK) >> D3DFVF_TEXCOUNT_SHIFT);
while (NrTextures > 0) {
NrTextures--;
Result += DxbxFVF_GetNumberOfTextureCoordinates(dwFVF, NrTextures) * sizeof(FLOAT);
}
}
return Result;
}
void EmuExecutePushBuffer
(
xbox::X_D3DPushBuffer *pPushBuffer,
@ -162,11 +92,11 @@ void EmuExecutePushBuffer
return;
}
DWORD CxbxGetStrideFromVertexShaderHandle(DWORD dwVertexShader)
DWORD CxbxGetStrideFromVertexDeclaration(CxbxVertexDeclaration* pCxbxVertexDeclaration)
{
DWORD Stride = 0;
if (VshHandleIsVertexShader(dwVertexShader)) {
if (pCxbxVertexDeclaration) {
// Test-case : Crash 'n' Burn [45530014]
// Test-case : CrimsonSea [4B4F0002]
// Test-case : Freedom Fighters
@ -178,24 +108,16 @@ DWORD CxbxGetStrideFromVertexShaderHandle(DWORD dwVertexShader)
// Test-case : SpyHunter 2 [4D57001B]
//LOG_TEST_CASE("Non-FVF Vertex Shaders not yet (completely) supported for PushBuffer emulation!");
CxbxVertexShader *pCxbxVertexShader = GetCxbxVertexShader(dwVertexShader);
if (pCxbxVertexShader) {
if (pCxbxVertexShader->Declaration.NumberOfVertexStreams == 1) {
if (pCxbxVertexDeclaration->NumberOfVertexStreams == 1) {
// Note : This assumes that the only stream in use will be stream zero :
Stride = pCxbxVertexShader->Declaration.VertexStreams[0].HostVertexStride;
Stride = pCxbxVertexDeclaration->VertexStreams[0].HostVertexStride;
}
else {
LOG_TEST_CASE("Non-FVF Vertex Shaders with multiple streams not supported for PushBuffer emulation!");
}
}
}
else {
if (VshHandleIsFVF(dwVertexShader)) {
Stride = DxbxFVFToVertexSizeInBytes(dwVertexShader, /*bIncludeTextures=*/true);
}
else {
LOG_TEST_CASE("Invalid Vertex Shader not supported for PushBuffer emulation!");
}
LOG_TEST_CASE("Missing Vertex Declaration not supported for PushBuffer emulation!");
}
return Stride;
@ -233,7 +155,7 @@ void HLE_draw_inline_array(NV2AState *d)
}
// render vertices
else {
DWORD dwVertexStride = CxbxGetStrideFromVertexShaderHandle(g_Xbox_VertexShader_Handle);
DWORD dwVertexStride = CxbxGetStrideFromVertexDeclaration(CxbxGetVertexDeclaration());
if (dwVertexStride > 0) {
UINT VertexCount = (pg->inline_array_length * sizeof(DWORD)) / dwVertexStride;
CxbxDrawContext DrawContext = {};
@ -252,7 +174,6 @@ void HLE_draw_inline_elements(NV2AState *d)
{
PGRAPHState *pg = &d->pgraph;
if (IsValidCurrentShader()) {
unsigned int uiIndexCount = pg->inline_elements_length;
CxbxDrawContext DrawContext = {};
@ -262,7 +183,6 @@ void HLE_draw_inline_elements(NV2AState *d)
CxbxDrawIndexed(DrawContext);
}
}
DWORD ABGR_to_ARGB(const uint32_t color)
{
@ -357,32 +277,38 @@ uint32_t HLE_read_NV2A_pgraph_register(const int reg)
return pg->regs[reg];
}
void HLE_write_NV2A_vertex_attribute_slot(unsigned slot, uint32_t parameter)
{
// Write value to LLE NV2A device
pgraph_handle_method(g_NV2A->GetDeviceState(),
/*subchannel=*/0,
/*method=*/NV097_SET_VERTEX_DATA4UB + (4 * slot),
parameter);
}
uint32_t HLE_read_NV2A_vertex_attribute_slot(unsigned slot)
float *HLE_get_NV2A_vertex_attribute_value_pointer(unsigned slot)
{
NV2AState* dev = g_NV2A->GetDeviceState();
PGRAPHState *pg = &(dev->pgraph);
// See CASE_16(NV097_SET_VERTEX_DATA4UB, 4) in LLE pgraph_handle_method()
VertexAttribute *vertex_attribute = &pg->vertex_attributes[slot];
// Inverse of D3DDevice_SetVertexDataColor
uint8_t a = uint8_t(vertex_attribute->inline_value[0] * 255.0f);
uint8_t b = uint8_t(vertex_attribute->inline_value[1] * 255.0f);
uint8_t c = uint8_t(vertex_attribute->inline_value[2] * 255.0f);
uint8_t d = uint8_t(vertex_attribute->inline_value[3] * 255.0f);
uint32_t value = a + (b << 8) + (c << 16) + (d << 24);
return vertex_attribute->inline_value;
}
uint32_t HLE_read_NV2A_vertex_program_slot(unsigned program_load, unsigned slot)
{
NV2AState* dev = g_NV2A->GetDeviceState();
PGRAPHState* pg = &(dev->pgraph);
// See CASE_32(NV097_SET_TRANSFORM_PROGRAM, 4) in LLE pgraph_handle_method()
assert(program_load < NV2A_MAX_TRANSFORM_PROGRAM_LENGTH);
uint32_t value = pg->program_data[program_load][slot % 4];
return value;
}
float *HLE_get_NV2A_vertex_constant_float4_ptr(unsigned const_index)
{
NV2AState* dev = g_NV2A->GetDeviceState();
PGRAPHState* pg = &(dev->pgraph);
// See CASE_32(NV097_SET_TRANSFORM_CONSTANT, 4) in LLE pgraph_handle_method()
assert(const_index < NV2A_VERTEXSHADER_CONSTANTS);
return (float*)&(pg->vsh_constants[const_index][0]);
}
// For now, skip the cache, but handle the pgraph method directly
// Note : Here's where the method gets multiplied by four!
// Note 2 : d is read from local scope, and ni is unused (same in LLE)
@ -439,8 +365,6 @@ extern void EmuExecutePushBufferRaw
uint32_t uSizeInBytes
)
{
HLE_init_pgraph_plugins(); // TODO : Move to more approriate spot
// Test-case : Azurik (see https://github.com/Cxbx-Reloaded/Cxbx-Reloaded/issues/360)
// Test-case : Crash 'n' Burn [45530014]
// Test-case : CrimsonSea [4B4F0002]

View File

@ -27,9 +27,6 @@
#include "core/hle/D3D8/XbVertexBuffer.h" // for CxbxDrawContext
extern int DxbxFVF_GetNumberOfTextureCoordinates(DWORD dwFVF, int aTextureIndex);
extern UINT DxbxFVFToVertexSizeInBytes(DWORD dwFVF, BOOL bIncludeTextures);
extern void CxbxDrawIndexed(CxbxDrawContext &DrawContext);
extern void CxbxDrawPrimitiveUP(CxbxDrawContext &DrawContext);

View File

@ -34,7 +34,7 @@
#include "core\hle\D3D8\Direct3D9\Direct3D9.h" // For g_pD3DDevice
#include "core\hle\D3D8\Direct3D9\WalkIndexBuffer.h" // for WalkIndexBuffer
#include "core\hle\D3D8\ResourceTracker.h"
#include "core\hle\D3D8\XbPushBuffer.h" // for DxbxFVF_GetNumberOfTextureCoordinates
#include "core\hle\D3D8\XbPushBuffer.h" // For CxbxDrawPrimitiveUP
#include "core\hle\D3D8\XbVertexBuffer.h"
#include "core\hle\D3D8\XbConvert.h"
@ -45,17 +45,16 @@
#define MAX_STREAM_NOT_USED_TIME (2 * CLOCKS_PER_SEC) // TODO: Trim the not used time
// Inline vertex buffer emulation
extern xbox::X_D3DPRIMITIVETYPE g_InlineVertexBuffer_PrimitiveType = xbox::X_D3DPT_INVALID;
extern DWORD g_InlineVertexBuffer_FVF = 0;
std::vector<_D3DIVB> g_InlineVertexBuffer_Table;
extern UINT g_InlineVertexBuffer_TableLength = 0;
extern UINT g_InlineVertexBuffer_TableOffset = 0;
xbox::X_D3DPRIMITIVETYPE g_InlineVertexBuffer_PrimitiveType = xbox::X_D3DPT_INVALID;
uint32_t g_InlineVertexBuffer_WrittenRegisters = 0; // A bitmask, indicating which registers have been set in g_InlineVertexBuffer_Table
xbox::X_VERTEXATTRIBUTEFORMAT g_InlineVertexBuffer_AttributeFormat = {};
bool g_InlineVertexBuffer_DeclarationOverride = false;
std::vector<D3DIVB> g_InlineVertexBuffer_Table;
UINT g_InlineVertexBuffer_TableLength = 0;
UINT g_InlineVertexBuffer_TableOffset = 0;
FLOAT *g_InlineVertexBuffer_pData = nullptr;
UINT g_InlineVertexBuffer_DataSize = 0;
extern DWORD g_dwPrimPerFrame = 0;
// Copy of active Xbox D3D Vertex Streams (and strides), set by [D3DDevice|CxbxImpl]_SetStreamSource*
xbox::X_STREAMINPUT g_Xbox_SetStreamSource[X_VSH_MAX_STREAMS] = { 0 }; // Note : .Offset member is never set (so always 0)
@ -63,29 +62,12 @@ extern xbox::X_D3DSurface* g_pXbox_RenderTarget;
extern xbox::X_D3DSurface* g_pXbox_BackBufferSurface;
extern xbox::X_D3DMULTISAMPLE_TYPE g_Xbox_MultiSampleType;
extern float *HLE_get_NV2A_vertex_attribute_value_pointer(unsigned VertexSlot); // Declared in PushBuffer.cpp
void *GetDataFromXboxResource(xbox::X_D3DResource *pXboxResource);
bool GetHostRenderTargetDimensions(DWORD* pHostWidth, DWORD* pHostHeight, IDirect3DSurface* pHostRenderTarget = nullptr);
uint32_t GetPixelContainerWidth(xbox::X_D3DPixelContainer* pPixelContainer);
uint32_t GetPixelContainerHeight(xbox::X_D3DPixelContainer* pPixelContainer);
void ApplyXboxMultiSampleOffsetAndScale(float& x, float& y);
_D3DIVB::_D3DIVB()
{
Position.x = 0.0f;
Position.y = 0.0f;
Position.z = 0.0f;
Rhw = 0.0f;
std::fill(std::begin(Blend), std::end(Blend), 0.0f);
Normal.x = 0.0f;
Normal.y = 0.0f;
Normal.z = 0.0f;
Diffuse = 0u;
Specular = 0u;
Fog = 0.0f;
BackDiffuse = 0u;
BackSpecular = 0u;
std::fill(std::begin(TexCoord), std::end(TexCoord), D3DXVECTOR4{ 0.0f , 0.0f, 0.0f, 0.0f });
}
struct _D3DIVB &_D3DIVB::operator=(const struct _D3DIVB &Val)
{
@ -100,14 +82,16 @@ struct _D3DIVB &_D3DIVB::operator=(const struct _D3DIVB &Val)
Diffuse = Val.Diffuse;
Specular = Val.Specular;
Fog = Val.Fog;
PointSize = Val.PointSize;
BackDiffuse = Val.BackDiffuse;
BackSpecular = Val.BackSpecular;
std::copy(std::begin(Val.TexCoord), std::end(Val.TexCoord), std::begin(TexCoord));
std::copy(std::begin(Val.Reg13Up), std::end(Val.Reg13Up), std::begin(Reg13Up));
return *this;
}
void CxbxPatchedStream::Activate(CxbxDrawContext *pDrawContext, UINT uiStream) const
void CxbxPatchedStream::Activate(CxbxDrawContext *pDrawContext, UINT HostStreamNumber) const
{
//LOG_INIT // Allows use of DEBUG_D3DRESULT
@ -119,7 +103,7 @@ void CxbxPatchedStream::Activate(CxbxDrawContext *pDrawContext, UINT uiStream) c
}
else {
HRESULT hRet = g_pD3DDevice->SetStreamSource(
uiStream,
HostStreamNumber,
pCachedHostVertexBuffer,
0, // OffsetInBytes
uiCachedHostVertexStride);
@ -136,7 +120,7 @@ CxbxPatchedStream::CxbxPatchedStream()
isValid = false;
}
CxbxPatchedStream::~CxbxPatchedStream()
void CxbxPatchedStream::Clear()
{
if (bCachedHostVertexStreamZeroDataIsAllocated) {
free(pCachedHostVertexStreamZeroData);
@ -151,26 +135,30 @@ CxbxPatchedStream::~CxbxPatchedStream()
}
}
CxbxPatchedStream::~CxbxPatchedStream()
{
Clear();
}
CxbxVertexBufferConverter::CxbxVertexBufferConverter()
{
m_uiNbrStreams = 0;
m_pCxbxVertexDeclaration = nullptr;
}
// TODO: CountActiveD3DStreams must be removed once we can rely on CxbxGetVertexDeclaration always being set
int CountActiveD3DStreams()
{
int lastStreamIndex = 0;
for (int i = 0; i < X_VSH_MAX_STREAMS; i++) {
if (g_Xbox_SetStreamSource[i].VertexBuffer != xbox::zeroptr) {
lastStreamIndex = i + 1;
int StreamCount = 0;
for (int XboxStreamNumber = 0; XboxStreamNumber < X_VSH_MAX_STREAMS; XboxStreamNumber++) {
if (GetXboxVertexStreamInput(XboxStreamNumber).VertexBuffer != xbox::zeroptr) {
StreamCount++;
}
}
return lastStreamIndex;
return StreamCount;
}
CxbxVertexDeclaration *GetCxbxVertexDeclaration(DWORD XboxVertexShaderHandle); // forward
UINT CxbxVertexBufferConverter::GetNbrStreams(CxbxDrawContext *pDrawContext)
{
// Draw..Up always have one stream
@ -178,20 +166,12 @@ UINT CxbxVertexBufferConverter::GetNbrStreams(CxbxDrawContext *pDrawContext)
return 1;
}
if(VshHandleIsVertexShader(g_Xbox_VertexShader_Handle)) {
CxbxVertexDeclaration *pDecl = GetCxbxVertexDeclaration(g_Xbox_VertexShader_Handle);
CxbxVertexDeclaration *pDecl = CxbxGetVertexDeclaration();
if (pDecl) {
if (pDecl->NumberOfVertexStreams <= X_VSH_MAX_STREAMS) {
return pDecl->NumberOfVertexStreams;
}
// If we reached here, pDecl was set,but with invalid data
LOG_TEST_CASE("NumberOfVertexStreams > 16");
}
return CountActiveD3DStreams();
}
// TODO: This code and CountActiveD3DStreams must be removed once we can rely on CxbxGetVertexDeclaration always being set
if (g_Xbox_VertexShader_Handle) {
return CountActiveD3DStreams();
}
@ -261,60 +241,24 @@ void CxbxVertexBufferConverter::ConvertStream
)
{
extern D3DCAPS g_D3DCaps;
bool bVshHandleIsFVF = VshHandleIsFVF(g_Xbox_VertexShader_Handle);
DWORD XboxFVF = bVshHandleIsFVF ? g_Xbox_VertexShader_Handle : 0;
// Texture normalization can only be set for FVF shaders
bool bNeedTextureNormalization = false;
struct { int NrTexCoords; bool bTexIsLinear; int Width; int Height; int Depth; } pActivePixelContainer[xbox::X_D3DTS_STAGECOUNT] = { 0 };
if (bVshHandleIsFVF) {
DWORD dwTexN = (XboxFVF & D3DFVF_TEXCOUNT_MASK) >> D3DFVF_TEXCOUNT_SHIFT;
if (dwTexN > xbox::X_D3DTS_STAGECOUNT) {
LOG_TEST_CASE("FVF,dwTexN > X_D3DTS_STAGECOUNT");
}
// Check for active linear textures.
//X_D3DBaseTexture *pLinearBaseTexture[xbox::X_D3DTS_STAGECOUNT];
for (unsigned int i = 0; i < xbox::X_D3DTS_STAGECOUNT; i++) {
// Only normalize coordinates used by the FVF shader :
if (i + 1 <= dwTexN) {
pActivePixelContainer[i].NrTexCoords = DxbxFVF_GetNumberOfTextureCoordinates(XboxFVF, i);
// TODO : Use GetXboxBaseTexture()
xbox::X_D3DBaseTexture *pXboxBaseTexture = g_pXbox_SetTexture[i];
if (pXboxBaseTexture != xbox::zeroptr) {
extern xbox::X_D3DFORMAT GetXboxPixelContainerFormat(const xbox::X_D3DPixelContainer *pXboxPixelContainer); // TODO : Move to XTL-independent header file
xbox::X_D3DFORMAT XboxFormat = GetXboxPixelContainerFormat(pXboxBaseTexture);
if (EmuXBFormatIsLinear(XboxFormat)) {
// This is often hit by the help screen in XDK samples.
bNeedTextureNormalization = true;
// Remember linearity, width and height :
pActivePixelContainer[i].bTexIsLinear = true;
// TODO : Use DecodeD3DSize or GetPixelContainerWidth + GetPixelContainerHeight
pActivePixelContainer[i].Width = (pXboxBaseTexture->Size & X_D3DSIZE_WIDTH_MASK) + 1;
pActivePixelContainer[i].Height = ((pXboxBaseTexture->Size & X_D3DSIZE_HEIGHT_MASK) >> X_D3DSIZE_HEIGHT_SHIFT) + 1;
// TODO : Support 3D textures
}
}
}
}
}
CxbxVertexShaderStreamInfo *pVertexShaderStreamInfo = nullptr;
UINT XboxStreamNumber = uiStream;
if (m_pCxbxVertexDeclaration != nullptr) {
if (uiStream > m_pCxbxVertexDeclaration->NumberOfVertexStreams + 1) {
if (uiStream > m_pCxbxVertexDeclaration->NumberOfVertexStreams) {
LOG_TEST_CASE("uiStream > NumberOfVertexStreams");
return;
}
pVertexShaderStreamInfo = &(m_pCxbxVertexDeclaration->VertexStreams[uiStream]);
XboxStreamNumber = pVertexShaderStreamInfo->XboxStreamIndex;
}
bool bNeedVertexPatching = (pVertexShaderStreamInfo != nullptr && pVertexShaderStreamInfo->NeedPatch);
bool bNeedRHWReset = bVshHandleIsFVF && ((XboxFVF & D3DFVF_POSITION_MASK) == D3DFVF_XYZRHW);
bool bNeedStreamCopy = bNeedTextureNormalization || bNeedVertexPatching || bNeedRHWReset;
bool bNeedStreamCopy = bNeedVertexPatching;
UINT HostStreamNumber = XboxStreamNumber; // Use Xbox stream index on host
uint8_t *pXboxVertexData = xbox::zeroptr;
UINT uiXboxVertexStride = 0;
UINT uiVertexCount = 0;
@ -325,7 +269,7 @@ void CxbxVertexBufferConverter::ConvertStream
if (pDrawContext->pXboxVertexStreamZeroData != xbox::zeroptr) {
// There should only be one stream (stream zero) in this case
if (uiStream != 0) {
if (XboxStreamNumber != 0) {
CxbxKrnlCleanup("Trying to patch a Draw..UP with more than stream zero!");
}
@ -335,23 +279,24 @@ void CxbxVertexBufferConverter::ConvertStream
uiHostVertexStride = (bNeedVertexPatching) ? pVertexShaderStreamInfo->HostVertexStride : uiXboxVertexStride;
dwHostVertexDataSize = uiVertexCount * uiHostVertexStride;
} else {
xbox::X_D3DVertexBuffer *pXboxVertexBuffer = g_Xbox_SetStreamSource[uiStream].VertexBuffer;
xbox::X_STREAMINPUT& XboxStreamInput = GetXboxVertexStreamInput(XboxStreamNumber);
xbox::X_D3DVertexBuffer *pXboxVertexBuffer = XboxStreamInput.VertexBuffer;
pXboxVertexData = (uint8_t*)GetDataFromXboxResource(pXboxVertexBuffer);
if (pXboxVertexData == xbox::zeroptr) {
HRESULT hRet = g_pD3DDevice->SetStreamSource(
uiStream,
HostStreamNumber,
nullptr,
0, // OffsetInBytes
0);
// DEBUG_D3DRESULT(hRet, "g_pD3DDevice->SetStreamSource");
if (FAILED(hRet)) {
EmuLog(LOG_LEVEL::WARNING, "g_pD3DDevice->SetStreamSource(uiStream, nullptr, 0)");
EmuLog(LOG_LEVEL::WARNING, "g_pD3DDevice->SetStreamSource(HostStreamNumber, nullptr, 0)");
}
return;
}
uiXboxVertexStride = g_Xbox_SetStreamSource[uiStream].Stride;
uiXboxVertexStride = XboxStreamInput.Stride;
// Set a new (exact) vertex count
uiVertexCount = pDrawContext->VerticesInBuffer;
// Dxbx note : Don't overwrite pDrawContext.dwVertexCount with uiVertexCount, because an indexed draw
@ -376,7 +321,7 @@ void CxbxVertexBufferConverter::ConvertStream
stream.uiCachedHostVertexStride = uiHostVertexStride;
stream.bCacheIsStreamZeroDrawUP = true;
stream.pCachedHostVertexStreamZeroData = pHostVertexData;
stream.Activate(pDrawContext, uiStream);
stream.Activate(pDrawContext, HostStreamNumber);
return;
}
@ -400,26 +345,17 @@ void CxbxVertexBufferConverter::ConvertStream
patchedStream.uiCachedXboxVertexStride == uiXboxVertexStride && // Make sure the Xbox Stride didn't change
patchedStream.uiCachedXboxVertexDataSize == xboxVertexDataSize ) { // Make sure the Xbox Data Size also didn't change
m_TotalCacheHits++;
patchedStream.Activate(pDrawContext, uiStream);
patchedStream.Activate(pDrawContext, HostStreamNumber);
return;
}
m_TotalCacheMisses++;
// If execution reaches here, the cached vertex buffer was not valid and we must reconvert the data
if (patchedStream.isValid) {
pHostVertexData = (uint8_t*)patchedStream.pCachedHostVertexStreamZeroData;
pNewHostVertexBuffer = patchedStream.pCachedHostVertexBuffer;
// Free the existing buffers
if (pHostVertexData != nullptr) {
free(pHostVertexData);
pHostVertexData = nullptr;
} else if (pNewHostVertexBuffer != nullptr) {
pNewHostVertexBuffer->Release();
pNewHostVertexBuffer = nullptr;
}
}
patchedStream.Clear();
assert(pHostVertexData == nullptr);
assert(pNewHostVertexBuffer == nullptr);
// If dwHostVertexDataSize is zero, the allocation/creation will fail
// This can be caused by a stride of 0, and 'other' invalid configurations
@ -478,9 +414,7 @@ void CxbxVertexBufferConverter::ConvertStream
// Make it SHORT2N
pHostVertexAsShort[0] = pXboxVertexAsShort[0];
pHostVertexAsShort[1] = 0;
}
else
{
} else {
// Make it FLOAT1
pHostVertexAsFloat[0] = NormShortToFloat(pXboxVertexAsShort[0]);
//pHostVertexAsFloat[1] = 0.0f; // Would be needed for FLOAT2
@ -496,9 +430,7 @@ void CxbxVertexBufferConverter::ConvertStream
// Make it SHORT2N
pHostVertexAsShort[0] = pXboxVertexAsShort[0];
pHostVertexAsShort[1] = pXboxVertexAsShort[1];
}
else
{
} else {
// Make it FLOAT2
pHostVertexAsFloat[0] = NormShortToFloat(pXboxVertexAsShort[0]);
pHostVertexAsFloat[1] = NormShortToFloat(pXboxVertexAsShort[1]);
@ -513,9 +445,7 @@ void CxbxVertexBufferConverter::ConvertStream
pHostVertexAsShort[1] = pXboxVertexAsShort[1];
pHostVertexAsShort[2] = pXboxVertexAsShort[2];
pHostVertexAsShort[3] = 32767; // TODO : verify
}
else
{
} else {
// Make it FLOAT3
pHostVertexAsFloat[0] = NormShortToFloat(pXboxVertexAsShort[0]);
pHostVertexAsFloat[1] = NormShortToFloat(pXboxVertexAsShort[1]);
@ -534,9 +464,7 @@ void CxbxVertexBufferConverter::ConvertStream
pHostVertexAsShort[1] = pXboxVertexAsShort[1];
pHostVertexAsShort[2] = pXboxVertexAsShort[2];
pHostVertexAsShort[3] = pXboxVertexAsShort[3];
}
else
{
} else {
// Make it FLOAT4
pHostVertexAsFloat[0] = NormShortToFloat(pXboxVertexAsShort[0]);
pHostVertexAsFloat[1] = NormShortToFloat(pXboxVertexAsShort[1]);
@ -586,9 +514,7 @@ void CxbxVertexBufferConverter::ConvertStream
pHostVertexAsByte[1] = 0;
pHostVertexAsByte[2] = 0;
pHostVertexAsByte[3] = 255; // TODO : Verify
}
else
{
} else {
// Make it FLOAT1
pHostVertexAsFloat[0] = ByteToFloat(pXboxVertexAsByte[0]);
}
@ -601,9 +527,7 @@ void CxbxVertexBufferConverter::ConvertStream
pHostVertexAsByte[1] = pXboxVertexAsByte[1];
pHostVertexAsByte[2] = 0;
pHostVertexAsByte[3] = 255; // TODO : Verify
}
else
{
} else {
// Make it FLOAT2
pHostVertexAsFloat[0] = ByteToFloat(pXboxVertexAsByte[0]);
pHostVertexAsFloat[1] = ByteToFloat(pXboxVertexAsByte[1]);
@ -618,9 +542,7 @@ void CxbxVertexBufferConverter::ConvertStream
pHostVertexAsByte[1] = pXboxVertexAsByte[1];
pHostVertexAsByte[2] = pXboxVertexAsByte[2];
pHostVertexAsByte[3] = 255; // TODO : Verify
}
else
{
} else {
// Make it FLOAT3
pHostVertexAsFloat[0] = ByteToFloat(pXboxVertexAsByte[0]);
pHostVertexAsFloat[1] = ByteToFloat(pXboxVertexAsByte[1]);
@ -639,9 +561,7 @@ void CxbxVertexBufferConverter::ConvertStream
pHostVertexAsByte[1] = pXboxVertexAsByte[1];
pHostVertexAsByte[2] = pXboxVertexAsByte[2];
pHostVertexAsByte[3] = pXboxVertexAsByte[3];
}
else
{
} else {
// Make it FLOAT4
pHostVertexAsFloat[0] = ByteToFloat(pXboxVertexAsByte[0]);
pHostVertexAsFloat[1] = ByteToFloat(pXboxVertexAsByte[1]);
@ -685,105 +605,6 @@ void CxbxVertexBufferConverter::ConvertStream
}
}
// Xbox FVF shaders are identical to host Direct3D 8.1, however
// texture coordinates may need normalization if used with linear textures.
if (bNeedTextureNormalization || bNeedRHWReset) {
// assert(bVshHandleIsFVF);
UINT uiTextureCoordinatesByteOffsetInVertex = 0;
// Locate texture coordinate offset in vertex structure.
if (bNeedTextureNormalization) {
uiTextureCoordinatesByteOffsetInVertex = DxbxFVFToVertexSizeInBytes(XboxFVF, /*bIncludeTextures=*/false);
if (bNeedVertexPatching) {
LOG_TEST_CASE("Potential xbox vs host texture-offset difference! (bNeedVertexPatching within bNeedTextureNormalization)");
}
// As long as vertices aren't resized / patched up until the texture coordinates,
// the uiTextureCoordinatesByteOffsetInVertex on host will match Xbox
}
// If for some reason the Xbox Render Target is not set, fallback to the backbuffer
if (g_pXbox_RenderTarget == xbox::zeroptr) {
LOG_TEST_CASE("SetRenderTarget fallback to backbuffer");
g_pXbox_RenderTarget = g_pXbox_BackBufferSurface;
}
DWORD HostRenderTarget_Width, HostRenderTarget_Height;
DWORD XboxRenderTarget_Width = GetPixelContainerWidth(g_pXbox_RenderTarget);
DWORD XboxRenderTarget_Height = GetPixelContainerHeight(g_pXbox_RenderTarget);
if (!GetHostRenderTargetDimensions(&HostRenderTarget_Width, &HostRenderTarget_Height)) {
HostRenderTarget_Width = XboxRenderTarget_Width;
HostRenderTarget_Height = XboxRenderTarget_Height;
}
bool bNeedRHWTransform = (g_Xbox_MultiSampleType > xbox::X_D3DMULTISAMPLE_NONE) || (XboxRenderTarget_Width < HostRenderTarget_Width && XboxRenderTarget_Height < HostRenderTarget_Height);
for (uint32_t uiVertex = 0; uiVertex < uiVertexCount; uiVertex++) {
FLOAT *pVertexDataAsFloat = (FLOAT*)(&pHostVertexData[uiVertex * uiHostVertexStride]);
// Handle pre-transformed vertices (which bypass the vertex shader pipeline)
if (bNeedRHWReset) {
// We need to transform these vertices only if the host render target was upscaled from the Xbox render target
// Transforming always breaks render to non-upscaled textures: Only surfaces are upscaled, intentionally so
if (bNeedRHWTransform) {
pVertexDataAsFloat[0] *= g_RenderScaleFactor;
pVertexDataAsFloat[1] *= g_RenderScaleFactor;
ApplyXboxMultiSampleOffsetAndScale(pVertexDataAsFloat[0], pVertexDataAsFloat[1]);
}
#if 0
// Check Z. TODO : Why reset Z from 0.0 to 1.0 ? (Maybe fog-related?)
if (pVertexDataAsFloat[2] == 0.0f) {
// LOG_TEST_CASE("D3DFVF_XYZRHW (Z)"); // Test-case : Many XDK Samples (AlphaFog, PointSprites)
pVertexDataAsFloat[2] = 1.0f;
}
#endif
#if 1
// Check RHW. TODO : Why reset from 0.0 to 1.0 ? (Maybe 1.0 indicates that the vertices are not to be transformed)
if (pVertexDataAsFloat[3] == 0.0f) {
// LOG_TEST_CASE("D3DFVF_XYZRHW (RHW)"); // Test-case : Many XDK Samples (AlphaFog, PointSprites)
pVertexDataAsFloat[3] = 1.0f;
}
#endif
}
// Normalize texture coordinates in FVF stream if needed
if (uiTextureCoordinatesByteOffsetInVertex > 0) { // implies bNeedTextureNormalization (using one is more efficient than both)
FLOAT *pVertexUVData = (FLOAT*)((uintptr_t)pVertexDataAsFloat + uiTextureCoordinatesByteOffsetInVertex);
for (unsigned int i = 0; i < xbox::X_D3DTS_STAGECOUNT; i++) {
if (pActivePixelContainer[i].bTexIsLinear) {
switch (pActivePixelContainer[i].NrTexCoords) {
case 0:
LOG_TEST_CASE("Normalize 0D?");
break;
case 1:
LOG_TEST_CASE("Normalize 1D");
pVertexUVData[0] /= pActivePixelContainer[i].Width;
break;
case 2:
pVertexUVData[0] /= pActivePixelContainer[i].Width;
pVertexUVData[1] /= pActivePixelContainer[i].Height;
break;
case 3:
LOG_TEST_CASE("Normalize 3D");
// Test case : HeatShimmer
pVertexUVData[0] /= pActivePixelContainer[i].Width;
pVertexUVData[1] /= pActivePixelContainer[i].Height;
pVertexUVData[2] /= pActivePixelContainer[i].Depth;
break;
default:
LOG_TEST_CASE("Normalize ?D");
break;
}
}
pVertexUVData += pActivePixelContainer[i].NrTexCoords;
}
}
}
}
patchedStream.isValid = true;
patchedStream.XboxPrimitiveType = pDrawContext->XboxPrimitiveType;
patchedStream.pCachedXboxVertexData = pXboxVertexData;
@ -802,7 +623,7 @@ void CxbxVertexBufferConverter::ConvertStream
patchedStream.pCachedHostVertexBuffer = pNewHostVertexBuffer;
}
patchedStream.Activate(pDrawContext, uiStream);
patchedStream.Activate(pDrawContext, HostStreamNumber);
}
void CxbxVertexBufferConverter::Apply(CxbxDrawContext *pDrawContext)
@ -810,10 +631,7 @@ void CxbxVertexBufferConverter::Apply(CxbxDrawContext *pDrawContext)
if ((pDrawContext->XboxPrimitiveType < xbox::X_D3DPT_POINTLIST) || (pDrawContext->XboxPrimitiveType > xbox::X_D3DPT_POLYGON))
CxbxKrnlCleanup("Unknown primitive type: 0x%.02X\n", pDrawContext->XboxPrimitiveType);
m_pCxbxVertexDeclaration = nullptr;
if (VshHandleIsVertexShader(g_Xbox_VertexShader_Handle)) {
m_pCxbxVertexDeclaration = &(GetCxbxVertexShader(g_Xbox_VertexShader_Handle)->Declaration);
}
m_pCxbxVertexDeclaration = CxbxGetVertexDeclaration();
// If we are drawing from an offset, we know that the vertex count must have
// 'offset' vertices before the first drawn vertices
@ -841,8 +659,8 @@ void CxbxVertexBufferConverter::Apply(CxbxDrawContext *pDrawContext)
m_uiNbrStreams = X_VSH_MAX_STREAMS;
}
for(UINT uiStream = 0; uiStream < m_uiNbrStreams; uiStream++) {
ConvertStream(pDrawContext, uiStream);
for(UINT i = 0; i < m_uiNbrStreams; i++) {
ConvertStream(pDrawContext, i);
}
if (pDrawContext->XboxPrimitiveType == xbox::X_D3DPT_QUADSTRIP) {
@ -869,48 +687,128 @@ void CxbxVertexBufferConverter::Apply(CxbxDrawContext *pDrawContext)
}
}
void EmuFlushIVB()
void CxbxSetVertexAttribute(int Register, FLOAT a, FLOAT b, FLOAT c, FLOAT d)
{
CxbxUpdateNativeD3DResources();
// Parse IVB table with current FVF shader if possible.
bool bFVF = VshHandleIsFVF(g_Xbox_VertexShader_Handle);
DWORD dwCurFVF = (bFVF) ? g_Xbox_VertexShader_Handle : g_InlineVertexBuffer_FVF;
EmuLog(LOG_LEVEL::DEBUG, "g_InlineVertexBuffer_TableOffset := %d", g_InlineVertexBuffer_TableOffset);
// Check the given FVF
switch (dwCurFVF & D3DFVF_POSITION_MASK) {
case 0: // No position ?
if (bFVF) {
EmuLog(LOG_LEVEL::WARNING, "EmuFlushIVB(): g_Xbox_VertexShader_Handle isn't a valid FVF - using D3DFVF_XYZRHW instead!");
dwCurFVF |= D3DFVF_XYZRHW;
if (Register < 0) {
LOG_TEST_CASE("Register < 0");
return;
}
else {
EmuLog(LOG_LEVEL::WARNING, "EmuFlushIVB(): using g_InlineVertexBuffer_FVF instead of current FVF!");
dwCurFVF = g_InlineVertexBuffer_FVF;
}
break;
case D3DFVF_XYZRHW:
// D3DFVF_NORMAL isn't allowed in combination with D3DFVF_XYZRHW
if (dwCurFVF & D3DFVF_NORMAL) {
EmuLog(LOG_LEVEL::WARNING, "EmuFlushIVB(): Normal encountered while D3DFVF_XYZRHW is given - switching back to D3DFVF_XYZ!");
dwCurFVF &= ~D3DFVF_POSITION_MASK;
dwCurFVF |= D3DFVF_XYZ;
}
break;
if (Register >= 16) {
LOG_TEST_CASE("Register >= 16");
return;
}
DWORD dwPos = dwCurFVF & D3DFVF_POSITION_MASK;
DWORD dwTexN = (dwCurFVF & D3DFVF_TEXCOUNT_MASK) >> D3DFVF_TEXCOUNT_SHIFT;
size_t TexSize[xbox::X_D3DTS_STAGECOUNT]; // Xbox supports up to 4 textures
// Write these values to the NV2A registers, so that we read them back when needed
float* attribute_floats = HLE_get_NV2A_vertex_attribute_value_pointer(Register);
attribute_floats[0] = a;
attribute_floats[1] = b;
attribute_floats[2] = c;
attribute_floats[3] = d;
for (unsigned int i = 0; i < dwTexN; i++) {
TexSize[i] = DxbxFVF_GetNumberOfTextureCoordinates(dwCurFVF, i);
// Also, write the given register value to a matching host vertex shader constant
// This allows us to implement Xbox functionality where SetVertexData4f can be used to specify attributes
// not present in the vertex declaration.
// We use range 193 and up to store these values, as Xbox shaders stop at c192!
g_pD3DDevice->SetVertexShaderConstantF(CXBX_D3DVS_CONSTREG_VREGDEFAULTS_BASE + Register, attribute_floats, 1);
}
DWORD Float4ToDWORD(float* floats)
{
// Inverse of D3DDevice_SetVertexDataColor
uint8_t a = uint8_t(floats[0] * 255.0f);
uint8_t b = uint8_t(floats[1] * 255.0f);
uint8_t c = uint8_t(floats[2] * 255.0f);
uint8_t d = uint8_t(floats[3] * 255.0f);
uint32_t value = a + (b << 8) + (c << 16) + (d << 24);
return value;
}
void CxbxImpl_Begin(xbox::X_D3DPRIMITIVETYPE PrimitiveType)
{
g_InlineVertexBuffer_PrimitiveType = PrimitiveType;
g_InlineVertexBuffer_TableOffset = 0;
g_InlineVertexBuffer_WrittenRegisters = 0;
}
void CxbxImpl_End()
{
using namespace xbox;
#ifndef FIELD_OFFSET
#define FIELD_OFFSET(type, field) ((LONG)(LONG_PTR)&(((type *)0)->field))
#endif
static const LONG OffsetPerRegister[X_VSH_MAX_ATTRIBUTES] = {
/*X_D3DVSDE_POSITION = 0:*/FIELD_OFFSET(D3DIVB, Position),
/*X_D3DVSDE_BLENDWEIGHT = 1:*/FIELD_OFFSET(D3DIVB, Blend),
/*X_D3DVSDE_NORMAL = 2:*/FIELD_OFFSET(D3DIVB, Normal),
/*X_D3DVSDE_DIFFUSE = 3:*/FIELD_OFFSET(D3DIVB, Diffuse),
/*X_D3DVSDE_SPECULAR = 4:*/FIELD_OFFSET(D3DIVB, Specular),
/*X_D3DVSDE_FOG = 5:*/FIELD_OFFSET(D3DIVB, Fog),
/*X_D3DVSDE_POINTSIZE = 6:*/FIELD_OFFSET(D3DIVB, PointSize),
/*X_D3DVSDE_BACKDIFFUSE = 7:*/FIELD_OFFSET(D3DIVB, BackDiffuse),
/*X_D3DVSDE_BACKSPECULAR = 8:*/FIELD_OFFSET(D3DIVB, BackSpecular),
/*X_D3DVSDE_TEXCOORD0 = 9:*/FIELD_OFFSET(D3DIVB, TexCoord[0]),
/*X_D3DVSDE_TEXCOORD1 = 10:*/FIELD_OFFSET(D3DIVB, TexCoord[1]),
/*X_D3DVSDE_TEXCOORD2 = 11:*/FIELD_OFFSET(D3DIVB, TexCoord[2]),
/*X_D3DVSDE_TEXCOORD3 = 12:*/FIELD_OFFSET(D3DIVB, TexCoord[3]),
/* 13:*/FIELD_OFFSET(D3DIVB, Reg13Up[0]),
/* 14:*/FIELD_OFFSET(D3DIVB, Reg13Up[1]),
/* 15:*/FIELD_OFFSET(D3DIVB, Reg13Up[2])
};
static const LONG FormatPerRegister[X_VSH_MAX_ATTRIBUTES] = {
/*X_D3DVSDE_POSITION = 0:*/X_D3DVSDT_FLOAT4,
/*X_D3DVSDE_BLENDWEIGHT = 1:*/X_D3DVSDT_FLOAT4,
/*X_D3DVSDE_NORMAL = 2:*/X_D3DVSDT_FLOAT3,
/*X_D3DVSDE_DIFFUSE = 3:*/X_D3DVSDT_D3DCOLOR,
/*X_D3DVSDE_SPECULAR = 4:*/X_D3DVSDT_D3DCOLOR,
/*X_D3DVSDE_FOG = 5:*/X_D3DVSDT_FLOAT1,
/*X_D3DVSDE_POINTSIZE = 6:*/X_D3DVSDT_FLOAT1,
/*X_D3DVSDE_BACKDIFFUSE = 7:*/X_D3DVSDT_D3DCOLOR,
/*X_D3DVSDE_BACKSPECULAR = 8:*/X_D3DVSDT_D3DCOLOR,
/*X_D3DVSDE_TEXCOORD0 = 9:*/X_D3DVSDT_FLOAT4,
/*X_D3DVSDE_TEXCOORD1 = 10:*/X_D3DVSDT_FLOAT4,
/*X_D3DVSDE_TEXCOORD2 = 11:*/X_D3DVSDT_FLOAT4,
/*X_D3DVSDE_TEXCOORD3 = 12:*/X_D3DVSDT_FLOAT4,
/* 13:*/X_D3DVSDT_FLOAT4,
/* 14:*/X_D3DVSDT_FLOAT4,
/* 15:*/X_D3DVSDT_FLOAT4
};
static const DWORD SizePerRegister[X_VSH_MAX_ATTRIBUTES] = {
/*X_D3DVSDE_POSITION = 0:*/sizeof(float) * 4,
/*X_D3DVSDE_BLENDWEIGHT = 1:*/sizeof(float) * 4,
/*X_D3DVSDE_NORMAL = 2:*/sizeof(float) * 3,
/*X_D3DVSDE_DIFFUSE = 3:*/sizeof(DWORD),
/*X_D3DVSDE_SPECULAR = 4:*/sizeof(DWORD),
/*X_D3DVSDE_FOG = 5:*/sizeof(float) * 1,
/*X_D3DVSDE_POINTSIZE = 6:*/sizeof(float) * 1,
/*X_D3DVSDE_BACKDIFFUSE = 7:*/sizeof(DWORD),
/*X_D3DVSDE_BACKSPECULAR = 8:*/sizeof(DWORD),
/*X_D3DVSDE_TEXCOORD0 = 9:*/sizeof(float) * 4,
/*X_D3DVSDE_TEXCOORD1 = 10:*/sizeof(float) * 4,
/*X_D3DVSDE_TEXCOORD2 = 11:*/sizeof(float) * 4,
/*X_D3DVSDE_TEXCOORD3 = 12:*/sizeof(float) * 4,
/* 13:*/sizeof(float) * 4,
/* 14:*/sizeof(float) * 4,
/* 15:*/sizeof(float) * 4
};
if (g_InlineVertexBuffer_TableOffset <= 0) {
return;
}
// Compose an Xbox vertex attribute format according to the registers that have been written to :
UINT uiStride = 0;
g_InlineVertexBuffer_AttributeFormat = {};
for (int reg = 0; reg < X_VSH_MAX_ATTRIBUTES; reg++) {
if (g_InlineVertexBuffer_WrittenRegisters & (1 << reg)) {
g_InlineVertexBuffer_AttributeFormat.Slots[reg].Format = FormatPerRegister[reg];
g_InlineVertexBuffer_AttributeFormat.Slots[reg].Offset = uiStride;
uiStride += SizePerRegister[reg];
} else {
g_InlineVertexBuffer_AttributeFormat.Slots[reg].Format = X_D3DVSDT_NONE;
}
}
// Use a tooling function to determine the vertex stride :
UINT uiStride = DxbxFVFToVertexSizeInBytes(dwCurFVF, /*bIncludeTextures=*/true);
// Make sure the output buffer is big enough
UINT NeededSize = g_InlineVertexBuffer_TableOffset * uiStride;
if (g_InlineVertexBuffer_DataSize < NeededSize) {
@ -922,101 +820,36 @@ void EmuFlushIVB()
g_InlineVertexBuffer_pData = (FLOAT*)malloc(g_InlineVertexBuffer_DataSize);
}
FLOAT *pVertexBufferData = g_InlineVertexBuffer_pData;
// For each register that ever got written, copy the data over from
// g_InlineVertexBuffer_Table to pVertexBufferData, but adjacent
// to eachother. This, so that host accepts this as a vertex declaration.
// Note, that if we figure out how to draw using vertex buffers that
// contain gaps, the following copy is no longer needed, and we could
// draw straight from g_InlineVertexBuffer_Table instead!
uint8_t* pVertexBufferData = (uint8_t*)g_InlineVertexBuffer_pData;
for (unsigned int v = 0; v < g_InlineVertexBuffer_TableOffset; v++) {
*pVertexBufferData++ = g_InlineVertexBuffer_Table[v].Position.x;
*pVertexBufferData++ = g_InlineVertexBuffer_Table[v].Position.y;
*pVertexBufferData++ = g_InlineVertexBuffer_Table[v].Position.z;
if (dwPos == D3DFVF_XYZRHW) {
*pVertexBufferData++ = g_InlineVertexBuffer_Table[v].Rhw;
EmuLog(LOG_LEVEL::DEBUG, "IVB Position := {%f, %f, %f, %f}", g_InlineVertexBuffer_Table[v].Position.x, g_InlineVertexBuffer_Table[v].Position.y, g_InlineVertexBuffer_Table[v].Position.z, g_InlineVertexBuffer_Table[v].Rhw);
}
else { // XYZRHW cannot be combined with NORMAL, but the other XYZ formats can :
switch (dwPos) {
case D3DFVF_XYZ:
EmuLog(LOG_LEVEL::DEBUG, "IVB Position := {%f, %f, %f}", g_InlineVertexBuffer_Table[v].Position.x, g_InlineVertexBuffer_Table[v].Position.y, g_InlineVertexBuffer_Table[v].Position.z);
break;
case D3DFVF_XYZB1:
*pVertexBufferData++ = g_InlineVertexBuffer_Table[v].Blend[0];
EmuLog(LOG_LEVEL::DEBUG, "IVB Position := {%f, %f, %f, %f}", g_InlineVertexBuffer_Table[v].Position.x, g_InlineVertexBuffer_Table[v].Position.y, g_InlineVertexBuffer_Table[v].Position.z, g_InlineVertexBuffer_Table[v].Blend[0]);
break;
case D3DFVF_XYZB2:
*pVertexBufferData++ = g_InlineVertexBuffer_Table[v].Blend[0];
*pVertexBufferData++ = g_InlineVertexBuffer_Table[v].Blend[1];
EmuLog(LOG_LEVEL::DEBUG, "IVB Position := {%f, %f, %f, %f, %f}", g_InlineVertexBuffer_Table[v].Position.x, g_InlineVertexBuffer_Table[v].Position.y, g_InlineVertexBuffer_Table[v].Position.z, g_InlineVertexBuffer_Table[v].Blend[0], g_InlineVertexBuffer_Table[v].Blend[1]);
break;
case D3DFVF_XYZB3:
*pVertexBufferData++ = g_InlineVertexBuffer_Table[v].Blend[0];
*pVertexBufferData++ = g_InlineVertexBuffer_Table[v].Blend[1];
*pVertexBufferData++ = g_InlineVertexBuffer_Table[v].Blend[2];
EmuLog(LOG_LEVEL::DEBUG, "IVB Position := {%f, %f, %f, %f, %f, %f}", g_InlineVertexBuffer_Table[v].Position.x, g_InlineVertexBuffer_Table[v].Position.y, g_InlineVertexBuffer_Table[v].Position.z, g_InlineVertexBuffer_Table[v].Blend[0], g_InlineVertexBuffer_Table[v].Blend[1], g_InlineVertexBuffer_Table[v].Blend[2]);
break;
case D3DFVF_XYZB4:
*pVertexBufferData++ = g_InlineVertexBuffer_Table[v].Blend[0];
*pVertexBufferData++ = g_InlineVertexBuffer_Table[v].Blend[1];
*pVertexBufferData++ = g_InlineVertexBuffer_Table[v].Blend[2];
*pVertexBufferData++ = g_InlineVertexBuffer_Table[v].Blend[3];
EmuLog(LOG_LEVEL::DEBUG, "IVB Position := {%f, %f, %f, %f, %f, %f, %f}", g_InlineVertexBuffer_Table[v].Position.x, g_InlineVertexBuffer_Table[v].Position.y, g_InlineVertexBuffer_Table[v].Position.z, g_InlineVertexBuffer_Table[v].Blend[0], g_InlineVertexBuffer_Table[v].Blend[1], g_InlineVertexBuffer_Table[v].Blend[2], g_InlineVertexBuffer_Table[v].Blend[3]);
break;
default:
CxbxKrnlCleanup("Unsupported Position Mask (FVF := 0x%.08X dwPos := 0x%.08X)", dwCurFVF, dwPos);
break;
}
if (dwCurFVF & D3DFVF_NORMAL) {
*pVertexBufferData++ = g_InlineVertexBuffer_Table[v].Normal.x;
*pVertexBufferData++ = g_InlineVertexBuffer_Table[v].Normal.y;
*pVertexBufferData++ = g_InlineVertexBuffer_Table[v].Normal.z;
EmuLog(LOG_LEVEL::DEBUG, "IVB Normal := {%f, %f, %f}", g_InlineVertexBuffer_Table[v].Normal.x, g_InlineVertexBuffer_Table[v].Normal.y, g_InlineVertexBuffer_Table[v].Normal.z);
}
}
#if 0 // TODO : Was this supported on Xbox from some point in time (pun intended)?
if (dwCurFVF & D3DFVF_PSIZE) {
*(DWORD*)pVertexBufferData++ = g_InlineVertexBuffer_Table[v].PointSize;
EmuLog(LOG_LEVEL::DEBUG, "IVB PointSize := 0x%.08X", g_InlineVertexBuffer_Table[v].PointSize);
}
#endif
if (dwCurFVF & D3DFVF_DIFFUSE) {
*(DWORD*)pVertexBufferData++ = g_InlineVertexBuffer_Table[v].Diffuse;
EmuLog(LOG_LEVEL::DEBUG, "IVB Diffuse := 0x%.08X", g_InlineVertexBuffer_Table[v].Diffuse);
}
if (dwCurFVF & D3DFVF_SPECULAR) {
*(DWORD*)pVertexBufferData++ = g_InlineVertexBuffer_Table[v].Specular;
EmuLog(LOG_LEVEL::DEBUG, "IVB Specular := 0x%.08X", g_InlineVertexBuffer_Table[v].Specular);
}
for (unsigned int i = 0; i < dwTexN; i++) {
*pVertexBufferData++ = g_InlineVertexBuffer_Table[v].TexCoord[i].x;
if (TexSize[i] >= 2) {
*pVertexBufferData++ = g_InlineVertexBuffer_Table[v].TexCoord[i].y;
if (TexSize[i] >= 3) {
*pVertexBufferData++ = g_InlineVertexBuffer_Table[v].TexCoord[i].z;
if (TexSize[i] >= 4) {
*pVertexBufferData++ = g_InlineVertexBuffer_Table[v].TexCoord[i].w;
auto vertex_ptr = (uint8_t*)&(g_InlineVertexBuffer_Table[v]);
for (unsigned int reg = 0; reg < X_VSH_MAX_ATTRIBUTES; reg++) {
if (g_InlineVertexBuffer_WrittenRegisters & (1 << reg)) {
auto source = vertex_ptr + OffsetPerRegister[reg];
auto size = SizePerRegister[reg];
// Note, that g_InlineVertexBuffer_AttributeFormat is declared with
// data types (in g_InlineVertexBuffer_Table ) that are host-compatible,
// so we can do a straight copy here, there's no conversion needed :
memcpy(pVertexBufferData, source, size);
pVertexBufferData += size;
}
}
}
if (g_bPrintfOn) {
switch (TexSize[i]) {
case 1: EmuLog(LOG_LEVEL::DEBUG, "IVB TexCoord%d := {%f}", i + 1, g_InlineVertexBuffer_Table[v].TexCoord[i].x); break;
case 2: EmuLog(LOG_LEVEL::DEBUG, "IVB TexCoord%d := {%f, %f}", i + 1, g_InlineVertexBuffer_Table[v].TexCoord[i].x, g_InlineVertexBuffer_Table[v].TexCoord[i].y); break;
case 3: EmuLog(LOG_LEVEL::DEBUG, "IVB TexCoord%d := {%f, %f, %f}", i + 1, g_InlineVertexBuffer_Table[v].TexCoord[i].x, g_InlineVertexBuffer_Table[v].TexCoord[i].y, g_InlineVertexBuffer_Table[v].TexCoord[i].z); break;
case 4: EmuLog(LOG_LEVEL::DEBUG, "IVB TexCoord%d := {%f, %f, %f, %f}", i + 1, g_InlineVertexBuffer_Table[v].TexCoord[i].x, g_InlineVertexBuffer_Table[v].TexCoord[i].y, g_InlineVertexBuffer_Table[v].TexCoord[i].z, g_InlineVertexBuffer_Table[v].TexCoord[i].w); break;
}
}
}
// Arrange for g_InlineVertexBuffer_AttributeFormat to be returned in CxbxGetVertexDeclaration,
// so that our above composed declaration will be used for the next draw :
g_InlineVertexBuffer_DeclarationOverride = true;
// Note, that g_Xbox_VertexShader_IsFixedFunction should be left untouched,
// because except for the declaration override, the Xbox shader (either FVF
// or a program, or even passthrough shaders) should still be in effect!
if (v == 0) {
unsigned int VertexBufferUsage = (uintptr_t)pVertexBufferData - (uintptr_t)g_InlineVertexBuffer_pData;
if (VertexBufferUsage != uiStride) {
CxbxKrnlCleanup("EmuFlushIVB uses wrong stride!");
}
}
}
CxbxUpdateNativeD3DResources();
CxbxDrawContext DrawContext = {};
@ -1025,20 +858,178 @@ void EmuFlushIVB()
DrawContext.pXboxVertexStreamZeroData = g_InlineVertexBuffer_pData;
DrawContext.uiXboxVertexStreamZeroStride = uiStride;
HRESULT hRet;
if (bFVF) {
g_pD3DDevice->SetVertexShader(nullptr);
hRet = g_pD3DDevice->SetFVF(dwCurFVF);
//DEBUG_D3DRESULT(hRet, "g_pD3DDevice->SetVertexShader");
}
CxbxDrawPrimitiveUP(DrawContext);
if (bFVF) {
hRet = g_pD3DDevice->SetFVF(g_Xbox_VertexShader_Handle);
//DEBUG_D3DRESULT(hRet, "g_pD3DDevice->SetVertexShader");
// Now that we've drawn, stop our override in CxbxGetVertexDeclaration :
g_InlineVertexBuffer_DeclarationOverride = false;
// TODO: Should technically clean this up at some point..but on XP doesnt matter much
// g_VMManager.Deallocate((VAddr)g_InlineVertexBuffer_pData);
// g_VMManager.Deallocate((VAddr)g_InlineVertexBuffer_Table);
}
void CxbxImpl_SetVertexData4f(int Register, FLOAT a, FLOAT b, FLOAT c, FLOAT d)
{
using namespace xbox;
HRESULT hRet = D3D_OK;
// Always update our attribute storage with the most recently set register value
CxbxSetVertexAttribute(Register, a, b, c, d);
// Grow g_InlineVertexBuffer_Table to contain at least current, and a potentially next vertex
if (g_InlineVertexBuffer_TableLength <= g_InlineVertexBuffer_TableOffset + 1) {
UINT InlineVertexBuffer_TableLength_Original = g_InlineVertexBuffer_TableLength;
if (g_InlineVertexBuffer_TableLength == 0) {
g_InlineVertexBuffer_TableLength = PAGE_SIZE / sizeof(D3DIVB);
} else {
g_InlineVertexBuffer_TableLength *= 2;
}
for (unsigned i = 0; i < (g_InlineVertexBuffer_TableLength - InlineVertexBuffer_TableLength_Original); ++i) {
g_InlineVertexBuffer_Table.emplace_back();
}
EmuLog(LOG_LEVEL::DEBUG, "Expanded g_InlineVertexBuffer_Table to %u entries", g_InlineVertexBuffer_TableLength);
// Sanity check: ensure that g_InlineVertexBuffer_Table is not growing indefinetly. This can happen if D3DDevice_Begin and D3DDevice_End
// are not patched, since they both reset g_InlineVertexBuffer_TableOffset back to zero, thus preventing further growth
if (g_InlineVertexBuffer_TableLength > 50000) {
LOG_TEST_CASE("g_InlineVertexBuffer_TableLength > 50000! This probably means that g_InlineVertexBuffer_Table is growing indefinitely.");
}
}
// Is this the initial call after D3DDevice_Begin() ?
if (g_InlineVertexBuffer_WrittenRegisters == 0) {
// Read starting values for all inline vertex attributes from HLE NV2A pgraph (converting them to required types) :
g_InlineVertexBuffer_Table[0].Position = D3DXVECTOR3(HLE_get_NV2A_vertex_attribute_value_pointer(X_D3DVSDE_POSITION));
g_InlineVertexBuffer_Table[0].Rhw = HLE_get_NV2A_vertex_attribute_value_pointer(X_D3DVSDE_POSITION)[3];
g_InlineVertexBuffer_Table[0].Blend[0] = HLE_get_NV2A_vertex_attribute_value_pointer(X_D3DVSDE_BLENDWEIGHT)[0];
g_InlineVertexBuffer_Table[0].Blend[1] = HLE_get_NV2A_vertex_attribute_value_pointer(X_D3DVSDE_BLENDWEIGHT)[1];
g_InlineVertexBuffer_Table[0].Blend[2] = HLE_get_NV2A_vertex_attribute_value_pointer(X_D3DVSDE_BLENDWEIGHT)[2];
g_InlineVertexBuffer_Table[0].Blend[3] = HLE_get_NV2A_vertex_attribute_value_pointer(X_D3DVSDE_BLENDWEIGHT)[3];
g_InlineVertexBuffer_Table[0].Normal = D3DXVECTOR3(HLE_get_NV2A_vertex_attribute_value_pointer(X_D3DVSDE_NORMAL));
g_InlineVertexBuffer_Table[0].Diffuse = Float4ToDWORD(HLE_get_NV2A_vertex_attribute_value_pointer(X_D3DVSDE_DIFFUSE));
g_InlineVertexBuffer_Table[0].Specular = Float4ToDWORD(HLE_get_NV2A_vertex_attribute_value_pointer(X_D3DVSDE_SPECULAR));
g_InlineVertexBuffer_Table[0].Fog = HLE_get_NV2A_vertex_attribute_value_pointer(X_D3DVSDE_FOG)[0];
g_InlineVertexBuffer_Table[0].PointSize = HLE_get_NV2A_vertex_attribute_value_pointer(X_D3DVSDE_POINTSIZE)[0];
g_InlineVertexBuffer_Table[0].BackDiffuse = Float4ToDWORD(HLE_get_NV2A_vertex_attribute_value_pointer(X_D3DVSDE_BACKDIFFUSE));
g_InlineVertexBuffer_Table[0].BackSpecular = Float4ToDWORD(HLE_get_NV2A_vertex_attribute_value_pointer(X_D3DVSDE_BACKSPECULAR));
g_InlineVertexBuffer_Table[0].TexCoord[0] = D3DXVECTOR4(HLE_get_NV2A_vertex_attribute_value_pointer(X_D3DVSDE_TEXCOORD0));
g_InlineVertexBuffer_Table[0].TexCoord[1] = D3DXVECTOR4(HLE_get_NV2A_vertex_attribute_value_pointer(X_D3DVSDE_TEXCOORD1));
g_InlineVertexBuffer_Table[0].TexCoord[2] = D3DXVECTOR4(HLE_get_NV2A_vertex_attribute_value_pointer(X_D3DVSDE_TEXCOORD2));
g_InlineVertexBuffer_Table[0].TexCoord[3] = D3DXVECTOR4(HLE_get_NV2A_vertex_attribute_value_pointer(X_D3DVSDE_TEXCOORD3));
g_InlineVertexBuffer_Table[0].Reg13Up[0] = D3DXVECTOR4(HLE_get_NV2A_vertex_attribute_value_pointer(13));
g_InlineVertexBuffer_Table[0].Reg13Up[1] = D3DXVECTOR4(HLE_get_NV2A_vertex_attribute_value_pointer(14));
g_InlineVertexBuffer_Table[0].Reg13Up[2] = D3DXVECTOR4(HLE_get_NV2A_vertex_attribute_value_pointer(15));
// Note : Because all members are assigned an initial value, there's no need for a clearing constructor for _D3DIVB!
}
if (Register == X_D3DVSDE_VERTEX)
g_InlineVertexBuffer_WrittenRegisters |= (1 << X_D3DVSDE_POSITION);
else if (Register < 16)
g_InlineVertexBuffer_WrittenRegisters |= (1 << Register);
unsigned o = g_InlineVertexBuffer_TableOffset;
switch (Register)
{
case X_D3DVSDE_VERTEX:
case X_D3DVSDE_POSITION:
{
// Note : Setting position signals completion of a vertex
g_InlineVertexBuffer_Table[o].Position.x = a;
g_InlineVertexBuffer_Table[o].Position.y = b;
g_InlineVertexBuffer_Table[o].Position.z = c;
g_InlineVertexBuffer_Table[o].Rhw = d;
// Start a new vertex
g_InlineVertexBuffer_TableOffset++;
// Copy all attributes of the prior vertex to the new one, to simulate persistent attribute values
g_InlineVertexBuffer_Table[g_InlineVertexBuffer_TableOffset] = g_InlineVertexBuffer_Table[o];
break;
}
case X_D3DVSDE_BLENDWEIGHT:
{
g_InlineVertexBuffer_Table[o].Blend[0] = a;
g_InlineVertexBuffer_Table[o].Blend[1] = b;
g_InlineVertexBuffer_Table[o].Blend[2] = c;
g_InlineVertexBuffer_Table[o].Blend[3] = d;
break;
}
case X_D3DVSDE_NORMAL:
{
g_InlineVertexBuffer_Table[o].Normal.x = a;
g_InlineVertexBuffer_Table[o].Normal.y = b;
g_InlineVertexBuffer_Table[o].Normal.z = c;
break;
}
case X_D3DVSDE_DIFFUSE:
{
g_InlineVertexBuffer_Table[o].Diffuse = D3DCOLOR_COLORVALUE(a, b, c, d);
break;
}
case X_D3DVSDE_SPECULAR:
{
g_InlineVertexBuffer_Table[o].Specular = D3DCOLOR_COLORVALUE(a, b, c, d);
break;
}
case X_D3DVSDE_FOG: // Xbox extension
{
g_InlineVertexBuffer_Table[o].Fog = a; // TODO : What about the other (b, c and d) arguments?
break;
}
case X_D3DVSDE_POINTSIZE:
{
g_InlineVertexBuffer_Table[o].PointSize = a; // TODO : What about the other (b, c and d) arguments?
break;
}
case X_D3DVSDE_BACKDIFFUSE: // Xbox extension
{
g_InlineVertexBuffer_Table[o].BackDiffuse = D3DCOLOR_COLORVALUE(a, b, c, d);
break;
}
case X_D3DVSDE_BACKSPECULAR: // Xbox extension
{
g_InlineVertexBuffer_Table[o].BackSpecular = D3DCOLOR_COLORVALUE(a, b, c, d);
break;
}
case X_D3DVSDE_TEXCOORD0:
case X_D3DVSDE_TEXCOORD1:
case X_D3DVSDE_TEXCOORD2:
case X_D3DVSDE_TEXCOORD3:
{
unsigned i = Register - X_D3DVSDE_TEXCOORD0;
g_InlineVertexBuffer_Table[o].TexCoord[i].x = a;
g_InlineVertexBuffer_Table[o].TexCoord[i].y = b;
g_InlineVertexBuffer_Table[o].TexCoord[i].z = c;
g_InlineVertexBuffer_Table[o].TexCoord[i].w = d;
break;
}
case 13:
case 14:
case 15:
{
unsigned i = Register - 13;
g_InlineVertexBuffer_Table[o].Reg13Up[i].x = a;
g_InlineVertexBuffer_Table[o].Reg13Up[i].y = b;
g_InlineVertexBuffer_Table[o].Reg13Up[i].z = c;
g_InlineVertexBuffer_Table[o].Reg13Up[i].w = d;
break;
}
default:
EmuLog(LOG_LEVEL::WARNING, "Unknown IVB Register : %d", Register);
}
g_InlineVertexBuffer_TableOffset = 0; // Might not be needed (also cleared in D3DDevice_Begin)
}
void CxbxImpl_SetStreamSource(UINT StreamNumber, xbox::X_D3DVertexBuffer* pStreamData, UINT Stride)

View File

@ -56,7 +56,8 @@ class CxbxPatchedStream
public:
CxbxPatchedStream();
~CxbxPatchedStream();
void Activate(CxbxDrawContext *pDrawContext, UINT uiStream) const;
void Clear();
void Activate(CxbxDrawContext *pDrawContext, UINT HostStreamNumber) const;
bool isValid = false;
xbox::X_D3DPRIMITIVETYPE XboxPrimitiveType = xbox::X_D3DPT_NONE;
PVOID pCachedXboxVertexData = xbox::zeroptr;
@ -99,11 +100,10 @@ class CxbxVertexBufferConverter
void ConvertStream(CxbxDrawContext *pPatchDesc, UINT uiStream);
};
// inline vertex buffer emulation
// Inline vertex buffer emulation
extern xbox::X_D3DPRIMITIVETYPE g_InlineVertexBuffer_PrimitiveType;
extern DWORD g_InlineVertexBuffer_FVF;
struct _D3DIVB
typedef struct _D3DIVB
{
D3DXVECTOR3 Position; // X_D3DVSDE_POSITION (*) > D3DFVF_XYZ / D3DFVF_XYZRHW
FLOAT Rhw; // X_D3DVSDE_VERTEX (*) > D3DFVF_XYZ / D3DFVF_XYZRHW
@ -112,26 +112,32 @@ struct _D3DIVB
D3DCOLOR Diffuse; // X_D3DVSDE_DIFFUSE > D3DFVF_DIFFUSE
D3DCOLOR Specular; // X_D3DVSDE_SPECULAR > D3DFVF_SPECULAR
FLOAT Fog; // X_D3DVSDE_FOG > D3DFVF_FOG unavailable; TODO : How to handle?
FLOAT PointSize; // X_D3DVSDE_POINTSIZE > D3DFVF_POINTSIZE unavailable; TODO : How to handle?
D3DCOLOR BackDiffuse; // X_D3DVSDE_BACKDIFFUSE > D3DFVF_BACKDIFFUSE unavailable; TODO : How to handle?
D3DCOLOR BackSpecular; // X_D3DVSDE_BACKSPECULAR > D3DFVF_BACKSPECULAR unavailable; TODO : How to handle?
D3DXVECTOR4 TexCoord[4]; // X_D3DVSDE_TEXCOORD0 > D3DFVF_TEX1 (and 4 more up to D3DFVF_TEX4)
D3DXVECTOR4 TexCoord[4]; // X_D3DVSDE_TEXCOORD0 > D3DFVF_TEX1, (and 3 more up to D3DFVF_TEX4)
D3DXVECTOR4 Reg13Up[3];
// (*) X_D3DVSDE_POSITION and X_D3DVSDE_VERTEX both set Position, but Rhw seems optional,
// hence, selection for D3DFVF_XYZ or D3DFVF_XYZRHW is rather fuzzy. We DO know that once
// D3DFVF_NORMAL is given, D3DFVF_XYZRHW is forbidden (see D3DDevice_SetVertexData4f)
_D3DIVB();
struct _D3DIVB &operator=(const struct _D3DIVB &Val);
};
extern std::vector<_D3DIVB> g_InlineVertexBuffer_Table;
} D3DIVB;
extern std::vector<D3DIVB> g_InlineVertexBuffer_Table;
extern UINT g_InlineVertexBuffer_TableLength;
extern UINT g_InlineVertexBuffer_TableOffset;
extern void EmuFlushIVB();
extern void EmuUpdateActiveTexture();
extern void CxbxSetVertexAttribute(int Register, FLOAT a, FLOAT b, FLOAT c, FLOAT d);
extern void CxbxImpl_Begin(xbox::X_D3DPRIMITIVETYPE PrimitiveType);
extern void CxbxImpl_End();
extern void CxbxImpl_SetStreamSource(UINT StreamNumber, xbox::X_D3DVertexBuffer* pStreamData, UINT Stride);
extern void CxbxImpl_SetVertexData4f(int Register, FLOAT a, FLOAT b, FLOAT c, FLOAT d);
extern DWORD g_dwPrimPerFrame;
#endif

File diff suppressed because it is too large Load Diff

View File

@ -40,9 +40,10 @@
typedef struct _CxbxVertexShaderStreamElement
{
UINT XboxType; // The stream element data types (xbox)
UINT XboxByteSize; // The stream element data sizes (xbox)
UINT HostByteSize; // The stream element data sizes (pc)
UINT XboxType; // The stream element data type (xbox)
UINT XboxByteSize; // The stream element data size (xbox)
BYTE HostDataType; // The stream element data type (pc)
UINT HostByteSize; // The stream element data size (pc)
}
CxbxVertexShaderStreamElement;
@ -60,53 +61,25 @@ CxbxVertexShaderStreamElement;
typedef struct _CxbxVertexShaderStreamInfo
{
BOOL NeedPatch; // This is to know whether it's data which must be patched
BOOL DeclPosition;
WORD HostVertexStride;
DWORD NumberOfVertexElements; // Number of the stream data types
WORD CurrentStreamNumber;
CxbxVertexShaderStreamElement VertexElements[X_VSH_MAX_ATTRIBUTES + 16]; // TODO : Why 16 extra host additions?)
WORD XboxStreamIndex;
CxbxVertexShaderStreamElement VertexElements[X_VSH_MAX_ATTRIBUTES];
}
CxbxVertexShaderStreamInfo;
typedef uint64_t VertexDeclarationKey;
typedef struct _CxbxVertexDeclaration
{
CxbxVertexShaderStreamInfo VertexStreams[X_VSH_MAX_STREAMS];
VertexDeclarationKey Key;
CxbxVertexShaderStreamInfo VertexStreams[X_VSH_MAX_STREAMS]; // Note : VertexStreams is indexed by a counter, NOT StreamIndex!
IDirect3DVertexDeclaration* pHostVertexDeclaration;
DWORD* pXboxDeclarationCopy;
DWORD XboxDeclarationCount; // Xbox's number of DWORD-sized X_D3DVSD* tokens
UINT NumberOfVertexStreams; // The number of streams the vertex shader uses
bool vRegisterInDeclaration[X_VSH_MAX_ATTRIBUTES];
}
CxbxVertexDeclaration;
typedef struct _CxbxVertexShader
{
// These are the parameters given by the XBE,
// we save them to be able to return them when necessary.
DWORD XboxFunctionSize;
DWORD* pXboxFunctionCopy;
UINT XboxNrAddressSlots;
DWORD XboxVertexShaderType;
// DWORD XboxStatus; // Used by VshHandleIsValidShader()
// The resulting host variables
uint64_t VertexShaderKey;
// Needed for dynamic stream patching
CxbxVertexDeclaration Declaration;
}
CxbxVertexShader;
// recompile xbox vertex shader declaration
extern D3DVERTEXELEMENT *EmuRecompileVshDeclaration
(
DWORD *pXboxDeclaration,
bool bIsFixedFunction,
DWORD *pXboxDeclarationCount,
CxbxVertexDeclaration *pCxbxVertexDeclaration
);
// Intermediate vertex shader structures
enum VSH_OREG_NAME {
@ -224,25 +197,21 @@ extern void EmuParseVshFunction
IntermediateVertexShader* pShader
);
extern void FreeVertexDynamicPatch(CxbxVertexShader *pVertexShader);
// Checks for failed vertex shaders, and shaders that would need patching
extern boolean IsValidCurrentShader(void);
inline boolean VshHandleIsVertexShader(DWORD Handle) { return (Handle & X_D3DFVF_RESERVED0) ? TRUE : FALSE; }
inline boolean VshHandleIsFVF(DWORD Handle) { return !VshHandleIsVertexShader(Handle); }
inline xbox::X_D3DVertexShader *VshHandleToXboxVertexShader(DWORD Handle) { return (xbox::X_D3DVertexShader *)(Handle & ~X_D3DFVF_RESERVED0);}
extern CxbxVertexShader* GetCxbxVertexShader(DWORD XboxVertexShaderHandle);
extern bool g_Xbox_VertexShader_IsFixedFunction;
extern CxbxVertexDeclaration* CxbxGetVertexDeclaration();
extern xbox::X_STREAMINPUT& GetXboxVertexStreamInput(unsigned XboxStreamNumber);
extern void CxbxImpl_SetScreenSpaceOffset(float x, float y);
extern void CxbxImpl_LoadVertexShaderProgram(CONST DWORD* pFunction, DWORD Address);
extern void CxbxImpl_LoadVertexShader(DWORD Handle, DWORD Address);
extern void CxbxImpl_SetVertexShader(DWORD Handle);
extern void CxbxImpl_SelectVertexShaderDirect(xbox::X_VERTEXATTRIBUTEFORMAT* pVAF, DWORD Address);
extern void CxbxImpl_SelectVertexShader(DWORD Handle, DWORD Address);
extern void CxbxImpl_SetVertexShaderInput(DWORD Handle, UINT StreamCount, xbox::X_STREAMINPUT* pStreamInputs);
extern void CxbxImpl_SetVertexShaderConstant(INT Register, PVOID pConstantData, DWORD ConstantCount);
extern HRESULT CxbxImpl_CreateVertexShader(CONST DWORD *pDeclaration, CONST DWORD *pFunction, DWORD *pHandle, DWORD Usage);
extern void CxbxImpl_DeleteVertexShader(DWORD Handle);
#endif

View File

@ -64,7 +64,7 @@ std::map<const std::string, const xbox_patch_t> g_PatchTable = {
PATCH_ENTRY("D3DDevice_BlockUntilVerticalBlank", xbox::EMUPATCH(D3DDevice_BlockUntilVerticalBlank), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_Clear", xbox::EMUPATCH(D3DDevice_Clear), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_CopyRects", xbox::EMUPATCH(D3DDevice_CopyRects), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_CreateVertexShader", xbox::EMUPATCH(D3DDevice_CreateVertexShader), PATCH_HLE_D3D),
// PATCH_ENTRY("D3DDevice_CreateVertexShader", xbox::EMUPATCH(D3DDevice_CreateVertexShader), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_DeleteVertexShader", xbox::EMUPATCH(D3DDevice_DeleteVertexShader), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_DeleteVertexShader_0", xbox::EMUPATCH(D3DDevice_DeleteVertexShader_0), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_DrawIndexedVertices", xbox::EMUPATCH(D3DDevice_DrawIndexedVertices), PATCH_HLE_D3D),
@ -95,7 +95,7 @@ std::map<const std::string, const xbox_patch_t> g_PatchTable = {
PATCH_ENTRY("D3DDevice_GetVertexShaderConstant", xbox::EMUPATCH(D3DDevice_GetVertexShaderConstant), PATCH_HLE_D3D),
//PATCH_ENTRY("D3DDevice_GetVertexShaderDeclaration", xbox::EMUPATCH(D3DDevice_GetVertexShaderDeclaration), PATCH_HLE_D3D),
//PATCH_ENTRY("D3DDevice_GetVertexShaderFunction", xbox::EMUPATCH(D3DDevice_GetVertexShaderFunction), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_GetVertexShaderInput", xbox::EMUPATCH(D3DDevice_GetVertexShaderInput), PATCH_HLE_D3D),
//PATCH_ENTRY("D3DDevice_GetVertexShaderInput", xbox::EMUPATCH(D3DDevice_GetVertexShaderInput), PATCH_HLE_D3D),
//PATCH_ENTRY("D3DDevice_GetVertexShaderSize", xbox::EMUPATCH(D3DDevice_GetVertexShaderSize), PATCH_HLE_D3D),
//PATCH_ENTRY("D3DDevice_GetVertexShaderType", xbox::EMUPATCH(D3DDevice_GetVertexShaderType), PATCH_HLE_D3D),
//PATCH_ENTRY("D3DDevice_GetViewportOffsetAndScale", xbox::EMUPATCH(D3DDevice_GetViewportOffsetAndScale), PATCH_HLE_D3D),
@ -117,7 +117,7 @@ std::map<const std::string, const xbox_patch_t> g_PatchTable = {
PATCH_ENTRY("D3DDevice_RunPushBuffer", xbox::EMUPATCH(D3DDevice_RunPushBuffer), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_RunVertexStateShader", xbox::EMUPATCH(D3DDevice_RunVertexStateShader), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SelectVertexShader", xbox::EMUPATCH(D3DDevice_SelectVertexShader), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SelectVertexShaderDirect", xbox::EMUPATCH(D3DDevice_SelectVertexShaderDirect), PATCH_HLE_D3D),
//PATCH_ENTRY("D3DDevice_SelectVertexShaderDirect", xbox::EMUPATCH(D3DDevice_SelectVertexShaderDirect), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SelectVertexShader_0", xbox::EMUPATCH(D3DDevice_SelectVertexShader_0), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SelectVertexShader_4", xbox::EMUPATCH(D3DDevice_SelectVertexShader_4), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetBackBufferScale", xbox::EMUPATCH(D3DDevice_SetBackBufferScale), PATCH_HLE_D3D),
@ -173,7 +173,7 @@ std::map<const std::string, const xbox_patch_t> g_PatchTable = {
PATCH_ENTRY("D3DDevice_SetVertexShaderConstantNotInlineFast", xbox::EMUPATCH(D3DDevice_SetVertexShaderConstantNotInlineFast), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetVertexShaderConstant_8", xbox::EMUPATCH(D3DDevice_SetVertexShaderConstant_8), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetVertexShaderInput", xbox::EMUPATCH(D3DDevice_SetVertexShaderInput), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetVertexShaderInputDirect", xbox::EMUPATCH(D3DDevice_SetVertexShaderInputDirect), PATCH_HLE_D3D),
//PATCH_ENTRY("D3DDevice_SetVertexShaderInputDirect", xbox::EMUPATCH(D3DDevice_SetVertexShaderInputDirect), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetVerticalBlankCallback", xbox::EMUPATCH(D3DDevice_SetVerticalBlankCallback), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_SetViewport", xbox::EMUPATCH(D3DDevice_SetViewport), PATCH_HLE_D3D),
PATCH_ENTRY("D3DDevice_Swap", xbox::EMUPATCH(D3DDevice_Swap), PATCH_HLE_D3D),

View File

@ -441,22 +441,78 @@ static uint64_t fnv_hash(const uint8_t *data, size_t len);
static uint64_t fast_hash(const uint8_t *data, size_t len, unsigned int samples);
/* PGRAPH - accelerated 2d/3d drawing engine */
static uint32_t pgraph_rdi_read(PGRAPHState *pg,
unsigned int select, unsigned int address)
{
uint32_t r = 0;
switch(select) {
case RDI_INDEX_VTX_CONSTANTS0:
case RDI_INDEX_VTX_CONSTANTS1:
assert((address / 4) < NV2A_VERTEXSHADER_CONSTANTS);
r = pg->vsh_constants[address / 4][3 - address % 4];
break;
default:
fprintf(stderr, "nv2a: unknown rdi read select 0x%x address 0x%x\n",
select, address);
assert(false);
break;
}
return r;
}
static void pgraph_rdi_write(PGRAPHState *pg,
unsigned int select, unsigned int address,
uint32_t val)
{
switch(select) {
case RDI_INDEX_VTX_CONSTANTS0:
case RDI_INDEX_VTX_CONSTANTS1:
assert(false); /* Untested */
assert((address / 4) < NV2A_VERTEXSHADER_CONSTANTS);
pg->vsh_constants_dirty[address / 4] |=
(val != pg->vsh_constants[address / 4][3 - address % 4]);
pg->vsh_constants[address / 4][3 - address % 4] = val;
break;
default:
NV2A_DPRINTF("unknown rdi write select 0x%x, address 0x%x, val 0x%08x\n",
select, address, val);
break;
}
}
DEVICE_READ32(PGRAPH)
{
qemu_mutex_lock(&d->pgraph.pgraph_lock);
PGRAPHState *pg = &d->pgraph;
DEVICE_READ32_SWITCH() {
case NV_PGRAPH_INTR:
result = d->pgraph.pending_interrupts;
result = pg->pending_interrupts;
break;
case NV_PGRAPH_INTR_EN:
result = d->pgraph.enabled_interrupts;
result = pg->enabled_interrupts;
break;
case NV_PGRAPH_RDI_DATA: {
unsigned int select = GET_MASK(pg->regs[NV_PGRAPH_RDI_INDEX],
NV_PGRAPH_RDI_INDEX_SELECT);
int address = GET_MASK(pg->regs[NV_PGRAPH_RDI_INDEX],
NV_PGRAPH_RDI_INDEX_ADDRESS);
result = pgraph_rdi_read(pg, select, address);
/* FIXME: Overflow into select? */
assert(address < GET_MASK(NV_PGRAPH_RDI_INDEX_ADDRESS,
NV_PGRAPH_RDI_INDEX_ADDRESS));
SET_MASK(pg->regs[NV_PGRAPH_RDI_INDEX],
NV_PGRAPH_RDI_INDEX_ADDRESS, address + 1);
break;
}
default:
DEVICE_READ32_REG(pgraph); // Was : DEBUG_READ32_UNHANDLED(PGRAPH);
}
qemu_mutex_unlock(&d->pgraph.pgraph_lock);
qemu_mutex_unlock(&pg->pgraph_lock);
// reg_log_read(NV_PGRAPH, addr, r);
@ -465,36 +521,53 @@ DEVICE_READ32(PGRAPH)
DEVICE_WRITE32(PGRAPH)
{
PGRAPHState *pg = &d->pgraph;
// reg_log_write(NV_PGRAPH, addr, val);
qemu_mutex_lock(&d->pgraph.pgraph_lock);
qemu_mutex_lock(&pg->pgraph_lock);
switch (addr) {
case NV_PGRAPH_INTR:
d->pgraph.pending_interrupts &= ~value;
qemu_cond_broadcast(&d->pgraph.interrupt_cond);
pg->pending_interrupts &= ~value;
qemu_cond_broadcast(&pg->interrupt_cond);
break;
case NV_PGRAPH_INTR_EN:
d->pgraph.enabled_interrupts = value;
pg->enabled_interrupts = value;
break;
case NV_PGRAPH_INCREMENT:
if (value & NV_PGRAPH_INCREMENT_READ_3D) {
SET_MASK(d->pgraph.regs[NV_PGRAPH_SURFACE],
SET_MASK(pg->regs[NV_PGRAPH_SURFACE],
NV_PGRAPH_SURFACE_READ_3D,
(GET_MASK(d->pgraph.regs[NV_PGRAPH_SURFACE],
(GET_MASK(pg->regs[NV_PGRAPH_SURFACE],
NV_PGRAPH_SURFACE_READ_3D) + 1)
% GET_MASK(d->pgraph.regs[NV_PGRAPH_SURFACE],
% GET_MASK(pg->regs[NV_PGRAPH_SURFACE],
NV_PGRAPH_SURFACE_MODULO_3D));
qemu_cond_broadcast(&d->pgraph.flip_3d);
qemu_cond_broadcast(&pg->flip_3d);
}
break;
case NV_PGRAPH_RDI_DATA: {
unsigned int select = GET_MASK(pg->regs[NV_PGRAPH_RDI_INDEX],
NV_PGRAPH_RDI_INDEX_SELECT);
int address = GET_MASK(pg->regs[NV_PGRAPH_RDI_INDEX],
NV_PGRAPH_RDI_INDEX_ADDRESS);
pgraph_rdi_write(pg, select, address, value);
/* FIXME: Overflow into select? */
assert(address < GET_MASK(NV_PGRAPH_RDI_INDEX_ADDRESS,
NV_PGRAPH_RDI_INDEX_ADDRESS));
SET_MASK(pg->regs[NV_PGRAPH_RDI_INDEX],
NV_PGRAPH_RDI_INDEX_ADDRESS, address + 1);
break;
}
case NV_PGRAPH_CHANNEL_CTX_TRIGGER: {
xbox::addr_xt context_address =
GET_MASK(d->pgraph.regs[NV_PGRAPH_CHANNEL_CTX_POINTER], NV_PGRAPH_CHANNEL_CTX_POINTER_INST) << 4;
GET_MASK(pg->regs[NV_PGRAPH_CHANNEL_CTX_POINTER],
NV_PGRAPH_CHANNEL_CTX_POINTER_INST) << 4;
if (value & NV_PGRAPH_CHANNEL_CTX_TRIGGER_READ_IN) {
unsigned pgraph_channel_id =
GET_MASK(d->pgraph.regs[NV_PGRAPH_CTX_USER], NV_PGRAPH_CTX_USER_CHID);
GET_MASK(pg->regs[NV_PGRAPH_CTX_USER], NV_PGRAPH_CTX_USER_CHID);
NV2A_DPRINTF("PGRAPH: read channel %d context from %" HWADDR_PRIx "\n",
pgraph_channel_id, context_address);
@ -504,7 +577,7 @@ DEVICE_WRITE32(PGRAPH)
NV2A_DPRINTF(" - CTX_USER = 0x%08X\n", context_user);
d->pgraph.regs[NV_PGRAPH_CTX_USER] = context_user;
pg->regs[NV_PGRAPH_CTX_USER] = context_user;
// pgraph_set_context_user(d, context_user);
}
if (value & NV_PGRAPH_CHANNEL_CTX_TRIGGER_WRITE_OUT) {
@ -521,11 +594,11 @@ DEVICE_WRITE32(PGRAPH)
// events
switch (addr) {
case NV_PGRAPH_FIFO:
qemu_cond_broadcast(&d->pgraph.fifo_access_cond);
qemu_cond_broadcast(&pg->fifo_access_cond);
break;
}
qemu_mutex_unlock(&d->pgraph.pgraph_lock);
qemu_mutex_unlock(&pg->pgraph_lock);
DEVICE_WRITE32_END(PGRAPH);
}
@ -1407,16 +1480,20 @@ void pgraph_handle_method(NV2AState *d,
GET_MASK(parameter, NV097_SET_SURFACE_PITCH_COLOR);
pg->surface_zeta.pitch =
GET_MASK(parameter, NV097_SET_SURFACE_PITCH_ZETA);
pg->surface_color.buffer_dirty = true;
pg->surface_zeta.buffer_dirty = true;
break;
case NV097_SET_SURFACE_COLOR_OFFSET:
pgraph_update_surface(d, false, true, true);
pg->surface_color.offset = parameter;
pg->surface_color.buffer_dirty = true;
break;
case NV097_SET_SURFACE_ZETA_OFFSET:
pgraph_update_surface(d, false, true, true);
pg->surface_zeta.offset = parameter;
pg->surface_zeta.buffer_dirty = true;
break;
CASE_8(NV097_SET_COMBINER_ALPHA_ICW, 4) :
@ -2596,17 +2673,12 @@ void pgraph_handle_method(NV2AState *d,
assert(false); /* FIXME: Untested! */
VertexAttribute *vertex_attribute = &pg->vertex_attributes[slot];
pgraph_allocate_inline_buffer_vertices(pg, slot);
/* FIXME: Is mapping to [-1,+1] correct? */
vertex_attribute->inline_value[0] = ((int16_t)(parameter & 0xFFFF) * 2.0f + 1)
/ 65535.0f;
vertex_attribute->inline_value[1] = ((int16_t)(parameter >> 16) * 2.0f + 1)
/ 65535.0f;
/* FIXME: Should these really be set to 0.0 and 1.0 ? Conditions? */
vertex_attribute->inline_value[0] = (float)(int16_t)(parameter & 0xFFFF);
vertex_attribute->inline_value[1] = (float)(int16_t)(parameter >> 16);
vertex_attribute->inline_value[2] = 0.0f;
vertex_attribute->inline_value[3] = 1.0f;
if (slot == 0) {
pgraph_finish_inline_buffer_vertex(pg);
assert(false); /* FIXME: Untested */
}
break;
}
@ -2638,7 +2710,6 @@ void pgraph_handle_method(NV2AState *d,
* 2.0f + 1) / 65535.0f;
if ((slot == 0) && (part == 1)) {
pgraph_finish_inline_buffer_vertex(pg);
assert(false); /* FIXME: Untested */
}
break;
}
@ -3364,7 +3435,15 @@ static void pgraph_bind_shaders(PGRAPHState *pg)
last_y = y;
}
for (i = 0; i < 8; i++) {
/* FIXME: We should memset(state, 0x00, sizeof(state)) instead */
memset(state.psh.rgb_inputs, 0, sizeof(state.psh.rgb_inputs));
memset(state.psh.rgb_outputs, 0, sizeof(state.psh.rgb_outputs));
memset(state.psh.alpha_inputs, 0, sizeof(state.psh.alpha_inputs));
memset(state.psh.alpha_outputs, 0, sizeof(state.psh.alpha_outputs));
/* Copy content of enabled combiner stages */
unsigned int num_stages = pg->regs[NV_PGRAPH_COMBINECTL] & 0xFF;
for (i = 0; i < num_stages; i++) {
state.psh.rgb_inputs[i] = pg->regs[NV_PGRAPH_COMBINECOLORI0 + i * 4];
state.psh.rgb_outputs[i] = pg->regs[NV_PGRAPH_COMBINECOLORO0 + i * 4];
state.psh.alpha_inputs[i] = pg->regs[NV_PGRAPH_COMBINEALPHAI0 + i * 4];
@ -3439,7 +3518,7 @@ static void pgraph_bind_shaders(PGRAPHState *pg)
pgraph_apply_anti_aliasing_factor(pg, &x_max, &y_max);
glUniform4i(pg->shader_binding->clip_region_loc[i],
x_min, y_min, x_max, y_max);
x_min, y_min, x_max + 1, y_max + 1);
}
pgraph_update_shader_constants(pg, pg->shader_binding, binding_changed,
@ -3787,10 +3866,10 @@ static void pgraph_update_surface(NV2AState *d, bool upload,
glDeleteTextures(1, &pg->gl_zeta_buffer);
pg->gl_zeta_buffer = 0;
}
}
memcpy(&pg->last_surface_shape, &pg->surface_shape,
sizeof(SurfaceShape));
}
if ((color_write || (!upload && pg->surface_color.write_enabled_cache))
&& (upload || pg->surface_color.draw_dirty)) {

View File

@ -1141,7 +1141,7 @@ void CxbxReserveNV2AMemory(NV2AState *d)
}
}
printf("[0x%.4X] INIT: Allocated %d MiB of Xbox NV2A PRAMIN memory at 0x%.8p to 0x%.8p\n",
printf("[0x%.4X] INIT: Allocated %d MiB of Xbox NV2A PRAMIN memory at 0x%.8x to 0x%.8x\n",
GetCurrentThreadId(), d->pramin.ramin_size / ONE_MB, (uintptr_t)d->pramin.ramin_ptr, (uintptr_t)d->pramin.ramin_ptr + d->pramin.ramin_size - 1);
}

View File

@ -95,7 +95,7 @@ public:
void Reset();
// State Getter: Used for HLE reading of device state
NV2AState* GetDeviceState() { return m_nv2a_state; };
inline NV2AState* GetDeviceState() { return m_nv2a_state; };
uint32_t IORead(int barIndex, uint32_t port, unsigned size);
void IOWrite(int barIndex, uint32_t port, uint32_t value, unsigned size);

View File

@ -335,6 +335,8 @@
#define NV_PGRAPH_FIFO 0x00000720
# define NV_PGRAPH_FIFO_ACCESS (1 << 0)
#define NV_PGRAPH_RDI_INDEX 0x00000750
# define NV_PGRAPH_RDI_INDEX_ADDRESS 0x00001FFC
# define NV_PGRAPH_RDI_INDEX_SELECT 0x01FF0000
#define NV_PGRAPH_RDI_DATA 0x00000754
#define NV_PGRAPH_FFINTFC_ST2 0x00000764
#define NV_PGRAPH_CHANNEL_CTX_TABLE 0x00000780
@ -691,6 +693,7 @@
#define NV_PCRTC_CONFIG 0x00000804
#define NV_PCRTC_RASTER 0x00000808
#define NV_PVIDEO_DEBUG_2 0x00000088
#define NV_PVIDEO_DEBUG_3 0x0000008C
#define NV_PVIDEO_INTR 0x00000100
@ -832,7 +835,7 @@
# define NV_PRAMDAC_PLL_TEST_COUNTER_NVPLL_LOCK (1 << 29)
# define NV_PRAMDAC_PLL_TEST_COUNTER_MPLL_LOCK (1 << 30)
# define NV_PRAMDAC_PLL_TEST_COUNTER_VPLL_LOCK (1 << 31)
#define NV_PRAMDAC_GENERAL_CONTROL 0x00680600
#define NV_PRAMDAC_GENERAL_CONTROL 0x00000600
# define NV_PRAMDAC_GENERAL_CONTROL_PIXMIX_ON (3 << 4)
# define NV_PRAMDAC_GENERAL_CONTROL_VGA_STATE_SEL (1 << 8)
# define NV_PRAMDAC_GENERAL_CONTROL_ALT_MODE_SEL (1 << 12)
@ -840,6 +843,14 @@
# define NV_PRAMDAC_GENERAL_CONTROL_BPC_8BITS (1 << 20)
# define NV_PRAMDAC_GENERAL_CONTROL_PIPE_LONG (2 << 28)
#define NV_PRAMDAC_FP_VDISPLAY_END 0x00000800
#define NV_PRAMDAC_FP_VCRTC 0x00000808
#define NV_PRAMDAC_FP_VSYNC_END 0x00000810
#define NV_PRAMDAC_FP_VVALID_END 0x00000818
#define NV_PRAMDAC_FP_HDISPLAY_END 0x00000820
#define NV_PRAMDAC_FP_HCRTC 0x00000828
#define NV_PRAMDAC_FP_HVALID_END 0x00000838
#define NV_PRMCIO_ARX 0x006013c0
#define NV_PRMCIO_AR__WRITE 0x006013c0
#define NV_PRMCIO_AR__READ 0x006013c1
@ -1795,6 +1806,14 @@
#define NV_IGRAPH_XF_LTC1_L6 0x12
#define NV_IGRAPH_XF_LTC1_L7 0x13
/* These RDI select values appear to be named by MS.
* nvidia seems to refer to RDI_INDEX_VTX_CONSTANTS0 by RDI_RAMSEL_XL_XFCTX.
* However, we don't have other nvidia names; so we use these aliases for now.
* Eventually we'll probably adopt nouveau names for these internals.
*/
#define RDI_INDEX_VTX_CONSTANTS0 0x17
#define RDI_INDEX_VTX_CONSTANTS1 0xCC
#define NV2A_VERTEX_ATTR_POSITION 0
#define NV2A_VERTEX_ATTR_WEIGHT 1