VR4Medical/ICI/Library/PackageCache/com.unity.xr.openxr@3903c1059bcf/Runtime/CompositionLayers/OpenXRProjectionLayer.cs
2025-07-29 13:45:50 +03:00

400 lines
17 KiB
C#

#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