using System; using System.Collections; using System.Collections.Generic; using System.Linq; using NUnit.Framework; using Unity.Collections; using UnityEngine; using UnityEngine.Perception.GroundTruth; #if HDRP_PRESENT using UnityEngine.Rendering.HighDefinition; using UnityEngine.Experimental.Rendering; #endif using UnityEngine.TestTools; using Object = UnityEngine.Object; namespace GroundTruthTests { public class ImageReaderBehaviour : MonoBehaviour { public RenderTexture source; public Camera cameraSource; RenderTextureReader m_Reader; public event Action> SegmentationImageReceived; void Awake() { m_Reader = new RenderTextureReader(source, cameraSource, ImageReadCallback); } void ImageReadCallback(int frameCount, NativeArray data, RenderTexture renderTexture) { if (SegmentationImageReceived != null) SegmentationImageReceived(frameCount, data); } void OnDestroy() { m_Reader.Dispose(); m_Reader = null; } } //Graphics issues with OpenGL Linux Editor. https://jira.unity3d.com/browse/AISV-422 [UnityPlatform(exclude = new[] {RuntimePlatform.LinuxEditor, RuntimePlatform.LinuxPlayer})] public class SegmentationPassTests : GroundTruthTestBase { // A UnityTest behaves like a coroutine in Play Mode. In Edit Mode you can use // `yield return null;` to skip a frame. [UnityTest] public IEnumerator SegmentationPassTestsWithEnumeratorPasses() { int timesSegmentationImageReceived = 0; int? frameStart = null; Action> onSegmentationImageReceived = (frameCount, data) => { if (frameStart == null || frameStart > frameCount) return; timesSegmentationImageReceived++; CollectionAssert.AreEqual(Enumerable.Repeat(1, data.Length), data); }; var cameraObject = SetupCamera(onSegmentationImageReceived); // // // Arbitrary wait for 5 frames for shaders to load. Workaround for issue with Shader.WarmupAllShaders() // for (int i=0 ; i<5 ; ++i) // yield return new WaitForSeconds(1); frameStart = Time.frameCount; //Put a plane in front of the camera var planeObject = GameObject.CreatePrimitive(PrimitiveType.Plane); planeObject.transform.SetPositionAndRotation(new Vector3(0, 0, 10), Quaternion.Euler(90, 0, 0)); planeObject.transform.localScale = new Vector3(10, -1, 10); planeObject.AddComponent(); AddTestObjectForCleanup(planeObject); yield return null; yield return null; yield return null; yield return null; //destroy the object to force all pending segmented image readbacks to finish and events to be fired. DestroyTestObject(cameraObject); DestroyTestObject(planeObject); Assert.AreEqual(4, timesSegmentationImageReceived); } [UnityTest] public IEnumerator SegmentationPassProducesCorrectValuesEachFrame() { int timesSegmentationImageReceived = 0; Dictionary expectedLabelAtFrame = null; //TestHelper.LoadAndStartRenderDocCapture(out var gameView); Action> onSegmentationImageReceived = (frameCount, data) => { if (expectedLabelAtFrame == null || !expectedLabelAtFrame.ContainsKey(frameCount)) return; timesSegmentationImageReceived++; Debug.Log($"Segmentation image received. FrameCount: {frameCount}"); try { CollectionAssert.AreEqual(Enumerable.Repeat(expectedLabelAtFrame[frameCount], data.Length), data); } // ReSharper disable once RedundantCatchClause catch (Exception) { //uncomment to get RenderDoc captures while this check is failing //RenderDoc.EndCaptureRenderDoc(gameView); throw; } }; var cameraObject = SetupCamera(onSegmentationImageReceived); expectedLabelAtFrame = new Dictionary { {Time.frameCount , 1}, {Time.frameCount + 1, 1}, {Time.frameCount + 2, 1} }; GameObject planeObject; //Put a plane in front of the camera planeObject = TestHelper.CreateLabeledPlane(); yield return null; //UnityEditorInternal.RenderDoc.EndCaptureRenderDoc(gameView); Object.DestroyImmediate(planeObject); planeObject = TestHelper.CreateLabeledPlane(); //TestHelper.LoadAndStartRenderDocCapture(out gameView); yield return null; //UnityEditorInternal.RenderDoc.EndCaptureRenderDoc(gameView); Object.DestroyImmediate(planeObject); planeObject = TestHelper.CreateLabeledPlane(); yield return null; Object.DestroyImmediate(planeObject); yield return null; //destroy the object to force all pending segmented image readbacks to finish and events to be fired. DestroyTestObject(cameraObject); Assert.AreEqual(3, timesSegmentationImageReceived); } GameObject SetupCamera(Action> onSegmentationImageReceived) { var cameraObject = new GameObject(); cameraObject.SetActive(false); var camera = cameraObject.AddComponent(); camera.orthographic = true; camera.orthographicSize = 1; #if HDRP_PRESENT cameraObject.AddComponent(); var customPassVolume = cameraObject.AddComponent(); customPassVolume.isGlobal = true; var rt = new RenderTexture(128, 128, 1, GraphicsFormat.R8G8B8A8_UNorm); rt.Create(); var instanceSegmentationPass = new InstanceSegmentationPass(); instanceSegmentationPass.targetCamera = camera; instanceSegmentationPass.targetTexture = rt; customPassVolume.customPasses.Add(instanceSegmentationPass); instanceSegmentationPass.name = nameof(instanceSegmentationPass); instanceSegmentationPass.EnsureInit(); var reader = cameraObject.AddComponent(); reader.source = rt; reader.cameraSource = camera; reader.SegmentationImageReceived += onSegmentationImageReceived; #endif #if URP_PRESENT var labelingConfiguration = ScriptableObject.CreateInstance(); var perceptionCamera = cameraObject.AddComponent(); perceptionCamera.LabelingConfiguration = labelingConfiguration; perceptionCamera.captureRgbImages = false; perceptionCamera.produceBoundingBoxAnnotations = false; perceptionCamera.produceObjectCountAnnotations = true; perceptionCamera.segmentationImageReceived += onSegmentationImageReceived; #endif AddTestObjectForCleanup(cameraObject); cameraObject.SetActive(true); return cameraObject; } } }