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