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