mutter/cogl/driver/gl/cogl-program.c
Robert Bragg 65196a4a9b 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.
2010-08-09 17:27:02 +01:00

444 lines
12 KiB
C

/*
* Cogl
*
* An object oriented GL/GLES Abstraction/Utility Layer
*
* Copyright (C) 2008,2009,2010 Intel Corporation.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* 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/>.
*
*
* Authors:
* Robert Bragg <robert@linux.intel.com>
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "cogl.h"
#include "cogl-program.h"
#include "cogl-shader-private.h"
#include "cogl-internal.h"
#include "cogl-handle.h"
#include "cogl-context.h"
#include "cogl-journal-private.h"
#include "cogl-material-opengl-private.h"
#include <glib.h>
#include <string.h>
#define glCreateProgram ctx->drv.pf_glCreateProgram
#define glAttachShader ctx->drv.pf_glAttachShader
#define glUseProgram ctx->drv.pf_glUseProgram
#define glLinkProgram ctx->drv.pf_glLinkProgram
#define glGetUniformLocation ctx->drv.pf_glGetUniformLocation
#define glUniform1f ctx->drv.pf_glUniform1f
#define glUniform2f ctx->drv.pf_glUniform2f
#define glUniform3f ctx->drv.pf_glUniform3f
#define glUniform4f ctx->drv.pf_glUniform4f
#define glUniform1fv ctx->drv.pf_glUniform1fv
#define glUniform2fv ctx->drv.pf_glUniform2fv
#define glUniform3fv ctx->drv.pf_glUniform3fv
#define glUniform4fv ctx->drv.pf_glUniform4fv
#define glUniform1i ctx->drv.pf_glUniform1i
#define glUniform2i ctx->drv.pf_glUniform2i
#define glUniform3i ctx->drv.pf_glUniform3i
#define glUniform4i ctx->drv.pf_glUniform4i
#define glUniform1iv ctx->drv.pf_glUniform1iv
#define glUniform2iv ctx->drv.pf_glUniform2iv
#define glUniform3iv ctx->drv.pf_glUniform3iv
#define glUniform4iv ctx->drv.pf_glUniform4iv
#define glUniformMatrix2fv ctx->drv.pf_glUniformMatrix2fv
#define glUniformMatrix3fv ctx->drv.pf_glUniformMatrix3fv
#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);
COGL_OBJECT_DEFINE_DEPRECATED_REF_COUNTING (program);
static void
_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);
}
CoglHandle
cogl_create_program (void)
{
CoglProgram *program;
_COGL_GET_CONTEXT (ctx, NULL);
program = g_slice_new0 (CoglProgram);
return _cogl_program_handle_new (program);
}
void
cogl_program_attach_shader (CoglHandle program_handle,
CoglHandle shader_handle)
{
CoglProgram *program;
CoglShader *shader;
CoglShaderLanguage language;
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
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)
{
CoglProgram *program;
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
if (!cogl_is_program (handle))
return;
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
cogl_program_use (CoglHandle handle)
{
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
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)
ctx->legacy_state_set--;
if (handle != COGL_INVALID_HANDLE)
cogl_handle_ref (handle);
if (ctx->current_program != COGL_INVALID_HANDLE)
cogl_handle_unref (ctx->current_program);
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)
{
CoglProgram *program;
_COGL_GET_CONTEXT (ctx, 0);
if (!cogl_is_program (handle))
return 0;
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);
}
void
cogl_program_uniform_1f (int uniform_no,
float value)
{
CoglProgram *program;
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
program = ctx->current_program;
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));
}
void
cogl_program_uniform_1i (int uniform_no,
int value)
{
CoglProgram *program;
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
program = ctx->current_program;
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));
}
void
cogl_program_uniform_float (int uniform_no,
int size,
int count,
const GLfloat *value)
{
CoglProgram *program;
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
program = ctx->current_program;
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)
{
case 1:
GE (glUniform1fv (uniform_no, count, value));
break;
case 2:
GE (glUniform2fv (uniform_no, count, value));
break;
case 3:
GE (glUniform3fv (uniform_no, count, value));
break;
case 4:
GE (glUniform4fv (uniform_no, count, value));
break;
default:
g_warning ("%s called with invalid size parameter", G_STRFUNC);
}
}
}
void
cogl_program_uniform_int (int uniform_no,
int size,
int count,
const int *value)
{
CoglProgram *program;
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
program = ctx->current_program;
g_return_if_fail (program != NULL);
_cogl_gl_use_program_wrapper (program);
switch (size)
{
case 1:
glUniform1iv (uniform_no, count, value);
break;
case 2:
glUniform2iv (uniform_no, count, value);
break;
case 3:
glUniform3iv (uniform_no, count, value);
break;
case 4:
glUniform4iv (uniform_no, count, value);
break;
default:
g_warning ("%s called with invalid size parameter", G_STRFUNC);
}
}
void
cogl_program_uniform_matrix (int uniform_no,
int size,
int count,
gboolean transpose,
const GLfloat *value)
{
CoglProgram *program;
_COGL_GET_CONTEXT (ctx, NO_RETVAL);
program = ctx->current_program;
g_return_if_fail (program != NULL);
g_return_if_fail (program->language != COGL_SHADER_LANGUAGE_ARBFP);
_cogl_gl_use_program_wrapper (program);
switch (size)
{
case 2 :
GE (glUniformMatrix2fv (uniform_no, count, transpose, value));
break;
case 3 :
GE (glUniformMatrix3fv (uniform_no, count, transpose, value));
break;
case 4 :
GE (glUniformMatrix4fv (uniform_no, count, transpose, value));
break;
default :
g_warning ("%s called with invalid size parameter", G_STRFUNC);
}
}
CoglShaderLanguage
_cogl_program_get_language (CoglHandle handle)
{
CoglProgram *program = handle;
return program->language;
}