Allow overriding of swizzle on vertex attributes during bytecode parsing.
authorRyan C. Gordon <icculus@icculus.org>
Tue, 26 Aug 2008 05:54:49 -0400
changeset 450 6a9faf398c1d
parent 449 5a978f37e750
child 451 43175917011f
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
mojoshader.c
mojoshader.h
mojoshader_opengl.c
testoutput.c
testparse.c
--- a/finderrors.c	Sat Aug 23 20:33:03 2008 -0400
+++ b/finderrors.c	Tue Aug 26 05:54:49 2008 -0400
@@ -75,7 +75,7 @@
     } // 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
--- a/mojoshader.c	Sat Aug 23 20:33:03 2008 -0400
+++ b/mojoshader.c	Tue Aug 26 05:54:49 2008 -0400
@@ -364,6 +364,8 @@
     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 @@
     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 @@
 } // 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 @@
     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 @@
         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 @@
         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 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 @@
     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 @@
     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 @@
     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 @@
         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->sampler_count = ctx->sampler_count;
-        retval->samplers = samplers;
+        retval->swizzle_count = ctx->swizzles_count;
+        retval->swizzles = swizzles;
     } // else
 
     retval->malloc = (ctx->malloc == internal_malloc) ? NULL : ctx->malloc;
@@ -7514,11 +7568,28 @@
 } // 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 @@
     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 @@
     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++)
--- a/mojoshader.h	Sat Aug 23 20:33:03 2008 -0400
+++ b/mojoshader.h	Tue Aug 26 05:54:49 2008 -0400
@@ -201,6 +201,20 @@
 } 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...
  */
 typedef struct
@@ -311,6 +325,19 @@
     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().
      */
     MOJOSHADER_malloc malloc;
@@ -396,6 +423,12 @@
  *  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 @@
 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 @@
  *
  *   (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 @@
  * 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);
 
 
 /*
--- a/mojoshader_opengl.c	Sat Aug 23 20:33:03 2008 -0400
+++ b/mojoshader_opengl.c	Tue Aug 26 05:54:49 2008 -0400
@@ -1037,12 +1037,15 @@
 
 
 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)
--- a/testoutput.c	Sat Aug 23 20:33:03 2008 -0400
+++ b/testoutput.c	Tue Aug 26 05:54:49 2008 -0400
@@ -16,7 +16,7 @@
     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
--- a/testparse.c	Sat Aug 23 20:33:03 2008 -0400
+++ b/testparse.c	Tue Aug 26 05:54:49 2008 -0400
@@ -59,7 +59,7 @@
     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);