graphics

experiments with opengl3.2/ogles3.3 on linux and win7
git clone http://frotz.net/git/graphics.git
Log | Files | Refs

shaders.cc (7123B)


      1 /* Copyright 2013 Brian Swetland <swetland@frotz.net>
      2  *
      3  * Licensed under the Apache License, Version 2.0 (the "License");
      4  * you may not use this file except in compliance with the License.
      5  * You may obtain a copy of the License at
      6  *
      7  *     http://www.apache.org/licenses/LICENSE-2.0
      8  *
      9  * Unless required by applicable law or agreed to in writing, software
     10  * distributed under the License is distributed on an "AS IS" BASIS,
     11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12  * See the License for the specific language governing permissions and
     13  * limitations under the License.
     14  */
     15 
     16 #include <stdio.h>
     17 #include <stdlib.h>
     18 
     19 #include "app.h"
     20 #include "util.h"
     21 #include "matrix.h"
     22 
     23 #define INCLUDE_SHADER_GLOBALS 1
     24 #include "shared.h"
     25 
     26 #define ENABLE_SHADER_CACHE 0
     27 
     28 static void dump_compile_error(unsigned id) {
     29 	int len;
     30 	char *buf;
     31 	glGetShaderiv(id, GL_INFO_LOG_LENGTH, &len);
     32 	buf = (char*) malloc(len + 1);
     33 	if (buf != 0) {
     34 		memset(buf, 0, len);
     35 		glGetShaderInfoLog(id, len, &len, buf);
     36 		buf[len] = 0;
     37 		printx("-- shader compiler error --\n%s\n", buf);
     38 		free(buf);
     39 	}
     40 }
     41 
     42 static void dump_link_error(unsigned id) {
     43 	int len;
     44 	char *buf;
     45 	glGetProgramiv(id, GL_INFO_LOG_LENGTH, &len);
     46 	buf = (char*) malloc(len + 1);
     47 	if (buf != 0) {
     48 		memset(buf, 0, len);
     49 		glGetProgramInfoLog(id, len, &len, buf);
     50 		buf[len] = 0;
     51 		printx("-- shader link error --\n%s\n", buf);
     52 		free(buf);
     53 	}
     54 }
     55 
     56 int Program::link(VertexShader *vs, PixelShader *ps) {
     57 	return link(vs, NULL, ps);
     58 }
     59 
     60 static const char *_blocknames[] = {
     61 	"block0", "block1", "block2", "block3",
     62 };
     63 static const char *_samplernames[] = {
     64 	"sampler0", "sampler1", "sampler2", "sampler3",
     65 };
     66 
     67 int Program::link(VertexShader *vs, GeometryShader *gs, PixelShader *ps) {
     68 	unsigned n;
     69 	int r;
     70 	n = glCreateProgram();
     71 	glAttachShader(n, vs->id);
     72 	if (gs)
     73 		glAttachShader(n, gs->id);
     74 	glAttachShader(n, ps->id);
     75 	glLinkProgram(n);
     76 	glGetProgramiv(n, GL_LINK_STATUS, &r);
     77 	if (!r) {
     78 		dump_link_error(n);
     79 		glDeleteProgram(n);
     80 		return error("shader program link error");
     81 	}
     82 	if (id)
     83 		glDeleteProgram(id);
     84 	id = n;
     85 
     86 	/* bind uniform block indices to bindpoints based on their name */
     87 	for (n = 0; n < sizeof(_blocknames) / sizeof(_blocknames[0]); n++) {
     88 		unsigned idx = glGetUniformBlockIndex(id, _blocknames[n]);
     89 		if (idx != GL_INVALID_INDEX) {
     90 			printx("found %s @ %d\n", _blocknames[n], idx);
     91 			glUniformBlockBinding(id, idx, n);
     92 		}
     93 	}
     94 
     95 	return 0;
     96 }
     97 
     98 /* defer final link steps until the first time glUseProgram() is called, */
     99 void Program::bind() {
    100 	unsigned n;
    101 	int r;
    102 	for (n = 0; n < sizeof(_samplernames) / sizeof(_samplernames[0]); n++) {
    103 		r = glGetUniformLocation(id, _samplernames[n]);
    104 		if (r != -1) {
    105 			printx("found %s @ %d\n", _samplernames[n], r);
    106 			glUniform1i(r, n);
    107 		}
    108 	}
    109 	bound = 1;
    110 }
    111 
    112 int Program::load(const char *vsfn, const char *gsfn, const char *psfn) {
    113 	VertexShader vs;
    114 	GeometryShader gs;
    115 	PixelShader ps;
    116 	if (vs.load(vsfn))
    117 		return -1;
    118 	if (gs.load(gsfn))
    119 		return -1;
    120 	if (ps.load(psfn))
    121 		return -1;
    122 	return link(&vs, &gs, &ps);
    123 }
    124 
    125 int Program::load(const char *vsfn, const char *psfn) {
    126 	VertexShader vs;
    127 	PixelShader ps;
    128 	if (vs.load(vsfn))
    129 		return -1;
    130 	if (ps.load(psfn))
    131 		return -1;
    132 	return link(&vs, &ps);
    133 }
    134 
    135 struct section {
    136 	section *next;
    137 	unsigned lineno;
    138 	const char *name;
    139 	const char *str;
    140 	unsigned len;
    141 };
    142 
    143 struct source {
    144 	source *next;
    145 	const char *name;
    146 	section *sections;
    147 	section common;
    148 	const char *str;
    149 	unsigned len;
    150 };
    151 
    152 #if ENABLE_SHADER_CACHE
    153 static source *source_cache = NULL;
    154 #endif
    155 
    156 static struct source *load_shader_source(const char *fn) {
    157 	source *src;
    158 	section *part;
    159 	unsigned lineno = 1;
    160 	unsigned len;
    161 	char *x, *end, *tmp;
    162 	char buf[1024];
    163 
    164 	x = (char*) strchr(fn, '.');
    165 	if (x) {
    166 		memcpy(buf, fn, x-fn);
    167 		strcpy(buf + (x - fn), ".glsl");
    168 	} else {
    169 		sprintf(buf, "%s.glsl", fn);
    170 	}
    171 
    172 #if ENABLE_SHADER_CACHE
    173 	for (src = source_cache; src; src = src->next)
    174 		if (!strcmp(buf, src->name))
    175 			return src;
    176 #endif
    177 
    178 	src = new source;
    179 	src->sections = NULL;
    180 	part = &src->common;
    181 	part->name = "$common$";
    182 
    183 	x = (char*) load_file(buf, &len);
    184 	if (!x) {
    185 		delete src;
    186 		return NULL;
    187 	}
    188 	src->str = x;
    189 	src->len = len;
    190 
    191 	part->str = x;
    192 	part->lineno = lineno;
    193 
    194 	end = x + len;
    195 	while (x < end) {
    196 		if (*x++ != '\n')
    197 			continue;
    198 		lineno++;
    199 		if ((end - x) < 3)
    200 			continue;
    201 		if ((x[0] != '-') || (x[1] != '-'))
    202 			continue;
    203 		part->len = x - part->str;
    204 		part->next = src->sections;
    205 		src->sections = part;
    206 		*x = 0;
    207 		x += 2;
    208 
    209 		part = new section;
    210 		part->next = NULL;
    211 		part->name = "";
    212 
    213 		tmp = x;
    214 		while (x < end) {
    215 			if (*x == '\n') {
    216 				*x = 0;
    217 				while ((tmp < x) && (*tmp <= ' '))
    218 					tmp++;
    219 				part->name = tmp;
    220 				while (tmp < x) {
    221 					if (*tmp <= ' ')
    222 						*tmp = 0;
    223 					tmp++;
    224 				}
    225 				x++;
    226 				break;
    227 			}
    228 			x++;
    229 		}
    230 
    231 		lineno++;
    232 		part->str = x;
    233 		part->lineno = lineno;
    234 	}
    235 
    236 	part->len = x - part->str;
    237 	part->next = src->sections;
    238 	src->sections = part;
    239 
    240 #if 0
    241 	printx("[ source '%s' %d lines ]\n", buf, lineno);
    242 	for (part = src->sections; part; part = part->next)
    243 		printx("  [ section '%s' %d bytes @ %d]\n",
    244 			part->name, part->len, part->lineno);
    245 #endif
    246 
    247 	src->name = strdup(buf);
    248 
    249 #if ENABLE_SHADER_CACHE
    250 	src->next = source_cache;
    251 	source_cache = src;
    252 #endif
    253 	return src;
    254 }
    255 
    256 static int compile_shader_source(source *src, const char *name, const char *defines, unsigned id) {
    257 	char misc[128];
    258 	const char *data[5];
    259 	int size[5];
    260 	section *part;
    261 
    262 	for (part = src->sections; part; part = part->next) {
    263 		if (!strcmp(name, part->name)) {
    264 			printx("Loaded shader section '%s' from '%s'\n",
    265 				part->name, src->name);
    266 			sprintf(misc, "#line %d\n", part->lineno - 1);
    267 			data[0] = src->common.str;
    268 			size[0] = src->common.len;
    269 			data[1] = shader_globals;
    270 			size[1] = strlen(shader_globals);
    271 			data[2] = defines;
    272 			size[2] = strlen(defines);
    273 			data[3] = misc;
    274 			size[3] = strlen(misc);
    275 			data[4] = part->str;
    276 			size[4] = part->len;
    277 			glShaderSource(id, 5, data, size);
    278 			return 0;
    279 		}
    280 	}
    281 	return error("cannot find section '%s'", name);
    282 }
    283 
    284 static int _load_shader(unsigned *out, const char *fn, const char *defines, unsigned type) {
    285 	unsigned id;
    286 	source *src;
    287 	const char *x;
    288 	int r;
    289 
    290 	x = strchr(fn, '.');
    291 	if (x == 0)
    292 		return error("shader source '%s' has no section", fn);
    293 	x++;
    294 
    295 	src = load_shader_source(fn);
    296 	if (src == NULL)
    297 		return error("cannot load shader source '%s'", fn);
    298 
    299 	id = glCreateShader(type);
    300 	if (compile_shader_source(src, x, defines, id)) {
    301 		glDeleteShader(id);
    302 		return -1;
    303 	}
    304 	glCompileShader(id);
    305 	glGetShaderiv(id, GL_COMPILE_STATUS, &r);
    306 	if (!r) {
    307 		dump_compile_error(id);
    308 		glDeleteShader(id);
    309 		return error("shader '%s' compile error", fn);
    310 	}
    311 	if (*out)
    312 		glDeleteShader(*out);
    313 	*out = id;
    314 	return 0;
    315 }
    316 
    317 int VertexShader::load(const char *fn, const char *defines) {
    318 	return _load_shader(&id, fn, defines, GL_VERTEX_SHADER);
    319 }
    320 
    321 int PixelShader::load(const char *fn, const char *defines) {
    322 	return _load_shader(&id, fn, defines, GL_FRAGMENT_SHADER);
    323 }
    324 
    325 int GeometryShader::load(const char *fn, const char *defines) {
    326 	return _load_shader(&id, fn, defines, GL_GEOMETRY_SHADER);
    327 }
    328