--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mojoshader_vulkan.c Wed Jul 01 04:29:09 2020 -0400
@@ -0,0 +1,802 @@
+/**
+ * MojoShader; generate shader programs from bytecode of compiled
+ * Direct3D shaders.
+ *
+ * Please see the file LICENSE.txt in the source's root directory.
+ *
+ * This file written by Ryan C. Gordon.
+ */
+
+#define __MOJOSHADER_INTERNAL__ 1
+#include "mojoshader_internal.h"
+
+#if SUPPORT_PROFILE_SPIRV
+
+#include "vulkan.h"
+
+#define VULKAN_INSTANCE_FUNCTION(ret, func, params) \
+ typedef ret (VKAPI_CALL *vkfntype_MOJOSHADER_##func) params;
+#define VULKAN_DEVICE_FUNCTION(ret, func, params) \
+ typedef ret (VKAPI_CALL *vkfntype_MOJOSHADER_##func) params;
+#include "mojoshader_vulkan_vkfuncs.h"
+
+#define UBO_BUFFER_COUNT 8
+#define UBO_BUFFER_SIZE 1048576 /* ~1MB */
+
+// Internal struct defs...
+
+typedef struct MOJOSHADER_vkShader
+{
+ VkShaderModule shaderModule;
+ const MOJOSHADER_parseData *parseData;
+ uint32_t refcount;
+} MOJOSHADER_vkShader;
+
+typedef struct MOJOSHADER_vkUniformBuffer
+{
+ VkBuffer buffer;
+ VkDeviceSize bufferSize;
+ VkDeviceSize memoryOffset;
+ VkDeviceSize dynamicOffset;
+ VkDeviceSize currentBlockSize;
+ int32_t full; // Records frame on which it became full, -1 if not full
+} MOJOSHADER_vkUniformBuffer;
+
+// Error state...
+static char error_buffer[1024] = { '\0' };
+
+static void set_error(const char *str)
+{
+ snprintf(error_buffer, sizeof (error_buffer), "%s", str);
+} // set_error
+
+static inline void out_of_memory(void)
+{
+ set_error("out of memory");
+} // out_of_memory
+
+/* Max entries for each register file type */
+#define MAX_REG_FILE_F 8192
+#define MAX_REG_FILE_I 2047
+#define MAX_REG_FILE_B 2047
+
+typedef struct MOJOSHADER_vkContext
+{
+ VkInstance *instance;
+ VkPhysicalDevice *physical_device;
+ VkDevice *logical_device;
+ PFN_vkGetInstanceProcAddr instance_proc_lookup;
+ PFN_vkGetDeviceProcAddr device_proc_lookup;
+ uint32_t graphics_queue_family_index;
+ uint32_t maxUniformBufferRange;
+ uint32_t minUniformBufferOffsetAlignment;
+
+ int32_t frames_in_flight;
+
+ MOJOSHADER_malloc malloc_fn;
+ MOJOSHADER_free free_fn;
+ void *malloc_data;
+
+ // The constant register files...
+ // !!! FIXME: Man, it kills me how much memory this takes...
+ // !!! FIXME: ... make this dynamically allocated on demand.
+ float vs_reg_file_f[MAX_REG_FILE_F * 4];
+ int32_t vs_reg_file_i[MAX_REG_FILE_I * 4];
+ uint8_t vs_reg_file_b[MAX_REG_FILE_B * 4];
+ float ps_reg_file_f[MAX_REG_FILE_F * 4];
+ int32_t ps_reg_file_i[MAX_REG_FILE_I * 4];
+ uint8_t ps_reg_file_b[MAX_REG_FILE_B * 4];
+
+ VkDeviceMemory vertUboMemory;
+ MOJOSHADER_vkUniformBuffer **vertUboBuffers;
+ uint32_t vertUboCurrentIndex;
+
+ VkDeviceMemory fragUboMemory;
+ MOJOSHADER_vkUniformBuffer **fragUboBuffers;
+ uint32_t fragUboCurrentIndex;
+
+ uint32_t uboBufferCount;
+
+ MOJOSHADER_vkShader *vertexShader;
+ MOJOSHADER_vkShader *pixelShader;
+
+ uint32_t currentFrame;
+
+ #define VULKAN_INSTANCE_FUNCTION(ret, func, params) \
+ vkfntype_MOJOSHADER_##func func;
+ #define VULKAN_DEVICE_FUNCTION(ret, func, params) \
+ vkfntype_MOJOSHADER_##func func;
+ #include "mojoshader_vulkan_vkfuncs.h"
+} MOJOSHADER_vkContext;
+
+static MOJOSHADER_vkContext *ctx = NULL;
+
+static uint8_t find_memory_type(
+ MOJOSHADER_vkContext *ctx,
+ uint32_t typeFilter,
+ VkMemoryPropertyFlags properties,
+ uint32_t *result
+) {
+ uint32_t i;
+ VkPhysicalDeviceMemoryProperties memoryProperties;
+ ctx->vkGetPhysicalDeviceMemoryProperties(*ctx->physical_device, &memoryProperties);
+
+ for (i = 0; i < memoryProperties.memoryTypeCount; i++)
+ {
+ if ((typeFilter & (1 << i))
+ && (memoryProperties.memoryTypes[i].propertyFlags & properties) == properties)
+ {
+ *result = i;
+ return 1;
+ } // if
+ } // for
+
+ return 0;
+} // find_memory_type
+
+static uint32_t next_highest_offset_alignment(uint32_t offset)
+{
+ return (
+ (offset + ctx->minUniformBufferOffsetAlignment - 1) /
+ ctx->minUniformBufferOffsetAlignment *
+ ctx->minUniformBufferOffsetAlignment
+ );
+} // next_highest_offset_alignment
+
+static MOJOSHADER_vkUniformBuffer *create_ubo(MOJOSHADER_vkContext *ctx,
+ MOJOSHADER_malloc m,
+ void *d
+) {
+ MOJOSHADER_vkUniformBuffer *result = (MOJOSHADER_vkUniformBuffer *) m(
+ sizeof(MOJOSHADER_vkUniformBuffer),
+ d
+ );
+ VkBufferCreateInfo bufferCreateInfo =
+ {
+ VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO
+ };
+
+ bufferCreateInfo.flags = 0;
+ bufferCreateInfo.size = UBO_BUFFER_SIZE;
+ bufferCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
+ bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
+ bufferCreateInfo.queueFamilyIndexCount = 1;
+ bufferCreateInfo.pQueueFamilyIndices = &ctx->graphics_queue_family_index;
+
+ ctx->vkCreateBuffer(
+ *ctx->logical_device,
+ &bufferCreateInfo,
+ NULL,
+ &result->buffer
+ );
+
+ result->bufferSize = UBO_BUFFER_SIZE;
+ result->currentBlockSize = 0;
+ result->dynamicOffset = 0;
+ result->full = -1;
+
+ return result;
+} // create_ubo
+
+static uint32_t uniform_data_size(MOJOSHADER_vkShader *shader)
+{
+ int32_t i;
+ int32_t uniformSize;
+ int32_t buflen = 0;
+ for (i = 0; i < shader->parseData->uniform_count; i++)
+ {
+ const int32_t arrayCount = shader->parseData->uniforms[i].array_count;
+ uniformSize = 16;
+ if (shader->parseData->uniforms[i].type == MOJOSHADER_UNIFORM_BOOL)
+ uniformSize = 1;
+ buflen += (arrayCount ? arrayCount : 1) * uniformSize;
+ } // for
+
+ return buflen;
+} // uniform_data_size
+
+static VkBuffer get_uniform_buffer(MOJOSHADER_vkShader *shader)
+{
+ if (shader == NULL || shader->parseData->uniform_count == 0)
+ return VK_NULL_HANDLE;
+
+ if (shader->parseData->shader_type == MOJOSHADER_TYPE_VERTEX)
+ return ctx->vertUboBuffers[ctx->vertUboCurrentIndex]->buffer;
+ else
+ return ctx->fragUboBuffers[ctx->fragUboCurrentIndex]->buffer;
+} // get_uniform_buffer
+
+static VkDeviceSize get_uniform_offset(MOJOSHADER_vkShader *shader)
+{
+ if (shader == NULL || shader->parseData->uniform_count == 0)
+ return 0;
+
+ if (shader->parseData->shader_type == MOJOSHADER_TYPE_VERTEX)
+ return ctx->vertUboBuffers[ctx->vertUboCurrentIndex]->dynamicOffset;
+ else
+ return ctx->fragUboBuffers[ctx->fragUboCurrentIndex]->dynamicOffset;
+} // get_uniform_offset
+
+static VkDeviceSize get_uniform_size(MOJOSHADER_vkShader *shader)
+{
+ if (shader == NULL || shader->parseData->uniform_count == 0)
+ return 0;
+
+ if (shader->parseData->shader_type == MOJOSHADER_TYPE_VERTEX)
+ return ctx->vertUboBuffers[ctx->vertUboCurrentIndex]->currentBlockSize;
+ else
+ return ctx->fragUboBuffers[ctx->fragUboCurrentIndex]->currentBlockSize;
+} // get_uniform_size
+
+static void update_uniform_buffer(MOJOSHADER_vkShader *shader)
+{
+ int32_t i;
+ void *map;
+ int32_t offset;
+ uint8_t *contents;
+ float *regF; int *regI; uint8_t *regB;
+ MOJOSHADER_vkUniformBuffer *ubo;
+ VkDeviceMemory uboMemory;
+
+ if (shader == NULL || shader->parseData->uniform_count == 0)
+ return;
+
+ if (shader->parseData->shader_type == MOJOSHADER_TYPE_VERTEX)
+ {
+ regF = ctx->vs_reg_file_f;
+ regI = ctx->vs_reg_file_i;
+ regB = ctx->vs_reg_file_b;
+
+ ubo = ctx->vertUboBuffers[ctx->vertUboCurrentIndex];
+ uboMemory = ctx->vertUboMemory;
+ } // if
+ else
+ {
+ regF = ctx->ps_reg_file_f;
+ regI = ctx->ps_reg_file_i;
+ regB = ctx->ps_reg_file_b;
+
+ ubo = ctx->fragUboBuffers[ctx->fragUboCurrentIndex];
+ uboMemory = ctx->fragUboMemory;
+ } // else
+
+ ubo->dynamicOffset += ubo->currentBlockSize;
+
+ ubo->currentBlockSize = next_highest_offset_alignment(uniform_data_size(shader));
+
+ // Rotate buffer if it would overrun
+ if (ubo->dynamicOffset + ubo->currentBlockSize >= ubo->bufferSize)
+ {
+ ubo->full = ctx->currentFrame;
+
+ if (shader->parseData->shader_type == MOJOSHADER_TYPE_VERTEX)
+ {
+ for (i = 0; i < ctx->uboBufferCount; i++)
+ {
+ ctx->vertUboCurrentIndex = (ctx->vertUboCurrentIndex + 1) % ctx->uboBufferCount;
+ if (ctx->vertUboBuffers[ctx->vertUboCurrentIndex]->full == -1)
+ break;
+ } // for
+
+ ubo = ctx->vertUboBuffers[ctx->vertUboCurrentIndex];
+ }
+ else
+ {
+ for (int i = 0; i < ctx->uboBufferCount; i++)
+ {
+ ctx->fragUboCurrentIndex = (ctx->fragUboCurrentIndex + 1) % ctx->uboBufferCount;
+ if (ctx->fragUboBuffers[ctx->fragUboCurrentIndex]->full == -1)
+ break;
+ } // for
+
+ ubo = ctx->fragUboBuffers[ctx->fragUboCurrentIndex];
+ } // else
+
+ ubo->dynamicOffset = 0;
+ ubo->currentBlockSize = next_highest_offset_alignment(uniform_data_size(shader));
+
+ if (ubo->full >= 0)
+ set_error("all UBO buffers are full");
+ } // if
+
+ ctx->vkMapMemory(
+ *ctx->logical_device,
+ uboMemory,
+ ubo->memoryOffset,
+ ubo->bufferSize,
+ 0,
+ &map
+ );
+
+ contents = ((uint8_t *) map) + ubo->dynamicOffset;
+
+ offset = 0;
+ for (i = 0; i < shader->parseData->uniform_count; i++)
+ {
+ const int32_t index = shader->parseData->uniforms[i].index;
+ const int32_t arrayCount = shader->parseData->uniforms[i].array_count;
+ const int32_t size = arrayCount ? arrayCount : 1;
+
+ switch (shader->parseData->uniforms[i].type)
+ {
+ case MOJOSHADER_UNIFORM_FLOAT:
+ memcpy(
+ contents + (offset * 16),
+ ®F[4 * index],
+ size * 16
+ );
+ break;
+
+ case MOJOSHADER_UNIFORM_INT:
+ memcpy(
+ contents + (offset * 16),
+ ®I[4 * index],
+ size * 16
+ );
+ break;
+
+ case MOJOSHADER_UNIFORM_BOOL:
+ memcpy(
+ contents + offset,
+ ®B[index],
+ size
+ );
+ break;
+
+ default:
+ set_error(
+ "SOMETHING VERY WRONG HAPPENED WHEN UPDATING UNIFORMS"
+ );
+ assert(0);
+ break;
+ } // switch
+
+ offset += size;
+ } // for
+
+ ctx->vkUnmapMemory(
+ *ctx->logical_device,
+ uboMemory
+ );
+} // update_uniform_buffer
+
+static void lookup_entry_points(MOJOSHADER_vkContext *ctx)
+{
+ #define VULKAN_INSTANCE_FUNCTION(ret, func, params) \
+ ctx->func = (vkfntype_MOJOSHADER_##func) ctx->instance_proc_lookup(*ctx->instance, #func);
+ #define VULKAN_DEVICE_FUNCTION(ret, func, params) \
+ ctx->func = (vkfntype_MOJOSHADER_##func) ctx->device_proc_lookup(*ctx->logical_device, #func);
+ #include "mojoshader_vulkan_vkfuncs.h"
+} // lookup_entry_points
+
+static int shader_bytecode_len(MOJOSHADER_vkShader *shader)
+{
+ return shader->parseData->output_len - sizeof(SpirvPatchTable);
+} // shader_bytecode_len
+
+static void delete_shader(
+ VkShaderModule shaderModule
+) {
+ ctx->vkDestroyShaderModule(
+ *ctx->logical_device,
+ shaderModule,
+ NULL
+ );
+} // delete_shader
+
+// Public API
+
+MOJOSHADER_vkContext *MOJOSHADER_vkCreateContext(
+ VkInstance *instance,
+ VkPhysicalDevice *physical_device,
+ VkDevice *logical_device,
+ int frames_in_flight,
+ PFN_MOJOSHADER_vkGetInstanceProcAddr instance_lookup,
+ PFN_MOJOSHADER_vkGetDeviceProcAddr device_lookup,
+ unsigned int graphics_queue_family_index,
+ unsigned int max_uniform_buffer_range,
+ unsigned int min_uniform_buffer_offset_alignment,
+ MOJOSHADER_malloc m, MOJOSHADER_free f,
+ void *malloc_d
+) {
+ int32_t i;
+ int32_t uboMemoryOffset;
+ VkMemoryAllocateInfo allocate_info =
+ {
+ VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO
+ };
+ VkMemoryRequirements memoryRequirements;
+ MOJOSHADER_vkContext* resultCtx;
+
+ if (m == NULL) m = MOJOSHADER_internal_malloc;
+ if (f == NULL) f = MOJOSHADER_internal_free;
+
+ resultCtx = (MOJOSHADER_vkContext *) m(sizeof(MOJOSHADER_vkContext), malloc_d);
+ if (resultCtx == NULL)
+ {
+ out_of_memory();
+ goto init_fail;
+ }
+
+ memset(resultCtx, '\0', sizeof(MOJOSHADER_vkContext));
+ resultCtx->malloc_fn = m;
+ resultCtx->free_fn = f;
+ resultCtx->malloc_data = malloc_d;
+
+ resultCtx->instance = (VkInstance*) instance;
+ resultCtx->physical_device = (VkPhysicalDevice*) physical_device;
+ resultCtx->logical_device = (VkDevice*) logical_device;
+ resultCtx->instance_proc_lookup = (PFN_vkGetInstanceProcAddr) instance_lookup;
+ resultCtx->device_proc_lookup = (PFN_vkGetDeviceProcAddr) device_lookup;
+ resultCtx->frames_in_flight = frames_in_flight;
+ resultCtx->graphics_queue_family_index = graphics_queue_family_index;
+ resultCtx->maxUniformBufferRange = max_uniform_buffer_range;
+ resultCtx->minUniformBufferOffsetAlignment = min_uniform_buffer_offset_alignment;
+ resultCtx->currentFrame = 0;
+
+ lookup_entry_points(resultCtx);
+
+ resultCtx->uboBufferCount = UBO_BUFFER_COUNT;
+
+ // Allocate vert UBO
+
+ resultCtx->vertUboCurrentIndex = 0;
+ resultCtx->vertUboBuffers = (MOJOSHADER_vkUniformBuffer**) m(
+ sizeof(MOJOSHADER_vkUniformBuffer*) * resultCtx->uboBufferCount,
+ malloc_d
+ );
+
+ for (i = 0; i < resultCtx->uboBufferCount; i++)
+ resultCtx->vertUboBuffers[i] = create_ubo(resultCtx, m, malloc_d);
+
+ resultCtx->vkGetBufferMemoryRequirements(
+ *resultCtx->logical_device,
+ resultCtx->vertUboBuffers[0]->buffer,
+ &memoryRequirements
+ );
+
+ allocate_info.allocationSize = UBO_BUFFER_SIZE * resultCtx->uboBufferCount;
+
+ if (!find_memory_type(resultCtx,
+ memoryRequirements.memoryTypeBits,
+ VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
+ &allocate_info.memoryTypeIndex))
+ {
+ set_error("failed to find suitable memory type for UBO memory");
+ return NULL;
+ } // if
+
+ resultCtx->vkAllocateMemory(
+ *resultCtx->logical_device,
+ &allocate_info,
+ NULL,
+ &resultCtx->vertUboMemory
+ );
+
+ uboMemoryOffset = 0;
+ for (i = 0; i < resultCtx->uboBufferCount; i++)
+ {
+ resultCtx->vertUboBuffers[i]->memoryOffset = uboMemoryOffset;
+
+ resultCtx->vkBindBufferMemory(
+ *resultCtx->logical_device,
+ resultCtx->vertUboBuffers[i]->buffer,
+ resultCtx->vertUboMemory,
+ uboMemoryOffset
+ );
+
+ uboMemoryOffset += UBO_BUFFER_SIZE;
+ } // for
+
+ // Allocate frag UBO
+
+ resultCtx->fragUboCurrentIndex = 0;
+ resultCtx->fragUboBuffers = (MOJOSHADER_vkUniformBuffer**) m(
+ sizeof(MOJOSHADER_vkUniformBuffer*) * resultCtx->uboBufferCount,
+ malloc_d
+ );
+
+ for (i = 0; i < resultCtx->uboBufferCount; i++)
+ resultCtx->fragUboBuffers[i] = create_ubo(resultCtx, m, malloc_d);
+
+ resultCtx->vkGetBufferMemoryRequirements(
+ *resultCtx->logical_device,
+ resultCtx->fragUboBuffers[0]->buffer,
+ &memoryRequirements
+ );
+
+ allocate_info.allocationSize = UBO_BUFFER_SIZE * resultCtx->uboBufferCount;
+
+ if (!find_memory_type(resultCtx,
+ memoryRequirements.memoryTypeBits,
+ VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
+ &allocate_info.memoryTypeIndex))
+ {
+ set_error("failed to find suitable memory type for UBO memory");
+ return NULL;
+ } // if
+
+ resultCtx->vkAllocateMemory(
+ *resultCtx->logical_device,
+ &allocate_info,
+ NULL,
+ &resultCtx->fragUboMemory
+ );
+
+ uboMemoryOffset = 0;
+ for (i = 0; i < resultCtx->uboBufferCount; i++)
+ {
+ resultCtx->fragUboBuffers[i]->memoryOffset = uboMemoryOffset;
+
+ resultCtx->vkBindBufferMemory(
+ *resultCtx->logical_device,
+ resultCtx->fragUboBuffers[i]->buffer,
+ resultCtx->fragUboMemory,
+ uboMemoryOffset
+ );
+
+ uboMemoryOffset += UBO_BUFFER_SIZE;
+ } // for
+
+ return resultCtx;
+
+init_fail:
+ if (resultCtx != NULL)
+ f(resultCtx, malloc_d);
+ return NULL;
+} // MOJOSHADER_vkCreateContext
+
+void MOJOSHADER_vkMakeContextCurrent(MOJOSHADER_vkContext *_ctx)
+{
+ ctx = _ctx;
+} // MOJOSHADER_vkMakeContextCurrent
+
+void MOJOSHADER_vkDestroyContext()
+{
+ int32_t i;
+ for (i = 0; i < ctx->uboBufferCount; i++)
+ {
+ ctx->vkDestroyBuffer(
+ *ctx->logical_device,
+ ctx->vertUboBuffers[i]->buffer,
+ NULL
+ );
+
+ ctx->free_fn(ctx->vertUboBuffers[i], ctx->malloc_data);
+
+ ctx->vkDestroyBuffer(
+ *ctx->logical_device,
+ ctx->fragUboBuffers[i]->buffer,
+ NULL
+ );
+
+ ctx->free_fn(ctx->fragUboBuffers[i], ctx->malloc_data);
+ } // for
+
+ ctx->free_fn(ctx->vertUboBuffers, ctx->malloc_data);
+ ctx->free_fn(ctx->fragUboBuffers, ctx->malloc_data);
+
+ ctx->vkFreeMemory(
+ *ctx->logical_device,
+ ctx->vertUboMemory,
+ NULL
+ );
+
+ ctx->vkFreeMemory(
+ *ctx->logical_device,
+ ctx->fragUboMemory,
+ NULL
+ );
+
+ ctx->free_fn(ctx, ctx->malloc_data);
+} // MOJOSHADER_vkDestroyContext
+
+MOJOSHADER_vkShader *MOJOSHADER_vkCompileShader(
+ const char *mainfn,
+ const unsigned char *tokenbuf,
+ const unsigned int bufsize,
+ const MOJOSHADER_swizzle *swiz,
+ const unsigned int swizcount,
+ const MOJOSHADER_samplerMap *smap,
+ const unsigned int smapcount
+) {
+ VkResult result;
+ VkShaderModule shaderModule;
+ VkShaderModuleCreateInfo shaderModuleCreateInfo =
+ {
+ VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO
+ };
+ MOJOSHADER_vkShader *shader;
+
+ const MOJOSHADER_parseData *pd = MOJOSHADER_parse(
+ "spirv", mainfn,
+ tokenbuf, bufsize,
+ swiz, swizcount,
+ smap, smapcount,
+ ctx->malloc_fn,
+ ctx->free_fn,
+ ctx->malloc_data
+ );
+
+ if (pd->error_count > 0)
+ {
+ set_error(pd->errors[0].error);
+ goto compile_shader_fail;
+ } // if
+
+ shader = (MOJOSHADER_vkShader *) ctx->malloc_fn(sizeof(MOJOSHADER_vkShader), ctx->malloc_data);
+ if (shader == NULL)
+ {
+ out_of_memory();
+ goto compile_shader_fail;
+ } // if
+
+ shader->parseData = pd;
+ shader->refcount = 1;
+
+ shaderModuleCreateInfo.flags = 0;
+ shaderModuleCreateInfo.codeSize = shader_bytecode_len(shader);
+ shaderModuleCreateInfo.pCode = (uint32_t*) pd->output;
+
+ result = ctx->vkCreateShaderModule(
+ *ctx->logical_device,
+ &shaderModuleCreateInfo,
+ NULL,
+ &shader->shaderModule
+ );
+
+ if (result != VK_SUCCESS)
+ {
+ // FIXME: should display VK error code
+ set_error("Error when creating VkShaderModule");
+ goto compile_shader_fail;
+ } // if
+
+ return shader;
+
+compile_shader_fail:
+ MOJOSHADER_freeParseData(pd);
+ if (shader != NULL)
+ {
+ delete_shader(shader->shaderModule);
+ ctx->free_fn(shader, ctx->malloc_data);
+ } // if
+ return NULL;
+
+} // MOJOSHADER_vkMakeContextCurrent
+
+void MOJOSHADER_vkShaderAddRef(MOJOSHADER_vkShader *shader)
+{
+ if (shader != NULL)
+ shader->refcount++;
+} // MOJOShader_vkShaderAddRef
+
+void MOJOSHADER_vkDeleteShader(MOJOSHADER_vkShader *shader)
+{
+ if (shader != NULL)
+ {
+ if (shader->refcount > 1)
+ shader->refcount--;
+ else
+ {
+ delete_shader(shader->shaderModule);
+ MOJOSHADER_freeParseData(shader->parseData);
+ ctx->free_fn(shader, ctx->malloc_data);
+ } // else
+ } // if
+} // MOJOSHADER_vkDeleteShader
+
+const MOJOSHADER_parseData *MOJOSHADER_vkGetShaderParseData(
+ MOJOSHADER_vkShader *shader
+) {
+ return (shader != NULL) ? shader->parseData : NULL;
+} // MOJOSHADER_vkGetShaderParseData
+
+void MOJOSHADER_vkBindShaders(MOJOSHADER_vkShader *vshader,
+ MOJOSHADER_vkShader *pshader)
+{
+ /* NOOP if shader is null */
+
+ if (vshader != NULL)
+ ctx->vertexShader = vshader;
+ if (pshader != NULL)
+ ctx->pixelShader = pshader;
+} // MOJOSHADER_vkBindShaders
+
+void MOJOSHADER_vkGetBoundShaders(MOJOSHADER_vkShader **vshader,
+ MOJOSHADER_vkShader **pshader)
+{
+ *vshader = ctx->vertexShader;
+ *pshader = ctx->pixelShader;
+} // MOJOSHADER_vkGetBoundShaders
+
+void MOJOSHADER_vkMapUniformBufferMemory(float **vsf, int **vsi, unsigned char **vsb,
+ float **psf, int **psi, unsigned char **psb)
+{
+ *vsf = ctx->vs_reg_file_f;
+ *vsi = ctx->vs_reg_file_i;
+ *vsb = ctx->vs_reg_file_b;
+ *psf = ctx->ps_reg_file_f;
+ *psi = ctx->ps_reg_file_i;
+ *psb = ctx->ps_reg_file_b;
+} // MOJOSHADER_vkMapUniformBufferMemory
+
+void MOJOSHADER_vkUnmapUniformBufferMemory()
+{
+ /* Why is this function named unmap instead of update?
+ * the world may never know...
+ */
+
+ update_uniform_buffer(ctx->vertexShader);
+ update_uniform_buffer(ctx->pixelShader);
+} // MOJOSHADER_vkUnmapUniformBufferMemory
+
+void MOJOSHADER_vkGetUniformBuffers(VkBuffer *vbuf, unsigned long long *voff, unsigned long long *vsize,
+ VkBuffer *pbuf, unsigned long long *poff, unsigned long long *psize)
+{
+ *vbuf = get_uniform_buffer(ctx->vertexShader);
+ *voff = get_uniform_offset(ctx->vertexShader);
+ *vsize = get_uniform_size(ctx->vertexShader);
+ *pbuf = get_uniform_buffer(ctx->pixelShader);
+ *poff = get_uniform_offset(ctx->pixelShader);
+ *psize = get_uniform_size(ctx->pixelShader);
+} // MOJOSHADER_vkGetUniformBuffers
+
+void MOJOSHADER_vkEndFrame()
+{
+ int32_t i;
+ ctx->currentFrame = (ctx->currentFrame + 1) % ctx->frames_in_flight;
+ for (i = 0; i < ctx->uboBufferCount; i++)
+ {
+ if (ctx->vertUboBuffers[i]->full == ctx->currentFrame)
+ {
+ ctx->vertUboBuffers[i]->dynamicOffset = 0;
+ ctx->vertUboBuffers[i]->currentBlockSize = 0;
+ ctx->vertUboBuffers[i]->full = -1;
+ } // if
+
+ if (ctx->fragUboBuffers[i]->full == ctx->currentFrame)
+ {
+ ctx->fragUboBuffers[i]->dynamicOffset = 0;
+ ctx->fragUboBuffers[i]->currentBlockSize = 0;
+ ctx->fragUboBuffers[i]->full = -1;
+ } // if
+ } // for
+} // MOJOSHADER_VkEndFrame
+
+int MOJOSHADER_vkGetVertexAttribLocation(MOJOSHADER_vkShader *vert,
+ MOJOSHADER_usage usage, int index)
+{
+ int32_t i;
+ if (vert == NULL)
+ return -1;
+
+ for (i = 0; i < vert->parseData->attribute_count; i++)
+ {
+ if (vert->parseData->attributes[i].usage == usage &&
+ vert->parseData->attributes[i].index == index)
+ {
+ return i;
+ } // if
+ } // for
+
+ // failure
+ return -1;
+} //MOJOSHADER_vkGetVertexAttribLocation
+
+unsigned long long MOJOSHADER_vkGetShaderModule(MOJOSHADER_vkShader *shader)
+{
+ if (shader == NULL)
+ return 0;
+
+ return (unsigned long long) shader->shaderModule;
+} //MOJOSHADER_vkGetShaderModule
+
+const char *MOJOSHADER_vkGetError(void)
+{
+ return error_buffer;
+} // MOJOSHADER_vkGetError
+
+#endif /* SUPPORT_PROFILE_SPIRV */
+
+// end of mojoshader_vulkan.c ...