flibitijibibo / flibitBounties

Pile of programming bounties for things flibit can't do right now
27 stars 0 forks source link

Finish SPIR-V emitter for MojoShader #1

Closed flibitijibibo closed 4 years ago

flibitijibibo commented 6 years ago

Introductory Information:

MojoShader is a project that takes in Direct3D shader bytecode and emits shaders for other graphics APIs, including ARB1, GLSL, and Metal shaders. It is used by FNA for its Effects framework implementation and is used by every single game using FNA, as well as other non-XNA games like Super Meat Boy, Psychonauts, and Killing Floor.

SPIR-V is the Khronos Group's standard IR for graphics/compute shaders, used by Vulkan, OpenCL, and OpenGL (via ARB_gl_spirv).

The Project:

We need to implement a SPIR-V emitter for MojoShader which works with both OpenGL as well as Vulkan. Currently our priority is OpenGL support since we have all the infrastructure (mostly) set up to work with ARB_gl_spirv program objects already. In the long term, this is primarily for FNA's VulkanDevice, though it is applicable to any game currently using MojoShader that properly uses MOJOSHADER_glBestProfile to autodetect usable shader profiles.

Currently, the MojoShader repo FNA works on is here. However, the current active repository for the SPIR-V work is here. The part you care about is in profiles/mojoshader_profile_spirv.c.

For the most part the SPIR-V emitter works, but we are missing many opcodes (check the file for EMIT_SPIRV_OPCODE_UNIMPLEMENTED_FUNC) and we need to be at least up to feature parity with the glsl120 emitter. glsl120 as well as the other emitters in the profiles folder will be helpful as references. A checklist can be found below.

Lastly, the whole file needs at least one final pass, for polish. Style cleanup would be nice but isn't mandatory.

Prerequisites:

MojoShader is written in C99, so you'll need to be reasonably comfortable with that language to finish this project. Experience with graphics programming is also a plus, but you won't be doing too much of that since you're just touching shader content and not stuff like GL callstreams and what have you.

Knowledge of the Direct3D Shader Model 3.0 specification is highly recommended unless you can read through a whole lot of documents on assembly-like languages very thoroughly. You'll mostly want to read up on the register sets (vs, ps) and instruction sets (vs, ps). MojoShader already handles the bytecode parsing but you'll want to know (at a high level, at least) what the emitter functions are referencing.

You'll also want to read up on the SPIR-V specification, for pretty much the same reasons as the Direct3D shader spec.

Example Games:

At a basic level, lots of FNA games use these Effect files which should work with MojoShader's testparse program, so if you want a quick set of test shaders, this is probably a good place to start. Just about every XNA game uses SpriteEffect; it'd probably be easier to write a list of games that don't use it if I could even remember any.

Examples of more complex Effects include those found in FEZ and Reus, with moderately complex shaders found in Skulls of the Shogun, Apotheon, Cryptark, Escape Goat 2, Murder Miners, and Gateways.

With some LD_PRELOAD magic it may be possible to test SPIR-V support with the Linux/macOS versions of Super Meat Boy and Psychonauts, but this has been untested - maybe ask Ryan C. Gordon about this if you want to hack on those games.

How Much Can flibit Help?

I might be able to help with ARB_gl_spirv integration and maybe help clear up questions about the D3D spec, but I'll confess to not having the best knowledge of the SPIR-V spec. By the time you get test shaders working you'll probably already know more about SPIR-V than me.

Budget/Timeline:

Measuring in weekends, I expect this to take about 2 weekends. The first weekend would be spent on scribbling out the remaining opcodes and the second weekend would be spent on testing/polish. I currently have $500 USD allocated for this project.

flibitijibibo commented 5 years ago

I just finished rewriting the bounty OP - for those familiar with this project already it's nothing really new, it just tweaks things to make it clear that we're now in the latter half of the project.

flibitijibibo commented 5 years ago

Something interesting for VPOS, if it hasn't been done already:

https://github.com/FNA-XNA/MojoShader/issues/14

The Vulkan/SPIR-V specification is thankfully very explicit about FragCoord:

https://vulkan.lunarg.com/doc/view/1.0.26.0/linux/vkspec.chunked/ch14s06.html

D9VK simply loads the value and subtracts the pixel center: https://github.com/Joshua-Ashton/d9vk/blob/bc64e8e0263004908090da60b4c478a6573fe7d0/src/dxso/dxso_compiler.cpp#L1111

flibitijibibo commented 5 years ago

Since I'm here, updated the checklist and included links to every instruction on MSDN.

flibitijibibo commented 5 years ago

A quick note for TEXLDD... it seems like even GLSL never ran into this. The emitter for this instruction is just some added setup for extensions like EXT_gpu_shader4 followed by a call to glsl_texld, which hard asserts on the ldd path:

https://github.com/FNA-XNA/MojoShader/blob/fna/profiles/mojoshader_profile_glsl.c#L1665

So we probably don't have any data for this, but on the other hand a lot of why the feature wasn't implemented was because of GLSL itself. I think the SPIR-V path is essentially the same as any image sample, but with the Grad (0x4) flag set and src2/src3 pulled in when texldd == 1 in spv_texld.

After that's done (or at least documented?), unless someone really wants to do the remaining SM1 instructions, I think we're at the point where we can start cleaning up and make a beta build for people to try out and review.

