Пример #1
0
bool vogl_binary_trace_file_reader::read_frame_file_offsets()
{
    VOGL_FUNC_TRACER

    m_frame_file_offsets.clear();
    m_max_frame_index = -1;
    m_found_frame_file_offsets_packet = false;

    uint8_vec frame_offsets_data;
    if (!m_archive_blob_manager.is_initialized() || !m_archive_blob_manager.get(VOGL_TRACE_ARCHIVE_FRAME_FILE_OFFSETS_FILENAME, frame_offsets_data))
    {
        vogl_debug_printf("%s: Couldn't find trace frame file offset file in trace archive, seeking will be slow in this trace file\n", VOGL_FUNCTION_INFO_CSTR);
        return false;
    }

    if (frame_offsets_data.size() & (sizeof(uint64_t) - 1))
    {
        vogl_error_printf("%s: Trace frame file offset file in trace archive is invalid, seeking will be slow in this trace file\n", VOGL_FUNCTION_INFO_CSTR);
        return false;
    }

    uint32_t total_offsets = frame_offsets_data.size() / sizeof(uint64_t);

    m_frame_file_offsets.resize(total_offsets);
    memcpy(m_frame_file_offsets.get_ptr(), frame_offsets_data.get_ptr(), m_frame_file_offsets.size_in_bytes());

    vogl_debug_printf("%s: Frame file offsets packet is OK, found %u total frame offsets\n", VOGL_FUNCTION_INFO_CSTR, m_frame_file_offsets.size());

    m_max_frame_index = m_frame_file_offsets.size() - 1;

    m_found_frame_file_offsets_packet = true;
    return true;
}
Пример #2
0
 bool file_utils::create_directory(const char *pPath)
 {
     #if defined(PLATFORM_WINDOWS)
         int status = _mkdir(pPath);
     #else
         int status = mkdir(pPath, S_IRWXU | S_IRWXG | S_IRWXO);
     #endif
     if (status == 0)
         vogl_debug_printf("Created directory %s\n", pPath);
     return !status;
 }
