]> git.tdb.fi Git - libs/gl.git/blob - source/framebuffer.cpp
Refactor Framebuffer::update_attachment for less indentation
[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                 dirty |= mask;
87                 return;
88         }
89
90         std::vector<GLenum> color_bufs;
91         color_bufs.reserve(attachments.size());
92         for(unsigned i=0; i<attachments.size(); ++i)
93         {
94                 const Attachment &attch = attachments[i];
95                 if(mask&(1<<i))
96                 {
97                         if(attch.type==GL_RENDERBUFFER)
98                                 glFramebufferRenderbuffer(GL_FRAMEBUFFER, attch.attachment, GL_RENDERBUFFER, attch.rbuf->get_id());
99                         else if(attch.type==GL_TEXTURE_2D)
100                         {
101                                 static_cast<Texture2D *>(attch.tex)->allocate(attch.level);
102                                 glFramebufferTexture2D(GL_FRAMEBUFFER, attch.attachment, attch.type, attch.tex->get_id(), attch.level);
103                         }
104                         else if(attch.type==GL_TEXTURE_CUBE_MAP)
105                         {
106                                 static_cast<TextureCube *>(attch.tex)->allocate(attch.level);
107                                 glFramebufferTexture2D(GL_FRAMEBUFFER, attch.attachment, attch.cube_face, attch.tex->get_id(), attch.level);
108                         }
109                         else
110                                 glFramebufferRenderbuffer(GL_FRAMEBUFFER, attch.attachment, 0, 0);
111                 }
112
113                 if(attch.attachment>=COLOR_ATTACHMENT0 && attch.attachment<=COLOR_ATTACHMENT3)
114                         color_bufs.push_back(attch.attachment);
115         }
116
117         if(color_bufs.size()>1)
118                 static Require _req(ARB_draw_buffers);
119
120         GLenum first_buffer = (color_bufs.empty() ? GL_NONE : color_bufs.front());
121         if(ARB_draw_buffers)
122                 glDrawBuffers(color_bufs.size(), &color_bufs[0]);
123         else if(MSP_draw_buffer)
124                 glDrawBuffer(first_buffer);
125
126         if(MSP_draw_buffer)
127                 glReadBuffer(first_buffer);
128 }
129
130 void Framebuffer::check_size()
131 {
132         bool full_viewport = (view.left==0 && view.bottom==0 && view.width==width && view.height==height);
133         for(vector<Attachment>::iterator i=attachments.begin(); i!=attachments.end(); ++i)
134                 if(i->type)
135                 {
136                         if(i->type==GL_RENDERBUFFER)
137                         {
138                                 width = i->rbuf->get_width();
139                                 height = i->rbuf->get_height();
140                         }
141                         else if(i->type==GL_TEXTURE_2D)
142                         {
143                                 Texture2D *tex = static_cast<Texture2D *>(i->tex);
144                                 width = tex->get_width();
145                                 height = tex->get_height();
146                         }
147                         else if(i->type==GL_TEXTURE_CUBE_MAP)
148                         {
149                                 width = static_cast<TextureCube *>(i->tex)->get_size();
150                                 height = width;
151                         }
152                         if(full_viewport)
153                                 reset_viewport();
154                         break;
155                 }
156 }
157
158 unsigned Framebuffer::get_attachment_index(FramebufferAttachment attch)
159 {
160         for(unsigned i=0; i<attachments.size(); ++i)
161                 if(attachments[i].attachment==attch)
162                         return i;
163         attachments.push_back(Attachment(attch));
164         return attachments.size()-1;
165 }
166
167 void Framebuffer::attach(FramebufferAttachment attch, Renderbuffer &rbuf)
168 {
169         if(!id)
170                 throw invalid_operation("Framebuffer::attach");
171
172         unsigned i = get_attachment_index(attch);
173         attachments[i].set(rbuf);
174         update_attachment(1<<i);
175         check_size();
176 }
177
178 void Framebuffer::attach(FramebufferAttachment attch, Texture2D &tex, unsigned level)
179 {
180         if(!id)
181                 throw invalid_operation("Framebuffer::attach");
182
183         unsigned i = get_attachment_index(attch);
184         attachments[i].set(tex, 0, level);
185         update_attachment(1<<i);
186         check_size();
187 }
188
189 void Framebuffer::attach(FramebufferAttachment attch, TextureCube &tex, TextureCubeFace face, unsigned level)
190 {
191         if(!id)
192                 throw invalid_operation("Framebuffer::attach");
193
194         unsigned i = get_attachment_index(attch);
195         attachments[i].set(tex, face, level);
196         update_attachment(1<<i);
197         check_size();
198 }
199
200 void Framebuffer::detach(FramebufferAttachment attch)
201 {
202         if(!id)
203                 throw invalid_operation("Framebuffer::detach");
204
205         unsigned i = get_attachment_index(attch);
206         attachments[i].clear();
207         update_attachment(1<<i);
208         check_size();
209 }
210
211 FramebufferStatus Framebuffer::check_status() const
212 {
213         BindRestore _bind(this);
214         return static_cast<FramebufferStatus>(glCheckFramebufferStatus(GL_FRAMEBUFFER));
215 }
216
217 void Framebuffer::require_complete() const
218 {
219         FramebufferStatus status = check_status();
220         if(status!=FRAMEBUFFER_COMPLETE)
221                 throw framebuffer_incomplete(status);
222 }
223
224 void Framebuffer::viewport(int l, int b, unsigned w, unsigned h)
225 {
226         view.left = l;
227         view.bottom = b;
228         view.width = w;
229         view.height = h;
230
231         if(current()==this)
232                 glViewport(view.left, view.bottom, view.width, view.height);
233 }
234
235 void Framebuffer::reset_viewport()
236 {
237         viewport(0, 0, width, height);
238 }
239
240 void Framebuffer::clear(BufferBits bits)
241 {
242         BindRestore _bind(this);
243         glClear(bits);
244 }
245
246 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)
247 {
248         static Require _req(EXT_framebuffer_blit);
249
250         const Framebuffer *old = current();
251         if(set_current(this))
252         {
253                 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, id);
254                 if(dirty)
255                 {
256                         update_attachment(dirty);
257                         dirty = 0;
258                 }
259         }
260         if(old!=&other)
261                 glBindFramebuffer(GL_READ_FRAMEBUFFER, other.id);
262
263         glBlitFramebuffer(sx0, sy0, sx1, sy1, dx0, dy0, dx1, dy1, bits, (filter ? GL_LINEAR : GL_NEAREST));
264
265         set_current(old);
266         glBindFramebuffer(GL_FRAMEBUFFER, (old ? old->id : 0));
267 }
268
269 void Framebuffer::blit_from(const Framebuffer &other, int sx, int sy, unsigned wd, unsigned ht, int dx, int dy, BufferBits bits)
270 {
271         blit_from(other, sx, sy, sx+wd, sy+ht, dx, dy, dx+wd, dy+ht, bits, false);
272 }
273
274 void Framebuffer::blit_from(const Framebuffer &other, BufferBits bits, bool filter)
275 {
276         blit_from(other, 0, 0, other.width, other.height, 0, 0, width, height, bits, filter);
277 }
278
279 void Framebuffer::bind() const
280 {
281         if(set_current(this))
282         {
283                 glBindFramebuffer(GL_FRAMEBUFFER, id);
284                 if(dirty)
285                 {
286                         update_attachment(dirty);
287                         dirty = 0;
288                 }
289
290                 if(width && height)
291                         glViewport(view.left, view.bottom, view.width, view.height);
292         }
293 }
294
295 const Framebuffer *Framebuffer::current()
296 {
297         if(!cur_obj)
298                 cur_obj = &system();
299         return cur_obj;
300 }
301
302 void Framebuffer::unbind()
303 {
304         system().bind();
305 }
306
307 Framebuffer &Framebuffer::system()
308 {
309         static Framebuffer sys_framebuf(0);
310         return sys_framebuf;
311 }
312
313
314 Framebuffer::Attachment::Attachment(FramebufferAttachment a):
315         attachment(a),
316         type(0),
317         level(0)
318 { }
319
320 void Framebuffer::Attachment::set(Renderbuffer &r)
321 {
322         type = GL_RENDERBUFFER;
323         rbuf = &r;
324         level = 0;
325 }
326
327 void Framebuffer::Attachment::set(Texture &t, GLenum f, unsigned l)
328 {
329         type = t.get_target();
330         tex = &t;
331         cube_face = f;
332         level = l;
333 }
334
335 void Framebuffer::Attachment::clear()
336 {
337         type = 0;
338 }
339
340
341 Framebuffer::Viewport::Viewport():
342         left(0),
343         bottom(0),
344         width(0),
345         height(0)
346 { }
347
348 } // namespace GL
349 } // namespace Msp