using System; using System.Collections.Generic; using Unity.Collections; using Unity.XR.CoreUtils.Collections; using UnityEngine.XR.ARSubsystems; namespace UnityEngine.XR.ARFoundation { /// /// Swapchain strategy for providers that expose a fixed-length swapchain of fixed-size textures. /// /// /// This strategy is especially useful for providers that represent textures using `RenderTexture`, as it /// allows you to create all the RenderTextures when the swapchain is initialized and re-use them for the duration /// of the app's lifecycle. /// class FixedLengthSwapchainStrategy : ISwapchainStrategy { /// /// A 2-dimensional array of texture info objects. /// Each row represents a frame. Each column represents a texture that is used that frame. /// IUpdatableTexture[][] m_UpdatableTextures; /// /// Enables a constant-time lookup operation. Given the native pointer of any texture in the swapchain, /// look up the index of the frame where that texture is used. /// readonly Dictionary m_FrameIndicesByTexturePtr = new(); readonly int m_NumTexturesPerFrame; internal FixedLengthSwapchainStrategy(NativeArray> swapchainDescriptors) { if (swapchainDescriptors.Length == 0 || swapchainDescriptors[0].Length == 0) throw new ArgumentException("Swapchain must contain at least one texture", nameof(swapchainDescriptors)); int numFramesInSwapchain = swapchainDescriptors.Length; m_NumTexturesPerFrame = swapchainDescriptors[0].Length; m_UpdatableTextures = new IUpdatableTexture[numFramesInSwapchain][]; for (var i = 0; i < numFramesInSwapchain; ++i) { if (swapchainDescriptors[i].Length != m_NumTexturesPerFrame) throw new ArgumentException( "Swapchain must use the same number of textures per frame", nameof(swapchainDescriptors)); m_UpdatableTextures[i] = new IUpdatableTexture[m_NumTexturesPerFrame]; for (var j = 0; j < m_NumTexturesPerFrame; ++j) { if (swapchainDescriptors[i][j].nativeTexture == IntPtr.Zero) throw new ArgumentException("Swapchain textures must have non-null native texture pointer.", nameof(swapchainDescriptors)); if (m_FrameIndicesByTexturePtr.ContainsKey(swapchainDescriptors[i][j].nativeTexture)) throw new ArgumentException("Swapchain native texture pointers must be unique.", nameof(swapchainDescriptors)); m_UpdatableTextures[i][j] = UpdatableTextureFactory.Create(swapchainDescriptors[i][j]); m_FrameIndicesByTexturePtr.Add(swapchainDescriptors[i][j].nativeTexture, i); } } } bool ISwapchainStrategy.TryUpdateTexturesForFrame( NativeArray descriptors, out ReadOnlyListSpan textureInfos) { if (descriptors.Length == 0) { textureInfos = ReadOnlyListSpan.Empty(); return true; } if (!m_FrameIndicesByTexturePtr.TryGetValue(descriptors[0].nativeTexture, out var frameIndex)) throw new InvalidOperationException( "Fixed-length swapchain strategy was given a texture that is not in the swapchain."); if (descriptors.Length != m_NumTexturesPerFrame) throw new InvalidOperationException( $"Fixed-length swapchain uses {m_NumTexturesPerFrame} textures per frame, but was given {descriptors.Length} textures this frame."); bool allRequiredTexturesDoExist = true; for (var j = 0; j < m_NumTexturesPerFrame; j++) { if (!m_UpdatableTextures[frameIndex][j].TryUpdateFromDescriptor(descriptors[j])) allRequiredTexturesDoExist = false; } textureInfos = new ReadOnlyListSpan(m_UpdatableTextures[frameIndex]); return allRequiredTexturesDoExist; } public void DestroyTextures() { foreach (var frame in m_UpdatableTextures) { foreach (var textureInfo in frame) { textureInfo.DestroyTexture(); } } } void IDisposable.Dispose() { DestroyTextures(); } } }