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