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