--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mojoshader_metal.c Sun Jan 12 11:39:49 2020 -0500
@@ -0,0 +1,1002 @@
+/**
+ * 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 ...