Skip to content

File plugin.hpp

File List > plugins > timewarp_gl > plugin.hpp

Go to the documentation of this file

#pragma once
#define VULKAN_REQUIRED
#if defined(_WIN32) || defined(_WIN64)
#    include 
#endif
#ifdef __ANDROID__
#    include "illixr/common_lock.hpp"
#endif
#include "illixr/data_format/frame.hpp"
#include "illixr/data_format/misc.hpp"
#include "illixr/data_format/pose_prediction.hpp"
#include "illixr/data_format/poses/pose_base.hpp"
#ifndef __ANDROID__
#    include "illixr/extended_window.hpp"
#endif
#include "illixr/hmd.hpp"
#include "illixr/phonebook.hpp"
#include "illixr/relative_clock.hpp"
#include "illixr/switchboard.hpp"
#include "illixr/threadloop.hpp"

namespace ILLIXR {

#ifdef __ANDROID__
#    define EGL_EGLEXT_PROTOTYPES 1
#    define GL_GLEXT_PROTOTYPES
#    include 

typedef EGLDisplay     TW_DISPLAY;
typedef ANativeWindow* TW_WINDOW;
typedef EGLContext     TW_GL_CONTEXT;
#elif defined(__linux__)
typedef Display*   TW_DISPLAY;
typedef Window     TW_WINDOW;
typedef GLXContext TW_GL_CONTEXT;
#else
typedef HGLRC TW_GL_CONTEXT;
#endif

#ifdef ENABLE_MONADO
typedef plugin timewarp_type;
#else
typedef threadloop timewarp_type;
#endif

class timewarp_gl : public timewarp_type {
public:
    // Public constructor, create_component passes Switchboard handles ("plugs")
    // to this constructor. In turn, the constructor fills in the private
    // references to the switchboard plugs, so the component can read the
    // data whenever it needs to.
    timewarp_gl(const std::string& name, phonebook* pb);
    void _setup();
    void _prepare_rendering();
    void warp(const switchboard::ptr<const data_format::rendered_frame>& most_recent_frame);
#ifndef ENABLE_MONADO
    skip_option _p_should_skip() override;
    void        _p_thread_setup() override;
    void        _p_one_iteration() override;
#endif

private:
    GLubyte* read_texture_image();
#ifdef __ANDROID__
    void vulkanGL_interop_buffer(const data_format::vk_buffer_handle& vk_buffer_handle, data_format::swapchain_usage usage);
#else
    void import_vulkan_image(const data_format::vk_image_handle& vk_handle, data_format::swapchain_usage usage);
#endif

    static GLuint convert_vk_format_to_GL(int64_t vk_format
#ifdef __ANDROID__
                                          ,
                                          GLint swizzle_mask[]
#endif
    );

    void        build_timewarp(HMD::hmd_info_t& hmd_info);
    static void calculate_time_warp_transform(Eigen::Matrix4f& transform, const Eigen::Matrix4f& render_projection_matrix,
                                              const Eigen::Matrix4f& render_view_matrix,
                                              const Eigen::Matrix4f& new_view_matrix);
#ifndef ENABLE_MONADO
    [[nodiscard]] time_point get_next_swap_time_estimate() const;
    [[nodiscard]] duration   estimate_time_to_sleep(double frame_percentage) const;
#elif defined(__ANDROID__)
    void import_vulkan_semaphore(const data_format::semaphore_handle& vk_handle);
#endif

    const std::shared_ptr<switchboard>                  switchboard_;
    const std::shared_ptr<data_format::pose_prediction> pose_prediction_;
#ifdef __ANDROID__
    const std::shared_ptr<common_lock> lock_;
#endif
    const std::shared_ptr<const relative_clock> clock_;

#if defined(_WIN32) || defined(_WIN64)
    HWND hwnd_;
    HDC  hdc_;
#else
    // OpenGL objects
    TW_DISPLAY display_;
#    if defined(__linux__) || (defined(__ANDROID) && !defined(ENABLE_MONADO))
    TW_WINDOW root_window_;
#    endif
#    ifdef __ANDROID__
    EGLSurface surface_;
#    endif
#endif
    TW_GL_CONTEXT context_;

