/** * 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 #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 ...