1 /**************************************************************************
3 * Copyright 2013-2014 RAD Game Tools and Valve Software
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:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
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
24 **************************************************************************/
26 // File: vogl_framebuffer_capturer.cpp
27 #include "vogl_framebuffer_capturer.h"
29 vogl_framebuffer_capturer::vogl_framebuffer_capturer()
30 : m_initialized(false),
31 m_did_any_write_fail(false),
33 m_pWrite_opaque(NULL),
34 m_pixel_format(GL_NONE),
35 m_pixel_type(GL_NONE),
46 vogl_framebuffer_capturer::~vogl_framebuffer_capturer()
50 // Purposely DO NOT deinit, that's the user's problem, because deinit() requires a current context.
55 vogl_warning_printf("%s: vogl_framebuffer_capturer being destroyed while still initialized\n", VOGL_METHOD_NAME);
59 bool vogl_framebuffer_capturer::init(uint num_buffers, vogl_write_image_callback_func_ptr pWrite_func, void *pWrite_opaque, GLenum pixel_format, GLenum pixel_type)
65 if (num_buffers > cMaxBufs)
71 m_num_pbos = num_buffers;
72 m_pWrite_func = pWrite_func;
73 m_pWrite_opaque = pWrite_opaque;
74 m_pixel_format = pixel_format;
75 m_pixel_type = pixel_type;
82 void vogl_framebuffer_capturer::deinit(bool ok_to_make_gl_calls)
89 if (ok_to_make_gl_calls)
96 m_initialized = false;
97 m_did_any_write_fail = false;
100 m_pWrite_opaque = NULL;
102 m_pixel_format = GL_NONE;
103 m_pixel_type = GL_NONE;
109 for (uint i = 0; i < cMaxBufs; i++)
116 bool vogl_framebuffer_capturer::flush_pbo(pbo &buf)
120 VOGL_ASSERT(buf.m_buffer);
122 if ((!buf.m_buffer) || (!buf.m_busy))
125 // Set it to not busy here, in case ctrl+c is hit while we're busy flushing the buffer
126 // TODO: Make this atomic?
131 vogl_scoped_binding_state binding_saver(GL_PIXEL_PACK_BUFFER);
135 GL_ENTRYPOINT(glBindBuffer)(GL_PIXEL_PACK_BUFFER, buf.m_buffer);
139 const void *pData = GL_ENTRYPOINT(glMapBuffer)(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY);
145 vogl_error_printf("%s: Unable to map pixel pack buffer %u\n", VOGL_METHOD_NAME, buf.m_buffer);
151 bool success = (*m_pWrite_func)(buf.m_width, buf.m_height, buf.m_pitch, buf.m_size, m_pixel_format, m_pixel_type, pData, m_pWrite_opaque, buf.m_frame_index);
153 m_did_any_write_fail = true;
156 GL_ENTRYPOINT(glUnmapBuffer)(GL_PIXEL_PACK_BUFFER);
164 bool vogl_framebuffer_capturer::flush()
171 while (m_num_busy_pbos)
173 pbo &tail = m_pbos[m_pbo_tail];
174 if (!flush_pbo(tail))
177 m_pbo_tail = (m_pbo_tail + 1) % m_num_pbos;
181 VOGL_ASSERT(m_pbo_tail == m_pbo_head);
185 void vogl_framebuffer_capturer::delete_all_bufs()
191 for (uint i = 0; i < m_num_pbos; i++)
193 pbo &buf = m_pbos[i];
197 GL_ENTRYPOINT(glDeleteBuffers)(1, &buf.m_buffer);
206 bool vogl_framebuffer_capturer::recreate_buffers(uint new_width, uint new_height)
217 m_cur_width = new_width;
218 m_cur_height = new_height;
220 vogl_scoped_state_saver state_saver(cGSTPixelStore);
221 vogl_scoped_binding_state binding_saver(GL_PIXEL_PACK_BUFFER);
223 if (vogl_check_gl_error())
226 vogl_reset_pixel_store_states();
228 size_t pitch = vogl_get_image_size(m_pixel_format, m_pixel_type, m_cur_width, 1, 1);
229 size_t total_size = vogl_get_image_size(m_pixel_format, m_pixel_type, m_cur_width, m_cur_height, 1);
231 if (vogl_check_gl_error())
234 for (uint i = 0; i < m_num_pbos; i++)
236 pbo &buf = m_pbos[i];
237 buf.m_width = m_cur_width;
238 buf.m_height = m_cur_height;
239 buf.m_pitch = (uint)pitch;
240 buf.m_size = total_size;
243 GL_ENTRYPOINT(glGenBuffers)(1, &buf.m_buffer);
244 if (vogl_check_gl_error())
247 GL_ENTRYPOINT(glBindBuffer)(GL_PIXEL_PACK_BUFFER, buf.m_buffer);
248 if (vogl_check_gl_error())
251 GL_ENTRYPOINT(glBufferData)(GL_PIXEL_PACK_BUFFER, total_size, NULL, GL_STREAM_READ);
252 if (vogl_check_gl_error())
259 bool vogl_framebuffer_capturer::capture(uint width, uint height, GLuint framebuffer, GLuint read_buffer, uint64_t frame_index)
269 if ((!width) || (!height))
277 if ((width != m_cur_width) || (height != m_cur_height))
279 if (!recreate_buffers(width, height))
283 if (m_num_busy_pbos == m_num_pbos)
285 pbo &buf_to_flush = m_pbos[m_pbo_tail];
286 VOGL_ASSERT(buf_to_flush.m_busy);
288 if (buf_to_flush.m_busy)
290 if (!flush_pbo(buf_to_flush))
294 m_pbo_tail = (m_pbo_tail + 1) % m_num_pbos;
298 pbo &buf_to_read = m_pbos[m_pbo_head];
299 VOGL_ASSERT(!buf_to_read.m_busy);
301 if (!buf_to_read.m_busy)
303 if (!vogl_copy_buffer_to_image(NULL, 0, m_cur_width, m_cur_height, m_pixel_format, m_pixel_type, false, framebuffer, read_buffer, buf_to_read.m_buffer))
306 buf_to_read.m_frame_index = frame_index;
307 buf_to_read.m_busy = true;
309 m_pbo_head = (m_pbo_head + 1) % m_num_pbos;
316 vogl::vector<GLuint> vogl_framebuffer_capturer::get_buffer_handles() const
320 vogl::vector<GLuint> res;
324 for (uint i = 0; i < cMaxBufs; i++)
326 if (m_pbos[i].m_buffer)
327 res.push_back(m_pbos[i].m_buffer);