Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Add support for emitting SPIR-V shaders.
Co-authors include:
- Angus Holder <aholder97@gmail.com>
- Melker Narikka <meklu@meklu.org>
- Caleb Cornett <caleb.cornett@outlook.com>
- Ethan Lee <flibitijibibo@flibitijibibo.com>
  • Loading branch information
krolli committed Dec 31, 2019
1 parent f2d0bd0 commit 801b57d
Show file tree
Hide file tree
Showing 13 changed files with 5,798 additions and 12 deletions.
15 changes: 15 additions & 0 deletions CMakeLists.txt
Expand Up @@ -10,6 +10,7 @@ OPTION(PROFILE_GLSL "Build MojoShader with support for the GLSL profile" ON)
OPTION(PROFILE_ARB1 "Build MojoShader with support for the ARB1 profile" ON)
OPTION(PROFILE_ARB1_NV "Build MojoShader with support for the ARB1_NV profile" ON)
OPTION(PROFILE_METAL "Build MojoShader with support for the Metal profile" ON)
OPTION(PROFILE_SPIRV "Build MojoShader with support for the SPIR-V profile" ON)
OPTION(EFFECT_SUPPORT "Build MojoShader with support for Effect framework files" ON)
OPTION(COMPILER_SUPPORT "Build MojoShader with support for HLSL source files" OFF)
OPTION(FLIP_VIEWPORT "Build MojoShader with the ability to flip the GL viewport" OFF)
Expand Down Expand Up @@ -124,6 +125,9 @@ ENDIF(NOT PROFILE_ARB1_NV)
IF(NOT PROFILE_METAL)
ADD_DEFINITIONS(-DSUPPORT_PROFILE_METAL=0)
ENDIF(NOT PROFILE_METAL)
IF(NOT PROFILE_SPIRV)
ADD_DEFINITIONS(-DSUPPORT_PROFILE_SPIRV=0)
ENDIF(NOT PROFILE_SPIRV)

IF(EFFECT_SUPPORT)
IF(UNIX)
Expand Down Expand Up @@ -159,6 +163,7 @@ ADD_LIBRARY(mojoshader ${LIBRARY_FORMAT}
profiles/mojoshader_profile_d3d.c
profiles/mojoshader_profile_glsl.c
profiles/mojoshader_profile_metal.c
profiles/mojoshader_profile_spirv.c
profiles/mojoshader_profile_common.c
)
IF(EFFECT_SUPPORT)
Expand Down Expand Up @@ -219,8 +224,18 @@ IF(COMPILER_SUPPORT)
ENDIF(SDL2)
ENDIF(COMPILER_SUPPORT)

FIND_PATH(SPIRV_TOOLS_INCLUDE_DIR "spirv-tools/libspirv.h" PATH_SUFFIXES "include")
FIND_LIBRARY(SPIRV_TOOLS_LIBRARY NAMES SPIRV-Tools-shared)
IF(SPIRV_TOOLS_INCLUDE_DIR AND SPIRV_TOOLS_LIBRARY)
INCLUDE_DIRECTORIES(${SPIRV_TOOLS_INCLUDE_DIR})
ADD_DEFINITIONS(-DMOJOSHADER_HAS_SPIRV_TOOLS)
ENDIF(SPIRV_TOOLS_INCLUDE_DIR AND SPIRV_TOOLS_LIBRARY)

ADD_EXECUTABLE(testparse utils/testparse.c)
TARGET_LINK_LIBRARIES(testparse mojoshader ${LIBM} ${CARBON_FRAMEWORK})
IF(SPIRV_TOOLS_INCLUDE_DIR AND SPIRV_TOOLS_LIBRARY)
TARGET_LINK_LIBRARIES(testparse ${SPIRV_TOOLS_LIBRARY})
ENDIF(SPIRV_TOOLS_INCLUDE_DIR AND SPIRV_TOOLS_LIBRARY)
ADD_EXECUTABLE(testoutput utils/testoutput.c)
TARGET_LINK_LIBRARIES(testoutput mojoshader ${LIBM} ${CARBON_FRAMEWORK})
IF(COMPILER_SUPPORT)
Expand Down
36 changes: 36 additions & 0 deletions mojoshader.c
Expand Up @@ -263,6 +263,15 @@ PREDECLARE_PROFILE(METAL)
PREDECLARE_PROFILE(ARB1)
#endif

#if !SUPPORT_PROFILE_SPIRV
#define PROFILE_EMITTER_SPIRV(op)
#else
#undef AT_LEAST_ONE_PROFILE
#define AT_LEAST_ONE_PROFILE 1
#define PROFILE_EMITTER_SPIRV(op) emit_SPIRV_##op,
PREDECLARE_PROFILE(SPIRV)
#endif

#if !AT_LEAST_ONE_PROFILE
#error No profiles are supported. Fix your build.
#endif
Expand Down Expand Up @@ -300,6 +309,9 @@ static const Profile profiles[] =
#if SUPPORT_PROFILE_METAL
DEFINE_PROFILE(METAL)
#endif
#if SUPPORT_PROFILE_SPIRV
DEFINE_PROFILE(SPIRV)
#endif
};

#undef DEFINE_PROFILE
Expand All @@ -321,6 +333,7 @@ static const struct { const char *from; const char *to; } profileMap[] =
PROFILE_EMITTER_GLSL(op) \
PROFILE_EMITTER_ARB1(op) \
PROFILE_EMITTER_METAL(op) \
PROFILE_EMITTER_SPIRV(op) \
}

static int parse_destination_token(Context *ctx, DestArgInfo *info)
Expand Down Expand Up @@ -3445,6 +3458,28 @@ static MOJOSHADER_parseData *build_parsedata(Context *ctx)
retval->preshader = ctx->preshader;
retval->mainfn = ctx->mainfn;

