src/shader.c

changeset 223
4f32c7755138
parent 222
2cb9a71df7a6
child 224
f72b80448413
equal deleted inserted replaced
222:2cb9a71df7a6 223:4f32c7755138
35 #include <stdio.h> 35 #include <stdio.h>
36 #include <GL/glew.h> 36 #include <GL/glew.h>
37 #include <cx/buffer.h> 37 #include <cx/buffer.h>
38 #include <cx/streams.h> 38 #include <cx/streams.h>
39 39
40 static char *asc_shader_load_code_file(const char *filename) {
41 cxmutstr fpath = asc_filesystem_combine_paths(cx_strcast(asc_context.shader_path), cx_str(filename));
42 asc_dprintf("Load shader code from %" CX_PRIstr, CX_SFMT(fpath));
43 FILE *f = fopen(fpath.ptr, "r");
44 cx_strfree(&fpath);
45 CxBuffer buffer;
46 if (f == NULL || cxBufferInit(&buffer, NULL, 1024, cxDefaultAllocator, CX_BUFFER_AUTO_EXTEND)) {
47 return NULL;
48 }
49 cx_stream_copy(f, &buffer, (cx_read_func) fread, cxBufferWriteFunc);
50 cxBufferPut(&buffer, '\0');
51 cxBufferShrink(&buffer, 0);
52 return buffer.space;
53 }
54
40 /** 55 /**
41 * Compiles a shader from the given source code. 56 * Compiles a shader from the given source code.
42 * 57 *
43 * The ID of the returned shader will be zero when something went wrong. 58 * The ID of the returned shader will be zero when something went wrong.
44 * 59 *
45 * @param type the shader type (use the GL enum) 60 * @param type the shader type (use the GL enum)
46 * @param code the source code 61 * @param code the source code information
47 * @param code_pp the optional preprocessor code
48 * @param code_pp_list list of optional preprocessor code
49 * @param code_pp_list_select selection flags for the preprocessor code list
50 * @return the compiled shader 62 * @return the compiled shader
51 */ 63 */
52 static unsigned asc_shader_compile(unsigned int type, const char *code, const char *code_pp, 64 static unsigned asc_shader_compile(unsigned int type, struct asc_shader_code_s code) {
53 const char * const *code_pp_list, unsigned short code_pp_list_select) { 65 // try to load the source code, first
66 char *source_text = asc_shader_load_code_file(code.source_file);
67 if (source_text == NULL) {
68 asc_error("Failed to load shader source code from %s", code.source_file);
69 return 0;
70 }
71
72 // create the shader
54 GLuint id = glCreateShader(type); 73 GLuint id = glCreateShader(type);
55 if (id == 0) { 74 if (id == 0) {
56 asc_error("glCreateShader failed: %s", glGetError()); 75 asc_error("glCreateShader failed: %s", glGetError());
57 return 0; 76 return 0;
58 } 77 }
59 78
60 GLint success; 79 const unsigned max_code_parts = 128+3; // version + preamble + line 1 + source text
61 const char *code_array[20]; 80 const char *code_array[max_code_parts];
62 GLsizei code_count = 0; 81 GLsizei code_count = 0;
63 code_array[code_count++] = "#version 400 core\n"; 82 code_array[code_count++] = "#version 400 core\n";
64 if (code_pp != NULL) code_array[code_count++] = code_pp; 83 uint64_t test_flag = 1;
65 unsigned test_flag = 1;
66 unsigned select_index = 0; 84 unsigned select_index = 0;
67 while (test_flag <= code_pp_list_select) { 85 while (test_flag <= code.preamble_code_flags) {
68 if (asc_test_flag(code_pp_list_select, test_flag)) { 86 if (asc_test_flag(code.preamble_code_flags, test_flag)) {
69 code_array[code_count++] = code_pp_list[select_index]; 87 code_array[code_count++] = code.preamble_code[select_index];
88 code_array[code_count++] = "\n";
70 } 89 }
71 select_index++; 90 select_index++;
72 test_flag <<= 1; 91 test_flag <<= 1;
73 } 92 }
74 code_array[code_count++] = "\n#line 1\n"; 93 code_array[code_count++] = "#line 1\n";
75 code_array[code_count++] = code; 94 code_array[code_count++] = source_text;
76
77 // compute the lengths
78 GLint length_array[20];
79 for (int i = 0; i < code_count ; i++) {
80 length_array[i] = (GLint) strlen(code_array[i]);
81 }
82 95
83 // compile 96 // compile
84 glShaderSource(id, code_count, code_array, length_array); 97 glShaderSource(id, code_count, code_array, NULL);
85 glCompileShader(id); 98 glCompileShader(id);
99
100 // free the source text
101 cxFreeDefault(source_text);
102
103 // check the compilation result
104 GLint success;
86 glGetShaderiv(id, GL_COMPILE_STATUS, &success); 105 glGetShaderiv(id, GL_COMPILE_STATUS, &success);
87 if (success) { 106 if (success) {
88 asc_dprintf("Shader %u compiled", id); 107 asc_dprintf("Shader %u compiled", id);
89 return id; 108 return id;
90 } else { 109 } else {
91 char *log = malloc(1024); 110 char *log = cxMallocDefault(1024);
92 glGetShaderInfoLog(id, 1024, NULL, log); 111 glGetShaderInfoLog(id, 1024, NULL, log);
93 glDeleteShader(id); 112 glDeleteShader(id);
94 asc_error("Shader %u compilation failed.\n%s", id, log); 113 asc_error("Shader %u compilation failed.\n%s", id, log);
95 free(log); 114 cxFreeDefault(log);
96 return 0; 115 return 0;
97 } 116 }
98 } 117 }
99 118
100 /** 119 /**
156 175
157 static bool asc_shader_invalid(const AscShaderProgram *program) { 176 static bool asc_shader_invalid(const AscShaderProgram *program) {
158 return program == NULL || program->gl_id == 0; 177 return program == NULL || program->gl_id == 0;
159 } 178 }
160 179
161 AscShaderProgram *asc_shader_create(AscShaderCodeInfo code_info, size_t mem_size, asc_shader_init_func init_func, int flags) { 180 AscShaderProgram *asc_shader_create(AscShaderCodes code_info, size_t mem_size, asc_shader_init_func init_func, int flags) {
162 AscShaderCodes codes; 181 unsigned shader[2];
163 if (asc_shader_load_code_files(code_info, &codes)) { 182 unsigned n = 0;
164 asc_error("Loading shader failed."); 183 if (code_info.vtx.source_file != NULL) {
165 return NULL; 184 shader[n++] = asc_shader_compile(GL_VERTEX_SHADER, code_info.vtx);
166 } 185 }
186 if (code_info.frag.source_file != NULL) {
187 shader[n++] = asc_shader_compile(GL_FRAGMENT_SHADER, code_info.frag);
188 }
189 #ifndef NDEBUG
190 // in debug mode, exit early when a shader compilation failed
191 for (unsigned i = 0 ; i < n ; i++) {
192 if (shader[i] == 0) {
193 asc_error("Shader %u compilation failed.", i);
194 for (unsigned j = 0; j < n; j++) {
195 if (shader[j] > 0) {
196 asc_dprintf("Delete shader: %u", shader[j]);
197 glDeleteShader(shader[j]);
198 }
199 }
200 return NULL;
201 }
202 }
203 #endif
167 AscShaderProgram *shader_program = cxZallocDefault(mem_size); 204 AscShaderProgram *shader_program = cxZallocDefault(mem_size);
168 { 205 if (shader_program != NULL) {
169 unsigned shader[2];
170 unsigned n = 0;
171 // TODO: clean up this pp mess by introducing proper nested structs
172 if (codes.vtx) {
173 shader[n++] = asc_shader_compile(GL_VERTEX_SHADER, codes.vtx, codes.vtx_pp, codes.vtx_pp_list, codes.vtx_pp_list_select);
174 }
175 if (codes.frag) {
176 shader[n++] = asc_shader_compile(GL_FRAGMENT_SHADER, codes.frag, codes.frag_pp, codes.frag_pp_list, codes.frag_pp_list_select);
177 }
178 asc_shader_link(shader, n, shader_program); 206 asc_shader_link(shader, n, shader_program);
179 } 207 }
180 asc_shader_free_codes(codes);
181 if (asc_error_catch_gl("Compile and link shader") || asc_shader_invalid(shader_program)) { 208 if (asc_error_catch_gl("Compile and link shader") || asc_shader_invalid(shader_program)) {
182 asc_shader_free(shader_program); 209 asc_shader_free(shader_program);
183 return NULL; 210 return NULL;
184 } 211 }
185 212
205 asc_active_glctx->active_program = shader->gl_id; 232 asc_active_glctx->active_program = shader->gl_id;
206 glUseProgram(shader->gl_id); 233 glUseProgram(shader->gl_id);
207 glUniformMatrix4fv(shader->projection, 1, GL_FALSE, camera->projection); 234 glUniformMatrix4fv(shader->projection, 1, GL_FALSE, camera->projection);
208 glUniformMatrix4fv(shader->view, 1, GL_FALSE, camera->view); 235 glUniformMatrix4fv(shader->view, 1, GL_FALSE, camera->view);
209 return asc_error_catch_gl("Activating shader"); 236 return asc_error_catch_gl("Activating shader");
210 }
211
212 static int asc_shader_load_code_file(const char *filename, char **code) {
213 if (filename == NULL) {
214 *code = NULL;
215 return 0;
216 }
217 cxmutstr fpath = asc_filesystem_combine_paths(cx_strcast(asc_context.shader_path), cx_str(filename));
218 asc_dprintf("Load shader code from %" CX_PRIstr, CX_SFMT(fpath));
219 FILE *f = fopen(fpath.ptr, "r");
220 cx_strfree(&fpath);
221 if (f == NULL) return -1;
222 CxBuffer buffer;
223 cxBufferInit(&buffer, NULL, 1024, NULL, CX_BUFFER_AUTO_EXTEND);
224 cx_stream_copy(f, &buffer, (cx_read_func) fread, cxBufferWriteFunc);
225 cxBufferPut(&buffer, '\0');
226 cxBufferShrink(&buffer, 0);
227 *code = buffer.space;
228 return *code == NULL ? -1 : 0;
229 }
230
231 int asc_shader_load_code_files(AscShaderCodeInfo info, AscShaderCodes *codes) {
232 int ret = 0;
233 ret |= asc_shader_load_code_file(info.files.vtx, &codes->vtx);
234 codes->vtx_pp = info.defines.vtx;
235 codes->vtx_pp_list = info.defines.vtx_list;
236 codes->vtx_pp_list_select = info.defines.vtx_list_select;
237 ret |= asc_shader_load_code_file(info.files.frag, &codes->frag);
238 codes->frag_pp = info.defines.frag;
239 codes->frag_pp_list = info.defines.frag_list;
240 codes->frag_pp_list_select = info.defines.frag_list_select;
241 return ret;
242 }
243
244 void asc_shader_free_codes(AscShaderCodes codes) {
245 cxFreeDefault(codes.vtx);
246 cxFreeDefault(codes.frag);
247 } 237 }
248 238
249 const AscShaderProgram *asc_shader_register(unsigned int id, asc_shader_create_func create_func, int create_flags) { 239 const AscShaderProgram *asc_shader_register(unsigned int id, asc_shader_create_func create_func, int create_flags) {
250 AscGLContext *glctx = asc_active_glctx; 240 AscGLContext *glctx = asc_active_glctx;
251 #ifndef NDEBUG 241 #ifndef NDEBUG

mercurial