mojoshader_vulkan.c
changeset 1271 5a67d082c55f
child 1272 cddbd25553fe
equal deleted inserted replaced
1270:fb28ba997299 1271:5a67d082c55f
       
     1 /**
       
     2  * MojoShader; generate shader programs from bytecode of compiled
       
     3  *  Direct3D shaders.
       
     4  *
       
     5  * Please see the file LICENSE.txt in the source's root directory.
       
     6  *
       
     7  *  This file written by Ryan C. Gordon.
       
     8  */
       
     9 
       
    10 #define __MOJOSHADER_INTERNAL__ 1
       
    11 #include "mojoshader_internal.h"
       
    12 
       
    13 #if SUPPORT_PROFILE_SPIRV
       
    14 
       
    15 #include "vulkan.h"
       
    16 
       
    17 #define VULKAN_INSTANCE_FUNCTION(ret, func, params) \
       
    18     typedef ret (VKAPI_CALL *vkfntype_MOJOSHADER_##func) params;
       
    19 #define VULKAN_DEVICE_FUNCTION(ret, func, params) \
       
    20 	typedef ret (VKAPI_CALL *vkfntype_MOJOSHADER_##func) params;
       
    21 #include "mojoshader_vulkan_vkfuncs.h"
       
    22 
       
    23 #define UBO_BUFFER_COUNT 8
       
    24 #define UBO_BUFFER_SIZE 1048576 /* ~1MB */
       
    25 
       
    26 // Internal struct defs...
       
    27 
       
    28 typedef struct MOJOSHADER_vkShader
       
    29 {
       
    30     VkShaderModule shaderModule;
       
    31     const MOJOSHADER_parseData *parseData;
       
    32     uint32_t refcount;
       
    33 } MOJOSHADER_vkShader;
       
    34 
       
    35 typedef struct MOJOSHADER_vkUniformBuffer
       
    36 {
       
    37     VkBuffer buffer;
       
    38     VkDeviceSize bufferSize;
       
    39     VkDeviceSize memoryOffset;
       
    40     VkDeviceSize dynamicOffset;
       
    41     VkDeviceSize currentBlockSize;
       
    42     int32_t full; // Records frame on which it became full, -1 if not full
       
    43 } MOJOSHADER_vkUniformBuffer;
       
    44 
       
    45 // Error state...
       
    46 static char error_buffer[1024] = { '\0' };
       
    47 
       
    48 static void set_error(const char *str)
       
    49 {
       
    50     snprintf(error_buffer, sizeof (error_buffer), "%s", str);
       
    51 } // set_error
       
    52 
       
    53 static inline void out_of_memory(void)
       
    54 {
       
    55     set_error("out of memory");
       
    56 } // out_of_memory
       
    57 
       
    58 /* Max entries for each register file type */
       
    59 #define MAX_REG_FILE_F 8192
       
    60 #define MAX_REG_FILE_I 2047
       
    61 #define MAX_REG_FILE_B 2047
       
    62 
       
    63 typedef struct MOJOSHADER_vkContext
       
    64 {
       
    65     VkInstance *instance;
       
    66     VkPhysicalDevice *physical_device;
       
    67     VkDevice *logical_device;
       
    68     PFN_vkGetInstanceProcAddr instance_proc_lookup;
       
    69     PFN_vkGetDeviceProcAddr device_proc_lookup;
       
    70     uint32_t graphics_queue_family_index;
       
    71     uint32_t maxUniformBufferRange;
       
    72     uint32_t minUniformBufferOffsetAlignment;
       
    73 
       
    74     int32_t frames_in_flight;
       
    75 
       
    76     MOJOSHADER_malloc malloc_fn;
       
    77     MOJOSHADER_free free_fn;
       
    78     void *malloc_data;
       
    79 
       
    80     // The constant register files...
       
    81     // !!! FIXME: Man, it kills me how much memory this takes...
       
    82     // !!! FIXME:  ... make this dynamically allocated on demand.
       
    83     float vs_reg_file_f[MAX_REG_FILE_F * 4];
       
    84     int32_t vs_reg_file_i[MAX_REG_FILE_I * 4];
       
    85     uint8_t vs_reg_file_b[MAX_REG_FILE_B * 4];
       
    86     float ps_reg_file_f[MAX_REG_FILE_F * 4];
       
    87     int32_t ps_reg_file_i[MAX_REG_FILE_I * 4];
       
    88     uint8_t ps_reg_file_b[MAX_REG_FILE_B * 4];
       
    89 
       
    90     VkDeviceMemory vertUboMemory;
       
    91     MOJOSHADER_vkUniformBuffer **vertUboBuffers;
       
    92     uint32_t vertUboCurrentIndex;
       
    93 
       
    94     VkDeviceMemory fragUboMemory;
       
    95     MOJOSHADER_vkUniformBuffer **fragUboBuffers;
       
    96     uint32_t fragUboCurrentIndex;
       
    97 
       
    98     uint32_t uboBufferCount;
       
    99 
       
   100     MOJOSHADER_vkShader *vertexShader;
       
   101     MOJOSHADER_vkShader *pixelShader;
       
   102 
       
   103     uint32_t currentFrame;
       
   104 
       
   105     #define VULKAN_INSTANCE_FUNCTION(ret, func, params) \
       
   106         vkfntype_MOJOSHADER_##func func;
       
   107     #define VULKAN_DEVICE_FUNCTION(ret, func, params) \
       
   108         vkfntype_MOJOSHADER_##func func;
       
   109     #include "mojoshader_vulkan_vkfuncs.h"
       
   110 } MOJOSHADER_vkContext;
       
   111 
       
   112 static MOJOSHADER_vkContext *ctx = NULL;
       
   113 
       
   114 static uint8_t find_memory_type(
       
   115     MOJOSHADER_vkContext *ctx,
       
   116 	uint32_t typeFilter,
       
   117 	VkMemoryPropertyFlags properties,
       
   118 	uint32_t *result
       
   119 ) {
       
   120 	uint32_t i;
       
   121 	VkPhysicalDeviceMemoryProperties memoryProperties;
       
   122 	ctx->vkGetPhysicalDeviceMemoryProperties(*ctx->physical_device, &memoryProperties);
       
   123 
       
   124 	for (i = 0; i < memoryProperties.memoryTypeCount; i++)
       
   125 	{
       
   126 		if ((typeFilter & (1 << i))
       
   127 		 && (memoryProperties.memoryTypes[i].propertyFlags & properties) == properties)
       
   128 		{
       
   129 			*result = i;
       
   130 			return 1;
       
   131 		} // if
       
   132 	} // for
       
   133 
       
   134 	return 0;
       
   135 } // find_memory_type
       
   136 
       
   137 static uint32_t next_highest_offset_alignment(uint32_t offset)
       
   138 {
       
   139     return (
       
   140         (offset + ctx->minUniformBufferOffsetAlignment - 1) /
       
   141         ctx->minUniformBufferOffsetAlignment *
       
   142         ctx->minUniformBufferOffsetAlignment
       
   143     );
       
   144 } // next_highest_offset_alignment
       
   145 
       
   146 static MOJOSHADER_vkUniformBuffer *create_ubo(MOJOSHADER_vkContext *ctx,
       
   147                                               MOJOSHADER_malloc m,
       
   148                                               void *d
       
   149 ) {
       
   150     MOJOSHADER_vkUniformBuffer *result = (MOJOSHADER_vkUniformBuffer *) m(
       
   151         sizeof(MOJOSHADER_vkUniformBuffer),
       
   152         d
       
   153     );
       
   154     VkBufferCreateInfo bufferCreateInfo =
       
   155     {
       
   156         VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO
       
   157     };
       
   158 
       
   159     bufferCreateInfo.flags = 0;
       
   160     bufferCreateInfo.size = UBO_BUFFER_SIZE;
       
   161     bufferCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
       
   162     bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
       
   163     bufferCreateInfo.queueFamilyIndexCount = 1;
       
   164     bufferCreateInfo.pQueueFamilyIndices = &ctx->graphics_queue_family_index;
       
   165 
       
   166     ctx->vkCreateBuffer(
       
   167         *ctx->logical_device,
       
   168         &bufferCreateInfo,
       
   169         NULL,
       
   170         &result->buffer
       
   171     );
       
   172 
       
   173     result->bufferSize = UBO_BUFFER_SIZE;
       
   174     result->currentBlockSize = 0;
       
   175     result->dynamicOffset = 0;
       
   176     result->full = -1;
       
   177 
       
   178     return result;
       
   179 } // create_ubo
       
   180 
       
   181 static uint32_t uniform_data_size(MOJOSHADER_vkShader *shader)
       
   182 {
       
   183     int32_t i;
       
   184     int32_t uniformSize;
       
   185     int32_t buflen = 0;
       
   186     for (i = 0; i < shader->parseData->uniform_count; i++)
       
   187     {
       
   188         const int32_t arrayCount = shader->parseData->uniforms[i].array_count;
       
   189         uniformSize = 16;
       
   190         if (shader->parseData->uniforms[i].type == MOJOSHADER_UNIFORM_BOOL)
       
   191             uniformSize = 1;
       
   192         buflen += (arrayCount ? arrayCount : 1) * uniformSize;
       
   193     } // for
       
   194 
       
   195     return buflen;
       
   196 } // uniform_data_size
       
   197 
       
   198 static VkBuffer get_uniform_buffer(MOJOSHADER_vkShader *shader)
       
   199 {
       
   200     if (shader == NULL || shader->parseData->uniform_count == 0)
       
   201         return VK_NULL_HANDLE;
       
   202 
       
   203     if (shader->parseData->shader_type == MOJOSHADER_TYPE_VERTEX)
       
   204         return ctx->vertUboBuffers[ctx->vertUboCurrentIndex]->buffer;
       
   205     else
       
   206         return ctx->fragUboBuffers[ctx->fragUboCurrentIndex]->buffer;
       
   207 } // get_uniform_buffer
       
   208 
       
   209 static VkDeviceSize get_uniform_offset(MOJOSHADER_vkShader *shader)
       
   210 {
       
   211     if (shader == NULL || shader->parseData->uniform_count == 0)
       
   212         return 0;
       
   213 
       
   214     if (shader->parseData->shader_type == MOJOSHADER_TYPE_VERTEX)
       
   215         return ctx->vertUboBuffers[ctx->vertUboCurrentIndex]->dynamicOffset;
       
   216     else
       
   217         return ctx->fragUboBuffers[ctx->fragUboCurrentIndex]->dynamicOffset;
       
   218 } // get_uniform_offset
       
   219 
       
   220 static VkDeviceSize get_uniform_size(MOJOSHADER_vkShader *shader)
       
   221 {
       
   222     if (shader == NULL || shader->parseData->uniform_count == 0)
       
   223         return 0;
       
   224 
       
   225     if (shader->parseData->shader_type == MOJOSHADER_TYPE_VERTEX)
       
   226         return ctx->vertUboBuffers[ctx->vertUboCurrentIndex]->currentBlockSize;
       
   227     else
       
   228         return ctx->fragUboBuffers[ctx->fragUboCurrentIndex]->currentBlockSize;
       
   229 } // get_uniform_size
       
   230 
       
   231 static void update_uniform_buffer(MOJOSHADER_vkShader *shader)
       
   232 {
       
   233     int32_t i;
       
   234     void *map;
       
   235     int32_t offset;
       
   236     uint8_t *contents;
       
   237     float *regF; int *regI; uint8_t *regB;
       
   238     MOJOSHADER_vkUniformBuffer *ubo;
       
   239     VkDeviceMemory uboMemory;
       
   240 
       
   241     if (shader == NULL || shader->parseData->uniform_count == 0)
       
   242         return;
       
   243 
       
   244     if (shader->parseData->shader_type == MOJOSHADER_TYPE_VERTEX)
       
   245     {
       
   246         regF = ctx->vs_reg_file_f;
       
   247         regI = ctx->vs_reg_file_i;
       
   248         regB = ctx->vs_reg_file_b;
       
   249 
       
   250         ubo = ctx->vertUboBuffers[ctx->vertUboCurrentIndex];
       
   251         uboMemory = ctx->vertUboMemory;
       
   252     } // if
       
   253     else
       
   254     {
       
   255         regF = ctx->ps_reg_file_f;
       
   256         regI = ctx->ps_reg_file_i;
       
   257         regB = ctx->ps_reg_file_b;
       
   258 
       
   259         ubo = ctx->fragUboBuffers[ctx->fragUboCurrentIndex];
       
   260         uboMemory = ctx->fragUboMemory;
       
   261     } // else
       
   262 
       
   263     ubo->dynamicOffset += ubo->currentBlockSize;
       
   264 
       
   265     ubo->currentBlockSize = next_highest_offset_alignment(uniform_data_size(shader));
       
   266 
       
   267     // Rotate buffer if it would overrun
       
   268     if (ubo->dynamicOffset + ubo->currentBlockSize >= ubo->bufferSize)
       
   269     {
       
   270         ubo->full = ctx->currentFrame;
       
   271 
       
   272         if (shader->parseData->shader_type == MOJOSHADER_TYPE_VERTEX)
       
   273         {
       
   274             for (i = 0; i < ctx->uboBufferCount; i++)
       
   275             {
       
   276                 ctx->vertUboCurrentIndex = (ctx->vertUboCurrentIndex + 1) % ctx->uboBufferCount;
       
   277                 if (ctx->vertUboBuffers[ctx->vertUboCurrentIndex]->full == -1)
       
   278                     break;
       
   279             } // for
       
   280 
       
   281             ubo = ctx->vertUboBuffers[ctx->vertUboCurrentIndex];
       
   282         }
       
   283         else
       
   284         {
       
   285             for (int i = 0; i < ctx->uboBufferCount; i++)
       
   286             {
       
   287                 ctx->fragUboCurrentIndex = (ctx->fragUboCurrentIndex + 1) % ctx->uboBufferCount;
       
   288                 if (ctx->fragUboBuffers[ctx->fragUboCurrentIndex]->full == -1)
       
   289                     break;
       
   290             } // for
       
   291 
       
   292             ubo = ctx->fragUboBuffers[ctx->fragUboCurrentIndex];
       
   293         } // else
       
   294 
       
   295         ubo->dynamicOffset = 0;
       
   296         ubo->currentBlockSize = next_highest_offset_alignment(uniform_data_size(shader));
       
   297 
       
   298         if (ubo->full >= 0)
       
   299             set_error("all UBO buffers are full");
       
   300     } // if
       
   301 
       
   302     ctx->vkMapMemory(
       
   303         *ctx->logical_device,
       
   304         uboMemory,
       
   305         ubo->memoryOffset,
       
   306         ubo->bufferSize,
       
   307         0,
       
   308         &map
       
   309     );
       
   310 
       
   311     contents = ((uint8_t *) map) + ubo->dynamicOffset;
       
   312 
       
   313     offset = 0;
       
   314     for (i = 0; i < shader->parseData->uniform_count; i++)
       
   315     {
       
   316         const int32_t index = shader->parseData->uniforms[i].index;
       
   317         const int32_t arrayCount = shader->parseData->uniforms[i].array_count;
       
   318         const int32_t size = arrayCount ? arrayCount : 1;
       
   319 
       
   320         switch (shader->parseData->uniforms[i].type)
       
   321         {
       
   322             case MOJOSHADER_UNIFORM_FLOAT:
       
   323                 memcpy(
       
   324                     contents + (offset * 16),
       
   325                     &regF[4 * index],
       
   326                     size * 16
       
   327                 );
       
   328                 break;
       
   329 
       
   330             case MOJOSHADER_UNIFORM_INT:
       
   331                 memcpy(
       
   332                     contents + (offset * 16),
       
   333                     &regI[4 * index],
       
   334                     size * 16
       
   335                 );
       
   336                 break;
       
   337 
       
   338             case MOJOSHADER_UNIFORM_BOOL:
       
   339                 memcpy(
       
   340                     contents + offset,
       
   341                     &regB[index],
       
   342                     size
       
   343                 );
       
   344                 break;
       
   345 
       
   346             default:
       
   347                 set_error(
       
   348                     "SOMETHING VERY WRONG HAPPENED WHEN UPDATING UNIFORMS"
       
   349                 );
       
   350                 assert(0);
       
   351                 break;
       
   352         } // switch
       
   353 
       
   354         offset += size;
       
   355     } // for
       
   356 
       
   357     ctx->vkUnmapMemory(
       
   358         *ctx->logical_device,
       
   359         uboMemory
       
   360     );
       
   361 } // update_uniform_buffer
       
   362 
       
   363 static void lookup_entry_points(MOJOSHADER_vkContext *ctx)
       
   364 {
       
   365     #define VULKAN_INSTANCE_FUNCTION(ret, func, params) \
       
   366         ctx->func = (vkfntype_MOJOSHADER_##func) ctx->instance_proc_lookup(*ctx->instance, #func);
       
   367     #define VULKAN_DEVICE_FUNCTION(ret, func, params) \
       
   368         ctx->func = (vkfntype_MOJOSHADER_##func) ctx->device_proc_lookup(*ctx->logical_device, #func);
       
   369     #include "mojoshader_vulkan_vkfuncs.h"
       
   370 } // lookup_entry_points
       
   371 
       
   372 static int shader_bytecode_len(MOJOSHADER_vkShader *shader)
       
   373 {
       
   374     return shader->parseData->output_len - sizeof(SpirvPatchTable);
       
   375 } // shader_bytecode_len
       
   376 
       
   377 static void delete_shader(
       
   378     VkShaderModule shaderModule
       
   379 ) {
       
   380     ctx->vkDestroyShaderModule(
       
   381         *ctx->logical_device,
       
   382         shaderModule,
       
   383         NULL
       
   384     );
       
   385 } // delete_shader
       
   386 
       
   387 // Public API
       
   388 
       
   389 MOJOSHADER_vkContext *MOJOSHADER_vkCreateContext(
       
   390     VkInstance *instance,
       
   391     VkPhysicalDevice *physical_device,
       
   392     VkDevice *logical_device,
       
   393     int frames_in_flight,
       
   394     PFN_MOJOSHADER_vkGetInstanceProcAddr instance_lookup,
       
   395     PFN_MOJOSHADER_vkGetDeviceProcAddr device_lookup,
       
   396     unsigned int graphics_queue_family_index,
       
   397     unsigned int max_uniform_buffer_range,
       
   398     unsigned int min_uniform_buffer_offset_alignment,
       
   399     MOJOSHADER_malloc m, MOJOSHADER_free f,
       
   400     void *malloc_d
       
   401 ) {
       
   402     int32_t i;
       
   403     int32_t uboMemoryOffset;
       
   404     VkMemoryAllocateInfo allocate_info =
       
   405     {
       
   406         VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO
       
   407     };
       
   408     VkMemoryRequirements memoryRequirements;
       
   409     MOJOSHADER_vkContext* resultCtx;
       
   410 
       
   411     if (m == NULL) m = MOJOSHADER_internal_malloc;
       
   412     if (f == NULL) f = MOJOSHADER_internal_free;
       
   413 
       
   414     resultCtx = (MOJOSHADER_vkContext *) m(sizeof(MOJOSHADER_vkContext), malloc_d);
       
   415     if (resultCtx == NULL)
       
   416     {
       
   417         out_of_memory();
       
   418         goto init_fail;
       
   419     }
       
   420 
       
   421     memset(resultCtx, '\0', sizeof(MOJOSHADER_vkContext));
       
   422     resultCtx->malloc_fn = m;
       
   423     resultCtx->free_fn = f;
       
   424     resultCtx->malloc_data = malloc_d;
       
   425 
       
   426     resultCtx->instance = (VkInstance*) instance;
       
   427     resultCtx->physical_device = (VkPhysicalDevice*) physical_device;
       
   428     resultCtx->logical_device = (VkDevice*) logical_device;
       
   429     resultCtx->instance_proc_lookup = (PFN_vkGetInstanceProcAddr) instance_lookup;
       
   430     resultCtx->device_proc_lookup = (PFN_vkGetDeviceProcAddr) device_lookup;
       
   431     resultCtx->frames_in_flight = frames_in_flight;
       
   432     resultCtx->graphics_queue_family_index = graphics_queue_family_index;
       
   433     resultCtx->maxUniformBufferRange = max_uniform_buffer_range;
       
   434     resultCtx->minUniformBufferOffsetAlignment = min_uniform_buffer_offset_alignment;
       
   435     resultCtx->currentFrame = 0;
       
   436 
       
   437     lookup_entry_points(resultCtx);
       
   438 
       
   439     resultCtx->uboBufferCount = UBO_BUFFER_COUNT;
       
   440 
       
   441     // Allocate vert UBO
       
   442 
       
   443     resultCtx->vertUboCurrentIndex = 0;
       
   444     resultCtx->vertUboBuffers = (MOJOSHADER_vkUniformBuffer**) m(
       
   445         sizeof(MOJOSHADER_vkUniformBuffer*) * resultCtx->uboBufferCount,
       
   446         malloc_d
       
   447     );
       
   448 
       
   449     for (i = 0; i < resultCtx->uboBufferCount; i++)
       
   450         resultCtx->vertUboBuffers[i] = create_ubo(resultCtx, m, malloc_d);
       
   451 
       
   452     resultCtx->vkGetBufferMemoryRequirements(
       
   453         *resultCtx->logical_device,
       
   454         resultCtx->vertUboBuffers[0]->buffer,
       
   455         &memoryRequirements
       
   456     );
       
   457 
       
   458     allocate_info.allocationSize = UBO_BUFFER_SIZE * resultCtx->uboBufferCount;
       
   459 
       
   460     if (!find_memory_type(resultCtx,
       
   461                           memoryRequirements.memoryTypeBits,
       
   462                           VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
       
   463                           &allocate_info.memoryTypeIndex))
       
   464     {
       
   465         set_error("failed to find suitable memory type for UBO memory");
       
   466         return NULL;
       
   467     } // if
       
   468 
       
   469     resultCtx->vkAllocateMemory(
       
   470         *resultCtx->logical_device,
       
   471         &allocate_info,
       
   472         NULL,
       
   473         &resultCtx->vertUboMemory
       
   474     );
       
   475 
       
   476     uboMemoryOffset = 0;
       
   477     for (i = 0; i < resultCtx->uboBufferCount; i++)
       
   478     {
       
   479         resultCtx->vertUboBuffers[i]->memoryOffset = uboMemoryOffset;
       
   480 
       
   481         resultCtx->vkBindBufferMemory(
       
   482             *resultCtx->logical_device,
       
   483             resultCtx->vertUboBuffers[i]->buffer,
       
   484             resultCtx->vertUboMemory,
       
   485             uboMemoryOffset
       
   486         );
       
   487 
       
   488         uboMemoryOffset += UBO_BUFFER_SIZE;
       
   489     } // for
       
   490 
       
   491     // Allocate frag UBO
       
   492 
       
   493     resultCtx->fragUboCurrentIndex = 0;
       
   494     resultCtx->fragUboBuffers = (MOJOSHADER_vkUniformBuffer**) m(
       
   495         sizeof(MOJOSHADER_vkUniformBuffer*) * resultCtx->uboBufferCount,
       
   496         malloc_d
       
   497     );
       
   498 
       
   499     for (i = 0; i < resultCtx->uboBufferCount; i++)
       
   500         resultCtx->fragUboBuffers[i] = create_ubo(resultCtx, m, malloc_d);
       
   501 
       
   502     resultCtx->vkGetBufferMemoryRequirements(
       
   503         *resultCtx->logical_device,
       
   504         resultCtx->fragUboBuffers[0]->buffer,
       
   505         &memoryRequirements
       
   506     );
       
   507 
       
   508     allocate_info.allocationSize = UBO_BUFFER_SIZE * resultCtx->uboBufferCount;
       
   509 
       
   510     if (!find_memory_type(resultCtx,
       
   511                           memoryRequirements.memoryTypeBits,
       
   512                           VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
       
   513                           &allocate_info.memoryTypeIndex))
       
   514     {
       
   515         set_error("failed to find suitable memory type for UBO memory");
       
   516         return NULL;
       
   517     } // if
       
   518 
       
   519     resultCtx->vkAllocateMemory(
       
   520         *resultCtx->logical_device,
       
   521         &allocate_info,
       
   522         NULL,
       
   523         &resultCtx->fragUboMemory
       
   524     );
       
   525 
       
   526     uboMemoryOffset = 0;
       
   527     for (i = 0; i < resultCtx->uboBufferCount; i++)
       
   528     {
       
   529         resultCtx->fragUboBuffers[i]->memoryOffset = uboMemoryOffset;
       
   530 
       
   531         resultCtx->vkBindBufferMemory(
       
   532             *resultCtx->logical_device,
       
   533             resultCtx->fragUboBuffers[i]->buffer,
       
   534             resultCtx->fragUboMemory,
       
   535             uboMemoryOffset
       
   536         );
       
   537 
       
   538         uboMemoryOffset += UBO_BUFFER_SIZE;
       
   539     } // for
       
   540 
       
   541     return resultCtx;
       
   542 
       
   543 init_fail:
       
   544     if (resultCtx != NULL)
       
   545         f(resultCtx, malloc_d);
       
   546     return NULL;
       
   547 } // MOJOSHADER_vkCreateContext
       
   548 
       
   549 void MOJOSHADER_vkMakeContextCurrent(MOJOSHADER_vkContext *_ctx)
       
   550 {
       
   551     ctx = _ctx;
       
   552 } // MOJOSHADER_vkMakeContextCurrent
       
   553 
       
   554 void MOJOSHADER_vkDestroyContext()
       
   555 {
       
   556     int32_t i;
       
   557     for (i = 0; i < ctx->uboBufferCount; i++)
       
   558     {
       
   559         ctx->vkDestroyBuffer(
       
   560             *ctx->logical_device,
       
   561             ctx->vertUboBuffers[i]->buffer,
       
   562             NULL
       
   563         );
       
   564 
       
   565         ctx->free_fn(ctx->vertUboBuffers[i], ctx->malloc_data);
       
   566 
       
   567         ctx->vkDestroyBuffer(
       
   568             *ctx->logical_device,
       
   569             ctx->fragUboBuffers[i]->buffer,
       
   570             NULL
       
   571         );
       
   572 
       
   573         ctx->free_fn(ctx->fragUboBuffers[i], ctx->malloc_data);
       
   574     } // for
       
   575 
       
   576     ctx->free_fn(ctx->vertUboBuffers, ctx->malloc_data);
       
   577     ctx->free_fn(ctx->fragUboBuffers, ctx->malloc_data);
       
   578 
       
   579     ctx->vkFreeMemory(
       
   580         *ctx->logical_device,
       
   581         ctx->vertUboMemory,
       
   582         NULL
       
   583     );
       
   584 
       
   585     ctx->vkFreeMemory(
       
   586         *ctx->logical_device,
       
   587         ctx->fragUboMemory,
       
   588         NULL
       
   589     );
       
   590 
       
   591     ctx->free_fn(ctx, ctx->malloc_data);
       
   592 } // MOJOSHADER_vkDestroyContext
       
   593 
       
   594 MOJOSHADER_vkShader *MOJOSHADER_vkCompileShader(
       
   595     const char *mainfn,
       
   596     const unsigned char *tokenbuf,
       
   597     const unsigned int bufsize,
       
   598     const MOJOSHADER_swizzle *swiz,
       
   599     const unsigned int swizcount,
       
   600     const MOJOSHADER_samplerMap *smap,
       
   601     const unsigned int smapcount
       
   602 ) {
       
   603     VkResult result;
       
   604     VkShaderModule shaderModule;
       
   605     VkShaderModuleCreateInfo shaderModuleCreateInfo =
       
   606     {
       
   607         VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO
       
   608     };
       
   609     MOJOSHADER_vkShader *shader;
       
   610 
       
   611     const MOJOSHADER_parseData *pd = MOJOSHADER_parse(
       
   612         "spirv", mainfn,
       
   613         tokenbuf, bufsize,
       
   614         swiz, swizcount,
       
   615         smap, smapcount,
       
   616         ctx->malloc_fn,
       
   617         ctx->free_fn,
       
   618         ctx->malloc_data
       
   619     );
       
   620 
       
   621     if (pd->error_count > 0)
       
   622     {
       
   623         set_error(pd->errors[0].error);
       
   624         goto compile_shader_fail;
       
   625     } // if
       
   626 
       
   627     shader = (MOJOSHADER_vkShader *) ctx->malloc_fn(sizeof(MOJOSHADER_vkShader), ctx->malloc_data);
       
   628     if (shader == NULL)
       
   629     {
       
   630         out_of_memory();
       
   631         goto compile_shader_fail;
       
   632     } // if
       
   633 
       
   634     shader->parseData = pd;
       
   635     shader->refcount = 1;
       
   636 
       
   637     shaderModuleCreateInfo.flags = 0;
       
   638     shaderModuleCreateInfo.codeSize = shader_bytecode_len(shader);
       
   639     shaderModuleCreateInfo.pCode = (uint32_t*) pd->output;
       
   640 
       
   641     result = ctx->vkCreateShaderModule(
       
   642         *ctx->logical_device,
       
   643         &shaderModuleCreateInfo,
       
   644         NULL,
       
   645         &shader->shaderModule
       
   646     );
       
   647 
       
   648     if (result != VK_SUCCESS)
       
   649     {
       
   650         // FIXME: should display VK error code
       
   651         set_error("Error when creating VkShaderModule");
       
   652         goto compile_shader_fail;
       
   653     } // if
       
   654 
       
   655     return shader;
       
   656 
       
   657 compile_shader_fail:
       
   658     MOJOSHADER_freeParseData(pd);
       
   659     if (shader != NULL)
       
   660     {
       
   661         delete_shader(shader->shaderModule);
       
   662         ctx->free_fn(shader, ctx->malloc_data);
       
   663     } // if
       
   664     return NULL;
       
   665 
       
   666 } // MOJOSHADER_vkMakeContextCurrent
       
   667 
       
   668 void MOJOSHADER_vkShaderAddRef(MOJOSHADER_vkShader *shader)
       
   669 {
       
   670     if (shader != NULL)
       
   671         shader->refcount++;
       
   672 } // MOJOShader_vkShaderAddRef
       
   673 
       
   674 void MOJOSHADER_vkDeleteShader(MOJOSHADER_vkShader *shader)
       
   675 {
       
   676     if (shader != NULL)
       
   677     {
       
   678         if (shader->refcount > 1)
       
   679             shader->refcount--;
       
   680         else
       
   681         {
       
   682             delete_shader(shader->shaderModule);
       
   683             MOJOSHADER_freeParseData(shader->parseData);
       
   684             ctx->free_fn(shader, ctx->malloc_data);
       
   685         } // else
       
   686     } // if
       
   687 } // MOJOSHADER_vkDeleteShader
       
   688 
       
   689 const MOJOSHADER_parseData *MOJOSHADER_vkGetShaderParseData(
       
   690     MOJOSHADER_vkShader *shader
       
   691 ) {
       
   692     return (shader != NULL) ? shader->parseData : NULL;
       
   693 } // MOJOSHADER_vkGetShaderParseData
       
   694 
       
   695 void MOJOSHADER_vkBindShaders(MOJOSHADER_vkShader *vshader,
       
   696                               MOJOSHADER_vkShader *pshader)
       
   697 {
       
   698     /* NOOP if shader is null */
       
   699 
       
   700     if (vshader != NULL)
       
   701         ctx->vertexShader = vshader;
       
   702     if (pshader != NULL)
       
   703         ctx->pixelShader = pshader;
       
   704 } // MOJOSHADER_vkBindShaders
       
   705 
       
   706 void MOJOSHADER_vkGetBoundShaders(MOJOSHADER_vkShader **vshader,
       
   707                                   MOJOSHADER_vkShader **pshader)
       
   708 {
       
   709     *vshader = ctx->vertexShader;
       
   710     *pshader = ctx->pixelShader;
       
   711 } // MOJOSHADER_vkGetBoundShaders
       
   712 
       
   713 void MOJOSHADER_vkMapUniformBufferMemory(float **vsf, int **vsi, unsigned char **vsb,
       
   714                                          float **psf, int **psi, unsigned char **psb)
       
   715 {
       
   716     *vsf = ctx->vs_reg_file_f;
       
   717     *vsi = ctx->vs_reg_file_i;
       
   718     *vsb = ctx->vs_reg_file_b;
       
   719     *psf = ctx->ps_reg_file_f;
       
   720     *psi = ctx->ps_reg_file_i;
       
   721     *psb = ctx->ps_reg_file_b;
       
   722 } // MOJOSHADER_vkMapUniformBufferMemory
       
   723 
       
   724 void MOJOSHADER_vkUnmapUniformBufferMemory()
       
   725 {
       
   726     /* Why is this function named unmap instead of update?
       
   727      * the world may never know...
       
   728      */
       
   729 
       
   730     update_uniform_buffer(ctx->vertexShader);
       
   731     update_uniform_buffer(ctx->pixelShader);
       
   732 } // MOJOSHADER_vkUnmapUniformBufferMemory
       
   733 
       
   734 void MOJOSHADER_vkGetUniformBuffers(VkBuffer *vbuf, unsigned long long *voff, unsigned long long *vsize,
       
   735                                     VkBuffer *pbuf, unsigned long long *poff, unsigned long long *psize)
       
   736 {
       
   737     *vbuf = get_uniform_buffer(ctx->vertexShader);
       
   738     *voff = get_uniform_offset(ctx->vertexShader);
       
   739     *vsize = get_uniform_size(ctx->vertexShader);
       
   740     *pbuf = get_uniform_buffer(ctx->pixelShader);
       
   741     *poff = get_uniform_offset(ctx->pixelShader);
       
   742     *psize = get_uniform_size(ctx->pixelShader);
       
   743 } // MOJOSHADER_vkGetUniformBuffers
       
   744 
       
   745 void MOJOSHADER_vkEndFrame()
       
   746 {
       
   747     int32_t i;
       
   748     ctx->currentFrame = (ctx->currentFrame + 1) % ctx->frames_in_flight;
       
   749     for (i = 0; i < ctx->uboBufferCount; i++)
       
   750     {
       
   751         if (ctx->vertUboBuffers[i]->full == ctx->currentFrame)
       
   752         {
       
   753             ctx->vertUboBuffers[i]->dynamicOffset = 0;
       
   754             ctx->vertUboBuffers[i]->currentBlockSize = 0;
       
   755             ctx->vertUboBuffers[i]->full = -1;
       
   756         } // if
       
   757 
       
   758         if (ctx->fragUboBuffers[i]->full == ctx->currentFrame)
       
   759         {
       
   760             ctx->fragUboBuffers[i]->dynamicOffset = 0;
       
   761             ctx->fragUboBuffers[i]->currentBlockSize = 0;
       
   762             ctx->fragUboBuffers[i]->full = -1;
       
   763         } // if
       
   764     } // for
       
   765 } // MOJOSHADER_VkEndFrame
       
   766 
       
   767 int MOJOSHADER_vkGetVertexAttribLocation(MOJOSHADER_vkShader *vert,
       
   768                                          MOJOSHADER_usage usage, int index)
       
   769 {
       
   770     int32_t i;
       
   771     if (vert == NULL)
       
   772         return -1;
       
   773 
       
   774     for (i = 0; i < vert->parseData->attribute_count; i++)
       
   775     {
       
   776         if (vert->parseData->attributes[i].usage == usage &&
       
   777             vert->parseData->attributes[i].index == index)
       
   778         {
       
   779             return i;
       
   780         } // if
       
   781     } // for
       
   782 
       
   783     // failure
       
   784     return -1;
       
   785 } //MOJOSHADER_vkGetVertexAttribLocation
       
   786 
       
   787 unsigned long long MOJOSHADER_vkGetShaderModule(MOJOSHADER_vkShader *shader)
       
   788 {
       
   789     if (shader == NULL)
       
   790         return 0;
       
   791 
       
   792     return (unsigned long long) shader->shaderModule;
       
   793 } //MOJOSHADER_vkGetShaderModule
       
   794 
       
   795 const char *MOJOSHADER_vkGetError(void)
       
   796 {
       
   797     return error_buffer;
       
   798 } // MOJOSHADER_vkGetError
       
   799 
       
   800 #endif /* SUPPORT_PROFILE_SPIRV */
       
   801 
       
   802 // end of mojoshader_vulkan.c ...