#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; using UnityEngine.Rendering; using static Unity.XR.CompositionLayers.CompositionLayersRuntimeSettings; namespace Unity.XR.PXR { /// /// A general-purpose helper class for composition layer support. /// public static class PXR_LayerUtility { internal unsafe delegate void LayerCallbackDelegate(int layerId, XrCompositionLayerBaseHeader* layer); private static Material textureM; private static Material cubeM; static Dictionary _textureMap = new Dictionary(); static Dictionary, Texture> _textureCache = new Dictionary, Texture>(); /// /// Calls the methods in its invocation list when a swapchain is created on the graphics thread inside the UnityPXR_ 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 UnityPXR_ 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 PXR_ 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.PXR_Feature.Internal_GetAppSpace(out ulong appSpaceId) ? appSpaceId : 0; // TODO public static ulong GetCurrentAppSpace() => 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.PXR_Feature.Internal_GetXRSession(out ulong xrSessionHandle) ? xrSessionHandle : 0; // TODO public static ulong GetXRSession() => 0; /// /// Create the struct that is passed to PXR_ 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) { Pxr_CompositorLayersCreateSwapchain(layerId, createInfo, isExternalSurface, callback); } /// /// Create the struct that is passed to PXR_ 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) { Pxr_CompositorLayersCreateStereoSwapchain(layerId, createInfo, callback); } /// /// Release swapchain according to the id provided. /// /// The instance id of the composition layer object. public static void ReleaseSwapchain(int layerId) { Pxr_CompositorLayersReleaseSwapchain(layerId); } /// /// Return swapchain supported color format. /// /// The color format the swapchains will be using. public static Int64 GetDefaultColorFormat() { if (GraphicsDeviceType.Vulkan == SystemInfo.graphicsDeviceType) { return (long)PXR_CompositionLayer.ColorForamt.VK_FORMAT_R8G8B8A8_SRGB; } else { return (long)PXR_CompositionLayer.ColorForamt.GL_SRGB8_ALPHA8; } } /// /// 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 Texture FindRenderTexture(int id, 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}]"; // for (int i = 0; i < objs.Length; i++) // { // Debug.Log($"FindRenderTexture 2 objs[i]={objs[i].name}"); // } bool found = false; foreach (var rt in objs) { if (rt.name == name) { renderTexture = rt; _textureMap[texId] = rt; found = true; 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 Texture FindRenderTexture(CompositionLayerManager.LayerInfo layerInfo) { UInt32 texId = Pxr_CompositorLayersCreateOrGetRenderTextureId(layerInfo.Id); return FindRenderTexture(layerInfo.Id, texId); } public static Texture CreateExternalTexture(int id, int width, int height, bool isCube) { int imageIndex = 0; PXR_Plugin.Render.UPxr_GetLayerNextImageIndexByRender(id, ref imageIndex); var cacheKey = ValueTuple.Create(id, imageIndex); if (_textureCache.TryGetValue(cacheKey, out var cachedTexture)) { return cachedTexture; } IntPtr ptr = IntPtr.Zero; PXR_Plugin.Render.UPxr_GetLayerImagePtr(id, (EyeType)0, imageIndex, ref ptr); if (IntPtr.Zero == ptr) { Debug.LogError($"WriteToRenderTexture id={id}, _textureMap, imageIndex={imageIndex}, IntPtr.Zero == ptr"); return null; } Texture nativeTexture; if (isCube) { nativeTexture = Cubemap.CreateExternalTexture(width, TextureFormat.RGBA32, false, ptr); } else { nativeTexture = Texture2D.CreateExternalTexture(width, height, TextureFormat.RGBA32, false, true, ptr); } if (nativeTexture == null) { Debug.LogError($"WriteToRenderTexture id={id}, _textureMap, imageIndex={imageIndex}, nativeTexture == null"); } if (nativeTexture != null) { nativeTexture.name = $"{id}+{imageIndex}"; _textureCache[cacheKey] = nativeTexture; } Debug.Log($"WriteToRenderTexture id={id}, imageIndex={imageIndex}, cacheKey={cacheKey}, ptr={ptr}"); return nativeTexture; } /// /// 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(int id, Texture sourceTextures, Texture nativeTexture, bool isCube) { if (sourceTextures == null) { Debug.LogError($"WriteToRenderTexture sourceTextures == null!"); return; } nativeTexture = CreateExternalTexture(id, sourceTextures.width, sourceTextures.height, isCube); if (nativeTexture == null) { Debug.LogError($"WriteToRenderTexture 11 id={id} nativeTexture == null"); return; } int eyeCount = 1; for (int i = 0; i < eyeCount; i++) { if (isCube && null == sourceTextures as Cubemap) { Debug.LogError($"WriteToRenderTexture 11 id={id} isCube && null == sourceTextures as Cubemap"); return; } int faceCount = isCube ? 6:1; for (int f = 0; f < faceCount; f++) { if (QualitySettings.activeColorSpace == ColorSpace.Gamma && sourceTextures != null) { Graphics.CopyTexture(sourceTextures, f, 0, nativeTexture, f, 0); } else { RenderTextureDescriptor rtDes = new RenderTextureDescriptor((int)sourceTextures.width, (int)sourceTextures.height, RenderTextureFormat.ARGB32, 0); rtDes.msaaSamples = 1; rtDes.useMipMap = true; rtDes.autoGenerateMips = false; rtDes.sRGB = true; RenderTexture renderTexture = RenderTexture.GetTemporary(rtDes); if (!renderTexture.IsCreated()) { renderTexture.Create(); } renderTexture.DiscardContents(); if (isCube) { if (cubeM == null) { Debug.Log($"WriteToRenderTexture id={id}, cubeM , f={f}, cubeM == null"); cubeM = new Material(Shader.Find("PXR_SDK/PXR_CubemapBlit")); } cubeM.SetInt("_d", f); Graphics.Blit(sourceTextures, renderTexture, cubeM); } else { if (textureM == null) { Debug.Log($"WriteToRenderTexture id={id}, textureM, textureM == null"); textureM = new Material(Shader.Find("PXR_SDK/PXR_Texture2DBlit")); } textureM.mainTexture = renderTexture; textureM.SetPass(0); //textureM.SetInt("_premultiply", isPremultipliedAlpha ? 1 : 0); Graphics.Blit(sourceTextures, renderTexture); } Graphics.CopyTexture(renderTexture, 0, 0, nativeTexture, f, 0); RenderTexture.ReleaseTemporary(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 Texture renderTexture, bool isCube) { if (texture == null) { Debug.Log($"FindAndWriteToRenderTexture texture == null"); renderTexture = null; return false; } renderTexture = FindRenderTexture(layerInfo); WriteToRenderTexture(layerInfo.Id, texture, renderTexture, isCube); return renderTexture != null; } /// /// Add native layer structs to the endFrameInfo struct inside the UnityPXR_ 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 PXR_CustomLayerHandler class. public static unsafe void AddActiveLayersToEndFrame(void* layers, void* orders, int count, int layerByteSize) { IntPtr ptrLayers = new IntPtr(layers); IntPtr ptrOrders = new IntPtr(orders); Pxr_CompositorLayersAddActiveLayers(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 (Pxr_CompositorLayersGetLayerAndroidSurfaceObject(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 PXR_DefautLayer class. public static unsafe void SetDefaultSceneLayerExtensions(void* extensions) { // IntPtr ptr = new IntPtr(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 PXR_DefautLayer class. public static unsafe void SetDefaultLayerFlags(XrCompositionLayerFlags flags) { // ext_composition_layers_SetDefaultSceneLayerFlags(flags); } const string LibraryName = "PxrPlatform"; [DllImport(LibraryName)] internal static extern UInt32 Pxr_CompositorLayersCreateOrGetRenderTextureId(int id); // Down [DllImport(LibraryName)] [return: MarshalAs(UnmanagedType.U1)] internal static extern bool Pxr_CompositorLayersCreateOrGetStereoRenderTextureIds(int id, out UInt32 leftId, out UInt32 rightId); // Down [DllImport(LibraryName)] internal static extern void Pxr_CompositorLayersCreateSwapchain(int id, XrSwapchainCreateInfo createInfo, [MarshalAs(UnmanagedType.I1)]bool isExternalSurface = false, SwapchainCallbackDelegate callback = null); // Down [DllImport(LibraryName)] internal static extern void Pxr_CompositorLayersCreateStereoSwapchain(int id, XrSwapchainCreateInfo createInfo, StereoSwapchainCallbackDelegate callback = null); // Down [DllImport(LibraryName)] internal static extern void Pxr_CompositorLayersReleaseSwapchain(int id); // Down [DllImport(LibraryName)] internal static extern unsafe void Pxr_CompositorLayersAddActiveLayers(void* layers, void* orders, int count, int size); // Down [DllImport(LibraryName)] [return: MarshalAs(UnmanagedType.U1)] internal static extern bool Pxr_CompositorLayersGetLayerAndroidSurfaceObject(int layerId, ref IntPtr surfaceObject); // Down [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