using System;
using Unity.Jobs;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
namespace UnityEngine.XR.ARSubsystems
{
///
/// A reference image library that can be constructed and modified at runtime.
///
///
/// This differs from an , which can only be constructed in the Editor and is
/// immutable at runtime.
///
/// > [!IMPORTANT]
/// > Implementors: providers must implement this class for their provider if
/// > is `true` to provide the functionality
/// > to support runtime mutable libraries.
/// >
/// > This is not something consumers of the namespace should implement.
///
///
public abstract class MutableRuntimeReferenceImageLibrary : RuntimeReferenceImageLibrary
{
///
/// This method should schedule a [Unity Job](xref:Unity.Jobs.IJob) which adds an image to this reference image
/// library.
///
/// The raw image bytes in . Assume the bytes will be valid
/// until the job completes.
/// The width and height of the image, in pixels.
/// The format of . The format has already been validated with
/// .
/// The data associated with the image to add to the
/// library. This includes information like physical dimensions, associated
/// [Texture2D](xref:UnityEngine.Texture2D) (optional), and string name.
/// Input dependencies for the add image job.
/// A [JobHandle](xref:Unity.Jobs.JobHandle) which can be used
/// to chain together multiple tasks or to query for completion.
///
protected abstract JobHandle ScheduleAddImageJobImpl(
NativeSlice imageBytes,
Vector2Int sizeInPixels,
TextureFormat format,
XRReferenceImage referenceImage,
JobHandle inputDeps);
///
/// Derived classes should call this to create an to be returned by
/// its implementation of .
///
/// A handle to the job state. This should be unique to this job state.
/// The [JobHandle](xref:Unity.Jobs.JobHandle) associated with the add job state.
/// Returns a new that contains information about the state of
/// an add image job.
/// Thrown if is true and
/// is `IntPtr.Zero`.
protected AddReferenceImageJobState CreateAddJobState(IntPtr handle, JobHandle jobHandle)
{
if (supportsValidation && handle == IntPtr.Zero)
throw new ArgumentException($"{nameof(handle)} must be non-zero if {nameof(supportsValidation)} is true.", nameof(handle));
return new AddReferenceImageJobState(handle, jobHandle, this);
}
///
/// Get the status of an .
///
///
/// > [!IMPORTANT]
/// > Implementors: If is `true`, then you must also implement this method.
///
/// The state whose status should be retrieved.
/// Returns the of an existing
/// .
/// Thrown if the has a non-zero
/// (non-zero means the concrete class should have overriden this method).
///
protected internal virtual AddReferenceImageJobStatus GetAddReferenceImageJobStatus(AddReferenceImageJobState state)
{
if (state.AsIntPtr() == IntPtr.Zero)
{
return state.jobHandle.IsCompleted
? AddReferenceImageJobStatus.Success
: AddReferenceImageJobStatus.Pending;
}
throw new NotImplementedException();
}
///
/// (Read Only) True if this supports the validation of images
/// when added via .
///
///
/// > [!IMPORTANT]
/// > Implementors: If this is `true`, then your implementation must also override:
/// > -
/// > -
///
public virtual bool supportsValidation => false;
///
/// This method should schedule a [Unity Job](xref:Unity.Jobs.IJob) which adds an image to this reference image
/// library.
///
/// The raw image bytes in . You can assume the bytes are
/// valid until the job completes.
/// The width and height of the image, in pixels.
/// The format of . The format has already been validated with
/// .
/// The data associated with the image to add to the
/// library. This includes information like physical dimensions, associated
/// [Texture2D](xref:UnityEngine.Texture2D) (optional), and string name.
/// A [JobHandle](xref:Unity.Jobs.JobHandle) that represents input dependencies for the add
/// image job.
/// Returns an which contains the state of the asynchronous
/// image addition.
/// Thrown by this base class implementation. If
/// is `true`, then this method should be implemented by the derived class.
///
protected virtual AddReferenceImageJobState ScheduleAddImageWithValidationJobImpl(
NativeSlice imageBytes,
Vector2Int sizeInPixels,
TextureFormat format,
XRReferenceImage referenceImage,
JobHandle inputDeps) => throw new NotImplementedException();
///
/// Asynchronously adds an image to this library.
///
///
/// Image addition can take some time (for example, several frames) due to the processing that must occur to
/// insert the image into the library. This is done using the [Unity Job System]
/// (https://docs.unity3d.com/Manual/JobSystem.html). The returned
/// allows your to query for the status of the job, and, if the job completed,
/// whether it was successful. The job state includes the [JobHandle](xref:Unity.Jobs.JobHandle) which can be
/// used to chain together multiple tasks.
///
/// This job, like all [Unity jobs](https://docs.unity3d.com/Manual/JobSystem.html), can have dependencies (using the
/// ). This can be useful, for example, if is the
/// output of another job. If you are adding multiple images to the library, it is not necessary to pass a
/// previous 's `JobHandle` as the input dependency to the next
/// .
///
/// The must be valid until this job completes. The caller is responsible for
/// managing its memory. You can use the resulting `JobHandle` to schedule a job that deallocates the
/// .
///
/// The raw image bytes in .
/// The width and height of the image, in pixels.
/// The format of . Test for and enumerate supported formats
/// with , , and
/// .
/// The data associated with the image to add to the
/// library. This includes information like physical dimensions, associated
/// [Texture2D](xref:UnityEngine.Texture2D) (optional), and string name. The
/// must be set to zero (). A new guid is
/// automatically generated for the new image.
/// (Optional) Input dependencies for the add image job.
/// Returns an that can be used to query the status of the job.
/// The can be used to determine whether the image was successfully
/// added. Invalid images will be not be added to the reference image library.
/// Thrown if does not contain any
/// bytes.
/// Thrown if 's
/// is not .
/// Thrown if 's
/// is `null`.
/// Thrown if 's
/// is `true` but
/// . contains values less than or equal
/// to zero.
/// Thrown if the is not supported.
/// You can query for support using .
/// Thrown if contains
/// values less than or equal to zero.
public AddReferenceImageJobState ScheduleAddImageWithValidationJob(
NativeSlice imageBytes,
Vector2Int sizeInPixels,
TextureFormat format,
XRReferenceImage referenceImage,
JobHandle inputDeps = default)
{
ValidateAndThrow(imageBytes, sizeInPixels, format, ref referenceImage);
return supportsValidation
? ScheduleAddImageWithValidationJobImpl(imageBytes, sizeInPixels, format, referenceImage, inputDeps)
: CreateAddJobState(IntPtr.Zero, ScheduleAddImageJobImpl(imageBytes, sizeInPixels, format, referenceImage, inputDeps));
}
void ValidateAndThrow(NativeSlice imageBytes, Vector2Int sizeInPixels, TextureFormat format, ref XRReferenceImage referenceImage)
{
unsafe
{
if (imageBytes.GetUnsafePtr() == null)
throw new ArgumentException($"{nameof(imageBytes)} does not contain any bytes.", nameof(imageBytes));
}
if (!referenceImage.guid.Equals(Guid.Empty))
throw new ArgumentException($"{nameof(referenceImage)}.{nameof(referenceImage.guid)} must be empty (all zeroes).", $"{nameof(referenceImage)}.{nameof(referenceImage.guid)}");
// Generate and assign a new guid for the new image
referenceImage.m_SerializedGuid = GenerateNewGuid();
if (string.IsNullOrEmpty(referenceImage.name))
throw new ArgumentNullException($"{nameof(referenceImage)}.{nameof(referenceImage.name)}");
if (referenceImage.specifySize && referenceImage.size.x <= 0)
throw new ArgumentOutOfRangeException($"{nameof(referenceImage)}.{nameof(referenceImage.size)}", referenceImage.size.x, $"Invalid physical image dimensions.");
if (!IsTextureFormatSupported(format))
throw new InvalidOperationException($"The texture format {format} is not supported by the current image tracking subsystem.");
if (sizeInPixels.x <= 0)
throw new ArgumentOutOfRangeException($"{nameof(sizeInPixels)}.{nameof(sizeInPixels.x)}", sizeInPixels.x, "Image width must be greater than zero.");
if (sizeInPixels.y <= 0)
throw new ArgumentOutOfRangeException($"{nameof(sizeInPixels)}.{nameof(sizeInPixels.y)}", sizeInPixels.y, "Image height must be greater than zero.");
}
///
/// The number of texture formats that are supported for image addition.
///
public abstract int supportedTextureFormatCount { get; }
///
/// Returns the supported texture format at . Useful for enumerating the supported texture formats for image addition.
///
/// The index of the format to retrieve.
/// The supported format at .
public TextureFormat GetSupportedTextureFormatAt(int index)
{
if (index < 0)
throw new ArgumentOutOfRangeException(nameof(index), index, $"{nameof(index)} must be greater than or equal to zero.");
if (index >= supportedTextureFormatCount)
throw new ArgumentOutOfRangeException(nameof(index), index, $"{nameof(index)} must be less than {nameof(supportedTextureFormatCount)} ({supportedTextureFormatCount}).");
return GetSupportedTextureFormatAtImpl(index);
}
///
/// Derived methods should return the [TextureFormat](xref:UnityEngine.TextureFormat) at the given .
/// has already been validated to be within [0..].
///
/// The index of the format to retrieve.
/// The supported format at .
protected abstract TextureFormat GetSupportedTextureFormatAtImpl(int index);
///
/// Determines whether the given is supported.
///
/// The [TextureFormat](xref:UnityEngine.TextureFormat) to test.
/// true if is supported for image addition; otherwise, false.
public bool IsTextureFormatSupported(TextureFormat format)
{
int n = supportedTextureFormatCount;
for (int i = 0; i < n; ++i)
{
if (GetSupportedTextureFormatAtImpl(i) == format)
return true;
}
return false;
}
///
/// Gets an enumerator for this collection of reference images. This allows this image library to act as a collection in a foreach statement.
/// The is a struct, so no garbage is generated.
///
/// An enumerator that can be used in a foreach statement.
public Enumerator GetEnumerator() => new Enumerator(this);
// Converts a System.Guid into two ulongs
static unsafe SerializableGuid GenerateNewGuid()
{
var newGuid = Guid.NewGuid();
var trackableId = *(TrackableId*)&newGuid;
return new SerializableGuid(trackableId.subId1, trackableId.subId2);
}
///
/// An enumerator to be used in a foreach statement.
///
public struct Enumerator : IEquatable
{
internal Enumerator(MutableRuntimeReferenceImageLibrary lib)
{
m_Library = lib;
m_Index = -1;
}
MutableRuntimeReferenceImageLibrary m_Library;
int m_Index;
///
/// Moves to the next element in the collection.
///
/// true if is valid after this call.
public bool MoveNext() => ++m_Index < m_Library.count;
///
/// The current .
///
public XRReferenceImage Current => m_Library[m_Index];
///
/// Disposes of the enumerator. This method does nothing.
///
public void Dispose() {}
///
/// Generates a hash code suitable for use in a `Dictionary` or `HashSet`.
///
/// A hash code of this Enumerator.
public override int GetHashCode()
{
unchecked
{
var hash = ReferenceEquals(m_Library, null) ? 0 : m_Library.GetHashCode();
return hash * 486187739 + m_Index.GetHashCode();
}
}
///
/// Compares for equality.
///
/// The object to compare against.
/// true if is of type and is true.
public override bool Equals(object obj) => (obj is Enumerator) && Equals((Enumerator)obj);
///
/// Compares for equality.
///
/// The other enumerator to compare against.
/// true if the other enumerator is equal to this one.
public bool Equals(Enumerator other)
{
return
ReferenceEquals(m_Library, other.m_Library) &&
(m_Index == other.m_Index);
}
///
/// Compares for equality.
///
/// The left-hand side of the comparison.
/// The right-hand side of the comparison.
/// The same as .
public static bool operator ==(Enumerator lhs, Enumerator rhs) => lhs.Equals(rhs);
///
/// Compares for inequality.
///
/// The left-hand side of the comparison.
/// The right-hand side of the comparison.
/// The negation of .
public static bool operator !=(Enumerator lhs, Enumerator rhs) => !lhs.Equals(rhs);
}
}
}