krolli commented 5 years ago

I reached the point where all the shaders I have at hand run through testparse without issues, including UT3 stuff and ignoring CTAB issues on Psychonauts. There are still some opcodes and source modifiers which are present in GLSL that I never hit so far. If someone is sitting on some large stack of shader binaries, now would be good time to throw them in my direction.

I know @TheSpydog had some issue with Chasm on Intel GPUs, but renderdoc capture he sent me looked fine on my end, when viewed on nvidia GPU. I suspect this might be a driver issue, but hard to say without seeing where it went wrong on his machine.

I think we can start working on some testing build. I could try to implement remaining pieces blindly, without running output through validator, but I'd rather have it barf an assert/error on unimplemented code path, than cause a driver crash on badly implemented one. I can also try to create test cases for some of those remaining code paths, but it might be easier to find examples of it in real shipped games.

flibitijibibo commented 5 years ago

Sounds good, I’ll do a cleanup pass on Monday, aiming for a public test on Wednesday.

flibitijibibo commented 5 years ago

@krolli Whenever you're free, can you do one more rebase against default? I'll try to send a big cleanup patch to go on top of that, and after everything's merged in your repo I'll put together some test games and post this to my plan file.

My current testing list:

I would probably put Bastion and Reus in there (Reus especially) but those are both internally maintained now - I'll have to see if simply swapping out FNA binaries still works for those. EDIT: Same for Celeste, good catch Caleb!

TheSpydog commented 5 years ago

I recommend adding Celeste to that list. Did some testing a few days ago and noticed it still had some major issues on Intel when dashing. Not sure if it affects other vendors as well but might be worth a look.

krolli commented 5 years ago

Alright, shall I also collapse all changesets while rebasing or should I try to keep as much history as possible? I will still keep it on spirv branch, if you don't mind, just so we can easily switch between the two.

Also, I just noticed "Replace glProgramViewportFlip with glProgramViewportInfo" change on default. It seems to have affected Chasm CRT shader. Will probably need to mirror that in spirv profile as well.

flibitijibibo commented 5 years ago

The final commit to upstream will be a single commit (with a whole boatload of credits) so you can squash it if it makes life easier.

I think Chasm’s CRT shader uses the broken VPOS, I’ll go ask James... EDIT: Yup it's VPOS... we're actually accurate to XNA now and it makes the shader less pretty, go figure.

krolli commented 5 years ago

Alright, things have been rebased and collapsed. Its in spirv-rebase branch and I closed spirv branch. I might go over changes done in other files and try to reduce some noise (eg. whitespace changes) later, unless patch you've mentioned already does that.

flibitijibibo commented 5 years ago

Here's the first of probably two cleanup patches. This cleans up every file except mojoshader_profile_spirv.c. It mostly fixes style, but does cut out a bunch of debug stuff to reduce clutter (since this patchset is going to be huge no matter how we div it up).

Notes aside from cleanup:

Expect the second patch later today.

# HG changeset patch
# User Ethan Lee <flibitijibibo@flibitijibibo.com>
# Date 1565792958 14400
#      Wed Aug 14 10:29:18 2019 -0400
# Branch spirv-rebased
# Node ID 1041c1e582135efa39a623bae87c238f287c6ef1
# Parent  73770d3b26ae1632e9778cd61ac5f8b0fbf889d0
spirv cleanup part 1

diff -r 73770d3b26ae -r 1041c1e58213 mojoshader.c
--- a/mojoshader.c  Wed Aug 14 08:07:15 2019 +0200
+++ b/mojoshader.c  Wed Aug 14 10:29:18 2019 -0400
@@ -3462,8 +3462,8 @@
         if (strcmp(retval->profile, "spirv") == 0)
         {
             int binary_size = retval->output_len - sizeof(SpirvPatchTable);
-            uint32* binary = (uint32*)retval->output;
-            SpirvPatchTable* table = (SpirvPatchTable*)&retval->output[binary_size];
+            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;
@@ -3475,8 +3475,8 @@
                 SpirvPatchEntry entry = table->samplers[i];
                 if (entry.offset)
                     binary[entry.offset] = entry.location;
-            }
-        }
+            } // for
+        } // if
 #endif

         // we don't own these now, retval does.
diff -r 73770d3b26ae -r 1041c1e58213 mojoshader_common.c
--- a/mojoshader_common.c   Wed Aug 14 08:07:15 2019 +0200
+++ b/mojoshader_common.c   Wed Aug 14 10:29:18 2019 -0400
@@ -1062,7 +1062,7 @@
         } // while
     } // if

-    const uint8 *data = (const uint8 *)_data;
+    const uint8 *data = (const uint8 *) _data;
     size_t write_pos = start - pos;
     size_t write_remain = len;
     size_t written = 0;
@@ -1079,7 +1079,7 @@
         write_pos     = 0;
         item          = item->next;
     } // while
-}
+} // buffer_patch

 // Based on SDL_string.c's SDL_PrintFloat function
 size_t MOJOSHADER_printFloat(char *text, size_t maxlen, float arg)
