Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Add dynamic linking support for SPIR-V modules
  • Loading branch information
flibitijibibo committed Jul 7, 2020
1 parent b76b339 commit 7a9966b
Show file tree
Hide file tree
Showing 6 changed files with 421 additions and 206 deletions.
57 changes: 53 additions & 4 deletions mojoshader.h
Expand Up @@ -3558,6 +3558,7 @@ VK_DEFINE_HANDLE(VkInstance)
VK_DEFINE_HANDLE(VkDevice)
VK_DEFINE_HANDLE(VkPhysicalDevice)
VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkBuffer)
VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkShaderModule)

#endif /* !NO_MOJOSHADER_VULKAN_TYPEDEFS */

Expand All @@ -3573,6 +3574,7 @@ typedef PFN_MOJOSHADER_vkVoidFunction (VKAPI_PTR *PFN_MOJOSHADER_vkGetInstancePr

typedef struct MOJOSHADER_vkContext MOJOSHADER_vkContext;
typedef struct MOJOSHADER_vkShader MOJOSHADER_vkShader;
typedef struct MOJOSHADER_vkProgram MOJOSHADER_vkProgram;

/*
* Prepares a context to manage Vulkan shaders.
Expand Down Expand Up @@ -3671,7 +3673,7 @@ DECLSPEC const char *MOJOSHADER_vkGetError();
* This function destroys the MOJOSHADER_vkContext you pass it. If it's the
* current context, then no context will be current upon return.
*/
DECLSPEC void MOJOSHADER_vkDestroyContext();
DECLSPEC void MOJOSHADER_vkDestroyContext(MOJOSHADER_vkContext *ctx);

/*
* Compile a buffer of Direct3D shader bytecode into a Vulkan shader module.
Expand Down Expand Up @@ -3727,6 +3729,53 @@ DECLSPEC void MOJOSHADER_vkDeleteShader(MOJOSHADER_vkShader *shader);
DECLSPEC const MOJOSHADER_parseData *MOJOSHADER_vkGetShaderParseData(
MOJOSHADER_vkShader *shader);

/*
* Link a vertex and pixel shader into a working Vulkan shader program.
* (vshader) or (pshader) can NOT be NULL, unlike OpenGL.
*
* You can reuse shaders in various combinations across
* multiple programs, by relinking different pairs.
*
* It is illegal to give a vertex shader for (pshader) or a pixel shader
* for (vshader).
*
* Once you have successfully linked a program, you may render with it.
*
* Returns NULL on error, or a program handle on success.
*
* This call requires a valid MOJOSHADER_vkContext to have been made current,
* or it will crash your program. See MOJOSHADER_vkMakeContextCurrent().
*/
DECLSPEC MOJOSHADER_vkProgram *MOJOSHADER_vkLinkProgram(MOJOSHADER_vkShader *vshader,
MOJOSHADER_vkShader *pshader);

/*
* This binds the program to the active context, and does nothing particularly
* special until you start working with uniform buffers or shader modules.
*
* After binding a program, you should update any uniforms you care about
* with MOJOSHADER_vkMapUniformBufferMemory() (etc), set any vertex arrays
* using MOJOSHADER_vkGetVertexAttribLocation(), and finally call
* MOJOSHADER_vkGetShaderModules() to get the final modules. Then you may
* begin building your pipeline state objects.
*
* This call requires a valid MOJOSHADER_vkContext to have been made current,
* or it will crash your program. See MOJOSHADER_vkMakeContextCurrent().
*/
DECLSPEC void MOJOSHADER_vkBindProgram(MOJOSHADER_vkProgram *program);

/*
* Free the resources of a linked program. This will delete the shader modules
* and free memory.
*
* If the program is currently bound by MOJOSHADER_vkBindProgram(), it will
* be deleted as soon as it becomes unbound.
*
* This call requires a valid MOJOSHADER_vkContext to have been made current,
* or it will crash your program. See MOJOSHADER_vkMakeContextCurrent().
*/
DECLSPEC void MOJOSHADER_vkDeleteProgram(MOJOSHADER_vkProgram *program);

/*
* This "binds" individual shaders, which effectively means the context
* will store these shaders for later retrieval. No actual binding or
Expand Down Expand Up @@ -3816,10 +3865,10 @@ DECLSPEC int MOJOSHADER_vkGetVertexAttribLocation(MOJOSHADER_vkShader *vert,
int index);

/*
* Get the VkShaderModule from the given MOJOSHADER_vkShader.
* Get the VkShaderModules from the currently bound shader program.
*/
DECLSPEC unsigned long long MOJOSHADER_vkGetShaderModule(
MOJOSHADER_vkShader *shader);
DECLSPEC void MOJOSHADER_vkGetShaderModules(VkShaderModule *vmodule,
VkShaderModule *pmodule);

/* D3D11 interface... */

Expand Down
63 changes: 63 additions & 0 deletions mojoshader_common.c
Expand Up @@ -1055,5 +1055,68 @@ size_t MOJOSHADER_printFloat(char *text, size_t maxlen, float arg)
return (text - textstart);
} // MOJOSHADER_printFloat

