1
0
mirror of https://github.com/doitsujin/dxvk.git synced 2025-01-21 02:52:10 +01:00
dxvk/src/d3d8/d3d8_shader.cpp
Jeff 60e523b4bf
[d3d8] Implement Direct3D 8 Frontend
Co-authored-by: WinterSnowfall <WinterSnowfall@users.noreply.github.com>

## Config Changes

Co-authored-by: Blisto91 <47954800+Blisto91@users.noreply.github.com>
Co-authored-by: simifor <simirmonfor@gmail.com>
2024-07-07 11:10:48 +00:00

337 lines
12 KiB
C++

#include "d3d8_shader.h"
#define VSD_SHIFT_MASK(token, field) ((token & field ## MASK) >> field ## SHIFT)
#define VSD_ENCODE(token, field) ((token << field ## _SHIFT) & field ## _MASK)
// Magic number from D3DVSD_SKIP(...)
#define VSD_SKIP_FLAG 0x10000000
// This bit is set on all parameter (non-instruction) tokens.
#define VS_BIT_PARAM 0x80000000
namespace dxvk {
static constexpr int D3D8_NUM_VERTEX_INPUT_REGISTERS = 17;
/**
* Standard mapping of vertex input registers v0-v16 to D3D9 usages and usage indices
* (See D3DVSDE_REGISTER values in d3d8types.h or DirectX 8 docs for vertex shader input registers vN)
*
* \cite https://learn.microsoft.com/en-us/windows/win32/direct3d9/mapping-between-a-directx-9-declaration-and-directx-8
*/
static constexpr BYTE D3D8_VERTEX_INPUT_REGISTERS[D3D8_NUM_VERTEX_INPUT_REGISTERS][2] = {
{d3d9::D3DDECLUSAGE_POSITION, 0}, // dcl_position v0
{d3d9::D3DDECLUSAGE_BLENDWEIGHT, 0}, // dcl_blendweight v1
{d3d9::D3DDECLUSAGE_BLENDINDICES, 0}, // dcl_blendindices v2
{d3d9::D3DDECLUSAGE_NORMAL, 0}, // dcl_normal v3
{d3d9::D3DDECLUSAGE_PSIZE, 0}, // dcl_psize v4
{d3d9::D3DDECLUSAGE_COLOR, 0}, // dcl_color v5 ; diffuse
{d3d9::D3DDECLUSAGE_COLOR, 1}, // dcl_color1 v6 ; specular
{d3d9::D3DDECLUSAGE_TEXCOORD, 0}, // dcl_texcoord0 v7
{d3d9::D3DDECLUSAGE_TEXCOORD, 1}, // dcl_texcoord1 v8
{d3d9::D3DDECLUSAGE_TEXCOORD, 2}, // dcl_texcoord2 v9
{d3d9::D3DDECLUSAGE_TEXCOORD, 3}, // dcl_texcoord3 v10
{d3d9::D3DDECLUSAGE_TEXCOORD, 4}, // dcl_texcoord4 v11
{d3d9::D3DDECLUSAGE_TEXCOORD, 5}, // dcl_texcoord5 v12
{d3d9::D3DDECLUSAGE_TEXCOORD, 6}, // dcl_texcoord6 v13
{d3d9::D3DDECLUSAGE_TEXCOORD, 7}, // dcl_texcoord7 v14
{d3d9::D3DDECLUSAGE_POSITION, 1}, // dcl_position1 v15 ; position 2
{d3d9::D3DDECLUSAGE_NORMAL, 1}, // dcl_normal1 v16 ; normal 2
};
/** Width in bytes of each d3d9::D3DDECLTYPE or d3d8 D3DVSDT_TYPE */
static constexpr BYTE D3D9_DECL_TYPE_SIZES[d3d9::MAXD3DDECLTYPE + 1] = {
4, // FLOAT1
8, // FLOAT2
12, // FLOAT3
16, // FLOAT4
4, // D3DCOLOR
4, // UBYTE4
4, // SHORT2
8, // SHORT4
// The following are for vs2.0+ //
4, // UBYTE4N
4, // SHORT2N
8, // SHORT4N
4, // USHORT2N
8, // USHORT4N
6, // UDEC3
6, // DEC3N
8, // FLOAT16_2
16, // FLOAT16_4
0 // UNUSED
};
/**
* Encodes a \ref DxsoShaderInstruction
*
* \param [in] opcode DxsoOpcode
* \cite https://learn.microsoft.com/en-us/windows-hardware/drivers/display/instruction-token
*/
constexpr DWORD encodeInstruction(d3d9::D3DSHADER_INSTRUCTION_OPCODE_TYPE opcode) {
DWORD token = 0;
token |= opcode & 0xFFFF; // bits 0:15
return token;
}
/**
* Encodes a \ref DxsoRegister
*
* \param [in] regType DxsoRegisterType
* \cite https://learn.microsoft.com/en-us/windows-hardware/drivers/display/destination-parameter-token
*/
constexpr DWORD encodeDestRegister(d3d9::D3DSHADER_PARAM_REGISTER_TYPE type, UINT reg) {
DWORD token = 0;
token |= reg & 0x7FF; // bits 0:10 num
token |= ((type & 0x07) << 28); // bits 28:30 type[0:2]
token |= ((type & 0x18) >> 3) << 11; // bits 11:12 type[3:4]
// UINT addrMode : 1; // bit 13 hasRelative
token |= 0b1111 << 16; // bits 16:19 DxsoRegMask
// UINT resultModifier : 3; // bits 20:23
// UINT resultShift : 3; // bits 24:27
token |= 1 << 31; // bit 31 always 1
return token;
}
/**
* Encodes a \ref DxsoDeclaration
*
* \cite https://learn.microsoft.com/en-us/windows-hardware/drivers/display/dcl-instruction
*/
constexpr DWORD encodeDeclaration(d3d9::D3DDECLUSAGE usage, DWORD index) {
DWORD token = 0;
token |= VSD_ENCODE(usage, D3DSP_DCL_USAGE); // bits 0:4 DxsoUsage (TODO: missing MSB)
token |= VSD_ENCODE(index, D3DSP_DCL_USAGEINDEX); // bits 16:19 usageIndex
token |= 1 << 31; // bit 31 always 1
return token;
}
/**
* Converts a D3D8 vertex shader + declaration
* to a D3D9 vertex shader + declaration.
*/
D3D9VertexShaderCode TranslateVertexShader8(
const DWORD* pDeclaration,
const DWORD* pFunction,
const D3D8Options& options) {
using d3d9::D3DDECLTYPE;
using d3d9::D3DDECLTYPE_UNUSED;
D3D9VertexShaderCode result;
std::vector<DWORD>& tokens = result.function;
std::vector<DWORD> defs; // Constant definitions
// shaderInputRegisters:
// set bit N to enable input register vN
DWORD shaderInputRegisters = 0;
d3d9::D3DVERTEXELEMENT9* vertexElements = result.declaration;
unsigned int elementIdx = 0;
// These are used for pDeclaration and pFunction
int i = 0;
DWORD token;
std::stringstream dbg;
dbg << "Vertex Declaration Tokens:\n\t";
WORD currentStream = 0;
WORD currentOffset = 0;
auto addVertexElement = [&] (D3DVSDE_REGISTER reg, D3DVSDT_TYPE type) {
vertexElements[elementIdx].Stream = currentStream;
vertexElements[elementIdx].Offset = currentOffset;
vertexElements[elementIdx].Method = d3d9::D3DDECLMETHOD_DEFAULT;
vertexElements[elementIdx].Type = D3DDECLTYPE(type); // (D3DVSDT_TYPE values map directly to D3DDECLTYPE)
vertexElements[elementIdx].Usage = D3D8_VERTEX_INPUT_REGISTERS[reg][0];
vertexElements[elementIdx].UsageIndex = D3D8_VERTEX_INPUT_REGISTERS[reg][1];
// Increase stream offset
currentOffset += D3D9_DECL_TYPE_SIZES[type];
// Enable register vn
shaderInputRegisters |= 1 << reg;
// Finished with this element
elementIdx++;
};
// Remap d3d8 decl tokens to d3d9 vertex elements,
// and enable bits on shaderInputRegisters for each.
if (options.forceVsDecl.size() == 0) do {
token = pDeclaration[i++];
D3DVSD_TOKENTYPE tokenType = D3DVSD_TOKENTYPE(VSD_SHIFT_MASK(token, D3DVSD_TOKENTYPE));
switch (tokenType) {
case D3DVSD_TOKEN_NOP:
dbg << "NOP";
break;
case D3DVSD_TOKEN_STREAM: {
dbg << "STREAM ";
// TODO: D3DVSD_STREAM_TESS
if (token & D3DVSD_STREAMTESSMASK) {
dbg << "TESS";
}
DWORD streamNum = VSD_SHIFT_MASK(token, D3DVSD_STREAMNUMBER);
currentStream = WORD(streamNum);
currentOffset = 0; // reset offset
dbg << ", num=" << streamNum;
break;
}
case D3DVSD_TOKEN_STREAMDATA: {
dbg << "STREAMDATA ";
// D3DVSD_SKIP
if (token & VSD_SKIP_FLAG) {
auto skipCount = VSD_SHIFT_MASK(token, D3DVSD_SKIPCOUNT);
dbg << "SKIP " << " count=" << skipCount;
currentOffset += WORD(skipCount) * sizeof(DWORD);
break;
}
// D3DVSD_REG
DWORD dataLoadType = VSD_SHIFT_MASK(token, D3DVSD_DATALOADTYPE);
if ( dataLoadType == 0 ) { // vertex
D3DVSDT_TYPE type = D3DVSDT_TYPE(VSD_SHIFT_MASK(token, D3DVSD_DATATYPE));
D3DVSDE_REGISTER reg = D3DVSDE_REGISTER(VSD_SHIFT_MASK(token, D3DVSD_VERTEXREG));
addVertexElement(reg, type);
dbg << "type=" << type << ", register=" << reg;
} else {
// TODO: When would this bit be 1?
dbg << "D3DVSD_DATALOADTYPE " << dataLoadType;
}
break;
}
case D3DVSD_TOKEN_TESSELLATOR:
dbg << "TESSELLATOR " << std::hex << token;
// TODO: D3DVSD_TOKEN_TESSELLATOR
break;
case D3DVSD_TOKEN_CONSTMEM: {
dbg << "CONSTMEM ";
DWORD count = VSD_SHIFT_MASK(token, D3DVSD_CONSTCOUNT);
DWORD regCount = count * 4;
DWORD addr = VSD_SHIFT_MASK(token, D3DVSD_CONSTADDRESS);
DWORD rs = VSD_SHIFT_MASK(token, D3DVSD_CONSTRS);
dbg << "count=" << count << ", addr=" << addr << ", rs=" << rs;
// Add a DEF instruction for each constant
for (DWORD j = 0; j < regCount; j += 4) {
defs.push_back(encodeInstruction(d3d9::D3DSIO_DEF));
defs.push_back(encodeDestRegister(d3d9::D3DSPR_CONST2, addr));
defs.push_back(pDeclaration[i+j+0]);
defs.push_back(pDeclaration[i+j+1]);
defs.push_back(pDeclaration[i+j+2]);
defs.push_back(pDeclaration[i+j+3]);
addr++;
}
i += regCount;
break;
}
case D3DVSD_TOKEN_EXT: {
dbg << "EXT " << std::hex << token << " ";
DWORD extInfo = VSD_SHIFT_MASK(token, D3DVSD_EXTINFO);
DWORD extCount = VSD_SHIFT_MASK(token, D3DVSD_EXTCOUNT);
dbg << "info=" << extInfo << ", count=" << extCount;
break;
}
case D3DVSD_TOKEN_END: {
vertexElements[elementIdx++] = D3DDECL_END();
dbg << "END";
break;
}
default:
dbg << "UNKNOWN TYPE";
break;
}
dbg << "\n\t";
//dbg << std::hex << token << " ";
} while (token != D3DVSD_END());
Logger::debug(dbg.str());
// If forceVsDecl is set, use that decl instead.
if (options.forceVsDecl.size() > 0) {
for (auto [reg, type] : options.forceVsDecl) {
addVertexElement(reg, type);
}
vertexElements[elementIdx++] = D3DDECL_END();
}
if (pFunction != nullptr) {
// Copy first token (version)
tokens.push_back(pFunction[0]);
DWORD vsMajor = D3DSHADER_VERSION_MAJOR(pFunction[0]);
DWORD vsMinor = D3DSHADER_VERSION_MINOR(pFunction[0]);
Logger::debug(str::format("VS version: ", vsMajor, ".", vsMinor));
// Insert dcl instructions
for (int vn = 0; vn < D3D8_NUM_VERTEX_INPUT_REGISTERS; vn++) {
// If bit N is set then we need to dcl register vN
if ((shaderInputRegisters & (1 << vn)) != 0) {
Logger::debug(str::format("\tShader Input Regsiter: v", vn));
DWORD usage = D3D8_VERTEX_INPUT_REGISTERS[vn][0];
DWORD index = D3D8_VERTEX_INPUT_REGISTERS[vn][1];
tokens.push_back(encodeInstruction(d3d9::D3DSIO_DCL)); // dcl opcode
tokens.push_back(encodeDeclaration(d3d9::D3DDECLUSAGE(usage), index)); // usage token
tokens.push_back(encodeDestRegister(d3d9::D3DSPR_INPUT, vn)); // dest register num
}
}
// Copy constant defs
for (DWORD def : defs) {
tokens.push_back(def);
}
// Copy shader tokens from input,
// skip first token (we already copied it)
i = 1;
do {
token = pFunction[i++];
DWORD opcode = token & D3DSI_OPCODE_MASK;
// Instructions
if ((token & VS_BIT_PARAM) == 0) {
// RSQ swizzle fixup
if (opcode == D3DSIO_RSQ) {
tokens.push_back(token); // instr
tokens.push_back(token = pFunction[i++]); // dest
token = pFunction[i++]; // src0
// If no swizzling is done, then use the w-component.
// See d8vk#43 for more information as this may need to change in some cases.
if (((token & D3DVS_NOSWIZZLE) == D3DVS_NOSWIZZLE)) {
token &= ~D3DVS_SWIZZLE_MASK;
token |= (D3DVS_X_W | D3DVS_Y_W | D3DVS_Z_W | D3DVS_W_W);
}
}
}
tokens.push_back(token);
} while (token != D3DVS_END());
}
return result;
}
}