#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