cogl: Allow setting ARBfp source on a CoglShader

This makes CoglProgram/Shader automatically detect when the user has
given an ARBfp program by checking for "!!ARBfp1.0" at the beginning of
the user's source.

ARBfp local parameters can be set with cogl_program_uniform_float
assuming you pass a @size of 4 (all ARBfp program.local parameters
are vectors of 4 floats).

This doesn't expose ARBfp environment parameters or double precision
local parameters.
This commit is contained in:
Robert Bragg 2010-08-04 17:53:51 +01:00
parent 5741e28cdc
commit 09e664c349
8 changed files with 373 additions and 84 deletions

View File

@ -79,6 +79,7 @@ typedef struct _CoglMaterialBackendARBfpPrivate
CoglMaterial *authority_cache;
unsigned long authority_cache_age;
CoglHandle user_program;
GString *source;
GLuint gl_program;
gboolean *sampled;
@ -160,7 +161,7 @@ layers_arbfp_would_differ (CoglMaterialLayer **material0_layers,
* generate.
*/
static CoglMaterial *
find_arbfp_authority (CoglMaterial *material)
find_arbfp_authority (CoglMaterial *material, CoglHandle user_program)
{
CoglMaterial *authority0;
CoglMaterial *authority1;
@ -171,6 +172,9 @@ find_arbfp_authority (CoglMaterial *material)
/* XXX: we'll need to update this when we add fog support to the
* arbfp codegen */
if (user_program != COGL_INVALID_HANDLE)
return material;
/* Find the first material that modifies state that affects the
* arbfp codegen... */
authority0 = _cogl_material_get_authority (material,
@ -253,9 +257,13 @@ _cogl_material_backend_arbfp_start (CoglMaterial *material,
CoglMaterial *authority;
CoglMaterialBackendARBfpPrivate *priv;
CoglMaterialBackendARBfpPrivate *authority_priv;
CoglHandle user_program;
_COGL_GET_CONTEXT (ctx, FALSE);
/* First validate that we can handle the current state using ARBfp
*/
if (!cogl_features_available (COGL_FEATURE_SHADERS_ARBFP))
return FALSE;
@ -263,11 +271,23 @@ _cogl_material_backend_arbfp_start (CoglMaterial *material,
if (ctx->legacy_fog_state.enabled)
return FALSE;
user_program = cogl_material_get_user_program (material);
if (user_program != COGL_INVALID_HANDLE &&
_cogl_program_get_language (user_program) != COGL_SHADER_LANGUAGE_ARBFP)
return FALSE;
/* Now lookup our ARBfp backend private state (allocating if
* necessary) that may contain a cache pointer refering us to the
* material's "arbfp-authority".
*
* The arbfp-authority is the oldest ancestor whos state will result in
* the same program being generated.
*/
/* Note: we allocate ARBfp private state for both the given material
* and the authority. (The oldest ancestor whos state will result in
* the same program being generated) The former will simply cache a
* pointer to the authority and the later will track the arbfp
* program that we will generate.
* and the arbfp-authority. The former will simply cache a pointer
* to the authority and the later will track the arbfp program that
* we will generate.
*/
if (!(material->backend_priv_set_mask & COGL_MATERIAL_BACKEND_ARBFP_MASK))
@ -285,16 +305,24 @@ _cogl_material_backend_arbfp_start (CoglMaterial *material,
* may invalidate authority_cache pointers.
*/
/* If the given material has changed since we last cached its
* arbfp-authority then invalidate our cache and then search the
* material's ancestors for one with matching fragment processing
* state. */
if (priv->authority_cache &&
priv->authority_cache_age != _cogl_material_get_age (material))
invalidate_arbfp_authority_cache (material);
if (!priv->authority_cache)
{
priv->authority_cache = find_arbfp_authority (material);
priv->authority_cache = find_arbfp_authority (material, user_program);
priv->authority_cache_age = _cogl_material_get_age (material);
}
/* Now we have our arbfp-authority fetch the ARBfp backend private
* state from it (allocting if necessary) */
authority = priv->authority_cache;
if (!(authority->backend_priv_set_mask & COGL_MATERIAL_BACKEND_ARBFP_MASK))
{
@ -304,7 +332,9 @@ _cogl_material_backend_arbfp_start (CoglMaterial *material,
}
authority_priv = authority->backend_privs[COGL_MATERIAL_BACKEND_ARBFP];
if (authority_priv->gl_program == 0)
authority_priv->user_program = user_program;
if (user_program == COGL_INVALID_HANDLE && authority_priv->gl_program == 0)
{
/* We reuse a single grow-only GString for ARBfp code-gen */
g_string_set_size (ctx->arbfp_source_buffer, 0);
@ -912,6 +942,7 @@ _cogl_material_backend_arbfp_end (CoglMaterial *material,
CoglMaterial *arbfp_authority = get_arbfp_authority (material);
CoglMaterialBackendARBfpPrivate *priv =
arbfp_authority->backend_privs[COGL_MATERIAL_BACKEND_ARBFP];
GLuint gl_program;
_COGL_GET_CONTEXT (ctx, FALSE);
@ -954,9 +985,16 @@ _cogl_material_backend_arbfp_end (CoglMaterial *material,
g_free (priv->sampled);
priv->sampled = NULL;
}
else
GE (glBindProgram (GL_FRAGMENT_PROGRAM_ARB, priv->gl_program));
if (priv->user_program != COGL_INVALID_HANDLE)
{
CoglProgram *program = (CoglProgram *)priv->user_program;
gl_program = program->gl_handle;
}
else
gl_program = priv->gl_program;
GE (glBindProgram (GL_FRAGMENT_PROGRAM_ARB, gl_program));
_cogl_use_program (COGL_INVALID_HANDLE, COGL_MATERIAL_PROGRAM_TYPE_ARBFP);
return TRUE;
@ -968,17 +1006,23 @@ _cogl_material_backend_arbfp_material_pre_change_notify (
CoglMaterialState change,
const CoglColor *new_color)
{
CoglMaterialBackendARBfpPrivate *priv =
material->backend_privs[COGL_MATERIAL_BACKEND_ARBFP];
CoglMaterialBackendARBfpPrivate *priv;
static const unsigned long fragment_op_changes =
COGL_MATERIAL_STATE_LAYERS;
COGL_MATERIAL_STATE_LAYERS |
COGL_MATERIAL_STATE_USER_SHADER;
/* TODO: COGL_MATERIAL_STATE_FOG */
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
if (material->backend_priv_set_mask & COGL_MATERIAL_BACKEND_ARBFP_MASK &&
priv->gl_program &&
change & fragment_op_changes)
if (!(material->backend_priv_set_mask & COGL_MATERIAL_BACKEND_ARBFP_MASK) ||
!(change & fragment_op_changes))
return;
priv = material->backend_privs[COGL_MATERIAL_BACKEND_ARBFP];
priv->user_program = COGL_INVALID_HANDLE;
if (priv->gl_program)
{
GE (glDeletePrograms (1, &priv->gl_program));
priv->gl_program = 0;

View File

@ -63,30 +63,22 @@ _cogl_material_backend_glsl_start (CoglMaterial *material,
int n_layers,
unsigned long materials_difference)
{
CoglHandle program;
_COGL_GET_CONTEXT (ctx, FALSE);
if (!cogl_features_available (COGL_FEATURE_SHADERS_GLSL))
return FALSE;
if (materials_difference & COGL_MATERIAL_STATE_USER_SHADER)
{
CoglMaterial *authority =
_cogl_material_get_authority (material,
COGL_MATERIAL_STATE_USER_SHADER);
CoglHandle program = authority->big_state->user_program;
if (program == COGL_INVALID_HANDLE)
program = cogl_material_get_user_program (material);
if (program == COGL_INVALID_HANDLE ||
_cogl_program_get_language (program) != COGL_SHADER_LANGUAGE_GLSL)
return FALSE; /* XXX: change me when we support code generation here */
_cogl_use_program (program, COGL_MATERIAL_PROGRAM_TYPE_GLSL);
return TRUE;
}
/* TODO: also support code generation */
return FALSE;
}
gboolean
_cogl_material_backend_glsl_add_layer (CoglMaterial *material,
CoglMaterialLayer *layer,

View File

@ -28,13 +28,26 @@
typedef struct _CoglShader CoglShader;
typedef enum
{
COGL_SHADER_LANGUAGE_GLSL,
#ifdef HAVE_COGL_GL
COGL_SHADER_LANGUAGE_ARBFP
#endif
} CoglShaderLanguage;
struct _CoglShader
{
CoglHandleObject _parent;
GLuint gl_handle;
char *arbfp_source;
CoglShaderType type;
CoglShaderLanguage language;
};
CoglShader *_cogl_shader_pointer_from_handle (CoglHandle handle);
CoglShaderLanguage
_cogl_program_get_language (CoglHandle handle);
#endif /* __COGL_SHADER_H */

View File

@ -33,6 +33,8 @@
#include <glib.h>
#include <string.h>
#ifdef HAVE_COGL_GL
#define glCreateShader ctx->drv.pf_glCreateShader
#define glGetShaderiv ctx->drv.pf_glGetShaderiv
@ -58,7 +60,14 @@ _cogl_shader_free (CoglShader *shader)
/* Frees shader resources but its handle is not
released! Do that separately before this! */
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
glDeleteShader (shader->gl_handle);
#ifdef HAVE_COGL_GL
if (shader->language == COGL_SHADER_LANGUAGE_ARBFP)
g_free (shader->arbfp_source);
else
#endif
if (shader->gl_handle)
GE (glDeleteShader (shader->gl_handle));
g_slice_free (CoglShader, shader);
}
@ -67,23 +76,23 @@ CoglHandle
cogl_create_shader (CoglShaderType type)
{
CoglShader *shader;
GLenum gl_type;
GET_CONTEXT (ctx, COGL_INVALID_HANDLE);
if (type == COGL_SHADER_TYPE_VERTEX)
gl_type = GL_VERTEX_SHADER;
else if (type == COGL_SHADER_TYPE_FRAGMENT)
gl_type = GL_FRAGMENT_SHADER;
else
switch (type)
{
case COGL_SHADER_TYPE_VERTEX:
case COGL_SHADER_TYPE_FRAGMENT:
break;
default:
g_warning ("Unexpected shader type (0x%08lX) given to "
"cogl_create_shader", (unsigned long) type);
return COGL_INVALID_HANDLE;
}
shader = g_slice_new (CoglShader);
shader->gl_handle = glCreateShader (gl_type);
shader->language = COGL_SHADER_LANGUAGE_GLSL;
shader->gl_handle = 0;
shader->type = type;
return _cogl_shader_handle_new (shader);
@ -94,6 +103,8 @@ cogl_shader_source (CoglHandle handle,
const char *source)
{
CoglShader *shader;
CoglShaderLanguage language;
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
if (!cogl_is_shader (handle))
@ -101,9 +112,59 @@ cogl_shader_source (CoglHandle handle,
shader = _cogl_shader_pointer_from_handle (handle);
#ifdef HAVE_COGL_GL
if (strncmp (source, "!!ARBfp1.0", 10) == 0)
language = COGL_SHADER_LANGUAGE_ARBFP;
else
#endif
language = COGL_SHADER_LANGUAGE_GLSL;
/* Delete the old object if the language is changing... */
if (G_UNLIKELY (language != shader->language))
{
#ifdef HAVE_COGL_GL
if (shader->language == COGL_SHADER_LANGUAGE_ARBFP)
{
g_free (shader->arbfp_source);
shader->arbfp_source = NULL;
}
#endif
else
{
if (shader->gl_handle)
GE (glDeleteShader (shader->gl_handle));
}
}
#ifdef HAVE_COGL_GL
if (language == COGL_SHADER_LANGUAGE_ARBFP)
shader->arbfp_source = g_strdup (source);
else
#endif
{
if (!shader->gl_handle)
{
GLenum gl_type;
switch (shader->type)
{
case COGL_SHADER_TYPE_VERTEX:
gl_type = GL_VERTEX_SHADER;
break;
case COGL_SHADER_TYPE_FRAGMENT:
gl_type = GL_FRAGMENT_SHADER;
break;
}
shader->gl_handle = glCreateShader (gl_type);
}
glShaderSource (shader->gl_handle, 1, &source, NULL);
}
shader->language = language;
}
void
cogl_shader_compile (CoglHandle handle)
{
@ -115,15 +176,14 @@ cogl_shader_compile (CoglHandle handle)
shader = _cogl_shader_pointer_from_handle (handle);
glCompileShader (shader->gl_handle);
if (shader->language == COGL_SHADER_LANGUAGE_GLSL)
GE (glCompileShader (shader->gl_handle));
}
char *
cogl_shader_get_info_log (CoglHandle handle)
{
CoglShader *shader;
char buffer[512];
int len = 0;
GET_CONTEXT (ctx, NULL);
@ -132,11 +192,22 @@ cogl_shader_get_info_log (CoglHandle handle)
shader = _cogl_shader_pointer_from_handle (handle);
if (shader->language == COGL_SHADER_LANGUAGE_ARBFP)
{
/* ARBfp exposes a program error string, but since cogl_program
* doesn't have any API to query an error log it is not currently
* exposed. */
return "";
}
else
{
char buffer[512];
int len = 0;
glGetShaderInfoLog (shader->gl_handle, 511, &len, buffer);
buffer[len] = '\0';
return g_strdup (buffer);
}
}
CoglShaderType
cogl_shader_get_type (CoglHandle handle)
@ -168,12 +239,19 @@ cogl_shader_is_compiled (CoglHandle handle)
shader = _cogl_shader_pointer_from_handle (handle);
#ifdef HAVE_COGL_GL
if (shader->language == COGL_SHADER_LANGUAGE_ARBFP)
return TRUE;
else
#endif
{
GE (glGetShaderiv (shader->gl_handle, GL_COMPILE_STATUS, &status));
if (status == GL_TRUE)
return TRUE;
else
return FALSE;
}
}
#else /* HAVE_COGL_GLES */

View File

@ -496,6 +496,9 @@ cogl_get_features (void)
if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_DISABLE_PBOS))
ctx->feature_flags &= ~COGL_FEATURE_PBOS;
if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_DISABLE_ARBFP))
ctx->feature_flags &= ~COGL_FEATURE_SHADERS_ARBFP;
if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_DISABLE_GLSL))
ctx->feature_flags &= ~COGL_FEATURE_SHADERS_GLSL;
@ -521,9 +524,6 @@ _cogl_features_available_private (CoglFeatureFlagsPrivate features)
if (!ctx->features_cached)
_cogl_features_init ();
if (G_UNLIKELY (cogl_debug_flags & COGL_DEBUG_DISABLE_ARBFP))
ctx->feature_flags_private &= ~COGL_FEATURE_SHADERS_ARBFP;
return (ctx->feature_flags_private & features) == features;
}

View File

@ -16,9 +16,12 @@
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
* License along with this library. If not, see
* <http://www.gnu.org/licenses/>.
*
*
* Authors:
* Robert Bragg <robert@linux.intel.com>
*/
#ifdef HAVE_CONFIG_H
@ -36,6 +39,8 @@
#include <glib.h>
#include <string.h>
#define glCreateProgram ctx->drv.pf_glCreateProgram
#define glAttachShader ctx->drv.pf_glAttachShader
#define glUseProgram ctx->drv.pf_glUseProgram
@ -62,6 +67,13 @@
#define glUniformMatrix4fv ctx->drv.pf_glUniformMatrix4fv
#define glDeleteProgram ctx->drv.pf_glDeleteProgram
#define glProgramString ctx->drv.pf_glProgramString
#define glBindProgram ctx->drv.pf_glBindProgram
#define glDeletePrograms ctx->drv.pf_glDeletePrograms
#define glGenPrograms ctx->drv.pf_glGenPrograms
#define glProgramLocalParameter4fv ctx->drv.pf_glProgramLocalParameter4fv
static void _cogl_program_free (CoglProgram *program);
COGL_HANDLE_DEFINE (Program, program);
@ -73,7 +85,14 @@ _cogl_program_free (CoglProgram *program)
/* Frees program resources but its handle is not
released! Do that separately before this! */
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
if (program->gl_handle)
{
if (program->language == COGL_SHADER_LANGUAGE_ARBFP)
GE (glDeletePrograms (1, &program->gl_handle));
else
GE (glDeleteProgram (program->gl_handle));
}
g_slice_free (CoglProgram, program);
}
@ -84,8 +103,7 @@ cogl_create_program (void)
CoglProgram *program;
_COGL_GET_CONTEXT (ctx, NULL);
program = g_slice_new (CoglProgram);
program->gl_handle = glCreateProgram ();
program = g_slice_new0 (CoglProgram);
return _cogl_program_handle_new (program);
}
@ -96,18 +114,65 @@ cogl_program_attach_shader (CoglHandle program_handle,
{
CoglProgram *program;
CoglShader *shader;
CoglShaderLanguage language;
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
if (!cogl_is_program (program_handle) || !cogl_is_shader (shader_handle))
return;
g_return_if_fail (cogl_is_program (program_handle));
g_return_if_fail (cogl_is_shader (shader_handle));
program = _cogl_program_pointer_from_handle (program_handle);
shader = _cogl_shader_pointer_from_handle (shader_handle);
language = shader->language;
/* We only allow attaching one ARBfp shader to a program */
if (language == COGL_SHADER_LANGUAGE_ARBFP)
g_return_if_fail (program->gl_handle == 0);
program->language = language;
if (language == COGL_SHADER_LANGUAGE_ARBFP)
{
#ifdef COGL_GL_DEBUG
GLenum gl_error;
#endif
GE (glGenPrograms (1, &program->gl_handle));
GE (glBindProgram (GL_FRAGMENT_PROGRAM_ARB, program->gl_handle));
#ifdef COGL_GL_DEBUG
while ((gl_error = glGetError ()) != GL_NO_ERROR)
;
#endif
glProgramString (GL_FRAGMENT_PROGRAM_ARB,
GL_PROGRAM_FORMAT_ASCII_ARB,
strlen (shader->arbfp_source),
shader->arbfp_source);
#ifdef COGL_GL_DEBUG
gl_error = glGetError ();
if (gl_error != GL_NO_ERROR)
{
g_warning ("%s: GL error (%d): Failed to compile ARBfp:\n%s\n%s",
G_STRLOC,
gl_error,
shader->arbfp_source,
glGetString (GL_PROGRAM_ERROR_STRING_ARB));
}
#endif
}
else
{
if (!program->gl_handle)
program->gl_handle = glCreateProgram ();
GE (glAttachShader (program->gl_handle, shader->gl_handle));
}
/* NB: There is no separation between shader objects and program
* objects for ARBfp */
}
void
cogl_program_link (CoglHandle handle)
{
@ -119,7 +184,11 @@ cogl_program_link (CoglHandle handle)
program = _cogl_program_pointer_from_handle (handle);
if (program->language == COGL_SHADER_LANGUAGE_GLSL &&
program->gl_handle)
GE (glLinkProgram (program->gl_handle));
program->is_linked = TRUE;
}
void
@ -130,6 +199,12 @@ cogl_program_use (CoglHandle handle)
g_return_if_fail (handle == COGL_INVALID_HANDLE ||
cogl_is_program (handle));
if (handle != COGL_INVALID_HANDLE)
{
CoglProgram *program = handle;
g_return_if_fail (program->is_linked);
}
if (ctx->current_program == 0 && handle != 0)
ctx->legacy_state_set++;
else if (handle == 0 && ctx->current_program != 0)
@ -142,6 +217,39 @@ cogl_program_use (CoglHandle handle)
ctx->current_program = handle;
}
/* ARBfp local parameters can be referenced like:
*
* "program.local[5]"
* ^14char offset (after whitespace is stripped)
*/
static int
get_local_param_index (const char *uniform_name)
{
char *input = g_strdup (uniform_name);
int i;
char *p = input;
char *endptr;
int _index;
for (i = 0; input[i] != '\0'; i++)
if (input[i] != '_' && input[i] != '\t')
*p++ = input[i];
input[i] = '\0';
g_return_val_if_fail (strncmp ("program.local[", input, 14) == 0, -1);
_index = g_ascii_strtoull (input + 14, &endptr, 10);
g_return_val_if_fail (endptr != input + 14, -1);
g_return_val_if_fail (*endptr == ']', -1);
g_return_val_if_fail (_index >= 0 &&
_index < COGL_PROGRAM_MAX_ARBFP_LOCAL_PARAMS, -1);
g_free (input);
return _index;
}
int
cogl_program_get_uniform_location (CoglHandle handle,
const char *uniform_name)
@ -154,6 +262,9 @@ cogl_program_get_uniform_location (CoglHandle handle,
program = _cogl_program_pointer_from_handle (handle);
if (program->language == COGL_SHADER_LANGUAGE_ARBFP)
return get_local_param_index (uniform_name);
else
return glGetUniformLocation (program->gl_handle, uniform_name);
}
@ -169,6 +280,8 @@ cogl_program_uniform_1f (int uniform_no,
g_return_if_fail (program != NULL);
g_return_if_fail (program->language != COGL_SHADER_LANGUAGE_ARBFP);
_cogl_gl_use_program_wrapper (program);
GE (glUniform1f (uniform_no, value));
@ -186,6 +299,8 @@ cogl_program_uniform_1i (int uniform_no,
g_return_if_fail (program != NULL);
g_return_if_fail (program->language != COGL_SHADER_LANGUAGE_ARBFP);
_cogl_gl_use_program_wrapper (program);
GE (glUniform1i (uniform_no, value));
@ -205,6 +320,28 @@ cogl_program_uniform_float (int uniform_no,
g_return_if_fail (program != NULL);
if (program->language == COGL_SHADER_LANGUAGE_ARBFP)
{
unsigned int _index = uniform_no;
unsigned int index_end = _index + count;
int i;
int j;
g_return_if_fail (size == 4);
GE (glBindProgram (GL_FRAGMENT_PROGRAM_ARB, program->gl_handle));
for (i = _index; i < index_end; i++)
for (j = 0; j < 4; j++)
program->arbfp_local_params[i][j] = *(value++);
for (i = _index; i < index_end; i++)
GE (glProgramLocalParameter4fv (GL_FRAGMENT_PROGRAM_ARB,
i,
&program->arbfp_local_params[i][0]));
}
else
{
_cogl_gl_use_program_wrapper (program);
switch (size)
@ -225,6 +362,7 @@ cogl_program_uniform_float (int uniform_no,
g_warning ("%s called with invalid size parameter", G_STRFUNC);
}
}
}
void
cogl_program_uniform_int (int uniform_no,
@ -276,6 +414,8 @@ cogl_program_uniform_matrix (int uniform_no,
g_return_if_fail (program != NULL);
g_return_if_fail (program->language != COGL_SHADER_LANGUAGE_ARBFP);
_cogl_gl_use_program_wrapper (program);
switch (size)
@ -293,3 +433,11 @@ cogl_program_uniform_matrix (int uniform_no,
g_warning ("%s called with invalid size parameter", G_STRFUNC);
}
}
CoglShaderLanguage
_cogl_program_get_language (CoglHandle handle)
{
CoglProgram *program = handle;
return program->language;
}

View File

@ -25,13 +25,20 @@
#define __COGL_PROGRAM_H
#include "cogl-handle.h"
#include "cogl-shader-private.h"
typedef struct _CoglProgram CoglProgram;
/* The ARBfp spec says at least 24 indices are available */
#define COGL_PROGRAM_MAX_ARBFP_LOCAL_PARAMS 24
struct _CoglProgram
{
CoglHandleObject _parent;
CoglShaderLanguage language;
float arbfp_local_params[COGL_PROGRAM_MAX_ARBFP_LOCAL_PARAMS][4];
GLuint gl_handle;
gboolean is_linked;
};
CoglProgram *_cogl_program_pointer_from_handle (CoglHandle handle);

View File

@ -365,3 +365,10 @@ cogl_program_uniform_matrix (int uniform_no,
#endif /* HAVE_COGL_GLES2 */
CoglShaderLanguage
_cogl_program_get_language (CoglHandle handle)
{
return COGL_SHADER_LANGUAGE_GLSL;
}