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