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