]> git.tdb.fi Git - libs/gl.git/blob - source/core/buffer.cpp
Set OpenGL debug labels on various objects loaded from Resources
[libs/gl.git] / source / core / buffer.cpp
1 #include <stdexcept>
2 #include <msp/gl/extensions/arb_buffer_storage.h>
3 #include <msp/gl/extensions/arb_direct_state_access.h>
4 #include <msp/gl/extensions/arb_map_buffer_range.h>
5 #include <msp/gl/extensions/khr_debug.h>
6 #include <msp/strings/format.h>
7 #include "buffer.h"
8 #include "error.h"
9 #include "misc.h"
10 #include "vertexsetup.h"
11
12 using namespace std;
13
14 namespace Msp {
15 namespace GL {
16
17 const Buffer *Buffer::bound[5] = { 0, 0, 0, 0, 0 };
18 BufferType buffer_types[] = { ARRAY_BUFFER, ELEMENT_ARRAY_BUFFER, PIXEL_PACK_BUFFER, PIXEL_UNPACK_BUFFER, UNIFORM_BUFFER };
19
20 Buffer::Buffer(BufferType t):
21         type(t),
22         size(0),
23         allocated(false)
24 {
25         require_buffer_type(type);
26
27         if(ARB_direct_state_access)
28                 glCreateBuffers(1, &id);
29         else
30                 glGenBuffers(1, &id);
31 }
32
33 Buffer::~Buffer()
34 {
35         for(unsigned i=0; i<5; ++i)
36                 if(bound[i]==this)
37                         unbind_from(buffer_types[i]);
38         glDeleteBuffers(1, &id);
39 }
40
41 void Buffer::require_buffer_type(BufferType type)
42 {
43         static Require _req_vbo(ARB_vertex_buffer_object);
44         if(type==PIXEL_PACK_BUFFER || type==PIXEL_UNPACK_BUFFER)
45                 static Require _req_pbo(ARB_pixel_buffer_object);
46         else if(type==UNIFORM_BUFFER)
47                 static Require _req_ubo(ARB_uniform_buffer_object);
48 }
49
50 void Buffer::storage(unsigned sz)
51 {
52         if(size>0)
53         {
54                 if(sz!=size)
55                         throw incompatible_data("Buffer::storage");
56                 return;
57         }
58         if(sz==0)
59                 throw invalid_argument("Buffer::storage");
60
61         size = sz;
62 }
63
64 void Buffer::allocate()
65 {
66         if(size==0)
67                 throw invalid_operation("Buffer::allocate");
68         if(allocated)
69                 return;
70
71         if(ARB_buffer_storage)
72         {
73                 static const int flags = GL_MAP_READ_BIT|GL_MAP_WRITE_BIT|GL_DYNAMIC_STORAGE_BIT;
74                 if(ARB_direct_state_access)
75                         glNamedBufferStorage(id, size, 0, flags);
76                 else
77                 {
78                         BindRestore _bind(this, type);
79                         glBufferStorage(type, size, 0, flags);
80                 }
81
82                 allocated = true;
83         }
84         else
85                 data(0);
86 }
87
88 void Buffer::set_usage(BufferUsage)
89 {
90 }
91
92 void Buffer::data(const void *d)
93 {
94         if(size==0)
95                 throw invalid_operation("Buffer::data");
96
97         if(ARB_buffer_storage)
98                 return sub_data(0, size, d);
99
100         if(ARB_direct_state_access)
101                 glNamedBufferData(id, size, d, STATIC_DRAW);
102         else
103         {
104                 BindRestore _bind(this, type);
105                 glBufferData(type, size, d, STATIC_DRAW);
106         }
107
108         allocated = true;
109 }
110
111 void Buffer::data(unsigned sz, const void *d)
112 {
113         if(size==0)
114                 storage(sz);
115         else if(sz!=size)
116                 throw incompatible_data("Buffer::data");
117
118         data(d);
119 }
120
121 void Buffer::sub_data(unsigned off, unsigned sz, const void *d)
122 {
123         if(size==0)
124                 throw invalid_operation("Buffer::sub_data");
125
126         allocate();
127
128         if(ARB_direct_state_access)
129                 glNamedBufferSubData(id, off, sz, d);
130         else
131         {
132                 BindRestore _bind(this, type);
133                 glBufferSubData(type, off, sz, d);
134         }
135 }
136
137 void Buffer::require_size(unsigned req_sz) const
138 {
139         if(size<req_sz)
140                 throw buffer_too_small(format("buffer has %d bytes; %d required", size, req_sz));
141 }
142
143 BufferRange *Buffer::create_range(unsigned s, unsigned o)
144 {
145         return new BufferRange(*this, s, o);
146 }
147
148 void *Buffer::map()
149 {
150         allocate();
151         if(ARB_map_buffer_range)
152         {
153                 if(ARB_direct_state_access)
154                         return glMapNamedBufferRange(id, 0, size, GL_MAP_READ_BIT|GL_MAP_WRITE_BIT);
155                 else
156                 {
157                         BindRestore _bind(this, type);
158                         return glMapBufferRange(type, 0, size, GL_MAP_READ_BIT|GL_MAP_WRITE_BIT);
159                 }
160         }
161         else if(ARB_direct_state_access)
162                 return glMapNamedBuffer(id, GL_READ_WRITE);
163         else if(OES_mapbuffer)
164         {
165                 BindRestore _bind(this, type);
166                 return glMapBuffer(type, GL_READ_WRITE);
167         }
168         else
169                 throw invalid_operation("Buffer::map");
170 }
171
172 bool Buffer::unmap()
173 {
174         // TODO check if it's mapped
175         if(ARB_direct_state_access)
176                 return glUnmapNamedBuffer(id);
177         else if(OES_mapbuffer)
178         {
179                 BindRestore _bind(this, type);
180                 return glUnmapBuffer(type);
181         }
182         else
183                 return true;
184 }
185
186 void Buffer::bind_to(BufferType t) const
187 {
188         if(t!=type)
189                 require_buffer_type(t);
190         if(t==ELEMENT_ARRAY_BUFFER)
191                 if(const VertexSetup *vs = VertexSetup::current())
192                 {
193                         // Don't change the binding in a vertex array object
194                         if(this==vs->get_index_buffer())
195                                 return;
196                         throw invalid_operation("Buffer::bind_to(ELEMENT_ARRAY_BUFFER)");
197                 }
198         if(set_current(t, this))
199                 glBindBuffer(t, id);
200 }
201
202 const Buffer *Buffer::current(BufferType t)
203 {
204         if(t==ELEMENT_ARRAY_BUFFER)
205                 if(const VertexSetup *vs = VertexSetup::current())
206                         return vs->get_index_buffer();
207         return binding(t);
208 }
209
210 void Buffer::unbind_from(BufferType type)
211 {
212         if(type==ELEMENT_ARRAY_BUFFER && VertexSetup::current())
213                 throw invalid_operation("Buffer::unbind_from(ELEMENT_ARRAY_BUFFER)");
214         if(set_current(type, 0))
215                 glBindBuffer(type, 0);
216 }
217
218 const Buffer *&Buffer::binding(BufferType type)
219 {
220         switch(type)
221         {
222         case ARRAY_BUFFER:         return bound[0];
223         case ELEMENT_ARRAY_BUFFER: return bound[1];
224         case PIXEL_PACK_BUFFER:    return bound[2];
225         case PIXEL_UNPACK_BUFFER:  return bound[3];
226         case UNIFORM_BUFFER:       return bound[4];
227         default: throw invalid_argument("Buffer::binding");
228         }
229 }
230
231 bool Buffer::set_current(BufferType type, const Buffer *buf)
232 {
233         const Buffer *&ptr = binding(type);
234         if(ptr==buf)
235                 return false;
236
237         ptr = buf;
238         return true;
239 }
240
241 void Buffer::set_debug_name(const string &name)
242 {
243 #ifdef DEBUG
244         if(KHR_debug)
245                 glObjectLabel(GL_BUFFER, id, name.size(), name.c_str());
246 #else
247         (void)name;
248 #endif
249 }
250
251
252 vector<const BufferRange *> BufferRange::bound_uniform;
253
254 BufferRange::BufferRange(Buffer &b, unsigned o, unsigned s):
255         buffer(b),
256         offset(o),
257         size(s)
258 {
259         if(o>buffer.get_size() || o+s>buffer.get_size())
260                 throw out_of_range("BufferRange::BufferRange");
261 }
262
263 BufferRange::~BufferRange()
264 {
265         for(unsigned i=0; i<bound_uniform.size(); ++i)
266                 if(bound_uniform[i]==this)
267                         unbind_from(UNIFORM_BUFFER, i);
268 }
269
270 void BufferRange::data(const void *d)
271 {
272         buffer.sub_data(offset, size, d);
273 }
274
275 void BufferRange::bind_to(BufferType t, unsigned i)
276 {
277         if(t!=buffer.type)
278                 Buffer::require_buffer_type(t);
279         if(set_current(t, i, this))
280         {
281                 // The buffer gets bound as a side effect
282                 Buffer::set_current(t, &buffer);
283                 glBindBufferRange(t, i, buffer.id, offset, size);
284         }
285 }
286
287 void BufferRange::unbind_from(BufferType t, unsigned i)
288 {
289         if(set_current(t, i, 0))
290         {
291                 Buffer::set_current(t, 0);
292                 glBindBufferBase(t, i, 0);
293         }
294 }
295
296 const BufferRange *&BufferRange::binding(BufferType type, unsigned index)
297 {
298         if(type==UNIFORM_BUFFER)
299         {
300                 if(index>=get_n_uniform_buffer_bindings())
301                         throw out_of_range("BufferRange::binding");
302                 if(bound_uniform.size()<=index)
303                         bound_uniform.resize(index+1);
304                 return bound_uniform[index];
305         }
306         else
307                 throw invalid_argument("BufferRange::binding");
308 }
309
310 bool BufferRange::set_current(BufferType type, unsigned index, const BufferRange *buf)
311 {
312         const BufferRange *&ptr = binding(type, index);
313         if(ptr==buf)
314                 return false;
315
316         ptr = buf;
317         return true;
318 }
319
320 unsigned BufferRange::get_n_uniform_buffer_bindings()
321 {
322         static unsigned count = get_i(GL_MAX_UNIFORM_BUFFER_BINDINGS);
323         return count;
324 }
325
326 unsigned BufferRange::get_uniform_buffer_alignment()
327 {
328         static unsigned align = get_i(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT);
329         return align;
330 }
331
332 } // namespace GL
333 } // namespace Msp