]> git.cworth.org Git - vogl/blob - src/voglcommon/vogl_framebuffer_capturer.cpp
Initial vogl checkin
[vogl] / src / voglcommon / vogl_framebuffer_capturer.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_framebuffer_capturer.cpp
27 #include "vogl_framebuffer_capturer.h"
28
29 vogl_framebuffer_capturer::vogl_framebuffer_capturer()
30     : m_initialized(false),
31       m_did_any_write_fail(false),
32       m_pWrite_func(NULL),
33       m_pWrite_opaque(NULL),
34       m_pixel_format(GL_NONE),
35       m_pixel_type(GL_NONE),
36       m_num_pbos(0),
37       m_pbo_head(0),
38       m_pbo_tail(0),
39       m_num_busy_pbos(0),
40       m_cur_width(0),
41       m_cur_height(0)
42 {
43     VOGL_FUNC_TRACER
44 }
45
46 vogl_framebuffer_capturer::~vogl_framebuffer_capturer()
47 {
48     VOGL_FUNC_TRACER
49
50     // Purposely DO NOT deinit, that's the user's problem, because deinit() requires a current context.
51     //deinit(false);
52
53     if (m_initialized)
54     {
55         vogl_warning_printf("%s: vogl_framebuffer_capturer being destroyed while still initialized\n", VOGL_METHOD_NAME);
56     }
57 }
58
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)
60 {
61     VOGL_FUNC_TRACER
62
63     deinit(true);
64
65     if (num_buffers > cMaxBufs)
66     {
67         VOGL_ASSERT_ALWAYS;
68         return false;
69     }
70
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;
76
77     m_initialized = true;
78
79     return true;
80 }
81
82 void vogl_framebuffer_capturer::deinit(bool ok_to_make_gl_calls)
83 {
84     VOGL_FUNC_TRACER
85
86     if (!m_initialized)
87         return;
88
89     if (ok_to_make_gl_calls)
90     {
91         flush();
92
93         delete_all_bufs();
94     }
95
96     m_initialized = false;
97     m_did_any_write_fail = false;
98
99     m_pWrite_func = NULL;
100     m_pWrite_opaque = NULL;
101
102     m_pixel_format = GL_NONE;
103     m_pixel_type = GL_NONE;
104
105     m_num_pbos = 0;
106     m_pbo_head = 0;
107     m_pbo_tail = 0;
108     m_num_busy_pbos = 0;
109     for (uint i = 0; i < cMaxBufs; i++)
110         m_pbos[i].clear();
111
112     m_cur_width = 0;
113     m_cur_height = 0;
114 }
115
116 bool vogl_framebuffer_capturer::flush_pbo(pbo &buf)
117 {
118     VOGL_FUNC_TRACER
119
120     VOGL_ASSERT(buf.m_buffer);
121
122     if ((!buf.m_buffer) || (!buf.m_busy))
123         return false;
124
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?
127     buf.m_busy = false;
128
129     VOGL_CHECK_GL_ERROR;
130
131     vogl_scoped_binding_state binding_saver(GL_PIXEL_PACK_BUFFER);
132
133     VOGL_CHECK_GL_ERROR;
134
135     GL_ENTRYPOINT(glBindBuffer)(GL_PIXEL_PACK_BUFFER, buf.m_buffer);
136
137     VOGL_CHECK_GL_ERROR;
138
139     const void *pData = GL_ENTRYPOINT(glMapBuffer)(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY);
140
141     VOGL_CHECK_GL_ERROR;
142
143     if (!pData)
144     {
145         vogl_error_printf("%s: Unable to map pixel pack buffer %u\n", VOGL_METHOD_NAME, buf.m_buffer);
146     }
147     else
148     {
149         if (m_pWrite_func)
150         {
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);
152             if (!success)
153                 m_did_any_write_fail = true;
154         }
155
156         GL_ENTRYPOINT(glUnmapBuffer)(GL_PIXEL_PACK_BUFFER);
157
158         VOGL_CHECK_GL_ERROR;
159     }
160
161     return true;
162 }
163
164 bool vogl_framebuffer_capturer::flush()
165 {
166     VOGL_FUNC_TRACER
167
168     if (!m_initialized)
169         return false;
170
171     while (m_num_busy_pbos)
172     {
173         pbo &tail = m_pbos[m_pbo_tail];
174         if (!flush_pbo(tail))
175             return false;
176
177         m_pbo_tail = (m_pbo_tail + 1) % m_num_pbos;
178         m_num_busy_pbos--;
179     }
180
181     VOGL_ASSERT(m_pbo_tail == m_pbo_head);
182     return true;
183 }
184
185 void vogl_framebuffer_capturer::delete_all_bufs()
186 {
187     VOGL_FUNC_TRACER
188
189     VOGL_CHECK_GL_ERROR;
190
191     for (uint i = 0; i < m_num_pbos; i++)
192     {
193         pbo &buf = m_pbos[i];
194         if (!buf.m_buffer)
195             continue;
196
197         GL_ENTRYPOINT(glDeleteBuffers)(1, &buf.m_buffer);
198         buf.clear();
199     }
200
201     m_num_busy_pbos = 0;
202
203     VOGL_CHECK_GL_ERROR;
204 }
205
206 bool vogl_framebuffer_capturer::recreate_buffers(uint new_width, uint new_height)
207 {
208     VOGL_FUNC_TRACER
209
210     if (!flush())
211         return false;
212
213     VOGL_CHECK_GL_ERROR;
214
215     delete_all_bufs();
216
217     m_cur_width = new_width;
218     m_cur_height = new_height;
219
220     vogl_scoped_state_saver state_saver(cGSTPixelStore);
221     vogl_scoped_binding_state binding_saver(GL_PIXEL_PACK_BUFFER);
222
223     if (vogl_check_gl_error())
224         return false;
225
226     vogl_reset_pixel_store_states();
227
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);
230
231     if (vogl_check_gl_error())
232         return false;
233
234     for (uint i = 0; i < m_num_pbos; i++)
235     {
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;
241         buf.m_busy = false;
242
243         GL_ENTRYPOINT(glGenBuffers)(1, &buf.m_buffer);
244         if (vogl_check_gl_error())
245             return false;
246
247         GL_ENTRYPOINT(glBindBuffer)(GL_PIXEL_PACK_BUFFER, buf.m_buffer);
248         if (vogl_check_gl_error())
249             return false;
250
251         GL_ENTRYPOINT(glBufferData)(GL_PIXEL_PACK_BUFFER, total_size, NULL, GL_STREAM_READ);
252         if (vogl_check_gl_error())
253             return false;
254     }
255
256     return true;
257 }
258
259 bool vogl_framebuffer_capturer::capture(uint width, uint height, GLuint framebuffer, GLuint read_buffer, uint64_t frame_index)
260 {
261     VOGL_FUNC_TRACER
262
263     if (!m_initialized)
264     {
265         VOGL_ASSERT_ALWAYS;
266         return false;
267     }
268
269     if ((!width) || (!height))
270     {
271         VOGL_ASSERT_ALWAYS;
272         return false;
273     }
274
275     VOGL_CHECK_GL_ERROR;
276
277     if ((width != m_cur_width) || (height != m_cur_height))
278     {
279         if (!recreate_buffers(width, height))
280             return false;
281     }
282
283     if (m_num_busy_pbos == m_num_pbos)
284     {
285         pbo &buf_to_flush = m_pbos[m_pbo_tail];
286         VOGL_ASSERT(buf_to_flush.m_busy);
287
288         if (buf_to_flush.m_busy)
289         {
290             if (!flush_pbo(buf_to_flush))
291                 return false;
292         }
293
294         m_pbo_tail = (m_pbo_tail + 1) % m_num_pbos;
295         m_num_busy_pbos--;
296     }
297
298     pbo &buf_to_read = m_pbos[m_pbo_head];
299     VOGL_ASSERT(!buf_to_read.m_busy);
300
301     if (!buf_to_read.m_busy)
302     {
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))
304             return false;
305
306         buf_to_read.m_frame_index = frame_index;
307         buf_to_read.m_busy = true;
308
309         m_pbo_head = (m_pbo_head + 1) % m_num_pbos;
310         m_num_busy_pbos++;
311     }
312
313     return true;
314 }
315
316 vogl::vector<GLuint> vogl_framebuffer_capturer::get_buffer_handles() const
317 {
318     VOGL_FUNC_TRACER
319
320     vogl::vector<GLuint> res;
321
322     if (m_initialized)
323     {
324         for (uint i = 0; i < cMaxBufs; i++)
325         {
326             if (m_pbos[i].m_buffer)
327                 res.push_back(m_pbos[i].m_buffer);
328         }
329     }
330
331     return res;
332 }