polyadvent

A game engine from scratch in C
git clone git://jb55.com/polyadvent
Log | Files | Refs | README

shader.c (14384B)


      1 
      2 #include <stdlib.h>
      3 #include <stdio.h>
      4 #include <assert.h>
      5 
      6 #include "file.h"
      7 #include "util.h"
      8 #include "gl.h"
      9 #include "debug.h"
     10 #include "shader.h"
     11 #include "common.h"
     12 
     13 #define MAX_LINES 4096
     14 
     15 static int file_buf_count = 0;
     16 static char *file_buffers[MAX_SHADER_INCLUDES];
     17 static char *file_names[MAX_SHADER_INCLUDES];
     18 static int file_name_lens[MAX_SHADER_INCLUDES];
     19 static char *line_buff[MAX_LINES];
     20 static u32 line_lens[MAX_LINES];
     21 
     22 static char *cached_file_contents(const char *filename) {
     23 	size_t len;
     24 	for (int i = 0; i < file_buf_count; i++) {
     25 		if (memcmp(file_names[i], filename, file_name_lens[i]) == 0) {
     26 			return file_buffers[i];
     27 		}
     28 	}
     29 
     30 	return (char*)file_contents(filename, &len);
     31 }
     32 
     33 static char *o_strsep(char **stringp, const char *delim) {
     34 	if (*stringp == NULL) { return NULL; }
     35 	char *token_start = *stringp;
     36 	*stringp = strpbrk(token_start, delim);
     37 	if (*stringp)
     38 		(*stringp)++;
     39 	return token_start;
     40 }
     41 
     42 static char **resolve_imports(char *contents, int *nlines, int level) {
     43 	char *line;
     44 	int nline = 0;
     45 	char *resolved_contents;
     46 	char fname_buf[32] = {0};
     47 
     48 	while ((line = o_strsep(&contents, "\n"))) {
     49 		nline++;
     50 		int line_len = contents - line;
     51 		int size = sizeof("#include");
     52 		if (memcmp(line, "#include ", size) == 0) {
     53 			char *filename = line + size;
     54 			int file_name_len = line_len - size - 1;
     55 			snprintf(fname_buf, 32, SHADER("%.*s"), file_name_len, filename);
     56 			file_name_lens[file_buf_count] = file_name_len;
     57 			file_names[file_buf_count] = filename;
     58 
     59 			/* printf("got include %.*s at line %d level %d ind %d\n", */
     60 			/*		   file_name_len, filename, nline, level, file_buf_count); */
     61 
     62 			// TODO (perf): cache file contents based on filename
     63 			resolved_contents = cached_file_contents(fname_buf);
     64 			file_buffers[file_buf_count] = resolved_contents;
     65 			file_buf_count++;
     66 			resolve_imports(resolved_contents, nlines, level + 1);
     67 		}
     68 		else {
     69 			line_buff[*nlines] = line;
     70 			line_lens[*nlines] = line_len;
     71 			(*nlines)++;
     72 			/* printf("%d %.*s", *nlines, line_len, line); */
     73 		}
     74 	}
     75 
     76 	return line_buff;
     77 }
     78 
     79 
     80 struct shader *get_program_shader(struct gpu_program *program, GLenum type)
     81 {
     82 	struct shader *shader;
     83 	for (int i = 0; i < program->n_shaders; i++) {
     84 		shader = &program->shaders[i];
     85 		if (shader->type == type)
     86 			return shader;
     87 	}
     88 
     89 	return NULL;
     90 }
     91 
     92 int make_shader(GLenum type, const char *filename, struct shader *shader) {
     93 	size_t length;
     94 	int nlines = 0;
     95 	GLchar *source = (GLchar *)file_contents(filename, &length);
     96 	if (!source)
     97 		return 0;
     98 
     99 	assert(file_buf_count == 0);
    100 	char **lines = resolve_imports(source, &nlines, 0);
    101 	
    102 	GLint shader_ok;
    103 	
    104 	shader->n_includes = 0;
    105 	shader->filename = filename;
    106 	shader->type = type;
    107 	shader->handle = glCreateShader(type);
    108 	
    109 	glShaderSource(shader->handle, nlines, (const char**)lines,
    110 		(const int*)line_lens);
    111 
    112 	check_gl();
    113 
    114   // shader dependency stuff
    115 	for (int i = 0; i < file_buf_count; ++i) {
    116 		assert(i < MAX_SHADER_INCLUDES);
    117 		char *p = shader->includes[shader->n_includes];
    118 		int name_len = file_name_lens[i];
    119 		/* printf("%.*s name len %d\n", name_len, file_names[i], name_len); */
    120 		snprintf(p, MAX_INCLUDE_FNAME_LEN, SHADER("%.*s"), name_len, file_names[i]);
    121 		assert(name_len < MAX_INCLUDE_FNAME_LEN);
    122 		shader->include_mtimes[shader->n_includes] = file_mtime(p);
    123 		/* printf("'%s' included in '%s'\n", p, shader->filename); */
    124 		shader->n_includes++;
    125 		free(file_buffers[i]);
    126 	}
    127 	free(source);
    128 
    129 	file_buf_count = 0;
    130 
    131 	glCompileShader(shader->handle);
    132 	check_gl();
    133 
    134 	glGetShaderiv(shader->handle, GL_COMPILE_STATUS, &shader_ok);
    135 	check_gl();
    136 
    137 	if (!shader_ok) {
    138 		fprintf(stderr, "Failed to compile %s:\n", filename);
    139 		show_shader_info_log(shader->handle);
    140 		glDeleteShader(shader->handle);
    141 		check_gl();
    142 		return 0;
    143 	}
    144 
    145 	shader->load_mtime = file_mtime(shader->filename);
    146 
    147 	return 1;
    148 }
    149 
    150 #define N_SHADERS 3
    151 
    152 void init_gpu_program(struct gpu_program *program) {
    153 	memset(program, 0, sizeof(*program));
    154 }
    155 
    156 gpu_addr link_attribute(struct gpu_program *program, const char *name,
    157 				   enum vertex_attr attr)
    158 {
    159 	program->vertex_attrs[attr] =
    160 		(gpu_addr)glGetAttribLocation(program->handle, name);
    161 	program->active_attributes |= 1 << attr;
    162 	check_gl();
    163 	return program->vertex_attrs[attr];
    164 }
    165 
    166 static inline struct lens *get_uniform_lens(struct gpu_program *program,
    167 		int binding_id, int lens_id)
    168 {
    169 	struct structure_binding *binding =
    170 		&program->bindings[binding_id];
    171 
    172 	return &binding->lenses[lens_id];
    173 }
    174 
    175 void add_uniform(struct gpu_program *program,
    176 		 int id,
    177 		 int binding_id,
    178 		 int lens_id)
    179 {
    180 	struct uniform *var = &program->uniforms[id];
    181 	struct lens *lens = get_uniform_lens(program, binding_id, lens_id);
    182 
    183 	var->location = glGetUniformLocation(program->handle, lens->name);
    184 	var->id = id;
    185 	var->binding_id = binding_id;
    186 	var->lens_id = lens_id;
    187 
    188 	if (var->location == -1) {
    189 		debug("warn: could not find uniform location: %s in program %s\n",
    190 			lens->name, program->name);
    191 	}
    192 	/* assert(var->location != -1); */
    193 	check_gl();
    194 }
    195 
    196 static GLenum datatype_to_gl_type(enum datatype type)
    197 {
    198 	switch (type) {
    199 	case DATA_ID_INT: return GL_INT;
    200 	case DATA_ID_FLOAT: return GL_FLOAT;
    201 	case DATA_ID_VEC2: return GL_FLOAT_VEC2;
    202 	case DATA_ID_VEC3: return GL_FLOAT_VEC3;
    203 	case DATA_ID_VEC3P: return GL_FLOAT_VEC3;
    204 	case DATA_ID_MAT3: return GL_FLOAT_MAT3;
    205 	case DATA_ID_MAT4: return GL_FLOAT_MAT4;
    206 	case DATA_ID_MAT4P: return GL_FLOAT_MAT4;
    207 	}
    208 
    209 	return -1;
    210 }
    211 
    212 static const char *gl_type_str(GLenum type)
    213 {
    214 	switch (type) {
    215 	case GL_BYTE: return "byte";
    216 	case GL_UNSIGNED_BYTE: return "ubyte";
    217 	case GL_SHORT: return "short";
    218 	case GL_UNSIGNED_SHORT: return "ushort";
    219 	case GL_INT: return "int";
    220 	case GL_UNSIGNED_INT: return "uint";
    221 	case GL_FLOAT: return "float";
    222 	case GL_FLOAT_VEC2: return "float_vec2";
    223 	case GL_FLOAT_VEC3: return "float_vec3";
    224 	case GL_FLOAT_MAT3: return "float_mat3";
    225 	case GL_FLOAT_MAT4: return "float_mat4";
    226 	case GL_DOUBLE: return "double";
    227 	case GL_TEXTURE_2D: return "texture2d";
    228 	case GL_TEXTURE_3D: return "texture3d";
    229 	case GL_SAMPLER_2D: return "sampler2d";
    230 	case GL_SAMPLER_CUBE: return "samplerCube";
    231 	default: return "unknown";
    232 	}
    233 }
    234 
    235 int vertex_attr_from_name(const char *name, enum vertex_attr *attr)
    236 {
    237 	if (!strcmp(name, "position")) {
    238 		*attr = va_position;
    239 		return 1;
    240 	} else if (!strcmp(name, "color")) {
    241 		*attr = va_color;
    242 		return 1;
    243 	} else if (!strcmp(name, "normal")) {
    244 		*attr = va_normal;
    245 		return 1;
    246 	} else if (!strcmp(name, "index")) {
    247 		*attr = va_index;
    248 		return 1;
    249 	} else if (!strncmp(name, "tex_coord", 9)) {
    250 		*attr = va_tex_coord;
    251 		return 1;
    252 	}
    253 	return 0;
    254 }
    255 
    256 void create_attribute_bindings(struct gpu_program *program)
    257 {
    258 	int i, size, length, count = 0;
    259 	char name[64];
    260 	GLenum type;
    261 	enum vertex_attr attr;
    262 
    263 	glGetProgramiv(program->handle, GL_ACTIVE_ATTRIBUTES, &count);
    264 	program->active_attributes = 0;
    265 
    266 	for (i = 0; i < count; i++) {
    267 		glGetActiveAttrib(program->handle, (GLuint)i, sizeof(name),
    268 				&length, &size, &type, name);
    269 
    270 		if (!vertex_attr_from_name(name, &attr)) {
    271 			printf("unknown vertex attribute: '%s'\n", name);
    272 			rtassert(0, "");
    273 		}
    274 
    275 		gpu_addr addr = link_attribute(program, name, attr);
    276 		debug("%s: linking %s to %d\n", program->name, name, addr);
    277 	}
    278 }
    279 
    280 void create_uniform_bindings(struct gpu_program *program,
    281 		struct structure_binding *bindings,
    282 		int num_bindings)
    283 {
    284 	int i, binding_id, lens_id, size, length, count = 0, unis = 0;
    285 	GLenum type, lens_type;
    286 	struct lens *lens;
    287 	struct structure_binding *binding;
    288 	unsigned char *p;
    289 	char name[64];
    290 
    291 	program->bindings = bindings;
    292 	program->num_bindings = num_bindings;
    293 
    294 	glGetProgramiv(program->handle, GL_ACTIVE_UNIFORMS, &count);
    295 
    296 	for (i = 0; i < count; i++) {
    297 		glGetActiveUniform(program->handle, (GLuint)i, sizeof(name),
    298 				&length, &size, &type, name);
    299 
    300 		debug("%s: var '%s' :: %s\n", program->name, name,
    301 				gl_type_str(type));
    302 		// TODO: bind sampler uniforms
    303 		if (type == GL_SAMPLER_2D || type == GL_SAMPLER_CUBE) {
    304 			debug("create_uniform_bindings: skipping %s :: %s\n",
    305 					name, gl_type_str(type));
    306 			continue;
    307 		}
    308 
    309 		for (binding_id = 0; binding_id < num_bindings; binding_id++) {
    310 			binding = &bindings[binding_id];
    311 		for (lens_id = 0; lens_id < binding->num_lenses; lens_id++) {
    312 			lens = &binding->lenses[lens_id];
    313 			if (!strcmp(name, lens->name)) {
    314 				lens_type = datatype_to_gl_type(lens->type);
    315 				if (type != lens_type) {
    316 					printf("shader var type mismatch: %s shader(%s) != struct(%s)\n",
    317 							name,
    318 							gl_type_str(type),
    319 							gl_type_str(lens_type));
    320 					assert(0);
    321 				}
    322 				assert(type == datatype_to_gl_type(lens->type));
    323 				add_uniform(program, unis++, binding_id, lens_id);
    324 				goto cont;
    325 			}
    326 		}
    327 		}
    328 		
    329 		printf("bind_uniforms: could not bind uniform '%s' in %s\n",
    330 				name, program->name);
    331 		assert(0);
    332 	cont:
    333 		;
    334 	}
    335 
    336 	program->num_uniforms = unis;
    337 }
    338 
    339 static void bind_uniform_lens(struct lens *lens, int location, void *structure)
    340 {
    341 	float f;
    342 	float *fs;
    343 	int i;
    344 
    345 	switch (lens->type) {
    346 	case DATA_ID_FLOAT:
    347 		f = get_lens_float(lens, structure);
    348 		glUniform1f(location, f);
    349 		check_gl();
    350 		break;
    351 
    352 	case DATA_ID_INT:
    353 		i = get_lens_int(lens, structure);
    354 		glUniform1i(location, i);
    355 		check_gl();
    356 		break;
    357 
    358 	case DATA_ID_VEC2:
    359 		fs = get_lens_float_array(lens, structure);
    360 		glUniform2f(location, fs[0], fs[1]);
    361 		check_gl();
    362 		break;
    363 
    364 	case DATA_ID_VEC3:
    365 		fs = get_lens_float_array(lens, structure);
    366 		glUniform3f(location, fs[0], fs[1], fs[2]);
    367 		check_gl();
    368 		break;
    369 
    370 	case DATA_ID_VEC3P:
    371 		fs = get_lens_ptr(lens, structure);
    372 		glUniform3f(location, fs[0], fs[1], fs[2]);
    373 		check_gl();
    374 		break;
    375 
    376 	case DATA_ID_MAT3:
    377 		fs = get_lens_float_array(lens, structure);
    378 		glUniformMatrix3fv(location, 1, 0, fs);
    379 		check_gl();
    380 		break;
    381 
    382 	case DATA_ID_MAT4P:
    383 		fs = get_lens_ptr(lens, structure);
    384 		glUniformMatrix4fv(location, 1, 0, fs);
    385 		check_gl();
    386 		break;
    387 
    388 	case DATA_ID_MAT4:
    389 		fs = get_lens_float_array(lens, structure);
    390 		glUniformMatrix4fv(location, 1, 0, fs);
    391 		check_gl();
    392 		break;
    393 	}
    394 }
    395 
    396 void bind_uniforms(struct gpu_program *program, void **structures, int num_structs)
    397 {
    398 	int i;
    399 	struct uniform *uniform;
    400 	struct lens *lens;
    401 	struct structure_binding *binding;
    402 	void *structure;
    403 	void *data;
    404 
    405 	if (num_structs < program->num_bindings) {
    406 		debug("%s: num_structs(%d) < program->num_bindings(%d)\n",
    407 				program->name, num_structs, program->num_bindings);
    408 	}
    409 	assert(num_structs >= program->num_bindings);
    410 
    411 	assert(program->name);
    412 	/*
    413 	debug("bind_uniforms: %s num_uniforms %d\n",
    414 			program->name, program->num_uniforms);
    415 			*/
    416 	for (i = 0; i < program->num_uniforms; i++) {
    417 		uniform = &program->uniforms[i];
    418 		binding = &program->bindings[uniform->binding_id];
    419 		lens = &binding->lenses[uniform->lens_id];
    420 		/*
    421 		debug("%s: binding %d %s binding_id:%d lens_id:%d\n",
    422 				program->name, i,
    423 				lens->name, uniform->binding_id,
    424 				uniform->lens_id);
    425 				*/
    426 		structure = structures[uniform->binding_id];
    427 		bind_uniform_lens(lens, uniform->location, structure);
    428 	}
    429 }
    430 
    431 static void link_uniform(struct gpu_program *program, struct uniform *u)
    432 {
    433 	struct lens *lens = get_uniform_lens(program, u->binding_id, u->lens_id);
    434 	u->location = glGetUniformLocation(program->handle, lens->name);
    435 	if (u->location == -1) {
    436 		debug("warn: could not find uniform location: %s in program %s\n",
    437 			lens->name, program->name);
    438 	}
    439 	/* assert(u->location != -1); */
    440 	check_gl();
    441 }
    442 
    443 int reload_program(struct gpu_program *program, struct gpu_program *programs)
    444 {
    445 	int ok;
    446 
    447 	int n_shaders = program->n_shaders;
    448 	struct gpu_program new_program;
    449 	struct shader new_shaders[n_shaders];
    450 	struct shader *new_shaders_p[n_shaders];
    451 
    452 	init_gpu_program(&new_program);
    453 
    454 	int changes[n_shaders];
    455 
    456 	for (int i = 0; i < n_shaders; i++) {
    457 		changes[i] = 0;
    458 		struct shader *shader = &program->shaders[i];
    459 		new_shaders_p[i] = shader;
    460 
    461 		time_t mtime = file_mtime(shader->filename);
    462 		int changed = mtime != shader->load_mtime;
    463 
    464 		for (int j = 0; j < shader->n_includes; ++j) {
    465 			time_t include_mtime = shader->include_mtimes[j];
    466 			time_t new_mtime = file_mtime(shader->includes[j]);
    467 			changed |= include_mtime != new_mtime;
    468 		}
    469 
    470 		changes[i] = changed;
    471 
    472 		if (changed) {
    473 			/* printf("making shader %s\n", shader->filename); */
    474 			ok = make_shader(shader->type, shader->filename, &new_shaders[i]);
    475 			if (!ok) {
    476 				// clean up all the new shaders we've created so far
    477 				for (int k = 0; k < i; k++)
    478 					glDeleteShader(new_shaders[k].handle);
    479 				return 0;
    480 			}
    481 			new_shaders_p[i] = &new_shaders[i];
    482 		}
    483 	}
    484 
    485 	int any_changes = 0;
    486 	for (int i = 0; i < n_shaders; i++)
    487 		any_changes |= changes[i];
    488 
    489 	if (!any_changes) {
    490 		return 2;
    491 	}
    492 
    493 	ok = make_program_from_shaders(program->name, new_shaders_p,
    494 				       n_shaders, &new_program,
    495 				       program->bindings,
    496 				       program->num_bindings);
    497 
    498 	if (!ok) {
    499 		for (int i = 0; i < n_shaders; i++) {
    500 			glDeleteShader(program->shaders[i].handle);
    501 		}
    502 		return 0;
    503 	}
    504 
    505 	*program = new_program;
    506 	return 1;
    507 }
    508 
    509 int
    510 make_program(const char *name, struct shader *vertex, struct shader *fragment,
    511 	     struct gpu_program *program, struct structure_binding *bindings,
    512 	     int num_bindings)
    513 {
    514 	struct shader *shaders[] = { vertex, fragment };
    515 	return make_program_from_shaders(name, shaders, 2, program, bindings,
    516 			num_bindings);
    517 }
    518 
    519 int make_program_from_shaders(const char *name, struct shader **shaders,
    520 			      int n_shaders, struct gpu_program *program,
    521 			      struct structure_binding *bindings,
    522 			      int num_bindings)
    523 {
    524 	GLint program_ok;
    525 
    526 	// TODO: relax these constraints
    527 	program->handle = glCreateProgram();
    528 	check_gl();
    529 	program->n_shaders = n_shaders;
    530 	program->name = name;
    531 
    532 	assert(n_shaders <= MAX_SHADERS);
    533 	for (int i = 0; i < n_shaders; i++) {
    534 		struct shader *shader = shaders[i];
    535 		program->shaders[i] = *shader;
    536 		/* printf("attaching shader %s\n", shader->filename); */
    537 		/* for (int j = 0; j < shader->n_includes; ++j) { */
    538 		/*	   printf("attaching shader %s dep %s \n", shader->filename, */
    539 		/*			  shader->includes[j]); */
    540 		/* } */
    541 		glAttachShader(program->handle, shader->handle);
    542 		check_gl();
    543 	}
    544 
    545 	glLinkProgram(program->handle);
    546 	check_gl();
    547 
    548 	glGetProgramiv(program->handle, GL_LINK_STATUS, &program_ok);
    549 
    550 	if (!program_ok) {
    551 		fprintf(stderr, "Failed to link shader program:\n");
    552 		show_program_info_log(program->handle);
    553 		glDeleteProgram(program->handle);
    554 		return 0;
    555 	}
    556 
    557 	if (num_bindings > 0)
    558 		create_uniform_bindings(program, bindings, num_bindings);
    559 
    560 	create_attribute_bindings(program);
    561 
    562 	return 1;
    563 }