From c99b39f320de258facfc21dd7e778046ef107e7a Mon Sep 17 00:00:00 2001 From: "Ryan C. Gordon" Date: Thu, 4 Dec 2008 21:36:11 -0500 Subject: [PATCH] Initial work on assembler. Not even close to done. --- CMakeLists.txt | 6 +- mojoshader.c | 270 +------- mojoshader.h | 103 +++ mojoshader_assembler.c | 1424 ++++++++++++++++++++++++++++++++++++++++ mojoshader_internal.h | 261 ++++++++ mojoshader_opengl.c | 10 - 6 files changed, 1806 insertions(+), 268 deletions(-) create mode 100644 mojoshader_assembler.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 1aa0fe33..be4bb3ba 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,7 +40,11 @@ IF(MSVC) ADD_DEFINITIONS(-TP) # force .c files to compile as C++. ENDIF(MSVC) -ADD_LIBRARY(mojoshader STATIC mojoshader.c mojoshader_opengl.c) +ADD_LIBRARY(mojoshader STATIC + mojoshader.c + mojoshader_assembler.c + mojoshader_opengl.c +) FIND_PACKAGE(SDL) IF(SDL_FOUND) diff --git a/mojoshader.c b/mojoshader.c index a9f97b7b..3a2aaa31 100644 --- a/mojoshader.c +++ b/mojoshader.c @@ -14,42 +14,6 @@ #define __MOJOSHADER_INTERNAL__ 1 #include "mojoshader_internal.h" -// we need to reference this by explicit value occasionally. -#define OPCODE_RET 28 - -typedef enum -{ - REG_TYPE_TEMP = 0, - REG_TYPE_INPUT = 1, - REG_TYPE_CONST = 2, - REG_TYPE_ADDRESS = 3, - REG_TYPE_TEXTURE = 3, // ALSO 3! - REG_TYPE_RASTOUT = 4, - REG_TYPE_ATTROUT = 5, - REG_TYPE_TEXCRDOUT = 6, - REG_TYPE_OUTPUT = 6, // ALSO 6! - REG_TYPE_CONSTINT = 7, - REG_TYPE_COLOROUT = 8, - REG_TYPE_DEPTHOUT = 9, - REG_TYPE_SAMPLER = 10, - REG_TYPE_CONST2 = 11, - REG_TYPE_CONST3 = 12, - REG_TYPE_CONST4 = 13, - REG_TYPE_CONSTBOOL = 14, - REG_TYPE_LOOP = 15, - REG_TYPE_TEMPFLOAT16 = 16, - REG_TYPE_MISCTYPE = 17, - REG_TYPE_LABEL = 18, - REG_TYPE_PREDICATE = 19, - REG_TYPE_MAX = 19 -} RegisterType; - -typedef enum -{ - TEXTURE_TYPE_2D = 2, - TEXTURE_TYPE_CUBE = 3, - TEXTURE_TYPE_VOLUME = 4, -} TextureType; // predeclare. typedef struct Context Context; @@ -122,21 +86,6 @@ typedef struct const_array_varname_function get_const_array_varname; } Profile; -typedef enum -{ - RASTOUT_TYPE_POSITION = 0, - RASTOUT_TYPE_FOG = 1, - RASTOUT_TYPE_POINT_SIZE = 2, - RASTOUT_TYPE_MAX = 2 -} RastOutType; - -typedef enum -{ - MISCTYPE_TYPE_POSITION = 0, - MISCTYPE_TYPE_FACE = 1, - MISCTYPE_TYPE_MAX = 1 -} MiscTypeType; - // A simple linked list of strings, so we can build the final output without // realloc()ing for each new line, and easily insert lines into the middle @@ -181,68 +130,6 @@ typedef struct RegisterList struct RegisterList *next; } RegisterList; -// result modifiers. -// !!! FIXME: why isn't this an enum? -#define MOD_SATURATE 0x01 -#define MOD_PP 0x02 -#define MOD_CENTROID 0x04 - -// source modifiers. -typedef enum -{ - SRCMOD_NONE, - SRCMOD_NEGATE, - SRCMOD_BIAS, - SRCMOD_BIASNEGATE, - SRCMOD_SIGN, - SRCMOD_SIGNNEGATE, - SRCMOD_COMPLEMENT, - SRCMOD_X2, - SRCMOD_X2NEGATE, - SRCMOD_DZ, - SRCMOD_DW, - SRCMOD_ABS, - SRCMOD_ABSNEGATE, - SRCMOD_NOT, - SRCMOD_TOTAL -} SourceMod; - - -typedef struct -{ - const uint32 *token; // this is the unmolested token in the stream. - int regnum; - int relative; - int writemask; // xyzw or rgba (all four, not split out). - int writemask0; // x or red - int writemask1; // y or green - int writemask2; // z or blue - int writemask3; // w or alpha - int orig_writemask; // writemask before mojoshader tweaks it. - int result_mod; - int result_shift; - RegisterType regtype; -} DestArgInfo; - -typedef struct -{ - const uint32 *token; // this is the unmolested token in the stream. - int regnum; - int swizzle; // xyzw (all four, not split out). - int swizzle_x; - int swizzle_y; - int swizzle_z; - int swizzle_w; - SourceMod src_mod; - RegisterType regtype; - int relative; - RegisterType relative_regtype; - int relative_regnum; - int relative_component; - const VariableList *relative_array; -} SourceArgInfo; - - #define SCRATCH_BUFFER_SIZE 128 #define SCRATCH_BUFFERS 32 @@ -738,28 +625,6 @@ static inline int replicate_swizzle(const int swizzle) } // replicate_swizzle -static inline int scalar_register(const RegisterType regtype, const int regnum) -{ - switch (regtype) - { - case REG_TYPE_DEPTHOUT: - case REG_TYPE_CONSTBOOL: - case REG_TYPE_PREDICATE: - case REG_TYPE_LOOP: - return 1; - - case REG_TYPE_MISCTYPE: - if ( ((const MiscTypeType) regnum) == MISCTYPE_TYPE_FACE ) - return 1; - return 0; - - default: break; - } // switch - - return 0; -} // scalar_register - - static inline int no_swizzle(const int swizzle) { return (swizzle == 0xE4); // 0xE4 == 11100100 ... 0 1 2 3. No swizzle. @@ -6365,125 +6230,23 @@ typedef struct // of the instruction token. static const Instruction instructions[] = { - // INSTRUCTION_STATE means this opcode has to update the state machine - // (we're entering an ELSE block, etc). INSTRUCTION means there's no - // state, just go straight to the emitters. - #define INSTRUCTION_STATE(op, slots, argsseq, t) { \ - #op, slots, t, parse_args_##argsseq, state_##op, PROFILE_EMITTERS(op) \ - } - #define INSTRUCTION(op, slots, argsseq, t) { \ - #op, slots, t, parse_args_##argsseq, 0, PROFILE_EMITTERS(op) \ - } + #define INSTRUCTION_STATE(op, opstr, slots, a, t) { \ + opstr, slots, t, parse_args_##a, state_##op, PROFILE_EMITTERS(op) \ + }, + + #define INSTRUCTION(op, opstr, slots, a, t) { \ + opstr, slots, t, parse_args_##a, 0, PROFILE_EMITTERS(op) \ + }, - // !!! FIXME: Some of these MOJOSHADER_TYPE_ANYs need to have their scope - // !!! FIXME: reduced to just PIXEL or VERTEX. - - INSTRUCTION(NOP, 1, NULL, MOJOSHADER_TYPE_ANY), - INSTRUCTION(MOV, 1, DS, MOJOSHADER_TYPE_ANY), - INSTRUCTION(ADD, 1, DSS, MOJOSHADER_TYPE_ANY), - INSTRUCTION(SUB, 1, DSS, MOJOSHADER_TYPE_ANY), - INSTRUCTION(MAD, 1, DSSS, MOJOSHADER_TYPE_ANY), - INSTRUCTION(MUL, 1, DSS, MOJOSHADER_TYPE_ANY), - INSTRUCTION_STATE(RCP, 1, DS, MOJOSHADER_TYPE_ANY), - INSTRUCTION(RSQ, 1, DS, MOJOSHADER_TYPE_ANY), - INSTRUCTION(DP3, 1, DSS, MOJOSHADER_TYPE_ANY), - INSTRUCTION_STATE(DP4, 1, DSS, MOJOSHADER_TYPE_ANY), - INSTRUCTION(MIN, 1, DSS, MOJOSHADER_TYPE_ANY), - INSTRUCTION(MAX, 1, DSS, MOJOSHADER_TYPE_ANY), - INSTRUCTION(SLT, 1, DSS, MOJOSHADER_TYPE_ANY), - INSTRUCTION(SGE, 1, DSS, MOJOSHADER_TYPE_ANY), - INSTRUCTION(EXP, 1, DS, MOJOSHADER_TYPE_ANY), - INSTRUCTION_STATE(LOG, 1, DS, MOJOSHADER_TYPE_ANY), - INSTRUCTION(LIT, 3, DS, MOJOSHADER_TYPE_ANY), - INSTRUCTION(DST, 1, DSS, MOJOSHADER_TYPE_VERTEX), - INSTRUCTION(LRP, 2, DSSS, MOJOSHADER_TYPE_ANY), - INSTRUCTION_STATE(FRC, 1, DS, MOJOSHADER_TYPE_ANY), - INSTRUCTION_STATE(M4X4, 4, DSS, MOJOSHADER_TYPE_ANY), - INSTRUCTION_STATE(M4X3, 3, DSS, MOJOSHADER_TYPE_ANY), - INSTRUCTION_STATE(M3X4, 4, DSS, MOJOSHADER_TYPE_ANY), - INSTRUCTION_STATE(M3X3, 3, DSS, MOJOSHADER_TYPE_ANY), - INSTRUCTION_STATE(M3X2, 2, DSS, MOJOSHADER_TYPE_ANY), - INSTRUCTION_STATE(CALL, 2, S, MOJOSHADER_TYPE_ANY), - INSTRUCTION_STATE(CALLNZ, 3, SS, MOJOSHADER_TYPE_ANY), - INSTRUCTION_STATE(LOOP, 3, SS, MOJOSHADER_TYPE_ANY), - INSTRUCTION_STATE(RET, 1, NULL, MOJOSHADER_TYPE_ANY), - INSTRUCTION_STATE(ENDLOOP, 2, NULL, MOJOSHADER_TYPE_ANY), - INSTRUCTION_STATE(LABEL, 0, S, MOJOSHADER_TYPE_ANY), - INSTRUCTION_STATE(DCL, 0, DCL, MOJOSHADER_TYPE_ANY), - INSTRUCTION_STATE(POW, 3, DSS, MOJOSHADER_TYPE_ANY), - INSTRUCTION(CRS, 2, DSS, MOJOSHADER_TYPE_ANY), - INSTRUCTION(SGN, 3, DSSS, MOJOSHADER_TYPE_ANY), - INSTRUCTION(ABS, 1, DS, MOJOSHADER_TYPE_ANY), - INSTRUCTION(NRM, 3, DS, MOJOSHADER_TYPE_ANY), - INSTRUCTION_STATE(SINCOS, 8, SINCOS, MOJOSHADER_TYPE_ANY), - INSTRUCTION_STATE(REP, 3, S, MOJOSHADER_TYPE_ANY), - INSTRUCTION_STATE(ENDREP, 2, NULL, MOJOSHADER_TYPE_ANY), - INSTRUCTION_STATE(IF, 3, S, MOJOSHADER_TYPE_ANY), - INSTRUCTION_STATE(IFC, 3, SS, MOJOSHADER_TYPE_ANY), - INSTRUCTION(ELSE, 1, NULL, MOJOSHADER_TYPE_ANY), // !!! FIXME: state! - INSTRUCTION(ENDIF, 1, NULL, MOJOSHADER_TYPE_ANY), // !!! FIXME: state! - INSTRUCTION_STATE(BREAK, 1, NULL, MOJOSHADER_TYPE_ANY), - INSTRUCTION_STATE(BREAKC, 3, SS, MOJOSHADER_TYPE_ANY), - INSTRUCTION_STATE(MOVA, 1, DS, MOJOSHADER_TYPE_VERTEX), - INSTRUCTION_STATE(DEFB, 0, DEFB, MOJOSHADER_TYPE_ANY), - INSTRUCTION_STATE(DEFI, 0, DEF, MOJOSHADER_TYPE_ANY), - INSTRUCTION(RESERVED, 0, NULL, MOJOSHADER_TYPE_UNKNOWN), - INSTRUCTION(RESERVED, 0, NULL, MOJOSHADER_TYPE_UNKNOWN), - INSTRUCTION(RESERVED, 0, NULL, MOJOSHADER_TYPE_UNKNOWN), - INSTRUCTION(RESERVED, 0, NULL, MOJOSHADER_TYPE_UNKNOWN), - INSTRUCTION(RESERVED, 0, NULL, MOJOSHADER_TYPE_UNKNOWN), - INSTRUCTION(RESERVED, 0, NULL, MOJOSHADER_TYPE_UNKNOWN), - INSTRUCTION(RESERVED, 0, NULL, MOJOSHADER_TYPE_UNKNOWN), - INSTRUCTION(RESERVED, 0, NULL, MOJOSHADER_TYPE_UNKNOWN), - INSTRUCTION(RESERVED, 0, NULL, MOJOSHADER_TYPE_UNKNOWN), - INSTRUCTION(RESERVED, 0, NULL, MOJOSHADER_TYPE_UNKNOWN), - INSTRUCTION(RESERVED, 0, NULL, MOJOSHADER_TYPE_UNKNOWN), - INSTRUCTION(RESERVED, 0, NULL, MOJOSHADER_TYPE_UNKNOWN), - INSTRUCTION(RESERVED, 0, NULL, MOJOSHADER_TYPE_UNKNOWN), - INSTRUCTION(RESERVED, 0, NULL, MOJOSHADER_TYPE_UNKNOWN), - INSTRUCTION(RESERVED, 0, NULL, MOJOSHADER_TYPE_UNKNOWN), - INSTRUCTION_STATE(TEXCRD, 1, TEXCRD, MOJOSHADER_TYPE_PIXEL), - INSTRUCTION_STATE(TEXKILL, 2, D, MOJOSHADER_TYPE_PIXEL), - INSTRUCTION_STATE(TEXLD, 1, TEXLD, MOJOSHADER_TYPE_PIXEL), - INSTRUCTION(TEXBEM, 1, DS, MOJOSHADER_TYPE_PIXEL), - INSTRUCTION(TEXBEML, 2, DS, MOJOSHADER_TYPE_PIXEL), - INSTRUCTION(TEXREG2AR, 1, DS, MOJOSHADER_TYPE_PIXEL), - INSTRUCTION(TEXREG2GB, 1, DS, MOJOSHADER_TYPE_PIXEL), - INSTRUCTION(TEXM3X2PAD, 1, DS, MOJOSHADER_TYPE_PIXEL), - INSTRUCTION(TEXM3X2TEX, 1, DS, MOJOSHADER_TYPE_PIXEL), - INSTRUCTION(TEXM3X3PAD, 1, DS, MOJOSHADER_TYPE_PIXEL), - INSTRUCTION(TEXM3X3TEX, 1, DS, MOJOSHADER_TYPE_PIXEL), - INSTRUCTION(RESERVED, 0, NULL, MOJOSHADER_TYPE_UNKNOWN), - INSTRUCTION(TEXM3X3SPEC, 1, DSS, MOJOSHADER_TYPE_PIXEL), - INSTRUCTION(TEXM3X3VSPEC, 1, DS, MOJOSHADER_TYPE_PIXEL), - INSTRUCTION(EXPP, 1, DS, MOJOSHADER_TYPE_ANY), - INSTRUCTION_STATE(LOGP, 1, DS, MOJOSHADER_TYPE_ANY), - INSTRUCTION_STATE(CND, 1, DSSS, MOJOSHADER_TYPE_PIXEL), - INSTRUCTION_STATE(DEF, 0, DEF, MOJOSHADER_TYPE_ANY), - INSTRUCTION(TEXREG2RGB, 1, DS, MOJOSHADER_TYPE_PIXEL), - INSTRUCTION(TEXDP3TEX, 1, DS, MOJOSHADER_TYPE_PIXEL), - INSTRUCTION(TEXM3X2DEPTH, 1, DS, MOJOSHADER_TYPE_PIXEL), - INSTRUCTION(TEXDP3, 1, DS, MOJOSHADER_TYPE_PIXEL), - INSTRUCTION(TEXM3X3, 1, DS, MOJOSHADER_TYPE_PIXEL), - INSTRUCTION(TEXDEPTH, 1, D, MOJOSHADER_TYPE_PIXEL), - INSTRUCTION_STATE(CMP, 1, DSSS, MOJOSHADER_TYPE_PIXEL), - INSTRUCTION(BEM, 2, DSS, MOJOSHADER_TYPE_PIXEL), - INSTRUCTION_STATE(DP2ADD, 2, DSSS, MOJOSHADER_TYPE_PIXEL), - INSTRUCTION(DSX, 2, DS, MOJOSHADER_TYPE_PIXEL), - INSTRUCTION(DSY, 2, DS, MOJOSHADER_TYPE_PIXEL), - INSTRUCTION(TEXLDD, 3, DSSSS, MOJOSHADER_TYPE_PIXEL), - INSTRUCTION_STATE(SETP, 1, DSS, MOJOSHADER_TYPE_ANY), - INSTRUCTION_STATE(TEXLDL, 2, DSS, MOJOSHADER_TYPE_ANY), - INSTRUCTION_STATE(BREAKP, 3, S, MOJOSHADER_TYPE_ANY), - - // !!! FIXME: TEXLDB? + #define MOJOSHADER_DO_INSTRUCTION_TABLE 1 + #include "mojoshader_internal.h" + #undef MOJOSHADER_DO_INSTRUCTION_TABLE #undef INSTRUCTION #undef INSTRUCTION_STATE }; - // parse various token types... static int parse_instruction_token(Context *ctx) @@ -6506,6 +6269,8 @@ static int parse_instruction_token(Context *ctx) if ((token & 0x80000000) != 0) return fail(ctx, "instruction token high bit must be zero."); // so says msdn. + else if (instruction->opcode_string == NULL) + return fail(ctx, "Unknown opcode."); if (coissue) { @@ -6743,6 +6508,7 @@ static int parse_end_token(Context *ctx) static int parse_phase_token(Context *ctx) { + // !!! FIXME: needs state; allow only one phase token per shader, I think? if (SWAP32(*(ctx->tokens)) != 0x0000FFFD) // phase token always 0x0000FFFD. return 0; // not us, eat no tokens. else if ( (!shader_is_pixel(ctx)) || (!shader_version_exactly(ctx, 1, 4)) ) @@ -6781,16 +6547,6 @@ static int parse_token(Context *ctx) } // parse_token -// #define this to force app to supply an allocator, so there's no reference -// to the C runtime's malloc() and free()... -#if MOJOSHADER_FORCE_ALLOCATOR -#define internal_malloc NULL -#define internal_free NULL -#else -static void *internal_malloc(int bytes, void *d) { return malloc(bytes); } -static void internal_free(void *ptr, void *d) { free(ptr); } -#endif - static int find_profile_id(const char *profile) { int i; diff --git a/mojoshader.h b/mojoshader.h index 9d12bb55..7823fb92 100644 --- a/mojoshader.h +++ b/mojoshader.h @@ -485,6 +485,109 @@ void MOJOSHADER_freeParseData(const MOJOSHADER_parseData *data); +/* Assembler interface... */ + +/* + * Structure used to return data from assembling of a shader... + */ +typedef struct MOJOSHADER_assembleData +{ + /* + * Human-readable error, if there is one. Will be NULL if there was no + * error. The string will be UTF-8 encoded, and English only. Most of + * these shouldn't be shown to the end-user anyhow. + */ + const char *error; + + /* + * Line number of error, if there is one. Will be zero if there isn't. + */ + unsigned int error_line; + + /* + * Bytes of output from assembling. Binary data. Will be NULL on error. + */ + const char *output; + + /* + * Byte count for output. Will be 0 on error. + */ + int output_len; + + /* + * Count of Direct3D instruction slots used. As with Microsoft's own + * assembler, this value is just a rough estimate, as unpredicable + * real-world factors make the actual value vary at least a little from + * this count. Still, it can give you a rough idea of the size of your + * shader. Will be zero on error. + */ + int instruction_count; + + /* + * The type of shader we parsed. Will be MOJOSHADER_TYPE_UNKNOWN on error. + */ + MOJOSHADER_shaderType shader_type; + + /* + * The shader's major version. If this was a "vs_3_0", this would be 3. + */ + int major_ver; + + /* + * The shader's minor version. If this was a "ps_1_4", this would be 4. + * Two notes: for "vs_2_x", this is 1, and for "vs_3_sw", this is 255. + */ + int minor_ver; + + /* + * This is the malloc implementation you passed to MOJOSHADER_parse(). + */ + MOJOSHADER_malloc malloc; + + /* + * This is the free implementation you passed to MOJOSHADER_parse(). + */ + MOJOSHADER_free free; + + /* + * This is the pointer you passed as opaque data for your allocator. + */ + void *malloc_data; +} MOJOSHADER_assembleData; + +/* + * This function is optional. Use this to convert Direct3D shader assembly + * language into bytecode, which can be handled by MOJOSHADER_parse(). + * + * (source) is an ASCII, NULL-terminated string of valid Direct3D shader + * assembly source code. + * + * As assembling requires some memory to be allocated, you may provide a + * custom allocator to this function, which will be used to allocate/free + * memory. They function just like malloc() and free(). We do not use + * realloc(). If you don't care, pass NULL in for the allocator functions. + * If your allocator needs instance-specific data, you may supply it with the + * (d) parameter. This pointer is passed as-is to your (m) and (f) functions. + * + * This function is thread safe, so long as (m) and (f) are too, and that + * (source) remains intact for the duration of the call. This allows you + * to assemble several shaders on separate CPU cores at the same time. + */ +const MOJOSHADER_assembleData *MOJOSHADER_assemble(const char *source, + MOJOSHADER_malloc m, MOJOSHADER_free f, void *d); + +/* + * Call this to dispose of assembling results when you are done with them. + * This will call the MOJOSHADER_free function you provided to + * MOJOSHADER_assemble() multiple times, if you provided one. + * Passing a NULL here is a safe no-op. + * + * This function is thread safe, so long as any allocator you passed into + * MOJOSHADER_assemble() is, too. + */ +void MOJOSHADER_freeAssembleData(const MOJOSHADER_assembleData *data); + + /* OpenGL interface... */ diff --git a/mojoshader_assembler.c b/mojoshader_assembler.c new file mode 100644 index 00000000..66607d87 --- /dev/null +++ b/mojoshader_assembler.c @@ -0,0 +1,1424 @@ +/** + * 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_internal.h" + +#define DEBUG_TOKENIZER 1 + + +typedef struct Context Context; + +// Context...this is state that changes as we assemble a shader... +struct Context +{ + MOJOSHADER_malloc malloc; + MOJOSHADER_free free; + void *malloc_data; + const char *failstr; + const char *source; + MOJOSHADER_shaderType shader_type; + uint8 major_ver; + uint8 minor_ver; + unsigned int instruction_count; + unsigned int linenum; + char prevchar; + char token[64]; + char pushedback; + char pushback_token[64]; + uint32 tokenbuf[16]; + int tokenbufpos; + int centroid_allowed; + DestArgInfo dest_arg; +}; + + +// Convenience functions for allocators... + +static MOJOSHADER_assembleData out_of_mem_data = { + "Out of memory", 0, 0, 0, MOJOSHADER_TYPE_UNKNOWN, 0, 0, 0, 0, 0 +}; + + +static const char *out_of_mem_str = "Out of memory"; +static inline int out_of_memory(Context *ctx) +{ + if (ctx->failstr == NULL) + ctx->failstr = out_of_mem_str; // fail() would call malloc(). + return FAIL; +} // out_of_memory + +static inline 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 + +static inline void Free(Context *ctx, void *ptr) +{ + if (ptr != NULL) // check for NULL in case of dumb free() impl. + ctx->free(ptr, ctx->malloc_data); +} // Free + +static int failf(Context *ctx, const char *fmt, ...) ISPRINTF(2,3); +static int failf(Context *ctx, const char *fmt, ...) +{ + if (ctx->failstr == NULL) // don't change existing error. + { + char scratch = 0; + va_list ap; + va_start(ap, fmt); + const int len = vsnprintf(&scratch, sizeof (scratch), fmt, ap); + va_end(ap); + + char *failstr = (char *) Malloc(ctx, len + 1); + if (failstr != NULL) + { + va_start(ap, fmt); + vsnprintf(failstr, len + 1, fmt, ap); // rebuild it. + va_end(ap); + ctx->failstr = failstr; + } // if + } // if + + return FAIL; +} // failf + +static inline int fail(Context *ctx, const char *reason) +{ + return failf(ctx, "%s", reason); +} // fail + +static inline int isfail(const Context *ctx) +{ + return (ctx->failstr != NULL); +} // isfail + + +// Shader model version magic... + +static inline uint32 ver_ui32(const uint8 major, const uint8 minor) +{ + return ( (((uint32) major) << 16) | (((minor) == 0xFF) ? 0 : (minor)) ); +} // version_ui32 + +static inline 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 + +static inline 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 + +static inline 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 + +static inline int shader_is_pixel(const Context *ctx) +{ + return (ctx->shader_type == MOJOSHADER_TYPE_PIXEL); +} // shader_is_pixel + +static inline int shader_is_vertex(const Context *ctx) +{ + return (ctx->shader_type == MOJOSHADER_TYPE_VERTEX); +} // shader_is_vertex + + +extern void writeme(void); + +static void output_token_noswap(Context *ctx, const uint32 token) +{ + if (isfail(ctx)) + return; + + writeme(); +} // output_token_noswap + + +static inline void output_token(Context *ctx, const uint32 token) +{ + output_token_noswap(ctx, SWAP32(token)); +} // output_token + + +static void output_comment_bytes(Context *ctx, const uint8 *buf, size_t len) +{ + if (len > (0xFFFF * 4)) // length is stored as token count, in 16 bits. + fail(ctx, "Comment field is too big"); + else if (!isfail(ctx)) + { + const uint32 tokencount = (len / 4) + ((len % 4) ? 1 : 0); + output_token(ctx, 0xFFFE | (tokencount << 16)); + while (len >= 4) + { + output_token_noswap(ctx, *((const uint32 *) buf)); + len -= 4; + buf += 4; + } // while + + if (len > 0) // handle spillover... + { + union { uint8 ui8[4]; uint32 ui32; } overflow; + overflow.ui32 = 0; + memcpy(overflow.ui8, buf, len); + output_token_noswap(ctx, overflow.ui32); + } // if + } // else if +} // output_comment_bytes + + +static inline void output_comment_string(Context *ctx, const char *str) +{ + output_comment_bytes(ctx, (const uint8 *) str, strlen(str)); +} // output_comment_string + + +static int _tokenize(Context *ctx) +{ + int idx = 0; + + if (isfail(ctx)) + return FAIL; + + if (ctx->pushedback) + { + ctx->pushedback = 0; + return NOFAIL; + } // if + + while (1) + { + if (idx >= sizeof (ctx->token)) + return fail(ctx, "buffer overflow"); + + char ch = *ctx->source; + if (ch == '\t') + ch = ' '; // collapse tabs into single spaces. + else if (ch == '\r') + { + if (ctx->source[1] == '\n') + continue; // ignore '\r' if this is "\r\n" ... + ch = '\n'; + } // else if + + if ((ch > '0') && (ch < '9')) + { + // starting a number, but rest of current token was not number. + if ((idx > 0) && ((ctx->prevchar < '0') || (ctx->prevchar > '9'))) + { + ctx->token[idx++] = '\0'; + return NOFAIL; + } // if + } // if + else + { + // starting a non-number, but rest of current token was numbers. + if ((idx > 0) && ((ctx->prevchar >= '0') || (ctx->prevchar <= '9'))) + { + ctx->token[idx++] = '\0'; + return NOFAIL; + } // if + } // else + + switch (ch) + { + case '/': + case ';': // !!! FIXME: comment, right? + if (idx != 0) // finish off existing token. + ctx->token[idx] = '\0'; + else + { + ctx->token[idx++] = ch; + ctx->source++; + if ((ch == '/') && (ctx->source[1] == '/')) + { + ctx->token[idx++] = '/'; + ctx->source++; + } // if + ctx->token[idx++] = '\0'; + } // else + return NOFAIL; + + case ' ': + if (ctx->prevch == ' ') + break; // multiple whitespace collapses into one. + // intentional fall-through... + + case '_': + case '[': + case ']': + case '(': + case ')': + case '!': + case '+': + case '-': + case ',': + case '.': + case '\n': + if (idx != 0) // finish off existing token. + ctx->token[idx] = '\0'; + else // this is a token in itself. + { + if (ch == '\n') + ctx->linenum++; + ctx->source++; + ctx->token[idx++] = ch; + ctx->token[idx++] = '\0'; + } // else + return NOFAIL; + + case '\0': + ctx->token[idx] = '\0'; + if (idx != 0) // had any chars? It's a token. + return NOFAIL; + return END_OF_STREAM; + + default: + ctx->source++; + ctx->token[idx++] = ch; + break; + } // switch + + ctx->prevchar = ch; + } // while + + return fail(ctx, "???"); // shouldn't hit this. +} // _tokenize + + +static inline int tokenize(Context *ctx) +{ + const int rc = _tokenize(ctx); + #if DEBUG_TOKENIZER + printf("TOKENIZE: %s '%s'\n", + (rc == END_OF_STREAM) ? "END_OF_STREAM" : + (rc == FAIL) ? "FAIL" : + (rc == NOFAIL) ? "NOFAIL" : "???", + ctx->token); + #endif + return rc; +} // tokenize + + +static inline void pushback(Context *ctx) +{ + #if DEBUG_TOKENIZER + printf("PUSHBACK\n"); + #endif + + if (ctx->pushedback) + fail(ctx, "BUG: Double pushback in parser"); + else + ctx->pushedback = 1; +} // pushback + + +static int nexttoken(Context *ctx, const int ignoreeol, + const int ignorewhitespace, const int eolok, + const int eosok) +{ + int rc = NOFAIL; + + while ((rc = tokenize(ctx)) == NOFAIL) + { + if (strcmp(ctx->token, "\n") == 0) + { + if (ignoreeol) + continue; + else if (!eolok) + return fail(ctx, "Unexpected EOL"); + } // if + + else if (strcmp(ctx->token, " ") == 0) + { + if (ignorewhitespace) + continue; + } // else if + + // skip comments... + else if ((strcmp(ctx->token, "//") == 0) || (strcmp(ctx->token, ";") == 0)) + { + while ((rc = tokenize(ctx)) == NOFAIL) + { + if (strcmp(ctx->token, "\n") == 0) + break; + } // while + } // if + + break; + } // while + + #if DEBUG_TOKENIZER + printf("NEXTTOKEN: %s '%s'\n", + (rc == END_OF_STREAM) ? "END_OF_STREAM" : + (rc == FAIL) ? "FAIL" : + (rc == NOFAIL) ? "NOFAIL" : "???", + ctx->token); + #endif + + if ((rc == END_OF_STREAM) && (!eosok)) + return fail(ctx, "Unexpected EOF"); + + return rc; +} // nexttoken + + +static int require_endline(Context *ctx) +{ + const int rc = nexttoken(ctx, 0, 1, 1, 1); + if (rc == FAIL) + return FAIL; + else if (rc == END_OF_STREAM) + return NOFAIL; // we'll call this an EOL. + else if (strcmp(ctx->token, "\n") != 0) + return fail(ctx, "Endline expected"); + return NOFAIL; +} // require_endline + + + +// !!! FIXME: merge parse_* into mojoshader.c to reduce cut-and-paste. +// !!! FIXME: we need to merge Context, which is the nastiest part. + +static int set_result_shift(Context *ctx, DestArgInfo *info, const int val) +{ + if (info->result_shift != 0) + return fail(ctx, "Multiple result shift modifiers"); + info->result_shift = val; + return NOFAIL; +} // set_result_shift + + +static int parse_register_name(Context *ctx, RegisterType *rtype, int *rnum) +{ + if (nexttoken(ctx, 0, 1, 0, 0) == FAIL) + return FAIL; + + // !!! FIXME: some of these registers are only valid for some shader types. + int neednum = 1; + int regnum = 0; + const char *t = ctx->token; + RegisterType regtype = REG_TYPE_TEMP; + if (strcasecmp(t, "r") == 0) + regtype = REG_TYPE_TEMP; + else if (strcasecmp(t, "v") == 0) + regtype = REG_TYPE_INPUT; + else if (strcasecmp(t, "c") == 0) + regtype = REG_TYPE_CONST; + else if (strcasecmp(t, "i") == 0) + regtype = REG_TYPE_CONSTINT; + else if (strcasecmp(t, "b") == 0) + regtype = REG_TYPE_CONSTBOOL; + else if (strcasecmp(t, "oC") == 0) + regtype = REG_TYPE_COLOROUT; + else if (strcasecmp(t, "oDepth") == 0) + regtype = REG_TYPE_DEPTHOUT; + else if (strcasecmp(t, "s") == 0) + regtype = REG_TYPE_SAMPLER; + else if (strcasecmp(t, "oD") == 0) + regtype = REG_TYPE_ATTROUT; + else if (strcasecmp(t, "l") == 0) + regtype = REG_TYPE_LABEL; + else if (strcasecmp(t, "p") == 0) + regtype = REG_TYPE_PREDICATE; + else if (strcasecmp(t, "aL") == 0) + { + regtype = REG_TYPE_LOOP; + neednum = 0; + } // else if + else if (strcasecmp(t, "o") == 0) + { + if (!shader_is_vertex(ctx) || !shader_version_atleast(ctx, 3, 0)) + return fail(ctx, "Output register not valid in this shader type"); + regtype = REG_TYPE_OUTPUT; + } // else if + else if (strcasecmp(t, "oT") == 0) + { + if (shader_is_vertex(ctx) || shader_version_atleast(ctx, 3, 0)) + return fail(ctx, "Output register not valid in this shader type"); + regtype = REG_TYPE_OUTPUT; + } // else if + else if (strcasecmp(t, "a") == 0) + { + if (!shader_is_vertex(ctx)) + return fail(ctx, "Address register only valid in vertex shaders."); + regtype = REG_TYPE_ADDRESS; + } // else if + else if (strcasecmp(t, "t") == 0) + { + if (!shader_is_pixel(ctx)) + return fail(ctx, "Address register only valid in pixel shaders."); + regtype = REG_TYPE_ADDRESS; + } // else if + else if (strcasecmp(t, "vPos") == 0) + { + regtype = REG_TYPE_MISCTYPE; + regnum = (int) MISCTYPE_TYPE_POSITION; + neednum = 0; + } // else if + else if (strcasecmp(t, "vFace") == 0) + { + regtype = REG_TYPE_MISCTYPE; + regnum = (int) MISCTYPE_TYPE_FACE; + neednum = 0; + } // else if + else if (strcasecmp(t, "oPos") == 0) + { + regtype = REG_TYPE_RASTOUT; + regnum = (int) RASTOUT_TYPE_POSITION; + neednum = 0; + } // else if + else if (strcasecmp(t, "oFog") == 0) + { + regtype = REG_TYPE_RASTOUT; + regnum = (int) RASTOUT_TYPE_FOG; + neednum = 0; + } // else if + else if (strcasecmp(t, "oPts") == 0) + { + regtype = REG_TYPE_RASTOUT; + regnum = (int) RASTOUT_TYPE_POINT_SIZE; + neednum = 0; + } // else if + + //case REG_TYPE_TEMPFLOAT16: // !!! FIXME: don't know this asm string + + else + { + return fail(ctx, "expected register type"); + } // else + + if (neednum) + { + if (nexttoken(ctx, 0, 0, 0, 0) == FAIL) + return FAIL; + + //minor = atoi(ctx->token); + char *endptr = NULL; + const long val = strtol(ctx->token, &endptr, 10); + regnum = (int) val; + if ((*ctx->token == '\0') || (*endptr != '\0')) + return fail(ctx, "Invalid version string"); + } // if + + // split up REG_TYPE_CONST + if (regtype == REG_TYPE_CONST) + { + if (regnum < 2048) + { + regtype = REG_TYPE_CONST; + regnum -= 0; + } // if + else if (regnum < 4096) + { + regtype = REG_TYPE_CONST2; + regnum -= 2048; + } // if + else if (regnum < 6144) + { + regtype = REG_TYPE_CONST3; + regnum -= 4096; + } // if + else if (regnum < 8192) + { + regtype = REG_TYPE_CONST4; + regnum -= 6144; + } // if + else + { + return fail(ctx, "Invalid const register index"); + } // else + } // if + + *rtype = regtype; + *rnum = regnum; + + return NOFAIL; +} // parse_register_name + + +static int parse_destination_token(Context *ctx, DestArgInfo *info) +{ + // !!! FIXME: recheck against the spec for ranges (like RASTOUT values, etc). + + memset(info, '\0', sizeof (info)); + info->token = token; + + // See if there are destination modifiers on the instruction itself... + while (1) + { + if (nexttoken(ctx, 0, 0, 0, 0) == FAIL) + return FAIL; + else if (strcmp(ctx->token, " ") == 0) + break; // done with modifiers. + else if (strcmp(ctx->token, "_") != 0) + return fail(ctx, "Expected modifier or whitespace"); + else if (nexttoken(ctx, 0, 0, 0, 0) == FAIL) + return FAIL; + else if (strcasecmp(ctx->token, "x2") == 0) + set_result_shift(ctx, info, 0x1); + else if (strcasecmp(ctx->token, "x4") == 0) + set_result_shift(ctx, info, 0x2); + else if (strcasecmp(ctx->token, "x8") == 0) + set_result_shift(ctx, info, 0x3); + else if (strcasecmp(ctx->token, "d8") == 0) + set_result_shift(ctx, info, 0xD); + else if (strcasecmp(ctx->token, "d4") == 0) + set_result_shift(ctx, info, 0xE); + else if (strcasecmp(ctx->token, "d2") == 0) + set_result_shift(ctx, info, 0xF); + else if (strcasecmp(ctx->token, "sat") == 0) + info->result_mod |= MOD_SATURATE; + else if (strcasecmp(ctx->token, "pp") == 0) + info->result_mod |= MOD_PP; + else if (strcasecmp(ctx->token, "centroid") == 0) + info->result_mod |= MOD_CENTROID; + else + return fail(ctx, "Expected modifier"); + } // while + + if (nexttoken(ctx, 0, 0, 0, 0) == FAIL) + return FAIL; + + // !!! FIXME: predicates. + if (strcmp(ctx->token, "(") == 0) + return fail(ctx, "Predicates unsupported at this time"); + pushback(ctx); // parse_register_name calls nexttoken(). + + if (parse_register_name(ctx, &info->regtype, &info->regnum) == FAIL) + return FAIL; + + if (nexttoken(ctx, 0, 0, 1, 1) == FAIL) + return FAIL; + + // !!! FIXME: can dest registers do relative addressing? + + if (strcmp(ctx->token, ".") != 0) + { + info->writemask = 0xF; + info->writemask0 = info->writemask1 = info->writemask2 = info->writemask3 = 1; + pushback(ctx); // no explicit writemask; do full mask. + } // if + else if (scalar_register(info->regtype, info->regnum)) + return fail(ctx, "Writemask specified for scalar register"); + else if (nexttoken(ctx, 0, 0, 0, 0) == FAIL) + return FAIL; + else if (ctx->token[0] == '\0') + return fail(ctx, "Invalid writemask"); + else + { + char *ptr = ctx->token; + info->writemask0 = info->writemask1 = info->writemask2 = info->writemask3 = 0; + if (*ptr = 'x') { info->writemask0 = 1; ptr++; } + if (*ptr = 'y') { info->writemask1 = 1; ptr++; } + if (*ptr = 'z') { info->writemask2 = 1; ptr++; } + if (*ptr = 'w') { info->writemask3 = 1; ptr++; } + if ((ptr == ctx->token) && (is_pixel_shader(ctx)) + { + if (*ptr = 'r') { info->writemask0 = 1; ptr++; } + if (*ptr = 'g') { info->writemask1 = 1; ptr++; } + if (*ptr = 'b') { info->writemask2 = 1; ptr++; } + if (*ptr = 'a') { info->writemask3 = 1; ptr++; } + } // if + + if (*ptr != '\0') + return fail(ctx, "Invalid writemask"); + + info->writemask = ( ((info->writemask0 & 0x1) << 0) | + ((info->writemask1 & 0x1) << 1) | + ((info->writemask2 & 0x1) << 2) | + ((info->writemask3 & 0x1) << 3) ); + } // else + + info->orig_writemask = info->writemask; + + // !!! FIXME: cut and paste from mojoshader.c ... + if (info->relative) + { + if (!shader_is_vertex(ctx)) + return fail(ctx, "Relative addressing in non-vertex shader"); + else if (!shader_version_atleast(ctx, 3, 0)) + return fail(ctx, "Relative addressing in vertex shader version < 3.0"); + else if (!ctx->have_ctab) // it's hard to do this efficiently without! + return fail(ctx, "relative addressing unsupported without a CTAB"); + // !!! FIXME: I don't have a shader that has a relative dest currently. + return fail(ctx, "Relative addressing of dest tokens is unsupported"); + } // if + + const int s = info->result_shift; + if (s != 0) + { + if (!shader_is_pixel(ctx)) + return fail(ctx, "Result shift scale in non-pixel shader"); + else if (shader_version_atleast(ctx, 2, 0)) + return fail(ctx, "Result shift scale in pixel shader version >= 2.0"); + else if ( ! (((s >= 1) && (s <= 3)) || ((s >= 0xD) && (s <= 0xF))) ) + return fail(ctx, "Result shift scale isn't 1 to 3, or 13 to 15."); + } // if + + if (info->result_mod & MOD_PP) // Partial precision (pixel shaders only) + { + if (!shader_is_pixel(ctx)) + return fail(ctx, "Partial precision result mod in non-pixel shader"); + } // if + + if (info->result_mod & MOD_CENTROID) // Centroid (pixel shaders only) + { + if (!shader_is_pixel(ctx)) + return fail(ctx, "Centroid result mod in non-pixel shader"); + else if (!ctx->centroid_allowed) // only on DCL opcodes! + return fail(ctx, "Centroid modifier not allowed here"); + } // if + + // !!! FIXME: from msdn: + // "_sat cannot be used with instructions writing to output o# registers." + // !!! FIXME: actually, just go over this page: + // http://msdn.microsoft.com/archive/default.asp?url=/archive/en-us/directx9_c/directx/graphics/reference/shaders/ps_instructionmodifiers.asp + + if (ctx->tokenbufpos >= STATICARRAYLEN(ctx->tokenbuf)) + return fail(ctx, "Too many tokens"); + + ctx->tokenbuf[ctx->tokenbufpos++] = + ( ((((uint32) 0x80000000)) << 0) | + ((((uint32) info->regnum) & 0x7ff) << 0) | + ((((uint32) info->relative) & 0x1) << 13) | + ((((uint32) info->result_mod) & 0xF) << 20) | + ((((uint32) info->result_shift) & 0xF) << 24) | + ((((uint32) info->regtype) & 0x7) << 28) | + ((((uint32) info->regtype) & 0x18) << 8) ); + + return 1; +} // parse_destination_token + + +static int parse_args_NULL(Context *ctx) +{ + return (isfail(ctx) ? FAIL : 1); +} // parse_args_NULL + + +static int parse_args_DEF(Context *ctx) +{ + if (parse_destination_token(ctx, &ctx->dest_arg) == FAIL) + return FAIL; + + if (ctx->dest_arg.relative) // I'm pretty sure this is illegal...? + return fail(ctx, "relative addressing in DEFB"); + +sdfsdf +// !!! FIXME: parse out def. + ctx->dwords[0] = SWAP32(ctx->tokens[0]); + ctx->dwords[1] = SWAP32(ctx->tokens[1]); + ctx->dwords[2] = SWAP32(ctx->tokens[2]); + ctx->dwords[3] = SWAP32(ctx->tokens[3]); + + return 6; +} // parse_args_DEF + + +static int parse_args_DEFB(Context *ctx) +{ + if (parse_destination_token(ctx, &ctx->dest_arg) == FAIL) + return FAIL; + + if (ctx->dest_arg.relative) // I'm pretty sure this is illegal...? + return fail(ctx, "relative addressing in DEFB"); + + ctx->dwords[0] = *(ctx->tokens) ? 1 : 0; + + return 3; +} // parse_args_DEFB + + +static int valid_texture_type(const uint32 ttype) +{ + switch ((const TextureType) ttype) + { + case TEXTURE_TYPE_2D: + case TEXTURE_TYPE_CUBE: + case TEXTURE_TYPE_VOLUME: + return 1; // it's okay. + } // switch + + return 0; +} // valid_texture_type + + +// !!! FIXME: this function is kind of a mess. +// !!! FIXME: cut-and-paste from mojoshader.c ... +static int parse_args_DCL(Context *ctx) +{ + int unsupported = 0; + char usage[sizeof (ctx->token)]; + +static int nexttoken(Context *ctx, const int ignoreeol, + const int ignorewhitespace, const int eolok, + const int eosok) + + char usagestr[sizeof (ctx->token)]; + static const char *usagestrs[] = { + "position", "blendweight", "blendindices", "normal", "psize", + "texcoord", "tangent", "binormal", "tessfactor", "positiont", + "color", "fog", "depth", "sample" + }; + + if (nexttoken(ctx, 0, 0, 0, 0) == FAIL) + return FAIL; + else if (strcmp(ctx->token, " ") == 0) + { + pushback(ctx); + usagestr[0] = '\0'; + } // else if + else if (strcmp(ctx->token, "_") != 0) + return fail(ctx, "Expected register or usage"); + else if (nexttoken(ctx, 0, 0, 0, 0) == FAIL) + return FAIL; + else + strcpy(usagestr, ctx->token); + + if (nexttoken(ctx, 0, 0, 0, 0) == FAIL) + return FAIL; + else if (strcmp(ctx->token, " ") == 0) + return fail(ctx, "Expected whitespace"); + + ctx->centroid_allowed = 1; + ctx->tokenbufpos++; + const int parse_dest_rc = parse_destination_token(ctx, &ctx->dest_arg); + ctx->centroid_allowed = 0; + if (parse_dest_rc == FAIL) + return FAIL; + + if (ctx->dest_arg.result_shift != 0) // I'm pretty sure this is illegal...? + return fail(ctx, "shift scale in DCL"); + else if (ctx->dest_arg.relative) // I'm pretty sure this is illegal...? + return fail(ctx, "relative addressing in DCL"); + + const RegisterType regtype = ctx->dest_arg.regtype; + const int regnum = ctx->dest_arg.regnum; + if ( (shader_is_pixel(ctx)) && (shader_version_atleast(ctx, 3, 0)) ) + { + if (regtype == REG_TYPE_INPUT) + { + token |= + const uint32 usage = (token & 0xF); + const uint32 index = ((token >> 16) & 0xF); + reserved_mask = 0x7FF0FFE0; + ctx->dwords[0] = usage; + ctx->dwords[1] = index; + } // if + + else if (regtype == REG_TYPE_MISCTYPE) + { + const MiscTypeType mt = (MiscTypeType) regnum; + if (mt == MISCTYPE_TYPE_POSITION) + reserved_mask = 0x7FFFFFFF; + else if (mt == MISCTYPE_TYPE_FACE) + { + reserved_mask = 0x7FFFFFFF; + if (!writemask_xyzw(ctx->dest_arg.orig_writemask)) + return fail(ctx, "DCL face writemask must be full"); + else if (ctx->dest_arg.result_mod != 0) + return fail(ctx, "DCL face result modifier must be zero"); + else if (ctx->dest_arg.result_shift != 0) + return fail(ctx, "DCL face shift scale must be zero"); + } // else if + else + { + unsupported = 1; + } // else + + ctx->dwords[0] = (uint32) MOJOSHADER_USAGE_UNKNOWN; + ctx->dwords[1] = 0; + } // else if + + else if (regtype == REG_TYPE_TEXTURE) + { + const uint32 usage = (token & 0xF); + const uint32 index = ((token >> 16) & 0xF); + if (usage == MOJOSHADER_USAGE_TEXCOORD) + { + if (index > 7) + return fail(ctx, "DCL texcoord usage must have 0-7 index"); + } // if + else if (usage == MOJOSHADER_USAGE_COLOR) + { + if (index != 0) + return fail(ctx, "DCL color usage must have 0 index"); + } // else if + else + { + return fail(ctx, "Invalid DCL texture usage"); + } // else + + reserved_mask = 0x7FF0FFE0; + ctx->dwords[0] = usage; + ctx->dwords[1] = index; + } // else if + + else if (regtype == REG_TYPE_SAMPLER) + { + const uint32 ttype = ((token >> 27) & 0xF); + if (!valid_texture_type(ttype)) + return fail(ctx, "unknown sampler texture type"); + reserved_mask = 0x7FFFFFF; + ctx->dwords[0] = ttype; + } // else if + + else + { + unsupported = 1; + } // else + } // if + + else if ( (shader_is_pixel(ctx)) && (shader_version_atleast(ctx, 2, 0)) ) + { + if (regtype == REG_TYPE_INPUT) + { + ctx->dwords[0] = (uint32) MOJOSHADER_USAGE_COLOR; + ctx->dwords[1] = regnum; + reserved_mask = 0x7FFFFFFF; + } // if + else if (regtype == REG_TYPE_TEXTURE) + { + ctx->dwords[0] = (uint32) MOJOSHADER_USAGE_TEXCOORD; + ctx->dwords[1] = regnum; + reserved_mask = 0x7FFFFFFF; + } // else if + else if (regtype == REG_TYPE_SAMPLER) + { + const uint32 ttype = ((token >> 27) & 0xF); + if (!valid_texture_type(ttype)) + return fail(ctx, "unknown sampler texture type"); + reserved_mask = 0x7FFFFFF; + ctx->dwords[0] = ttype; + } // else if + else + { + unsupported = 1; + } // else + } // if + + else if ( (shader_is_vertex(ctx)) && (shader_version_atleast(ctx, 3, 0)) ) + { + if ((regtype == REG_TYPE_INPUT) || (regtype == REG_TYPE_OUTPUT)) + { + const uint32 usage = (token & 0xF); + const uint32 index = ((token >> 16) & 0xF); + reserved_mask = 0x7FF0FFE0; + ctx->dwords[0] = usage; + ctx->dwords[1] = index; + } // if + else + { + unsupported = 1; + } // else + } // else if + + else if ( (shader_is_vertex(ctx)) && (shader_version_atleast(ctx, 2, 0)) ) + { + if (regtype == REG_TYPE_INPUT) + { + const uint32 usage = (token & 0xF); + const uint32 index = ((token >> 16) & 0xF); + reserved_mask = 0x7FF0FFE0; + ctx->dwords[0] = usage; + ctx->dwords[1] = index; + } // if + else + { + unsupported = 1; + } // else + } // else if + + else + { + unsupported = 1; + } // else + + if (unsupported) + return fail(ctx, "invalid DCL register type for this shader model"); + + if ((token & reserved_mask) != 0) + return fail(ctx, "reserved bits in DCL dword aren't zero"); + + return 3; +} // parse_args_DCL + + +static int parse_args_D(Context *ctx) +{ + int retval = 1; + retval += parse_destination_token(ctx, &ctx->dest_arg); + return isfail(ctx) ? FAIL : retval; +} // parse_args_D + + +static int parse_args_S(Context *ctx) +{ + int retval = 1; + retval += parse_source_token(ctx, &ctx->source_args[0]); + return isfail(ctx) ? FAIL : retval; +} // parse_args_S + + +static int parse_args_SS(Context *ctx) +{ + int retval = 1; + retval += parse_source_token(ctx, &ctx->source_args[0]); + retval += parse_source_token(ctx, &ctx->source_args[1]); + return isfail(ctx) ? FAIL : retval; +} // parse_args_SS + + +static int parse_args_DS(Context *ctx) +{ + int retval = 1; + retval += parse_destination_token(ctx, &ctx->dest_arg); + retval += parse_source_token(ctx, &ctx->source_args[0]); + return isfail(ctx) ? FAIL : retval; +} // parse_args_DS + + +static int parse_args_DSS(Context *ctx) +{ + int retval = 1; + retval += parse_destination_token(ctx, &ctx->dest_arg); + retval += parse_source_token(ctx, &ctx->source_args[0]); + retval += parse_source_token(ctx, &ctx->source_args[1]); + return isfail(ctx) ? FAIL : retval; +} // parse_args_DSS + + +static int parse_args_DSSS(Context *ctx) +{ + int retval = 1; + retval += parse_destination_token(ctx, &ctx->dest_arg); + retval += parse_source_token(ctx, &ctx->source_args[0]); + retval += parse_source_token(ctx, &ctx->source_args[1]); + retval += parse_source_token(ctx, &ctx->source_args[2]); + return isfail(ctx) ? FAIL : retval; +} // parse_args_DSSS + + +static int parse_args_DSSSS(Context *ctx) +{ + int retval = 1; + retval += parse_destination_token(ctx, &ctx->dest_arg); + retval += parse_source_token(ctx, &ctx->source_args[0]); + retval += parse_source_token(ctx, &ctx->source_args[1]); + retval += parse_source_token(ctx, &ctx->source_args[2]); + retval += parse_source_token(ctx, &ctx->source_args[3]); + return isfail(ctx) ? FAIL : retval; +} // parse_args_DSSSS + + +static int parse_args_SINCOS(Context *ctx) +{ + // this opcode needs extra registers for sm2 and lower. + if (!shader_version_atleast(ctx, 3, 0)) + return parse_args_DSSS(ctx); + return parse_args_DS(ctx); +} // parse_args_SINCOS + + +static int parse_args_TEXCRD(Context *ctx) +{ + // added extra register in ps_1_4. + if (shader_version_atleast(ctx, 1, 4)) + return parse_args_DS(ctx); + return parse_args_D(ctx); +} // parse_args_TEXCRD + + +static int parse_args_TEXLD(Context *ctx) +{ + // different registers in px_1_3, ps_1_4, and ps_2_0! + if (shader_version_atleast(ctx, 2, 0)) + return parse_args_DSS(ctx); + else if (shader_version_atleast(ctx, 1, 4)) + return parse_args_DS(ctx); + return parse_args_D(ctx); +} // parse_args_TEXLD + + + + + + + +// one args function for each possible sequence of opcode arguments. +typedef int (*args_function)(Context *ctx); + +// one state function for each opcode where we have state machine updates. +typedef void (*state_function)(Context *ctx); + +// Lookup table for instruction opcodes... +typedef struct +{ + const char *opcode_string; + int slots; // number of instruction slots this opcode eats. + MOJOSHADER_shaderType shader_types; // mask of types that can use opcode. + args_function parse_args; + state_function state; +} Instruction; + + +static const Instruction instructions[] = +{ +/* !!! FIXME: push this through mojoshader.c's state machine. + #define INSTRUCTION_STATE(op, opstr, slots, a, t) { \ + opstr, slots, t, parse_args_##a, state_##op \ + }, +*/ + #define INSTRUCTION_STATE(op, opstr, slots, a, t) { \ + opstr, slots, t, parse_args_##a, 0 \ + }, + + #define INSTRUCTION(op, opstr, slots, a, t) { \ + opstr, slots, t, parse_args_##a, 0 \ + }, + + #define MOJOSHADER_DO_INSTRUCTION_TABLE 1 + #include "mojoshader_internal.h" + #undef MOJOSHADER_DO_INSTRUCTION_TABLE + + #undef INSTRUCTION + #undef INSTRUCTION_STATE +}; + +static int parse_condition(Context *ctx, uint32 *controls) +{ + if (nexttoken(ctx, 0, 0, 0, 0) == FAIL) + return 0; + else if (strcmp(ctx->token, "_") != 0) + { + pushback(ctx); + return 0; + } // else if + + if (nexttoken(ctx, 0, 0, 0, 0) == FAIL) + return 0; + else + { + int i; + static const char *comps[] = {"", "gt", "eq", "ge", "lt", "ne", "le"}; + for (i = 1; i < STATICARRAYLEN(comps); i++) + { + if (strcasecmp(ctx->token, comps[i]) == 0) + { + *controls = i; + return 1; + } + } // for + + fail(ctx, "Expected comparison token"); + return 0; + } // else if + + return 0; +} // parse_condition + + +static int parse_instruction_token(Context *ctx) +{ + int coissue = 0; + int predicated = 0; + + if (strcmp(ctx->token, "+") == 0) + { + if (nexttoken(ctx, 0, 1, 0, 0) == FAIL) + return FAIL; + coissue = 1; + } // if + + if (coissue) + { + if (!shader_is_pixel(ctx)) + return fail(ctx, "coissue instruction on non-pixel shader"); + else if (shader_version_atleast(ctx, 2, 0)) + return fail(ctx, "coissue instruction in Shader Model >= 2.0"); + } // if + + int i; + int valid_opcode = 0; + const Instruction *instruction = NULL; + for (i = 0; i < STATICARRAYLEN(instructions); i++) + { + instruction = &instructions[i]; + if (instruction->opcode_string == NULL) + continue; // skip this. + else if (strcasecmp(ctx->token, instruction->opcode_string) != 0) + continue; // not us. + valid_opcode = 1; + break; + } // for + + uint32 opcode = (uint32) i; + uint32 controls = 0; + + if (!valid_opcode) + return failf(ctx, "Unknown instruction '%s'", ctx->token); + + // This might need to be IFC instead of IF. + if (strcmp(instruction->opcode_string, "IF") == 0) + { + if (parse_condition(ctx, &controls)) + opcode = OPCODE_IFC; + } // if + + // This might need to be BREAKC instead of BREAK. + else if (strcmp(instruction->opcode_string, "BREAK") == 0) + { + if (parse_condition(ctx, &controls)) + opcode = OPCODE_BREAKC; + } // else if + + // SETP has a conditional code, always. + else if (strcmp(instruction->opcode_string, "SETP") == 0) + { + if (!parse_condition(ctx, &controls)) + return fail(ctx, "SETP requires a condition"); + } // else if + + instruction = &instructions[opcode]; // ...in case this changed. + + if ((ctx->shader_type & instruction->shader_types) == 0) + { + return failf(ctx, "opcode '%s' not available in this shader type.", + instruction->opcode_string); + } // if + + // !!! FIXME: predicated instructions + + ctx->tokenbufpos = 0; + + const int tokcount = instruction->parse_args(ctx); + if (isfail(ctx)) + return FAIL; + + if (instruction->state != NULL) + instruction->state(ctx); + + if (isfail(ctx)) + return FAIL; + + ctx->instruction_count += instruction->slots; + const uint32 insttoks = shader_version_atleast(ctx, 2, 0) ? tokcount : 0; + + // write out the instruction token. + output_token(ctx, ((opcode & 0xFFFF) << 0) | + ((controls & 0xFF) << 16) | + ((insttoks & 0xF) << 24) | + ((coissue) ? 0x40000000 : 0x00000000) | + ((predicated) ? 0x10000000 : 0x00000000) ); + + // write out the argument tokens. + for (i = 0; i < (tokcount-1); i++) + output_token(ctx, ctx->tokenbuf[i]); + + return NOFAIL; +} // parse_instruction_token + + +static int parse_version_token(Context *ctx) +{ + if (nexttoken(ctx, 1, 1, 0, 0) == FAIL) + return FAIL; + + uint32 shader_type = 0; + + const char *t = ctx->token; + switch (t[0]) + { + case 'v': + shader_type = 0xFFFE; + ctx->shader_type = MOJOSHADER_TYPE_VERTEX; + break; + case 'p': + shader_type = 0xFFFF; + ctx->shader_type = MOJOSHADER_TYPE_PIXEL; + break; + // !!! FIXME: geometry shaders? + default: return fail(ctx, "Expected version string"); + } // switch + + if ((t[1] != 's') || (t[2] != '_') || (t[4] != '_')) + return fail(ctx, "Expected version string"); + + ctx->major_ver = t[3] - '0'; + + const char *minstr = &t[5]; + if (strcasecmp(minstr, "x") == 0) + ctx->minor_ver = 1; + else if (strcasecmp(minstr, "sw") == 0) + ctx->minor_ver = 255; + else if (strcmp(minstr, "0") == 0) + ctx->minor_ver = 0; + else + { + //minor = atoi(minstr); + char *endptr = NULL; + const long val = strtol(minstr, &endptr, 10); + ctx->minor_ver = (uint8) val; + if ((*minstr == '\0') || (*endptr != '\0') || (val < 0) || (val > 255)) + return fail(ctx, "Invalid version string"); + } // else + + // !!! FIXME: 1.x and 4.x? + if ((ctx->major_ver < 2) || (ctx->major_ver > MAX_SHADER_MAJOR)) + return fail(ctx, "Unsupported shader model"); + + if (require_endline(ctx) == FAIL) + return FAIL; + + output_token(ctx, (((uint32) shader_type) << 16) | + (((uint32) ctx->major_ver) << 8) | + (((uint32) ctx->minor_ver) << 0) ); + return NOFAIL; +} // parse_version_token + + +static int parse_phase_token(Context *ctx) +{ + if (require_endline(ctx) == FAIL) + return FAIL; + + // !!! FIXME: needs state; allow only one phase token per shader, I think? + if ( (!shader_is_pixel(ctx)) || (!shader_version_exactly(ctx, 1, 4)) ) + return fail(ctx, "phase token only available in 1.4 pixel shaders"); + output_token(ctx, 0x0000FFFD); // phase token always 0x0000FFFD. + return NOFAIL; +} // parse_phase_token + + +static int parse_end_token(Context *ctx) +{ + if (require_endline(ctx) == FAIL) + return FAIL; + + // We don't emit the end token bits here, since it's valid for a shader + // to not specify an "end" string at all; it's implicit, in that case. + // Instead, we make sure if we see "end" that it's the last thing we see. + if (nexttoken(ctx, 1, 1, 0, 1) != END_OF_STREAM) + return fail(ctx, "Content after END token"); + return NOFAIL; +} // parse_end_token + + +static int parse_token(Context *ctx) +{ + const char *t = ctx->token; + if (strcasecmp(t, "end") == 0) + return parse_end_token(ctx); + else if (strcasecmp(t, "phase") == 0) + return parse_phase_token(ctx); + return parse_instruction_token(ctx); +} // parse_token + + +static Context *build_context(const char *source, MOJOSHADER_malloc m, + MOJOSHADER_free f, void *d) +{ + if (m == NULL) m = internal_malloc; + if (f == NULL) f = internal_free; + + Context *ctx = (Context *) m(sizeof (Context), d); + if (ctx == NULL) + return NULL; + + memset(ctx, '\0', sizeof (Context)); + ctx->malloc = m; + ctx->free = f; + ctx->malloc_data = d; + ctx->source = source; + ctx->linenum = 1; + + return ctx; +} // build_context + + +static void destroy_context(Context *ctx) +{ + if (ctx != NULL) + { + MOJOSHADER_free f = ((ctx->free != NULL) ? ctx->free : internal_free); + void *d = ctx->malloc_data; + if ((ctx->failstr != NULL) && (ctx->failstr != out_of_mem_str)) + f((void *) ctx->failstr, d); + f(ctx, d); + } // if +} // destroy_context + + + +// API entry point... + +const MOJOSHADER_assembleData *MOJOSHADER_assemble(const char *source, + MOJOSHADER_malloc m, MOJOSHADER_free f, void *d) +{ + MOJOSHADER_assembleData *retval = NULL; + Context *ctx = NULL; + + if ( ((m == NULL) && (f != NULL)) || ((m != NULL) && (f == NULL)) ) + return &out_of_mem_data; // supply both or neither. + + ctx = build_context(source, m, f, d); + if (ctx == NULL) + return &out_of_mem_data; + + // Version token always comes first. + parse_version_token(ctx); + + const char *credit = "Generated by MojoShader assembler revision " + MOJOSHADER_CHANGESET + ", http://icculus.org/mojoshader/"; + output_comment_string(ctx, credit); + + // !!! FIXME: insert CTAB here. + + // parse out the rest of the tokens after the version token... + while (nexttoken(ctx, 1, 1, 0, 1) == NOFAIL) + parse_token(ctx); + + output_token(ctx, 0x0000FFFF); // end token always 0x0000FFFF. + + retval = build_assembledata(ctx); + destroy_context(ctx); + return retval; +} // MOJOSHADER_assemble + + +void MOJOSHADER_freeAssembleData(const MOJOSHADER_assembleData *_data) +{ + MOJOSHADER_assembleData *data = (MOJOSHADER_assembleData *) _data; + if ((data == NULL) || (data == &out_of_mem_data)) + return; // no-op. + + MOJOSHADER_free f = (data->free == NULL) ? internal_free : data->free; + void *d = data->malloc_data; + + if (data->output != NULL) // check for NULL in case of dumb free() impl. + f((void *) data->output, d); + + if ((data->error != NULL) && (data->error != out_of_mem_str)) + f((void *) data->error, d); + + f(data, d); +} // MOJOSHADER_freeParseData + +// end of mojoshader_assembler.c ... + diff --git a/mojoshader_internal.h b/mojoshader_internal.h index c7fc8b2a..4672e550 100644 --- a/mojoshader_internal.h +++ b/mojoshader_internal.h @@ -59,6 +59,7 @@ typedef unsigned int uint; // this is a printf() helper. don't use for code. #ifdef _MSC_VER #include #define snprintf _snprintf +#define strcasecmp stricmp typedef unsigned __int8 uint8; typedef unsigned __int16 uint16; typedef unsigned __int32 uint32; @@ -116,7 +117,267 @@ typedef int32_t int32; # define SWAP32(x) (x) #endif + +// we need to reference these by explicit value occasionally... +#define OPCODE_RET 28 +#define OPCODE_IFC 41 +#define OPCODE_BREAKC 45 + +// #define this to force app to supply an allocator, so there's no reference +// to the C runtime's malloc() and free()... +#if MOJOSHADER_FORCE_ALLOCATOR +#define internal_malloc NULL +#define internal_free NULL +#else +static void *internal_malloc(int bytes, void *d) { return malloc(bytes); } +static void internal_free(void *ptr, void *d) { free(ptr); } +#endif + + +// result modifiers. +// !!! FIXME: why isn't this an enum? +#define MOD_SATURATE 0x01 +#define MOD_PP 0x02 +#define MOD_CENTROID 0x04 + +typedef enum +{ + REG_TYPE_TEMP = 0, + REG_TYPE_INPUT = 1, + REG_TYPE_CONST = 2, + REG_TYPE_ADDRESS = 3, + REG_TYPE_TEXTURE = 3, // ALSO 3! + REG_TYPE_RASTOUT = 4, + REG_TYPE_ATTROUT = 5, + REG_TYPE_TEXCRDOUT = 6, + REG_TYPE_OUTPUT = 6, // ALSO 6! + REG_TYPE_CONSTINT = 7, + REG_TYPE_COLOROUT = 8, + REG_TYPE_DEPTHOUT = 9, + REG_TYPE_SAMPLER = 10, + REG_TYPE_CONST2 = 11, + REG_TYPE_CONST3 = 12, + REG_TYPE_CONST4 = 13, + REG_TYPE_CONSTBOOL = 14, + REG_TYPE_LOOP = 15, + REG_TYPE_TEMPFLOAT16 = 16, + REG_TYPE_MISCTYPE = 17, + REG_TYPE_LABEL = 18, + REG_TYPE_PREDICATE = 19, + REG_TYPE_MAX = 19 +} RegisterType; + +typedef enum +{ + TEXTURE_TYPE_2D = 2, + TEXTURE_TYPE_CUBE = 3, + TEXTURE_TYPE_VOLUME = 4, +} TextureType; + +typedef enum +{ + RASTOUT_TYPE_POSITION = 0, + RASTOUT_TYPE_FOG = 1, + RASTOUT_TYPE_POINT_SIZE = 2, + RASTOUT_TYPE_MAX = 2 +} RastOutType; + +typedef enum +{ + MISCTYPE_TYPE_POSITION = 0, + MISCTYPE_TYPE_FACE = 1, + MISCTYPE_TYPE_MAX = 1 +} MiscTypeType; + +// source modifiers. +typedef enum +{ + SRCMOD_NONE, + SRCMOD_NEGATE, + SRCMOD_BIAS, + SRCMOD_BIASNEGATE, + SRCMOD_SIGN, + SRCMOD_SIGNNEGATE, + SRCMOD_COMPLEMENT, + SRCMOD_X2, + SRCMOD_X2NEGATE, + SRCMOD_DZ, + SRCMOD_DW, + SRCMOD_ABS, + SRCMOD_ABSNEGATE, + SRCMOD_NOT, + SRCMOD_TOTAL +} SourceMod; + + +typedef struct +{ + const uint32 *token; // this is the unmolested token in the stream. + int regnum; + int relative; + int writemask; // xyzw or rgba (all four, not split out). + int writemask0; // x or red + int writemask1; // y or green + int writemask2; // z or blue + int writemask3; // w or alpha + int orig_writemask; // writemask before mojoshader tweaks it. + int result_mod; + int result_shift; + RegisterType regtype; +} DestArgInfo; + +typedef struct +{ + const uint32 *token; // this is the unmolested token in the stream. + int regnum; + int swizzle; // xyzw (all four, not split out). + int swizzle_x; + int swizzle_y; + int swizzle_z; + int swizzle_w; + SourceMod src_mod; + RegisterType regtype; + int relative; + RegisterType relative_regtype; + int relative_regnum; + int relative_component; + const VariableList *relative_array; +} SourceArgInfo; + +static inline int scalar_register(const RegisterType regtype, const int regnum) +{ + switch (regtype) + { + case REG_TYPE_DEPTHOUT: + case REG_TYPE_CONSTBOOL: + case REG_TYPE_PREDICATE: + case REG_TYPE_LOOP: + return 1; + + case REG_TYPE_MISCTYPE: + if ( ((const MiscTypeType) regnum) == MISCTYPE_TYPE_FACE ) + return 1; + return 0; + + default: break; + } // switch + + return 0; +} // scalar_register + #endif // _INCLUDE_MOJOSHADER_INTERNAL_H_ + +#if MOJOSHADER_DO_INSTRUCTION_TABLE +// These have to be in the right order! Arrays are indexed by the value +// of the instruction token. + +// INSTRUCTION_STATE means this opcode has to update the state machine +// (we're entering an ELSE block, etc). INSTRUCTION means there's no +// state, just go straight to the emitters. + +// !!! FIXME: Some of these MOJOSHADER_TYPE_ANYs need to have their scope +// !!! FIXME: reduced to just PIXEL or VERTEX. + +INSTRUCTION(NOP, "NOP", 1, NULL, MOJOSHADER_TYPE_ANY) +INSTRUCTION(MOV, "MOV", 1, DS, MOJOSHADER_TYPE_ANY) +INSTRUCTION(ADD, "ADD", 1, DSS, MOJOSHADER_TYPE_ANY) +INSTRUCTION(SUB, "SUB", 1, DSS, MOJOSHADER_TYPE_ANY) +INSTRUCTION(MAD, "MAD", 1, DSSS, MOJOSHADER_TYPE_ANY) +INSTRUCTION(MUL, "MUL", 1, DSS, MOJOSHADER_TYPE_ANY) +INSTRUCTION_STATE(RCP, "RCP", 1, DS, MOJOSHADER_TYPE_ANY) +INSTRUCTION(RSQ, "RSQ", 1, DS, MOJOSHADER_TYPE_ANY) +INSTRUCTION(DP3, "DP3", 1, DSS, MOJOSHADER_TYPE_ANY) +INSTRUCTION_STATE(DP4, "DP4", 1, DSS, MOJOSHADER_TYPE_ANY) +INSTRUCTION(MIN, "MIN", 1, DSS, MOJOSHADER_TYPE_ANY) +INSTRUCTION(MAX, "MAX", 1, DSS, MOJOSHADER_TYPE_ANY) +INSTRUCTION(SLT, "SLT", 1, DSS, MOJOSHADER_TYPE_ANY) +INSTRUCTION(SGE, "SGE", 1, DSS, MOJOSHADER_TYPE_ANY) +INSTRUCTION(EXP, "EXP", 1, DS, MOJOSHADER_TYPE_ANY) +INSTRUCTION_STATE(LOG, "LOG", 1, DS, MOJOSHADER_TYPE_ANY) +INSTRUCTION(LIT, "LIT", 3, DS, MOJOSHADER_TYPE_ANY) +INSTRUCTION(DST, "DST", 1, DSS, MOJOSHADER_TYPE_VERTEX) +INSTRUCTION(LRP, "LRP", 2, DSSS, MOJOSHADER_TYPE_ANY) +INSTRUCTION_STATE(FRC, "FRC", 1, DS, MOJOSHADER_TYPE_ANY) +INSTRUCTION_STATE(M4X4, "M4X4", 4, DSS, MOJOSHADER_TYPE_ANY) +INSTRUCTION_STATE(M4X3, "M4X3", 3, DSS, MOJOSHADER_TYPE_ANY) +INSTRUCTION_STATE(M3X4, "M3X4", 4, DSS, MOJOSHADER_TYPE_ANY) +INSTRUCTION_STATE(M3X3, "M3X3", 3, DSS, MOJOSHADER_TYPE_ANY) +INSTRUCTION_STATE(M3X2, "M3X2", 2, DSS, MOJOSHADER_TYPE_ANY) +INSTRUCTION_STATE(CALL, "CALL", 2, S, MOJOSHADER_TYPE_ANY) +INSTRUCTION_STATE(CALLNZ, "CALLNZ", 3, SS, MOJOSHADER_TYPE_ANY) +INSTRUCTION_STATE(LOOP, "LOOP", 3, SS, MOJOSHADER_TYPE_ANY) +INSTRUCTION_STATE(RET, "RET", 1, NULL, MOJOSHADER_TYPE_ANY) +INSTRUCTION_STATE(ENDLOOP, "ENDLOOP", 2, NULL, MOJOSHADER_TYPE_ANY) +INSTRUCTION_STATE(LABEL, "LABEL", 0, S, MOJOSHADER_TYPE_ANY) +INSTRUCTION_STATE(DCL, "DCL", 0, DCL, MOJOSHADER_TYPE_ANY) +INSTRUCTION_STATE(POW, "POW", 3, DSS, MOJOSHADER_TYPE_ANY) +INSTRUCTION(CRS, "CRS", 2, DSS, MOJOSHADER_TYPE_ANY) +INSTRUCTION(SGN, "SGN", 3, DSSS, MOJOSHADER_TYPE_ANY) +INSTRUCTION(ABS, "ABS", 1, DS, MOJOSHADER_TYPE_ANY) +INSTRUCTION(NRM, "NRM", 3, DS, MOJOSHADER_TYPE_ANY) +INSTRUCTION_STATE(SINCOS, "SINCOS", 8, SINCOS, MOJOSHADER_TYPE_ANY) +INSTRUCTION_STATE(REP, "REP", 3, S, MOJOSHADER_TYPE_ANY) +INSTRUCTION_STATE(ENDREP, "ENDREP", 2, NULL, MOJOSHADER_TYPE_ANY) +INSTRUCTION_STATE(IF, "IF", 3, S, MOJOSHADER_TYPE_ANY) +INSTRUCTION_STATE(IFC, "IF", 3, SS, MOJOSHADER_TYPE_ANY) +INSTRUCTION(ELSE, "ELSE", 1, NULL, MOJOSHADER_TYPE_ANY) // !!! FIXME: state! +INSTRUCTION(ENDIF, "ENDIF", 1, NULL, MOJOSHADER_TYPE_ANY) // !!! FIXME: state! +INSTRUCTION_STATE(BREAK, "BREAK", 1, NULL, MOJOSHADER_TYPE_ANY) +INSTRUCTION_STATE(BREAKC, "BREAK", 3, SS, MOJOSHADER_TYPE_ANY) +INSTRUCTION_STATE(MOVA, "MOVA", 1, DS, MOJOSHADER_TYPE_VERTEX) +INSTRUCTION_STATE(DEFB, "DEFB", 0, DEFB, MOJOSHADER_TYPE_ANY) +INSTRUCTION_STATE(DEFI, "DEFI", 0, DEF, MOJOSHADER_TYPE_ANY) +INSTRUCTION(RESERVED, 0, 0, NULL, MOJOSHADER_TYPE_UNKNOWN) +INSTRUCTION(RESERVED, 0, 0, NULL, MOJOSHADER_TYPE_UNKNOWN) +INSTRUCTION(RESERVED, 0, 0, NULL, MOJOSHADER_TYPE_UNKNOWN) +INSTRUCTION(RESERVED, 0, 0, NULL, MOJOSHADER_TYPE_UNKNOWN) +INSTRUCTION(RESERVED, 0, 0, NULL, MOJOSHADER_TYPE_UNKNOWN) +INSTRUCTION(RESERVED, 0, 0, NULL, MOJOSHADER_TYPE_UNKNOWN) +INSTRUCTION(RESERVED, 0, 0, NULL, MOJOSHADER_TYPE_UNKNOWN) +INSTRUCTION(RESERVED, 0, 0, NULL, MOJOSHADER_TYPE_UNKNOWN) +INSTRUCTION(RESERVED, 0, 0, NULL, MOJOSHADER_TYPE_UNKNOWN) +INSTRUCTION(RESERVED, 0, 0, NULL, MOJOSHADER_TYPE_UNKNOWN) +INSTRUCTION(RESERVED, 0, 0, NULL, MOJOSHADER_TYPE_UNKNOWN) +INSTRUCTION(RESERVED, 0, 0, NULL, MOJOSHADER_TYPE_UNKNOWN) +INSTRUCTION(RESERVED, 0, 0, NULL, MOJOSHADER_TYPE_UNKNOWN) +INSTRUCTION(RESERVED, 0, 0, NULL, MOJOSHADER_TYPE_UNKNOWN) +INSTRUCTION(RESERVED, 0, 0, NULL, MOJOSHADER_TYPE_UNKNOWN) +INSTRUCTION_STATE(TEXCRD, "TEXCRD", 1, TEXCRD, MOJOSHADER_TYPE_PIXEL) +INSTRUCTION_STATE(TEXKILL, "TEXKILL", 2, D, MOJOSHADER_TYPE_PIXEL) +INSTRUCTION_STATE(TEXLD, "TEXLD", 1, TEXLD, MOJOSHADER_TYPE_PIXEL) +INSTRUCTION(TEXBEM, "TEXBEM", 1, DS, MOJOSHADER_TYPE_PIXEL) +INSTRUCTION(TEXBEML, "TEXBEML", 2, DS, MOJOSHADER_TYPE_PIXEL) +INSTRUCTION(TEXREG2AR, "TEXREG2AR", 1, DS, MOJOSHADER_TYPE_PIXEL) +INSTRUCTION(TEXREG2GB, "TEXREG2GB", 1, DS, MOJOSHADER_TYPE_PIXEL) +INSTRUCTION(TEXM3X2PAD, "TEXM3X2PAD", 1, DS, MOJOSHADER_TYPE_PIXEL) +INSTRUCTION(TEXM3X2TEX, "TEXM3X2TEX", 1, DS, MOJOSHADER_TYPE_PIXEL) +INSTRUCTION(TEXM3X3PAD, "TEXM3X3PAD", 1, DS, MOJOSHADER_TYPE_PIXEL) +INSTRUCTION(TEXM3X3TEX, "TEXM3X3TEX", 1, DS, MOJOSHADER_TYPE_PIXEL) +INSTRUCTION(RESERVED, 0, 0, NULL, MOJOSHADER_TYPE_UNKNOWN) +INSTRUCTION(TEXM3X3SPEC, "TEXM3X3SPEC", 1, DSS, MOJOSHADER_TYPE_PIXEL) +INSTRUCTION(TEXM3X3VSPEC, "TEXM3X3VSPEC", 1, DS, MOJOSHADER_TYPE_PIXEL) +INSTRUCTION(EXPP, "EXPP", 1, DS, MOJOSHADER_TYPE_ANY) +INSTRUCTION_STATE(LOGP, "LOGP", 1, DS, MOJOSHADER_TYPE_ANY) +INSTRUCTION_STATE(CND, "CND", 1, DSSS, MOJOSHADER_TYPE_PIXEL) +INSTRUCTION_STATE(DEF, "DEF", 0, DEF, MOJOSHADER_TYPE_ANY) +INSTRUCTION(TEXREG2RGB, "TEXREG2RGB", 1, DS, MOJOSHADER_TYPE_PIXEL) +INSTRUCTION(TEXDP3TEX, "TEXDP3TEX", 1, DS, MOJOSHADER_TYPE_PIXEL) +INSTRUCTION(TEXM3X2DEPTH, "TEXM3X2DEPTH", 1, DS, MOJOSHADER_TYPE_PIXEL) +INSTRUCTION(TEXDP3, "TEXDP3", 1, DS, MOJOSHADER_TYPE_PIXEL) +INSTRUCTION(TEXM3X3, "TEXM3X3", 1, DS, MOJOSHADER_TYPE_PIXEL) +INSTRUCTION(TEXDEPTH, "TEXDEPTH", 1, D, MOJOSHADER_TYPE_PIXEL) +INSTRUCTION_STATE(CMP, "CMP", 1, DSSS, MOJOSHADER_TYPE_PIXEL) +INSTRUCTION(BEM, "BEM", 2, DSS, MOJOSHADER_TYPE_PIXEL) +INSTRUCTION_STATE(DP2ADD, "DP2ADD", 2, DSSS, MOJOSHADER_TYPE_PIXEL) +INSTRUCTION(DSX, "DSX", 2, DS, MOJOSHADER_TYPE_PIXEL) +INSTRUCTION(DSY, "DSY", 2, DS, MOJOSHADER_TYPE_PIXEL) +INSTRUCTION(TEXLDD, "TEXLDD", 3, DSSSS, MOJOSHADER_TYPE_PIXEL) +INSTRUCTION_STATE(SETP, "SETP", 1, DSS, MOJOSHADER_TYPE_ANY) +INSTRUCTION_STATE(TEXLDL, "TEXLDL", 2, DSS, MOJOSHADER_TYPE_ANY) +INSTRUCTION_STATE(BREAKP, "BREAKP", 3, S, MOJOSHADER_TYPE_ANY) +// !!! FIXME: TEXLDB? +#endif + // end of mojoshader_internal.h ... diff --git a/mojoshader_opengl.c b/mojoshader_opengl.c index f3997b5c..d3b65d20 100644 --- a/mojoshader_opengl.c +++ b/mojoshader_opengl.c @@ -247,16 +247,6 @@ static inline int macosx_version_atleast(int x, int y, int z) #endif -// #define this to force app to supply an allocator, so there's no reference -// to the C runtime's malloc() and free()... -#if MOJOSHADER_FORCE_ALLOCATOR -#define internal_malloc NULL -#define internal_free NULL -#else -static void *internal_malloc(int bytes, void *d) { return malloc(bytes); } -static void internal_free(void *ptr, void *d) { free(ptr); } -#endif - static inline void *Malloc(const size_t len) { void *retval = ctx->malloc_fn((int) len, ctx->malloc_data);