]> git.tdb.fi Git - libs/gl.git/blob - source/core/pipelinestate.cpp
Create a class for issuing drawing commands
[libs/gl.git] / source / core / pipelinestate.cpp
1 #include <stdexcept>
2 #include <msp/core/algorithm.h>
3 #include <msp/gl/extensions/arb_direct_state_access.h>
4 #include <msp/gl/extensions/arb_sampler_objects.h>
5 #include <msp/gl/extensions/arb_shader_objects.h>
6 #include <msp/gl/extensions/arb_uniform_buffer_object.h>
7 #include <msp/gl/extensions/arb_vertex_array_object.h>
8 #include <msp/gl/extensions/msp_primitive_restart.h>
9 #include "blend.h"
10 #include "buffer.h"
11 #include "deviceinfo.h"
12 #include "depthtest.h"
13 #include "framebuffer.h"
14 #include "pipelinestate.h"
15 #include "program.h"
16 #include "rect.h"
17 #include "stenciltest.h"
18 #include "texture.h"
19 #include "uniformblock.h"
20 #include "vertexsetup.h"
21
22 using namespace std;
23
24 namespace Msp {
25 namespace GL {
26
27 const PipelineState *PipelineState::last_applied = 0;
28 vector<int> PipelineState::bound_tex_targets;
29 unsigned PipelineState::restart_index = 0;
30
31 PipelineState::PipelineState():
32         framebuffer(0),
33         viewport(0),
34         scissor(0),
35         shprog(0),
36         vertex_setup(0),
37         front_face(COUNTERCLOCKWISE),
38         face_cull(NO_CULL),
39         enabled_clip_planes(0),
40         depth_test(0),
41         stencil_test(0),
42         blend(0),
43         changes(0)
44 {
45         if(!ARB_direct_state_access && bound_tex_targets.empty())
46                 bound_tex_targets.resize(Limits::get_global().max_texture_bindings);
47 }
48
49 PipelineState::~PipelineState()
50 {
51         if(this==last_applied)
52                 last_applied = 0;
53 }
54
55 template<typename T>
56 void PipelineState::set(T &target, T value, unsigned flag)
57 {
58         if(value!=target)
59         {
60                 target = value;
61                 changes |= flag;
62         }
63 }
64
65 void PipelineState::set_framebuffer(const Framebuffer *f)
66 {
67         set(framebuffer, f, FRAMEBUFFER|VIEWPORT);
68 }
69
70 void PipelineState::set_viewport(const Rect *v)
71 {
72         set(viewport, v, VIEWPORT);
73 }
74
75 void PipelineState::set_scissor(const Rect *s)
76 {
77         set(scissor, s, SCISSOR);
78 }
79
80 void PipelineState::set_shader_program(const Program *p)
81 {
82         set(shprog, p, SHPROG);
83 }
84
85 void PipelineState::set_vertex_setup(const VertexSetup *s)
86 {
87         set(vertex_setup, s, VERTEX_SETUP);
88 }
89
90 void PipelineState::set_front_face(FaceWinding w)
91 {
92         set(front_face, w, FACE_CULL);
93 }
94
95 void PipelineState::set_face_cull(CullMode c)
96 {
97         set(face_cull, c, FACE_CULL);
98 }
99
100 void PipelineState::set_enabled_clip_planes(unsigned p)
101 {
102         set(enabled_clip_planes, p, CLIP_PLANES);
103 }
104
105 void PipelineState::set_texture(unsigned binding, const Texture *tex, const Sampler *samp)
106 {
107         if((tex!=0)!=(samp!=0))
108                 throw invalid_argument("PipelineState::set_texture");
109
110         vector<BoundTexture>::iterator i = lower_bound_member(textures, binding, &BoundTexture::binding);
111         if(i==textures.end() || i->binding!=binding)
112                 i = textures.insert(i, BoundTexture(binding));
113         if(tex!=i->texture || samp!=i->sampler)
114         {
115                 i->texture = tex;
116                 i->sampler = samp;
117                 i->changed = true;
118                 changes |= TEXTURES;
119         }
120 }
121
122 void PipelineState::set_uniforms(const DefaultUniformBlock *block)
123 {
124         set_uniform_block_(-1, block);
125 }
126
127 void PipelineState::set_uniform_block(unsigned binding, const BufferBackedUniformBlock *block)
128 {
129         set_uniform_block_(binding, block);
130 }
131
132 void PipelineState::set_uniform_block_(int binding, const UniformBlock *block)
133 {
134         vector<BoundUniformBlock>::iterator i = lower_bound_member(uniform_blocks, binding, &BoundUniformBlock::binding);
135         if(i==uniform_blocks.end() || i->binding!=binding)
136                 i = uniform_blocks.insert(i, BoundUniformBlock(binding));
137         if(block!=i->block || binding<0)
138         {
139                 i->block = block;
140                 i->changed = true;
141                 changes |= UNIFORMS;
142         }
143 }
144
145 void PipelineState::set_depth_test(const DepthTest *dt)
146 {
147         set(depth_test, dt, DEPTH_TEST);
148 }
149
150 void PipelineState::set_stencil_test(const StencilTest *st)
151 {
152         set(stencil_test, st, STENCIL_TEST);
153 }
154
155 void PipelineState::set_blend(const Blend *b)
156 {
157         set(blend, b, BLEND);
158 }
159
160 void PipelineState::apply() const
161 {
162         apply(this==last_applied ? changes : ~0U);
163 }
164
165 void PipelineState::apply(unsigned mask) const
166 {
167         if(mask&FRAMEBUFFER)
168         {
169                 glBindFramebuffer(GL_FRAMEBUFFER, framebuffer ? framebuffer->get_id() : 0);
170                 if(framebuffer)
171                 {
172                         framebuffer->refresh();
173                         framebuffer->require_complete();
174                 }
175         }
176
177         if(mask&VIEWPORT)
178         {
179                 if(viewport)
180                         glViewport(viewport->left, viewport->bottom, viewport->width, viewport->height);
181                 else if(framebuffer)
182                         glViewport(0, 0, framebuffer->get_width(), framebuffer->get_height());
183         }
184
185         if(mask&SCISSOR)
186         {
187                 if(scissor)
188                 {
189                         glEnable(GL_SCISSOR_TEST);
190                         glScissor(scissor->left, scissor->bottom, scissor->width, scissor->height);
191                 }
192                 else
193                         glDisable(GL_SCISSOR_TEST);
194         }
195
196         if(mask&SHPROG)
197                 glUseProgram(shprog ? shprog->get_id() : 0);
198
199         if(mask&VERTEX_SETUP)
200         {
201                 glBindVertexArray(vertex_setup ? vertex_setup->get_id() : 0);
202                 if(vertex_setup)
203                 {
204                         static Require _req(MSP_primitive_restart);
205
206                         vertex_setup->refresh();
207                         unsigned ri = (vertex_setup->get_index_type()==UNSIGNED_INT ? 0xFFFFFFFF : 0xFFFF);
208                         if(ri!=restart_index)
209                         {
210                                 if(!restart_index)
211                                         glEnable(GL_PRIMITIVE_RESTART);
212                                 glPrimitiveRestartIndex(ri);
213                                 restart_index = ri;
214                         }
215                 }
216         }
217
218         if(mask&FACE_CULL)
219         {
220                 glFrontFace(front_face==CLOCKWISE ? GL_CW : GL_CCW);
221
222                 if(face_cull!=NO_CULL && front_face!=NON_MANIFOLD)
223                 {
224                         glEnable(GL_CULL_FACE);
225                         glCullFace(face_cull==CULL_FRONT ? GL_FRONT : GL_BACK);
226                 }
227                 else
228                         glDisable(GL_CULL_FACE);
229         }
230
231         if(mask&CLIP_PLANES)
232         {
233                 unsigned max_clip_planes = Limits::get_global().max_clip_planes;
234                 for(unsigned i=0; i<max_clip_planes; ++i)
235                 {
236                         if((enabled_clip_planes>>i)&1)
237                                 glEnable(GL_CLIP_PLANE0+i);
238                         else
239                                 glDisable(GL_CLIP_PLANE0+i);
240                 }
241         }
242
243         if(mask&TEXTURES)
244         {
245                 if(last_applied && this!=last_applied)
246                 {
247                         vector<BoundTexture>::const_iterator i = textures.begin();
248                         vector<BoundTexture>::const_iterator j = last_applied->textures.begin();
249                         while(j!=last_applied->textures.end())
250                         {
251                                 if(i==textures.end() || j->binding<i->binding)
252                                 {
253                                         if(bound_tex_targets[j->binding])
254                                         {
255                                                 if(ARB_direct_state_access)
256                                                         glBindTextureUnit(j->binding, 0);
257                                                 else
258                                                 {
259                                                         glActiveTexture(GL_TEXTURE0+j->binding);
260                                                         glBindTexture(bound_tex_targets[j->binding], 0);
261                                                 }
262                                         }
263                                         ++j;
264                                 }
265                                 else
266                                 {
267                                         if(i->binding==j->binding)
268                                                 ++j;
269                                         ++i;
270                                 }
271                         }
272                 }
273
274                 for(vector<BoundTexture>::const_iterator i=textures.begin(); i!=textures.end(); ++i)
275                         if(i->changed || mask==~0U)
276                         {
277                                 if(i->texture && i->sampler)
278                                 {
279                                         if(ARB_direct_state_access)
280                                                 glBindTextureUnit(i->binding, i->texture->get_id());
281                                         else
282                                         {
283                                                 glActiveTexture(GL_TEXTURE0+i->binding);
284                                                 if(bound_tex_targets[i->binding] && static_cast<int>(i->texture->get_target())!=bound_tex_targets[i->binding])
285                                                         glBindTexture(bound_tex_targets[i->binding], 0);
286                                                 glBindTexture(i->texture->get_target(), i->texture->get_id());
287                                                 bound_tex_targets[i->binding] = i->texture->get_target();
288                                         }
289
290                                         glBindSampler(i->binding, i->sampler->get_id());
291                                         i->sampler->refresh();
292                                 }
293                                 else if(bound_tex_targets[i->binding])
294                                 {
295                                         if(ARB_direct_state_access)
296                                                 glBindTextureUnit(i->binding, 0);
297                                         else
298                                         {
299                                                 glActiveTexture(GL_TEXTURE0+i->binding);
300                                                 glBindTexture(bound_tex_targets[i->binding], 0);
301                                                 bound_tex_targets[i->binding] = 0;
302                                         }
303                                 }
304
305                                 i->changed = false;
306                         }
307         }
308
309         if(mask&UNIFORMS)
310         {
311                 if(last_applied && this!=last_applied)
312                 {
313                         vector<BoundUniformBlock>::const_iterator i = uniform_blocks.begin();
314                         vector<BoundUniformBlock>::const_iterator j = last_applied->uniform_blocks.begin();
315                         while(j!=last_applied->uniform_blocks.end())
316                         {
317                                 if(i==uniform_blocks.end() || j->binding<i->binding)
318                                 {
319                                         glBindBufferBase(GL_UNIFORM_BUFFER, j->binding, 0);
320                                         ++j;
321                                 }
322                                 else
323                                 {
324                                         if(i->binding==j->binding)
325                                                 ++j;
326                                         ++i;
327                                 }
328                         }
329                 }
330
331                 for(vector<BoundUniformBlock>::const_iterator i=uniform_blocks.begin(); i!=uniform_blocks.end(); ++i)
332                         if(i->changed || mask==~0U)
333                         {
334                                 if(i->block)
335                                 {
336                                         if(i->binding>=0)
337                                         {
338                                                 const BufferBackedUniformBlock *block = static_cast<const BufferBackedUniformBlock *>(i->block);
339                                                 glBindBufferRange(GL_UNIFORM_BUFFER, i->binding, block->get_buffer()->get_id(), block->get_offset(), block->get_data_size());
340                                         }
341                                         else
342                                                 static_cast<const DefaultUniformBlock *>(i->block)->apply();
343                                 }
344                                 else
345                                         glBindBufferBase(GL_UNIFORM_BUFFER, i->binding, 0);
346
347                                 i->changed = false;
348                         }
349         }
350
351         if(mask&DEPTH_TEST)
352         {
353                 if(depth_test && depth_test->enabled)
354                 {
355                         glEnable(GL_DEPTH_TEST);
356                         glDepthFunc(get_gl_predicate(depth_test->compare));
357                 }
358                 else
359                         glDisable(GL_DEPTH_TEST);
360
361                 glDepthMask(!depth_test || depth_test->write);
362         }
363
364         if(mask&STENCIL_TEST)
365         {
366                 if(stencil_test && stencil_test->enabled)
367                 {
368                         glEnable(GL_STENCIL_TEST);
369                         glStencilFunc(get_gl_predicate(stencil_test->compare), stencil_test->reference, 0xFFFFFFFF);
370                         glStencilOp(get_gl_stencil_op(stencil_test->stencil_fail_op), get_gl_stencil_op(stencil_test->depth_fail_op), get_gl_stencil_op(stencil_test->depth_pass_op));
371                 }
372                 else
373                         glDisable(GL_STENCIL_TEST);
374         }
375
376         if(mask&BLEND)
377         {
378                 if(blend && blend->enabled)
379                 {
380                         glEnable(GL_BLEND);
381                         glBlendEquation(get_gl_blend_equation(blend->equation));
382                         glBlendFunc(get_gl_blend_factor(blend->src_factor), get_gl_blend_factor(blend->dst_factor));
383                         glBlendColor(blend->constant.r, blend->constant.g, blend->constant.b, blend->constant.a);
384                 }
385                 else
386                         glDisable(GL_BLEND);
387         }
388
389         last_applied = this;
390         changes &= ~mask;
391 }
392
393 void PipelineState::clear()
394 {
395         if(last_applied)
396         {
397                 glUseProgram(0);
398                 glBindVertexArray(0);
399
400                 unsigned max_clip_planes = Limits::get_global().max_clip_planes;
401                 for(unsigned i=0; i<max_clip_planes; ++i)
402                         if((last_applied->enabled_clip_planes>>i)&1)
403                                 glDisable(GL_CLIP_PLANE0+i);
404
405                 for(vector<BoundTexture>::const_iterator i=last_applied->textures.begin(); i!=last_applied->textures.end(); ++i)
406                         if(i->texture && i->sampler)
407                         {
408                                 if(ARB_direct_state_access)
409                                         glBindTextureUnit(i->binding, 0);
410                                 else
411                                 {
412                                         glActiveTexture(GL_TEXTURE0+i->binding);
413                                         glBindTexture(bound_tex_targets[i->binding], 0);
414                                         bound_tex_targets[i->binding] = 0;
415                                 }
416                         }
417
418                 for(vector<BoundUniformBlock>::const_iterator i=last_applied->uniform_blocks.begin(); i!=last_applied->uniform_blocks.end(); ++i)
419                         if(i->block)
420                                 glBindBufferBase(GL_UNIFORM_BUFFER, i->binding, 0);
421
422                 glDisable(GL_DEPTH_TEST);
423                 glDepthMask(true);
424                 glDisable(GL_STENCIL_TEST);
425                 glDisable(GL_BLEND);
426
427                 last_applied = 0;
428         }
429 }
430
431
432 PipelineState::BoundTexture::BoundTexture(unsigned b):
433         binding(b),
434         changed(false),
435         texture(0),
436         sampler(0)
437 { }
438
439
440 PipelineState::BoundUniformBlock::BoundUniformBlock(int b):
441         binding(b),
442         changed(false),
443         block(0)
444 { }
445
446 } // namespace GL
447 } // namespace Msp