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