protected override unsafe JobHandle ScheduleAddImageJobImpl(
            NativeSlice <byte> imageBytes,
            Vector2Int sizeInPixels,
            TextureFormat format,
            XRReferenceImage referenceImage,
            JobHandle inputDeps)
        {
            if (!referenceImage.specifySize)
            {
                throw new InvalidOperationException("ARKit requires physical dimensions for all reference images.");
            }

            // Add a reference to keep the native object alive
            // even if we get finalized while a job is running
            UnityARKit_CFRetain(nativePtr);

            // RGBA32 is not supported by CVPixelBuffer, but ARGB32 is, so
            // we offer a conversion for this common case.
            var convertedImage = new NativeArray <byte>();

            if (format == TextureFormat.RGBA32)
            {
                int numPixels = sizeInPixels.x * sizeInPixels.y;
                convertedImage = new NativeArray <byte>(
                    numPixels * 4,
                    Allocator.Persistent,
                    NativeArrayOptions.UninitializedMemory);

                inputDeps = new ConvertRGBA32ToARGB32Job
                {
                    rgbaImage = imageBytes.SliceConvert <uint>(),
                    argbImage = convertedImage.Slice().SliceConvert <uint>()
                }.Schedule(numPixels, 64, inputDeps);

                // Format is now ARGB32
                format = TextureFormat.ARGB32;
            }

            // Schedule the actual addition of the image to the database
            inputDeps = new AddImageJob
            {
                image                 = convertedImage.IsCreated ? new NativeSlice <byte>(convertedImage) : imageBytes,
                database              = nativePtr,
                width                 = sizeInPixels.x,
                height                = sizeInPixels.y,
                physicalWidth         = referenceImage.size.x,
                format                = format,
                managedReferenceImage = new ManagedReferenceImage(referenceImage)
            }.Schedule(inputDeps);

            // If we had to perform a conversion, then release that memory
            if (convertedImage.IsCreated)
            {
                inputDeps = new DeallocateNativeArrayJob <byte> {
                    array = convertedImage
                }.Schedule(inputDeps);
            }

            return(inputDeps);
        }
        protected override AddReferenceImageJobState ScheduleAddImageWithValidationJobImpl(
            NativeSlice <byte> imageBytes,
            Vector2Int sizeInPixels,
            TextureFormat format,
            XRReferenceImage referenceImage,
            JobHandle inputDeps)
        {
            if (!referenceImage.specifySize)
            {
                throw new InvalidOperationException("ARKit requires physical dimensions for all reference images.");
            }

            // Add a reference to keep the native object alive even if we get finalized while a job is running
            var convertedImage = new NativeArray <byte>();
            var validator      = IntPtr.Zero;

            try
            {
                UnityARKit_CFRetain(nativePtr);

                // Schedules a job to convert the image bytes if necessary
                convertedImage = GetImageBytesToConvert(imageBytes, sizeInPixels, ref format, ref inputDeps);

                // Create a native object to contain the validation status
                validator = CreateValidator(nativePtr);

                inputDeps = new AddImageJob
                {
                    image                 = convertedImage.IsCreated ? new NativeSlice <byte>(convertedImage) : imageBytes,
                    database              = nativePtr,
                    validator             = validator,
                    width                 = sizeInPixels.x,
                    height                = sizeInPixels.y,
                    physicalWidth         = referenceImage.size.x,
                    format                = format,
                    managedReferenceImage = new ManagedReferenceImage(referenceImage)
                }.Schedule(inputDeps);
            }
            catch
            {
                DestroyValidator(nativePtr, validator);
                throw;
            }
            finally
            {
                // Release our native counterpart that we previously retained
                inputDeps = ScheduleReleaseJob(inputDeps);

                // If we had to perform a conversion, then release that memory
                if (convertedImage.IsCreated)
                {
                    inputDeps = new DeallocateNativeArrayJob <byte> {
                        array = convertedImage
                    }.Schedule(inputDeps);
                }
            }

            return(CreateAddJobState(validator, inputDeps));
        }