mojoshader_vulkan.c
changeset 1277 da61410edbc9
parent 1276 89c389e4112f
child 1280 d2a0d76469f9
equal deleted inserted replaced
1276:89c389e4112f 1277:da61410edbc9
    24 
    24 
    25 // Internal struct defs...
    25 // Internal struct defs...
    26 
    26 
    27 typedef struct MOJOSHADER_vkShader
    27 typedef struct MOJOSHADER_vkShader
    28 {
    28 {
    29     VkShaderModule shaderModule;
       
    30     const MOJOSHADER_parseData *parseData;
    29     const MOJOSHADER_parseData *parseData;
       
    30     uint16_t tag;
    31     uint32_t refcount;
    31     uint32_t refcount;
    32 } MOJOSHADER_vkShader;
    32 } MOJOSHADER_vkShader;
       
    33 
       
    34 typedef struct MOJOSHADER_vkProgram
       
    35 {
       
    36     VkShaderModule vertexModule;
       
    37     VkShaderModule pixelModule;
       
    38     MOJOSHADER_vkShader *vertexShader;
       
    39     MOJOSHADER_vkShader *pixelShader;
       
    40 } MOJOSHADER_vkProgram;
    33 
    41 
    34 typedef struct MOJOSHADER_vkUniformBuffer
    42 typedef struct MOJOSHADER_vkUniformBuffer
    35 {
    43 {
    36     VkBuffer buffer;
    44     VkBuffer buffer;
    37     VkDeviceMemory deviceMemory;
    45     VkDeviceMemory deviceMemory;
    87     uint8_t ps_reg_file_b[MAX_REG_FILE_B * 4];
    95     uint8_t ps_reg_file_b[MAX_REG_FILE_B * 4];
    88 
    96 
    89     MOJOSHADER_vkUniformBuffer *vertUboBuffer;
    97     MOJOSHADER_vkUniformBuffer *vertUboBuffer;
    90     MOJOSHADER_vkUniformBuffer *fragUboBuffer;
    98     MOJOSHADER_vkUniformBuffer *fragUboBuffer;
    91 
    99 
    92     MOJOSHADER_vkShader *vertexShader;
   100     MOJOSHADER_vkProgram *bound_program;
    93     MOJOSHADER_vkShader *pixelShader;
   101     HashTable *linker_cache;
    94 
   102 
    95     #define VULKAN_INSTANCE_FUNCTION(ret, func, params) \
   103     #define VULKAN_INSTANCE_FUNCTION(ret, func, params) \
    96         vkfntype_MOJOSHADER_##func func;
   104         vkfntype_MOJOSHADER_##func func;
    97     #define VULKAN_DEVICE_FUNCTION(ret, func, params) \
   105     #define VULKAN_DEVICE_FUNCTION(ret, func, params) \
    98         vkfntype_MOJOSHADER_##func func;
   106         vkfntype_MOJOSHADER_##func func;
    99     #include "mojoshader_vulkan_vkfuncs.h"
   107     #include "mojoshader_vulkan_vkfuncs.h"
   100 } MOJOSHADER_vkContext;
   108 } MOJOSHADER_vkContext;
   101 
   109 
   102 static MOJOSHADER_vkContext *ctx = NULL;
   110 static MOJOSHADER_vkContext *ctx = NULL;
       
   111 static uint16_t tagCounter = 1;
   103 
   112 
   104 static uint8_t find_memory_type(
   113 static uint8_t find_memory_type(
   105     MOJOSHADER_vkContext *ctx,
   114     MOJOSHADER_vkContext *ctx,
   106 	uint32_t typeFilter,
   115 	uint32_t typeFilter,
   107 	VkMemoryPropertyFlags properties,
   116 	VkMemoryPropertyFlags properties,
   350 static int shader_bytecode_len(MOJOSHADER_vkShader *shader)
   359 static int shader_bytecode_len(MOJOSHADER_vkShader *shader)
   351 {
   360 {
   352     return shader->parseData->output_len - sizeof(SpirvPatchTable);
   361     return shader->parseData->output_len - sizeof(SpirvPatchTable);
   353 } // shader_bytecode_len
   362 } // shader_bytecode_len
   354 
   363 
   355 static void delete_shader(
   364 static VkShaderModule compile_shader(MOJOSHADER_vkShader *shader)
   356     VkShaderModule shaderModule
   365 {
   357 ) {
   366     VkResult result;
   358     ctx->vkDestroyShaderModule(
   367     VkShaderModule module;
       
   368     VkShaderModuleCreateInfo shaderModuleCreateInfo =
       
   369     {
       
   370         VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO
       
   371     };
       
   372 
       
   373     shaderModuleCreateInfo.flags = 0;
       
   374     shaderModuleCreateInfo.codeSize = shader_bytecode_len(shader);
       
   375     shaderModuleCreateInfo.pCode = (uint32_t*) shader->parseData->output;
       
   376 
       
   377     result = ctx->vkCreateShaderModule(
   359         *ctx->logical_device,
   378         *ctx->logical_device,
   360         shaderModule,
   379         &shaderModuleCreateInfo,
   361         NULL
   380         NULL,
       
   381         &module
   362     );
   382     );
   363 } // delete_shader
   383 
       
   384     if (result != VK_SUCCESS)
       
   385     {
       
   386         // FIXME: should display VK error code
       
   387         set_error("Error when creating VkShaderModule");
       
   388         ctx->vkDestroyShaderModule(
       
   389             *ctx->logical_device,
       
   390             module,
       
   391             NULL
       
   392         );
       
   393         return VK_NULL_HANDLE;
       
   394     } // if
       
   395 
       
   396     return module;
       
   397 } // compile_shader
       
   398 
       
   399 typedef struct
       
   400 {
       
   401     MOJOSHADER_vkShader *vertex;
       
   402     MOJOSHADER_vkShader *fragment;
       
   403 } BoundShaders;
       
   404 
       
   405 static uint32_t hash_shaders(const void *sym, void *data)
       
   406 {
       
   407     (void) data;
       
   408     const BoundShaders *s = (const BoundShaders *) sym;
       
   409     const uint16_t v = (s->vertex) ? s->vertex->tag : 0;
       
   410     const uint16_t f = (s->fragment) ? s->fragment->tag : 0;
       
   411     return ((uint32_t) v << 16) | (uint32_t) f;
       
   412 } // hash_shaders
       
   413 
       
   414 static int match_shaders(const void *_a, const void *_b, void *data)
       
   415 {
       
   416     (void) data;
       
   417     const BoundShaders *a = (const BoundShaders *) _a;
       
   418     const BoundShaders *b = (const BoundShaders *) _b;
       
   419 
       
   420     const uint16_t av = (a->vertex) ? a->vertex->tag : 0;
       
   421     const uint16_t bv = (b->vertex) ? b->vertex->tag : 0;
       
   422     if (av != bv)
       
   423         return 0;
       
   424 
       
   425     const uint16_t af = (a->fragment) ? a->fragment->tag : 0;
       
   426     const uint16_t bf = (b->fragment) ? b->fragment->tag : 0;
       
   427     if (af != bf)
       
   428         return 0;
       
   429 
       
   430     return 1;
       
   431 } // match_shaders
       
   432 
       
   433 static void nuke_shaders(const void *key, const void *value, void *data)
       
   434 {
       
   435     (void) data;
       
   436     ctx->free_fn((void *) key, ctx->malloc_data); // this was a BoundShaders struct.
       
   437     MOJOSHADER_vkDeleteProgram((MOJOSHADER_vkProgram *) value);
       
   438 } // nuke_shaders
   364 
   439 
   365 // Public API
   440 // Public API
   366 
   441 
   367 MOJOSHADER_vkContext *MOJOSHADER_vkCreateContext(
   442 MOJOSHADER_vkContext *MOJOSHADER_vkCreateContext(
   368     VkInstance *instance,
   443     VkInstance *instance,
   420 void MOJOSHADER_vkMakeContextCurrent(MOJOSHADER_vkContext *_ctx)
   495 void MOJOSHADER_vkMakeContextCurrent(MOJOSHADER_vkContext *_ctx)
   421 {
   496 {
   422     ctx = _ctx;
   497     ctx = _ctx;
   423 } // MOJOSHADER_vkMakeContextCurrent
   498 } // MOJOSHADER_vkMakeContextCurrent
   424 
   499 
   425 void MOJOSHADER_vkDestroyContext()
   500 void MOJOSHADER_vkDestroyContext(MOJOSHADER_vkContext *_ctx)
   426 {
   501 {
       
   502     MOJOSHADER_vkContext *current_ctx = ctx;
       
   503     ctx = _ctx;
       
   504 
       
   505     MOJOSHADER_vkBindProgram(NULL);
       
   506     if (ctx->linker_cache)
       
   507         hash_destroy(ctx->linker_cache);
       
   508 
   427     ctx->vkDestroyBuffer(*ctx->logical_device,
   509     ctx->vkDestroyBuffer(*ctx->logical_device,
   428                          ctx->vertUboBuffer->buffer,
   510                          ctx->vertUboBuffer->buffer,
   429                          NULL);
   511                          NULL);
   430 
   512 
   431     ctx->vkDestroyBuffer(*ctx->logical_device,
   513     ctx->vkDestroyBuffer(*ctx->logical_device,
   442 
   524 
   443     ctx->free_fn(ctx->vertUboBuffer, ctx->malloc_data);
   525     ctx->free_fn(ctx->vertUboBuffer, ctx->malloc_data);
   444     ctx->free_fn(ctx->fragUboBuffer, ctx->malloc_data);
   526     ctx->free_fn(ctx->fragUboBuffer, ctx->malloc_data);
   445 
   527 
   446     ctx->free_fn(ctx, ctx->malloc_data);
   528     ctx->free_fn(ctx, ctx->malloc_data);
       
   529 
       
   530     ctx = ((current_ctx == _ctx) ? NULL : current_ctx);
   447 } // MOJOSHADER_vkDestroyContext
   531 } // MOJOSHADER_vkDestroyContext
   448 
   532 
   449 MOJOSHADER_vkShader *MOJOSHADER_vkCompileShader(
   533 MOJOSHADER_vkShader *MOJOSHADER_vkCompileShader(
   450     const char *mainfn,
   534     const char *mainfn,
   451     const unsigned char *tokenbuf,
   535     const unsigned char *tokenbuf,
   453     const MOJOSHADER_swizzle *swiz,
   537     const MOJOSHADER_swizzle *swiz,
   454     const unsigned int swizcount,
   538     const unsigned int swizcount,
   455     const MOJOSHADER_samplerMap *smap,
   539     const MOJOSHADER_samplerMap *smap,
   456     const unsigned int smapcount
   540     const unsigned int smapcount
   457 ) {
   541 ) {
   458     VkResult result;
       
   459     VkShaderModuleCreateInfo shaderModuleCreateInfo =
       
   460     {
       
   461         VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO
       
   462     };
       
   463     MOJOSHADER_vkShader *shader;
   542     MOJOSHADER_vkShader *shader;
   464 
   543 
   465     const MOJOSHADER_parseData *pd = MOJOSHADER_parse(
   544     const MOJOSHADER_parseData *pd = MOJOSHADER_parse(
   466         "spirv", mainfn,
   545         "spirv", mainfn,
   467         tokenbuf, bufsize,
   546         tokenbuf, bufsize,
   473     );
   552     );
   474 
   553 
   475     if (pd->error_count > 0)
   554     if (pd->error_count > 0)
   476     {
   555     {
   477         set_error(pd->errors[0].error);
   556         set_error(pd->errors[0].error);
   478         goto compile_shader_fail;
   557         goto parse_shader_fail;
   479     } // if
   558     } // if
   480 
   559 
   481     shader = (MOJOSHADER_vkShader *) ctx->malloc_fn(sizeof(MOJOSHADER_vkShader), ctx->malloc_data);
   560     shader = (MOJOSHADER_vkShader *) ctx->malloc_fn(sizeof(MOJOSHADER_vkShader), ctx->malloc_data);
   482     if (shader == NULL)
   561     if (shader == NULL)
   483     {
   562     {
   484         out_of_memory();
   563         out_of_memory();
   485         goto compile_shader_fail;
   564         goto parse_shader_fail;
   486     } // if
   565     } // if
   487 
   566 
   488     shader->parseData = pd;
   567     shader->parseData = pd;
   489     shader->refcount = 1;
   568     shader->refcount = 1;
   490 
   569     shader->tag = tagCounter++;
   491     shaderModuleCreateInfo.flags = 0;
       
   492     shaderModuleCreateInfo.codeSize = shader_bytecode_len(shader);
       
   493     shaderModuleCreateInfo.pCode = (uint32_t*) pd->output;
       
   494 
       
   495     result = ctx->vkCreateShaderModule(
       
   496         *ctx->logical_device,
       
   497         &shaderModuleCreateInfo,
       
   498         NULL,
       
   499         &shader->shaderModule
       
   500     );
       
   501 
       
   502     if (result != VK_SUCCESS)
       
   503     {
       
   504         // FIXME: should display VK error code
       
   505         set_error("Error when creating VkShaderModule");
       
   506         goto compile_shader_fail;
       
   507     } // if
       
   508 
       
   509     return shader;
   570     return shader;
   510 
   571 
   511 compile_shader_fail:
   572 parse_shader_fail:
   512     MOJOSHADER_freeParseData(pd);
   573     MOJOSHADER_freeParseData(pd);
   513     if (shader != NULL)
   574     if (shader != NULL)
   514     {
       
   515         delete_shader(shader->shaderModule);
       
   516         ctx->free_fn(shader, ctx->malloc_data);
   575         ctx->free_fn(shader, ctx->malloc_data);
   517     } // if
       
   518     return NULL;
   576     return NULL;
   519 
   577 } // MOJOSHADER_vkCompileShader
   520 } // MOJOSHADER_vkMakeContextCurrent
       
   521 
   578 
   522 void MOJOSHADER_vkShaderAddRef(MOJOSHADER_vkShader *shader)
   579 void MOJOSHADER_vkShaderAddRef(MOJOSHADER_vkShader *shader)
   523 {
   580 {
   524     if (shader != NULL)
   581     if (shader != NULL)
   525         shader->refcount++;
   582         shader->refcount++;
   531     {
   588     {
   532         if (shader->refcount > 1)
   589         if (shader->refcount > 1)
   533             shader->refcount--;
   590             shader->refcount--;
   534         else
   591         else
   535         {
   592         {
   536             delete_shader(shader->shaderModule);
   593             // See if this was bound as an unlinked program anywhere...
       
   594             if (ctx->linker_cache)
       
   595             {
       
   596                 const void *key = NULL;
       
   597                 void *iter = NULL;
       
   598                 int morekeys = hash_iter_keys(ctx->linker_cache, &key, &iter);
       
   599                 while (morekeys)
       
   600                 {
       
   601                     const BoundShaders *shaders = (const BoundShaders *) key;
       
   602                     // Do this here so we don't confuse the iteration by removing...
       
   603                     morekeys = hash_iter_keys(ctx->linker_cache, &key, &iter);
       
   604                     if ((shaders->vertex == shader) || (shaders->fragment == shader))
       
   605                     {
       
   606                         // Deletes the linked program
       
   607                         hash_remove(ctx->linker_cache, shaders);
       
   608                     } // if
       
   609                 } // while
       
   610             } // if
       
   611 
   537             MOJOSHADER_freeParseData(shader->parseData);
   612             MOJOSHADER_freeParseData(shader->parseData);
   538             ctx->free_fn(shader, ctx->malloc_data);
   613             ctx->free_fn(shader, ctx->malloc_data);
   539         } // else
   614         } // else
   540     } // if
   615     } // if
   541 } // MOJOSHADER_vkDeleteShader
   616 } // MOJOSHADER_vkDeleteShader
   544     MOJOSHADER_vkShader *shader
   619     MOJOSHADER_vkShader *shader
   545 ) {
   620 ) {
   546     return (shader != NULL) ? shader->parseData : NULL;
   621     return (shader != NULL) ? shader->parseData : NULL;
   547 } // MOJOSHADER_vkGetShaderParseData
   622 } // MOJOSHADER_vkGetShaderParseData
   548 
   623 
       
   624 void MOJOSHADER_vkDeleteProgram(MOJOSHADER_vkProgram *p)
       
   625 {
       
   626     if (p->vertexModule != VK_NULL_HANDLE)
       
   627         ctx->vkDestroyShaderModule(*ctx->logical_device, p->vertexModule, NULL);
       
   628     if (p->pixelModule != VK_NULL_HANDLE)
       
   629         ctx->vkDestroyShaderModule(*ctx->logical_device, p->pixelModule, NULL);
       
   630     ctx->free_fn(p, ctx->malloc_data);
       
   631 } // MOJOSHADER_vkDeleteProgram
       
   632 
       
   633 MOJOSHADER_vkProgram *MOJOSHADER_vkLinkProgram(MOJOSHADER_vkShader *vshader,
       
   634                                                MOJOSHADER_vkShader *pshader)
       
   635 {
       
   636     MOJOSHADER_vkProgram *result;
       
   637 
       
   638     if ((vshader == NULL) && (pshader == NULL))
       
   639         return NULL;
       
   640 
       
   641     result = ctx->malloc_fn(sizeof (MOJOSHADER_vkProgram), ctx->malloc_data);
       
   642     if (result == NULL)
       
   643     {
       
   644         out_of_memory();
       
   645         return NULL;
       
   646     } // if
       
   647 
       
   648     MOJOSHADER_spirv_link_attributes(vshader->parseData, pshader->parseData);
       
   649     result->vertexModule = compile_shader(vshader);
       
   650     result->pixelModule = compile_shader(pshader);
       
   651     result->vertexShader = vshader;
       
   652     result->pixelShader = pshader;
       
   653 
       
   654     if (result->vertexModule == VK_NULL_HANDLE
       
   655      || result->pixelModule == VK_NULL_HANDLE)
       
   656     {
       
   657         MOJOSHADER_vkDeleteProgram(result);
       
   658         return NULL;
       
   659     }
       
   660     return result;
       
   661 } // MOJOSHADER_vkLinkProgram
       
   662 
       
   663 void MOJOSHADER_vkBindProgram(MOJOSHADER_vkProgram *p)
       
   664 {
       
   665     ctx->bound_program = p;
       
   666 } // MOJOSHADER_vkBindProgram
       
   667 
   549 void MOJOSHADER_vkBindShaders(MOJOSHADER_vkShader *vshader,
   668 void MOJOSHADER_vkBindShaders(MOJOSHADER_vkShader *vshader,
   550                               MOJOSHADER_vkShader *pshader)
   669                               MOJOSHADER_vkShader *pshader)
   551 {
   670 {
   552     // NOOP if shader is null
   671     if (ctx->linker_cache == NULL)
   553 
   672     {
   554     if (vshader != NULL)
   673         ctx->linker_cache = hash_create(NULL, hash_shaders, match_shaders,
   555         ctx->vertexShader = vshader;
   674                                         nuke_shaders, 0, ctx->malloc_fn,
   556     if (pshader != NULL)
   675                                         ctx->free_fn, ctx->malloc_data);
   557         ctx->pixelShader = pshader;
   676 
       
   677         if (ctx->linker_cache == NULL)
       
   678         {
       
   679             out_of_memory();
       
   680             return;
       
   681         } // if
       
   682     } // if
       
   683 
       
   684     MOJOSHADER_vkProgram *program = NULL;
       
   685     BoundShaders shaders;
       
   686     shaders.vertex = vshader;
       
   687     shaders.fragment = pshader;
       
   688 
       
   689     const void *val = NULL;
       
   690     if (hash_find(ctx->linker_cache, &shaders, &val))
       
   691         program = (MOJOSHADER_vkProgram *) val;
       
   692     else
       
   693     {
       
   694         program = MOJOSHADER_vkLinkProgram(vshader, pshader);
       
   695         if (program == NULL)
       
   696             return;
       
   697 
       
   698         BoundShaders *item = (BoundShaders *) ctx->malloc_fn(sizeof (BoundShaders),
       
   699                                                              ctx->malloc_data);
       
   700         if (item == NULL)
       
   701         {
       
   702             MOJOSHADER_vkDeleteProgram(program);
       
   703             return;
       
   704         } // if
       
   705 
       
   706         memcpy(item, &shaders, sizeof (BoundShaders));
       
   707         if (hash_insert(ctx->linker_cache, item, program) != 1)
       
   708         {
       
   709             ctx->free_fn(item, ctx->malloc_data);
       
   710             MOJOSHADER_vkDeleteProgram(program);
       
   711             out_of_memory();
       
   712             return;
       
   713         } // if
       
   714     } // else
       
   715 
       
   716     assert(program != NULL);
       
   717     ctx->bound_program = program;
   558 } // MOJOSHADER_vkBindShaders
   718 } // MOJOSHADER_vkBindShaders
   559 
   719 
   560 void MOJOSHADER_vkGetBoundShaders(MOJOSHADER_vkShader **vshader,
   720 void MOJOSHADER_vkGetBoundShaders(MOJOSHADER_vkShader **vshader,
   561                                   MOJOSHADER_vkShader **pshader)
   721                                   MOJOSHADER_vkShader **pshader)
   562 {
   722 {
   563     *vshader = ctx->vertexShader;
   723     if (vshader != NULL)
   564     *pshader = ctx->pixelShader;
   724     {
       
   725         if (ctx->bound_program != NULL)
       
   726             *vshader = ctx->bound_program->vertexShader;
       
   727         else
       
   728             *vshader = NULL;
       
   729     } // if
       
   730     if (pshader != NULL)
       
   731     {
       
   732         if (ctx->bound_program != NULL)
       
   733             *pshader = ctx->bound_program->pixelShader;
       
   734         else
       
   735             *pshader = NULL;
       
   736     } // if
   565 } // MOJOSHADER_vkGetBoundShaders
   737 } // MOJOSHADER_vkGetBoundShaders
   566 
   738 
   567 void MOJOSHADER_vkMapUniformBufferMemory(float **vsf, int **vsi, unsigned char **vsb,
   739 void MOJOSHADER_vkMapUniformBufferMemory(float **vsf, int **vsi, unsigned char **vsb,
   568                                          float **psf, int **psi, unsigned char **psb)
   740                                          float **psf, int **psi, unsigned char **psb)
   569 {
   741 {
   578 void MOJOSHADER_vkUnmapUniformBufferMemory()
   750 void MOJOSHADER_vkUnmapUniformBufferMemory()
   579 {
   751 {
   580     /* Why is this function named unmap instead of update?
   752     /* Why is this function named unmap instead of update?
   581      * the world may never know...
   753      * the world may never know...
   582      */
   754      */
   583 
   755     assert(ctx->bound_program != NULL);
   584     update_uniform_buffer(ctx->vertexShader);
   756     update_uniform_buffer(ctx->bound_program->vertexShader);
   585     update_uniform_buffer(ctx->pixelShader);
   757     update_uniform_buffer(ctx->bound_program->pixelShader);
   586 } // MOJOSHADER_vkUnmapUniformBufferMemory
   758 } // MOJOSHADER_vkUnmapUniformBufferMemory
   587 
   759 
   588 void MOJOSHADER_vkGetUniformBuffers(VkBuffer *vbuf, unsigned long long *voff, unsigned long long *vsize,
   760 void MOJOSHADER_vkGetUniformBuffers(VkBuffer *vbuf, unsigned long long *voff, unsigned long long *vsize,
   589                                     VkBuffer *pbuf, unsigned long long *poff, unsigned long long *psize)
   761                                     VkBuffer *pbuf, unsigned long long *poff, unsigned long long *psize)
   590 {
   762 {
   591     *vbuf = get_uniform_buffer(ctx->vertexShader);
   763     assert(ctx->bound_program != NULL);
   592     *voff = get_uniform_offset(ctx->vertexShader);
   764     *vbuf = get_uniform_buffer(ctx->bound_program->vertexShader);
   593     *vsize = get_uniform_size(ctx->vertexShader);
   765     *voff = get_uniform_offset(ctx->bound_program->vertexShader);
   594     *pbuf = get_uniform_buffer(ctx->pixelShader);
   766     *vsize = get_uniform_size(ctx->bound_program->vertexShader);
   595     *poff = get_uniform_offset(ctx->pixelShader);
   767     *pbuf = get_uniform_buffer(ctx->bound_program->pixelShader);
   596     *psize = get_uniform_size(ctx->pixelShader);
   768     *poff = get_uniform_offset(ctx->bound_program->pixelShader);
       
   769     *psize = get_uniform_size(ctx->bound_program->pixelShader);
   597 } // MOJOSHADER_vkGetUniformBuffers
   770 } // MOJOSHADER_vkGetUniformBuffers
   598 
   771 
   599 void MOJOSHADER_vkEndFrame()
   772 void MOJOSHADER_vkEndFrame()
   600 {
   773 {
   601     ctx->vertUboBuffer->dynamicOffset = 0;
   774     ctx->vertUboBuffer->dynamicOffset = 0;
   622 
   795 
   623     // failure
   796     // failure
   624     return -1;
   797     return -1;
   625 } //MOJOSHADER_vkGetVertexAttribLocation
   798 } //MOJOSHADER_vkGetVertexAttribLocation
   626 
   799 
   627 unsigned long long MOJOSHADER_vkGetShaderModule(MOJOSHADER_vkShader *shader)
   800 void MOJOSHADER_vkGetShaderModules(VkShaderModule *vmodule,
   628 {
   801                                    VkShaderModule *pmodule)
   629     if (shader == NULL)
   802 {
   630         return 0;
   803     assert(ctx->bound_program != NULL);
   631 
   804     if (vmodule != NULL)
   632     return (unsigned long long) shader->shaderModule;
   805         *vmodule = ctx->bound_program->vertexModule;
   633 } //MOJOSHADER_vkGetShaderModule
   806     if (pmodule != NULL)
       
   807         *pmodule = ctx->bound_program->pixelModule;
       
   808 } //MOJOSHADER_vkGetShaderModules
   634 
   809 
   635 const char *MOJOSHADER_vkGetError(void)
   810 const char *MOJOSHADER_vkGetError(void)
   636 {
   811 {
   637     return error_buffer;
   812     return error_buffer;
   638 } // MOJOSHADER_vkGetError
   813 } // MOJOSHADER_vkGetError