]> git.tdb.fi Git - libs/gl.git/blob - source/core/framebuffer.cpp
Remove RenderBuffer and always use textures as framebuffer attachments
[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 "texture2d.h"
12 #include "texture2dmultisample.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.tex)
110                         {
111                                 GLenum type = attch.tex->get_target();
112                                 if(ARB_direct_state_access)
113                                 {
114                                         if(type==GL_TEXTURE_2D || type==GL_TEXTURE_2D_MULTISAMPLE || attch.layer<0)
115                                                 glNamedFramebufferTexture(id, attch.attachment, attch.tex->get_id(), attch.level);
116                                         else
117                                                 glNamedFramebufferTextureLayer(id, attch.attachment, attch.tex->get_id(), attch.level, attch.layer);
118                                 }
119                                 else if(type==GL_TEXTURE_2D || type==GL_TEXTURE_2D_MULTISAMPLE)
120                                         glFramebufferTexture2D(GL_FRAMEBUFFER, attch.attachment, type, attch.tex->get_id(), attch.level);
121                                 else if(attch.layer<0)
122                                         glFramebufferTexture(GL_FRAMEBUFFER, attch.attachment, attch.tex->get_id(), attch.level);
123                                 else if(type==GL_TEXTURE_2D_ARRAY)
124                                         glFramebufferTextureLayer(GL_FRAMEBUFFER, attch.attachment, attch.tex->get_id(), attch.level, attch.layer);
125                                 else if(type==GL_TEXTURE_3D)
126                                         glFramebufferTexture3D(GL_FRAMEBUFFER, attch.attachment, type, attch.tex->get_id(), attch.level, attch.layer);
127                                 else if(type==GL_TEXTURE_CUBE_MAP)
128                                         glFramebufferTexture2D(GL_FRAMEBUFFER, attch.attachment, TextureCube::enumerate_faces(attch.layer), attch.tex->get_id(), attch.level);
129                         }
130                         else if(ARB_direct_state_access)
131                                 glNamedFramebufferTexture(id, attch.attachment, 0, 0);
132                         else
133                                 glFramebufferTexture2D(GL_FRAMEBUFFER, attch.attachment, GL_TEXTURE_2D, 0, 0);
134                 }
135
136                 if(attch.attachment>=COLOR_ATTACHMENT0 && attch.attachment<=COLOR_ATTACHMENT3)
137                         color_bufs.push_back(attch.attachment);
138         }
139
140         if(color_bufs.size()>1)
141                 static Require _req(ARB_draw_buffers);
142
143         GLenum first_buffer = (color_bufs.empty() ? GL_NONE : color_bufs.front());
144         if(ARB_direct_state_access)
145         {
146                 /* ARB_direct_state_access ties the availability of these functions to
147                 framebuffers themselves, so no further checks are needed. */
148                 glNamedFramebufferDrawBuffers(id, color_bufs.size(), &color_bufs[0]);
149                 glNamedFramebufferReadBuffer(id, first_buffer);
150         }
151         else
152         {
153                 if(ARB_draw_buffers)
154                         glDrawBuffers(color_bufs.size(), &color_bufs[0]);
155                 else if(MSP_buffer_control)
156                         glDrawBuffer(first_buffer);
157
158                 if(MSP_buffer_control)
159                         glReadBuffer(first_buffer);
160         }
161
162         if(ARB_direct_state_access)
163                 status = static_cast<FramebufferStatus>(glCheckNamedFramebufferStatus(id, GL_FRAMEBUFFER));
164         else
165                 status = static_cast<FramebufferStatus>(glCheckFramebufferStatus(GL_FRAMEBUFFER));
166
167         dirty = 0;
168 }
169
170 void Framebuffer::check_size()
171 {
172         for(vector<Attachment>::iterator i=attachments.begin(); i!=attachments.end(); ++i)
173                 if(i->tex)
174                 {
175                         GLenum type = i->tex->get_target();
176                         if(type==GL_TEXTURE_2D)
177                         {
178                                 Texture2D *tex = static_cast<Texture2D *>(i->tex);
179                                 width = max(tex->get_width()>>i->level, 1U);
180                                 height = max(tex->get_height()>>i->level, 1U);
181                         }
182                         else if(type==GL_TEXTURE_2D_MULTISAMPLE)
183                         {
184                                 Texture2DMultisample *tex = static_cast<Texture2DMultisample *>(i->tex);
185                                 width = tex->get_width();
186                                 height = tex->get_height();
187                         }
188                         else if(type==GL_TEXTURE_3D || type==GL_TEXTURE_2D_ARRAY)
189                         {
190                                 Texture3D *tex = static_cast<Texture3D *>(i->tex);
191                                 width = max(tex->get_width()>>i->level, 1U);
192                                 height = max(tex->get_height()>>i->level, 1U);
193                         }
194                         else if(type==GL_TEXTURE_CUBE_MAP)
195                         {
196                                 width = max(static_cast<TextureCube *>(i->tex)->get_size()>>i->level, 1U);
197                                 height = width;
198                         }
199                         break;
200                 }
201 }
202
203 unsigned Framebuffer::get_attachment_index(FramebufferAttachment attch)
204 {
205         for(unsigned i=0; i<attachments.size(); ++i)
206                 if(attachments[i].attachment==attch)
207                         return i;
208         attachments.push_back(Attachment(attch));
209         return attachments.size()-1;
210 }
211
212 void Framebuffer::set_attachment(FrameAttachment attch, Texture &tex, unsigned level, int layer)
213 {
214         if(!id)
215                 throw invalid_operation("Framebuffer::attach");
216
217         unsigned i = get_attachment_index(attch);
218         attachments[i].set(tex, level, layer);
219         dirty |= 1<<i;
220         check_size();
221 }
222
223 void Framebuffer::attach(FramebufferAttachment attch, Texture2D &tex, unsigned level)
224 {
225         tex.allocate(level);
226         set_attachment(attch, tex, level, 0);
227 }
228
229 void Framebuffer::attach(FramebufferAttachment attch, Texture2DMultisample &tex)
230 {
231         set_attachment(attch, tex, 0, 0);
232 }
233
234 void Framebuffer::attach(FramebufferAttachment attch, Texture3D &tex, unsigned layer, unsigned level)
235 {
236         tex.allocate(level);
237         set_attachment(attch, tex, level, layer);
238 }
239
240 void Framebuffer::attach(FramebufferAttachment attch, TextureCube &tex, TextureCubeFace face, unsigned level)
241 {
242         tex.allocate(level);
243         set_attachment(attch, tex, level, TextureCube::get_face_index(face));
244 }
245
246 void Framebuffer::attach_layered(FramebufferAttachment attch, Texture3D &tex, unsigned level)
247 {
248         static Require _req(ARB_geometry_shader4);
249         tex.allocate(level);
250         set_attachment(attch, tex, level, -1);
251 }
252
253 void Framebuffer::attach_layered(FramebufferAttachment attch, TextureCube &tex, unsigned level)
254 {
255         static Require _req(ARB_geometry_shader4);
256         tex.allocate(level);
257         set_attachment(attch, tex, level, -1);
258 }
259
260 void Framebuffer::detach(FramebufferAttachment attch)
261 {
262         if(!id)
263                 throw invalid_operation("Framebuffer::detach");
264
265         unsigned i = get_attachment_index(attch);
266         attachments[i].clear();
267         dirty |= 1<<i;
268         check_size();
269 }
270
271 void Framebuffer::resize(const WindowView &view)
272 {
273         if(id)
274                 throw invalid_operation("Framebuffer::resize");
275
276         width = view.get_width();
277         height = view.get_height();
278 }
279
280 void Framebuffer::require_complete() const
281 {
282         if(status!=FRAMEBUFFER_COMPLETE)
283                 throw framebuffer_incomplete(status);
284 }
285
286 void Framebuffer::set_debug_name(const string &name)
287 {
288 #ifdef DEBUG
289         if(KHR_debug)
290                 glObjectLabel(GL_FRAMEBUFFER, id, name.size(), name.c_str());
291 #else
292         (void)name;
293 #endif
294 }
295
296 Framebuffer &Framebuffer::system()
297 {
298         static Framebuffer sys_framebuf(0);
299         return sys_framebuf;
300 }
301
302
303 Framebuffer::Attachment::Attachment(FramebufferAttachment a):
304         attachment(a),
305         tex(0),
306         level(0),
307         layer(0)
308 { }
309
310 void Framebuffer::Attachment::set(Texture &t, unsigned l, int z)
311 {
312         tex = &t;
313         level = l;
314         layer = z;
315 }
316
317 void Framebuffer::Attachment::clear()
318 {
319         tex = 0;
320 }
321
322 } // namespace GL
323 } // namespace Msp