Files
panopainter/src/platform_windows/windows_vr_shell.h

178 lines
5.0 KiB
C++

#pragma once
#include "app.h"
#include "hmd.h"
#include "log.h"
#include <atomic>
#include <condition_variable>
#include <functional>
#include <memory>
#include <mutex>
#include <thread>
namespace pp::platform::windows {
[[nodiscard]] App* bound_app() noexcept;
struct VrShellState final {
std::mutex snapshot_mutex;
VrSessionSnapshot session;
std::jthread hmd_renderer;
std::mutex hmd_render_mutex;
std::condition_variable hmd_render_cv;
std::unique_ptr<Vive> vive;
std::atomic<int> vr_frames{0};
std::atomic_bool vr_running{false};
};
inline int current_vr_fps(const VrShellState& state)
{
return state.vr_frames.load(std::memory_order_relaxed);
}
inline VrSessionSnapshot read_vr_session_snapshot(VrShellState& state)
{
std::lock_guard<std::mutex> lock(state.snapshot_mutex);
return state.session;
}
inline void write_vr_session_snapshot(
VrShellState& state,
bool has_vr,
bool vr_active,
const VRController& primary_controller,
const glm::mat4& vr_head)
{
std::lock_guard<std::mutex> lock(state.snapshot_mutex);
state.session.has_vr = has_vr;
state.session.vr_active = vr_active;
state.session.vr_controllers[0] = primary_controller;
state.session.vr_head = vr_head;
}
inline void clear_vr_session_snapshot(VrShellState& state, bool has_vr)
{
std::lock_guard<std::mutex> lock(state.snapshot_mutex);
state.session.has_vr = has_vr;
state.session.vr_active = false;
state.session.vr_controllers[0] = {};
state.session.vr_head = glm::mat4(1.0f);
}
inline void request_stop_and_join_vr_thread(VrShellState& state)
{
if (state.hmd_renderer.joinable())
{
state.hmd_renderer.request_stop();
state.hmd_renderer.join();
}
}
inline bool start_vr_shell(VrShellState& state, bool sandboxed, std::atomic<int>& running)
{
if (sandboxed)
return false;
auto* app = bound_app();
state.vive = std::make_unique<Vive>();
state.vive->on_draw = [app](const glm::mat4& proj, const glm::mat4& view, const glm::mat4& pose) {
app->vr_draw(proj, view, pose);
};
if (!state.vive->Initialize())
{
state.vive.reset();
LOG("VR: failed to initialize vive");
return false;
}
if (state.hmd_renderer.joinable())
{
state.hmd_renderer.request_stop();
state.hmd_renderer.join();
}
state.hmd_renderer = std::jthread([app, &state, &running](std::stop_token stop_token) {
if (!state.vive)
return;
BT_SetTerminate();
LOG("start hmd render thread");
clear_vr_session_snapshot(state, true);
state.vr_running.store(true, std::memory_order_relaxed);
state.vive->on_analog_button = std::bind(&App::vr_analog, app, std::placeholders::_1,
std::placeholders::_2, std::placeholders::_3, std::placeholders::_4);
state.vive->on_button = std::bind(&App::vr_digital, app, std::placeholders::_1,
std::placeholders::_2, std::placeholders::_3, std::placeholders::_4);
app->render_task([app] {
app->vr_draw_ui();
});
const float target_tick_rate = 90;
auto t0 = GetTickCount64();
float one_sec_timer = 0;
int frames = 0;
while (!stop_token.stop_requested() &&
state.vr_running.load(std::memory_order_relaxed) &&
running.load(std::memory_order_relaxed) == 1 &&
state.vive->Valid())
{
std::unique_lock<std::mutex> lock(state.hmd_render_mutex);
auto t1 = GetTickCount64();
float dt = (float)(t1 - t0) / 1000.0f;
one_sec_timer += dt;
if (one_sec_timer >= 1.f)
{
one_sec_timer = 0;
state.vr_frames.store(frames, std::memory_order_relaxed);
frames = 0;
}
frames++;
state.vive->Update();
write_vr_session_snapshot(
state,
true,
state.vive->m_active,
state.vive->m_controllers[0],
state.vive->m_pose);
app->vr_update(dt);
if (state.vr_running.load(std::memory_order_relaxed) && state.vive->m_active)
{
app->render_task([&state] {
state.vive->Draw();
});
}
const int framerate = (1.f / target_tick_rate) * 1000;
const int diff = framerate - (t1 - t0);
(void)diff;
//state.hmd_render_cv.wait_for(lock, std::chrono::milliseconds(diff));
t0 = t1;
}
clear_vr_session_snapshot(state, false);
state.vr_running.store(false, std::memory_order_relaxed);
state.vive->Terminate();
LOG("hmd renderer terminated");
});
return true;
}
inline void stop_vr_shell(VrShellState& state)
{
if (state.vive)
{
state.vr_running.store(false, std::memory_order_relaxed);
request_stop_and_join_vr_thread(state);
state.vive->Terminate();
state.vive.reset();
}
}
}