gamma correction in post when there's no sRGB framebuffer and simple shader compositi...
[vrfileman] / src / rtarg.cc
1 #include "rtarg.h"
2 #include "texture.h"
3 #include "app.h"
4
5 unsigned int RenderTarget::default_fbo = 0;
6
7 RenderTarget::RenderTarget()
8 {
9         width = height = 0;
10         fbo = 0;
11         rbuf_zstencil = 0;
12         color_tex = 0;
13         tex_face = 0;
14         tex_targ = 0;
15 }
16
17 RenderTarget::~RenderTarget()
18 {
19         cleanup();
20 }
21
22 bool RenderTarget::create(unsigned int fmt)
23 {
24         return create(win_width, win_height, fmt);
25 }
26
27 bool RenderTarget::create(int width, int height, unsigned int fmt)
28 {
29         fprintf(stderr, "RenderTarget::create(%d, %d)\n", width, height);
30         cleanup();
31
32         tex_targ = GL_TEXTURE_2D;
33         this->width = width;
34         this->height = height;
35         int tex_width = next_pow2(width);
36         int tex_height = next_pow2(height);
37
38         tex_matrix.scaling((float)width / (float)tex_width, (float)height / (float)tex_height, 1.0);
39
40         color_tex = new Texture2D;
41         color_tex->create(tex_width, tex_height, fmt);
42         tex_face = 0;
43
44         glGenFramebuffers(1, &fbo);
45         glBindFramebuffer(GL_FRAMEBUFFER, fbo);
46
47         glBindTexture(GL_TEXTURE_2D, color_tex->get_id());
48         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color_tex->get_id(), 0);
49         glBindTexture(GL_TEXTURE_2D, 0);
50
51         glGenRenderbuffers(1, &rbuf_zstencil);
52         glBindRenderbuffer(GL_RENDERBUFFER, rbuf_zstencil);
53 #ifdef GL_ES_VERSION_2_0
54         // XXX really? 16bit zbuffer attachment is the best we can do on ES2?
55         glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, tex_width, tex_height);
56 #else
57         glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, tex_width, tex_height);
58 #endif
59         glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rbuf_zstencil);
60 #ifndef GL_ES_VERSION_2_0
61         glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbuf_zstencil);
62 #endif
63
64         glBindFramebuffer(GL_FRAMEBUFFER, RenderTarget::default_fbo);
65         return true;
66 }
67
68 bool RenderTarget::create(Texture *tex, int face)
69 {
70         fprintf(stderr, "RenderTarget::create(tex{%d, %d}, face:%d)\n", tex->get_size(0),
71                         tex->get_size(1), face);
72
73         tex_targ = GL_TEXTURE_2D;
74         if(dynamic_cast<TextureCube*>(tex)) {
75                 if(face >= GL_TEXTURE_CUBE_MAP_POSITIVE_X && face <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z) {
76                         tex_targ = face;
77                 } else if(face >= 0 && face < 6) {
78                         tex_targ = GL_TEXTURE_CUBE_MAP_POSITIVE_X + face;
79                 } else {
80                         fprintf(stderr, "invalid face (%d) passed to RenderTarget::create(TextureCube*, int)\n", face);
81                         return false;
82                 }
83         }
84
85         cleanup();
86
87         width = tex->get_size(0);
88         height = tex->get_size(1);
89
90         tex_matrix = Mat4::identity;
91
92         color_tex = tex;
93
94         glGenFramebuffers(1, &fbo);
95         glBindFramebuffer(GL_FRAMEBUFFER, fbo);
96
97         glBindTexture(tex_targ, color_tex->get_id());
98         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex_targ, color_tex->get_id(), 0);
99         glBindTexture(tex_targ, 0);
100
101         glGenRenderbuffers(1, &rbuf_zstencil);
102         glBindRenderbuffer(GL_RENDERBUFFER, rbuf_zstencil);
103         glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height);
104         glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rbuf_zstencil);
105         glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbuf_zstencil);
106
107         glBindFramebuffer(GL_FRAMEBUFFER, RenderTarget::default_fbo);
108         return true;
109 }
110
111 void RenderTarget::cleanup()
112 {
113         delete color_tex;
114         color_tex = 0;
115
116         if(fbo) {
117                 glDeleteFramebuffers(1, &fbo);
118         }
119         if(rbuf_zstencil) {
120                 glDeleteRenderbuffers(1, &rbuf_zstencil);
121         }
122
123         fbo = rbuf_zstencil = 0;
124         width = height = 0;
125         tex_face = 0;
126         tex_targ = 0;
127 }
128
129 bool RenderTarget::resize(int width, int height)
130 {
131         if(width == this->width && height == this->height) {
132                 return true;
133         }
134
135         int tex_width = next_pow2(width);
136         int tex_height = next_pow2(height);
137         this->width = width;
138         this->height = height;
139
140         if(tex_width != color_tex->get_size(0) || tex_height != color_tex->get_size(1)) {
141                 printf("resizing render target (fbo %u): %dx%d [%dx%d]\n", fbo, width, height, tex_width, tex_height);
142
143                 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
144
145                 color_tex->create(tex_width, tex_height, color_tex->get_format());
146                 color_tex->bind();
147                 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex_targ, color_tex->get_id(), 0);
148                 glBindTexture(tex_targ, 0);
149
150                 glBindRenderbuffer(GL_RENDERBUFFER, rbuf_zstencil);
151
152                 unsigned int depth_format;
153 #ifndef GL_ES_VERSION_2_0
154                 // desktop OpenGL, use typical depth24-stencil8 format
155                 depth_format = GL_DEPTH24_STENCIL8;
156 #else   // OpenGL ES
157                 // OpenGL ES provides combined depth/stencil formats as an extension
158 #ifdef GL_OES_packed_depth_stencil
159                 if(have_ext_packed_depth_stencil) {
160                         depth_format = GL_DEPTH24_STENCIL8_OES;
161                 } else
162 #endif  // GL_OES_packed_depth_stencil
163                 {
164                         depth_format = GL_DEPTH_COMPONENT16;    // no compile-time or runtime support for packed d/s
165                 }
166 #endif  // GL_ES_VERSION_2_0
167                 glRenderbufferStorage(GL_RENDERBUFFER, depth_format, tex_width, tex_height);
168
169                 glBindFramebuffer(GL_FRAMEBUFFER, RenderTarget::default_fbo);
170         }
171
172         tex_matrix.scaling((float)width / (float)tex_width, (float)height / (float)tex_height, 1.0);
173         return true;
174 }
175
176 int RenderTarget::get_width() const
177 {
178         return width;
179 }
180
181 int RenderTarget::get_height() const
182 {
183         return height;
184 }
185
186 Texture *RenderTarget::get_texture() const
187 {
188         return color_tex;
189 }
190
191 const Mat4 &RenderTarget::get_texture_matrix() const
192 {
193         return tex_matrix;
194 }
195
196
197 static const char *fbstname[] = {
198         "GL_FRAMEBUFFER_COMPLETE",
199         "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT",
200         "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT",
201         "no such fbo error",
202         "GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS",
203         "GL_FRAMEBUFFER_INCOMPLETE_FORMATS",
204         "GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER",
205         "GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER",
206         "GL_FRAMEBUFFER_UNSUPPORTED"
207 };
208
209 bool RenderTarget::check() const
210 {
211         bool res = true;
212
213 #ifndef GL_ES_VERSION_2_0
214         int prev_fb = 0;
215         glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &prev_fb);
216 #endif
217
218         glBindFramebuffer(GL_FRAMEBUFFER, fbo);
219
220         int status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
221         if(status != GL_FRAMEBUFFER_COMPLETE) {
222                 fprintf(stderr, "RenderTarget::check: incomplete FBO %u: %s\n", fbo,
223                                 fbstname[status - GL_FRAMEBUFFER_COMPLETE]);
224                 res = false;
225                 goto end;
226         }
227
228 end:
229 #ifndef GL_ES_VERSION_2_0
230         glBindFramebuffer(GL_FRAMEBUFFER, prev_fb);
231 #endif
232         return res;
233 }
234
235 void RenderTarget::bind() const
236 {
237         glBindFramebuffer(GL_FRAMEBUFFER, fbo);
238
239         glViewport(0, 0, width, height);
240 }
241
242 void set_render_target(const RenderTarget *rtarg)
243 {
244         if(rtarg) {
245                 rtarg->bind();
246         } else {
247                 glBindFramebuffer(GL_FRAMEBUFFER, RenderTarget::default_fbo);
248                 glViewport(0, 0, win_width, win_height);
249         }
250 }
251
252 int next_pow2(int x)
253 {
254         x--;
255         x = (x >> 1) | x;
256         x = (x >> 2) | x;
257         x = (x >> 4) | x;
258         x = (x >> 8) | x;
259         x = (x >> 16) | x;
260         return x + 1;
261 }
262