]> git.tdb.fi Git - libs/gl.git/blob - source/core/program.cpp
Refactor the interface of SL::Compiler
[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_shader_objects.h>
7 #include <msp/gl/extensions/arb_uniform_buffer_object.h>
8 #include <msp/gl/extensions/arb_vertex_shader.h>
9 #include <msp/gl/extensions/ext_gpu_shader4.h>
10 #include <msp/gl/extensions/nv_non_square_matrices.h>
11 #include <msp/io/print.h>
12 #include <msp/strings/format.h>
13 #include "buffer.h"
14 #include "error.h"
15 #include "misc.h"
16 #include "program.h"
17 #include "resources.h"
18 #include "shader.h"
19 #include "glsl/compiler.h"
20
21 using namespace std;
22
23 namespace Msp {
24 namespace GL {
25
26 Program::Program()
27 {
28         init();
29 }
30
31 Program::Program(const std::string &source)
32 {
33         init();
34
35         SL::Compiler compiler;
36         if(source.find(';')==string::npos && source.size()>5 && !source.compare(source.size()-5, 5, ".glsl"))
37         {
38                 if(RefPtr<IO::Seekable> io = Resources::get_builtins().open(source))
39                         compiler.load_source(*io, source);
40                 else
41                         throw IO::file_not_found(source);
42         }
43         else
44                 compiler.set_source(source);
45         compiler.compile();
46         compiler.add_shaders(*this);
47         link();
48 }
49
50 Program::Program(const string &vert, const string &frag)
51 {
52         init();
53
54         attach_shader_owned(new VertexShader(vert));
55         attach_shader_owned(new FragmentShader(frag));
56         link();
57 }
58
59 void Program::init()
60 {
61         static Require _req(ARB_shader_objects);
62
63         linked = false;
64         id = glCreateProgram();
65 }
66
67 Program::~Program()
68 {
69         for(ShaderList::iterator i=owned_data.begin(); i!=owned_data.end(); ++i)
70                 delete *i;
71         glDeleteProgram(id);
72 }
73
74 void Program::attach_shader(Shader &shader)
75 {
76         if(find(shaders.begin(), shaders.end(), &shader)==shaders.end())
77         {
78                 glAttachShader(id, shader.get_id());
79                 shaders.push_back(&shader);
80         }
81 }
82
83 void Program::attach_shader_owned(Shader *shader)
84 {
85         attach_shader(*shader);
86         if(find(owned_data.begin(), owned_data.end(), shader)==owned_data.end())
87                 owned_data.push_back(shader);
88 }
89
90 void Program::detach_shader(Shader &shader)
91 {
92         ShaderList::iterator i = remove(shaders.begin(), shaders.end(), &shader);
93         if(i!=shaders.end())
94         {
95                 shaders.erase(i, shaders.end());
96                 glDetachShader(id, shader.get_id());
97         }
98 }
99
100 void Program::bind_attribute(unsigned index, const string &name)
101 {
102         static Require _req(ARB_vertex_shader);
103         glBindAttribLocation(id, index, name.c_str());
104 }
105
106 void Program::bind_attribute(VertexComponent comp, const string &name)
107 {
108         bind_attribute(get_component_type(comp), name);
109 }
110
111 void Program::bind_fragment_data(unsigned index, const string &name)
112 {
113         static Require _req(EXT_gpu_shader4);
114         glBindFragDataLocation(id, index, name.c_str());
115 }
116
117 void Program::link()
118 {
119         for(ShaderList::iterator i=shaders.begin(); i!=shaders.end(); ++i)
120                 if(!(*i)->is_compiled())
121                         (*i)->compile();
122
123         uniforms.clear();
124
125         glLinkProgram(id);
126         linked = get_program_i(id, GL_LINK_STATUS);
127         if(!linked)
128                 throw compile_error(get_info_log());
129
130 #ifdef DEBUG
131         std::string info_log = get_info_log();
132         if(!info_log.empty())
133                 IO::print("Program link info log:\n%s", info_log);
134 #endif
135
136         query_uniforms();
137         query_attributes();
138
139         for(UniformMap::const_iterator i=uniforms.begin(); i!=uniforms.end(); ++i)
140                 require_type(i->second.type);
141         for(AttributeMap::const_iterator i=attributes.begin(); i!=attributes.end(); ++i)
142                 require_type(i->second.type);
143 }
144
145 void Program::require_type(GLenum t)
146 {
147         switch(t)
148         {
149         case GL_FLOAT_MAT2x3:
150         case GL_FLOAT_MAT2x4:
151         case GL_FLOAT_MAT3x2:
152         case GL_FLOAT_MAT3x4:
153         case GL_FLOAT_MAT4x2:
154         case GL_FLOAT_MAT4x3:
155                 { static Require _req(NV_non_square_matrices); }
156                 break;
157         }
158 }
159
160 void Program::query_uniforms()
161 {
162         unsigned count = get_program_i(id, GL_ACTIVE_UNIFORMS);
163         vector<UniformInfo *> uniforms_by_index(count);
164         for(unsigned i=0; i<count; ++i)
165         {
166                 char name[128];
167                 int len = 0;
168                 int size;
169                 GLenum type;
170                 glGetActiveUniform(id, i, sizeof(name), &len, &size, &type, name);
171                 if(len && strncmp(name, "gl_", 3))
172                 {
173                         /* Some implementations report the first element of a uniform array,
174                         others report just the name of the array itself. */
175                         if(len>3 && !strcmp(name+len-3, "[0]"))
176                                 name[len-3] = 0;
177
178                         UniformInfo &info = uniforms[name];
179                         info.block = 0;
180                         info.name = name;
181                         info.size = size;
182                         info.array_stride = 0;
183                         info.matrix_stride = 0;
184                         info.type = type;
185                         uniforms_by_index[i] = &info;
186                 }
187         }
188
189         if(ARB_uniform_buffer_object)
190                 query_uniform_blocks(uniforms_by_index);
191
192         UniformBlockInfo &default_block = uniform_blocks[string()];
193         default_block.data_size = 0;
194         default_block.bind_point = -1;
195
196         for(UniformMap::iterator i=uniforms.begin(); i!=uniforms.end(); ++i)
197                 if(!i->second.block)
198                 {
199                         i->second.location = glGetUniformLocation(id, i->second.name.c_str());
200                         i->second.block = &default_block;
201                         default_block.uniforms.push_back(&i->second);
202                 }
203
204         default_block.layout_hash = compute_layout_hash(default_block.uniforms);
205
206         string layout_descriptor;
207         for(UniformBlockMap::const_iterator i=uniform_blocks.begin(); i!=uniform_blocks.end(); ++i)
208                 layout_descriptor += format("%d:%x\n", i->second.bind_point, i->second.layout_hash);
209         uniform_layout_hash = hash32(layout_descriptor);
210 }
211
212 void Program::query_uniform_blocks(const vector<UniformInfo *> &uniforms_by_index)
213 {
214         uniform_blocks.clear();
215
216         std::set<unsigned> used_bind_points;
217         unsigned count = get_program_i(id, GL_ACTIVE_UNIFORM_BLOCKS);
218         for(unsigned i=0; i<count; ++i)
219         {
220                 char name[128];
221                 int len;
222                 glGetActiveUniformBlockName(id, i, sizeof(name), &len, name);
223                 UniformBlockInfo &info = uniform_blocks[name];
224                 info.name = name;
225
226                 int value;
227                 glGetActiveUniformBlockiv(id, i, GL_UNIFORM_BLOCK_DATA_SIZE, &value);
228                 info.data_size = value;
229
230                 glGetActiveUniformBlockiv(id, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &value);
231                 vector<int> indices(value);
232                 glGetActiveUniformBlockiv(id, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES, &indices[0]);
233                 for(vector<int>::iterator j=indices.begin(); j!=indices.end(); ++j)
234                 {
235                         if(!uniforms_by_index[*j])
236                                 throw logic_error("Program::link");
237                         info.uniforms.push_back(uniforms_by_index[*j]);
238                         uniforms_by_index[*j]->block = &info;
239                 }
240
241                 vector<unsigned> indices2(indices.begin(), indices.end());
242                 vector<int> values(indices.size());
243                 glGetActiveUniformsiv(id, indices.size(), &indices2[0], GL_UNIFORM_OFFSET, &values[0]);
244                 for(unsigned j=0; j<indices.size(); ++j)
245                         uniforms_by_index[indices[j]]->location = values[j];
246
247                 indices2.clear();
248                 for(vector<int>::iterator j=indices.begin(); j!=indices.end(); ++j)
249                         if(uniforms_by_index[*j]->size>1)
250                                 indices2.push_back(*j);
251                 if(!indices2.empty())
252                 {
253                         glGetActiveUniformsiv(id, indices2.size(), &indices2[0], GL_UNIFORM_ARRAY_STRIDE, &values[0]);
254                         for(unsigned j=0; j<indices2.size(); ++j)
255                                 uniforms_by_index[indices2[j]]->array_stride = values[j];
256                 }
257
258                 indices2.clear();
259                 for(vector<int>::iterator j=indices.begin(); j!=indices.end(); ++j)
260                 {
261                         GLenum t = uniforms_by_index[*j]->type;
262                         if(t==GL_FLOAT_MAT4 || t==GL_FLOAT_MAT3 || t==GL_FLOAT_MAT2 ||
263                                 t==GL_FLOAT_MAT2x3 || t==GL_FLOAT_MAT2x4 || t==GL_FLOAT_MAT3x2 ||
264                                 t==GL_FLOAT_MAT3x4 || t==GL_FLOAT_MAT4x2 || t==GL_FLOAT_MAT4x3)
265                                 indices2.push_back(*j);
266                 }
267                 if(!indices2.empty())
268                 {
269                         glGetActiveUniformsiv(id, indices2.size(), &indices2[0], GL_UNIFORM_MATRIX_STRIDE, &values[0]);
270                         for(unsigned j=0; j<indices2.size(); ++j)
271                                 uniforms_by_index[indices2[j]]->matrix_stride = values[j];
272                 }
273
274                 sort(info.uniforms.begin(), info.uniforms.end(), uniform_location_compare);
275                 info.layout_hash = compute_layout_hash(info.uniforms);
276                 unsigned n_bindings = BufferRange::get_n_uniform_buffer_bindings();
277                 info.bind_point = info.layout_hash%n_bindings;
278                 while(used_bind_points.count(info.bind_point))
279                         info.bind_point = (info.bind_point+1)%n_bindings;
280                 glUniformBlockBinding(id, i, info.bind_point);
281                 used_bind_points.insert(info.bind_point);
282         }
283 }
284
285 void Program::query_attributes()
286 {
287         unsigned count = get_program_i(id, GL_ACTIVE_ATTRIBUTES);
288         for(unsigned i=0; i<count; ++i)
289         {
290                 char name[128];
291                 int len = 0;
292                 int size;
293                 GLenum type;
294                 glGetActiveAttrib(id, i, sizeof(name), &len, &size, &type, name);
295                 if(len && strncmp(name, "gl_", 3))
296                 {
297                         if(len>3 && !strcmp(name+len-3, "[0]"))
298                                 name[len-3] = 0;
299
300                         AttributeInfo &info = attributes[name];
301                         info.name = name;
302                         info.location = glGetAttribLocation(id, name);
303                         info.size = size;
304                         info.type = type;
305                 }
306         }
307 }
308
309 Program::LayoutHash Program::compute_layout_hash(const vector<const UniformInfo *> &uniforms)
310 {
311         string layout_descriptor;
312         for(vector<const UniformInfo *>::const_iterator i = uniforms.begin(); i!=uniforms.end(); ++i)
313                 layout_descriptor += format("%d:%s:%x:%d\n", (*i)->location, (*i)->name, (*i)->type, (*i)->size);
314         return hash32(layout_descriptor);
315 }
316
317 bool Program::uniform_location_compare(const UniformInfo *uni1, const UniformInfo *uni2)
318 {
319         return uni1->location<uni2->location;
320 }
321
322 string Program::get_info_log() const
323 {
324         GLsizei len = get_program_i(id, GL_INFO_LOG_LENGTH);
325         string log(len+1, 0);
326         glGetProgramInfoLog(id, len+1, &len, &log[0]);
327         log.erase(len);
328         return log;
329 }
330
331 const Program::UniformBlockInfo &Program::get_uniform_block_info(const string &name) const
332 {
333         return get_item(uniform_blocks, name);
334 }
335
336 const Program::UniformInfo &Program::get_uniform_info(const string &name) const
337 {
338         return get_item(uniforms, name);
339 }
340
341 int Program::get_uniform_location(const string &n) const
342 {
343         if(n[n.size()-1]==']')
344                 throw invalid_argument("Program::get_uniform_location");
345
346         UniformMap::const_iterator i = uniforms.find(n);
347         if(i==uniforms.end())
348                 return -1;
349
350         return i->second.block->bind_point<0 ? i->second.location : -1;
351 }
352
353 const Program::AttributeInfo &Program::get_attribute_info(const string &name) const
354 {
355         return get_item(attributes, name);
356 }
357
358 int Program::get_attribute_location(const string &n) const
359 {
360         if(n[n.size()-1]==']')
361                 throw invalid_argument("Program::get_attribute_location");
362
363         AttributeMap::const_iterator i = attributes.find(n);
364         return i!=attributes.end() ? i->second.location : -1;
365 }
366
367 void Program::bind() const
368 {
369         if(!linked)
370                 throw invalid_operation("Program::bind");
371
372         if(!set_current(this))
373                 return;
374
375         glUseProgram(id);
376 }
377
378 void Program::unbind()
379 {
380         if(!set_current(0))
381                 return;
382
383         glUseProgram(0);
384 }
385
386
387 Program::Loader::Loader(Program &p):
388         DataFile::ObjectLoader<Program>(p)
389 {
390         add("attribute",       &Loader::attribute);
391         add("fragment_shader", &Loader::fragment_shader);
392         add("geometry_shader", &Loader::geometry_shader);
393         add("vertex_shader",   &Loader::vertex_shader);
394 }
395
396 void Program::Loader::finish()
397 {
398         obj.link();
399 }
400
401 void Program::Loader::attribute(unsigned i, const string &n)
402 {
403         obj.bind_attribute(i, n);
404 }
405
406 void Program::Loader::fragment_shader(const string &src)
407 {
408         obj.attach_shader_owned(new FragmentShader(src));
409 }
410
411 void Program::Loader::geometry_shader(const string &src)
412 {
413         obj.attach_shader_owned(new GeometryShader(src));
414 }
415
416 void Program::Loader::vertex_shader(const string &src)
417 {
418         obj.attach_shader_owned(new VertexShader(src));
419 }
420
421 } // namespace GL
422 } // namespace Msp