]> git.tdb.fi Git - libs/gl.git/blob - source/framebuffer.cpp
Rewrite Framebuffer to defer attach/detach operations until bound
[libs/gl.git] / source / framebuffer.cpp
1 /* $Id$
2
3 This file is part of libmspgl
4 Copyright © 2007  Mikko Rasa, Mikkosoft Productions
5 Distributed under the LGPL
6 */
7
8 #include "extension.h"
9 #include "ext_framebuffer_object.h"
10 #include "framebuffer.h"
11 #include "misc.h"
12 #include "renderbuffer.h"
13 #include "texture2d.h"
14
15 using namespace std;
16
17 namespace Msp {
18 namespace GL {
19
20 Framebuffer::Framebuffer(unsigned i):
21         id(i),
22         dirty(0)
23 {
24         if(id)
25                 throw InvalidParameterValue("System framebuffer must have id 0");
26
27         int viewport[4];
28         glGetIntegerv(GL_VIEWPORT, viewport);
29         width = viewport[2];
30         height = viewport[3];
31 }
32
33 Framebuffer::Framebuffer():
34         width(0),
35         height(0),
36         dirty(0)
37 {
38         static RequireExtension _ext("GL_EXT_framebuffer_object");
39
40         glGenFramebuffersEXT(1, &id);
41 }
42
43 Framebuffer::~Framebuffer()
44 {
45         if(id)
46                 glDeleteFramebuffersEXT(1, &id);
47         if(current()==this)
48                 unbind();
49 }
50
51 void Framebuffer::update_attachment(unsigned mask) const
52 {
53         if(current()==this)
54         {
55                 bool has_color = false;
56                 bool has_depth = false;
57                 for(unsigned i=0; i<attachments.size(); ++i)
58                 {
59                         const Attachment &attch = attachments[i];
60                         if(mask&(1<<i))
61                         {
62                                 if(attch.type==GL_RENDERBUFFER_EXT)
63                                         glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, attch.attachment, GL_RENDERBUFFER_EXT, attch.rbuf->get_id());
64                                 else if(attch.type==GL_TEXTURE_2D)
65                                         glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, attch.attachment, attch.type, attch.tex->get_id(), attch.level);
66                                 else
67                                         glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, attch.attachment, 0, 0);
68                         }
69
70                         if(attch.attachment>=COLOR_ATTACHMENT0 && attch.attachment<=COLOR_ATTACHMENT3)
71                                 has_color = true;
72                         if(attch.attachment==DEPTH_ATTACHMENT)
73                                 has_depth = true;
74                 }
75
76                 glDrawBuffer(has_color ? GL_FRONT : GL_NONE);
77                 glDepthMask(has_depth);
78         }
79         else
80                 dirty |= mask;
81 }
82
83 void Framebuffer::check_size()
84 {
85         for(vector<Attachment>::iterator i=attachments.begin(); i!=attachments.end(); ++i)
86                 if(i->type)
87                 {
88                         if(i->type==GL_RENDERBUFFER_EXT)
89                         {
90                                 width = i->rbuf->get_width();
91                                 height = i->rbuf->get_height();
92                         }
93                         else if(i->type==GL_TEXTURE_2D)
94                         {
95                                 Texture2D *tex = static_cast<Texture2D *>(i->tex);
96                                 width = tex->get_width();
97                                 height = tex->get_height();
98                         }
99                         if(current()==this)
100                                 glViewport(0, 0, width, height);
101                         break;
102                 }
103 }
104
105 void Framebuffer::attach(FramebufferAttachment attch, Renderbuffer &rbuf)
106 {
107         if(!id)
108                 throw InvalidState("Can't attach to system framebuffer");
109
110         unsigned i = get_attachment_index(attch);
111         attachments[i].set(rbuf);
112         update_attachment(1<<i);
113         check_size();
114 }
115
116 void Framebuffer::attach(FramebufferAttachment attch, Texture2D &tex, int level)
117 {
118         if(!id)
119                 throw InvalidState("Can't attach to system framebuffer");
120
121         unsigned i = get_attachment_index(attch);
122         attachments[i].set(tex, level);
123         update_attachment(1<<i);
124         check_size();
125 }
126
127 void Framebuffer::detach(FramebufferAttachment attch)
128 {
129         if(!id)
130                 throw InvalidState("Can't detach from system framebuffer");
131
132         unsigned i = get_attachment_index(attch);
133         attachments[i].clear();
134         update_attachment(1<<i);
135         check_size();
136 }
137
138 FramebufferStatus Framebuffer::check_status() const
139 {
140         Bind _bind(this, true);
141         return static_cast<FramebufferStatus>(glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT));
142 }
143
144 void Framebuffer::clear(BufferBits bits)
145 {
146         Bind _bind(this, true);
147         glClear(bits);
148 }
149
150 void Framebuffer::bind() const
151 {
152         if(set_current(this))
153         {
154                 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, id);
155                 if(dirty)
156                 {
157                         update_attachment(dirty);
158                         dirty = 0;
159                 }
160                 if(width && height)
161                         glViewport(0, 0, width, height);
162         }
163 }
164
165 const Framebuffer *Framebuffer::current()
166 {       
167         if(!cur_obj)
168                 cur_obj = &system();
169         return cur_obj;
170 }
171
172 void Framebuffer::unbind()
173 {
174         system().bind();
175 }
176
177 Framebuffer &Framebuffer::system()
178 {
179         static Framebuffer sys_framebuf(0);
180         return sys_framebuf;
181 }
182
183 unsigned Framebuffer::get_attachment_index(FramebufferAttachment attch)
184 {
185         for(unsigned i=0; i<attachments.size(); ++i)
186                 if(attachments[i].attachment==attch)
187                         return i;
188         attachments.push_back(Attachment(attch));
189         return attachments.size()-1;
190 }
191
192
193 Framebuffer::Attachment::Attachment(FramebufferAttachment a):
194         attachment(a),
195         type(0),
196         level(0)
197 { }
198
199 void Framebuffer::Attachment::set(Renderbuffer &r)
200 {
201         type = GL_RENDERBUFFER_EXT;
202         rbuf = &r;
203         level = 0;
204 }
205
206 void Framebuffer::Attachment::set(Texture &t, int l)
207 {
208         type = t.get_target();
209         tex = &t;
210         level = l;
211 }
212
213 void Framebuffer::Attachment::clear()
214 {
215         type = 0;
216 }
217
218 } // namespace GL
219 } // namespace Msp