/* * 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 * . * * * Authors: * Robert Bragg */ #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 #include #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; }