Пример #1
0
static dynamic_string
getfullpath(const char *filename)
{
    char buf[PATH_MAX];

    // Get the full path to our executable. It should be running in vogl/bin/x86_64 (or i386).
    if ((readlink("/proc/self/exe", buf, sizeof(buf)) <= 0))
        errorf(VOGL_FUNCTION_INFO_CSTR, "ERROR: readlink failed '%s.'\n", strerror(errno));

    // Get just the directory name and relative path to libvogltrace.so.
    dynamic_string filedir = dirname(buf);

    filedir += "/";
    filedir += filename;

    // Trim all the relative path parts.
    char *vogltracepath = realpath(filedir.c_str(), NULL);
    if (!vogltracepath)
    {
        vogl_warning_printf("\nWARNING: realpath failed: '%s.' Does '%s' exist?\n", strerror(errno), filename);
        return filename;
    }

    // Make sure our libvogltrace.so exists.
    if(access(vogltracepath, F_OK) != 0)
    {
        vogl_warning_printf("\nWARNING: %s file does not exist.\n", vogltracepath);
        return filename;
    }

    return vogltracepath;
}
Пример #2
0
bool vogleditor_traceReplayer::applying_snapshot_and_process_resize(const vogl_gl_state_snapshot* pSnapshot)
{
    vogl_gl_replayer::status_t status = m_pTraceReplayer->begin_applying_snapshot(pSnapshot, false);

    bool bStatus = true;
    while (status == vogl_gl_replayer::cStatusResizeWindow)
    {
        vogl_warning_printf("%s: Waiting for window to resize\n", VOGL_METHOD_NAME);

        // Pump X events in case the window is resizing
        if (process_x_events())
        {
            status = m_pTraceReplayer->process_pending_window_resize();
        }
        else
        {
            bStatus = false;
            break;
        }
    }

    if (bStatus && status != vogl_gl_replayer::cStatusOK)
    {
        vogl_error_printf("%s: Replay unable to apply snapshot\n", VOGL_FUNCTION_NAME);
        bStatus = false;
    }

    return bStatus;
}
Пример #3
0
	char *file_utils::get_exec_filename(char *pPath, size_t dest_len)
	{
	#if VOGL_HAS_PROC_FILESYSTEM
		ssize_t s = readlink("/proc/self/exe", pPath, dest_len);
		if (s >= 0)
		{
			pPath[s] = '\0';
			return pPath;
		}

		VOGL_VERIFY(0);
		pPath[0] = '\0';

		return pPath;

	#elif defined(VOGL_USE_OSX_API)
		char **argv = *_NSGetArgv();
		
		strncpy(pPath, argv[0], dest_len);
	
		vogl_warning_printf("UNTESTED: file_utils::get_exec_filename returning [%s]\n", pPath);
		return pPath;
	
	#elif defined(VOGL_USE_WIN32_API)
		DWORD result = GetModuleFileNameA(0, pPath, safe_int_cast<DWORD>(dest_len));
		VOGL_VERIFY(result != 0);
		return pPath;
	#else
		VOGL_ASSUME(!"Implement get_exec_filename for this platform.");
	#endif
	}
