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