static void QueueForAsyncBatchReadback(AsyncRequest <CaptureState> req, Channel channel, Func <AsyncRequest <CaptureState>, AsyncRequest.Result> functor, RenderTexture target) { Func <AsyncRequest <CaptureState>, AsyncRequest <CaptureState> .Result> wrapper; wrapper = (AsyncRequest <CaptureState> r) => { BatchReadback.Instance().QueueReadback(target, bytes => { if (functor != null) { r.data.SetBuffer(channel, bytes); r.Enqueue(functor); r.Execute(); } return(AsyncRequest.Result.Completed); }); return(AsyncRequest.Result.Completed); }; req.Enqueue(wrapper); req.Execute(AsyncRequest.ExecutionContext.EndOfFrame); }
ReadbackRequest GetReadBackRequestFromPool(AsyncRequest <CaptureCamera.CaptureState> request, CaptureCamera.Channel channel, RenderTexture renderTexture) { ReadbackRequest rbr; if (_requestsPool.Count > 0) { rbr = _requestsPool.Dequeue(); } else { rbr = new ReadbackRequest(); } rbr.request = request; rbr.channel = channel; rbr.callback = request.data.SetFunctor(channel, null); if (rbr.renderTexture == null || rbr.renderTexture.width != renderTexture.width || rbr.renderTexture.height != renderTexture.height || !rbr.renderTexture.CompareFormat(renderTexture.graphicsFormat)) { rbr.renderTexture = new RenderTexture(renderTexture); } Graphics.Blit(renderTexture, rbr.renderTexture); return(rbr); }
/// <summary> /// Setup a capture request for a channel. Once completed, the functor will be called with the channel data, in the format requested. /// </summary> /// <param name="request"> AsyncRequest to enqueue readbacks to. When all are completed, the request is marked completed. </param> /// <param name="channel"> The channel to capture data from (color, depth etc.) </param> /// <param name="camera"> The Camera to capture data from. </param> /// <param name="format"> The graphics format you want the data to be in. </param> /// <param name="functor"> The completion functor to call with the data. </param> /// <param name="forceFlipY"> Flags allowing you to force flipY for arbitrary channels. </param> /// <param name="readWrite"> Specify the desired color space conversion. If Default, then will be set to sRGB for SRP Color channel. </param> public static void SetupCaptureRequest ( AsyncRequest <CaptureState> request, Channel channel, Camera camera, GraphicsFormat format, Func <AsyncRequest <CaptureState>, AsyncRequest.Result> functor, ForceFlip forceFlipY, RenderTextureReadWrite readWrite = RenderTextureReadWrite.Default ) { request.data.SetFunctor(channel, functor); Debug.Assert(request.data.camera == camera, "Capture: Camera must match the camera in request.data.camera"); Debug.Assert(GraphicsUtilities.SupportsRenderTextureFormat(format), $"Capture: GraphicsFormat {format} not supported for {channel} channel"); var material = SelectShaderVariantForChannel(channel, format); if (scriptableRenderPipeline) { request.data.SetTrigger(channel, (cb, rtid) => SetupCaptureRequestCommandBufferForChannel(request, channel, camera, cb, rtid, material, format, forceFlipY, readWrite, HandleReadbackCompletion)); } else { SetupCaptureRequestCommandBufferForChannel(request, channel, camera, null, default, material, format, forceFlipY, readWrite, HandleReadbackCompletion);
/// <summary> /// With different rendering pipelines, the moment when you need to capture a camera migh be different. /// This method will allow for the CaptureCamera class to operate as normal, while allowing the author /// of the render pipeline to decide when the work get dispatched. /// </summary> /// <param name="camera">The camera that you wish to queue a request for.</param> /// <param name="request">The request you are queueing for this camera.</param> public void QueueCameraRequest(Camera camera, AsyncRequest <CaptureCamera.CaptureState> request) { if (!_instance._pendingCameraRequests.ContainsKey(camera)) { _instance._pendingCameraRequests.Add(camera, new List <AsyncRequest <CaptureCamera.CaptureState> >()); } _instance._pendingCameraRequests[camera].Add(request); }
/// <summary> /// Queue a rendertexture for readback. The readback will happen after the number of requests reaches the batchsize. /// </summary> /// <param name="request">The request associated with this batch readback instance.</param> /// <param name="channel">Which channel this readback is for.</param> /// <param name="renderTexture">Render texture to readback.</param> public void QueueReadback(AsyncRequest <CaptureCamera.CaptureState> request, CaptureCamera.Channel channel, RenderTexture renderTexture) { Debug.Assert(request.data.GetFunctor(channel) != null, $"QueueReadback request has no completion function for {channel} channel"); var rbr = GetReadBackRequestFromPool(request, channel, renderTexture); _requestsBatch.Enqueue(rbr); if (_requestsBatch.Count == BatchSize) { Flush(); } }
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); } } } }