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