Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
1076a99
Implement custom controller time sync
RealSupremium Aug 17, 2025
1012f2e
Merge remote-tracking branch 'origin/main' into custom-controller-sync
RealSupremium Aug 23, 2025
0457854
Simplify syncing
RealSupremium Aug 24, 2025
717504f
Implement LED based latency calculation on controller start up. Add o…
RealSupremium Sep 2, 2025
1ae3acc
Fix issue with sync getting stuck
RealSupremium Sep 2, 2025
2963de1
Fix disableGaze breaking sync. Finish battery optimization work.
RealSupremium Sep 2, 2025
e5f5e62
Oops
RealSupremium Sep 2, 2025
603e25a
Put broad back to period length 36
RealSupremium Sep 2, 2025
780368f
Improve reliability and time to calibrate latency
RealSupremium Sep 4, 2025
a7f1842
Ensure calibration is valid by checking controller tracking status
RealSupremium Sep 5, 2025
beca0fb
Add timeout to avoid stuck async writes
RealSupremium Sep 5, 2025
4c86aae
Tweak and fix LED energy improvements. Use LED command system.
RealSupremium Sep 6, 2025
c45c657
Clean up a little bit. Move controller specific information into Sens…
RealSupremium Sep 6, 2025
b2e374a
Set bounds as calibration progresses and add edge validation steps
RealSupremium Sep 7, 2025
8482e20
Per controller calibration. Change percentile for time offset. Requir…
RealSupremium Sep 12, 2025
703a0de
Properly cleanup sense controller thread on exit
RealSupremium Sep 12, 2025
1555df7
Merge branch 'main' into custom-controller-sync
RealSupremium Sep 16, 2025
0daa7bd
Merge main into custom-controller-sync
RealSupremium Oct 1, 2025
ff1e552
Fix formatting and logging
RealSupremium Oct 1, 2025
caea288
Reduce dependency on GetProperty in PollNextEvent for controller haptics
RealSupremium Oct 1, 2025
246576c
Merge remote-tracking branch 'origin/main' into custom-controller-sync
RealSupremium Oct 1, 2025
b419da5
Fix NOMINMAX being missing for CI configurations
RealSupremium Oct 1, 2025
571bb16
Fix "phase jump" for enhanced haptics
RealSupremium Oct 2, 2025
3e6725a
Stop calibration if controller is disconnected
RealSupremium Oct 4, 2025
8d7fd75
Fix broken haptics intensity setting in Sony overlay
RealSupremium Oct 5, 2025
f59c63e
Remove debug logging of output report
RealSupremium Oct 9, 2025
c297683
Refactor LED calibration to find both edges and real LED on time
RealSupremium Oct 10, 2025
0e5b471
Improve logging for SetSyncLedCommand, fix controller tracking check …
RealSupremium Oct 11, 2025
d1be165
Merge remote-tracking branch 'origin/main' into custom-controller-sync
RealSupremium Oct 11, 2025
efb1b4b
Prefer int32_t over int
RealSupremium Oct 13, 2025
bd2d4ca
Merge in latest from main
RealSupremium Oct 15, 2025
2d39059
Try lower percentile for a (hopefully) more consistent timestamp offset
RealSupremium Nov 13, 2025
3009a42
Merge in changes from main
RealSupremium Nov 21, 2025
498655a
Fix build issue on VS2026
RealSupremium Nov 21, 2025
c820d08
Use lowest time sync offset estimation with slow backoff
RealSupremium Nov 26, 2025
3931463
Filter offset to avoid large jumps that affect the driver UKF
RealSupremium Nov 26, 2025
8da7e15
Make backoff more aggressive for machines that may skew differently
RealSupremium Nov 27, 2025
52b25e4
Clean up and moving stuff around
RealSupremium Dec 5, 2025
39b4832
Handle duration of 0 better, various small haptics improvements
RealSupremium Dec 6, 2025
c189ede
Merge remote-tracking branch 'origin/main' into custom-controller-sync
RealSupremium Dec 11, 2025
5d8f2b9
Remove logging for too many pending operations, allow pending 3 opera…
RealSupremium Dec 12, 2025
122d4b3
Merge in main
RealSupremium Dec 19, 2025
0251a7d
Make custom sync possible without enhanced haptics
RealSupremium Dec 27, 2025
1a0b6db
Merge remote-tracking branch 'origin/main' into custom-controller-sync
RealSupremium Feb 6, 2026
d5c75b9
Formatting pointers/references to right and some cleanup
RealSupremium Feb 6, 2026
ce5b6b0
Formatting touch-ups
RealSupremium Feb 6, 2026
5a0c780
Some more formatting touch-ups
RealSupremium Feb 6, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 17 additions & 5 deletions projects/psvr2_openvr_driver_ex/device_provider_proxy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@
#include "hmd_driver_loader.h"
#include "hook_lib.h"
#include "ipc_server.h"
#include "libpad_hooks.h"
#include "trigger_effect_manager.h"
#include "usb_thread_hooks.h"
#include "util.h"
#include "vr_settings.h"