diff -r 73770d3b26ae -r 1041c1e58213 mojoshader_effects.c
--- a/mojoshader_effects.c  Wed Aug 14 08:07:15 2019 +0200
+++ b/mojoshader_effects.c  Wed Aug 14 10:29:18 2019 -0400
@@ -849,24 +849,6 @@
     const uint8 *ptr = (const uint8 *) buf;
     uint32 len = (uint32) _len;

-    #if 0
-    {
-        unsigned long long checksum = 0x2394F7C94D7A6E13ull;
-        for (uint32 i = 0; i < len; i++)
-        {
-            checksum = (checksum ^ ptr[i]) * 13;
-        }
-        char pathbuf[128];
-        snprintf(pathbuf, sizeof(pathbuf), "mojoshader-binaries/%llX.fxb", checksum);
-        FILE *fp = fopen(pathbuf, "wb");
-        if (fp)
-        {
-            fwrite(ptr, 1, len, fp);
-            fclose(fp);
-        }
-    }
-    #endif
-
     /* Supply both m and f, or neither */
     if ( ((m == NULL) && (f != NULL)) || ((m != NULL) && (f == NULL)) )
         return &MOJOSHADER_out_of_mem_effect;
diff -r 73770d3b26ae -r 1041c1e58213 mojoshader_opengl.c
--- a/mojoshader_opengl.c   Wed Aug 14 08:07:15 2019 +0200
+++ b/mojoshader_opengl.c   Wed Aug 14 10:29:18 2019 -0400
@@ -433,11 +433,11 @@
 } // impl_GLSL_MaxUniforms

 #if SUPPORT_PROFILE_SPIRV
-static const SpirvPatchTable* spv_getPatchTable(MOJOSHADER_glShader* shader)
+static const SpirvPatchTable* spv_getPatchTable(MOJOSHADER_glShader *shader)
 {
-    const MOJOSHADER_parseData* pd = shader->parseData;
+    const MOJOSHADER_parseData *pd = shader->parseData;
     size_t table_offset = pd->output_len - sizeof(SpirvPatchTable);
-    return (const SpirvPatchTable*)(pd->output + table_offset);
+    return (const SpirvPatchTable *) (pd->output + table_offset);
 } // spv_getPatchTable

 static int spv_CompileShader(const MOJOSHADER_parseData *pd, int32 base_location, GLuint *s)
@@ -446,12 +446,12 @@

     GLsizei data_len = pd->output_len - sizeof(SpirvPatchTable);
     const GLvoid* data = pd->output;
-    uint32* patched_data = NULL;
+    uint32 *patched_data = NULL;
     if (base_location)
     {
-        patched_data = (uint32*)Malloc(data_len);
+        patched_data = (uint32 *) Malloc(data_len);
         memcpy(patched_data, data, data_len);
-        const SpirvPatchTable* table = (const SpirvPatchTable*)&pd->output[data_len];
+        const SpirvPatchTable *table = (const SpirvPatchTable *) &pd->output[data_len];
         if (table->vpflip.offset)      patched_data[table->vpflip.offset]      += base_location;
         if (table->array_vec4.offset)  patched_data[table->array_vec4.offset]  += base_location;
         if (table->array_ivec4.offset) patched_data[table->array_ivec4.offset] += base_location;
@@ -462,10 +462,10 @@
             SpirvPatchEntry entry = table->samplers[i];
             if (entry.offset)
                 patched_data[entry.offset] += base_location;
-        }
+        } // for

         data = patched_data;
-    }
+    } // if

     const GLuint shader = ctx->glCreateShader(glsl_shader_type(pd->shader_type));
     ctx->glShaderBinary(1, &shader, GL_SHADER_BINARY_FORMAT_SPIR_V_ARB, data, data_len);
@@ -479,7 +479,7 @@
     {
         GLsizei len = 0;
         ctx->glGetShaderInfoLog(shader, sizeof(error_buffer), &len,
-                             (GLchar *)error_buffer);
+                             (GLchar *) error_buffer);
         ctx->glDeleteShader(shader);
         *s = 0;
         return 0;
@@ -492,12 +492,6 @@

 static int impl_SPIRV_CompileShader(const MOJOSHADER_parseData *pd, GLuint *s)
 {
-    #if 0
-    FILE *fp = fopen(pd->mainfn, "w");
-    fwrite(pd->output, 1, pd->output_len, fp);
-    fclose(fp);
-    #endif
-
     // Compilation postponed until linking, but generate dummy shader id so hash table lookups work.
     *s = ctx->glCreateShader(glsl_shader_type(pd->shader_type));
     return 1;
@@ -519,14 +513,14 @@
             return 0;

         base_location += spv_getPatchTable(vshader)->location_count;
-    }
+    } // if

     GLuint ps_handle = 0;
     if (pshader)
     {
         if (!spv_CompileShader(pshader->parseData, base_location, &ps_handle))
             return 0;
-    }
+    } // if

     if (ctx->have_opengl_2)
     {
@@ -535,12 +529,12 @@
         {
             ctx->glAttachShader(program, vs_handle);
             ctx->glDeleteShader(vs_handle);
-        }
+        } // if
         if (ps_handle)
         {
             ctx->glAttachShader(program, ps_handle);
             ctx->glDeleteShader(ps_handle);
-        }
+        } // if
         ctx->glLinkProgram(program);
         ctx->glGetProgramiv(program, GL_LINK_STATUS, &ok);
         if (!ok)
