Show More
Commit Description:
Add timers for Simulation and various engines...
Commit Description:
Add timers for Simulation and various engines Starting to add additional timers for different stages of the process of updating in order to get more insight into what is slowing it down. The update takes 9ms, which is much longer than it used to. Engine-specific timers are coming later.
File last commit:
Show/Diff file:
Action:
FNA/lib/MojoShader/mojoshader_metal.c
1002 lines | 32.7 KiB | text/x-c | CLexer
/**
* 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 = &params[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),
&regF[4 * idx],
size * 16
);
break;
case MOJOSHADER_UNIFORM_INT:
// !!! FIXME: Need a test case
memcpy(
contents + (offset * 16),
&regI[4 * idx],
size * 16
);
break;
case MOJOSHADER_UNIFORM_BOOL:
// !!! FIXME: Need a test case
memcpy(
contents + offset,
&regB[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 ...