Show More
Commit Description:
Various UI improvements.
Commit Description:
Various UI improvements.
References:
File last commit:
Show/Diff file:
Action:
FNA/lib/MojoShader/mojoshader_metal.c
1002 lines | 32.7 KiB | text/x-c | CLexer
1002 lines | 32.7 KiB | text/x-c | CLexer
r0 | /** | |||
* 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. | ||||
*/ | ||||
#if (defined(__APPLE__) && defined(__MACH__)) | ||||
#define PLATFORM_APPLE 1 | ||||
#include "TargetConditionals.h" | ||||
#define OBJC_OLD_DISPATCH_PROTOTYPES 1 | ||||
#include <objc/message.h> | ||||
#define objc_msgSend_STR ((void* (*)(void*, void*, const char*))objc_msgSend) | ||||
#define objc_msgSend_PTR ((void* (*)(void*, void*, void*))objc_msgSend) | ||||
#define objc_msgSend_INT_PTR ((void* (*)(void*, void*, int, void*))objc_msgSend) | ||||
#define objc_msgSend_PTR_PTR_PTR ((void* (*)(void*, void*, void*, void*, void*))objc_msgSend) | ||||
#endif /* (defined(__APPLE__) && defined(__MACH__)) */ | ||||
#define __MOJOSHADER_INTERNAL__ 1 | ||||
#include "mojoshader_internal.h" | ||||
typedef struct MOJOSHADER_mtlUniformBuffer MOJOSHADER_mtlUniformBuffer; | ||||
typedef struct MOJOSHADER_mtlShader | ||||
{ | ||||
const MOJOSHADER_parseData *parseData; | ||||
MOJOSHADER_mtlUniformBuffer *ubo; | ||||
void *library; // MTLLibrary* | ||||
int numInternalBuffers; | ||||
} MOJOSHADER_mtlShader; | ||||
// 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 | ||||
// profile-specific implementations... | ||||
#if SUPPORT_PROFILE_METAL && PLATFORM_APPLE | ||||
#ifdef MOJOSHADER_EFFECT_SUPPORT | ||||
/* Structs */ | ||||
typedef struct MOJOSHADER_mtlEffect | ||||
{ | ||||
MOJOSHADER_effect *effect; | ||||
unsigned int num_shaders; | ||||
MOJOSHADER_mtlShader *shaders; | ||||
unsigned int *shader_indices; | ||||
unsigned int num_preshaders; | ||||
unsigned int *preshader_indices; | ||||
MOJOSHADER_mtlShader *current_vert; | ||||
MOJOSHADER_mtlShader *current_frag; | ||||
MOJOSHADER_effectShader *current_vert_raw; | ||||
MOJOSHADER_effectShader *current_frag_raw; | ||||
MOJOSHADER_mtlShader *prev_vert; | ||||
MOJOSHADER_mtlShader *prev_frag; | ||||
void *library; // MTLLibrary* | ||||
} MOJOSHADER_mtlEffect; | ||||
typedef struct MOJOSHADER_mtlUniformBuffer | ||||
{ | ||||
void *device; // MTLDevice* | ||||
int bufferSize; | ||||
int numInternalBuffers; | ||||
void **internalBuffers; // MTLBuffer* | ||||
int internalBufferSize; | ||||
int internalOffset; | ||||
int currentFrame; | ||||
int alreadyWritten; | ||||
} MOJOSHADER_mtlUniformBuffer; | ||||
typedef struct MOJOSHADER_mtlShaderState | ||||
{ | ||||
MOJOSHADER_mtlShader *vertexShader; | ||||
MOJOSHADER_mtlShader *fragmentShader; | ||||
void *vertexUniformBuffer; // MTLBuffer* | ||||
void *fragmentUniformBuffer; // MTLBuffer* | ||||
int vertexUniformOffset; | ||||
int fragmentUniformOffset; | ||||
} MOJOSHADER_mtlShaderState; | ||||
/* Objective-C selector references */ | ||||
static void *classNSString = NULL; | ||||
static void *selAlloc = NULL; | ||||
static void *selInitWithUTF8String = NULL; | ||||
static void *selUTF8String = NULL; | ||||
static void *selLength = NULL; | ||||
static void *selContents = NULL; | ||||
static void *selNewBufferWithLength = NULL; | ||||
static void *selRelease = NULL; | ||||
static void *selNewLibraryWithSource = NULL; | ||||
static void *selLocalizedDescription = NULL; | ||||
static void *selNewFunctionWithName = NULL; | ||||
static void *selRetain = NULL; | ||||
/* Helper functions */ | ||||
static void initSelectors(void) | ||||
{ | ||||
classNSString = (void*) objc_getClass("NSString"); | ||||
selAlloc = sel_registerName("alloc"); | ||||
selInitWithUTF8String = sel_registerName("initWithUTF8String:"); | ||||
selUTF8String = sel_registerName("UTF8String"); | ||||
selLength = sel_registerName("length"); | ||||
selContents = sel_registerName("contents"); | ||||
selNewBufferWithLength = sel_registerName("newBufferWithLength:options:"); | ||||
selRelease = sel_registerName("release"); | ||||
selNewLibraryWithSource = sel_registerName("newLibraryWithSource:options:error:"); | ||||
selLocalizedDescription = sel_registerName("localizedDescription"); | ||||
selNewFunctionWithName = sel_registerName("newFunctionWithName:"); | ||||
selRetain = sel_registerName("retain"); | ||||
} // initSelectors | ||||
static void *cstr_to_nsstr(const char *str) | ||||
{ | ||||
return objc_msgSend_STR( | ||||
objc_msgSend(classNSString, selAlloc), | ||||
selInitWithUTF8String, | ||||
str | ||||
); | ||||
} // cstr_to_nsstr | ||||
static const char *nsstr_to_cstr(void *str) | ||||
{ | ||||
return (char *) objc_msgSend(str, selUTF8String); | ||||
} // nssstr_to_cstr | ||||
/* Linked list */ | ||||
typedef struct LLNODE { | ||||
MOJOSHADER_mtlUniformBuffer *data; | ||||
struct LLNODE *next; | ||||
} LLNODE; | ||||
static LLNODE *LL_append_node(LLNODE **baseNode, | ||||
MOJOSHADER_malloc m, | ||||
void *d) | ||||
{ | ||||
LLNODE *prev = NULL; | ||||
LLNODE *node = *baseNode; | ||||
/* Append a node to the linked list. */ | ||||
while (node != NULL) | ||||
{ | ||||
prev = node; | ||||
node = node->next; | ||||
} // while | ||||
node = m(sizeof(LLNODE), d); | ||||
node->next = NULL; | ||||
/* Connect the old to the new. */ | ||||
if (prev != NULL) | ||||
prev->next = node; | ||||
/* Special case for the first node. */ | ||||
if (*baseNode == NULL) | ||||
*baseNode = node; | ||||
return node; | ||||
} // LL_append_node | ||||
static void LL_remove_node(LLNODE **baseNode, | ||||
MOJOSHADER_mtlUniformBuffer *data, | ||||
MOJOSHADER_free f, | ||||
void *d) | ||||
{ | ||||
LLNODE *prev = NULL; | ||||
LLNODE *node = *baseNode; | ||||
/* Search for node with matching data pointer. */ | ||||
while (node != NULL && node->data != data) | ||||
{ | ||||
prev = node; | ||||
node = node->next; | ||||
} // while | ||||
if (node == NULL) | ||||
{ | ||||
/* This should never happen. */ | ||||
assert(0); | ||||
} // if | ||||
/* Clear data pointer. The data must be freed separately. */ | ||||
node->data = NULL; | ||||
/* Connect the old to the new. */ | ||||
if (prev != NULL) | ||||
prev->next = node->next; | ||||
/* Special cases where the first node is removed. */ | ||||
if (prev == NULL) | ||||
*baseNode = (node->next != NULL) ? node->next : NULL; | ||||
/* Free the node! */ | ||||
f(node, d); | ||||
} // LL_remove_node | ||||
/* Internal register utilities */ | ||||
// 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 | ||||
// 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]; | ||||
int vs_reg_file_i[MAX_REG_FILE_I * 4]; | ||||
uint8 vs_reg_file_b[MAX_REG_FILE_B]; | ||||
float ps_reg_file_f[MAX_REG_FILE_F * 4]; | ||||
int ps_reg_file_i[MAX_REG_FILE_I * 4]; | ||||
uint8 ps_reg_file_b[MAX_REG_FILE_B]; | ||||
static inline void copy_parameter_data(MOJOSHADER_effectParam *params, | ||||
unsigned int *param_loc, | ||||
MOJOSHADER_symbol *symbols, | ||||
unsigned int symbol_count, | ||||
float *regf, int *regi, uint8 *regb) | ||||
{ | ||||
int i, j, r, c; | ||||
i = 0; | ||||
for (i = 0; i < symbol_count; i++) | ||||
{ | ||||
const MOJOSHADER_symbol *sym = &symbols[i]; | ||||
const MOJOSHADER_effectValue *param = ¶ms[param_loc[i]].value; | ||||
// float/int registers are vec4, so they have 4 elements each | ||||
const uint32 start = sym->register_index << 2; | ||||
if (param->type.parameter_type == MOJOSHADER_SYMTYPE_FLOAT) | ||||
memcpy(regf + start, param->valuesF, sym->register_count << 4); | ||||
else if (sym->register_set == MOJOSHADER_SYMREGSET_FLOAT4) | ||||
{ | ||||
// Structs are a whole different world... | ||||
if (param->type.parameter_class == MOJOSHADER_SYMCLASS_STRUCT) | ||||
memcpy(regf + start, param->valuesF, sym->register_count << 4); | ||||
else | ||||
{ | ||||
// Sometimes int/bool parameters get thrown into float registers... | ||||
j = 0; | ||||
do | ||||
{ | ||||
c = 0; | ||||
do | ||||
{ | ||||
regf[start + (j << 2) + c] = (float) param->valuesI[(j << 2) + c]; | ||||
} while (++c < param->type.columns); | ||||
} while (++j < sym->register_count); | ||||
} // else | ||||
} // else if | ||||
else if (sym->register_set == MOJOSHADER_SYMREGSET_INT4) | ||||
memcpy(regi + start, param->valuesI, sym->register_count << 4); | ||||
else if (sym->register_set == MOJOSHADER_SYMREGSET_BOOL) | ||||
{ | ||||
j = 0; | ||||
r = 0; | ||||
do | ||||
{ | ||||
c = 0; | ||||
do | ||||
{ | ||||
// regb is not a vec4, enjoy that 'start' bitshift! -flibit | ||||
regb[(start >> 2) + r + c] = param->valuesI[(j << 2) + c]; | ||||
c++; | ||||
} while (c < param->type.columns && ((r + c) < sym->register_count)); | ||||
r += c; | ||||
j++; | ||||
} while (r < sym->register_count); | ||||
} // else if | ||||
} // for | ||||
} // copy_parameter_data | ||||
/* Uniform buffer utilities */ | ||||
static inline int next_highest_alignment(int n) | ||||
{ | ||||
#if TARGET_OS_IOS || TARGET_OS_TV || TARGET_OS_SIMULATOR | ||||
int align = 16; | ||||
#else | ||||
int align = 256; | ||||
#endif | ||||
return align * ((n + align - 1) / align); | ||||
} // next_highest_alignment | ||||
static int UBO_buffer_length(void *buffer) | ||||
{ | ||||
return (int) objc_msgSend(buffer, selLength); | ||||
} // UBO_buffer_length | ||||
static void *UBO_buffer_contents(void *buffer) | ||||
{ | ||||
return (void *) objc_msgSend(buffer, selContents); | ||||
} // UBO_buffer_contents | ||||
static void *UBO_create_backing_buffer(MOJOSHADER_mtlUniformBuffer *ubo, int f) | ||||
{ | ||||
void *oldBuffer = ubo->internalBuffers[f]; | ||||
void *newBuffer = objc_msgSend_INT_PTR( | ||||
ubo->device, | ||||
selNewBufferWithLength, | ||||
ubo->internalBufferSize, | ||||
NULL | ||||
); | ||||
if (oldBuffer != NULL) | ||||
{ | ||||
// Copy over data from old buffer | ||||
memcpy( | ||||
UBO_buffer_contents(newBuffer), | ||||
UBO_buffer_contents(oldBuffer), | ||||
UBO_buffer_length(oldBuffer) | ||||
); | ||||
// Free the old buffer | ||||
objc_msgSend(oldBuffer, selRelease); | ||||
} //if | ||||
return newBuffer; | ||||
} // UBO_create_backing_buffer | ||||
static void UBO_predraw(MOJOSHADER_mtlUniformBuffer *ubo) | ||||
{ | ||||
if (!ubo->alreadyWritten) | ||||
{ | ||||
ubo->alreadyWritten = 1; | ||||
return; | ||||
} // if | ||||
ubo->internalOffset += ubo->bufferSize; | ||||
int buflen = UBO_buffer_length(ubo->internalBuffers[ubo->currentFrame]); | ||||
if (ubo->internalOffset >= buflen) | ||||
{ | ||||
// Double capacity when we're out of room | ||||
if (ubo->internalOffset >= ubo->internalBufferSize) | ||||
ubo->internalBufferSize *= 2; | ||||
ubo->internalBuffers[ubo->currentFrame] = | ||||
UBO_create_backing_buffer(ubo, ubo->currentFrame); | ||||
} //if | ||||
} // UBO_predraw | ||||
static void UBO_end_frame(MOJOSHADER_mtlUniformBuffer *ubo) | ||||
{ | ||||
ubo->internalOffset = 0; | ||||
ubo->currentFrame = (ubo->currentFrame + 1) % ubo->numInternalBuffers; | ||||
ubo->alreadyWritten = 0; | ||||
} // UBO_end_frame | ||||
LLNODE *ubos = NULL; /* global linked list of all active UBOs */ | ||||
static MOJOSHADER_mtlUniformBuffer *create_ubo(MOJOSHADER_mtlShader *shader, | ||||
void *mtlDevice, | ||||
MOJOSHADER_malloc m, | ||||
void *d) | ||||
{ | ||||
int uniformCount = shader->parseData->uniform_count; | ||||
if (uniformCount == 0) | ||||
return NULL; | ||||
// Calculate how big we need to make the buffer | ||||
int buflen = 0; | ||||
for (int i = 0; i < uniformCount; i += 1) | ||||
{ | ||||
int arrayCount = shader->parseData->uniforms[i].array_count; | ||||
int uniformSize = 16; | ||||
if (shader->parseData->uniforms[i].type == MOJOSHADER_UNIFORM_BOOL) | ||||
uniformSize = 1; | ||||
buflen += (arrayCount ? arrayCount : 1) * uniformSize; | ||||
} // for | ||||
// Make the UBO | ||||
MOJOSHADER_mtlUniformBuffer *ubo = (MOJOSHADER_mtlUniformBuffer *) m(sizeof(MOJOSHADER_mtlUniformBuffer), d); | ||||
ubo->device = mtlDevice; | ||||
ubo->alreadyWritten = 0; | ||||
ubo->bufferSize = next_highest_alignment(buflen); | ||||
ubo->currentFrame = 0; | ||||
ubo->numInternalBuffers = shader->numInternalBuffers; | ||||
ubo->internalBufferSize = ubo->bufferSize * 16; // pre-allocate some extra room! | ||||
ubo->internalBuffers = m(ubo->numInternalBuffers * sizeof(void*), d); | ||||
ubo->internalOffset = 0; | ||||
for (int i = 0; i < ubo->numInternalBuffers; i++) | ||||
{ | ||||
ubo->internalBuffers[i] = NULL; | ||||
ubo->internalBuffers[i] = UBO_create_backing_buffer(ubo, i); | ||||
} // for | ||||
/* Add the UBO to the global list so it can be updated. */ | ||||
LLNODE *node = LL_append_node(&ubos, m, d); | ||||
node->data = ubo; | ||||
return ubo; | ||||
} // create_ubo | ||||
static void dealloc_ubo(MOJOSHADER_mtlShader *shader, | ||||
MOJOSHADER_free f, | ||||
void* d) | ||||
{ | ||||
if (shader->ubo == NULL) | ||||
return; | ||||
LL_remove_node(&ubos, shader->ubo, f, d); | ||||
for (int i = 0; i < shader->ubo->numInternalBuffers; i++) | ||||
{ | ||||
objc_msgSend(shader->ubo->internalBuffers[i], selRelease); | ||||
shader->ubo->internalBuffers[i] = NULL; | ||||
} // for | ||||
f(shader->ubo->internalBuffers, d); | ||||
f(shader->ubo, d); | ||||
} // dealloc_ubo | ||||
static void *get_uniform_buffer(MOJOSHADER_mtlShader *shader) | ||||
{ | ||||
if (shader == NULL || shader->ubo == NULL) | ||||
return NULL; | ||||
return shader->ubo->internalBuffers[shader->ubo->currentFrame]; | ||||
} // get_uniform_buffer | ||||
static int get_uniform_offset(MOJOSHADER_mtlShader *shader) | ||||
{ | ||||
if (shader == NULL || shader->ubo == NULL) | ||||
return 0; | ||||
return shader->ubo->internalOffset; | ||||
} // get_uniform_offset | ||||
static void update_uniform_buffer(MOJOSHADER_mtlShader *shader) | ||||
{ | ||||
if (shader == NULL || shader->ubo == NULL) | ||||
return; | ||||
float *regF; int *regI; uint8 *regB; | ||||
if (shader->parseData->shader_type == MOJOSHADER_TYPE_VERTEX) | ||||
{ | ||||
regF = vs_reg_file_f; | ||||
regI = vs_reg_file_i; | ||||
regB = vs_reg_file_b; | ||||
} // if | ||||
else | ||||
{ | ||||
regF = ps_reg_file_f; | ||||
regI = ps_reg_file_i; | ||||
regB = ps_reg_file_b; | ||||
} // else | ||||
UBO_predraw(shader->ubo); | ||||
void *buf = shader->ubo->internalBuffers[shader->ubo->currentFrame]; | ||||
void *contents = UBO_buffer_contents(buf) + shader->ubo->internalOffset; | ||||
int offset = 0; | ||||
for (int i = 0; i < shader->parseData->uniform_count; i++) | ||||
{ | ||||
int idx = shader->parseData->uniforms[i].index; | ||||
int arrayCount = shader->parseData->uniforms[i].array_count; | ||||
int size = arrayCount ? arrayCount : 1; | ||||
switch (shader->parseData->uniforms[i].type) | ||||
{ | ||||
case MOJOSHADER_UNIFORM_FLOAT: | ||||
memcpy( | ||||
contents + (offset * 16), | ||||
®F[4 * idx], | ||||
size * 16 | ||||
); | ||||
break; | ||||
case MOJOSHADER_UNIFORM_INT: | ||||
// !!! FIXME: Need a test case | ||||
memcpy( | ||||
contents + (offset * 16), | ||||
®I[4 * idx], | ||||
size * 16 | ||||
); | ||||
break; | ||||
case MOJOSHADER_UNIFORM_BOOL: | ||||
// !!! FIXME: Need a test case | ||||
memcpy( | ||||
contents + offset, | ||||
®B[idx], | ||||
size | ||||
); | ||||
break; | ||||
default: | ||||
assert(0); // This should never happen. | ||||
break; | ||||
} // switch | ||||
offset += size; | ||||
} // for | ||||
} // update_uniform_buffer | ||||
/* Public API */ | ||||
MOJOSHADER_mtlEffect *MOJOSHADER_mtlCompileEffect(MOJOSHADER_effect *effect, | ||||
void *mtlDevice, | ||||
int numBackingBuffers) | ||||
{ | ||||
int i; | ||||
MOJOSHADER_malloc m = effect->malloc; | ||||
MOJOSHADER_free f = effect->free; | ||||
void *d = effect->malloc_data; | ||||
int current_shader = 0; | ||||
int current_preshader = 0; | ||||
int src_len = 0; | ||||
// Make sure the Objective-C selectors have been initialized... | ||||
if (selAlloc == NULL) | ||||
initSelectors(); | ||||
MOJOSHADER_mtlEffect *retval = (MOJOSHADER_mtlEffect *) m(sizeof (MOJOSHADER_mtlEffect), d); | ||||
if (retval == NULL) | ||||
{ | ||||
out_of_memory(); | ||||
return NULL; | ||||
} // if | ||||
memset(retval, '\0', sizeof (MOJOSHADER_mtlEffect)); | ||||
// Count the number of shaders before allocating | ||||
for (i = 0; i < effect->object_count; i++) | ||||
{ | ||||
MOJOSHADER_effectObject *object = &effect->objects[i]; | ||||
if (object->type == MOJOSHADER_SYMTYPE_PIXELSHADER | ||||
|| object->type == MOJOSHADER_SYMTYPE_VERTEXSHADER) | ||||
{ | ||||
if (object->shader.is_preshader) | ||||
retval->num_preshaders++; | ||||
else | ||||
{ | ||||
retval->num_shaders++; | ||||
src_len += object->shader.shader->output_len; | ||||
} // else | ||||
} // if | ||||
} // for | ||||
// Alloc shader source buffer | ||||
char *shader_source = (char *) m(src_len + 1, d); | ||||
memset(shader_source, '\0', src_len + 1); | ||||
int src_pos = 0; | ||||
// Copy all the source text into the buffer | ||||
for (i = 0; i < effect->object_count; i++) | ||||
{ | ||||
MOJOSHADER_effectObject *object = &effect->objects[i]; | ||||
if (object->type == MOJOSHADER_SYMTYPE_PIXELSHADER | ||||
|| object->type == MOJOSHADER_SYMTYPE_VERTEXSHADER) | ||||
{ | ||||
if (!object->shader.is_preshader) | ||||
{ | ||||
int output_len = object->shader.shader->output_len; | ||||
memcpy(&shader_source[src_pos], object->shader.shader->output, output_len); | ||||
src_pos += output_len; | ||||
} // if | ||||
} // if | ||||
} // for | ||||
// Handle texcoord0 -> point_coord conversion | ||||
if (strstr(shader_source, "[[point_size]]")) | ||||
{ | ||||
// !!! FIXME: This assumes all texcoord0 attributes in the effect are | ||||
// !!! FIXME: actually point coords! It ain't necessarily so! -caleb | ||||
const char *repl = "[[ point_coord ]]"; | ||||
char *ptr; | ||||
while ((ptr = strstr(shader_source, "[[user(texcoord0)]]"))) | ||||
{ | ||||
memcpy(ptr, repl, strlen(repl)); | ||||
// float4 -> float2 | ||||
int spaces = 0; | ||||
while (spaces < 2) | ||||
if (*(ptr--) == ' ') | ||||
spaces++; | ||||
memcpy(ptr, "2", sizeof(char)); | ||||
} // while | ||||
} // if | ||||
// Alloc shader information | ||||
retval->shaders = (MOJOSHADER_mtlShader *) m(retval->num_shaders * sizeof (MOJOSHADER_mtlShader), d); | ||||
if (retval->shaders == NULL) | ||||
{ | ||||
f(retval, d); | ||||
out_of_memory(); | ||||
return NULL; | ||||
} // if | ||||
memset(retval->shaders, '\0', retval->num_shaders * sizeof (MOJOSHADER_mtlShader)); | ||||
retval->shader_indices = (unsigned int *) m(retval->num_shaders * sizeof (unsigned int), d); | ||||
if (retval->shader_indices == NULL) | ||||
{ | ||||
f(retval->shaders, d); | ||||
f(retval, d); | ||||
out_of_memory(); | ||||
return NULL; | ||||
} // if | ||||
memset(retval->shader_indices, '\0', retval->num_shaders * sizeof (unsigned int)); | ||||
// Alloc preshader information | ||||
if (retval->num_preshaders > 0) | ||||
{ | ||||
retval->preshader_indices = (unsigned int *) m(retval->num_preshaders * sizeof (unsigned int), d); | ||||
if (retval->preshader_indices == NULL) | ||||
{ | ||||
f(retval->shaders, d); | ||||
f(retval->shader_indices, d); | ||||
f(retval, d); | ||||
out_of_memory(); | ||||
return NULL; | ||||
} // if | ||||
memset(retval->preshader_indices, '\0', retval->num_preshaders * sizeof (unsigned int)); | ||||
} // if | ||||
// Compile the source into a library | ||||
void *compileError = NULL; | ||||
void *shader_source_ns = cstr_to_nsstr(shader_source); | ||||
void *library = objc_msgSend_PTR_PTR_PTR( | ||||
mtlDevice, | ||||
selNewLibraryWithSource, | ||||
shader_source_ns, | ||||
NULL, | ||||
&compileError | ||||
); | ||||
retval->library = library; | ||||
f(shader_source, d); | ||||
objc_msgSend(shader_source_ns, selRelease); | ||||
if (library == NULL) | ||||
{ | ||||
// Set the error | ||||
void *error_nsstr = objc_msgSend(compileError, selLocalizedDescription); | ||||
set_error(nsstr_to_cstr(error_nsstr)); | ||||
goto compile_shader_fail; | ||||
} // if | ||||
// Run through the shaders again, tracking the object indices | ||||
for (i = 0; i < effect->object_count; i++) | ||||
{ | ||||
MOJOSHADER_effectObject *object = &effect->objects[i]; | ||||
if (object->type == MOJOSHADER_SYMTYPE_PIXELSHADER | ||||
|| object->type == MOJOSHADER_SYMTYPE_VERTEXSHADER) | ||||
{ | ||||
if (object->shader.is_preshader) | ||||
{ | ||||
retval->preshader_indices[current_preshader++] = i; | ||||
continue; | ||||
} // if | ||||
MOJOSHADER_mtlShader *curshader = &retval->shaders[current_shader]; | ||||
curshader->parseData = object->shader.shader; | ||||
curshader->numInternalBuffers = numBackingBuffers; | ||||
curshader->ubo = create_ubo(curshader, mtlDevice, m, d); | ||||
curshader->library = library; | ||||
retval->shader_indices[current_shader] = i; | ||||
current_shader++; | ||||
} // if | ||||
} // for | ||||
retval->effect = effect; | ||||
return retval; | ||||
compile_shader_fail: | ||||
f(retval->shader_indices, d); | ||||
f(retval->shaders, d); | ||||
f(retval, d); | ||||
return NULL; | ||||
} // MOJOSHADER_mtlCompileEffect | ||||
void MOJOSHADER_mtlDeleteEffect(MOJOSHADER_mtlEffect *mtlEffect) | ||||
{ | ||||
MOJOSHADER_free f = mtlEffect->effect->free; | ||||
void *d = mtlEffect->effect->malloc_data; | ||||
int i; | ||||
for (i = 0; i < mtlEffect->num_shaders; i++) | ||||
{ | ||||
/* Release the uniform buffers */ | ||||
dealloc_ubo(&mtlEffect->shaders[i], f, d); | ||||
} // for | ||||
/* Release the library */ | ||||
objc_msgSend(mtlEffect->library, selRelease); | ||||
f(mtlEffect->shader_indices, d); | ||||
f(mtlEffect->preshader_indices, d); | ||||
f(mtlEffect, d); | ||||
} // MOJOSHADER_mtlDeleteEffect | ||||
void MOJOSHADER_mtlEffectBegin(MOJOSHADER_mtlEffect *mtlEffect, | ||||
unsigned int *numPasses, | ||||
int saveShaderState, | ||||
MOJOSHADER_effectStateChanges *stateChanges) | ||||
{ | ||||
*numPasses = mtlEffect->effect->current_technique->pass_count; | ||||
mtlEffect->effect->restore_shader_state = saveShaderState; | ||||
mtlEffect->effect->state_changes = stateChanges; | ||||
if (mtlEffect->effect->restore_shader_state) | ||||
{ | ||||
mtlEffect->prev_vert = mtlEffect->current_vert; | ||||
mtlEffect->prev_frag = mtlEffect->current_frag; | ||||
} // if | ||||
} // MOJOSHADER_mtlEffectBegin | ||||
// Predeclare | ||||
void MOJOSHADER_mtlEffectCommitChanges(MOJOSHADER_mtlEffect *mtlEffect, | ||||
MOJOSHADER_mtlShaderState *shState); | ||||
void MOJOSHADER_mtlEffectBeginPass(MOJOSHADER_mtlEffect *mtlEffect, | ||||
unsigned int pass, | ||||
MOJOSHADER_mtlShaderState *shState) | ||||
{ | ||||
int i, j; | ||||
MOJOSHADER_effectPass *curPass; | ||||
MOJOSHADER_effectState *state; | ||||
MOJOSHADER_effectShader *rawVert = mtlEffect->current_vert_raw; | ||||
MOJOSHADER_effectShader *rawFrag = mtlEffect->current_frag_raw; | ||||
int has_preshader = 0; | ||||
assert(shState != NULL); | ||||
assert(mtlEffect->effect->current_pass == -1); | ||||
mtlEffect->effect->current_pass = pass; | ||||
curPass = &mtlEffect->effect->current_technique->passes[pass]; | ||||
// !!! FIXME: I bet this could be stored at parse/compile time. -flibit | ||||
for (i = 0; i < curPass->state_count; i++) | ||||
{ | ||||
state = &curPass->states[i]; | ||||
#define ASSIGN_SHADER(stype, raw, mtls) \ | ||||
(state->type == stype) \ | ||||
{ \ | ||||
j = 0; \ | ||||
do \ | ||||
{ \ | ||||
if (*state->value.valuesI == mtlEffect->shader_indices[j]) \ | ||||
{ \ | ||||
raw = &mtlEffect->effect->objects[*state->value.valuesI].shader; \ | ||||
mtlEffect->mtls = &mtlEffect->shaders[j]; \ | ||||
break; \ | ||||
} \ | ||||
else if (mtlEffect->num_preshaders > 0 \ | ||||
&& *state->value.valuesI == mtlEffect->preshader_indices[j]) \ | ||||
{ \ | ||||
raw = &mtlEffect->effect->objects[*state->value.valuesI].shader; \ | ||||
has_preshader = 1; \ | ||||
break; \ | ||||
} \ | ||||
} while (++j < mtlEffect->num_shaders); \ | ||||
} | ||||
if ASSIGN_SHADER(MOJOSHADER_RS_VERTEXSHADER, rawVert, current_vert) | ||||
else if ASSIGN_SHADER(MOJOSHADER_RS_PIXELSHADER, rawFrag, current_frag) | ||||
#undef ASSIGN_SHADER | ||||
} // for | ||||
mtlEffect->effect->state_changes->render_state_changes = curPass->states; | ||||
mtlEffect->effect->state_changes->render_state_change_count = curPass->state_count; | ||||
mtlEffect->current_vert_raw = rawVert; | ||||
mtlEffect->current_frag_raw = rawFrag; | ||||
/* If this effect pass has an array of shaders, we get to wait until | ||||
* CommitChanges to actually bind the final shaders. | ||||
* -flibit | ||||
*/ | ||||
if (!has_preshader) | ||||
{ | ||||
if (mtlEffect->current_vert != NULL) | ||||
{ | ||||
MOJOSHADER_mtlShader *vert = mtlEffect->current_vert; | ||||
shState->vertexShader = vert; | ||||
shState->vertexUniformBuffer = get_uniform_buffer(vert); | ||||
shState->vertexUniformOffset = get_uniform_offset(vert); | ||||
} // if | ||||
if (mtlEffect->current_frag != NULL) | ||||
{ | ||||
MOJOSHADER_mtlShader *frag = mtlEffect->current_frag; | ||||
shState->fragmentShader = frag; | ||||
shState->fragmentUniformBuffer = get_uniform_buffer(frag); | ||||
shState->fragmentUniformOffset = get_uniform_offset(frag); | ||||
} // if | ||||
if (mtlEffect->current_vert_raw != NULL) | ||||
{ | ||||
mtlEffect->effect->state_changes->vertex_sampler_state_changes = rawVert->samplers; | ||||
mtlEffect->effect->state_changes->vertex_sampler_state_change_count = rawVert->sampler_count; | ||||
} // if | ||||
if (mtlEffect->current_frag_raw != NULL) | ||||
{ | ||||
mtlEffect->effect->state_changes->sampler_state_changes = rawFrag->samplers; | ||||
mtlEffect->effect->state_changes->sampler_state_change_count = rawFrag->sampler_count; | ||||
} // if | ||||
} // if | ||||
MOJOSHADER_mtlEffectCommitChanges(mtlEffect, shState); | ||||
} // MOJOSHADER_mtlEffectBeginPass | ||||
void MOJOSHADER_mtlEffectCommitChanges(MOJOSHADER_mtlEffect *mtlEffect, | ||||
MOJOSHADER_mtlShaderState *shState) | ||||
{ | ||||
MOJOSHADER_effectShader *rawVert = mtlEffect->current_vert_raw; | ||||
MOJOSHADER_effectShader *rawFrag = mtlEffect->current_frag_raw; | ||||
/* Used for shader selection from preshaders */ | ||||
int i, j; | ||||
MOJOSHADER_effectValue *param; | ||||
float selector; | ||||
int shader_object; | ||||
int selector_ran = 0; | ||||
/* For effect passes with arrays of shaders, we have to run a preshader | ||||
* that determines which shader to use, based on a parameter's value. | ||||
* -flibit | ||||
*/ | ||||
// !!! FIXME: We're just running the preshaders every time. Blech. -flibit | ||||
#define SELECT_SHADER_FROM_PRESHADER(raw, mtls) \ | ||||
if (raw != NULL && raw->is_preshader) \ | ||||
{ \ | ||||
i = 0; \ | ||||
do \ | ||||
{ \ | ||||
param = &mtlEffect->effect->params[raw->preshader_params[i]].value; \ | ||||
for (j = 0; j < (param->value_count >> 2); j++) \ | ||||
memcpy(raw->preshader->registers + raw->preshader->symbols[i].register_index + j, \ | ||||
param->valuesI + (j << 2), \ | ||||
param->type.columns << 2); \ | ||||
} while (++i < raw->preshader->symbol_count); \ | ||||
MOJOSHADER_runPreshader(raw->preshader, &selector); \ | ||||
shader_object = mtlEffect->effect->params[raw->params[0]].value.valuesI[(int) selector]; \ | ||||
raw = &mtlEffect->effect->objects[shader_object].shader; \ | ||||
i = 0; \ | ||||
do \ | ||||
{ \ | ||||
if (shader_object == mtlEffect->shader_indices[i]) \ | ||||
{ \ | ||||
mtls = &mtlEffect->shaders[i]; \ | ||||
break; \ | ||||
} \ | ||||
} while (++i < mtlEffect->num_shaders); \ | ||||
selector_ran = 1; \ | ||||
} | ||||
SELECT_SHADER_FROM_PRESHADER(rawVert, mtlEffect->current_vert) | ||||
SELECT_SHADER_FROM_PRESHADER(rawFrag, mtlEffect->current_frag) | ||||
#undef SELECT_SHADER_FROM_PRESHADER | ||||
if (selector_ran) | ||||
{ | ||||
if (mtlEffect->current_vert != NULL) | ||||
shState->vertexShader = mtlEffect->current_vert; | ||||
if (mtlEffect->current_frag != NULL) | ||||
shState->fragmentShader = mtlEffect->current_frag; | ||||
if (mtlEffect->current_vert_raw != NULL) | ||||
{ | ||||
mtlEffect->effect->state_changes->vertex_sampler_state_changes = rawVert->samplers; | ||||
mtlEffect->effect->state_changes->vertex_sampler_state_change_count = rawVert->sampler_count; | ||||
} // if | ||||
if (mtlEffect->current_frag_raw != NULL) | ||||
{ | ||||
mtlEffect->effect->state_changes->sampler_state_changes = rawFrag->samplers; | ||||
mtlEffect->effect->state_changes->sampler_state_change_count = rawFrag->sampler_count; | ||||
} // if | ||||
} // if | ||||
/* This is where parameters are copied into the constant buffers. | ||||
* If you're looking for where things slow down immensely, look at | ||||
* the copy_parameter_data() and MOJOSHADER_runPreshader() functions. | ||||
* -flibit | ||||
*/ | ||||
// !!! FIXME: We're just copying everything every time. Blech. -flibit | ||||
// !!! FIXME: We're just running the preshaders every time. Blech. -flibit | ||||
// !!! FIXME: Will the preshader ever want int/bool registers? -flibit | ||||
#define COPY_PARAMETER_DATA(raw, stage) \ | ||||
if (raw != NULL) \ | ||||
{ \ | ||||
copy_parameter_data(mtlEffect->effect->params, raw->params, \ | ||||
raw->shader->symbols, \ | ||||
raw->shader->symbol_count, \ | ||||
stage##_reg_file_f, \ | ||||
stage##_reg_file_i, \ | ||||
stage##_reg_file_b); \ | ||||
if (raw->shader->preshader) \ | ||||
{ \ | ||||
copy_parameter_data(mtlEffect->effect->params, raw->preshader_params, \ | ||||
raw->shader->preshader->symbols, \ | ||||
raw->shader->preshader->symbol_count, \ | ||||
raw->shader->preshader->registers, \ | ||||
NULL, \ | ||||
NULL); \ | ||||
MOJOSHADER_runPreshader(raw->shader->preshader, stage##_reg_file_f); \ | ||||
} \ | ||||
} | ||||
COPY_PARAMETER_DATA(rawVert, vs) | ||||
COPY_PARAMETER_DATA(rawFrag, ps) | ||||
#undef COPY_PARAMETER_DATA | ||||
update_uniform_buffer(shState->vertexShader); | ||||
shState->vertexUniformBuffer = get_uniform_buffer(shState->vertexShader); | ||||
shState->vertexUniformOffset = get_uniform_offset(shState->vertexShader); | ||||
update_uniform_buffer(shState->fragmentShader); | ||||
shState->fragmentUniformBuffer = get_uniform_buffer(shState->fragmentShader); | ||||
shState->fragmentUniformOffset = get_uniform_offset(shState->fragmentShader); | ||||
} // MOJOSHADER_mtlEffectCommitChanges | ||||
void MOJOSHADER_mtlEffectEndPass(MOJOSHADER_mtlEffect *mtlEffect) | ||||
{ | ||||
assert(mtlEffect->effect->current_pass != -1); | ||||
mtlEffect->effect->current_pass = -1; | ||||
} // MOJOSHADER_mtlEffectEndPass | ||||
void MOJOSHADER_mtlEffectEnd(MOJOSHADER_mtlEffect *mtlEffect, | ||||
MOJOSHADER_mtlShaderState *shState) | ||||
{ | ||||
if (mtlEffect->effect->restore_shader_state) | ||||
{ | ||||
mtlEffect->effect->restore_shader_state = 0; | ||||
shState->vertexShader = mtlEffect->prev_vert; | ||||
shState->fragmentShader = mtlEffect->prev_frag; | ||||
shState->vertexUniformBuffer = get_uniform_buffer(mtlEffect->prev_vert); | ||||
shState->fragmentUniformBuffer = get_uniform_buffer(mtlEffect->prev_frag); | ||||
shState->vertexUniformOffset = get_uniform_offset(mtlEffect->prev_vert); | ||||
shState->fragmentUniformOffset = get_uniform_offset(mtlEffect->prev_frag); | ||||
} // if | ||||
mtlEffect->effect->state_changes = NULL; | ||||
} // MOJOSHADER_mtlEffectEnd | ||||
void *MOJOSHADER_mtlGetFunctionHandle(MOJOSHADER_mtlShader *shader) | ||||
{ | ||||
if (shader == NULL) | ||||
return NULL; | ||||
void *fnname = cstr_to_nsstr(shader->parseData->mainfn); | ||||
void *ret = objc_msgSend_PTR( | ||||
shader->library, | ||||
selNewFunctionWithName, | ||||
fnname | ||||
); | ||||
objc_msgSend(fnname, selRelease); | ||||
objc_msgSend(ret, selRetain); | ||||
return ret; | ||||
} // MOJOSHADER_mtlGetFunctionHandle | ||||
void MOJOSHADER_mtlEndFrame() | ||||
{ | ||||
LLNODE *node = ubos; | ||||
while (node != NULL) | ||||
{ | ||||
UBO_end_frame((MOJOSHADER_mtlUniformBuffer *) node->data); | ||||
node = node->next; | ||||
} // while | ||||
} // MOJOSHADER_mtlEndFrame | ||||
int MOJOSHADER_mtlGetVertexAttribLocation(MOJOSHADER_mtlShader *vert, | ||||
MOJOSHADER_usage usage, int index) | ||||
{ | ||||
if (vert == NULL) | ||||
return -1; | ||||
for (int 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, couldn't find requested attribute | ||||
return -1; | ||||
} // MOJOSHADER_mtlGetVertexAttribLocation | ||||
const char *MOJOSHADER_mtlGetError(void) | ||||
{ | ||||
return error_buffer; | ||||
} // MOJOSHADER_mtlGetError | ||||
#endif /* MOJOSHADER_EFFECT_SUPPORT */ | ||||
#endif /* SUPPORT_PROFILE_METAL && PLATFORM_APPLE */ | ||||
// end of mojoshader_metal.c ... | ||||