/// <summary> /// </summary> /// <param name="src">RenderTexture to capture.</param> /// <param name="functor">Completion functor for handling the captured data. The object passed is a byte[] of the captured data.</param> /// <returns>AsyncRequest<object></returns> public static AsyncRequest <object> Capture(RenderTexture src, Func <AsyncRequest <object>, AsyncRequest <object> .Result> functor = null) { var req = Manager.Instance.CreateRequest <AsyncRequest <object> >(); #if !UNITY_2019_2_OR_NEWER && (PLATFORM_STANDALONE_OSX || UNITY_EDITOR) req.data = GraphicsUtilities.GetPixelsSlow(src as RenderTexture); req.Enqueue(functor); req.Execute(); #else if (GraphicsUtilities.SupportsAsyncReadback()) { AsyncGPUReadback.Request(src, 0, (AsyncGPUReadbackRequest request) => { if (request.hasError) { req.error = true; } else { req.data = request.GetData <byte>().ToArray(); req.Enqueue(functor); req.Execute(); } }); } else { req.data = GraphicsUtilities.GetPixelsSlow(src as RenderTexture); req.Enqueue(functor); req.Execute(); } #endif return(req); }
/// <summary> /// Check if for the given rendering pipeline and GfxAPI there is a need to flip Y during the readback from the backbuffer. /// </summary> /// <param name="camera">Camera from which the readback is being performed.</param> /// <returns>A boolean indicating if the flip is required.</returns> public static bool ShouldFlipY(Camera camera) { #if UNITY_2019_3_OR_NEWER if (SRPSupport != null) { switch (SRPSupport.GetCurrentPipelineRenderingType()) { #if URP_ENABLED case RenderingPipelineType.URP: { return((SystemInfo.graphicsDeviceType == GraphicsDeviceType.Direct3D11 || SystemInfo.graphicsDeviceType == GraphicsDeviceType.Metal) && (camera.targetTexture == null && camera.cameraType == CameraType.Game)); } #endif #if HDRP_ENABLED case RenderingPipelineType.HDRP: { var hdAdditionalCameraData = camera.gameObject.GetComponent <UnityEngine.Rendering.HighDefinition.HDAdditionalCameraData>(); //Based on logic in HDRenderPipeline.PrepareFinalBlitParameters return(camera.targetTexture != null || hdAdditionalCameraData.flipYMode == UnityEngine.Rendering.HighDefinition.HDAdditionalCameraData.FlipYMode.ForceFlipY || camera.cameraType == CameraType.Game); } #endif default: { return(camera.targetTexture == null && GraphicsUtilities.SupportsAsyncReadback()); } } } #endif return(camera.targetTexture == null && GraphicsUtilities.SupportsAsyncReadback()); }
/// <summary> /// Perform async read back from the provided source texture. /// </summary> /// <param name="src">Texture source to be used for the read back.</param> /// <param name="mipIndex">Index of the mipmap to be fetched.</param> /// <param name="functor">Functor that will be invoked after the async read back request is complete.</param> /// <typeparam name="T">Type for the destination data buffer.</typeparam> /// <returns>Returns an AsyncRequest</returns> public static AsyncRequest <object> Capture <T>(Texture src, int mipIndex = 0, Func <AsyncRequest <object>, AsyncRequest <object> .Result> functor = null) where T : struct { var req = Manager.Instance.CreateRequest <AsyncRequest <object> >(); if (GraphicsUtilities.SupportsAsyncReadback()) { AsyncGPUReadback.Request(src, mipIndex, (AsyncGPUReadbackRequest request) => { req.error = request.hasError; if (!request.hasError) { req.data = request.GetData <T>().ToArray(); req.Enqueue(functor); req.Execute(); } }); } else { req.data = GraphicsUtilities.GetPixelsSlow(src as RenderTexture); req.Enqueue(functor); req.Execute(); } return(req); }
/// <summary> /// Perform async read back from the provided compute buffer with size and offset. /// </summary> /// <param name="src">Compute buffer source to be used for the read back.</param> /// <param name="size">Size in bytes of the data to be retrieved from the ComputeBuffer.</param> /// <param name="offset">Offset in bytes in the ComputeBuffer.</param> /// <param name="functor">Functor that will be invoked after the async read back request is complete.</param> /// <typeparam name="T">Type for the destination data buffer.</typeparam> /// <returns>Returns an AsyncRequest</returns> public static AsyncRequest <object> Capture <T>(ComputeBuffer src, int size, int offset, Func <AsyncRequest <object>, AsyncRequest <object> .Result> functor = null) where T : struct { var req = Manager.Instance.CreateRequest <AsyncRequest <object> >(); if (GraphicsUtilities.SupportsAsyncReadback()) { AsyncGPUReadback.Request(src, size, offset, (AsyncGPUReadbackRequest request) => { req.error = request.hasError; if (!request.hasError) { req.data = request.GetData <T>().ToArray(); req.Enqueue(functor); req.Execute(); } }); } else { T[] dst = new T[size]; src.GetData(dst, offset, offset, size); req.data = dst; req.Enqueue(functor); req.Execute(); } return(req); }
void Flush() { if (GraphicsUtilities.SupportsAsyncReadback()) { ProcessBatchAsync(); } else { ProcessBatch(); } }
static void SetupCaptureRequest ( AsyncRequest <CaptureState> req, Channel channel, Camera camera, CameraEvent cameraEvent, BuiltinRenderTextureType source, GraphicsFormat format, Func <AsyncRequest <CaptureState>, AsyncRequest <CaptureState> .Result> functor, bool flipY ) { if (functor != null) { // declared for possible capture, to avoid use from other threads. var cameraTargetTexture = camera.targetTexture; RenderTexture target1 = null; RenderTexture target2 = null; Action ReleaseTargets = () => { if (target1 != null && target1 != cameraTargetTexture) { RenderTexture.ReleaseTemporary(target1); target1 = null; } if (target2 != null) { Debug.Assert(target2 != cameraTargetTexture); RenderTexture.ReleaseTemporary(target2); target2 = null; } }; Material depthMaterial = null; if (source == BuiltinRenderTextureType.Depth) { depthMaterial = SelectDepthShaderVariant(format); } #if UNITY_2019_3_OR_NEWER if (scriptableRenderPipeline) { if (CaptureOptions.useBatchReadback) { QueueForAsyncBatchReadback(req, channel, functor, SetupRenderTargets(ref target1, ref target2, camera, null, format, cameraTargetTexture, depthMaterial, flipY)); } else { req.data.SetFunctor(channel, (AsyncRequest <CaptureState> r) => { var target = SetupRenderTargets(ref target1, ref target2, camera, null, format, cameraTargetTexture, depthMaterial, flipY); if (GraphicsUtilities.SupportsAsyncReadback()) { AsyncGPUReadback.Request(target, 0, (AsyncGPUReadbackRequest request) => { ReleaseTargets(); if (request.hasError) { req.error = true; } else { if (functor != null) { req.data.SetBuffer(channel, request.GetData <byte>().ToArray()); req.Enqueue(functor); req.Execute(); } } }); } else { r.data.SetBuffer(channel, GraphicsUtilities.GetPixelsSlow(target)); ReleaseTargets(); req.Enqueue(functor); req.Execute(); } return(AsyncRequest.Result.None); }); } } else #endif // UNITY_2019_3_OR_NEWER { req.data.SetFunctor(channel, functor); CommandBuffer commandBuffer = GetCommandBufferForCamera(cameraEvent, camera); commandBuffer.name = $"CaptureCamera.{channel.ToString()}"; var target = SetupRenderTargets(ref target1, ref target2, camera, commandBuffer, format, cameraTargetTexture, depthMaterial, flipY); if (GraphicsUtilities.SupportsAsyncReadback()) { #if UNITY_2019_3_OR_NEWER if (CaptureOptions.useBatchReadback) { QueueForAsyncBatchReadback(req, channel, functor, target); ReleaseTargets(); } else #endif { commandBuffer.RequestAsyncReadback(target, (AsyncGPUReadbackRequest request) => { commandBuffer.Clear(); if (request.hasError) { req.error = true; } else { functor = req.data.SetFunctor(channel, null); if (functor != null) { req.data.SetBuffer(channel, request.GetData <byte>().ToArray()); req.Enqueue(functor); req.Execute(); } } ReleaseTargets(); }); } } else { Func <AsyncRequest <CaptureState>, AsyncRequest <CaptureState> .Result> wrapper; #if UNITY_2019_3_OR_NEWER if (CaptureOptions.useBatchReadback) { wrapper = (AsyncRequest <CaptureState> r) => { BatchReadback.Instance().QueueReadback(target, data => { r.data.SetBuffer(channel, data); ReleaseTargets(); r.Enqueue(functor); r.Execute(); return(AsyncRequest.Result.Completed); }); return(AsyncRequest.Result.Completed); }; } else #endif // UNITY_2019_3_OR_NEWER { wrapper = (AsyncRequest <CaptureState> r) => { r.data.SetBuffer(channel, GraphicsUtilities.GetPixelsSlow(target)); ReleaseTargets(); r.Enqueue(functor); r.Execute(); return(AsyncRequest.Result.Completed); }; } req.Enqueue(wrapper); req.Execute(AsyncRequest.ExecutionContext.EndOfFrame); } } } }