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