]> git.tdb.fi Git - libs/gl.git/blob - source/core/framebuffer.cpp
Remove the FramebufferStatus enum
[libs/gl.git] / source / core / framebuffer.cpp
1 #include <msp/gl/extensions/arb_draw_buffers.h>
2 #include <msp/gl/extensions/arb_direct_state_access.h>
3 #include <msp/gl/extensions/ext_framebuffer_object.h>
4 #include <msp/gl/extensions/ext_texture_array.h>
5 #include <msp/gl/extensions/ext_texture3d.h>
6 #include <msp/gl/extensions/msp_buffer_control.h>
7 #include <msp/gl/extensions/khr_debug.h>
8 #include <msp/strings/format.h>
9 #include "error.h"
10 #include "framebuffer.h"
11 #include "misc.h"
12 #include "texture2d.h"
13 #include "texture2dmultisample.h"
14 #include "texture3d.h"
15 #include "windowview.h"
16
17 using namespace std;
18
19 namespace Msp {
20 namespace GL {
21
22 framebuffer_incomplete::framebuffer_incomplete(const std::string &reason):
23         runtime_error(reason)
24 { }
25
26
27 Framebuffer::Framebuffer(unsigned i):
28         id(i),
29         status(GL_FRAMEBUFFER_COMPLETE),
30         dirty(0)
31 {
32         if(id)
33                 throw invalid_argument("System framebuffer must have id 0");
34
35         int view[4];
36         glGetIntegerv(GL_VIEWPORT, view);
37         width = view[2];
38         height = view[3];
39 }
40
41 Framebuffer::Framebuffer()
42 {
43         init();
44 }
45
46 Framebuffer::Framebuffer(FrameAttachment fa)
47 {
48         init();
49         set_format(fa);
50 }
51
52 Framebuffer::Framebuffer(const FrameFormat &f)
53 {
54         init();
55         set_format(f);
56 }
57
58 void Framebuffer::init()
59 {
60         static Require _req(EXT_framebuffer_object);
61
62         width = 0;
63         height = 0;
64         status = GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT;
65         dirty = 0;
66
67         if(ARB_direct_state_access)
68                 glCreateFramebuffers(1, &id);
69         else
70                 glGenFramebuffers(1, &id);
71 }
72
73 Framebuffer::~Framebuffer()
74 {
75         if(id)
76                 glDeleteFramebuffers(1, &id);
77 }
78
79 void Framebuffer::set_format(const FrameFormat &fmt)
80 {
81         if(!format.empty())
82                 throw invalid_operation("Framebuffer::set_format");
83         if(fmt.empty())
84                 throw invalid_argument("Framebuffer::set_format");
85
86         format = fmt;
87         attachments.resize(format.size());
88 }
89
90 void Framebuffer::update() const
91 {
92         vector<GLenum> color_bufs;
93         color_bufs.reserve(format.size());
94         unsigned i = 0;
95         for(FrameAttachment a: format)
96         {
97                 GLenum gl_attach_point = get_gl_attachment(a);
98                 if(dirty&(1<<i))
99                 {
100                         const Attachment &attch = attachments[i];
101                         if(attch.tex)
102                         {
103                                 if(ARB_direct_state_access)
104                                 {
105                                         if(attch.tex->target==GL_TEXTURE_2D || attch.tex->target==GL_TEXTURE_2D_MULTISAMPLE || attch.layer<0)
106                                                 glNamedFramebufferTexture(id, gl_attach_point, attch.tex->id, attch.level);
107                                         else
108                                                 glNamedFramebufferTextureLayer(id, gl_attach_point, attch.tex->id, attch.level, attch.layer);
109                                 }
110                                 else if(attch.tex->target==GL_TEXTURE_2D || attch.tex->target==GL_TEXTURE_2D_MULTISAMPLE)
111                                         glFramebufferTexture2D(GL_FRAMEBUFFER, gl_attach_point, attch.tex->target, attch.tex->id, attch.level);
112                                 else if(attch.layer<0)
113                                         glFramebufferTexture(GL_FRAMEBUFFER, gl_attach_point, attch.tex->id, attch.level);
114                                 else if(attch.tex->target==GL_TEXTURE_2D_ARRAY)
115                                         glFramebufferTextureLayer(GL_FRAMEBUFFER, gl_attach_point, attch.tex->id, attch.level, attch.layer);
116                                 else if(attch.tex->target==GL_TEXTURE_3D)
117                                         glFramebufferTexture3D(GL_FRAMEBUFFER, gl_attach_point, attch.tex->target, attch.tex->id, attch.level, attch.layer);
118                                 else if(attch.tex->target==GL_TEXTURE_CUBE_MAP)
119                                         glFramebufferTexture2D(GL_FRAMEBUFFER, gl_attach_point, get_gl_cube_face(static_cast<TextureCubeFace>(attch.layer)), attch.tex->id, attch.level);
120                         }
121                         else if(ARB_direct_state_access)
122                                 glNamedFramebufferTexture(id, gl_attach_point, 0, 0);
123                         else
124                                 glFramebufferTexture2D(GL_FRAMEBUFFER, gl_attach_point, GL_TEXTURE_2D, 0, 0);
125                 }
126
127                 if(gl_attach_point!=GL_DEPTH_ATTACHMENT && gl_attach_point!=GL_STENCIL_ATTACHMENT)
128                         color_bufs.push_back(gl_attach_point);
129
130                 ++i;
131         }
132
133         if(color_bufs.size()>1)
134                 static Require _req(ARB_draw_buffers);
135
136         GLenum first_buffer = (color_bufs.empty() ? GL_NONE : color_bufs.front());
137         if(ARB_direct_state_access)
138         {
139                 /* ARB_direct_state_access ties the availability of these functions to
140                 framebuffers themselves, so no further checks are needed. */
141                 glNamedFramebufferDrawBuffers(id, color_bufs.size(), &color_bufs[0]);
142                 glNamedFramebufferReadBuffer(id, first_buffer);
143         }
144         else
145         {
146                 if(ARB_draw_buffers)
147                         glDrawBuffers(color_bufs.size(), &color_bufs[0]);
148                 else if(MSP_buffer_control)
149                         glDrawBuffer(first_buffer);
150
151                 if(MSP_buffer_control)
152                         glReadBuffer(first_buffer);
153         }
154
155         if(ARB_direct_state_access)
156                 status = glCheckNamedFramebufferStatus(id, GL_FRAMEBUFFER);
157         else
158                 status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
159
160         dirty = 0;
161 }
162
163 void Framebuffer::check_size()
164 {
165         bool first = true;
166         for(Attachment &a: attachments)
167                 if(a.tex)
168                 {
169                         unsigned w = 0;
170                         unsigned h = 0;
171                         if(a.tex->target==GL_TEXTURE_2D)
172                         {
173                                 Texture2D *tex = static_cast<Texture2D *>(a.tex);
174                                 w = max(tex->get_width()>>a.level, 1U);
175                                 h = max(tex->get_height()>>a.level, 1U);
176                         }
177                         else if(a.tex->target==GL_TEXTURE_2D_MULTISAMPLE)
178                         {
179                                 Texture2DMultisample *tex = static_cast<Texture2DMultisample *>(a.tex);
180                                 w = tex->get_width();
181                                 h = tex->get_height();
182                         }
183                         else if(a.tex->target==GL_TEXTURE_3D || a.tex->target==GL_TEXTURE_2D_ARRAY)
184                         {
185                                 Texture3D *tex = static_cast<Texture3D *>(a.tex);
186                                 w = max(tex->get_width()>>a.level, 1U);
187                                 h = max(tex->get_height()>>a.level, 1U);
188                         }
189                         else if(a.tex->target==GL_TEXTURE_CUBE_MAP)
190                         {
191                                 w = max(static_cast<TextureCube *>(a.tex)->get_size()>>a.level, 1U);
192                                 h = w;
193                         }
194
195                         if(first)
196                         {
197                                 width = w;
198                                 height = h;
199                                 first = false;
200                         }
201                         else
202                         {
203                                 width = min(width, w);
204                                 height = min(height, h);
205                         }
206                 }
207 }
208
209 void Framebuffer::set_attachment(FrameAttachment attch, Texture &tex, unsigned level, int layer, unsigned samples)
210 {
211         if(format.empty() || !id)
212                 throw invalid_operation("Framebuffer::attach");
213
214         if((format.get_samples()>1 && samples!=format.get_samples()) || (format.get_samples()==1 && samples))
215                 throw incompatible_data("Framebuffer::attach");
216
217         unsigned i = 0;
218         for(FrameAttachment a: format)
219         {
220                 if(a==attch)
221                 {
222                         attachments[i].set(tex, level, layer);
223                         dirty |= 1<<i;
224                         check_size();
225                         return;
226                 }
227                 ++i;
228         }
229
230         throw incompatible_data("Framebuffer::attach");
231 }
232
233 void Framebuffer::attach(FrameAttachment attch, Texture2D &tex, unsigned level)
234 {
235         set_attachment(make_typed_attachment(attch, tex.get_format()), tex, level, 0, 0);
236 }
237
238 void Framebuffer::attach(FrameAttachment attch, Texture2DMultisample &tex)
239 {
240         set_attachment(make_typed_attachment(attch, tex.get_format()), tex, 0, 0, tex.get_samples());
241 }
242
243 void Framebuffer::attach(FrameAttachment attch, Texture3D &tex, unsigned layer, unsigned level)
244 {
245         set_attachment(make_typed_attachment(attch, tex.get_format()), tex, level, layer, 0);
246 }
247
248 void Framebuffer::attach(FrameAttachment attch, TextureCube &tex, TextureCubeFace face, unsigned level)
249 {
250         set_attachment(make_typed_attachment(attch, tex.get_format()), tex, level, face, 0);
251 }
252
253 void Framebuffer::attach_layered(FrameAttachment attch, Texture3D &tex, unsigned level)
254 {
255         static Require _req(ARB_geometry_shader4);
256         set_attachment(make_typed_attachment(attch, tex.get_format()), tex, level, -1, 0);
257 }
258
259 void Framebuffer::attach_layered(FrameAttachment attch, TextureCube &tex, unsigned level)
260 {
261         static Require _req(ARB_geometry_shader4);
262         set_attachment(make_typed_attachment(attch, tex.get_format()), tex, level, -1, 0);
263 }
264
265 void Framebuffer::detach(FrameAttachment attch)
266 {
267         if(!id)
268                 throw invalid_operation("Framebuffer::detach");
269
270         int i = format.index(attch);
271         if(i>=0)
272         {
273                 attachments[i].clear();
274                 dirty |= 1<<i;
275                 check_size();
276         }
277 }
278
279 void Framebuffer::resize(const WindowView &view)
280 {
281         if(id)
282                 throw invalid_operation("Framebuffer::resize");
283
284         width = view.get_width();
285         height = view.get_height();
286 }
287
288 void Framebuffer::require_complete() const
289 {
290         if(!id)
291                 return;
292
293         bool layered = (!attachments.empty() && attachments.front().layer<0);
294         for(const Attachment &a: attachments)
295         {
296                 if(!a.tex)
297                         throw framebuffer_incomplete("missing attachment");
298                 if(layered!=(a.layer<0))
299                         throw framebuffer_incomplete("inconsistent layering");
300         }
301
302         if(status==GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT)
303                 throw framebuffer_incomplete("incomplete or unsupported attachment");
304         if(status==GL_FRAMEBUFFER_UNSUPPORTED)
305                 throw framebuffer_incomplete("unsupported configuration");
306         if(status!=GL_FRAMEBUFFER_COMPLETE)
307                 throw framebuffer_incomplete(Msp::format("incomplete (%#x)", status));
308 }
309
310 void Framebuffer::set_debug_name(const string &name)
311 {
312 #ifdef DEBUG
313         if(KHR_debug)
314                 glObjectLabel(GL_FRAMEBUFFER, id, name.size(), name.c_str());
315 #else
316         (void)name;
317 #endif
318 }
319
320 Framebuffer &Framebuffer::system()
321 {
322         static Framebuffer sys_framebuf(0);
323         return sys_framebuf;
324 }
325
326
327 Framebuffer::Attachment::Attachment():
328         tex(0),
329         level(0),
330         layer(0)
331 { }
332
333 void Framebuffer::Attachment::set(Texture &t, unsigned l, int z)
334 {
335         tex = &t;
336         level = l;
337         layer = z;
338 }
339
340 void Framebuffer::Attachment::clear()
341 {
342         tex = 0;
343 }
344
345 } // namespace GL
346 } // namespace Msp