/**
* 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)
{
// elements 1|2 match 3|4 and element 1 matches element 2.
return ( (((swizzle >> 4) & 0xF) == ((swizzle >> 0) & 0xF)) &&
(((swizzle >> 0) & 0x3) == ((swizzle >> 2) & 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