Skip to content
Closed
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
16 changes: 16 additions & 0 deletions Editor/DisplayXRPreviewSession.cs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,12 @@ public static class DisplayXRPreviewSession
private static Camera s_SelectedSourceCamera;
public static Camera SelectedCamera => s_SelectedSourceCamera;

/// <summary>
/// EditorPrefs key for the shared-texture-mode toggle (issue #131).
/// Read before start; the Preview Window exposes the toggle. Default off.
/// </summary>
public const string SharedTextureModePrefKey = "DisplayXR_SA_SharedTextureMode";

// SessionState survives domain reload (unlike static fields)
private const string kPlayModeStartedKey = "DisplayXR_SA_StartedByPlayMode";
private const string kXRWasEnabledKey = "DisplayXR_SA_XRWasEnabled";
Expand Down Expand Up @@ -278,6 +284,16 @@ public static bool Start()
tempRT.Release();
UnityEngine.Object.DestroyImmediate(tempRT);
}

// Shared-texture mode (issue #131) — opt-in, default off. When on,
// the runtime weaves into a plugin-allocated shared texture and the
// plugin presents it; required for 2D surround. Read from EditorPrefs
// so the Preview Window toggle controls it; pushed before start.
{
bool sharedTex = UnityEditor.EditorPrefs.GetBool(SharedTextureModePrefKey, false);
try { DisplayXRNative.displayxr_standalone_set_shared_texture_mode(sharedTex ? 1 : 0); }
catch (System.EntryPointNotFoundException) { /* older cached DLL */ }
}
#endif

int result = DisplayXRNative.displayxr_standalone_start(runtimeJson);
Expand Down
19 changes: 19 additions & 0 deletions Editor/DisplayXRPreviewWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,25 @@ void OnGUI()

m_AutoRefresh = GUILayout.Toggle(m_AutoRefresh, "Auto Refresh",
EditorStyles.toolbarButton, GUILayout.Width(100));

// Shared-texture mode (issue #131). Opt-in; takes effect at next
// Start (handle vs shared-texture is decided at session create), so
// toggling while running just stages the change. Required for 2D
// surround. Default off → current handle-mode behavior.
{
bool cur = EditorPrefs.GetBool(
DisplayXRPreviewSession.SharedTextureModePrefKey, false);
bool next = GUILayout.Toggle(cur,
new GUIContent("Shared Tex (#131)",
"Shared-texture mode: runtime weaves into a plugin-allocated " +
"texture and the plugin presents it. Required for 2D surround. " +
"Applies at next Start."),
EditorStyles.toolbarButton, GUILayout.Width(120));
if (next != cur)
EditorPrefs.SetBool(
DisplayXRPreviewSession.SharedTextureModePrefKey, next);
}

GUILayout.FlexibleSpace();

