ValveSoftware / steam-runtime

A runtime environment for Steam applications
Other
1.21k stars 86 forks source link

Monado OpenXR runtime paired with OpenComposite fails under pressure vessel for VRChat VR mode #575

Open SpookySkeletons opened 1 year ago

SpookySkeletons commented 1 year ago

Hello, got a bit of an issue hope you guys could help with.

Got the following setup between multiple users and systems we'd like to report: We're using Monado as an OpenXR runtime which is paired with OpenComposite to translate OpenVR bindings to OpenXR. The setup works as expected when run outside pressure vessel, the game runs but pressure vessel appears to provide a necessary interface for gstreamer/ ffmpeg video decode for the in-game video players. When run with pressure vessel and the following ENV variables the game launches in desktop mode rather than inside the XR runtime, this is verified to be an issue as the same parameters used to launch beat saber work perfectly within the XR runtime. VR_OVERRIDE=~/OpenOVR/build XR_RUNTIME_JSON=/run/host/usr/share/openxr/1/openxr_monado.json PRESSURE_VESSEL_FILESYSTEMS_RW=$XDG_RUNTIME_DIR/monado_comp_ipc %command% Found in the repo: https://gitlab.com/znixian/OpenOVR

moshimeow commented 1 year ago

I have this issue too

smcv commented 1 year ago

Please provide the information that the issue report template asked for, like system information and logs. We are not able to read your mind or the contents of your computer.

I don't have access to VR hardware, so I really need that information: I can't just try this myself, even if there's enough information here to reproduce exactly what you did (but I also don't know enough about VR to know whether those instructions are complete).

Also, please try to use the usual bug reporting template: what you did; what you expected to happen; what actually happened. All of this is key information for being able to solve a bug.

If you have a workaround scenario where you are getting the behaviour that you expected (for example using a "known-good" title like The Lab, Beat Saber or VRChat instead of your mystery game, or using SteamVR instead of OpenComposite, or using a different OpenXR runtime, or something), then it would be useful to see a log from the scenario that isn't working, and a log from the most similar possible scenario that is working, to be able to compare them.

Also, what's in /usr/share/openxr/1/openxr_monado.json, and how is discovery and choice of VR stuff meant to work, in the absence of a container?

From the name of that file, I suspect that OpenXR might be structured like Vulkan drivers, Vulkan layers and EGL drivers, with a JSON manifest file that points to a native-code library that gets loaded by the OpenXR equivalent of Vulkan-Loader and libglvnd. Is that true?

If yes, then the only way it's likely to work reliably across the container boundary is by teaching pressure-vessel to do the same things that it does for Vulkan and EGL: load the JSON manifest on the host system, make sure the library is visible inside the container, adjust the library paths to be appropriate inside the container, write out a new JSON manifest, and set environment variables like XR_RUNTIME_JSON to point to the new manifest.

