]> git.tdb.fi Git - libs/gl.git/blob - source/core/buffer.cpp
56810b6f4ff12aa41f9c37b7812f5d4496187e7a
[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         if(ARB_map_buffer_range)
146         {
147                 if(ARB_direct_state_access)
148                         return glMapNamedBufferRange(id, 0, size, GL_MAP_READ_BIT|GL_MAP_WRITE_BIT);
149                 else
150                 {
151                         BindRestore _bind(this, type);
152                         return glMapBufferRange(type, 0, size, GL_MAP_READ_BIT|GL_MAP_WRITE_BIT);
153                 }
154         }
155         else if(ARB_direct_state_access)
156                 return glMapNamedBuffer(id, GL_READ_WRITE);
157         else if(OES_mapbuffer)
158         {
159                 BindRestore _bind(this, type);
160                 return glMapBuffer(type, GL_READ_WRITE);
161         }
162         else
163                 throw invalid_operation("Buffer::map");
164 }
165
166 bool Buffer::unmap()
167 {
168         // TODO check if it's mapped
169         if(ARB_direct_state_access)
170                 return glUnmapNamedBuffer(id);
171         else if(OES_mapbuffer)
172         {
173                 BindRestore _bind(this, type);
174                 return glUnmapBuffer(type);
175         }
176         else
177                 return true;
178 }
179
180 void Buffer::bind_to(BufferType t) const
181 {
182         if(t!=type)
183                 require_buffer_type(t);
184         if(t==ELEMENT_ARRAY_BUFFER)
185                 if(const VertexSetup *vs = VertexSetup::current())
186                 {
187                         // Don't change the binding in a vertex array object
188                         if(this==vs->get_index_buffer())
189                                 return;
190                         throw invalid_operation("Buffer::bind_to(ELEMENT_ARRAY_BUFFER)");
191                 }
192         if(set_current(t, this))
193                 glBindBuffer(t, id);
194 }
195
196 const Buffer *Buffer::current(BufferType t)
197 {
198         if(t==ELEMENT_ARRAY_BUFFER)
199                 if(const VertexSetup *vs = VertexSetup::current())
200                         return vs->get_index_buffer();
201         return binding(t);
202 }
203
204 void Buffer::unbind_from(BufferType type)
205 {
206         if(type==ELEMENT_ARRAY_BUFFER && VertexSetup::current())
207                 throw invalid_operation("Buffer::unbind_from(ELEMENT_ARRAY_BUFFER)");
208         if(set_current(type, 0))
209                 glBindBuffer(type, 0);
210 }
211
212 const Buffer *&Buffer::binding(BufferType type)
213 {
214         switch(type)
215         {
216         case ARRAY_BUFFER:         return bound[0];
217         case ELEMENT_ARRAY_BUFFER: return bound[1];
218         case PIXEL_PACK_BUFFER:    return bound[2];
219         case PIXEL_UNPACK_BUFFER:  return bound[3];
220         case UNIFORM_BUFFER:       return bound[4];
221         default: throw invalid_argument("Buffer::binding");
222         }
223 }
224
225 bool Buffer::set_current(BufferType type, const Buffer *buf)
226 {
227         const Buffer *&ptr = binding(type);
228         if(ptr==buf)
229                 return false;
230
231         ptr = buf;
232         return true;
233 }
234
235
236 vector<const BufferRange *> BufferRange::bound_uniform;
237
238 BufferRange::BufferRange(Buffer &b, unsigned o, unsigned s):
239         buffer(b),
240         offset(o),
241         size(s)
242 {
243         if(o>buffer.get_size() || o+s>buffer.get_size())
244                 throw out_of_range("BufferRange::BufferRange");
245 }
246
247 BufferRange::~BufferRange()
248 {
249         for(unsigned i=0; i<bound_uniform.size(); ++i)
250                 if(bound_uniform[i]==this)
251                         unbind_from(UNIFORM_BUFFER, i);
252 }
253
254 void BufferRange::data(const void *d)
255 {
256         buffer.sub_data(offset, size, d);
257 }
258
259 void BufferRange::bind_to(BufferType t, unsigned i)
260 {
261         if(t!=buffer.type)
262                 Buffer::require_buffer_type(t);
263         if(set_current(t, i, this))
264         {
265                 // The buffer gets bound as a side effect
266                 Buffer::set_current(t, &buffer);
267                 glBindBufferRange(t, i, buffer.id, offset, size);
268         }
269 }
270
271 void BufferRange::unbind_from(BufferType t, unsigned i)
272 {
273         if(set_current(t, i, 0))
274         {
275                 Buffer::set_current(t, 0);
276                 glBindBufferBase(t, i, 0);
277         }
278 }
279
280 const BufferRange *&BufferRange::binding(BufferType type, unsigned index)
281 {
282         if(type==UNIFORM_BUFFER)
283         {
284                 if(index>=get_n_uniform_buffer_bindings())
285                         throw out_of_range("BufferRange::binding");
286                 if(bound_uniform.size()<=index)
287                         bound_uniform.resize(index+1);
288                 return bound_uniform[index];
289         }
290         else
291                 throw invalid_argument("BufferRange::binding");
292 }
293
294 bool BufferRange::set_current(BufferType type, unsigned index, const BufferRange *buf)
295 {
296         const BufferRange *&ptr = binding(type, index);
297         if(ptr==buf)
298                 return false;
299
300         ptr = buf;
301         return true;
302 }
303
304 unsigned BufferRange::get_n_uniform_buffer_bindings()
305 {
306         static unsigned count = get_i(GL_MAX_UNIFORM_BUFFER_BINDINGS);
307         return count;
308 }
309
310 unsigned BufferRange::get_uniform_buffer_alignment()
311 {
312         static unsigned align = get_i(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT);
313         return align;
314 }
315
316 } // namespace GL
317 } // namespace Msp