#if !RIVE_USING_URP && !RIVE_USING_HDRP using System.Collections; using System.Collections.Generic; using Rive.Utils; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.SceneManagement; namespace Rive.Components.BuiltIn { public class BuiltInRenderPipelineHandler : MonoBehaviour, IRenderPipelineHandler { private Dictionary m_activeRenderCommandBuffers = new Dictionary(); [Tooltip("The cameras that will render the Rive content. If not provided, the main camera will be used.")] [SerializeField] private Camera m_renderCamera; private CommandBufferPool m_commandBufferPool; [SerializeField] private CameraEvent m_cameraEvent = CameraEvent.AfterEverything; private Coroutine m_setNewRenderCameraCoroutine; private List m_rendererCleanupList = new List(); private bool m_isDestroyed = false; private bool m_registeredWithOrchestrator = false; private Camera[] m_camerasInScene; /// /// The default capacity of the render pass pool. /// public int CommandBufferPoolDefaultCapacity { get; set; } = 5; /// /// The maximum size of the render pass pool. /// public int CommandBufferPoolMaxSize { get; set; } = 10000; /// /// The camera that will render the Rive content. /// public Camera RenderCamera { get { return m_renderCamera; } set => SetRenderCamera(value); } /// /// The current instance of the BuiltInRenderPipelineHandler in the scene. /// public static BuiltInRenderPipelineHandler Instance { get { return RenderPipelineHelper.CurrentHandler as BuiltInRenderPipelineHandler; } } protected virtual void OnEnable() { SceneManager.activeSceneChanged += ChangedActiveScene; TrySubscribeToOrchestrator(); } protected virtual void OnDisable() { SceneManager.activeSceneChanged -= ChangedActiveScene; UnsubscribeFromOrchestrator(); } private void TrySubscribeToOrchestrator() { if (m_registeredWithOrchestrator || !isActiveAndEnabled || m_activeRenderCommandBuffers.Count == 0) { return; } var orchestrator = Orchestrator.Instance; if (orchestrator != null) { orchestrator.OnPostRenderPreparation += ValidateRenderCamera; m_registeredWithOrchestrator = true; } } private void UnsubscribeFromOrchestrator() { if (!m_registeredWithOrchestrator) { return; } var orchestrator = Orchestrator.Instance; if (orchestrator != null) { orchestrator.OnPostRenderPreparation -= ValidateRenderCamera; } m_registeredWithOrchestrator = false; } private void ValidateRenderCamera() { if (m_renderCamera != null && !m_renderCamera.isActiveAndEnabled) { Camera newCamera = GetRenderCameraInScene(); if (newCamera != null) { RenderCamera = newCamera; } } } private void ChangedActiveScene(Scene arg0, Scene arg1) { // If the scene changes, it's likely the main camera was destroyed, we need to wait until there's one // This is also called on initial scene load when entering play mode if (m_setNewRenderCameraCoroutine == null) { m_setNewRenderCameraCoroutine = StartCoroutine(SetNewRenderCamera()); } } private IEnumerator SetNewRenderCamera() { // Wait until the scene is loaded while (!SceneManager.GetActiveScene().isLoaded) { yield return null; } if (m_renderCamera == null) { // Wait until there's a main camera in the scene Camera camera = null; while (camera == null) { camera = GetRenderCameraInScene(); if (camera == null) { yield return null; } } RenderCamera = camera; } m_setNewRenderCameraCoroutine = null; } public virtual RenderTexture AllocateRenderTexture(int width, int height) { RenderTextureDescriptor descriptor = TextureHelper.Descriptor(width, height); RenderTexture renderTexture = new RenderTexture(descriptor); return renderTexture; } /// /// Looks for the main camera in the scene to render the Rive content. /// /// private Camera GetRenderCameraInScene() { return CameraHelper.GetRenderCameraInScene(); } private void SetRenderCamera(Camera camera) { if (ReferenceEquals(m_renderCamera, camera)) { return; } // If we have a previous camera, remove the command buffers from it if (m_renderCamera != null) { foreach (var kvp in m_activeRenderCommandBuffers) { SafeRemoveCommandBuffer(m_renderCamera, kvp.Value); } } m_renderCamera = camera; if (m_renderCamera != null) { // Add existing command buffers to the new camera foreach (var kvp in m_activeRenderCommandBuffers) { AddCommandBufferToCamera(m_renderCamera, kvp.Value); } } } private void InitCommandBufferPoolIfNeeded() { if (m_commandBufferPool == null) { m_commandBufferPool = new CommandBufferPool(CommandBufferPoolDefaultCapacity, CommandBufferPoolMaxSize); } } public virtual void Register(IRenderer renderer) { if (m_isDestroyed) { return; } InitCommandBufferPoolIfNeeded(); if (renderer == null) { DebugLogger.Instance.LogWarning("Trying to register a null renderer."); return; } if (m_activeRenderCommandBuffers.ContainsKey(renderer)) { DebugLogger.Instance.LogWarning("Trying to register the same renderer twice."); return; } CommandBuffer commandBuffer = m_commandBufferPool.Get("RiveRenderPass"); renderer.AddToCommandBuffer(commandBuffer); m_activeRenderCommandBuffers.Add(renderer, commandBuffer); TrySubscribeToOrchestrator(); if (renderer is Renderer r && r.RenderQueue != null && r.RenderQueue.Texture != null) { InternalSetRenderTargetToCommandBufferIfNeeded(commandBuffer, r.RenderQueue.Texture); } Camera cameraToUse = RenderCamera; if (cameraToUse == null) { cameraToUse = GetRenderCameraInScene(); if (cameraToUse != null) { // If we found a camera in the scene, set it as the render camera // We do this immediately, instead of waiting for the coroutine to finish because we want the visuals to show up on the initial frame RenderCamera = cameraToUse; } else { // Schedule the camera to be set when available in the scene if (m_setNewRenderCameraCoroutine == null) { m_setNewRenderCameraCoroutine = StartCoroutine(SetNewRenderCamera()); } } } else { // If we already have a camera, add the new command buffer to it AddCommandBufferToCamera(cameraToUse, commandBuffer); } } public void SetRendererTexture(IRenderer renderer, RenderTexture renderTexture) { if (renderer == null) { DebugLogger.Instance.LogError("Cannot set texture on a null renderer."); return; } Renderer riveRenderer = renderer as Renderer; if (riveRenderer == null) { DebugLogger.Instance.LogError("Cannot set texture on a non-Rive renderer."); return; } riveRenderer.RenderQueue.UpdateTexture(renderTexture); if (m_activeRenderCommandBuffers.TryGetValue(renderer, out var commandBuffer)) { InternalSetRenderTargetToCommandBufferIfNeeded(commandBuffer, renderTexture); } } private void InternalSetRenderTargetToCommandBufferIfNeeded(CommandBuffer commandBuffer, RenderTexture renderTexture) { if (commandBuffer == null || renderTexture == null) { return; } if (TextureHelper.NeedsRenderTargetSetOnCommandBuffer()) { commandBuffer.SetRenderTarget(renderTexture); } } public virtual void Unregister(IRenderer renderer) { if (renderer == null) { return; } if (m_activeRenderCommandBuffers.TryGetValue(renderer, out var commandBuffer)) { SafeRemoveCommandBuffer(m_renderCamera, commandBuffer); m_activeRenderCommandBuffers.Remove(renderer); m_commandBufferPool.Release(commandBuffer); if (m_activeRenderCommandBuffers.Count == 0) { UnsubscribeFromOrchestrator(); } } } private void SafeRemoveCommandBuffer(Camera camera, CommandBuffer commandBuffer) { if (camera != null && commandBuffer != null) { camera.RemoveCommandBuffer(m_cameraEvent, commandBuffer); } } private void AddCommandBufferToCamera(Camera camera, CommandBuffer commandBuffer) { if (camera != null && commandBuffer != null) { camera.AddCommandBuffer(m_cameraEvent, commandBuffer); } } private void Cleanup() { m_isDestroyed = true; UnsubscribeFromOrchestrator(); if (m_setNewRenderCameraCoroutine != null) { StopCoroutine(m_setNewRenderCameraCoroutine); m_setNewRenderCameraCoroutine = null; } m_rendererCleanupList.Clear(); m_rendererCleanupList.AddRange(m_activeRenderCommandBuffers.Keys); for (int i = 0; i < m_rendererCleanupList.Count; i++) { Unregister(m_rendererCleanupList[i]); } } protected virtual void OnDestroy() { Cleanup(); } public bool IsRendererRegistered(IRenderer renderer) { return m_activeRenderCommandBuffers.ContainsKey(renderer); } public void ReleaseRenderTexture(RenderTexture renderTexture) { if (renderTexture != null) { renderTexture.Release(); } } public RenderTexture ResizeRenderTexture(RenderTexture renderTexture, int width, int height) { if (renderTexture == null) { DebugLogger.Instance.LogError("Cannot resize a null render texture."); return null; } renderTexture.Release(); renderTexture.width = width; renderTexture.height = height; renderTexture.Create(); return renderTexture; } } } #endif