This commit is contained in:
2025-11-13 17:40:28 +08:00
parent 962ab49609
commit 10156da245
5503 changed files with 805282 additions and 0 deletions

View File

@@ -0,0 +1,84 @@
#if XR_COMPOSITION_LAYERS
using System.Collections.Generic;
using Unity.XR.CompositionLayers;
using Unity.XR.CompositionLayers.Extensions;
using Unity.XR.CompositionLayers.Layers;
using Unity.XR.CompositionLayers.Services;
using UnityEngine;
namespace Unity.XR.PXR
{
internal class PXR_CubeLayer : PXR_CustomLayerHandler<XrCompositionLayerCubeKHR>
{
protected override unsafe bool CreateSwapchain(CompositionLayerManager.LayerInfo layerInfo, out SwapchainCreateInfo swapchainCreateInfo)
{
TexturesExtension texture = layerInfo.Layer.GetComponent<TexturesExtension>();
if (texture == null || texture.enabled == false || texture.LeftTexture == null)
{
swapchainCreateInfo = default;
return false;
}
swapchainCreateInfo = new XrSwapchainCreateInfo()
{
Type = (uint)XrStructureType.XR_TYPE_SWAPCHAIN_CREATE_INFO,
Next = PXR_LayerUtility.GetExtensionsChain(layerInfo, CompositionLayerExtension.ExtensionTarget.Swapchain),
CreateFlags = 0,
UsageFlags = (ulong)(XrSwapchainUsageFlags.XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XrSwapchainUsageFlags.XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT),
Format = PXR_LayerUtility.GetDefaultColorFormat(),
SampleCount = 1,
Width = (uint)(texture.LeftTexture.width),
Height = (uint)(texture.LeftTexture.height),
FaceCount = 6,
ArraySize = 1,
MipCount = (uint)texture.LeftTexture.mipmapCount,
};
return true;
}
protected override unsafe bool CreateNativeLayer(CompositionLayerManager.LayerInfo layerInfo, SwapchainCreatedOutput swapchainOutput, out XrCompositionLayerCubeKHR nativeLayer)
{
var data = layerInfo.Layer.LayerData as CubeProjectionLayerData;
var transform = layerInfo.Layer.GetComponent<Transform>();
nativeLayer = new XrCompositionLayerCubeKHR()
{
Type = (uint)XrStructureType.XR_TYPE_COMPOSITION_LAYER_CUBE_KHR,
Next = PXR_LayerUtility.GetExtensionsChain(layerInfo, CompositionLayerExtension.ExtensionTarget.Layer),
LayerFlags = data.BlendType == BlendType.Premultiply ? XrCompositionLayerFlags.SourceAlpha : XrCompositionLayerFlags.SourceAlpha | XrCompositionLayerFlags.UnPremultipliedAlpha,
Space = PXR_LayerUtility.GetCurrentAppSpace(),
EyeVisibility = 0,
Swapchain = swapchainOutput.handle,
ImageArrayIndex = 0,
Orientation = new XrQuaternionf(PXR_Utility.ComputePoseToWorldSpace(transform, CompositionLayerManager.mainCameraCache).rotation)
};
return true;
}
protected override bool ModifyNativeLayer(CompositionLayerManager.LayerInfo layerInfo, ref XrCompositionLayerCubeKHR nativeLayer)
{
var texturesExtension = layerInfo.Layer.GetComponent<TexturesExtension>();
if (texturesExtension == null || texturesExtension.enabled == false || texturesExtension.LeftTexture == null)
return false;
var transform = layerInfo.Layer.GetComponent<Transform>();
nativeLayer.Space = PXR_LayerUtility.GetCurrentAppSpace();
nativeLayer.Orientation = new XrQuaternionf(PXR_Utility.ComputePoseToWorldSpace(transform, CompositionLayerManager.mainCameraCache).rotation);
return true;
}
protected override bool ActiveNativeLayer(CompositionLayerManager.LayerInfo layerInfo, ref XrCompositionLayerCubeKHR nativeLayer)
{
nativeLayer.Space = PXR_LayerUtility.GetCurrentAppSpace();
return base.ActiveNativeLayer(layerInfo, ref nativeLayer);
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0f76b65d70a4bf240be9fa70a4d5d87e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,491 @@
#if XR_COMPOSITION_LAYERS
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using Unity.XR.CompositionLayers.Extensions;
using Unity.XR.CompositionLayers.Services;
using UnityEngine;
#if UNITY_VIDEO
using UnityEngine.Video;
#endif
namespace Unity.XR.PXR
{
/// <summary>
/// Provides a base implementation for the <see cref="PXR_LayerProvider.ILayerHandler"/> interface.
/// You can implement the required methods of this abstract class to create a concrete layer handler.
/// </summary>
/// <remarks>
/// The <see cref="PXR_LayerProvider.ILayerHandler"/> methods that this class implements handle adding
/// and removing composition layers from native arrays, swap chain creation dispatching and other tasks
/// required by the Unity side of the API.
///
/// The abstract methods that you must implement handle the custom aspects of your layer. These methods include:
///
/// * <see cref="CreateSwapchain(CompositionLayerManager.LayerInfo, out SwapchainCreateInfo)"/>
/// * <see cref="CreateNativeLayer(CompositionLayerManager.LayerInfo, PXR_CustomLayerHandler{T}.SwapchainCreatedOutput, out T)"/>
/// * <see cref="ModifyNativeLayer(CompositionLayerManager.LayerInfo, ref T)"/>
///
/// You are not required to implement a custom layer handler based on this abstract class, but doing so should be
/// easier than implementing the <see cref="PXR_LayerProvider.ILayerHandler"/> interface in its entirety.
///
/// You must register your concrete layer handler object with
/// <see cref="PXR_LayerProvider.RegisterLayerHandler(Type, PXR_LayerProvider.ILayerHandler)"/>.
/// </remarks>
/// <typeparam name="T">The native PXR_ structure of the composition layer to handle.</typeparam>
public abstract class PXR_CustomLayerHandler<T> : PXR_LayerProvider.ILayerHandler, IDisposable where T : struct
{
/// <summary>
/// Container for swapchain related information that may be needed during the creation of the native PXR_ composition layer struct.
/// </summary>
protected struct SwapchainCreateInfo
{
/// <summary>
/// Native structure for the swapchain creation info.
/// </summary>
public XrSwapchainCreateInfo nativeStruct;
/// <summary>
/// Tells if swapchain is using an external surface.
/// </summary>
[MarshalAs(UnmanagedType.I1)]
public bool isExternalSurface;
/// <summary>
/// Tells if swapchain should be stereo.
/// </summary>
public bool isStereo;
/// <summary>
/// Initializes and returns an instance of SwapchainCreateInfo with the provided parameters.
/// </summary>
/// <param name="xrSwapchainCreateInfo">Native structure for the swapchain creation info.</param>
/// <param name="isExternalSurface">Tells if swapchain is using an external surface.</param>
/// <param name="isStereo">Tells if swapchain should be stereo.</param>
public SwapchainCreateInfo(XrSwapchainCreateInfo xrSwapchainCreateInfo, bool isExternalSurface = false, bool isStereo = false)
{
this.nativeStruct = xrSwapchainCreateInfo;
this.isExternalSurface = isExternalSurface;
this.isStereo = isStereo;
}
/// <summary>
/// Implicit conversion with just a native XrSwapchainCreateInfo struct.
/// </summary>
/// <param name="createInfo">The native struct to convert.</param>
public static implicit operator SwapchainCreateInfo(XrSwapchainCreateInfo createInfo) => new SwapchainCreateInfo(createInfo);
}
/// <summary>
/// Container for swapchain related information that may be needed during the creation of the native PXR_ composition layer struct.
/// </summary>
protected struct SwapchainCreatedOutput
{
/// <summary>
/// The handle of the created swapchain.
/// Can be used to initialize the swapchain member of a native PXR_ composition layer struct.
/// </summary>
public ulong handle;
/// <summary>
/// The second handle of the created stereo swapchain.
/// Can be used to initialize the swapchain member of a native PXR_ composition layer struct.
/// </summary>
public ulong secondStereoHandle;
}
/// <summary>
/// Container for grouping render information for each compostion layer.
/// </summary>
class LayerRenderInfo
{
public Texture RenderTexture;
public Texture Texture;
#if UNITY_VIDEO
public VideoPlayer videoPlayer;
#endif
public MeshCollider meshCollider;
}
/// <summary>
/// Initializes and returns an instance of this <c>PXR_CustomLayerHandler&lt;T&gt;</c> while also setting the singleton instance member.
/// </summary>
protected PXR_CustomLayerHandler() => Instance = this;
/// <summary>
/// Singleton instance of this specific handler.
/// </summary>
protected static PXR_CustomLayerHandler<T> Instance;
/// <summary>
/// Deinitializes this instance of c>PXR_CustomLayerHandler&lt;T&gt;</c>.
/// </summary>
~PXR_CustomLayerHandler() => Dispose(false);
/// <summary>
/// Override this method to create the <see cref="XrSwapchainCreateInfo"/> struct that is passed to PXR_
/// to create a swapchain.
/// </summary>
/// <remarks>
/// To add extensions when constructing the <see cref="XrSwapchainCreateInfo"/> struct, initialize
/// the <c>Next</c> pointer with
/// <see cref="PXR_LayerUtility.GetExtensionsChain(CompositionLayerManager.LayerInfo, Unity.XR.CompositionLayers.CompositionLayerExtension.ExtensionTarget)"/>.
/// </remarks>
/// <param name="layerInfo"> Container for the instance id and CompositionLayer component of the composition layer
/// that was just created.</param>
/// <param name="swapchainCreateInfo"> An <c>XrSwapchainCreateInfo</c> object created and initialized by the concrete implementation of this method.</returns>
/// <returns> A bool indicating success or failure.</returns>
protected abstract bool CreateSwapchain(CompositionLayerManager.LayerInfo layerInfo, out SwapchainCreateInfo swapchainCreateInfo);
/// <summary>
/// Override this method to create the native composition layer struct of type T that is passed to PXR_.
/// A swapchain info struct is provided so your layer handler has access to any needed swapchain information.
/// </summary>
/// <remarks>
/// To add extensions when constructing the <see cref="XrSwapchainCreateInfo"/> struct, initialize
/// the <c>Next</c> pointer with <see cref="PXR_LayerUtility.GetExtensionsChain(CompositionLayerManager.LayerInfo, Unity.XR.CompositionLayers.CompositionLayerExtension.ExtensionTarget)"/>.
///
/// If your struct needs any XrSpace relative info you can use <see cref="PXR_LayerUtility.GetCurrentAppSpace"/>
/// to get the current app space.
/// </remarks>
/// <param name="layerInfo"> Container for the instance id and CompositionLayer component of the composition layer
/// that was just created.</param>
/// <param name="swapchainOutput"> Information regarding the swapchain that was created for this layer,
/// such as the associated swapchain handle.</param>
/// <param name="nativeLayer"> An object of type T that is created and initialized by the concrete implementation of this method.</returns>
/// <returns> A bool indicating success or failure.</returns>
protected abstract bool CreateNativeLayer(CompositionLayerManager.LayerInfo layerInfo, SwapchainCreatedOutput swapchainOutput, out T nativeLayer);
/// <summary>
/// Override this method to modify a native composition layer struct in response to changes on the associated
/// <see cref="Unity.XR.CompositionLayers.Layers.LayerData"/> object or any extension components on the
/// <see cref="Unity.XR.CompositionLayers.CompositionLayer"/> GameObject.
/// </summary>
/// <remarks>
/// You must reinitialize the Next pointer with <see cref="PXR_LayerUtility.GetExtensionsChain(CompositionLayerManager.LayerInfo, Unity.XR.CompositionLayers.CompositionLayerExtension.ExtensionTarget)"/>
/// to get any potential updates from extension components.
/// </remarks>
/// <param name="layerInfo"> Container for the instance id and CompositionLayer component of the composition
/// layer that was modified.</param>
/// <param name="nativeLayer"> A reference to the native PXR_ structure of the composition layer that was modified.
/// The concrete implementation of this method should update the values of the structure as appropriate.</param>
/// <returns> A bool indicating success or failure.</returns>
protected abstract bool ModifyNativeLayer(CompositionLayerManager.LayerInfo layerInfo, ref T nativeLayer);
/// <summary>
/// Mapping of instance ids and native layer structs to help determine what layers are currently set to be active.
/// </summary>
protected Dictionary<int, T> m_nativeLayers = new Dictionary<int, T>();
/// <summary>
/// Thread safe queue used to dispatch callbacks that may come from other threads such as the swapchain creation
/// on the graphics thread.
/// </summary>
protected ConcurrentQueue<Action> actionsForMainThread = new ConcurrentQueue<Action>();
Dictionary<int, LayerRenderInfo> m_renderInfos = new Dictionary<int, LayerRenderInfo>();
Dictionary<int, CompositionLayerManager.LayerInfo> m_layerInfos = new Dictionary<int, CompositionLayerManager.LayerInfo>();
NativeArray<T> m_ActiveNativeLayers;
NativeArray<int> m_ActiveNativeLayerOrders;
int m_ActiveNativeLayerCount;
/// <summary>
/// Implements the <see cref="PXR_LayerProvider.ILayerHandler"/> method that is called by the
/// <see cref="PXR_LayerProvider"/> during the Unity update loop.
/// </summary>
/// <remarks>
/// This implementation carries out two tasks. It dequeues actions for the main thread like dispatch when
/// the swapchain has been
/// created and it adds all the active layers to the <c>endFrameInfo</c> struct in the native UnityPXR_ lib.
/// </remarks>
public virtual void OnUpdate()
{
while (actionsForMainThread.Count > 0)
{
if (actionsForMainThread.TryDequeue(out Action action))
action();
}
unsafe
{
if (m_ActiveNativeLayerCount > 0)
PXR_LayerUtility.AddActiveLayersToEndFrame(m_ActiveNativeLayers.GetUnsafePtr(), m_ActiveNativeLayerOrders.GetUnsafePtr(), m_ActiveNativeLayerCount, UnsafeUtility.SizeOf<T>());
}
m_ActiveNativeLayerCount = 0;
}
/// <summary>
/// Implements the <see cref="PXR_LayerProvider.ILayerHandler"/> method that is called by the
/// <see cref="PXR_LayerProvider"/> when a new layer has been created.
/// This implementation triggers the creation of a swapchain before the actual native layer struct is created.
/// </summary>
/// <param name="layerInfo"> Container for the instance id and CompositionLayer component of the composition layer
/// being created.</param>
public void CreateLayer(CompositionLayerManager.LayerInfo layerInfo)
{
CreateSwapchainAsync(layerInfo);
}
/// <summary>
/// Implements the <see cref="PXR_LayerProvider.ILayerHandler"/> method that is called by the
/// <see cref="PXR_LayerProvider"/> when a layer or attached extension has been modified.
/// This implementation asks the subclass for any changes that must be made to the layer via
/// <see cref="ModifyNativeLayer(CompositionLayerManager.LayerInfo, ref T)"/>
/// by sending a reference to the native layer struct.
/// </summary>
/// <param name="layerInfo"> Container for the instance id and CompositionLayer component of the composition layer
/// that was modified.</param>
public virtual void ModifyLayer(CompositionLayerManager.LayerInfo layerInfo)
{
var texturesExtension = layerInfo.Layer.GetComponent<TexturesExtension>();
if (!m_nativeLayers.TryGetValue(layerInfo.Id, out var nativeLayer))
{
if (texturesExtension != null && texturesExtension.TextureAdded)
{
texturesExtension.TextureAdded = false;
CreateLayer(layerInfo);
}
return;
}
var success = ModifyNativeLayer(layerInfo, ref nativeLayer);
if (success)
m_nativeLayers[layerInfo.Id] = nativeLayer;
}
/// <summary>
/// Implements the <see cref="PXR_LayerProvider.ILayerHandler"/> method that is called by the
/// <see cref="PXR_LayerProvider"/> when a layer is destroyed or disabled.
/// </summary>
/// <param name="removedLayerId"> The instance id of the CompositionLayer component that was removed.</param>
public virtual void RemoveLayer(int removedLayerId)
{
PXR_LayerUtility.ReleaseSwapchain(removedLayerId);
m_nativeLayers.Remove(removedLayerId);
m_layerInfos.Remove(removedLayerId);
m_renderInfos.Remove(removedLayerId);
}
/// <summary>
/// Implements the <see cref="PXR_LayerProvider.ILayerHandler"/> method that is called by the
/// <see cref="PXR_LayerProvider"/> when a layer is considered to be currently active.
/// </summary>
/// <param name="layerInfo"> Container for the instance id and CompositionLayer component of the composition layer
/// being set to active.</param>
public virtual void SetActiveLayer(CompositionLayerManager.LayerInfo layerInfo)
{
if (!m_nativeLayers.TryGetValue(layerInfo.Id, out var nativeLayer))
return;
var success = ActiveNativeLayer(layerInfo, ref nativeLayer);
if (!success)
return;
m_nativeLayers[layerInfo.Id] = nativeLayer;
ResizeNativeArrays();
m_ActiveNativeLayers[m_ActiveNativeLayerCount] = m_nativeLayers[layerInfo.Id];
m_ActiveNativeLayerOrders[m_ActiveNativeLayerCount] = layerInfo.Layer.Order;
++m_ActiveNativeLayerCount;
}
/// <summary>
/// Implements method from <see cref="IDisposable"/> that is called by the <see cref="PXR_LayerProvider"/>
/// when this custom layer handler instance is disposed.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Clears all maps and disposes any created native arrays.
/// </summary>
/// <param name="disposing">Determines if this method was called from the Dispose() method or the finalizer.</param>
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
m_layerInfos.Clear();
m_nativeLayers.Clear();
m_renderInfos.Clear();
}
if (m_ActiveNativeLayers.IsCreated)
m_ActiveNativeLayers.Dispose();
if (m_ActiveNativeLayerOrders.IsCreated)
m_ActiveNativeLayerOrders.Dispose();
}
/// <summary>
/// Calls <see cref="CreateSwapchain(CompositionLayerManager.LayerInfo, out SwapchainCreateInfo)"/> to create a
/// <see cref="SwapchainCreateInfo"/> struct that is then passed to the
/// UnityPXR_ lib to actually create the swapchain on the graphics thread.
/// The static <see cref="OnCreatedSwapchainCallback(int, ulong)"/> method is passed as a callback and invoked when
/// the swapchain has been created.
/// </summary>
/// <param name="layerInfo"> Container for the instance id and CompositionLayer component of the composition layer
/// that was just created.</param>
protected virtual void CreateSwapchainAsync(CompositionLayerManager.LayerInfo layerInfo)
{
m_layerInfos[layerInfo.Id] = layerInfo;
var success = CreateSwapchain(layerInfo, out var swapChainInfo);
if (!success)
return;
if (swapChainInfo.isStereo)
PXR_LayerUtility.CreateStereoSwapchain(layerInfo.Id, swapChainInfo.nativeStruct, OnCreatedStereoSwapchainCallback);
else
PXR_LayerUtility.CreateSwapchain(layerInfo.Id, swapChainInfo.nativeStruct, swapChainInfo.isExternalSurface, OnCreatedSwapchainCallback);
}
/// <summary>
/// This method is dispatched to the main thread inside <see cref="OnCreatedSwapchainCallback(int, ulong)"/>
/// and asks this subclass to create the native layer struct by invoking
/// <see cref="CreateNativeLayer(CompositionLayerManager.LayerInfo, SwapchainCreatedOutput, out T)"/>.
/// </summary>
/// <param name="layerInfo"> Container for the instance id and CompositionLayer component of the composition layer
/// that was just created.</param>
/// <param name="swapchainOutput"> Information regarding the swapchain that was created for this layer, such as
/// the associated swapchain handle.</param>
protected virtual void OnCreatedSwapchain(CompositionLayerManager.LayerInfo layerInfo, SwapchainCreatedOutput swapchainOutput)
{
var success = CreateNativeLayer(layerInfo, swapchainOutput, out var nativeLayer);
if (success){
m_nativeLayers[layerInfo.Id] = nativeLayer;
}
}
/// <summary>
/// Ensures that the native arrays are of the same size as the m_nativeLayers map.
/// </summary>
protected virtual void ResizeNativeArrays()
{
if (!m_ActiveNativeLayers.IsCreated && !m_ActiveNativeLayerOrders.IsCreated)
{
m_ActiveNativeLayers = new NativeArray<T>(m_nativeLayers.Count, Allocator.Persistent);
m_ActiveNativeLayerOrders = new NativeArray<int>(m_nativeLayers.Count, Allocator.Persistent);
return;
}
UnityEngine.Assertions.Assert.AreEqual(m_ActiveNativeLayers.Length, m_ActiveNativeLayerOrders.Length);
if (m_ActiveNativeLayers.Length < m_nativeLayers.Count)
{
var newLayerArray = new NativeArray<T>(m_nativeLayers.Count, Allocator.Persistent);
NativeArray<T>.Copy(m_ActiveNativeLayers, newLayerArray, m_ActiveNativeLayers.Length);
m_ActiveNativeLayers.Dispose();
m_ActiveNativeLayers = newLayerArray;
var newOrderArray = new NativeArray<int>(m_nativeLayers.Count, Allocator.Persistent);
NativeArray<int>.Copy(m_ActiveNativeLayerOrders, newOrderArray, m_ActiveNativeLayerOrders.Length);
m_ActiveNativeLayerOrders.Dispose();
m_ActiveNativeLayerOrders = newOrderArray;
}
}
/// <summary>
/// Override this method to modify a native composition layer struct in response to when it is active.
/// An active compositon layer will invoke this every frame.
/// </summary>
/// <param name="layerInfo"> Container for the instance id and CompositionLayer component of the composition
/// layer that is active.</param>
/// <param name="nativeLayer"> A reference to the native PXR_ structure of the composition layer that is active.</param>
/// <returns>Bool indicating success or failure. A failure case will result in the native composition layer struct not being added into the final XrFrameEndInfo struct.</returns>
protected virtual bool ActiveNativeLayer(CompositionLayerManager.LayerInfo layerInfo, ref T nativeLayer)
{
var texturesExtension = layerInfo.Layer.GetComponent<TexturesExtension>();
if (texturesExtension == null || texturesExtension.LeftTexture == null || texturesExtension.sourceTexture == TexturesExtension.SourceTextureEnum.AndroidSurface)
return true;
if (m_renderInfos.TryGetValue(layerInfo.Id, out var container))
{
PXR_LayerUtility.WriteToRenderTexture(layerInfo.Id, container.Texture, container.RenderTexture, nativeLayer.GetType() == typeof(XrCompositionLayerCubeKHR));
bool isNewTexture = container.Texture != texturesExtension.LeftTexture;
if (isNewTexture)
{
// If we have a new texture with different dimensions then we need to release the current swapchain and create another.
// This is an async procedure that also creates a new native layer object.
if (container.Texture.width != texturesExtension.LeftTexture.width || container.Texture.height != texturesExtension.LeftTexture.height)
{
RemoveLayer(layerInfo.Id);
CreateSwapchainAsync(layerInfo);
return false;
}
else
container.Texture = texturesExtension.LeftTexture;
#if UNITY_VIDEO
container.videoPlayer = layerInfo.Layer.GetComponent<VideoPlayer>();
#endif
container.meshCollider = layerInfo.Layer.GetComponent<MeshCollider>();
}
bool isVideo = false;
#if UNITY_VIDEO
isVideo = container.videoPlayer != null && container.videoPlayer.enabled;
#endif
bool isUI = container.meshCollider != null && container.meshCollider.enabled;
#if UNITY_EDITOR
// Layers with a video or ui component in editor may have multiple native render textures associated with the layer id so we must find them.
if (isVideo || isUI){
PXR_LayerUtility.FindAndWriteToRenderTexture(layerInfo, container.Texture, out container.RenderTexture, nativeLayer.GetType() == typeof(XrCompositionLayerCubeKHR));
}
else if (isNewTexture){
PXR_LayerUtility.WriteToRenderTexture(layerInfo.Id, container.Texture, container.RenderTexture, nativeLayer.GetType() == typeof(XrCompositionLayerCubeKHR));
}
#else
// We only need to write continuously to the native render texture if our texture is changing.
if (isVideo || isUI || isNewTexture)
PXR_LayerUtility.WriteToRenderTexture(layerInfo.Id, container.Texture, container.RenderTexture, nativeLayer.GetType() == typeof(XrCompositionLayerCubeKHR));
#endif
}
else
{
bool isRenderTextureWritten = PXR_LayerUtility.FindAndWriteToRenderTexture(layerInfo, texturesExtension.LeftTexture, out Texture renderTexture, nativeLayer.GetType() == typeof(XrCompositionLayerCubeKHR));
if (isRenderTextureWritten)
{
var layerRenderInfo = new LayerRenderInfo()
{ Texture = texturesExtension.LeftTexture, RenderTexture = renderTexture,
#if UNITY_VIDEO
videoPlayer = layerInfo.Layer.GetComponent<VideoPlayer>(),
#endif
meshCollider = layerInfo.Layer.GetComponent<MeshCollider>() };
m_renderInfos.Add(layerInfo.Id, layerRenderInfo);
};
}
return true;
}
[AOT.MonoPInvokeCallback(typeof(PXR_LayerUtility.SwapchainCallbackDelegate))]
static void OnCreatedSwapchainCallback(int layerId, ulong swapchainHandle)
{
if (Instance == null)
return;
Instance.actionsForMainThread.Enqueue(() => { Instance.OnCreatedSwapchain(Instance.m_layerInfos[layerId], new SwapchainCreatedOutput { handle = swapchainHandle });});
}
[AOT.MonoPInvokeCallback(typeof(PXR_LayerUtility.StereoSwapchainCallbackDelegate))]
static void OnCreatedStereoSwapchainCallback(int layerId, ulong swapchainHandleLeft, ulong swapchainHandleRight)
{
if (Instance == null)
return;
Instance.actionsForMainThread.Enqueue(() => { Instance.OnCreatedSwapchain(Instance.m_layerInfos[layerId], new SwapchainCreatedOutput { handle = swapchainHandleLeft, secondStereoHandle = swapchainHandleRight}); });
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c975401c3bdf0db46afd0591e8ce6a27
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,314 @@
#if XR_COMPOSITION_LAYERS
using Unity.XR.CompositionLayers;
using Unity.XR.CompositionLayers.Extensions;
using Unity.XR.CompositionLayers.Layers;
using Unity.XR.CompositionLayers.Services;
using UnityEngine;
namespace Unity.XR.PXR
{
internal class PXR_CylinderLayer : PXR_CustomLayerHandler<XrCompositionLayerCylinderKHR>
{
float savedDelta;
bool layerDataChanged = false;
struct CylinderLayerSize
{
public float radius;
public float centralAngle;
public float aspectRatio;
public static implicit operator CylinderLayerSize(Vector3 v) => new CylinderLayerSize
{
radius = v.x,
centralAngle = v.y,
aspectRatio = v.z
};
}
protected override bool CreateSwapchain(CompositionLayerManager.LayerInfo layer, out SwapchainCreateInfo swapchainCreateInfo)
{
if (layer.Layer == null)
{
swapchainCreateInfo = default;
return false;
}
unsafe
{
var texturesExtension = layer.Layer.GetComponent<TexturesExtension>();
if (texturesExtension == null || texturesExtension.enabled == false)
{
swapchainCreateInfo = default;
return false;
}
switch (texturesExtension.sourceTexture)
{
case TexturesExtension.SourceTextureEnum.LocalTexture:
{
if (texturesExtension.LeftTexture == null)
goto default;
var xrCreateInfo = new XrSwapchainCreateInfo()
{
Type = (uint)XrStructureType.XR_TYPE_SWAPCHAIN_CREATE_INFO,
Next = PXR_LayerUtility.GetExtensionsChain(layer, CompositionLayerExtension.ExtensionTarget.Swapchain),
CreateFlags = 0,
UsageFlags = (ulong)(XrSwapchainUsageFlags.XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XrSwapchainUsageFlags.XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT),
Format = PXR_LayerUtility.GetDefaultColorFormat(),
SampleCount = 1,
Width = (uint)texturesExtension.LeftTexture.width,
Height = (uint)texturesExtension.LeftTexture.height,
FaceCount = 1,
ArraySize = 1,
MipCount = (uint)texturesExtension.LeftTexture.mipmapCount,
};
swapchainCreateInfo = new SwapchainCreateInfo(xrCreateInfo, isExternalSurface: false);
return true;
}
case TexturesExtension.SourceTextureEnum.AndroidSurface:
{
#if UNITY_ANDROID
var xrCreateInfo = new XrSwapchainCreateInfo()
{
Type = (uint)XrStructureType.XR_TYPE_SWAPCHAIN_CREATE_INFO,
Next = PXR_LayerUtility.GetExtensionsChain(layer, CompositionLayerExtension.ExtensionTarget.Swapchain),
CreateFlags = 0,
UsageFlags = (ulong)(XrSwapchainUsageFlags.XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XrSwapchainUsageFlags.XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT),
Format = 0,
SampleCount = 0,
Width = (uint)texturesExtension.Resolution.x,
Height = (uint)texturesExtension.Resolution.y,
FaceCount = 0,
ArraySize = 0,
MipCount = 0,
};
swapchainCreateInfo = new SwapchainCreateInfo(xrCreateInfo, isExternalSurface: true);
return true;
#else
goto default;
#endif
}
default:
swapchainCreateInfo = default;
return false;
}
}
}
protected override bool CreateNativeLayer(CompositionLayerManager.LayerInfo layer, SwapchainCreatedOutput swapchainOutput, out XrCompositionLayerCylinderKHR nativeLayer)
{
unsafe
{
var data = layer.Layer.LayerData as CylinderLayerData;
var transform = layer.Layer.GetComponent<Transform>();
var texturesExtension = layer.Layer.GetComponent<TexturesExtension>();
int subImageWidth = 0;
int subImageHeight = 0;
switch (texturesExtension.sourceTexture)
{
case TexturesExtension.SourceTextureEnum.LocalTexture:
{
if (texturesExtension.LeftTexture != null)
{
subImageWidth = texturesExtension.LeftTexture.width;
subImageHeight = texturesExtension.LeftTexture.height;
}
break;
}
case TexturesExtension.SourceTextureEnum.AndroidSurface:
{
subImageWidth = (int)texturesExtension.Resolution.x;
subImageHeight = (int)texturesExtension.Resolution.y;
break;
}
}
CylinderLayerSize scaledSize = data.GetScaledSize(transform.lossyScale);
if (texturesExtension.CropToAspect)
{
scaledSize = FixAspectRatio(data, scaledSize, subImageWidth, subImageHeight);
}
nativeLayer = new XrCompositionLayerCylinderKHR()
{
Type = (uint)XrStructureType.XR_TYPE_COMPOSITION_LAYER_CYLINDER_KHR,
Next = PXR_LayerUtility.GetExtensionsChain(layer, CompositionLayerExtension.ExtensionTarget.Layer),
LayerFlags = data.BlendType == BlendType.Premultiply ? XrCompositionLayerFlags.SourceAlpha : XrCompositionLayerFlags.SourceAlpha | XrCompositionLayerFlags.UnPremultipliedAlpha,
Space = PXR_LayerUtility.GetCurrentAppSpace(),
EyeVisibility = 0,
SubImage = new XrSwapchainSubImage()
{
Swapchain = swapchainOutput.handle,
ImageRect = new XrRect2Di()
{
Offset = new XrOffset2Di() { X = 0, Y = 0 },
Extent = new XrExtent2Di()
{
Width = subImageWidth,
Height = subImageHeight
}
},
ImageArrayIndex = 0
},
Pose = new XrPosef(PXR_Utility.ComputePoseToWorldSpace(transform, CompositionLayerManager.mainCameraCache).position, PXR_Utility.ComputePoseToWorldSpace(transform, CompositionLayerManager.mainCameraCache).rotation),
Radius = data.ApplyTransformScale ? scaledSize.radius : data.Radius,
CentralAngle = data.ApplyTransformScale ? scaledSize.centralAngle : data.CentralAngle,
AspectRatio = data.ApplyTransformScale ? scaledSize.aspectRatio : data.AspectRatio,
};
layerDataChanged = true;
return true;
}
}
protected override bool ModifyNativeLayer(CompositionLayerManager.LayerInfo layerInfo, ref XrCompositionLayerCylinderKHR nativeLayer)
{
var texturesExtension = layerInfo.Layer.GetComponent<TexturesExtension>();
if (texturesExtension == null || texturesExtension.enabled == false)
return false;
var data = layerInfo.Layer.LayerData as CylinderLayerData;
GetSubImageDimensions(out int subImageWidth, out int subImageHeight, texturesExtension);
nativeLayer.SubImage.ImageRect.Extent = new XrExtent2Di()
{
Width = subImageWidth,
Height = subImageHeight
};
var transform = layerInfo.Layer.GetComponent<Transform>();
CylinderLayerSize scaledSize = data.GetScaledSize(transform.lossyScale);
if (texturesExtension.CropToAspect)
{
scaledSize = FixAspectRatio(data, scaledSize, subImageWidth, subImageHeight);
}
nativeLayer.Radius = data.ApplyTransformScale ? scaledSize.radius : data.Radius;
nativeLayer.CentralAngle = data.ApplyTransformScale ? scaledSize.centralAngle : data.CentralAngle;
nativeLayer.AspectRatio = data.ApplyTransformScale ? scaledSize.aspectRatio : data.AspectRatio;
unsafe
{
nativeLayer.Next = PXR_LayerUtility.GetExtensionsChain(layerInfo, CompositionLayerExtension.ExtensionTarget.Layer);
}
layerDataChanged = true;
return true;
}
protected override bool ActiveNativeLayer(CompositionLayerManager.LayerInfo layerInfo, ref XrCompositionLayerCylinderKHR nativeLayer)
{
var texturesExtension = layerInfo.Layer.GetComponent<TexturesExtension>();
if (texturesExtension == null || texturesExtension.enabled == false)
return false;
var transform = layerInfo.Layer.GetComponent<Transform>();
// Special treatment for cylinder type based on destination rects.
if (texturesExtension != null && texturesExtension.CustomRects)
{
var cylinderLayer = layerInfo.Layer.LayerData as CylinderLayerData;
float rotationDelta = (texturesExtension.LeftEyeDestinationRect.x + (0.5f * texturesExtension.LeftEyeDestinationRect.width) - 0.5f) * cylinderLayer.CentralAngle / (float)System.Math.PI * 180.0f;
if (rotationDelta != savedDelta)
{
Quaternion savedDeltaQuaternion = Quaternion.AngleAxis(savedDelta, Vector3.up);
Quaternion deltaQuaternion = Quaternion.AngleAxis(rotationDelta, Vector3.up);
Quaternion difference = deltaQuaternion * Quaternion.Inverse(savedDeltaQuaternion);
savedDelta = rotationDelta;
transform.rotation *= difference;
}
}
nativeLayer.Pose = new XrPosef(PXR_Utility.ComputePoseToWorldSpace(transform, CompositionLayerManager.mainCameraCache).position, PXR_Utility.ComputePoseToWorldSpace(transform, CompositionLayerManager.mainCameraCache).rotation);
nativeLayer.Space = PXR_LayerUtility.GetCurrentAppSpace();
if (texturesExtension.CustomRects && layerDataChanged)
{
GetSubImageDimensions(out int subImageWidth, out int subImageHeight, texturesExtension);
nativeLayer.SubImage.ImageRect = new XrRect2Di()
{
Offset = new XrOffset2Di()
{
X = (int)(subImageWidth * texturesExtension.LeftEyeSourceRect.x),
Y = (int)(subImageHeight * texturesExtension.LeftEyeSourceRect.y)
},
Extent = new XrExtent2Di()
{
Width = (int)(subImageWidth * texturesExtension.LeftEyeSourceRect.width),
Height = (int)(subImageHeight * texturesExtension.LeftEyeSourceRect.height)
}
};
var currentPosition = PXR_Utility.ComputePoseToWorldSpace(transform, CompositionLayerManager.mainCameraCache).position;
float cylinderHeight = nativeLayer.Radius * nativeLayer.CentralAngle / nativeLayer.AspectRatio;
float transformedY = currentPosition.y + (((texturesExtension.LeftEyeDestinationRect.y + (0.5f * texturesExtension.LeftEyeDestinationRect.height) - 0.5f)) * (-1.0f * cylinderHeight));
nativeLayer.Pose = new XrPosef(new Vector3(currentPosition.x, transformedY, currentPosition.z), PXR_Utility.ComputePoseToWorldSpace(transform, CompositionLayerManager.mainCameraCache).rotation);
nativeLayer.CentralAngle = nativeLayer.CentralAngle * texturesExtension.LeftEyeDestinationRect.width;
nativeLayer.AspectRatio = nativeLayer.AspectRatio * texturesExtension.LeftEyeDestinationRect.width / texturesExtension.LeftEyeDestinationRect.height;
layerDataChanged = false;
}
return base.ActiveNativeLayer(layerInfo, ref nativeLayer);
}
static CylinderLayerSize FixAspectRatio(CylinderLayerData data, CylinderLayerSize scaledSize, int texWidth, int texHeight)
{
// because we're cropping and trying to maintain the same other parameters, we don't
// need to consider data.MaintainAspectRatio here. That's mostly an editor concern, anyway.
float texRatio = (float)texWidth / (float)texHeight;
if (scaledSize.aspectRatio > texRatio)
{
// too wide
float width = scaledSize.radius * scaledSize.centralAngle;
float height = width / scaledSize.aspectRatio;
scaledSize.centralAngle = height * texRatio / scaledSize.radius;
scaledSize.aspectRatio = texRatio;
}
else if (scaledSize.aspectRatio < texRatio)
{
// too narrow
scaledSize.aspectRatio = texRatio;
}
return scaledSize;
}
static void GetSubImageDimensions(out int width, out int height, TexturesExtension texturesExtension)
{
width = 0;
height = 0;
switch (texturesExtension.sourceTexture)
{
case TexturesExtension.SourceTextureEnum.LocalTexture:
{
if (texturesExtension.LeftTexture != null)
{
width = texturesExtension.LeftTexture.width;
height = texturesExtension.LeftTexture.height;
}
break;
}
case TexturesExtension.SourceTextureEnum.AndroidSurface:
{
width = (int)texturesExtension.Resolution.x;
height = (int)texturesExtension.Resolution.y;
break;
}
}
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 47eec2213f606e6469915b08b46fe725
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,40 @@
#if XR_COMPOSITION_LAYERS
using Unity.XR.CompositionLayers;
using Unity.XR.CompositionLayers.Layers;
using Unity.XR.CompositionLayers.Services;
namespace Unity.XR.PXR
{
internal class PXR_DefaultLayer : PXR_LayerProvider.ILayerHandler
{
unsafe void SetDefaultLayerAttributes(CompositionLayerManager.LayerInfo layerInfo)
{
var extensions = PXR_LayerUtility.GetExtensionsChain(layerInfo, CompositionLayerExtension.ExtensionTarget.Layer);
PXR_LayerUtility.SetDefaultSceneLayerExtensions(extensions);
var flags = layerInfo.Layer.LayerData.BlendType == BlendType.Premultiply ? XrCompositionLayerFlags.SourceAlpha : XrCompositionLayerFlags.SourceAlpha | XrCompositionLayerFlags.UnPremultipliedAlpha;
PXR_LayerUtility.SetDefaultLayerFlags(flags);
}
public void CreateLayer(CompositionLayerManager.LayerInfo layerInfo) => SetDefaultLayerAttributes(layerInfo);
public void ModifyLayer(CompositionLayerManager.LayerInfo layerInfo) => SetDefaultLayerAttributes(layerInfo);
public void OnUpdate()
{
return;
}
public void RemoveLayer(int id)
{
return;
}
public void SetActiveLayer(CompositionLayerManager.LayerInfo layerInfo)
{
return;
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0074c6f14de152e4ca8c07c4bb7601b0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,120 @@
#if XR_COMPOSITION_LAYERS
using System;
using Unity.XR.CompositionLayers;
using Unity.XR.CompositionLayers.Extensions;
using Unity.XR.CompositionLayers.Layers;
using Unity.XR.CompositionLayers.Services;
using UnityEngine;
namespace Unity.XR.PXR
{
internal class PXR_Equirect2Layer : PXR_CustomLayerHandler<XrCompositionLayerEquirect2KHR>
{
protected override unsafe bool CreateSwapchain(CompositionLayerManager.LayerInfo layerInfo, out SwapchainCreateInfo swapchainCreateInfo)
{
TexturesExtension texturesExtension = layerInfo.Layer.GetComponent<TexturesExtension>();
if (texturesExtension == null || texturesExtension.enabled == false || texturesExtension.LeftTexture == null)
{
swapchainCreateInfo = default;
return false;
}
swapchainCreateInfo = new XrSwapchainCreateInfo()
{
Type = (uint)XrStructureType.XR_TYPE_SWAPCHAIN_CREATE_INFO,
Next = PXR_LayerUtility.GetExtensionsChain(layerInfo, CompositionLayerExtension.ExtensionTarget.Swapchain),
CreateFlags = 0,
UsageFlags = (ulong)(XrSwapchainUsageFlags.XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XrSwapchainUsageFlags.XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT),
Format = PXR_LayerUtility.GetDefaultColorFormat(),
SampleCount = 1,
Width = (uint)(texturesExtension.LeftTexture.width),
Height = (uint)(texturesExtension.LeftTexture.height),
FaceCount = 1,
ArraySize = 1,
MipCount = (uint)texturesExtension.LeftTexture.mipmapCount,
};
return true;
}
protected override unsafe bool CreateNativeLayer(CompositionLayerManager.LayerInfo layerInfo, SwapchainCreatedOutput swapchainOutput, out XrCompositionLayerEquirect2KHR nativeLayer)
{
TexturesExtension texturesExtension = layerInfo.Layer.GetComponent<TexturesExtension>();
if (texturesExtension == null || texturesExtension.enabled == false || texturesExtension.LeftTexture == null)
{
nativeLayer = default;
return false;
}
var transform = layerInfo.Layer.GetComponent<Transform>();
var data = layerInfo.Layer.LayerData as EquirectMeshLayerData;
Vector2 scaleCalculated = CalculateScale(data.CentralHorizontalAngle, data.UpperVerticalAngle, data.LowerVerticalAngle);
nativeLayer = new XrCompositionLayerEquirect2KHR()
{
Type = (uint)XrStructureType.XR_TYPE_COMPOSITION_LAYER_EQUIRECT2_KHR,
Next = PXR_LayerUtility.GetExtensionsChain(layerInfo, CompositionLayerExtension.ExtensionTarget.Layer),
LayerFlags = data.BlendType == BlendType.Premultiply ? XrCompositionLayerFlags.SourceAlpha : XrCompositionLayerFlags.SourceAlpha | XrCompositionLayerFlags.UnPremultipliedAlpha,
Space = PXR_LayerUtility.GetCurrentAppSpace(),
EyeVisibility = 0,
SubImage = new XrSwapchainSubImage()
{
Swapchain = swapchainOutput.handle,
ImageRect = new XrRect2Di()
{
Offset = new XrOffset2Di() { X = 0, Y = 0 },
Extent = new XrExtent2Di()
{
Width = texturesExtension.LeftTexture.width,
Height = texturesExtension.LeftTexture.height
}
},
ImageArrayIndex = 0
},
Pose = new XrPosef(PXR_Utility.ComputePoseToWorldSpace(transform, CompositionLayerManager.mainCameraCache).position, PXR_Utility.ComputePoseToWorldSpace(transform, CompositionLayerManager.mainCameraCache).rotation),
Radius = data.Radius,
CentralHorizontalAngle = data.CentralHorizontalAngle,
UpperVerticalAngle = data.UpperVerticalAngle,
LowerVerticalAngle = -data.LowerVerticalAngle
};
return true;
}
protected override unsafe bool ModifyNativeLayer(CompositionLayerManager.LayerInfo layerInfo, ref XrCompositionLayerEquirect2KHR nativeLayer)
{
TexturesExtension texturesExtension = layerInfo.Layer.GetComponent<TexturesExtension>();
if (texturesExtension == null || texturesExtension.enabled == false || texturesExtension.LeftTexture == null)
return false;
var transform = layerInfo.Layer.GetComponent<Transform>();
var data = layerInfo.Layer.LayerData as EquirectMeshLayerData;
Vector2 scaleCalculated = CalculateScale(data.CentralHorizontalAngle, data.UpperVerticalAngle, data.LowerVerticalAngle);
nativeLayer.SubImage.ImageRect.Extent = new XrExtent2Di()
{
Width = texturesExtension.LeftTexture.width,
Height = texturesExtension.LeftTexture.height
};
nativeLayer.Pose = new XrPosef(PXR_Utility.ComputePoseToWorldSpace(transform, CompositionLayerManager.mainCameraCache).position, PXR_Utility.ComputePoseToWorldSpace(transform, CompositionLayerManager.mainCameraCache).rotation);
nativeLayer.Radius = data.Radius;
nativeLayer.CentralHorizontalAngle = data.CentralHorizontalAngle;
nativeLayer.UpperVerticalAngle = data.UpperVerticalAngle;
nativeLayer.LowerVerticalAngle = -data.LowerVerticalAngle;
nativeLayer.Next = PXR_LayerUtility.GetExtensionsChain(layerInfo, CompositionLayerExtension.ExtensionTarget.Layer);
return true;
}
protected override bool ActiveNativeLayer(CompositionLayerManager.LayerInfo layerInfo, ref XrCompositionLayerEquirect2KHR nativeLayer)
{
nativeLayer.Space = PXR_LayerUtility.GetCurrentAppSpace();
return base.ActiveNativeLayer(layerInfo, ref nativeLayer);
}
Vector2 CalculateScale(float centralHorizontalAngle, float upperVerticalAngle, float lowerVerticalAngle)
{
return new Vector2((2.0f * (float)Math.PI) / centralHorizontalAngle, (float)Math.PI / (upperVerticalAngle - lowerVerticalAngle));
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d2e2d51ed59c3174c8104de10442fb25
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,300 @@
#if XR_COMPOSITION_LAYERS
using System;
using System.Collections.Generic;
using System.Reflection.Emit;
using Unity.Profiling;
using Unity.XR.CompositionLayers.Extensions;
using Unity.XR.CompositionLayers.Layers;
using Unity.XR.CompositionLayers.Provider;
using Unity.XR.CompositionLayers.Services;
using UnityEngine;
namespace Unity.XR.PXR
{
/// <summary>
/// Manages communication of changes between an application and the UnityPXR_ lib for all
/// <see cref="Unity.XR.CompositionLayers.Layers.LayerData"/> objects.
/// </summary>
/// <remarks>
/// PXR_ providers or extensions that create custom composition layer types or that override how the built-in
/// layer types are handled, must implement the <see cref="ILayerProvider"/> interface and register instances of
/// these implementations with the <c>PXR_LayerProvider</c> via <see cref="RegisterLayerHandler(Type, PXR_LayerProvider.ILayerHandler)"/>.
/// </remarks>
public class PXR_LayerProvider : ILayerProvider, IDisposable
{
/// <summary>
/// An interface used by the <see cref="PXR_LayerProvider"/> to communicate layer data changes to
/// registered layer handlers.
/// </summary>
/// <remarks>
/// <c>ILayerHandler</c> instances must register themselves via
/// <see cref="PXR_LayerProvider.RegisterLayerHandler(Type, PXR_LayerProvider.ILayerHandler)"/>
/// to specify the <see cref="LayerData"/> type to handle.
/// If more than one object registers itself as a handler for a specific <see cref="LayerData"/>
/// type, the last registered handler is used.
///
/// The <see cref="PXR_CustomLayerHandler{T}"/> class provides a partial, base implementation of this interface that you can
/// use to create custom layer handlers.
/// </remarks>
public interface ILayerHandler
{
/// <summary>
/// Called by the <see cref="PXR_LayerProvider"/> during the Unity Update loop.
/// All implementations must call <see cref="PXR_LayerUtility.AddActiveLayersToEndFrame(void*,void*,int,int)"/> every frame
/// to add their native layer structs to the <c>endFrameInfo</c> struct inside the UnityPXR_ lib.
/// </summary>
public void OnUpdate();
/// <summary>
/// Called by the <see cref="PXR_LayerProvider"/> when a new <see cref="LayerData"/>
/// object of the type registered to this <c>ILayerHandler</c> instance has been created.
/// </summary>
/// <param name="layerInfo"> Container for the instance id and CompositionLayer component of the composition layer
/// that was just created.</param>
public void CreateLayer(CompositionLayerManager.LayerInfo layerInfo);
/// <summary>
/// Called by the <see cref="PXR_LayerProvider"/> when a <see cref="LayerData"/> object
/// of the type registered to this <c>ILayerHandler</c> instance has been destroyed or disabled.
/// </summary>
/// <param name="removedLayerId"> The instance id of the CompositionLayer component that was removed.</param>
public void RemoveLayer(int id);
/// <summary>
/// Called by the <see cref="PXR_LayerProvider"/> when a <see cref="LayerData"/> object
/// or any attached extension components have had a member modified.
/// </summary>
/// <param name="layerInfo"> Container for the instance id and CompositionLayer component of the composition layer
/// that was modified.</param>
public void ModifyLayer(CompositionLayerManager.LayerInfo layerInfo);
/// <summary>
/// Called every frame by the <see cref="PXR_LayerProvider"/> for all currently active <see cref="LayerData"/> objects
/// of the type registered to this <c>ILayerHandler</c> instance.
/// </summary>
/// <param name="layerInfo"> Container for the instance id and CompositionLayer component of the composition layer
/// being set to active.</param>
public void SetActiveLayer(CompositionLayerManager.LayerInfo layerInfo);
}
/// <summary>
/// Initializes and returns an instance of <c>PXR_LayerProvider</c>.
/// Initializes and registers all the default, built-in layer handlers.
/// </summary>
/// <remarks>
/// The <c>PXR_LayerProvider</c> is created and disposed by the <see cref="Management.XRLoader"/>.
/// You do not need to create an instance of <c>PXR_LayerProvider</c> yourself. Layer handlers
/// should only use the static methods and properties of this class
/// </remarks>
public PXR_LayerProvider() => InitializeAndRegisterBuiltInHandlers();
/// <summary>
/// Calls the methods in its invocation list when the <c>PXR_LayerProvider</c> has started and registered it's built-in layer handlers.
/// </summary>
/// <remarks>
/// You can use this event to wait for the <c>PXR_LayerProvider</c> to finish registering its built-in layer handlers
/// so that you can override them with your own custom layer handlers.
/// </remarks>
public static event Action Started;
/// <summary>
/// Calls the methods in its invocation list when the <c>PXR_LayerProvider</c> has stopped and is disposed.
/// </summary>
public static event Action Stopped;
/// <summary>
/// Reports whether the <c>PXR_LayerProvider</c> has already been created and started.
/// </summary>
public static bool isStarted { get; set; }
static Dictionary<Type, ILayerHandler> LayerHandlers = new Dictionary<Type, ILayerHandler>();
static readonly ProfilerMarker s_PXR_LayerProviderCreate = new ProfilerMarker("PXR_LayerProvider.Create");
static readonly ProfilerMarker s_PXR_LayerProviderRemove = new ProfilerMarker("PXR_LayerProvider.Remove");
static readonly ProfilerMarker s_PXR_LayerProviderModify = new ProfilerMarker("PXR_LayerProvider.Modify");
static readonly ProfilerMarker s_PXR_LayerProviderActive = new ProfilerMarker("PXR_LayerProvider.Active");
static readonly ProfilerMarker s_PXR_LayerProviderUpdate = new ProfilerMarker("PXR_LayerProvider.Update");
/// <summary>
/// Registers a concrete <see cref="ILayerHandler"/> object as the handler for all layers of a specific
/// <see cref="LayerData"/> subclass.
/// </summary>
/// <remarks>
/// If more than one object registers itself as a handler for a specific <see cref="LayerData"/>
/// type, the last registered handler is used.
///
/// The <c>PXR_LayerProvider</c> invokes the registered layer handler's <see cref="ILayerHandler"/> methods
/// when any object of the associated <see cref="LayerData"/> type is updated in some way.
/// </remarks>
/// <param name="layerDataType">The <see cref="LayerData"/> subclass to handle.</param>
/// <param name="handler">The concrete <c>ILayerHandler</c> instance> to register.</param>
public static void RegisterLayerHandler(Type layerDataType, ILayerHandler handler)
{
if (handler == null)
{
LayerHandlers.Remove(layerDataType);
return;
}
LayerHandlers[layerDataType] = handler;
}
/// <summary>
/// Sets the layer provider state on first assignment to the <see cref="CompositionLayerManager" />.
/// </summary>
/// <param name="layers">The list of all currently known <see cref="CompositionLayer"/> instances, regardless of active state.</param>
public void SetInitialState(List<CompositionLayerManager.LayerInfo> layers)
{
UpdateLayers(layers, null, null, null);
}
/// <summary>
/// Called by the <see cref="CompositionLayerManager" /> to tell the instance of <see cref="ILayerProvider" /> about
/// the current state of layers it is managing.
/// </summary>
///
/// <param name="createdLayers">The list of layers that were just created. Any layer in
/// this list may be in the <paramref name="activeLayers" /> list if it is activated in the same frame.
/// Any layer in this list should not be in <paramref name="modifiedLayers" /> or <paramref name="removedLayers" />.
/// This list is ephemeral and cleared after each call.</param>
///
/// <param name="removedLayers">The list of layers that are no longer being managed. Any layer in
/// this list should not be in the <paramref name="createdLayers" />, <paramref name="modifiedLayers" />, or
/// <paramref name="activeLayers" /> lists.
/// This list is ephemeral and cleared after each call.</param>
///
/// <param name="modifiedLayers">The list of layers that have been recently modified. Any layer in
/// this list may also be in the <paramref name="activeLayers" /> list. Any layer in this list should not
/// be in <paramref name="createdLayers" /> or <paramref name="removedLayers" />.
/// This list is ephemeral and cleared after each call.</param>
///
/// <param name="activeLayers">The list of layers currently active within the scene.
/// Layers in this list may also be in the <paramref name="createdLayers" /> or <paramref name="modifiedLayers" /> lists
/// if they became active in the same frame.</param>
public void UpdateLayers(List<CompositionLayerManager.LayerInfo> createdLayers, List<int> removedLayers, List<CompositionLayerManager.LayerInfo> modifiedLayers, List<CompositionLayerManager.LayerInfo> activeLayers)
{
if (removedLayers != null && removedLayers.Count != 0)
{
foreach (var handler in LayerHandlers.Values)
{
foreach (var removed in removedLayers)
{
s_PXR_LayerProviderRemove.Begin();
handler?.RemoveLayer(removed);
s_PXR_LayerProviderRemove.End();
}
}
}
if (createdLayers != null && createdLayers.Count != 0)
{
foreach (var created in createdLayers)
{
if (created.Layer == null)
continue;
var layerDataType = created.Layer.LayerData.GetType();
if (LayerHandlers.TryGetValue(layerDataType, out ILayerHandler handler))
{
s_PXR_LayerProviderCreate.Begin();
handler?.CreateLayer(created);
s_PXR_LayerProviderCreate.End();
}
}
}
if (modifiedLayers != null && modifiedLayers.Count != 0)
{
foreach (var modified in modifiedLayers)
{
if (modified.Layer == null)
continue;
var layerDataType = modified.Layer.LayerData.GetType();
if (LayerHandlers.TryGetValue(layerDataType, out ILayerHandler handler))
{
s_PXR_LayerProviderModify.Begin();
handler?.ModifyLayer(modified);
s_PXR_LayerProviderModify.End();
}
}
}
if (activeLayers != null && activeLayers.Count != 0)
{
foreach (var active in activeLayers)
{
if (active.Layer == null)
continue;
var layerDataType = active.Layer.LayerData.GetType();
if (LayerHandlers.TryGetValue(layerDataType, out ILayerHandler handler))
{
s_PXR_LayerProviderActive.Begin();
handler?.SetActiveLayer(active);
s_PXR_LayerProviderActive.End();
}
}
}
foreach (var handler in LayerHandlers.Values)
{
s_PXR_LayerProviderUpdate.Begin();
handler?.OnUpdate();
s_PXR_LayerProviderUpdate.End();
}
}
/// <summary>
/// Used for cleanup and to call Dispose() on registered layer handlers.
/// </summary>
/// <remarks>This is called by the PXR_Loader class when StopInternal() is invoked.</remarks>
public void Dispose()
{
foreach (var handler in LayerHandlers.Values)
{
if (handler is IDisposable)
{
((IDisposable)handler)?.Dispose();
}
}
LayerHandlers.Clear();
isStarted = false;
Stopped?.Invoke();
}
public void CleanupState()
{
}
public void LateUpdate()
{
}
void InitializeAndRegisterBuiltInHandlers()
{
// var defaultLayerHandler = new PXR_DefaultLayer();
var quadLayerHandler = new PXR_QuadLayer();
//var projectionLayerHandler = new PXR_ProjectionLayer();
var cylinderLayerHandler = new PXR_CylinderLayer();
var cubeLayerHandler = new PXR_CubeLayer() ;
ILayerHandler equirectLayerHandler = new PXR_Equirect2Layer() ;
// RegisterLayerHandler(typeof(DefaultLayerData), defaultLayerHandler);
RegisterLayerHandler(typeof(QuadLayerData), quadLayerHandler);
RegisterLayerHandler(typeof(CylinderLayerData), cylinderLayerHandler);
//RegisterLayerHandler(typeof(ProjectionLayerData), projectionLayerHandler);
//RegisterLayerHandler(typeof(ProjectionLayerRigData), projectionLayerHandler);
RegisterLayerHandler(typeof(CubeProjectionLayerData), cubeLayerHandler);
RegisterLayerHandler(typeof(EquirectMeshLayerData), equirectLayerHandler);
isStarted = true;
Started?.Invoke();
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a8aed7bd439ad8c4196f22bd4fa60d1b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,420 @@
#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
{
/// <summary>
/// A general-purpose helper class for composition layer support.
/// </summary>
public static class PXR_LayerUtility
{
internal unsafe delegate void LayerCallbackDelegate(int layerId, XrCompositionLayerBaseHeader* layer);
private static Material textureM;
private static Material cubeM;
static Dictionary<UInt32, Texture> _textureMap = new Dictionary<UInt32, Texture>();
static Dictionary<ValueTuple<int, int>, Texture> _textureCache = new Dictionary<ValueTuple<int, int>, Texture>();
/// <summary>
/// Calls the methods in its invocation list when a swapchain is created on the graphics thread inside the UnityPXR_ lib.
/// </summary>
/// <param name="layerId">The instance id of the composition layer object.</param>
/// <param name="swapchainHandle">The handle to the native swapchain that was just created.</param>
public unsafe delegate void SwapchainCallbackDelegate(int layerId, ulong swapchainHandle);
/// <summary>
/// Calls the methods in its invocation list when a stereo swapchain is created on the graphics thread inside the UnityPXR_ lib.
/// </summary>
/// <param name="layerId">The instance id of the composition layer object.</param>
/// <param name="swapchainHandleLeft">The handle to one of the stereo swapchains that was just created.</param>
/// <param name="swapchainHandleRight">The handle to one of the stereo swapchains that was just created.</param>
public unsafe delegate void StereoSwapchainCallbackDelegate(int layerId, ulong swapchainHandleLeft, ulong swapchainHandleRight);
/// <summary>
/// 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.
/// </summary>
/// <param name="layerInfo"> Container for the instance id and CompositionLayer component of the composition layer.</param>
/// <param name="extensionTarget"> Represents what part of the composition layer to retrieve extensions for.</param>
/// <returns>A pointer to the head of an array of native extension objects that will be associated with a composition layer.</returns>
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;
}
/// <summary>
/// Helper method used get the current app space for any native composition layer structs that may require an associated XrSpace.
/// </summary>
/// <returns>A handle to the current app space.</returns>
/// <remarks>Normally used when creating native composition layers.</remarks>
//public static ulong GetCurrentAppSpace() => Features.PXR_Feature.Internal_GetAppSpace(out ulong appSpaceId) ? appSpaceId : 0; // TODO
public static ulong GetCurrentAppSpace() => 0;
/// <summary>
/// Helper method used get the XR session handle for any native composition layer structs that may require an associated XrSession.
/// </summary>
/// <returns>A handle to the current xr session.</returns>
//public static ulong GetXRSession() => Features.PXR_Feature.Internal_GetXRSession(out ulong xrSessionHandle) ? xrSessionHandle : 0; // TODO
public static ulong GetXRSession() => 0;
/// <summary>
/// Create the <see cref="XrSwapchainCreateInfo"/> struct that is passed to PXR_ SDK to create a swapchain.
/// </summary>
/// <param name="layerId">The instance id of the composition layer object.</param>
/// <param name="createInfo">The struct used to create the swapchain.</param>
/// <param name="isExternalSurface"> Optional parameter that can be used when an external surface will be used, like when using the Android Surface feature.</param>
/// <param name="callback"> Optional parameter that can be used if your composition layer needs to know the handle after swapchain creation.</param>
public static void CreateSwapchain(int layerId, XrSwapchainCreateInfo createInfo, bool isExternalSurface = false, SwapchainCallbackDelegate callback = null)
{
Pxr_CompositorLayersCreateSwapchain(layerId, createInfo, isExternalSurface, callback);
}
/// <summary>
/// Create the <see cref="XrSwapchainCreateInfo"/> struct that is passed to PXR_ SDK to create a swapchain for stereo projection, like Projection layer type.
/// </summary>
/// <param name="layerId">The instance id of the composition layer object.</param>
/// <param name="createInfo">The struct used to create the swapchain.</param>
/// <param name="callback"> Optional parameter that can be used if your composition layer needs to know the handles after swapchain creation.</param>
public static void CreateStereoSwapchain(int layerId, XrSwapchainCreateInfo createInfo, StereoSwapchainCallbackDelegate callback = null)
{
Pxr_CompositorLayersCreateStereoSwapchain(layerId, createInfo, callback);
}
/// <summary>
/// Release swapchain according to the id provided.
/// </summary>
/// <param name="layerId">The instance id of the composition layer object.</param>
public static void ReleaseSwapchain(int layerId)
{
Pxr_CompositorLayersReleaseSwapchain(layerId);
}
/// <summary>
/// Return swapchain supported color format.
/// </summary>
/// <returns>The color format the swapchains will be using.</returns>
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;
}
}
/// <summary>
/// Finds the render texture of the give texture id.
/// </summary>
/// <param name="texId">The id of the render texture to find.</param>
/// <returns>The render texture with the provided id or null if no render textrue with that id was found.</returns>
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<RenderTexture>();
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;
}
/// <summary>
/// Finds the render texture of the layer id.
/// </summary>
/// <param name="layerInfo"> Container for the instance id and CompositionLayer component of the composition layer.</param>
/// <returns>The render texture with the provided id or null if no render textrue with that id was found.</returns>
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;
}
/// <summary>
/// Handles transfering texture data to a render texture.
/// </summary>
/// <param name="texture">The source texture that will be written into the provided render texture.</param>
/// <param name="renderTexture">The render texture that will be written to.</param>
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);
}
}
}
}
/// <summary>
/// Query the correct XR Textures for rendering and blit the layer textures.
/// </summary>
/// <param name="layerInfo"> Container for the instance id and CompositionLayer component of the composition layer.</param>
/// <param name="texture">The source texture that will be written into the provided render texture.</param>
/// <param name="renderTexture">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.</param>
/// <returns>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.</returns>
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;
}
/// <summary>
/// Add native layer structs to the <c>endFrameInfo</c> struct inside the UnityPXR_ lib - for custom layer type support
/// </summary>
/// <param name="layers">Pointer to the native array of currently active composition layers.</param>
/// <param name="orders">Pointer to the native array of order values for the currently active composition layers.</param>
/// <param name="count">Indicates the size of the layers and orders arrays.</param>
/// <param name="layerByteSize">Indicates the size in bytes of a single element of the given array of composition layers.</param>
/// <remarks>Layers sent must all be of the same type.Demonstrated in the PXR_CustomLayerHandler class.</remarks>
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);
}
/// <summary>
/// Return the Surface object for Android External Surface support (Android only).
/// </summary>
/// <param name="layerId">The instance id of the composition layer object.</param>
/// <returns>Pointer to the android surface object.</returns>
public static System.IntPtr GetLayerAndroidSurfaceObject(int layerId)
{
IntPtr surfaceObject = IntPtr.Zero;
if (Pxr_CompositorLayersGetLayerAndroidSurfaceObject(layerId, ref surfaceObject))
{
return surfaceObject;
}
return IntPtr.Zero;
}
/// <summary>
/// Sends an array of extensions to be attached to the native default compostion layer.
/// </summary>
/// <param name="extensions">Pointer to the array of extensions to attach to the default compostion layer.</param>
/// <remarks>Currently only called by the PXR_DefautLayer class.</remarks>
public static unsafe void SetDefaultSceneLayerExtensions(void* extensions)
{
// IntPtr ptr = new IntPtr(extensions);
// ext_composition_layers_SetDefaultSceneLayerExtensions(extensions);
}
/// <summary>
/// Sends what flags are to be added to the native default compostion layer.
/// </summary>
/// <param name="flags">Flags to be added to the native default compostion layer.</param>
/// <remarks>Currently only called by the PXR_DefautLayer class.</remarks>
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

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 50cb351d62257144eabbecc94c61adef
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,300 @@
#if XR_COMPOSITION_LAYERS
using Unity.XR.CompositionLayers;
using Unity.XR.CompositionLayers.Extensions;
using Unity.XR.CompositionLayers.Layers;
using Unity.XR.CompositionLayers.Services;
using Unity.XR.PXR;
using UnityEngine;
namespace Unity.XR.PXR
{
//Default PXR_ Composition Layer - Quad Layer support
internal class PXR_QuadLayer : PXR_CustomLayerHandler<XrCompositionLayerQuad>
{
protected override bool CreateSwapchain(CompositionLayerManager.LayerInfo layer, out SwapchainCreateInfo swapchainCreateInfo)
{
unsafe
{
var texturesExtension = layer.Layer.GetComponent<TexturesExtension>();
if (texturesExtension == null || texturesExtension.enabled == false)
{
swapchainCreateInfo = default;
return false;
}
switch (texturesExtension.sourceTexture)
{
case TexturesExtension.SourceTextureEnum.LocalTexture:
{
if (texturesExtension.LeftTexture == null)
goto default;
var xrCreateInfo = new XrSwapchainCreateInfo()
{
Type = (uint)XrStructureType.XR_TYPE_SWAPCHAIN_CREATE_INFO,
Next = PXR_LayerUtility.GetExtensionsChain(layer, CompositionLayerExtension.ExtensionTarget.Swapchain),
CreateFlags = 0,
UsageFlags = (ulong)(XrSwapchainUsageFlags.XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XrSwapchainUsageFlags.XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT),
Format = PXR_LayerUtility.GetDefaultColorFormat(),
SampleCount = 1,
Width = (uint)texturesExtension.LeftTexture.width,
Height = (uint)texturesExtension.LeftTexture.height,
FaceCount = 1,
ArraySize = 1,
MipCount = (uint)texturesExtension.LeftTexture.mipmapCount,
};
swapchainCreateInfo = new SwapchainCreateInfo(xrCreateInfo, isExternalSurface: false);
return true;
}
case TexturesExtension.SourceTextureEnum.AndroidSurface:
{
#if UNITY_ANDROID
var xrCreateInfo = new XrSwapchainCreateInfo()
{
Type = (uint)XrStructureType.XR_TYPE_SWAPCHAIN_CREATE_INFO,
Next = PXR_LayerUtility.GetExtensionsChain(layer, CompositionLayerExtension.ExtensionTarget.Swapchain),
CreateFlags = 0,
UsageFlags = (ulong)(XrSwapchainUsageFlags.XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XrSwapchainUsageFlags.XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT),
Format = 0,
SampleCount = 0,
Width = (uint)texturesExtension.Resolution.x,
Height = (uint)texturesExtension.Resolution.y,
FaceCount = 0,
ArraySize = 0,
MipCount = 0,
};
swapchainCreateInfo = new SwapchainCreateInfo(xrCreateInfo, isExternalSurface: true);
return true;
#else
goto default;
#endif
}
default:
swapchainCreateInfo = default;
return false;
}
}
}
protected override bool CreateNativeLayer(CompositionLayerManager.LayerInfo layer, SwapchainCreatedOutput swapchainOutput, out XrCompositionLayerQuad nativeLayer)
{
unsafe
{
var texturesExtension = layer.Layer.GetComponent<TexturesExtension>();
if (texturesExtension == null || texturesExtension.enabled == false)
{
nativeLayer = default;
return false;
}
var data = layer.Layer.LayerData as QuadLayerData;
var transform = layer.Layer.GetComponent<Transform>();
int subImageWidth = 0;
int subImageHeight = 0;
switch (texturesExtension.sourceTexture)
{
case TexturesExtension.SourceTextureEnum.LocalTexture:
{
if (texturesExtension.LeftTexture != null)
{
subImageWidth = texturesExtension.LeftTexture.width;
subImageHeight = texturesExtension.LeftTexture.height;
}
break;
}
case TexturesExtension.SourceTextureEnum.AndroidSurface:
{
subImageWidth = (int)texturesExtension.Resolution.x;
subImageHeight = (int)texturesExtension.Resolution.y;
break;
}
}
var correctedSize = texturesExtension.CropToAspect ?
FixAspectRatio(data, transform, subImageWidth, subImageHeight) :
data.GetScaledSize(transform.lossyScale);
nativeLayer = new XrCompositionLayerQuad()
{
Type = (uint)XrStructureType.XR_TYPE_COMPOSITION_LAYER_QUAD,
Next = PXR_LayerUtility.GetExtensionsChain(layer, CompositionLayerExtension.ExtensionTarget.Layer),
LayerFlags = data.BlendType == BlendType.Premultiply ? XrCompositionLayerFlags.SourceAlpha : XrCompositionLayerFlags.SourceAlpha | XrCompositionLayerFlags.UnPremultipliedAlpha,
Space = PXR_LayerUtility.GetCurrentAppSpace(),
EyeVisibility = 0,
SubImage = new XrSwapchainSubImage()
{
Swapchain = swapchainOutput.handle,
ImageRect = new XrRect2Di()
{
Offset = new XrOffset2Di() { X = 0, Y = 0 },
Extent = new XrExtent2Di()
{
Width = subImageWidth,
Height = subImageHeight
}
},
ImageArrayIndex = 0
},
Pose = new XrPosef(PXR_Utility.ComputePoseToWorldSpace(transform, CompositionLayerManager.mainCameraCache).position, PXR_Utility.ComputePoseToWorldSpace(transform, CompositionLayerManager.mainCameraCache).rotation),
Size = new XrExtent2Df()
{
width = correctedSize.x,
height = correctedSize.y
}
};
return true;
}
}
protected override bool ModifyNativeLayer(CompositionLayerManager.LayerInfo layerInfo, ref XrCompositionLayerQuad nativeLayer)
{
var texturesExtension = layerInfo.Layer.GetComponent<TexturesExtension>();
if (texturesExtension == null || texturesExtension.enabled == false)
return false;
var data = layerInfo.Layer.LayerData as QuadLayerData;
var transform = layerInfo.Layer.GetComponent<Transform>();
nativeLayer.Pose = new XrPosef(PXR_Utility.ComputePoseToWorldSpace(transform, CompositionLayerManager.mainCameraCache).position, PXR_Utility.ComputePoseToWorldSpace(transform, CompositionLayerManager.mainCameraCache).rotation);
int subImageWidth = 0;
int subImageHeight = 0;
switch (texturesExtension.sourceTexture)
{
case TexturesExtension.SourceTextureEnum.LocalTexture:
{
if (texturesExtension.LeftTexture != null)
{
subImageWidth = texturesExtension.LeftTexture.width;
subImageHeight = texturesExtension.LeftTexture.height;
}
break;
}
case TexturesExtension.SourceTextureEnum.AndroidSurface:
{
subImageWidth = (int)texturesExtension.Resolution.x;
subImageHeight = (int)texturesExtension.Resolution.y;
break;
}
}
nativeLayer.SubImage.ImageRect.Extent = new XrExtent2Di()
{
Width = subImageWidth,
Height = subImageHeight
};
var correctedSize = texturesExtension.CropToAspect ?
FixAspectRatio(data, transform, subImageWidth, subImageHeight) :
data.GetScaledSize(transform.lossyScale);
nativeLayer.Size = new XrExtent2Df()
{
width = correctedSize.x,
height = correctedSize.y
};
unsafe
{
nativeLayer.Next = PXR_LayerUtility.GetExtensionsChain(layerInfo, CompositionLayerExtension.ExtensionTarget.Layer);
}
return true;
}
protected override bool ActiveNativeLayer(CompositionLayerManager.LayerInfo layerInfo, ref XrCompositionLayerQuad nativeLayer)
{
var texturesExtension = layerInfo.Layer.GetComponent<TexturesExtension>();
if (texturesExtension == null || texturesExtension.enabled == false)
return false;
var data = layerInfo.Layer.LayerData as QuadLayerData;
var transform = layerInfo.Layer.GetComponent<Transform>();
nativeLayer.Pose = new XrPosef(PXR_Utility.ComputePoseToWorldSpace(transform, CompositionLayerManager.mainCameraCache).position, PXR_Utility.ComputePoseToWorldSpace(transform, CompositionLayerManager.mainCameraCache).rotation);
nativeLayer.Space = PXR_LayerUtility.GetCurrentAppSpace();
if (texturesExtension.CustomRects)
{
int subImageWidth = 0;
int subImageHeight = 0;
switch (texturesExtension.sourceTexture)
{
case TexturesExtension.SourceTextureEnum.LocalTexture:
{
if (texturesExtension.LeftTexture != null)
{
subImageWidth = texturesExtension.LeftTexture.width;
subImageHeight = texturesExtension.LeftTexture.height;
}
break;
}
case TexturesExtension.SourceTextureEnum.AndroidSurface:
{
subImageWidth = (int)texturesExtension.Resolution.x;
subImageHeight = (int)texturesExtension.Resolution.y;
break;
}
}
nativeLayer.SubImage.ImageRect = new XrRect2Di()
{
Offset = new XrOffset2Di()
{
X = (int)(subImageWidth * texturesExtension.LeftEyeSourceRect.x),
Y = (int)(subImageHeight * texturesExtension.LeftEyeSourceRect.y)
},
Extent = new XrExtent2Di()
{
Width = (int)(subImageWidth * texturesExtension.LeftEyeSourceRect.width),
Height = (int)(subImageHeight * texturesExtension.LeftEyeSourceRect.height)
}
};
var currentPosition = PXR_Utility.ComputePoseToWorldSpace(transform, CompositionLayerManager.mainCameraCache).position;
var correctedSize = texturesExtension.CropToAspect ?
FixAspectRatio(data, transform, subImageWidth, subImageHeight) :
data.GetScaledSize(transform.lossyScale);
float transformedX = currentPosition.x + (((texturesExtension.LeftEyeDestinationRect.x + (0.5f * texturesExtension.LeftEyeDestinationRect.width) - 0.5f)) * correctedSize.x);
float transformedY = currentPosition.y + (((texturesExtension.LeftEyeDestinationRect.y + (0.5f * texturesExtension.LeftEyeDestinationRect.height) - 0.5f)) * (-1.0f * correctedSize.y));
nativeLayer.Pose = new XrPosef(new Vector3(transformedX, transformedY, currentPosition.z), PXR_Utility.ComputePoseToWorldSpace(transform, CompositionLayerManager.mainCameraCache).rotation);
nativeLayer.Size = new XrExtent2Df()
{
width = correctedSize.x * texturesExtension.LeftEyeDestinationRect.width,
height = correctedSize.y * texturesExtension.LeftEyeDestinationRect.height
};
}
return base.ActiveNativeLayer(layerInfo, ref nativeLayer);
}
static Vector2 FixAspectRatio(QuadLayerData data, Transform transform, int texWidth, int texHeight)
{
var requestedSize = data.GetScaledSize(transform.lossyScale);
float reqSizeRatio = (float)requestedSize.x / (float)requestedSize.y;
float texRatio = (float)texWidth / (float)texHeight;
if (reqSizeRatio > texRatio)
{
// too wide
requestedSize.x = requestedSize.y * texRatio;
}
else if (reqSizeRatio < texRatio)
{
// too narrow
requestedSize.y = requestedSize.x / texRatio;
}
return requestedSize;
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c5e1a0709a0c74345b2e0408bec5348c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,65 @@
using System.Runtime.InteropServices;
using UnityEngine;
namespace Unity.XR.PXR
{
/// <summary>
/// PXR_ Utility Class contains helper methods that any script can use.
/// </summary>
public static class PXR_Utility
{
/// <summary>
/// Computes the inverse of the given pose.
/// </summary>
private static Pose Inverse(Pose p)
{
Pose ret;
ret.rotation = Quaternion.Inverse(p.rotation);
ret.position = ret.rotation * -p.position;
return ret;
}
/// <summary>
/// Recalculate object position and rotation from tracking-space to world-space, for use cases like teleporting.
/// </summary>
/// <param name="t">original transform of the object in the scene, typically obtained by gameObject.transform</param>
/// <param name="camera">camera the calculation is based on, normally it is the main camera</param>
/// <returns>the recalculated pose <see cref="UnityEngine.Pose"/> in world-space.</returns>
public static Pose ComputePoseToWorldSpace(Transform t, Camera camera)
{
if (camera == null)
return default;
Transform cameraTransform = camera.transform;
Pose headPose = new Pose(cameraTransform.localPosition, cameraTransform.localRotation);
Pose camPose = new Pose(cameraTransform.position, cameraTransform.rotation);
Pose transformPose = new Pose(t.position, t.rotation);
Pose headSpacePose = transformPose.GetTransformedBy(Inverse(camPose));
return headSpacePose.GetTransformedBy(headPose);
}
/// <summary>
/// Returns if the current session is in the focused state.
/// See <a href="https://registry.khronos.org/PXR_/specs/1.0/html/xrspec.html#session-states">XR_SESSION_STATE_FOCUSED.</a> for reference.
/// </summary>
public static bool IsSessionFocused => Internal_IsSessionFocused();
/// <summary>
/// Returns the change of user presence, such as when the user has taken off or put on an XR headset.
/// If the system does not support user presence sensing, runtime assumes that the user is always present and IsUserPresent always returns True.
/// If the system supports the sensing of user presence, returns true when detected the presence of a user and returns false when detected the absence of a user.
/// See <a href="https://registry.khronos.org/PXR_/specs/1.0/html/xrspec.html#XR_EXT_user_presence">XR_EXT_user_presence.</a> for reference.
/// </summary>
public static bool IsUserPresent => Internal_GetUserPresence();
private const string LibraryName = "PxrPlatform";
[DllImport(LibraryName, EntryPoint = "NativeConfig_IsSessionFocused")]
[return: MarshalAs(UnmanagedType.U1)]
private static extern bool Internal_IsSessionFocused();
[DllImport(LibraryName, EntryPoint = "NativeConfig_GetUserPresence")]
[return: MarshalAs(UnmanagedType.U1)]
private static extern bool Internal_GetUserPresence();
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5fe75684f72312e4187498fd4db6f5ee
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: