]> git.cworth.org Git - vogl/blob - src/voglcommon/vogl_default_framebuffer_state.cpp
Initial vogl checkin
[vogl] / src / voglcommon / vogl_default_framebuffer_state.cpp
1 /**************************************************************************
2  *
3  * Copyright 2013-2014 RAD Game Tools and Valve Software
4  * All Rights Reserved.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  *
24  **************************************************************************/
25
26 // File: vogl_default_framebuffer_state.cpp
27 #include "vogl_default_framebuffer_state.h"
28
29 #include "vogl_console.h"
30 #include "vogl_data_stream_serializer.h"
31 #include "vogl_dynamic_stream.h"
32
33 #include "vogl_common.h"
34 #include "vogl_texture_format.h"
35
36 static const GLenum g_def_framebuffer_enums[] =
37 {
38     GL_FRONT_LEFT,
39     GL_BACK_LEFT,
40     GL_FRONT_RIGHT,
41     GL_BACK_RIGHT,
42     0
43 };
44
45 bool vogl_get_default_framebuffer_attribs(vogl_default_framebuffer_attribs &attribs, uint screen)
46 {
47     GLXDrawable pDrawable = GL_ENTRYPOINT(glXGetCurrentDrawable)();
48     Display *pDisplay = GL_ENTRYPOINT(glXGetCurrentDisplay)();
49     if ((!pDrawable) || (!pDisplay))
50         return false;
51
52     GLuint fbconfig_id = 0;
53     GL_ENTRYPOINT(glXQueryDrawable)(pDisplay, pDrawable, GLX_FBCONFIG_ID, &fbconfig_id);
54
55     GLint attrib_list[3] = { GLX_FBCONFIG_ID, static_cast<GLint>(fbconfig_id), None };
56     GLint nelements = 0;
57     GLXFBConfig *pConfigs = GL_ENTRYPOINT(glXChooseFBConfig)(pDisplay, screen, attrib_list, &nelements);
58
59     GLint red_size = 8, green_size = 8, blue_size = 8, alpha_size = 8;
60     GLint doublebuffer = 1, stereo = 0, depth_size = 24, stencil_size = 8;
61     GLint drawable_type = GLX_WINDOW_BIT;
62     GLint render_type = GLX_RGBA_BIT;
63     GLint samples = 0, sample_buffers = 0;
64
65     if ((pConfigs) && (nelements > 0))
66     {
67         GLXFBConfig config = pConfigs[0];
68
69         GL_ENTRYPOINT(glXGetFBConfigAttrib)(pDisplay, config, GLX_DRAWABLE_TYPE, &drawable_type); // what does a drawable type of 7 mean?
70         GL_ENTRYPOINT(glXGetFBConfigAttrib)(pDisplay, config, GLX_RENDER_TYPE, &render_type);
71         GL_ENTRYPOINT(glXGetFBConfigAttrib)(pDisplay, config, GLX_RED_SIZE, &red_size);
72         GL_ENTRYPOINT(glXGetFBConfigAttrib)(pDisplay, config, GLX_GREEN_SIZE, &green_size);
73         GL_ENTRYPOINT(glXGetFBConfigAttrib)(pDisplay, config, GLX_BLUE_SIZE, &blue_size);
74         GL_ENTRYPOINT(glXGetFBConfigAttrib)(pDisplay, config, GLX_ALPHA_SIZE, &alpha_size);
75         GL_ENTRYPOINT(glXGetFBConfigAttrib)(pDisplay, config, GLX_DOUBLEBUFFER, &doublebuffer);
76         GL_ENTRYPOINT(glXGetFBConfigAttrib)(pDisplay, config, GLX_STEREO, &stereo);
77         GL_ENTRYPOINT(glXGetFBConfigAttrib)(pDisplay, config, GLX_DEPTH_SIZE, &depth_size);
78         GL_ENTRYPOINT(glXGetFBConfigAttrib)(pDisplay, config, GLX_STENCIL_SIZE, &stencil_size);
79         GL_ENTRYPOINT(glXGetFBConfigAttrib)(pDisplay, config, GLX_SAMPLES, &samples);
80         GL_ENTRYPOINT(glXGetFBConfigAttrib)(pDisplay, config, GLX_SAMPLE_BUFFERS, &sample_buffers);
81     }
82
83     attribs.m_r_size = red_size;
84     attribs.m_g_size = green_size;
85     attribs.m_b_size = blue_size;
86     attribs.m_a_size = alpha_size;
87     attribs.m_depth_size = depth_size;
88     attribs.m_stencil_size = stencil_size;
89     attribs.m_samples = samples;
90     attribs.m_double_buffered = (doublebuffer != 0);
91
92     GL_ENTRYPOINT(glXQueryDrawable)(pDisplay, pDrawable, GLX_WIDTH, &attribs.m_width);
93     GL_ENTRYPOINT(glXQueryDrawable)(pDisplay, pDrawable, GLX_HEIGHT, &attribs.m_height);
94
95     return true;
96 }
97
98 vogl_default_framebuffer_state::vogl_default_framebuffer_state() :
99     m_valid(false)
100 {
101 }
102
103 vogl_default_framebuffer_state::~vogl_default_framebuffer_state()
104 {
105 }
106
107 bool vogl_default_framebuffer_state::snapshot(const vogl_context_info &context_info, const vogl_default_framebuffer_attribs &fb_attribs)
108 {
109     VOGL_NOTE_UNUSED(context_info);
110
111     clear();
112
113     m_fb_attribs = fb_attribs;
114
115     // Create compatible GL texture
116     // Attach this texture to an FBO
117     // Blit default framebuffer to this FBO
118     // Capture this texture's state
119
120     vogl_scoped_state_saver framebuffer_state_saver(cGSTReadBuffer, cGSTDrawBuffer);
121
122     vogl_scoped_binding_state orig_framebuffers(GL_DRAW_FRAMEBUFFER, GL_READ_FRAMEBUFFER, GL_TEXTURE_2D, GL_TEXTURE_2D_MULTISAMPLE);
123
124     GL_ENTRYPOINT(glBindFramebuffer)(GL_READ_FRAMEBUFFER, 0);
125     VOGL_CHECK_GL_ERROR;
126
127     // TODO: Test multisampled default framebuffers
128     const GLenum tex_target = (fb_attribs.m_samples > 1) ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D;
129
130     for (uint i = 0; i < cDefFramebufferTotal; i++)
131     {
132         GLenum internal_fmt, pixel_fmt, pixel_type;
133
134         // TODO: This uses fixed pixel formats, and assumes there's always a depth/stencil buffer.
135         if (i == cDefFramebufferDepthStencil)
136         {
137             if ((fb_attribs.m_depth_size + fb_attribs.m_stencil_size) == 0)
138                 continue;
139
140             GL_ENTRYPOINT(glReadBuffer)(fb_attribs.m_double_buffered ? GL_BACK_LEFT : GL_FRONT_LEFT);
141
142             internal_fmt = GL_DEPTH_STENCIL;
143             pixel_fmt = GL_DEPTH_STENCIL;
144             pixel_type = GL_UNSIGNED_INT_24_8;
145         }
146         else
147         {
148             if ((fb_attribs.m_r_size + fb_attribs.m_g_size + fb_attribs.m_b_size + fb_attribs.m_a_size) == 0)
149                 continue;
150
151             GL_ENTRYPOINT(glReadBuffer)(g_def_framebuffer_enums[i]);
152
153             internal_fmt = GL_RGBA;
154             pixel_fmt = GL_RGBA;
155             pixel_type = GL_UNSIGNED_INT_8_8_8_8_REV;
156         }
157
158         if (vogl_check_gl_error_internal(true))
159             continue;
160
161         // Create texture
162         GLuint tex_handle = 0;
163         GL_ENTRYPOINT(glGenTextures)(1, &tex_handle);
164         VOGL_CHECK_GL_ERROR;
165
166         GL_ENTRYPOINT(glBindTexture)(tex_target, tex_handle);
167         VOGL_CHECK_GL_ERROR;
168
169         if (fb_attribs.m_samples > 1)
170         {
171             GL_ENTRYPOINT(glTexImage2DMultisample)(tex_target,
172                 fb_attribs.m_samples,
173                 internal_fmt,
174                 fb_attribs.m_width,
175                 fb_attribs.m_height,
176                 GL_TRUE);
177         }
178         else
179         {
180             GL_ENTRYPOINT(glTexImage2D)(tex_target,
181                 0,
182                 internal_fmt,
183                 fb_attribs.m_width,
184                 fb_attribs.m_height,
185                 0,
186                 pixel_fmt,
187                 pixel_type,
188                 NULL);
189         }
190
191         if (vogl_check_gl_error_internal())
192         {
193             GL_ENTRYPOINT(glBindTexture)(tex_target, 0);
194             VOGL_CHECK_GL_ERROR;
195
196             GL_ENTRYPOINT(glDeleteTextures)(1, &tex_handle);
197             VOGL_CHECK_GL_ERROR;
198
199             continue;
200         }
201
202         GL_ENTRYPOINT(glTexParameteri)(tex_target, GL_TEXTURE_MAX_LEVEL, 0);
203         VOGL_CHECK_GL_ERROR;
204
205         // Create FBO
206         GLuint fbo_handle = 0;
207         GL_ENTRYPOINT(glGenFramebuffers)(1, &fbo_handle);
208         VOGL_CHECK_GL_ERROR;
209
210         GL_ENTRYPOINT(glBindFramebuffer)(GL_DRAW_FRAMEBUFFER, fbo_handle);
211         VOGL_CHECK_GL_ERROR;
212
213         GL_ENTRYPOINT(glFramebufferTexture2D)(GL_DRAW_FRAMEBUFFER, (i == cDefFramebufferDepthStencil) ? GL_DEPTH_STENCIL_ATTACHMENT : GL_COLOR_ATTACHMENT0, tex_target, tex_handle, 0);
214         VOGL_CHECK_GL_ERROR;
215
216         GLenum draw_buf = (i == cDefFramebufferDepthStencil) ? GL_NONE : GL_COLOR_ATTACHMENT0;
217         GL_ENTRYPOINT(glDrawBuffers)(1, &draw_buf);
218         VOGL_CHECK_GL_ERROR;
219
220         GLenum cur_status = GL_ENTRYPOINT(glCheckFramebufferStatus)(GL_DRAW_FRAMEBUFFER);
221         VOGL_CHECK_GL_ERROR;
222
223         bool status = true;
224
225         if (cur_status == GL_FRAMEBUFFER_COMPLETE)
226         {
227             GL_ENTRYPOINT(glBlitFramebuffer)(
228                 0, 0, fb_attribs.m_width, fb_attribs.m_height,
229                 0, 0, fb_attribs.m_width, fb_attribs.m_height,
230                 (i == cDefFramebufferDepthStencil) ? (GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT) : GL_COLOR_BUFFER_BIT,
231                 GL_NEAREST);
232
233             if (vogl_check_gl_error_internal())
234             {
235                 status = false;
236             }
237         }
238
239         if (status)
240         {
241             vogl_handle_remapper def_handle_remapper;
242             status = m_textures[i].snapshot(context_info, def_handle_remapper, tex_handle, tex_target);
243
244             if (!status)
245             {
246                 vogl_error_printf("%s: Failed snapshotting texture for default framebuffer %s\n", VOGL_METHOD_NAME, g_gl_enums.find_gl_name(g_def_framebuffer_enums[i]));
247             }
248         }
249         else
250         {
251             vogl_warning_printf("%s: Failed blitting framebuffer %u\n", VOGL_METHOD_NAME, i);
252         }
253
254         // Delete FBO
255         GL_ENTRYPOINT(glBindFramebuffer)(GL_DRAW_FRAMEBUFFER, 0);
256         VOGL_CHECK_GL_ERROR;
257
258         GL_ENTRYPOINT(glDeleteFramebuffers)(1, &fbo_handle);
259         VOGL_CHECK_GL_ERROR;
260
261         GL_ENTRYPOINT(glDrawBuffer)(GL_FRONT_LEFT);
262         VOGL_CHECK_GL_ERROR;
263
264         GL_ENTRYPOINT(glBindTexture)(tex_target, 0);
265         VOGL_CHECK_GL_ERROR;
266
267         GL_ENTRYPOINT(glDeleteTextures)(1, &tex_handle);
268         VOGL_CHECK_GL_ERROR;
269     }
270
271     m_valid = true;
272
273     return true;
274 }
275
276 bool vogl_default_framebuffer_state::restore(const vogl_context_info &context_info) const
277 {
278     VOGL_NOTE_UNUSED(context_info);
279
280     if (!m_valid)
281         return false;
282
283     // TODO: Test multisampled default framebuffers
284     // TODO: Check to ensure the stored fb is compatible with the current framebuffer
285     const GLenum tex_target = (m_fb_attribs.m_samples > 1) ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D;
286
287     vogl_scoped_state_saver framebuffer_state_saver(cGSTReadBuffer, cGSTDrawBuffer);
288
289     vogl_scoped_binding_state orig_framebuffers(GL_DRAW_FRAMEBUFFER, GL_READ_FRAMEBUFFER, GL_TEXTURE_2D, GL_TEXTURE_2D_MULTISAMPLE);
290
291     GL_ENTRYPOINT(glBindFramebuffer)(GL_DRAW_FRAMEBUFFER, 0);
292     VOGL_CHECK_GL_ERROR;
293
294     for (uint i = 0; i < cDefFramebufferTotal; i++)
295     {
296         if (!m_textures[i].is_valid())
297             continue;
298
299         GL_ENTRYPOINT(glDrawBuffer)((i == cDefFramebufferDepthStencil) ? GL_NONE : g_def_framebuffer_enums[i]);
300         if (vogl_check_gl_error_internal(true))
301             continue;
302
303         GLuint64 tex_handle64 = 0;
304
305         vogl_handle_remapper def_handle_remapper;
306         if (!m_textures[i].restore(context_info, def_handle_remapper, tex_handle64))
307         {
308             vogl_error_printf("%s: Failed restoring texture %u\n", VOGL_METHOD_NAME, i);
309             continue;
310         }
311
312         GLuint tex_handle = static_cast<GLuint>(tex_handle64);
313
314         // Create FBO
315         GLuint fbo_handle = 0;
316         GL_ENTRYPOINT(glGenFramebuffers)(1, &fbo_handle);
317         VOGL_CHECK_GL_ERROR;
318
319         GL_ENTRYPOINT(glBindFramebuffer)(GL_READ_FRAMEBUFFER, fbo_handle);
320         VOGL_CHECK_GL_ERROR;
321
322         GL_ENTRYPOINT(glFramebufferTexture2D)(GL_READ_FRAMEBUFFER, (i == cDefFramebufferDepthStencil) ? GL_DEPTH_STENCIL_ATTACHMENT : GL_COLOR_ATTACHMENT0, tex_target, tex_handle, 0);
323         VOGL_CHECK_GL_ERROR;
324
325         GL_ENTRYPOINT(glReadBuffer)((i == cDefFramebufferDepthStencil) ? GL_NONE : GL_COLOR_ATTACHMENT0);
326         VOGL_CHECK_GL_ERROR;
327
328         GLenum cur_status = GL_ENTRYPOINT(glCheckFramebufferStatus)(GL_READ_FRAMEBUFFER);
329         VOGL_CHECK_GL_ERROR;
330
331         bool status = true;
332
333         if (cur_status == GL_FRAMEBUFFER_COMPLETE)
334         {
335             GL_ENTRYPOINT(glBlitFramebuffer)(
336                 0, 0, m_fb_attribs.m_width, m_fb_attribs.m_height,
337                 0, 0, m_fb_attribs.m_width, m_fb_attribs.m_height,
338                 (i == cDefFramebufferDepthStencil) ? (GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT) : GL_COLOR_BUFFER_BIT,
339                 GL_NEAREST);
340
341             if (vogl_check_gl_error_internal())
342             {
343                 status = false;
344             }
345         }
346
347         if (!status)
348         {
349             vogl_warning_printf("%s: Failed blitting framebuffer %u\n", VOGL_METHOD_NAME, i);
350         }
351
352         // Delete FBO
353         GL_ENTRYPOINT(glBindFramebuffer)(GL_READ_FRAMEBUFFER, 0);
354         VOGL_CHECK_GL_ERROR;
355
356         GL_ENTRYPOINT(glDeleteFramebuffers)(1, &fbo_handle);
357         VOGL_CHECK_GL_ERROR;
358
359         GL_ENTRYPOINT(glReadBuffer)(GL_FRONT_LEFT);
360         VOGL_CHECK_GL_ERROR;
361
362         GL_ENTRYPOINT(glBindTexture)(tex_target, 0);
363         VOGL_CHECK_GL_ERROR;
364
365         GL_ENTRYPOINT(glDeleteTextures)(1, &tex_handle);
366         VOGL_CHECK_GL_ERROR;
367     }
368
369     return true;
370 }
371
372 void vogl_default_framebuffer_state::clear()
373 {
374     m_fb_attribs.clear();
375
376     for (uint i = 0; i < cDefFramebufferTotal; i++)
377         m_textures[i].clear();
378
379     m_valid = false;
380 }
381
382 bool vogl_default_framebuffer_state::serialize(json_node &node, vogl_blob_manager &blob_manager) const
383 {
384     if (!m_valid)
385         return false;
386
387     if (!m_fb_attribs.serialize(node.add_object("attribs")))
388         return false;
389
390     json_node &framebuffers_array = node.add_array("framebuffers");
391     for (uint i = 0; i < cDefFramebufferTotal; i++)
392     {
393         json_node &object_node = framebuffers_array.add_object();
394         if (m_textures[i].is_valid())
395         {
396             if (!m_textures[i].serialize(object_node, blob_manager))
397                 return false;
398         }
399     }
400
401     return true;
402 }
403
404 bool vogl_default_framebuffer_state::deserialize(const json_node &node, const vogl_blob_manager &blob_manager)
405 {
406     clear();
407
408     if (!node.has_object("attribs"))
409         return false;
410
411     if (!m_fb_attribs.deserialize(*node.find_child_object("attribs")))
412         return false;
413
414     const json_node *pFramebuffers_array = node.find_child_array("framebuffers");
415     if (pFramebuffers_array)
416     {
417         for (uint i = 0; i < math::minimum<uint>(cDefFramebufferTotal, pFramebuffers_array->size()); i++)
418         {
419             if ((pFramebuffers_array->is_child_object(i)) && (pFramebuffers_array->get_child(i)->size()))
420             {
421                 if (!m_textures[i].deserialize(*pFramebuffers_array->get_child(i), blob_manager))
422                     return false;
423             }
424         }
425     }
426
427     m_valid = true;
428
429     return true;
430 }
431