Пример #4
0
bool vogl_vao_state::restore(const vogl_context_info &context_info, vogl_handle_remapper &remapper, GLuint64 &handle) const
{
    VOGL_FUNC_TRACER

    VOGL_CHECK_GL_ERROR;

    if (!m_is_valid)
        return false;

    vogl_scoped_binding_state orig_binding(GL_VERTEX_ARRAY, GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER);

    if ((!m_snapshot_handle) && (!handle))
    {
        GL_ENTRYPOINT(glBindVertexArray)(0);
        VOGL_CHECK_GL_ERROR;
    }
    else
    {
        if (!handle)
        {
            GLuint handle32 = 0;
            GL_ENTRYPOINT(glGenVertexArrays)(1, &handle32);
            if ((vogl_check_gl_error()) || (!handle32))
                return false;
            handle = handle32;

            if (m_snapshot_handle)
            {
                remapper.declare_handle(VOGL_NAMESPACE_VERTEX_ARRAYS, m_snapshot_handle, handle, GL_NONE);
                VOGL_ASSERT(remapper.remap_handle(VOGL_NAMESPACE_VERTEX_ARRAYS, m_snapshot_handle) == handle);
            }
        }

        if (m_has_been_bound)
        {
            GL_ENTRYPOINT(glBindVertexArray)(static_cast<GLuint>(handle));
            VOGL_CHECK_GL_ERROR;
        }
    }

    if (m_has_been_bound)
    {
        GL_ENTRYPOINT(glBindBuffer)(GL_ELEMENT_ARRAY_BUFFER, static_cast<GLuint>(remapper.remap_handle(VOGL_NAMESPACE_BUFFERS, m_element_array_binding)));
        VOGL_CHECK_GL_ERROR;

        if (m_vertex_attribs.size() > context_info.get_max_vertex_attribs())
        {
            vogl_warning_printf("Saved VAO state has %u attribs, but context only allows %u attribs\n", m_vertex_attribs.size(), context_info.get_max_vertex_attribs());
        }

        for (uint32_t i = 0; i < math::minimum<uint32_t>(context_info.get_max_vertex_attribs(), m_vertex_attribs.size()); i++)
        {
            const vogl_vertex_attrib_desc &desc = m_vertex_attribs[i];

            GL_ENTRYPOINT(glBindBuffer)(GL_ARRAY_BUFFER, static_cast<GLuint>(remapper.remap_handle(VOGL_NAMESPACE_BUFFERS, desc.m_array_binding)));
            VOGL_CHECK_GL_ERROR;

            vogl_trace_ptr_value trace_ptr_val = desc.m_pointer;

            vogl_trace_ptr_value restore_ptr_val = trace_ptr_val;
            if ((!desc.m_array_binding) && (trace_ptr_val) && (context_info.is_compatibility_profile()))
                restore_ptr_val = remapper.remap_vertex_attrib_ptr(i, trace_ptr_val);

            void *pRestore_ptr = reinterpret_cast<void *>(restore_ptr_val);

            if ((handle) && (desc.m_array_binding == 0))
            {
                // If it's a non-default VAO, and there's no array binding, we can't call glVertexAttribPointer() because it's not allowed by AMD drivers (it thinks we're trying to set client side array data)
                // "OpenGL: glVertexAttribPointer failed because it is not allowed to specify a client-side vertex or element array when a non-default vertex array object is bound (GL_INVALID_OPERATION) [source=API type=ERROR severity=HIGH id=2100]"

                // Sanity checks.
                if ((pRestore_ptr != NULL) || (desc.m_stride) || (desc.m_enabled))
                {
                    vogl_warning_printf("Can't bind client side vertex array data on a non-default VAO, trace handle %u GL handle %u, restore ptr %p, size %i stride %i enabled %u\n",
                                        m_snapshot_handle, static_cast<uint32_t>(handle), pRestore_ptr, desc.m_size, desc.m_stride, desc.m_enabled);
                }
            }
            else
            {
                if (desc.m_integer)
                {
                    GL_ENTRYPOINT(glVertexAttribIPointer)(i, desc.m_size, desc.m_type, desc.m_stride, pRestore_ptr);
                    VOGL_CHECK_GL_ERROR;
                }
                else
                {
                    GL_ENTRYPOINT(glVertexAttribPointer)(i, desc.m_size, desc.m_type, desc.m_normalized, desc.m_stride, pRestore_ptr);
                    VOGL_CHECK_GL_ERROR;
                }
            }

            GL_ENTRYPOINT(glVertexAttribDivisor)(i, desc.m_divisor);
            VOGL_CHECK_GL_ERROR;

            if (desc.m_enabled)
            {
                GL_ENTRYPOINT(glEnableVertexAttribArray)(i);
                VOGL_CHECK_GL_ERROR;
            }
            else
            {
                GL_ENTRYPOINT(glDisableVertexAttribArray)(i);
                VOGL_CHECK_GL_ERROR;
            }
        }
    }

    return true;
}
// TODO: return bool
void vogleditor_QApiCallTreeModel::setupModelData(vogl_trace_file_reader* pTrace_reader, vogleditor_apiCallTreeItem *parent)
{
   const vogl_trace_stream_start_of_file_packet &sof_packet = pTrace_reader->get_sof_packet();
   VOGL_NOTE_UNUSED(sof_packet);

   uint64_t total_swaps = 0;
   // TODO probably need to handle eof_packet
   //bool found_eof_packet = false;

   // make a PendingFrame node to hold the api calls
   // this will remain in the pending state until the first
   // api call is seen, then it will be made the CurFrame and
   // appended to the parent
   vogleditor_frameItem* pCurFrame = NULL;
   vogleditor_apiCallTreeItem* pCurParent = parent;

   // Make a PendingSnapshot that may or may not be populated when reading the trace.
   // This snapshot will be assigned to the next API call that occurs.
   vogleditor_gl_state_snapshot* pPendingSnapshot = NULL;

   m_trace_ctypes.init(pTrace_reader->get_sof_packet().m_pointer_sizes);

   for ( ; ; )
   {
      vogl_trace_file_reader::trace_file_reader_status_t read_status = pTrace_reader->read_next_packet();

      if ((read_status != vogl_trace_file_reader::c*K) && (read_status != vogl_trace_file_reader::cEOF))
      {
         vogl_error_printf("Failed reading from trace file!\n");
         break;
      }

      if (read_status == vogl_trace_file_reader::cEOF)
      {
         vogl_printf("At trace file EOF on swap %" PRIu64 "\n", total_swaps);
         break;
      }

      const vogl::vector<uint8> &packet_buf = pTrace_reader->get_packet_buf(); VOGL_NOTE_UNUSED(packet_buf);

      const vogl_trace_stream_packet_base &base_packet = pTrace_reader->get_base_packet(); VOGL_NOTE_UNUSED(base_packet);
      const vogl_trace_gl_entrypoint_packet *pGL_packet = NULL;

      if (pTrace_reader->get_packet_type() == cTSPTGLEntrypoint)
      {
         vogl_trace_packet* pTrace_packet = vogl_new(vogl_trace_packet, &m_trace_ctypes);

         if (!pTrace_packet->deserialize(pTrace_reader->get_packet_buf().get_ptr(), pTrace_reader->get_packet_buf().size(), false))
         {
            console::error("%s: Failed parsing GL entrypoint packet\n", VOGL_FUNCTION_NAME);
            break;
         }

         pGL_packet = &pTrace_reader->get_packet<vogl_trace_gl_entrypoint_packet>();
         gl_entrypoint_id_t entrypoint_id = static_cast<gl_entrypoint_id_t>(pGL_packet->m_entrypoint_id);

         if (entrypoint_id == VOGL_ENTRYPOINT_glInternalTraceCommandRAD)
         {
            // Check if this is a state snapshot.
            // This is entirely optional since the client is designed to dynamically get new snapshots
            // if they don't exist.
            GLuint cmd = pTrace_packet->get_param_value<GLuint>(0);
            GLuint size = pTrace_packet->get_param_value<GLuint>(1); VOGL_NOTE_UNUSED(size);

            if (cmd == cITCRKeyValueMap)
            {
               key_value_map &kvm = pTrace_packet->get_key_value_map();

               dynamic_string cmd_type(kvm.get_string("command_type"));
               if (cmd_type == "state_snapshot")
               {
                  dynamic_string id(kvm.get_string("binary_id"));
                  if (id.is_empty())
                  {
                     vogl_warning_printf("%s: Missing binary_id field in glInternalTraceCommandRAD key_valye_map command type: \"%s\"\n", VOGL_FUNCTION_NAME, cmd_type.get_ptr());
                     continue;
                  }

                  uint8_vec snapshot_data;
                  {
                     timed_scope ts("get_multi_blob_manager().get");
                     if (!pTrace_reader->get_multi_blob_manager().get(id, snapshot_data) || (snapshot_data.is_empty()))
                     {
                        vogl_warning_printf("%s: Failed reading snapshot blob data \"%s\"!\n", VOGL_FUNCTION_NAME, id.get_ptr());
                        continue;
                     }
                  }

                  json_document doc;
                  {
                     timed_scope ts("doc.binary_deserialize");
                     if (!doc.binary_deserialize(snapshot_data) || (!doc.get_root()))
                     {
                        vogl_warning_printf("%s: Failed deserializing JSON snapshot blob data \"%s\"!\n", VOGL_FUNCTION_NAME, id.get_ptr());
                        continue;
                     }
                  }

                  vogl_gl_state_snapshot* pGLSnapshot = vogl_new(vogl_gl_state_snapshot);
                  pPendingSnapshot = vogl_new(vogleditor_gl_state_snapshot, pGLSnapshot);

                  timed_scope ts("pPendingSnapshot->deserialize");
                  if (!pPendingSnapshot->get_snapshot()->deserialize(*doc.get_root(), pTrace_reader->get_multi_blob_manager(), &m_trace_ctypes))
                  {
                     vogl_delete(pPendingSnapshot);
                     pPendingSnapshot = NULL;

                     vogl_warning_printf("%s: Failed deserializing snapshot blob data \"%s\"!\n", VOGL_FUNCTION_NAME, id.get_ptr());
                  }
               }
            }

            continue;
         }

         const gl_entrypoint_desc_t &entrypoint_desc = g_vogl_entrypoint_descs[entrypoint_id];

         QString funcCall = entrypoint_desc.m_pName;

         // format parameters
         funcCall.append("( ");
         dynamic_string paramStr;
         for (uint param_index = 0; param_index < pTrace_packet->total_params(); param_index++)
         {
            if (param_index != 0)
               funcCall.append(", ");

            paramStr.clear();
            pTrace_packet->pretty_print_param(paramStr, param_index, false);

            funcCall.append(paramStr.c_str());
         }
         funcCall.append(" )");

         if (pTrace_packet->has_return_value())
         {
            funcCall.append(" = ");
            paramStr.clear();
            pTrace_packet->pretty_print_return_value(paramStr, false);
            funcCall.append(paramStr.c_str());
         }

         // if we don't have a current frame, make a new frame node
         // and append it to the parent
         if (pCurFrame == NULL)
         {
            pCurFrame = vogl_new(vogleditor_frameItem, total_swaps);
            vogleditor_apiCallTreeItem* pNewFrameNode = vogl_new(vogleditor_apiCallTreeItem, pCurFrame, pCurParent);
            pCurParent->appendChild(pNewFrameNode);
            m_itemList.append(pNewFrameNode);

            if (pPendingSnapshot != NULL)
            {
               pCurFrame->set_snapshot(pPendingSnapshot);
               pPendingSnapshot = NULL;
            }

            // update current parent
            pCurParent = pNewFrameNode;
         }

         // make item and node for the api call
         vogleditor_apiCallItem* pCallItem = vogl_new(vogleditor_apiCallItem, pCurFrame, pTrace_packet, *pGL_packet);
         pCurFrame->appendCall(pCallItem);

         if (pPendingSnapshot != NULL)
         {
            pCallItem->set_snapshot(pPendingSnapshot);
            pPendingSnapshot = NULL;
         }

         vogleditor_apiCallTreeItem* item = vogl_new(vogleditor_apiCallTreeItem, funcCall, pCallItem, pCurParent);
         pCurParent->appendChild(item);
         m_itemList.append(item);

         if (vogl_is_swap_buffers_entrypoint(entrypoint_id))
         {
            total_swaps++;

            // reset the CurParent back to the original parent so that the next frame will be at the root level
            pCurParent = parent;

            // reset the CurFrame so that a new frame node will be created on the next api call
            pCurFrame = NULL;
         }
         else if (vogl_is_start_nested_entrypoint(entrypoint_id))
         {
             // Nest logically paired blocks of gl calls including terminating
             // nest call
             pCurParent = item;
         }
         else if (vogl_is_end_nested_entrypoint(entrypoint_id))
         {
             // move the parent back one level of the hierarchy, to its own parent
             // (but not past Frame parent [e.g., unpaired "end" operation])
             if (pCurParent->parent() != parent)
                 pCurParent = pCurParent->parent();
         }
      }

      if (pTrace_reader->get_packet_type() == cTSPTEOF)
      {
          //found_eof_packet = true;
          vogl_printf("Found trace file EOF packet on swap %" PRIu64 "\n", total_swaps);
          break;
      }
   }
}
Пример #6
0
//----------------------------------------------------------------------------------------------------------------------
// Function vogl_init_gl_entrypoint_descs
//----------------------------------------------------------------------------------------------------------------------
void vogl_init_gl_entrypoint_descs()
{
    gl_entrypoint_param_desc_t *pDst = &g_vogl_entrypoint_param_descs[0][0];

#define DEF_FUNCTION_BEGIN(exported, category, ret, ret_type, num_params, name, args, params)
#define DEF_FUNCTION_INFO(namespace_index, return_spectype, category, version, profile, deprecated, is_whitelisted, is_nullable, whitelisted_for_displaylists, listable)
#define DEF_FUNCTION_BEGIN_PARAMS \
    {                             \
        gl_entrypoint_param_desc_t *pCur = pDst;
#define DEF_FUNCTION_IN_VALUE_PARAM(namespace_index, spectype, type, ctype, name) \
    {                                                                             \
        pCur->m_pSpec_type = #spectype;                                           \
        pCur->m_pName = #name;                                                    \
        pCur->m_ctype = ctype;                                                    \
        pCur->m_class = VOGL_VALUE_PARAM;                                          \
        pCur->m_input = true;                                                     \
        pCur->m_namespace = (vogl_namespace_t)namespace_index;                     \
        ++pCur;                                                                   \
    }
#define DEF_FUNCTION_IN_REFERENCE_PARAM(namespace_index, spectype, type, ctype, name) \
    {                                                                                 \
        pCur->m_pSpec_type = #spectype;                                               \
        pCur->m_pName = #name;                                                        \
        pCur->m_ctype = ctype;                                                        \
        pCur->m_class = VOGL_REF_PARAM;                                                \
        pCur->m_input = true;                                                         \
        pCur->m_namespace = (vogl_namespace_t)namespace_index;                         \
        ++pCur;                                                                       \
    }
#define DEF_FUNCTION_IN_ARRAY_PARAM(namespace_index, spectype, type, ctype, name, size) \
    {                                                                                   \
        pCur->m_pSpec_type = #spectype;                                                 \
        pCur->m_pName = #name;                                                          \
        pCur->m_ctype = ctype;                                                          \
        pCur->m_class = VOGL_ARRAY_PARAM;                                                \
        pCur->m_input = true;                                                           \
        pCur->m_pSize = #size;                                                          \
        pCur->m_namespace = (vogl_namespace_t)namespace_index;                           \
        ++pCur;                                                                         \
    }
#define DEF_FUNCTION_OUT_REFERENCE_PARAM(namespace_index, spectype, type, ctype, name) \
    {                                                                                  \
        pCur->m_pSpec_type = #spectype;                                                \
        pCur->m_pName = #name;                                                         \
        pCur->m_ctype = ctype;                                                         \
        pCur->m_class = VOGL_ARRAY_PARAM;                                               \
        pCur->m_input = false;                                                         \
        pCur->m_namespace = (vogl_namespace_t)namespace_index;                          \
        ++pCur;                                                                        \
    }
#define DEF_FUNCTION_OUT_ARRAY_PARAM(namespace_index, spectype, type, ctype, name, size) \
    {                                                                                    \
        pCur->m_pSpec_type = #spectype;                                                  \
        pCur->m_pName = #name;                                                           \
        pCur->m_ctype = ctype;                                                           \
        pCur->m_class = VOGL_ARRAY_PARAM;                                                 \
        pCur->m_input = false;                                                           \
        pCur->m_pSize = #size;                                                           \
        pCur->m_namespace = (vogl_namespace_t)namespace_index;                            \
        ++pCur;                                                                          \
    }
#define DEF_FUNCTION_END_PARAMS                                    \
    VOGL_ASSERT((pCur - pDst) <= VOGL_MAX_ENTRYPOINT_PARAMETERS); \
    }
#define DEF_FUNCTION_RETURN(spectype, type, ctype)
#define DEF_FUNCTION_END(exported, category, ret, ret_type, num_params, name, args, params) pDst += VOGL_MAX_ENTRYPOINT_PARAMETERS;

#include "gl_glx_cgl_wgl_func_descs.inc"

    for (uint32_t i = 0; i < VOGL_ARRAY_SIZE(g_custom_array_size_macro_indices); i++)
    {
        uint32_t idx = g_custom_array_size_macro_indices[i];
        uint32_t func = idx >> 16;
        uint32_t param = idx & 0xFFFF;
        VOGL_ASSERT(func < VOGL_NUM_ENTRYPOINTS);
        VOGL_ASSERT(param < g_vogl_entrypoint_descs[func].m_num_params);

        g_vogl_entrypoint_param_descs[func][param].m_has_custom_array_size_macro = true;
    }

    for (uint32_t i = 0; i < VOGL_NUM_ENTRYPOINTS; i++)
    {
        gl_entrypoint_desc_t &desc = g_vogl_entrypoint_descs[i];
        VOGL_ASSERT(desc.m_return_namespace >= VOGL_NAMESPACE_UNKNOWN);
        VOGL_ASSERT(desc.m_return_namespace < VOGL_TOTAL_NAMESPACES);

        if (strcmp(desc.m_pName, "glGenTextures") == 0)
        {
            if (g_vogl_entrypoint_param_descs[i][1].m_namespace != VOGL_NAMESPACE_TEXTURES)
            {
                vogl_error_printf("vogl_namespace_t enum is bad, please rebuild");
                exit(EXIT_FAILURE);
            }
        }

        if (desc.m_return_ctype != VOGL_VOID)
        {
            if ((size_t)get_vogl_process_gl_ctypes()[desc.m_return_ctype].m_size < 1)
                vogl_warning_printf("function %s's return ctype %s is too small\n", desc.m_pName, get_vogl_process_gl_ctypes()[desc.m_return_ctype].m_pName);

            if ((size_t)get_vogl_process_gl_ctypes()[desc.m_return_ctype].m_size > sizeof(uint64_t))
                vogl_warning_printf("function %s's return ctype %s is too large\n", desc.m_pName, get_vogl_process_gl_ctypes()[desc.m_return_ctype].m_pName);
        }

        for (uint32_t j = 0; j < desc.m_num_params; j++)
        {
            if ((size_t)get_vogl_process_gl_ctypes()[g_vogl_entrypoint_param_descs[i][j].m_ctype].m_size < 1)
                vogl_warning_printf("param %u of function %s ctype %s is too small\n", j, desc.m_pName, get_vogl_process_gl_ctypes()[g_vogl_entrypoint_param_descs[i][j].m_ctype].m_pName);

            if ((size_t)get_vogl_process_gl_ctypes()[g_vogl_entrypoint_param_descs[i][j].m_ctype].m_size > sizeof(uint64_t))
                vogl_warning_printf("param %u of function %s ctype %s is too large\n", j, desc.m_pName, get_vogl_process_gl_ctypes()[g_vogl_entrypoint_param_descs[i][j].m_ctype].m_pName);
        }

        desc.m_pAPI_prefix = "GL";

        // FIXME: Add more prefixes as we add platforms. voglgen should probably supply this.
        // They must correspond with the GL enum prefixes in glx_enum_desc.inc, gl_enum_desc.inc, etc.
        if ((desc.m_pName[0] == 'w') && (desc.m_pName[1] == 'g') && (desc.m_pName[2] == 'l'))
            desc.m_pAPI_prefix = "WGL";
        else if ((desc.m_pName[0] == 'C') && (desc.m_pName[1] == 'G') && (desc.m_pName[2] == 'L'))
            desc.m_pAPI_prefix = "CGL";
        else if ((desc.m_pName[0] == 'g') && (desc.m_pName[1] == 'l') && (desc.m_pName[2] == 'X'))
            desc.m_pAPI_prefix = "GLX";
        else if ((desc.m_pName[0] == 'g') && (desc.m_pName[1] == 'l'))
            desc.m_pAPI_prefix = "GL";
        else
        {
            vogl_warning_printf("Unknown function prefix: %s\n", desc.m_pName);
        }
    }

    for (uint32_t i = 0; i < VOGL_NUM_ENTRYPOINTS; i++)
    {
        get_vogl_entrypoint_hashmap().insert(g_vogl_entrypoint_descs[i].m_pName, static_cast<gl_entrypoint_id_t>(i));
    }
}
Пример #7
0
bool vogl_binary_trace_file_reader::open(const char *pFilename, const char *pLoose_file_path)
{
    VOGL_FUNC_TRACER

    close();

    if (!init_loose_file_blob_manager(pFilename, pLoose_file_path))
        return false;

    if (!m_trace_stream.open(pFilename, cDataStreamReadable | cDataStreamSeekable, true))
    {
        close();
        return false;
    }

    m_trace_file_size = m_trace_stream.get_size();

    if (m_trace_stream.read(&m_sof_packet, sizeof(m_sof_packet)) != sizeof(m_sof_packet))
    {
        close();
        return false;
    }

    if (!m_sof_packet.full_validation(sizeof(m_sof_packet)))
    {
        vogl_error_printf("%s: Trace file failed validation!\n", VOGL_FUNCTION_INFO_CSTR);
        close();
        return false;
    }

    if (m_sof_packet.m_version < static_cast<uint16_t>(VOGL_TRACE_FILE_MINIMUM_COMPATIBLE_VERSION))
    {
        vogl_error_printf("%s: Trace file version is not supported, found version 0x%04X, expected at least version 0x%04X!\n", VOGL_FUNCTION_INFO_CSTR, m_sof_packet.m_version, VOGL_TRACE_FILE_MINIMUM_COMPATIBLE_VERSION);
        return false;
    }
    else if (m_sof_packet.m_version > static_cast<uint16_t>(VOGL_TRACE_FILE_VERSION))
    {
        // TODO: Make this an error? Backwards compat?
        vogl_warning_printf("%s: Trace file version is 0x%04X, expected version 0x%04X, this may not work at all!\n", VOGL_FUNCTION_INFO_CSTR, m_sof_packet.m_version, VOGL_TRACE_FILE_VERSION);
    }

    if (m_sof_packet.m_archive_size)
    {
        if (!m_archive_blob_manager.init_file(cBMFReadable | cBMFOpenExisting, pFilename, m_sof_packet.m_archive_offset, m_sof_packet.m_archive_size))
        {
            vogl_error_printf("%s: Failed reading in-trace archive!\n", VOGL_FUNCTION_INFO_CSTR);
            return false;
        }
    }

    m_packet_buf.reserve(512 * 1024);

    m_trace_stream.seek(m_sof_packet.m_first_packet_offset, false);

    if (!read_frame_file_offsets())
    {
        // Keep this in sync with the offset pushed in vogl_init_tracefile()!
        m_frame_file_offsets.push_back(get_cur_file_ofs());
    }

    return true;
}
Пример #8
0
    // This helper only works on files with valid file sizes (i.e. it won't work on some files under proc such as /proc/self/status).
    bool file_utils::read_text_file(const char *pPath, dynamic_string_array &lines, uint32_t flags)
    {
        cfile_stream stream;
        if (!stream.open(pPath, cDataStreamReadable))
        {
            if (flags & cRTFPrintErrorMessages)
                vogl_error_printf("Failed opening text file \"%s\" for reading\n", pPath);
            else if (flags & cRTFPrintWarningMessages)
                vogl_warning_printf("Failed opening text file \"%s\" for reading\n", pPath);

            return false;
        }

        int line_count = 0;
        dynamic_string line_str;
        while (stream.get_remaining())
        {
            ++line_count;
            if (!stream.read_line(line_str))
            {
                if (flags & cRTFPrintErrorMessages)
                    vogl_error_printf("Failed reading from text file \"%s\"\n", pPath);
                else if (flags & cRTFPrintWarningMessages)
                    vogl_warning_printf("Failed reading from text file \"%s\"\n", pPath);

                break;
            }

            if (flags & cRTFTrim)
                line_str.trim();

            if (flags & cRTFTrimEnd)
                line_str.trim_end();

            if (flags & cRTFIgnoreEmptyLines)
            {
                if (line_str.is_empty())
                    continue;
            }

            if ((flags & cRTFIgnoreCommentedLines) && (line_str.get_len() >= 2))
            {
                bool found_comment = false;

                for (int i = 0; i < static_cast<int>(line_str.get_len()); ++i)
                {
                    char c = line_str[i];

                    if ((c == ' ') || (c == '\t'))
                        continue;
                    else if ((c == '/') && (line_str[i + 1] == '/'))
                        found_comment = true;

                    break;
                }

                if (found_comment)
                    continue;
            }

            lines.push_back(line_str);
        }

        return true;
    }
