From 94974ebea013b1628d32da5b2ab9e279f018374c Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Tue, 26 Aug 2008 05:54:49 -0400 Subject: [PATCH] Allow overriding of swizzle on vertex attributes during bytecode parsing. 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. --- finderrors.c | 2 +- mojoshader.c | 106 ++++++++++++++++++++++++++++++++++++++------ mojoshader.h | 40 ++++++++++++++++- mojoshader_opengl.c | 7 ++- testoutput.c | 2 +- testparse.c | 2 +- 6 files changed, 139 insertions(+), 20 deletions(-) diff --git a/finderrors.c b/finderrors.c index e56708ab..90d2aa35 100644 --- a/finderrors.c +++ b/finderrors.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 diff --git a/mojoshader.c b/mojoshader.c index 699fdf10..082eed10 100644 --- a/mojoshader.c +++ b/mojoshader.c @@ -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++) diff --git a/mojoshader.h b/mojoshader.h index bd8c7b66..182bb728 100644 --- a/mojoshader.h +++ b/mojoshader.h @@ -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,6 +423,12 @@ 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. @@ -403,6 +436,8 @@ typedef struct 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); /* diff --git a/mojoshader_opengl.c b/mojoshader_opengl.c index e3c5e723..c9dbcbfb 100644 --- a/mojoshader_opengl.c +++ b/mojoshader_opengl.c @@ -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) diff --git a/testoutput.c b/testoutput.c index 8c44eaac..a4d80905 100644 --- a/testoutput.c +++ b/testoutput.c @@ -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 diff --git a/testparse.c b/testparse.c index 932d5587..0c565782 100644 --- a/testparse.c +++ b/testparse.c @@ -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);