]> git.cworth.org Git - vogl/blob - src/voglcommon/vogl_vao_state.cpp
Initial vogl checkin
[vogl] / src / voglcommon / vogl_vao_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_vao_state.cpp
27 #include "vogl_vao_state.h"
28
29 bool vogl_vertex_attrib_desc::operator==(const vogl_vertex_attrib_desc &rhs) const
30 {
31     VOGL_FUNC_TRACER
32
33 #define CMP(x)      \
34     if (x != rhs.x) \
35         return false;
36     CMP(m_pointer);
37     CMP(m_element_array_binding);
38     CMP(m_array_binding);
39     CMP(m_size);
40     CMP(m_type);
41     CMP(m_stride);
42     CMP(m_integer);
43     CMP(m_divisor);
44     CMP(m_enabled);
45     CMP(m_normalized);
46 #undef CMP
47     return true;
48 }
49
50 vogl_vao_state::vogl_vao_state()
51     : m_snapshot_handle(0),
52       m_has_been_bound(false),
53       m_is_valid(false)
54 {
55     VOGL_FUNC_TRACER
56 }
57
58 vogl_vao_state::~vogl_vao_state()
59 {
60     VOGL_FUNC_TRACER
61 }
62
63 static int vogl_get_vertex_attrib_int(uint index, GLenum pname)
64 {
65     VOGL_FUNC_TRACER
66
67     int values[4] = { 0, 0, 0, 0 };
68     GL_ENTRYPOINT(glGetVertexAttribIiv)(index, pname, values);
69     return values[0];
70 }
71
72 static uint vogl_get_vertex_attrib_uint(uint index, GLenum pname)
73 {
74     VOGL_FUNC_TRACER
75
76     uint values[4] = { 0, 0, 0, 0 };
77     GL_ENTRYPOINT(glGetVertexAttribIuiv)(index, pname, values);
78     return values[0];
79 }
80
81 #if 0
82 static vec4D vogl_get_vertex_attrib_vec4D(uint index, GLenum pname)
83 {
84         vec4D values(0);
85         ACTUAL_GL_ENTRYPOINT(glGetVertexAttribdv)(index, pname, &values[0]);
86         return values;
87 }
88 #endif
89
90 bool vogl_vao_state::snapshot(const vogl_context_info &context_info, vogl_handle_remapper &remapper, GLuint64 handle, GLenum target)
91 {
92     VOGL_FUNC_TRACER
93
94     VOGL_NOTE_UNUSED(target);
95     VOGL_NOTE_UNUSED(remapper);
96
97     VOGL_CHECK_GL_ERROR;
98
99     clear();
100
101     VOGL_ASSERT(handle <= cUINT32_MAX);
102
103     m_snapshot_handle = static_cast<GLuint>(handle);
104
105     // TODO: Core profile support
106     m_has_been_bound = handle ? (GL_ENTRYPOINT(glIsVertexArray)(static_cast<GLuint>(handle)) != 0) : true;
107
108     if (m_has_been_bound)
109     {
110         vogl_scoped_binding_state orig_binding(GL_VERTEX_ARRAY);
111
112         GL_ENTRYPOINT(glBindVertexArray)(m_snapshot_handle);
113         VOGL_CHECK_GL_ERROR;
114
115         m_vertex_attribs.resize(context_info.get_max_vertex_attribs());
116
117         for (uint i = 0; i < context_info.get_max_vertex_attribs(); i++)
118         {
119             vogl_vertex_attrib_desc &desc = m_vertex_attribs[i];
120
121             desc.m_element_array_binding = vogl_get_gl_integer(GL_ELEMENT_ARRAY_BUFFER_BINDING);
122             desc.m_array_binding = vogl_get_vertex_attrib_int(i, GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING);
123             desc.m_enabled = vogl_get_vertex_attrib_int(i, GL_VERTEX_ATTRIB_ARRAY_ENABLED) != 0;
124             desc.m_size = vogl_get_vertex_attrib_int(i, GL_VERTEX_ATTRIB_ARRAY_SIZE);
125             desc.m_type = vogl_get_vertex_attrib_int(i, GL_VERTEX_ATTRIB_ARRAY_TYPE);
126             desc.m_normalized = vogl_get_vertex_attrib_int(i, GL_VERTEX_ATTRIB_ARRAY_NORMALIZED) != 0;
127             desc.m_stride = vogl_get_vertex_attrib_int(i, GL_VERTEX_ATTRIB_ARRAY_STRIDE);
128             desc.m_integer = vogl_get_vertex_attrib_int(i, GL_VERTEX_ATTRIB_ARRAY_INTEGER) != 0;
129             desc.m_divisor = vogl_get_vertex_attrib_uint(i, GL_VERTEX_ATTRIB_ARRAY_DIVISOR);
130
131             GLvoid *ptr = NULL;
132             GL_ENTRYPOINT(glGetVertexAttribPointerv)(i, GL_VERTEX_ATTRIB_ARRAY_POINTER, &ptr);
133             desc.m_pointer = reinterpret_cast<vogl_trace_ptr_value>(ptr);
134
135             VOGL_CHECK_GL_ERROR;
136         }
137     }
138
139     m_is_valid = true;
140
141     return true;
142 }
143
144 bool vogl_vao_state::restore(const vogl_context_info &context_info, vogl_handle_remapper &remapper, GLuint64 &handle) const
145 {
146     VOGL_FUNC_TRACER
147
148     VOGL_CHECK_GL_ERROR;
149
150     if (!m_is_valid)
151         return false;
152
153     vogl_scoped_binding_state orig_binding(GL_VERTEX_ARRAY, GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER);
154
155     if ((!m_snapshot_handle) && (!handle))
156     {
157         GL_ENTRYPOINT(glBindVertexArray)(0);
158         VOGL_CHECK_GL_ERROR;
159     }
160     else
161     {
162         if (!handle)
163         {
164             GLuint handle32 = 0;
165             GL_ENTRYPOINT(glGenVertexArrays)(1, &handle32);
166             if ((vogl_check_gl_error()) || (!handle32))
167                 return false;
168             handle = handle32;
169
170             if (m_snapshot_handle)
171             {
172                 remapper.declare_handle(VOGL_NAMESPACE_VERTEX_ARRAYS, m_snapshot_handle, handle, GL_NONE);
173                 VOGL_ASSERT(remapper.remap_handle(VOGL_NAMESPACE_VERTEX_ARRAYS, m_snapshot_handle) == handle);
174             }
175         }
176
177         if (m_has_been_bound)
178         {
179             GL_ENTRYPOINT(glBindVertexArray)(static_cast<GLuint>(handle));
180             VOGL_CHECK_GL_ERROR;
181         }
182     }
183
184     if (m_has_been_bound)
185     {
186         if (m_vertex_attribs.size() > context_info.get_max_vertex_attribs())
187         {
188             vogl_warning_printf("%s: Saved VAO state has %u attribs, but context only allows %u attribs\n", VOGL_METHOD_NAME, m_vertex_attribs.size(), context_info.get_max_vertex_attribs());
189         }
190
191         for (uint i = 0; i < math::minimum<uint>(context_info.get_max_vertex_attribs(), m_vertex_attribs.size()); i++)
192         {
193             const vogl_vertex_attrib_desc &desc = m_vertex_attribs[i];
194
195             GL_ENTRYPOINT(glBindBuffer)(GL_ELEMENT_ARRAY_BUFFER, static_cast<GLuint>(remapper.remap_handle(VOGL_NAMESPACE_BUFFERS, desc.m_element_array_binding)));
196             VOGL_CHECK_GL_ERROR;
197
198             GL_ENTRYPOINT(glBindBuffer)(GL_ARRAY_BUFFER, static_cast<GLuint>(remapper.remap_handle(VOGL_NAMESPACE_BUFFERS, desc.m_array_binding)));
199             VOGL_CHECK_GL_ERROR;
200
201             vogl_trace_ptr_value trace_ptr_val = desc.m_pointer;
202
203             vogl_trace_ptr_value restore_ptr_val = trace_ptr_val;
204             if ((!desc.m_array_binding) && (trace_ptr_val) && (context_info.is_compatibility_profile()))
205                 restore_ptr_val = remapper.remap_vertex_attrib_ptr(i, trace_ptr_val);
206
207             void *pRestore_ptr = reinterpret_cast<void *>(restore_ptr_val);
208
209             if ((handle) && (desc.m_array_binding == 0))
210             {
211                 // If it's a non-default VAO, and there's no array binding, we can't call glVertexAttribPointer() because it's not allowed by AMD drivers (it thinks we're trying to set client side array data)
212                 // "OpenGL: glVertexAttribPointer failed because it is not allowed to specify a client-side vertex or element array when a non-default vertex array object is bound (GL_INVALID_OPERATION) [source=API type=ERROR severity=HIGH id=2100]"
213
214                 // Sanity checks.
215                 if ((pRestore_ptr != NULL) || (desc.m_stride) || (desc.m_enabled))
216                 {
217                     vogl_warning_printf("%s: Can't bind client side vertex array data on a non-default VAO, trace handle %u GL handle %u, restore ptr %p, size %i stride %i enabled %u\n",
218                                        VOGL_METHOD_NAME, m_snapshot_handle, static_cast<uint>(handle), pRestore_ptr, desc.m_size, desc.m_stride, desc.m_enabled);
219                 }
220             }
221             else
222             {
223                 if (desc.m_integer)
224                 {
225                     GL_ENTRYPOINT(glVertexAttribIPointer)(i, desc.m_size, desc.m_type, desc.m_stride, pRestore_ptr);
226                     VOGL_CHECK_GL_ERROR;
227                 }
228                 else
229                 {
230                     GL_ENTRYPOINT(glVertexAttribPointer)(i, desc.m_size, desc.m_type, desc.m_normalized, desc.m_stride, pRestore_ptr);
231                     VOGL_CHECK_GL_ERROR;
232                 }
233             }
234
235             GL_ENTRYPOINT(glVertexAttribDivisor)(i, desc.m_divisor);
236             VOGL_CHECK_GL_ERROR;
237
238             if (desc.m_enabled)
239             {
240                 GL_ENTRYPOINT(glEnableVertexAttribArray)(i);
241                 VOGL_CHECK_GL_ERROR;
242             }
243             else
244             {
245                 GL_ENTRYPOINT(glDisableVertexAttribArray)(i);
246                 VOGL_CHECK_GL_ERROR;
247             }
248         }
249     }
250
251     return true;
252 }
253
254 bool vogl_vao_state::remap_handles(vogl_handle_remapper &remapper)
255 {
256     VOGL_FUNC_TRACER
257
258     m_snapshot_handle = static_cast<GLuint>(remapper.remap_handle(VOGL_NAMESPACE_VERTEX_ARRAYS, m_snapshot_handle));
259
260     for (uint i = 0; i < m_vertex_attribs.size(); i++)
261     {
262         if (m_vertex_attribs[i].m_element_array_binding)
263             m_vertex_attribs[i].m_element_array_binding = static_cast<GLuint>(remapper.remap_handle(VOGL_NAMESPACE_BUFFERS, m_vertex_attribs[i].m_element_array_binding));
264
265         if (m_vertex_attribs[i].m_array_binding)
266             m_vertex_attribs[i].m_array_binding = static_cast<GLuint>(remapper.remap_handle(VOGL_NAMESPACE_BUFFERS, m_vertex_attribs[i].m_array_binding));
267
268         else if (m_vertex_attribs[i].m_pointer)
269             m_vertex_attribs[i].m_pointer = remapper.remap_vertex_attrib_ptr(i, m_vertex_attribs[i].m_pointer);
270     }
271
272     return true;
273 }
274
275 void vogl_vao_state::clear()
276 {
277     VOGL_FUNC_TRACER
278
279     m_vertex_attribs.clear();
280     m_snapshot_handle = 0;
281     m_has_been_bound = false;
282     m_is_valid = false;
283 }
284
285 bool vogl_vao_state::serialize(json_node &node, vogl_blob_manager &blob_manager) const
286 {
287     VOGL_FUNC_TRACER
288
289     VOGL_NOTE_UNUSED(blob_manager);
290
291     if (!m_is_valid)
292         return false;
293
294     node.add_key_value("handle", m_snapshot_handle);
295     node.add_key_value("has_been_bound", m_has_been_bound);
296
297     json_node &vertex_attribs_array = node.add_array("vertex_attribs");
298     for (uint i = 0; i < m_vertex_attribs.size(); i++)
299     {
300         const vogl_vertex_attrib_desc &desc = m_vertex_attribs[i];
301
302         json_node &attribs_obj = vertex_attribs_array.add_object();
303         attribs_obj.add_key_value("pointer", desc.m_pointer);
304         attribs_obj.add_key_value("element_array_binding", desc.m_element_array_binding);
305         attribs_obj.add_key_value("array_binding", desc.m_array_binding);
306         attribs_obj.add_key_value("size", desc.m_size);
307         attribs_obj.add_key_value("type", g_gl_enums.find_gl_name(desc.m_type));
308         attribs_obj.add_key_value("stride", desc.m_stride);
309         attribs_obj.add_key_value("integer", desc.m_integer);
310         attribs_obj.add_key_value("divisor", desc.m_divisor);
311         attribs_obj.add_key_value("enabled", desc.m_enabled);
312         attribs_obj.add_key_value("normalized", desc.m_normalized);
313     }
314
315     return true;
316 }
317
318 bool vogl_vao_state::deserialize(const json_node &node, const vogl_blob_manager &blob_manager)
319 {
320     VOGL_FUNC_TRACER
321
322     VOGL_NOTE_UNUSED(blob_manager);
323
324     clear();
325
326     m_snapshot_handle = node.value_as_uint32("handle");
327     m_has_been_bound = node.value_as_bool("has_been_bound", true);
328
329     const json_node *pVertex_attribs_array = node.find_child_array("vertex_attribs");
330     if (!pVertex_attribs_array)
331         return false;
332
333     m_vertex_attribs.resize(pVertex_attribs_array->size());
334     for (uint i = 0; i < pVertex_attribs_array->size(); i++)
335     {
336         vogl_vertex_attrib_desc &desc = m_vertex_attribs[i];
337
338         const json_node *pAttribs_obj = pVertex_attribs_array->get_value_as_object(i);
339         if (!pAttribs_obj)
340             return false;
341
342         desc.m_pointer = static_cast<vogl_trace_ptr_value>(pAttribs_obj->value_as_uint64("pointer"));
343         desc.m_element_array_binding = pAttribs_obj->value_as_uint32("element_array_binding");
344         desc.m_array_binding = pAttribs_obj->value_as_uint32("array_binding");
345         desc.m_size = pAttribs_obj->value_as_int("size");
346         desc.m_type = vogl_get_json_value_as_enum(*pAttribs_obj, "type");
347         desc.m_stride = pAttribs_obj->value_as_int("stride");
348         desc.m_integer = pAttribs_obj->value_as_bool("integer");
349         desc.m_divisor = pAttribs_obj->value_as_uint32("divisor");
350         desc.m_enabled = pAttribs_obj->value_as_bool("enabled");
351         desc.m_normalized = pAttribs_obj->value_as_bool("normalized");
352     }
353
354     m_is_valid = true;
355
356     return true;
357 }
358
359 bool vogl_vao_state::compare_all_state(const vogl_vao_state &rhs) const
360 {
361     VOGL_FUNC_TRACER
362
363     if ((!is_valid()) || (!rhs.is_valid()))
364         return false;
365
366     if (this == &rhs)
367         return true;
368
369     return (m_snapshot_handle == rhs.m_snapshot_handle) && (m_vertex_attribs == rhs.m_vertex_attribs) && (m_has_been_bound == rhs.m_has_been_bound);
370 }
371
372 bool vogl_vao_state::compare_restorable_state(const vogl_gl_object_state &rhs_obj) const
373 {
374     VOGL_FUNC_TRACER
375
376     if ((!is_valid()) || (!rhs_obj.is_valid()))
377         return false;
378
379     if (rhs_obj.get_type() != cGLSTVertexArray)
380         return false;
381
382     const vogl_vao_state &rhs = static_cast<const vogl_vao_state &>(rhs_obj);
383
384     if (this == &rhs)
385         return true;
386
387     if (m_has_been_bound != rhs.m_has_been_bound)
388         return false;
389
390     if (m_vertex_attribs.size() != rhs.m_vertex_attribs.size())
391         return false;
392
393     for (uint i = 0; i < m_vertex_attribs.size(); i++)
394     {
395         const vogl_vertex_attrib_desc &lhs_desc = m_vertex_attribs[i];
396         const vogl_vertex_attrib_desc &rhs_desc = rhs.m_vertex_attribs[i];
397
398         if ((lhs_desc.m_element_array_binding != 0) != (rhs_desc.m_element_array_binding != 0))
399             return false;
400
401         if ((lhs_desc.m_array_binding != 0) != (rhs_desc.m_array_binding != 0))
402             return false;
403
404 #define CMP(x)                    \
405     if (lhs_desc.x != rhs_desc.x) \
406         return false;
407         CMP(m_enabled);
408         CMP(m_size);
409         CMP(m_type);
410         CMP(m_normalized);
411         CMP(m_stride);
412         CMP(m_integer);
413         CMP(m_divisor);
414 #undef CMP
415     }
416
417     return true;
418 }