#if SUPPORT_PROFILE_SPIRV
#include "spirv/spirv.h"
#include "spirv/GLSL.std.450.h"
void MOJOSHADER_spirv_link_attributes(const MOJOSHADER_parseData *vertex,
const MOJOSHADER_parseData *pixel)
{
int i;
uint32 attr_loc = 1; // 0 is reserved for COLOR0
uint32 vOffset, pOffset;
int vDataLen = vertex->output_len - sizeof(SpirvPatchTable);
int pDataLen = pixel->output_len - sizeof(SpirvPatchTable);
SpirvPatchTable *vTable = (SpirvPatchTable *) &vertex->output[vDataLen];
SpirvPatchTable *pTable = (SpirvPatchTable *) &pixel->output[pDataLen];
const uint32 texcoord0Loc = pTable->attrib_offsets[MOJOSHADER_USAGE_TEXCOORD][0];

for (i = 0; i < pixel->attribute_count; i++)
{
const MOJOSHADER_attribute *pAttr = &pixel->attributes[i];
if (pAttr->usage == MOJOSHADER_USAGE_COLOR && pAttr->index == 0)
continue;

// The input may not exist in the output list!
pOffset = pTable->attrib_offsets[pAttr->usage][pAttr->index];
vOffset = vTable->attrib_offsets[pAttr->usage][pAttr->index];
((uint32 *) pixel->output)[pOffset] = attr_loc;
if (vOffset)
((uint32 *) vertex->output)[vOffset] = attr_loc;
attr_loc++;
} // for

// There may be outputs not present in the input list!
for (i = 0; i < vertex->output_count; i++)
{
const MOJOSHADER_attribute *vAttr = &vertex->outputs[i];
if (vAttr->usage == MOJOSHADER_USAGE_POSITION && vAttr->index == 0)
continue;
if (vAttr->usage == MOJOSHADER_USAGE_COLOR && vAttr->index == 0)
continue;

if (!pTable->attrib_offsets[vAttr->usage][vAttr->index])
{
vOffset = vTable->attrib_offsets[vAttr->usage][vAttr->index];
((uint32 *) vertex->output)[vOffset] = attr_loc++;
} // if
} // while

// gl_PointCoord support
if (texcoord0Loc)
{
if (vTable->attrib_offsets[MOJOSHADER_USAGE_POINTSIZE][0] > 0)
{
((uint32 *) pixel->output)[texcoord0Loc - 1] = SpvDecorationBuiltIn;
((uint32 *) pixel->output)[texcoord0Loc] = SpvBuiltInPointCoord;
} // if
else
{
// texcoord0Loc should already have attr_loc from the above work!
((uint32 *) pixel->output)[texcoord0Loc - 1] = SpvDecorationLocation;
} // else
} // if
} // MOJOSHADER_spirv_link_attributes
#endif

// end of mojoshader_common.c ...

17 changes: 7 additions & 10 deletions mojoshader_internal.h
Expand Up @@ -743,23 +743,20 @@ typedef struct SpirvPatchEntry

typedef struct SpirvPatchTable
{
// Patches for uniforms
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;
};

// Patches for linking vertex output/pixel input
uint32 attrib_offsets[MOJOSHADER_USAGE_TOTAL][16];
} SpirvPatchTable;

void MOJOSHADER_spirv_link_attributes(const MOJOSHADER_parseData *vertex,
const MOJOSHADER_parseData *pixel);
#endif

#endif // _INCLUDE_MOJOSHADER_INTERNAL_H_
Expand Down
32 changes: 12 additions & 20 deletions mojoshader_opengl.c
Expand Up @@ -448,14 +448,14 @@ static const SpirvPatchTable* spv_getPatchTable(MOJOSHADER_glShader *shader)
return (const SpirvPatchTable *) (pd->output + table_offset);
} // spv_getPatchTable

static int spv_CompileShader(const MOJOSHADER_parseData *pd, int32 base_location, GLuint *s, int32 patch_pcoord)
static int spv_CompileShader(const MOJOSHADER_parseData *pd, int32 base_location, GLuint *s)
{
GLint ok = 0;

GLsizei data_len = pd->output_len - sizeof(SpirvPatchTable);
const GLvoid* data = pd->output;
uint32 *patched_data = NULL;
if (base_location || patch_pcoord)
if (base_location)
{
size_t i, max;

Expand All @@ -474,16 +474,6 @@ static int spv_CompileShader(const MOJOSHADER_parseData *pd, int32 base_location
patched_data[entry.offset] += base_location;
} // for

if (patch_pcoord && table->ps_texcoord0_offset)
{
// Subtract 3 to get from Location value offset to start of op.
uint32 op_base = table->ps_texcoord0_offset - 3;
assert(patched_data[op_base+0] == (SpvOpDecorate | (4 << 16)));
assert(patched_data[op_base+2] == SpvDecorationLocation);
patched_data[op_base+2] = SpvDecorationBuiltIn;
patched_data[op_base+3] = SpvBuiltInPointCoord;
} // if

data = patched_data;
} // if

Expand Down Expand Up @@ -521,27 +511,29 @@ static GLuint impl_SPIRV_LinkProgram(MOJOSHADER_glShader *vshader,
MOJOSHADER_glShader *pshader)
{
GLint ok = 0;

// Shader compilation postponed until linking due to uniform locations being global in program.
// To avoid overlap between VS and PS, we need to know about other shader stages to assign final
// uniform locations before compilation.
GLuint vs_handle = 0;
int32 base_location = 0;
int32 patch_pcoord = 0;

// Shader compilation postponed until linking due to locations being global
// in program. To avoid overlap between VS and PS, we need to know about
// other shader stages to assign final uniform/attrib locations before
// compilation.

MOJOSHADER_spirv_link_attributes(vshader->parseData, pshader->parseData);

if (vshader)
{
if (!spv_CompileShader(vshader->parseData, base_location, &vs_handle, patch_pcoord))
if (!spv_CompileShader(vshader->parseData, base_location, &vs_handle))
return 0;

const SpirvPatchTable* patch_table = spv_getPatchTable(vshader);
base_location += patch_table->location_count;
patch_pcoord = patch_table->vs_has_psize;
} // if

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

Expand Down

0 comments on commit 7a9966b

Please sign in to comment.