···11+Fixes tests_aux_d3d_d3d11 failing in DuplicateHandle before importing
+1-1
doc/changes/state_trackers/mr.2399.md
···11-gui: Refactor GUI state tracker into a base that can be more easily re-used
11+gui: Refactor GUI state tracker into a base that can be more easily reused
22and that brings in fewer dependencies.
+1
doc/changes/state_trackers/mr.2647.md
···11+st/oxr: Report proper timestamps in XrEventDataSessionStateChanged instead of 0.
+3
doc/changes/xrt/mr.2612.1.md
···11+Add @ref xrt_view_type argument to @ref xrt_device::get_view_poses function,
22+this decouples the type of the view from the number. Letting us have different
33+semantics for the same view count.
+4
doc/changes/xrt/mr.2612.2.md
···11+Make it possible for the compositor to expose multiple view configurations types
22+supported, and letting the compositor control the sizes for them separately.
33+This change introduces @ref xrt_view_config and @ref xrt_view_config_properties
44+which are added to @ref xrt_system_compositor_info.
+3
doc/changes/xrt/mr.2612.3.md
···11+Change the interface to allow for late creation of the @ref xrt_system and other
22+system level structs. Allowing the server to launch accept apps and decide
33+which form factor or view configuration to support until a later date.
+3
doc/changes/xrt/mr.2633.md
···11+Enable the use of [X_macro](https://en.wikipedia.org/wiki/X_macro) pattern with
22+more the enums in the `xrt_defines.h` header. Code has been changed to use these
33+as well reducing the amount of generated code needed.
···3131 * @param xsci Swapchain create info: note that the format is assumed to be a DXGI_FORMAT (conversion to typeless is
3232 * automatic)
3333 * @param image_count The number of images to create.
3434- * @param keyed_mutex Whether to create images with a shared "keyed mutex" as well
3534 * @param[out] out_images A vector that will be cleared and populated with the images.
3635 * @param[out] out_handles A vector that will be cleared and populated with the corresponding native handles.
3736 * @param[out] out_image_mem_size The image memory allocation size in bytes
+21
src/xrt/auxiliary/math/m_api.h
···18181919#include "xrt/xrt_defines.h"
20202121+#include "math/m_mathinclude.h"
2222+2123#ifdef __cplusplus
2224extern "C" {
2325#endif
···7476 * @ingroup aux_math
7577 */
7678#define CLAMP(X, A, B) (MIN(MAX((X), (A)), (B)))
7979+8080+/*!
8181+ * Degrees to radians conversion.
8282+ *
8383+ * @ingroup aux_math
8484+ */
8585+// clang-format off
8686+// @todo: Remove the clang-format off/on when we move to a newer clang-format in CI.
8787+#define DEG_TO_RAD(DEG) ((DEG) * M_PI / 180.)
8888+// clang-format on
8989+9090+/*!
9191+ * Radians to degrees conversion.
9292+ *
9393+ * @ingroup aux_math
9494+ */
9595+// clang-format off
9696+#define RAD_TO_DEG(RAD) ((RAD) * 180.0 / M_PI)
9797+// clang-format on
7798789979100/*
···6363m_relation_history_push(struct m_relation_history *rh, struct xrt_space_relation const *in_relation, int64_t timestamp);
64646565/*!
6666- * Interpolates or extrapolates to the desired timestamp.
6666+ * Pushes a new pose to the history, estimating linear and angular velocity based on the previous entry.
6767 *
6868- * Read-only operation - doesn't remove anything from the buffer or anything like that - you can call this as often as
6969- * you want.
6868+ * If the history is full, it will also pop a pose out of the other side of the buffer.
6969+ *
7070+ * @return false if the timestamp is earlier than the most recent timestamp already recorded
7071 *
7172 * @public @memberof m_relation_history
7273 */
7373-enum m_relation_history_result
7474-m_relation_history_get(const struct m_relation_history *rh,
7575- int64_t at_timestamp_ns,
7676- struct xrt_space_relation *out_relation);
7474+bool
7575+m_relation_history_push_with_motion_estimation(struct m_relation_history *rh,
7676+ struct xrt_space_relation const *in_relation,
7777+ int64_t timestamp);
77787879/*!
7979- * Estimates the movement (velocity and angular velocity) of a new relation based on
8080- * the latest relation found in the buffer (as returned by m_relation_history_get_latest).
8080+ * Interpolates or extrapolates to the desired timestamp.
8181 *
8282- * Read-only on m_relation_history and in_relation.
8383- * Copies in_relation->pose to out_relation->pose, and writes new flags and linear/angular velocities to
8484- * out_relation->pose. OK to alias in_relation and out_relation.
8282+ * Read-only operation - doesn't remove anything from the buffer or anything like that - you can call this as often
8383+ * as you want.
8584 *
8685 * @public @memberof m_relation_history
8786 */
8888-bool
8989-m_relation_history_estimate_motion(struct m_relation_history *rh,
9090- const struct xrt_space_relation *in_relation,
9191- int64_t timestamp,
9292- struct xrt_space_relation *out_relation);
8787+enum m_relation_history_result
8888+m_relation_history_get(const struct m_relation_history *rh,
8989+ int64_t at_timestamp_ns,
9090+ struct xrt_space_relation *out_relation);
93919492/*!
9593 * Get the latest report in the buffer, if any.
···11// Copyright 2019-2025, Collabora, Ltd.
22+// Copyright 2025, NVIDIA CORPORATION.
23// SPDX-License-Identifier: BSL-1.0
34/*!
45 * @file
···74757576/*
7677 *
7878+ * Logging file code.
7979+ *
8080+ */
8181+8282+static FILE *g_log_file = NULL;
8383+8484+void
8585+u_log_set_output_file(const char *filename)
8686+{
8787+ // While not thread safe, this function is externally synchronized.
8888+ if (g_log_file != NULL) {
8989+ FILE *tmp = g_log_file;
9090+ g_log_file = NULL;
9191+9292+ fflush(tmp);
9393+ fclose(tmp);
9494+ tmp = NULL;
9595+ }
9696+9797+ if (filename == NULL) {
9898+ return; // Turning off file logging.
9999+ }
100100+101101+ g_log_file = fopen(filename, "w");
102102+ if (g_log_file == NULL) {
103103+ U_LOG_E("Failed to open '%s'", filename);
104104+ }
105105+}
106106+107107+108108+/*
109109+ *
77110 * Logging sink.
78111 *
79112 */
···294327 int ret = 0;
295328296329#ifdef XRT_FEATURE_COLOR_LOG
297297- if (isatty(STDERR_FILENO)) {
330330+ if (g_log_file == NULL && isatty(STDERR_FILENO)) {
298331 ret = print_prefix_color(func, level, buf, remaining);
299332 } else {
300333 ret = print_prefix_mono(func, level, buf, remaining);
···407440 OutputDebugStringA(storage);
408441#endif
409442410410- fwrite(storage, printed, 1, stderr);
443443+ FILE *output = g_log_file != NULL ? g_log_file : stderr;
444444+ fwrite(storage, printed, 1, output);
445445+446446+#if defined(XRT_OS_WINDOWS)
447447+ /*
448448+ * Windows like to buffer messages, call flush here to make sure all
449449+ * logs gets written out in case of sudden exits and crashes.
450450+ */
451451+ fflush(output);
452452+#endif
411453412454#else
413455#error "Port needed for logging function"
···454496 u_log(file, line, calling_fn, level, "%s", sink.buffer);
455497}
456498499499+static u_log_filter_func_t g_filter = NULL;
500500+501501+void
502502+u_log_set_filter(u_log_filter_func_t filter)
503503+{
504504+ g_filter = filter;
505505+}
506506+457507void
458508u_log(const char *file, int line, const char *func, enum u_logging_level level, const char *format, ...)
459509{
460510 va_list args;
511511+ // Check filter first
512512+ if (g_filter != NULL && !g_filter(file, line, func, level)) {
513513+ return; // Skip this message
514514+ }
515515+461516 va_start(args, format);
462517 DISPATCH_SINK(file, line, func, level, format, args);
463518 do_print(file, line, func, level, format, args);
···474529 ...)
475530{
476531 va_list args;
532532+ // Check filter first
533533+ if (g_filter != NULL && !g_filter(file, line, func, level)) {
534534+ return; // Skip this message
535535+ }
477536 va_start(args, format);
478537 DISPATCH_SINK(file, line, func, level, format, args);
479538 do_print(file, line, func, level, format, args);
+32
src/xrt/auxiliary/util/u_logging.h
···11// Copyright 2020-2025, Collabora, Ltd.
22+// Copyright 2025, NVIDIA CORPORATION.
23// SPDX-License-Identifier: BSL-1.0
34/*!
45 * @file
···6869 const char *format,
6970 va_list args,
7071 void *data);
7272+7373+/*!
7474+ * Function typedef for filtering log messages.
7575+ *
7676+ * @param file Source file name associated with a message.
7777+ * @param line Source file line associated with a message.
7878+ * @param func Function name associated with a message.
7979+ * @param level Message level: used for formatting or forwarding to native log functions.
8080+ * @return true if message should be logged, false to filter it out.
8181+ */
8282+typedef bool (*u_log_filter_func_t)(const char *file, int line, const char *func, enum u_logging_level level);
71837284/*!
7385 * For places where you really want printf, prints a new-line.
···270282u_log_get_global_level(void);
271283272284/*!
285285+ * Sets the output file for the logging instead of stderr, this function is
286286+ * externally synchronized with ALL other logging functions. Which means do not
287287+ * call any other logging function from different threads during a call to this
288288+ * function. Also to avoid leaks call this function with NULL to close the
289289+ * internally managed FILE object.
290290+ *
291291+ * WANRING THIS FUNCTION IS EXTERNALLY SYNCHRONIZED WITH ALL OTHER FUNCTIONS.
292292+ */
293293+void
294294+u_log_set_output_file(const char *filename);
295295+296296+/*!
273297 * @brief Main non-device-related log implementation function: do not call directly, use a macro that wraps it.
274298 *
275299 * This function always logs: level is used for printing or passed to native logging functions.
···377401 const char *calling_fn,
378402 xrt_result_t xret,
379403 const char *called_fn);
404404+405405+/*!
406406+ * @brief Add function to set the filter
407407+ *
408408+ * @param filter Filter function to set
409409+ */
410410+void
411411+u_log_set_filter(u_log_filter_func_t filter);
380412381413/*!
382414 * @}
···2929 * they can easily be chained together to form a debug message printing out
3030 * various information. Most of the final logging functions in Monado inserts a
3131 * newline at the end of the message and we don't want two to be inserted.
3232+ *
3333+ * There are also helpers that goes from an enum to a string that that doesn't
3434+ * use the delegate to do the printing, these returns string that are compiled
3535+ * into the binary. They will return 'UNKNOWN' if they don't know the value.
3636+ * But can be made to return NULL on unknown.
3237 */
3838+3939+/*!
4040+ * Returns a string of the input name, or NULL if invalid.
4141+ *
4242+ * @ingroup aux_pretty
4343+ */
4444+const char *
4545+u_str_xrt_input_name_or_null(enum xrt_input_name name);
4646+4747+/*!
4848+ * Returns a string of the output name, or NULL if invalid.
4949+ *
5050+ * @ingroup aux_pretty
5151+ */
5252+const char *
5353+u_str_xrt_output_name_or_null(enum xrt_output_name name);
5454+5555+/*!
5656+ * Returns a string of the device name, or NULL if invalid.
5757+ *
5858+ * @ingroup aux_pretty
5959+ */
6060+const char *
6161+u_str_xrt_device_name_or_null(enum xrt_device_name name);
6262+6363+#define U_STR_NO_NULL(NAME, TYPE) \
6464+ static inline const char *NAME(TYPE enumerate) \
6565+ { \
6666+ const char *str = NAME##_or_null(enumerate); \
6767+ return str != NULL ? str : "UNKNOWN"; \
6868+ }
6969+7070+U_STR_NO_NULL(u_str_xrt_input_name, enum xrt_input_name)
7171+U_STR_NO_NULL(u_str_xrt_output_name, enum xrt_output_name)
7272+U_STR_NO_NULL(u_str_xrt_device_name, enum xrt_device_name)
7373+7474+#undef U_STR_NO_NULL
7575+33763477/*!
3578 * Function prototype for receiving pretty printed strings.
···11// Copyright 2023, Collabora, Ltd.
22+// Copyright 2025, NVIDIA CORPORATION.
23// SPDX-License-Identifier: BSL-1.0
34/*!
45 * @file
···4243 U_SPACE_TYPE_POSE,
4344 U_SPACE_TYPE_OFFSET,
4445 U_SPACE_TYPE_ROOT,
4646+4747+ /*!
4848+ * Space designed to be attachable to others, most importantly it is
4949+ * re-attachable, and in order to move all of the spaces that has this
5050+ * space as it's parent/next we need a node that can be updated.
5151+ */
5252+ U_SPACE_TYPE_ATTACHABLE,
4553};
46544755/*!
···330338 m_relation_chain_push_relation(xrc, &xsr);
331339 } break;
332340 case U_SPACE_TYPE_OFFSET: m_relation_chain_push_pose_if_not_identity(xrc, &space->offset.pose); break;
333333- case U_SPACE_TYPE_ROOT: return; // Stops the traversing.
341341+ case U_SPACE_TYPE_ROOT: return; // Stops the traversing.
342342+ case U_SPACE_TYPE_ATTACHABLE: break; // No-op
334343 }
335344336345 // Please tail-call optimise this miss compiler.
···352361 case U_SPACE_TYPE_NULL: break;
353362 case U_SPACE_TYPE_POSE: break;
354363 case U_SPACE_TYPE_OFFSET: break;
355355- case U_SPACE_TYPE_ROOT: return; // Stops the traversing.
364364+ case U_SPACE_TYPE_ROOT: return; // Stops the traversing.
365365+ case U_SPACE_TYPE_ATTACHABLE: break; // No-op
356366 }
357367358368 // Can't tail-call optimise this one :(
···371381 } break;
372382 case U_SPACE_TYPE_OFFSET: m_relation_chain_push_inverted_pose_if_not_identity(xrc, &space->offset.pose); break;
373383 case U_SPACE_TYPE_ROOT: assert(false); // Should not get here.
384384+ case U_SPACE_TYPE_ATTACHABLE: break; // No-op
374385 }
375386}
376387···469480470481 // Created with one reference.
471482 uso->base.semantic.root = &us->base;
483483+}
484484+485485+486486+/*
487487+ *
488488+ * Device helpers.
489489+ *
490490+ */
491491+492492+/*!
493493+ * Helper function to add a device to the space overseer. This function
494494+ * handles creating or finding a space for the device's tracking origin
495495+ * and linking the device to that space.
496496+ */
497497+static xrt_result_t
498498+add_device_helper(struct u_space_overseer *uso, struct xrt_device *xdev)
499499+{
500500+ struct xrt_tracking_origin *torig = xdev->tracking_origin;
501501+ assert(torig != NULL);
502502+503503+ struct xrt_space *root = uso->base.semantic.root;
504504+ uint64_t key = (uint64_t)(intptr_t)torig;
505505+ struct xrt_space *xs = NULL;
506506+507507+ // Need to take the write lock.
508508+ pthread_rwlock_wrlock(&uso->lock);
509509+510510+ // Does this tracking origin already have space.
511511+ void *ptr = NULL;
512512+ u_hashmap_int_find(uso->xto_map, key, &ptr);
513513+514514+ if (ptr != NULL) {
515515+ xs = (struct xrt_space *)ptr;
516516+ } else if (torig->type == XRT_TRACKING_TYPE_ATTACHABLE) {
517517+ /*
518518+ * If we ever make u_space_overseer sub-classable make sure
519519+ * this calls the right function, can't call interface function
520520+ * as the lock is held here.
521521+ */
522522+ xs = (struct xrt_space *)create_space(U_SPACE_TYPE_ATTACHABLE, u_space(root));
523523+ u_hashmap_int_insert(uso->xto_map, key, xs);
524524+ } else {
525525+ /*
526526+ * If we ever make u_space_overseer sub-classable make sure
527527+ * this calls the right function, can't call interface function
528528+ * as the lock is held here.
529529+ */
530530+ xs = (struct xrt_space *)create_space(U_SPACE_TYPE_OFFSET, u_space(root));
531531+532532+ update_offset_write_locked(u_space(xs), &torig->initial_offset);
533533+534534+ u_hashmap_int_insert(uso->xto_map, key, xs);
535535+ }
536536+537537+ pthread_rwlock_unlock(&uso->lock);
538538+539539+ u_space_overseer_link_space_to_device(uso, xs, xdev);
540540+541541+ return XRT_SUCCESS;
472542}
473543474544···10331103 return xret;
10341104}
1035110511061106+static xrt_result_t
11071107+add_device(struct xrt_space_overseer *xso, struct xrt_device *xdev)
11081108+{
11091109+ struct u_space_overseer *uso = u_space_overseer(xso);
11101110+11111111+ return add_device_helper(uso, xdev);
11121112+}
11131113+11141114+static xrt_result_t
11151115+attach_device(struct xrt_space_overseer *xso, struct xrt_device *xdev, struct xrt_space *space)
11161116+{
11171117+ struct u_space_overseer *uso = u_space_overseer(xso);
11181118+11191119+ // Check that the device has the correct tracking origin type.
11201120+ if (xdev->tracking_origin == NULL || xdev->tracking_origin->type != XRT_TRACKING_TYPE_ATTACHABLE) {
11211121+ U_LOG_E("Device '%s' does not have XRT_TRACKING_TYPE_ATTACHABLE tracking origin type", xdev->str);
11221122+ return XRT_ERROR_DEVICE_NOT_ATTACHABLE;
11231123+ }
11241124+11251125+ // If no space is provided, use the root space.
11261126+ struct xrt_space *target_space = space;
11271127+ if (target_space == NULL) {
11281128+ target_space = uso->base.semantic.root;
11291129+ }
11301130+11311131+ xrt_result_t xret = XRT_SUCCESS;
11321132+ pthread_rwlock_wrlock(&uso->lock);
11331133+11341134+11351135+ void *ptr = NULL;
11361136+ uint64_t key = (uint64_t)(intptr_t)xdev->tracking_origin;
11371137+ u_hashmap_int_find(uso->xto_map, key, &ptr);
11381138+ if (ptr == NULL) {
11391139+ U_LOG_E("Device doesn't have space associated with it!");
11401140+ xret = XRT_ERROR_DEVICE_NOT_ATTACHABLE;
11411141+ goto err_unlock;
11421142+ }
11431143+11441144+ struct u_space *us = (struct u_space *)ptr;
11451145+ if (us->type != U_SPACE_TYPE_ATTACHABLE) {
11461146+ U_LOG_E("Device doesn't have a attachable space!");
11471147+ xret = XRT_ERROR_DEVICE_NOT_ATTACHABLE;
11481148+ goto err_unlock;
11491149+ }
11501150+11511151+ // Update the link.
11521152+ u_space_reference(&us->next, u_space(target_space));
11531153+11541154+err_unlock:
11551155+ pthread_rwlock_unlock(&uso->lock);
11561156+11571157+ return xret;
11581158+}
11591159+10361160static void
10371161destroy(struct xrt_space_overseer *xso)
10381162{
···10891213 uso->base.set_tracking_origin_offset = set_tracking_origin_offset;
10901214 uso->base.get_reference_space_offset = get_reference_space_offset;
10911215 uso->base.set_reference_space_offset = set_reference_space_offset;
12161216+ uso->base.add_device = add_device;
12171217+ uso->base.attach_device = attach_device;
10921218 uso->base.destroy = destroy;
10931219 uso->broadcast = broadcast;
10941220···11171243 bool root_is_unbounded,
11181244 bool per_app_local_spaces)
11191245{
11201120- struct xrt_space *root = uso->base.semantic.root; // Convenience
11211246 uso->per_app_local_spaces = per_app_local_spaces;
1122124712481248+ // Add all devices to the space overseer.
11231249 for (uint32_t i = 0; i < xdev_count; i++) {
11241124- struct xrt_device *xdev = xdevs[i];
11251125- struct xrt_tracking_origin *torig = xdev->tracking_origin;
11261126- uint64_t key = (uint64_t)(intptr_t)torig;
11271127- struct xrt_space *xs = NULL;
11281128-11291129- void *ptr = NULL;
11301130- u_hashmap_int_find(uso->xto_map, key, &ptr);
11311131-11321132- if (ptr != NULL) {
11331133- xs = (struct xrt_space *)ptr;
11341134- } else {
11351135- u_space_overseer_create_offset_space(uso, root, &torig->initial_offset, &xs);
11361136- u_hashmap_int_insert(uso->xto_map, key, xs);
12501250+ xrt_result_t xret = add_device_helper(uso, xdevs[i]);
12511251+ if (xret != XRT_SUCCESS) {
12521252+ U_LOG_E("Failed to add device '%s' to space overseer!", xdevs[i]->str);
11371253 }
11381138-11391139- u_space_overseer_link_space_to_device(uso, xs, xdev);
11401254 }
1141125511421256 // If these are set something is probably wrong, but just in case unset them.
+12-2
src/xrt/auxiliary/util/u_var.h
···11// Copyright 2019-2024, Collabora, Ltd.
22+// Copyright 2024-2025, NVIDIA CORPORATION.
23// SPDX-License-Identifier: BSL-1.0
34/*!
45 * @file
···8384 //! Pointer that will be passed to the function as its only argument
8485 void *ptr;
85868787+ /*!
8888+ * Is the pointer pressing down on the button curruently, this is not
8989+ * edge triggered like the callback. For a mouse this means that the
9090+ * pointer is hovering the button and the left is held down.
9191+ */
9292+ xrt_atomic_s32_t downed;
9393+8694 //! Button text, use var `name` if zeroed
8795 char label[64];
8896···229237 U_VAR_KIND_NATIVE_IMAGES_DEBUG,
230238 U_VAR_KIND_LOG_LEVEL,
231239 U_VAR_KIND_RO_TEXT,
232232- U_VAR_KIND_RO_FTEXT,
240240+ U_VAR_KIND_RO_RAW_TEXT,
233241 U_VAR_KIND_RO_I16,
234242 U_VAR_KIND_RO_I32,
235243 U_VAR_KIND_RO_U16,
···247255 U_VAR_KIND_GUI_HEADER,
248256 U_VAR_KIND_GUI_HEADER_BEGIN,
249257 U_VAR_KIND_GUI_HEADER_END,
258258+ U_VAR_KIND_GUI_SAMELINE,
250259 U_VAR_KIND_BUTTON,
251260 U_VAR_KIND_COMBO,
252261 U_VAR_KIND_HISTOGRAM_F32,
···389398 ADD_FUNC(native_images_debug, struct u_native_images_debug, NATIVE_IMAGES_DEBUG) \
390399 ADD_FUNC(log_level, enum u_logging_level, LOG_LEVEL) \
391400 ADD_FUNC(ro_text, const char, RO_TEXT) \
392392- ADD_FUNC(ro_ftext, const char, RO_FTEXT) \
401401+ ADD_FUNC(ro_raw_text, const char, RO_RAW_TEXT) \
393402 ADD_FUNC(ro_i16, int16_t, RO_I16) \
394403 ADD_FUNC(ro_i32, int32_t, RO_I32) \
395404 ADD_FUNC(ro_u16, uint16_t, RO_U16) \
···407416 ADD_FUNC(gui_header, bool, GUI_HEADER) \
408417 ADD_FUNC(gui_header_begin, bool, GUI_HEADER_BEGIN) \
409418 ADD_FUNC(gui_header_end, bool, GUI_HEADER_END) \
419419+ ADD_FUNC(gui_sameline, void, GUI_SAMELINE) \
410420 ADD_FUNC(button, struct u_var_button, BUTTON) \
411421 ADD_FUNC(combo, struct u_var_combo, COMBO) \
412422 ADD_FUNC(draggable_f32, struct u_var_draggable_f32, DRAGGABLE_F32) \
+52-17
src/xrt/auxiliary/util/u_worker.c
···11// Copyright 2022, Collabora, Ltd.
22+// Copyright 2024-2025, NVIDIA CORPORATION.
23// SPDX-License-Identifier: BSL-1.0
34/*!
45 * @file
···9495 //! Pointer to poll of threads.
9596 struct u_worker_thread_pool *uwtp;
96979797- //! Number of tasks that is pending or being worked on in this group.
9898- size_t current_submitted_tasks_count;
9898+ /*!
9999+ * The number of tasks that are pending execution by a worker.
100100+ * They reside in the pool::tasks array.
101101+ */
102102+ uint32_t current_tasks_in_array;
103103+104104+ /*!
105105+ * Number of tasks that are being worked on.
106106+ * They live inside of the working thread.
107107+ */
108108+ uint32_t current_working_tasks;
99109100100- //! Number of threads that have been released or newly entered wait.
110110+ /*!
111111+ * Number of waiting threads that have been released by a worker,
112112+ * or a thread that has started waiting (see u_worker_group_wait_all).
113113+ */
101114 size_t released_count;
102115103116 struct
···143156 continue;
144157 }
145158146146- *out_task = p->tasks[i];
159159+ struct task task = p->tasks[i];
147160 p->tasks[i] = (struct task){NULL, NULL, NULL};
161161+148162 p->tasks_in_array_count--;
163163+ task.g->current_tasks_in_array--;
164164+ task.g->current_working_tasks++;
165165+166166+ *out_task = task;
167167+149168 return;
150169 }
151170···164183165184 p->tasks[i] = (struct task){g, func, data};
166185 p->tasks_in_array_count++;
167167- g->current_submitted_tasks_count++;
186186+ g->current_tasks_in_array++;
168187 return;
169188 }
170189···201220 */
202221203222static bool
204204-locked_group_should_enter_wait_loop(struct pool *p, struct group *g)
223223+locked_group_has_tasks_waiting_or_inflight(const struct group *g)
205224{
206206- if (g->current_submitted_tasks_count == 0) {
225225+ if (g->current_tasks_in_array == 0 && g->current_working_tasks == 0) {
207226 return false;
208227 }
209228210210- // Enter the loop as a released thread.
211211- g->released_count++;
212212-213229 return true;
214230}
215231···237253 */
238254239255 // Tasks available.
240240- if (g->current_submitted_tasks_count > 0) {
256256+ if (locked_group_has_tasks_waiting_or_inflight(g)) {
241257242258 // We have been released or newly entered the loop.
243259 if (g->released_count > 0) {
···265281locked_group_wake_waiter_if_allowed(struct pool *p, struct group *g)
266282{
267283 // Are there still outstanding tasks?
268268- if (g->current_submitted_tasks_count > 0) {
284284+ if (locked_group_has_tasks_waiting_or_inflight(g)) {
269285 return;
270286 }
271287···343359344360 snprintf(t->name, sizeof(t->name), "%s: Worker", p->prefix);
345361 U_TRACE_SET_THREAD_NAME(t->name);
362362+ os_thread_name(&t->thread, t->name);
346363347364 os_mutex_lock(&p->mutex);
348365···373390 // No longer working.
374391 p->working_count--;
375392376376- // Only now decrement the task count on the owning group.
377377- task.g->current_submitted_tasks_count--;
393393+ // We are no longer working on the task.
394394+ task.g->current_working_tasks--;
395395+396396+ // This must hold true.
397397+ assert(task.g->current_tasks_in_array <= p->tasks_in_array_count);
378398379399 // Wake up any waiter.
380400 locked_group_wake_waiter_if_allowed(p, task.g);
···401421 XRT_TRACE_MARKER();
402422 int ret;
403423404404- assert(starting_worker_count < thread_count);
405405- if (starting_worker_count >= thread_count) {
424424+ assert(starting_worker_count <= thread_count);
425425+ if (starting_worker_count > thread_count) {
406426 return NULL;
407427 }
408428···532552 os_mutex_lock(&p->mutex);
533553534554 // Can we early out?
535535- if (!locked_group_should_enter_wait_loop(p, g)) {
555555+ if (!locked_group_has_tasks_waiting_or_inflight(g)) {
536556 os_mutex_unlock(&p->mutex);
537557 return;
538558 }
559559+560560+ /*
561561+ * The released_count is tied to the decrement of worker_limit, that is
562562+ * when a waiting thread is woken up the worker_limit is decreased, and
563563+ * released_count is increased. The waiting thread will then double
564564+ * check that it can be released or not, if it can not be released it
565565+ * will once again donate this thread and increase the worker_limit.
566566+ *
567567+ * If it can be released it will decrement released_count and exit the
568568+ * loop below.
569569+ *
570570+ * So if we increment it here, the loop will increase worker_limit
571571+ * which is what we want.
572572+ */
573573+ g->released_count++;
539574540575 // Wait here until all work been started and completed.
541576 while (locked_group_should_wait(p, g)) {
+12
src/xrt/auxiliary/util/u_worker.hpp
···11// Copyright 2022, Collabora, Ltd.
22+// Copyright 2024-2025, NVIDIA CORPORATION.
23// SPDX-License-Identifier: BSL-1.0
34/*!
45 * @file
···103104 ~SharedThreadGroup()
104105 {
105106 u_worker_group_reference(&mGroup, nullptr);
107107+ }
108108+109109+ /*!
110110+ * In-general it's recommended to use the TaskCollection helper,
111111+ * but some use cases requires direct access to the push function,
112112+ * so it's provided here.
113113+ */
114114+ void
115115+ push(u_worker_group_func_t f, void *data)
116116+ {
117117+ u_worker_group_push(mGroup, f, data);
106118 }
107119108120 friend TaskCollection;
···200200 //! Were timeline semaphore requested, available, and enabled?
201201 bool timeline_semaphore;
202202203203- //! Per stage limit on sampled images (includes combined).
204204- uint32_t max_per_stage_descriptor_sampled_images;
205205-206206- //! Per stage limit on storage images.
207207- uint32_t max_per_stage_descriptor_storage_images;
208208-209203 //! Was synchronization2 requested, available, and enabled?
210204 bool synchronization_2;
211205···215209 //! Was KHR_video_maintenance1 requested, available, and enabled?
216210 bool video_maintenance_1;
217211 } features;
212212+213213+ struct
214214+ {
215215+ //! Maximum number of sampler objects, as created by vkCreateSampler, which can simultaneously exist on
216216+ uint32_t max_sampler_allocation_count;
217217+218218+ //! Maximum number of descriptor sets that can be simultaneously used by a pipeline.
219219+ uint32_t max_bound_descriptor_sets;
220220+221221+ //! Maximum number of samplers that can be included in a pipeline layout.
222222+ uint32_t max_descriptor_set_samplers;
223223+224224+ //! Maximum number of sampled images that can be included in a pipeline layout.
225225+ uint32_t max_descriptor_set_sampled_images;
226226+227227+ //! Maximum number of samplers that can be accessible to a single shader stage in a pipeline layout.
228228+ uint32_t max_per_stage_descriptor_samplers;
229229+230230+ //! Per stage limit on sampled images (includes combined).
231231+ uint32_t max_per_stage_descriptor_sampled_images;
232232+233233+ //! Per stage limit on storage images.
234234+ uint32_t max_per_stage_descriptor_storage_images;
235235+ } limits;
218236219237 //! Is the GPU a tegra device.
220238 bool is_tegra;
···1077109510781096/*!
10791097 * Creates a VkDevice and initialises the VkQueue.
10981098+ *
10991099+ * The @p vk_bundle must have been zero initialized, have the instance functions
11001100+ * loaded and a valid instance.
10801101 *
10811102 * @ingroup aux_vk
10821103 */
+15-6
src/xrt/auxiliary/vk/vk_image_allocator.c
···184184 .sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID,
185185 .externalFormat = a_buffer_format_props.externalFormat,
186186 };
187187- CHAIN(format_android);
188187189188 if (image_format == VK_FORMAT_R8G8B8A8_SRGB) {
190189 // Some versions of Android can't allocate native sRGB, use UNORM and correct gamma later.
···192191193192 // https://www.khronos.org/registry/vulkan/specs/1.0/html/vkspec.html#VUID-VkImageViewCreateInfo-image-01019
194193 image_create_flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
195195-196196- // https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VkImageCreateInfo.html#VUID-VkImageCreateInfo-pNext-02396
197197- format_android.externalFormat = 0;
198198- assert(a_buffer_format_props.format != VK_FORMAT_UNDEFINED); // Make sure there is a Vulkan format.
199199- assert(format_android.externalFormat == 0);
194194+ has_mutable_usage = true;
200195201196 add_format_non_dup(&flh, VK_FORMAT_R8G8B8A8_UNORM);
202197 add_format_non_dup(&flh, VK_FORMAT_R8G8B8A8_SRGB);
198198+ }
199199+200200+ // https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VkImageCreateInfo.html#VUID-VkImageCreateInfo-pNext-02396
201201+ if (has_mutable_usage) {
202202+ format_android.externalFormat = 0;
203203+ // Make sure there is a Vulkan format.
204204+ if (a_buffer_format_props.format == VK_FORMAT_UNDEFINED) {
205205+ VK_WARN(vk,
206206+ "vkGetAndroidHardwareBufferPropertiesANDROID: AHB has no Vulkan-mappable format. "
207207+ "External format chain required, but external formats cannot be mutable. Swapchain "
208208+ "creation may fail!");
209209+ assert(false);
210210+ }
203211 }
204212205213 if (vk_csci_is_format_supported(vk, image_format, info->bits)) {
···256264 // VUID-VkImageCreateInfo-pNext-01974
257265 if (format_android.externalFormat != 0) {
258266 create_info.format = VK_FORMAT_UNDEFINED;
267267+ CHAIN(format_android);
259268 }
260269#endif
261270
+21-5
src/xrt/compositor/client/comp_d3d11_client.cpp
···763763static void
764764client_d3d11_compositor_init_try_timeline_semaphores(struct client_d3d11_compositor *c)
765765{
766766+ struct xrt_compositor_semaphore *xcsem{nullptr};
767767+ HANDLE timeline_semaphore_handle_raw{};
768768+ xrt_result_t xret;
769769+770770+ // Set the value to something non-zero.
766771 c->timeline_semaphore_value = 1;
772772+767773 // See if we can make a "timeline semaphore", also known as ID3D11Fence
768774 if (!c->xcn->base.create_semaphore || !c->xcn->base.layer_commit_with_semaphore) {
769775 return;
770776 }
771771- struct xrt_compositor_semaphore *xcsem = nullptr;
772772- wil::unique_handle timeline_semaphore_handle;
773773- if (XRT_SUCCESS != xrt_comp_create_semaphore(&(c->xcn->base), timeline_semaphore_handle.put(), &xcsem)) {
777777+778778+ /*
779779+ * This call returns a HANDLE in the out_handle argument, it is owned by
780780+ * the returned xrt_compositor_semaphore object we should not track it.
781781+ */
782782+ xret = xrt_comp_create_semaphore( //
783783+ &(c->xcn->base), // xc
784784+ &timeline_semaphore_handle_raw, // out_handle
785785+ &xcsem); // out_xcsem
786786+ if (xret != XRT_SUCCESS) {
774787 D3D_WARN(c, "Native compositor tried but failed to created a timeline semaphore for us.");
775788 return;
776789 }
777790 D3D_INFO(c, "Native compositor created a timeline semaphore for us.");
778791792792+ // Because importFence throws on failure we use this ref.
779793 unique_compositor_semaphore_ref timeline_semaphore{xcsem};
780794781781- // try to import and signal
782782- wil::com_ptr<ID3D11Fence> fence = import_fence(*(c->fence_device), timeline_semaphore_handle.get());
795795+ // Try to import the fence.
796796+ wil::com_ptr<ID3D11Fence> fence = import_fence(*(c->fence_device), timeline_semaphore_handle_raw);
797797+798798+ // And try to signal the fence to make sure it works.
783799 HRESULT hr = c->fence_context->Signal(fence.get(), c->timeline_semaphore_value);
784800 if (!SUCCEEDED(hr)) {
785801 D3D_WARN(c,
+20-7
src/xrt/compositor/client/comp_d3d12_client.cpp
···11// Copyright 2019-2024, Collabora, Ltd.
22+// Copyright 2025, NVIDIA CORPORATION.
23// SPDX-License-Identifier: BSL-1.0
34/*!
45 * @file
···801802 }
802803 struct xrt_layer_data d = *data;
803804805805+ // Scale to compensate for power-of-two texture sizes.
806806+ for (uint32_t i = 0; i < data->view_count; ++i) {
807807+ client_d3d12_swapchain_scale_rect(xsc[i], &d.proj.v[i].sub.norm_rect);
808808+ }
804809 // No flip required: D3D12 swapchain image convention matches Vulkan.
805810 return xrt_comp_layer_projection(&c->xcn->base, xdev, xscn, &d);
806811}
···10171022static void
10181023client_d3d12_compositor_init_try_timeline_semaphores(struct client_d3d12_compositor *c)
10191024{
10251025+ struct xrt_compositor_semaphore *xcsem{nullptr};
10261026+ HANDLE timeline_semaphore_handle_raw{};
10271027+ xrt_result_t xret;
10281028+10291029+ // Set the value to something non-zero.
10201030 c->timeline_semaphore_value = 1;
1021103110221032 // See if we can make a "timeline semaphore", also known as ID3D12Fence
···10241034 return;
10251035 }
1026103610271027- struct xrt_compositor_semaphore *xcsem = nullptr;
10281028- wil::unique_handle timeline_semaphore_handle;
10291029- if (XRT_SUCCESS != xrt_comp_create_semaphore(&(c->xcn->base), timeline_semaphore_handle.put(), &xcsem)) {
10371037+ /*
10381038+ * This call returns a HANDLE in the out_handle argument, it is owned by
10391039+ * the returned xrt_compositor_semaphore object we should not track it.
10401040+ */
10411041+ xret = xrt_comp_create_semaphore( //
10421042+ &(c->xcn->base), // xc
10431043+ &timeline_semaphore_handle_raw, // out_handle
10441044+ &xcsem); // out_xcsem
10451045+ if (xret != XRT_SUCCESS) {
10301046 D3D_WARN(c, "Native compositor tried but failed to created a timeline semaphore for us.");
10311047 return;
10321048 }
···10381054 // Try to import, importFence throws on failure.
10391055 wil::com_ptr<ID3D12Fence1> fence = xrt::auxiliary::d3d::d3d12::importFence( //
10401056 *(c->device), //
10411041- timeline_semaphore_handle.get()); //
10421042-10431043- // The fence now owns the handle., importFence throws on failure.
10441044- timeline_semaphore_handle.release();
10571057+ timeline_semaphore_handle_raw); //
1045105810461059 // Check flags.
10471060 D3D12_FENCE_FLAGS flags = fence->GetCreationFlags();
+189-19
src/xrt/compositor/client/comp_egl_client.c
···200200#endif
201201}
202202203203+struct egl_attrs
204204+{
205205+ EGLint api;
206206+ EGLint major;
207207+ EGLint minor;
208208+ bool compat_profile; // Only for desktop OpenGL
209209+ bool robust;
210210+ bool lose_context_on_reset;
211211+ EGLint image_prio;
212212+};
213213+214214+static int
215215+make_egl_attrs(EGLint *attrs, size_t len, const struct egl_attrs *params)
216216+{
217217+ if (params->api != EGL_OPENGL_API && params->api != EGL_OPENGL_ES_API) {
218218+ U_LOG_E("make_egl_attrs: only OpenGL and OpenGL ES is supported");
219219+ return -1;
220220+ }
221221+222222+ size_t attrc = 0;
223223+224224+#define ADD_ATTR(v) \
225225+ do { \
226226+ if (!len) { \
227227+ return -1; \
228228+ } \
229229+ len--; \
230230+ attrs[attrc++] = (v); \
231231+ } while (0)
232232+233233+#define ADD_PAIR(k, v) \
234234+ do { \
235235+ ADD_ATTR(k); \
236236+ ADD_ATTR(v); \
237237+ } while (0)
238238+239239+ EGLint khr_flags = 0;
240240+241241+ ADD_PAIR(EGL_CONTEXT_MAJOR_VERSION, params->major);
242242+ ADD_PAIR(EGL_CONTEXT_MINOR_VERSION, params->minor);
243243+244244+ if (params->api == EGL_OPENGL_API && params->compat_profile) {
245245+ ADD_PAIR(EGL_CONTEXT_OPENGL_PROFILE_MASK, EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT);
246246+ }
247247+248248+ if (params->robust) {
249249+ if (params->api == EGL_OPENGL_ES_API) {
250250+ ADD_PAIR(EGL_CONTEXT_OPENGL_ROBUST_ACCESS_EXT, EGL_TRUE);
251251+ } else if (params->api == EGL_OPENGL_API) {
252252+ khr_flags |= EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR;
253253+ }
254254+ }
255255+256256+ if (params->lose_context_on_reset) {
257257+ if (params->api == EGL_OPENGL_ES_API) {
258258+ ADD_PAIR(EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_EXT, EGL_LOSE_CONTEXT_ON_RESET_EXT);
259259+ } else if (params->api == EGL_OPENGL_API) {
260260+ ADD_PAIR(EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR, EGL_LOSE_CONTEXT_ON_RESET_KHR);
261261+ }
262262+ }
263263+264264+ if (params->api == EGL_OPENGL_API && khr_flags) {
265265+ ADD_PAIR(EGL_CONTEXT_FLAGS_KHR, khr_flags);
266266+ }
267267+268268+ if (params->image_prio && params->image_prio != EGL_CONTEXT_PRIORITY_MEDIUM_IMG) {
269269+ ADD_PAIR(EGL_CONTEXT_PRIORITY_LEVEL_IMG, params->image_prio);
270270+ }
271271+272272+ ADD_ATTR(EGL_NONE);
273273+274274+#undef ADD_ATTR
275275+#undef ADD_PAIR
276276+277277+ return attrc;
278278+}
279279+280280+static void
281281+print_egl_attrs(EGLint *attrs)
282282+{
283283+ int i = 0;
284284+ EGL_DEBUG("egl attributes:");
285285+ do {
286286+ EGL_DEBUG("> attr: %d 0x%x", i, attrs[i]);
287287+ } while (attrs[i++] != EGL_NONE);
288288+}
289289+290290+static EGLint
291291+get_reset_strategy(EGLint api_type, EGLDisplay display, EGLContext app_context)
292292+{
293293+ EGLint ext;
294294+ switch (api_type) {
295295+ case EGL_OPENGL_ES_API: ext = EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_EXT; break;
296296+ case EGL_OPENGL_API:
297297+ /* This is non-standard, but supported in mesa */
298298+ ext = EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR;
299299+ break;
300300+ default: return 0;
301301+ }
302302+303303+ EGLint strategy = 0;
304304+ if (!eglQueryContext(display, app_context, ext, &strategy)) {
305305+ return 0;
306306+ }
307307+ return strategy;
308308+}
309309+203310static xrt_result_t
204311create_context(
205312 EGLDisplay display, EGLConfig config, EGLContext app_context, EGLint api_type, EGLContext *out_our_context)
···207314 EGLint old_api_type = eglQueryAPI();
208315209316 eglBindAPI(api_type);
317317+ const char *api_string =
318318+ api_type == EGL_OPENGL_API ? "opengl" : (api_type == EGL_OPENGL_ES_API ? "opengl_es" : "");
210319211211- size_t attrc = 0;
212212- EGLint attrs[9] = {0};
213213-214214- attrs[attrc++] = EGL_CONTEXT_MAJOR_VERSION;
215215- attrs[attrc++] = 3;
216320 // Panfrost only supports 3.1
217217- attrs[attrc++] = EGL_CONTEXT_MINOR_VERSION;
218218- attrs[attrc++] = 1;
321321+ const EGLint major = 3;
322322+ const EGLint minor = 1;
219323220220- if (api_type == EGL_OPENGL_API) {
221221- attrs[attrc++] = EGL_CONTEXT_OPENGL_PROFILE_MASK;
222222- attrs[attrc++] = EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT;
324324+ EGLint image_prio;
325325+ if (!eglQueryContext(display, app_context, EGL_CONTEXT_PRIORITY_LEVEL_IMG, &image_prio)) {
326326+ image_prio = 0;
223327 }
224328225225- EGLint strategy;
226226- if (api_type == EGL_OPENGL_ES_API &&
227227- eglQueryContext(display, app_context, EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_EXT, &strategy)) {
228228- attrs[attrc++] = EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_EXT;
229229- attrs[attrc++] = strategy;
329329+ const size_t attrs_len = 21;
330330+ EGLint attrs[4][attrs_len];
331331+ int attrc = 0;
332332+333333+ struct egl_attrs build_attrs = {
334334+ .api = api_type,
335335+ .major = major,
336336+ .minor = minor,
337337+ .compat_profile = true,
338338+ .image_prio = image_prio,
339339+ };
340340+341341+ /* If the source context was created with robust and/or lose notify enabled,
342342+ * then we need to enable it too or we get EGL_BAD_MATCH.
343343+ *
344344+ * Problems with those options:
345345+ * 1. Different way to activate them for OpenGL and OpenGL ES.
346346+ * 2. No standard way to know if the options were used to create the original context.
347347+ * (with an exception for OpenGL ES)
348348+ *
349349+ * So, the most reliable way to create our shared context is to try all those combinations...
350350+ */
351351+ EGLint reset_strategy = get_reset_strategy(api_type, display, app_context);
352352+ EGL_DEBUG("Current reset strategy (%s): 0x%x", api_string, reset_strategy);
353353+ if (reset_strategy) {
354354+ build_attrs.lose_context_on_reset = (reset_strategy == EGL_LOSE_CONTEXT_ON_RESET);
355355+356356+ if (make_egl_attrs(attrs[attrc++], attrs_len, &build_attrs) == -1) {
357357+ return XRT_ERROR_OPENGL;
358358+ }
359359+360360+ build_attrs.robust = true;
361361+ if (make_egl_attrs(attrs[attrc++], attrs_len, &build_attrs) == -1) {
362362+ return XRT_ERROR_OPENGL;
363363+ }
364364+ } else {
365365+ /* Try context with all optional features disabled first */
366366+ if (make_egl_attrs(attrs[attrc++], attrs_len, &build_attrs) == -1) {
367367+ return XRT_ERROR_OPENGL;
368368+ }
369369+370370+ /* Then, try all combination of robustness and lose notification */
371371+ build_attrs.robust = false;
372372+ build_attrs.lose_context_on_reset = true;
373373+ if (make_egl_attrs(attrs[attrc++], attrs_len, &build_attrs) == -1) {
374374+ return XRT_ERROR_OPENGL;
375375+ }
376376+377377+ build_attrs.robust = true;
378378+ build_attrs.lose_context_on_reset = false;
379379+ if (make_egl_attrs(attrs[attrc++], attrs_len, &build_attrs) == -1) {
380380+ return XRT_ERROR_OPENGL;
381381+ }
382382+383383+ build_attrs.robust = true;
384384+ build_attrs.lose_context_on_reset = true;
385385+ if (make_egl_attrs(attrs[attrc++], attrs_len, &build_attrs) == -1) {
386386+ return XRT_ERROR_OPENGL;
387387+ }
230388 }
231389232232- attrs[attrc++] = EGL_NONE;
233233- assert(attrc <= ARRAY_SIZE(attrs));
390390+ assert((unsigned)attrc <= ARRAY_SIZE(attrs));
234391235235- EGLContext our_context = eglCreateContext(display, config, app_context, attrs);
392392+ EGLContext our_context = EGL_NO_CONTEXT;
393393+ const char *last_error = "";
394394+ for (int i = 0; i != attrc; i++) {
395395+ our_context = eglCreateContext(display, config, app_context, attrs[i]);
396396+ if (our_context != EGL_NO_CONTEXT) {
397397+ EGL_DEBUG("eglCreateContext (%s): try %d, context created", api_string, i);
398398+ print_egl_attrs(attrs[i]);
399399+ break;
400400+ }
401401+402402+ last_error = egl_error_str(eglGetError());
403403+ EGL_DEBUG("eglCreateContext (%s): try %d, %s", api_string, i, last_error);
404404+ print_egl_attrs(attrs[i]);
405405+ }
236406237407 // Restore old API type.
238408 if (old_api_type == EGL_NONE) {
···240410 }
241411242412 if (our_context == EGL_NO_CONTEXT) {
243243- EGL_ERROR("eglCreateContext: %s", egl_error_str(eglGetError()));
413413+ EGL_ERROR("eglCreateContext (%s): %s", api_string, last_error);
244414 return XRT_ERROR_OPENGL;
245415 }
246416
+27-11
src/xrt/compositor/main/comp_compositor.c
···1031103110321032 COMP_DEBUG(c, "Doing init %p", (void *)c);
1033103310341034- if (xdev->hmd->view_count == 0) {
10351035- U_LOG_E("Bug detected: HMD \"%s\" does not set xdev->hmd.view_count. Value must be > 0!", xdev->str);
10341034+ uint32_t view_count = xdev->hmd->view_count;
10351035+ enum xrt_view_type view_type = 0; // Invalid
10361036+10371037+ switch (view_count) {
10381038+ case 0:
10391039+ U_LOG_E("Bug detected: HMD \"%s\" xdev->hmd.view_count must be > 0!", xdev->str);
10361040 assert(xdev->hmd->view_count > 0);
10411041+ break;
10421042+ case 1: view_type = XRT_VIEW_TYPE_MONO; break;
10431043+ case 2: view_type = XRT_VIEW_TYPE_STEREO; break;
10441044+ default:
10451045+ U_LOG_E("Bug detected: HMD \"%s\" xdev->hmd.view_count must be 1 or 2, not %u!", xdev->str, view_count);
10461046+ assert(view_count == 1 && view_count == 2);
10471047+ break;
10371048 }
1038104910391050 // Do this as early as possible.
···11211132 struct xrt_system_compositor_info sys_info_storage = {0};
11221133 struct xrt_system_compositor_info *sys_info = &sys_info_storage;
1123113411241124- // Required by OpenXR spec.
11251125- sys_info->max_layers = XRT_MAX_LAYERS;
11351135+ // Required by OpenXR spec (minimum 16).
11361136+ sys_info->max_layers = render_max_layers_capable( //
11371137+ get_vk(c), //
11381138+ c->settings.use_compute, //
11391139+ XRT_MAX_LAYERS); //
11261140 sys_info->compositor_vk_deviceUUID = c->settings.selected_gpu_deviceUUID;
11271141 sys_info->client_vk_deviceUUID = c->settings.client_gpu_deviceUUID;
11281142 sys_info->client_d3d_deviceLUID = c->settings.client_gpu_deviceLUID;
···11311145 sys_info->supports_fov_mutable = true;
1132114611331147 // clang-format off
11341134- uint32_t view_count = xdev->hmd->view_count;
11351148 for (uint32_t i = 0; i < view_count; ++i) {
11361149 uint32_t w = (uint32_t)(xdev->hmd->views[i].display.w_pixels * scale);
11371150 uint32_t h = (uint32_t)(xdev->hmd->views[i].display.h_pixels * scale);
11381151 uint32_t w_2 = xdev->hmd->views[i].display.w_pixels * 2;
11391152 uint32_t h_2 = xdev->hmd->views[i].display.h_pixels * 2;
1140115311411141- sys_info->views[i].recommended.width_pixels = w;
11421142- sys_info->views[i].recommended.height_pixels = h;
11431143- sys_info->views[i].recommended.sample_count = 1;
11441144- sys_info->views[i].max.width_pixels = w_2;
11451145- sys_info->views[i].max.height_pixels = h_2;
11461146- sys_info->views[i].max.sample_count = 1;
11541154+ sys_info->view_configs[0].views[i].recommended.width_pixels = w;
11551155+ sys_info->view_configs[0].views[i].recommended.height_pixels = h;
11561156+ sys_info->view_configs[0].views[i].recommended.sample_count = 1;
11571157+ sys_info->view_configs[0].views[i].max.width_pixels = w_2;
11581158+ sys_info->view_configs[0].views[i].max.height_pixels = h_2;
11591159+ sys_info->view_configs[0].views[i].max.sample_count = 1;
11471160 }
11481161 // clang-format on
11621162+ sys_info->view_configs[0].view_type = view_type;
11631163+ sys_info->view_configs[0].view_count = view_count;
11641164+ sys_info->view_config_count = 1; // Only one view config for now.
1149116511501166 // If we can add e.g. video pass-through capabilities, we may need to change (augment) this list.
11511167 // Just copying it directly right now.
···268268{
269269 struct xrt_system_compositor_info *sys_info = &c->sys_info;
270270271271- // Required by OpenXR spec.
271271+ /*
272272+ * Required by OpenXR spec (minimum 16).
273273+ *
274274+ * NOTE: When using Vulkan compositor components (c/render, c/util),
275275+ * call render_max_layers_capable() to clamp this value based on
276276+ * actual device limits.
277277+ */
272278 sys_info->max_layers = XRT_MAX_LAYERS;
273279280280+ uint32_t view_count = xdev->hmd->view_count;
281281+ enum xrt_view_type view_type = 0; // Invalid
282282+283283+ switch (view_count) {
284284+ case 0: U_LOG_E("Bug detected: HMD \"%s\" xdev->hmd.view_count must be > 0!", xdev->str); return false;
285285+ case 1: view_type = XRT_VIEW_TYPE_MONO; break;
286286+ case 2: view_type = XRT_VIEW_TYPE_STEREO; break;
287287+ default:
288288+ U_LOG_E("Bug detected: HMD \"%s\" xdev->hmd.view_count must be 1 or 2, not %u!", xdev->str, view_count);
289289+ return false;
290290+ }
291291+274292 // UUIDs and LUID already set in vk init.
275293 (void)sys_info->compositor_vk_deviceUUID;
276294 (void)sys_info->client_vk_deviceUUID;
277295 (void)sys_info->client_d3d_deviceLUID;
278296 (void)sys_info->client_d3d_deviceLUID_valid;
279279- uint32_t view_count = xdev->hmd->view_count;
280297 // clang-format off
281298 for (uint32_t i = 0; i < view_count; ++i) {
282282- sys_info->views[i].recommended.width_pixels = RECOMMENDED_VIEW_WIDTH;
283283- sys_info->views[i].recommended.height_pixels = RECOMMENDED_VIEW_HEIGHT;
284284- sys_info->views[i].recommended.sample_count = 1;
285285- sys_info->views[i].max.width_pixels = MAX_VIEW_WIDTH;
286286- sys_info->views[i].max.height_pixels = MAX_VIEW_HEIGHT;
287287- sys_info->views[i].max.sample_count = 1;
299299+ sys_info->view_configs[0].views[i].recommended.width_pixels = RECOMMENDED_VIEW_WIDTH;
300300+ sys_info->view_configs[0].views[i].recommended.height_pixels = RECOMMENDED_VIEW_HEIGHT;
301301+ sys_info->view_configs[0].views[i].recommended.sample_count = 1;
302302+ sys_info->view_configs[0].views[i].max.width_pixels = MAX_VIEW_WIDTH;
303303+ sys_info->view_configs[0].views[i].max.height_pixels = MAX_VIEW_HEIGHT;
304304+ sys_info->view_configs[0].views[i].max.sample_count = 1;
288305 }
289306 // clang-format on
307307+ sys_info->view_configs[0].view_type = view_type;
308308+ sys_info->view_configs[0].view_count = view_count;
309309+ sys_info->view_config_count = 1; // Only one view config type supported.
290310291311 // Copy the list directly.
292312 assert(xdev->hmd->blend_mode_count <= XRT_MAX_DEVICE_BLEND_MODES);
···436456437457 struct xrt_fov fovs[2] = {0};
438458 struct xrt_pose poses[2] = {0};
439439- xrt_result_t xret =
440440- xrt_device_get_view_poses(c->xdev, &default_eye_relation, display_time_ns, 2, &head_relation, fovs, poses);
459459+460460+ xrt_result_t xret = xrt_device_get_view_poses( //
461461+ c->xdev, //
462462+ &default_eye_relation, //
463463+ display_time_ns, //
464464+ XRT_VIEW_TYPE_STEREO, //
465465+ 2, //
466466+ &head_relation, //
467467+ fovs, //
468468+ poses); //
441469 if (xret != XRT_SUCCESS) {
442470 return xret;
443471 }
+48-12
src/xrt/compositor/render/render_interface.h
···11// Copyright 2019-2023, Collabora, Ltd.
22+// Copyright 2025, NVIDIA CORPORATION.
23// SPDX-License-Identifier: BSL-1.0
34/*!
45 * @file
···5859#define RENDER_MAX_LAYERS (XRT_MAX_LAYERS)
59606061/*!
6262+ * The maximum number samplers per view that can be used by the compute shader
6363+ * for layer composition (layer.comp)
6464+ */
6565+#define RENDER_CS_MAX_SAMPLERS_PER_VIEW 2
6666+6767+/*!
6168 * Max number of images that can be given at a single time to the layer
6269 * squasher in a single dispatch.
6370 */
6464-#define RENDER_MAX_IMAGES_SIZE (RENDER_MAX_LAYERS * XRT_MAX_VIEWS)
6565-#define RENDER_MAX_IMAGES_COUNT(RENDER_RESOURCES) (RENDER_MAX_LAYERS * RENDER_RESOURCES->view_count)
7171+#define RENDER_MAX_IMAGES_SIZE (RENDER_MAX_LAYERS * RENDER_CS_MAX_SAMPLERS_PER_VIEW)
66726773/*!
6874 * Maximum number of times that the layer squasher shader can run per
···8793//! The binding that the shared layer fragment shader has its source on.
8894#define RENDER_BINDING_LAYER_SHARED_SRC 1
89959696+/*!
9797+ * The maximum number samplers per view that can be used by the compute shader
9898+ * for layer composition (layer.comp)
9999+ */
100100+#define RENDER_CS_MAX_SAMPLERS_PER_VIEW 2
9010191102/*
92103 *
93104 * Util functions.
94105 *
95106 */
107107+108108+/*!
109109+ * Determines the maximum number of compositor layers supported based on Vulkan
110110+ * device limits and the composition path being used.
111111+ *
112112+ * @param vk Vulkan bundle containing device properties
113113+ * @param use_compute True if using compute pipeline path, false for graphics
114114+ * @param desired_max_layers Maximum layers requested by the compositor
115115+ * @return Actual maximum layers supported, clamped by device limits (minimum 16)
116116+ *
117117+ */
118118+uint32_t
119119+render_max_layers_capable(const struct vk_bundle *vk, bool use_compute, uint32_t desired_max_layers);
9612097121/*!
98122 * Create a simplified projection matrix for timewarp.
···12071231 struct
12081232 {
12091233 uint32_t value;
12101210- uint32_t padding[3];
12341234+ uint32_t padding[3]; // Padding up to a vec4.
12111235 } layer_count;
1212123612131237 struct xrt_normalized_rect pre_transform;
12141238 struct xrt_normalized_rect post_transforms[RENDER_MAX_LAYERS];
1215123912161216- //! std140 uvec2, corresponds to enum xrt_layer_type and unpremultiplied alpha.
12401240+ /*!
12411241+ * Corresponds to enum xrt_layer_type and unpremultiplied alpha.
12421242+ *
12431243+ * std140 uvec2, because it is an array it gets padded to vec4.
12441244+ */
12171245 struct
12181246 {
12191219- uint32_t val;
12201220- uint32_t unpremultiplied;
12211221- uint32_t padding[XRT_MAX_VIEWS];
12221222- } layer_type[RENDER_MAX_LAYERS];
12471247+ uint32_t layer_type;
12481248+ uint32_t unpremultiplied_alpha;
12491249+ uint32_t _padding0;
12501250+ uint32_t _padding1;
12511251+ } layer_data[RENDER_MAX_LAYERS];
1223125212241224- //! Which image/sampler(s) correspond to each layer.
12531253+ /*!
12541254+ * Which image/sampler(s) correspond to each layer.
12551255+ *
12561256+ * std140 uvec2, because it is an array it gets padded to vec4.
12571257+ */
12251258 struct
12261259 {
12271227- uint32_t images[XRT_MAX_VIEWS];
12601260+ uint32_t color_image_index;
12611261+ uint32_t depth_image_index;
12621262+12281263 //! @todo Implement separated samplers and images (and change to samplers[2])
12291229- uint32_t padding[XRT_MAX_VIEWS];
12301230- } images_samplers[RENDER_MAX_LAYERS];
12641264+ uint32_t _padding0;
12651265+ uint32_t _padding1;
12661266+ } image_info[RENDER_MAX_LAYERS];
1231126712321268 //! Shared between cylinder and equirect2.
12331269 struct xrt_matrix_4x4 mv_inverse[RENDER_MAX_LAYERS];
···8787 *
8888 */
89899090+uint32_t
9191+render_max_layers_capable(const struct vk_bundle *vk, bool use_compute, uint32_t desired_max_layers)
9292+{
9393+ /*!
9494+ * Graphics pipeline:
9595+ *
9696+ * This path has no relevant Vulkan device limits that would
9797+ * constrain the maximum number of layers (each layer uses a single descriptor
9898+ * set bound individually per draw).
9999+ */
100100+ if (!use_compute) {
101101+ // The min required by OpenXR spec is 16.
102102+ return MAX(desired_max_layers, 16);
103103+ }
104104+105105+ /*!
106106+ * Compute pipeline:
107107+ *
108108+ * Clamp max layers based on compute pipeline descriptor limits.
109109+ *
110110+ * The compute path uses an array of combined image samplers, with
111111+ * @ref samplers_per_layer samplers needed per layer. We check both the
112112+ * per-stage sampler and sampled image limits, then calculate the
113113+ * maximum number of complete layers that fit within those limits.
114114+ */
115115+ uint32_t desired_image_sampler_count = desired_max_layers * RENDER_CS_MAX_SAMPLERS_PER_VIEW;
116116+117117+ const uint32_t max_sizes[] = {
118118+ vk->limits.max_per_stage_descriptor_samplers,
119119+ vk->limits.max_per_stage_descriptor_sampled_images,
120120+ };
121121+ for (uint32_t i = 0; i < ARRAY_SIZE(max_sizes); ++i) {
122122+ desired_image_sampler_count = MIN(desired_image_sampler_count, max_sizes[i]);
123123+ }
124124+125125+ const uint32_t calculated_max_layers = desired_image_sampler_count / RENDER_CS_MAX_SAMPLERS_PER_VIEW;
126126+127127+ if (calculated_max_layers < 16) {
128128+ VK_WARN(vk,
129129+ "Device supports only %u compositor layers due to Vulkan limits. "
130130+ "which is below Vulkan minimum of 16. "
131131+ "This may indicate a driver bug. Attempting 16 anyway.",
132132+ calculated_max_layers);
133133+ }
134134+135135+ // The min required by OpenXR spec is 16.
136136+ return MAX(calculated_max_layers, 16);
137137+}
138138+90139void
91140render_calc_time_warp_matrix(const struct xrt_pose *src_pose,
92141 const struct xrt_fov *src_fov,
+38-24
src/xrt/compositor/shaders/layer.comp
···11// Copyright 2021-2023, Collabora Ltd.
22+// Copyright 2025, NVIDIA CORPORATION.
23// Author: Jakob Bornecrantz <jakob@collabora.com>
34// Author: Christoph Haag <christoph.haag@collabora.com>
45// SPDX-License-Identifier: BSL-1.0
···78#extension GL_GOOGLE_include_directive : require
89910#include "srgb.inc.glsl"
1111+#include "layer_defines.inc.glsl"
1212+1313+1414+struct layer_data
1515+{
1616+ uint layer_type;
1717+ uint unpremultiplied_alpha;
10181111-//! @todo should this be a spcialization const?
1212-#define XRT_LAYER_PROJECTION 0
1313-#define XRT_LAYER_PROJECTION_DEPTH 1
1414-#define XRT_LAYER_QUAD 2
1515-#define XRT_LAYER_CUBE 3
1616-#define XRT_LAYER_CYLINDER 4
1717-#define XRT_LAYER_EQUIRECT1 5
1818-#define XRT_LAYER_EQUIRECT2 6
1919+ // This struct is used in an array, gets padded to vec4.
2020+ uint _padding0;
2121+ uint _padding1;
2222+};
2323+2424+struct image_info
2525+{
2626+ uint color_image_index;
2727+ uint depth_image_index;
2828+2929+ // This struct is used in an array, gets padded to vec4.
3030+ uint _padding0;
3131+ uint _padding1;
3232+};
3333+19342035const float PI = acos(-1);
2136···4055 vec4 pre_transform;
4156 vec4 post_transform[RENDER_MAX_LAYERS];
42574343- // corresponds to enum xrt_layer_type
4444- uvec2 layer_type_and_unpremultiplied[RENDER_MAX_LAYERS];
5858+ // Per-layer data.
5959+ layer_data layer_data[RENDER_MAX_LAYERS];
45604661 // which image/sampler(s) correspond to each layer
4747- ivec2 images_samplers[RENDER_MAX_LAYERS];
6262+ image_info image_info[RENDER_MAX_LAYERS];
48634964 // shared between cylinder and equirect2
5065 mat4 mv_inverse[RENDER_MAX_LAYERS];
···226241227242 vec2 uv_sub = fma(sample_point, ubo.post_transform[layer].zw, ubo.post_transform[layer].xy);
228243229229- uint index = ubo.images_samplers[layer].x;
244244+ uint index = ubo.image_info[layer].color_image_index;
230245#ifdef DEBUG
231246 out_color += texture(source[index], uv_sub) / 2.f;
232247#else
···325340326341 vec2 uv_sub = fma(sample_point, ubo.post_transform[layer].zw, ubo.post_transform[layer].xy);
327342328328- uint index = ubo.images_samplers[layer].x;
343343+ uint index = ubo.image_info[layer].color_image_index;
329344#ifdef DEBUG
330345 out_color += texture(source[index], uv_sub) / 2.0;
331346#else
···341356342357vec4 do_projection(vec2 view_uv, uint layer)
343358{
344344- uint source_image_index = ubo.images_samplers[layer].x;
359359+ uint source_image_index = ubo.image_info[layer].color_image_index;
345360346361 // Do any transformation needed.
347362 vec2 uv = transform_uv(view_uv, layer);
···373388374389vec4 do_quad(vec2 view_uv, uint layer)
375390{
376376- uint source_image_index = ubo.images_samplers[layer].x;
391391+ uint source_image_index = ubo.image_info[layer].color_image_index;
377392378393 // center point of the plane in view space.
379394 vec3 quad_position = ubo.quad_position[layer].xyz;
···459474 for (uint layer = 0; layer < layer_count; layer++) {
460475 vec4 rgba = vec4(0, 0, 0, 0);
461476462462- switch (ubo.layer_type_and_unpremultiplied[layer].x) {
463463- case XRT_LAYER_CYLINDER:
477477+ switch (ubo.layer_data[layer].layer_type) {
478478+ case LAYER_COMP_TYPE_QUAD:
479479+ rgba = do_quad(view_uv, layer);
480480+ break;
481481+ case LAYER_COMP_TYPE_CYLINDER:
464482 rgba = do_cylinder(view_uv, layer);
465483 break;
466466- case XRT_LAYER_EQUIRECT2:
484484+ case LAYER_COMP_TYPE_EQUIRECT2:
467485 rgba = do_equirect2(view_uv, layer);
468486 break;
469469- case XRT_LAYER_PROJECTION:
470470- case XRT_LAYER_PROJECTION_DEPTH:
487487+ case LAYER_COMP_TYPE_PROJECTION:
471488 rgba = do_projection(view_uv, layer);
472489 break;
473473- case XRT_LAYER_QUAD:
474474- rgba = do_quad(view_uv, layer);
475475- break;
476490 default: break;
477491 }
478492479479- if (ubo.layer_type_and_unpremultiplied[layer].y != 0) {
493493+ if (ubo.layer_data[layer].unpremultiplied_alpha != 0) {
480494 // Unpremultipled blend factor of src.a.
481495 accum.rgb = mix(accum.rgb, rgba.rgb, rgba.a);
482496 } else {
+22
src/xrt/compositor/shaders/layer_defines.inc.glsl
···11+// Copyright 2025, NVIDIA CORPORATION.
22+// SPDX-License-Identifier: BSL-1.0
33+44+/*
55+ * This file is included by both C code, like comp_render_cs.c, and GLSL code,
66+ * like layer.comp, so it uses a very limited set of features of both.
77+ */
88+#ifndef LAYER_DEFINES_INC_GLSL
99+#define LAYER_DEFINES_INC_GLSL
1010+1111+//! To handle invalid/unsupported layer types.
1212+#define LAYER_COMP_TYPE_NOOP 0
1313+//! Maps to XRT_LAYER_QUAD (not numerically)
1414+#define LAYER_COMP_TYPE_QUAD 1
1515+//! Maps to XRT_LAYER_CYLINDER (not numerically)
1616+#define LAYER_COMP_TYPE_CYLINDER 2
1717+//! Maps to XRT_LAYER_EQUIRECT2 (not numerically)
1818+#define LAYER_COMP_TYPE_EQUIRECT2 3
1919+//! Maps to XRT_LAYER_PROJECTION[_DEPTH] (not numerically)
2020+#define LAYER_COMP_TYPE_PROJECTION 4
2121+2222+#endif // LAYER_DEFINES_INC_GLSL
···103103 //! Nominal frame interval
104104 uint64_t nominal_frame_interval_ns;
105105 enum xrt_scanout_direction scanout_direction;
106106- uint64_t scanout_time_ns;
106106+ int64_t scanout_time_ns;
107107 } screens[1];
108108109109 /*!
···406406 struct xrt_facial_expression_set *out_value);
407407408408 /*!
409409+ * @brief Gets the face tracking calibration state
410410+ *
411411+ * @param[in] xdev The device.
412412+ * @param[in] out_value Is face tracking calibrated?
413413+ *
414414+ * @see xrt_input_name
415415+ */
416416+ xrt_result_t (*get_face_calibration_state_android)(struct xrt_device *xdev, bool *out_face_is_calibrated);
417417+418418+ /*!
409419 * @brief Get the body skeleton in T-pose, used to query the skeleton hierarchy, scale, proportions etc
410420 *
411421 * @param[in] xdev The device.
···489499 * @param[out] out_plane_detection_id The id of the new plane detection request generated by the xdev.
490500 * @return generally XRT_SUCCESS, except for internal runtime failures.
491501 */
492492- enum xrt_result (*begin_plane_detection_ext)(struct xrt_device *xdev,
493493- const struct xrt_plane_detector_begin_info_ext *begin_info,
494494- uint64_t plane_detection_id,
495495- uint64_t *out_plane_detection_id);
502502+ xrt_result_t (*begin_plane_detection_ext)(struct xrt_device *xdev,
503503+ const struct xrt_plane_detector_begin_info_ext *begin_info,
504504+ uint64_t plane_detection_id,
505505+ uint64_t *out_plane_detection_id);
496506497507 /*!
498508 * Destroy internal resources associated with plane_detector_id.
···501511 * @param[in] plane_detection_id An id generated by the xdev.
502512 * @return generally XRT_SUCCESS, except for internal runtime failures.
503513 */
504504- enum xrt_result (*destroy_plane_detection_ext)(struct xrt_device *xdev, uint64_t plane_detection_id);
514514+ xrt_result_t (*destroy_plane_detection_ext)(struct xrt_device *xdev, uint64_t plane_detection_id);
505515506516 /*!
507517 * Get the state of a plane detection request.
···511521 * @param[out] out_state The state of the plane detection.
512522 * @return generally XRT_SUCCESS, except for internal runtime failures.
513523 */
514514- enum xrt_result (*get_plane_detection_state_ext)(struct xrt_device *xdev,
515515- uint64_t plane_detection_id,
516516- enum xrt_plane_detector_state_ext *out_state);
524524+ xrt_result_t (*get_plane_detection_state_ext)(struct xrt_device *xdev,
525525+ uint64_t plane_detection_id,
526526+ enum xrt_plane_detector_state_ext *out_state);
517527518528 /*!
519529 * Get results of a plane detection request.
···523533 * @param[out] detections The detected planes, if any.
524534 * @return generally XRT_SUCCESS, except for internal runtime failures.
525535 */
526526- enum xrt_result (*get_plane_detections_ext)(struct xrt_device *xdev,
527527- uint64_t plane_detection_id,
528528- struct xrt_plane_detections_ext *out_detections);
536536+ xrt_result_t (*get_plane_detections_ext)(struct xrt_device *xdev,
537537+ uint64_t plane_detection_id,
538538+ struct xrt_plane_detections_ext *out_detections);
529539530540 /*!
531541 * @brief Get the per-view pose in relation to the view space.
···550560 * input.
551561 * @param[in] at_timestamp_ns This is when the caller wants the poses and FOVs to be from.
552562 * @param[in] view_count Number of views.
563563+ * @param[in] view_type Type of view configuration (mono or stereo).
553564 * @param[out] out_head_relation
554565 * The head pose in the device tracking space.
555566 * Combine with @p out_poses to get the views in
···567578 xrt_result_t (*get_view_poses)(struct xrt_device *xdev,
568579 const struct xrt_vec3 *default_eye_relation,
569580 int64_t at_timestamp_ns,
581581+ enum xrt_view_type view_type,
570582 uint32_t view_count,
571583 struct xrt_space_relation *out_head_relation,
572584 struct xrt_fov *out_fovs,
···754766}
755767756768/*!
769769+ * Helper function for @ref xrt_device::get_face_calibration_state_android.
770770+ *
771771+ * @copydoc xrt_device::get_face_calibration_state_android
772772+ *
773773+ * @public @memberof xrt_device
774774+ */
775775+static inline xrt_result_t
776776+xrt_device_get_face_calibration_state_android(struct xrt_device *xdev, bool *out_face_is_calibrated)
777777+{
778778+ return xdev->get_face_calibration_state_android(xdev, out_face_is_calibrated);
779779+}
780780+781781+/*!
757782 * Helper function for @ref xrt_device::get_body_skeleton.
758783 *
759784 * @copydoc xrt_device::get_body_skeleton
···858883}
859884860885/*!
861861- * Helper function for @ref xrt_device::begin_plane_detection.
886886+ * Helper function for @ref xrt_device::begin_plane_detection_ext.
862887 *
863888 * @public @memberof xrt_device
864889 */
865865-static inline enum xrt_result
890890+static inline xrt_result_t
866891xrt_device_begin_plane_detection_ext(struct xrt_device *xdev,
867892 const struct xrt_plane_detector_begin_info_ext *begin_info,
868893 uint64_t plane_detection_id,
···872897}
873898874899/*!
875875- * Helper function for @ref xrt_device::destroy_plane_detection.
900900+ * Helper function for @ref xrt_device::destroy_plane_detection_ext.
876901 *
877902 * @public @memberof xrt_device
878903 */
879879-static inline enum xrt_result
904904+static inline xrt_result_t
880905xrt_device_destroy_plane_detection_ext(struct xrt_device *xdev, uint64_t plane_detection_id)
881906{
882907 return xdev->destroy_plane_detection_ext(xdev, plane_detection_id);
883908}
884909885910/*!
886886- * Helper function for @ref xrt_device::get_plane_detections.
911911+ * Helper function for @ref xrt_device::get_plane_detections_ext.
887912 *
888913 * @public @memberof xrt_device
889914 */
890890-static inline enum xrt_result
915915+static inline xrt_result_t
891916xrt_device_get_plane_detection_state_ext(struct xrt_device *xdev,
892917 uint64_t plane_detection_id,
893918 enum xrt_plane_detector_state_ext *out_state)
···896921}
897922898923/*!
899899- * Helper function for @ref xrt_device::get_plane_detections.
924924+ * Helper function for @ref xrt_device::get_plane_detections_ext.
900925 *
901926 * @public @memberof xrt_device
902927 */
903903-static inline enum xrt_result
928928+static inline xrt_result_t
904929xrt_device_get_plane_detections_ext(struct xrt_device *xdev,
905930 uint64_t plane_detection_id,
906931 struct xrt_plane_detections_ext *out_detections)
···918943xrt_device_get_view_poses(struct xrt_device *xdev,
919944 const struct xrt_vec3 *default_eye_relation,
920945 int64_t at_timestamp_ns,
946946+ enum xrt_view_type view_type,
921947 uint32_t view_count,
922948 struct xrt_space_relation *out_head_relation,
923949 struct xrt_fov *out_fovs,
924950 struct xrt_pose *out_poses)
925951{
926926- return xdev->get_view_poses(xdev, default_eye_relation, at_timestamp_ns, view_count, out_head_relation,
927927- out_fovs, out_poses);
952952+ return xdev->get_view_poses( //
953953+ xdev, //
954954+ default_eye_relation, //
955955+ at_timestamp_ns, //
956956+ view_type, //
957957+ view_count, //
958958+ out_head_relation, //
959959+ out_fovs, //
960960+ out_poses); //
928961}
929962930963/*!
+21
src/xrt/include/xrt/xrt_instance.h
···11// Copyright 2020-2024, Collabora, Ltd.
22+// Copyright 2025, NVIDIA CORPORATION.
23// SPDX-License-Identifier: BSL-1.0
34/*!
45 * @file
···8182 bool fb_face_tracking2_enabled;
8283 bool meta_body_tracking_full_body_enabled;
8384 bool meta_body_tracking_calibration_enabled;
8585+ bool android_face_tracking_enabled;
8486};
85878688/*!
···126128 */
127129128130 /*!
131131+ * Checks if the system can be created with create_system().
132132+ */
133133+ xrt_result_t (*is_system_available)(struct xrt_instance *xinst, bool *out_available);
134134+135135+ /*!
129136 * Creates all of the system resources like the devices and system
130137 * compositor. The system compositor is optional.
131138 *
···198205 */
199206 struct xrt_instance_android *android_instance;
200207};
208208+209209+210210+/*!
211211+ * @copydoc xrt_instance::create_system
212212+ *
213213+ * Helper for calling through the function pointer.
214214+ *
215215+ * @public @memberof xrt_instance
216216+ */
217217+static inline xrt_result_t
218218+xrt_instance_is_system_available(struct xrt_instance *xinst, bool *out_available)
219219+{
220220+ return xinst->is_system_available(xinst, out_available);
221221+}
201222202223/*!
203224 * @copydoc xrt_instance::create_system
+18
src/xrt/include/xrt/xrt_limits.h
···11// Copyright 2019-2022, Collabora, Ltd.
22+// Copyright 2025, NVIDIA CORPORATION.
23// SPDX-License-Identifier: BSL-1.0
34/*!
45 * @file
···9101011#pragma once
11121313+#include "xrt/xrt_config_os.h"
1214#include "xrt/xrt_compiler.h"
13151416···2022 * Max number of views supported by a compositor, artificial limit.
2123 */
2224#define XRT_MAX_VIEWS 2
2525+2626+/*
2727+ * System needs to support at least 4 views for stereo with foveated inset.
2828+ */
2929+#define XRT_MAX_COMPOSITOR_VIEW_CONFIGS_VIEW_COUNT (XRT_MAX_VIEWS > 4 ? XRT_MAX_VIEWS : 4)
3030+3131+/*
3232+ * Max number of view configurations a system compositor can support simultaneously.
3333+ */
3434+#define XRT_MAX_COMPOSITOR_VIEW_CONFIGS_COUNT 2
23352436/*!
2537 * Maximum number of handles sent in one call.
···6173/*!
6274 * Max number of layers which can be handled at once.
6375 */
7676+#ifdef XRT_OS_ANDROID
7777+#define XRT_MAX_LAYERS 32
7878+#elif defined(XRT_OS_LINUX) || defined(XRT_OS_WINDOWS)
6479#define XRT_MAX_LAYERS 128
8080+#else
8181+#error "Unknown platform, define XRT_MAX_LAYERS for your OS"
8282+#endif
65836684/*!
6785 * @}
+6
src/xrt/include/xrt/xrt_results.h
···251251 * Invoking complete on an already completed future
252252 */
253253 XRT_ERROR_FUTURE_ALREADY_COMPLETE = -41,
254254+255255+ /*!
256256+ * The device's tracking origin is not of type
257257+ * XRT_TRACKING_TYPE_ATTACHABLE.
258258+ */
259259+ XRT_ERROR_DEVICE_NOT_ATTACHABLE = -42,
254260} xrt_result_t;
···11// Copyright 2019-2023, Collabora, Ltd.
22+// Copyright 2025, NVIDIA CORPORATION.
23// SPDX-License-Identifier: BSL-1.0
34/*!
45 * @file
···303304 struct xrt_space **out_local_space,
304305 struct xrt_space **out_local_floor_space);
305306307307+ /*
308308+ *
309309+ * Special inter-monado component functions.
310310+ *
311311+ */
312312+313313+ /*!
314314+ * Add a new device to be tracked by the space overseer. The exact
315315+ * semantic of the space is determined by the implementation of the
316316+ * space overseer. And may be outright rejected by the implementation.
317317+ *
318318+ * After this call completes successfully, the device can be passed
319319+ * into the @ref xrt_space_overseer::locate_device function, but may
320320+ * not be locatable immediately.
321321+ *
322322+ * This function is not intended to be called by the OpenXR state
323323+ * tracker, but by other monado components that need to add devices
324324+ * but does not own the space overseer. Components like the fixer
325325+ * uppers or for push devices.
326326+ *
327327+ * @param[in] xso The space overseer.
328328+ * @param[in] xdev The device to be tracked.
329329+ * @return XRT_SUCCESS if added, otherwise an error code.
330330+ */
331331+ xrt_result_t (*add_device)(struct xrt_space_overseer *xso, struct xrt_device *xdev);
332332+333333+ /*!
334334+ * Attach a device to a different space then it was associated with
335335+ * originally, the space overseer might not support this operation.
336336+ *
337337+ * For some space overseer implementations this operation requires
338338+ * that the device has the tracking origin type of
339339+ * @ref XRT_TRACKING_TYPE_ATTACHABLE. Which space that becomes the
340340+ * parent space of the device when @p space is NULL is undefined,
341341+ * and the device might become un-trackable.
342342+ *
343343+ * @param[in] xso Owning space overseer.
344344+ * @param[in] xdev Device to attach.
345345+ * @param[in] space Space to attach the device to, may be NULL.
346346+ *
347347+ * @return XRT_SUCCESS on success.
348348+ * @return XRT_ERROR_DEVICE_NOT_ATTACHABLE if the device does not have
349349+ * the XRT_TRACKING_TYPE_ATTACHABLE tracking origin type.
350350+ */
351351+ xrt_result_t (*attach_device)(struct xrt_space_overseer *xso, struct xrt_device *xdev, struct xrt_space *space);
352352+353353+354354+ /*
355355+ *
356356+ * Destroy function always comes last.
357357+ *
358358+ */
359359+306360 /*!
307361 * Destroy function.
308362 *
···513567 struct xrt_space **out_local_floor_space)
514568{
515569 return xso->create_local_space(xso, out_local_space, out_local_floor_space);
570570+}
571571+572572+/*!
573573+ * @copydoc xrt_space_overseer::add_device
574574+ *
575575+ * Helper for calling through the function pointer.
576576+ *
577577+ * @public @memberof xrt_space_overseer
578578+ */
579579+static inline xrt_result_t
580580+xrt_space_overseer_add_device(struct xrt_space_overseer *xso, struct xrt_device *xdev)
581581+{
582582+ return xso->add_device(xso, xdev);
583583+}
584584+585585+/*!
586586+ * @copydoc xrt_space_overseer::attach_device
587587+ *
588588+ * Helper for calling through the function pointer.
589589+ *
590590+ * @public @memberof xrt_space_overseer
591591+ */
592592+static inline xrt_result_t
593593+xrt_space_overseer_attach_device(struct xrt_space_overseer *xso, struct xrt_device *xdev, struct xrt_space *space)
594594+{
595595+ return xso->attach_device(xso, xdev, space);
516596}
517597518598/*!
+4
src/xrt/include/xrt/xrt_tracking.h
···11// Copyright 2019, Collabora, Ltd.
22+// Copyright 2025, NVIDIA CORPORATION.
23// SPDX-License-Identifier: BSL-1.0
34/*!
45 * @file
···60616162 // The device(s) are tracked by other methods.
6263 XRT_TRACKING_TYPE_OTHER,
6464+6565+ // The device(s) are (re)attachable.
6666+ XRT_TRACKING_TYPE_ATTACHABLE,
6367};
64686569/*!
···391391ipc_client_xdev_init(struct ipc_client_xdev *icx,
392392 struct ipc_connection *ipc_c,
393393 struct xrt_tracking_origin *xtrack,
394394- uint32_t device_id)
394394+ uint32_t device_id,
395395+ u_device_destroy_function_t destroy_fn)
395396{
396397 // Helpers.
397398 struct ipc_shared_memory *ism = ipc_c->ism;
···400401 // Important fields.
401402 icx->ipc_c = ipc_c;
402403 icx->device_id = device_id;
404404+405405+ /*
406406+ * Fill in not implemented or noop versions first,
407407+ * destroy gets filled in by either device or HMD.
408408+ */
409409+ u_device_populate_function_pointers(&icx->base, ipc_client_xdev_get_tracked_pose, destroy_fn);
403410404411 // Shared implemented functions.
405412 icx->base.update_inputs = ipc_client_xdev_update_inputs;
406406- icx->base.get_tracked_pose = ipc_client_xdev_get_tracked_pose;
407413 icx->base.get_hand_tracking = ipc_client_xdev_get_hand_tracking;
408414 icx->base.get_face_tracking = ipc_client_xdev_get_face_tracking;
409415 icx->base.get_body_skeleton = ipc_client_xdev_get_body_skeleton;
···420426 icx->base.destroy_plane_detection_ext = ipc_client_xdev_destroy_plane_detection_ext;
421427 icx->base.get_plane_detection_state_ext = ipc_client_xdev_get_plane_detection_state_ext;
422428 icx->base.get_plane_detections_ext = ipc_client_xdev_get_plane_detections_ext;
423423-424424- // Not implemented functions, some get overridden.
425425- icx->base.get_view_poses = u_device_ni_get_view_poses;
426426- icx->base.compute_distortion = u_device_ni_compute_distortion;
427427- icx->base.get_visibility_mask = u_device_ni_get_visibility_mask;
428428- icx->base.is_form_factor_available = u_device_ni_is_form_factor_available;
429429- icx->base.get_battery_status = u_device_ni_get_battery_status;
430429431430 // Copying the information from the isdev.
432431 icx->base.device_type = isdev->device_type;
+5-1
src/xrt/ipc/client/ipc_client_xdev.h
···13131414#pragma once
15151616+#include "util/u_device.h"
1717+1818+1619#ifdef __cplusplus
1720extern "C" {
1821#endif
···5861ipc_client_xdev_init(struct ipc_client_xdev *icx,
5962 struct ipc_connection *ipc_c,
6063 struct xrt_tracking_origin *xtrack,
6161- uint32_t device_id);
6464+ uint32_t device_id,
6565+ u_device_destroy_function_t destroy_fn);
62666367/*!
6468 * Frees any memory that was allocated as part of init and resets some pointers.
+29-4
src/xrt/ipc/server/ipc_server.h
···11// Copyright 2020-2023, Collabora, Ltd.
22+// Copyright 2025, NVIDIA CORPORATION.
23// SPDX-License-Identifier: BSL-1.0
34/*!
45 * @file
···22232324#include "shared/ipc_protocol.h"
2425#include "shared/ipc_message_channel.h"
2626+2727+#include "ipc_server_interface.h"
25282629#include <stdio.h>
2730···8992{
9093 //! Link back to the main server.
9194 struct ipc_server *server;
9595+9696+ //! Has the system part of the shm initialized.
9797+ bool has_init_shm_system;
92989399 //! Session for this client.
94100 struct xrt_session *xs;
···188194{
189195 //! The actual device.
190196 struct xrt_device *xdev;
191191-192192- //! Is the IO suppressed for this device.
193193- bool io_active;
194197};
195198196199/*!
···329332 * @public @memberof ipc_server_mainloop
330333 */
331334int
332332-ipc_server_mainloop_init(struct ipc_server_mainloop *ml);
335335+ipc_server_mainloop_init(struct ipc_server_mainloop *ml, bool no_stdin);
333336334337/*!
335338 * @brief Poll the mainloop.
···407410408411 struct os_mutex lock;
409412 } global_state;
413413+414414+ /*!
415415+ * Callbacks for server events.
416416+ */
417417+ const struct ipc_server_callbacks *callbacks;
418418+419419+ /*!
420420+ * User data passed to callbacks.
421421+ */
422422+ void *callback_data;
423423+424424+ //! Disable listening on stdin for server stop.
425425+ bool no_stdin;
410426};
411427428428+/*!
429429+ * Finish setting up the server by creating the system, compositor and devices.
430430+ *
431431+ * @ingroup ipc_server
432432+ */
433433+xrt_result_t
434434+ipc_server_init_system_if_available_locked(struct ipc_server *s,
435435+ volatile struct ipc_client_state *ics,
436436+ bool *out_available);
412437413438/*!
414439 * Get the current state of a client.
···3636{
3737 //! Information passed onto the debug gui.
3838 struct u_debug_gui_create_info udgci;
3939+4040+ //! Flag whether runtime should exit on app disconnect.
4141+ bool exit_on_disconnect;
4242+4343+ //! Disable listening on stdin for server stop.
4444+ bool no_stdin;
3945};
40464147/*!
···7076 * @param[in] data User data given passed into the main function.
7177 */
7278 void (*mainloop_leaving)(struct ipc_server *s, struct xrt_instance *xinst, void *data);
7979+8080+ /*!
8181+ * A new client has connected to the IPC server.
8282+ *
8383+ * param s The IPC server.
8484+ * param client_id The ID of the newly connected client.
8585+ * param data User data given passed into the main function.
8686+ */
8787+ void (*client_connected)(struct ipc_server *s, uint32_t client_id, void *data);
8888+8989+ /*!
9090+ * A client has disconnected from the IPC server.
9191+ *
9292+ * param s The IPC server.
9393+ * param client_id The ID of the newly connected client.
9494+ * param data User data given passed into the main function.
9595+ */
9696+ void (*client_disconnected)(struct ipc_server *s, uint32_t client_id, void *data);
7397};
74987599/*!
···80104int
81105ipc_server_main_common(const struct ipc_server_main_info *ismi, const struct ipc_server_callbacks *iscb, void *data);
82106107107+/*!
108108+ * Asks the server to shut down, this call is asynchronous and will return
109109+ * immediately. Use callbacks to be notified when the server stops.
110110+ *
111111+ * @memberof ipc_server
112112+ */
113113+int
114114+ipc_server_stop(struct ipc_server *s);
8311584116#ifndef XRT_OS_ANDROID
85117
+1-1
src/xrt/ipc/server/ipc_server_mainloop_android.c
···139139}
140140141141int
142142-ipc_server_mainloop_init(struct ipc_server_mainloop *ml)
142142+ipc_server_mainloop_init(struct ipc_server_mainloop *ml, bool no_stdin)
143143{
144144 int ret = init_pipe(ml);
145145 if (ret < 0) {
+5-12
src/xrt/ipc/server/ipc_server_mainloop_linux.c
···11// Copyright 2020-2021, Collabora, Ltd.
22+// Copyright 2025, NVIDIA CORPORATION.
23// SPDX-License-Identifier: BSL-1.0
34/*!
45 * @file
···4647#include <systemd/sd-daemon.h>
4748#endif
48494949-/*
5050- * "XRT_NO_STDIN" option disables stdin and prevents monado-service from terminating.
5151- * This could be useful for situations where there is no proper or in a non-interactive shell.
5252- * Two example scenarios are:
5353- * * IDE terminals,
5454- * * Some scripting environments where monado-service is spawned in the background
5555- */
5656-DEBUG_GET_ONCE_BOOL_OPTION(skip_stdin, "XRT_NO_STDIN", false)
57505851/*
5952 *
···175168}
176169177170static int
178178-init_epoll(struct ipc_server_mainloop *ml)
171171+init_epoll(struct ipc_server_mainloop *ml, bool no_stdin)
179172{
180173 int ret = epoll_create1(EPOLL_CLOEXEC);
181174 if (ret < 0) {
···186179187180 struct epoll_event ev = {0};
188181189189- if (!ml->launched_by_socket && !debug_get_bool_option_skip_stdin()) {
182182+ if (!ml->launched_by_socket && !no_stdin) {
190183 // Can't do this when launched by systemd socket activation by
191184 // default.
192185 // This polls stdin.
···265258}
266259267260int
268268-ipc_server_mainloop_init(struct ipc_server_mainloop *ml)
261261+ipc_server_mainloop_init(struct ipc_server_mainloop *ml, bool no_stdin)
269262{
270263 IPC_TRACE_MARKER();
271264···275268 return ret;
276269 }
277270278278- ret = init_epoll(ml);
271271+ ret = init_epoll(ml, no_stdin);
279272 if (ret < 0) {
280273 ipc_server_mainloop_deinit(ml);
281274 return ret;
···11// Copyright 2020-2023, Collabora, Ltd.
22+// Copyright 2025, NVIDIA CORPORATION.
23// SPDX-License-Identifier: BSL-1.0
34/*!
45 * @file
···200201{
201202 U_TRACE_SET_THREAD_NAME("IPC Client");
202203203203- IPC_INFO(ics->server, "Client %u connected", ics->client_state.id);
204204+ // Call the client connected callback.
205205+ ics->server->callbacks->client_connected( //
206206+ ics->server, //
207207+ ics->client_state.id, //
208208+ ics->server->callback_data); //
204209205210 // Claim the client fd.
206211 int epoll_fd = setup_epoll(ics);
···231236232237 // Detect clients disconnecting gracefully.
233238 if (ret > 0 && (event.events & EPOLLHUP) != 0) {
234234- IPC_INFO(ics->server, "Client disconnected.");
235239 break;
236240 }
237241···249253 break;
250254 }
251255256256+ // This needs to hold true.
257257+ if (cmd_size > IPC_BUF_SIZE) {
258258+ IPC_ERROR(ics->server, "Command too large! (%u > %u)", (uint32_t)cmd_size, IPC_BUF_SIZE);
259259+ break;
260260+ }
261261+252262 // Read the whole command now that we know its size
253263 uint8_t buf[IPC_BUF_SIZE] = {0};
254264···274284 close(epoll_fd);
275285 epoll_fd = -1;
276286287287+ // Call the client disconnected callback.
288288+ ics->server->callbacks->client_disconnected( //
289289+ ics->server, //
290290+ ics->client_state.id, //
291291+ ics->server->callback_data); //
292292+277293 // Following code is same for all platforms.
278294 common_shutdown(ics);
279295}
···297313{
298314 U_TRACE_SET_THREAD_NAME("IPC Client");
299315300300- IPC_INFO(ics->server, "Client connected");
316316+ // Call the client connected callback.
317317+ ics->server->callbacks->client_connected( //
318318+ ics->server, //
319319+ ics->client_state.id, //
320320+ ics->server->callback_data); //
301321302322 while (ics->server->running) {
303323 uint8_t buf[IPC_BUF_SIZE] = {0};
···353373 break;
354374 }
355375 }
376376+377377+ // Call the client disconnected callback.
378378+ ics->server->callbacks->client_disconnected( //
379379+ ics->server, //
380380+ ics->client_state.id, //
381381+ ics->server->callback_data); //
356382357383 // Following code is same for all platforms.
358384 common_shutdown(ics);
+164-48
src/xrt/ipc/server/ipc_server_process.c
···5757 *
5858 */
59596060-DEBUG_GET_ONCE_BOOL_OPTION(exit_on_disconnect, "IPC_EXIT_ON_DISCONNECT", false)
6160DEBUG_GET_ONCE_BOOL_OPTION(exit_when_idle, "IPC_EXIT_WHEN_IDLE", false)
6261DEBUG_GET_ONCE_NUM_OPTION(exit_when_idle_delay_ms, "IPC_EXIT_WHEN_IDLE_DELAY_MS", 5000)
6362DEBUG_GET_ONCE_LOG_OPTION(ipc_log, "IPC_LOG", U_LOGGING_INFO)
6363+6464+/*
6565+ * "XRT_NO_STDIN" option disables stdin and prevents monado-service from terminating.
6666+ * This could be useful for situations where there is no proper or in a non-interactive shell.
6767+ * Two example scenarios are:
6868+ * * IDE terminals,
6969+ * * Some scripting environments where monado-service is spawned in the background
7070+ */
7171+DEBUG_GET_ONCE_BOOL_OPTION(no_stdin, "XRT_NO_STDIN", false)
647265736674/*
···9098static void
9199init_idev(struct ipc_device *idev, struct xrt_device *xdev)
92100{
9393- if (xdev != NULL) {
9494- idev->io_active = true;
9595- idev->xdev = xdev;
9696- } else {
9797- idev->io_active = false;
9898- }
101101+ idev->xdev = xdev;
99102}
100103101104static void
102105teardown_idev(struct ipc_device *idev)
103106{
104104- idev->io_active = false;
107107+ idev->xdev = NULL;
105108}
106109107110static void
···280283}
281284282285XRT_CHECK_RESULT static xrt_result_t
283283-init_shm(struct ipc_server *s, volatile struct ipc_client_state *cs)
286286+init_shm_and_instance_state(struct ipc_server *s, volatile struct ipc_client_state *ics)
284287{
285288 const size_t size = sizeof(struct ipc_shared_memory);
286289 xrt_shmem_handle_t handle;
287290288288- xrt_result_t xret = ipc_shmem_create(size, &handle, (void **)&s->isms[cs->server_thread_index]);
291291+ xrt_result_t xret = ipc_shmem_create(size, &handle, (void **)&s->isms[ics->server_thread_index]);
289292 IPC_CHK_AND_RET(s, xret, "ipc_shmem_create");
290293291294 // we have a filehandle, we will pass this to our client
292292- cs->ism_handle = handle;
295295+ ics->ism_handle = handle;
296296+297297+ // Convenience
298298+ struct ipc_shared_memory *ism = s->isms[ics->server_thread_index];
293299300300+ // Clients expect git version info and timestamp available upon connect.
301301+ snprintf(ism->u_git_tag, IPC_VERSION_NAME_LEN, "%s", u_git_tag);
294302303303+ // Used to synchronize all client's xrt_instance::startup_timestamp.
304304+ ism->startup_timestamp = os_monotonic_get_ns();
305305+306306+ return XRT_SUCCESS;
307307+}
308308+309309+static void
310310+init_system_shm_state(struct ipc_server *s, volatile struct ipc_client_state *cs)
311311+{
295312 /*
296313 *
297314 * Setup the shared memory state.
···300317301318 uint32_t count = 0;
302319 struct ipc_shared_memory *ism = s->isms[cs->server_thread_index];
303303-304304- ism->startup_timestamp = os_monotonic_get_ns();
305320306321 // Setup the tracking origins.
307322 count = 0;
···402417403418 // Setup the HMD
404419 // set view count
405405- assert(s->xsysd->static_roles.head->hmd);
406406- ism->hmd.view_count = s->xsysd->static_roles.head->hmd->view_count;
407407- for (uint32_t view = 0; view < s->xsysd->static_roles.head->hmd->view_count; ++view) {
408408- ism->hmd.views[view].display.w_pixels = s->xsysd->static_roles.head->hmd->views[view].display.w_pixels;
409409- ism->hmd.views[view].display.h_pixels = s->xsysd->static_roles.head->hmd->views[view].display.h_pixels;
410410- }
420420+ const struct xrt_device *xhead = s->xsysd->static_roles.head;
421421+ const struct xrt_hmd_parts *xhmd = xhead != NULL ? xhead->hmd : NULL;
422422+ U_ZERO(&ism->hmd);
423423+ if (xhmd != NULL) {
424424+ ism->hmd.view_count = xhmd->view_count;
425425+ for (uint32_t view = 0; view < xhmd->view_count; ++view) {
426426+ ism->hmd.views[view].display.w_pixels = xhmd->views[view].display.w_pixels;
427427+ ism->hmd.views[view].display.h_pixels = xhmd->views[view].display.h_pixels;
428428+ }
411429412412- for (size_t i = 0; i < s->xsysd->static_roles.head->hmd->blend_mode_count; i++) {
413413- // Not super necessary, we also do this assert in oxr_system.c
414414- assert(u_verify_blend_mode_valid(s->xsysd->static_roles.head->hmd->blend_modes[i]));
415415- ism->hmd.blend_modes[i] = s->xsysd->static_roles.head->hmd->blend_modes[i];
430430+ for (uint32_t i = 0; i < xhmd->blend_mode_count; i++) {
431431+ // Not super necessary, we also do this assert in oxr_system.c
432432+ assert(u_verify_blend_mode_valid(xhmd->blend_modes[i]));
433433+ ism->hmd.blend_modes[i] = xhmd->blend_modes[i];
434434+ }
435435+ ism->hmd.blend_mode_count = xhmd->blend_mode_count;
416436 }
417417- ism->hmd.blend_mode_count = s->xsysd->static_roles.head->hmd->blend_mode_count;
418437419438 // Finally tell the client how many devices we have.
420439 ism->isdev_count = count;
···424443 ism->roles.eyes = find_xdev_index(s, s->xsysd->static_roles.eyes);
425444 ism->roles.face = find_xdev_index(s, s->xsysd->static_roles.face);
426445 ism->roles.body = find_xdev_index(s, s->xsysd->static_roles.body);
446446+427447#define SET_HT_ROLE(SRC) \
428448 ism->roles.hand_tracking.SRC.left = find_xdev_index(s, s->xsysd->static_roles.hand_tracking.SRC.left); \
429449 ism->roles.hand_tracking.SRC.right = find_xdev_index(s, s->xsysd->static_roles.hand_tracking.SRC.right);
430450 SET_HT_ROLE(unobstructed)
431451 SET_HT_ROLE(conforming)
432452#undef SET_HT_ROLE
433433-434434- // Fill out git version info.
435435- snprintf(ism->u_git_tag, IPC_VERSION_NAME_LEN, "%s", u_git_tag);
436436-437437- return XRT_SUCCESS;
438453}
439454440455static void
···455470}
456471457472static xrt_result_t
458458-init_all(struct ipc_server *s, enum u_logging_level log_level)
473473+init_all(struct ipc_server *s,
474474+ enum u_logging_level log_level,
475475+ const struct ipc_server_callbacks *callbacks,
476476+ void *callback_data,
477477+ bool exit_on_disconnect)
459478{
460479 xrt_result_t xret = XRT_SUCCESS;
461480 int ret;
···463482 // First order of business set the log level.
464483 s->log_level = log_level;
465484485485+ // Store callbacks and data
486486+ s->callbacks = callbacks;
487487+ s->callback_data = callback_data;
488488+466489 // This should never fail.
467490 ret = os_mutex_init(&s->global_state.lock);
468491 if (ret < 0) {
···480503481504 // Yes we should be running.
482505 s->running = true;
483483- s->exit_on_disconnect = debug_get_bool_option_exit_on_disconnect();
506506+ s->exit_on_disconnect = exit_on_disconnect;
484507 s->exit_when_idle = debug_get_bool_option_exit_when_idle();
485508 s->last_client_disconnect_ns = 0;
486509 uint64_t delay_ms = debug_get_num_option_exit_when_idle_delay_ms();
···489512 xret = xrt_instance_create(NULL, &s->xinst);
490513 IPC_CHK_WITH_GOTO(s, xret, "xrt_instance_create", error);
491514492492- xret = xrt_instance_create_system(s->xinst, &s->xsys, &s->xsysd, &s->xso, &s->xsysc);
493493- IPC_CHK_WITH_GOTO(s, xret, "xrt_instance_create_system", error);
494494-495495- // Always succeeds.
496496- init_idevs(s);
497497- init_tracking_origins(s);
498498-499499- ret = ipc_server_mainloop_init(&s->ml);
515515+ ret = ipc_server_mainloop_init(&s->ml, s->no_stdin);
500516 if (ret < 0) {
501517 xret = XRT_ERROR_IPC_MAINLOOP_FAILED_TO_INIT;
502518 }
···599615 ics->client_state.z_order = z_order;
600616601617 if (ics->xc != NULL) {
602602- xrt_syscomp_set_state(ics->server->xsysc, ics->xc, visible, focused);
618618+ xrt_syscomp_set_state(ics->server->xsysc, ics->xc, visible, focused, os_monotonic_get_ns());
603619 xrt_syscomp_set_z_order(ics->server->xsysc, ics->xc, z_order);
604620 }
605621}
···771787 return XRT_SUCCESS;
772788}
773789790790+static uint32_t
791791+allocate_id_locked(struct ipc_server *s)
792792+{
793793+ uint32_t id = 0;
794794+ while (id == 0) {
795795+ // Allocate a new one.
796796+ id = ++s->id_generator;
797797+798798+ for (uint32_t i = 0; i < IPC_MAX_CLIENTS; i++) {
799799+ volatile struct ipc_client_state *ics = &s->threads[i].ics;
800800+801801+ // If we find the ID, get a new one by setting to zero.
802802+ if (ics->client_state.id == id) {
803803+ id = 0;
804804+ break;
805805+ }
806806+ }
807807+ }
808808+809809+ // Paranoia.
810810+ if (id == 0) {
811811+ U_LOG_E("Got app(client) id 0, not allowed!");
812812+ assert(id > 0);
813813+ }
814814+815815+ return id;
816816+}
817817+774818775819/*
776820 *
···779823 */
780824781825xrt_result_t
826826+ipc_server_init_system_if_available_locked(struct ipc_server *s,
827827+ volatile struct ipc_client_state *ics,
828828+ bool *out_available)
829829+{
830830+ xrt_result_t xret = XRT_SUCCESS;
831831+832832+ bool available = false;
833833+834834+ if (s->xsys) {
835835+ available = true;
836836+ } else {
837837+ xret = xrt_instance_is_system_available(s->xinst, &available);
838838+ IPC_CHK_WITH_GOTO(s, xret, "xrt_instance_is_system_available", error);
839839+840840+ if (available) {
841841+ xret = xrt_instance_create_system(s->xinst, &s->xsys, &s->xsysd, &s->xso, &s->xsysc);
842842+ IPC_CHK_WITH_GOTO(s, xret, "xrt_instance_create_system", error);
843843+844844+ // Always succeeds.
845845+ init_idevs(s);
846846+ init_tracking_origins(s);
847847+ }
848848+ }
849849+850850+ if (available && ics != NULL && !ics->has_init_shm_system) {
851851+ init_system_shm_state(s, ics);
852852+ ics->has_init_shm_system = true;
853853+ }
854854+855855+ if (out_available) {
856856+ *out_available = available;
857857+ }
858858+859859+ return XRT_SUCCESS;
860860+861861+error:
862862+ return xret;
863863+}
864864+865865+xrt_result_t
782866ipc_server_get_client_app_state(struct ipc_server *s, uint32_t client_id, struct ipc_app_state *out_ias)
783867{
784868 os_mutex_lock(&s->global_state.lock);
···9371021 it->state = IPC_THREAD_STARTING;
93810229391023 // Allocate a new ID, avoid zero.
940940- //! @todo validate ID.
941941- uint32_t id = ++vs->id_generator;
10241024+ uint32_t id = allocate_id_locked(vs);
94210259431026 // Reset everything.
9441027 U_ZERO((struct ipc_client_state *)ics);
94510289461029 // Set state.
10301030+ ics->local_space_overseer_index = UINT32_MAX;
9471031 ics->client_state.id = id;
9481032 ics->imc.ipc_handle = ipc_handle;
9491033 ics->server = vs;
···9551039 ics->plane_detection_ids = NULL;
9561040 ics->plane_detection_xdev = NULL;
9571041958958- xrt_result_t xret = init_shm(vs, ics);
10421042+ xrt_result_t xret = init_shm_and_instance_state(vs, ics);
9591043 if (xret != XRT_SUCCESS) {
96010449611045 // Unlock when we are done.
···9951079 // Allocate the server itself.
9961080 struct ipc_server *s = U_TYPED_CALLOC(struct ipc_server);
997108110821082+ // Can be set by either.
10831083+ s->no_stdin = ismi->no_stdin || debug_get_bool_option_no_stdin();
10841084+9981085#ifdef XRT_OS_WINDOWS
9991086 timeBeginPeriod(1);
10001087#endif
···10061093 */
10071094 u_debug_gui_create(&ismi->udgci, &s->debug_gui);
1008109510091009- xret = init_all(s, log_level);
10961096+ xret = init_all(s, log_level, callbacks, data, ismi->exit_on_disconnect);
10101097 U_LOG_CHK_ONLY_PRINT(log_level, xret, "init_all");
10111098 if (xret != XRT_SUCCESS) {
10121012- // Propegate the failure.
10991099+ // Propagate the failure.
10131100 callbacks->init_failed(xret, data);
10141101 u_debug_gui_stop(&s->debug_gui);
10151102 free(s);
10161103 return -1;
10171104 }
1018110511061106+ // Tell the callbacks we are entering the main-loop.
11071107+ callbacks->mainloop_entering(s, s->xinst, data);
11081108+11091109+ // Early init the system. If not available now, will try again per client request.
11101110+ xret = ipc_server_init_system_if_available_locked( //
11111111+ s, //
11121112+ NULL, // optional - ics
11131113+ NULL); // optional - out_available
11141114+ if (xret != XRT_SUCCESS) {
11151115+ U_LOG_CHK_ONLY_PRINT(log_level, xret, "ipc_server_init_system_if_available_locked");
11161116+ }
11171117+10191118 // Start the debug UI now (if enabled).
10201119 u_debug_gui_start(s->debug_gui, s->xinst, s->xsysd);
10211021-10221022- // Tell the callbacks we are entering the main-loop.
10231023- callbacks->mainloop_entering(s, s->xinst, data);
1024112010251121 // Main loop.
10261122 ret = main_loop(s);
···10441140 return ret;
10451141}
1046114211431143+int
11441144+ipc_server_stop(struct ipc_server *s)
11451145+{
11461146+ s->running = false;
11471147+ return 0;
11481148+}
1047114910481150#ifndef XRT_OS_ANDROID
10491151···10711173 // No-op
10721174}
1073117511761176+void
11771177+client_connected(struct ipc_server *s, uint32_t client_id, void *data)
11781178+{
11791179+ IPC_INFO(s, "Client %u connected", client_id);
11801180+}
11811181+11821182+void
11831183+client_disconnected(struct ipc_server *s, uint32_t client_id, void *data)
11841184+{
11851185+ IPC_INFO(s, "Client %u disconnected", client_id);
11861186+}
11871187+10741188int
10751189ipc_server_main(int argc, char **argv, const struct ipc_server_main_info *ismi)
10761190{
···10781192 .init_failed = init_failed,
10791193 .mainloop_entering = mainloop_entering,
10801194 .mainloop_leaving = mainloop_leaving,
11951195+ .client_connected = client_connected,
11961196+ .client_disconnected = client_disconnected,
10811197 };
1082119810831199 return ipc_server_main_common(ismi, &callbacks, NULL);
+7-6
src/xrt/ipc/shared/ipc_protocol.h
···11// Copyright 2020-2024 Collabora, Ltd.
22+// Copyright 2025, NVIDIA CORPORATION.
23// SPDX-License-Identifier: BSL-1.0
34/*!
45 * @file
···313232333334#define IPC_CRED_SIZE 1 // auth not implemented
3434-#define IPC_BUF_SIZE 512 // must be >= largest message length in bytes
3535+#define IPC_BUF_SIZE 2048 // must be >= largest message length in bytes
3536#define IPC_MAX_VIEWS 8 // max views we will return configs for
3637#define IPC_MAX_FORMATS 32 // max formats our server-side compositor supports
3738#define IPC_MAX_DEVICES 8 // max number of devices we will map using shared mem
3839#define IPC_MAX_LAYERS XRT_MAX_LAYERS
3940#define IPC_MAX_SLOTS 128
4040-#define IPC_MAX_CLIENTS 8
4141+#define IPC_MAX_CLIENTS 32
4142#define IPC_MAX_RAW_VIEWS 32 // Max views that we can get, artificial limit.
4243#define IPC_EVENT_QUEUE_SIZE 32
4344···8485{
8586 enum xrt_device_name name;
86878888+ //! Offset into the array of pairs where this input bindings starts.
8989+ uint32_t first_input_index;
8790 //! Number of inputs.
8891 uint32_t input_count;
8989- //! Offset into the array of pairs where this input bindings starts.
9090- uint32_t first_input_index;
91929292- //! Number of outputs.
9393- uint32_t output_count;
9493 //! Offset into the array of pairs where this output bindings starts.
9594 uint32_t first_output_index;
9595+ //! Number of outputs.
9696+ uint32_t output_count;
9697};
97989899/*!
···11// Copyright 2018-2024, Collabora, Ltd.
22+// Copyright 2025, NVIDIA CORPORATION.
23// SPDX-License-Identifier: BSL-1.0
34/*!
45 * @file
···2223struct oxr_action_set;
2324struct oxr_extension_status;
2425struct oxr_instance;
2626+struct oxr_system;
2527struct oxr_logger;
2628struct oxr_subaction_paths;
2729···9597#define OXR_VERIFY_FUTURE_AND_INIT_LOG(log, thing, new_thing, name) \
9698 OXR_VERIFY_AND_SET_AND_INIT(log, thing, new_thing, oxr_future_ext, FUTURE, name, new_thing->inst); \
9799 OXR_VERIFY_FUTURE_VALID(log, new_thing)
100100+#define OXR_VERIFY_FACE_TRACKER_ANDROID_AND_INIT_LOG(log, thing, new_thing, name) \
101101+ OXR_VERIFY_AND_SET_AND_INIT(log, thing, new_thing, oxr_face_tracker_android, FTRACKER, name, new_thing->sess->sys->inst)
98102// clang-format on
99103100104#define OXR_VERIFY_INSTANCE_NOT_NULL(log, arg, new_arg) OXR_VERIFY_SET(log, arg, new_arg, oxr_instance, INSTANCE);
···200204 } \
201205 } while (false)
202206207207+#define OXR_VERIFY_TWO_CALL_ARRAY(log, inputCapacity, countOutput, array) \
208208+ do { \
209209+ OXR_VERIFY_ARG_NOT_NULL(log, countOutput); \
210210+ if (inputCapacity > 0) { \
211211+ OXR_VERIFY_ARG_NOT_NULL(log, array); \
212212+ } \
213213+ } while (false)
214214+215215+#define OXR_VERIFY_TWO_CALL_ARRAY_AND_TYPE(log, inputCapacity, countOutput, array, type_enum) \
216216+ do { \
217217+ OXR_VERIFY_ARG_NOT_NULL(log, countOutput); \
218218+ if (inputCapacity > 0) { \
219219+ OXR_VERIFY_ARG_NOT_NULL(log, array); \
220220+ for (uint32_t i = 0; i < inputCapacity; ++i) { \
221221+ OXR_VERIFY_ARG_ARRAY_ELEMENT_TYPE(log, array, i, type_enum); \
222222+ } \
223223+ } \
224224+ } while (false)
225225+203226#define OXR_VERIFY_SUBACTION_PATHS(log, count, paths) \
204227 do { \
205228 if (count > 0 && paths == NULL) { \
···232255 \
233256 if (!math_vec3_validate((struct xrt_vec3 *)&p.position)) { \
234257 return oxr_error(log, XR_ERROR_POSE_INVALID, "(" #p ".position) is not valid"); \
258258+ } \
259259+ } while (false)
260260+261261+#define OXR_VERIFY_FORM_FACTOR(log, form_factor) \
262262+ do { \
263263+ XrFormFactor _form_factor = (form_factor); \
264264+ if (_form_factor != XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY && \
265265+ _form_factor != XR_FORM_FACTOR_HANDHELD_DISPLAY) { \
266266+ \
267267+ return oxr_error(log, XR_ERROR_FORM_FACTOR_UNSUPPORTED, \
268268+ "(" #form_factor " == 0x%08x) is not a valid form factor", _form_factor); \
235269 } \
236270 } while (false)
237271···243277 } \
244278 } while (false)
245279280280+#define OXR_VERIFY_VIEW_CONFIG_TYPE_SUPPORTED(log, sys, view_conf) \
281281+ do { \
282282+ XrResult verify_ret = oxr_verify_view_config_type_supported(log, sys, view_conf, #view_conf); \
283283+ if (verify_ret != XR_SUCCESS) { \
284284+ return verify_ret; \
285285+ } \
286286+ } while (false)
287287+246288#define OXR_VERIFY_VIEW_INDEX(log, index) \
247289 do { \
248290 if (index > 2) { \
···308350 } \
309351 } while (false)
310352311311-#define OXR_VERIFY_FORM_FACTOR(log, form_factor) \
312312- do { \
313313- XrFormFactor _form_factor = (form_factor); \
314314- if (_form_factor != XR_FORM_FACTOR_HEAD_MOUNTED_DISPLAY && \
315315- _form_factor != XR_FORM_FACTOR_HANDHELD_DISPLAY) { \
316316- \
317317- return oxr_error(log, XR_ERROR_FORM_FACTOR_UNSUPPORTED, \
318318- "(" #form_factor " == 0x%08x) is not a valid form factor", _form_factor); \
319319- } \
320320- } while (false)
321321-322353#define OXR_VERIFY_HAND_TRACKING_DATA_SOURCE_OR_NULL(log, data_source_info) \
323354 do { \
324355 if (data_source_info != NULL) { \
···333364 do { \
334365 if (OXR_FT->xft == NULL) { \
335366 return oxr_error(LOG, XR_ERROR_FUTURE_INVALID_EXT, "future is not valid"); \
367367+ } \
368368+ } while (false)
369369+370370+#define OXR_VERIFY_ARG_TIME_NOT_ZERO(log, xr_time) \
371371+ do { \
372372+ if (xr_time <= (XrTime)0) { \
373373+ return oxr_error(log, XR_ERROR_TIME_INVALID, "(time == %" PRIi64 ") is not a valid time.", \
374374+ xr_time); \
336375 } \
337376 } while (false)
338377···408447 struct oxr_instance *inst,
409448 XrViewConfigurationType view_conf,
410449 const char *view_conf_name);
450450+451451+XrResult
452452+oxr_verify_view_config_type_supported(struct oxr_logger *log,
453453+ struct oxr_system *sys,
454454+ XrViewConfigurationType view_conf,
455455+ const char *view_conf_name);
411456412457XrResult
413458oxr_verify_XrSessionCreateInfo(struct oxr_logger * /*log*/,
+23-2
src/xrt/state_trackers/oxr/oxr_binding.c
···11// Copyright 2018-2020,2023 Collabora, Ltd.
22+// Copyright 2025, NVIDIA CORPORATION.
23// SPDX-License-Identifier: BSL-1.0
34/*!
45 * @file
···462463 if (name == XRT_DEVICE_INVALID) {
463464 return false;
464465 }
466466+465467 /*
466468 * Map xrt_device_name to an interaction profile XrPath.
467467- * Set *out_p to an oxr_interaction_profile if bindings for that interaction profile XrPath have been suggested.
469469+ *
470470+ * There might be multiple OpenXR interaction profiles that maps to a
471471+ * a single @ref xrt_device_name, so we can't just grab the first one
472472+ * that we find and assume that wasn't bound then there isn't an OpenXR
473473+ * interaction profile bound for that device name. So we will need to
474474+ * keep looping until we find an OpenXR interaction profile, or we run
475475+ * out of interaction profiles that the app has suggested.
476476+ *
477477+ * For XRT_DEVICE_HAND_INTERACTION both the OpenXR hand-interaction
478478+ * profiles maps to it, but the app might only provide binding for one.
479479+ *
480480+ * Set *out_p to an oxr_interaction_profile if bindings for that
481481+ * interaction profile XrPath have been suggested.
468482 */
469483 for (uint32_t i = 0; i < ARRAY_SIZE(profile_templates); i++) {
470484 if (name == profile_templates[i].name) {
471471- if (interaction_profile_find_in_session(log, sess, profile_templates[i].path_cache, out_p)) {
485485+ interaction_profile_find_in_session(log, sess, profile_templates[i].path_cache, out_p);
486486+487487+ /*
488488+ * Keep looping even if the current matching OpenXR
489489+ * interaction profile wasn't suggested by the app.
490490+ * See comment above.
491491+ */
492492+ if (*out_p != NULL) {
472493 return true;
473494 }
474495 }
···11// Copyright 2018-2024, Collabora, Ltd.
22-// Copyright 2023, NVIDIA CORPORATION.
22+// Copyright 2023-2025, NVIDIA CORPORATION.
33// SPDX-License-Identifier: BSL-1.0
44/*!
55 * @file
···99 * @ingroup oxr_main
1010 */
11111212-#include "b_generated_bindings_helpers.h"
1312#include "oxr_bindings/b_oxr_generated_bindings.h"
1413#include "util/u_debug.h"
1514#include "util/u_time.h"
···515514 */
516515517516static bool
517517+find_xdev_name_from_pairs(const struct xrt_device *xdev,
518518+ const struct xrt_binding_profile *xbp,
519519+ enum xrt_input_name from_name,
520520+ enum xrt_input_name *out_name)
521521+{
522522+ if (from_name == 0) {
523523+ *out_name = 0;
524524+ return true; // Not asking for anything, just keep going.
525525+ }
526526+527527+ /*
528528+ * For asymmetrical devices like PS Sense being re-bound to a symmetrical
529529+ * "device" like simple controller can be problemantic as depending on
530530+ * which hand it is menu is bound to different inputs. Instead of making
531531+ * the driver have two completely unique binding mappings per hand, we
532532+ * instead loop over all pairs finding the first match. In other words
533533+ * this means there can be multiple of the same 'from' value in the
534534+ * array of input pairs.
535535+ */
536536+ for (size_t i = 0; i < xbp->input_count; i++) {
537537+ if (from_name != xbp->inputs[i].from) {
538538+ continue;
539539+ }
540540+541541+ // What is the name on the xdev?
542542+ enum xrt_input_name xdev_name = xbp->inputs[i].device;
543543+544544+ // See if we can't find it.
545545+ for (uint32_t k = 0; k < xdev->input_count; k++) {
546546+ if (xdev->inputs[k].name == xdev_name) {
547547+ *out_name = xdev_name;
548548+ return true;
549549+ }
550550+ }
551551+ }
552552+553553+ return false;
554554+}
555555+556556+static bool
518557do_inputs(struct oxr_binding *binding_point,
519558 struct xrt_device *xdev,
520559 struct xrt_binding_profile *xbp,
···523562 uint32_t *input_count)
524563{
525564 enum xrt_input_name name = 0;
526526- if (xbp == NULL) {
527527- name = binding_point->input;
528528- } else {
529529- for (size_t i = 0; i < xbp->input_count; i++) {
530530- if (binding_point->input != xbp->inputs[i].from) {
531531- continue;
532532- }
565565+ enum xrt_input_name dpad_activate_name = 0;
533566534534- // We have found a device mapping.
535535- name = xbp->inputs[i].device;
536536- break;
537537- }
567567+ if (xbp != NULL) {
568568+ bool t1 = find_xdev_name_from_pairs(xdev, xbp, binding_point->input, &name);
569569+ bool t2 = find_xdev_name_from_pairs(xdev, xbp, binding_point->dpad_activate, &dpad_activate_name);
538570539539- // Didn't find a mapping.
540540- if (name == 0) {
571571+ // We couldn't find the needed inputs on the device.
572572+ if (!t1 || !t2) {
541573 return false;
542574 }
575575+ } else {
576576+ name = binding_point->input;
577577+ dpad_activate_name = binding_point->dpad_activate;
543578 }
544579545580 struct xrt_input *input = NULL;
546546- if (oxr_xdev_find_input(xdev, name, &input)) {
547547- uint32_t index = (*input_count)++;
548548- inputs[index].input = input;
549549- inputs[index].xdev = xdev;
550550- inputs[index].bound_path = matched_path;
551551- if (binding_point->dpad_activate != 0) {
552552- struct xrt_input *dpad_activate = NULL;
553553- if (!oxr_xdev_find_input(xdev, binding_point->dpad_activate, &dpad_activate)) {
554554- return false;
555555- }
556556- inputs[index].dpad_activate_name = binding_point->dpad_activate;
557557- inputs[index].dpad_activate = dpad_activate;
581581+ struct xrt_input *dpad_activate_input = NULL;
582582+583583+ // Early out if there is no such input.
584584+ if (!oxr_xdev_find_input(xdev, name, &input)) {
585585+ return false;
586586+ }
587587+588588+ // Check this before allocating an input.
589589+ if (dpad_activate_name != 0) {
590590+ if (!oxr_xdev_find_input(xdev, dpad_activate_name, &dpad_activate_input)) {
591591+ return false;
558592 }
559559- return true;
593593+ }
594594+595595+ uint32_t index = (*input_count)++;
596596+ inputs[index].input = input;
597597+ inputs[index].xdev = xdev;
598598+ inputs[index].bound_path = matched_path;
599599+ if (dpad_activate_input != NULL) {
600600+ inputs[index].dpad_activate_name = dpad_activate_name;
601601+ inputs[index].dpad_activate = dpad_activate_input;
560602 }
561603562562- return false;
604604+ return true;
563605}
564606565607static bool
···771813 if (found) {
772814 if (xbp == NULL) {
773815 oxr_slog(slog, "\t\t\t\tBound (xdev '%s'): %s!\n", xdev->str,
774774- xrt_input_name_string(binding_points[i]->input));
816816+ u_str_xrt_input_name(binding_points[i]->input));
775817 } else {
776818 oxr_slog(slog, "\t\t\t\tBound (xbp)!\n");
777819 }
···16261668 // Only add the input if we can find a transform.
1627166916281670 oxr_slog(slog, "\t\tFinding transforms for '%s' to action '%s' of type '%s'\n",
16291629- xrt_input_name_string(inputs[i].input->name), act_ref->name,
16711671+ u_str_xrt_input_name(inputs[i].input->name), act_ref->name,
16301672 xr_action_type_to_str(act_ref->action_type));
1631167316321674 enum oxr_dpad_region dpad_region;
···16671709 struct xrt_input *input = cache->inputs[i].input;
16681710 enum xrt_input_type t = XRT_GET_INPUT_TYPE(input->name);
16691711 bool active = input->active;
16701670- oxr_slog(slog, "\t\t\t'%s' ('%s') on '%s' (%s)\n", xrt_input_name_string(input->name),
17121712+ oxr_slog(slog, "\t\t\t'%s' ('%s') on '%s' (%s)\n", u_str_xrt_input_name(input->name),
16711713 xrt_input_type_to_str(t), cache->inputs[i].xdev->str,
16721714 active ? "active" : "inactive");
16731715 }
···18621904 struct oxr_action_set *act_set = NULL;
18631905 struct oxr_action_set_attachment *act_set_attached = NULL;
1864190619071907+ /*
19081908+ * No side-effects allowed in this section as we are still
19091909+ * validating and checking for errors at this point.
19101910+ */
19111911+18651912 // Check that all action sets has been attached.
18661913 for (uint32_t i = 0; i < countActionSets; i++) {
18671914 oxr_session_get_action_set_attachment(sess, actionSets[i].actionSet, &act_set_attached, &act_set);
···18711918 "not been attached to this session",
18721919 i, act_set != NULL ? act_set->data->name : "NULL");
18731920 }
19211921+ }
19221922+19231923+ /*
19241924+ * Can only call this function if the session state is focused. This is
19251925+ * not an error and has to be checked after all validation, but before
19261926+ * any side-effects happens.
19271927+ */
19281928+ if (sess->state != XR_SESSION_STATE_FOCUSED) {
19291929+ return XR_SESSION_NOT_FOCUSED;
19301930+ }
19311931+19321932+ /*
19331933+ * Side-effects allowed below, but not validation.
19341934+ */
19351935+19361936+ if (countActionSets == 0) {
19371937+ // Nothing to do.
19381938+ return XR_SUCCESS;
18741939 }
1875194018761941 // Synchronize outputs to this time.
···19221987 OXR_FOR_EACH_SUBACTION_PATH(ACCUMULATE_REQUESTED)
19231988#undef ACCUMULATE_REQUESTED
19241989 }
19901990+19911991+ //! @TODO This validation is not allowed here, must done above.
19251992 if (!any_action_with_subactionpath) {
19261993 return oxr_error(log, XR_ERROR_PATH_UNSUPPORTED,
19271994 "No action with specified subactionpath in actionset");
+96-54
src/xrt/state_trackers/oxr/oxr_instance.c
···9191 inst->system.visibility_mask[i] = NULL;
9292 }
93939494+ os_mutex_destroy(&inst->system_init_lock);
9595+9496 xrt_space_overseer_destroy(&inst->system.xso);
9597 os_mutex_destroy(&inst->system.sync_actions_mutex);
9698 xrt_system_devices_destroy(&inst->system.xsysd);
···199201{
200202 // Reset.
201203 inst->quirks.skip_end_session = false;
204204+ inst->quirks.disable_vulkan_format_depth = false;
202205 inst->quirks.disable_vulkan_format_depth_stencil = false;
203206 inst->quirks.no_validation_error_in_create_ref_space = false;
204207···256259 ret = oxr_error(log, XR_ERROR_RUNTIME_FAILURE, "Failed to init sync action mutex");
257260 return ret;
258261 }
262262+263263+ m_ret = os_mutex_init(&inst->system_init_lock);
264264+ if (m_ret < 0) {
265265+ ret = oxr_error(log, XR_ERROR_RUNTIME_FAILURE, "Failed to init system init mutex");
266266+ return ret;
267267+ }
268268+259269260270#ifdef XRT_FEATURE_CLIENT_DEBUG_GUI
261271 struct u_debug_gui_create_info udgci = {
···331341#ifdef OXR_HAVE_META_body_tracking_calibration
332342 .meta_body_tracking_calibration_enabled = extensions->META_body_tracking_calibration,
333343#endif
344344+#ifdef OXR_HAVE_ANDROID_face_tracking
345345+ .android_face_tracking_enabled = extensions->ANDROID_face_tracking,
346346+#endif
334347 };
335348 snprintf(i_info.app_info.application_name, sizeof(i_info.app_info.application_name), "%s",
336349 createInfo->applicationInfo.applicationName);
···370383 }
371384#endif // XRT_OS_ANDROID
372385373373- struct oxr_system *sys = &inst->system;
374374-375375- // Create the compositor if we are not headless, currently always create it.
376376- bool should_create_compositor = true /* !inst->extensions.MND_headless */;
377377-378378- // Create the system.
379379- if (should_create_compositor) {
380380- xret = xrt_instance_create_system(inst->xinst, &sys->xsys, &sys->xsysd, &sys->xso, &sys->xsysc);
381381- } else {
382382- xret = xrt_instance_create_system(inst->xinst, &sys->xsys, &sys->xsysd, &sys->xso, NULL);
383383- }
384384-385385- if (xret != XRT_SUCCESS) {
386386- ret = oxr_error(log, XR_ERROR_INITIALIZATION_FAILED, "Failed to create the system '%i'", xret);
387387- oxr_instance_destroy(log, &inst->handle);
388388- return ret;
389389- }
390390-391391- ret = XR_SUCCESS;
392392- if (sys->xsysd == NULL) {
393393- ret = oxr_error(log, XR_ERROR_RUNTIME_FAILURE, "Huh?! Field sys->xsysd was NULL?");
394394- } else if (should_create_compositor && sys->xsysc == NULL) {
395395- ret = oxr_error(log, XR_ERROR_RUNTIME_FAILURE, "Huh?! Field sys->xsysc was NULL?");
396396- } else if (!should_create_compositor && sys->xsysc != NULL) {
397397- ret = oxr_error(log, XR_ERROR_RUNTIME_FAILURE, "Huh?! Field sys->xsysc was not NULL?");
398398- }
399399-400400- if (ret != XR_SUCCESS) {
401401- oxr_instance_destroy(log, &inst->handle);
402402- return ret;
403403- }
404404-405405- // Did we find any HMD
406406- // @todo Headless with only controllers?
407407- struct xrt_device *dev = GET_XDEV_BY_ROLE(sys, head);
408408- if (dev == NULL) {
409409- ret = oxr_error(log, XR_ERROR_RUNTIME_FAILURE, "Failed to find any HMD device");
410410- oxr_instance_destroy(log, &inst->handle);
411411- return ret;
412412- }
413413- uint32_t view_count = dev->hmd->view_count;
414414- ret = oxr_system_fill_in(log, inst, XRT_SYSTEM_ID, view_count, &inst->system);
415415- if (ret != XR_SUCCESS) {
416416- oxr_instance_destroy(log, &inst->handle);
417417- return ret;
418418- }
419419-420386 inst->timekeeping = time_state_create(inst->xinst->startup_timestamp);
421387422388 //! @todo check if this (and other creates) failed?
···429395430396 u_var_add_root((void *)inst, "XrInstance", true);
431397432432-#ifdef XRT_FEATURE_CLIENT_DEBUG_GUI
433433- u_debug_gui_start(inst->debug_ui, inst->xinst, sys->xsysd);
434434-#endif
435435-436398 oxr_log(log,
437399 "Instance created\n"
438400 "\tcreateInfo->applicationInfo.applicationName: %s\n"
···442404 "\tcreateInfo->applicationInfo.apiVersion: %d.%d.%d\n"
443405 "\tappinfo.detected.engine.name: %s\n"
444406 "\tappinfo.detected.engine.version: %i.%i.%i\n"
407407+ "\tquirks.disable_vulkan_format_depth: %s\n"
445408 "\tquirks.disable_vulkan_format_depth_stencil: %s\n"
446409 "\tquirks.no_validation_error_in_create_ref_space: %s\n"
447410 "\tquirks.skip_end_session: %s\n"
···457420 inst->appinfo.detected.engine.major, //
458421 inst->appinfo.detected.engine.minor, //
459422 inst->appinfo.detected.engine.patch, //
423423+ inst->quirks.disable_vulkan_format_depth ? "true" : "false", //
460424 inst->quirks.disable_vulkan_format_depth_stencil ? "true" : "false", //
461425 inst->quirks.no_validation_error_in_create_ref_space ? "true" : "false", //
462426 inst->quirks.skip_end_session ? "true" : "false", //
463427 inst->quirks.parallel_views ? "true" : "false" //
464428 ); //
465429466466- debug_print_devices(log, sys);
467467-468468-469430#ifdef XRT_FEATURE_RENDERDOC
470431471432#ifdef __GNUC__
···506467#endif
507468508469 *out_instance = inst;
470470+471471+ return XR_SUCCESS;
472472+}
473473+474474+XrResult
475475+oxr_instance_init_system_locked(struct oxr_logger *log, struct oxr_instance *inst)
476476+{
477477+ struct oxr_system *sys = &inst->system;
478478+ if (sys->xsys) {
479479+ return XR_SUCCESS;
480480+ }
481481+482482+ xrt_result_t xret;
483483+ XrResult ret;
484484+485485+ bool available = false;
486486+ xret = xrt_instance_is_system_available(inst->xinst, &available);
487487+ if (xret != XRT_SUCCESS) {
488488+ struct u_pp_sink_stack_only sink;
489489+ u_pp_delegate_t dg = u_pp_sink_stack_only_init(&sink);
490490+ u_pp(dg, "Call to xrt_instance_is_system_available failed: ");
491491+ u_pp_xrt_result(dg, xret);
492492+ ret = oxr_error(log, xret == XRT_ERROR_IPC_FAILURE ? XR_ERROR_INSTANCE_LOST : XR_ERROR_RUNTIME_FAILURE,
493493+ "%s", sink.buffer);
494494+ return ret;
495495+ }
496496+ if (!available) {
497497+ return XR_ERROR_FORM_FACTOR_UNAVAILABLE;
498498+ }
499499+500500+ // Create the compositor if we are not headless, currently always create it.
501501+ bool should_create_compositor = true /* !inst->extensions.MND_headless */;
502502+503503+ // Create the system.
504504+ if (should_create_compositor) {
505505+ xret = xrt_instance_create_system(inst->xinst, &sys->xsys, &sys->xsysd, &sys->xso, &sys->xsysc);
506506+ } else {
507507+ xret = xrt_instance_create_system(inst->xinst, &sys->xsys, &sys->xsysd, &sys->xso, NULL);
508508+ }
509509+510510+ if (xret != XRT_SUCCESS) {
511511+ struct u_pp_sink_stack_only sink;
512512+ u_pp_delegate_t dg = u_pp_sink_stack_only_init(&sink);
513513+ u_pp(dg, "Call to xrt_instance_create_system failed: ");
514514+ u_pp_xrt_result(dg, xret);
515515+ ret = oxr_error(log, XR_ERROR_INITIALIZATION_FAILED, "%s", sink.buffer);
516516+ return ret;
517517+ }
518518+519519+#ifdef XRT_FEATURE_CLIENT_DEBUG_GUI
520520+ // Do this after creating the system.
521521+ u_debug_gui_start(inst->debug_ui, inst->xinst, sys->xsysd);
522522+#endif
523523+524524+ ret = XR_SUCCESS;
525525+ if (sys->xsysd == NULL) {
526526+ ret = oxr_error(log, XR_ERROR_RUNTIME_FAILURE, "Huh?! Field sys->xsysd was NULL?");
527527+ } else if (should_create_compositor && sys->xsysc == NULL) {
528528+ ret = oxr_error(log, XR_ERROR_RUNTIME_FAILURE, "Huh?! Field sys->xsysc was NULL?");
529529+ } else if (!should_create_compositor && sys->xsysc != NULL) {
530530+ ret = oxr_error(log, XR_ERROR_RUNTIME_FAILURE, "Huh?! Field sys->xsysc was not NULL?");
531531+ }
532532+533533+ if (ret != XR_SUCCESS) {
534534+ return ret;
535535+ }
536536+537537+ // Did we find any HMD
538538+ // @todo Headless with only controllers?
539539+ struct xrt_device *dev = GET_XDEV_BY_ROLE(sys, head);
540540+ if (dev == NULL) {
541541+ ret = oxr_error(log, XR_ERROR_RUNTIME_FAILURE, "Failed to find any HMD device");
542542+ return ret;
543543+ }
544544+ uint32_t view_count = (uint32_t)dev->hmd->view_count;
545545+ ret = oxr_system_fill_in(log, inst, XRT_SYSTEM_ID, view_count, &inst->system);
546546+ if (ret != XR_SUCCESS) {
547547+ return ret;
548548+ }
549549+550550+ debug_print_devices(log, sys);
509551510552 return XR_SUCCESS;
511553}
+134-6
src/xrt/state_trackers/oxr/oxr_objects.h
···11// Copyright 2018-2024, Collabora, Ltd.
22-// Copyright 2023, NVIDIA CORPORATION.
22+// Copyright 2023-2025, NVIDIA CORPORATION.
33// SPDX-License-Identifier: BSL-1.0
44/*!
55 * @file
···127127struct oxr_action_set_ref;
128128struct oxr_action_ref;
129129struct oxr_hand_tracker;
130130+struct oxr_face_tracker_android;
130131struct oxr_facial_tracker_htc;
131132struct oxr_face_tracker2_fb;
132133struct oxr_body_tracker_fb;
···252253 struct oxr_instance **out_inst);
253254254255/*!
256256+ * Must be called with oxr_instance::system_init_lock held.
257257+ *
258258+ * @public @memberof oxr_instance
259259+ */
260260+XrResult
261261+oxr_instance_init_system_locked(struct oxr_logger *log, struct oxr_instance *inst);
262262+263263+/*!
255264 * @public @memberof oxr_instance
256265 */
257266XrResult
···459468 return XRT_CAST_PTR_TO_OXR_HANDLE(XrFaceTracker2FB, face_tracker2_fb);
460469}
461470#endif
471471+472472+#ifdef OXR_HAVE_ANDROID_face_tracking
473473+/*!
474474+ * To go back to a OpenXR object.
475475+ *
476476+ * @relates oxr_facial_tracker_htc
477477+ */
478478+static inline XrFaceTrackerANDROID
479479+oxr_face_tracker_android_to_openxr(struct oxr_face_tracker_android *face_tracker_android)
480480+{
481481+ return XRT_CAST_PTR_TO_OXR_HANDLE(XrFaceTrackerANDROID, face_tracker_android);
482482+}
483483+#endif
484484+462485/*!
463486 *
464487 * @name oxr_input.c
···976999 XrTime time,
9771000 struct xrt_space_relation *out_relation);
978100110021002+/*!
10031003+ * Get the xrt_space associated with this oxr_space, the @ref xrt_space will
10041004+ * be reference counted by this function so the caller will need to call
10051005+ * @ref xrt_space_reference to decrement the reference count.
10061006+ *
10071007+ * @param log Logging struct.
10081008+ * @param spc Oxr space to get the xrt_space from.
10091009+ * @param[out] out_xspace Returns the xrt_space associated with this oxr_space.
10101010+ * @return Any errors, XR_SUCCESS, xspace is not set on XR_ERROR_*.
10111011+ */
10121012+XRT_CHECK_RESULT XrResult
10131013+oxr_space_get_xrt_space(struct oxr_logger *log, struct oxr_space *spc, struct xrt_space **out_xspace);
10141014+97910159801016/*
9811017 *
···1086112210871123bool
10881124oxr_system_get_force_feedback_support(struct oxr_logger *log, struct oxr_instance *inst);
11251125+11261126+void
11271127+oxr_system_get_face_tracking_android_support(struct oxr_logger *log, struct oxr_instance *inst, bool *supported);
1089112810901129void
10911130oxr_system_get_face_tracking_htc_support(struct oxr_logger *log,
···14741513};
1475151414761515/*!
15161516+ * Holds the properties that a system supports for a view configuration type.
15171517+ *
15181518+ * @relates oxr_system
15191519+ */
15201520+struct oxr_view_config_properties
15211521+{
15221522+ XrViewConfigurationType view_config_type;
15231523+15241524+ uint32_t view_count;
15251525+ XrViewConfigurationView views[XRT_MAX_COMPOSITOR_VIEW_CONFIGS_VIEW_COUNT];
15261526+15271527+ uint32_t blend_mode_count;
15281528+ XrEnvironmentBlendMode blend_modes[3];
15291529+};
15301530+15311531+/*!
14771532 * Single or multiple devices grouped together to form a system that sessions
14781533 * can be created from. Might need to open devices to get all
14791534 * properties from it, but shouldn't.
···15051560 //! Have the client application called the gfx api requirements func?
15061561 bool gotten_requirements;
1507156215081508- XrViewConfigurationType view_config_type;
15091509- XrViewConfigurationView views[2];
15101510- uint32_t blend_mode_count;
15111511- XrEnvironmentBlendMode blend_modes[3];
15631563+ uint32_t view_config_count;
15641564+ struct oxr_view_config_properties view_configs[XRT_MAX_COMPOSITOR_VIEW_CONFIGS_COUNT];
1512156515131566 XrReferenceSpaceType reference_spaces[5];
15141567 uint32_t reference_space_count;
···15291582 //! The device returned with the last xrGetVulkanGraphicsDeviceKHR or xrGetVulkanGraphicsDevice2KHR call.
15301583 //! XR_NULL_HANDLE if neither has been called.
15311584 VkPhysicalDevice suggested_vulkan_physical_device;
15851585+15861586+ /*!
15871587+ * Stores the vkGetInstanceProcAddr passed to xrCreateVulkanInstanceKHR to be
15881588+ * used when looking up Vulkan functions used by xrGetVulkanGraphicsDevice2KHR.
15891589+ */
15901590+ PFN_vkGetInstanceProcAddr vk_get_instance_proc_addr;
1532159115331592 struct
15341593 {
···16891748 XrVersion major_minor;
16901749 } openxr_version;
1691175017511751+ // Protects the function oxr_instance_init_system_locked.
17521752+ struct os_mutex system_init_lock;
17531753+16921754 // Hardcoded single system.
16931755 struct oxr_system system;
16941756···1748181017491811 struct
17501812 {
17511751- //! Unreal has a bug in the VulkanRHI backend.
18131813+ /*!
18141814+ * Some applications can't handle depth formats, or they trigger
18151815+ * a bug in a specific version of the application or engine.
18161816+ * This flag only disables depth formats
18171817+ * @see disable_vulkan_format_depth_stencil for depth-stencil formats.
18181818+ */
18191819+ bool disable_vulkan_format_depth;
18201820+18211821+ /*!
18221822+ * Some applications can't handle depth stencil formats, or they
18231823+ * trigger a bug in a specific version of the application or
18241824+ * engine.
18251825+ *
18261826+ * This flag only disables depth-stencil formats,
18271827+ * @see disable_vulkan_format_depth flag for depth only formats.
18281828+ *
18291829+ * In the past it was used to work around a bug in Unreal's
18301830+ * VulkanRHI backend.
18311831+ */
17521832 bool disable_vulkan_format_depth_stencil;
18331833+17531834 //! Unreal 4 has a bug calling xrEndSession; the function should just exit
17541835 bool skip_end_session;
17551836···18111892 struct oxr_session *next;
1812189318131894 XrSessionState state;
18951895+18961896+ /*!
18971897+ * This is set in xrBeginSession and is the primaryViewConfiguration
18981898+ * argument, this is then used in xrEndFrame to know which view
18991899+ * configuration the application is submitting it's frame in.
19001900+ */
19011901+ XrViewConfigurationType current_view_config_type;
1814190218151903 /*!
18161904 * There is a extra state between xrBeginSession has been called and
···30233111XrResult
30243112oxr_event_push_XrEventDataUserPresenceChangedEXT(struct oxr_logger *log, struct oxr_session *sess, bool isUserPresent);
30253113#endif // OXR_HAVE_EXT_user_presence
31143114+31153115+#ifdef OXR_HAVE_ANDROID_face_tracking
31163116+/*!
31173117+ * Android specific Facial tracker.
31183118+ *
31193119+ * Parent type/handle is @ref oxr_instance
31203120+ *
31213121+ *
31223122+ * @obj{XrFaceTrackerANDROID}
31233123+ * @extends oxr_handle_base
31243124+ */
31253125+struct oxr_face_tracker_android
31263126+{
31273127+ //! Common structure for things referred to by OpenXR handles.
31283128+ struct oxr_handle_base handle;
31293129+31303130+ //! Owner of this face tracker.
31313131+ struct oxr_session *sess;
31323132+31333133+ //! xrt_device backing this face tracker
31343134+ struct xrt_device *xdev;
31353135+};
31363136+31373137+XrResult
31383138+oxr_face_tracker_android_create(struct oxr_logger *log,
31393139+ struct oxr_session *sess,
31403140+ const XrFaceTrackerCreateInfoANDROID *createInfo,
31413141+ XrFaceTrackerANDROID *faceTracker);
31423142+31433143+XrResult
31443144+oxr_get_face_state_android(struct oxr_logger *log,
31453145+ struct oxr_face_tracker_android *facial_tracker_android,
31463146+ const XrFaceStateGetInfoANDROID *getInfo,
31473147+ XrFaceStateANDROID *faceStateOutput);
31483148+31493149+XrResult
31503150+oxr_get_face_calibration_state_android(struct oxr_logger *log,
31513151+ struct oxr_face_tracker_android *facial_tracker_android,
31523152+ XrBool32 *faceIsCalibratedOutput);
31533153+#endif // OXR_HAVE_ANDROID_face_tracking
3026315430273155/*!
30283156 * @}
+203-80
src/xrt/state_trackers/oxr/oxr_session.c
···11// Copyright 2018-2024, Collabora, Ltd.
22+// Copyright 2024-2025, NVIDIA CORPORATION.
23// SPDX-License-Identifier: BSL-1.0
34/*!
45 * @file
···6566 */
66676768static bool
6969+should_skip_format_vk_1_and_2(const struct oxr_instance *inst, uint64_t format)
7070+{
7171+ bool skip_depth_stencil = inst->quirks.disable_vulkan_format_depth_stencil;
7272+ bool skip_stencil = false;
7373+ bool skip_depth = inst->quirks.disable_vulkan_format_depth;
7474+7575+ // Access to Vulkan headers are not guaranteed.
7676+ switch (format) {
7777+ case 124: /* VK_FORMAT_D16_UNORM */ return skip_depth;
7878+ case 125: /* VK_FORMAT_X8_D24_UNORM_PACK32*/ return skip_depth;
7979+ case 126: /* VK_FORMAT_D32_SFLOAT */ return skip_depth;
8080+ case 127: /* VK_FORMAT_S8_UINT */ return skip_stencil;
8181+ case 129: /* VK_FORMAT_D24_UNORM_S8_UINT */ return skip_depth_stencil;
8282+ case 130: /* VK_FORMAT_D32_SFLOAT_S8_UINT */ return skip_depth_stencil;
8383+ default: return false;
8484+ }
8585+}
8686+8787+static bool
8888+should_skip_format(const struct oxr_instance *inst, const struct oxr_session *sess, uint64_t format)
8989+{
9090+ if (sess->gfx_ext == OXR_SESSION_GRAPHICS_EXT_VULKAN) {
9191+ /*
9292+ * Hello future computer whisperer, if we split the graphics
9393+ * extension enum into Vulkan1 and Vulkan2, make sure we call this
9494+ * function for both, kthx.
9595+ */
9696+ return should_skip_format_vk_1_and_2(inst, format);
9797+ } else {
9898+ return false;
9999+ }
100100+}
101101+102102+static bool
68103should_render(XrSessionState state)
69104{
70105 switch (state) {
···127162 if (inst->quirks.map_stage_to_local_floor) {
128163 /* When stage is mapped to local_floor:
129164 * ignore stage changes
130130- * for local_floor changes, send a duplicate envent for stage
165165+ * for local_floor changes, send a duplicate event for stage
131166 * */
132167 switch (ref_change->ref_type) {
133168 case XRT_SPACE_REFERENCE_TYPE_STAGE: return XR_SUCCESS;
···224259 for (uint32_t i = 0; i < xc->info.format_count; i++) {
225260 int64_t format = xc->info.formats[i];
226261227227- if (inst->quirks.disable_vulkan_format_depth_stencil &&
228228- format == 130 /* VK_FORMAT_D32_SFLOAT_S8_UINT */) {
262262+ if (should_skip_format(inst, sess, format)) {
229263 continue;
230264 }
231265···249283250284 struct xrt_compositor *xc = sess->compositor;
251285 if (xc != NULL) {
252252- XrViewConfigurationType view_type = beginInfo->primaryViewConfigurationType;
253253-254254- // in a headless session there is no compositor and primaryViewConfigurationType must be ignored
255255- if (sess->compositor != NULL && view_type != sess->sys->view_config_type) {
256256- /*! @todo we only support a single view config type per
257257- * system right now */
258258- return oxr_error(log, XR_ERROR_VIEW_CONFIGURATION_TYPE_UNSUPPORTED,
259259- "(beginInfo->primaryViewConfigurationType == "
260260- "0x%08x) view configuration type not supported",
261261- view_type);
262262- }
263263-264286 const struct oxr_extension_status *extensions = &sess->sys->inst->extensions;
265287266288 const struct xrt_begin_session_info begin_session_info = {
···295317#ifdef OXR_HAVE_META_body_tracking_calibration
296318 .meta_body_tracking_calibration_enabled = extensions->META_body_tracking_calibration,
297319#endif
320320+#ifdef OXR_HAVE_ANDROID_face_tracking
321321+ .android_face_tracking_enabled = extensions->ANDROID_face_tracking,
322322+#endif
298323 };
299324300325 xrt_result_t xret = xrt_comp_begin_session(xc, &begin_session_info);
···313338 // Headless, pretend we got event from the compositor.
314339 sess->compositor_visible = true;
315340 sess->compositor_focused = true;
341341+342342+ int64_t now = os_monotonic_get_ns();
343343+ XrTime now_xr = time_state_monotonic_to_ts_ns(sess->sys->inst->timekeeping, now);
344344+ if (now_xr <= 0) {
345345+ // shouldn't happen but be sure to log if it does
346346+ U_LOG_W("Time keeping oddity: XR_SESSION_STATE_SYNCHRONIZED state reached at XrTime %" PRIi64,
347347+ now_xr);
348348+ }
316349317350 // Transition into focused.
318318- oxr_session_change_state(log, sess, XR_SESSION_STATE_SYNCHRONIZED, 0);
319319- oxr_session_change_state(log, sess, XR_SESSION_STATE_VISIBLE, 0);
320320- oxr_session_change_state(log, sess, XR_SESSION_STATE_FOCUSED, 0);
351351+ oxr_session_change_state(log, sess, XR_SESSION_STATE_SYNCHRONIZED, now_xr);
352352+ oxr_session_change_state(log, sess, XR_SESSION_STATE_VISIBLE, now_xr);
353353+ oxr_session_change_state(log, sess, XR_SESSION_STATE_FOCUSED, now_xr);
321354 }
322355 XrResult ret = oxr_frame_sync_begin_session(&sess->frame_sync);
323356 if (ret != XR_SUCCESS) {
···325358 "Frame sync object refused to let us begin session, probably already running");
326359 }
327360361361+ // Set the current view configuration type, used in xrEndFrame.
362362+ sess->current_view_config_type = beginInfo->primaryViewConfigurationType;
363363+328364 return oxr_session_success_result(sess);
329365}
330366···372408 sess->compositor_focused = false;
373409 }
374410375375- oxr_session_change_state(log, sess, XR_SESSION_STATE_IDLE, 0);
411411+ int64_t now = os_monotonic_get_ns();
412412+ XrTime now_xr = time_state_monotonic_to_ts_ns(sess->sys->inst->timekeeping, now);
413413+ if (now_xr <= 0) {
414414+ // shouldn't happen but be sure to log if it does
415415+ U_LOG_W("Time keeping oddity: ending session at XrTime %" PRIi64, now_xr);
416416+ }
417417+418418+ oxr_session_change_state(log, sess, XR_SESSION_STATE_IDLE, now_xr);
376419 if (sess->exiting) {
377377- oxr_session_change_state(log, sess, XR_SESSION_STATE_EXITING, 0);
420420+ oxr_session_change_state(log, sess, XR_SESSION_STATE_EXITING, now_xr);
378421 } else {
379422#ifndef XRT_OS_ANDROID
380423 // @todo In multi-clients scenario with a session being reused, changing session
···391434 }
392435 sess->has_ended_once = false;
393436437437+ // Unset the current view configuration type here.
438438+ sess->current_view_config_type = XR_VIEW_CONFIGURATION_TYPE_MAX_ENUM;
439439+394440 return oxr_session_success_result(sess);
395441}
396442397443XrResult
398444oxr_session_request_exit(struct oxr_logger *log, struct oxr_session *sess)
399445{
446446+ int64_t now = os_monotonic_get_ns();
447447+ XrTime now_xr = time_state_monotonic_to_ts_ns(sess->sys->inst->timekeeping, now);
448448+ if (now_xr <= 0) {
449449+ // shouldn't happen but be sure to log if it does
450450+ U_LOG_W("Time keeping oddity: Requesting exit at XrTime %" PRIi64, now_xr);
451451+ }
452452+400453 if (sess->state == XR_SESSION_STATE_FOCUSED) {
401401- oxr_session_change_state(log, sess, XR_SESSION_STATE_VISIBLE, 0);
454454+ oxr_session_change_state(log, sess, XR_SESSION_STATE_VISIBLE, now_xr);
402455 }
403456 if (sess->state == XR_SESSION_STATE_VISIBLE) {
404404- oxr_session_change_state(log, sess, XR_SESSION_STATE_SYNCHRONIZED, 0);
457457+ oxr_session_change_state(log, sess, XR_SESSION_STATE_SYNCHRONIZED, now_xr);
405458 }
406459 if (!sess->has_ended_once && sess->state != XR_SESSION_STATE_SYNCHRONIZED) {
407407- oxr_session_change_state(log, sess, XR_SESSION_STATE_SYNCHRONIZED, 0);
460460+ oxr_session_change_state(log, sess, XR_SESSION_STATE_SYNCHRONIZED, now_xr);
408461 // Fake the synchronization.
409462 sess->has_ended_once = true;
410463 }
411464412465 //! @todo start fading out the app.
413413- oxr_session_change_state(log, sess, XR_SESSION_STATE_STOPPING, 0);
466466+ oxr_session_change_state(log, sess, XR_SESSION_STATE_STOPPING, now_xr);
414467 sess->exiting = true;
415468 return oxr_session_success_result(sess);
416469}
···446499 return oxr_error(log, XR_ERROR_RUNTIME_FAILURE, "xrt_session is null");
447500 }
448501502502+ int64_t now = os_monotonic_get_ns();
503503+ XrTime now_xr = time_state_monotonic_to_ts_ns(sess->sys->inst->timekeeping, now);
504504+ if (now_xr <= 0) {
505505+ // shouldn't happen but be sure to log if it does
506506+ U_LOG_W("Time keeping oddity: Polling session events at XrTime %" PRIi64, now_xr);
507507+ }
508508+449509#ifdef XRT_OS_ANDROID
450510 // Most recent Android activity lifecycle event was OnPause: move toward stopping
451511 if (sess->sys->inst->activity_state == XRT_ANDROID_LIVECYCLE_EVENT_ON_PAUSE) {
452512 if (sess->state == XR_SESSION_STATE_FOCUSED) {
453513 U_LOG_I("Activity paused: changing session state FOCUSED->VISIBLE");
454454- oxr_session_change_state(log, sess, XR_SESSION_STATE_VISIBLE, 0);
514514+ oxr_session_change_state(log, sess, XR_SESSION_STATE_VISIBLE, now_xr);
455515 }
456516457517 if (sess->state == XR_SESSION_STATE_VISIBLE) {
458518 U_LOG_I("Activity paused: changing session state VISIBLE->SYNCHRONIZED");
459459- oxr_session_change_state(log, sess, XR_SESSION_STATE_SYNCHRONIZED, 0);
519519+ oxr_session_change_state(log, sess, XR_SESSION_STATE_SYNCHRONIZED, now_xr);
460520 }
461521462522 if (sess->state == XR_SESSION_STATE_SYNCHRONIZED) {
463523 U_LOG_I("Activity paused: changing session state SYNCHRONIZED->STOPPING");
464464- oxr_session_change_state(log, sess, XR_SESSION_STATE_STOPPING, 0);
524524+ oxr_session_change_state(log, sess, XR_SESSION_STATE_STOPPING, now_xr);
465525 }
466526 // TODO return here to avoid polling other events?
467527 // see https://gitlab.freedesktop.org/monado/monado/-/issues/419
···471531 if (sess->sys->inst->activity_state == XRT_ANDROID_LIVECYCLE_EVENT_ON_RESUME) {
472532 if (sess->state == XR_SESSION_STATE_IDLE) {
473533 U_LOG_I("Activity resumed: changing session state IDLE->READY");
474474- oxr_session_change_state(log, sess, XR_SESSION_STATE_READY, 0);
534534+ oxr_session_change_state(log, sess, XR_SESSION_STATE_READY, now_xr);
475535 }
476536 }
477537#endif // XRT_OS_ANDROID
···491551 case XRT_SESSION_EVENT_STATE_CHANGE:
492552 sess->compositor_visible = xse.state.visible;
493553 sess->compositor_focused = xse.state.focused;
554554+555555+ // Do not use xse.state.timestamp_ns, server side focused / visible state does not correspond
556556+ // 1:1 to the cycle we tell the app. In particular the compositor may have become focused /
557557+ // visible much earlier than what we tell the app when it became so.
558558+494559 break;
495560 case XRT_SESSION_EVENT_OVERLAY_CHANGE:
496561#ifdef OXR_HAVE_EXTX_overlay
···530595 break;
531596 case XRT_SESSION_EVENT_VISIBILITY_MASK_CHANGE:
532597#ifdef OXR_HAVE_KHR_visibility_mask
533533- oxr_event_push_XrEventDataVisibilityMaskChangedKHR(log, sess, sess->sys->view_config_type,
534534- xse.mask_change.view_index);
598598+ // Assume mask changed for all view configuration types.
599599+ for (uint32_t i = 0; i < sess->sys->view_config_count; i++) {
600600+ struct oxr_view_config_properties *props = &sess->sys->view_configs[i];
601601+ oxr_event_push_XrEventDataVisibilityMaskChangedKHR( //
602602+ log, //
603603+ sess, //
604604+ props->view_config_type, //
605605+ xse.mask_change.view_index); //
606606+ }
535607 break;
536608#endif // OXR_HAVE_KHR_visibility_mask
537609 case XRT_SESSION_EVENT_USER_PRESENCE_CHANGE:
···545617 }
546618547619 if (sess->state == XR_SESSION_STATE_SYNCHRONIZED && sess->compositor_visible) {
548548- oxr_session_change_state(log, sess, XR_SESSION_STATE_VISIBLE, 0);
620620+ oxr_session_change_state(log, sess, XR_SESSION_STATE_VISIBLE, now_xr);
549621 }
550622551623 if (sess->state == XR_SESSION_STATE_VISIBLE && sess->compositor_focused) {
552552- oxr_session_change_state(log, sess, XR_SESSION_STATE_FOCUSED, 0);
624624+ oxr_session_change_state(log, sess, XR_SESSION_STATE_FOCUSED, now_xr);
553625 }
554626555627 if (sess->state == XR_SESSION_STATE_FOCUSED && !sess->compositor_focused) {
556556- oxr_session_change_state(log, sess, XR_SESSION_STATE_VISIBLE, 0);
628628+ oxr_session_change_state(log, sess, XR_SESSION_STATE_VISIBLE, now_xr);
557629 }
558630559631 if (sess->state == XR_SESSION_STATE_VISIBLE && !sess->compositor_visible) {
560560- oxr_session_change_state(log, sess, XR_SESSION_STATE_SYNCHRONIZED, 0);
632632+ oxr_session_change_state(log, sess, XR_SESSION_STATE_SYNCHRONIZED, now_xr);
561633 }
562634563635 return XR_SUCCESS;
···655727 struct xrt_space_relation T_xdev_head = XRT_SPACE_RELATION_ZERO;
656728 struct xrt_fov fovs[XRT_MAX_VIEWS] = {0};
657729 struct xrt_pose poses[XRT_MAX_VIEWS] = {0};
730730+731731+ enum xrt_view_type view_type = view_count == 1 ? XRT_VIEW_TYPE_MONO : XRT_VIEW_TYPE_STEREO;
658732659733 xrt_result_t xret = xrt_device_get_view_poses( //
660734 xdev, //
661735 &default_eye_relation, //
662736 xdisplay_time, //
737737+ view_type, //
663738 view_count, //
664739 &T_xdev_head, //
665740 fovs, //
···10471122 u_hashmap_int_create(&sess->act_sets_attachments_by_key);
10481123 u_hashmap_int_create(&sess->act_attachments_by_key);
1049112411251125+ // This is set to something valid in begin session, used in xrEndFrame.
11261126+ sess->current_view_config_type = XR_VIEW_CONFIGURATION_TYPE_MAX_ENUM;
11271127+10501128 // Done with basic init, set out variable.
10511129 *out_session = sess;
10521130···10611139 } \
10621140 } while (false)
1063114110641064-#define OXR_CREATE_XRT_SESSION_AND_NATIVE_COMPOSITOR(LOG, XSI, SESS) \
11421142+#define OXR_CHECK_XR_SUCCESS(LOG, FUNC, MSG) \
10651143 do { \
10661066- if ((SESS)->sys->xsysc == NULL) { \
10671067- return oxr_error((LOG), XR_ERROR_RUNTIME_FAILURE, \
10681068- "The system compositor wasn't created, can't create native compositor!"); \
10691069- } \
10701070- xrt_result_t xret = xrt_system_create_session((SESS)->sys->xsys, (XSI), &(SESS)->xs, &(SESS)->xcn); \
10711071- if (xret == XRT_ERROR_MULTI_SESSION_NOT_IMPLEMENTED) { \
10721072- return oxr_error((LOG), XR_ERROR_LIMIT_REACHED, "Per instance multi-session not supported."); \
10731073- } \
10741074- if (xret != XRT_SUCCESS) { \
10751075- return oxr_error((LOG), XR_ERROR_RUNTIME_FAILURE, \
10761076- "Failed to create xrt_session and xrt_compositor_native! '%i'", xret); \
10771077- } \
10781078- if ((SESS)->sys->xsysc->xmcc != NULL) { \
10791079- xrt_syscomp_set_state((SESS)->sys->xsysc, &(SESS)->xcn->base, true, true); \
10801080- xrt_syscomp_set_z_order((SESS)->sys->xsysc, &(SESS)->xcn->base, 0); \
11441144+ XrResult _xr_result = FUNC; \
11451145+ if (_xr_result != XR_SUCCESS) { \
11461146+ return oxr_error(LOG, _xr_result, MSG); \
10811147 } \
10821148 } while (false)
1083114910841084-#define OXR_SESSION_ALLOCATE_AND_INIT(LOG, SYS, GFX_TYPE, OUT) \
10851085- do { \
10861086- XrResult ret = oxr_session_allocate_and_init(LOG, SYS, GFX_TYPE, &OUT); \
10871087- if (ret != XR_SUCCESS) { \
10881088- return ret; \
10891089- } \
10901090- } while (0)
11501150+11511151+11521152+static XrResult
11531153+oxr_create_xrt_session_and_native_compositor(struct oxr_logger *log,
11541154+ const struct xrt_session_info *xsi,
11551155+ struct oxr_session *sess)
11561156+{
11571157+ if (sess->sys->xsysc == NULL) {
11581158+ return oxr_error(log, XR_ERROR_RUNTIME_FAILURE,
11591159+ "The system compositor wasn't created, can't create native compositor!");
11601160+ }
11611161+ xrt_result_t xret = xrt_system_create_session(sess->sys->xsys, xsi, &sess->xs, &sess->xcn);
11621162+ if (xret == XRT_ERROR_MULTI_SESSION_NOT_IMPLEMENTED) {
11631163+ return oxr_error(log, XR_ERROR_LIMIT_REACHED, "Per instance multi-session not supported.");
11641164+ }
11651165+ if (xret != XRT_SUCCESS) {
11661166+ return oxr_error(log, XR_ERROR_RUNTIME_FAILURE,
11671167+ "Failed to create xrt_session and xrt_compositor_native! '%i'", xret);
11681168+ }
11691169+ if (sess->sys->xsysc->xmcc != NULL) {
11701170+ xrt_syscomp_set_state(sess->sys->xsysc, &sess->xcn->base, true, true, os_monotonic_get_ns());
11711171+ xrt_syscomp_set_z_order(sess->sys->xsysc, &sess->xcn->base, 0);
11721172+ }
11731173+ return XR_SUCCESS;
11741174+}
11751175+109111761092117710931178/*
···11011186 const struct xrt_session_info *xsi,
11021187 struct oxr_session **out_session)
11031188{
11891189+ XrResult ret = XR_SUCCESS;
11901190+11041191#if defined(XR_USE_PLATFORM_XLIB) && defined(XR_USE_GRAPHICS_API_OPENGL)
11051192 XrGraphicsBindingOpenGLXlibKHR const *opengl_xlib = OXR_GET_INPUT_FROM_CHAIN(
11061193 createInfo, XR_TYPE_GRAPHICS_BINDING_OPENGL_XLIB_KHR, XrGraphicsBindingOpenGLXlibKHR);
···11131200 "xrGetOpenGL[ES]GraphicsRequirementsKHR");
11141201 }
1115120211161116- OXR_SESSION_ALLOCATE_AND_INIT(log, sys, OXR_SESSION_GRAPHICS_EXT_XLIB_GL, *out_session);
11171117- OXR_CREATE_XRT_SESSION_AND_NATIVE_COMPOSITOR(log, xsi, *out_session);
12031203+ ret = oxr_session_allocate_and_init(log, sys, OXR_SESSION_GRAPHICS_EXT_XLIB_GL, out_session);
12041204+ OXR_CHECK_XR_SUCCESS(log, ret, "Failed to allocate session");
12051205+12061206+ ret = oxr_create_xrt_session_and_native_compositor(log, xsi, *out_session);
12071207+ OXR_CHECK_XR_SUCCESS(log, ret, "Failed to create session/compositor");
12081208+11181209 return oxr_session_populate_gl_xlib(log, sys, opengl_xlib, *out_session);
11191210 }
11201211#endif
···11321223 "xrGetOpenGLESGraphicsRequirementsKHR");
11331224 }
1134122511351135- OXR_SESSION_ALLOCATE_AND_INIT(log, sys, OXR_SESSION_GRAPHICS_EXT_ANDROID_GLES, *out_session);
11361136- OXR_CREATE_XRT_SESSION_AND_NATIVE_COMPOSITOR(log, xsi, *out_session);
12261226+ ret = oxr_session_allocate_and_init(log, sys, OXR_SESSION_GRAPHICS_EXT_ANDROID_GLES, out_session);
12271227+ OXR_CHECK_XR_SUCCESS(log, ret, "Failed to allocate session");
12281228+12291229+ ret = oxr_create_xrt_session_and_native_compositor(log, xsi, *out_session);
12301230+ OXR_CHECK_XR_SUCCESS(log, ret, "Failed to create session/compositor");
12311231+11371232 return oxr_session_populate_gles_android(log, sys, opengles_android, *out_session);
11381233 }
11391234#endif
···11491244 "Has not called xrGetOpenGLGraphicsRequirementsKHR");
11501245 }
1151124611521152- OXR_SESSION_ALLOCATE_AND_INIT(log, sys, OXR_SESSION_GRAPHICS_EXT_WIN32_GL, *out_session);
11531153- OXR_CREATE_XRT_SESSION_AND_NATIVE_COMPOSITOR(log, xsi, *out_session);
12471247+ ret = oxr_session_allocate_and_init(log, sys, OXR_SESSION_GRAPHICS_EXT_WIN32_GL, out_session);
12481248+ OXR_CHECK_XR_SUCCESS(log, ret, "Failed to allocate session");
12491249+12501250+ ret = oxr_create_xrt_session_and_native_compositor(log, xsi, *out_session);
12511251+ OXR_CHECK_XR_SUCCESS(log, ret, "Failed to create session/compositor");
12521252+11541253 return oxr_session_populate_gl_win32(log, sys, opengl_win32, *out_session);
11551254 }
11561255#endif
···11881287 (void *)vulkan->physicalDevice, (void *)sys->suggested_vulkan_physical_device, fn);
11891288 }
1190128911911191- OXR_SESSION_ALLOCATE_AND_INIT(log, sys, OXR_SESSION_GRAPHICS_EXT_VULKAN, *out_session);
11921192- OXR_CREATE_XRT_SESSION_AND_NATIVE_COMPOSITOR(log, xsi, *out_session);
12901290+ ret = oxr_session_allocate_and_init(log, sys, OXR_SESSION_GRAPHICS_EXT_VULKAN, out_session);
12911291+ OXR_CHECK_XR_SUCCESS(log, ret, "Failed to allocate session");
12921292+12931293+ ret = oxr_create_xrt_session_and_native_compositor(log, xsi, *out_session);
12941294+ OXR_CHECK_XR_SUCCESS(log, ret, "Failed to create session/compositor");
12951295+11931296 return oxr_session_populate_vk(log, sys, vulkan, *out_session);
11941297 }
11951298#endif
···12061309 "xrGetOpenGL[ES]GraphicsRequirementsKHR");
12071310 }
1208131112091209- OXR_SESSION_ALLOCATE_AND_INIT(log, sys, OXR_SESSION_GRAPHICS_EXT_EGL, *out_session);
12101210- OXR_CREATE_XRT_SESSION_AND_NATIVE_COMPOSITOR(log, xsi, *out_session);
13121312+ ret = oxr_session_allocate_and_init(log, sys, OXR_SESSION_GRAPHICS_EXT_EGL, out_session);
13131313+ OXR_CHECK_XR_SUCCESS(log, ret, "Failed to allocate session");
13141314+13151315+ ret = oxr_create_xrt_session_and_native_compositor(log, xsi, *out_session);
13161316+ OXR_CHECK_XR_SUCCESS(log, ret, "Failed to create session/compositor");
13171317+12111318 return oxr_session_populate_egl(log, sys, egl, *out_session);
12121319 }
12131320#endif
···12241331 return oxr_error(log, XR_ERROR_GRAPHICS_REQUIREMENTS_CALL_MISSING,
12251332 "Has not called xrGetD3D11GraphicsRequirementsKHR");
12261333 }
12271227- XrResult result = oxr_d3d11_check_device(log, sys, d3d11->device);
13341334+ ret = oxr_d3d11_check_device(log, sys, d3d11->device);
1228133512291229- if (!XR_SUCCEEDED(result)) {
12301230- return result;
13361336+ if (!XR_SUCCEEDED(ret)) {
13371337+ return ret;
12311338 }
1232133913401340+ ret = oxr_session_allocate_and_init(log, sys, OXR_SESSION_GRAPHICS_EXT_D3D11, out_session);
13411341+ OXR_CHECK_XR_SUCCESS(log, ret, "Failed to allocate session");
1233134212341234- OXR_SESSION_ALLOCATE_AND_INIT(log, sys, OXR_SESSION_GRAPHICS_EXT_D3D11, *out_session);
12351235- OXR_CREATE_XRT_SESSION_AND_NATIVE_COMPOSITOR(log, xsi, *out_session);
13431343+ ret = oxr_create_xrt_session_and_native_compositor(log, xsi, *out_session);
13441344+ OXR_CHECK_XR_SUCCESS(log, ret, "Failed to create session/compositor");
13451345+12361346 return oxr_session_populate_d3d11(log, sys, d3d11, *out_session);
12371347 }
12381348#endif
···12491359 return oxr_error(log, XR_ERROR_GRAPHICS_REQUIREMENTS_CALL_MISSING,
12501360 "Has not called xrGetD3D12GraphicsRequirementsKHR");
12511361 }
12521252- XrResult result = oxr_d3d12_check_device(log, sys, d3d12->device);
13621362+ ret = oxr_d3d12_check_device(log, sys, d3d12->device);
1253136312541254- if (!XR_SUCCEEDED(result)) {
12551255- return result;
13641364+ if (!XR_SUCCEEDED(ret)) {
13651365+ return ret;
12561366 }
1257136713681368+ ret = oxr_session_allocate_and_init(log, sys, OXR_SESSION_GRAPHICS_EXT_D3D12, out_session);
13691369+ OXR_CHECK_XR_SUCCESS(log, ret, "Failed to allocate session");
1258137012591259- OXR_SESSION_ALLOCATE_AND_INIT(log, sys, OXR_SESSION_GRAPHICS_EXT_D3D12, *out_session);
12601260- OXR_CREATE_XRT_SESSION_AND_NATIVE_COMPOSITOR(log, xsi, *out_session);
13711371+ ret = oxr_create_xrt_session_and_native_compositor(log, xsi, *out_session);
13721372+ OXR_CHECK_XR_SUCCESS(log, ret, "Failed to create session/compositor");
13731373+12611374 return oxr_session_populate_d3d12(log, sys, d3d12, *out_session);
12621375 }
12631376#endif
···1271138412721385#ifdef OXR_HAVE_MND_headless
12731386 if (sys->inst->extensions.MND_headless) {
12741274- OXR_SESSION_ALLOCATE_AND_INIT(log, sys, OXR_SESSION_GRAPHICS_EXT_HEADLESS, *out_session);
13871387+13881388+ ret = oxr_session_allocate_and_init(log, sys, OXR_SESSION_GRAPHICS_EXT_HEADLESS, out_session);
13891389+ OXR_CHECK_XR_SUCCESS(log, ret, "Failed to allocate session");
13901390+12751391 (*out_session)->compositor = NULL;
12761392 (*out_session)->create_swapchain = NULL;
12771393···13201436 return ret;
13211437 }
1322143814391439+ int64_t now = os_monotonic_get_ns();
14401440+ XrTime now_xr = time_state_monotonic_to_ts_ns(sess->sys->inst->timekeeping, now);
14411441+ if (now_xr <= 0) {
14421442+ // shouldn't happen but be sure to log if it does
14431443+ U_LOG_W("Time keeping oddity: XR_SESSION_STATE_IDLE reached at XrTime %" PRIi64, now_xr);
14441444+ }
14451445+13231446 // Everything is in order, start the state changes.
13241324- oxr_session_change_state(log, sess, XR_SESSION_STATE_IDLE, 0);
13251325- oxr_session_change_state(log, sess, XR_SESSION_STATE_READY, 0);
14471447+ oxr_session_change_state(log, sess, XR_SESSION_STATE_IDLE, now_xr);
14481448+ oxr_session_change_state(log, sess, XR_SESSION_STATE_READY, now_xr);
1326144913271450 *out_session = sess;
13281451
···2727 list(APPEND tests tests_aux_d3d_d3d11 tests_comp_client_d3d11)
2828endif()
2929if(XRT_HAVE_D3D12)
3030- list(APPEND tests tests_comp_client_d3d12)
3030+ list(APPEND tests tests_aux_d3d_d3d12 tests_comp_client_d3d12)
3131endif()
3232if(XRT_HAVE_VULKAN)
3333 list(APPEND tests tests_comp_client_vulkan tests_uv_to_tangent)
···5252 target_link_libraries(${testname} PRIVATE xrt-external-catch2)
5353 target_link_libraries(${testname} PRIVATE aux_util)
5454 add_test(NAME ${testname} COMMAND ${testname} --success --allow-running-no-tests)
5555+ set_target_properties(${testname} PROPERTIES FOLDER monado_tests)
5556endforeach()
56575758# For tests that require more than just aux_util, link those other libs down here.
···8687endif()
87888889if(XRT_HAVE_D3D12)
9090+ target_link_libraries(tests_aux_d3d_d3d12 PRIVATE aux_d3d)
8991 target_link_libraries(tests_comp_client_d3d12 PRIVATE comp_client comp_mock)
9092endif()
9193···111113if(XRT_HAVE_VULKAN AND XRT_HAVE_D3D11)
112114 target_link_libraries(tests_aux_d3d_d3d11 PRIVATE comp_util aux_vk)
113115endif()
116116+117117+if(XRT_HAVE_VULKAN AND XRT_HAVE_D3D12)
118118+ target_link_libraries(tests_aux_d3d_d3d12 PRIVATE comp_util aux_vk)
119119+endif()
+13-15
tests/tests_aux_d3d_d3d11.cpp
···8888 std::vector<xrt_image_native> xins;
8989 xins.reserve(image_count);
90909191- // Keep this around until after successful import, then detach all.
9292- std::vector<wil::unique_handle> handlesForImport;
9393- handlesForImport.reserve(image_count);
9494-9591 for (HANDLE handle : handles) {
9696- wil::unique_handle duped{u_graphics_buffer_ref(handle)};
9292+ /*!
9393+ * If shared resources have been allocated without using NT handles we can't use DuplicateHandle
9494+ *(like u_graphics_buffer_ref does internally) or CloseHandle (which wil::~unique_handle will call).
9595+ * More info:
9696+ * https://learn.microsoft.com/en-us/windows/win32/api/dxgi/nf-dxgi-idxgiresource-getsharedhandle#remarks
9797+ * When using KMT handles, their validity is tied to the underlying video memory (I guess that means a
9898+ * ID3D11Texture2D object).
9999+ */
97100 xrt_image_native xin;
9898- xin.handle = duped.get();
101101+ xin.handle = handle;
99102 xin.size = 0;
100103 xin.use_dedicated_allocation = use_dedicated_allocation;
104104+ xin.is_dxgi_handle = true;
101105102102- handlesForImport.emplace_back(std::move(duped));
103106 xins.emplace_back(xin);
104107 }
105108106109 // Import into a vulkan image collection
107107- bool result = VK_SUCCESS == vk_ic_from_natives(vk, &vk_info, xins.data(), (uint32_t)xins.size(), vkic.get());
110110+ const VkResult ret = vk_ic_from_natives(vk, &vk_info, xins.data(), (uint32_t)xins.size(), vkic.get());
111111+ VK_CHK_WITH_RET(ret, "vk_ic_from_natives", false);
108112109109- if (result) {
110110- // The imported swapchain took ownership of them now, release them from ownership here.
111111- for (auto &h : handlesForImport) {
112112- h.release();
113113- }
114114- }
115115- return result;
113113+ return true;
116114}
117115#else
118116