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