Show More
Commit Description:
Fix build errors.
Commit Description:
Fix build errors.
References:
File last commit:
Show/Diff file:
Action:
FNA/lib/FNA3D/MojoShader/mojoshader_vulkan.c
851 lines | 25.1 KiB | text/x-c | CLexer
851 lines | 25.1 KiB | text/x-c | CLexer
r690 | /** | |||
* 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 | ||||
#define VK_NO_PROTOTYPES | ||||
#include "vulkan/vulkan.h" | ||||
#define UBO_BUFFER_SIZE 8000000 // 8MB | ||||
#define UBO_ACTUAL_SIZE (UBO_BUFFER_SIZE * 2) // Double so we can "rotate" the buffer and unblock main thread | ||||
// Internal struct defs... | ||||
typedef struct MOJOSHADER_vkShader | ||||
{ | ||||
const MOJOSHADER_parseData *parseData; | ||||
uint16_t tag; | ||||
uint32_t refcount; | ||||
} MOJOSHADER_vkShader; | ||||
typedef struct MOJOSHADER_vkProgram | ||||
{ | ||||
VkShaderModule vertexModule; | ||||
VkShaderModule pixelModule; | ||||
MOJOSHADER_vkShader *vertexShader; | ||||
MOJOSHADER_vkShader *pixelShader; | ||||
} MOJOSHADER_vkProgram; | ||||
typedef struct MOJOSHADER_vkUniformBuffer | ||||
{ | ||||
VkBuffer buffer; | ||||
VkDeviceMemory deviceMemory; | ||||
VkDeviceSize bufferSize; | ||||
VkDeviceSize dynamicOffset; | ||||
VkDeviceSize currentBlockSize; | ||||
VkDeviceSize currentBlockIncrement; | ||||
uint8_t *mapPointer; | ||||
} 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; | ||||
uint32_t frameIndex; | ||||
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]; | ||||
MOJOSHADER_vkUniformBuffer *vertUboBuffer; | ||||
MOJOSHADER_vkUniformBuffer *fragUboBuffer; | ||||
MOJOSHADER_vkProgram *bound_program; | ||||
HashTable *linker_cache; | ||||
// Note that these may not necessarily align with bound_program! | ||||
// We need to store these so effects can have overlapping shaders. | ||||
MOJOSHADER_vkShader *bound_vshader; | ||||
MOJOSHADER_vkShader *bound_pshader; | ||||
#define VULKAN_INSTANCE_FUNCTION(name) \ | ||||
PFN_##name name; | ||||
#define VULKAN_DEVICE_FUNCTION(name) \ | ||||
PFN_##name name; | ||||
#include "mojoshader_vulkan_vkfuncs.h" | ||||
} MOJOSHADER_vkContext; | ||||
static uint16_t tagCounter = 1; | ||||
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( | ||||
MOJOSHADER_vkContext *ctx, | ||||
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_vkUniformBuffer *result = (MOJOSHADER_vkUniformBuffer *) ctx->malloc_fn( | ||||
sizeof(MOJOSHADER_vkUniformBuffer), | ||||
ctx->malloc_data | ||||
); | ||||
VkBufferCreateInfo bufferCreateInfo = | ||||
{ | ||||
VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO | ||||
}; | ||||
VkMemoryRequirements memoryRequirements; | ||||
VkMemoryAllocateInfo allocateInfo = | ||||
{ | ||||
VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO | ||||
}; | ||||
bufferCreateInfo.flags = 0; | ||||
bufferCreateInfo.size = UBO_ACTUAL_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 | ||||
); | ||||
ctx->vkGetBufferMemoryRequirements( | ||||
*ctx->logical_device, | ||||
result->buffer, | ||||
&memoryRequirements | ||||
); | ||||
allocateInfo.allocationSize = UBO_ACTUAL_SIZE; | ||||
if (!find_memory_type(ctx, | ||||
memoryRequirements.memoryTypeBits, | ||||
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, | ||||
&allocateInfo.memoryTypeIndex)) | ||||
{ | ||||
set_error("failed to find suitable memory type for UBO memory"); | ||||
return NULL; | ||||
} // if | ||||
ctx->vkAllocateMemory(*ctx->logical_device, | ||||
&allocateInfo, | ||||
NULL, | ||||
&result->deviceMemory | ||||
); | ||||
ctx->vkBindBufferMemory(*ctx->logical_device, | ||||
result->buffer, | ||||
result->deviceMemory, | ||||
0 | ||||
); | ||||
ctx->vkMapMemory(*ctx->logical_device, | ||||
result->deviceMemory, | ||||
0, | ||||
UBO_ACTUAL_SIZE, | ||||
0, | ||||
(void**) &result->mapPointer | ||||
); | ||||
result->bufferSize = UBO_ACTUAL_SIZE; | ||||
result->currentBlockSize = 0; | ||||
result->currentBlockIncrement = 0; | ||||
result->dynamicOffset = 0; | ||||
return result; | ||||
} // create_ubo | ||||
static uint32_t uniform_data_size(MOJOSHADER_vkShader *shader) | ||||
{ | ||||
int32_t i; | ||||
int32_t buflen = 0; | ||||
const int32_t uniformSize = 16; // Yes, even the bool registers | ||||
for (i = 0; i < shader->parseData->uniform_count; i++) | ||||
{ | ||||
const int32_t arrayCount = shader->parseData->uniforms[i].array_count; | ||||
buflen += (arrayCount ? arrayCount : 1) * uniformSize; | ||||
} // for | ||||
return buflen; | ||||
} // uniform_data_size | ||||
static VkBuffer get_uniform_buffer( | ||||
MOJOSHADER_vkContext *ctx, | ||||
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->vertUboBuffer->buffer; | ||||
else | ||||
return ctx->fragUboBuffer->buffer; | ||||
} // get_uniform_buffer | ||||
static VkDeviceSize get_uniform_offset( | ||||
MOJOSHADER_vkContext *ctx, | ||||
MOJOSHADER_vkShader *shader | ||||
) { | ||||
if (shader == NULL || shader->parseData->uniform_count == 0) | ||||
return 0; | ||||
if (shader->parseData->shader_type == MOJOSHADER_TYPE_VERTEX) | ||||
return ctx->vertUboBuffer->dynamicOffset; | ||||
else | ||||
return ctx->fragUboBuffer->dynamicOffset; | ||||
} // get_uniform_offset | ||||
static VkDeviceSize get_uniform_size( | ||||
MOJOSHADER_vkContext *ctx, | ||||
MOJOSHADER_vkShader *shader | ||||
) { | ||||
if (shader == NULL || shader->parseData->uniform_count == 0) | ||||
return 0; | ||||
if (shader->parseData->shader_type == MOJOSHADER_TYPE_VERTEX) | ||||
return ctx->vertUboBuffer->currentBlockSize; | ||||
else | ||||
return ctx->fragUboBuffer->currentBlockSize; | ||||
} // get_uniform_size | ||||
static void update_uniform_buffer( | ||||
MOJOSHADER_vkContext *ctx, | ||||
MOJOSHADER_vkShader *shader | ||||
) { | ||||
int32_t i, j; | ||||
int32_t offset; | ||||
uint8_t *contents; | ||||
uint32_t *contentsI; | ||||
float *regF; int *regI; uint8_t *regB; | ||||
MOJOSHADER_vkUniformBuffer *ubo; | ||||
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->vertUboBuffer; | ||||
} // if | ||||
else | ||||
{ | ||||
regF = ctx->ps_reg_file_f; | ||||
regI = ctx->ps_reg_file_i; | ||||
regB = ctx->ps_reg_file_b; | ||||
ubo = ctx->fragUboBuffer; | ||||
} // else | ||||
ubo->dynamicOffset += ubo->currentBlockIncrement; | ||||
ubo->currentBlockSize = next_highest_offset_alignment(ctx, uniform_data_size(shader)); | ||||
ubo->currentBlockIncrement = ubo->currentBlockSize; | ||||
if (ubo->dynamicOffset + ubo->currentBlockSize >= ubo->bufferSize * ctx->frameIndex) | ||||
{ | ||||
set_error("UBO overflow!!"); | ||||
} // if | ||||
contents = ubo->mapPointer + 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, | ||||
®F[4 * index], | ||||
size * 16 | ||||
); | ||||
break; | ||||
case MOJOSHADER_UNIFORM_INT: | ||||
memcpy( | ||||
contents + offset, | ||||
®I[4 * index], | ||||
size * 16 | ||||
); | ||||
break; | ||||
case MOJOSHADER_UNIFORM_BOOL: | ||||
contentsI = (uint32_t *) (contents + offset); | ||||
for (j = 0; j < size; j++) | ||||
contentsI[j * 4] = regB[index + j]; | ||||
break; | ||||
default: | ||||
set_error( | ||||
"SOMETHING VERY WRONG HAPPENED WHEN UPDATING UNIFORMS" | ||||
); | ||||
assert(0); | ||||
break; | ||||
} // switch | ||||
offset += size * 16; | ||||
} // for | ||||
} // update_uniform_buffer | ||||
static void lookup_entry_points(MOJOSHADER_vkContext *ctx) | ||||
{ | ||||
#define VULKAN_INSTANCE_FUNCTION(name) \ | ||||
ctx->name = (PFN_##name) ctx->instance_proc_lookup(*ctx->instance, #name); | ||||
#define VULKAN_DEVICE_FUNCTION(name) \ | ||||
ctx->name = (PFN_##name) ctx->device_proc_lookup(*ctx->logical_device, #name); | ||||
#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 VkShaderModule compile_shader( | ||||
MOJOSHADER_vkContext *ctx, | ||||
MOJOSHADER_vkShader *shader | ||||
) { | ||||
VkResult result; | ||||
VkShaderModule module; | ||||
VkShaderModuleCreateInfo shaderModuleCreateInfo = | ||||
{ | ||||
VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO | ||||
}; | ||||
shaderModuleCreateInfo.flags = 0; | ||||
shaderModuleCreateInfo.codeSize = shader_bytecode_len(shader); | ||||
shaderModuleCreateInfo.pCode = (uint32_t*) shader->parseData->output; | ||||
result = ctx->vkCreateShaderModule( | ||||
*ctx->logical_device, | ||||
&shaderModuleCreateInfo, | ||||
NULL, | ||||
&module | ||||
); | ||||
if (result != VK_SUCCESS) | ||||
{ | ||||
// FIXME: should display VK error code | ||||
set_error("Error when creating VkShaderModule"); | ||||
ctx->vkDestroyShaderModule( | ||||
*ctx->logical_device, | ||||
module, | ||||
NULL | ||||
); | ||||
return VK_NULL_HANDLE; | ||||
} // if | ||||
return module; | ||||
} // compile_shader | ||||
typedef struct | ||||
{ | ||||
MOJOSHADER_vkShader *vertex; | ||||
MOJOSHADER_vkShader *fragment; | ||||
} BoundShaders; | ||||
static uint32_t hash_shaders(const void *sym, void *data) | ||||
{ | ||||
(void) data; | ||||
const BoundShaders *s = (const BoundShaders *) sym; | ||||
const uint16_t v = (s->vertex) ? s->vertex->tag : 0; | ||||
const uint16_t f = (s->fragment) ? s->fragment->tag : 0; | ||||
return ((uint32_t) v << 16) | (uint32_t) f; | ||||
} // hash_shaders | ||||
static int match_shaders(const void *_a, const void *_b, void *data) | ||||
{ | ||||
(void) data; | ||||
const BoundShaders *a = (const BoundShaders *) _a; | ||||
const BoundShaders *b = (const BoundShaders *) _b; | ||||
const uint16_t av = (a->vertex) ? a->vertex->tag : 0; | ||||
const uint16_t bv = (b->vertex) ? b->vertex->tag : 0; | ||||
if (av != bv) | ||||
return 0; | ||||
const uint16_t af = (a->fragment) ? a->fragment->tag : 0; | ||||
const uint16_t bf = (b->fragment) ? b->fragment->tag : 0; | ||||
if (af != bf) | ||||
return 0; | ||||
return 1; | ||||
} // match_shaders | ||||
static void nuke_shaders( | ||||
const void *_ctx, | ||||
const void *key, | ||||
const void *value, | ||||
void *data | ||||
) { | ||||
MOJOSHADER_vkContext *ctx = (MOJOSHADER_vkContext *) _ctx; | ||||
(void) data; | ||||
ctx->free_fn((void *) key, ctx->malloc_data); // this was a BoundShaders struct. | ||||
MOJOSHADER_vkDeleteProgram(ctx, (MOJOSHADER_vkProgram *) value); | ||||
} // nuke_shaders | ||||
// Public API | ||||
MOJOSHADER_vkContext *MOJOSHADER_vkCreateContext( | ||||
VkInstance *instance, | ||||
VkPhysicalDevice *physical_device, | ||||
VkDevice *logical_device, | ||||
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 | ||||
) { | ||||
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; | ||||
} // if | ||||
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->frameIndex = 0; | ||||
resultCtx->graphics_queue_family_index = graphics_queue_family_index; | ||||
resultCtx->maxUniformBufferRange = max_uniform_buffer_range; | ||||
resultCtx->minUniformBufferOffsetAlignment = min_uniform_buffer_offset_alignment; | ||||
lookup_entry_points(resultCtx); | ||||
resultCtx->vertUboBuffer = create_ubo(resultCtx); | ||||
resultCtx->fragUboBuffer = create_ubo(resultCtx); | ||||
return resultCtx; | ||||
init_fail: | ||||
if (resultCtx != NULL) | ||||
f(resultCtx, malloc_d); | ||||
return NULL; | ||||
} // MOJOSHADER_vkCreateContext | ||||
void MOJOSHADER_vkDestroyContext(MOJOSHADER_vkContext *ctx) | ||||
{ | ||||
MOJOSHADER_vkBindProgram(ctx, NULL); | ||||
if (ctx->linker_cache) | ||||
hash_destroy(ctx->linker_cache, ctx); | ||||
ctx->vkDestroyBuffer(*ctx->logical_device, | ||||
ctx->vertUboBuffer->buffer, | ||||
NULL); | ||||
ctx->vkDestroyBuffer(*ctx->logical_device, | ||||
ctx->fragUboBuffer->buffer, | ||||
NULL); | ||||
ctx->vkFreeMemory(*ctx->logical_device, | ||||
ctx->vertUboBuffer->deviceMemory, | ||||
NULL); | ||||
ctx->vkFreeMemory(*ctx->logical_device, | ||||
ctx->fragUboBuffer->deviceMemory, | ||||
NULL); | ||||
ctx->free_fn(ctx->vertUboBuffer, ctx->malloc_data); | ||||
ctx->free_fn(ctx->fragUboBuffer, ctx->malloc_data); | ||||
ctx->free_fn(ctx, ctx->malloc_data); | ||||
} // MOJOSHADER_vkDestroyContext | ||||
MOJOSHADER_vkShader *MOJOSHADER_vkCompileShader( | ||||
MOJOSHADER_vkContext *ctx, | ||||
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 | ||||
) { | ||||
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 parse_shader_fail; | ||||
} // if | ||||
shader = (MOJOSHADER_vkShader *) ctx->malloc_fn(sizeof(MOJOSHADER_vkShader), ctx->malloc_data); | ||||
if (shader == NULL) | ||||
{ | ||||
out_of_memory(); | ||||
goto parse_shader_fail; | ||||
} // if | ||||
shader->parseData = pd; | ||||
shader->refcount = 1; | ||||
shader->tag = tagCounter++; | ||||
return shader; | ||||
parse_shader_fail: | ||||
MOJOSHADER_freeParseData(pd); | ||||
if (shader != NULL) | ||||
ctx->free_fn(shader, ctx->malloc_data); | ||||
return NULL; | ||||
} // MOJOSHADER_vkCompileShader | ||||
void MOJOSHADER_vkShaderAddRef(MOJOSHADER_vkShader *shader) | ||||
{ | ||||
if (shader != NULL) | ||||
shader->refcount++; | ||||
} // MOJOShader_vkShaderAddRef | ||||
void MOJOSHADER_vkDeleteShader( | ||||
MOJOSHADER_vkContext *ctx, | ||||
MOJOSHADER_vkShader *shader | ||||
) { | ||||
if (shader != NULL) | ||||
{ | ||||
if (shader->refcount > 1) | ||||
shader->refcount--; | ||||
else | ||||
{ | ||||
// See if this was bound as an unlinked program anywhere... | ||||
if (ctx->linker_cache) | ||||
{ | ||||
const void *key = NULL; | ||||
void *iter = NULL; | ||||
int morekeys = hash_iter_keys(ctx->linker_cache, &key, &iter); | ||||
while (morekeys) | ||||
{ | ||||
const BoundShaders *shaders = (const BoundShaders *) key; | ||||
// Do this here so we don't confuse the iteration by removing... | ||||
morekeys = hash_iter_keys(ctx->linker_cache, &key, &iter); | ||||
if ((shaders->vertex == shader) || (shaders->fragment == shader)) | ||||
{ | ||||
// Deletes the linked program | ||||
hash_remove(ctx->linker_cache, shaders, ctx); | ||||
} // if | ||||
} // while | ||||
} // if | ||||
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_vkDeleteProgram( | ||||
MOJOSHADER_vkContext *ctx, | ||||
MOJOSHADER_vkProgram *p | ||||
) { | ||||
if (p->vertexModule != VK_NULL_HANDLE) | ||||
ctx->vkDestroyShaderModule(*ctx->logical_device, p->vertexModule, NULL); | ||||
if (p->pixelModule != VK_NULL_HANDLE) | ||||
ctx->vkDestroyShaderModule(*ctx->logical_device, p->pixelModule, NULL); | ||||
ctx->free_fn(p, ctx->malloc_data); | ||||
} // MOJOSHADER_vkDeleteProgram | ||||
MOJOSHADER_vkProgram *MOJOSHADER_vkLinkProgram(MOJOSHADER_vkContext *ctx, | ||||
MOJOSHADER_vkShader *vshader, | ||||
MOJOSHADER_vkShader *pshader) | ||||
{ | ||||
MOJOSHADER_vkProgram *result; | ||||
if ((vshader == NULL) || (pshader == NULL)) // Both shaders MUST exist! | ||||
return NULL; | ||||
result = (MOJOSHADER_vkProgram *) ctx->malloc_fn(sizeof (MOJOSHADER_vkProgram), | ||||
ctx->malloc_data); | ||||
if (result == NULL) | ||||
{ | ||||
out_of_memory(); | ||||
return NULL; | ||||
} // if | ||||
MOJOSHADER_spirv_link_attributes(vshader->parseData, pshader->parseData); | ||||
result->vertexModule = compile_shader(ctx, vshader); | ||||
result->pixelModule = compile_shader(ctx, pshader); | ||||
result->vertexShader = vshader; | ||||
result->pixelShader = pshader; | ||||
if (result->vertexModule == VK_NULL_HANDLE | ||||
|| result->pixelModule == VK_NULL_HANDLE) | ||||
{ | ||||
MOJOSHADER_vkDeleteProgram(ctx, result); | ||||
return NULL; | ||||
} | ||||
return result; | ||||
} // MOJOSHADER_vkLinkProgram | ||||
void MOJOSHADER_vkBindProgram( | ||||
MOJOSHADER_vkContext *ctx, | ||||
MOJOSHADER_vkProgram *p | ||||
) { | ||||
ctx->bound_program = p; | ||||
} // MOJOSHADER_vkBindProgram | ||||
void MOJOSHADER_vkBindShaders( | ||||
MOJOSHADER_vkContext *ctx, | ||||
MOJOSHADER_vkShader *vshader, | ||||
MOJOSHADER_vkShader *pshader | ||||
) { | ||||
if (ctx->linker_cache == NULL) | ||||
{ | ||||
ctx->linker_cache = hash_create(NULL, hash_shaders, match_shaders, | ||||
nuke_shaders, 0, ctx->malloc_fn, | ||||
ctx->free_fn, ctx->malloc_data); | ||||
if (ctx->linker_cache == NULL) | ||||
{ | ||||
out_of_memory(); | ||||
return; | ||||
} // if | ||||
} // if | ||||
MOJOSHADER_vkProgram *program = NULL; | ||||
BoundShaders shaders; | ||||
shaders.vertex = vshader; | ||||
shaders.fragment = pshader; | ||||
ctx->bound_vshader = vshader; | ||||
ctx->bound_pshader = pshader; | ||||
const void *val = NULL; | ||||
if (hash_find(ctx->linker_cache, &shaders, &val)) | ||||
program = (MOJOSHADER_vkProgram *) val; | ||||
else | ||||
{ | ||||
program = MOJOSHADER_vkLinkProgram(ctx, vshader, pshader); | ||||
if (program == NULL) | ||||
return; | ||||
BoundShaders *item = (BoundShaders *) ctx->malloc_fn(sizeof (BoundShaders), | ||||
ctx->malloc_data); | ||||
if (item == NULL) | ||||
{ | ||||
MOJOSHADER_vkDeleteProgram(ctx, program); | ||||
return; | ||||
} // if | ||||
memcpy(item, &shaders, sizeof (BoundShaders)); | ||||
if (hash_insert(ctx->linker_cache, item, program) != 1) | ||||
{ | ||||
ctx->free_fn(item, ctx->malloc_data); | ||||
MOJOSHADER_vkDeleteProgram(ctx, program); | ||||
out_of_memory(); | ||||
return; | ||||
} // if | ||||
} // else | ||||
assert(program != NULL); | ||||
ctx->bound_program = program; | ||||
} // MOJOSHADER_vkBindShaders | ||||
void MOJOSHADER_vkGetBoundShaders( | ||||
MOJOSHADER_vkContext *ctx, | ||||
MOJOSHADER_vkShader **vshader, | ||||
MOJOSHADER_vkShader **pshader | ||||
) { | ||||
if (vshader != NULL) | ||||
{ | ||||
if (ctx->bound_program != NULL) | ||||
*vshader = ctx->bound_program->vertexShader; | ||||
else | ||||
*vshader = ctx->bound_vshader; // In case a pshader isn't set yet | ||||
} // if | ||||
if (pshader != NULL) | ||||
{ | ||||
if (ctx->bound_program != NULL) | ||||
*pshader = ctx->bound_program->pixelShader; | ||||
else | ||||
*pshader = ctx->bound_pshader; // In case a vshader isn't set yet | ||||
} // if | ||||
} // MOJOSHADER_vkGetBoundShaders | ||||
void MOJOSHADER_vkMapUniformBufferMemory( | ||||
MOJOSHADER_vkContext *ctx, | ||||
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(MOJOSHADER_vkContext *ctx) | ||||
{ | ||||
if (ctx->bound_program == NULL) | ||||
return; // Ignore buffer updates until we have a real program linked | ||||
update_uniform_buffer(ctx, ctx->bound_program->vertexShader); | ||||
update_uniform_buffer(ctx, ctx->bound_program->pixelShader); | ||||
} // MOJOSHADER_vkUnmapUniformBufferMemory | ||||
void MOJOSHADER_vkGetUniformBuffers( | ||||
MOJOSHADER_vkContext *ctx, | ||||
VkBuffer *vbuf, unsigned long long *voff, unsigned long long *vsize, | ||||
VkBuffer *pbuf, unsigned long long *poff, unsigned long long *psize | ||||
) { | ||||
assert(ctx->bound_program != NULL); | ||||
*vbuf = get_uniform_buffer(ctx, ctx->bound_program->vertexShader); | ||||
*voff = get_uniform_offset(ctx, ctx->bound_program->vertexShader); | ||||
*vsize = get_uniform_size(ctx, ctx->bound_program->vertexShader); | ||||
*pbuf = get_uniform_buffer(ctx, ctx->bound_program->pixelShader); | ||||
*poff = get_uniform_offset(ctx, ctx->bound_program->pixelShader); | ||||
*psize = get_uniform_size(ctx, ctx->bound_program->pixelShader); | ||||
} // MOJOSHADER_vkGetUniformBuffers | ||||
void MOJOSHADER_vkEndFrame(MOJOSHADER_vkContext *ctx) | ||||
{ | ||||
ctx->frameIndex = (ctx->frameIndex + 1) % 2; | ||||
// Reset counters | ||||
// Offset by size of buffer to simulate "rotating" the buffers | ||||
ctx->vertUboBuffer->dynamicOffset = UBO_BUFFER_SIZE * ctx->frameIndex; | ||||
ctx->vertUboBuffer->currentBlockIncrement = 0; | ||||
ctx->fragUboBuffer->dynamicOffset = UBO_BUFFER_SIZE * ctx->frameIndex; | ||||
ctx->fragUboBuffer->currentBlockIncrement = 0; | ||||
} // 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 | ||||
void MOJOSHADER_vkGetShaderModules( | ||||
MOJOSHADER_vkContext *ctx, | ||||
VkShaderModule *vmodule, | ||||
VkShaderModule *pmodule | ||||
) { | ||||
assert(ctx->bound_program != NULL); | ||||
if (vmodule != NULL) | ||||
*vmodule = ctx->bound_program->vertexModule; | ||||
if (pmodule != NULL) | ||||
*pmodule = ctx->bound_program->pixelModule; | ||||
} //MOJOSHADER_vkGetShaderModules | ||||
const char *MOJOSHADER_vkGetError(MOJOSHADER_vkContext *ctx) | ||||
{ | ||||
return error_buffer; | ||||
} // MOJOSHADER_vkGetError | ||||
#endif /* SUPPORT_PROFILE_SPIRV */ | ||||
// end of mojoshader_vulkan.c ... | ||||