293 lines
13 KiB
C#
Raw Normal View History

2024-12-26 14:17:41 +08:00
/*
* Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved.
*
* NVIDIA CORPORATION and its licensors retain all intellectual property
* and proprietary rights in and to this software, related documentation
* and any modifications thereto. Any use, reproduction, disclosure or
* distribution of this software and related documentation without an express
* license agreement from NVIDIA CORPORATION is strictly prohibited.
*/
using System;
using System.Collections;
using System.Runtime.InteropServices;
using UnityEngine;
using UnityEngine.Rendering;
using NVStreamline;
namespace NVStreamline
{
[AddComponentMenu("NVIDIA/Streamline Reflex Builtin & HDRP")]
public class StreamlineReflex : MonoBehaviour
{
[Tooltip("Is this the first camera to be rendered")]
public bool isFirstCamera = true;
private bool isAttachedBeforeRender = false;
[Tooltip("Is this the last camera to be rendered")]
public bool isLastCamera = true;
private bool isAttachedAfterRender = false;
[Tooltip("Minimum interval in microseconds for frame synchronization. 0 = no frame rate limit")]
public uint intervalUs = 0;
[Tooltip("NVIDIA Reflex Low Latency mode.")]
public bool isLowLatencyMode = true;
[Tooltip("NVIDIA Reflex Low Latency Boost")]
public bool isLowLatencyBoost = false;
[Tooltip("NVIDIA Reflex Low Latency Boost")]
public bool useMarkersToOptimize = false;
private static uint previousIntervalUs = 0;
private static bool previousIsLowLatencyMode = false;
private static bool previousIsLowLatencyBoost = true;
private static bool previousUseMarkersToOptimize = false;
private bool hasSimulationStarted = false;
private CommandBuffer renderSubmitStart_CommandBuffer;
private CommandBuffer renderSubmitEnd_CommandBuffer;
public bool isIgnored = false; // Used by DLSS plugin for downsampled camera.
private uint m_id;
private bool m_reflexEnabled = false;
private IEnumerator SleepCoroutine()
{
while (true)
{
yield return (new WaitForEndOfFrame());
if (StreamlineReflexCore.NvReflex_Plugin_HasReceivedRenderEvent() != 0)
{
if (isLastCamera)
{
if ((previousIsLowLatencyMode != isLowLatencyMode) || (previousIntervalUs != intervalUs) || (previousIsLowLatencyBoost != isLowLatencyBoost) || (previousUseMarkersToOptimize != useMarkersToOptimize))
{
StreamlineReflexCore.NvReflex_Plugin_SetSleepMode(isLowLatencyMode, intervalUs, isLowLatencyBoost, useMarkersToOptimize);
previousIsLowLatencyMode = isLowLatencyMode;
previousIsLowLatencyBoost = isLowLatencyBoost;
previousIntervalUs = intervalUs;
previousUseMarkersToOptimize = useMarkersToOptimize;
}
if (isLowLatencyMode)
{
GL.Flush();
StreamlineReflexCore.NvReflex_Plugin_Sleep((ulong)Time.frameCount);
}
}
}
}
}
private void Awake()
{
if (!StreamlineCore.IsFeatureSupported(StreamlineCore.Feature.eFeatureReflex))
{
Debug.Log("Reflex is not supported");
return;
}
int id = StreamlineCore.CreateFeature(StreamlineCore.Feature.eFeatureReflex);
if (id != 1) //CreateFeature must return 1 for Reflex
{
Debug.Log("Reflex Awake FAILED to get feature id");
m_reflexEnabled = false;
return;
}
m_id = (uint)id;
m_reflexEnabled = true;
renderSubmitStart_CommandBuffer = new CommandBuffer();
renderSubmitStart_CommandBuffer.name = "Reflex RENDERSUBMIT_START";
renderSubmitEnd_CommandBuffer = new CommandBuffer();
renderSubmitEnd_CommandBuffer.name = "Reflex RENDERSUBMIT_END";
}
void OnDestroy()
{
StreamlineCore.DestroyFeature((int)m_id);
}
private void OnBeginFrameRendering(ScriptableRenderContext context, Camera[] camera)
{
context.ExecuteCommandBuffer(renderSubmitStart_CommandBuffer);
}
private void OnEndFrameRendering(ScriptableRenderContext context, Camera[] camera)
{
context.ExecuteCommandBuffer(renderSubmitEnd_CommandBuffer);
context.Submit();
}
private void AfterPresent()
{
if (hasSimulationStarted == false)
{
int simStartEventID = StreamlineReflexCore.NvReflex_Plugin_GetEventIDForEvent((int)StreamlineReflexCore.NvReflex_LATENCY_MARKER_TYPE.SIMULATION_START);
StreamlineReflexCore.SetLatencyMarker(simStartEventID, (ulong)Time.frameCount);
hasSimulationStarted = true;
}
}
public void FixedUpdate()
{
if (m_reflexEnabled)
{
if (isFirstCamera &&
!isIgnored &&
StreamlineReflexCore.NvReflex_Plugin_HasReceivedRenderEvent() != 0)
{
AfterPresent();
}
}
}
public void Update()
{
if (m_reflexEnabled)
{
if (isFirstCamera &&
!isIgnored &&
StreamlineReflexCore.NvReflex_Plugin_HasReceivedRenderEvent() != 0)
{
AfterPresent();
int inputSampleEventID = StreamlineReflexCore.NvReflex_Plugin_GetEventIDForEvent((int)StreamlineReflexCore.NvReflex_LATENCY_MARKER_TYPE.INPUT_SAMPLE);
StreamlineReflexCore.SetLatencyMarker(inputSampleEventID, (ulong)Time.frameCount);
if (Input.GetKeyUp(KeyCode.F13))
{
StreamlineReflexCore.PCLatencyPing();
}
if (Input.GetMouseButtonDown(0))
{
StreamlineReflexCore.TriggerFlash();
}
}
}
}
private void AttachDetachCommandBuffers()
{
if ((renderSubmitStart_CommandBuffer != null) & (renderSubmitEnd_CommandBuffer != null))
{
Camera camComponent = this.GetComponent<Camera>();
bool shouldFirstCameraBeAttached = (isFirstCamera & !isIgnored & enabled);
bool shouldLastCameraBeAttached = (isLastCamera & /*!isIgnored &*/ enabled);
if (shouldFirstCameraBeAttached != isAttachedBeforeRender)
{
if (shouldFirstCameraBeAttached)
{
// Send renderSubmitStart_CommandBuffer on BeforeDepthTexture, BeforeForwardOpaque and BeforeGBuffer events. This ensures whatever configuration the Legacy pipeline is using
// with regard to 2D/3D and Deferred/Rendering the event will be sent. The plugin filters the SubmitStart event and only processes the first that occurs.
var oldBDCmdBuffers = camComponent.GetCommandBuffers(CameraEvent.BeforeDepthTexture);
var oldBOCmdBuffers = camComponent.GetCommandBuffers(CameraEvent.BeforeForwardOpaque);
var oldBDGmdBuffers = camComponent.GetCommandBuffers(CameraEvent.BeforeGBuffer);
camComponent.RemoveCommandBuffers(CameraEvent.BeforeDepthTexture);
camComponent.RemoveCommandBuffers(CameraEvent.BeforeForwardOpaque);
camComponent.RemoveCommandBuffers(CameraEvent.BeforeGBuffer);
camComponent.AddCommandBuffer(CameraEvent.BeforeDepthTexture, renderSubmitStart_CommandBuffer);
camComponent.AddCommandBuffer(CameraEvent.BeforeForwardOpaque, renderSubmitStart_CommandBuffer);
camComponent.AddCommandBuffer(CameraEvent.BeforeGBuffer, renderSubmitStart_CommandBuffer);
foreach (var cmdBuff in oldBDCmdBuffers)
{
camComponent.AddCommandBuffer(CameraEvent.BeforeDepthTexture, cmdBuff);
}
foreach (var cmdBuff in oldBOCmdBuffers)
{
camComponent.AddCommandBuffer(CameraEvent.BeforeForwardOpaque, cmdBuff);
}
foreach (var cmdBuff in oldBDGmdBuffers)
{
camComponent.AddCommandBuffer(CameraEvent.BeforeGBuffer, cmdBuff);
}
#if UNITY_2019_1_OR_NEWER
RenderPipelineManager.beginFrameRendering += OnBeginFrameRendering;
#endif
}
else
{
camComponent.RemoveCommandBuffer(CameraEvent.BeforeDepthTexture, renderSubmitStart_CommandBuffer);
camComponent.RemoveCommandBuffer(CameraEvent.BeforeForwardOpaque, renderSubmitStart_CommandBuffer);
camComponent.RemoveCommandBuffer(CameraEvent.BeforeGBuffer, renderSubmitStart_CommandBuffer);
#if UNITY_2019_1_OR_NEWER
RenderPipelineManager.beginFrameRendering -= OnBeginFrameRendering;
#endif
}
isAttachedBeforeRender = shouldFirstCameraBeAttached;
}
if (shouldLastCameraBeAttached != isAttachedAfterRender)
{
if (shouldLastCameraBeAttached)
{
camComponent.AddCommandBuffer(CameraEvent.AfterEverything, renderSubmitEnd_CommandBuffer);
#if UNITY_2019_1_OR_NEWER
RenderPipelineManager.endFrameRendering += OnEndFrameRendering;
#endif
}
else
{
camComponent.RemoveCommandBuffer(CameraEvent.AfterEverything, renderSubmitEnd_CommandBuffer);
#if UNITY_2019_1_OR_NEWER
RenderPipelineManager.endFrameRendering -= OnEndFrameRendering;
#endif
}
isAttachedAfterRender = shouldLastCameraBeAttached;
}
}
}
void OnEnable()
{
if (m_reflexEnabled)
{
AttachDetachCommandBuffers();
StartCoroutine(SleepCoroutine());
}
}
void OnDisable()
{
if (m_reflexEnabled)
{
AttachDetachCommandBuffers();
StopCoroutine(SleepCoroutine());
}
}
private void LateUpdate()
{
if (m_reflexEnabled)
{
AttachDetachCommandBuffers();
if (isFirstCamera & !isIgnored)
{
if (hasSimulationStarted &&
StreamlineReflexCore.NvReflex_Plugin_HasReceivedRenderEvent() != 0)
{
int simEndEventID = StreamlineReflexCore.NvReflex_Plugin_GetEventIDForEvent((int)StreamlineReflexCore.NvReflex_LATENCY_MARKER_TYPE.SIMULATION_END);
StreamlineReflexCore.SetLatencyMarker(simEndEventID, (ulong)Time.frameCount);
hasSimulationStarted = false;
}
int startEventID = StreamlineReflexCore.NvReflex_Plugin_GetEventIDForEvent((int)StreamlineReflexCore.NvReflex_LATENCY_MARKER_TYPE.RENDERSUBMIT_START);
renderSubmitStart_CommandBuffer.Clear();
renderSubmitStart_CommandBuffer.IssuePluginEventAndData(StreamlineReflexCore.NvReflex_Plugin_GetRenderEventAndDataFunc(), startEventID, (IntPtr)(ulong)Time.frameCount);
}
if (isLastCamera /*& !isIgnored*/)
{
int endEventID = StreamlineReflexCore.NvReflex_Plugin_GetEventIDForEvent((int)StreamlineReflexCore.NvReflex_LATENCY_MARKER_TYPE.RENDERSUBMIT_END);
renderSubmitEnd_CommandBuffer.Clear();
renderSubmitEnd_CommandBuffer.IssuePluginEventAndData(StreamlineReflexCore.NvReflex_Plugin_GetRenderEventAndDataFunc(), endEventID, (IntPtr)(ulong)Time.frameCount);
endEventID = StreamlineReflexCore.NvReflex_Plugin_GetEventIDForEvent((int)StreamlineReflexCore.NvReflex_LATENCY_MARKER_TYPE.PRESENT_START);
renderSubmitEnd_CommandBuffer.IssuePluginEventAndData(StreamlineReflexCore.NvReflex_Plugin_GetRenderEventAndDataFunc(), endEventID, (IntPtr)(ulong)Time.frameCount);
endEventID = StreamlineReflexCore.NvReflex_Plugin_GetEventIDForEvent((int)StreamlineReflexCore.NvReflex_LATENCY_MARKER_TYPE.PRESENT_END);
renderSubmitEnd_CommandBuffer.IssuePluginEventAndData(StreamlineReflexCore.NvReflex_Plugin_GetRenderEventAndDataFunc(), endEventID, (IntPtr)(ulong)Time.frameCount);
}
}
}
}
}