178 lines
5.0 KiB
C++
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();
|
|
}
|
|
}
|
|
|
|
}
|