// %BANNER_BEGIN%
// ---------------------------------------------------------------------
// %COPYRIGHT_BEGIN%
// Copyright (c) (2018-2022) Magic Leap, Inc. All Rights Reserved.
// Use of this file is governed by the Software License Agreement, located here: https://www.magicleap.com/software-license-agreement-ml2
// Terms and conditions applicable to third-party materials accompanying this distribution may also be found in the top-level NOTICE file appearing herein.
// %COPYRIGHT_END%
// ---------------------------------------------------------------------
// %BANNER_END%
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using UnityEngine.Rendering;
using UnityEngine.XR.MagicLeap.Native;
namespace UnityEngine.XR.MagicLeap
{
[Obsolete("YcbcrRenderer has been deprecated. Use YCbCrRenderer instead")]
///
/// Implements a renderer for android native & hardware buffers (Vulkan-only).
///
public abstract partial class YcbcrRenderer
{
///
/// GCHandle for the callback user context
///
protected GCHandle gcHandle;
///
/// MLYcbcrRenderer handle
///
private ulong handle = MagicLeapNativeBindings.InvalidHandle;
///
/// Pointer to the unmanaged memory passed to the native rendering plugin for every event.
///
private IntPtr eventDataPtr = IntPtr.Zero;
///
/// Managed memory for the data passed to the native rendering plugin for every event.
///
private NativeBindings.PluginEventData eventData;
protected RenderTexture RenderTarget;
private bool didExecuteSetTextureCmdBuffer;
///
/// Command buffers for every native rendering plugin event.
///
private readonly Dictionary commandBuffers = new Dictionary();
public delegate void OnCleanupCompleteDelegate();
public delegate void OnFirstFrameRendereredDelegate();
///
/// Event fired on the callback thread to indicate that resource cleanup is complete in the native plugin
/// and it is now safe to cleanup associated managed resources like the RenderTexture.
///
public event OnCleanupCompleteDelegate OnCleanupComplete_CallbackThread = delegate { };
///
/// Event fired on Unity's main thread to indicate that resource cleanup is complete in the native plugin
/// and it is now safe to cleanup associated managed resources like the RenderTexture.
///
public event OnCleanupCompleteDelegate OnCleanupComplete = delegate { };
///
/// Event fired to indicate a frame has been rendered on the current RenderTexture for the first time.
/// Apps can use this event to disable UI elements like loading indicators since the RenderTexture will
/// have a valid frame to display.
///
public event OnFirstFrameRendereredDelegate OnFirstFrameRendered = delegate { };
private ulong nativeBufferHandle;
///
/// Initialize the native api handle & the graphics command buffers.
///
protected void Initialize(bool waitForQueueIdleOnSubmit = false)
{
eventDataPtr = Marshal.AllocHGlobal(Marshal.SizeOf());
gcHandle = GCHandle.Alloc(this, GCHandleType.Weak);
var createInfo = new NativeBindings.CreateInfo(gcHandle, this, waitForQueueIdleOnSubmit);
var result = NativeBindings.MLYCbCrRendererCreate(ref createInfo, out handle);
if (DidSucceed(result, nameof(NativeBindings.MLYCbCrRendererCreate)))
{
eventData.RendererHandle = handle;
Marshal.StructureToPtr(eventData, eventDataPtr, false);
CreateAndStoreCommandBufferForEvent(NativeBindings.PluginEvent.Draw);
CreateAndStoreCommandBufferForEvent(NativeBindings.PluginEvent.Cleanup);
}
else
{
handle = MagicLeapNativeBindings.InvalidHandle;
}
}
~YcbcrRenderer()
{
ReleaseUnmanagedMemory();
}
///
/// Currently only 1 call after obj instantiation will work
///
///
public void SetRenderBuffer(RenderTexture renderTexture)
{
RenderTarget = renderTexture;
// clear the texture
var rt = RenderTexture.active;
RenderTexture.active = RenderTarget;
GL.Clear(true, true, Color.clear);
RenderTexture.active = rt;
// Make sure the hardware resources are created before we start using them
RenderTarget.Create();
didExecuteSetTextureCmdBuffer = false;
OnRenderTargetSet();
}
protected virtual void OnRenderTargetSet()
{
}
///
/// Render the latest native buffer onto the provided Unity texture. Should preferably be called every frame.
///
public void Render()
{
if (RenderTarget == null)
{
return;
}
if (!didExecuteSetTextureCmdBuffer)
{
if (RenderTarget != null && RenderTarget.IsCreated() && RenderTarget.colorBuffer.GetNativeRenderBufferPtr() != IntPtr.Zero)
{
eventData = new NativeBindings.PluginEventData(handle, RenderTarget);
}
Marshal.StructureToPtr(eventData, eventDataPtr, false);
CreateAndStoreCommandBufferForEvent(NativeBindings.PluginEvent.SetTexture);
Graphics.ExecuteCommandBuffer(commandBuffers[NativeBindings.PluginEvent.SetTexture]);
didExecuteSetTextureCmdBuffer = true;
}
if (RenderTarget != null && !RenderTarget.IsCreated())
{
return;
}
if (commandBuffers.TryGetValue(NativeBindings.PluginEvent.Draw, out var cmdBuffer))
{
Graphics.ExecuteCommandBuffer(cmdBuffer);
}
}
///
/// Destroy all resources held by the native rendering plugin.
///
public void Cleanup()
{
if (commandBuffers.TryGetValue(NativeBindings.PluginEvent.Cleanup, out var cmdBuffer))
{
Graphics.ExecuteCommandBuffer(cmdBuffer);
}
}
private void ReleaseUnmanagedMemory()
{
if (eventDataPtr == IntPtr.Zero)
{
return;
}
Marshal.FreeHGlobal(eventDataPtr);
eventDataPtr = IntPtr.Zero;
}
private void CreateAndStoreCommandBufferForEvent(NativeBindings.PluginEvent pluginEvent)
{
if (!commandBuffers.ContainsKey(pluginEvent))
{
var cmdBuffer = new CommandBuffer();
if (pluginEvent == NativeBindings.PluginEvent.Draw)
{
cmdBuffer.IssuePluginEventAndData(
NativeBindings.MLYCbCrRendererGetCallbackForPluginEvent(NativeBindings.PluginEvent.AccessTexture),
NativeBindings.MLYCbCrRendererGetEventIdForPluginEvent(NativeBindings.PluginEvent.AccessTexture),
eventDataPtr);
}
cmdBuffer.IssuePluginEventAndData(
NativeBindings.MLYCbCrRendererGetCallbackForPluginEvent(pluginEvent),
NativeBindings.MLYCbCrRendererGetEventIdForPluginEvent(pluginEvent),
eventDataPtr);
commandBuffers.Add(pluginEvent, cmdBuffer);
}
}
private void InvokeOnCleanupComplete_CallbackThread()
{
OnCleanupComplete_CallbackThread();
MLThreadDispatch.ScheduleMain(InvokeOnCleanupCompleted_MainThread);
}
private void InvokeOnCleanupCompleted_MainThread()
{
if (RenderTarget != null)
{
RenderTarget.Release();
}
didExecuteSetTextureCmdBuffer = false;
handle = MagicLeapNativeBindings.InvalidHandle;
commandBuffers.Clear();
gcHandle.Free();
ReleaseUnmanagedMemory();
OnCleanupComplete();
}
private void InvokeOnFirstFrameRendered()
{
MLThreadDispatch.Call(OnFirstFrameRendered);
}
}
}