#define __MOJOSHADER_INTERNAL__ 1
#include "profiles/mojoshader_profile.h"
static void free_reglist(MOJOSHADER_free f, void *d, RegisterList *item)
{
while (item != NULL)
{
RegisterList *next = item->next;
f(item, d);
item = next;
} }
static inline const RegisterList *reglist_exists(RegisterList *prev,
const RegisterType regtype,
const int regnum)
{
return (reglist_find(prev, regtype, regnum));
}
static inline int register_was_written(Context *ctx, const RegisterType rtype,
const int regnum)
{
RegisterList *reg = reglist_find(&ctx->used_registers, rtype, regnum);
return (reg && reg->written);
}
static inline int get_defined_register(Context *ctx, const RegisterType rtype,
const int regnum)
{
return (reglist_exists(&ctx->defined_registers, rtype, regnum) != NULL);
}
static void add_attribute_register(Context *ctx, const RegisterType rtype,
const int regnum, const MOJOSHADER_usage usage,
const int index, const int writemask, int flags)
{
RegisterList *item = reglist_insert(ctx, &ctx->attributes, rtype, regnum);
item->usage = usage;
item->index = index;
item->writemask = writemask;
item->misc = flags;
if ((rtype == REG_TYPE_OUTPUT) && (usage == MOJOSHADER_USAGE_POINTSIZE))
ctx->uses_pointsize = 1; else if ((rtype == REG_TYPE_OUTPUT) && (usage == MOJOSHADER_USAGE_FOG))
ctx->uses_fog = 1; }
static inline TextureType cvtMojoToD3DSamplerType(const MOJOSHADER_samplerType type)
{
return (TextureType) (((int) type) + 2);
}
static inline MOJOSHADER_samplerType cvtD3DToMojoSamplerType(const TextureType type)
{
return (MOJOSHADER_samplerType) (((int) type) - 2);
}
static inline void add_sampler(Context *ctx, const int regnum,
TextureType ttype, const int texbem)
{
const RegisterType rtype = REG_TYPE_SAMPLER;
RegisterList *item = reglist_insert(ctx, &ctx->samplers, rtype, regnum);
if (ctx->samplermap != NULL)
{
unsigned int i;
for (i = 0; i < ctx->samplermap_count; i++)
{
if (ctx->samplermap[i].index == regnum)
{
ttype = cvtMojoToD3DSamplerType(ctx->samplermap[i].type);
break;
} } }
item->index = (int) ttype;
item->misc |= texbem;
}
static inline void adjust_token_position(Context *ctx, const int incr)
{
ctx->tokens += incr;
ctx->tokencount -= incr;
ctx->current_position += incr * sizeof (uint32);
}
#define PREDECLARE_PROFILE(prof) \
void emit_##prof##_start(Context *ctx, const char *profilestr); \
void emit_##prof##_end(Context *ctx); \
void emit_##prof##_phase(Context *ctx); \
void emit_##prof##_finalize(Context *ctx); \
void emit_##prof##_global(Context *ctx, RegisterType regtype, int regnum);\
void emit_##prof##_array(Context *ctx, VariableList *var); \
void emit_##prof##_const_array(Context *ctx, const ConstantsList *clist, \
int base, int size); \
void emit_##prof##_uniform(Context *ctx, RegisterType regtype, int regnum,\
const VariableList *var); \
void emit_##prof##_sampler(Context *ctx, int stage, TextureType ttype, \
int tb); \
void emit_##prof##_attribute(Context *ctx, RegisterType regtype, \
int regnum, MOJOSHADER_usage usage, \
int index, int wmask, int flags); \
void emit_##prof##_NOP(Context *ctx); \
void emit_##prof##_MOV(Context *ctx); \
void emit_##prof##_ADD(Context *ctx); \
void emit_##prof##_SUB(Context *ctx); \
void emit_##prof##_MAD(Context *ctx); \
void emit_##prof##_MUL(Context *ctx); \
void emit_##prof##_RCP(Context *ctx); \
void emit_##prof##_RSQ(Context *ctx); \
void emit_##prof##_DP3(Context *ctx); \
void emit_##prof##_DP4(Context *ctx); \
void emit_##prof##_MIN(Context *ctx); \
void emit_##prof##_MAX(Context *ctx); \
void emit_##prof##_SLT(Context *ctx); \
void emit_##prof##_SGE(Context *ctx); \
void emit_##prof##_EXP(Context *ctx); \
void emit_##prof##_LOG(Context *ctx); \
void emit_##prof##_LIT(Context *ctx); \
void emit_##prof##_DST(Context *ctx); \
void emit_##prof##_LRP(Context *ctx); \
void emit_##prof##_FRC(Context *ctx); \
void emit_##prof##_M4X4(Context *ctx); \
void emit_##prof##_M4X3(Context *ctx); \
void emit_##prof##_M3X4(Context *ctx); \
void emit_##prof##_M3X3(Context *ctx); \
void emit_##prof##_M3X2(Context *ctx); \
void emit_##prof##_CALL(Context *ctx); \
void emit_##prof##_CALLNZ(Context *ctx); \
void emit_##prof##_LOOP(Context *ctx); \
void emit_##prof##_ENDLOOP(Context *ctx); \
void emit_##prof##_LABEL(Context *ctx); \
void emit_##prof##_DCL(Context *ctx); \
void emit_##prof##_POW(Context *ctx); \
void emit_##prof##_CRS(Context *ctx); \
void emit_##prof##_SGN(Context *ctx); \
void emit_##prof##_ABS(Context *ctx); \
void emit_##prof##_NRM(Context *ctx); \
void emit_##prof##_SINCOS(Context *ctx); \
void emit_##prof##_REP(Context *ctx); \
void emit_##prof##_ENDREP(Context *ctx); \
void emit_##prof##_IF(Context *ctx); \
void emit_##prof##_IFC(Context *ctx); \
void emit_##prof##_ELSE(Context *ctx); \
void emit_##prof##_ENDIF(Context *ctx); \
void emit_##prof##_BREAK(Context *ctx); \
void emit_##prof##_BREAKC(Context *ctx); \
void emit_##prof##_MOVA(Context *ctx); \
void emit_##prof##_DEFB(Context *ctx); \
void emit_##prof##_DEFI(Context *ctx); \
void emit_##prof##_TEXCRD(Context *ctx); \
void emit_##prof##_TEXKILL(Context *ctx); \
void emit_##prof##_TEXLD(Context *ctx); \
void emit_##prof##_TEXBEM(Context *ctx); \
void emit_##prof##_TEXBEML(Context *ctx); \
void emit_##prof##_TEXREG2AR(Context *ctx); \
void emit_##prof##_TEXREG2GB(Context *ctx); \
void emit_##prof##_TEXM3X2PAD(Context *ctx); \
void emit_##prof##_TEXM3X2TEX(Context *ctx); \
void emit_##prof##_TEXM3X3PAD(Context *ctx); \
void emit_##prof##_TEXM3X3TEX(Context *ctx); \
void emit_##prof##_TEXM3X3SPEC(Context *ctx); \
void emit_##prof##_TEXM3X3VSPEC(Context *ctx); \
void emit_##prof##_EXPP(Context *ctx); \
void emit_##prof##_LOGP(Context *ctx); \
void emit_##prof##_CND(Context *ctx); \
void emit_##prof##_DEF(Context *ctx); \
void emit_##prof##_TEXREG2RGB(Context *ctx); \
void emit_##prof##_TEXDP3TEX(Context *ctx); \
void emit_##prof##_TEXM3X2DEPTH(Context *ctx); \
void emit_##prof##_TEXDP3(Context *ctx); \
void emit_##prof##_TEXM3X3(Context *ctx); \
void emit_##prof##_TEXDEPTH(Context *ctx); \
void emit_##prof##_CMP(Context *ctx); \
void emit_##prof##_BEM(Context *ctx); \
void emit_##prof##_DP2ADD(Context *ctx); \
void emit_##prof##_DSX(Context *ctx); \
void emit_##prof##_DSY(Context *ctx); \
void emit_##prof##_TEXLDD(Context *ctx); \
void emit_##prof##_SETP(Context *ctx); \
void emit_##prof##_TEXLDL(Context *ctx); \
void emit_##prof##_BREAKP(Context *ctx); \
void emit_##prof##_RESERVED(Context *ctx); \
void emit_##prof##_RET(Context *ctx); \
const char *get_##prof##_varname(Context *ctx, RegisterType rt, \
int regnum); \
const char *get_##prof##_const_array_varname(Context *ctx, \
int base, int size);
#define AT_LEAST_ONE_PROFILE 0
#if !SUPPORT_PROFILE_BYTECODE
#define PROFILE_EMITTER_BYTECODE(op)
#else
#undef AT_LEAST_ONE_PROFILE
#define AT_LEAST_ONE_PROFILE 1
#define PROFILE_EMITTER_BYTECODE(op) emit_BYTECODE_##op,
PREDECLARE_PROFILE(BYTECODE)
#endif
#if !SUPPORT_PROFILE_D3D
#define PROFILE_EMITTER_D3D(op)
#else
#undef AT_LEAST_ONE_PROFILE
#define AT_LEAST_ONE_PROFILE 1
#define PROFILE_EMITTER_D3D(op) emit_D3D_##op,
PREDECLARE_PROFILE(D3D)
#endif
#if !SUPPORT_PROFILE_HLSL
#define PROFILE_EMITTER_HLSL(op)
#else
#undef AT_LEAST_ONE_PROFILE
#define AT_LEAST_ONE_PROFILE 1
#define PROFILE_EMITTER_HLSL(op) emit_HLSL_##op,
PREDECLARE_PROFILE(HLSL)
#endif
#if !SUPPORT_PROFILE_GLSL
#define PROFILE_EMITTER_GLSL(op)
#else
#undef AT_LEAST_ONE_PROFILE
#define AT_LEAST_ONE_PROFILE 1
#define PROFILE_EMITTER_GLSL(op) emit_GLSL_##op,
PREDECLARE_PROFILE(GLSL)
#endif
#if !SUPPORT_PROFILE_METAL
#define PROFILE_EMITTER_METAL(op)
#else
#undef AT_LEAST_ONE_PROFILE
#define AT_LEAST_ONE_PROFILE 1
#define PROFILE_EMITTER_METAL(op) emit_METAL_##op,
PREDECLARE_PROFILE(METAL)
#endif
#if !SUPPORT_PROFILE_ARB1
#define PROFILE_EMITTER_ARB1(op)
#else
#undef AT_LEAST_ONE_PROFILE
#define AT_LEAST_ONE_PROFILE 1
#define PROFILE_EMITTER_ARB1(op) emit_ARB1_##op,
PREDECLARE_PROFILE(ARB1)
#endif
#if !SUPPORT_PROFILE_SPIRV
#define PROFILE_EMITTER_SPIRV(op)
#else
#undef AT_LEAST_ONE_PROFILE
#define AT_LEAST_ONE_PROFILE 1
#define PROFILE_EMITTER_SPIRV(op) emit_SPIRV_##op,
PREDECLARE_PROFILE(SPIRV)
#endif
#if !AT_LEAST_ONE_PROFILE
#error No profiles are supported. Fix your build.
#endif
#define DEFINE_PROFILE(prof) { \
MOJOSHADER_PROFILE_##prof, \
emit_##prof##_start, \
emit_##prof##_end, \
emit_##prof##_phase, \
emit_##prof##_global, \
emit_##prof##_array, \
emit_##prof##_const_array, \
emit_##prof##_uniform, \
emit_##prof##_sampler, \
emit_##prof##_attribute, \
emit_##prof##_finalize, \
get_##prof##_varname, \
get_##prof##_const_array_varname, \
},
static const Profile profiles[] =
{
#if SUPPORT_PROFILE_D3D
DEFINE_PROFILE(D3D)
#endif
#if SUPPORT_PROFILE_BYTECODE
DEFINE_PROFILE(BYTECODE)
#endif
#if SUPPORT_PROFILE_HLSL
DEFINE_PROFILE(HLSL)
#endif
#if SUPPORT_PROFILE_GLSL
DEFINE_PROFILE(GLSL)
#endif
#if SUPPORT_PROFILE_ARB1
DEFINE_PROFILE(ARB1)
#endif
#if SUPPORT_PROFILE_METAL
DEFINE_PROFILE(METAL)
#endif
#if SUPPORT_PROFILE_SPIRV
DEFINE_PROFILE(SPIRV)
#endif
};
#undef DEFINE_PROFILE
static const struct { const char *from; const char *to; } profileMap[] =
{
{ MOJOSHADER_PROFILE_GLSPIRV, MOJOSHADER_PROFILE_SPIRV },
{ MOJOSHADER_PROFILE_GLSLES, MOJOSHADER_PROFILE_GLSL },
{ MOJOSHADER_PROFILE_GLSL120, MOJOSHADER_PROFILE_GLSL },
{ MOJOSHADER_PROFILE_NV2, MOJOSHADER_PROFILE_ARB1 },
{ MOJOSHADER_PROFILE_NV3, MOJOSHADER_PROFILE_ARB1 },
{ MOJOSHADER_PROFILE_NV4, MOJOSHADER_PROFILE_ARB1 },
};
#define PROFILE_EMITTERS(op) { \
PROFILE_EMITTER_D3D(op) \
PROFILE_EMITTER_BYTECODE(op) \
PROFILE_EMITTER_HLSL(op) \
PROFILE_EMITTER_GLSL(op) \
PROFILE_EMITTER_ARB1(op) \
PROFILE_EMITTER_METAL(op) \
PROFILE_EMITTER_SPIRV(op) \
}
static int parse_destination_token(Context *ctx, DestArgInfo *info)
{
if (ctx->tokencount == 0)
{
fail(ctx, "Out of tokens in destination parameter");
return 0;
}
const uint32 token = SWAP32(*(ctx->tokens));
const int reserved1 = (int) ((token >> 14) & 0x3); const int reserved2 = (int) ((token >> 31) & 0x1);
info->token = ctx->tokens;
info->regnum = (int) (token & 0x7ff); info->relative = (int) ((token >> 13) & 0x1); info->orig_writemask = (int) ((token >> 16) & 0xF); info->result_mod = (int) ((token >> 20) & 0xF); info->result_shift = (int) ((token >> 24) & 0xF); info->regtype = (RegisterType) (((token >> 28) & 0x7) | ((token >> 8) & 0x18));
int writemask;
if (isscalar(ctx, ctx->shader_type, info->regtype, info->regnum))
writemask = 0x1; else
writemask = info->orig_writemask;
set_dstarg_writemask(info, writemask);
if (info->regtype == REG_TYPE_CONST2)
{
info->regtype = REG_TYPE_CONST;
info->regnum += 2048;
} else if (info->regtype == REG_TYPE_CONST3)
{
info->regtype = REG_TYPE_CONST;
info->regnum += 4096;
} else if (info->regtype == REG_TYPE_CONST4)
{
info->regtype = REG_TYPE_CONST;
info->regnum += 6144;
}
adjust_token_position(ctx, 1);
if (reserved1 != 0x0)
fail(ctx, "Reserved bit #1 in destination token must be zero");
if (reserved2 != 0x1)
fail(ctx, "Reserved bit #2 in destination token must be one");
if (info->relative)
{
if (!shader_is_vertex(ctx))
fail(ctx, "Relative addressing in non-vertex shader");
if (!shader_version_atleast(ctx, 3, 0))
fail(ctx, "Relative addressing in vertex shader version < 3.0");
if ((!ctx->ctab.have_ctab) && (!ctx->ignores_ctab))
{
fail(ctx, "relative addressing unsupported without a CTAB");
}
fail(ctx, "Relative addressing of dest tokens is unsupported");
return 2;
}
const int s = info->result_shift;
if (s != 0)
{
if (!shader_is_pixel(ctx))
fail(ctx, "Result shift scale in non-pixel shader");
if (shader_version_atleast(ctx, 2, 0))
fail(ctx, "Result shift scale in pixel shader version >= 2.0");
if ( ! (((s >= 1) && (s <= 3)) || ((s >= 0xD) && (s <= 0xF))) )
fail(ctx, "Result shift scale isn't 1 to 3, or 13 to 15.");
}
if (info->result_mod & MOD_PP) {
if (!shader_is_pixel(ctx))
fail(ctx, "Partial precision result mod in non-pixel shader");
}
if (info->result_mod & MOD_CENTROID) {
if (!shader_is_pixel(ctx))
fail(ctx, "Centroid result mod in non-pixel shader");
else if (!ctx->centroid_allowed) fail(ctx, "Centroid modifier not allowed here");
}
if ( (info->regtype > REG_TYPE_MAX))
fail(ctx, "Register type is out of range");
if (!isfail(ctx))
set_used_register(ctx, info->regtype, info->regnum, 1);
return 1;
}
static void determine_constants_arrays(Context *ctx)
{
if (ctx->determined_constants_arrays)
return;
ctx->determined_constants_arrays = 1;
if (ctx->constant_count <= 1)
return;
ConstantsList **array = (ConstantsList **) alloca(sizeof (ConstantsList *) * (ctx->constant_count + 1));
ConstantsList *item = ctx->constants;
int i;
for (i = 0; i < ctx->constant_count; i++)
{
if (item == NULL)
{
fail(ctx, "BUG: mismatched constant list and count");
return;
}
array[i] = item;
item = item->next;
}
array[ctx->constant_count] = NULL;
int sorted;
do
{
sorted = 1;
for (i = 0; i < ctx->constant_count-1; i++)
{
if (array[i]->constant.index > array[i+1]->constant.index)
{
ConstantsList *tmp = array[i];
array[i] = array[i+1];
array[i+1] = tmp;
sorted = 0;
} } } while (!sorted);
for (i = 0; i < ctx->constant_count; i++)
array[i]->next = array[i+1];
ctx->constants = array[0];
int start = -1;
int prev = -1;
int count = 0;
const int hi = ctx->constant_count;
for (i = 0; i <= hi; i++)
{
if (array[i] && (array[i]->constant.type != MOJOSHADER_UNIFORM_FLOAT))
continue;
if (start == -1)
{
prev = start = i; continue;
}
if ( (array[i]) && (array[i]->constant.index == (array[prev]->constant.index + 1)) )
count++;
else
{
if (count > 0) {
VariableList *var;
var = (VariableList *) Malloc(ctx, sizeof (VariableList));
if (var == NULL)
break;
var->type = MOJOSHADER_UNIFORM_FLOAT;
var->index = array[start]->constant.index;
var->count = (array[prev]->constant.index - var->index) + 1;
var->constant = array[start];
var->used = 0;
var->emit_position = -1;
var->next = ctx->variables;
ctx->variables = var;
}
start = i; }
prev = i;
} }
static void shader_model_1_input_usage(const int regnum, MOJOSHADER_usage *usage, int *index)
{
*index = 0;
switch (regnum) {
case 0: *usage = MOJOSHADER_USAGE_POSITION; break;
case 1: *usage = MOJOSHADER_USAGE_BLENDWEIGHT; break;
case 2: *usage = MOJOSHADER_USAGE_BLENDINDICES; break;
case 3: *usage = MOJOSHADER_USAGE_NORMAL; break;
case 4: *usage = MOJOSHADER_USAGE_POINTSIZE; break;
case 5: *usage = MOJOSHADER_USAGE_COLOR; break; case 6: *usage = MOJOSHADER_USAGE_COLOR; *index = 1; break; case 7: *usage = MOJOSHADER_USAGE_TEXCOORD; break;
case 8: *usage = MOJOSHADER_USAGE_TEXCOORD; *index = 1; break;
case 9: *usage = MOJOSHADER_USAGE_TEXCOORD; *index = 2; break;
case 10: *usage = MOJOSHADER_USAGE_TEXCOORD; *index = 3; break;
case 11: *usage = MOJOSHADER_USAGE_TEXCOORD; *index = 4; break;
case 12: *usage = MOJOSHADER_USAGE_TEXCOORD; *index = 5; break;
case 13: *usage = MOJOSHADER_USAGE_TEXCOORD; *index = 6; break;
case 14: *usage = MOJOSHADER_USAGE_TEXCOORD; *index = 7; break;
case 15: *usage = MOJOSHADER_USAGE_POSITION; *index = 1; break;
case 16: *usage = MOJOSHADER_USAGE_NORMAL; *index = 1; break;
default: *usage = MOJOSHADER_USAGE_UNKNOWN; break;
} }
static int adjust_swizzle(const Context *ctx, const RegisterType regtype,
const int regnum, const int swizzle)
{
if (regtype != REG_TYPE_INPUT) return swizzle;
else if (ctx->swizzles_count == 0)
return swizzle;
MOJOSHADER_usage usage = MOJOSHADER_USAGE_UNKNOWN;
int index = 0;
if (!shader_version_atleast(ctx, 2, 0))
shader_model_1_input_usage(regnum, &usage, &index);
else
{
const RegisterList *reg = reglist_find(&ctx->attributes, regtype, regnum);
if (reg == NULL)
return swizzle;
usage = reg->usage;
index = reg->index;
}
if (usage == MOJOSHADER_USAGE_UNKNOWN)
return swizzle;
size_t i;
for (i = 0; i < ctx->swizzles_count; i++)
{
const MOJOSHADER_swizzle *swiz = &ctx->swizzles[i];
if ((swiz->usage == usage) && (swiz->index == index))
{
return ( (((int)(swiz->swizzles[((swizzle >> 0) & 0x3)])) << 0) |
(((int)(swiz->swizzles[((swizzle >> 2) & 0x3)])) << 2) |
(((int)(swiz->swizzles[((swizzle >> 4) & 0x3)])) << 4) |
(((int)(swiz->swizzles[((swizzle >> 6) & 0x3)])) << 6) );
} }
return swizzle;
}
static int parse_source_token(Context *ctx, SourceArgInfo *info)
{
int retval = 1;
if (ctx->tokencount == 0)
{
fail(ctx, "Out of tokens in source parameter");
return 0;
}
const uint32 token = SWAP32(*(ctx->tokens));
const int reserved1 = (int) ((token >> 14) & 0x3); const int reserved2 = (int) ((token >> 31) & 0x1);
info->token = ctx->tokens;
info->regnum = (int) (token & 0x7ff); info->relative = (int) ((token >> 13) & 0x1); const int swizzle = (int) ((token >> 16) & 0xFF); info->src_mod = (SourceMod) ((token >> 24) & 0xF); info->regtype = (RegisterType) (((token >> 28) & 0x7) | ((token >> 8) & 0x18));
if (info->regtype == REG_TYPE_CONST2)
{
info->regtype = REG_TYPE_CONST;
info->regnum += 2048;
} else if (info->regtype == REG_TYPE_CONST3)
{
info->regtype = REG_TYPE_CONST;
info->regnum += 4096;
} else if (info->regtype == REG_TYPE_CONST4)
{
info->regtype = REG_TYPE_CONST;
info->regnum += 6144;
}
info->swizzle = adjust_swizzle(ctx, info->regtype, info->regnum, swizzle);
info->swizzle_x = ((info->swizzle >> 0) & 0x3);
info->swizzle_y = ((info->swizzle >> 2) & 0x3);
info->swizzle_z = ((info->swizzle >> 4) & 0x3);
info->swizzle_w = ((info->swizzle >> 6) & 0x3);
adjust_token_position(ctx, 1);
if (reserved1 != 0x0)
fail(ctx, "Reserved bits #1 in source token must be zero");
if (reserved2 != 0x1)
fail(ctx, "Reserved bit #2 in source token must be one");
if ((info->relative) && (ctx->tokencount == 0))
{
fail(ctx, "Out of tokens in relative source parameter");
info->relative = 0; }
if (info->relative)
{
if ( (shader_is_pixel(ctx)) && (!shader_version_atleast(ctx, 3, 0)) )
fail(ctx, "Relative addressing in pixel shader version < 3.0");
if (!shader_version_atleast(ctx, 2, 0))
{
info->relative_regnum = 0;
info->relative_regtype = REG_TYPE_ADDRESS;
info->relative_component = 0;
}
else {
const uint32 reltoken = SWAP32(*(ctx->tokens));
adjust_token_position(ctx, 1);
const int relswiz = (int) ((reltoken >> 16) & 0xFF);
info->relative_regnum = (int) (reltoken & 0x7ff);
info->relative_regtype = (RegisterType)
(((reltoken >> 28) & 0x7) |
((reltoken >> 8) & 0x18));
if (((reltoken >> 31) & 0x1) == 0)
fail(ctx, "bit #31 in relative address must be set");
if ((reltoken & 0xF00E000) != 0) fail(ctx, "relative address reserved bit must be zero");
switch (info->relative_regtype)
{
case REG_TYPE_LOOP:
case REG_TYPE_ADDRESS:
break;
default:
fail(ctx, "invalid register for relative address");
break;
}
if (info->relative_regnum != 0) fail(ctx, "invalid register for relative address");
if ( (info->relative_regtype != REG_TYPE_LOOP) && !replicate_swizzle(relswiz) )
fail(ctx, "relative address needs replicate swizzle");
info->relative_component = (relswiz & 0x3);
retval++;
}
if (info->regtype == REG_TYPE_INPUT)
{
if ( (shader_is_pixel(ctx)) || (!shader_version_atleast(ctx, 3, 0)) )
fail(ctx, "relative addressing of input registers not supported in this shader model");
ctx->have_relative_input_registers = 1;
} else if (info->regtype == REG_TYPE_CONST)
{
if (!ctx->ignores_ctab)
{
if (!ctx->ctab.have_ctab) fail(ctx, "relative addressing unsupported without a CTAB");
else
{
determine_constants_arrays(ctx);
VariableList *var;
const int reltarget = info->regnum;
for (var = ctx->variables; var != NULL; var = var->next)
{
const int lo = var->index;
if ( (reltarget >= lo) && (reltarget < (lo + var->count)) )
break; }
if (var == NULL)
fail(ctx, "relative addressing of indeterminate array");
else
{
var->used = 1;
info->relative_array = var;
set_used_register(ctx, info->relative_regtype, info->relative_regnum, 0);
} } } } else
{
fail(ctx, "relative addressing of invalid register");
} }
switch (info->src_mod)
{
case SRCMOD_NONE:
case SRCMOD_ABSNEGATE:
case SRCMOD_ABS:
case SRCMOD_NEGATE:
break;
case SRCMOD_BIASNEGATE:
case SRCMOD_BIAS:
case SRCMOD_SIGNNEGATE:
case SRCMOD_SIGN:
case SRCMOD_COMPLEMENT:
case SRCMOD_X2NEGATE:
case SRCMOD_X2:
case SRCMOD_DZ:
case SRCMOD_DW:
if (shader_version_atleast(ctx, 2, 0))
fail(ctx, "illegal source mod for this Shader Model.");
break;
case SRCMOD_NOT: if (shader_version_atleast(ctx, 2, 0))
{
if (info->regtype != REG_TYPE_PREDICATE
&& info->regtype != REG_TYPE_CONSTBOOL)
fail(ctx, "NOT only allowed on bool registers.");
} break;
default:
fail(ctx, "Unknown source modifier");
}
if (!isfail(ctx))
{
RegisterList *reg;
reg = set_used_register(ctx, info->regtype, info->regnum, 0);
if ((info->regtype == REG_TYPE_TEMP) && (reg) && (!reg->written))
failf(ctx, "Temp register r%d used uninitialized", info->regnum);
}
return retval;
}
static int parse_predicated_token(Context *ctx)
{
SourceArgInfo *arg = &ctx->predicate_arg;
parse_source_token(ctx, arg);
if (arg->regtype != REG_TYPE_PREDICATE)
fail(ctx, "Predicated instruction but not predicate register!");
if ((arg->src_mod != SRCMOD_NONE) && (arg->src_mod != SRCMOD_NOT))
fail(ctx, "Predicated instruction register is not NONE or NOT");
if ( !no_swizzle(arg->swizzle) && !replicate_swizzle(arg->swizzle) )
fail(ctx, "Predicated instruction register has wrong swizzle");
if (arg->relative) fail(ctx, "relative addressing in predicated token");
return 1;
}
static int parse_args_NULL(Context *ctx)
{
return 1;
}
static int parse_args_DEF(Context *ctx)
{
parse_destination_token(ctx, &ctx->dest_arg);
if (ctx->dest_arg.regtype != REG_TYPE_CONST)
fail(ctx, "DEF using non-CONST register");
if (ctx->dest_arg.relative) fail(ctx, "relative addressing in DEF");
ctx->dwords[0] = SWAP32(ctx->tokens[0]);
ctx->dwords[1] = SWAP32(ctx->tokens[1]);
ctx->dwords[2] = SWAP32(ctx->tokens[2]);
ctx->dwords[3] = SWAP32(ctx->tokens[3]);
return 6;
}
static int parse_args_DEFI(Context *ctx)
{
parse_destination_token(ctx, &ctx->dest_arg);
if (ctx->dest_arg.regtype != REG_TYPE_CONSTINT)
fail(ctx, "DEFI using non-CONSTING register");
if (ctx->dest_arg.relative) fail(ctx, "relative addressing in DEFI");
ctx->dwords[0] = SWAP32(ctx->tokens[0]);
ctx->dwords[1] = SWAP32(ctx->tokens[1]);
ctx->dwords[2] = SWAP32(ctx->tokens[2]);
ctx->dwords[3] = SWAP32(ctx->tokens[3]);
return 6;
}
static int parse_args_DEFB(Context *ctx)
{
parse_destination_token(ctx, &ctx->dest_arg);
if (ctx->dest_arg.regtype != REG_TYPE_CONSTBOOL)
fail(ctx, "DEFB using non-CONSTBOOL register");
if (ctx->dest_arg.relative) fail(ctx, "relative addressing in DEFB");
ctx->dwords[0] = *(ctx->tokens) ? 1 : 0;
return 3;
}
static int valid_texture_type(const uint32 ttype)
{
switch ((const TextureType) ttype)
{
case TEXTURE_TYPE_2D:
case TEXTURE_TYPE_CUBE:
case TEXTURE_TYPE_VOLUME:
return 1; }
return 0;
}
static int parse_args_DCL(Context *ctx)
{
int unsupported = 0;
const uint32 token = SWAP32(*(ctx->tokens));
const int reserved1 = (int) ((token >> 31) & 0x1); uint32 reserved_mask = 0x00000000;
if (reserved1 != 0x1)
fail(ctx, "Bit #31 in DCL token must be one");
ctx->centroid_allowed = 1;
adjust_token_position(ctx, 1);
parse_destination_token(ctx, &ctx->dest_arg);
ctx->centroid_allowed = 0;
if (ctx->dest_arg.result_shift != 0) fail(ctx, "shift scale in DCL");
if (ctx->dest_arg.relative) fail(ctx, "relative addressing in DCL");
const RegisterType regtype = ctx->dest_arg.regtype;
const int regnum = ctx->dest_arg.regnum;
if ( (shader_is_pixel(ctx)) && (shader_version_atleast(ctx, 3, 0)) )
{
if (regtype == REG_TYPE_INPUT)
{
const uint32 usage = (token & 0xF);
const uint32 index = ((token >> 16) & 0xF);
reserved_mask = 0x7FF0FFE0;
ctx->dwords[0] = usage;
ctx->dwords[1] = index;
}
else if (regtype == REG_TYPE_MISCTYPE)
{
const MiscTypeType mt = (MiscTypeType) regnum;
if (mt == MISCTYPE_TYPE_POSITION)
reserved_mask = 0x7FFFFFFF;
else if (mt == MISCTYPE_TYPE_FACE)
{
reserved_mask = 0x7FFFFFFF;
if (!writemask_xyzw(ctx->dest_arg.orig_writemask))
fail(ctx, "DCL face writemask must be full");
if (ctx->dest_arg.result_mod != 0)
fail(ctx, "DCL face result modifier must be zero");
if (ctx->dest_arg.result_shift != 0)
fail(ctx, "DCL face shift scale must be zero");
} else
{
unsupported = 1;
}
ctx->dwords[0] = (uint32) MOJOSHADER_USAGE_UNKNOWN;
ctx->dwords[1] = 0;
}
else if (regtype == REG_TYPE_TEXTURE)
{
const uint32 usage = (token & 0xF);
const uint32 index = ((token >> 16) & 0xF);
if (usage == MOJOSHADER_USAGE_TEXCOORD)
{
if (index > 7)
fail(ctx, "DCL texcoord usage must have 0-7 index");
} else if (usage == MOJOSHADER_USAGE_COLOR)
{
if (index != 0)
fail(ctx, "DCL color usage must have 0 index");
} else
{
fail(ctx, "Invalid DCL texture usage");
}
reserved_mask = 0x7FF0FFE0;
ctx->dwords[0] = usage;
ctx->dwords[1] = index;
}
else if (regtype == REG_TYPE_SAMPLER)
{
const uint32 ttype = ((token >> 27) & 0xF);
if (!valid_texture_type(ttype))
fail(ctx, "unknown sampler texture type");
reserved_mask = 0x7FFFFFF;
ctx->dwords[0] = ttype;
}
else
{
unsupported = 1;
} }
else if ( (shader_is_pixel(ctx)) && (shader_version_atleast(ctx, 2, 0)) )
{
if (regtype == REG_TYPE_INPUT)
{
ctx->dwords[0] = (uint32) MOJOSHADER_USAGE_COLOR;
ctx->dwords[1] = regnum;
reserved_mask = 0x7FFFFFFF;
} else if (regtype == REG_TYPE_TEXTURE)
{
ctx->dwords[0] = (uint32) MOJOSHADER_USAGE_TEXCOORD;
ctx->dwords[1] = regnum;
reserved_mask = 0x7FFFFFFF;
} else if (regtype == REG_TYPE_SAMPLER)
{
const uint32 ttype = ((token >> 27) & 0xF);
if (!valid_texture_type(ttype))
fail(ctx, "unknown sampler texture type");
reserved_mask = 0x7FFFFFF;
ctx->dwords[0] = ttype;
} else
{
unsupported = 1;
} }
else if ( (shader_is_vertex(ctx)) && (shader_version_atleast(ctx, 3, 0)) )
{
if ((regtype == REG_TYPE_INPUT) || (regtype == REG_TYPE_OUTPUT))
{
const uint32 usage = (token & 0xF);
const uint32 index = ((token >> 16) & 0xF);
reserved_mask = 0x7FF0FFE0;
ctx->dwords[0] = usage;
ctx->dwords[1] = index;
} else if (regtype == REG_TYPE_TEXTURE)
{
const uint32 usage = (token & 0xF);
const uint32 index = ((token >> 16) & 0xF);
if (usage == MOJOSHADER_USAGE_TEXCOORD)
{
if (index > 7)
fail(ctx, "DCL texcoord usage must have 0-7 index");
} else if (usage == MOJOSHADER_USAGE_COLOR)
{
if (index != 0)
fail(ctx, "DCL texcoord usage must have 0 index");
} else
fail(ctx, "Invalid DCL texture usage");
reserved_mask = 0x7FF0FFE0;
ctx->dwords[0] = usage;
ctx->dwords[1] = index;
} else if (regtype == REG_TYPE_SAMPLER)
{
const uint32 ttype = ((token >> 27) & 0xF);
if (!valid_texture_type(ttype))
fail(ctx, "Unknown sampler texture type");
reserved_mask = 0x0FFFFFFF;
ctx->dwords[0] = ttype;
} else
{
unsupported = 1;
} }
else if ( (shader_is_vertex(ctx)) && (shader_version_atleast(ctx, 1, 1)) )
{
if (regtype == REG_TYPE_INPUT)
{
const uint32 usage = (token & 0xF);
const uint32 index = ((token >> 16) & 0xF);
reserved_mask = 0x7FF0FFE0;
ctx->dwords[0] = usage;
ctx->dwords[1] = index;
} else
{
unsupported = 1;
} }
else
{
unsupported = 1;
}
if (unsupported)
fail(ctx, "invalid DCL register type for this shader model");
if ((token & reserved_mask) != 0)
fail(ctx, "reserved bits in DCL dword aren't zero");
return 3;
}
static int parse_args_D(Context *ctx)
{
int retval = 1;
retval += parse_destination_token(ctx, &ctx->dest_arg);
return retval;
}
static int parse_args_S(Context *ctx)
{
int retval = 1;
retval += parse_source_token(ctx, &ctx->source_args[0]);
return retval;
}
static int parse_args_SS(Context *ctx)
{
int retval = 1;
retval += parse_source_token(ctx, &ctx->source_args[0]);
retval += parse_source_token(ctx, &ctx->source_args[1]);
return retval;
}
static int parse_args_DS(Context *ctx)
{
int retval = 1;
retval += parse_destination_token(ctx, &ctx->dest_arg);
retval += parse_source_token(ctx, &ctx->source_args[0]);
return retval;
}
static int parse_args_DSS(Context *ctx)
{
int retval = 1;
retval += parse_destination_token(ctx, &ctx->dest_arg);
retval += parse_source_token(ctx, &ctx->source_args[0]);
retval += parse_source_token(ctx, &ctx->source_args[1]);
return retval;
}
static int parse_args_DSSS(Context *ctx)
{
int retval = 1;
retval += parse_destination_token(ctx, &ctx->dest_arg);
retval += parse_source_token(ctx, &ctx->source_args[0]);
retval += parse_source_token(ctx, &ctx->source_args[1]);
retval += parse_source_token(ctx, &ctx->source_args[2]);
return retval;
}
static int parse_args_DSSSS(Context *ctx)
{
int retval = 1;
retval += parse_destination_token(ctx, &ctx->dest_arg);
retval += parse_source_token(ctx, &ctx->source_args[0]);
retval += parse_source_token(ctx, &ctx->source_args[1]);
retval += parse_source_token(ctx, &ctx->source_args[2]);
retval += parse_source_token(ctx, &ctx->source_args[3]);
return retval;
}
static int parse_args_SINCOS(Context *ctx)
{
if (!shader_version_atleast(ctx, 3, 0))
return parse_args_DSSS(ctx);
return parse_args_DS(ctx);
}
static int parse_args_TEXCRD(Context *ctx)
{
if (shader_version_atleast(ctx, 1, 4))
return parse_args_DS(ctx);
return parse_args_D(ctx);
}
static int parse_args_TEXLD(Context *ctx)
{
if (shader_version_atleast(ctx, 2, 0))
return parse_args_DSS(ctx);
else if (shader_version_atleast(ctx, 1, 4))
return parse_args_DS(ctx);
return parse_args_D(ctx);
}
static ConstantsList *alloc_constant_listitem(Context *ctx)
{
ConstantsList *item = (ConstantsList *) Malloc(ctx, sizeof (ConstantsList));
if (item == NULL)
return NULL;
memset(&item->constant, '\0', sizeof (MOJOSHADER_constant));
item->next = ctx->constants;
ctx->constants = item;
ctx->constant_count++;
return item;
}
static void state_DEF(Context *ctx)
{
const RegisterType regtype = ctx->dest_arg.regtype;
const int regnum = ctx->dest_arg.regnum;
if (ctx->instruction_count != 0)
fail(ctx, "DEF token must come before any instructions");
else if (regtype != REG_TYPE_CONST)
fail(ctx, "DEF token using invalid register");
else
{
ConstantsList *item = alloc_constant_listitem(ctx);
if (item != NULL)
{
item->constant.index = regnum;
item->constant.type = MOJOSHADER_UNIFORM_FLOAT;
memcpy(item->constant.value.f, ctx->dwords,
sizeof (item->constant.value.f));
set_defined_register(ctx, regtype, regnum);
} } }
static void state_DEFI(Context *ctx)
{
const RegisterType regtype = ctx->dest_arg.regtype;
const int regnum = ctx->dest_arg.regnum;
if (ctx->instruction_count != 0)
fail(ctx, "DEFI token must come before any instructions");
else if (regtype != REG_TYPE_CONSTINT)
fail(ctx, "DEFI token using invalid register");
else
{
ConstantsList *item = alloc_constant_listitem(ctx);
if (item != NULL)
{
item->constant.index = regnum;
item->constant.type = MOJOSHADER_UNIFORM_INT;
memcpy(item->constant.value.i, ctx->dwords,
sizeof (item->constant.value.i));
set_defined_register(ctx, regtype, regnum);
} } }
static void state_DEFB(Context *ctx)
{
const RegisterType regtype = ctx->dest_arg.regtype;
const int regnum = ctx->dest_arg.regnum;
if (ctx->instruction_count != 0)
fail(ctx, "DEFB token must come before any instructions");
else if (regtype != REG_TYPE_CONSTBOOL)
fail(ctx, "DEFB token using invalid register");
else
{
ConstantsList *item = alloc_constant_listitem(ctx);
if (item != NULL)
{
item->constant.index = regnum;
item->constant.type = MOJOSHADER_UNIFORM_BOOL;
item->constant.value.b = ctx->dwords[0] ? 1 : 0;
set_defined_register(ctx, regtype, regnum);
} } }
static void state_DCL(Context *ctx)
{
const DestArgInfo *arg = &ctx->dest_arg;
const RegisterType regtype = arg->regtype;
const int regnum = arg->regnum;
const int wmask = arg->writemask;
const int mods = arg->result_mod;
if (ctx->instruction_count != 0)
fail(ctx, "DCL token must come before any instructions");
else if (shader_is_vertex(ctx) || shader_is_pixel(ctx))
{
if (regtype == REG_TYPE_SAMPLER)
add_sampler(ctx, regnum, (TextureType) ctx->dwords[0], 0);
else
{
const MOJOSHADER_usage usage = (const MOJOSHADER_usage) ctx->dwords[0];
const int index = ctx->dwords[1];
if (usage >= MOJOSHADER_USAGE_TOTAL)
{
fail(ctx, "unknown DCL usage");
return;
} add_attribute_register(ctx, regtype, regnum, usage, index, wmask, mods);
} }
else
{
fail(ctx, "unsupported shader type."); return;
}
set_defined_register(ctx, regtype, regnum);
}
static void state_TEXCRD(Context *ctx)
{
if (shader_version_atleast(ctx, 2, 0))
fail(ctx, "TEXCRD in Shader Model >= 2.0"); }
static void state_FRC(Context *ctx)
{
const DestArgInfo *dst = &ctx->dest_arg;
if (dst->result_mod & MOD_SATURATE) fail(ctx, "FRC destination can't use saturate modifier");
else if (!shader_version_atleast(ctx, 2, 0))
{
if (!writemask_y(dst->writemask) && !writemask_xy(dst->writemask))
fail(ctx, "FRC writemask must be .y or .xy for shader model 1.x");
} }
static void srcarg_matrix_replicate(Context *ctx, const int idx,
const int rows)
{
int i;
SourceArgInfo *src = &ctx->source_args[idx];
SourceArgInfo *dst = &ctx->source_args[idx+1];
for (i = 0; i < (rows-1); i++, dst++)
{
memcpy(dst, src, sizeof (SourceArgInfo));
dst->regnum += (i + 1);
set_used_register(ctx, dst->regtype, dst->regnum, 0);
} }
static void state_M4X4(Context *ctx)
{
const DestArgInfo *info = &ctx->dest_arg;
if (!writemask_xyzw(info->writemask))
fail(ctx, "M4X4 writemask must be full");
srcarg_matrix_replicate(ctx, 1, 4);
}
static void state_M4X3(Context *ctx)
{
const DestArgInfo *info = &ctx->dest_arg;
if (!writemask_xyz(info->writemask))
fail(ctx, "M4X3 writemask must be .xyz");
srcarg_matrix_replicate(ctx, 1, 3);
}
static void state_M3X4(Context *ctx)
{
const DestArgInfo *info = &ctx->dest_arg;
if (!writemask_xyzw(info->writemask))
fail(ctx, "M3X4 writemask must be .xyzw");
srcarg_matrix_replicate(ctx, 1, 4);
}
static void state_M3X3(Context *ctx)
{
const DestArgInfo *info = &ctx->dest_arg;
if (!writemask_xyz(info->writemask))
fail(ctx, "M3X3 writemask must be .xyz");
srcarg_matrix_replicate(ctx, 1, 3);
}
static void state_M3X2(Context *ctx)
{
const DestArgInfo *info = &ctx->dest_arg;
if (!writemask_xy(info->writemask))
fail(ctx, "M3X2 writemask must be .xy");
srcarg_matrix_replicate(ctx, 1, 2);
}
static void state_RET(Context *ctx)
{
if (ctx->loops > 0)
fail(ctx, "LOOP without ENDLOOP");
if (ctx->reps > 0)
fail(ctx, "REP without ENDREP");
}
static void check_label_register(Context *ctx, int arg, const char *opcode)
{
const SourceArgInfo *info = &ctx->source_args[arg];
const RegisterType regtype = info->regtype;
const int regnum = info->regnum;
if (regtype != REG_TYPE_LABEL)
failf(ctx, "%s with a non-label register specified", opcode);
if (!shader_version_atleast(ctx, 2, 0))
failf(ctx, "%s not supported in Shader Model 1", opcode);
if ((shader_version_atleast(ctx, 2, 255)) && (regnum > 2047))
fail(ctx, "label register number must be <= 2047");
if (regnum > 15)
fail(ctx, "label register number must be <= 15");
}
static void state_LABEL(Context *ctx)
{
if (ctx->previous_opcode != OPCODE_RET)
fail(ctx, "LABEL not followed by a RET");
check_label_register(ctx, 0, "LABEL");
set_defined_register(ctx, REG_TYPE_LABEL, ctx->source_args[0].regnum);
}
static void check_call_loop_wrappage(Context *ctx, const int regnum)
{
const int current_usage = (ctx->loops > 0) ? 1 : -1;
RegisterList *reg = reglist_find(&ctx->used_registers, REG_TYPE_LABEL, regnum);
if (reg == NULL)
fail(ctx, "Invalid label for CALL");
else if (reg->misc == 0)
reg->misc = current_usage;
else if (reg->misc != current_usage)
{
if (current_usage == 1)
fail(ctx, "CALL to this label must be wrapped in LOOP/ENDLOOP");
else
fail(ctx, "CALL to this label must not be wrapped in LOOP/ENDLOOP");
} }
static void state_CALL(Context *ctx)
{
check_label_register(ctx, 0, "CALL");
check_call_loop_wrappage(ctx, ctx->source_args[0].regnum);
}
static void state_CALLNZ(Context *ctx)
{
const RegisterType regtype = ctx->source_args[1].regtype;
if ((regtype != REG_TYPE_CONSTBOOL) && (regtype != REG_TYPE_PREDICATE))
fail(ctx, "CALLNZ argument isn't constbool or predicate register");
check_label_register(ctx, 0, "CALLNZ");
check_call_loop_wrappage(ctx, ctx->source_args[0].regnum);
}
static void state_MOVA(Context *ctx)
{
if (ctx->dest_arg.regtype != REG_TYPE_ADDRESS)
fail(ctx, "MOVA argument isn't address register");
}
static void state_RCP(Context *ctx)
{
if (!replicate_swizzle(ctx->source_args[0].swizzle))
fail(ctx, "RCP without replicate swizzle");
}
static void state_RSQ(Context *ctx)
{
if (!replicate_swizzle(ctx->source_args[0].swizzle))
fail(ctx, "RSQ without replicate swizzle");
}
static void state_LOOP(Context *ctx)
{
if (ctx->source_args[0].regtype != REG_TYPE_LOOP)
fail(ctx, "LOOP argument isn't loop register");
else if (ctx->source_args[1].regtype != REG_TYPE_CONSTINT)
fail(ctx, "LOOP argument isn't constint register");
else
ctx->loops++;
}
static void state_ENDLOOP(Context *ctx)
{
if (ctx->loops <= 0)
fail(ctx, "ENDLOOP without LOOP");
ctx->loops--;
}
static void state_BREAKP(Context *ctx)
{
const RegisterType regtype = ctx->source_args[0].regtype;
if (regtype != REG_TYPE_PREDICATE)
fail(ctx, "BREAKP argument isn't predicate register");
else if (!replicate_swizzle(ctx->source_args[0].swizzle))
fail(ctx, "BREAKP without replicate swizzle");
else if ((ctx->loops == 0) && (ctx->reps == 0))
fail(ctx, "BREAKP outside LOOP/ENDLOOP or REP/ENDREP");
}
static void state_BREAK(Context *ctx)
{
if ((ctx->loops == 0) && (ctx->reps == 0))
fail(ctx, "BREAK outside LOOP/ENDLOOP or REP/ENDREP");
}
static void state_SETP(Context *ctx)
{
const RegisterType regtype = ctx->dest_arg.regtype;
if (regtype != REG_TYPE_PREDICATE)
fail(ctx, "SETP argument isn't predicate register");
}
static void state_REP(Context *ctx)
{
const RegisterType regtype = ctx->source_args[0].regtype;
if (regtype != REG_TYPE_CONSTINT)
fail(ctx, "REP argument isn't constint register");
ctx->reps++;
if (ctx->reps > ctx->max_reps)
ctx->max_reps = ctx->reps;
}
static void state_ENDREP(Context *ctx)
{
if (ctx->reps <= 0)
fail(ctx, "ENDREP without REP");
ctx->reps--;
}
static void state_CMP(Context *ctx)
{
ctx->cmps++;
if (!shader_version_atleast(ctx, 1, 4))
{
int i;
const DestArgInfo *dst = &ctx->dest_arg;
const RegisterType dregtype = dst->regtype;
const int dregnum = dst->regnum;
if (ctx->cmps > 3)
fail(ctx, "only 3 CMP instructions allowed in this shader model");
for (i = 0; i < 3; i++)
{
const SourceArgInfo *src = &ctx->source_args[i];
const RegisterType sregtype = src->regtype;
const int sregnum = src->regnum;
if ((dregtype == sregtype) && (dregnum == sregnum))
fail(ctx, "CMP dest can't match sources in this shader model");
}
ctx->instruction_count++; } }
static void state_DP4(Context *ctx)
{
if (!shader_version_atleast(ctx, 1, 4))
ctx->instruction_count++; }
static void state_CND(Context *ctx)
{
if (shader_version_atleast(ctx, 2, 0))
fail(ctx, "CND not allowed in this shader model");
else if (!shader_version_atleast(ctx, 1, 4))
{
const SourceArgInfo *src = &ctx->source_args[0];
if ((src->regtype != REG_TYPE_TEMP) || (src->regnum != 0) ||
(src->swizzle != 0xFF))
{
fail(ctx, "CND src must be r0.a in this shader model");
} } }
static void state_POW(Context *ctx)
{
if (!replicate_swizzle(ctx->source_args[0].swizzle))
fail(ctx, "POW src0 must have replicate swizzle");
else if (!replicate_swizzle(ctx->source_args[1].swizzle))
fail(ctx, "POW src1 must have replicate swizzle");
}
static void state_LOG(Context *ctx)
{
if (!replicate_swizzle(ctx->source_args[0].swizzle))
fail(ctx, "LOG src0 must have replicate swizzle");
}
static void state_LOGP(Context *ctx)
{
if (!replicate_swizzle(ctx->source_args[0].swizzle))
fail(ctx, "LOGP src0 must have replicate swizzle");
}
static void state_SINCOS(Context *ctx)
{
const DestArgInfo *dst = &ctx->dest_arg;
const int mask = dst->writemask;
if (!writemask_x(mask) && !writemask_y(mask) && !writemask_xy(mask))
fail(ctx, "SINCOS write mask must be .x or .y or .xy");
else if (!replicate_swizzle(ctx->source_args[0].swizzle))
fail(ctx, "SINCOS src0 must have replicate swizzle");
else if (dst->result_mod & MOD_SATURATE) fail(ctx, "SINCOS destination can't use saturate modifier");
else if (!shader_version_atleast(ctx, 3, 0))
{
int i;
for (i = 1; i < 3; i++)
{
if (ctx->source_args[i].regtype != REG_TYPE_CONST)
{
failf(ctx, "SINCOS src%d must be constfloat", i);
return;
} }
if (ctx->source_args[1].regnum == ctx->source_args[2].regnum)
fail(ctx, "SINCOS src1 and src2 must be different registers");
} }
static void state_IF(Context *ctx)
{
const RegisterType regtype = ctx->source_args[0].regtype;
if ((regtype != REG_TYPE_PREDICATE) && (regtype != REG_TYPE_CONSTBOOL))
fail(ctx, "IF src0 must be CONSTBOOL or PREDICATE");
}
static void state_IFC(Context *ctx)
{
if (!replicate_swizzle(ctx->source_args[0].swizzle))
fail(ctx, "IFC src0 must have replicate swizzle");
else if (!replicate_swizzle(ctx->source_args[1].swizzle))
fail(ctx, "IFC src1 must have replicate swizzle");
}
static void state_BREAKC(Context *ctx)
{
if (!replicate_swizzle(ctx->source_args[0].swizzle))
fail(ctx, "BREAKC src1 must have replicate swizzle");
else if (!replicate_swizzle(ctx->source_args[1].swizzle))
fail(ctx, "BREAKC src2 must have replicate swizzle");
else if ((ctx->loops == 0) && (ctx->reps == 0))
fail(ctx, "BREAKC outside LOOP/ENDLOOP or REP/ENDREP");
}
static void state_TEXKILL(Context *ctx)
{
const DestArgInfo *info = &ctx->dest_arg;
const RegisterType regtype = info->regtype;
if (!writemask_xyzw(info->writemask))
fail(ctx, "TEXKILL writemask must be .xyzw");
else if ((regtype != REG_TYPE_TEMP) && (regtype != REG_TYPE_TEXTURE))
fail(ctx, "TEXKILL must use a temp or texture register");
}
static void state_texops(Context *ctx, const char *opcode,
const int dims, const int texbem)
{
const DestArgInfo *dst = &ctx->dest_arg;
const SourceArgInfo *src = &ctx->source_args[0];
if (dst->regtype != REG_TYPE_TEXTURE)
failf(ctx, "%s destination must be a texture register", opcode);
if (src->regtype != REG_TYPE_TEXTURE)
failf(ctx, "%s source must be a texture register", opcode);
if (src->regnum >= dst->regnum) failf(ctx, "%s dest must be a higher register than source", opcode);
if (dims)
{
TextureType ttyp = (dims == 2) ? TEXTURE_TYPE_2D : TEXTURE_TYPE_CUBE;
add_sampler(ctx, dst->regnum, ttyp, texbem);
}
add_attribute_register(ctx, REG_TYPE_TEXTURE, dst->regnum,
MOJOSHADER_USAGE_TEXCOORD, dst->regnum, 0xF, 0);
add_attribute_register(ctx, REG_TYPE_TEXTURE, src->regnum,
MOJOSHADER_USAGE_TEXCOORD, src->regnum, 0xF, 0);
}
static void state_texbem(Context *ctx, const char *opcode)
{
if (shader_version_atleast(ctx, 1, 4))
failf(ctx, "%s opcode not available after Shader Model 1.3", opcode);
if (!shader_version_atleast(ctx, 1, 2))
{
if (ctx->source_args[0].src_mod == SRCMOD_SIGN)
failf(ctx, "%s forbids _bx2 on source reg before ps_1_2", opcode);
}
state_texops(ctx, opcode, 2, 1);
}
static void state_TEXBEM(Context *ctx)
{
state_texbem(ctx, "TEXBEM");
}
static void state_TEXBEML(Context *ctx)
{
state_texbem(ctx, "TEXBEML");
}
static void state_TEXM3X2PAD(Context *ctx)
{
if (shader_version_atleast(ctx, 1, 4))
fail(ctx, "TEXM3X2PAD opcode not available after Shader Model 1.3");
state_texops(ctx, "TEXM3X2PAD", 0, 0);
ctx->texm3x2pad_src0 = ctx->source_args[0].regnum;
ctx->texm3x2pad_dst0 = ctx->dest_arg.regnum;
}
static void state_TEXM3X2TEX(Context *ctx)
{
if (shader_version_atleast(ctx, 1, 4))
fail(ctx, "TEXM3X2TEX opcode not available after Shader Model 1.3");
if (ctx->texm3x2pad_dst0 == -1)
fail(ctx, "TEXM3X2TEX opcode without matching TEXM3X2PAD");
state_texops(ctx, "TEXM3X2TEX", 2, 0);
ctx->reset_texmpad = 1;
RegisterList *sreg = reglist_find(&ctx->samplers, REG_TYPE_SAMPLER,
ctx->dest_arg.regnum);
const TextureType ttype = (TextureType) (sreg ? sreg->index : 0);
if (ttype != TEXTURE_TYPE_2D)
fail(ctx, "TEXM3X2TEX needs a 2D sampler");
}
static void state_TEXM3X3PAD(Context *ctx)
{
if (shader_version_atleast(ctx, 1, 4))
fail(ctx, "TEXM3X2TEX opcode not available after Shader Model 1.3");
state_texops(ctx, "TEXM3X3PAD", 0, 0);
if (ctx->texm3x3pad_dst0 == -1)
{
ctx->texm3x3pad_src0 = ctx->source_args[0].regnum;
ctx->texm3x3pad_dst0 = ctx->dest_arg.regnum;
} else if (ctx->texm3x3pad_dst1 == -1)
{
ctx->texm3x3pad_src1 = ctx->source_args[0].regnum;
ctx->texm3x3pad_dst1 = ctx->dest_arg.regnum;
} }
static void state_texm3x3(Context *ctx, const char *opcode, const int dims)
{
if (shader_version_atleast(ctx, 1, 4))
failf(ctx, "%s opcode not available after Shader Model 1.3", opcode);
if (ctx->texm3x3pad_dst1 == -1)
failf(ctx, "%s opcode without matching TEXM3X3PADs", opcode);
state_texops(ctx, opcode, dims, 0);
ctx->reset_texmpad = 1;
RegisterList *sreg = reglist_find(&ctx->samplers, REG_TYPE_SAMPLER,
ctx->dest_arg.regnum);
const TextureType ttype = (TextureType) (sreg ? sreg->index : 0);
if ((ttype != TEXTURE_TYPE_VOLUME) && (ttype != TEXTURE_TYPE_CUBE))
failf(ctx, "%s needs a 3D or Cubemap sampler", opcode);
}
static void state_TEXM3X3(Context *ctx)
{
if (!shader_version_atleast(ctx, 1, 2))
fail(ctx, "TEXM3X3 opcode not available in Shader Model 1.1");
state_texm3x3(ctx, "TEXM3X3", 0);
}
static void state_TEXM3X3TEX(Context *ctx)
{
state_texm3x3(ctx, "TEXM3X3TEX", 3);
}
static void state_TEXM3X3SPEC(Context *ctx)
{
state_texm3x3(ctx, "TEXM3X3SPEC", 3);
if (ctx->source_args[1].regtype != REG_TYPE_CONST)
fail(ctx, "TEXM3X3SPEC final arg must be a constant register");
}
static void state_TEXM3X3VSPEC(Context *ctx)
{
state_texm3x3(ctx, "TEXM3X3VSPEC", 3);
}
static void state_TEXLD(Context *ctx)
{
if (shader_version_atleast(ctx, 2, 0))
{
const SourceArgInfo *src0 = &ctx->source_args[0];
const SourceArgInfo *src1 = &ctx->source_args[1];
if (src0->src_mod != SRCMOD_NONE)
fail(ctx, "TEXLD src0 must have no modifiers");
else if (src1->regtype != REG_TYPE_SAMPLER)
fail(ctx, "TEXLD src1 must be sampler register");
else if (src1->src_mod != SRCMOD_NONE)
fail(ctx, "TEXLD src1 must have no modifiers");
else if ( (ctx->instruction_controls != CONTROL_TEXLD) &&
(ctx->instruction_controls != CONTROL_TEXLDP) &&
(ctx->instruction_controls != CONTROL_TEXLDB) )
{
fail(ctx, "TEXLD has unknown control bits");
}
if (!shader_version_atleast(ctx, 3, 0))
{
if (!no_swizzle(src0->swizzle))
fail(ctx, "TEXLD src0 must not swizzle");
else if (!no_swizzle(src1->swizzle))
fail(ctx, "TEXLD src1 must not swizzle");
}
if ( ((TextureType) ctx->source_args[1].regnum) == TEXTURE_TYPE_CUBE )
ctx->instruction_count += 3;
}
else if (shader_version_atleast(ctx, 1, 4))
{
}
else
{
const DestArgInfo *info = &ctx->dest_arg;
const int sampler = info->regnum;
if (info->regtype != REG_TYPE_TEXTURE)
fail(ctx, "TEX param must be a texture register");
add_sampler(ctx, sampler, TEXTURE_TYPE_2D, 0);
} }
static void state_TEXLDL(Context *ctx)
{
if (!shader_version_atleast(ctx, 3, 0))
fail(ctx, "TEXLDL in version < Shader Model 3.0");
else if (ctx->source_args[1].regtype != REG_TYPE_SAMPLER)
fail(ctx, "TEXLDL src1 must be sampler register");
else
{
if ( ((TextureType) ctx->source_args[1].regnum) == TEXTURE_TYPE_CUBE )
ctx->instruction_count += 3;
} }
static void state_DP2ADD(Context *ctx)
{
if (!replicate_swizzle(ctx->source_args[2].swizzle))
fail(ctx, "DP2ADD src2 must have replicate swizzle");
}
typedef struct
{
const char *opcode_string;
int slots; MOJOSHADER_shaderType shader_types; args_function parse_args;
state_function state;
emit_function emitter[STATICARRAYLEN(profiles)];
} Instruction;
static const Instruction instructions[] =
{
#define INSTRUCTION_STATE(op, opstr, slots, a, t, w) { \
opstr, slots, t, parse_args_##a, state_##op, PROFILE_EMITTERS(op) \
},
#define INSTRUCTION(op, opstr, slots, a, t, w) { \
opstr, slots, t, parse_args_##a, NULL, PROFILE_EMITTERS(op) \
},
#define MOJOSHADER_DO_INSTRUCTION_TABLE 1
#include "mojoshader_internal.h"
};
static int parse_instruction_token(Context *ctx)
{
int retval = 0;
const int start_position = ctx->current_position;
const uint32 *start_tokens = ctx->tokens;
const uint32 start_tokencount = ctx->tokencount;
const uint32 token = SWAP32(*(ctx->tokens));
const uint32 opcode = (token & 0xFFFF);
const uint32 controls = ((token >> 16) & 0xFF);
const uint32 insttoks = ((token >> 24) & 0x0F);
const int coissue = (token & 0x40000000) ? 1 : 0;
const int predicated = (token & 0x10000000) ? 1 : 0;
if ( opcode >= (sizeof (instructions) / sizeof (instructions[0])) )
return 0;
const Instruction *instruction = &instructions[opcode];
const emit_function emitter = instruction->emitter[ctx->profileid];
if ((token & 0x80000000) != 0)
fail(ctx, "instruction token high bit must be zero.");
if (instruction->opcode_string == NULL)
{
fail(ctx, "Unknown opcode.");
return insttoks + 1; }
ctx->coissue = coissue;
if (coissue)
{
if (!shader_is_pixel(ctx))
fail(ctx, "coissue instruction on non-pixel shader");
if (shader_version_atleast(ctx, 2, 0))
fail(ctx, "coissue instruction in Shader Model >= 2.0");
}
if ((ctx->shader_type & instruction->shader_types) == 0)
{
failf(ctx, "opcode '%s' not available in this shader type.",
instruction->opcode_string);
}
memset(ctx->dwords, '\0', sizeof (ctx->dwords));
ctx->instruction_controls = controls;
ctx->predicated = predicated;
adjust_token_position(ctx, 1);
retval = instruction->parse_args(ctx);
if (predicated)
retval += parse_predicated_token(ctx);
ctx->tokens = start_tokens;
ctx->tokencount = start_tokencount;
ctx->current_position = start_position;
if (instruction->state != NULL)
instruction->state(ctx);
ctx->instruction_count += instruction->slots;
if (!isfail(ctx))
emitter(ctx);
if (ctx->reset_texmpad)
{
ctx->texm3x2pad_dst0 = -1;
ctx->texm3x2pad_src0 = -1;
ctx->texm3x3pad_dst0 = -1;
ctx->texm3x3pad_src0 = -1;
ctx->texm3x3pad_dst1 = -1;
ctx->texm3x3pad_src1 = -1;
ctx->reset_texmpad = 0;
}
ctx->previous_opcode = opcode;
ctx->scratch_registers = 0;
if (!shader_version_atleast(ctx, 2, 0))
{
if (insttoks != 0) fail(ctx, "instruction token count must be zero");
} else
{
if (((uint32)retval) != (insttoks+1))
{
failf(ctx, "wrong token count (%u, not %u) for opcode '%s'.",
(uint) retval, (uint) (insttoks+1),
instruction->opcode_string);
retval = insttoks + 1; } }
return retval;
}
static int parse_version_token(Context *ctx, const char *profilestr)
{
if (ctx->tokencount == 0)
{
fail(ctx, "Expected version token, got none at all.");
return 0;
}
const uint32 token = SWAP32(*(ctx->tokens));
const uint32 shadertype = ((token >> 16) & 0xFFFF);
const uint8 major = (uint8) ((token >> 8) & 0xFF);
const uint8 minor = (uint8) (token & 0xFF);
ctx->version_token = token;
if (shadertype == 0xFFFF)
{
ctx->shader_type = MOJOSHADER_TYPE_PIXEL;
ctx->shader_type_str = "ps";
} else if (shadertype == 0xFFFE)
{
ctx->shader_type = MOJOSHADER_TYPE_VERTEX;
ctx->shader_type_str = "vs";
} else {
fail(ctx, "Unsupported shader type or not a shader at all");
return -1;
}
ctx->major_ver = major;
ctx->minor_ver = minor;
if (!shader_version_supported(major, minor))
{
failf(ctx, "Shader Model %u.%u is currently unsupported.",
(uint) major, (uint) minor);
}
if (!isfail(ctx))
ctx->profile->start_emitter(ctx, profilestr);
return 1; }
static int parse_ctab_string(const uint8 *start, const uint32 bytes,
const uint32 name)
{
if (name < bytes)
{
int i;
const int slenmax = bytes - name;
const char *namestr = (const char *) (start + name);
for (i = 0; i < slenmax; i++)
{
if (namestr[i] == '\0')
return 1; } }
return 0; }
static int parse_ctab_typeinfo(Context *ctx, const uint8 *start,
const uint32 bytes, const uint32 pos,
MOJOSHADER_symbolTypeInfo *info,
const int depth)
{
if ((bytes <= pos) || ((bytes - pos) < 16))
return 0;
const uint16 *typeptr = (const uint16 *) (start + pos);
info->parameter_class = (MOJOSHADER_symbolClass) SWAP16(typeptr[0]);
info->parameter_type = (MOJOSHADER_symbolType) SWAP16(typeptr[1]);
info->rows = (unsigned int) SWAP16(typeptr[2]);
info->columns = (unsigned int) SWAP16(typeptr[3]);
info->elements = (unsigned int) SWAP16(typeptr[4]);
if (info->parameter_class >= MOJOSHADER_SYMCLASS_TOTAL)
{
failf(ctx, "Unknown parameter class (0x%X)", info->parameter_class);
info->parameter_class = MOJOSHADER_SYMCLASS_SCALAR;
}
if (info->parameter_type >= MOJOSHADER_SYMTYPE_TOTAL)
{
failf(ctx, "Unknown parameter type (0x%X)", info->parameter_type);
info->parameter_type = MOJOSHADER_SYMTYPE_INT;
}
const unsigned int member_count = (unsigned int) SWAP16(typeptr[5]);
info->member_count = 0;
info->members = NULL;
if ((pos + 16 + (member_count * 8)) >= bytes)
return 0;
if (member_count > 0)
{
if (depth > 300) {
fail(ctx, "Possible infinite loop in CTAB structure.");
return 0;
}
const size_t len = sizeof (MOJOSHADER_symbolStructMember) * member_count;
info->members = (MOJOSHADER_symbolStructMember *) Malloc(ctx, len);
if (info->members == NULL)
return 1; memset(info->members, '\0', len);
info->member_count = member_count;
}
unsigned int i;
const uint32 *member = (const uint32 *) (start + typeptr[6]);
for (i = 0; i < member_count; i++)
{
MOJOSHADER_symbolStructMember *mbr = &info->members[i];
const uint32 name = SWAP32(member[0]);
const uint32 memberinfopos = SWAP32(member[1]);
member += 2;
if (!parse_ctab_string(start, bytes, name))
return 0;
mbr->name = StrDup(ctx, (const char *) (start + name));
if (mbr->name == NULL)
return 1; if (!parse_ctab_typeinfo(ctx, start, bytes, memberinfopos, &mbr->info, depth + 1))
return 0;
if (ctx->out_of_memory)
return 1; }
return 1;
}
static void parse_constant_table(Context *ctx, const uint32 *tokens,
const uint32 bytes, const uint32 okay_version,
const int setvariables, CtabData *ctab)
{
const uint32 id = SWAP32(tokens[1]);
if (id != CTAB_ID)
return;
if (ctab->have_ctab) {
fail(ctx, "Shader has multiple CTAB sections");
return;
}
ctab->have_ctab = 1;
const uint8 *start = (uint8 *) &tokens[2];
if (bytes < 32)
{
fail(ctx, "Truncated CTAB data");
return;
}
const uint32 size = SWAP32(tokens[2]);
const uint32 creator = SWAP32(tokens[3]);
const uint32 version = SWAP32(tokens[4]);
const uint32 constants = SWAP32(tokens[5]);
const uint32 constantinfo = SWAP32(tokens[6]);
const uint32 target = SWAP32(tokens[8]);
if (size != CTAB_SIZE)
goto corrupt_ctab;
else if (constants > 1000000) goto corrupt_ctab;
if (version != okay_version) goto corrupt_ctab;
if (creator >= bytes) goto corrupt_ctab;
if (constantinfo >= bytes) goto corrupt_ctab;
if ((bytes - constantinfo) < (constants * CINFO_SIZE)) goto corrupt_ctab;
if (target >= bytes) goto corrupt_ctab;
if (!parse_ctab_string(start, bytes, target)) goto corrupt_ctab;
ctab->symbols = NULL;
if (constants > 0)
{
ctab->symbols = (MOJOSHADER_symbol *) Malloc(ctx, sizeof (MOJOSHADER_symbol) * constants);
if (ctab->symbols == NULL)
return;
memset(ctab->symbols, '\0', sizeof (MOJOSHADER_symbol) * constants);
} ctab->symbol_count = constants;
uint32 i = 0;
for (i = 0; i < constants; i++)
{
const uint8 *ptr = start + constantinfo + (i * CINFO_SIZE);
const uint32 name = SWAP32(*((uint32 *) (ptr + 0)));
const uint16 regset = SWAP16(*((uint16 *) (ptr + 4)));
const uint16 regidx = SWAP16(*((uint16 *) (ptr + 6)));
const uint16 regcnt = SWAP16(*((uint16 *) (ptr + 8)));
const uint32 typeinf = SWAP32(*((uint32 *) (ptr + 12)));
const uint32 defval = SWAP32(*((uint32 *) (ptr + 16)));
MOJOSHADER_uniformType mojotype = MOJOSHADER_UNIFORM_UNKNOWN;
if (!parse_ctab_string(start, bytes, name)) goto corrupt_ctab;
if (defval >= bytes) goto corrupt_ctab;
switch (regset)
{
case 0: mojotype = MOJOSHADER_UNIFORM_BOOL; break;
case 1: mojotype = MOJOSHADER_UNIFORM_INT; break;
case 2: mojotype = MOJOSHADER_UNIFORM_FLOAT; break;
case 3: break;
default: goto corrupt_ctab;
}
if ((setvariables) && (mojotype != MOJOSHADER_UNIFORM_UNKNOWN))
{
VariableList *item;
item = (VariableList *) Malloc(ctx, sizeof (VariableList));
if (item != NULL)
{
item->type = mojotype;
item->index = regidx;
item->count = regcnt;
item->constant = NULL;
item->used = 0;
item->emit_position = -1;
item->next = ctx->variables;
ctx->variables = item;
} }
const char *namecpy = StrDup(ctx, (const char *) (start + name));
if (namecpy == NULL)
return;
MOJOSHADER_symbol *sym = &ctab->symbols[i];
sym->name = namecpy;
sym->register_set = (MOJOSHADER_symbolRegisterSet) regset;
sym->register_index = (unsigned int) regidx;
sym->register_count = (unsigned int) regcnt;
if (!parse_ctab_typeinfo(ctx, start, bytes, typeinf, &sym->info, 0))
goto corrupt_ctab; else if (ctx->out_of_memory)
return; }
return;
corrupt_ctab:
fail(ctx, "Shader has corrupt CTAB data");
}
static void free_symbols(MOJOSHADER_free f, void *d, MOJOSHADER_symbol *syms,
const int symcount);
static int is_comment_token(Context *ctx, const uint32 tok, uint32 *tokcount)
{
const uint32 token = SWAP32(tok);
if ((token & 0xFFFF) == 0xFFFE) {
if ((token & 0x80000000) != 0)
fail(ctx, "comment token high bit must be zero."); *tokcount = ((token >> 16) & 0xFFFF);
return 1;
}
return 0;
}
typedef struct PreshaderBlockInfo
{
const uint32 *tokens;
uint32 tokcount;
int seen;
} PreshaderBlockInfo;
static void parse_preshader(Context *ctx, const uint32 *tokens, uint32 tokcount)
{
#ifndef MOJOSHADER_EFFECT_SUPPORT
fail(ctx, "Preshader found, but effect support is disabled!");
#else
uint32 i;
assert(ctx->have_preshader == 0); ctx->have_preshader = 1;
const uint32 version_magic = 0x46580000;
const uint32 min_version = 0x00000200 | version_magic;
const uint32 max_version = 0x00000201 | version_magic;
const uint32 version = SWAP32(tokens[0]);
if (version < min_version || version > max_version)
{
fail(ctx, "Unsupported preshader version.");
return; }
tokens++;
tokcount--;
PreshaderBlockInfo ctab = { 0, 0, 0 };
PreshaderBlockInfo prsi = { 0, 0, 0 };
PreshaderBlockInfo fxlc = { 0, 0, 0 };
PreshaderBlockInfo clit = { 0, 0, 0 };
while (tokcount > 0)
{
uint32 subtokcount = 0;
if ( (!is_comment_token(ctx, *tokens, &subtokcount)) ||
(subtokcount > tokcount) )
{
if (SWAP32(*tokens) == 0xFFFF)
break;
fail(ctx, "Bogus preshader data.");
return;
}
tokens++;
tokcount--;
const uint32 *nexttokens = tokens + subtokcount;
const uint32 nexttokcount = tokcount - subtokcount;
if (subtokcount > 0)
{
switch (SWAP32(*tokens))
{
#define PRESHADER_BLOCK_CASE(id, var) \
case id##_ID: { \
if (var.seen) { \
fail(ctx, "Multiple " #id " preshader blocks."); \
return; \
} \
var.tokens = tokens; \
var.tokcount = subtokcount; \
var.seen = 1; \
break; \
}
PRESHADER_BLOCK_CASE(CTAB, ctab);
PRESHADER_BLOCK_CASE(PRSI, prsi);
PRESHADER_BLOCK_CASE(FXLC, fxlc);
PRESHADER_BLOCK_CASE(CLIT, clit);
default: fail(ctx, "Bogus preshader section."); return;
#undef PRESHADER_BLOCK_CASE
} }
tokens = nexttokens;
tokcount = nexttokcount;
}
if (!ctab.seen) { fail(ctx, "No CTAB block in preshader."); return; }
if (!fxlc.seen) { fail(ctx, "No FXLC block in preshader."); return; }
if (!clit.seen) { fail(ctx, "No CLIT block in preshader."); return; }
MOJOSHADER_preshader *preshader = (MOJOSHADER_preshader *)
Malloc(ctx, sizeof (MOJOSHADER_preshader));
if (preshader == NULL)
return;
memset(preshader, '\0', sizeof (MOJOSHADER_preshader));
preshader->malloc = ctx->malloc;
preshader->free = ctx->free;
preshader->malloc_data = ctx->malloc_data;
ctx->preshader = preshader;
if (clit.tokcount == 0)
fail(ctx, "Bogus CLIT block in preshader.");
else
{
const uint32 lit_count = SWAP32(clit.tokens[1]);
if (lit_count > ((clit.tokcount - 2) / 2))
{
fail(ctx, "Bogus CLIT block in preshader.");
return;
} else if (lit_count > 0)
{
preshader->literal_count = (unsigned int) lit_count;
assert(sizeof (double) == 8); const size_t len = sizeof (double) * lit_count;
preshader->literals = (double *) Malloc(ctx, len);
if (preshader->literals == NULL)
return; const double *litptr = (const double *) (clit.tokens + 2);
memcpy(preshader->literals, litptr, len);
} }
uint32 output_map_count = 0;
const uint32 *output_map = NULL;
if (prsi.seen)
{
if (prsi.tokcount < 8)
{
fail(ctx, "Bogus preshader PRSI data");
return;
}
output_map_count = SWAP32(prsi.tokens[7]);
prsi.tokcount -= 8;
prsi.tokens += 8;
if (prsi.tokcount < ((output_map_count + 1) * 2))
{
fail(ctx, "Bogus preshader PRSI data");
return;
}
output_map = prsi.tokens;
}
CtabData ctabdata = { 0, 0, 0 };
parse_constant_table(ctx, ctab.tokens - 1, ctab.tokcount * 4,
version, 0, &ctabdata);
preshader->symbol_count = ctabdata.symbol_count;
preshader->symbols = ctabdata.symbols;
if (!ctabdata.have_ctab)
{
fail(ctx, "Bogus preshader CTAB data");
return;
}
uint32 opcode_count = SWAP32(fxlc.tokens[1]);
const size_t len = sizeof (MOJOSHADER_preshaderInstruction) * opcode_count;
preshader->instruction_count = (unsigned int) opcode_count;
preshader->instructions = (MOJOSHADER_preshaderInstruction *) Malloc(ctx, len);
if (preshader->instructions == NULL)
return;
memset(preshader->instructions, '\0', len);
fxlc.tokens += 2;
fxlc.tokcount -= 2;
if (opcode_count > (fxlc.tokcount / 2))
{
fail(ctx, "Bogus preshader FXLC block.");
return;
}
MOJOSHADER_preshaderInstruction *inst = preshader->instructions;
while (opcode_count--)
{
const uint32 opcodetok = SWAP32(fxlc.tokens[0]);
MOJOSHADER_preshaderOpcode opcode = MOJOSHADER_PRESHADEROP_NOP;
switch ((opcodetok >> 16) & 0xFFFF)
{
case 0x1000: opcode = MOJOSHADER_PRESHADEROP_MOV; break;
case 0x1010: opcode = MOJOSHADER_PRESHADEROP_NEG; break;
case 0x1030: opcode = MOJOSHADER_PRESHADEROP_RCP; break;
case 0x1040: opcode = MOJOSHADER_PRESHADEROP_FRC; break;
case 0x1050: opcode = MOJOSHADER_PRESHADEROP_EXP; break;
case 0x1060: opcode = MOJOSHADER_PRESHADEROP_LOG; break;
case 0x1070: opcode = MOJOSHADER_PRESHADEROP_RSQ; break;
case 0x1080: opcode = MOJOSHADER_PRESHADEROP_SIN; break;
case 0x1090: opcode = MOJOSHADER_PRESHADEROP_COS; break;
case 0x10A0: opcode = MOJOSHADER_PRESHADEROP_ASIN; break;
case 0x10B0: opcode = MOJOSHADER_PRESHADEROP_ACOS; break;
case 0x10C0: opcode = MOJOSHADER_PRESHADEROP_ATAN; break;
case 0x2000: opcode = MOJOSHADER_PRESHADEROP_MIN; break;
case 0x2010: opcode = MOJOSHADER_PRESHADEROP_MAX; break;
case 0x2020: opcode = MOJOSHADER_PRESHADEROP_LT; break;
case 0x2030: opcode = MOJOSHADER_PRESHADEROP_GE; break;
case 0x2040: opcode = MOJOSHADER_PRESHADEROP_ADD; break;
case 0x2050: opcode = MOJOSHADER_PRESHADEROP_MUL; break;
case 0x2060: opcode = MOJOSHADER_PRESHADEROP_ATAN2; break;
case 0x2080: opcode = MOJOSHADER_PRESHADEROP_DIV; break;
case 0x3000: opcode = MOJOSHADER_PRESHADEROP_CMP; break;
case 0x3010: opcode = MOJOSHADER_PRESHADEROP_MOVC; break;
case 0x5000: opcode = MOJOSHADER_PRESHADEROP_DOT; break;
case 0x5020: opcode = MOJOSHADER_PRESHADEROP_NOISE; break;
case 0xA000: opcode = MOJOSHADER_PRESHADEROP_MIN_SCALAR; break;
case 0xA010: opcode = MOJOSHADER_PRESHADEROP_MAX_SCALAR; break;
case 0xA020: opcode = MOJOSHADER_PRESHADEROP_LT_SCALAR; break;
case 0xA030: opcode = MOJOSHADER_PRESHADEROP_GE_SCALAR; break;
case 0xA040: opcode = MOJOSHADER_PRESHADEROP_ADD_SCALAR; break;
case 0xA050: opcode = MOJOSHADER_PRESHADEROP_MUL_SCALAR; break;
case 0xA060: opcode = MOJOSHADER_PRESHADEROP_ATAN2_SCALAR; break;
case 0xA080: opcode = MOJOSHADER_PRESHADEROP_DIV_SCALAR; break;
case 0xD000: opcode = MOJOSHADER_PRESHADEROP_DOT_SCALAR; break;
case 0xD020: opcode = MOJOSHADER_PRESHADEROP_NOISE_SCALAR; break;
default: fail(ctx, "Unknown preshader opcode."); break;
}
uint32 operand_count = SWAP32(fxlc.tokens[1]) + 1;
inst->opcode = opcode;
inst->element_count = (unsigned int) (opcodetok & 0xFF);
inst->operand_count = (unsigned int) operand_count;
fxlc.tokens += 2;
fxlc.tokcount -= 2;
if ((operand_count * 3) > fxlc.tokcount)
{
fail(ctx, "Bogus preshader FXLC block.");
return;
}
MOJOSHADER_preshaderOperand *operand = inst->operands;
while (operand_count--)
{
const unsigned int item = (unsigned int) SWAP32(fxlc.tokens[2]);
const uint32 numarrays = SWAP32(fxlc.tokens[0]);
switch (SWAP32(fxlc.tokens[1]))
{
case 1: {
if (item > preshader->literal_count)
{
fail(ctx, "Bogus preshader literal index.");
break;
} operand->type = MOJOSHADER_PRESHADEROPERAND_LITERAL;
break;
}
case 2: {
MOJOSHADER_symbol *sym = ctabdata.symbols;
const uint32 symcount = (uint32) ctabdata.symbol_count;
for (i = 0; i < symcount; i++, sym++)
{
const uint32 base = sym->register_index * 4;
const uint32 count = sym->register_count * 4;
assert(sym->register_set==MOJOSHADER_SYMREGSET_FLOAT4);
if ( (base <= item) && ((base + count) > item) )
break;
} if (i == ctabdata.symbol_count)
{
fail(ctx, "Bogus preshader input index.");
break;
} operand->type = MOJOSHADER_PRESHADEROPERAND_INPUT;
if (numarrays > 0)
{
const uint32 siz = numarrays * sizeof (uint32);
operand->array_register_count = numarrays;
operand->array_registers = (uint32 *) Malloc(ctx, siz);
memset(operand->array_registers, '\0', siz);
for (i = 0; i < numarrays; i++)
{
const uint32 jmp = SWAP32(fxlc.tokens[4]);
const uint32 bigjmp = (jmp >> 4) * 4;
const uint32 ltljmp = (jmp >> 2) & 3;
operand->array_registers[i] = bigjmp + ltljmp;
fxlc.tokens += 2;
fxlc.tokcount -= 2;
} } break;
}
case 4:
{
operand->type = MOJOSHADER_PRESHADEROPERAND_OUTPUT;
for (i = 0; i < output_map_count; i++)
{
const uint32 base = output_map[(i*2)] * 4;
const uint32 count = output_map[(i*2)+1] * 4;
if ( (base <= item) && ((base + count) > item) )
break;
}
if (i == output_map_count)
{
if (prsi.seen) fail(ctx, "Bogus preshader output index.");
}
break;
}
case 7:
{
operand->type = MOJOSHADER_PRESHADEROPERAND_TEMP;
if (item >= preshader->temp_count)
preshader->temp_count = item + 1;
break;
}
default:
assert(0 && "Unhandled fxlc.tokens[1] in parse_preshader!");
break;
}
operand->index = item;
fxlc.tokens += 3;
fxlc.tokcount -= 3;
operand++;
}
inst++;
}
preshader->temp_count = (preshader->temp_count + 3) & ~3;
unsigned int largest = 0;
const MOJOSHADER_symbol *sym = preshader->symbols;
const uint32 symcount = (uint32) preshader->symbol_count;
for (i = 0; i < symcount; i++, sym++)
{
const unsigned int val = sym->register_index + sym->register_count;
if (val > largest)
largest = val;
}
if (largest > 0)
{
const size_t len = largest * sizeof (float) * 4;
preshader->registers = (float *) Malloc(ctx, len);
memset(preshader->registers, '\0', len);
preshader->register_count = largest;
} #endif
}
static int parse_comment_token(Context *ctx)
{
uint32 commenttoks = 0;
if (is_comment_token(ctx, *ctx->tokens, &commenttoks))
{
if ((commenttoks >= 2) && (commenttoks < ctx->tokencount))
{
const uint32 id = SWAP32(ctx->tokens[1]);
if (id == PRES_ID)
parse_preshader(ctx, ctx->tokens + 2, commenttoks - 2);
else if (id == CTAB_ID)
{
parse_constant_table(ctx, ctx->tokens, commenttoks * 4,
ctx->version_token, 1, &ctx->ctab);
} } return commenttoks + 1; }
return 0; }
static int parse_end_token(Context *ctx)
{
if (SWAP32(*(ctx->tokens)) != 0x0000FFFF) return 0;
if (!ctx->know_shader_size) ctx->tokencount = 1;
else if (ctx->tokencount != 1) fail(ctx, "end token before end of stream");
if (!isfail(ctx))
ctx->profile->end_emitter(ctx);
return 1;
}
static int parse_phase_token(Context *ctx)
{
if (SWAP32(*(ctx->tokens)) != 0x0000FFFD) return 0;
if ( (!shader_is_pixel(ctx)) || (!shader_version_exactly(ctx, 1, 4)) )
fail(ctx, "phase token only available in 1.4 pixel shaders");
if (!isfail(ctx))
ctx->profile->phase_emitter(ctx);
return 1;
}
static int parse_token(Context *ctx)
{
int rc = 0;
assert(ctx->output_stack_len == 0);
if (ctx->tokencount == 0)
fail(ctx, "unexpected end of shader.");
else if ((rc = parse_comment_token(ctx)) != 0)
return rc;
else if ((rc = parse_end_token(ctx)) != 0)
return rc;
else if ((rc = parse_phase_token(ctx)) != 0)
return rc;
else if ((rc = parse_instruction_token(ctx)) != 0)
return rc;
failf(ctx, "unknown token (0x%x)", (uint) *ctx->tokens);
return 1; }
static int find_profile_id(const char *profile)
{
size_t i;
for (i = 0; i < STATICARRAYLEN(profileMap); i++)
{
const char *name = profileMap[i].from;
if (strcmp(name, profile) == 0)
{
profile = profileMap[i].to;
break;
} }
for (i = 0; i < STATICARRAYLEN(profiles); i++)
{
const char *name = profiles[i].name;
if (strcmp(name, profile) == 0)
return i;
}
return -1; }
static Context *build_context(const char *profile,
const char *mainfn,
const unsigned char *tokenbuf,
const unsigned int bufsize,
const MOJOSHADER_swizzle *swiz,
const unsigned int swizcount,
const MOJOSHADER_samplerMap *smap,
const unsigned int smapcount,
MOJOSHADER_malloc m, MOJOSHADER_free f, void *d)
{
if (m == NULL) m = MOJOSHADER_internal_malloc;
if (f == NULL) f = MOJOSHADER_internal_free;
Context *ctx = (Context *) m(sizeof (Context), d);
if (ctx == NULL)
return NULL;
memset(ctx, '\0', sizeof (Context));
ctx->malloc = m;
ctx->free = f;
ctx->malloc_data = d;
ctx->tokens = (const uint32 *) tokenbuf;
ctx->orig_tokens = (const uint32 *) tokenbuf;
ctx->know_shader_size = (bufsize != 0);
ctx->tokencount = ctx->know_shader_size ? (bufsize / sizeof (uint32)) : 0xFFFFFFFF;
ctx->swizzles = swiz;
ctx->swizzles_count = swizcount;
ctx->samplermap = smap;
ctx->samplermap_count = smapcount;
ctx->endline = ENDLINE_STR;
ctx->endline_len = strlen(ctx->endline);
ctx->last_address_reg_component = -1;
ctx->current_position = MOJOSHADER_POSITION_BEFORE;
ctx->texm3x2pad_dst0 = -1;
ctx->texm3x2pad_src0 = -1;
ctx->texm3x3pad_dst0 = -1;
ctx->texm3x3pad_src0 = -1;
ctx->texm3x3pad_dst1 = -1;
ctx->texm3x3pad_src1 = -1;
ctx->errors = errorlist_create(MallocBridge, FreeBridge, ctx);
if (ctx->errors == NULL)
{
f(ctx, d);
return NULL;
}
if (!set_output(ctx, &ctx->mainline))
{
errorlist_destroy(ctx->errors);
f(ctx, d);
return NULL;
}
if (mainfn != NULL)
{
if (strlen(mainfn) > 55) failf(ctx, "Main function name '%s' is too big", mainfn);
else
ctx->mainfn = StrDup(ctx, mainfn);
}
if (profile != NULL)
{
const int profileid = find_profile_id(profile);
ctx->profileid = profileid;
if (profileid >= 0)
ctx->profile = &profiles[profileid];
else
failf(ctx, "Profile '%s' is unknown or unsupported", profile);
}
return ctx;
}
static void free_constants_list(MOJOSHADER_free f, void *d, ConstantsList *item)
{
while (item != NULL)
{
ConstantsList *next = item->next;
f(item, d);
item = next;
} }
static void free_variable_list(MOJOSHADER_free f, void *d, VariableList *item)
{
while (item != NULL)
{
VariableList *next = item->next;
f(item, d);
item = next;
} }
static void free_sym_typeinfo(MOJOSHADER_free f, void *d,
MOJOSHADER_symbolTypeInfo *typeinfo)
{
unsigned int i;
for (i = 0; i < typeinfo->member_count; i++)
{
f((void *) typeinfo->members[i].name, d);
free_sym_typeinfo(f, d, &typeinfo->members[i].info);
} f((void *) typeinfo->members, d);
}
static void free_symbols(MOJOSHADER_free f, void *d, MOJOSHADER_symbol *syms,
const int symcount)
{
int i;
for (i = 0; i < symcount; i++)
{
f((void *) syms[i].name, d);
free_sym_typeinfo(f, d, &syms[i].info);
} f((void *) syms, d);
}
static void destroy_context(Context *ctx)
{
if (ctx != NULL)
{
MOJOSHADER_free f = ((ctx->free != NULL) ? ctx->free : MOJOSHADER_internal_free);
void *d = ctx->malloc_data;
buffer_destroy(ctx->preflight);
buffer_destroy(ctx->globals);
buffer_destroy(ctx->inputs);
buffer_destroy(ctx->outputs);
buffer_destroy(ctx->helpers);
buffer_destroy(ctx->subroutines);
buffer_destroy(ctx->mainline_intro);
buffer_destroy(ctx->mainline_arguments);
buffer_destroy(ctx->mainline_top);
buffer_destroy(ctx->mainline);
buffer_destroy(ctx->postflight);
buffer_destroy(ctx->ignore);
free_constants_list(f, d, ctx->constants);
free_reglist(f, d, ctx->used_registers.next);
free_reglist(f, d, ctx->defined_registers.next);
free_reglist(f, d, ctx->uniforms.next);
free_reglist(f, d, ctx->attributes.next);
free_reglist(f, d, ctx->samplers.next);
free_variable_list(f, d, ctx->variables);
errorlist_destroy(ctx->errors);
free_symbols(f, d, ctx->ctab.symbols, ctx->ctab.symbol_count);
MOJOSHADER_freePreshader(ctx->preshader);
f((void *) ctx->mainfn, d);
f(ctx, d);
} }
static char *build_output(Context *ctx, size_t *len)
{
Buffer *buffers[] = {
ctx->preflight, ctx->globals, ctx->inputs, ctx->outputs, ctx->helpers,
ctx->subroutines, ctx->mainline_intro, ctx->mainline_arguments,
ctx->mainline_top, ctx->mainline, ctx->postflight
};
char *retval = buffer_merge(buffers, STATICARRAYLEN(buffers), len);
return retval;
}
static inline const char *alloc_varname(Context *ctx, const RegisterList *reg)
{
return ctx->profile->get_varname(ctx, reg->regtype, reg->regnum);
}
static MOJOSHADER_uniform *build_uniforms(Context *ctx)
{
const size_t len = sizeof (MOJOSHADER_uniform) * ctx->uniform_count;
MOJOSHADER_uniform *retval = (MOJOSHADER_uniform *) Malloc(ctx, len);
if (retval != NULL)
{
MOJOSHADER_uniform *wptr = retval;
memset(wptr, '\0', len);
VariableList *var;
int written = 0;
for (var = ctx->variables; var != NULL; var = var->next)
{
if (var->used)
{
const char *name = ctx->profile->get_const_array_varname(ctx,
var->index, var->count);
if (name != NULL)
{
wptr->type = MOJOSHADER_UNIFORM_FLOAT;
wptr->index = var->index;
wptr->array_count = var->count;
wptr->constant = (var->constant != NULL) ? 1 : 0;
wptr->name = name;
wptr++;
written++;
} } }
RegisterList *item = ctx->uniforms.next;
MOJOSHADER_uniformType type = MOJOSHADER_UNIFORM_FLOAT;
while (written < ctx->uniform_count)
{
int skip = 0;
if (item == NULL)
{
fail(ctx, "BUG: mismatched uniform list and count");
break;
}
int index = item->regnum;
switch (item->regtype)
{
case REG_TYPE_CONST:
skip = (item->array != NULL);
type = MOJOSHADER_UNIFORM_FLOAT;
break;
case REG_TYPE_CONSTINT:
type = MOJOSHADER_UNIFORM_INT;
break;
case REG_TYPE_CONSTBOOL:
type = MOJOSHADER_UNIFORM_BOOL;
break;
default:
fail(ctx, "unknown uniform datatype");
break;
}
if (!skip)
{
wptr->type = type;
wptr->index = index;
wptr->array_count = 0;
wptr->name = alloc_varname(ctx, item);
wptr++;
written++;
}
item = item->next;
} }
return retval;
}
static MOJOSHADER_constant *build_constants(Context *ctx)
{
const size_t len = sizeof (MOJOSHADER_constant) * ctx->constant_count;
MOJOSHADER_constant *retval = (MOJOSHADER_constant *) Malloc(ctx, len);
if (retval != NULL)
{
ConstantsList *item = ctx->constants;
int i;
for (i = 0; i < ctx->constant_count; i++)
{
if (item == NULL)
{
fail(ctx, "BUG: mismatched constant list and count");
break;
}
memcpy(&retval[i], &item->constant, sizeof (MOJOSHADER_constant));
item = item->next;
} }
return retval;
}
static MOJOSHADER_sampler *build_samplers(Context *ctx)
{
const size_t len = sizeof (MOJOSHADER_sampler) * ctx->sampler_count;
MOJOSHADER_sampler *retval = (MOJOSHADER_sampler *) Malloc(ctx, len);
if (retval != NULL)
{
RegisterList *item = ctx->samplers.next;
int i;
memset(retval, '\0', len);
for (i = 0; i < ctx->sampler_count; i++)
{
if (item == NULL)
{
fail(ctx, "BUG: mismatched sampler list and count");
break;
}
assert(item->regtype == REG_TYPE_SAMPLER);
retval[i].type = cvtD3DToMojoSamplerType((TextureType) item->index);
retval[i].index = item->regnum;
retval[i].name = alloc_varname(ctx, item);
retval[i].texbem = (item->misc != 0) ? 1 : 0;
item = item->next;
} }
return retval;
}
static MOJOSHADER_attribute *build_attributes(Context *ctx, int *_count)
{
int count = 0;
if (ctx->attribute_count == 0)
{
*_count = 0;
return NULL; }
const size_t len = sizeof (MOJOSHADER_attribute) * ctx->attribute_count;
MOJOSHADER_attribute *retval = (MOJOSHADER_attribute *) Malloc(ctx, len);
if (retval != NULL)
{
RegisterList *item = ctx->attributes.next;
MOJOSHADER_attribute *wptr = retval;
int ignore = 0;
int i;
memset(retval, '\0', len);
for (i = 0; i < ctx->attribute_count; i++)
{
if (item == NULL)
{
fail(ctx, "BUG: mismatched attribute list and count");
break;
}
switch (item->regtype)
{
case REG_TYPE_RASTOUT:
case REG_TYPE_ATTROUT:
case REG_TYPE_TEXCRDOUT:
case REG_TYPE_COLOROUT:
case REG_TYPE_DEPTHOUT:
ignore = 1;
break;
default:
ignore = 0;
break;
}
if (!ignore)
{
wptr->usage = item->usage;
wptr->index = item->index;
wptr->name = alloc_varname(ctx, item);
wptr++;
count++;
}
item = item->next;
} }
*_count = count;
return retval;
}
static MOJOSHADER_attribute *build_outputs(Context *ctx, int *_count)
{
int count = 0;
if (ctx->attribute_count == 0)
{
*_count = 0;
return NULL; }
const size_t len = sizeof (MOJOSHADER_attribute) * ctx->attribute_count;
MOJOSHADER_attribute *retval = (MOJOSHADER_attribute *) Malloc(ctx, len);
if (retval != NULL)
{
RegisterList *item = ctx->attributes.next;
MOJOSHADER_attribute *wptr = retval;
int i;
memset(retval, '\0', len);
for (i = 0; i < ctx->attribute_count; i++)
{
if (item == NULL)
{
fail(ctx, "BUG: mismatched attribute list and count");
break;
}
switch (item->regtype)
{
case REG_TYPE_RASTOUT:
case REG_TYPE_ATTROUT:
case REG_TYPE_TEXCRDOUT:
case REG_TYPE_COLOROUT:
case REG_TYPE_DEPTHOUT:
wptr->usage = item->usage;
wptr->index = item->index;
wptr->name = alloc_varname(ctx, item);
wptr++;
count++;
break;
default:
break;
}
item = item->next;
} }
*_count = count;
return retval;
}
static MOJOSHADER_parseData *build_parsedata(Context *ctx)
{
char *output = NULL;
MOJOSHADER_constant *constants = NULL;
MOJOSHADER_uniform *uniforms = NULL;
MOJOSHADER_attribute *attributes = NULL;
MOJOSHADER_attribute *outputs = NULL;
MOJOSHADER_sampler *samplers = NULL;
MOJOSHADER_swizzle *swizzles = NULL;
MOJOSHADER_error *errors = NULL;
MOJOSHADER_parseData *retval = NULL;
size_t output_len = 0;
int attribute_count = 0;
int output_count = 0;
if (ctx->out_of_memory)
return &MOJOSHADER_out_of_mem_data;
retval = (MOJOSHADER_parseData*) Malloc(ctx, sizeof(MOJOSHADER_parseData));
if (retval == NULL)
return &MOJOSHADER_out_of_mem_data;
memset(retval, '\0', sizeof (MOJOSHADER_parseData));
if (!isfail(ctx))
output = build_output(ctx, &output_len);
if (!isfail(ctx))
constants = build_constants(ctx);
if (!isfail(ctx))
uniforms = build_uniforms(ctx);
if (!isfail(ctx))
attributes = build_attributes(ctx, &attribute_count);
if (!isfail(ctx))
outputs = build_outputs(ctx, &output_count);
if (!isfail(ctx))
samplers = build_samplers(ctx);
const int error_count = errorlist_count(ctx->errors);
errors = errorlist_flatten(ctx->errors);
if (!isfail(ctx))
{
if (ctx->swizzles_count > 0)
{
const int len = ctx->swizzles_count * sizeof (MOJOSHADER_swizzle);
swizzles = (MOJOSHADER_swizzle *) Malloc(ctx, len);
if (swizzles != NULL)
memcpy(swizzles, ctx->swizzles, len);
} }
if (isfail(ctx))
{
int i;
Free(ctx, output);
Free(ctx, constants);
Free(ctx, swizzles);
if (uniforms != NULL)
{
for (i = 0; i < ctx->uniform_count; i++)
Free(ctx, (void *) uniforms[i].name);
Free(ctx, uniforms);
}
if (attributes != NULL)
{
for (i = 0; i < attribute_count; i++)
Free(ctx, (void *) attributes[i].name);
Free(ctx, attributes);
}
if (outputs != NULL)
{
for (i = 0; i < output_count; i++)
Free(ctx, (void *) outputs[i].name);
Free(ctx, outputs);
}
if (samplers != NULL)
{
for (i = 0; i < ctx->sampler_count; i++)
Free(ctx, (void *) samplers[i].name);
Free(ctx, samplers);
}
if (ctx->out_of_memory)
{
for (i = 0; i < error_count; i++)
{
Free(ctx, (void *) errors[i].filename);
Free(ctx, (void *) errors[i].error);
} Free(ctx, errors);
Free(ctx, retval);
return &MOJOSHADER_out_of_mem_data;
} } else
{
retval->profile = ctx->profile->name;
retval->output = output;
retval->output_len = (int) output_len;
retval->instruction_count = ctx->instruction_count;
retval->shader_type = ctx->shader_type;
retval->major_ver = (int) ctx->major_ver;
retval->minor_ver = (int) ctx->minor_ver;
retval->uniform_count = ctx->uniform_count;
retval->uniforms = uniforms;
retval->constant_count = ctx->constant_count;
retval->constants = constants;
retval->sampler_count = ctx->sampler_count;
retval->samplers = samplers;
retval->attribute_count = attribute_count;
retval->attributes = attributes;
retval->output_count = output_count;
retval->outputs = outputs;
retval->swizzle_count = ctx->swizzles_count;
retval->swizzles = swizzles;
retval->symbol_count = ctx->ctab.symbol_count;
retval->symbols = ctx->ctab.symbols;
retval->preshader = ctx->preshader;
retval->mainfn = ctx->mainfn;
#if SUPPORT_PROFILE_SPIRV
if (strcmp(retval->profile, MOJOSHADER_PROFILE_SPIRV) == 0
|| strcmp(retval->profile, MOJOSHADER_PROFILE_GLSPIRV) == 0)
{
size_t i, max;
int binary_size = retval->output_len - sizeof(SpirvPatchTable);
uint32 *binary = (uint32 *) retval->output;
SpirvPatchTable *table = (SpirvPatchTable *) &retval->output[binary_size];
if (table->vpflip.offset) binary[table->vpflip.offset] = table->vpflip.location;
if (table->array_vec4.offset) binary[table->array_vec4.offset] = table->array_vec4.location;
if (table->array_ivec4.offset) binary[table->array_ivec4.offset] = table->array_ivec4.location;
if (table->array_bool.offset) binary[table->array_bool.offset] = table->array_bool.location;
for (i = 0, max = STATICARRAYLEN(table->samplers); i < max; i++)
{
SpirvPatchEntry entry = table->samplers[i];
if (entry.offset)
binary[entry.offset] = entry.location;
} } #endif
ctx->ctab.symbols = NULL;
ctx->preshader = NULL;
ctx->ctab.symbol_count = 0;
ctx->mainfn = NULL;
}
retval->error_count = error_count;
retval->errors = errors;
retval->malloc = (ctx->malloc == MOJOSHADER_internal_malloc) ? NULL : ctx->malloc;
retval->free = (ctx->free == MOJOSHADER_internal_free) ? NULL : ctx->free;
retval->malloc_data = ctx->malloc_data;
return retval;
}
static void process_definitions(Context *ctx)
{
determine_constants_arrays(ctx);
RegisterList *uitem = &ctx->uniforms;
RegisterList *prev = &ctx->used_registers;
RegisterList *item = prev->next;
while (item != NULL)
{
RegisterList *next = item->next;
const RegisterType regtype = item->regtype;
const int regnum = item->regnum;
MOJOSHADER_usage usage;
if (!get_defined_register(ctx, regtype, regnum))
{
switch (regtype)
{
case REG_TYPE_RASTOUT:
case REG_TYPE_ATTROUT:
case REG_TYPE_TEXCRDOUT:
case REG_TYPE_COLOROUT:
case REG_TYPE_DEPTHOUT:
if (shader_is_vertex(ctx)&&shader_version_atleast(ctx,3,0))
{
fail(ctx, "vs_3 can't use output registers"
" without declaring them first.");
return;
}
if (regtype == REG_TYPE_RASTOUT)
{
if ((RastOutType) regnum == RASTOUT_TYPE_POSITION)
usage = MOJOSHADER_USAGE_POSITION;
else if ((RastOutType) regnum == RASTOUT_TYPE_FOG)
usage = MOJOSHADER_USAGE_FOG;
else if ((RastOutType) regnum==RASTOUT_TYPE_POINT_SIZE)
usage = MOJOSHADER_USAGE_POINTSIZE;
} else if (regtype == REG_TYPE_ATTROUT ||
regtype == REG_TYPE_COLOROUT)
{
usage = MOJOSHADER_USAGE_COLOR;
} else if (regtype == REG_TYPE_TEXCRDOUT)
usage = MOJOSHADER_USAGE_TEXCOORD;
else if (regtype == REG_TYPE_DEPTHOUT)
usage = MOJOSHADER_USAGE_DEPTH;
add_attribute_register(ctx, regtype, regnum, usage,
regnum, 0xF, 0);
break;
case REG_TYPE_ADDRESS:
case REG_TYPE_PREDICATE:
case REG_TYPE_TEMP:
case REG_TYPE_LOOP:
case REG_TYPE_LABEL:
ctx->profile->global_emitter(ctx, regtype, regnum);
break;
case REG_TYPE_CONST:
case REG_TYPE_CONSTINT:
case REG_TYPE_CONSTBOOL:
prev->next = next;
item->next = NULL;
uitem->next = item;
uitem = item;
item = prev;
break;
case REG_TYPE_INPUT:
if (!shader_version_atleast(ctx,2,0))
{
if (shader_is_pixel(ctx))
{
add_attribute_register(ctx, regtype, regnum,
MOJOSHADER_USAGE_COLOR, regnum,
0xF, 0);
break;
} else if (shader_is_vertex(ctx))
{
int index = 0;
shader_model_1_input_usage(regnum, &usage, &index);
if (usage != MOJOSHADER_USAGE_UNKNOWN)
{
add_attribute_register(ctx, regtype, regnum, usage, index, 0xF, 0);
break;
} } }
default:
fail(ctx, "BUG: we used a register we don't know how to define.");
} }
prev = item;
item = next;
}
for (VariableList *var = ctx->variables; var != NULL; var = var->next)
{
if (var->used)
{
if (var->constant)
{
ctx->profile->const_array_emitter(ctx, var->constant,
var->index, var->count);
} else
{
ctx->profile->array_emitter(ctx, var);
ctx->uniform_float4_count += var->count;
} ctx->uniform_count++;
} }
for (item = ctx->uniforms.next; item != NULL; item = item->next)
{
int arraysize = -1;
VariableList *var = NULL;
if (item->regtype == REG_TYPE_CONST)
{
for (var = ctx->variables; var != NULL; var = var->next)
{
if (!var->used)
continue;
const int regnum = item->regnum;
const int lo = var->index;
if ( (regnum >= lo) && (regnum < (lo + var->count)) )
{
assert(!var->constant);
item->array = var; arraysize = var->count;
break;
} } }
ctx->profile->uniform_emitter(ctx, item->regtype, item->regnum, var);
if (arraysize < 0) {
ctx->uniform_count++;
switch (item->regtype)
{
case REG_TYPE_CONST: ctx->uniform_float4_count++; break;
case REG_TYPE_CONSTINT: ctx->uniform_int4_count++; break;
case REG_TYPE_CONSTBOOL: ctx->uniform_bool_count++; break;
default: break;
} } }
for (item = ctx->samplers.next; item != NULL; item = item->next)
{
ctx->sampler_count++;
ctx->profile->sampler_emitter(ctx, item->regnum,
(TextureType) item->index,
item->misc != 0);
}
for (item = ctx->attributes.next; item != NULL; item = item->next)
{
ctx->attribute_count++;
ctx->profile->attribute_emitter(ctx, item->regtype, item->regnum,
item->usage, item->index,
item->writemask, item->misc);
} }
static void verify_swizzles(Context *ctx)
{
size_t i;
const char *failmsg = "invalid swizzle";
for (i = 0; i < ctx->swizzles_count; i++)
{
const MOJOSHADER_swizzle *swiz = &ctx->swizzles[i];
if (swiz->swizzles[0] > 3) { fail(ctx, failmsg); return; }
if (swiz->swizzles[1] > 3) { fail(ctx, failmsg); return; }
if (swiz->swizzles[2] > 3) { fail(ctx, failmsg); return; }
if (swiz->swizzles[3] > 3) { fail(ctx, failmsg); return; }
} }
const MOJOSHADER_parseData *MOJOSHADER_parse(const char *profile,
const char *mainfn,
const unsigned char *tokenbuf,
const unsigned int bufsize,
const MOJOSHADER_swizzle *swiz,
const unsigned int swizcount,
const MOJOSHADER_samplerMap *smap,
const unsigned int smapcount,
MOJOSHADER_malloc m,
MOJOSHADER_free f, void *d)
{
MOJOSHADER_parseData *retval = NULL;
Context *ctx = NULL;
int rc = 0;
int failed = 0;
if ( ((m == NULL) && (f != NULL)) || ((m != NULL) && (f == NULL)) )
return &MOJOSHADER_out_of_mem_data;
ctx = build_context(profile, mainfn, tokenbuf, bufsize, swiz, swizcount,
smap, smapcount, m, f, d);
if (ctx == NULL)
return &MOJOSHADER_out_of_mem_data;
if (profile == NULL) fail(ctx, "Profile name is NULL");
if (isfail(ctx))
{
retval = build_parsedata(ctx);
destroy_context(ctx);
return retval;
}
verify_swizzles(ctx);
if (!ctx->mainfn)
ctx->mainfn = StrDup(ctx, "main");
ctx->current_position = 0;
rc = parse_version_token(ctx, profile);
if (rc < 0)
{
retval = build_parsedata(ctx);
destroy_context(ctx);
return retval;
}
if ( ((uint32) rc) > ctx->tokencount )
{
fail(ctx, "Corrupted or truncated shader");
ctx->tokencount = rc;
}
adjust_token_position(ctx, rc);
while (ctx->tokencount > 0)
{
if (!ctx->know_shader_size)
ctx->tokencount = 0xFFFFFFFF;
if (isfail(ctx))
{
failed = 1;
ctx->isfail = 0;
}
rc = parse_token(ctx);
if ( ((uint32) rc) > ctx->tokencount )
{
fail(ctx, "Corrupted or truncated shader");
break;
}
adjust_token_position(ctx, rc);
}
ctx->current_position = MOJOSHADER_POSITION_AFTER;
if (shader_is_pixel(ctx) && !shader_version_atleast(ctx, 2, 0))
{
if (!register_was_written(ctx, REG_TYPE_TEMP, 0))
fail(ctx, "r0 (pixel shader 1.x color output) never written to");
}
if (!failed)
{
process_definitions(ctx);
failed = isfail(ctx);
}
if (!failed)
ctx->profile->finalize_emitter(ctx);
ctx->isfail = failed;
retval = build_parsedata(ctx);
destroy_context(ctx);
return retval;
}
void MOJOSHADER_freeParseData(const MOJOSHADER_parseData *_data)
{
MOJOSHADER_parseData *data = (MOJOSHADER_parseData *) _data;
if ((data == NULL) || (data == &MOJOSHADER_out_of_mem_data))
return;
MOJOSHADER_free f = (data->free == NULL) ? MOJOSHADER_internal_free : data->free;
void *d = data->malloc_data;
int i;
f((void *) data->mainfn, d);
f((void *) data->output, d);
f((void *) data->constants, d);
f((void *) data->swizzles, d);
for (i = 0; i < data->error_count; i++)
{
f((void *) data->errors[i].error, d);
f((void *) data->errors[i].filename, d);
} f((void *) data->errors, d);
for (i = 0; i < data->uniform_count; i++)
f((void *) data->uniforms[i].name, d);
f((void *) data->uniforms, d);
for (i = 0; i < data->attribute_count; i++)
f((void *) data->attributes[i].name, d);
f((void *) data->attributes, d);
for (i = 0; i < data->output_count; i++)
f((void *) data->outputs[i].name, d);
f((void *) data->outputs, d);
for (i = 0; i < data->sampler_count; i++)
f((void *) data->samplers[i].name, d);
f((void *) data->samplers, d);
free_symbols(f, d, data->symbols, data->symbol_count);
MOJOSHADER_freePreshader(data->preshader);
f(data, d);
}
int MOJOSHADER_version(void)
{
return MOJOSHADER_VERSION;
}
const char *MOJOSHADER_changeset(void)
{
return MOJOSHADER_CHANGESET;
}
int MOJOSHADER_maxShaderModel(const char *profile)
{
#define PROFILE_SHADER_MODEL(p,v) if (strcmp(profile, p) == 0) return v;
PROFILE_SHADER_MODEL(MOJOSHADER_PROFILE_D3D, 3);
PROFILE_SHADER_MODEL(MOJOSHADER_PROFILE_BYTECODE, 3);
PROFILE_SHADER_MODEL(MOJOSHADER_PROFILE_HLSL, 3);
PROFILE_SHADER_MODEL(MOJOSHADER_PROFILE_GLSL, 3);
PROFILE_SHADER_MODEL(MOJOSHADER_PROFILE_GLSL120, 3);
PROFILE_SHADER_MODEL(MOJOSHADER_PROFILE_GLSLES, 3);
PROFILE_SHADER_MODEL(MOJOSHADER_PROFILE_ARB1, 2);
PROFILE_SHADER_MODEL(MOJOSHADER_PROFILE_NV2, 2);
PROFILE_SHADER_MODEL(MOJOSHADER_PROFILE_NV3, 2);
PROFILE_SHADER_MODEL(MOJOSHADER_PROFILE_NV4, 3);
PROFILE_SHADER_MODEL(MOJOSHADER_PROFILE_METAL, 3);
PROFILE_SHADER_MODEL(MOJOSHADER_PROFILE_SPIRV, 3);
PROFILE_SHADER_MODEL(MOJOSHADER_PROFILE_GLSPIRV, 3);
#undef PROFILE_SHADER_MODEL
return -1; }
const MOJOSHADER_preshader *MOJOSHADER_parsePreshader(const unsigned char *buf,
const unsigned int buflen,
MOJOSHADER_malloc m,
MOJOSHADER_free f,
void *d)
{
MOJOSHADER_preshader *retval = NULL;
Context *ctx = build_context(NULL, NULL, buf, buflen, NULL, 0, NULL, 0, m, f, d);
parse_preshader(ctx, ctx->tokens, ctx->tokencount);
if (!isfail(ctx))
{
retval = ctx->preshader;
ctx->preshader = NULL; }
destroy_context(ctx);
return retval;
}
void MOJOSHADER_freePreshader(const MOJOSHADER_preshader *preshader)
{
if (preshader != NULL)
{
unsigned int i, j;
void *d = preshader->malloc_data;
MOJOSHADER_free f = preshader->free;
if (f == NULL) f = MOJOSHADER_internal_free;
f((void *) preshader->literals, d);
for (i = 0; i < preshader->instruction_count; i++)
{
for (j = 0; j < preshader->instructions[i].operand_count; j++)
f((void *) preshader->instructions[i].operands[j].array_registers, d);
} f((void *) preshader->instructions, d);
f((void *) preshader->registers, d);
free_symbols(f, d, preshader->symbols, preshader->symbol_count);
f((void *) preshader, d);
} }