#include <windows.h>
#include "sense_controller.h"

using namespace psvr2_toolkit::ipc;

Expand All @@ -23,7 +25,8 @@ namespace psvr2_toolkit {
DeviceProviderProxy::DeviceProviderProxy()
: m_initOnce(false)
, m_pDeviceProvider(nullptr)
{}
{
}

DeviceProviderProxy *DeviceProviderProxy::Instance() {
if (!m_pInstance) {
Expand Down Expand Up @@ -53,12 +56,17 @@ namespace psvr2_toolkit {

static DriverContextProxy *pDriverContextProxy = DriverContextProxy::Instance();
pDriverContextProxy->SetDriverContext(pDriverContext);

return m_pDeviceProvider->Init(pDriverContextProxy);
}

void DeviceProviderProxy::Cleanup() {
IpcServer::Instance()->Stop();

if (VRSettings::GetBool(STEAMVR_SETTINGS_USE_ENHANCED_HAPTICS, SETTING_USE_TOOLKIT_SYNC_DEFAULT_VALUE)) {
SenseController::Destroy();
}

m_pDeviceProvider->Cleanup();

VR_CLEANUP_SERVER_DRIVER_CONTEXT();
Expand Down Expand Up @@ -116,26 +124,30 @@ namespace psvr2_toolkit {

// If disableSense is enabled, we must disable the overlay and dialog regardless due to a bug.
if (VRSettings::GetBool(STEAMVR_SETTINGS_DISABLE_OVERLAY, SETTING_DISABLE_OVERLAY_DEFAULT_VALUE) ||
VRSettings::GetBool(STEAMVR_SETTINGS_DISABLE_SENSE, SETTING_DISABLE_SENSE_DEFAULT_VALUE) ||
isRunningOnWine)
VRSettings::GetBool(STEAMVR_SETTINGS_DISABLE_SENSE, SETTING_DISABLE_SENSE_DEFAULT_VALUE) ||
isRunningOnWine)
{
INSTALL_STUB(reinterpret_cast<void *>(pHmdDriverLoader->GetBaseAddress() + 0x12F830)); // VrDialogManager::CreateDashboardProcess
}
if (VRSettings::GetBool(STEAMVR_SETTINGS_DISABLE_DIALOG, SETTING_DISABLE_DIALOG_DEFAULT_VALUE) ||
VRSettings::GetBool(STEAMVR_SETTINGS_DISABLE_SENSE, SETTING_DISABLE_SENSE_DEFAULT_VALUE) ||
isRunningOnWine)
VRSettings::GetBool(STEAMVR_SETTINGS_DISABLE_SENSE, SETTING_DISABLE_SENSE_DEFAULT_VALUE) ||
isRunningOnWine)
{
INSTALL_STUB(reinterpret_cast<void *>(pHmdDriverLoader->GetBaseAddress() + 0x130020)); // VrDialogManager::CreateDialogProcess
}

CaesarManagerHooks::InstallHooks();
HmdDeviceHooks::InstallHooks();
LibpadHooks::InstallHooks();
UsbThreadHooks::InstallHooks();
}

void DeviceProviderProxy::InitSystems() {
IpcServer::Instance()->Initialize();
TriggerEffectManager::Instance()->Initialize();
if (VRSettings::GetBool(STEAMVR_SETTINGS_USE_ENHANCED_HAPTICS, SETTING_USE_TOOLKIT_SYNC_DEFAULT_VALUE)) {
SenseController::Initialize();
}
}

} // psvr2_toolkit
38 changes: 32 additions & 6 deletions projects/psvr2_openvr_driver_ex/driver_host_proxy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ namespace psvr2_toolkit {
: m_pDriverHost(nullptr)
, m_pfnEventHandlers()
{}

DriverHostProxy *DriverHostProxy::Instance() {
if (!m_pInstance) {
m_pInstance = new DriverHostProxy;
Expand All @@ -39,12 +39,38 @@ namespace psvr2_toolkit {

bool DriverHostProxy::TrackedDeviceAdded(const char *pchDeviceSerialNumber, vr::ETrackedDeviceClass eDeviceClass, vr::ITrackedDeviceServerDriver *pDriver) {
if (Util::StartsWith(pchDeviceSerialNumber, "playstation_vr2_sense_controller_") &&
VRSettings::GetBool(STEAMVR_SETTINGS_DISABLE_SENSE, SETTING_DISABLE_SENSE_DEFAULT_VALUE))
VRSettings::GetBool(STEAMVR_SETTINGS_DISABLE_SENSE, SETTING_DISABLE_SENSE_DEFAULT_VALUE))
{
return false;
}

return m_pDriverHost->TrackedDeviceAdded(pchDeviceSerialNumber, eDeviceClass, pDriver);
bool success = m_pDriverHost->TrackedDeviceAdded(pchDeviceSerialNumber, eDeviceClass, pDriver);

if (success)
{
switch (eDeviceClass)
{
case vr::ETrackedDeviceClass::TrackedDeviceClass_HMD:
if (hmdContainer == vr::k_ulInvalidPropertyContainer) {
hmdContainer = vr::VRProperties()->TrackedDeviceToPropertyContainer(k_unDeviceIndexHeadset);
}
break;
case vr::ETrackedDeviceClass::TrackedDeviceClass_Controller:
if (Util::StartsWith(pchDeviceSerialNumber, "playstation_vr2_sense_controller_left")) {
if (leftControllerContainer == vr::k_ulInvalidPropertyContainer) {
leftControllerContainer = vr::VRProperties()->TrackedDeviceToPropertyContainer(k_unDeviceIndexSenseControllerLeft);
}
}

else if (Util::StartsWith(pchDeviceSerialNumber, "playstation_vr2_sense_controller_right")) {
if (rightControllerContainer == vr::k_ulInvalidPropertyContainer) {
rightControllerContainer = vr::VRProperties()->TrackedDeviceToPropertyContainer(k_unDeviceIndexSenseControllerRight);
}
}
}
}

return success;
}

void DriverHostProxy::TrackedDevicePoseUpdated(uint32_t unWhichDevice, const vr::DriverPose_t &newPose, uint32_t unPoseStructSize) {
Expand All @@ -69,7 +95,7 @@ namespace psvr2_toolkit {

bool DriverHostProxy::PollNextEvent(vr::VREvent_t *pEvent, uint32_t uncbVREvent) {
if (m_pDriverHost->PollNextEvent(pEvent, uncbVREvent)) {
for (auto& m_pfnEventHandler : m_pfnEventHandlers) {
for (auto &m_pfnEventHandler : m_pfnEventHandlers) {
m_pfnEventHandler(pEvent);
}
return true;
Expand Down Expand Up @@ -115,7 +141,7 @@ namespace psvr2_toolkit {
newPose.qRotation = HmdMath::QuaternionMultiply(newPose.qRotation, imuRotationOffsetInverse);

// PS VR2 driver pose offset.
vr::HmdVector3d_t poseOffset = {isLeft ? 0.03439270332455635 : -0.03439270332455635, 0.05370872840285301, -0.09804324805736542};
vr::HmdVector3d_t poseOffset = { isLeft ? 0.03439270332455635 : -0.03439270332455635, 0.05370872840285301, -0.09804324805736542 };

// Rotate the offset by the new rotation.
vr::HmdVector3d_t rotationOffset = HmdMath::RotateVectorByQuaternion(poseOffset, newPose.qRotation);
Expand All @@ -127,7 +153,7 @@ namespace psvr2_toolkit {

// Offset from the driver's root to the IMU. Given by the PS VR2 driver.
// We'll also have to factor it to make the result pose identical to the one from the driver.
vr::HmdVector3d_t imuOffset = {isLeft ? -0.00937270000576973 : 0.020072702318429947, 0.012248100712895393, 0.006003900431096554};
vr::HmdVector3d_t imuOffset = { isLeft ? -0.00937270000576973 : 0.020072702318429947, 0.012248100712895393, 0.006003900431096554 };

// Rotate IMU offset to counteract the rotation we did on qRotation. See next comment.
imuOffset = HmdMath::RotateVectorByQuaternion(imuOffset, imuRotationOffset);
Expand Down
19 changes: 19 additions & 0 deletions projects/psvr2_openvr_driver_ex/driver_host_proxy.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#pragma once

#include "hmd_types.h"

#include <openvr_driver.h>

#include <list>
Expand All @@ -15,6 +17,19 @@ namespace psvr2_toolkit {
void SetDriverHost(vr::IVRServerDriverHost *pDriverHost);
void AddEventHandler(void (*pfnEventHandler)(vr::VREvent_t *)); // Required for intercepting polled events from the PS VR2 driver.

DeviceType GetDeviceType(vr::PropertyContainerHandle_t propertyContainer) const {
if (propertyContainer == hmdContainer) {
return DeviceType::HMD;
}
else if (propertyContainer == leftControllerContainer) {
return DeviceType::SenseControllerLeft;
}
else if (propertyContainer == rightControllerContainer) {
return DeviceType::SenseControllerRight;
}
return DeviceType::None;
}

/** IVRServerDriverHost **/

bool TrackedDeviceAdded(const char *pchDeviceSerialNumber, vr::ETrackedDeviceClass eDeviceClass, vr::ITrackedDeviceServerDriver *pDriver) override;
Expand All @@ -38,6 +53,10 @@ namespace psvr2_toolkit {

// Used internally for controller pose correction.
vr::DriverPose_t GetPose(uint32_t unWhichDevice, const vr::DriverPose_t &originalPose);

vr::PropertyContainerHandle_t hmdContainer = vr::k_ulInvalidPropertyContainer;
vr::PropertyContainerHandle_t leftControllerContainer = vr::k_ulInvalidPropertyContainer;
vr::PropertyContainerHandle_t rightControllerContainer = vr::k_ulInvalidPropertyContainer;
};

} // psvr2_toolkit
68 changes: 34 additions & 34 deletions projects/psvr2_openvr_driver_ex/hmd_device_hooks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,19 @@
#include <cstdint>

namespace psvr2_toolkit {
void* (*CaesarManager__getInstance)();
uint64_t (*CaesarManager__getIMUTimestampOffset)(void* thisptr, int64_t* hmdToHostOffset);
void* (*ShareManager__getInstance)();
void (*ShareManager__getIntConfig)(void* thisPtr, uint32_t configId, int64_t* outValue);
void (*ShareManager__setIntConfig)(void* thisPtr, uint32_t configId, int64_t* value);
void *(*CaesarManager__getInstance)();
uint64_t(*CaesarManager__getIMUTimestampOffset)(void *thisptr, int64_t *hmdToHostOffset);
void *(*ShareManager__getInstance)();
void (*ShareManager__getIntConfig)(void *thisPtr, uint32_t configId, int64_t *outValue);
void (*ShareManager__setIntConfig)(void *thisPtr, uint32_t configId, int64_t *value);

#ifdef OPENVR_EXTENSIONS_AVAILABLE
void* g_pOpenVRExHandle = nullptr;
void *g_pOpenVRExHandle = nullptr;
#endif
vr::VRInputComponentHandle_t eyeTrackingComponent;
int64_t currentBrightness;

vr::EVRInitError (*sie__psvr2__HmdDevice__Activate)(void *, uint32_t) = nullptr;
vr::EVRInitError(*sie__psvr2__HmdDevice__Activate)(void *, uint32_t) = nullptr;
vr::EVRInitError sie__psvr2__HmdDevice__ActivateHook(void *thisptr, uint32_t unObjectId) {
vr::EVRInitError result = sie__psvr2__HmdDevice__Activate(thisptr, unObjectId);
vr::PropertyContainerHandle_t ulPropertyContainer = vr::VRProperties()->TrackedDeviceToPropertyContainer(unObjectId);
Expand Down Expand Up @@ -61,7 +61,7 @@ namespace psvr2_toolkit {
vr::VRSettings()->SetFloat(vr::k_pch_SteamVR_Section, "analogGain", powf(static_cast<float>(currentBrightness) / 31.0f, 2.2f));

// Set event handler for when brightness ("analogGain") changes.
DriverHostProxy::Instance()->AddEventHandler([](vr::VREvent_t* event) {
DriverHostProxy::Instance()->AddEventHandler([](vr::VREvent_t *event) {
if (event->eventType == vr::EVREventType::VREvent_SteamVRSectionSettingChanged) {
float currentFloatBrightness = powf(vr::VRSettings()->GetFloat(vr::k_pch_SteamVR_Section, "analogGain"), 1 / 2.2f);
if (static_cast<int64_t>(ceilf(currentFloatBrightness * 31.0f)) != currentBrightness)
Expand All @@ -80,14 +80,14 @@ namespace psvr2_toolkit {

if (vr::VRDriverInput())
{
vr::EVRInputError result = (vr::VRDriverInput())->CreateEyeTrackingComponent(ulPropertyContainer, "/eyetracking", &eyeTrackingComponent);
if (result != vr::VRInputError_None) {
vr::VRDriverLog()->Log("Failed to create eye tracking component.");
}
vr::EVRInputError result = (vr::VRDriverInput())->CreateEyeTrackingComponent(ulPropertyContainer, "/eyetracking", &eyeTrackingComponent);
if (result != vr::VRInputError_None) {
vr::VRDriverLog()->Log("Failed to create eye tracking component.");
}
}
else
{
vr::VRDriverLog()->Log("Failed to get driver input interface. Are you on the latest version of SteamVR?");
vr::VRDriverLog()->Log("Failed to get driver input interface. Are you on the latest version of SteamVR?");
}

#ifdef OPENVR_EXTENSIONS_AVAILABLE
Expand Down Expand Up @@ -120,39 +120,39 @@ namespace psvr2_toolkit {
QueryPerformanceCounter(&now);

return static_cast<int64_t>((static_cast<double>(now.QuadPart) /
static_cast<double>(frequency.QuadPart)) * 1e6);
static_cast<double>(frequency.QuadPart)) * 1e6);
}

void HmdDeviceHooks::UpdateGaze(void* pData, size_t dwSize)
void HmdDeviceHooks::UpdateGaze(void *pData, size_t dwSize)
{
Hmd2GazeState* pGazeState = reinterpret_cast<Hmd2GazeState*>(pData);
vr::VREyeTrackingData_t eyeTrackingData {};
Hmd2GazeState *pGazeState = reinterpret_cast<Hmd2GazeState *>(pData);
vr::VREyeTrackingData_t eyeTrackingData{};

bool valid = pGazeState->combined.isGazeDirValid;
bool valid = pGazeState->combined.isGazeDirValid;

eyeTrackingData.bActive = valid;
eyeTrackingData.bTracked = valid;
eyeTrackingData.bValid = valid;
eyeTrackingData.bActive = valid;
eyeTrackingData.bTracked = valid;
eyeTrackingData.bValid = valid;

auto& origin = pGazeState->combined.gazeOriginMm;
auto& direction = pGazeState->combined.gazeDirNorm;
auto &origin = pGazeState->combined.gazeOriginMm;
auto &direction = pGazeState->combined.gazeDirNorm;

eyeTrackingData.vGazeOrigin = vr::HmdVector3_t { -origin.x / 1000.0f, origin.y / 1000.0f, -origin.z / 1000.0f };
eyeTrackingData.vGazeTarget = vr::HmdVector3_t { -direction.x, direction.y, -direction.z };
eyeTrackingData.vGazeOrigin = vr::HmdVector3_t{ -origin.x / 1000.0f, origin.y / 1000.0f, -origin.z / 1000.0f };
eyeTrackingData.vGazeTarget = vr::HmdVector3_t{ -direction.x, direction.y, -direction.z };

int64_t hmdToHostOffset;
int64_t hmdToHostOffset;

CaesarManager__getIMUTimestampOffset(CaesarManager__getInstance(), &hmdToHostOffset);
CaesarManager__getIMUTimestampOffset(CaesarManager__getInstance(), &hmdToHostOffset);

double timeOffset = ((static_cast<int64_t>(pGazeState->combined.timestamp) + hmdToHostOffset) - GetHostTimestamp()) / 1e6;
double timeOffset = ((static_cast<int64_t>(pGazeState->combined.timestamp) + hmdToHostOffset) - GetHostTimestamp()) / 1e6;

(vr::VRDriverInput())->UpdateEyeTrackingComponent(eyeTrackingComponent, &eyeTrackingData, timeOffset);
(vr::VRDriverInput())->UpdateEyeTrackingComponent(eyeTrackingComponent, &eyeTrackingData, timeOffset);

#ifdef OPENVR_EXTENSIONS_AVAILABLE
if (g_pOpenVRExHandle) {
psvr2_toolkit::openvr_ex::OnHmdUpdate(&g_pOpenVRExHandle, pData, dwSize);
}
#endif
#ifdef OPENVR_EXTENSIONS_AVAILABLE
if (g_pOpenVRExHandle) {
psvr2_toolkit::openvr_ex::OnHmdUpdate(&g_pOpenVRExHandle, pData, dwSize);
}
#endif
}

void HmdDeviceHooks::InstallHooks() {
Expand Down
26 changes: 13 additions & 13 deletions projects/psvr2_openvr_driver_ex/hmd_device_hooks.h
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
#pragma once

#include <openvr_driver.h>

namespace psvr2_toolkit {

class HmdDeviceHooks {
public:
static void InstallHooks();
static void UpdateGaze(void* pData, size_t dwSize);
};

} // psvr2_toolkit
#pragma once
#include <openvr_driver.h>
namespace psvr2_toolkit {
class HmdDeviceHooks {
public:
static void InstallHooks();
static void UpdateGaze(void *pData, size_t dwSize);
};
} // psvr2_toolkit
Loading
Loading