Пример #3
0
bool vogl_replay_window::open(int width, int height, int samples)
{
    VOGL_FUNC_TRACER
    close();

    const char *pWindow_name = (sizeof(void *) == sizeof(uint32_t)) ? "voglreplay 32-bit" : "voglreplay 64-bit";

    #if (VOGL_PLATFORM_HAS_SDL)

        SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
        SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
        SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
        SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
        SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
        SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
        SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
    
        if (samples > 1) {
            SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1);
            SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, samples);
        }

        m_win = SDL_CreateWindow(pWindow_name, 20, 20, width, height, SDL_WINDOW_OPENGL);
        // Don't actually create the context here, that will be done later.
        // TODO: Support better window creation modes, like with a particular backbuffer format.

    #else
        #error "Need vogl_replay_window::open for this platform"
        return false;
    #endif

    m_width = width;
    m_height = height;

    uint32_t actual_width = 0, actual_height = 0;
    vogl_replay_window::get_actual_dimensions(actual_width, actual_height);
    vogl_debug_printf("Created window, requested dimensions %ux%u, actual dimensions %ux%u\n", m_width, m_height, actual_width, actual_height);

    return true;
}
Пример #4
0
bool vogl_trace_file_writer::close()
{
    VOGL_FUNC_TRACER

    vogl_debug_printf("%s\n", VOGL_METHOD_NAME);

    if (!m_stream.is_opened())
        return false;

    vogl_message_printf("%s: Flushing trace file %s (this could take some time), %u total frame file offsets\n", VOGL_METHOD_NAME, m_filename.get_ptr(), m_frame_file_offsets.size());

    bool success = true;

    dynamic_string trace_archive_filename;

    if (!write_eof_packet())
    {
        vogl_error_printf("%s: Failed writing to trace file \"%s\"\n", VOGL_METHOD_NAME, m_filename.get_ptr());
        success = false;
    }
    else if (m_pTrace_archive.get())
    {
        trace_archive_filename = m_pTrace_archive->get_archive_filename();

        if ((!write_frame_file_offsets_to_archive()) || !m_pTrace_archive->deinit())
        {
            vogl_error_printf("%s: Failed closing trace archive \"%s\"!\n", VOGL_FUNCTION_NAME, trace_archive_filename.get_ptr());
            success = false;
        }
        else
        {
            if (!file_utils::get_file_size(trace_archive_filename.get_ptr(), m_sof_packet.m_archive_size))
            {
                vogl_error_printf("%s: Failed determining file size of archive file \"%s\"\n", VOGL_FUNCTION_NAME, trace_archive_filename.get_ptr());
                success = false;
            }
            else if (m_sof_packet.m_archive_size)
            {
                m_sof_packet.m_archive_offset = m_stream.get_size();

                vogl_message_printf("Copying %" PRIu64 " archive bytes into output trace file\n", m_sof_packet.m_archive_size);

                if (!m_stream.write_file_data(trace_archive_filename.get_ptr()))
                {
                    vogl_error_printf("%s: Failed copying source archive \"%s\" into trace file!\n", VOGL_METHOD_NAME, trace_archive_filename.get_ptr());
                    success = false;
                }

                m_sof_packet.m_archive_size = m_stream.get_size() - m_sof_packet.m_archive_offset;
            }
        }

        if (success)
        {
            m_sof_packet.finalize();
            VOGL_VERIFY(m_sof_packet.full_validation(sizeof(m_sof_packet)));

            if (!m_stream.seek(0, false) || (m_stream.write(&m_sof_packet, sizeof(m_sof_packet)) != sizeof(m_sof_packet)))
            {
                vogl_error_printf("%s: Failed writing to trace file \"%s\"\n", VOGL_METHOD_NAME, m_filename.get_ptr());
                success = false;
            }
        }
    }

    close_archive(trace_archive_filename.get_ptr());

    uint64_t total_trace_file_size = m_stream.get_size();

    if (!m_stream.close())
    {
        vogl_error_printf("Failed writing to or closing trace file!\n");
        success = false;
    }

    dynamic_string full_trace_filename(m_filename);
    file_utils::full_path(full_trace_filename);

    if (success)
        vogl_message_printf("%s: Successfully closed trace file %s, total file size: %s\n", VOGL_METHOD_NAME, full_trace_filename.get_ptr(), uint64_to_string_with_commas(total_trace_file_size).get_ptr());
    else
        vogl_error_printf("%s: Failed closing trace file %s! (Trace will probably not be valid.)\n", VOGL_METHOD_NAME, full_trace_filename.get_ptr());

    return success;
}
Пример #5
0
bool vogl_replay_window::open(int width, int height, int samples)
{
    VOGL_FUNC_TRACER
    #if (VOGL_PLATFORM_HAS_GLX)

        close();

        if (!check_glx_version())
            return false;

        // TODO: These attribs (especially the sizes) should be passed in by the caller!
        int fbAttribs[64];

        int *pAttribs = fbAttribs;

        *pAttribs++ = GLX_RENDER_TYPE;      *pAttribs++ = GLX_RGBA_BIT;
        *pAttribs++ = GLX_X_RENDERABLE;     *pAttribs++ = True;
        *pAttribs++ = GLX_DRAWABLE_TYPE;    *pAttribs++ = GLX_WINDOW_BIT;
        *pAttribs++ = GLX_DOUBLEBUFFER;     *pAttribs++ = True;
        *pAttribs++ = GLX_RED_SIZE;         *pAttribs++ = 8;
        *pAttribs++ = GLX_BLUE_SIZE;        *pAttribs++ = 8;
        *pAttribs++ = GLX_GREEN_SIZE;       *pAttribs++ = 8;
        *pAttribs++ = GLX_ALPHA_SIZE;       *pAttribs++ = 8;
        *pAttribs++ = GLX_DEPTH_SIZE;       *pAttribs++ = 24;
        *pAttribs++ = GLX_STENCIL_SIZE;     *pAttribs++ = 8;

        if (samples > 1)
        {
            *pAttribs++ = GLX_SAMPLE_BUFFERS; *pAttribs++ = 1;
            *pAttribs++ = GLX_SAMPLES;        *pAttribs++ = samples;
        }

        *pAttribs++ = 0;

        // Tell X we are going to use the display
        m_dpy = XOpenDisplay(NULL);
        if (!m_dpy)
        {
            console::error("%s: XOpenDisplay() failed!\n", VOGL_FUNCTION_INFO_CSTR);
            return false;
        }

        // Get a new fb config that meets our attrib requirements

        m_pFB_configs = GL_ENTRYPOINT(glXChooseFBConfig)(m_dpy, DefaultScreen(m_dpy), fbAttribs, &m_num_fb_configs);
        if ((!m_pFB_configs) || (!m_num_fb_configs))
        {
            console::error("%s: glXChooseFBConfig() failed!\n", VOGL_FUNCTION_INFO_CSTR);
            return false;
        }

        XVisualInfo *pVisual_info = GL_ENTRYPOINT(glXGetVisualFromFBConfig)(m_dpy, m_pFB_configs[0]);
        if (!pVisual_info)
        {
            console::error("%s: glXGetVisualFromFBConfig() failed!\n", VOGL_FUNCTION_INFO_CSTR);
            return false;
        }

        // Now create an X window
        XSetWindowAttributes winAttribs;
        winAttribs.event_mask = ExposureMask | VisibilityChangeMask |
                                KeyPressMask | PointerMotionMask |
                                StructureNotifyMask;

        winAttribs.border_pixel = 0;
        winAttribs.bit_gravity = StaticGravity;
        winAttribs.colormap = XCreateColormap(m_dpy,
                                              RootWindow(m_dpy, pVisual_info->screen),
                                              pVisual_info->visual, AllocNone);
        GLint winmask = CWBorderPixel | CWBitGravity | CWEventMask | CWColormap;

        m_win = XCreateWindow(m_dpy, DefaultRootWindow(m_dpy), 20, 20,
                              width, height, 0,
                              pVisual_info->depth, InputOutput,
                              pVisual_info->visual, winmask, &winAttribs);

        const char *pWindow_name = (sizeof(void *) == sizeof(uint32)) ? "voglreplay 32-bit" : "voglreplay 64-bit";
        XStoreName(m_dpy, m_win, pWindow_name);
        XSetIconName(m_dpy, m_win, pWindow_name);

        XSizeHints sh;
        utils::zero_object(sh);
        sh.x = 0; // slam position up so when/if we resize the window glReadPixels still works as expected (this may be a bug in the NV driver, I dunno yet)
        sh.y = 0;
        sh.width = sh.min_width = sh.max_width = sh.base_width = width;
        sh.height = sh.min_height = sh.max_height = sh.base_height = height;
        sh.flags = PSize | PMinSize | PMaxSize | PBaseSize | PPosition;
        XSetWMNormalHints(m_dpy, m_win, &sh);

        XResizeWindow(m_dpy, m_win, width, height);

        XMapWindow(m_dpy, m_win);

        //glXWaitX();

        m_width = width;
        m_height = height;

        uint actual_width = 0, actual_height = 0;
        vogl_replay_window::get_actual_dimensions(actual_width, actual_height);
        vogl_debug_printf("%s: Created window, requested dimensions %ux%u, actual dimensions %ux%u\n", VOGL_FUNCTION_INFO_CSTR, m_width, m_height, actual_width, actual_height);

        return true;
    #else
        VOGL_ASSERT(!"impl");
        return false;
    #endif
}
Пример #6
0
    //----------------------------------------------------------------------------------------------------------------------
    // tool_replay_mode
    //----------------------------------------------------------------------------------------------------------------------
    static bool tool_replay_mode()
    {
        VOGL_FUNC_TRACER

        dynamic_string trace_filename(g_command_line_params().get_value_as_string_or_empty("", 1));
        if (trace_filename.is_empty())
        {
            vogl_error_printf("No trace file specified!\n");
            return false;
        }

        dynamic_string actual_trace_filename;
        vogl_unique_ptr<vogl_trace_file_reader> pTrace_reader(
            vogl_open_trace_file(
                trace_filename,
                actual_trace_filename,
                g_command_line_params().get_value_as_string_or_empty("loose_file_path").get_ptr()
            )
        );

        if (!pTrace_reader.get())
        {
            vogl_error_printf("File not found, or unable to determine file type of trace file \"%s\"\n", trace_filename.get_ptr());
            return false;
        }

        vogl_printf("Reading trace file %s\n", actual_trace_filename.get_ptr());

        vogl_gl_replayer replayer;
        vogl_replay_window window;

        #if defined(PLATFORM_WINDOWS)
            // We need to get proc addresses for windows late.
            replayer.set_proc_address_helper(vogl_get_proc_address_helper, false);
        #endif

        uint replayer_flags = get_replayer_flags_from_command_line_params();

        // TODO: This will create a window with default attributes, which seems fine for the majority of traces.
        // Unfortunately, some GL call streams *don't* want an alpha channel, or depth, or stencil etc. in the default framebuffer so this may become a problem.
        // Also, this design only supports a single window, which is going to be a problem with multiple window traces.
        if (!window.open(g_command_line_params().get_value_as_int("width", 0, 1024, 1, 65535), g_command_line_params().get_value_as_int("height", 0, 768, 1, 65535), g_command_line_params().get_value_as_int("msaa", 0, 0, 0, 65535)))
        {
            vogl_error_printf("Failed initializing replay window\n");
            return false;
        }

        if (!replayer.init(replayer_flags, &window, pTrace_reader->get_sof_packet(), pTrace_reader->get_multi_blob_manager()))
        {
            vogl_error_printf("Failed initializing GL replayer\n");
            return false;
        }

        // Disable all glGetError() calls in vogl_utils.cpp.
        vogl_disable_gl_get_error();

        // Bool win_mapped = false;

        vogl_gl_state_snapshot *pSnapshot = NULL;
        int64_t snapshot_loop_start_frame = -1;
        int64_t snapshot_loop_end_frame = -1;

        vogl::hash_map<SDL_Keycode> keys_pressed, keys_down;

        int loop_frame = g_command_line_params().get_value_as_int("loop_frame", 0, -1);
        int loop_len = math::maximum<int>(g_command_line_params().get_value_as_int("loop_len", 0, 1), 1);
        int loop_count = math::maximum<int>(g_command_line_params().get_value_as_int("loop_count", 0, cINT32_MAX), 1);
        bool endless_mode = g_command_line_params().get_value_as_bool("endless");

        timer tm;
        tm.start();

        for (;;)
        {
            tmZone(TELEMETRY_LEVEL0, TMZF_NONE, "Main Loop");

            SDL_Event wnd_event;
            
            while (SDL_PollEvent(&wnd_event))
            {
                switch (wnd_event.type)
                {
                    case SDL_KEYDOWN:
                    {
                        keys_down.insert(wnd_event.key.keysym.sym);
                        keys_pressed.insert(wnd_event.key.keysym.sym);
                        break;
                    }

                    case SDL_KEYUP:
                    {
                        keys_down.erase(wnd_event.key.keysym.sym);

                        break;
                    }

                    case SDL_WINDOWEVENT:
                    {
                        switch(wnd_event.window.event)
                        {
                            case SDL_WINDOWEVENT_FOCUS_GAINED:
                            case SDL_WINDOWEVENT_FOCUS_LOST:
                                keys_down.reset();
                                break;

                            case SDL_WINDOWEVENT_CLOSE:
                                vogl_message_printf("Window told to close, exiting.\n");
                                goto normal_exit;
                                break;
                            default:
                                break;
                        };
                        break;
                    }

                    default:
                        // TODO: Handle these somehow?
                        break;
                };
            }

            if (replayer.get_at_frame_boundary())
            {
                if ((!pSnapshot) && (loop_frame != -1) && (static_cast<int64_t>(replayer.get_frame_index()) == loop_frame))
                {
                    vogl_debug_printf("Capturing replayer state at start of frame %u\n", replayer.get_frame_index());

                    pSnapshot = replayer.snapshot_state();

                    if (pSnapshot)
                    {
                        vogl_printf("Snapshot succeeded\n");

                        snapshot_loop_start_frame = pTrace_reader->get_cur_frame();
                        snapshot_loop_end_frame = pTrace_reader->get_cur_frame() + loop_len;

                        vogl_debug_printf("Loop start: %" PRIi64 " Loop end: %" PRIi64 "\n", snapshot_loop_start_frame, snapshot_loop_end_frame);
                    }
                    else
                    {
                        vogl_error_printf("Snapshot failed!\n");
                        loop_frame = -1;
                    }
                }
            }

            vogl_gl_replayer::status_t status = replayer.process_pending_window_resize();
            if (status == vogl_gl_replayer::cStatusOK)
            {
                for (;;)
                {
                    status = replayer.process_pending_packets();

                    if (status == vogl_gl_replayer::cStatusOK)
                    {
                        status = replayer.process_next_packet(*pTrace_reader);
                    }

                    if ((status == vogl_gl_replayer::cStatusNextFrame) ||
                        (status == vogl_gl_replayer::cStatusResizeWindow) ||
                        (status == vogl_gl_replayer::cStatusAtEOF) ||
                        (status == vogl_gl_replayer::cStatusHardFailure))
                    {
                        break;
                    }
                }
            }

            if (status == vogl_gl_replayer::cStatusHardFailure)
                break;

            if (status == vogl_gl_replayer::cStatusAtEOF)
            {
                vogl_message_printf("At trace EOF, frame index %u\n", replayer.get_frame_index());
            }

            if (replayer.get_at_frame_boundary() &&
                pSnapshot && 
                (loop_count > 0) &&
                ((pTrace_reader->get_cur_frame() == snapshot_loop_end_frame) || (status == vogl_gl_replayer::cStatusAtEOF)))
            {
                status = replayer.begin_applying_snapshot(pSnapshot, false);
                if ((status != vogl_gl_replayer::cStatusOK) && (status != vogl_gl_replayer::cStatusResizeWindow))
                    goto error_exit;

                pTrace_reader->seek_to_frame(static_cast<uint>(snapshot_loop_start_frame));

                vogl_debug_printf("Applying snapshot and seeking back to frame %" PRIi64 "\n", snapshot_loop_start_frame);
                loop_count--;
            }
            else
            {
                bool print_progress = (status == vogl_gl_replayer::cStatusAtEOF) ||
                    ((replayer.get_at_frame_boundary()) && ((replayer.get_frame_index() % 100) == 0));
                if (print_progress)
                {
                    if (pTrace_reader->get_type() == cBINARY_TRACE_FILE_READER)
                    {
                        vogl_binary_trace_file_reader &binary_trace_reader = *static_cast<vogl_binary_trace_file_reader *>(pTrace_reader.get());

                        vogl_printf("Replay now at frame index %u, trace file offet %" PRIu64 ", GL call counter %" PRIu64 ", %3.2f%% percent complete\n",
                            replayer.get_frame_index(),
                            binary_trace_reader.get_cur_file_ofs(),
                            replayer.get_last_parsed_call_counter(),
                            binary_trace_reader.get_trace_file_size() ? (binary_trace_reader.get_cur_file_ofs() * 100.0f) / binary_trace_reader.get_trace_file_size() : 0);
                    }
                }

                if (status == vogl_gl_replayer::cStatusAtEOF)
                {
                    if (!endless_mode)
                    {
                        double time_since_start = tm.get_elapsed_secs();

                        vogl_printf("%u total swaps, %.3f secs, %3.3f avg fps\n", replayer.get_total_swaps(), time_since_start, replayer.get_frame_index() / time_since_start);
                        break;
                    }

                    vogl_printf("Resetting state and rewinding back to frame 0\n");

                    replayer.reset_state();

                    if (!pTrace_reader->seek_to_frame(0))
                    {
                        vogl_error_printf("Failed rewinding trace reader!\n");
                        goto error_exit;
                    }
                }
            }

            telemetry_tick();
        }

    normal_exit:
        return true;

    error_exit:
        return false;
    }
