Show More
Commit Description:
Tidy code.
Commit Description:
Tidy code.
References:
File last commit:
Show/Diff file:
Action:
FNA/lib/MojoShader/mojoshader_opengl.c
3377 lines | 114.3 KiB | text/x-c | CLexer
3377 lines | 114.3 KiB | text/x-c | CLexer
r0 | /** | |||
* MojoShader; generate shader programs from bytecode of compiled | ||||
* Direct3D shaders. | ||||
* | ||||
* Please see the file LICENSE.txt in the source's root directory. | ||||
* | ||||
* This file written by Ryan C. Gordon. | ||||
*/ | ||||
#include <stdio.h> | ||||
#include <string.h> | ||||
#include <stdlib.h> | ||||
#include <stdarg.h> | ||||
#include <assert.h> | ||||
#ifdef _WIN32 | ||||
#define WIN32_LEAN_AND_MEAN 1 | ||||
#include <windows.h> // GL headers need this for WINGDIAPI definition. | ||||
#endif | ||||
#if (defined(__APPLE__) && defined(__MACH__)) | ||||
#include "TargetConditionals.h" | ||||
#if !TARGET_OS_IPHONE && !TARGET_OS_TV | ||||
#define PLATFORM_MACOSX 1 | ||||
#endif /* !TARGET_OS_IPHONE && !TARGET_OS_TV */ | ||||
#endif /* (defined(__APPLE__) && defined(__MACH__)) */ | ||||
#if PLATFORM_MACOSX | ||||
#include <Carbon/Carbon.h> | ||||
#endif | ||||
#define __MOJOSHADER_INTERNAL__ 1 | ||||
#include "mojoshader_internal.h" | ||||
#define GL_GLEXT_LEGACY 1 | ||||
#include "GL/gl.h" | ||||
#include "GL/glext.h" | ||||
#if SUPPORT_PROFILE_GLSPIRV | ||||
#include "spirv/spirv.h" | ||||
#endif | ||||
#ifndef GL_HALF_FLOAT_NV | ||||
#define GL_HALF_FLOAT_NV 0x140B | ||||
#endif | ||||
#ifndef GL_HALF_FLOAT_ARB | ||||
#define GL_HALF_FLOAT_ARB 0x140B | ||||
#endif | ||||
#ifndef GL_HALF_FLOAT_OES | ||||
#define GL_HALF_FLOAT_OES 0x8D61 | ||||
#endif | ||||
// this happens to be the same value for ARB1 and GLSL. | ||||
#ifndef GL_PROGRAM_POINT_SIZE | ||||
#define GL_PROGRAM_POINT_SIZE 0x8642 | ||||
#endif | ||||
// FIXME: ARB_gl_spirv in glext.h? -flibit | ||||
#ifndef GL_ARB_gl_spirv | ||||
#define GL_ARB_gl_spirv 1 | ||||
#define GL_SHADER_BINARY_FORMAT_SPIR_V_ARB 0x9551 | ||||
typedef void (APIENTRYP PFNGLSPECIALIZESHADERARBPROC) ( | ||||
GLuint shader, | ||||
const GLchar* pEntryPoint, | ||||
GLuint numSpecializationConstants, | ||||
const GLuint* pConstantIndex, | ||||
const GLuint* pConstantValue | ||||
); | ||||
#endif | ||||
struct MOJOSHADER_glShader | ||||
{ | ||||
const MOJOSHADER_parseData *parseData; | ||||
GLuint handle; | ||||
uint32 refcount; | ||||
}; | ||||
typedef struct | ||||
{ | ||||
MOJOSHADER_shaderType shader_type; | ||||
const MOJOSHADER_uniform *uniform; | ||||
GLint location; | ||||
} UniformMap; | ||||
typedef struct | ||||
{ | ||||
const MOJOSHADER_attribute *attribute; | ||||
GLint location; | ||||
} AttributeMap; | ||||
struct MOJOSHADER_glProgram | ||||
{ | ||||
MOJOSHADER_glShader *vertex; | ||||
MOJOSHADER_glShader *fragment; | ||||
GLuint handle; | ||||
uint32 generation; | ||||
uint32 uniform_count; | ||||
uint32 texbem_count; | ||||
UniformMap *uniforms; | ||||
uint32 attribute_count; | ||||
AttributeMap *attributes; | ||||
size_t vs_uniforms_float4_count; | ||||
GLfloat *vs_uniforms_float4; | ||||
size_t vs_uniforms_int4_count; | ||||
GLint *vs_uniforms_int4; | ||||
size_t vs_uniforms_bool_count; | ||||
GLint *vs_uniforms_bool; | ||||
size_t ps_uniforms_float4_count; | ||||
GLfloat *ps_uniforms_float4; | ||||
size_t ps_uniforms_int4_count; | ||||
GLint *ps_uniforms_int4; | ||||
size_t ps_uniforms_bool_count; | ||||
GLint *ps_uniforms_bool; | ||||
uint32 refcount; | ||||
int uses_pointsize; | ||||
// According to MSDN... | ||||
// | ||||
// n is an optional integer between 0 and the number of resources supported. | ||||
// For example, POSITION0, TEXCOOR1, etc. | ||||
// | ||||
// The input registers consist of 16 four-component floating-point vectors, | ||||
// designated as v0 through v15. | ||||
GLint vertex_attrib_loc[MOJOSHADER_USAGE_TOTAL][16]; | ||||
// GLSL uses these...location of uniform arrays. | ||||
GLint vs_float4_loc; | ||||
GLint vs_int4_loc; | ||||
GLint vs_bool_loc; | ||||
GLint ps_float4_loc; | ||||
GLint ps_int4_loc; | ||||
GLint ps_bool_loc; | ||||
// Numerous fixes for coordinate system mismatches | ||||
GLint ps_vpos_flip_loc; | ||||
int current_vpos_flip[2]; | ||||
#ifdef MOJOSHADER_FLIP_RENDERTARGET | ||||
GLint vs_flip_loc; | ||||
int current_flip; | ||||
#endif | ||||
}; | ||||
#ifndef WINGDIAPI | ||||
#define WINGDIAPI | ||||
#endif | ||||
// Entry points in base OpenGL that lack function pointer prototypes... | ||||
typedef WINGDIAPI void (APIENTRYP PFNGLGETINTEGERVPROC) (GLenum pname, GLint *params); | ||||
typedef WINGDIAPI const GLubyte * (APIENTRYP PFNGLGETSTRINGPROC) (GLenum name); | ||||
typedef WINGDIAPI GLenum (APIENTRYP PFNGLGETERRORPROC) (void); | ||||
typedef WINGDIAPI void (APIENTRYP PFNGLENABLEPROC) (GLenum cap); | ||||
typedef WINGDIAPI void (APIENTRYP PFNGLDISABLEPROC) (GLenum cap); | ||||
// Max entries for each register file type... | ||||
#define MAX_REG_FILE_F 8192 | ||||
#define MAX_REG_FILE_I 2047 | ||||
#define MAX_REG_FILE_B 2047 | ||||
#define MAX_TEXBEMS 3 // ps_1_1 allows 4 texture stages, texbem can't use t0. | ||||
struct MOJOSHADER_glContext | ||||
{ | ||||
// Allocators... | ||||
MOJOSHADER_malloc malloc_fn; | ||||
MOJOSHADER_free free_fn; | ||||
void *malloc_data; | ||||
// The constant register files... | ||||
// !!! FIXME: Man, it kills me how much memory this takes... | ||||
// !!! FIXME: ... make this dynamically allocated on demand. | ||||
GLfloat vs_reg_file_f[MAX_REG_FILE_F * 4]; | ||||
GLint vs_reg_file_i[MAX_REG_FILE_I * 4]; | ||||
uint8 vs_reg_file_b[MAX_REG_FILE_B]; | ||||
GLfloat ps_reg_file_f[MAX_REG_FILE_F * 4]; | ||||
GLint ps_reg_file_i[MAX_REG_FILE_I * 4]; | ||||
uint8 ps_reg_file_b[MAX_REG_FILE_B]; | ||||
GLuint sampler_reg_file[16]; | ||||
GLfloat texbem_state[MAX_TEXBEMS * 6]; | ||||
// This increments every time we change the register files. | ||||
uint32 generation; | ||||
// This keeps track of implicitly linked programs. | ||||
HashTable *linker_cache; | ||||
// This tells us which vertex attribute arrays we have enabled. | ||||
GLint max_attrs; | ||||
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; | ||||
// GL stuff... | ||||
int opengl_major; | ||||
int opengl_minor; | ||||
int glsl_major; | ||||
int glsl_minor; | ||||
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; | ||||
int have_GL_NV_fragment_program2; | ||||
int have_GL_NV_vertex_program3; | ||||
int have_GL_NV_gpu_program4; | ||||
int have_GL_ARB_shader_objects; | ||||
int have_GL_ARB_vertex_shader; | ||||
int have_GL_ARB_fragment_shader; | ||||
int have_GL_ARB_shading_language_100; | ||||
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; | ||||
int have_GL_ARB_ES2_compatibility; | ||||
int have_GL_ARB_gl_spirv; | ||||
// Entry points... | ||||
PFNGLGETSTRINGPROC glGetString; | ||||
PFNGLGETSTRINGIPROC glGetStringi; | ||||
PFNGLGETERRORPROC glGetError; | ||||
PFNGLGETINTEGERVPROC glGetIntegerv; | ||||
PFNGLENABLEPROC glEnable; | ||||
PFNGLDISABLEPROC glDisable; | ||||
PFNGLDELETESHADERPROC glDeleteShader; | ||||
PFNGLDELETEPROGRAMPROC glDeleteProgram; | ||||
PFNGLATTACHSHADERPROC glAttachShader; | ||||
PFNGLCOMPILESHADERPROC glCompileShader; | ||||
PFNGLCREATESHADERPROC glCreateShader; | ||||
PFNGLCREATEPROGRAMPROC glCreateProgram; | ||||
PFNGLDISABLEVERTEXATTRIBARRAYPROC glDisableVertexAttribArray; | ||||
PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArray; | ||||
PFNGLGETATTRIBLOCATIONPROC glGetAttribLocation; | ||||
PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog; | ||||
PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog; | ||||
PFNGLGETSHADERIVPROC glGetShaderiv; | ||||
PFNGLGETPROGRAMIVPROC glGetProgramiv; | ||||
PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation; | ||||
PFNGLLINKPROGRAMPROC glLinkProgram; | ||||
PFNGLSHADERSOURCEPROC glShaderSource; | ||||
PFNGLUNIFORM1IPROC glUniform1i; | ||||
PFNGLUNIFORM1IVPROC glUniform1iv; | ||||
PFNGLUNIFORM2FPROC glUniform2f; | ||||
#ifdef MOJOSHADER_FLIP_RENDERTARGET | ||||
PFNGLUNIFORM1FPROC glUniform1f; | ||||
#endif | ||||
PFNGLUNIFORM4FVPROC glUniform4fv; | ||||
PFNGLUNIFORM4IVPROC glUniform4iv; | ||||
PFNGLUSEPROGRAMPROC glUseProgram; | ||||
PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer; | ||||
PFNGLDELETEOBJECTARBPROC glDeleteObjectARB; | ||||
PFNGLATTACHOBJECTARBPROC glAttachObjectARB; | ||||
PFNGLCOMPILESHADERARBPROC glCompileShaderARB; | ||||
PFNGLCREATEPROGRAMOBJECTARBPROC glCreateProgramObjectARB; | ||||
PFNGLCREATESHADEROBJECTARBPROC glCreateShaderObjectARB; | ||||
PFNGLDISABLEVERTEXATTRIBARRAYARBPROC glDisableVertexAttribArrayARB; | ||||
PFNGLENABLEVERTEXATTRIBARRAYARBPROC glEnableVertexAttribArrayARB; | ||||
PFNGLGETATTRIBLOCATIONARBPROC glGetAttribLocationARB; | ||||
PFNGLGETINFOLOGARBPROC glGetInfoLogARB; | ||||
PFNGLGETOBJECTPARAMETERIVARBPROC glGetObjectParameterivARB; | ||||
PFNGLGETUNIFORMLOCATIONARBPROC glGetUniformLocationARB; | ||||
PFNGLLINKPROGRAMARBPROC glLinkProgramARB; | ||||
PFNGLSHADERSOURCEARBPROC glShaderSourceARB; | ||||
PFNGLUNIFORM1IARBPROC glUniform1iARB; | ||||
PFNGLUNIFORM1IVARBPROC glUniform1ivARB; | ||||
PFNGLUNIFORM4FVARBPROC glUniform4fvARB; | ||||
PFNGLUNIFORM4IVARBPROC glUniform4ivARB; | ||||
PFNGLUSEPROGRAMOBJECTARBPROC glUseProgramObjectARB; | ||||
PFNGLVERTEXATTRIBPOINTERARBPROC glVertexAttribPointerARB; | ||||
PFNGLGETPROGRAMIVARBPROC glGetProgramivARB; | ||||
PFNGLPROGRAMLOCALPARAMETER4FVARBPROC glProgramLocalParameter4fvARB; | ||||
PFNGLPROGRAMLOCALPARAMETERI4IVNVPROC glProgramLocalParameterI4ivNV; | ||||
PFNGLDELETEPROGRAMSARBPROC glDeleteProgramsARB; | ||||
PFNGLGENPROGRAMSARBPROC glGenProgramsARB; | ||||
PFNGLBINDPROGRAMARBPROC glBindProgramARB; | ||||
PFNGLPROGRAMSTRINGARBPROC glProgramStringARB; | ||||
PFNGLVERTEXATTRIBDIVISORARBPROC glVertexAttribDivisorARB; | ||||
PFNGLSHADERBINARYPROC glShaderBinary; | ||||
PFNGLSPECIALIZESHADERARBPROC glSpecializeShaderARB; | ||||
// interface for profile-specific things. | ||||
int (*profileMaxUniforms)(MOJOSHADER_shaderType shader_type); | ||||
int (*profileCompileShader)(const MOJOSHADER_parseData *pd, GLuint *s); | ||||
void (*profileDeleteShader)(const GLuint shader); | ||||
void (*profileDeleteProgram)(const GLuint program); | ||||
GLint (*profileGetAttribLocation)(MOJOSHADER_glProgram *program, int idx); | ||||
GLint (*profileGetUniformLocation)(MOJOSHADER_glProgram *program, MOJOSHADER_glShader *shader, int idx); | ||||
GLint (*profileGetSamplerLocation)(MOJOSHADER_glProgram *, MOJOSHADER_glShader *, int); | ||||
GLuint (*profileLinkProgram)(MOJOSHADER_glShader *, MOJOSHADER_glShader *); | ||||
void (*profileFinalInitProgram)(MOJOSHADER_glProgram *program); | ||||
void (*profileUseProgram)(MOJOSHADER_glProgram *program); | ||||
void (*profilePushConstantArray)(MOJOSHADER_glProgram *, const MOJOSHADER_uniform *, const GLfloat *); | ||||
void (*profilePushUniforms)(void); | ||||
void (*profilePushSampler)(GLint loc, GLuint sampler); | ||||
int (*profileMustPushConstantArrays)(void); | ||||
int (*profileMustPushSamplers)(void); | ||||
void (*profileToggleProgramPointSize)(int enable); | ||||
}; | ||||
static MOJOSHADER_glContext *ctx = NULL; | ||||
// Error state... | ||||
static char error_buffer[1024] = { '\0' }; | ||||
static void set_error(const char *str) | ||||
{ | ||||
snprintf(error_buffer, sizeof (error_buffer), "%s", str); | ||||
} // set_error | ||||
#if PLATFORM_MACOSX | ||||
static inline int macosx_version_atleast(int x, int y, int z) | ||||
{ | ||||
static int checked = 0; | ||||
static int combined = 0; | ||||
if (!checked) | ||||
{ | ||||
SInt32 ver = 0; | ||||
SInt32 major = 0; | ||||
SInt32 minor = 0; | ||||
SInt32 patch = 0; | ||||
int convert = 0; | ||||
if (Gestalt(gestaltSystemVersion, &ver) != noErr) | ||||
{ | ||||
ver = 0x1000; // oh well. | ||||
convert = 1; // split (ver) into (major),(minor),(patch). | ||||
} | ||||
else if (ver < 0x1030) | ||||
{ | ||||
convert = 1; // split (ver) into (major),(minor),(patch). | ||||
} | ||||
else | ||||
{ | ||||
// presumably this won't fail. But if it does, we'll just use the | ||||
// original version value. This might cut the value--10.12.11 will | ||||
// come out to 10.9.9, for example--but it's better than nothing. | ||||
if (Gestalt(gestaltSystemVersionMajor, &major) != noErr) | ||||
convert = 1; | ||||
else if (Gestalt(gestaltSystemVersionMinor, &minor) != noErr) | ||||
convert = 1; | ||||
else if (Gestalt(gestaltSystemVersionBugFix, &patch) != noErr) | ||||
convert = 1; | ||||
} // else | ||||
if (convert) | ||||
{ | ||||
major = ((ver & 0xFF00) >> 8); | ||||
major = (((major / 16) * 10) + (major % 16)); | ||||
minor = ((ver & 0xF0) >> 4); | ||||
patch = (ver & 0xF); | ||||
} // if | ||||
combined = (major << 16) | (minor << 8) | patch; | ||||
checked = 1; | ||||
} // if | ||||
return (combined >= ((x << 16) | (y << 8) | z)); | ||||
} // macosx_version_atleast | ||||
#endif | ||||
static inline void out_of_memory(void) | ||||
{ | ||||
set_error("out of memory"); | ||||
} // out_of_memory | ||||
static inline void *Malloc(const size_t len) | ||||
{ | ||||
void *retval = ctx->malloc_fn((int) len, ctx->malloc_data); | ||||
if (retval == NULL) | ||||
out_of_memory(); | ||||
return retval; | ||||
} // Malloc | ||||
static inline void Free(void *ptr) | ||||
{ | ||||
if (ptr != NULL) | ||||
ctx->free_fn(ptr, ctx->malloc_data); | ||||
} // Free | ||||
static inline void toggle_gl_state(GLenum state, int val) | ||||
{ | ||||
if (val) | ||||
ctx->glEnable(state); | ||||
else | ||||
ctx->glDisable(state); | ||||
} // toggle_gl_state | ||||
// profile-specific implementations... | ||||
#if SUPPORT_PROFILE_GLSL || SUPPORT_PROFILE_GLSPIRV | ||||
static inline GLenum glsl_shader_type(const MOJOSHADER_shaderType t) | ||||
{ | ||||
// these enums match between core 2.0 and the ARB extensions. | ||||
if (t == MOJOSHADER_TYPE_VERTEX) | ||||
return GL_VERTEX_SHADER; | ||||
else if (t == MOJOSHADER_TYPE_PIXEL) | ||||
return GL_FRAGMENT_SHADER; | ||||
// !!! FIXME: geometry shaders? | ||||
assert(0 && "Unknown GLSL shader type!"); | ||||
return GL_NONE; | ||||
} // glsl_shader_type | ||||
static int impl_GLSL_MustPushConstantArrays(void) { return 1; } | ||||
static int impl_GLSL_MustPushSamplers(void) { return 1; } | ||||
static int impl_GLSL_MaxUniforms(MOJOSHADER_shaderType shader_type) | ||||
{ | ||||
// these enums match between core 2.0 and the ARB extensions. | ||||
GLenum pname = GL_NONE; | ||||
GLint val = 0; | ||||
if (shader_type == MOJOSHADER_TYPE_VERTEX) | ||||
pname = GL_MAX_VERTEX_UNIFORM_COMPONENTS; | ||||
else if (shader_type == MOJOSHADER_TYPE_PIXEL) | ||||
pname = GL_MAX_FRAGMENT_UNIFORM_COMPONENTS; | ||||
else | ||||
return -1; | ||||
ctx->glGetIntegerv(pname, &val); | ||||
return (int) val; | ||||
} // impl_GLSL_MaxUniforms | ||||
#if SUPPORT_PROFILE_GLSPIRV | ||||
static const SpirvPatchTable* spv_getPatchTable(MOJOSHADER_glShader *shader) | ||||
{ | ||||
const MOJOSHADER_parseData *pd = shader->parseData; | ||||
size_t table_offset = pd->output_len - sizeof(SpirvPatchTable); | ||||
return (const SpirvPatchTable *) (pd->output + table_offset); | ||||
} // spv_getPatchTable | ||||
static int spv_CompileShader(const MOJOSHADER_parseData *pd, int32 base_location, GLuint *s, int32 patch_pcoord) | ||||
{ | ||||
GLint ok = 0; | ||||
GLsizei data_len = pd->output_len - sizeof(SpirvPatchTable); | ||||
const GLvoid* data = pd->output; | ||||
uint32 *patched_data = NULL; | ||||
if (base_location || patch_pcoord) | ||||
{ | ||||
size_t i, max; | ||||
patched_data = (uint32 *) Malloc(data_len); | ||||
memcpy(patched_data, data, data_len); | ||||
const SpirvPatchTable *table = (const SpirvPatchTable *) &pd->output[data_len]; | ||||
if (table->vpflip.offset) patched_data[table->vpflip.offset] += base_location; | ||||
if (table->array_vec4.offset) patched_data[table->array_vec4.offset] += base_location; | ||||
if (table->array_ivec4.offset) patched_data[table->array_ivec4.offset] += base_location; | ||||
if (table->array_bool.offset) patched_data[table->array_bool.offset] += base_location; | ||||
for (i = 0, max = STATICARRAYLEN(table->samplers); i < max; i++) | ||||
{ | ||||
SpirvPatchEntry entry = table->samplers[i]; | ||||
if (entry.offset) | ||||
patched_data[entry.offset] += base_location; | ||||
} // for | ||||
if (patch_pcoord && table->ps_texcoord0_offset) | ||||
{ | ||||
// Subtract 3 to get from Location value offset to start of op. | ||||
uint32 op_base = table->ps_texcoord0_offset - 3; | ||||
assert(patched_data[op_base+0] == (SpvOpDecorate | (4 << 16))); | ||||
assert(patched_data[op_base+2] == SpvDecorationLocation); | ||||
patched_data[op_base+2] = SpvDecorationBuiltIn; | ||||
patched_data[op_base+3] = SpvBuiltInPointCoord; | ||||
} // if | ||||
data = patched_data; | ||||
} // if | ||||
const GLuint shader = ctx->glCreateShader(glsl_shader_type(pd->shader_type)); | ||||
ctx->glShaderBinary(1, &shader, GL_SHADER_BINARY_FORMAT_SPIR_V_ARB, data, data_len); | ||||
ctx->glSpecializeShaderARB(shader, pd->mainfn, 0, NULL, NULL); // FIXME: Spec Constants? -flibit | ||||
ctx->glGetShaderiv(shader, GL_COMPILE_STATUS, &ok); | ||||
if (patched_data) | ||||
Free(patched_data); | ||||
if (!ok) | ||||
{ | ||||
GLsizei len = 0; | ||||
ctx->glGetShaderInfoLog(shader, sizeof(error_buffer), &len, | ||||
(GLchar *) error_buffer); | ||||
ctx->glDeleteShader(shader); | ||||
*s = 0; | ||||
return 0; | ||||
} // if | ||||
*s = shader; | ||||
return 1; | ||||
} // spv_CompileShader | ||||
static int impl_SPIRV_CompileShader(const MOJOSHADER_parseData *pd, GLuint *s) | ||||
{ | ||||
// Compilation postponed until linking, but generate dummy shader id so hash table lookups work. | ||||
*s = ctx->glCreateShader(glsl_shader_type(pd->shader_type)); | ||||
return 1; | ||||
} // impl_SPIRV_CompileShader | ||||
static GLuint impl_SPIRV_LinkProgram(MOJOSHADER_glShader *vshader, | ||||
MOJOSHADER_glShader *pshader) | ||||
{ | ||||
GLint ok = 0; | ||||
// Shader compilation postponed until linking due to uniform locations being global in program. | ||||
// To avoid overlap between VS and PS, we need to know about other shader stages to assign final | ||||
// uniform locations before compilation. | ||||
GLuint vs_handle = 0; | ||||
int32 base_location = 0; | ||||
int32 patch_pcoord = 0; | ||||
if (vshader) | ||||
{ | ||||
if (!spv_CompileShader(vshader->parseData, base_location, &vs_handle, patch_pcoord)) | ||||
return 0; | ||||
const SpirvPatchTable* patch_table = spv_getPatchTable(vshader); | ||||
base_location += patch_table->location_count; | ||||
patch_pcoord = patch_table->vs_has_psize; | ||||
} // if | ||||
GLuint ps_handle = 0; | ||||
if (pshader) | ||||
{ | ||||
if (!spv_CompileShader(pshader->parseData, base_location, &ps_handle, patch_pcoord)) | ||||
return 0; | ||||
} // if | ||||
if (ctx->have_opengl_2) | ||||
{ | ||||
const GLuint program = ctx->glCreateProgram(); | ||||
if (vs_handle) | ||||
{ | ||||
ctx->glAttachShader(program, vs_handle); | ||||
ctx->glDeleteShader(vs_handle); | ||||
} // if | ||||
if (ps_handle) | ||||
{ | ||||
ctx->glAttachShader(program, ps_handle); | ||||
ctx->glDeleteShader(ps_handle); | ||||
} // if | ||||
ctx->glLinkProgram(program); | ||||
ctx->glGetProgramiv(program, GL_LINK_STATUS, &ok); | ||||
if (!ok) | ||||
{ | ||||
GLsizei len = 0; | ||||
ctx->glGetProgramInfoLog(program, sizeof (error_buffer), | ||||
&len, (GLchar *) error_buffer); | ||||
ctx->glDeleteProgram(program); | ||||
return 0; | ||||
} // if | ||||
return program; | ||||
} // if | ||||
else | ||||
{ | ||||
const GLhandleARB program = ctx->glCreateProgramObjectARB(); | ||||
assert(sizeof(program) == sizeof(GLuint)); // not always true on OS X! | ||||
if (vs_handle) | ||||
{ | ||||
ctx->glAttachObjectARB(program, (GLhandleARB) vs_handle); | ||||
ctx->glDeleteObjectARB((GLhandleARB) vs_handle); | ||||
} // if | ||||
if (ps_handle) | ||||
{ | ||||
ctx->glAttachObjectARB(program, (GLhandleARB) ps_handle); | ||||
ctx->glDeleteObjectARB((GLhandleARB) ps_handle); | ||||
} // if | ||||
ctx->glLinkProgramARB(program); | ||||
ctx->glGetObjectParameterivARB(program, GL_OBJECT_LINK_STATUS_ARB, &ok); | ||||
if (!ok) | ||||
{ | ||||
GLsizei len = 0; | ||||
ctx->glGetInfoLogARB(program, sizeof (error_buffer), | ||||
&len, (GLcharARB *) error_buffer); | ||||
ctx->glDeleteObjectARB(program); | ||||
return 0; | ||||
} // if | ||||
return (GLuint) program; | ||||
} // else | ||||
} // impl_SPIRV_LinkProgram | ||||
static void impl_SPIRV_DeleteShader(const GLuint shader) | ||||
{ | ||||
ctx->glDeleteShader(shader); | ||||
} // impl_SPIRV_DeleteShader | ||||
static void impl_SPIRV_DeleteProgram(const GLuint program) | ||||
{ | ||||
if (ctx->have_opengl_2) | ||||
ctx->glDeleteProgram(program); | ||||
else | ||||
ctx->glDeleteObjectARB((GLhandleARB) program); | ||||
} // impl_SPIRV_DeleteProgram | ||||
static GLint impl_SPIRV_GetAttribLocation(MOJOSHADER_glProgram *program, int idx) | ||||
{ | ||||
return idx; | ||||
} // impl_SPIRV_GetAttribLocation | ||||
static GLint impl_SPIRV_GetUniformLocation(MOJOSHADER_glProgram *program, MOJOSHADER_glShader *shader, int idx) | ||||
{ | ||||
return 0; // no-op, we push this as one big-ass array now. | ||||
} // impl_SPIRV_GetUniformLocation | ||||
static GLint impl_SPIRV_GetSamplerLocation(MOJOSHADER_glProgram *program, MOJOSHADER_glShader *shader, int idx) | ||||
{ | ||||
const SpirvPatchTable *table = spv_getPatchTable(shader); | ||||
GLint location = table->samplers[idx].location; | ||||
if (location == -1) | ||||
return location; | ||||
assert(location >= 0); | ||||
if (shader->parseData->shader_type == MOJOSHADER_TYPE_PIXEL) | ||||
location += spv_getPatchTable(program->vertex)->location_count; | ||||
return location; | ||||
} // impl_SPIRV_GetSamplerLocation | ||||
static void impl_SPIRV_FinalInitProgram(MOJOSHADER_glProgram *program) | ||||
{ | ||||
const SpirvPatchTable *vs_table = spv_getPatchTable(program->vertex); | ||||
const SpirvPatchTable *ps_table = spv_getPatchTable(program->fragment); | ||||
program->vs_float4_loc = vs_table->array_vec4.location; | ||||
program->vs_int4_loc = vs_table->array_ivec4.location; | ||||
program->vs_bool_loc = vs_table->array_bool.location; | ||||
program->ps_float4_loc = ps_table->array_vec4.location; | ||||
program->ps_int4_loc = ps_table->array_ivec4.location; | ||||
program->ps_bool_loc = ps_table->array_bool.location; | ||||
program->ps_vpos_flip_loc = ps_table->vpflip.location; | ||||
#ifdef MOJOSHADER_FLIP_RENDERTARGET | ||||
program->vs_flip_loc = vs_table->vpflip.location; | ||||
#endif | ||||
int32 ps_base_location = vs_table->location_count; | ||||
if (ps_base_location) | ||||
{ | ||||
if (program->ps_float4_loc != -1) program->ps_float4_loc += ps_base_location; | ||||
if (program->ps_int4_loc != -1) program->ps_int4_loc += ps_base_location; | ||||
if (program->ps_bool_loc != -1) program->ps_bool_loc += ps_base_location; | ||||
if (program->ps_vpos_flip_loc != -1) program->ps_vpos_flip_loc += ps_base_location; | ||||
} // if | ||||
} // impl_SPIRV_FinalInitProgram | ||||
#endif // SUPPORT_PROFILE_GLSPIRV | ||||
#if SUPPORT_PROFILE_GLSL | ||||
static int impl_GLSL_CompileShader(const MOJOSHADER_parseData *pd, GLuint *s) | ||||
{ | ||||
GLint ok = 0; | ||||
const GLint codelen = (GLint) pd->output_len; | ||||
const GLenum shader_type = glsl_shader_type(pd->shader_type); | ||||
if (ctx->have_opengl_2) | ||||
{ | ||||
const GLuint shader = ctx->glCreateShader(shader_type); | ||||
ctx->glShaderSource(shader, 1, (const GLchar**) &pd->output, &codelen); | ||||
ctx->glCompileShader(shader); | ||||
ctx->glGetShaderiv(shader, GL_COMPILE_STATUS, &ok); | ||||
if (!ok) | ||||
{ | ||||
GLsizei len = 0; | ||||
ctx->glGetShaderInfoLog(shader, sizeof (error_buffer), &len, | ||||
(GLchar *) error_buffer); | ||||
ctx->glDeleteShader(shader); | ||||
*s = 0; | ||||
return 0; | ||||
} // if | ||||
*s = shader; | ||||
} // if | ||||
else | ||||
{ | ||||
const GLhandleARB shader = ctx->glCreateShaderObjectARB(shader_type); | ||||
assert(sizeof (shader) == sizeof (*s)); // not always true on OS X! | ||||
ctx->glShaderSourceARB(shader, 1, | ||||
(const GLcharARB **) &pd->output, &codelen); | ||||
ctx->glCompileShaderARB(shader); | ||||
ctx->glGetObjectParameterivARB(shader,GL_OBJECT_COMPILE_STATUS_ARB,&ok); | ||||
if (!ok) | ||||
{ | ||||
GLsizei len = 0; | ||||
ctx->glGetInfoLogARB(shader, sizeof (error_buffer), &len, | ||||
(GLcharARB *) error_buffer); | ||||
ctx->glDeleteObjectARB(shader); | ||||
*s = 0; | ||||
return 0; | ||||
} // if | ||||
*s = (GLuint) shader; | ||||
} // else | ||||
return 1; | ||||
} // impl_GLSL_CompileShader | ||||
#endif // SUPPORT_PROFILE_GLSL | ||||
static void impl_GLSL_DeleteShader(const GLuint shader) | ||||
{ | ||||
if (ctx->have_opengl_2) | ||||
ctx->glDeleteShader(shader); | ||||
else | ||||
ctx->glDeleteObjectARB((GLhandleARB) shader); | ||||
} // impl_GLSL_DeleteShader | ||||
static void impl_GLSL_DeleteProgram(const GLuint program) | ||||
{ | ||||
if (ctx->have_opengl_2) | ||||
ctx->glDeleteProgram(program); | ||||
else | ||||
ctx->glDeleteObjectARB((GLhandleARB) program); | ||||
} // impl_GLSL_DeleteProgram | ||||
static GLint impl_GLSL_GetUniformLocation(MOJOSHADER_glProgram *program, | ||||
MOJOSHADER_glShader *shader, int idx) | ||||
{ | ||||
return 0; // no-op, we push this as one big-ass array now. | ||||
} // impl_GLSL_GetUniformLocation | ||||
static inline GLint glsl_uniform_loc(MOJOSHADER_glProgram *program, | ||||
const char *name) | ||||
{ | ||||
return ctx->have_opengl_2 ? | ||||
ctx->glGetUniformLocation(program->handle, name) : | ||||
ctx->glGetUniformLocationARB((GLhandleARB) program->handle, name); | ||||
} // glsl_uniform_loc | ||||
static GLint impl_GLSL_GetSamplerLocation(MOJOSHADER_glProgram *program, | ||||
MOJOSHADER_glShader *shader, int idx) | ||||
{ | ||||
return glsl_uniform_loc(program, shader->parseData->samplers[idx].name); | ||||
} // impl_GLSL_GetSamplerLocation | ||||
static GLint impl_GLSL_GetAttribLocation(MOJOSHADER_glProgram *program, int idx) | ||||
{ | ||||
const MOJOSHADER_parseData *pd = program->vertex->parseData; | ||||
const MOJOSHADER_attribute *a = pd->attributes; | ||||
if (ctx->have_opengl_2) | ||||
{ | ||||
return ctx->glGetAttribLocation(program->handle, | ||||
(const GLchar *) a[idx].name); | ||||
} // if | ||||
return ctx->glGetAttribLocationARB((GLhandleARB) program->handle, | ||||
(const GLcharARB *) a[idx].name); | ||||
} // impl_GLSL_GetAttribLocation | ||||
static GLuint impl_GLSL_LinkProgram(MOJOSHADER_glShader *vshader, | ||||
MOJOSHADER_glShader *pshader) | ||||
{ | ||||
GLint ok = 0; | ||||
if (ctx->have_opengl_2) | ||||
{ | ||||
const GLuint program = ctx->glCreateProgram(); | ||||
if (vshader != NULL) ctx->glAttachShader(program, vshader->handle); | ||||
if (pshader != NULL) ctx->glAttachShader(program, pshader->handle); | ||||
ctx->glLinkProgram(program); | ||||
ctx->glGetProgramiv(program, GL_LINK_STATUS, &ok); | ||||
if (!ok) | ||||
{ | ||||
GLsizei len = 0; | ||||
ctx->glGetProgramInfoLog(program, sizeof (error_buffer), | ||||
&len, (GLchar *) error_buffer); | ||||
ctx->glDeleteProgram(program); | ||||
return 0; | ||||
} // if | ||||
return program; | ||||
} // if | ||||
else | ||||
{ | ||||
const GLhandleARB program = ctx->glCreateProgramObjectARB(); | ||||
assert(sizeof(program) == sizeof(GLuint)); // not always true on OS X! | ||||
if (vshader != NULL) | ||||
ctx->glAttachObjectARB(program, (GLhandleARB) vshader->handle); | ||||
if (pshader != NULL) | ||||
ctx->glAttachObjectARB(program, (GLhandleARB) pshader->handle); | ||||
ctx->glLinkProgramARB(program); | ||||
ctx->glGetObjectParameterivARB(program, GL_OBJECT_LINK_STATUS_ARB, &ok); | ||||
if (!ok) | ||||
{ | ||||
GLsizei len = 0; | ||||
ctx->glGetInfoLogARB(program, sizeof (error_buffer), | ||||
&len, (GLcharARB *) error_buffer); | ||||
ctx->glDeleteObjectARB(program); | ||||
return 0; | ||||
} // if | ||||
return (GLuint) program; | ||||
} // else | ||||
} // impl_GLSL_LinkProgram | ||||
static void impl_GLSL_FinalInitProgram(MOJOSHADER_glProgram *program) | ||||
{ | ||||
program->vs_float4_loc = glsl_uniform_loc(program, "vs_uniforms_vec4"); | ||||
program->vs_int4_loc = glsl_uniform_loc(program, "vs_uniforms_ivec4"); | ||||
program->vs_bool_loc = glsl_uniform_loc(program, "vs_uniforms_bool"); | ||||
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"); | ||||
program->ps_vpos_flip_loc = glsl_uniform_loc(program, "vposFlip"); | ||||
#ifdef MOJOSHADER_FLIP_RENDERTARGET | ||||
program->vs_flip_loc = glsl_uniform_loc(program, "vpFlip"); | ||||
#endif | ||||
} // impl_GLSL_FinalInitProgram | ||||
static void impl_GLSL_UseProgram(MOJOSHADER_glProgram *program) | ||||
{ | ||||
if (ctx->have_opengl_2) | ||||
ctx->glUseProgram(program ? program->handle : 0); | ||||
else | ||||
ctx->glUseProgramObjectARB((GLhandleARB) (program ? program->handle : 0)); | ||||
} // impl_GLSL_UseProgram | ||||
static void impl_GLSL_PushConstantArray(MOJOSHADER_glProgram *program, | ||||
const MOJOSHADER_uniform *u, | ||||
const GLfloat *f) | ||||
{ | ||||
const GLint loc = glsl_uniform_loc(program, u->name); | ||||
if (loc >= 0) // not optimized out? | ||||
ctx->glUniform4fv(loc, u->array_count, f); | ||||
} // impl_GLSL_PushConstantArray | ||||
static void impl_GLSL_PushUniforms(void) | ||||
{ | ||||
const MOJOSHADER_glProgram *program = ctx->bound_program; | ||||
assert(program->uniform_count > 0); // don't call with nothing to do! | ||||
if (program->vs_float4_loc != -1) | ||||
{ | ||||
ctx->glUniform4fv(program->vs_float4_loc, | ||||
program->vs_uniforms_float4_count, | ||||
program->vs_uniforms_float4); | ||||
} // if | ||||
if (program->vs_int4_loc != -1) | ||||
{ | ||||
ctx->glUniform4iv(program->vs_int4_loc, | ||||
program->vs_uniforms_int4_count, | ||||
program->vs_uniforms_int4); | ||||
} // if | ||||
if (program->vs_bool_loc != -1) | ||||
{ | ||||
ctx->glUniform1iv(program->vs_bool_loc, | ||||
program->vs_uniforms_bool_count, | ||||
program->vs_uniforms_bool); | ||||
} // if | ||||
if (program->ps_float4_loc != -1) | ||||
{ | ||||
ctx->glUniform4fv(program->ps_float4_loc, | ||||
program->ps_uniforms_float4_count, | ||||
program->ps_uniforms_float4); | ||||
} // if | ||||
if (program->ps_int4_loc != -1) | ||||
{ | ||||
ctx->glUniform4iv(program->ps_int4_loc, | ||||
program->ps_uniforms_int4_count, | ||||
program->ps_uniforms_int4); | ||||
} // if | ||||
if (program->ps_bool_loc != -1) | ||||
{ | ||||
ctx->glUniform1iv(program->ps_bool_loc, | ||||
program->ps_uniforms_bool_count, | ||||
program->ps_uniforms_bool); | ||||
} // if | ||||
} // impl_GLSL_PushUniforms | ||||
static void impl_GLSL_PushSampler(GLint loc, GLuint sampler) | ||||
{ | ||||
ctx->glUniform1i(loc, sampler); | ||||
} // impl_GLSL_PushSampler | ||||
#endif // SUPPORT_PROFILE_GLSL || SUPPORT_PROFILE_GLSPIRV | ||||
#if SUPPORT_PROFILE_ARB1 | ||||
static inline GLenum arb1_shader_type(const MOJOSHADER_shaderType t) | ||||
{ | ||||
if (t == MOJOSHADER_TYPE_VERTEX) | ||||
return GL_VERTEX_PROGRAM_ARB; | ||||
else if (t == MOJOSHADER_TYPE_PIXEL) | ||||
return GL_FRAGMENT_PROGRAM_ARB; | ||||
// !!! FIXME: geometry shaders? | ||||
return GL_NONE; | ||||
} // arb1_shader_type | ||||
static int impl_ARB1_MustPushConstantArrays(void) { return 0; } | ||||
static int impl_ARB1_MustPushSamplers(void) { return 0; } | ||||
static int impl_ARB1_MaxUniforms(MOJOSHADER_shaderType shader_type) | ||||
{ | ||||
GLint retval = 0; | ||||
const GLenum program_type = arb1_shader_type(shader_type); | ||||
if (program_type == GL_NONE) | ||||
return -1; | ||||
ctx->glGetProgramivARB(program_type, GL_MAX_PROGRAM_PARAMETERS_ARB, &retval); | ||||
return (int) retval; // !!! FIXME: times four? | ||||
} // impl_ARB1_MaxUniforms | ||||
static int impl_ARB1_CompileShader(const MOJOSHADER_parseData *pd, GLuint *s) | ||||
{ | ||||
GLint shaderlen = (GLint) pd->output_len; | ||||
const GLenum shader_type = arb1_shader_type(pd->shader_type); | ||||
GLuint shader = 0; | ||||
ctx->glGenProgramsARB(1, &shader); | ||||
ctx->glGetError(); // flush any existing error state. | ||||
ctx->glBindProgramARB(shader_type, shader); | ||||
ctx->glProgramStringARB(shader_type, GL_PROGRAM_FORMAT_ASCII_ARB, | ||||
shaderlen, pd->output); | ||||
if (ctx->glGetError() == GL_INVALID_OPERATION) | ||||
{ | ||||
GLint pos = 0; | ||||
ctx->glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &pos); | ||||
const GLubyte *errstr = ctx->glGetString(GL_PROGRAM_ERROR_STRING_ARB); | ||||
snprintf(error_buffer, sizeof (error_buffer), | ||||
"ARB1 compile error at position %d: %s", | ||||
(int) pos, (const char *) errstr); | ||||
ctx->glBindProgramARB(shader_type, 0); | ||||
ctx->glDeleteProgramsARB(1, &shader); | ||||
*s = 0; | ||||
return 0; | ||||
} // if | ||||
*s = shader; | ||||
return 1; | ||||
} // impl_ARB1_CompileShader | ||||
static void impl_ARB1_DeleteShader(const GLuint _shader) | ||||
{ | ||||
GLuint shader = _shader; // const removal. | ||||
ctx->glDeleteProgramsARB(1, &shader); | ||||
} // impl_ARB1_DeleteShader | ||||
static void impl_ARB1_DeleteProgram(const GLuint program) | ||||
{ | ||||
// no-op. ARB1 doesn't have real linked programs. | ||||
} // impl_ARB1_DeleteProgram | ||||
static GLint impl_ARB1_GetUniformLocation(MOJOSHADER_glProgram *program, | ||||
MOJOSHADER_glShader *shader, int idx) | ||||
{ | ||||
return 0; // no-op, we push this as one big-ass array now. | ||||
} // impl_ARB1_GetUniformLocation | ||||
static GLint impl_ARB1_GetSamplerLocation(MOJOSHADER_glProgram *program, | ||||
MOJOSHADER_glShader *shader, int idx) | ||||
{ | ||||
return shader->parseData->samplers[idx].index; | ||||
} // impl_ARB1_GetSamplerLocation | ||||
static GLint impl_ARB1_GetAttribLocation(MOJOSHADER_glProgram *program, int idx) | ||||
{ | ||||
return idx; // map to vertex arrays in the same order as the parseData. | ||||
} // impl_ARB1_GetAttribLocation | ||||
static GLuint impl_ARB1_LinkProgram(MOJOSHADER_glShader *vshader, | ||||
MOJOSHADER_glShader *pshader) | ||||
{ | ||||
// there is no formal linking in ARB1...just return a unique value. | ||||
static GLuint retval = 1; | ||||
return retval++; | ||||
} // impl_ARB1_LinkProgram | ||||
static void impl_ARB1_FinalInitProgram(MOJOSHADER_glProgram *program) | ||||
{ | ||||
// no-op. | ||||
} // impl_ARB1_FinalInitProgram | ||||
static void impl_ARB1_UseProgram(MOJOSHADER_glProgram *program) | ||||
{ | ||||
GLuint vhandle = 0; | ||||
GLuint phandle = 0; | ||||
if (program != NULL) | ||||
{ | ||||
if (program->vertex != NULL) | ||||
vhandle = program->vertex->handle; | ||||
if (program->fragment != NULL) | ||||
phandle = program->fragment->handle; | ||||
} // if | ||||
toggle_gl_state(GL_VERTEX_PROGRAM_ARB, vhandle != 0); | ||||
toggle_gl_state(GL_FRAGMENT_PROGRAM_ARB, phandle != 0); | ||||
ctx->glBindProgramARB(GL_VERTEX_PROGRAM_ARB, vhandle); | ||||
ctx->glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, phandle); | ||||
} // impl_ARB1_UseProgram | ||||
static void impl_ARB1_PushConstantArray(MOJOSHADER_glProgram *program, | ||||
const MOJOSHADER_uniform *u, | ||||
const GLfloat *f) | ||||
{ | ||||
// no-op. Constant arrays are defined in source code for arb1. | ||||
} // impl_ARB1_PushConstantArray | ||||
static void impl_ARB1_PushUniforms(void) | ||||
{ | ||||
// vertex shader uniforms come first in program->uniforms array. | ||||
MOJOSHADER_shaderType shader_type = MOJOSHADER_TYPE_VERTEX; | ||||
GLenum arb_shader_type = arb1_shader_type(shader_type); | ||||
const MOJOSHADER_glProgram *program = ctx->bound_program; | ||||
const uint32 count = program->uniform_count; | ||||
const GLfloat *srcf = program->vs_uniforms_float4; | ||||
const GLint *srci = program->vs_uniforms_int4; | ||||
const GLint *srcb = program->vs_uniforms_bool; | ||||
GLint loc = 0; | ||||
GLint texbem_loc = 0; | ||||
uint32 i; | ||||
assert(count > 0); // shouldn't call this with nothing to do! | ||||
for (i = 0; i < count; i++) | ||||
{ | ||||
UniformMap *map = &program->uniforms[i]; | ||||
const MOJOSHADER_shaderType uniform_shader_type = map->shader_type; | ||||
const MOJOSHADER_uniform *u = map->uniform; | ||||
const MOJOSHADER_uniformType type = u->type; | ||||
const int size = u->array_count ? u->array_count : 1; | ||||
assert(!u->constant); | ||||
// Did we switch from vertex to pixel (to geometry, etc)? | ||||
if (shader_type != uniform_shader_type) | ||||
{ | ||||
if (shader_type == MOJOSHADER_TYPE_PIXEL) | ||||
texbem_loc = loc; | ||||
// we start with vertex, move to pixel, then to geometry, etc. | ||||
// The array should always be sorted as such. | ||||
if (uniform_shader_type == MOJOSHADER_TYPE_PIXEL) | ||||
{ | ||||
assert(shader_type == MOJOSHADER_TYPE_VERTEX); | ||||
srcf = program->ps_uniforms_float4; | ||||
srci = program->ps_uniforms_int4; | ||||
srcb = program->ps_uniforms_bool; | ||||
loc = 0; | ||||
} // if | ||||
else | ||||
{ | ||||
// These should be ordered vertex, then pixel, then geometry. | ||||
assert(0 && "Unexpected shader type"); | ||||
} // else | ||||
shader_type = uniform_shader_type; | ||||
arb_shader_type = arb1_shader_type(uniform_shader_type); | ||||
} // if | ||||
if (type == MOJOSHADER_UNIFORM_FLOAT) | ||||
{ | ||||
int i; | ||||
for (i = 0; i < size; i++, srcf += 4, loc++) | ||||
ctx->glProgramLocalParameter4fvARB(arb_shader_type, loc, srcf); | ||||
} // if | ||||
else if (type == MOJOSHADER_UNIFORM_INT) | ||||
{ | ||||
int i; | ||||
if (ctx->have_GL_NV_gpu_program4) | ||||
{ | ||||
// GL_NV_gpu_program4 has integer uniform loading support. | ||||
for (i = 0; i < size; i++, srci += 4, loc++) | ||||
ctx->glProgramLocalParameterI4ivNV(arb_shader_type, loc, srci); | ||||
} // if | ||||
else | ||||
{ | ||||
for (i = 0; i < size; i++, srci += 4, loc++) | ||||
{ | ||||
const GLfloat fv[4] = { | ||||
(GLfloat) srci[0], (GLfloat) srci[1], | ||||
(GLfloat) srci[2], (GLfloat) srci[3] | ||||
}; | ||||
ctx->glProgramLocalParameter4fvARB(arb_shader_type, loc, fv); | ||||
} // for | ||||
} // else | ||||
} // else if | ||||
else if (type == MOJOSHADER_UNIFORM_BOOL) | ||||
{ | ||||
int i; | ||||
if (ctx->have_GL_NV_gpu_program4) | ||||
{ | ||||
// GL_NV_gpu_program4 has integer uniform loading support. | ||||
for (i = 0; i < size; i++, srcb++, loc++) | ||||
{ | ||||
const GLint ib = (GLint) ((*srcb) ? 1 : 0); | ||||
const GLint iv[4] = { ib, ib, ib, ib }; | ||||
ctx->glProgramLocalParameterI4ivNV(arb_shader_type, loc, iv); | ||||
} // for | ||||
} // if | ||||
else | ||||
{ | ||||
for (i = 0; i < size; i++, srcb++, loc++) | ||||
{ | ||||
const GLfloat fb = (GLfloat) ((*srcb) ? 1.0f : 0.0f); | ||||
const GLfloat fv[4] = { fb, fb, fb, fb }; | ||||
ctx->glProgramLocalParameter4fvARB(arb_shader_type, loc, fv); | ||||
} // for | ||||
} // else | ||||
} // else if | ||||
} // for | ||||
if (program->texbem_count) | ||||
{ | ||||
const GLenum target = GL_FRAGMENT_PROGRAM_ARB; | ||||
GLfloat *srcf = program->ps_uniforms_float4; | ||||
srcf += (program->ps_uniforms_float4_count * 4) - | ||||
(program->texbem_count * 8); | ||||
loc = texbem_loc; | ||||
for (i = 0; i < program->texbem_count; i++, srcf += 8) | ||||
{ | ||||
ctx->glProgramLocalParameter4fvARB(target, loc++, srcf); | ||||
ctx->glProgramLocalParameter4fvARB(target, loc++, srcf + 4); | ||||
} // for | ||||
} // if | ||||
} // impl_ARB1_PushUniforms | ||||
static void impl_ARB1_PushSampler(GLint loc, GLuint sampler) | ||||
{ | ||||
// no-op in this profile...arb1 uses the texture units as-is. | ||||
assert(loc == (GLint) sampler); | ||||
} // impl_ARB1_PushSampler | ||||
#endif // SUPPORT_PROFILE_ARB1 | ||||
#if SUPPORT_PROFILE_GLSL || SUPPORT_PROFILE_ARB1 | ||||
static void impl_REAL_ToggleProgramPointSize(int enable) | ||||
{ | ||||
toggle_gl_state(GL_PROGRAM_POINT_SIZE, enable); | ||||
} // impl_REAL_ToggleProgramPointSize | ||||
static void impl_NOOP_ToggleProgramPointSize(int enable) | ||||
{ | ||||
// No-op, this profile's GL context forces this to always be on | ||||
} // impl_NOOP_ToggleProgramPointSize | ||||
#endif // SUPPORT_PROFILE_GLSL || SUPPORT_PROFILE_ARB1 | ||||
const char *MOJOSHADER_glGetError(void) | ||||
{ | ||||
return error_buffer; | ||||
} // MOJOSHADER_glGetError | ||||
static void *loadsym(MOJOSHADER_glGetProcAddress lookup, void *d, | ||||
const char *fn, int *ext) | ||||
{ | ||||
void *retval = NULL; | ||||
if (lookup != NULL) | ||||
retval = lookup(fn, d); | ||||
if (retval == NULL) | ||||
*ext = 0; | ||||
return retval; | ||||
} // loadsym | ||||
static void lookup_entry_points(MOJOSHADER_glGetProcAddress lookup, void *d) | ||||
{ | ||||
#define DO_LOOKUP(ext, typ, fn) { \ | ||||
ctx->fn = (typ) loadsym(lookup, d, #fn, &ctx->have_##ext); \ | ||||
} | ||||
DO_LOOKUP(core_opengl, PFNGLGETSTRINGPROC, glGetString); | ||||
DO_LOOKUP(core_opengl, PFNGLGETERRORPROC, glGetError); | ||||
DO_LOOKUP(core_opengl, PFNGLGETINTEGERVPROC, glGetIntegerv); | ||||
DO_LOOKUP(core_opengl, PFNGLENABLEPROC, glEnable); | ||||
DO_LOOKUP(core_opengl, PFNGLDISABLEPROC, glDisable); | ||||
DO_LOOKUP(opengl_3, PFNGLGETSTRINGIPROC, glGetStringi); | ||||
DO_LOOKUP(opengl_2, PFNGLDELETESHADERPROC, glDeleteShader); | ||||
DO_LOOKUP(opengl_2, PFNGLDELETEPROGRAMPROC, glDeleteProgram); | ||||
DO_LOOKUP(opengl_2, PFNGLATTACHSHADERPROC, glAttachShader); | ||||
DO_LOOKUP(opengl_2, PFNGLCOMPILESHADERPROC, glCompileShader); | ||||
DO_LOOKUP(opengl_2, PFNGLCREATESHADERPROC, glCreateShader); | ||||
DO_LOOKUP(opengl_2, PFNGLCREATEPROGRAMPROC, glCreateProgram); | ||||
DO_LOOKUP(opengl_2, PFNGLDISABLEVERTEXATTRIBARRAYPROC, glDisableVertexAttribArray); | ||||
DO_LOOKUP(opengl_2, PFNGLENABLEVERTEXATTRIBARRAYPROC, glEnableVertexAttribArray); | ||||
DO_LOOKUP(opengl_2, PFNGLGETATTRIBLOCATIONPROC, glGetAttribLocation); | ||||
DO_LOOKUP(opengl_2, PFNGLGETPROGRAMINFOLOGPROC, glGetProgramInfoLog); | ||||
DO_LOOKUP(opengl_2, PFNGLGETSHADERINFOLOGPROC, glGetShaderInfoLog); | ||||
DO_LOOKUP(opengl_2, PFNGLGETSHADERIVPROC, glGetShaderiv); | ||||
DO_LOOKUP(opengl_2, PFNGLGETPROGRAMIVPROC, glGetProgramiv); | ||||
DO_LOOKUP(opengl_2, PFNGLGETUNIFORMLOCATIONPROC, glGetUniformLocation); | ||||
DO_LOOKUP(opengl_2, PFNGLLINKPROGRAMPROC, glLinkProgram); | ||||
DO_LOOKUP(opengl_2, PFNGLSHADERSOURCEPROC, glShaderSource); | ||||
DO_LOOKUP(opengl_2, PFNGLUNIFORM1IPROC, glUniform1i); | ||||
DO_LOOKUP(opengl_2, PFNGLUNIFORM1IVPROC, glUniform1iv); | ||||
DO_LOOKUP(opengl_2, PFNGLUNIFORM2FPROC, glUniform2f); | ||||
#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); | ||||
DO_LOOKUP(opengl_2, PFNGLVERTEXATTRIBPOINTERPROC, glVertexAttribPointer); | ||||
DO_LOOKUP(GL_ARB_shader_objects, PFNGLDELETEOBJECTARBPROC, glDeleteObjectARB); | ||||
DO_LOOKUP(GL_ARB_shader_objects, PFNGLATTACHOBJECTARBPROC, glAttachObjectARB); | ||||
DO_LOOKUP(GL_ARB_shader_objects, PFNGLCOMPILESHADERARBPROC, glCompileShaderARB); | ||||
DO_LOOKUP(GL_ARB_shader_objects, PFNGLCREATEPROGRAMOBJECTARBPROC, glCreateProgramObjectARB); | ||||
DO_LOOKUP(GL_ARB_shader_objects, PFNGLCREATESHADEROBJECTARBPROC, glCreateShaderObjectARB); | ||||
DO_LOOKUP(GL_ARB_shader_objects, PFNGLGETINFOLOGARBPROC, glGetInfoLogARB); | ||||
DO_LOOKUP(GL_ARB_shader_objects, PFNGLGETOBJECTPARAMETERIVARBPROC, glGetObjectParameterivARB); | ||||
DO_LOOKUP(GL_ARB_shader_objects, PFNGLGETUNIFORMLOCATIONARBPROC, glGetUniformLocationARB); | ||||
DO_LOOKUP(GL_ARB_shader_objects, PFNGLLINKPROGRAMARBPROC, glLinkProgramARB); | ||||
DO_LOOKUP(GL_ARB_shader_objects, PFNGLSHADERSOURCEARBPROC, glShaderSourceARB); | ||||
DO_LOOKUP(GL_ARB_shader_objects, PFNGLUNIFORM1IARBPROC, glUniform1iARB); | ||||
DO_LOOKUP(GL_ARB_shader_objects, PFNGLUNIFORM1IVARBPROC, glUniform1ivARB); | ||||
DO_LOOKUP(GL_ARB_shader_objects, PFNGLUNIFORM4FVARBPROC, glUniform4fvARB); | ||||
DO_LOOKUP(GL_ARB_shader_objects, PFNGLUNIFORM4IVARBPROC, glUniform4ivARB); | ||||
DO_LOOKUP(GL_ARB_shader_objects, PFNGLUSEPROGRAMOBJECTARBPROC, glUseProgramObjectARB); | ||||
DO_LOOKUP(GL_ARB_vertex_shader, PFNGLDISABLEVERTEXATTRIBARRAYARBPROC, glDisableVertexAttribArrayARB); | ||||
DO_LOOKUP(GL_ARB_vertex_shader, PFNGLENABLEVERTEXATTRIBARRAYARBPROC, glEnableVertexAttribArrayARB); | ||||
DO_LOOKUP(GL_ARB_vertex_shader, PFNGLGETATTRIBLOCATIONARBPROC, glGetAttribLocationARB); | ||||
DO_LOOKUP(GL_ARB_vertex_shader, PFNGLVERTEXATTRIBPOINTERARBPROC, glVertexAttribPointerARB); | ||||
DO_LOOKUP(GL_ARB_vertex_program, PFNGLVERTEXATTRIBPOINTERARBPROC, glVertexAttribPointerARB); | ||||
DO_LOOKUP(GL_ARB_vertex_program, PFNGLGETPROGRAMIVARBPROC, glGetProgramivARB); | ||||
DO_LOOKUP(GL_ARB_vertex_program, PFNGLPROGRAMLOCALPARAMETER4FVARBPROC, glProgramLocalParameter4fvARB); | ||||
DO_LOOKUP(GL_ARB_vertex_program, PFNGLDELETEPROGRAMSARBPROC, glDeleteProgramsARB); | ||||
DO_LOOKUP(GL_ARB_vertex_program, PFNGLGENPROGRAMSARBPROC, glGenProgramsARB); | ||||
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); | ||||
DO_LOOKUP(GL_ARB_ES2_compatibility, PFNGLSHADERBINARYPROC, glShaderBinary); | ||||
DO_LOOKUP(GL_ARB_gl_spirv, PFNGLSPECIALIZESHADERARBPROC, glSpecializeShaderARB); | ||||
#undef DO_LOOKUP | ||||
} // lookup_entry_points | ||||
static inline int opengl_version_atleast(const int major, const int minor) | ||||
{ | ||||
return ( ((ctx->opengl_major << 16) | (ctx->opengl_minor & 0xFFFF)) >= | ||||
((major << 16) | (minor & 0xFFFF)) ); | ||||
} // opengl_version_atleast | ||||
static int verify_extension(const char *ext, int have, StringCache *exts, | ||||
int major, int minor) | ||||
{ | ||||
if (have == 0) | ||||
return 0; // don't bother checking, we're missing an entry point. | ||||
else if (!ctx->have_core_opengl) | ||||
return 0; // don't bother checking, we're missing basic functionality. | ||||
// See if it's in the spec for this GL implementation's version. | ||||
if ((major > 0) && (opengl_version_atleast(major, minor))) | ||||
return 1; | ||||
// Not available in the GL version, check the extension list. | ||||
return stringcache_iscached(exts, ext); | ||||
} // verify_extension | ||||
static void parse_opengl_version_str(const char *verstr, int *maj, int *min) | ||||
{ | ||||
if (verstr == NULL) | ||||
*maj = *min = 0; | ||||
else | ||||
sscanf(verstr, "%d.%d", maj, min); | ||||
} // parse_opengl_version_str | ||||
#if SUPPORT_PROFILE_GLSL | ||||
static inline int glsl_version_atleast(const int major, const int minor) | ||||
{ | ||||
return ( ((ctx->glsl_major << 16) | (ctx->glsl_minor & 0xFFFF)) >= | ||||
((major << 16) | (minor & 0xFFFF)) ); | ||||
} // glsl_version_atleast | ||||
#endif | ||||
static void detect_glsl_version(void) | ||||
{ | ||||
ctx->glsl_major = ctx->glsl_minor = 0; | ||||
#if SUPPORT_PROFILE_GLSL | ||||
if (!ctx->have_core_opengl) | ||||
return; // everything's busted, give up. | ||||
#if PLATFORM_MACOSX | ||||
// If running on Mac OS X <= 10.4, don't ever use GLSL, even if | ||||
// the system claims it is available. | ||||
if (!macosx_version_atleast(10, 5, 0)) | ||||
return; | ||||
#endif | ||||
if ( ctx->have_opengl_2 || | ||||
( ctx->have_GL_ARB_shader_objects && | ||||
ctx->have_GL_ARB_vertex_shader && | ||||
ctx->have_GL_ARB_fragment_shader && | ||||
ctx->have_GL_ARB_shading_language_100 ) ) | ||||
{ | ||||
// the GL2.0 and ARB enum is the same value. | ||||
const GLenum enumval = GL_SHADING_LANGUAGE_VERSION; | ||||
ctx->glGetError(); // flush any existing error state. | ||||
const char *str = (const char *) ctx->glGetString(enumval); | ||||
if (ctx->glGetError() == GL_INVALID_ENUM) | ||||
str = NULL; | ||||
if (strstr(str, "OpenGL ES GLSL ")) | ||||
str += 15; | ||||
if (strstr(str, "ES ")) | ||||
str += 3; | ||||
parse_opengl_version_str(str, &ctx->glsl_major, &ctx->glsl_minor); | ||||
} // if | ||||
#endif | ||||
} // detect_glsl_version | ||||
static int iswhitespace(const char ch) | ||||
{ | ||||
switch (ch) | ||||
{ | ||||
case ' ': case '\t': case '\r': case '\n': return 1; | ||||
default: return 0; | ||||
} // switch | ||||
} // iswhitespace | ||||
static void load_extensions(MOJOSHADER_glGetProcAddress lookup, void *d) | ||||
{ | ||||
StringCache *exts = stringcache_create(ctx->malloc_fn, ctx->free_fn, ctx->malloc_data); | ||||
if (!exts) | ||||
{ | ||||
out_of_memory(); | ||||
return; | ||||
} // if | ||||
ctx->have_core_opengl = 1; | ||||
ctx->have_opengl_2 = 1; | ||||
ctx->have_opengl_3 = 1; | ||||
ctx->have_GL_ARB_vertex_program = 1; | ||||
ctx->have_GL_ARB_fragment_program = 1; | ||||
ctx->have_GL_NV_vertex_program2_option = 1; | ||||
ctx->have_GL_NV_fragment_program2 = 1; | ||||
ctx->have_GL_NV_vertex_program3 = 1; | ||||
ctx->have_GL_NV_gpu_program4 = 1; | ||||
ctx->have_GL_ARB_shader_objects = 1; | ||||
ctx->have_GL_ARB_vertex_shader = 1; | ||||
ctx->have_GL_ARB_fragment_shader = 1; | ||||
ctx->have_GL_ARB_shading_language_100 = 1; | ||||
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; | ||||
ctx->have_GL_ARB_ES2_compatibility = 1; | ||||
ctx->have_GL_ARB_gl_spirv = 1; | ||||
lookup_entry_points(lookup, d); | ||||
if (!ctx->have_core_opengl) | ||||
set_error("missing basic OpenGL entry points"); | ||||
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))) | ||||
{ | ||||
GLint i; | ||||
GLint num_exts = 0; | ||||
ctx->glGetIntegerv(GL_NUM_EXTENSIONS, &num_exts); | ||||
for (i = 0; i < num_exts; i++) | ||||
{ | ||||
if (!stringcache(exts, (const char *) ctx->glGetStringi(GL_EXTENSIONS, i))) | ||||
out_of_memory(); | ||||
} // for | ||||
} // if | ||||
else | ||||
{ | ||||
const char *str = (const char *) ctx->glGetString(GL_EXTENSIONS); | ||||
const char *ext; | ||||
ctx->have_opengl_3 = 0; | ||||
while (*str && iswhitespace(*str)) | ||||
str++; | ||||
ext = str; | ||||
while (1) | ||||
{ | ||||
const char ch = *str; | ||||
if (ch && (!iswhitespace(ch))) | ||||
{ | ||||
str++; | ||||
continue; | ||||
} // else if | ||||
if (str != ext) | ||||
{ | ||||
if (!stringcache_len(exts, ext, (unsigned int) (str - ext))) | ||||
{ | ||||
out_of_memory(); | ||||
break; | ||||
} // if | ||||
} // if | ||||
if (ch == '\0') | ||||
break; | ||||
str++; | ||||
while (*str && iswhitespace(*str)) | ||||
str++; | ||||
ext = str; | ||||
} // while | ||||
} // else | ||||
} // else | ||||
if ((ctx->have_opengl_2) && (!opengl_version_atleast(2, 0))) | ||||
{ | ||||
ctx->have_opengl_2 = 0; // Not GL2! must have the ARB extensions! | ||||
// Force compatible ARB function pointers in...this keeps the code | ||||
// cleaner when they are identical, so we don't have to if/else | ||||
// every function call, but we definitely have the right entry | ||||
// point. Be careful what you add here. | ||||
// These may be NULL, btw. | ||||
ctx->glUniform1i = ctx->glUniform1iARB; | ||||
ctx->glUniform4fv = ctx->glUniform4fvARB; | ||||
ctx->glUniform4iv = ctx->glUniform4ivARB; | ||||
ctx->glDisableVertexAttribArray = ctx->glDisableVertexAttribArrayARB; | ||||
ctx->glEnableVertexAttribArray = ctx->glEnableVertexAttribArrayARB; | ||||
ctx->glVertexAttribPointer = ctx->glVertexAttribPointerARB; | ||||
} // if | ||||
#define VERIFY_EXT(ext, major, minor) \ | ||||
ctx->have_##ext = verify_extension(#ext, ctx->have_##ext, exts, major, minor) | ||||
VERIFY_EXT(GL_ARB_vertex_program, -1, -1); | ||||
VERIFY_EXT(GL_ARB_fragment_program, -1, -1); | ||||
VERIFY_EXT(GL_ARB_shader_objects, -1, -1); | ||||
VERIFY_EXT(GL_ARB_vertex_shader, -1, -1); | ||||
VERIFY_EXT(GL_ARB_fragment_shader, -1, -1); | ||||
VERIFY_EXT(GL_ARB_shading_language_100, -1, -1); | ||||
VERIFY_EXT(GL_NV_vertex_program2_option, -1, -1); | ||||
VERIFY_EXT(GL_NV_fragment_program2, -1, -1); | ||||
VERIFY_EXT(GL_NV_vertex_program3, -1, -1); | ||||
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); | ||||
VERIFY_EXT(GL_ARB_ES2_compatibility, 4, 1); | ||||
VERIFY_EXT(GL_ARB_gl_spirv, -1, -1); | ||||
#undef VERIFY_EXT | ||||
stringcache_destroy(exts); | ||||
detect_glsl_version(); | ||||
} // load_extensions | ||||
static int valid_profile(const char *profile) | ||||
{ | ||||
if (!ctx->have_core_opengl) | ||||
return 0; | ||||
#define MUST_HAVE(p, x) \ | ||||
if (!ctx->have_##x) { set_error(#p " profile needs " #x); return 0; } | ||||
// we might actually _have_ maj.min, but forcibly disabled GLSL elsewhere. | ||||
#define MUST_HAVE_GLSL(p, maj, min) \ | ||||
if (!glsl_version_atleast(maj, min)) { \ | ||||
set_error(#p " profile needs missing GLSL support"); return 0; \ | ||||
} | ||||
if (profile == NULL) | ||||
{ | ||||
set_error("NULL profile"); | ||||
return 0; | ||||
} // if | ||||
#if SUPPORT_PROFILE_ARB1 | ||||
else if (strcmp(profile, MOJOSHADER_PROFILE_ARB1) == 0) | ||||
{ | ||||
MUST_HAVE(MOJOSHADER_PROFILE_ARB1, GL_ARB_vertex_program); | ||||
MUST_HAVE(MOJOSHADER_PROFILE_ARB1, GL_ARB_fragment_program); | ||||
} // else if | ||||
#endif | ||||
#if SUPPORT_PROFILE_ARB1_NV | ||||
else if (strcmp(profile, MOJOSHADER_PROFILE_NV2) == 0) | ||||
{ | ||||
MUST_HAVE(MOJOSHADER_PROFILE_NV2, GL_ARB_vertex_program); | ||||
MUST_HAVE(MOJOSHADER_PROFILE_NV2, GL_ARB_fragment_program); | ||||
MUST_HAVE(MOJOSHADER_PROFILE_NV2, GL_NV_vertex_program2_option); | ||||
MUST_HAVE(MOJOSHADER_PROFILE_NV2, GL_NV_fragment_program2); | ||||
} // else if | ||||
else if (strcmp(profile, MOJOSHADER_PROFILE_NV3) == 0) | ||||
{ | ||||
MUST_HAVE(MOJOSHADER_PROFILE_NV3, GL_ARB_vertex_program); | ||||
MUST_HAVE(MOJOSHADER_PROFILE_NV3, GL_ARB_fragment_program); | ||||
MUST_HAVE(MOJOSHADER_PROFILE_NV3, GL_NV_vertex_program3); | ||||
MUST_HAVE(MOJOSHADER_PROFILE_NV3, GL_NV_fragment_program2); | ||||
} // else if | ||||
else if (strcmp(profile, MOJOSHADER_PROFILE_NV4) == 0) | ||||
{ | ||||
MUST_HAVE(MOJOSHADER_PROFILE_NV4, GL_NV_gpu_program4); | ||||
} // else if | ||||
#endif | ||||
#if SUPPORT_PROFILE_GLSPIRV | ||||
else if (strcmp(profile, MOJOSHADER_PROFILE_GLSPIRV) == 0) | ||||
{ | ||||
MUST_HAVE(MOJOSHADER_PROFILE_GLSPIRV, GL_ARB_ES2_compatibility); | ||||
MUST_HAVE(MOJOSHADER_PROFILE_GLSPIRV, GL_ARB_gl_spirv); | ||||
} // else if | ||||
#endif | ||||
#if SUPPORT_PROFILE_GLSLES | ||||
else if (strcmp(profile, MOJOSHADER_PROFILE_GLSLES) == 0) | ||||
{ | ||||
MUST_HAVE_GLSL(MOJOSHADER_PROFILE_GLSLES, 1, 00); | ||||
} // else if | ||||
#endif | ||||
#if SUPPORT_PROFILE_GLSL120 | ||||
else if (strcmp(profile, MOJOSHADER_PROFILE_GLSL120) == 0) | ||||
{ | ||||
MUST_HAVE_GLSL(MOJOSHADER_PROFILE_GLSL120, 1, 20); | ||||
} // else if | ||||
#endif | ||||
#if SUPPORT_PROFILE_GLSL | ||||
else if (strcmp(profile, MOJOSHADER_PROFILE_GLSL) == 0) | ||||
{ | ||||
MUST_HAVE_GLSL(MOJOSHADER_PROFILE_GLSL, 1, 10); | ||||
} // else if | ||||
#endif | ||||
else | ||||
{ | ||||
set_error("unknown or unsupported profile"); | ||||
return 0; | ||||
} // else | ||||
#undef MUST_HAVE | ||||
return 1; | ||||
} // valid_profile | ||||
static const char *profile_priorities[] = { | ||||
#if SUPPORT_PROFILE_GLSPIRV | ||||
MOJOSHADER_PROFILE_GLSPIRV, | ||||
#endif | ||||
#if SUPPORT_PROFILE_GLSL120 | ||||
MOJOSHADER_PROFILE_GLSL120, | ||||
#endif | ||||
#if SUPPORT_PROFILE_GLSL | ||||
MOJOSHADER_PROFILE_GLSL, | ||||
#endif | ||||
#if SUPPORT_PROFILE_ARB1_NV | ||||
MOJOSHADER_PROFILE_NV4, | ||||
MOJOSHADER_PROFILE_NV3, | ||||
MOJOSHADER_PROFILE_NV2, | ||||
#endif | ||||
#if SUPPORT_PROFILE_ARB1 | ||||
MOJOSHADER_PROFILE_ARB1, | ||||
#endif | ||||
}; | ||||
int MOJOSHADER_glAvailableProfiles(MOJOSHADER_glGetProcAddress lookup, | ||||
void *lookup_d, | ||||
const char **profs, const int size, | ||||
MOJOSHADER_malloc m, MOJOSHADER_free f, | ||||
void *malloc_d) | ||||
{ | ||||
int retval = 0; | ||||
MOJOSHADER_glContext _ctx; | ||||
MOJOSHADER_glContext *current_ctx = ctx; | ||||
if (m == NULL) m = MOJOSHADER_internal_malloc; | ||||
if (f == NULL) f = MOJOSHADER_internal_free; | ||||
ctx = &_ctx; | ||||
memset(ctx, '\0', sizeof (MOJOSHADER_glContext)); | ||||
ctx->malloc_fn = m; | ||||
ctx->free_fn = f; | ||||
ctx->malloc_data = malloc_d; | ||||
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; | ||||
for (i = 0; i < STATICARRAYLEN(profile_priorities); i++) | ||||
{ | ||||
const char *profile = profile_priorities[i]; | ||||
if (valid_profile(profile)) | ||||
{ | ||||
if (retval < size) | ||||
profs[retval] = profile; | ||||
retval++; | ||||
} // if | ||||
} // for | ||||
} // if | ||||
ctx = current_ctx; | ||||
return retval; | ||||
} // MOJOSHADER_glAvailableProfiles | ||||
const char *MOJOSHADER_glBestProfile(MOJOSHADER_glGetProcAddress gpa, | ||||
void *lookup_d, | ||||
MOJOSHADER_malloc m, MOJOSHADER_free f, | ||||
void *malloc_d) | ||||
{ | ||||
const char *prof[STATICARRAYLEN(profile_priorities)]; | ||||
const int avail = MOJOSHADER_glAvailableProfiles(gpa, lookup_d, prof, | ||||
STATICARRAYLEN(prof), | ||||
m, f, malloc_d); | ||||
if (avail <= 0) | ||||
{ | ||||
set_error("no profiles available"); | ||||
return NULL; | ||||
} // if | ||||
return prof[0]; // profiles are sorted "best" to "worst." | ||||
} // MOJOSHADER_glBestProfile | ||||
MOJOSHADER_glContext *MOJOSHADER_glCreateContext(const char *profile, | ||||
MOJOSHADER_glGetProcAddress lookup, | ||||
void *lookup_d, | ||||
MOJOSHADER_malloc m, MOJOSHADER_free f, | ||||
void *malloc_d) | ||||
{ | ||||
MOJOSHADER_glContext *retval = NULL; | ||||
MOJOSHADER_glContext *current_ctx = ctx; | ||||
ctx = NULL; | ||||
if (m == NULL) m = MOJOSHADER_internal_malloc; | ||||
if (f == NULL) f = MOJOSHADER_internal_free; | ||||
ctx = (MOJOSHADER_glContext *) m(sizeof (MOJOSHADER_glContext), malloc_d); | ||||
if (ctx == NULL) | ||||
{ | ||||
out_of_memory(); | ||||
goto init_fail; | ||||
} // if | ||||
memset(ctx, '\0', sizeof (MOJOSHADER_glContext)); | ||||
ctx->malloc_fn = m; | ||||
ctx->free_fn = f; | ||||
ctx->malloc_data = malloc_d; | ||||
snprintf(ctx->profile, sizeof (ctx->profile), "%s", profile); | ||||
load_extensions(lookup, lookup_d); | ||||
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_GLSPIRV here, since valid_profile() does. | ||||
#if SUPPORT_PROFILE_GLSPIRV | ||||
else if (strcmp(profile, MOJOSHADER_PROFILE_GLSPIRV) == 0) | ||||
{ | ||||
ctx->profileMaxUniforms = impl_GLSL_MaxUniforms; | ||||
ctx->profileCompileShader = impl_SPIRV_CompileShader; | ||||
ctx->profileDeleteShader = impl_SPIRV_DeleteShader; | ||||
ctx->profileDeleteProgram = impl_SPIRV_DeleteProgram; | ||||
ctx->profileGetAttribLocation = impl_SPIRV_GetAttribLocation; | ||||
ctx->profileGetUniformLocation = impl_SPIRV_GetUniformLocation; | ||||
ctx->profileGetSamplerLocation = impl_SPIRV_GetSamplerLocation; | ||||
ctx->profileLinkProgram = impl_SPIRV_LinkProgram; | ||||
ctx->profileFinalInitProgram = impl_SPIRV_FinalInitProgram; | ||||
ctx->profileUseProgram = impl_GLSL_UseProgram; | ||||
ctx->profilePushConstantArray = impl_GLSL_PushConstantArray; | ||||
ctx->profilePushUniforms = impl_GLSL_PushUniforms; | ||||
ctx->profilePushSampler = impl_GLSL_PushSampler; | ||||
ctx->profileMustPushConstantArrays = impl_GLSL_MustPushConstantArrays; | ||||
ctx->profileMustPushSamplers = impl_GLSL_MustPushSamplers; | ||||
ctx->profileToggleProgramPointSize = impl_REAL_ToggleProgramPointSize; | ||||
} // if | ||||
#endif | ||||
// 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_GLSLES) == 0) ) | ||||
{ | ||||
ctx->profileMaxUniforms = impl_GLSL_MaxUniforms; | ||||
ctx->profileCompileShader = impl_GLSL_CompileShader; | ||||
ctx->profileDeleteShader = impl_GLSL_DeleteShader; | ||||
ctx->profileDeleteProgram = impl_GLSL_DeleteProgram; | ||||
ctx->profileGetAttribLocation = impl_GLSL_GetAttribLocation; | ||||
ctx->profileGetUniformLocation = impl_GLSL_GetUniformLocation; | ||||
ctx->profileGetSamplerLocation = impl_GLSL_GetSamplerLocation; | ||||
ctx->profileLinkProgram = impl_GLSL_LinkProgram; | ||||
ctx->profileFinalInitProgram = impl_GLSL_FinalInitProgram; | ||||
ctx->profileUseProgram = impl_GLSL_UseProgram; | ||||
ctx->profilePushConstantArray = impl_GLSL_PushConstantArray; | ||||
ctx->profilePushUniforms = impl_GLSL_PushUniforms; | ||||
ctx->profilePushSampler = impl_GLSL_PushSampler; | ||||
ctx->profileMustPushConstantArrays = impl_GLSL_MustPushConstantArrays; | ||||
ctx->profileMustPushSamplers = impl_GLSL_MustPushSamplers; | ||||
if (strcmp(profile, MOJOSHADER_PROFILE_GLSLES) == 0) | ||||
ctx->profileToggleProgramPointSize = impl_NOOP_ToggleProgramPointSize; | ||||
else | ||||
ctx->profileToggleProgramPointSize = impl_REAL_ToggleProgramPointSize; | ||||
} // if | ||||
#endif | ||||
// We don't check SUPPORT_PROFILE_ARB1_NV here, since valid_profile() does. | ||||
#if SUPPORT_PROFILE_ARB1 | ||||
else if ( (strcmp(profile, MOJOSHADER_PROFILE_ARB1) == 0) || | ||||
(strcmp(profile, MOJOSHADER_PROFILE_NV2) == 0) || | ||||
(strcmp(profile, MOJOSHADER_PROFILE_NV3) == 0) || | ||||
(strcmp(profile, MOJOSHADER_PROFILE_NV4) == 0) ) | ||||
{ | ||||
ctx->profileMaxUniforms = impl_ARB1_MaxUniforms; | ||||
ctx->profileCompileShader = impl_ARB1_CompileShader; | ||||
ctx->profileDeleteShader = impl_ARB1_DeleteShader; | ||||
ctx->profileDeleteProgram = impl_ARB1_DeleteProgram; | ||||
ctx->profileGetAttribLocation = impl_ARB1_GetAttribLocation; | ||||
ctx->profileGetUniformLocation = impl_ARB1_GetUniformLocation; | ||||
ctx->profileGetSamplerLocation = impl_ARB1_GetSamplerLocation; | ||||
ctx->profileLinkProgram = impl_ARB1_LinkProgram; | ||||
ctx->profileFinalInitProgram = impl_ARB1_FinalInitProgram; | ||||
ctx->profileUseProgram = impl_ARB1_UseProgram; | ||||
ctx->profilePushConstantArray = impl_ARB1_PushConstantArray; | ||||
ctx->profilePushUniforms = impl_ARB1_PushUniforms; | ||||
ctx->profilePushSampler = impl_ARB1_PushSampler; | ||||
ctx->profileMustPushConstantArrays = impl_ARB1_MustPushConstantArrays; | ||||
ctx->profileMustPushSamplers = impl_ARB1_MustPushSamplers; | ||||
ctx->profileToggleProgramPointSize = impl_REAL_ToggleProgramPointSize; | ||||
} // if | ||||
#endif | ||||
assert(ctx->profileMaxUniforms != NULL); | ||||
assert(ctx->profileCompileShader != NULL); | ||||
assert(ctx->profileDeleteShader != NULL); | ||||
assert(ctx->profileDeleteProgram != NULL); | ||||
assert(ctx->profileMaxUniforms != NULL); | ||||
assert(ctx->profileGetAttribLocation != NULL); | ||||
assert(ctx->profileGetUniformLocation != NULL); | ||||
assert(ctx->profileGetSamplerLocation != NULL); | ||||
assert(ctx->profileLinkProgram != NULL); | ||||
assert(ctx->profileFinalInitProgram != NULL); | ||||
assert(ctx->profileUseProgram != NULL); | ||||
assert(ctx->profilePushConstantArray != NULL); | ||||
assert(ctx->profilePushUniforms != NULL); | ||||
assert(ctx->profilePushSampler != NULL); | ||||
assert(ctx->profileMustPushConstantArrays != NULL); | ||||
assert(ctx->profileMustPushSamplers != NULL); | ||||
assert(ctx->profileToggleProgramPointSize != NULL); | ||||
retval = ctx; | ||||
ctx = current_ctx; | ||||
return retval; | ||||
init_fail: | ||||
if (ctx != NULL) | ||||
f(ctx, malloc_d); | ||||
ctx = current_ctx; | ||||
return NULL; | ||||
} // MOJOSHADER_glCreateContext | ||||
void MOJOSHADER_glMakeContextCurrent(MOJOSHADER_glContext *_ctx) | ||||
{ | ||||
ctx = _ctx; | ||||
} // MOJOSHADER_glMakeContextCurrent | ||||
int MOJOSHADER_glMaxUniforms(MOJOSHADER_shaderType shader_type) | ||||
{ | ||||
return ctx->profileMaxUniforms(shader_type); | ||||
} // MOJOSHADER_glMaxUniforms | ||||
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) | ||||
{ | ||||
MOJOSHADER_glShader *retval = NULL; | ||||
GLuint shader = 0; | ||||
// This doesn't need a mainfn, since there's no GL lang that does. | ||||
const MOJOSHADER_parseData *pd = MOJOSHADER_parse(ctx->profile, NULL, | ||||
tokenbuf, bufsize, | ||||
swiz, swizcount, | ||||
smap, smapcount, | ||||
ctx->malloc_fn, | ||||
ctx->free_fn, | ||||
ctx->malloc_data); | ||||
if (pd->error_count > 0) | ||||
{ | ||||
// !!! FIXME: put multiple errors in the buffer? Don't use | ||||
// !!! FIXME: MOJOSHADER_glGetError() for this? | ||||
set_error(pd->errors[0].error); | ||||
goto compile_shader_fail; | ||||
} // if | ||||
retval = (MOJOSHADER_glShader *) Malloc(sizeof (MOJOSHADER_glShader)); | ||||
if (retval == NULL) | ||||
goto compile_shader_fail; | ||||
if (!ctx->profileCompileShader(pd, &shader)) | ||||
goto compile_shader_fail; | ||||
retval->parseData = pd; | ||||
retval->handle = shader; | ||||
retval->refcount = 1; | ||||
return retval; | ||||
compile_shader_fail: | ||||
MOJOSHADER_freeParseData(pd); | ||||
Free(retval); | ||||
if (shader != 0) | ||||
ctx->profileDeleteShader(shader); | ||||
return NULL; | ||||
} // MOJOSHADER_glCompileShader | ||||
const MOJOSHADER_parseData *MOJOSHADER_glGetShaderParseData( | ||||
MOJOSHADER_glShader *shader) | ||||
{ | ||||
return (shader != NULL) ? shader->parseData : NULL; | ||||
} // MOJOSHADER_glGetShaderParseData | ||||
static void shader_unref(MOJOSHADER_glShader *shader) | ||||
{ | ||||
if (shader != NULL) | ||||
{ | ||||
const uint32 refcount = shader->refcount; | ||||
if (refcount > 1) | ||||
shader->refcount--; | ||||
else | ||||
{ | ||||
ctx->profileDeleteShader(shader->handle); | ||||
MOJOSHADER_freeParseData(shader->parseData); | ||||
Free(shader); | ||||
} // else | ||||
} // if | ||||
} // shader_unref | ||||
static void program_unref(MOJOSHADER_glProgram *program) | ||||
{ | ||||
if (program != NULL) | ||||
{ | ||||
const uint32 refcount = program->refcount; | ||||
if (refcount > 1) | ||||
program->refcount--; | ||||
else | ||||
{ | ||||
ctx->profileDeleteProgram(program->handle); | ||||
shader_unref(program->vertex); | ||||
shader_unref(program->fragment); | ||||
Free(program->vs_uniforms_float4); | ||||
Free(program->vs_uniforms_int4); | ||||
Free(program->vs_uniforms_bool); | ||||
Free(program->ps_uniforms_float4); | ||||
Free(program->ps_uniforms_int4); | ||||
Free(program->ps_uniforms_bool); | ||||
Free(program->uniforms); | ||||
Free(program->attributes); | ||||
Free(program); | ||||
} // else | ||||
} // if | ||||
} // program_unref | ||||
static void fill_constant_array(GLfloat *f, const int base, const int size, | ||||
const MOJOSHADER_parseData *pd) | ||||
{ | ||||
int i; | ||||
int filled = 0; | ||||
for (i = 0; i < pd->constant_count; i++) | ||||
{ | ||||
const MOJOSHADER_constant *c = &pd->constants[i]; | ||||
if (c->type != MOJOSHADER_UNIFORM_FLOAT) | ||||
continue; | ||||
else if (c->index < base) | ||||
continue; | ||||
else if (c->index >= (base+size)) | ||||
continue; | ||||
memcpy(&f[(c->index-base) * 4], &c->value.f, sizeof (c->value.f)); | ||||
filled++; | ||||
} // for | ||||
assert(filled == size); | ||||
} // fill_constant_array | ||||
static int lookup_uniforms(MOJOSHADER_glProgram *program, | ||||
MOJOSHADER_glShader *shader, int *bound) | ||||
{ | ||||
const MOJOSHADER_parseData *pd = shader->parseData; | ||||
const MOJOSHADER_shaderType shader_type = pd->shader_type; | ||||
uint32 float4_count = 0; | ||||
uint32 int4_count = 0; | ||||
uint32 bool_count = 0; | ||||
int i; | ||||
for (i = 0; i < pd->uniform_count; i++) | ||||
{ | ||||
const MOJOSHADER_uniform *u = &pd->uniforms[i]; | ||||
if (u->constant) | ||||
{ | ||||
// only do constants once, at link time. These aren't changed ever. | ||||
if (ctx->profileMustPushConstantArrays()) | ||||
{ | ||||
const int base = u->index; | ||||
const int size = u->array_count; | ||||
GLfloat *f = (GLfloat *) alloca(sizeof (GLfloat) * (size * 4)); | ||||
fill_constant_array(f, base, size, pd); | ||||
if (!(*bound)) | ||||
{ | ||||
ctx->profileUseProgram(program); | ||||
*bound = 1; | ||||
} // if | ||||
ctx->profilePushConstantArray(program, u, f); | ||||
} // if | ||||
} // if | ||||
else | ||||
{ | ||||
const GLint loc = ctx->profileGetUniformLocation(program, shader, i); | ||||
if (loc != -1) // -1 means it was optimized out, or failure. | ||||
{ | ||||
const int regcount = u->array_count; | ||||
UniformMap *map = &program->uniforms[program->uniform_count]; | ||||
map->shader_type = shader_type; | ||||
map->uniform = u; | ||||
map->location = (GLuint) loc; | ||||
program->uniform_count++; | ||||
if (u->type == MOJOSHADER_UNIFORM_FLOAT) | ||||
float4_count += regcount ? regcount : 1; | ||||
else if (u->type == MOJOSHADER_UNIFORM_INT) | ||||
int4_count += regcount ? regcount : 1; | ||||
else if (u->type == MOJOSHADER_UNIFORM_BOOL) | ||||
bool_count += regcount ? regcount : 1; | ||||
else | ||||
assert(0 && "Unexpected register type"); | ||||
} // if | ||||
} // else | ||||
} // for | ||||
if (shader_type == MOJOSHADER_TYPE_PIXEL) | ||||
{ | ||||
for (i = 0; i < pd->sampler_count; i++) | ||||
{ | ||||
if (pd->samplers[i].texbem) | ||||
{ | ||||
float4_count += 2; | ||||
program->texbem_count++; | ||||
} // if | ||||
} // for | ||||
} // if | ||||
#define MAKE_ARRAY(typ, gltyp, siz, count) \ | ||||
if (count) { \ | ||||
const size_t buflen = sizeof (gltyp) * siz * count; \ | ||||
gltyp *ptr = (gltyp *) Malloc(buflen); \ | ||||
if (ptr == NULL) { \ | ||||
return 0; \ | ||||
} else if (shader_type == MOJOSHADER_TYPE_VERTEX) { \ | ||||
program->vs_uniforms_##typ = ptr; \ | ||||
program->vs_uniforms_##typ##_count = count; \ | ||||
} else if (shader_type == MOJOSHADER_TYPE_PIXEL) { \ | ||||
program->ps_uniforms_##typ = ptr; \ | ||||
program->ps_uniforms_##typ##_count = count; \ | ||||
} else { \ | ||||
assert(0 && "unsupported shader type"); \ | ||||
} \ | ||||
memset(ptr, '\0', buflen); \ | ||||
} | ||||
MAKE_ARRAY(float4, GLfloat, 4, float4_count); | ||||
MAKE_ARRAY(int4, GLint, 4, int4_count); | ||||
MAKE_ARRAY(bool, GLint, 1, bool_count); | ||||
#undef MAKE_ARRAY | ||||
return 1; | ||||
} // lookup_uniforms | ||||
static void lookup_samplers(MOJOSHADER_glProgram *program, | ||||
MOJOSHADER_glShader *shader, int *bound) | ||||
{ | ||||
const MOJOSHADER_parseData *pd = shader->parseData; | ||||
const MOJOSHADER_sampler *s = pd->samplers; | ||||
int i; | ||||
if ((pd->sampler_count == 0) || (!ctx->profileMustPushSamplers())) | ||||
return; // nothing to do here, so don't bother binding, etc. | ||||
// Link up the Samplers. These never change after link time, since they | ||||
// are meant to be constant texture unit ids and not textures. | ||||
if (!(*bound)) | ||||
{ | ||||
ctx->profileUseProgram(program); | ||||
*bound = 1; | ||||
} // if | ||||
for (i = 0; i < pd->sampler_count; i++) | ||||
{ | ||||
const GLint loc = ctx->profileGetSamplerLocation(program, shader, i); | ||||
if (loc >= 0) // maybe the Sampler was optimized out? | ||||
{ | ||||
#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 | ||||
// Right now, this just decides if we have to toggle pointsize support later. | ||||
static void lookup_outputs(MOJOSHADER_glProgram *program, | ||||
MOJOSHADER_glShader *shader) | ||||
{ | ||||
if (shader == NULL) | ||||
return; | ||||
const MOJOSHADER_parseData *pd = shader->parseData; | ||||
int i; | ||||
for (i = 0; i < pd->output_count; i++) | ||||
{ | ||||
if (pd->outputs[i].usage == MOJOSHADER_USAGE_POINTSIZE) | ||||
{ | ||||
program->uses_pointsize = 1; | ||||
break; | ||||
} // if | ||||
} // for | ||||
} // lookup_outputs | ||||
static int lookup_attributes(MOJOSHADER_glProgram *program) | ||||
{ | ||||
int i; | ||||
const MOJOSHADER_parseData *pd = program->vertex->parseData; | ||||
const MOJOSHADER_attribute *a = pd->attributes; | ||||
for (i = 0; i < pd->attribute_count; i++) | ||||
{ | ||||
const GLint loc = ctx->profileGetAttribLocation(program, i); | ||||
if (loc >= 0) // maybe the Attribute was optimized out? | ||||
{ | ||||
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)) | ||||
{ | ||||
assert(0 && "Static array is too small."); // laziness fail. | ||||
return 0; | ||||
} // if | ||||
} // if | ||||
} // for | ||||
return 1; | ||||
} // lookup_attributes | ||||
// !!! FIXME: misnamed | ||||
// build a list of indexes that need to be overwritten with constant values | ||||
// when pushing a uniform array to the GL. | ||||
static int build_constants_lists(MOJOSHADER_glProgram *program) | ||||
{ | ||||
int i; | ||||
const int count = program->uniform_count; | ||||
for (i = 0; i < count; i++) | ||||
{ | ||||
UniformMap *map = &program->uniforms[i]; | ||||
const MOJOSHADER_uniform *u = map->uniform; | ||||
const int size = u->array_count; | ||||
assert(!u->constant); | ||||
if (size == 0) | ||||
continue; // nothing to see here. | ||||
// only use arrays for 'c' registers. | ||||
assert(u->type == MOJOSHADER_UNIFORM_FLOAT); | ||||
// !!! FIXME: deal with this. | ||||
} // for | ||||
return 1; | ||||
} // build_constants_lists | ||||
MOJOSHADER_glProgram *MOJOSHADER_glLinkProgram(MOJOSHADER_glShader *vshader, | ||||
MOJOSHADER_glShader *pshader) | ||||
{ | ||||
int bound = 0; | ||||
if ((vshader == NULL) && (pshader == NULL)) | ||||
return NULL; | ||||
int numregs = 0; | ||||
MOJOSHADER_glProgram *retval = NULL; | ||||
const GLuint program = ctx->profileLinkProgram(vshader, pshader); | ||||
if (program == 0) | ||||
goto link_program_fail; | ||||
retval = (MOJOSHADER_glProgram *) Malloc(sizeof (MOJOSHADER_glProgram)); | ||||
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; | ||||
if (pshader != NULL) numregs += pshader->parseData->uniform_count; | ||||
if (numregs > 0) | ||||
{ | ||||
const size_t len = sizeof (UniformMap) * numregs; | ||||
retval->uniforms = (UniformMap *) Malloc(len); | ||||
if (retval->uniforms == NULL) | ||||
goto link_program_fail; | ||||
memset(retval->uniforms, '\0', len); | ||||
} // if | ||||
retval->handle = program; | ||||
retval->vertex = vshader; | ||||
retval->fragment = pshader; | ||||
retval->generation = ctx->generation - 1; | ||||
retval->refcount = 1; | ||||
if (vshader != NULL) | ||||
{ | ||||
if (vshader->parseData->attribute_count > 0) | ||||
{ | ||||
const int count = vshader->parseData->attribute_count; | ||||
const size_t len = sizeof (AttributeMap) * count; | ||||
retval->attributes = (AttributeMap *) Malloc(len); | ||||
if (retval->attributes == NULL) | ||||
goto link_program_fail; | ||||
memset(retval->attributes, '\0', len); | ||||
if (!lookup_attributes(retval)) | ||||
goto link_program_fail; | ||||
} // if | ||||
if (!lookup_uniforms(retval, vshader, &bound)) | ||||
goto link_program_fail; | ||||
lookup_samplers(retval, vshader, &bound); | ||||
lookup_outputs(retval, vshader); | ||||
vshader->refcount++; | ||||
} // if | ||||
if (pshader != NULL) | ||||
{ | ||||
if (!lookup_uniforms(retval, pshader, &bound)) | ||||
goto link_program_fail; | ||||
lookup_samplers(retval, pshader, &bound); | ||||
lookup_outputs(retval, pshader); | ||||
pshader->refcount++; | ||||
} // if | ||||
if (!build_constants_lists(retval)) | ||||
goto link_program_fail; | ||||
if (bound) // reset the old binding. | ||||
ctx->profileUseProgram(ctx->bound_program); | ||||
ctx->profileFinalInitProgram(retval); | ||||
return retval; | ||||
link_program_fail: | ||||
if (retval != NULL) | ||||
{ | ||||
Free(retval->vs_uniforms_float4); | ||||
Free(retval->vs_uniforms_int4); | ||||
Free(retval->vs_uniforms_bool); | ||||
Free(retval->ps_uniforms_float4); | ||||
Free(retval->ps_uniforms_int4); | ||||
Free(retval->ps_uniforms_bool); | ||||
Free(retval->uniforms); | ||||
Free(retval->attributes); | ||||
Free(retval); | ||||
} // if | ||||
if (program != 0) | ||||
ctx->profileDeleteProgram(program); | ||||
if (bound) | ||||
ctx->profileUseProgram(ctx->bound_program); | ||||
return NULL; | ||||
} // MOJOSHADER_glLinkProgram | ||||
static void update_enabled_arrays(void) | ||||
{ | ||||
int highest_enabled = 0; | ||||
int i; | ||||
// Enable/disable vertex arrays to match our needs. | ||||
// this happens to work in both ARB1 and GLSL, but if something alien | ||||
// shows up, we'll have to split these into profile*() functions. | ||||
for (i = 0; i < ctx->max_attrs; i++) | ||||
{ | ||||
const int want = (const int) ctx->want_attr[i]; | ||||
const int have = (const int) ctx->have_attr[i]; | ||||
if (want != have) | ||||
{ | ||||
if (want) | ||||
ctx->glEnableVertexAttribArray(i); | ||||
else | ||||
ctx->glDisableVertexAttribArray(i); | ||||
ctx->have_attr[i] = want; | ||||
} // if | ||||
if (want) | ||||
highest_enabled = i + 1; | ||||
} // for | ||||
ctx->max_attrs = highest_enabled; // trim unneeded iterations next time. | ||||
} // update_enabled_arrays | ||||
void MOJOSHADER_glBindProgram(MOJOSHADER_glProgram *program) | ||||
{ | ||||
if (program == ctx->bound_program) | ||||
return; // nothing to do. | ||||
if (program != NULL) | ||||
program->refcount++; | ||||
memset(ctx->want_attr, '\0', sizeof (ctx->want_attr[0]) * ctx->max_attrs); | ||||
// If no program bound, disable all arrays, in case we're switching to | ||||
// fixed function pipeline. Otherwise, we try to minimize state changes | ||||
// by toggling just the changed set of needed arrays in ProgramReady(). | ||||
if (program == NULL) | ||||
update_enabled_arrays(); | ||||
ctx->profileUseProgram(program); | ||||
program_unref(ctx->bound_program); | ||||
ctx->bound_program = program; | ||||
} // MOJOSHADER_glBindProgram | ||||
typedef struct | ||||
{ | ||||
MOJOSHADER_glShader *vertex; | ||||
MOJOSHADER_glShader *fragment; | ||||
} BoundShaders; | ||||
static uint32 hash_shaders(const void *sym, void *data) | ||||
{ | ||||
(void) data; | ||||
const BoundShaders *s = (const BoundShaders *) sym; | ||||
const uint32 v = (s->vertex) ? (uint32) s->vertex->handle : 0; | ||||
const uint32 f = (s->fragment) ? (uint32) s->fragment->handle : 0; | ||||
return ((v & 0xFFFF) << 16) | (f & 0xFFFF); | ||||
} // hash_shaders | ||||
static int match_shaders(const void *_a, const void *_b, void *data) | ||||
{ | ||||
(void) data; | ||||
const BoundShaders *a = (const BoundShaders *) _a; | ||||
const BoundShaders *b = (const BoundShaders *) _b; | ||||
const GLuint av = (a->vertex) ? a->vertex->handle : 0; | ||||
const GLuint bv = (b->vertex) ? b->vertex->handle : 0; | ||||
if (av != bv) | ||||
return 0; | ||||
const GLuint af = (a->fragment) ? a->fragment->handle : 0; | ||||
const GLuint bf = (b->fragment) ? b->fragment->handle : 0; | ||||
if (af != bf) | ||||
return 0; | ||||
return 1; | ||||
} // match_shaders | ||||
static void nuke_shaders(const void *key, const void *value, void *data) | ||||
{ | ||||
(void) data; | ||||
Free((void *) key); // this was a BoundShaders struct. | ||||
MOJOSHADER_glDeleteProgram((MOJOSHADER_glProgram *) value); | ||||
} // nuke_shaders | ||||
void MOJOSHADER_glBindShaders(MOJOSHADER_glShader *v, MOJOSHADER_glShader *p) | ||||
{ | ||||
if ((v == NULL) && (p == NULL)) | ||||
{ | ||||
MOJOSHADER_glBindProgram(NULL); | ||||
return; | ||||
} // if | ||||
// !!! FIXME: eventually support GL_EXT_separate_shader_objects. | ||||
if (ctx->linker_cache == NULL) | ||||
{ | ||||
ctx->linker_cache = hash_create(NULL, hash_shaders, match_shaders, | ||||
nuke_shaders, 0, ctx->malloc_fn, | ||||
ctx->free_fn, ctx->malloc_data); | ||||
if (ctx->linker_cache == NULL) | ||||
{ | ||||
out_of_memory(); | ||||
return; | ||||
} // if | ||||
} // if | ||||
MOJOSHADER_glProgram *program = NULL; | ||||
BoundShaders shaders; | ||||
shaders.vertex = v; | ||||
shaders.fragment = p; | ||||
const void *val = NULL; | ||||
if (hash_find(ctx->linker_cache, &shaders, &val)) | ||||
program = (MOJOSHADER_glProgram *) val; | ||||
else | ||||
{ | ||||
program = MOJOSHADER_glLinkProgram(v, p); | ||||
if (program == NULL) | ||||
return; | ||||
BoundShaders *item = (BoundShaders *) Malloc(sizeof (BoundShaders)); | ||||
if (item == NULL) | ||||
{ | ||||
MOJOSHADER_glDeleteProgram(program); | ||||
return; | ||||
} // if | ||||
memcpy(item, &shaders, sizeof (BoundShaders)); | ||||
if (hash_insert(ctx->linker_cache, item, program) != 1) | ||||
{ | ||||
Free(item); | ||||
MOJOSHADER_glDeleteProgram(program); | ||||
out_of_memory(); | ||||
return; | ||||
} // if | ||||
} // else | ||||
assert(program != NULL); | ||||
MOJOSHADER_glBindProgram(program); | ||||
} // MOJOSHADER_glBindShaders | ||||
static inline uint minuint(const uint a, const uint b) | ||||
{ | ||||
return ((a < b) ? a : b); | ||||
} // minuint | ||||
void MOJOSHADER_glSetVertexShaderUniformF(unsigned int idx, const float *data, | ||||
unsigned int vec4n) | ||||
{ | ||||
const uint maxregs = STATICARRAYLEN(ctx->vs_reg_file_f) / 4; | ||||
if (idx < maxregs) | ||||
{ | ||||
assert(sizeof (GLfloat) == sizeof (float)); | ||||
const uint cpy = (minuint(maxregs - idx, vec4n) * sizeof (*data)) * 4; | ||||
memcpy(ctx->vs_reg_file_f + (idx * 4), data, cpy); | ||||
ctx->generation++; | ||||
} // if | ||||
} // MOJOSHADER_glSetVertexShaderUniformF | ||||
void MOJOSHADER_glGetVertexShaderUniformF(unsigned int idx, float *data, | ||||
unsigned int vec4n) | ||||
{ | ||||
const uint maxregs = STATICARRAYLEN(ctx->vs_reg_file_f) / 4; | ||||
if (idx < maxregs) | ||||
{ | ||||
assert(sizeof (GLfloat) == sizeof (float)); | ||||
const uint cpy = (minuint(maxregs - idx, vec4n) * sizeof (*data)) * 4; | ||||
memcpy(data, ctx->vs_reg_file_f + (idx * 4), cpy); | ||||
} // if | ||||
} // MOJOSHADER_glGetVertexShaderUniformF | ||||
void MOJOSHADER_glSetVertexShaderUniformI(unsigned int idx, const int *data, | ||||
unsigned int ivec4n) | ||||
{ | ||||
const uint maxregs = STATICARRAYLEN(ctx->vs_reg_file_i) / 4; | ||||
if (idx < maxregs) | ||||
{ | ||||
assert(sizeof (GLint) == sizeof (int)); | ||||
const uint cpy = (minuint(maxregs - idx, ivec4n) * sizeof (*data)) * 4; | ||||
memcpy(ctx->vs_reg_file_i + (idx * 4), data, cpy); | ||||
ctx->generation++; | ||||
} // if | ||||
} // MOJOSHADER_glSetVertexShaderUniformI | ||||
void MOJOSHADER_glGetVertexShaderUniformI(unsigned int idx, int *data, | ||||
unsigned int ivec4n) | ||||
{ | ||||
const uint maxregs = STATICARRAYLEN(ctx->vs_reg_file_i) / 4; | ||||
if (idx < maxregs) | ||||
{ | ||||
assert(sizeof (GLint) == sizeof (int)); | ||||
const uint cpy = (minuint(maxregs - idx, ivec4n) * sizeof (*data)) * 4; | ||||
memcpy(data, ctx->vs_reg_file_i + (idx * 4), cpy); | ||||
} // if | ||||
} // MOJOSHADER_glGetVertexShaderUniformI | ||||
void MOJOSHADER_glSetVertexShaderUniformB(unsigned int idx, const int *data, | ||||
unsigned int bcount) | ||||
{ | ||||
const uint maxregs = STATICARRAYLEN(ctx->vs_reg_file_b) / 4; | ||||
if (idx < maxregs) | ||||
{ | ||||
uint8 *wptr = ctx->vs_reg_file_b + idx; | ||||
uint8 *endptr = wptr + minuint(maxregs - idx, bcount); | ||||
while (wptr != endptr) | ||||
*(wptr++) = *(data++) ? 1 : 0; | ||||
ctx->generation++; | ||||
} // if | ||||
} // MOJOSHADER_glSetVertexShaderUniformB | ||||
void MOJOSHADER_glGetVertexShaderUniformB(unsigned int idx, int *data, | ||||
unsigned int bcount) | ||||
{ | ||||
const uint maxregs = STATICARRAYLEN(ctx->vs_reg_file_b) / 4; | ||||
if (idx < maxregs) | ||||
{ | ||||
uint8 *rptr = ctx->vs_reg_file_b + idx; | ||||
uint8 *endptr = rptr + minuint(maxregs - idx, bcount); | ||||
while (rptr != endptr) | ||||
*(data++) = (int) *(rptr++); | ||||
} // if | ||||
} // MOJOSHADER_glGetVertexShaderUniformB | ||||
void MOJOSHADER_glSetPixelShaderUniformF(unsigned int idx, const float *data, | ||||
unsigned int vec4n) | ||||
{ | ||||
const uint maxregs = STATICARRAYLEN(ctx->ps_reg_file_f) / 4; | ||||
if (idx < maxregs) | ||||
{ | ||||
assert(sizeof (GLfloat) == sizeof (float)); | ||||
const uint cpy = (minuint(maxregs - idx, vec4n) * sizeof (*data)) * 4; | ||||
memcpy(ctx->ps_reg_file_f + (idx * 4), data, cpy); | ||||
ctx->generation++; | ||||
} // if | ||||
} // MOJOSHADER_glSetPixelShaderUniformF | ||||
void MOJOSHADER_glGetPixelShaderUniformF(unsigned int idx, float *data, | ||||
unsigned int vec4n) | ||||
{ | ||||
const uint maxregs = STATICARRAYLEN(ctx->ps_reg_file_f) / 4; | ||||
if (idx < maxregs) | ||||
{ | ||||
assert(sizeof (GLfloat) == sizeof (float)); | ||||
const uint cpy = (minuint(maxregs - idx, vec4n) * sizeof (*data)) * 4; | ||||
memcpy(data, ctx->ps_reg_file_f + (idx * 4), cpy); | ||||
} // if | ||||
} // MOJOSHADER_glGetPixelShaderUniformF | ||||
void MOJOSHADER_glSetPixelShaderUniformI(unsigned int idx, const int *data, | ||||
unsigned int ivec4n) | ||||
{ | ||||
const uint maxregs = STATICARRAYLEN(ctx->ps_reg_file_i) / 4; | ||||
if (idx < maxregs) | ||||
{ | ||||
assert(sizeof (GLint) == sizeof (int)); | ||||
const uint cpy = (minuint(maxregs - idx, ivec4n) * sizeof (*data)) * 4; | ||||
memcpy(ctx->ps_reg_file_i + (idx * 4), data, cpy); | ||||
ctx->generation++; | ||||
} // if | ||||
} // MOJOSHADER_glSetPixelShaderUniformI | ||||
void MOJOSHADER_glGetPixelShaderUniformI(unsigned int idx, int *data, | ||||
unsigned int ivec4n) | ||||
{ | ||||
const uint maxregs = STATICARRAYLEN(ctx->ps_reg_file_i) / 4; | ||||
if (idx < maxregs) | ||||
{ | ||||
assert(sizeof (GLint) == sizeof (int)); | ||||
const uint cpy = (minuint(maxregs - idx, ivec4n) * sizeof (*data)) * 4; | ||||
memcpy(data, ctx->ps_reg_file_i + (idx * 4), cpy); | ||||
} // if | ||||
} // MOJOSHADER_glGetPixelShaderUniformI | ||||
void MOJOSHADER_glSetPixelShaderUniformB(unsigned int idx, const int *data, | ||||
unsigned int bcount) | ||||
{ | ||||
const uint maxregs = STATICARRAYLEN(ctx->ps_reg_file_b) / 4; | ||||
if (idx < maxregs) | ||||
{ | ||||
uint8 *wptr = ctx->ps_reg_file_b + idx; | ||||
uint8 *endptr = wptr + minuint(maxregs - idx, bcount); | ||||
while (wptr != endptr) | ||||
*(wptr++) = *(data++) ? 1 : 0; | ||||
ctx->generation++; | ||||
} // if | ||||
} // MOJOSHADER_glSetPixelShaderUniformB | ||||
void MOJOSHADER_glGetPixelShaderUniformB(unsigned int idx, int *data, | ||||
unsigned int bcount) | ||||
{ | ||||
const uint maxregs = STATICARRAYLEN(ctx->ps_reg_file_b) / 4; | ||||
if (idx < maxregs) | ||||
{ | ||||
uint8 *rptr = ctx->ps_reg_file_b + idx; | ||||
uint8 *endptr = rptr + minuint(maxregs - idx, bcount); | ||||
while (rptr != endptr) | ||||
*(data++) = (int) *(rptr++); | ||||
} // if | ||||
} // MOJOSHADER_glGetPixelShaderUniformB | ||||
static inline GLenum opengl_attr_type(const MOJOSHADER_attributeType type) | ||||
{ | ||||
switch (type) | ||||
{ | ||||
case MOJOSHADER_ATTRIBUTE_UNKNOWN: return GL_NONE; // oh well. | ||||
case MOJOSHADER_ATTRIBUTE_BYTE: return GL_BYTE; | ||||
case MOJOSHADER_ATTRIBUTE_UBYTE: return GL_UNSIGNED_BYTE; | ||||
case MOJOSHADER_ATTRIBUTE_SHORT: return GL_SHORT; | ||||
case MOJOSHADER_ATTRIBUTE_USHORT: return GL_UNSIGNED_SHORT; | ||||
case MOJOSHADER_ATTRIBUTE_INT: return GL_INT; | ||||
case MOJOSHADER_ATTRIBUTE_UINT: return GL_UNSIGNED_INT; | ||||
case MOJOSHADER_ATTRIBUTE_FLOAT: return GL_FLOAT; | ||||
case MOJOSHADER_ATTRIBUTE_DOUBLE: return GL_DOUBLE; | ||||
case MOJOSHADER_ATTRIBUTE_HALF_FLOAT: | ||||
if (ctx->have_GL_NV_half_float) | ||||
return GL_HALF_FLOAT_NV; | ||||
else if (ctx->have_GL_ARB_half_float_vertex) | ||||
return GL_HALF_FLOAT_ARB; | ||||
else if (ctx->have_GL_OES_vertex_half_float) | ||||
return GL_HALF_FLOAT_OES; | ||||
break; | ||||
} // switch | ||||
return GL_NONE; // oh well. Raises a GL error later. | ||||
} // 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, | ||||
MOJOSHADER_attributeType type, | ||||
int normalized, unsigned int stride, | ||||
const void *ptr) | ||||
{ | ||||
if ((ctx->bound_program == NULL) || (ctx->bound_program->vertex == NULL)) | ||||
return; | ||||
const GLenum gl_type = opengl_attr_type(type); | ||||
const GLboolean norm = (normalized) ? GL_TRUE : GL_FALSE; | ||||
const GLint gl_index = ctx->bound_program->vertex_attrib_loc[usage][index]; | ||||
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. | ||||
ctx->glVertexAttribPointer(gl_index, size, gl_type, norm, stride, ptr); | ||||
// flag this array as in use, so we can enable it later. | ||||
ctx->want_attr[gl_index] = 1; | ||||
if (ctx->max_attrs < (gl_index + 1)) | ||||
ctx->max_attrs = gl_index + 1; | ||||
} // MOJOSHADER_glSetVertexAttribute | ||||
// !!! FIXME: shouldn't (index) be unsigned? | ||||
void MOJOSHADER_glSetVertexAttribDivisor(MOJOSHADER_usage usage, | ||||
int index, unsigned int divisor) | ||||
{ | ||||
assert(ctx->have_GL_ARB_instanced_arrays); | ||||
if ((ctx->bound_program == NULL) || (ctx->bound_program->vertex == NULL)) | ||||
return; | ||||
const GLint gl_index = ctx->bound_program->vertex_attrib_loc[usage][index]; | ||||
if (gl_index == -1) | ||||
return; // Nothing to do, this shader doesn't use this stream. | ||||
if (divisor != ctx->attr_divisor[gl_index]) | ||||
{ | ||||
ctx->glVertexAttribDivisorARB(gl_index, divisor); | ||||
ctx->attr_divisor[gl_index] = divisor; | ||||
} // if | ||||
} // MOJOSHADER_glSetVertexAttribDivisor | ||||
void MOJOSHADER_glSetLegacyBumpMapEnv(unsigned int sampler, float mat00, | ||||
float mat01, float mat10, float mat11, | ||||
float lscale, float loffset) | ||||
{ | ||||
if ((sampler == 0) || (sampler > (MAX_TEXBEMS+1))) | ||||
return; | ||||
GLfloat *dstf = ctx->texbem_state + (6 * (sampler-1)); | ||||
*(dstf++) = (GLfloat) mat00; | ||||
*(dstf++) = (GLfloat) mat01; | ||||
*(dstf++) = (GLfloat) mat10; | ||||
*(dstf++) = (GLfloat) mat11; | ||||
*(dstf++) = (GLfloat) lscale; | ||||
*(dstf++) = (GLfloat) loffset; | ||||
ctx->generation++; | ||||
} // MOJOSHADER_glSetLegacyBumpMapEnv | ||||
void MOJOSHADER_glProgramReady(void) | ||||
{ | ||||
MOJOSHADER_glProgram *program = ctx->bound_program; | ||||
if (program == NULL) | ||||
return; // nothing to do. | ||||
// Toggle vertex attribute arrays on/off, based on our needs. | ||||
update_enabled_arrays(); | ||||
if (program->uses_pointsize != ctx->pointsize_enabled) | ||||
{ | ||||
ctx->profileToggleProgramPointSize(program->uses_pointsize); | ||||
ctx->pointsize_enabled = program->uses_pointsize; | ||||
} // if | ||||
// push Uniforms to the program from our register files... | ||||
if ( ((program->uniform_count) || (program->texbem_count)) && | ||||
(program->generation != ctx->generation)) | ||||
{ | ||||
// vertex shader uniforms come first in program->uniforms array. | ||||
const uint32 count = program->uniform_count; | ||||
const GLfloat *srcf = ctx->vs_reg_file_f; | ||||
const GLint *srci = ctx->vs_reg_file_i; | ||||
const uint8 *srcb = ctx->vs_reg_file_b; | ||||
MOJOSHADER_shaderType shader_type = MOJOSHADER_TYPE_VERTEX; | ||||
GLfloat *dstf = program->vs_uniforms_float4; | ||||
GLint *dsti = program->vs_uniforms_int4; | ||||
GLint *dstb = program->vs_uniforms_bool; | ||||
uint8 uniforms_changed = 0; | ||||
uint32 i; | ||||
for (i = 0; i < count; i++) | ||||
{ | ||||
UniformMap *map = &program->uniforms[i]; | ||||
const MOJOSHADER_shaderType uniform_shader_type = map->shader_type; | ||||
const MOJOSHADER_uniform *u = map->uniform; | ||||
const MOJOSHADER_uniformType type = u->type; | ||||
const int index = u->index; | ||||
const int size = u->array_count ? u->array_count : 1; | ||||
assert(!u->constant); | ||||
// Did we switch from vertex to pixel (to geometry, etc)? | ||||
if (shader_type != uniform_shader_type) | ||||
{ | ||||
// we start with vertex, move to pixel, then to geometry, etc. | ||||
// The array should always be sorted as such. | ||||
if (uniform_shader_type == MOJOSHADER_TYPE_PIXEL) | ||||
{ | ||||
assert(shader_type == MOJOSHADER_TYPE_VERTEX); | ||||
srcf = ctx->ps_reg_file_f; | ||||
srci = ctx->ps_reg_file_i; | ||||
srcb = ctx->ps_reg_file_b; | ||||
dstf = program->ps_uniforms_float4; | ||||
dsti = program->ps_uniforms_int4; | ||||
dstb = program->ps_uniforms_bool; | ||||
} // if | ||||
else | ||||
{ | ||||
// Should be ordered vertex, then pixel, then geometry. | ||||
assert(0 && "Unexpected shader type"); | ||||
} // else | ||||
shader_type = uniform_shader_type; | ||||
} // if | ||||
if (type == MOJOSHADER_UNIFORM_FLOAT) | ||||
{ | ||||
const size_t count = 4 * size; | ||||
const GLfloat *f = &srcf[index * 4]; | ||||
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]; | ||||
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) | ||||
{ | ||||
const size_t count = size; | ||||
const uint8 *b = &srcb[index]; | ||||
size_t i; | ||||
for (i = 0; i < count; i++) | ||||
if (dstb[i] != b[i]) | ||||
{ | ||||
dstb[i] = (GLint) b[i]; | ||||
uniforms_changed = 1; | ||||
} // if | ||||
dstb += count; | ||||
} // else if | ||||
// !!! FIXME: set constants that overlap the array. | ||||
} // for | ||||
assert((!program->texbem_count) || (program->fragment)); | ||||
if ((program->texbem_count) && (program->fragment)) | ||||
{ | ||||
const MOJOSHADER_parseData *pd = program->fragment->parseData; | ||||
const int samp_count = pd->sampler_count; | ||||
const MOJOSHADER_sampler *samps = pd->samplers; | ||||
GLfloat *dstf = program->ps_uniforms_float4; | ||||
int texbem_count = 0; | ||||
dstf += (program->ps_uniforms_float4_count * 4) - | ||||
(program->texbem_count * 8); | ||||
assert(program->texbem_count <= MAX_TEXBEMS); | ||||
for (i = 0; i < samp_count; i++) | ||||
{ | ||||
if (samps[i].texbem) | ||||
{ | ||||
assert(samps[i].index > 0); | ||||
assert(samps[i].index <= MAX_TEXBEMS); | ||||
memcpy(dstf, &ctx->texbem_state[6 * (samps[i].index-1)], | ||||
sizeof (GLfloat) * 6); | ||||
dstf[6] = 0.0f; | ||||
dstf[7] = 0.0f; | ||||
dstf += 8; | ||||
texbem_count++; | ||||
} // if | ||||
} // for | ||||
assert(texbem_count == program->texbem_count); | ||||
} // if | ||||
program->generation = ctx->generation; | ||||
if (uniforms_changed) | ||||
ctx->profilePushUniforms(); | ||||
} // if | ||||
} // MOJOSHADER_glProgramReady | ||||
void MOJOSHADER_glProgramViewportInfo(int viewportW, int viewportH, | ||||
int backbufferW, int backbufferH, | ||||
int renderTargetBound) | ||||
{ | ||||
int vposFlip[2]; | ||||
/* The uniform is only going to exist if VPOS is used! */ | ||||
if (ctx->bound_program->ps_vpos_flip_loc != -1) | ||||
{ | ||||
if (renderTargetBound) | ||||
{ | ||||
vposFlip[0] = 1; | ||||
vposFlip[1] = 0; | ||||
} // if | ||||
else | ||||
{ | ||||
vposFlip[0] = -1; | ||||
vposFlip[1] = backbufferH; | ||||
} // else | ||||
if ( (ctx->bound_program->current_vpos_flip[0] != vposFlip[0]) || | ||||
(ctx->bound_program->current_vpos_flip[1] != vposFlip[1]) ) | ||||
{ | ||||
ctx->glUniform2f( | ||||
ctx->bound_program->ps_vpos_flip_loc, | ||||
(float) vposFlip[0], | ||||
(float) vposFlip[1] | ||||
); | ||||
ctx->bound_program->current_vpos_flip[0] = vposFlip[0]; | ||||
ctx->bound_program->current_vpos_flip[1] = vposFlip[1]; | ||||
} // if | ||||
} // if | ||||
#ifdef MOJOSHADER_FLIP_RENDERTARGET | ||||
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 | ||||
*/ | ||||
const int flip = renderTargetBound ? -1 : 1; | ||||
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 | ||||
} // MOJOSHADER_glViewportInfo | ||||
void MOJOSHADER_glDeleteProgram(MOJOSHADER_glProgram *program) | ||||
{ | ||||
program_unref(program); | ||||
} // MOJOSHADER_glDeleteProgram | ||||
void MOJOSHADER_glDeleteShader(MOJOSHADER_glShader *shader) | ||||
{ | ||||
// See if this was bound as an unlinked program anywhere... | ||||
if (ctx->linker_cache) | ||||
{ | ||||
const void *key = NULL; | ||||
void *iter = NULL; | ||||
int morekeys = hash_iter_keys(ctx->linker_cache, &key, &iter); | ||||
while (morekeys) | ||||
{ | ||||
const BoundShaders *shaders = (const BoundShaders *) key; | ||||
// Do this here so we don't confuse the iteration by removing... | ||||
morekeys = hash_iter_keys(ctx->linker_cache, &key, &iter); | ||||
if ((shaders->vertex == shader) || (shaders->fragment == shader)) | ||||
{ | ||||
// Deletes the linked program, which will unref the shader. | ||||
hash_remove(ctx->linker_cache, shaders); | ||||
} // if | ||||
} // while | ||||
} // if | ||||
shader_unref(shader); | ||||
} // MOJOSHADER_glDeleteShader | ||||
void MOJOSHADER_glDestroyContext(MOJOSHADER_glContext *_ctx) | ||||
{ | ||||
MOJOSHADER_glContext *current_ctx = ctx; | ||||
ctx = _ctx; | ||||
MOJOSHADER_glBindProgram(NULL); | ||||
if (ctx->linker_cache) | ||||
hash_destroy(ctx->linker_cache); | ||||
lookup_entry_points(NULL, NULL); // !!! FIXME: is there a value to this? | ||||
Free(ctx); | ||||
ctx = ((current_ctx == _ctx) ? NULL : current_ctx); | ||||
} // MOJOSHADER_glDestroyContext | ||||
#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->type.parameter_type == MOJOSHADER_SYMTYPE_FLOAT) | ||||
memcpy(regf + start, param->valuesF, sym->register_count << 4); | ||||
else if (sym->register_set == MOJOSHADER_SYMREGSET_FLOAT4) | ||||
{ | ||||
// Structs are a whole different world... | ||||
if (param->type.parameter_class == MOJOSHADER_SYMCLASS_STRUCT) | ||||
memcpy(regf + start, param->valuesF, sym->register_count << 4); | ||||
else | ||||
{ | ||||
// Sometimes int/bool parameters get thrown into float registers... | ||||
j = 0; | ||||
do | ||||
{ | ||||
c = 0; | ||||
do | ||||
{ | ||||
regf[start + (j << 2) + c] = (float) param->valuesI[(j << 2) + c]; | ||||
} while (++c < param->type.columns); | ||||
} while (++j < sym->register_count); | ||||
} // else | ||||
} // else if | ||||
else if (sym->register_set == MOJOSHADER_SYMREGSET_INT4) | ||||
memcpy(regi + start, param->valuesI, sym->register_count << 4); | ||||
else if (sym->register_set == MOJOSHADER_SYMREGSET_BOOL) | ||||
{ | ||||
j = 0; | ||||
r = 0; | ||||
do | ||||
{ | ||||
c = 0; | ||||
do | ||||
{ | ||||
// regb is not a vec4, enjoy that 'start' bitshift! -flibit | ||||
regb[(start >> 2) + r + c] = param->valuesI[(j << 2) + c]; | ||||
c++; | ||||
} while (c < param->type.columns && ((r + c) < sym->register_count)); | ||||
r += c; | ||||
j++; | ||||
} while (r < sym->register_count); | ||||
} // else if | ||||
} // for | ||||
} // copy_parameter_data | ||||
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, j; | ||||
MOJOSHADER_effectValue *param; | ||||
float selector; | ||||
int shader_object; | ||||
int selector_ran = 0; | ||||
/* For effect passes with arrays of shaders, we have to run a preshader | ||||
* that determines which shader to use, based on a parameter's value. | ||||
* -flibit | ||||
*/ | ||||
// !!! FIXME: We're just running the preshaders every time. Blech. -flibit | ||||
#define SELECT_SHADER_FROM_PRESHADER(raw, gls) \ | ||||
if (raw != NULL && raw->is_preshader) \ | ||||
{ \ | ||||
i = 0; \ | ||||
do \ | ||||
{ \ | ||||
param = &glEffect->effect->params[raw->preshader_params[i]].value; \ | ||||
for (j = 0; j < (param->value_count >> 2); j++) \ | ||||
memcpy(raw->preshader->registers + raw->preshader->symbols[i].register_index + j, \ | ||||
param->valuesI + (j << 2), \ | ||||
param->type.columns << 2); \ | ||||
} while (++i < raw->preshader->symbol_count); \ | ||||
MOJOSHADER_runPreshader(raw->preshader, &selector); \ | ||||
shader_object = 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) \ | ||||
{ \ | ||||
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 ... | ||||