Пример #9
0
bool vogl_matrix_state::save_matrix_stack(const vogl_context_info &context_info, GLenum matrix, uint32_t index, GLenum depth_get, GLenum matrix_get)
{
    VOGL_FUNC_TRACER

    VOGL_NOTE_UNUSED(context_info);

    GL_ENTRYPOINT(glMatrixMode)(matrix);
    if (vogl_check_gl_error())
        return false;

    GLint depth = 0;
    GL_ENTRYPOINT(glGetIntegerv)(depth_get, &depth);
    if (vogl_check_gl_error())
    {
        // This *should* work on AMD because it supports the ARB_imaging subset on compat contexts, and it supports the GL_COLOR matrix and the max stack depth is reported as 10.
        if (depth_get == GL_COLOR_MATRIX_STACK_DEPTH)
        {
            vogl_warning_printf("Using GL_COLOR_MATRIX_STACK_DEPTH workaround for AMD drivers - this will purposely force a stack underflow!\n");

            vogl::vector<matrix44D> matrices;
            for (;;)
            {
                matrix44D mat;
                GL_ENTRYPOINT(glGetDoublev)(matrix_get, mat.get_ptr());
                if (vogl_check_gl_error())
                    return false;

                matrices.push_back(mat);

                GL_ENTRYPOINT(glPopMatrix)();
                bool failed_popping = vogl_check_gl_error();

                GL_ENTRYPOINT(glGetDoublev)(matrix_get, mat.get_ptr());
                // AMD fails with a stack underflow on the get, not the pop - at least during tracing?
                if (vogl_check_gl_error())
                    failed_popping = true;

                if (failed_popping)
                    break;
            }

            for (int i = matrices.size() - 1; i >= 0; i--)
            {
                if (i != static_cast<int>(matrices.size()) - 1)
                {
                    GL_ENTRYPOINT(glPushMatrix)();
                    if (vogl_check_gl_error())
                        return false;
                }

                GL_ENTRYPOINT(glLoadMatrixd)(matrices[i].get_ptr());
                if (vogl_check_gl_error())
                    return false;
            }

            depth = matrices.size();
        }
        else
        {
            return false;
        }
    }

    if (depth < 1)
        return false;

    matrix_vec &vec = m_matrices[matrix_key(matrix, index)];
    vec.resize(depth);

    // deepest .. current
    // 0       1   2

    for (int i = 0; i < depth; i++)
    {
        matrix44D mat;
        GL_ENTRYPOINT(glGetDoublev)(matrix_get, mat.get_ptr());
        if (vogl_check_gl_error())
            return false;

        vec[depth - 1 - i] = mat;

        if (i != (depth - 1))
        {
            GL_ENTRYPOINT(glPopMatrix)();
            if (vogl_check_gl_error())
                return false;
        }
    }

    for (int i = 1; i < depth; i++)
    {
        GL_ENTRYPOINT(glPushMatrix)();
        if (vogl_check_gl_error())
            return false;

        GL_ENTRYPOINT(glLoadMatrixd)(vec[i].get_ptr());
        if (vogl_check_gl_error())
            return false;
    }

    return true;
}
Пример #10
0
//----------------------------------------------------------------------------------------------------------------------
// vogl_devel_dump_internal_texture_formats
// This func is only for testing various internal GL format related API's
// This func is used to generate vogl_internal_texture_formats.inc
//----------------------------------------------------------------------------------------------------------------------
void vogl_devel_dump_internal_texture_formats(const vogl_context_info &context_info)
{
    VOGL_FUNC_TRACER

    VOGL_CHECK_GL_ERROR;

    vogl_scoped_binding_state orig_texture;
    orig_texture.save_textures(&context_info);

    vogl_scoped_state_saver state_saver(cGSTPixelStore, cGSTPixelTransfer);

    vogl_reset_pixel_store_states();
    vogl_reset_pixel_transfer_states(context_info);

#if 0
	// silly experiment
	{
		GLuint handle;
		ACTUAL_GL_ENTRYPOINT(glGenTextures)(1, &handle);
		VOGL_CHECK_GL_ERROR;

		ACTUAL_GL_ENTRYPOINT(glBindTexture)(GL_TEXTURE_2D, handle);
		VOGL_CHECK_GL_ERROR;

		for (uint32_t i = 0; i < 256; i++)
		{
			uint8_t vals[4] = { i, 0, 0, 0 };
			//ACTUAL_GL_ENTRYPOINT(glTexImage2D)(GL_TEXTURE_2D, 0, GL_R8_SNORM, 1, 1, 0, GL_RED, GL_BYTE, vals);

			//float vals[1] = { ( i - 128.0f) / 127.0f };
			//float vals[1] = { i / 255.0f };

			//ACTUAL_GL_ENTRYPOINT(glPixelTransferf)(GL_RED_SCALE, .5f);
			//ACTUAL_GL_ENTRYPOINT(glPixelTransferf)(GL_RED_BIAS, 0.5f);

			ACTUAL_GL_ENTRYPOINT(glTexImage2D)(GL_TEXTURE_2D, 0, GL_RGB8UI, 1, 1, 0, GL_RGB_INTEGER, GL_UNSIGNED_BYTE, vals);

			//ACTUAL_GL_ENTRYPOINT(glPixelTransferf)(GL_RED_SCALE, 1.0f);
			//ACTUAL_GL_ENTRYPOINT(glPixelTransferf)(GL_RED_BIAS, 0.0f);

			VOGL_CHECK_GL_ERROR;

			uint16_t gvals[4] = { 0, 0, 0, 0 };
			ACTUAL_GL_ENTRYPOINT(glGetTexImage)(GL_TEXTURE_2D, 0, GL_RGB_INTEGER, GL_UNSIGNED_BYTE, gvals);
			VOGL_CHECK_GL_ERROR;

			printf("%u %u %u %u, %u %u %u %u\n", vals[0], vals[1], vals[2], vals[3],
			       gvals[0], gvals[1], gvals[2], gvals[3]);
		}


		ACTUAL_GL_ENTRYPOINT(glDeleteTextures)(1, &handle);
	}
#endif

    typedef vogl::map<GLenum, vogl_internal_tex_format> tex_format_map;
    tex_format_map internal_formats;

    // Iterate through the base internal fmts, which need some special handling (argh) because the actual internal fmt != the requested internal fmt
    GLenum base_internal_formats[] =
        {
            GL_DEPTH_COMPONENT,
            GL_DEPTH_STENCIL,
            GL_ALPHA,
            GL_RED,
            GL_RG,
            GL_RGB,
            GL_RGBA,
            GL_LUMINANCE,
            GL_LUMINANCE_ALPHA,
            GL_INTENSITY,
            GL_SLUMINANCE,
            GL_SLUMINANCE_ALPHA,
            GL_SRGB,
            GL_SRGB_ALPHA
        };

    for (uint32_t i = 0; i < VOGL_ARRAY_SIZE(base_internal_formats); i++)
    {
        printf("%s\n", get_gl_enums().find_gl_name(base_internal_formats[i]));

        GLuint handle;
        GL_ENTRYPOINT(glGenTextures)(1, &handle);
        VOGL_CHECK_GL_ERROR;

        GLenum target = GL_TEXTURE_2D;

        GL_ENTRYPOINT(glBindTexture)(target, handle);

        GLenum base_internal_fmt = base_internal_formats[i];

        vogl_internal_tex_format f;
        GL_ENTRYPOINT(glGetInternalformativ)(target, base_internal_fmt, GL_GET_TEXTURE_IMAGE_TYPE, sizeof(f.m_optimum_get_image_type), (GLint *)&f.m_optimum_get_image_type);
        GL_ENTRYPOINT(glGetInternalformativ)(target, base_internal_fmt, GL_GET_TEXTURE_IMAGE_FORMAT, sizeof(f.m_optimum_get_image_fmt), (GLint *)&f.m_optimum_get_image_fmt);
        VOGL_CHECK_GL_ERROR;

        GLenum &get_fmt = f.m_optimum_get_image_fmt;
        GLenum &get_type = f.m_optimum_get_image_type;

        // manual fixups, ARGH
        switch (base_internal_fmt)
        {
            case GL_DEPTH_COMPONENT:
            {
                get_fmt = GL_DEPTH_COMPONENT;
                get_type = GL_FLOAT;
                break;
            }
            case GL_RG:
            {
                get_fmt = GL_RG;
                get_type = GL_UNSIGNED_BYTE;
                break;
            }
            case GL_RGB:
            {
                get_fmt = GL_RGB;
                get_type = GL_UNSIGNED_BYTE;
                break;
            }
            case GL_RED:
            {
                get_fmt = GL_RED;
                get_type = GL_UNSIGNED_BYTE;
                break;
            }
            case GL_COMPRESSED_LUMINANCE:
            {
                get_fmt = GL_LUMINANCE;
                get_type = GL_UNSIGNED_BYTE;
                break;
            }
            case GL_COMPRESSED_LUMINANCE_ALPHA:
            {
                get_fmt = GL_LUMINANCE_ALPHA;
                get_type = GL_UNSIGNED_BYTE;
                break;
            }
            case GL_COMPRESSED_RGB:
            {
                get_fmt = GL_RGBA;
                get_type = GL_UNSIGNED_BYTE;
                break;
            }
            case GL_COMPRESSED_RGBA:
            {
                get_fmt = GL_RGBA;
                get_type = GL_UNSIGNED_BYTE;
                break;
            }
            case GL_LUMINANCE_ALPHA:
            {
                get_fmt = GL_LUMINANCE_ALPHA;
                get_type = GL_UNSIGNED_BYTE;
                break;
            }
            case GL_SLUMINANCE_ALPHA:
            {
                get_fmt = GL_LUMINANCE_ALPHA;
                get_type = GL_UNSIGNED_BYTE;
                break;
            }
            case GL_SRGB:
            {
                get_fmt = GL_RGB;
                get_type = GL_UNSIGNED_BYTE;
                break;
            }
            case GL_SRGB_ALPHA:
            {
                get_fmt = GL_RGBA;
                get_type = GL_UNSIGNED_BYTE;
                break;
            }
            default:
            {
                break;
            }
        }

        VOGL_VERIFY(get_fmt != GL_NONE);
        VOGL_VERIFY(get_type != GL_NONE);

        GL_ENTRYPOINT(glTexImage2D)(target, 0, base_internal_fmt, 32, 32, 0, get_fmt, get_type, NULL);
        VOGL_VERIFY(!vogl_check_gl_error());

//bool any_gl_errors = false;

#define GET_INT(dst, gl_enum)                                                  \
    do                                                                         \
    {                                                                          \
        int values[4];                                                         \
        utils::zero_object(values);                                            \
        GL_ENTRYPOINT(glGetTexLevelParameteriv)(target, 0, (gl_enum), values); \
        (dst) = values[0];                                                     \
    } while (0)

#define GET_BOOL(dst, gl_enum)                                                 \
    do                                                                         \
    {                                                                          \
        int values[4];                                                         \
        utils::zero_object(values);                                            \
        GL_ENTRYPOINT(glGetTexLevelParameteriv)(target, 0, (gl_enum), values); \
        (dst) = values[0] != 0;                                                \
    } while (0)


        GLenum actual_internal_fmt;
        GET_INT(actual_internal_fmt, GL_TEXTURE_INTERNAL_FORMAT);

        f.m_tex_image_flags = ((1 << 0) | (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4));
        f.m_fmt = base_internal_fmt;
        f.m_actual_internal_fmt = actual_internal_fmt;
        f.m_name = get_gl_enums().find_name(base_internal_fmt, "gl");

        GET_INT(f.m_comp_sizes[cTCRed], GL_TEXTURE_RED_SIZE);
        GET_INT(f.m_comp_sizes[cTCGreen], GL_TEXTURE_GREEN_SIZE);
        GET_INT(f.m_comp_sizes[cTCBlue], GL_TEXTURE_BLUE_SIZE);
        GET_INT(f.m_comp_sizes[cTCAlpha], GL_TEXTURE_ALPHA_SIZE);
        GET_INT(f.m_comp_sizes[cTCStencil], GL_TEXTURE_STENCIL_SIZE);
        GET_INT(f.m_comp_sizes[cTCDepth], GL_TEXTURE_DEPTH_SIZE);
        GET_INT(f.m_comp_sizes[cTCIntensity], GL_TEXTURE_INTENSITY_SIZE);
        GET_INT(f.m_comp_sizes[cTCLuminance], GL_TEXTURE_LUMINANCE_SIZE);

        GET_INT(f.m_comp_types[cTCRed], GL_TEXTURE_RED_TYPE);
        GET_INT(f.m_comp_types[cTCGreen], GL_TEXTURE_GREEN_TYPE);
        GET_INT(f.m_comp_types[cTCBlue], GL_TEXTURE_BLUE_TYPE);
        GET_INT(f.m_comp_types[cTCAlpha], GL_TEXTURE_ALPHA_TYPE);
        GET_INT(f.m_comp_types[cTCDepth], GL_TEXTURE_DEPTH_TYPE);
        GET_INT(f.m_comp_types[cTCIntensity], GL_TEXTURE_INTENSITY_TYPE);
        GET_INT(f.m_comp_types[cTCLuminance], GL_TEXTURE_LUMINANCE_TYPE);

        GET_INT(f.m_shared_size, GL_TEXTURE_SHARED_SIZE);
        GET_BOOL(f.m_compressed, GL_TEXTURE_COMPRESSED);

        printf("base_internal_fmt: %s get_fmt: %s get_type: %s, actual_internal_fmt: %s compressed: %u\n",
               get_gl_enums().find_gl_name(base_internal_fmt), get_gl_enums().find_gl_name(get_fmt), get_gl_enums().find_gl_name(get_type),
               get_gl_enums().find_gl_name(actual_internal_fmt),
               f.m_compressed);
#undef GET_INT
#undef GET_BOOL

        //VOGL_ASSERT(!any_gl_errors);

        VOGL_ASSERT(f.m_actual_internal_fmt != GL_NONE);
        VOGL_ASSERT(f.m_optimum_get_image_fmt != GL_NONE);
        VOGL_ASSERT(f.m_optimum_get_image_type != GL_NONE);

        VOGL_ASSERT(!f.m_compressed);
        VOGL_ASSERT(!ktx_is_compressed_ogl_fmt(f.m_fmt) && !ktx_is_compressed_ogl_fmt(f.m_actual_internal_fmt));
        VOGL_ASSERT(ktx_get_ogl_compressed_base_internal_fmt(f.m_fmt) == 0 && ktx_get_ogl_compressed_base_internal_fmt(f.m_actual_internal_fmt) == 0);

        if (!internal_formats.insert(base_internal_fmt, f).second)
        {
            internal_formats.find_value(base_internal_fmt)->m_tex_image_flags |= ((1 << 0) | (1 << 1) | (1 << 2) | (1 << 3) | (1 << 4));
        }

        GL_ENTRYPOINT(glBindTexture)(target, 0);
        VOGL_CHECK_GL_ERROR;

        GL_ENTRYPOINT(glDeleteTextures)(1, &handle);
        VOGL_CHECK_GL_ERROR;
    }

    for (uint32_t t = 0; t < 5; t++)
    {
        GLenum target = GL_NONE;
        switch (t)
        {
            case 0:
            {
                target = GL_TEXTURE_1D;
                break;
            }
            case 1:
            {
                target = GL_TEXTURE_2D;
                break;
            }
            case 2:
            {
                target = GL_TEXTURE_3D;
                break;
            }
            case 3:
            {
                target = GL_TEXTURE_2D_MULTISAMPLE;
                break;
            }
            case 4:
            {
                target = GL_TEXTURE_2D_MULTISAMPLE_ARRAY;
                break;
            }
            default:
            {
                VOGL_ASSERT_ALWAYS;
                break;
            }
        }

        for (uint32_t fmt = 0; fmt <= 0xFFFF; fmt++)
        {
            GLuint handle;
            GL_ENTRYPOINT(glGenTextures)(1, &handle);
            VOGL_CHECK_GL_ERROR;

            GL_ENTRYPOINT(glBindTexture)(target, handle);

            vogl_debug_message_control(context_info, GL_INVALID_ENUM, false);
            vogl_debug_message_control(context_info, GL_INVALID_OPERATION, false);

            bool failed = false;

            switch (t)
            {
                case 0:
                {
                    GL_ENTRYPOINT(glTexStorage1D)(target, 1, fmt, 32);
                    failed = vogl_check_gl_error_suppress_message();
                    break;
                }
                case 1:
                {
                    GL_ENTRYPOINT(glTexStorage2D)(target, 1, fmt, 32, 32);
                    failed = vogl_check_gl_error_suppress_message();
                    break;
                }
                case 2:
                {
                    GL_ENTRYPOINT(glTexStorage3D)(target, 1, fmt, 32, 32, 32);
                    failed = vogl_check_gl_error_suppress_message();
                    break;
                }
                case 3:
                {
                    GL_ENTRYPOINT(glTexStorage2DMultisample)(target, 2, fmt, 32, 32, GL_TRUE);
                    failed = vogl_check_gl_error_suppress_message();
                    break;
                }
                case 4:
                {
                    GL_ENTRYPOINT(glTexStorage3DMultisample)(target, 2, fmt, 32, 32, 2, GL_TRUE);
                    failed = vogl_check_gl_error_suppress_message();
                    break;
                }
            }

            vogl_debug_message_control(context_info, GL_INVALID_ENUM, true);
            vogl_debug_message_control(context_info, GL_INVALID_OPERATION, true);

            if (failed)
                continue;

            bool any_gl_errors = false;
            VOGL_NOTE_UNUSED(any_gl_errors);

            vogl_internal_tex_format f;
            f.m_tex_image_flags = (1 << t);
            f.m_fmt = fmt;
            f.m_actual_internal_fmt = fmt; // this assumes the actual internal fmt will match here!
            f.m_name = get_gl_enums().find_name(fmt, "gl");

#define GET_INT(dst, gl_enum)                                                  \
    do                                                                         \
    {                                                                          \
        int values[4];                                                         \
        utils::zero_object(values);                                            \
        GL_ENTRYPOINT(glGetTexLevelParameteriv)(target, 0, (gl_enum), values); \
        (dst) = values[0];                                                     \
    } while (0)

#define GET_BOOL(dst, gl_enum)                                                 \
    do                                                                         \
    {                                                                          \
        int values[4];                                                         \
        utils::zero_object(values);                                            \
        GL_ENTRYPOINT(glGetTexLevelParameteriv)(target, 0, (gl_enum), values); \
        (dst) = values[0] != 0;                                                \
    } while (0)

            GLenum internal_fmt;
            GET_INT(internal_fmt, GL_TEXTURE_INTERNAL_FORMAT);
            VOGL_ASSERT(internal_fmt == fmt);
            GET_INT(f.m_comp_sizes[cTCRed], GL_TEXTURE_RED_SIZE);
            GET_INT(f.m_comp_sizes[cTCGreen], GL_TEXTURE_GREEN_SIZE);
            GET_INT(f.m_comp_sizes[cTCBlue], GL_TEXTURE_BLUE_SIZE);
            GET_INT(f.m_comp_sizes[cTCAlpha], GL_TEXTURE_ALPHA_SIZE);
            GET_INT(f.m_comp_sizes[cTCStencil], GL_TEXTURE_STENCIL_SIZE);
            GET_INT(f.m_comp_sizes[cTCDepth], GL_TEXTURE_DEPTH_SIZE);
            GET_INT(f.m_comp_sizes[cTCIntensity], GL_TEXTURE_INTENSITY_SIZE);
            GET_INT(f.m_comp_sizes[cTCLuminance], GL_TEXTURE_LUMINANCE_SIZE);

            GET_INT(f.m_comp_types[cTCRed], GL_TEXTURE_RED_TYPE);
            GET_INT(f.m_comp_types[cTCGreen], GL_TEXTURE_GREEN_TYPE);
            GET_INT(f.m_comp_types[cTCBlue], GL_TEXTURE_BLUE_TYPE);
            GET_INT(f.m_comp_types[cTCAlpha], GL_TEXTURE_ALPHA_TYPE);
            GET_INT(f.m_comp_types[cTCDepth], GL_TEXTURE_DEPTH_TYPE);
            GET_INT(f.m_comp_types[cTCIntensity], GL_TEXTURE_INTENSITY_TYPE);
            GET_INT(f.m_comp_types[cTCLuminance], GL_TEXTURE_LUMINANCE_TYPE);

            GET_INT(f.m_shared_size, GL_TEXTURE_SHARED_SIZE);
            GET_BOOL(f.m_compressed, GL_TEXTURE_COMPRESSED);
#undef GET_INT
#undef GET_BOOL

            VOGL_ASSERT(!any_gl_errors);

            GL_ENTRYPOINT(glGetInternalformativ)(target, fmt, GL_GET_TEXTURE_IMAGE_TYPE, sizeof(f.m_optimum_get_image_type), (GLint *)&f.m_optimum_get_image_type);
            GL_ENTRYPOINT(glGetInternalformativ)(target, fmt, GL_GET_TEXTURE_IMAGE_FORMAT, sizeof(f.m_optimum_get_image_fmt), (GLint *)&f.m_optimum_get_image_fmt);
            VOGL_CHECK_GL_ERROR;

            if (f.m_compressed)
            {
                f.m_optimum_get_image_fmt = GL_RGBA;
                f.m_optimum_get_image_type = GL_UNSIGNED_BYTE;
            }
            else
            {
#define HANDLE_FMT(gl_enum, fmt, type)     \
    case gl_enum:                          \
    {                                      \
        f.m_optimum_get_image_fmt = fmt;   \
        f.m_optimum_get_image_type = type; \
        break;                             \
    }
                bool unhandled = false;
                switch (fmt)
                {
                    HANDLE_FMT(GL_R11F_G11F_B10F, GL_RGB, GL_UNSIGNED_INT_10F_11F_11F_REV);
                    HANDLE_FMT(GL_RGB9_E5, GL_RGB, GL_UNSIGNED_INT_5_9_9_9_REV);
                    HANDLE_FMT(GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT);
                    HANDLE_FMT(GL_DEPTH_COMPONENT32, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT);
                    HANDLE_FMT(GL_INTENSITY32F_ARB, GL_RED, GL_FLOAT);

                    HANDLE_FMT(2, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE);
                    HANDLE_FMT(3, GL_RGB, GL_UNSIGNED_BYTE);
                    HANDLE_FMT(GL_LUMINANCE4_ALPHA4, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE);
                    HANDLE_FMT(GL_LUMINANCE6_ALPHA2, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE);
                    HANDLE_FMT(GL_LUMINANCE8_ALPHA8, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE);
                    HANDLE_FMT(GL_LUMINANCE12_ALPHA4, GL_LUMINANCE_ALPHA, GL_UNSIGNED_SHORT);
                    HANDLE_FMT(GL_LUMINANCE12_ALPHA12, GL_LUMINANCE_ALPHA, GL_UNSIGNED_SHORT);
                    HANDLE_FMT(GL_LUMINANCE16_ALPHA16, GL_LUMINANCE_ALPHA, GL_UNSIGNED_SHORT);

                    HANDLE_FMT(GL_RGB8, GL_RGB, GL_UNSIGNED_BYTE);
                    HANDLE_FMT(GL_RGB8I, GL_RGB_INTEGER, GL_BYTE);
                    HANDLE_FMT(GL_RGB10, GL_RGB, GL_UNSIGNED_SHORT);
                    HANDLE_FMT(GL_RGB12, GL_RGB, GL_UNSIGNED_SHORT);
                    HANDLE_FMT(GL_RGB16, GL_RGB, GL_UNSIGNED_SHORT);
                    HANDLE_FMT(GL_RGBA12, GL_RGB, GL_UNSIGNED_SHORT);
                    HANDLE_FMT(GL_RG8, GL_RG, GL_UNSIGNED_BYTE);
                    HANDLE_FMT(GL_RG16, GL_RG, GL_UNSIGNED_SHORT);
                    HANDLE_FMT(GL_RG16F, GL_RG, GL_HALF_FLOAT);
                    HANDLE_FMT(GL_RG32F, GL_RG, GL_FLOAT);

                    HANDLE_FMT(GL_SRGB8, GL_RGB, GL_UNSIGNED_BYTE);
                    HANDLE_FMT(GL_SRGB8_ALPHA8, GL_RGBA, GL_UNSIGNED_BYTE);
                    HANDLE_FMT(GL_SLUMINANCE8_ALPHA8, GL_LUMINANCE_ALPHA, GL_BYTE);

                    HANDLE_FMT(GL_RGB32I, GL_RGB_INTEGER, GL_INT);
                    HANDLE_FMT(GL_RGB16I, GL_RGB_INTEGER, GL_SHORT);

                    HANDLE_FMT(GL_RGB32UI, GL_RGB_INTEGER, GL_UNSIGNED_INT);
                    HANDLE_FMT(GL_RGB16UI, GL_RGB_INTEGER, GL_UNSIGNED_SHORT);
                    HANDLE_FMT(GL_RGB8UI, GL_RGB_INTEGER, GL_UNSIGNED_INT);
                    HANDLE_FMT(GL_SIGNED_RGBA8_NV, GL_RGBA, GL_BYTE);
                    HANDLE_FMT(GL_SIGNED_RGB8_NV, GL_RGB, GL_BYTE);
                    HANDLE_FMT(GL_SIGNED_LUMINANCE8_ALPHA8_NV, GL_LUMINANCE_ALPHA, GL_BYTE);
                    HANDLE_FMT(GL_SIGNED_RGB8_UNSIGNED_ALPHA8_NV, GL_RGBA, GL_BYTE);
                    HANDLE_FMT(GL_RG8_SNORM, GL_RG, GL_BYTE);
                    HANDLE_FMT(GL_RGB8_SNORM, GL_RGB, GL_BYTE);
                    HANDLE_FMT(GL_RG16_SNORM, GL_RG, GL_SHORT);
                    HANDLE_FMT(GL_RGB16_SNORM, GL_RGB, GL_SHORT);

                    HANDLE_FMT(GL_RGB32F, GL_RGB, GL_FLOAT);
                    HANDLE_FMT(GL_RGB16F, GL_RGB, GL_HALF_FLOAT);

                    // TODO: Research oddball formats
                    HANDLE_FMT(GL_PALETTE4_RGB8_OES, GL_RGB, GL_UNSIGNED_BYTE);
                    HANDLE_FMT(GL_PALETTE4_R5_G6_B5_OES, GL_RGB, GL_UNSIGNED_BYTE);
                    HANDLE_FMT(GL_PALETTE8_RGB8_OES, GL_RGB, GL_UNSIGNED_BYTE);
                    HANDLE_FMT(GL_PALETTE8_R5_G6_B5_OES, GL_RGB, GL_UNSIGNED_BYTE);

                    HANDLE_FMT(GL_HILO16_NV, GL_NONE, GL_NONE);
                    HANDLE_FMT(GL_SIGNED_HILO16_NV, GL_NONE, GL_NONE);
                    HANDLE_FMT(GL_DSDT8_MAG8_INTENSITY8_NV, GL_NONE, GL_NONE);
                    HANDLE_FMT(GL_HILO8_NV, GL_NONE, GL_NONE);
                    HANDLE_FMT(GL_SIGNED_HILO8_NV, GL_NONE, GL_NONE);
                    HANDLE_FMT(GL_DSDT8_NV, GL_NONE, GL_NONE);
                    HANDLE_FMT(GL_DSDT8_MAG8_NV, GL_NONE, GL_NONE);

                    default:
                        unhandled = true;
                        break;
                }

                if ((unhandled) && ((f.m_optimum_get_image_fmt == GL_NONE) || (f.m_optimum_get_image_type == GL_NONE)))
                {
                    printf("INVALID: %s %s %s\n", f.m_name.get_ptr(), get_gl_enums().find_name(f.m_optimum_get_image_fmt, "gl"), get_gl_enums().find_name(f.m_optimum_get_image_type, "gl"));
                }
            }

#undef HANDLE_FMT

            VOGL_ASSERT(f.m_actual_internal_fmt != GL_NONE);

            if ((f.m_optimum_get_image_fmt == GL_NONE) || (f.m_optimum_get_image_type == GL_NONE))
                vogl_warning_printf("Don't have an optimal get format/type for internal format %s\n", get_gl_enums().find_gl_name(fmt));

            VOGL_ASSERT(fmt != GL_LUMINANCE);

            VOGL_ASSERT(f.m_fmt == f.m_actual_internal_fmt);
            if (!f.m_compressed)
            {
                VOGL_ASSERT(!ktx_is_compressed_ogl_fmt(f.m_fmt) && !ktx_is_compressed_ogl_fmt(f.m_actual_internal_fmt));
                VOGL_ASSERT(ktx_get_ogl_compressed_base_internal_fmt(f.m_fmt) == 0 && ktx_get_ogl_compressed_base_internal_fmt(f.m_actual_internal_fmt) == 0);
            }
            else
            {
                VOGL_ASSERT(ktx_is_compressed_ogl_fmt(f.m_actual_internal_fmt));
                VOGL_ASSERT(ktx_get_ogl_compressed_base_internal_fmt(f.m_actual_internal_fmt) != 0);
            }

            if (!internal_formats.insert(fmt, f).second)
            {
                internal_formats.find_value(fmt)->m_tex_image_flags |= (1 << t);
            }

            GL_ENTRYPOINT(glBindTexture)(target, 0);
            VOGL_CHECK_GL_ERROR;

            GL_ENTRYPOINT(glDeleteTextures)(1, &handle);
            VOGL_CHECK_GL_ERROR;
        }
    }

    const char *pOutput_filename = "internal_texture_formats.inc";
    FILE *pFile = vogl_fopen(pOutput_filename, "w");
    VOGL_VERIFY(pFile);
    if (!pFile)
        return;

    for (tex_format_map::const_iterator it = internal_formats.begin(); it != internal_formats.end(); ++it)
    {
        vogl_internal_tex_format fmt(it->second);

        uint32_t actual_size = 0;

        if (!fmt.m_compressed)
        {
            VOGL_ASSERT(!ktx_is_compressed_ogl_fmt(fmt.m_fmt));
            VOGL_ASSERT(ktx_get_ogl_compressed_base_internal_fmt(fmt.m_fmt) == 0);
        }
        else
        {
            VOGL_ASSERT(ktx_is_compressed_ogl_fmt(fmt.m_fmt));
            VOGL_ASSERT(ktx_get_ogl_compressed_base_internal_fmt(fmt.m_fmt) != 0);
        }

        if ((!fmt.m_compressed) && (fmt.m_optimum_get_image_fmt != GL_NONE) && (fmt.m_optimum_get_image_type != GL_NONE))
        {
            GLuint handle;
            GL_ENTRYPOINT(glGenTextures)(1, &handle);
            VOGL_CHECK_GL_ERROR;

            GL_ENTRYPOINT(glBindTexture)(GL_TEXTURE_2D, handle);
            VOGL_CHECK_GL_ERROR;

            uint8_t vals[128];
            utils::zero_object(vals);
            vals[1] = 64;
            GL_ENTRYPOINT(glTexImage2D)(GL_TEXTURE_2D, 0, fmt.m_fmt, 1, 1, 0, fmt.m_optimum_get_image_fmt, fmt.m_optimum_get_image_type, vals);
            if (vogl_check_gl_error())
            {
                printf("glTexImage2D FAILED: %s %s %s\n", fmt.m_name.get_ptr(), get_gl_enums().find_name(fmt.m_optimum_get_image_fmt, "gl"), get_gl_enums().find_name(fmt.m_optimum_get_image_type, "gl"));
            }

            uint8_t gvals[128];
            memset(gvals, 0xCD, sizeof(gvals));
            GL_ENTRYPOINT(glGetTexImage)(GL_TEXTURE_2D, 0, fmt.m_optimum_get_image_fmt, fmt.m_optimum_get_image_type, gvals);

            uint32_t actual_size0 = 0;
            for (actual_size0 = 0; actual_size0 < sizeof(gvals); actual_size0++)
                if (gvals[actual_size0] == 0xCD)
                    break;

            memset(gvals, 0x12, sizeof(gvals));
            GL_ENTRYPOINT(glGetTexImage)(GL_TEXTURE_2D, 0, fmt.m_optimum_get_image_fmt, fmt.m_optimum_get_image_type, gvals);

            uint32_t actual_size1 = 0;
            for (actual_size1 = 0; actual_size1 < sizeof(gvals); actual_size1++)
                if (gvals[actual_size1] == 0x12)
                    break;

            VOGL_VERIFY(actual_size0 == actual_size1);

            //printf("glGetTexImage() wrote %u bytes\n", actual_size0);

            if (vogl_check_gl_error()) // || gvals[1] != vals[1])
            {
                printf("glGetTexImage() failed: %s %s %s\n", fmt.m_name.get_ptr(), get_gl_enums().find_name(fmt.m_optimum_get_image_fmt, "gl"), get_gl_enums().find_name(fmt.m_optimum_get_image_type, "gl"));
            }

            GL_ENTRYPOINT(glBindTexture)(GL_TEXTURE_2D, 0);
            VOGL_CHECK_GL_ERROR;

            GL_ENTRYPOINT(glDeleteTextures)(1, &handle);

            actual_size = actual_size0;

            uint32_t s = vogl_get_image_format_size_in_bytes(fmt.m_optimum_get_image_fmt, fmt.m_optimum_get_image_type);
            VOGL_VERIFY(s);
            if (s != actual_size0)
            {
                VOGL_VERIFY(0);
            }

            vogl::ktx_texture ktx_tex;
            GLenum img_fmt;
            GLenum img_type;
            img_fmt = fmt.m_optimum_get_image_fmt;
            img_type = fmt.m_optimum_get_image_type;

            uint32_t block_dim, bytes_per_block;
            bool success = ktx_get_ogl_fmt_desc(img_fmt, img_type, block_dim, bytes_per_block);
            VOGL_VERIFY(success);
            VOGL_VERIFY(block_dim == 1);
            VOGL_VERIFY(bytes_per_block == actual_size);

            if (!ktx_tex.init_2D(1, 1, 1, fmt.m_fmt, img_fmt, img_type))
            {
                printf("ktx_texture::init_2D() failed: %s %s %s\n", fmt.m_name.get_ptr(), get_gl_enums().find_name(fmt.m_optimum_get_image_fmt, "gl"), get_gl_enums().find_name(fmt.m_optimum_get_image_type, "gl"));
            }
        }
        else if (fmt.m_compressed)
        {
            GLuint handle;
            GL_ENTRYPOINT(glGenTextures)(1, &handle);
            VOGL_CHECK_GL_ERROR;

            GL_ENTRYPOINT(glBindTexture)(GL_TEXTURE_2D, handle);
            VOGL_CHECK_GL_ERROR;

            GL_ENTRYPOINT(glTexStorage2D)(GL_TEXTURE_2D, 1, fmt.m_fmt, 1, 1);
            VOGL_CHECK_GL_ERROR;

            GL_ENTRYPOINT(glGetTexLevelParameteriv)(GL_TEXTURE_2D, 0, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, (GLint *)&actual_size);
            VOGL_CHECK_GL_ERROR;

            GL_ENTRYPOINT(glBindTexture)(GL_TEXTURE_2D, 0);
            VOGL_CHECK_GL_ERROR;

            GL_ENTRYPOINT(glDeleteTextures)(1, &handle);
            VOGL_CHECK_GL_ERROR;

            uint32_t block_width = 0, block_height = 0, block_size = 0;
            GL_ENTRYPOINT(glGetInternalformativ)(GL_TEXTURE_2D, fmt.m_fmt, GL_TEXTURE_COMPRESSED_BLOCK_WIDTH, sizeof(int), reinterpret_cast<GLint *>(&block_width));
            GL_ENTRYPOINT(glGetInternalformativ)(GL_TEXTURE_2D, fmt.m_fmt, GL_TEXTURE_COMPRESSED_BLOCK_HEIGHT, sizeof(int), reinterpret_cast<GLint *>(&block_height));
            GL_ENTRYPOINT(glGetInternalformativ)(GL_TEXTURE_2D, fmt.m_fmt, GL_TEXTURE_COMPRESSED_BLOCK_SIZE, sizeof(int), reinterpret_cast<GLint *>(&block_size));
            VOGL_CHECK_GL_ERROR;

            if (block_size == actual_size * 8U)
                block_size /= 8;

            uint32_t block_dim, bytes_per_block;
            bool success = ktx_get_ogl_fmt_desc(fmt.m_fmt, GL_UNSIGNED_BYTE, block_dim, bytes_per_block);
            if ((!success) || (block_dim != block_width) || (block_dim != block_height) || (bytes_per_block != actual_size) || (bytes_per_block != block_size))
            {
                printf("ktx_get_ogl_fmt_desc on compressed format failed: %s %s %s %u %i %i %i\n", fmt.m_name.get_ptr(), get_gl_enums().find_name(fmt.m_optimum_get_image_fmt, "gl"), get_gl_enums().find_name(fmt.m_optimum_get_image_type, "gl"), actual_size, block_width, block_height, block_size);
            }

            fmt.m_block_width = block_width;
            fmt.m_block_height = block_height;

            vogl::ktx_texture ktx_tex;
            if (!ktx_tex.init_2D(1, 1, 1, fmt.m_fmt, GL_NONE, GL_NONE))
            {
                printf("ktx_texture::init_2D() compressed failed: %s %s %s\n", fmt.m_name.get_ptr(), get_gl_enums().find_name(fmt.m_optimum_get_image_fmt, "gl"), get_gl_enums().find_name(fmt.m_optimum_get_image_type, "gl"));
            }
        }

        fmt.m_image_bytes_per_pixel_or_block = actual_size;

        fprintf(pFile, "   vogl_internal_tex_format(0x%04X, \"%s\", 0x%04X,\n", fmt.m_fmt, fmt.m_name.get_ptr(), fmt.m_actual_internal_fmt);

        fprintf(pFile, "      ");
        for (uint32_t i = 0; i < cTCTotalComponents; i++)
            fprintf(pFile, "%u, ", fmt.m_comp_sizes[i]);
        fprintf(pFile, "\n");

        fprintf(pFile, "      ");
        for (uint32_t i = 0; i < cTCTotalComponents; i++)
            fprintf(pFile, "%s, ", get_gl_enums().find_name(fmt.m_comp_types[i], "gl"));
        fprintf(pFile, "\n");

        fprintf(pFile, "      %u, 0x%02X, %u, \n", fmt.m_shared_size, fmt.m_tex_image_flags, fmt.m_compressed);
        fprintf(pFile, "      %s, %s, %u, %u, %u),\n",
                get_gl_enums().find_name(fmt.m_optimum_get_image_fmt, "gl"),
                get_gl_enums().find_name(fmt.m_optimum_get_image_type, "gl"),
                fmt.m_image_bytes_per_pixel_or_block,
                fmt.m_block_width, fmt.m_block_height);

#if 0
        uint32_t tex_formats_count;
        static const vogl_internal_tex_format *tex_formats = get_vogl_internal_texture_formats(&tex_formats_count);
		for (uint32_t q = 0; q < tex_formats_count; q++)
		{
			if (tex_formats[q].m_fmt == fmt.m_fmt)
			{
				if (!tex_formats[q].compare(fmt))
				{
					VOGL_ASSERT_ALWAYS;
				}
				break;
			}
		}
		if (q == tex_formats_count)
		{
			VOGL_ASSERT_ALWAYS;
		}
#endif
    }

    vogl_fclose(pFile);

    printf("Wrote file %s\n", pOutput_filename);
}
bool vogl_default_framebuffer_state::restore(const vogl_context_info &context_info, bool restore_front_buffer) const
{
    VOGL_NOTE_UNUSED(context_info);

    if (!m_valid)
        return false;

    // TODO: Test multisampled default framebuffers
    // TODO: Check to ensure the stored fb is compatible with the current framebuffer
    const GLenum tex_target = (m_fb_attribs.m_samples > 1) ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D;

    vogl_scoped_state_saver framebuffer_state_saver(cGSTReadBuffer, cGSTDrawBuffer);

    vogl_scoped_binding_state orig_framebuffers(GL_DRAW_FRAMEBUFFER, GL_READ_FRAMEBUFFER, GL_TEXTURE_2D, GL_TEXTURE_2D_MULTISAMPLE);

    GL_ENTRYPOINT(glBindFramebuffer)(GL_DRAW_FRAMEBUFFER, 0);
    VOGL_CHECK_GL_ERROR;

    for (uint i = 0; i < cDefFramebufferTotal; i++)
    {
        if ((!restore_front_buffer) && ((i == cDefFramebufferFrontLeft) || (i == cDefFramebufferFrontRight)))
            continue;

        if (!m_textures[i].is_valid())
            continue;

        GL_ENTRYPOINT(glDrawBuffer)((i == cDefFramebufferDepthStencil) ? GL_NONE : g_def_framebuffer_enums[i]);
        if (vogl_check_gl_error_internal(true))
            continue;

        GLuint64 tex_handle64 = 0;

        vogl_handle_remapper def_handle_remapper;
        if (!m_textures[i].restore(context_info, def_handle_remapper, tex_handle64))
        {
            vogl_error_printf("%s: Failed restoring texture %u\n", VOGL_METHOD_NAME, i);
            continue;
        }

        GLuint tex_handle = static_cast<GLuint>(tex_handle64);

        // Create FBO
        GLuint fbo_handle = 0;
        GL_ENTRYPOINT(glGenFramebuffers)(1, &fbo_handle);
        VOGL_CHECK_GL_ERROR;

        GL_ENTRYPOINT(glBindFramebuffer)(GL_READ_FRAMEBUFFER, fbo_handle);
        VOGL_CHECK_GL_ERROR;

        GL_ENTRYPOINT(glFramebufferTexture2D)(GL_READ_FRAMEBUFFER, (i == cDefFramebufferDepthStencil) ? GL_DEPTH_STENCIL_ATTACHMENT : GL_COLOR_ATTACHMENT0, tex_target, tex_handle, 0);
        VOGL_CHECK_GL_ERROR;

        GL_ENTRYPOINT(glReadBuffer)((i == cDefFramebufferDepthStencil) ? GL_NONE : GL_COLOR_ATTACHMENT0);
        VOGL_CHECK_GL_ERROR;

        GLenum cur_status = GL_ENTRYPOINT(glCheckFramebufferStatus)(GL_READ_FRAMEBUFFER);
        VOGL_CHECK_GL_ERROR;

        bool status = true;

        if (cur_status == GL_FRAMEBUFFER_COMPLETE)
        {
            GL_ENTRYPOINT(glBlitFramebuffer)(
                0, 0, m_fb_attribs.m_width, m_fb_attribs.m_height,
                0, 0, m_fb_attribs.m_width, m_fb_attribs.m_height,
                (i == cDefFramebufferDepthStencil) ? (GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT) : GL_COLOR_BUFFER_BIT,
                GL_NEAREST);

            if (vogl_check_gl_error_internal())
            {
                status = false;
            }
        }

        if (!status)
        {
            vogl_warning_printf("%s: Failed blitting framebuffer %u\n", VOGL_METHOD_NAME, i);
        }

        // Delete FBO
        GL_ENTRYPOINT(glBindFramebuffer)(GL_READ_FRAMEBUFFER, 0);
        VOGL_CHECK_GL_ERROR;

        GL_ENTRYPOINT(glDeleteFramebuffers)(1, &fbo_handle);
        VOGL_CHECK_GL_ERROR;

        GL_ENTRYPOINT(glReadBuffer)(GL_FRONT_LEFT);
        VOGL_CHECK_GL_ERROR;

        GL_ENTRYPOINT(glBindTexture)(tex_target, 0);
        VOGL_CHECK_GL_ERROR;

        GL_ENTRYPOINT(glDeleteTextures)(1, &tex_handle);
        VOGL_CHECK_GL_ERROR;
    }

    return true;
}
bool vogl_default_framebuffer_state::snapshot(const vogl_context_info &context_info, const vogl_default_framebuffer_attribs &fb_attribs)
{
    VOGL_NOTE_UNUSED(context_info);

    clear();

    m_fb_attribs = fb_attribs;

    // Create compatible GL texture
    // Attach this texture to an FBO
    // Blit default framebuffer to this FBO
    // Capture this texture's state

    vogl_scoped_state_saver framebuffer_state_saver(cGSTReadBuffer, cGSTDrawBuffer);

    vogl_scoped_binding_state orig_framebuffers(GL_DRAW_FRAMEBUFFER, GL_READ_FRAMEBUFFER, GL_TEXTURE_2D, GL_TEXTURE_2D_MULTISAMPLE);

    GL_ENTRYPOINT(glBindFramebuffer)(GL_READ_FRAMEBUFFER, 0);
    VOGL_CHECK_GL_ERROR;

    vogl_scoped_binding_state orig_bindings(GL_PIXEL_PACK_BUFFER, GL_PIXEL_UNPACK_BUFFER);

    GL_ENTRYPOINT(glBindBuffer)(GL_PIXEL_PACK_BUFFER, 0);
    VOGL_CHECK_GL_ERROR;

    GL_ENTRYPOINT(glBindBuffer)(GL_PIXEL_UNPACK_BUFFER, 0);
    VOGL_CHECK_GL_ERROR;

    vogl_scoped_state_saver pixelstore_state_saver(cGSTPixelStore);

    vogl_scoped_state_saver pixeltransfer_state_saver;
    if (!context_info.is_core_profile())
        pixeltransfer_state_saver.save(cGSTPixelTransfer);

    vogl_reset_pixel_store_states();
    if (!context_info.is_core_profile())
        vogl_reset_pixel_transfer_states();

    // TODO: Test multisampled default framebuffers
    const GLenum tex_target = (fb_attribs.m_samples > 1) ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D;

    for (uint i = 0; i < cDefFramebufferTotal; i++)
    {
        GLenum internal_fmt, pixel_fmt, pixel_type;

        // TODO: This uses fixed pixel formats, and assumes there's always a depth/stencil buffer.
        if (i == cDefFramebufferDepthStencil)
        {
            if ((fb_attribs.m_depth_size + fb_attribs.m_stencil_size) == 0)
                continue;

            GL_ENTRYPOINT(glReadBuffer)(fb_attribs.m_double_buffered ? GL_BACK_LEFT : GL_FRONT_LEFT);

            internal_fmt = GL_DEPTH_STENCIL;
            pixel_fmt = GL_DEPTH_STENCIL;
            pixel_type = GL_UNSIGNED_INT_24_8;
        }
        else
        {
            if ((fb_attribs.m_r_size + fb_attribs.m_g_size + fb_attribs.m_b_size + fb_attribs.m_a_size) == 0)
                continue;

            GL_ENTRYPOINT(glReadBuffer)(g_def_framebuffer_enums[i]);

            internal_fmt = GL_RGBA;
            pixel_fmt = GL_RGBA;
            pixel_type = GL_UNSIGNED_INT_8_8_8_8_REV;
        }

        if (vogl_check_gl_error_internal(true))
            continue;

        // Create texture
        GLuint tex_handle = 0;
        GL_ENTRYPOINT(glGenTextures)(1, &tex_handle);
        VOGL_CHECK_GL_ERROR;

        GL_ENTRYPOINT(glBindTexture)(tex_target, tex_handle);
        VOGL_CHECK_GL_ERROR;

        if (fb_attribs.m_samples > 1)
        {
            GL_ENTRYPOINT(glTexImage2DMultisample)(tex_target,
                fb_attribs.m_samples,
                internal_fmt,
                fb_attribs.m_width,
                fb_attribs.m_height,
                GL_TRUE);
        }
        else
        {
            GL_ENTRYPOINT(glTexImage2D)(tex_target,
                0,
                internal_fmt,
                fb_attribs.m_width,
                fb_attribs.m_height,
                0,
                pixel_fmt,
                pixel_type,
                NULL);
        }

        if (vogl_check_gl_error_internal())
        {
            GL_ENTRYPOINT(glBindTexture)(tex_target, 0);
            VOGL_CHECK_GL_ERROR;

            GL_ENTRYPOINT(glDeleteTextures)(1, &tex_handle);
            VOGL_CHECK_GL_ERROR;

            continue;
        }

        GL_ENTRYPOINT(glTexParameteri)(tex_target, GL_TEXTURE_MAX_LEVEL, 0);
        VOGL_CHECK_GL_ERROR;

        // Create FBO
        GLuint fbo_handle = 0;
        GL_ENTRYPOINT(glGenFramebuffers)(1, &fbo_handle);
        VOGL_CHECK_GL_ERROR;

        GL_ENTRYPOINT(glBindFramebuffer)(GL_DRAW_FRAMEBUFFER, fbo_handle);
        VOGL_CHECK_GL_ERROR;

        GL_ENTRYPOINT(glFramebufferTexture2D)(GL_DRAW_FRAMEBUFFER, (i == cDefFramebufferDepthStencil) ? GL_DEPTH_STENCIL_ATTACHMENT : GL_COLOR_ATTACHMENT0, tex_target, tex_handle, 0);
        VOGL_CHECK_GL_ERROR;

        GLenum draw_buf = (i == cDefFramebufferDepthStencil) ? GL_NONE : GL_COLOR_ATTACHMENT0;
        GL_ENTRYPOINT(glDrawBuffers)(1, &draw_buf);
        VOGL_CHECK_GL_ERROR;

        GLenum cur_status = GL_ENTRYPOINT(glCheckFramebufferStatus)(GL_DRAW_FRAMEBUFFER);
        VOGL_CHECK_GL_ERROR;

        bool status = true;

        if (cur_status == GL_FRAMEBUFFER_COMPLETE)
        {
            GL_ENTRYPOINT(glBlitFramebuffer)(
                0, 0, fb_attribs.m_width, fb_attribs.m_height,
                0, 0, fb_attribs.m_width, fb_attribs.m_height,
                (i == cDefFramebufferDepthStencil) ? (GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT) : GL_COLOR_BUFFER_BIT,
                GL_NEAREST);

            if (vogl_check_gl_error_internal())
            {
                status = false;
            }
        }

        if (status)
        {
            vogl_handle_remapper def_handle_remapper;
            status = m_textures[i].snapshot(context_info, def_handle_remapper, tex_handle, tex_target);

            if (!status)
            {
                vogl_error_printf("%s: Failed snapshotting texture for default framebuffer %s\n", VOGL_METHOD_NAME, g_gl_enums.find_gl_name(g_def_framebuffer_enums[i]));
            }
        }
        else
        {
            vogl_warning_printf("%s: Failed blitting framebuffer %u\n", VOGL_METHOD_NAME, i);
        }

        // Delete FBO
        GL_ENTRYPOINT(glBindFramebuffer)(GL_DRAW_FRAMEBUFFER, 0);
        VOGL_CHECK_GL_ERROR;

        GL_ENTRYPOINT(glDeleteFramebuffers)(1, &fbo_handle);
        VOGL_CHECK_GL_ERROR;

        GL_ENTRYPOINT(glDrawBuffer)(GL_FRONT_LEFT);
        VOGL_CHECK_GL_ERROR;

        GL_ENTRYPOINT(glBindTexture)(tex_target, 0);
        VOGL_CHECK_GL_ERROR;

        GL_ENTRYPOINT(glDeleteTextures)(1, &tex_handle);
        VOGL_CHECK_GL_ERROR;
    }

    m_valid = true;

    return true;
}