using System; using System.Collections.Generic; using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; using Unity.Jobs; using UnityEngine.Experimental.Rendering; using UnityEditor.Bindings.OpenImageIO; namespace UnityEditor.Recorder.FileFormats { /// /// Job to write and image frame /// unsafe struct WriteImageFrameJob : IJob { /// /// List of byte arrays with frame data. /// public UnsafeList> FramesData; /// /// Frame width. /// public uint Width; /// /// Frame height. /// public uint Height; /// /// List of file attributes arrays. /// public UnsafeList> FileAttributes; /// /// Path to file. /// public FixedString4096Bytes FilePath; /// /// Execute the job. /// public void Execute() { WriteOiioImageFrames(FramesData, Width, Height, FileAttributes, FilePath); } static void WriteOiioImageFrames(UnsafeList> frames, uint width, uint height, UnsafeList> fileAttributes, FixedString4096Bytes path) { var headers = new NativeArray(frames.Length, Allocator.Temp); for (var i = 0; i < frames.Length; i++) { const int sizeHalf = 2; var channelsCount = (uint)(frames[i].Length / (width * height * sizeHalf)); headers[i] = new OiioWrapper.ImageHeader { width = width, height = height, channelsCount = channelsCount, data = new IntPtr(frames[i].GetUnsafeReadOnlyPtr()), attributesCount = (uint)fileAttributes[i].Length, attributes = new IntPtr(fileAttributes[i].GetUnsafeReadOnlyPtr()) }; } OiioWrapper.WriteImage(path, (uint)frames.Length, (OiioWrapper.ImageHeader*)headers.GetUnsafeReadOnlyPtr()); } } /// /// Class to write image frame job buffers /// class WriteImageFrameJobBuffers : IDisposable { /// /// Byte array of image data /// public UnsafeList> framesData; /// /// Attributes of file /// public UnsafeList> fileAttributes; /// /// Write Frame Job Buffers for an array of images /// /// Frame width /// Frame height /// List of readback formats /// List of booleans to indicate if the layer needs alpha /// List of list of attributes public WriteImageFrameJobBuffers(int width, int height, IList readbackFormats, IList needAlphas, IList> layersAttributesList) { framesData = new UnsafeList>(0, Allocator.Persistent); fileAttributes = new UnsafeList>(0, Allocator.Persistent); for (int i = 0; i < layersAttributesList.Count; ++i) { var bufferSize = ComputeBufferSize(width, height, readbackFormats[i], needAlphas[i]); framesData.Add(new NativeArray(bufferSize, Allocator.Persistent)); var layerAttributes = new NativeArray(layersAttributesList[i].Count, Allocator.Persistent); layerAttributes.CopyFrom(layersAttributesList[i].ToArray()); fileAttributes.Add(layerAttributes); } } static int ComputeBufferSize(int width, int height, GraphicsFormat format, bool needAlpha) { var mipmapSize = (int)GraphicsFormatUtility.ComputeMipmapSize(width, height, format); if (format is GraphicsFormat.R16G16B16A16_SFloat && !needAlpha) { return mipmapSize * 3 / 4; } return mipmapSize; } /// /// Disposes of the frame. /// public void Dispose() { if (framesData.IsCreated) { foreach (var frame in framesData) { if (frame.IsCreated) { frame.Dispose(); } } framesData.Dispose(); } if (fileAttributes.IsCreated) { foreach (var attrib in fileAttributes) { if (attrib.IsCreated) { attrib.Dispose(); } } fileAttributes.Dispose(); } } } }