@@ -562,12 +556,12 @@
         {
             ctx->glAttachObjectARB(program, (GLhandleARB) vs_handle);
             ctx->glDeleteObjectARB((GLhandleARB) vs_handle);
-        }
+        } // if
         if (ps_handle)
         {
             ctx->glAttachObjectARB(program, (GLhandleARB) ps_handle);
             ctx->glDeleteObjectARB((GLhandleARB) ps_handle);
-        }
+        } // if
         ctx->glLinkProgramARB(program);
         ctx->glGetObjectParameterivARB(program, GL_OBJECT_LINK_STATUS_ARB, &ok);
         if (!ok)
@@ -611,11 +605,9 @@
 {
     GLint base_location = 0;

-    const SpirvPatchTable* table = spv_getPatchTable(shader);
+    const SpirvPatchTable *table = spv_getPatchTable(shader);
     if (shader->parseData->shader_type == MOJOSHADER_TYPE_PIXEL)
-    {
         base_location += spv_getPatchTable(program->vertex)->location_count;
-    }

     assert(table->samplers[idx].offset);
     return base_location + table->samplers[idx].location;
@@ -623,8 +615,8 @@

 static void impl_SPIRV_FinalInitProgram(MOJOSHADER_glProgram *program)
 {
-    const SpirvPatchTable* vs_table = spv_getPatchTable(program->vertex);
-    const SpirvPatchTable* ps_table = spv_getPatchTable(program->fragment);
+    const SpirvPatchTable *vs_table = spv_getPatchTable(program->vertex);
+    const SpirvPatchTable *ps_table = spv_getPatchTable(program->fragment);
     program->vs_float4_loc = vs_table->array_vec4.location;
     program->vs_int4_loc   = vs_table->array_ivec4.location;
     program->vs_bool_loc   = vs_table->array_bool.location;
@@ -641,9 +633,8 @@
         if (program->ps_float4_loc != -1) program->ps_float4_loc += ps_base_location;
         if (program->ps_int4_loc   != -1) program->ps_int4_loc   += ps_base_location;
         if (program->ps_bool_loc   != -1) program->ps_bool_loc   += ps_base_location;
-    }
+    } // if
 } // impl_SPIRV_FinalInitProgram
-
 #endif // SUPPORT_PROFILE_SPIRV

 #if SUPPORT_PROFILE_GLSL
