]> git.tdb.fi Git - libs/gl.git/blob - source/core/program.cpp
Split Module into a base class and format-specific class
[libs/gl.git] / source / core / program.cpp
1 #include <algorithm>
2 #include <cstring>
3 #include <set>
4 #include <msp/core/hash.h>
5 #include <msp/core/maputils.h>
6 #include <msp/gl/extensions/arb_fragment_shader.h>
7 #include <msp/gl/extensions/arb_geometry_shader4.h>
8 #include <msp/gl/extensions/arb_shader_objects.h>
9 #include <msp/gl/extensions/arb_uniform_buffer_object.h>
10 #include <msp/gl/extensions/arb_vertex_shader.h>
11 #include <msp/gl/extensions/ext_gpu_shader4.h>
12 #include <msp/gl/extensions/nv_non_square_matrices.h>
13 #include <msp/io/print.h>
14 #include <msp/strings/format.h>
15 #include "buffer.h"
16 #include "error.h"
17 #include "misc.h"
18 #include "module.h"
19 #include "program.h"
20 #include "resources.h"
21 #include "shader.h"
22 #include "glsl/compiler.h"
23
24 using namespace std;
25
26 namespace Msp {
27 namespace GL {
28
29 Program::Program()
30 {
31         init();
32 }
33
34 Program::Program(const std::string &source)
35 {
36         init();
37
38         GlslModule mod;
39         mod.set_source(source);
40         add_stages(mod);
41
42         link();
43         module = 0;
44 }
45
46 Program::Program(const string &vert, const string &frag)
47 {
48         init();
49
50 #pragma GCC diagnostic push
51 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
52         attach_shader_owned(new VertexShader(vert));
53         attach_shader_owned(new FragmentShader(frag));
54 #pragma GCC diagnostic pop
55         link();
56 }
57
58 Program::Program(const Module &mod, const map<string, int> &spec_values)
59 {
60         init();
61         add_stages(mod, spec_values);
62         link();
63 }
64
65 void Program::init()
66 {
67         static Require _req(ARB_shader_objects);
68
69         id = glCreateProgram();
70         module = 0;
71         linked = false;
72 }
73
74 Program::~Program()
75 {
76         for(vector<unsigned>::iterator i=stage_ids.begin(); i!=stage_ids.end(); ++i)
77                 glDeleteShader(*i);
78         glDeleteProgram(id);
79 }
80
81 void Program::add_stages(const Module &mod, const map<string, int> &spec_values)
82 {
83         switch(mod.get_format())
84         {
85         case Module::GLSL: return add_glsl_stages(static_cast<const GlslModule &>(mod), spec_values);
86         default: throw invalid_argument("Program::add_stages");
87         }
88 }
89
90 unsigned Program::add_stage(GLenum type)
91 {
92         switch(type)
93         {
94         case GL_VERTEX_SHADER: { static Require _req(ARB_vertex_shader); } break;
95         case GL_GEOMETRY_SHADER: { static Require _req(ARB_geometry_shader4); } break;
96         case GL_FRAGMENT_SHADER: { static Require _req(ARB_fragment_shader); } break;
97         default: throw invalid_argument("Program::add_stage");
98         }
99
100         unsigned stage_id = glCreateShader(type);
101         stage_ids.push_back(stage_id);
102         glAttachShader(id, stage_id);
103
104         return stage_id;
105 }
106
107 void Program::add_glsl_stages(const GlslModule &mod, const map<string, int> &spec_values)
108 {
109         module = &mod;
110
111         SL::Compiler compiler;
112         compiler.set_source(mod.get_prepared_source(), "<module>");
113         compiler.specialize(spec_values);
114         compiler.compile(SL::Compiler::PROGRAM);
115 #ifdef DEBUG
116         string diagnostics = compiler.get_diagnostics();
117         if(!diagnostics.empty())
118                 IO::print("Program diagnostics:\n%s\n", diagnostics);
119 #endif
120
121         vector<SL::Stage::Type> stages = compiler.get_stages();
122         for(vector<SL::Stage::Type>::const_iterator i=stages.begin(); i!=stages.end(); ++i)
123         {
124                 unsigned stage_id = 0;
125                 switch(*i)
126                 {
127                 case SL::Stage::VERTEX: stage_id = add_stage(GL_VERTEX_SHADER); break;
128                 case SL::Stage::GEOMETRY: stage_id = add_stage(GL_GEOMETRY_SHADER); break;
129                 case SL::Stage::FRAGMENT: stage_id = add_stage(GL_FRAGMENT_SHADER); break;
130                 default: throw invalid_operation("Program::add_glsl_stages");
131                 }
132
133                 string stage_src = compiler.get_stage_glsl(*i);
134                 const char *src_ptr = stage_src.data();
135                 int src_len = stage_src.size();
136                 glShaderSource(stage_id, 1, &src_ptr, &src_len);
137
138                 if(*i==SL::Stage::VERTEX)
139                 {
140                         const map<string, unsigned> &attribs = compiler.get_vertex_attributes();
141                         for(map<string, unsigned>::const_iterator j=attribs.begin(); j!=attribs.end(); ++j)
142                                 glBindAttribLocation(id, j->second, j->first.c_str());
143                 }
144
145                 if(*i==SL::Stage::FRAGMENT && EXT_gpu_shader4)
146                 {
147                         const map<string, unsigned> &frag_outs = compiler.get_fragment_outputs();
148                         for(map<string, unsigned>::const_iterator j=frag_outs.begin(); j!=frag_outs.end(); ++j)
149                                 glBindFragDataLocation(id, j->second, j->first.c_str());
150                 }
151
152                 compile_glsl_stage(stage_id);
153         }
154 }
155
156 void Program::compile_glsl_stage(unsigned stage_id)
157 {
158         glCompileShader(stage_id);
159         bool compiled = get_shader_i(stage_id, GL_COMPILE_STATUS);
160
161         GLsizei info_log_len = get_shader_i(stage_id, GL_INFO_LOG_LENGTH);
162         string info_log(info_log_len+1, 0);
163         glGetShaderInfoLog(stage_id, info_log_len+1, &info_log_len, &info_log[0]);
164         info_log.erase(info_log_len);
165         if(module && module->get_format()==Module::GLSL)
166                 info_log = static_cast<const GlslModule *>(module)->get_source_map().translate_errors(info_log);
167
168         if(!compiled)
169                 throw compile_error(info_log);
170 #ifdef DEBUG
171         if(!info_log.empty())
172                 IO::print("Shader compile info log:\n%s", info_log);
173 #endif
174 }
175
176 #pragma GCC diagnostic push
177 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
178 void Program::attach_shader(Shader &shader)
179 {
180         unsigned shader_id = shader.steal_id();
181         if(!shader_id)
182                 throw invalid_argument("Program::attach_shader");
183         stage_ids.push_back(shader_id);
184         compile_glsl_stage(shader_id);
185 }
186
187 void Program::attach_shader_owned(Shader *shader)
188 {
189         attach_shader(*shader);
190         delete shader;
191 }
192
193 void Program::detach_shader(Shader &)
194 {
195 }
196
197 const vector<Shader *> &Program::get_attached_shaders() const
198 {
199         static vector<Shader *> dummy;
200         return dummy;
201 }
202
203 void Program::bind_attribute(unsigned index, const string &name)
204 {
205         static Require _req(ARB_vertex_shader);
206         glBindAttribLocation(id, index, name.c_str());
207 }
208
209 void Program::bind_attribute(VertexAttribute attr, const string &name)
210 {
211         bind_attribute(get_attribute_semantic(attr), name);
212 }
213
214 void Program::bind_fragment_data(unsigned index, const string &name)
215 {
216         static Require _req(EXT_gpu_shader4);
217         glBindFragDataLocation(id, index, name.c_str());
218 }
219 #pragma GCC diagnostic pop
220
221 void Program::link()
222 {
223         uniforms.clear();
224
225         glLinkProgram(id);
226         linked = get_program_i(id, GL_LINK_STATUS);
227
228         GLsizei info_log_len = get_program_i(id, GL_INFO_LOG_LENGTH);
229         string info_log(info_log_len+1, 0);
230         glGetProgramInfoLog(id, info_log_len+1, &info_log_len, &info_log[0]);
231         info_log.erase(info_log_len);
232         if(module && module->get_format()==Module::GLSL)
233                 info_log = static_cast<const GlslModule *>(module)->get_source_map().translate_errors(info_log);
234
235         if(!linked)
236                 throw compile_error(info_log);
237 #ifdef DEBUG
238         if(!info_log.empty())
239                 IO::print("Program link info log:\n%s", info_log);
240 #endif
241
242         query_uniforms();
243         query_attributes();
244
245         for(UniformMap::const_iterator i=uniforms.begin(); i!=uniforms.end(); ++i)
246                 require_type(i->second.type);
247         for(AttributeMap::const_iterator i=attributes.begin(); i!=attributes.end(); ++i)
248                 require_type(i->second.type);
249 }
250
251 void Program::require_type(GLenum t)
252 {
253         switch(t)
254         {
255         case GL_FLOAT_MAT2x3:
256         case GL_FLOAT_MAT2x4:
257         case GL_FLOAT_MAT3x2:
258         case GL_FLOAT_MAT3x4:
259         case GL_FLOAT_MAT4x2:
260         case GL_FLOAT_MAT4x3:
261                 { static Require _req(NV_non_square_matrices); }
262                 break;
263         }
264 }
265
266 void Program::query_uniforms()
267 {
268         unsigned count = get_program_i(id, GL_ACTIVE_UNIFORMS);
269         vector<UniformInfo *> uniforms_by_index(count);
270         for(unsigned i=0; i<count; ++i)
271         {
272                 char name[128];
273                 int len = 0;
274                 int size;
275                 GLenum type;
276                 glGetActiveUniform(id, i, sizeof(name), &len, &size, &type, name);
277                 if(len && strncmp(name, "gl_", 3))
278                 {
279                         /* Some implementations report the first element of a uniform array,
280                         others report just the name of the array itself. */
281                         if(len>3 && !strcmp(name+len-3, "[0]"))
282                                 name[len-3] = 0;
283
284                         UniformInfo &info = uniforms[name];
285                         info.block = 0;
286                         info.name = name;
287                         info.size = size;
288                         info.array_stride = 0;
289                         info.matrix_stride = 0;
290                         info.type = type;
291                         uniforms_by_index[i] = &info;
292                 }
293         }
294
295         if(ARB_uniform_buffer_object)
296                 query_uniform_blocks(uniforms_by_index);
297
298         UniformBlockInfo &default_block = uniform_blocks[string()];
299         default_block.data_size = 0;
300         default_block.bind_point = -1;
301
302         for(UniformMap::iterator i=uniforms.begin(); i!=uniforms.end(); ++i)
303                 if(!i->second.block)
304                 {
305                         i->second.location = glGetUniformLocation(id, i->second.name.c_str());
306                         i->second.block = &default_block;
307                         default_block.uniforms.push_back(&i->second);
308                 }
309
310         default_block.layout_hash = compute_layout_hash(default_block.uniforms);
311
312         string layout_descriptor;
313         for(UniformBlockMap::const_iterator i=uniform_blocks.begin(); i!=uniform_blocks.end(); ++i)
314                 layout_descriptor += format("%d:%x\n", i->second.bind_point, i->second.layout_hash);
315         uniform_layout_hash = hash32(layout_descriptor);
316 }
317
318 void Program::query_uniform_blocks(const vector<UniformInfo *> &uniforms_by_index)
319 {
320         uniform_blocks.clear();
321
322         std::set<unsigned> used_bind_points;
323         unsigned count = get_program_i(id, GL_ACTIVE_UNIFORM_BLOCKS);
324         for(unsigned i=0; i<count; ++i)
325         {
326                 char name[128];
327                 int len;
328                 glGetActiveUniformBlockName(id, i, sizeof(name), &len, name);
329                 UniformBlockInfo &info = uniform_blocks[name];
330                 info.name = name;
331
332                 int value;
333                 glGetActiveUniformBlockiv(id, i, GL_UNIFORM_BLOCK_DATA_SIZE, &value);
334                 info.data_size = value;
335
336                 glGetActiveUniformBlockiv(id, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &value);
337                 vector<int> indices(value);
338                 glGetActiveUniformBlockiv(id, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES, &indices[0]);
339                 for(vector<int>::iterator j=indices.begin(); j!=indices.end(); ++j)
340                 {
341                         if(!uniforms_by_index[*j])
342                                 throw logic_error("Program::link");
343                         info.uniforms.push_back(uniforms_by_index[*j]);
344                         uniforms_by_index[*j]->block = &info;
345                 }
346
347                 vector<unsigned> indices2(indices.begin(), indices.end());
348                 vector<int> values(indices.size());
349                 glGetActiveUniformsiv(id, indices.size(), &indices2[0], GL_UNIFORM_OFFSET, &values[0]);
350                 for(unsigned j=0; j<indices.size(); ++j)
351                         uniforms_by_index[indices[j]]->location = values[j];
352
353                 indices2.clear();
354                 for(vector<int>::iterator j=indices.begin(); j!=indices.end(); ++j)
355                         if(uniforms_by_index[*j]->size>1)
356                                 indices2.push_back(*j);
357                 if(!indices2.empty())
358                 {
359                         glGetActiveUniformsiv(id, indices2.size(), &indices2[0], GL_UNIFORM_ARRAY_STRIDE, &values[0]);
360                         for(unsigned j=0; j<indices2.size(); ++j)
361                                 uniforms_by_index[indices2[j]]->array_stride = values[j];
362                 }
363
364                 indices2.clear();
365                 for(vector<int>::iterator j=indices.begin(); j!=indices.end(); ++j)
366                 {
367                         GLenum t = uniforms_by_index[*j]->type;
368                         if(t==GL_FLOAT_MAT4 || t==GL_FLOAT_MAT3 || t==GL_FLOAT_MAT2 ||
369                                 t==GL_FLOAT_MAT2x3 || t==GL_FLOAT_MAT2x4 || t==GL_FLOAT_MAT3x2 ||
370                                 t==GL_FLOAT_MAT3x4 || t==GL_FLOAT_MAT4x2 || t==GL_FLOAT_MAT4x3)
371                                 indices2.push_back(*j);
372                 }
373                 if(!indices2.empty())
374                 {
375                         glGetActiveUniformsiv(id, indices2.size(), &indices2[0], GL_UNIFORM_MATRIX_STRIDE, &values[0]);
376                         for(unsigned j=0; j<indices2.size(); ++j)
377                                 uniforms_by_index[indices2[j]]->matrix_stride = values[j];
378                 }
379
380                 sort(info.uniforms.begin(), info.uniforms.end(), uniform_location_compare);
381                 info.layout_hash = compute_layout_hash(info.uniforms);
382                 unsigned n_bindings = BufferRange::get_n_uniform_buffer_bindings();
383                 info.bind_point = info.layout_hash%n_bindings;
384                 while(used_bind_points.count(info.bind_point))
385                         info.bind_point = (info.bind_point+1)%n_bindings;
386                 glUniformBlockBinding(id, i, info.bind_point);
387                 used_bind_points.insert(info.bind_point);
388         }
389 }
390
391 void Program::query_attributes()
392 {
393         unsigned count = get_program_i(id, GL_ACTIVE_ATTRIBUTES);
394         for(unsigned i=0; i<count; ++i)
395         {
396                 char name[128];
397                 int len = 0;
398                 int size;
399                 GLenum type;
400                 glGetActiveAttrib(id, i, sizeof(name), &len, &size, &type, name);
401                 if(len && strncmp(name, "gl_", 3))
402                 {
403                         if(len>3 && !strcmp(name+len-3, "[0]"))
404                                 name[len-3] = 0;
405
406                         AttributeInfo &info = attributes[name];
407                         info.name = name;
408                         info.location = glGetAttribLocation(id, name);
409                         info.size = size;
410                         info.type = type;
411                 }
412         }
413 }
414
415 Program::LayoutHash Program::compute_layout_hash(const vector<const UniformInfo *> &uniforms)
416 {
417         string layout_descriptor;
418         for(vector<const UniformInfo *>::const_iterator i = uniforms.begin(); i!=uniforms.end(); ++i)
419                 layout_descriptor += format("%d:%s:%x:%d\n", (*i)->location, (*i)->name, (*i)->type, (*i)->size);
420         return hash32(layout_descriptor);
421 }
422
423 bool Program::uniform_location_compare(const UniformInfo *uni1, const UniformInfo *uni2)
424 {
425         return uni1->location<uni2->location;
426 }
427
428 string Program::get_info_log() const
429 {
430         GLsizei len = get_program_i(id, GL_INFO_LOG_LENGTH);
431         string log(len+1, 0);
432         glGetProgramInfoLog(id, len+1, &len, &log[0]);
433         log.erase(len);
434         return log;
435 }
436
437 const Program::UniformBlockInfo &Program::get_uniform_block_info(const string &name) const
438 {
439         return get_item(uniform_blocks, name);
440 }
441
442 const Program::UniformInfo &Program::get_uniform_info(const string &name) const
443 {
444         return get_item(uniforms, name);
445 }
446
447 int Program::get_uniform_location(const string &n) const
448 {
449         if(n[n.size()-1]==']')
450                 throw invalid_argument("Program::get_uniform_location");
451
452         UniformMap::const_iterator i = uniforms.find(n);
453         if(i==uniforms.end())
454                 return -1;
455
456         return i->second.block->bind_point<0 ? i->second.location : -1;
457 }
458
459 const Program::AttributeInfo &Program::get_attribute_info(const string &name) const
460 {
461         return get_item(attributes, name);
462 }
463
464 int Program::get_attribute_location(const string &n) const
465 {
466         if(n[n.size()-1]==']')
467                 throw invalid_argument("Program::get_attribute_location");
468
469         AttributeMap::const_iterator i = attributes.find(n);
470         return i!=attributes.end() ? i->second.location : -1;
471 }
472
473 void Program::bind() const
474 {
475         if(!linked)
476                 throw invalid_operation("Program::bind");
477
478         if(!set_current(this))
479                 return;
480
481         glUseProgram(id);
482 }
483
484 void Program::unbind()
485 {
486         if(!set_current(0))
487                 return;
488
489         glUseProgram(0);
490 }
491
492
493 Program::Loader::Loader(Program &p, Collection &c):
494         DataFile::CollectionObjectLoader<Program>(p, &c)
495 {
496         add("module",          &Loader::module);
497
498         // Deprecated
499         add("attribute",       &Loader::attribute);
500         add("fragment_shader", &Loader::fragment_shader);
501         add("geometry_shader", &Loader::geometry_shader);
502         add("vertex_shader",   &Loader::vertex_shader);
503 }
504
505 void Program::Loader::finish()
506 {
507         obj.link();
508 }
509
510 void Program::Loader::module(const string &n)
511 {
512         map<string, int> spec_values;
513         SpecializationLoader ldr(spec_values);
514         load_sub_with(ldr);
515         obj.add_stages(get_collection().get<Module>(n), spec_values);
516 }
517
518 #pragma GCC diagnostic push
519 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
520 void Program::Loader::attribute(unsigned i, const string &n)
521 {
522         obj.bind_attribute(i, n);
523 }
524
525 void Program::Loader::fragment_shader(const string &src)
526 {
527         obj.attach_shader_owned(new FragmentShader(src));
528 }
529
530 void Program::Loader::geometry_shader(const string &src)
531 {
532         obj.attach_shader_owned(new GeometryShader(src));
533 }
534
535 void Program::Loader::vertex_shader(const string &src)
536 {
537         obj.attach_shader_owned(new VertexShader(src));
538 }
539 #pragma GCC diagnostic pop
540
541
542 DataFile::Loader::ActionMap Program::SpecializationLoader::shared_actions;
543
544 Program::SpecializationLoader::SpecializationLoader(map<string, int> &sv):
545         spec_values(sv)
546 {
547         set_actions(shared_actions);
548 }
549
550 void Program::SpecializationLoader::init_actions()
551 {
552         add("specialize", &SpecializationLoader::specialize_bool);
553         add("specialize", &SpecializationLoader::specialize_int);
554 }
555
556 void Program::SpecializationLoader::specialize_bool(const string &name, bool value)
557 {
558         spec_values[name] = value;
559 }
560
561 void Program::SpecializationLoader::specialize_int(const string &name, int value)
562 {
563         spec_values[name] = value;
564 }
565
566 } // namespace GL
567 } // namespace Msp