Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 70 additions & 13 deletions Runtime/DisplayXRWindowSpaceUI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,9 @@ public class DisplayXRWindowSpaceUI : MonoBehaviour
private const int kPanelSizeNone = 0;
private const int kPanelSizeTile = 1;
private const int kPanelSizeScreen = 2;
private const int kPanelSizeComposited = 3;
private int m_PanelSizeSource = kPanelSizeNone;
private int m_ShellMode = -1; // -1 = not yet queried
private static int s_ShellMode = -1; // -1 = not yet queried (fixed for the process)

// This component's private stage (see DisplayXROverlayStage): its canvas and
// camera sit there, far from any scene content and from every other overlay's
Expand Down Expand Up @@ -656,9 +657,23 @@ void LateUpdate()
}
}

private bool TryGetPanelPixelSize(out float pw, out float ph)
/// <summary>
/// The size in pixels of the window this app's window-space UI is composited into —
/// the frame <see cref="positionX"/>/<see cref="width"/> etc. are fractions of, and the
/// size the HUD RT aspect is derived from. Lay out HUD rects against THIS, not
/// <c>Screen.*</c>: in a transparent-overlay app on Windows <c>Screen.*</c> is Unity's
/// own cloaked window, which can have any size (#291). Source order: workspace tile
/// (shell) → the provider's composited window → <c>Screen.*</c>. Only the aspect is
/// meaningful across sources (native-DPI pixels). Returns false when nothing is known.
/// </summary>
public static bool TryGetWindowPixelSize(out float width, out float height)
{
float cw = 0f, ch = 0f;
return GetWindowPixelSize(out width, out height) != kPanelSizeNone;
}

private static int GetWindowPixelSize(out float cw, out float ch)
{
cw = 0f; ch = 0f;
int source = kPanelSizeNone;

// Workspace tile (#323): under the shell Unity's window is launched
Expand All @@ -675,14 +690,33 @@ private bool TryGetPanelPixelSize(out float pw, out float ph)
cw = tw; ch = th;
source = kPanelSizeTile;
}
// Built-app / Play Mode: the runtime composites into Unity's main
// window, so Screen.* is meaningful.
// The window the runtime actually composites into, as the provider last
// measured it (the eye swapchain is sized from the same number). In a
// transparent-overlay app that is the visible overlay window, while Unity's own
// window is cloaked/parked at whatever size Unity persisted (observed 512x728 and
// 3872x2248 next to a 3840x2160 overlay; it grows by the frame size each run) —
// so Screen.* gave a wrong aspect and squeezed the HUD. Only the ASPECT is used
// (see ComputeRtSize), so the native-DPI pixels need no conversion.
else if (TryGetCompositedSize(out float qw, out float qh))
{
cw = qw; ch = qh;
source = kPanelSizeComposited;
}
// No provider session (or an older native plugin): Screen.*, which is right
// whenever the runtime composites into Unity's main window.
else if (Screen.width > 0 && Screen.height > 0)
{
cw = Screen.width; ch = Screen.height;
source = kPanelSizeScreen;
}

return source;
}