@@ -809,7 +800,6 @@

 static void impl_GLSL_FinalInitProgram(MOJOSHADER_glProgram *program)
 {
-
     program->vs_float4_loc = glsl_uniform_loc(program, "vs_uniforms_vec4");
     program->vs_int4_loc = glsl_uniform_loc(program, "vs_uniforms_ivec4");
     program->vs_bool_loc = glsl_uniform_loc(program, "vs_uniforms_bool");
diff -r 73770d3b26ae -r 1041c1e58213 utils/testparse.c
--- a/utils/testparse.c Wed Aug 14 08:07:15 2019 +0200
+++ b/utils/testparse.c Wed Aug 14 10:29:18 2019 -0400
@@ -46,10 +46,6 @@
 #define Free NULL
 #endif

-static FILE* g_out;
-#define printf(...) fprintf(g_out, __VA_ARGS__)
-#define putchar(c) putc((c), g_out)
-
 static inline void do_indent(const unsigned int indent)
 {
     unsigned int i;
@@ -79,15 +75,15 @@
                            unsigned int indent)
 {
     static const char *symclasses[] = {
-            "scalar", "vector", "row-major matrix",
-            "column-major matrix", "object", "struct"
+        "scalar", "vector", "row-major matrix",
+        "column-major matrix", "object", "struct"
     };

     static const char *symtypes[] = {
-            "void", "bool", "int", "float", "string", "texture",
-            "texture1d", "texture2d", "texture3d", "texturecube",
-            "sampler", "sampler1d", "sampler2d", "sampler3d",
-            "samplercube", "pixelshader", "vertexshader", "unsupported"
+        "void", "bool", "int", "float", "string", "texture",
+        "texture1d", "texture2d", "texture3d", "texturecube",
+        "sampler", "sampler1d", "sampler2d", "sampler3d",
+        "samplercube", "pixelshader", "vertexshader", "unsupported"
     };

     INDENT();
@@ -132,7 +128,7 @@
         for (i = 0; i < symbol_count; i++, sym++)
         {
             static const char *regsets[] = {
-                    "bool", "int4", "float4", "sampler"
+                "bool", "int4", "float4", "sampler"
             };

             INDENT(); printf("    * %d: \"%s\"\n", i, sym->name);
@@ -223,10 +219,10 @@
     int i, j;

     static const char *opcodestr[] = {
-            "nop", "mov", "neg", "rcp", "frc", "exp", "log", "rsq", "sin", "cos",
-            "asin", "acos", "atan", "min", "max", "lt", "ge", "add", "mul",
-            "atan2", "div", "cmp", "movc", "dot", "noise", "min", "max", "lt",
-            "ge", "add", "mul", "atan2", "div", "dot", "noise"
+        "nop", "mov", "neg", "rcp", "frc", "exp", "log", "rsq", "sin", "cos",
+        "asin", "acos", "atan", "min", "max", "lt", "ge", "add", "mul",
+        "atan2", "div", "cmp", "movc", "dot", "noise", "min", "max", "lt",
+        "ge", "add", "mul", "atan2", "div", "dot", "noise"
     };

     INDENT(); printf("PRESHADER:\n");
@@ -267,10 +263,11 @@
         printf("\n");
         for (i = 0; i < count; i++)
         {
-            static const char *usagenames[] = { "<unknown>",
-                    "position", "blendweight", "blendindices", "normal",
-                    "psize", "texcoord", "tangent", "binormal", "tessfactor",
-                    "positiont", "color", "fog", "depth", "sample"
+            static const char *usagenames[] = {
+                "<unknown>",
+                "position", "blendweight", "blendindices", "normal",
+                "psize", "texcoord", "tangent", "binormal", "tessfactor",
+                "positiont", "color", "fog", "depth", "sample"
             };
             const MOJOSHADER_attribute *a = &attributes[i];
             char numstr[16] = { 0 };
@@ -287,7 +284,7 @@

 static void print_shader(const char *fname, const MOJOSHADER_parseData *pd,
-                         unsigned int indent, const char* srcfile)
+                         unsigned int indent)
 {
     INDENT(); printf("PROFILE: %s\n", pd->profile);
     if (pd->error_count > 0)
@@ -327,12 +324,12 @@
                 if (c->type == MOJOSHADER_UNIFORM_FLOAT)
                 {
                     printf("%f %f %f %f", c->value.f[0], c->value.f[1],
-                           c->value.f[2], c->value.f[3]);
+                                          c->value.f[2], c->value.f[3]);
                 } // if
                 else if (c->type == MOJOSHADER_UNIFORM_INT)
                 {
                     printf("%d %d %d %d", c->value.i[0], c->value.i[1],
-                           c->value.i[2], c->value.i[3]);
+                                          c->value.i[2], c->value.i[3]);
                 } // else if
                 else if (c->type == MOJOSHADER_UNIFORM_BOOL)
                 {
@@ -406,36 +403,14 @@
         {
             const char *output;
             int output_len;
-            int i, is_spirv;
+            int i;

-            is_spirv = strcmp(pd->profile, "spirv") == 0;
-
-            if (is_spirv)
+            if (strcmp(pd->profile, "spirv") == 0)
             {
 #if SUPPORT_PROFILE_SPIRV && defined(MOJOSHADER_HAS_SPIRV_TOOLS)
                 int binary_len = pd->output_len - sizeof(SpirvPatchTable);
-                #if 0
-                const char *shader_type = NULL;
-                switch (pd->shader_type)
-                {
-                    case MOJOSHADER_TYPE_PIXEL: shader_type = "ps"; break;
-                    case MOJOSHADER_TYPE_VERTEX: shader_type = "vs"; break;
-                    case MOJOSHADER_TYPE_GEOMETRY: shader_type = "gs"; break;
-                    default: assert(!"Invalid shader type");
-                }
-                #endif

-                #if 0
-                {
-                    char filename[512];
-                    snprintf(filename, sizeof(filename), "%s.%s.%s.spv", srcfile, pd->mainfn, shader_type);
-                    FILE* f = fopen(filename, "wb");
-                    fwrite(pd->output, 1, binary_len, f);
-                    fclose(f);
-                }
-                #endif
-
-                uint32_t *words = (uint32_t *)pd->output;
+                uint32_t *words = (uint32_t *) pd->output;
                 size_t word_count = binary_len / 4;

                 spv_text text;
@@ -447,16 +422,6 @@
                 {
                     output = text->str;
                     output_len = text->length;
-
-                    #if 0
-                    {
-                        char filename[512];
-                        snprintf(filename, sizeof(filename), "%s.%s.spvasm", pd->mainfn, shader_type);
-                        FILE* fdis = fopen(filename, "wt");
-                        fwrite(output, 1, output_len, fdis);
-                        fclose(fdis);
-                    }
-                    #endif
                 } // if
                 else
                 {
@@ -469,16 +434,6 @@
                     fprintf(stderr, "\nVALIDATION FAILURE: %s\n\n", diagnostic->error);
                 } // if

-                #if 0
-                {
-                    char filename[512];
-                    snprintf(filename, sizeof(filename), "%s.%s.spvasm", pd->mainfn, shader_type);
-                    FILE* fdis = fopen(filename, "wb");
-                    fwrite(pd->output, 1, binary_len, fdis);
-                    fclose(fdis);
-                }
-                #endif
-
                 if (disResult != SPV_SUCCESS || validateResult != SPV_SUCCESS)
                 {
                     exit(EXIT_FAILURE);
@@ -534,35 +489,35 @@
     printf("VALUE: %s -> %s\n", value->name, value->semantic);

     static const char *classes[] =
-            {
-                    "SCALAR",
-                    "VECTOR",
-                    "ROW-MAJOR MATRIX",
-                    "COLUMN-MAJOR MATRIX",
-                    "OBJECT",
-                    "STRUCT"
-            };
+    {
+        "SCALAR",
+        "VECTOR",
+        "ROW-MAJOR MATRIX",
+        "COLUMN-MAJOR MATRIX",
+        "OBJECT",
+        "STRUCT"
+    };
     static const char *types[] =
-            {
-                    "VOID",
-                    "BOOL",
-                    "INT",
-                    "FLOAT",
-                    "STRING",
-                    "TEXTURE",
-                    "TEXTURE1D",
-                    "TEXTURE2D",
-                    "TEXTURE3D",
-                    "TEXTURECUBE",
-                    "SAMPLER",
-                    "SAMPLER1D",
-                    "SAMPLER2D",
-                    "SAMPLER3D",
-                    "SAMPLERCUBE",
-                    "PIXELSHADER",
-                    "VERTEXSHADER",
-                    "UNSUPPORTED"
-            };
+    {
+        "VOID",
+        "BOOL",
+        "INT",
+        "FLOAT",
+        "STRING",
+        "TEXTURE",
+        "TEXTURE1D",
+        "TEXTURE2D",
+        "TEXTURE3D",
+        "TEXTURECUBE",
+        "SAMPLER",
+        "SAMPLER1D",
+        "SAMPLER2D",
+        "SAMPLER3D",
+        "SAMPLERCUBE",
+        "PIXELSHADER",
+        "VERTEXSHADER",
+        "UNSUPPORTED"
+    };
     do_indent(indent + 1);
     printf("CLASS: %s\n", classes[value->type.parameter_class]);
     do_indent(indent + 1);
@@ -575,10 +530,10 @@
     printf("TOTAL VALUES: %d\n", value->value_count);

     if (value->type.parameter_type == MOJOSHADER_SYMTYPE_SAMPLER
-        || value->type.parameter_type == MOJOSHADER_SYMTYPE_SAMPLER1D
-        || value->type.parameter_type == MOJOSHADER_SYMTYPE_SAMPLER2D
-        || value->type.parameter_type == MOJOSHADER_SYMTYPE_SAMPLER3D
-        || value->type.parameter_type == MOJOSHADER_SYMTYPE_SAMPLERCUBE)
+     || value->type.parameter_type == MOJOSHADER_SYMTYPE_SAMPLER1D
+     || value->type.parameter_type == MOJOSHADER_SYMTYPE_SAMPLER2D
+     || value->type.parameter_type == MOJOSHADER_SYMTYPE_SAMPLER3D
+     || value->type.parameter_type == MOJOSHADER_SYMTYPE_SAMPLERCUBE)
     {
         do_indent(indent + 1);
         printf("SAMPLER VALUES:\n");
@@ -587,26 +542,26 @@
             MOJOSHADER_effectSamplerState *state = &value->valuesSS[i];

             static const char *samplerstatetypes[] =
-                    {
-                            "UNKNOWN0",
-                            "UNKNOWN1",
-                            "UNKNOWN2",
-                            "UNKNOWN3",
-                            "TEXTURE",
-                            "ADDRESSU",
-                            "ADDRESSV",
-                            "ADDRESSW",
-                            "BORDERCOLOR",
-                            "MAGFILTER",
-                            "MINFILTER",
-                            "MIPFILTER",
-                            "MIPMAPLODBIAS",
-                            "MAXMIPLEVEL",
-                            "MAXANISOTROPY",
-                            "SRGBTEXTURE",
-                            "ELEMENTINDEX",
-                            "DMAPOFFSET",
-                    };
+            {
+                "UNKNOWN0",
+                "UNKNOWN1",
+                "UNKNOWN2",
+                "UNKNOWN3",
+                "TEXTURE",
+                "ADDRESSU",
+                "ADDRESSV",
+                "ADDRESSW",
+                "BORDERCOLOR",
+                "MAGFILTER",
+                "MINFILTER",
+                "MIPFILTER",
+                "MIPMAPLODBIAS",
+                "MAXMIPLEVEL",
+                "MAXANISOTROPY",
+                "SRGBTEXTURE",
+                "ELEMENTINDEX",
+                "DMAPOFFSET",
+            };
             do_indent(indent + 2);
             printf("TYPE: %s -> ", samplerstatetypes[state->type]);

@@ -631,26 +586,26 @@
         do
         {
             static const char *prints[] =
-                    {
-                            "%X ",
-                            "%d ",
-                            "%d ",
-                            "%.2f ",
-                            "%d ",
-                            "%d ",
-                            "%d ",
-                            "%d ",
-                            "%d ",
-                            "%d ",
-                            "SAMPLER?! ",
-                            "SAMPLER?! ",
-                            "SAMPLER?! ",
-                            "SAMPLER?! ",
-                            "SAMPLER?! ",
-                            "%d ",
-                            "%d ",
-                            "%X "
-                    };
+            {
+                "%X ",
+                "%d ",
+                "%d ",
+                "%.2f ",
+                "%d ",
+                "%d ",
+                "%d ",
+                "%d ",
+                "%d ",
+                "%d ",
+                "SAMPLER?! ",
+                "SAMPLER?! ",
+                "SAMPLER?! ",
+                "SAMPLER?! ",
+                "SAMPLER?! ",
+                "%d ",
+                "%d ",
+                "%X "
+            };
             for (r = 0; r < value->type.rows; r++)
             {
                 do_indent(indent + 2);
@@ -670,7 +625,7 @@

 static void print_effect(const char *fname, const MOJOSHADER_effect *effect,
-                         const unsigned int indent, const char* srcfile)
+                         const unsigned int indent)
 {
     INDENT();
     printf("PROFILE: %s\n", effect->profile);
@@ -683,8 +638,8 @@
             const MOJOSHADER_error *err = &effect->errors[i];
             INDENT();
             printf("%s:%d: ERROR: %s\n",
-                   err->filename ? err->filename : fname,
-                   err->error_position, err->error);
+                    err->filename ? err->filename : fname,
+                    err->error_position, err->error);
         } // for
     } // if
     else
@@ -738,7 +693,7 @@
         {
             INDENT();
             if (object->type == MOJOSHADER_SYMTYPE_PIXELSHADER
-                || object->type == MOJOSHADER_SYMTYPE_VERTEXSHADER)
+             || object->type == MOJOSHADER_SYMTYPE_VERTEXSHADER)
             {
                 if (object->shader.is_preshader)
                 {
@@ -751,24 +706,24 @@
                 {
                     printf("OBJECT #%d: SHADER, technique %u, pass %u\n", i,
                            object->shader.technique, object->shader.pass);
-                    print_shader(fname, object->shader.shader, indent + 1, srcfile);
+                    print_shader(fname, object->shader.shader, indent + 1);
                 } // else
             } // if
             else if (object->type == MOJOSHADER_SYMTYPE_STRING)
                 printf("OBJECT #%d: STRING, '%s'\n", i,
                        object->string.string);
             else if (object->type == MOJOSHADER_SYMTYPE_SAMPLER
-                     || object->type == MOJOSHADER_SYMTYPE_SAMPLER1D
-                     || object->type == MOJOSHADER_SYMTYPE_SAMPLER2D
-                     || object->type == MOJOSHADER_SYMTYPE_SAMPLER3D
-                     || object->type == MOJOSHADER_SYMTYPE_SAMPLERCUBE)
+                  || object->type == MOJOSHADER_SYMTYPE_SAMPLER1D
+                  || object->type == MOJOSHADER_SYMTYPE_SAMPLER2D
+                  || object->type == MOJOSHADER_SYMTYPE_SAMPLER3D
+                  || object->type == MOJOSHADER_SYMTYPE_SAMPLERCUBE)
                 printf("OBJECT #%d: MAPPING, '%s'\n", i,
                        object->mapping.name);
             else if (object->type == MOJOSHADER_SYMTYPE_TEXTURE
-                     || object->type == MOJOSHADER_SYMTYPE_TEXTURE1D
-                     || object->type == MOJOSHADER_SYMTYPE_TEXTURE2D
-                     || object->type == MOJOSHADER_SYMTYPE_TEXTURE3D
-                     || object->type == MOJOSHADER_SYMTYPE_TEXTURECUBE)
+                  || object->type == MOJOSHADER_SYMTYPE_TEXTURE1D
+                  || object->type == MOJOSHADER_SYMTYPE_TEXTURE2D
+                  || object->type == MOJOSHADER_SYMTYPE_TEXTURE3D
+                  || object->type == MOJOSHADER_SYMTYPE_TEXTURECUBE)
                 printf("OBJECT #%d: TEXTURE\n", i);
             else
                 printf("UNKNOWN OBJECT: #%d\n", i);
@@ -781,7 +736,7 @@

 static int do_parse(const char *fname, const unsigned char *buf,
-                    const int len, const char *prof, const char* srcfile)
+                    const int len, const char *prof)
 {
     int retval = 0;

@@ -808,13 +763,15 @@
                         const MOJOSHADER_parseData *shader = object->shader.shader;
                         if (shader)
                             error_count += shader->error_count;
-                    }
+                    } // if
+                    break;
+                default:
                     break;
             }
         }
         retval = (error_count == 0);
         printf("EFFECT:\n");
-        print_effect(fname, effect, 1, srcfile);
+        print_effect(fname, effect, 1);
         MOJOSHADER_freeEffect(effect);
 #else
         printf("Is an effect, but effect support is disabled!\n");
@@ -828,66 +785,49 @@
                               NULL, 0, Malloc, Free, NULL);
         retval = (pd->error_count == 0);
         printf("SHADER:\n");
