]> git.tdb.fi Git - libs/gl.git/blob - source/framebuffer.cpp
Support OpenGL ES on Android
[libs/gl.git] / source / framebuffer.cpp
1 #include <msp/gl/extensions/arb_draw_buffers.h>
2 #include <msp/gl/extensions/ext_framebuffer_blit.h>
3 #include <msp/gl/extensions/ext_framebuffer_object.h>
4 #include <msp/gl/extensions/msp_draw_buffer.h>
5 #include "error.h"
6 #include "framebuffer.h"
7 #include "misc.h"
8 #include "renderbuffer.h"
9 #include "texture2d.h"
10
11 using namespace std;
12
13 namespace Msp {
14 namespace GL {
15
16 void operator<<(LexicalConverter &conv, FramebufferStatus status)
17 {
18         switch(status)
19         {
20         case FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
21                 conv.result("incomplete attachment");
22                 break;
23         case FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
24                 conv.result("missing attachment");
25                 break;
26         case FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
27                 conv.result("mismatched attachment dimensions");
28                 break;
29         case FRAMEBUFFER_INCOMPLETE_FORMATS:
30                 conv.result("mismatched attachment formats");
31                 break;
32         case FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
33                 conv.result("missing draw buffer attachment");
34                 break;
35         case FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
36                 conv.result("missing read buffer attachment");
37                 break;
38         case FRAMEBUFFER_UNSUPPORTED:
39                 conv.result("unsupported");
40                 break;
41         default:
42                 conv.result(lexical_cast<string, unsigned>(status, "%#x"));
43                 break;
44         }
45 }
46
47 framebuffer_incomplete::framebuffer_incomplete(FramebufferStatus status):
48         runtime_error(lexical_cast<string>(status))
49 { }
50
51
52 Framebuffer::Framebuffer(unsigned i):
53         id(i),
54         dirty(0)
55 {
56         if(id)
57                 throw invalid_argument("System framebuffer must have id 0");
58
59         glGetIntegerv(GL_VIEWPORT, &view.left);
60         width = view.width;
61         height = view.height;
62 }
63
64 Framebuffer::Framebuffer():
65         width(0),
66         height(0),
67         dirty(0)
68 {
69         static Require _req(EXT_framebuffer_object);
70
71         glGenFramebuffers(1, &id);
72 }
73
74 Framebuffer::~Framebuffer()
75 {
76         if(id)
77                 glDeleteFramebuffers(1, &id);
78         if(current()==this)
79                 unbind();
80 }
81
82 void Framebuffer::update_attachment(unsigned mask) const
83 {
84         if(current()==this)
85         {
86                 std::vector<GLenum> color_bufs;
87                 color_bufs.reserve(attachments.size());
88                 for(unsigned i=0; i<attachments.size(); ++i)
89                 {
90                         const Attachment &attch = attachments[i];
91                         if(mask&(1<<i))
92                         {
93                                 if(attch.type==GL_RENDERBUFFER)
94                                         glFramebufferRenderbuffer(GL_FRAMEBUFFER, attch.attachment, GL_RENDERBUFFER, attch.rbuf->get_id());
95                                 else if(attch.type==GL_TEXTURE_2D)
96                                 {
97                                         static_cast<Texture2D *>(attch.tex)->allocate(attch.level);
98                                         glFramebufferTexture2D(GL_FRAMEBUFFER, attch.attachment, attch.type, attch.tex->get_id(), attch.level);
99                                 }
100                                 else if(attch.type==GL_TEXTURE_CUBE_MAP)
101                                 {
102                                         static_cast<TextureCube *>(attch.tex)->allocate(attch.level);
103                                         glFramebufferTexture2D(GL_FRAMEBUFFER, attch.attachment, attch.cube_face, attch.tex->get_id(), attch.level);
104                                 }
105                                 else
106                                         glFramebufferRenderbuffer(GL_FRAMEBUFFER, attch.attachment, 0, 0);
107                         }
108
109                         if(attch.attachment>=COLOR_ATTACHMENT0 && attch.attachment<=COLOR_ATTACHMENT3)
110                                 color_bufs.push_back(attch.attachment);
111                 }
112
113                 if(color_bufs.size()>1)
114                         static Require _req(ARB_draw_buffers);
115
116                 GLenum first_buffer = (color_bufs.empty() ? GL_NONE : color_bufs.front());
117                 if(ARB_draw_buffers)
118                         glDrawBuffers(color_bufs.size(), &color_bufs[0]);
119                 else if(MSP_draw_buffer)
120                         glDrawBuffer(first_buffer);
121
122                 if(MSP_draw_buffer)
123                         glReadBuffer(first_buffer);
124         }
125         else
126                 dirty |= mask;
127 }
128
129 void Framebuffer::check_size()
130 {
131         bool full_viewport = (view.left==0 && view.bottom==0 && view.width==width && view.height==height);
132         for(vector<Attachment>::iterator i=attachments.begin(); i!=attachments.end(); ++i)
133                 if(i->type)
134                 {
135                         if(i->type==GL_RENDERBUFFER)
136                         {
137                                 width = i->rbuf->get_width();
138                                 height = i->rbuf->get_height();
139                         }
140                         else if(i->type==GL_TEXTURE_2D)
141                         {
142                                 Texture2D *tex = static_cast<Texture2D *>(i->tex);
143                                 width = tex->get_width();
144                                 height = tex->get_height();
145                         }
146                         else if(i->type==GL_TEXTURE_CUBE_MAP)
147                         {
148                                 width = static_cast<TextureCube *>(i->tex)->get_size();
149                                 height = width;
150                         }
151                         if(full_viewport)
152                                 reset_viewport();
153                         break;
154                 }
155 }
156
157 unsigned Framebuffer::get_attachment_index(FramebufferAttachment attch)
158 {
159         for(unsigned i=0; i<attachments.size(); ++i)
160                 if(attachments[i].attachment==attch)
161                         return i;
162         attachments.push_back(Attachment(attch));
163         return attachments.size()-1;
164 }
165
166 void Framebuffer::attach(FramebufferAttachment attch, Renderbuffer &rbuf)
167 {
168         if(!id)
169                 throw invalid_operation("Framebuffer::attach");
170
171         unsigned i = get_attachment_index(attch);
172         attachments[i].set(rbuf);
173         update_attachment(1<<i);
174         check_size();
175 }
176
177 void Framebuffer::attach(FramebufferAttachment attch, Texture2D &tex, unsigned level)
178 {
179         if(!id)
180                 throw invalid_operation("Framebuffer::attach");
181
182         unsigned i = get_attachment_index(attch);
183         attachments[i].set(tex, 0, level);
184         update_attachment(1<<i);
185         check_size();
186 }
187
188 void Framebuffer::attach(FramebufferAttachment attch, TextureCube &tex, TextureCubeFace face, unsigned level)
189 {
190         if(!id)
191                 throw invalid_operation("Framebuffer::attach");
192
193         unsigned i = get_attachment_index(attch);
194         attachments[i].set(tex, face, level);
195         update_attachment(1<<i);
196         check_size();
197 }
198
199 void Framebuffer::detach(FramebufferAttachment attch)
200 {
201         if(!id)
202                 throw invalid_operation("Framebuffer::detach");
203
204         unsigned i = get_attachment_index(attch);
205         attachments[i].clear();
206         update_attachment(1<<i);
207         check_size();
208 }
209
210 FramebufferStatus Framebuffer::check_status() const
211 {
212         BindRestore _bind(this);
213         return static_cast<FramebufferStatus>(glCheckFramebufferStatus(GL_FRAMEBUFFER));
214 }
215
216 void Framebuffer::require_complete() const
217 {
218         FramebufferStatus status = check_status();
219         if(status!=FRAMEBUFFER_COMPLETE)
220                 throw framebuffer_incomplete(status);
221 }
222
223 void Framebuffer::viewport(int l, int b, unsigned w, unsigned h)
224 {
225         view.left = l;
226         view.bottom = b;
227         view.width = w;
228         view.height = h;
229
230         if(current()==this)
231                 glViewport(view.left, view.bottom, view.width, view.height);
232 }
233
234 void Framebuffer::reset_viewport()
235 {
236         viewport(0, 0, width, height);
237 }
238
239 void Framebuffer::clear(BufferBits bits)
240 {
241         BindRestore _bind(this);
242         glClear(bits);
243 }
244
245 void Framebuffer::blit_from(const Framebuffer &other, int sx0, int sy0, int sx1, int sy1, int dx0, int dy0, int dx1, int dy1, BufferBits bits, bool filter)
246 {
247         static Require _req(EXT_framebuffer_blit);
248
249         const Framebuffer *old = current();
250         if(set_current(this))
251         {
252                 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, id);
253                 if(dirty)
254                 {
255                         update_attachment(dirty);
256                         dirty = 0;
257                 }
258         }
259         if(old!=&other)
260                 glBindFramebuffer(GL_READ_FRAMEBUFFER, other.id);
261
262         glBlitFramebuffer(sx0, sy0, sx1, sy1, dx0, dy0, dx1, dy1, bits, (filter ? GL_LINEAR : GL_NEAREST));
263
264         set_current(old);
265         glBindFramebuffer(GL_FRAMEBUFFER, (old ? old->id : 0));
266 }
267
268 void Framebuffer::blit_from(const Framebuffer &other, int sx, int sy, unsigned wd, unsigned ht, int dx, int dy, BufferBits bits)
269 {
270         blit_from(other, sx, sy, sx+wd, sy+ht, dx, dy, dx+wd, dy+ht, bits, false);
271 }
272
273 void Framebuffer::blit_from(const Framebuffer &other, BufferBits bits, bool filter)
274 {
275         blit_from(other, 0, 0, other.width, other.height, 0, 0, width, height, bits, filter);
276 }
277
278 void Framebuffer::bind() const
279 {
280         if(set_current(this))
281         {
282                 glBindFramebuffer(GL_FRAMEBUFFER, id);
283                 if(dirty)
284                 {
285                         update_attachment(dirty);
286                         dirty = 0;
287                 }
288
289                 if(width && height)
290                         glViewport(view.left, view.bottom, view.width, view.height);
291         }
292 }
293
294 const Framebuffer *Framebuffer::current()
295 {
296         if(!cur_obj)
297                 cur_obj = &system();
298         return cur_obj;
299 }
300
301 void Framebuffer::unbind()
302 {
303         system().bind();
304 }
305
306 Framebuffer &Framebuffer::system()
307 {
308         static Framebuffer sys_framebuf(0);
309         return sys_framebuf;
310 }
311
312
313 Framebuffer::Attachment::Attachment(FramebufferAttachment a):
314         attachment(a),
315         type(0),
316         level(0)
317 { }
318
319 void Framebuffer::Attachment::set(Renderbuffer &r)
320 {
321         type = GL_RENDERBUFFER;
322         rbuf = &r;
323         level = 0;
324 }
325
326 void Framebuffer::Attachment::set(Texture &t, GLenum f, unsigned l)
327 {
328         type = t.get_target();
329         tex = &t;
330         cube_face = f;
331         level = l;
332 }
333
334 void Framebuffer::Attachment::clear()
335 {
336         type = 0;
337 }
338
339
340 Framebuffer::Viewport::Viewport():
341         left(0),
342         bottom(0),
343         width(0),
344         height(0)
345 { }
346
347 } // namespace GL
348 } // namespace Msp