    // Shared objects between ILLIXR and the application (either gldemo or Monado)
    bool                      rendering_ready_;
    data_format::graphics_api client_backend_;
    std::atomic<bool>         image_handles_ready_{};

    // Left and right eye images
    std::array<std::vector<data_format::image_handle>, 2> eye_image_handles_;
    std::array<std::vector<GLuint>, 2>                    eye_swapchains_;
    std::array<size_t, 2>                                 eye_swapchains_size_{};

    // Intermediate timewarp framebuffers for left and right eye textures
    std::array<GLuint, 2> eye_output_textures_{};
    std::array<GLuint, 2> eye_framebuffers_{};

#ifdef ENABLE_MONADO
    std::array<data_format::image_handle, 2> eye_output_handles_;

    // Synchronization helper for Monado
    switchboard::writer<data_format::signal_to_quad> signal_quad_;
#    ifdef __ANDROID__
    ullong signal_quad_seq_{0};
#    endif

    // When using Monado, timewarp is a plugin and not a threadloop, but we still keep track of the iteration number
    std::size_t iteration_no = 0;
#else
    // Note: 0.9 works fine without hologram, but we need a larger safety net with hologram enabled
    static constexpr double DELAY_FRACTION = 0.9;

    // Switchboard plug for application eye buffer.
    switchboard::reader<data_format::rendered_frame> eyebuffer_;

    // Switchboard plug for publishing vsync estimates
    switchboard::writer<switchboard::event_wrapper<time_point>> vsync_estimate_;

    // Switchboard plug for publishing offloaded data
    switchboard::writer<data_format::pose::texture_pose> offload_data_;
    // Timewarp only has vsync estimates with native-gl
    record_coalescer mtp_logger_;
#endif

    GLuint timewarp_shader_program_{};

    time_point time_last_swap_{};

    HMD::hmd_info_t hmd_info_{};

    // Eye sampler array
    GLuint                  eye_sampler_0_{};
    [[maybe_unused]] GLuint eye_sampler_1_{};

    // Eye index uniform
    [[maybe_unused]] GLuint tw_eye_index_uniform_{};

    // VAOs
    GLuint tw_vao{};

    // Position and UV attribute locations
    GLuint distortion_pos_attr_{};
    GLuint distortion_uv0_attr_{};
    GLuint distortion_uv1_attr_{};
    GLuint distortion_uv2_attr_{};

    // Distortion mesh information
    GLuint num_distortion_vertices_{};
    GLuint num_distortion_indices_{};

    // Distortion mesh CPU buffers and GPU VBO handles
    std::vector<HMD::mesh_coord3d_t> distortion_positions_;
    GLuint                           distortion_positions_vbo_{};
    std::vector<GLuint>              distortion_indices_;
    GLuint                           distortion_indices_vbo_{};
    std::vector<HMD::uv_coord_t>     distortion_uv0_;
    GLuint                           distortion_uv0_vbo_{};
    std::vector<HMD::uv_coord_t>     distortion_uv1_;
    GLuint                           distortion_uv1_vbo_{};
    std::vector<HMD::uv_coord_t>     distortion_uv2_;
    GLuint                           distortion_uv2_vbo_{};

    // Handles to the start and end timewarp
    // transform matrices (3x4 uniforms)
    GLuint tw_start_transform_uniform_{};
    GLuint tw_end_transform_uniform_{};
    // bool uniform to check if the Y axis needs to be inverted
    GLuint flip_y_uniform_{};
    // Basic perspective projection matrix
    Eigen::Matrix4f basic_projection_;

    // Hologram call data
    ullong hologram_seq_{0};
#ifndef __ANDROID__
    ullong signal_quad_seq_{0};
#endif

    bool disable_warp_;

    bool             enable_offload_;
    record_coalescer timewarp_gpu_logger_;
    // Switchboard plug for sending hologram calls
    switchboard::writer<data_format::hologram_input> hologram_;

    // PBO buffer for reading texture image
    GLuint PBO_buffer_{};

    duration offload_duration_{};

#ifndef NDEBUG
    size_t log_count_  = 0;
    size_t LOG_PERIOD_ = 20;
#endif
};

} // namespace ILLIXR