using System; using Unity.Collections; using UnityEngine; using UnityEngine.Assertions; using DataType = TensorFlowLite.Interpreter.DataType; using Object = UnityEngine.Object; namespace TensorFlowLite { /// /// Converts tensor to texture /// public sealed class TensorToTexture : IDisposable { [Serializable] public class Options { public ComputeShader compute = null; public int kernel = 0; public int width = 0; public int height = 0; public int channels = 0; public DataType inputType = DataType.Float32; } private static readonly int _InputTensor = Shader.PropertyToID("_InputTensor"); private static readonly int _InputSize = Shader.PropertyToID("_InputSize"); private static readonly int _OutputTexture = Shader.PropertyToID("_OutputTexture"); private static readonly Lazy DefaultComputeShaderFloat32 = new(() => Resources.Load("com.github.asus4.tflite.common/TensorToTextureFloat32")); private readonly ComputeShader compute; private readonly int kernel; private readonly int width; private readonly int height; private readonly int channels; private readonly GraphicsBuffer tensorBuffer; private readonly RenderTexture outputTexture; public RenderTexture OutputTexture => outputTexture; public TensorToTexture(Options options) { compute = options.compute != null ? options.compute : DefaultComputeShaderFloat32.Value; kernel = options.kernel; width = options.width; height = options.height; channels = options.channels; Assert.IsNotNull(compute, "ComputeShader is not set"); int stride = channels * DataTypeToStride(options.inputType); tensorBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, width * height, stride); outputTexture = new RenderTexture(width, height, 0, RenderTextureFormat.ARGB32) { enableRandomWrite = true }; outputTexture.Create(); } public void Dispose() { tensorBuffer.Dispose(); outputTexture.Release(); Object.Destroy(outputTexture); } public RenderTexture Convert(Array data) { tensorBuffer.SetData(data); compute.SetInts(_InputSize, width, height); compute.SetBuffer(kernel, _InputTensor, tensorBuffer); compute.SetTexture(kernel, _OutputTexture, outputTexture); compute.Dispatch(kernel, Mathf.CeilToInt(width / 8f), Mathf.CeilToInt(height / 8f), 1); return outputTexture; } public RenderTexture Convert(NativeArray data) where T : struct { tensorBuffer.SetData(data); compute.SetInts(_InputSize, width, height); compute.SetBuffer(kernel, _InputTensor, tensorBuffer); compute.SetTexture(kernel, _OutputTexture, outputTexture); compute.Dispatch(kernel, Mathf.CeilToInt(width / 8f), Mathf.CeilToInt(height / 8f), 1); return outputTexture; } private static int DataTypeToStride(DataType type) { return type switch { DataType.Float32 => sizeof(float), _ => throw new NotSupportedException($"Unsupported type: {type}"), }; } } }