-        print_shader(fname, pd, 1, srcfile);
+        print_shader(fname, pd, 1);
         MOJOSHADER_freeParseData(pd);
     } // else

     return retval;
 } // do_parse

-static void print_logo(FILE* f)
+static void print_logo()
 {
-    fprintf(f, "MojoShader testparse\n");
-    fprintf(f, "Compiled against changeset %s\n", MOJOSHADER_CHANGESET);
-    fprintf(f, "Linked against changeset %s\n", MOJOSHADER_changeset());
-    fprintf(f, "\n");
+    printf("MojoShader testparse\n");
+    printf("Compiled against changeset %s\n", MOJOSHADER_CHANGESET);
+    printf("Linked against changeset %s\n", MOJOSHADER_changeset());
+    printf("\n");
 }

 int main(int argc, char **argv)
 {
     int retval = 0;

-    g_out = stdout;
-
     int no_logo = 0;
     int i = 1;
     while (argc - i >= 2)
     {
-        if (0 == strcmp(argv[i], "-o"))
-        {
-            FILE* f = fopen(argv[i + 1], "wb");
-            if (f)
-            {
-                if (g_out != stdout)
-                    fclose(g_out);
-
-                g_out = f;
-            }
-
-            i += 2;
-        }
-        else if (0 == strcmp(argv[i], "--no-logo"))
+        if (strcmp(argv[i], "--no-logo") == 0)
         {
             no_logo = 1;
             i += 1;
-        }
+        } // if
         else
-        {
             break;
-        }
     }

