]> git.tdb.fi Git - libs/gl.git/blob - source/core/buffer.cpp
4750846ab1dceb291a600043977f1c06f868f403
[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 "deviceinfo.h"
9 #include "error.h"
10 #include "misc.h"
11 #include "vertexsetup.h"
12
13 using namespace std;
14
15 namespace Msp {
16 namespace GL {
17
18 const Buffer *Buffer::bound[5] = { 0, 0, 0, 0, 0 };
19 BufferType buffer_types[] = { ARRAY_BUFFER, ELEMENT_ARRAY_BUFFER, PIXEL_PACK_BUFFER, PIXEL_UNPACK_BUFFER, UNIFORM_BUFFER };
20
21 Buffer::Buffer():
22         size(0),
23         allocated(false)
24 {
25         static Require _req(ARB_vertex_buffer_object);
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, ARRAY_BUFFER);
79                         glBufferStorage(ARRAY_BUFFER, 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, ARRAY_BUFFER);
105                 glBufferData(ARRAY_BUFFER, 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, ARRAY_BUFFER);
133                 glBufferSubData(ARRAY_BUFFER, 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         static Require _req(ARB_map_buffer_range);
151
152         allocate();
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, ARRAY_BUFFER);
158                 return glMapBufferRange(ARRAY_BUFFER, 0, size, GL_MAP_READ_BIT|GL_MAP_WRITE_BIT);
159         }
160 }
161
162 bool Buffer::unmap()
163 {
164         // TODO check if it's mapped
165         if(ARB_direct_state_access)
166                 return glUnmapNamedBuffer(id);
167         else if(OES_mapbuffer)
168         {
169                 BindRestore _bind(this, ARRAY_BUFFER);
170                 return glUnmapBuffer(ARRAY_BUFFER);
171         }
172         else
173                 return true;
174 }
175
176 void Buffer::bind_to(BufferType t) const
177 {
178         require_buffer_type(t);
179         if(t==ELEMENT_ARRAY_BUFFER)
180                 if(const VertexSetup *vs = VertexSetup::current())
181                 {
182                         // Don't change the binding in a vertex array object
183                         if(this==vs->get_index_buffer())
184                                 return;
185                         throw invalid_operation("Buffer::bind_to(ELEMENT_ARRAY_BUFFER)");
186                 }
187         if(set_current(t, this))
188                 glBindBuffer(t, id);
189 }
190
191 const Buffer *Buffer::current(BufferType t)
192 {
193         if(t==ELEMENT_ARRAY_BUFFER)
194                 if(const VertexSetup *vs = VertexSetup::current())
195                         return vs->get_index_buffer();
196         return binding(t);
197 }
198
199 void Buffer::unbind_from(BufferType type)
200 {
201         if(type==ELEMENT_ARRAY_BUFFER && VertexSetup::current())
202                 throw invalid_operation("Buffer::unbind_from(ELEMENT_ARRAY_BUFFER)");
203         if(set_current(type, 0))
204                 glBindBuffer(type, 0);
205 }
206
207 const Buffer *&Buffer::binding(BufferType type)
208 {
209         switch(type)
210         {
211         case ARRAY_BUFFER:         return bound[0];
212         case ELEMENT_ARRAY_BUFFER: return bound[1];
213         case PIXEL_PACK_BUFFER:    return bound[2];
214         case PIXEL_UNPACK_BUFFER:  return bound[3];
215         case UNIFORM_BUFFER:       return bound[4];
216         default: throw invalid_argument("Buffer::binding");
217         }
218 }
219
220 bool Buffer::set_current(BufferType type, const Buffer *buf)
221 {
222         const Buffer *&ptr = binding(type);
223         if(ptr==buf)
224                 return false;
225
226         ptr = buf;
227         return true;
228 }
229
230 void Buffer::set_debug_name(const string &name)
231 {
232 #ifdef DEBUG
233         if(KHR_debug)
234                 glObjectLabel(GL_BUFFER, id, name.size(), name.c_str());
235 #else
236         (void)name;
237 #endif
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         Buffer::require_buffer_type(t);
267         if(set_current(t, i, this))
268         {
269                 // The buffer gets bound as a side effect
270                 Buffer::set_current(t, &buffer);
271                 glBindBufferRange(t, i, buffer.id, offset, size);
272         }
273 }
274
275 void BufferRange::unbind_from(BufferType t, unsigned i)
276 {
277         if(set_current(t, i, 0))
278         {
279                 Buffer::set_current(t, 0);
280                 glBindBufferBase(t, i, 0);
281         }
282 }
283
284 const BufferRange *&BufferRange::binding(BufferType type, unsigned index)
285 {
286         if(type==UNIFORM_BUFFER)
287         {
288                 if(index>=Limits::get_global().max_uniform_bindings)
289                         throw out_of_range("BufferRange::binding");
290                 if(bound_uniform.size()<=index)
291                         bound_uniform.resize(index+1);
292                 return bound_uniform[index];
293         }
294         else
295                 throw invalid_argument("BufferRange::binding");
296 }
297
298 bool BufferRange::set_current(BufferType type, unsigned index, const BufferRange *buf)
299 {
300         const BufferRange *&ptr = binding(type, index);
301         if(ptr==buf)
302                 return false;
303
304         ptr = buf;
305         return true;
306 }
307
308 unsigned BufferRange::get_n_uniform_buffer_bindings()
309 {
310         return Limits::get_global().max_uniform_bindings;
311 }
312
313 unsigned BufferRange::get_uniform_buffer_alignment()
314 {
315         return Limits::get_global().uniform_buffer_alignment;
316 }
317
318 } // namespace GL
319 } // namespace Msp