diff --git a/CMakeLists.txt b/CMakeLists.txt index 6483ba4e831f139e97e2e99028f2b62e8dfc629e..50a6bc9b2b87ae27935f601651407f5e5ab23e44 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,17 +19,21 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") include(CMakeDependentOption) include(SPIR-V) include(GNUInstallDirs) +if(NOT ${CMAKE_VERSION} VERSION_LESS 3.9) + include(CheckIPOSupported) + check_ipo_supported(RESULT HAS_IPO) +endif() find_package(Eigen3 REQUIRED) find_package(Vulkan REQUIRED) find_package(EGL) find_package(HIDAPI) find_package(OpenHMD) -find_package(OpenCV COMPONENTS core calib3d highgui imgproc imgcodecs features2d video) +find_package(OpenCV COMPONENTS core calib3d highgui imgproc imgcodecs features2d video CONFIG) find_package(Libusb1) find_package(JPEG) -find_package(realsense2) -find_package(SDL2) +find_package(realsense2 CONFIG) +find_package(SDL2 CONFIG) find_package(Threads) find_package(ZLIB) find_package(cJSON) @@ -64,6 +68,7 @@ else() find_package(OpenGL) endif() +cmake_dependent_option(CMAKE_INTERPROCEDURAL_OPTIMIZATION "Enable inter-procedural (link-time) optimization" OFF "HAS_IPO" OFF) cmake_dependent_option(XRT_HAVE_WAYLAND "Enable Wayland support" ON "WAYLAND_FOUND AND WAYLAND_SCANNER_FOUND AND WAYLAND_PROTOCOLS_FOUND" OFF) cmake_dependent_option(XRT_HAVE_XLIB "Enable xlib support" ON "X11_FOUND" OFF) cmake_dependent_option(XRT_HAVE_XCB "Enable xcb support" ON "XCB_FOUND" OFF) @@ -152,6 +157,10 @@ endif() # Default to PIC code set(CMAKE_POSITION_INDEPENDENT_CODE ON) +# Describe IPO setting +if(CMAKE_INTERPROCEDURAL_OPTIMIZATION) + message(STATUS "Inter-procedural optimization enabled") +endif() ### # Decend into madness. diff --git a/doc/CHANGELOG.md b/doc/CHANGELOG.md index 8b1cea0a008982d41bd312417a0a6272e6cb45a2..82c404089252b97bc34e50c51eb0fe9db0c2fef9 100644 --- a/doc/CHANGELOG.md +++ b/doc/CHANGELOG.md @@ -172,7 +172,7 @@ SPDX-FileCopyrightText: 2020 Collabora, Ltd. and the Monado contributors ([!266](https://gitlab.freedesktop.org/monado/monado/merge_requests/266)) - u/json: Add bool getter function. ([!266](https://gitlab.freedesktop.org/monado/monado/merge_requests/266)) - - tracking: Expose save function with none hardcode path for calibration data. + - tracking: Expose save function with non-hardcoded path for calibration data. ([!266](https://gitlab.freedesktop.org/monado/monado/merge_requests/266)) - tracking: Remove all path hardcoded calibration data loading and saving functions. @@ -180,7 +180,7 @@ SPDX-FileCopyrightText: 2020 Collabora, Ltd. and the Monado contributors - threading: New helper functions and structs for doing threaded work, these are on a higher level then the one in os wrappers. ([!278](https://gitlab.freedesktop.org/monado/monado/merge_requests/278)) - - threading: Fix missing `#pragma once` in `os/os_threading.h`. + - threading: Fix missing `#``pragma once` in `os/os_threading.h`. ([!282](https://gitlab.freedesktop.org/monado/monado/merge_requests/282)) - u/time: Temporarily disable the time skew in time state and used fixed offset instead to fix various time issues in `st/oxr`. Will be fixed properly later. diff --git a/doc/changes/misc_features/mr.330.md b/doc/changes/misc_features/mr.330.md new file mode 100644 index 0000000000000000000000000000000000000000..120b0cf0fbfecbb9a23ff15a94097cb5ac3773fe --- /dev/null +++ b/doc/changes/misc_features/mr.330.md @@ -0,0 +1 @@ +build: Allow enabling inter-procedural optimization in CMake GUIs, if supported by platform and compiler. diff --git a/doc/changes/state_trackers/mr.377.md b/doc/changes/state_trackers/mr.377.md new file mode 100644 index 0000000000000000000000000000000000000000..55313b7b227953126be5706702dd6336b9b38044 --- /dev/null +++ b/doc/changes/state_trackers/mr.377.md @@ -0,0 +1,2 @@ +OpenXR: More correctly verify the interactive profile binding data, including +the given interactive profile is correct and the binding point is valid. diff --git a/src/xrt/auxiliary/util/u_hashmap.cpp b/src/xrt/auxiliary/util/u_hashmap.cpp index 325f1cd659c0a5dbbdac0678beaf4a97dae324ba..7d65135ca0ffce92882f5cab0de1767a40651577 100644 --- a/src/xrt/auxiliary/util/u_hashmap.cpp +++ b/src/xrt/auxiliary/util/u_hashmap.cpp @@ -73,6 +73,12 @@ u_hashmap_int_erase(struct u_hashmap_int *hmi, uint64_t key) return 0; } +bool +u_hashmap_int_empty(const struct u_hashmap_int *hmi) +{ + return hmi->map.empty(); +} + extern "C" void u_hashmap_int_clear_and_call_for_each(struct u_hashmap_int *hmi, u_hashmap_int_callback cb, diff --git a/src/xrt/auxiliary/util/u_hashmap.h b/src/xrt/auxiliary/util/u_hashmap.h index a123e1b26af45e68bf921a39405920c22807a0c6..f9afcf0b52d63c1a6ec70312ecbfd727ea1c24ce 100644 --- a/src/xrt/auxiliary/util/u_hashmap.h +++ b/src/xrt/auxiliary/util/u_hashmap.h @@ -41,6 +41,12 @@ u_hashmap_int_insert(struct u_hashmap_int *hmi, uint64_t key, void *value); int u_hashmap_int_erase(struct u_hashmap_int *hmi, uint64_t key); +/*! + * Is the hash map empty? + */ +bool +u_hashmap_int_empty(const struct u_hashmap_int *hmi); + /*! * First clear the hashmap and then call the given callback with each item that * was in the hashmap. diff --git a/src/xrt/auxiliary/vk/vk_helpers.c b/src/xrt/auxiliary/vk/vk_helpers.c index 4b9061acae662986adccf12f15f5c816682c9891..c499001d627aa08fb93b71a4a83190a4aa14b2fc 100644 --- a/src/xrt/auxiliary/vk/vk_helpers.c +++ b/src/xrt/auxiliary/vk/vk_helpers.c @@ -276,7 +276,8 @@ vk_create_image_from_fd(struct vk_bundle *vk, VkImage *out_image, VkDeviceMemory *out_mem) { - VkImageUsageFlags image_usage = (VkImageUsageFlags)0; + VkImageUsageFlags image_usage = + VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; VkImage image = VK_NULL_HANDLE; VkResult ret = VK_SUCCESS; @@ -291,9 +292,6 @@ vk_create_image_from_fd(struct vk_bundle *vk, if ((swapchain_usage & XRT_SWAPCHAIN_USAGE_DEPTH_STENCIL) != 0) { image_usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; } - if ((swapchain_usage & XRT_SWAPCHAIN_USAGE_UNORDERED_ACCESS) != 0) { - image_usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; - } if ((swapchain_usage & XRT_SWAPCHAIN_USAGE_TRANSFER_SRC) != 0) { image_usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT; } @@ -348,6 +346,38 @@ vk_create_image_from_fd(struct vk_bundle *vk, return ret; } +VkResult +vk_create_semaphore_from_fd(struct vk_bundle *vk, int fd, VkSemaphore *out_sem) +{ + VkResult ret; + + VkSemaphoreCreateInfo semaphore_create_info = { + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, + }; + ret = vk->vkCreateSemaphore(vk->device, &semaphore_create_info, NULL, + out_sem); + if (ret != VK_SUCCESS) { + VK_ERROR(vk, "vkCreateSemaphore: %s", vk_result_string(ret)); + // Nothing to cleanup + return ret; + } + + VkImportSemaphoreFdInfoKHR import_semaphore_fd_info = { + .sType = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR, + .semaphore = *out_sem, + .handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT, + .fd = fd, + }; + ret = vk->vkImportSemaphoreFdKHR(vk->device, &import_semaphore_fd_info); + if (ret != VK_SUCCESS) { + VK_ERROR(vk, "vkImportSemaphoreFdKHR: %s", + vk_result_string(ret)); + vk->vkDestroySemaphore(vk->device, *out_sem, NULL); + return ret; + } + return ret; +} + VkResult vk_create_sampler(struct vk_bundle *vk, VkSampler *out_sampler) { @@ -497,7 +527,6 @@ VkResult vk_submit_cmd_buffer(struct vk_bundle *vk, VkCommandBuffer cmd_buffer) { VkResult ret = VK_SUCCESS; - VkQueue queue; VkFence fence; VkFenceCreateInfo fence_info = { .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, @@ -515,9 +544,6 @@ vk_submit_cmd_buffer(struct vk_bundle *vk, VkCommandBuffer cmd_buffer) goto out; } - // Get the queue. - vk->vkGetDeviceQueue(vk->device, vk->queue_family_index, 0, &queue); - // Create the fence. ret = vk->vkCreateFence(vk->device, &fence_info, NULL, &fence); if (ret != VK_SUCCESS) { @@ -526,9 +552,9 @@ vk_submit_cmd_buffer(struct vk_bundle *vk, VkCommandBuffer cmd_buffer) } // Do the actual submitting. - ret = vk->vkQueueSubmit(queue, 1, &submitInfo, fence); + ret = vk->vkQueueSubmit(vk->queue, 1, &submitInfo, fence); if (ret != VK_SUCCESS) { - VK_ERROR(vk, "Error: Could not submit queue.\n"); + VK_ERROR(vk, "Error: Could not submit to queue.\n"); goto out_fence; } @@ -711,6 +737,8 @@ vk_get_device_functions(struct vk_bundle *vk) vk->vkGetSwapchainImagesKHR = GET_DEV_PROC(vk, vkGetSwapchainImagesKHR); vk->vkAcquireNextImageKHR = GET_DEV_PROC(vk, vkAcquireNextImageKHR); vk->vkQueuePresentKHR = GET_DEV_PROC(vk, vkQueuePresentKHR); + vk->vkImportSemaphoreFdKHR = GET_DEV_PROC(vk, vkImportSemaphoreFdKHR); + vk->vkGetSemaphoreFdKHR = GET_DEV_PROC(vk, vkGetSemaphoreFdKHR); // clang-format on return VK_SUCCESS; @@ -915,6 +943,7 @@ vk_create_device(struct vk_bundle *vk, int forced_index) if (ret != VK_SUCCESS) { goto err_destroy; } + vk->vkGetDeviceQueue(vk->device, vk->queue_family_index, 0, &vk->queue); return ret; @@ -966,6 +995,8 @@ vk_init_from_given(struct vk_bundle *vk, goto err_memset; } + vk->vkGetDeviceQueue(vk->device, vk->queue_family_index, 0, &vk->queue); + // Create the pool. ret = vk_init_cmd_pool(vk); if (ret != VK_SUCCESS) { @@ -1005,6 +1036,37 @@ vk_get_access_flags(VkImageLayout layout) return 0; } +VkAccessFlags +vk_swapchain_access_flags(enum xrt_swapchain_usage_bits bits) +{ + VkAccessFlags result = 0; + if ((bits & XRT_SWAPCHAIN_USAGE_UNORDERED_ACCESS) != 0) { + result |= VK_ACCESS_INPUT_ATTACHMENT_READ_BIT; + if ((bits & XRT_SWAPCHAIN_USAGE_COLOR) != 0) { + result |= VK_ACCESS_COLOR_ATTACHMENT_READ_BIT; + } + if ((bits & XRT_SWAPCHAIN_USAGE_DEPTH_STENCIL) != 0) { + result |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT; + } + } + if ((bits & XRT_SWAPCHAIN_USAGE_COLOR) != 0) { + result |= VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + } + if ((bits & XRT_SWAPCHAIN_USAGE_DEPTH_STENCIL) != 0) { + result |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + } + if ((bits & XRT_SWAPCHAIN_USAGE_TRANSFER_SRC) != 0) { + result |= VK_ACCESS_TRANSFER_READ_BIT; + } + if ((bits & XRT_SWAPCHAIN_USAGE_TRANSFER_DST) != 0) { + result |= VK_ACCESS_TRANSFER_WRITE_BIT; + } + if ((bits & XRT_SWAPCHAIN_USAGE_SAMPLED) != 0) { + result |= VK_ACCESS_SHADER_READ_BIT; + } + return result; +} + bool vk_init_descriptor_pool(struct vk_bundle *vk, const VkDescriptorPoolSize *pool_sizes, diff --git a/src/xrt/auxiliary/vk/vk_helpers.h b/src/xrt/auxiliary/vk/vk_helpers.h index 324e1fc13ac2919264cc12ba71bd4b4ec85274ed..29954d8ba434a40604b4f6736dcb3ff4320981b6 100644 --- a/src/xrt/auxiliary/vk/vk_helpers.h +++ b/src/xrt/auxiliary/vk/vk_helpers.h @@ -40,6 +40,7 @@ struct vk_bundle VkDevice device; uint32_t queue_family_index; uint32_t queue_index; + VkQueue queue; VkDebugReportCallbackEXT debug_report_cb; @@ -177,6 +178,9 @@ struct vk_bundle PFN_vkGetSwapchainImagesKHR vkGetSwapchainImagesKHR; PFN_vkAcquireNextImageKHR vkAcquireNextImageKHR; PFN_vkQueuePresentKHR vkQueuePresentKHR; + + PFN_vkImportSemaphoreFdKHR vkImportSemaphoreFdKHR; + PFN_vkGetSemaphoreFdKHR vkGetSemaphoreFdKHR; // clang-format on }; @@ -350,6 +354,12 @@ vk_create_image_from_fd(struct vk_bundle *vk, VkImage *out_image, VkDeviceMemory *out_mem); +/*! + * @ingroup aux_vk + */ +VkResult +vk_create_semaphore_from_fd(struct vk_bundle *vk, int fd, VkSemaphore *out_sem); + /*! * @ingroup aux_vk */ @@ -406,6 +416,9 @@ vk_submit_cmd_buffer(struct vk_bundle *vk, VkCommandBuffer cmd_buffer); VkAccessFlags vk_get_access_flags(VkImageLayout layout); +VkAccessFlags +vk_swapchain_access_flags(enum xrt_swapchain_usage_bits bits); + bool vk_init_descriptor_pool(struct vk_bundle *vk, const VkDescriptorPoolSize *pool_sizes, diff --git a/src/xrt/compositor/client/comp_gl_client.c b/src/xrt/compositor/client/comp_gl_client.c index 118d4bf08b7f7d4d89bfcee5b1f3f952627316c0..a231adafcc54686da788f845a55243ac74fb66b0 100644 --- a/src/xrt/compositor/client/comp_gl_client.c +++ b/src/xrt/compositor/client/comp_gl_client.c @@ -56,12 +56,13 @@ client_gl_swapchain_destroy(struct xrt_swapchain *xsc) } static xrt_result_t -client_gl_swapchain_acquire_image(struct xrt_swapchain *xsc, uint32_t *index) +client_gl_swapchain_acquire_image(struct xrt_swapchain *xsc, + uint32_t *out_index) { struct client_gl_swapchain *sc = client_gl_swapchain(xsc); // Pipe down call into fd swapchain. - return xrt_swapchain_acquire_image(&sc->xscfd->base, index); + return xrt_swapchain_acquire_image(&sc->xscfd->base, out_index); } static xrt_result_t @@ -154,61 +155,40 @@ client_gl_compositor_layer_begin(struct xrt_compositor *xc, } static xrt_result_t -client_gl_compositor_layer_stereo_projection( - struct xrt_compositor *xc, - uint64_t timestamp, - struct xrt_device *xdev, - enum xrt_input_name name, - enum xrt_layer_composition_flags layer_flags, - struct xrt_swapchain *l_sc, - uint32_t l_image_index, - struct xrt_rect *l_rect, - uint32_t l_array_index, - struct xrt_fov *l_fov, - struct xrt_pose *l_pose, - struct xrt_swapchain *r_sc, - uint32_t r_image_index, - struct xrt_rect *r_rect, - uint32_t r_array_index, - struct xrt_fov *r_fov, - struct xrt_pose *r_pose, - bool flip_y) +client_gl_compositor_layer_stereo_projection(struct xrt_compositor *xc, + struct xrt_device *xdev, + struct xrt_swapchain *l_xsc, + struct xrt_swapchain *r_xsc, + struct xrt_layer_data *data) { struct client_gl_compositor *c = client_gl_compositor(xc); struct xrt_swapchain *l_xscfd, *r_xscfd; - l_xscfd = &client_gl_swapchain(l_sc)->xscfd->base; - r_xscfd = &client_gl_swapchain(r_sc)->xscfd->base; + assert(data->type == XRT_LAYER_STEREO_PROJECTION); - return xrt_comp_layer_stereo_projection( - &c->xcfd->base, timestamp, xdev, name, layer_flags, l_xscfd, - l_image_index, l_rect, l_array_index, l_fov, l_pose, r_xscfd, - r_image_index, r_rect, r_array_index, r_fov, r_pose, true); + l_xscfd = &client_gl_swapchain(l_xsc)->xscfd->base; + r_xscfd = &client_gl_swapchain(r_xsc)->xscfd->base; + data->flip_y = true; + + return xrt_comp_layer_stereo_projection(&c->xcfd->base, xdev, l_xscfd, + r_xscfd, data); } static xrt_result_t client_gl_compositor_layer_quad(struct xrt_compositor *xc, - uint64_t timestamp, struct xrt_device *xdev, - enum xrt_input_name name, - enum xrt_layer_composition_flags layer_flags, - enum xrt_layer_eye_visibility visibility, - struct xrt_swapchain *sc, - uint32_t image_index, - struct xrt_rect *rect, - uint32_t array_index, - struct xrt_pose *pose, - struct xrt_vec2 *size, - bool flip_y) + struct xrt_swapchain *xsc, + struct xrt_layer_data *data) { struct client_gl_compositor *c = client_gl_compositor(xc); struct xrt_swapchain *xscfb; - xscfb = &client_gl_swapchain(sc)->xscfd->base; + assert(data->type == XRT_LAYER_QUAD); + + xscfb = &client_gl_swapchain(xsc)->xscfd->base; + data->flip_y = true; - return xrt_comp_layer_quad(&c->xcfd->base, timestamp, xdev, name, - layer_flags, visibility, xscfb, image_index, - rect, array_index, pose, size, true); + return xrt_comp_layer_quad(&c->xcfd->base, xdev, xscfb, data); } static xrt_result_t diff --git a/src/xrt/compositor/client/comp_vk_client.c b/src/xrt/compositor/client/comp_vk_client.c index aa00ea5d2423b906164d3cdece07cc3db28e4134..59b0939a520348e2cf9a525bac8713b79d246d4a 100644 --- a/src/xrt/compositor/client/comp_vk_client.c +++ b/src/xrt/compositor/client/comp_vk_client.c @@ -8,13 +8,14 @@ * @ingroup comp_client */ -#include <stdio.h> -#include <stdlib.h> - #include "util/u_misc.h" #include "comp_vk_client.h" +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> + /*! * Down-cast helper. * @@ -70,12 +71,32 @@ client_vk_swapchain_destroy(struct xrt_swapchain *xsc) } static xrt_result_t -client_vk_swapchain_acquire_image(struct xrt_swapchain *xsc, uint32_t *index) +client_vk_swapchain_acquire_image(struct xrt_swapchain *xsc, + uint32_t *out_index) { struct client_vk_swapchain *sc = client_vk_swapchain(xsc); + struct vk_bundle *vk = &sc->c->vk; // Pipe down call into fd swapchain. - return xrt_swapchain_acquire_image(&sc->xscfd->base, index); + xrt_result_t xret = + xrt_swapchain_acquire_image(&sc->xscfd->base, out_index); + if (xret != XRT_SUCCESS) { + return xret; + } + + // Acquire ownership and complete layout transition + VkSubmitInfo submitInfo = { + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, + .commandBufferCount = 1, + .pCommandBuffers = &sc->base.acquire[*out_index], + }; + VkResult ret = + vk->vkQueueSubmit(vk->queue, 1, &submitInfo, VK_NULL_HANDLE); + if (ret != VK_SUCCESS) { + VK_ERROR(vk, "Error: Could not submit to queue.\n"); + return XRT_ERROR_FAILED_TO_SUBMIT_VULKAN_COMMANDS; + } + return XRT_SUCCESS; } static xrt_result_t @@ -93,6 +114,20 @@ static xrt_result_t client_vk_swapchain_release_image(struct xrt_swapchain *xsc, uint32_t index) { struct client_vk_swapchain *sc = client_vk_swapchain(xsc); + struct vk_bundle *vk = &sc->c->vk; + + // Release ownership and begin layout transition + VkSubmitInfo submitInfo = { + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, + .commandBufferCount = 1, + .pCommandBuffers = &sc->base.release[index], + }; + VkResult ret = + vk->vkQueueSubmit(vk->queue, 1, &submitInfo, VK_NULL_HANDLE); + if (ret != VK_SUCCESS) { + VK_ERROR(vk, "Error: Could not submit to queue.\n"); + return XRT_ERROR_FAILED_TO_SUBMIT_VULKAN_COMMANDS; + } // Pipe down call into fd swapchain. return xrt_swapchain_release_image(&sc->xscfd->base, index); @@ -118,6 +153,10 @@ client_vk_compositor_destroy(struct xrt_compositor *xc) struct client_vk_compositor *c = client_vk_compositor(xc); if (c->vk.cmd_pool != VK_NULL_HANDLE) { + // Make sure that any of the command buffers from this command + // pool are n used here, this pleases the validation layer. + c->vk.vkDeviceWaitIdle(c->vk.device); + c->vk.vkDestroyCommandPool(c->vk.device, c->vk.cmd_pool, NULL); c->vk.cmd_pool = VK_NULL_HANDLE; } @@ -190,61 +229,40 @@ client_vk_compositor_layer_begin(struct xrt_compositor *xc, } static xrt_result_t -client_vk_compositor_layer_stereo_projection( - struct xrt_compositor *xc, - uint64_t timestamp, - struct xrt_device *xdev, - enum xrt_input_name name, - enum xrt_layer_composition_flags layer_flags, - struct xrt_swapchain *l_sc, - uint32_t l_image_index, - struct xrt_rect *l_rect, - uint32_t l_array_index, - struct xrt_fov *l_fov, - struct xrt_pose *l_pose, - struct xrt_swapchain *r_sc, - uint32_t r_image_index, - struct xrt_rect *r_rect, - uint32_t r_array_index, - struct xrt_fov *r_fov, - struct xrt_pose *r_pose, - bool flip_y) +client_vk_compositor_layer_stereo_projection(struct xrt_compositor *xc, + struct xrt_device *xdev, + struct xrt_swapchain *l_xsc, + struct xrt_swapchain *r_xsc, + struct xrt_layer_data *data) { struct client_vk_compositor *c = client_vk_compositor(xc); struct xrt_swapchain *l_xscfd, *r_xscfd; - l_xscfd = &client_vk_swapchain(l_sc)->xscfd->base; - r_xscfd = &client_vk_swapchain(r_sc)->xscfd->base; + assert(data->type == XRT_LAYER_STEREO_PROJECTION); + + l_xscfd = &client_vk_swapchain(l_xsc)->xscfd->base; + r_xscfd = &client_vk_swapchain(r_xsc)->xscfd->base; + data->flip_y = false; - return xrt_comp_layer_stereo_projection( - &c->xcfd->base, timestamp, xdev, name, layer_flags, l_xscfd, - l_image_index, l_rect, l_array_index, l_fov, l_pose, r_xscfd, - r_image_index, r_rect, r_array_index, r_fov, r_pose, false); + return xrt_comp_layer_stereo_projection(&c->xcfd->base, xdev, l_xscfd, + r_xscfd, data); } static xrt_result_t client_vk_compositor_layer_quad(struct xrt_compositor *xc, - uint64_t timestamp, struct xrt_device *xdev, - enum xrt_input_name name, - enum xrt_layer_composition_flags layer_flags, - enum xrt_layer_eye_visibility visibility, - struct xrt_swapchain *sc, - uint32_t image_index, - struct xrt_rect *rect, - uint32_t array_index, - struct xrt_pose *pose, - struct xrt_vec2 *size, - bool flip_y) + struct xrt_swapchain *xsc, + struct xrt_layer_data *data) { struct client_vk_compositor *c = client_vk_compositor(xc); struct xrt_swapchain *xscfb; - xscfb = &client_vk_swapchain(sc)->xscfd->base; + assert(data->type == XRT_LAYER_QUAD); - return xrt_comp_layer_quad(&c->xcfd->base, timestamp, xdev, name, - layer_flags, visibility, xscfb, image_index, - rect, array_index, pose, size, false); + xscfb = &client_vk_swapchain(xsc)->xscfd->base; + data->flip_y = false; + + return xrt_comp_layer_quad(&c->xcfd->base, xdev, xscfb, data); } static xrt_result_t @@ -288,9 +306,9 @@ client_vk_swapchain_create(struct xrt_compositor *xc, VkImageSubresourceRange subresource_range = { .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .baseMipLevel = 0, - .levelCount = 1, + .levelCount = VK_REMAINING_MIP_LEVELS, .baseArrayLayer = 0, - .layerCount = array_size, + .layerCount = VK_REMAINING_ARRAY_LAYERS, }; struct client_vk_swapchain *sc = @@ -316,10 +334,15 @@ client_vk_swapchain_create(struct xrt_compositor *xc, return NULL; } + /* + * This is only to please the validation layer, that may or may + * not be a bug in the validation layer. That may or may not be + * fixed in the future version of the validation layer. + */ vk_set_image_layout(&c->vk, cmd_buffer, sc->base.images[i], 0, VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_IMAGE_LAYOUT_UNDEFINED, - VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, subresource_range); } @@ -328,6 +351,90 @@ client_vk_swapchain_create(struct xrt_compositor *xc, return NULL; } + // Prerecord command buffers for swapchain image ownership/layout + // transitions + for (uint32_t i = 0; i < xsc->num_images; i++) { + ret = vk_init_cmd_buffer(&c->vk, &sc->base.acquire[i]); + if (ret != VK_SUCCESS) { + return NULL; + } + ret = vk_init_cmd_buffer(&c->vk, &sc->base.release[i]); + if (ret != VK_SUCCESS) { + return NULL; + } + + VkImageSubresourceRange subresource_range = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = VK_REMAINING_MIP_LEVELS, + .baseArrayLayer = 0, + .layerCount = VK_REMAINING_ARRAY_LAYERS, + }; + + /* + * The biggest reason is that VK_IMAGE_LAYOUT_PRESENT_SRC_KHR is + * used here is that this is what hello_xr used to barrier to, + * and it worked on a wide verity of drivers. So it's safe. + * + * There might not be a Vulkan renderer on the other endm + * there could be a OpenGL compositor, heck there could be a X + * server even. On Linux VK_IMAGE_LAYOUT_PRESENT_SRC_KHR is what + * you use if you want to "flush" out all of the pixels to the + * memory buffer that has been shared to you from a X11 server. + * + * This is not what the spec says you should do when it comes to + * external images thou. Instead we should use the queue family + * index `VK_QUEUE_FAMILY_EXTERNAL`. And use semaphores to + * synchronize. + */ + VkImageMemoryBarrier acquire = { + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .srcAccessMask = 0, + .dstAccessMask = vk_swapchain_access_flags(bits), + .oldLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, + .newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + .srcQueueFamilyIndex = c->vk.queue_family_index, + .dstQueueFamilyIndex = c->vk.queue_family_index, + .image = sc->base.images[i], + .subresourceRange = subresource_range, + }; + + VkImageMemoryBarrier release = { + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .srcAccessMask = vk_swapchain_access_flags(bits), + .dstAccessMask = 0, + .oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + .newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, + .srcQueueFamilyIndex = c->vk.queue_family_index, + .dstQueueFamilyIndex = c->vk.queue_family_index, + .image = sc->base.images[i], + .subresourceRange = subresource_range, + }; + + //! @todo less conservative pipeline stage masks based on usage + c->vk.vkCmdPipelineBarrier(sc->base.acquire[i], + VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, + VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + 0, 0, NULL, 0, NULL, 1, &acquire); + c->vk.vkCmdPipelineBarrier(sc->base.release[i], + VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, + 0, 0, NULL, 0, NULL, 1, &release); + + ret = c->vk.vkEndCommandBuffer(sc->base.acquire[i]); + if (ret != VK_SUCCESS) { + VK_ERROR(vk, "vkEndCommandBuffer: %s", + vk_result_string(ret)); + return NULL; + } + ret = c->vk.vkEndCommandBuffer(sc->base.release[i]); + if (ret != VK_SUCCESS) { + VK_ERROR(vk, "vkEndCommandBuffer: %s", + vk_result_string(ret)); + return NULL; + } + } + return &sc->base.base; } diff --git a/src/xrt/compositor/main/comp_compositor.c b/src/xrt/compositor/main/comp_compositor.c index 6ca0a045dbb394deccfe55fcdf97e16ec111790a..5ea1ce993dfd04bf2413f08cd59ff409857da7ba 100644 --- a/src/xrt/compositor/main/comp_compositor.c +++ b/src/xrt/compositor/main/comp_compositor.c @@ -294,23 +294,10 @@ compositor_layer_begin(struct xrt_compositor *xc, static xrt_result_t compositor_layer_stereo_projection(struct xrt_compositor *xc, - uint64_t timestamp, struct xrt_device *xdev, - enum xrt_input_name name, - enum xrt_layer_composition_flags layer_flags, - struct xrt_swapchain *l_sc, - uint32_t l_image_index, - struct xrt_rect *l_rect, - uint32_t l_array_index, - struct xrt_fov *l_fov, - struct xrt_pose *l_pose, - struct xrt_swapchain *r_sc, - uint32_t r_image_index, - struct xrt_rect *r_rect, - uint32_t r_array_index, - struct xrt_fov *r_fov, - struct xrt_pose *r_pose, - bool flip_y) + struct xrt_swapchain *l_xsc, + struct xrt_swapchain *r_xsc, + struct xrt_layer_data *data) { struct comp_compositor *c = comp_compositor(xc); @@ -319,16 +306,9 @@ compositor_layer_stereo_projection(struct xrt_compositor *xc, uint32_t layer_id = c->slots[slot_id].num_layers; struct comp_layer *layer = &c->slots[slot_id].layers[layer_id]; - layer->stereo.l.sc = comp_swapchain(l_sc); - layer->stereo.l.image_index = l_image_index; - layer->stereo.l.array_index = l_array_index; - layer->stereo.r.sc = comp_swapchain(r_sc); - layer->stereo.r.image_index = r_image_index; - layer->stereo.r.array_index = r_array_index; - - layer->flags = layer_flags; - layer->flip_y = flip_y; - layer->type = COMP_LAYER_STEREO_PROJECTION; + layer->scs[0] = comp_swapchain(l_xsc); + layer->scs[1] = comp_swapchain(r_xsc); + layer->data = *data; c->slots[slot_id].num_layers++; return XRT_SUCCESS; @@ -336,18 +316,9 @@ compositor_layer_stereo_projection(struct xrt_compositor *xc, static xrt_result_t compositor_layer_quad(struct xrt_compositor *xc, - uint64_t timestamp, struct xrt_device *xdev, - enum xrt_input_name name, - enum xrt_layer_composition_flags layer_flags, - enum xrt_layer_eye_visibility visibility, - struct xrt_swapchain *sc, - uint32_t image_index, - struct xrt_rect *rect, - uint32_t array_index, - struct xrt_pose *pose, - struct xrt_vec2 *size, - bool flip_y) + struct xrt_swapchain *xsc, + struct xrt_layer_data *data) { struct comp_compositor *c = comp_compositor(xc); @@ -356,17 +327,9 @@ compositor_layer_quad(struct xrt_compositor *xc, uint32_t layer_id = c->slots[slot_id].num_layers; struct comp_layer *layer = &c->slots[slot_id].layers[layer_id]; - layer->quad.sc = comp_swapchain(sc); - layer->quad.visibility = visibility; - layer->quad.image_index = image_index; - layer->quad.rect = *rect; - layer->quad.array_index = array_index; - layer->quad.pose = *pose; - layer->quad.size = *size; - - layer->flags = layer_flags; - layer->flip_y = flip_y; - layer->type = COMP_LAYER_QUAD; + layer->scs[0] = comp_swapchain(xsc); + layer->scs[1] = NULL; + layer->data = *data; c->slots[slot_id].num_layers++; return XRT_SUCCESS; @@ -388,25 +351,26 @@ compositor_layer_commit(struct xrt_compositor *xc) for (uint32_t i = 0; i < num_layers; i++) { struct comp_layer *layer = &c->slots[slot_id].layers[i]; - switch (layer->type) { - case COMP_LAYER_QUAD: { - struct comp_layer_quad *quad = &layer->quad; + struct xrt_layer_data *data = &layer->data; + switch (data->type) { + case XRT_LAYER_QUAD: { + struct xrt_layer_quad_data *quad = &layer->data.quad; struct comp_swapchain_image *image; - image = &quad->sc->images[quad->image_index]; - comp_renderer_set_quad_layer(c->r, image, &quad->pose, - &quad->size, layer->flip_y, - i, quad->array_index); + image = &layer->scs[0]->images[quad->sub.image_index]; + comp_renderer_set_quad_layer(c->r, i, image, data); } break; - case COMP_LAYER_STEREO_PROJECTION: { - struct comp_layer_stereo *stereo = &layer->stereo; + case XRT_LAYER_STEREO_PROJECTION: { + struct xrt_layer_stereo_projection_data *stereo = + &data->stereo; struct comp_swapchain_image *right; struct comp_swapchain_image *left; - left = &stereo->l.sc->images[stereo->l.image_index]; - right = &stereo->l.sc->images[stereo->r.image_index]; + left = + &layer->scs[0]->images[stereo->l.sub.image_index]; + right = + &layer->scs[1]->images[stereo->r.sub.image_index]; - comp_renderer_set_projection_layer( - c->r, left, right, layer->flip_y, i, - stereo->l.array_index, stereo->r.array_index); + comp_renderer_set_projection_layer(c->r, i, left, right, + data); } break; } } diff --git a/src/xrt/compositor/main/comp_compositor.h b/src/xrt/compositor/main/comp_compositor.h index e9142241a9f8b738584ed860420bdf96f2cde4b6..90c5c7e55356750d206fb1188dcfec4d40ab0743 100644 --- a/src/xrt/compositor/main/comp_compositor.h +++ b/src/xrt/compositor/main/comp_compositor.h @@ -77,49 +77,6 @@ struct comp_swapchain */ struct u_index_fifo fifo; }; -/*! - * Tag for distinguishing the union contents of @ref comp_layer. - */ -enum comp_layer_type -{ - //! comp_layer::stereo is initialized - COMP_LAYER_STEREO_PROJECTION, - //! comp_layer::quad is initialized - COMP_LAYER_QUAD, -}; - -/*! - * A quad layer. - * - * @ingroup comp_main - * @see comp_layer - */ -struct comp_layer_quad -{ - struct comp_swapchain *sc; - enum xrt_layer_eye_visibility visibility; - uint32_t image_index; - struct xrt_rect rect; - uint32_t array_index; - struct xrt_pose pose; - struct xrt_vec2 size; -}; - -/*! - * A stereo projection layer. - * - * @ingroup comp_main - * @see comp_layer - */ -struct comp_layer_stereo -{ - struct - { - struct comp_swapchain *sc; - uint32_t image_index; - uint32_t array_index; - } l, r; -}; /*! * A single layer. @@ -129,14 +86,17 @@ struct comp_layer_stereo */ struct comp_layer { - int64_t timestamp; - enum xrt_layer_composition_flags flags; - enum comp_layer_type type; - bool flip_y; - union { - struct comp_layer_quad quad; - struct comp_layer_stereo stereo; - }; + /*! + * Up to two compositor swapchains referenced per layer. + * + * Unused elements should be set to null. + */ + struct comp_swapchain *scs[2]; + + /*! + * All basic (trivially-serializable) data associated with a layer. + */ + struct xrt_layer_data data; }; /*! diff --git a/src/xrt/compositor/main/comp_layer.c b/src/xrt/compositor/main/comp_layer.c index 992e1072cecb166b5bb093bdc30c0404f7f7d01f..d444f6f33bdf885fe2a11ed79ddd863913e6861d 100644 --- a/src/xrt/compositor/main/comp_layer.c +++ b/src/xrt/compositor/main/comp_layer.c @@ -154,7 +154,7 @@ comp_layer_update_stereo_descriptors(struct comp_render_layer *self, static bool _init(struct comp_render_layer *self, struct vk_bundle *vk, - enum comp_layer_type type, + enum xrt_layer_type type, VkDescriptorSetLayout *layout) { self->vk = vk; @@ -162,6 +162,7 @@ _init(struct comp_render_layer *self, self->type = type; self->visible = true; + self->view_space = true; math_matrix_4x4_identity(&self->model_matrix); @@ -202,7 +203,8 @@ comp_layer_draw(struct comp_render_layer *self, VkPipelineLayout pipeline_layout, VkCommandBuffer cmd_buffer, const struct vk_buffer *vertex_buffer, - const struct xrt_matrix_4x4 *vp) + const struct xrt_matrix_4x4 *vp_world, + const struct xrt_matrix_4x4 *vp_eye) { if (!self->visible) return; @@ -210,11 +212,14 @@ comp_layer_draw(struct comp_render_layer *self, self->vk->vkCmdBindPipeline(cmd_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); + // Is this layer viewspace or not. + const struct xrt_matrix_4x4 *vp = self->view_space ? vp_eye : vp_world; + switch (self->type) { - case COMP_LAYER_STEREO_PROJECTION: + case XRT_LAYER_STEREO_PROJECTION: _update_mvp_matrix(self, eye, &proj_scale); break; - case COMP_LAYER_QUAD: _update_mvp_matrix(self, eye, vp); break; + case XRT_LAYER_QUAD: _update_mvp_matrix(self, eye, vp); break; } self->vk->vkCmdBindDescriptorSets( @@ -230,7 +235,7 @@ comp_layer_draw(struct comp_render_layer *self, struct comp_render_layer * comp_layer_create(struct vk_bundle *vk, - enum comp_layer_type type, + enum xrt_layer_type type, VkDescriptorSetLayout *layout) { struct comp_render_layer *q = U_TYPED_CALLOC(struct comp_render_layer); diff --git a/src/xrt/compositor/main/comp_layer.h b/src/xrt/compositor/main/comp_layer.h index 35135fa5f03842e3715e55c607ef216a6ae5efb2..c2a60ac1e8d44661b3fbd0b6d179890462542558 100644 --- a/src/xrt/compositor/main/comp_layer.h +++ b/src/xrt/compositor/main/comp_layer.h @@ -23,8 +23,9 @@ struct comp_render_layer struct vk_bundle *vk; bool visible; + bool view_space; - enum comp_layer_type type; + enum xrt_layer_type type; struct layer_transformation transformation[2]; struct vk_buffer transformation_ubos[2]; @@ -37,7 +38,7 @@ struct comp_render_layer struct comp_render_layer * comp_layer_create(struct vk_bundle *vk, - enum comp_layer_type type, + enum xrt_layer_type type, VkDescriptorSetLayout *layout); void @@ -47,7 +48,8 @@ comp_layer_draw(struct comp_render_layer *self, VkPipelineLayout pipeline_layout, VkCommandBuffer cmd_buffer, const struct vk_buffer *vertex_buffer, - const struct xrt_matrix_4x4 *vp); + const struct xrt_matrix_4x4 *vp_world, + const struct xrt_matrix_4x4 *vp_eye); void comp_layer_set_model_matrix(struct comp_render_layer *self, diff --git a/src/xrt/compositor/main/comp_layer_renderer.c b/src/xrt/compositor/main/comp_layer_renderer.c index 5abf110dfdf529a266bef166f2cb01a51c5e10c6..705afe019257b85c9b66c95dd22bc63e6f37907d 100644 --- a/src/xrt/compositor/main/comp_layer_renderer.c +++ b/src/xrt/compositor/main/comp_layer_renderer.c @@ -381,14 +381,18 @@ _render_eye(struct comp_layer_renderer *self, VkCommandBuffer cmd_buffer, VkPipelineLayout pipeline_layout) { - struct xrt_matrix_4x4 vp; + struct xrt_matrix_4x4 vp_world; + struct xrt_matrix_4x4 vp_eye; math_matrix_4x4_multiply(&self->mat_projection[eye], - &self->mat_view[eye], &vp); + &self->mat_world_view[eye], &vp_world); + math_matrix_4x4_multiply(&self->mat_projection[eye], + &self->mat_eye_view[eye], &vp_eye); - for (uint32_t i = 0; i < self->num_layers; i++) + for (uint32_t i = 0; i < self->num_layers; i++) { comp_layer_draw(self->layers[i], eye, self->pipeline, pipeline_layout, cmd_buffer, - &self->vertex_buffer, &vp); + &self->vertex_buffer, &vp_world, &vp_eye); + } } static bool @@ -451,7 +455,7 @@ comp_layer_renderer_allocate_layers(struct comp_layer_renderer *self, for (uint32_t i = 0; i < self->num_layers; i++) { self->layers[i] = comp_layer_create( - vk, COMP_LAYER_QUAD, &self->descriptor_set_layout); + vk, XRT_LAYER_QUAD, &self->descriptor_set_layout); } } @@ -484,7 +488,8 @@ _init(struct comp_layer_renderer *self, for (uint32_t i = 0; i < 2; i++) { math_matrix_4x4_identity(&self->mat_projection[i]); - math_matrix_4x4_identity(&self->mat_view[i]); + math_matrix_4x4_identity(&self->mat_world_view[i]); + math_matrix_4x4_identity(&self->mat_eye_view[i]); } if (!_init_render_pass(vk, format, @@ -649,7 +654,7 @@ comp_layer_renderer_destroy(struct comp_layer_renderer *self) void comp_layer_renderer_set_fov(struct comp_layer_renderer *self, const struct xrt_fov *fov, - uint32_t view_id) + uint32_t eye) { const float tan_left = tanf(fov->angle_left); const float tan_right = tanf(fov->angle_right); @@ -670,7 +675,7 @@ comp_layer_renderer_set_fov(struct comp_layer_renderer *self, const float a43 = -(self->far * self->near) / (self->far - self->near); // clang-format off - self->mat_projection[view_id] = (struct xrt_matrix_4x4) { + self->mat_projection[eye] = (struct xrt_matrix_4x4) { .v = { a11, 0, 0, 0, 0, a22, 0, 0, @@ -683,8 +688,10 @@ comp_layer_renderer_set_fov(struct comp_layer_renderer *self, void comp_layer_renderer_set_pose(struct comp_layer_renderer *self, - const struct xrt_pose *pose, - uint32_t view_id) + const struct xrt_pose *eye_pose, + const struct xrt_pose *world_pose, + uint32_t eye) { - math_matrix_4x4_view_from_pose(pose, &self->mat_view[view_id]); + math_matrix_4x4_view_from_pose(eye_pose, &self->mat_eye_view[eye]); + math_matrix_4x4_view_from_pose(world_pose, &self->mat_world_view[eye]); } diff --git a/src/xrt/compositor/main/comp_layer_renderer.h b/src/xrt/compositor/main/comp_layer_renderer.h index 33b5daeb22ab49feaffada3b032e96a53aa670ee..42de72c5b2b5fa5081fe8d7dffcbf9776d10805b 100644 --- a/src/xrt/compositor/main/comp_layer_renderer.h +++ b/src/xrt/compositor/main/comp_layer_renderer.h @@ -42,7 +42,8 @@ struct comp_layer_renderer VkPipelineLayout pipeline_layout; VkPipelineCache pipeline_cache; - struct xrt_matrix_4x4 mat_view[2]; + struct xrt_matrix_4x4 mat_world_view[2]; + struct xrt_matrix_4x4 mat_eye_view[2]; struct xrt_matrix_4x4 mat_projection[2]; struct vk_buffer vertex_buffer; @@ -68,12 +69,13 @@ comp_layer_renderer_draw(struct comp_layer_renderer *self); void comp_layer_renderer_set_fov(struct comp_layer_renderer *self, const struct xrt_fov *fov, - uint32_t view_id); + uint32_t eye); void comp_layer_renderer_set_pose(struct comp_layer_renderer *self, - const struct xrt_pose *pose, - uint32_t view_id); + const struct xrt_pose *eye_pose, + const struct xrt_pose *world_pose, + uint32_t eye); void comp_layer_renderer_allocate_layers(struct comp_layer_renderer *self, diff --git a/src/xrt/compositor/main/comp_renderer.c b/src/xrt/compositor/main/comp_renderer.c index 3b980b2d97dea472689a2fd868f4b41354b035b9..ee114fbddbd3020f32a314d9b095b031626d999c 100644 --- a/src/xrt/compositor/main/comp_renderer.c +++ b/src/xrt/compositor/main/comp_renderer.c @@ -416,15 +416,15 @@ _get_view_projection(struct comp_renderer *r) comp_layer_renderer_set_fov(r->lr, &fov, i); - struct xrt_pose view_pose; + struct xrt_pose eye_pose; xrt_device_get_view_pose(r->c->xdev, &eye_relation, i, - &view_pose); + &eye_pose); - struct xrt_pose pose; - math_pose_openxr_locate(&view_pose, &relation.pose, - &base_space_pose, &pose); + struct xrt_pose world_pose; + math_pose_openxr_locate(&eye_pose, &relation.pose, + &base_space_pose, &world_pose); - comp_layer_renderer_set_pose(r->lr, &pose, i); + comp_layer_renderer_set_pose(r->lr, &eye_pose, &world_pose, i); } } @@ -474,44 +474,48 @@ renderer_init(struct comp_renderer *r) void comp_renderer_set_quad_layer(struct comp_renderer *r, - struct comp_swapchain_image *image, - struct xrt_pose *pose, - struct xrt_vec2 *size, - bool flip_y, uint32_t layer, - uint32_t array_index) + struct comp_swapchain_image *image, + struct xrt_layer_data *data) { comp_layer_update_descriptors(r->lr->layers[layer], image->sampler, - image->views[array_index]); + image->views[data->quad.sub.array_index]); struct xrt_matrix_4x4 model_matrix; - math_matrix_4x4_quad_model(pose, size, &model_matrix); + math_matrix_4x4_quad_model(&data->quad.pose, &data->quad.size, + &model_matrix); comp_layer_set_model_matrix(r->lr->layers[layer], &model_matrix); - r->lr->layers[layer]->type = COMP_LAYER_QUAD; - comp_layer_set_flip_y(r->lr->layers[layer], flip_y); + comp_layer_set_flip_y(r->lr->layers[layer], data->flip_y); + + r->lr->layers[layer]->type = XRT_LAYER_QUAD; + r->lr->layers[layer]->view_space = + (data->flags & XRT_LAYER_COMPOSITION_VIEW_SPACE_BIT) != 0; r->c->vk.vkDeviceWaitIdle(r->c->vk.device); } void comp_renderer_set_projection_layer(struct comp_renderer *r, + uint32_t layer, struct comp_swapchain_image *left_image, struct comp_swapchain_image *right_image, - bool flip_y, - uint32_t layer, - uint32_t left_array_index, - uint32_t right_array_index) + struct xrt_layer_data *data) { + uint32_t left_array_index = data->stereo.l.sub.array_index; + uint32_t right_array_index = data->stereo.r.sub.array_index; + comp_layer_update_stereo_descriptors( r->lr->layers[layer], left_image->sampler, right_image->sampler, left_image->views[left_array_index], right_image->views[right_array_index]); - comp_layer_set_flip_y(r->lr->layers[layer], flip_y); + comp_layer_set_flip_y(r->lr->layers[layer], data->flip_y); - r->lr->layers[layer]->type = COMP_LAYER_STEREO_PROJECTION; + r->lr->layers[layer]->type = XRT_LAYER_STEREO_PROJECTION; + r->lr->layers[layer]->view_space = + (data->flags & XRT_LAYER_COMPOSITION_VIEW_SPACE_BIT) != 0; } void diff --git a/src/xrt/compositor/main/comp_renderer.h b/src/xrt/compositor/main/comp_renderer.h index ec3d60748b177a26f6860537f421e74108340e99..89438edc3f1fc9d003b5b352e914fa72bc8c360e 100644 --- a/src/xrt/compositor/main/comp_renderer.h +++ b/src/xrt/compositor/main/comp_renderer.h @@ -56,22 +56,16 @@ comp_renderer_draw(struct comp_renderer *r); void comp_renderer_set_projection_layer(struct comp_renderer *r, + uint32_t layer, struct comp_swapchain_image *left_image, struct comp_swapchain_image *right_image, - bool flip_y, - uint32_t layer, - uint32_t left_array_index, - uint32_t right_array_index); + struct xrt_layer_data *data); void comp_renderer_set_quad_layer(struct comp_renderer *r, - struct comp_swapchain_image *image, - struct xrt_pose *pose, - struct xrt_vec2 *size, - bool flip_y, uint32_t layer, - uint32_t array_index); - + struct comp_swapchain_image *image, + struct xrt_layer_data *data); void comp_renderer_allocate_layers(struct comp_renderer *self, uint32_t num_layers); diff --git a/src/xrt/compositor/main/comp_swapchain.c b/src/xrt/compositor/main/comp_swapchain.c index ef7f5ca7e7ec3ea57523480a63d64bbd2dcd91cc..c15ceb6c1e91b78a4e3a7c68c7e2dc5eb2f69298 100644 --- a/src/xrt/compositor/main/comp_swapchain.c +++ b/src/xrt/compositor/main/comp_swapchain.c @@ -27,14 +27,14 @@ swapchain_destroy(struct xrt_swapchain *xsc) } static xrt_result_t -swapchain_acquire_image(struct xrt_swapchain *xsc, uint32_t *index) +swapchain_acquire_image(struct xrt_swapchain *xsc, uint32_t *out_index) { struct comp_swapchain *sc = comp_swapchain(xsc); COMP_SPEW(sc->c, "ACQUIRE_IMAGE"); // Returns negative on empty fifo. - int res = u_index_fifo_pop(&sc->fifo, index); + int res = u_index_fifo_pop(&sc->fifo, out_index); if (res >= 0) { return XRT_SUCCESS; } else { diff --git a/src/xrt/compositor/meson.build b/src/xrt/compositor/meson.build index 84254d2edfce30746e0f2251f47d34ad1887f592..22ed10ac66503cb1a86d1b84c31c236af856bf94 100644 --- a/src/xrt/compositor/meson.build +++ b/src/xrt/compositor/meson.build @@ -6,7 +6,7 @@ subdir('shaders') comp_include = include_directories('.') # TODO: Dependency resolution and subsequent configuration could be improved -compositor_deps = [aux, shaders, vulkan, xrt_config_vulkan] +compositor_deps = [aux, shaders, vulkan, xrt_config_vulkan, xcb_randr] compositor_includes = [xrt_include] compositor_srcs = [ diff --git a/src/xrt/include/xrt/xrt_compositor.h b/src/xrt/include/xrt/xrt_compositor.h index cb3b60558fbf8015f2237a5d9421032d95d8b304..de8347643dafe9a4ee5f860b61330d5bb6046ce9 100644 --- a/src/xrt/include/xrt/xrt_compositor.h +++ b/src/xrt/include/xrt/xrt_compositor.h @@ -69,6 +69,174 @@ enum xrt_view_type XRT_VIEW_TYPE_STEREO = 2, }; +/*! + * Layer type. + * + * @ingroup xrt_iface + */ +enum xrt_layer_type +{ + XRT_LAYER_STEREO_PROJECTION, + XRT_LAYER_QUAD, +}; + +/*! + * Bit field for holding information about how a layer should be composited. + * + * @ingroup xrt_iface + */ +enum xrt_layer_composition_flags +{ + XRT_LAYER_COMPOSITION_CORRECT_CHROMATIC_ABERRATION_BIT = 1 << 0, + XRT_LAYER_COMPOSITION_BLEND_TEXTURE_SOURCE_ALPHA_BIT = 1 << 1, + XRT_LAYER_COMPOSITION_UNPREMULTIPLIED_ALPHA_BIT = 1 << 2, + /*! + * The layer is locked to the device and the pose should only be + * adjusted for the IPD. + */ + XRT_LAYER_COMPOSITION_VIEW_SPACE_BIT = 1 << 3, +}; + +/*! + * Which view is the layer visible to? + * + * Used for quad layers. + * + * @note Doesn't have the same values as the OpenXR counterpart! + * + * @ingroup xrt_iface + */ +enum xrt_layer_eye_visibility +{ + XRT_LAYER_EYE_VISIBILITY_NONE = 0x0, + XRT_LAYER_EYE_VISIBILITY_LEFT_BIT = 0x1, + XRT_LAYER_EYE_VISIBILITY_RIGHT_BIT = 0x2, + XRT_LAYER_EYE_VISIBILITY_BOTH = 0x3, +}; + +/*! + * Specifies a sub-image in a layer. + * + * @ingroup xrt_iface + */ +struct xrt_sub_image +{ + //! Image index in the (implicit) swapchain + uint32_t image_index; + //! Index in image array (for array textures) + uint32_t array_index; + //! The rectangle in the image to use + struct xrt_rect rect; +}; + +/*! + * All the pure data values associated with a quad layer. + * + * The @ref xrt_swapchain references and @ref xrt_device are provided outside of + * this struct. + * + * @ingroup xrt_iface + */ +struct xrt_layer_quad_data +{ + enum xrt_layer_eye_visibility visibility; + + struct xrt_sub_image sub; + + struct xrt_pose pose; + struct xrt_vec2 size; +}; + +/*! + * All of the pure data values associated with a single view in a projection + * layer. + * + * The @ref xrt_swapchain references and @ref xrt_device are provided outside of + * this struct. + * + * @ingroup xrt_iface + */ +struct xrt_layer_projection_view_data +{ + struct xrt_sub_image sub; + + struct xrt_fov fov; + struct xrt_pose pose; +}; + +/*! + * All the pure data values associated with a stereo projection layer. + * + * The @ref xrt_swapchain references and @ref xrt_device are provided outside of + * this struct. + * + * @ingroup xrt_iface + */ +struct xrt_layer_stereo_projection_data +{ + struct xrt_layer_projection_view_data l, r; +}; + +/*! + * All the pure data values associated with a composition layer. + * + * The @ref xrt_swapchain references and @ref xrt_device are provided outside of + * this struct. + * + * @ingroup xrt_iface + */ +struct xrt_layer_data +{ + /*! + * Tag for compositor layer type. + */ + enum xrt_layer_type type; + + /*! + * Often @ref XRT_INPUT_GENERIC_HEAD_POSE + */ + enum xrt_input_name name; + + /*! + * "Display no-earlier-than" timestamp for this layer. + * + * The layer may be displayed after this point, but must never be + * displayed before. + */ + uint64_t timestamp; + + /*! + * Composition flags + */ + enum xrt_layer_composition_flags flags; + + /*! + * Whether the main compositor should flip the direction of y when + * rendering. + * + * This is actually an input only to the "main" compositor + * comp_compositor. It is overwritten by the various client + * implementations of the @ref xrt_compositor interface depending on the + * conventions of the associated graphics API. Other @ref + * xrt_compositor_fd implementations that are not the main compositor + * just pass this field along unchanged to the "real" compositor. + */ + bool flip_y; + + /*! + * Union of data values for the various layer types. + * + * The initialized member of this union should match the value of + * xrt_layer_data::type. It also should be clear because of the layer + * function called between xrt_compositor::layer_begin and + * xrt_compositor::layer_commit where this data was passed. + */ + union { + struct xrt_layer_quad_data quad; + struct xrt_layer_stereo_projection_data stereo; + }; +}; + /*! * @interface xrt_swapchain * Common swapchain interface/base. @@ -78,7 +246,9 @@ enum xrt_view_type struct xrt_swapchain { /*! - * Number of images, the images themselves are on the subclasses. + * Number of images. + * + * The images themselves are on the subclasses. */ uint32_t num_images; @@ -88,11 +258,19 @@ struct xrt_swapchain void (*destroy)(struct xrt_swapchain *xsc); /*! - * See xrWaitSwapchainImage, must make sure that no image is acquired - * before calling acquire_image. + * Obtain the index of the next image to use, without blocking on being + * able to write to it. + * + * See xrAcquireSwapchainImage. + * + * Caller must make sure that no image is acquired before calling + * acquire_image. + * + * @param xsc Self pointer + * @param[out] out_index Image index to use next */ xrt_result_t (*acquire_image)(struct xrt_swapchain *xsc, - uint32_t *index); + uint32_t *out_index); /*! * See xrWaitSwapchainImage, state tracker needs to track index. @@ -116,9 +294,9 @@ struct xrt_swapchain * @public @memberof xrt_swapchain */ static inline xrt_result_t -xrt_swapchain_acquire_image(struct xrt_swapchain *xsc, uint32_t *index) +xrt_swapchain_acquire_image(struct xrt_swapchain *xsc, uint32_t *out_index) { - return xsc->acquire_image(xsc, index); + return xsc->acquire_image(xsc, out_index); } /*! @@ -267,75 +445,30 @@ struct xrt_compositor * Adds a stereo projection layer for submissions. * * @param xc Self pointer - * @param timestamp When should this layer be shown. - * @param xdev The device the layer is relative to. - * @param name Which pose this layer is relative to. - * @param layer_flags Flags for this layer, applied to both images. - * @param l_sc Left swapchain. - * @param l_image_index Left image index as return by acquire_image. - * @param l_rect Left subimage rect. - * @param l_array_index Left array index. - * @param l_fov Left fov the left projection rendered with. - * @param l_pose Left pose the left projection rendered with. - * @param r_sc Right swapchain. - * @param r_image_index Right image index as return by acquire_image. - * @param r_rect Right subimage rect. - * @param r_array_index Right array index. - * @param r_fov Right fov the left projection rendered with. - * @param r_pose Right pose the left projection rendered with. - * @param flip_y Flip Y texture coordinates. + * @param xdev The device the layer is relative to. + * @param l_xsc Left swapchain. + * @param r_xsc Right swapchain. + * @param data All of the pure data bits. */ - xrt_result_t (*layer_stereo_projection)( - struct xrt_compositor *xc, - uint64_t timestamp, - struct xrt_device *xdev, - enum xrt_input_name name, - enum xrt_layer_composition_flags layer_flags, - struct xrt_swapchain *l_sc, - uint32_t l_image_index, - struct xrt_rect *l_rect, - uint32_t l_array_index, - struct xrt_fov *l_fov, - struct xrt_pose *l_pose, - struct xrt_swapchain *r_sc, - uint32_t r_image_index, - struct xrt_rect *r_rect, - uint32_t r_array_index, - struct xrt_fov *r_fov, - struct xrt_pose *r_pose, - bool flip_y); + xrt_result_t (*layer_stereo_projection)(struct xrt_compositor *xc, + struct xrt_device *xdev, + struct xrt_swapchain *l_xsc, + struct xrt_swapchain *r_xsc, + struct xrt_layer_data *data); /*! * Adds a quad layer for submission, the center of the quad is specified * by the pose and extends outwards from it. * * @param xc Self pointer - * @param timestamp When should this layer be shown. * @param xdev The device the layer is relative to. - * @param name Which pose this layer is relative to. - * @param layer_flags Flags for this layer. - * @param visibility Which views are is this layer visible in. - * @param sc Swapchain. - * @param image_index Image index as return by acquire_image. - * @param rect Subimage rect. - * @param array_index Array index. - * @param pose Pose the left projection rendered with. - * @param size Size of the quad in meters. - * @param flip_y Flip Y texture coordinates. + * @param xsc Swapchain. + * @param data All of the pure data bits. */ xrt_result_t (*layer_quad)(struct xrt_compositor *xc, - uint64_t timestamp, struct xrt_device *xdev, - enum xrt_input_name name, - enum xrt_layer_composition_flags layer_flags, - enum xrt_layer_eye_visibility visibility, - struct xrt_swapchain *sc, - uint32_t image_index, - struct xrt_rect *rect, - uint32_t array_index, - struct xrt_pose *pose, - struct xrt_vec2 *size, - bool flip_y); + struct xrt_swapchain *xsc, + struct xrt_layer_data *data); /*! * Commits all of the submitted layers, it's from this on that the @@ -493,28 +626,12 @@ xrt_comp_layer_begin(struct xrt_compositor *xc, */ static inline xrt_result_t xrt_comp_layer_stereo_projection(struct xrt_compositor *xc, - uint64_t timestamp, struct xrt_device *xdev, - enum xrt_input_name name, - enum xrt_layer_composition_flags layer_flags, - struct xrt_swapchain *l_sc, - uint32_t l_image_index, - struct xrt_rect *l_rect, - uint32_t l_array_index, - struct xrt_fov *l_fov, - struct xrt_pose *l_pose, - struct xrt_swapchain *r_sc, - uint32_t r_image_index, - struct xrt_rect *r_rect, - uint32_t r_array_index, - struct xrt_fov *r_fov, - struct xrt_pose *r_pose, - bool flip_y) -{ - return xc->layer_stereo_projection( - xc, timestamp, xdev, name, layer_flags, l_sc, l_image_index, l_rect, - l_array_index, l_fov, l_pose, r_sc, r_image_index, r_rect, - r_array_index, r_fov, r_pose, flip_y); + struct xrt_swapchain *l_xsc, + struct xrt_swapchain *r_xsc, + struct xrt_layer_data *data) +{ + return xc->layer_stereo_projection(xc, xdev, l_xsc, r_xsc, data); } /*! @@ -526,22 +643,11 @@ xrt_comp_layer_stereo_projection(struct xrt_compositor *xc, */ static inline xrt_result_t xrt_comp_layer_quad(struct xrt_compositor *xc, - uint64_t timestamp, struct xrt_device *xdev, - enum xrt_input_name name, - enum xrt_layer_composition_flags layer_flags, - enum xrt_layer_eye_visibility visibility, - struct xrt_swapchain *sc, - uint32_t image_index, - struct xrt_rect *rect, - uint32_t array_index, - struct xrt_pose *pose, - struct xrt_vec2 *size, - bool flip_y) -{ - return xc->layer_quad(xc, timestamp, xdev, name, layer_flags, - visibility, sc, image_index, rect, array_index, - pose, size, flip_y); + struct xrt_swapchain *xsc, + struct xrt_layer_data *data) +{ + return xc->layer_quad(xc, xdev, xsc, data); } /*! @@ -643,6 +749,7 @@ xrt_compositor_gl(struct xrt_compositor *xc) * */ +typedef struct VkCommandBuffer_T *VkCommandBuffer; #ifdef XRT_64_BIT typedef struct VkImage_T *VkImage; typedef struct VkDeviceMemory_T *VkDeviceMemory; @@ -664,6 +771,10 @@ struct xrt_swapchain_vk VkImage images[XRT_MAX_SWAPCHAIN_IMAGES]; VkDeviceMemory mems[XRT_MAX_SWAPCHAIN_IMAGES]; + + // Prerecorded swapchain image ownership/layout transition barriers + VkCommandBuffer acquire[XRT_MAX_SWAPCHAIN_IMAGES]; + VkCommandBuffer release[XRT_MAX_SWAPCHAIN_IMAGES]; }; /*! diff --git a/src/xrt/include/xrt/xrt_defines.h b/src/xrt/include/xrt/xrt_defines.h index 3906f42c50030f99cde7e7d0d79f05a1eb98158f..60773d7f263931bc5e552641d855ef70a72f46e9 100644 --- a/src/xrt/include/xrt/xrt_defines.h +++ b/src/xrt/include/xrt/xrt_defines.h @@ -155,32 +155,6 @@ enum xrt_stereo_format XRT_STEREO_FORMAT_OAU, //!< Over & Under. }; -/*! - * Bit field for holding information about how a layer should be composited. - * - * @ingroup xrt_iface - */ -enum xrt_layer_composition_flags -{ - XRT_LAYER_COMPOSITION_CORRECT_CHROMATIC_ABERRATION_BIT = 1 << 0, - XRT_LAYER_COMPOSITION_BLEND_TEXTURE_SOURCE_ALPHA_BIT = 1 << 1, - XRT_LAYER_COMPOSITION_UNPREMULTIPLIED_ALPHA_BIT = 1 << 2, -}; - -/*! - * Which view is they layer visible to, used for quad layers. Doesn't have the - * same values as the OpenXR counter-parts. - * - * @ingroup xrt_iface - */ -enum xrt_layer_eye_visibility -{ - XRT_LAYER_EYE_VISIBILITY_NONE = 0x0, - XRT_LAYER_EYE_VISIBILITY_LEFT_BIT = 0x1, - XRT_LAYER_EYE_VISIBILITY_RIGHT_BIT = 0x2, - XRT_LAYER_EYE_VISIBILITY_BOTH = 0x3, -}; - /*! * A quaternion with single floats. * @@ -517,8 +491,8 @@ enum xrt_input_type * * @param name A xrt_input_name value * - * @see xrt_input_name - * @see xrt_input_type + * @relates xrt_input_name + * @returns @ref xrt_input_type * @ingroup xrt_iface */ #define XRT_GET_INPUT_TYPE(name) (name & 0xff) @@ -610,11 +584,9 @@ enum xrt_input_name union xrt_input_value { struct xrt_vec1 vec1; struct xrt_vec2 vec2; - struct xrt_vec3 vec3; bool boolean; }; - /*! * Base type of this output. * diff --git a/src/xrt/include/xrt/xrt_results.h b/src/xrt/include/xrt/xrt_results.h index f9fc6d17b8089aeb1a94764b86ec961a7367da6f..6818f935a040f23ee2c7a0b2a197d7d694d4face 100644 --- a/src/xrt/include/xrt/xrt_results.h +++ b/src/xrt/include/xrt/xrt_results.h @@ -13,5 +13,6 @@ typedef enum xrt_result { XRT_SUCCESS = 0, XRT_ERROR_IPC_FAILURE = -1, - XRT_ERROR_NO_IMAGE_AVAILABLE = -2 + XRT_ERROR_NO_IMAGE_AVAILABLE = -2, + XRT_ERROR_FAILED_TO_SUBMIT_VULKAN_COMMANDS = -3, } xrt_result_t; diff --git a/src/xrt/ipc/ipc_client_compositor.c b/src/xrt/ipc/ipc_client_compositor.c index 5d469e4eecf8a567c7749eec71fd8371676d359b..5f2680b7f069315e3d3286a05b84c7cf86157ec2 100644 --- a/src/xrt/ipc/ipc_client_compositor.c +++ b/src/xrt/ipc/ipc_client_compositor.c @@ -363,55 +363,24 @@ ipc_compositor_layer_begin(struct xrt_compositor *xc, } static xrt_result_t -ipc_compositor_layer_stereo_projection( - struct xrt_compositor *xc, - uint64_t timestamp, - struct xrt_device *xdev, - enum xrt_input_name name, - enum xrt_layer_composition_flags layer_flags, - struct xrt_swapchain *l_sc, - uint32_t l_image_index, - struct xrt_rect *l_rect, - uint32_t l_array_index, - struct xrt_fov *l_fov, - struct xrt_pose *l_pose, - struct xrt_swapchain *r_sc, - uint32_t r_image_index, - struct xrt_rect *r_rect, - uint32_t r_array_index, - struct xrt_fov *r_fov, - struct xrt_pose *r_pose, - bool flip_y) +ipc_compositor_layer_stereo_projection(struct xrt_compositor *xc, + struct xrt_device *xdev, + struct xrt_swapchain *l_xsc, + struct xrt_swapchain *r_xsc, + struct xrt_layer_data *data) { struct ipc_client_compositor *icc = ipc_client_compositor(xc); struct ipc_shared_memory *ism = icc->ipc_c->ism; struct ipc_layer_slot *slot = &ism->slots[icc->layers.slot_id]; struct ipc_layer_entry *layer = &slot->layers[icc->layers.num_layers]; - struct ipc_layer_stereo_projection *stereo = &layer->stereo; - struct ipc_client_swapchain *l = ipc_client_swapchain(l_sc); - struct ipc_client_swapchain *r = ipc_client_swapchain(r_sc); - - stereo->timestamp = timestamp; - stereo->xdev_id = 0; //! @todo Real id. - stereo->name = name; - stereo->layer_flags = layer_flags; - stereo->l.swapchain_id = l->id; - stereo->l.image_index = l_image_index; - stereo->l.rect = *l_rect; - stereo->l.array_index = l_array_index; - stereo->l.fov = *l_fov; - stereo->l.pose = *l_pose; - stereo->r.swapchain_id = r->id; - stereo->r.image_index = r_image_index; - stereo->r.rect = *r_rect; - stereo->r.array_index = r_array_index; - stereo->r.fov = *r_fov; - stereo->r.pose = *r_pose; - - layer->flip_y = flip_y; - - layer->type = IPC_LAYER_STEREO_PROJECTION; + struct ipc_client_swapchain *l = ipc_client_swapchain(l_xsc); + struct ipc_client_swapchain *r = ipc_client_swapchain(r_xsc); + + layer->xdev_id = 0; //! @todo Real id. + layer->swapchain_ids[0] = l->id; + layer->swapchain_ids[1] = r->id; + layer->data = *data; // Increment the number of layers. icc->layers.num_layers++; @@ -421,40 +390,23 @@ ipc_compositor_layer_stereo_projection( static xrt_result_t ipc_compositor_layer_quad(struct xrt_compositor *xc, - uint64_t timestamp, struct xrt_device *xdev, - enum xrt_input_name name, - enum xrt_layer_composition_flags layer_flags, - enum xrt_layer_eye_visibility visibility, - struct xrt_swapchain *sc, - uint32_t image_index, - struct xrt_rect *rect, - uint32_t array_index, - struct xrt_pose *pose, - struct xrt_vec2 *size, - bool flip_y) + struct xrt_swapchain *xsc, + struct xrt_layer_data *data) { struct ipc_client_compositor *icc = ipc_client_compositor(xc); struct ipc_shared_memory *ism = icc->ipc_c->ism; struct ipc_layer_slot *slot = &ism->slots[icc->layers.slot_id]; struct ipc_layer_entry *layer = &slot->layers[icc->layers.num_layers]; - struct ipc_layer_quad *quad = &layer->quad; - struct ipc_client_swapchain *ics = ipc_client_swapchain(sc); - - quad->timestamp = timestamp; - quad->xdev_id = 0; //! @todo Real id. - quad->name = name; - quad->layer_flags = layer_flags; - quad->swapchain_id = ics->id; - quad->image_index = image_index; - quad->rect = *rect; - quad->array_index = array_index; - quad->pose = *pose; - quad->size = *size; - - layer->flip_y = flip_y; - layer->type = IPC_LAYER_QUAD; + struct ipc_client_swapchain *ics = ipc_client_swapchain(xsc); + + assert(data->type == XRT_LAYER_QUAD); + + layer->xdev_id = 0; //! @todo Real id. + layer->swapchain_ids[0] = ics->id; + layer->swapchain_ids[1] = -1; + layer->data = *data; // Increment the number of layers. icc->layers.num_layers++; diff --git a/src/xrt/ipc/ipc_protocol.h b/src/xrt/ipc/ipc_protocol.h index f955f16bde4fcee2afb9201d7b449cb256a08590..3ef5bdeef407de06edcd14100537c938a0a1f117 100644 --- a/src/xrt/ipc/ipc_protocol.h +++ b/src/xrt/ipc/ipc_protocol.h @@ -78,58 +78,37 @@ struct ipc_shared_device uint32_t first_output_index; }; -struct ipc_layer_stereo_projection -{ - uint64_t timestamp; - - uint32_t xdev_id; - enum xrt_input_name name; - enum xrt_layer_composition_flags layer_flags; - - struct - { - uint32_t swapchain_id; - uint32_t image_index; - struct xrt_rect rect; - uint32_t array_index; - struct xrt_fov fov; - struct xrt_pose pose; - } l, r; -}; - -struct ipc_layer_quad -{ - uint64_t timestamp; - - uint32_t xdev_id; - enum xrt_input_name name; - enum xrt_layer_composition_flags layer_flags; - - uint32_t swapchain_id; - uint32_t image_index; - struct xrt_rect rect; - uint32_t array_index; - struct xrt_pose pose; - struct xrt_vec2 size; -}; - -enum ipc_layer_type -{ - IPC_LAYER_STEREO_PROJECTION, - IPC_LAYER_QUAD, -}; - +/*! + * Data for a single composition layer. + * + * Similar in function to @ref comp_layer + * + * @ingroup ipc + */ struct ipc_layer_entry { - enum ipc_layer_type type; - bool flip_y; + //! @todo what is this used for? + uint32_t xdev_id; - union { - struct ipc_layer_quad quad; - struct ipc_layer_stereo_projection stereo; - }; + /*! + * Up to two indices of swapchains to use. + * + * How many are actually used depends on the value of @p data.type + */ + uint32_t swapchain_ids[2]; + + /*! + * All basic (trivially-serializable) data associated with a layer, + * aside from which swapchain(s) are used. + */ + struct xrt_layer_data data; }; +/*! + * Render state for a single client, including all layers. + * + * @ingroup ipc + */ struct ipc_layer_slot { enum xrt_blend_mode env_blend_mode; @@ -215,7 +194,7 @@ struct ipc_compositor_state /* * - * Reset of protocol is generated. + * Rest of protocol is generated. * */ diff --git a/src/xrt/ipc/ipc_server.h b/src/xrt/ipc/ipc_server.h index d5613646815467b1aa9acd8c70c934ce72c4b1c2..e30b1e081b605fa43f6560f73cc9fb993804b3a2 100644 --- a/src/xrt/ipc/ipc_server.h +++ b/src/xrt/ipc/ipc_server.h @@ -85,50 +85,6 @@ struct ipc_swapchain_data bool active; }; -struct ipc_quad_render_state -{ - uint32_t swapchain_index; - uint32_t image_index; - uint32_t array_index; - - struct xrt_pose pose; - struct xrt_vec2 size; -}; - -struct ipc_stereo_projection_render_state -{ - struct - { - uint32_t swapchain_index; - uint32_t image_index; - uint32_t array_index; - } l, r; -}; - -struct ipc_layer_render_state -{ - enum ipc_layer_type type; - bool flip_y; - - union { - struct ipc_quad_render_state quad; - struct ipc_stereo_projection_render_state stereo; - }; -}; - -/*! - * Render state for a client. - * - * @ingroup ipc_server - */ -struct ipc_render_state -{ - bool rendering; - enum xrt_blend_mode env_blend_mode; - uint32_t num_layers; - struct ipc_layer_render_state layers[IPC_MAX_LAYERS]; - uint32_t slot_id; -}; struct ipc_queued_event { @@ -163,7 +119,10 @@ struct ipc_client_state int ipc_socket_fd; //! State for rendering. - struct ipc_render_state render_state; + struct ipc_layer_slot render_state; + + //! Whether we are currently rendering @ref render_state + bool rendering_state; struct xrt_client_state client_state; struct ipc_queued_event queued_events[IPC_EVENT_QUEUE_SIZE]; @@ -252,6 +211,8 @@ ipc_server_client_thread(void *_cs); * Create a single wait thread. * * @ingroup ipc_server + * @public @memberof ipc_server + * @relatesalso ipc_wait */ int ipc_server_wait_alloc(struct ipc_server *s, struct ipc_wait **out_iw); @@ -260,6 +221,7 @@ ipc_server_wait_alloc(struct ipc_server *s, struct ipc_wait **out_iw); * Destroy a wait thread, checks for NULL and sets to NULL. * * @ingroup ipc_server + * @public @memberof ipc_wait */ void ipc_server_wait_free(struct ipc_wait **out_iw); @@ -269,11 +231,21 @@ ipc_server_wait_free(struct ipc_wait **out_iw); * wait frame. * * @ingroup ipc_server + * @public @memberof ipc_wait */ void ipc_server_wait_add_frame(struct ipc_wait *iw, volatile struct ipc_client_state *cs); +/*! + * Reset the wait state for wait frame, after the client disconnected + * + * @ingroup ipc_server + * @public @memberof ipc_wait + */ +void +ipc_server_wait_reset_client(struct ipc_wait *iw, + volatile struct ipc_client_state *cs); #ifdef __cplusplus } diff --git a/src/xrt/ipc/ipc_server_client.c b/src/xrt/ipc/ipc_server_client.c index e52ed3e64c50fd43291c54dfcadda7568acd60f0..6a21f1bd97ef358577d6427ac3f6f06c1e07d38d 100644 --- a/src/xrt/ipc/ipc_server_client.c +++ b/src/xrt/ipc/ipc_server_client.c @@ -122,40 +122,9 @@ ipc_handle_compositor_layer_sync(volatile struct ipc_client_state *ics, struct ipc_shared_memory *ism = ics->server->ism; struct ipc_layer_slot *slot = &ism->slots[slot_id]; - for (uint32_t i = 0; i < slot->num_layers; i++) { - struct ipc_layer_render_state *irs = - &ics->render_state.layers[i]; - struct ipc_layer_entry *sl = &slot->layers[i]; - - irs->type = sl->type; - irs->flip_y = sl->flip_y; - - switch (irs->type) { - case IPC_LAYER_STEREO_PROJECTION: - irs->stereo.l.swapchain_index = - sl->stereo.l.swapchain_id; - irs->stereo.l.image_index = sl->stereo.l.image_index; - irs->stereo.r.swapchain_index = - sl->stereo.r.swapchain_id; - irs->stereo.r.image_index = sl->stereo.r.image_index; - irs->stereo.l.array_index = sl->stereo.l.array_index; - irs->stereo.r.array_index = sl->stereo.r.array_index; - - break; - case IPC_LAYER_QUAD: - irs->quad.swapchain_index = sl->quad.swapchain_id; - irs->quad.image_index = sl->quad.image_index; - irs->quad.pose = sl->quad.pose; - irs->quad.size = sl->quad.size; - irs->quad.array_index = sl->quad.array_index; - break; - } - } - - ics->render_state.num_layers = slot->num_layers; - ics->render_state.slot_id = slot_id; - ics->render_state.rendering = true; - + // Copy current slot data to our state. + ics->render_state = *slot; + ics->rendering_state = true; os_mutex_lock(&ics->server->global_state_lock); *out_free_slot_id = @@ -577,19 +546,24 @@ client_loop(volatile struct ipc_client_state *ics) ics->num_swapchains = 0; // Make sure to reset the renderstate fully. + ics->rendering_state = false; ics->render_state.num_layers = 0; - ics->render_state.rendering = false; for (uint32_t i = 0; i < ARRAY_SIZE(ics->render_state.layers); ++i) { - volatile struct ipc_layer_render_state *lrs = + volatile struct ipc_layer_entry *rl = &ics->render_state.layers[i]; - lrs->flip_y = false; - lrs->stereo.l.swapchain_index = 0; - lrs->stereo.l.image_index = 0; - lrs->stereo.r.swapchain_index = 0; - lrs->stereo.r.image_index = 0; - lrs->quad.swapchain_index = 0; - lrs->quad.image_index = 0; + rl->swapchain_ids[0] = 0; + rl->swapchain_ids[1] = 0; + rl->data.flip_y = false; + /*! + * @todo this is redundant, we're setting both elements of a + * union. Why? Can we just zero the whole render_state? + */ + rl->data.stereo.l.sub.image_index = 0; + rl->data.stereo.r.sub.image_index = 0; + rl->data.quad.sub.image_index = 0; + + //! @todo set rects or array index? } // Destroy all swapchains now. @@ -601,6 +575,8 @@ client_loop(volatile struct ipc_client_state *ics) if (ics->server->exit_on_disconnect) { ics->server->running = false; } + + ipc_server_wait_reset_client(ics->server->iw, ics); } diff --git a/src/xrt/ipc/ipc_server_process.c b/src/xrt/ipc/ipc_server_process.c index a4c06b0e94e41e5d1b52f11d01a07bf2a20bab68..8db940b8c291f3779166edab96c1b2ed7caf2758 100644 --- a/src/xrt/ipc/ipc_server_process.c +++ b/src/xrt/ipc/ipc_server_process.c @@ -594,11 +594,13 @@ send_client_state(struct ipc_client_state *ics) static bool _update_projection_layer(struct comp_compositor *c, volatile struct ipc_client_state *active_client, - volatile struct ipc_layer_render_state *layer, + volatile struct ipc_layer_entry *layer, uint32_t i) { - uint32_t lsi = layer->stereo.l.swapchain_index; - uint32_t rsi = layer->stereo.r.swapchain_index; + // left + uint32_t lsi = layer->swapchain_ids[0]; + // right + uint32_t rsi = layer->swapchain_ids[1]; if (active_client->xscs[lsi] == NULL || active_client->xscs[rsi] == NULL) { @@ -612,12 +614,15 @@ _update_projection_layer(struct comp_compositor *c, struct comp_swapchain_image *l = NULL; struct comp_swapchain_image *r = NULL; - l = &cl->images[layer->stereo.l.image_index]; - r = &cr->images[layer->stereo.r.image_index]; + l = &cl->images[layer->data.stereo.l.sub.image_index]; + r = &cr->images[layer->data.stereo.r.sub.image_index]; - comp_renderer_set_projection_layer(c->r, l, r, layer->flip_y, i, - layer->stereo.l.array_index, - layer->stereo.r.array_index); + + // Cast away volatile. + struct xrt_layer_data *data = (struct xrt_layer_data *)&layer->data; + + //! @todo we are ignoring subrect here! + comp_renderer_set_projection_layer(c->r, i, l, r, data); return true; } @@ -625,10 +630,10 @@ _update_projection_layer(struct comp_compositor *c, static bool _update_quad_layer(struct comp_compositor *c, volatile struct ipc_client_state *active_client, - volatile struct ipc_layer_render_state *layer, + volatile struct ipc_layer_entry *layer, uint32_t i) { - uint32_t sci = layer->quad.swapchain_index; + uint32_t sci = layer->swapchain_ids[0]; if (active_client->xscs[sci] == NULL) { fprintf(stderr, "ERROR: Invalid swap chain for quad layer.\n"); @@ -637,13 +642,13 @@ _update_quad_layer(struct comp_compositor *c, struct comp_swapchain *sc = comp_swapchain(active_client->xscs[sci]); struct comp_swapchain_image *image = NULL; - image = &sc->images[layer->quad.image_index]; + image = &sc->images[layer->data.quad.sub.image_index]; - struct xrt_pose pose = layer->quad.pose; - struct xrt_vec2 size = layer->quad.size; + // Cast away volatile. + struct xrt_layer_data *data = (struct xrt_layer_data *)&layer->data; - comp_renderer_set_quad_layer(c->r, image, &pose, &size, layer->flip_y, - i, layer->quad.array_index); + //! @todo we are ignoring subrect here! + comp_renderer_set_quad_layer(c->r, i, image, data); return true; } @@ -721,16 +726,16 @@ _update_layers(struct comp_compositor *c, for (uint32_t j = 0; j < ics->render_state.num_layers; j++) { - volatile struct ipc_layer_render_state *layer = + volatile struct ipc_layer_entry *layer = &ics->render_state.layers[j]; - switch (layer->type) { - case IPC_LAYER_STEREO_PROJECTION: { + switch (layer->data.type) { + case XRT_LAYER_STEREO_PROJECTION: { _update_projection_layer(c, ics, layer, layer_id); layer_id++; break; } - case IPC_LAYER_QUAD: { + case XRT_LAYER_QUAD: { _update_quad_layer(c, ics, layer, layer_id); layer_id++; @@ -751,8 +756,8 @@ main_loop(struct ipc_server *s) struct xrt_compositor *xc = s->xc; struct comp_compositor *c = comp_compositor(xc); - // make sure all our client connections have a handle to the compositor - // and consistent initial state + // make sure all our client connections have a handle to the + // compositor and consistent initial state uint32_t num_layers = 0; @@ -791,21 +796,18 @@ main_loop(struct ipc_server *s) // swapchain indices and toggle wait to false // when the client calls end_frame, signalling // us to render. - volatile struct ipc_render_state *render_state = - &active_client->render_state; - - if (render_state->rendering) { + if (active_client->rendering_state) { if (!_update_layers(c, s, &num_layers)) continue; // set our client state back to waiting. - render_state->rendering = false; + active_client->rendering_state = false; } } comp_renderer_draw(c->r); - // we should release any slots that are not used by a rendering - // client + // we should release any slots that are not used by a + // rendering client // Now is a good time to destroy objects. @@ -848,8 +850,8 @@ handle_focused_client_events(struct ipc_client_state *ics, int prev_active_id) { - // if our prev active id is -1 and our cur active id is -1, we can bail - // out early + // if our prev active id is -1 and our cur active id is -1, we + // can bail out early if (active_id == -1 && prev_active_id == -1) { return; @@ -868,7 +870,8 @@ handle_focused_client_events(struct ipc_client_state *ics, ics->client_state.session_visible = true; } - // set visible + focused if we are the primary application + // set visible + focused if we are the primary + // application if (ics->server_thread_index == active_id) { ics->client_state.session_visible = true; ics->client_state.session_focused = true; @@ -877,7 +880,8 @@ handle_focused_client_events(struct ipc_client_state *ics, return; } - // no primary application, set all overlays to synchronised state + // no primary application, set all overlays to synchronised + // state if (ics->client_state.session_overlay) { ics->client_state.session_focused = false; ics->client_state.session_visible = false; @@ -914,8 +918,8 @@ void update_server_state(struct ipc_server *s) { - pthread_mutex_lock(&s->global_state_lock); // multiple threads could - // call this at + pthread_mutex_lock(&s->global_state_lock); // multiple threads + // could call this at // the same time // if our client that is set to active is still active, @@ -957,8 +961,9 @@ update_server_state(struct ipc_server *s) } } - // if our currentlly-set active primary application is not actually - // active/displayable, use the fallback application instead. + // if our currently-set active primary application is not + // actually active/displayable, use the fallback application + // instead. struct ipc_client_state *ics = &s->thread_state[s->active_client_index]; if (!(ics->client_state.session_overlay == false && s->active_client_index >= 0 && diff --git a/src/xrt/ipc/ipc_server_wait.c b/src/xrt/ipc/ipc_server_wait.c index 25a565b49561acfe1b6afd2c19b964e057b4f244..aa7bc0d6cd3ce8d9d5620a9aa39b82272da89182 100644 --- a/src/xrt/ipc/ipc_server_wait.c +++ b/src/xrt/ipc/ipc_server_wait.c @@ -114,6 +114,28 @@ ipc_server_wait_add_frame(struct ipc_wait *iw, os_thread_helper_unlock(&iw->oth); } +void +ipc_server_wait_reset_client(struct ipc_wait *iw, + volatile struct ipc_client_state *cs) +{ + os_thread_helper_lock(&iw->oth); + + /* ipc_server_wait_add_frame would overwrite dangling references, + * but clean them up anyway to be less confusing. */ + for (int i = 0; i < IPC_MAX_CLIENTS; i++) { + if (iw->cs[i] == cs) { + iw->cs[i] = NULL; + } + } + + volatile struct ipc_shared_memory *ism = iw->s->ism; + sem_init((sem_t *)&ism->wait_frame.sem, true, 0); + ism->wait_frame.predicted_display_period = 0; + ism->wait_frame.predicted_display_time = 0; + + os_thread_helper_unlock(&iw->oth); +} + void ipc_server_wait_free(struct ipc_wait **out_iw) { diff --git a/src/xrt/state_trackers/gui/CMakeLists.txt b/src/xrt/state_trackers/gui/CMakeLists.txt index 863a76f8d1ae5b7e2814cbabe6b74ab775e238be..ca119247c776c894c454abe04e1716693386e02e 100644 --- a/src/xrt/state_trackers/gui/CMakeLists.txt +++ b/src/xrt/state_trackers/gui/CMakeLists.txt @@ -1,6 +1,9 @@ # Copyright 2019-2020, Collabora, Ltd. # SPDX-License-Identifier: BSL-1.0 +# c-imgui doesn't do well with IPO - lots of warnings. +set(CMAKE_INTERPROCEDURAL_OPTIMIZATION OFF) + set(GUI_SOURCE_FILES gui_common.h gui_imgui.h diff --git a/src/xrt/state_trackers/oxr/CMakeLists.txt b/src/xrt/state_trackers/oxr/CMakeLists.txt index 6540cd4bfcdb0222e1e7937f8dbbb3e4669ae2c6..9559c0d6b983758eb2002f139618cd4d79e02763 100644 --- a/src/xrt/state_trackers/oxr/CMakeLists.txt +++ b/src/xrt/state_trackers/oxr/CMakeLists.txt @@ -1,7 +1,32 @@ # Copyright 2019-2020, Collabora, Ltd. # SPDX-License-Identifier: BSL-1.0 + +### +# Binding generation +# + +function(bindings_gen output) + add_custom_command(OUTPUT ${output} + COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/bindings.py + ${CMAKE_CURRENT_SOURCE_DIR}/bindings.json + ${output} + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/bindings.py + ${CMAKE_CURRENT_SOURCE_DIR}/bindings.json + ) +endfunction(bindings_gen) + +bindings_gen(${CMAKE_CURRENT_BINARY_DIR}/oxr_generated_bindings.h) +bindings_gen(${CMAKE_CURRENT_BINARY_DIR}/oxr_generated_bindings.c) + + +### +# Main code +# + set(OXR_SOURCE_FILES + ${CMAKE_CURRENT_BINARY_DIR}/oxr_generated_bindings.h + ${CMAKE_CURRENT_BINARY_DIR}/oxr_generated_bindings.c oxr_api_action.c oxr_api_funcs.h oxr_api_instance.c @@ -52,4 +77,14 @@ if(XRT_HAVE_EGL) endif() add_library(st_oxr STATIC ${OXR_SOURCE_FILES}) -target_link_libraries(st_oxr PRIVATE xrt-interfaces xrt-external-openxr aux_util aux_math Vulkan::Vulkan comp_client) +target_link_libraries(st_oxr PRIVATE + xrt-interfaces + xrt-external-openxr + aux_util + aux_math + Vulkan::Vulkan + comp_client + ) +target_include_directories(st_oxr PRIVATE + ${CMAKE_CURRENT_BINARY_DIR} + ) diff --git a/src/xrt/state_trackers/oxr/bindings.json b/src/xrt/state_trackers/oxr/bindings.json new file mode 100644 index 0000000000000000000000000000000000000000..93647e9c8502f0c73fc866c1b0416ca5f6dbfd2d --- /dev/null +++ b/src/xrt/state_trackers/oxr/bindings.json @@ -0,0 +1,245 @@ +{ + "/interaction_profiles/khr/simple_controller": { + "title": "Khronos Simple Controller", + "user_paths": [ + "/user/hand/left", + "/user/hand/right" + ], + "components": [ + {"subpath": "/input/select/click"}, + {"subpath": "/input/menu/click"}, + {"subpath": "/input/grip/pose"}, + {"subpath": "/input/aim/pose"}, + {"subpath": "/output/haptic"} + ] + }, + + "/interaction_profiles/google/daydream_controller": { + "title": "Google Daydream Controller", + "user_paths": [ + "/user/hand/left", + "/user/hand/right" + ], + "components": [ + {"subpath": "/input/select/click"}, + {"subpath": "/input/trackpad/x"}, + {"subpath": "/input/trackpad/y"}, + {"subpath": "/input/trackpad/click"}, + {"subpath": "/input/trackpad/touch"}, + {"subpath": "/input/grip/pose"}, + {"subpath": "/input/aim/pose"} + ] + }, + + "/interaction_profiles/htc/vive_controller": { + "title": "HTC Vive Controller", + "user_paths": [ + "/user/hand/left", + "/user/hand/right" + ], + "components": [ + {"subpath": "/input/system/click", "system": "true"}, + {"subpath": "/input/squeeze/click"}, + {"subpath": "/input/menu/click"}, + {"subpath": "/input/trigger/click"}, + {"subpath": "/input/trigger/value"}, + {"subpath": "/input/trackpad/x"}, + {"subpath": "/input/trackpad/y"}, + {"subpath": "/input/trackpad/click"}, + {"subpath": "/input/trackpad/touch"}, + {"subpath": "/input/grip/pose"}, + {"subpath": "/input/aim/pose"}, + {"subpath": "/output/haptic"} + ] + }, + + "/interaction_profiles/htc/vive_pro": { + "title": "HTC Vive Pro", + "user_paths": [ + "/user/head" + ], + "components": [ + {"subpath": "/input/system/click", "system": "true"}, + {"subpath": "/input/volume_up/click"}, + {"subpath": "/input/volume_down/click"}, + {"subpath": "/input/mute_mic/click"} + ] + }, + + "/interaction_profiles/microsoft/motion_controller": { + "title": "Microsoft Mixed Reality Motion Controller", + "user_paths": [ + "/user/hand/left", + "/user/hand/right" + ], + "components": [ + {"subpath": "/input/menu/click"}, + {"subpath": "/input/squeeze/click"}, + {"subpath": "/input/trigger/value"}, + {"subpath": "/input/thumbstick/x"}, + {"subpath": "/input/thumbstick/y"}, + {"subpath": "/input/thumbstick/click"}, + {"subpath": "/input/trackpad/x"}, + {"subpath": "/input/trackpad/y"}, + {"subpath": "/input/trackpad/click"}, + {"subpath": "/input/trackpad/touch"}, + {"subpath": "/input/grip/pose"}, + {"subpath": "/input/aim/pose"}, + {"subpath": "/output/haptic"} + ] + }, + + "/interaction_profiles/microsoft/xbox_controller": { + "title": "Microsoft Xbox Controller", + "user_paths": [ + "/user/gamepad" + ], + "components": [ + {"subpath": "/input/menu/click"}, + {"subpath": "/input/view/click"}, + {"subpath": "/input/a/click"}, + {"subpath": "/input/b/click"}, + {"subpath": "/input/x/click"}, + {"subpath": "/input/y/click"}, + {"subpath": "/input/dpad_down/click"}, + {"subpath": "/input/dpad_right/click"}, + {"subpath": "/input/dpad_up/click"}, + {"subpath": "/input/dpad_left/click"}, + {"subpath": "/input/shoulder_left/click"}, + {"subpath": "/input/shoulder_right/click"}, + {"subpath": "/input/thumbstick_left/click"}, + {"subpath": "/input/thumbstick_right/click"}, + {"subpath": "/input/trigger_left/value"}, + {"subpath": "/input/trigger_right/value"}, + {"subpath": "/input/thumbstick_left/x"}, + {"subpath": "/input/thumbstick_left/y"}, + {"subpath": "/input/thumbstick_right/x"}, + {"subpath": "/input/thumbstick_right/y"}, + {"subpath": "/output/haptic_left"}, + {"subpath": "/output/haptic_right"}, + {"subpath": "/output/haptic_left_trigger"}, + {"subpath": "/output/haptic_right_trigger"} + ] + }, + + "/interaction_profiles/oculus/go_controller": { + "title": "Oculus Go Controller", + "user_paths": [ + "/user/hand/left", + "/user/hand/right" + ], + "components": [ + {"subpath": "/input/system/click", "system": "true"}, + {"subpath": "/input/trigger/click"}, + {"subpath": "/input/back/click"}, + {"subpath": "/input/trackpad/x"}, + {"subpath": "/input/trackpad/y"}, + {"subpath": "/input/trackpad/click"}, + {"subpath": "/input/trackpad/touch"}, + {"subpath": "/input/grip/pose"}, + {"subpath": "/input/aim/pose"} + ] + }, + + "/interaction_profiles/oculus/touch_controller": { + "title": "Oculus Touch Controller", + "user_paths": [ + "/user/hand/left", + "/user/hand/right" + ], + "components": [ + {"user_paths": "/user/hand/left", "subpath": "/input/x/click"}, + {"user_paths": "/user/hand/left", "subpath": "/input/x/touch"}, + {"user_paths": "/user/hand/left", "subpath": "/input/y/click"}, + {"user_paths": "/user/hand/left", "subpath": "/input/y/touch"}, + {"user_paths": "/user/hand/left", "subpath": "/input/menu/click"}, + {"user_paths": "/user/hand/right", "subpath": "/input/a/click"}, + {"user_paths": "/user/hand/right", "subpath": "/input/a/touch"}, + {"user_paths": "/user/hand/right", "subpath": "/input/b/click"}, + {"user_paths": "/user/hand/right", "subpath": "/input/b/touch"}, + {"user_paths": "/user/hand/right", "subpath": "/input/system/click", "system": "true"}, + {"subpath": "/input/squeeze/value"}, + {"subpath": "/input/trigger/value"}, + {"subpath": "/input/trigger/touch"}, + {"subpath": "/input/thumbstick/x"}, + {"subpath": "/input/thumbstick/y"}, + {"subpath": "/input/thumbstick/click"}, + {"subpath": "/input/thumbstick/touch"}, + {"subpath": "/input/thumbrest/touch"}, + {"subpath": "/input/grip/pose"}, + {"subpath": "/input/aim/pose"}, + {"subpath": "/output/haptic"} + ] + }, + + "/interaction_profiles/valve/index_controller": { + "title": "Valve Index Controller", + "user_paths": [ + "/user/hand/left", + "/user/hand/right" + ], + "components": [ + {"subpath": "/input/system/click", "system": "true"}, + {"subpath": "/input/system/touch", "system": "true"}, + {"subpath": "/input/a/click"}, + {"subpath": "/input/a/touch"}, + {"subpath": "/input/b/click"}, + {"subpath": "/input/b/touch"}, + {"subpath": "/input/squeeze/value"}, + {"subpath": "/input/squeeze/force"}, + {"subpath": "/input/trigger/click"}, + {"subpath": "/input/trigger/value"}, + {"subpath": "/input/trigger/touch"}, + {"subpath": "/input/thumbstick/x"}, + {"subpath": "/input/thumbstick/y"}, + {"subpath": "/input/thumbstick/click"}, + {"subpath": "/input/thumbstick/touch"}, + {"subpath": "/input/trackpad/x"}, + {"subpath": "/input/trackpad/y"}, + {"subpath": "/input/trackpad/force"}, + {"subpath": "/input/trackpad/touch"}, + {"subpath": "/input/grip/pose"}, + {"subpath": "/input/aim/pose"}, + {"subpath": "/output/haptic"} + ] + }, + + "/interaction_profiles/microsoft/hand_interaction": { + "title": "Microsoft hand interaction", + "extension": "XR_MSFT_hand_interaction", + "user_paths": [ + "/user/hand/left", + "/user/hand/right" + ], + "components": [ + {"subpath": "/input/select/value"}, + {"subpath": "/input/squeeze/value"}, + {"subpath": "/input/grip/pose"}, + {"subpath": "/input/aim/pose"} + ] + }, + + "/interaction_profiles/mnd/ball_on_a_stick_controller": { + "title": "Monado ball on a stick controller", + "extension": "XR_MND_ball_on_a_stick_controller", + "user_paths": [ + "/user/hand/left", + "/user/hand/right" + ], + "components": [ + {"subpath": "/input/system/click", "system": "true"}, + {"subpath": "/input/menu/click"}, + {"subpath": "/input/start/click"}, + {"subpath": "/input/select/click"}, + {"subpath": "/input/square_mnd/click"}, + {"subpath": "/input/cross_mnd/click"}, + {"subpath": "/input/circle_mnd/click"}, + {"subpath": "/input/triangle_mnd/click"}, + {"subpath": "/input/trigger/value"}, + {"subpath": "/input/grip/pose"}, + {"subpath": "/input/ball_mnd/pose"}, + {"subpath": "/input/aim/pose"}, + {"subpath": "/output/haptic"} + ] + } +} diff --git a/src/xrt/state_trackers/oxr/bindings.py b/src/xrt/state_trackers/oxr/bindings.py new file mode 100755 index 0000000000000000000000000000000000000000..0f33961c40ceb1c20885895189654449f596d98d --- /dev/null +++ b/src/xrt/state_trackers/oxr/bindings.py @@ -0,0 +1,175 @@ +#!/usr/bin/env python3 +# Copyright 2020, Collabora, Ltd. +# SPDX-License-Identifier: BSL-1.0 +"""Generate code from a JSON file describing interaction profiles and bindings.""" + +import json +import argparse + + +def strip_subpath_end(path): + if (path.endswith("/value")): + return True, path[:-6] + if (path.endswith("/click")): + return True, path[:-6] + if (path.endswith("/touch")): + return True, path[:-6] + if (path.endswith("/pose")): + return True, path[:-5] + if (path.endswith("/x")): + return True, path[:-2] + if (path.endswith("/y")): + return True, path[:-2] + if (path.endswith("/output/haptic")): + return False, path + return False, path + + +class Component: + + @classmethod + def parse_array(cls, user_paths, arr): + """Turn an array of data into an array of Component objects. + Creates a Component for each user_path and stripped subpaths. + """ + check = {} + ret = [] + for elm in arr: + ups = user_paths + if ("user_paths" in elm): + ups = [elm["user_paths"]] + for up in ups: + subpath = elm["subpath"] + did_strip, stripped_path = strip_subpath_end(subpath) + fullpath = up + stripped_path + if (did_strip and not fullpath in check): + check[fullpath] = True + ret.append(cls(fullpath, elm)) + fullpath = up + subpath + ret.append(cls(fullpath, elm)) + return ret + + def __init__(self, path, elm): + """Construct an component.""" + self.path = path + + +class Profile: + """An interctive bindings profile.""" + def __init__(self, name, data): + """Construct an profile.""" + self.name = name + self.func = name[22:].replace("/", "_") + self.components = Component.parse_array(data["user_paths"], data["components"]) + self.by_length = {} + for component in self.components: + l = len(component.path) + if (l in self.by_length): + self.by_length[l].append(component) + else: + self.by_length[l] = [component] + + +class Bindings: + """A group of interactive profiles used in bindings.""" + + @classmethod + def parse(cls, data): + """Parse a dictionary defining a protocol into Profile objects.""" + return cls(data) + + @classmethod + def load_and_parse(cls, file): + """Load a JSON file and parse it into Profile objects.""" + with open(file) as infile: + return cls.parse(json.loads(infile.read())) + + def __init__(self, data): + """Construct a bindings from a dictionary of profiles.""" + self.profiles = [Profile(name, call) for name, call in data.items()] + + +header = '''// Copyright 2020, Collabora, Ltd. +// SPDX-License-Identifier: BSL-1.0 +/*! + * @file + * @brief {brief}. + * @author Jakob Bornecrantz <jakob@collabora.com> + * @ingroup {group} + */ +''' + +func_start = ''' +bool +oxr_verify_{func}_subpath(const char *str, size_t length) +{{ +\tswitch (length) {{ +''' + +if_strcmp = '''if (strcmp(str, "{check}") == 0) {{ +\t\t\treturn true; +\t\t}} else ''' + +def generate_bindings_c(file, p): + """Generate the file to verify subpaths on a interaction profile.""" + f = open(file, "w") + f.write(header.format(brief='Generated bindings data', group='oxr_main')) + f.write(''' +#include "xrt/xrt_compiler.h" + +#include <string.h> + + +// clang-format off +''') + + for profile in p.profiles: + f.write(func_start.format(func=profile.func)) + for length in profile.by_length: + f.write("\tcase " + str(length) + ":\n\t\t") + for component in profile.by_length[length]: + f.write(if_strcmp.format(check=component.path)) + f.write("{\n\t\t\treturn false;\n\t\t}\n") + f.write("\tdefault:\n\t\treturn false;\n\t}\n}\n") + f.write("\n// clang-format on\n") + f.close() + + +def generate_bindings_h(file, p): + """Generate header for the verify subpaths functions.""" + f = open(file, "w") + f.write(header.format(brief='Generated bindings data header', group='oxr_api')) + f.write(''' +#include "xrt/xrt_compiler.h" + + +// clang-format off +''') + + for profile in p.profiles: + f.write("\nbool\noxr_verify_" + profile.func + "_subpath(const char *str, size_t length);\n") + f.write("\n// clang-format on\n") + f.close() + + +def main(): + """Handle command line and generate a file.""" + parser = argparse.ArgumentParser(description='Bindings generator.') + parser.add_argument( + 'bindings', help='Bindings file to use') + parser.add_argument( + 'output', type=str, nargs='+', + help='Output file, uses the name to choose output type') + args = parser.parse_args() + + p = Bindings.load_and_parse(args.bindings) + + for output in args.output: + if output.endswith("oxr_generated_bindings.c"): + generate_bindings_c(output, p) + if output.endswith("oxr_generated_bindings.h"): + generate_bindings_h(output, p) + + +if __name__ == "__main__": + main() diff --git a/src/xrt/state_trackers/oxr/meson.build b/src/xrt/state_trackers/oxr/meson.build index 94f20952af07b923dc45d203d302242a6d5e4516..955c54cd5b5c4e4656c5f6e82b12b926b0b909be 100644 --- a/src/xrt/state_trackers/oxr/meson.build +++ b/src/xrt/state_trackers/oxr/meson.build @@ -1,6 +1,27 @@ # Copyright 2019-2020, Collabora, Ltd. # SPDX-License-Identifier: BSL-1.0 + +### +# Binding generation +# + +prog_python = import('python').find_installation('python3') + +generated = custom_target('bindings code', + command: [prog_python, '@INPUT@', '@OUTPUT@'], + input: ['bindings.py', 'bindings.json'], + output: [ + 'oxr_generated_bindings.h', + 'oxr_generated_bindings.c', + ] +) + + +### +# Main code +# + compile_args = [] if build_opengl compile_args += ['-DXR_USE_GRAPHICS_API_OPENGL', '-DXR_USE_GRAPHICS_API_OPENGL_ES'] @@ -16,7 +37,9 @@ endif lib_st_oxr = static_library( 'st_oxr', - files( + [ + generated[0], + generated[1], 'oxr_api_action.c', 'oxr_api_funcs.h', 'oxr_api_instance.c', @@ -50,7 +73,7 @@ lib_st_oxr = static_library( 'oxr_verify.c', 'oxr_vulkan.c', 'oxr_xdev.c', - ), + ], include_directories: [ xrt_include, openxr_include, diff --git a/src/xrt/state_trackers/oxr/oxr_api_action.c b/src/xrt/state_trackers/oxr/oxr_api_action.c index 027b354f74a4c060aba042a99cbe193bcc59ca6a..6b4e730b8c6c839a6f73e3a3b2fb7d322fd09a2c 100644 --- a/src/xrt/state_trackers/oxr/oxr_api_action.c +++ b/src/xrt/state_trackers/oxr/oxr_api_action.c @@ -15,6 +15,7 @@ #include "oxr_api_funcs.h" #include "oxr_api_verify.h" +#include "oxr_generated_bindings.h" #include <stdio.h> #include <inttypes.h> @@ -105,6 +106,51 @@ oxr_xrSuggestInteractionProfileBindings( "== 0) can not suggest 0 bindings"); } + XrPath ip = suggestedBindings->interactionProfile; + const char *str = NULL; + size_t length; + + XrResult ret = oxr_path_get_string(&log, inst, ip, &str, &length); + if (ret != XR_SUCCESS) { + oxr_error( + &log, ret, + "(suggestedBindings->countSuggestedBindings == 0x%08" PRIx64 + ") invalid path", + ip); + } + + // Used in the loop that verifies the suggested bindings paths. + bool (*func)(const char *, size_t) = NULL; + + if (ip == inst->path_cache.khr_simple_controller) { + func = oxr_verify_khr_simple_controller_subpath; + } else if (ip == inst->path_cache.google_daydream_controller) { + func = oxr_verify_google_daydream_controller_subpath; + } else if (ip == inst->path_cache.htc_vive_controller) { + func = oxr_verify_htc_vive_controller_subpath; + } else if (ip == inst->path_cache.htc_vive_pro) { + func = oxr_verify_htc_vive_pro_subpath; + } else if (ip == inst->path_cache.microsoft_motion_controller) { + func = oxr_verify_microsoft_motion_controller_subpath; + } else if (ip == inst->path_cache.microsoft_xbox_controller) { + func = oxr_verify_microsoft_xbox_controller_subpath; + } else if (ip == inst->path_cache.oculus_go_controller) { + func = oxr_verify_oculus_go_controller_subpath; + } else if (ip == inst->path_cache.oculus_touch_controller) { + func = oxr_verify_oculus_touch_controller_subpath; + } else if (ip == inst->path_cache.valve_index_controller) { + func = oxr_verify_valve_index_controller_subpath; + } else if (ip == inst->path_cache.mnd_ball_on_stick_controller) { + func = oxr_verify_mnd_ball_on_a_stick_controller_subpath; + } else { + return oxr_error( + &log, XR_ERROR_PATH_UNSUPPORTED, + "(suggestedBindings->interactionProfile == \"%s\") is not " + "a supported interaction profile", + str); + } + + for (size_t i = 0; i < suggestedBindings->countSuggestedBindings; i++) { const XrActionSuggestedBinding *s = &suggestedBindings->suggestedBindings[i]; @@ -112,15 +158,17 @@ oxr_xrSuggestInteractionProfileBindings( struct oxr_action *act; OXR_VERIFY_ACTION_NOT_NULL(&log, s->action, act); - if (act->act_set->attached) { + if (act->act_set->data->attached) { return oxr_error( &log, XR_ERROR_ACTIONSETS_ALREADY_ATTACHED, "(suggestedBindings->suggestedBindings[%zu]->" "action) action '%s/%s' has already been attached", - i, act->act_set->name, act->name); + i, act->act_set->data->name, act->data->name); } - if (!oxr_path_is_valid(&log, inst, s->binding)) { + ret = + oxr_path_get_string(&log, inst, s->binding, &str, &length); + if (ret != XR_SUCCESS) { return oxr_error( &log, XR_ERROR_PATH_INVALID, "(suggestedBindings->suggestedBindings[%zu]->" @@ -128,7 +176,13 @@ oxr_xrSuggestInteractionProfileBindings( i, s->binding); } - //! @todo verify path (s->binding). + if (!func(str, length)) { + return oxr_error( + &log, XR_ERROR_PATH_UNSUPPORTED, + "(suggestedBindings->suggestedBindings[%zu]->" + "binding == \"%s\") is not a valid path", + i, str); + } } return oxr_action_suggest_interaction_profile_bindings( @@ -283,7 +337,7 @@ oxr_xrCreateAction(XrActionSet actionSet, OXR_VERIFY_ARG_LOCALIZED_NAME(&log, createInfo->localizedActionName); OXR_VERIFY_ARG_NOT_NULL(&log, action); - if (act_set->attached) { + if (act_set->data->attached) { return oxr_error( &log, XR_ERROR_ACTIONSETS_ALREADY_ATTACHED, "(actionSet) has been attached and is now immutable"); @@ -303,7 +357,7 @@ oxr_xrCreateAction(XrActionSet actionSet, * Dup checks. */ - h_ret = u_hashset_find_c_str(act_set->actions.name_store, + h_ret = u_hashset_find_c_str(act_set->data->actions.name_store, createInfo->actionName, &d); if (h_ret >= 0) { return oxr_error( @@ -312,7 +366,7 @@ oxr_xrCreateAction(XrActionSet actionSet, createInfo->actionName); } - h_ret = u_hashset_find_c_str(act_set->actions.loc_store, + h_ret = u_hashset_find_c_str(act_set->data->actions.loc_store, createInfo->localizedActionName, &d); if (h_ret >= 0) { return oxr_error(&log, XR_ERROR_LOCALIZED_NAME_DUPLICATED, @@ -364,19 +418,20 @@ oxr_xrGetActionStateBoolean(XrSession session, XR_TYPE_ACTION_STATE_GET_INFO); OXR_VERIFY_ACTION_NOT_NULL(&log, getInfo->action, act); - if (act->action_type != XR_ACTION_TYPE_BOOLEAN_INPUT) { + if (act->data->action_type != XR_ACTION_TYPE_BOOLEAN_INPUT) { return oxr_error(&log, XR_ERROR_ACTION_TYPE_MISMATCH, "Not created with boolean type"); } ret = oxr_verify_subaction_path_get( - &log, act->act_set->inst, getInfo->subactionPath, &act->sub_paths, - &sub_paths, "getInfo->subactionPath"); + &log, act->act_set->inst, getInfo->subactionPath, + &act->data->sub_paths, &sub_paths, "getInfo->subactionPath"); if (ret != XR_SUCCESS) { return ret; } - return oxr_action_get_boolean(&log, sess, act->key, sub_paths, data); + return oxr_action_get_boolean(&log, sess, act->act_key, sub_paths, + data); } XrResult @@ -397,19 +452,20 @@ oxr_xrGetActionStateFloat(XrSession session, XR_TYPE_ACTION_STATE_GET_INFO); OXR_VERIFY_ACTION_NOT_NULL(&log, getInfo->action, act); - if (act->action_type != XR_ACTION_TYPE_FLOAT_INPUT) { + if (act->data->action_type != XR_ACTION_TYPE_FLOAT_INPUT) { return oxr_error(&log, XR_ERROR_ACTION_TYPE_MISMATCH, "Not created with float type"); } ret = oxr_verify_subaction_path_get( - &log, act->act_set->inst, getInfo->subactionPath, &act->sub_paths, - &sub_paths, "getInfo->subactionPath"); + &log, act->act_set->inst, getInfo->subactionPath, + &act->data->sub_paths, &sub_paths, "getInfo->subactionPath"); if (ret != XR_SUCCESS) { return ret; } - return oxr_action_get_vector1f(&log, sess, act->key, sub_paths, data); + return oxr_action_get_vector1f(&log, sess, act->act_key, sub_paths, + data); } XrResult @@ -430,19 +486,20 @@ oxr_xrGetActionStateVector2f(XrSession session, XR_TYPE_ACTION_STATE_GET_INFO); OXR_VERIFY_ACTION_NOT_NULL(&log, getInfo->action, act); - if (act->action_type != XR_ACTION_TYPE_VECTOR2F_INPUT) { + if (act->data->action_type != XR_ACTION_TYPE_VECTOR2F_INPUT) { return oxr_error(&log, XR_ERROR_ACTION_TYPE_MISMATCH, "Not created with float[2] type"); } ret = oxr_verify_subaction_path_get( - &log, act->act_set->inst, getInfo->subactionPath, &act->sub_paths, - &sub_paths, "getInfo->subactionPath"); + &log, act->act_set->inst, getInfo->subactionPath, + &act->data->sub_paths, &sub_paths, "getInfo->subactionPath"); if (ret != XR_SUCCESS) { return ret; } - return oxr_action_get_vector2f(&log, sess, act->key, sub_paths, data); + return oxr_action_get_vector2f(&log, sess, act->act_key, sub_paths, + data); } XrResult @@ -462,19 +519,19 @@ oxr_xrGetActionStatePose(XrSession session, XR_TYPE_ACTION_STATE_GET_INFO); OXR_VERIFY_ACTION_NOT_NULL(&log, getInfo->action, act); - if (act->action_type != XR_ACTION_TYPE_POSE_INPUT) { + if (act->data->action_type != XR_ACTION_TYPE_POSE_INPUT) { return oxr_error(&log, XR_ERROR_ACTION_TYPE_MISMATCH, "Not created with pose type"); } ret = oxr_verify_subaction_path_get( - &log, act->act_set->inst, getInfo->subactionPath, &act->sub_paths, - &sub_paths, "getInfo->subactionPath"); + &log, act->act_set->inst, getInfo->subactionPath, + &act->data->sub_paths, &sub_paths, "getInfo->subactionPath"); if (ret != XR_SUCCESS) { return ret; } - return oxr_action_get_pose(&log, sess, act->key, sub_paths, data); + return oxr_action_get_pose(&log, sess, act->act_key, sub_paths, data); } XrResult @@ -501,7 +558,7 @@ oxr_xrEnumerateBoundSourcesForAction( "been called on this session."); } - return oxr_action_enumerate_bound_sources(&log, sess, act->key, + return oxr_action_enumerate_bound_sources(&log, sess, act->act_key, sourceCapacityInput, sourceCountOutput, sources); } @@ -533,18 +590,18 @@ oxr_xrApplyHapticFeedback(XrSession session, ret = oxr_verify_subaction_path_get( &log, act->act_set->inst, hapticActionInfo->subactionPath, - &act->sub_paths, &sub_paths, "getInfo->subactionPath"); + &act->data->sub_paths, &sub_paths, "getInfo->subactionPath"); if (ret != XR_SUCCESS) { return ret; } - if (act->action_type != XR_ACTION_TYPE_VIBRATION_OUTPUT) { + if (act->data->action_type != XR_ACTION_TYPE_VIBRATION_OUTPUT) { return oxr_error(&log, XR_ERROR_ACTION_TYPE_MISMATCH, "Not created with output vibration type"); } - return oxr_action_apply_haptic_feedback(&log, sess, act->key, sub_paths, - hapticEvent); + return oxr_action_apply_haptic_feedback(&log, sess, act->act_key, + sub_paths, hapticEvent); } XrResult @@ -564,15 +621,16 @@ oxr_xrStopHapticFeedback(XrSession session, ret = oxr_verify_subaction_path_get( &log, act->act_set->inst, hapticActionInfo->subactionPath, - &act->sub_paths, &sub_paths, "getInfo->subactionPath"); + &act->data->sub_paths, &sub_paths, "getInfo->subactionPath"); if (ret != XR_SUCCESS) { return ret; } - if (act->action_type != XR_ACTION_TYPE_VIBRATION_OUTPUT) { + if (act->data->action_type != XR_ACTION_TYPE_VIBRATION_OUTPUT) { return oxr_error(&log, XR_ERROR_ACTION_TYPE_MISMATCH, "Not created with output vibration type"); } - return oxr_action_stop_haptic_feedback(&log, sess, act->key, sub_paths); + return oxr_action_stop_haptic_feedback(&log, sess, act->act_key, + sub_paths); } diff --git a/src/xrt/state_trackers/oxr/oxr_api_space.c b/src/xrt/state_trackers/oxr/oxr_api_space.c index 8cc574ecdff37fc0aa9fe7f42d375dc81cc0c682..09dfe7a89f9142bd4e557a39bd0ac1a756902c52 100644 --- a/src/xrt/state_trackers/oxr/oxr_api_space.c +++ b/src/xrt/state_trackers/oxr/oxr_api_space.c @@ -42,7 +42,7 @@ oxr_xrCreateActionSpace(XrSession session, struct oxr_space *spc; XrResult ret = - oxr_space_action_create(&log, sess, act->key, createInfo, &spc); + oxr_space_action_create(&log, sess, act->act_key, createInfo, &spc); if (ret != XR_SUCCESS) { return ret; } diff --git a/src/xrt/state_trackers/oxr/oxr_binding.c b/src/xrt/state_trackers/oxr/oxr_binding.c index 9cb63dbfd4c4922da4754d401b6a640ac2a13b7f..0d84a481583533906241c217fa884b25369d5f26 100644 --- a/src/xrt/state_trackers/oxr/oxr_binding.c +++ b/src/xrt/state_trackers/oxr/oxr_binding.c @@ -14,9 +14,11 @@ #include "oxr_objects.h" #include "oxr_logger.h" +#include "oxr_binding_data.h" + #include <stdio.h> -#include "oxr_binding_data.h" + static void setup_paths(struct oxr_logger *log, struct oxr_instance *inst, @@ -84,21 +86,6 @@ setup_outputs(struct oxr_logger *log, } } -static bool -is_valid_interaction_profile(struct oxr_instance *inst, XrPath path) -{ - return inst->path_cache.khr_simple_controller == path || - inst->path_cache.google_daydream_controller == path || - inst->path_cache.htc_vive_controller == path || - inst->path_cache.htc_vive_pro == path || - inst->path_cache.microsoft_motion_controller == path || - inst->path_cache.microsoft_xbox_controller == path || - inst->path_cache.oculus_go_controller == path || - inst->path_cache.oculus_touch_controller == path || - inst->path_cache.valve_index_controller == path || - inst->path_cache.mnd_ball_on_stick_controller == path; -} - static bool interaction_profile_find(struct oxr_logger *log, struct oxr_instance *inst, @@ -360,24 +347,12 @@ oxr_action_suggest_interaction_profile_bindings( const XrInteractionProfileSuggestedBinding *suggestedBindings) { struct oxr_interaction_profile *p = NULL; - XrPath path = suggestedBindings->interactionProfile; - const char *str; - size_t length; - - // Check if this profile is valid. - if (!is_valid_interaction_profile(inst, path)) { - oxr_path_get_string(log, inst, path, &str, &length); - - return oxr_error(log, XR_ERROR_PATH_UNSUPPORTED, - "(suggestedBindings->interactionProfile) " - "non-supported profile '%s'", - str); - } + // Path already validated. + XrPath path = suggestedBindings->interactionProfile; interaction_profile_find_or_create(log, inst, path, &p); // Valid path, but not used. - //! @todo Still needs to validate the paths. if (p == NULL) { return XR_SUCCESS; } @@ -385,7 +360,7 @@ oxr_action_suggest_interaction_profile_bindings( struct oxr_binding *bindings = p->bindings; size_t num_bindings = p->num_bindings; - //! @todo Validate keys **FIRST** then reset. + // Everything is now valid, reset the keys. reset_all_keys(bindings, num_bindings); for (size_t i = 0; i < suggestedBindings->countSuggestedBindings; i++) { @@ -394,13 +369,8 @@ oxr_action_suggest_interaction_profile_bindings( struct oxr_action *act = XRT_CAST_OXR_HANDLE_TO_PTR(struct oxr_action *, s->action); -#if 0 - oxr_path_get_string(log, inst, s->binding, &str, &length); - fprintf(stderr, "\t\t%s %i -> %s\n", act->name, act->key, str); -#endif - add_key_to_matching_bindings(bindings, num_bindings, s->binding, - act->key); + act->act_key); } return XR_SUCCESS; diff --git a/src/xrt/state_trackers/oxr/oxr_input.c b/src/xrt/state_trackers/oxr/oxr_input.c index 725c439a06c0a0cd0eaa4662884004c4c3c6bbd1..9942abee8f4d9c981cbfe3c42558a062df20dfd7 100644 --- a/src/xrt/state_trackers/oxr/oxr_input.c +++ b/src/xrt/state_trackers/oxr/oxr_input.c @@ -31,50 +31,162 @@ */ static void -oxr_session_get_source_set(struct oxr_session *sess, - XrActionSet actionSet, - struct oxr_source_set **src_set, - struct oxr_action_set **act_set); +oxr_session_get_action_set_attachment( + struct oxr_session *sess, + XrActionSet actionSet, + struct oxr_action_set_attachment **act_set_attached, + struct oxr_action_set **act_set); static void -oxr_session_get_source(struct oxr_session *sess, - uint32_t act_key, - struct oxr_source **out_src); +oxr_session_get_action_attachment( + struct oxr_session *sess, + uint32_t act_key, + struct oxr_action_attachment **out_act_attached); static void -oxr_source_cache_update(struct oxr_logger *log, +oxr_action_cache_update(struct oxr_logger *log, struct oxr_session *sess, - struct oxr_source_cache *cache, + struct oxr_action_cache *cache, int64_t time, bool select); +/*! + * @private @memberof oxr_action_attachment + */ static void -oxr_source_update(struct oxr_logger *log, - struct oxr_session *sess, - struct oxr_source *src, - int64_t time, - struct oxr_sub_paths sub_paths); +oxr_action_attachment_update(struct oxr_logger *log, + struct oxr_session *sess, + struct oxr_action_attachment *act_attached, + int64_t time, + struct oxr_sub_paths sub_paths); static void -oxr_source_bind_inputs(struct oxr_logger *log, +oxr_action_bind_inputs(struct oxr_logger *log, struct oxr_sink_logger *slog, struct oxr_session *sess, struct oxr_action *act, - struct oxr_source_cache *cache, + struct oxr_action_cache *cache, struct oxr_interaction_profile *profile, enum oxr_sub_action_path sub_path); +/* + * + * Action attachment functions + * + */ + +/*! + * De-initialize/de-allocate all dynamic members of @ref oxr_action_cache + * @private @memberof oxr_action_cache + */ +static void +oxr_action_cache_teardown(struct oxr_action_cache *cache) +{ + free(cache->inputs); + cache->inputs = NULL; + free(cache->outputs); + cache->outputs = NULL; +} + +/*! + * Tear down an action attachment struct. + * + * Does not deallocate the struct itself. + * + * @public @memberof oxr_action_attachment + */ +static void +oxr_action_attachment_teardown(struct oxr_action_attachment *act_attached) +{ + struct oxr_session *sess = act_attached->sess; + u_hashmap_int_erase(sess->act_attachments_by_key, + act_attached->act_key); + oxr_action_cache_teardown(&(act_attached->user)); + oxr_action_cache_teardown(&(act_attached->head)); + oxr_action_cache_teardown(&(act_attached->left)); + oxr_action_cache_teardown(&(act_attached->right)); + oxr_action_cache_teardown(&(act_attached->gamepad)); + // Unref this action's refcounted data + oxr_refcounted_unref(&act_attached->act_ref->base); +} + +/*! + * Set up an action attachment struct. + * + * @public @memberof oxr_action_attachment + */ static XrResult -oxr_source_destroy_cb(struct oxr_logger *log, struct oxr_handle_base *hb); +oxr_action_attachment_init(struct oxr_logger *log, + struct oxr_action_set_attachment *act_set_attached, + struct oxr_action_attachment *act_attached, + struct oxr_action *act) +{ + struct oxr_session *sess = act_set_attached->sess; + act_attached->sess = sess; + act_attached->act_set_attached = act_set_attached; + u_hashmap_int_insert(sess->act_attachments_by_key, act->act_key, + act_attached); + + // Reference this action's refcounted data + act_attached->act_ref = act->data; + oxr_refcounted_ref(&act_attached->act_ref->base); + + // Copy this for efficiency. + act_attached->act_key = act->act_key; + return XR_SUCCESS; +} + + +/* + * + * Action set attachment functions + * + */ +/*! + * @public @memberof oxr_action_set_attachment + */ static XrResult -oxr_source_create(struct oxr_logger *log, - struct oxr_source_set *src_set, - struct oxr_action *act, - struct oxr_interaction_profile *head, - struct oxr_interaction_profile *left, - struct oxr_interaction_profile *right, - struct oxr_interaction_profile *gamepad); +oxr_action_set_attachment_init( + struct oxr_logger *log, + struct oxr_session *sess, + struct oxr_action_set *act_set, + struct oxr_action_set_attachment *act_set_attached) +{ + act_set_attached->sess = sess; + + // Reference this action set's refcounted data + act_set_attached->act_set_ref = act_set->data; + oxr_refcounted_ref(&act_set_attached->act_set_ref->base); + + u_hashmap_int_insert(sess->act_sets_attachments_by_key, + act_set->act_set_key, act_set_attached); + + // Copy this for efficiency. + act_set_attached->act_set_key = act_set->act_set_key; + + return XR_SUCCESS; +} + +void +oxr_action_set_attachment_teardown( + struct oxr_action_set_attachment *act_set_attached) +{ + for (size_t i = 0; i < act_set_attached->num_action_attachments; ++i) { + oxr_action_attachment_teardown( + &(act_set_attached->act_attachments[i])); + } + free(act_set_attached->act_attachments); + act_set_attached->act_attachments = NULL; + act_set_attached->num_action_attachments = 0; + + struct oxr_session *sess = act_set_attached->sess; + u_hashmap_int_erase(sess->act_sets_attachments_by_key, + act_set_attached->act_set_key); + + // Unref this action set's refcounted data + oxr_refcounted_unref(&act_set_attached->act_set_ref->base); +} /* @@ -82,13 +194,26 @@ oxr_source_create(struct oxr_logger *log, * Action set functions * */ +static void +oxr_action_set_ref_destroy_cb(struct oxr_refcounted *orc) +{ + struct oxr_action_set_ref *act_set_ref = + (struct oxr_action_set_ref *)orc; + + u_hashset_destroy(&act_set_ref->actions.name_store); + u_hashset_destroy(&act_set_ref->actions.loc_store); + + free(act_set_ref); +} static XrResult oxr_action_set_destroy_cb(struct oxr_logger *log, struct oxr_handle_base *hb) { - //! @todo Move to oxr_objects.h struct oxr_action_set *act_set = (struct oxr_action_set *)hb; + oxr_refcounted_unref(&act_set->data->base); + act_set->data = NULL; + if (act_set->name_item != NULL) { u_hashset_erase_item(act_set->inst->action_sets.name_store, act_set->name_item); @@ -102,9 +227,6 @@ oxr_action_set_destroy_cb(struct oxr_logger *log, struct oxr_handle_base *hb) act_set->loc_item = NULL; } - u_hashset_destroy(&act_set->actions.name_store); - u_hashset_destroy(&act_set->actions.loc_store); - free(act_set); return XR_SUCCESS; @@ -120,30 +242,37 @@ oxr_action_set_create(struct oxr_logger *log, static uint32_t key_gen = 1; int h_ret; - //! @todo Implement more fully. struct oxr_action_set *act_set = NULL; OXR_ALLOCATE_HANDLE_OR_RETURN(log, act_set, OXR_XR_DEBUG_ACTIONSET, oxr_action_set_destroy_cb, &inst->handle); - h_ret = u_hashset_create(&act_set->actions.name_store); + struct oxr_action_set_ref *act_set_ref = + U_TYPED_CALLOC(struct oxr_action_set_ref); + act_set_ref->base.destroy = oxr_action_set_ref_destroy_cb; + oxr_refcounted_ref(&act_set_ref->base); + act_set->data = act_set_ref; + + act_set_ref->act_set_key = key_gen++; + act_set->act_set_key = act_set_ref->act_set_key; + + act_set->inst = inst; + + h_ret = u_hashset_create(&act_set_ref->actions.name_store); if (h_ret != 0) { oxr_handle_destroy(log, &act_set->handle); return oxr_error(log, XR_ERROR_RUNTIME_FAILURE, "Failed to create name_store hashset"); } - h_ret = u_hashset_create(&act_set->actions.loc_store); + h_ret = u_hashset_create(&act_set_ref->actions.loc_store); if (h_ret != 0) { oxr_handle_destroy(log, &act_set->handle); return oxr_error(log, XR_ERROR_RUNTIME_FAILURE, "Failed to create loc_store hashset"); } - act_set->key = key_gen++; - - act_set->inst = inst; - strncpy(act_set->name, createInfo->actionSetName, - sizeof(act_set->name)); + strncpy(act_set_ref->name, createInfo->actionSetName, + sizeof(act_set_ref->name)); u_hashset_create_and_insert_str_c(inst->action_sets.name_store, createInfo->actionSetName, @@ -164,20 +293,29 @@ oxr_action_set_create(struct oxr_logger *log, * */ +static void +oxr_action_ref_destroy_cb(struct oxr_refcounted *orc) +{ + struct oxr_action_ref *act_ref = (struct oxr_action_ref *)orc; + free(act_ref); +} + static XrResult oxr_action_destroy_cb(struct oxr_logger *log, struct oxr_handle_base *hb) { - //! @todo Move to oxr_objects.h struct oxr_action *act = (struct oxr_action *)hb; + oxr_refcounted_unref(&act->data->base); + act->data = NULL; + if (act->name_item != NULL) { - u_hashset_erase_item(act->act_set->actions.name_store, + u_hashset_erase_item(act->act_set->data->actions.name_store, act->name_item); free(act->name_item); act->name_item = NULL; } if (act->loc_item != NULL) { - u_hashset_erase_item(act->act_set->actions.loc_store, + u_hashset_erase_item(act->act_set->data->actions.loc_store, act->loc_item); free(act->loc_item); act->loc_item = NULL; @@ -200,24 +338,35 @@ oxr_action_create(struct oxr_logger *log, // Mod music for all! static uint32_t key_gen = 1; - oxr_classify_sub_action_paths(log, inst, - createInfo->countSubactionPaths, - createInfo->subactionPaths, &sub_paths); + if (!oxr_classify_sub_action_paths( + log, inst, createInfo->countSubactionPaths, + createInfo->subactionPaths, &sub_paths)) { + return XR_ERROR_PATH_UNSUPPORTED; + } struct oxr_action *act = NULL; OXR_ALLOCATE_HANDLE_OR_RETURN(log, act, OXR_XR_DEBUG_ACTION, oxr_action_destroy_cb, &act_set->handle); - act->key = key_gen++; + + + struct oxr_action_ref *act_ref = U_TYPED_CALLOC(struct oxr_action_ref); + act_ref->base.destroy = oxr_action_ref_destroy_cb; + oxr_refcounted_ref(&act_ref->base); + act->data = act_ref; + + act_ref->act_key = key_gen++; + act->act_key = act_ref->act_key; + act->act_set = act_set; - act->sub_paths = sub_paths; - act->action_type = createInfo->actionType; + act_ref->sub_paths = sub_paths; + act_ref->action_type = createInfo->actionType; - strncpy(act->name, createInfo->actionName, sizeof(act->name)); + strncpy(act_ref->name, createInfo->actionName, sizeof(act_ref->name)); - u_hashset_create_and_insert_str_c(act_set->actions.name_store, + u_hashset_create_and_insert_str_c(act_set->data->actions.name_store, createInfo->actionName, &act->name_item); - u_hashset_create_and_insert_str_c(act_set->actions.loc_store, + u_hashset_create_and_insert_str_c(act_set->data->actions.loc_store, createInfo->localizedActionName, &act->loc_item); @@ -229,11 +378,11 @@ oxr_action_create(struct oxr_logger *log, /* * - * "Exproted" helper functions. + * "Exported" helper functions. * */ -void +bool oxr_classify_sub_action_paths(struct oxr_logger *log, struct oxr_instance *inst, uint32_t num_subaction_paths, @@ -242,13 +391,14 @@ oxr_classify_sub_action_paths(struct oxr_logger *log, { const char *str = NULL; size_t length = 0; + bool ret = true; // Reset the sub_paths completely. U_ZERO(sub_paths); if (num_subaction_paths == 0) { sub_paths->any = true; - return; + return ret; } for (uint32_t i = 0; i < num_subaction_paths; i++) { @@ -266,50 +416,58 @@ oxr_classify_sub_action_paths(struct oxr_logger *log, sub_paths->right = true; } else if (path == inst->path_cache.gamepad) { sub_paths->gamepad = true; + } else if (path == inst->path_cache.treadmill) { + sub_paths->treadmill = true; } else { oxr_path_get_string(log, inst, path, &str, &length); oxr_warn(log, " unrecognized sub action path '%s'", str); + ret = false; } } + return ret; } XrResult -oxr_source_get_pose_input(struct oxr_logger *log, +oxr_action_get_pose_input(struct oxr_logger *log, struct oxr_session *sess, uint32_t act_key, const struct oxr_sub_paths *sub_paths, - struct oxr_source_input **out_input) + struct oxr_action_input **out_input) { - struct oxr_source *src = NULL; + struct oxr_action_attachment *act_attached = NULL; - oxr_session_get_source(sess, act_key, &src); + oxr_session_get_action_attachment(sess, act_key, &act_attached); - if (src == NULL) { + if (act_attached == NULL) { return XR_SUCCESS; } // Priority of inputs. - if (src->head.current.active && (sub_paths->head || sub_paths->any)) { - *out_input = src->head.inputs; + if (act_attached->head.current.active && + (sub_paths->head || sub_paths->any)) { + *out_input = act_attached->head.inputs; return XR_SUCCESS; } - if (src->left.current.active && (sub_paths->left || sub_paths->any)) { - *out_input = src->left.inputs; + if (act_attached->left.current.active && + (sub_paths->left || sub_paths->any)) { + *out_input = act_attached->left.inputs; return XR_SUCCESS; } - if (src->right.current.active && (sub_paths->right || sub_paths->any)) { - *out_input = src->right.inputs; + if (act_attached->right.current.active && + (sub_paths->right || sub_paths->any)) { + *out_input = act_attached->right.inputs; return XR_SUCCESS; } - if (src->gamepad.current.active && + if (act_attached->gamepad.current.active && (sub_paths->gamepad || sub_paths->any)) { - *out_input = src->gamepad.inputs; + *out_input = act_attached->gamepad.inputs; return XR_SUCCESS; } - if (src->user.current.active && (sub_paths->user || sub_paths->any)) { - *out_input = src->user.inputs; + if (act_attached->user.current.active && + (sub_paths->user || sub_paths->any)) { + *out_input = act_attached->user.inputs; return XR_SUCCESS; } @@ -326,7 +484,7 @@ oxr_source_get_pose_input(struct oxr_logger *log, static bool do_inputs(struct oxr_binding *bind, struct xrt_device *xdev, - struct oxr_source_input inputs[16], + struct oxr_action_input inputs[16], uint32_t *num_inputs) { struct xrt_input *input = NULL; @@ -347,7 +505,7 @@ do_inputs(struct oxr_binding *bind, static bool do_outputs(struct oxr_binding *bind, struct xrt_device *xdev, - struct oxr_source_output outputs[16], + struct oxr_action_output outputs[16], uint32_t *num_outputs) { struct xrt_output *output = NULL; @@ -365,18 +523,22 @@ do_outputs(struct oxr_binding *bind, return found; } +/*! + * Delegate to @ref do_outputs or @ref do_inputs depending on whether the action + * is output or input. + */ static bool do_io_bindings(struct oxr_binding *b, struct oxr_action *act, struct xrt_device *xdev, - struct oxr_source_input inputs[16], + struct oxr_action_input inputs[16], uint32_t *num_inputs, - struct oxr_source_output outputs[16], + struct oxr_action_output outputs[16], uint32_t *num_outputs) { bool found = false; - if (act->action_type == XR_ACTION_TYPE_VIBRATION_OUTPUT) { + if (act->data->action_type == XR_ACTION_TYPE_VIBRATION_OUTPUT) { found |= do_outputs(b, xdev, outputs, num_outputs); } else { found |= do_inputs(b, xdev, inputs, num_inputs); @@ -396,16 +558,16 @@ ends_with(const char *str, const char *suffix) } static void -oxr_source_cache_determine_redirect(struct oxr_logger *log, +oxr_action_cache_determine_redirect(struct oxr_logger *log, struct oxr_session *sess, struct oxr_action *act, - struct oxr_source_cache *cache, + struct oxr_action_cache *cache, XrPath bound_path) { cache->redirect = INPUT_REDIRECT_DEFAULT; - struct oxr_source_input *input = &cache->inputs[0]; + struct oxr_action_input *input = &cache->inputs[0]; if (input == NULL) return; @@ -418,7 +580,7 @@ oxr_source_cache_determine_redirect(struct oxr_logger *log, // trackpad/thumbstick data is kept in vec2f values. // When a float action binds to ../trackpad/x or ../thumbstick/y, store // the information which vec2f component to read. - if (act->action_type == XR_ACTION_TYPE_FLOAT_INPUT && + if (act->data->action_type == XR_ACTION_TYPE_FLOAT_INPUT && t == XRT_INPUT_TYPE_VEC2_MINUS_ONE_TO_ONE) { if (ends_with(str, "/x")) { cache->redirect = INPUT_REDIRECT_VEC2_X_TO_VEC1; @@ -428,7 +590,7 @@ oxr_source_cache_determine_redirect(struct oxr_logger *log, oxr_log(log, "No rule to get float from vec2f for action " "%s, binding %s\n", - act->name, str); + act->data->name, str); } } } @@ -438,7 +600,7 @@ get_matched_xrpath(struct oxr_binding *b, struct oxr_action *act) { XrPath preferred_path = XR_NULL_PATH; for (uint32_t i = 0; i < b->num_keys; i++) { - if (b->keys[i] == act->key) { + if (b->keys[i] == act->act_key) { uint32_t preferred_path_index = XR_NULL_PATH; preferred_path_index = b->preferred_binding_path_index[i]; @@ -456,9 +618,9 @@ get_binding(struct oxr_logger *log, struct oxr_action *act, struct oxr_interaction_profile *profile, enum oxr_sub_action_path sub_path, - struct oxr_source_input inputs[16], + struct oxr_action_input inputs[16], uint32_t *num_inputs, - struct oxr_source_output outputs[16], + struct oxr_action_output outputs[16], uint32_t *num_outputs, XrPath *bound_path) { @@ -512,7 +674,7 @@ get_binding(struct oxr_logger *log, oxr_slog(slog, "\t\tProfile: %s\n", profile_str); size_t num = 0; - oxr_binding_find_bindings_from_key(log, profile, act->key, bindings, + oxr_binding_find_bindings_from_key(log, profile, act->act_key, bindings, &num); if (num == 0) { oxr_slog(slog, "\t\tNo bindings\n"); @@ -547,120 +709,56 @@ get_binding(struct oxr_logger *log, } -/* - * - * Source set functions - * - */ - -static XrResult -oxr_source_set_destroy_cb(struct oxr_logger *log, struct oxr_handle_base *hb) -{ - //! @todo Move to oxr_objects.h - struct oxr_source_set *src_set = (struct oxr_source_set *)hb; - - free(src_set); - - return XR_SUCCESS; -} - -static XrResult -oxr_source_set_create(struct oxr_logger *log, - struct oxr_session *sess, - struct oxr_action_set *act_set, - struct oxr_source_set **out_src_set) -{ - struct oxr_source_set *src_set = NULL; - OXR_ALLOCATE_HANDLE_OR_RETURN(log, src_set, OXR_XR_DEBUG_SOURCESET, - oxr_source_set_destroy_cb, &sess->handle); - - src_set->sess = sess; - u_hashmap_int_insert(sess->act_sets, act_set->key, src_set); - - src_set->next = sess->src_set_list; - sess->src_set_list = src_set; - - *out_src_set = src_set; - return XR_SUCCESS; -} - - -/* - * - * Source functions - * +/*! + * @public @memberof oxr_action_attachment */ - static XrResult -oxr_source_destroy_cb(struct oxr_logger *log, struct oxr_handle_base *hb) +oxr_action_attachment_bind(struct oxr_logger *log, + struct oxr_action_attachment *act_attached, + struct oxr_action *act, + struct oxr_interaction_profile *head, + struct oxr_interaction_profile *left, + struct oxr_interaction_profile *right, + struct oxr_interaction_profile *gamepad) { - //! @todo Move to oxr_objects.h - struct oxr_source *src = (struct oxr_source *)hb; - - free(src->user.inputs); - free(src->user.outputs); - free(src->head.inputs); - free(src->head.outputs); - free(src->left.inputs); - free(src->left.outputs); - free(src->right.inputs); - free(src->right.outputs); - free(src->gamepad.inputs); - free(src->gamepad.outputs); - free(src); - - return XR_SUCCESS; -} - -static XrResult -oxr_source_create(struct oxr_logger *log, - struct oxr_source_set *src_set, - struct oxr_action *act, - struct oxr_interaction_profile *head, - struct oxr_interaction_profile *left, - struct oxr_interaction_profile *right, - struct oxr_interaction_profile *gamepad) -{ - struct oxr_session *sess = src_set->sess; - struct oxr_source *src = NULL; struct oxr_sink_logger slog = {0}; - OXR_ALLOCATE_HANDLE_OR_RETURN(log, src, OXR_XR_DEBUG_SOURCE, - oxr_source_destroy_cb, &src_set->handle); - - u_hashmap_int_insert(src_set->sess->sources, act->key, src); - - // Need to copy this. - src->action_type = act->action_type; + struct oxr_action_ref *act_ref = act->data; + struct oxr_session *sess = act_attached->sess; // Start logging into a single buffer. - oxr_slog(&slog, ": Binding %s/%s\n", act->act_set->name, act->name); + oxr_slog(&slog, ": Binding %s/%s\n", act->act_set->data->name, + act_ref->name); - if (act->sub_paths.user || act->sub_paths.any) { + if (act_ref->sub_paths.user || act_ref->sub_paths.any) { #if 0 - oxr_source_bind_inputs(log, slog, sess, act, &src->user, user, + oxr_action_bind_inputs(log, slog, sess, act, &act_attached->user, user, OXR_SUB_ACTION_PATH_USER); #endif } - if (act->sub_paths.head || act->sub_paths.any) { - oxr_source_bind_inputs(log, &slog, sess, act, &src->head, head, + if (act_ref->sub_paths.head || act_ref->sub_paths.any) { + oxr_action_bind_inputs(log, &slog, sess, act, + &act_attached->head, head, OXR_SUB_ACTION_PATH_HEAD); } - if (act->sub_paths.left || act->sub_paths.any) { - oxr_source_bind_inputs(log, &slog, sess, act, &src->left, left, + if (act_ref->sub_paths.left || act_ref->sub_paths.any) { + oxr_action_bind_inputs(log, &slog, sess, act, + &act_attached->left, left, OXR_SUB_ACTION_PATH_LEFT); } - if (act->sub_paths.right || act->sub_paths.any) { - oxr_source_bind_inputs(log, &slog, sess, act, &src->right, - right, OXR_SUB_ACTION_PATH_RIGHT); + if (act_ref->sub_paths.right || act_ref->sub_paths.any) { + oxr_action_bind_inputs(log, &slog, sess, act, + &act_attached->right, right, + OXR_SUB_ACTION_PATH_RIGHT); } - if (act->sub_paths.gamepad || act->sub_paths.any) { - oxr_source_bind_inputs(log, &slog, sess, act, &src->gamepad, - gamepad, OXR_SUB_ACTION_PATH_GAMEPAD); + if (act_ref->sub_paths.gamepad || act_ref->sub_paths.any) { + oxr_action_bind_inputs(log, &slog, sess, act, + &act_attached->gamepad, gamepad, + OXR_SUB_ACTION_PATH_GAMEPAD); } oxr_slog(&slog, "\tDone"); @@ -676,9 +774,9 @@ oxr_source_create(struct oxr_logger *log, } static void -oxr_source_cache_stop_output(struct oxr_logger *log, +oxr_action_cache_stop_output(struct oxr_logger *log, struct oxr_session *sess, - struct oxr_source_cache *cache) + struct oxr_action_cache *cache) { // Set this as stopped. cache->stop_output_time = 0; @@ -686,7 +784,7 @@ oxr_source_cache_stop_output(struct oxr_logger *log, union xrt_output_value value = {0}; for (uint32_t i = 0; i < cache->num_outputs; i++) { - struct oxr_source_output *output = &cache->outputs[i]; + struct oxr_action_output *output = &cache->outputs[i]; struct xrt_device *xdev = output->xdev; xrt_device_set_output(xdev, output->name, &value); @@ -694,17 +792,17 @@ oxr_source_cache_stop_output(struct oxr_logger *log, } static void -oxr_source_cache_update(struct oxr_logger *log, +oxr_action_cache_update(struct oxr_logger *log, struct oxr_session *sess, - struct oxr_source_cache *cache, + struct oxr_action_cache *cache, int64_t time, bool selected) { - struct oxr_source_state last = cache->current; + struct oxr_action_state last = cache->current; if (!selected) { if (cache->stop_output_time > 0) { - oxr_source_cache_stop_output(log, sess, cache); + oxr_action_cache_stop_output(log, sess, cache); } U_ZERO(&cache->current); return; @@ -713,7 +811,7 @@ oxr_source_cache_update(struct oxr_logger *log, if (cache->num_outputs > 0) { cache->current.active = true; if (cache->stop_output_time < time) { - oxr_source_cache_stop_output(log, sess, cache); + oxr_action_cache_stop_output(log, sess, cache); } } @@ -744,31 +842,30 @@ oxr_source_cache_update(struct oxr_logger *log, switch (XRT_GET_INPUT_TYPE(input->name)) { case XRT_INPUT_TYPE_VEC1_ZERO_TO_ONE: case XRT_INPUT_TYPE_VEC1_MINUS_ONE_TO_ONE: { - changed = (input->value.vec1.x != last.vec1.x); - cache->current.vec1.x = input->value.vec1.x; + changed = (input->value.vec1.x != last.value.vec1.x); + cache->current.value = input->value; break; } case XRT_INPUT_TYPE_VEC2_MINUS_ONE_TO_ONE: { - changed = (input->value.vec2.x != last.vec2.x) || - (input->value.vec2.y != last.vec2.y); - cache->current.vec2.x = input->value.vec2.x; - cache->current.vec2.y = input->value.vec2.y; + changed = (input->value.vec2.x != last.value.vec2.x) || + (input->value.vec2.y != last.value.vec2.y); + cache->current.value = input->value; break; } #if 0 case XRT_INPUT_TYPE_VEC3_MINUS_ONE_TO_ONE: { - changed = (input->value.vec3.x != last.vec3.x) || - (input->value.vec3.y != last.vec3.y) || - (input->value.vec3.z != last.vec3.z); - cache->current.vec3.x = input->value.vec3.x; - cache->current.vec3.y = input->value.vec3.y; - cache->current.vec3.z = input->value.vec3.z; + changed = (input->value.vec3.x != last.value.vec3.x) || + (input->value.vec3.y != last.value.vec3.y) || + (input->value.vec3.z != last.value.vec3.z); + cache->current.value.vec3.x = input->value.vec3.x; + cache->current.value.vec3.y = input->value.vec3.y; + cache->current.value.vec3.z = input->value.vec3.z; break; } #endif case XRT_INPUT_TYPE_BOOLEAN: { - changed = (input->value.boolean != last.boolean); - cache->current.boolean = input->value.boolean; + changed = (input->value.boolean != last.value.boolean); + cache->current.value.boolean = input->value.boolean; break; } case XRT_INPUT_TYPE_POSE: return; @@ -791,42 +888,42 @@ oxr_source_cache_update(struct oxr_logger *log, } #define BOOL_CHECK(NAME) \ - if (src->NAME.current.active) { \ + if (act_attached->NAME.current.active) { \ active |= true; \ - value |= src->NAME.current.boolean; \ - timestamp = src->NAME.current.timestamp; \ + value |= act_attached->NAME.current.value.boolean; \ + timestamp = act_attached->NAME.current.timestamp; \ } #define VEC1_CHECK(NAME) \ - if (src->NAME.current.active) { \ + if (act_attached->NAME.current.active) { \ active |= true; \ - if (value < src->NAME.current.vec1.x) { \ - value = src->NAME.current.vec1.x; \ - timestamp = src->NAME.current.timestamp; \ + if (value < act_attached->NAME.current.value.vec1.x) { \ + value = act_attached->NAME.current.value.vec1.x; \ + timestamp = act_attached->NAME.current.timestamp; \ } \ } #define VEC2_CHECK(NAME) \ - if (src->NAME.current.active) { \ + if (act_attached->NAME.current.active) { \ active |= true; \ - float curr_x = src->NAME.current.vec2.x; \ - float curr_y = src->NAME.current.vec2.y; \ + float curr_x = act_attached->NAME.current.value.vec2.x; \ + float curr_y = act_attached->NAME.current.value.vec2.y; \ float curr_d = curr_x * curr_x + curr_y * curr_y; \ if (distance < curr_d) { \ x = curr_x; \ y = curr_y; \ distance = curr_d; \ - timestamp = src->NAME.current.timestamp; \ + timestamp = act_attached->NAME.current.timestamp; \ } \ } static void -oxr_source_update(struct oxr_logger *log, - struct oxr_session *sess, - struct oxr_source *src, - int64_t time, - struct oxr_sub_paths sub_paths) +oxr_action_attachment_update(struct oxr_logger *log, + struct oxr_session *sess, + struct oxr_action_attachment *act_attached, + int64_t time, + struct oxr_sub_paths sub_paths) { // This really shouldn't be happening. - if (src == NULL) { + if (act_attached == NULL) { return; } @@ -839,26 +936,26 @@ oxr_source_update(struct oxr_logger *log, bool select_gamepad = sub_paths.gamepad || sub_paths.any; // clang-format off - oxr_source_cache_update(log, sess, &src->head, time, select_head); - oxr_source_cache_update(log, sess, &src->left, time, select_left); - oxr_source_cache_update(log, sess, &src->right, time, select_right); - oxr_source_cache_update(log, sess, &src->gamepad, time, select_gamepad); + oxr_action_cache_update(log, sess, &act_attached->head, time, select_head); + oxr_action_cache_update(log, sess, &act_attached->left, time, select_left); + oxr_action_cache_update(log, sess, &act_attached->right, time, select_right); + oxr_action_cache_update(log, sess, &act_attached->gamepad, time, select_gamepad); // clang-format on if (!select_any) { - U_ZERO(&src->any_state); + U_ZERO(&act_attached->any_state); return; } /* * Any state. */ - struct oxr_source_state last = src->any_state; + struct oxr_action_state last = act_attached->any_state; bool active = false; bool changed = false; XrTime timestamp = 0; - switch (src->action_type) { + switch (act_attached->act_ref->action_type) { case XR_ACTION_TYPE_BOOLEAN_INPUT: { bool value = false; BOOL_CHECK(user); @@ -867,8 +964,8 @@ oxr_source_update(struct oxr_logger *log, BOOL_CHECK(right); BOOL_CHECK(gamepad); - changed = last.boolean != value; - src->any_state.boolean = value; + changed = (last.value.boolean != value); + act_attached->any_state.value.boolean = value; break; } case XR_ACTION_TYPE_FLOAT_INPUT: { @@ -879,8 +976,8 @@ oxr_source_update(struct oxr_logger *log, VEC1_CHECK(right); VEC1_CHECK(gamepad); - changed = last.vec1.x != value; - src->any_state.vec1.x = value; + changed = last.value.vec1.x != value; + act_attached->any_state.value.vec1.x = value; break; } case XR_ACTION_TYPE_VECTOR2F_INPUT: { @@ -893,9 +990,9 @@ oxr_source_update(struct oxr_logger *log, VEC2_CHECK(right); VEC2_CHECK(gamepad); - changed = last.vec2.x != x || last.vec2.y != y; - src->any_state.vec2.x = x; - src->any_state.vec2.y = y; + changed = (last.value.vec2.x != x) || (last.value.vec2.y != y); + act_attached->any_state.value.vec2.x = x; + act_attached->any_state.value.vec2.y = y; break; } default: @@ -907,34 +1004,34 @@ oxr_source_update(struct oxr_logger *log, } if (!active) { - U_ZERO(&src->any_state); + U_ZERO(&act_attached->any_state); } else if (last.active && changed) { - src->any_state.timestamp = timestamp; - src->any_state.changed = true; - src->any_state.active = true; + act_attached->any_state.timestamp = timestamp; + act_attached->any_state.changed = true; + act_attached->any_state.active = true; } else if (last.active) { - src->any_state.timestamp = last.timestamp; - src->any_state.changed = false; - src->any_state.active = true; + act_attached->any_state.timestamp = last.timestamp; + act_attached->any_state.changed = false; + act_attached->any_state.active = true; } else { - src->any_state.timestamp = timestamp; - src->any_state.changed = false; - src->any_state.active = true; + act_attached->any_state.timestamp = timestamp; + act_attached->any_state.changed = false; + act_attached->any_state.active = true; } } static void -oxr_source_bind_inputs(struct oxr_logger *log, +oxr_action_bind_inputs(struct oxr_logger *log, struct oxr_sink_logger *slog, struct oxr_session *sess, struct oxr_action *act, - struct oxr_source_cache *cache, + struct oxr_action_cache *cache, struct oxr_interaction_profile *profile, enum oxr_sub_action_path sub_path) { - struct oxr_source_input inputs[16] = {0}; + struct oxr_action_input inputs[16] = {0}; uint32_t num_inputs = 0; - struct oxr_source_output outputs[16] = {0}; + struct oxr_action_output outputs[16] = {0}; uint32_t num_outputs = 0; //! @todo Should this be asserted to be none-null? @@ -947,7 +1044,7 @@ oxr_source_bind_inputs(struct oxr_logger *log, if (num_inputs > 0) { cache->current.active = true; cache->inputs = - U_TYPED_ARRAY_CALLOC(struct oxr_source_input, num_inputs); + U_TYPED_ARRAY_CALLOC(struct oxr_action_input, num_inputs); for (uint32_t i = 0; i < num_inputs; i++) { cache->inputs[i] = inputs[i]; } @@ -957,14 +1054,14 @@ oxr_source_bind_inputs(struct oxr_logger *log, if (num_outputs > 0) { cache->current.active = true; cache->outputs = - U_TYPED_ARRAY_CALLOC(struct oxr_source_output, num_outputs); + U_TYPED_ARRAY_CALLOC(struct oxr_action_output, num_outputs); for (uint32_t i = 0; i < num_outputs; i++) { cache->outputs[i] = outputs[i]; } cache->num_outputs = num_outputs; } - oxr_source_cache_determine_redirect(log, sess, act, cache, bound_path); + oxr_action_cache_determine_redirect(log, sess, act, cache, bound_path); } @@ -974,35 +1071,63 @@ oxr_source_bind_inputs(struct oxr_logger *log, * */ +/*! + * Given an Action Set handle, return the @ref oxr_action_set and the associated + * @ref oxr_action_set_attachment in the given Session. + * + * @private @memberof oxr_session + */ static void -oxr_session_get_source_set(struct oxr_session *sess, - XrActionSet actionSet, - struct oxr_source_set **src_set, - struct oxr_action_set **act_set) +oxr_session_get_action_set_attachment( + struct oxr_session *sess, + XrActionSet actionSet, + struct oxr_action_set_attachment **act_set_attached, + struct oxr_action_set **act_set) { void *ptr = NULL; *act_set = XRT_CAST_OXR_HANDLE_TO_PTR(struct oxr_action_set *, actionSet); - int ret = u_hashmap_int_find(sess->act_sets, (*act_set)->key, &ptr); + int ret = u_hashmap_int_find(sess->act_sets_attachments_by_key, + (*act_set)->act_set_key, &ptr); if (ret == 0) { - *src_set = (struct oxr_source_set *)ptr; + *act_set_attached = (struct oxr_action_set_attachment *)ptr; } } +/*! + * Given an action act_key, look up the @ref oxr_action_attachment of the + * associated action in the given Session. + * + * @private @memberof oxr_session + */ static void -oxr_session_get_source(struct oxr_session *sess, - uint32_t act_key, - struct oxr_source **out_src) +oxr_session_get_action_attachment( + struct oxr_session *sess, + uint32_t act_key, + struct oxr_action_attachment **out_act_attached) { void *ptr = NULL; - int ret = u_hashmap_int_find(sess->sources, act_key, &ptr); + int ret = + u_hashmap_int_find(sess->act_attachments_by_key, act_key, &ptr); if (ret == 0) { - *out_src = (struct oxr_source *)ptr; + *out_act_attached = (struct oxr_action_attachment *)ptr; } } +static inline size_t +oxr_handle_base_get_num_children(struct oxr_handle_base *hb) +{ + size_t ret = 0; + for (uint32_t i = 0; i < XRT_MAX_HANDLE_CHILDREN; ++i) { + if (hb->children[i] != NULL) { + ++ret; + } + } + return ret; +} + XrResult oxr_session_attach_action_sets(struct oxr_logger *log, struct oxr_session *sess, @@ -1012,30 +1137,61 @@ oxr_session_attach_action_sets(struct oxr_logger *log, struct oxr_interaction_profile *head = NULL; struct oxr_interaction_profile *left = NULL; struct oxr_interaction_profile *right = NULL; - struct oxr_action_set *act_set = NULL; - struct oxr_source_set *src_set = NULL; - struct oxr_action *act = NULL; oxr_find_profile_for_device(log, inst, sess->sys->head, &head); oxr_find_profile_for_device(log, inst, sess->sys->left, &left); oxr_find_profile_for_device(log, inst, sess->sys->right, &right); + //! @todo add other subaction paths here - // Has any of the bound action sets been updated. - for (uint32_t i = 0; i < bindInfo->countActionSets; i++) { - act_set = XRT_CAST_OXR_HANDLE_TO_PTR(struct oxr_action_set *, - bindInfo->actionSets[i]); - act_set->attached = true; + // Before allocating, make sure nothing has been attached yet. - oxr_source_set_create(log, sess, act_set, &src_set); + for (uint32_t i = 0; i < bindInfo->countActionSets; i++) { + struct oxr_action_set *act_set = XRT_CAST_OXR_HANDLE_TO_PTR( + struct oxr_action_set *, bindInfo->actionSets[i]); + if (act_set->data->attached) { + return XR_ERROR_ACTIONSETS_ALREADY_ATTACHED; + } + } + // Allocate room for list. + sess->num_action_set_attachments = bindInfo->countActionSets; + sess->act_set_attachments = U_TYPED_ARRAY_CALLOC( + struct oxr_action_set_attachment, sess->num_action_set_attachments); + + // Set up the per-session data for these action sets. + for (uint32_t i = 0; i < sess->num_action_set_attachments; i++) { + struct oxr_action_set *act_set = XRT_CAST_OXR_HANDLE_TO_PTR( + struct oxr_action_set *, bindInfo->actionSets[i]); + struct oxr_action_set_ref *act_set_ref = act_set->data; + act_set_ref->attached = true; + struct oxr_action_set_attachment *act_set_attached = + &sess->act_set_attachments[i]; + oxr_action_set_attachment_init(log, sess, act_set, + act_set_attached); + + // Allocate the action attachments for this set. + act_set_attached->num_action_attachments = + oxr_handle_base_get_num_children(&act_set->handle); + act_set_attached->act_attachments = U_TYPED_ARRAY_CALLOC( + struct oxr_action_attachment, + act_set_attached->num_action_attachments); + + // Set up the per-session data for the actions. + uint32_t child_index = 0; for (uint32_t k = 0; k < XRT_MAX_HANDLE_CHILDREN; k++) { - act = (struct oxr_action *)act_set->handle.children[k]; + struct oxr_action *act = + (struct oxr_action *)act_set->handle.children[k]; if (act == NULL) { continue; } - oxr_source_create(log, src_set, act, head, left, right, - NULL); + struct oxr_action_attachment *act_attached = + &act_set_attached->act_attachments[child_index]; + oxr_action_attachment_init(log, act_set_attached, + act_attached, act); + oxr_action_attachment_bind(log, act_attached, act, head, + left, right, NULL); + ++child_index; } } @@ -1061,18 +1217,18 @@ oxr_action_sync_data(struct oxr_logger *log, const XrActiveActionSet *actionSets) { struct oxr_action_set *act_set = NULL; - struct oxr_source_set *src_set = NULL; + struct oxr_action_set_attachment *act_set_attached = NULL; // Check that all action sets has been attached. for (uint32_t i = 0; i < countActionSets; i++) { - oxr_session_get_source_set(sess, actionSets[i].actionSet, - &src_set, &act_set); - if (src_set == NULL) { + oxr_session_get_action_set_attachment( + sess, actionSets[i].actionSet, &act_set_attached, &act_set); + if (act_set_attached == NULL) { return oxr_error( log, XR_ERROR_ACTIONSET_NOT_ATTACHED, "(actionSets[%i].actionSet) action set '%s' has " "not been attached to this session", - i, act_set != NULL ? act_set->name : "NULL"); + i, act_set != NULL ? act_set->data->name : "NULL"); } } @@ -1084,55 +1240,54 @@ oxr_action_sync_data(struct oxr_logger *log, oxr_xdev_update(sess->sys->xdevs[i]); } - // Reset all requested source sets. - src_set = sess->src_set_list; - while (src_set != NULL) { - U_ZERO(&src_set->requested_sub_paths); - - // Grab the next one. - src_set = src_set->next; + // Reset all action set attachments. + for (size_t i = 0; i < sess->num_action_set_attachments; ++i) { + act_set_attached = &sess->act_set_attachments[i]; + U_ZERO(&act_set_attached->requested_sub_paths); } - // Go over all action sets and update them. + // Go over all requested action sets and update their attachment. + //! @todo can be listed more than once with different paths! for (uint32_t i = 0; i < countActionSets; i++) { struct oxr_sub_paths sub_paths; - oxr_session_get_source_set(sess, actionSets[i].actionSet, - &src_set, &act_set); - assert(src_set != NULL); - - oxr_classify_sub_action_paths(log, sess->sys->inst, 1, - &actionSets[i].subactionPath, - &sub_paths); + oxr_session_get_action_set_attachment( + sess, actionSets[i].actionSet, &act_set_attached, &act_set); + assert(act_set_attached != NULL); + + if (!oxr_classify_sub_action_paths(log, sess->sys->inst, 1, + &actionSets[i].subactionPath, + &sub_paths)) { + return XR_ERROR_PATH_UNSUPPORTED; + } - src_set->requested_sub_paths.any |= sub_paths.any; - src_set->requested_sub_paths.user |= sub_paths.user; - src_set->requested_sub_paths.head |= sub_paths.head; - src_set->requested_sub_paths.left |= sub_paths.left; - src_set->requested_sub_paths.right |= sub_paths.right; - src_set->requested_sub_paths.gamepad |= sub_paths.gamepad; + act_set_attached->requested_sub_paths.any |= sub_paths.any; + act_set_attached->requested_sub_paths.user |= sub_paths.user; + act_set_attached->requested_sub_paths.head |= sub_paths.head; + act_set_attached->requested_sub_paths.left |= sub_paths.left; + act_set_attached->requested_sub_paths.right |= sub_paths.right; + act_set_attached->requested_sub_paths.gamepad |= + sub_paths.gamepad; } - // Reset all source sets. - src_set = sess->src_set_list; - while (src_set != NULL) { - struct oxr_sub_paths sub_paths = src_set->requested_sub_paths; + // Now, update all action attachments + for (size_t i = 0; i < sess->num_action_set_attachments; ++i) { + act_set_attached = &sess->act_set_attachments[i]; + struct oxr_sub_paths sub_paths = + act_set_attached->requested_sub_paths; - for (uint32_t k = 0; k < XRT_MAX_HANDLE_CHILDREN; k++) { - // This assumes that all children of a - // source set are actions. - struct oxr_source *src = - (struct oxr_source *)src_set->handle.children[k]; + for (uint32_t k = 0; + k < act_set_attached->num_action_attachments; k++) { + struct oxr_action_attachment *act_attached = + &act_set_attached->act_attachments[k]; - if (src == NULL) { + if (act_attached == NULL) { continue; } - oxr_source_update(log, sess, src, now, sub_paths); + oxr_action_attachment_update(log, sess, act_attached, + now, sub_paths); } - - // Grab the next one. - src_set = src_set->next; } @@ -1147,30 +1302,30 @@ oxr_action_sync_data(struct oxr_logger *log, */ static void -get_state_from_state_bool(struct oxr_source_state *state, +get_state_from_state_bool(struct oxr_action_state *state, XrActionStateBoolean *data, enum xrt_source_value_redirect redirect) { - data->currentState = state->boolean; + data->currentState = state->value.boolean; data->lastChangeTime = state->timestamp; data->changedSinceLastSync = state->changed; data->isActive = XR_TRUE; } static void -get_state_from_state_vec1(struct oxr_source_state *state, +get_state_from_state_vec1(struct oxr_action_state *state, XrActionStateFloat *data, enum xrt_source_value_redirect redirect) { switch (redirect) { case INPUT_REDIRECT_VEC2_X_TO_VEC1: - data->currentState = state->vec2.x; + data->currentState = state->value.vec2.x; break; case INPUT_REDIRECT_VEC2_Y_TO_VEC1: - data->currentState = state->vec2.y; + data->currentState = state->value.vec2.y; break; case INPUT_REDIRECT_DEFAULT: - default: data->currentState = state->vec1.x; break; + default: data->currentState = state->value.vec1.x; break; } data->lastChangeTime = state->timestamp; @@ -1179,55 +1334,57 @@ get_state_from_state_vec1(struct oxr_source_state *state, } static void -get_state_from_state_vec2(struct oxr_source_state *state, +get_state_from_state_vec2(struct oxr_action_state *state, XrActionStateVector2f *data, enum xrt_source_value_redirect redirect) { - data->currentState.x = state->vec2.x; - data->currentState.y = state->vec2.y; + data->currentState.x = state->value.vec2.x; + data->currentState.y = state->value.vec2.y; data->lastChangeTime = state->timestamp; data->changedSinceLastSync = state->changed; data->isActive = XR_TRUE; } #define OXR_ACTION_GET_FILLER(TYPE) \ - if (sub_paths.any && src->any_state.active) { \ - get_state_from_state_##TYPE(&src->any_state, data, \ + if (sub_paths.any && act_attached->any_state.active) { \ + get_state_from_state_##TYPE(&act_attached->any_state, data, \ INPUT_REDIRECT_DEFAULT); \ } \ - if (sub_paths.user && src->user.current.active) { \ - get_state_from_state_##TYPE(&src->user.current, data, \ - src->user.redirect); \ + if (sub_paths.user && act_attached->user.current.active) { \ + get_state_from_state_##TYPE(&act_attached->user.current, data, \ + act_attached->user.redirect); \ } \ - if (sub_paths.head && src->head.current.active) { \ - get_state_from_state_##TYPE(&src->head.current, data, \ - src->head.redirect); \ + if (sub_paths.head && act_attached->head.current.active) { \ + get_state_from_state_##TYPE(&act_attached->head.current, data, \ + act_attached->head.redirect); \ } \ - if (sub_paths.left && src->left.current.active) { \ - get_state_from_state_##TYPE(&src->left.current, data, \ - src->left.redirect); \ + if (sub_paths.left && act_attached->left.current.active) { \ + get_state_from_state_##TYPE(&act_attached->left.current, data, \ + act_attached->left.redirect); \ } \ - if (sub_paths.right && src->right.current.active) { \ - get_state_from_state_##TYPE(&src->right.current, data, \ - src->right.redirect); \ + if (sub_paths.right && act_attached->right.current.active) { \ + get_state_from_state_##TYPE(&act_attached->right.current, \ + data, \ + act_attached->right.redirect); \ } \ - if (sub_paths.gamepad && src->gamepad.current.active) { \ - get_state_from_state_##TYPE(&src->gamepad.current, data, \ - src->gamepad.redirect); \ + if (sub_paths.gamepad && act_attached->gamepad.current.active) { \ + get_state_from_state_##TYPE(&act_attached->gamepad.current, \ + data, \ + act_attached->gamepad.redirect); \ } XrResult oxr_action_get_boolean(struct oxr_logger *log, struct oxr_session *sess, - uint64_t key, + uint32_t act_key, struct oxr_sub_paths sub_paths, XrActionStateBoolean *data) { - struct oxr_source *src = NULL; + struct oxr_action_attachment *act_attached = NULL; - oxr_session_get_source(sess, key, &src); - if (src == NULL) { + oxr_session_get_action_attachment(sess, act_key, &act_attached); + if (act_attached == NULL) { return oxr_error( log, XR_ERROR_ACTIONSET_NOT_ATTACHED, "Action has not been attached to this session"); @@ -1245,14 +1402,14 @@ oxr_action_get_boolean(struct oxr_logger *log, XrResult oxr_action_get_vector1f(struct oxr_logger *log, struct oxr_session *sess, - uint64_t key, + uint32_t act_key, struct oxr_sub_paths sub_paths, XrActionStateFloat *data) { - struct oxr_source *src = NULL; + struct oxr_action_attachment *act_attached = NULL; - oxr_session_get_source(sess, key, &src); - if (src == NULL) { + oxr_session_get_action_attachment(sess, act_key, &act_attached); + if (act_attached == NULL) { return oxr_error( log, XR_ERROR_ACTIONSET_NOT_ATTACHED, "Action has not been attached to this session"); @@ -1269,14 +1426,14 @@ oxr_action_get_vector1f(struct oxr_logger *log, XrResult oxr_action_get_vector2f(struct oxr_logger *log, struct oxr_session *sess, - uint64_t key, + uint32_t act_key, struct oxr_sub_paths sub_paths, XrActionStateVector2f *data) { - struct oxr_source *src = NULL; + struct oxr_action_attachment *act_attached = NULL; - oxr_session_get_source(sess, key, &src); - if (src == NULL) { + oxr_session_get_action_attachment(sess, act_key, &act_attached); + if (act_attached == NULL) { return oxr_error( log, XR_ERROR_ACTIONSET_NOT_ATTACHED, "Action has not been attached to this session"); @@ -1293,14 +1450,14 @@ oxr_action_get_vector2f(struct oxr_logger *log, XrResult oxr_action_get_pose(struct oxr_logger *log, struct oxr_session *sess, - uint64_t key, + uint32_t act_key, struct oxr_sub_paths sub_paths, XrActionStatePose *data) { - struct oxr_source *src = NULL; + struct oxr_action_attachment *act_attached = NULL; - oxr_session_get_source(sess, key, &src); - if (src == NULL) { + oxr_session_get_action_attachment(sess, act_key, &act_attached); + if (act_attached == NULL) { return oxr_error( log, XR_ERROR_ACTIONSET_NOT_ATTACHED, "Action has not been attached to this session"); @@ -1309,19 +1466,19 @@ oxr_action_get_pose(struct oxr_logger *log, data->isActive = XR_FALSE; if (sub_paths.user || sub_paths.any) { - data->isActive |= src->user.current.active; + data->isActive |= act_attached->user.current.active; } if (sub_paths.head || sub_paths.any) { - data->isActive |= src->head.current.active; + data->isActive |= act_attached->head.current.active; } if (sub_paths.left || sub_paths.any) { - data->isActive |= src->left.current.active; + data->isActive |= act_attached->left.current.active; } if (sub_paths.right || sub_paths.any) { - data->isActive |= src->right.current.active; + data->isActive |= act_attached->right.current.active; } if (sub_paths.gamepad || sub_paths.any) { - data->isActive |= src->gamepad.current.active; + data->isActive |= act_attached->gamepad.current.active; } return oxr_session_success_result(sess); @@ -1335,8 +1492,8 @@ oxr_action_get_pose(struct oxr_logger *log, */ static void -set_source_output_vibration(struct oxr_session *sess, - struct oxr_source_cache *cache, +set_action_output_vibration(struct oxr_session *sess, + struct oxr_action_cache *cache, int64_t stop, const XrHapticVibration *data) { @@ -1348,7 +1505,7 @@ set_source_output_vibration(struct oxr_session *sess, value.vibration.duration = data->duration; for (uint32_t i = 0; i < cache->num_outputs; i++) { - struct oxr_source_output *output = &cache->outputs[i]; + struct oxr_action_output *output = &cache->outputs[i]; struct xrt_device *xdev = output->xdev; xrt_device_set_output(xdev, output->name, &value); @@ -1360,14 +1517,14 @@ set_source_output_vibration(struct oxr_session *sess, XrResult oxr_action_apply_haptic_feedback(struct oxr_logger *log, struct oxr_session *sess, - uint64_t key, + uint32_t act_key, struct oxr_sub_paths sub_paths, const XrHapticBaseHeader *hapticEvent) { - struct oxr_source *src = NULL; + struct oxr_action_attachment *act_attached = NULL; - oxr_session_get_source(sess, key, &src); - if (src == NULL) { + oxr_session_get_action_attachment(sess, act_key, &act_attached); + if (act_attached == NULL) { return oxr_error( log, XR_ERROR_ACTIONSET_NOT_ATTACHED, "Action has not been attached to this session"); @@ -1379,20 +1536,20 @@ oxr_action_apply_haptic_feedback(struct oxr_logger *log, int64_t stop = data->duration <= 0 ? now : now + data->duration; // clang-format off - if (src->user.current.active && (sub_paths.user || sub_paths.any)) { - set_source_output_vibration(sess, &src->user, stop, data); + if (act_attached->user.current.active && (sub_paths.user || sub_paths.any)) { + set_action_output_vibration(sess, &act_attached->user, stop, data); } - if (src->head.current.active && (sub_paths.head || sub_paths.any)) { - set_source_output_vibration(sess, &src->head, stop, data); + if (act_attached->head.current.active && (sub_paths.head || sub_paths.any)) { + set_action_output_vibration(sess, &act_attached->head, stop, data); } - if (src->left.current.active && (sub_paths.left || sub_paths.any)) { - set_source_output_vibration(sess, &src->left, stop, data); + if (act_attached->left.current.active && (sub_paths.left || sub_paths.any)) { + set_action_output_vibration(sess, &act_attached->left, stop, data); } - if (src->right.current.active && (sub_paths.right || sub_paths.any)) { - set_source_output_vibration(sess, &src->right, stop, data); + if (act_attached->right.current.active && (sub_paths.right || sub_paths.any)) { + set_action_output_vibration(sess, &act_attached->right, stop, data); } - if (src->gamepad.current.active && (sub_paths.gamepad || sub_paths.any)) { - set_source_output_vibration(sess, &src->gamepad, stop, data); + if (act_attached->gamepad.current.active && (sub_paths.gamepad || sub_paths.any)) { + set_action_output_vibration(sess, &act_attached->gamepad, stop, data); } // clang-format on @@ -1402,33 +1559,33 @@ oxr_action_apply_haptic_feedback(struct oxr_logger *log, XrResult oxr_action_stop_haptic_feedback(struct oxr_logger *log, struct oxr_session *sess, - uint64_t key, + uint32_t act_key, struct oxr_sub_paths sub_paths) { - struct oxr_source *src = NULL; + struct oxr_action_attachment *act_attached = NULL; - oxr_session_get_source(sess, key, &src); - if (src == NULL) { + oxr_session_get_action_attachment(sess, act_key, &act_attached); + if (act_attached == NULL) { return oxr_error( log, XR_ERROR_ACTIONSET_NOT_ATTACHED, "Action has not been attached to this session"); } // clang-format off - if (src->user.current.active && (sub_paths.user || sub_paths.any)) { - oxr_source_cache_stop_output(log, sess, &src->user); + if (act_attached->user.current.active && (sub_paths.user || sub_paths.any)) { + oxr_action_cache_stop_output(log, sess, &act_attached->user); } - if (src->head.current.active && (sub_paths.head || sub_paths.any)) { - oxr_source_cache_stop_output(log, sess, &src->head); + if (act_attached->head.current.active && (sub_paths.head || sub_paths.any)) { + oxr_action_cache_stop_output(log, sess, &act_attached->head); } - if (src->left.current.active && (sub_paths.left || sub_paths.any)) { - oxr_source_cache_stop_output(log, sess, &src->left); + if (act_attached->left.current.active && (sub_paths.left || sub_paths.any)) { + oxr_action_cache_stop_output(log, sess, &act_attached->left); } - if (src->right.current.active && (sub_paths.right || sub_paths.any)) { - oxr_source_cache_stop_output(log, sess, &src->right); + if (act_attached->right.current.active && (sub_paths.right || sub_paths.any)) { + oxr_action_cache_stop_output(log, sess, &act_attached->right); } - if (src->gamepad.current.active && (sub_paths.gamepad || sub_paths.any)) { - oxr_source_cache_stop_output(log, sess, &src->gamepad); + if (act_attached->gamepad.current.active && (sub_paths.gamepad || sub_paths.any)) { + oxr_action_cache_stop_output(log, sess, &act_attached->gamepad); } // clang-format on diff --git a/src/xrt/state_trackers/oxr/oxr_instance.c b/src/xrt/state_trackers/oxr/oxr_instance.c index 7c651b6f65d215315428dd7a681569bb6bc003ed..6c25fb8cfca03cce75ecebade64467d49a565dfa 100644 --- a/src/xrt/state_trackers/oxr/oxr_instance.c +++ b/src/xrt/state_trackers/oxr/oxr_instance.c @@ -154,6 +154,7 @@ oxr_instance_create(struct oxr_logger *log, cache_path(log, inst, "/user/hand/left", &inst->path_cache.left); cache_path(log, inst, "/user/hand/right", &inst->path_cache.right); cache_path(log, inst, "/user/gamepad", &inst->path_cache.gamepad); + cache_path(log, inst, "/user/treadmill", &inst->path_cache.treadmill); cache_path(log, inst, "/interaction_profiles/khr/simple_controller", &inst->path_cache.khr_simple_controller); cache_path(log, inst, "/interaction_profiles/google/daydream_controller", &inst->path_cache.google_daydream_controller); cache_path(log, inst, "/interaction_profiles/htc/vive_controller", &inst->path_cache.htc_vive_controller); diff --git a/src/xrt/state_trackers/oxr/oxr_objects.h b/src/xrt/state_trackers/oxr/oxr_objects.h index fbc32daac4f115f8e19315a8396a52014b26ce5e..28ad48adc2eef55a2e382f6c048144a4d3583d6c 100644 --- a/src/xrt/state_trackers/oxr/oxr_objects.h +++ b/src/xrt/state_trackers/oxr/oxr_objects.h @@ -105,12 +105,14 @@ struct oxr_action; struct oxr_debug_messenger; struct oxr_handle_base; struct oxr_sub_paths; -struct oxr_source; -struct oxr_source_set; -struct oxr_source_input; -struct oxr_source_output; +struct oxr_action_attachment; +struct oxr_action_set_attachment; +struct oxr_action_input; +struct oxr_action_output; struct oxr_binding; struct oxr_interaction_profile; +struct oxr_action_set_ref; +struct oxr_action_ref; #define XRT_MAX_HANDLE_CHILDREN 256 #define OXR_MAX_SWAPCHAIN_IMAGES 8 @@ -363,9 +365,17 @@ oxr_action_to_openxr(struct oxr_action *act) /*! * Helper function to classify sub_paths. * + * Sets all members of @p sub_paths ( @ref oxr_sub_paths ) as appropriate based + * on the subaction paths found in the list. + * + * If no paths are provided, @p sub_paths->any will be true. + * + * @return false if an invalid subaction path is provided. + * * @public @memberof oxr_instance + * @relatesalso oxr_sub_paths */ -void +bool oxr_classify_sub_action_paths(struct oxr_logger *log, struct oxr_instance *inst, uint32_t num_subaction_paths, @@ -378,11 +388,11 @@ oxr_classify_sub_action_paths(struct oxr_logger *log, * @public @memberof oxr_session */ XrResult -oxr_source_get_pose_input(struct oxr_logger *log, +oxr_action_get_pose_input(struct oxr_logger *log, struct oxr_session *sess, uint32_t key, const struct oxr_sub_paths *sub_paths, - struct oxr_source_input **out_input); + struct oxr_action_input **out_input); /*! * @public @memberof oxr_instance */ @@ -422,7 +432,7 @@ oxr_action_sync_data(struct oxr_logger *log, XrResult oxr_action_get_boolean(struct oxr_logger *log, struct oxr_session *sess, - uint64_t key, + uint32_t act_key, struct oxr_sub_paths sub_paths, XrActionStateBoolean *data); /*! @@ -431,7 +441,7 @@ oxr_action_get_boolean(struct oxr_logger *log, XrResult oxr_action_get_vector1f(struct oxr_logger *log, struct oxr_session *sess, - uint64_t key, + uint32_t act_key, struct oxr_sub_paths sub_paths, XrActionStateFloat *data); @@ -441,7 +451,7 @@ oxr_action_get_vector1f(struct oxr_logger *log, XrResult oxr_action_get_vector2f(struct oxr_logger *log, struct oxr_session *sess, - uint64_t key, + uint32_t act_key, struct oxr_sub_paths sub_paths, XrActionStateVector2f *data); /*! @@ -450,7 +460,7 @@ oxr_action_get_vector2f(struct oxr_logger *log, XrResult oxr_action_get_pose(struct oxr_logger *log, struct oxr_session *sess, - uint64_t key, + uint32_t act_key, struct oxr_sub_paths sub_paths, XrActionStatePose *data); /*! @@ -459,7 +469,7 @@ oxr_action_get_pose(struct oxr_logger *log, XrResult oxr_action_apply_haptic_feedback(struct oxr_logger *log, struct oxr_session *sess, - uint64_t key, + uint32_t act_key, struct oxr_sub_paths sub_paths, const XrHapticBaseHeader *hapticEvent); /*! @@ -468,7 +478,7 @@ oxr_action_apply_haptic_feedback(struct oxr_logger *log, XrResult oxr_action_stop_haptic_feedback(struct oxr_logger *log, struct oxr_session *sess, - uint64_t key, + uint32_t act_key, struct oxr_sub_paths sub_paths); /*! @@ -1123,6 +1133,7 @@ struct oxr_instance XrPath left; XrPath right; XrPath gamepad; + XrPath treadmill; XrPath khr_simple_controller; XrPath google_daydream_controller; @@ -1172,11 +1183,27 @@ struct oxr_session bool frame_started; bool exiting; - struct u_hashmap_int *act_sets; - struct u_hashmap_int *sources; + /*! + * An array of action set attachments that this session owns. + */ + struct oxr_action_set_attachment *act_set_attachments; + /*! + * Length of @ref oxr_session::act_set_attachments. + */ + size_t num_action_set_attachments; + + /*! + * A map of action set key to action set attachments. + */ + struct u_hashmap_int *act_sets_attachments_by_key; - //! List of created source sets. - struct oxr_source_set *src_set_list; + /*! + * A map of action key to action attachment. + * + * The action attachments are actually owned by the action set + * attachments, but we own the action set attachments, so this is OK. + */ + struct u_hashmap_int *act_attachments_by_key; //! Has xrAttachSessionActionSets been called? bool actionsAttached; @@ -1272,7 +1299,10 @@ struct oxr_binding }; /*! - * To carry around a sementic selection of sub action paths. + * A parsed equivalent of a list of sub-action paths. + * + * If @p any is true, then no paths were provided, which typically means any + * input is acceptable. */ struct oxr_sub_paths { @@ -1282,51 +1312,84 @@ struct oxr_sub_paths bool left; bool right; bool gamepad; + bool treadmill; }; /*! - * Session input source. + * The data associated with the attachment of an Action Set (@ref + * oxr_action_set) to as Session (@ref oxr_session). + * + * Action sets are created as children of the Instance, but are primarily used + * with one or more Sessions. They may be used with multiple sessions at a time, + * so we can't just put the per-session information directly in the action set + * or action. Instead, we have the _attachment structures, which mirror the + * action sets and actions but are rooted under the Session: + * + * - For every action set attached to a session, that session owns a @ref + * oxr_action_set_attachment. + * - For each action in those attached action sets, the action set attachment + * owns an @ref oxr_action_attachment. + * + * We go from the public handle to the _attachment structure by using a `key` + * value and a hash map: specifically, we look up the oxr_action_set::key and + * oxr_action::key in the session. + * + * This structure has no pointer to the @ref oxr_action_set that created it + * because the application is allowed to destroy an action before the session, + * which should change nothing except not allow the application to access the + * corresponding data anymore. * * @see oxr_action_set - * @extends oxr_handle_base */ -struct oxr_source_set +struct oxr_action_set_attachment { - //! Common structure for things referred to by OpenXR handles. - struct oxr_handle_base handle; - //! Owning session. struct oxr_session *sess; + //! Action set refcounted data + struct oxr_action_set_ref *act_set_ref; + + //! Unique key for the session hashmap. + uint32_t act_set_key; + //! Which sub-action paths are requested on the latest sync. struct oxr_sub_paths requested_sub_paths; - //! Next source set on this session. - struct oxr_source_set *next; + //! An array of action attachments we own. + struct oxr_action_attachment *act_attachments; + + /*! + * Length of @ref oxr_action_set_attachment::act_attachments. + */ + size_t num_action_attachments; }; /*! - * The state of a action input source. + * De-initialize an action set attachment and its action attachments. * - * @see oxr_source + * Frees the action attachments, but does not de-allocate the action set + * attachment. + * + * @public @memberof oxr_action_set_attachment */ -struct oxr_source_state -{ - union { - struct - { - float x; - } vec1; +void +oxr_action_set_attachment_teardown( + struct oxr_action_set_attachment *act_set_attached); - struct - { - float x; - float y; - } vec2; - bool boolean; - }; +/*! + * The state of a action input. + * + * @see oxr_action_attachment + */ +struct oxr_action_state +{ + /*! + * The actual value - must interpret using action type + */ + union xrt_input_value value; + //! Is this active (bound and providing input)? bool active; // Was this changed. @@ -1337,24 +1400,24 @@ struct oxr_source_state }; /*! - * A input source pair of a @ref xrt_input and a @ref xrt_device. + * A input action pair of a @ref xrt_input and a @ref xrt_device. * * @see xrt_device * @see xrt_input */ -struct oxr_source_input +struct oxr_action_input { struct xrt_device *xdev; struct xrt_input *input; }; /*! - * A output source pair of a @ref xrt_output_name and a @ref xrt_device. + * A output action pair of a @ref xrt_output_name and a @ref xrt_device. * * @see xrt_device * @see xrt_output_name */ -struct oxr_source_output +struct oxr_action_output { struct xrt_device *xdev; enum xrt_output_name name; @@ -1363,43 +1426,57 @@ struct oxr_source_output /*! * A set of inputs for a single sub action path. * - * @see oxr_source + * @see oxr_action_attachment */ -struct oxr_source_cache +struct oxr_action_cache { - struct oxr_source_state current; + struct oxr_action_state current; size_t num_inputs; - struct oxr_source_input *inputs; + struct oxr_action_input *inputs; int64_t stop_output_time; size_t num_outputs; - struct oxr_source_output *outputs; + struct oxr_action_output *outputs; enum xrt_source_value_redirect redirect; }; /*! - * Session input source. + * Data associated with an Action that has been attached to a Session. + * + * More information on the action vs action attachment and action set vs action + * set attachment parallel is in the docs for @ref oxr_action_set_attachment. * * @see oxr_action - * @extends oxr_handle_base + * @see oxr_action_set_attachment */ -struct oxr_source +struct oxr_action_attachment { - //! Common structure for things referred to by OpenXR handles. - struct oxr_handle_base handle; + //! The owning action set attachment + struct oxr_action_set_attachment *act_set_attached; - //! Type the action this source was created from is. - XrActionType action_type; + //! This action's refcounted data + struct oxr_action_ref *act_ref; - struct oxr_source_state any_state; + /*! + * The corresponding session. + * + * This will always be valid: the session outlives this object because + * it owns act_set_attached. + */ + struct oxr_session *sess; + + //! Unique key for the session hashmap. + uint32_t act_key; + + struct oxr_action_state any_state; - struct oxr_source_cache user; - struct oxr_source_cache head; - struct oxr_source_cache left; - struct oxr_source_cache right; - struct oxr_source_cache gamepad; + struct oxr_action_cache user; + struct oxr_action_cache head; + struct oxr_action_cache left; + struct oxr_action_cache right; + struct oxr_action_cache gamepad; }; /*! @@ -1514,13 +1591,70 @@ struct oxr_swapchain const XrSwapchainImageReleaseInfo *); }; +struct oxr_refcounted +{ + struct xrt_reference base; + //! Destruction callback + void (*destroy)(struct oxr_refcounted *); +}; + +/*! + * Increase the reference count of @p orc. + */ +static inline void +oxr_refcounted_ref(struct oxr_refcounted *orc) +{ + xrt_reference_inc(&orc->base); +} + +/*! + * Decrease the reference count of @p orc, destroying it if it reaches 0. + */ +static inline void +oxr_refcounted_unref(struct oxr_refcounted *orc) +{ + if (xrt_reference_dec(&orc->base)) { + orc->destroy(orc); + } +} + +/*! + * The reference-counted data of an action set. + * + * One or more sessions may still need this data after the application destroys + * its XrActionSet handle, so this data is refcounted. + * + * @see oxr_action_set + * @extends oxr_refcounted + */ +struct oxr_action_set_ref +{ + struct oxr_refcounted base; + + //! Application supplied name of this action. + char name[XR_MAX_ACTION_SET_NAME_SIZE]; + + //! Has this action set been attached. + bool attached; + + //! Unique key for the session hashmap. + uint32_t act_set_key; + + struct + { + struct u_hashset *name_store; + struct u_hashset *loc_store; + } actions; +}; + /*! * A group of actions. * * Parent type/handle is @ref oxr_instance * - * Note, however, that an action set must be "attached" to a session (@ref - * oxr_session) to be used and not just configured. + * Note, however, that an action set must be "attached" to a session + * ( @ref oxr_session ) to be used and not just configured. + * The corresponding data is in @ref oxr_action_set_attachment. * * @obj{XrActionSet} * @extends oxr_handle_base @@ -1533,26 +1667,51 @@ struct oxr_action_set //! Owner of this action set. struct oxr_instance *inst; - //! Application supplied name of this action. - char name[XR_MAX_ACTION_SET_NAME_SIZE]; + /*! + * The data for this action set that must live as long as any session we + * are attached to. + */ + struct oxr_action_set_ref *data; - //! Has this action set been attached. - bool attached; - //! Unique key for the session hashmap. - uint32_t key; + /*! + * Unique key for the session hashmap. + * + * Duplicated from oxr_action_set_ref::act_set_key for efficiency. + */ + uint32_t act_set_key; //! The item in the name hashset. struct u_hashset_item *name_item; //! The item in the localized hashset. struct u_hashset_item *loc_item; +}; - struct - { - struct u_hashset *name_store; - struct u_hashset *loc_store; - } actions; +/*! + * The reference-counted data of an action. + * + * One or more sessions may still need this data after the application destroys + * its XrAction handle, so this data is refcounted. + * + * @see oxr_action + * @extends oxr_refcounted + */ +struct oxr_action_ref +{ + struct oxr_refcounted base; + + //! Application supplied name of this action. + char name[XR_MAX_ACTION_NAME_SIZE]; + + //! Unique key for the session hashmap. + uint32_t act_key; + + //! Type this action was created with. + XrActionType action_type; + + //! Which sub action paths that this action was created with. + struct oxr_sub_paths sub_paths; }; /*! @@ -1560,6 +1719,9 @@ struct oxr_action_set * * Parent type/handle is @ref oxr_action_set * + * For actual usage, an action is attached to a session: the corresponding data + * is in @ref oxr_action_attachment + * * @obj{XrAction} * @extends oxr_handle_base */ @@ -1571,17 +1733,16 @@ struct oxr_action //! Owner of this action. struct oxr_action_set *act_set; - //! Application supplied name of this action. - char name[XR_MAX_ACTION_NAME_SIZE]; - - //! Unique key for the session hashmap. - uint32_t key; + //! The data for this action that must live as long as any session we + //! are attached to. + struct oxr_action_ref *data; - //! Type this action was created with. - XrActionType action_type; - - //! Which sub action paths that this action was created with. - struct oxr_sub_paths sub_paths; + /*! + * Unique key for the session hashmap. + * + * Duplicated from oxr_action_ref::act_key for efficiency. + */ + uint32_t act_key; //! The item in the name hashset. struct u_hashset_item *name_item; diff --git a/src/xrt/state_trackers/oxr/oxr_session.c b/src/xrt/state_trackers/oxr/oxr_session.c index c62e8cab6285ae02772fea83df2479778405196f..d277129b1663a9680069ed531802d65731b27b7a 100644 --- a/src/xrt/state_trackers/oxr/oxr_session.c +++ b/src/xrt/state_trackers/oxr/oxr_session.c @@ -776,6 +776,44 @@ verify_projection_layer(struct xrt_compositor *xc, return XR_SUCCESS; } +static enum xrt_layer_composition_flags +convert_layer_flags(XrSwapchainUsageFlags xr_flags) +{ + enum xrt_layer_composition_flags flags = 0; + + // clang-format off + if ((xr_flags & XR_COMPOSITION_LAYER_CORRECT_CHROMATIC_ABERRATION_BIT) != 0) { + flags |= XRT_LAYER_COMPOSITION_CORRECT_CHROMATIC_ABERRATION_BIT; + } + if ((xr_flags & XR_COMPOSITION_LAYER_BLEND_TEXTURE_SOURCE_ALPHA_BIT) != 0) { + flags |= XRT_LAYER_COMPOSITION_BLEND_TEXTURE_SOURCE_ALPHA_BIT; + } + if ((xr_flags & XR_COMPOSITION_LAYER_UNPREMULTIPLIED_ALPHA_BIT) != 0) { + flags |= XRT_LAYER_COMPOSITION_UNPREMULTIPLIED_ALPHA_BIT; + } + // clang-format on + + return flags; +} + +static enum xrt_layer_eye_visibility +convert_eye_visibility(XrSwapchainUsageFlags xr_visibility) +{ + enum xrt_layer_eye_visibility visibility = 0; + + if (xr_visibility == XR_EYE_VISIBILITY_BOTH) { + visibility = XRT_LAYER_EYE_VISIBILITY_BOTH; + } + if (xr_visibility == XR_EYE_VISIBILITY_LEFT) { + visibility = XRT_LAYER_EYE_VISIBILITY_LEFT_BIT; + } + if (xr_visibility == XR_EYE_VISIBILITY_RIGHT) { + visibility = XRT_LAYER_EYE_VISIBILITY_RIGHT_BIT; + } + + return visibility; +} + static XrResult submit_quad_layer(struct xrt_compositor *xc, struct oxr_logger *log, @@ -786,17 +824,47 @@ submit_quad_layer(struct xrt_compositor *xc, { struct oxr_swapchain *sc = XRT_CAST_OXR_HANDLE_TO_PTR( struct oxr_swapchain *, quad->subImage.swapchain); + struct oxr_space *spc = + XRT_CAST_OXR_HANDLE_TO_PTR(struct oxr_space *, quad->space); + + enum xrt_layer_composition_flags flags = + convert_layer_flags(quad->layerFlags); + + struct xrt_pose *pose_ptr = (struct xrt_pose *)&quad->pose; + struct xrt_pose pose = *pose_ptr; + + if (spc->is_reference && spc->type == XR_REFERENCE_SPACE_TYPE_VIEW) { + flags |= XRT_LAYER_COMPOSITION_VIEW_SPACE_BIT; + // The space might have a pose, transform that in as well. + math_pose_transform(&spc->pose, &pose, &pose); + } else { + //! @todo Handle action spaces. + + // The space might have a pose, transform that in as well. + math_pose_transform(&spc->pose, &pose, &pose); + + // Remove the tracking system origin offset. + math_pose_transform(inv_offset, &pose, &pose); + } + + struct xrt_layer_data data; + U_ZERO(&data); + data.type = XRT_LAYER_QUAD; + data.name = XRT_INPUT_GENERIC_HEAD_POSE; + data.timestamp = timestamp; + data.flags = flags; - struct xrt_pose pose; + struct xrt_vec2 *size = (struct xrt_vec2 *)&quad->size; + struct xrt_rect *rect = (struct xrt_rect *)&quad->subImage.imageRect; - math_pose_transform(inv_offset, (struct xrt_pose *)&quad->pose, &pose); + data.quad.visibility = convert_eye_visibility(quad->eyeVisibility); + data.quad.sub.image_index = sc->released.index; + data.quad.sub.array_index = quad->subImage.imageArrayIndex; + data.quad.sub.rect = *rect; + data.quad.pose = pose; + data.quad.size = *size; - CALL_CHK(xrt_comp_layer_quad( - xc, timestamp, head, XRT_INPUT_GENERIC_HEAD_POSE, quad->layerFlags, - (enum xrt_layer_eye_visibility)quad->eyeVisibility, sc->swapchain, - sc->released.index, (struct xrt_rect *)&quad->subImage.imageRect, - quad->subImage.imageArrayIndex, &pose, - (struct xrt_vec2 *)&quad->size, false)); + CALL_CHK(xrt_comp_layer_quad(xc, head, sc->swapchain, &data)); return XR_SUCCESS; } @@ -809,34 +877,70 @@ submit_projection_layer(struct xrt_compositor *xc, struct xrt_pose *inv_offset, uint64_t timestamp) { - enum xrt_layer_composition_flags flags = 0; + struct oxr_space *spc = + XRT_CAST_OXR_HANDLE_TO_PTR(struct oxr_space *, proj->space); struct oxr_swapchain *scs[2]; + struct xrt_pose *pose_ptr[2]; + struct xrt_pose pose[2]; - uint32_t num_chains = ARRAY_SIZE(scs); + enum xrt_layer_composition_flags flags = + convert_layer_flags(proj->layerFlags); + uint32_t num_chains = ARRAY_SIZE(scs); for (uint32_t i = 0; i < num_chains; i++) { scs[i] = XRT_CAST_OXR_HANDLE_TO_PTR( struct oxr_swapchain *, proj->views[i].subImage.swapchain); + pose_ptr[i] = (struct xrt_pose *)&proj->views[i].pose; + pose[i] = *pose_ptr[i]; } - struct xrt_pose pose[2]; - math_pose_transform(inv_offset, (struct xrt_pose *)&proj->views[0].pose, - &pose[0]); - math_pose_transform(inv_offset, (struct xrt_pose *)&proj->views[1].pose, - &pose[1]); - - CALL_CHK(xrt_comp_layer_stereo_projection( - xc, timestamp, head, XRT_INPUT_GENERIC_HEAD_POSE, flags, - scs[0]->swapchain, // Left - scs[0]->released.index, - (struct xrt_rect *)&proj->views[0].subImage.imageRect, - proj->views[0].subImage.imageArrayIndex, - (struct xrt_fov *)&proj->views[0].fov, &pose[0], - scs[1]->swapchain, // Right - scs[1]->released.index, - (struct xrt_rect *)&proj->views[1].subImage.imageRect, - proj->views[1].subImage.imageArrayIndex, - (struct xrt_fov *)&proj->views[1].fov, &pose[1], false)); + if (spc->is_reference && spc->type == XR_REFERENCE_SPACE_TYPE_VIEW) { + flags |= XRT_LAYER_COMPOSITION_VIEW_SPACE_BIT; + // The space might have a pose, transform that in as well. + math_pose_transform(&spc->pose, &pose[0], &pose[0]); + math_pose_transform(&spc->pose, &pose[1], &pose[1]); + } else { + //! @todo Handle action spaces. + + // The space might have a pose, transform that in as well. + math_pose_transform(&spc->pose, &pose[0], &pose[0]); + math_pose_transform(&spc->pose, &pose[1], &pose[1]); + + // Remove the tracking system origin offset. + math_pose_transform(inv_offset, &pose[0], &pose[0]); + math_pose_transform(inv_offset, &pose[1], &pose[1]); + } + + struct xrt_rect *l_rect = + (struct xrt_rect *)&proj->views[0].subImage.imageRect; + struct xrt_fov *l_fov = (struct xrt_fov *)&proj->views[0].fov; + struct xrt_rect *r_rect = + (struct xrt_rect *)&proj->views[1].subImage.imageRect; + struct xrt_fov *r_fov = (struct xrt_fov *)&proj->views[1].fov; + + struct xrt_layer_data data; + U_ZERO(&data); + data.type = XRT_LAYER_STEREO_PROJECTION; + data.name = XRT_INPUT_GENERIC_HEAD_POSE; + data.timestamp = timestamp; + data.flags = flags; + + data.stereo.l.sub.image_index = scs[0]->released.index; + data.stereo.l.sub.array_index = proj->views[0].subImage.imageArrayIndex; + data.stereo.l.sub.rect = *l_rect; + data.stereo.l.fov = *l_fov; + data.stereo.l.pose = pose[0]; + + data.stereo.r.sub.image_index = scs[1]->released.index; + data.stereo.r.sub.array_index = proj->views[1].subImage.imageArrayIndex; + data.stereo.r.sub.rect = *r_rect; + data.stereo.r.fov = *r_fov; + data.stereo.r.pose = pose[1]; + + CALL_CHK(xrt_comp_layer_stereo_projection(xc, head, + scs[0]->swapchain, // Left + scs[1]->swapchain, // Right + &data)); return XR_SUCCESS; } @@ -1023,9 +1127,20 @@ oxr_session_destroy(struct oxr_logger *log, struct oxr_handle_base *hb) // Does a null-ptr check. xrt_comp_destroy(&sess->compositor); + for (size_t i = 0; i < sess->num_action_set_attachments; ++i) { + oxr_action_set_attachment_teardown( + &sess->act_set_attachments[i]); + } + free(sess->act_set_attachments); + sess->act_set_attachments = NULL; + sess->num_action_set_attachments = 0; + + // If we tore everything down correctly, these are empty now. + assert(u_hashmap_int_empty(sess->act_sets_attachments_by_key)); + assert(u_hashmap_int_empty(sess->act_attachments_by_key)); - u_hashmap_int_destroy(&sess->act_sets); - u_hashmap_int_destroy(&sess->sources); + u_hashmap_int_destroy(&sess->act_sets_attachments_by_key); + u_hashmap_int_destroy(&sess->act_attachments_by_key); free(sess); @@ -1163,8 +1278,8 @@ oxr_session_create(struct oxr_logger *log, oxr_session_change_state(log, sess, XR_SESSION_STATE_IDLE); oxr_session_change_state(log, sess, XR_SESSION_STATE_READY); - u_hashmap_int_create(&sess->act_sets); - u_hashmap_int_create(&sess->sources); + u_hashmap_int_create(&sess->act_sets_attachments_by_key); + u_hashmap_int_create(&sess->act_attachments_by_key); *out_session = sess; diff --git a/src/xrt/state_trackers/oxr/oxr_space.c b/src/xrt/state_trackers/oxr/oxr_space.c index f9214b9c48e5379642dd3340aa55889a51785c13..e159da78721c670e2df338e0e7e1f0662f61492a 100644 --- a/src/xrt/state_trackers/oxr/oxr_space.c +++ b/src/xrt/state_trackers/oxr/oxr_space.c @@ -197,7 +197,7 @@ oxr_space_action_relation(struct oxr_logger *log, XrTime at_time, struct xrt_space_relation *out_relation) { - struct oxr_source_input *input = NULL; + struct oxr_action_input *input = NULL; struct oxr_space *act_spc, *ref_spc = NULL; uint64_t timestamp = 0; bool invert = false; @@ -235,7 +235,7 @@ oxr_space_action_relation(struct oxr_logger *log, return XR_SUCCESS; } - oxr_source_get_pose_input(log, sess, act_spc->act_key, + oxr_action_get_pose_input(log, sess, act_spc->act_key, &act_spc->sub_paths, &input); // If the input isn't active.