Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement async IPC #38

Open
wants to merge 26 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
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
64 changes: 45 additions & 19 deletions .github/workflows/c-cpp.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: SlimeVR OpenVR Driver

on: [ push, pull_request ]
on: [ push, pull_request, workflow_dispatch ]

jobs:
build:
Expand All @@ -15,11 +15,11 @@ jobs:
- os: windows-latest
triplet: x64-windows-static-md
target: ALL_BUILD
release_dir: Release/driver # dir of driver binaries within env.CMAKE_BUILD_DIR, VS multi-config uses <CONFIG>/ subfolder
release_dir: Release # dir of driver binaries within env.CMAKE_BUILD_DIR, VS multi-config uses <CONFIG>/ subfolder
- os: ubuntu-latest
triplet: x64-linux
target: all
release_dir: driver # makefile single config won't have subfolder
release_dir: "" # makefile single config won't have subfolder
env:
# Indicates the CMake build directory where project files and binaries are being produced.
CMAKE_BUILD_DIR: ${{ github.workspace }}/build
Expand All @@ -30,15 +30,12 @@ jobs:
- uses: actions/checkout@v2
with:
submodules: true

- uses: lukka/get-cmake@latest

- name: Clone vcpkg
uses: actions/checkout@v2
with:
repository: microsoft/vcpkg
path: ${{ env.VCPKG_ROOT }}
submodules: true

- name: Get submodule commit hashes
id: submodule_hashes
run: git submodule foreach --recursive git rev-parse HEAD > submodule_hashes.txt

- name: Restore vcpkg and its artifacts
uses: actions/cache@v2
Expand All @@ -52,12 +49,11 @@ jobs:
!${{ env.VCPKG_ROOT }}/buildtrees
!${{ env.VCPKG_ROOT }}/packages
!${{ env.VCPKG_ROOT }}/downloads
# The key is composed in a way that it gets properly invalidated: this must happen whenever vcpkg's Git commit id changes, or the list of packages changes. In this case a cache miss must happen and a new entry with a new key with be pushed to GitHub the cache service.
# The key is composed in a way that it gets properly invalidated: this must happen whenever vcpkg/submodule Git commit id changes, or the list of packages changes. In this case a cache miss must happen and a new entry with a new key with be pushed to GitHub the cache service.
# The key includes: hash of the vcpkg.json file, the hash of the vcpkg Git commit id, and the used vcpkg's triplet. The vcpkg's commit id would suffice, but computing an hash out it does not harm.
# Note: given a key, the cache content is immutable. If a cache entry has been created improperly, in order the recreate the right content the key must be changed as well, and it must be brand new (i.e. not existing already).
key: |
${{ hashFiles( 'vcpkg_manifest/vcpkg.json' ) }}-${{ hashFiles( '.git/modules/vcpkg/HEAD' )}}-${{ hashFiles( '${{ env.VCPKG_ROOT }}/.git/HEAD' )}}-${{ matrix.triplet }}-invalidate

key: ${{ matrix.triplet }}-${{ hashFiles( '**/vcpkg.json', '**/CMakeLists.txt' ) }}-${{ hashFiles( 'submodule_hashes.txt' )}}

