Skip to content

Instantly share code, notes, and snippets.

@dwilliamson
Last active December 8, 2017 17:50
Show Gist options
  • Save dwilliamson/5456c7ac254c92c544e65f06a4237905 to your computer and use it in GitHub Desktop.
Save dwilliamson/5456c7ac254c92c544e65f06a4237905 to your computer and use it in GitHub Desktop.
Compile-time Shader Assembly for DX8

In the olden DX8 days a common technique was to compile shaders at runtime using assembly instructions in strings. You would composite sections of pre-written shader based on how many lights were active, what type of material you were lighting and any other properties that would affect rendering.

This had two problems, in order of effect:

  1. You'd have to pass the assembly code onto the DX8 runtime which would then parse and compile it.
  2. You're doing a whole bunch of string concatenations everywhere. With dynamic register allocation, that means sprintf calls.

The reason runtime compilation was common was that precompiling all combinations offline was prohibitive as the total count would be in the 10s of thousands.

Anyway, I was never happy with the performance of compiling shaders at runtime; there'd be noticable hitching as the game would spend upwards of 100ms compiling. There were imperfect ways of stepping around the issue, like precompiling all shaders on specific offline camera paths, but you'd still hit failure paths.

I ended up building a compositing engine that allowed you to author shaders in C++, using a pseudo shader assembly instruction set that would compile directly to the opcodes in memory. As a result we never had hitching. Edit: you'd still have to create the shader object and the driver would sometimes do non-trivial work on that behind the scenes.

Here is a small portion of the files involved:

  • ShaderConfig.h: Each surface shader was configured using a set of properties from the incoming light, the mesh type being shaded and the material.
  • ShaderConfig.cpp: Shows the compositing of shader config info into a single integer. These integers would be cached as much as possible but the failure path was fast enough.
  • ShaderManager.cpp: Would map a shader config to one of the pre-written shader compositing engines. First it lookup by integer ID in the hash table before attempting to build a new one.
  • Shader_DiffuseSpecularBump.cpp: One example of a shader and its possible permutations. There were about 30 of these.
  • RegisterAllocator.h: You could ensure minimal temporary register count use by dynamically allocating them.
  • ShaderAssembler.h: The core shader assembler itself.
  • Opcodes.h: 1.1/1.4 specific shader opcides.

This code dates back to around 2003.

