Skip to content
Permalink
Browse files
Allow overriding of swizzle on vertex attributes during bytecode pars…
…ing.

This lets you compile a shader that you know wants ARGB color data, for
 example, when you're definitely going to pass it RGBA. The parser will handle
 this by changing the swizzle on that input register (including handling
 swizzling explicit swizzles), at no extra cost over not overriding swizzling;
 it does not generate any extra shader opcodes.
  • Loading branch information
icculus committed Aug 26, 2008
1 parent d307474 commit 94974ebea013b1628d32da5b2ab9e279f018374c
Showing with 139 additions and 20 deletions.
  1. +1 −1 finderrors.c
  2. +92 −14 mojoshader.c
  3. +39 −1 mojoshader.h
  4. +5 −2 mojoshader_opengl.c
  5. +1 −1 testoutput.c
  6. +1 −1 testparse.c
@@ -75,7 +75,7 @@ static int do_file(const char *profile, const char *dname, const char *fn, int *
} // if

#if FINDERRORS_COMPILE_SHADERS
MOJOSHADER_glShader *shader = MOJOSHADER_glCompileShader(buf, rc);
MOJOSHADER_glShader *shader = MOJOSHADER_glCompileShader(buf, rc, NULL, 0);
if (shader == NULL)
report("FAIL: %s %s\n", fname, MOJOSHADER_glGetError());
else
@@ -364,6 +364,8 @@ struct Context
void *malloc_data;
const uint32 *tokens;
uint32 tokencount;
const MOJOSHADER_swizzle *swizzles;
unsigned int swizzles_count;
OutputList *output;
OutputList globals;
OutputList helpers;
@@ -729,8 +731,8 @@ static RegisterList *reglist_insert(Context *ctx, RegisterList *prev,
return item;
} // reglist_insert

static RegisterList *reglist_find(RegisterList *prev, const RegisterType rtype,
const int regnum)
static RegisterList *reglist_find(const RegisterList *prev,
const RegisterType rtype, const int regnum)
{
const uint32 newval = reg_to_ui32(rtype, regnum);
RegisterList *item = prev->next;
@@ -5333,6 +5335,35 @@ static void determine_constants_arrays(Context *ctx)
} // determine_constants_arrays


static int adjust_swizzle(const Context *ctx, const RegisterType regtype,
const int regnum, const int swizzle)
{
if (regtype != REG_TYPE_INPUT) // !!! FIXME: maybe lift this later?
return swizzle;
else if (ctx->swizzles_count == 0)
return swizzle;

const RegisterList *reg = reglist_find(&ctx->attributes, regtype, regnum);
if (reg == NULL)
return swizzle;

int i;
const MOJOSHADER_swizzle *swiz = ctx->swizzles;
for (i = 0; i < ctx->swizzles_count; i++, swiz++)
{
if ((swiz->usage == reg->usage) && (swiz->index == reg->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) );
} // if
} // for

return swizzle;
} // adjust_swizzle


static int parse_source_token(Context *ctx, SourceArgInfo *info)
{
int retval = 1;
@@ -5350,11 +5381,7 @@ static int parse_source_token(Context *ctx, SourceArgInfo *info)
info->token = ctx->tokens;
info->regnum = (int) (token & 0x7ff); // bits 0 through 10
info->relative = (int) ((token >> 13) & 0x1); // bit 13
info->swizzle = (int) ((token >> 16) & 0xFF); // bits 16 through 23
info->swizzle_x = (int) ((token >> 16) & 0x3); // bits 16 through 17
info->swizzle_y = (int) ((token >> 18) & 0x3); // bits 18 through 19
info->swizzle_z = (int) ((token >> 20) & 0x3); // bits 20 through 21
info->swizzle_w = (int) ((token >> 22) & 0x3); // bits 22 through 23
const int swizzle = (int) ((token >> 16) & 0xFF); // bits 16 through 23
info->src_mod = (SourceMod) ((token >> 24) & 0xF); // bits 24 through 27
info->regtype = (RegisterType) (((token >> 28) & 0x7) | ((token >> 8) & 0x18)); // bits 28-30, 11-12

@@ -5376,6 +5403,12 @@ static int parse_source_token(Context *ctx, SourceArgInfo *info)
info->regnum += 6144;
} // else if

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);

ctx->tokens++; // swallow token for now, for multiple calls in a row.
ctx->tokencount--; // swallow token for now, for multiple calls in a row.

@@ -5397,8 +5430,7 @@ static int parse_source_token(Context *ctx, SourceArgInfo *info)
ctx->tokens++; // swallow token for now, for multiple calls in a row.
ctx->tokencount--; // swallow token for now, for multiple calls in a row.

