vrshoot
diff src/shader.cc @ 0:b2f14e535253
initial commit
author | John Tsiombikas <nuclear@member.fsf.org> |
---|---|
date | Sat, 01 Feb 2014 19:58:19 +0200 |
parents | |
children |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/shader.cc Sat Feb 01 19:58:19 2014 +0200 1.3 @@ -0,0 +1,659 @@ 1.4 +#include <stdio.h> 1.5 +#include <string.h> 1.6 +#include <stdarg.h> 1.7 +#include <errno.h> 1.8 +#include "opengl.h" 1.9 +#include "shader.h" 1.10 +#include "logger.h" 1.11 +#include "unistate.h" 1.12 +#include "mesh.h" 1.13 + 1.14 +#ifdef _MSC_VER 1.15 +#include <malloc.h> 1.16 +#else 1.17 +#include <alloca.h> 1.18 +#endif 1.19 + 1.20 +#ifdef __GLEW_H__ 1.21 +#define HAVE_GEOMETRY_SHADER 1.22 +#define HAVE_TESSELATION_SHADER 1.23 +#endif 1.24 + 1.25 +static void bind_standard_attr(const ShaderProg *prog); 1.26 +static const char *strtype(unsigned int type); 1.27 + 1.28 +ShaderProg *ShaderProg::current; 1.29 + 1.30 +void bind_shader(const ShaderProg *sdr) 1.31 +{ 1.32 + if(sdr) { 1.33 + sdr->bind(); 1.34 + } else { 1.35 +#ifndef GL_ES_VERSION_2_0 1.36 + glUseProgram(0); 1.37 + ShaderProg::current = 0; 1.38 +#endif 1.39 + } 1.40 +} 1.41 + 1.42 +const ShaderProg *get_current_shader() 1.43 +{ 1.44 + return ShaderProg::current; 1.45 +} 1.46 + 1.47 + 1.48 +Shader::Shader() 1.49 +{ 1.50 + sdr = type = 0; 1.51 + name = 0; 1.52 +} 1.53 + 1.54 +Shader::~Shader() 1.55 +{ 1.56 + destroy(); 1.57 +} 1.58 + 1.59 +unsigned int Shader::get_id() const 1.60 +{ 1.61 + return sdr; 1.62 +} 1.63 + 1.64 +void Shader::set_name(const char *name) 1.65 +{ 1.66 + delete [] this->name; 1.67 + this->name = new char[strlen(name) + 1]; 1.68 + strcpy(this->name, name); 1.69 +} 1.70 + 1.71 +const char *Shader::get_name() const 1.72 +{ 1.73 + return name; 1.74 +} 1.75 + 1.76 +bool Shader::create(const char *src, unsigned int type) 1.77 +{ 1.78 +#if !GL_ES_VERSION_2_0 1.79 + const char *src_arr[] = {src}; 1.80 +#else 1.81 + const char *src_arr[] = { "precision mediump float; ", src }; 1.82 +#endif 1.83 + 1.84 + if(!sdr) { 1.85 + sdr = glCreateShader(type); 1.86 + } 1.87 + 1.88 + info_log("compiling shader: %s... ", name ? name : ""); 1.89 + 1.90 + glShaderSource(sdr, sizeof src_arr / sizeof *src_arr, src_arr, 0); 1.91 + glCompileShader(sdr); 1.92 + 1.93 + int status; 1.94 + glGetShaderiv(sdr, GL_COMPILE_STATUS, &status); 1.95 + 1.96 + info_log(status ? "success\n" : "failed\n"); 1.97 + 1.98 + int info_len; 1.99 + glGetShaderiv(sdr, GL_INFO_LOG_LENGTH, &info_len); 1.100 + if(info_len > 1) { 1.101 + char *buf = (char*)alloca(info_len); 1.102 + glGetShaderInfoLog(sdr, info_len, 0, buf); 1.103 + buf[info_len - 1] = 0; 1.104 + 1.105 + if(status) { 1.106 + info_log("%s\n", buf); 1.107 + } else { 1.108 + error_log("%s\n", buf); 1.109 + } 1.110 + } 1.111 + 1.112 + return status == GL_TRUE; 1.113 +} 1.114 + 1.115 +void Shader::destroy() 1.116 +{ 1.117 + if(sdr) { 1.118 + glDeleteShader(sdr); 1.119 + } 1.120 + sdr = type = 0; 1.121 + 1.122 + delete [] name; 1.123 + name = 0; 1.124 +} 1.125 + 1.126 +bool Shader::load(const char *fname, unsigned int type) 1.127 +{ 1.128 + FILE *fp; 1.129 + 1.130 + if(!(fp = fopen(fname, "rb"))) { 1.131 + error_log("failed to load %s shader: %s: %s\n", strtype(type), fname, strerror(errno)); 1.132 + return false; 1.133 + } 1.134 + 1.135 + fseek(fp, 0, SEEK_END); 1.136 + long sz = ftell(fp); 1.137 + rewind(fp); 1.138 + 1.139 + char *src = (char*)alloca(sz + 1); 1.140 + if(fread(src, 1, sz, fp) < (size_t)sz) { 1.141 + error_log("failed to load %s shader: %s: %s\n", strtype(type), fname, strerror(errno)); 1.142 + fclose(fp); 1.143 + return false; 1.144 + } 1.145 + src[sz] = 0; 1.146 + fclose(fp); 1.147 + 1.148 + set_name(fname); 1.149 + return create(src, type); 1.150 +} 1.151 + 1.152 +// ---- shader program ---- 1.153 +ShaderProg::ShaderProg() 1.154 +{ 1.155 + prog = 0; 1.156 + must_link = true; 1.157 +} 1.158 + 1.159 +ShaderProg::~ShaderProg() 1.160 +{ 1.161 + destroy(); 1.162 +} 1.163 + 1.164 +unsigned int ShaderProg::get_id() const 1.165 +{ 1.166 + return prog; 1.167 +} 1.168 + 1.169 +bool ShaderProg::create(const char *src, unsigned int type, ...) 1.170 +{ 1.171 + va_list ap; 1.172 + 1.173 + va_start(ap, type); 1.174 + bool res = create(src, type, ap); 1.175 + va_end(ap); 1.176 + 1.177 + return res; 1.178 +} 1.179 + 1.180 +bool ShaderProg::create(const char *src, unsigned int type, va_list ap) 1.181 +{ 1.182 + destroy(); 1.183 + prog = glCreateProgram(); 1.184 + 1.185 + while(src) { 1.186 + Shader *sdr = new Shader; 1.187 + if(!sdr->create(src, type)) { 1.188 + va_end(ap); 1.189 + return false; 1.190 + } 1.191 + add_shader(sdr); 1.192 + src = va_arg(ap, const char*); 1.193 + type = va_arg(ap, unsigned int); 1.194 + } 1.195 + return link(); 1.196 +} 1.197 + 1.198 +bool ShaderProg::create(const char *vsrc, const char *psrc) 1.199 +{ 1.200 + return create(VSDR(vsrc), PSDR(psrc), 0); 1.201 +} 1.202 + 1.203 +bool ShaderProg::create(Shader *sdr, ...) 1.204 +{ 1.205 + va_list ap; 1.206 + 1.207 + va_start(ap, sdr); 1.208 + bool res = create(sdr, ap); 1.209 + va_end(ap); 1.210 + 1.211 + return res; 1.212 +} 1.213 + 1.214 +bool ShaderProg::create(Shader *sdr, va_list ap) 1.215 +{ 1.216 + destroy(); 1.217 + prog = glCreateProgram(); 1.218 + 1.219 + while(sdr) { 1.220 + add_shader(sdr); 1.221 + sdr = va_arg(ap, Shader*); 1.222 + } 1.223 + return link(); 1.224 +} 1.225 + 1.226 +bool ShaderProg::create(Shader *vsdr, Shader *psdr) 1.227 +{ 1.228 + return create(vsdr, psdr, 0); 1.229 +} 1.230 + 1.231 +void ShaderProg::destroy() 1.232 +{ 1.233 + if(prog) { 1.234 + glDeleteProgram(prog); 1.235 + } 1.236 + prog = 0; 1.237 + 1.238 + shaders.clear(); 1.239 + // don't actually destroy the shaders, let the ShaderSet own them 1.240 +} 1.241 + 1.242 +bool ShaderProg::load(const char *fname, unsigned int type, ...) 1.243 +{ 1.244 + va_list ap; 1.245 + va_start(ap, type); 1.246 + bool res = load(fname, type, ap); 1.247 + va_end(ap); 1.248 + 1.249 + return res; 1.250 +} 1.251 + 1.252 +bool ShaderProg::load(const char *fname, unsigned int type, va_list ap) 1.253 +{ 1.254 + destroy(); 1.255 + prog = glCreateProgram(); 1.256 + 1.257 + while(fname) { 1.258 + Shader *sdr = new Shader; 1.259 + if(!sdr->load(fname, type)) { 1.260 + delete sdr; 1.261 + return false; 1.262 + } 1.263 + add_shader(sdr); 1.264 + 1.265 + if((fname = va_arg(ap, const char*))) { 1.266 + type = va_arg(ap, unsigned int); 1.267 + } 1.268 + } 1.269 + 1.270 + return link(); 1.271 +} 1.272 + 1.273 +bool ShaderProg::load(const char *vfname, const char *pfname) 1.274 +{ 1.275 + return load(VSDR(vfname), PSDR(pfname), 0); 1.276 +} 1.277 + 1.278 +void ShaderProg::add_shader(Shader *sdr) 1.279 +{ 1.280 + glAttachShader(prog, sdr->get_id()); 1.281 +} 1.282 + 1.283 +bool ShaderProg::link() const 1.284 +{ 1.285 + bind_standard_attr(this); 1.286 + 1.287 + CHECKGLERR; 1.288 + info_log("linking program ... "); 1.289 + glLinkProgram(prog); 1.290 + 1.291 + int status; 1.292 + glGetProgramiv(prog, GL_LINK_STATUS, &status); 1.293 + 1.294 + info_log(status ? "success\n" : "failed\n"); 1.295 + 1.296 + int info_len; 1.297 + glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &info_len); 1.298 + if(info_len > 1) { 1.299 + char *buf = (char*)alloca(info_len); 1.300 + glGetProgramInfoLog(prog, info_len, 0, buf); 1.301 + buf[info_len - 1] = 0; 1.302 + 1.303 + if(status) { 1.304 + info_log("%s\n", buf); 1.305 + } else { 1.306 + error_log("%s\n", buf); 1.307 + } 1.308 + } 1.309 + 1.310 + if(status) { 1.311 + must_link = false; 1.312 + cache_state_uniforms(); 1.313 + 1.314 + return true; 1.315 + } 1.316 + return false; 1.317 +} 1.318 + 1.319 +void ShaderProg::bind() const 1.320 +{ 1.321 + CHECKGLERR; 1.322 + if(must_link) { 1.323 + if(!link()) { 1.324 + return; 1.325 + } 1.326 + } 1.327 + CHECKGLERR; 1.328 + glUseProgram(prog); 1.329 + ShaderProg::current = (ShaderProg*)this; 1.330 + 1.331 + setup_state_uniforms(); 1.332 +} 1.333 + 1.334 + 1.335 +int ShaderProg::get_attrib_location(const char *name) const 1.336 +{ 1.337 + glUseProgram(prog); 1.338 + return glGetAttribLocation(prog, name); 1.339 +} 1.340 + 1.341 +void ShaderProg::set_attrib_location(const char *name, int loc) const 1.342 +{ 1.343 + glBindAttribLocation(prog, loc, name); 1.344 + must_link = true; 1.345 +} 1.346 + 1.347 +int ShaderProg::get_uniform_location(const char *name) const 1.348 +{ 1.349 + glUseProgram(prog); 1.350 + return glGetUniformLocation(prog, name); 1.351 +} 1.352 + 1.353 +bool ShaderProg::set_uniform(int loc, int val) const 1.354 +{ 1.355 + glUseProgram(prog); 1.356 + if(loc >= 0) { 1.357 + glUniform1i(loc, val); 1.358 + return true; 1.359 + } 1.360 + return false; 1.361 +} 1.362 + 1.363 +bool ShaderProg::set_uniform(int loc, float val) const 1.364 +{ 1.365 + glUseProgram(prog); 1.366 + if(loc >= 0) { 1.367 + glUniform1f(loc, val); 1.368 + return true; 1.369 + } 1.370 + return false; 1.371 +} 1.372 + 1.373 +bool ShaderProg::set_uniform(int loc, const Vector2 &v) const 1.374 +{ 1.375 + glUseProgram(prog); 1.376 + if(loc >= 0) { 1.377 + glUniform2f(loc, v.x, v.y); 1.378 + return true; 1.379 + } 1.380 + return false; 1.381 +} 1.382 + 1.383 +bool ShaderProg::set_uniform(int loc, const Vector3 &v) const 1.384 +{ 1.385 + glUseProgram(prog); 1.386 + if(loc >= 0) { 1.387 + glUniform3f(loc, v.x, v.y, v.z); 1.388 + return true; 1.389 + } 1.390 + return false; 1.391 +} 1.392 + 1.393 +bool ShaderProg::set_uniform(int loc, const Vector4 &v) const 1.394 +{ 1.395 + glUseProgram(prog); 1.396 + if(loc >= 0) { 1.397 + glUniform4f(loc, v.x, v.y, v.z, v.w); 1.398 + return true; 1.399 + } 1.400 + return false; 1.401 +} 1.402 + 1.403 +bool ShaderProg::set_uniform(int loc, const Matrix3x3 &m) const 1.404 +{ 1.405 + glUseProgram(prog); 1.406 + if(loc >= 0) { 1.407 + glUniformMatrix3fv(loc, 1, GL_TRUE, m[0]); 1.408 + return true; 1.409 + } 1.410 + return false; 1.411 +} 1.412 + 1.413 +bool ShaderProg::set_uniform(int loc, const Matrix4x4 &m) const 1.414 +{ 1.415 + glUseProgram(prog); 1.416 + if(loc >= 0) { 1.417 + glUniformMatrix4fv(loc, 1, GL_TRUE, m[0]); 1.418 + return true; 1.419 + } 1.420 + return false; 1.421 +} 1.422 + 1.423 + 1.424 +bool ShaderProg::set_uniform(const char *name, int val) const 1.425 +{ 1.426 + return set_uniform(get_uniform_location(name), val); 1.427 +} 1.428 + 1.429 +bool ShaderProg::set_uniform(const char *name, float val) const 1.430 +{ 1.431 + return set_uniform(get_uniform_location(name), val); 1.432 +} 1.433 + 1.434 +bool ShaderProg::set_uniform(const char *name, const Vector2 &v) const 1.435 +{ 1.436 + return set_uniform(get_uniform_location(name), v); 1.437 +} 1.438 + 1.439 +bool ShaderProg::set_uniform(const char *name, const Vector3 &v) const 1.440 +{ 1.441 + return set_uniform(get_uniform_location(name), v); 1.442 +} 1.443 + 1.444 +bool ShaderProg::set_uniform(const char *name, const Vector4 &v) const 1.445 +{ 1.446 + return set_uniform(get_uniform_location(name), v); 1.447 +} 1.448 + 1.449 +bool ShaderProg::set_uniform(const char *name, const Matrix3x3 &m) const 1.450 +{ 1.451 + return set_uniform(get_uniform_location(name), m); 1.452 +} 1.453 + 1.454 +bool ShaderProg::set_uniform(const char *name, const Matrix4x4 &m) const 1.455 +{ 1.456 + return set_uniform(get_uniform_location(name), m); 1.457 +} 1.458 + 1.459 +static StType unist_type(GLenum type) 1.460 +{ 1.461 + switch(type) { 1.462 + case GL_FLOAT: 1.463 + return ST_FLOAT; 1.464 + case GL_FLOAT_VEC2: 1.465 + return ST_FLOAT2; 1.466 + case GL_FLOAT_VEC3: 1.467 + return ST_FLOAT3; 1.468 + case GL_FLOAT_VEC4: 1.469 + return ST_FLOAT4; 1.470 + case GL_INT: 1.471 + case GL_SAMPLER_2D: 1.472 + case GL_SAMPLER_CUBE: 1.473 +#if !GL_ES_VERSION_2_0 1.474 + case GL_SAMPLER_1D: 1.475 + case GL_SAMPLER_3D: 1.476 + case GL_SAMPLER_1D_SHADOW: 1.477 + case GL_SAMPLER_2D_SHADOW: 1.478 +#endif 1.479 + return ST_INT; 1.480 + case GL_INT_VEC2: 1.481 + return ST_INT2; 1.482 + case GL_INT_VEC3: 1.483 + return ST_INT3; 1.484 + case GL_INT_VEC4: 1.485 + return ST_INT4; 1.486 + case GL_FLOAT_MAT3: 1.487 + return ST_MATRIX3; 1.488 + case GL_FLOAT_MAT4: 1.489 + return ST_MATRIX4; 1.490 + default: 1.491 + break; 1.492 + } 1.493 + return ST_UNKNOWN; 1.494 +} 1.495 + 1.496 +void ShaderProg::cache_state_uniforms() const 1.497 +{ 1.498 + if(!glIsProgram(prog)) { 1.499 + return; 1.500 + } 1.501 + 1.502 + int num_uni; 1.503 + glGetProgramiv(prog, GL_ACTIVE_UNIFORMS, &num_uni); 1.504 + 1.505 + char name[256]; 1.506 + for(int i=0; i<num_uni; i++) { 1.507 + GLint sz; 1.508 + GLenum type; 1.509 + glGetActiveUniform(prog, i, sizeof name - 1, 0, &sz, &type, name); 1.510 + 1.511 + if(strstr(name, "st_") == name) { 1.512 + StateLocCache s; 1.513 + s.sidx = add_unistate(name, unist_type(type)); 1.514 + s.loc = glGetUniformLocation(prog, name); 1.515 + stloc_cache.push_back(s); 1.516 + } 1.517 + } 1.518 +} 1.519 + 1.520 +void ShaderProg::setup_state_uniforms() const 1.521 +{ 1.522 + for(size_t i=0; i<stloc_cache.size(); i++) { 1.523 + setup_unistate(stloc_cache[i].sidx, this, stloc_cache[i].loc); 1.524 + CHECKGLERR; 1.525 + } 1.526 +} 1.527 + 1.528 +// ---- ShaderSet ---- 1.529 +static Shader *load_shader(const char *fname, unsigned int type) 1.530 +{ 1.531 + Shader *sdr = new Shader; 1.532 + if(!sdr->load(fname, type)) { 1.533 + delete sdr; 1.534 + return 0; 1.535 + } 1.536 + return sdr; 1.537 +} 1.538 + 1.539 +static Shader *load_vertex_shader(const char *fname) 1.540 +{ 1.541 + return load_shader(fname, GL_VERTEX_SHADER); 1.542 +} 1.543 + 1.544 +static Shader *load_pixel_shader(const char *fname) 1.545 +{ 1.546 + return load_shader(fname, GL_FRAGMENT_SHADER); 1.547 +} 1.548 + 1.549 +#ifdef HAVE_GEOMETRY_SHADER 1.550 +static Shader *load_geom_shader(const char *fname) 1.551 +{ 1.552 + return load_shader(fname, GL_GEOMETRY_SHADER); 1.553 +} 1.554 +#endif 1.555 + 1.556 +#ifdef HAVE_TESSELATION_SHADER 1.557 +static Shader *load_tc_shader(const char *fname) 1.558 +{ 1.559 + return load_shader(fname, GL_TESS_CONTROL_SHADER); 1.560 +} 1.561 + 1.562 +static Shader *load_te_shader(const char *fname) 1.563 +{ 1.564 + return load_shader(fname, GL_TESS_EVALUATION_SHADER); 1.565 +} 1.566 +#endif 1.567 + 1.568 +static void destroy_shader(Shader *sdr) 1.569 +{ 1.570 + delete sdr; 1.571 +} 1.572 + 1.573 +ShaderSet::ShaderSet(unsigned int type) 1.574 + : DataSet<Shader*>(0, destroy_shader) 1.575 +{ 1.576 + this->type = type; 1.577 + 1.578 + switch(type) { 1.579 + case GL_VERTEX_SHADER: 1.580 + load = load_vertex_shader; 1.581 + break; 1.582 + 1.583 + case GL_FRAGMENT_SHADER: 1.584 + load = load_pixel_shader; 1.585 + break; 1.586 + 1.587 +#ifdef HAVE_GEOMETRY_SHADER 1.588 + case GL_GEOMETRY_SHADER: 1.589 + load = load_geom_shader; 1.590 + break; 1.591 +#endif 1.592 + 1.593 +#ifdef HAVE_TESSELATION_SHADER 1.594 + case GL_TESS_CONTROL_SHADER: 1.595 + load = load_tc_shader; 1.596 + break; 1.597 + 1.598 + case GL_TESS_EVALUATION_SHADER: 1.599 + load = load_te_shader; 1.600 + break; 1.601 +#endif 1.602 + 1.603 + default: 1.604 + error_log("ShaderSet constructed with invalid shader type!\n"); 1.605 + } 1.606 +} 1.607 + 1.608 +static struct { const char *name; int loc; } attr_loc[] = { 1.609 + {"attr_vertex", MESH_ATTR_VERTEX}, 1.610 + {"attr_normal", MESH_ATTR_NORMAL}, 1.611 + {"attr_tangent", MESH_ATTR_TANGENT}, 1.612 + {"attr_texcoord", MESH_ATTR_TEXCOORD}, 1.613 + {"attr_color", MESH_ATTR_COLOR}, 1.614 + {"attr_boneweights", MESH_ATTR_BONEWEIGHTS}, 1.615 + {"attr_boneidx", MESH_ATTR_BONEIDX} 1.616 +}; 1.617 + 1.618 +static void bind_standard_attr(const ShaderProg *prog) 1.619 +{ 1.620 + // we must link once to find out which are the active attributes 1.621 + glLinkProgram(prog->get_id()); 1.622 + 1.623 + int num_attr; 1.624 + glGetProgramiv(prog->get_id(), GL_ACTIVE_ATTRIBUTES, &num_attr); 1.625 + 1.626 + char name[256]; 1.627 + for(int i=0; i<num_attr; i++) { 1.628 + GLint sz; 1.629 + GLenum type; 1.630 + glGetActiveAttrib(prog->get_id(), i, sizeof name - 1, 0, &sz, &type, name); 1.631 + 1.632 + for(int j=0; j<(int)(sizeof attr_loc / sizeof *attr_loc); j++) { 1.633 + if(strcmp(name, attr_loc[j].name) == 0) { 1.634 + prog->set_attrib_location(name, attr_loc[j].loc); 1.635 + } 1.636 + } 1.637 + } 1.638 +} 1.639 + 1.640 + 1.641 +static const char *strtype(unsigned int type) 1.642 +{ 1.643 + switch(type) { 1.644 + case GL_VERTEX_SHADER: 1.645 + return "vertex"; 1.646 + case GL_FRAGMENT_SHADER: 1.647 + return "fragment"; 1.648 +#ifdef HAVE_GEOMETRY_SHADER 1.649 + case GL_GEOMETRY_SHADER: 1.650 + return "geometry"; 1.651 +#endif 1.652 +#ifdef HAVE_TESSELATION_SHADER 1.653 + case GL_TESS_CONTROL_SHADER: 1.654 + return "tesselation control"; 1.655 + case GL_TESS_EVALUATION_SHADER: 1.656 + return "tesselation evaluation"; 1.657 +#endif 1.658 + default: 1.659 + break; 1.660 + } 1.661 + return "<unknown>"; 1.662 +}