]> git.cworth.org Git - vogl/blob - src/voglcommon/vogl_buffer_state.cpp
- Features for 10ft: PBO's, snapshotting/restoring mapped buffers during replaying
[vogl] / src / voglcommon / vogl_buffer_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_buffer_state.cpp
27 #include "vogl_common.h"
28 #include "vogl_buffer_state.h"
29 #include "vogl_gl_state_snapshot.h"
30
31 vogl_buffer_state::vogl_buffer_state()
32     : m_snapshot_handle(0),
33       m_target(GL_NONE),
34       m_is_valid(false)
35 {
36     VOGL_FUNC_TRACER
37 }
38
39 vogl_buffer_state::~vogl_buffer_state()
40 {
41     VOGL_FUNC_TRACER
42 }
43
44 // TODO: GL3/4 buffer types, add GL_QUERY_BUFFER
45 bool vogl_buffer_state::snapshot(const vogl_context_info &context_info, vogl_handle_remapper &remapper, GLuint64 handle, GLenum target)
46 {
47     VOGL_FUNC_TRACER
48
49     VOGL_NOTE_UNUSED(remapper);
50     VOGL_NOTE_UNUSED(context_info);
51
52     VOGL_CHECK_GL_ERROR;
53
54     clear();
55
56     VOGL_ASSERT(handle <= cUINT32_MAX);
57
58     m_snapshot_handle = static_cast<GLuint>(handle);
59     m_target = target;
60
61     if (m_target != GL_NONE)
62     {
63         vogl_scoped_binding_state orig_bindings(target);
64
65         GL_ENTRYPOINT(glBindBuffer)(target, m_snapshot_handle);
66         VOGL_CHECK_GL_ERROR;
67
68         bool any_gl_errors = false;
69
70 #define GET_INT(pname)                                                  \
71     do                                                                  \
72     {                                                                   \
73         int value = 0;                                                  \
74         GL_ENTRYPOINT(glGetBufferParameteriv)(m_target, pname, &value); \
75         if (vogl_check_gl_error())                                       \
76             any_gl_errors = true;                                       \
77         m_params.insert(pname, 0, &value, sizeof(value));               \
78     } while (0)
79
80         GET_INT(GL_BUFFER_ACCESS);
81         GET_INT(GL_BUFFER_MAPPED);
82         GET_INT(GL_BUFFER_SIZE);
83         GET_INT(GL_BUFFER_USAGE);
84
85 #undef GET_INT
86
87         GLvoid *pSnapshot_map_ptr;
88         GL_ENTRYPOINT(glGetBufferPointerv)(m_target, GL_BUFFER_MAP_POINTER, &pSnapshot_map_ptr);
89         if (vogl_check_gl_error())
90             any_gl_errors = true;
91
92         if (any_gl_errors)
93         {
94             vogl_error_printf("%s: GL error while retrieving buffer %" PRIu64 " target %s's params\n", VOGL_METHOD_NAME,
95                              (uint64_t)handle, g_gl_enums.find_gl_name(target));
96             clear();
97             return false;
98         }
99
100         int buf_size = m_params.get_value<int>(GL_BUFFER_SIZE);
101         if (buf_size < 0)
102         {
103             vogl_error_printf("%s: Invalid buffer size, buffer %" PRIu64 " target %s size %i\n", VOGL_METHOD_NAME, (uint64_t)handle, g_gl_enums.find_gl_name(target), buf_size);
104             clear();
105             return false;
106         }
107
108         if (m_params.get_value<int>(GL_BUFFER_MAPPED) != 0)
109         {
110             vogl_error_printf("%s: Can't snapshot buffer %" PRIu64 " target %s while it's currently mapped\n", VOGL_METHOD_NAME,
111                              (uint64_t)handle, g_gl_enums.find_gl_name(target));
112             clear();
113             return false;
114         }
115
116         if (buf_size)
117         {
118             if (!m_buffer_data.try_resize(buf_size))
119             {
120                 vogl_error_printf("%s: Out of memory while trying to allocate buffer, buffer %" PRIu64 " target %s size %i\n", VOGL_METHOD_NAME, (uint64_t)handle, g_gl_enums.find_gl_name(target), buf_size);
121                 clear();
122                 return false;
123             }
124
125             // This will fail if the buffer is currently mapped.
126             GL_ENTRYPOINT(glGetBufferSubData)(target, 0, buf_size, m_buffer_data.get_ptr());
127
128             if (vogl_check_gl_error())
129             {
130                 vogl_warning_printf("%s: GL error while retrieving buffer data, buffer %" PRIu64 " target %s size %i\n", VOGL_METHOD_NAME, (uint64_t)handle, g_gl_enums.find_gl_name(target), buf_size);
131             }
132         }
133     }
134
135     m_is_valid = true;
136
137     return true;
138 }
139
140 void vogl_buffer_state::set_mapped_buffer_snapshot_state(const vogl_mapped_buffer_desc &map_desc)
141 {
142     VOGL_FUNC_TRACER
143
144     VOGL_ASSERT(map_desc.m_buffer == m_snapshot_handle);
145
146     m_is_mapped = true;
147
148     m_map_ofs = map_desc.m_offset;
149     m_map_size = map_desc.m_length;
150     m_map_access = map_desc.m_access;
151     m_map_range = map_desc.m_range;
152 }
153
154 bool vogl_buffer_state::restore(const vogl_context_info &context_info, vogl_handle_remapper &remapper, GLuint64 &handle) const
155 {
156     VOGL_FUNC_TRACER
157
158     VOGL_NOTE_UNUSED(context_info);
159
160     int buf_usage = 0, buf_size = 0;
161
162     if (!m_is_valid)
163         return false;
164
165     VOGL_CHECK_GL_ERROR;
166
167     bool created_handle = false;
168
169     if (!handle)
170     {
171         GLuint handle32 = 0;
172         GL_ENTRYPOINT(glGenBuffers)(1, &handle32);
173         if ((vogl_check_gl_error()) || (!handle32))
174             return false;
175
176         handle = handle32;
177
178         remapper.declare_handle(VOGL_NAMESPACE_BUFFERS, m_snapshot_handle, handle, m_target);
179         VOGL_ASSERT(remapper.remap_handle(VOGL_NAMESPACE_BUFFERS, m_snapshot_handle) == handle);
180
181         created_handle = true;
182     }
183
184     if (m_target != GL_NONE)
185     {
186         vogl_scoped_binding_state orig_bindings(m_target);
187
188         GL_ENTRYPOINT(glBindBuffer)(m_target, static_cast<GLuint>(handle));
189         if (vogl_check_gl_error())
190             goto handle_failure;
191
192         buf_usage = m_params.get_value<int>(GL_BUFFER_USAGE);
193         buf_size = m_params.get_value<int>(GL_BUFFER_SIZE);
194
195         if (buf_size != static_cast<int>(m_buffer_data.size()))
196         {
197             VOGL_ASSERT_ALWAYS;
198             goto handle_failure;
199         }
200
201         GL_ENTRYPOINT(glBufferData)(m_target, buf_size, m_buffer_data.get_ptr(), buf_usage);
202         if (vogl_check_gl_error())
203             goto handle_failure;
204     }
205
206     return true;
207
208 handle_failure:
209     vogl_error_printf("%s: Failed restoring trace buffer %u target %s size %u\n", VOGL_METHOD_NAME, m_snapshot_handle, g_gl_enums.find_gl_name(m_target), buf_size);
210
211     GL_ENTRYPOINT(glBindBuffer)(m_target, 0);
212     VOGL_CHECK_GL_ERROR;
213
214     if ((handle) && (created_handle))
215     {
216         remapper.delete_handle_and_object(VOGL_NAMESPACE_BUFFERS, m_snapshot_handle, handle);
217
218         //GLuint handle32 = static_cast<GLuint>(handle);
219         //GL_ENTRYPOINT(glDeleteBuffers)(1, &handle32);
220         //VOGL_CHECK_GL_ERROR;
221
222         handle = 0;
223     }
224
225     return false;
226 }
227
228 bool vogl_buffer_state::remap_handles(vogl_handle_remapper &remapper)
229 {
230     VOGL_FUNC_TRACER
231
232     if (!m_is_valid)
233         return false;
234
235     m_snapshot_handle = static_cast<GLuint>(remapper.remap_handle(VOGL_NAMESPACE_BUFFERS, m_snapshot_handle));
236
237     return true;
238 }
239
240 void vogl_buffer_state::clear()
241 {
242     VOGL_FUNC_TRACER
243
244     m_snapshot_handle = 0;
245     m_target = GL_NONE;
246     m_buffer_data.clear();
247     m_params.clear();
248     m_is_valid = false;
249
250     m_map_ofs = 0;
251     m_map_size = 0;
252     m_map_access = 0;
253     m_map_range = false;
254     m_is_mapped = false;
255 }
256
257 bool vogl_buffer_state::serialize(json_node &node, vogl_blob_manager &blob_manager) const
258 {
259     VOGL_FUNC_TRACER
260
261     if (!m_is_valid)
262         return false;
263
264     dynamic_string blob_id;
265
266     if (m_buffer_data.size())
267     {
268         const char *pBuf_type = utils::map_value(static_cast<int>(m_target), "buf",
269                                                  GL_ARRAY_BUFFER, "buf_vertex",
270                                                  GL_ELEMENT_ARRAY_BUFFER, "buf_index",
271                                                  GL_UNIFORM_BUFFER, "buf_uniform");
272
273         dynamic_string prefix;
274         prefix.format("%s_0x%04X", pBuf_type, m_params.get_value<int>(GL_BUFFER_USAGE));
275
276         blob_id = blob_manager.add_buf_compute_unique_id(m_buffer_data.get_ptr(), m_buffer_data.size(), prefix.get_ptr(), "raw");
277         if (blob_id.is_empty())
278             return false;
279     }
280
281     node.add_key_value("handle", m_snapshot_handle);
282     node.add_key_value("target", g_gl_enums.find_gl_name(m_target));
283     node.add_key_value("buffer_data_blob_id", blob_id);
284
285     node.add_key_value("map_ofs", m_map_ofs);
286     node.add_key_value("map_size", m_map_size);
287     node.add_key_value("map_access", m_map_access);
288     node.add_key_value("map_range", m_map_range);
289     node.add_key_value("is_mapped", m_is_mapped);
290
291     if (!m_params.serialize(node.add_object("params"), blob_manager))
292         return false;
293
294     return true;
295 }
296
297 bool vogl_buffer_state::deserialize(const json_node &node, const vogl_blob_manager &blob_manager)
298 {
299     VOGL_FUNC_TRACER
300
301     clear();
302
303     m_snapshot_handle = node.value_as_uint32("handle");
304     m_target = vogl_get_json_value_as_enum(node, "target");
305     if (m_target != GL_NONE)
306     {
307         const json_node *pParams_obj = node.find_child_object("params");
308         if (!pParams_obj)
309         {
310             clear();
311             return false;
312         }
313
314         if (!m_params.deserialize(*pParams_obj, blob_manager))
315         {
316             clear();
317             return false;
318         }
319
320         int buf_size = m_params.get_value<int>(GL_BUFFER_SIZE);
321
322         if (buf_size)
323         {
324             dynamic_string blob_id(node.value_as_string_ptr("buffer_data_blob_id"));
325             if (blob_id.is_empty())
326             {
327                 clear();
328                 return false;
329             }
330
331             if (!blob_manager.get(blob_id, m_buffer_data))
332             {
333                 clear();
334                 return false;
335             }
336         }
337
338         if (buf_size != static_cast<int>(m_buffer_data.size()))
339         {
340             clear();
341             return false;
342         }
343     }
344
345     m_map_ofs = node.value_as_uint64("map_ofs");
346     m_map_size = node.value_as_uint64("map_size");
347     m_map_access = node.value_as_uint32("map_access");
348     m_map_range = node.value_as_bool("map_range");
349     m_is_mapped = node.value_as_bool("is_mapped");
350
351     m_is_valid = true;
352
353     return true;
354 }
355
356 // Content comparison, ignores handle.
357 bool vogl_buffer_state::compare_restorable_state(const vogl_gl_object_state &rhs_obj) const
358 {
359     VOGL_FUNC_TRACER
360
361     if ((!m_is_valid) || (!rhs_obj.is_valid()))
362         return false;
363
364     if (rhs_obj.get_type() != cGLSTBuffer)
365         return false;
366
367     const vogl_buffer_state &rhs = static_cast<const vogl_buffer_state &>(rhs_obj);
368
369     if (this == &rhs)
370         return true;
371
372     if (m_target != rhs.m_target)
373         return false;
374
375     if (m_buffer_data != rhs.m_buffer_data)
376         return false;
377
378     if (m_params != rhs.m_params)
379         return false;
380
381     return true;
382 }
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407