-    if (argc - i < 2)
+    if ((argc - i) < 2)
     {
         if (!no_logo)
-            print_logo(stdout);
+            print_logo();

         fprintf(stdout, "\n\nUSAGE: %s [-o <dstFile>] <profile> [file1] ... [fileN]\n\n", argv[0]);
-    }
+    } // if
     else
     {
         if (!no_logo)
-            print_logo(g_out);
+            print_logo();

         const char *profile = argv[i++];
         for (; i < argc; i++)
@@ -897,24 +837,19 @@
             {
                 fprintf(stderr, "%s\n", argv[i]);
                 fprintf(stderr, " ... fopen('%s') failed.\n", argv[i]);
-            }
+            } // if
             else
             {
                 unsigned char *buf = (unsigned char *) malloc(1000000);
                 int rc = fread(buf, 1, 1000000, io);
                 fclose(io);
-                if (!do_parse(argv[i], buf, rc, profile, argv[i]))
+                if (!do_parse(argv[i], buf, rc, profile))
                     retval = 1;
                 free(buf);
             } // else
         } // for
     } // else

-    if (g_out != stdout)
-    {
-        fclose(g_out);
-    }
-
     return retval;
 } // main
krolli commented 5 years ago

Patch applied.

Probably have to implement the vpos flip variable (no need to adjust half-pixel!)