#ifndef _INCLUDED_OPCODES_H
#define _INCLUDED_OPCODES_H
#ifndef _INCLUDED_ASSEMBLER_H
#error "Must include Assembler.h, not this file"
#endif
// Within namespace sha
extern DWORD* sptr;
SA_INLINE void BeginVS(DWORD * ptr)
{
// All shaders begin with a version number
sptr = ptr;
nb_instructions = 0;
*sptr++ = D3DVS_VERSION(1, 1);
}
SA_INLINE void EndVS(void)
{
*sptr++ = D3DVS_END();
sptr = 0;
}
SA_INLINE void BeginPS(DWORD* ptr)
{
sptr = ptr;
nb_instructions = 0;
*sptr++ = D3DPS_VERSION(1, 1);
}
SA_INLINE void EndPS(void)
{
*sptr++ = D3DPS_END();
sptr = 0;
}
#define MATHOP_3_SINGLE(name, op, mod) \
__forceinline void name(CDWORD dst, CDWORD src_a, CDWORD src_b) \
{ \
opcode3(sptr, op, dst | mod, src_a, src_b); \
}
#define MATHOP_4_SINGLE(name, op, mod) \
__forceinline void name(CDWORD dst, CDWORD src_a, CDWORD src_b, CDWORD src_c) \
{ \
opcode4(sptr, op, dst | mod, src_a, src_b, src_c); \
}
#define MATHOP(name, c, op) \
MATHOP_##c##_SINGLE(name, op, 0) \
MATHOP_##c##_SINGLE(name##_##x2, op, (1 << D3DSP_DSTSHIFT_SHIFT)) \
MATHOP_##c##_SINGLE(name##_##x4, op, (2 << D3DSP_DSTSHIFT_SHIFT)) \
MATHOP_##c##_SINGLE(name##_##sat, op, D3DSPDM_SATURATE) \
MATHOP_##c##_SINGLE(name##_##x2_sat, op, ((1 << D3DSP_DSTSHIFT_SHIFT) | D3DSPDM_SATURATE)) \
MATHOP_##c##_SINGLE(name##_##x4_sat, op, ((2 << D3DSP_DSTSHIFT_SHIFT) | D3DSPDM_SATURATE))
MATHOP(mul, 3, D3DSIO_MUL);
MATHOP(dp3, 3, D3DSIO_DP3);
MATHOP(dp4, 3, D3DSIO_DP4);
MATHOP(min, 3, D3DSIO_MIN);
MATHOP(max, 3, D3DSIO_MAX);
MATHOP(add, 3, D3DSIO_ADD);
MATHOP(sub, 3, D3DSIO_SUB);
MATHOP(mad, 4, D3DSIO_MAD);
SA_INLINE void nop(void)
{
opcode0(sptr, D3DSIO_NOP);
}
SA_INLINE void mov(CDWORD dst, CDWORD src)
{
opcode2(sptr, D3DSIO_MOV, dst, src);
}
SA_INLINE void rcp(CDWORD dst, CDWORD src)
{
opcode2(sptr, D3DSIO_RCP, dst, src);
}
SA_INLINE void rsq(CDWORD dst, CDWORD src)
{
opcode2(sptr, D3DSIO_RSQ, dst, src);
}
SA_INLINE void slt(CDWORD dst, CDWORD src_a, CDWORD src_b)
{
opcode3(sptr, D3DSIO_SLT, dst, src_a, src_b);
}
SA_INLINE void sge(CDWORD dst, CDWORD src_a, CDWORD src_b)
{
opcode3(sptr, D3DSIO_SGE, dst, src_a, src_b);
}
SA_INLINE void exp(CDWORD dst, CDWORD src)
{
opcode2(sptr, D3DSIO_EXP, dst, src);
}
SA_INLINE void log(CDWORD dst, CDWORD src)
{
opcode2(sptr, D3DSIO_LOG, dst, src);
}
SA_INLINE void lit(CDWORD dst, CDWORD src)
{
opcode2(sptr, D3DSIO_LIT, dst, src);
}
SA_INLINE void dst(CDWORD dst, CDWORD src_a, CDWORD src_b)
{
opcode3(sptr, D3DSIO_DST, dst, src_a, src_b);
}
SA_INLINE void lrp(CDWORD dst, CDWORD src_a, CDWORD src_b, CDWORD src_c)
{
opcode4(sptr, D3DSIO_LRP, dst, src_a, src_b, src_c);
}
SA_INLINE void frc(CDWORD dst, CDWORD src)
{
opcode2(sptr, D3DSIO_FRC, dst, src);
}
SA_INLINE void m4x4(CDWORD dst, CDWORD src_a, CDWORD src_b)
{
opcode3(sptr, D3DSIO_M4x4, dst, src_a, src_b);
}
SA_INLINE void m4x3(CDWORD dst, CDWORD src_a, CDWORD src_b)
{
opcode3(sptr, D3DSIO_M4x3, dst, src_a, src_b);
}
SA_INLINE void m3x4(CDWORD dst, CDWORD src_a, CDWORD src_b)
{
opcode3(sptr, D3DSIO_M3x4, dst, src_a, src_b);
}
SA_INLINE void m3x3(CDWORD dst, CDWORD src_a, CDWORD src_b)
{
opcode3(sptr, D3DSIO_M3x3, dst, src_a, src_b);
}
SA_INLINE void m3x2(CDWORD dst, CDWORD src_a, CDWORD src_b)
{
opcode3(sptr, D3DSIO_M3x2, dst, src_a, src_b);
}
SA_INLINE void texcoord(CDWORD dst, CDWORD src)
{
opcode2(sptr, D3DSIO_TEXCOORD, dst, src);
}
SA_INLINE void texkill(CDWORD dst)
{
opcode1_dst(sptr, D3DSIO_TEXKILL, dst);
}
SA_INLINE void tex(CDWORD dst)
{
opcode1_dst(sptr, D3DSIO_TEX, dst);
}
SA_INLINE void texbem(CDWORD dst, CDWORD src)
{
opcode2(sptr, D3DSIO_TEXBEM, dst, src);
}
SA_INLINE void texbeml(CDWORD dst, CDWORD src)
{
opcode2(sptr, D3DSIO_TEXBEML, dst, src);
}
SA_INLINE void texreg2ar(CDWORD dst, CDWORD src)
{
opcode2(sptr, D3DSIO_TEXREG2AR, dst, src);
}
SA_INLINE void texreg2gb(CDWORD dst, CDWORD src)
{
opcode2(sptr, D3DSIO_TEXREG2GB, dst, src);
}
SA_INLINE void texm3x2pad(CDWORD dst, CDWORD src)
{
opcode2(sptr, D3DSIO_TEXM3x2PAD, dst, src);
}
SA_INLINE void texm3x2tex(CDWORD dst, CDWORD src)
{
opcode2(sptr, D3DSIO_TEXM3x2TEX, dst, src);
}
SA_INLINE void texm3x3pad(CDWORD dst, CDWORD src)
{
opcode2(sptr, D3DSIO_TEXM3x3PAD, dst, src);
}
SA_INLINE void texm3x3tex(CDWORD dst, CDWORD src)
{
opcode2(sptr, D3DSIO_TEXM3x3TEX, dst, src);
}
SA_INLINE void texm3x3spec(CDWORD dst, CDWORD src_a, CDWORD src_b)
{
opcode3(sptr, D3DSIO_TEXM3x3SPEC, dst, src_a, src_b);
}
SA_INLINE void texm3x3vspec(CDWORD dst, CDWORD src)
{
opcode2(sptr, D3DSIO_TEXM3x3VSPEC, dst, src);
}
SA_INLINE void cnd(CDWORD dst, CDWORD src_a, CDWORD src_b, CDWORD src_c, CDWORD src_d)
{
opcode4(sptr, D3DSIO_CND, dst, src_a, src_b, src_d);
}
SA_INLINE void def(CDWORD dst, CDWORD src_a, CDWORD src_b, CDWORD src_c, CDWORD src_d)
{
opcode5(sptr, D3DSIO_DEF, dst, src_a, src_b, src_c, src_d);
}
SA_INLINE void texreg2rgb(CDWORD dst, CDWORD src)
{
opcode2(sptr, D3DSIO_TEXREG2RGB, dst, src);
}
SA_INLINE void texdp3tex(CDWORD dst, CDWORD src)
{
opcode2(sptr, D3DSIO_TEXDP3TEX, dst, src);
}
SA_INLINE void texm3x2depth(CDWORD dst, CDWORD src)
{
opcode2(sptr, D3DSIO_TEXM3x2DEPTH, dst, src);
}
SA_INLINE void texdp3(CDWORD dst, CDWORD src)
{
opcode2(sptr, D3DSIO_TEXDP3, dst, src);
}
SA_INLINE void texm3x3(CDWORD dst, CDWORD src)
{
opcode2(sptr, D3DSIO_TEXM3x3, dst, src);
}
SA_INLINE void cmp(CDWORD dst, CDWORD src_a, CDWORD src_b, CDWORD src_c)
{
opcode4(sptr, D3DSIO_CMP, dst, src_a, src_b, src_c);
}
#endif /* _INCLUDED_OPCODES_H */
#ifndef _INCLUDED_REGISTERALLOCATOR_H
#define _INCLUDED_REGISTERALLOCATOR_H
/**
* Simple run-time register allocator that can allocate from within the Vertex or Pixel shader temporary
* register set. Each register maintains a reference count; once the reference count of the register
* becomes zero, the register is free for allocation.
*/
template <int C> class tRegisterAllocator
{
public:
/**
* Basic smart pointer for registers. This class holds the temporary register index or the register code
* in the case of a register that is not a temporary. When this class holds a non-temporary register,
* no smart pointer operations apply.
*/
class Register
{
public:
/**
* Default constructor.
*/
Register(void) : m_Allocator(0), m_Index(-1), m_IsRaw(true)
{
}
/**
* Construct a register by allocating from within the given allocator.
*
* @param alloc Register allocator to allocate from.
*/
Register(tRegisterAllocator<C>& alloc) : m_Allocator(&alloc), m_Index(alloc.GetFreeIndex()), m_IsRaw(false)
{
if (m_Allocator)
m_Allocator->IncRefCount(m_Index);
}
/**
* Construct from a register code. Using this constructor disables all smart pointer operations,
* allowing the register to contain non-temporary registers such as constant registers or shader
* input registers.
*
* @param code Register code (e.g. c[0]).
*/
Register(const int code) : m_Allocator(0), m_Index(code), m_IsRaw(true)
{
}
/**
* Copy constructor, increases reference count of contained register.
*
* @param other Register being copied.
*/
Register(const Register& other) : m_Allocator(other.m_Allocator), m_Index(other.m_Index), m_IsRaw(other.m_IsRaw)
{
if (m_Allocator)
m_Allocator->IncRefCount(m_Index);
}
/**
* Destructor; decreases register reference count.
*/
~Register(void)
{
Relinquish();
}
/**
* Relinquish the contained register reference; decreases the register reference count.
*/
void Relinquish(void)
{
if (m_Index != -1 && m_Allocator)
m_Allocator->DecRefCount(m_Index);
m_Index = -1;
}
/**
* Assignment operator. Increases the register reference count.
*
* @param rhs Register being copied.
*/
Register& operator = (const Register& rhs)
{
m_Allocator = rhs.m_Allocator;
m_Index = rhs.m_Index;
m_IsRaw = rhs.m_IsRaw;
if (m_Allocator)
m_Allocator->IncRefCount(m_Index);
return (*this);
}
/**
* Returns the register code. If the contained register is a temporary, <b>r[x]</b> is returned, where <b>x</b>
* is the register index. Otherwise, the register code the register was constructed with is returned.
*
* @return Register code.
*/
operator int (void) const
{
ASSERT(m_Index != -1);
// A non-controlled raw register code
if (m_IsRaw)
return (m_Index);
// Index into the controlled list of temporary registers
return (sha::r[m_Index]);
}
friend class tRegisterAllocator<C>;
private:
/**
* Used by the allocator to construct after an allocation.
*
* @param alloc Allocator.
* @param index Temporary register index allocated.
*/
Register(tRegisterAllocator<C>& alloc, const int index) : m_Allocator(&alloc), m_Index(index), m_IsRaw(false)
{
if (m_Allocator)
m_Allocator->IncRefCount(m_Index);
}
// Is the register not a temporary?
bool m_IsRaw;
// Register temporary index
int m_Index;
// Register allocator
tRegisterAllocator* m_Allocator;
};
/**
* Default constructor.
*/
tRegisterAllocator(void)
{
// Initially, all registers are free
for (int i = 0; i < C; i++)
m_Registers[i] = 0;
}
/**
* Allocate a temporary register.
*
* @param Temporary register.
*/
Register GetFree(void)
{
// Search for a free register
return (Register(*this, GetFreeIndex()));
}
private:
/**
* Increase the reference count of the given register.
*
* @param reg Register index.
*/
void IncRefCount(const int reg)
{
ASSERT(reg >= 0 && reg < C)(reg)(C).Abort("Register index out of range");
m_Registers[reg]++;
}
/**
* Decrease the reference count of the given register.
*
* @param reg Register index.
*/
void DecRefCount(const int reg)
{
ASSERT(reg >= 0 && reg < C)(reg)(C).Abort("Register index out of range");
ASSERT(m_Registers[reg]).Abort("Register already relinquished");
m_Registers[reg]--;
}
/**
* Return a free register. A free register is one that has a reference count of zero.
*
* @return Free register index.
*/
int GetFreeIndex(void)
{
// Search for a free register
for (int i = 0; i < C; i++)
if (m_Registers[i] == 0)
return (i);
// None left!
ASSERT(false).Abort("No free registers left");
return (-1);
}
// List of registers reference counts
int m_Registers[C];
};
#endif /* _INCLUDED_REGISTERALLOCATOR_H */
#include "Shader_DiffuseSpecularBump.h"
#include "VShaderFragments.h"
using namespace sha;
namespace
{
bool LightDiffuse(const cLightShader& light_shader, const cMaterialShader& material_shader)
{
// Shading method is diffuse and the material can reflect it
return (light_shader.ShadingMethod() == cLightShader::METHOD_DIFFUSE &&
material_shader.ReflectsDiffuse());
}
bool LightSpecular(const cLightShader& light_shader, const cMaterialShader& material_shader)
{
// Shading method is DIFFUSE and the material can reflect specular
// Note that single pass doesn't have an extra specular pass, it just uses diffuse
return (light_shader.ShadingMethod() == cLightShader::METHOD_DIFFUSE &&
material_shader.ReflectsSpecular());
}
// Approximately raise reg to the power of 32.
void ApproxPower32(DWORD reg)
{
// Approximate (N . H) ^ 16 ~= [(N . H) ^ 2 - 0.75] * 4
mad_x4_sat(reg, reg, reg, c[PS_3o4]);
// (N . H) ^ 32
mul_sat(reg, reg, reg);
}
}
void VertexShaders::cDiffuseSpecularBump::ColoursHaveMaterial(Alloc::Register& attenuation)
{
// Modulate by material colour
mov(oD[0], c[VS_MATERIALCOLOUR]);
// Move attenuation into the specular output
if (m_LightShader.LightType() == cLightShader::TYPE_POINT)
mov(oD[1], attenuation | s(w));
}
bool VertexShaders::cDiffuseSpecularBump::LightDiffuse(void) const
{
return (::LightDiffuse(m_LightShader, m_MaterialShader));
}
bool VertexShaders::cDiffuseSpecularBump::LightSpecular(void) const
{
return (::LightSpecular(m_LightShader, m_MaterialShader));
}
void VertexShaders::cDiffuseSpecularBump::Build(void)
{
Alloc::Register light_dir;
// By default, take the input position and normal unmodified
Alloc::Register position(v[DX8Constants::VSHADER_POSITION]);
Alloc::Register normal(v[DX8Constants::VSHADER_NORMAL]);
{
// Do skinning?
RegMat3 skin;
if (m_GeometryShader.MeshType() == DX8Constants::MESHTYPE_GPUSKINNING)
skin = DoSkinning(position, normal);
// Calculate the light vector
light_dir = CalculateLightVector(position, skin);
}
// Transform into clip-space
Transform(oPos, position);
// Setup bump mapping pass using desired method
switch (m_Method)
{
case (DSBM_ILLUMMAP):
{
// Update per-vertex attenuation (ugh)
Alloc::Register attenuation;
if (m_LightShader.LightType() == cLightShader::TYPE_POINT)
attenuation = BuildAttenuation(light_dir);
DoIllumMap(light_dir, attenuation);
break;
}
case (DSBM_INTERP):
{
Normalise(light_dir);
DoInterp(light_dir);
break;
}
case (DSBM_INTERPCUBEMAP):
{
Normalise(light_dir);
DoInterpCubeMap(light_dir);
break;
}
}
// Write diffuse texcoord
if (m_MaterialShader.HasColourMap())
mov(oT[TS_DIFFUSE], v[DX8Constants::VSHADER_COLOURUV]);
// Write bump texcoord (TEMP: Check for specular also)
if (m_MaterialShader.HasBumpMap())
mov(oT[TS_BUMP], v[DX8Constants::VSHADER_COLOURUV]);
}
void PixelShaders::cDiffuseSpecularBump::Build(void)
{
// Sample colour-map
if (m_MaterialShader.HasColourMap())
tex(t[TS_DIFFUSE]);
// Do entire bump mapping pass using desired method
switch (m_Method)
{
case (DSBM_ILLUMMAP):
DoIllumMap();
break;
case (DSBM_INTERP):
DoInterp();
break;
case (DSBM_INTERPCUBEMAP):
DoInterpCubeMap();
break;
}
}
void PixelShaders::cDiffuseSpecularBump::DoIllumMap(void)
{
// Sample bump map
tex(t[TS_BUMP]);
// u = N . L
// v = N . H
// Read tex(u,v) from the lookup table
texm3x2pad(t[TS_DSMAP - 1], t[TS_BUMP] | bx2);
texm3x2tex(t[TS_DSMAP - 0], t[TS_BUMP] | bx2);
// Retrieve the specular term
mul(r[0], t[TS_DSMAP] | s(w), c[PS_ONE] | s(w));
// Multiply specular by specular map
if (m_MaterialShader.SpecularLocation() == DX8Constants::SPECULAR_INBUMPALPHA)
mul(r[0], r[0] | s(w), t[TS_BUMP] | s(w));
else if (m_MaterialShader.SpecularLocation() == DX8Constants::SPECULAR_INTEXALPHA)
mul(r[0], r[0] | s(w), t[TS_DIFFUSE] | s(w));
// Modulate texture by incoming material colour = TMC
if (m_MaterialShader.HasColourMap())
mul(r[1], t[TS_DIFFUSE], v[0]);
else
mov(r[1], v[0]);
// Modulate diffuse by TMC, add specular over the top
if (LightSpecular(m_LightShader, m_MaterialShader))
mad(r[0] | x | y | z, t[TS_DSMAP], r[1], r[0]);
else
mul(r[0] | x | y | z, t[TS_DSMAP], r[1]);
// Set alpha channel to ONE
coissue();
mov(r[0].w, c[PS_ONE] | s(w));
// Modulate light contribution by attenuation
if (m_LightShader.LightType() == cLightShader::TYPE_POINT)
mul(r[0] | x | y | z, r[0], v[1]);
// Modulate light colour
mul(r[0] | x | y | z, r[0], c[PS_LIGHTCOLOUR]);
DoAlpha();
}
void PixelShaders::cDiffuseSpecularBump::DoInterp(void)
{
// Sample bump map
tex(t[TS_BUMP]);
// N . L
dp3_sat(r[0], t[TS_BUMP] | bx2, v[0] | bx2);
if (LightSpecular(m_LightShader, m_MaterialShader))
{
// Read the input half-vector and normalise it
dp3(r[1], v[1] | bx2, v[1] | bx2);
mad(r[1], v[1] | bias, r[1] | comp, v[1] | bx2);
// (N . H) ^ 32
dp3_sat(r[1], t[TS_BUMP] | bx2, r[1]);
ApproxPower32(r[1]);
// Modulate diffuse by texture and add specular
mad_sat(r[0], r[0], t[TS_DIFFUSE], r[1]);
}
else
{
// Just modulate diffuse by texture
mul(r[0], r[0], t[TS_DIFFUSE]);
}
// Modulate light colour
mul(r[0] | x | y | z, r[0], c[PS_LIGHTCOLOUR]);
DoAlpha();
}
void PixelShaders::cDiffuseSpecularBump::DoInterpCubeMap(void)
{
// Sample bump map
tex(t[TS_BUMP]);
// Sample normalisation cube maps
tex(t[TS_DIFFNORM]);
tex(t[TS_SPECNORM]);
// N . L
dp3_sat(r[0], t[TS_BUMP] | bx2, t[TS_DIFFNORM] | bx2);
if (LightSpecular(m_LightShader, m_MaterialShader))
{
// (N . H) ^ 32
dp3_sat(r[1], t[TS_BUMP] | bx2, t[TS_SPECNORM] | bx2);
ApproxPower32(r[1]);
// Multiply specular by specular map
if (m_MaterialShader.SpecularLocation() == DX8Constants::SPECULAR_INBUMPALPHA)
mul(r[1], r[1] | s(w), t[TS_BUMP] | s(w));
else if (m_MaterialShader.SpecularLocation() == DX8Constants::SPECULAR_INTEXALPHA)
mul(r[1], r[1] | s(w), t[TS_DIFFUSE] | s(w));
// Modulate diffuse by texture and add specular
mad_sat(r[0], r[0], t[TS_DIFFUSE], r[1]);
}
else
{
// Just modulate diffuse by texture
mul(r[0], r[0], t[TS_DIFFUSE]);
}
// Modulate light colour
mul(r[0] | x | y | z, r[0], c[PS_LIGHTCOLOUR]);
DoAlpha();
}
// COMPILE-TIME SHADER ASSEMBLER
#ifndef _INCLUDED_ASSEMBLER_H
#define _INCLUDED_ASSEMBLER_H
#ifndef _INCLUDED_D3D_H
#include "D3D.h"
#endif
// Where do you NOT want to go today?
#undef min
#undef max
// inline....
#define SA_INLINE __forceinline
// shADER aSSEMBLER
namespace sha
{
// Just got annoying typing "const DWORD" all over the place
typedef const DWORD CDWORD;
// Two types of modification:
// Masking: destination, o*, rn
// ---
// Allows you to select which parts of the register get transferred from source to dest.
// mov r0.x, r1 -- only moves .x
// mov r0.xw, r1 -- only moves .xw
//
// Swizzling: source, vn, cn, rn
// ---
// Defines where parts move from source to dest.
// mov r0, r1.xywz -- moves r1.w into r0.z and r1.z into r0.w
// mov r0, r1.wxyz -- rotates all components right by 1
// mov r0, r1.xxxx -- moves r1.x into all (unmasked) components of r0
// Individual register components
enum Component
{
x = D3DSP_WRITEMASK_0,
y = D3DSP_WRITEMASK_1,
z = D3DSP_WRITEMASK_2,
w = D3DSP_WRITEMASK_3,
all = x | y | z | w
};
// Destination parameter modifiers
enum DestMod { saturate = D3DSPDM_SATURATE };
// Source parameter modifiers
enum SrcMod
{
neg = D3DSPSM_NEG, // -r0
bias = D3DSPSM_BIAS, // r0_bias
biasneg = D3DSPSM_BIASNEG, // ???
sign = D3DSPSM_SIGN, // ???
signneg = D3DSPSM_SIGNNEG, // ???
comp = D3DSPSM_COMP, // 1-r0
x2 = D3DSPSM_X2, // r0_x2 (ps.1.4)
bx2 = D3DSPSM_SIGN, // r0_bx2
dz = D3DSPSM_DZ, // ps.1.4
dw = D3DSPSM_DW // ps.1.4
};
// Converts the D3DSP_WRITEMASK_n into an index for the swizzle code, avoiding any
// branching in the process
SA_INLINE CDWORD MaskToSwizzle(CDWORD mask)
{
return (
(((mask >> 16) & 1) >> 0) * 0 +
(((mask >> 16) & 2) >> 1) * 1 +
(((mask >> 16) & 4) >> 2) * 2 +
(((mask >> 16) & 8) >> 3) * 3);
}
struct ComponentStruct
{
SA_INLINE ComponentStruct(CDWORD _op) :
op(_op), x(op | sha::x), y(op | sha::y), z(op | sha::z), w(op | sha::w) { }
SA_INLINE operator CDWORD (void) const { return (op); }
CDWORD op;
CDWORD x;
CDWORD y;
CDWORD z;
CDWORD w;
};
// Class for mapping registers and their indices to a register op
// This is quite cool because it allows any number of any registers (eg. GF3/4 R8500 differences)
template <int REG>
struct RegHerring
{
SA_INLINE ComponentStruct operator [] (CDWORD index) const { return (REG | index); }
SA_INLINE operator CDWORD (void) const { return (REG); }
};
// Constructs the swizzle code
// Bit of a cheat this because it uses the definitions of D3DVS_X_X and the like to work
// and the internals theoretically *could* be changed in a later version of DX
SA_INLINE CDWORD s(const Component a, const Component b, const Component c, const Component d)
{
return (
(MaskToSwizzle(a) << (D3DVS_SWIZZLE_SHIFT + 0)) |
(MaskToSwizzle(b) << (D3DVS_SWIZZLE_SHIFT + 2)) |
(MaskToSwizzle(c) << (D3DVS_SWIZZLE_SHIFT + 4)) |
(MaskToSwizzle(d) << (D3DVS_SWIZZLE_SHIFT + 6)) |
// Set this here to flag that the user wants to set the swizzle code
0x80000000);
}
// Overloads for opcodes that take one partially complete swizzle codes
SA_INLINE CDWORD s(const Component a) { return (s(a, a, a, a)); }
SA_INLINE CDWORD s(const Component a, const Component b) { return (s(a, b, b, b)); }
SA_INLINE CDWORD s(const Component a, const Component b, Component c) { return (s(a, b, c, c)); }
// All vertex shader registers
static const RegHerring<D3DSPR_TEMP> r;
static const RegHerring<D3DSPR_INPUT> v;
static const RegHerring<D3DSPR_CONST> c;
static const RegHerring<D3DSPR_ADDR> a;
static const RegHerring<D3DSPR_TEXTURE> t;
static const RegHerring<D3DSPR_ATTROUT> oD;
static const RegHerring<D3DSPR_RASTOUT> oPos;
static const RegHerring<D3DSPR_TEXCRDOUT> oT;
// This will set the high bit and also set the write mask to all if no write mask
// has been manually set
SA_INLINE CDWORD MakeDest(CDWORD d)
{
return (((d & all) == 0 ? d | all : d) | 0x80000000);
}
// This checks to see if the user has already set the swizzle code before ORing
// with the "no swizzle" code if they haven't, and then setting the high bit
SA_INLINE CDWORD MakeSource(CDWORD d)
{
return ((d & 0x80000000) ? d : d | D3DVS_NOSWIZZLE | 0x80000000);
}
extern DWORD do_coissue;
SA_INLINE void coissue(void)
{
do_coissue = D3DSI_COISSUE;
}
// Adding this to a register addressing is the same as a bitwise OR
static const int addr0 = D3DVS_ADDRMODE_RELATIVE;
// ===========================================================================================
// Functions which automate the tedious process of constructing opcodes
extern DWORD nb_instructions;
SA_INLINE void opcode0(DWORD *& ptr, CDWORD opcode)
{
*ptr++ = opcode | do_coissue;
nb_instructions++;
do_coissue = 0;
}
SA_INLINE void opcode1_dst(DWORD *& ptr, CDWORD opcode, CDWORD dst)
{
ptr[0] = opcode | do_coissue;
ptr[1] = MakeDest(dst);
ptr += 2;
nb_instructions++;
do_coissue = 0;
}
SA_INLINE void opcode1_src(DWORD *& ptr, CDWORD opcode, CDWORD src)
{
ptr[0] = opcode | do_coissue;
ptr[1] = MakeSource(src);
ptr += 2;
nb_instructions++;
do_coissue = 0;
}
SA_INLINE void opcode2(DWORD *& ptr, CDWORD opcode, CDWORD dst, CDWORD src)
{
ptr[0] = opcode | do_coissue;
ptr[1] = MakeDest(dst);
ptr[2] = MakeSource(src);
ptr += 3;
nb_instructions++;
do_coissue = 0;
}
SA_INLINE void opcode3(DWORD *& ptr, CDWORD opcode, CDWORD dst, CDWORD src_a, CDWORD src_b)
{
ptr[0] = opcode | do_coissue;
ptr[1] = MakeDest(dst);
ptr[2] = MakeSource(src_a);
ptr[3] = MakeSource(src_b);
ptr += 4;
nb_instructions++;
do_coissue = 0;
}
SA_INLINE void opcode4(DWORD *& ptr, CDWORD opcode, CDWORD dst, CDWORD src_a, CDWORD src_b, CDWORD src_c)
{
ptr[0] = opcode | do_coissue;
ptr[1] = MakeDest(dst);
ptr[2] = MakeSource(src_a);
ptr[3] = MakeSource(src_b);
ptr[4] = MakeSource(src_c);
ptr += 5;
nb_instructions++;
do_coissue = 0;
}
SA_INLINE void opcode5(DWORD *& ptr, CDWORD opcode, CDWORD dst, CDWORD src_a, CDWORD src_b, CDWORD src_c, CDWORD src_d)
{
ptr[0] = opcode | do_coissue;
ptr[1] = MakeDest(dst);
ptr[2] = MakeSource(src_a);
ptr[3] = MakeSource(src_b);
ptr[4] = MakeSource(src_c);
ptr[5] = MakeSource(src_d);
ptr += 6;
nb_instructions++;
do_coissue = 0;
}
// ===========================================================================================
// Include in this namespace
#include "Opcodes.h"
}; // End namespace sha
// Don't need it anymore
#undef SA_INLINE
#endif /* _INCLUDED_ASSEMBLER_H */
#include "ShaderConfig.h"
#include <cstring>
void cLightShader::EvaluateCode(void) const
{
// Merge shading codes together
m_ShaderCode = 0;
m_ShaderCode |= (((int)m_LightType & 3) << cShaderConfig::LS_LightType);
m_ShaderCode |= (((int)m_ShadingMethod & 7) << cShaderConfig::LS_ShadingMethod);
m_ShaderCode |= (m_DoubleSpot << cShaderConfig::LS_DoubleSpot);
}
void cMaterialShader::EvaluateCode(void) const
{
// Merge shading codes together
m_ShaderCode = 0;
m_ShaderCode |= (m_HasColourMap << cShaderConfig::MS_HasColourMap);
m_ShaderCode |= (m_HasBumpMap << cShaderConfig::MS_HasBumpMap);
m_ShaderCode |= (m_HasLuminousityMap << cShaderConfig::MS_HasLuminousityMap);
m_ShaderCode |= (m_HasAlpha << cShaderConfig::MS_HasAlpha);
m_ShaderCode |= (m_HasTexAlpha << cShaderConfig::MS_HasTexAlpha);
m_ShaderCode |= (m_ReflectsDiffuse << cShaderConfig::MS_ReflectsDiffuse);
m_ShaderCode |= (m_ReflectsSpecular << cShaderConfig::MS_ReflectsSpecular);
m_ShaderCode |= (m_Emissive << cShaderConfig::MS_Emissive);
m_ShaderCode |= (((int)m_SpecularLocation & 3) << cShaderConfig::MS_SpecularLocation);
}
void cGeometryShader::EvaluateCode(void) const
{
// Merge shading codes together
m_ShaderCode = 0;
m_ShaderCode |= (((int)m_Type & 3) << cShaderConfig::GS_Type);
m_ShaderCode |= (m_IsVertexColoured << cShaderConfig::GS_IsVertexColoured);
m_ShaderCode |= (m_HasAlpha << cShaderConfig::GS_HasAlpha);
m_ShaderCode |= (m_HasNormals << cShaderConfig::GS_HasNormals);
m_ShaderCode |= (((int)m_NMapType & 3) << cShaderConfig::GS_NMapType);
m_ShaderCode |= (((int)m_MeshType & 3) << cShaderConfig::GS_MeshType);
}
cShaderConfig::cShaderConfig(const cLightShader& ls, const cMaterialShader& ms, const cGeometryShader& gs) :
m_LightShader(ls),
m_MaterialShader(ms),
m_GeometryShader(gs)
{
// Merge all shader codes to form the final one
m_ShaderCode = m_LightShader.GetCode() | m_MaterialShader.GetCode() | m_GeometryShader.GetCode();
}
void cShaderConfig::EvaluateCode(void)
{
m_LightShader.EvaluateCode();
m_MaterialShader.EvaluateCode();
m_GeometryShader.EvaluateCode();
m_ShaderCode = m_LightShader.GetCode() | m_MaterialShader.GetCode() | m_GeometryShader.GetCode();
}
#ifndef _INCLUDED_SHADERCONFIG_H
#define _INCLUDED_SHADERCONFIG_H
#pragma pack(push, 1)
// Property class for shader config values
#define SHC_PROPERTY(type, name) \
public: \
const type name(void) const { return (m_##name); } \
private: \
type m_##name; \
public:
class cLightShader
{
public:
// Light type
enum Type
{
TYPE_NONE = 0,
TYPE_DIRECTION,
TYPE_POINT,
TYPE_POINTPROJECTION
};
// Shading method
enum Method
{
METHOD_AMBIENT = 0,
METHOD_DIFFUSE,
METHOD_SPECULAR,
METHOD_LUMINOUSITY,
// TEMP: Need to think of some way to let shaders be as many passes as they want so the render
// look is something like:
//
// shader = shadermanager.getshader;
// for (int i = 0; i < shader.nb_passes; i++)
// shader.getpass.set
// drawbatch
//
METHOD_ATTENUATION
};
// Default constructor
cLightShader(void) : m_ShaderCode(-1), m_LightType(TYPE_NONE), m_ShadingMethod(METHOD_AMBIENT), m_DoubleSpot(false) { }
// Construct with light type and ambient shading
cLightShader(const Type type) : m_ShaderCode(-1), m_LightType(type), m_ShadingMethod(METHOD_AMBIENT), m_DoubleSpot(false) { }
// Lighting properties
SHC_PROPERTY(Type, LightType);
SHC_PROPERTY(Method, ShadingMethod);
SHC_PROPERTY(bool, DoubleSpot);
// Set the shading method for different rendering passes
void SetShadingMethod(const Method method)
{
if (method == METHOD_AMBIENT || method == METHOD_LUMINOUSITY)
m_LightType = TYPE_NONE;
m_ShadingMethod = method;
}
// Force evaluation of shader code
void EvaluateCode(void) const;
// Get partial shader code
unsigned int GetCode(void) const
{
return (m_ShaderCode);
}
// The only class capable of writing
friend class cDX8Light;
private:
// Partial shader code
mutable unsigned int m_ShaderCode;
};
class cMaterialShader
{
public:
// Default constructor
cMaterialShader(void) :
m_HasColourMap(false),
m_HasBumpMap(false),
m_HasLuminousityMap(false),
m_HasAlpha(false),
m_HasTexAlpha(false),
m_ReflectsDiffuse(false),
m_ReflectsSpecular(false),
m_Emissive(false),
m_SpecularLocation(DX8Constants::SPECULAR_NONE)
{
}
// Material properties
SHC_PROPERTY(bool, HasColourMap);
SHC_PROPERTY(bool, HasBumpMap);
SHC_PROPERTY(bool, HasLuminousityMap);
SHC_PROPERTY(bool, HasAlpha);
SHC_PROPERTY(bool, HasTexAlpha);
SHC_PROPERTY(bool, ReflectsDiffuse);
SHC_PROPERTY(bool, ReflectsSpecular);
SHC_PROPERTY(bool, Emissive);
SHC_PROPERTY(DX8Constants::SpecularOptimise, SpecularLocation);
// Force evaluation of shader code
void EvaluateCode(void) const;
// Get partial shader code
unsigned int GetCode(void) const
{
return (m_ShaderCode);
}
bool IsTransparent(void) const
{
return (HasAlpha() || HasTexAlpha());
}
// The only classes capable of writing
friend class cDX8Material;
friend class cDX8RuntimeMesh;
private:
// Partial shader code
mutable unsigned int m_ShaderCode;
};
class cGeometryShader
{
public:
enum GeomType
{
TYPE_EDGEEXTRUDE = 0,
TYPE_SPRITE2D,
TYPE_SPRITE3D,
TYPE_MESH,
TYPE_PARTICLEBILLBOARD,
};
// Default constructor
cGeometryShader(void) :
m_Type(TYPE_EDGEEXTRUDE), m_IsVertexColoured(false), m_HasNormals(true), m_NMapType((DX8Constants::NormalMapType)0),
m_MeshType((DX8Constants::MeshType)0), m_HasAlpha(false) { }
// Construct with geomtype only
cGeometryShader(const GeomType gt) :
m_Type(gt), m_IsVertexColoured(false), m_HasNormals(true), m_NMapType((DX8Constants::NormalMapType)0),
m_MeshType((DX8Constants::MeshType)0), m_HasAlpha(false) { }
// Construct with per-vertex attributes
cGeometryShader(const GeomType gt, const bool is_vcoloured, const bool has_normals) :
m_Type(gt), m_IsVertexColoured(is_vcoloured), m_HasNormals(has_normals), m_NMapType((DX8Constants::NormalMapType)0),
m_MeshType((DX8Constants::MeshType)0), m_HasAlpha(false) { }
// Type of geometry to be constructed
SHC_PROPERTY(GeomType, Type);
// Properties for the mesh type
SHC_PROPERTY(bool, IsVertexColoured);
SHC_PROPERTY(bool, HasNormals);
SHC_PROPERTY(bool, HasAlpha);
SHC_PROPERTY(DX8Constants::NormalMapType, NMapType);
SHC_PROPERTY(DX8Constants::MeshType, MeshType);
// Force evaluation of shader code
void EvaluateCode(void) const;
// Get partial shader code
unsigned int GetCode(void) const
{
return (m_ShaderCode);
}
bool IsTransparent(void) const
{
return (HasAlpha());
}
void SetTransparent(bool t)
{
m_HasAlpha = t;
}
// The only class capable of writing
friend class cDX8Mesh;
friend class cDX8RuntimeMesh;
private:
// Partial shader code
mutable unsigned int m_ShaderCode;
};
class cShaderConfig
{
public:
enum BitPositions
{
LS_LightType = 0,
LS_ShadingMethod = 2,
LS_DoubleSpot = 5,
MS_HasColourMap = 6,
MS_HasBumpMap = 7,
MS_HasLuminousityMap = 8,
MS_HasAlpha = 9,
MS_HasTexAlpha = 10,
MS_ReflectsDiffuse = 11,
MS_ReflectsSpecular = 12,
MS_Emissive = 13,
MS_SpecularLocation = 14,
GS_Type = 16,
GS_IsVertexColoured = 19,
GS_HasNormals = 20,
GS_NMapType = 21,
GS_MeshType = 23,
GS_HasAlpha = 24,
};
// Constructor
cShaderConfig(const cLightShader& ls, const cMaterialShader& ms, const cGeometryShader& gs);
// Force evaluation of full shader code
void EvaluateCode(void);
// Get the partial shader code
unsigned int GetCode(void) const
{
return (m_ShaderCode);
}
// Accessors for shader parts
const cLightShader& GetLightShader(void) const { return (m_LightShader); }
const cMaterialShader& GetMaterialShader(void) const { return (m_MaterialShader); }
const cGeometryShader& GetGeometryShader(void) const { return (m_GeometryShader); }
private:
// Shader hash code
unsigned int m_ShaderCode;
// Shaders to combine
const cLightShader& m_LightShader;
const cMaterialShader& m_MaterialShader;
const cGeometryShader& m_GeometryShader;
};
#undef SHC_PROPERTY
#pragma pack(pop)
#endif /* _INCLUDED_SHADERCONFIG_H */
namespace
{
struct Shader
{
// Initialise default values
Shader(void) :
decl_index(-1),
code(-1),
nb_instructions(0),
shader(0),
next(0)
{
}
// Type of shader
cShaderConstant::Type type;
// Shader declaration index
int decl_index;
// Shader hash code
unsigned int code;
// Number of instructions in the shader
DWORD nb_instructions;
// D3D returned shader handle
DWORD shader;
// Pointer to the next shader in the bucket
Shader* next;
};
// Shader allocator
tSmallAllocator<Shader>* g_ShaderAlloc;
// Shader hash table
const int MAX_NB_DECLS = 512;
Shader* g_ShaderHash[MAX_NB_DECLS];
// D3D device
cD3DDeviceWrapper8* g_Device;
unsigned int Hash(unsigned int h)
{
// 32-bit integer mix function
h += (h << 12);
h ^= (h >> 22);
h += (h << 4);
h ^= (h >> 9);
h += (h << 10);
h ^= (h >> 2);
h += (h << 7);
h ^= (h >> 12);
h &= (MAX_NB_DECLS - 1);
return (h);
}
void AddShaderToHash(Shader* shader_ptr)
{
// Hash by shader code
unsigned int h = Hash(shader_ptr->code) & (MAX_NB_DECLS - 1);
// If the space is empty, just add it
if (g_ShaderHash[h] == 0)
{
g_ShaderHash[h] = shader_ptr;
}
// Else insert at the beinning of the bucket
else
{
shader_ptr->next = g_ShaderHash[h];
g_ShaderHash[h] = shader_ptr;
}
}
Shader* FindShader(const int decl_index, const cShaderConfig& config)
{
// Hash by shader code
int shader_code = config.GetCode();
unsigned int h = Hash(shader_code) & (MAX_NB_DECLS - 1);
// Found some shaders?
if (g_ShaderHash[h])
{
// Search for the matching code
for (Shader* cur_shader = g_ShaderHash[h]; cur_shader; cur_shader = cur_shader->next)
if (cur_shader->code == shader_code && cur_shader->decl_index == decl_index)
return (cur_shader);
}
return (0);
}
DWORD* BuildVertexShader(const cShaderConfig& config, DWORD& nb_instructions)
{
static DWORD tokens[512];
sha::BeginVS(tokens);
{
cShader* shader_ptr = 0;
// Create the appropriate vertex shader
switch (config.GetGeometryShader().Type())
{
// Basic sprites and shadow silouhette extrusion
case (cGeometryShader::TYPE_SPRITE2D):
shader_ptr = new VertexShaders::cSprite(config, true);
break;
case (cGeometryShader::TYPE_SPRITE3D):
shader_ptr = new VertexShaders::cSprite(config, false);
break;
case (cGeometryShader::TYPE_EDGEEXTRUDE):
shader_ptr = new VertexShaders::cEdgeExtrude(config);
break;
case (cGeometryShader::TYPE_PARTICLEBILLBOARD):
shader_ptr = new VertexShaders::cPrtBillBoard(config);
break;
// Mesh surfaces
case (cGeometryShader::TYPE_MESH):
{
switch (config.GetLightShader().ShadingMethod())
{
// Ambient and luminous passes
case (cLightShader::METHOD_AMBIENT):
shader_ptr = new VertexShaders::cAmbient(config);
break;
case (cLightShader::METHOD_LUMINOUSITY):
shader_ptr = new VertexShaders::cLuminousity(config);
break;
// Combined diffuse and specular passes
case (cLightShader::METHOD_DIFFUSE):
if (config.GetLightShader().LightType() == cLightShader::TYPE_POINTPROJECTION)
shader_ptr = new VertexShaders::cDiffuseBump(config);
// Bump mapping?
else if (config.GetMaterialShader().HasBumpMap())
{
if (config.GetGeometryShader().NMapType() == DX8Constants::NORMALMAP_OBJECTSPACE)
shader_ptr = new VertexShaders::cDiffuseSpecularBumpOS(config, DSBM_INTERPCUBEMAP);
else
shader_ptr = new VertexShaders::cDiffuseSpecularBumpTS(config, DSBM_INTERPCUBEMAP);
}
// Fall back to per-vertex lighting
else
shader_ptr = new VertexShaders::cDiffuseSpecularPV(config);
break;
case (cLightShader::METHOD_SPECULAR):
if (config.GetLightShader().LightType() == cLightShader::TYPE_POINTPROJECTION)
shader_ptr = new VertexShaders::cSpecularBump(config);
break;
// Temp location for attenuation passes
case (cLightShader::METHOD_ATTENUATION):
shader_ptr = new VertexShaders::cAttenuation(config);
break;
}
}
}
// Build her up!
shader_ptr->Build();
delete shader_ptr;
}
sha::EndVS();
cDebugger::UserOutput("Don", "Created vertex shader : %d\n", sha::nb_instructions);
return (tokens);
}
DWORD* BuildPixelShader(const cShaderConfig& config, DWORD& nb_instructions)
{
static DWORD tokens[512];
sha::BeginPS(tokens);
{
cShader* shader_ptr = 0;
// Create the appropriate pixel shader
switch (config.GetGeometryShader().Type())
{
// Basic sprites and shadow silouhette extrusion
case (cGeometryShader::TYPE_SPRITE2D):
case (cGeometryShader::TYPE_SPRITE3D):
shader_ptr = new PixelShaders::cSprite(config);
break;
case (cGeometryShader::TYPE_EDGEEXTRUDE):
shader_ptr = new PixelShaders::cEdgeExtrude(config);
break;
case (cGeometryShader::TYPE_PARTICLEBILLBOARD):
shader_ptr = new PixelShaders::cPrtBillBoard(config);
break;
// Mesh surfaces
case (cGeometryShader::TYPE_MESH):
{
switch (config.GetLightShader().ShadingMethod())
{
// Ambient and luminous passes
case (cLightShader::METHOD_AMBIENT):
shader_ptr = new PixelShaders::cAmbient(config);
break;
case (cLightShader::METHOD_LUMINOUSITY):
shader_ptr = new PixelShaders::cLuminousity(config);
break;
// Combined diffuse and specular passes
case (cLightShader::METHOD_DIFFUSE):
if (config.GetLightShader().LightType() == cLightShader::TYPE_POINTPROJECTION)
shader_ptr = new PixelShaders::cDiffuseBump(config);
// Bump mapping?
else if (config.GetMaterialShader().HasBumpMap())
shader_ptr = new PixelShaders::cDiffuseSpecularBump(config, DSBM_INTERPCUBEMAP);
// Fall back to per-vertex lighting
else
shader_ptr = new PixelShaders::cDiffuseSpecularPV(config);
break;
case (cLightShader::METHOD_SPECULAR):
if (config.GetLightShader().LightType() == cLightShader::TYPE_POINTPROJECTION)
shader_ptr = new PixelShaders::cSpecularBump(config);
break;
// Temp location for attenuation passes
case (cLightShader::METHOD_ATTENUATION):
shader_ptr = new PixelShaders::cAttenuation(config);
break;
}
}
}
// Build her up!
shader_ptr->Build();
delete shader_ptr;
}
sha::EndPS();
cDebugger::UserOutput("Don", "Created pixel shader : %d (%d, %d, %d, %d)\n", sha::nb_instructions, config.GetCode(),
config.GetLightShader().GetCode(), config.GetMaterialShader().GetCode(), config.GetGeometryShader().GetCode());
return (tokens);
}
}
void ShaderManager::Initialise(cD3DDeviceWrapper8* device_ptr, const int max_nb_shaders)
{
// Create shader allocator
g_ShaderAlloc = new tSmallAllocator<Shader>(max_nb_shaders, GetComponentHeap());
// Clear the hash table
memset(g_ShaderHash, 0, sizeof(g_ShaderHash));
g_Device = device_ptr;
}
void ShaderManager::Shutdown(void)
{
// Release all D3D shader resources
for (int i = 0; i < MAX_NB_DECLS; i++)
{
for (Shader* cur_shader = g_ShaderHash[i]; cur_shader; cur_shader = cur_shader->next)
{
if (cur_shader->type == cShaderConstant::VS)
g_Device->DeleteVertexShader(cur_shader->shader);
else if (cur_shader->type == cShaderConstant::PS)
g_Device->DeletePixelShader(cur_shader->shader);
}
}
// Release shader allocator
delete g_ShaderAlloc;
}
DWORD ShaderManager::GetVertexShader(const int decl_index, const cShaderConfig& config)
{
// First attempt to find the shader
Shader* shader_ptr = FindShader(decl_index, config);
if (shader_ptr == 0)
{
// Allocate me up a new one
shader_ptr = new (*g_ShaderAlloc) Shader;
shader_ptr->decl_index = decl_index;
shader_ptr->code = config.GetCode();
// Add the shader to the hash table
AddShaderToHash(shader_ptr);
// Build her up
DWORD* tokens = BuildVertexShader(config, shader_ptr->nb_instructions);
// Create the D3D shader
DWORD* decl_ptr = DeclPool::GetDeclaration(decl_index);
shader_ptr->shader = g_Device->CreateVertexShader(decl_ptr, tokens, 0);
shader_ptr->type = cShaderConstant::VS;
}
return (shader_ptr->shader);
}
DWORD ShaderManager::GetPixelShader(const cShaderConfig& config)
{
// First attempt to find the shader
Shader* shader_ptr = FindShader(-1, config);
if (shader_ptr == 0)
{
// Allocate me up a new one
shader_ptr = new (*g_ShaderAlloc) Shader;
shader_ptr->code = config.GetCode();
shader_ptr->next = 0;
// Add the shader to the hash table
AddShaderToHash(shader_ptr);
// Build her up
DWORD* tokens = BuildPixelShader(config, shader_ptr->nb_instructions);
// Create the D3D shader
shader_ptr->shader = g_Device->CreatePixelShader(tokens);
shader_ptr->type = cShaderConstant::PS;
}
return (shader_ptr->shader);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment