#if XR_COMPOSITION_LAYERS using System; using System.Collections.Generic; using System.Runtime.InteropServices; using Unity.XR.CompositionLayers; using Unity.XR.CompositionLayers.Extensions; using Unity.XR.CompositionLayers.Services; using UnityEngine.XR.OpenXR.NativeTypes; namespace UnityEngine.XR.OpenXR.CompositionLayers { /// /// A general-purpose helper class for composition layer support. /// public static class OpenXRLayerUtility { internal unsafe delegate void LayerCallbackDelegate(int layerId, XrCompositionLayerBaseHeader* layer); static Dictionary _textureMap = new Dictionary(); /// /// Calls the methods in its invocation list when a swapchain is created on the graphics thread inside the UnityOpenXR lib. /// /// The instance id of the composition layer object. /// The handle to the native swapchain that was just created. public unsafe delegate void SwapchainCallbackDelegate(int layerId, ulong swapchainHandle); /// /// Calls the methods in its invocation list when a stereo swapchain is created on the graphics thread inside the UnityOpenXR lib. /// /// The instance id of the composition layer object. /// The handle to one of the stereo swapchains that was just created. /// The handle to one of the stereo swapchains that was just created. public unsafe delegate void StereoSwapchainCallbackDelegate(int layerId, ulong swapchainHandleLeft, ulong swapchainHandleRight); /// /// Helper method used to gather the extension components attached to a CompositionLayer GameObject. /// This method chains the native extension struct pointers of those extension components to initialize an OpenXR native object's Next pointer struct chain. /// /// Container for the instance id and CompositionLayer component of the composition layer. /// Represents what part of the composition layer to retrieve extensions for. /// A pointer to the head of an array of native extension objects that will be associated with a composition layer. public static unsafe void* GetExtensionsChain(CompositionLayerManager.LayerInfo layerInfo, CompositionLayerExtension.ExtensionTarget extensionTarget) { void* extensionsChainHead = null; void* extensionsChain = null; foreach (var extension in layerInfo.Layer.Extensions) { // Skip extension if not enabled or not the intended target. if (!extension.enabled || extension.Target != extensionTarget) continue; var extensionNativeStructPtr = extension.GetNativeStructPtr(); // Skip extension if no native pointer is provided. if (extensionNativeStructPtr == null) continue; // Initialize pointer chain if head has not been set. if (extensionsChainHead == null) { extensionsChainHead = extensionNativeStructPtr; extensionsChain = extensionsChainHead; } // Chain pointer if head has been initialized. else { ((XrBaseInStructure*)extensionsChain)->Next = extensionNativeStructPtr; extensionsChain = extensionNativeStructPtr; } } return extensionsChainHead; } /// /// Helper method used get the current app space for any native composition layer structs that may require an associated XrSpace. /// /// A handle to the current app space. /// Normally used when creating native composition layers. public static ulong GetCurrentAppSpace() => Features.OpenXRFeature.Internal_GetAppSpace(out ulong appSpaceId) ? appSpaceId : 0; /// /// Helper method used get the XR session handle for any native composition layer structs that may require an associated XrSession. /// /// A handle to the current xr session. public static ulong GetXRSession() => Features.OpenXRFeature.Internal_GetXRSession(out ulong xrSessionHandle) ? xrSessionHandle : 0; /// /// Create the struct that is passed to OpenXR SDK to create a swapchain. /// /// The instance id of the composition layer object. /// The struct used to create the swapchain. /// Optional parameter that can be used when an external surface will be used, like when using the Android Surface feature. /// Optional parameter that can be used if your composition layer needs to know the handle after swapchain creation. public static void CreateSwapchain(int layerId, XrSwapchainCreateInfo createInfo, bool isExternalSurface = false, SwapchainCallbackDelegate callback = null) { ext_composition_layers_CreateSwapchain(layerId, createInfo, isExternalSurface, callback); } /// /// Create the struct that is passed to OpenXR SDK to create a swapchain for stereo projection, like Projection layer type. /// /// The instance id of the composition layer object. /// The struct used to create the swapchain. /// Optional parameter that can be used if your composition layer needs to know the handles after swapchain creation. public static void CreateStereoSwapchain(int layerId, XrSwapchainCreateInfo createInfo, StereoSwapchainCallbackDelegate callback = null) { ext_composition_layers_CreateStereoSwapchain(layerId, createInfo, callback); } /// /// Release swapchain according to the id provided. /// /// The instance id of the composition layer object. public static void ReleaseSwapchain(int layerId) { ext_composition_layers_ReleaseSwapchain(layerId); } /// /// Return swapchain supported color format. /// /// The color format the swapchains will be using. public static Int64 GetDefaultColorFormat() { return ext_composition_layers_GetUnityDefaultColorFormat(); } /// /// Finds the render texture of the give texture id. /// /// The id of the render texture to find. /// The render texture with the provided id or null if no render textrue with that id was found. public static RenderTexture FindRenderTexture(UInt32 texId) { // texId will be 0 if swapchain has no images. if (texId == 0) return null; if (!_textureMap.TryGetValue(texId, out var renderTexture)) { var objs = Resources.FindObjectsOfTypeAll(); var name = $"XR Texture [{texId}]"; foreach (var rt in objs) { if (rt.name == name) { renderTexture = rt; _textureMap[texId] = rt; break; } } } return renderTexture; } /// /// Finds the render texture of the layer id. /// /// Container for the instance id and CompositionLayer component of the composition layer. /// The render texture with the provided id or null if no render textrue with that id was found. public static RenderTexture FindRenderTexture(CompositionLayerManager.LayerInfo layerInfo) { UInt32 texId = ext_compositor_layers_CreateOrGetRenderTextureId(layerInfo.Id); return FindRenderTexture(texId); } /// /// Handles transfering texture data to a render texture. /// /// The source texture that will be written into the provided render texture. /// The render texture that will be written to. public static void WriteToRenderTexture(Texture texture, RenderTexture renderTexture) { if (texture == null || renderTexture == null) return; if (renderTexture.dimension == Rendering.TextureDimension.Cube && texture.dimension == Rendering.TextureDimension.Cube) { Cubemap convertedTexture = null; // Check if graphics format or mipmap count is different and convert if necessary. if (renderTexture.graphicsFormat != texture.graphicsFormat || renderTexture.mipmapCount != texture.mipmapCount) { convertedTexture = new Cubemap(texture.width, renderTexture.graphicsFormat, Experimental.Rendering.TextureCreationFlags.None); // Convert the texture to the render texture format. if (!Graphics.ConvertTexture(texture, convertedTexture)) { Debug.LogError("Failed to convert Cubemap to Render Texture format!"); return; } } for (int i = 0; i < 6; i++) Graphics.CopyTexture(convertedTexture == null ? texture : convertedTexture, i, renderTexture, i); } else Graphics.Blit(texture, renderTexture); } /// /// Query the correct XR Textures for rendering and blit the layer textures. /// /// Container for the instance id and CompositionLayer component of the composition layer. /// The source texture that will be written into the provided render texture. /// The render texture that will be searched for and written to. /// Will be null if no render texture can be found for the provided layerInfo object. /// True if a render texture was found and written to, false if the provided texture is null or if no render texture was found for the provided layerInfo object. public static bool FindAndWriteToRenderTexture(CompositionLayerManager.LayerInfo layerInfo, Texture texture, out RenderTexture renderTexture) { if (texture == null) { renderTexture = null; return false; } renderTexture = FindRenderTexture(layerInfo); WriteToRenderTexture(texture, renderTexture); return renderTexture != null; } /// /// Query the correct XR Textures for rendering and blit the layer textures (For Projection Layer type). /// /// Container for the instance id and CompositionLayer component of the composition layer. /// The left stereo render texture that will be searched for and written to. /// Will be null if no render textures can be found for the provided layerInfo object. /// The right stereo render texture that will be searched for and written to. /// Will be null if no render textures can be found for the provided layerInfo object. /// True if both render textures were found and written to, false if no texture was found on the TexturesExtension component of the layerInfo object or if no render texture was found for the provided layerInfo object. public static bool FindAndWriteToStereoRenderTextures(CompositionLayerManager.LayerInfo layerInfo, out RenderTexture renderTextureLeft, out RenderTexture renderTextureRight) { var tex = layerInfo.Layer.GetComponent(); if (tex == null || tex.LeftTexture == null || tex.RightTexture == null) { renderTextureLeft = null; renderTextureRight = null; return false; } ext_compositor_layers_CreateOrGetStereoRenderTextureIds(layerInfo.Id, out uint leftId, out uint rightId); renderTextureLeft = FindRenderTexture(leftId); renderTextureRight = FindRenderTexture(rightId); WriteToRenderTexture(tex.LeftTexture, renderTextureLeft); WriteToRenderTexture(tex.RightTexture, renderTextureRight); return renderTextureLeft != null && renderTextureRight != null; } /// /// Add native layer structs to the endFrameInfo struct inside the UnityOpenXR lib - for custom layer type support /// /// Pointer to the native array of currently active composition layers. /// Pointer to the native array of order values for the currently active composition layers. /// Indicates the size of the layers and orders arrays. /// Indicates the size in bytes of a single element of the given array of composition layers. /// Layers sent must all be of the same type.Demonstrated in the OpenXRCustomLayerHandler class. public static unsafe void AddActiveLayersToEndFrame(void* layers, void* orders, int count, int layerByteSize) { ext_composition_layers_AddActiveLayers(layers, orders, count, layerByteSize); } /// /// Return the Surface object for Android External Surface support (Android only). /// /// The instance id of the composition layer object. /// Pointer to the android surface object. public static System.IntPtr GetLayerAndroidSurfaceObject(int layerId) { IntPtr surfaceObject = IntPtr.Zero; if (ext_composition_layers_GetLayerAndroidSurfaceObject(layerId, ref surfaceObject)) { return surfaceObject; } return IntPtr.Zero; } /// /// Sends an array of extensions to be attached to the native default compostion layer. /// /// Pointer to the array of extensions to attach to the default compostion layer. /// Currently only called by the OpenXRDefautLayer class. public static unsafe void SetDefaultSceneLayerExtensions(void* extensions) { ext_composition_layers_SetDefaultSceneLayerExtensions(extensions); } /// /// Sends what flags are to be added to the native default compostion layer. /// /// Flags to be added to the native default compostion layer. /// Currently only called by the OpenXRDefautLayer class. public static unsafe void SetDefaultLayerFlags(XrCompositionLayerFlags flags) { ext_composition_layers_SetDefaultSceneLayerFlags(flags); } const string LibraryName = "UnityOpenXR"; [DllImport(LibraryName)] internal static extern UInt32 ext_compositor_layers_CreateOrGetRenderTextureId(int id); [DllImport(LibraryName)] [return: MarshalAs(UnmanagedType.U1)] internal static extern bool ext_compositor_layers_CreateOrGetStereoRenderTextureIds(int id, out UInt32 leftId, out UInt32 rightId); [DllImport(LibraryName)] internal static extern void ext_composition_layers_CreateSwapchain(int id, XrSwapchainCreateInfo createInfo, [MarshalAs(UnmanagedType.I1)]bool isExternalSurface = false, SwapchainCallbackDelegate callback = null); [DllImport(LibraryName)] internal static extern void ext_composition_layers_CreateStereoSwapchain(int id, XrSwapchainCreateInfo createInfo, StereoSwapchainCallbackDelegate callback = null); [DllImport(LibraryName)] internal static extern Int64 ext_composition_layers_GetUnityDefaultColorFormat(); [DllImport(LibraryName)] internal static extern void ext_composition_layers_ReleaseSwapchain(int id); [DllImport(LibraryName)] internal static extern unsafe void ext_composition_layers_AddActiveLayers(void* layers, void* orders, int count, int size); [DllImport(LibraryName)] [return: MarshalAs(UnmanagedType.U1)] internal static extern bool ext_composition_layers_GetLayerAndroidSurfaceObject(int layerId, ref IntPtr surfaceObject); [DllImport(LibraryName)] internal static extern unsafe void ext_composition_layers_SetDefaultSceneLayerExtensions(void* extensions); [DllImport(LibraryName)] internal static extern void ext_composition_layers_SetDefaultSceneLayerFlags(XrCompositionLayerFlags flags); } } #endif