]> git.tdb.fi Git - libs/gl.git/blob - source/core/framebuffer.cpp
Move all OpenGL-specific code to a separate directory
[libs/gl.git] / source / core / framebuffer.cpp
1 #include "error.h"
2 #include "framebuffer.h"
3 #include "texture2d.h"
4 #include "texture2dmultisample.h"
5 #include "texture3d.h"
6 #include "windowview.h"
7
8 using namespace std;
9
10 namespace Msp {
11 namespace GL {
12
13 framebuffer_incomplete::framebuffer_incomplete(const std::string &reason):
14         runtime_error(reason)
15 { }
16
17
18 Framebuffer::Framebuffer(bool s):
19         FramebufferBackend(s),
20         dirty(0)
21 {
22         if(s)
23         {
24                 format = get_system_format();
25                 get_system_size(width, height);
26         }
27 }
28
29 Framebuffer::Framebuffer():
30         FramebufferBackend(false)
31 {
32         init();
33 }
34
35 Framebuffer::Framebuffer(FrameAttachment fa):
36         FramebufferBackend(false)
37 {
38         init();
39         set_format(fa);
40 }
41
42 Framebuffer::Framebuffer(const FrameFormat &f):
43         FramebufferBackend(false)
44 {
45         init();
46         set_format(f);
47 }
48
49 void Framebuffer::init()
50 {
51         width = 0;
52         height = 0;
53         dirty = 0;
54 }
55
56 void Framebuffer::set_format(const FrameFormat &fmt)
57 {
58         if(!format.empty() || !id)
59                 throw invalid_operation("Framebuffer::set_format");
60         if(fmt.empty() || !is_format_supported(fmt))
61                 throw invalid_argument("Framebuffer::set_format");
62
63         format = fmt;
64         attachments.resize(format.size());
65 }
66
67 void Framebuffer::update() const
68 {
69         FramebufferBackend::update(dirty);
70         dirty = 0;
71 }
72
73 void Framebuffer::check_size()
74 {
75         bool first = true;
76         for(Attachment &a: attachments)
77                 if(a.tex)
78                 {
79                         unsigned w = 0;
80                         unsigned h = 0;
81                         if(const Texture2D *tex2d = dynamic_cast<const Texture2D *>(a.tex))
82                         {
83                                 w = max(tex2d->get_width()>>a.level, 1U);
84                                 h = max(tex2d->get_height()>>a.level, 1U);
85                         }
86                         else if(const Texture2DMultisample *tex2d_ms = dynamic_cast<const Texture2DMultisample *>(a.tex))
87                         {
88                                 w = tex2d_ms->get_width();
89                                 h = tex2d_ms->get_height();
90                         }
91                         else if(const Texture3D *tex3d = dynamic_cast<const Texture3D *>(a.tex))
92                         {
93                                 w = max(tex3d->get_width()>>a.level, 1U);
94                                 h = max(tex3d->get_height()>>a.level, 1U);
95                         }
96                         else if(const TextureCube *tex_cube = dynamic_cast<const TextureCube *>(a.tex))
97                         {
98                                 w = max(tex_cube->get_size()>>a.level, 1U);
99                                 h = w;
100                         }
101
102                         if(first)
103                         {
104                                 width = w;
105                                 height = h;
106                                 first = false;
107                         }
108                         else
109                         {
110                                 width = min(width, w);
111                                 height = min(height, h);
112                         }
113                 }
114 }
115
116 void Framebuffer::set_attachment(FrameAttachment attch, Texture &tex, unsigned level, int layer, unsigned samples)
117 {
118         if(format.empty() || !id)
119                 throw invalid_operation("Framebuffer::attach");
120
121         if((format.get_samples()>1 && samples!=format.get_samples()) || (format.get_samples()==1 && samples))
122                 throw incompatible_data("Framebuffer::attach");
123
124         unsigned i = 0;
125         for(FrameAttachment a: format)
126         {
127                 if(a==attch)
128                 {
129                         attachments[i].set(tex, level, layer);
130                         dirty |= 1<<i;
131                         check_size();
132                         return;
133                 }
134                 ++i;
135         }
136
137         throw incompatible_data("Framebuffer::attach");
138 }
139
140 void Framebuffer::attach(FrameAttachment attch, Texture2D &tex, unsigned level)
141 {
142         set_attachment(make_typed_attachment(attch, tex.get_format()), tex, level, 0, 0);
143 }
144
145 void Framebuffer::attach(FrameAttachment attch, Texture2DMultisample &tex)
146 {
147         set_attachment(make_typed_attachment(attch, tex.get_format()), tex, 0, 0, tex.get_samples());
148 }
149
150 void Framebuffer::attach(FrameAttachment attch, Texture3D &tex, unsigned layer, unsigned level)
151 {
152         set_attachment(make_typed_attachment(attch, tex.get_format()), tex, level, layer, 0);
153 }
154
155 void Framebuffer::attach(FrameAttachment attch, TextureCube &tex, TextureCubeFace face, unsigned level)
156 {
157         set_attachment(make_typed_attachment(attch, tex.get_format()), tex, level, face, 0);
158 }
159
160 void Framebuffer::attach_layered(FrameAttachment attch, Texture3D &tex, unsigned level)
161 {
162         require_layered();
163         set_attachment(make_typed_attachment(attch, tex.get_format()), tex, level, -1, 0);
164 }
165
166 void Framebuffer::attach_layered(FrameAttachment attch, TextureCube &tex, unsigned level)
167 {
168         require_layered();
169         set_attachment(make_typed_attachment(attch, tex.get_format()), tex, level, -1, 0);
170 }
171
172 void Framebuffer::detach(FrameAttachment attch)
173 {
174         if(!id)
175                 throw invalid_operation("Framebuffer::detach");
176
177         int i = format.index(attch);
178         if(i>=0)
179         {
180                 attachments[i].clear();
181                 dirty |= 1<<i;
182                 check_size();
183         }
184 }
185
186 void Framebuffer::resize(const WindowView &view)
187 {
188         if(id)
189                 throw invalid_operation("Framebuffer::resize");
190
191         width = view.get_width();
192         height = view.get_height();
193 }
194
195 void Framebuffer::require_complete() const
196 {
197         if(!id)
198                 return;
199
200         bool layered = (!attachments.empty() && attachments.front().layer<0);
201         for(const Attachment &a: attachments)
202         {
203                 if(!a.tex)
204                         throw framebuffer_incomplete("missing attachment");
205                 if(layered!=(a.layer<0))
206                         throw framebuffer_incomplete("inconsistent layering");
207         }
208
209         FramebufferBackend::require_complete();
210 }
211
212 Framebuffer &Framebuffer::system()
213 {
214         static Framebuffer sys_framebuf(true);
215         return sys_framebuf;
216 }
217
218
219 Framebuffer::Attachment::Attachment():
220         tex(0),
221         level(0),
222         layer(0)
223 { }
224
225 void Framebuffer::Attachment::set(Texture &t, unsigned l, int z)
226 {
227         tex = &t;
228         level = l;
229         layer = z;
230 }
231
232 void Framebuffer::Attachment::clear()
233 {
234         tex = 0;
235 }
236
237 } // namespace GL
238 } // namespace Msp