上传YomovSDK

This commit is contained in:
Sora丶kong
2026-03-03 03:15:46 +08:00
parent 9096da7e6c
commit eb97f31065
6477 changed files with 1932208 additions and 3 deletions

View File

@@ -0,0 +1,47 @@
#if XR_COMPOSITION_LAYERS
using UnityEditor;
using Unity.XR.CompositionLayers.Services;
using UnityEngine.XR.OpenXR.CompositionLayers;
#if UNITY_EDITOR
using UnityEditor.XR.OpenXR.Features;
#endif
namespace UnityEngine.XR.OpenXR.Features.CompositionLayers
{
/// <summary>
/// Enables OpenXR Composition Layers.
/// </summary>
#if UNITY_EDITOR
[OpenXRFeature(UiName = FeatureName,
Desc = "Necessary to use OpenXR Composition Layers.",
Company = "Unity",
OpenxrExtensionStrings = "XR_KHR_composition_layer_cylinder XR_KHR_composition_layer_equirect XR_KHR_composition_layer_equirect2 XR_KHR_composition_layer_cube XR_KHR_composition_layer_color_scale_bias XR_KHR_android_surface_swapchain",
Version = "1.0.0",
BuildTargetGroups = new[] { BuildTargetGroup.Android, BuildTargetGroup.Standalone, BuildTargetGroup.WSA },
FeatureId = FeatureId
)]
#endif
public class OpenXRCompositionLayersFeature : OpenXRFeature
{
public const string FeatureId = "com.unity.openxr.feature.compositionlayers";
internal const string FeatureName = "Composition Layers Support";
protected internal override void OnSessionBegin(ulong xrSession)
{
if (CompositionLayerManager.Instance != null)
{
CompositionLayerManager.Instance.LayerProvider ??= new OpenXRLayerProvider();
}
}
protected internal override void OnSessionEnd(ulong xrSession)
{
if (CompositionLayerManager.Instance?.LayerProvider is OpenXRLayerProvider)
{
((OpenXRLayerProvider)CompositionLayerManager.Instance.LayerProvider).Dispose();
CompositionLayerManager.Instance.LayerProvider = null;
}
}
}
}
#endif

View File

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

View File

@@ -0,0 +1,86 @@
#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.XR.OpenXR.NativeTypes;
namespace UnityEngine.XR.OpenXR.CompositionLayers
{
internal class OpenXRCubeLayer : OpenXRCustomLayerHandler<XrCompositionLayerCubeKHR>
{
public static bool ExtensionEnabled = OpenXRRuntime.IsExtensionEnabled("XR_KHR_composition_layer_cube");
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 = OpenXRLayerUtility.GetExtensionsChain(layerInfo, CompositionLayerExtension.ExtensionTarget.Swapchain),
CreateFlags = 0,
UsageFlags = (ulong)(XrSwapchainUsageFlags.XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XrSwapchainUsageFlags.XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT),
Format = OpenXRLayerUtility.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 = OpenXRLayerUtility.GetExtensionsChain(layerInfo, CompositionLayerExtension.ExtensionTarget.Layer),
LayerFlags = data.BlendType == BlendType.Premultiply ? XrCompositionLayerFlags.SourceAlpha : XrCompositionLayerFlags.SourceAlpha | XrCompositionLayerFlags.UnPremultipliedAlpha,
Space = OpenXRLayerUtility.GetCurrentAppSpace(),
EyeVisibility = 0,
Swapchain = swapchainOutput.handle,
ImageArrayIndex = 0,
Orientation = new XrQuaternionf(OpenXRUtility.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 = OpenXRLayerUtility.GetCurrentAppSpace();
nativeLayer.Orientation = new XrQuaternionf(OpenXRUtility.ComputePoseToWorldSpace(transform, CompositionLayerManager.mainCameraCache).rotation);
return true;
}
protected override bool ActiveNativeLayer(CompositionLayerManager.LayerInfo layerInfo, ref XrCompositionLayerCubeKHR nativeLayer)
{
nativeLayer.Space = OpenXRLayerUtility.GetCurrentAppSpace();
return base.ActiveNativeLayer(layerInfo, ref nativeLayer);
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
}
}
}
#endif

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: eb695c3684cd4e948bac4768c2e36307
timeCreated: 1693948528

View File