private bool TryGetPanelPixelSize(out float pw, out float ph)
{
int source = GetWindowPixelSize(out float cw, out float ch);

if (source == kPanelSizeNone)
{
pw = ph = 0f;
Expand All @@ -695,7 +729,7 @@ private bool TryGetPanelPixelSize(out float pw, out float ph)
{
m_PanelSizeSource = source;
Debug.Log($"[DisplayXR] wsui: panel size source = " +
$"{(source == kPanelSizeTile ? "workspace tile" : "Screen.*")} " +
$"{(source == kPanelSizeTile ? "workspace tile" : source == kPanelSizeComposited ? "composited window" : "Screen.*")} " +
$"({cw}x{ch})");
}

Expand All @@ -704,10 +738,33 @@ private bool TryGetPanelPixelSize(out float pw, out float ph)
return true;
}

// The provider's last measured composited window size; false with no provider
// session, before the first measurement, or against a native plugin without the
// export (then never asked again).
private static bool s_CompositedSizeMissing;
private static bool TryGetCompositedSize(out float w, out float h)
{
w = h = 0f;
if (s_CompositedSizeMissing || !DisplayXRProviderDriver.IsActive) return false;
try
{
if (DisplayXRProviderNative.dxr_prov_get_composited_size(out uint cw, out uint ch) == 0 ||
cw == 0 || ch == 0)
return false;
w = cw; h = ch;
return true;
}
catch (System.EntryPointNotFoundException)
{
s_CompositedSizeMissing = true;
return false;
}
}

// The live workspace-tile canvas in px, or false when there is no tile size
// yet (slot not bound, older runtime, or no native binary at all in the
// editor before a Play session).
private bool TryGetTileCanvasSize(out float w, out float h)
private static bool TryGetTileCanvasSize(out float w, out float h)
{
w = h = 0f;
try
Expand All @@ -725,20 +782,20 @@ private bool TryGetTileCanvasSize(out float w, out float h)
// Shell mode is fixed for the process (an env var read at load), so cache it
// rather than P/Invoking twice a frame. An older native binary without the
// export would throw per call, so a missing entry point also latches here.
private bool IsShellMode
private static bool IsShellMode
{
get
{
if (m_ShellMode < 0)
if (s_ShellMode < 0)
{
try
{
m_ShellMode = DisplayXRNative.displayxr_is_shell_mode() != 0 ? 1 : 0;
s_ShellMode = DisplayXRNative.displayxr_is_shell_mode() != 0 ? 1 : 0;
}
catch (System.EntryPointNotFoundException) { m_ShellMode = 0; }
catch (System.DllNotFoundException) { m_ShellMode = 0; }
catch (System.EntryPointNotFoundException) { s_ShellMode = 0; }
catch (System.DllNotFoundException) { s_ShellMode = 0; }
}
return m_ShellMode > 0;
return s_ShellMode > 0;
}
}

Expand Down
9 changes: 9 additions & 0 deletions Runtime/Provider/DisplayXRProviderNative.cs
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,15 @@ public static extern void dxr_prov_get_local2d_bridge(
uint width, uint height,
out System.IntPtr nativePtr, out uint outWidth, out uint outHeight);

/// <summary>
/// Live size in px of the window (or workspace tile) the runtime composites this app
/// into, as last measured by the provider; 0 when no session runs or no size is known.
/// In a transparent-overlay app Unity's own window is cloaked, so <c>Screen.*</c> does
/// not describe it. Use the aspect; the absolute values are native-DPI pixels.
/// </summary>
[DllImport(LibName, CallingConvention = CallingConvention.Cdecl)]
public static extern int dxr_prov_get_composited_size(out uint width, out uint height);

/// <summary>Set the Local2D dest rect in client-window pixels. w&lt;=0||h&lt;=0 clears.</summary>
[DllImport(LibName, CallingConvention = CallingConvention.Cdecl)]
public static extern void dxr_prov_set_local2d_rect(int x, int y, int w, int h);
Expand Down
22 changes: 22 additions & 0 deletions native~/displayxr_xrprovider/displayxr_provider_session.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1094,6 +1094,25 @@ extern "C" int dxr_prov_workspace_tile_size(uint32_t *w, uint32_t *h)
return 0;
}

// The last LIVE size ps_window_size measured (tile canvas or the bound window's client),
// packed w<<32|h; 0 = none yet / only the display-dims fallback. Published for C#
// (dxr_prov_get_composited_size): DisplayXRWindowSpaceUI derives its panel aspect from it,
// because in a transparent-overlay app Screen.* is the size of Unity's own CLOAKED window,
// which has nothing to do with the window the runtime composites into. A cached value, not a
// live query, on purpose: on Linux the measurement goes through the X connection the runtime
// borrows (see displayxr_linux.c), which must not be driven from Unity's main thread.
static std::atomic<uint64_t> s_live_window_px{0};

int dxr_prov_get_composited_size(uint32_t *w, uint32_t *h)
{
uint64_t v = s_live_window_px.load();
uint32_t ww = (uint32_t)(v >> 32), hh = (uint32_t)(v & 0xffffffffu);
if (!s_ps.running || ww == 0 || hh == 0) return 0;
if (w) *w = ww;
if (h) *h = hh;
return 1;
}

// The size the app + provider render/author for. In workspace-tile mode (#225)
// this is the shell-driven tile canvas (so the render follows 3D-window resize);
// otherwise the bound overlay's live client size (= Unity's window client area).
Expand All @@ -1108,6 +1127,7 @@ static void ps_window_size(uint32_t *w, uint32_t *h)
ps_query_tile_size(); // lazy first fetch until the slot binds
if (s_ps.tile_px_w > 0 && s_ps.tile_px_h > 0) {
*w = s_ps.tile_px_w; *h = s_ps.tile_px_h;
s_live_window_px.store(((uint64_t)s_ps.tile_px_w << 32) | s_ps.tile_px_h);
return;
}
}
Expand All @@ -1130,6 +1150,7 @@ static void ps_window_size(uint32_t *w, uint32_t *h)
// self-hosting), which falls through to the display-info default below.
displayxr_linux_window_size(&ww, &hh);
#endif
if (ww > 0 && hh > 0) s_live_window_px.store(((uint64_t)ww << 32) | hh); // a real measurement
if (ww == 0 || hh == 0) {
ww = s_ps.display_info.is_valid ? s_ps.display_info.pixel_width : 1920;
hh = s_ps.display_info.is_valid ? s_ps.display_info.pixel_height : 1080;
Expand Down Expand Up @@ -5023,6 +5044,7 @@ int dxr_prov_session_start(const char *runtime_json_path,

void dxr_prov_session_stop(void)
{
s_live_window_px.store(0); // the next session measures its own window
for (int i = 0; i < PS_MAX_WSUI; i++) {
ProviderWsui *ws = &s_ps.wsui[i];
if (ws->swapchain && s_ps.pfn_destroy_swapchain) s_ps.pfn_destroy_swapchain(ws->swapchain);
Expand Down
8 changes: 8 additions & 0 deletions native~/displayxr_xrprovider/displayxr_provider_session.h
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,14 @@ DISPLAYXR_EXPORT int dxr_prov_get_zone_stereo_matrices(uint32_t zone, float *lv,
/// Fill `zone`'s window-client pixel rect (top-left origin). Returns 1 on success.
DISPLAYXR_EXPORT int dxr_prov_get_zone_rect_px(uint32_t zone, int *x, int *y, int *w, int *h);

/// The live size in pixels of the window (or workspace tile) the runtime composites this
/// app into — the size the eye swapchain is derived from — as last measured by the provider.
/// Returns 1 and fills *w/*h while a session runs and a real size is known; 0 otherwise.
/// In a transparent-overlay app Unity's own window is cloaked and Screen.* does not describe
/// it, so a window-space UI derives its panel aspect from this instead. Pixels of one window,
/// so the ASPECT is DPI-independent; don't mix the absolute values with managed geometry.
DISPLAYXR_EXPORT int dxr_prov_get_composited_size(uint32_t *w, uint32_t *h);

/// Local2D layer (#166 Phase B): lazily create the provider's Local2D overlay
/// swapchain + cross-device bridge sized to w×h and return the Unity-device handle
/// of the bridge. C# (DisplayXRLocal2D) Graphics.CopyTexture's its canvas RT into it
Expand Down
Loading