#if SUPPORT_PROFILE_SPIRV
if (strcmp(retval->profile, "spirv") == 0)
{
size_t i, max;
int binary_size = retval->output_len - sizeof(SpirvPatchTable);
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;
if (table->array_ivec4.offset) binary[table->array_ivec4.offset] = table->array_ivec4.location;
if (table->array_bool.offset) binary[table->array_bool.offset] = table->array_bool.location;

for (i = 0, max = STATICARRAYLEN(table->samplers); i < max; i++)
{
SpirvPatchEntry entry = table->samplers[i];
if (entry.offset)
binary[entry.offset] = entry.location;
} // for
} // if
#endif

// we don't own these now, retval does.
ctx->ctab.symbols = NULL;
ctx->preshader = NULL;
Expand Down Expand Up @@ -3828,6 +3863,7 @@ int MOJOSHADER_maxShaderModel(const char *profile)
PROFILE_SHADER_MODEL(MOJOSHADER_PROFILE_NV3, 2);
PROFILE_SHADER_MODEL(MOJOSHADER_PROFILE_NV4, 3);
PROFILE_SHADER_MODEL(MOJOSHADER_PROFILE_METAL, 3);
PROFILE_SHADER_MODEL(MOJOSHADER_PROFILE_SPIRV, 3);
#undef PROFILE_SHADER_MODEL
return -1; // unknown profile?
} // MOJOSHADER_maxShaderModel
Expand Down
5 changes: 5 additions & 0 deletions mojoshader.h
Expand Up @@ -719,6 +719,11 @@ typedef struct MOJOSHADER_parseData
*/
#define MOJOSHADER_PROFILE_METAL "metal"

/*
* Profile string for SPIR-V binary output
*/
#define MOJOSHADER_PROFILE_SPIRV "spirv"

/*
* Determine the highest supported Shader Model for a profile.
*/
Expand Down
43 changes: 43 additions & 0 deletions mojoshader_common.c
Expand Up @@ -1036,6 +1036,49 @@ ssize_t buffer_find(Buffer *buffer, const size_t start,
return -1; // no match found.
} // buffer_find

void buffer_patch(Buffer *buffer, const size_t start,
const void *_data, const size_t len)
{
if (len == 0)
return; // Nothing to do.

if ((start + len) > buffer->total_bytes)
return; // definitely can't patch.

// Find the start point somewhere in the center of a buffer.
BufferBlock *item = buffer->head;
size_t pos = 0;
if (start > 0)
{
while (1)
{
assert(item != NULL);
if ((pos + item->bytes) > start) // start is in this block.
break;

pos += item->bytes;
item = item->next;
} // while
} // if

const uint8 *data = (const uint8 *) _data;
size_t write_pos = start - pos;
size_t write_remain = len;
size_t written = 0;
while (write_remain)
{
size_t write_end = write_pos + write_remain;
if (write_end > item->bytes)
write_end = item->bytes;

size_t to_write = write_end - write_pos;
memcpy(item->data + write_pos, data + written, to_write);
write_remain -= to_write;
written += to_write;
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)
Expand Down
42 changes: 42 additions & 0 deletions mojoshader_internal.h
Expand Up @@ -64,6 +64,10 @@
#define SUPPORT_PROFILE_METAL 1
#endif

#ifndef SUPPORT_PROFILE_SPIRV
#define SUPPORT_PROFILE_SPIRV 1
#endif

#if SUPPORT_PROFILE_ARB1_NV && !SUPPORT_PROFILE_ARB1
#error nv profiles require arb1 profile. Fix your build.
#endif
Expand Down Expand Up @@ -265,6 +269,8 @@ char *buffer_merge(Buffer **buffers, const size_t n, size_t *_len);
void buffer_destroy(Buffer *buffer);
ssize_t buffer_find(Buffer *buffer, const size_t start,
const void *data, const size_t len);
void buffer_patch(Buffer *buffer, const size_t start,
const void *data, const size_t len);



Expand Down Expand Up @@ -596,6 +602,42 @@ void MOJOSHADER_print_debug_token(const char *subsystem, const char *token,
const unsigned int tokenlen,
const Token tokenval);


#if SUPPORT_PROFILE_SPIRV
// Patching SPIR-V binaries before linking is needed to ensure locations do not
// overlap between shader stages. Unfortunately, OpDecorate takes Literal, so we
// can't use Result <id> from OpSpecConstant and leave this up to specialization
// mechanism.
// Patch table must be propagated from parsing to program linking, but since
// MOJOSHADER_parseData is public and I'd like to avoid changing ABI and exposing
// this, it is appended to MOJOSHADER_parseData::output using postflight buffer.
typedef struct SpirvPatchEntry
{
uint32 offset;
int32 location;
} SpirvPatchEntry;

typedef struct SpirvPatchTable
{
SpirvPatchEntry vpflip;
SpirvPatchEntry array_vec4;
SpirvPatchEntry array_ivec4;
SpirvPatchEntry array_bool;
SpirvPatchEntry samplers[16];
int32 location_count;
union
{
// VS only; non-0 when there is PSIZE output
uint32 vs_has_psize;

// PS only; offset to TEXCOORD0 location part of OpDecorate.
// Used to find OpDecorate and patch it to BuiltIn PointCoord when
// VS outputs PSIZE.
uint32 ps_texcoord0_offset;
};
} SpirvPatchTable;
#endif

#endif // _INCLUDE_MOJOSHADER_INTERNAL_H_


Expand Down

0 comments on commit 801b57d

Please sign in to comment.