using System; using System.Linq; using Unity.Collections; using Unity.Simulation; using UnityEngine.Experimental.Rendering; using UnityEngine.Rendering; namespace UnityEngine.Perception.GroundTruth { /// /// RenderTextureReader reads a RenderTexture from the GPU each frame and passes the data back through a provided callback. /// /// The type of the raw texture data to be provided. public class RenderTextureReader : IDisposable where T : struct { RenderTexture m_Source; Action, RenderTexture> m_ImageReadCallback; int m_NextFrameToCapture; Texture2D m_CpuTexture; Camera m_CameraRenderingToSource; /// /// Creates a new for the given , , and image readback callback /// /// The to read from. /// The which renders to the given renderTexture. This is used to determine when to read from the texture. /// The callback to call after reading the texture public RenderTextureReader(RenderTexture source, Camera cameraRenderingToSource, Action, RenderTexture> imageReadCallback) { this.m_Source = source; this.m_ImageReadCallback = imageReadCallback; this.m_CameraRenderingToSource = cameraRenderingToSource; m_NextFrameToCapture = Time.frameCount; // TODO find a better place for this but hardcoded right now to make progress CaptureOptions.useAsyncReadbackIfSupported = false; if (!GraphicsUtilities.SupportsAsyncReadback()) m_CpuTexture = new Texture2D(m_Source.width, m_Source.height, m_Source.graphicsFormat, TextureCreationFlags.None); RenderPipelineManager.endFrameRendering += OnEndFrameRendering; } void OnEndFrameRendering(ScriptableRenderContext context, Camera[] cameras) { #if UNITY_EDITOR if (UnityEditor.EditorApplication.isPaused) return; #endif if (!cameras.Contains(m_CameraRenderingToSource)) return; if (m_NextFrameToCapture > Time.frameCount) return; m_NextFrameToCapture = Time.frameCount + 1; if (!GraphicsUtilities.SupportsAsyncReadback()) { RenderTexture.active = m_Source; m_CpuTexture.ReadPixels(new Rect( Vector2.zero, new Vector2(m_Source.width, m_Source.height)), 0, 0); RenderTexture.active = null; var data = m_CpuTexture.GetRawTextureData(); m_ImageReadCallback(Time.frameCount, data, m_Source); return; } var commandBuffer = CommandBufferPool.Get("RenderTextureReader"); var frameCount = Time.frameCount; commandBuffer.RequestAsyncReadback(m_Source, r => OnGpuReadback(r, frameCount)); context.ExecuteCommandBuffer(commandBuffer); context.Submit(); CommandBufferPool.Release(commandBuffer); } void OnGpuReadback(AsyncGPUReadbackRequest request, int frameCount) { if (request.hasError) { Debug.LogError("Error reading segmentation image from GPU"); } else if (request.done && m_ImageReadCallback != null) { m_ImageReadCallback(frameCount, request.GetData(), m_Source); } } /// /// Synchronously wait for all image requests to complete. /// public void WaitForAllImages() { AsyncGPUReadback.WaitAllRequests(); } /// /// Shut down the reader, waiting for all requests to return. /// public void Dispose() { WaitForAllImages(); RenderPipelineManager.endFrameRendering -= OnEndFrameRendering; if (m_CpuTexture != null) { Object.Destroy(m_CpuTexture); m_CpuTexture = null; } } } }