Skip to content

File openwarp_vk.hpp

File List > openwarp_vk > openwarp_vk.hpp

Go to the documentation of this file

#pragma once

#include "illixr/data_format/pose_prediction.hpp"
#include "illixr/hmd.hpp"
#include "illixr/switchboard.hpp"
// #include "illixr/threadloop.hpp"
#include "illixr/vk/display_provider.hpp"
#include "illixr/vk/render_pass.hpp"

#if defined(_WIN32) || defined(_WIN64)
#    include 
#endif

#define GLM_FORCE_RADIANS
#define GLM_FORCE_DEPTH_ZERO_TO_ONE
#define GLM_ENABLE_EXPERIMENTAL
#include 
#include 
#include 
#include 
#include 

namespace ILLIXR {

struct DistortionCorrectionVertex {
    glm::vec3 pos;
    glm::vec2 uv0;
    glm::vec2 uv1;
    glm::vec2 uv2;

    static VkVertexInputBindingDescription get_binding_description() {
        VkVertexInputBindingDescription binding_description = {};
        binding_description.binding                         = 0;            // index of the binding in the array of bindings
        binding_description.stride    = sizeof(DistortionCorrectionVertex); // number of bytes from one entry to the next
        binding_description.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;        // no instancing

        return binding_description;
    }

    static std::array<VkVertexInputAttributeDescription, 4> get_attribute_descriptions() {
        std::array<VkVertexInputAttributeDescription, 4> attribute_descriptions = {};

        // position
        attribute_descriptions[0].binding  = 0;                          // which binding the per-vertex data comes from
        attribute_descriptions[0].location = 0;                          // location directive of the input in the vertex shader
        attribute_descriptions[0].format   = VK_FORMAT_R32G32B32_SFLOAT; // format of the data
        attribute_descriptions[0].offset =
            offsetof(DistortionCorrectionVertex, pos); // number of bytes since the start of the per-vertex data to read from

        // uv0
        attribute_descriptions[1].binding  = 0;
        attribute_descriptions[1].location = 1;
        attribute_descriptions[1].format   = VK_FORMAT_R32G32_SFLOAT;
        attribute_descriptions[1].offset   = offsetof(DistortionCorrectionVertex, uv0);

        // uv1
        attribute_descriptions[2].binding  = 0;
        attribute_descriptions[2].location = 2;
        attribute_descriptions[2].format   = VK_FORMAT_R32G32_SFLOAT;
        attribute_descriptions[2].offset   = offsetof(DistortionCorrectionVertex, uv1);

        // uv2
        attribute_descriptions[3].binding  = 0;
        attribute_descriptions[3].location = 3;
        attribute_descriptions[3].format   = VK_FORMAT_R32G32_SFLOAT;
        attribute_descriptions[3].offset   = offsetof(DistortionCorrectionVertex, uv2);

        return attribute_descriptions;
    }
};

struct OpenWarpVertex {
    glm::vec3 pos;
    glm::vec2 uv;

    static VkVertexInputBindingDescription get_binding_description() {
        VkVertexInputBindingDescription binding_description = {};
        binding_description.binding                         = 0;     // index of the binding in the array of bindings
        binding_description.stride    = sizeof(OpenWarpVertex);      // number of bytes from one entry to the next
        binding_description.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; // no instancing

        return binding_description;
    }

    static std::array<VkVertexInputAttributeDescription, 2> get_attribute_descriptions() {
        std::array<VkVertexInputAttributeDescription, 2> attribute_descriptions = {};

        // position
        attribute_descriptions[0].binding  = 0;                          // which binding the per-vertex data comes from
        attribute_descriptions[0].location = 0;                          // location directive of the input in the vertex shader
        attribute_descriptions[0].format   = VK_FORMAT_R32G32B32_SFLOAT; // format of the data
        attribute_descriptions[0].offset =
            offsetof(OpenWarpVertex, pos); // number of bytes since the start of the per-vertex data to read from

        // uv
        attribute_descriptions[1].binding  = 0;
        attribute_descriptions[1].location = 1;
        attribute_descriptions[1].format   = VK_FORMAT_R32G32_SFLOAT;
        attribute_descriptions[1].offset   = offsetof(OpenWarpVertex, uv);

        return attribute_descriptions;
    }
};

struct DistortionMatrix {
    glm::mat4 transform;
};

struct WarpMatrices {
    glm::mat4 render_inv_projection[2];
    glm::mat4 render_inv_view[2];
    glm::mat4 warp_view_projection[2];
};

class openwarp_vk : public vulkan::timewarp {
public:
    explicit openwarp_vk(const phonebook* pb);
    void initialize();
    void setup(VkRenderPass render_pass, uint32_t subpass,
               std::shared_ptr<vulkan::buffer_pool<data_format::pose::fast_head_pose_type>> buffer_pool_,
               bool input_texture_external_, struct illixr_framebuffer* framebuffer_array, VkExtent2D extent) override;
    void partial_destroy();
    void update_uniforms(const data_format::pose::fast_head_pose_type& render_pose) override;
    void record_command_buffer(VkCommandBuffer commandBuffer, VkFramebuffer framebuffer, int buffer_ind, bool left) override;
    bool is_external() override;
    void destroy() override;

private:
    void                   create_offscreen_images();
    void                   create_vertex_buffers();
    void                   create_index_buffers();
    void                   generate_distortion_data();
    void                   generate_openwarp_mesh(size_t width, size_t height);
    void                   create_texture_sampler();
    void                   create_descriptor_set_layouts();
    void                   create_uniform_buffers();
    void                   create_descriptor_pool();
    void                   create_descriptor_sets();
    void                   create_openwarp_pipeline();
    VkPipeline             create_distortion_correction_pipeline(VkRenderPass render_pass, [[maybe_unused]] uint32_t subpass);
    static Eigen::Matrix4f create_camera_matrix(const data_format::pose::head_pose_type& pose, int eye);
    static Eigen::Matrix4f calculate_distortion_transform(const Eigen::Matrix4f& projection_matrix);

