intial commit

This commit is contained in:
Caleb Sandford deQuincey
2025-06-27 23:27:49 +01:00
commit ecdd3e2a9e
31269 changed files with 4937644 additions and 0 deletions

View File

@@ -0,0 +1,167 @@
using UnityEngine.Rendering;
namespace UnityEngine.Experimental.Rendering
{
/// <summary>
/// Helper static class used by render pipelines to setup stereo constants accessed by builtin shaders.
/// </summary>
public static class XRBuiltinShaderConstants
{
/// <summary>
/// Cached unique id for unity_StereoCameraProjection
/// </summary>
static public readonly int unity_StereoCameraProjection = Shader.PropertyToID("unity_StereoCameraProjection");
/// <summary>
/// Cached unique id for unity_StereoCameraInvProjection
/// </summary>
static public readonly int unity_StereoCameraInvProjection = Shader.PropertyToID("unity_StereoCameraInvProjection");
/// <summary>
/// Cached unique id for unity_StereoMatrixV
/// </summary>
static public readonly int unity_StereoMatrixV = Shader.PropertyToID("unity_StereoMatrixV");
/// <summary>
/// Cached unique id for unity_StereoMatrixInvV
/// </summary>
static public readonly int unity_StereoMatrixInvV = Shader.PropertyToID("unity_StereoMatrixInvV");
/// <summary>
/// Cached unique id for unity_StereoMatrixP
/// </summary>
static public readonly int unity_StereoMatrixP = Shader.PropertyToID("unity_StereoMatrixP");
/// <summary>
/// Cached unique id for unity_StereoMatrixInvP
/// </summary>
static public readonly int unity_StereoMatrixInvP = Shader.PropertyToID("unity_StereoMatrixInvP");
/// <summary>
/// Cached unique id for unity_StereoMatrixVP
/// </summary>
static public readonly int unity_StereoMatrixVP = Shader.PropertyToID("unity_StereoMatrixVP");
/// <summary>
/// Cached unique id for unity_StereoMatrixInvVP
/// </summary>
static public readonly int unity_StereoMatrixInvVP = Shader.PropertyToID("unity_StereoMatrixInvVP");
/// <summary>
/// Cached unique id for unity_StereoWorldSpaceCameraPos
/// </summary>
static public readonly int unity_StereoWorldSpaceCameraPos = Shader.PropertyToID("unity_StereoWorldSpaceCameraPos");
// Pre-allocate arrays to avoid GC
static Matrix4x4[] s_cameraProjMatrix = new Matrix4x4[2];
static Matrix4x4[] s_invCameraProjMatrix = new Matrix4x4[2];
static Matrix4x4[] s_viewMatrix = new Matrix4x4[2];
static Matrix4x4[] s_invViewMatrix = new Matrix4x4[2];
static Matrix4x4[] s_projMatrix = new Matrix4x4[2];
static Matrix4x4[] s_invProjMatrix = new Matrix4x4[2];
static Matrix4x4[] s_viewProjMatrix = new Matrix4x4[2];
static Matrix4x4[] s_invViewProjMatrix = new Matrix4x4[2];
static Vector4[] s_worldSpaceCameraPos = new Vector4[2];
/// <summary>
/// Update the shader constant data used by the C++ builtin renderer.
/// </summary>
/// <param name="viewMatrix"> The new view matrix that XR shaders constant should update to use. </param>
/// <param name="projMatrix"> The new projection matrix that XR shaders constant should update to use. </param>
/// <param name="renderIntoTexture"> Determines the yflip state for the projection matrix. </param>
/// <param name="viewIndex"> Index of the XR shader constant to update. </param>
public static void UpdateBuiltinShaderConstants(Matrix4x4 viewMatrix, Matrix4x4 projMatrix, bool renderIntoTexture, int viewIndex)
{
#if ENABLE_VR && ENABLE_XR_MODULE
var gpuProjMatrix = GL.GetGPUProjectionMatrix(projMatrix, renderIntoTexture);
var gpuViewProjMatrix = gpuProjMatrix * viewMatrix;
s_cameraProjMatrix[viewIndex] = projMatrix;
s_projMatrix[viewIndex] = gpuProjMatrix;
s_viewMatrix[viewIndex] = viewMatrix;
Matrix4x4.Inverse3DAffine(viewMatrix, ref s_invViewMatrix[viewIndex]);
s_viewProjMatrix[viewIndex] = gpuViewProjMatrix;
s_invCameraProjMatrix[viewIndex] = Matrix4x4.Inverse(projMatrix);
s_invProjMatrix[viewIndex] = Matrix4x4.Inverse(gpuProjMatrix);
s_invViewProjMatrix[viewIndex] = Matrix4x4.Inverse(gpuViewProjMatrix);
s_worldSpaceCameraPos[viewIndex] = s_invViewMatrix[viewIndex].GetColumn(3);
#endif
}
/// <summary>
/// Bind the shader constants used by the C++ builtin renderer via a command buffer. `UpdateBuiltinShaderConstants` should be called before to update the constants.
/// This is required to maintain compatibility with legacy code and shaders.
/// </summary>
/// <param name="cmd"> Commandbuffer on which to set XR shader constants. </param>
public static void SetBuiltinShaderConstants(CommandBuffer cmd)
{
#if ENABLE_VR && ENABLE_XR_MODULE
cmd.SetGlobalMatrixArray(unity_StereoCameraProjection, s_cameraProjMatrix);
cmd.SetGlobalMatrixArray(unity_StereoCameraInvProjection, s_invCameraProjMatrix);
cmd.SetGlobalMatrixArray(unity_StereoMatrixV, s_viewMatrix);
cmd.SetGlobalMatrixArray(unity_StereoMatrixInvV, s_invViewMatrix);
cmd.SetGlobalMatrixArray(unity_StereoMatrixP, s_projMatrix);
cmd.SetGlobalMatrixArray(unity_StereoMatrixInvP, s_invProjMatrix);
cmd.SetGlobalMatrixArray(unity_StereoMatrixVP, s_viewProjMatrix);
cmd.SetGlobalMatrixArray(unity_StereoMatrixInvVP, s_invViewProjMatrix);
cmd.SetGlobalVectorArray(unity_StereoWorldSpaceCameraPos, s_worldSpaceCameraPos);
#endif
}
/// <summary>
/// Bind the shader constants used by the C++ builtin renderer via a raster command buffer. `UpdateBuiltinShaderConstants` should be called before to update the constants.
/// This is required to maintain compatibility with legacy code and shaders.
/// </summary>
/// <param name="cmd"> RasterCommandbuffer on which to set XR shader constants. </param>
public static void SetBuiltinShaderConstants(RasterCommandBuffer cmd)
{
SetBuiltinShaderConstants(cmd.m_WrappedCommandBuffer);
}
/// <summary>
/// Update and bind shader constants used by the C++ builtin renderer given the XRPass. For better control of setting up builtin shader constants, see `UpdateBuiltinShaderConstants`
/// and `SetBuiltinShaderConstants` which do the same logic but could take in custom projection and view matricies instead.
/// This is required to maintain compatibility with legacy code and shaders.
/// </summary>
/// <param name="xrPass"> The new XRPass that XR shader constants should update to use. </param>
/// <param name="cmd"> CommandBuffer on which to set XR shader constants. </param>
/// <param name="renderIntoTexture"> Determines the yflip state for the projection matrix. </param>
public static void Update(XRPass xrPass, CommandBuffer cmd, bool renderIntoTexture)
{
#if ENABLE_VR && ENABLE_XR_MODULE
if (xrPass.enabled)
{
cmd.SetViewProjectionMatrices(xrPass.GetViewMatrix(), xrPass.GetProjMatrix());
if (xrPass.singlePassEnabled)
{
for (int viewIndex = 0; viewIndex < 2; ++viewIndex)
{
s_cameraProjMatrix[viewIndex] = xrPass.GetProjMatrix(viewIndex);
s_viewMatrix[viewIndex] = xrPass.GetViewMatrix(viewIndex);
s_projMatrix[viewIndex] = GL.GetGPUProjectionMatrix(s_cameraProjMatrix[viewIndex], renderIntoTexture);
s_viewProjMatrix[viewIndex] = s_projMatrix[viewIndex] * s_viewMatrix[viewIndex];
s_invCameraProjMatrix[viewIndex] = Matrix4x4.Inverse(s_cameraProjMatrix[viewIndex]);
Matrix4x4.Inverse3DAffine(s_viewMatrix[viewIndex], ref s_invViewMatrix[viewIndex]);
s_invProjMatrix[viewIndex] = Matrix4x4.Inverse(s_projMatrix[viewIndex]);
s_invViewProjMatrix[viewIndex] = Matrix4x4.Inverse(s_viewProjMatrix[viewIndex]);
s_worldSpaceCameraPos[viewIndex] = s_invViewMatrix[viewIndex].GetColumn(3);
}
cmd.SetGlobalMatrixArray(unity_StereoCameraProjection, s_cameraProjMatrix);
cmd.SetGlobalMatrixArray(unity_StereoCameraInvProjection, s_invCameraProjMatrix);
cmd.SetGlobalMatrixArray(unity_StereoMatrixV, s_viewMatrix);
cmd.SetGlobalMatrixArray(unity_StereoMatrixInvV, s_invViewMatrix);
cmd.SetGlobalMatrixArray(unity_StereoMatrixP, s_projMatrix);
cmd.SetGlobalMatrixArray(unity_StereoMatrixInvP, s_invProjMatrix);
cmd.SetGlobalMatrixArray(unity_StereoMatrixVP, s_viewProjMatrix);
cmd.SetGlobalMatrixArray(unity_StereoMatrixInvVP, s_invViewProjMatrix);
cmd.SetGlobalVectorArray(unity_StereoWorldSpaceCameraPos, s_worldSpaceCameraPos);
}
}
#endif
}
}
}

