// "VIVE SDK // © 2020 HTC Corporation. All Rights Reserved. // // Unless otherwise required by copyright law and practice, // upon the execution of HTC SDK license agreement, // HTC grants you access to and use of the VIVE SDK(s). // You shall fully comply with all of HTC’s SDK license agreement terms and // conditions signed by you and all SDK and API requirements, // specifications, and documentation provided by HTC to You." using AOT; using System; using System.Collections.Generic; using UnityEngine; using System.Runtime.InteropServices; namespace VIVE.OpenXR.CompositionLayer { public class CompositionLayerRenderThreadSyncObject { private static IntPtr GetFunctionPointerForDelegate(Delegate del) { #if UNITY_EDITOR && UNITY_ANDROID return IntPtr.Zero; #elif UNITY_ANDROID return Marshal.GetFunctionPointerForDelegate(del); #else return IntPtr.Zero; #endif } public delegate void CompositionLayerRenderEventDelegate(int eventID); private static readonly CompositionLayerRenderEventDelegate handle = new CompositionLayerRenderEventDelegate(RunSyncObjectInRenderThread); private static readonly IntPtr handlePtr = GetFunctionPointerForDelegate(handle); public delegate void TaskQueueDelagate(PreAllocatedQueue taskQueue); private static List taskList = new List(); private readonly PreAllocatedQueue queue = new PreAllocatedQueue(); public PreAllocatedQueue Queue { get { return queue; } } private readonly TaskQueueDelagate receiver; private readonly int taskID; public CompositionLayerRenderThreadSyncObject(TaskQueueDelagate taskQueueDelegate) { receiver = taskQueueDelegate; if (receiver == null) throw new ArgumentNullException("receiver should not be null"); taskList.Add(this); taskID = taskList.IndexOf(this); } ~CompositionLayerRenderThreadSyncObject() { try { taskList.RemoveAt(taskID); } finally { } } [MonoPInvokeCallback(typeof(CompositionLayerRenderEventDelegate))] private static void RunSyncObjectInRenderThread(int taskID) { taskList[taskID].ReceiveEvent(); } // Run in GameThread public void IssueEvent() { #if UNITY_EDITOR && UNITY_ANDROID if (Application.isEditor) { receiver(queue); return; } #endif // Let the render thread run the RunSyncObjectInRenderThread(id) #if UNITY_ANDROID GL.IssuePluginEvent(handlePtr, taskID); #else receiver(queue); return; #endif } private void ReceiveEvent() { receiver(queue); } } public class Task { public bool isFree = true; } public class TaskPool { private readonly List pool = new List(2) { }; private int index = 0; public TaskPool() { } private int Next(int value) { if (++value >= pool.Count) value = 0; return value; } public T Obtain() where T : Task, new() { int c = pool.Count; int i = index; for (int j = 0; j < c; i++, j++) { if (i >= c) i = 0; if (pool[i].isFree) { //Debug.LogError("Obtain idx=" + i); index = i; return (T)pool[i]; } } index = Next(i); var newItem = new T() { isFree = true }; pool.Insert(index, newItem); //Debug.LogError("Obtain new one. Pool.Count=" + pool.Count); return newItem; } public void Lock(Task msg) { msg.isFree = false; } public void Release(Task msg) { msg.isFree = true; } } public class PreAllocatedQueue : TaskPool { private readonly List list = new List(2) { null, null }; private int queueBegin = 0; private int queueEnd = 0; public PreAllocatedQueue() : base() { } private int Next(int value) { if (++value >= list.Count) value = 0; return value; } public void Enqueue(Task msg) { Lock(msg); queueEnd = Next(queueEnd); if (queueEnd == queueBegin) { list.Insert(queueEnd, msg); queueBegin++; } else { list[queueEnd] = msg; } } public Task Dequeue() { queueBegin = Next(queueBegin); return list[queueBegin]; } } }