From 1194c38568538207a3e1d22a4ea2e3849612f516 Mon Sep 17 00:00:00 2001 From: neuecc Date: Sun, 28 Jun 2020 17:59:03 +0900 Subject: [PATCH] Add AsyncGPUReadbackRequest await support --- .../UnityAsyncExtensions.AsyncGPUReadback.cs | 139 ++++++++++++++++++ ...tyAsyncExtensions.AsyncGPUReadback.cs.meta | 11 ++ src/UniTask/Assets/Scenes/SandboxMain.cs | 105 ++++++++++--- src/UniTask/Assets/Scenes/SandboxMain.unity | 80 +++++++++- 4 files changed, 314 insertions(+), 21 deletions(-) create mode 100644 src/UniTask/Assets/Plugins/UniTask/Runtime/UnityAsyncExtensions.AsyncGPUReadback.cs create mode 100644 src/UniTask/Assets/Plugins/UniTask/Runtime/UnityAsyncExtensions.AsyncGPUReadback.cs.meta diff --git a/src/UniTask/Assets/Plugins/UniTask/Runtime/UnityAsyncExtensions.AsyncGPUReadback.cs b/src/UniTask/Assets/Plugins/UniTask/Runtime/UnityAsyncExtensions.AsyncGPUReadback.cs new file mode 100644 index 0000000..7c8e5ff --- /dev/null +++ b/src/UniTask/Assets/Plugins/UniTask/Runtime/UnityAsyncExtensions.AsyncGPUReadback.cs @@ -0,0 +1,139 @@ +#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member + +using System; +using System.Threading; +using UnityEngine.Rendering; + +namespace Cysharp.Threading.Tasks +{ + public static partial class UnityAsyncExtensions + { + #region AsyncGPUReadbackRequest + + public static UniTask.Awaiter GetAwaiter(this AsyncGPUReadbackRequest asyncOperation) + { + return ToUniTask(asyncOperation).GetAwaiter(); + } + + public static UniTask WithCancellation(this AsyncGPUReadbackRequest asyncOperation, CancellationToken cancellationToken) + { + return ToUniTask(asyncOperation, cancellationToken: cancellationToken); + } + + public static UniTask ToUniTask(this AsyncGPUReadbackRequest asyncOperation, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken)) + { + if (asyncOperation.done) return UniTask.FromResult(asyncOperation); + return new UniTask(AsyncGPUReadbackRequestAwaiterConfiguredSource.Create(asyncOperation, timing, cancellationToken, out var token), token); + } + + sealed class AsyncGPUReadbackRequestAwaiterConfiguredSource : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode + { + static TaskPool pool; + public AsyncGPUReadbackRequestAwaiterConfiguredSource NextNode { get; set; } + + static AsyncGPUReadbackRequestAwaiterConfiguredSource() + { + TaskPool.RegisterSizeGetter(typeof(AsyncGPUReadbackRequestAwaiterConfiguredSource), () => pool.Size); + } + + AsyncGPUReadbackRequest asyncOperation; + CancellationToken cancellationToken; + + UniTaskCompletionSourceCore core; + + AsyncGPUReadbackRequestAwaiterConfiguredSource() + { + + } + + public static IUniTaskSource Create(AsyncGPUReadbackRequest asyncOperation, PlayerLoopTiming timing, CancellationToken cancellationToken, out short token) + { + if (cancellationToken.IsCancellationRequested) + { + return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token); + } + + if (!pool.TryPop(out var result)) + { + result = new AsyncGPUReadbackRequestAwaiterConfiguredSource(); + } + + result.asyncOperation = asyncOperation; + result.cancellationToken = cancellationToken; + + TaskTracker.TrackActiveTask(result, 3); + + PlayerLoopHelper.AddAction(timing, result); + + token = result.core.Version; + return result; + } + + public AsyncGPUReadbackRequest GetResult(short token) + { + try + { + return core.GetResult(token); + } + finally + { + TryReturn(); + } + } + + void IUniTaskSource.GetResult(short token) + { + GetResult(token); + } + + public UniTaskStatus GetStatus(short token) + { + return core.GetStatus(token); + } + + public UniTaskStatus UnsafeGetStatus() + { + return core.UnsafeGetStatus(); + } + + public void OnCompleted(Action continuation, object state, short token) + { + core.OnCompleted(continuation, state, token); + } + + public bool MoveNext() + { + if (cancellationToken.IsCancellationRequested) + { + core.TrySetCanceled(cancellationToken); + return false; + } + + if (asyncOperation.hasError) + { + core.TrySetException(new Exception("AsyncGPUReadbackRequest.hasError = true")); + return false; + } + + if (asyncOperation.done) + { + core.TrySetResult(asyncOperation); + return false; + } + + return true; + } + + bool TryReturn() + { + TaskTracker.RemoveTracking(this); + core.Reset(); + asyncOperation = default; + cancellationToken = default; + return pool.TryPush(this); + } + } + + #endregion + } +} \ No newline at end of file diff --git a/src/UniTask/Assets/Plugins/UniTask/Runtime/UnityAsyncExtensions.AsyncGPUReadback.cs.meta b/src/UniTask/Assets/Plugins/UniTask/Runtime/UnityAsyncExtensions.AsyncGPUReadback.cs.meta new file mode 100644 index 0000000..510c49e --- /dev/null +++ b/src/UniTask/Assets/Plugins/UniTask/Runtime/UnityAsyncExtensions.AsyncGPUReadback.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 98f5fedb44749ab4688674d79126b46a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/UniTask/Assets/Scenes/SandboxMain.cs b/src/UniTask/Assets/Scenes/SandboxMain.cs index d75fb3e..8a23443 100644 --- a/src/UniTask/Assets/Scenes/SandboxMain.cs +++ b/src/UniTask/Assets/Scenes/SandboxMain.cs @@ -15,6 +15,8 @@ using UnityEngine.LowLevel; using UnityEngine.Networking; using UnityEngine.UI; using UnityEngine.SceneManagement; +using UnityEngine.Rendering; +using System.IO; // using DG.Tweening; @@ -117,6 +119,8 @@ public class AsyncMessageBroker : IDisposable public class SandboxMain : MonoBehaviour { + public Camera camera; + public Button okButton; public Button cancelButton; @@ -161,6 +165,9 @@ public class SandboxMain : MonoBehaviour + + + //setHp = Hp.GetSetter(); } @@ -178,10 +185,6 @@ public class SandboxMain : MonoBehaviour public Button button; - void Start2() - { - - } @@ -437,29 +440,37 @@ public class SandboxMain : MonoBehaviour + async void Nanika() + { + await UniTask.Yield(); + Debug.Log("Here"); + throw new Exception(); + } + + + + + + + + private void Awake() + { + PlayerLoopInfo.Inject(); + PrepareCamera(); + } + + async UniTaskVoid Start() { - //_ = Foo(); // unhandled. - //Go(); - var cts = new CancellationTokenSource(); - okButton.onClick.AddListener(() => { - cts.Cancel(); + ShootAsync().Forget(); }); - - UnityEngine.Debug.Log("Start:" + PlayerLoopInfo.CurrentLoopType); - - var token = UniTask.Delay(TimeSpan.FromSeconds(3), DelayType.Realtime).ToCancellationToken(cts.Token); - while (!token.IsCancellationRequested) - { - UnityEngine.Debug.Log("in loop"); - await UniTask.Yield(); - } - UnityEngine.Debug.Log("end"); + // Nanika(); + await UniTask.Yield(); // this.GetCancellationTokenOnDestroy() //PlayerLoopInfo.Inject(); @@ -974,6 +985,62 @@ public class SandboxMain : MonoBehaviour // e.SetObserved(); // or other custom write code. UnityEngine.Debug.LogError("Unobserved:" + e.Exception.ToString()); + } + + + // GPU Screenshot Sample + + void PrepareCamera() + { + Debug.Log("Support AsyncGPUReadback:" + SystemInfo.supportsAsyncGPUReadback); + + var width = 480; + var height = 240; + var depth = 24; + + camera.targetTexture = new RenderTexture(width, height, depth, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Default) + { + antiAliasing = 8 + }; + camera.enabled = true; + + //myRenderTexture = new RenderTexture(width, height, depth, RenderTextureFormat.ARGB32, RenderTextureReadWrite.Default) + //{ + // antiAliasing = 8 + //}; + } + + RenderTexture myRenderTexture; + + async UniTask ShootAsync() + { + var rt = camera.targetTexture; + + + + var req = await AsyncGPUReadback.Request(rt, 0); + + Debug.Log("GPU Readback done?:" + req.done); + + var rawByteArray = req.GetData().ToArray(); + var graphicsFormat = rt.graphicsFormat; + var width = (uint)rt.width; + var height = (uint)rt.height; + + Debug.Log("BytesSize:" + rawByteArray.Length); + + + var imageBytes = ImageConversion.EncodeArrayToPNG(rawByteArray, graphicsFormat, width, height); + + + File.WriteAllBytes("my_screenshot.png", imageBytes); // test + + + + + + + } } diff --git a/src/UniTask/Assets/Scenes/SandboxMain.unity b/src/UniTask/Assets/Scenes/SandboxMain.unity index 8f96fdb..983e5d0 100644 --- a/src/UniTask/Assets/Scenes/SandboxMain.unity +++ b/src/UniTask/Assets/Scenes/SandboxMain.unity @@ -239,6 +239,80 @@ CanvasRenderer: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 16537670} m_CullTransparentMesh: 0 +--- !u!1 &518730348 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 518730350} + - component: {fileID: 518730349} + m_Layer: 0 + m_Name: Camera + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!20 &518730349 +Camera: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 518730348} + m_Enabled: 1 + serializedVersion: 2 + m_ClearFlags: 1 + m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} + m_projectionMatrixMode: 1 + m_GateFitMode: 2 + m_FOVAxisMode: 0 + m_SensorSize: {x: 36, y: 24} + m_LensShift: {x: 0, y: 0} + m_FocalLength: 50 + m_NormalizedViewPortRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + near clip plane: 0.3 + far clip plane: 1000 + field of view: 60 + orthographic: 0 + orthographic size: 5 + m_Depth: 0 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingPath: -1 + m_TargetTexture: {fileID: 0} + m_TargetDisplay: 0 + m_TargetEye: 3 + m_HDR: 1 + m_AllowMSAA: 1 + m_AllowDynamicResolution: 0 + m_ForceIntoRT: 0 + m_OcclusionCulling: 1 + m_StereoConvergence: 10 + m_StereoSeparation: 0.022 +--- !u!4 &518730350 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 518730348} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 488, y: 418, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &519420028 GameObject: m_ObjectHideFlags: 0 @@ -278,9 +352,11 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: f0bc6c75abb2e0b47a25aa49bfd488ed, type: 3} m_Name: m_EditorClassIdentifier: + camera: {fileID: 518730349} okButton: {fileID: 16537672} cancelButton: {fileID: 628393011} text: {fileID: 2101290655} + button: {fileID: 0} --- !u!20 &519420031 Camera: m_ObjectHideFlags: 0 @@ -597,7 +673,7 @@ Transform: m_LocalScale: {x: 1, y: 1, z: 1} m_Children: [] m_Father: {fileID: 0} - m_RootOrder: 1 + m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &1556045504 GameObject: @@ -693,7 +769,7 @@ RectTransform: - {fileID: 628393010} - {fileID: 2101290654} m_Father: {fileID: 0} - m_RootOrder: 2 + m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 0, y: 0}