View File

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

View File

@@ -0,0 +1,86 @@
using System;
using System.Collections.Generic;
using UnityEngine.Experimental.Rendering;
namespace UnityEngine.Rendering
{
/// <summary>
/// Utility class to connect SRP to automated test framework.
/// </summary>
public static class XRGraphicsAutomatedTests
{
// XR tests can be enabled from the command line. Cache result to avoid GC.
static bool activatedFromCommandLine
{
#if UNITY_EDITOR
get => Array.Exists(Environment.GetCommandLineArgs(), arg => arg == "-xr-reuse-tests");
#elif XR_REUSE_TESTS_STANDALONE
get => true;
#else
get => false;
#endif
}
/// <summary>
/// Used by render pipelines to initialize XR tests.
/// </summary>
public static bool enabled { get; } = activatedFromCommandLine;
/// <summary>
/// Set by automated test framework and read by render pipelines.
/// </summary>
public static bool running = false;
// Helper function to override the XR default layout using settings of new camera
internal static void OverrideLayout(XRLayout layout, Camera camera)
{
#if ENABLE_VR && ENABLE_XR_MODULE
if (enabled && running)
{
var camProjMatrix = camera.projectionMatrix;
var camViewMatrix = camera.worldToCameraMatrix;
if (camera.TryGetCullingParameters(false, out var cullingParams))
{
cullingParams.stereoProjectionMatrix = camProjMatrix;
cullingParams.stereoViewMatrix = camViewMatrix;
cullingParams.stereoSeparationDistance = 0.0f;
List<(Camera, XRPass)> xrPasses = layout.GetActivePasses();
for (int passId = 0; passId < xrPasses.Count; passId++)
{
var xrPass = xrPasses[passId].Item2;
xrPass.AssignCullingParams(xrPass.cullingPassId, cullingParams);
for (int viewId = 0; viewId < xrPass.viewCount; viewId++)
{
var projMatrix = camProjMatrix;
var viewMatrix = camViewMatrix;
bool isFirstViewMultiPass = xrPasses.Count == 2 && passId == 0;
bool isFirstViewSinglePass = xrPasses.Count == 1 && viewId == 0;
if (isFirstViewMultiPass || isFirstViewSinglePass)
{
// Modify the render viewpoint and frustum of the first view in order to
// distinguish it from the final view used for image comparison.
// This is a trick to help detect issues related to view indexing.
var planes = projMatrix.decomposeProjection;
planes.left *= 0.44f;
planes.right *= 0.88f;
planes.top *= 0.11f;
planes.bottom *= 0.33f;
projMatrix = Matrix4x4.Frustum(planes);
viewMatrix *= Matrix4x4.Translate(new Vector3(.34f, 0.25f, -0.08f));
}
XRView xrView = new XRView(projMatrix, viewMatrix, Matrix4x4.identity, false, xrPass.GetViewport(viewId), null, xrPass.GetTextureArraySlice(viewId));
xrPass.AssignView(viewId, xrView);
}
}
}
}
#endif
}
}
}

View File

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

View File

@@ -0,0 +1,107 @@
using System.Collections.Generic;
using System.Text;
namespace UnityEngine.Experimental.Rendering
{
/// <summary>
/// Used by render pipelines to store information about the XR device layout.
/// </summary>
public class XRLayout
{
readonly List<(Camera, XRPass)> m_ActivePasses = new List<(Camera, XRPass)>();
/// <summary>
/// Configure the layout to render from the specified camera by generating passes from the the connected XR device.
/// </summary>
/// <param name="camera"> Camera that has XR device connected to. </param>
/// <param name="enableXR"> Determines XR capability of the generated layout. Can be used to force generate non-XR layout. </param>
public void AddCamera(Camera camera, bool enableXR)
{
if (camera == null)
return;
// Enable XR layout only for game camera
bool isGameCamera = (camera.cameraType == CameraType.Game || camera.cameraType == CameraType.VR);
bool xrSupported = isGameCamera && camera.targetTexture == null && enableXR;
if (XRSystem.displayActive && xrSupported)
{
XRSystem.SetDisplayZRange(camera.nearClipPlane, camera.farClipPlane);
XRSystem.CreateDefaultLayout(camera, this);
}
else
{
AddPass(camera, XRSystem.emptyPass);
}
}
/// <summary>
/// Used by render pipelines to reconfigure a pass from a camera.
/// </summary>
/// <param name="xrPass"> XRPass that needs to be reconfigured. </param>
/// <param name="camera"> The camera that XRPass configures against. </param>
public void ReconfigurePass(XRPass xrPass, Camera camera)
{
if (xrPass.enabled)
{
XRSystem.ReconfigurePass(xrPass, camera);
xrPass.UpdateCombinedOcclusionMesh();
}
}
/// <summary>
/// Used by render pipelines to access all registered passes on this layout.
/// </summary>
/// <returns> A list of registered camera/XRPass tuples. </returns>
public List<(Camera, XRPass)> GetActivePasses()
{
return m_ActivePasses;
}
internal void AddPass(Camera camera, XRPass xrPass)
{
xrPass.UpdateCombinedOcclusionMesh();
m_ActivePasses.Add((camera, xrPass));
}
internal void Clear()
{
for (int i = 0; i < m_ActivePasses.Count; i++)
{
// Pop from the back to keep initial ordering (see implementation of ObjectPool)
(Camera _, XRPass xrPass) = m_ActivePasses[m_ActivePasses.Count - i - 1];
if (xrPass != XRSystem.emptyPass)
xrPass.Release();
}
m_ActivePasses.Clear();
}
internal void LogDebugInfo()
{
var sb = new StringBuilder();
sb.AppendFormat("XRSystem setup for frame {0}, active: {1}", Time.frameCount, XRSystem.displayActive);
sb.AppendLine();
for (int passIndex = 0; passIndex < m_ActivePasses.Count; passIndex++)
{
var pass = m_ActivePasses[passIndex].Item2;
for (int viewIndex = 0; viewIndex < pass.viewCount; viewIndex++)
{
var viewport = pass.GetViewport(viewIndex);
sb.AppendFormat("XR Pass {0} Cull {1} View {2} Slice {3} : {4} x {5}",
pass.multipassId,
pass.cullingPassId,
viewIndex,
pass.GetTextureArraySlice(viewIndex),
viewport.width,
viewport.height);
sb.AppendLine();
}
}
Debug.Log(sb);
}
}
}

View File

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

View File