Just setting XR_RUNTIME_JSON=/run/host/... is not going to be reliable if that file points to resources by their host-filesystem paths, because inside the pressure-vessel container, the filesystem is structured differently (that's the whole purpose of the container).

smcv commented 1 year ago

Reminder that this is waiting for more information, and cannot be worked on without that information.

SpookySkeletons commented 1 year ago

@smcv I've since found a configuration that works after quite a bit of strace sifting.

It appears that opencomposite's lib is not allowed to load by the steam-runtime if it is located in the system root and I have kept the build folder beneath the home directory.

The only remaining issue now: Is there a possibility of a whitelist allowance to load the custom vrclient.so library from system installs so it may be distributed by package manager?

SpookySkeletons commented 1 year ago

More info: XR_RUNTIME_JSON=/run/host/usr/share/openxr/1/openxr_monado.json PRESSURE_VESSEL_FILESYSTEMS_RW=$XDG_RUNTIME_DIR/monado_comp_ipc %command%

The VR_OVERRIDE must not be specified to work. The vrpaths must point at the opencomposite build folder where the correct openvr driver structure and placement of the vrclient lib is located and this must currently be installed to the home folder for read access.

The main issue is failure to load the library if the vrpaths driver selection specifies a system root directory, the steam runtime will fail to read the library.

smcv commented 1 year ago

Please explain how this loading works, please don't just tell me workarounds! I want pressure-vessel to be able to do this in a way that will continue to work in future, which requires the information I asked for in https://github.com/ValveSoftware/steam-runtime/issues/575#issuecomment-1488543447.

smcv commented 1 year ago

Is there a possibility of a whitelist allowance to load the custom vrclient.so library from system installs so it may be distributed by package manager?

No, that's not technically possible - but if you provide the information I asked for, then pressure-vessel should be able to set this up automatically, in a more general way, with no special workarounds required.

kisak-valve commented 1 year ago

Closing pending actionable details.

SpookySkeletons commented 10 months ago

@smcv Please forgive me for the long time since but I would like to reopen this issue as I've since gained a better understanding of what we need here and why.

If you still have a moment to help me address I would be hugely appreciative & so would our active users.

I am expecting to launch an OpenVR or OpenXR game through Steam and for it to work with zero env var configuration of the game launch options as long as the OpenVR config and the OpenXR json in the home dir point to OpenComposite and Monado respectively as the OpenVR and OpenXR runtimes.

XR_RUNTIME_JSON takes absolute priority for the XR runtime location, followed by the installed home folder json, followed by the root json if neither home nor the var is set.

VRChat is the poison of choice here, launch has become pretty consistent after some work in the last months.

It is exactly like the vulkan loader but for the OpenXR API enabling implementations of the spec to not clash, your expectations are spot on.

After we stabilized some other components in our Rust frontend and a ton of other issues along the way we've found only the IPC PRESSURE_VESSEL_FILESYSTEMS_RW pinhole to be necessary.

We need to pinhole $XDG_RUNTIME_DIR/monado_comp_ipc socket in bwrap to ensure the Monado IPC can be accessed without a manual env var in the game itself.

The VR/XR application has to be able to interact with this IPC socket as well as OpenComposite compat layer which is achieved by the above.

https://gitlab.com/znixian/OpenOVR#linux-specific-info

logs.txt

SpookySkeletons commented 10 months ago

Adding one more IPC socket of interest

Ditto for $XDG_RUNTIME_DIR/wivrn_comp_ipc

WiVRn is the WiFi streaming XR runtime for wireless headsets and it uses monado as a submodule with a slightly different IPC socket name for its own purposes.

https://github.com/Meumeu/WiVRn

smcv commented 10 months ago

I am expecting to launch an OpenVR or OpenXR game through Steam and for it to work with zero env var configuration of the game launch options

I would like to achieve that too, but to do that, I need concrete, actionable details. Please don't assume that I can read your mind, or that I can see what's installed on your computer, or that I have VR hardware myself - none of these are true.

as long as the OpenVR config and the OpenXR json in the home dir point to OpenComposite and Monado respectively as the OpenVR and OpenXR runtimes

What, precisely, does this mean?

I don't know what "the OpenVR config points to OpenComposite" means, but if you tell me precise filenames and what they are expected to contain, then that would be something I can work with.

XR_RUNTIME_JSON takes absolute priority for the XR runtime location, followed by the installed home folder json, followed by the root json if neither home nor the var is set.

The information I need here is the equivalent of https://github.com/KhronosGroup/Vulkan-Loader/blob/main/docs/LoaderDriverInterface.md#driver-discovery, but for OpenXR. If there is a specification or a reference implementation for this, you can probably answer a lot of these questions with a link to the spec or the reference implementation.

Does OpenXR have multiple drivers loaded at the same time, like Vulkan, or is only a single driver ever loaded, like VA-API or VDPAU?

If you have a 64-bit process and a 32-bit process both using XR, can they end up using different drivers? Or are they somehow constrained to use the same driver? Or are 32-bit OpenXR processes not something that exists in reality?

I assume that by "XR_RUNTIME_JSON takes absolute priority for the XR runtime location" you mean "the environment variable XR_RUNTIME_JSON contains a path or paths, and if set, it is read first". Is that correct?

What are the acceptable values for that variable? Is it a single path, or a colon-separated list of paths, or what?

If that variable is set, are lower-priority sources of OpenXR runtimes ignored completely, like Vulkan VK_DRIVER_FILES, or are the runtimes listed in XR_RUNTIME_JSON treated as higher-priority but without completely ignoring the others, like VK_ADD_DRIVER_FILES?

If that variable is not set, what other paths are searched for JSON files describing drivers? From what you've said above, I infer that there is:

Are there others?

After locating one of these JSON files, what is the typical content of that file? [edited to add: What I really want here is the specification for the file format, equivalent to Vulkan's https://github.com/KhronosGroup/Vulkan-Loader/blob/main/docs/LoaderDriverInterface.md#driver-manifest-file-format.]

We need to pinhole $XDG_RUNTIME_DIR/monado_comp_ipc socket in bwrap to ensure the Monado IPC can be accessed without a manual env var in the game itself. Ditto for $XDG_RUNTIME_DIR/wivrn_comp_ipc

That's understood, and that is something that can be implemented, but if your game can't find an appropriate OpenXR or OpenVR driver, then this is not going to be enough. I want to solve this properly, not just put enough hacks in place to let you do the rest with out-of-band workarounds that make assumptions about how the container runtime works - but to achieve that, I need information.

Do these components have any fallbacks if XDG_RUNTIME_DIR is unset, or do they hard-require XDG_RUNTIME_DIR?

Conversely, is there any environment variable that is a higher priority than XDG_RUNTIME_DIR for this?

SpookySkeletons commented 10 months ago

This should be of assistance, loader spec located here: https://registry.khronos.org/OpenXR/specs/1.0/loader.html

This should help you answer a significant amount of the above questions for XR including the 32/64 bit, acceptable formats, and usage.

For OpenComposite in specific, it acts as a functional replacement for the SteamVR runtime, we set the ~/.config/openvr/openvrpaths.vrpath for our own purposes to change the OpenVR runtime path and mark it as read only to prevent the steam client from interfering with our desired setting.

This just for instance is the OpenXR config that Steam sets for its own XR loader:

bones@rog ~ $ cat ~/.config/openxr/1/active_runtime.json 
{
  "file_format_version": "1.0.0",
  "runtime": {
    "VALVE_runtime_is_steamvr": true,
    "library_path": "/home/bones/.local/share/Steam/steamapps/common/SteamVR/bin/linux64/vrclient.so",
    "name": "SteamVR"
  }
}

And Steam's natural OpenVR config:

bones@rog ~ $ cat ~/.config/openvr/openvrpaths.vrpath 
{
  "config": [
    "/home/bones/.local/share/Steam/config"
  ],
  "external_drivers": null,
  "jsonid": "vrpathreg",
  "log": [
    "/home/bones/.local/share/Steam/logs"
  ],
  "runtime": [
    "/home/bones/.local/share/Steam/steamapps/common/SteamVR"
  ],
  "version": 1
}

Now here is both of those configs while monado is active, switching both our XR to monado and the OpenVR runtime over to our translation layer:

bones@rog ~ $ cat ~/.config/openxr/1/active_runtime.json 
{
  "file_format_version": "1.0.0",
  "runtime": {
    "VALVE_runtime_is_steamvr": null,
    "library_path": "/home/bones/.local/share/envision/prefixes/lighthouse_default/lib64/libopenxr_monado.so",
    "name": null
  }
}

bones@rog ~ $ cat ~/.config/openvr/openvrpaths.vrpath 
{
  "config": [
    "/home/bones/.local/share/Steam/config"
  ],
  "external_drivers": null,
  "jsonid": "vrpathreg",
  "log": [
    "/home/bones/.local/share/Steam/logs"
  ],
  "runtime": [
    "/home/bones/Programs/git/opencomposite/build"
  ],
  "version": 1
}

All this above seems to go through just fine in the current steam-runtime with the only necessary fix being below.

I took the suggestion for an IPC pinhole for each runtime from this pipewire IPC bit in the wrapper: https://gitlab.steamos.cloud/steamrt/steam-runtime-tools/-/blob/main/pressure-vessel/wrap-pipewire.c#L125

Relevant pathing would be /run/user/%d/wivrn_comp_ipc and /run/user/%d/monado_comp_ipc as currently we use the PRESSURE_VESSEL_FILESYSTEMS_RW to allow through these two bits of IPC to make it work.

My own var dumped at the moment is exactly this PRESSURE_VESSEL_FILESYSTEMS_RW=/run/user/1000/monado_comp_ipc %command% on the game launch option.

Is an allowance for these two bits of IPC clean enough to land in your opinion?

Edit: for partial accidental post.

smcv commented 10 months ago

This should be of assistance, loader spec located here: https://registry.khronos.org/OpenXR/specs/1.0/loader.html

Thanks, I'll try to look into that next week or as time permits. At first glance, it looks very much "the same shape" as Vulkan, so generalizing it from our existing Vulkan support will be a good starting point.

Do you know how much of this gets loaded by the game itself (inside the pressure-vessel container), and how much is for external infrastructure that we don't need to care about? This will go quicker if I can ignore the parts that are never going to be in a pressure-vessel container anyway, like maybe if there is a system-level compositor that's analogous to your X11/Wayland server.

~/.config/openvr/openvrpaths.vrpath

Is this part of OpenXR, or is it a different thing? If different, I'll probably need similar information for that.

Is an allowance for these two bits of IPC clean enough to land in your opinion?

The problem is that if OpenXR is "the same shape" as Vulkan, then the only reason this is working for you is that you have it all installed in your home directory, and pressure-vessel shares your home directory with the game by default. If your OpenXR or OpenVR drivers were installed in /usr, then they wouldn't work.

Similarly, if your OpenXR or OpenVR drivers have non-trivial dependencies, then it's only a matter of luck that they are working in the container. If they depended on some library that wasn't already pulled into the container "by mistake" by your GL/Vulkan/etc. drivers, then they'd fail to load.

But, if we teach pressure-vessel how OpenXR works, the same way we already taught it to discover Vulkan drivers, then it probably could load drivers and their dependencies from /usr, without needing any special hacks like hard-coding /run/host/ paths.

smcv commented 10 months ago

if we teach pressure-vessel how OpenXR works

... for which a prerequisite is someone teaching me how OpenXR works :-)

SpookySkeletons commented 10 months ago

You'll have to ask Valve if you need hard details on the openvrpaths.vrpath as it's their own proprietary implementation of an OpenVR loader but it can be essentially directed away from the SteamVR folder and at the OpenComposite build folder where the translation binary resides that converts the OpenVR calls to OpenXR.

Makes everything happy in practice.

A /run/host for now would have us quite happy if you'd oblige for a short term fix and we'd be quite ecstatic to see a long term fix on the level with your vulkan loading for all the future OpenXR implementations coming down the pipe here on linux!

And yes, I do believe we originally settled on using the XR_RUNTIME_JSON setting to bypass exactly what you mention of not finding what's under the host /usr but our current dev model is sticking everything (OpenXR impl and the OpenVR translation layer) into home, so it's momentary nonissue; until distro repo bins begin shipping at which point it will matter...

We did have some issues with system cjson not being able to be pulled in so it had to be bundled of course, exactly a you say.

Thankyou much!

PassiveLemon commented 8 months ago

I did open a related issue at https://github.com/ValveSoftware/SteamVR-for-Linux/issues/679 but I will close that and contribute here instead as this issue has more activity.

xytovl commented 8 months ago

Hi, WiVRn dev here.

Just a little summary of what OpenXR applications are supposed to do.

Application loads an OpenXR loader, which will then locate the active OpenXR runtime.

Active runtime location is searched in XDG configuration directory, typically ~/.config/openxr/1/active_runtime.<arch>.json, /etc/openxr/1/active_runtime.<arch>.json, ~/.config/openxr/1/active_runtime.json, /etc/openxr/1/active_runtime.json where <arch> is defined here.

This manifest file contains the relative or absolute path to the OpenXR runtime library. In case of a relative path, this has to be intepreted as relative to the real path of the manifest file. For instance if /etc/openxr/1/active_runtime.json is a symlink to /usr/share/wivrn/openxr_manifest.json, path should be relative to /usr/share/wivrn/openxr_manifest.json.

As runtime implementations typically work as a service: library will connect to a daemon process which is managed by the host system. This communication channel must also be opened for OpenXR to work.

Each implementation may have different internals, in our case, Monado will use a socket file $XDG_RUNTIME_DIR/monado_comp_ipc, and WiVRn use $XDG_RUNTIME_DIR/wivrn_comp_ipc.

SpookySkeletons commented 6 months ago

@smcv Hey Simon, I've got a very rudimentary outline patch addressing the steam runtime container problem here Just a quick writeup matching existing wrappers with a happy clangd that looks about right Could you help me produce a steam-runtime dist I could test this code on as well and let you know if it solves the issue?

new file mode 100644
index 0000000..8a4067c
--- /dev/null
+++ b/pressure-vessel/wrap-openxr-ipc.c
@@ -0,0 +1,57 @@
+#include "wrap-openxr-ipc.h"
+
+static const char *
+get_temp_dir (void)
+{
+  const char *temp_dir;
+
+  temp_dir = g_getenv ("XDG_RUNTIME_DIR");
+
+  if (temp_dir == NULL)
+    temp_dir = g_getenv ("TMPDIR");
+
+  if (temp_dir == NULL)
+    temp_dir = g_getenv ("TMP");
+
+  if (temp_dir == NULL)
+    temp_dir = g_getenv ("TEMP");
+
+  if (temp_dir == NULL)
+    temp_dir = "/tmp";
+
+  return temp_dir;
+}
+
+void
+pv_wrap_add_openxr_ipc_args (FlatpakBwrap *sharing_bwrap)
+{
+  g_autoptr(GDir) dir = NULL;
+  const char *temp_dir = get_temp_dir ();
+  const char *member;
+
+  dir = g_dir_open (temp_dir, 0, NULL);
+
+  if (dir == NULL)
+    return;
+
+  for (member = g_dir_read_name (dir);
+       member != NULL;
+       member = g_dir_read_name (dir))
+    {
+      /* Bind the XR runtime compositor IPC sockets. They are expected to
+       * end in `_comp_ipc`, prefixed usually by monado or wivrn. */
+      if (g_str_has_suffix (member, "_comp_ipc"))
+        {
+          g_autofree gchar *host_socket =
+            g_build_filename (temp_dir, member, NULL);
+          g_autofree gchar *container_socket =
+            g_strdup_printf ("/run/user/%d/%s", getuid (), member);
+
+          flatpak_bwrap_add_args (sharing_bwrap,
+                                  "--ro-bind",
+                                    host_socket,
+                                    container_socket,
+                                  NULL);
+        }
+    }
+}
\ No newline at end of file
diff --git a/pressure-vessel/wrap-openxr-ipc.h b/pressure-vessel/wrap-openxr-ipc.h
new file mode 100644
index 0000000..e3ddb70
--- /dev/null
+++ b/pressure-vessel/wrap-openxr-ipc.h
@@ -0,0 +1,7 @@
+#pragma once
+
+#include "libglnx.h"
+
+#include "flatpak-bwrap-private.h"
+
+void pv_wrap_add_openxr_ipc_args (FlatpakBwrap *sharing_bwrap);
\ No newline at end of file
diff --git a/pressure-vessel/wrap-setup.c b/pressure-vessel/wrap-setup.c
index 79a4d9a..31e3318 100644
--- a/pressure-vessel/wrap-setup.c
+++ b/pressure-vessel/wrap-setup.c
@@ -161,6 +161,7 @@ pv_wrap_share_sockets (SrtEnvOverlay *container_env,
       flatpak_run_add_journal_args (sharing_bwrap);
       pv_wrap_add_pipewire_args (sharing_bwrap, container_env);
       pv_wrap_add_discord_args (sharing_bwrap);
+      pv_wrap_add_openxr_ipc_args (sharing_bwrap);
     }

   flatpak_bwrap_populate_runtime_dir (sharing_bwrap, NULL);
diff --git a/pressure-vessel/wrap-setup.h b/pressure-vessel/wrap-setup.h
index a690e3c..60dfc7d 100644
--- a/pressure-vessel/wrap-setup.h
+++ b/pressure-vessel/wrap-setup.h
@@ -34,6 +34,7 @@
 #include "wrap-discord.h"
 #include "wrap-home.h"
 #include "wrap-pipewire.h"
+#include "wrap-openxr-ipc.h"

 gchar *pv_wrap_check_bwrap (gboolean only_prepare,
                             SrtBwrapFlags *flags_out,
smcv commented 6 months ago

I've got a very rudimentary outline patch addressing the steam runtime container problem here

I would prefer to solve the OpenXR situation properly (including drivers installed in /usr) rather than adding ad-hoc code to pass through these specific sockets but without fully solving OpenXR. This is on my list, but my list is not short, so please be patient.

If your intention is to contribute code to steam-runtime-tools, please say who the copyright holders are (probably you, but possibly also the copyright holders of whatever code you have derived it from), and give permission to redistribute those contributions under one of the licenses used in steam-runtime-tools (either SPDX-License-Identifier: MIT or SPDX-License-Identifier: LGPL-2.1-or-later. Normally we use MIT for code written specifically for steam-runtime-tools, and LGPL-2.1-or-later for code derived from GLib or Flatpak (which are under that license). Sorry, I can't accept code contributions without a copyright holder and license.

get_temp_dir

Is this actually how OpenXR components find these sockets, or is it copy/paste from the Discord integration?

Whatever search path is genuinely used by these components, we need to use that same search path in pressure-vessel. If the relevant OpenXR components only look in $XDG_RUNTIME_DIR, we should only look in $XDG_RUNTIME_DIR. If the OpenXR components have a fallback to other paths, we should search those other paths, but we should not search paths that these OpenXR components wouldn't use.

For example, if you compare wrap-discord.c and wrap-pipewire.c you'll see that their fallbacks are different. The required fallbacks for these OpenXR components might be the same as one of those, or they might be some different thing. We need to make it correct, not just something that happens to work.

+      /* Bind the XR runtime compositor IPC sockets. They are expected to
+       * end in `_comp_ipc`, prefixed usually by monado or wivrn. */
+      if (g_str_has_suffix (member, "_comp_ipc"))

I'd prefer to iterate through a list of known compositors' sockets, instead of assuming that a _comp_ipc suffix always means "this is an XR compositor".

TTimo commented 6 months ago

Is any of this work related to https://github.com/ValveSoftware/SteamVR-for-Linux/issues/422 ?

SpookySkeletons commented 6 months ago

Not to my knowledge, SteamVR uses TCP sockets which work around the pressure vessel restrictions whereas monado and more utilize a unix socket for performance, so I don't expect this to be related but I could be wrong.

Thankyou for the review I will update the patch on the original post shortly to reflect in the hopes to get this upstream.

SpookySkeletons commented 6 months ago

I have updated the patch. MIT. Restricted socket to only what's necessary.

These sockets are present similar to the existing pipewire/ discord systems and need to be integrated either way beside a functional system for /usr installed runtimes. Monado or wivrn will only ever produce a single static named socket that needs to be reached by XR clients.

We can wait for a more official solution around /usr installed systems as currently we are using an orchestrator to deploy from source to ~/.local/ locations, as you can get to it, no trouble or rush there.

Please let me know if I can help any other way here.

new file mode 100644
index 0000000..9fe5d97
--- /dev/null
+++ b/pressure-vessel/wrap-openxr-ipc.c
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2017 Discord
+ * Copyright 2024 Duncan Spaulding <babblebones@protonmail.com>
+ * Copyright 2021-2023 Collabora Ltd.
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include "wrap-openxr-ipc.h"
+
+void
+pv_wrap_add_openxr_ipc_args (FlatpakBwrap *sharing_bwrap)
+{
+  g_autoptr(GDir) dir = NULL;
+  /* The monado/ wivrn socket is always found in $XDG_RUNTIME_DIR */
+  const char *temp_dir = g_getenv ("XDG_RUNTIME_DIR");
+  const char *member;
+
+  dir = g_dir_open (temp_dir, 0, NULL);
+
+  if (dir == NULL)
+    return;
+
+  for (member = g_dir_read_name (dir);
+       member != NULL;
+       member = g_dir_read_name (dir))
+    {
+      /* Bind the XR runtime compositor IPC sockets. They are expected to
+       * be `monado_comp_ipc` or `wivrn_comp_ipc`. More runtime ipc can
+       * be introduced as appropriate. */
+      if (g_str_equal (member, "monado_comp_ipc") || g_str_equal (member, "wivrn_comp_ipc"))
+        {
+          g_autofree gchar *host_socket =
+            g_build_filename (temp_dir, member, NULL);
+          g_autofree gchar *container_socket =
+            g_strdup_printf ("/run/user/%d/%s", getuid (), member);
+
+          flatpak_bwrap_add_args (sharing_bwrap,
+                                  "--ro-bind",
+                                    host_socket,
+                                    container_socket,
+                                  NULL);
+        }
+    }
+}
\ No newline at end of file
diff --git a/pressure-vessel/wrap-openxr-ipc.h b/pressure-vessel/wrap-openxr-ipc.h
new file mode 100644
index 0000000..e3ddb70
--- /dev/null
+++ b/pressure-vessel/wrap-openxr-ipc.h
@@ -0,0 +1,7 @@
+#pragma once
+
+#include "libglnx.h"
+
+#include "flatpak-bwrap-private.h"
+
+void pv_wrap_add_openxr_ipc_args (FlatpakBwrap *sharing_bwrap);
\ No newline at end of file
diff --git a/pressure-vessel/wrap-setup.c b/pressure-vessel/wrap-setup.c
index 79a4d9a..7efa478 100644
--- a/pressure-vessel/wrap-setup.c
+++ b/pressure-vessel/wrap-setup.c
@@ -161,6 +161,7 @@ pv_wrap_share_sockets (SrtEnvOverlay *container_env,
       flatpak_run_add_journal_args (sharing_bwrap);
       pv_wrap_add_pipewire_args (sharing_bwrap, container_env);
       pv_wrap_add_discord_args (sharing_bwrap);
+      pv_wrap_add_openxr_ipc_args (sharing_bwrap);
     }

   flatpak_bwrap_populate_runtime_dir (sharing_bwrap, NULL);
diff --git a/pressure-vessel/wrap-setup.h b/pressure-vessel/wrap-setup.h
index a690e3c..60dfc7d 100644
--- a/pressure-vessel/wrap-setup.h
+++ b/pressure-vessel/wrap-setup.h
@@ -34,6 +34,7 @@
 #include "wrap-discord.h"
 #include "wrap-home.h"
 #include "wrap-pipewire.h"
+#include "wrap-openxr-ipc.h"

 gchar *pv_wrap_check_bwrap (gboolean only_prepare,
                             SrtBwrapFlags *flags_out,