const int relswiz = (int) ((reltoken >> 16) & 0xFF);
info->relative_component = relswiz & 0x3;
const int relswiz = (int) ((reltoken >> 16) & 0xFF);
info->relative_regnum = (int) (reltoken & 0x7ff);
info->relative_regtype = (RegisterType)
(((reltoken >> 28) & 0x7) |
@@ -6890,6 +6922,8 @@ static int find_profile_id(const char *profile)
static Context *build_context(const char *profile,
const unsigned char *tokenbuf,
const unsigned int bufsize,
const MOJOSHADER_swizzle *swiz,
const unsigned int swizcount,
MOJOSHADER_malloc m, MOJOSHADER_free f, void *d)
{
if (m == NULL) m = internal_malloc;
@@ -6905,6 +6939,8 @@ static Context *build_context(const char *profile,
ctx->malloc_data = d;
ctx->tokens = (const uint32 *) tokenbuf;
ctx->tokencount = bufsize / sizeof (uint32);
ctx->swizzles = swiz;
ctx->swizzles_count = swizcount;
ctx->endline = endline_str;
ctx->endline_len = strlen(ctx->endline);
ctx->globals.tail = &ctx->globals.head;
@@ -7289,6 +7325,7 @@ static MOJOSHADER_parseData *build_parsedata(Context *ctx)
MOJOSHADER_uniform *uniforms = NULL;
MOJOSHADER_attribute *attributes = NULL;
MOJOSHADER_sampler *samplers = NULL;
MOJOSHADER_swizzle *swizzles = NULL;
MOJOSHADER_parseData *retval = NULL;
int attribute_count = 0;

@@ -7313,13 +7350,28 @@ static MOJOSHADER_parseData *build_parsedata(Context *ctx)
if (!isfail(ctx))
samplers = build_samplers(ctx);

// check again, in case build_output ran out of memory.
if (!isfail(ctx))
samplers = build_samplers(ctx);

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
} // if

// check again, in case build_output, etc, ran out of memory.
if (isfail(ctx))
{
int i;

Free(ctx, output);
Free(ctx, constants);
Free(ctx, swizzles);

if (uniforms != NULL)
{
@@ -7358,10 +7410,12 @@ static MOJOSHADER_parseData *build_parsedata(Context *ctx)
retval->uniforms = uniforms;
retval->constant_count = ctx->constant_count;
retval->constants = constants;
retval->attribute_count = attribute_count;
retval->attributes = attributes;
retval->sampler_count = ctx->sampler_count;
retval->samplers = samplers;
retval->attribute_count = attribute_count;
retval->attributes = attributes;
retval->swizzle_count = ctx->swizzles_count;
retval->swizzles = swizzles;
} // else

retval->malloc = (ctx->malloc == internal_malloc) ? NULL : ctx->malloc;
@@ -7514,11 +7568,28 @@ static void process_definitions(Context *ctx)
} // process_definitions


static void verify_swizzles(Context *ctx)
{
int i;
const char *failmsg = "invalid swizzle";
const MOJOSHADER_swizzle *swiz = ctx->swizzles;
for (i = 0; i < ctx->swizzles_count; i++, swiz++)
{
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; }
} // for
} // verify_swizzles


// API entry point...

const MOJOSHADER_parseData *MOJOSHADER_parse(const char *profile,
const unsigned char *tokenbuf,
const unsigned int bufsize,
const MOJOSHADER_swizzle *swiz,
const unsigned int swizcount,
MOJOSHADER_malloc m,
MOJOSHADER_free f, void *d)
{
@@ -7529,11 +7600,15 @@ const MOJOSHADER_parseData *MOJOSHADER_parse(const char *profile,
if ( ((m == NULL) && (f != NULL)) || ((m != NULL) && (f == NULL)) )
return &out_of_mem_data; // supply both or neither.

if ((ctx = build_context(profile, tokenbuf, bufsize, m, f, d)) == NULL)
ctx = build_context(profile, tokenbuf, bufsize, swiz, swizcount, m, f, d);
if (ctx == NULL)
return &out_of_mem_data;

verify_swizzles(ctx);

// Version token always comes first.
rc = parse_version_token(ctx, profile);
if (!isfail(ctx))
rc = parse_version_token(ctx, profile);

// parse out the rest of the tokens after the version token...
while ( (rc > 0) && (!isfail(ctx)) )
@@ -7581,6 +7656,9 @@ void MOJOSHADER_freeParseData(const MOJOSHADER_parseData *_data)
if (data->constants != NULL)
f((void *) data->constants, d);

if (data->swizzles != NULL)
f((void *) data->swizzles, d);

if (data->uniforms != NULL)
{
for (i = 0; i < data->uniform_count; i++)
@@ -200,6 +200,20 @@ typedef struct
const char *name;
} MOJOSHADER_attribute;

/*
* Use this if you want to specify newly-parsed code to swizzle incoming
* data. This can be useful if you know, at parse time, that a shader
* will be processing data on COLOR0 that should be RGBA, but you'll
* be passing it a vertex array full of ARGB instead.
*/
typedef struct
{
MOJOSHADER_usage usage;
unsigned int index;
unsigned char swizzles[4]; /* {0,1,2,3} == .xyzw, {2,2,2,2} == .zzzz */
} MOJOSHADER_swizzle;


/*
* Structure used to return data from parsing of a shader...
*/
@@ -310,6 +324,19 @@ typedef struct
*/
MOJOSHADER_attribute *attributes;

/*
* The number of elements pointed to by (swizzles).
*/
int swizzle_count;

/*
* (swizzle_count) elements of data that specify swizzles the shader will
* apply to incoming attributes. This is a copy of what was passed to
* MOJOSHADER_parseData().
* This can be NULL on error or if (swizzle_count) is zero.
*/
MOJOSHADER_swizzle *swizzles;

/*
* This is the malloc implementation you passed to MOJOSHADER_parse().
*/
@@ -396,13 +423,21 @@ typedef struct
* MOJOSHADER_parseData object, which is still safe to pass to
* MOJOSHADER_freeParseData()).
*
* You can tell the generated program to swizzle certain inputs. If you know
* that COLOR0 should be RGBA but you're passing in ARGB, you can specify
* a swizzle of { MOJOSHADER_USAGE_COLOR, 0, {1,2,3,0} } to (swiz). If the
* input register in the code would produce reg.ywzx, that swizzle would
* change it to reg.wzxy ... (swiz) can be NULL.
*
* This function is thread safe, so long as (m) and (f) are too, and that
* (tokenbuf) remains intact for the duration of the call. This allows you
* to parse several shaders on separate CPU cores at the same time.
*/
const MOJOSHADER_parseData *MOJOSHADER_parse(const char *profile,
const unsigned char *tokenbuf,
const unsigned int bufsize,
const MOJOSHADER_swizzle *swiz,
const unsigned int swizcount,
MOJOSHADER_malloc m,
MOJOSHADER_free f,
void *d);
@@ -594,6 +629,7 @@ int MOJOSHADER_glMaxUniforms(MOJOSHADER_shaderType shader_type);
*
* (tokenbuf) is a buffer of Direct3D shader bytecode.
* (bufsize) is the size, in bytes, of the bytecode buffer.
* (swiz) and (swizcount) are passed to MOJOSHADER_parse() unmolested.
*
* Returns NULL on error, or a shader handle on success.
*
@@ -607,7 +643,9 @@ int MOJOSHADER_glMaxUniforms(MOJOSHADER_shaderType shader_type);
* Compiled shaders from this function may not be shared between contexts.
*/
MOJOSHADER_glShader *MOJOSHADER_glCompileShader(const unsigned char *tokenbuf,
const unsigned int bufsize);
const unsigned int bufsize,
const MOJOSHADER_swizzle *swiz,
const unsigned int swizcount);


/*
@@ -1037,12 +1037,15 @@ int MOJOSHADER_glMaxUniforms(MOJOSHADER_shaderType shader_type)


MOJOSHADER_glShader *MOJOSHADER_glCompileShader(const unsigned char *tokenbuf,
const unsigned int bufsize)
const unsigned int bufsize,
const MOJOSHADER_swizzle *swiz,
const unsigned int swizcount)
{
MOJOSHADER_glShader *retval = NULL;
GLuint shader = 0;
const MOJOSHADER_parseData *pd = MOJOSHADER_parse(ctx->profile, tokenbuf,
bufsize, ctx->malloc_fn,
bufsize, swiz, swizcount,
ctx->malloc_fn,
ctx->free_fn,
ctx->malloc_data);
if (pd->error != NULL)
@@ -16,7 +16,7 @@ static int do_parse(const unsigned char *buf, const int len, const char *prof)
const MOJOSHADER_parseData *pd;
int retval = 0;

pd = MOJOSHADER_parse(prof, buf, len, NULL, NULL, NULL);
pd = MOJOSHADER_parse(prof, buf, len, NULL, 0, NULL, NULL, NULL);
if (pd->error != NULL)
printf("ERROR: %s\n", pd->error);
else
@@ -59,7 +59,7 @@ static int do_parse(const unsigned char *buf, const int len, const char *prof)
const MOJOSHADER_parseData *pd;
int retval = 0;

pd = MOJOSHADER_parse(prof, buf, len, Malloc, Free, NULL);
pd = MOJOSHADER_parse(prof, buf, len, NULL, 0, Malloc, Free, NULL);
printf("PROFILE: %s\n", prof);
if (pd->error != NULL)
printf("ERROR: %s\n", pd->error);

0 comments on commit 94974eb

Please sign in to comment.