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 |