Пример #7
0
//----------------------------------------------------------------------------------------------------------------------
// tool_replay_mode
//----------------------------------------------------------------------------------------------------------------------
static bool tool_replay_mode()
{
    VOGL_FUNC_TRACER

    dynamic_string trace_filename(g_command_line_params().get_value_as_string_or_empty("", 1));
    if (trace_filename.is_empty())
    {
        vogl_error_printf("%s: No trace file specified!\n", VOGL_FUNCTION_INFO_CSTR);
        return false;
    }

    dynamic_string actual_trace_filename;
    vogl_unique_ptr<vogl_trace_file_reader> pTrace_reader(vogl_open_trace_file(
                trace_filename,
                actual_trace_filename,
                g_command_line_params().get_value_as_string_or_empty("loose_file_path").get_ptr()));
    if (!pTrace_reader.get())
    {
        vogl_error_printf("%s: File not found, or unable to determine file type of trace file \"%s\"\n", VOGL_FUNCTION_INFO_CSTR, trace_filename.get_ptr());
        return false;
    }

    vogl_printf("Reading trace file %s\n", actual_trace_filename.get_ptr());

    vogl_gl_replayer replayer;
    vogl_replay_window window;

    uint replayer_flags = get_replayer_flags_from_command_line_params();

    // TODO: This will create a window with default attributes, which seems fine for the majority of traces.
    // Unfortunately, some GL call streams *don't* want an alpha channel, or depth, or stencil etc. in the default framebuffer so this may become a problem.
    // Also, this design only supports a single window, which is going to be a problem with multiple window traces.
    if (!window.open(g_command_line_params().get_value_as_int("width", 0, 1024, 1, 65535), g_command_line_params().get_value_as_int("height", 0, 768, 1, 65535), g_command_line_params().get_value_as_int("msaa", 0, 0, 0, 65535)))
    {
        vogl_error_printf("%s: Failed initializing replay window\n", VOGL_FUNCTION_INFO_CSTR);
        return false;
    }

    if (!replayer.init(replayer_flags, &window, pTrace_reader->get_sof_packet(), pTrace_reader->get_multi_blob_manager()))
    {
        vogl_error_printf("%s: Failed initializing GL replayer\n", VOGL_FUNCTION_INFO_CSTR);
        return false;
    }

    // Disable all glGetError() calls in vogl_utils.cpp.
    vogl_disable_gl_get_error();

    XSelectInput(window.get_display(), window.get_xwindow(),
                 EnterWindowMask | LeaveWindowMask | ButtonPressMask | ButtonReleaseMask | PointerMotionMask | ExposureMask | FocusChangeMask | KeyPressMask | KeyReleaseMask | PropertyChangeMask | StructureNotifyMask | KeymapStateMask);

    Atom wmDeleteMessage = XInternAtom(window.get_display(), "WM_DELETE_WINDOW", False);
    XSetWMProtocols(window.get_display(), window.get_xwindow(), &wmDeleteMessage, 1);

    // Bool win_mapped = false;

    vogl_gl_state_snapshot *pSnapshot = NULL;
    int64_t snapshot_loop_start_frame = -1;
    int64_t snapshot_loop_end_frame = -1;

    vogl::hash_map<uint64_t> keys_pressed, keys_down;

    int loop_frame = g_command_line_params().get_value_as_int("loop_frame", 0, -1);
    int loop_len = math::maximum<int>(g_command_line_params().get_value_as_int("loop_len", 0, 1), 1);
    int loop_count = math::maximum<int>(g_command_line_params().get_value_as_int("loop_count", 0, cINT32_MAX), 1);
    bool endless_mode = g_command_line_params().get_value_as_bool("endless");

    timer tm;
    tm.start();

    for (;;)
    {
        tmZone(TELEMETRY_LEVEL0, TMZF_NONE, "Main Loop");

        while (X11_Pending(window.get_display()))
        {
            XEvent newEvent;

            // Watch for new X eventsn
            XNextEvent(window.get_display(), &newEvent);

            switch (newEvent.type)
            {
                case KeyPress:
                {
                    KeySym xsym = XLookupKeysym(&newEvent.xkey, 0);

                    //printf("KeyPress 0%04llX %" PRIu64 "\n", (uint64_t)xsym, (uint64_t)xsym);

                    keys_down.insert(xsym);
                    keys_pressed.insert(xsym);

                    break;
                }
                case KeyRelease:
                {
                    KeySym xsym = XLookupKeysym(&newEvent.xkey, 0);

                    //printf("KeyRelease 0x%04llX %" PRIu64 "\n", (uint64_t)xsym, (uint64_t)xsym);

                    keys_down.erase(xsym);

                    break;
                }
                case FocusIn:
                case FocusOut:
                {
                    //printf("FocusIn/FocusOut\n");

                    keys_down.reset();

                    break;
                }
                case MappingNotify:
                {
                    //XRefreshKeyboardMapping(&newEvent);
                    break;
                }
                case UnmapNotify:
                {
                    // printf("UnmapNotify\n");
                    // win_mapped = false;

                    keys_down.reset();

                    break;
                }
                case MapNotify:
                {
                    // printf("MapNotify\n");
                    // win_mapped = true;

                    keys_down.reset();

                    if (!replayer.update_window_dimensions())
                        goto error_exit;

                    break;
                }
                case ConfigureNotify:
                {
                    if (!replayer.update_window_dimensions())
                        goto error_exit;

                    break;
                }
                case DestroyNotify:
                {
                    vogl_message_printf("Exiting\n");
                    goto normal_exit;
                }
                case ClientMessage:
                {
                    if (newEvent.xclient.data.l[0] == (int)wmDeleteMessage)
                    {
                        vogl_message_printf("Exiting\n");
                        goto normal_exit;
                    }

                    break;
                }
                default:
                    break;
            }
        }

        if (replayer.get_at_frame_boundary())
        {
            if ((!pSnapshot) && (loop_frame != -1) && (static_cast<int64_t>(replayer.get_frame_index()) == loop_frame))
            {
                vogl_debug_printf("%s: Capturing replayer state at start of frame %u\n", VOGL_FUNCTION_INFO_CSTR, replayer.get_frame_index());

                pSnapshot = replayer.snapshot_state();

                if (pSnapshot)
                {
                    vogl_printf("Snapshot succeeded\n");

                    snapshot_loop_start_frame = pTrace_reader->get_cur_frame();
                    snapshot_loop_end_frame = pTrace_reader->get_cur_frame() + loop_len;

                    vogl_debug_printf("%s: Loop start: %" PRIi64 " Loop end: %" PRIi64 "\n", VOGL_FUNCTION_INFO_CSTR, snapshot_loop_start_frame, snapshot_loop_end_frame);
                }
                else
                {
                    vogl_error_printf("Snapshot failed!\n");
                    loop_frame = -1;
                }
            }
        }

        vogl_gl_replayer::status_t status = replayer.process_pending_window_resize();
        if (status == vogl_gl_replayer::cStatusOK)
        {
            for (;;)
            {
                status = replayer.process_next_packet(*pTrace_reader);

                if ((status == vogl_gl_replayer::cStatusNextFrame) ||
                    (status == vogl_gl_replayer::cStatusResizeWindow) ||
                    (status == vogl_gl_replayer::cStatusAtEOF) ||
                    (status == vogl_gl_replayer::cStatusHardFailure))
                {
                    break;
                }
            }
        }

        if (status == vogl_gl_replayer::cStatusHardFailure)
            break;

        if (status == vogl_gl_replayer::cStatusAtEOF)
        {
            vogl_message_printf("%s: At trace EOF, frame index %u\n", VOGL_FUNCTION_INFO_CSTR, replayer.get_frame_index());
        }

        if (replayer.get_at_frame_boundary() &&
                pSnapshot && 
                (loop_count > 0) &&
                ((pTrace_reader->get_cur_frame() == snapshot_loop_end_frame) || (status == vogl_gl_replayer::cStatusAtEOF)))
        {
            status = replayer.begin_applying_snapshot(pSnapshot, false);
            if ((status != vogl_gl_replayer::cStatusOK) && (status != vogl_gl_replayer::cStatusResizeWindow))
                goto error_exit;

            pTrace_reader->seek_to_frame(static_cast<uint>(snapshot_loop_start_frame));

            vogl_debug_printf("%s: Applying snapshot and seeking back to frame %" PRIi64 "\n", VOGL_FUNCTION_INFO_CSTR, snapshot_loop_start_frame);
            loop_count--;
        }
        else
        {
            bool print_progress = (status == vogl_gl_replayer::cStatusAtEOF) ||
                    ((replayer.get_at_frame_boundary()) && ((replayer.get_frame_index() % 100) == 0));
            if (print_progress)
            {
                if (pTrace_reader->get_type() == cBINARY_TRACE_FILE_READER)
                {
                    vogl_binary_trace_file_reader &binary_trace_reader = *static_cast<vogl_binary_trace_file_reader *>(pTrace_reader.get());

                    vogl_printf("Replay now at frame index %u, trace file offet %" PRIu64 ", GL call counter %" PRIu64 ", %3.2f%% percent complete\n",
                               replayer.get_frame_index(),
                               binary_trace_reader.get_cur_file_ofs(),
                               replayer.get_last_parsed_call_counter(),
                               binary_trace_reader.get_trace_file_size() ? (binary_trace_reader.get_cur_file_ofs() * 100.0f) / binary_trace_reader.get_trace_file_size() : 0);
                }
            }

            if (status == vogl_gl_replayer::cStatusAtEOF)
            {
                if (!endless_mode)
                {
                    double time_since_start = tm.get_elapsed_secs();

                    vogl_printf("%u total swaps, %.3f secs, %3.3f avg fps\n", replayer.get_total_swaps(), time_since_start, replayer.get_frame_index() / time_since_start);
                    break;
                }

                vogl_printf("Resetting state and rewinding back to frame 0\n");

                replayer.reset_state();

                if (!pTrace_reader->seek_to_frame(0))
                {
                    vogl_error_printf("%s: Failed rewinding trace reader!\n", VOGL_FUNCTION_INFO_CSTR);
                    goto error_exit;
                }
            }
        }

        telemetry_tick();
    }

normal_exit:
    return true;

error_exit:
    return false;
}