using System.Reflection; using Cysharp.Threading.Tasks; using HarmonyLib; using UnityEngine; namespace ADOFAIRenderer.Core; /// /// Per-frame hook attached to the scrCamera GameObject by RenderManager when the game scene of a render has loaded, /// and destroyed again when a render is cancelled or fails. /// Execution order int.MaxValue: its LateUpdate runs after every other script's LateUpdate of the frame. /// Each frame it calls RenderManager.OnLastUpdate (LateUpdate) and, after the frame has been rendered /// (end of frame), RenderManager.OnPostFrame with the texture shown on scrCamera's camera quad, which is the frame /// that gets captured. /// Reference: https://docs.unity3d.com/ScriptReference/DefaultExecutionOrder.html /// [DefaultExecutionOrder(int.MaxValue)] internal class PostRenderHook : MonoBehaviour { /// Private field scrCamera.camQuadMesh: the MeshRenderer of the quad that displays the camera RenderTexture. private FieldInfo? _camQuadMeshField; /// Cached value of camQuadMesh for the current scrCamera instance. Looked up again while it is null or destroyed. private MeshRenderer? _camQuadMeshRenderer; private void Awake() { _camQuadMeshField = AccessTools.Field(typeof(scrCamera), "camQuadMesh"); } /// /// Main thread, once per frame after all other LateUpdate calls. Runs the last-update work of RenderManager /// (OnLastUpdate callbacks of the session) and schedules the end-of-frame capture. /// private void LateUpdate() { if (RenderManager.Instance) RenderManager.Instance.OnLastUpdate(); PostFrameAsync().Forget(); } /// /// Waits until the end of the current frame (after the cameras have rendered) and passes the camera quad's main /// texture to RenderManager.OnPostFrame. The wait is cancelled when this component is destroyed. /// The texture is null when the quad's material has no RenderTexture; OnPostFrame then only updates the audio. /// private async UniTaskVoid PostFrameAsync() { await UniTask.WaitForEndOfFrame(this, destroyCancellationToken); if (!_camQuadMeshRenderer && scrCamera.instance) _camQuadMeshRenderer = _camQuadMeshField?.GetValue(scrCamera.instance) as MeshRenderer; if (RenderManager.Instance && _camQuadMeshRenderer) RenderManager.Instance.OnPostFrame(_camQuadMeshRenderer.sharedMaterial.mainTexture as RenderTexture); } }