From aca99017596f92d1b9abe948371100f1c9409706 Mon Sep 17 00:00:00 2001 From: Ethan Lee Date: Fri, 1 Jan 2016 02:12:19 -0500 Subject: [PATCH] - Add ability to build MojoShader as a shared library - Off by default, turn on BUILD_SHARED in CMake - Add ability to disable shader profiles from CMake - Add MOJOSHADER_glSetVertexAttribDivisor - Behaves like glSetVertexAttribute, but for glVertexAttribDivisor - Add MOJOSHADER_glGetVertexAttribLocation - Allows user to directly call glVertexAttribPointer in their program - Full Effect framework support - API is in a separate header, see mojoshader_effects.h for details - Off by default, turn on EFFECT_SUPPORT in CMake - #define MOJOSHADER_EFFECT_SUPPORT before including mojoshader.h - Add MOJOSHADER_PROFILE_GLSLES shader profile - Basically just GLSL120 without using builtins - Add ability to flip gl_Position via MOJOSHADER_glProgramViewportFlip - You probably shouldn't use this unless you're a project like FNA. - Off by default, turn on FLIP_VIEWPORT in CMake - #define MOJOSHADER_FLIP_RENDERTARGET before including mojoshader.h - Add ability to adjust Z coordinates for [0, 1]-like depth clipping - You probably shouldn't use this unless you're a project like FNA. - Off by default, turn on DEPTH_CLIPPING in CMake - Add ability to build MojoShader with XNA4 vertex texturing behavior - You probably shouldn't use this unless you're reimplementing XNA. - Off by default, turn on XNA4_VERTEXTEXTURE in CMake - Add DECLSPEC/MOJOSHADERCALL to functions/function pointers - This allows for easier interop with other languages, i.e. C# P/Invoke - Fix printing float values for locales where radix is not '.' - Various minor bugfixes, optimizations, Win32 buildfixes, etc. --- CMakeLists.txt | 73 +- mojoshader.c | 586 ++++++++++++++- mojoshader.h | 400 ++++------ mojoshader_common.c | 76 +- mojoshader_effects.c | 1651 +++++++++++++++++++++++++++++++++-------- mojoshader_effects.h | 809 ++++++++++++++++++++ mojoshader_internal.h | 27 +- mojoshader_opengl.c | 783 +++++++++++++++---- utils/testparse.c | 241 +++++- 9 files changed, 3837 insertions(+), 809 deletions(-) create mode 100644 mojoshader_effects.h diff --git a/CMakeLists.txt b/CMakeLists.txt index ff3748c2..19613d38 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,18 @@ CMAKE_MINIMUM_REQUIRED(VERSION 2.6) PROJECT(MojoShader) +OPTION(BUILD_SHARED "Build MojoShader as a shared library" OFF) +OPTION(PROFILE_D3D "Build MojoShader with support for the D3D profile" ON) +OPTION(PROFILE_BYTECODE "Build MojoShader with support for the BYTECODE profile" ON) +OPTION(PROFILE_GLSL120 "Build MojoShader with support for the GLSL120 profile" ON) +OPTION(PROFILE_GLSL "Build MojoShader with support for the GLSL profile" ON) +OPTION(PROFILE_ARB1 "Build MojoShader with support for the ARB1 profile" ON) +OPTION(PROFILE_ARB1_NV "Build MojoShader with support for the ARB1_NV profile" ON) +OPTION(EFFECT_SUPPORT "Build MojoShader with support for Effect framework files" OFF) +OPTION(FLIP_VIEWPORT "Build MojoShader with the ability to flip the GL viewport" OFF) +OPTION(DEPTH_CLIPPING "Build MojoShader with the ability to simulate [0, 1] depth clipping" OFF) +OPTION(XNA4_VERTEXTEXTURE "Build MojoShader with XNA4 vertex texturing behavior" OFF) + INCLUDE_DIRECTORIES(.) FIND_PROGRAM(HG hg DOC "Path to hg command line app: http://www.selenic.com/mercurial/") @@ -61,7 +73,55 @@ ADD_CUSTOM_COMMAND( ARGS -q "-T${CMAKE_CURRENT_SOURCE_DIR}/misc/lempar.c" "${CMAKE_CURRENT_SOURCE_DIR}/mojoshader_parser_hlsl.lemon" ) -ADD_LIBRARY(mojoshader STATIC +IF(APPLE) + find_library(CARBON_FRAMEWORK Carbon) # Stupid Gestalt. +ENDIF(APPLE) + +IF(NOT PROFILE_D3D) + ADD_DEFINITIONS(-DSUPPORT_PROFILE_D3D=0) +ENDIF(NOT PROFILE_D3D) +IF(NOT PROFILE_BYTECODE) + ADD_DEFINITIONS(-DSUPPORT_PROFILE_BYTECODE=0) +ENDIF(NOT PROFILE_BYTECODE) +IF(NOT PROFILE_GLSL120) + ADD_DEFINITIONS(-DSUPPORT_PROFILE_GLSL120=0) +ENDIF(NOT PROFILE_GLSL120) +IF(NOT PROFILE_GLSL) + ADD_DEFINITIONS(-DSUPPORT_PROFILE_GLSL=0) +ENDIF(NOT PROFILE_GLSL) +IF(NOT PROFILE_ARB1) + ADD_DEFINITIONS(-DSUPPORT_PROFILE_ARB1=0) +ENDIF(NOT PROFILE_ARB1) +IF(NOT PROFILE_ARB1_NV) + ADD_DEFINITIONS(-DSUPPORT_PROFILE_ARB1_NV=0) +ENDIF(NOT PROFILE_ARB1_NV) + +IF(EFFECT_SUPPORT) + IF(UNIX) + SET(LIBM -lm) + ENDIF(UNIX) + ADD_DEFINITIONS(-DMOJOSHADER_EFFECT_SUPPORT) +ENDIF(EFFECT_SUPPORT) + +IF(FLIP_VIEWPORT) + ADD_DEFINITIONS(-DMOJOSHADER_FLIP_RENDERTARGET) +ENDIF(FLIP_VIEWPORT) + +IF(DEPTH_CLIPPING) + ADD_DEFINITIONS(-DMOJOSHADER_DEPTH_CLIPPING) +ENDIF(DEPTH_CLIPPING) + +IF(XNA4_VERTEXTEXTURE) + ADD_DEFINITIONS(-DMOJOSHADER_XNA4_VERTEX_TEXTURES) +ENDIF(XNA4_VERTEXTEXTURE) + +IF(BUILD_SHARED) + SET(LIBRARY_FORMAT SHARED) +ELSE(BUILD_SHARED) + SET(LIBRARY_FORMAT STATIC) +ENDIF(BUILD_SHARED) + +ADD_LIBRARY(mojoshader ${LIBRARY_FORMAT} mojoshader.c mojoshader_common.c mojoshader_effects.c @@ -71,10 +131,9 @@ ADD_LIBRARY(mojoshader STATIC mojoshader_assembler.c mojoshader_opengl.c ) - -IF(UNIX) - SET(LIBM -lm) -ENDIF(UNIX) +IF(BUILD_SHARED) + TARGET_LINK_LIBRARIES(mojoshader ${LIBM} ${CARBON_FRAMEWORK}) +ENDIF(BUILD_SHARED) SET_SOURCE_FILES_PROPERTIES( mojoshader_compiler.c @@ -94,10 +153,6 @@ ELSE(NOT RE2C) ) ENDIF(NOT RE2C) -IF(APPLE) - find_library(CARBON_FRAMEWORK Carbon) # Stupid Gestalt. -ENDIF(APPLE) - find_path(SDL2_INCLUDE_DIR SDL.h PATH_SUFFIXES include/SDL2) find_library(SDL2 NAMES SDL2 PATH_SUFFIXES lib) IF(SDL2) diff --git a/mojoshader.c b/mojoshader.c index 19a2014b..7a84e9ba 100644 --- a/mojoshader.c +++ b/mojoshader.c @@ -178,6 +178,9 @@ typedef struct Context #if SUPPORT_PROFILE_GLSL120 int profile_supports_glsl120; #endif +#if SUPPORT_PROFILE_GLSLES + int profile_supports_glsles; +#endif } Context; @@ -198,6 +201,12 @@ typedef struct Context #define support_glsl120(ctx) (0) #endif +#if SUPPORT_PROFILE_GLSLES +#define support_glsles(ctx) ((ctx)->profile_supports_glsles) +#else +#define support_glsles(ctx) (0) +#endif + // Profile entry points... @@ -300,12 +309,12 @@ static inline void Free(Context *ctx, void *ptr) ctx->free(ptr, ctx->malloc_data); } // Free -static void *MallocBridge(int bytes, void *data) +static void * MOJOSHADERCALL MallocBridge(int bytes, void *data) { return Malloc((Context *) data, (size_t) bytes); } // MallocBridge -static void FreeBridge(void *ptr, void *data) +static void MOJOSHADERCALL FreeBridge(void *ptr, void *data) { Free((Context *) data, ptr); } // FreeBridge @@ -446,7 +455,7 @@ static inline void output_blank_line(Context *ctx) static void floatstr(Context *ctx, char *buf, size_t bufsize, float f, int leavedecimal) { - const size_t len = snprintf(buf, bufsize, "%f", f); + const size_t len = MOJOSHADER_printFloat(buf, bufsize, f); if ((len+2) >= bufsize) fail(ctx, "BUG: internal buffer is too small"); else @@ -2185,6 +2194,24 @@ static void emit_GLSL_start(Context *ctx, const char *profilestr) } // else if #endif + #if SUPPORT_PROFILE_GLSLES + else if (strcmp(profilestr, MOJOSHADER_PROFILE_GLSLES) == 0) + { + ctx->profile_supports_glsles = 1; + push_output(ctx, &ctx->preflight); + output_line(ctx, "#version 100"); + if (shader_is_vertex(ctx)) + output_line(ctx, "precision highp float;"); + else + output_line(ctx, "precision mediump float;"); + output_line(ctx, "precision mediump int;"); + output_line(ctx, "varying vec4 v_FrontColor;"); + output_line(ctx, "varying vec4 v_FrontSecondaryColor;"); + output_line(ctx, "varying vec4 v_TexCoord[10];"); // 10 according to SM3 + pop_output(ctx); + } // else if + #endif + else { failf(ctx, "Profile '%s' unsupported or unknown.", profilestr); @@ -2212,6 +2239,15 @@ static void emit_GLSL_end(Context *ctx) set_used_register(ctx, REG_TYPE_COLOROUT, 0, 1); output_line(ctx, "%s_oC0 = %s_r0;", shstr, shstr); } // if + else if (shader_is_vertex(ctx)) + { +#ifdef MOJOSHADER_FLIP_RENDERTARGET + output_line(ctx, "gl_Position.y = gl_Position.y * vpFlip;"); +#endif +#ifdef MOJOSHADER_DEPTH_CLIPPING + output_line(ctx, "gl_Position.z = gl_Position.z * 2.0 - gl_Position.w;"); +#endif + } // else if // force a RET opcode if we're at the end of the stream without one. if (ctx->previous_opcode != OPCODE_RET) @@ -2230,7 +2266,19 @@ static void output_GLSL_uniform_array(Context *ctx, const RegisterType regtype, { char buf[64]; get_GLSL_uniform_array_varname(ctx, regtype, buf, sizeof (buf)); - output_line(ctx, "uniform vec4 %s[%d];", buf, size); + const char *typ; + switch (regtype) + { + case REG_TYPE_CONST: typ = "vec4"; break; + case REG_TYPE_CONSTINT: typ ="ivec4"; break; + case REG_TYPE_CONSTBOOL: typ = "bool"; break; + default: + { + fail(ctx, "BUG: used a uniform we don't know how to define."); + return; + } // default + } // switch + output_line(ctx, "uniform %s %s[%d];", typ, buf, size); } // if } // output_GLSL_uniform_array @@ -2252,6 +2300,10 @@ static void emit_GLSL_finalize(Context *ctx) output_GLSL_uniform_array(ctx, REG_TYPE_CONST, ctx->uniform_float4_count); output_GLSL_uniform_array(ctx, REG_TYPE_CONSTINT, ctx->uniform_int4_count); output_GLSL_uniform_array(ctx, REG_TYPE_CONSTBOOL, ctx->uniform_bool_count); +#ifdef MOJOSHADER_FLIP_RENDERTARGET + if (shader_is_vertex(ctx)) + output_line(ctx, "uniform float vpFlip;"); +#endif pop_output(ctx); } // emit_GLSL_finalize @@ -2535,15 +2587,34 @@ static void emit_GLSL_attribute(Context *ctx, RegisterType regtype, int regnum, case MOJOSHADER_USAGE_COLOR: index_str[0] = '\0'; // no explicit number. if (index == 0) + { +#if SUPPORT_PROFILE_GLSLES + if (support_glsles(ctx)) + usage_str = "v_FrontColor"; + else +#endif usage_str = "gl_FrontColor"; + } // if else if (index == 1) + { +#if SUPPORT_PROFILE_GLSLES + if (support_glsles(ctx)) + usage_str = "v_FrontSecondaryColor"; + else +#endif usage_str = "gl_FrontSecondaryColor"; + } // else if break; case MOJOSHADER_USAGE_FOG: usage_str = "gl_FogFragCoord"; break; case MOJOSHADER_USAGE_TEXCOORD: snprintf(index_str, sizeof (index_str), "%u", (uint) index); +#if SUPPORT_PROFILE_GLSLES + if (support_glsles(ctx)) + usage_str = "v_TexCoord"; + else +#endif usage_str = "gl_TexCoord"; arrayleft = "["; arrayright = "]"; @@ -2609,6 +2680,11 @@ static void emit_GLSL_attribute(Context *ctx, RegisterType regtype, int regnum, if (shader_version_atleast(ctx, 1, 4)) { snprintf(index_str, sizeof (index_str), "%u", (uint) index); +#if SUPPORT_PROFILE_GLSLES + if (support_glsles(ctx)) + usage_str = "v_TexCoord"; + else +#endif usage_str = "gl_TexCoord"; arrayleft = "["; arrayright = "]"; @@ -2619,12 +2695,33 @@ static void emit_GLSL_attribute(Context *ctx, RegisterType regtype, int regnum, { index_str[0] = '\0'; // no explicit number. if (index == 0) + { +#if SUPPORT_PROFILE_GLSLES + if (support_glsles(ctx)) + usage_str = "v_FrontColor"; + else +#endif usage_str = "gl_Color"; + } // if else if (index == 1) + { +#if SUPPORT_PROFILE_GLSLES + if (support_glsles(ctx)) + usage_str = "v_FrontSecondaryColor"; + else +#endif usage_str = "gl_SecondaryColor"; + } // else if else fail(ctx, "unsupported color index"); } // else if + + else if (usage == MOJOSHADER_USAGE_DEPTH) // !!! FIXME: Possibly more! -flibit + { + push_output(ctx, &ctx->globals); + output_line(ctx, "attribute vec4 %s;", var); + pop_output(ctx); + } // else if } // else if else if (regtype == REG_TYPE_MISCTYPE) @@ -6155,6 +6252,7 @@ static const Profile profiles[] = // This is for profiles that extend other profiles... static const struct { const char *from; const char *to; } profileMap[] = { + { MOJOSHADER_PROFILE_GLSLES, MOJOSHADER_PROFILE_GLSL }, { MOJOSHADER_PROFILE_GLSL120, MOJOSHADER_PROFILE_GLSL }, { MOJOSHADER_PROFILE_NV2, MOJOSHADER_PROFILE_ARB1 }, { MOJOSHADER_PROFILE_NV3, MOJOSHADER_PROFILE_ARB1 }, @@ -6843,6 +6941,35 @@ static int parse_args_DCL(Context *ctx) ctx->dwords[0] = usage; ctx->dwords[1] = index; } // 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) + fail(ctx, "DCL texcoord usage must have 0-7 index"); + } // if + else if (usage == MOJOSHADER_USAGE_COLOR) + { + if (index != 0) + fail(ctx, "DCL texcoord usage must have 0 index"); + } // else if + else + fail(ctx, "Invalid DCL texture usage"); + + 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)) + fail(ctx, "Unknown sampler texture type"); + reserved_mask = 0x6FFFFFFF; + ctx->dwords[0] = ttype; + } // else if else { unsupported = 1; @@ -7086,14 +7213,19 @@ static void state_DCL(Context *ctx) else if (shader_is_vertex(ctx)) { - const MOJOSHADER_usage usage = (const MOJOSHADER_usage) ctx->dwords[0]; - const int index = ctx->dwords[1]; - if (usage >= MOJOSHADER_USAGE_TOTAL) + if (regtype == REG_TYPE_SAMPLER) + add_sampler(ctx, regnum, (TextureType) ctx->dwords[0], 0); + else { - fail(ctx, "unknown DCL usage"); - return; - } // if - add_attribute_register(ctx, regtype, regnum, usage, index, wmask, mods); + const MOJOSHADER_usage usage = (const MOJOSHADER_usage) ctx->dwords[0]; + const int index = ctx->dwords[1]; + if (usage >= MOJOSHADER_USAGE_TOTAL) + { + fail(ctx, "unknown DCL usage"); + return; + } // if + add_attribute_register(ctx, regtype, regnum, usage, index, wmask, mods); + } // else } // if else if (shader_is_pixel(ctx)) @@ -7470,8 +7602,6 @@ static void state_IF(Context *ctx) const RegisterType regtype = ctx->source_args[0].regtype; if ((regtype != REG_TYPE_PREDICATE) && (regtype != REG_TYPE_CONSTBOOL)) fail(ctx, "IF src0 must be CONSTBOOL or PREDICATE"); - else if (!replicate_swizzle(ctx->source_args[0].swizzle)) - fail(ctx, "IF src0 must have replicate swizzle"); // !!! FIXME: track if nesting depth. } // state_IF @@ -8061,7 +8191,7 @@ static void parse_constant_table(Context *ctx, const uint32 *tokens, // !!! FIXME: check that (start+target) points to "ps_3_0", etc. ctab->symbol_count = constants; - ctab->symbols = Malloc(ctx, sizeof (MOJOSHADER_symbol) * constants); + ctab->symbols = (MOJOSHADER_symbol *) Malloc(ctx, sizeof (MOJOSHADER_symbol) * constants); if (ctab->symbols == NULL) return; memset(ctab->symbols, '\0', sizeof (MOJOSHADER_symbol) * constants); @@ -8173,8 +8303,8 @@ static void parse_preshader(Context *ctx, uint32 tokcount) if ((tokcount < 2) || (SWAP32(tokens[1]) != PRES_ID)) return; // not a preshader. -#if !SUPPORT_PRESHADERS - fail(ctx, "Preshader found, but preshader support is disabled!"); +#ifndef MOJOSHADER_EFFECT_SUPPORT + fail(ctx, "Preshader found, but effect support is disabled!"); #else assert(ctx->have_preshader == 0); // !!! FIXME: can you have more than one? @@ -8183,10 +8313,12 @@ static void parse_preshader(Context *ctx, uint32 tokcount) // !!! FIXME: I don't know what specific versions signify, but we need to // !!! FIXME: save this to test against the CTAB version field, if // !!! FIXME: nothing else. - // !!! FIXME: 0x02 0x01 is probably the version (fx_2_1), + // !!! FIXME: 0x02 0x0? is probably the version (fx_2_?), // !!! FIXME: and 0x4658 is the magic, like a real shader's version token. - const uint32 okay_version = 0x46580201; - if (SWAP32(tokens[2]) != okay_version) + const uint32 min_version = 0x46580200; + const uint32 max_version = 0x46580201; + const uint32 version = SWAP32(tokens[2]); + if (version < min_version || version > max_version) { fail(ctx, "Unsupported preshader version."); return; // fail because the shader will malfunction w/o this. @@ -8313,7 +8445,7 @@ static void parse_preshader(Context *ctx, uint32 tokcount) // Now we'll figure out the CTAB... CtabData ctabdata = { 0, 0, 0 }; parse_constant_table(ctx, ctab.tokens - 1, ctab.tokcount * 4, - okay_version, 0, &ctabdata); + version, 0, &ctabdata); // preshader owns this now. Don't free it in this function. preshader->symbol_count = ctabdata.symbol_count; @@ -8412,7 +8544,7 @@ static void parse_preshader(Context *ctx, uint32 tokcount) { case 1: // literal from CLIT block. { - if (item >= preshader->literal_count) + if (item > preshader->literal_count) { fail(ctx, "Bogus preshader literal index."); break; @@ -8480,6 +8612,27 @@ static void parse_preshader(Context *ctx, uint32 tokcount) inst++; } // while + + // Registers need to be vec4, round up to nearest 4 + preshader->temp_count = (preshader->temp_count + 3) & ~3; + + unsigned int largest = 0; + const MOJOSHADER_symbol *sym = preshader->symbols; + int i; + for (i = 0; i < preshader->symbol_count; i++, sym++) + { + const unsigned int val = sym->register_index + sym->register_count; + if (val > largest) + largest = val; + } // for + + if (largest > 0) + { + const size_t len = largest * sizeof (float) * 4; + preshader->registers = (float *) Malloc(ctx, len); + memset(preshader->registers, '\0', len); + preshader->register_count = largest; + } // if #endif } // parse_preshader @@ -8702,19 +8855,6 @@ static void free_symbols(MOJOSHADER_free f, void *d, MOJOSHADER_symbol *syms, } // free_symbols -static void free_preshader(MOJOSHADER_free f, void *d, - MOJOSHADER_preshader *preshader) -{ - if (preshader != NULL) - { - f((void *) preshader->literals, d); - f((void *) preshader->instructions, d); - free_symbols(f, d, preshader->symbols, preshader->symbol_count); - f((void *) preshader, d); - } // if -} // free_preshader - - static void destroy_context(Context *ctx) { if (ctx != NULL) @@ -8737,7 +8877,7 @@ static void destroy_context(Context *ctx) free_variable_list(f, d, ctx->variables); errorlist_destroy(ctx->errors); free_symbols(f, d, ctx->ctab.symbols, ctx->ctab.symbol_count); - free_preshader(f, d, ctx->preshader); + MOJOSHADER_freePreshader(ctx->preshader, f, d); f(ctx, d); } // if } // destroy_context @@ -9514,7 +9654,7 @@ void MOJOSHADER_freeParseData(const MOJOSHADER_parseData *_data) f((void *) data->samplers, d); free_symbols(f, d, data->symbols, data->symbol_count); - free_preshader(f, d, data->preshader); + MOJOSHADER_freePreshader(data->preshader, f, d); f(data, d); } // MOJOSHADER_freeParseData @@ -9539,6 +9679,7 @@ int MOJOSHADER_maxShaderModel(const char *profile) PROFILE_SHADER_MODEL(MOJOSHADER_PROFILE_BYTECODE, 3); PROFILE_SHADER_MODEL(MOJOSHADER_PROFILE_GLSL, 3); PROFILE_SHADER_MODEL(MOJOSHADER_PROFILE_GLSL120, 3); + PROFILE_SHADER_MODEL(MOJOSHADER_PROFILE_GLSLES, 3); PROFILE_SHADER_MODEL(MOJOSHADER_PROFILE_ARB1, 2); PROFILE_SHADER_MODEL(MOJOSHADER_PROFILE_NV2, 2); PROFILE_SHADER_MODEL(MOJOSHADER_PROFILE_NV3, 2); @@ -9547,5 +9688,378 @@ int MOJOSHADER_maxShaderModel(const char *profile) return -1; // unknown profile? } // MOJOSHADER_maxShaderModel + +const MOJOSHADER_preshader *MOJOSHADER_parsePreshader(const unsigned char *buf, + const unsigned int _len, + MOJOSHADER_malloc m, + MOJOSHADER_free f, + void *d) +{ + // !!! FIXME: This is copypasta ripped from parse_preshader -flibit + int i; + + const uint32 *tokens = (const uint32 *) buf; + uint32 tokcount = _len / 4; + + // !!! FIXME: This is stupid. -flibit + Context fillerContext; + fillerContext.malloc = m; + fillerContext.free = f; + fillerContext.malloc_data = d; + fillerContext.out_of_memory = 0; + + MOJOSHADER_preshader *preshader = NULL; + + const uint32 version_magic = 0x46580000; + const uint32 min_version = 0x00000200 | version_magic; + const uint32 max_version = 0x00000201 | version_magic; + const uint32 version = SWAP32(tokens[0]); + if (version < min_version || version > max_version) + { + // fail because the shader will malfunction w/o this. + goto parsePreshader_notAPreshader; + } // if + + tokens++; + tokcount++; + + // All sections of a preshader are packed into separate comment tokens, + // inside the containing comment token block. Find them all before + // we start, so we don't care about the order they appear in the file. + PreshaderBlockInfo ctab = { 0, 0, 0 }; + PreshaderBlockInfo prsi = { 0, 0, 0 }; + PreshaderBlockInfo fxlc = { 0, 0, 0 }; + PreshaderBlockInfo clit = { 0, 0, 0 }; + + while (tokcount > 0) + { + uint32 subtokcount = 0; + // !!! FIXME: Passing a NULL ctx! -flibit + if ( (!is_comment_token(&fillerContext, *tokens, &subtokcount)) || + (subtokcount > tokcount) ) + { + goto parsePreshader_notAPreshader; + } // if + + tokens++; + tokcount--; + + const uint32 *nexttokens = tokens + subtokcount; + const uint32 nexttokcount = tokcount - subtokcount; + + if (subtokcount > 0) + { + switch (SWAP32(*tokens)) + { + #define PRESHADER_BLOCK_CASE(id, var) \ + case id##_ID: { \ + if (var.seen) \ + goto parsePreshader_notAPreshader; \ + var.tokens = tokens; \ + var.tokcount = subtokcount; \ + var.seen = 1; \ + break; \ + } + PRESHADER_BLOCK_CASE(CTAB, ctab); + PRESHADER_BLOCK_CASE(PRSI, prsi); + PRESHADER_BLOCK_CASE(FXLC, fxlc); + PRESHADER_BLOCK_CASE(CLIT, clit); + default: + // Bogus preshader section + goto parsePreshader_notAPreshader; + #undef PRESHADER_BLOCK_CASE + } // switch + } // if + + tokens = nexttokens; + tokcount = nexttokcount; + } // while + + if (!ctab.seen) // No CTAB block in preshader + goto parsePreshader_notAPreshader; + if (!fxlc.seen) // No FXLC block in preshader + goto parsePreshader_notAPreshader; + if (!clit.seen) // No CLIT block in preshader + goto parsePreshader_notAPreshader; + + preshader = (MOJOSHADER_preshader *) m(sizeof (MOJOSHADER_preshader), d); + if (preshader == NULL) + goto parsePreshader_outOfMemory; + memset(preshader, '\0', sizeof (MOJOSHADER_preshader)); + + // Let's set up the constant literals first... + if (clit.tokcount == 0) // Bogus CLIT block in preshader + goto parsePreshader_notAPreshader; + else + { + const uint32 lit_count = SWAP32(clit.tokens[1]); + if (lit_count > ((clit.tokcount - 2) / 2)) + // Bogus CLIT block in preshader + goto parsePreshader_notAPreshader; + else if (lit_count > 0) + { + preshader->literal_count = (unsigned int) lit_count; + assert(sizeof (double) == 8); // just in case. + const size_t len = sizeof (double) * lit_count; + preshader->literals = (double *) m(len, d); + if (preshader->literals == NULL) + goto parsePreshader_notAPreshader; // oh well. + const double *litptr = (const double *) (clit.tokens + 2); + for (i = 0; i < lit_count; i++) + preshader->literals[i] = SWAPDBL(litptr[i]); + } // else if + } // else + + // Parse out the PRSI block. This is used to map the output registers. + uint32 output_map_count = 0; + const uint32 *output_map = NULL; + if (prsi.seen) + { + if (prsi.tokcount < 8) // Bogus preshader PRSI data + goto parsePreshader_notAPreshader; + + //const uint32 first_output_reg = SWAP32(prsi.tokens[1]); + // !!! FIXME: there are a lot of fields here I don't know about. + // !!! FIXME: maybe [2] and [3] are for int4 and bool registers? + //const uint32 output_reg_count = SWAP32(prsi.tokens[4]); + // !!! FIXME: maybe [5] and [6] are for int4 and bool registers? + output_map_count = SWAP32(prsi.tokens[7]); + + prsi.tokcount -= 8; + prsi.tokens += 8; + + if (prsi.tokcount < ((output_map_count + 1) * 2)) + // Bogus preshader PRSI data + goto parsePreshader_notAPreshader; + + output_map = prsi.tokens; + } + + // Now we'll figure out the CTAB... + CtabData ctabdata = { 0, 0, 0 }; + parse_constant_table(&fillerContext, ctab.tokens - 1, ctab.tokcount * 4, + version, 0, &ctabdata); + + // preshader owns this now. Don't free it in this function. + preshader->symbol_count = ctabdata.symbol_count; + preshader->symbols = ctabdata.symbols; + + if (!ctabdata.have_ctab) // Bogus preshader CTAB data + goto parsePreshader_notAPreshader; + + // The FXLC block has the actual instructions... + uint32 opcode_count = SWAP32(fxlc.tokens[1]); + + size_t len = sizeof (MOJOSHADER_preshaderInstruction) * opcode_count; + preshader->instruction_count = (unsigned int) opcode_count; + preshader->instructions = (MOJOSHADER_preshaderInstruction *) + m(len, d); + if (preshader->instructions == NULL) + goto parsePreshader_outOfMemory; + memset(preshader->instructions, '\0', len); + + fxlc.tokens += 2; + fxlc.tokcount -= 2; + if (opcode_count > (fxlc.tokcount / 2)) // Bogus preshader FXLC block + goto parsePreshader_notAPreshader; + + MOJOSHADER_preshaderInstruction *inst = preshader->instructions; + while (opcode_count--) + { + const uint32 opcodetok = SWAP32(fxlc.tokens[0]); + MOJOSHADER_preshaderOpcode opcode = MOJOSHADER_PRESHADEROP_NOP; + switch ((opcodetok >> 16) & 0xFFFF) + { + case 0x1000: opcode = MOJOSHADER_PRESHADEROP_MOV; break; + case 0x1010: opcode = MOJOSHADER_PRESHADEROP_NEG; break; + case 0x1030: opcode = MOJOSHADER_PRESHADEROP_RCP; break; + case 0x1040: opcode = MOJOSHADER_PRESHADEROP_FRC; break; + case 0x1050: opcode = MOJOSHADER_PRESHADEROP_EXP; break; + case 0x1060: opcode = MOJOSHADER_PRESHADEROP_LOG; break; + case 0x1070: opcode = MOJOSHADER_PRESHADEROP_RSQ; break; + case 0x1080: opcode = MOJOSHADER_PRESHADEROP_SIN; break; + case 0x1090: opcode = MOJOSHADER_PRESHADEROP_COS; break; + case 0x10A0: opcode = MOJOSHADER_PRESHADEROP_ASIN; break; + case 0x10B0: opcode = MOJOSHADER_PRESHADEROP_ACOS; break; + case 0x10C0: opcode = MOJOSHADER_PRESHADEROP_ATAN; break; + case 0x2000: opcode = MOJOSHADER_PRESHADEROP_MIN; break; + case 0x2010: opcode = MOJOSHADER_PRESHADEROP_MAX; break; + case 0x2020: opcode = MOJOSHADER_PRESHADEROP_LT; break; + case 0x2030: opcode = MOJOSHADER_PRESHADEROP_GE; break; + case 0x2040: opcode = MOJOSHADER_PRESHADEROP_ADD; break; + case 0x2050: opcode = MOJOSHADER_PRESHADEROP_MUL; break; + case 0x2060: opcode = MOJOSHADER_PRESHADEROP_ATAN2; break; + case 0x2080: opcode = MOJOSHADER_PRESHADEROP_DIV; break; + case 0x3000: opcode = MOJOSHADER_PRESHADEROP_CMP; break; + case 0x3010: opcode = MOJOSHADER_PRESHADEROP_MOVC; break; + case 0x5000: opcode = MOJOSHADER_PRESHADEROP_DOT; break; + case 0x5020: opcode = MOJOSHADER_PRESHADEROP_NOISE; break; + case 0xA000: opcode = MOJOSHADER_PRESHADEROP_MIN_SCALAR; break; + case 0xA010: opcode = MOJOSHADER_PRESHADEROP_MAX_SCALAR; break; + case 0xA020: opcode = MOJOSHADER_PRESHADEROP_LT_SCALAR; break; + case 0xA030: opcode = MOJOSHADER_PRESHADEROP_GE_SCALAR; break; + case 0xA040: opcode = MOJOSHADER_PRESHADEROP_ADD_SCALAR; break; + case 0xA050: opcode = MOJOSHADER_PRESHADEROP_MUL_SCALAR; break; + case 0xA060: opcode = MOJOSHADER_PRESHADEROP_ATAN2_SCALAR; break; + case 0xA080: opcode = MOJOSHADER_PRESHADEROP_DIV_SCALAR; break; + case 0xD000: opcode = MOJOSHADER_PRESHADEROP_DOT_SCALAR; break; + case 0xD020: opcode = MOJOSHADER_PRESHADEROP_NOISE_SCALAR; break; + default: + // Unknown preshader opcode + goto parsePreshader_notAPreshader; + } // switch + + uint32 operand_count = SWAP32(fxlc.tokens[1]) + 1; // +1 for dest. + + inst->opcode = opcode; + inst->element_count = (unsigned int) (opcodetok & 0xFF); + inst->operand_count = (unsigned int) operand_count; + + fxlc.tokens += 2; + fxlc.tokcount -= 2; + if ((operand_count * 3) > fxlc.tokcount) + // Bogus preshader FXLC block + goto parsePreshader_notAPreshader; + + MOJOSHADER_preshaderOperand *operand = inst->operands; + while (operand_count--) + { + const unsigned int item = (unsigned int) SWAP32(fxlc.tokens[2]); + + // !!! FIXME: Is this used anywhere other than INPUT? -flibit + const uint32 numArrays = SWAP32(fxlc.tokens[0]); + + switch (SWAP32(fxlc.tokens[1])) + { + case 1: // literal from CLIT block. + { + if ((item + inst->element_count) >= preshader->literal_count) + // Bogus preshader literal index + goto parsePreshader_notAPreshader; + operand->type = MOJOSHADER_PRESHADEROPERAND_LITERAL; + break; + } // case + + case 2: // item from ctabdata. + { + MOJOSHADER_symbol *sym = ctabdata.symbols; + for (i = 0; i < ctabdata.symbol_count; i++, sym++) + { + const uint32 base = sym->register_index * 4; + const uint32 count = sym->register_count * 4; + assert(sym->register_set==MOJOSHADER_SYMREGSET_FLOAT4); + if ( (base <= item) && ((base + count) >= (item + inst->element_count)) ) + break; + } // for + if (i == ctabdata.symbol_count) + // Bogus preshader input index + goto parsePreshader_notAPreshader; + operand->type = MOJOSHADER_PRESHADEROPERAND_INPUT; + if (numArrays > 0) + { + // malloc the array symbol name array + const uint32 siz = numArrays * sizeof (uint32); + operand->array_register_count = numArrays; + operand->array_registers = (uint32 *) m(siz, d); + memset(operand->array_registers, '\0', siz); + // Get each register base, indicating the arrays used. + for (i = 0; i < numArrays; i++) + { + const uint32 jmp = SWAP32(fxlc.tokens[4]); + const uint32 bigjmp = (jmp >> 4) * 4; + const uint32 ltljmp = (jmp >> 2) & 3; + operand->array_registers[i] = bigjmp + ltljmp; + fxlc.tokens += 2; + fxlc.tokcount -= 2; + } // for + } // if + break; + } // case + + case 4: + { + operand->type = MOJOSHADER_PRESHADEROPERAND_OUTPUT; + if (!prsi.seen) + // No PRSI tokens, no output map. + continue; + for (i = 0; i < output_map_count; i++) + { + const uint32 base = output_map[(i*2)] * 4; + const uint32 count = output_map[(i*2)+1] * 4; + if ( (base <= item) && ((base + count) >= (item + inst->element_count)) ) + break; + } // for + if (i == output_map_count) + // Bogus preshader output index + goto parsePreshader_notAPreshader; + break; + } // case + + case 7: + { + operand->type = MOJOSHADER_PRESHADEROPERAND_TEMP; + if (item >= preshader->temp_count) + preshader->temp_count = item + inst->element_count; + break; + } // case + } // switch + + operand->index = item; + + fxlc.tokens += 3; + fxlc.tokcount -= 3; + operand++; + } // while + + inst++; + } // while + + // Registers need to be vec4, round up to nearest 4 + preshader->temp_count = (preshader->temp_count + 3) & ~3; + + unsigned int largest = 0; + const MOJOSHADER_symbol *sym = preshader->symbols; + for (i = 0; i < preshader->symbol_count; i++, sym++) + { + const unsigned int val = sym->register_index + sym->register_count; + if (val > largest) + largest = val; + } // for + + if (largest > 0) + { + const size_t len = largest * sizeof (float) * 4; + preshader->registers = (float *) m(len, d); + memset(preshader->registers, '\0', len); + preshader->register_count = largest; + } // if + + return preshader; + +parsePreshader_notAPreshader: +parsePreshader_outOfMemory: + MOJOSHADER_freePreshader(preshader, f, d); + return NULL; +} // MOJOSHADER_parsePreshader + + +void MOJOSHADER_freePreshader(const MOJOSHADER_preshader *preshader, + MOJOSHADER_free f, + void *d) +{ + int i, j; + if (preshader != NULL) + { + f((void *) preshader->literals, d); + for (i = 0; i < preshader->instruction_count; i++) + for (j = 0; j < preshader->instructions[i].operand_count; j++) + f((void *) preshader->instructions[i].operands[j].array_registers, d); + f((void *) preshader->instructions, d); + f((void *) preshader->registers, d); + free_symbols(f, d, preshader->symbols, preshader->symbol_count); + f((void *) preshader, d); + } // if +} // MOJOSHADER_freePreshader + // end of mojoshader.c ... diff --git a/mojoshader.h b/mojoshader.h index 89dcf2e1..2c50b6e2 100644 --- a/mojoshader.h +++ b/mojoshader.h @@ -27,6 +27,22 @@ extern "C" { #define MOJOSHADER_CHANGESET "???" #endif +#ifndef DECLSPEC +#ifdef _WIN32 +#define DECLSPEC __declspec(dllexport) +#else +#define DECLSPEC +#endif +#endif + +#ifndef MOJOSHADERCALL +#ifdef _WIN32 +#define MOJOSHADERCALL __stdcall +#else +#define MOJOSHADERCALL +#endif +#endif + /* * For determining the version of MojoShader you are using: * const int compiled_against = MOJOSHADER_VERSION; @@ -34,7 +50,7 @@ extern "C" { * * The version is a single integer that increments, not a major/minor value. */ -int MOJOSHADER_version(void); +DECLSPEC int MOJOSHADER_version(void); /* * For determining the revision control changeset of MojoShader you are using: @@ -47,7 +63,7 @@ int MOJOSHADER_version(void); * * Do not attempt to free this string; it's statically allocated. */ -const char *MOJOSHADER_changeset(void); +DECLSPEC const char *MOJOSHADER_changeset(void); /* * These allocators work just like the C runtime's malloc() and free() @@ -57,8 +73,8 @@ const char *MOJOSHADER_changeset(void); * callbacks, in case you need instance-specific data...it is passed through * to your allocator unmolested, and can be NULL if you like. */ -typedef void *(*MOJOSHADER_malloc)(int bytes, void *data); -typedef void (*MOJOSHADER_free)(void *ptr, void *data); +typedef void *(MOJOSHADERCALL *MOJOSHADER_malloc)(int bytes, void *data); +typedef void (MOJOSHADERCALL *MOJOSHADER_free)(void *ptr, void *data); /* @@ -428,6 +444,8 @@ typedef struct MOJOSHADER_preshaderOperand { MOJOSHADER_preshaderOperandType type; unsigned int index; + unsigned int array_register_count; + unsigned int *array_registers; } MOJOSHADER_preshaderOperand; typedef struct MOJOSHADER_preshaderInstruction @@ -435,7 +453,7 @@ typedef struct MOJOSHADER_preshaderInstruction MOJOSHADER_preshaderOpcode opcode; unsigned int element_count; unsigned int operand_count; - MOJOSHADER_preshaderOperand operands[3]; + MOJOSHADER_preshaderOperand operands[4]; } MOJOSHADER_preshaderInstruction; typedef struct MOJOSHADER_preshader @@ -447,6 +465,8 @@ typedef struct MOJOSHADER_preshader MOJOSHADER_symbol *symbols; unsigned int instruction_count; MOJOSHADER_preshaderInstruction *instructions; + unsigned int register_count; + float *registers; } MOJOSHADER_preshader; /* @@ -650,6 +670,11 @@ typedef struct MOJOSHADER_parseData */ #define MOJOSHADER_PROFILE_GLSL120 "glsl120" +/* + * Profile string for GLSL ES: minor changes to GLSL output for ES compliance. + */ +#define MOJOSHADER_PROFILE_GLSLES "glsles" + /* * Profile string for OpenGL ARB 1.0 shaders: GL_ARB_(vertex|fragment)_program. */ @@ -676,7 +701,7 @@ typedef struct MOJOSHADER_parseData /* * Determine the highest supported Shader Model for a profile. */ -int MOJOSHADER_maxShaderModel(const char *profile); +DECLSPEC int MOJOSHADER_maxShaderModel(const char *profile); /* @@ -732,16 +757,17 @@ int MOJOSHADER_maxShaderModel(const char *profile); * (tokenbuf) remains intact for the duration of the call. This allows you * to parse several shaders on separate CPU cores at the same time. */ -const MOJOSHADER_parseData *MOJOSHADER_parse(const char *profile, - const unsigned char *tokenbuf, - const unsigned int bufsize, - const MOJOSHADER_swizzle *swiz, - const unsigned int swizcount, - const MOJOSHADER_samplerMap *smap, - const unsigned int smapcount, - MOJOSHADER_malloc m, - MOJOSHADER_free f, - void *d); +DECLSPEC const MOJOSHADER_parseData *MOJOSHADER_parse(const char *profile, + const unsigned char *tokenbuf, + const unsigned int bufsize, + const MOJOSHADER_swizzle *swiz, + const unsigned int swizcount, + const MOJOSHADER_samplerMap *smap, + const unsigned int smapcount, + MOJOSHADER_malloc m, + MOJOSHADER_free f, + void *d); + /* * Call this to dispose of parsing results when you are done with them. @@ -752,152 +778,25 @@ const MOJOSHADER_parseData *MOJOSHADER_parse(const char *profile, * This function is thread safe, so long as any allocator you passed into * MOJOSHADER_parse() is, too. */ -void MOJOSHADER_freeParseData(const MOJOSHADER_parseData *data); - - -/* Effects interface... */ /* !!! FIXME: THIS API IS NOT STABLE YET! */ - -typedef struct MOJOSHADER_effectParam -{ - const char *name; - const char *semantic; -} MOJOSHADER_effectParam; - -typedef struct MOJOSHADER_effectState -{ - unsigned int type; -} MOJOSHADER_effectState; - -typedef struct MOJOSHADER_effectPass -{ - const char *name; - unsigned int state_count; - MOJOSHADER_effectState *states; -} MOJOSHADER_effectPass; - -typedef struct MOJOSHADER_effectTechnique -{ - const char *name; - unsigned int pass_count; - MOJOSHADER_effectPass *passes; -} MOJOSHADER_effectTechnique; - -typedef struct MOJOSHADER_effectTexture -{ - unsigned int param; - const char *name; -} MOJOSHADER_effectTexture; - -typedef struct MOJOSHADER_effectShader -{ - unsigned int technique; - unsigned int pass; - const MOJOSHADER_parseData *shader; -} MOJOSHADER_effectShader; - -/* - * Structure used to return data from parsing of an effect file... - */ -/* !!! FIXME: most of these ints should be unsigned. */ -typedef struct MOJOSHADER_effect -{ - /* - * The number of elements pointed to by (errors). - */ - int error_count; - - /* - * (error_count) elements of data that specify errors that were generated - * by parsing this shader. - * This can be NULL if there were no errors or if (error_count) is zero. - */ - MOJOSHADER_error *errors; - - /* - * The name of the profile used to parse the shader. Will be NULL on error. - */ - const char *profile; - - /* - * The number of params pointed to by (params). - */ - int param_count; - - /* - * (param_count) elements of data that specify parameter bind points for - * this effect. - * This can be NULL on error or if (param_count) is zero. - */ - MOJOSHADER_effectParam *params; - - /* - * The number of elements pointed to by (techniques). - */ - int technique_count; +DECLSPEC void MOJOSHADER_freeParseData(const MOJOSHADER_parseData *data); - /* - * (technique_count) elements of data that specify techniques used in - * this effect. Each technique contains a series of passes, and each pass - * specifies state and shaders that affect rendering. - * This can be NULL on error or if (technique_count) is zero. - */ - MOJOSHADER_effectTechnique *techniques; - - /* - * The number of elements pointed to by (textures). - */ - int texture_count; - - /* - * (texture_count) elements of data that specify textures used in - * this effect. - * This can be NULL on error or if (texture_count) is zero. - */ - MOJOSHADER_effectTexture *textures; - - /* - * The number of elements pointed to by (shaders). - */ - int shader_count; - - /* - * (shader_count) elements of data that specify shaders used in - * this effect. - * This can be NULL on error or if (shader_count) is zero. - */ - MOJOSHADER_effectShader *shaders; - - /* - * This is the malloc implementation you passed to MOJOSHADER_parseEffect(). - */ - MOJOSHADER_malloc malloc; - /* - * This is the free implementation you passed to MOJOSHADER_parseEffect(). - */ - MOJOSHADER_free free; +/* !!! FIXME: document me. */ +DECLSPEC const MOJOSHADER_preshader *MOJOSHADER_parsePreshader(const unsigned char *buf, + const unsigned int len, + MOJOSHADER_malloc m, + MOJOSHADER_free f, + void *d); - /* - * This is the pointer you passed as opaque data for your allocator. - */ - void *malloc_data; -} MOJOSHADER_effect; /* !!! FIXME: document me. */ -const MOJOSHADER_effect *MOJOSHADER_parseEffect(const char *profile, - const unsigned char *buf, - const unsigned int _len, - const MOJOSHADER_swizzle *swiz, - const unsigned int swizcount, - const MOJOSHADER_samplerMap *smap, - const unsigned int smapcount, - MOJOSHADER_malloc m, - MOJOSHADER_free f, - void *d); +DECLSPEC void MOJOSHADER_freePreshader(const MOJOSHADER_preshader *preshader, + MOJOSHADER_free f, + void *d); -/* !!! FIXME: document me. */ -void MOJOSHADER_freeEffect(const MOJOSHADER_effect *effect); +/* Effects interface... */ +#include "mojoshader_effects.h" /* Preprocessor interface... */ @@ -1001,7 +900,7 @@ typedef struct MOJOSHADER_preprocessData * * If you supply an includeOpen callback, you must supply includeClose, too. */ -typedef int (*MOJOSHADER_includeOpen)(MOJOSHADER_includeType inctype, +typedef int (MOJOSHADERCALL *MOJOSHADER_includeOpen)(MOJOSHADER_includeType inctype, const char *fname, const char *parent, const char **outdata, unsigned int *outbytes, MOJOSHADER_malloc m, MOJOSHADER_free f, void *d); @@ -1019,7 +918,7 @@ typedef int (*MOJOSHADER_includeOpen)(MOJOSHADER_includeType inctype, * * If you supply an includeClose callback, you must supply includeOpen, too. */ -typedef void (*MOJOSHADER_includeClose)(const char *data, +typedef void (MOJOSHADERCALL *MOJOSHADER_includeClose)(const char *data, MOJOSHADER_malloc m, MOJOSHADER_free f, void *d); @@ -1079,7 +978,7 @@ typedef void (*MOJOSHADER_includeClose)(const char *data, * call. This allows you to preprocess several shaders on separate CPU cores * at the same time. */ -const MOJOSHADER_preprocessData *MOJOSHADER_preprocess(const char *filename, +DECLSPEC const MOJOSHADER_preprocessData *MOJOSHADER_preprocess(const char *filename, const char *source, unsigned int sourcelen, const MOJOSHADER_preprocessorDefine *defines, unsigned int define_count, @@ -1097,7 +996,7 @@ const MOJOSHADER_preprocessData *MOJOSHADER_preprocess(const char *filename, * This function is thread safe, so long as any allocator you passed into * MOJOSHADER_preprocess() is, too. */ -void MOJOSHADER_freePreprocessData(const MOJOSHADER_preprocessData *data); +DECLSPEC void MOJOSHADER_freePreprocessData(const MOJOSHADER_preprocessData *data); /* Assembler interface... */ @@ -1159,7 +1058,7 @@ void MOJOSHADER_freePreprocessData(const MOJOSHADER_preprocessData *data); * call. This allows you to assemble several shaders on separate CPU cores * at the same time. */ -const MOJOSHADER_parseData *MOJOSHADER_assemble(const char *filename, +DECLSPEC const MOJOSHADER_parseData *MOJOSHADER_assemble(const char *filename, const char *source, unsigned int sourcelen, const char **comments, unsigned int comment_count, const MOJOSHADER_symbol *symbols, @@ -2010,7 +1909,7 @@ typedef struct MOJOSHADER_astData * call. This allows you to parse several shaders on separate CPU cores * at the same time. */ -const MOJOSHADER_astData *MOJOSHADER_parseAst(const char *srcprofile, +DECLSPEC const MOJOSHADER_astData *MOJOSHADER_parseAst(const char *srcprofile, const char *filename, const char *source, unsigned int sourcelen, const MOJOSHADER_preprocessorDefine *defs, @@ -2033,7 +1932,7 @@ const MOJOSHADER_astData *MOJOSHADER_parseAst(const char *srcprofile, * This function is thread safe, so long as any allocator you passed into * MOJOSHADER_parseAst() is, too. */ -void MOJOSHADER_freeAstData(const MOJOSHADER_astData *data); +DECLSPEC void MOJOSHADER_freeAstData(const MOJOSHADER_astData *data); /* Intermediate Representation interface... */ @@ -2450,7 +2349,7 @@ typedef struct MOJOSHADER_compileData * call. This allows you to compile several shaders on separate CPU cores * at the same time. */ -const MOJOSHADER_compileData *MOJOSHADER_compile(const char *srcprofile, +DECLSPEC const MOJOSHADER_compileData *MOJOSHADER_compile(const char *srcprofile, const char *filename, const char *source, unsigned int sourcelen, const MOJOSHADER_preprocessorDefine *defs, @@ -2470,7 +2369,7 @@ const MOJOSHADER_compileData *MOJOSHADER_compile(const char *srcprofile, * This function is thread safe, so long as any allocator you passed into * MOJOSHADER_compile() is, too. */ -void MOJOSHADER_freeCompileData(const MOJOSHADER_compileData *data); +DECLSPEC void MOJOSHADER_freeCompileData(const MOJOSHADER_compileData *data); /* OpenGL interface... */ @@ -2493,7 +2392,7 @@ void MOJOSHADER_freeCompileData(const MOJOSHADER_compileData *data); * to check two places to find the desired entry point, depending on your * platform (Windows might need to look in OpenGL32.dll and use WGL, etc). */ -typedef void *(*MOJOSHADER_glGetProcAddress)(const char *fnname, void *data); +typedef void *(MOJOSHADERCALL *MOJOSHADER_glGetProcAddress)(const char *fnname, void *data); /* @@ -2544,11 +2443,11 @@ typedef struct MOJOSHADER_glProgram MOJOSHADER_glProgram; * safe, you should probably only call this from the same thread that created * the GL context. */ -int MOJOSHADER_glAvailableProfiles(MOJOSHADER_glGetProcAddress lookup, - void *lookup_d, - const char **profs, const int size, - MOJOSHADER_malloc m, MOJOSHADER_free f, - void *malloc_d); +DECLSPEC int MOJOSHADER_glAvailableProfiles(MOJOSHADER_glGetProcAddress lookup, + void *lookup_d, + const char **profs, const int size, + MOJOSHADER_malloc m, MOJOSHADER_free f, + void *malloc_d); /* @@ -2581,7 +2480,7 @@ int MOJOSHADER_glAvailableProfiles(MOJOSHADER_glGetProcAddress lookup, * safe, you should probably only call this from the same thread that created * the GL context. */ -const char *MOJOSHADER_glBestProfile(MOJOSHADER_glGetProcAddress lookup, +DECLSPEC const char *MOJOSHADER_glBestProfile(MOJOSHADER_glGetProcAddress lookup, void *lookup_d, MOJOSHADER_malloc m, MOJOSHADER_free f, void *malloc_d); @@ -2623,7 +2522,7 @@ const char *MOJOSHADER_glBestProfile(MOJOSHADER_glGetProcAddress lookup, * are not thread safe, you should probably only call this from the same * thread that created the GL context. */ -MOJOSHADER_glContext *MOJOSHADER_glCreateContext(const char *profile, +DECLSPEC MOJOSHADER_glContext *MOJOSHADER_glCreateContext(const char *profile, MOJOSHADER_glGetProcAddress lookup, void *lookup_d, MOJOSHADER_malloc m, MOJOSHADER_free f, @@ -2640,7 +2539,7 @@ MOJOSHADER_glContext *MOJOSHADER_glCreateContext(const char *profile, * It is legal to call this with a NULL pointer to make no context current, * but you need a valid context to be current to use most of MojoShader. */ -void MOJOSHADER_glMakeContextCurrent(MOJOSHADER_glContext *ctx); +DECLSPEC void MOJOSHADER_glMakeContextCurrent(MOJOSHADER_glContext *ctx); /* * Get any error state we might have picked up. MojoShader will NOT call @@ -2667,7 +2566,7 @@ void MOJOSHADER_glMakeContextCurrent(MOJOSHADER_glContext *ctx); * current. The error buffer is shared between contexts, so you can get * error results from a failed MOJOSHADER_glCreateContext(). */ -const char *MOJOSHADER_glGetError(void); +DECLSPEC const char *MOJOSHADER_glGetError(void); /* * Get the maximum uniforms a shader can support for the current GL context, @@ -2684,7 +2583,7 @@ const char *MOJOSHADER_glGetError(void); * This call requires a valid MOJOSHADER_glContext to have been made current, * or it will crash your program. See MOJOSHADER_glMakeContextCurrent(). */ -int MOJOSHADER_glMaxUniforms(MOJOSHADER_shaderType shader_type); +DECLSPEC int MOJOSHADER_glMaxUniforms(MOJOSHADER_shaderType shader_type); /* * Compile a buffer of Direct3D shader bytecode into an OpenGL shader. @@ -2706,12 +2605,12 @@ int MOJOSHADER_glMaxUniforms(MOJOSHADER_shaderType shader_type); * * Compiled shaders from this function may not be shared between contexts. */ -MOJOSHADER_glShader *MOJOSHADER_glCompileShader(const unsigned char *tokenbuf, - const unsigned int bufsize, - const MOJOSHADER_swizzle *swiz, - const unsigned int swizcount, - const MOJOSHADER_samplerMap *smap, - const unsigned int smapcount); +DECLSPEC MOJOSHADER_glShader *MOJOSHADER_glCompileShader(const unsigned char *tokenbuf, + const unsigned int bufsize, + const MOJOSHADER_swizzle *swiz, + const unsigned int swizcount, + const MOJOSHADER_samplerMap *smap, + const unsigned int smapcount); /* @@ -2721,7 +2620,7 @@ MOJOSHADER_glShader *MOJOSHADER_glCompileShader(const unsigned char *tokenbuf, * This data is read-only, and you should NOT attempt to free it. This * pointer remains valid until the shader is deleted. */ -const MOJOSHADER_parseData *MOJOSHADER_glGetShaderParseData( +DECLSPEC const MOJOSHADER_parseData *MOJOSHADER_glGetShaderParseData( MOJOSHADER_glShader *shader); /* * Link a vertex and pixel shader into an OpenGL program. @@ -2746,8 +2645,8 @@ const MOJOSHADER_parseData *MOJOSHADER_glGetShaderParseData( * * Linked programs from this function may not be shared between contexts. */ -MOJOSHADER_glProgram *MOJOSHADER_glLinkProgram(MOJOSHADER_glShader *vshader, - MOJOSHADER_glShader *pshader); +DECLSPEC MOJOSHADER_glProgram *MOJOSHADER_glLinkProgram(MOJOSHADER_glShader *vshader, + MOJOSHADER_glShader *pshader); /* * This binds the program (using, for example, glUseProgramObjectARB()), and @@ -2770,7 +2669,7 @@ MOJOSHADER_glProgram *MOJOSHADER_glLinkProgram(MOJOSHADER_glShader *vshader, * This call requires a valid MOJOSHADER_glContext to have been made current, * or it will crash your program. See MOJOSHADER_glMakeContextCurrent(). */ -void MOJOSHADER_glBindProgram(MOJOSHADER_glProgram *program); +DECLSPEC void MOJOSHADER_glBindProgram(MOJOSHADER_glProgram *program); /* * This binds individual shaders as if you had linked them with @@ -2797,8 +2696,8 @@ void MOJOSHADER_glBindProgram(MOJOSHADER_glProgram *program); * This call requires a valid MOJOSHADER_glContext to have been made current, * or it will crash your program. See MOJOSHADER_glMakeContextCurrent(). */ -void MOJOSHADER_glBindShaders(MOJOSHADER_glShader *vshader, - MOJOSHADER_glShader *pshader); +DECLSPEC void MOJOSHADER_glBindShaders(MOJOSHADER_glShader *vshader, + MOJOSHADER_glShader *pshader); /* * Set a floating-point uniform value (what Direct3D calls a "constant"). @@ -2821,8 +2720,8 @@ void MOJOSHADER_glBindShaders(MOJOSHADER_glShader *vshader, * * Uniforms are not shared between contexts. */ -void MOJOSHADER_glSetVertexShaderUniformF(unsigned int idx, const float *data, - unsigned int vec4count); +DECLSPEC void MOJOSHADER_glSetVertexShaderUniformF(unsigned int idx, const float *data, + unsigned int vec4count); /* * Retrieve a floating-point uniform value (what Direct3D calls a "constant"). @@ -2851,8 +2750,8 @@ void MOJOSHADER_glSetVertexShaderUniformF(unsigned int idx, const float *data, * * Uniforms are not shared between contexts. */ -void MOJOSHADER_glGetVertexShaderUniformF(unsigned int idx, float *data, - unsigned int vec4count); +DECLSPEC void MOJOSHADER_glGetVertexShaderUniformF(unsigned int idx, float *data, + unsigned int vec4count); /* @@ -2876,8 +2775,8 @@ void MOJOSHADER_glGetVertexShaderUniformF(unsigned int idx, float *data, * * Uniforms are not shared between contexts. */ -void MOJOSHADER_glSetVertexShaderUniformI(unsigned int idx, const int *data, - unsigned int ivec4count); +DECLSPEC void MOJOSHADER_glSetVertexShaderUniformI(unsigned int idx, const int *data, + unsigned int ivec4count); /* * Retrieve an integer uniform value (what Direct3D calls a "constant"). @@ -2906,8 +2805,8 @@ void MOJOSHADER_glSetVertexShaderUniformI(unsigned int idx, const int *data, * * Uniforms are not shared between contexts. */ -void MOJOSHADER_glGetVertexShaderUniformI(unsigned int idx, int *data, - unsigned int ivec4count); +DECLSPEC void MOJOSHADER_glGetVertexShaderUniformI(unsigned int idx, int *data, + unsigned int ivec4count); /* * Set a boolean uniform value (what Direct3D calls a "constant"). @@ -2935,8 +2834,8 @@ void MOJOSHADER_glGetVertexShaderUniformI(unsigned int idx, int *data, * * Uniforms are not shared between contexts. */ -void MOJOSHADER_glSetVertexShaderUniformB(unsigned int idx, const int *data, - unsigned int bcount); +DECLSPEC void MOJOSHADER_glSetVertexShaderUniformB(unsigned int idx, const int *data, + unsigned int bcount); /* * Retrieve a boolean uniform value (what Direct3D calls a "constant"). @@ -2972,8 +2871,8 @@ void MOJOSHADER_glSetVertexShaderUniformB(unsigned int idx, const int *data, * * Uniforms are not shared between contexts. */ -void MOJOSHADER_glGetVertexShaderUniformB(unsigned int idx, int *data, - unsigned int bcount); +DECLSPEC void MOJOSHADER_glGetVertexShaderUniformB(unsigned int idx, int *data, + unsigned int bcount); /* * The equivalent of MOJOSHADER_glSetVertexShaderUniformF() for pixel @@ -2989,8 +2888,8 @@ void MOJOSHADER_glGetVertexShaderUniformB(unsigned int idx, int *data, * * Uniforms are not shared between contexts. */ -void MOJOSHADER_glSetPixelShaderUniformF(unsigned int idx, const float *data, - unsigned int vec4count); +DECLSPEC void MOJOSHADER_glSetPixelShaderUniformF(unsigned int idx, const float *data, + unsigned int vec4count); /* @@ -3007,8 +2906,8 @@ void MOJOSHADER_glSetPixelShaderUniformF(unsigned int idx, const float *data, * * Uniforms are not shared between contexts. */ -void MOJOSHADER_glGetPixelShaderUniformF(unsigned int idx, float *data, - unsigned int vec4count); +DECLSPEC void MOJOSHADER_glGetPixelShaderUniformF(unsigned int idx, float *data, + unsigned int vec4count); /* @@ -3025,8 +2924,8 @@ void MOJOSHADER_glGetPixelShaderUniformF(unsigned int idx, float *data, * * Uniforms are not shared between contexts. */ -void MOJOSHADER_glSetPixelShaderUniformI(unsigned int idx, const int *data, - unsigned int ivec4count); +DECLSPEC void MOJOSHADER_glSetPixelShaderUniformI(unsigned int idx, const int *data, + unsigned int ivec4count); /* @@ -3043,8 +2942,8 @@ void MOJOSHADER_glSetPixelShaderUniformI(unsigned int idx, const int *data, * * Uniforms are not shared between contexts. */ -void MOJOSHADER_glGetPixelShaderUniformI(unsigned int idx, int *data, - unsigned int ivec4count); +DECLSPEC void MOJOSHADER_glGetPixelShaderUniformI(unsigned int idx, int *data, + unsigned int ivec4count); /* * The equivalent of MOJOSHADER_glSetVertexShaderUniformB() for pixel @@ -3060,8 +2959,8 @@ void MOJOSHADER_glGetPixelShaderUniformI(unsigned int idx, int *data, * * Uniforms are not shared between contexts. */ -void MOJOSHADER_glSetPixelShaderUniformB(unsigned int idx, const int *data, - unsigned int bcount); +DECLSPEC void MOJOSHADER_glSetPixelShaderUniformB(unsigned int idx, const int *data, + unsigned int bcount); /* * The equivalent of MOJOSHADER_glGetVertexShaderUniformB() for pixel @@ -3077,8 +2976,8 @@ void MOJOSHADER_glSetPixelShaderUniformB(unsigned int idx, const int *data, * * Uniforms are not shared between contexts. */ -void MOJOSHADER_glGetPixelShaderUniformB(unsigned int idx, int *data, - unsigned int bcount); +DECLSPEC void MOJOSHADER_glGetPixelShaderUniformB(unsigned int idx, int *data, + unsigned int bcount); /* * Set up the vector for the TEXBEM opcode. Most apps can ignore this API. @@ -3115,9 +3014,23 @@ void MOJOSHADER_glGetPixelShaderUniformB(unsigned int idx, int *data, * * These values are not shared between contexts. */ -void MOJOSHADER_glSetLegacyBumpMapEnv(unsigned int sampler, float mat00, - float mat01, float mat10, float mat11, - float lscale, float loffset); +DECLSPEC void MOJOSHADER_glSetLegacyBumpMapEnv(unsigned int sampler, float mat00, + float mat01, float mat10, float mat11, + float lscale, float loffset); + +/* + * Return the location of a vertex attribute for the currently-bound program. + * + * (usage) and (index) map to Direct3D vertex declaration values: COLOR1 would + * be MOJOSHADER_USAGE_COLOR and 1. + * + * The return value is the index of the attribute to be sent to + * glVertexAttribPointer, or -1 if the stream is not used. + * + * This call requires a valid MOJOSHADER_glContext to have been made current, + * or it will crash your program. See MOJOSHADER_glMakeContextCurrent(). + */ +DECLSPEC int MOJOSHADER_glGetVertexAttribLocation(MOJOSHADER_usage usage, int index); /* * Connect a client-side array to the currently-bound program. @@ -3146,30 +3059,30 @@ void MOJOSHADER_glSetLegacyBumpMapEnv(unsigned int sampler, float mat00, */ /* !!! FIXME: this should probably be "input" and not "attribute" */ /* !!! FIXME: or maybe "vertex array" or something. */ -void MOJOSHADER_glSetVertexAttribute(MOJOSHADER_usage usage, - int index, unsigned int size, - MOJOSHADER_attributeType type, - int normalized, unsigned int stride, - const void *ptr); - - - - -/* These below functions are temporary and will be removed from the API once - the real Effects API is written. Do not use! */ -void MOJOSHADER_glSetVertexPreshaderUniformF(unsigned int idx, const float *data, - unsigned int vec4n); -void MOJOSHADER_glGetVertexPreshaderUniformF(unsigned int idx, float *data, - unsigned int vec4n); -void MOJOSHADER_glSetPixelPreshaderUniformF(unsigned int idx, const float *data, - unsigned int vec4n); -void MOJOSHADER_glGetPixelPreshaderUniformF(unsigned int idx, float *data, - unsigned int vec4n); -/* These above functions are temporary and will be removed from the API once - the real Effects API is written. Do not use! */ - - +DECLSPEC void MOJOSHADER_glSetVertexAttribute(MOJOSHADER_usage usage, + int index, unsigned int size, + MOJOSHADER_attributeType type, + int normalized, unsigned int stride, + const void *ptr); +/* + * Modify the rate at which this vertex attribute advances during instanced + * rendering. + * + * This should be called alongside glSetVertexAttribute, as this does not flag + * the vertex array as being in use. This just calls glVertexAttribDivisorARB. + * + * This call is NOT thread safe! As most OpenGL implementations are not thread + * safe, you should probably only call this from the same thread that created + * the GL context. + * + * This call requires a valid MOJOSHADER_glContext to have been made current, + * or it will crash your program. See MOJOSHADER_glMakeContextCurrent(). + * + * Vertex attributes are not shared between contexts. + */ +DECLSPEC void MOJOSHADER_glSetVertexAttribDivisor(MOJOSHADER_usage usage, + int index, unsigned int divisor); /* * Inform MojoShader that it should commit any pending state to the GL. This @@ -3184,7 +3097,12 @@ void MOJOSHADER_glGetPixelPreshaderUniformF(unsigned int idx, float *data, * This call requires a valid MOJOSHADER_glContext to have been made current, * or it will crash your program. See MOJOSHADER_glMakeContextCurrent(). */ -void MOJOSHADER_glProgramReady(void); +DECLSPEC void MOJOSHADER_glProgramReady(void); + +#ifdef MOJOSHADER_FLIP_RENDERTARGET +// !!! FIXME: Document me. +DECLSPEC void MOJOSHADER_glProgramViewportFlip(int flip); +#endif /* * Free the resources of a linked program. This will delete the GL object @@ -3200,7 +3118,7 @@ void MOJOSHADER_glProgramReady(void); * This call requires a valid MOJOSHADER_glContext to have been made current, * or it will crash your program. See MOJOSHADER_glMakeContextCurrent(). */ -void MOJOSHADER_glDeleteProgram(MOJOSHADER_glProgram *program); +DECLSPEC void MOJOSHADER_glDeleteProgram(MOJOSHADER_glProgram *program); /* * Free the resources of a compiled shader. This will delete the GL object @@ -3217,7 +3135,7 @@ void MOJOSHADER_glDeleteProgram(MOJOSHADER_glProgram *program); * This call requires a valid MOJOSHADER_glContext to have been made current, * or it will crash your program. See MOJOSHADER_glMakeContextCurrent(). */ -void MOJOSHADER_glDeleteShader(MOJOSHADER_glShader *shader); +DECLSPEC void MOJOSHADER_glDeleteShader(MOJOSHADER_glShader *shader); /* * Deinitialize MojoShader's OpenGL shader management. @@ -3241,7 +3159,7 @@ void MOJOSHADER_glDeleteShader(MOJOSHADER_glShader *shader); * are not thread safe, you should probably only call this from the same * thread that created the GL context. */ -void MOJOSHADER_glDestroyContext(MOJOSHADER_glContext *ctx); +DECLSPEC void MOJOSHADER_glDestroyContext(MOJOSHADER_glContext *ctx); #ifdef __cplusplus } diff --git a/mojoshader_common.c b/mojoshader_common.c index 4519555c..a8b1cb8f 100644 --- a/mojoshader_common.c +++ b/mojoshader_common.c @@ -3,8 +3,8 @@ // Convenience functions for allocators... #if !MOJOSHADER_FORCE_ALLOCATOR -void *MOJOSHADER_internal_malloc(int bytes, void *d) { return malloc(bytes); } -void MOJOSHADER_internal_free(void *ptr, void *d) { free(ptr); } +void * MOJOSHADERCALL MOJOSHADER_internal_malloc(int bytes, void *d) { return malloc(bytes); } +void MOJOSHADERCALL MOJOSHADER_internal_free(void *ptr, void *d) { free(ptr); } #endif MOJOSHADER_error MOJOSHADER_out_of_mem_error = { @@ -78,7 +78,7 @@ int hash_find(const HashTable *table, const void *key, const void **_value) int hash_iter(const HashTable *table, const void *key, const void **_value, void **iter) { - HashItem *item = *iter; + HashItem *item = (HashItem *) *iter; if (item == NULL) item = table->table[calc_hash(table, key)]; else @@ -103,7 +103,7 @@ int hash_iter(const HashTable *table, const void *key, int hash_iter_keys(const HashTable *table, const void **_key, void **iter) { - HashItem *item = *iter; + HashItem *item = (HashItem *) *iter; int idx = 0; if (item != NULL) @@ -1011,5 +1011,73 @@ ssize_t buffer_find(Buffer *buffer, const size_t start, return -1; // no match found. } // buffer_find + +// Based on SDL_string.c's SDL_PrintFloat function +size_t MOJOSHADER_printFloat(char *text, size_t maxlen, float arg) +{ + size_t len; + size_t left = maxlen; + char *textstart = text; + + int precision = 9; + + if (arg) + { + /* This isn't especially accurate, but hey, it's easy. :) */ + unsigned long value; + + if (arg < 0) + { + if (left > 1) + { + *text = '-'; + --left; + } // if + ++text; + arg = -arg; + } // if + value = (unsigned long) arg; + len = snprintf(text, left, "%lu", value); + text += len; + if (len >= left) + left = (left < 1) ? left : 1; + else + left -= len; + arg -= value; + + int mult = 10; + if (left > 1) + { + *text = '.'; + --left; + } // if + ++text; + while (precision-- > 0) + { + value = (unsigned long) (arg * mult); + len = snprintf(text, left, "%lu", value); + text += len; + if (len >= left) + left = (left < 1) ? left : 1; + else + left -= len; + arg -= (double) value / mult; + if (arg < 0) arg = -arg; // Sometimes that bit gets flipped... + mult *= 10; + } // while + } // if + else + { + if (left > 3) + { + snprintf(text, left, "0.0"); + left -= 3; + } // if + text += 3; + } // else + + return (text - textstart); +} // MOJOSHADER_printFloat + // end of mojoshader_common.c ... diff --git a/mojoshader_effects.c b/mojoshader_effects.c index 7aa5e317..6f9d087c 100644 --- a/mojoshader_effects.c +++ b/mojoshader_effects.c @@ -10,12 +10,15 @@ #define __MOJOSHADER_INTERNAL__ 1 #include "mojoshader_internal.h" +#ifdef MOJOSHADER_EFFECT_SUPPORT + #include -#if SUPPORT_PRESHADERS void MOJOSHADER_runPreshader(const MOJOSHADER_preshader *preshader, - const float *inregs, float *outregs) + float *outregs) { + const float *inregs = preshader->registers; + // this is fairly straightforward, as there aren't any branching // opcodes in the preshader instruction set (at the moment, at least). const int scalarstart = (int) MOJOSHADER_PRESHADEROP_SCALAR_OPS; @@ -56,21 +59,32 @@ void MOJOSHADER_runPreshader(const MOJOSHADER_preshader *preshader, { case MOJOSHADER_PRESHADEROPERAND_LITERAL: { - const double *lit = &preshader->literals[index]; - assert((index + elems) <= preshader->literal_count); if (!isscalar) - memcpy(&src[opiter][0], lit, elemsbytes); + { + assert((index + elems) <= preshader->literal_count); + memcpy(&src[opiter][0], &preshader->literals[index], elemsbytes); + } // if else { - const double val = *lit; for (elemiter = 0; elemiter < elems; elemiter++) - src[opiter][elemiter] = val; + src[opiter][elemiter] = preshader->literals[index]; } // else break; } // case case MOJOSHADER_PRESHADEROPERAND_INPUT: - if (isscalar) + if (operand->array_register_count > 0) + { + int i; + const int *regsi = (const int *) inregs; + int arrIndex = regsi[((index >> 4) * 4) + ((index >> 2) & 3)]; + for (i = 0; i < operand->array_register_count; i++) + { + arrIndex = regsi[operand->array_registers[i] + arrIndex]; + } + src[opiter][0] = arrIndex; + } // if + else if (isscalar) src[opiter][0] = inregs[index]; else { @@ -159,6 +173,7 @@ void MOJOSHADER_runPreshader(const MOJOSHADER_preshader *preshader, final += src0[i] * src1[i]; for (i = 0; i < elems; i++) dst[i] = final; // !!! FIXME: is this right? + break; } // case default: @@ -181,7 +196,6 @@ void MOJOSHADER_runPreshader(const MOJOSHADER_preshader *preshader, } // else } // for } // MOJOSHADER_runPreshader -#endif static MOJOSHADER_effect MOJOSHADER_out_of_mem_effect = { 1, &MOJOSHADER_out_of_mem_error, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 @@ -202,357 +216,671 @@ static uint32 readui32(const uint8 **_ptr, uint32 *_len) return retval; } // readui32 -// !!! FIXME: this is sort of a big, ugly function. -const MOJOSHADER_effect *MOJOSHADER_parseEffect(const char *profile, - const unsigned char *buf, - const unsigned int _len, - const MOJOSHADER_swizzle *swiz, - const unsigned int swizcount, - const MOJOSHADER_samplerMap *smap, - const unsigned int smapcount, - MOJOSHADER_malloc m, - MOJOSHADER_free f, - void *d) +static char *readstring(const uint8 *base, + const uint32 offset, + MOJOSHADER_malloc m, + void *d) { - if ( ((m == NULL) && (f != NULL)) || ((m != NULL) && (f == NULL)) ) - return &MOJOSHADER_out_of_mem_effect; // supply both or neither. + // !!! FIXME: sanity checks! + // !!! FIXME: verify this doesn't go past EOF looking for a null. + const char *str = ((const char *) base) + offset; + const uint32 len = *((const uint32 *) str); + char *strptr = NULL; + if (len == 0) return NULL; /* No length? No string. */ + strptr = (char *) m(len, d); + memcpy(strptr, str + 4, len); + return strptr; +} // readstring + +static int findparameter(const MOJOSHADER_effectParam *params, + const uint32 param_count, + const char *name) +{ + int i; + for (i = 0; i < param_count; i++) + if (strcmp(name, params[i].value.name) == 0) + return i; + assert(0 && "Parameter not found!"); +} + +static void readvalue(const uint8 *base, + const uint32 typeoffset, + const uint32 valoffset, + MOJOSHADER_effectValue *value, + MOJOSHADER_effectObject *objects, + MOJOSHADER_malloc m, + void *d) +{ + int i; + const uint8 *typeptr = base + typeoffset; + const uint8 *valptr = base + valoffset; + unsigned int typelen = 9999999; // !!! FIXME + const uint32 type = readui32(&typeptr, &typelen); + const uint32 valclass = readui32(&typeptr, &typelen); + const uint32 name = readui32(&typeptr, &typelen); + const uint32 semantic = readui32(&typeptr, &typelen); + const uint32 numelements = readui32(&typeptr, &typelen); + + value->value_type = (MOJOSHADER_symbolType) type; + value->value_class = (MOJOSHADER_symbolClass) valclass; + value->name = readstring(base, name, m, d); + value->semantic = readstring(base, semantic, m, d); + value->element_count = numelements; + + /* Class sanity check */ + assert(valclass >= MOJOSHADER_SYMCLASS_SCALAR && valclass <= MOJOSHADER_SYMCLASS_STRUCT); + + if (valclass == MOJOSHADER_SYMCLASS_SCALAR + || valclass == MOJOSHADER_SYMCLASS_VECTOR + || valclass == MOJOSHADER_SYMCLASS_MATRIX_ROWS + || valclass == MOJOSHADER_SYMCLASS_MATRIX_COLUMNS) + { + /* These classes only ever contain scalar values */ + assert(type >= MOJOSHADER_SYMTYPE_BOOL && type <= MOJOSHADER_SYMTYPE_FLOAT); + + const uint32 columncount = readui32(&typeptr, &typelen); + const uint32 rowcount = readui32(&typeptr, &typelen); + + value->column_count = columncount; + value->row_count = rowcount; + + uint32 siz = columncount * rowcount; + if (numelements > 0) + siz *= numelements; + value->value_count = siz; + siz *= 4; + value->values = m(siz, d); + memcpy(value->values, valptr, siz); + } // if + else if (valclass == MOJOSHADER_SYMCLASS_OBJECT) + { + /* This class contains either samplers or "objects" */ + assert(type >= MOJOSHADER_SYMTYPE_STRING && type <= MOJOSHADER_SYMTYPE_VERTEXSHADER); + + if (type == MOJOSHADER_SYMTYPE_SAMPLER + || type == MOJOSHADER_SYMTYPE_SAMPLER1D + || type == MOJOSHADER_SYMTYPE_SAMPLER2D + || type == MOJOSHADER_SYMTYPE_SAMPLER3D + || type == MOJOSHADER_SYMTYPE_SAMPLERCUBE) + { + unsigned int vallen = 9999999; // !!! FIXME + const uint32 numstates = readui32(&valptr, &vallen); - if (m == NULL) m = MOJOSHADER_internal_malloc; - if (f == NULL) f = MOJOSHADER_internal_free; + value->value_count = numstates; - MOJOSHADER_effect *retval = m(sizeof (MOJOSHADER_effect), d); - if (retval == NULL) - return &MOJOSHADER_out_of_mem_effect; // supply both or neither. - memset(retval, '\0', sizeof (*retval)); + const uint32 siz = sizeof(MOJOSHADER_effectSamplerState) * numstates; + value->values = m(siz, d); + memset(value->values, '\0', siz); - retval->malloc = m; - retval->free = f; - retval->malloc_data = d; + for (i = 0; i < numstates; i++) + { + MOJOSHADER_effectSamplerState *state = &value->valuesSS[i]; + const uint32 stype = readui32(&valptr, &vallen) & ~0xA0; + /*const uint32 FIXME =*/ readui32(&valptr, &vallen); + const uint32 statetypeoffset = readui32(&valptr, &vallen); + const uint32 statevaloffset = readui32(&valptr, &vallen); + + state->type = (MOJOSHADER_samplerStateType) stype; + readvalue(base, statetypeoffset, statevaloffset, + &state->value, objects, + m, d); + if (stype == MOJOSHADER_SAMP_TEXTURE) + objects[state->value.valuesI[0]].type = (MOJOSHADER_symbolType) type; + } // for + } // if + else + { + uint32 numobjects = 1; + if (numelements > 0) + numobjects = numelements; - const uint8 *ptr = (const uint8 *) buf; - uint32 len = (uint32) _len; - size_t siz = 0; - int i, j, k; + value->value_count = numobjects; - if (len < 8) - goto parseEffect_unexpectedEOF; + const uint32 siz = 4 * numobjects; + value->values = m(siz, d); + memcpy(value->values, valptr, siz); - const uint8 *base = NULL; - if (readui32(&ptr, &len) != 0xFEFF0901) // !!! FIXME: is this always magic? - goto parseEffect_notAnEffectsFile; - else + for (i = 0; i < value->value_count; i++) + objects[value->valuesI[i]].type = (MOJOSHADER_symbolType) type; + } // else + } // else if + else if (valclass == MOJOSHADER_SYMCLASS_STRUCT) { - const uint32 offset = readui32(&ptr, &len); - base = ptr; -//printf("base offset == %u\n", offset); - if (offset > len) - goto parseEffect_unexpectedEOF; - ptr += offset; - len -= offset; - } // else + /* TODO: Maybe this is like parse_ctab_typeinfo? -flibit */ + assert(0 && "Effect struct value parsing not implemented!"); + } // else if +} // readvalue + +static void readannotations(const uint32 numannos, + const uint8 *base, + const uint8 **ptr, + uint32 *len, + MOJOSHADER_effectAnnotation **annotations, + MOJOSHADER_effectObject *objects, + MOJOSHADER_malloc m, + void *d) +{ + int i; + if (numannos == 0) return; - // params... + const uint32 siz = sizeof(MOJOSHADER_effectAnnotation) * numannos; + *annotations = (MOJOSHADER_effectAnnotation *) m(siz, d); + memset(*annotations, '\0', siz); - if (len < 16) - goto parseEffect_unexpectedEOF; + for (i = 0; i < numannos; i++) + { + MOJOSHADER_effectAnnotation *anno = &(*annotations)[i]; - const uint32 numparams = readui32(&ptr, &len); - const uint32 numtechniques = readui32(&ptr, &len); + const uint32 typeoffset = readui32(ptr, len); + const uint32 valoffset = readui32(ptr, len); - readui32(&ptr, &len); // !!! FIXME: there are 8 unknown bytes here. Annotations? - /*const uint32 numobjects = */ readui32(&ptr, &len); + readvalue(base, typeoffset, valoffset, + anno, objects, + m, d); + } // for +} // readannotation + +static void readparameters(const uint32 numparams, + const uint8 *base, + const uint8 **ptr, + uint32 *len, + MOJOSHADER_effectParam **params, + MOJOSHADER_effectObject *objects, + MOJOSHADER_malloc m, + void *d) +{ + int i; + if (numparams == 0) return; - if (numparams > 0) + uint32 siz = sizeof(MOJOSHADER_effectParam) * numparams; + *params = (MOJOSHADER_effectParam *) m(siz, d); + memset(*params, '\0', siz); + + for (i = 0; i < numparams; i++) { - siz = sizeof (MOJOSHADER_effectParam) * numparams; - retval->params = (MOJOSHADER_effectParam *) m(siz, d); - if (retval->params == NULL) - goto parseEffect_outOfMemory; - memset(retval->params, '\0', siz); + MOJOSHADER_effectParam *param = &(*params)[i]; - retval->param_count = numparams; + const uint32 typeoffset = readui32(ptr, len); + const uint32 valoffset = readui32(ptr, len); + /*const uint32 flags =*/ readui32(ptr, len); + const uint32 numannos = readui32(ptr, len); - for (i = 0; i < numparams; i++) - { - if (len < 16) - goto parseEffect_unexpectedEOF; - - const uint32 typeoffset = readui32(&ptr, &len); - /*const uint32 valoffset =*/ readui32(&ptr, &len); - /*const uint32 flags =*/ readui32(&ptr, &len); - const uint32 numannos = readui32(&ptr, &len); - for (j = 0; j < numannos; j++) - { - if (len < 8) - goto parseEffect_unexpectedEOF; - // !!! FIXME: parse annotations. - readui32(&ptr, &len); - readui32(&ptr, &len); - } // for + param->annotation_count = numannos; + readannotations(numannos, base, ptr, len, + ¶m->annotations, objects, + m, d); - const uint8 *typeptr = base + typeoffset; - unsigned int typelen = 9999999; // !!! FIXME - /*const uint32 paramtype =*/ readui32(&typeptr, &typelen); - /*const uint32 paramclass =*/ readui32(&typeptr, &typelen); - const uint32 paramname = readui32(&typeptr, &typelen); - const uint32 paramsemantic = readui32(&typeptr, &typelen); - - // !!! FIXME: sanity checks! - const char *namestr = ((const char *) base) + paramname; - const char *semstr = ((const char *) base) + paramsemantic; - uint32 len; - char *strptr; - len = *((const uint32 *) namestr); - strptr = (char *) m(len + 1, d); - memcpy(strptr, namestr + 4, len); - strptr[len] = '\0'; - retval->params[i].name = strptr; - len = *((const uint32 *) semstr); - strptr = (char *) m(len + 1, d); - memcpy(strptr, semstr + 4, len); - strptr[len] = '\0'; - retval->params[i].semantic = strptr; - } // for - } // if + readvalue(base, typeoffset, valoffset, + ¶m->value, objects, + m, d); + } // for +} // readparameters + +static void readstates(const uint32 numstates, + const uint8 *base, + const uint8 **ptr, + uint32 *len, + MOJOSHADER_effectState **states, + MOJOSHADER_effectObject *objects, + MOJOSHADER_malloc m, + void *d) +{ + int i; + if (numstates == 0) return; - uint32 numshaders = 0; // we'll calculate this later. + const uint32 siz = sizeof (MOJOSHADER_effectState) * numstates; + *states = (MOJOSHADER_effectState *) m(siz, d); + memset(*states, '\0', siz); + + for (i = 0; i < numstates; i++) + { + MOJOSHADER_effectState *state = &(*states)[i]; - // techniques... + const uint32 type = readui32(ptr, len); + /*const uint32 FIXME =*/ readui32(ptr, len); + const uint32 typeoffset = readui32(ptr, len); + const uint32 valoffset = readui32(ptr, len); - if (numtechniques > 0) + state->type = (MOJOSHADER_renderStateType) type; + readvalue(base, typeoffset, valoffset, + &state->value, objects, + m, d); + } // for +} // readstates + +static void readpasses(const uint32 numpasses, + const uint8 *base, + const uint8 **ptr, + uint32 *len, + MOJOSHADER_effectPass **passes, + MOJOSHADER_effectObject *objects, + MOJOSHADER_malloc m, + void *d) +{ + int i; + if (numpasses == 0) return; + + const uint32 siz = sizeof (MOJOSHADER_effectPass) * numpasses; + *passes = (MOJOSHADER_effectPass *) m(siz, d); + memset(*passes, '\0', siz); + + for (i = 0; i < numpasses; i++) { - siz = sizeof (MOJOSHADER_effectTechnique) * numtechniques; - retval->techniques = (MOJOSHADER_effectTechnique *) m(siz, d); - if (retval->techniques == NULL) - goto parseEffect_outOfMemory; - memset(retval->techniques, '\0', siz); + MOJOSHADER_effectPass *pass = &(*passes)[i]; - retval->technique_count = numtechniques; + const uint32 passnameoffset = readui32(ptr, len); + const uint32 numannos = readui32(ptr, len); + const uint32 numstates = readui32(ptr, len); - for (i = 0; i < numtechniques; i++) - { - if (len < 12) - goto parseEffect_unexpectedEOF; - - MOJOSHADER_effectTechnique *technique = &retval->techniques[i]; + pass->name = readstring(base, passnameoffset, m, d); - const uint32 nameoffset = readui32(&ptr, &len); - const uint32 numannos = readui32(&ptr, &len); - const uint32 numpasses = readui32(&ptr, &len); + pass->annotation_count = numannos; + readannotations(numannos, base, ptr, len, + &pass->annotations, objects, + m, d); - if (nameoffset >= _len) - goto parseEffect_unexpectedEOF; + pass->state_count = numstates; + readstates(numstates, base, ptr, len, + &pass->states, objects, + m, d); + } // for +} // readpasses + +static void readtechniques(const uint32 numtechniques, + const uint8 *base, + const uint8 **ptr, + uint32 *len, + MOJOSHADER_effectTechnique **techniques, + MOJOSHADER_effectObject *objects, + MOJOSHADER_malloc m, + void *d) +{ + int i; + if (numtechniques == 0) return; - if (numannos > 0) - { - // !!! FIXME: expose these to the caller? - for (j = 0; j < numannos; j++) - { - if (len < 8) - goto parseEffect_unexpectedEOF; - readui32(&ptr, &len); // typedef offset - readui32(&ptr, &len); // value offset - } // for - } // if + const uint32 siz = sizeof (MOJOSHADER_effectTechnique) * numtechniques; + *techniques = (MOJOSHADER_effectTechnique *) m(siz, d); + memset(*techniques, '\0', siz); - // !!! FIXME: verify this doesn't go past EOF looking for a null. - { - const char *namestr = ((char *) base) + nameoffset; - uint32 len = *((const uint32 *) namestr); - char *strptr = (char *) m(len + 1, d); - memcpy(strptr, namestr + 4, len); - strptr[len] = '\0'; - technique->name = strptr; - } + for (i = 0; i < numtechniques; i++) + { + MOJOSHADER_effectTechnique *technique = &(*techniques)[i]; - if (numpasses > 0) - { - technique->pass_count = numpasses; + const uint32 nameoffset = readui32(ptr, len); + const uint32 numannos = readui32(ptr, len); + const uint32 numpasses = readui32(ptr, len); - siz = sizeof (MOJOSHADER_effectPass) * numpasses; - technique->passes = (MOJOSHADER_effectPass *) m(siz, d); - if (technique->passes == NULL) - goto parseEffect_outOfMemory; - memset(technique->passes, '\0', siz); + technique->name = readstring(base, nameoffset, m, d); - for (j = 0; j < numpasses; j++) - { - if (len < 12) - goto parseEffect_unexpectedEOF; + technique->annotation_count = numannos; + readannotations(numannos, base, ptr, len, + &technique->annotations, objects, + m, d); - MOJOSHADER_effectPass *pass = &technique->passes[j]; + technique->pass_count = numpasses; + readpasses(numpasses, base, ptr, len, + &technique->passes, objects, + m, d); + } // for +} // readtechniques + +static void readsmallobjects(const uint32 numsmallobjects, + const uint8 **ptr, + uint32 *len, + MOJOSHADER_effect *effect, + const char *profile, + const MOJOSHADER_swizzle *swiz, + const unsigned int swizcount, + const MOJOSHADER_samplerMap *smap, + const unsigned int smapcount, + MOJOSHADER_malloc m, + MOJOSHADER_free f, + void *d) +{ + int i, j; + if (numsmallobjects == 0) return; - const uint32 passnameoffset = readui32(&ptr, &len); - const uint32 numannos = readui32(&ptr, &len); - const uint32 numstates = readui32(&ptr, &len); + for (i = 1; i < numsmallobjects + 1; i++) + { + const uint32 index = readui32(ptr, len); + const uint32 length = readui32(ptr, len); - if (passnameoffset >= _len) - goto parseEffect_unexpectedEOF; + MOJOSHADER_effectObject *object = &effect->objects[index]; + if (object->type == MOJOSHADER_SYMTYPE_STRING) + { + if (length > 0) + { + char *str = (char *) m(length, d); + memcpy(str, *ptr, length); + object->string.string = str; + } // if + } // if + else if (object->type == MOJOSHADER_SYMTYPE_TEXTURE + || object->type == MOJOSHADER_SYMTYPE_TEXTURE1D + || object->type == MOJOSHADER_SYMTYPE_TEXTURE2D + || object->type == MOJOSHADER_SYMTYPE_TEXTURE3D + || object->type == MOJOSHADER_SYMTYPE_TEXTURECUBE) + { + // No-op. Why is this even here? + } // else if + else if (object->type == MOJOSHADER_SYMTYPE_SAMPLER + || object->type == MOJOSHADER_SYMTYPE_SAMPLER1D + || object->type == MOJOSHADER_SYMTYPE_SAMPLER2D + || object->type == MOJOSHADER_SYMTYPE_SAMPLER3D + || object->type == MOJOSHADER_SYMTYPE_SAMPLERCUBE) + { + if (length > 0) + { + char *str = (char *) m(length, d); + memcpy(str, *ptr, length); + object->mapping.name = str; + } // if + } // else if + else if (object->type == MOJOSHADER_SYMTYPE_PIXELSHADER + || object->type == MOJOSHADER_SYMTYPE_VERTEXSHADER) + { + object->shader.technique = -1; + object->shader.pass = -1; + object->shader.shader = MOJOSHADER_parse(profile, *ptr, length, + swiz, swizcount, smap, smapcount, + m, f, d); + // !!! FIXME: check for errors. + for (j = 0; j < object->shader.shader->symbol_count; j++) + if (object->shader.shader->symbols[j].register_set == MOJOSHADER_SYMREGSET_SAMPLER) + object->shader.sampler_count++; + object->shader.param_count = object->shader.shader->symbol_count; + object->shader.params = (uint32 *) m(object->shader.param_count * sizeof (uint32), d); + object->shader.samplers = (MOJOSHADER_samplerStateRegister *) m(object->shader.sampler_count * sizeof (MOJOSHADER_samplerStateRegister), d); + uint32 curSampler = 0; + for (j = 0; j < object->shader.shader->symbol_count; j++) + { + int par = findparameter(effect->params, + effect->param_count, + object->shader.shader->symbols[j].name); + object->shader.params[j] = par; + if (object->shader.shader->symbols[j].register_set == MOJOSHADER_SYMREGSET_SAMPLER) + { + object->shader.samplers[curSampler].sampler_name = object->shader.shader->symbols[j].name; + object->shader.samplers[curSampler].sampler_register = object->shader.shader->symbols[j].register_index; + object->shader.samplers[curSampler].sampler_state_count = effect->params[par].value.value_count; + object->shader.samplers[curSampler].sampler_states = effect->params[par].value.valuesSS; + curSampler++; + } // if + } // for + if (object->shader.shader->preshader) + { + object->shader.preshader_param_count = object->shader.shader->preshader->symbol_count; + object->shader.preshader_params = (uint32 *) m(object->shader.preshader_param_count * sizeof (uint32), d); + for (j = 0; j < object->shader.shader->preshader->symbol_count; j++) + { + object->shader.preshader_params[j] = findparameter(effect->params, + effect->param_count, + object->shader.shader->preshader->symbols[j].name); + } // for + } // if + } // else if + else + { + assert(0 && "Small object type unknown!"); + } // else - // !!! FIXME: verify this doesn't go past EOF looking for a null. - { - const char *namestr = ((char *) base) + passnameoffset; - uint32 len = *((const uint32 *) namestr); - char *strptr = (char *) m(len + 1, d); - memcpy(strptr, namestr + 4, len); - strptr[len] = '\0'; - pass->name = strptr; - } - - if (numannos > 0) - { - for (k = 0; k < numannos; k++) - { - if (len < 8) - goto parseEffect_unexpectedEOF; - // !!! FIXME: do something with this. - readui32(&ptr, &len); - readui32(&ptr, &len); - } // for - } // if + /* Object block is always a multiple of four */ + const uint32 blocklen = (length + 3) - ((length - 1) % 4); + *ptr += blocklen; + *len -= blocklen; + } // for +} // readstrings + +static void readlargeobjects(const uint32 numlargeobjects, + const uint32 numsmallobjects, + const uint8 **ptr, + uint32 *len, + MOJOSHADER_effect *effect, + const char *profile, + const MOJOSHADER_swizzle *swiz, + const unsigned int swizcount, + const MOJOSHADER_samplerMap *smap, + const unsigned int smapcount, + MOJOSHADER_malloc m, + MOJOSHADER_free f, + void *d) +{ + int i, j; + if (numlargeobjects == 0) return; - if (numstates > 0) - { - pass->state_count = numstates; + int numobjects = numsmallobjects + numlargeobjects + 1; + for (i = numsmallobjects + 1; i < numobjects; i++) + { + const uint32 technique = readui32(ptr, len); + const uint32 index = readui32(ptr, len); + /*const uint32 FIXME =*/ readui32(ptr, len); + const uint32 state = readui32(ptr, len); + const uint32 type = readui32(ptr, len); + const uint32 length = readui32(ptr, len); + + uint32 objectIndex; + if (technique == -1) + objectIndex = effect->params[index].value.valuesSS[state].value.valuesI[0]; + else + objectIndex = effect->techniques[technique].passes[index].states[state].value.valuesI[0]; - siz = sizeof (MOJOSHADER_effectState) * numstates; - pass->states = (MOJOSHADER_effectState *) m(siz, d); - if (pass->states == NULL) - goto parseEffect_outOfMemory; - memset(pass->states, '\0', siz); + MOJOSHADER_effectObject *object = &effect->objects[objectIndex]; + if (object->type == MOJOSHADER_SYMTYPE_PIXELSHADER + || object->type == MOJOSHADER_SYMTYPE_VERTEXSHADER) + { + object->shader.technique = technique; + object->shader.pass = index; - for (k = 0; k < numstates; k++) - { - if (len < 16) - goto parseEffect_unexpectedEOF; - - MOJOSHADER_effectState *state = &pass->states[k]; - const uint32 type = readui32(&ptr, &len); - readui32(&ptr, &len); // !!! FIXME: don't know what this field does. - /*const uint32 offsetend = */ readui32(&ptr, &len); - /*const uint32 offsetstart = */ readui32(&ptr, &len); - state->type = type; - - if ((type == 0x92) || (type == 0x93)) - numshaders++; - } // for + const char *emitter = profile; + if (type == 2) + { + /* This is a standalone preshader! + * It exists solely for effect passes that do not use a single + * vertex/fragment shader. + */ + object->shader.is_preshader = 1; + const uint32 start = *((uint32 *) *ptr) + 4; + const uint32 end = 24; // FIXME: Why? -flibit + const char *array = readstring(*ptr, 0, m, d); + object->shader.param_count = 1; + object->shader.params = (uint32 *) m(sizeof (uint32), d); + object->shader.params[0] = findparameter(effect->params, + effect->param_count, + array); + f((void *) array, d); + object->shader.preshader = MOJOSHADER_parsePreshader(*ptr + start, length - end, + m, f, d); + // !!! FIXME: check for errors. + object->shader.preshader_param_count = object->shader.preshader->symbol_count; + object->shader.preshader_params = (uint32 *) m(object->shader.preshader_param_count * sizeof (uint32), d); + for (j = 0; j < object->shader.preshader->symbol_count; j++) + { + object->shader.preshader_params[j] = findparameter(effect->params, + effect->param_count, + object->shader.preshader->symbols[j].name); + } // for + } // if + else + { + object->shader.shader = MOJOSHADER_parse(emitter, *ptr, length, + swiz, swizcount, smap, smapcount, + m, f, d); + // !!! FIXME: check for errors. + for (j = 0; j < object->shader.shader->symbol_count; j++) + if (object->shader.shader->symbols[j].register_set == MOJOSHADER_SYMREGSET_SAMPLER) + object->shader.sampler_count++; + object->shader.param_count = object->shader.shader->symbol_count; + object->shader.params = (uint32 *) m(object->shader.param_count * sizeof (uint32), d); + object->shader.samplers = (MOJOSHADER_samplerStateRegister *) m(object->shader.sampler_count * sizeof (MOJOSHADER_samplerStateRegister), d); + uint32 curSampler = 0; + for (j = 0; j < object->shader.shader->symbol_count; j++) + { + int par = findparameter(effect->params, + effect->param_count, + object->shader.shader->symbols[j].name); + object->shader.params[j] = par; + if (object->shader.shader->symbols[j].register_set == MOJOSHADER_SYMREGSET_SAMPLER) + { + object->shader.samplers[curSampler].sampler_name = object->shader.shader->symbols[j].name; + object->shader.samplers[curSampler].sampler_register = object->shader.shader->symbols[j].register_index; + object->shader.samplers[curSampler].sampler_state_count = effect->params[par].value.value_count; + object->shader.samplers[curSampler].sampler_states = effect->params[par].value.valuesSS; + curSampler++; } // if } // for + if (object->shader.shader->preshader) + { + object->shader.preshader_param_count = object->shader.shader->preshader->symbol_count; + object->shader.preshader_params = (uint32 *) m(object->shader.preshader_param_count * sizeof (uint32), d); + for (j = 0; j < object->shader.shader->preshader->symbol_count; j++) + { + object->shader.preshader_params[j] = findparameter(effect->params, + effect->param_count, + object->shader.shader->preshader->symbols[j].name); + } // for + } // if + } + } // if + else if (object->type == MOJOSHADER_SYMTYPE_SAMPLER + || object->type == MOJOSHADER_SYMTYPE_SAMPLER1D + || object->type == MOJOSHADER_SYMTYPE_SAMPLER2D + || object->type == MOJOSHADER_SYMTYPE_SAMPLER3D + || object->type == MOJOSHADER_SYMTYPE_SAMPLERCUBE) + { + if (length > 0) + { + char *str = (char *) m(length, d); + memcpy(str, *ptr, length); + object->mapping.name = str; } // if - } // for - } // if + } // else if + else if (object->type != MOJOSHADER_SYMTYPE_VOID) // FIXME: Why? -flibit + { + assert(0 && "Large object type unknown!"); + } // else - // textures... + /* Object block is always a multiple of four */ + const uint32 blocklen = (length + 3) - ((length - 1) % 4); + *ptr += blocklen; + *len -= blocklen; + } // for +} // readobjects + +MOJOSHADER_effect *MOJOSHADER_parseEffect(const char *profile, + const unsigned char *buf, + const unsigned int _len, + const MOJOSHADER_swizzle *swiz, + const unsigned int swizcount, + const MOJOSHADER_samplerMap *smap, + const unsigned int smapcount, + MOJOSHADER_malloc m, + MOJOSHADER_free f, + void *d) +{ + const uint8 *ptr = (const uint8 *) buf; + uint32 len = (uint32) _len; + + /* Supply both m and f, or neither */ + if ( ((m == NULL) && (f != NULL)) || ((m != NULL) && (f == NULL)) ) + return &MOJOSHADER_out_of_mem_effect; + + /* Use default malloc/free if m/f were not passed */ + if (m == NULL) m = MOJOSHADER_internal_malloc; + if (f == NULL) f = MOJOSHADER_internal_free; + + /* malloc base effect structure */ + MOJOSHADER_effect *retval = (MOJOSHADER_effect *) m(sizeof (MOJOSHADER_effect), d); + if (retval == NULL) + return &MOJOSHADER_out_of_mem_effect; + memset(retval, '\0', sizeof (*retval)); + + /* Store m/f/d in effect structure */ + retval->malloc = m; + retval->free = f; + retval->malloc_data = d; if (len < 8) goto parseEffect_unexpectedEOF; - const int numtextures = readui32(&ptr, &len); - const int numobjects = readui32(&ptr, &len); // !!! FIXME: "objects" for lack of a better word. - - if (numtextures > 0) + /* Read in header magic, seek to initial offset */ + const uint8 *base = NULL; + uint32 header = readui32(&ptr, &len); + if (header == 0xBCF00BCF) { - siz = sizeof (MOJOSHADER_effectTexture) * numtextures; - retval->textures = m(siz, d); - if (retval->textures == NULL) - goto parseEffect_outOfMemory; - memset(retval->textures, '\0', siz); - - for (i = 0; i < numtextures; i++) - { - if (len < 8) - goto parseEffect_unexpectedEOF; - - MOJOSHADER_effectTexture *texture = &retval->textures[i]; - const uint32 texparam = readui32(&ptr, &len); - const uint32 texsize = readui32(&ptr, &len); - // apparently texsize will pad out to 32 bits. - const uint32 readsize = (((texsize + 3) / 4) * 4); - if (len < readsize) - goto parseEffect_unexpectedEOF; - - texture->param = texparam; - char *str = m(texsize + 1, d); - if (str == NULL) - goto parseEffect_outOfMemory; - memcpy(str, ptr, texsize); - str[texsize] = '\0'; - texture->name = str; - - ptr += readsize; - len -= readsize; - } // for + /* The Effect compiler provided with XNA4 adds some extra mess at the + * beginning of the file. It's useless though, so just skip it. + * -flibit + */ + const uint32 skip = readui32(&ptr, &len) - 8; + ptr += skip; + len += skip; + header = readui32(&ptr, &len); } // if + if (header != 0xFEFF0901) + goto parseEffect_notAnEffectsFile; + else + { + const uint32 offset = readui32(&ptr, &len); + base = ptr; + if (offset > len) + goto parseEffect_unexpectedEOF; + ptr += offset; + len -= offset; + } // else + + if (len < 16) + goto parseEffect_unexpectedEOF; - // shaders... + /* Parse structure counts */ + const uint32 numparams = readui32(&ptr, &len); + const uint32 numtechniques = readui32(&ptr, &len); + /*const uint32 FIXME =*/ readui32(&ptr, &len); + const uint32 numobjects = readui32(&ptr, &len); + + /* Alloc structures now, so object types can be stored */ + retval->object_count = numobjects; + const uint32 siz = sizeof (MOJOSHADER_effectObject) * numobjects; + retval->objects = (MOJOSHADER_effectObject *) m(siz, d); + if (retval->objects == NULL) + goto parseEffect_outOfMemory; + memset(retval->objects, '\0', siz); - if (numshaders > 0) - { - siz = sizeof (MOJOSHADER_effectShader) * numshaders; - retval->shaders = (MOJOSHADER_effectShader *) m(siz, d); - if (retval->shaders == NULL) - goto parseEffect_outOfMemory; - memset(retval->shaders, '\0', siz); + /* Parse effect parameters */ + retval->param_count = numparams; + readparameters(numparams, base, &ptr, &len, + &retval->params, retval->objects, + m, d); - retval->shader_count = numshaders; + /* Parse effect techniques */ + retval->technique_count = numtechniques; + readtechniques(numtechniques, base, &ptr, &len, + &retval->techniques, retval->objects, + m, d); - // !!! FIXME: I wonder if we should pull these from offsets and not - // !!! FIXME: count on them all being in a line like this. - for (i = 0; i < numshaders; i++) - { - if (len < 24) - goto parseEffect_unexpectedEOF; - - MOJOSHADER_effectShader *shader = &retval->shaders[i]; - const uint32 technique = readui32(&ptr, &len); - const uint32 pass = readui32(&ptr, &len); - readui32(&ptr, &len); // !!! FIXME: don't know what this does. - readui32(&ptr, &len); // !!! FIXME: don't know what this does (vertex/pixel/geometry?) - readui32(&ptr, &len); // !!! FIXME: don't know what this does. - const uint32 shadersize = readui32(&ptr, &len); - - if (len < shadersize) - goto parseEffect_unexpectedEOF; - - shader->technique = technique; - shader->pass = pass; - shader->shader = MOJOSHADER_parse(profile, ptr, shadersize, - swiz, swizcount, smap, smapcount, - m, f, d); + /* Initial effect technique/pass */ + retval->current_technique = &retval->techniques[0]; + retval->current_pass = -1; - // !!! FIXME: check for errors. + if (len < 8) + goto parseEffect_unexpectedEOF; - ptr += shadersize; - len -= shadersize; - } // for - } // if + /* Parse object counts */ + const int numsmallobjects = readui32(&ptr, &len); + const int numlargeobjects = readui32(&ptr, &len); - // !!! FIXME: we parse this, but don't expose the data, yet. - // mappings ... - assert(numshaders <= numobjects); - const uint32 nummappings = numobjects - numshaders; - if (nummappings > 0) - { - for (i = 0; i < nummappings; i++) - { - if (len < 24) - goto parseEffect_unexpectedEOF; - - /*const uint32 magic = */ readui32(&ptr, &len); - /*const uint32 index = */ readui32(&ptr, &len); - readui32(&ptr, &len); // !!! FIXME: what is this field? - readui32(&ptr, &len); // !!! FIXME: what is this field? - /*const uint32 type = */ readui32(&ptr, &len); - const uint32 mapsize = readui32(&ptr, &len); - if (mapsize > 0) - { - const uint32 readsize = (((mapsize + 3) / 4) * 4); - if (len < readsize) - goto parseEffect_unexpectedEOF; - } // if - } // for - } // if + /* Parse "small" object table */ + readsmallobjects(numsmallobjects, &ptr, &len, + retval, + profile, swiz, swizcount, smap, smapcount, + m, f, d); + + /* Parse "large" object table. */ + readlargeobjects(numlargeobjects, numsmallobjects, &ptr, &len, + retval, + profile, swiz, swizcount, smap, smapcount, + m, f, d); + /* Store MojoShader profile in effect structure */ retval->profile = (char *) m(strlen(profile) + 1, d); if (retval->profile == NULL) goto parseEffect_outOfMemory; @@ -560,7 +888,6 @@ const MOJOSHADER_effect *MOJOSHADER_parseEffect(const char *profile, return retval; - // !!! FIXME: do something with this. parseEffect_notAnEffectsFile: parseEffect_unexpectedEOF: @@ -570,6 +897,22 @@ const MOJOSHADER_effect *MOJOSHADER_parseEffect(const char *profile, } // MOJOSHADER_parseEffect +void freevalue(MOJOSHADER_effectValue *value, MOJOSHADER_free f, void *d) +{ + int i; + f((void *) value->name, d); + f((void *) value->semantic, d); + if (value->value_type == MOJOSHADER_SYMTYPE_SAMPLER + || value->value_type == MOJOSHADER_SYMTYPE_SAMPLER1D + || value->value_type == MOJOSHADER_SYMTYPE_SAMPLER2D + || value->value_type == MOJOSHADER_SYMTYPE_SAMPLER3D + || value->value_type == MOJOSHADER_SYMTYPE_SAMPLERCUBE) + for (i = 0; i < value->value_count; i++) + freevalue(&value->valuesSS[i].value, f, d); + f(value->values, d); +} // freevalue + + void MOJOSHADER_freeEffect(const MOJOSHADER_effect *_effect) { MOJOSHADER_effect *effect = (MOJOSHADER_effect *) _effect; @@ -578,8 +921,9 @@ void MOJOSHADER_freeEffect(const MOJOSHADER_effect *_effect) MOJOSHADER_free f = effect->free; void *d = effect->malloc_data; - int i, j; + int i, j, k; + /* Free errors */ for (i = 0; i < effect->error_count; i++) { f((void *) effect->errors[i].error, d); @@ -587,39 +931,702 @@ void MOJOSHADER_freeEffect(const MOJOSHADER_effect *_effect) } // for f((void *) effect->errors, d); + /* Free profile string */ f((void *) effect->profile, d); + /* Free parameters, including annotations */ for (i = 0; i < effect->param_count; i++) { - f((void *) effect->params[i].name, d); - f((void *) effect->params[i].semantic, d); + MOJOSHADER_effectParam *param = &effect->params[i]; + freevalue(¶m->value, f, d); + for (j = 0; j < param->annotation_count; j++) + { + freevalue(¶m->annotations[j], f, d); + } // for + f((void *) param->annotations, d); } // for - f(effect->params, d); + f((void *) effect->params, d); + /* Free techniques, including passes and all annotations */ for (i = 0; i < effect->technique_count; i++) { MOJOSHADER_effectTechnique *technique = &effect->techniques[i]; f((void *) technique->name, d); for (j = 0; j < technique->pass_count; j++) { - f((void *) technique->passes[j].name, d); - f(technique->passes[j].states, d); + MOJOSHADER_effectPass *pass = &technique->passes[j]; + f((void *) pass->name, d); + for (k = 0; k < pass->state_count; k++) + { + freevalue(&pass->states[k].value, f, d); + } // for + f((void *) pass->states, d); + for (k = 0; k < pass->annotation_count; k++) + { + freevalue(&pass->annotations[k], f, d); + } // for + f((void *) pass->annotations, d); + } // for + f((void *) technique->passes, d); + for (j = 0; j < technique->annotation_count; j++) + { + freevalue(&technique->annotations[j], f, d); } // for - f(technique->passes, d); + f((void *) technique->annotations, d); + } // for + f((void *) effect->techniques, d); + + /* Free object table */ + 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) + MOJOSHADER_freePreshader(object->shader.preshader, f, d); + else + MOJOSHADER_freeParseData(object->shader.shader); + f((void *) object->shader.params, d); + f((void *) object->shader.samplers, d); + f((void *) object->shader.preshader_params, d); + } // if + else if (object->type == MOJOSHADER_SYMTYPE_SAMPLER + || object->type == MOJOSHADER_SYMTYPE_SAMPLER1D + || object->type == MOJOSHADER_SYMTYPE_SAMPLER2D + || object->type == MOJOSHADER_SYMTYPE_SAMPLER3D + || object->type == MOJOSHADER_SYMTYPE_SAMPLERCUBE) + f((void *) object->mapping.name, d); + else if (object->type == MOJOSHADER_SYMTYPE_STRING) + f((void *) object->string.string, d); } // for + f((void *) effect->objects, d); - f(effect->techniques, d); + /* Free base effect structure */ + f((void *) effect, d); +} // MOJOSHADER_freeEffect - for (i = 0; i < effect->texture_count; i++) - f((void *) effect->textures[i].name, d); - f(effect->textures, d); - for (i = 0; i < effect->shader_count; i++) - MOJOSHADER_freeParseData(effect->shaders[i].shader); - f(effect->shaders, d); +void copyvalue(MOJOSHADER_effectValue *dst, + MOJOSHADER_effectValue *src, + MOJOSHADER_malloc m, + void *d) +{ + int i; + uint32 siz = 0; + char *stringcopy = NULL; + + // !!! FIXME: Out of memory check! + #define COPY_STRING(location) \ + if (src->location != NULL) \ + { \ + siz = strlen(src->location) + 1; \ + stringcopy = (char *) m(siz, d); \ + strcpy(stringcopy, src->location); \ + dst->location = stringcopy; \ + } // if - f(effect, d); -} // MOJOSHADER_freeEffect + COPY_STRING(name) + COPY_STRING(semantic) + dst->element_count = src->element_count; + dst->row_count = src->row_count; + dst->column_count = src->column_count; + dst->value_class = src->value_class; + dst->value_type = src->value_type; + dst->value_count = src->value_count; + + if (dst->value_class == MOJOSHADER_SYMCLASS_SCALAR + || dst->value_class == MOJOSHADER_SYMCLASS_VECTOR + || dst->value_class == MOJOSHADER_SYMCLASS_MATRIX_ROWS + || dst->value_class == MOJOSHADER_SYMCLASS_MATRIX_COLUMNS) + { + siz = dst->value_count * 4; + dst->values = m(siz, d); + // !!! FIXME: Out of memory check! + memcpy(dst->values, src->values, siz); + } // if + else if (dst->value_class == MOJOSHADER_SYMCLASS_OBJECT) + { + if (dst->value_type == MOJOSHADER_SYMTYPE_SAMPLER + || dst->value_type == MOJOSHADER_SYMTYPE_SAMPLER1D + || dst->value_type == MOJOSHADER_SYMTYPE_SAMPLER2D + || dst->value_type == MOJOSHADER_SYMTYPE_SAMPLER3D + || dst->value_type == MOJOSHADER_SYMTYPE_SAMPLERCUBE) + { + siz = dst->value_count * sizeof (MOJOSHADER_effectSamplerState); + dst->values = m(siz, d); + // !!! FIXME: Out of memory check! + memset(dst->values, '\0', siz); + for (i = 0; i < dst->value_count; i++) + { + dst->valuesSS[i].type = src->valuesSS[i].type; + copyvalue(&dst->valuesSS[i].value, + &src->valuesSS[i].value, + m, d); + } // for + } // if + else + { + siz = dst->value_count * 4; + dst->values = m(siz, d); + // !!! FIXME: Out of memory check! + memcpy(dst->values, src->values, siz); + } // else + } // else if + else if (dst->value_class == MOJOSHADER_SYMCLASS_STRUCT) + { + /* TODO: See readvalue! -flibit */ + } // else if + + #undef COPY_STRING +} // copyvalue + + +void copysymbolinfo(MOJOSHADER_symbolTypeInfo *dst, + MOJOSHADER_symbolTypeInfo *src, + MOJOSHADER_malloc m, + void *d) +{ + int i; + uint32 siz; + char *stringcopy; + + dst->parameter_class = src->parameter_class; + dst->parameter_type = src->parameter_type; + dst->rows = src->rows; + dst->columns = src->columns; + dst->elements = src->elements; + dst->member_count = src->member_count; + + if (dst->member_count > 0) + { + siz = sizeof (MOJOSHADER_symbolStructMember) * dst->member_count; + dst->members = (MOJOSHADER_symbolStructMember *) m(siz, d); + // !!! FIXME: Out of memory check! + for (i = 0; i < dst->member_count; i++) + { + if (src->members[i].name != NULL) + { + siz = strlen(src->members[i].name) + 1; + stringcopy = (char *) m(siz, d); + strcpy(stringcopy, src->members[i].name); + dst->members[i].name = stringcopy; + } // if + copysymbolinfo(&dst->members[i].info, &src->members[i].info, m, d); + } // for + } // if +} // copysymbolinfo + + +void copysymbol(MOJOSHADER_symbol *dst, + MOJOSHADER_symbol *src, + MOJOSHADER_malloc m, + void *d) +{ + uint32 siz = strlen(src->name) + 1; + char *stringcopy = (char *) m(siz, d); + // !!! FIXME: Out of memory check! + strcpy(stringcopy, src->name); + dst->name = stringcopy; + dst->register_set = src->register_set; + dst->register_index = src->register_index; + dst->register_count = src->register_count; + copysymbolinfo(&dst->info, &src->info, m, d); +} // copysymbol + + +MOJOSHADER_preshader *copypreshader(const MOJOSHADER_preshader *src, + MOJOSHADER_malloc m, + void *d) +{ + int i, j; + uint32 siz; + MOJOSHADER_preshader *retval; + + retval = (MOJOSHADER_preshader *) m(sizeof (MOJOSHADER_preshader), d); + // !!! FIXME: Out of memory check! + memset(retval, '\0', sizeof (MOJOSHADER_preshader)); + + siz = sizeof (double) * src->literal_count; + retval->literal_count = src->literal_count; + retval->literals = (double *) m(siz, d); + // !!! FIXME: Out of memory check! + memcpy(retval->literals, src->literals, siz); + + retval->temp_count = src->temp_count; + + siz = sizeof (MOJOSHADER_symbol) * src->symbol_count; + retval->symbol_count = src->symbol_count; + retval->symbols = (MOJOSHADER_symbol *) m(siz, d); + // !!! FIXME: Out of memory check! + memset(retval->symbols, '\0', siz); + + for (i = 0; i < retval->symbol_count; i++) + copysymbol(&retval->symbols[i], &src->symbols[i], m, d); + + siz = sizeof (MOJOSHADER_preshaderInstruction) * src->instruction_count; + retval->instruction_count = src->instruction_count; + retval->instructions = (MOJOSHADER_preshaderInstruction *) m(siz, d); + // !!! FIXME: Out of memory check! + memcpy(retval->instructions, src->instructions, siz); + for (i = 0; i < retval->instruction_count; i++) + for (j = 0; j < retval->instructions[i].operand_count; j++) + { + siz = sizeof (unsigned int) * retval->instructions[i].operands[j].array_register_count; + retval->instructions[i].operands[j].array_registers = (unsigned int *) m(siz, d); + // !!! FIXME: Out of memory check! + memcpy(retval->instructions[i].operands[j].array_registers, + src->instructions[i].operands[j].array_registers, + siz); + } // for + + siz = sizeof (float) * 4 * src->register_count; + retval->register_count = src->register_count; + retval->registers = (float *) m(siz, d); + // !!! FIXME: Out of memory check! + memcpy(retval->registers, src->registers, siz); + + return retval; +} // copypreshader + + +MOJOSHADER_parseData *copyparsedata(const MOJOSHADER_parseData *src, + MOJOSHADER_malloc m, + void *d) +{ + int i; + uint32 siz; + char *stringcopy; + MOJOSHADER_parseData *retval; + + retval = (MOJOSHADER_parseData *) m(sizeof (MOJOSHADER_parseData), d); + memset(retval, '\0', sizeof (MOJOSHADER_parseData)); + + /* Copy malloc/free */ + retval->malloc = src->malloc; + retval->free = src->free; + retval->malloc_data = src->malloc_data; + + // !!! FIXME: Out of memory check! + #define COPY_STRING(location) \ + siz = strlen(src->location) + 1; \ + stringcopy = (char *) m(siz, d); \ + strcpy(stringcopy, src->location); \ + retval->location = stringcopy; \ + + /* Copy errors */ + siz = sizeof (MOJOSHADER_error) * src->error_count; + retval->error_count = src->error_count; + retval->errors = (MOJOSHADER_error *) m(siz, d); + // !!! FIXME: Out of memory check! + memset(retval->errors, '\0', siz); + for (i = 0; i < retval->error_count; i++) + { + COPY_STRING(errors[i].error) + COPY_STRING(errors[i].filename) + retval->errors[i].error_position = src->errors[i].error_position; + } // for + + /* Copy profile string constant */ + retval->profile = src->profile; + + /* Copy shader output */ + retval->output_len = src->output_len; + stringcopy = (char *) m(src->output_len, d); + memcpy(stringcopy, src->output, src->output_len); + retval->output = stringcopy; + + /* Copy miscellaneous shader info */ + retval->instruction_count = src->instruction_count; + retval->shader_type = src->shader_type; + retval->major_ver = src->major_ver; + retval->minor_ver = src->minor_ver; + + /* Copy uniforms */ + siz = sizeof (MOJOSHADER_uniform) * src->uniform_count; + retval->uniform_count = src->uniform_count; + retval->uniforms = (MOJOSHADER_uniform *) m(siz, d); + // !!! FIXME: Out of memory check! + memset(retval->uniforms, '\0', siz); + for (i = 0; i < retval->uniform_count; i++) + { + retval->uniforms[i].type = src->uniforms[i].type; + retval->uniforms[i].index = src->uniforms[i].index; + retval->uniforms[i].array_count = src->uniforms[i].array_count; + retval->uniforms[i].constant = src->uniforms[i].constant; + COPY_STRING(uniforms[i].name) + } // for + + /* Copy constants */ + siz = sizeof (MOJOSHADER_constant) * src->constant_count; + retval->constant_count = src->constant_count; + retval->constants = (MOJOSHADER_constant *) m(siz, d); + // !!! FIXME: Out of memory check! + memcpy(retval->constants, src->constants, siz); + + /* Copy samplers */ + siz = sizeof (MOJOSHADER_sampler) * src->sampler_count; + retval->sampler_count = src->sampler_count; + retval->samplers = (MOJOSHADER_sampler *) m(siz, d); + // !!! FIXME: Out of memory check! + memset(retval->samplers, '\0', siz); + for (i = 0; i < retval->sampler_count; i++) + { + retval->samplers[i].type = src->samplers[i].type; + retval->samplers[i].index = src->samplers[i].index; + COPY_STRING(samplers[i].name) + retval->samplers[i].texbem = src->samplers[i].texbem; + } // for + + /* Copy attributes */ + siz = sizeof (MOJOSHADER_attribute) * src->attribute_count; + retval->attribute_count = src->attribute_count; + retval->attributes = (MOJOSHADER_attribute *) m(siz, d); + // !!! FIXME: Out of memory check! + memset(retval->attributes, '\0', siz); + for (i = 0; i < retval->attribute_count; i++) + { + retval->attributes[i].usage = src->attributes[i].usage; + retval->attributes[i].index = src->attributes[i].index; + COPY_STRING(attributes[i].name) + } // for + + /* Copy outputs */ + siz = sizeof (MOJOSHADER_attribute) * src->output_count; + retval->output_count = src->output_count; + retval->outputs = (MOJOSHADER_attribute *) m(siz, d); + // !!! FIXME: Out of memory check! + memset(retval->outputs, '\0', siz); + for (i = 0; i < retval->output_count; i++) + { + retval->outputs[i].usage = src->outputs[i].usage; + retval->outputs[i].index = src->outputs[i].index; + COPY_STRING(outputs[i].name) + } // for + + #undef COPY_STRING + + /* Copy swizzles */ + siz = sizeof (MOJOSHADER_swizzle) * src->swizzle_count; + retval->swizzle_count = src->swizzle_count; + retval->swizzles = (MOJOSHADER_swizzle *) m(siz, d); + // !!! FIXME: Out of memory check! + memcpy(retval->swizzles, src->swizzles, siz); + + /* Copy symbols */ + siz = sizeof (MOJOSHADER_symbol) * src->symbol_count; + retval->symbol_count = src->symbol_count; + retval->symbols = (MOJOSHADER_symbol *) m(siz, d); + // !!! FIXME: Out of memory check! + memset(retval->symbols, '\0', siz); + for (i = 0; i < retval->symbol_count; i++) + copysymbol(&retval->symbols[i], &src->symbols[i], m, d); + + /* Copy preshader */ + if (src->preshader != NULL) + retval->preshader = copypreshader(src->preshader, m, d); + + return retval; +} // copyparsedata + + +MOJOSHADER_effect *MOJOSHADER_cloneEffect(const MOJOSHADER_effect *effect) +{ + int i, j, k; + MOJOSHADER_effect *clone; + MOJOSHADER_malloc m = effect->malloc; + void *d = effect->malloc_data; + uint32 siz = 0; + char *stringcopy = NULL; + uint32 curSampler; + + if ((effect == NULL) || (effect == &MOJOSHADER_out_of_mem_effect)) + return NULL; // no-op. + + clone = (MOJOSHADER_effect *) m(sizeof (MOJOSHADER_effect), d); + if (clone == NULL) + return NULL; // Maybe out_of_mem_effect instead? + memset(clone, '\0', sizeof (MOJOSHADER_effect)); + + /* Copy malloc/free */ + clone->malloc = effect->malloc; + clone->free = effect->free; + clone->malloc_data = effect->malloc_data; + + #define COPY_STRING(location) \ + siz = strlen(effect->location) + 1; \ + stringcopy = (char *) m(siz, d); \ + if (stringcopy == NULL) \ + goto cloneEffect_outOfMemory; \ + strcpy(stringcopy, effect->location); \ + clone->location = stringcopy; \ + + /* Copy errors */ + siz = sizeof (MOJOSHADER_error) * effect->error_count; + clone->error_count = effect->error_count; + clone->errors = (MOJOSHADER_error *) m(siz, d); + if (clone->errors == NULL) + goto cloneEffect_outOfMemory; + memset(clone->errors, '\0', siz); + for (i = 0; i < clone->error_count; i++) + { + COPY_STRING(errors[i].error) + COPY_STRING(errors[i].filename) + clone->errors[i].error_position = effect->errors[i].error_position; + } // for + + /* Copy profile string */ + COPY_STRING(profile) + + /* Copy parameters */ + siz = sizeof (MOJOSHADER_effectParam) * effect->param_count; + clone->param_count = effect->param_count; + clone->params = (MOJOSHADER_effectParam *) m(siz, d); + if (clone->params == NULL) + goto cloneEffect_outOfMemory; + memset(clone->params, '\0', siz); + for (i = 0; i < clone->param_count; i++) + { + copyvalue(&clone->params[i].value, &effect->params[i].value, m, d); + + /* Copy parameter annotations */ + siz = sizeof (MOJOSHADER_effectAnnotation) * effect->params[i].annotation_count; + clone->params[i].annotation_count = effect->params[i].annotation_count; + clone->params[i].annotations = (MOJOSHADER_effectAnnotation *) m(siz, d); + if (clone->params[i].annotations == NULL) + goto cloneEffect_outOfMemory; + memset(clone->params[i].annotations, '\0', siz); + for (j = 0; j < clone->params[i].annotation_count; j++) + copyvalue(&clone->params[i].annotations[j], + &effect->params[i].annotations[j], + m, d); + } // for + + /* Copy techniques */ + siz = sizeof (MOJOSHADER_effectTechnique) * effect->technique_count; + clone->technique_count = effect->technique_count; + clone->techniques = (MOJOSHADER_effectTechnique *) m(siz, d); + if (clone->techniques == NULL) + goto cloneEffect_outOfMemory; + memset(clone->techniques, '\0', siz); + for (i = 0; i < clone->technique_count; i++) + { + COPY_STRING(techniques[i].name) + + /* Copy passes */ + siz = sizeof (MOJOSHADER_effectPass) * effect->techniques[i].pass_count; + clone->techniques[i].pass_count = effect->techniques[i].pass_count; + clone->techniques[i].passes = (MOJOSHADER_effectPass *) m(siz, d); + if (clone->techniques[i].passes == NULL) + goto cloneEffect_outOfMemory; + memset(clone->techniques[i].passes, '\0', siz); + for (j = 0; j < clone->techniques[i].pass_count; j++) + { + COPY_STRING(techniques[i].passes[j].name) + + /* Copy pass states */ + siz = sizeof (MOJOSHADER_effectState) * effect->techniques[i].passes[j].state_count; + clone->techniques[i].passes[j].state_count = effect->techniques[i].passes[j].state_count; + clone->techniques[i].passes[j].states = (MOJOSHADER_effectState *) m(siz, d); + if (clone->techniques[i].passes[j].states == NULL) + goto cloneEffect_outOfMemory; + memset(clone->techniques[i].passes[j].states, '\0', siz); + for (k = 0; k < clone->techniques[i].passes[j].state_count; k++) + { + clone->techniques[i].passes[j].states[k].type = effect->techniques[i].passes[j].states[k].type; + copyvalue(&clone->techniques[i].passes[j].states[k].value, + &effect->techniques[i].passes[j].states[k].value, + m, d); + } // for + + /* Copy pass annotations */ + siz = sizeof (MOJOSHADER_effectAnnotation) * effect->techniques[i].passes[j].annotation_count; + clone->techniques[i].passes[j].annotation_count = effect->techniques[i].passes[j].annotation_count; + clone->techniques[i].passes[j].annotations = (MOJOSHADER_effectAnnotation *) m(siz, d); + if (clone->techniques[i].passes[j].annotations == NULL) + goto cloneEffect_outOfMemory; + memset(clone->techniques[i].passes[j].annotations, '\0', siz); + for (k = 0; k < clone->techniques[i].passes[j].annotation_count; k++) + copyvalue(&clone->techniques[i].passes[j].annotations[k], + &effect->techniques[i].passes[j].annotations[k], + m, d); + } // for + + /* Copy technique annotations */ + siz = sizeof (MOJOSHADER_effectAnnotation) * effect->techniques[i].annotation_count; + clone->techniques[i].annotation_count = effect->techniques[i].annotation_count; + clone->techniques[i].annotations = (MOJOSHADER_effectAnnotation *) m(siz, d); + if (clone->techniques[i].annotations == NULL) + goto cloneEffect_outOfMemory; + memset(clone->techniques[i].annotations, '\0', siz); + for (j = 0; j < clone->techniques[i].annotation_count; j++) + copyvalue(&clone->techniques[i].annotations[j], + &effect->techniques[i].annotations[j], + m, d); + } // for + + /* Copy the current technique/pass */ + for (i = 0; i < effect->technique_count; i++) + if (&effect->techniques[i] == effect->current_technique) + { + clone->current_technique = &clone->techniques[i]; + break; + } // if + assert(clone->current_technique != NULL); + clone->current_pass = effect->current_pass; + assert(clone->current_pass == -1); + + /* Copy object table */ + siz = sizeof (MOJOSHADER_effectObject) * effect->object_count; + clone->object_count = effect->object_count; + clone->objects = (MOJOSHADER_effectObject *) m(siz, d); + if (clone->objects == NULL) + goto cloneEffect_outOfMemory; + memset(clone->objects, '\0', siz); + for (i = 0; i < clone->object_count; i++) + { + clone->objects[i].type = effect->objects[i].type; + if (clone->objects[i].type == MOJOSHADER_SYMTYPE_PIXELSHADER + || clone->objects[i].type == MOJOSHADER_SYMTYPE_VERTEXSHADER) + { + clone->objects[i].shader.technique = effect->objects[i].shader.technique; + clone->objects[i].shader.pass = effect->objects[i].shader.pass; + clone->objects[i].shader.is_preshader = effect->objects[i].shader.is_preshader; + siz = sizeof (uint32) * effect->objects[i].shader.preshader_param_count; + clone->objects[i].shader.preshader_param_count = effect->objects[i].shader.preshader_param_count; + clone->objects[i].shader.preshader_params = (uint32 *) m(siz, d); + memcpy(clone->objects[i].shader.preshader_params, + effect->objects[i].shader.preshader_params, + siz); + siz = sizeof (uint32) * effect->objects[i].shader.param_count; + clone->objects[i].shader.param_count = effect->objects[i].shader.param_count; + clone->objects[i].shader.params = (uint32 *) m(siz, d); + memcpy(clone->objects[i].shader.params, + effect->objects[i].shader.params, + siz); + + if (clone->objects[i].shader.is_preshader) + { + clone->objects[i].shader.preshader = copypreshader(effect->objects[i].shader.preshader, + m, d); + continue; + } // if + + clone->objects[i].shader.shader = copyparsedata(effect->objects[i].shader.shader, + m, d); + + siz = sizeof (MOJOSHADER_samplerStateRegister) * effect->objects[i].shader.sampler_count; + clone->objects[i].shader.sampler_count = effect->objects[i].shader.sampler_count; + clone->objects[i].shader.samplers = (MOJOSHADER_samplerStateRegister *) m(siz, d); + if (clone->objects[i].shader.samplers == NULL) + goto cloneEffect_outOfMemory; + curSampler = 0; + for (j = 0; j < clone->objects[i].shader.shader->symbol_count; j++) + if (clone->objects[i].shader.shader->symbols[j].register_set == MOJOSHADER_SYMREGSET_SAMPLER) + { + clone->objects[i].shader.samplers[curSampler].sampler_name = clone->objects[i].shader.shader->symbols[j].name; + clone->objects[i].shader.samplers[curSampler].sampler_register = clone->objects[i].shader.shader->symbols[j].register_index; + clone->objects[i].shader.samplers[curSampler].sampler_state_count = clone->params[clone->objects[i].shader.params[j]].value.value_count; + clone->objects[i].shader.samplers[curSampler].sampler_states = clone->params[clone->objects[i].shader.params[j]].value.valuesSS; + curSampler++; + } // if + } // if + else if (clone->objects[i].type == MOJOSHADER_SYMTYPE_SAMPLER + || clone->objects[i].type == MOJOSHADER_SYMTYPE_SAMPLER1D + || clone->objects[i].type == MOJOSHADER_SYMTYPE_SAMPLER2D + || clone->objects[i].type == MOJOSHADER_SYMTYPE_SAMPLER3D + || clone->objects[i].type == MOJOSHADER_SYMTYPE_SAMPLERCUBE) + { + COPY_STRING(objects[i].mapping.name) + } // else if + else if (clone->objects[i].type == MOJOSHADER_SYMTYPE_STRING) + { + COPY_STRING(objects[i].string.string) + } // else if + } // for + + #undef COPY_STRING + + return clone; + +cloneEffect_outOfMemory: + MOJOSHADER_freeEffect(clone); + return NULL; +} // MOJOSHADER_cloneEffect + + +void MOJOSHADER_effectSetRawValueHandle(const MOJOSHADER_effectParam *parameter, + const void *data, + const unsigned int offset, + const unsigned int len) +{ + // !!! FIXME: uint32* case is arbitary, for Win32 -flibit + memcpy((uint32 *) parameter->value.values + offset, data, len); +} // MOJOSHADER_effectSetRawValueHandle + + +void MOJOSHADER_effectSetRawValueName(const MOJOSHADER_effect *effect, + const char *name, + const void *data, + const unsigned int offset, + const unsigned int len) +{ + int i; + for (i = 0; i < effect->param_count; i++) + { + if (strcmp(name, effect->params[i].value.name) == 0) + { + // !!! FIXME: uint32* case is arbitary, for Win32 -flibit + memcpy((uint32 *) effect->params[i].value.values + offset, data, len); + return; + } // if + } // for + assert(0 && "Effect parameter not found!"); +} // MOJOSHADER_effectSetRawValueName + + +const MOJOSHADER_effectTechnique *MOJOSHADER_effectGetCurrentTechnique(const MOJOSHADER_effect *effect) +{ + return effect->current_technique; +} // MOJOSHADER_effectGetCurrentTechnique + + +void MOJOSHADER_effectSetTechnique(MOJOSHADER_effect *effect, + const MOJOSHADER_effectTechnique *technique) +{ + int i; + for (i = 0; i < effect->technique_count; i++) + { + if (technique == &effect->techniques[i]) + { + effect->current_technique = technique; + return; + } // if + } // for + assert(0 && "Technique is not part of this effect!"); +} // MOJOSHADER_effectSetTechnique + + +const MOJOSHADER_effectTechnique *MOJOSHADER_effectFindNextValidTechnique(const MOJOSHADER_effect *effect, + const MOJOSHADER_effectTechnique *technique +) +{ + int i; + if (technique == NULL) + return &effect->techniques[0]; + for (i = 0; i < effect->technique_count; i++) + { + if (technique == &effect->techniques[i]) + { + if (i == effect->technique_count - 1) + return NULL; /* We were passed the last technique! */ + return &effect->techniques[i + 1]; + } // if + } // for + assert(0 && "Technique is not part of this effect!"); +} // MOJOSHADER_effectFindNextValidTechnique + +#endif // MOJOSHADER_EFFECT_SUPPORT // end of mojoshader_effects.c ... diff --git a/mojoshader_effects.h b/mojoshader_effects.h new file mode 100644 index 00000000..f50fca90 --- /dev/null +++ b/mojoshader_effects.h @@ -0,0 +1,809 @@ +/** + * 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. + */ + +#ifndef MOJOSHADER_EFFECTS_H +#define MOJOSHADER_EFFECTS_H + +#ifdef MOJOSHADER_EFFECT_SUPPORT + +/* MOJOSHADER_effectState types... */ + +typedef enum MOJOSHADER_renderStateType +{ + /* Note that we are NOT using the actual RS values from D3D here. + * For some reason, in the binary data, it's 0-based. + * Even worse, it doesn't even seem to be in order. + * Here is the list of changes compared to the real D3DRS enum: + * - All of the RS_WRAP values are in a row, not separate! + * + * -flibit + */ + MOJOSHADER_RS_ZENABLE, + MOJOSHADER_RS_FILLMODE, + MOJOSHADER_RS_SHADEMODE, + MOJOSHADER_RS_ZWRITEENABLE, + MOJOSHADER_RS_ALPHATESTENABLE, + MOJOSHADER_RS_LASTPIXEL, + MOJOSHADER_RS_SRCBLEND, + MOJOSHADER_RS_DESTBLEND, + MOJOSHADER_RS_CULLMODE, + MOJOSHADER_RS_ZFUNC, + MOJOSHADER_RS_ALPHAREF, + MOJOSHADER_RS_ALPHAFUNC, + MOJOSHADER_RS_DITHERENABLE, + MOJOSHADER_RS_ALPHABLENDENABLE, + MOJOSHADER_RS_FOGENABLE, + MOJOSHADER_RS_SPECULARENABLE, + MOJOSHADER_RS_FOGCOLOR, + MOJOSHADER_RS_FOGTABLEMODE, + MOJOSHADER_RS_FOGSTART, + MOJOSHADER_RS_FOGEND, + MOJOSHADER_RS_FOGDENSITY, + MOJOSHADER_RS_RANGEFOGENABLE, + MOJOSHADER_RS_STENCILENABLE, + MOJOSHADER_RS_STENCILFAIL, + MOJOSHADER_RS_STENCILZFAIL, + MOJOSHADER_RS_STENCILPASS, + MOJOSHADER_RS_STENCILFUNC, + MOJOSHADER_RS_STENCILREF, + MOJOSHADER_RS_STENCILMASK, + MOJOSHADER_RS_STENCILWRITEMASK, + MOJOSHADER_RS_TEXTUREFACTOR, + MOJOSHADER_RS_WRAP0, + MOJOSHADER_RS_WRAP1, + MOJOSHADER_RS_WRAP2, + MOJOSHADER_RS_WRAP3, + MOJOSHADER_RS_WRAP4, + MOJOSHADER_RS_WRAP5, + MOJOSHADER_RS_WRAP6, + MOJOSHADER_RS_WRAP7, + MOJOSHADER_RS_WRAP8, + MOJOSHADER_RS_WRAP9, + MOJOSHADER_RS_WRAP10, + MOJOSHADER_RS_WRAP11, + MOJOSHADER_RS_WRAP12, + MOJOSHADER_RS_WRAP13, + MOJOSHADER_RS_WRAP14, + MOJOSHADER_RS_WRAP15, + MOJOSHADER_RS_CLIPPING, + MOJOSHADER_RS_LIGHTING, + MOJOSHADER_RS_AMBIENT, + MOJOSHADER_RS_FOGVERTEXMODE, + MOJOSHADER_RS_COLORVERTEX, + MOJOSHADER_RS_LOCALVIEWER, + MOJOSHADER_RS_NORMALIZENORMALS, + MOJOSHADER_RS_DIFFUSEMATERIALSOURCE, + MOJOSHADER_RS_SPECULARMATERIALSOURCE, + MOJOSHADER_RS_AMBIENTMATERIALSOURCE, + MOJOSHADER_RS_EMISSIVEMATERIALSOURCE, + MOJOSHADER_RS_VERTEXBLEND, + MOJOSHADER_RS_CLIPPLANEENABLE, + MOJOSHADER_RS_POINTSIZE, + MOJOSHADER_RS_POINTSIZE_MIN, + MOJOSHADER_RS_POINTSPRITEENABLE, + MOJOSHADER_RS_POINTSCALEENABLE, + MOJOSHADER_RS_POINTSCALE_A, + MOJOSHADER_RS_POINTSCALE_B, + MOJOSHADER_RS_POINTSCALE_C, + MOJOSHADER_RS_MULTISAMPLEANTIALIAS, + MOJOSHADER_RS_MULTISAMPLEMASK, + MOJOSHADER_RS_PATCHEDGESTYLE, + MOJOSHADER_RS_DEBUGMONITORTOKEN, + MOJOSHADER_RS_POINTSIZE_MAX, + MOJOSHADER_RS_INDEXEDVERTEXBLENDENABLE, + MOJOSHADER_RS_COLORWRITEENABLE, + MOJOSHADER_RS_TWEENFACTOR, + MOJOSHADER_RS_BLENDOP, + MOJOSHADER_RS_POSITIONDEGREE, + MOJOSHADER_RS_NORMALDEGREE, + MOJOSHADER_RS_SCISSORTESTENABLE, + MOJOSHADER_RS_SLOPESCALEDEPTHBIAS, + MOJOSHADER_RS_ANTIALIASEDLINEENABLE, + MOJOSHADER_RS_MINTESSELLATIONLEVEL, + MOJOSHADER_RS_MAXTESSELLATIONLEVEL, + MOJOSHADER_RS_ADAPTIVETESS_X, + MOJOSHADER_RS_ADAPTIVETESS_Y, + MOJOSHADER_RS_ADAPTIVETESS_Z, + MOJOSHADER_RS_ADAPTIVETESS_W, + MOJOSHADER_RS_ENABLEADAPTIVETESSELLATION, + MOJOSHADER_RS_TWOSIDEDSTENCILMODE, + MOJOSHADER_RS_CCW_STENCILFAIL, + MOJOSHADER_RS_CCW_STENCILZFAIL, + MOJOSHADER_RS_CCW_STENCILPASS, + MOJOSHADER_RS_CCW_STENCILFUNC, + MOJOSHADER_RS_COLORWRITEENABLE1, + MOJOSHADER_RS_COLORWRITEENABLE2, + MOJOSHADER_RS_COLORWRITEENABLE3, + MOJOSHADER_RS_BLENDFACTOR, + MOJOSHADER_RS_SRGBWRITEENABLE, + MOJOSHADER_RS_DEPTHBIAS, + MOJOSHADER_RS_SEPARATEALPHABLENDENABLE, + MOJOSHADER_RS_SRCBLENDALPHA, + MOJOSHADER_RS_DESTBLENDALPHA, + MOJOSHADER_RS_BLENDOPALPHA, + + /* These aren't really "states", but these numbers are + * referred to by MOJOSHADER_effectStateType as such. + */ + MOJOSHADER_RS_VERTEXSHADER = 146, + MOJOSHADER_RS_PIXELSHADER = 147 +} MOJOSHADER_renderStateType; + +typedef enum MOJOSHADER_zBufferType +{ + MOJOSHADER_ZB_FALSE, + MOJOSHADER_ZB_TRUE, + MOJOSHADER_ZB_USEW +} MOJOSHADER_zBufferType; + +typedef enum MOJOSHADER_fillMode +{ + MOJOSHADER_FILL_POINT = 1, + MOJOSHADER_FILL_WIREFRAME = 2, + MOJOSHADER_FILL_SOLID = 3 +} MOJOSHADER_fillMode; + +typedef enum MOJOSHADER_shadeMode +{ + MOJOSHADER_SHADE_FLAT = 1, + MOJOSHADER_SHADE_GOURAUD = 2, + MOJOSHADER_SHADE_PHONG = 3, +} MOJOSHADER_shadeMode; + +typedef enum MOJOSHADER_blendMode +{ + MOJOSHADER_BLEND_ZERO = 1, + MOJOSHADER_BLEND_ONE = 2, + MOJOSHADER_BLEND_SRCCOLOR = 3, + MOJOSHADER_BLEND_INVSRCCOLOR = 4, + MOJOSHADER_BLEND_SRCALPHA = 5, + MOJOSHADER_BLEND_INVSRCALPHA = 6, + MOJOSHADER_BLEND_DESTALPHA = 7, + MOJOSHADER_BLEND_INVDESTALPHA = 8, + MOJOSHADER_BLEND_DESTCOLOR = 9, + MOJOSHADER_BLEND_INVDESTCOLOR = 10, + MOJOSHADER_BLEND_SRCALPHASAT = 11, + MOJOSHADER_BLEND_BOTHSRCALPHA = 12, + MOJOSHADER_BLEND_BOTHINVSRCALPHA = 13, + MOJOSHADER_BLEND_BLENDFACTOR = 14, + MOJOSHADER_BLEND_INVBLENDFACTOR = 15, + MOJOSHADER_BLEND_SRCCOLOR2 = 16, + MOJOSHADER_BLEND_INVSRCCOLOR2 = 17 +} MOJOSHADER_blendMode; + +typedef enum MOJOSHADER_cullMode +{ + MOJOSHADER_CULL_NONE = 1, + MOJOSHADER_CULL_CW = 2, + MOJOSHADER_CULL_CCW = 3 +} MOJOSHADER_cullMode; + +typedef enum MOJOSHADER_compareFunc +{ + MOJOSHADER_CMP_NEVER = 1, + MOJOSHADER_CMP_LESS = 2, + MOJOSHADER_CMP_EQUAL = 3, + MOJOSHADER_CMP_LESSEQUAL = 4, + MOJOSHADER_CMP_GREATER = 5, + MOJOSHADER_CMP_NOTEQUAL = 6, + MOJOSHADER_CMP_GREATEREQUAL = 7, + MOJOSHADER_CMP_ALWAYS = 8 +} MOJOSHADER_compareFunc; + +typedef enum MOJOSHADER_fogMode +{ + MOJOSHADER_FOG_NONE, + MOJOSHADER_FOG_EXP, + MOJOSHADER_FOG_EXP2, + MOJOSHADER_FOG_LINEAR +} MOJOSHADER_fogMode; + +typedef enum MOJOSHADER_stencilOp +{ + MOJOSHADER_STENCILOP_KEEP = 1, + MOJOSHADER_STENCILOP_ZERO = 2, + MOJOSHADER_STENCILOP_REPLACE = 3, + MOJOSHADER_STENCILOP_INCRSAT = 4, + MOJOSHADER_STENCILOP_DECRSAT = 5, + MOJOSHADER_STENCILOP_INVERT = 6, + MOJOSHADER_STENCILOP_INCR = 7, + MOJOSHADER_STENCILOP_DECR = 8 +} MOJOSHADER_stencilOp; + +typedef enum MOJOSHADER_materialColorSource +{ + MOJOSHADER_MCS_MATERIAL, + MOJOSHADER_MCS_COLOR1, + MOJOSHADER_MCS_COLOR2 +} MOJOSHADER_materialColorSource; + +typedef enum MOJOSHADER_vertexBlendFlags +{ + MOJOSHADER_VBF_DISABLE = 0, + MOJOSHADER_VBF_1WEIGHTS = 1, + MOJOSHADER_VBF_2WEIGHTS = 2, + MOJOSHADER_VBF_3WEIGHTS = 3, + MOJOSHADER_VBF_TWEENING = 255, + MOJOSHADER_VBF_0WEIGHTS = 256, +} MOJOSHADER_vertexBlendFlags; + +typedef enum MOJOSHADER_patchedEdgeStyle +{ + MOJOSHADER_PATCHEDGE_DISCRETE, + MOJOSHADER_PATCHEDGE_CONTINUOUS +} MOJOSHADER_patchedEdgeStyle; + +typedef enum MOJOSHADER_debugMonitorTokens +{ + MOJOSHADER_DMT_ENABLE, + MOJOSHADER_DMT_DISABLE +} MOJOSHADER_debugMonitorTokens; + +typedef enum MOJOSHADER_blendOp +{ + MOJOSHADER_BLENDOP_ADD = 1, + MOJOSHADER_BLENDOP_SUBTRACT = 2, + MOJOSHADER_BLENDOP_REVSUBTRACT = 3, + MOJOSHADER_BLENDOP_MIN = 4, + MOJOSHADER_BLENDOP_MAX = 5 +} MOJOSHADER_blendOp; + +typedef enum MOJOSHADER_degreeType +{ + MOJOSHADER_DEGREE_LINEAR = 1, + MOJOSHADER_DEGREE_QUADRATIC = 2, + MOJOSHADER_DEGREE_CUBIC = 3, + MOJOSHADER_DEGREE_QUINTIC = 5 +} MOJOSHADER_degreeType; + + +/* MOJOSHADER_effectSamplerState types... */ + +typedef enum MOJOSHADER_samplerStateType +{ + MOJOSHADER_SAMP_UNKNOWN0 = 0, + MOJOSHADER_SAMP_UNKNOWN1 = 1, + MOJOSHADER_SAMP_UNKNOWN2 = 2, + MOJOSHADER_SAMP_UNKNOWN3 = 3, + MOJOSHADER_SAMP_TEXTURE = 4, + MOJOSHADER_SAMP_ADDRESSU = 5, + MOJOSHADER_SAMP_ADDRESSV = 6, + MOJOSHADER_SAMP_ADDRESSW = 7, + MOJOSHADER_SAMP_BORDERCOLOR = 8, + MOJOSHADER_SAMP_MAGFILTER = 9, + MOJOSHADER_SAMP_MINFILTER = 10, + MOJOSHADER_SAMP_MIPFILTER = 11, + MOJOSHADER_SAMP_MIPMAPLODBIAS = 12, + MOJOSHADER_SAMP_MAXMIPLEVEL = 13, + MOJOSHADER_SAMP_MAXANISOTROPY = 14, + MOJOSHADER_SAMP_SRGBTEXTURE = 15, + MOJOSHADER_SAMP_ELEMENTINDEX = 16, + MOJOSHADER_SAMP_DMAPOFFSET = 17 +} MOJOSHADER_samplerStateType; + +typedef enum MOJOSHADER_textureAddress +{ + MOJOSHADER_TADDRESS_WRAP = 1, + MOJOSHADER_TADDRESS_MIRROR = 2, + MOJOSHADER_TADDRESS_CLAMP = 3, + MOJOSHADER_TADDRESS_BORDER = 4, + MOJOSHADER_TADDRESS_MIRRORONCE = 5 +} MOJOSHADER_textureAddress; + +typedef enum MOJOSHADER_textureFilterType +{ + MOJOSHADER_TEXTUREFILTER_NONE, + MOJOSHADER_TEXTUREFILTER_POINT, + MOJOSHADER_TEXTUREFILTER_LINEAR, + MOJOSHADER_TEXTUREFILTER_ANISOTROPIC, + MOJOSHADER_TEXTUREFILTER_PYRAMIDALQUAD, + MOJOSHADER_TEXTUREFILTER_GAUSSIANQUAD, + MOJOSHADER_TEXTUREFILTER_CONVOLUTIONMONO +} MOJOSHADER_textureFilterType; + + +/* Effect value types... */ + +typedef struct MOJOSHADER_effectSamplerState MOJOSHADER_effectSamplerState; + +typedef struct MOJOSHADER_effectValue +{ + const char *name; + const char *semantic; + unsigned int element_count; + unsigned int row_count; + unsigned int column_count; + MOJOSHADER_symbolClass value_class; + MOJOSHADER_symbolType value_type; + unsigned int value_count; + union + { + /* Raw value types */ + void *values; + int *valuesI; + float *valuesF; + /* As used by MOJOSHADER_effectState */ + MOJOSHADER_zBufferType *valuesZBT; + MOJOSHADER_fillMode *valuesFiM; + MOJOSHADER_shadeMode *valuesSM; + MOJOSHADER_blendMode *valuesBM; + MOJOSHADER_cullMode *valuesCM; + MOJOSHADER_compareFunc *valuesCF; + MOJOSHADER_fogMode *valuesFoM; + MOJOSHADER_stencilOp *valuesSO; + MOJOSHADER_materialColorSource *valuesMCS; + MOJOSHADER_vertexBlendFlags *valuesVBF; + MOJOSHADER_patchedEdgeStyle *valuesPES; + MOJOSHADER_debugMonitorTokens *valuesDMT; + MOJOSHADER_blendOp *valuesBO; + MOJOSHADER_degreeType *valuesDT; + /* As used by MOJOSHADER_effectSamplerState */ + MOJOSHADER_textureAddress *valuesTA; + MOJOSHADER_textureFilterType *valuesTFT; + /* As used by MOJOSHADER_effectParameter */ + MOJOSHADER_effectSamplerState *valuesSS; + }; +} MOJOSHADER_effectValue; + +typedef struct MOJOSHADER_effectState +{ + MOJOSHADER_renderStateType type; + MOJOSHADER_effectValue value; +} MOJOSHADER_effectState; + +struct MOJOSHADER_effectSamplerState +{ + MOJOSHADER_samplerStateType type; + MOJOSHADER_effectValue value; +}; + +typedef MOJOSHADER_effectValue MOJOSHADER_effectAnnotation; + + +/* Effect interface structures... */ + +typedef struct MOJOSHADER_effectParam +{ + MOJOSHADER_effectValue value; + unsigned int annotation_count; + MOJOSHADER_effectAnnotation *annotations; +} MOJOSHADER_effectParam; + +typedef struct MOJOSHADER_effectPass +{ + const char *name; + unsigned int state_count; + MOJOSHADER_effectState *states; + unsigned int annotation_count; + MOJOSHADER_effectAnnotation* annotations; +} MOJOSHADER_effectPass; + +typedef struct MOJOSHADER_effectTechnique +{ + const char *name; + unsigned int pass_count; + MOJOSHADER_effectPass *passes; + unsigned int annotation_count; + MOJOSHADER_effectAnnotation* annotations; +} MOJOSHADER_effectTechnique; + + +/* Effect "objects"... */ + +/* Defined later in the state change types... */ +typedef struct MOJOSHADER_samplerStateRegister MOJOSHADER_samplerStateRegister; + +typedef struct MOJOSHADER_effectShader +{ + MOJOSHADER_symbolType type; + unsigned int technique; + unsigned int pass; + unsigned int is_preshader; + unsigned int preshader_param_count; + unsigned int *preshader_params; + unsigned int param_count; + unsigned int *params; + unsigned int sampler_count; + MOJOSHADER_samplerStateRegister *samplers; + union + { + const MOJOSHADER_parseData *shader; + const MOJOSHADER_preshader *preshader; + }; +} MOJOSHADER_effectShader; + +typedef struct MOJOSHADER_effectSamplerMap +{ + MOJOSHADER_symbolType type; + const char *name; +} MOJOSHADER_effectSamplerMap; + +typedef struct MOJOSHADER_effectString +{ + MOJOSHADER_symbolType type; + const char *string; +} MOJOSHADER_effectString; + +typedef struct MOJOSHADER_effectTexture +{ + MOJOSHADER_symbolType type; + /* FIXME: Does this even do anything? */ +} MOJOSHADER_effectTexture; + +typedef union MOJOSHADER_effectObject +{ + MOJOSHADER_symbolType type; + union + { + MOJOSHADER_effectShader shader; + MOJOSHADER_effectSamplerMap mapping; + MOJOSHADER_effectString string; + MOJOSHADER_effectTexture texture; + }; +} MOJOSHADER_effectObject; + + +/* Effect state change types... */ + +/* Used to store sampler states with accompanying sampler registers */ +struct MOJOSHADER_samplerStateRegister +{ + const char *sampler_name; + unsigned int sampler_register; + unsigned int sampler_state_count; + const MOJOSHADER_effectSamplerState *sampler_states; +}; + +/* + * Used to acquire the desired render state by the effect pass. + */ +typedef struct MOJOSHADER_effectStateChanges +{ + /* Render state changes caused by effect technique */ + unsigned int render_state_change_count; + const MOJOSHADER_effectState *render_state_changes; + + /* Sampler state changes caused by effect technique */ + unsigned int sampler_state_change_count; + const MOJOSHADER_samplerStateRegister *sampler_state_changes; + + /* Vertex sampler state changes caused by effect technique */ + unsigned int vertex_sampler_state_change_count; + const MOJOSHADER_samplerStateRegister *vertex_sampler_state_changes; +} MOJOSHADER_effectStateChanges; + + +/* + * Structure used to return data from parsing of an effect file... + */ +/* !!! FIXME: most of these ints should be unsigned. */ +typedef struct MOJOSHADER_effect +{ + /* + * The number of elements pointed to by (errors). + */ + int error_count; + + /* + * (error_count) elements of data that specify errors that were generated + * by parsing this shader. + * This can be NULL if there were no errors or if (error_count) is zero. + */ + MOJOSHADER_error *errors; + + /* + * The name of the profile used to parse the shader. Will be NULL on error. + */ + const char *profile; + + /* + * The number of params pointed to by (params). + */ + int param_count; + + /* + * (param_count) elements of data that specify parameter bind points for + * this effect. + * This can be NULL on error or if (param_count) is zero. + */ + MOJOSHADER_effectParam *params; + + /* + * The number of elements pointed to by (techniques). + */ + int technique_count; + + /* + * (technique_count) elements of data that specify techniques used in + * this effect. Each technique contains a series of passes, and each pass + * specifies state and shaders that affect rendering. + * This can be NULL on error or if (technique_count) is zero. + */ + MOJOSHADER_effectTechnique *techniques; + + /* + * The technique currently being rendered by this effect. + */ + const MOJOSHADER_effectTechnique *current_technique; + + /* + * The index of the current pass being rendered by this effect. + */ + int current_pass; + + /* + * The number of elements pointed to by (objects). + */ + int object_count; + + /* + * (object_count) elements of data that specify objects used in + * this effect. + * This can be NULL on error or if (object_count) is zero. + */ + MOJOSHADER_effectObject *objects; + + /* + * Value used to determine whether or not to restore the previous shader + * state after rendering an effect, as requested by application. + */ + int restore_shader_state; + + /* + * The structure provided by the appliation to store the state changes. + */ + MOJOSHADER_effectStateChanges *state_changes; + + /* + * This is the malloc implementation you passed to MOJOSHADER_parseEffect(). + */ + MOJOSHADER_malloc malloc; + + /* + * This is the free implementation you passed to MOJOSHADER_parseEffect(). + */ + MOJOSHADER_free free; + + /* + * This is the pointer you passed as opaque data for your allocator. + */ + void *malloc_data; +} MOJOSHADER_effect; + + +/* Effect parsing interface... */ + +/* !!! FIXME: document me. */ +DECLSPEC MOJOSHADER_effect *MOJOSHADER_parseEffect(const char *profile, + const unsigned char *buf, + const unsigned int _len, + const MOJOSHADER_swizzle *swiz, + const unsigned int swizcount, + const MOJOSHADER_samplerMap *smap, + const unsigned int smapcount, + MOJOSHADER_malloc m, + MOJOSHADER_free f, + void *d); + + +/* !!! FIXME: document me. */ +DECLSPEC void MOJOSHADER_freeEffect(const MOJOSHADER_effect *effect); + + +/* !!! FIXME: document me. */ +DECLSPEC MOJOSHADER_effect *MOJOSHADER_cloneEffect(const MOJOSHADER_effect *effect); + + +/* Effect parameter interface... */ + +/* Set the constant value for the specified effect parameter. + * + * This function maps to ID3DXEffect::SetRawValue. + * + * (parameter) is a parameter obtained from a MOJOSHADER_effect*. + * (data) is the constant values to be applied to the parameter. + * (offset) is the offset, in bytes, of the parameter data being modified. + * (len) is the size, in bytes, of the data buffer being applied. + * + * This function is thread safe. + */ +DECLSPEC void MOJOSHADER_effectSetRawValueHandle(const MOJOSHADER_effectParam *parameter, + const void *data, + const unsigned int offset, + const unsigned int len); + +/* Set the constant value for the effect parameter, specified by name. + * Note: this function is slower than MOJOSHADER_effectSetRawValueHandle(), + * but we still provide it to fully map to ID3DXEffect. + * + * This function maps to ID3DXEffect::SetRawValue. + * + * (effect) is a MOJOSHADER_effect* obtained from MOJOSHADER_parseEffect(). + * (name) is the human-readable name of the parameter being modified. + * (data) is the constant values to be applied to the parameter. + * (offset) is the offset, in bytes, of the parameter data being modified. + * (len) is the size, in bytes, of the data buffer being applied. + * + * This function is thread safe. + */ +DECLSPEC void MOJOSHADER_effectSetRawValueName(const MOJOSHADER_effect *effect, + const char *name, + const void *data, + const unsigned int offset, + const unsigned int len); + + +/* Effect technique interface... */ + +/* Get the current technique in use by an effect. + * + * This function maps to ID3DXEffect::GetCurrentTechnique. + * + * (effect) is a MOJOSHADER_effect* obtained from MOJOSHADER_parseEffect(). + * + * This function returns the technique currently used by the given effect. + * + * This function is thread safe. + */ +DECLSPEC const MOJOSHADER_effectTechnique *MOJOSHADER_effectGetCurrentTechnique(const MOJOSHADER_effect *effect); + +/* Set the current technique to be used an effect. + * + * This function maps to ID3DXEffect::SetTechnique. + * + * (effect) is a MOJOSHADER_effect* obtained from MOJOSHADER_parseEffect(). + * (technique) is the technique to be used by the effect when rendered. + * + * This function is thread safe. + */ +DECLSPEC void MOJOSHADER_effectSetTechnique(MOJOSHADER_effect *effect, + const MOJOSHADER_effectTechnique *technique); + +/* Get the next technique in an effect's list. + * + * This function maps to ID3DXEffect::FindNextValidTechnique. + * + * (effect) is a MOJOSHADER_effect* obtained from MOJOSHADER_parseEffect(). + * (technique) can either be a technique found in the given effect, or NULL to + * find the first technique in the given effect. + * + * This function returns either the next technique after the passed technique, + * or the first technique if the passed technique is NULL. + * + * This function is thread safe. + */ +DECLSPEC const MOJOSHADER_effectTechnique *MOJOSHADER_effectFindNextValidTechnique(const MOJOSHADER_effect *effect, + const MOJOSHADER_effectTechnique *technique); + + +/* OpenGL effect interface... */ + +typedef struct MOJOSHADER_glEffect MOJOSHADER_glEffect; + +/* Fully compile/link the shaders found within the effect. + * + * The MOJOSHADER_glEffect* is solely for use within the OpenGL-specific calls. + * In all other cases you will be using the MOJOSHADER_effect* instead. + * + * In a typical use case, you will be calling this immediately after obtaining + * the MOJOSHADER_effect*. + * + * (effect) is a MOJOSHADER_effect* obtained from MOJOSHADER_parseEffect(). + * + * This function returns a MOJOSHADER_glEffect*, containing OpenGL-specific + * data for an accompanying MOJOSHADER_effect*. + * + * This call is NOT thread safe! As most OpenGL implementations are not thread + * safe, you should probably only call this from the same thread that created + * the GL context. + */ +DECLSPEC MOJOSHADER_glEffect *MOJOSHADER_glCompileEffect(MOJOSHADER_effect *effect); + +/* Delete the shaders that were allocated for an effect. + * + * (glEffect) is a MOJOSHADER_glEffect* obtained from + * MOJOSHADER_glCompileEffect(). + * + * This call is NOT thread safe! As most OpenGL implementations are not thread + * safe, you should probably only call this from the same thread that created + * the GL context. + */ +DECLSPEC void MOJOSHADER_glDeleteEffect(MOJOSHADER_glEffect *glEffect); + +/* Prepare the effect for rendering with the currently applied technique. + * + * This function maps to ID3DXEffect::Begin. + * + * In addition to the expected Begin parameters, we also include a parameter + * to pass in a MOJOSHADER_effectRenderState. Rather than change the render + * state within MojoShader itself we will simply provide what the effect wants + * and allow you to use this information with your own renderer. + * MOJOSHADER_glEffectBeginPass will update with the render state desired by + * the current effect pass. + * + * Note that we only provide the ability to preserve the shader state, but NOT + * the ability to preserve the render/sampler states. You are expected to + * track your own GL state and restore these states as needed for your + * application. + * + * (glEffect) is a MOJOSHADER_glEffect* obtained from + * MOJOSHADER_glCompileEffect(). + * (numPasses) will be filled with the number of passes that this technique + * will need to fully render. + * (saveShaderState) is a boolean value informing the effect whether or not to + * restore the shader bindings after calling MOJOSHADER_glEffectEnd. + * (renderState) will be filled by the effect to inform you of the render state + * changes introduced by the technique and its passes. + * + * This call is NOT thread safe! As most OpenGL implementations are not thread + * safe, you should probably only call this from the same thread that created + * the GL context. + */ +DECLSPEC void MOJOSHADER_glEffectBegin(MOJOSHADER_glEffect *glEffect, + unsigned int *numPasses, + int saveShaderState, + MOJOSHADER_effectStateChanges *stateChanges); + +/* Begin an effect pass from the currently applied technique. + * + * This function maps to ID3DXEffect::BeginPass. + * + * (glEffect) is a MOJOSHADER_glEffect* obtained from + * MOJOSHADER_glCompileEffect(). + * (pass) is the index of the effect pass as found in the current technique. + * + * This call is NOT thread safe! As most OpenGL implementations are not thread + * safe, you should probably only call this from the same thread that created + * the GL context. + */ +DECLSPEC void MOJOSHADER_glEffectBeginPass(MOJOSHADER_glEffect *glEffect, + unsigned int pass); + +/* Push render state changes that occurred within an actively rendering pass. + * + * This function maps to ID3DXEffect::CommitChanges. + * + * (glEffect) is a MOJOSHADER_glEffect* obtained from + * MOJOSHADER_glCompileEffect(). + * + * This call is NOT thread safe! As most OpenGL implementations are not thread + * safe, you should probably only call this from the same thread that created + * the GL context. + */ +DECLSPEC void MOJOSHADER_glEffectCommitChanges(MOJOSHADER_glEffect *glEffect); + +/* End an effect pass from the currently applied technique. + * + * This function maps to ID3DXEffect::EndPass. + * + * (glEffect) is a MOJOSHADER_glEffect* obtained from + * MOJOSHADER_glCompileEffect(). + * + * This call is NOT thread safe! As most OpenGL implementations are not thread + * safe, you should probably only call this from the same thread that created + * the GL context. + */ +DECLSPEC void MOJOSHADER_glEffectEndPass(MOJOSHADER_glEffect *glEffect); + +/* Complete rendering the effect technique, and restore the render state. + * + * This function maps to ID3DXEffect::End. + * + * (glEffect) is a MOJOSHADER_glEffect* obtained from + * MOJOSHADER_glCompileEffect(). + * + * This call is NOT thread safe! As most OpenGL implementations are not thread + * safe, you should probably only call this from the same thread that created + * the GL context. + */ +DECLSPEC void MOJOSHADER_glEffectEnd(MOJOSHADER_glEffect *glEffect); + +#endif /* MOJOSHADER_EFFECT_SUPPORT */ + +#endif /* MOJOSHADER_EFFECTS_H */ diff --git a/mojoshader_internal.h b/mojoshader_internal.h index 0b06334d..ae2ef092 100644 --- a/mojoshader_internal.h +++ b/mojoshader_internal.h @@ -52,6 +52,10 @@ #define SUPPORT_PROFILE_GLSL120 1 #endif +#ifndef SUPPORT_PROFILE_GLSLES +#define SUPPORT_PROFILE_GLSLES 1 +#endif + #ifndef SUPPORT_PROFILE_ARB1 #define SUPPORT_PROFILE_ARB1 1 #endif @@ -68,6 +72,10 @@ #error glsl120 profile requires glsl profile. Fix your build. #endif +#if SUPPORT_PROFILE_GLSLES && !SUPPORT_PROFILE_GLSL +#error glsles profile requires glsl profile. Fix your build. +#endif + // Microsoft's preprocessor has some quirks. In some ways, it doesn't work // like you'd expect a C preprocessor to function. #ifndef MATCH_MICROSOFT_PREPROCESSOR @@ -76,16 +84,8 @@ // Other stuff you can disable... -// This removes the preshader parsing and execution code. You can save some -// bytes if you have normal shaders and not Effect files. -#ifndef SUPPORT_PRESHADERS -#define SUPPORT_PRESHADERS 1 -#endif - -#if SUPPORT_PRESHADERS -void MOJOSHADER_runPreshader(const MOJOSHADER_preshader*, const float*, float*); -#else -#define MOJOSHADER_runPreshader(a, b) +#ifdef MOJOSHADER_EFFECT_SUPPORT +void MOJOSHADER_runPreshader(const MOJOSHADER_preshader*, float*); #endif @@ -99,6 +99,9 @@ void MOJOSHADER_runPreshader(const MOJOSHADER_preshader*, const float*, float*); typedef unsigned int uint; // this is a printf() helper. don't use for code. +// Locale-independent float printing replacement for snprintf +size_t MOJOSHADER_printFloat(char *text, size_t maxlen, float arg); + #ifdef _MSC_VER #include #define va_copy(a, b) a = b @@ -293,8 +296,8 @@ ssize_t buffer_find(Buffer *buffer, const size_t start, #define MOJOSHADER_internal_malloc NULL #define MOJOSHADER_internal_free NULL #else -void *MOJOSHADER_internal_malloc(int bytes, void *d); -void MOJOSHADER_internal_free(void *ptr, void *d); +void * MOJOSHADERCALL MOJOSHADER_internal_malloc(int bytes, void *d); +void MOJOSHADERCALL MOJOSHADER_internal_free(void *ptr, void *d); #endif #if MOJOSHADER_FORCE_INCLUDE_CALLBACKS diff --git a/mojoshader_opengl.c b/mojoshader_opengl.c index 6e48751a..44e77a34 100644 --- a/mojoshader_opengl.c +++ b/mojoshader_opengl.c @@ -7,9 +7,6 @@ * This file written by Ryan C. Gordon. */ -// !!! FIXME: preshaders shouldn't be handled in here at all. This should -// !!! FIXME: be in the Effects API, once that's actually written. - #include #include #include @@ -98,15 +95,13 @@ struct MOJOSHADER_glProgram size_t ps_uniforms_bool_count; GLint *ps_uniforms_bool; - size_t vs_preshader_reg_count; - GLfloat *vs_preshader_regs; - size_t ps_preshader_reg_count; - GLfloat *ps_preshader_regs; - uint32 refcount; int uses_pointsize; + // 10 is apparently the resource limit according to SM3 -flibit + GLint vertex_attrib_loc[MOJOSHADER_USAGE_TOTAL][10]; + // GLSL uses these...location of uniform arrays. GLint vs_float4_loc; GLint vs_int4_loc; @@ -114,6 +109,10 @@ struct MOJOSHADER_glProgram GLint ps_float4_loc; GLint ps_int4_loc; GLint ps_bool_loc; +#ifdef MOJOSHADER_FLIP_RENDERTARGET + GLint vs_flip_loc; + int current_flip; +#endif }; #ifndef WINGDIAPI @@ -163,6 +162,9 @@ struct MOJOSHADER_glContext uint8 want_attr[32]; uint8 have_attr[32]; + // This shadows vertex attribute and divisor states. + GLuint attr_divisor[32]; + // rarely used, so we don't touch when we don't have to. int pointsize_enabled; @@ -174,10 +176,16 @@ struct MOJOSHADER_glContext MOJOSHADER_glProgram *bound_program; char profile[16]; +#ifdef MOJOSHADER_XNA4_VERTEX_TEXTURES + // Vertex texture sampler offset... + int vertex_sampler_offset; +#endif + // Extensions... int have_core_opengl; int have_opengl_2; // different entry points than ARB extensions. int have_opengl_3; // different extension query. + int have_opengl_es; // different extension requirements int have_GL_ARB_vertex_program; int have_GL_ARB_fragment_program; int have_GL_NV_vertex_program2_option; @@ -191,6 +199,7 @@ struct MOJOSHADER_glContext int have_GL_NV_half_float; int have_GL_ARB_half_float_vertex; int have_GL_OES_vertex_half_float; + int have_GL_ARB_instanced_arrays; // Entry points... PFNGLGETSTRINGPROC glGetString; @@ -217,6 +226,9 @@ struct MOJOSHADER_glContext PFNGLSHADERSOURCEPROC glShaderSource; PFNGLUNIFORM1IPROC glUniform1i; PFNGLUNIFORM1IVPROC glUniform1iv; +#ifdef MOJOSHADER_FLIP_RENDERTARGET + PFNGLUNIFORM1FPROC glUniform1f; +#endif PFNGLUNIFORM4FVPROC glUniform4fv; PFNGLUNIFORM4IVPROC glUniform4iv; PFNGLUSEPROGRAMPROC glUseProgram; @@ -247,6 +259,7 @@ struct MOJOSHADER_glContext PFNGLGENPROGRAMSARBPROC glGenProgramsARB; PFNGLBINDPROGRAMARBPROC glBindProgramARB; PFNGLPROGRAMSTRINGARBPROC glProgramStringARB; + PFNGLVERTEXATTRIBDIVISORARBPROC glVertexAttribDivisorARB; // interface for profile-specific things. int (*profileMaxUniforms)(MOJOSHADER_shaderType shader_type); @@ -562,6 +575,9 @@ static void impl_GLSL_FinalInitProgram(MOJOSHADER_glProgram *program) program->ps_float4_loc = glsl_uniform_loc(program, "ps_uniforms_vec4"); program->ps_int4_loc = glsl_uniform_loc(program, "ps_uniforms_ivec4"); program->ps_bool_loc = glsl_uniform_loc(program, "ps_uniforms_bool"); +#ifdef MOJOSHADER_FLIP_RENDERTARGET + program->vs_flip_loc = glsl_uniform_loc(program, "vpFlip"); +#endif } // impl_GLSL_FinalInitProgram @@ -951,6 +967,9 @@ static void lookup_entry_points(MOJOSHADER_glGetProcAddress lookup, void *d) DO_LOOKUP(opengl_2, PFNGLSHADERSOURCEPROC, glShaderSource); DO_LOOKUP(opengl_2, PFNGLUNIFORM1IPROC, glUniform1i); DO_LOOKUP(opengl_2, PFNGLUNIFORM1IVPROC, glUniform1iv); +#ifdef MOJOSHADER_FLIP_RENDERTARGET + DO_LOOKUP(opengl_2, PFNGLUNIFORM1FPROC, glUniform1f); +#endif DO_LOOKUP(opengl_2, PFNGLUNIFORM4FVPROC, glUniform4fv); DO_LOOKUP(opengl_2, PFNGLUNIFORM4IVPROC, glUniform4iv); DO_LOOKUP(opengl_2, PFNGLUSEPROGRAMPROC, glUseProgram); @@ -982,6 +1001,7 @@ static void lookup_entry_points(MOJOSHADER_glGetProcAddress lookup, void *d) DO_LOOKUP(GL_ARB_vertex_program, PFNGLBINDPROGRAMARBPROC, glBindProgramARB); DO_LOOKUP(GL_ARB_vertex_program, PFNGLPROGRAMSTRINGARBPROC, glProgramStringARB); DO_LOOKUP(GL_NV_gpu_program4, PFNGLPROGRAMLOCALPARAMETERI4IVNVPROC, glProgramLocalParameterI4ivNV); + DO_LOOKUP(GL_ARB_instanced_arrays, PFNGLVERTEXATTRIBDIVISORARBPROC, glVertexAttribDivisorARB); #undef DO_LOOKUP } // lookup_entry_points @@ -1054,6 +1074,8 @@ static void detect_glsl_version(void) const char *str = (const char *) ctx->glGetString(enumval); if (ctx->glGetError() == GL_INVALID_ENUM) str = NULL; + if (strstr(str, "OpenGL ES GLSL ES ")) + str += 18; parse_opengl_version_str(str, &ctx->glsl_major, &ctx->glsl_minor); } // if #endif @@ -1095,6 +1117,7 @@ static void load_extensions(MOJOSHADER_glGetProcAddress lookup, void *d) ctx->have_GL_NV_half_float = 1; ctx->have_GL_ARB_half_float_vertex = 1; ctx->have_GL_OES_vertex_half_float = 1; + ctx->have_GL_ARB_instanced_arrays = 1; lookup_entry_points(lookup, d); @@ -1103,6 +1126,11 @@ static void load_extensions(MOJOSHADER_glGetProcAddress lookup, void *d) else { const char *str = (const char *) ctx->glGetString(GL_VERSION); + if (strstr(str, "OpenGL ES ")) + { + ctx->have_opengl_es = 1; + str += 10; + } parse_opengl_version_str(str, &ctx->opengl_major, &ctx->opengl_minor); if ((ctx->have_opengl_3) && (opengl_version_atleast(3, 0))) @@ -1187,6 +1215,7 @@ static void load_extensions(MOJOSHADER_glGetProcAddress lookup, void *d) VERIFY_EXT(GL_NV_half_float, -1, -1); VERIFY_EXT(GL_ARB_half_float_vertex, 3, 0); VERIFY_EXT(GL_OES_vertex_half_float, -1, -1); + VERIFY_EXT(GL_ARB_instanced_arrays, 3, 3); #undef VERIFY_EXT @@ -1247,6 +1276,13 @@ static int valid_profile(const char *profile) } // else if #endif + #if SUPPORT_PROFILE_GLSLES + else if (strcmp(profile, MOJOSHADER_PROFILE_GLSLES) == 0) + { + MUST_HAVE_GLSL(MOJOSHADER_PROFILE_GLSLES, 1, 10); + } // else if + #endif + #if SUPPORT_PROFILE_GLSL120 else if (strcmp(profile, MOJOSHADER_PROFILE_GLSL120) == 0) { @@ -1311,6 +1347,14 @@ int MOJOSHADER_glAvailableProfiles(MOJOSHADER_glGetProcAddress lookup, load_extensions(lookup, lookup_d); +#if SUPPORT_PROFILE_GLSLES + if (ctx->have_opengl_es) + { + profs[0] = MOJOSHADER_PROFILE_GLSLES; + return 1; + } // if +#endif + if (ctx->have_core_opengl) { size_t i; @@ -1381,15 +1425,24 @@ MOJOSHADER_glContext *MOJOSHADER_glCreateContext(const char *profile, if (!valid_profile(profile)) goto init_fail; +#ifdef MOJOSHADER_XNA4_VERTEX_TEXTURES + GLint maxTextures; + GLint maxVertexTextures; + ctx->glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &maxTextures); + maxVertexTextures = ((maxTextures - 16) < 4) ? (maxTextures - 16) : 4; + ctx->vertex_sampler_offset = maxTextures - maxVertexTextures; +#endif + MOJOSHADER_glBindProgram(NULL); // !!! FIXME: generalize this part. if (profile == NULL) {} - // We don't check SUPPORT_PROFILE_GLSL120 here, since valid_profile() does. + // We don't check SUPPORT_PROFILE_GLSL120/ES here, since valid_profile() does. #if SUPPORT_PROFILE_GLSL else if ( (strcmp(profile, MOJOSHADER_PROFILE_GLSL) == 0) || - (strcmp(profile, MOJOSHADER_PROFILE_GLSL120) == 0) ) + (strcmp(profile, MOJOSHADER_PROFILE_GLSL120) == 0) || + (strcmp(profile, MOJOSHADER_PROFILE_GLSLES) == 0) ) { ctx->profileMaxUniforms = impl_GLSL_MaxUniforms; ctx->profileCompileShader = impl_GLSL_CompileShader; @@ -1555,8 +1608,6 @@ static void program_unref(MOJOSHADER_glProgram *program) ctx->profileDeleteProgram(program->handle); shader_unref(program->vertex); shader_unref(program->fragment); - Free(program->vs_preshader_regs); - Free(program->ps_preshader_regs); Free(program->vs_uniforms_float4); Free(program->vs_uniforms_int4); Free(program->vs_uniforms_bool); @@ -1685,38 +1736,6 @@ static int lookup_uniforms(MOJOSHADER_glProgram *program, #undef MAKE_ARRAY - if (pd->preshader) - { - unsigned int largest = 0; - const MOJOSHADER_symbol *sym = pd->preshader->symbols; - for (i = 0; i < pd->preshader->symbol_count; i++, sym++) - { - const unsigned int val = sym->register_index + sym->register_count; - if (val > largest) - largest = val; - } // for - - if (largest > 0) - { - const size_t len = largest * sizeof (GLfloat) * 4; - GLfloat *buf = (GLfloat *) Malloc(len); - if (buf == NULL) - return 0; - memset(buf, '\0', len); - - if (shader_type == MOJOSHADER_TYPE_VERTEX) - { - program->vs_preshader_reg_count = largest; - program->vs_preshader_regs = buf; - } // if - else if (shader_type == MOJOSHADER_TYPE_PIXEL) - { - program->ps_preshader_reg_count = largest; - program->ps_preshader_regs = buf; - } // else if - } // if - } // if - return 1; } // lookup_uniforms @@ -1744,7 +1763,14 @@ static void lookup_samplers(MOJOSHADER_glProgram *program, { const GLint loc = ctx->profileGetSamplerLocation(program, shader, i); if (loc >= 0) // maybe the Sampler was optimized out? - ctx->profilePushSampler(loc, s[i].index); + { +#ifdef MOJOSHADER_XNA4_VERTEX_TEXTURES + if (pd->shader_type == MOJOSHADER_TYPE_VERTEX) + ctx->profilePushSampler(loc, s[i].index + ctx->vertex_sampler_offset); + else +#endif + ctx->profilePushSampler(loc, s[i].index); + } // if } // for } // lookup_samplers @@ -1784,6 +1810,7 @@ static int lookup_attributes(MOJOSHADER_glProgram *program) AttributeMap *map = &program->attributes[program->attribute_count]; map->attribute = &a[i]; map->location = loc; + program->vertex_attrib_loc[map->attribute->usage][map->attribute->index] = loc; program->attribute_count++; if (((size_t)loc) > STATICARRAYLEN(ctx->want_attr)) @@ -1844,6 +1871,7 @@ MOJOSHADER_glProgram *MOJOSHADER_glLinkProgram(MOJOSHADER_glShader *vshader, if (retval == NULL) goto link_program_fail; memset(retval, '\0', sizeof (MOJOSHADER_glProgram)); + memset(retval->vertex_attrib_loc, 0xFF, sizeof(retval->vertex_attrib_loc)); numregs = 0; if (vshader != NULL) numregs += vshader->parseData->uniform_count; @@ -2278,6 +2306,15 @@ static inline GLenum opengl_attr_type(const MOJOSHADER_attributeType type) } // opengl_attr_type +int MOJOSHADER_glGetVertexAttribLocation(MOJOSHADER_usage usage, int index) +{ + if ((ctx->bound_program == NULL) || (ctx->bound_program->vertex == NULL)) + return -1; + + return ctx->bound_program->vertex_attrib_loc[usage][index]; +} // MOJOSHADER_glGetVertexAttribLocation + + // !!! FIXME: shouldn't (index) be unsigned? void MOJOSHADER_glSetVertexAttribute(MOJOSHADER_usage usage, int index, unsigned int size, @@ -2290,27 +2327,10 @@ void MOJOSHADER_glSetVertexAttribute(MOJOSHADER_usage usage, const GLenum gl_type = opengl_attr_type(type); const GLboolean norm = (normalized) ? GL_TRUE : GL_FALSE; - const int count = ctx->bound_program->attribute_count; - GLint gl_index = 0; - int i; - - for (i = 0; i < count; i++) - { - const AttributeMap *map = &ctx->bound_program->attributes[i]; - const MOJOSHADER_attribute *a = map->attribute; + const GLint gl_index = ctx->bound_program->vertex_attrib_loc[usage][index]; - // !!! FIXME: is this array guaranteed to be sorted by usage? - // !!! FIXME: if so, we can break if a->usage > usage. - - if ((a->usage == usage) && (a->index == index)) - { - gl_index = map->location; - break; - } // if - } // for - - if (i == count) - return; // nothing to do, this shader doesn't use this stream. + if (gl_index == -1) + return; // Nothing to do, this shader doesn't use this stream. // this happens to work in both ARB1 and GLSL, but if something alien // shows up, we'll have to split these into profile*() functions. @@ -2323,76 +2343,26 @@ void MOJOSHADER_glSetVertexAttribute(MOJOSHADER_usage usage, } // MOJOSHADER_glSetVertexAttribute -void MOJOSHADER_glSetVertexPreshaderUniformF(unsigned int idx, - const float *data, - unsigned int vec4n) -{ - MOJOSHADER_glProgram *program = ctx->bound_program; - if (program == NULL) - return; // nothing to do. - - const uint maxregs = program->vs_preshader_reg_count; - if (idx < maxregs) - { - assert(sizeof (GLfloat) == sizeof (float)); - const uint cpy = (minuint(maxregs - idx, vec4n) * sizeof (*data)) * 4; - memcpy(program->vs_preshader_regs + (idx * 4), data, cpy); - program->generation = ctx->generation-1; - } // if -} // MOJOSHADER_glSetVertexPreshaderUniformF - - -void MOJOSHADER_glGetVertexPreshaderUniformF(unsigned int idx, float *data, - unsigned int vec4n) -{ - MOJOSHADER_glProgram *program = ctx->bound_program; - if (program == NULL) - return; // nothing to do. - - const uint maxregs = program->vs_preshader_reg_count; - if (idx < maxregs) - { - assert(sizeof (GLfloat) == sizeof (float)); - const uint cpy = (minuint(maxregs - idx, vec4n) * sizeof (*data)) * 4; - memcpy(data, program->vs_preshader_regs + (idx * 4), cpy); - } // if -} // MOJOSHADER_glGetVertexPreshaderUniformF - - -void MOJOSHADER_glSetPixelPreshaderUniformF(unsigned int idx, - const float *data, - unsigned int vec4n) +// !!! FIXME: shouldn't (index) be unsigned? +void MOJOSHADER_glSetVertexAttribDivisor(MOJOSHADER_usage usage, + int index, unsigned int divisor) { - MOJOSHADER_glProgram *program = ctx->bound_program; - if (program == NULL) - return; // nothing to do. + assert(ctx->have_GL_ARB_instanced_arrays); - const uint maxregs = program->ps_preshader_reg_count; - if (idx < maxregs) - { - assert(sizeof (GLfloat) == sizeof (float)); - const uint cpy = (minuint(maxregs - idx, vec4n) * sizeof (*data)) * 4; - memcpy(program->ps_preshader_regs + (idx * 4), data, cpy); - program->generation = ctx->generation-1; - } // if -} // MOJOSHADER_glSetPixelPreshaderUniformF + if ((ctx->bound_program == NULL) || (ctx->bound_program->vertex == NULL)) + return; + const GLint gl_index = ctx->bound_program->vertex_attrib_loc[usage][index]; -void MOJOSHADER_glGetPixelPreshaderUniformF(unsigned int idx, float *data, - unsigned int vec4n) -{ - MOJOSHADER_glProgram *program = ctx->bound_program; - if (program == NULL) - return; // nothing to do. + if (gl_index == -1) + return; // Nothing to do, this shader doesn't use this stream. - const uint maxregs = program->ps_preshader_reg_count; - if (idx < maxregs) + if (divisor != ctx->attr_divisor[gl_index]) { - assert(sizeof (GLfloat) == sizeof (float)); - const uint cpy = (minuint(maxregs - idx, vec4n) * sizeof (*data)) * 4; - memcpy(data, program->ps_preshader_regs + (idx * 4), cpy); + ctx->glVertexAttribDivisorARB(gl_index, divisor); + ctx->attr_divisor[gl_index] = divisor; } // if -} // MOJOSHADER_glGetPixelPreshaderUniformF +} // MOJOSHADER_glSetVertexAttribDivisor void MOJOSHADER_glSetLegacyBumpMapEnv(unsigned int sampler, float mat00, @@ -2445,38 +2415,9 @@ void MOJOSHADER_glProgramReady(void) GLfloat *dstf = program->vs_uniforms_float4; GLint *dsti = program->vs_uniforms_int4; GLint *dstb = program->vs_uniforms_bool; - const MOJOSHADER_preshader *preshader = NULL; + uint8 uniforms_changed = 0; uint32 i; - // !!! FIXME: shouldn't this run even if the generation hasn't changed? - #if SUPPORT_PRESHADERS - int ran_preshader = 0; - if (program->vertex) - { - preshader = program->vertex->parseData->preshader; - if (preshader) - { - MOJOSHADER_runPreshader(preshader, program->vs_preshader_regs, - ctx->vs_reg_file_f); - ran_preshader = 1; - } // if - } // if - - if (program->fragment) - { - preshader = program->fragment->parseData->preshader; - if (preshader) - { - MOJOSHADER_runPreshader(preshader, program->ps_preshader_regs, - ctx->ps_reg_file_f); - ran_preshader = 1; - } // if - } // if - - if (ran_preshader) - ctx->generation++; - #endif - for (i = 0; i < count; i++) { UniformMap *map = &program->uniforms[i]; @@ -2516,14 +2457,22 @@ void MOJOSHADER_glProgramReady(void) { const size_t count = 4 * size; const GLfloat *f = &srcf[index * 4]; - memcpy(dstf, f, sizeof (GLfloat) * count); + if (memcmp(dstf, f, sizeof (GLfloat) * count) != 0) + { + memcpy(dstf, f, sizeof (GLfloat) * count); + uniforms_changed = 1; + } dstf += count; } // if else if (type == MOJOSHADER_UNIFORM_INT) { const size_t count = 4 * size; const GLint *i = &srci[index * 4]; - memcpy(dsti, i, sizeof (GLint) * count); + if (memcmp(dsti, i, sizeof (GLint) * count) != 0) + { + memcpy(dsti, i, sizeof (GLint) * count); + uniforms_changed = 1; + } // if dsti += count; } // else if else if (type == MOJOSHADER_UNIFORM_BOOL) @@ -2532,7 +2481,11 @@ void MOJOSHADER_glProgramReady(void) const uint8 *b = &srcb[index]; size_t i; for (i = 0; i < count; i++) - dstb[i] = (GLint) b[i]; + if (dstb[i] != b[i]) + { + dstb[i] = (GLint) b[i]; + uniforms_changed = 1; + } // if dstb += count; } // else if @@ -2572,7 +2525,8 @@ void MOJOSHADER_glProgramReady(void) program->generation = ctx->generation; - ctx->profilePushUniforms(); + if (uniforms_changed) + ctx->profilePushUniforms(); } // if } // MOJOSHADER_glProgramReady @@ -2620,5 +2574,510 @@ void MOJOSHADER_glDestroyContext(MOJOSHADER_glContext *_ctx) ctx = ((current_ctx == _ctx) ? NULL : current_ctx); } // MOJOSHADER_glDestroyContext + +#ifdef MOJOSHADER_FLIP_RENDERTARGET + + +void MOJOSHADER_glProgramViewportFlip(int flip) +{ + assert(ctx->bound_program->vs_flip_loc != -1); + + /* Some compilers require that vpFlip be a float value, rather than int. + * However, there's no real reason for it to be a float in the API, so we + * do a cast in here. That's not so bad, right...? + * -flibit + */ + if (flip != ctx->bound_program->current_flip) + { + ctx->glUniform1f(ctx->bound_program->vs_flip_loc, (float) flip); + ctx->bound_program->current_flip = flip; + } // if +} + + +#endif + + +#ifdef MOJOSHADER_EFFECT_SUPPORT + + +struct MOJOSHADER_glEffect +{ + MOJOSHADER_effect *effect; + unsigned int num_shaders; + MOJOSHADER_glShader *shaders; + unsigned int *shader_indices; + unsigned int num_preshaders; + unsigned int *preshader_indices; + MOJOSHADER_glShader *current_vert; + MOJOSHADER_glShader *current_frag; + MOJOSHADER_effectShader *current_vert_raw; + MOJOSHADER_effectShader *current_frag_raw; + MOJOSHADER_glProgram *prev_program; +}; + + +MOJOSHADER_glEffect *MOJOSHADER_glCompileEffect(MOJOSHADER_effect *effect) +{ + 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; + GLuint shader = 0; + + MOJOSHADER_glEffect *retval = (MOJOSHADER_glEffect *) m(sizeof (MOJOSHADER_glEffect), d); + if (retval == NULL) + { + out_of_memory(); + return NULL; + } // if + memset(retval, '\0', sizeof (MOJOSHADER_glEffect)); + + // 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++; + } // if + } // for + + // Alloc shader information + retval->shaders = (MOJOSHADER_glShader *) m(retval->num_shaders * sizeof (MOJOSHADER_glShader), d); + if (retval->shaders == NULL) + { + f(retval, d); + out_of_memory(); + return NULL; + } // if + memset(retval->shaders, '\0', retval->num_shaders * sizeof (MOJOSHADER_glShader)); + 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 + + // Run through the shaders again, compiling and 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 + if (!ctx->profileCompileShader(object->shader.shader, &shader)) + goto compile_shader_fail; + retval->shaders[current_shader].parseData = object->shader.shader; + retval->shaders[current_shader].handle = shader; + retval->shaders[current_shader].refcount = 1; + retval->shader_indices[current_shader] = i; + current_shader++; + } // if + } // for + + retval->effect = effect; + return retval; + +compile_shader_fail: + for (i = 0; i < retval->num_shaders; i++) + if (retval->shaders[i].handle != 0) + ctx->profileDeleteShader(retval->shaders[i].handle); + f(retval->shader_indices, d); + f(retval->shaders, d); + f(retval, d); + return NULL; +} // MOJOSHADER_glCompileEffect + + +void MOJOSHADER_glDeleteEffect(MOJOSHADER_glEffect *glEffect) +{ + int i; + MOJOSHADER_free f = glEffect->effect->free; + void *d = glEffect->effect->malloc_data; + + for (i = 0; i < glEffect->num_shaders; i++) + { + /* Arbitarily add a reference to the refcount. + * We're going to be calling glDeleteShader so we can clean out the + * program cache, but we can NOT let it free() the array elements! + * We'll do that ourselves, as we malloc()'d in CompileEffect. + * -flibit + */ + glEffect->shaders[i].refcount++; + MOJOSHADER_glDeleteShader(&glEffect->shaders[i]); + + /* Delete the shader, but do NOT delete the parse data! + * The parse data belongs to the parent effect. + * -flibit + */ + ctx->profileDeleteShader(glEffect->shaders[i].handle); + } // for + + f(glEffect->shader_indices, d); + f(glEffect->preshader_indices, d); + f(glEffect, d); +} // MOJOSHADER_glDeleteEffect + + +void MOJOSHADER_glEffectBegin(MOJOSHADER_glEffect *glEffect, + unsigned int *numPasses, + int saveShaderState, + MOJOSHADER_effectStateChanges *stateChanges) +{ + *numPasses = glEffect->effect->current_technique->pass_count; + glEffect->effect->restore_shader_state = saveShaderState; + glEffect->effect->state_changes = stateChanges; + + if (glEffect->effect->restore_shader_state) + glEffect->prev_program = ctx->bound_program; +} // MOJOSHADER_glEffectBegin + + +void MOJOSHADER_glEffectBeginPass(MOJOSHADER_glEffect *glEffect, + unsigned int pass) +{ + int i, j; + MOJOSHADER_effectPass *curPass; + MOJOSHADER_effectState *state; + MOJOSHADER_effectShader *rawVert = glEffect->current_vert_raw; + MOJOSHADER_effectShader *rawFrag = glEffect->current_frag_raw; + int has_preshader = 0; + + if (ctx->bound_program != NULL) + { + glEffect->current_vert = ctx->bound_program->vertex; + glEffect->current_frag = ctx->bound_program->fragment; + } // if + + assert(glEffect->effect->current_pass == -1); + glEffect->effect->current_pass = pass; + curPass = &glEffect->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, gls) \ + (state->type == stype) \ + { \ + j = 0; \ + do \ + { \ + if (*state->value.valuesI == glEffect->shader_indices[j]) \ + { \ + raw = &glEffect->effect->objects[*state->value.valuesI].shader; \ + glEffect->gls = &glEffect->shaders[j]; \ + break; \ + } \ + else if (glEffect->num_preshaders > 0 \ + && *state->value.valuesI == glEffect->preshader_indices[j]) \ + { \ + raw = &glEffect->effect->objects[*state->value.valuesI].shader; \ + has_preshader = 1; \ + break; \ + } \ + } while (++j < glEffect->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 + + glEffect->effect->state_changes->render_state_changes = curPass->states; + glEffect->effect->state_changes->render_state_change_count = curPass->state_count; + + glEffect->current_vert_raw = rawVert; + glEffect->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) + { + MOJOSHADER_glBindShaders(glEffect->current_vert, + glEffect->current_frag); + if (glEffect->current_vert_raw != NULL) + { + glEffect->effect->state_changes->vertex_sampler_state_changes = rawVert->samplers; + glEffect->effect->state_changes->vertex_sampler_state_change_count = rawVert->sampler_count; + } // if + if (glEffect->current_frag_raw != NULL) + { + glEffect->effect->state_changes->sampler_state_changes = rawFrag->samplers; + glEffect->effect->state_changes->sampler_state_change_count = rawFrag->sampler_count; + } // if + } // if + + MOJOSHADER_glEffectCommitChanges(glEffect); +} // MOJOSHADER_glEffectBeginPass + + +static inline void copy_parameter_data(MOJOSHADER_effectParam *params, + unsigned int *param_loc, + MOJOSHADER_symbol *symbols, + unsigned int symbol_count, + GLfloat *regf, GLint *regi, uint8 *regb) +{ + int i, j, r, c; + + i = 0; + for (i = 0; i < symbol_count; i++) + { + const MOJOSHADER_symbol *sym = &symbols[i]; + const MOJOSHADER_effectValue *param = ¶ms[param_loc[i]].value; + + // float/int registers are vec4, so they have 4 elements each + const uint32 start = sym->register_index << 2; + + if (param->value_type == MOJOSHADER_SYMTYPE_FLOAT) + { + // Matrices have to be transposed from row-major to column-major! + if (param->value_class == MOJOSHADER_SYMCLASS_MATRIX_ROWS) + { + if (param->element_count > 1) + { + const uint32 regcount = sym->register_count / param->element_count; + j = 0; + do + { + r = 0; + do + { + c = 0; + do + { + const uint32 dest = start + c + + (r << 2) + + ((j << 2) * regcount); + const uint32 src = r + + (c * param->row_count) + + (j * param->row_count * param->column_count); + regf[dest] = param->valuesF[src]; + } while (++c < param->column_count); + } while (++r < regcount); + } while (++j < param->element_count); + } // if + else + { + r = 0; + do + { + c = 0; + do + { + regf[start + (r << 2 ) + c] = param->valuesF[r + (c * param->row_count)]; + } while (++c < param->column_count); + } while (++r < sym->register_count); + } // else + } // if + else if (sym->register_count > 1) + { + j = 0; + do + { + memcpy(regf + start + (j << 2), + param->valuesF + (j * param->column_count), + param->column_count << 2); + } while (++j < sym->register_count); + } // else if + else + memcpy(regf + start, param->valuesF, param->column_count << 2); + } // if + else if (sym->register_set == MOJOSHADER_SYMREGSET_FLOAT4) + { + // Sometimes int/bool parameters get thrown into float registers... + j = 0; + do + { + c = 0; + do + { + regf[start + (j << 2) + c] = (float) param->valuesI[(j * param->column_count) + c]; + } while (++c < param->column_count); + } while (++j < sym->register_count); + } // else if + else if (sym->register_set == MOJOSHADER_SYMREGSET_INT4) + { + if (sym->register_count > 1) + { + j = 0; + do + { + memcpy(regi + start + (j << 2), + param->valuesI + (j * param->column_count), + param->column_count << 2); + } while (++j < sym->register_count); + } // if + else + memcpy(regi + start, param->valuesI, param->column_count << 2); + } // else if + else if (sym->register_set == MOJOSHADER_SYMREGSET_BOOL) + { + j = 0; + do + { + // regb is not a vec4, enjoy this bitshift! -flibit + regb[(start >> 2) + j] = param->valuesI[j]; + } while (++j < sym->register_count); + } // else if + } // for +} // copy_parameter_data + + +void MOJOSHADER_glEffectCommitChanges(MOJOSHADER_glEffect *glEffect) +{ + MOJOSHADER_effectShader *rawVert = glEffect->current_vert_raw; + MOJOSHADER_effectShader *rawFrag = glEffect->current_frag_raw; + + /* Used for shader selection from preshaders */ + int i; + 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, gls) \ + if (raw != NULL && raw->is_preshader) \ + { \ + i = 0; \ + do \ + { \ + param = &glEffect->effect->params[raw->preshader_params[i]].value; \ + memcpy(raw->preshader->registers + raw->preshader->symbols[i].register_index, \ + param->values, \ + param->value_count << 2); \ + } while (++i < raw->preshader->symbol_count); \ + MOJOSHADER_runPreshader(raw->preshader, &selector); \ + shader_object = glEffect->effect->params[raw->params[0]].value.valuesI[(int) selector]; \ + raw = &glEffect->effect->objects[shader_object].shader; \ + i = 0; \ + do \ + { \ + if (shader_object == glEffect->shader_indices[i]) \ + { \ + gls = &glEffect->shaders[i]; \ + break; \ + } \ + } while (++i < glEffect->num_shaders); \ + selector_ran = 1; \ + } + SELECT_SHADER_FROM_PRESHADER(rawVert, glEffect->current_vert) + SELECT_SHADER_FROM_PRESHADER(rawFrag, glEffect->current_frag) + #undef SELECT_SHADER_FROM_PRESHADER + if (selector_ran) + { + MOJOSHADER_glBindShaders(glEffect->current_vert, + glEffect->current_frag); + if (glEffect->current_vert_raw != NULL) + { + glEffect->effect->state_changes->vertex_sampler_state_changes = rawVert->samplers; + glEffect->effect->state_changes->vertex_sampler_state_change_count = rawVert->sampler_count; + } // if + if (glEffect->current_frag_raw != NULL) + { + glEffect->effect->state_changes->sampler_state_changes = rawFrag->samplers; + glEffect->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) \ + { \ + if (ctx->bound_program->stage##_float4_loc != -1) \ + memset(ctx->stage##_reg_file_f, '\0', ctx->bound_program->stage##_uniforms_float4_count << 4); \ + if (ctx->bound_program->stage##_int4_loc != -1) \ + memset(ctx->stage##_reg_file_i, '\0', ctx->bound_program->stage##_uniforms_int4_count << 4); \ + if (ctx->bound_program->stage##_bool_loc != -1) \ + memset(ctx->stage##_reg_file_b, '\0', ctx->bound_program->stage##_uniforms_bool_count << 2); \ + copy_parameter_data(glEffect->effect->params, raw->params, \ + raw->shader->symbols, \ + raw->shader->symbol_count, \ + ctx->stage##_reg_file_f, \ + ctx->stage##_reg_file_i, \ + ctx->stage##_reg_file_b); \ + if (raw->shader->preshader) \ + { \ + copy_parameter_data(glEffect->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, ctx->stage##_reg_file_f); \ + } \ + } + COPY_PARAMETER_DATA(rawVert, vs) + COPY_PARAMETER_DATA(rawFrag, ps) + #undef COPY_PARAMETER_DATA + + ctx->generation++; +} // MOJOSHADER_glEffectCommitChanges + + +void MOJOSHADER_glEffectEndPass(MOJOSHADER_glEffect *glEffect) +{ + assert(glEffect->effect->current_pass != -1); + glEffect->effect->current_pass = -1; +} // MOJOSHADER_glEffectEndPass + + +void MOJOSHADER_glEffectEnd(MOJOSHADER_glEffect *glEffect) +{ + if (glEffect->effect->restore_shader_state) + { + glEffect->effect->restore_shader_state = 0; + MOJOSHADER_glBindProgram(glEffect->prev_program); + } // if + + glEffect->effect->state_changes = NULL; +} // MOJOSHADER_glEffectEnd + + +#endif // MOJOSHADER_EFFECT_SUPPORT + // end of mojoshader_opengl.c ... diff --git a/utils/testparse.c b/utils/testparse.c index 5fa59450..b38e3e17 100644 --- a/utils/testparse.c +++ b/utils/testparse.c @@ -177,6 +177,15 @@ static void print_preshader_operand(const MOJOSHADER_preshader *preshader, if (operand->type == MOJOSHADER_PRESHADEROPERAND_TEMP) regch = 'r'; + if (operand->array_register_count > 0) + { + for (i = operand->array_register_count - 1; i >= 0; i--) + printf("c%d[", operand->array_registers[i]); + printf("%c%d.%c", regch, operand->index / 4, mask[idx]); + for (i = 0; i < operand->array_register_count; i++) + printf("]"); + break; + } // if printf("%c%d", regch, operand->index / 4); if (isscalar) printf(".%c", mask[idx]); @@ -403,10 +412,149 @@ static void print_shader(const char *fname, const MOJOSHADER_parseData *pd, } // print_shader +#ifdef MOJOSHADER_EFFECT_SUPPORT + + +static void print_value(const MOJOSHADER_effectValue *value, + const unsigned int indent) +{ + int i; + + INDENT(); + printf("VALUE: %s -> %s\n", value->name, value->semantic); + + static const char *classes[] = + { + "SCALAR", + "VECTOR", + "ROW-MAJOR MATRIX", + "COLUMN-MAJOR MATRIX", + "OBJECT", + "STRUCT" + }; + static const char *types[] = + { + "VOID", + "BOOL", + "INT", + "FLOAT", + "STRING", + "TEXTURE", + "TEXTURE1D", + "TEXTURE2D", + "TEXTURE3D", + "TEXTURECUBE", + "SAMPLER", + "SAMPLER1D", + "SAMPLER2D", + "SAMPLER3D", + "SAMPLERCUBE", + "PIXELSHADER", + "VERTEXSHADER", + "UNSUPPORTED" + }; + do_indent(indent + 1); + printf("CLASS: %s\n", classes[value->value_class]); + do_indent(indent + 1); + printf("TYPE: %s\n", types[value->value_type]); + + do_indent(indent + 1); + printf("ROWS/COLUMNS/ELEMENTS: %d, %d, %d\n", + value->row_count, value->column_count, value->element_count); + do_indent(indent + 1); + printf("TOTAL VALUES: %d\n", value->value_count); + + if (value->value_type == MOJOSHADER_SYMTYPE_SAMPLER + || value->value_type == MOJOSHADER_SYMTYPE_SAMPLER1D + || value->value_type == MOJOSHADER_SYMTYPE_SAMPLER2D + || value->value_type == MOJOSHADER_SYMTYPE_SAMPLER3D + || value->value_type == MOJOSHADER_SYMTYPE_SAMPLERCUBE) + { + do_indent(indent + 1); + printf("SAMPLER VALUES:\n"); + for (i = 0; i < value->value_count; i++) + { + MOJOSHADER_effectSamplerState *state = &value->valuesSS[i]; + + static const char *samplerstatetypes[] = + { + "UNKNOWN0", + "UNKNOWN1", + "UNKNOWN2", + "UNKNOWN3", + "TEXTURE", + "ADDRESSU", + "ADDRESSV", + "ADDRESSW", + "BORDERCOLOR", + "MAGFILTER", + "MINFILTER", + "MIPFILTER", + "MIPMAPLODBIAS", + "MAXMIPLEVEL", + "MAXANISOTROPY", + "SRGBTEXTURE", + "ELEMENTINDEX", + "DMAPOFFSET", + }; + do_indent(indent + 2); + printf("TYPE: %s -> ", samplerstatetypes[state->type]); + + /* Assuming only one value per state! */ + if (state->type == MOJOSHADER_SAMP_MIPMAPLODBIAS) + { + /* float types */ + printf("%.2f\n", *state->value.valuesF); + } // if + else + { + /* int/enum types */ + printf("%d\n", *state->value.valuesI); + } // else + } // for + } // if + else + { + do_indent(indent + 1); + printf("%s VALUES:\n", types[value->value_type]); + for (i = 0; i < value->value_count; i++) + { + do_indent(indent + 2); + static const char *prints[] = + { + "%X\n", + "%d\n", + "%d\n", + "%.2f\n", + "%d\n", + "%d\n", + "%d\n", + "%d\n", + "%d\n", + "%d\n", + "SAMPLER?!\n", + "SAMPLER?!\n", + "SAMPLER?!\n", + "SAMPLER?!\n", + "SAMPLER?!\n", + "%d\n", + "%d\n", + "%X\n" + }; + if (value->value_type == MOJOSHADER_SYMTYPE_FLOAT) + printf(prints[value->value_type], value->valuesF[i]); + else + printf(prints[value->value_type], value->valuesI[i]); + } // for + } // else +} // print_value + + static void print_effect(const char *fname, const MOJOSHADER_effect *effect, const unsigned int indent) { - INDENT(); printf("PROFILE: %s\n", effect->profile); + INDENT(); + printf("PROFILE: %s\n", effect->profile); printf("\n"); if (effect->error_count > 0) { @@ -424,63 +572,107 @@ static void print_effect(const char *fname, const MOJOSHADER_effect *effect, { int i, j, k; const MOJOSHADER_effectTechnique *technique = effect->techniques; - const MOJOSHADER_effectTexture *texture = effect->textures; - const MOJOSHADER_effectShader *shader = effect->shaders; + const MOJOSHADER_effectObject *object = effect->objects; const MOJOSHADER_effectParam *param = effect->params; for (i = 0; i < effect->param_count; i++, param++) { INDENT(); - printf("PARAM #%d '%s' -> '%s'\n", i, param->name, param->semantic); - } // for + printf("PARAM #%d\n", i); + print_value(¶m->value, indent + 1); + if (param->annotation_count > 0) + { + do_indent(indent + 1); + printf("ANNOTATIONS:\n"); + } // if + for (j = 0; j < param->annotation_count; j++) + { + print_value(¶m->annotations[j], indent + 2); + } // for + } // for printf("\n"); for (i = 0; i < effect->technique_count; i++, technique++) { const MOJOSHADER_effectPass *pass = technique->passes; - INDENT(); printf("TECHNIQUE #%d ('%s'):\n", i, technique->name); + INDENT(); + printf("TECHNIQUE #%d ('%s'):\n", i, technique->name); for (j = 0; j < technique->pass_count; j++, pass++) { const MOJOSHADER_effectState *state = pass->states; - INDENT(); printf(" PASS #%d ('%s'):\n", j, pass->name); + do_indent(indent + 1); + printf("PASS #%d ('%s'):\n", j, pass->name); for (k = 0; k < pass->state_count; k++, state++) { - INDENT(); printf(" STATE 0x%X\n", state->type); + do_indent(indent + 2); + printf("STATE %d:\n", state->type); + print_value(&state->value, indent + 3); } // for } // for - printf("\n"); } // for - - for (i = 0; i < effect->texture_count; i++, texture++) - { - INDENT(); - printf("TEXTURE #%d ('%s'): %u\n", i, - texture->name, texture->param); - } // for - printf("\n"); - for (i = 0; i < effect->shader_count; i++, shader++) + /* Start at index 1, 0 is always empty (thanks Microsoft!) */ + object++; + for (i = 1; i < effect->object_count; i++, object++) { INDENT(); - printf("SHADER #%d: technique %u, pass %u\n", i, - shader->technique, shader->pass); - print_shader(fname, shader->shader, indent + 1); + if (object->type == MOJOSHADER_SYMTYPE_PIXELSHADER + || object->type == MOJOSHADER_SYMTYPE_VERTEXSHADER) + { + if (object->shader.is_preshader) + { + printf("OBJECT #%d: PRESHADER, technique %u, pass %u, param %s\n", i, + object->shader.technique, object->shader.pass, + effect->params[object->shader.params[0]].value.name); + print_preshader(object->shader.preshader, indent + 1); + } // if + else + { + printf("OBJECT #%d: SHADER, technique %u, pass %u\n", i, + object->shader.technique, object->shader.pass); + print_shader(fname, object->shader.shader, indent + 1); + } // else + } // if + else if (object->type == MOJOSHADER_SYMTYPE_STRING) + printf("OBJECT #%d: STRING, '%s'\n", i, + object->string.string); + else if (object->type == MOJOSHADER_SYMTYPE_SAMPLER + || object->type == MOJOSHADER_SYMTYPE_SAMPLER1D + || object->type == MOJOSHADER_SYMTYPE_SAMPLER2D + || object->type == MOJOSHADER_SYMTYPE_SAMPLER3D + || object->type == MOJOSHADER_SYMTYPE_SAMPLERCUBE) + printf("OBJECT #%d: MAPPING, '%s'\n", i, + object->mapping.name); + else if (object->type == MOJOSHADER_SYMTYPE_TEXTURE + || object->type == MOJOSHADER_SYMTYPE_TEXTURE1D + || object->type == MOJOSHADER_SYMTYPE_TEXTURE2D + || object->type == MOJOSHADER_SYMTYPE_TEXTURE3D + || object->type == MOJOSHADER_SYMTYPE_TEXTURECUBE) + printf("OBJECT #%d: TEXTURE\n", i); + else + printf("UNKNOWN OBJECT: #%d\n", i); } // for } // else } // print_effect +#endif // MOJOSHADER_EFFECT_SUPPORT + + static int do_parse(const char *fname, const unsigned char *buf, const int len, const char *prof) { int retval = 0; // magic for an effects file (!!! FIXME: I _think_). - if ( (buf[0] == 0x01) && (buf[1] == 0x09) && - (buf[2] == 0xFF) && (buf[3] == 0xFE) ) + if ( ((buf[0] == 0x01) && (buf[1] == 0x09) && + (buf[2] == 0xFF) && (buf[3] == 0xFE)) || + ((buf[0] == 0xCF) && (buf[1] == 0x0B) && + (buf[2] == 0xF0) && (buf[3] == 0xBC)) ) { +#ifdef MOJOSHADER_EFFECT_SUPPORT const MOJOSHADER_effect *effect; effect = MOJOSHADER_parseEffect(prof, buf, len, NULL, 0, NULL, 0, Malloc, Free, 0); @@ -488,6 +680,9 @@ static int do_parse(const char *fname, const unsigned char *buf, printf("EFFECT: %s\n", fname); print_effect(fname, effect, 1); MOJOSHADER_freeEffect(effect); +#else + printf("Is an effect, but effect support is disabled!\n"); +#endif } // if else // do it as a regular compiled shader.