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