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