- if: matrix.os == 'windows-latest'
name: Set up vcpkg for Windows
run: ${{ env.VCPKG_ROOT }}/bootstrap-vcpkg.bat
Expand All @@ -80,16 +76,46 @@ jobs:
# A file, directory or wildcard pattern that describes what to upload
# Using wildcards so that only the driver directory gets included (if you specify it, then it won't be included)
path: |
${{env.CMAKE_BUILD_DIR}}/${{ matrix.release_dir }}/*
${{env.CMAKE_BUILD_DIR}}/${{ matrix.release_dir }}/driver/*
${{env.CMAKE_BUILD_DIR}}/${{ matrix.release_dir }}/tests*

- name: Zip
if: startsWith(github.ref, 'refs/tags/')
working-directory: ${{env.CMAKE_BUILD_DIR}}/${{ matrix.release_dir }}
run: cmake -E tar "cf" "${{env.CMAKE_BUILD_DIR}}/slimevr-openvr-driver-${{ matrix.triplet }}.zip" --format=zip -- ${{env.CMAKE_BUILD_DIR}}/${{ matrix.release_dir }}/slimevr
working-directory: ${{env.CMAKE_BUILD_DIR}}/${{ matrix.release_dir }}/driver
run: cmake -E tar "cf" "${{env.CMAKE_BUILD_DIR}}/slimevr-openvr-driver-${{ matrix.triplet }}.zip" --format=zip -- ${{env.CMAKE_BUILD_DIR}}/${{ matrix.release_dir }}/driver/slimevr

- name: Release
uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/')
with:
files: "${{env.CMAKE_BUILD_DIR}}/slimevr-openvr-driver-${{ matrix.triplet }}.zip"


test:
name: Run tests
needs: build
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [windows-latest, ubuntu-latest]
include:
- os: windows-latest
triplet: x64-windows-static-md
target: RUN_TESTS
- os: ubuntu-latest
triplet: x64-linux
target: test
steps:
- name: Download build artifact
uses: actions/download-artifact@v2
with:
name: slimevr-openvr-driver-${{ matrix.triplet }}
path: ${{ github.workspace }}
- if: matrix.os == 'windows-latest'
name: Run tests on Windows
working-directory: ${{ github.workspace }}/
run: .\tests.exe
- if: matrix.os != 'windows-latest'
name: Run tests on Unix
working-directory: ${{ github.workspace }}/
run: chmod +x ./tests && ./tests
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@
[submodule "libraries/linalg"]
path = libraries/linalg
url = https://github.com/sgorsten/linalg.git
[submodule "vcpkg"]
path = vcpkg
url = https://github.com/Microsoft/vcpkg.git
55 changes: 47 additions & 8 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -69,17 +69,56 @@ SET_SOURCE_FILES_PROPERTIES(${PROTO_SRC} ${PROTO_INCL} PROPERTIES GENERATED TRUE

find_package(simdjson CONFIG REQUIRED)

# libuv
find_package(uvw CONFIG REQUIRED)

# Catch2
find_package(Catch2 3 REQUIRED)

# Project
file(GLOB_RECURSE HEADERS "${CMAKE_CURRENT_SOURCE_DIR}/src/*.hpp")
set(DEPS_INCLUDES
"${OPENVR_INCLUDE_DIR}"
"${CMAKE_CURRENT_SOURCE_DIR}/libraries/linalg"
"${CMAKE_CURRENT_SOURCE_DIR}/src/"
)
set(DEPS_LIBS
"${OPENVR_LIB}"
protobuf::libprotoc
protobuf::libprotobuf
protobuf::libprotobuf-lite
simdjson::simdjson
uvw::uvw
)

# compile into a static lib
file(GLOB_RECURSE SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp")
add_library("${PROJECT_NAME}" SHARED "${HEADERS}" "${SOURCES}" ${PROTO_HEADER} ${PROTO_SRC})
target_include_directories("${PROJECT_NAME}" PUBLIC "${OPENVR_INCLUDE_DIR}")
target_include_directories("${PROJECT_NAME}" PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/libraries/linalg")
target_include_directories("${PROJECT_NAME}" PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/src/")
target_link_libraries("${PROJECT_NAME}" PUBLIC "${OPENVR_LIB}" protobuf::libprotoc protobuf::libprotobuf protobuf::libprotobuf-lite simdjson::simdjson)
file(GLOB_RECURSE HEADERS "${CMAKE_CURRENT_SOURCE_DIR}/src/*.hpp")
add_library("${PROJECT_NAME}_static" STATIC ${SOURCES} ${PROTO_HEADER} ${PROTO_SRC})
target_link_libraries("${PROJECT_NAME}_static" PUBLIC ${DEPS_LIBS})
set_property(TARGET "${PROJECT_NAME}_static" PROPERTY CXX_STANDARD 17)
include_directories("${PROJECT_NAME}_static" PUBLIC ${DEPS_INCLUDES} ${Protobuf_INCLUDE_DIRS} ${CMAKE_CURRENT_BINARY_DIR})
if(UNIX)
target_compile_options("${PROJECT_NAME}_static" PRIVATE "-fPIC")
target_link_libraries("${PROJECT_NAME}_static" PUBLIC atomic)
endif()

# compile driver
file(GLOB_RECURSE DRIVER_MAIN "${CMAKE_CURRENT_SOURCE_DIR}/src/DriverFactory.cpp")
add_library("${PROJECT_NAME}" SHARED ${DRIVER_MAIN} ${HEADERS} ${PROTO_HEADER})
target_link_libraries("${PROJECT_NAME}" PUBLIC "${PROJECT_NAME}_static")
set_property(TARGET "${PROJECT_NAME}" PROPERTY CXX_STANDARD 17)
include_directories(${Protobuf_INCLUDE_DIRS})
include_directories(${CMAKE_CURRENT_BINARY_DIR})

# compile tests
function(build_tests target_name test_dir)
file(GLOB TESTS "${CMAKE_CURRENT_SOURCE_DIR}/${test_dir}/*.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/${test_dir}/*.hpp")
file(GLOB TESTS_COMMON "${CMAKE_CURRENT_SOURCE_DIR}/test/common/*.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/test/common/*.hpp")
add_executable(${target_name} ${TESTS} ${TESTS_COMMON} ${HEADERS} ${PROTO_HEADER})
target_link_libraries(${target_name} PUBLIC "${PROJECT_NAME}_static" Catch2::Catch2WithMain)
set_property(TARGET ${target_name} PROPERTY CXX_STANDARD 17)
endfunction()
build_tests(tests "test")
build_tests(tests_integration "test/integration")
add_test(NAME tests COMMAND "tests")

# IDE Config
source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}/src" PREFIX "Header Files" FILES ${HEADERS})
Expand Down
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,16 @@ okay with this and that you are authorized to provide the above licenses.

### Building

To build the project with VSCode you need to install two things: [vcpkg](https://vcpkg.io/en/getting-started.html) and [VS Build Tools](https://visualstudio.microsoft.com/downloads/).
Clone the repo with `git clone --recurse-submodules https://github.com/SlimeVR/SlimeVR-OpenVR-Driver` to clone with all libraries and [vcpkg](https://vcpkg.io/en/getting-started.html) registry.

To build the project with VSCode on Windows you need to install [VS Build Tools](https://visualstudio.microsoft.com/downloads/).

Run the bootstrap script to build vcpkg binary `.\vcpkg\bootstrap-vcpkg.bat` or `./vcpkg/bootstrap-vcpkg.sh`.

After installing vcpkg if you're on Windows, you need to run `vcpkg integrate install` command from the vcpkg folder to integrate it for VSCode.

For other systems and IDEs instructions are not available as of now, contributions are welcome.

### Updating vcpkg packages

To update vcpkg packages set the vcpkg registry submodule to a newer commit.
25 changes: 13 additions & 12 deletions src/DriverFactory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,22 @@
static std::shared_ptr<SlimeVRDriver::IVRDriver> driver;

void* HmdDriverFactory(const char* interface_name, int* return_code) {
if (std::strcmp(interface_name, vr::IServerTrackedDeviceProvider_Version) == 0) {
if (!driver) {
// Instantiate concrete impl
driver = std::make_shared<SlimeVRDriver::VRDriver>();
}
// We always have at least 1 ref to the shared ptr in "driver" so passing out raw pointer is ok
return driver.get();
}
if (std::strcmp(interface_name, vr::IServerTrackedDeviceProvider_Version) == 0) {
if (!driver) {
// Instantiate concrete impl
driver = std::make_shared<SlimeVRDriver::VRDriver>();
}
// We always have at least 1 ref to the shared ptr in "driver" so passing out raw pointer is ok
return driver.get();
}

if (return_code)
*return_code = vr::VRInitError_Init_InterfaceNotFound;
if (return_code) {
*return_code = vr::VRInitError_Init_InterfaceNotFound;
}

return nullptr;
return nullptr;
}

std::shared_ptr<SlimeVRDriver::IVRDriver> SlimeVRDriver::GetDriver() {
return driver;
return driver;
}
78 changes: 50 additions & 28 deletions src/IVRDevice.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,38 +6,41 @@
#include "ProtobufMessages.pb.h"

namespace SlimeVRDriver {

class IVRDevice : public vr::ITrackedDeviceServerDriver {
public:
/// <summary>
/// Returns the serial string for this device
/// </summary>
/// <returns>Device serial</returns>
/**
* Returns the serial string for this device.
*
* @return Device serial.
*/
virtual std::string GetSerial() = 0;

