From 29d1837b0a6df7fe46087fff9638053ccb90e3fd Mon Sep 17 00:00:00 2001 From: dfattal Date: Sat, 3 Oct 2026 23:28:39 -0700 Subject: [PATCH] =?UTF-8?q?feat(weave):=20v15=20XrWeaveSubmitOverlayFilter?= =?UTF-8?q?DXR=20=E2=80=94=20app-tunable=20lens-filter=20strength=20for=20?= =?UTF-8?q?the=202D=20layer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The DP's lens filter of the 2D over-layer trades aliasing for softness, and the best point depends on content: David judged the full filter too soft and picked 0.6 by eye, to ship as the DEFAULT, with API flexibility for an app that knows its content (text/UI more filtering, photos/video less). - XR_DXR_weave spec v15: XrWeaveSubmitOverlayFilterDXR (1004999291), chained next to the v4 overlay: float filterStrength in [0,1] (0 = no lens filtering, 1 = the DP's full filter). Omitted = the DP's OWN default (a vendor value tuned on its panel; Leia SR ships 0.6) - the runtime passes "not set", never a number of its own. Out of range / NaN = XR_ERROR_VALIDATION_FAILURE. Per submit, no latch; no version gate needed. - oxr -> IPC (ipc_arg_weave_submit.overlay_filter_strength, -1 = not set; 836 B) -> handler -> comp_d3d11_service_weave_set_overlay_filter_strength (per-submit setter, consumed first in weave_submit; untrusted values outside [0,1] read as default). - New appended D3D11 DP slot set_overlay_2d_filter_strength(float; negative = default) + XRT_DP_D3D11_HAS_OVERLAY_2D_FILTER_STRENGTH (ADR-020; the shipped set_overlay_2d signature stays frozen). Called each frame the layer goes to the DP, before set_overlay_2d. - weave_rpc_probe: --filter-strength=X chains it. - Spec §2d "Filter strength (v15)" + version table; type registry. Co-Authored-By: Claude Opus 5.5 --- docs/specs/extensions/XR_DXR_weave.md | 26 ++++++++++ src/external/openxr_includes/openxr/README.md | 2 +- .../openxr_includes/openxr/XR_DXR_weave.h | 37 ++++++++++++++- .../d3d11_service/comp_d3d11_service.cpp | 20 ++++++++ .../d3d11_service/comp_d3d11_service.h | 9 ++++ .../include/xrt/xrt_display_processor_d3d11.h | 47 ++++++++++++++++++- src/xrt/ipc/client/ipc_client.h | 1 + src/xrt/ipc/client/ipc_client_compositor.c | 13 +++-- src/xrt/ipc/server/ipc_server_handler.c | 2 + src/xrt/ipc/shared/ipc_protocol.h | 5 ++ src/xrt/state_trackers/oxr/oxr_weave.c | 22 ++++++++- .../probes/weave_rpc_probe_d3d11_win/main.cpp | 12 +++++ 12 files changed, 188 insertions(+), 8 deletions(-) diff --git a/docs/specs/extensions/XR_DXR_weave.md b/docs/specs/extensions/XR_DXR_weave.md index 01ae913a89..e4f3da7a27 100644 --- a/docs/specs/extensions/XR_DXR_weave.md +++ b/docs/specs/extensions/XR_DXR_weave.md @@ -233,6 +233,28 @@ typedef struct XrWeaveSubmitOverlayUnchangedDXR { - D3D11 service path today (`set_overlay_2d(..., layer_unchanged)`); the Vulkan weave engines accept and ignore it until their DP slot exists. +### Filter strength (v15) + +The lens filter trades aliasing against softness, and the best point depends on the content +(text and UI want more filtering, photos and video less). `XrWeaveSubmitOverlayFilterDXR` +(1004999291), chained next to the overlay, sets it per submit: + +```c +typedef struct XrWeaveSubmitOverlayFilterDXR { + XrStructureType type; // XR_TYPE_WEAVE_SUBMIT_OVERLAY_FILTER_DXR + const void* next; + float filterStrength; // 0 = no lens filtering .. 1 = the DP's full filter +} XrWeaveSubmitOverlayFilterDXR; +``` + +- **Omit it unless you know your content.** Omitted = the display processor's own default, a + vendor value tuned by eye on its panel (Leia SR: 0.6). The runtime passes "not set", never a + number of its own. +- `filterStrength` outside [0,1] or NaN is `XR_ERROR_VALIDATION_FAILURE`. +- Per submit, no latch. Carried to the DP by the appended D3D11 slot + `set_overlay_2d_filter_strength` (negative = default), called before `set_overlay_2d` each + frame the layer goes to the DP. + Each submit carries a fixed cost independent of the rect area: the runtime IPC round-trip, @@ -882,8 +904,12 @@ any unknown chained struct. One `weave(v13)` WARN in the service log per edge. | 11 | `xrWeaveSnapWindowGridDXR` + `XrWeaveSnapGridInfoDXR` / `XrWeaveSnapGridPointDXR` — bulk grid snap: the per-point snap evaluated over a grid by the runtime, one call (one IPC round trip) instead of one per point (§5c, #1723). | | 12 | `XrWeaveOutputOriginDXR` (out, per frame: the origin each woven output was woven for + a serial) and `XrWeaveWindowLogicalOriginDXR` (in: the caller's logical origin + scale, echoed verbatim) — so a present-owner can tag its own Wayland commit for move-synchronised drag (§5e, browser-pvt#180). | | 13 | `XrWeaveSubmitMonoIn2DDXR` — one flat view (stereo: the left view; N > 2: the view nearest the display axis) instead of the weave while the session's hardware state is 2D (§5f). Desktop Linux; accepted and ignored elsewhere. | +| 15 | `XrWeaveSubmitOverlayFilterDXR` — the caller's lens-filter strength for the 2D overlay, 0..1; omitted = the DP's own default (§2d). | | 14 | `XrWeaveSubmitOverlayUnchangedDXR` — the v4 overlay atlas holds the previous accepted submit's pixels, so a DP may reuse its lens prefilter of the 2D layer (ADR-027 Amendment; §2d). | +**v15 needs no gate either**: omitting the struct, or a runtime/DP that predates it, means +the display processor's default strength. + **v14 needs no gate**: a pre-v14 runtime skips the unknown struct, which is the "may have changed" behaviour, so a caller can always send it. diff --git a/src/external/openxr_includes/openxr/README.md b/src/external/openxr_includes/openxr/README.md index 67450000e2..89837363cf 100644 --- a/src/external/openxr_includes/openxr/README.md +++ b/src/external/openxr_includes/openxr/README.md @@ -48,7 +48,7 @@ Rules: | 1004999260–269 | `XR_DXR_depth_budget` | 260 = `XR_TYPE_REAR_DEPTH_BUDGET_DXR`, 261 = `XR_TYPE_CONTENT_BOUNDS_DXR`, 262 = `XR_TYPE_EVENT_DATA_REAR_DEPTH_BUDGET_STATE_CHANGED_DXR`, 263 = `XR_TYPE_CONTENT_MASK_DXR` (ADR-040). Was taken without a registry row — recorded retroactively in #1486 PR 2 | | 1004999270–279 | `XR_DXR_lift` | 270 = `XR_TYPE_LIFT_PROPERTIES_DXR`, 271 = `XR_TYPE_LIFT_STREAM_CREATE_INFO_DXR`, 272 = `XR_TYPE_LIFT_FRAME_SUBMIT_INFO_DXR`, 273 = `XR_TYPE_LIFT_OPTIONS_DXR`, 274 = `XR_TYPE_LIFT_RESULT_DXR`, 275 = `XR_TYPE_WEAVE_SUBMIT_LIFT_RECTS_DXR`, 276 = `XR_TYPE_WEAVE_RECT_LIFT_DXR`, 277 = `XR_TYPE_LIFT_BLOB_DXR` (spec v1, ADR-042). 278 = `XR_OBJECT_TYPE_LIFT_STREAM_DXR` — an `XrObjectType`, not an `XrStructureType`. 279 = `XR_LIFT_NOT_READY_DXR` — a **success**-class `XrResult` (positive) | | 1004999280–289 | `XR_DXR_lift` (continued) | 280 = `XR_TYPE_LIFT_STREAM_STATS_DXR` (spec v1). A second decade because 270–279 is full | -| 1004999290–299 | `XR_DXR_weave` (v14+ additions) | 290 = `XR_TYPE_WEAVE_SUBMIT_OVERLAY_UNCHANGED_DXR` (the v4 overlay atlas is unchanged since the previous submit, so a DP may reuse its lens prefilter of the 2D layer, spec v14, ADR-027 Amendment). The third weave decade (240–249 is full) | +| 1004999290–299 | `XR_DXR_weave` (v14+ additions) | 290 = `XR_TYPE_WEAVE_SUBMIT_OVERLAY_UNCHANGED_DXR` (the v4 overlay atlas is unchanged since the previous submit, so a DP may reuse its lens prefilter of the 2D layer, spec v14, ADR-027 Amendment); 291 = `XR_TYPE_WEAVE_SUBMIT_OVERLAY_FILTER_DXR` (the caller's lens-filter strength for the 2D layer, 0..1, omitted = the DP's default, spec v15). The third weave decade (240–249 is full) | | 1004999300+ | **next free** | | `XR_DXR_android_surface_binding` was implemented in #1037 and now has its own diff --git a/src/external/openxr_includes/openxr/XR_DXR_weave.h b/src/external/openxr_includes/openxr/XR_DXR_weave.h index 94d9e08e57..2572571bfa 100644 --- a/src/external/openxr_includes/openxr/XR_DXR_weave.h +++ b/src/external/openxr_includes/openxr/XR_DXR_weave.h @@ -198,6 +198,12 @@ * back verbatim), so a present-owner can tag its own Wayland commit for * move-synchronised drag. * + * Overlay filter strength (SPEC_VERSION 15). XrWeaveSubmitOverlayFilterDXR, chained + * next to an XrWeaveSubmitOverlaysDXR: how strongly a display processor that + * band-limits the 2D layer for its lens filters it, 0..1 (0 = no lens filtering, + * 1 = the full lens cell). Absent = the display processor's own default, which + * is what a caller that does not know better should use. + * * Overlay unchanged (SPEC_VERSION 14). XrWeaveSubmitOverlayUnchangedDXR, chained * on XrWeaveSubmitInfoDXR next to an XrWeaveSubmitOverlaysDXR: the caller declares * that this submit's overlay atlas holds exactly the pixels of the previous @@ -275,7 +281,7 @@ extern "C" { #endif #define XR_DXR_weave 1 -#define XR_DXR_weave_SPEC_VERSION 14 +#define XR_DXR_weave_SPEC_VERSION 15 #define XR_DXR_WEAVE_EXTENSION_NAME "XR_DXR_weave" // Reserved 1004999190..199. Final values reconcile with the Khronos registry @@ -314,6 +320,8 @@ extern "C" { // Reserved 1004999290..299 — the third weave decade (240..249 is full). Same registry. // Spec v14 (ADR-027 Amendment): the overlay atlas is unchanged since the last submit. #define XR_TYPE_WEAVE_SUBMIT_OVERLAY_UNCHANGED_DXR ((XrStructureType)1004999290) +// Spec v15 (ADR-027 Amendment): the caller's lens-filter strength for the 2D layer. +#define XR_TYPE_WEAVE_SUBMIT_OVERLAY_FILTER_DXR ((XrStructureType)1004999291) //! Upper bound on eye positions carried by XrWeaveSubmitInfoDXR (mirrors the //! runtime's XRT_MAX_VIEWS). Phase 1: carried but unused. @@ -1038,6 +1046,33 @@ typedef struct XrWeaveSubmitOverlayUnchangedDXR { XrBool32 overlayUnchanged; //!< XR_TRUE: same overlay pixels as the previous accepted submit } XrWeaveSubmitOverlayUnchangedDXR; +/*! + * @brief Lens-filter strength for the v4 overlay atlas (spec v15). + * + * Chain onto XrWeaveSubmitInfoDXR::next, in the same submit as an + * XrWeaveSubmitOverlaysDXR. A display processor that band-limits the 2D layer + * for its lens (ADR-027 Amendment) trades aliasing against softness; this sets + * where on that curve THIS submit sits: + * + * 0.0 — no lens filtering (the layer is composited as-is: sharpest, aliases) + * 1.0 — the display processor's full anti-aliasing filter (softest) + * + * Omitting the struct selects the DISPLAY PROCESSOR's default, a vendor-tuned + * value chosen by eye on its panel (Leia SR ships 0.6). That is the right choice + * for a caller that does not know its content better; set it only to tune for + * known content (text/UI wants more filtering, photos/video can take less). + * + * Per submit, no latch. @c filterStrength outside [0,1] or NaN is + * XR_ERROR_VALIDATION_FAILURE. A display processor without a strength control + * ignores it, as does a pre-v15 runtime (unknown chained struct) — both behave + * as the default, so no version gate is needed to send it. + */ +typedef struct XrWeaveSubmitOverlayFilterDXR { + XrStructureType type; //!< XR_TYPE_WEAVE_SUBMIT_OVERLAY_FILTER_DXR + const void* XR_MAY_ALIAS next; + float filterStrength; //!< 0 = no lens filtering .. 1 = full; omit = DP default +} XrWeaveSubmitOverlayFilterDXR; + typedef XrResult (XRAPI_PTR *PFN_xrWeaveBindWindowDXR)( XrSession session, void* windowHandle); diff --git a/src/xrt/compositor/d3d11_service/comp_d3d11_service.cpp b/src/xrt/compositor/d3d11_service/comp_d3d11_service.cpp index 3cbc7512a8..c753059db4 100644 --- a/src/xrt/compositor/d3d11_service/comp_d3d11_service.cpp +++ b/src/xrt/compositor/d3d11_service/comp_d3d11_service.cpp @@ -810,6 +810,9 @@ struct d3d11_client_render_resources //! XR_DXR_weave v14: the next submit's overlay is unchanged (set by //! comp_d3d11_service_weave_set_overlay_unchanged, consumed by the submit). bool weave_overlay_unchanged_next; + //! XR_DXR_weave v15: the next submit's overlay lens-filter strength [0,1]; + //! negative = the DP's default. Consumed (reset to -1) by the submit. + float weave_overlay_filter_strength_next; //! DXR_WEAVE_GPU_TIMING=1 diagnostic (ADR-027 Amendment perf A/B): a ring of //! disjoint + 3 timestamp queries per weave_submit — T0 before the ingest @@ -25220,6 +25223,17 @@ comp_d3d11_service_weave_set_overlay_unchanged(struct xrt_compositor *xc, bool o c->render.weave_overlay_unchanged_next = overlay_unchanged; } +void +comp_d3d11_service_weave_set_overlay_filter_strength(struct xrt_compositor *xc, float strength) +{ + if (xc == nullptr || xc->destroy != compositor_destroy) { + return; + } + struct d3d11_service_compositor *c = d3d11_service_compositor_from_xrt(xc); + // The wire is untrusted: anything outside [0,1] (incl. NaN) means "default". + c->render.weave_overlay_filter_strength_next = (strength >= 0.0f && strength <= 1.0f) ? strength : -1.0f; +} + bool comp_d3d11_service_weave_set_screen_flat_regions(struct xrt_compositor *xc, uint32_t rect_count, @@ -25691,6 +25705,8 @@ comp_d3d11_service_weave_submit(struct xrt_compositor *xc, // "unchanged" for the next submit (per submit, no latch). const bool ov_unchanged = c->render.weave_overlay_unchanged_next; c->render.weave_overlay_unchanged_next = false; + const float ov_filter_strength = c->render.weave_overlay_filter_strength_next; + c->render.weave_overlay_filter_strength_next = -1.0f; struct d3d11_service_system *sys = c->sys; if (sys == nullptr) { return false; @@ -26243,6 +26259,10 @@ comp_d3d11_service_weave_submit(struct xrt_compositor *xc, bool ov_in_dp = false; auto hand_overlay_to_dp = [&]() { const bool fits = (ov_srv != nullptr && ov_w == win_w && ov_h == win_h); + if (fits) { + // v15: per frame, before the layer itself (the slot is stateless). + xrt_display_processor_d3d11_set_overlay_2d_filter_strength(dp, ov_filter_strength); + } ov_in_dp = xrt_display_processor_d3d11_set_overlay_2d(dp, sys->context.get(), fits ? ov_srv : nullptr, fits ? ov_w : 0, fits ? ov_h : 0, XRT_ATLAS_ENCODING_ENCODED, fits && ov_unchanged) && diff --git a/src/xrt/compositor/d3d11_service/comp_d3d11_service.h b/src/xrt/compositor/d3d11_service/comp_d3d11_service.h index f2f11413c0..e76e65397d 100644 --- a/src/xrt/compositor/d3d11_service/comp_d3d11_service.h +++ b/src/xrt/compositor/d3d11_service/comp_d3d11_service.h @@ -1192,6 +1192,15 @@ comp_d3d11_service_weave_set_screen_flat_regions(struct xrt_compositor *xc, void comp_d3d11_service_weave_set_overlay_unchanged(struct xrt_compositor *xc, bool overlay_unchanged); +/*! + * XR_DXR_weave v15 (XrWeaveSubmitOverlayFilterDXR): the lens-filter strength + * the NEXT @ref comp_d3d11_service_weave_submit asks for its overlay, [0,1]; + * negative = the display processor's own default. Per submit: consumed and + * reset by the submit, same pattern as the v14 setter above. + */ +void +comp_d3d11_service_weave_set_overlay_filter_strength(struct xrt_compositor *xc, float strength); + /*! * Export the persistent server-allocated weaved-output texture handle (+ dims) * for the caller to import once and present each frame. Returns false until the diff --git a/src/xrt/include/xrt/xrt_display_processor_d3d11.h b/src/xrt/include/xrt/xrt_display_processor_d3d11.h index 30b495a7f7..ec6b95b69c 100644 --- a/src/xrt/include/xrt/xrt_display_processor_d3d11.h +++ b/src/xrt/include/xrt/xrt_display_processor_d3d11.h @@ -736,6 +736,26 @@ struct xrt_display_processor_d3d11 uint32_t height, enum xrt_atlas_encoding encoding, bool layer_unchanged); + + /*! + * Lens-filter strength for the 2D over-layer of the NEXT @ref process_atlas + * (XR_DXR_weave v15, ADR-027 Amendment). A DP that band-limits the layer + * for its lens trades aliasing against softness; @p strength picks the + * point: 0 = no lens filtering, 1 = the DP's full filter. NEGATIVE = the + * DP's own default (a vendor-tuned value; the caller did not set one). + * + * Stateless, per frame: called right before @ref set_overlay_2d whenever an + * overlay goes to the DP, so a DP that keeps the value sticky must take it + * from here every frame. Separate from set_overlay_2d because that slot + * shipped (v2.25.2) and its signature is frozen (ADR-020). + * + * Optional — absent slot or NULL ⟹ the DP's default is used. Appended per + * ADR-020 (append-only within a major). + * + * @param xdp Pointer to self. + * @param strength [0,1], or negative for the DP's default. + */ + void (*set_overlay_2d_filter_strength)(struct xrt_display_processor_d3d11 *xdp, float strength); }; @@ -817,7 +837,8 @@ XRT_DP_ABI_ASSERT(offsetof(struct xrt_display_processor_d3d11, lift_stream_destr XRT_DP_ABI_ASSERT(offsetof(struct xrt_display_processor_d3d11, lift_convert) == XRT_DP_D3D11_BASE_OFF + 28 * sizeof(void *), XRT_DP_ABI_MSG); XRT_DP_ABI_ASSERT(offsetof(struct xrt_display_processor_d3d11, lift_convert_blob) == XRT_DP_D3D11_BASE_OFF + 29 * sizeof(void *), XRT_DP_ABI_MSG); XRT_DP_ABI_ASSERT(offsetof(struct xrt_display_processor_d3d11, set_overlay_2d) == XRT_DP_D3D11_BASE_OFF + 30 * sizeof(void *), XRT_DP_ABI_MSG); -XRT_DP_ABI_ASSERT(sizeof(struct xrt_display_processor_d3d11) == XRT_DP_D3D11_BASE_OFF + 31 * sizeof(void *), XRT_DP_ABI_MSG); +XRT_DP_ABI_ASSERT(offsetof(struct xrt_display_processor_d3d11, set_overlay_2d_filter_strength) == XRT_DP_D3D11_BASE_OFF + 31 * sizeof(void *), XRT_DP_ABI_MSG); +XRT_DP_ABI_ASSERT(sizeof(struct xrt_display_processor_d3d11) == XRT_DP_D3D11_BASE_OFF + 32 * sizeof(void *), XRT_DP_ABI_MSG); /*! * Defined when this header carries the five lift slots (lift_get_caps, @@ -864,6 +885,13 @@ XRT_DP_ABI_ASSERT(sizeof(struct xrt_display_processor_d3d11) */ #define XRT_DP_D3D11_HAS_OVERLAY_2D 1 +/*! + * Defined when this header carries the set_overlay_2d_filter_strength slot + * (XR_DXR_weave v15), so a plug-in built against an older runtime can + * #ifdef-guard its implementation. Purely additive (ADR-020). + */ +#define XRT_DP_D3D11_HAS_OVERLAY_2D_FILTER_STRENGTH 1 + // clang-format on /*! @@ -1397,6 +1425,23 @@ xrt_display_processor_d3d11_set_overlay_2d(struct xrt_display_processor_d3d11 *x return xdp->set_overlay_2d(xdp, d3d11_context, overlay_srv, width, height, encoding, layer_unchanged); } +/*! + * @copydoc xrt_display_processor_d3d11::set_overlay_2d_filter_strength + * + * Helper for calling through the function pointer; a no-op (the DP's default + * applies) when the slot is absent or NULL. + * + * @public @memberof xrt_display_processor_d3d11 + */ +static inline void +xrt_display_processor_d3d11_set_overlay_2d_filter_strength(struct xrt_display_processor_d3d11 *xdp, float strength) +{ + if (!XRT_DP_HAS_SLOT(xdp, set_overlay_2d_filter_strength) || xdp->set_overlay_2d_filter_strength == NULL) { + return; + } + xdp->set_overlay_2d_filter_strength(xdp, strength); +} + //! True when @p xdp also carries lift_convert_blob (GAUSSIANS). static inline bool xrt_display_processor_d3d11_has_lift_blob(struct xrt_display_processor_d3d11 *xdp) diff --git a/src/xrt/ipc/client/ipc_client.h b/src/xrt/ipc/client/ipc_client.h index 387c1d9a4e..749831947c 100644 --- a/src/xrt/ipc/client/ipc_client.h +++ b/src/xrt/ipc/client/ipc_client.h @@ -268,6 +268,7 @@ comp_ipc_client_compositor_weave_submit(struct xrt_compositor *xc, const struct xrt_rect *flat_rects, bool mono_in_2d, bool overlay_unchanged, + float overlay_filter_strength, bool *out_have_output, uint32_t *out_width, uint32_t *out_height, diff --git a/src/xrt/ipc/client/ipc_client_compositor.c b/src/xrt/ipc/client/ipc_client_compositor.c index b9f5e082c8..9d77981c01 100644 --- a/src/xrt/ipc/client/ipc_client_compositor.c +++ b/src/xrt/ipc/client/ipc_client_compositor.c @@ -587,7 +587,8 @@ weave_pack_submit_args(struct ipc_arg_weave_submit *args, uint32_t flat_rect_count, const struct xrt_rect *flat_rects, bool mono_in_2d, - bool overlay_unchanged) + bool overlay_unchanged, + float overlay_filter_strength) { U_ZERO(args); args->rect_x = rect_x; @@ -650,6 +651,10 @@ weave_pack_submit_args(struct ipc_arg_weave_submit *args, // reuse its lens prefilter of it. Only meaningful with an overlay; 0 (a // pre-v14 caller) is "may have changed", the old behaviour. args->overlay_unchanged = (have_overlay && overlay_unchanged) ? 1u : 0u; + + // v15: the caller's lens-filter strength for the overlay; negative = not set + // (the DP's own default). Only meaningful with an overlay. + args->overlay_filter_strength = have_overlay ? overlay_filter_strength : -1.0f; } xrt_result_t @@ -672,6 +677,7 @@ comp_ipc_client_compositor_weave_submit(struct xrt_compositor *xc, const struct xrt_rect *flat_rects, bool mono_in_2d, bool overlay_unchanged, + float overlay_filter_strength, bool *out_have_output, uint32_t *out_width, uint32_t *out_height, @@ -706,7 +712,7 @@ comp_ipc_client_compositor_weave_submit(struct xrt_compositor *xc, const bool have_overlay = (overlay_handle != XRT_GRAPHICS_BUFFER_HANDLE_INVALID); weave_pack_submit_args(&args, rect_x, rect_y, rect_w, rect_h, rect_count, rects, have_overlay, overlay_rect_count, overlay_rects, weave_frame_first, layout, flat_rect_count, - flat_rects, mono_in_2d, overlay_unchanged); + flat_rects, mono_in_2d, overlay_unchanged, overlay_filter_strength); xrt_graphics_buffer_handle_t handles[2] = {in_handle, overlay_handle}; uint32_t handle_count = have_overlay ? 2u : 1u; @@ -886,6 +892,7 @@ comp_ipc_client_compositor_weave_submit_dmabuf(struct xrt_compositor *xc, const struct xrt_rect *flat_rects, bool mono_in_2d, bool overlay_unchanged, + float overlay_filter_strength, int *out_release_fence_fd, bool *out_have_output, uint32_t *out_width, @@ -928,7 +935,7 @@ comp_ipc_client_compositor_weave_submit_dmabuf(struct xrt_compositor *xc, struct ipc_arg_weave_submit args; weave_pack_submit_args(&args, rect_x, rect_y, rect_w, rect_h, rect_count, rects, overlay != NULL, overlay_rect_count, NULL, weave_frame_first, layout, flat_rect_count, flat_rects, - mono_in_2d, overlay_unchanged); + mono_in_2d, overlay_unchanged, overlay_filter_strength); struct ipc_arg_weave_dmabuf dmabuf = {0}; weave_pack_dmabuf(&dmabuf.input, in); diff --git a/src/xrt/ipc/server/ipc_server_handler.c b/src/xrt/ipc/server/ipc_server_handler.c index 75466650f4..a49ca1accd 100644 --- a/src/xrt/ipc/server/ipc_server_handler.c +++ b/src/xrt/ipc/server/ipc_server_handler.c @@ -6571,6 +6571,8 @@ ipc_handle_weave_submit(volatile struct ipc_client_state *ics, struct xrt_eye_positions eyes = {0}; // v14: per submit — a submit without XrWeaveSubmitOverlayUnchangedDXR clears it. comp_d3d11_service_weave_set_overlay_unchanged(ics->xc, args->overlay_unchanged != 0); + // v15: per submit — negative = not set by the app, the DP's own default applies. + comp_d3d11_service_weave_set_overlay_filter_strength(ics->xc, args->overlay_filter_strength); bool ok = comp_d3d11_service_weave_submit( // ics->xc, in_handle, in_is_dxgi, // args->rect_x, args->rect_y, args->rect_w, args->rect_h, // diff --git a/src/xrt/ipc/shared/ipc_protocol.h b/src/xrt/ipc/shared/ipc_protocol.h index 562eb1d43d..710ea4579f 100644 --- a/src/xrt/ipc/shared/ipc_protocol.h +++ b/src/xrt/ipc/shared/ipc_protocol.h @@ -913,6 +913,11 @@ struct ipc_arg_weave_submit //! Amendment). Advisory; 0 = may have changed (pre-v14 behaviour). Appended: //! 828 + 4 = 832 B; the dma-buf call is 4 + 832 + 136 = 972 of 1024. uint32_t overlay_unchanged; + + //! XR_DXR_weave v15 (XrWeaveSubmitOverlayFilterDXR): the caller's lens-filter + //! strength for the overlay, [0,1]; NEGATIVE = not set (the DP's own default). + //! Appended: 832 + 4 = 836 B; the dma-buf call is 4 + 836 + 136 = 976 of 1024. + float overlay_filter_strength; }; /*! diff --git a/src/xrt/state_trackers/oxr/oxr_weave.c b/src/xrt/state_trackers/oxr/oxr_weave.c index aeefa57401..8a78f9fa0a 100644 --- a/src/xrt/state_trackers/oxr/oxr_weave.c +++ b/src/xrt/state_trackers/oxr/oxr_weave.c @@ -151,6 +151,7 @@ comp_ipc_client_compositor_weave_submit(struct xrt_compositor *xc, const struct xrt_rect *flat_rects, bool mono_in_2d, bool overlay_unchanged, + float overlay_filter_strength, bool *out_have_output, uint32_t *out_width, uint32_t *out_height, @@ -194,6 +195,7 @@ comp_ipc_client_compositor_weave_submit_dmabuf(struct xrt_compositor *xc, const struct xrt_rect *flat_rects, bool mono_in_2d, bool overlay_unchanged, + float overlay_filter_strength, int *out_release_fence_fd, bool *out_have_output, uint32_t *out_width, @@ -584,6 +586,22 @@ oxr_xrWeaveSubmitDXR(XrSession session, const XrWeaveSubmitInfoDXR *submitInfo, submitInfo, XR_TYPE_WEAVE_SUBMIT_OVERLAY_UNCHANGED_DXR, XrWeaveSubmitOverlayUnchangedDXR); const bool overlay_unchanged = ovu != NULL && ovu->overlayUnchanged == XR_TRUE; + // Spec v15: a chained XrWeaveSubmitOverlayFilterDXR sets the lens-filter + // strength for the overlay; absent = the display processor's default (-1 on + // the wire). (x >= 0 && x <= 1) is false for NaN. + float overlay_filter_strength = -1.0f; + const XrWeaveSubmitOverlayFilterDXR *ovf = OXR_GET_INPUT_FROM_CHAIN( + submitInfo, XR_TYPE_WEAVE_SUBMIT_OVERLAY_FILTER_DXR, XrWeaveSubmitOverlayFilterDXR); + if (ovf != NULL) { + if (!(ovf->filterStrength >= 0.0f && ovf->filterStrength <= 1.0f)) { + return oxr_error(&log, XR_ERROR_VALIDATION_FAILURE, + "xrWeaveSubmitDXR: XrWeaveSubmitOverlayFilterDXR::filterStrength (%f) must be " + "in [0, 1]", + (double)ovf->filterStrength); + } + overlay_filter_strength = ovf->filterStrength; + } + // Spec v6 (#774): a chained XrWeaveSubmitLayoutDXR declares that the input // is a worst-case-sized N-view atlas (tiles packed contiguously from the // top-left at contentViewWidth/Height) instead of per-rect squeezed SBS. @@ -813,7 +831,7 @@ oxr_xrWeaveSubmitDXR(XrSession session, const XrWeaveSubmitInfoDXR *submitInfo, submitInfo->rect.offset.x, submitInfo->rect.offset.y, (uint32_t)submitInfo->rect.extent.width, (uint32_t)submitInfo->rect.extent.height, rect_count, rect_count > 0 ? rects : NULL, overlay_rect_count, submitInfo->firstChunk == XR_TRUE, layout.view_count > 0 ? &layout : NULL, - flat_rect_count, flat_rect_count > 0 ? flat_rects : NULL, mono_in_2d, overlay_unchanged, &release_fd, &have_out, &w, + flat_rect_count, flat_rect_count > 0 ? flat_rects : NULL, mono_in_2d, overlay_unchanged, overlay_filter_strength, &release_fd, &have_out, &w, &h, &fence_value, &eyes, &woven); } else #endif @@ -825,7 +843,7 @@ oxr_xrWeaveSubmitDXR(XrSession session, const XrWeaveSubmitInfoDXR *submitInfo, rect_count > 0 ? rects : NULL, overlay_handle, overlay_is_dxgi, overlay_rect_count, overlay_rect_count > 0 ? overlay_rects : NULL, submitInfo->firstChunk == XR_TRUE, layout.view_count > 0 ? &layout : NULL, flat_rect_count, flat_rect_count > 0 ? flat_rects : NULL, - mono_in_2d, overlay_unchanged, &have_out, &w, &h, &fence_value, &eyes); + mono_in_2d, overlay_unchanged, overlay_filter_strength, &have_out, &w, &h, &fence_value, &eyes); } if (xret == XRT_ERROR_FEATURE_NOT_SUPPORTED) { // The service has no weave engine for this platform (desktop Linux diff --git a/test_apps/probes/weave_rpc_probe_d3d11_win/main.cpp b/test_apps/probes/weave_rpc_probe_d3d11_win/main.cpp index 34de461684..b83c290304 100644 --- a/test_apps/probes/weave_rpc_probe_d3d11_win/main.cpp +++ b/test_apps/probes/weave_rpc_probe_d3d11_win/main.cpp @@ -128,6 +128,9 @@ enum OverlayMode OVERLAY_PAGE_ANIM, }; static OverlayMode g_overlayMode = OVERLAY_BAR; +//! --filter-strength=X (XR_DXR_weave v15): chain XrWeaveSubmitOverlayFilterDXR with +//! this lens-filter strength on every overlay submit; negative = omit (DP default). +static float g_filterStrength = -1.0f; static uint32_t g_pageFrame = 0; static const uint32_t kRectW = 640; //!< weaved sub-rect (per-view = kRectW) static const uint32_t kRectH = 360; @@ -1020,6 +1023,8 @@ ParseOptions(PWSTR cmdLineW) } else { LOG_ERROR("--overlay: bad value '%s' (want bar|none|page|page-anim) — ignored", v); } + } else if (_strnicmp(tok, "--filter-strength=", 18) == 0) { + g_filterStrength = (float)atof(tok + 18); } else if (_strnicmp(tok, "--size=", 7) == 0) { unsigned sw = 0, sh = 0; if (sscanf_s(tok + 7, "%ux%u", &sw, &sh) == 2 && sw >= 64 && sh >= 64 && sw <= 8192 && sh <= 8192) { @@ -1336,6 +1341,13 @@ wWinMain(HINSTANCE hInst, HINSTANCE, PWSTR pCmdLine, int) } // v14: declare the overlay unchanged (static page) so a DP can reuse its // lens prefilter of it. Advisory; a pre-v14 runtime ignores the struct. + // v15: an explicit lens-filter strength for the overlay (omitted = DP default). + XrWeaveSubmitOverlayFilterDXR ovf = {XR_TYPE_WEAVE_SUBMIT_OVERLAY_FILTER_DXR}; + if (haveOverlay && g_filterStrength >= 0.0f) { + ovf.filterStrength = g_filterStrength; + *tail = &ovf; + tail = &ovf.next; + } XrWeaveSubmitOverlayUnchangedDXR ovu = {XR_TYPE_WEAVE_SUBMIT_OVERLAY_UNCHANGED_DXR}; if (haveOverlay && overlayUnchanged) { ovu.overlayUnchanged = XR_TRUE;