]> git.cworth.org Git - vogl/blob - src/voglcommon/vogl_texture_state.cpp
fbf84d1832c8e0ba4126f8bd89c1e4f7e7a50429
[vogl] / src / voglcommon / vogl_texture_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_texture_state.cpp
27 #include "vogl_texture_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 #include "vogl_shader_utils.h"
36 #include "vogl_msaa_texture.h"
37
38 #define VOGL_SERIALIZED_TEXTURE_STATE_VERSION 0x101
39
40 vogl_texture_state::vogl_texture_state()
41     : m_snapshot_handle(0),
42       m_target(GL_NONE),
43       m_buffer(0),
44       m_num_samples(0),
45       m_is_unquerable(false),
46       m_is_valid(false)
47 {
48     VOGL_FUNC_TRACER
49 }
50
51 vogl_texture_state::~vogl_texture_state()
52 {
53     VOGL_FUNC_TRACER
54
55     clear();
56 }
57
58 // TODO: Split this bad boy up into multiple methods
59 // TODO: Add cubemap array support
60 bool vogl_texture_state::snapshot(const vogl_context_info &context_info, vogl_handle_remapper &remapper, GLuint64 handle, GLenum target)
61 {
62     VOGL_FUNC_TRACER
63
64     VOGL_NOTE_UNUSED(remapper);
65
66     const bool is_target_multisampled = ((target == GL_TEXTURE_2D_MULTISAMPLE) || (target == GL_TEXTURE_2D_MULTISAMPLE_ARRAY));
67
68     // HACK HACK
69     //vogl_devel_dump_internal_texture_formats(context_info); exit(0);
70
71     VOGL_CHECK_GL_ERROR;
72
73     vogl_scoped_binding_state orig_bindings(GL_PIXEL_PACK_BUFFER, GL_PIXEL_UNPACK_BUFFER);
74     orig_bindings.save_textures();
75
76     vogl_scoped_state_saver pixelstore_state_saver(cGSTPixelStore);
77
78     vogl_scoped_state_saver pixeltransfer_state_saver;
79     if (!context_info.is_core_profile())
80         pixeltransfer_state_saver.save(cGSTPixelTransfer);
81
82     vogl_reset_pixel_store_states();
83     if (!context_info.is_core_profile())
84         vogl_reset_pixel_transfer_states();
85
86     GL_ENTRYPOINT(glBindBuffer)(GL_PIXEL_PACK_BUFFER, 0);
87     VOGL_CHECK_GL_ERROR;
88
89     GL_ENTRYPOINT(glBindBuffer)(GL_PIXEL_UNPACK_BUFFER, 0);
90     VOGL_CHECK_GL_ERROR;
91
92     clear();
93
94     VOGL_ASSERT(handle <= cUINT32_MAX);
95
96     m_snapshot_handle = static_cast<uint32>(handle);
97     m_target = target;
98
99     if (m_target == GL_NONE)
100     {
101         // Texture was genned, but not bound to a target yet, so there's nothing more to do.
102         m_is_valid = true;
103
104         return true;
105     }
106
107     GL_ENTRYPOINT(glBindTexture)(target, m_snapshot_handle);
108     VOGL_CHECK_GL_ERROR;
109
110     if (m_target == GL_TEXTURE_BUFFER)
111     {
112         GLint handle = 0;
113         GL_ENTRYPOINT(glGetIntegerv)(GL_TEXTURE_BUFFER_DATA_STORE_BINDING_ARB, &handle);
114         VOGL_CHECK_GL_ERROR;
115
116         m_buffer = handle;
117
118         GLint format = 0;
119         GL_ENTRYPOINT(glGetIntegerv)(GL_TEXTURE_BUFFER_FORMAT_ARB, &format);
120         VOGL_CHECK_GL_ERROR;
121
122         m_params.insert(GL_TEXTURE_INTERNAL_FORMAT, 0, &format, sizeof(format));
123
124         m_is_valid = true;
125         return true;
126     }
127
128     bool any_gl_errors = false;
129
130 #define GET_INT(pname)                                               \
131     do                                                               \
132     {                                                                \
133         int values[4] = { 0, 0, 0, 0 };                              \
134         GL_ENTRYPOINT(glGetTexParameteriv)(m_target, pname, values); \
135         if (vogl_check_gl_error())                                    \
136             any_gl_errors = true;                                    \
137         m_params.insert(pname, 0, values, sizeof(values[0]));        \
138     } while (0)
139 #define GET_FLOAT(pname)                                             \
140     do                                                               \
141     {                                                                \
142         float values[4] = { 0, 0, 0, 0 };                            \
143         GL_ENTRYPOINT(glGetTexParameterfv)(m_target, pname, values); \
144         if (vogl_check_gl_error())                                    \
145             any_gl_errors = true;                                    \
146         m_params.insert(pname, 0, values, sizeof(values[0]));        \
147     } while (0)
148
149     GET_INT(GL_TEXTURE_BASE_LEVEL);
150     GET_INT(GL_TEXTURE_MAX_LEVEL);
151
152     if (!is_target_multisampled)
153     {
154         GET_FLOAT(GL_TEXTURE_BORDER_COLOR);
155         GET_INT(GL_TEXTURE_COMPARE_MODE);
156         GET_INT(GL_TEXTURE_COMPARE_FUNC);
157         GET_FLOAT(GL_TEXTURE_LOD_BIAS);
158         GET_INT(GL_TEXTURE_MIN_FILTER);
159         GET_INT(GL_TEXTURE_MAG_FILTER);
160         GET_FLOAT(GL_TEXTURE_MIN_LOD);
161         GET_FLOAT(GL_TEXTURE_MAX_LOD);
162         GET_INT(GL_TEXTURE_WRAP_S);
163         GET_INT(GL_TEXTURE_WRAP_T);
164         GET_INT(GL_TEXTURE_WRAP_R);
165     }
166
167     GET_INT(GL_TEXTURE_SWIZZLE_R);
168     GET_INT(GL_TEXTURE_SWIZZLE_G);
169     GET_INT(GL_TEXTURE_SWIZZLE_B);
170     GET_INT(GL_TEXTURE_SWIZZLE_A);
171     GET_INT(GL_TEXTURE_SWIZZLE_RGBA);
172
173     if (!context_info.is_core_profile())
174     {
175         GET_INT(GL_GENERATE_MIPMAP);
176     }
177
178     GET_INT(GL_TEXTURE_IMMUTABLE_FORMAT);
179
180     if (context_info.supports_extension("GL_EXT_texture_filter_anisotropic") && (!is_target_multisampled))
181     {
182         GET_FLOAT(GL_TEXTURE_MAX_ANISOTROPY_EXT);
183     }
184
185     if (context_info.supports_extension("GL_EXT_texture_sRGB_decode") && (!is_target_multisampled))
186     {
187         GET_INT(GL_TEXTURE_SRGB_DECODE_EXT);
188     }
189
190     if (!context_info.is_core_profile() && context_info.supports_extension("GL_ARB_shadow_ambient"))
191     {
192         GET_FLOAT(GL_TEXTURE_COMPARE_FAIL_VALUE_ARB);
193     }
194
195     if (!context_info.is_core_profile())
196     {
197         GET_INT(GL_DEPTH_TEXTURE_MODE);
198
199         // TODO
200         //GL_TEXTURE_PRIORITY
201         //GL_TEXTURE_RESIDENT
202     }
203 #undef GET_INT
204 #undef GET_FLOAT
205
206     if (any_gl_errors)
207     {
208         vogl_error_printf("%s: GL error while enumerating texture %" PRIu64 " target %s's params\n", VOGL_METHOD_NAME, (uint64_t)handle, g_gl_enums.find_gl_name(m_target));
209         clear();
210         return false;
211     }
212
213     int base_level = m_params.get_value<int>(GL_TEXTURE_BASE_LEVEL, 0, 0);
214     if (base_level > 18)
215     {
216         vogl_error_printf("%s: Unsupported base level %u while enumerating texture %" PRIu64 " target %s\n", VOGL_METHOD_NAME, base_level, (uint64_t)handle, g_gl_enums.find_gl_name(m_target));
217         clear();
218         return false;
219     }
220
221     if (base_level > 0)
222     {
223         vogl_debug_printf("%s: Base level is non-zero (%u) while enumerating texture %" PRIu64 " target %s\n", VOGL_METHOD_NAME, base_level, (uint64_t)handle, g_gl_enums.find_gl_name(m_target));
224     }
225
226     int max_level = m_params.get_value<int>(GL_TEXTURE_MAX_LEVEL, 0, 1000);
227
228     const GLenum target_to_query = (m_target == GL_TEXTURE_CUBE_MAP) ? GL_TEXTURE_CUBE_MAP_POSITIVE_X : m_target;
229
230     const int max_supported_mip_levels = vogl_get_max_supported_mip_levels();
231
232     // Now try to find the highest actually defined mip level before we go any further.
233     int trial_level;
234     for (trial_level = max_supported_mip_levels - 1; trial_level >= 0; trial_level--)
235     {
236         GLenum level_internal_fmt = 0;
237         GL_ENTRYPOINT(glGetTexLevelParameteriv)(target_to_query, trial_level, GL_TEXTURE_INTERNAL_FORMAT, reinterpret_cast<GLint *>(&level_internal_fmt));
238
239         // Super paranoid here because every driver seems to handle this crap slightly differently and apps lie
240         bool is_valid = true;
241
242         if (vogl_check_gl_error())
243             is_valid = false;
244
245         if (is_valid)
246         {
247             // Needed for ACTC's 3D textures, not sure why yet
248             GLint level_width = 0;
249             GL_ENTRYPOINT(glGetTexLevelParameteriv)(target_to_query, trial_level, GL_TEXTURE_WIDTH, &level_width);
250             if (vogl_check_gl_error())
251                 is_valid = false;
252             else if (!level_width)
253                 is_valid = false;
254         }
255
256         if (is_valid)
257             break;
258     }
259
260     if (trial_level < 0)
261     {
262         // Texture was genned and bound to a target yet, but we couldn't find any valid levels.
263         m_is_unquerable = true;
264         m_is_valid = true;
265
266         return true;
267     }
268
269     uint num_actual_mip_levels = trial_level + 1;
270
271     // Try to query the base level
272     if (num_actual_mip_levels != (static_cast<uint>(max_level) + 1U))
273     {
274         vogl_warning_printf("%s: Texture's GL_TEXTURE_MAX_LEVEL is %u, but the max defined mip level is %u. Note GL may not act consistently with this texture object, GL texture %" PRIu64 " target %s\n", VOGL_METHOD_NAME,
275                               max_level, trial_level,
276                               (uint64_t)handle, g_gl_enums.find_gl_name(m_target));
277     }
278
279     GLenum internal_fmt = 0;
280     GL_ENTRYPOINT(glGetTexLevelParameteriv)(target_to_query, base_level, GL_TEXTURE_INTERNAL_FORMAT, reinterpret_cast<GLint *>(&internal_fmt));
281     if (vogl_check_gl_error())
282     {
283         vogl_error_printf("%s: GL error while enumerating texture %" PRIu64 " target %s's base level GL_TEXTURE_INTERNAL_FORMAT\n", VOGL_METHOD_NAME, static_cast<uint64_t>(handle), g_gl_enums.find_gl_name(m_target));
284         clear();
285         return false;
286     }
287
288     const vogl_internal_tex_format *pInternal_tex_fmt = vogl_find_internal_texture_format(internal_fmt);
289     if (!pInternal_tex_fmt)
290     {
291         vogl_error_printf("%s: Unknown texture format 0x%04X (%s) while snapshotting texture %" PRIu64 " target %s\n", VOGL_METHOD_NAME,
292                          internal_fmt, g_gl_enums.find_gl_image_format_name(internal_fmt), static_cast<uint64_t>(handle), g_gl_enums.find_gl_name(m_target));
293         clear();
294         return false;
295     }
296
297     if ((pInternal_tex_fmt->m_optimum_get_image_fmt == GL_NONE) || (pInternal_tex_fmt->m_optimum_get_image_type == GL_NONE))
298     {
299         vogl_warning_printf("%s: Don't know how to retrieve texture data of internal format 0x%04X (%s) while snapshotting texture %" PRIu64 " target %s\n", VOGL_METHOD_NAME,
300                            internal_fmt, g_gl_enums.find_gl_image_format_name(internal_fmt), static_cast<uint64_t>(handle), g_gl_enums.find_gl_name(m_target));
301     }
302
303     // Note: Mips below the base_level may or may not actually exist, AND the app can dynamically manipulate the base level so we need to try and save everything we can.
304     int base_width = 0, base_height = 0, base_depth = 0;
305     GL_ENTRYPOINT(glGetTexLevelParameteriv)(target_to_query, base_level, GL_TEXTURE_WIDTH, &base_width);
306     GL_ENTRYPOINT(glGetTexLevelParameteriv)(target_to_query, base_level, GL_TEXTURE_HEIGHT, &base_height);
307     GL_ENTRYPOINT(glGetTexLevelParameteriv)(target_to_query, base_level, GL_TEXTURE_DEPTH, &base_depth);
308     VOGL_CHECK_GL_ERROR;
309
310     if ((base_width < 1) || (base_height < 1) || (base_depth < 1))
311     {
312         vogl_warning_printf("%s: Couldn't retrieve base level's width, height and/or depth of GL texture %" PRIu64 " target %s internal format 0x%04X (%s)\n", VOGL_METHOD_NAME,
313                            static_cast<uint64_t>(handle), g_gl_enums.find_gl_name(m_target), internal_fmt, g_gl_enums.find_gl_image_format_name(internal_fmt));
314
315         // Texture has been deleted but it remained bound (but at least in source1 no shaders actually tried to read it), but we can't query anything about it on NV so we're kinda screwed.
316         m_is_unquerable = true;
317         m_is_valid = true;
318         return true;
319     }
320
321     m_num_samples = 1;
322     GL_ENTRYPOINT(glGetTexLevelParameteriv)(target_to_query, base_level, GL_TEXTURE_SAMPLES, reinterpret_cast<GLint *>(&m_num_samples));
323     VOGL_CHECK_GL_ERROR;
324     m_num_samples = math::maximum(m_num_samples, 1U);
325
326     if (m_num_samples > 1)
327     {
328         if (m_num_samples > cMaxSamples)
329         {
330             vogl_error_printf("%s: Unsupported number of samples (%u) while snapshotting texture %" PRIu64 " target %s\n", VOGL_METHOD_NAME,
331                              m_num_samples, static_cast<uint64_t>(handle), g_gl_enums.find_gl_name(m_target));
332             clear();
333             return false;
334         }
335         else if ((target != GL_TEXTURE_2D_MULTISAMPLE) && (target != GL_TEXTURE_2D_MULTISAMPLE_ARRAY))
336         {
337             vogl_error_printf("%s: Unexpected number of samples (%u) while snapshotting texture %" PRIu64 " target %s\n", VOGL_METHOD_NAME,
338                              m_num_samples, static_cast<uint64_t>(handle), g_gl_enums.find_gl_name(m_target));
339             clear();
340             return false;
341         }
342     }
343
344     uint width = base_width << base_level;
345     uint height = 1;
346     uint depth = 1;
347
348     switch (target)
349     {
350         case GL_TEXTURE_2D:
351         case GL_TEXTURE_2D_ARRAY:
352         case GL_TEXTURE_2D_MULTISAMPLE:
353         case GL_TEXTURE_2D_MULTISAMPLE_ARRAY:
354         {
355             height = base_height << base_level;
356             break;
357         }
358         case GL_TEXTURE_CUBE_MAP:
359         {
360             height = width;
361             break;
362         }
363         case GL_TEXTURE_3D:
364         {
365             height = base_height << base_level;
366             depth = base_depth << base_level;
367             break;
368         }
369         case GL_TEXTURE_RECTANGLE:
370         {
371             VOGL_VERIFY(!base_level);
372             height = base_height << base_level;
373             break;
374         }
375         default:
376             break;
377     }
378
379     //uint max_possible_mip_levels = (m_target == GL_TEXTURE_RECTANGLE) ? 1 : utils::compute_max_mips(width, height, depth);
380
381     GLenum image_fmt = pInternal_tex_fmt->m_optimum_get_image_fmt;
382     GLenum image_type = pInternal_tex_fmt->m_optimum_get_image_type;
383
384     uint num_faces = 1;
385
386     GLenum ktx_image_fmt = GL_NONE, ktx_image_type = GL_NONE;
387     if (!pInternal_tex_fmt->m_compressed)
388     {
389         ktx_image_fmt = image_fmt;
390         ktx_image_type = image_type;
391     }
392
393     GLenum ktx_tex_target = m_target;
394
395     if (m_target == GL_TEXTURE_1D)
396     {
397         if (!m_textures[0].init_1D(width, num_actual_mip_levels, internal_fmt, ktx_image_fmt, ktx_image_type))
398         {
399             vogl_error_printf("%s: Failed initializing KTX texture object for texture %" PRIu64 " target %s", VOGL_METHOD_NAME, (uint64_t)handle, g_gl_enums.find_gl_name(m_target));
400             clear();
401             return false;
402         }
403     }
404     else if ((m_target == GL_TEXTURE_2D) || (m_target == GL_TEXTURE_RECTANGLE))
405     {
406         if (!m_textures[0].init_2D(width, height, num_actual_mip_levels, internal_fmt, ktx_image_fmt, ktx_image_type))
407         {
408             vogl_error_printf("%s: Failed initializing KTX texture object for texture %" PRIu64 " target %s", VOGL_METHOD_NAME, (uint64_t)handle, g_gl_enums.find_gl_name(m_target));
409             clear();
410             return false;
411         }
412     }
413     else if (m_target == GL_TEXTURE_CUBE_MAP)
414     {
415         if (width != height)
416         {
417             vogl_error_printf("%s: Unsupported cubemap dimensions (%ux%u) for texture %" PRIu64 " target %s", VOGL_METHOD_NAME, width, height, (uint64_t)handle, g_gl_enums.find_gl_name(m_target));
418             clear();
419             return false;
420         }
421
422         if (!m_textures[0].init_cubemap(width, num_actual_mip_levels, internal_fmt, ktx_image_fmt, ktx_image_type))
423         {
424             vogl_error_printf("%s: Failed initializing KTX texture object for texture %" PRIu64 " target %s", VOGL_METHOD_NAME, (uint64_t)handle, g_gl_enums.find_gl_name(m_target));
425             clear();
426             return false;
427         }
428
429         num_faces = cCubeMapFaces;
430     }
431     else if (m_target == GL_TEXTURE_3D)
432     {
433         if (!m_textures[0].init_3D(width, height, depth, num_actual_mip_levels, internal_fmt, ktx_image_fmt, ktx_image_type))
434         {
435             vogl_error_printf("%s: Failed initializing KTX texture object for texture %" PRIu64 " target %s", VOGL_METHOD_NAME, (uint64_t)handle, g_gl_enums.find_gl_name(m_target));
436             clear();
437             return false;
438         }
439     }
440     else if (m_target == GL_TEXTURE_1D_ARRAY)
441     {
442         if (!m_textures[0].init_1D_array(width, num_actual_mip_levels, base_height, internal_fmt, ktx_image_fmt, ktx_image_type))
443         {
444             vogl_error_printf("%s: Failed initializing KTX texture object for texture %" PRIu64 " target %s", VOGL_METHOD_NAME, (uint64_t)handle, g_gl_enums.find_gl_name(m_target));
445             clear();
446             return false;
447         }
448     }
449     else if (m_target == GL_TEXTURE_2D_ARRAY)
450     {
451         if (!m_textures[0].init_2D_array(width, height, num_actual_mip_levels, base_depth, internal_fmt, ktx_image_fmt, ktx_image_type))
452         {
453             vogl_error_printf("%s: Failed initializing KTX texture object for texture %" PRIu64 " target %s", VOGL_METHOD_NAME, (uint64_t)handle, g_gl_enums.find_gl_name(m_target));
454             clear();
455             return false;
456         }
457     }
458     else if (m_target == GL_TEXTURE_2D_MULTISAMPLE)
459     {
460         ktx_tex_target = GL_TEXTURE_2D;
461
462         for (uint sample_index = 0; sample_index < m_num_samples; sample_index++)
463         {
464             if (!m_textures[sample_index].init_2D(width, height, num_actual_mip_levels, internal_fmt, ktx_image_fmt, ktx_image_type))
465             {
466                 vogl_error_printf("%s: Failed initializing KTX texture object for texture %" PRIu64 " target %s", VOGL_METHOD_NAME, (uint64_t)handle, g_gl_enums.find_gl_name(m_target));
467                 clear();
468                 return false;
469             }
470         }
471     }
472     else if (m_target == GL_TEXTURE_2D_MULTISAMPLE_ARRAY)
473     {
474         ktx_tex_target = GL_TEXTURE_2D_ARRAY;
475
476         for (uint sample_index = 0; sample_index < m_num_samples; sample_index++)
477         {
478             if (!m_textures[sample_index].init_2D_array(width, height, num_actual_mip_levels, base_depth, internal_fmt, ktx_image_fmt, ktx_image_type))
479             {
480                 vogl_error_printf("%s: Failed initializing KTX texture object for texture %" PRIu64 " target %s", VOGL_METHOD_NAME, (uint64_t)handle, g_gl_enums.find_gl_name(m_target));
481                 clear();
482                 return false;
483             }
484         }
485     }
486     else
487     {
488         // TODO
489         vogl_error_printf("%s: Unsupported target, texture %" PRIu64 " target %s", VOGL_METHOD_NAME, (uint64_t)handle, g_gl_enums.find_gl_name(m_target));
490         clear();
491         return false;
492     }
493
494     VOGL_VERIFY(m_textures[0].get_ogl_internal_fmt() == internal_fmt);
495     if (!pInternal_tex_fmt->m_compressed)
496     {
497         VOGL_VERIFY(m_textures[0].get_ogl_fmt() == image_fmt);
498         VOGL_VERIFY(m_textures[0].get_ogl_type() == image_type);
499     }
500
501     // We can't directly get the data of multisampled textures. Instead, copy the MSAA data into X separate non-MSAA textures.
502     vogl::vector<GLuint> split_texture_handles;
503     vogl::vector<GLuint> split_stencil_texture_handles;
504
505 #define VOGL_FREE_SPLIT_TEXTURES \
506     if (split_texture_handles.size()) { GL_ENTRYPOINT(glDeleteTextures)(split_texture_handles.size(), split_texture_handles.get_ptr()); VOGL_CHECK_GL_ERROR; } \
507     if (split_stencil_texture_handles.size()) { GL_ENTRYPOINT(glDeleteTextures)(split_stencil_texture_handles.size(), split_stencil_texture_handles.get_ptr()); VOGL_CHECK_GL_ERROR; }
508
509     if ((m_target == GL_TEXTURE_2D_MULTISAMPLE) || (m_target == GL_TEXTURE_2D_MULTISAMPLE_ARRAY))
510     {
511         // Note: This temporarily switches GL contexts!
512         vogl_msaa_texture_splitter splitter;
513
514         if (!splitter.init())
515         {
516             vogl_error_printf("%s: Failed initializing multisample texture splitter while snapshotting texture %" PRIu64 " target %s\n", VOGL_METHOD_NAME, (uint64_t)handle, g_gl_enums.find_gl_name(m_target));
517             clear();
518             return false;
519         }
520
521         if ( (pInternal_tex_fmt->m_comp_sizes[cTCDepth]) ||
522              (pInternal_tex_fmt->m_comp_sizes[cTCRed]) || (pInternal_tex_fmt->m_comp_sizes[cTCGreen]) || (pInternal_tex_fmt->m_comp_sizes[cTCBlue]) || (pInternal_tex_fmt->m_comp_sizes[cTCAlpha]) ||
523              (pInternal_tex_fmt->m_comp_sizes[cTCIntensity]) || (pInternal_tex_fmt->m_comp_sizes[cTCLuminance]) )
524         {
525             if (!splitter.split(target, static_cast<GLuint>(handle), split_texture_handles))
526             {
527                 vogl_error_printf("%s: Failed splitting multisample texture %" PRIu64 " target %s\n", VOGL_METHOD_NAME, (uint64_t)handle, g_gl_enums.find_gl_name(m_target));
528                 clear();
529                 return false;
530             }
531         }
532
533         if (pInternal_tex_fmt->m_comp_sizes[cTCStencil])
534         {
535             GLuint temp_msaa_color_tex = 0;
536             if (!splitter.copy_stencil_samples_to_color(target, static_cast<GLuint>(handle), temp_msaa_color_tex))
537             {
538                 vogl_error_printf("%s: Failed splitting multisample texture %" PRIu64 " target %s\n", VOGL_METHOD_NAME, (uint64_t)handle, g_gl_enums.find_gl_name(m_target));
539                 clear();
540                 return false;
541             }
542
543             bool status = splitter.split(target, temp_msaa_color_tex, split_stencil_texture_handles);
544
545             GL_ENTRYPOINT(glDeleteTextures)(1, &temp_msaa_color_tex);
546             VOGL_CHECK_GL_ERROR;
547
548             temp_msaa_color_tex = 0;
549
550             if (!status)
551             {
552                 vogl_error_printf("%s: Failed splitting multisample texture %" PRIu64 " target %s\n", VOGL_METHOD_NAME, (uint64_t)handle, g_gl_enums.find_gl_name(m_target));
553                 clear();
554                 return false;
555             }
556         }
557     }
558
559     for (uint face = 0; face < num_faces; face++)
560     {
561         GLenum face_target_to_query = m_target;
562         if (m_target == GL_TEXTURE_CUBE_MAP)
563             face_target_to_query = GL_TEXTURE_CUBE_MAP_POSITIVE_X + face;
564
565         m_level_params[face].resize(num_actual_mip_levels);
566
567         for (uint level = 0; level < num_actual_mip_levels; level++)
568         {
569             GLenum level_internal_fmt = 0;
570             GL_ENTRYPOINT(glGetTexLevelParameteriv)(target_to_query, level, GL_TEXTURE_INTERNAL_FORMAT, reinterpret_cast<GLint *>(&level_internal_fmt));
571
572             bool is_valid = true;
573             if (vogl_check_gl_error())
574                 is_valid = false;
575             else if (level_internal_fmt != internal_fmt)
576                 is_valid = false;
577
578             if (is_valid)
579             {
580                 // Needed for ACTC's 3D textures, not sure why yet
581                 GLint level_width = 0;
582                 GL_ENTRYPOINT(glGetTexLevelParameteriv)(target_to_query, level, GL_TEXTURE_WIDTH, &level_width);
583                 if (vogl_check_gl_error())
584                     is_valid = false;
585                 else if (!level_width)
586                     is_valid = false;
587                 else if (level_width != math::maximum<int>(width >> level, 1))
588                 {
589                     VOGL_ASSERT_ALWAYS;
590                     is_valid = false;
591                 }
592             }
593
594             if (!is_valid)
595                 continue;
596
597             vogl_state_vector level_params;
598
599             bool any_gl_errors = false;
600
601 // TODO: Check for core vs. compat profiles and not query the old stuff
602 #define GET_INT(gl_enum)                                                                        \
603     do                                                                                          \
604     {                                                                                           \
605         int values[4] = { 0, 0, 0, 0 };                                                         \
606         GL_ENTRYPOINT(glGetTexLevelParameteriv)(face_target_to_query, level, gl_enum, values);  \
607         if (vogl_check_gl_error())                                                              \
608             any_gl_errors = true;                                                               \
609         level_params.insert(gl_enum, 0, values, sizeof(values[0]));                             \
610     } while (0)
611
612             GET_INT(GL_TEXTURE_WIDTH);
613             GET_INT(GL_TEXTURE_HEIGHT);
614             GET_INT(GL_TEXTURE_DEPTH);
615             GET_INT(GL_TEXTURE_INTERNAL_FORMAT);
616             if (any_gl_errors)
617             {
618                 vogl_error_printf("%s: Failed retrieving face %u level %u's internal format and/or width while enumerating texture %" PRIu64 " target %s's level params\n", VOGL_METHOD_NAME, face, level, (uint64_t)handle, g_gl_enums.find_gl_name(m_target));
619                 clear();
620                 VOGL_FREE_SPLIT_TEXTURES
621                 return false;
622             }
623
624             GET_INT(GL_TEXTURE_SAMPLES);
625             GET_INT(GL_TEXTURE_FIXED_SAMPLE_LOCATIONS);
626
627             GET_INT(GL_TEXTURE_RED_SIZE);
628             GET_INT(GL_TEXTURE_GREEN_SIZE);
629             GET_INT(GL_TEXTURE_BLUE_SIZE);
630             GET_INT(GL_TEXTURE_ALPHA_SIZE);
631             GET_INT(GL_TEXTURE_DEPTH_SIZE);
632             GET_INT(GL_TEXTURE_STENCIL_SIZE);
633             GET_INT(GL_TEXTURE_LUMINANCE_SIZE);
634             GET_INT(GL_TEXTURE_INTENSITY_SIZE);
635             GET_INT(GL_TEXTURE_SHARED_SIZE);
636             GET_INT(GL_TEXTURE_COMPRESSED);
637
638             if (context_info.supports_extension("GL_ARB_depth_texture"))
639             {
640                 GET_INT(GL_TEXTURE_DEPTH_SIZE);
641                 GET_INT(GL_TEXTURE_DEPTH_TYPE);
642             }
643
644             if (context_info.supports_extension("GL_EXT_packed_depth_stencil"))
645                 GET_INT(GL_TEXTURE_STENCIL_SIZE_EXT);
646
647             if (m_target == GL_TEXTURE_BUFFER)
648             {
649                 GET_INT(GL_TEXTURE_BUFFER_DATA_STORE_BINDING);
650                 GET_INT(GL_TEXTURE_BUFFER_OFFSET);
651                 GET_INT(GL_TEXTURE_BUFFER_SIZE);
652             }
653
654             bool is_compressed = level_params.get_value<bool>(GL_TEXTURE_COMPRESSED);
655             if (!is_compressed)
656             {
657                 int value = 0;
658                 level_params.insert(GL_TEXTURE_COMPRESSED_IMAGE_SIZE, 0, &value, sizeof(value));
659             }
660             else
661             {
662                 GET_INT(GL_TEXTURE_COMPRESSED_IMAGE_SIZE);
663             }
664 #undef GET_INT
665
666             if (any_gl_errors)
667             {
668                 vogl_warning_printf("%s: One or more GL errors while enumerating texture %" PRIu64 " target %s's level params\n", VOGL_METHOD_NAME, (uint64_t)handle, g_gl_enums.find_gl_name(m_target));
669             }
670
671             if ((level_params.get_value<int>(GL_TEXTURE_WIDTH) == 0) ||
672                 (level_params.get_value<int>(GL_TEXTURE_HEIGHT) == 0) ||
673                 (level_params.get_value<int>(GL_TEXTURE_DEPTH) == 0) ||
674                 (level_params.get_value<GLenum>(GL_TEXTURE_INTERNAL_FORMAT) != internal_fmt))
675             {
676                 vogl_error_printf("%s: Failed retrieving level %u's parameters while enumerating texture %" PRIu64 " target %s's level params\n", VOGL_METHOD_NAME, level, (uint64_t)handle, g_gl_enums.find_gl_name(m_target));
677                 clear();
678                 VOGL_FREE_SPLIT_TEXTURES
679                 return false;
680             }
681
682             size_t size_in_bytes = level_params.get_value<uint>(GL_TEXTURE_COMPRESSED_IMAGE_SIZE);
683             if (pInternal_tex_fmt->m_compressed)
684             {
685                 if (!size_in_bytes)
686                 {
687                     vogl_error_printf("%s: Failed retrieving level %u's compressed size while enumerating texture %" PRIu64 " target %s's level params\n", VOGL_METHOD_NAME, level, (uint64_t)handle, g_gl_enums.find_gl_name(m_target));
688                     clear();
689                     VOGL_FREE_SPLIT_TEXTURES
690                     return false;
691                 }
692             }
693             else
694             {
695                 if (size_in_bytes)
696                 {
697                     vogl_error_printf("%s: Unexpected compressed size on level %u's while enumerating texture %" PRIu64 " target %s's level params\n", VOGL_METHOD_NAME, level, (uint64_t)handle, g_gl_enums.find_gl_name(m_target));
698                     clear();
699                     VOGL_FREE_SPLIT_TEXTURES
700                     return false;
701                 }
702             }
703
704             m_level_params[face][level] = level_params;
705         }
706     }
707
708     VOGL_CHECK_GL_ERROR;
709
710     uint8_vec temp_img;
711
712     // Now grab the data from each face, mipmap level, and slice/layer and supply it to the KTX texture object.
713     for (uint face = 0; face < num_faces; face++)
714     {
715         GLenum face_target_to_query = m_target;
716         if (m_target == GL_TEXTURE_CUBE_MAP)
717             face_target_to_query = GL_TEXTURE_CUBE_MAP_POSITIVE_X + face;
718
719         // Query available mip levels and add them to the texture.
720         for (uint level = 0; level < num_actual_mip_levels; level++)
721         {
722             const vogl_state_vector &level_params = m_level_params[face][level];
723
724             if (!level_params.find(GL_TEXTURE_WIDTH))
725             {
726                 for (uint sample_index = 0; sample_index < m_num_samples; sample_index++)
727                 {
728                     // Insert placeholder images for the missing texture levels
729                     int image_size = m_textures[sample_index].get_expected_image_size(level);
730                     temp_img.resize(image_size);
731
732                     if ((ktx_tex_target == GL_TEXTURE_1D_ARRAY) || (ktx_tex_target == GL_TEXTURE_2D_ARRAY))
733                     {
734                         VOGL_ASSERT(base_depth);
735                         uint num_array_elements = base_depth;
736
737                         if (ktx_tex_target == GL_TEXTURE_1D_ARRAY)
738                         {
739                             VOGL_ASSERT(base_height);
740                             num_array_elements = base_height;
741                         }
742
743                         for (uint array_index = 0; array_index < num_array_elements; array_index++)
744                         {
745                             m_textures[sample_index].add_image(level, array_index, face, 0, temp_img.get_ptr(), image_size);
746                         }
747                     }
748                     else
749                     {
750                         int level_depth = (target == GL_TEXTURE_3D) ? math::maximum<int>(1, depth >> level) : 1;
751
752                         for (int zslice = 0; zslice < level_depth; zslice++)
753                         {
754                             m_textures[sample_index].add_image(level, 0, face, zslice, temp_img.get_ptr(), temp_img.size());
755                         }
756                     }
757                 } // sample_index
758             }
759             else
760             {
761                 int level_width = level_params.get_value<int>(GL_TEXTURE_WIDTH);
762                 int level_height = level_params.get_value<int>(GL_TEXTURE_HEIGHT);
763                 int level_depth = level_params.get_value<int>(GL_TEXTURE_DEPTH);
764
765                 int size_in_bytes = level_params.get_value<uint>(GL_TEXTURE_COMPRESSED_IMAGE_SIZE);
766
767                 if (!pInternal_tex_fmt->m_compressed)
768                 {
769                     VOGL_ASSERT(!size_in_bytes);
770
771                     size_t size_in_bytes64 = vogl_get_image_size(pInternal_tex_fmt->m_optimum_get_image_fmt, pInternal_tex_fmt->m_optimum_get_image_type, level_width, level_height, level_depth);
772                     if (!size_in_bytes64)
773                     {
774                         vogl_error_printf("%s: Failed computing image size of face %u level %u, texture %" PRIu64 " target %s\n", VOGL_METHOD_NAME, face, level, (uint64_t)handle, g_gl_enums.find_gl_name(m_target));
775                         clear();
776                         VOGL_FREE_SPLIT_TEXTURES
777                         return false;
778                     }
779
780                     if (size_in_bytes64 > static_cast<size_t>(cINT32_MAX))
781                     {
782                         vogl_error_printf("%s: Image size too large for face %u level %u, texture %" PRIu64 " target %s\n", VOGL_METHOD_NAME, face, level, (uint64_t)handle, g_gl_enums.find_gl_name(m_target));
783                         clear();
784                         VOGL_FREE_SPLIT_TEXTURES
785                         return false;
786                     }
787
788                     size_in_bytes = static_cast<int>(size_in_bytes64);
789                 }
790
791                 for (uint sample_index = 0; sample_index < m_num_samples; sample_index++)
792                 {
793                     GLenum get_target = face_target_to_query;
794
795                     if ((m_target == GL_TEXTURE_2D_MULTISAMPLE) || (m_target == GL_TEXTURE_2D_MULTISAMPLE_ARRAY))
796                     {
797                         GL_ENTRYPOINT(glBindTexture)(ktx_tex_target, split_texture_handles[sample_index]);
798                         VOGL_CHECK_GL_ERROR;
799
800                         get_target = ktx_tex_target;
801                     }
802
803                     const uint num_guard_bytes = 2;
804                     if (!temp_img.try_resize(size_in_bytes + num_guard_bytes))
805                     {
806                         vogl_error_printf("%s: Out of memory while trying to retrieve texture data, texture %" PRIu64 " target %s\n", VOGL_METHOD_NAME, (uint64_t)handle, g_gl_enums.find_gl_name(m_target));
807                         clear();
808                         VOGL_FREE_SPLIT_TEXTURES
809                         return false;
810                     }
811
812                     // Write a pattern after the buffer to detect buffer size computation screwups.
813                     if (size_in_bytes >= 4)
814                     {
815                         temp_img[size_in_bytes - 4] = 0x67;
816                         temp_img[size_in_bytes - 3] = 0xCC;
817                         temp_img[size_in_bytes - 2] = 0xD4;
818                         temp_img[size_in_bytes - 1] = 0xF9;
819                     }
820
821                     temp_img[size_in_bytes] = 0xDE;
822                     temp_img[size_in_bytes + 1] = 0xAD;
823
824                     if (pInternal_tex_fmt->m_compressed)
825                     {
826                         GL_ENTRYPOINT(glGetCompressedTexImage)(get_target, level, temp_img.get_ptr());
827                     }
828                     else
829                     {
830                         GL_ENTRYPOINT(glGetTexImage)(get_target, level, pInternal_tex_fmt->m_optimum_get_image_fmt, pInternal_tex_fmt->m_optimum_get_image_type, temp_img.get_ptr());
831                     }
832
833                     if (vogl_check_gl_error())
834                     {
835                         vogl_error_printf("%s: Failed retrieving image data for face %u level %u, texture %" PRIu64 " target %s\n", VOGL_METHOD_NAME, face, level, (uint64_t)handle, g_gl_enums.find_gl_name(m_target));
836                         clear();
837                         VOGL_FREE_SPLIT_TEXTURES
838                         return false;
839                     }
840
841                     if (size_in_bytes >= 4)
842                     {
843                         if ((temp_img[size_in_bytes - 4] == 0x67) && (temp_img[size_in_bytes - 3] == 0xCC) &&
844                             (temp_img[size_in_bytes - 2] == 0xD4) && (temp_img[size_in_bytes - 1] == 0xF9))
845                         {
846                             vogl_error_printf("%s: Image data retrieval may have failed for face %u level %u, texture %" PRIu64 " target %s\n", VOGL_METHOD_NAME, face, level, (uint64_t)handle, g_gl_enums.find_gl_name(m_target));
847                         }
848                     }
849
850                     VOGL_VERIFY((temp_img[size_in_bytes] == 0xDE) && (temp_img[size_in_bytes + 1] == 0xAD));
851
852                     temp_img.try_resize(size_in_bytes);
853
854                     if ((m_target == GL_TEXTURE_2D_MULTISAMPLE) || (m_target == GL_TEXTURE_2D_MULTISAMPLE_ARRAY))
855                     {
856                         if (split_stencil_texture_handles.size())
857                         {
858                             size_t split_color_size_in_bytes64 = vogl_get_image_size(GL_RGBA, GL_UNSIGNED_BYTE, level_width, level_height, level_depth);
859
860                             if (!split_color_size_in_bytes64)
861                             {
862                                 vogl_error_printf("%s: Failed computing image size of face %u level %u, texture %" PRIu64 " target %s\n", VOGL_METHOD_NAME, face, level, (uint64_t)handle, g_gl_enums.find_gl_name(m_target));
863                                 clear();
864                                 VOGL_FREE_SPLIT_TEXTURES
865                                 return false;
866                             }
867
868                             if (split_color_size_in_bytes64 > static_cast<size_t>(cINT32_MAX))
869                             {
870                                 vogl_error_printf("%s: Image size too large for face %u level %u, texture %" PRIu64 " target %s\n", VOGL_METHOD_NAME, face, level, (uint64_t)handle, g_gl_enums.find_gl_name(m_target));
871                                 clear();
872                                 VOGL_FREE_SPLIT_TEXTURES
873                                 return false;
874                             }
875
876                             uint8_vec stencil_image_data(static_cast<uint>(split_color_size_in_bytes64));
877
878                             GL_ENTRYPOINT(glBindTexture)(ktx_tex_target, split_stencil_texture_handles[sample_index]);
879                             VOGL_CHECK_GL_ERROR;
880
881                             GL_ENTRYPOINT(glGetTexImage)(ktx_tex_target, level, GL_RGBA, GL_UNSIGNED_BYTE, stencil_image_data.get_ptr());
882                             VOGL_CHECK_GL_ERROR;
883
884                             switch (internal_fmt)
885                             {
886                                 case GL_DEPTH_STENCIL:          // GL_UNSIGNED_INT_24_8
887                                 case GL_DEPTH24_STENCIL8:       // GL_UNSIGNED_INT_24_8
888                                 {
889                                     for (uint y = 0; y < height; y++)
890                                     {
891                                         for (uint x = 0; x < width; x++)
892                                         {
893                                             uint ofs = (x * sizeof(uint32)) + (y * width * sizeof(uint32));
894                                             // I'm paranoid
895                                             if ((ofs < stencil_image_data.size()) && (ofs < temp_img.size()))
896                                             {
897                                                 uint8 *pSrc = stencil_image_data.get_ptr() + ofs;
898                                                 uint8 *pDest = temp_img.get_ptr() + ofs;
899
900                                                 pDest[0] = pSrc[0];
901                                             }
902                                         }
903                                     }
904                                     break;
905                                 }
906                                 case GL_DEPTH32F_STENCIL8:      // GL_FLOAT_32_UNSIGNED_INT_24_8_REV
907                                 case GL_DEPTH32F_STENCIL8_NV:   // GL_FLOAT_32_UNSIGNED_INT_24_8_REV
908                                 {
909                                     for (uint y = 0; y < height; y++)
910                                     {
911                                         for (uint x = 0; x < width; x++)
912                                         {
913                                             uint ofs = (x * sizeof(uint32)) + (y * width * sizeof(uint32));
914                                             // I'm paranoid
915                                             if ((ofs < stencil_image_data.size()) && ((ofs + 3) < temp_img.size()))
916                                             {
917                                                 uint8 *pSrc = stencil_image_data.get_ptr() + ofs;
918                                                 uint8 *pDest = temp_img.get_ptr() + ofs;
919
920                                                 pDest[3] = pSrc[0];
921                                             }
922                                         }
923                                     }
924
925                                     break;
926                                 }
927                                 default:
928                                 {
929                                     vogl_warning_printf("%s: Unable to set stencil data in texture %" PRIu64 "\n", VOGL_METHOD_NAME, (uint64_t)handle);
930                                     break;
931                                 }
932                             }
933
934                         }
935                     }
936
937                     if (ktx_tex_target == GL_TEXTURE_3D)
938                     {
939                         uint zslice_size = size_in_bytes;
940                         if (level_depth > 1)
941                         {
942                             VOGL_ASSERT((size_in_bytes % level_depth) == 0);
943                             zslice_size = size_in_bytes / level_depth;
944                             VOGL_ASSERT(zslice_size);
945                         }
946
947                         VOGL_ASSERT((size_in_bytes % zslice_size) == 0);
948
949                         uint cur_ofs = 0;
950                         for (int zslice = 0; zslice < level_depth; zslice++)
951                         {
952                             m_textures[sample_index].add_image(level, 0, face, zslice, temp_img.get_ptr() + cur_ofs, zslice_size);
953                             cur_ofs += zslice_size;
954                         }
955                         VOGL_ASSERT(static_cast<int>(cur_ofs) == size_in_bytes);
956                     }
957                     else if ((ktx_tex_target == GL_TEXTURE_1D_ARRAY) || (ktx_tex_target == GL_TEXTURE_2D_ARRAY))
958                     {
959                         VOGL_ASSERT(base_depth);
960                         uint num_array_elements = base_depth;
961
962                         if (ktx_tex_target == GL_TEXTURE_1D_ARRAY)
963                         {
964                             num_array_elements = base_height;
965                         }
966
967                         VOGL_ASSERT((size_in_bytes % num_array_elements) == 0);
968                         uint element_size = size_in_bytes / num_array_elements;
969                         VOGL_ASSERT(element_size);
970                         VOGL_ASSERT((size_in_bytes % element_size) == 0);
971
972                         uint cur_ofs = 0;
973                         for (uint array_index = 0; array_index < num_array_elements; array_index++)
974                         {
975                             m_textures[sample_index].add_image(level, array_index, face, 0, temp_img.get_ptr() + cur_ofs, element_size);
976                             cur_ofs += element_size;
977                         }
978                     }
979                     else
980                     {
981                         m_textures[sample_index].add_image_grant_ownership(level, 0, face, 0, temp_img);
982                     }
983
984                 } // sample_index
985
986             } // if
987
988         } // level
989     } // face
990
991     GL_ENTRYPOINT(glBindTexture)(target, m_snapshot_handle);
992     VOGL_CHECK_GL_ERROR;
993
994     // TODO: Add more key/values?
995     m_textures[0].add_key_value("VOGL_TARGET", dynamic_string(cVarArg, "%u", m_target).get_ptr());
996     m_textures[0].add_key_value("VOGL_BASE_LEVEL", dynamic_string(cVarArg, "%u", m_params.get_value<int>(GL_TEXTURE_BASE_LEVEL)).get_ptr());
997     m_textures[0].add_key_value("VOGL_MAX_LEVEL", dynamic_string(cVarArg, "%u", m_params.get_value<int>(GL_TEXTURE_MAX_LEVEL)).get_ptr());
998
999     bool x_flipped = false, y_flipped = true;
1000     dynamic_string ktx_orient_str(cVarArg, "S=%c,T=%c", x_flipped ? 'l' : 'r', y_flipped ? 'u' : 'd');
1001     m_textures[0].add_key_value("KTXorientation", ktx_orient_str.get_ptr());
1002
1003     if (!m_textures[0].consistency_check())
1004     {
1005         vogl_error_printf("%s: Internal error: KTX texture failed internal consistency check, texture %" PRIu64 " target %s\n", VOGL_METHOD_NAME, (uint64_t)handle, g_gl_enums.find_gl_name(m_target));
1006         clear();
1007         VOGL_FREE_SPLIT_TEXTURES
1008         return false;
1009     }
1010
1011     VOGL_FREE_SPLIT_TEXTURES
1012
1013     m_is_valid = true;
1014
1015     return true;
1016 }
1017
1018 bool vogl_texture_state::set_tex_parameter(GLenum pname) const
1019 {
1020     VOGL_FUNC_TRACER
1021
1022     const vogl_state_data *pData = m_params.find(pname);
1023     if (!pData)
1024     {
1025         // We only return false on a GL error.
1026         return true;
1027     }
1028
1029     enum
1030     {
1031         cMaxElements = 16
1032     };
1033
1034     if (pData->get_num_elements() > cMaxElements)
1035     {
1036         VOGL_ASSERT_ALWAYS;
1037         return false;
1038     }
1039
1040     if ((pData->get_data_type() == cSTFloat) || (pData->get_data_type() == cSTDouble))
1041     {
1042         float fvals[16];
1043         pData->get_float(fvals);
1044         if (pData->get_num_elements() == 1)
1045             GL_ENTRYPOINT(glTexParameterf)(m_target, pname, fvals[0]);
1046         else
1047             GL_ENTRYPOINT(glTexParameterfv)(m_target, pname, fvals);
1048     }
1049     else
1050     {
1051         int ivals[16];
1052         pData->get_int(ivals);
1053         if (pData->get_num_elements() == 1)
1054             GL_ENTRYPOINT(glTexParameteri)(m_target, pname, ivals[0]);
1055         else
1056             GL_ENTRYPOINT(glTexParameteriv)(m_target, pname, ivals);
1057     }
1058
1059     return !vogl_check_gl_error();
1060 }
1061
1062 // Note: We'll need the remapper for buffer textures.
1063 bool vogl_texture_state::restore(const vogl_context_info &context_info, vogl_handle_remapper &remapper, GLuint64 &handle) const
1064 {
1065     VOGL_FUNC_TRACER
1066
1067     if (!m_is_valid)
1068         return false;
1069
1070     VOGL_CHECK_GL_ERROR;
1071
1072     vogl_msaa_texture_splitter splitter;
1073
1074     vogl_scoped_binding_state orig_bindings(GL_PIXEL_PACK_BUFFER, GL_PIXEL_UNPACK_BUFFER);
1075     orig_bindings.save_textures();
1076
1077     vogl_scoped_state_saver pixeltransfer_state_saver(cGSTPixelStore);
1078
1079     if (!context_info.is_core_profile())
1080         pixeltransfer_state_saver.save(cGSTPixelTransfer);
1081
1082     vogl_reset_pixel_store_states();
1083
1084     if (!context_info.is_core_profile())
1085         vogl_reset_pixel_transfer_states();
1086
1087     GL_ENTRYPOINT(glBindBuffer)(GL_PIXEL_PACK_BUFFER, 0);
1088     VOGL_CHECK_GL_ERROR;
1089
1090     GL_ENTRYPOINT(glBindBuffer)(GL_PIXEL_UNPACK_BUFFER, 0);
1091     VOGL_CHECK_GL_ERROR;
1092
1093     bool created_handle = false;
1094
1095     if (!handle)
1096     {
1097         GLuint handle32 = 0;
1098         GL_ENTRYPOINT(glGenTextures)(1, &handle32);
1099         if ((vogl_check_gl_error()) || (!handle32))
1100             return false;
1101         handle = handle32;
1102
1103         remapper.declare_handle(VOGL_NAMESPACE_TEXTURES, m_snapshot_handle, handle, m_target);
1104         if (!remapper.is_default_remapper())
1105         {
1106             VOGL_ASSERT(remapper.remap_handle(VOGL_NAMESPACE_TEXTURES, m_snapshot_handle) == handle);
1107         }
1108
1109         created_handle = true;
1110     }
1111
1112     if (m_target == GL_NONE)
1113     {
1114         // Texture has not been bound to a target yet, so we're done.
1115         return true;
1116     }
1117
1118     uint face = 0, level = 0;
1119     uint8_vec temp_img;
1120
1121     uint tex_width = 0, tex_height = 0, tex_depth = 0, total_actual_levels = 0, num_faces = 0;
1122     int base_level = 0, max_level = 0;
1123     VOGL_NOTE_UNUSED(base_level);
1124     VOGL_NOTE_UNUSED(max_level);
1125     bool is_immutable_format = false, is_compressed = false;
1126     GLenum internal_fmt = GL_NONE;
1127
1128     const ktx_texture &tex0 = m_textures[0];
1129
1130     GL_ENTRYPOINT(glBindTexture)(m_target, static_cast<uint32>(handle));
1131     if (vogl_check_gl_error())
1132         goto handle_error;
1133
1134     if (m_target == GL_TEXTURE_BUFFER)
1135     {
1136         if (m_buffer)
1137         {
1138             GLuint buffer_handle = static_cast<GLuint>(remapper.remap_handle(VOGL_NAMESPACE_BUFFERS, m_buffer));
1139             if (!buffer_handle)
1140             {
1141                 vogl_error_printf("%s: Failed remapping buffer handle for buffer texture trace handle %u GL handle %" PRIu64 ", trace buffer %u GL buffer %u\n", VOGL_METHOD_NAME, m_snapshot_handle, static_cast<uint64_t>(handle), m_buffer, buffer_handle);
1142                 return false;
1143             }
1144
1145             internal_fmt = m_params.get_value<int>(GL_TEXTURE_INTERNAL_FORMAT);
1146
1147             if (!internal_fmt)
1148             {
1149                 vogl_error_printf("%s: Failed retrieving GL_TEXTURE_INTERNAL_FORMAT for buffer texture trace handle %u GL handle %" PRIu64 ", trace buffer %u GL buffer %u\n", VOGL_METHOD_NAME, m_snapshot_handle, static_cast<uint64_t>(handle), m_buffer, buffer_handle);
1150                 return false;
1151             }
1152
1153             GL_ENTRYPOINT(glTexBuffer)(m_target, internal_fmt, buffer_handle);
1154             VOGL_CHECK_GL_ERROR;
1155         }
1156
1157         return true;
1158     }
1159
1160     bool any_failures;
1161     any_failures = false;
1162
1163 #define SET_INT(pname)                 \
1164     do                                 \
1165     {                                  \
1166         if (!set_tex_parameter(pname)) \
1167             any_failures = true;       \
1168     } while (0)
1169 #define SET_FLOAT(pname)               \
1170     do                                 \
1171     {                                  \
1172         if (!set_tex_parameter(pname)) \
1173             any_failures = true;       \
1174     } while (0)
1175     SET_INT(GL_TEXTURE_BASE_LEVEL);
1176     SET_INT(GL_TEXTURE_MAX_LEVEL);
1177     SET_FLOAT(GL_TEXTURE_BORDER_COLOR);
1178     SET_INT(GL_TEXTURE_COMPARE_MODE);
1179     SET_INT(GL_TEXTURE_COMPARE_FUNC);
1180     SET_INT(GL_TEXTURE_MIN_FILTER);
1181     SET_INT(GL_TEXTURE_MAG_FILTER);
1182     if (m_target != GL_TEXTURE_RECTANGLE)
1183     {
1184         SET_FLOAT(GL_TEXTURE_LOD_BIAS);
1185         SET_FLOAT(GL_TEXTURE_MIN_LOD);
1186         SET_FLOAT(GL_TEXTURE_MAX_LOD);
1187     }
1188     SET_INT(GL_TEXTURE_SWIZZLE_RGBA);
1189     SET_INT(GL_TEXTURE_WRAP_S);
1190     SET_INT(GL_TEXTURE_WRAP_T);
1191     SET_INT(GL_TEXTURE_WRAP_R);
1192
1193     if (context_info.supports_extension("GL_EXT_texture_filter_anisotropic"))
1194     {
1195         SET_FLOAT(GL_TEXTURE_MAX_ANISOTROPY_EXT);
1196     }
1197
1198     if (context_info.supports_extension("GL_EXT_texture_sRGB_decode"))
1199     {
1200         SET_INT(GL_TEXTURE_SRGB_DECODE_EXT);
1201     }
1202
1203     if (!context_info.is_core_profile() && context_info.supports_extension("GL_ARB_shadow_ambient"))
1204     {
1205         SET_FLOAT(GL_TEXTURE_COMPARE_FAIL_VALUE_ARB);
1206     }
1207
1208     if (!context_info.is_core_profile())
1209     {
1210         SET_INT(GL_DEPTH_TEXTURE_MODE);
1211     }
1212
1213     // TODO?
1214     // GL_TEXTURE_PRIORITY
1215     // GL_TEXTURE_RESIDENT
1216
1217     if ((m_is_unquerable) || (!m_textures[0].is_valid()))
1218     {
1219         if (m_params.get_value<int>(GL_GENERATE_MIPMAP))
1220         {
1221             GL_ENTRYPOINT(glGenerateMipmap)(m_target);
1222         }
1223
1224         return true;
1225     }
1226
1227     if (m_num_samples < 1)
1228         return false;
1229
1230     tex_width = tex0.get_width();
1231     tex_height = tex0.get_height();
1232     tex_depth = tex0.get_depth();
1233     total_actual_levels = tex0.get_num_mips();
1234     base_level = m_params.get_value<int>(GL_TEXTURE_BASE_LEVEL);
1235     max_level = m_params.get_value<int>(GL_TEXTURE_MAX_LEVEL);
1236
1237     is_immutable_format = m_params.get_value<int>(GL_TEXTURE_IMMUTABLE_FORMAT) != 0;
1238     is_compressed = tex0.is_compressed();
1239     internal_fmt = tex0.get_ogl_internal_fmt();
1240
1241     num_faces = (m_target == GL_TEXTURE_CUBE_MAP) ? cCubeMapFaces : 1;
1242
1243     // Sanity checking
1244     for (uint sample_index = 1; sample_index < m_num_samples; sample_index++)
1245     {
1246         const ktx_texture &cmp_tex = m_textures[sample_index];
1247
1248         if ( (!cmp_tex.is_valid()) || (tex_width != cmp_tex.get_width()) || (tex_height != cmp_tex.get_height()) || (tex_depth != cmp_tex.get_depth()) ||
1249              (tex0.get_num_mips() != cmp_tex.get_num_mips()) || (tex0.get_array_size() != cmp_tex.get_array_size()) || (tex0.get_num_faces() != cmp_tex.get_num_faces()) ||
1250              (tex0.get_ogl_fmt() != cmp_tex.get_ogl_fmt()) || (tex0.get_ogl_type() != cmp_tex.get_ogl_type()) || (tex0.get_ogl_internal_fmt() != cmp_tex.get_ogl_internal_fmt()) )
1251         {
1252             vogl_error_printf("%s: MSAA consistency error\n", VOGL_METHOD_NAME);
1253
1254             VOGL_ASSERT_ALWAYS;
1255             goto handle_error;
1256         }
1257     }
1258
1259     // TODO: Support immutable textures, incomplete textures, etc. Lots more to do here.
1260     if (is_immutable_format)
1261     {
1262         vogl_warning_printf("%s: TODO: Support immutable textures\n", VOGL_METHOD_NAME);
1263     }
1264
1265     for (face = 0; face < num_faces; face++)
1266     {
1267         for (level = 0; level < total_actual_levels; level++)
1268         {
1269             const vogl_state_vector &level_params = m_level_params[face][level];
1270
1271             if (!level_params.find(GL_TEXTURE_WIDTH))
1272                 continue;
1273
1274             int level_width = level_params.get_value<int>(GL_TEXTURE_WIDTH);
1275             int level_height = level_params.get_value<int>(GL_TEXTURE_HEIGHT);
1276             int level_depth = level_params.get_value<int>(GL_TEXTURE_DEPTH);
1277             int level_samples = level_params.get_value<int>(GL_TEXTURE_SAMPLES);
1278
1279             GLenum level_internal_fmt = level_params.get_value<int>(GL_TEXTURE_INTERNAL_FORMAT);
1280
1281             if ((level_width < 1) || (level_height < 1) || (level_depth < 1) || (level_internal_fmt != internal_fmt))
1282             {
1283                 vogl_error_printf("%s: Consistency error\n", VOGL_METHOD_NAME);
1284                 VOGL_ASSERT_ALWAYS;
1285                 goto handle_error;
1286             }
1287
1288             if ((m_target == GL_TEXTURE_2D_MULTISAMPLE) || (m_target == GL_TEXTURE_2D_MULTISAMPLE_ARRAY))
1289             {
1290                 if ((is_compressed) || (level_samples != static_cast<int>(m_num_samples)) || (level != 0))
1291                 {
1292                     vogl_error_printf("%s: Multisampled texture consistency error\n", VOGL_METHOD_NAME);
1293                     VOGL_ASSERT_ALWAYS;
1294                     goto handle_error;
1295                 }
1296
1297                 if (m_target == GL_TEXTURE_2D_MULTISAMPLE)
1298                 {
1299                     GL_ENTRYPOINT(glTexImage2DMultisample)(m_target, m_num_samples, internal_fmt, level_width, level_height, level_params.get_value<GLenum>(GL_TEXTURE_FIXED_SAMPLE_LOCATIONS));
1300                     VOGL_CHECK_GL_ERROR;
1301                 }
1302                 else if (m_target == GL_TEXTURE_2D_MULTISAMPLE_ARRAY)
1303                 {
1304                     GL_ENTRYPOINT(glTexImage3DMultisample)(m_target, m_num_samples, internal_fmt, level_width, level_height, tex0.get_array_size(), level_params.get_value<GLenum>(GL_TEXTURE_FIXED_SAMPLE_LOCATIONS));
1305                     VOGL_CHECK_GL_ERROR;
1306                 }
1307                 else
1308                 {
1309                     vogl_error_printf("%s: Unexpected target error\n", VOGL_METHOD_NAME);
1310                     VOGL_ASSERT_ALWAYS;
1311                     goto handle_error;
1312                 }
1313
1314                 // Note: This changes the active GL context to a work context!
1315                 if (!splitter.init())
1316                 {
1317                     vogl_error_printf("%s: Failed initializing texture splitter object!\n", VOGL_METHOD_NAME);
1318                     VOGL_ASSERT_ALWAYS;
1319                     goto handle_error;
1320                 }
1321
1322                 for (uint sample_index = 0; sample_index < m_num_samples; sample_index++)
1323                 {
1324                     const uint8_vec &src_img = m_textures[sample_index].get_image_data(level, 0, face, 0);
1325                     VOGL_ASSERT(src_img.size());
1326
1327                     GLuint src_texture = 0;
1328                     GL_ENTRYPOINT(glGenTextures)(1, &src_texture);
1329                     VOGL_CHECK_GL_ERROR;
1330
1331                     const GLenum src_target = (m_target == GL_TEXTURE_2D_MULTISAMPLE_ARRAY) ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D;
1332
1333                     GL_ENTRYPOINT(glBindTexture)(src_target, src_texture);
1334                     VOGL_CHECK_GL_ERROR;
1335
1336                     GL_ENTRYPOINT(glTexParameteri)(src_target, GL_TEXTURE_MAX_LEVEL, 0);
1337                     GL_ENTRYPOINT(glTexParameteri)(src_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1338                     GL_ENTRYPOINT(glTexParameteri)(src_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1339                     VOGL_CHECK_GL_ERROR;
1340
1341                     if (src_target == GL_TEXTURE_2D_ARRAY)
1342                     {
1343                         uint array_size = tex0.get_array_size();
1344
1345                         temp_img.resize(0);
1346                         temp_img.reserve(src_img.size() * array_size);
1347
1348                         for (uint array_index = 0; array_index < array_size; array_index++)
1349                         {
1350                             temp_img.append(m_textures[sample_index].get_image_data(level, array_index, face, 0));
1351                         }
1352
1353                         GL_ENTRYPOINT(glTexImage3D)(src_target, level, level_internal_fmt, level_width, level_height, array_size, 0, tex0.get_ogl_fmt(), tex0.get_ogl_type(), temp_img.get_ptr());
1354                         VOGL_CHECK_GL_ERROR;
1355                     }
1356                     else
1357                     {
1358                         GL_ENTRYPOINT(glTexImage2D)(src_target, level, level_internal_fmt, level_width, level_height, 0, tex0.get_ogl_fmt(), tex0.get_ogl_type(), src_img.get_ptr());
1359                         VOGL_CHECK_GL_ERROR;
1360                     }
1361
1362                     bool status = splitter.combine(src_texture, sample_index, m_target, static_cast<uint32>(handle));
1363
1364                     GL_ENTRYPOINT(glBindTexture)(src_target, 0);
1365                     VOGL_CHECK_GL_ERROR;
1366
1367                     GL_ENTRYPOINT(glDeleteTextures)(1, &src_texture);
1368                     VOGL_CHECK_GL_ERROR;
1369
1370                     if (!status)
1371                     {
1372                         splitter.deinit();
1373
1374                         goto handle_error;
1375                     }
1376
1377                     // Check if dest texture has stencil
1378                     bool has_stencil = utils::is_in_set<GLenum, GLenum>(internal_fmt, GL_DEPTH_STENCIL, GL_DEPTH24_STENCIL8, GL_DEPTH32F_STENCIL8, GL_DEPTH32F_STENCIL8_NV);
1379                     bool is_reversed_fmt = utils::is_in_set<GLenum, GLenum>(internal_fmt, GL_DEPTH32F_STENCIL8, GL_DEPTH32F_STENCIL8_NV);
1380
1381                     if (has_stencil)
1382                     {
1383                         GLenum temp_color_format = GL_RGBA;
1384                         GLenum temp_color_type = is_reversed_fmt ? GL_UNSIGNED_INT_8_8_8_8 : GL_UNSIGNED_INT_8_8_8_8_REV;
1385
1386                         GLuint temp_color_texture = 0;
1387                         GL_ENTRYPOINT(glGenTextures)(1, &temp_color_texture);
1388                         VOGL_CHECK_GL_ERROR;
1389
1390                         const GLenum src_target = (m_target == GL_TEXTURE_2D_MULTISAMPLE_ARRAY) ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D;
1391
1392                         GL_ENTRYPOINT(glBindTexture)(src_target, temp_color_texture);
1393                         VOGL_CHECK_GL_ERROR;
1394
1395                         GL_ENTRYPOINT(glTexParameteri)(src_target, GL_TEXTURE_MAX_LEVEL, 0);
1396                         GL_ENTRYPOINT(glTexParameteri)(src_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1397                         GL_ENTRYPOINT(glTexParameteri)(src_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1398                         VOGL_CHECK_GL_ERROR;
1399
1400                         if (src_target == GL_TEXTURE_2D_ARRAY)
1401                         {
1402                             uint array_size = tex0.get_array_size();
1403
1404                             temp_img.resize(0);
1405                             temp_img.reserve(src_img.size() * array_size);
1406
1407                             for (uint array_index = 0; array_index < array_size; array_index++)
1408                             {
1409                                 temp_img.append(m_textures[sample_index].get_image_data(level, array_index, face, 0));
1410                             }
1411
1412                             GL_ENTRYPOINT(glTexImage3D)(src_target, level, GL_RGBA, level_width, level_height, array_size, 0, temp_color_format, temp_color_type, temp_img.get_ptr());
1413                             VOGL_CHECK_GL_ERROR;
1414                         }
1415                         else
1416                         {
1417                             GL_ENTRYPOINT(glTexImage2D)(src_target, level, GL_RGBA, level_width, level_height, 0, temp_color_format, temp_color_type, src_img.get_ptr());
1418                             VOGL_CHECK_GL_ERROR;
1419                         }
1420
1421                         bool status = splitter.copy_color_sample_to_stencil(temp_color_texture, sample_index, m_target, static_cast<uint32>(handle));
1422
1423                         GL_ENTRYPOINT(glBindTexture)(src_target, 0);
1424                         VOGL_CHECK_GL_ERROR;
1425
1426                         GL_ENTRYPOINT(glDeleteTextures)(1, &temp_color_texture);
1427                         VOGL_CHECK_GL_ERROR;
1428
1429                         if (!status)
1430                         {
1431                             splitter.deinit();
1432
1433                             vogl_error_printf("%s: Failed copying MSAA stencil samples from temp color texture to stencil", VOGL_METHOD_NAME);
1434
1435                             goto handle_error;
1436                         }
1437                     }
1438                 }
1439
1440                 splitter.deinit();
1441             }
1442             else
1443             {
1444                 GLenum target_to_set = m_target;
1445                 if (m_target == GL_TEXTURE_CUBE_MAP)
1446                     target_to_set = GL_TEXTURE_CUBE_MAP_POSITIVE_X + face;
1447
1448                 if (level_samples > 1)
1449                 {
1450                     vogl_error_printf("%s: GL_TEXTURE_SAMPLES consistency error\n", VOGL_METHOD_NAME);
1451                     VOGL_ASSERT_ALWAYS;
1452                     goto handle_error;
1453                 }
1454
1455                 const uint8_vec &src_img = tex0.get_image_data(level, 0, face, 0);
1456                 VOGL_ASSERT(src_img.size());
1457
1458                 switch (m_target)
1459                 {
1460                     case GL_TEXTURE_1D:
1461                     {
1462                         if (is_compressed)
1463                         {
1464                             GL_ENTRYPOINT(glCompressedTexImage1D)(target_to_set, level, level_internal_fmt, level_width, 0, src_img.size(), src_img.get_ptr());
1465                         }
1466                         else
1467                         {
1468                             GL_ENTRYPOINT(glTexImage1D)(target_to_set, level, level_internal_fmt, level_width, 0, tex0.get_ogl_fmt(), tex0.get_ogl_type(), src_img.get_ptr());
1469                         }
1470
1471                         break;
1472                     }
1473                     case GL_TEXTURE_2D:
1474                     case GL_TEXTURE_RECTANGLE:
1475                     case GL_TEXTURE_CUBE_MAP:
1476                     case GL_TEXTURE_1D_ARRAY:
1477                     {
1478                         if (m_target == GL_TEXTURE_1D_ARRAY)
1479                         {
1480                             uint array_size = tex0.get_array_size();
1481                             for (uint array_index = 0; array_index < array_size; array_index++)
1482                             {
1483                                 temp_img.append(tex0.get_image_data(level, array_index, face, 0));
1484                             }
1485                             level_height = array_size;
1486                         }
1487                         else
1488                         {
1489                             temp_img = src_img;
1490                         }
1491
1492                         if (is_compressed)
1493                         {
1494                             GL_ENTRYPOINT(glCompressedTexImage2D)(target_to_set, level, level_internal_fmt, level_width, level_height, 0, src_img.size(), temp_img.get_ptr());
1495                         }
1496                         else
1497                         {
1498                             GL_ENTRYPOINT(glTexImage2D)(target_to_set, level, level_internal_fmt, level_width, level_height, 0, tex0.get_ogl_fmt(), tex0.get_ogl_type(), temp_img.get_ptr());
1499                         }
1500
1501                         break;
1502                     }
1503                     case GL_TEXTURE_2D_ARRAY:
1504                     case GL_TEXTURE_3D:
1505                     {
1506                         temp_img.resize(0);
1507                         temp_img.reserve(src_img.size() * level_depth);
1508
1509                         if (m_target == GL_TEXTURE_3D)
1510                         {
1511                             for (int zslice = 0; zslice < level_depth; zslice++)
1512                             {
1513                                 temp_img.append(tex0.get_image_data(level, 0, face, zslice));
1514                             }
1515                         }
1516                         else
1517                         {
1518                             // 2D_ARRAY
1519                             uint array_size = tex0.get_array_size();
1520                             for (uint array_index = 0; array_index < array_size; array_index++)
1521                             {
1522                                 temp_img.append(tex0.get_image_data(level, array_index, face, 0));
1523                             }
1524                             level_depth = array_size;
1525                         }
1526
1527                         if (is_compressed)
1528                         {
1529                             GL_ENTRYPOINT(glCompressedTexImage3D)(target_to_set, level, level_internal_fmt, level_width, level_height, level_depth, 0, temp_img.size(), temp_img.get_ptr());
1530                         }
1531                         else
1532                         {
1533                             GL_ENTRYPOINT(glTexImage3D)(target_to_set, level, level_internal_fmt, level_width, level_height, level_depth, 0, tex0.get_ogl_fmt(), tex0.get_ogl_type(), temp_img.get_ptr());
1534                         }
1535
1536                         break;
1537                     }
1538                     default:
1539                     {
1540                         VOGL_ASSERT_ALWAYS;
1541                         vogl_error_printf("%s: Unsupported target type %d\n", VOGL_METHOD_NAME, m_target);
1542                         goto handle_error;
1543                     }
1544                 }
1545
1546                 if (vogl_check_gl_error())
1547                 {
1548                     vogl_error_printf("%s: Failed creating texture image\n", VOGL_METHOD_NAME);
1549                     goto handle_error;
1550                 }
1551
1552             } // sample_index
1553         } // level
1554     } // face
1555
1556     if (m_params.get_value<int>(GL_GENERATE_MIPMAP))
1557     {
1558         GL_ENTRYPOINT(glGenerateMipmap)(m_target);
1559         vogl_debug_printf("%s: Generating mipmaps for texture, snapshot handle %u GL handle %u\n", VOGL_METHOD_NAME, m_snapshot_handle, (uint)handle);
1560     }
1561
1562 #undef SET_INT
1563 #undef SET_FLOAT
1564
1565     if (any_failures)
1566     {
1567         vogl_warning_printf("%s: One or more texture params could not be set on trace texture %u, GL texture %" PRIu64 " target %s, dimensions %ux%ux%u, internal format %s\n", VOGL_METHOD_NAME,
1568                            m_snapshot_handle, (uint64_t)handle, g_gl_enums.find_gl_name(m_target), tex_width, tex_height, tex_depth, g_gl_enums.find_gl_image_format_name(internal_fmt));
1569     }
1570
1571     return true;
1572
1573 handle_error:
1574     vogl_error_printf("%s: Failed restoring trace texture %u, GL texture %" PRIu64 " target %s, while processing face %u level %u, dimensions %ux%ux%u, internal format %s\n", VOGL_METHOD_NAME,
1575                      m_snapshot_handle, (uint64_t)handle, g_gl_enums.find_gl_name(m_target), face, level, tex_width, tex_height, tex_depth, g_gl_enums.find_gl_image_format_name(internal_fmt));
1576
1577     GL_ENTRYPOINT(glBindTexture)(m_target, 0);
1578     VOGL_CHECK_GL_ERROR;
1579
1580     if (created_handle)
1581     {
1582         remapper.delete_handle_and_object(VOGL_NAMESPACE_TEXTURES, m_snapshot_handle, handle);
1583
1584         handle = 0;
1585     }
1586
1587     return false;
1588 }
1589
1590 bool vogl_texture_state::remap_handles(vogl_handle_remapper &remapper)
1591 {
1592     VOGL_FUNC_TRACER
1593
1594     if (!m_is_valid)
1595         return false;
1596
1597     m_snapshot_handle = static_cast<uint32>(remapper.remap_handle(VOGL_NAMESPACE_TEXTURES, m_snapshot_handle));
1598
1599     if (m_buffer)
1600     {
1601         m_buffer = static_cast<GLuint>(remapper.remap_handle(VOGL_NAMESPACE_BUFFERS, m_buffer));
1602     }
1603
1604     return true;
1605 }
1606
1607 void vogl_texture_state::clear()
1608 {
1609     VOGL_FUNC_TRACER
1610
1611     m_snapshot_handle = 0;
1612     m_target = GL_NONE;
1613     m_buffer = 0;
1614
1615     m_num_samples = 0;
1616
1617     for (uint i = 0; i < cMaxSamples; i++)
1618         m_textures[i].clear();
1619
1620     m_params.clear();
1621     for (uint i = 0; i < VOGL_ARRAY_SIZE(m_level_params); i++)
1622         m_level_params[i].clear();
1623
1624     m_is_unquerable = false;
1625     m_is_valid = false;
1626 }
1627
1628 bool vogl_texture_state::serialize(json_node &node, vogl_blob_manager &blob_manager) const
1629 {
1630     VOGL_FUNC_TRACER
1631
1632     if (!m_is_valid)
1633         return false;
1634
1635     node.add_key_value("version", VOGL_SERIALIZED_TEXTURE_STATE_VERSION);
1636     node.add_key_value("handle", m_snapshot_handle);
1637     node.add_key_value("target", g_gl_enums.find_gl_name(m_target));
1638     node.add_key_value("is_unquerable", m_is_unquerable);
1639     node.add_key_value("buffer", m_buffer);
1640     node.add_key_value("samples", m_num_samples);
1641
1642     if ((!m_is_unquerable) && (m_target != GL_NONE))
1643     {
1644         json_node &tex_params_obj = node.add_object("tex_params");
1645         if (!m_params.serialize(tex_params_obj, blob_manager))
1646             return false;
1647
1648         if ((m_target != GL_TEXTURE_BUFFER) && (m_num_samples))
1649         {
1650             json_node &textures_array_node = node.add_array("textures");
1651
1652             for (uint sample_index = 0; sample_index < m_num_samples; sample_index++)
1653             {
1654                 json_node &texture_node = textures_array_node.add_object();
1655
1656                 const ktx_texture &tex = m_textures[sample_index];
1657
1658                 const char *pTex_type = utils::map_value(static_cast<int>(m_target), "tex",
1659                                                          GL_TEXTURE_1D, "tex_1d",
1660                                                          GL_TEXTURE_2D, "tex_2d",
1661                                                          GL_TEXTURE_3D, "tex_3d",
1662                                                          GL_TEXTURE_CUBE_MAP, "tex_cube",
1663                                                          GL_TEXTURE_RECTANGLE, "tex_rect",
1664                                                          GL_TEXTURE_2D_ARRAY, "tex_2d_array",
1665                                                          GL_TEXTURE_1D_ARRAY, "tex_1d_array",
1666                                                          GL_TEXTURE_BUFFER, "tex_buffer",
1667                                                          GL_TEXTURE_2D_MULTISAMPLE, "tex_2d_multisample",
1668                                                          GL_TEXTURE_2D_MULTISAMPLE_ARRAY, "tex_2d_multisample_array",
1669                                                          GL_TEXTURE_CUBE_MAP_ARRAY, "tex_cube_array");
1670
1671                 uint actual_mip_levels = m_params.get_value<GLint>(GL_TEXTURE_MAX_LEVEL) + 1;
1672                 if (tex.is_valid())
1673                     actual_mip_levels = math::minimum(actual_mip_levels, tex.get_num_mips());
1674
1675                 dynamic_string prefix;
1676                 switch (m_target)
1677                 {
1678                     case GL_TEXTURE_1D:
1679                         prefix.format("%s_%u_levels_%u_%s", pTex_type, tex.get_width(), actual_mip_levels, g_gl_enums.find_gl_name(tex.get_ogl_internal_fmt()));
1680                         break;
1681                     case GL_TEXTURE_RECTANGLE:
1682                     case GL_TEXTURE_2D:
1683                         prefix.format("%s_%ux%u_levels_%u_%s", pTex_type, tex.get_width(), tex.get_height(), actual_mip_levels, g_gl_enums.find_gl_image_format_name(tex.get_ogl_internal_fmt()));
1684                         break;
1685                     case GL_TEXTURE_3D:
1686                         prefix.format("%s_%ux%ux%u_levels_%u_%s", pTex_type, tex.get_width(), tex.get_height(), tex.get_depth(), actual_mip_levels, g_gl_enums.find_gl_image_format_name(tex.get_ogl_internal_fmt()));
1687                         break;
1688                     case GL_TEXTURE_1D_ARRAY:
1689                         prefix.format("%s_%u_levels_%u_arraysize_%u_%s", pTex_type, tex.get_width(), actual_mip_levels, tex.get_array_size(), g_gl_enums.find_gl_name(tex.get_ogl_internal_fmt()));
1690                         break;
1691                     case GL_TEXTURE_2D_ARRAY:
1692                         prefix.format("%s_%ux%u_levels_%u_arraysize_%u_%s", pTex_type, tex.get_width(), tex.get_height(), actual_mip_levels, tex.get_array_size(), g_gl_enums.find_gl_image_format_name(tex.get_ogl_internal_fmt()));
1693                         break;
1694                     case GL_TEXTURE_2D_MULTISAMPLE:
1695                         prefix.format("%s_%ux%u_levels_%u_sample_%u_of_%u_%s", pTex_type, tex.get_width(), tex.get_height(), actual_mip_levels, sample_index, m_num_samples, g_gl_enums.find_gl_image_format_name(tex.get_ogl_internal_fmt()));
1696                         break;
1697                     case GL_TEXTURE_2D_MULTISAMPLE_ARRAY:
1698                         prefix.format("%s_%ux%u_levels_%u_sample_%u_of_%u_arraysize_%u_%s", pTex_type, tex.get_width(), tex.get_height(), actual_mip_levels, sample_index, m_num_samples, tex.get_array_size(), g_gl_enums.find_gl_image_format_name(tex.get_ogl_internal_fmt()));
1699                         break;
1700                     case GL_TEXTURE_CUBE_MAP:
1701                         prefix.format("%s_%ux%u_levels_%u_%s", pTex_type, tex.get_width(), tex.get_height(), actual_mip_levels, g_gl_enums.find_gl_image_format_name(tex.get_ogl_internal_fmt()));
1702                         break;
1703                     case GL_TEXTURE_CUBE_MAP_ARRAY:
1704                         prefix.format("%s_%ux%u_levels_%u_arraysize_%u_%s", pTex_type, tex.get_width(), tex.get_height(), actual_mip_levels, tex.get_array_size(), g_gl_enums.find_gl_image_format_name(tex.get_ogl_internal_fmt()));
1705                         break;
1706                     default:
1707                         VOGL_ASSERT_ALWAYS;
1708                         return false;
1709                 }
1710
1711                 dynamic_stream dyn_stream;
1712                 data_stream_serializer serializer(dyn_stream);
1713                 if (!tex.write_to_stream(serializer))
1714                     return false;
1715
1716                 dyn_stream.seek(0, false);
1717
1718                 dynamic_string blob_id(blob_manager.add_stream_compute_unique_id(dyn_stream, prefix.get_ptr(), "ktx"));
1719                 if (blob_id.is_empty())
1720                     return false;
1721
1722                 dyn_stream.close();
1723
1724                 texture_node.add_key_value("texture_data_blob_id", blob_id);
1725             }
1726
1727             json_node &level_params_array = node.add_array("level_params");
1728             for (uint face = 0; face < m_textures[0].get_num_faces(); face++)
1729             {
1730                 for (uint level = 0; level < m_textures[0].get_num_mips(); level++)
1731                 {
1732                     json_node &level_param_array_value = level_params_array.add_object();
1733                     if (m_target == GL_TEXTURE_CUBE_MAP)
1734                         level_param_array_value.add_key_value("face", face);
1735                     level_param_array_value.add_key_value("level", level);
1736
1737                     json_node &params_node = level_param_array_value.add_object("params");
1738                     if (!m_level_params[face][level].serialize(params_node, blob_manager))
1739                         return false;
1740                 }
1741             }
1742         }
1743     }
1744
1745     return true;
1746 }
1747
1748 bool vogl_texture_state::deserialize(const json_node &node, const vogl_blob_manager &blob_manager)
1749 {
1750     VOGL_FUNC_TRACER
1751
1752     clear();
1753
1754     if ((!node.has_key("handle")) || (!node.has_key("target")))
1755         return false;
1756
1757     m_snapshot_handle = node.value_as_uint32("handle");
1758     m_target = vogl_get_json_value_as_enum(node, "target");
1759     m_is_unquerable = node.value_as_bool("is_unquerable");
1760     m_buffer = node.value_as_uint32("buffer");
1761     m_num_samples = node.value_as_uint32("samples", 1);
1762
1763     // Sanity check.
1764     if (!utils::is_in_set(static_cast<int>(m_target), GL_NONE, GL_TEXTURE_1D, GL_TEXTURE_2D, GL_TEXTURE_3D, GL_TEXTURE_CUBE_MAP, GL_TEXTURE_CUBE_MAP_ARRAY,
1765         GL_TEXTURE_1D_ARRAY, GL_TEXTURE_2D_ARRAY, GL_TEXTURE_RECTANGLE, GL_TEXTURE_BUFFER, GL_TEXTURE_2D_MULTISAMPLE, GL_TEXTURE_2D_MULTISAMPLE_ARRAY))
1766     {
1767         return false;
1768     }
1769
1770     const json_node *pTex_params_obj = node.find_child_object("tex_params");
1771     if (pTex_params_obj)
1772     {
1773         if (!m_params.deserialize(*pTex_params_obj, blob_manager))
1774             return false;
1775     }
1776
1777     if ((!m_is_unquerable) && (m_target != GL_NONE) && (m_target != GL_TEXTURE_BUFFER))
1778     {
1779         if (node.has_key("texture_data_blob_id"))
1780         {
1781             if (m_num_samples != 1)
1782                 return false;
1783
1784             dynamic_string blob_id(node.value_as_string_ptr("texture_data_blob_id"));
1785             if (blob_id.is_empty())
1786                 return false;
1787
1788             dynamic_stream tex_data;
1789             if (!blob_manager.get(blob_id, tex_data.get_buf()))
1790                 return false;
1791
1792             data_stream_serializer serializer(&tex_data);
1793             if (!m_textures[0].read_from_stream(serializer))
1794                 return false;
1795         }
1796         else if (node.has_array("textures"))
1797         {
1798             const json_node *pTextures_array_node = node.find_child_array("textures");
1799             if ((!pTextures_array_node) || (!pTextures_array_node->size()) || (pTextures_array_node->size() > cMaxSamples))
1800                 return false;
1801
1802             for (uint i = 0; i < pTextures_array_node->size(); i++)
1803             {
1804                 const json_node *pTexture_node = pTextures_array_node->get_child(i);
1805                 if (!pTexture_node)
1806                     return false;
1807
1808                 dynamic_string blob_id(pTexture_node->value_as_string_ptr("texture_data_blob_id"));
1809                 if (blob_id.is_empty())
1810                     return false;
1811
1812                 dynamic_stream tex_data;
1813                 if (!blob_manager.get(blob_id, tex_data.get_buf()))
1814                     return false;
1815
1816                 data_stream_serializer serializer(&tex_data);
1817                 if (!m_textures[i].read_from_stream(serializer))
1818                     return false;
1819             }
1820         }
1821         else
1822         {
1823             return false;
1824         }
1825
1826         const json_node *pLevel_params_array = node.find_child_array("level_params");
1827         if (pLevel_params_array)
1828         {
1829             for (uint i = 0; i < pLevel_params_array->size(); i++)
1830             {
1831                 const json_node *pLevel_params_node = pLevel_params_array->get_value_as_object(i);
1832                 if (!pLevel_params_node)
1833                     return false;
1834
1835                 uint face = pLevel_params_node->value_as_uint32("face");
1836                 if (face)
1837                 {
1838                     if ((m_target != GL_TEXTURE_CUBE_MAP) || (face > cCubeMapFaces))
1839                         return false;
1840                 }
1841
1842                 uint level = pLevel_params_node->value_as_uint32("level");
1843                 // obviously crazy level
1844                 if (level > 20)
1845                     return false;
1846
1847                 if (level >= m_level_params[face].size())
1848                     m_level_params[face].resize(level + 1);
1849
1850                 const json_node *pParams_node = pLevel_params_node->find_child_object("params");
1851                 if (!pParams_node)
1852                     return false;
1853
1854                 if (!m_level_params[face][level].deserialize(*pParams_node, blob_manager))
1855                     return false;
1856             }
1857         }
1858
1859
1860     }
1861
1862     m_is_valid = true;
1863
1864     return true;
1865 }
1866
1867 bool vogl_texture_state::compare_restorable_state(const vogl_gl_object_state &rhs_obj) const
1868 {
1869     VOGL_FUNC_TRACER
1870
1871     if ((!m_is_valid) || (!rhs_obj.is_valid()))
1872         return false;
1873
1874     if (rhs_obj.get_type() != cGLSTTexture)
1875         return false;
1876
1877     const vogl_texture_state &rhs = static_cast<const vogl_texture_state &>(rhs_obj);
1878
1879     if (this == &rhs)
1880         return true;
1881
1882 #define CMP(x)      \
1883     if (x != rhs.x) \
1884         return false;
1885     CMP(m_is_unquerable);
1886     CMP(m_target);
1887     CMP(m_params);
1888     CMP(m_buffer);
1889
1890     for (uint i = 0; i < cCubeMapFaces; i++)
1891         CMP(m_level_params[i]);
1892
1893     CMP(m_num_samples);
1894
1895     for (uint i = 0; i < m_num_samples; i++)
1896     {
1897         if (m_textures[i].is_valid() != rhs.m_textures[i].is_valid())
1898             return false;
1899         if (m_textures[i] != rhs.m_textures[i])
1900             return false;
1901     }
1902 #undef CMP
1903
1904     return true;
1905 }
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922