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