XRPass.cs 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501
  1. // This file contain the two main data structures controlled by the XRSystem.
  2. // XRView contains the parameters required to render (proj and view matrices, viewport, etc)
  3. // XRPass holds the render target information and a list of XRView.
  4. // When a pass has 2+ views, single-pass will be active.
  5. // To avoid allocating every frame, XRView is a struct and XRPass is pooled.
  6. #if ENABLE_VR && ENABLE_XR_MODULE
  7. using System;
  8. using System.Collections.Generic;
  9. using UnityEngine.XR;
  10. namespace UnityEngine.Rendering.Universal
  11. {
  12. internal struct XRPassCreateInfo
  13. {
  14. public int multipassId;
  15. public int cullingPassId;
  16. public RenderTexture renderTarget;
  17. public RenderTextureDescriptor renderTargetDesc;
  18. public bool renderTargetIsRenderTexture;
  19. public ScriptableCullingParameters cullingParameters;
  20. public XRPass.CustomMirrorView customMirrorView;
  21. }
  22. internal struct XRViewCreateInfo
  23. {
  24. public Matrix4x4 projMatrix;
  25. public Matrix4x4 viewMatrix;
  26. public Rect viewport;
  27. public int textureArraySlice;
  28. }
  29. internal struct XRView
  30. {
  31. internal readonly Matrix4x4 projMatrix;
  32. internal readonly Matrix4x4 viewMatrix;
  33. internal readonly Rect viewport;
  34. internal readonly Mesh occlusionMesh;
  35. internal readonly int textureArraySlice;
  36. internal XRView(Matrix4x4 proj, Matrix4x4 view, Rect vp, int dstSlice)
  37. {
  38. projMatrix = proj;
  39. viewMatrix = view;
  40. viewport = vp;
  41. occlusionMesh = null;
  42. textureArraySlice = dstSlice;
  43. }
  44. internal XRView(XRDisplaySubsystem.XRRenderPass renderPass, XRDisplaySubsystem.XRRenderParameter renderParameter)
  45. {
  46. projMatrix = renderParameter.projection;
  47. viewMatrix = renderParameter.view;
  48. viewport = renderParameter.viewport;
  49. occlusionMesh = renderParameter.occlusionMesh;
  50. textureArraySlice = renderParameter.textureArraySlice;
  51. // Convert viewport from normalized to screen space
  52. viewport.x *= renderPass.renderTargetDesc.width;
  53. viewport.width *= renderPass.renderTargetDesc.width;
  54. viewport.y *= renderPass.renderTargetDesc.height;
  55. viewport.height *= renderPass.renderTargetDesc.height;
  56. }
  57. }
  58. class XRPass
  59. {
  60. internal List<XRView> views = new List<XRView>(2);
  61. internal bool enabled { get => views.Count > 0; }
  62. internal bool xrSdkEnabled { get; private set; }
  63. internal bool copyDepth { get; private set; }
  64. internal int multipassId { get; private set; }
  65. internal int cullingPassId { get; private set; }
  66. // Ability to specify where to render the pass
  67. internal RenderTargetIdentifier renderTarget { get; private set; }
  68. internal RenderTextureDescriptor renderTargetDesc { get; private set; }
  69. static RenderTargetIdentifier invalidRT = -1;
  70. internal bool renderTargetValid { get => renderTarget != invalidRT; }
  71. internal bool renderTargetIsRenderTexture { get; private set; }
  72. internal bool isLateLatchEnabled { get; set; }
  73. internal bool canMarkLateLatch { get; set; }
  74. internal bool hasMarkedLateLatch { get; set; }
  75. // Access to view information
  76. internal Matrix4x4 GetProjMatrix(int viewIndex = 0) { return views[viewIndex].projMatrix; }
  77. internal Matrix4x4 GetViewMatrix(int viewIndex = 0) { return views[viewIndex].viewMatrix; }
  78. internal int GetTextureArraySlice(int viewIndex = 0) { return views[viewIndex].textureArraySlice; }
  79. internal Rect GetViewport(int viewIndex = 0) { return views[viewIndex].viewport; }
  80. // Combined projection and view matrices for culling
  81. internal ScriptableCullingParameters cullingParams { get; private set; }
  82. // Single-pass rendering support (instanced draw calls or multiview extension)
  83. internal int viewCount { get => views.Count; }
  84. internal bool singlePassEnabled { get => viewCount > 1; }
  85. // Occlusion mesh rendering
  86. Material occlusionMeshMaterial = null;
  87. Mesh occlusionMeshCombined = null;
  88. int occlusionMeshCombinedHashCode = 0;
  89. internal bool isOcclusionMeshSupported { get => enabled && xrSdkEnabled && occlusionMeshMaterial != null; }
  90. internal bool hasValidOcclusionMesh
  91. {
  92. get
  93. {
  94. if (isOcclusionMeshSupported)
  95. {
  96. if (singlePassEnabled)
  97. return occlusionMeshCombined != null;
  98. else
  99. return views[0].occlusionMesh != null;
  100. }
  101. return false;
  102. }
  103. }
  104. // Ability to override mirror view behavior for each pass
  105. internal delegate void CustomMirrorView(XRPass pass, CommandBuffer cmd, RenderTexture rt, Rect viewport);
  106. CustomMirrorView customMirrorView = null;
  107. internal void SetCustomMirrorView(CustomMirrorView callback) => customMirrorView = callback;
  108. const string k_XRCustomMirrorTag = "XR Custom Mirror View";
  109. static ProfilingSampler _XRCustomMirrorProfilingSampler = new ProfilingSampler(k_XRCustomMirrorTag);
  110. const string k_XROcclusionTag = "XR Occlusion Mesh";
  111. static ProfilingSampler _XROcclusionProfilingSampler = new ProfilingSampler(k_XROcclusionTag);
  112. internal static XRPass Create(XRPassCreateInfo createInfo)
  113. {
  114. XRPass passInfo = GenericPool<XRPass>.Get();
  115. passInfo.multipassId = createInfo.multipassId;
  116. passInfo.cullingPassId = createInfo.cullingPassId;
  117. passInfo.cullingParams = createInfo.cullingParameters;
  118. passInfo.customMirrorView = createInfo.customMirrorView;
  119. passInfo.views.Clear();
  120. if (createInfo.renderTarget != null)
  121. {
  122. passInfo.renderTarget = new RenderTargetIdentifier(createInfo.renderTarget, 0, CubemapFace.Unknown, -1);
  123. passInfo.renderTargetDesc = createInfo.renderTarget.descriptor;
  124. passInfo.renderTargetIsRenderTexture = createInfo.renderTargetIsRenderTexture;
  125. }
  126. else
  127. {
  128. passInfo.renderTarget = invalidRT;
  129. passInfo.renderTargetDesc = createInfo.renderTargetDesc;
  130. passInfo.renderTargetIsRenderTexture = createInfo.renderTargetIsRenderTexture;
  131. }
  132. passInfo.occlusionMeshMaterial = null;
  133. passInfo.xrSdkEnabled = false;
  134. passInfo.copyDepth = false;
  135. return passInfo;
  136. }
  137. internal void UpdateView(int viewId, XRDisplaySubsystem.XRRenderPass xrSdkRenderPass, XRDisplaySubsystem.XRRenderParameter xrSdkRenderParameter)
  138. {
  139. if (viewId >= views.Count)
  140. throw new NotImplementedException($"Invalid XR setup to update, trying to update non-existing xr view.");
  141. views[viewId] = new XRView(xrSdkRenderPass, xrSdkRenderParameter);
  142. }
  143. internal void UpdateView(int viewId, Matrix4x4 proj, Matrix4x4 view, Rect vp, int textureArraySlice = -1)
  144. {
  145. if (viewId >= views.Count)
  146. throw new NotImplementedException($"Invalid XR setup to update, trying to update non-existing xr view.");
  147. views[viewId] = new XRView(proj, view, vp, textureArraySlice);
  148. }
  149. internal void UpdateCullingParams(int cullingPassId, ScriptableCullingParameters cullingParams)
  150. {
  151. this.cullingPassId = cullingPassId;
  152. this.cullingParams = cullingParams;
  153. }
  154. internal void AddView(Matrix4x4 proj, Matrix4x4 view, Rect vp, int textureArraySlice = -1)
  155. {
  156. AddViewInternal(new XRView(proj, view, vp, textureArraySlice));
  157. }
  158. internal static XRPass Create(XRDisplaySubsystem.XRRenderPass xrRenderPass, int multipassId, ScriptableCullingParameters cullingParameters, Material occlusionMeshMaterial)
  159. {
  160. XRPass passInfo = GenericPool<XRPass>.Get();
  161. passInfo.multipassId = multipassId;
  162. passInfo.cullingPassId = xrRenderPass.cullingPassIndex;
  163. passInfo.cullingParams = cullingParameters;
  164. passInfo.views.Clear();
  165. // URP ScriptableRenderer does not track current active depth slice state. We make sure to set all texture slices(-1) across the pipeline to ensure consistency.
  166. passInfo.renderTarget = new RenderTargetIdentifier(xrRenderPass.renderTarget, 0, CubemapFace.Unknown, -1);
  167. RenderTextureDescriptor xrDesc = xrRenderPass.renderTargetDesc;
  168. RenderTextureDescriptor rtDesc = new RenderTextureDescriptor(xrDesc.width, xrDesc.height, xrDesc.colorFormat, xrDesc.depthBufferBits, xrDesc.mipCount);
  169. rtDesc.dimension = xrRenderPass.renderTargetDesc.dimension;
  170. rtDesc.volumeDepth = xrRenderPass.renderTargetDesc.volumeDepth;
  171. rtDesc.vrUsage = xrRenderPass.renderTargetDesc.vrUsage;
  172. rtDesc.sRGB = xrRenderPass.renderTargetDesc.sRGB;
  173. // Can't use xr descriptor directly as its descriptor force off y-flip cap
  174. //passInfo.renderTargetDesc = xrDesc;
  175. passInfo.renderTargetDesc = rtDesc;
  176. // Eye textures are back buffer type internally (See c++ core XRTextureManager)
  177. passInfo.renderTargetIsRenderTexture = false;
  178. passInfo.occlusionMeshMaterial = occlusionMeshMaterial;
  179. passInfo.xrSdkEnabled = true;
  180. passInfo.copyDepth = xrRenderPass.shouldFillOutDepth;
  181. passInfo.customMirrorView = null;
  182. Debug.Assert(passInfo.renderTargetValid, "Invalid render target from XRDisplaySubsystem!");
  183. return passInfo;
  184. }
  185. internal void AddView(XRDisplaySubsystem.XRRenderPass xrSdkRenderPass, XRDisplaySubsystem.XRRenderParameter xrSdkRenderParameter)
  186. {
  187. AddViewInternal(new XRView(xrSdkRenderPass, xrSdkRenderParameter));
  188. }
  189. internal static void Release(XRPass xrPass)
  190. {
  191. GenericPool<XRPass>.Release(xrPass);
  192. }
  193. internal void AddViewInternal(XRView xrView)
  194. {
  195. // XRTODO: Fix hard coded max views
  196. int maxSupportedViews = Math.Min(TextureXR.slices, 2/*ShaderConfig.s_XrMaxViews*/);
  197. if (views.Count < maxSupportedViews)
  198. {
  199. views.Add(xrView);
  200. }
  201. else
  202. {
  203. throw new NotImplementedException($"Invalid XR setup for single-pass, trying to add too many views! Max supported: {maxSupportedViews}");
  204. }
  205. }
  206. // Must be called after all views have been added to the pass
  207. internal void UpdateOcclusionMesh()
  208. {
  209. if (isOcclusionMeshSupported && singlePassEnabled && TryGetOcclusionMeshCombinedHashCode(out var hashCode))
  210. {
  211. if (occlusionMeshCombined == null || hashCode != occlusionMeshCombinedHashCode)
  212. {
  213. CreateOcclusionMeshCombined();
  214. occlusionMeshCombinedHashCode = hashCode;
  215. }
  216. }
  217. else
  218. {
  219. occlusionMeshCombined = null;
  220. occlusionMeshCombinedHashCode = 0;
  221. }
  222. }
  223. private bool TryGetOcclusionMeshCombinedHashCode(out int hashCode)
  224. {
  225. hashCode = 17;
  226. for (int viewId = 0; viewId < viewCount; ++viewId)
  227. {
  228. if (views[viewId].occlusionMesh != null)
  229. {
  230. hashCode = hashCode * 23 + views[viewId].occlusionMesh.GetHashCode();
  231. }
  232. else
  233. {
  234. hashCode = 0;
  235. return false;
  236. }
  237. }
  238. return true;
  239. }
  240. // Create a new mesh that contains the occlusion data from all views
  241. private void CreateOcclusionMeshCombined()
  242. {
  243. occlusionMeshCombined = new Mesh();
  244. occlusionMeshCombined.indexFormat = IndexFormat.UInt16;
  245. int combinedVertexCount = 0;
  246. uint combinedIndexCount = 0;
  247. for (int viewId = 0; viewId < viewCount; ++viewId)
  248. {
  249. Mesh mesh = views[viewId].occlusionMesh;
  250. Debug.Assert(mesh != null);
  251. Debug.Assert(mesh.subMeshCount == 1);
  252. Debug.Assert(mesh.indexFormat == IndexFormat.UInt16);
  253. combinedVertexCount += mesh.vertexCount;
  254. combinedIndexCount += mesh.GetIndexCount(0);
  255. }
  256. Vector3[] vertices = new Vector3[combinedVertexCount];
  257. ushort[] indices = new ushort[combinedIndexCount];
  258. int vertexStart = 0;
  259. int indexStart = 0;
  260. for (int viewId = 0; viewId < viewCount; ++viewId)
  261. {
  262. Mesh mesh = views[viewId].occlusionMesh;
  263. var meshIndices = mesh.GetIndices(0);
  264. // Encore the viewId into the z channel
  265. {
  266. mesh.vertices.CopyTo(vertices, vertexStart);
  267. for (int i = 0; i < mesh.vertices.Length; i++)
  268. vertices[vertexStart + i].z = viewId;
  269. }
  270. // Combine indices into one buffer
  271. for (int i = 0; i < meshIndices.Length; i++)
  272. {
  273. int newIndex = vertexStart + meshIndices[i];
  274. Debug.Assert(meshIndices[i] < ushort.MaxValue);
  275. indices[indexStart + i] = (ushort)newIndex;
  276. }
  277. vertexStart += mesh.vertexCount;
  278. indexStart += meshIndices.Length;
  279. }
  280. occlusionMeshCombined.vertices = vertices;
  281. occlusionMeshCombined.SetIndices(indices, MeshTopology.Triangles, 0);
  282. }
  283. Vector4[] stereoEyeIndices = new Vector4[2] { Vector4.zero , Vector4.one };
  284. internal void StartSinglePass(CommandBuffer cmd)
  285. {
  286. if (enabled)
  287. {
  288. if (singlePassEnabled)
  289. {
  290. if (viewCount <= TextureXR.slices)
  291. {
  292. if (SystemInfo.supportsMultiview)
  293. {
  294. cmd.EnableShaderKeyword("STEREO_MULTIVIEW_ON");
  295. cmd.SetGlobalVectorArray("unity_StereoEyeIndices", stereoEyeIndices);
  296. }
  297. else
  298. {
  299. cmd.EnableShaderKeyword("STEREO_INSTANCING_ON");
  300. cmd.SetInstanceMultiplier((uint)viewCount);
  301. }
  302. }
  303. else
  304. {
  305. throw new NotImplementedException($"Invalid XR setup for single-pass, trying to render too many views! Max supported: {TextureXR.slices}");
  306. }
  307. }
  308. }
  309. }
  310. internal void StopSinglePass(CommandBuffer cmd)
  311. {
  312. if (enabled)
  313. {
  314. if (singlePassEnabled)
  315. {
  316. if (SystemInfo.supportsMultiview)
  317. {
  318. cmd.DisableShaderKeyword("STEREO_MULTIVIEW_ON");
  319. }
  320. else
  321. {
  322. cmd.DisableShaderKeyword("STEREO_INSTANCING_ON");
  323. cmd.SetInstanceMultiplier(1);
  324. }
  325. }
  326. }
  327. }
  328. internal void EndCamera(CommandBuffer cmd, CameraData cameraData)
  329. {
  330. if (!enabled)
  331. return;
  332. StopSinglePass(cmd);
  333. // Callback for custom mirror view
  334. if (customMirrorView != null)
  335. {
  336. using (new ProfilingScope(cmd, _XRCustomMirrorProfilingSampler))
  337. {
  338. customMirrorView(this, cmd, cameraData.targetTexture, cameraData.pixelRect);
  339. }
  340. }
  341. }
  342. internal void RenderOcclusionMesh(CommandBuffer cmd)
  343. {
  344. if (isOcclusionMeshSupported)
  345. {
  346. using (new ProfilingScope(cmd, _XROcclusionProfilingSampler))
  347. {
  348. if (singlePassEnabled)
  349. {
  350. if (occlusionMeshCombined != null && SystemInfo.supportsRenderTargetArrayIndexFromVertexShader)
  351. {
  352. StopSinglePass(cmd);
  353. cmd.EnableShaderKeyword("XR_OCCLUSION_MESH_COMBINED");
  354. cmd.DrawMesh(occlusionMeshCombined, Matrix4x4.identity, occlusionMeshMaterial);
  355. cmd.DisableShaderKeyword("XR_OCCLUSION_MESH_COMBINED");
  356. StartSinglePass(cmd);
  357. }
  358. }
  359. else if (views[0].occlusionMesh != null)
  360. {
  361. cmd.DrawMesh(views[0].occlusionMesh, Matrix4x4.identity, occlusionMeshMaterial);
  362. }
  363. }
  364. }
  365. }
  366. // Store array to avoid allocating every frame
  367. private Matrix4x4[] stereoProjectionMatrix = new Matrix4x4[2];
  368. private Matrix4x4[] stereoViewMatrix = new Matrix4x4[2];
  369. private Matrix4x4[] stereoCameraProjectionMatrix = new Matrix4x4[2];
  370. internal void UpdateGPUViewAndProjectionMatrices(CommandBuffer cmd, ref CameraData cameraData, bool isRenderToTexture)
  371. {
  372. Matrix4x4 projectionMatrix = GL.GetGPUProjectionMatrix(cameraData.xr.GetProjMatrix(0), isRenderToTexture);
  373. RenderingUtils.SetViewAndProjectionMatrices(cmd, cameraData.xr.GetViewMatrix(0), projectionMatrix, true);
  374. if (cameraData.xr.singlePassEnabled)
  375. {
  376. for (int i = 0; i < 2; i++)
  377. {
  378. stereoCameraProjectionMatrix[i] = cameraData.xr.GetProjMatrix(i);
  379. stereoViewMatrix[i] = cameraData.xr.GetViewMatrix(i);
  380. stereoProjectionMatrix[i] = GL.GetGPUProjectionMatrix(stereoCameraProjectionMatrix[i], isRenderToTexture);
  381. }
  382. RenderingUtils.SetStereoViewAndProjectionMatrices(cmd, stereoViewMatrix, stereoProjectionMatrix, stereoCameraProjectionMatrix, true);
  383. if (cameraData.xr.canMarkLateLatch)
  384. MarkLateLatchShaderProperties(cmd, ref cameraData);
  385. }
  386. }
  387. internal static readonly int UNITY_STEREO_MATRIX_V = Shader.PropertyToID("unity_StereoMatrixV");
  388. internal static readonly int UNITY_STEREO_MATRIX_IV = Shader.PropertyToID("unity_StereoMatrixInvV");
  389. internal static readonly int UNITY_STEREO_MATRIX_VP = Shader.PropertyToID("unity_StereoMatrixVP");
  390. internal static readonly int UNITY_STEREO_MATRIX_IVP = Shader.PropertyToID("unity_StereoMatrixIVP");
  391. internal void MarkLateLatchShaderProperties(CommandBuffer cmd, ref CameraData cameraData)
  392. {
  393. cmd.MarkLateLatchMatrixShaderPropertyID(CameraLateLatchMatrixType.View, UNITY_STEREO_MATRIX_V);
  394. cmd.MarkLateLatchMatrixShaderPropertyID(CameraLateLatchMatrixType.InverseView, UNITY_STEREO_MATRIX_IV);
  395. cmd.MarkLateLatchMatrixShaderPropertyID(CameraLateLatchMatrixType.ViewProjection, UNITY_STEREO_MATRIX_VP);
  396. cmd.MarkLateLatchMatrixShaderPropertyID(CameraLateLatchMatrixType.InverseViewProjection, UNITY_STEREO_MATRIX_IVP);
  397. cmd.SetLateLatchProjectionMatrices(stereoProjectionMatrix);
  398. cameraData.xr.hasMarkedLateLatch = true;
  399. }
  400. internal void UnmarkLateLatchShaderProperties(CommandBuffer cmd, ref CameraData cameraData)
  401. {
  402. cmd.UnmarkLateLatchMatrix(CameraLateLatchMatrixType.View);
  403. cmd.UnmarkLateLatchMatrix(CameraLateLatchMatrixType.InverseView);
  404. cmd.UnmarkLateLatchMatrix(CameraLateLatchMatrixType.ViewProjection);
  405. cmd.UnmarkLateLatchMatrix(CameraLateLatchMatrixType.InverseViewProjection);
  406. cameraData.xr.hasMarkedLateLatch = false;
  407. }
  408. }
  409. }
  410. #else
  411. namespace UnityEngine.Rendering.Universal
  412. {
  413. internal class XRPass
  414. {
  415. internal static readonly XRPass emptyPass = new XRPass();
  416. internal bool enabled { get => false; }
  417. internal void StartSinglePass(CommandBuffer cmd) { }
  418. internal void StopSinglePass(CommandBuffer cmd) { }
  419. internal void EndCamera(CommandBuffer cmd, CameraData camera) { }
  420. internal void RenderOcclusionMesh(CommandBuffer cmd) { }
  421. }
  422. }
  423. #endif