// %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.Runtime.InteropServices;
using AOT;
using MagicLeap.Android.NDK.NativeWindow;
namespace UnityEngine.XR.MagicLeap
{
public partial class YCbCrRenderer
{
public enum YCbCrResult
{
Success = 0,
InvalidParam = 1,
RendererCreationFailed,
UnityGraphicsInterfaceNotFound,
AllocationFailed
}
internal static bool DidSucceed(YCbCrResult result, string functionName, bool logIfError = true)
{
var didSucceed = result == YCbCrResult.Success;
if (!didSucceed && logIfError)
{
Debug.LogError($"{functionName} failed with error: {result}");
}
return didSucceed;
}
private static class NativeBindings
{
private const string YCbCrRendererDll = "ml_ycbcr_renderer";
///
/// Create an instance of the YcbcrRenderer.
///
/// Info used to create the instance
/// Handle to the instance
/// MLResult.Code.Ok if instance was created successfully.
/// MLResult.Code.InvalidParam if one of params was null.
/// MLResult.Code.MediaGenericNoInit if func was called before Unity graphics was initialized.
[DllImport(YCbCrRendererDll, CallingConvention = CallingConvention.Cdecl)]
public static extern YCbCrResult MLYCbCrRendererCreate([In] ref CreateInfo createInfo, out ulong handle);
///
/// Get the event id to be used in CommandBuffer.IssuePluginEvent() for a given rendering plugin event.
///
/// Rendering plugin event to get the id for
/// Event Id
[DllImport(YCbCrRendererDll, CallingConvention = CallingConvention.Cdecl)]
public static extern int MLYCbCrRendererGetEventIdForPluginEvent(PluginEvent pluginEvent);
///
/// Get the callback function pointer to be used in CommandBuffer.IssuePluginEvent() for a given rendering plugin event.
///
/// Rendering plugin event to get the callback function pointer for
/// Callback function pointer
[DllImport(YCbCrRendererDll, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr MLYCbCrRendererGetCallbackForPluginEvent(PluginEvent pluginEvent);
///
/// Delegate signature for the callback invoked by the native rendering plugin, requesting a new
/// AHardwareBuffer.
///
/// Whether a new native buffer handle was acquired
/// Acquired native buffer handle
/// User context passed during instance creation
public delegate void AcquireNextAvailableHwBufferDelegate([MarshalAs(UnmanagedType.I1)][In][Out] ref bool success, [In][Out] ref AHardwareBuffer hwBuffer, IntPtr context);
///
/// Delegate signature for the callback invoked by the native rendering plugin, requesting the
/// given AHardwareBuffer to be released.
///
/// User context passed during instance creation
public delegate void ReleaseHwBufferDelegate(AHardwareBuffer hwBuffer, IntPtr context);
///
/// Delegate signature for the callback invoked by the native rendering plugin, requesting the
/// frame transform matrix for the last acquired native buffer handle.
///
/// Whether a valid frame transform matrix was provided
/// Frame transform matrix
/// User context passed during instance creation
public delegate void GetFrameTransformMatrixDelegate([MarshalAs(UnmanagedType.I1)][In][Out] ref bool success, [In][Out] ref Matrix4x4 frameMat, IntPtr context);
public delegate void IsNewFrameAvailableDelegate([MarshalAs(UnmanagedType.I1)][In][Out] ref bool success, IntPtr context);
public delegate void CleanupCompleteDelegate(IntPtr context);
public delegate void FirstFrameRenderedDelegate(IntPtr context);
public delegate void OverrideYCbCrConversionSamplerDelegate([In] ref VkAndroidHardwareBufferFormatPropertiesAndroid hwBufferFormatProperties, [MarshalAs(UnmanagedType.I1)][In][Out] ref bool samplerChanged, [In][Out] ref VkSamplerYCbCrConversionCreateInfo sampler, IntPtr context);
[MonoPInvokeCallback(typeof(AcquireNextAvailableHwBufferDelegate))]
private static void AcquireNextAvailableHwBuffer([MarshalAs(UnmanagedType.I1)][In][Out] ref bool success, [In][Out] ref AHardwareBuffer hwBuffer, IntPtr context)
{
var gCHandle = GCHandle.FromIntPtr(context);
if (gCHandle.Target is not IHardwareBufferProvider provider)
{
return;
}
success = provider.AcquireNextAvailableHwBuffer(out hwBuffer);
}
[MonoPInvokeCallback(typeof(ReleaseHwBufferDelegate))]
private static void ReleaseHwBuffer(AHardwareBuffer hwBuffer, IntPtr context)
{
var gCHandle = GCHandle.FromIntPtr(context);
var provider = gCHandle.Target as IHardwareBufferProvider;
provider?.ReleaseHwBuffer(hwBuffer);
}
[MonoPInvokeCallback(typeof(GetFrameTransformMatrixDelegate))]
private static void GetFrameTransformMatrix([MarshalAs(UnmanagedType.I1)][In][Out] ref bool success, [In][Out] ref Matrix4x4 frameMat, IntPtr context)
{
var gCHandle = GCHandle.FromIntPtr(context);
if (gCHandle.Target is not IFrameTransformMatrixProvider provider)
{
return;
}
success = provider.GetFrameTransformMatrix(ref frameMat);
}
[MonoPInvokeCallback(typeof(IsNewFrameAvailableDelegate))]
private static void IsNewFrameAvailable([MarshalAs(UnmanagedType.I1)][In][Out] ref bool success, IntPtr context)
{
var gCHandle = GCHandle.FromIntPtr(context);
if (gCHandle.Target is not IFrameAvailabilityProvider provider)
{
return;
}
success = provider.IsNewFrameAvailable();
}
[MonoPInvokeCallback(typeof(CleanupCompleteDelegate))]
private static void OnCleanupComplete(IntPtr context)
{
var gcHandle = GCHandle.FromIntPtr(context);
var yCbCrRenderer = (gcHandle.Target as YCbCrRenderer);
yCbCrRenderer?.CleanupCompleteCallbackThread();
}
[MonoPInvokeCallback(typeof(FirstFrameRenderedDelegate))]
private static void OnFirstFrameRendered(IntPtr context)
{
var gcHandle = GCHandle.FromIntPtr(context);
var yCbCrRenderer = (gcHandle.Target as YCbCrRenderer);
yCbCrRenderer?.InvokeOnFirstFrameRendered();
}
[MonoPInvokeCallback(typeof(OverrideYCbCrConversionSamplerDelegate))]
private static void OverrideYCbCrConversionSampler([In] ref VkAndroidHardwareBufferFormatPropertiesAndroid hwBufferFormatProperties, [MarshalAs(UnmanagedType.I1)][In][Out] ref bool samplerChanged, [In][Out] ref VkSamplerYCbCrConversionCreateInfo sampler, IntPtr context)
{
var gcHandle = GCHandle.FromIntPtr(context);
if (gcHandle.Target is not IYCbCrConversionSamplerProvider renderer)
{
return;
}
samplerChanged = renderer.OverrideYCbCrConversionSampler(ref hwBufferFormatProperties, ref sampler);
}
///
/// Color spaces supported by the native rendering plugin
///
public enum ColorSpace : uint
{
Linear,
Gamma
}
///
/// Plugin events supported by the native rendering plugin
///
public enum PluginEvent : uint
{
///
/// Pass a unity texture and its width, height to the native rendering plugin
///
SetTexture,
///
/// Draw the latest native buffer onto the unity texture
///
Draw,
///
/// Destroy all resources and the renderer instance
///
Cleanup,
AccessTexture
}
///
/// Data to be passed down to the native plugin
/// for a rendering event.
///
[StructLayout(LayoutKind.Sequential)]
public struct PluginEventData
{
///
/// Handle of the YCbCrRenderer instance received from a MLYCbCrRendererCreate() call.
///
public ulong RendererHandle;
///
/// Native pointer of the Unity texture to render the android native buffer on.
///
public IntPtr TextureHandle;
///
/// Width of the Unity texture
///
public int Width;
///
/// Height of the Unity texture
///
public int Height;
///
/// Color space to render the native buffer in.
///
public ColorSpace ColorSpace;
public PluginEventData(ulong rendererHandle, RenderTexture renderBuffer)
{
RendererHandle = rendererHandle;
TextureHandle = renderBuffer.colorBuffer.GetNativeRenderBufferPtr();
Width = renderBuffer.width;
Height = renderBuffer.height;
ColorSpace = renderBuffer.sRGB ? ColorSpace.Linear : ColorSpace.Gamma;
}
}
///
/// Info to create the native rendering plugin instance with
///
[StructLayout(LayoutKind.Sequential)]
public struct CreateInfo
{
///
/// User context data provided in the callbacks.
///
public IntPtr Context;
public AcquireNextAvailableHwBufferDelegate AcquireNextAvailableHwBufferCallback;
public ReleaseHwBufferDelegate ReleaseHwBufferCallback;
///
/// Callback invoked by the native plugin to get the frame transform matrix.
///
public GetFrameTransformMatrixDelegate GetFrameTransformMatrixCallback;
public IsNewFrameAvailableDelegate IsNewFrameAvailableCallback;
public CleanupCompleteDelegate OnCleanupCompleteCallback;
public FirstFrameRenderedDelegate OnFirstFrameRenderedCallback;
public OverrideYCbCrConversionSamplerDelegate OverrideYCbCrConversionSamplerCallback;
[MarshalAs(UnmanagedType.I1)]
public bool ShouldWaitForQueueIdleOnSubmit;
///
/// Construct the info for the native plugin instance
///
/// GCHandle passed back to the callbacks as the user context
/// The YCbCr renderer
/// Whether to wait after submitting a frame
public CreateInfo(GCHandle context, YCbCrRenderer renderer, bool waitForQueueIdleOnSubmit)
{
Context = GCHandle.ToIntPtr(context);
AcquireNextAvailableHwBufferCallback = null;
ReleaseHwBufferCallback = null;
if (renderer is IHardwareBufferProvider)
{
AcquireNextAvailableHwBufferCallback = AcquireNextAvailableHwBuffer;
ReleaseHwBufferCallback = ReleaseHwBuffer;
}
GetFrameTransformMatrixCallback = null;
if (renderer is IFrameTransformMatrixProvider)
{
GetFrameTransformMatrixCallback = GetFrameTransformMatrix;
}
IsNewFrameAvailableCallback = null;
if (renderer is IFrameAvailabilityProvider)
{
IsNewFrameAvailableCallback = IsNewFrameAvailable;
}
OnCleanupCompleteCallback = OnCleanupComplete;
OnFirstFrameRenderedCallback = OnFirstFrameRendered;
OverrideYCbCrConversionSamplerCallback = null;
if (renderer is IYCbCrConversionSamplerProvider)
{
OverrideYCbCrConversionSamplerCallback = OverrideYCbCrConversionSampler;
}
ShouldWaitForQueueIdleOnSubmit = waitForQueueIdleOnSubmit;
}
}
}
}
}