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