]> git.cworth.org Git - vogl/blob - src/voglcommon/vogl_texture_state.cpp
- regression test now works on AMD
[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 #if 0
385     // OK, I can't retrive the default framebuffer's depth/stencil buffer on AMD - all I get is INVALID_OPERATION. Crap. This works fine on NV though.
386     // This workaround didn't work.
387     if (internal_fmt == GL_DEPTH_STENCIL)
388     {
389         if (GL_ENTRYPOINT(glGetInternalformativ) && context_info.supports_extension("GL_ARB_internalformat_query"))
390         {
391             GL_ENTRYPOINT(glGetInternalformativ)(GL_TEXTURE_2D, internal_fmt, GL_GET_TEXTURE_IMAGE_FORMAT, sizeof(image_fmt), (GLint *)&image_fmt);
392             VOGL_CHECK_GL_ERROR;
393
394             GL_ENTRYPOINT(glGetInternalformativ)(GL_TEXTURE_2D, internal_fmt, GL_GET_TEXTURE_IMAGE_TYPE, sizeof(image_type), (GLint *)&image_type);
395             VOGL_CHECK_GL_ERROR;
396         }
397     }
398 #endif
399
400     uint num_faces = 1;
401
402     GLenum ktx_image_fmt = GL_NONE, ktx_image_type = GL_NONE;
403     if (!pInternal_tex_fmt->m_compressed)
404     {
405         ktx_image_fmt = image_fmt;
406         ktx_image_type = image_type;
407     }
408
409     GLenum ktx_tex_target = m_target;
410
411     if (m_target == GL_TEXTURE_1D)
412     {
413         if (!m_textures[0].init_1D(width, num_actual_mip_levels, internal_fmt, ktx_image_fmt, ktx_image_type))
414         {
415             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));
416             clear();
417             return false;
418         }
419     }
420     else if ((m_target == GL_TEXTURE_2D) || (m_target == GL_TEXTURE_RECTANGLE))
421     {
422         if (!m_textures[0].init_2D(width, height, 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     else if (m_target == GL_TEXTURE_CUBE_MAP)
430     {
431         if (width != height)
432         {
433             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));
434             clear();
435             return false;
436         }
437
438         if (!m_textures[0].init_cubemap(width, num_actual_mip_levels, internal_fmt, ktx_image_fmt, ktx_image_type))
439         {
440             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));
441             clear();
442             return false;
443         }
444
445         num_faces = cCubeMapFaces;
446     }
447     else if (m_target == GL_TEXTURE_3D)
448     {
449         if (!m_textures[0].init_3D(width, height, depth, num_actual_mip_levels, internal_fmt, ktx_image_fmt, ktx_image_type))
450         {
451             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));
452             clear();
453             return false;
454         }
455     }
456     else if (m_target == GL_TEXTURE_1D_ARRAY)
457     {
458         if (!m_textures[0].init_1D_array(width, num_actual_mip_levels, base_height, internal_fmt, ktx_image_fmt, ktx_image_type))
459         {
460             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));
461             clear();
462             return false;
463         }
464     }
465     else if (m_target == GL_TEXTURE_2D_ARRAY)
466     {
467         if (!m_textures[0].init_2D_array(width, height, num_actual_mip_levels, base_depth, internal_fmt, ktx_image_fmt, ktx_image_type))
468         {
469             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));
470             clear();
471             return false;
472         }
473     }
474     else if (m_target == GL_TEXTURE_2D_MULTISAMPLE)
475     {
476         ktx_tex_target = GL_TEXTURE_2D;
477
478         for (uint sample_index = 0; sample_index < m_num_samples; sample_index++)
479         {
480             if (!m_textures[sample_index].init_2D(width, height, num_actual_mip_levels, internal_fmt, ktx_image_fmt, ktx_image_type))
481             {
482                 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));
483                 clear();
484                 return false;
485             }
486         }
487     }
488     else if (m_target == GL_TEXTURE_2D_MULTISAMPLE_ARRAY)
489     {
490         ktx_tex_target = GL_TEXTURE_2D_ARRAY;
491
492         for (uint sample_index = 0; sample_index < m_num_samples; sample_index++)
493         {
494             if (!m_textures[sample_index].init_2D_array(width, height, num_actual_mip_levels, base_depth, internal_fmt, ktx_image_fmt, ktx_image_type))
495             {
496                 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));
497                 clear();
498                 return false;
499             }
500         }
501     }
502     else
503     {
504         // TODO
505         vogl_error_printf("%s: Unsupported target, texture %" PRIu64 " target %s", VOGL_METHOD_NAME, (uint64_t)handle, g_gl_enums.find_gl_name(m_target));
506         clear();
507         return false;
508     }
509
510     VOGL_VERIFY(m_textures[0].get_ogl_internal_fmt() == internal_fmt);
511     if (!pInternal_tex_fmt->m_compressed)
512     {
513         VOGL_VERIFY(m_textures[0].get_ogl_fmt() == image_fmt);
514         VOGL_VERIFY(m_textures[0].get_ogl_type() == image_type);
515     }
516
517     // We can't directly get the data of multisampled textures. Instead, copy the MSAA data into X separate non-MSAA textures.
518     vogl::vector<GLuint> split_texture_handles;
519     vogl::vector<GLuint> split_stencil_texture_handles;
520
521 #define VOGL_FREE_SPLIT_TEXTURES \
522     if (split_texture_handles.size()) { GL_ENTRYPOINT(glDeleteTextures)(split_texture_handles.size(), split_texture_handles.get_ptr()); VOGL_CHECK_GL_ERROR; } \
523     if (split_stencil_texture_handles.size()) { GL_ENTRYPOINT(glDeleteTextures)(split_stencil_texture_handles.size(), split_stencil_texture_handles.get_ptr()); VOGL_CHECK_GL_ERROR; }
524
525     if ((m_target == GL_TEXTURE_2D_MULTISAMPLE) || (m_target == GL_TEXTURE_2D_MULTISAMPLE_ARRAY))
526     {
527         // Note: This temporarily switches GL contexts!
528         vogl_msaa_texture_splitter splitter;
529
530         if (!splitter.init())
531         {
532             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));
533             clear();
534             return false;
535         }
536
537         if ( (pInternal_tex_fmt->m_comp_sizes[cTCDepth]) ||
538              (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]) ||
539              (pInternal_tex_fmt->m_comp_sizes[cTCIntensity]) || (pInternal_tex_fmt->m_comp_sizes[cTCLuminance]) )
540         {
541             if (!splitter.split(target, static_cast<GLuint>(handle), split_texture_handles))
542             {
543                 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));
544                 clear();
545                 return false;
546             }
547         }
548
549         if (pInternal_tex_fmt->m_comp_sizes[cTCStencil])
550         {
551             GLuint temp_msaa_color_tex = 0;
552             if (!splitter.copy_stencil_samples_to_color(target, static_cast<GLuint>(handle), temp_msaa_color_tex))
553             {
554                 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));
555                 clear();
556                 return false;
557             }
558
559             bool status = splitter.split(target, temp_msaa_color_tex, split_stencil_texture_handles);
560
561             GL_ENTRYPOINT(glDeleteTextures)(1, &temp_msaa_color_tex);
562             VOGL_CHECK_GL_ERROR;
563
564             temp_msaa_color_tex = 0;
565
566             if (!status)
567             {
568                 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));
569                 clear();
570                 return false;
571             }
572         }
573     }
574
575     for (uint face = 0; face < num_faces; face++)
576     {
577         GLenum face_target_to_query = m_target;
578         if (m_target == GL_TEXTURE_CUBE_MAP)
579             face_target_to_query = GL_TEXTURE_CUBE_MAP_POSITIVE_X + face;
580
581         m_level_params[face].resize(num_actual_mip_levels);
582
583         for (uint level = 0; level < num_actual_mip_levels; level++)
584         {
585             GLenum level_internal_fmt = 0;
586             GL_ENTRYPOINT(glGetTexLevelParameteriv)(target_to_query, level, GL_TEXTURE_INTERNAL_FORMAT, reinterpret_cast<GLint *>(&level_internal_fmt));
587
588             bool is_valid = true;
589             if (vogl_check_gl_error())
590                 is_valid = false;
591             else if (level_internal_fmt != internal_fmt)
592                 is_valid = false;
593
594             if (is_valid)
595             {
596                 // Needed for ACTC's 3D textures, not sure why yet
597                 GLint level_width = 0;
598                 GL_ENTRYPOINT(glGetTexLevelParameteriv)(target_to_query, level, GL_TEXTURE_WIDTH, &level_width);
599                 if (vogl_check_gl_error())
600                     is_valid = false;
601                 else if (!level_width)
602                     is_valid = false;
603                 else if (level_width != math::maximum<int>(width >> level, 1))
604                 {
605                     VOGL_ASSERT_ALWAYS;
606                     is_valid = false;
607                 }
608             }
609
610             if (!is_valid)
611                 continue;
612
613             vogl_state_vector level_params;
614
615             bool any_gl_errors = false;
616
617 // TODO: Check for core vs. compat profiles and not query the old stuff
618 #define GET_INT(gl_enum)                                                                        \
619     do                                                                                          \
620     {                                                                                           \
621         int values[4] = { 0, 0, 0, 0 };                                                         \
622         GL_ENTRYPOINT(glGetTexLevelParameteriv)(face_target_to_query, level, gl_enum, values);  \
623         if (vogl_check_gl_error())                                                              \
624             any_gl_errors = true;                                                               \
625         level_params.insert(gl_enum, 0, values, sizeof(values[0]));                             \
626     } while (0)
627
628             GET_INT(GL_TEXTURE_WIDTH);
629             GET_INT(GL_TEXTURE_HEIGHT);
630             GET_INT(GL_TEXTURE_DEPTH);
631             GET_INT(GL_TEXTURE_INTERNAL_FORMAT);
632             if (any_gl_errors)
633             {
634                 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));
635                 clear();
636                 VOGL_FREE_SPLIT_TEXTURES
637                 return false;
638             }
639
640             GET_INT(GL_TEXTURE_SAMPLES);
641             GET_INT(GL_TEXTURE_FIXED_SAMPLE_LOCATIONS);
642
643             GET_INT(GL_TEXTURE_RED_SIZE);
644             GET_INT(GL_TEXTURE_GREEN_SIZE);
645             GET_INT(GL_TEXTURE_BLUE_SIZE);
646             GET_INT(GL_TEXTURE_ALPHA_SIZE);
647             GET_INT(GL_TEXTURE_DEPTH_SIZE);
648             GET_INT(GL_TEXTURE_STENCIL_SIZE);
649             GET_INT(GL_TEXTURE_LUMINANCE_SIZE);
650             GET_INT(GL_TEXTURE_INTENSITY_SIZE);
651             GET_INT(GL_TEXTURE_SHARED_SIZE);
652             GET_INT(GL_TEXTURE_COMPRESSED);
653
654             if (context_info.supports_extension("GL_ARB_depth_texture"))
655             {
656                 GET_INT(GL_TEXTURE_DEPTH_SIZE);
657                 GET_INT(GL_TEXTURE_DEPTH_TYPE);
658             }
659
660             if (context_info.supports_extension("GL_EXT_packed_depth_stencil"))
661                 GET_INT(GL_TEXTURE_STENCIL_SIZE_EXT);
662
663             if (m_target == GL_TEXTURE_BUFFER)
664             {
665                 GET_INT(GL_TEXTURE_BUFFER_DATA_STORE_BINDING);
666                 GET_INT(GL_TEXTURE_BUFFER_OFFSET);
667                 GET_INT(GL_TEXTURE_BUFFER_SIZE);
668             }
669
670             bool is_compressed = level_params.get_value<bool>(GL_TEXTURE_COMPRESSED);
671             if (!is_compressed)
672             {
673                 int value = 0;
674                 level_params.insert(GL_TEXTURE_COMPRESSED_IMAGE_SIZE, 0, &value, sizeof(value));
675             }
676             else
677             {
678                 GET_INT(GL_TEXTURE_COMPRESSED_IMAGE_SIZE);
679             }
680 #undef GET_INT
681
682             if (any_gl_errors)
683             {
684                 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));
685             }
686
687             if ((level_params.get_value<int>(GL_TEXTURE_WIDTH) == 0) ||
688                 (level_params.get_value<int>(GL_TEXTURE_HEIGHT) == 0) ||
689                 (level_params.get_value<int>(GL_TEXTURE_DEPTH) == 0) ||
690                 (level_params.get_value<GLenum>(GL_TEXTURE_INTERNAL_FORMAT) != internal_fmt))
691             {
692                 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));
693                 clear();
694                 VOGL_FREE_SPLIT_TEXTURES
695                 return false;
696             }
697
698             size_t size_in_bytes = level_params.get_value<uint>(GL_TEXTURE_COMPRESSED_IMAGE_SIZE);
699             if (pInternal_tex_fmt->m_compressed)
700             {
701                 if (!size_in_bytes)
702                 {
703                     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));
704                     clear();
705                     VOGL_FREE_SPLIT_TEXTURES
706                     return false;
707                 }
708             }
709             else
710             {
711                 if (size_in_bytes)
712                 {
713                     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));
714                     clear();
715                     VOGL_FREE_SPLIT_TEXTURES
716                     return false;
717                 }
718             }
719
720             m_level_params[face][level] = level_params;
721         }
722     }
723
724     VOGL_CHECK_GL_ERROR;
725
726     uint8_vec temp_img;
727
728     // Now grab the data from each face, mipmap level, and slice/layer and supply it to the KTX texture object.
729     for (uint face = 0; face < num_faces; face++)
730     {
731         GLenum face_target_to_query = m_target;
732         if (m_target == GL_TEXTURE_CUBE_MAP)
733             face_target_to_query = GL_TEXTURE_CUBE_MAP_POSITIVE_X + face;
734
735         // Query available mip levels and add them to the texture.
736         for (uint level = 0; level < num_actual_mip_levels; level++)
737         {
738             const vogl_state_vector &level_params = m_level_params[face][level];
739
740             if (!level_params.find(GL_TEXTURE_WIDTH))
741             {
742                 for (uint sample_index = 0; sample_index < m_num_samples; sample_index++)
743                 {
744                     // Insert placeholder images for the missing texture levels
745                     int image_size = m_textures[sample_index].get_expected_image_size(level);
746                     temp_img.resize(image_size);
747
748                     if ((ktx_tex_target == GL_TEXTURE_1D_ARRAY) || (ktx_tex_target == GL_TEXTURE_2D_ARRAY))
749                     {
750                         VOGL_ASSERT(base_depth);
751                         uint num_array_elements = base_depth;
752
753                         if (ktx_tex_target == GL_TEXTURE_1D_ARRAY)
754                         {
755                             VOGL_ASSERT(base_height);
756                             num_array_elements = base_height;
757                         }
758
759                         for (uint array_index = 0; array_index < num_array_elements; array_index++)
760                         {
761                             m_textures[sample_index].add_image(level, array_index, face, 0, temp_img.get_ptr(), image_size);
762                         }
763                     }
764                     else
765                     {
766                         int level_depth = (target == GL_TEXTURE_3D) ? math::maximum<int>(1, depth >> level) : 1;
767
768                         for (int zslice = 0; zslice < level_depth; zslice++)
769                         {
770                             m_textures[sample_index].add_image(level, 0, face, zslice, temp_img.get_ptr(), temp_img.size());
771                         }
772                     }
773                 } // sample_index
774             }
775             else
776             {
777                 int level_width = level_params.get_value<int>(GL_TEXTURE_WIDTH);
778                 int level_height = level_params.get_value<int>(GL_TEXTURE_HEIGHT);
779                 int level_depth = level_params.get_value<int>(GL_TEXTURE_DEPTH);
780
781                 int size_in_bytes = level_params.get_value<uint>(GL_TEXTURE_COMPRESSED_IMAGE_SIZE);
782
783                 if (!pInternal_tex_fmt->m_compressed)
784                 {
785                     VOGL_ASSERT(!size_in_bytes);
786
787                     size_t size_in_bytes64 = vogl_get_image_size(image_fmt, image_type, level_width, level_height, level_depth);
788                     if (!size_in_bytes64)
789                     {
790                         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));
791                         clear();
792                         VOGL_FREE_SPLIT_TEXTURES
793                         return false;
794                     }
795
796                     if (size_in_bytes64 > static_cast<size_t>(cINT32_MAX))
797                     {
798                         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));
799                         clear();
800                         VOGL_FREE_SPLIT_TEXTURES
801                         return false;
802                     }
803
804                     size_in_bytes = static_cast<int>(size_in_bytes64);
805                 }
806
807                 for (uint sample_index = 0; sample_index < m_num_samples; sample_index++)
808                 {
809                     GLenum get_target = face_target_to_query;
810
811                     if ((m_target == GL_TEXTURE_2D_MULTISAMPLE) || (m_target == GL_TEXTURE_2D_MULTISAMPLE_ARRAY))
812                     {
813                         GL_ENTRYPOINT(glBindTexture)(ktx_tex_target, split_texture_handles[sample_index]);
814                         VOGL_CHECK_GL_ERROR;
815
816                         get_target = ktx_tex_target;
817                     }
818
819                     const uint num_guard_bytes = 2;
820                     if (!temp_img.try_resize(size_in_bytes + num_guard_bytes))
821                     {
822                         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));
823                         clear();
824                         VOGL_FREE_SPLIT_TEXTURES
825                         return false;
826                     }
827
828                     // Write a pattern after the buffer to detect buffer size computation screwups.
829                     if (size_in_bytes >= 4)
830                     {
831                         temp_img[size_in_bytes - 4] = 0x67;
832                         temp_img[size_in_bytes - 3] = 0xCC;
833                         temp_img[size_in_bytes - 2] = 0xD4;
834                         temp_img[size_in_bytes - 1] = 0xF9;
835                     }
836
837                     temp_img[size_in_bytes] = 0xDE;
838                     temp_img[size_in_bytes + 1] = 0xAD;
839
840                     if (pInternal_tex_fmt->m_compressed)
841                     {
842                         GL_ENTRYPOINT(glGetCompressedTexImage)(get_target, level, temp_img.get_ptr());
843                     }
844                     else
845                     {
846                         GL_ENTRYPOINT(glGetTexImage)(get_target, level, image_fmt, image_type, temp_img.get_ptr());
847                     }
848
849                     if (vogl_check_gl_error())
850                     {
851                         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));
852                         clear();
853                         VOGL_FREE_SPLIT_TEXTURES
854                         return false;
855                     }
856
857                     if (size_in_bytes >= 4)
858                     {
859                         if ((temp_img[size_in_bytes - 4] == 0x67) && (temp_img[size_in_bytes - 3] == 0xCC) &&
860                             (temp_img[size_in_bytes - 2] == 0xD4) && (temp_img[size_in_bytes - 1] == 0xF9))
861                         {
862                             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));
863                         }
864                     }
865
866                     VOGL_VERIFY((temp_img[size_in_bytes] == 0xDE) && (temp_img[size_in_bytes + 1] == 0xAD));
867
868                     temp_img.try_resize(size_in_bytes);
869
870                     if ((m_target == GL_TEXTURE_2D_MULTISAMPLE) || (m_target == GL_TEXTURE_2D_MULTISAMPLE_ARRAY))
871                     {
872                         if (split_stencil_texture_handles.size())
873                         {
874                             size_t split_color_size_in_bytes64 = vogl_get_image_size(GL_RGBA, GL_UNSIGNED_BYTE, level_width, level_height, level_depth);
875
876                             if (!split_color_size_in_bytes64)
877                             {
878                                 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));
879                                 clear();
880                                 VOGL_FREE_SPLIT_TEXTURES
881                                 return false;
882                             }
883
884                             if (split_color_size_in_bytes64 > static_cast<size_t>(cINT32_MAX))
885                             {
886                                 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));
887                                 clear();
888                                 VOGL_FREE_SPLIT_TEXTURES
889                                 return false;
890                             }
891
892                             uint8_vec stencil_image_data(static_cast<uint>(split_color_size_in_bytes64));
893
894                             GL_ENTRYPOINT(glBindTexture)(ktx_tex_target, split_stencil_texture_handles[sample_index]);
895                             VOGL_CHECK_GL_ERROR;
896
897                             GL_ENTRYPOINT(glGetTexImage)(ktx_tex_target, level, GL_RGBA, GL_UNSIGNED_BYTE, stencil_image_data.get_ptr());
898                             VOGL_CHECK_GL_ERROR;
899
900                             switch (internal_fmt)
901                             {
902                                 case GL_DEPTH_STENCIL:          // GL_UNSIGNED_INT_24_8
903                                 case GL_DEPTH24_STENCIL8:       // GL_UNSIGNED_INT_24_8
904                                 {
905                                     for (uint y = 0; y < height; y++)
906                                     {
907                                         for (uint x = 0; x < width; x++)
908                                         {
909                                             uint ofs = (x * sizeof(uint32)) + (y * width * sizeof(uint32));
910                                             // I'm paranoid
911                                             if ((ofs < stencil_image_data.size()) && (ofs < temp_img.size()))
912                                             {
913                                                 uint8 *pSrc = stencil_image_data.get_ptr() + ofs;
914                                                 uint8 *pDest = temp_img.get_ptr() + ofs;
915
916                                                 pDest[0] = pSrc[0];
917                                             }
918                                         }
919                                     }
920                                     break;
921                                 }
922                                 case GL_DEPTH32F_STENCIL8:      // GL_FLOAT_32_UNSIGNED_INT_24_8_REV
923                                 case GL_DEPTH32F_STENCIL8_NV:   // GL_FLOAT_32_UNSIGNED_INT_24_8_REV
924                                 {
925                                     for (uint y = 0; y < height; y++)
926                                     {
927                                         for (uint x = 0; x < width; x++)
928                                         {
929                                             uint ofs = (x * sizeof(uint32)) + (y * width * sizeof(uint32));
930                                             // I'm paranoid
931                                             if ((ofs < stencil_image_data.size()) && ((ofs + 3) < temp_img.size()))
932                                             {
933                                                 uint8 *pSrc = stencil_image_data.get_ptr() + ofs;
934                                                 uint8 *pDest = temp_img.get_ptr() + ofs;
935
936                                                 pDest[3] = pSrc[0];
937                                             }
938                                         }
939                                     }
940
941                                     break;
942                                 }
943                                 default:
944                                 {
945                                     vogl_warning_printf("%s: Unable to set stencil data in texture %" PRIu64 "\n", VOGL_METHOD_NAME, (uint64_t)handle);
946                                     break;
947                                 }
948                             }
949
950                         }
951                     }
952
953                     if (ktx_tex_target == GL_TEXTURE_3D)
954                     {
955                         uint zslice_size = size_in_bytes;
956                         if (level_depth > 1)
957                         {
958                             VOGL_ASSERT((size_in_bytes % level_depth) == 0);
959                             zslice_size = size_in_bytes / level_depth;
960                             VOGL_ASSERT(zslice_size);
961                         }
962
963                         VOGL_ASSERT((size_in_bytes % zslice_size) == 0);
964
965                         uint cur_ofs = 0;
966                         for (int zslice = 0; zslice < level_depth; zslice++)
967                         {
968                             m_textures[sample_index].add_image(level, 0, face, zslice, temp_img.get_ptr() + cur_ofs, zslice_size);
969                             cur_ofs += zslice_size;
970                         }
971                         VOGL_ASSERT(static_cast<int>(cur_ofs) == size_in_bytes);
972                     }
973                     else if ((ktx_tex_target == GL_TEXTURE_1D_ARRAY) || (ktx_tex_target == GL_TEXTURE_2D_ARRAY))
974                     {
975                         VOGL_ASSERT(base_depth);
976                         uint num_array_elements = base_depth;
977
978                         if (ktx_tex_target == GL_TEXTURE_1D_ARRAY)
979                         {
980                             num_array_elements = base_height;
981                         }
982
983                         VOGL_ASSERT((size_in_bytes % num_array_elements) == 0);
984                         uint element_size = size_in_bytes / num_array_elements;
985                         VOGL_ASSERT(element_size);
986                         VOGL_ASSERT((size_in_bytes % element_size) == 0);
987
988                         uint cur_ofs = 0;
989                         for (uint array_index = 0; array_index < num_array_elements; array_index++)
990                         {
991                             m_textures[sample_index].add_image(level, array_index, face, 0, temp_img.get_ptr() + cur_ofs, element_size);
992                             cur_ofs += element_size;
993                         }
994                     }
995                     else
996                     {
997                         m_textures[sample_index].add_image_grant_ownership(level, 0, face, 0, temp_img);
998                     }
999
1000                 } // sample_index
1001
1002             } // if
1003
1004         } // level
1005     } // face
1006
1007     GL_ENTRYPOINT(glBindTexture)(target, m_snapshot_handle);
1008     VOGL_CHECK_GL_ERROR;
1009
1010     // TODO: Add more key/values?
1011     m_textures[0].add_key_value("VOGL_TARGET", dynamic_string(cVarArg, "%u", m_target).get_ptr());
1012     m_textures[0].add_key_value("VOGL_BASE_LEVEL", dynamic_string(cVarArg, "%u", m_params.get_value<int>(GL_TEXTURE_BASE_LEVEL)).get_ptr());
1013     m_textures[0].add_key_value("VOGL_MAX_LEVEL", dynamic_string(cVarArg, "%u", m_params.get_value<int>(GL_TEXTURE_MAX_LEVEL)).get_ptr());
1014
1015     bool x_flipped = false, y_flipped = true;
1016     dynamic_string ktx_orient_str(cVarArg, "S=%c,T=%c", x_flipped ? 'l' : 'r', y_flipped ? 'u' : 'd');
1017     m_textures[0].add_key_value("KTXorientation", ktx_orient_str.get_ptr());
1018
1019     if (!m_textures[0].consistency_check())
1020     {
1021         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));
1022         clear();
1023         VOGL_FREE_SPLIT_TEXTURES
1024         return false;
1025     }
1026
1027     VOGL_FREE_SPLIT_TEXTURES
1028
1029     m_is_valid = true;
1030
1031     return true;
1032 }
1033
1034 bool vogl_texture_state::set_tex_parameter(GLenum pname) const
1035 {
1036     VOGL_FUNC_TRACER
1037
1038     const vogl_state_data *pData = m_params.find(pname);
1039     if (!pData)
1040     {
1041         // We only return false on a GL error.
1042         return true;
1043     }
1044
1045     enum
1046     {
1047         cMaxElements = 16
1048     };
1049
1050     if (pData->get_num_elements() > cMaxElements)
1051     {
1052         VOGL_ASSERT_ALWAYS;
1053         return false;
1054     }
1055
1056     if ((pData->get_data_type() == cSTFloat) || (pData->get_data_type() == cSTDouble))
1057     {
1058         float fvals[16];
1059         pData->get_float(fvals);
1060         if (pData->get_num_elements() == 1)
1061             GL_ENTRYPOINT(glTexParameterf)(m_target, pname, fvals[0]);
1062         else
1063             GL_ENTRYPOINT(glTexParameterfv)(m_target, pname, fvals);
1064     }
1065     else
1066     {
1067         int ivals[16];
1068         pData->get_int(ivals);
1069         if (pData->get_num_elements() == 1)
1070             GL_ENTRYPOINT(glTexParameteri)(m_target, pname, ivals[0]);
1071         else
1072             GL_ENTRYPOINT(glTexParameteriv)(m_target, pname, ivals);
1073     }
1074
1075     return !vogl_check_gl_error();
1076 }
1077
1078 // Note: We'll need the remapper for buffer textures.
1079 bool vogl_texture_state::restore(const vogl_context_info &context_info, vogl_handle_remapper &remapper, GLuint64 &handle) const
1080 {
1081     VOGL_FUNC_TRACER
1082
1083     if (!m_is_valid)
1084         return false;
1085
1086     VOGL_CHECK_GL_ERROR;
1087
1088     vogl_msaa_texture_splitter splitter;
1089
1090     vogl_scoped_binding_state orig_bindings(GL_PIXEL_PACK_BUFFER, GL_PIXEL_UNPACK_BUFFER);
1091     orig_bindings.save_textures();
1092
1093     vogl_scoped_state_saver pixeltransfer_state_saver(cGSTPixelStore);
1094
1095     if (!context_info.is_core_profile())
1096         pixeltransfer_state_saver.save(cGSTPixelTransfer);
1097
1098     vogl_reset_pixel_store_states();
1099
1100     if (!context_info.is_core_profile())
1101         vogl_reset_pixel_transfer_states();
1102
1103     GL_ENTRYPOINT(glBindBuffer)(GL_PIXEL_PACK_BUFFER, 0);
1104     VOGL_CHECK_GL_ERROR;
1105
1106     GL_ENTRYPOINT(glBindBuffer)(GL_PIXEL_UNPACK_BUFFER, 0);
1107     VOGL_CHECK_GL_ERROR;
1108
1109     bool created_handle = false;
1110
1111     if (!handle)
1112     {
1113         GLuint handle32 = 0;
1114         GL_ENTRYPOINT(glGenTextures)(1, &handle32);
1115         if ((vogl_check_gl_error()) || (!handle32))
1116             return false;
1117         handle = handle32;
1118
1119         remapper.declare_handle(VOGL_NAMESPACE_TEXTURES, m_snapshot_handle, handle, m_target);
1120         if (!remapper.is_default_remapper())
1121         {
1122             VOGL_ASSERT(remapper.remap_handle(VOGL_NAMESPACE_TEXTURES, m_snapshot_handle) == handle);
1123         }
1124
1125         created_handle = true;
1126     }
1127
1128     if (m_target == GL_NONE)
1129     {
1130         // Texture has not been bound to a target yet, so we're done.
1131         return true;
1132     }
1133
1134     uint face = 0, level = 0;
1135     uint8_vec temp_img;
1136
1137     uint tex_width = 0, tex_height = 0, tex_depth = 0, total_actual_levels = 0, num_faces = 0;
1138     int base_level = 0, max_level = 0;
1139     VOGL_NOTE_UNUSED(base_level);
1140     VOGL_NOTE_UNUSED(max_level);
1141     bool is_immutable_format = false, is_compressed = false;
1142     GLenum internal_fmt = GL_NONE;
1143
1144     const ktx_texture &tex0 = m_textures[0];
1145
1146     GL_ENTRYPOINT(glBindTexture)(m_target, static_cast<uint32>(handle));
1147     if (vogl_check_gl_error())
1148         goto handle_error;
1149
1150     if (m_target == GL_TEXTURE_BUFFER)
1151     {
1152         if (m_buffer)
1153         {
1154             GLuint buffer_handle = static_cast<GLuint>(remapper.remap_handle(VOGL_NAMESPACE_BUFFERS, m_buffer));
1155             if (!buffer_handle)
1156             {
1157                 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);
1158                 return false;
1159             }
1160
1161             internal_fmt = m_params.get_value<int>(GL_TEXTURE_INTERNAL_FORMAT);
1162
1163             if (!internal_fmt)
1164             {
1165                 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);
1166                 return false;
1167             }
1168
1169             GL_ENTRYPOINT(glTexBuffer)(m_target, internal_fmt, buffer_handle);
1170             VOGL_CHECK_GL_ERROR;
1171         }
1172
1173         return true;
1174     }
1175
1176     bool any_failures;
1177     any_failures = false;
1178
1179 #define SET_INT(pname)                 \
1180     do                                 \
1181     {                                  \
1182         if (!set_tex_parameter(pname)) \
1183             any_failures = true;       \
1184     } while (0)
1185 #define SET_FLOAT(pname)               \
1186     do                                 \
1187     {                                  \
1188         if (!set_tex_parameter(pname)) \
1189             any_failures = true;       \
1190     } while (0)
1191     SET_INT(GL_TEXTURE_BASE_LEVEL);
1192     SET_INT(GL_TEXTURE_MAX_LEVEL);
1193     SET_FLOAT(GL_TEXTURE_BORDER_COLOR);
1194     SET_INT(GL_TEXTURE_COMPARE_MODE);
1195     SET_INT(GL_TEXTURE_COMPARE_FUNC);
1196     SET_INT(GL_TEXTURE_MIN_FILTER);
1197     SET_INT(GL_TEXTURE_MAG_FILTER);
1198     if (m_target != GL_TEXTURE_RECTANGLE)
1199     {
1200         SET_FLOAT(GL_TEXTURE_LOD_BIAS);
1201         SET_FLOAT(GL_TEXTURE_MIN_LOD);
1202         SET_FLOAT(GL_TEXTURE_MAX_LOD);
1203     }
1204     SET_INT(GL_TEXTURE_SWIZZLE_RGBA);
1205     SET_INT(GL_TEXTURE_WRAP_S);
1206     SET_INT(GL_TEXTURE_WRAP_T);
1207     SET_INT(GL_TEXTURE_WRAP_R);
1208
1209     if (context_info.supports_extension("GL_EXT_texture_filter_anisotropic"))
1210     {
1211         SET_FLOAT(GL_TEXTURE_MAX_ANISOTROPY_EXT);
1212     }
1213
1214     if (context_info.supports_extension("GL_EXT_texture_sRGB_decode"))
1215     {
1216         SET_INT(GL_TEXTURE_SRGB_DECODE_EXT);
1217     }
1218
1219     if (!context_info.is_core_profile() && context_info.supports_extension("GL_ARB_shadow_ambient"))
1220     {
1221         SET_FLOAT(GL_TEXTURE_COMPARE_FAIL_VALUE_ARB);
1222     }
1223
1224     if (!context_info.is_core_profile())
1225     {
1226         SET_INT(GL_DEPTH_TEXTURE_MODE);
1227     }
1228
1229     // TODO?
1230     // GL_TEXTURE_PRIORITY
1231     // GL_TEXTURE_RESIDENT
1232
1233     if ((m_is_unquerable) || (!m_textures[0].is_valid()))
1234     {
1235         if (m_params.get_value<int>(GL_GENERATE_MIPMAP))
1236         {
1237             GL_ENTRYPOINT(glGenerateMipmap)(m_target);
1238         }
1239
1240         return true;
1241     }
1242
1243     if (m_num_samples < 1)
1244         return false;
1245
1246     tex_width = tex0.get_width();
1247     tex_height = tex0.get_height();
1248     tex_depth = tex0.get_depth();
1249     total_actual_levels = tex0.get_num_mips();
1250     base_level = m_params.get_value<int>(GL_TEXTURE_BASE_LEVEL);
1251     max_level = m_params.get_value<int>(GL_TEXTURE_MAX_LEVEL);
1252
1253     is_immutable_format = m_params.get_value<int>(GL_TEXTURE_IMMUTABLE_FORMAT) != 0;
1254     is_compressed = tex0.is_compressed();
1255     internal_fmt = tex0.get_ogl_internal_fmt();
1256
1257     num_faces = (m_target == GL_TEXTURE_CUBE_MAP) ? cCubeMapFaces : 1;
1258
1259     // Sanity checking
1260     for (uint sample_index = 1; sample_index < m_num_samples; sample_index++)
1261     {
1262         const ktx_texture &cmp_tex = m_textures[sample_index];
1263
1264         if ( (!cmp_tex.is_valid()) || (tex_width != cmp_tex.get_width()) || (tex_height != cmp_tex.get_height()) || (tex_depth != cmp_tex.get_depth()) ||
1265              (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()) ||
1266              (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()) )
1267         {
1268             vogl_error_printf("%s: MSAA consistency error\n", VOGL_METHOD_NAME);
1269
1270             VOGL_ASSERT_ALWAYS;
1271             goto handle_error;
1272         }
1273     }
1274
1275     // TODO: Support immutable textures, incomplete textures, etc. Lots more to do here.
1276     if (is_immutable_format)
1277     {
1278         vogl_warning_printf("%s: TODO: Support immutable textures\n", VOGL_METHOD_NAME);
1279     }
1280
1281     for (face = 0; face < num_faces; face++)
1282     {
1283         for (level = 0; level < total_actual_levels; level++)
1284         {
1285             const vogl_state_vector &level_params = m_level_params[face][level];
1286
1287             if (!level_params.find(GL_TEXTURE_WIDTH))
1288                 continue;
1289
1290             int level_width = level_params.get_value<int>(GL_TEXTURE_WIDTH);
1291             int level_height = level_params.get_value<int>(GL_TEXTURE_HEIGHT);
1292             int level_depth = level_params.get_value<int>(GL_TEXTURE_DEPTH);
1293             int level_samples = level_params.get_value<int>(GL_TEXTURE_SAMPLES);
1294
1295             GLenum level_internal_fmt = level_params.get_value<int>(GL_TEXTURE_INTERNAL_FORMAT);
1296
1297             if ((level_width < 1) || (level_height < 1) || (level_depth < 1) || (level_internal_fmt != internal_fmt))
1298             {
1299                 vogl_error_printf("%s: Consistency error\n", VOGL_METHOD_NAME);
1300                 VOGL_ASSERT_ALWAYS;
1301                 goto handle_error;
1302             }
1303
1304             if ((m_target == GL_TEXTURE_2D_MULTISAMPLE) || (m_target == GL_TEXTURE_2D_MULTISAMPLE_ARRAY))
1305             {
1306                 if ((is_compressed) || (level_samples != static_cast<int>(m_num_samples)) || (level != 0))
1307                 {
1308                     vogl_error_printf("%s: Multisampled texture consistency error\n", VOGL_METHOD_NAME);
1309                     VOGL_ASSERT_ALWAYS;
1310                     goto handle_error;
1311                 }
1312
1313                 if (m_target == GL_TEXTURE_2D_MULTISAMPLE)
1314                 {
1315                     GL_ENTRYPOINT(glTexImage2DMultisample)(m_target, m_num_samples, internal_fmt, level_width, level_height, level_params.get_value<GLenum>(GL_TEXTURE_FIXED_SAMPLE_LOCATIONS));
1316                     VOGL_CHECK_GL_ERROR;
1317                 }
1318                 else if (m_target == GL_TEXTURE_2D_MULTISAMPLE_ARRAY)
1319                 {
1320                     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));
1321                     VOGL_CHECK_GL_ERROR;
1322                 }
1323                 else
1324                 {
1325                     vogl_error_printf("%s: Unexpected target error\n", VOGL_METHOD_NAME);
1326                     VOGL_ASSERT_ALWAYS;
1327                     goto handle_error;
1328                 }
1329
1330                 // Note: This changes the active GL context to a work context!
1331                 if (!splitter.init())
1332                 {
1333                     vogl_error_printf("%s: Failed initializing texture splitter object!\n", VOGL_METHOD_NAME);
1334                     VOGL_ASSERT_ALWAYS;
1335                     goto handle_error;
1336                 }
1337
1338                 for (uint sample_index = 0; sample_index < m_num_samples; sample_index++)
1339                 {
1340                     const uint8_vec &src_img = m_textures[sample_index].get_image_data(level, 0, face, 0);
1341                     VOGL_ASSERT(src_img.size());
1342
1343                     GLuint src_texture = 0;
1344                     GL_ENTRYPOINT(glGenTextures)(1, &src_texture);
1345                     VOGL_CHECK_GL_ERROR;
1346
1347                     const GLenum src_target = (m_target == GL_TEXTURE_2D_MULTISAMPLE_ARRAY) ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D;
1348
1349                     GL_ENTRYPOINT(glBindTexture)(src_target, src_texture);
1350                     VOGL_CHECK_GL_ERROR;
1351
1352                     GL_ENTRYPOINT(glTexParameteri)(src_target, GL_TEXTURE_MAX_LEVEL, 0);
1353                     GL_ENTRYPOINT(glTexParameteri)(src_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1354                     GL_ENTRYPOINT(glTexParameteri)(src_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1355                     VOGL_CHECK_GL_ERROR;
1356
1357                     if (src_target == GL_TEXTURE_2D_ARRAY)
1358                     {
1359                         uint array_size = tex0.get_array_size();
1360
1361                         temp_img.resize(0);
1362                         temp_img.reserve(src_img.size() * array_size);
1363
1364                         for (uint array_index = 0; array_index < array_size; array_index++)
1365                         {
1366                             temp_img.append(m_textures[sample_index].get_image_data(level, array_index, face, 0));
1367                         }
1368
1369                         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());
1370                         VOGL_CHECK_GL_ERROR;
1371                     }
1372                     else
1373                     {
1374                         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());
1375                         VOGL_CHECK_GL_ERROR;
1376                     }
1377
1378                     bool status = splitter.combine(src_texture, sample_index, m_target, static_cast<uint32>(handle));
1379
1380                     GL_ENTRYPOINT(glBindTexture)(src_target, 0);
1381                     VOGL_CHECK_GL_ERROR;
1382
1383                     GL_ENTRYPOINT(glDeleteTextures)(1, &src_texture);
1384                     VOGL_CHECK_GL_ERROR;
1385
1386                     if (!status)
1387                     {
1388                         splitter.deinit();
1389
1390                         goto handle_error;
1391                     }
1392
1393                     // Check if dest texture has stencil
1394                     bool has_stencil = utils::is_in_set<GLenum, GLenum>(internal_fmt, GL_DEPTH_STENCIL, GL_DEPTH24_STENCIL8, GL_DEPTH32F_STENCIL8, GL_DEPTH32F_STENCIL8_NV);
1395                     bool is_reversed_fmt = utils::is_in_set<GLenum, GLenum>(internal_fmt, GL_DEPTH32F_STENCIL8, GL_DEPTH32F_STENCIL8_NV);
1396
1397                     if (has_stencil)
1398                     {
1399                         GLenum temp_color_format = GL_RGBA;
1400                         GLenum temp_color_type = is_reversed_fmt ? GL_UNSIGNED_INT_8_8_8_8 : GL_UNSIGNED_INT_8_8_8_8_REV;
1401
1402                         GLuint temp_color_texture = 0;
1403                         GL_ENTRYPOINT(glGenTextures)(1, &temp_color_texture);
1404                         VOGL_CHECK_GL_ERROR;
1405
1406                         const GLenum src_target = (m_target == GL_TEXTURE_2D_MULTISAMPLE_ARRAY) ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D;
1407
1408                         GL_ENTRYPOINT(glBindTexture)(src_target, temp_color_texture);
1409                         VOGL_CHECK_GL_ERROR;
1410
1411                         GL_ENTRYPOINT(glTexParameteri)(src_target, GL_TEXTURE_MAX_LEVEL, 0);
1412                         GL_ENTRYPOINT(glTexParameteri)(src_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1413                         GL_ENTRYPOINT(glTexParameteri)(src_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1414                         VOGL_CHECK_GL_ERROR;
1415
1416                         if (src_target == GL_TEXTURE_2D_ARRAY)
1417                         {
1418                             uint array_size = tex0.get_array_size();
1419
1420                             temp_img.resize(0);
1421                             temp_img.reserve(src_img.size() * array_size);
1422
1423                             for (uint array_index = 0; array_index < array_size; array_index++)
1424                             {
1425                                 temp_img.append(m_textures[sample_index].get_image_data(level, array_index, face, 0));
1426                             }
1427
1428                             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());
1429                             VOGL_CHECK_GL_ERROR;
1430                         }
1431                         else
1432                         {
1433                             GL_ENTRYPOINT(glTexImage2D)(src_target, level, GL_RGBA, level_width, level_height, 0, temp_color_format, temp_color_type, src_img.get_ptr());
1434                             VOGL_CHECK_GL_ERROR;
1435                         }
1436
1437                         bool status = splitter.copy_color_sample_to_stencil(temp_color_texture, sample_index, m_target, static_cast<uint32>(handle));
1438
1439                         GL_ENTRYPOINT(glBindTexture)(src_target, 0);
1440                         VOGL_CHECK_GL_ERROR;
1441
1442                         GL_ENTRYPOINT(glDeleteTextures)(1, &temp_color_texture);
1443                         VOGL_CHECK_GL_ERROR;
1444
1445                         if (!status)
1446                         {
1447                             splitter.deinit();
1448
1449                             vogl_error_printf("%s: Failed copying MSAA stencil samples from temp color texture to stencil", VOGL_METHOD_NAME);
1450
1451                             goto handle_error;
1452                         }
1453                     }
1454                 }
1455
1456                 splitter.deinit();
1457             }
1458             else
1459             {
1460                 GLenum target_to_set = m_target;
1461                 if (m_target == GL_TEXTURE_CUBE_MAP)
1462                     target_to_set = GL_TEXTURE_CUBE_MAP_POSITIVE_X + face;
1463
1464                 if (level_samples > 1)
1465                 {
1466                     vogl_error_printf("%s: GL_TEXTURE_SAMPLES consistency error\n", VOGL_METHOD_NAME);
1467                     VOGL_ASSERT_ALWAYS;
1468                     goto handle_error;
1469                 }
1470
1471                 const uint8_vec &src_img = tex0.get_image_data(level, 0, face, 0);
1472                 VOGL_ASSERT(src_img.size());
1473
1474                 switch (m_target)
1475                 {
1476                     case GL_TEXTURE_1D:
1477                     {
1478                         if (is_compressed)
1479                         {
1480                             GL_ENTRYPOINT(glCompressedTexImage1D)(target_to_set, level, level_internal_fmt, level_width, 0, src_img.size(), src_img.get_ptr());
1481                         }
1482                         else
1483                         {
1484                             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());
1485                         }
1486
1487                         break;
1488                     }
1489                     case GL_TEXTURE_2D:
1490                     case GL_TEXTURE_RECTANGLE:
1491                     case GL_TEXTURE_CUBE_MAP:
1492                     case GL_TEXTURE_1D_ARRAY:
1493                     {
1494                         if (m_target == GL_TEXTURE_1D_ARRAY)
1495                         {
1496                             uint array_size = tex0.get_array_size();
1497                             for (uint array_index = 0; array_index < array_size; array_index++)
1498                             {
1499                                 temp_img.append(tex0.get_image_data(level, array_index, face, 0));
1500                             }
1501                             level_height = array_size;
1502                         }
1503                         else
1504                         {
1505                             temp_img = src_img;
1506                         }
1507
1508                         if (is_compressed)
1509                         {
1510                             GL_ENTRYPOINT(glCompressedTexImage2D)(target_to_set, level, level_internal_fmt, level_width, level_height, 0, src_img.size(), temp_img.get_ptr());
1511                         }
1512                         else
1513                         {
1514                             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());
1515                         }
1516
1517                         break;
1518                     }
1519                     case GL_TEXTURE_2D_ARRAY:
1520                     case GL_TEXTURE_3D:
1521                     {
1522                         temp_img.resize(0);
1523                         temp_img.reserve(src_img.size() * level_depth);
1524
1525                         if (m_target == GL_TEXTURE_3D)
1526                         {
1527                             for (int zslice = 0; zslice < level_depth; zslice++)
1528                             {
1529                                 temp_img.append(tex0.get_image_data(level, 0, face, zslice));
1530                             }
1531                         }
1532                         else
1533                         {
1534                             // 2D_ARRAY
1535                             uint array_size = tex0.get_array_size();
1536                             for (uint array_index = 0; array_index < array_size; array_index++)
1537                             {
1538                                 temp_img.append(tex0.get_image_data(level, array_index, face, 0));
1539                             }
1540                             level_depth = array_size;
1541                         }
1542
1543                         if (is_compressed)
1544                         {
1545                             GL_ENTRYPOINT(glCompressedTexImage3D)(target_to_set, level, level_internal_fmt, level_width, level_height, level_depth, 0, temp_img.size(), temp_img.get_ptr());
1546                         }
1547                         else
1548                         {
1549                             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());
1550                         }
1551
1552                         break;
1553                     }
1554                     default:
1555                     {
1556                         VOGL_ASSERT_ALWAYS;
1557                         vogl_error_printf("%s: Unsupported target type %d\n", VOGL_METHOD_NAME, m_target);
1558                         goto handle_error;
1559                     }
1560                 }
1561
1562                 if (vogl_check_gl_error())
1563                 {
1564                     vogl_error_printf("%s: Failed creating texture image\n", VOGL_METHOD_NAME);
1565                     goto handle_error;
1566                 }
1567
1568             } // sample_index
1569         } // level
1570     } // face
1571
1572     if (m_params.get_value<int>(GL_GENERATE_MIPMAP))
1573     {
1574         GL_ENTRYPOINT(glGenerateMipmap)(m_target);
1575         vogl_debug_printf("%s: Generating mipmaps for texture, snapshot handle %u GL handle %u\n", VOGL_METHOD_NAME, m_snapshot_handle, (uint)handle);
1576     }
1577
1578 #undef SET_INT
1579 #undef SET_FLOAT
1580
1581     if (any_failures)
1582     {
1583         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,
1584                            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));
1585     }
1586
1587     return true;
1588
1589 handle_error:
1590     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,
1591                      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));
1592
1593     GL_ENTRYPOINT(glBindTexture)(m_target, 0);
1594     VOGL_CHECK_GL_ERROR;
1595
1596     if (created_handle)
1597     {
1598         remapper.delete_handle_and_object(VOGL_NAMESPACE_TEXTURES, m_snapshot_handle, handle);
1599
1600         handle = 0;
1601     }
1602
1603     return false;
1604 }
1605
1606 bool vogl_texture_state::remap_handles(vogl_handle_remapper &remapper)
1607 {
1608     VOGL_FUNC_TRACER
1609
1610     if (!m_is_valid)
1611         return false;
1612
1613     m_snapshot_handle = static_cast<uint32>(remapper.remap_handle(VOGL_NAMESPACE_TEXTURES, m_snapshot_handle));
1614
1615     if (m_buffer)
1616     {
1617         m_buffer = static_cast<GLuint>(remapper.remap_handle(VOGL_NAMESPACE_BUFFERS, m_buffer));
1618     }
1619
1620     return true;
1621 }
1622
1623 void vogl_texture_state::clear()
1624 {
1625     VOGL_FUNC_TRACER
1626
1627     m_snapshot_handle = 0;
1628     m_target = GL_NONE;
1629     m_buffer = 0;
1630
1631     m_num_samples = 0;
1632
1633     for (uint i = 0; i < cMaxSamples; i++)
1634         m_textures[i].clear();
1635
1636     m_params.clear();
1637     for (uint i = 0; i < VOGL_ARRAY_SIZE(m_level_params); i++)
1638         m_level_params[i].clear();
1639
1640     m_is_unquerable = false;
1641     m_is_valid = false;
1642 }
1643
1644 bool vogl_texture_state::serialize(json_node &node, vogl_blob_manager &blob_manager) const
1645 {
1646     VOGL_FUNC_TRACER
1647
1648     if (!m_is_valid)
1649         return false;
1650
1651     node.add_key_value("version", VOGL_SERIALIZED_TEXTURE_STATE_VERSION);
1652     node.add_key_value("handle", m_snapshot_handle);
1653     node.add_key_value("target", g_gl_enums.find_gl_name(m_target));
1654     node.add_key_value("is_unquerable", m_is_unquerable);
1655     node.add_key_value("buffer", m_buffer);
1656     node.add_key_value("samples", m_num_samples);
1657
1658     if ((!m_is_unquerable) && (m_target != GL_NONE))
1659     {
1660         json_node &tex_params_obj = node.add_object("tex_params");
1661         if (!m_params.serialize(tex_params_obj, blob_manager))
1662             return false;
1663
1664         if ((m_target != GL_TEXTURE_BUFFER) && (m_num_samples))
1665         {
1666             json_node &textures_array_node = node.add_array("textures");
1667
1668             for (uint sample_index = 0; sample_index < m_num_samples; sample_index++)
1669             {
1670                 json_node &texture_node = textures_array_node.add_object();
1671
1672                 const ktx_texture &tex = m_textures[sample_index];
1673
1674                 const char *pTex_type = utils::map_value(static_cast<int>(m_target), "tex",
1675                                                          GL_TEXTURE_1D, "tex_1d",
1676                                                          GL_TEXTURE_2D, "tex_2d",
1677                                                          GL_TEXTURE_3D, "tex_3d",
1678                                                          GL_TEXTURE_CUBE_MAP, "tex_cube",
1679                                                          GL_TEXTURE_RECTANGLE, "tex_rect",
1680                                                          GL_TEXTURE_2D_ARRAY, "tex_2d_array",
1681                                                          GL_TEXTURE_1D_ARRAY, "tex_1d_array",
1682                                                          GL_TEXTURE_BUFFER, "tex_buffer",
1683                                                          GL_TEXTURE_2D_MULTISAMPLE, "tex_2d_multisample",
1684                                                          GL_TEXTURE_2D_MULTISAMPLE_ARRAY, "tex_2d_multisample_array",
1685                                                          GL_TEXTURE_CUBE_MAP_ARRAY, "tex_cube_array");
1686
1687                 uint actual_mip_levels = m_params.get_value<GLint>(GL_TEXTURE_MAX_LEVEL) + 1;
1688                 if (tex.is_valid())
1689                     actual_mip_levels = math::minimum(actual_mip_levels, tex.get_num_mips());
1690
1691                 dynamic_string prefix;
1692                 switch (m_target)
1693                 {
1694                     case GL_TEXTURE_1D:
1695                         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()));
1696                         break;
1697                     case GL_TEXTURE_RECTANGLE:
1698                     case GL_TEXTURE_2D:
1699                         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()));
1700                         break;
1701                     case GL_TEXTURE_3D:
1702                         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()));
1703                         break;
1704                     case GL_TEXTURE_1D_ARRAY:
1705                         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()));
1706                         break;
1707                     case GL_TEXTURE_2D_ARRAY:
1708                         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()));
1709                         break;
1710                     case GL_TEXTURE_2D_MULTISAMPLE:
1711                         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()));
1712                         break;
1713                     case GL_TEXTURE_2D_MULTISAMPLE_ARRAY:
1714                         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()));
1715                         break;
1716                     case GL_TEXTURE_CUBE_MAP:
1717                         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()));
1718                         break;
1719                     case GL_TEXTURE_CUBE_MAP_ARRAY:
1720                         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()));
1721                         break;
1722                     default:
1723                         VOGL_ASSERT_ALWAYS;
1724                         return false;
1725                 }
1726
1727                 dynamic_stream dyn_stream;
1728                 data_stream_serializer serializer(dyn_stream);
1729                 if (!tex.write_to_stream(serializer))
1730                     return false;
1731
1732                 dyn_stream.seek(0, false);
1733
1734                 dynamic_string blob_id(blob_manager.add_stream_compute_unique_id(dyn_stream, prefix.get_ptr(), "ktx"));
1735                 if (blob_id.is_empty())
1736                     return false;
1737
1738                 dyn_stream.close();
1739
1740                 texture_node.add_key_value("texture_data_blob_id", blob_id);
1741             }
1742
1743             json_node &level_params_array = node.add_array("level_params");
1744             for (uint face = 0; face < m_textures[0].get_num_faces(); face++)
1745             {
1746                 for (uint level = 0; level < m_textures[0].get_num_mips(); level++)
1747                 {
1748                     json_node &level_param_array_value = level_params_array.add_object();
1749                     if (m_target == GL_TEXTURE_CUBE_MAP)
1750                         level_param_array_value.add_key_value("face", face);
1751                     level_param_array_value.add_key_value("level", level);
1752
1753                     json_node &params_node = level_param_array_value.add_object("params");
1754                     if (!m_level_params[face][level].serialize(params_node, blob_manager))
1755                         return false;
1756                 }
1757             }
1758         }
1759     }
1760
1761     return true;
1762 }
1763
1764 bool vogl_texture_state::deserialize(const json_node &node, const vogl_blob_manager &blob_manager)
1765 {
1766     VOGL_FUNC_TRACER
1767
1768     clear();
1769
1770     if ((!node.has_key("handle")) || (!node.has_key("target")))
1771         return false;
1772
1773     m_snapshot_handle = node.value_as_uint32("handle");
1774     m_target = vogl_get_json_value_as_enum(node, "target");
1775     m_is_unquerable = node.value_as_bool("is_unquerable");
1776     m_buffer = node.value_as_uint32("buffer");
1777     m_num_samples = node.value_as_uint32("samples", 1);
1778
1779     // Sanity check.
1780     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,
1781         GL_TEXTURE_1D_ARRAY, GL_TEXTURE_2D_ARRAY, GL_TEXTURE_RECTANGLE, GL_TEXTURE_BUFFER, GL_TEXTURE_2D_MULTISAMPLE, GL_TEXTURE_2D_MULTISAMPLE_ARRAY))
1782     {
1783         return false;
1784     }
1785
1786     const json_node *pTex_params_obj = node.find_child_object("tex_params");
1787     if (pTex_params_obj)
1788     {
1789         if (!m_params.deserialize(*pTex_params_obj, blob_manager))
1790             return false;
1791     }
1792
1793     if ((!m_is_unquerable) && (m_target != GL_NONE) && (m_target != GL_TEXTURE_BUFFER))
1794     {
1795         if (node.has_key("texture_data_blob_id"))
1796         {
1797             if (m_num_samples != 1)
1798                 return false;
1799
1800             dynamic_string blob_id(node.value_as_string_ptr("texture_data_blob_id"));
1801             if (blob_id.is_empty())
1802                 return false;
1803
1804             dynamic_stream tex_data;
1805             if (!blob_manager.get(blob_id, tex_data.get_buf()))
1806                 return false;
1807
1808             data_stream_serializer serializer(&tex_data);
1809             if (!m_textures[0].read_from_stream(serializer))
1810                 return false;
1811         }
1812         else if (node.has_array("textures"))
1813         {
1814             const json_node *pTextures_array_node = node.find_child_array("textures");
1815             if ((!pTextures_array_node) || (!pTextures_array_node->size()) || (pTextures_array_node->size() > cMaxSamples))
1816                 return false;
1817
1818             for (uint i = 0; i < pTextures_array_node->size(); i++)
1819             {
1820                 const json_node *pTexture_node = pTextures_array_node->get_child(i);
1821                 if (!pTexture_node)
1822                     return false;
1823
1824                 dynamic_string blob_id(pTexture_node->value_as_string_ptr("texture_data_blob_id"));
1825                 if (blob_id.is_empty())
1826                     return false;
1827
1828                 dynamic_stream tex_data;
1829                 if (!blob_manager.get(blob_id, tex_data.get_buf()))
1830                     return false;
1831
1832                 data_stream_serializer serializer(&tex_data);
1833                 if (!m_textures[i].read_from_stream(serializer))
1834                     return false;
1835             }
1836         }
1837         else
1838         {
1839             return false;
1840         }
1841
1842         const json_node *pLevel_params_array = node.find_child_array("level_params");
1843         if (pLevel_params_array)
1844         {
1845             for (uint i = 0; i < pLevel_params_array->size(); i++)
1846             {
1847                 const json_node *pLevel_params_node = pLevel_params_array->get_value_as_object(i);
1848                 if (!pLevel_params_node)
1849                     return false;
1850
1851                 uint face = pLevel_params_node->value_as_uint32("face");
1852                 if (face)
1853                 {
1854                     if ((m_target != GL_TEXTURE_CUBE_MAP) || (face > cCubeMapFaces))
1855                         return false;
1856                 }
1857
1858                 uint level = pLevel_params_node->value_as_uint32("level");
1859                 // obviously crazy level
1860                 if (level > 20)
1861                     return false;
1862
1863                 if (level >= m_level_params[face].size())
1864                     m_level_params[face].resize(level + 1);
1865
1866                 const json_node *pParams_node = pLevel_params_node->find_child_object("params");
1867                 if (!pParams_node)
1868                     return false;
1869
1870                 if (!m_level_params[face][level].deserialize(*pParams_node, blob_manager))
1871                     return false;
1872             }
1873         }
1874
1875
1876     }
1877
1878     m_is_valid = true;
1879
1880     return true;
1881 }
1882
1883 bool vogl_texture_state::compare_restorable_state(const vogl_gl_object_state &rhs_obj) const
1884 {
1885     VOGL_FUNC_TRACER
1886
1887     if ((!m_is_valid) || (!rhs_obj.is_valid()))
1888         return false;
1889
1890     if (rhs_obj.get_type() != cGLSTTexture)
1891         return false;
1892
1893     const vogl_texture_state &rhs = static_cast<const vogl_texture_state &>(rhs_obj);
1894
1895     if (this == &rhs)
1896         return true;
1897
1898 #define CMP(x)      \
1899     if (x != rhs.x) \
1900         return false;
1901     CMP(m_is_unquerable);
1902     CMP(m_target);
1903     CMP(m_params);
1904     CMP(m_buffer);
1905
1906     for (uint i = 0; i < cCubeMapFaces; i++)
1907         CMP(m_level_params[i]);
1908
1909     CMP(m_num_samples);
1910
1911     for (uint i = 0; i < m_num_samples; i++)
1912     {
1913         if (m_textures[i].is_valid() != rhs.m_textures[i].is_valid())
1914             return false;
1915         if (m_textures[i] != rhs.m_textures[i])
1916             return false;
1917     }
1918 #undef CMP
1919
1920     return true;
1921 }
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938