    const phonebook* const             phonebook_;
    const std::shared_ptr<switchboard> switchboard_;

    const std::shared_ptr<data_format::pose_prediction> pose_prediction_;

    bool                                      disable_warp_     = false;
    std::shared_ptr<vulkan::display_provider> display_provider_ = nullptr;
    std::mutex                                setup_mutex_;

    bool initialized_            = false;
    bool input_texture_external_ = false;

    bool using_godot_         = false;
    bool offloaded_rendering_ = false;

    uint64_t frame_count_ = 0;

    // Vulkan resources
    std::stack<std::function<void()>> deletion_queue_;
    VmaAllocator                      vma_allocator_{};

    // Note that each frame occupies 2 elements in the buffer pool:
    // i for the image itself, and i + 1 for the depth image.
    size_t swapchain_width_{};
    size_t swapchain_height_{};

    std::shared_ptr<vulkan::buffer_pool<data_format::pose::fast_head_pose_type>> buffer_pool_;

    VkSampler fb_sampler_{};

    VkDescriptorPool descriptor_pool_{};
    VkCommandPool    command_pool_{};
    VkCommandBuffer  command_buffer_{};

    // offscreen image used as an intermediate render target
    std::array<VkImage, 2>       offscreen_images_{};
    std::array<VkImageView, 2>   offscreen_image_views_{};
    std::array<VmaAllocation, 2> offscreen_image_allocs_{};

    std::array<VkImage, 2>       offscreen_depths_{};
    std::array<VkImageView, 2>   offscreen_depth_views_{};
    std::array<VmaAllocation, 2> offscreen_depth_allocs_{};

    std::array<VkFramebuffer, 2> offscreen_framebuffers_{};

    // openwarp mesh
    VkPipelineLayout ow_pipeline_layout_{};

    VkBuffer          ow_matrices_uniform_buffer_{};
    VmaAllocation     ow_matrices_uniform_alloc_{};
    VmaAllocationInfo ow_matrices_uniform_alloc_info_{};

    VkDescriptorSetLayout                       ow_descriptor_set_layout_{};
    std::array<std::vector<VkDescriptorSet>, 2> ow_descriptor_sets_;

    uint32_t                    num_openwarp_vertices_{};
    uint32_t                    num_openwarp_indices_{};
    std::vector<OpenWarpVertex> openwarp_vertices_;
    std::vector<uint32_t>       openwarp_indices_;
    size_t                      openwarp_width_  = 0;
    size_t                      openwarp_height_ = 0;

    VkBuffer      ow_vertex_buffer_{};
    VmaAllocation ow_vertex_alloc_{};
    VkBuffer      ow_index_buffer_{};
    VmaAllocation ow_index_alloc_{};

    VkRenderPass openwarp_render_pass_{};
    VkPipeline   openwarp_pipeline_ = VK_NULL_HANDLE;

    // distortion data
    HMD::hmd_info_t hmd_info_{};
    Eigen::Matrix4f basic_projection_[2];
    Eigen::Matrix4f inverse_projection_[2];

    VkPipelineLayout  dp_pipeline_layout_{};
    VkBuffer          dp_uniform_buffer_{};
    VmaAllocation     dp_uniform_alloc_{};
    VmaAllocationInfo dp_uniform_alloc_info_{};

    VkDescriptorSetLayout                       dp_descriptor_set_layout_{};
    std::array<std::vector<VkDescriptorSet>, 2> dp_descriptor_sets_;

    uint32_t                                num_distortion_vertices_{};
    uint32_t                                num_distortion_indices_{};
    std::vector<DistortionCorrectionVertex> distortion_vertices_;
    std::vector<uint32_t>                   distortion_indices_;

    VkRenderPass  distortion_correction_render_pass_{};
    VkBuffer      dc_vertex_buffer_{};
    VmaAllocation dc_vertex_alloc_{};
    VkBuffer      dc_index_buffer_{};
    VmaAllocation dc_index_alloc_{};

    // metrics
    std::atomic<uint32_t> num_record_calls_{0};
    std::atomic<uint32_t> num_update_uniforms_calls_{0};

    friend class openwarp_vk_plugin;
};

} // namespace ILLIXR