#include #include #include #if defined(__APPLE__) #include #endif #include "shader.h" typedef struct opaque_shader { char *error_text; GLhandleARB program; size_t vertex_shader_count; GLhandleARB *vertex_shaders; size_t fragment_shader_count; GLhandleARB *fragment_shaders; } opaque_shader; static int _extension_supported(const char *extension) { return gluCheckExtension( (const GLubyte *)extension, glGetString(GL_EXTENSIONS)); } static void _clean_up_gl_bits(shader_t shader) { glDeleteObjectARB(shader->program); size_t i; for (i = 0; i < shader->vertex_shader_count; ++i) { glDeleteObjectARB(shader->vertex_shaders[i]); } for (i = 0; i < shader->fragment_shader_count; ++i) { glDeleteObjectARB(shader->fragment_shaders[i]); } free(shader->vertex_shaders); free(shader->fragment_shaders); shader->program = 0; shader->vertex_shader_count = 0; shader->fragment_shader_count = 0; shader->vertex_shaders = NULL; shader->fragment_shaders = NULL; } shader_t make_shader( size_t vertex_shader_count, const char **vertex_shader_texts, size_t fragment_shader_count, const char **fragment_shader_texts) { shader_t shader = calloc(1, sizeof(opaque_shader)); if (shader == NULL) { return NULL; } if (!_extension_supported("GL_ARB_shader_objects") || !_extension_supported("GL_ARB_vertex_shader") || !_extension_supported("GL_ARB_fragment_shader") || !_extension_supported("GL_ARB_shading_language_100")) { shader->error_text = strdup("OpenGL Shading Language not supported"); goto error; } shader->program = glCreateProgramObjectARB(); if (shader->program == 0) { shader->error_text = strdup("Can't generate program name"); goto error; } shader->vertex_shader_count = vertex_shader_count; shader->vertex_shaders = calloc(vertex_shader_count, sizeof(GLhandleARB)); if (shader->vertex_shaders == NULL) { shader->error_text = strdup("Can't allocate space for vertex shaders"); goto error; } shader->fragment_shader_count = fragment_shader_count; shader->fragment_shaders = calloc(fragment_shader_count, sizeof(GLhandleARB)); if (shader->vertex_shaders == NULL) { shader->error_text = strdup("Can't allocate space for fragment shaders"); goto error; } size_t i; for (i = 0; i < vertex_shader_count; ++i) { shader->vertex_shaders[i] = glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB); if (shader->vertex_shaders[i] == 0) { shader->error_text = strdup("Can't generate vertex shader name"); goto error; } glShaderSourceARB( shader->vertex_shaders[i], 1, vertex_shader_texts + i, NULL); glCompileShaderARB(shader->vertex_shaders[i]); GLint compiled_ok; glGetObjectParameterivARB( shader->vertex_shaders[i], GL_OBJECT_COMPILE_STATUS_ARB, &compiled_ok); if (!compiled_ok) { GLint info_log_length; glGetObjectParameterivARB( shader->vertex_shaders[i], GL_OBJECT_INFO_LOG_LENGTH_ARB, &info_log_length); char *info_log = malloc(info_log_length); glGetInfoLogARB( shader->vertex_shaders[i], info_log_length, NULL, info_log); char *text; asprintf( &text, "Compilation failed for vertex shader %u:\n%s", (unsigned)i, info_log); free(info_log); shader->error_text = text; goto error; } glAttachObjectARB(shader->program, shader->vertex_shaders[i]); } for (i = 0; i < fragment_shader_count; ++i) { shader->fragment_shaders[i] = glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB); if (shader->fragment_shaders[i] == 0) { shader->error_text = strdup("Can't generate fragment shader name"); goto error; } glShaderSourceARB( shader->fragment_shaders[i], 1, fragment_shader_texts + i, NULL); glCompileShaderARB(shader->fragment_shaders[i]); GLint compiled_ok; glGetObjectParameterivARB( shader->fragment_shaders[i], GL_OBJECT_COMPILE_STATUS_ARB, &compiled_ok); if (!compiled_ok) { GLint info_log_length; glGetObjectParameterivARB( shader->fragment_shaders[i], GL_OBJECT_INFO_LOG_LENGTH_ARB, &info_log_length); char *info_log = malloc(info_log_length); glGetInfoLogARB( shader->fragment_shaders[i], info_log_length, NULL, info_log); char *text; asprintf( &text, "Compilation failed for fragment shader %u:\n%s", (unsigned)i, info_log); free(info_log); shader->error_text = text; goto error; } glAttachObjectARB(shader->program, shader->fragment_shaders[i]); } glLinkProgramARB(shader->program); GLint linked_ok; glGetObjectParameterivARB( shader->program, GL_OBJECT_LINK_STATUS_ARB, &linked_ok); if (!linked_ok) { GLint info_log_length; glGetObjectParameterivARB( shader->program, GL_OBJECT_INFO_LOG_LENGTH_ARB, &info_log_length); char *info_log = malloc(info_log_length); glGetInfoLogARB( shader->program, info_log_length, NULL, info_log); char *text; asprintf( &text, "Linking failed:\n%s", info_log); free(info_log); shader->error_text = text; goto error; } #if defined(__APPLE__) glUseProgramObjectARB(shader->program); long hardwareAccelerated; CGLGetParameter( CGLGetCurrentContext(), kCGLCPGPUVertexProcessing, &hardwareAccelerated); if (!hardwareAccelerated) { fprintf(stderr, "Warning: Vertex shader is NOT being hardware-accelerated\n"); } CGLGetParameter( CGLGetCurrentContext(), kCGLCPGPUFragmentProcessing, &hardwareAccelerated); if (!hardwareAccelerated) { fprintf(stderr, "Warning: Fragment shader is NOT being hardware-accelerated\n"); } glUseProgramObjectARB(0); #endif return shader; error: _clean_up_gl_bits(shader); return shader; } void delete_shader(shader_t shader) { _clean_up_gl_bits(shader); free(shader->error_text); free(shader); } const char *shader_error_text(shader_t shader) { return shader->error_text; } GLhandleARB shader_program(shader_t shader) { return shader->program; }