// Status indicator
Expand Down
10 changes: 10 additions & 0 deletions Runtime/DisplayXRNative.cs
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,16 @@ public static extern int displayxr_standalone_get_preview_mouse_position(
[DllImport(LibName, CallingConvention = CallingConvention.Cdecl)]
public static extern void displayxr_standalone_set_unity_graphics_api(int api);

/// <summary>
/// (Windows only) Opt into shared-texture mode (issue #131): the plugin
/// allocates a panel-size shared output texture, binds it at session
/// create so the runtime weaves into it, and presents it each frame.
/// Required for 2D surround. Default off (handle mode — runtime presents).
/// Call BEFORE displayxr_standalone_start(); effective at next start.
/// </summary>
[DllImport(LibName, CallingConvention = CallingConvention.Cdecl)]
public static extern void displayxr_standalone_set_shared_texture_mode(int enable);

/// <summary>
/// (Windows D3D12 only) Get the atlas bridge texture opened on Unity's device.
/// C# uses Graphics.CopyTexture to copy the atlas RT into this each frame.
Expand Down
Binary file modified Runtime/Plugins/Windows/x64/displayxr_unity.dll
Binary file not shown.
86 changes: 84 additions & 2 deletions native~/displayxr_standalone.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,17 @@ typedef struct StandaloneState {
int32_t canvas_x;
int32_t canvas_y;
int has_display_mode_ext;

// Shared-texture mode (issue #131): resolved runtime state, set during
// start. 1 only if the mode was requested (s_shared_texture_mode_requested,
// a static that survives the start-time memset of s_sa) AND the backend
// produced a shared handle (non-D3D12 backends return null → handle-mode
// fallback). When active, the runtime weaves into our shared output texture
// and we present it via the backend's fw_* swapchain; default off = handle
// mode (runtime presents), preserved bit-for-bit.
int shared_texture_active;
#if defined(_WIN32)
int fw_created; // 1 once the plugin-side present swapchain is created
int window_closed; // Set to 1 when user closes the preview window
// Mouse button state tracked by sa_wndproc for the C# input controller
// (Unity's new Input System doesn't see our preview window's clicks).
Expand All @@ -175,6 +185,11 @@ static StandaloneGraphicsBackend *s_sa_backend = nullptr;
// -1 = unknown → default to the D3D12 backend on Windows (back-compat).
static int s_unity_gfx_api = -1;

// Shared-texture mode request (issue #131), pushed from C# before start. Kept
// outside s_sa so it survives the memset(&s_sa, ...) at the top of start. Read
// at session create to decide whether to allocate + bind the shared texture.
static int s_shared_texture_mode_requested = 0;

// Pick + create the standalone graphics backend for this platform / editor
// graphics API. Idempotent (no-op once created). A Vulkan editor selects the
// Vulkan backend; otherwise the D3D12 backend, which also bridges to a D3D11
Expand Down Expand Up @@ -1192,13 +1207,39 @@ displayxr_standalone_start(const char *runtime_json_path)
// Win32 window binding — identical for both graphics APIs. The runtime
// composites the woven output directly into this plugin-owned HWND.
// Declared at function scope so it outlives the xrCreateSession call below.
// Shared-texture mode (issue #131): allocate a panel-size shared output
// texture and hand its NT handle to the runtime so it weaves into the
// texture instead of presenting to the HWND. We then present it ourselves
// (fw_* swapchain) after each xrEndFrame. get_shared_handle() returns null
// on backends that don't implement it (D3D11/Metal/Vulkan today) → we fall
// back to handle mode so the flag is a safe no-op there.
void *shared_handle = NULL;
s_sa.shared_texture_active = 0;
if (s_shared_texture_mode_requested && s_sa_backend) {
if (s_sa_backend->create_shared_texture(
s_sa.display_info.display_pixel_width,
s_sa.display_info.display_pixel_height)) {
shared_handle = s_sa_backend->get_shared_handle();
}
if (shared_handle) {
s_sa.shared_texture_active = 1;
sa_log("[DisplayXR-SA] Shared-texture mode ACTIVE: %ux%u handle=%p\n",
s_sa.display_info.display_pixel_width,
s_sa.display_info.display_pixel_height, shared_handle);
} else {
sa_log("[DisplayXR-SA] Shared-texture mode requested but backend "
"produced no shared handle — falling back to handle mode\n");
}
}

XrWin32WindowBindingCreateInfoEXT win_binding = {};
win_binding.type = XR_TYPE_WIN32_WINDOW_BINDING_CREATE_INFO_EXT;
win_binding.windowHandle = (void *)s_sa.preview_hwnd;
win_binding.readbackCallback = NULL;
win_binding.readbackUserdata = NULL;
win_binding.sharedTextureHandle = NULL;
sa_log("[DisplayXR-SA] Win32 binding: preview HWND=%p\n", (void *)s_sa.preview_hwnd);
win_binding.sharedTextureHandle = shared_handle; // NULL = handle mode (runtime presents)
sa_log("[DisplayXR-SA] Win32 binding: preview HWND=%p sharedTex=%p\n",
(void *)s_sa.preview_hwnd, shared_handle);

XrGraphicsBindingD3D12KHR d3d12_binding = {}; // kept in scope for create_session
#if defined(ENABLE_VULKAN)
Expand Down Expand Up @@ -1277,6 +1318,16 @@ displayxr_standalone_stop(void)
wsui_standalone_on_session_destroyed(s_sa.pfn_destroy_swapchain);
destroy_atlas_swapchain();

#if defined(_WIN32)
// Tear down the shared-texture-mode present swapchain (must happen before
// the backend releases its device in destroy()). No-op if never created.
if (s_sa.fw_created && s_sa_backend) {
s_sa_backend->fw_destroy_swapchain();
}
s_sa.fw_created = 0;
s_sa.shared_texture_active = 0;
#endif

if (s_sa.local_space != XR_NULL_HANDLE && s_sa.pfn_destroy_space) {
s_sa.pfn_destroy_space(s_sa.local_space);
s_sa.local_space = XR_NULL_HANDLE;
Expand Down Expand Up @@ -1623,6 +1674,26 @@ displayxr_standalone_submit_frame_atlas(void *atlas_tex)
end_info.layers = layers;

s_sa.pfn_end_frame(s_sa.session, &end_info);

#if defined(_WIN32)
// Shared-texture mode (issue #131): the runtime wove into our shared output
// texture during xrEndFrame above (rather than presenting to the HWND), so
// present it to the preview window ourselves. Lazily create the present
// swapchain at panel dims — DXGI stretches it to the window client area, so
// no WM_SIZE reallocation is needed. Skipped in handle mode (runtime presents).
if (s_sa.shared_texture_active && s_sa_backend) {
uint32_t pw = s_sa.display_info.display_pixel_width;
uint32_t ph = s_sa.display_info.display_pixel_height;
if (!s_sa.fw_created && s_sa.preview_hwnd && pw > 0 && ph > 0) {
if (s_sa_backend->fw_create_swapchain((void *)s_sa.preview_hwnd, pw, ph)) {
s_sa.fw_created = 1;
sa_log("[DisplayXR-SA] Present swapchain created %ux%u\n", pw, ph);
}
}
if (s_sa.fw_created)
s_sa_backend->fw_present(pw, ph);
}
#endif
return 1;
}

Expand Down Expand Up @@ -1941,6 +2012,17 @@ displayxr_standalone_set_unity_graphics_api(int api)
ensure_sa_backend(); // create the matching backend up front
}

void
displayxr_standalone_set_shared_texture_mode(int enable)
{
// Must be called BEFORE displayxr_standalone_start — the flag is read at
// session create to decide whether to allocate + bind the shared output
// texture. Changing it mid-session has no effect until the next start.
s_shared_texture_mode_requested = enable ? 1 : 0;
sa_log("[DisplayXR-SA] shared_texture_mode = %d (effective at next start)\n",
s_shared_texture_mode_requested);
}

void
displayxr_standalone_set_unity_device(void *unity_native_tex)
{
Expand Down
7 changes: 7 additions & 0 deletions native~/displayxr_standalone.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,13 @@ DISPLAYXR_EXPORT void displayxr_standalone_set_unity_device(void *unity_native_t
/// @param api UnityGfxRenderer id of the editor's active graphics device.
DISPLAYXR_EXPORT void displayxr_standalone_set_unity_graphics_api(int api);

/// Opt into shared-texture mode (issue #131). When enabled, the plugin
/// allocates a panel-size shared output texture and binds it at session create
/// so the runtime weaves into it; the plugin then presents it each frame.
/// Required for 2D surround. Default off (handle mode — runtime presents).
/// Must be called BEFORE displayxr_standalone_start(); effective at next start.
DISPLAYXR_EXPORT void displayxr_standalone_set_shared_texture_mode(int enable);

/// Get the atlas bridge texture opened on Unity's device (Windows only).
/// Returns ID3D11Texture2D* if Unity is on D3D11, ID3D12Resource* if on D3D12.
/// C# passes this to Texture2D.CreateExternalTexture and uses Graphics.CopyTexture
Expand Down
Loading