Skip to content

Commit

Permalink
Allow overriding of swizzle on vertex attributes during bytecode pars…
Browse files Browse the repository at this point in the history
…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 94974eb
Show file tree
Hide file tree
Showing 6 changed files with 139 additions and 20 deletions.
2 changes: 1 addition & 1 deletion finderrors.c
Expand Up @@ -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
Expand Down
106 changes: 92 additions & 14 deletions mojoshader.c
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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

Expand All @@ -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.

Expand All @@ -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) |
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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;

Expand All @@ -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)
{
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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)
{
Expand All @@ -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)) )
Expand Down Expand Up @@ -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++)
Expand Down
40 changes: 39 additions & 1 deletion mojoshader.h
Expand Up @@ -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...
*/
Expand Down Expand Up @@ -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().
*/
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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.
*
Expand All @@ -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);


/*
Expand Down
7 changes: 5 additions & 2 deletions mojoshader_opengl.c
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion testoutput.c
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion testparse.c
Expand Up @@ -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);
Expand Down

0 comments on commit 94974eb

Please sign in to comment.