@@ -0,0 +1,589 @@
#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.Layers;
using Unity.XR.CompositionLayers.Services;
using UnityEngine.XR.OpenXR.NativeTypes;
#if UNITY_VIDEO
using UnityEngine.Video;
#endif
namespace UnityEngine.XR.OpenXR.CompositionLayers
{
/// <summary>
/// Provides a base implementation for the <see cref="OpenXRLayerProvider.ILayerHandler"/> interface.
/// You can implement the required methods of this abstract class to create a concrete layer handler.
/// </summary>
/// <remarks>
/// The <see cref="OpenXRLayerProvider.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, OpenXRCustomLayerHandler{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="OpenXRLayerProvider.ILayerHandler"/> interface in its entirety.
///
/// You must register your concrete layer handler object with
/// <see cref="OpenXRLayerProvider.RegisterLayerHandler(Type, OpenXRLayerProvider.ILayerHandler)"/>.
/// </remarks>
/// <typeparam name="T">The native OpenXR structure of the composition layer to handle.</typeparam>
public abstract class OpenXRCustomLayerHandler<T> : OpenXRLayerProvider.ILayerHandler, IDisposable where T : struct
{
/// <summary>
/// Container for swapchain related information that may be needed during the creation of the native OpenXR 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 OpenXR 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 OpenXR 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 OpenXR composition layer struct.
/// </summary>
public ulong secondStereoHandle;
}
/// <summary>
/// Container for grouping render information for each compostion layer.
/// </summary>
class LayerRenderInfo
{
public RenderTexture RenderTexture;
public Texture Texture;
#if UNITY_VIDEO
public VideoPlayer videoPlayer;
#endif
public MeshCollider meshCollider;
}
/// <summary>
/// Initializes and returns an instance of this <c>OpenXRCustomLayerHandler&lt;T&gt;</c> while also setting the singleton instance member.
/// </summary>
protected OpenXRCustomLayerHandler() => Instance = this;
/// <summary>
/// Singleton instance of this specific handler.
/// </summary>
protected static OpenXRCustomLayerHandler<T> Instance;
/// <summary>
/// Deinitializes this instance of c>OpenXRCustomLayerHandler&lt;T&gt;</c>.
/// </summary>
~OpenXRCustomLayerHandler() => Dispose(false);
/// <summary>
/// Override this method to create the <see cref="XrSwapchainCreateInfo"/> struct that is passed to OpenXR
/// 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="OpenXRLayerUtility.GetExtensionsChain(CompositionLayerManager.LayerInfo, Unity.XR.CompositionLayers.CompositionLayerExtension.ExtensionTarget)"/>.
/// </remarks>
/// <example>
/// Constructs an XrSwapchainCreateInfo struct with some members using component data.
/// <code>
/// <![CDATA[
/// protected override bool CreateSwapchain(CompositionLayerManager.LayerInfo layerInfo, out SwapchainCreateInfo swapchainCreateInfo)
/// {
/// var texturesExtension = layerInfo.Layer.GetComponent<TexturesExtension>();
/// unsafe
/// {
/// swapchainCreateInfo = new XrSwapchainCreateInfo()
/// {
/// Type = (uint)XrStructureType.XR_TYPE_SWAPCHAIN_CREATE_INFO,
/// Next = OpenXRLayerUtility.GetExtensionsChain(layerInfo, CompositionLayerExtension.ExtensionTarget.Swapchain),
/// CreateFlags = 0,
/// UsageFlags = (ulong)(XrSwapchainUsageFlags.XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XrSwapchainUsageFlags.XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT),
/// Format = OpenXRLayerUtility.GetDefaultColorFormat(),
/// SampleCount = 1,
/// Width = (uint)texturesExtension.LeftTexture.width,
/// Height = (uint)texturesExtension.LeftTexture.height,
/// FaceCount = 1,
/// ArraySize = 1,
/// MipCount = (uint)texturesExtension.LeftTexture.mipmapCount,
/// };
/// }
/// return true;
/// }
/// ]]>
/// </code>
/// </example>
/// <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 OpenXR.
/// 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="OpenXRLayerUtility.GetExtensionsChain(CompositionLayerManager.LayerInfo, Unity.XR.CompositionLayers.CompositionLayerExtension.ExtensionTarget)"/>.
///
/// If your struct needs any XrSpace relative info you can use <see cref="OpenXRLayerUtility.GetCurrentAppSpace"/>
/// to get the current app space.
/// </remarks>
/// <example>
/// Constructs an XrCompositionLayerQuad struct with some members using component data.
/// <code>
/// <![CDATA[
/// protected override bool CreateNativeLayer(CompositionLayerManager.LayerInfo layerInfo, SwapchainCreatedOutput swapchainOutput, out XrCompositionLayerQuad nativeLayer)
/// {
/// var texturesExtension = layerInfo.Layer.GetComponent<TexturesExtension>();
/// var transform = layerInfo.Layer.GetComponent<Transform>();
///
/// unsafe
/// {
/// var data = layerInfo.Layer.LayerData as QuadLayerData;
///
/// nativeLayer = new XrCompositionLayerQuad()
/// {
/// Type = 36,
/// Next = OpenXRLayerUtility.GetExtensionsChain(layerInfo, CompositionLayerExtension.ExtensionTarget.Layer),
/// LayerFlags = 0x00000002,
/// Space = OpenXRLayerUtility.GetCurrentAppSpace(),
/// EyeVisibility = 0,
/// SubImage = new SwapchainSubImage()
/// {
/// 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(transform.position, transform.rotation),
/// Size = new XrExtend2Df()
/// {
/// width = data.GetScaledSize(transform.lossyScale).x,
/// height = data.GetScaledSize(transform.lossyScale).y
/// }
/// };
/// }
/// return true;
/// }
/// ]]>
/// </code>
/// </example>
/// <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="OpenXRLayerUtility.GetExtensionsChain(CompositionLayerManager.LayerInfo, Unity.XR.CompositionLayers.CompositionLayerExtension.ExtensionTarget)"/>
/// to get any potential updates from extension components.
/// </remarks>
/// <example>
/// Modifies an XrCompositionLayerQuad struct with new transform and extension information.
/// <code>
/// <![CDATA[
/// protected override bool ModifyNativeLayer(CompositionLayerManager.LayerInfo layerInfo, ref XrCompositionLayerQuad nativeLayer)
/// {
/// var data = layerInfo.Layer.LayerData as QuadLayerData;
/// var transform = layerInfo.Layer.GetComponent<Transform>();
///
/// nativeLayer.Pose = new XrPosef(transform.position, transform.rotation);
/// nativeLayer.Size = new XrExtend2Df()
/// {
/// width = data.GetScaledSize(transform.lossyScale).x,
/// height = data.GetScaledSize(transform.lossyScale).y
/// };
///
/// unsafe
/// {
/// nativeLayer.Next = OpenXRLayerUtility.GetExtensionsChain(layerInfo, CompositionLayerExtension.ExtensionTarget.Layer);
/// }
/// return true;
/// }
/// ]]>
/// </code>
/// </example>
/// <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 OpenXR 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="OpenXRLayerProvider.ILayerHandler"/> method that is called by the
/// <see cref="OpenXRLayerProvider"/> 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 UnityOpenXR lib.
/// </remarks>
public virtual void OnUpdate()
{
while (actionsForMainThread.Count > 0)
{
if (actionsForMainThread.TryDequeue(out Action action))
action();
}
unsafe
{
if (m_ActiveNativeLayerCount > 0)
OpenXRLayerUtility.AddActiveLayersToEndFrame(m_ActiveNativeLayers.GetUnsafePtr(), m_ActiveNativeLayerOrders.GetUnsafePtr(), m_ActiveNativeLayerCount, UnsafeUtility.SizeOf<T>());
}
m_ActiveNativeLayerCount = 0;
}
/// <summary>
/// Implements the <see cref="OpenXRLayerProvider.ILayerHandler"/> method that is called by the
/// <see cref="OpenXRLayerProvider"/> 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="OpenXRLayerProvider.ILayerHandler"/> method that is called by the
/// <see cref="OpenXRLayerProvider"/> 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="OpenXRLayerProvider.ILayerHandler"/> method that is called by the
/// <see cref="OpenXRLayerProvider"/> 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)
{
OpenXRLayerUtility.ReleaseSwapchain(removedLayerId);
m_nativeLayers.Remove(removedLayerId);
m_layerInfos.Remove(removedLayerId);
m_renderInfos.Remove(removedLayerId);
}
/// <summary>
/// Implements the <see cref="OpenXRLayerProvider.ILayerHandler"/> method that is called by the
/// <see cref="OpenXRLayerProvider"/> 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="OpenXRLayerProvider"/>
/// 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
/// UnityOpenXR 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)
OpenXRLayerUtility.CreateStereoSwapchain(layerInfo.Id, swapChainInfo.nativeStruct, OnCreatedStereoSwapchainCallback);
else
OpenXRLayerUtility.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;
}
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 OpenXR 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))
{
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)
OpenXRLayerUtility.FindAndWriteToRenderTexture(layerInfo, container.Texture, out container.RenderTexture);
else if (isNewTexture)
OpenXRLayerUtility.WriteToRenderTexture(container.Texture, container.RenderTexture);
#else
// We only need to write continuously to the native render texture if our texture is changing.
if (isVideo || isUI || isNewTexture)
OpenXRLayerUtility.WriteToRenderTexture(container.Texture, container.RenderTexture);
#endif
}
else
{
bool isRenderTextureWritten = OpenXRLayerUtility.FindAndWriteToRenderTexture(layerInfo, texturesExtension.LeftTexture, out RenderTexture renderTexture);
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(OpenXRLayerUtility.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(OpenXRLayerUtility.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: 6aba3e678d47ef44585d1e55c9d4d821
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,316 @@
#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.XR.OpenXR.NativeTypes;
namespace UnityEngine.XR.OpenXR.CompositionLayers
{
internal class OpenXRCylinderLayer : OpenXRCustomLayerHandler<XrCompositionLayerCylinderKHR>
{
public static bool ExtensionEnabled = OpenXRRuntime.IsExtensionEnabled("XR_KHR_composition_layer_cylinder");
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 = OpenXRLayerUtility.GetExtensionsChain(layer, CompositionLayerExtension.ExtensionTarget.Swapchain),
CreateFlags = 0,
UsageFlags = (ulong)(XrSwapchainUsageFlags.XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XrSwapchainUsageFlags.XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT),
Format = OpenXRLayerUtility.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 = OpenXRLayerUtility.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 = OpenXRLayerUtility.GetExtensionsChain(layer, CompositionLayerExtension.ExtensionTarget.Layer),
LayerFlags = data.BlendType == BlendType.Premultiply ? XrCompositionLayerFlags.SourceAlpha : XrCompositionLayerFlags.SourceAlpha | XrCompositionLayerFlags.UnPremultipliedAlpha,
Space = OpenXRLayerUtility.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(OpenXRUtility.ComputePoseToWorldSpace(transform, CompositionLayerManager.mainCameraCache).position, OpenXRUtility.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 = OpenXRLayerUtility.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(OpenXRUtility.ComputePoseToWorldSpace(transform, CompositionLayerManager.mainCameraCache).position, OpenXRUtility.ComputePoseToWorldSpace(transform, CompositionLayerManager.mainCameraCache).rotation);
nativeLayer.Space = OpenXRLayerUtility.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 = OpenXRUtility.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), OpenXRUtility.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,3 @@
fileFormatVersion: 2
guid: 2f0fe043a738427795fec6ef768571ed
timeCreated: 1681172287

View File

@@ -0,0 +1,41 @@
#if XR_COMPOSITION_LAYERS
using Unity.XR.CompositionLayers;
using Unity.XR.CompositionLayers.Layers;
using Unity.XR.CompositionLayers.Services;
using UnityEngine.XR.OpenXR.NativeTypes;
namespace UnityEngine.XR.OpenXR.CompositionLayers
{
internal class OpenXRDefaultLayer : OpenXRLayerProvider.ILayerHandler
{
unsafe void SetDefaultLayerAttributes(CompositionLayerManager.LayerInfo layerInfo)
{
var extensions = OpenXRLayerUtility.GetExtensionsChain(layerInfo, CompositionLayerExtension.ExtensionTarget.Layer);
OpenXRLayerUtility.SetDefaultSceneLayerExtensions(extensions);
var flags = layerInfo.Layer.LayerData.BlendType == BlendType.Premultiply ? XrCompositionLayerFlags.SourceAlpha : XrCompositionLayerFlags.SourceAlpha | XrCompositionLayerFlags.UnPremultipliedAlpha;
OpenXRLayerUtility.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: 0e69d044c52e6d64cb5468a07a96b67f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,240 @@
#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.XR.OpenXR.NativeTypes;
namespace UnityEngine.XR.OpenXR.CompositionLayers
{
//OpenXR Composition Layer - Equirect Layer support : only the interior of the mesh surface will be visible
internal class OpenXREquirectLayer : OpenXRCustomLayerHandler<XrCompositionLayerEquirectKHR>
{
public static bool ExtensionEnabled = OpenXRRuntime.IsExtensionEnabled("XR_KHR_composition_layer_equirect");
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 = OpenXRLayerUtility.GetExtensionsChain(layerInfo, CompositionLayerExtension.ExtensionTarget.Swapchain),
CreateFlags = 0,
UsageFlags = (ulong)(XrSwapchainUsageFlags.XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XrSwapchainUsageFlags.XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT),
Format = OpenXRLayerUtility.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 XrCompositionLayerEquirectKHR 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);
Vector2 biasCalculated = CalculateBias(scaleCalculated, data.UpperVerticalAngle);
nativeLayer = new XrCompositionLayerEquirectKHR()
{
Type = (uint)XrStructureType.XR_TYPE_COMPOSITION_LAYER_EQUIRECT_KHR,
Next = OpenXRLayerUtility.GetExtensionsChain(layerInfo, CompositionLayerExtension.ExtensionTarget.Layer),
LayerFlags = data.BlendType == BlendType.Premultiply ? XrCompositionLayerFlags.SourceAlpha : XrCompositionLayerFlags.SourceAlpha | XrCompositionLayerFlags.UnPremultipliedAlpha,
Space = OpenXRLayerUtility.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(OpenXRUtility.ComputePoseToWorldSpace(transform, CompositionLayerManager.mainCameraCache).position, OpenXRUtility.ComputePoseToWorldSpace(transform, CompositionLayerManager.mainCameraCache).rotation),
Radius = data.Radius,
Scale = new XrVector2f(scaleCalculated),
Bias = new XrVector2f(biasCalculated),
};
return true;
}
protected override unsafe bool ModifyNativeLayer(CompositionLayerManager.LayerInfo layerInfo, ref XrCompositionLayerEquirectKHR 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);
Vector2 biasCalculated = CalculateBias(scaleCalculated, data.UpperVerticalAngle);
nativeLayer.SubImage.ImageRect.Extent = new XrExtent2Di()
{
Width = texturesExtension.LeftTexture.width,
Height = texturesExtension.LeftTexture.height
};
nativeLayer.Pose = new XrPosef(OpenXRUtility.ComputePoseToWorldSpace(transform, CompositionLayerManager.mainCameraCache).position, OpenXRUtility.ComputePoseToWorldSpace(transform, CompositionLayerManager.mainCameraCache).rotation);
nativeLayer.Radius = data.Radius;
nativeLayer.Scale = new XrVector2f(scaleCalculated);
nativeLayer.Bias = new XrVector2f(biasCalculated);
nativeLayer.Next = OpenXRLayerUtility.GetExtensionsChain(layerInfo, CompositionLayerExtension.ExtensionTarget.Layer);
return true;
}
protected override bool ActiveNativeLayer(CompositionLayerManager.LayerInfo layerInfo, ref XrCompositionLayerEquirectKHR nativeLayer)
{
nativeLayer.Space = OpenXRLayerUtility.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));
}
Vector2 CalculateBias(Vector2 scaleCalculated, float upperVerticalAngle)
{
return new Vector2((1.0f - scaleCalculated.x) * 0.5f, (upperVerticalAngle / (float)Math.PI - 0.5f) * scaleCalculated.y);
}
}
internal class OpenXREquirect2Layer : OpenXRCustomLayerHandler<XrCompositionLayerEquirect2KHR>
{
public static bool ExtensionEnabled = OpenXRRuntime.IsExtensionEnabled("XR_KHR_composition_layer_equirect2");
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 = OpenXRLayerUtility.GetExtensionsChain(layerInfo, CompositionLayerExtension.ExtensionTarget.Swapchain),
CreateFlags = 0,
UsageFlags = (ulong)(XrSwapchainUsageFlags.XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XrSwapchainUsageFlags.XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT),
Format = OpenXRLayerUtility.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 = OpenXRLayerUtility.GetExtensionsChain(layerInfo, CompositionLayerExtension.ExtensionTarget.Layer),
LayerFlags = data.BlendType == BlendType.Premultiply ? XrCompositionLayerFlags.SourceAlpha : XrCompositionLayerFlags.SourceAlpha | XrCompositionLayerFlags.UnPremultipliedAlpha,
Space = OpenXRLayerUtility.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(OpenXRUtility.ComputePoseToWorldSpace(transform, CompositionLayerManager.mainCameraCache).position, OpenXRUtility.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(OpenXRUtility.ComputePoseToWorldSpace(transform, CompositionLayerManager.mainCameraCache).position, OpenXRUtility.ComputePoseToWorldSpace(transform, CompositionLayerManager.mainCameraCache).rotation);
nativeLayer.Radius = data.Radius;
nativeLayer.CentralHorizontalAngle = data.CentralHorizontalAngle;
nativeLayer.UpperVerticalAngle = data.UpperVerticalAngle;
nativeLayer.LowerVerticalAngle = -data.LowerVerticalAngle;
nativeLayer.Next = OpenXRLayerUtility.GetExtensionsChain(layerInfo, CompositionLayerExtension.ExtensionTarget.Layer);
return true;
}
protected override bool ActiveNativeLayer(CompositionLayerManager.LayerInfo layerInfo, ref XrCompositionLayerEquirect2KHR nativeLayer)
{
nativeLayer.Space = OpenXRLayerUtility.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: cdbb650170aefbc46964b76b43714332
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,299 @@
#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;
namespace UnityEngine.XR.OpenXR.CompositionLayers
{
/// <summary>
/// Manages communication of changes between an application and the UnityOpenXR lib for all
/// <see cref="Unity.XR.CompositionLayers.Layers.LayerData"/> objects.
/// </summary>
/// <remarks>
/// OpenXR 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>OpenXRLayerProvider</c> via <see cref="RegisterLayerHandler(Type, OpenXRLayerProvider.ILayerHandler)"/>.
/// </remarks>
public class OpenXRLayerProvider : ILayerProvider, IDisposable
{
/// <summary>
/// An interface used by the <see cref="OpenXRLayerProvider"/> to communicate layer data changes to
/// registered layer handlers.
/// </summary>
/// <remarks>
/// <c>ILayerHandler</c> instances must register themselves via
/// <see cref="OpenXRLayerProvider.RegisterLayerHandler(Type, OpenXRLayerProvider.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="OpenXRCustomLayerHandler{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="OpenXRLayerProvider"/> during the Unity Update loop.
/// All implementations must call <see cref="OpenXRLayerUtility.AddActiveLayersToEndFrame(void*,void*,int,int)"/> every frame
/// to add their native layer structs to the <c>endFrameInfo</c> struct inside the UnityOpenXR lib.
/// </summary>
public void OnUpdate();
/// <summary>
/// Called by the <see cref="OpenXRLayerProvider"/> 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="OpenXRLayerProvider"/> 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="OpenXRLayerProvider"/> 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="OpenXRLayerProvider"/> 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>OpenXRLayerProvider</c>.
/// Initializes and registers all the default, built-in layer handlers.
/// </summary>
/// <remarks>
/// The <c>OpenXRLayerProvider</c> is created and disposed by the <see cref="Management.XRLoader"/>.
/// You do not need to create an instance of <c>OpenXRLayerProvider</c> yourself. Layer handlers
/// should only use the static methods and properties of this class
/// </remarks>
public OpenXRLayerProvider() => InitializeAndRegisterBuiltInHandlers();
/// <summary>
/// Calls the methods in its invocation list when the <c>OpenXRLayerProvider</c> has started and registered it's built-in layer handlers.
/// </summary>
/// <remarks>
/// You can use this event to wait for the <c>OpenXRLayerProvider</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>OpenXRLayerProvider</c> has stopped and is disposed.
/// </summary>
public static event Action Stopped;
/// <summary>
/// Reports whether the <c>OpenXRLayerProvider</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_OpenXRLayerProviderCreate = new ProfilerMarker("OpenXRLayerProvider.Create");
static readonly ProfilerMarker s_OpenXRLayerProviderRemove = new ProfilerMarker("OpenXRLayerProvider.Remove");
static readonly ProfilerMarker s_OpenXRLayerProviderModify = new ProfilerMarker("OpenXRLayerProvider.Modify");
static readonly ProfilerMarker s_OpenXRLayerProviderActive = new ProfilerMarker("OpenXRLayerProvider.Active");
static readonly ProfilerMarker s_OpenXRLayerProviderUpdate = new ProfilerMarker("OpenXRLayerProvider.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>OpenXRLayerProvider</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_OpenXRLayerProviderRemove.Begin();
handler?.RemoveLayer(removed);
s_OpenXRLayerProviderRemove.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_OpenXRLayerProviderCreate.Begin();
handler?.CreateLayer(created);
s_OpenXRLayerProviderCreate.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_OpenXRLayerProviderModify.Begin();
handler?.ModifyLayer(modified);
s_OpenXRLayerProviderModify.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_OpenXRLayerProviderActive.Begin();
handler?.SetActiveLayer(active);
s_OpenXRLayerProviderActive.End();
}
}
}
foreach (var handler in LayerHandlers.Values)
{
s_OpenXRLayerProviderUpdate.Begin();
handler?.OnUpdate();
s_OpenXRLayerProviderUpdate.End();
}
}
/// <summary>
/// Used for cleanup and to call Dispose() on registered layer handlers.
/// </summary>
/// <remarks>This is called by the OpenXRLoader 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 OpenXRDefaultLayer();
var quadLayerHandler = new OpenXRQuadLayer();
var projectionLayerHandler = new OpenXRProjectionLayer();
var cylinderLayerHandler = OpenXRCylinderLayer.ExtensionEnabled ? new OpenXRCylinderLayer() : null;
var cubeLayerHandler = OpenXRCubeLayer.ExtensionEnabled ? new OpenXRCubeLayer() : null;
ILayerHandler equirectLayerHandler = OpenXREquirect2Layer.ExtensionEnabled ? new OpenXREquirect2Layer() : OpenXREquirectLayer.ExtensionEnabled ? new OpenXREquirectLayer() : null;
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: d72c58aa04070824aa72a1425788ddbb
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,340 @@
#if XR_COMPOSITION_LAYERS
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using Unity.XR.CompositionLayers;
using Unity.XR.CompositionLayers.Extensions;
using Unity.XR.CompositionLayers.Services;
using UnityEngine.XR.OpenXR.NativeTypes;
namespace UnityEngine.XR.OpenXR.CompositionLayers
{
/// <summary>
/// A general-purpose helper class for composition layer support.
/// </summary>
public static class OpenXRLayerUtility
{
internal unsafe delegate void LayerCallbackDelegate(int layerId, XrCompositionLayerBaseHeader* layer);
static Dictionary<UInt32, RenderTexture> _textureMap = new Dictionary<UInt32, RenderTexture>();
/// <summary>
/// Calls the methods in its invocation list when a swapchain is created on the graphics thread inside the UnityOpenXR 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 UnityOpenXR 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 OpenXR 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.OpenXRFeature.Internal_GetAppSpace(out ulong appSpaceId) ? appSpaceId : 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.OpenXRFeature.Internal_GetXRSession(out ulong xrSessionHandle) ? xrSessionHandle : 0;
/// <summary>
/// Create the <see cref="XrSwapchainCreateInfo"/> struct that is passed to OpenXR 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)
{
ext_composition_layers_CreateSwapchain(layerId, createInfo, isExternalSurface, callback);
}
/// <summary>
/// Create the <see cref="XrSwapchainCreateInfo"/> struct that is passed to OpenXR 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)
{
ext_composition_layers_CreateStereoSwapchain(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)
{
ext_composition_layers_ReleaseSwapchain(layerId);
}
/// <summary>
/// Return swapchain supported color format.
/// </summary>
/// <returns>The color format the swapchains will be using.</returns>
public static Int64 GetDefaultColorFormat()
{
return ext_composition_layers_GetUnityDefaultColorFormat();
}
/// <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 RenderTexture FindRenderTexture(UInt32 texId)
{
// texId will be 0 if swapchain has no images.
if (texId == 0)
return null;
if (!_textureMap.TryGetValue(texId, out var renderTexture))
{
var objs = Resources.FindObjectsOfTypeAll<RenderTexture>();
var name = $"XR Texture [{texId}]";
foreach (var rt in objs)
{
if (rt.name == name)
{
renderTexture = rt;
_textureMap[texId] = rt;
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 RenderTexture FindRenderTexture(CompositionLayerManager.LayerInfo layerInfo)
{
UInt32 texId = ext_compositor_layers_CreateOrGetRenderTextureId(layerInfo.Id);
return FindRenderTexture(texId);
}
/// <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(Texture texture, RenderTexture renderTexture)
{
if (texture == null || renderTexture == null)
return;
if (renderTexture.dimension == Rendering.TextureDimension.Cube && texture.dimension == Rendering.TextureDimension.Cube)
{
Cubemap convertedTexture = null;
// Check if graphics format or mipmap count is different and convert if necessary.
if (renderTexture.graphicsFormat != texture.graphicsFormat || renderTexture.mipmapCount != texture.mipmapCount)
{
convertedTexture = new Cubemap(texture.width, renderTexture.graphicsFormat, Experimental.Rendering.TextureCreationFlags.None);
// Convert the texture to the render texture format.
if (!Graphics.ConvertTexture(texture, convertedTexture))
{
Debug.LogError("Failed to convert Cubemap to Render Texture format!");
return;
}
}
for (int i = 0; i < 6; i++)
Graphics.CopyTexture(convertedTexture == null ? texture : convertedTexture, i, renderTexture, i);
}
else
Graphics.Blit(texture, renderTexture);
}
/// <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 RenderTexture renderTexture)
{
if (texture == null)
{
renderTexture = null;
return false;
}
renderTexture = FindRenderTexture(layerInfo);
WriteToRenderTexture(texture, renderTexture);
return renderTexture != null;
}
/// <summary>
/// Query the correct XR Textures for rendering and blit the layer textures (For Projection Layer type).
/// </summary>
/// <param name="layerInfo"> Container for the instance id and CompositionLayer component of the composition layer.</param>
/// <param name="renderTextureLeft">The left stereo render texture that will be searched for and written to.
/// Will be null if no render textures can be found for the provided layerInfo object.</param>
/// <param name="renderTextureRight">The right stereo render texture that will be searched for and written to.
/// Will be null if no render textures can be found for the provided layerInfo object.</param>
/// <returns>True if both render textures were found and written to, false if no texture was found on the TexturesExtension component of the layerInfo object or if no render texture was found for the provided layerInfo object.</returns>
public static bool FindAndWriteToStereoRenderTextures(CompositionLayerManager.LayerInfo layerInfo, out RenderTexture renderTextureLeft, out RenderTexture renderTextureRight)
{
var tex = layerInfo.Layer.GetComponent<TexturesExtension>();
if (tex == null || tex.LeftTexture == null || tex.RightTexture == null)
{
renderTextureLeft = null;
renderTextureRight = null;
return false;
}
ext_compositor_layers_CreateOrGetStereoRenderTextureIds(layerInfo.Id, out uint leftId, out uint rightId);
renderTextureLeft = FindRenderTexture(leftId);
renderTextureRight = FindRenderTexture(rightId);
WriteToRenderTexture(tex.LeftTexture, renderTextureLeft);
WriteToRenderTexture(tex.RightTexture, renderTextureRight);
return renderTextureLeft != null && renderTextureRight != null;
}
/// <summary>
/// Add native layer structs to the <c>endFrameInfo</c> struct inside the UnityOpenXR 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 OpenXRCustomLayerHandler class.</remarks>
public static unsafe void AddActiveLayersToEndFrame(void* layers, void* orders, int count, int layerByteSize)
{
ext_composition_layers_AddActiveLayers(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 (ext_composition_layers_GetLayerAndroidSurfaceObject(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 OpenXRDefautLayer class.</remarks>
public static unsafe void SetDefaultSceneLayerExtensions(void* 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 OpenXRDefautLayer class.</remarks>
public static unsafe void SetDefaultLayerFlags(XrCompositionLayerFlags flags)
{
ext_composition_layers_SetDefaultSceneLayerFlags(flags);
}
const string LibraryName = "UnityOpenXR";
[DllImport(LibraryName)]
internal static extern UInt32 ext_compositor_layers_CreateOrGetRenderTextureId(int id);
[DllImport(LibraryName)]
[return: MarshalAs(UnmanagedType.U1)]
internal static extern bool ext_compositor_layers_CreateOrGetStereoRenderTextureIds(int id, out UInt32 leftId, out UInt32 rightId);
[DllImport(LibraryName)]
internal static extern void ext_composition_layers_CreateSwapchain(int id, XrSwapchainCreateInfo createInfo, [MarshalAs(UnmanagedType.I1)]bool isExternalSurface = false, SwapchainCallbackDelegate callback = null);
[DllImport(LibraryName)]
internal static extern void ext_composition_layers_CreateStereoSwapchain(int id, XrSwapchainCreateInfo createInfo, StereoSwapchainCallbackDelegate callback = null);
[DllImport(LibraryName)]
internal static extern Int64 ext_composition_layers_GetUnityDefaultColorFormat();
[DllImport(LibraryName)]
internal static extern void ext_composition_layers_ReleaseSwapchain(int id);
[DllImport(LibraryName)]
internal static extern unsafe void ext_composition_layers_AddActiveLayers(void* layers, void* orders, int count, int size);
[DllImport(LibraryName)]
[return: MarshalAs(UnmanagedType.U1)]
internal static extern bool ext_composition_layers_GetLayerAndroidSurfaceObject(int layerId, ref IntPtr surfaceObject);
[DllImport(LibraryName)]
internal static extern unsafe void ext_composition_layers_SetDefaultSceneLayerExtensions(void* extensions);
[DllImport(LibraryName)]
internal static extern void ext_composition_layers_SetDefaultSceneLayerFlags(XrCompositionLayerFlags flags);
}
}
#endif

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: c2efbbbd0477445382219aa4c87a1f00
timeCreated: 1667257119

View File

@@ -0,0 +1,399 @@
#if XR_COMPOSITION_LAYERS
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using Unity.Collections;
using Unity.XR.CompositionLayers;
using Unity.XR.CompositionLayers.Extensions;
using Unity.XR.CompositionLayers.Layers;
using Unity.XR.CompositionLayers.Services;
using UnityEngine.Rendering;
using UnityEngine.XR.OpenXR.NativeTypes;
using Unity.Collections.LowLevel.Unsafe;
namespace UnityEngine.XR.OpenXR.CompositionLayers
{
//Default OpenXR Composition Layer - Projection Layer support
internal class OpenXRProjectionLayer : OpenXRCustomLayerHandler<XrCompositionLayerProjection>, IDisposable
{
class ProjectionData : IDisposable
{
public CompositionLayerManager.LayerInfo layer;
public NativeArray<XrCompositionLayerProjectionView> nativeViews;
public RenderTexture[] renderTextures;
public Camera[] cameras;
public void Dispose()
{
nativeViews.Dispose();
if (renderTextures != null)
{
foreach (var renderTexture in renderTextures)
{
renderTexture.Release();
}
}
}
}
Dictionary<int, ProjectionData> m_ProjectionData = new Dictionary<int, ProjectionData>();
bool m_isMainCameraRendered = false;
bool m_isOnBeforeRender = false;
/// <summary>
/// Constructor for OpenXR Projection Layer.
/// </summary>
public OpenXRProjectionLayer()
{
Application.onBeforeRender += OnBeforeRender;
RenderPipelineManager.endCameraRendering += OnCameraEndRendering;
Camera.onPostRender += OnCameraPostRender;
}
/// <summary>
/// Used to clean up.
/// </summary>
protected override void Dispose(bool disposing)
{
if (disposing)
{
foreach (var projectionData in m_ProjectionData.Values)
projectionData.Dispose();
m_ProjectionData.Clear();
}
Application.onBeforeRender -= OnBeforeRender;
base.Dispose(disposing);
}
protected override bool CreateSwapchain(CompositionLayerManager.LayerInfo layer, out SwapchainCreateInfo swapchainCreateInfo)
{
bool result = ext_composition_layers_GetViewConfigurationData(out uint width, out uint height, out uint sampleCount);
if (!result)
{
swapchainCreateInfo = default;
return false;
}
var isProjectionRig = layer.Layer.LayerData.GetType() == typeof(ProjectionLayerRigData);
if (!isProjectionRig)
{
TexturesExtension texturesExtension = layer.Layer.GetComponent<TexturesExtension>();
if (texturesExtension == null || texturesExtension.enabled == false || texturesExtension.LeftTexture == null || texturesExtension.RightTexture == null)
{
swapchainCreateInfo = default;
return false;
}
}
unsafe
{
var xrCreateInfo = new XrSwapchainCreateInfo()
{
Type = (uint)XrStructureType.XR_TYPE_SWAPCHAIN_CREATE_INFO,
Next = OpenXRLayerUtility.GetExtensionsChain(layer, CompositionLayerExtension.ExtensionTarget.Swapchain),
CreateFlags = 0,
UsageFlags = (ulong)(XrSwapchainUsageFlags.XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XrSwapchainUsageFlags.XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT),
Format = OpenXRLayerUtility.GetDefaultColorFormat(),
SampleCount = sampleCount,
Width = width,
Height = height,
FaceCount = 1,
ArraySize = 1,
MipCount = 1,
};
swapchainCreateInfo = new SwapchainCreateInfo(xrCreateInfo, isStereo: true);
return true;
}
}
/// <summary>
/// Called by the <see cref="OpenXRLayerProvider"/> when a new <see cref="LayerData"/>
/// object of the type registered to this <c>ILayerHandler</c> instance has been created.
/// </summary>
protected override unsafe bool CreateNativeLayer(CompositionLayerManager.LayerInfo layer, SwapchainCreatedOutput swapchainCreatedOutput, out XrCompositionLayerProjection nativeLayer)
{
if (layer.Layer == null)
{
nativeLayer = default;
return false;
}
bool result = ext_composition_layers_GetViewConfigurationData(out uint width, out uint height, out uint sampleCount);
if (!result)
{
nativeLayer = default;
return false;
}
var isProjectionRig = layer.Layer.LayerData.GetType() == typeof(ProjectionLayerRigData);
if (!isProjectionRig)
{
TexturesExtension texturesExtension = layer.Layer.GetComponent<TexturesExtension>();
if (texturesExtension == null || texturesExtension.enabled == false || texturesExtension.LeftTexture == null || texturesExtension.RightTexture == null)
{
nativeLayer = default;
return false;
}
}
XrView xrView1 = default;
XrView xrView2 = default;
bool validViews = ext_composition_layers_GetStereoViews(&xrView1, &xrView2);
XrView[] xrViews = new XrView[2] { xrView1, xrView2 };
var nativeView1 = new XrCompositionLayerProjectionView()
{
Type = (uint)XrStructureType.XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW,
Next = null,
Pose = validViews ? xrViews[0].Pose : default,
Fov = validViews ? xrViews[0].Fov : default,
SubImage = new XrSwapchainSubImage()
{
Swapchain = swapchainCreatedOutput.handle,
ImageRect = new XrRect2Di()
{
Offset = new XrOffset2Di() { X = 0, Y = 0 },
Extent = new XrExtent2Di()
{
Width = (int)width,
Height = (int)height
}
},
ImageArrayIndex = 0
},
};
var nativeView2 = new XrCompositionLayerProjectionView()
{
Type = (uint)XrStructureType.XR_TYPE_COMPOSITION_LAYER_PROJECTION_VIEW,
Next = null,
Pose = validViews ? xrViews[1].Pose : default,
Fov = validViews ? xrViews[1].Fov : default,
SubImage = new XrSwapchainSubImage()
{
Swapchain = swapchainCreatedOutput.secondStereoHandle,
ImageRect = new XrRect2Di()
{
Offset = new XrOffset2Di() { X = 0, Y = 0 },
Extent = new XrExtent2Di()
{
Width = (int)width,
Height = (int)height
}
},
ImageArrayIndex = 0
},
};
var nativeViews = new NativeArray<XrCompositionLayerProjectionView>(2, Allocator.Persistent);
nativeViews[0] = nativeView1;
nativeViews[1] = nativeView2;
nativeLayer = new XrCompositionLayerProjection()
{
Type = (uint)XrStructureType.XR_TYPE_COMPOSITION_LAYER_PROJECTION,
Next = OpenXRLayerUtility.GetExtensionsChain(layer, CompositionLayerExtension.ExtensionTarget.Layer),
LayerFlags = XrCompositionLayerFlags.SourceAlpha,
Space = OpenXRLayerUtility.GetCurrentAppSpace(),
ViewCount = 2,
Views = (XrCompositionLayerProjectionView*)nativeViews.GetUnsafePtr()
};
if (!isProjectionRig)
{
m_ProjectionData.Add(layer.Id, new ProjectionData
{
layer = layer,
nativeViews = nativeViews,
renderTextures = null,
cameras = null
});
return true;
}
// Create render textures to be the target texture of the child cameras.
Camera leftCamera = null;
Camera rightCamera = null;
if (layer.Layer.transform.childCount >= 2)
{
leftCamera = layer.Layer.transform.GetChild(0).GetComponent<Camera>();
rightCamera = layer.Layer.transform.GetChild(1).GetComponent<Camera>();
}
String layerName = String.Format("projectLayer_{0}", layer.Id);
RenderTexture leftRT = new RenderTexture((int)width, (int)height, 0, RenderTextureFormat.ARGB32) {name = layerName + "_left", depthStencilFormat = Experimental.Rendering.GraphicsFormat.D32_SFloat_S8_UInt };
leftRT.Create();
RenderTexture rightRT = new RenderTexture((int)width, (int)height, 0, RenderTextureFormat.ARGB32) {name = layerName + "_right", depthStencilFormat = Experimental.Rendering.GraphicsFormat.D32_SFloat_S8_UInt };
rightRT.Create();
TexturesExtension texExt = layer.Layer.GetComponent<TexturesExtension>();
texExt.LeftTexture = leftRT;
texExt.RightTexture = rightRT;
if (leftCamera)
{
leftCamera.targetTexture = leftRT;
leftCamera.enabled = false;
}
if (rightCamera != null)
{
rightCamera.targetTexture = rightRT;
rightCamera.enabled = false;
}
m_ProjectionData.Add(layer.Id, new ProjectionData
{
layer = layer,
nativeViews = nativeViews,
renderTextures = new RenderTexture[2] { leftRT, rightRT },
cameras = new Camera[2] { leftCamera, rightCamera }
});
return true;
}
/// <summary>
/// Called by the <see cref="OpenXRLayerProvider"/> when a <see cref="LayerData"/> object
/// of the type registered to this <c>ILayerHandler</c> instance has been destroyed or disabled.
/// </summary>
public override void RemoveLayer(int id)
{
if (m_ProjectionData.ContainsKey(id))
{
m_ProjectionData[id].Dispose();
m_ProjectionData.Remove(id);
}
base.RemoveLayer(id);
}
/// <summary>
/// Called by the <see cref="OpenXRLayerProvider"/> when a <see cref="LayerData"/> object
/// or any attached extension components have had a member modified.
/// </summary>
protected override unsafe bool ModifyNativeLayer(CompositionLayerManager.LayerInfo layerInfo, ref XrCompositionLayerProjection nativeLayer)
{
if (layerInfo.Layer.LayerData is ProjectionLayerRigData && !m_isOnBeforeRender)
return false;
var texturesExtension = layerInfo.Layer.GetComponent<TexturesExtension>();
if (texturesExtension == null || texturesExtension.enabled == false || texturesExtension.LeftTexture == null || texturesExtension.RightTexture == null)
return false;
XrView view1 = default;
XrView view2 = default;
bool validViews = ext_composition_layers_GetStereoViews(&view1, &view2);
if (validViews)
{
XrView[] views = new XrView[2] { view1, view2 };
nativeLayer.Views[0].Pose = views[0].Pose;
nativeLayer.Views[0].Fov = views[0].Fov;
nativeLayer.Views[1].Pose = views[1].Pose;
nativeLayer.Views[1].Fov = views[1].Fov;
}
nativeLayer.Next = OpenXRLayerUtility.GetExtensionsChain(layerInfo, CompositionLayerExtension.ExtensionTarget.Layer);
return true;
}
/// <summary>
/// Called every frame by the <see cref="OpenXRLayerProvider"/> for all currently active <see cref="LayerData"/> objects
/// of the type registered to this <c>ILayerHandler</c> instance.
/// </summary>
protected override unsafe bool ActiveNativeLayer(CompositionLayerManager.LayerInfo layerInfo, ref XrCompositionLayerProjection nativeLayer)
{
if (layerInfo.Layer.LayerData is ProjectionLayerRigData && !m_isOnBeforeRender)
return false;
var texturesExtension = layerInfo.Layer.GetComponent<TexturesExtension>();
if (texturesExtension != null || texturesExtension.enabled && texturesExtension.CustomRects && texturesExtension.LeftTexture != null || texturesExtension.RightTexture != null)
{
nativeLayer.Views[0].SubImage.ImageRect = GetSubImageOffsetAndExtent(texturesExtension.LeftEyeSourceRect, new Vector2(texturesExtension.LeftTexture?.width ?? 0, texturesExtension.LeftTexture?.height ?? 0));
nativeLayer.Views[1].SubImage.ImageRect = GetSubImageOffsetAndExtent(texturesExtension.RightEyeSourceRect, new Vector2(texturesExtension.RightTexture?.width ?? 0, texturesExtension.RightTexture?.height ?? 0));
}
nativeLayer.Space = OpenXRLayerUtility.GetCurrentAppSpace();
OpenXRLayerUtility.FindAndWriteToStereoRenderTextures(layerInfo, out RenderTexture renderTextureLeft, out RenderTexture renderTextureRight);
return true;
}
// Used to hault calls to GetStereoProjectionMatrix before the main camera's projection matrix has been initialized
void OnCameraPostRender(Camera cam)
{
var mainCamera = CompositionLayerManager.mainCameraCache;
if (m_isMainCameraRendered || mainCamera == null || cam != mainCamera)
return;
m_isMainCameraRendered = true;
Camera.onPostRender -= OnCameraPostRender;
RenderPipelineManager.endCameraRendering -= OnCameraEndRendering;
}
// Used to hault calls to GetStereoProjectionMatrix before the main camera's projection matrix has been initialized
void OnCameraEndRendering(ScriptableRenderContext cxt, Camera cam) => OnCameraPostRender(cam);
void OnBeforeRender()
{
m_isOnBeforeRender = true;
UpdateProjectionRigs();
m_isOnBeforeRender = false;
}
void UpdateProjectionRigs()
{
foreach (var projectionData in m_ProjectionData.Values)
{
// Only projection rig layers will have associated camera data.
if (projectionData.cameras == null || projectionData.layer.Layer == null)
continue;
var mainCamera = CompositionLayerManager.mainCameraCache;
var leftCamera = projectionData.cameras[0];
var rightCamera = projectionData.cameras[1];
if (m_isMainCameraRendered && leftCamera != null && rightCamera != null)
{
leftCamera.projectionMatrix = mainCamera.GetStereoProjectionMatrix(Camera.StereoscopicEye.Left);
rightCamera.projectionMatrix = mainCamera.GetStereoProjectionMatrix(Camera.StereoscopicEye.Right);
leftCamera.Render();
rightCamera.Render();
}
this.ModifyLayer(projectionData.layer);
this.SetActiveLayer(projectionData.layer);
}
}
XrRect2Di GetSubImageOffsetAndExtent(Rect sourceRect, Vector2 textureSize)
{
var sourceTextureOffset = new XrOffset2Di()
{
X = (int)(sourceRect.x * textureSize.x),
Y = (int)(sourceRect.y * textureSize.y)
};
var sourceTextureExtent = new XrExtent2Di()
{
Width = (int)(sourceRect.width * textureSize.x),
Height = (int)(sourceRect.height * textureSize.y)
};
return new XrRect2Di { Offset = sourceTextureOffset, Extent = sourceTextureExtent };
}
[DllImport("UnityOpenXR")]
[return: MarshalAs(UnmanagedType.U1)]
internal static extern bool ext_composition_layers_GetViewConfigurationData(out uint width, out uint height, out uint sampleCount);
[DllImport("UnityOpenXR")]
[return: MarshalAs(UnmanagedType.U1)]
internal unsafe static extern bool ext_composition_layers_GetStereoViews(XrView* view1, XrView* view2);
}
}
#endif

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f21e188e121149e695a8c8490aa086dd
timeCreated: 1681426276

View File

@@ -0,0 +1,299 @@
#if XR_COMPOSITION_LAYERS
using UnityEngine.XR.OpenXR.NativeTypes;
using Unity.XR.CompositionLayers;
using Unity.XR.CompositionLayers.Extensions;
using Unity.XR.CompositionLayers.Layers;
using Unity.XR.CompositionLayers.Services;
namespace UnityEngine.XR.OpenXR.CompositionLayers
{
//Default OpenXR Composition Layer - Quad Layer support
internal class OpenXRQuadLayer : OpenXRCustomLayerHandler<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 = OpenXRLayerUtility.GetExtensionsChain(layer, CompositionLayerExtension.ExtensionTarget.Swapchain),
CreateFlags = 0,
UsageFlags = (ulong)(XrSwapchainUsageFlags.XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XrSwapchainUsageFlags.XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT),
Format = OpenXRLayerUtility.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 = OpenXRLayerUtility.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 = OpenXRLayerUtility.GetExtensionsChain(layer, CompositionLayerExtension.ExtensionTarget.Layer),
LayerFlags = data.BlendType == BlendType.Premultiply ? XrCompositionLayerFlags.SourceAlpha : XrCompositionLayerFlags.SourceAlpha | XrCompositionLayerFlags.UnPremultipliedAlpha,
Space = OpenXRLayerUtility.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(OpenXRUtility.ComputePoseToWorldSpace(transform, CompositionLayerManager.mainCameraCache).position, OpenXRUtility.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(OpenXRUtility.ComputePoseToWorldSpace(transform, CompositionLayerManager.mainCameraCache).position, OpenXRUtility.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 = OpenXRLayerUtility.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(OpenXRUtility.ComputePoseToWorldSpace(transform, CompositionLayerManager.mainCameraCache).position, OpenXRUtility.ComputePoseToWorldSpace(transform, CompositionLayerManager.mainCameraCache).rotation);
nativeLayer.Space = OpenXRLayerUtility.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 = OpenXRUtility.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), OpenXRUtility.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,3 @@
fileFormatVersion: 2
guid: bc3d136d6dda4723899962838c66fbe9
timeCreated: 1667255803