Show More
Commit Description:
Various UI improvements.
Commit Description:
Various UI improvements.
References:
File last commit:
Show/Diff file:
Action:
FNA/lib/MojoShader/profiles/mojoshader_profile_common.c
508 lines | 13.8 KiB | text/x-c | CLexer
508 lines | 13.8 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. | ||||
*/ | ||||
#define __MOJOSHADER_INTERNAL__ 1 | ||||
#include "mojoshader_profile.h" | ||||
#pragma GCC visibility push(hidden) | ||||
// Common Utilities | ||||
void out_of_memory(Context *ctx) | ||||
{ | ||||
ctx->isfail = ctx->out_of_memory = 1; | ||||
} // out_of_memory | ||||
void *Malloc(Context *ctx, const size_t len) | ||||
{ | ||||
void *retval = ctx->malloc((int) len, ctx->malloc_data); | ||||
if (retval == NULL) | ||||
out_of_memory(ctx); | ||||
return retval; | ||||
} // Malloc | ||||
char *StrDup(Context *ctx, const char *str) | ||||
{ | ||||
char *retval = (char *) Malloc(ctx, strlen(str) + 1); | ||||
if (retval != NULL) | ||||
strcpy(retval, str); | ||||
return retval; | ||||
} // StrDup | ||||
void Free(Context *ctx, void *ptr) | ||||
{ | ||||
ctx->free(ptr, ctx->malloc_data); | ||||
} // Free | ||||
void * MOJOSHADERCALL MallocBridge(int bytes, void *data) | ||||
{ | ||||
return Malloc((Context *) data, (size_t) bytes); | ||||
} // MallocBridge | ||||
void MOJOSHADERCALL FreeBridge(void *ptr, void *data) | ||||
{ | ||||
Free((Context *) data, ptr); | ||||
} // FreeBridge | ||||
// Jump between output sections in the context... | ||||
int set_output(Context *ctx, Buffer **section) | ||||
{ | ||||
// only create output sections on first use. | ||||
if (*section == NULL) | ||||
{ | ||||
*section = buffer_create(256, MallocBridge, FreeBridge, ctx); | ||||
if (*section == NULL) | ||||
return 0; | ||||
} // if | ||||
ctx->output = *section; | ||||
return 1; | ||||
} // set_output | ||||
void push_output(Context *ctx, Buffer **section) | ||||
{ | ||||
assert(ctx->output_stack_len < (int) (STATICARRAYLEN(ctx->output_stack))); | ||||
ctx->output_stack[ctx->output_stack_len] = ctx->output; | ||||
ctx->indent_stack[ctx->output_stack_len] = ctx->indent; | ||||
ctx->output_stack_len++; | ||||
if (!set_output(ctx, section)) | ||||
return; | ||||
ctx->indent = 0; | ||||
} // push_output | ||||
void pop_output(Context *ctx) | ||||
{ | ||||
assert(ctx->output_stack_len > 0); | ||||
ctx->output_stack_len--; | ||||
ctx->output = ctx->output_stack[ctx->output_stack_len]; | ||||
ctx->indent = ctx->indent_stack[ctx->output_stack_len]; | ||||
} // pop_output | ||||
// Shader model version magic... | ||||
uint32 ver_ui32(const uint8 major, const uint8 minor) | ||||
{ | ||||
return ( (((uint32) major) << 16) | (((minor) == 0xFF) ? 1 : (minor)) ); | ||||
} // version_ui32 | ||||
int shader_version_supported(const uint8 maj, const uint8 min) | ||||
{ | ||||
return (ver_ui32(maj,min) <= ver_ui32(MAX_SHADER_MAJOR, MAX_SHADER_MINOR)); | ||||
} // shader_version_supported | ||||
int shader_version_atleast(const Context *ctx, const uint8 maj, | ||||
const uint8 min) | ||||
{ | ||||
return (ver_ui32(ctx->major_ver, ctx->minor_ver) >= ver_ui32(maj, min)); | ||||
} // shader_version_atleast | ||||
int shader_version_exactly(const Context *ctx, const uint8 maj, | ||||
const uint8 min) | ||||
{ | ||||
return ((ctx->major_ver == maj) && (ctx->minor_ver == min)); | ||||
} // shader_version_exactly | ||||
int shader_is_pixel(const Context *ctx) | ||||
{ | ||||
return (ctx->shader_type == MOJOSHADER_TYPE_PIXEL); | ||||
} // shader_is_pixel | ||||
int shader_is_vertex(const Context *ctx) | ||||
{ | ||||
return (ctx->shader_type == MOJOSHADER_TYPE_VERTEX); | ||||
} // shader_is_vertex | ||||
// Fail... | ||||
int isfail(const Context *ctx) | ||||
{ | ||||
return ctx->isfail; | ||||
} // isfail | ||||
void failf(Context *ctx, const char *fmt, ...) ISPRINTF(2,3); | ||||
void failf(Context *ctx, const char *fmt, ...) | ||||
{ | ||||
ctx->isfail = 1; | ||||
if (ctx->out_of_memory) | ||||
return; | ||||
// no filename at this level (we pass a NULL to errorlist_add_va()...) | ||||
va_list ap; | ||||
va_start(ap, fmt); | ||||
errorlist_add_va(ctx->errors, NULL, ctx->current_position, fmt, ap); | ||||
va_end(ap); | ||||
} // failf | ||||
void fail(Context *ctx, const char *reason) | ||||
{ | ||||
failf(ctx, "%s", reason); | ||||
} // fail | ||||
// Output Lines... | ||||
void output_line(Context *ctx, const char *fmt, ...) ISPRINTF(2,3); | ||||
void output_line(Context *ctx, const char *fmt, ...) | ||||
{ | ||||
assert(ctx->output != NULL); | ||||
if (isfail(ctx)) | ||||
return; // we failed previously, don't go on... | ||||
const int indent = ctx->indent; | ||||
if (indent > 0) | ||||
{ | ||||
char *indentbuf = (char *) alloca(indent); | ||||
memset(indentbuf, '\t', indent); | ||||
buffer_append(ctx->output, indentbuf, indent); | ||||
} // if | ||||
va_list ap; | ||||
va_start(ap, fmt); | ||||
buffer_append_va(ctx->output, fmt, ap); | ||||
va_end(ap); | ||||
buffer_append(ctx->output, ctx->endline, ctx->endline_len); | ||||
} // output_line | ||||
void output_blank_line(Context *ctx) | ||||
{ | ||||
assert(ctx->output != NULL); | ||||
if (!isfail(ctx)) | ||||
buffer_append(ctx->output, ctx->endline, ctx->endline_len); | ||||
} // output_blank_line | ||||
// !!! FIXME: this is sort of nasty. | ||||
void floatstr(Context *ctx, char *buf, size_t bufsize, float f, | ||||
int leavedecimal) | ||||
{ | ||||
const size_t len = MOJOSHADER_printFloat(buf, bufsize, f); | ||||
if ((len+2) >= bufsize) | ||||
fail(ctx, "BUG: internal buffer is too small"); | ||||
else | ||||
{ | ||||
char *end = buf + len; | ||||
char *ptr = strchr(buf, '.'); | ||||
if (ptr == NULL) | ||||
{ | ||||
if (leavedecimal) | ||||
strcat(buf, ".0"); | ||||
return; // done. | ||||
} // if | ||||
while (--end != ptr) | ||||
{ | ||||
if (*end != '0') | ||||
{ | ||||
end++; | ||||
break; | ||||
} // if | ||||
} // while | ||||
if ((leavedecimal) && (end == ptr)) | ||||
end += 2; | ||||
*end = '\0'; // chop extra '0' or all decimal places off. | ||||
} // else | ||||
} // floatstr | ||||
// Deal with register lists... | ||||
static inline uint32 reg_to_ui32(const RegisterType regtype, const int regnum) | ||||
{ | ||||
return ( ((uint32) regnum) | (((uint32) regtype) << 16) ); | ||||
} // reg_to_uint32 | ||||
// !!! FIXME: ditch this for a hash table. | ||||
RegisterList *reglist_insert(Context *ctx, RegisterList *prev, | ||||
const RegisterType regtype, | ||||
const int regnum) | ||||
{ | ||||
const uint32 newval = reg_to_ui32(regtype, regnum); | ||||
RegisterList *item = prev->next; | ||||
while (item != NULL) | ||||
{ | ||||
const uint32 val = reg_to_ui32(item->regtype, item->regnum); | ||||
if (newval == val) | ||||
return item; // already set, so we're done. | ||||
else if (newval < val) // insert it here. | ||||
break; | ||||
else // if (newval > val) | ||||
{ | ||||
// keep going, we're not to the insertion point yet. | ||||
prev = item; | ||||
item = item->next; | ||||
} // else | ||||
} // while | ||||
// we need to insert an entry after (prev). | ||||
item = (RegisterList *) Malloc(ctx, sizeof (RegisterList)); | ||||
if (item != NULL) | ||||
{ | ||||
item->regtype = regtype; | ||||
item->regnum = regnum; | ||||
item->usage = MOJOSHADER_USAGE_UNKNOWN; | ||||
item->index = 0; | ||||
item->writemask = 0; | ||||
item->misc = 0; | ||||
item->written = 0; | ||||
#if SUPPORT_PROFILE_SPIRV | ||||
item->spirv.iddecl = 0; | ||||
item->spirv.is_ssa = 0; | ||||
#endif | ||||
item->array = NULL; | ||||
item->next = prev->next; | ||||
prev->next = item; | ||||
} // if | ||||
return item; | ||||
} // reglist_insert | ||||
RegisterList *reglist_find(const RegisterList *prev, | ||||
const RegisterType rtype, | ||||
const int regnum) | ||||
{ | ||||
const uint32 newval = reg_to_ui32(rtype, regnum); | ||||
RegisterList *item = prev->next; | ||||
while (item != NULL) | ||||
{ | ||||
const uint32 val = reg_to_ui32(item->regtype, item->regnum); | ||||
if (newval == val) | ||||
return item; // here it is. | ||||
else if (newval < val) // should have been here if it existed. | ||||
return NULL; | ||||
else // if (newval > val) | ||||
item = item->next; | ||||
} // while | ||||
return NULL; // wasn't in the list. | ||||
} // reglist_find | ||||
RegisterList *set_used_register(Context *ctx, | ||||
const RegisterType regtype, | ||||
const int regnum, | ||||
const int written) | ||||
{ | ||||
RegisterList *reg = NULL; | ||||
if ((regtype == REG_TYPE_COLOROUT) && (regnum > 0)) | ||||
ctx->have_multi_color_outputs = 1; | ||||
reg = reglist_insert(ctx, &ctx->used_registers, regtype, regnum); | ||||
if (reg && written) | ||||
reg->written = 1; | ||||
return reg; | ||||
} // set_used_register | ||||
void set_defined_register(Context *ctx, const RegisterType rtype, | ||||
const int regnum) | ||||
{ | ||||
reglist_insert(ctx, &ctx->defined_registers, rtype, regnum); | ||||
} // set_defined_register | ||||
// Writemasks | ||||
int writemask_xyzw(const int writemask) | ||||
{ | ||||
return (writemask == 0xF); // 0xF == 1111. No explicit mask (full!). | ||||
} // writemask_xyzw | ||||
int writemask_xyz(const int writemask) | ||||
{ | ||||
return (writemask == 0x7); // 0x7 == 0111. (that is: xyz) | ||||
} // writemask_xyz | ||||
int writemask_xy(const int writemask) | ||||
{ | ||||
return (writemask == 0x3); // 0x3 == 0011. (that is: xy) | ||||
} // writemask_xy | ||||
int writemask_x(const int writemask) | ||||
{ | ||||
return (writemask == 0x1); // 0x1 == 0001. (that is: x) | ||||
} // writemask_x | ||||
int writemask_y(const int writemask) | ||||
{ | ||||
return (writemask == 0x2); // 0x2 == 0010. (that is: y) | ||||
} // writemask_y | ||||
int replicate_swizzle(const int swizzle) | ||||
{ | ||||
return ( (((swizzle >> 0) & 0x3) == ((swizzle >> 2) & 0x3)) && | ||||
(((swizzle >> 2) & 0x3) == ((swizzle >> 4) & 0x3)) && | ||||
(((swizzle >> 4) & 0x3) == ((swizzle >> 6) & 0x3)) ); | ||||
} // replicate_swizzle | ||||
int no_swizzle(const int swizzle) | ||||
{ | ||||
return (swizzle == 0xE4); // 0xE4 == 11100100 ... 0 1 2 3. No swizzle. | ||||
} // no_swizzle | ||||
int vecsize_from_writemask(const int m) | ||||
{ | ||||
return (m & 1) + ((m >> 1) & 1) + ((m >> 2) & 1) + ((m >> 3) & 1); | ||||
} // vecsize_from_writemask | ||||
void set_dstarg_writemask(DestArgInfo *dst, const int mask) | ||||
{ | ||||
dst->writemask = mask; | ||||
dst->writemask0 = ((mask >> 0) & 1); | ||||
dst->writemask1 = ((mask >> 1) & 1); | ||||
dst->writemask2 = ((mask >> 2) & 1); | ||||
dst->writemask3 = ((mask >> 3) & 1); | ||||
} // set_dstarg_writemask | ||||
// D3D stuff that's used in more than just the d3d profile... | ||||
int isscalar(Context *ctx, const MOJOSHADER_shaderType shader_type, | ||||
const RegisterType rtype, const int rnum) | ||||
{ | ||||
const int uses_psize = ctx->uses_pointsize; | ||||
const int uses_fog = ctx->uses_fog; | ||||
if ( (rtype == REG_TYPE_OUTPUT) && ((uses_psize) || (uses_fog)) ) | ||||
{ | ||||
const RegisterList *reg = reglist_find(&ctx->attributes, rtype, rnum); | ||||
if (reg != NULL) | ||||
{ | ||||
const MOJOSHADER_usage usage = reg->usage; | ||||
return ( (uses_psize && (usage == MOJOSHADER_USAGE_POINTSIZE)) || | ||||
(uses_fog && (usage == MOJOSHADER_USAGE_FOG)) ); | ||||
} // if | ||||
} // if | ||||
return scalar_register(shader_type, rtype, rnum); | ||||
} // isscalar | ||||
const char *get_D3D_register_string(Context *ctx, | ||||
RegisterType regtype, | ||||
int regnum, char *regnum_str, | ||||
size_t regnum_size) | ||||
{ | ||||
const char *retval = NULL; | ||||
int has_number = 1; | ||||
switch (regtype) | ||||
{ | ||||
case REG_TYPE_TEMP: | ||||
retval = "r"; | ||||
break; | ||||
case REG_TYPE_INPUT: | ||||
retval = "v"; | ||||
break; | ||||
case REG_TYPE_CONST: | ||||
retval = "c"; | ||||
break; | ||||
case REG_TYPE_ADDRESS: // (or REG_TYPE_TEXTURE, same value.) | ||||
retval = shader_is_vertex(ctx) ? "a" : "t"; | ||||
break; | ||||
case REG_TYPE_RASTOUT: | ||||
switch ((RastOutType) regnum) | ||||
{ | ||||
case RASTOUT_TYPE_POSITION: retval = "oPos"; break; | ||||
case RASTOUT_TYPE_FOG: retval = "oFog"; break; | ||||
case RASTOUT_TYPE_POINT_SIZE: retval = "oPts"; break; | ||||
} // switch | ||||
has_number = 0; | ||||
break; | ||||
case REG_TYPE_ATTROUT: | ||||
retval = "oD"; | ||||
break; | ||||
case REG_TYPE_OUTPUT: // (or REG_TYPE_TEXCRDOUT, same value.) | ||||
if (shader_is_vertex(ctx) && shader_version_atleast(ctx, 3, 0)) | ||||
retval = "o"; | ||||
else | ||||
retval = "oT"; | ||||
break; | ||||
case REG_TYPE_CONSTINT: | ||||
retval = "i"; | ||||
break; | ||||
case REG_TYPE_COLOROUT: | ||||
retval = "oC"; | ||||
break; | ||||
case REG_TYPE_DEPTHOUT: | ||||
retval = "oDepth"; | ||||
has_number = 0; | ||||
break; | ||||
case REG_TYPE_SAMPLER: | ||||
retval = "s"; | ||||
break; | ||||
case REG_TYPE_CONSTBOOL: | ||||
retval = "b"; | ||||
break; | ||||
case REG_TYPE_LOOP: | ||||
retval = "aL"; | ||||
has_number = 0; | ||||
break; | ||||
case REG_TYPE_MISCTYPE: | ||||
switch ((const MiscTypeType) regnum) | ||||
{ | ||||
case MISCTYPE_TYPE_POSITION: retval = "vPos"; break; | ||||
case MISCTYPE_TYPE_FACE: retval = "vFace"; break; | ||||
} // switch | ||||
has_number = 0; | ||||
break; | ||||
case REG_TYPE_LABEL: | ||||
retval = "l"; | ||||
break; | ||||
case REG_TYPE_PREDICATE: | ||||
retval = "p"; | ||||
break; | ||||
//case REG_TYPE_TEMPFLOAT16: // !!! FIXME: don't know this asm string | ||||
default: | ||||
fail(ctx, "unknown register type"); | ||||
retval = "???"; | ||||
has_number = 0; | ||||
break; | ||||
} // switch | ||||
if (has_number) | ||||
snprintf(regnum_str, regnum_size, "%u", (uint) regnum); | ||||
else | ||||
regnum_str[0] = '\0'; | ||||
return retval; | ||||
} // get_D3D_register_string | ||||
// !!! FIXME: These should stay in the mojoshader_profile_d3d file | ||||
// !!! FIXME: but ARB1 relies on them, so we have to move them here. | ||||
// !!! FIXME: If/when we kill off ARB1, we can move these back. | ||||
const char *get_D3D_varname_in_buf(Context *ctx, RegisterType rt, | ||||
int regnum, char *buf, | ||||
const size_t len) | ||||
{ | ||||
char regnum_str[16]; | ||||
const char *regtype_str = get_D3D_register_string(ctx, rt, regnum, | ||||
regnum_str, sizeof (regnum_str)); | ||||
snprintf(buf,len,"%s%s", regtype_str, regnum_str); | ||||
return buf; | ||||
} // get_D3D_varname_in_buf | ||||
const char *get_D3D_varname(Context *ctx, RegisterType rt, int regnum) | ||||
{ | ||||
char buf[64]; | ||||
get_D3D_varname_in_buf(ctx, rt, regnum, buf, sizeof (buf)); | ||||
return StrDup(ctx, buf); | ||||
} // get_D3D_varname | ||||
#pragma GCC visibility pop | ||||