|
|
/**
|
|
|
* 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 ...
|
|
|
|
|
|
|