]> git.tdb.fi Git - libs/gl.git/blob - source/core/vertexsetup.cpp
Clear VertexSetup state when a Mesh is unloaded
[libs/gl.git] / source / core / vertexsetup.cpp
1 #include <msp/core/raii.h>
2 #include <msp/gl/extensions/arb_direct_state_access.h>
3 #include <msp/gl/extensions/arb_instanced_arrays.h>
4 #include <msp/gl/extensions/arb_vertex_array_object.h>
5 #include <msp/gl/extensions/arb_vertex_attrib_binding.h>
6 #include <msp/gl/extensions/arb_vertex_buffer_object.h>
7 #include <msp/gl/extensions/arb_vertex_shader.h>
8 #include <msp/gl/extensions/khr_debug.h>
9 #include "buffer.h"
10 #include "error.h"
11 #include "gl.h"
12 #include "misc.h"
13 #include "vertexarray.h"
14 #include "vertexsetup.h"
15
16 using namespace std;
17
18 namespace Msp {
19 namespace GL {
20
21 VertexSetup::VertexSetup():
22         dirty(0),
23         vertex_array(0),
24         inst_array(0),
25         index_buffer(0)
26 {
27         static Require req(ARB_vertex_array_object);
28         if(ARB_direct_state_access)
29                 glCreateVertexArrays(1, &id);
30         else
31                 glGenVertexArrays(1, &id);
32 }
33
34 VertexSetup::~VertexSetup()
35 {
36         if(current()==this)
37                 unbind();
38         glDeleteVertexArrays(1, &id);
39 }
40
41 void VertexSetup::set_vertex_array(const VertexArray &a)
42 {
43         if(!verify_array(a))
44                 throw invalid_argument("VertexSetup::set_vertex_array");
45
46         vertex_array = &a;
47         update(get_update_mask(VERTEX_ARRAY, vertex_format, *vertex_array));
48         vertex_format = vertex_array->get_format();
49 }
50
51 void VertexSetup::set_instance_array(const VertexArray *a)
52 {
53         if(a)
54         {
55                 if(!verify_array(*a))
56                         throw invalid_argument("VertexSetup::set_instance_array");
57
58                 static Require req(ARB_instanced_arrays);
59         }
60
61         inst_array = a;
62         update(get_update_mask(INSTANCE_ARRAY, inst_format, *inst_array));
63         inst_format = inst_array->get_format();
64 }
65
66 void VertexSetup::set_index_buffer(const Buffer &ibuf)
67 {
68         index_buffer = &ibuf;
69         update(INDEX_BUFFER);
70 }
71
72 void VertexSetup::refresh()
73 {
74         if(vertex_array && vertex_array->get_format()!=vertex_format)
75                 set_vertex_array(*vertex_array);
76
77         if(inst_array && inst_array->get_format()!=inst_format)
78                 set_instance_array(inst_array);
79 }
80
81 bool VertexSetup::verify_array(const VertexArray &array)
82 {
83         if(!array.get_buffer())
84                 return false;
85
86         static int max_attribs = -1;
87         if(max_attribs<0)
88                 max_attribs = get_i(GL_MAX_VERTEX_ATTRIBS);
89
90         const VertexFormat &fmt = array.get_format();
91         for(const unsigned char *a=fmt.begin(); a!=fmt.end(); ++a)
92                 if(static_cast<int>(get_attribute_semantic(*a))>=max_attribs)
93                         return false;
94
95         return true;
96 }
97
98 unsigned VertexSetup::get_attribs(const VertexFormat &fmt)
99 {
100         unsigned mask = 0;
101         for(const unsigned char *a=fmt.begin(); a!=fmt.end(); ++a)
102                 mask |= 1<<get_attribute_semantic(*a);
103         return mask;
104 }
105
106 unsigned VertexSetup::get_update_mask(unsigned base, const VertexFormat &cur_fmt, const VertexArray &new_array)
107 {
108         unsigned unused = get_attribs(cur_fmt)&~get_attribs(new_array.get_format());
109         return base | (unused ? UNUSED_ATTRIBS | (unused<<ATTRIB_SHIFT) : 0);
110 }
111
112 void VertexSetup::update(unsigned mask) const
113 {
114         static bool direct = ARB_direct_state_access && ARB_vertex_attrib_binding;
115         if(!direct && current()!=this)
116         {
117                 dirty |= mask;
118                 return;
119         }
120
121         if(mask&UNUSED_ATTRIBS)
122         {
123                 for(unsigned i=0, am=mask>>ATTRIB_SHIFT; am; ++i, am>>=1)
124                         if(am&1)
125                         {
126                                 if(direct)
127                                         glDisableVertexArrayAttrib(id, i);
128                                 else
129                                         glDisableVertexAttribArray(i);
130                         }
131         }
132
133         if(mask&VERTEX_ARRAY)
134                 update_vertex_array(*vertex_array, 0, 0, direct);
135
136         if((mask&INSTANCE_ARRAY) && inst_array)
137                 update_vertex_array(*inst_array, 1, 1, direct);
138
139         if(mask&INDEX_BUFFER)
140         {
141                 if(direct)
142                         glVertexArrayElementBuffer(id, index_buffer->get_id());
143                 else
144                         glBindBuffer(ELEMENT_ARRAY_BUFFER, index_buffer->get_id());
145         }
146 }
147
148 void VertexSetup::update_vertex_array(const VertexArray &array, unsigned binding, unsigned divisor, bool direct) const
149 {
150         Conditional<Bind> bind_vbuf(!direct, array.get_buffer(), ARRAY_BUFFER);
151
152         const VertexFormat &fmt = array.get_format();
153         unsigned stride = fmt.stride()*sizeof(float);
154         if(direct)
155         {
156                 glVertexArrayVertexBuffer(id, binding, array.get_buffer()->get_id(), 0, stride);
157                 glVertexArrayBindingDivisor(id, binding, divisor);
158         }
159
160         unsigned offset = 0;
161         for(const unsigned char *a=fmt.begin(); a!=fmt.end(); ++a)
162         {
163                 unsigned sem = get_attribute_semantic(*a);
164                 unsigned sz = get_attribute_size(*a);
165                 if(direct)
166                 {
167                         if(*a==COLOR4_UBYTE)
168                                 glVertexArrayAttribFormat(id, sem, 4, GL_UNSIGNED_BYTE, true, offset);
169                         else
170                                 glVertexArrayAttribFormat(id, sem, sz, GL_FLOAT, false, offset);
171                         glVertexArrayAttribBinding(id, sem, binding);
172                         glEnableVertexArrayAttrib(id, sem);
173                 }
174                 else
175                 {
176                         if(*a==COLOR4_UBYTE)
177                                 glVertexAttribPointer(sem, 4, GL_UNSIGNED_BYTE, true, stride, reinterpret_cast<unsigned char *>(offset));
178                         else
179                                 glVertexAttribPointer(sem, sz, GL_FLOAT, false, stride, reinterpret_cast<float *>(offset));
180                         if(ARB_instanced_arrays)
181                                 glVertexAttribDivisor(sem, divisor);
182                         glEnableVertexAttribArray(sem);
183                 }
184                 offset += sz*sizeof(float);
185         }
186 }
187
188 void VertexSetup::bind() const
189 {
190         if(!vertex_array || !index_buffer)
191                 throw invalid_operation("VertexSetup::bind");
192
193         if(set_current(this))
194         {
195                 vertex_array->refresh();
196                 if(inst_array)
197                         inst_array->refresh();
198                 glBindVertexArray(id);
199                 if(dirty)
200                 {
201                         update(dirty);
202                         dirty = 0;
203                 }
204         }
205 }
206
207 void VertexSetup::unbind()
208 {
209         if(set_current(0))
210                 glBindVertexArray(0);
211 }
212
213 void VertexSetup::unload()
214 {
215         if(ARB_direct_state_access)
216         {
217                 glVertexArrayVertexBuffer(id, 0, 0, 0, 0);
218                 glVertexArrayVertexBuffer(id, 1, 0, 0, 0);
219                 glVertexArrayElementBuffer(id, 0);
220         }
221         else
222         {
223                 BindRestore _bind(*this);
224                 Buffer::unbind_from(ARRAY_BUFFER);
225
226                 unsigned mask = get_attribs(vertex_format)|get_attribs(inst_format);
227                 for(unsigned i=0; mask; ++i, mask>>=1)
228                         if(mask&1)
229                         {
230                                 glDisableVertexAttribArray(i);
231                                 glVertexAttribPointer(i, 1, GL_FLOAT, false, 0, 0);
232                         }
233                 glBindBuffer(ELEMENT_ARRAY_BUFFER, 0);
234         }
235
236         vertex_array = 0;
237         vertex_format = VertexFormat();
238         inst_array = 0;
239         inst_format = VertexFormat();
240         index_buffer = 0;
241 }
242
243 void VertexSetup::set_debug_name(const string &name)
244 {
245 #ifdef DEBUG
246         if(KHR_debug)
247                 glObjectLabel(GL_VERTEX_ARRAY, id, name.size(), name.c_str());
248 #else
249         (void)name;
250 #endif
251 }
252
253 } // namespace GL
254 } // namespace Msp