]> git.tdb.fi Git - libs/gl.git/blob - source/render/renderer.cpp
Use persistent uniform blocks for Camera, Lighting and Clipping
[libs/gl.git] / source / render / renderer.cpp
1 #include "batch.h"
2 #include "buffer.h"
3 #include "camera.h"
4 #include "clipping.h"
5 #include "error.h"
6 #include "lighting.h"
7 #include "material.h"
8 #include "program.h"
9 #include "programdata.h"
10 #include "renderable.h"
11 #include "renderer.h"
12 #include "sampler.h"
13 #include "texture.h"
14 #include "texturing.h"
15 #include "texunit.h"
16 #include "vertexarray.h"
17 #include "vertexsetup.h"
18 #include "windingtest.h"
19
20 using namespace std;
21
22 namespace Msp {
23 namespace GL {
24
25 Renderer::Renderer():
26         default_camera(0)
27 {
28         init();
29 }
30
31 Renderer::Renderer(const Camera *c):
32         default_camera(c)
33 {
34         init();
35
36         if(c)
37                 set_camera(*c);
38 }
39
40 void Renderer::init()
41 {
42         state_stack.reserve(16);
43         state_stack.push_back(State());
44         shdata_stack.reserve(32);
45         state = &state_stack.back();
46 }
47
48 Renderer::~Renderer()
49 {
50         end();
51 }
52
53 void Renderer::set_camera(const Camera &c)
54 {
55         state->camera = &c;
56         changed |= CAMERA_SHDATA;
57         set_matrix(Matrix());
58 }
59
60 void Renderer::set_matrix(const Matrix &matrix)
61 {
62         state->model_matrix = matrix;
63         changed |= MATRIX;
64 }
65
66 void Renderer::transform(const Matrix &matrix)
67 {
68         state->model_matrix *= matrix;
69         changed |= MATRIX;
70 }
71
72 void Renderer::set_texture(Tag tag, const Texture *tex, const Sampler *samp)
73 {
74         set_texture(tag, -1, tex, samp);
75 }
76
77 void Renderer::set_texture(Tag tag, int unit, const Texture *tex, const Sampler *samp)
78 {
79         if(texture_stack.size()>state->texture_count)
80         {
81                 BoundTexture &bt = texture_stack[state->texture_count];
82                 if((!tag.id || bt.tag==tag) && (unit<0 || bt.unit==unit) && bt.texture==tex && bt.sampler==samp)
83                 {
84                         ++state->texture_count;
85                         return;
86                 }
87                 else
88                         flush_textures();
89         }
90
91         for(vector<BoundTexture>::iterator i=texture_stack.end(); i!=texture_stack.begin(); )
92                 if((--i)->tag==tag && i->unit==unit)
93                 {
94                         i->replaced = texture_stack.size();
95                         break;
96                 }
97
98         texture_stack.push_back(BoundTexture());
99         BoundTexture &bound_tex = texture_stack.back();
100         bound_tex.tag = tag;
101         bound_tex.unit = unit;
102         bound_tex.texture = tex;
103         bound_tex.sampler = samp;
104         state->texture_count = texture_stack.size();
105 }
106
107 void Renderer::set_texture(const Texture *t, const Sampler *s)
108 {
109         set_texture(Tag(), 0, t, s);
110 }
111
112 void Renderer::flush_textures()
113 {
114         for(unsigned i=0; i<texture_stack.size(); ++i)
115         {
116                 BoundTexture &bt = texture_stack[i];
117                 if(i>=state->texture_count && bt.unit>=0)
118                 {
119                         Texture::unbind_from(bt.unit);
120                         Sampler::unbind_from(bt.unit);
121                 }
122                 else if(bt.replaced>=static_cast<int>(state->texture_count))
123                         bt.replaced = -1;
124         }
125
126         texture_stack.erase(texture_stack.begin()+state->texture_count, texture_stack.end());
127 }
128
129 #pragma GCC diagnostic push
130 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
131 void Renderer::set_texturing(const Texturing *t)
132 {
133         if(t)
134         {
135                 unsigned n_units = TexUnit::get_n_units();
136                 for(unsigned i=0; i<n_units; ++i)
137                         if(const Texture *tex = t->get_attached_texture(i))
138                                 set_texture(Tag(), i, tex, t->get_attached_sampler(i));
139         }
140 }
141 #pragma GCC diagnostic pop
142
143 unsigned Renderer::allocate_effect_texunit()
144 {
145         return --state->lowest_effect_texunit;
146 }
147
148 void Renderer::set_material(const Material *m)
149 {
150         state->material = m;
151         changed |= MATERIAL_SHDATA;
152 }
153
154 void Renderer::set_lighting(const Lighting *l)
155 {
156         state->lighting = l;
157         changed |= LIGHTING_SHDATA;
158 }
159
160 void Renderer::set_clipping(const Clipping *c)
161 {
162         state->clipping = c;
163         changed |= CLIPPING_SHDATA;
164 }
165
166 void Renderer::set_shader_program(const Program *p, const ProgramData *d)
167 {
168         state->shprog = p;
169         if(p && d)
170                 add_shader_data(*d);
171 }
172
173 void Renderer::add_shader_data(const ProgramData &d)
174 {
175         if(state->shdata_count<shdata_stack.size())
176         {
177                 const BoundProgramData &top = shdata_stack.back();
178                 if(top.shdata==&d && top.generation==d.get_generation())
179                 {
180                         ++state->shdata_count;
181                         return;
182                 }
183         }
184
185         flush_shader_data();
186         shdata_stack.push_back(&d);
187         state->shdata_count = shdata_stack.size();
188         changed |= SHADER_DATA;
189 }
190
191 void Renderer::flush_shader_data()
192 {
193         if(shdata_stack.size()>state->shdata_count)
194                 shdata_stack.erase(shdata_stack.begin()+state->shdata_count, shdata_stack.end());
195 }
196
197 void Renderer::set_vertex_setup(const VertexSetup *vs)
198 {
199         state->vertex_setup = vs;
200 }
201
202 void Renderer::set_winding_test(const WindingTest *w)
203 {
204         state->winding_test = w;
205 }
206
207 void Renderer::set_reverse_winding(bool r)
208 {
209         state->reverse_winding = r;
210 }
211
212 void Renderer::set_object_lod_bias(unsigned b)
213 {
214         state->object_lod_bias = b;
215 }
216
217 void Renderer::push_state()
218 {
219         state_stack.push_back(state_stack.back());
220         state = &state_stack.back();
221 }
222
223 void Renderer::pop_state()
224 {
225         if(state_stack.size()==1)
226                 throw stack_underflow("Renderer::pop_state");
227
228         const Camera *old_camera = state->camera;
229         const Lighting *old_lighting = state->lighting;
230         const Clipping *old_clipping = state->clipping;
231         state_stack.pop_back();
232         state = &state_stack.back();
233         changed |= MATRIX;
234         bool camera_changed = (state->camera!=old_camera);
235         if(camera_changed)
236                 changed |= CAMERA_SHDATA;
237         if(state->lighting!=old_lighting)
238                 changed |= LIGHTING_SHDATA;
239         if(state->clipping!=old_clipping)
240                 changed |= CLIPPING_SHDATA;
241 }
242
243 void Renderer::end()
244 {
245         if(state_stack.size()>1)
246                 throw invalid_operation("Renderer::end");
247
248         *state = State();
249         if(default_camera)
250                 set_camera(*default_camera);
251         shdata_stack.clear();
252         excluded.clear();
253
254         for(vector<BoundTexture>::iterator i=texture_stack.begin(); i!=texture_stack.end(); ++i)
255                 if(i->unit>=0)
256                 {
257                         Texture::unbind_from(i->unit);
258                         Sampler::unbind_from(i->unit);
259                 }
260         Clipping::unbind();
261         Program::unbind();
262         VertexSetup::unbind();
263         Buffer::unbind_from(ELEMENT_ARRAY_BUFFER);
264         WindingTest::unbind();
265 }
266
267 void Renderer::exclude(const Renderable &renderable)
268 {
269         excluded.insert(&renderable);
270 }
271
272 void Renderer::include(const Renderable &renderable)
273 {
274         excluded.erase(&renderable);
275 }
276
277 void Renderer::render(const Renderable &renderable, Tag tag)
278 {
279         if(!excluded.count(&renderable))
280                 renderable.render(*this, tag);
281 }
282
283 void Renderer::draw(const Batch &batch)
284 {
285         apply_state();
286
287         batch.draw();
288 }
289
290 void Renderer::draw_instanced(const Batch &batch, unsigned count)
291 {
292         apply_state();
293
294         batch.draw_instanced(count);
295 }
296
297 void Renderer::apply_state()
298 {
299         if(!state->shprog)
300                 throw invalid_operation("Renderer::apply_state");
301
302         /* We (mostly) let the objects themselves figure out if the binding has
303         changed */
304
305         if(state->texture_count<texture_stack.size())
306                 flush_textures();
307
308         for(vector<BoundTexture>::const_iterator i=texture_stack.begin(); i!=texture_stack.end(); ++i)
309         {
310                 int unit = (i->tag.id ? state->shprog->get_uniform_binding(i->tag) : i->unit);
311                 if(unit>=0)
312                 {
313                         if(i->texture)
314                                 i->texture->bind_to(unit);
315                         if(i->sampler)
316                                 i->sampler->bind_to(unit);
317                         i->unit = unit;
318                 }
319         }
320
321         if(state->clipping)
322                 state->clipping->bind();
323         else
324                 Clipping::unbind();
325
326         bool shprog_changed = (state->shprog!=Program::current());
327         state->shprog->bind();
328
329         if(changed&MATRIX)
330         {
331                 standard_shdata.uniform("world_obj_matrix", state->model_matrix);
332                 LinAl::SquareMatrix<float, 3> nm = state->model_matrix.block<3, 3>(0, 0);
333                 nm = transpose(invert(nm));
334                 standard_shdata.uniform("world_obj_normal_matrix", nm);
335                 changed = (changed&~MATRIX)|STANDARD_SHDATA;
336         }
337
338         if(state->camera && ((changed&CAMERA_SHDATA) || shprog_changed))
339         {
340                 state->camera->get_shader_data().apply();
341                 changed &= ~CAMERA_SHDATA;
342         }
343
344         if(state->material && ((changed&MATERIAL_SHDATA) || shprog_changed))
345         {
346                 state->material->get_shader_data().apply();
347                 changed &= ~MATERIAL_SHDATA;
348         }
349
350         if(state->lighting && ((changed&LIGHTING_SHDATA) || shprog_changed))
351         {
352                 state->lighting->get_shader_data().apply();
353                 changed &= ~LIGHTING_SHDATA;
354         }
355
356         if(state->clipping && ((changed&CLIPPING_SHDATA) || shprog_changed))
357         {
358                 state->clipping->get_shader_data().apply();
359                 changed &= ~CLIPPING_SHDATA;
360         }
361
362         if((changed&STANDARD_SHDATA) || shprog_changed)
363         {
364                 standard_shdata.apply();
365                 changed &= ~STANDARD_SHDATA;
366         }
367
368         bool shdata_changed = changed&SHADER_DATA;
369         for(vector<BoundProgramData>::const_iterator i=shdata_stack.begin(); (!shdata_changed && i!=shdata_stack.end()); ++i)
370                 shdata_changed = (i->shdata->get_generation()!=i->generation);
371         bool extra_shdata = (shdata_stack.size()>state->shdata_count);
372
373         if(shdata_changed || shprog_changed || extra_shdata)
374         {
375                 if(extra_shdata)
376                         shdata_stack.erase(shdata_stack.begin()+state->shdata_count, shdata_stack.end());
377                 for(vector<BoundProgramData>::const_iterator i=shdata_stack.begin(); i!=shdata_stack.end(); ++i)
378                 {
379                         i->shdata->apply();
380                         i->generation = i->shdata->get_generation();
381                 }
382                 changed &= ~SHADER_DATA;
383         }
384
385         if(state->vertex_setup)
386                 state->vertex_setup->bind();
387         else
388                 VertexSetup::unbind();
389
390         if(state->winding_test)
391         {
392                 if(state->reverse_winding)
393                         state->winding_test->get_reverse().bind();
394                 else
395                         state->winding_test->bind();
396         }
397         else
398                 WindingTest::unbind();
399 }
400
401
402 Renderer::BoundTexture::BoundTexture():
403         unit(-1),
404         texture(0),
405         sampler(0),
406         replaced(-1)
407 { }
408
409
410 Renderer::BoundProgramData::BoundProgramData(const ProgramData *d):
411         shdata(d),
412         generation(0)
413 { }
414
415
416 Renderer::State::State():
417         camera(0),
418         texture_count(0),
419         lowest_effect_texunit(TexUnit::get_n_units()),
420         material(0),
421         lighting(0),
422         clipping(0),
423         shprog(0),
424         shdata_count(0),
425         vertex_setup(0),
426         winding_test(0),
427         reverse_winding(false),
428         object_lod_bias(0)
429 { }
430
431 } // namespace GL
432 } // namespace Msp