@@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using UnityEngine.Pool;
namespace UnityEngine.Experimental.Rendering
{
internal class XRLayoutStack : IDisposable
{
readonly Stack<XRLayout> m_Stack = new ();
public XRLayout New()
{
GenericPool<XRLayout>.Get(out var layout);
m_Stack.Push(layout);
return layout;
}
public XRLayout top => m_Stack.Peek();
public void Release()
{
if (!m_Stack.TryPop(out var value))
throw new InvalidOperationException($"Calling {nameof(Release)} without calling {nameof(New)} first.");
value.Clear();
GenericPool<XRLayout>.Release(value);
}
public void Dispose()
{
if (m_Stack.Count != 0)
throw new Exception($"Stack is not empty. Did you skip a call to {nameof(Release)}?");
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d3562354edd44e76aa9d77b720395854
timeCreated: 1714053686

View File

@@ -0,0 +1,149 @@
using System;
using Unity.Mathematics;
using UnityEngine.Rendering;
#if ENABLE_VR && ENABLE_XR_MODULE
using UnityEngine.XR;
#endif
namespace UnityEngine.Experimental.Rendering
{
internal static class XRMirrorView
{
static readonly MaterialPropertyBlock s_MirrorViewMaterialProperty = new MaterialPropertyBlock();
static readonly ProfilingSampler k_MirrorViewProfilingSampler = new ProfilingSampler("XR Mirror View");
static readonly int k_SourceTex = Shader.PropertyToID("_SourceTex");
static readonly int k_SourceTexArraySlice = Shader.PropertyToID("_SourceTexArraySlice");
static readonly int k_ScaleBias = Shader.PropertyToID("_ScaleBias");
static readonly int k_ScaleBiasRt = Shader.PropertyToID("_ScaleBiasRt");
static readonly int k_SRGBRead = Shader.PropertyToID("_SRGBRead");
static readonly int k_SRGBWrite = Shader.PropertyToID("_SRGBWrite");
static readonly int k_MaxNits = Shader.PropertyToID("_MaxNits");
static readonly int k_SourceMaxNits = Shader.PropertyToID("_SourceMaxNits");
static readonly int k_SourceHDREncoding = Shader.PropertyToID("_SourceHDREncoding");
static readonly int k_ColorTransform = Shader.PropertyToID("_ColorTransform");
#if ENABLE_VR && ENABLE_XR_MODULE
internal static void RenderMirrorView(CommandBuffer cmd, Camera camera, Material mat, XRDisplaySubsystem display)
{
// XRTODO : remove this check when the Quest plugin is fixed
if (Application.platform == RuntimePlatform.Android && !XRGraphicsAutomatedTests.running)
return;
if (display == null || !display.running || mat == null)
return;
int mirrorBlitMode = display.GetPreferredMirrorBlitMode();
if (display.GetMirrorViewBlitDesc(null, out var blitDesc, mirrorBlitMode))
{
using (new ProfilingScope(cmd, k_MirrorViewProfilingSampler))
{
cmd.SetRenderTarget(camera.targetTexture != null ? camera.targetTexture : new RenderTargetIdentifier(BuiltinRenderTextureType.CameraTarget));
if (blitDesc.nativeBlitAvailable)
{
display.AddGraphicsThreadMirrorViewBlit(cmd, blitDesc.nativeBlitInvalidStates, mirrorBlitMode);
}
else
{
for (int i = 0; i < blitDesc.blitParamsCount; ++i)
{
blitDesc.GetBlitParameter(i, out var blitParam);
Vector4 scaleBias = new Vector4(blitParam.srcRect.width, blitParam.srcRect.height, blitParam.srcRect.x, blitParam.srcRect.y);
Vector4 scaleBiasRt = new Vector4(blitParam.destRect.width, blitParam.destRect.height, blitParam.destRect.x, blitParam.destRect.y);
// Deal with y-flip
if (camera.targetTexture != null || camera.cameraType == CameraType.SceneView || camera.cameraType == CameraType.Preview)
{
scaleBias.y = -scaleBias.y;
scaleBias.w += blitParam.srcRect.height;
}
HDROutputSettings mainDisplayHdrSettings = HDROutputSettings.main;
// If we are writing to a HDR surface or reading from one we use the conversion shader to handle both
if (blitParam.srcHdrEncoded || mainDisplayHdrSettings.active)
{
ColorGamut mainDisplayColorGamut = mainDisplayHdrSettings.active ? mainDisplayHdrSettings.displayColorGamut
: ColorGamut.sRGB;
ColorGamut xrDisplayColorGamut = blitParam.srcHdrEncoded ? blitParam.srcHdrColorGamut
: ColorGamut.sRGB;
ColorPrimaries mainDisplayColorPrimaries = ColorGamutUtility.GetColorPrimaries(mainDisplayColorGamut);
ColorPrimaries xrDisplayColorPrimaries = ColorGamutUtility.GetColorPrimaries(xrDisplayColorGamut);
// Use the material? And use the passes?
HDROutputUtils.ConfigureHDROutput(s_MirrorViewMaterialProperty, mainDisplayColorGamut);
HDROutputUtils.ConfigureHDROutput(mat, HDROutputUtils.Operation.ColorConversion | HDROutputUtils.Operation.ColorEncoding);
int sourceHdrEncoding;
HDROutputUtils.GetColorEncodingForGamut(xrDisplayColorGamut, out sourceHdrEncoding);
s_MirrorViewMaterialProperty.SetInteger(k_SourceHDREncoding, sourceHdrEncoding);
float3x3 sourceToRec2020 = float3x3.identity;
if (xrDisplayColorPrimaries == ColorPrimaries.Rec709)
sourceToRec2020 = ColorSpaceUtils.Rec709ToRec2020Mat;
else if (xrDisplayColorPrimaries == ColorPrimaries.P3)
sourceToRec2020 = ColorSpaceUtils.P3D65ToRec2020Mat;
float3x3 rec2020ToDest = float3x3.identity;
if (mainDisplayColorPrimaries == ColorPrimaries.Rec709)
rec2020ToDest = ColorSpaceUtils.Rec2020ToRec709Mat;
else if (mainDisplayColorPrimaries == ColorPrimaries.P3)
rec2020ToDest = ColorSpaceUtils.Rec2020ToP3D65Mat;
// Quicker to go straight to a Matrix4x4 and multiply there? Or to store these as Matrix4x4 instead due to Unity missing a 3x3 type?
float3x3 colorTransform = math.mul(sourceToRec2020, rec2020ToDest);
Matrix4x4 m = new Matrix4x4((Vector4) new float4(colorTransform.c0, 0.0f),
(Vector4) new float4(colorTransform.c1, 0.0f), (Vector4) new float4(colorTransform.c2, 0.0f),
new Vector4(0.0f, 0.0f, 0.0f, 0.0f));
s_MirrorViewMaterialProperty.SetMatrix(k_ColorTransform, m);
s_MirrorViewMaterialProperty.SetFloat(k_MaxNits, mainDisplayHdrSettings.active ? mainDisplayHdrSettings.maxToneMapLuminance : 160.0f);
s_MirrorViewMaterialProperty.SetFloat(k_SourceMaxNits, blitParam.srcHdrEncoded ? blitParam.srcHdrMaxLuminance : 160.0f);
}
// For 8888 formats we always gamma correct eye textures : use explicit sRGB read in shader only if the source is not using sRGB format.
bool manualSRGBRead = !blitParam.srcTex.sRGB &&
(blitParam.srcTex.graphicsFormat == GraphicsFormat.R8G8B8A8_UNorm ||
blitParam.srcTex.graphicsFormat == GraphicsFormat.B8G8R8A8_UNorm);
s_MirrorViewMaterialProperty.SetFloat(k_SRGBRead, manualSRGBRead ? 1.0f : 0.0f);
// Perform explicit sRGB write in shader if color space is gamma
s_MirrorViewMaterialProperty.SetFloat(k_SRGBWrite, (QualitySettings.activeColorSpace == ColorSpace.Linear) ? 0.0f : 1.0f);
s_MirrorViewMaterialProperty.SetTexture(k_SourceTex, blitParam.srcTex);
s_MirrorViewMaterialProperty.SetVector(k_ScaleBias, scaleBias);
s_MirrorViewMaterialProperty.SetVector(k_ScaleBiasRt, scaleBiasRt);
s_MirrorViewMaterialProperty.SetFloat(k_SourceTexArraySlice, blitParam.srcTexArraySlice);
if (XRSystem.foveatedRenderingCaps.HasFlag(FoveatedRenderingCaps.NonUniformRaster) && blitParam.foveatedRenderingInfo != IntPtr.Zero)
{
cmd.ConfigureFoveatedRendering(blitParam.foveatedRenderingInfo);
cmd.EnableShaderKeyword("_FOVEATED_RENDERING_NON_UNIFORM_RASTER");
}
if (blitParam.srcTex.dimension != TextureDimension.Tex2DArray)
cmd.EnableShaderKeyword("DISABLE_TEXTURE2D_X_ARRAY");
cmd.DrawProcedural(Matrix4x4.identity, mat, 0, MeshTopology.Quads, 4, 1, s_MirrorViewMaterialProperty);
// Set back the XR texture for regular XR calls
if (blitParam.srcTex.dimension != TextureDimension.Tex2DArray && TextureXR.useTexArray)
cmd.DisableShaderKeyword("DISABLE_TEXTURE2D_X_ARRAY");
}
}
}
}
if (XRSystem.foveatedRenderingCaps.HasFlag(FoveatedRenderingCaps.NonUniformRaster))
{
cmd.DisableShaderKeyword("_FOVEATED_RENDERING_NON_UNIFORM_RASTER");
cmd.ConfigureFoveatedRendering(IntPtr.Zero);
}
}
#endif
}
}

View File

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

View File

@@ -0,0 +1,186 @@
using UnityEngine.Rendering;
namespace UnityEngine.Experimental.Rendering
{
// Helper class to render occlusion meshes.
// If possible, the mesh for each view will be combined into one mesh to reduce draw calls.
internal class XROcclusionMesh
{
XRPass m_Pass;
Mesh m_CombinedMesh;
Material m_Material;
int m_CombinedMeshHashCode;
static readonly ProfilingSampler k_OcclusionMeshProfilingSampler = new ProfilingSampler("XR Occlusion Mesh");
internal XROcclusionMesh(XRPass xrPass)
{
m_Pass = xrPass;
}
internal void SetMaterial(Material mat)
{
m_Material = mat;
}
internal bool hasValidOcclusionMesh
{
get
{
if (IsOcclusionMeshSupported())
{
if (m_Pass.singlePassEnabled)
return m_CombinedMesh != null;
else
return m_Pass.GetOcclusionMesh() != null;
}
return false;
}
}
internal void RenderOcclusionMesh(CommandBuffer cmd, float occlusionMeshScale, bool yFlip = false)
{
if (IsOcclusionMeshSupported())
{
using (new ProfilingScope(cmd, k_OcclusionMeshProfilingSampler))
{
if (m_Pass.singlePassEnabled)
{
// Prefer multiview draw
if (m_CombinedMesh != null && SystemInfo.supportsMultiview)
{
// For the multiview code path, keep the multiview state on to propagate geometries to all eye texture slices
cmd.EnableShaderKeyword("XR_OCCLUSION_MESH_COMBINED");
Vector3 scale = new Vector3(occlusionMeshScale, yFlip? occlusionMeshScale : -occlusionMeshScale, 1.0f);
cmd.DrawMesh(m_CombinedMesh, Matrix4x4.Scale(scale), m_Material);
cmd.DisableShaderKeyword("XR_OCCLUSION_MESH_COMBINED");
}
else if (m_CombinedMesh != null && SystemInfo.supportsRenderTargetArrayIndexFromVertexShader)
{
m_Pass.StopSinglePass(cmd);
cmd.EnableShaderKeyword("XR_OCCLUSION_MESH_COMBINED");
Vector3 scale = new Vector3(occlusionMeshScale, yFlip ? occlusionMeshScale : -occlusionMeshScale, 1.0f);
cmd.DrawMesh(m_CombinedMesh, Matrix4x4.Scale(scale), m_Material);
cmd.DisableShaderKeyword("XR_OCCLUSION_MESH_COMBINED");
m_Pass.StartSinglePass(cmd);
}
}
else
{
Mesh mesh = m_Pass.GetOcclusionMesh(0);
if (mesh != null)
{
cmd.DrawMesh(mesh, Matrix4x4.identity, m_Material);
}
}
}
}
}
internal void UpdateCombinedMesh()
{
if (IsOcclusionMeshSupported() && m_Pass.singlePassEnabled && TryGetOcclusionMeshCombinedHashCode(out var hashCode))
{
if (m_CombinedMesh == null || hashCode != m_CombinedMeshHashCode)
{
CreateOcclusionMeshCombined();
m_CombinedMeshHashCode = hashCode;
}
}
else
{
m_CombinedMesh = null;
m_CombinedMeshHashCode = 0;
}
}
bool IsOcclusionMeshSupported()
{
return m_Pass.enabled && m_Material != null;
}
bool TryGetOcclusionMeshCombinedHashCode(out int hashCode)
{
hashCode = 17;
for (int viewId = 0; viewId < m_Pass.viewCount; ++viewId)
{
Mesh mesh = m_Pass.GetOcclusionMesh(viewId);
if (mesh != null)
{
hashCode = hashCode * 23 + mesh.GetHashCode();
}
else
{
hashCode = 0;
return false;
}
}
return true;
}
// Create a new mesh that contains the occlusion data from all views
void CreateOcclusionMeshCombined()
{
CoreUtils.Destroy(m_CombinedMesh);
m_CombinedMesh = new Mesh();
m_CombinedMesh.indexFormat = IndexFormat.UInt16;
int combinedVertexCount = 0;
uint combinedIndexCount = 0;
for (int viewId = 0; viewId < m_Pass.viewCount; ++viewId)
{
Mesh mesh = m_Pass.GetOcclusionMesh(viewId);
Debug.Assert(mesh != null);
Debug.Assert(mesh.subMeshCount == 1);
Debug.Assert(mesh.indexFormat == IndexFormat.UInt16);
combinedVertexCount += mesh.vertexCount;
combinedIndexCount += mesh.GetIndexCount(0);
}
Vector3[] vertices = new Vector3[combinedVertexCount];
ushort[] indices = new ushort[combinedIndexCount];
int vertexStart = 0;
int indexStart = 0;
for (int viewId = 0; viewId < m_Pass.viewCount; ++viewId)
{
Mesh mesh = m_Pass.GetOcclusionMesh(viewId);
var meshIndices = mesh.GetIndices(0);
// Encore the viewId into the z channel
{
mesh.vertices.CopyTo(vertices, vertexStart);
for (int i = 0; i < mesh.vertices.Length; i++)
vertices[vertexStart + i].z = viewId;
}
// Combine indices into one buffer
for (int i = 0; i < meshIndices.Length; i++)
{
int newIndex = vertexStart + meshIndices[i];
Debug.Assert(meshIndices[i] < ushort.MaxValue);
indices[indexStart + i] = (ushort)newIndex;
}
vertexStart += mesh.vertexCount;
indexStart += meshIndices.Length;
}
m_CombinedMesh.vertices = vertices;
m_CombinedMesh.SetIndices(indices, MeshTopology.Triangles, 0);
}
}
}

View File

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

View File

@@ -0,0 +1,486 @@
using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine.Rendering;
namespace UnityEngine.Experimental.Rendering
{
/// <summary>
/// Set of data used to create a XRPass object.
/// </summary>
public struct XRPassCreateInfo
{
internal RenderTargetIdentifier renderTarget;
internal RenderTextureDescriptor renderTargetDesc;
internal RenderTargetIdentifier motionVectorRenderTarget;
internal RenderTextureDescriptor motionVectorRenderTargetDesc;
internal ScriptableCullingParameters cullingParameters;
internal Material occlusionMeshMaterial;
internal float occlusionMeshScale;
internal IntPtr foveatedRenderingInfo;
internal int multipassId;
internal int cullingPassId;
internal bool copyDepth;
internal bool hasMotionVectorPass;
#if ENABLE_VR && ENABLE_XR_MODULE
internal UnityEngine.XR.XRDisplaySubsystem.XRRenderPass xrSdkRenderPass;
#endif
}
/// <summary>
/// XRPass holds the render target information and a list of XRView.
/// XRView contains the parameters required to render (projection and view matrices, viewport, etc)
/// When a pass has 2 views or more, single-pass will be active if the platform supports it.
/// To avoid allocating every frame, XRView is a struct and XRPass is pooled.
/// </summary>
public class XRPass
{
readonly List<XRView> m_Views;
readonly XROcclusionMesh m_OcclusionMesh;
/// <summary>
/// Parameterless constructor.
/// Note: in order to avoid GC, the render pipeline should use XRPass.Create instead of this method.
/// </summary>
public XRPass()
{
m_Views = new List<XRView>(2);
m_OcclusionMesh = new XROcclusionMesh(this);
}
/// <summary>
/// Default allocator method for XRPass.
/// </summary>
/// <param name="createInfo"> A descriptor used to create and initialize the XRPass. </param>
/// <returns> Default XRPass created from createInfo descriptor. </returns>
public static XRPass CreateDefault(XRPassCreateInfo createInfo)
{
XRPass pass = GenericPool<XRPass>.Get();
pass.InitBase(createInfo);
return pass;
}
/// <summary>
/// Default release method. Can be overridden by render pipelines.
/// </summary>
virtual public void Release()
{
GenericPool<XRPass>.Release(this);
}
/// <summary>
/// Returns true if the pass contains at least one view.
/// </summary>
public bool enabled
{
#if ENABLE_VR && ENABLE_XR_MODULE
get => viewCount > 0;
#else
get => false;
#endif
}
/// <summary>
/// Returns true if the pass can use foveated rendering commands.
/// </summary>
public bool supportsFoveatedRendering
{
#if ENABLE_VR && ENABLE_XR_MODULE
get => enabled && foveatedRenderingInfo != IntPtr.Zero && XRSystem.foveatedRenderingCaps != FoveatedRenderingCaps.None;
#else
get => false;
#endif
}
/// <summary>
/// If true, the render pipeline is expected to output a valid depth buffer to the renderTarget.
/// </summary>
public bool copyDepth { get; private set; }
/// <summary>
/// If true, the render pipeline is expected to generate motion data and output to the motionVectorRenderTarget.
/// </summary>
public bool hasMotionVectorPass { get; private set; }
/// <summary>
/// If true, is the first pass of a xr camera
/// </summary>
public bool isFirstCameraPass => multipassId == 0;
/// <summary>
/// If true, is the last pass of a xr camera
/// Multipass last pass: pass ID == 1, viewCount == 1
/// Singlepass last pass: pass ID == 0, viewCount ==2
/// Emptypass(non-XR) last pass: pass ID == 0, viewCount == 0
/// </summary>
public bool isLastCameraPass => (multipassId == 1 && viewCount <= 1) || (multipassId == 0 && viewCount > 1) || (multipassId == 0 && viewCount == 0) /* ViewCount 0 handles the empty pass*/;
/// <summary>
/// Index of the pass inside the frame.
/// </summary>
public int multipassId { get; private set; }
/// <summary>
/// Index used for culling. It can be shared between multiple passes.
/// </summary>
public int cullingPassId { get; private set; }
/// <summary>
/// Destination render target.
/// </summary>
public RenderTargetIdentifier renderTarget { get; private set; }
/// <summary>
/// Destination render target descriptor.
/// </summary>
public RenderTextureDescriptor renderTargetDesc { get; private set; }
/// <summary>
/// Destination render target for motion vectors
/// </summary>
public RenderTargetIdentifier motionVectorRenderTarget { get; private set; }
/// <summary>
/// Destination render target descriptor for motion vectors.
/// </summary>
public RenderTextureDescriptor motionVectorRenderTargetDesc { get; private set; }
/// <summary>
/// Parameters used for culling.
/// </summary>
public ScriptableCullingParameters cullingParams { get; private set; }
/// <summary>
/// Returns the number of views inside this pass.
/// </summary>
public int viewCount { get => m_Views.Count; }
/// <summary>
/// If true, the render pipeline is expected to use single-pass techniques to save CPU time.
/// </summary>
public bool singlePassEnabled { get => viewCount > 1; }
/// <summary>
/// Native pointer from the XR plugin to be consumed by ConfigureFoveatedRendering.
/// </summary>
public IntPtr foveatedRenderingInfo { get; private set; }
/// <summary>
/// Returns true when the active display has HDR enabled.
/// </summary>
public bool isHDRDisplayOutputActive
{
#if ENABLE_VR && ENABLE_XR_MODULE
get => XRSystem.GetActiveDisplay().hdrOutputSettings?.active ?? false;
#else
get => false;
#endif
}
/// <summary>
/// Returns color gamut of the active HDR display.
/// </summary>
public ColorGamut hdrDisplayOutputColorGamut
{
#if ENABLE_VR && ENABLE_XR_MODULE
get => XRSystem.GetActiveDisplay().hdrOutputSettings?.displayColorGamut ?? ColorGamut.sRGB;
#else
get => ColorGamut.sRGB;
#endif
}
/// <summary>
/// Returns HDR display information of the active HDR display.
/// </summary>
public HDROutputUtils.HDRDisplayInformation hdrDisplayOutputInformation
{
#if ENABLE_VR && ENABLE_XR_MODULE
get => new HDROutputUtils.HDRDisplayInformation(
XRSystem.GetActiveDisplay().hdrOutputSettings?.maxFullFrameToneMapLuminance ?? -1,
XRSystem.GetActiveDisplay().hdrOutputSettings?.maxToneMapLuminance ?? -1,
XRSystem.GetActiveDisplay().hdrOutputSettings?.minToneMapLuminance ?? -1,
XRSystem.GetActiveDisplay().hdrOutputSettings?.paperWhiteNits ?? 160.0f
);
#else
get => new HDROutputUtils.HDRDisplayInformation(-1, -1, -1, 160.0f);
#endif
}
/// <summary>
/// Scaling factor used when drawing the occlusion mesh.
/// </summary>
public float occlusionMeshScale { get; private set; }
/// <summary>
/// Returns the projection matrix for a given view.
/// </summary>
/// <param name="viewIndex"> Index of XRView to retrieve the data from. </param>
/// <returns> XR projection matrix for the specified XRView. </returns>
public Matrix4x4 GetProjMatrix(int viewIndex = 0)
{
return m_Views[viewIndex].projMatrix;
}
/// <summary>
/// Returns the view matrix for a given view.
/// </summary>
/// <param name="viewIndex"> Index of XRView to retrieve the data from. </param>
/// <returns> XR view matrix for the specified XRView. </returns>
public Matrix4x4 GetViewMatrix(int viewIndex = 0)
{
return m_Views[viewIndex].viewMatrix;
}
/// <summary>
/// Returns true if the previous frame view matrix for a given view is valid.
/// </summary>
/// <param name="viewIndex"> Index of XRView to retrieve the data from. </param>
/// <returns> Boolean describing if previous frame view matrix for a given view is valid. </returns>
public bool GetPrevViewValid(int viewIndex = 0)
{
return m_Views[viewIndex].isPrevViewMatrixValid;
}
/// <summary>
/// Returns the previous frame view matrix for a given view.
/// </summary>
/// <param name="viewIndex"> Index of XRView to retrieve the data from. </param>
/// <returns> Previous frame XR view matrix for the specified XRView. </returns>
public Matrix4x4 GetPrevViewMatrix(int viewIndex = 0)
{
return m_Views[viewIndex].prevViewMatrix;
}
/// <summary>
/// Returns the viewport for a given view.
/// </summary>
/// <param name="viewIndex"> Index of XRView to retrieve the data from. </param>
/// <returns> XR viewport rect for the specified XRView. </returns>
public Rect GetViewport(int viewIndex = 0)
{
return m_Views[viewIndex].viewport;
}
/// <summary>
/// Returns the occlusion mesh for a given view.
/// </summary>
/// <param name="viewIndex"> Index of XRView to retrieve the data from. </param>
/// <returns> XR occlusion mesh for the specified XRView. </returns>
public Mesh GetOcclusionMesh(int viewIndex = 0)
{
return m_Views[viewIndex].occlusionMesh;
}
/// <summary>
/// Returns the destination slice index (for texture array) for a given view.
/// </summary>
/// <param name="viewIndex"> Index of XRView to retrieve the data from. </param>
/// <returns> XR target slice index for the specified XRView. </returns>
public int GetTextureArraySlice(int viewIndex = 0)
{
return m_Views[viewIndex].textureArraySlice;
}
/// <summary>
/// Queue up render commands to enable single-pass techniques.
/// Note: depending on the platform and settings, either single-pass instancing or the multiview extension will be used.
/// </summary>
/// <param name="cmd">CommandBuffer to modify</param>
public void StartSinglePass(CommandBuffer cmd)
{
if (enabled)
{
if (singlePassEnabled)
{
if (viewCount <= TextureXR.slices)
{
if (SystemInfo.supportsMultiview)
{
cmd.EnableKeyword(SinglepassKeywords.STEREO_MULTIVIEW_ON);
}
else
{
cmd.EnableKeyword(SinglepassKeywords.STEREO_INSTANCING_ON);
cmd.SetInstanceMultiplier((uint)viewCount);
}
}
else
{
throw new NotImplementedException($"Invalid XR setup for single-pass, trying to render too many views! Max supported: {TextureXR.slices}");
}
}
}
}
/// <summary>
/// Queue up render commands to disable single-pass techniques.
/// </summary>
/// <param name="cmd">IRasterCommandBuffer compatible command buffer to modify (This can be a RasterCommandBuffer or an UnsafeCommandBuffer)</param>
public void StartSinglePass(IRasterCommandBuffer cmd)
{
StartSinglePass((cmd as BaseCommandBuffer).m_WrappedCommandBuffer);
}
/// <summary>
/// Queue up render commands to disable single-pass techniques.
/// </summary>
/// <param name="cmd">CommandBuffer to modify.</param>
public void StopSinglePass(CommandBuffer cmd)
{
if (enabled)
{
if (singlePassEnabled)
{
if (SystemInfo.supportsMultiview)
{
cmd.DisableKeyword(SinglepassKeywords.STEREO_MULTIVIEW_ON);
}
else
{
cmd.DisableKeyword(SinglepassKeywords.STEREO_INSTANCING_ON);
cmd.SetInstanceMultiplier(1);
}
}
}
}
/// <summary>
/// Queue up render commands to disable single-pass techniques.
/// </summary>
/// <param name="cmd">BaseCommandBuffer to modify</param>
public void StopSinglePass(BaseCommandBuffer cmd)
{
StopSinglePass(cmd.m_WrappedCommandBuffer);
}
/// <summary>
/// Returns true if the pass was setup with expected mesh and material.
/// </summary>
public bool hasValidOcclusionMesh { get => m_OcclusionMesh.hasValidOcclusionMesh; }
/// <summary>
/// Generate commands to render the occlusion mesh for this pass.
/// In single-pass mode : the meshes for all views are combined into one mesh,
/// where the corresponding view index is encoded into each vertex. The keyword
/// "XR_OCCLUSION_MESH_COMBINED" is also enabled when rendering the combined mesh.
/// </summary>
/// <param name="cmd">CommandBuffer to modify</param>
/// <param name="renderIntoTexture">Set to true when rendering into a render texture. Used for handling Unity yflip.</param>
public void RenderOcclusionMesh(CommandBuffer cmd, bool renderIntoTexture = false)
{
if(occlusionMeshScale > 0)
m_OcclusionMesh.RenderOcclusionMesh(cmd, occlusionMeshScale, renderIntoTexture);
}
/// <summary>
/// Generate commands to render the occlusion mesh for this pass.
/// In single-pass mode : the meshes for all views are combined into one mesh,
/// where the corresponding view index is encoded into each vertex. The keyword
/// "XR_OCCLUSION_MESH_COMBINED" is also enabled when rendering the combined mesh.
/// </summary>
/// <param name="cmd">RasterCommandBuffer to modify</param>
/// <param name="renderIntoTexture">Set to true when rendering into a render texture. Used for handling Unity yflip.</param>
public void RenderOcclusionMesh(RasterCommandBuffer cmd, bool renderIntoTexture = false)
{
if (occlusionMeshScale > 0)
m_OcclusionMesh.RenderOcclusionMesh(cmd.m_WrappedCommandBuffer, occlusionMeshScale, renderIntoTexture);
}
/// <summary>
/// Draw debug line for all XR views.
/// </summary>
public void RenderDebugXRViewsFrustum()
{
for(int i = 0; i < m_Views.Count; i++)
{
const float k_DebugVeiwsFrustumDepthZ = 10.0f;
var view = m_Views[i];
var corners = CoreUtils.CalculateViewSpaceCorners(view.projMatrix, k_DebugVeiwsFrustumDepthZ);
// Get world space camera pos
Vector3 worldSpaceCameraPos = -(view.viewMatrix).GetColumn(3);
for(int j = 0; j < 4; j++)
Debug.DrawLine(worldSpaceCameraPos, view.viewMatrix.MultiplyPoint(corners[j]), i == 0 ? Color.green : Color.red);
}
}
/// <summary>
/// Take a point that is center-relative (0.5, 0.5) and modify it to be placed relative to the view's center instead, respecting the asymmetric FOV (if it is used)
/// </summary>
/// <param name="center"> Center relative point for symmetric FOV. </param>
/// <returns> View center relative points. First view center is stored in x,y components and second view center is stored in z,w components. </returns>
public Vector4 ApplyXRViewCenterOffset(Vector2 center)
{
Vector4 result = Vector4.zero;
float centerDeltaX = 0.5f - center.x;
float centerDeltaY = 0.5f - center.y;
result.x = m_Views[0].eyeCenterUV.x - centerDeltaX;
result.y = m_Views[0].eyeCenterUV.y - centerDeltaY;
if (singlePassEnabled)
{
// With single-pass XR, we need to add the data for the 2nd view
result.z = m_Views[1].eyeCenterUV.x - centerDeltaX;
result.w = m_Views[1].eyeCenterUV.y - centerDeltaY;
}
return result;
}
internal void AssignView(int viewId, XRView xrView)
{
if (viewId < 0 || viewId >= m_Views.Count)
throw new ArgumentOutOfRangeException(nameof(viewId));
m_Views[viewId] = xrView;
}
internal void AssignCullingParams(int cullingPassId, ScriptableCullingParameters cullingParams)
{
// Disable legacy stereo culling path
cullingParams.cullingOptions &= ~CullingOptions.Stereo;
this.cullingPassId = cullingPassId;
this.cullingParams = cullingParams;
}
internal void UpdateCombinedOcclusionMesh()
{
m_OcclusionMesh.UpdateCombinedMesh();
}
/// <summary>
/// Initialize the base class fields.
/// </summary>
/// <param name="createInfo"> A descriptor used to create and initialize the XRPass. </param>
public void InitBase(XRPassCreateInfo createInfo)
{
m_Views.Clear();
copyDepth = createInfo.copyDepth;
multipassId = createInfo.multipassId;
AssignCullingParams(createInfo.cullingPassId, createInfo.cullingParameters);
renderTarget = new RenderTargetIdentifier(createInfo.renderTarget, 0, CubemapFace.Unknown, -1);
renderTargetDesc = createInfo.renderTargetDesc;
motionVectorRenderTarget = new RenderTargetIdentifier(createInfo.motionVectorRenderTarget, 0, CubemapFace.Unknown, -1);
motionVectorRenderTargetDesc = createInfo.motionVectorRenderTargetDesc;
hasMotionVectorPass = createInfo.hasMotionVectorPass;
m_OcclusionMesh.SetMaterial(createInfo.occlusionMeshMaterial);
occlusionMeshScale = createInfo.occlusionMeshScale;
foveatedRenderingInfo = createInfo.foveatedRenderingInfo;
}
internal void AddView(XRView xrView)
{
if (m_Views.Count < TextureXR.slices)
{
m_Views.Add(xrView);
}
else
{
throw new NotImplementedException($"Invalid XR setup for single-pass, trying to add too many views! Max supported: {TextureXR.slices}");
}
}
}
}

View File

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

View File

@@ -0,0 +1,180 @@
using System;
using UnityEditor;
using UnityEngine.Experimental.Rendering;
#if ENABLE_VR && ENABLE_VR_MODULE
using UnityEngine.XR;
#endif
namespace UnityEngine.Rendering
{
/// <summary>
/// XRGraphics insulates SRP from API changes across platforms, Editor versions, and as XR transitions into XR SDK
/// </summary>
[Serializable]
public class XRSRPSettings
{
/// <summary>
/// Try enable.
/// </summary>
#if UNITY_EDITOR
// TryEnable gets updated before "play" is pressed- we use this for updating GUI only.
public static bool tryEnable
{
get
{
return false;
}
}
#endif
/// <summary>
/// SRP should use this to safely determine whether XR is enabled at runtime.
/// </summary>
public static bool enabled
{
get
{
#if ENABLE_VR && ENABLE_VR_MODULE
return XRSettings.enabled;
#else
return false;
#endif
}
}
/// <summary>
/// Returns true if the XR device is active.
/// </summary>
public static bool isDeviceActive
{
get
{
#if ENABLE_VR && ENABLE_VR_MODULE
if (enabled)
return XRSettings.isDeviceActive;
#endif
return false;
}
}
/// <summary>
/// Name of the loaded XR device.
/// </summary>
public static string loadedDeviceName
{
get
{
#if ENABLE_VR && ENABLE_VR_MODULE
if (enabled)
return XRSettings.loadedDeviceName;
#endif
return "No XR device loaded";
}
}
/// <summary>
/// List of supported XR devices.
/// </summary>
public static string[] supportedDevices
{
get
{
#if ENABLE_VR && ENABLE_VR_MODULE
if (enabled)
return XRSettings.supportedDevices;
#endif
return new string[1];
}
}
/// <summary>
/// Eye texture descriptor.
/// </summary>
public static RenderTextureDescriptor eyeTextureDesc
{
get
{
#if ENABLE_VR && ENABLE_VR_MODULE
if (enabled)
return XRSettings.eyeTextureDesc;
#endif
return new RenderTextureDescriptor(0, 0);
}
}
/// <summary>
/// Eye texture width.
/// </summary>
public static int eyeTextureWidth
{
get
{
#if ENABLE_VR && ENABLE_VR_MODULE
if (enabled)
return XRSettings.eyeTextureWidth;
#endif
return 0;
}
}
/// <summary>
/// Eye texture height.
/// </summary>
public static int eyeTextureHeight
{
get
{
#if ENABLE_VR && ENABLE_VR_MODULE
if (enabled)
return XRSettings.eyeTextureHeight;
#endif
return 0;
}
}
/// <summary>
/// Occlusion mesh's scaling factor.
/// </summary>
public static float occlusionMeshScale
{
get
{
#if ENABLE_VR && ENABLE_VR_MODULE
if (enabled)
return XRSystem.GetOcclusionMeshScale();
#endif
return 0;
}
set
{
#if ENABLE_VR && ENABLE_VR_MODULE
if (enabled)
XRSystem.SetOcclusionMeshScale(value);
#endif
}
}
/// <summary>
/// Controls XR mirror view blit operation
/// </summary>
public static int mirrorViewMode
{
get
{
#if ENABLE_VR && ENABLE_VR_MODULE
if (enabled)
return XRSystem.GetMirrorViewMode();
#endif
return 0;
}
set
{
#if ENABLE_VR && ENABLE_VR_MODULE
if (enabled)
XRSystem.SetMirrorViewMode(value);
#endif
}
}
}
}

View File

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

View File

@@ -0,0 +1,494 @@
using System;
using System.Collections.Generic;
using UnityEngine.Rendering;
#if ENABLE_VR && ENABLE_XR_MODULE
using UnityEngine.XR;
#endif
namespace UnityEngine.Experimental.Rendering
{
/// <summary>
/// Used by render pipelines to control the active XR shader variant.
/// </summary>
public static class SinglepassKeywords
{
/// <summary> XR shader keyword used by multiview rendering </summary>
public static GlobalKeyword STEREO_MULTIVIEW_ON;
/// <summary> XR shader keywordused by single pass instanced rendering </summary>
public static GlobalKeyword STEREO_INSTANCING_ON;
}
/// <summary>
/// Used by render pipelines to communicate with XR SDK.
/// </summary>
public static class XRSystem
{
// Keep track of only one XR layout
static XRLayoutStack s_Layout = new ();
// Delegate allocations of XRPass to the render pipeline
static Func<XRPassCreateInfo, XRPass> s_PassAllocator = null;
#if ENABLE_VR && ENABLE_XR_MODULE
static List<XRDisplaySubsystem> s_DisplayList = new List<XRDisplaySubsystem>();
static XRDisplaySubsystem s_Display;
/// <summary>
/// Returns the active XR display.
/// </summary>
static public XRDisplaySubsystem GetActiveDisplay()
{
return s_Display;
}
#endif
// MSAA level (number of samples per pixel) shared by all XR displays
static MSAASamples s_MSAASamples = MSAASamples.None;
#if ENABLE_VR && ENABLE_XR_MODULE
// Occlusion Mesh scaling factor
static float s_OcclusionMeshScaling = 1.0f;
#endif
// Internal resources used by XR rendering
static Material s_OcclusionMeshMaterial;
static Material s_MirrorViewMaterial;
// Ability to override the default XR layout
static Action<XRLayout, Camera> s_LayoutOverride = null;
/// <summary>
/// Returns true if a XR device is connected and running.
/// </summary>
static public bool displayActive
{
#if ENABLE_VR && ENABLE_XR_MODULE
get => (s_Display != null) ? s_Display.running : false;
#else
get => false;
#endif
}
/// <summary>
/// Returns if the XR display is running in HDR mode.
/// </summary>
static public bool isHDRDisplayOutputActive
{
#if ENABLE_VR && ENABLE_XR_MODULE
get => s_Display?.hdrOutputSettings?.active ?? false;
#else
get => false;
#endif
}
/// <summary>
/// Valid empty pass when a camera is not using XR.
/// </summary>
public static readonly XRPass emptyPass = new XRPass();
/// <summary>
/// If true, the system will try to create a layout compatible with single-pass rendering.
/// </summary>
static public bool singlePassAllowed { get; set; } = true;
/// <summary>
/// Cached value of SystemInfo.foveatedRenderingCaps.
/// </summary>
static public FoveatedRenderingCaps foveatedRenderingCaps { get; set; }
/// <summary>
/// If true, the system will log some information about the layout to the console.
/// </summary>
static public bool dumpDebugInfo { get; set; } = false;
/// <summary>
/// Use this method to assign the shaders that will be used to render occlusion mesh for each XRPass and the final mirror view.
/// </summary>
/// <param name="passAllocator"> Delegate funcion used to allocate XRPasses. </param>
/// <param name="occlusionMeshPS"> Fragement shader used for rendering occlusion mesh. </param>
/// <param name="mirrorViewPS"> Fragement shader used for rendering mirror view. </param>
public static void Initialize(Func<XRPassCreateInfo, XRPass> passAllocator, Shader occlusionMeshPS, Shader mirrorViewPS)
{
if (passAllocator == null)
throw new ArgumentNullException("passCreator");
s_PassAllocator = passAllocator;
RefreshDeviceInfo();
foveatedRenderingCaps = SystemInfo.foveatedRenderingCaps;
if (occlusionMeshPS != null && s_OcclusionMeshMaterial == null)
s_OcclusionMeshMaterial = CoreUtils.CreateEngineMaterial(occlusionMeshPS);
if (mirrorViewPS != null && s_MirrorViewMaterial == null)
s_MirrorViewMaterial = CoreUtils.CreateEngineMaterial(mirrorViewPS);
if (XRGraphicsAutomatedTests.enabled)
SetLayoutOverride(XRGraphicsAutomatedTests.OverrideLayout);
SinglepassKeywords.STEREO_MULTIVIEW_ON = GlobalKeyword.Create("STEREO_MULTIVIEW_ON");
SinglepassKeywords.STEREO_INSTANCING_ON = GlobalKeyword.Create("STEREO_INSTANCING_ON");
}
/// <summary>
/// Used by the render pipeline to communicate to the XR device how many samples are used by MSAA.
/// </summary>
/// <param name="msaaSamples"> The active msaa samples the XRDisplay to set to. The eye texture surfaces are reallocated when necessary to work with the active msaa samples. </param>
public static void SetDisplayMSAASamples(MSAASamples msaaSamples)
{
if (s_MSAASamples == msaaSamples)
return;
s_MSAASamples = msaaSamples;
#if ENABLE_VR && ENABLE_XR_MODULE
SubsystemManager.GetSubsystems(s_DisplayList);
foreach (var display in s_DisplayList)
display.SetMSAALevel((int)s_MSAASamples);
#endif
}
/// <summary>
/// Returns the number of samples (MSAA) currently configured on the XR device.
/// </summary>
/// <returns> Returns current active msaa samples. </returns>
public static MSAASamples GetDisplayMSAASamples()
{
return s_MSAASamples;
}
/// <summary>
/// Used by the render pipeline to scale all occlusion meshes used by all XRPasses.
/// </summary>
/// <param name="occlusionMeshScale">A value of 1.0f represents 100% of the original mesh size. A value less or equal to 0.0f disables occlusion mesh draw. </param>
internal static void SetOcclusionMeshScale(float occlusionMeshScale)
{
#if ENABLE_VR && ENABLE_XR_MODULE
s_OcclusionMeshScaling = occlusionMeshScale;
#endif
}
/// <summary>
/// Returned value used by the render pipeline to scale all occlusion meshes used by all XRPasses.
/// </summary>
internal static float GetOcclusionMeshScale()
{
#if ENABLE_VR && ENABLE_XR_MODULE
return s_OcclusionMeshScaling;
#else
return 1.0f;
#endif
}
/// <summary>
/// Used to communicate to the XR device how to render the XR MirrorView. Note: not all blit modes are supported by all providers. Blitmode set here serves as preference purpose.
/// </summary>
/// <param name="mirrorBlitMode"> Mirror view mode to be set as preferred. See `XRMirrorViewBlitMode` for the builtin blit modes. </param>
internal static void SetMirrorViewMode(int mirrorBlitMode)
{
#if ENABLE_VR && ENABLE_XR_MODULE
if (s_Display == null)
return;
s_Display.SetPreferredMirrorBlitMode(mirrorBlitMode);
#endif
}
/// <summary>
/// Get current blit modes preferred by XRDisplay
/// </summary>
internal static int GetMirrorViewMode()
{
#if ENABLE_VR && ENABLE_XR_MODULE
if (s_Display == null)
return XRMirrorViewBlitMode.None;
return s_Display.GetPreferredMirrorBlitMode();
#else
return 0;
#endif
}
/// <summary>
/// Used by the render pipeline to scale the render target on the XR device.
/// </summary>
/// <param name="renderScale">A value of 1.0f represents 100% of the original resolution.</param>
public static void SetRenderScale(float renderScale)
{
#if ENABLE_VR && ENABLE_XR_MODULE
SubsystemManager.GetSubsystems(s_DisplayList);
foreach (var display in s_DisplayList)
display.scaleOfAllRenderTargets = renderScale;
#endif
}
/// <summary>
/// Used by the render pipeline to retrieve the renderViewportScale value from the XR display.
/// One use case for retriving this value is that render pipeline can properly sync some SRP owned textures to scale accordingly
/// </summary>
/// <returns> Returns current scaleOfAllViewports value from the XRDisplaySubsystem. </returns>
public static float GetRenderViewportScale()
{
#if ENABLE_VR && ENABLE_XR_MODULE
return s_Display.scaleOfAllViewports;
#else
return 1.0f;
#endif
}
/// <summary>
/// Used by the render pipeline to initiate a new rendering frame through a XR layout.
/// </summary>
/// <returns> Returns a new default layout. </returns>
public static XRLayout NewLayout()
{
RefreshDeviceInfo();
return s_Layout.New();
}
/// <summary>
/// Used by the render pipeline to complete the XR layout at the end of the frame.
/// </summary>
public static void EndLayout()
{
if (dumpDebugInfo)
s_Layout.top.LogDebugInfo();
s_Layout.Release();
}
/// <summary>
/// Used by the render pipeline to render the mirror view to the gameview, as configured by the XR device.
/// </summary>
/// <param name="cmd"> CommandBuffer on which to perform the mirror view draw. </param>
/// <param name="camera"> Camera that has XR device connected to. The connected XR device determines how to perform the mirror view draw. </param>
public static void RenderMirrorView(CommandBuffer cmd, Camera camera)
{
#if ENABLE_VR && ENABLE_XR_MODULE
XRMirrorView.RenderMirrorView(cmd, camera, s_MirrorViewMaterial, s_Display);
#endif
}
/// <summary>
/// Free up resources used by the system.
/// </summary>
public static void Dispose()
{
if (s_OcclusionMeshMaterial != null)
{
CoreUtils.Destroy(s_OcclusionMeshMaterial);
s_OcclusionMeshMaterial = null;
}
if (s_MirrorViewMaterial != null)
{
CoreUtils.Destroy(s_MirrorViewMaterial);
s_MirrorViewMaterial = null;
}
}
// Used by the render pipeline to communicate to the XR device the range of the depth buffer.
internal static void SetDisplayZRange(float zNear, float zFar)
{
#if ENABLE_VR && ENABLE_XR_MODULE
if (s_Display != null)
{
s_Display.zNear = zNear;
s_Display.zFar = zFar;
}
#endif
}
// XRTODO : expose as public API
static void SetLayoutOverride(Action<XRLayout, Camera> action)
{
s_LayoutOverride = action;
}
// Disable legacy VR system before rendering first frame
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSplashScreen)]
static void XRSystemInit()
{
if (GraphicsSettings.currentRenderPipeline != null)
{
RefreshDeviceInfo();
}
}
static void RefreshDeviceInfo()
{
#if ENABLE_VR && ENABLE_XR_MODULE
SubsystemManager.GetSubsystems(s_DisplayList);
if (s_DisplayList.Count > 0)
{
if (s_DisplayList.Count > 1)
throw new NotImplementedException("Only one XR display is supported!");
s_Display = s_DisplayList[0];
s_Display.disableLegacyRenderer = true;
s_Display.sRGB = QualitySettings.activeColorSpace == ColorSpace.Linear;
// XRTODO : discuss this code and UI implications
s_Display.textureLayout = XRDisplaySubsystem.TextureLayout.Texture2DArray;
// XRTODO : replace by API from XR SDK, assume we have 2 views max for now
TextureXR.maxViews = Math.Max(TextureXR.slices, 2);
}
else
{
s_Display = null;
}
#endif
}
// Setup the layout to use multi-pass or single-pass based on the runtime caps
internal static void CreateDefaultLayout(Camera camera, XRLayout layout)
{
#if ENABLE_VR && ENABLE_XR_MODULE
if (s_Display == null)
throw new NullReferenceException(nameof(s_Display));
void AddViewToPass(XRPass xrPass, XRDisplaySubsystem.XRRenderPass renderPass, int renderParamIndex)
{
renderPass.GetRenderParameter(camera, renderParamIndex, out var renderParam);
xrPass.AddView(BuildView(renderPass, renderParam));
}
for (int renderPassIndex = 0; renderPassIndex < s_Display.GetRenderPassCount(); ++renderPassIndex)
{
s_Display.GetRenderPass(renderPassIndex, out var renderPass);
s_Display.GetCullingParameters(camera, renderPass.cullingPassIndex, out var cullingParams);
int renderParameterCount = renderPass.GetRenderParameterCount();
if (CanUseSinglePass(camera, renderPass))
{
var createInfo = BuildPass(renderPass, cullingParams, layout);
var xrPass = s_PassAllocator(createInfo);
for (int renderParamIndex = 0; renderParamIndex < renderParameterCount; ++renderParamIndex)
{
AddViewToPass(xrPass, renderPass, renderParamIndex);
}
layout.AddPass(camera, xrPass);
}
else
{
for (int renderParamIndex = 0; renderParamIndex < renderParameterCount; ++renderParamIndex)
{
var createInfo = BuildPass(renderPass, cullingParams, layout);
var xrPass = s_PassAllocator(createInfo);
AddViewToPass(xrPass, renderPass, renderParamIndex);
layout.AddPass(camera, xrPass);
}
}
}
s_LayoutOverride?.Invoke(layout, camera);
#endif
}
// Update the parameters of one pass with a different camera
internal static void ReconfigurePass(XRPass xrPass, Camera camera)
{
#if ENABLE_VR && ENABLE_XR_MODULE
if (xrPass.enabled && s_Display != null)
{
s_Display.GetRenderPass(xrPass.multipassId, out var renderPass);
Debug.Assert(xrPass.singlePassEnabled || renderPass.GetRenderParameterCount() == 1);
s_Display.GetCullingParameters(camera, renderPass.cullingPassIndex, out var cullingParams);
xrPass.AssignCullingParams(renderPass.cullingPassIndex, cullingParams);
for (int renderParamIndex = 0; renderParamIndex < renderPass.GetRenderParameterCount(); ++renderParamIndex)
{
renderPass.GetRenderParameter(camera, renderParamIndex, out var renderParam);
xrPass.AssignView(renderParamIndex, BuildView(renderPass, renderParam));
}
s_LayoutOverride?.Invoke(s_Layout.top, camera);
}
#endif
}
#if ENABLE_VR && ENABLE_XR_MODULE
static bool CanUseSinglePass(Camera camera, XRDisplaySubsystem.XRRenderPass renderPass)
{
if (!singlePassAllowed)
return false;
if (renderPass.renderTargetDesc.dimension != TextureDimension.Tex2DArray)
return false;
if (renderPass.GetRenderParameterCount() != 2 || renderPass.renderTargetDesc.volumeDepth != 2)
return false;
renderPass.GetRenderParameter(camera, 0, out var renderParam0);
renderPass.GetRenderParameter(camera, 1, out var renderParam1);
if (renderParam0.textureArraySlice != 0 || renderParam1.textureArraySlice != 1)
return false;
return true;
}
static XRView BuildView(XRDisplaySubsystem.XRRenderPass renderPass, XRDisplaySubsystem.XRRenderParameter renderParameter)
{
// Convert viewport from normalized to screen space
Rect viewport = renderParameter.viewport;
viewport.x *= renderPass.renderTargetDesc.width;
viewport.width *= renderPass.renderTargetDesc.width;
viewport.y *= renderPass.renderTargetDesc.height;
viewport.height *= renderPass.renderTargetDesc.height;
// XRTODO : remove this line and use XRSettings.useOcclusionMesh instead when it's fixed
Mesh occlusionMesh = XRGraphicsAutomatedTests.running ? null : renderParameter.occlusionMesh;
return new XRView(renderParameter.projection, renderParameter.view, renderParameter.previousView, renderParameter.isPreviousViewValid, viewport, occlusionMesh, renderParameter.textureArraySlice);
}
private static RenderTextureDescriptor XrRenderTextureDescToUnityRenderTextureDesc(RenderTextureDescriptor xrDesc)
{
// We can't use descriptor directly because y-flip is forced
// XRTODO : fix root problem
RenderTextureDescriptor rtDesc = new RenderTextureDescriptor(xrDesc.width, xrDesc.height, xrDesc.graphicsFormat, xrDesc.depthStencilFormat, xrDesc.mipCount);
rtDesc.dimension = xrDesc.dimension;
rtDesc.volumeDepth = xrDesc.volumeDepth;
rtDesc.vrUsage = xrDesc.vrUsage;
rtDesc.sRGB = xrDesc.sRGB;
rtDesc.shadowSamplingMode = xrDesc.shadowSamplingMode;
return rtDesc;
}
static XRPassCreateInfo BuildPass(XRDisplaySubsystem.XRRenderPass xrRenderPass, ScriptableCullingParameters cullingParameters, XRLayout layout)
{
XRPassCreateInfo passInfo = new XRPassCreateInfo
{
renderTarget = xrRenderPass.renderTarget,
renderTargetDesc = XrRenderTextureDescToUnityRenderTextureDesc(xrRenderPass.renderTargetDesc),
hasMotionVectorPass = xrRenderPass.hasMotionVectorPass,
motionVectorRenderTarget = xrRenderPass.motionVectorRenderTarget,
motionVectorRenderTargetDesc = XrRenderTextureDescToUnityRenderTextureDesc(xrRenderPass.motionVectorRenderTargetDesc),
cullingParameters = cullingParameters,
occlusionMeshMaterial = s_OcclusionMeshMaterial,
occlusionMeshScale = GetOcclusionMeshScale(),
foveatedRenderingInfo = xrRenderPass.foveatedRenderingInfo,
multipassId = layout.GetActivePasses().Count,
cullingPassId = xrRenderPass.cullingPassIndex,
copyDepth = xrRenderPass.shouldFillOutDepth,
xrSdkRenderPass = xrRenderPass
};
return passInfo;
}
#endif
}
}

View File

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

View File

@@ -0,0 +1,38 @@
using System;
namespace UnityEngine.Experimental.Rendering
{
internal readonly struct XRView
{
internal readonly Matrix4x4 projMatrix;
internal readonly Matrix4x4 viewMatrix;
internal readonly Matrix4x4 prevViewMatrix;
internal readonly Rect viewport;
internal readonly Mesh occlusionMesh;
internal readonly int textureArraySlice;
internal readonly Vector2 eyeCenterUV;
internal readonly bool isPrevViewMatrixValid;
internal XRView(Matrix4x4 projMatrix, Matrix4x4 viewMatrix, Matrix4x4 prevViewMatrix, bool isPrevViewMatrixValid, Rect viewport, Mesh occlusionMesh, int textureArraySlice)
{
this.projMatrix = projMatrix;
this.viewMatrix = viewMatrix;
this.prevViewMatrix = prevViewMatrix;
this.viewport = viewport;
this.occlusionMesh = occlusionMesh;
this.textureArraySlice = textureArraySlice;
this.isPrevViewMatrixValid = isPrevViewMatrixValid;
eyeCenterUV = ComputeEyeCenterUV(projMatrix);
}
private static Vector2 ComputeEyeCenterUV(Matrix4x4 proj)
{
var projectionParameters = proj.decomposeProjection;
float left = Math.Abs(projectionParameters.left);
float right = Math.Abs(projectionParameters.right);
float top = Math.Abs(projectionParameters.top);
float bottom = Math.Abs(projectionParameters.bottom);
return new Vector2(left / (right + left), top / (top + bottom));
}
}
}

View File

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