Files
Cesarus85--mjolnir-vr/libs/d3d8/block.cpp
T

719 lines
21 KiB
C++

/* January state-block reconstruction; target PDB and COFF are authoritative.
* Related RXDK block implementation: Copyright (C) 2026 Team Resurgent.
* SPDX-License-Identifier: GPL-3.0-or-later. See retained reference provenance. */
#include "block_internal.h"
#include "vshader_internal.h"
#include "memory_internal.h"
#pragma code_seg("D3D")
#pragma const_seg("D3D_RD")
extern "C"
{
BYTE D3D__StateBlockDirty[(D3DSBD_MAX + 3) & ~3];
}
namespace D3D
{
void WINAPI ClearStateBlockFlags(
void)
{
CDevice *device = g_pDevice;
ZeroMemory(D3D__StateBlockDirty, sizeof(D3D__StateBlockDirty));
Light *light = device->m_pLights;
for (DWORD i = 0; i < device->m_LightCount; ++i, ++light)
{
light->Flags &= ~(LIGHT_STATEBLOCK_SET | LIGHT_STATEBLOCK_ENABLED);
}
return;
}
extern "C" void WINAPI D3DDevice_BeginStateBlock(
void)
{
g_pDevice->m_StateFlags |= STATE_RECORDBLOCK;
ClearStateBlockFlags();
return;
}
extern "C" void WINAPI D3DDevice_Suspend(
void)
{
g_pDevice->SetStateVB(0);
return;
}
extern "C" void WINAPI D3DDevice_ApplyStateBlock(
DWORD token)
{
DWORD i;
CDevice *device = g_pDevice;
StateBlock *block = (StateBlock *)token;
DWORD *data = StateBlockData(block);
for (i = 0; i < block->D0_Textures; ++i)
{
device->SetTexture(data[0], (D3DBaseTexture *)data[1]);
data += 2;
}
if (block->D1_PixelShader)
{
device->SetPixelShader(data[0]);
++data;
}
if (block->D2_VertexShader)
{
device->SetVertexShader(data[0]);
++data;
}
if (block->D3_Index)
{
device->SetIndices((D3DIndexBuffer *)data[1], data[0]);
data += 2;
}
for (i = 0; i < block->D4_Streams; ++i)
{
device->SetStreamSource(data[0], (D3DVertexBuffer *)data[2], data[1]);
data += 3;
}
for (i = 0; i < block->D6_PixelShaderConstants; ++i)
{
device->SetPixelShaderConstant(data[0], data + 1, 1);
data += 5;
}
for (i = 0; i < block->D7_VertexShaderConstants; ++i)
{
device->SetVertexShaderConstant(data[0] - 96, data + 1, 1);
data += 5;
}
for (i = 0; i < block->D8_RenderStates; ++i)
{
DWORD state = data[0];
DWORD value = data[1];
if (D3D__RenderState[state] != value)
{
device->SetRenderStateNotInline((D3DRENDERSTATETYPE)state, value);
}
data += 2;
}
for (i = 0; i < block->D9_TextureStates; ++i)
{
DWORD stage = data[0];
DWORD state = data[1];
DWORD value = data[2];
if (D3D__TextureState[stage][state] != value)
{
device->SetTextureStageStateNotInline(stage, (D3DTEXTURESTAGESTATETYPE)state, value);
}
data += 3;
}
for (i = 0; i < block->D10_Transforms; ++i)
{
device->SetTransform((D3DTRANSFORMSTATETYPE)data[0], (const D3DMATRIX *)(data + 1));
data += 1 + sizeof(D3DMATRIX) / sizeof(DWORD);
}
if (block->D11_Viewport)
{
device->SetViewport((const D3DVIEWPORT8 *)data);
data += sizeof(D3DVIEWPORT8) / sizeof(DWORD);
}
if (block->D12_Material)
{
device->SetMaterial((const D3DMATERIAL8 *)data);
data += sizeof(D3DMATERIAL8) / sizeof(DWORD);
}
if (block->D13_BackMaterial)
{
device->SetBackMaterial((const D3DMATERIAL8 *)data);
data += sizeof(D3DMATERIAL8) / sizeof(DWORD);
}
for (i = 0; i < block->D14_Lights; ++i)
{
device->SetLight(data[0], (const D3DLIGHT8 *)(data + 1));
data += 1 + sizeof(D3DLIGHT8) / sizeof(DWORD);
}
for (i = 0; i < block->D15_LightEnables; ++i)
{
device->LightEnable(data[0], data[1]);
data += 2;
}
return;
}
extern "C" void WINAPI D3DDevice_DeleteStateBlock(
DWORD token)
{
DWORD i;
StateBlock *block = (StateBlock *)token;
DWORD *data = StateBlockData(block);
for (i = 0; i < block->D0_Textures; ++i)
{
D3DBaseTexture *texture = (D3DBaseTexture *)data[1];
data += 2;
if (texture != NULL)
{
texture->Release();
}
}
if (block->D1_PixelShader)
{
DWORD handle = *data++;
if (handle != 0)
{
D3DDevice::DeletePixelShader(handle);
}
}
if (block->D2_VertexShader)
{
DWORD handle = *data++;
if (handle & D3DFVF_RESERVED0)
{
D3DDevice::DeleteVertexShader(handle);
}
}
if (block->D3_Index)
{
D3DIndexBuffer *indexBuffer = (D3DIndexBuffer *)data[1];
data += 2;
if (indexBuffer != NULL)
{
indexBuffer->Release();
}
}
for (i = 0; i < block->D4_Streams; ++i)
{
D3DVertexBuffer *vertexBuffer = (D3DVertexBuffer *)data[2];
data += 3;
if (vertexBuffer != NULL)
{
vertexBuffer->Release();
}
}
LocalFree(block);
return;
}
extern "C" void WINAPI D3DDevice_CaptureStateBlock(
DWORD token)
{
DWORD i;
CDevice *device = g_pDevice;
StateBlock *block = (StateBlock *)token;
DWORD *data = StateBlockData(block);
for (i = 0; i < block->D0_Textures; ++i)
{
DWORD stage = data[0];
D3DBaseTexture *texture = (D3DBaseTexture *)data[1];
if (texture) texture->Release();
texture = device->m_Textures[stage];
if (texture) texture->AddRef();
data[1] = (DWORD)texture;
data += 2;
}
if (block->D1_PixelShader)
{
DWORD handle = data[0];
if (handle) device->DeletePixelShader(handle);
handle = (DWORD)device->m_pPixelShader;
if (handle) ++device->m_pPixelShader->RefCount;
data[0] = handle;
++data;
}
if (block->D2_VertexShader)
{
DWORD handle = data[0];
if (handle & D3DFVF_RESERVED0) device->DeleteVertexShader(handle);
handle = device->m_VertexShaderHandle;
if (handle & D3DFVF_RESERVED0) ++device->m_pVertexShader->RefCount;
data[0] = handle;
++data;
}
if (block->D3_Index)
{
D3DIndexBuffer *indexBuffer = (D3DIndexBuffer *)data[1];
if (indexBuffer) indexBuffer->Release();
indexBuffer = device->m_pIndexBuffer;
if (indexBuffer) indexBuffer->AddRef();
data[0] = device->m_IndexBase;
data[1] = (DWORD)indexBuffer;
data += 2;
}
for (i = 0; i < block->D4_Streams; ++i)
{
DWORD index = data[0];
D3DVertexBuffer *vertexBuffer = (D3DVertexBuffer *)data[2];
if (vertexBuffer) vertexBuffer->Release();
vertexBuffer = g_Stream[index].pVertexBuffer;
if (vertexBuffer) vertexBuffer->AddRef();
data[1] = g_Stream[index].Stride;
data[2] = (DWORD)vertexBuffer;
data += 3;
}
for (i = 0; i < block->D6_PixelShaderConstants; ++i)
{
DWORD index = data[0];
void *constantData = data + 1;
memcpy(constantData, device->m_PixelShaderConstants[index], 4 * sizeof(FLOAT));
data += 5;
}
for (i = 0; i < block->D7_VertexShaderConstants; ++i)
{
DWORD index = data[0];
void *constantData = data + 1;
memcpy(constantData, device->m_VertexShaderConstants[index], 4 * sizeof(FLOAT));
data += 5;
}
for (i = 0; i < block->D8_RenderStates; ++i)
{
DWORD state = data[0];
data[1] = D3D__RenderState[state];
data += 2;
}
for (i = 0; i < block->D9_TextureStates; ++i)
{
DWORD stage = data[0];
DWORD state = data[1];
data[2] = D3D__TextureState[stage][state];
data += 3;
}
for (i = 0; i < block->D10_Transforms; ++i)
{
DWORD state = data[0];
memcpy((void *)(data + 1), &device->m_Transform[state], sizeof(device->m_Transform[state]));
data += 1 + sizeof(D3DMATRIX) / sizeof(DWORD);
}
if (block->D11_Viewport)
{
memcpy(data, &device->m_Viewport, sizeof(D3DVIEWPORT8));
data += sizeof(D3DVIEWPORT8) / sizeof(DWORD);
}
if (block->D12_Material)
{
memcpy(data, &device->m_Material, sizeof(D3DMATERIAL8));
data += sizeof(D3DMATERIAL8) / sizeof(DWORD);
}
if (block->D13_BackMaterial)
{
memcpy(data, &device->m_BackMaterial, sizeof(D3DMATERIAL8));
data += sizeof(D3DMATERIAL8) / sizeof(DWORD);
}
for (i = 0; i < block->D14_Lights; ++i)
{
DWORD index = data[0];
memcpy(data + 1, &device->m_pLights[index].Light8, sizeof(D3DLIGHT8));
data += 1 + sizeof(D3DLIGHT8) / sizeof(DWORD);
}
for (i = 0; i < block->D15_LightEnables; ++i)
{
device->GetLightEnable(data[0], (BOOL *)(data + 1));
data += 2;
}
return;
}
/* Cursor is a byte count during sizing and an allocated address during writing.
* Using ULONG_PTR avoids the historical NULL-based member/pointer arithmetic.
* Each conversion to a writable record occurs only after allocation succeeded.
* The dirty set and device state must not change concurrently between passes. */
HRESULT WINAPI RecordStateBlock(
DWORD *token)
{
DWORD i;
DWORD j;
BYTE *dirty;
StateBlock *block = NULL;
ULONG_PTR cursor = offsetof(StateBlock, Data);
CDevice *device = g_pDevice;
Light *lights = device->m_pLights;
*token = 0;
for (;;)
{
dirty = D3D__StateBlockDirty;
for (i = 0; i < D3DTSS_MAXSTAGES; ++i)
{
if (*dirty++)
{
if (block)
{
DWORD *data = (DWORD *)cursor;
D3DBaseTexture *texture = device->m_Textures[i];
++block->D0_Textures;
data[0] = i;
data[1] = (DWORD)texture;
if (texture) texture->AddRef();
}
cursor += 2 * sizeof(DWORD);
}
}
if (*dirty++)
{
if (block)
{
PixelShader *shader = device->m_pPixelShader;
block->D1_PixelShader = TRUE;
*(DWORD *)cursor = (DWORD)shader;
if (shader) ++shader->RefCount;
}
cursor += sizeof(DWORD);
}
if (*dirty++)
{
if (block)
{
DWORD handle = device->m_VertexShaderHandle;
block->D2_VertexShader = TRUE;
*(DWORD *)cursor = handle;
if (handle & D3DFVF_RESERVED0) ++device->m_pVertexShader->RefCount;
}
cursor += sizeof(DWORD);
}
if (*dirty++)
{
if (block)
{
DWORD *data = (DWORD *)cursor;
D3DIndexBuffer *indexBuffer = device->m_pIndexBuffer;
block->D3_Index = TRUE;
data[0] = device->m_IndexBase;
data[1] = (DWORD)indexBuffer;
if (indexBuffer) indexBuffer->AddRef();
}
cursor += 2 * sizeof(DWORD);
}
for (i = 0; i < D3DVS_STREAMS_MAX_V1_0; ++i)
{
if (*dirty++)
{
if (block)
{
DWORD *data = (DWORD *)cursor;
D3DVertexBuffer *vertexBuffer = g_Stream[i].pVertexBuffer;
++block->D4_Streams;
data[0] = i;
data[1] = g_Stream[i].Stride;
data[2] = (DWORD)vertexBuffer;
if (vertexBuffer) vertexBuffer->AddRef();
}
cursor += 3 * sizeof(DWORD);
}
}
for (i = 0; i < D3DPS_CONSTREG_MAX_DX8; ++i)
{
if (*dirty++ && device->m_pPixelShader)
{
if (block)
{
DWORD *data = (DWORD *)cursor;
++block->D6_PixelShaderConstants;
data[0] = i;
memcpy(data + 1, device->m_PixelShaderConstants[i], 4 * sizeof(FLOAT));
}
cursor += 5 * sizeof(DWORD);
}
}
for (i = 0; i < D3DVS_CONSTREG_COUNT_XBOX; ++i)
{
if (*dirty++ && i >= 96)
{
if (block)
{
DWORD *data = (DWORD *)cursor;
++block->D7_VertexShaderConstants;
data[0] = i;
memcpy(data + 1, device->m_VertexShaderConstants[i], 4 * sizeof(FLOAT));
}
cursor += 5 * sizeof(DWORD);
}
}
for (i = 0; i < D3DRS_MAX; ++i)
{
if (*dirty++)
{
if (device->m_pPixelShader || i < D3DRS_PSALPHAINPUTS0 ||
(i > D3DRS_PSINPUTTEXTURE && i != D3DRS_PSTEXTUREMODES))
{
if (block)
{
DWORD *data = (DWORD *)cursor;
++block->D8_RenderStates;
data[0] = i;
data[1] = D3D__RenderState[i];
}
cursor += 2 * sizeof(DWORD);
}
}
}
for (j = 0; j < D3DTSS_MAX; ++j)
{
for (i = 0; i < D3DTSS_MAXSTAGES; ++i)
{
if (*dirty++)
{
if (block)
{
DWORD *data = (DWORD *)cursor;
++block->D9_TextureStates;
data[0] = i;
data[1] = j;
data[2] = D3D__TextureState[i][j];
}
cursor += 3 * sizeof(DWORD);
}
}
}
for (i = 0; i < D3DTS_MAX; ++i)
{
if (*dirty++)
{
if (block)
{
DWORD *data = (DWORD *)cursor;
++block->D10_Transforms;
data[0] = i;
memcpy(data + 1, &device->m_Transform[i], sizeof(D3DMATRIX));
}
cursor += sizeof(DWORD) + sizeof(D3DMATRIX);
}
}
if (*dirty++)
{
if (block)
{
block->D11_Viewport = TRUE;
memcpy((void *)cursor, &device->m_Viewport, sizeof(D3DVIEWPORT8));
}
cursor += sizeof(D3DVIEWPORT8);
}
if (*dirty++)
{
if (block)
{
block->D12_Material = TRUE;
memcpy((void *)cursor, &device->m_Material, sizeof(D3DMATERIAL8));
}
cursor += sizeof(D3DMATERIAL8);
}
if (*dirty++)
{
if (block)
{
block->D13_BackMaterial = TRUE;
memcpy((void *)cursor, &device->m_BackMaterial, sizeof(D3DMATERIAL8));
}
cursor += sizeof(D3DMATERIAL8);
}
for (i = 0; i < device->m_LightCount; ++i)
{
if (lights[i].Flags & LIGHT_STATEBLOCK_SET)
{
if (block)
{
DWORD *data = (DWORD *)cursor;
++block->D14_Lights;
data[0] = i;
/* Original record path copies Light8 from the first light,
* whereas CaptureStateBlock uses the indexed light. Preserve
* this observed distinction; do not call it a corrected API. */
memcpy(data + 1, &lights->Light8, sizeof(D3DLIGHT8));
}
cursor += sizeof(DWORD) + sizeof(D3DLIGHT8);
}
}
for (i = 0; i < device->m_LightCount; ++i)
{
if (lights[i].Flags & LIGHT_STATEBLOCK_ENABLED)
{
if (block)
{
DWORD *data = (DWORD *)cursor;
++block->D15_LightEnables;
data[0] = i;
device->GetLightEnable(i, (BOOL *)(data + 1));
}
cursor += 2 * sizeof(DWORD);
}
}
if (block)
{
*token = (DWORD)block;
return S_OK;
}
block = (StateBlock *)LocalAlloc(LMEM_FIXED, cursor);
if (!block) return E_OUTOFMEMORY;
ZeroMemory(block, offsetof(StateBlock, Data));
cursor = (ULONG_PTR)StateBlockData(block);
}
}
const BYTE g_PixelRenderStates[] =
{
D3DRS_ZENABLE,
D3DRS_FILLMODE,
D3DRS_SHADEMODE,
D3DRS_ZWRITEENABLE,
D3DRS_ALPHATESTENABLE,
D3DRS_SRCBLEND,
D3DRS_DESTBLEND,
D3DRS_ZFUNC,
D3DRS_ALPHAREF,
D3DRS_ALPHAFUNC,
D3DRS_DITHERENABLE,
D3DRS_FOGSTART,
D3DRS_FOGEND,
D3DRS_FOGDENSITY,
D3DRS_EDGEANTIALIAS,
D3DRS_ALPHABLENDENABLE,
D3DRS_ZBIAS,
D3DRS_STENCILENABLE,
D3DRS_STENCILFAIL,
D3DRS_STENCILZFAIL,
D3DRS_STENCILPASS,
D3DRS_STENCILFUNC,
D3DRS_STENCILREF,
D3DRS_STENCILMASK,
D3DRS_STENCILWRITEMASK,
D3DRS_TEXTUREFACTOR,
D3DRS_WRAP0,
D3DRS_WRAP1,
D3DRS_WRAP2,
D3DRS_WRAP3,
D3DRS_COLORWRITEENABLE,
D3DRS_BLENDOP,
};
const BYTE g_PixelTextureStates[] =
{
D3DTSS_COLOROP,
D3DTSS_COLORARG1,
D3DTSS_COLORARG2,
D3DTSS_ALPHAOP,
D3DTSS_ALPHAARG1,
D3DTSS_ALPHAARG2,
D3DTSS_BUMPENVMAT00,
D3DTSS_BUMPENVMAT01,
D3DTSS_BUMPENVMAT10,
D3DTSS_BUMPENVMAT11,
D3DTSS_TEXCOORDINDEX,
D3DTSS_ADDRESSU,
D3DTSS_ADDRESSV,
D3DTSS_BORDERCOLOR,
D3DTSS_MAGFILTER,
D3DTSS_MINFILTER,
D3DTSS_MIPFILTER,
D3DTSS_MIPMAPLODBIAS,
D3DTSS_MAXMIPLEVEL,
D3DTSS_MAXANISOTROPY,
D3DTSS_BUMPENVLSCALE,
D3DTSS_BUMPENVLOFFSET,
D3DTSS_TEXTURETRANSFORMFLAGS,
D3DTSS_ADDRESSW,
D3DTSS_COLORARG0,
D3DTSS_ALPHAARG0,
D3DTSS_RESULTARG,
};
const BYTE g_VertexRenderStates[] =
{
D3DRS_SHADEMODE,
D3DRS_SPECULARENABLE,
D3DRS_CULLMODE,
D3DRS_FOGENABLE,
D3DRS_FOGCOLOR,
D3DRS_FOGTABLEMODE,
D3DRS_FOGSTART,
D3DRS_FOGEND,
D3DRS_FOGDENSITY,
D3DRS_RANGEFOGENABLE,
D3DRS_AMBIENT,
D3DRS_COLORVERTEX,
D3DRS_LIGHTING,
D3DRS_NORMALIZENORMALS,
D3DRS_LOCALVIEWER,
D3DRS_EMISSIVEMATERIALSOURCE,
D3DRS_AMBIENTMATERIALSOURCE,
D3DRS_DIFFUSEMATERIALSOURCE,
D3DRS_SPECULARMATERIALSOURCE,
D3DRS_VERTEXBLEND,
D3DRS_POINTSIZE,
D3DRS_POINTSIZE_MIN,
D3DRS_POINTSPRITEENABLE,
D3DRS_POINTSCALEENABLE,
D3DRS_POINTSCALE_A,
D3DRS_POINTSCALE_B,
D3DRS_POINTSCALE_C,
D3DRS_MULTISAMPLEANTIALIAS,
D3DRS_MULTISAMPLEMASK,
D3DRS_PATCHEDGESTYLE,
D3DRS_PATCHSEGMENTS,
D3DRS_POINTSIZE_MAX,
};
const BYTE g_VertexTextureStates[] =
{
D3DTSS_TEXCOORDINDEX,
D3DTSS_TEXTURETRANSFORMFLAGS,
};
extern "C" HRESULT WINAPI D3DDevice_CreateStateBlock(
D3DSTATEBLOCKTYPE type,
DWORD *token)
{
DWORD i;
DWORD j;
CDevice *device = g_pDevice;
if (type == D3DSBT_ALL)
{
memset(D3D__StateBlockDirty, 0xff, sizeof(D3D__StateBlockDirty));
Light *lights = device->m_pLights;
for (i = 0; i < device->m_LightCount; ++i)
{
if (lights[i].Flags & LIGHT_SET)
lights[i].Flags |= LIGHT_STATEBLOCK_SET | LIGHT_STATEBLOCK_ENABLED;
}
}
else
{
ClearStateBlockFlags();
if (type == D3DSBT_PIXELSTATE)
{
D3D__StateBlockDirty[D3DSBD_PIXELSHADER] = TRUE;
memset(D3D__StateBlockDirty + D3DSBD_PIXELSHADERCONSTANTS, 0xff, D3DPS_CONSTREG_MAX_DX8);
for (i = 0; i < sizeof(g_PixelRenderStates); ++i)
D3D__StateBlockDirty[D3DSBD_RENDERSTATES + g_PixelRenderStates[i]] = TRUE;
for (i = 0; i < D3DTSS_MAXSTAGES; ++i)
for (j = 0; j < sizeof(g_PixelTextureStates); ++j)
D3D__StateBlockDirty[D3DSBD_TEXTURESTATES + g_PixelTextureStates[j] * D3DTSS_MAXSTAGES + i] = TRUE;
}
else
{
D3D__StateBlockDirty[D3DSBD_VERTEXSHADER] = TRUE;
memset(D3D__StateBlockDirty + D3DSBD_VERTEXSHADERCONSTANTS, 0xff, D3DVS_CONSTREG_COUNT_XBOX);
for (i = 0; i < sizeof(g_VertexRenderStates); ++i)
D3D__StateBlockDirty[D3DSBD_RENDERSTATES + g_VertexRenderStates[i]] = TRUE;
for (i = 0; i < D3DTSS_MAXSTAGES; ++i)
for (j = 0; j < sizeof(g_VertexTextureStates); ++j)
D3D__StateBlockDirty[D3DSBD_TEXTURESTATES + g_VertexTextureStates[j] * D3DTSS_MAXSTAGES + i] = TRUE;
Light *lights = device->m_pLights;
for (i = 0; i < device->m_LightCount; ++i)
{
if (lights[i].Flags & LIGHT_SET)
lights[i].Flags |= LIGHT_STATEBLOCK_SET | LIGHT_STATEBLOCK_ENABLED;
}
}
}
return RecordStateBlock(token);
}
extern "C" HRESULT WINAPI D3DDevice_EndStateBlock(
DWORD *token)
{
g_pDevice->m_StateFlags &= ~STATE_RECORDBLOCK;
return RecordStateBlock(token);
}
extern "C" void WINAPI D3DDevice_Resume(
BOOL reset)
{
if (reset)
{
/* The original Resume path assumes state-block allocation succeeds.
* This is not a checked recovery path for allocation failure. */
DWORD token;
D3DDevice_CreateStateBlock(D3DSBT_ALL, &token);
D3DDevice_ApplyStateBlock(token);
D3DDevice_DeleteStateBlock(token);
}
return;
}
}