]> git.tdb.fi Git - libs/gl.git/blob - source/framebuffer.cpp
Plug a memory leak
[libs/gl.git] / source / framebuffer.cpp
1 #include "error.h"
2 #include "extension.h"
3 #include "ext_framebuffer_blit.h"
4 #include "ext_framebuffer_object.h"
5 #include "framebuffer.h"
6 #include "misc.h"
7 #include "renderbuffer.h"
8 #include "texture2d.h"
9
10 using namespace std;
11
12 namespace Msp {
13 namespace GL {
14
15 Framebuffer::Framebuffer(unsigned i):
16         id(i),
17         dirty(0)
18 {
19         if(id)
20                 throw invalid_argument("System framebuffer must have id 0");
21
22         int viewport[4];
23         glGetIntegerv(GL_VIEWPORT, viewport);
24         width = viewport[2];
25         height = viewport[3];
26 }
27
28 Framebuffer::Framebuffer():
29         width(0),
30         height(0),
31         dirty(0)
32 {
33         static RequireExtension _ext("GL_EXT_framebuffer_object");
34
35         glGenFramebuffersEXT(1, &id);
36 }
37
38 Framebuffer::~Framebuffer()
39 {
40         if(id)
41                 glDeleteFramebuffersEXT(1, &id);
42         if(current()==this)
43                 unbind();
44 }
45
46 void Framebuffer::update_attachment(unsigned mask) const
47 {
48         if(current()==this)
49         {
50                 GLenum color_buf = GL_NONE;
51                 for(unsigned i=0; i<attachments.size(); ++i)
52                 {
53                         const Attachment &attch = attachments[i];
54                         if(mask&(1<<i))
55                         {
56                                 if(attch.type==GL_RENDERBUFFER_EXT)
57                                         glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, attch.attachment, GL_RENDERBUFFER_EXT, attch.rbuf->get_id());
58                                 else if(attch.type==GL_TEXTURE_2D)
59                                 {
60                                         static_cast<Texture2D *>(attch.tex)->allocate(attch.level);
61                                         glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, attch.attachment, attch.type, attch.tex->get_id(), attch.level);
62                                 }
63                                 else if(attch.type==GL_TEXTURE_CUBE_MAP)
64                                 {
65                                         static_cast<TextureCube *>(attch.tex)->allocate(attch.level);
66                                         glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, attch.attachment, attch.cube_face, attch.tex->get_id(), attch.level);
67                                 }
68                                 else
69                                         glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, attch.attachment, 0, 0);
70                         }
71
72                         if(attch.attachment>=COLOR_ATTACHMENT0 && attch.attachment<=COLOR_ATTACHMENT3)
73                                 color_buf = attch.attachment;
74                 }
75
76                 glDrawBuffer(color_buf);
77         }
78         else
79                 dirty |= mask;
80 }
81
82 void Framebuffer::check_size()
83 {
84         for(vector<Attachment>::iterator i=attachments.begin(); i!=attachments.end(); ++i)
85                 if(i->type)
86                 {
87                         if(i->type==GL_RENDERBUFFER_EXT)
88                         {
89                                 width = i->rbuf->get_width();
90                                 height = i->rbuf->get_height();
91                         }
92                         else if(i->type==GL_TEXTURE_2D)
93                         {
94                                 Texture2D *tex = static_cast<Texture2D *>(i->tex);
95                                 width = tex->get_width();
96                                 height = tex->get_height();
97                         }
98                         else if(i->type==GL_TEXTURE_CUBE_MAP)
99                         {
100                                 width = static_cast<TextureCube *>(i->tex)->get_size();
101                                 height = width;
102                         }
103                         if(current()==this)
104                                 glViewport(0, 0, width, height);
105                         break;
106                 }
107 }
108
109 unsigned Framebuffer::get_attachment_index(FramebufferAttachment attch)
110 {
111         for(unsigned i=0; i<attachments.size(); ++i)
112                 if(attachments[i].attachment==attch)
113                         return i;
114         attachments.push_back(Attachment(attch));
115         return attachments.size()-1;
116 }
117
118 void Framebuffer::attach(FramebufferAttachment attch, Renderbuffer &rbuf)
119 {
120         if(!id)
121                 throw invalid_operation("Framebuffer::attach");
122
123         unsigned i = get_attachment_index(attch);
124         attachments[i].set(rbuf);
125         update_attachment(1<<i);
126         check_size();
127 }
128
129 void Framebuffer::attach(FramebufferAttachment attch, Texture2D &tex, unsigned level)
130 {
131         if(!id)
132                 throw invalid_operation("Framebuffer::attach");
133
134         unsigned i = get_attachment_index(attch);
135         attachments[i].set(tex, 0, level);
136         update_attachment(1<<i);
137         check_size();
138 }
139
140 void Framebuffer::attach(FramebufferAttachment attch, TextureCube &tex, TextureCubeFace face, unsigned level)
141 {
142         if(!id)
143                 throw invalid_operation("Framebuffer::attach");
144
145         unsigned i = get_attachment_index(attch);
146         attachments[i].set(tex, face, level);
147         update_attachment(1<<i);
148         check_size();
149 }
150
151 void Framebuffer::detach(FramebufferAttachment attch)
152 {
153         if(!id)
154                 throw invalid_operation("Framebuffer::detach");
155
156         unsigned i = get_attachment_index(attch);
157         attachments[i].clear();
158         update_attachment(1<<i);
159         check_size();
160 }
161
162 FramebufferStatus Framebuffer::check_status() const
163 {
164         Bind _bind(this, true);
165         return static_cast<FramebufferStatus>(glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT));
166 }
167
168 void Framebuffer::clear(BufferBits bits)
169 {
170         Bind _bind(this, true);
171         glClear(bits);
172 }
173
174 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)
175 {
176         static RequireExtension _ext("GL_EXT_framebuffer_blit");
177
178         const Framebuffer *old = current();
179         if(set_current(this))
180         {
181                 glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, id);
182                 if(dirty)
183                 {
184                         update_attachment(dirty);
185                         dirty = 0;
186                 }
187         }
188         if(old!=&other)
189                 glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, other.id);
190
191         glBlitFramebufferEXT(sx0, sy0, sx1, sy1, dx0, dy0, dx1, dy1, bits, (filter ? GL_LINEAR : GL_NEAREST));
192
193         set_current(old);
194         glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, (old ? old->id : 0));
195 }
196
197 void Framebuffer::blit_from(const Framebuffer &other, int sx, int sy, unsigned wd, unsigned ht, int dx, int dy, BufferBits bits)
198 {
199         blit_from(other, sx, sy, sx+wd, sy+ht, dx, dy, dx+wd, dy+ht, bits, false);
200 }
201
202 void Framebuffer::blit_from(const Framebuffer &other, BufferBits bits, bool filter)
203 {
204         blit_from(other, 0, 0, other.width, other.height, 0, 0, width, height, bits, filter);
205 }
206
207 void Framebuffer::bind() const
208 {
209         if(set_current(this))
210         {
211                 glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, id);
212                 if(dirty)
213                 {
214                         update_attachment(dirty);
215                         dirty = 0;
216                 }
217                 if(width && height)
218                         glViewport(0, 0, width, height);
219         }
220 }
221
222 const Framebuffer *Framebuffer::current()
223 {       
224         if(!cur_obj)
225                 cur_obj = &system();
226         return cur_obj;
227 }
228
229 void Framebuffer::unbind()
230 {
231         system().bind();
232 }
233
234 Framebuffer &Framebuffer::system()
235 {
236         static Framebuffer sys_framebuf(0);
237         return sys_framebuf;
238 }
239
240
241 Framebuffer::Attachment::Attachment(FramebufferAttachment a):
242         attachment(a),
243         type(0),
244         level(0)
245 { }
246
247 void Framebuffer::Attachment::set(Renderbuffer &r)
248 {
249         type = GL_RENDERBUFFER_EXT;
250         rbuf = &r;
251         level = 0;
252 }
253
254 void Framebuffer::Attachment::set(Texture &t, GLenum f, unsigned l)
255 {
256         type = t.get_target();
257         tex = &t;
258         cube_face = f;
259         level = l;
260 }
261
262 void Framebuffer::Attachment::clear()
263 {
264         type = 0;
265 }
266
267 } // namespace GL
268 } // namespace Msp