fteqw/engine/d3d/d3d_shader.c

480 lines
14 KiB
C

#include "quakedef.h"
#ifdef D3D9QUAKE
#include "shader.h"
#include "winquake.h"
#if !defined(HMONITOR_DECLARED) && (WINVER < 0x0500)
#define HMONITOR_DECLARED
DECLARE_HANDLE(HMONITOR);
#endif
#include <d3d9.h>
extern LPDIRECT3DDEVICE9 pD3DDev9;
typedef struct {
LPCSTR Name;
LPCSTR Definition;
} D3DXMACRO;
#define D3DXHANDLE void *
#define LPD3DXINCLUDE void *
#undef INTERFACE
#define INTERFACE d3dxbuffer
DECLARE_INTERFACE_(d3dxbuffer,IUnknown)
{
STDMETHOD(QueryInterface)(THIS_ REFIID,PVOID*) PURE;
STDMETHOD_(ULONG,AddRef)(THIS) PURE;
STDMETHOD_(ULONG,Release)(THIS) PURE;
STDMETHOD_(LPVOID,GetBufferPointer)(THIS) PURE;
STDMETHOD_(SIZE_T,GetBufferSize)(THIS) PURE;
};
typedef struct d3dxbuffer *LPD3DXBUFFER;
typedef enum _D3DXREGISTER_SET
{
D3DXRS_BOOL,
D3DXRS_INT4,
D3DXRS_FLOAT4,
D3DXRS_SAMPLER,
D3DXRS_FORCE_DWORD = 0x7fffffff
} D3DXREGISTER_SET, *LPD3DXREGISTER_SET;
typedef enum _D3DXPARAMETER_CLASS
{
D3DXPC_SCALAR,
D3DXPC_VECTOR,
D3DXPC_MATRIX_ROWS,
D3DXPC_MATRIX_COLUMNS,
D3DXPC_OBJECT,
D3DXPC_STRUCT,
D3DXPC_FORCE_DWORD = 0x7fffffff
} D3DXPARAMETER_CLASS, *LPD3DXPARAMETER_CLASS;
typedef enum _D3DXPARAMETER_TYPE
{
D3DXPT_VOID,
D3DXPT_BOOL,
D3DXPT_INT,
D3DXPT_FLOAT,
D3DXPT_STRING,
D3DXPT_TEXTURE,
D3DXPT_TEXTURE1D,
D3DXPT_TEXTURE2D,
D3DXPT_TEXTURE3D,
D3DXPT_TEXTURECUBE,
D3DXPT_SAMPLER,
D3DXPT_SAMPLER1D,
D3DXPT_SAMPLER2D,
D3DXPT_SAMPLER3D,
D3DXPT_SAMPLERCUBE,
D3DXPT_PIXELSHADER,
D3DXPT_VERTEXSHADER,
D3DXPT_PIXELFRAGMENT,
D3DXPT_VERTEXFRAGMENT,
} D3DXPARAMETER_TYPE, *LPD3DXPARAMETER_TYPE;
typedef struct _D3DXCONSTANT_DESC
{
LPCSTR Name; // Constant name
D3DXREGISTER_SET RegisterSet; // Register set
UINT RegisterIndex; // Register index
UINT RegisterCount; // Number of registers occupied
D3DXPARAMETER_CLASS Class; // Class
D3DXPARAMETER_TYPE Type; // Component type
UINT Rows; // Number of rows
UINT Columns; // Number of columns
UINT Elements; // Number of array elements
UINT StructMembers; // Number of structure member sub-parameters
UINT Bytes; // Data size, in bytes
LPCVOID DefaultValue; // Pointer to default value
} D3DXCONSTANT_DESC, *LPD3DXCONSTANT_DESC;
typedef struct _D3DXCONSTANTTABLE_DESC
{
LPCSTR Creator; // Creator string
DWORD Version; // Shader version
UINT Constants; // Number of constants
} D3DXCONSTANTTABLE_DESC, *LPD3DXCONSTANTTABLE_DESC;
#undef INTERFACE
#define INTERFACE d3dxconstanttable
DECLARE_INTERFACE_(d3dxconstanttable,IUnknown)
{
STDMETHOD(QueryInterface)(THIS_ REFIID,PVOID*) PURE;
STDMETHOD_(ULONG,AddRef)(THIS) PURE;
STDMETHOD_(ULONG,Release)(THIS) PURE;
STDMETHOD_(LPVOID,GetBufferPointer)(THIS) PURE;
STDMETHOD_(SIZE_T,GetBufferSize)(THIS) PURE;
STDMETHOD(GetDesc)(THIS_ D3DXCONSTANTTABLE_DESC *pDesc) PURE;
STDMETHOD(GetConstantDesc)(THIS_ D3DXHANDLE hConstant, D3DXCONSTANT_DESC *pConstantDesc, UINT *pCount) PURE;
STDMETHOD_(D3DXHANDLE, GetConstant)(THIS_ D3DXHANDLE hConstant, UINT Index) PURE;
STDMETHOD_(D3DXHANDLE, GetConstantByName)(THIS_ D3DXHANDLE hConstant, LPCSTR pName) PURE;
STDMETHOD_(D3DXHANDLE, GetConstantElement)(THIS_ D3DXHANDLE hConstant, UINT Index) PURE;
/*more stuff not included here cos I don't need it*/
};
typedef struct d3dxconstanttable *LPD3DXCONSTANTTABLE;
HRESULT (WINAPI *pD3DXCompileShader) (
LPCSTR pSrcData,
UINT SrcDataLen,
const D3DXMACRO *pDefines,
LPD3DXINCLUDE pInclude,
LPCSTR pEntrypoint,
LPCSTR pTarget,
UINT Flags,
LPD3DXBUFFER *ppCode,
LPD3DXBUFFER *ppErrorMsgs,
LPD3DXCONSTANTTABLE *constants
);
static dllhandle_t *shaderlib;
#ifndef IUnknown_Release
#define IUnknown_Release(This) \
(This)->lpVtbl -> Release(This)
#endif
static qboolean D3D9Shader_CreateProgram (program_t *prog, const char *sname, unsigned int permu, int ver, const char **precompilerconstants, const char *vert, const char *tcs, const char *tes, const char *frag, qboolean silent, vfsfile_t *blobfile)
{
D3DXMACRO defines[64];
LPD3DXBUFFER code = NULL, errors = NULL;
qboolean success = false;
prog->permu[permu].handle.hlsl.vert = NULL;
prog->permu[permu].handle.hlsl.frag = NULL;
if (pD3DXCompileShader)
{
int consts;
for (consts = 0; precompilerconstants[consts]; consts++)
;
consts+=2;
if (consts >= sizeof(defines) / sizeof(defines[0]))
return success;
consts = 0;
defines[consts].Name = NULL; /*shader type*/
defines[consts].Definition = "1";
consts++;
defines[consts].Name = "ENGINE_"DISTRIBUTION;
defines[consts].Definition = __DATE__;
consts++;
for (; *precompilerconstants; precompilerconstants++)
{
defines[consts].Name = NULL;
defines[consts].Definition = NULL;
consts++;
}
defines[consts].Name = NULL;
defines[consts].Definition = NULL;
success = true;
defines[0].Name = "VERTEX_SHADER";
if (FAILED(pD3DXCompileShader(vert, strlen(vert), defines, NULL, "main", "vs_2_0", 0, &code, &errors, (LPD3DXCONSTANTTABLE*)&prog->permu[permu].handle.hlsl.ctabv)))
success = false;
else
{
IDirect3DDevice9_CreateVertexShader(pD3DDev9, code->lpVtbl->GetBufferPointer(code), (IDirect3DVertexShader9**)&prog->permu[permu].handle.hlsl.vert);
code->lpVtbl->Release(code);
}
if (errors)
{
char *messages = errors->lpVtbl->GetBufferPointer(errors);
Con_Printf("Error compiling vertex shader %s:\n%s", sname, messages);
errors->lpVtbl->Release(errors);
}
defines[0].Name = "FRAGMENT_SHADER";
if (FAILED(pD3DXCompileShader(frag, strlen(frag), defines, NULL, "main", "ps_2_0", 0, &code, &errors, (LPD3DXCONSTANTTABLE*)&prog->permu[permu].handle.hlsl.ctabf)))
success = false;
else
{
IDirect3DDevice9_CreatePixelShader(pD3DDev9, code->lpVtbl->GetBufferPointer(code), (IDirect3DPixelShader9**)&prog->permu[permu].handle.hlsl.frag);
code->lpVtbl->Release(code);
}
if (errors)
{
char *messages = errors->lpVtbl->GetBufferPointer(errors);
Con_Printf("Error compiling pixel shader %s:\n%s", sname, messages);
errors->lpVtbl->Release(errors);
}
}
return success;
}
static int D3D9Shader_FindUniform_(LPD3DXCONSTANTTABLE ct, const char *name)
{
if (ct)
{
UINT dc = 1;
D3DXCONSTANT_DESC d;
if (!FAILED(ct->lpVtbl->GetConstantDesc(ct, (void*)name, &d, &dc)))
return d.RegisterIndex;
}
return -1;
}
static int D3D9Shader_FindUniform(union programhandle_u *h, int type, const char *name)
{
int offs;
if (!type || type == 1)
{
offs = D3D9Shader_FindUniform_(h->hlsl.ctabv, name);
if (offs >= 0)
return offs;
}
if (!type || type == 2)
{
offs = D3D9Shader_FindUniform_(h->hlsl.ctabf, name);
if (offs >= 0)
return offs;
}
return -1;
}
static void D3D9Shader_ProgAutoFields(program_t *prog, char **cvarnames, int *cvartypes)
{
unsigned int i, p;
qboolean found;
int uniformloc;
char tmpname[128];
cvar_t *cvar;
static const char *defaultsamplers[] =
{
"s_diffuse",
"s_normalmap",
"s_specular",
"s_upper",
"s_lower",
"s_fullbright",
"s_paletted",
"s_shadowmap",
"s_projectionmap",
"s_lightmap",
"s_deluxmap"
#if MAXRLIGHTMAPS > 1
,"s_lightmap1"
,"s_lightmap2"
,"s_lightmap3"
,"s_deluxmap1"
,"s_deluxmap2"
,"s_deluxmap3"
#endif
};
prog->numparams = 0;
prog->nofixedcompat = true;
/*set cvar uniforms*/
for (i = 0; cvarnames[i]; i++)
{
for (p = 0; cvarnames[i][p] && (unsigned char)cvarnames[i][p] > 32 && p < sizeof(tmpname)-1; p++)
tmpname[p] = cvarnames[i][p];
tmpname[p] = 0;
cvar = Cvar_FindVar(tmpname);
if (!cvar)
continue;
cvar->flags |= CVAR_SHADERSYSTEM;
for (p = 0; p < PERMUTATIONS; p++)
{
if (!prog->permu[p].handle.hlsl.vert || !prog->permu[p].handle.hlsl.frag)
continue;
uniformloc = D3D9Shader_FindUniform(&prog->permu[p].handle, 1, va("cvar_%s", tmpname));
if (uniformloc != -1)
{
vec4_t v = {cvar->value, 0, 0, 0};
IDirect3DDevice9_SetVertexShader(pD3DDev9, prog->permu[p].handle.hlsl.vert);
IDirect3DDevice9_SetVertexShaderConstantF(pD3DDev9, 0, v, 1);
}
uniformloc = D3D9Shader_FindUniform(&prog->permu[p].handle, 2, va("cvar_%s", tmpname));
if (uniformloc != -1)
{
vec4_t v = {cvar->value, 0, 0, 0};
IDirect3DDevice9_SetPixelShader(pD3DDev9, prog->permu[p].handle.hlsl.frag);
IDirect3DDevice9_SetPixelShaderConstantF(pD3DDev9, 0, v, 1);
}
}
}
for (i = 0; shader_unif_names[i].name; i++)
{
found = false;
for (p = 0; p < PERMUTATIONS; p++)
{
uniformloc = D3D9Shader_FindUniform(&prog->permu[p].handle, 0, shader_unif_names[i].name);
if (uniformloc != -1)
found = true;
prog->permu[p].parm[prog->numparams] = uniformloc;
}
if (found)
{
prog->parm[prog->numparams].type = shader_unif_names[i].ptype;
prog->numparams++;
}
}
/*set texture uniforms*/
for (p = 0; p < PERMUTATIONS; p++)
{
for (i = 0; i < 8; i++)
{
uniformloc = D3D9Shader_FindUniform(&prog->permu[p].handle, 2, va("s_t%i", i));
if (uniformloc != -1)
{
int v[4] = {i};
IDirect3DDevice9_SetPixelShader(pD3DDev9, prog->permu[p].handle.hlsl.frag);
IDirect3DDevice9_SetPixelShaderConstantI(pD3DDev9, 0, v, 1);
if (prog->numsamplers < i+1)
prog->numsamplers = i+1;
}
}
for (i = 0; i < sizeof(defaultsamplers)/sizeof(defaultsamplers[0]); i++)
{
//figure out which ones are needed.
if (prog->defaulttextures & (1u<<i))
continue; //don't spam
uniformloc = D3D9Shader_FindUniform(&prog->permu[p].handle, 2, defaultsamplers[i]);
if (uniformloc != -1)
prog->defaulttextures |= (1u<<i);
}
}
//multiple lightmaps is kinda hacky. if any are set, all must be.
if (prog->defaulttextures & ((1u<<11) | (1u<<12) | (1u<<13)))
prog->defaulttextures |=((1u<<11) | (1u<<12) | (1u<<13));
if (prog->defaulttextures & ((1u<<14) | (1u<<15) | (1u<<16)))
prog->defaulttextures |=((1u<<14) | (1u<<15) | (1u<<16));
if (prog->defaulttextures)
{
unsigned int sampnum;
/*set default texture uniforms*/
for (p = 0; p < PERMUTATIONS; p++)
{
if (!prog->permu[p].handle.glsl.handle)
continue;
sampnum = prog->numsamplers;
for (i = 0; i < sizeof(defaultsamplers)/sizeof(defaultsamplers[0]); i++)
{
if (prog->defaulttextures & (1u<<i))
{
uniformloc = D3D9Shader_FindUniform(&prog->permu[p].handle, 2, defaultsamplers[i]);
if (uniformloc != -1)
{
int v[4] = {sampnum};
IDirect3DDevice9_SetPixelShader(pD3DDev9, prog->permu[p].handle.hlsl.frag);
IDirect3DDevice9_SetPixelShaderConstantI(pD3DDev9, 0, v, 1);
}
sampnum++;
}
}
}
}
IDirect3DDevice9_SetVertexShader(pD3DDev9, NULL);
IDirect3DDevice9_SetPixelShader(pD3DDev9, NULL);
}
void D3D9Shader_DeleteProg(program_t *prog, unsigned int permu)
{
if (prog->permu[permu].handle.hlsl.vert)
{
IDirect3DVertexShader9 *vs = prog->permu[permu].handle.hlsl.vert;
prog->permu[permu].handle.hlsl.vert = NULL;
IDirect3DVertexShader9_Release(vs);
}
if (prog->permu[permu].handle.hlsl.frag)
{
IDirect3DPixelShader9 *fs = prog->permu[permu].handle.hlsl.frag;
prog->permu[permu].handle.hlsl.frag = NULL;
IDirect3DPixelShader9_Release(fs);
}
if (prog->permu[permu].handle.hlsl.ctabv)
{
LPD3DXCONSTANTTABLE vct = prog->permu[permu].handle.hlsl.ctabv;
prog->permu[permu].handle.hlsl.ctabv = NULL;
IUnknown_Release(vct);
}
if (prog->permu[permu].handle.hlsl.ctabf)
{
LPD3DXCONSTANTTABLE fct = prog->permu[permu].handle.hlsl.ctabf;
prog->permu[permu].handle.hlsl.ctabf = NULL;
IUnknown_Release(fct);
}
}
void D3D9Shader_Init(void)
{
D3DCAPS9 caps;
dllfunction_t funcs[] =
{
{(void**)&pD3DXCompileShader, "D3DXCompileShader"},
{NULL,NULL}
};
if (!shaderlib)
shaderlib = Sys_LoadLibrary("d3dx9_32", funcs);
if (!shaderlib)
shaderlib = Sys_LoadLibrary("d3dx9_34", funcs);
memset(&sh_config, 0, sizeof(sh_config));
sh_config.minver = 9;
sh_config.maxver = 9;
sh_config.blobpath = "hlsl/%s.blob";
sh_config.progpath = shaderlib?"hlsl/%s.hlsl":NULL;
sh_config.shadernamefmt = "%s_hlsl9";
sh_config.progs_supported = !!shaderlib;
sh_config.progs_required = false;
sh_config.pDeleteProg = D3D9Shader_DeleteProg;
sh_config.pLoadBlob = NULL;//D3D9Shader_LoadBlob;
sh_config.pCreateProgram = D3D9Shader_CreateProgram;
sh_config.pProgAutoFields = D3D9Shader_ProgAutoFields;
sh_config.tex_env_combine = 1;
sh_config.nv_tex_env_combine4 = 1;
sh_config.env_add = 1;
//FIXME: check caps
sh_config.texfmt[PTI_RGBX8] = true; //fixme: shouldn't support
sh_config.texfmt[PTI_RGBA8] = true; //fixme: shouldn't support
sh_config.texfmt[PTI_BGRX8] = true;
sh_config.texfmt[PTI_BGRA8] = true;
sh_config.texfmt[PTI_RGB565] = true;
sh_config.texfmt[PTI_ARGB1555] = true;
sh_config.texfmt[PTI_ARGB4444] = true;
IDirect3DDevice9_GetDeviceCaps(pD3DDev9, &caps);
if (caps.TextureCaps & D3DPTEXTURECAPS_POW2)
{ //this flag is a LIMITATION, not a capability.
sh_config.texture_non_power_of_two = false;
sh_config.texture_non_power_of_two_pic = !!(caps.TextureCaps & D3DPTEXTURECAPS_NONPOW2CONDITIONAL); //pic npot is supported if both flags are set.
}
else
{ //modern cards support npot
sh_config.texture_non_power_of_two_pic = true;
sh_config.texture_non_power_of_two = true;
}
sh_config.texture_maxsize = min(caps.MaxTextureWidth, caps.MaxTextureHeight);
}
#endif