/// <summary>
/// Runs any update logic for this device.
/// Called once per frame
/// </summary>
/**
* Runs any update logic for this device.
* Called once per frame.
*/
virtual void Update() = 0;

/// <summary>
/// Returns the OpenVR device index
/// This should be 0 for HMDs
/// </summary>
/// <returns>OpenVR device index</returns>
/**
* Returns the OpenVR device index.
* This should be 0 for HMDs.
*
* @returns OpenVR device index.
*/
virtual vr::TrackedDeviceIndex_t GetDeviceIndex() = 0;

/// <summary>
/// Returns which type of device this device is
/// </summary>
/// <returns>The type of device</returns>
/**
* Returns which type of device this device is.
*
* @returns The type of device.
*/
virtual DeviceType GetDeviceType() = 0;

/// <summary>
/// Makes a default device pose
/// </summary>
/// <returns>Default initialised pose</returns>
/**
* Makes a default device pose.
*
* @returns Default initialised pose.
*/
static inline vr::DriverPose_t MakeDefaultPose(bool connected = true, bool tracking = true) {
vr::DriverPose_t out_pose = { 0 };

Expand All @@ -51,6 +54,31 @@ namespace SlimeVRDriver {
return out_pose;
}

/**
* Returns the device id.
*/
virtual int GetDeviceId() = 0;

/**
* Sets the device id.
*/
virtual void SetDeviceId(int device_id) = 0;

/**
* Updates device position from a received message.
*/
virtual void PositionMessage(messages::Position& position) = 0;

/**
* Updates device status from a received message.
*/
virtual void StatusMessage(messages::TrackerStatus& status) = 0;

/**
* Updates battery indicator from a received message.
*/
virtual void BatteryMessage(messages::Battery& battery) = 0;

// Inherited via ITrackedDeviceServerDriver
virtual vr::EVRInitError Activate(uint32_t unObjectId) = 0;
virtual void Deactivate() = 0;
Expand All @@ -59,12 +87,6 @@ namespace SlimeVRDriver {
virtual void DebugRequest(const char* pchRequest, char* pchResponseBuffer, uint32_t unResponseBufferSize) = 0;
virtual vr::DriverPose_t GetPose() = 0;

virtual int getDeviceId() = 0;
virtual void PositionMessage(messages::Position& position) = 0;
virtual void StatusMessage(messages::TrackerStatus& status) = 0;

virtual void BatteryMessage(messages::Battery& battery) = 0;

~IVRDevice() = default;
};
};
Loading
Loading