Was planning to take a look at it after I saw the change on default branch.

Can we remove the ARB program paths from SPIR-V? Seems unlikely that a driver will support ARB_gl_spirv but not GL2...

Yeah, I don't think we'll run into a driver like that either. It is just a couple lines so I didn't think too much about it (and I kept it for parity with glsl), but it can be removed.

I was planning to remove most of the debug stuff myself, but I guess you beat me to it. On git I kept debug things on separate branch, but it was pain to work with and I figured it would be easier to remove it later.

I will miss having testparse spit output deterministically into a file using command line args instead of redirecting stdout+stderr, but I can always use some of the older revisions for that. :)

flibitijibibo commented 5 years ago

Part 2 is here! This is 100% pure style cleanup, no functionality or structural changes were made. I only had one note for this pass: We should move all the internal static spv_ functions to the top, maybe inline them as well. But, for now we're going to run the testing with this stuff exactly as-is, just in case it makes life easier before we start moving functions around.

http://www.flibitijibibo.com/spvcp2.diff

Expect the announcement to show up on my plan file...

https://icculus.org/finger/flibitijibibo

flibitijibibo commented 5 years ago

Quick update with a patch to fix the CentOS build...

http://www.flibitijibibo.com/spvcp3.diff

flibitijibibo commented 5 years ago

Spamming this thread a lot today... sorry about that >_>

I just checked my test list and it looks like we're almost good to go; I need to update FEZ to 1.13 but other than that...

FEZ     GOOD, NEEDS 1.13
Dust: AET   GOOD
Murder Miners   NOTGOOD
Escape Goat 2   NOTGOOD
Capsized    GOOD
Apotheon    GOOD
Cryptark    GOOD
Bastion     GOOD
Reus        GOOD
Celeste     GOOD

... all you have to do is extract my custom FNA build to the game directory and it'll work. You can even set FNATEST_DISABLE_SPIRV=1 to be sure GLSL still works.

The only thing hanging us up now is vpFlip not working, seems NVIDIA Linux generates GL_INVALID_OPERATION in glProgramViewportInfo. Once that's fixed and all the patches are in I'll do a new build and get this into testing!

flibitijibibo commented 5 years ago

We hacked on this in the Discord and now the builds work! Expect the plan file update tomorrow (maybe).

flibitijibibo commented 5 years ago

Just pushed some updates to upstream that affects the GLSL texld instructions. texld is still kinda eh...

https://github.com/FNA-XNA/MojoShader/blob/fna/profiles/mojoshader_profile_glsl.c#L1696-L1819

... but check out texldd/texldl:

https://github.com/FNA-XNA/MojoShader/blob/fna/profiles/mojoshader_profile_glsl.c#L2246-L2300

https://github.com/FNA-XNA/MojoShader/blob/fna/profiles/mojoshader_profile_glsl.c#L2326-L2373

If anyone wants those functions, hopefully this is WAY easier to sit through.

flibitijibibo commented 5 years ago

Sent krolli some patches for TEXLD/TEXLDL/TEXLDD. They seemed simple enough, so I think those are okay...? (Unless I screwed up the source arg loading, which it sounds like I did.)

Something really amusing that happened this week was D9VK's 0.20 release having the ~exact~ list of instructions that we ignored for this bounty, may be worth exploring:

flibitijibibo commented 4 years ago

TEXM3X3SPEC got done in the latest d9vk.

krolli commented 4 years ago

Went over the remaining opcodes and code paths I knew were missing for parity with glsl emittor and got them out of the way. In other words:

I also have some minimal shaders that use those instructions and I think there might be some bugs in GLSL emitter and even mojoshader parser when dealing with some of those. I will have to take a better look at things to be sure though.

flibitijibibo commented 4 years ago

SPIR-V support has been merged! Thanks to everyone who helped out with this.