diff --git a/Packages/com.unity.render-pipelines.core/Editor-PrivateShared/Unity.RenderPipelines.Core.Editor.Shared.asmdef b/Packages/com.unity.render-pipelines.core/Editor-PrivateShared/Unity.RenderPipelines.Core.Editor.Shared.asmdef index ef7b83146e1..130e0c31164 100644 --- a/Packages/com.unity.render-pipelines.core/Editor-PrivateShared/Unity.RenderPipelines.Core.Editor.Shared.asmdef +++ b/Packages/com.unity.render-pipelines.core/Editor-PrivateShared/Unity.RenderPipelines.Core.Editor.Shared.asmdef @@ -2,7 +2,13 @@ "name": "Unity.RenderPipelines.Core.Editor.Shared", "rootNamespace": "", "references": [ - "GUID:3eae0364be2026648bf74846acb8a731" + "GUID:3eae0364be2026648bf74846acb8a731", + "GUID:716365d8000d25e4c8621aa2d34a92fa", + "GUID:9149a3cbb036049f68e4e5b6388b9c85", + "GUID:c49c619b6af2be941af9bcbca2641964", + "GUID:df380645f10b7bc4b97d4f5eb6303d95", + "GUID:4fd6538c1c56b409fb53fdf0183170ec", + "GUID:214c0945bb158c940aada223f3223ee8" ], "includePlatforms": [ "Editor" diff --git a/Packages/com.unity.render-pipelines.core/Editor/GPUDriven.meta b/Packages/com.unity.render-pipelines.core/Editor/GPUDriven.meta new file mode 100644 index 00000000000..f6effe0b3ad --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Editor/GPUDriven.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 33bc05f8901a90242bd198cc0d48b301 diff --git a/Packages/com.unity.render-pipelines.core/Editor/GPUDriven/GPUInstanceDataBufferViewer.cs b/Packages/com.unity.render-pipelines.core/Editor/GPUDriven/GPUInstanceDataBufferViewer.cs new file mode 100644 index 00000000000..351875550c6 --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Editor/GPUDriven/GPUInstanceDataBufferViewer.cs @@ -0,0 +1,77 @@ +using UnityEditor; + +namespace UnityEngine.Rendering +{ + //@ This is a simple instance data viewer class used for instance data debugging. + //@ This could become a user facing tool in the future. Where the user could see the instance data in the GPU. + //@ It could show layouts, archetypes, components and instance data. + internal class GPUInstanceDataBufferViewer : EditorWindow + { + [SerializeField] private Vector2 m_ScrollPos; + + public static void InitializeWindow() + { + GPUInstanceDataBufferViewer wnd = GetWindow(); + wnd.titleContent = new GUIContent("GPU Resident Instance Data Viewer"); + } + + public void OnGUI() + { + if (!GPUResidentDrawer.IsInitialized()) + { + EditorGUILayout.LabelField("GPU Resident Drawer is not enabled in Render Pipeline Asset."); + return; + } + + m_ScrollPos = EditorGUILayout.BeginScrollView(m_ScrollPos); + + //@ This is usefully for internal GPU Resident Drawer debugging. + + ref GPUArchetypeManager archetypeMgr = ref GPUResidentDrawer.GetGPUArchetypeManager().GetRef(); + ref DefaultGPUComponents defaultGPUComponents = ref GPUResidentDrawer.GetDefaultGPUComponents(); + var buffer = GPUResidentDrawer.GetInstanceDataBuffer(); + var readback = GPUResidentDrawer.ReadbackInstanceDataBuffer(); + + EditorGUILayout.LabelField($"Archetypes Count: {buffer.layout.archetypes.Length}"); + + for (int i = 0; i < buffer.layout.archetypes.Length; ++i) + { + var archetype = buffer.layout.archetypes[i]; + var archetypeDesc = archetypeMgr.GetArchetypeDesc(archetype); + EditorGUILayout.LabelField($"Archetype {i}, ID: {archetype.index}"); + EditorGUILayout.LabelField("Components:"); + for (int j = 0; j < archetypeDesc.components.Length; ++j) + { + var component = archetypeDesc.components[j]; + var componentDesc = archetypeMgr.GetComponentDesc(component); + + // Replace when Shader.PropertyIDToName API lands + //var componentName = Shader.PropertyIDToName(componentDesc.propertyID); + string componentName = ""; + EditorGUILayout.LabelField($"Component {j} - Name: {componentName}, ID: {component.index}"); + } + } + + for (int archIndex = 0; archIndex < buffer.layout.archetypes.Length; ++archIndex) + { + var archetype = buffer.layout.archetypes[archIndex]; + var archetypeDesc = archetypeMgr.GetArchetypeDesc(archetype); + + EditorGUILayout.LabelField($"====== Archetype {archetype} Instance Data ======"); + + var count = buffer.layout.instancesCount[archIndex]; + for (int instanceIndex = 0; instanceIndex < count; ++instanceIndex) + { + GPUInstanceIndex gpuIndex = buffer.InstanceGPUHandleToGPUIndex(InstanceGPUHandle.Create(archetype, instanceIndex)); + PackedMatrix objectToWorld = readback.LoadData(defaultGPUComponents.objectToWorld, gpuIndex); + EditorGUILayout.LabelField($"Instance {instanceIndex}; GPU Index: {gpuIndex.index}; ObjectToWorld {objectToWorld}"); + //@ Read and display more instance data here if needed... + } + } + + readback.Dispose(); + + EditorGUILayout.EndScrollView(); + } + } +} diff --git a/Packages/com.unity.render-pipelines.core/Editor/GPUDriven/GPUInstanceDataBufferViewer.cs.meta b/Packages/com.unity.render-pipelines.core/Editor/GPUDriven/GPUInstanceDataBufferViewer.cs.meta new file mode 100644 index 00000000000..c249d941e15 --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Editor/GPUDriven/GPUInstanceDataBufferViewer.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: d4b89c964238d1d47a1f41b93628fc84 \ No newline at end of file diff --git a/Packages/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/AdaptiveProbeVolumes.BakePipelineDriver.cs b/Packages/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/AdaptiveProbeVolumes.BakePipelineDriver.cs index 3880a1392be..0ae9763b993 100644 --- a/Packages/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/AdaptiveProbeVolumes.BakePipelineDriver.cs +++ b/Packages/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/AdaptiveProbeVolumes.BakePipelineDriver.cs @@ -1,36 +1,30 @@ using System; +using System.Collections.Generic; using System.Reflection; using System.Runtime.CompilerServices; -using UnityEditor.LightBaking; namespace UnityEngine.Rendering { partial class AdaptiveProbeVolumes { // This class is used to (1) access the internal class UnityEditor.LightBaking.BakePipelineDriver and (2) provide a slightly higher level API. - private sealed class BakePipelineDriver : IDisposable + sealed class BakePipelineDriver : IDisposable { - // Keep in sync with the enum BakePipeline::Run::StageName - public enum StageName : int - { - Initialized, - Preprocess, - Bake, - PostProcess, - AdditionalBake, - Done - } - - private readonly object _bakePipelineDriver; - private readonly Type _bakePipelineDriverType; + readonly object m_BakePipelineDriver; + readonly Type m_BakePipelineDriverType; + readonly Type m_StageNameType; internal BakePipelineDriver() { - _bakePipelineDriverType = Type.GetType("UnityEditor.LightBaking.BakePipelineDriver, UnityEditor"); - bool newed = _bakePipelineDriverType != null; - Debug.Assert(newed, "Unexpected, could not find the type UnityEditor.LightBaking.BakePipelineDriver"); - _bakePipelineDriver = newed ? Activator.CreateInstance(_bakePipelineDriverType) : null; - Debug.Assert(_bakePipelineDriver != null, "Unexpected, could not new up a BakePipelineDriver"); + Debug.Assert(UnityEditorInternal.InternalEditorUtility.CurrentThreadIsMainThread()); + + m_BakePipelineDriverType = Type.GetType("UnityEditor.LightBaking.BakePipelineDriver, UnityEditor"); + Debug.Assert(m_BakePipelineDriverType != null, "Unexpected, could not find the type UnityEditor.LightBaking.BakePipelineDriver"); + m_StageNameType = m_BakePipelineDriverType.GetNestedType("StageName", BindingFlags.NonPublic | BindingFlags.Public); + Debug.Assert(m_StageNameType is { IsEnum: true }, "Unexpected, could not find the nested enum StageName on BakePipelineDriver"); + Debug.Assert(IsStageNameEnumConsistent(m_StageNameType), "Unexpected, StageName enum is not consistent with BakePipelineDriver.StageName enum"); + m_BakePipelineDriver = Activator.CreateInstance(m_BakePipelineDriverType, nonPublic: true); + Debug.Assert(m_BakePipelineDriver != null, "Unexpected, could not new up a BakePipelineDriver"); } internal void StartBake(bool enablePatching, ref float progress, ref StageName stage) @@ -51,36 +45,78 @@ internal bool RunInProgress() internal void Step(ref float progress, ref StageName stage) => Update(true, true, true, out progress, out stage); - private void SetEnableBakedLightmaps(bool enable) => + void SetEnableBakedLightmaps(bool enable) => InvokeMethod(new object[] { enable }, out _); - private void SetEnablePatching(bool enable) => + void SetEnablePatching(bool enable) => InvokeMethod(new object[] { enable }, out _); - private void Update(bool isOnDemandBakeInProgress, bool isOnDemandBakeAsync, bool shouldBeRunning, + void Update(bool isOnDemandBakeInProgress, bool isOnDemandBakeAsync, bool shouldBeRunning, out float progress, out StageName stage) { - object[] parameters = { isOnDemandBakeInProgress, isOnDemandBakeAsync, shouldBeRunning, -1.0f, -1 }; + progress = -1.0f; + stage = StageName.Invalid; + object[] parameters = { isOnDemandBakeInProgress, isOnDemandBakeAsync, shouldBeRunning, progress, + Enum.ToObject(m_StageNameType, (int)stage) }; InvokeMethod(parameters, out _); progress = (float)parameters[3]; - stage = (StageName)parameters[4]; + stage = (StageName)Convert.ToInt32(parameters[4]); } public void Dispose() => InvokeMethod(new object[] { }, out _); - private bool InvokeMethod(object[] parameters, out object result, [CallerMemberName] string methodName = "") + bool InvokeMethod(object[] parameters, out object result, [CallerMemberName] string methodName = "") { - MethodInfo methodInfo = _bakePipelineDriverType.GetMethod(methodName, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); + MethodInfo methodInfo = m_BakePipelineDriverType.GetMethod(methodName, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); bool gotMethod = methodInfo != null; Debug.Assert(gotMethod, $"Unexpected, could not find {methodName} on BakePipelineDriver"); - if (!gotMethod) - { - result = null; + + result = methodInfo.Invoke(m_BakePipelineDriver, parameters); + + return true; + } + + // Keep this in sync with the enum in Editor\Src\GI\BakePipeline\BakePipeline.bindings.h + public enum StageName + { + Invalid = -1, + Initialized = 0, + Preprocess = 1, + PreprocessProbes = 2, + Bake = 3, + PostProcess = 4, + AdditionalBake = 5, + Done = 6 + } + + // If StageName is not kept in sync, this should return false + static bool IsStageNameEnumConsistent(Type otherType) + { + string[] ourNames = Enum.GetNames(typeof(StageName)); + string[] otherNames = Enum.GetNames(otherType); + + if (ourNames.Length != otherNames.Length) return false; - } - result = methodInfo.Invoke(_bakePipelineDriver, parameters); + Array ourValues = Enum.GetValues(typeof(StageName)); + Array otherValues = Enum.GetValues(otherType); + + // Brute-force compare each local name against the external by lookup + for (int i = 0; i < ourNames.Length; i++) + { + string name = ourNames[i]; + + int otherIndex = Array.IndexOf(otherNames, name); + if (otherIndex < 0) + return false; + + int ourVal = Convert.ToInt32(ourValues.GetValue(i)); + int otherVal = Convert.ToInt32(otherValues.GetValue(otherIndex)); + + if (ourVal != otherVal) + return false; + } return true; } diff --git a/Packages/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbeGIBaking.LightTransport.cs b/Packages/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbeGIBaking.LightTransport.cs index 02db2704c1b..44516cd7bce 100644 --- a/Packages/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbeGIBaking.LightTransport.cs +++ b/Packages/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbeGIBaking.LightTransport.cs @@ -4,13 +4,13 @@ using System.Runtime.CompilerServices; using Unity.Collections; using UnityEditor; - using UnityEngine.LightTransport; using UnityEngine.LightTransport.PostProcessing; using UnityEditor.PathTracing.LightBakerBridge; using UnityEngine.PathTracing.Core; using UnityEngine.PathTracing.Integration; using UnityEngine.PathTracing.PostProcessing; +using UnityEditor.LightBaking; using UnityEngine.Rendering.Sampling; using UnityEngine.Rendering.UnifiedRayTracing; using UnityEngine.SceneManagement; @@ -72,28 +72,23 @@ public abstract class LightingBaker : IDisposable class DefaultLightTransport : LightingBaker { -#if UNIFIED_BAKER - public override bool isThreadSafe => false; // Unified backend is not thread safe until we backport the Async APV PR. -#else - public override bool isThreadSafe => true; // Can be run on a worker thread as it uses RadeonRays + OpenCL which is thread safe. -#endif - - int bakedProbeCount; - NativeArray positions; - InputExtraction.BakeInput input; - bool bakeProbeOcclusion; + public override bool isThreadSafe => false; // The Unified backend is not thread safe; the bake pipeline driver must be run on the main thread for VO baking. - public BakeJob[] jobs; + public BakeType bakeType { get; set; } + private BakePipelineDriver bakePipelineDriver; // Outputs - public NativeArray irradianceResults; - public NativeArray validityResults; - public NativeArray occlusionResults; - // Baked in a other job, but used in this one if available when fixing seams - private NativeArray renderingLayerMasks; - - public override ulong currentStep => (ulong)bakedProbeCount; - public override ulong stepCount => (ulong)positions.Length; + private NativeArray irradianceResults; + private NativeArray validityResults; + private NativeArray occlusionResults; + private NativeArray renderingLayerMasks; // Baked in a other job, but used in this one if available when fixing seams + + private bool isDone; + private long probeCount; + private ulong step; + public override ulong currentStep => isDone ? stepCount : Math.Min(step, inProgressMaxStepCount); + private ulong inProgressMaxStepCount => stepCount == 0 ? 0 : stepCount - 1; + public override ulong stepCount => (ulong)probeCount; public override NativeArray irradiance => irradianceResults; public override NativeArray validity => validityResults; @@ -101,21 +96,14 @@ class DefaultLightTransport : LightingBaker public override void Initialize(bool bakeProbeOcclusion, NativeArray probePositions) { - if (!InputExtraction.ExtractFromScene(out input, true)) - { - Debug.LogError("InputExtraction.ExtractFromScene failed."); - return; - } - - bakedProbeCount = 0; - positions = probePositions; - - irradianceResults = new NativeArray(positions.Length, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); - validityResults = new NativeArray(positions.Length, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); - - this.bakeProbeOcclusion = bakeProbeOcclusion; + bakeType = BakeType.Full; + isDone = false; + step = 0; + probeCount = probePositions.Length; + irradianceResults = new NativeArray(probePositions.Length, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); + validityResults = new NativeArray(probePositions.Length, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); if (bakeProbeOcclusion) - occlusionResults = new NativeArray(positions.Length, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); + occlusionResults = new NativeArray(probePositions.Length, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); } public override void Initialize(bool bakeProbeOcclusion, NativeArray probePositions, NativeArray bakedRenderingLayerMasks) @@ -132,47 +120,77 @@ public override void Initialize(bool bakeProbeOcclusion, NativeArray pr public override bool Step() { - if (input == null) - return false; - //RayTracingContext raytracingContext = null; + // If we are baking APV only, kick off the bake pipeline if we have not done it already. + bool apvOnly = bakeType is BakeType.ApvOnly or BakeType.AdditionalApvOnly; + if (apvOnly) + { + float progress = 0; + BakePipelineDriver.StageName stage = default; -#if UNIFIED_BAKER - RayTracingBackend backend = RayTracingContext.IsBackendSupported(RayTracingBackend.Hardware) ? RayTracingBackend.Hardware : RayTracingBackend.Compute; - RayTracingResources rayTracingResources = new RayTracingResources(); - rayTracingResources.Load(); - RayTracingContext raytracingContext = new RayTracingContext(backend, rayTracingResources); - BakeContext bakeContext = BakeContext.CreateUnityComputeContext(input, raytracingContext, out var creationSucceeded); -#else - BakeContext bakeContext = BakeContext.CreateRadeonRaysContext(input, out var creationSucceeded); -#endif + if (bakePipelineDriver == null) + { + Debug.Assert(UnityEditorInternal.InternalEditorUtility.CurrentThreadIsMainThread()); + + bakePipelineDriver = new BakePipelineDriver(); - try + bakePipelineDriver.StartBake( + bakeType == BakeType.ApvOnly, // We need patching for ApvOnly, not for AdditionalApvOnly. + ref progress, ref stage); + } + + // For a full bake, we are called after the bake pipeline driver has finished. + // For an APV only bake,we need to repeatedly call the pipeline driver until finished. + if (bakePipelineDriver.RunInProgress()) + { + bakePipelineDriver.Step(ref progress, ref stage); + // Update the step count based on the progress, the step count 0 - number of probes. + // For APV only the stepcount is used to report progress. + if (stage == BakePipelineDriver.StageName.Bake) + step = (ulong)(Math.Clamp(progress, 0.0f, 1.0f) * probeCount); + return true; + } + } + + // At this point, the baked data exists on disk. Either the regular LightBaker process wrote it, + // or our local BakePipeline wrote it, in case of APV-only bake. + { + using NativeArray shBytes = new(System.IO.File.ReadAllBytes(System.IO.Path.Combine(APVLightBakerPostProcessingOutputFolder, "irradiance.shl2")), Allocator.TempJob); + using NativeArray shData = shBytes.GetSubArray(sizeof(ulong), shBytes.Length - sizeof(ulong)).Reinterpret(sizeof(byte)); + irradiance.CopyFrom(shData); + } { - if (!creationSucceeded) - return false; + using NativeArray validityBytes = new(System.IO.File.ReadAllBytes(System.IO.Path.Combine(APVLightBakerOutputFolder, "validity0.float")), Allocator.TempJob); + using NativeArray validityData = validityBytes.GetSubArray(sizeof(ulong), validityBytes.Length - sizeof(ulong)).Reinterpret(sizeof(byte)); + validity.CopyFrom(validityData); + } + if (occlusionResults.IsCreated) + { + // Read LightProbeOcclusion structs from disk + using NativeArray occlusionBytes = new(System.IO.File.ReadAllBytes(System.IO.Path.Combine(APVLightBakerPostProcessingOutputFolder, "occlusion.occ")), Allocator.TempJob); + using NativeArray occlusionData = occlusionBytes.GetSubArray(sizeof(ulong), occlusionBytes.Length - sizeof(ulong)).Reinterpret(sizeof(byte)); - if (!bakeContext.Init(input, positions, bakeProbeOcclusion)) - return false; - - for (int i = 0; i < jobs.Length; i++) + // Create swizzled occlusion buffer which is indexed by shadowmask channel. This the format expected by shader code. + NativeArray swizzledOcclusion = new NativeArray(occlusionData.Length, Allocator.TempJob); + for (int probeIdx = 0; probeIdx < occlusionData.Length; probeIdx++) { - ref var job = ref jobs[i]; - if (job.probeCount != 0) + LightProbeOcclusion occlusion = occlusionData[probeIdx]; + Vector4 swizzled = Vector4.zero; + for (int lightIdx = 0; lightIdx < 4; lightIdx++) { - if (!bakeContext.Bake(job, ref irradianceResults, ref validityResults, ref occlusionResults)) - return false; - - bakedProbeCount += job.probeCount; + if (occlusionData[probeIdx].GetOcclusionMaskChannel(lightIdx, out sbyte shadowmaskIdx) && shadowmaskIdx >= 0) + { + occlusion.GetOcclusion(lightIdx, out float occlusionFactor); + swizzled[shadowmaskIdx] = occlusionFactor; + } } + + swizzledOcclusion[probeIdx] = swizzled; } + occlusion.CopyFrom(swizzledOcclusion); + swizzledOcclusion.Dispose(); } - finally - { - bakeContext.Dispose(); -#if UNIFIED_BAKER - raytracingContext?.Dispose(); -#endif - } + + isDone = true; return true; } @@ -181,13 +199,15 @@ public override void Dispose() { irradianceResults.Dispose(); validityResults.Dispose(); - if (bakeProbeOcclusion) + if (occlusionResults.IsCreated) occlusionResults.Dispose(); renderingLayerMasks.Dispose(); + + bakePipelineDriver?.Dispose(); } } - struct BakeJob + private struct BakeJob : IDisposable { public Bounds aabb; public ProbeReferenceVolume.Volume obb; @@ -269,397 +289,6 @@ public void Dispose() } } - struct BakeContext: IDisposable - { - internal class LightTransportBakingProfiling : BakingProfiling, IDisposable - { - //protected override string LogFile => "BakeGI"; - protected override bool ShowProgressBar => false; - - public enum Stages - { - BakeGI, - IntegrateDirectRadiance, - IntegrateIndirectRadiance, - IntegrateValidity, - IntegrateOcclusion, - Postprocess, - ReadBack, - None - } - - static Stages currentStage = Stages.None; - public LightTransportBakingProfiling(Stages stage) : base(stage, ref currentStage) { } - public override Stages GetLastStep() => Stages.None; - public static void GetProgressRange(out float progress0, out float progress1) { float s = 1 / (float)Stages.None; progress0 = (float)currentStage * s; progress1 = progress0 + s; } - public void Dispose() { OnDispose(ref currentStage); } - } - - public IDeviceContext deviceContext; - public IProbeIntegrator integrator; - public IWorld world; - public IProbePostProcessor postProcessor; - - public BufferID positionsBufferID; - public BufferID directRadianceBufferId; - public BufferID indirectRadianceBufferId; - public BufferID validityBufferId; - public BufferID perProbeLightIndicesId; - public BufferID occlusionBufferId; - - public BufferID windowedDirectSHBufferId; - public BufferID boostedIndirectSHBufferId; - public BufferID combinedSHBufferId; - public BufferID irradianceBufferId; - - private bool allocatedBuffers; - - const float k_PushOffset = 0.0001f; - const int k_MaxProbeCountPerBatch = 128 * 1024; - - const int maxOcclusionLightsPerProbe = 4; - static readonly int sizeOfFloat = 4; - static readonly int SHL2RGBElements = 3 * 9; - static readonly int sizeSHL2RGB = sizeOfFloat * SHL2RGBElements; - - int[] perProbeShadowmaskIndices; - bool bakeProbeOcclusion; - - SamplingResources samplingResources; - - public bool Init(InputExtraction.BakeInput input, NativeArray probePositions, bool doBakeProbeOcclusion) - { - if (!postProcessor.Initialize(deviceContext)) - { - Debug.LogError("Failed to initialize postprocessor."); - return false; - } - - bakeProbeOcclusion = doBakeProbeOcclusion; - CreateBuffers(probePositions.Length); - - // Upload probe positions - var positionsSlice = new BufferSlice(positionsBufferID, 0); - var positionWriteEvent = deviceContext.CreateEvent(); - deviceContext.WriteBuffer(positionsSlice, probePositions, positionWriteEvent); - - if (bakeProbeOcclusion) - { - // Upload per probe light indices - int[] perProbeLightIndicesArray = InputExtraction.ComputeOcclusionLightIndicesFromBakeInput(input, probePositions.ToArray(), (uint)maxOcclusionLightsPerProbe); - using var perProbeLightIndices = new NativeArray(perProbeLightIndicesArray, Allocator.TempJob); - var perProbeLightIndicesSlice = new BufferSlice(perProbeLightIndicesId, 0); - var perProbeLightIndicesWriteEvent = deviceContext.CreateEvent(); - deviceContext.WriteBuffer(perProbeLightIndicesSlice, perProbeLightIndices, perProbeLightIndicesWriteEvent); - deviceContext.Wait(perProbeLightIndicesWriteEvent); - deviceContext.DestroyEvent(perProbeLightIndicesWriteEvent); - - // Store per-probe shadowmask indices. They will be used to swizzle the occlusion buffer. - perProbeShadowmaskIndices = InputExtraction.GetShadowmaskChannelsFromLightIndices(input, perProbeLightIndicesArray); - } - - // Wait for writes to finish - deviceContext.Wait(positionWriteEvent); - deviceContext.DestroyEvent(positionWriteEvent); - - return true; - } - - public static BakeContext CreateUnityComputeContext(InputExtraction.BakeInput input, RayTracingContext rayTracingContext, out bool creationSucceeded) - { - creationSucceeded = true; - - var probePostProcessingShader = AssetDatabase.LoadAssetAtPath("Packages/com.unity.render-pipelines.core/Runtime/PathTracing/Shaders/ProbePostProcessing.compute"); - var probeOcclusionLightIndexMappingShader = AssetDatabase.LoadAssetAtPath("Packages/com.unity.render-pipelines.core/Runtime/PathTracing/Shaders/ProbeOcclusionLightIndexMapping.compute"); - var autoEstimateLUTRange = true; - - var bakeContext = new BakeContext(); - - var ucDevCtx = new UnityComputeDeviceContext(); - bakeContext.deviceContext = ucDevCtx; - - // setup world - var ucWorld = new UnityComputeWorld(); - bakeContext.world = ucWorld; - var worldResources = new WorldResourceSet(); - worldResources.LoadFromAssetDatabase(); - - // Create and init world - ucWorld.Init(rayTracingContext, worldResources); - - bakeContext.postProcessor = new UnityComputeProbePostProcessor(probePostProcessingShader); - - bakeContext.samplingResources = new SamplingResources(); - bakeContext.samplingResources.Load((uint)SamplingResources.ResourceType.All); - - ProbeIntegratorResources integrationResources = new(); - integrationResources.Load(rayTracingContext); - - bakeContext.integrator = new UnityComputeProbeIntegrator(true, bakeContext.samplingResources, integrationResources, probeOcclusionLightIndexMappingShader); - - if (!bakeContext.deviceContext.Initialize()) - { - Debug.LogError("Failed to initialize context."); - creationSucceeded = false; - return bakeContext; - } - - using var inputProgress = new BakeProgressState(); - // Deserialize BakeInput, inject data into world - BakeInputToWorldConversion.PopulateWorld(input, ucWorld, bakeContext.samplingResources, ucDevCtx.GetCommandBuffer(), autoEstimateLUTRange); - - return bakeContext; - } - - public static BakeContext CreateRadeonRaysContext(InputExtraction.BakeInput input, out bool creationSucceeded) - { - creationSucceeded = true; - var bakeContext = new BakeContext - { - deviceContext = new RadeonRaysContext(), - integrator = new RadeonRaysProbeIntegrator(), - world = new RadeonRaysWorld(), - postProcessor = new RadeonRaysProbePostProcessor(), - }; - - if (!bakeContext.deviceContext.Initialize()) - { - creationSucceeded = false; - Debug.LogError("Failed to initialize context."); - return bakeContext; - } - - using var inputProgress = new BakeProgressState(); - if (!InputExtraction.PopulateWorld(input, inputProgress, bakeContext.deviceContext, bakeContext.world)) - { - creationSucceeded = false; - Debug.LogError("Failed to extract inputs."); - return bakeContext; - } - - return bakeContext; - } - - private void CreateBuffers(int probeCount) - { - // Allocate shared position and light index buffer for all jobs - positionsBufferID = deviceContext.CreateBuffer((ulong)probeCount, (ulong)(3 * sizeOfFloat)); - - int batchSize = Mathf.Min(k_MaxProbeCountPerBatch, probeCount); - var shBytes = (ulong)(sizeSHL2RGB * batchSize); - var validityBytes = (ulong)(sizeOfFloat * batchSize); - - directRadianceBufferId = deviceContext.CreateBuffer((ulong)(batchSize * SHL2RGBElements), (ulong)sizeOfFloat); - indirectRadianceBufferId = deviceContext.CreateBuffer((ulong)(batchSize * SHL2RGBElements), (ulong)sizeOfFloat); - validityBufferId = deviceContext.CreateBuffer((ulong)batchSize, (ulong)sizeOfFloat); - - windowedDirectSHBufferId = deviceContext.CreateBuffer((ulong)(batchSize * SHL2RGBElements), (ulong)sizeOfFloat); - boostedIndirectSHBufferId = deviceContext.CreateBuffer((ulong)(batchSize * SHL2RGBElements), (ulong)sizeOfFloat); - combinedSHBufferId = deviceContext.CreateBuffer((ulong)(batchSize * SHL2RGBElements), (ulong)sizeOfFloat); - irradianceBufferId = deviceContext.CreateBuffer((ulong)(batchSize * SHL2RGBElements), (ulong)sizeOfFloat); - - if (bakeProbeOcclusion) - { - var lightIndicesBytes = (ulong)(sizeOfFloat * maxOcclusionLightsPerProbe * probeCount); - perProbeLightIndicesId = deviceContext.CreateBuffer((ulong)(maxOcclusionLightsPerProbe * probeCount), (ulong)sizeOfFloat); - - var occlusionBytes = (ulong)(sizeOfFloat * maxOcclusionLightsPerProbe * batchSize); - occlusionBufferId = deviceContext.CreateBuffer((ulong)(maxOcclusionLightsPerProbe * batchSize), (ulong)sizeOfFloat); - } - allocatedBuffers = true; - } - - public bool Bake(in BakeJob job, ref NativeArray irradianceResults, ref NativeArray validityResults, ref NativeArray occlusionResults) - { - // Divide the job into batches of 128k probes to reduce memory usage. - int batchCount = CoreUtils.DivRoundUp(job.probeCount, k_MaxProbeCountPerBatch); - - // Get slices for all buffers because the API require those - // All jobs use overlapping slices as they are not run simultaneously - var directRadianceSlice = new BufferSlice(directRadianceBufferId, 0); - var indirectRadianceSlice = new BufferSlice(indirectRadianceBufferId, 0); - var validitySlice = new BufferSlice(validityBufferId, 0); - var occlusionSlice = new BufferSlice(occlusionBufferId, 0); - var windowedDirectRadianceSlice = new BufferSlice(windowedDirectSHBufferId, 0); - var boostedIndirectRadianceSlice = indirectRadianceSlice; - var combinedSHSlice = new BufferSlice(combinedSHBufferId, 0); - var irradianceSlice = new BufferSlice(irradianceBufferId, 0); - - // Loop over all batches - for (int batchIndex = 0; batchIndex < batchCount; batchIndex++) - { - int batchOffset = batchIndex * k_MaxProbeCountPerBatch; - int probeCount = Mathf.Min(job.probeCount - batchOffset, k_MaxProbeCountPerBatch); - - // Get the correct slice of position and light indices as all jobs share the same array. - var positionsSlice = new BufferSlice(positionsBufferID, (ulong)(job.startOffset + batchOffset)); - var perProbeLightIndicesSlice = new BufferSlice(perProbeLightIndicesId, (ulong)(job.startOffset + batchOffset) * maxOcclusionLightsPerProbe); - - /// Baking - - // Prepare integrator. - integrator.Prepare(deviceContext, world, positionsSlice, k_PushOffset, job.maxBounces); - integrator.SetProgressReporter(job.progress); - - // Bake direct radiance - using (new LightTransportBakingProfiling(LightTransportBakingProfiling.Stages.IntegrateDirectRadiance)) - { - var integrationResult = integrator.IntegrateDirectRadiance(deviceContext, 0, probeCount, job.directSampleCount, job.ignoreEnvironement, directRadianceSlice); - if (integrationResult.type != IProbeIntegrator.ResultType.Success) return false; - if (LightingBaker.cancel) return true; - } - - // Bake indirect radiance - using (new LightTransportBakingProfiling(LightTransportBakingProfiling.Stages.IntegrateIndirectRadiance)) - { - var integrationResult = integrator.IntegrateIndirectRadiance(deviceContext, 0, probeCount, job.indirectSampleCount, job.ignoreEnvironement, indirectRadianceSlice); - if (integrationResult.type != IProbeIntegrator.ResultType.Success) return false; - if (LightingBaker.cancel) return true; - } - - // Bake validity - using (new LightTransportBakingProfiling(LightTransportBakingProfiling.Stages.IntegrateValidity)) - { - var validityResult = integrator.IntegrateValidity(deviceContext, 0, probeCount, job.validitySampleCount, validitySlice); - if (validityResult.type != IProbeIntegrator.ResultType.Success) return false; - if (LightingBaker.cancel) return true; - } - - // Bake occlusion - if (bakeProbeOcclusion) - { - using (new LightTransportBakingProfiling(LightTransportBakingProfiling.Stages.IntegrateOcclusion)) - { - var occlusionResult = integrator.IntegrateOcclusion(deviceContext, 0, probeCount, job.occlusionSampleCount, (int)maxOcclusionLightsPerProbe, perProbeLightIndicesSlice, occlusionSlice.SafeReinterpret()); - if (occlusionResult.type != IProbeIntegrator.ResultType.Success) return false; - if (LightingBaker.cancel) return true; - } - } - - /// Postprocess - - using (new LightTransportBakingProfiling(LightTransportBakingProfiling.Stages.Postprocess)) - { - // Apply windowing to direct component. - if (!postProcessor.WindowSphericalHarmonicsL2(deviceContext, directRadianceSlice, windowedDirectRadianceSlice, probeCount)) - return false; - - // Apply indirect intensity multiplier to indirect radiance - if (job.indirectScale.Equals(1.0f) == false) - { - boostedIndirectRadianceSlice = new BufferSlice(boostedIndirectSHBufferId, 0); - if (!postProcessor.ScaleSphericalHarmonicsL2(deviceContext, indirectRadianceSlice, boostedIndirectRadianceSlice, probeCount, job.indirectScale)) - return false; - } - - // Combine direct and indirect radiance - if (!postProcessor.AddSphericalHarmonicsL2(deviceContext, windowedDirectRadianceSlice, boostedIndirectRadianceSlice, combinedSHSlice, probeCount)) - return false; - - // Convert radiance to irradiance - if (!postProcessor.ConvolveRadianceToIrradiance(deviceContext, combinedSHSlice, irradianceSlice, probeCount)) - return false; - - // Transform to the format expected by the Unity renderer - if (!postProcessor.ConvertToUnityFormat(deviceContext, irradianceSlice, combinedSHSlice, probeCount)) - return false; - - // Apply de-ringing to combined SH - if (!postProcessor.DeringSphericalHarmonicsL2(deviceContext, combinedSHSlice, combinedSHSlice, probeCount)) - return false; - } - - /// Read results - - var jobIrradianceResults = irradianceResults.GetSubArray(job.startOffset + batchOffset, probeCount); - var jobValidityResults = validityResults.GetSubArray(job.startOffset + batchOffset, probeCount); - var jobOcclusionResults = default(NativeArray); - if (bakeProbeOcclusion) - jobOcclusionResults = occlusionResults.GetSubArray(job.startOffset + batchOffset, probeCount); - - // Schedule read backs to get results back from GPU memory into CPU memory. - var irradianceReadEvent = deviceContext.CreateEvent(); - deviceContext.ReadBuffer(combinedSHSlice, jobIrradianceResults, irradianceReadEvent); - var validityReadEvent = deviceContext.CreateEvent(); - deviceContext.ReadBuffer(validitySlice, jobValidityResults, validityReadEvent); - var occlusionReadEvent = default(EventID); - if (bakeProbeOcclusion) - { - occlusionReadEvent = deviceContext.CreateEvent(); - deviceContext.ReadBuffer(occlusionSlice, jobOcclusionResults, occlusionReadEvent); - } - if (!deviceContext.Flush()) return false; - - using (new LightTransportBakingProfiling(LightTransportBakingProfiling.Stages.ReadBack)) - { - // Wait for read backs to complete. - bool waitResult = deviceContext.Wait(irradianceReadEvent) && deviceContext.Wait(validityReadEvent) && (!bakeProbeOcclusion || deviceContext.Wait(occlusionReadEvent)); - if (!waitResult) return false; - } - - deviceContext.DestroyEvent(irradianceReadEvent); - deviceContext.DestroyEvent(validityReadEvent); - - if (bakeProbeOcclusion) - { - deviceContext.DestroyEvent(occlusionReadEvent); - - // Swizzle occlusion buffer so it is indexed by shadowmask channel. - // This is the format expected by shader code. - int baseProbeIdx = job.startOffset + batchOffset; - for (int probeIdx = 0; probeIdx < probeCount; probeIdx++) - { - Vector4 original = jobOcclusionResults[probeIdx]; - Vector4 swizzled = Vector3.zero; - - for (int lightIdx = 0; lightIdx < maxOcclusionLightsPerProbe; lightIdx++) - { - int shadowmaskIdx = perProbeShadowmaskIndices[(baseProbeIdx + probeIdx) * maxOcclusionLightsPerProbe + lightIdx]; - if (shadowmaskIdx >= 0) - { - swizzled[shadowmaskIdx] = original[lightIdx]; - } - } - - jobOcclusionResults[probeIdx] = swizzled; - } - } - - if (LightingBaker.cancel) - return true; - } - - return true; - } - - public void Dispose() - { - if (allocatedBuffers) - { - deviceContext.DestroyBuffer(positionsBufferID); - deviceContext.DestroyBuffer(directRadianceBufferId); - deviceContext.DestroyBuffer(indirectRadianceBufferId); - deviceContext.DestroyBuffer(validityBufferId); - if (bakeProbeOcclusion) - { - deviceContext.DestroyBuffer(occlusionBufferId); - deviceContext.DestroyBuffer(perProbeLightIndicesId); - } - - deviceContext.DestroyBuffer(windowedDirectSHBufferId); - deviceContext.DestroyBuffer(boostedIndirectSHBufferId); - deviceContext.DestroyBuffer(combinedSHBufferId); - deviceContext.DestroyBuffer(irradianceBufferId); - } - - samplingResources?.Dispose(); - postProcessor.Dispose(); - world.Dispose(); - integrator.Dispose(); - deviceContext.Dispose(); - } - } - // The contribution from all Baked and Mixed lights in the scene should be disabled to avoid double contribution. static void UpdateLightStatus() { @@ -883,41 +512,18 @@ public void Dispose() } } - // Helper functions to bake a subset of the probes - - internal static void BakeProbes(Vector3[] positionValues, SphericalHarmonicsL2[] shValues, float[] validityValues) + private static void BakeAdditionalProbes(out SphericalHarmonicsL2[] shValues, + out float[] validityValues) { - int numProbes = positionValues.Length; - - var positionsInput = new NativeArray(positionValues, Allocator.Temp); + while (s_BakeData.lightingJob.currentStep < s_BakeData.lightingJob.stepCount) + if (!s_BakeData.lightingJob.Step()) + s_BakeData.failed = true; - var lightingJob = lightingOverride ?? new DefaultLightTransport(); - lightingJob.Initialize(false, positionsInput); + shValues = s_BakeData.lightingJob.irradiance.ToArray(); + validityValues = s_BakeData.lightingJob.validity.ToArray(); - var defaultJob = lightingJob as DefaultLightTransport; - if (defaultJob != null) - { - var job = new BakeJob(); - job.Create(null, ProbeVolumeLightingTab.GetLightingSettings(), false); - job.probeCount = numProbes; - - defaultJob.jobs = new BakeJob[] { job }; - } - - while (lightingJob.currentStep < lightingJob.stepCount) - lightingJob.Step(); - - lightingJob.irradiance.CopyTo(shValues); - lightingJob.validity.CopyTo(validityValues); - - if (defaultJob != null) - { - foreach (var job in defaultJob.jobs) - job.Dispose(); - } - lightingJob.Dispose(); - positionsInput.Dispose(); + CleanBakeData(); } internal static void BakeAdjustmentVolume(ProbeVolumeBakingSet bakingSet, ProbeAdjustmentVolume touchup) @@ -1061,12 +667,11 @@ internal static void BakeAdjustmentVolume(ProbeVolumeBakingSet bakingSet, ProbeA failed |= !layerMaskJob.Step(); // Bake probe SH - var lightingJob = lightingOverride ?? new DefaultLightTransport(); - lightingJob.Initialize(ProbeVolumeLightingTab.GetLightingSettings().mixedBakeMode != MixedLightingMode.IndirectOnly, uniquePositions.AsArray(), layerMaskJob.renderingLayerMasks); - if (lightingJob is DefaultLightTransport defaultLightingJob) - defaultLightingJob.jobs = jobs; + s_BakeData.InitLightingJob(m_BakingSet, uniquePositions, BakeType.ApvOnly); + LightingBaker lightingJob = s_BakeData.lightingJob; while (!failed && lightingJob.currentStep < lightingJob.stepCount) failed |= !lightingJob.Step(); + CleanLightingBakeData(); // Upload new data in cells foreach ((int uniqueProbeIndex, int cellIndex, int i) in bakedProbes) @@ -1140,7 +745,7 @@ internal static void BakeAdjustmentVolume(ProbeVolumeBakingSet bakingSet, ProbeA } if (ProbeVolumeLightingTab.instance == null) - AdaptiveProbeVolumes.Dispose(); + CleanUp(); } } } diff --git a/Packages/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbeGIBaking.VirtualOffset.cs b/Packages/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbeGIBaking.VirtualOffset.cs index 16968c6f7a7..afeb8c5c959 100644 --- a/Packages/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbeGIBaking.VirtualOffset.cs +++ b/Packages/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbeGIBaking.VirtualOffset.cs @@ -1,5 +1,7 @@ using System; using System.Collections.Generic; +using System.Reflection; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Unity.Collections; using UnityEditor; @@ -10,7 +12,7 @@ namespace UnityEngine.Rendering partial class AdaptiveProbeVolumes { /// - /// Virtual offset baker + /// Virtual offset baker. This API allows implementing custom virtual offset baking strategies. Virtual offsets are used to offset probe positions away from geometry to avoid light leaking. /// public abstract class VirtualOffsetBaker : IDisposable { @@ -488,4 +490,33 @@ static uint[] GetMaterialIndices(Renderer renderer) return matIndices; } } + + // This class is used to access the internal class UnityEditor.LightBaking.VirtualOffsets. + class VirtualOffsets : IDisposable + { + readonly object m_VirtualOffsets; + readonly Type m_VirtualOffsetsType; + + public VirtualOffsets(IntPtr ptr) + { + m_VirtualOffsetsType = Type.GetType("UnityEditor.LightBaking.VirtualOffsets, UnityEditor"); + bool newed = m_VirtualOffsetsType != null; + Debug.Assert(newed, "Unexpected, could not find the type UnityEditor.LightBaking.VirtualOffsets"); + m_VirtualOffsets = newed ? Activator.CreateInstance(m_VirtualOffsetsType, ptr) : null; + Debug.Assert(m_VirtualOffsets != null, "Unexpected, could not new up a VirtualOffsets"); + } + internal void SetVirtualOffsets(Vector3[] offsets) => + InvokeMethod(new object[] { offsets }, out _); + + public void Dispose() => + InvokeMethod(new object[] { }, out _); + + void InvokeMethod(object[] parameters, out object result, [CallerMemberName] string methodName = "") + { + MethodInfo methodInfo = m_VirtualOffsetsType.GetMethod(methodName, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); + bool gotMethod = methodInfo != null; + Debug.Assert(gotMethod, $"Unexpected, could not find {methodName} on VirtualOffsets"); + result = !gotMethod ? null : methodInfo.Invoke(m_VirtualOffsets, parameters); + } + } } diff --git a/Packages/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbeGIBaking.cs b/Packages/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbeGIBaking.cs index c4de5688616..a749bf754b4 100644 --- a/Packages/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbeGIBaking.cs +++ b/Packages/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbeGIBaking.cs @@ -1,15 +1,15 @@ using System; +using System.Collections.Generic; using System.Linq; +using System.Reflection; using System.Threading; -using System.Collections.Generic; using Unity.Collections; -using UnityEngine.SceneManagement; -using UnityEditor; using Unity.Mathematics; - +using UnityEditor; +using UnityEngine.LightTransport; +using UnityEngine.SceneManagement; using Brick = UnityEngine.Rendering.ProbeBrickIndex.Brick; using IndirectionEntryInfo = UnityEngine.Rendering.ProbeReferenceVolume.IndirectionEntryInfo; - using TouchupVolumeWithBoundsList = System.Collections.Generic.List<(UnityEngine.Rendering.ProbeReferenceVolume.Volume obb, UnityEngine.Bounds aabb, UnityEngine.Rendering.ProbeAdjustmentVolume volume)>; namespace UnityEngine.Rendering @@ -270,8 +270,8 @@ internal int GetBakingHashCode() class BakingBatch : IDisposable { - public Dictionary> cellIndex2SceneReferences = new (); - public List cells = new (); + public Dictionary> cellIndex2SceneReferences = new(); + public List cells = new(); // Used to retrieve probe data from it's position in order to fix seams public NativeHashMap positionToIndex; // Allow to get a mapping to subdiv level with the unique positions. It stores the minimum subdiv level found for a given position. @@ -279,14 +279,14 @@ class BakingBatch : IDisposable public NativeHashMap uniqueBrickSubdiv; // Mapping for explicit invalidation, whether it comes from the auto finding of occluders or from the touch up volumes // TODO: This is not used yet. Will soon. - public Dictionary invalidatedPositions = new (); + public Dictionary invalidatedPositions = new(); // Utilities to compute unique probe position hash Vector3Int maxBrickCount; float inverseScale; Vector3 offset; - public Dictionary<(int, int), float> customDilationThresh = new (); - public Dictionary forceInvalidatedProbesAndTouchupVols = new (); + public Dictionary<(int, int), float> customDilationThresh = new(); + public Dictionary forceInvalidatedProbesAndTouchupVols = new(); private GIContributors? m_Contributors; public GIContributors contributors @@ -459,6 +459,8 @@ struct BakeData public NativeArray originalPositions; public NativeArray sortedPositions; + public InputExtraction.BakeInput bakeInput; + // Workers public Thread bakingThread; public VirtualOffsetBaker virtualOffsetJob; @@ -477,7 +479,64 @@ struct BakeData // Cancellation public bool failed; - public void Init(ProbeVolumeBakingSet bakingSet, NativeList probePositions, List requests) + internal static void InitVirtualOffsetJob(IntPtr pVirtualOffsetsBuffer, ref bool bakeVirtualOffsets) + { + bool usingVirtualOffset = m_BakingSet.settings.virtualOffsetSettings.useVirtualOffset; + if (!usingVirtualOffset) + { + bakeVirtualOffsets = false; + Debug.Assert(s_BakeData.virtualOffsetJob == null); + + return; + } + + var virtualOffsets = new VirtualOffsets(pVirtualOffsetsBuffer); + + s_BakeData.virtualOffsetJob = virtualOffsetOverride ?? new DefaultVirtualOffset(); + s_BakeData.virtualOffsetJob.Initialize(m_BakingSet, + s_BakeData.sortedPositions.GetSubArray(0, s_BakeData.probeCount)); + s_BakeData.VirtualOffsets = virtualOffsets; // This is an internal secret, it is only used by our own virtual offsets baker + + bakeVirtualOffsets = true; + } + VirtualOffsets VirtualOffsets { get; set; } + + internal static void UpdateVirtualOffsetJob(ref float progress, ref bool done) + { + if (s_BakeData.virtualOffsetJob == null) + { + done = true; + progress = 1.0f; + + return; + } + if (!s_BakeData.virtualOffsetJob.Step()) + { + s_BakeData.failed = true; + done = true; + progress = 1.0f; + + return; + } + if (s_BakeData.virtualOffsetJob.currentStep >= s_BakeData.virtualOffsetJob.stepCount) + { + done = true; + progress = 1.0f; + if (s_BakeData.virtualOffsetJob.offsets.IsCreated) + { + s_BakeData.ApplyVirtualOffset(); + s_BakeData.VirtualOffsets.SetVirtualOffsets(s_BakeData.virtualOffsetJob.offsets.ToArray()); + } + s_BakeData.VirtualOffsets?.Dispose(); // Disposes our own wrapper, we don't own the underlying data so it will not be disposed + s_BakeData.VirtualOffsets = null; + } + else + progress = s_BakeData.virtualOffsetJob.currentStep == 0 + ? 0 + : (float)s_BakeData.virtualOffsetJob.currentStep / s_BakeData.virtualOffsetJob.stepCount; + } + + public void Init(ProbeVolumeBakingSet bakingSet, NativeList probePositions, List requests, BakeType bakeType) { probeCount = probePositions.Length; reflectionProbeCount = requests.Count; @@ -486,9 +545,6 @@ public void Init(ProbeVolumeBakingSet bakingSet, NativeList probePositi originalPositions = probePositions.ToArray(Allocator.Persistent); SortPositions(probePositions, requests); - virtualOffsetJob = virtualOffsetOverride ?? new DefaultVirtualOffset(); - virtualOffsetJob.Initialize(bakingSet, sortedPositions.GetSubArray(0, probeCount)); - skyOcclusionJob = skyOcclusionOverride ?? new DefaultSkyOcclusion(); skyOcclusionJob.Initialize(bakingSet, sortedPositions.GetSubArray(0, probeCount)); if (skyOcclusionJob is DefaultSkyOcclusion defaultSOJob) @@ -499,14 +555,53 @@ public void Init(ProbeVolumeBakingSet bakingSet, NativeList probePositi lightingJob = lightingOverride ?? new DefaultLightTransport(); lightingJob.Initialize(ProbeVolumeLightingTab.GetLightingSettings().mixedBakeMode != MixedLightingMode.IndirectOnly, sortedPositions, layerMaskJob.renderingLayerMasks); - if (lightingJob is DefaultLightTransport defaultLightingJob) - defaultLightingJob.jobs = jobs; + if (lightingJob is DefaultLightTransport defaultLightTransport) + defaultLightTransport.bakeType = bakeType; cellIndex = 0; LightingBaker.cancel = false; - step = BakingStep.VirtualOffset; - stepCount = virtualOffsetJob.stepCount + lightingJob.stepCount + skyOcclusionJob.stepCount; + step = BakingStep.LaunchThread; + stepCount = skyOcclusionJob.stepCount + lightingJob.stepCount; + } + + public void InitAdditionalRequests(NativeList probePositions, List requests, BakeType bakeType) + { + probeCount = probePositions.Length; + reflectionProbeCount = requests.Count; + + jobs = CreateAdditionalBakingJobs(); + originalPositions = probePositions.ToArray(Allocator.Persistent); + SortPositions(probePositions, requests); + + lightingJob = lightingOverride ?? new DefaultLightTransport(); + using var layerMask = new NativeArray(); + lightingJob.Initialize(ProbeVolumeLightingTab.GetLightingSettings().mixedBakeMode != MixedLightingMode.IndirectOnly, sortedPositions, layerMask); + if (lightingJob is DefaultLightTransport defaultLightTransport) + defaultLightTransport.bakeType = bakeType; + + cellIndex = 0; + + LightingBaker.cancel = false; + step = BakingStep.LaunchThread; + stepCount = lightingJob.stepCount; + } + + public void InitLightingJob(ProbeVolumeBakingSet bakingSet, NativeList probePositions, BakeType bakeType) + { + probeCount = probePositions.Length; + + s_AdjustmentVolumes = new TouchupVolumeWithBoundsList(); + + jobs = CreateBakingJobs(bakingSet, false); + SortPositions(probePositions, new List()); + + lightingJob = lightingOverride ?? new DefaultLightTransport(); + lightingJob.Initialize(ProbeVolumeLightingTab.GetLightingSettings().mixedBakeMode != MixedLightingMode.IndirectOnly, sortedPositions); + if (lightingJob is DefaultLightTransport defaultLightTransport) + defaultLightTransport.bakeType = bakeType; + + LightingBaker.cancel = false; } public void ExecuteLightingAsync() @@ -559,6 +654,35 @@ static BakeJob[] CreateBakingJobs(ProbeVolumeBakingSet bakingSet, bool hasAdditi return jobs; } + static BakeJob[] CreateAdditionalBakingJobs() + { + // Build the list of adjustment volumes affecting sample count + var touchupVolumesAndBounds = new TouchupVolumeWithBoundsList(); + { + // This is slow, but we should have very little amount of touchup volumes. + foreach (var adjustment in s_AdjustmentVolumes) + { + if (adjustment.volume.mode == ProbeAdjustmentVolume.Mode.OverrideSampleCount) + touchupVolumesAndBounds.Add(adjustment); + } + + // Sort by volume to give priority to smaller volumes + touchupVolumesAndBounds.Sort((a, b) => (a.volume.ComputeVolume(a.obb).CompareTo(b.volume.ComputeVolume(b.obb)))); + } + + var lightingSettings = ProbeVolumeLightingTab.GetLightingSettings(); + bool skyOcclusion = false; + + var jobs = new BakeJob[touchupVolumesAndBounds.Count + 1]; + + for (int i = 0; i < touchupVolumesAndBounds.Count; i++) + jobs[i].Create(lightingSettings, skyOcclusion, touchupVolumesAndBounds[i]); + + jobs[touchupVolumesAndBounds.Count].Create(null, lightingSettings, false); + + return jobs; + } + // Place positions contiguously for each bake job in a single array, with reflection probes at the end public void SortPositions(NativeList probePositions, List additionalRequests) { @@ -620,18 +744,18 @@ public void SortPositions(NativeList probePositions, List addi public void ApplyVirtualOffset() { - var offsets = virtualOffsetJob.offsets; + NativeArray offsets = virtualOffsetJob.offsets; for (int i = 0; i < offsets.Length; i++) sortedPositions[i] += offsets[i]; } public bool Done() { - ulong currentStep = s_BakeData.virtualOffsetJob.currentStep + lightingJob.currentStep + s_BakeData.skyOcclusionJob.currentStep; + ulong currentStep = s_BakeData.lightingJob.currentStep + s_BakeData.skyOcclusionJob.currentStep; return currentStep >= s_BakeData.stepCount && s_BakeData.step == BakingStep.Last; } - public void Dispose() + public void CleanUp() { if (failed) Debug.LogError("Probe Volume Baking failed."); @@ -646,11 +770,23 @@ public void Dispose() originalPositions.Dispose(); sortedPositions.Dispose(); - skyOcclusionJob.encodedDirections.Dispose(); - virtualOffsetJob.Dispose(); - skyOcclusionJob.Dispose(); + skyOcclusionJob?.encodedDirections.Dispose(); + virtualOffsetJob?.Dispose(); + virtualOffsetJob = null; + skyOcclusionJob?.Dispose(); + lightingJob.Dispose(); + layerMaskJob?.Dispose(); + + // clear references to managed data + this = default; + } + + public void CleanUpLightingJob() + { + if (failed) + Debug.LogError("Probe Volume Baking failed."); + lightingJob.Dispose(); - layerMaskJob.Dispose(); // clear references to managed data this = default; @@ -716,10 +852,11 @@ static internal void Init() Lightmapping.lightingDataCleared += OnLightingDataCleared; Lightmapping.bakeStarted += OnBakeStarted; Lightmapping.bakeCancelled += OnBakeCancelled; + Lightmapping.inputExtraction += OnInputExtraction; } } - static internal void Dispose() + internal static void CleanUp() { s_TracingContext.Dispose(); } @@ -915,49 +1052,140 @@ enum BakingStep static void OnBakeStarted() { - if (PrepareBaking()) + if (PrepareBaking(BakeType.Full, DoProbePlacement, AdditionalGIBakeRequestsManager.GetProbeNormalizationRequests)) { ProbeReferenceVolume.instance.checksDuringBakeAction = CheckPVChanges; - Lightmapping.SetAdditionalBakeDelegate(BakeDelegate); + Lightmapping.AddBakeDelegate(BakeDelegate); } } - internal static bool PrepareBaking() + private static readonly string APVLightBakerFolder = "APVBake"; + private static readonly string APVLightBakerOutputFolder = $"Temp/LightBakerOutput/{APVLightBakerFolder}"; + private static readonly string APVLightBakerPostProcessingOutputFolder = $"Temp/PostProcessingOutput/{APVLightBakerFolder}"; + private static void OnInputExtraction(InputExtraction.BakeInput bakeInput) + { + if (s_BakeData.sortedPositions.Length == 0) + return; + + var lsa = ProbeVolumeLightingTab.GetLightingSettings(); + ProbeBakeRequestOutput outputTypes = ProbeBakeRequestOutput.All; + if (lsa.mixedBakeMode == MixedLightingMode.IndirectOnly) + outputTypes &= ~ProbeBakeRequestOutput.Occlusion; + + var extraPos = s_BakeData.sortedPositions.ToArray(); + + Vector3[] newPositions = bakeInput.GetProbePositions(); + int prevProbeCount = newPositions.Length; + Array.Resize(ref newPositions, newPositions.Length + extraPos.Length); + Array.Copy(extraPos, 0, newPositions, prevProbeCount, extraPos.Length); + + int[] newOcclusionIndices = bakeInput.GetOcclusionLightIndices(); + int prevOcclusionCount = newOcclusionIndices.Length; + int[] extraOcclusionIndices = InputExtraction.ComputeOcclusionLightIndicesFromBakeInput(bakeInput, extraPos, 4); + Array.Resize(ref newOcclusionIndices, newOcclusionIndices.Length + extraOcclusionIndices.Length); + Array.Copy(extraOcclusionIndices, 0, newOcclusionIndices, prevOcclusionCount, extraOcclusionIndices.Length); + + bakeInput.SetProbePositions(newPositions); + bakeInput.SetOcclusionLightIndices(newOcclusionIndices); + bakeInput.AddProbeRequest(new ProbeBakeRequest + { + outputTypes = outputTypes, + positionOffset = (ulong)prevProbeCount, + positionLength = (ulong)extraPos.Length, + bakeOutputFolderPath = APVLightBakerOutputFolder, + postProcessOutputFolderPath = APVLightBakerPostProcessingOutputFolder, + ignoreDirectEnvironment = m_BakingSet != null ? m_BakingSet.bakedSkyOcclusion : false, + ignoreIndirectEnvironment = m_BakingSet != null ? m_BakingSet.bakedSkyOcclusion : false, + pushoff = 0.0001f, + indirectScale = lsa.indirectScale, + dering = true, + }); + + s_BakeData.bakeInput = bakeInput; + } + + private delegate bool GetProbesFunc(out NativeList b); + private delegate List GetAdditionalRequestsFunc(); + + private static bool PrepareBaking(BakeType bakeType, GetProbesFunc getProbes, GetAdditionalRequestsFunc getAdditionalRequests) { if (AdaptiveProbeVolumes.isRunning) AdaptiveProbeVolumes.Cancel(); - List requests; NativeList positions; + List requests; using (new BakingSetupProfiling(BakingSetupProfiling.Stages.OnBakeStarted)) { if (!InitializeBake()) return false; s_AdjustmentVolumes = GetAdjustementVolumes(); - requests = AdditionalGIBakeRequestsManager.GetProbeNormalizationRequests(); + requests = getAdditionalRequests(); + + if (!getProbes(out positions)) + return false; + } - bool canceledByUser = false; - // Note: this could be executed in the baking delegate to be non blocking - using (new BakingSetupProfiling(BakingSetupProfiling.Stages.PlaceProbes)) - positions = RunPlacement(m_ProfileInfo, ProbeReferenceVolume.instance, ref canceledByUser); + s_BakeData.Init(m_BakingSet, positions, requests, bakeType); + positions.Dispose(); - if (positions.Length == 0 || canceledByUser) - { - positions.Dispose(); + // Set delegates used for virtual offsets baking + Type lightmappingType = typeof(Lightmapping); + FieldInfo initField = lightmappingType.GetField("s_VirtualOffsetBakeInitializeDataDelegate", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); + Debug.Assert(initField != null, "Unexpected, could not find s_VirtualOffsetBakeInitializeDataDelegate"); + FieldInfo updateField = lightmappingType.GetField("s_VirtualOffsetBakeUpdateDelegate", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); + Debug.Assert(updateField != null, "Unexpected, could not find s_VirtualOffsetBakeUpdateDelegate"); - Clear(); - CleanBakeData(); + initField.SetValue(null, Delegate.CreateDelegate(initField.FieldType, typeof(BakeData), nameof(BakeData.InitVirtualOffsetJob))); + updateField.SetValue(null, Delegate.CreateDelegate(updateField.FieldType, typeof(BakeData), nameof(BakeData.UpdateVirtualOffsetJob))); - if (canceledByUser) - Lightmapping.Cancel(); + return true; + } + private static bool PrepareAdditionalProbesBaking(BakeType bakeType, GetProbesFunc getProbes, GetAdditionalRequestsFunc getAdditionalRequests) + { + if (AdaptiveProbeVolumes.isRunning) + AdaptiveProbeVolumes.Cancel(); + + NativeList positions; + List requests; + using (new BakingSetupProfiling(BakingSetupProfiling.Stages.OnBakeStarted)) + { + if (!InitializeAdditionalRequestsBake()) + return false; + + s_AdjustmentVolumes = GetAdjustementVolumes(); + requests = getAdditionalRequests(); + + if (!getProbes(out positions)) return false; - } } - s_BakeData.Init(m_BakingSet, positions, requests); + s_BakeData.InitAdditionalRequests(positions, requests, bakeType); positions.Dispose(); + + return true; + } + private static bool DoProbePlacement(out NativeList positions) + { + bool canceledByUser = false; + // Note: this could be executed in the baking delegate to be non blocking + using (new BakingSetupProfiling(BakingSetupProfiling.Stages.PlaceProbes)) + positions = RunPlacement(m_ProfileInfo, ProbeReferenceVolume.instance, ref canceledByUser); + + if (positions.Length == 0 || canceledByUser) + { + positions.Dispose(); + + Clear(); + CleanBakeData(); + + if (canceledByUser) + Lightmapping.Cancel(); + + return false; + } + return true; } @@ -1017,7 +1245,7 @@ static bool InitializeBake() if (!ProbeReferenceVolume.instance.EnsureCurrentBakingSet(m_BakingSet)) return false; - if (!Lightmapping.TryGetLightingSettings(out var lightingSettings) || lightingSettings ==null || lightingSettings.lightmapper == LightingSettings.Lightmapper.ProgressiveCPU) + if (!Lightmapping.TryGetLightingSettings(out LightingSettings lightingSettings)) { m_BakingSet.skyOcclusion = false; } @@ -1032,20 +1260,41 @@ static bool InitializeBake() return true; } - static void BakeDelegate(ref float progress, ref bool done) + static bool InitializeAdditionalRequestsBake() { - if (s_BakeData.step == BakingStep.VirtualOffset) + if (ProbeVolumeLightingTab.instance?.PrepareAPVAdditionalRequestsBake(ProbeReferenceVolume.instance) == false) + return false; + + if (!ProbeReferenceVolume.instance.isInitialized || !ProbeReferenceVolume.instance.enabledBySRP) + return false; + + using var scope = new BakingSetupProfiling(BakingSetupProfiling.Stages.PrepareWorldSubdivision); + + // Verify to make sure we can still do it. Shortcircuting so that we don't run CanFreezePlacement unless is needed. + isFreezingPlacement = isFreezingPlacement && CanFreezePlacement(); + if (!isFreezingPlacement) { - if (!s_BakeData.virtualOffsetJob.Step()) - s_BakeData.failed = true; - if (s_BakeData.virtualOffsetJob.currentStep >= s_BakeData.virtualOffsetJob.stepCount) + using (new BakingSetupProfiling(BakingSetupProfiling.Stages.EnsurePerSceneDataInOpenScenes)) { - if (s_BakeData.virtualOffsetJob.offsets.IsCreated) - s_BakeData.ApplyVirtualOffset(); - s_BakeData.step++; + if (!EnsurePerSceneDataInOpenScenes()) + return false; } } + return true; + } + + internal enum BakeType + { + Full, + ApvOnly, + AdditionalApvOnly + } + + private static void BakeDelegate(ref float progress, out bool done, InputExtraction.BakeInput bakeInput) + { + done = false; + if (s_BakeData.step == BakingStep.LaunchThread) { if (s_BakeData.lightingJob.isThreadSafe) @@ -1053,6 +1302,7 @@ static void BakeDelegate(ref float progress, ref bool done) s_BakeData.step++; } + // Skyocclusion, rendering layer mask, and virtual offset should use data from the BakeInput ideally. See https://jira.unity3d.com/browse/GFXLIGHT-1612. if (s_BakeData.step == BakingStep.SkyOcclusion) { if (!s_BakeData.skyOcclusionJob.Step()) @@ -1126,7 +1376,7 @@ static void BakeDelegate(ref float progress, ref bool done) FinalizeCell(s_BakeData.cellIndex++, s_BakeData.positionRemap, s_BakeData.lightingJob.irradiance, s_BakeData.lightingJob.validity, s_BakeData.layerMaskJob.renderingLayerMasks, - s_BakeData.virtualOffsetJob.offsets, + s_BakeData.virtualOffsetJob?.offsets ?? new NativeArray(), s_BakeData.skyOcclusionJob.occlusion, s_BakeData.skyOcclusionJob.encodedDirections, s_BakeData.lightingJob.occlusion); @@ -1144,14 +1394,7 @@ static void BakeDelegate(ref float progress, ref bool done) // When using default backend, live progress report is not accurate // So we can't rely on it to know when baking is done, but it's useful for showing progress - ulong currentStep = s_BakeData.virtualOffsetJob.currentStep + s_BakeData.skyOcclusionJob.currentStep; - if (s_BakeData.lightingJob is DefaultLightTransport defaultJob) - { - foreach (var job in defaultJob.jobs) - currentStep += job.currentStep; - } - else - currentStep += s_BakeData.lightingJob.currentStep; + ulong currentStep = s_BakeData.skyOcclusionJob.currentStep + s_BakeData.lightingJob.currentStep; progress = currentStep / (float)s_BakeData.stepCount; // Use our counter to determine when baking is done @@ -1177,7 +1420,7 @@ static bool CurrentSceneHasBakedData() return sceneData.bakingSet.HasBeenBaked(); } - static void FinalizeBake(bool cleanup = true) + static void FinalizeBake() { using (new BakingCompleteProfiling(BakingCompleteProfiling.Stages.FinalizingBake)) { @@ -1201,8 +1444,7 @@ static void FinalizeBake(bool cleanup = true) } } - if (cleanup) - CleanBakeData(); + CleanBakeData(); // We need to reset that view ProbeReferenceVolume.instance.ResetDebugViewToMaxSubdiv(); @@ -1229,21 +1471,30 @@ static void OnBakeCancelled() static void CleanBakeData() { - s_BakeData.Dispose(); + s_BakeData.CleanUp(); m_BakingBatch?.Dispose(); m_BakingBatch = null; s_AdjustmentVolumes = null; - // If lighting pannel is not created, we have to dispose ourselves + // If lighting panel is not created, we have to dispose ourselves if (ProbeVolumeLightingTab.instance == null) - AdaptiveProbeVolumes.Dispose(); + CleanUp(); - Lightmapping.ResetAdditionalBakeDelegate(); + Lightmapping.RemoveBakeDelegate(BakeDelegate); partialBakeSceneList = null; ProbeReferenceVolume.instance.checksDuringBakeAction = null; } + // Clean-up after the Lighting job has been initialized with InitLightingJob + private static void CleanLightingBakeData() + { + foreach (BakeJob job in s_BakeData.jobs) + job.Dispose(); + s_BakeData = default; + s_AdjustmentVolumes = null; + } + class VoxelToBrickCache { class CacheEntry @@ -1325,7 +1576,7 @@ internal static void FixSeams( { // Seams are caused are caused by probes on the boundary between two subdivision levels // The idea is to find first them and do a kind of dilation to smooth the values on the boundary - // the dilation process consits in doing a trilinear sample of the higher subdivision brick and override the lower subdiv with that + // the dilation process consists in doing a trilinear sample of the higher subdivision brick and override the lower subdiv with that // We have to mark the probes on the boundary as valid otherwise leak reduction at runtime will interfere with this method bool doProbeOcclusion = probeOcclusion.IsCreated && probeOcclusion.Length > 0; @@ -1588,21 +1839,20 @@ static void ApplyPostBakeOperations() } - static int s_AsyncBakeTaskID = -1; + static int _asyncBakeTaskId = -1; internal static void AsyncBakeCallback() { float progress = 0.0f; - bool done = false; - BakeDelegate(ref progress, ref done); - Progress.Report(s_AsyncBakeTaskID, progress, s_BakeData.step.ToString()); + BakeDelegate(ref progress, out bool done, s_BakeData.bakeInput); + Progress.Report(_asyncBakeTaskId, progress, s_BakeData.step.ToString()); if (done) { UpdateLightStatus(); - Progress.Remove(s_AsyncBakeTaskID); + Progress.Remove(_asyncBakeTaskId); EditorApplication.update -= AsyncBakeCallback; - s_AsyncBakeTaskID = -1; + _asyncBakeTaskId = -1; } } @@ -1612,32 +1862,33 @@ internal static void AsyncBakeCallback() /// Returns true if the bake was successfully started. public static bool BakeAsync() { - if (Lightmapping.isRunning || AdaptiveProbeVolumes.isRunning || !PrepareBaking()) + if (Lightmapping.isRunning || AdaptiveProbeVolumes.isRunning || !PrepareBaking(BakeType.ApvOnly, DoProbePlacement, AdditionalGIBakeRequestsManager.GetProbeNormalizationRequests)) return false; - s_AsyncBakeTaskID = Progress.Start("Bake Adaptive Probe Volumes"); - Progress.RegisterCancelCallback(s_AsyncBakeTaskID, () => + _asyncBakeTaskId = Progress.Start("Bake Adaptive Probe Volumes"); + Progress.RegisterCancelCallback(_asyncBakeTaskId, () => { OnBakeCancelled(); EditorApplication.update -= AsyncBakeCallback; - s_AsyncBakeTaskID = -1; + _asyncBakeTaskId = -1; return true; }); EditorApplication.update += AsyncBakeCallback; + return true; } /// /// Returns true when the async baking of adaptive probe volumes only is running, false otherwise (Read Only). /// - public static bool isRunning => s_AsyncBakeTaskID != -1; + public static bool isRunning => _asyncBakeTaskId != -1; /// /// Cancels the currently running asynchronous bake job. /// /// Returns true if baking was successfully cancelled. - public static bool Cancel() => Progress.Cancel(s_AsyncBakeTaskID); + public static bool Cancel() => Progress.Cancel(_asyncBakeTaskId); /// /// Request additional bake request manager to recompute baked data for an array of requests @@ -1645,33 +1896,54 @@ public static bool BakeAsync() /// Array of instance IDs of the probes doing the request. public static void BakeAdditionalRequests(EntityId[] probeInstanceIDs) { - List validProbeInstanceIDs = new List(); - List positions = new List(); - foreach (var probeInstanceID in probeInstanceIDs) + GetValidProbeInstanceIds(probeInstanceIDs, out List validProbeInstanceIds); + + int numValidProbes = validProbeInstanceIds.Count; + if (numValidProbes <= 0) + return; + + if (!PrepareAdditionalProbesBaking(BakeType.AdditionalApvOnly, GetProbes, GetNormalizationRequests)) + return; + + // Bake all probes in a single batch + BakeAdditionalProbes(out SphericalHarmonicsL2[] sh, out float[] validity); + + Debug.Assert(numValidProbes == sh.Length); + for (int probeIndex = 0; probeIndex < numValidProbes; ++probeIndex) + AdditionalGIBakeRequestsManager.SetSHCoefficients(validProbeInstanceIds[probeIndex], sh[probeIndex], validity[probeIndex]); + + return; + + List GetNormalizationRequests() { -#pragma warning disable 618 // Todo(@daniel.andersen): Remove deprecated API usage - if (AdditionalGIBakeRequestsManager.GetPositionForRequest(probeInstanceID, out var position)) -#pragma warning restore 618 + List positions = new(); + foreach (EntityId probeInstanceId in probeInstanceIDs) { - validProbeInstanceIDs.Add(probeInstanceID); - positions.Add(position); + if (AdditionalGIBakeRequestsManager.GetPositionForRequest(probeInstanceId, out Vector3 position)) + { + positions.Add(position); + } } + + m_BakingBatch = new BakingBatch(Vector3Int.zero, ProbeReferenceVolume.instance); // We have zero cells + + return positions; } - int numValidProbes = validProbeInstanceIDs.Count; - if (numValidProbes > 0) + bool GetProbes(out NativeList positions) { - SphericalHarmonicsL2[] sh = new SphericalHarmonicsL2[numValidProbes]; - float[] validity = new float[numValidProbes]; - - // Bake all probes in a single batch - BakeProbes(positions.ToArray(), sh, validity); + positions = new NativeList(Allocator.Persistent); + return true; + } - for (int probeIndex = 0; probeIndex < numValidProbes; ++probeIndex) - { -#pragma warning disable 618 // Todo(@daniel.andersen): Remove deprecated API usage - AdditionalGIBakeRequestsManager.SetSHCoefficients(validProbeInstanceIDs[probeIndex], sh[probeIndex], validity[probeIndex]); -#pragma warning restore 618 + static void GetValidProbeInstanceIds(EntityId[] entityIds, out List validProbeInstanceIds) + { + validProbeInstanceIds = new List(); + foreach (EntityId probeInstanceId in entityIds)
 { + if (AdditionalGIBakeRequestsManager.GetPositionForRequest(probeInstanceId, out Vector3 _)) + { + validProbeInstanceIds.Add(probeInstanceId); + } } } } diff --git a/Packages/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbeVolumeLightingTab.cs b/Packages/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbeVolumeLightingTab.cs index bb2045301a0..fd87b3966a8 100644 --- a/Packages/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbeVolumeLightingTab.cs +++ b/Packages/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbeVolumeLightingTab.cs @@ -210,7 +210,7 @@ public override void OnDisable() // We keep allocated acceleration structures while the Lighting window is open in order to make subsequent bakes faster, but when the window closes we dispose of them // Unless a bake is running, in which case we leave disposing to CleanBakeData() if (!AdaptiveProbeVolumes.isRunning && !Lightmapping.isRunning) - AdaptiveProbeVolumes.Dispose(); + AdaptiveProbeVolumes.CleanUp(); } #region On GUI @@ -1070,6 +1070,8 @@ internal bool PrepareAPVBake(ProbeReferenceVolume prv) return true; } + internal bool PrepareAPVAdditionalRequestsBake(ProbeReferenceVolume prv) => prv.isInitialized && prv.enabledBySRP; + static T ObjectFieldWithNew(GUIContent label, T obj, Func onClick) where T : Object { const int k_NewFieldWidth = 70; diff --git a/Packages/com.unity.render-pipelines.core/Editor/PathTracing/BakeInputToWorldConversion.cs b/Packages/com.unity.render-pipelines.core/Editor/PathTracing/BakeInputToWorldConversion.cs index 803b4060b6a..b83ba215aa6 100644 --- a/Packages/com.unity.render-pipelines.core/Editor/PathTracing/BakeInputToWorldConversion.cs +++ b/Packages/com.unity.render-pipelines.core/Editor/PathTracing/BakeInputToWorldConversion.cs @@ -389,7 +389,7 @@ internal static void ConvertInstancesAndMeshes( sceneBounds = new Bounds(); // Extract meshes - meshes = new Mesh[bakeInput.meshData.Length]; + meshes = new Mesh[bakeInput.meshData.Length + bakeInput.terrainData.Length]; int meshIndex = 0; for (int i = 0; i < bakeInput.meshData.Length; i++) { diff --git a/Packages/com.unity.render-pipelines.core/Editor/PathTracing/LightBakerStrangler.cs b/Packages/com.unity.render-pipelines.core/Editor/PathTracing/LightBakerStrangler.cs index a6502d92fc1..f0b242bb8ab 100644 --- a/Packages/com.unity.render-pipelines.core/Editor/PathTracing/LightBakerStrangler.cs +++ b/Packages/com.unity.render-pipelines.core/Editor/PathTracing/LightBakerStrangler.cs @@ -126,34 +126,6 @@ internal enum Result Cancelled }; - // Mirrors native side LightProbeOcclusion struct - [StructLayout(LayoutKind.Sequential)] - public unsafe struct LightProbeOcclusion - { - public const int MaxLightsPerProbe = 4; - - public fixed int ProbeOcclusionLightIndex[MaxLightsPerProbe]; - public fixed float Occlusion[MaxLightsPerProbe]; - public fixed sbyte OcclusionMaskChannel[MaxLightsPerProbe]; - - public int GetProbeOcclusionLightIndex(int index) => ProbeOcclusionLightIndex[index]; - public void SetProbeOcclusionLightIndex(int index, int value) => ProbeOcclusionLightIndex[index] = value; - public float GetOcclusion(int index) => Occlusion[index]; - public void SetOcclusion(int index, float value) => Occlusion[index] = value; - public sbyte GetOcclusionMaskChannel(int index) => OcclusionMaskChannel[index]; - public void SetOcclusionMaskChannel(int index, sbyte value) => OcclusionMaskChannel[index] = value; - - public void SetDefaultValues() - { - for (int i = 0; i < MaxLightsPerProbe; ++i) - { - ProbeOcclusionLightIndex[i] = -1; - Occlusion[i] = 0.0f; - OcclusionMaskChannel[i] = -1; - } - } - } - // File layout matches WriteInterleavedSHArrayToFile. private static bool SaveProbesToFileInterleaved(string filename, NativeArray shArray) { @@ -460,13 +432,13 @@ private static ulong CalculateProbeWorkSteps(ulong count, ProbeRequestOutputType { ulong workSteps = 0; if (outputTypeMask.HasFlag(ProbeRequestOutputType.RadianceIndirect)) - workSteps += ProbeIntegrator.CalculateWorkSteps((uint)count, effectiveIndirectSampleCount, bounceCount); + workSteps += ProbeIntegrator.CalculateWorkSteps(count, effectiveIndirectSampleCount, bounceCount); if (outputTypeMask.HasFlag(ProbeRequestOutputType.RadianceDirect)) - workSteps += ProbeIntegrator.CalculateWorkSteps((uint)count, directSampleCount, 0); + workSteps += ProbeIntegrator.CalculateWorkSteps(count, directSampleCount, 0); if (outputTypeMask.HasFlag(ProbeRequestOutputType.Validity)) - workSteps += ProbeIntegrator.CalculateWorkSteps((uint)count, effectiveIndirectSampleCount, 0); + workSteps += ProbeIntegrator.CalculateWorkSteps(count, effectiveIndirectSampleCount, 0); if (outputTypeMask.HasFlag(ProbeRequestOutputType.LightProbeOcclusion) && usesProbeOcclusion) - workSteps += ProbeIntegrator.CalculateWorkSteps((uint)count, effectiveIndirectSampleCount, 0); + workSteps += ProbeIntegrator.CalculateWorkSteps(count, effectiveIndirectSampleCount, 0); return workSteps; } diff --git a/Packages/com.unity.render-pipelines.core/Runtime/AssemblyInfo.cs b/Packages/com.unity.render-pipelines.core/Runtime/AssemblyInfo.cs index 1ae793a5f88..33fc788c636 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/AssemblyInfo.cs +++ b/Packages/com.unity.render-pipelines.core/Runtime/AssemblyInfo.cs @@ -18,3 +18,6 @@ [assembly: InternalsVisibleTo("SRPSmoke.Runtime.Tests")] [assembly: InternalsVisibleTo("SRPSmoke.Editor.Tests")] [assembly: InternalsVisibleTo("Assembly-CSharp-Editor-testable")] + +// Access for URP Tests so they use internals from Core Runtime +[assembly: InternalsVisibleTo("Unity.RenderPipelines.Universal.Runtime.Tests")] diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/AssemblyInfo.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/AssemblyInfo.cs index 3441704c446..526cb5626df 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/AssemblyInfo.cs +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/AssemblyInfo.cs @@ -3,3 +3,4 @@ [assembly: InternalsVisibleTo("Unity.RenderPipelines.Core.Editor")] [assembly: InternalsVisibleTo("Unity.RenderPipelines.Core.Editor.Tests")] [assembly: InternalsVisibleTo("UnityEngine.TestTools.Graphics.Contexts")] +[assembly: InternalsVisibleTo("Unity.Entities.Graphics")] diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Batching.meta similarity index 77% rename from Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData.meta rename to Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Batching.meta index c8dd2aacd93..5840b7139d0 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData.meta +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Batching.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: b5f7bf756dcca7044ace748986382c79 +guid: 80469c239862e3c48ba6b3917aa31891 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Batching/CPUDrawInstanceData.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Batching/CPUDrawInstanceData.cs new file mode 100644 index 00000000000..dee1f6977f1 --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Batching/CPUDrawInstanceData.cs @@ -0,0 +1,190 @@ +using System; +using UnityEngine.Assertions; +using Unity.Collections; +using Unity.Jobs; +using Unity.Collections.LowLevel.Unsafe; +using UnityEngine.Profiling; + +namespace UnityEngine.Rendering +{ + internal class CPUDrawInstanceData + { + public NativeList drawInstances => m_DrawInstances; + public NativeParallelHashMap batchHash => m_BatchHash; + public NativeList drawBatches => m_DrawBatches; + public NativeParallelHashMap rangeHash => m_RangeHash; + public NativeList drawRanges => m_DrawRanges; + public NativeArray drawBatchIndices => m_DrawBatchIndices.AsArray(); + public NativeArray drawInstanceIndices => m_DrawInstanceIndices.AsArray(); + + private NativeParallelHashMap m_RangeHash; // index in m_DrawRanges, hashes by range state + private NativeList m_DrawRanges; + private NativeParallelHashMap m_BatchHash; // index in m_DrawBatches, hashed by draw state + private NativeList m_DrawBatches; + private NativeList m_DrawInstances; + private NativeList m_DrawInstanceIndices; // DOTS instance index, arranged in contiguous blocks in m_DrawBatches order (see DrawBatch.instanceOffset, DrawBatch.instanceCount) + private NativeList m_DrawBatchIndices; // index in m_DrawBatches, arranged in contiguous blocks in m_DrawRanges order (see DrawRange.drawOffset, DrawRange.drawCount) + + private bool m_NeedsRebuild; + + public bool valid => m_DrawInstances.IsCreated; + + public void Initialize() + { + Assert.IsTrue(!valid); + m_RangeHash = new NativeParallelHashMap(1024, Allocator.Persistent); + m_DrawRanges = new NativeList(Allocator.Persistent); + m_BatchHash = new NativeParallelHashMap(1024, Allocator.Persistent); + m_DrawBatches = new NativeList(Allocator.Persistent); + m_DrawInstances = new NativeList(1024, Allocator.Persistent); + m_DrawInstanceIndices = new NativeList(1024, Allocator.Persistent); + m_DrawBatchIndices = new NativeList(1024, Allocator.Persistent); + } + + public void Dispose() + { + if (m_DrawBatchIndices.IsCreated) + m_DrawBatchIndices.Dispose(); + + if (m_DrawInstanceIndices.IsCreated) + m_DrawInstanceIndices.Dispose(); + + if (m_DrawInstances.IsCreated) + m_DrawInstances.Dispose(); + + if (m_DrawBatches.IsCreated) + m_DrawBatches.Dispose(); + + if (m_BatchHash.IsCreated) + m_BatchHash.Dispose(); + + if (m_DrawRanges.IsCreated) + m_DrawRanges.Dispose(); + + if (m_RangeHash.IsCreated) + m_RangeHash.Dispose(); + } + + public void RebuildDrawListsIfNeeded() + { + if (!m_NeedsRebuild) + return; + + m_NeedsRebuild = false; + + Assert.IsTrue(m_RangeHash.Count() == m_DrawRanges.Length); + Assert.IsTrue(m_BatchHash.Count() == m_DrawBatches.Length); + + m_DrawInstanceIndices.ResizeUninitialized(m_DrawInstances.Length); + m_DrawBatchIndices.ResizeUninitialized(m_DrawBatches.Length); + + var internalDrawIndex = new NativeArray(drawBatches.Length * BuildDrawListsJob.k_IntsPerCacheLine, Allocator.TempJob, NativeArrayOptions.ClearMemory); + + var prefixSumDrawInstancesJob = new PrefixSumDrawInstancesJob() + { + rangeHash = m_RangeHash, + drawRanges = m_DrawRanges, + drawBatches = m_DrawBatches, + drawBatchIndices = m_DrawBatchIndices.AsArray() + }; + + var prefixSumJobHandle = prefixSumDrawInstancesJob.Schedule(); + + new BuildDrawListsJob() + { + drawInstances = m_DrawInstances, + batchHash = m_BatchHash, + drawBatches = m_DrawBatches, + internalDrawIndex = internalDrawIndex, + drawInstanceIndices = m_DrawInstanceIndices.AsArray(), + } + .RunParallel(m_DrawInstances.Length, 128, prefixSumJobHandle); + + internalDrawIndex.Dispose(); + } + + public unsafe void DestroyDrawInstanceIndices(NativeArray drawInstanceIndicesToDestroy) + { + Profiler.BeginSample("DestroyDrawInstanceIndices.ParallelSort"); + drawInstanceIndicesToDestroy.ParallelSort().Complete(); + Profiler.EndSample(); + + Profiler.BeginSample("DestroyDrawInstanceIndices.RemoveDrawInstanceIndices"); + CPUDrawInstanceDataBurst.RemoveDrawInstanceIndices(drawInstanceIndicesToDestroy, + ref m_DrawInstances, + ref m_RangeHash, + ref m_BatchHash, + ref m_DrawRanges, + ref m_DrawBatches); + Profiler.EndSample(); + } + + public unsafe void DestroyDrawInstances(NativeArray destroyedInstances) + { + if (m_DrawInstances.IsEmpty || destroyedInstances.Length == 0) + return; + + Profiler.BeginSample("DestroyDrawInstances"); + + NeedsRebuild(); + + var destroyedInstancesSorted = new NativeArray(destroyedInstances, Allocator.TempJob); + Assert.AreEqual(UnsafeUtility.SizeOf(), UnsafeUtility.SizeOf()); + + Profiler.BeginSample("DestroyDrawInstances.ParallelSort"); + destroyedInstancesSorted.Reinterpret().ParallelSort().Complete(); + Profiler.EndSample(); + + var drawInstanceIndicesToDestroy = new NativeList(m_DrawInstances.Length, Allocator.TempJob); + + new FindDrawInstancesJob() + { + instancesSorted = destroyedInstancesSorted, + drawInstances = m_DrawInstances, + outDrawInstanceIndicesWriter = drawInstanceIndicesToDestroy.AsParallelWriter() + } + .RunBatchParallel(m_DrawInstances.Length, FindDrawInstancesJob.k_MaxBatchSize); + + DestroyDrawInstanceIndices(drawInstanceIndicesToDestroy.AsArray()); + + destroyedInstancesSorted.Dispose(); + drawInstanceIndicesToDestroy.Dispose(); + + Profiler.EndSample(); + } + + public unsafe void DestroyMaterialDrawInstances(NativeArray destroyedBatchMaterials) + { + if (m_DrawInstances.IsEmpty || destroyedBatchMaterials.Length == 0) + return; + + NeedsRebuild(); + + var destroyedBatchMaterialsSorted = new NativeArray(destroyedBatchMaterials, Allocator.TempJob); + + Profiler.BeginSample("DestroyedBatchMaterials.ParallelSort"); + destroyedBatchMaterialsSorted.Reinterpret().ParallelSort().Complete(); + Profiler.EndSample(); + + var drawInstanceIndicesToDestroy = new NativeList(m_DrawInstances.Length, Allocator.TempJob); + + new FindMaterialDrawInstancesJob() + { + materialsSorted = destroyedBatchMaterialsSorted, + drawInstances = m_DrawInstances, + outDrawInstanceIndicesWriter = drawInstanceIndicesToDestroy.AsParallelWriter() + } + .RunBatchParallel(m_DrawInstances.Length, FindMaterialDrawInstancesJob.k_MaxBatchSize); + + DestroyDrawInstanceIndices(drawInstanceIndicesToDestroy.AsArray()); + + destroyedBatchMaterialsSorted.Dispose(); + drawInstanceIndicesToDestroy.Dispose(); + } + + public void NeedsRebuild() + { + m_NeedsRebuild = true; + } + } +} diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/RenderersParameters.cs.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Batching/CPUDrawInstanceData.cs.meta similarity index 83% rename from Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/RenderersParameters.cs.meta rename to Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Batching/CPUDrawInstanceData.cs.meta index b264d3cfe0b..7e7319e53c8 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/RenderersParameters.cs.meta +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Batching/CPUDrawInstanceData.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 79a7f7b13566d4a409e0df42e8f32bcc +guid: 8967952de1d564742ae0197935b63bd7 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Batching/CPUDrawInstanceDataBurst.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Batching/CPUDrawInstanceDataBurst.cs new file mode 100644 index 00000000000..d6ff5473e54 --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Batching/CPUDrawInstanceDataBurst.cs @@ -0,0 +1,71 @@ +using Unity.Collections; +using UnityEngine.Rendering; +using Unity.Burst; +using Unity.Collections.LowLevel.Unsafe; +using UnityEngine.Assertions; + +namespace UnityEngine.Rendering +{ + [BurstCompile] + internal static class CPUDrawInstanceDataBurst + { + private static void RemoveDrawRange(in RangeKey key, ref NativeParallelHashMap rangeHash, ref NativeList drawRanges) + { + int drawRangeIndex = rangeHash[key]; + + ref DrawRange lastDrawRange = ref drawRanges.ElementAt(drawRanges.Length - 1); + rangeHash[lastDrawRange.key] = drawRangeIndex; + + rangeHash.Remove(key); + drawRanges.RemoveAtSwapBack(drawRangeIndex); + } + + private static void RemoveDrawBatch(in DrawKey key, ref NativeParallelHashMap rangeHash, ref NativeParallelHashMap batchHash, + ref NativeList drawRanges, ref NativeList drawBatches) + { + int drawBatchIndex = batchHash[key]; + + ref DrawBatch drawBatch = ref drawBatches.ElementAt(drawBatchIndex); + + int drawRangeIndex = rangeHash[key.range]; + ref DrawRange drawRange = ref drawRanges.ElementAt(drawRangeIndex); + + Assert.IsTrue(drawRange.drawCount > 0); + + if (--drawRange.drawCount == 0) + RemoveDrawRange(drawRange.key, ref rangeHash, ref drawRanges); + + ref DrawBatch lastDrawBatch = ref drawBatches.ElementAt(drawBatches.Length - 1); + batchHash[lastDrawBatch.key] = drawBatchIndex; + + batchHash.Remove(key); + drawBatches.RemoveAtSwapBack(drawBatchIndex); + } + + [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] + public static unsafe void RemoveDrawInstanceIndices(in NativeArray drawInstanceIndicesSorted, ref NativeList drawInstances, ref NativeParallelHashMap rangeHash, + ref NativeParallelHashMap batchHash, ref NativeList drawRanges, ref NativeList drawBatches) + { + var drawInstancesPtr = (DrawInstance*)drawInstances.GetUnsafePtr(); + var drawInstancesNewBack = drawInstances.Length - 1; + + for (int indexRev = drawInstanceIndicesSorted.Length - 1; indexRev >= 0; --indexRev) + { + int indexToRemove = drawInstanceIndicesSorted[indexRev]; + DrawInstance* drawInstance = drawInstancesPtr + indexToRemove; + + int drawBatchIndex = batchHash[drawInstance->key]; + ref DrawBatch drawBatch = ref drawBatches.ElementAt(drawBatchIndex); + + Assert.IsTrue(drawBatch.instanceCount > 0); + + if (--drawBatch.instanceCount == 0) + RemoveDrawBatch(drawBatch.key, ref rangeHash, ref batchHash, ref drawRanges, ref drawBatches); + + UnsafeUtility.MemCpy(drawInstance, drawInstancesPtr + drawInstancesNewBack--, sizeof(DrawInstance)); + } + + drawInstances.ResizeUninitialized(drawInstancesNewBack + 1); + } + } +} diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Batching/CPUDrawInstanceDataBurst.cs.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Batching/CPUDrawInstanceDataBurst.cs.meta new file mode 100644 index 00000000000..a676aafb0af --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Batching/CPUDrawInstanceDataBurst.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: f89188da7ac3dd74e920a8a99c1c0de1 \ No newline at end of file diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Batching/InstanceCullingBatcher.Jobs.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Batching/InstanceCullingBatcher.Jobs.cs new file mode 100644 index 00000000000..79601e296fb --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Batching/InstanceCullingBatcher.Jobs.cs @@ -0,0 +1,234 @@ +using System; +using System.Threading; +using UnityEngine.Assertions; +using Unity.Collections; +using Unity.Jobs; +using Unity.Jobs.LowLevel.Unsafe; +using Unity.Collections.LowLevel.Unsafe; +using Unity.Burst; +using Unity.Mathematics; + +[assembly: RegisterGenericJobType(typeof(UnityEngine.Rendering.FindNonRegisteredInstanceIDsJob))] +[assembly: RegisterGenericJobType(typeof(UnityEngine.Rendering.FindNonRegisteredInstanceIDsJob))] + +namespace UnityEngine.Rendering +{ + [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] + internal struct PrefixSumDrawInstancesJob : IJob + { + [ReadOnly] public NativeParallelHashMap rangeHash; + + public NativeList drawRanges; + public NativeList drawBatches; + public NativeArray drawBatchIndices; + + public void Execute() + { + Assert.AreEqual(rangeHash.Count(), drawRanges.Length); + Assert.AreEqual(drawBatchIndices.Length, drawBatches.Length); + + // Prefix sum to calculate draw offsets for each DrawRange + int drawPrefixSum = 0; + + for (int i = 0; i < drawRanges.Length; ++i) + { + ref DrawRange drawRange = ref drawRanges.ElementAt(i); + drawRange.drawOffset = drawPrefixSum; + drawPrefixSum += drawRange.drawCount; + } + + // Generate DrawBatch index ranges for each DrawRange + var internalRangeIndex = new NativeArray(drawRanges.Length, Allocator.Temp); + + for (int i = 0; i < drawBatches.Length; ++i) + { + ref DrawBatch drawBatch = ref drawBatches.ElementAt(i); + Assert.IsTrue(drawBatch.instanceCount > 0); + + if (rangeHash.TryGetValue(drawBatch.key.range, out int drawRangeIndex)) + { + ref DrawRange drawRange = ref drawRanges.ElementAt(drawRangeIndex); + drawBatchIndices[drawRange.drawOffset + internalRangeIndex[drawRangeIndex]] = i; + internalRangeIndex[drawRangeIndex]++; + } + } + + // Prefix sum to calculate instance offsets for each DrawCommand + int drawInstancesPrefixSum = 0; + + for (int i = 0; i < drawBatchIndices.Length; ++i) + { + // DrawIndices remap to get DrawCommands ordered by DrawRange + var drawBatchIndex = drawBatchIndices[i]; + ref DrawBatch drawBatch = ref drawBatches.ElementAt(drawBatchIndex); + drawBatch.instanceOffset = drawInstancesPrefixSum; + drawInstancesPrefixSum += drawBatch.instanceCount; + } + + internalRangeIndex.Dispose(); + } + } + + [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] + internal unsafe struct BuildDrawListsJob : IJobParallelFor + { + public const int k_IntsPerCacheLine = JobsUtility.CacheLineSize / sizeof(int); + + [ReadOnly] public NativeParallelHashMap batchHash; + [NativeDisableContainerSafetyRestriction, NoAlias] [ReadOnly] public NativeList drawInstances; + [NativeDisableContainerSafetyRestriction, NoAlias] [ReadOnly] public NativeList drawBatches; + + [NativeDisableContainerSafetyRestriction, NoAlias] [WriteOnly] public NativeArray internalDrawIndex; + [NativeDisableContainerSafetyRestriction, NoAlias] [WriteOnly] public NativeArray drawInstanceIndices; + + private unsafe static int IncrementCounter(int* counter) + { + return Interlocked.Increment(ref UnsafeUtility.AsRef(counter)) - 1; + } + + public void Execute(int index) + { + // Generate instance index ranges for each DrawCommand + ref DrawInstance drawInstance = ref drawInstances.ElementAt(index); + int drawBatchIndex = batchHash[drawInstance.key]; + + ref DrawBatch drawBatch = ref drawBatches.ElementAt(drawBatchIndex); + var offset = IncrementCounter((int*)internalDrawIndex.GetUnsafePtr() + drawBatchIndex * k_IntsPerCacheLine); + var writeIndex = drawBatch.instanceOffset + offset; + drawInstanceIndices[writeIndex] = drawInstance.instanceIndex; + } + } + + [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] + internal unsafe struct FindDrawInstancesJob : IJobParallelForBatch + { + public const int k_MaxBatchSize = 128; + + [ReadOnly] public NativeArray instancesSorted; + [NativeDisableContainerSafetyRestriction, NoAlias] [ReadOnly] public NativeList drawInstances; + + [WriteOnly] public NativeList.ParallelWriter outDrawInstanceIndicesWriter; + + public void Execute(int startIndex, int count) + { + int* instancesToRemovePtr = stackalloc int[k_MaxBatchSize]; + var instancesToRemove = new UnsafeList(instancesToRemovePtr, k_MaxBatchSize); + instancesToRemove.Length = 0; + + for (int i = startIndex; i < startIndex + count; ++i) + { + ref DrawInstance drawInstance = ref drawInstances.ElementAt(i); + + if (instancesSorted.BinarySearch(InstanceHandle.Create(drawInstance.instanceIndex)) >= 0) + instancesToRemove.AddNoResize(i); + } + + outDrawInstanceIndicesWriter.AddRangeNoResize(instancesToRemove); + } + } + + [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] + internal unsafe struct FindMaterialDrawInstancesJob : IJobParallelForBatch + { + public const int k_MaxBatchSize = 128; + + [ReadOnly] public NativeArray materialsSorted; + [NativeDisableContainerSafetyRestriction, NoAlias] [ReadOnly] public NativeList drawInstances; + + [WriteOnly] public NativeList.ParallelWriter outDrawInstanceIndicesWriter; + + public void Execute(int startIndex, int count) + { + int* instancesToRemovePtr = stackalloc int[k_MaxBatchSize]; + var instancesToRemove = new UnsafeList(instancesToRemovePtr, k_MaxBatchSize); + instancesToRemove.Length = 0; + + for (int i = startIndex; i < startIndex + count; ++i) + { + ref DrawInstance drawInstance = ref drawInstances.ElementAt(i); + + if (materialsSorted.BinarySearch(drawInstance.key.materialID.value) >= 0) + instancesToRemove.AddNoResize(i); + } + + outDrawInstanceIndicesWriter.AddRangeNoResize(instancesToRemove); + } + } + + [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] + internal struct FindNonRegisteredInstanceIDsJob : IJobParallelFor where T : unmanaged + { + public const int MaxBatchSize = 128; + + [ReadOnly] public NativeArray jobRanges; + [ReadOnly] public JaggedSpan jaggedInstanceIDs; + [ReadOnly] public NativeParallelHashMap hashMap; + + [WriteOnly] public NativeParallelHashSet.ParallelWriter outInstanceIDWriter; + + public unsafe void Execute(int jobIndex) + { + JaggedJobRange jobRange = jobRanges[jobIndex]; + NativeArray instanceIDs = jaggedInstanceIDs[jobRange.sectionIndex]; + + for (int i = jobRange.localStart; i < jobRange.localEnd; ++i) + { + EntityId instanceID = instanceIDs[i]; + + if (!hashMap.ContainsKey(instanceID)) + outInstanceIDWriter.Add(instanceID); + } + } + } + + [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] + internal struct RegisterNewMaterialsJob : IJobParallelFor + { + [ReadOnly] public NativeArray instanceIDs; + [ReadOnly] public NativeArray materials; + + [WriteOnly] public NativeParallelHashMap.ParallelWriter materialMap; + + public unsafe void Execute(int index) + { + bool success = materialMap.TryAdd(instanceIDs[index], materials.ElementAt(index)); + Assert.IsTrue(success); + } + } + + [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] + internal struct RegisterNewMeshesJob : IJobParallelFor + { + [ReadOnly] public NativeArray instanceIDs; + [ReadOnly] public NativeArray batchMeshIDs; + [ReadOnly] public NativeArray meshDatas; + [ReadOnly] public NativeArray subMeshOffsets; + [ReadOnly] public NativeArray subMeshBuffer; + + [WriteOnly] public NativeParallelHashMap.ParallelWriter meshMap; + + public unsafe void Execute(int index) + { + Assert.IsTrue(instanceIDs.Length == meshDatas.Length); + Assert.IsTrue(instanceIDs.Length == subMeshOffsets.Length); + + EntityId instanceID = instanceIDs[index]; + int subMeshOffset = subMeshOffsets[index]; + GPUDrivenMeshData meshData = meshDatas[index]; + BatchMeshID batchMeshID = batchMeshIDs[index]; + + int totalSubMeshCount = meshData.subMeshCount * math.max(meshData.meshLodCount, 1); + NativeArray subMeshes = subMeshBuffer.GetSubArray(subMeshOffset, totalSubMeshCount); + var embeddedSubMeshes = new EmbeddedArray64(subMeshes, Allocator.Persistent); + + MeshInfo meshInfo = default; + meshInfo.meshID = batchMeshID; + meshInfo.meshLodCount = meshData.meshLodCount; + meshInfo.meshLodSelectionCurve = meshData.meshLodSelectionCurve; + meshInfo.subMeshes = embeddedSubMeshes; + + bool success = meshMap.TryAdd(instanceID, meshInfo); + Assert.IsTrue(success); + } + } +} diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/GPUInstanceDataBuffer.cs.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Batching/InstanceCullingBatcher.Jobs.cs.meta similarity index 83% rename from Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/GPUInstanceDataBuffer.cs.meta rename to Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Batching/InstanceCullingBatcher.Jobs.cs.meta index 5f41e3dcbcb..d70322baa48 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/GPUInstanceDataBuffer.cs.meta +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Batching/InstanceCullingBatcher.Jobs.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 390ad9eb5576b8e46bf6230c1bb8081b +guid: 84f2d5310507cd645a110b652fbfd1d7 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Batching/InstanceCullingBatcher.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Batching/InstanceCullingBatcher.cs new file mode 100644 index 00000000000..8d6f8ec1d37 --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Batching/InstanceCullingBatcher.cs @@ -0,0 +1,474 @@ +using System; +using Unity.Collections; +using Unity.Jobs; +using UnityEngine.Assertions; +using UnityEngine.Profiling; + +namespace UnityEngine.Rendering +{ + internal delegate void OnCullingCompleteCallback(JobHandle jobHandle, in BatchCullingContext cullingContext, in BatchCullingOutput cullingOutput); + + internal struct MeshInfo + { + public BatchMeshID meshID; + public int meshLodCount; + public Mesh.LodSelectionCurve meshLodSelectionCurve; + public EmbeddedArray64 subMeshes; + + public bool isLodSelectionActive => meshLodCount > 1; + + public bool EqualsSourceData(in GPUDrivenMeshData otherData, NativeArray otherSubMeshes) + { + if (meshLodCount != otherData.meshLodCount) + return false; + + if (meshLodSelectionCurve.lodBias != otherData.meshLodSelectionCurve.lodBias) + return false; + + if (meshLodSelectionCurve.lodSlope != otherData.meshLodSelectionCurve.lodSlope) + return false; + + if (subMeshes.Length != otherSubMeshes.Length) + return false; + + for (int i = 0; i < subMeshes.Length; i++) + { + GPUDrivenSubMesh oldSubMesh = subMeshes[i]; + GPUDrivenSubMesh newSubMesh = otherSubMeshes[i]; + + if (!oldSubMesh.Equals(newSubMesh)) + return false; + } + + return true; + } + } + + internal class InstanceCullingBatcher : IDisposable + { + private GPUResidentContext m_GRDContext; + private InstanceDataSystem m_InstanceDataSystem; + private LODGroupDataSystem m_LODGroupDataSystem; + private CPUDrawInstanceData m_DrawInstanceData; + private BatchRendererGroup m_BRG; + private OnCullingCompleteCallback m_OnCompleteCallback; + private NativeParallelHashMap m_BatchIDs; + private NativeParallelHashMap m_MaterialMap; + private NativeParallelHashMap m_MeshMap; + private int m_CachedInstanceDataBufferLayoutVersion; + private NativeArray m_TempBatchMeshIDs; + private NativeHashSet m_TempChangedMeshIDs; + + public NativeParallelHashMap materialMap => m_MaterialMap; + public NativeParallelHashMap meshMap => m_MeshMap; + + public void Initialize(GPUResidentContext grdContext, + in GPUResidentDrawerSettings settings, + BatchRendererGroup.OnFinishedCulling onFinishedCulling, + OnCullingCompleteCallback onCompleteCallback = null) + { + m_GRDContext = grdContext; + m_InstanceDataSystem = grdContext.instanceDataSystem; + m_LODGroupDataSystem = grdContext.lodGroupDataSystem; + m_DrawInstanceData = new CPUDrawInstanceData(); + m_DrawInstanceData.Initialize(); + + m_BRG = new BatchRendererGroup(new BatchRendererGroupCreateInfo + { + cullingCallback = OnPerformCulling, + finishedCullingCallback = onFinishedCulling, + userContext = IntPtr.Zero + }); + +#if UNITY_EDITOR + if (settings.pickingShader != null) + { + var mat = new Material(settings.pickingShader); + mat.hideFlags = HideFlags.HideAndDontSave; + m_BRG.SetPickingMaterial(mat); + } + if (settings.loadingShader != null) + { + var mat = new Material(settings.loadingShader); + mat.hideFlags = HideFlags.HideAndDontSave; + m_BRG.SetLoadingMaterial(mat); + } + if (settings.errorShader != null) + { + var mat = new Material(settings.errorShader); + mat.hideFlags = HideFlags.HideAndDontSave; + m_BRG.SetErrorMaterial(mat); + } + m_BRG.SetEnabledViewTypes(new BatchCullingViewType[] + { + BatchCullingViewType.Light, + BatchCullingViewType.Camera, + BatchCullingViewType.Picking, + BatchCullingViewType.SelectionOutline, + BatchCullingViewType.Filtering + }); +#endif + + m_CachedInstanceDataBufferLayoutVersion = -1; + m_OnCompleteCallback = onCompleteCallback; + m_MaterialMap = new NativeParallelHashMap(64, Allocator.Persistent); + m_MeshMap = new NativeParallelHashMap(64, Allocator.Persistent); + m_BatchIDs = new NativeParallelHashMap(8, Allocator.Persistent); + m_InstanceDataSystem.onGPUBufferLayoutChanged += UpdateInstanceDataBufferLayoutVersion; + } + + public void Dispose() + { + m_OnCompleteCallback = null; + + m_InstanceDataSystem.onGPUBufferLayoutChanged -= UpdateInstanceDataBufferLayoutVersion; + + foreach (var batchID in m_BatchIDs) + { + m_BRG.RemoveBatch(batchID.Value); + } + m_BatchIDs.Dispose(); + + if (m_BRG != null) + m_BRG.Dispose(); + + m_DrawInstanceData.Dispose(); + m_DrawInstanceData = null; + + m_MaterialMap.Dispose(); + + foreach (var kv in m_MeshMap) + kv.Value.subMeshes.Dispose(); + m_MeshMap.Dispose(); + } + + private BatchID AddBatch(GPUArchetypeHandle archetype) + { + if (m_CachedInstanceDataBufferLayoutVersion != m_InstanceDataSystem.gpuBufferLayoutVersion) + return BatchID.Null; + + var instanceDataBuffer = m_InstanceDataSystem.gpuBuffer; + var archetypeDesc = m_InstanceDataSystem.archetypeManager.GetRef().GetArchetypeDesc(archetype); + var components = archetypeDesc.components; + + var metadatas = new NativeArray(components.Length, Allocator.Temp); + + for (int i = 0; i < metadatas.Length; ++i) + { + var componentHandle = components[i]; + var compIndex = instanceDataBuffer.GetComponentIndex(componentHandle).index; + var metadata = m_InstanceDataSystem.gpuBuffer.componentsMetadata[compIndex]; + metadatas[i] = metadata; + } + + return m_BRG.AddBatch(metadatas, m_InstanceDataSystem.gpuBufferHandle); + } + + private void UpdateInstanceDataBufferLayoutVersion() + { + if (m_CachedInstanceDataBufferLayoutVersion != m_InstanceDataSystem.gpuBufferLayoutVersion) + { + m_CachedInstanceDataBufferLayoutVersion = m_InstanceDataSystem.gpuBufferLayoutVersion; + + foreach (var kv in m_BatchIDs) + { + var batchID = kv.Value; + m_BRG.RemoveBatch(batchID); + } + m_BatchIDs.Clear(); + + var archetypeManager = m_InstanceDataSystem.archetypeManager; + var archetypeCount = archetypeManager.GetRef().GetArchetypesCount(); + + for (int i = 0; i < archetypeCount; i++) + { + var archetype = GPUArchetypeHandle.Create((short)i); + var batchID = AddBatch(archetype); + m_BatchIDs.Add(archetype, batchID); + } + } + } + + private void OnFetchMeshesDataForRegistration(NativeArray meshIDs, + NativeArray meshDatas, + NativeArray subMeshOffsets, + NativeArray subMeshes) + { + Assert.IsTrue(meshIDs.Length == meshDatas.Length); + Assert.IsTrue(m_TempBatchMeshIDs.Length == meshIDs.Length); + + new RegisterNewMeshesJob + { + meshMap = m_MeshMap.AsParallelWriter(), + instanceIDs = meshIDs, + batchMeshIDs = m_TempBatchMeshIDs, + meshDatas = meshDatas, + subMeshOffsets = subMeshOffsets, + subMeshBuffer = subMeshes, + } + .RunParallel(meshIDs.Length, 128); + } + + private void RegisterMeshes(JaggedSpan meshIDs) + { + Profiler.BeginSample("RegisterMeshes"); + var jobRanges = JaggedJobRange.FromSpanWithMaxBatchSize(meshIDs, FindNonRegisteredInstanceIDsJob.MaxBatchSize, Allocator.TempJob); + var newMeshSet = new NativeParallelHashSet(meshIDs.totalLength, Allocator.TempJob); + + new FindNonRegisteredInstanceIDsJob + { + jobRanges = jobRanges.AsArray(), + jaggedInstanceIDs = meshIDs, + hashMap = m_MeshMap, + outInstanceIDWriter = newMeshSet.AsParallelWriter() + } + .RunParallel(jobRanges); + + if (!newMeshSet.IsEmpty) + { + NativeArray newMeshIDs = newMeshSet.ToNativeArray(Allocator.TempJob); + + var batchMeshIDs = new NativeArray(newMeshIDs.Length, Allocator.TempJob); + GPUDrivenProcessor.RegisterMeshes(m_BRG, newMeshIDs, batchMeshIDs); + + int totalMeshesNum = m_MeshMap.Count() + newMeshIDs.Length; + m_MeshMap.Capacity = Math.Max(m_MeshMap.Capacity, Mathf.CeilToInt(totalMeshesNum / 1023.0f) * 1024); + + m_TempBatchMeshIDs = batchMeshIDs; + GPUDrivenProcessor.FetchMeshDatas(newMeshIDs, OnFetchMeshesDataForRegistration); + m_TempBatchMeshIDs = default; + + newMeshIDs.Dispose(); + batchMeshIDs.Dispose(); + } + + jobRanges.Dispose(); + newMeshSet.Dispose(); + Profiler.EndSample(); + } + + private void RegisterMaterials(JaggedSpan materials) + { + Profiler.BeginSample("RegisterMaterials"); + var jobRanges = JaggedJobRange.FromSpanWithMaxBatchSize(materials, FindNonRegisteredInstanceIDsJob.MaxBatchSize, Allocator.TempJob); + var newMaterialIDSet = new NativeParallelHashSet(materials.totalLength, Allocator.TempJob); + + new FindNonRegisteredInstanceIDsJob + { + jobRanges = jobRanges.AsArray(), + jaggedInstanceIDs = materials, + hashMap = m_MaterialMap, + outInstanceIDWriter = newMaterialIDSet.AsParallelWriter() + } + .RunParallel(jobRanges); + + if (!newMaterialIDSet.IsEmpty) + { + NativeArray newMaterialIDs = newMaterialIDSet.ToNativeArray(Allocator.TempJob); + + var newMaterials = new NativeArray(newMaterialIDs.Length, Allocator.TempJob); + GPUDrivenProcessor.RegisterMaterials(m_BRG, newMaterialIDs, newMaterials); + + int totalMaterialsNum = m_MaterialMap.Count() + newMaterialIDs.Length; + m_MaterialMap.Capacity = Math.Max(m_MaterialMap.Capacity, Mathf.CeilToInt(totalMaterialsNum / 1023.0f) * 1024); + + new RegisterNewMaterialsJob + { + instanceIDs = newMaterialIDs, + materials = newMaterials, + materialMap = m_MaterialMap.AsParallelWriter() + } + .RunParallel(newMaterialIDs.Length, 128); + + newMaterialIDs.Dispose(); + newMaterials.Dispose(); + } + + jobRanges.Dispose(); + newMaterialIDSet.Dispose(); + Profiler.EndSample(); + } + + private void OnFetchMeshesDataForUpdate(NativeArray meshIDs, + NativeArray meshDatas, + NativeArray subMeshOffsets, + NativeArray subMeshBuffer) + { + Assert.IsTrue(meshIDs.Length == meshDatas.Length); + Assert.IsTrue(m_TempChangedMeshIDs.IsCreated); + + InstanceCullingBatcherBurst.UpdateMeshData(meshIDs, meshDatas, subMeshOffsets, subMeshBuffer, ref m_MeshMap, ref m_TempChangedMeshIDs); + } + + public NativeHashSet UpdateMeshData(NativeArray meshIDs, Allocator allocator) + { + NativeHashSet changeMeshIDs = new NativeHashSet(meshIDs.Length, allocator); + + m_TempChangedMeshIDs = changeMeshIDs; + GPUDrivenProcessor.FetchMeshDatas(meshIDs, OnFetchMeshesDataForUpdate); + m_TempChangedMeshIDs = default; + + return changeMeshIDs; + } + + public NativeHashSet UpdateMaterialData(NativeArray materials, NativeArray materialDatas, Allocator allocator) + { + NativeHashSet changeMaterialIDs = new NativeHashSet(materials.Length, allocator); + InstanceCullingBatcherBurst.UpdateMaterialData(materials, materialDatas, ref m_MaterialMap, ref changeMaterialIDs); + return changeMaterialIDs; + } + + public CPUDrawInstanceData GetDrawInstanceData() + { + return m_DrawInstanceData; + } + + public unsafe JobHandle OnPerformCulling(BatchRendererGroup rendererGroup, BatchCullingContext context, BatchCullingOutput cullingOutput, IntPtr userContext) + { + foreach (var batchID in m_BatchIDs) + { + if (batchID.Value == BatchID.Null) + return new JobHandle(); + } + + m_DrawInstanceData.RebuildDrawListsIfNeeded(); + + if (m_DrawInstanceData.drawRanges.Length == 0) + return default; + + if (m_DrawInstanceData.drawBatches.Length == 0) + return default; + + IncludeExcludeListFilter includeExcludeListFilter = default; + +#if UNITY_EDITOR + + includeExcludeListFilter = IncludeExcludeListFilter.GetFilterForCurrentCullingCallback(context, Allocator.TempJob); + + // If inclusive filtering is enabled and we know there are no included entities, + // we can skip all the work because we know that the result will be nothing. + if (includeExcludeListFilter.IsIncludeEnabled && includeExcludeListFilter.IsIncludeEmpty) + { + includeExcludeListFilter.Dispose(); + return default; + } +#else + includeExcludeListFilter = IncludeExcludeListFilter.GetEmptyFilter(Allocator.TempJob); +#endif + + bool allowOcclusionCulling = m_InstanceDataSystem.hasBoundingSpheres; + JobHandle jobHandle = m_GRDContext.culler.CreateCullJobTree( + context, + cullingOutput, + m_InstanceDataSystem.renderWorld, + m_GRDContext.batcher.meshMap, + m_InstanceDataSystem.gpuBuffer.AsReadOnly(), + m_LODGroupDataSystem.lodGroupCullingData, + m_DrawInstanceData, + m_BatchIDs, + m_GRDContext.smallMeshScreenPercentage, + allowOcclusionCulling ? m_GRDContext.occlusionCullingCommon : null, + includeExcludeListFilter); + + if (m_OnCompleteCallback != null) + m_OnCompleteCallback(jobHandle, context, cullingOutput); + + includeExcludeListFilter.Dispose(jobHandle); + return jobHandle; + } + + public void DestroyDrawInstances(NativeArray instances) + { + m_DrawInstanceData.DestroyDrawInstances(instances); + } + + public void DestroyMaterials(NativeArray destroyedInstanceIDs) + { + if (destroyedInstanceIDs.Length == 0) + return; + + Profiler.BeginSample("DestroyMaterials"); + + var destroyedBatchMaterials = new NativeList(destroyedInstanceIDs.Length, Allocator.TempJob); + + foreach (EntityId instanceID in destroyedInstanceIDs) + { + if (m_MaterialMap.TryGetValue(instanceID, out GPUDrivenMaterial materialData)) + { + BatchMaterialID batchMaterialID = materialData.materialID; + destroyedBatchMaterials.Add(batchMaterialID.value); + m_MaterialMap.Remove(instanceID); + m_BRG.UnregisterMaterial(batchMaterialID); + } + } + + m_DrawInstanceData.DestroyMaterialDrawInstances(destroyedBatchMaterials.AsArray()); + + destroyedBatchMaterials.Dispose(); + + Profiler.EndSample(); + } + + public void DestroyMeshes(NativeArray destroyedInstanceIDs) + { + if (destroyedInstanceIDs.Length == 0) + return; + + Profiler.BeginSample("DestroyMeshes"); + + foreach (EntityId instanceID in destroyedInstanceIDs) + { + if (m_MeshMap.TryGetValue(instanceID, out var meshData)) + { + meshData.subMeshes.Dispose(); + m_MeshMap.Remove(instanceID); + m_BRG.UnregisterMesh(meshData.meshID); + } + } + + Profiler.EndSample(); + } + + public void BuildBatches(NativeArray instances) + { + Profiler.BeginSample("BuildBatches"); + + DestroyDrawInstances(instances); + + var rangeHash = m_DrawInstanceData.rangeHash; + var drawRanges = m_DrawInstanceData.drawRanges; + var batchHash = m_DrawInstanceData.batchHash; + var drawBatches = m_DrawInstanceData.drawBatches; + var drawInstances = m_DrawInstanceData.drawInstances; + + InstanceCullingBatcherBurst.CreateDrawBatches(instances, + ref m_InstanceDataSystem.renderWorld, + m_MeshMap, + m_MaterialMap, + ref rangeHash, + ref drawRanges, + ref batchHash, + ref drawBatches, + ref drawInstances); + + m_DrawInstanceData.NeedsRebuild(); + + Profiler.EndSample(); + } + + public void RegisterAndBuildBatches(NativeArray instances, in MeshRendererUpdateBatch updateBatch) + { + Profiler.BeginSample("RegisterAndBuildBatches"); + + if (updateBatch.HasAnyComponent(MeshRendererComponentMask.Material)) + RegisterMaterials(updateBatch.materialIDs); + + if (updateBatch.HasAnyComponent(MeshRendererComponentMask.Mesh)) + RegisterMeshes(updateBatch.meshIDs); + + BuildBatches(instances); + + Profiler.EndSample(); + } + } +} diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceCullingBatcher.cs.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Batching/InstanceCullingBatcher.cs.meta similarity index 100% rename from Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceCullingBatcher.cs.meta rename to Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Batching/InstanceCullingBatcher.cs.meta diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Batching/InstanceCullingBatcherBurst.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Batching/InstanceCullingBatcherBurst.cs new file mode 100644 index 00000000000..33ec5d56f1d --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Batching/InstanceCullingBatcherBurst.cs @@ -0,0 +1,248 @@ +using Unity.Collections; +using Unity.Burst; +using UnityEngine.Assertions; +using Unity.Mathematics; + +namespace UnityEngine.Rendering +{ + [BurstCompile] + internal static class InstanceCullingBatcherBurst + { + [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] + public static void UpdateMaterialData(in NativeArray materialIDs, + in NativeArray materialDatas, + ref NativeParallelHashMap materialMap, + ref NativeHashSet changedMaterialIDs) + { + for (int index = 0; index < materialIDs.Length; index++) + { + EntityId materialID = materialIDs[index]; + GPUDrivenMaterialData newMaterialData = materialDatas[index]; + + if (!materialMap.TryGetValue(materialID, out var material) || material.data.Equals(newMaterialData)) + continue; + + // Update the material data + material.data = newMaterialData; + materialMap[materialID] = material; + changedMaterialIDs.Add(materialID); + } + } + + [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] + public static void UpdateMeshData(in NativeArray meshIDs, + in NativeArray meshDatas, + in NativeArray subMeshOffsets, + in NativeArray subMeshBuffer, + ref NativeParallelHashMap meshMap, + ref NativeHashSet changedMeshIDs) + { + for (int index = 0; index < meshIDs.Length; index++) + { + EntityId meshID = meshIDs[index]; + + GPUDrivenMeshData newMeshData = meshDatas[index]; + int subMeshOffset = subMeshOffsets[index]; + int totalSubMeshCount = newMeshData.subMeshCount * math.max(newMeshData.meshLodCount, 1); + NativeArray newSubMeshes = subMeshBuffer.GetSubArray(subMeshOffset, totalSubMeshCount); + + if (!meshMap.TryGetValue(meshID, out var meshInfo) || meshInfo.EqualsSourceData(newMeshData, newSubMeshes)) + continue; + + MeshInfo newMeshInfo = meshInfo; + newMeshInfo.meshLodCount = newMeshData.meshLodCount; + newMeshInfo.meshLodSelectionCurve = newMeshData.meshLodSelectionCurve; + newMeshInfo.subMeshes = new EmbeddedArray64(newSubMeshes, Allocator.Persistent); + + // Update the mesh data + meshMap[meshID] = newMeshInfo; + changedMeshIDs.Add(meshID); + + meshInfo.subMeshes.Dispose(); + } + } + + private static ref DrawRange EditDrawRange(in RangeKey key, ref NativeParallelHashMap rangeHash, ref NativeList drawRanges) + { + int drawRangeIndex; + + if (!rangeHash.TryGetValue(key, out drawRangeIndex)) + { + var drawRange = new DrawRange { key = key, drawCount = 0, drawOffset = 0 }; + drawRangeIndex = drawRanges.Length; + rangeHash.Add(key, drawRangeIndex); + drawRanges.Add(drawRange); + } + + ref DrawRange data = ref drawRanges.ElementAt(drawRangeIndex); + Assert.IsTrue(data.key.Equals(key)); + + return ref data; + } + + private static ref DrawBatch EditDrawBatch(in DrawKey key, + in GPUDrivenSubMesh subMesh, + ref NativeParallelHashMap batchHash, + ref NativeList drawBatches) + { + int drawBatchIndex; + + if (!batchHash.TryGetValue(key, out drawBatchIndex)) + { + var drawBatch = new DrawBatch + { + key = key, + instanceCount = 0, + instanceOffset = 0, + baseVertex = subMesh.baseVertex, + firstIndex = subMesh.indexStart, + indexCount = subMesh.indexCount, + topology = subMesh.topology, + }; + + drawBatchIndex = drawBatches.Length; + batchHash.Add(key, drawBatchIndex); + drawBatches.Add(drawBatch); + } + + ref DrawBatch data = ref drawBatches.ElementAt(drawBatchIndex); + Assert.IsTrue(data.key.Equals(key)); + + return ref data; + } + + private static void ProcessRenderer(InstanceHandle instance, + ref RenderWorld renderWorld, + in NativeParallelHashMap meshMap, + in NativeParallelHashMap materialMap, + ref NativeParallelHashMap rangeHash, + ref NativeList drawRanges, + ref NativeParallelHashMap batchHash, + ref NativeList drawBatches, + ref NativeList drawInstances) + { + Assert.IsTrue(instance.isValid, "Invalid Instance"); + if (!instance.isValid) + return; + + int instanceIndex = renderWorld.HandleToIndex(instance); + GPUArchetypeHandle archetype = renderWorld.gpuHandles[instanceIndex].archetype; + EntityId meshID = renderWorld.meshIDs[instanceIndex]; + EntityId rendererID = renderWorld.instanceIDs[instanceIndex]; + short lightmapIndex = renderWorld.lightmapIndices[instanceIndex]; + InternalMeshRendererSettings rendererSettings = renderWorld.rendererSettings[instanceIndex]; + int rendererPriority = renderWorld.rendererPriorities[instanceIndex]; + ushort subMeshStartIndex = renderWorld.subMeshStartIndices[instanceIndex]; + EmbeddedArray32 subMaterialIDs = renderWorld.materialIDArrays[instanceIndex]; + + if (!meshMap.TryGetValue(meshID, out MeshInfo mesh)) + return; + + // Scan all materials once to retrieve whether this renderer is indirect-compatible or not (and store it in the RangeKey). + // Also cache hash map lookups since we need them right after. + bool supportsIndirect = true; + NativeArray subMaterials = new NativeArray(subMaterialIDs.Length, Allocator.Temp); + for (int i = 0; i < subMaterialIDs.Length; i++) + { + EntityId subMaterialID = subMaterialIDs[i]; + if (!materialMap.TryGetValue(subMaterialID, out GPUDrivenMaterial subMaterial)) + continue; + + supportsIndirect &= subMaterial.isIndirectSupported; + subMaterials[i] = subMaterial; + } + + var rangeKey = new RangeKey + { + layer = rendererSettings.ObjectLayer, + renderingLayerMask = rendererSettings.RenderingLayerMask, + motionMode = rendererSettings.MotionVectorGenerationMode, + shadowCastingMode = rendererSettings.ShadowCastingMode, + staticShadowCaster = rendererSettings.StaticShadowCaster, + rendererPriority = rendererPriority, + supportsIndirect = supportsIndirect + }; + + ref DrawRange drawRange = ref EditDrawRange(rangeKey, ref rangeHash, ref drawRanges); + + for (int i = 0; i < subMaterials.Length; i++) + { + GPUDrivenMaterial subMaterial = subMaterials[i]; + if (subMaterial.materialID == BatchMaterialID.Null) + continue; + + // We always provide crossfade value packed in instance index. We don't use None even if there is no LOD to not split the batch. + var flags = BatchDrawCommandFlags.LODCrossFadeValuePacked; + + if (LightmapUtils.UsesLightmaps(lightmapIndex)) + flags |= BatchDrawCommandFlags.UseLegacyLightmapsKeyword; + + // assume that a custom motion vectors pass contains deformation motion, so should always output motion vectors + // (otherwise this flag is set dynamically during culling only when the transform is changing) + if (subMaterial.isMotionVectorsPassEnabled) + flags |= BatchDrawCommandFlags.HasMotion; + + if (subMaterial.isTransparent) + flags |= BatchDrawCommandFlags.HasSortingPosition; + + if (subMaterial.supportsCrossFade) + flags |= BatchDrawCommandFlags.LODCrossFadeKeyword; + + // Static batching uses per MeshRenderer sub-mesh offset + int subMeshIndex = subMeshStartIndex + i; + int lodLoopCount = math.max(mesh.meshLodCount, 1); + + for (int lodLoopIndex = 0; lodLoopIndex < lodLoopCount; lodLoopIndex++) + { + GPUDrivenSubMesh subMesh = mesh.subMeshes[subMeshIndex * lodLoopCount + lodLoopIndex]; + + var drawKey = new DrawKey + { + materialID = subMaterial.materialID, + meshID = mesh.meshID, + submeshIndex = subMeshIndex, + activeMeshLod = mesh.isLodSelectionActive ? lodLoopIndex : -1, + flags = flags, + transparentInstanceID = subMaterial.isTransparent ? rendererID : EntityId.None, + range = rangeKey, + archetype = archetype, + // When we've opted out of lightmap texture arrays, we + // need to pass in a valid lightmap index. The engine + // uses this index for sorting and for breaking the + // batch when lightmaps change across draw calls, and + // for binding the correct light map. + lightmapIndex = lightmapIndex + }; + + ref DrawBatch drawBatch = ref EditDrawBatch(drawKey, subMesh, ref batchHash, ref drawBatches); + + if (drawBatch.instanceCount == 0) + drawRange.drawCount += 1; + + drawBatch.instanceCount += 1; + + drawInstances.Add(new DrawInstance + { + key = drawKey, + instanceIndex = instance.index + }); + } + } + } + + [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] + public static void CreateDrawBatches(in NativeArray instances, + ref RenderWorld renderWorld, + in NativeParallelHashMap meshMap, + in NativeParallelHashMap materialMap, + ref NativeParallelHashMap rangeHash, + ref NativeList drawRanges, + ref NativeParallelHashMap batchHash, + ref NativeList drawBatches, + ref NativeList drawInstances) + { + for (int i = 0; i < instances.Length; i++) + ProcessRenderer(instances[i], ref renderWorld, meshMap, materialMap, ref rangeHash, ref drawRanges, ref batchHash, ref drawBatches, ref drawInstances); + } + } +} diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Batching/InstanceCullingBatcherBurst.cs.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Batching/InstanceCullingBatcherBurst.cs.meta new file mode 100644 index 00000000000..1b0e01d25f8 --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Batching/InstanceCullingBatcherBurst.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: a27d8a9d1dff4734cb0e88406d333941 \ No newline at end of file diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems.meta new file mode 100644 index 00000000000..124a9455229 --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 88615a6f33ae421468a6a44c5ec2a601 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/DefaultGPUComponents.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/DefaultGPUComponents.cs new file mode 100644 index 00000000000..a9857151f98 --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/DefaultGPUComponents.cs @@ -0,0 +1,115 @@ +using Unity.Collections; + +namespace UnityEngine.Rendering +{ + // Move all propertyID static fields to a separate struct. + // Burst does not support external calls like PropertyToID from static constructors. + static internal class DefaultShaderPropertyID + { + public static readonly int unity_SHCoefficients = Shader.PropertyToID("unity_SHCoefficients"); + public static readonly int unity_LightmapST = Shader.PropertyToID("unity_LightmapST"); + public static readonly int unity_ObjectToWorld = Shader.PropertyToID("unity_ObjectToWorld"); + public static readonly int unity_WorldToObject = Shader.PropertyToID("unity_WorldToObject"); + public static readonly int unity_MatrixPreviousM = Shader.PropertyToID("unity_MatrixPreviousM"); + public static readonly int unity_MatrixPreviousMI = Shader.PropertyToID("unity_MatrixPreviousMI"); + public static readonly int unity_WorldBoundingSphere = Shader.PropertyToID("unity_WorldBoundingSphere"); + public static readonly int unity_RendererUserValuesPropertyEntry = Shader.PropertyToID("unity_RendererUserValuesPropertyEntry"); + + public static readonly int[] DOTS_ST_WindParams = new int[(int)SpeedTreeWindParamIndex.MaxWindParamsCount]; + public static readonly int[] DOTS_ST_WindHistoryParams = new int[(int)SpeedTreeWindParamIndex.MaxWindParamsCount]; + + static DefaultShaderPropertyID() + { + for (int i = 0; i < (int)SpeedTreeWindParamIndex.MaxWindParamsCount; ++i) + { + DOTS_ST_WindParams[i] = Shader.PropertyToID($"DOTS_ST_WindParam{i}"); + DOTS_ST_WindHistoryParams[i] = Shader.PropertyToID($"DOTS_ST_WindHistoryParam{i}"); + } + } + } + + internal struct DefaultGPUComponents + { + public readonly GPUComponentHandle shCoefficients; + public readonly GPUComponentHandle lightmapScaleOffset; + public readonly GPUComponentHandle objectToWorld; + public readonly GPUComponentHandle worldToObject; + public readonly GPUComponentHandle matrixPreviousM; + public readonly GPUComponentHandle matrixPreviousMI; + public readonly GPUComponentHandle rendererUserValues; + public readonly GPUComponentHandle boundingSphere; + public readonly NativeArray speedTreeWind; + public readonly NativeArray speedTreeWindHistory; + + public readonly GPUComponentSet requiredComponentSet; + public readonly GPUComponentSet lightProbesComponentSet; + public readonly GPUComponentSet speedTreeComponentSet; + public readonly GPUComponentSet defaultGOComponentSet; + public readonly GPUComponentSet defaultSpeedTreeComponentSet; + + public readonly GPUArchetypeHandle defaultGOArchetype; + + public DefaultGPUComponents(ref GPUArchetypeManager archetypeManager, bool enableBoundingSpheresInstanceData) + { + shCoefficients = archetypeManager.CreateComponent(DefaultShaderPropertyID.unity_SHCoefficients, true); + lightmapScaleOffset = archetypeManager.CreateComponent(DefaultShaderPropertyID.unity_LightmapST, true); + objectToWorld = archetypeManager.CreateComponent(DefaultShaderPropertyID.unity_ObjectToWorld, true); + worldToObject = archetypeManager.CreateComponent(DefaultShaderPropertyID.unity_WorldToObject, true); + matrixPreviousM = archetypeManager.CreateComponent(DefaultShaderPropertyID.unity_MatrixPreviousM, true); + matrixPreviousMI = archetypeManager.CreateComponent(DefaultShaderPropertyID.unity_MatrixPreviousMI, true); + rendererUserValues = archetypeManager.CreateComponent(DefaultShaderPropertyID.unity_RendererUserValuesPropertyEntry, true); + + boundingSphere = enableBoundingSpheresInstanceData + ? archetypeManager.CreateComponent(DefaultShaderPropertyID.unity_WorldBoundingSphere, true) + : default; + + speedTreeWind = new NativeArray((int)SpeedTreeWindParamIndex.MaxWindParamsCount, Allocator.Persistent); + speedTreeWindHistory = new NativeArray((int)SpeedTreeWindParamIndex.MaxWindParamsCount, Allocator.Persistent); + + for (int i = 0; i < (int)SpeedTreeWindParamIndex.MaxWindParamsCount; ++i) + speedTreeWind[i] = archetypeManager.CreateComponent(DefaultShaderPropertyID.DOTS_ST_WindParams[i], true); + for (int i = 0; i < (int)SpeedTreeWindParamIndex.MaxWindParamsCount; ++i) + speedTreeWindHistory[i] = archetypeManager.CreateComponent(DefaultShaderPropertyID.DOTS_ST_WindHistoryParams[i], true); + + //@ Investigate how to avoid uploading the previous matrices for everything. + // It should only be needed when using object motion vectors. + requiredComponentSet = new GPUComponentSet() + { + objectToWorld, + worldToObject, + matrixPreviousM, + matrixPreviousMI, + rendererUserValues, + }; + + if (enableBoundingSpheresInstanceData) + requiredComponentSet.Add(boundingSphere); + + lightProbesComponentSet = new GPUComponentSet() + { + shCoefficients, + }; + + speedTreeComponentSet = new GPUComponentSet(); + + for (int i = 0; i < speedTreeWind.Length; ++i) + speedTreeComponentSet.Add(speedTreeWind[i]); + for (int i = 0; i < speedTreeWindHistory.Length; ++i) + speedTreeComponentSet.Add(speedTreeWindHistory[i]); + + defaultGOComponentSet = requiredComponentSet; + + defaultSpeedTreeComponentSet = defaultGOComponentSet; + defaultSpeedTreeComponentSet.Add(lightmapScaleOffset); + defaultSpeedTreeComponentSet.AddSet(speedTreeComponentSet); + + defaultGOArchetype = archetypeManager.GetOrCreateArchetype(defaultGOComponentSet); + } + + public void Dispose() + { + speedTreeWind.Dispose(); + speedTreeWindHistory.Dispose(); + } + } +} diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/DefaultGPUComponents.cs.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/DefaultGPUComponents.cs.meta new file mode 100644 index 00000000000..4233a8e57db --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/DefaultGPUComponents.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 22f804ff14ea3354fb0ae80ecfba8616 \ No newline at end of file diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/GPUArchetypeManager.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/GPUArchetypeManager.cs new file mode 100644 index 00000000000..488214876a4 --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/GPUArchetypeManager.cs @@ -0,0 +1,300 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; +using Unity.Mathematics; +using UnityEngine.Assertions; + +namespace UnityEngine.Rendering +{ + internal struct GPUArchetypeManager : IDisposable + { + // We can make this larger if needed. If we need 128 the we just need to have 2 ulong componentMask in GPUComponentSet. + public const int kMaxComponentsCount = 64; + + public const int kGPUArchetypeBits = 6; + public const int kMaxGPUArchetypesCount = 1 << kGPUArchetypeBits; + public const int kGPUArchetypeBitsMask = kMaxGPUArchetypesCount - 1; + + private NativeHandleAllocator m_ComponentHandleAllocator; + private NativeHandleAllocator m_ArchetypeHandleAllocator; + + private NativeList m_Components; + private NativeList m_Archetypes; + + private NativeParallelHashMap m_ComponentHash; + private NativeParallelHashMap m_ArchetypeHash; + private NativeParallelHashMap m_ComponentSetHash; + + private GraphicsDeviceType m_GraphicsDeviceType; + + public void Initialize() + { + m_ComponentHandleAllocator = new NativeHandleAllocator(); + m_ComponentHandleAllocator.Initialize(); + m_ArchetypeHandleAllocator = new NativeHandleAllocator(); + m_ArchetypeHandleAllocator.Initialize(); + + m_Components = new NativeList(Allocator.Persistent); + m_Archetypes = new NativeList(Allocator.Persistent); + + m_ComponentHash = new NativeParallelHashMap(16, Allocator.Persistent); + m_ArchetypeHash = new NativeParallelHashMap(16, Allocator.Persistent); + m_ComponentSetHash = new NativeParallelHashMap(16, Allocator.Persistent); + + m_GraphicsDeviceType = SystemInfo.graphicsDeviceType; + } + + public void Dispose() + { + m_ComponentHandleAllocator.Dispose(); + m_ArchetypeHandleAllocator.Dispose(); + foreach (var component in m_Components) + component.archetypes.Dispose(); + foreach (var archetype in m_Archetypes) + archetype.components.Dispose(); + m_Components.Dispose(); + m_Archetypes.Dispose(); + m_ComponentHash.Dispose(); + m_ArchetypeHash.Dispose(); + m_ComponentSetHash.Dispose(); + } + + public GPUComponentHandle FindComponent(int propertyID) + { + if (m_ComponentHash.TryGetValue(propertyID, out var componentHandle)) + return componentHandle; + + return GPUComponentHandle.Invalid; + } + + public GPUArchetypeHandle FindArchetype(GPUComponentSet componentSet) + { + if (m_ArchetypeHash.TryGetValue(componentSet, out var archetype)) + return archetype; + + return GPUArchetypeHandle.Invalid; + } + + public GPUComponentSet FindComponentSet(GPUArchetypeHandle archetype) + { + Assert.IsTrue(archetype.valid); + Assert.IsTrue(m_ComponentSetHash.ContainsKey(archetype)); + return m_ComponentSetHash[archetype]; + } + + public ref readonly GPUComponentDesc GetComponentDesc(GPUComponentHandle componentHandle) + { + Assert.IsTrue(componentHandle.valid); + return ref m_Components.ElementAt(componentHandle.index); + } + + public ref readonly GPUArchetypeDesc GetArchetypeDesc(GPUArchetypeHandle archetypeHandle) + { + Assert.IsTrue(archetypeHandle.valid); + return ref m_Archetypes.ElementAt(archetypeHandle.index); + } + + public int GetComponentsCount() => m_Components.Length; + + public int GetArchetypesCount() => m_Archetypes.Length; + + public GPUComponentHandle CreateComponent(int propertyID, int byteSize, bool isPerInstance) + { + //@ Should we extend this assert to all devices to address slowdowns caused by unaligned access on GPU? + if (m_GraphicsDeviceType == GraphicsDeviceType.OpenGLES3) + Assert.IsTrue(byteSize % UnsafeUtility.SizeOf() == 0, "On OpenGLES3 component size must be aligned by float4."); + Assert.IsTrue(m_Components.Length < kMaxComponentsCount, "Maximum GPU components count reached."); + Assert.IsTrue(byteSize > 0, "Component size is zero."); + Assert.IsTrue(!m_ComponentHash.ContainsKey(propertyID), $"Component with propertyID {propertyID} already exists."); + + var componentDesc = new GPUComponentDesc + { + propertyID = propertyID, + byteSize = byteSize, + isPerInstance = isPerInstance, + archetypes = new UnsafeList(4, Allocator.Persistent) + }; + + var componentHandle = GPUComponentHandle.Create((short)m_ComponentHandleAllocator.Allocate()); + + if (m_Components.Length <= componentHandle.index) + m_Components.Resize(componentHandle.index + 1, NativeArrayOptions.ClearMemory); + + m_Components[componentHandle.index] = componentDesc; + m_ComponentHash.Add(propertyID, componentHandle); + + return componentHandle; + } + + public GPUComponentHandle CreateComponent(int propertyID, bool isPerInstance) where T : unmanaged + { + return CreateComponent(propertyID, UnsafeUtility.SizeOf(), isPerInstance); + } + + public unsafe GPUArchetypeHandle CreateArchetype(GPUComponentSet componentSet) + { + Assert.IsTrue(m_Archetypes.Length < kMaxGPUArchetypesCount, "Maximum GPU archetypes count reached."); + Assert.IsFalse(m_ArchetypeHash.ContainsKey(componentSet), "Archetype with the same component set already exists."); + + var archetypeHandle = GPUArchetypeHandle.Create((short)m_ArchetypeHandleAllocator.Allocate()); + var components = componentSet.GetComponents(Allocator.Persistent); + var archetypeDesc = new GPUArchetypeDesc { components = components }; + + for (int i = 0; i < components.Length; ++i) + { + Assert.IsTrue(components[i].valid); + ref GPUComponentDesc componentDesc = ref m_Components.ElementAt(components[i].index); + Assert.IsFalse(componentDesc.archetypes.Contains(archetypeHandle), "Archetype already contains component."); + componentDesc.archetypes.Add(archetypeHandle); + } + + if (m_Archetypes.Length <= archetypeHandle.index) + m_Archetypes.Resize(archetypeHandle.index + 1, NativeArrayOptions.ClearMemory); + + m_Archetypes[archetypeHandle.index] = archetypeDesc; + m_ArchetypeHash.Add(componentSet, archetypeHandle); + m_ComponentSetHash.Add(archetypeHandle, componentSet); + + return archetypeHandle; + } + + public GPUArchetypeHandle GetOrCreateArchetype(GPUComponentSet componentSet) + { + if (m_ArchetypeHash.TryGetValue(componentSet, out var archetype)) + return archetype; + + return CreateArchetype(componentSet); + } + + public GPUComponentHandle GetOrCreateComponent(int propertyID, int byteSize, bool perInstance) + { + var componentHandle = FindComponent(propertyID); + if (!componentHandle.Equals(GPUComponentHandle.Invalid)) + return componentHandle; + + return CreateComponent(propertyID, byteSize, perInstance); + } + } + + internal struct GPUComponentDesc + { + public int propertyID; + public int byteSize; + public bool isPerInstance; + public UnsafeList archetypes; + } + + internal struct GPUArchetypeDesc + { + public NativeList components; + } + + internal struct GPUComponentHandle : IEquatable, IComparable + { + public short index { get; private set; } + public bool valid => index >= 0; + public static GPUComponentHandle Create(short index) { return new GPUComponentHandle { index = index }; } + public static readonly GPUComponentHandle Invalid = new GPUComponentHandle { index = -1 }; + public bool Equals(GPUComponentHandle other) => index == other.index; + public int CompareTo(GPUComponentHandle other) { return index.CompareTo(other.index); } + public override int GetHashCode() { return index; } + } + + internal struct GPUArchetypeHandle : IEquatable, IComparable + { + public short index { get; private set; } + public bool valid => index >= 0; + public static GPUArchetypeHandle Create(short index) { return new GPUArchetypeHandle { index = index }; } + public static readonly GPUArchetypeHandle Invalid = new GPUArchetypeHandle { index = -1 }; + public bool Equals(GPUArchetypeHandle other) => index == other.index; + public int CompareTo(GPUArchetypeHandle other) { return index.CompareTo(other.index); } + public override int GetHashCode() { return index; } + } + + //@ We could separate shader parameters from GPUComponents and alias different shader parameters to the same GPUComponent if they have the same size. + //@ Basically that would allow to have multiple GPUComponentSets ([default + float4 ParamName1], [default + float4 ParamName2], etc...) with the same component sizes but different propertyIDs but mapped to the same archetype. + //@ This will not be very hard to do but it will require some additional bookkeeping. But in the end it will decrease the number of archetypes that we have to handle on GPU. + internal struct GPUComponentSet : IEquatable, IComparable, IEnumerable + { + public ulong componentsMask { get; private set; } + + public bool isEmpty => componentsMask == 0; + + public bool Equals(GPUComponentSet other) => componentsMask == other.componentsMask; + public int CompareTo(GPUComponentSet other) { return componentsMask.CompareTo(other.componentsMask); } + public override int GetHashCode() { return componentsMask.GetHashCode(); } + + static GPUComponentSet() + { + Assert.AreEqual(GPUArchetypeManager.kMaxComponentsCount, UnsafeUtility.SizeOf() * 8, "Adjust componentsMask type to correspond to kMaxComponentsCount."); + } + + public GPUComponentSet(NativeArray components) + { + componentsMask = 0; + AddRange(components); + } + + public void Add(GPUComponentHandle componentHandle) + { + Assert.IsTrue(componentHandle.valid); + componentsMask |= 1ul << componentHandle.index; + } + + public void AddSet(GPUComponentSet componentSet) + { + componentsMask |= componentSet.componentsMask; + } + + public void AddRange(NativeArray components) + { + for (int i = 0; i < components.Length; ++i) + Add(components[i]); + } + + public int GetComponentsCount() + { + return math.countbits(componentsMask); + } + + public IEnumerator GetEnumerator() + { + int count = GetComponentsCount(); + for (int i = 0; i < count; i++) + yield return GetComponentByIndex(i); + } + + public GPUComponentHandle GetComponentByIndex(int index) + { + Assert.IsTrue(index >= 0 && index < GetComponentsCount(), "Index out of range."); + + ulong mask = componentsMask; + int compIndex = 0; + + while (mask != 0) + { + var component = GPUComponentHandle.Create((short)math.tzcnt(mask)); + mask &= ~(1ul << component.index); + if (compIndex++ == index) + return component; + } + + throw new InvalidOperationException("Index out of range."); + } + + public NativeList GetComponents(Allocator allocator) + { + var components = new NativeList(GetComponentsCount(), allocator); + + for (int i = 0; i < GPUArchetypeManager.kMaxComponentsCount; ++i) + { + if ((componentsMask & (1ul << i)) != 0) + components.AddNoResize(GPUComponentHandle.Create((short)i)); + } + + return components; + } + } +} diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/GPUArchetypeManager.cs.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/GPUArchetypeManager.cs.meta new file mode 100644 index 00000000000..e7708945d51 --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/GPUArchetypeManager.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 18fb68347df974242bdf9a3f2efe05bd diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/GPUInstanceDataBuffer.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/GPUInstanceDataBuffer.cs new file mode 100644 index 00000000000..9322fc2e58e --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/GPUInstanceDataBuffer.cs @@ -0,0 +1,1020 @@ +using System; +using System.Collections; +using Unity.Burst; +using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; +using Unity.Jobs; +using Unity.Mathematics; +using UnityEngine.Assertions; +using UnityEngine.Profiling; + +namespace UnityEngine.Rendering +{ + [GenerateHLSL(needAccessors = false)] + enum GPUInstanceDataBufferConstants + { + ThreadGroupSize = 128, + UIntPerThread = 16, + UIntPerThreadGroup = ThreadGroupSize * UIntPerThread, + MaxThreadGroupsPerDispatch = 65535, + } + + // GPU Buffer layout looks like this: + // Archetype0 { Component0, Component1 } + // Archetype1 { Component0, Component1, Component2 } + // Archetype2 { Component0, Component2, Component3 } + // See GPU Archetype PR for details https://github.cds.internal.unity3d.com/unity/unity/pull/50000 + internal class GPUInstanceDataBuffer : IDisposable + { + //@ For now 1Gb but this is should be smaller for OpenGL ES 3.1 + public const int MaxGPUInstancDataBufferSize = 1024 * 1024 * 1024; + + internal const int InvalidIndex = -1; + + private const int ThreadGroupSize = (int)GPUInstanceDataBufferConstants.ThreadGroupSize; + private const int UIntPerThread = (int)GPUInstanceDataBufferConstants.UIntPerThread; + private const int UIntPerThreadGroup = (int)GPUInstanceDataBufferConstants.UIntPerThreadGroup; + private const int MaxThreadGroupsPerDispatch = (int)GPUInstanceDataBufferConstants.MaxThreadGroupsPerDispatch; + + private int m_MainUploadScatterInstancesKernelID; + private int m_MainCopyInstancesKernelID; + + private InternalGPUInstanceDataBuffer m_InternalBuffer; + private int m_LayoutVersion = 0; + + private uint3 m_UploaKernelThreadGroupSize; + private ComputeShader m_InstanceDataBufferUploadKernels; + private ComputeShader m_InstanceDataBufferCopyKernels; + private GraphicsBuffer m_InputInstanceIndicesBuffer; + private GraphicsBuffer m_InputComponentAddressesBuffer; + private GraphicsBuffer m_OutputComponentIndicesBuffer; + + public NativeArray.ReadOnly componentPerInstance => m_InternalBuffer.componentPerInstance.AsReadOnly(); + public NativeArray.ReadOnly componentsGPUAddress => m_InternalBuffer.componentsGPUAddress.AsReadOnly(); + public NativeArray.ReadOnly componentByteSizes => m_InternalBuffer.componentByteSizes.AsReadOnly(); + public NativeArray.ReadOnly componentInstanceIndexRanges => m_InternalBuffer.componentInstanceIndexRanges.AsReadOnly(); + public NativeArray.ReadOnly componentsMetadata => m_InternalBuffer.componentsMetadata.AsReadOnly(); + public NativeArray.ReadOnly components => m_InternalBuffer.components.AsReadOnly(); + public int gpuBufferByteSize => m_InternalBuffer.gpuBufferByteSize; + public GraphicsBuffer nativeBuffer => m_InternalBuffer.gpuBuffer; + public int layoutVersion => m_LayoutVersion; + + public GPUInstanceDataBuffer(ref GPUArchetypeManager archetypeManager, in GPUInstanceDataBufferLayout layout, GPUResidentDrawerResources resources) + { + m_InstanceDataBufferUploadKernels = resources.instanceDataBufferUploadKernels; + m_InstanceDataBufferCopyKernels = resources.instanceDataBufferCopyKernels; + m_InternalBuffer = new InternalGPUInstanceDataBuffer(ref archetypeManager, layout); + + m_MainUploadScatterInstancesKernelID = m_InstanceDataBufferUploadKernels.FindKernel("MainUploadScatterInstances"); + m_MainCopyInstancesKernelID = m_InstanceDataBufferCopyKernels.FindKernel("MainCopyInstances"); + Assert.IsTrue(m_MainUploadScatterInstancesKernelID != -1, "Unable to load MainUploadScatterInstances compute shader kernel."); + Assert.IsTrue(m_MainCopyInstancesKernelID != -1, "Unable to load MainCopyInstances compute shader kernel."); + + m_InstanceDataBufferUploadKernels.GetKernelThreadGroupSizes(m_MainUploadScatterInstancesKernelID, + out m_UploaKernelThreadGroupSize.x, + out m_UploaKernelThreadGroupSize.y, + out m_UploaKernelThreadGroupSize.z); + } + + public void Dispose() + { + m_InputInstanceIndicesBuffer?.Release(); + m_InputComponentAddressesBuffer?.Release(); + m_OutputComponentIndicesBuffer?.Release(); + m_InternalBuffer.Dispose(); + m_InternalBuffer = null; + } + + private static MetadataValue CreateMetadataValue(int nameID, int gpuAddress, bool isPerInstance) + { + // See UnityDOTSInstancing.hlsl + const uint kPerInstanceDataBit = 0x80000000; + return new MetadataValue { NameID = nameID, Value = (uint)gpuAddress | (isPerInstance ? kPerInstanceDataBit : 0) }; + } + + public ComponentIndex GetComponentIndex(GPUComponentHandle component) + { + int componentIndex = m_InternalBuffer.FindComponentIndex(component); + Assert.IsTrue(componentIndex != InvalidIndex, "Component is not allocated."); + return new ComponentIndex(componentIndex, m_LayoutVersion); + } + + public int GetComponentGPUAddress(ComponentIndex compIndex) + { + Assert.IsTrue(compIndex.layoutVersion == m_LayoutVersion, "Component index was acquired from previous layout. Update component index."); + return m_InternalBuffer.componentsGPUAddress[compIndex.index]; + } + + public int GetComponentGPUAddress(GPUComponentHandle component) + { + ComponentIndex compIndex = GetComponentIndex(component); + return m_InternalBuffer.componentsGPUAddress[compIndex.index]; + } + + public int GetComponentGPUUIntOffset(GPUComponentHandle component) + { + return GetComponentGPUAddress(component) / UnsafeUtility.SizeOf(); + } + + public bool IsArchetypeAllocated(GPUArchetypeHandle archetype) + { + return m_InternalBuffer.FindArchetypeIndex(archetype) != InvalidIndex; + } + + public ArchetypeIndex GetArchetypeIndex(GPUArchetypeHandle archetype) + { + int archetypeIndex = m_InternalBuffer.FindArchetypeIndex(archetype); + Assert.IsTrue(archetypeIndex != InvalidIndex, "Archetype is not allocated."); + return new ArchetypeIndex(archetypeIndex, m_LayoutVersion); + } + + public int GetInstancesCount(in ArchetypeIndex archIndex) + { + Assert.IsTrue(archIndex.layoutVersion == m_LayoutVersion, "Archetype index was acquired from previous layout. Update archetype index."); + return m_InternalBuffer.layout.instancesCount[archIndex.index]; + } + + public GPUInstanceIndex InstanceToGPUIndex(in ArchetypeIndex archIndex, int instanceIndex) + { + Assert.IsTrue(archIndex.layoutVersion == m_LayoutVersion, "Archetype index was acquired from previous layout. Update archetype index."); + int instancesBegin = m_InternalBuffer.instancesCountPrefixSum[archIndex.index]; + int instancesCount = m_InternalBuffer.layout.instancesCount[archIndex.index]; + Assert.IsTrue(instanceIndex >= 0 && instanceIndex < instancesCount); + return GPUInstanceIndex.Create(instancesBegin + instanceIndex); + } + + public void QueryInstanceGPUIndices(in RenderWorld renderWorld, NativeArray instances, NativeArray gpuIndices) + { + Assert.AreEqual(instances.Length, gpuIndices.Length); + + Profiler.BeginSample("QueryInstanceGPUIndices"); + + new InstancesToGPUIndicesJob + { + renderWorld = renderWorld, + instancesCountPrefixSum = m_InternalBuffer.instancesCountPrefixSum, + layout = m_InternalBuffer.layout, + instances = instances, + gpuIndices = gpuIndices + } + .RunParallel(instances.Length, 512); + + Profiler.EndSample(); + } + + public void UploadDataToGPU(CommandBuffer cmd, GraphicsBuffer uploadBuffer, in GPUInstanceUploadData uploadData, NativeArray scatterGPUIndices) + { + Assert.IsNotNull(cmd); + + if (uploadData.length == 0) + return; + + Profiler.BeginSample("UploadGPUInstanceData"); + + Assert.IsTrue(scatterGPUIndices.Length <= uploadData.length); + + var outputComponentIndices = new NativeArray(uploadData.writtenComponents.Length, Allocator.Temp, NativeArrayOptions.UninitializedMemory); + var inputComponentAddresses = new NativeArray(uploadData.writtenComponents.Length, Allocator.Temp, NativeArrayOptions.UninitializedMemory); + + for (int i = 0; i < uploadData.writtenComponents.Length; ++i) + { + outputComponentIndices[i] = m_InternalBuffer.FindComponentIndex(uploadData.writtenComponents[i]); + Assert.AreNotEqual(outputComponentIndices[i], InvalidIndex); + int inputComponentIndex = uploadData.FindComponentIndex(uploadData.writtenComponents[i]); + inputComponentAddresses[i] = uploadData.componentGPUAddress[inputComponentIndex]; + } + + m_InputInstanceIndicesBuffer = EnsureBufferCountOrResize(m_InputInstanceIndicesBuffer, scatterGPUIndices.Length, UnsafeUtility.SizeOf()); + m_InputComponentAddressesBuffer = EnsureBufferCountOrResize(m_InputComponentAddressesBuffer, inputComponentAddresses.Length, sizeof(int)); + m_OutputComponentIndicesBuffer = EnsureBufferCountOrResize(m_OutputComponentIndicesBuffer, outputComponentIndices.Length, sizeof(int)); + + m_InputInstanceIndicesBuffer.SetData(scatterGPUIndices); + m_InputComponentAddressesBuffer.SetData(inputComponentAddresses); + m_OutputComponentIndicesBuffer.SetData(outputComponentIndices); + + ComputeShader shader = m_InstanceDataBufferUploadKernels; + cmd.SetComputeIntParam(shader, UploadKernelID.kInputComponentsCount, uploadData.writtenComponents.Length); + cmd.SetComputeIntParam(shader, UploadKernelID.kInputInstancesCount, scatterGPUIndices.Length); + cmd.SetComputeBufferParam(shader, m_MainUploadScatterInstancesKernelID, UploadKernelID.kInputInstanceData, uploadBuffer); + cmd.SetComputeBufferParam(shader, m_MainUploadScatterInstancesKernelID, UploadKernelID.kInputInstanceIndices, m_InputInstanceIndicesBuffer); + cmd.SetComputeBufferParam(shader, m_MainUploadScatterInstancesKernelID, UploadKernelID.kInputComponentAddresses, m_InputComponentAddressesBuffer); + cmd.SetComputeBufferParam(shader, m_MainUploadScatterInstancesKernelID, UploadKernelID.kOutputComponentByteCounts, m_InternalBuffer.componentByteCountsGPUBuffer); + cmd.SetComputeBufferParam(shader, m_MainUploadScatterInstancesKernelID, UploadKernelID.kOutputComponentIndices, m_OutputComponentIndicesBuffer); + cmd.SetComputeBufferParam(shader, m_MainUploadScatterInstancesKernelID, UploadKernelID.kOutputComponentInstanceIndexRanges, m_InternalBuffer.componentGPUInstanceIndexRangesGPUBuffer); + cmd.SetComputeBufferParam(shader, m_MainUploadScatterInstancesKernelID, UploadKernelID.kOutputComponentIsPerInstance, m_InternalBuffer.componentsPerInstanceGPUBuffer); + cmd.SetComputeBufferParam(shader, m_MainUploadScatterInstancesKernelID, UploadKernelID.kOutputComponentAddresses, m_InternalBuffer.componentsGPUAddressGPUBuffer); + cmd.SetComputeBufferParam(shader, m_MainUploadScatterInstancesKernelID, UploadKernelID.kOutputBuffer, m_InternalBuffer.gpuBuffer); + + int threadGroupCountX = CoreUtils.DivRoundUp(scatterGPUIndices.Length, (int)m_UploaKernelThreadGroupSize.x); + Assert.IsTrue(m_UploaKernelThreadGroupSize.y == 1); + Assert.IsTrue(m_UploaKernelThreadGroupSize.z == 1); + + cmd.DispatchCompute(shader, m_MainUploadScatterInstancesKernelID, threadGroupCountX, 1, 1); + + Graphics.ExecuteCommandBuffer(cmd); + cmd.Clear(); + + Profiler.EndSample(); + } + + public void SetGPULayout(CommandBuffer cmd, ref GPUArchetypeManager archetypeManager, in GPUInstanceDataBufferLayout newLayout, bool submitCmdBuffer) + { + if (submitCmdBuffer) + Assert.IsNotNull(cmd); + + if (m_InternalBuffer.layout.Equals(newLayout)) + return; + + //@ If the GPU layout is smaller, we could perform the re-layout in place using a smaller scratch GPU helper buffer, instead of always allocating a new one. + var newInternalBuffer = new InternalGPUInstanceDataBuffer(ref archetypeManager, newLayout); + + var inputComponentDataAddresses = new NativeList(Allocator.Temp); + var outputComponentDataAddresses = new NativeList(Allocator.Temp); + var outputComponentDataUIntSizes = new NativeList(Allocator.Temp); + var inputThreadGroupBeginIndices = new NativeList(Allocator.Temp); + var threadGroupsCount = 0; + + for (int newArchetypeIndex = 0; newArchetypeIndex < newInternalBuffer.layout.archetypes.Length; ++newArchetypeIndex) + { + var archetype = newInternalBuffer.layout.archetypes[newArchetypeIndex]; + int archetypeIndex = m_InternalBuffer.FindArchetypeIndex(archetype); + + if (archetypeIndex == InvalidIndex) + continue; + + var instancesBegin = m_InternalBuffer.instancesCountPrefixSum[archetypeIndex]; + var instancesCount = m_InternalBuffer.layout.instancesCount[archetypeIndex]; + var newInstancesBegin = newInternalBuffer.instancesCountPrefixSum[newArchetypeIndex]; + var newInstancesCount = newInternalBuffer.layout.instancesCount[newArchetypeIndex]; + + if (instancesCount == 0 || newInstancesCount == 0) + continue; + + ref readonly GPUArchetypeDesc archetypeDesc = ref archetypeManager.GetArchetypeDesc(archetype); + + foreach (GPUComponentHandle component in archetypeDesc.components) + { + int componentIndex = m_InternalBuffer.FindComponentIndex(component); + int newComponentIndex = newInternalBuffer.FindComponentIndex(component); + Assert.AreNotEqual(componentIndex, InvalidIndex); + Assert.AreNotEqual(newComponentIndex, InvalidIndex); + + var newComponentArchetypeIndexSpan = newInternalBuffer.componentsArchetypeIndexSpan[newComponentIndex]; + var newComponentInstanceIndexRange = newInternalBuffer.componentInstanceIndexRanges[newComponentIndex]; + Assert.IsTrue(newArchetypeIndex >= newComponentArchetypeIndexSpan.x && newArchetypeIndex < newComponentArchetypeIndexSpan.y); + Assert.IsTrue(newInstancesBegin >= newComponentInstanceIndexRange.x && newInstancesBegin + newInstancesCount <= newComponentInstanceIndexRange.y); + Assert.AreEqual(newInternalBuffer.componentByteSizes[newComponentIndex], m_InternalBuffer.componentByteSizes[componentIndex]); + + ref readonly GPUComponentDesc componentDesc = ref archetypeManager.GetComponentDesc(component); + int isPerInstance = componentDesc.isPerInstance ? 1 : 0; + int instancesToCopy = componentDesc.isPerInstance ? math.min(instancesCount, newInstancesCount) : 1; + int componentByteSize = newInternalBuffer.componentByteSizes[newComponentIndex]; + int componentDataAddress = m_InternalBuffer.componentsGPUAddress[componentIndex] + instancesBegin * componentByteSize * isPerInstance; + int newCopmonentDataAddress = newInternalBuffer.componentsGPUAddress[newComponentIndex] + newInstancesBegin * componentByteSize * isPerInstance; + int componentDataUIntSize = (componentByteSize * instancesToCopy) / sizeof(uint); + + int threadGroupBeginIndex = threadGroupsCount; + int threadGroups = CoreUtils.DivRoundUp(componentDataUIntSize, UIntPerThreadGroup); + threadGroupsCount += threadGroups; + + inputComponentDataAddresses.Add(componentDataAddress); + outputComponentDataAddresses.Add(newCopmonentDataAddress); + outputComponentDataUIntSizes.Add(componentDataUIntSize); + inputThreadGroupBeginIndices.Add(threadGroupBeginIndex); + } + } + + inputThreadGroupBeginIndices.Add(threadGroupsCount); + + if (threadGroupsCount > 0) + { + inputThreadGroupBeginIndices.ResizeUninitialized(CollectionHelper.Align(inputThreadGroupBeginIndices.Length, sizeof(uint))); + inputComponentDataAddresses.ResizeUninitialized(CollectionHelper.Align(inputComponentDataAddresses.Length, sizeof(uint))); + outputComponentDataAddresses.ResizeUninitialized(CollectionHelper.Align(outputComponentDataAddresses.Length, sizeof(uint))); + outputComponentDataUIntSizes.ResizeUninitialized(CollectionHelper.Align(outputComponentDataUIntSizes.Length, sizeof(uint))); + + var inputThreadGroupBeginIndicesGPUBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, inputThreadGroupBeginIndices.Length, sizeof(uint)); + var inputComponentDataAddressesGPUBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, inputComponentDataAddresses.Length, sizeof(uint)); + var outputComponentDataAddressesGPUBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, outputComponentDataAddresses.Length, sizeof(uint)); + var outputComponentDataUIntSizesGPUBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, outputComponentDataUIntSizes.Length, sizeof(uint)); + + inputThreadGroupBeginIndicesGPUBuffer.SetData(inputThreadGroupBeginIndices.AsArray()); + inputComponentDataAddressesGPUBuffer.SetData(inputComponentDataAddresses.AsArray()); + outputComponentDataAddressesGPUBuffer.SetData(outputComponentDataAddresses.AsArray()); + outputComponentDataUIntSizesGPUBuffer.SetData(outputComponentDataUIntSizes.AsArray()); + + var shader = m_InstanceDataBufferCopyKernels; + cmd.SetComputeIntParam(shader, CopyKernelID.kInputComponentsCount, inputComponentDataAddresses.Length); + cmd.SetComputeBufferParam(shader, m_MainCopyInstancesKernelID, CopyKernelID.kInputThreadGroupBeginIndices, inputThreadGroupBeginIndicesGPUBuffer); + cmd.SetComputeBufferParam(shader, m_MainCopyInstancesKernelID, CopyKernelID.kInputComponentDataAddresses, inputComponentDataAddressesGPUBuffer); + cmd.SetComputeBufferParam(shader, m_MainCopyInstancesKernelID, CopyKernelID.kOutputComponentDataAddresses, outputComponentDataAddressesGPUBuffer); + cmd.SetComputeBufferParam(shader, m_MainCopyInstancesKernelID, CopyKernelID.kOutputComponentDataUIntSizes, outputComponentDataUIntSizesGPUBuffer); + cmd.SetComputeBufferParam(shader, m_MainCopyInstancesKernelID, CopyKernelID.kInputBuffer, m_InternalBuffer.gpuBuffer); + cmd.SetComputeBufferParam(shader, m_MainCopyInstancesKernelID, CopyKernelID.kOutputBuffer, newInternalBuffer.gpuBuffer); + int dispatchesCount = CoreUtils.DivRoundUp(threadGroupsCount, MaxThreadGroupsPerDispatch); + int dispatchedThreadGroups = 0; + for (int i = 0; i < dispatchesCount; ++i) + { + int dispatchThreadGroupCount = math.min(threadGroupsCount - dispatchedThreadGroups, MaxThreadGroupsPerDispatch); + cmd.SetComputeIntParam(shader, CopyKernelID.kDispatchThreadGroupBase, dispatchedThreadGroups); + cmd.DispatchCompute(shader, m_MainCopyInstancesKernelID, dispatchThreadGroupCount, 1, 1); + dispatchedThreadGroups += dispatchThreadGroupCount; + } + Assert.AreEqual(dispatchedThreadGroups, threadGroupsCount); + + if (submitCmdBuffer) + { + Graphics.ExecuteCommandBuffer(cmd); + cmd.Clear(); + } + + inputThreadGroupBeginIndicesGPUBuffer.Release(); + inputComponentDataAddressesGPUBuffer.Release(); + outputComponentDataAddressesGPUBuffer.Release(); + outputComponentDataUIntSizesGPUBuffer.Release(); + } + + m_InternalBuffer.Dispose(); + m_InternalBuffer = newInternalBuffer; + m_LayoutVersion += 1; + } + + public ReadOnly AsReadOnly() + { + return new ReadOnly(this); + } + + static GraphicsBuffer EnsureBufferCountOrResize(GraphicsBuffer buffer, int requestedCount, int stride) + { + if (buffer == null || buffer.count < requestedCount) + { + int currentCount = 0; + if (buffer != null) + { + // Stride changes not handled + Assert.IsTrue(buffer.stride == stride); + + currentCount = buffer.count; + buffer.Release(); + } + + // At least double on resize + int newCount = math.max(currentCount * 2, requestedCount); + buffer = new GraphicsBuffer(GraphicsBuffer.Target.Raw, newCount, stride); + } + + return buffer; + } + + internal readonly struct ReadOnly + { + public readonly GPUInstanceDataBufferLayout.ReadOnly layout; + public readonly NativeArray.ReadOnly componentsGPUAddress; + public readonly NativeArray.ReadOnly instancesCountPrefixSum; + public readonly NativeArray.ReadOnly componentIndices; + + public ReadOnly(GPUInstanceDataBuffer buffer) + { + layout = buffer.m_InternalBuffer.layout.AsReadOnly(); + componentsGPUAddress = buffer.m_InternalBuffer.componentsGPUAddress.AsReadOnly(); + instancesCountPrefixSum = buffer.m_InternalBuffer.instancesCountPrefixSum.AsReadOnly(); + componentIndices = buffer.m_InternalBuffer.componentIndices.AsReadOnly(); + } + + public int GetComponentIndex(GPUComponentHandle component) + { + Assert.IsTrue(component.valid); + if (component.index < componentIndices.Length) + return componentIndices[component.index]; + + Assert.IsTrue(false, "Component is not allocated."); + return InvalidIndex; + } + + public int GetComponentGPUAddress(GPUComponentHandle component) + { + Assert.IsTrue(component.valid && component.index < componentIndices.Length); + return componentsGPUAddress[componentIndices[component.index]]; + } + + public GPUInstanceIndex InstanceGPUHandleToGPUIndex(InstanceGPUHandle gpuHandle) + { + Assert.IsTrue(gpuHandle.isValid); + int archetypeIndex = layout.FindArchetypeIndex(gpuHandle.archetype); + int instancesBegin = instancesCountPrefixSum[archetypeIndex]; + int instancesCount = layout.instancesCount[archetypeIndex]; + Assert.IsTrue(gpuHandle.archetypeInstanceIndex >= 0 && gpuHandle.archetypeInstanceIndex < instancesCount); + return GPUInstanceIndex.Create(instancesBegin + gpuHandle.archetypeInstanceIndex); + } + } + + internal class InternalGPUInstanceDataBuffer + { + public const int kComponentAddressAlignment = 32 * 4; + + internal readonly GPUInstanceDataBufferLayout layout; + + internal readonly NativeList components; + internal readonly NativeList componentsArchetypeIndexSpan; + internal readonly NativeArray componentsMetadata; + internal readonly NativeArray componentsGPUAddress; + internal readonly NativeArray componentPerInstance; + internal readonly NativeArray componentByteSizes; + internal readonly NativeArray componentInstanceIndexRanges; + internal readonly NativeArray componentIndices; + + internal readonly NativeArray instancesCountPrefixSum; + + internal readonly int gpuBufferByteSize; + internal readonly GraphicsBuffer gpuBuffer; + internal readonly GraphicsBuffer componentsPerInstanceGPUBuffer; + internal readonly GraphicsBuffer componentsGPUAddressGPUBuffer; + internal readonly GraphicsBuffer componentGPUInstanceIndexRangesGPUBuffer; + internal readonly GraphicsBuffer componentByteCountsGPUBuffer; + + public InternalGPUInstanceDataBuffer(ref GPUArchetypeManager archetypeManager, in GPUInstanceDataBufferLayout layout) + { + this.layout = new GPUInstanceDataBufferLayout(layout, Allocator.Persistent); + + components = new NativeList(Allocator.Persistent); + componentsArchetypeIndexSpan = new NativeList(Allocator.Persistent); + componentIndices = new NativeArray(archetypeManager.GetComponentsCount(), Allocator.Persistent, NativeArrayOptions.UninitializedMemory); + componentIndices.FillArray(InvalidIndex); + + instancesCountPrefixSum = new NativeArray(layout.archetypes.Length, Allocator.Persistent, NativeArrayOptions.ClearMemory); + + int instancesCountSum = 0; + + for (int i = 0; i < layout.archetypes.Length; ++i) + { + instancesCountPrefixSum[i] = instancesCountSum; + instancesCountSum += layout.instancesCount[i]; + + ref readonly GPUArchetypeDesc archetypeDesc = ref archetypeManager.GetArchetypeDesc(layout.archetypes[i]); + + for (int j = 0; j < archetypeDesc.components.Length; ++j) + { + GPUComponentHandle component = archetypeDesc.components[j]; + Assert.IsTrue(component.valid); + + int compIndex = componentIndices[component.index]; + + if (compIndex == InvalidIndex) + { + compIndex = components.Length; + components.Add(component); + componentsArchetypeIndexSpan.Add(new int2(i, i + 1)); + componentIndices[component.index] = compIndex; + } + else + { + int2 archetypeSpan = componentsArchetypeIndexSpan[compIndex]; + archetypeSpan.x = math.min(archetypeSpan.x, i); + archetypeSpan.y = math.max(archetypeSpan.y, i + 1); + componentsArchetypeIndexSpan[compIndex] = archetypeSpan; + } + } + } + + Assert.AreEqual(components.Length, componentsArchetypeIndexSpan.Length); + + componentsMetadata = new NativeArray(components.Length, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); + componentsGPUAddress = new NativeArray(components.Length, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); + componentPerInstance = new NativeArray(components.Length, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); + componentByteSizes = new NativeArray(components.Length, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); + componentInstanceIndexRanges = new NativeArray(components.Length, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); + + // Initial offset, must be {0, 0, 0, 0} for BatchRendererGroup header. + int byteOffset = 4 * UnsafeUtility.SizeOf(); + + for (int compIndex = 0; compIndex < components.Length; ++compIndex) + { + ref readonly GPUComponentDesc componentDesc = ref archetypeManager.GetComponentDesc(components[compIndex]); + int2 archetypeSpan = componentsArchetypeIndexSpan[compIndex]; + Assert.IsTrue(archetypeSpan.y > archetypeSpan.x); + + int firstArchetypeIndex = archetypeSpan.x; + int lastArchetypeIndex = archetypeSpan.y - 1; + + int instancesBegin = instancesCountPrefixSum[firstArchetypeIndex]; + int instancesEnd = instancesCountPrefixSum[lastArchetypeIndex] + layout.instancesCount[lastArchetypeIndex]; + int instancesNum = componentDesc.isPerInstance ? instancesEnd - instancesBegin : 1; + Assert.IsTrue(instancesNum >= 0); + + byteOffset = CollectionHelper.Align(byteOffset, kComponentAddressAlignment); + + int componentGPUAddress; + // Offset for the address of the component when it does not start at the zero instance index. + int componentInstanceOffset = instancesBegin * componentDesc.byteSize; + //@ In the future, for OpenGL ES 3.1 support, make sure that an instance data element doesn't cross 16KB boundary. + if (componentDesc.isPerInstance) + componentGPUAddress = byteOffset - componentInstanceOffset; + else + componentGPUAddress = byteOffset; + // GPU component address should not become negative. This generally should not happen often. See 'kIsOverriddenBit'. + if (componentGPUAddress < 0) + { + byteOffset = CollectionHelper.Align(byteOffset + Math.Abs(componentGPUAddress), kComponentAddressAlignment); + componentGPUAddress = byteOffset - componentInstanceOffset; + } + + componentsGPUAddress[compIndex] = componentGPUAddress; + componentsMetadata[compIndex] = CreateMetadataValue(componentDesc.propertyID, componentGPUAddress, componentDesc.isPerInstance); + + componentPerInstance[compIndex] = componentDesc.isPerInstance ? 1 : 0; + componentInstanceIndexRanges[compIndex] = new int2(instancesBegin, instancesEnd); + componentByteSizes[compIndex] = componentDesc.byteSize; + + int componentTotalByteSize = componentDesc.byteSize * instancesNum; + byteOffset += componentTotalByteSize; + } + + gpuBufferByteSize = byteOffset; + + Assert.IsTrue((gpuBufferByteSize % sizeof(uint)) == 0 && gpuBufferByteSize <= MaxGPUInstancDataBufferSize); + + gpuBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Raw, gpuBufferByteSize / sizeof(uint), sizeof(uint)); + gpuBuffer.SetData(new NativeArray(4, Allocator.Temp), 0, 0, 4); + componentsPerInstanceGPUBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Raw, components.Length, sizeof(int)); + componentsPerInstanceGPUBuffer.SetData(componentPerInstance, 0, 0, components.Length); + componentsGPUAddressGPUBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Raw, components.Length, sizeof(int)); + componentsGPUAddressGPUBuffer.SetData(componentsGPUAddress, 0, 0, components.Length); + componentGPUInstanceIndexRangesGPUBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Raw, components.Length, UnsafeUtility.SizeOf()); + componentGPUInstanceIndexRangesGPUBuffer.SetData(componentInstanceIndexRanges, 0, 0, components.Length); + componentByteCountsGPUBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Raw, components.Length, sizeof(int)); + componentByteCountsGPUBuffer.SetData(componentByteSizes, 0, 0, components.Length); + } + + public void Dispose() + { + layout.Dispose(); + components.Dispose(); + componentsMetadata.Dispose(); + componentsGPUAddress.Dispose(); + componentPerInstance.Dispose(); + componentByteSizes.Dispose(); + componentInstanceIndexRanges.Dispose(); + componentsArchetypeIndexSpan.Dispose(); + componentIndices.Dispose(); + instancesCountPrefixSum.Dispose(); + gpuBuffer.Dispose(); + componentsPerInstanceGPUBuffer.Dispose(); + componentsGPUAddressGPUBuffer.Dispose(); + componentGPUInstanceIndexRangesGPUBuffer.Dispose(); + componentByteCountsGPUBuffer.Dispose(); + } + + public int FindComponentIndex(GPUComponentHandle component) + { + Assert.IsTrue(component.valid); + if (component.index < componentIndices.Length) + return componentIndices[component.index]; + return InvalidIndex; + } + + public int FindArchetypeIndex(GPUArchetypeHandle archetype) + { + return layout.FindArchetypeIndex(archetype); + } + } + + internal readonly struct ComponentIndex + { + public readonly int index; + public readonly int layoutVersion; + + public ComponentIndex(int index, int layoutVersion) + { + this.index = index; + this.layoutVersion = layoutVersion; + } + } + + internal readonly struct ArchetypeIndex + { + public readonly int index; + public readonly int layoutVersion; + + public ArchetypeIndex(int index, int layoutVersion) + { + this.index = index; + this.layoutVersion = layoutVersion; + } + } + + internal static class UploadKernelID + { + public static readonly int kInputComponentsCount = Shader.PropertyToID("_InputComponentsCount"); + public static readonly int kInputInstancesCount = Shader.PropertyToID("_InputInstancesCount"); + public static readonly int kInputInstanceData = Shader.PropertyToID("_InputInstanceData"); + public static readonly int kInputInstanceIndices = Shader.PropertyToID("_InputInstanceIndices"); + public static readonly int kInputComponentAddresses = Shader.PropertyToID("_InputComponentAddresses"); + public static readonly int kOutputComponentByteCounts = Shader.PropertyToID("_OutputComponentByteCounts"); + public static readonly int kOutputComponentIndices = Shader.PropertyToID("_OutputComponentIndices"); + public static readonly int kOutputComponentInstanceIndexRanges = Shader.PropertyToID("_OutputComponentInstanceIndexRanges"); + public static readonly int kOutputComponentIsPerInstance = Shader.PropertyToID("_OutputComponentIsPerInstance"); + public static readonly int kOutputComponentAddresses = Shader.PropertyToID("_OutputComponentAddresses"); + public static readonly int kOutputBuffer = Shader.PropertyToID("_OutputBuffer"); + } + + internal static class CopyKernelID + { + public static readonly int kDispatchThreadGroupBase = Shader.PropertyToID("_DispatchThreadGroupBase"); + public static readonly int kInputComponentsCount = Shader.PropertyToID("_InputComponentsCount"); + public static readonly int kInputThreadGroupBeginIndices = Shader.PropertyToID("_InputThreadGroupBeginIndices"); + public static readonly int kInputComponentDataAddresses = Shader.PropertyToID("_InputComponentDataAddresses"); + public static readonly int kOutputComponentDataAddresses = Shader.PropertyToID("_OutputComponentDataAddresses"); + public static readonly int kOutputComponentDataUIntSizes = Shader.PropertyToID("_OutputComponentDataUIntSizes"); + public static readonly int kInputBuffer = Shader.PropertyToID("_InputBuffer"); + public static readonly int kOutputBuffer = Shader.PropertyToID("_OutputBuffer"); + } + } + + internal struct GPUInstanceUploadData : IDisposable + { + private NativeArray m_Components; + private NativeArray m_ComponentGPUAddress; + private NativeArray m_ComponentSize; + private NativeArray m_ComponentPerInstance; + private NativeArray m_ComponentIndices; + private NativeList m_WrittenComponents; + private int m_Length; + private int m_UploadDataUIntSize; + + public int length => m_Length; + public int uploadDataUIntSize => m_UploadDataUIntSize; + public NativeArray.ReadOnly componentGPUAddress => m_ComponentGPUAddress.AsReadOnly(); + public NativeArray.ReadOnly writtenComponents => m_WrittenComponents.AsReadOnly(); + + public GPUInstanceUploadData(ref GPUArchetypeManager archetypeManager, NativeArray components, int length, Allocator allocator) + { + Assert.IsTrue(length > 0); + Assert.IsTrue(components.Length != 0); + + m_Length = length; + m_Components = new NativeArray(components.Length, allocator); + m_Components.CopyFrom(components); + m_ComponentGPUAddress = new NativeArray(components.Length, allocator, NativeArrayOptions.UninitializedMemory); + m_ComponentSize = new NativeArray(components.Length, allocator, NativeArrayOptions.UninitializedMemory); + m_ComponentPerInstance = new NativeArray(components.Length, allocator, NativeArrayOptions.UninitializedMemory); + m_ComponentIndices = new NativeArray(archetypeManager.GetComponentsCount(), allocator); + m_ComponentIndices.FillArray(GPUInstanceDataBuffer.InvalidIndex); + m_WrittenComponents = new NativeList(components.Length, allocator); + + int byteOffset = 0; + + for (int i = 0; i < components.Length; ++i) + { + GPUComponentHandle component = components[i]; + Assert.IsTrue(component.valid); + + if (m_ComponentIndices[component.index] == GPUInstanceDataBuffer.InvalidIndex) + m_ComponentIndices[component.index] = i; + else + Assert.IsTrue(false, "Component is added more than once."); + + ref readonly GPUComponentDesc componentDesc = ref archetypeManager.GetComponentDesc(component); + Assert.IsTrue(componentDesc.byteSize % sizeof(uint) == 0); + + m_ComponentGPUAddress[i] = byteOffset; + m_ComponentSize[i] = componentDesc.byteSize; + m_ComponentPerInstance[i] = componentDesc.isPerInstance; + byteOffset += componentDesc.byteSize * (componentDesc.isPerInstance ? length : 1); + } + + Assert.IsTrue(byteOffset % sizeof(uint) == 0); + m_UploadDataUIntSize = byteOffset / sizeof(uint); + } + + public void Dispose() + { + m_Components.Dispose(); + m_ComponentGPUAddress.Dispose(); + m_ComponentSize.Dispose(); + m_ComponentPerInstance.Dispose(); + m_ComponentIndices.Dispose(); + m_WrittenComponents.Dispose(); + } + + public int FindComponentIndex(GPUComponentHandle component) + { + Assert.IsTrue(component.valid); + Assert.IsTrue(component.index < m_ComponentIndices.Length, "Component index is invalid."); + int componentIndex = m_ComponentIndices[component.index]; + Assert.IsTrue(componentIndex != GPUInstanceDataBuffer.InvalidIndex, "Component is not allocated."); + return componentIndex; + } + + public int PrepareComponentWrite(GPUComponentHandle component) where T : unmanaged + { + int componentIndex = FindComponentIndex(component); + Assert.IsTrue(UnsafeUtility.SizeOf() == m_ComponentSize[componentIndex], + "Component to write is incompatible, must be same stride as destination."); + + if (!m_WrittenComponents.Contains(component)) + m_WrittenComponents.Add(component); + + return m_ComponentGPUAddress[componentIndex] / UnsafeUtility.SizeOf(); + } + + public JobHandle ScheduleWriteComponentsJob(NativeArray instanceData, GPUComponentHandle component, NativeArray uploadBuffer) where T : unmanaged + { + var jaggedInstanceData = instanceData.Reinterpret(UnsafeUtility.SizeOf()).ToJaggedSpan(Allocator.TempJob); + JobHandle jobHandle = ScheduleWriteComponentsJob(jaggedInstanceData, component, UnsafeUtility.SizeOf(), uploadBuffer); + jaggedInstanceData.Dispose(jobHandle); + return jobHandle; + } + + public unsafe JobHandle ScheduleWriteComponentsJob(JaggedSpan instanceData, GPUComponentHandle component, int componentSize, NativeArray uploadBuffer) + { + if (instanceData.sectionCount == 0) + return default; + + Assert.IsTrue(uploadBuffer.Length >= m_UploadDataUIntSize); + Assert.IsTrue(m_Length > 0, "Space is not reserved. Did you forget to call ReserveUploadSpace()?"); + + int componentCount = instanceData.totalLength / componentSize; + int componentIndex = FindComponentIndex(component); + int componentOffset = m_ComponentGPUAddress[componentIndex]; + bool isPerInstance = m_ComponentPerInstance[componentIndex]; + + if (isPerInstance) + { + Assert.IsTrue(componentCount == m_Length, "Wrong instance data length."); + } + else + { + Assert.IsTrue(m_Length >= 1 && componentCount == 1); + } + + Assert.IsTrue(componentSize == m_ComponentSize[componentIndex], + "Component to write is incompatible, must be same stride as destination."); + + if (!m_WrittenComponents.Contains(component)) + m_WrittenComponents.Add(component); + else + Assert.IsTrue(false, "Component is already written."); + + if (isPerInstance) + { + // Each job writes 16 cache lines (1KiB) + NativeList jobRanges = JaggedJobRange.FromSpanWithRelaxedBatchSize(instanceData, 16 * 64, Allocator.TempJob); + + var jobHandle = new WriteGPUComponentDataJob + { + JobRanges = jobRanges.AsArray(), + ComponentOffsetInBytes = m_ComponentGPUAddress[componentIndex], + JaggedInstanceData = instanceData, + UploadBuffer = uploadBuffer.Reinterpret(sizeof(uint)) + } + .Schedule(jobRanges); + jobRanges.Dispose(jobHandle); + + return jobHandle; + } + else + { + Assert.IsTrue(instanceData.sectionCount == 1); + Assert.IsTrue(instanceData.sections[0].Length == componentSize); + + UnsafeList section = instanceData.sections[0]; + UnsafeUtility.MemCpy((byte*)uploadBuffer.GetUnsafePtr() + componentOffset, section.Ptr, section.Length); + + return default; + } + } + } + + internal struct GPUInstanceDataBufferLayout : IDisposable, IEnumerable, IEquatable + { + private NativeList m_Archetypes; + private NativeList m_InstancesCount; + private NativeList m_ArchetypeIndex; + + public NativeArray.ReadOnly archetypes => m_Archetypes.AsReadOnly(); + public NativeArray.ReadOnly instancesCount => m_InstancesCount.AsReadOnly(); + + public GPUInstanceDataBufferLayout(int capacity, Allocator allocator) + { + m_Archetypes = new NativeList(capacity, allocator); + m_InstancesCount = new NativeList(capacity, allocator); + m_ArchetypeIndex = new NativeList(capacity, allocator); + } + + public GPUInstanceDataBufferLayout(in GPUInstanceDataBufferLayout otherLayout, Allocator allocator) + { + m_Archetypes = new NativeList(otherLayout.m_Archetypes.Length, allocator); + m_InstancesCount = new NativeList(otherLayout.m_InstancesCount.Length, allocator); + m_ArchetypeIndex = new NativeList(otherLayout.m_ArchetypeIndex.Length, allocator); + m_Archetypes.CopyFrom(otherLayout.m_Archetypes); + m_InstancesCount.CopyFrom(otherLayout.m_InstancesCount); + m_ArchetypeIndex.CopyFrom(otherLayout.m_ArchetypeIndex); + } + + public void Add(GPUArchetypeHandle archetype, int instanceCount) + { + Assert.IsTrue(archetype.valid, "The archetype is invalid."); + Assert.IsTrue(instanceCount >= 0, "The instance count is negative."); + + if (!m_Archetypes.IsCreated) + m_Archetypes = new NativeList(1, Allocator.Temp); + if (!m_InstancesCount.IsCreated) + m_InstancesCount = new NativeList(1, Allocator.Temp); + if (!m_ArchetypeIndex.IsCreated) + m_ArchetypeIndex = new NativeList(1, Allocator.Temp); + + if (archetype.index >= m_ArchetypeIndex.Length) + m_ArchetypeIndex.AddReplicate(GPUArchetypeHandle.Invalid.index, archetype.index - m_Archetypes.Length + 1); + + Assert.IsTrue(m_ArchetypeIndex[archetype.index] == GPUArchetypeHandle.Invalid.index, "The layout already contains the archetype."); + + m_ArchetypeIndex[archetype.index] = m_Archetypes.Length; + m_Archetypes.Add(archetype); + m_InstancesCount.Add(instanceCount); + } + + public IEnumerator GetEnumerator() + { + for (int i = 0; i < m_Archetypes.Length; i++) + yield return (m_Archetypes[i], m_InstancesCount[i]); + } + + public void Dispose() + { + m_Archetypes.Dispose(); + m_InstancesCount.Dispose(); + m_ArchetypeIndex.Dispose(); + } + + public bool Equals(GPUInstanceDataBufferLayout other) + { + return m_Archetypes.ArraysEqual(other.m_Archetypes) && m_InstancesCount.ArraysEqual(other.m_InstancesCount); + } + + public int FindArchetypeIndex(GPUArchetypeHandle archetype) + { + Assert.IsTrue(archetype.valid, "The archetype is invalid."); + if(archetype.index < m_ArchetypeIndex.Length) + return m_ArchetypeIndex[archetype.index]; + else + return GPUInstanceDataBuffer.InvalidIndex; + } + + public ReadOnly AsReadOnly() + { + return new ReadOnly(this); + } + + public struct ReadOnly + { + public readonly NativeArray.ReadOnly archetypes; + public readonly NativeArray.ReadOnly instancesCount; + public readonly NativeArray.ReadOnly archetypeIndex; + + public ReadOnly(GPUInstanceDataBufferLayout layout) + { + archetypes = layout.m_Archetypes.AsReadOnly(); + instancesCount = layout.m_InstancesCount.AsReadOnly(); + archetypeIndex = layout.m_ArchetypeIndex.AsReadOnly(); + } + + public int FindArchetypeIndex(GPUArchetypeHandle archetype) + { + Assert.IsTrue(archetype.valid, "The archetype is invalid."); + if (archetype.index < archetypeIndex.Length) + return archetypeIndex[archetype.index]; + else + return -1; + } + } + } + + internal struct GPUInstanceDataBufferReadback : IDisposable where TData : unmanaged + { + private GPUInstanceDataBuffer m_InstanceDataBuffer; + + public NativeArray data { get; private set; } + + public bool Load(CommandBuffer cmd, GPUInstanceDataBuffer instanceDataBuffer) + { + Assert.IsNotNull(instanceDataBuffer); + + m_InstanceDataBuffer = instanceDataBuffer; + + var dataSize = UnsafeUtility.SizeOf(); + var localData = new NativeArray((instanceDataBuffer.gpuBufferByteSize + (dataSize - 1)) / dataSize, Allocator.Persistent); + var errorCount = 0; + + cmd.RequestAsyncReadback(instanceDataBuffer.nativeBuffer, (AsyncGPUReadbackRequest req) => + { + if (req.done) + localData.CopyFrom(req.GetData()); + else ++errorCount; + }); + + cmd.WaitAllAsyncReadbackRequests(); + Graphics.ExecuteCommandBuffer(cmd); + cmd.Clear(); + data = localData; + + if (errorCount != 0) + Debug.LogError("GPUInstanceDataBufferReadback: request async readback failed."); + + return errorCount == 0; + } + + public unsafe T LoadData(GPUComponentHandle component, GPUInstanceIndex gpuInstanceIndex) where T : unmanaged + { + var compIndex = m_InstanceDataBuffer.GetComponentIndex(component); + var componentIndexRange = m_InstanceDataBuffer.componentInstanceIndexRanges[compIndex.index]; + Assert.IsTrue(gpuInstanceIndex.index >= componentIndexRange.x && gpuInstanceIndex.index < componentIndexRange.y, "GPUInstanceIndex is out of component range."); + var componentByteSize = m_InstanceDataBuffer.componentByteSizes[compIndex.index]; + Assert.IsTrue(componentByteSize == UnsafeUtility.SizeOf(), "Component byte size doesn't match."); + int isPerInstance = m_InstanceDataBuffer.componentPerInstance[compIndex.index]; + int gpuBaseAddress = m_InstanceDataBuffer.componentsGPUAddress[compIndex.index]; + int index = (gpuBaseAddress + componentByteSize * gpuInstanceIndex.index * isPerInstance) / UnsafeUtility.SizeOf(); + uint* dataPtr = (uint*)data.GetUnsafePtr() + index; + T result = *(T*)(dataPtr); + return result; + } + + public void Dispose() + { + data.Dispose(); + } + } + + [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] + internal struct WriteGPUComponentDataJob : IJobParallelFor + { + [ReadOnly] public NativeArray JobRanges; + [ReadOnly] public int ComponentOffsetInBytes; + [NativeDisableContainerSafetyRestriction, NoAlias][ReadOnly] public JaggedSpan JaggedInstanceData; + + [NativeDisableContainerSafetyRestriction, NoAlias][WriteOnly] public NativeArray UploadBuffer; + + public unsafe void Execute(int jobIndex) + { + JaggedJobRange jobRange = JobRanges[jobIndex]; + + NativeArray section = JaggedInstanceData[jobRange.sectionIndex]; + + byte* srcBasePtr = (byte*)section.GetUnsafeReadOnlyPtr(); + byte* dstBasePtr = (byte*)UploadBuffer.GetUnsafePtr(); + + byte* srcPtr = srcBasePtr + jobRange.localStart; + byte* dstPtr = dstBasePtr + ComponentOffsetInBytes + jobRange.absoluteStart; + + Assert.IsTrue(srcPtr + jobRange.length <= srcBasePtr + section.Length); + Assert.IsTrue(dstPtr + jobRange.length <= dstPtr + UploadBuffer.Length); + + UnsafeUtility.MemCpy(dstPtr, srcPtr, jobRange.length); + } + } + + [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] + internal struct InstancesToGPUIndicesJob : IJobParallelFor + { + [ReadOnly] public RenderWorld renderWorld; + [ReadOnly] public NativeArray instancesCountPrefixSum; + [ReadOnly] public GPUInstanceDataBufferLayout layout; + [ReadOnly] public NativeArray instances; + + [WriteOnly] public NativeArray gpuIndices; + + public void Execute(int index) + { + InstanceHandle instance = instances[index]; + Assert.IsTrue(instance.isValid, "Invalid Instance"); + if (!instance.isValid) + { + gpuIndices[index] = GPUInstanceIndex.Invalid; + return; + } + + int instanceIndex = renderWorld.HandleToIndex(instance); + InstanceGPUHandle gpuHandle = renderWorld.gpuHandles[instanceIndex]; + Assert.IsTrue(gpuHandle.archetype.valid && gpuHandle.archetypeInstanceIndex >= 0); + int archetypeIndex = layout.FindArchetypeIndex(gpuHandle.archetype); + Assert.IsTrue(archetypeIndex >= 0); + int instancesBegin = instancesCountPrefixSum[archetypeIndex]; + int instancesCount = layout.instancesCount[archetypeIndex]; + Assert.IsTrue(gpuHandle.archetypeInstanceIndex < instancesCount); + gpuIndices[index] = GPUInstanceIndex.Create(instancesBegin + gpuHandle.archetypeInstanceIndex); + } + } + + // This is the actual instance offset from the component base address on the GPU. + // Note: The component base address might differ from the address of the first allocated component element. + // It may have a negative offset to allow proper referencing by GPUInstanceIndex. + internal struct GPUInstanceIndex : IEquatable, IComparable + { + public int index { get; private set; } + public bool valid => index >= 0; + public static GPUInstanceIndex Create(int index) { return new GPUInstanceIndex { index = index }; } + public static readonly GPUInstanceIndex Invalid = new GPUInstanceIndex { index = -1 }; + public bool Equals(GPUInstanceIndex other) => index == other.index; + public int CompareTo(GPUInstanceIndex other) { return index.CompareTo(other.index); } + public override int GetHashCode() { return index; } + } +} diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/GPUInstanceDataBuffer.cs.hlsl b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/GPUInstanceDataBuffer.cs.hlsl new file mode 100644 index 00000000000..781f2ebd16b --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/GPUInstanceDataBuffer.cs.hlsl @@ -0,0 +1,16 @@ +// +// This file was automatically generated. Please don't edit by hand. Execute Editor command [ Edit > Rendering > Generate Shader Includes ] instead +// + +#ifndef GPUINSTANCEDATABUFFER_CS_HLSL +#define GPUINSTANCEDATABUFFER_CS_HLSL +// +// UnityEngine.Rendering.GPUInstanceDataBufferConstants: static fields +// +#define GPUINSTANCEDATABUFFERCONSTANTS_THREAD_GROUP_SIZE (128) +#define GPUINSTANCEDATABUFFERCONSTANTS_UINT_PER_THREAD (16) +#define GPUINSTANCEDATABUFFERCONSTANTS_UINT_PER_THREAD_GROUP (2048) +#define GPUINSTANCEDATABUFFERCONSTANTS_MAX_THREAD_GROUPS_PER_DISPATCH (65535) + + +#endif diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/GPUInstanceDataBuffer.cs.hlsl.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/GPUInstanceDataBuffer.cs.hlsl.meta new file mode 100644 index 00000000000..d68f71f417c --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/GPUInstanceDataBuffer.cs.hlsl.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: c624d8646a289a747b3dfb97cf7aebdd +ShaderIncludeImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/GPUInstanceDataBuffer.cs.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/GPUInstanceDataBuffer.cs.meta new file mode 100644 index 00000000000..1c9d06c9f5e --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/GPUInstanceDataBuffer.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 7e2ab3702e1f0d2408e45282d1cb4e9f \ No newline at end of file diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/InstanceAllocators.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/InstanceAllocators.cs new file mode 100644 index 00000000000..814dabab854 --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/InstanceAllocators.cs @@ -0,0 +1,108 @@ +using System; +using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; +using Unity.Jobs; +using UnityEngine.Assertions; + +namespace UnityEngine.Rendering +{ + //@ Add instance version or generation to detect dangling instance handles. + + // This is used to uniquely reference an instance on the CPU side. + internal struct InstanceHandle : IEquatable, IComparable + { + public int index { get; private set; } + public bool isValid => index >= 0; + public static InstanceHandle Create(int index) { return new InstanceHandle { index = index }; } + public static readonly InstanceHandle Invalid = new InstanceHandle { index = -1 }; + public bool Equals(InstanceHandle other) => index == other.index; + public int CompareTo(InstanceHandle other) { return index.CompareTo(other.index); } + public override int GetHashCode() { return index; } + } + + // This is used to uniquely define an instance within its archetype on the GPU side. (Instance data is sorted by archetype on the GPU). + internal struct InstanceGPUHandle : IEquatable, IComparable + { + private int m_Data; + public bool isValid => m_Data >= 0; + public GPUArchetypeHandle archetype => GPUArchetypeHandle.Create((short)(m_Data & GPUArchetypeManager.kGPUArchetypeBitsMask)); + public int archetypeInstanceIndex => m_Data >> GPUArchetypeManager.kGPUArchetypeBits; + public static InstanceGPUHandle Create(GPUArchetypeHandle gpuArchetype, int gpuPerArchetypeIndex) + { + Assert.IsTrue(gpuArchetype.index < GPUArchetypeManager.kMaxGPUArchetypesCount); + + return new InstanceGPUHandle + { + m_Data = ((gpuPerArchetypeIndex << GPUArchetypeManager.kGPUArchetypeBits) | (int)gpuArchetype.index) + }; + } + public static readonly InstanceGPUHandle Invalid = new InstanceGPUHandle { m_Data = -1 }; + public bool Equals(InstanceGPUHandle other) => m_Data == other.m_Data; + public int CompareTo(InstanceGPUHandle other) { return m_Data.CompareTo(other.m_Data); } + public override int GetHashCode() { return m_Data; } + } + + internal unsafe struct InstanceAllocators + { + private NativeHandleAllocator m_InstanceCPUHandleAllocator; + private NativeArray m_InstanceGPUHandleAllocators; + + public void Initialize() + { + m_InstanceCPUHandleAllocator = new NativeHandleAllocator(); + m_InstanceCPUHandleAllocator.Initialize(); + + m_InstanceGPUHandleAllocators = new NativeArray(GPUArchetypeManager.kMaxGPUArchetypesCount, Allocator.Persistent); + for(int i = 0; i < m_InstanceGPUHandleAllocators.Length; ++i) + UnsafeUtility.ArrayElementAsRef(m_InstanceGPUHandleAllocators.GetUnsafePtr(), i).Initialize(); + } + + public unsafe void Dispose() + { + m_InstanceCPUHandleAllocator.Dispose(); + for (int i = 0; i < m_InstanceGPUHandleAllocators.Length; ++i) + UnsafeUtility.ArrayElementAsRef(m_InstanceGPUHandleAllocators.GetUnsafePtr(), i).Dispose(); + m_InstanceGPUHandleAllocators.Dispose(); + } + + private ref NativeHandleAllocator GetInstanceGPUHandleAllocator(GPUArchetypeHandle archetype) + { + Assert.IsTrue(archetype.valid); + return ref UnsafeUtility.ArrayElementAsRef(m_InstanceGPUHandleAllocators.GetUnsafePtr(), archetype.index); + } + + public int TrimGPUAllocatorLength(GPUArchetypeHandle archetype) + { + ref var allocator = ref GetInstanceGPUHandleAllocator(archetype); + allocator.TrimLength(); + return allocator.length; + } + + public int GetInstanceGPUHandlesAllocatedCount(GPUArchetypeHandle archetype) + { + return GetInstanceGPUHandleAllocator(archetype).allocatedCount; + } + + public InstanceHandle AllocateInstance() + { + return InstanceHandle.Create(m_InstanceCPUHandleAllocator.Allocate()); + } + + public InstanceGPUHandle AllocateInstanceGPUHandle(GPUArchetypeHandle archetype) + { + return InstanceGPUHandle.Create(archetype, GetInstanceGPUHandleAllocator(archetype).Allocate()); + } + + public void FreeInstance(InstanceHandle instance) + { + Assert.IsTrue(instance.isValid); + m_InstanceCPUHandleAllocator.Free(instance.index); + } + + public void FreeInstanceGPUHandle(InstanceGPUHandle gpuHandle) + { + Assert.IsTrue(gpuHandle.isValid); + GetInstanceGPUHandleAllocator(gpuHandle.archetype).Free(gpuHandle.archetypeInstanceIndex); + } + } +} diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceAllocator.cs.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/InstanceAllocators.cs.meta similarity index 100% rename from Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceAllocator.cs.meta rename to Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/InstanceAllocators.cs.meta diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/InstanceDataSystem.Jobs.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/InstanceDataSystem.Jobs.cs new file mode 100644 index 00000000000..92d2925e411 --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/InstanceDataSystem.Jobs.cs @@ -0,0 +1,654 @@ +using System; +using System.Runtime.CompilerServices; +using System.Threading; +using Unity.Burst; +using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; +using Unity.Jobs; +using Unity.Mathematics; +using UnityEngine.Assertions; + +namespace UnityEngine.Rendering +{ + internal partial class InstanceDataSystem : IDisposable + { + static float3 AABBRotateExtents(float3 extents, float3 m0, float3 m1, float3 m2) + { + return math.abs(m0 * extents.x) + math.abs(m1 * extents.y) + math.abs(m2 * extents.z); + } + + public static AABB AABBTransform(float4x4 transform, AABB localBounds) + { + AABB transformed; + transformed.extents = AABBRotateExtents(localBounds.extents, transform.c0.xyz, transform.c1.xyz, transform.c2.xyz); + transformed.center = math.transform(transform, localBounds.center); + return transformed; + } + + private unsafe static int AtomicAddLengthNoResize(in NativeList list, int count) where T : unmanaged + { + UnsafeList* unsafeList = list.GetUnsafeList(); + var newLength = Interlocked.Add(ref unsafeList->m_length, count); + Assert.IsTrue(unsafeList->Capacity >= newLength); + return newLength - count; + } + + [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] + private unsafe struct QueryRendererInstancesJob : IJobParallelFor + { + [ReadOnly] public NativeArray jobRanges; + [ReadOnly] public NativeParallelHashMap rendererToInstanceMap; + [NativeDisableContainerSafetyRestriction, NoAlias][ReadOnly] public JaggedSpan jaggedRenderers; + + [NativeDisableContainerSafetyRestriction, NoAlias][WriteOnly] public NativeArray instances; + [NativeDisableUnsafePtrRestriction] public UnsafeAtomicCounter32 atomicNonFoundInstancesCount; + + public void Execute(int jobIndex) + { + JaggedJobRange jobRange = jobRanges[jobIndex]; + NativeArray renderers = jaggedRenderers[jobRange.sectionIndex]; + int newInstancesCountJob = 0; + + for (int i = 0; i < jobRange.length; ++i) + { + int localIndex = jobRange.localStart + i; + int absoluteIndex = jobRange.absoluteStart + i; + + if (rendererToInstanceMap.TryGetValue(renderers[localIndex], out var instance)) + { + instances[absoluteIndex] = instance; + } + else + { + newInstancesCountJob += 1; + instances[absoluteIndex] = InstanceHandle.Invalid; + } + } + + if (atomicNonFoundInstancesCount.Counter != null && newInstancesCountJob > 0) + atomicNonFoundInstancesCount.Add(newInstancesCountJob); + } + } + + [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] + private struct QuerySortedMeshInstancesJob : IJobParallelForBatch + { + public const int MaxBatchSize = 64; + + [ReadOnly] public RenderWorld renderWorld; + [ReadOnly] public NativeArray sortedMeshes; + + [NativeDisableParallelForRestriction][WriteOnly] public NativeList instances; + + public void Execute(int startIndex, int count) + { + Assert.IsTrue(MaxBatchSize <= 64 && count <= 64); + ulong validBits = 0; + + // Do a first pass to count the number of valid instances so we can do only one atomic add for the whole range. + for (int i = 0; i < count; ++i) + { + int instanceIndex = startIndex + i; + InstanceHandle instance = renderWorld.IndexToHanle(instanceIndex); + EntityId mesh = renderWorld.meshIDs[instanceIndex]; + + if (sortedMeshes.BinarySearch(mesh) >= 0) + validBits |= 1ul << i; + } + + int validBitCount = math.countbits(validBits); + + if (validBitCount > 0) + { + int writeIndex = AtomicAddLengthNoResize(instances, validBitCount); + int validBitIndex = math.tzcnt(validBits); + + while (validBits != 0) + { + int instanceIndex = startIndex + validBitIndex; + instances[writeIndex] = renderWorld.indexToHandle[instanceIndex]; + + writeIndex += 1; + validBits &= ~(1ul << validBitIndex); + validBitIndex = math.tzcnt(validBits); + } + } + } + } + + [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] + private struct CalculateInterpolatedLightAndOcclusionProbesBatchJob : IJobParallelFor + { + public const int k_CalculatedProbesPerBatch = 8; + + [ReadOnly] public int probesCount; + [ReadOnly] public LightProbesQuery lightProbesQuery; + + [NativeDisableParallelForRestriction][ReadOnly] public NativeArray queryPostitions; + [NativeDisableParallelForRestriction] public NativeArray compactTetrahedronCache; + [NativeDisableParallelForRestriction][WriteOnly] public NativeArray probesSphericalHarmonics; + [NativeDisableParallelForRestriction][WriteOnly] public NativeArray probesOcclusion; + + public void Execute(int index) + { + var startIndex = index * k_CalculatedProbesPerBatch; + var endIndex = math.min(probesCount, startIndex + k_CalculatedProbesPerBatch); + var count = endIndex - startIndex; + + var compactTetrahedronCacheSubArray = compactTetrahedronCache.GetSubArray(startIndex, count); + var queryPostitionsSubArray = queryPostitions.GetSubArray(startIndex, count); + var probesSphericalHarmonicsSubArray = probesSphericalHarmonics.GetSubArray(startIndex, count); + var probesOcclusionSubArray = probesOcclusion.GetSubArray(startIndex, count); + lightProbesQuery.CalculateInterpolatedLightAndOcclusionProbes(queryPostitionsSubArray, compactTetrahedronCacheSubArray, probesSphericalHarmonicsSubArray, probesOcclusionSubArray); + } + } + + [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] + private struct ScatterTetrahedronCacheIndicesJob : IJobParallelFor + { + [ReadOnly] public NativeArray probeInstances; + [ReadOnly] public NativeArray compactTetrahedronCache; + + [NativeDisableContainerSafetyRestriction, NoAlias][NativeDisableParallelForRestriction] public RenderWorld renderWorld; + + public void Execute(int index) + { + InstanceHandle instance = probeInstances[index]; + int instanceIndex = renderWorld.HandleToIndex(instance); + renderWorld.tetrahedronCacheIndices.ElementAtRW(instanceIndex) = compactTetrahedronCache[index]; + } + } + + [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] + private unsafe struct TransformUpdateJob : IJobParallelFor + { + public const int MaxBatchSize = 64; + + [ReadOnly] public NativeArray jobRanges; + [ReadOnly] public NativeArray instances; + [ReadOnly] public JaggedSpan jaggedLocalToWorldMatrices; + [ReadOnly] public JaggedSpan jaggedPrevLocalToWorldMatrices; + [ReadOnly] public bool initialize; + [ReadOnly] public bool enableBoundingSpheres; + + [NativeDisableUnsafePtrRestriction] public UnsafeAtomicCounter32 atomicTransformQueueCount; + [NativeDisableParallelForRestriction] public RenderWorld renderWorld; + [NativeDisableParallelForRestriction] public NativeArray transformUpdateInstanceQueue; + [NativeDisableParallelForRestriction] public NativeArray transformUpdateDataQueue; + [NativeDisableParallelForRestriction] public NativeArray boundingSpheresDataQueue; + + public void Execute(int jobIndex) + { + var jobRange = jobRanges[jobIndex]; + var localToWorldMatrices = jaggedLocalToWorldMatrices[jobRange.sectionIndex]; + var prevLocalToWorldMatrices = jaggedPrevLocalToWorldMatrices[jobRange.sectionIndex]; + + // The bitfield can only contain up to 64 instances + Assert.IsTrue(MaxBatchSize <= 64 && jobRange.length <= 64); + ulong validBits = 0; + + for (int i = 0; i < jobRange.length; ++i) + { + InstanceHandle instance = instances[jobRange.absoluteStart + i]; + if (!instance.isValid) + continue; + + if (!initialize) + { + int instanceIndex = renderWorld.HandleToIndex(instance); + bool movedCurrentFrame = renderWorld.movedInCurrentFrameBits.Get(instanceIndex); + bool isStaticObject = renderWorld.rendererSettings[instanceIndex].IsPartOfStaticBatch; + + if (isStaticObject || movedCurrentFrame) + continue; + } + + validBits |= 1ul << i; + } + + int validBitCount = math.countbits(validBits); + + if (validBitCount > 0) + { + int writeIndex = atomicTransformQueueCount.Add(validBitCount); + int validBitIndex = math.tzcnt(validBits); + + while (validBits != 0) + { + int absoluteIndex = jobRange.absoluteStart + validBitIndex; + int localIndex = jobRange.localStart + validBitIndex; + + InstanceHandle instance = instances[absoluteIndex]; + int instanceIndex = renderWorld.HandleToIndex(instance); + bool isStaticObject = renderWorld.rendererSettings[instanceIndex].IsPartOfStaticBatch; + + renderWorld.movedInCurrentFrameBits.Set(instanceIndex, !isStaticObject); + transformUpdateInstanceQueue[writeIndex] = instance; + + ref readonly float4x4 l2w = ref localToWorldMatrices.ElementAt(localIndex); + ref readonly AABB localAABB = ref renderWorld.localAABBs.ElementAt(instanceIndex); + AABB worldAABB = AABBTransform(l2w, localAABB); + float det = math.determinant((float3x3)l2w); + + renderWorld.worldAABBs.ElementAtRW(instanceIndex) = worldAABB; + renderWorld.localToWorldIsFlippedBits.Set(instanceIndex, det < 0.0f); + + if (initialize) + { + PackedMatrix l2wPacked = PackedMatrix.FromFloat4x4(l2w); + PackedMatrix l2wPrevPacked = PackedMatrix.FromFloat4x4(prevLocalToWorldMatrices.ElementAt(localIndex)); + + transformUpdateDataQueue[writeIndex * 2] = new TransformUpdatePacket() + { + localToWorld0 = l2wPacked.packed0, + localToWorld1 = l2wPacked.packed1, + localToWorld2 = l2wPacked.packed2, + }; + transformUpdateDataQueue[writeIndex * 2 + 1] = new TransformUpdatePacket() + { + localToWorld0 = l2wPrevPacked.packed0, + localToWorld1 = l2wPrevPacked.packed1, + localToWorld2 = l2wPrevPacked.packed2, + }; + } + else + { + PackedMatrix l2wPacked = PackedMatrix.FromFloat4x4(l2w); + + transformUpdateDataQueue[writeIndex] = new TransformUpdatePacket() + { + localToWorld0 = l2wPacked.packed0, + localToWorld1 = l2wPacked.packed1, + localToWorld2 = l2wPacked.packed2, + }; + } + + if (enableBoundingSpheres) + boundingSpheresDataQueue[writeIndex] = new float4(worldAABB.center.x, worldAABB.center.y, worldAABB.center.z, math.distance(worldAABB.max, worldAABB.min) * 0.5f); + + writeIndex += 1; + validBits &= ~(1ul << validBitIndex); + validBitIndex = math.tzcnt(validBits); + } + } + } + } + + [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] + private unsafe struct ProbesUpdateJob : IJobParallelForBatch + { + public const int MaxBatchSize = 64; + + [ReadOnly] [NativeDisableContainerSafetyRestriction, NoAlias] public NativeArray instances; + [ReadOnly] [NativeDisableContainerSafetyRestriction, NoAlias] public RenderWorld renderWorld; + + [NativeDisableUnsafePtrRestriction] public UnsafeAtomicCounter32 atomicProbesQueueCount; + [NativeDisableParallelForRestriction] public NativeArray probeInstanceQueue; + [NativeDisableParallelForRestriction] public NativeArray compactTetrahedronCache; + [NativeDisableParallelForRestriction] public NativeArray probeQueryPosition; + + public void Execute(int startIndex, int count) + { + Assert.IsTrue(MaxBatchSize <= 64 && count <= 64); + ulong validBits = 0; + + for (int i = 0; i < count; ++i) + { + InstanceHandle instance = instances[startIndex + i]; + if (!instance.isValid) + continue; + + int instanceIndex = renderWorld.HandleToIndex(instance); + InternalMeshRendererSettings rendererSettings = renderWorld.rendererSettings[instanceIndex]; + int lightmapIndex = renderWorld.lightmapIndices[instanceIndex]; + bool hasLightProbes = !LightmapUtils.UsesLightmaps(lightmapIndex) + && rendererSettings.LightProbeUsage == LightProbeUsage.BlendProbes; + + if (!hasLightProbes) + continue; + + validBits |= 1ul << i; + } + + int validBitCount = math.countbits(validBits); + + if (validBitCount > 0) + { + int writeIndex = atomicProbesQueueCount.Add(validBitCount); + int validBitIndex = math.tzcnt(validBits); + + while (validBits != 0) + { + InstanceHandle instance = instances[startIndex + validBitIndex]; + int instanceIndex = renderWorld.HandleToIndex(instance); + ref readonly AABB worldAABB = ref renderWorld.worldAABBs.ElementAt(instanceIndex); + + probeInstanceQueue[writeIndex] = instance; + probeQueryPosition[writeIndex] = worldAABB.center; + compactTetrahedronCache[writeIndex] = renderWorld.tetrahedronCacheIndices[instanceIndex]; + + writeIndex += 1; + validBits &= ~(1ul << validBitIndex); + validBitIndex = math.tzcnt(validBits); + } + } + } + } + + [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] + private struct MotionUpdateJob : IJobParallelFor + { + [ReadOnly] public int queueWriteBase; + + [NativeDisableParallelForRestriction] public RenderWorld renderWorld; + [NativeDisableUnsafePtrRestriction] public UnsafeAtomicCounter32 atomicUpdateQueueCount; + [NativeDisableParallelForRestriction][WriteOnly] public NativeArray transformUpdateInstanceQueue; + + public void Execute(int chunk_index) + { + int maxChunkBitCount = math.min(renderWorld.instanceCount - 64 * chunk_index, 64); + ulong chunkBitMask = ~0ul >> (64 - maxChunkBitCount); + + ulong currentChunkBits = renderWorld.movedInCurrentFrameBits.GetChunk(chunk_index) & chunkBitMask; + ulong prevChunkBits = renderWorld.movedInPreviousFrameBits.GetChunk(chunk_index) & chunkBitMask; + + // update state in memory for the next frame + renderWorld.movedInCurrentFrameBits.SetChunk(chunk_index, 0); + renderWorld.movedInPreviousFrameBits.SetChunk(chunk_index, currentChunkBits); + + // ensure that objects that were moved last frame update their previous world matrix, if not already fully updated + ulong remainingChunkBits = prevChunkBits & ~currentChunkBits; + + // allocate space for all the writes from this chunk + int chunkBitCount = math.countbits(remainingChunkBits); + int writeIndex = queueWriteBase; + if (chunkBitCount > 0) + writeIndex += atomicUpdateQueueCount.Add(chunkBitCount); + + // loop over set bits to do the writes + int indexInChunk = math.tzcnt(remainingChunkBits); + + while (indexInChunk < 64) + { + int instanceIndex = 64 * chunk_index + indexInChunk; + transformUpdateInstanceQueue[writeIndex] = renderWorld.IndexToHanle(instanceIndex); + + writeIndex += 1; + remainingChunkBits &= ~(1ul << indexInChunk); + indexInChunk = math.tzcnt(remainingChunkBits); + } + } + } + + [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] + private unsafe struct UpdateRendererInstancesJob : IJobParallelFor + { + [ReadOnly] public NativeArray jobRanges; + [ReadOnly] public MeshRendererUpdateBatch updateBatch; + [ReadOnly] public NativeArray instances; + [ReadOnly] public NativeParallelHashMap lodGroupDataMap; + + [NativeDisableParallelForRestriction][NativeDisableContainerSafetyRestriction] public RenderWorld renderWorld; + + public void Execute(int jobIndex) + { + JaggedJobRange jobRange = jobRanges[jobIndex]; + NativeArray rendererSection = updateBatch.instanceIDs[jobRange.sectionIndex]; + NativeArray meshSection = updateBatch.GetMeshSectionOrDefault(jobRange.sectionIndex); + NativeArray subMeshStartIndexSection = updateBatch.GetSubMeshStartIndexSectionOrDefault(jobRange.sectionIndex); + NativeArray materialSection = updateBatch.GetMaterialSectionOrDefault(jobRange.sectionIndex); + NativeArray subMaterialRangeSection = updateBatch.GetSubMaterialRangeSectionOrDefault(jobRange.sectionIndex); + NativeArray localToWorldSection = updateBatch.GetLocalToWorldSectionOrDefault(jobRange.sectionIndex); + NativeArray localBoundsSection = updateBatch.GetLocalBoundsSectionOrDefault(jobRange.sectionIndex); + NativeArray rendererSettingsSection = updateBatch.GetRendererSettingsSectionOrDefault(jobRange.sectionIndex); + NativeArray rendererPrioritySection = updateBatch.GetRendererPrioritySectionOrDefault(jobRange.sectionIndex); + NativeArray lightmapIndexSection = updateBatch.GetLightmapIndexSectionOrDefault(jobRange.sectionIndex); + NativeArray parentLODGroupSection = updateBatch.GetParentLODGroupIDSectionOrDefault(jobRange.sectionIndex); + NativeArray lodMaskSection = updateBatch.GetLODMaskSectionOrDefault(jobRange.sectionIndex); + NativeArray meshLodSettingsSection = updateBatch.GetMeshLodSettingsSectionOrDefault(jobRange.sectionIndex); + UnsafeBitArray renderingEnabledSection = updateBatch.GetRenderingEnabledSectionOrDefault(jobRange.sectionIndex); + + NativeArray sceneCullingMaskSection = default; + ulong sharedSceneCullingMask = 0; + if (updateBatch.HasAnyComponent(MeshRendererComponentMask.SceneCullingMask)) + { + if (updateBatch.useSharedSceneCullingMask) + { + sharedSceneCullingMask = updateBatch.sharedSceneCullingMasks[jobRange.sectionIndex]; + } + else + { + sceneCullingMaskSection = updateBatch.sceneCullingMasks[jobRange.sectionIndex]; + } + } + + MeshRendererUpdateType updateType = updateBatch.updateType; + bool hasOnlyKnowInstances = updateType == MeshRendererUpdateType.NoStructuralChanges + || updateType == MeshRendererUpdateType.RecreateOnlyKnownInstances; + + int treeCountDelta = 0; + + for (int i = 0; i < jobRange.length; i++) + { + int localIndex = jobRange.localStart + i; + int absoluteIndex = jobRange.absoluteStart + i; + EntityId renderer = rendererSection[localIndex]; + + InstanceHandle instance = instances[absoluteIndex]; + Assert.IsTrue(instance.isValid, "Invalid Instance"); + if (!instance.isValid) + continue; + + int instanceIndex = renderWorld.HandleToIndex(instance); + bool oldInstanceHasTree = renderWorld.rendererSettings[instanceIndex].HasTree; + + // Rebuild instance from scratch in all cases except when we know there is no structural changes. + if (updateType != MeshRendererUpdateType.NoStructuralChanges) + renderWorld.ResetInstance(instanceIndex); + + if (hasOnlyKnowInstances) + { + Assert.IsTrue(renderWorld.instanceIDs[instanceIndex] == renderer); + } + else + { + renderWorld.instanceIDs.ElementAtRW(instanceIndex) = renderer; + } + + if (updateBatch.HasAnyComponent(MeshRendererComponentMask.Material)) + { + var subMaterialRange = subMaterialRangeSection[localIndex]; + var subMaterialIDs = new EmbeddedArray32(materialSection.GetSubArray(subMaterialRange.start, subMaterialRange.length), + Allocator.Persistent); + + renderWorld.materialIDArrays.ElementAtRW(instanceIndex).Dispose(); + renderWorld.materialIDArrays.ElementAtRW(instanceIndex) = subMaterialIDs; + } + + if (updateBatch.HasAnyComponent(MeshRendererComponentMask.LocalBounds)) + { + Assert.AreEqual(localToWorldSection.Length, rendererSection.Length); + ref readonly float4x4 l2w = ref localToWorldSection.ElementAt(localIndex); + AABB localBounds = localBoundsSection[localIndex]; + AABB worldBounds = AABBTransform(l2w, localBounds); + + // Note that world bounds are updated here only when local bounds change, but not when transforms change. + // Transform changes are handled in other dedicated jobs. + renderWorld.localAABBs.ElementAtRW(instanceIndex) = localBounds; + renderWorld.worldAABBs.ElementAtRW(instanceIndex) = worldBounds; + } + + if (updateBatch.HasAnyComponent(MeshRendererComponentMask.ParentLODGroup)) + { + EntityId parentLODGroup = parentLODGroupSection[localIndex]; + + // Renderer's LODGroup could be disabled which means that the renderer is not managed by it. + GPUInstanceIndex parentLODGroupHandle = GPUInstanceIndex.Invalid; + if (lodGroupDataMap.TryGetValue(parentLODGroup, out var handle)) + parentLODGroupHandle = handle; + + renderWorld.lodGroupIndices.ElementAtRW(instanceIndex) = parentLODGroupHandle; + } + + if (updateBatch.HasAnyComponent(MeshRendererComponentMask.LODMask)) + renderWorld.lodMasks.ElementAtRW(instanceIndex) = lodMaskSection[localIndex]; + + if (updateBatch.HasAnyComponent(MeshRendererComponentMask.MeshLodSettings)) + renderWorld.meshLodRendererSettings.ElementAtRW(instanceIndex) = meshLodSettingsSection[localIndex]; + + if (updateBatch.HasAnyComponent(MeshRendererComponentMask.Mesh)) + renderWorld.meshIDs.ElementAtRW(instanceIndex) = meshSection[localIndex]; + + if (updateBatch.HasAnyComponent(MeshRendererComponentMask.SubMeshStartIndex)) + renderWorld.subMeshStartIndices.ElementAtRW(instanceIndex) = subMeshStartIndexSection[localIndex]; + + if (updateBatch.HasAnyComponent(MeshRendererComponentMask.RendererSettings)) + { + InternalMeshRendererSettings newSettings = rendererSettingsSection[localIndex]; + + if (oldInstanceHasTree) + { + if (!newSettings.HasTree) + --treeCountDelta; + } + else + { + if (newSettings.HasTree) + ++treeCountDelta; + } + + renderWorld.rendererSettings.ElementAtRW(instanceIndex) = newSettings; + } + + if (updateBatch.HasAnyComponent(MeshRendererComponentMask.Lightmap)) + renderWorld.lightmapIndices.ElementAtRW(instanceIndex) = lightmapIndexSection[localIndex]; + + if (updateBatch.HasAnyComponent(MeshRendererComponentMask.RendererPriority)) + renderWorld.rendererPriorities.ElementAtRW(instanceIndex) = rendererPrioritySection[localIndex]; + + if (updateBatch.HasAnyComponent(MeshRendererComponentMask.RenderingEnabled)) + renderWorld.renderingEnabled.Set(instanceIndex, renderingEnabledSection.IsSet(localIndex)); +#if UNITY_EDITOR + if (updateBatch.HasAnyComponent(MeshRendererComponentMask.SceneCullingMask)) + renderWorld.sceneCullingMasks.ElementAtRW(instanceIndex) = updateBatch.useSharedSceneCullingMask ? + sharedSceneCullingMask : + sceneCullingMaskSection[localIndex]; +#endif + } + + int oldTotalTreeCount = renderWorld.atomicTotalTreeCount.Add(treeCountDelta); + Assert.IsTrue(oldTotalTreeCount + treeCountDelta >= 0); + } + } + + [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] + private struct GetVisibleNonProcessedTreeInstancesJob : IJobParallelForBatch + { + public const int MaxBatchSize = 64; + + [ReadOnly] public RenderWorld renderWorld; + [ReadOnly][NativeDisableContainerSafetyRestriction, NoAlias] public ParallelBitArray compactedVisibilityMasks; + [ReadOnly] public bool becomeVisible; + + [NativeDisableParallelForRestriction] public ParallelBitArray processedBits; + + [NativeDisableParallelForRestriction][WriteOnly] public NativeArray renderers; + [NativeDisableParallelForRestriction][WriteOnly] public NativeArray instances; + + [NativeDisableUnsafePtrRestriction] public UnsafeAtomicCounter32 atomicTreeInstancesCount; + + public void Execute(int startIndex, int count) + { + Assert.IsTrue(MaxBatchSize == 64 && count <= 64); + ulong validBits = 0; + + var chunkIndex = startIndex / 64; + var visibleInPrevFrameChunk = renderWorld.visibleInPreviousFrameBits.GetChunk(chunkIndex); + var processedChunk = processedBits.GetChunk(chunkIndex); + + for (int i = 0; i < count; ++i) + { + int instanceIndex = startIndex + i; + InstanceHandle instance = renderWorld.IndexToHanle(instanceIndex); + bool hasTree = renderWorld.rendererSettings[instanceIndex].HasTree; + + if (hasTree && compactedVisibilityMasks.Get(instance.index)) + { + var bitMask = 1ul << i; + + var processedInCurrentFrame = (processedChunk & bitMask) != 0; + + if (!processedInCurrentFrame) + { + bool visibleInPrevFrame = (visibleInPrevFrameChunk & bitMask) != 0; + + if (becomeVisible) + { + if (!visibleInPrevFrame) + validBits |= bitMask; + } + else + { + if (visibleInPrevFrame) + validBits |= bitMask; + } + } + } + } + + int validBitsCount = math.countbits(validBits); + + if (validBitsCount > 0) + { + processedBits.SetChunk(chunkIndex, processedChunk | validBits); + + int writeIndex = atomicTreeInstancesCount.Add(validBitsCount); + int validBitIndex = math.tzcnt(validBits); + + while (validBits != 0) + { + int instanceIndex = startIndex + validBitIndex; + EntityId renderer = renderWorld.instanceIDs[instanceIndex]; + InstanceHandle instance = renderWorld.IndexToHanle(instanceIndex); + + renderers[writeIndex] = renderer; + instances[writeIndex] = instance; + + writeIndex += 1; + validBits &= ~(1ul << validBitIndex); + validBitIndex = math.tzcnt(validBits); + } + } + } + } + + [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] + private struct UpdateCompactedInstanceVisibilityJob : IJobParallelForBatch + { + public const int MaxBatchSize = 64; + + [ReadOnly] public ParallelBitArray compactedVisibilityMasks; + + [NativeDisableContainerSafetyRestriction, NoAlias][NativeDisableParallelForRestriction] public RenderWorld renderWorld; + + public void Execute(int startIndex, int count) + { + Assert.IsTrue(MaxBatchSize == 64 && count <= 64); + ulong visibleBits = 0; + + for (int i = 0; i < count; ++i) + { + int instanceIndex = startIndex + i; + InstanceHandle instance = renderWorld.IndexToHanle(instanceIndex); + bool visible = compactedVisibilityMasks.Get(instance.index); + + if (visible) + visibleBits |= 1ul << i; + } + + renderWorld.visibleInPreviousFrameBits.SetChunk(startIndex / 64, visibleBits); + } + } + + } +} diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/InstanceDataSystem.Jobs.cs.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/InstanceDataSystem.Jobs.cs.meta new file mode 100644 index 00000000000..4c65ce22b63 --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/InstanceDataSystem.Jobs.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 4fde0d9aabe2aee46b306a42532dafc7 \ No newline at end of file diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/InstanceDataSystem.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/InstanceDataSystem.cs new file mode 100644 index 00000000000..bf6a57994a5 --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/InstanceDataSystem.cs @@ -0,0 +1,974 @@ +using System; +using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; +using Unity.Jobs; +using Unity.Mathematics; +using Unity.Profiling; +using UnityEngine.Assertions; +using UnityEngine.Profiling; + +namespace UnityEngine.Rendering +{ + internal partial class InstanceDataSystem : IDisposable + { + private NativeReference m_ArchetypeManager; + private DefaultGPUComponents m_DefaultGPUComponents; + private GPUInstanceDataBuffer m_InstanceDataBuffer; + private InstanceAllocators m_InstanceAllocators; + private RenderWorld m_RenderWorld; + private NativeParallelHashMap m_RendererToInstanceMap; + private GPUCapacityResizingPolicy m_GPUResizingPolicy; + private CommandBuffer m_CmdBuffer; + + private ComputeShader m_TransformUpdateCS; + private ComputeShader m_WindDataUpdateCS; + private int m_TransformInitKernel; + private int m_TransformUpdateKernel; + private int m_MotionUpdateKernel; + private int m_ProbeUpdateKernel; + private int m_LODUpdateKernel; + private int m_WindDataCopyHistoryKernel; + + private ComputeBuffer m_UpdateIndexQueueBuffer; + private ComputeBuffer m_ProbeUpdateDataQueueBuffer; + private ComputeBuffer m_ProbeOcclusionUpdateDataQueueBuffer; + private ComputeBuffer m_TransformUpdateDataQueueBuffer; + private ComputeBuffer m_BoundingSpheresUpdateDataQueueBuffer; + + private bool m_EnableBoundingSpheres; + + private readonly int[] m_ScratchWindParamAddressArray = new int[(int)SpeedTreeWindParamIndex.MaxWindParamsCount * 4]; + + public NativeReference archetypeManager => m_ArchetypeManager; + public ref DefaultGPUComponents defaultGPUComponents => ref m_DefaultGPUComponents; + public bool hasBoundingSpheres => m_EnableBoundingSpheres; + public int totalTreeCount => m_RenderWorld.totalTreeCount; + public GPUInstanceDataBuffer gpuBuffer => m_InstanceDataBuffer; + public GraphicsBufferHandle gpuBufferHandle => m_InstanceDataBuffer.nativeBuffer.bufferHandle; + public int gpuBufferLayoutVersion => m_InstanceDataBuffer.layoutVersion; + public ref RenderWorld renderWorld => ref m_RenderWorld; + public NativeArray indexToHandle => m_RenderWorld.indexToHandle; + public NativeArray.ReadOnly componentsMetadata => m_InstanceDataBuffer.componentsMetadata; + + public event Action onGPUBufferLayoutChanged; + + public InstanceDataSystem(int instancesCPUCapacity, + bool enableBoundingSpheres, + GPUResidentDrawerResources resources, + GPUCapacityResizingPolicy gpuResizingPolicy = GPUCapacityResizingPolicy.DoubleOnGrow_HalveOnQuarterShrink) + { + m_ArchetypeManager = new NativeReference(Allocator.Persistent); + m_ArchetypeManager.GetRef().Initialize(); + + m_DefaultGPUComponents = new DefaultGPUComponents(ref m_ArchetypeManager.GetRef(), enableBoundingSpheres); + + m_InstanceDataBuffer = new GPUInstanceDataBuffer(ref m_ArchetypeManager.GetRef(), new GPUInstanceDataBufferLayout(1, Allocator.Temp) + { + { m_DefaultGPUComponents.defaultGOArchetype, 0 } + }, + resources); + + m_GPUResizingPolicy = gpuResizingPolicy; + + m_InstanceAllocators = new InstanceAllocators(); + + m_InstanceAllocators.Initialize(); + m_RenderWorld.Initialize(instancesCPUCapacity); + + m_CmdBuffer = new CommandBuffer { name = "InstanceDataSystemBuffer" }; + + m_RendererToInstanceMap = new NativeParallelHashMap(instancesCPUCapacity, Allocator.Persistent); + + m_TransformUpdateCS = resources.transformUpdaterKernels; + m_WindDataUpdateCS = resources.windDataUpdaterKernels; + + m_TransformInitKernel = m_TransformUpdateCS.FindKernel("ScatterInitTransformMain"); + m_TransformUpdateKernel = m_TransformUpdateCS.FindKernel("ScatterUpdateTransformMain"); + m_MotionUpdateKernel = m_TransformUpdateCS.FindKernel("ScatterUpdateMotionMain"); + m_ProbeUpdateKernel = m_TransformUpdateCS.FindKernel("ScatterUpdateProbesMain"); + if (enableBoundingSpheres) + m_TransformUpdateCS.EnableKeyword("PROCESS_BOUNDING_SPHERES"); + else + m_TransformUpdateCS.DisableKeyword("PROCESS_BOUNDING_SPHERES"); + + m_WindDataCopyHistoryKernel = m_WindDataUpdateCS.FindKernel("WindDataCopyHistoryMain"); + + m_EnableBoundingSpheres = enableBoundingSpheres; + } + + public void Dispose() + { + m_CmdBuffer.Release(); + + m_InstanceAllocators.Dispose(); + m_RenderWorld.Dispose(); + + m_RendererToInstanceMap.Dispose(); + + m_UpdateIndexQueueBuffer?.Dispose(); + m_ProbeUpdateDataQueueBuffer?.Dispose(); + m_ProbeOcclusionUpdateDataQueueBuffer?.Dispose(); + m_TransformUpdateDataQueueBuffer?.Dispose(); + m_BoundingSpheresUpdateDataQueueBuffer?.Dispose(); + + m_InstanceDataBuffer.Dispose(); + + m_DefaultGPUComponents.Dispose(); + m_ArchetypeManager.GetRef().Dispose(); + m_ArchetypeManager.Dispose(); + } + + private void EnsureIndexQueueBufferCapacity(int capacity) + { + if(m_UpdateIndexQueueBuffer == null || m_UpdateIndexQueueBuffer.count < capacity) + { + m_UpdateIndexQueueBuffer?.Dispose(); + m_UpdateIndexQueueBuffer = new ComputeBuffer(capacity, sizeof(int), ComputeBufferType.Raw); + } + } + + private void EnsureProbeBuffersCapacity(int capacity) + { + EnsureIndexQueueBufferCapacity(capacity); + + if (m_ProbeUpdateDataQueueBuffer == null || m_ProbeUpdateDataQueueBuffer.count < capacity) + { + m_ProbeUpdateDataQueueBuffer?.Dispose(); + m_ProbeOcclusionUpdateDataQueueBuffer?.Dispose(); + m_ProbeUpdateDataQueueBuffer = new ComputeBuffer(capacity, UnsafeUtility.SizeOf(), ComputeBufferType.Structured); + m_ProbeOcclusionUpdateDataQueueBuffer = new ComputeBuffer(capacity, UnsafeUtility.SizeOf(), ComputeBufferType.Structured); + } + } + + private void EnsureTransformBuffersCapacity(int capacity) + { + EnsureIndexQueueBufferCapacity(capacity); + + // Current and the previous matrices + int transformsCapacity = capacity * 2; + + if (m_TransformUpdateDataQueueBuffer == null || m_TransformUpdateDataQueueBuffer.count < transformsCapacity) + { + m_TransformUpdateDataQueueBuffer?.Dispose(); + m_BoundingSpheresUpdateDataQueueBuffer?.Dispose(); + m_TransformUpdateDataQueueBuffer = new ComputeBuffer(transformsCapacity, UnsafeUtility.SizeOf(), ComputeBufferType.Structured); + if (m_EnableBoundingSpheres) + m_BoundingSpheresUpdateDataQueueBuffer = new ComputeBuffer(capacity, UnsafeUtility.SizeOf(), ComputeBufferType.Structured); + } + } + + private JobHandle ScheduleInterpolateProbesAndUpdateTetrahedronCache(int queueCount, NativeArray probeUpdateInstanceQueue, + NativeArray compactTetrahedronCache, + NativeArray probeQueryPosition, + NativeArray probeUpdateDataQueue, + NativeArray probeOcclusionUpdateDataQueue) + { + var lightProbesQuery = new LightProbesQuery(Allocator.TempJob); + + var calculateProbesJob = new CalculateInterpolatedLightAndOcclusionProbesBatchJob + { + lightProbesQuery = lightProbesQuery, + probesCount = queueCount, + queryPostitions = probeQueryPosition, + compactTetrahedronCache = compactTetrahedronCache, + probesSphericalHarmonics = probeUpdateDataQueue, + probesOcclusion = probeOcclusionUpdateDataQueue + }; + + var totalBatchCount = 1 + (queueCount / CalculateInterpolatedLightAndOcclusionProbesBatchJob.k_CalculatedProbesPerBatch); + + var calculateProbesJobHandle = calculateProbesJob.ScheduleByRef(totalBatchCount, 1); + + lightProbesQuery.Dispose(calculateProbesJobHandle); + + var scatterTetrahedronCacheIndicesJob = new ScatterTetrahedronCacheIndicesJob + { + compactTetrahedronCache = compactTetrahedronCache, + probeInstances = probeUpdateInstanceQueue, + renderWorld = m_RenderWorld + }; + + return scatterTetrahedronCacheIndicesJob.ScheduleByRef(queueCount, 128, calculateProbesJobHandle); + } + + private void InterpolateProbesAndUpdateTetrahedronCache(int queueCount, NativeArray probeUpdateInstanceQueue, + NativeArray compactTetrahedronCache, + NativeArray probeQueryPosition, + NativeArray probeUpdateDataQueue, + NativeArray probeOcclusionUpdateDataQueue) + { + Profiler.BeginSample("InterpolateProbesAndUpdateTetrahedronCache"); + + ScheduleInterpolateProbesAndUpdateTetrahedronCache(queueCount, + probeUpdateInstanceQueue, + compactTetrahedronCache, + probeQueryPosition, + probeUpdateDataQueue, + probeOcclusionUpdateDataQueue) + .Complete(); + + Profiler.EndSample(); + } + + private void DispatchProbeUpdateCommand(int queueCount, + NativeArray probeInstanceQueue, + NativeArray probeUpdateDataQueue, + NativeArray probeOcclusionUpdateDataQueue) + { + Profiler.BeginSample("DispatchProbeUpdateCommand"); + EnsureProbeBuffersCapacity(queueCount); + + var gpuIndices = new NativeArray(queueCount, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); + m_InstanceDataBuffer.QueryInstanceGPUIndices(m_RenderWorld, probeInstanceQueue.GetSubArray(0, queueCount), gpuIndices); + + Profiler.BeginSample("ComputeBuffer.SetData"); + m_UpdateIndexQueueBuffer.SetData(gpuIndices, 0, 0, queueCount); + m_ProbeUpdateDataQueueBuffer.SetData(probeUpdateDataQueue, 0, 0, queueCount); + m_ProbeOcclusionUpdateDataQueueBuffer.SetData(probeOcclusionUpdateDataQueue, 0, 0, queueCount); + Profiler.EndSample(); + + m_TransformUpdateCS.SetInt(InstanceTransformUpdateIDs._ProbeUpdateQueueCount, queueCount); + m_TransformUpdateCS.SetInt(InstanceTransformUpdateIDs._SHUpdateVec4Offset, m_InstanceDataBuffer.GetComponentGPUUIntOffset(m_DefaultGPUComponents.shCoefficients)); + m_TransformUpdateCS.SetBuffer(m_ProbeUpdateKernel, InstanceTransformUpdateIDs._ProbeUpdateIndexQueue, m_UpdateIndexQueueBuffer); + m_TransformUpdateCS.SetBuffer(m_ProbeUpdateKernel, InstanceTransformUpdateIDs._ProbeUpdateDataQueue, m_ProbeUpdateDataQueueBuffer); + m_TransformUpdateCS.SetBuffer(m_ProbeUpdateKernel, InstanceTransformUpdateIDs._ProbeOcclusionUpdateDataQueue, m_ProbeOcclusionUpdateDataQueueBuffer); + m_TransformUpdateCS.SetBuffer(m_ProbeUpdateKernel, InstanceTransformUpdateIDs._OutputProbeBuffer, m_InstanceDataBuffer.nativeBuffer); + m_TransformUpdateCS.Dispatch(m_ProbeUpdateKernel, (queueCount + 63) / 64, 1, 1); + + gpuIndices.Dispose(); + Profiler.EndSample(); + } + + private void DispatchMotionUpdateCommand(int motionQueueCount, NativeArray transformInstanceQueue) + { + Profiler.BeginSample("DispatchMotionUpdateCommand"); + EnsureTransformBuffersCapacity(motionQueueCount); + + var gpuIndices = new NativeArray(motionQueueCount, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); + m_InstanceDataBuffer.QueryInstanceGPUIndices(m_RenderWorld, transformInstanceQueue.GetSubArray(0, motionQueueCount), gpuIndices); + + Profiler.BeginSample("ComputeBuffer.SetData"); + m_UpdateIndexQueueBuffer.SetData(gpuIndices, 0, 0, motionQueueCount); + Profiler.EndSample(); + + m_TransformUpdateCS.SetInt(InstanceTransformUpdateIDs._TransformUpdateQueueCount, motionQueueCount); + m_TransformUpdateCS.SetInt(InstanceTransformUpdateIDs._TransformUpdateOutputL2WVec4Offset, m_InstanceDataBuffer.GetComponentGPUUIntOffset(m_DefaultGPUComponents.objectToWorld)); + m_TransformUpdateCS.SetInt(InstanceTransformUpdateIDs._TransformUpdateOutputW2LVec4Offset, m_InstanceDataBuffer.GetComponentGPUUIntOffset(m_DefaultGPUComponents.worldToObject)); + m_TransformUpdateCS.SetInt(InstanceTransformUpdateIDs._TransformUpdateOutputPrevL2WVec4Offset, m_InstanceDataBuffer.GetComponentGPUUIntOffset(m_DefaultGPUComponents.matrixPreviousM)); + m_TransformUpdateCS.SetInt(InstanceTransformUpdateIDs._TransformUpdateOutputPrevW2LVec4Offset, m_InstanceDataBuffer.GetComponentGPUUIntOffset(m_DefaultGPUComponents.matrixPreviousMI)); + m_TransformUpdateCS.SetBuffer(m_MotionUpdateKernel, InstanceTransformUpdateIDs._TransformUpdateIndexQueue, m_UpdateIndexQueueBuffer); + m_TransformUpdateCS.SetBuffer(m_MotionUpdateKernel, InstanceTransformUpdateIDs._OutputTransformBuffer, m_InstanceDataBuffer.nativeBuffer); + m_TransformUpdateCS.Dispatch(m_MotionUpdateKernel, (motionQueueCount + 63) / 64, 1, 1); + + gpuIndices.Dispose(); + Profiler.EndSample(); + } + + private void DispatchTransformUpdateCommand(bool initialize, + int transformQueueCount, + NativeArray transformInstanceQueue, + NativeArray updateDataQueue, + NativeArray boundingSphereUpdateDataQueue) + { + Profiler.BeginSample("DispatchTransformUpdateCommand"); + EnsureTransformBuffersCapacity(transformQueueCount); + + int transformQueueDataSize; + int kernel; + + if (initialize) + { + // When we reinitialize we have the current and the previous matrices per transform. + transformQueueDataSize = transformQueueCount * 2; + kernel = m_TransformInitKernel; + } + else + { + transformQueueDataSize = transformQueueCount; + kernel = m_TransformUpdateKernel; + } + + var gpuIndices = new NativeArray(transformQueueCount, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); + m_InstanceDataBuffer.QueryInstanceGPUIndices(m_RenderWorld, transformInstanceQueue.GetSubArray(0, transformQueueCount), gpuIndices); + + Profiler.BeginSample("ComputeBuffer.SetData"); + m_UpdateIndexQueueBuffer.SetData(gpuIndices, 0, 0, transformQueueCount); + m_TransformUpdateDataQueueBuffer.SetData(updateDataQueue, 0, 0, transformQueueDataSize); + if (m_EnableBoundingSpheres) + m_BoundingSpheresUpdateDataQueueBuffer.SetData(boundingSphereUpdateDataQueue, 0, 0, transformQueueCount); + Profiler.EndSample(); + + m_TransformUpdateCS.SetInt(InstanceTransformUpdateIDs._TransformUpdateQueueCount, transformQueueCount); + m_TransformUpdateCS.SetInt(InstanceTransformUpdateIDs._TransformUpdateOutputL2WVec4Offset, m_InstanceDataBuffer.GetComponentGPUUIntOffset(m_DefaultGPUComponents.objectToWorld)); + m_TransformUpdateCS.SetInt(InstanceTransformUpdateIDs._TransformUpdateOutputW2LVec4Offset, m_InstanceDataBuffer.GetComponentGPUUIntOffset(m_DefaultGPUComponents.worldToObject)); + m_TransformUpdateCS.SetInt(InstanceTransformUpdateIDs._TransformUpdateOutputPrevL2WVec4Offset, m_InstanceDataBuffer.GetComponentGPUUIntOffset(m_DefaultGPUComponents.matrixPreviousM)); + m_TransformUpdateCS.SetInt(InstanceTransformUpdateIDs._TransformUpdateOutputPrevW2LVec4Offset, m_InstanceDataBuffer.GetComponentGPUUIntOffset(m_DefaultGPUComponents.matrixPreviousMI)); + m_TransformUpdateCS.SetBuffer(kernel, InstanceTransformUpdateIDs._TransformUpdateIndexQueue, m_UpdateIndexQueueBuffer); + m_TransformUpdateCS.SetBuffer(kernel, InstanceTransformUpdateIDs._TransformUpdateDataQueue, m_TransformUpdateDataQueueBuffer); + if (m_EnableBoundingSpheres) + { + Assert.IsTrue(m_DefaultGPUComponents.boundingSphere.valid); + m_TransformUpdateCS.SetInt(InstanceTransformUpdateIDs._BoundingSphereOutputVec4Offset, m_InstanceDataBuffer.GetComponentGPUUIntOffset(m_DefaultGPUComponents.boundingSphere)); + m_TransformUpdateCS.SetBuffer(kernel, InstanceTransformUpdateIDs._BoundingSphereDataQueue, m_BoundingSpheresUpdateDataQueueBuffer); + } + m_TransformUpdateCS.SetBuffer(kernel, InstanceTransformUpdateIDs._OutputTransformBuffer, m_InstanceDataBuffer.nativeBuffer); + m_TransformUpdateCS.Dispatch(kernel, (transformQueueCount + 63) / 64, 1, 1); + + gpuIndices.Dispose(); + Profiler.EndSample(); + } + + private void DispatchWindDataCopyHistoryCommand(NativeArray gpuIndices) + { + Profiler.BeginSample("DispatchWindDataCopyHistory"); + + int kernel = m_WindDataCopyHistoryKernel; + int instancesCount = gpuIndices.Length; + + EnsureIndexQueueBufferCapacity(instancesCount); + + Profiler.BeginSample("ComputeBuffer.SetData"); + m_UpdateIndexQueueBuffer.SetData(gpuIndices, 0, 0, instancesCount); + Profiler.EndSample(); + + m_WindDataUpdateCS.SetInt(InstanceWindDataUpdateIDs._WindDataQueueCount, instancesCount); + for (int i = 0; i < (int)SpeedTreeWindParamIndex.MaxWindParamsCount; ++i) + m_ScratchWindParamAddressArray[i * 4] = m_InstanceDataBuffer.GetComponentGPUAddress(m_DefaultGPUComponents.speedTreeWind[i]); + m_WindDataUpdateCS.SetInts(InstanceWindDataUpdateIDs._WindParamAddressArray, m_ScratchWindParamAddressArray); + for (int i = 0; i < (int)SpeedTreeWindParamIndex.MaxWindParamsCount; ++i) + m_ScratchWindParamAddressArray[i * 4] = m_InstanceDataBuffer.GetComponentGPUAddress(m_DefaultGPUComponents.speedTreeWindHistory[i]); + m_WindDataUpdateCS.SetInts(InstanceWindDataUpdateIDs._WindHistoryParamAddressArray, m_ScratchWindParamAddressArray); + + m_WindDataUpdateCS.SetBuffer(kernel, InstanceWindDataUpdateIDs._WindDataUpdateIndexQueue, m_UpdateIndexQueueBuffer); + m_WindDataUpdateCS.SetBuffer(kernel, InstanceWindDataUpdateIDs._WindDataBuffer, m_InstanceDataBuffer.nativeBuffer); + m_WindDataUpdateCS.Dispatch(kernel, (instancesCount + 63) / 64, 1, 1); + + Profiler.EndSample(); + } + + private unsafe void UpdateInstanceMotionsDataInternal() + { + var transformUpdateInstanceQueue = new NativeArray(m_RenderWorld.instanceCount, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); + + var motionQueueCount = 0; + + new MotionUpdateJob + { + queueWriteBase = 0, + renderWorld = m_RenderWorld, + atomicUpdateQueueCount = new UnsafeAtomicCounter32(&motionQueueCount), + transformUpdateInstanceQueue = transformUpdateInstanceQueue, + } + .RunParallel((m_RenderWorld.instanceCount + 63) / 64, 16); + + if (motionQueueCount > 0) + DispatchMotionUpdateCommand(motionQueueCount, transformUpdateInstanceQueue); + + transformUpdateInstanceQueue.Dispose(); + } + + private unsafe void UpdateInstanceTransformsData(bool initialize, + NativeArray instances, + JaggedSpan jaggedLocalToWorldMatrices, + JaggedSpan jaggedPrevLocalToWorldMatrices, + bool anyInstanceUseBlendProbes) + { + Assert.AreEqual(instances.Length, jaggedLocalToWorldMatrices.totalLength); + Assert.AreEqual(instances.Length, jaggedPrevLocalToWorldMatrices.totalLength); + Assert.IsTrue(jaggedLocalToWorldMatrices.HasSameLayout(jaggedPrevLocalToWorldMatrices)); + if (instances.Length == 0) + return; + + Profiler.BeginSample("AllocateBuffers"); + var transformUpdateInstanceQueue = new NativeArray(instances.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); + // When we reinitialize we have the current and the previous matrices per transform. + var transformUpdateDataQueue = new NativeArray(initialize ? instances.Length * 2 : instances.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); + var boundingSpheresUpdateDataQueue = new NativeArray(m_EnableBoundingSpheres ? instances.Length : 0, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); + + bool updateProbes = anyInstanceUseBlendProbes; + NativeArray probeInstanceQueue = default; + NativeArray compactTetrahedronCache = default; + NativeArray probeQueryPosition = default; + NativeArray probeUpdateDataQueue = default; + NativeArray probeOcclusionUpdateDataQueue = default; + + if (updateProbes) + { + probeInstanceQueue = new NativeArray(instances.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); + compactTetrahedronCache = new NativeArray(instances.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); + probeQueryPosition = new NativeArray(instances.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); + probeUpdateDataQueue = new NativeArray(instances.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); + probeOcclusionUpdateDataQueue = new NativeArray(instances.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); + } + + Profiler.EndSample(); + + var transformJobRanges = JaggedJobRange.FromSpanWithMaxBatchSize(jaggedLocalToWorldMatrices, TransformUpdateJob.MaxBatchSize, Allocator.TempJob); + + Profiler.BeginSample("UpdateTransformsAndProbes"); + + var transformQueueCount = 0; + int probesQueueCount = 0; + + JobHandle transformJobHandle = new TransformUpdateJob + { + jobRanges = transformJobRanges.AsArray(), + initialize = initialize, + enableBoundingSpheres = m_EnableBoundingSpheres, + instances = instances, + jaggedLocalToWorldMatrices = jaggedLocalToWorldMatrices, + jaggedPrevLocalToWorldMatrices = jaggedPrevLocalToWorldMatrices, + atomicTransformQueueCount = new UnsafeAtomicCounter32(&transformQueueCount), + renderWorld = m_RenderWorld, + transformUpdateInstanceQueue = transformUpdateInstanceQueue, + transformUpdateDataQueue = transformUpdateDataQueue, + boundingSpheresDataQueue = boundingSpheresUpdateDataQueue, + } + .Schedule(transformJobRanges); + + JobHandle probesJobHandle = default; + if (updateProbes) + { + probesJobHandle = new ProbesUpdateJob + { + instances = instances, + renderWorld = m_RenderWorld, + atomicProbesQueueCount = new UnsafeAtomicCounter32(&probesQueueCount), + probeInstanceQueue = probeInstanceQueue, + compactTetrahedronCache = compactTetrahedronCache, + probeQueryPosition = probeQueryPosition + } + .ScheduleParallel(instances.Length, ProbesUpdateJob.MaxBatchSize, transformJobHandle); + } + + JobHandle.CombineDependencies(transformJobHandle, probesJobHandle).Complete(); + + Profiler.EndSample(); + + if (probesQueueCount > 0) + { + InterpolateProbesAndUpdateTetrahedronCache(probesQueueCount, probeInstanceQueue, compactTetrahedronCache, + probeQueryPosition, probeUpdateDataQueue, probeOcclusionUpdateDataQueue); + + DispatchProbeUpdateCommand(probesQueueCount, probeInstanceQueue, probeUpdateDataQueue, probeOcclusionUpdateDataQueue); + } + + if (transformQueueCount > 0) + DispatchTransformUpdateCommand(initialize, transformQueueCount, transformUpdateInstanceQueue, transformUpdateDataQueue, boundingSpheresUpdateDataQueue); + + transformJobRanges.Dispose(); + transformUpdateInstanceQueue.Dispose(); + transformUpdateDataQueue.Dispose(); + boundingSpheresUpdateDataQueue.Dispose(); + + probeInstanceQueue.Dispose(); + compactTetrahedronCache.Dispose(); + probeQueryPosition.Dispose(); + probeUpdateDataQueue.Dispose(); + probeOcclusionUpdateDataQueue.Dispose(); + } + + private unsafe void UpdateInstanceProbesData(NativeArray instances) + { + var probeInstanceQueue = new NativeArray(instances.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); + var compactTetrahedronCache = new NativeArray(instances.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); + var probeQueryPosition = new NativeArray(instances.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); + var probeUpdateDataQueue = new NativeArray(instances.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); + var probeOcclusionUpdateDataQueue = new NativeArray(instances.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); + + int probesQueueCount = 0; + + new ProbesUpdateJob + { + instances = instances, + renderWorld = m_RenderWorld, + atomicProbesQueueCount = new UnsafeAtomicCounter32(&probesQueueCount), + probeInstanceQueue = probeInstanceQueue, + compactTetrahedronCache = compactTetrahedronCache, + probeQueryPosition = probeQueryPosition + } + .RunBatchParallel(instances.Length, ProbesUpdateJob.MaxBatchSize); + + if (probesQueueCount > 0) + { + InterpolateProbesAndUpdateTetrahedronCache(probesQueueCount, probeInstanceQueue, compactTetrahedronCache, + probeQueryPosition, probeUpdateDataQueue, probeOcclusionUpdateDataQueue); + + DispatchProbeUpdateCommand(probesQueueCount, probeInstanceQueue, probeUpdateDataQueue, probeOcclusionUpdateDataQueue); + } + + probeInstanceQueue.Dispose(); + compactTetrahedronCache.Dispose(); + probeQueryPosition.Dispose(); + probeUpdateDataQueue.Dispose(); + probeOcclusionUpdateDataQueue.Dispose(); + } + + // This represents the upper limit of all allocated instances. + // It is used to define the capacity of the instance data for this archetype in the GPU instance data buffer. + public int TrimGPUAllocatorLength(GPUArchetypeHandle archetype) + { + return m_InstanceAllocators.TrimGPUAllocatorLength(archetype); + } + + // This represents the actual number of instances that are currently allocated and active. + public int GetGPUArchetypeAliveInstancesCount(GPUArchetypeHandle archetype) + { + return m_InstanceAllocators.GetInstanceGPUHandlesAllocatedCount(archetype); + } + + public void EnsureGPUInstanceDataBufferLayout() + { + Assert.IsTrue(m_GPUResizingPolicy == GPUCapacityResizingPolicy.DoubleOnGrow || m_GPUResizingPolicy == GPUCapacityResizingPolicy.DoubleOnGrow_HalveOnQuarterShrink, + "GPU capacity resizing policy is not supported yet."); + + const float kGrowMult = 2.0f; + const float kShrinkThreshold = 0.25f; + + var archetypeCount = m_ArchetypeManager.GetRef().GetArchetypesCount(); + var bufferLayoutParams = new NativeArray<(GPUArchetypeHandle, int)>(archetypeCount, Allocator.Temp); + var needLayoutUpdate = false; + + for (int i = 0; i < archetypeCount; i++) + { + var archetypeHandle = GPUArchetypeHandle.Create((short)i); + if (m_InstanceDataBuffer.IsArchetypeAllocated(archetypeHandle)) + { + var archetypeIndex = m_InstanceDataBuffer.GetArchetypeIndex(archetypeHandle); + var maxCPUCount = TrimGPUAllocatorLength(archetypeHandle); + int maxGPUCount = m_InstanceDataBuffer.GetInstancesCount(archetypeIndex); + int newGPUCount = maxGPUCount; + + if (maxCPUCount > maxGPUCount) + newGPUCount = Mathf.CeilToInt(maxCPUCount * kGrowMult); + + if (m_GPUResizingPolicy == GPUCapacityResizingPolicy.DoubleOnGrow_HalveOnQuarterShrink) + { + if (maxCPUCount <= maxGPUCount * kShrinkThreshold) + newGPUCount = Mathf.CeilToInt(maxCPUCount * kGrowMult); + } + else + { + // Reset GPU capacity if no instances at all. + int aliveCPUDefaultCount = GetGPUArchetypeAliveInstancesCount(archetypeHandle); + if (aliveCPUDefaultCount == 0) + newGPUCount = 0; + } + + if (newGPUCount != maxGPUCount) + needLayoutUpdate = true; + + bufferLayoutParams[i] = (archetypeHandle, newGPUCount); + } + else + { + needLayoutUpdate = true; + + var maxCPUCount = TrimGPUAllocatorLength(archetypeHandle); + var newGPUCount = Mathf.CeilToInt(maxCPUCount * kGrowMult); + bufferLayoutParams[i] = (archetypeHandle, newGPUCount); + } + } + + if (needLayoutUpdate) + { + //@ Optimize this with a ctor taking param arrays + var newLayout = new GPUInstanceDataBufferLayout(bufferLayoutParams.Length, Allocator.Temp); + foreach (var param in bufferLayoutParams) + { + newLayout.Add(param.Item1, param.Item2); + } + + m_InstanceDataBuffer.SetGPULayout(m_CmdBuffer, ref m_ArchetypeManager.GetRef(), newLayout, submitCmdBuffer: true); + onGPUBufferLayoutChanged?.Invoke(); + } + } + + public void UpdateInstanceWindDataHistory(NativeArray gpuIndices) + { + if (gpuIndices.Length == 0) + return; + + DispatchWindDataCopyHistoryCommand(gpuIndices); + } + + public void AddCameras(NativeArray cameraIDs) + { + m_RenderWorld.AddCameras(cameraIDs); + } + + public void RemoveCameras(NativeArray cameraIDs) + { + m_RenderWorld.RemoveCameras(cameraIDs); + } + + public void AllocateNewInstances(JaggedSpan jaggedInstanceIDs, + NativeArray instances, + NativeArray archetypes, + int newInstancesCount) + { + using (new ProfilerMarker("AllocateNewInstances").Auto()) + { + HandleInstancesAllocations(InstanceAllocatorVariant.AllocOnly, jaggedInstanceIDs, instances, archetypes, newInstancesCount); + } + } + + public void ReallocateExistingGPUInstances(NativeArray instances, + NativeArray archetypes) + { + using (new ProfilerMarker("ReallocateExistingGPUInstances").Auto()) + { + var dummyRenderers = new JaggedSpan(0, Allocator.TempJob); + HandleInstancesAllocations(InstanceAllocatorVariant.GPUReallocOnly, dummyRenderers, instances, archetypes, 0); + dummyRenderers.Dispose(); + } + } + + public void AllocOrGPUReallocInstances(JaggedSpan jaggedInstanceIDs, + NativeArray instances, + NativeArray archetypes, + int newInstancesCount) + { + using (new ProfilerMarker("AllocOrGPUReallocInstances").Auto()) + { + HandleInstancesAllocations(InstanceAllocatorVariant.AllocOrGPURealloc, jaggedInstanceIDs, instances, archetypes, newInstancesCount); + } + } + + private unsafe void HandleInstancesAllocations(InstanceAllocatorVariant allocVariant, + JaggedSpan jaggedInstanceIDs, + NativeArray instances, + NativeArray archetypes, + int newInstancesCount) + { + Assert.IsTrue(allocVariant != InstanceAllocatorVariant.Null); + Assert.IsTrue(jaggedInstanceIDs.totalLength == 0 || jaggedInstanceIDs.totalLength == instances.Length); + Assert.IsTrue(archetypes.Length == 1 || archetypes.Length == instances.Length); + + m_RenderWorld.EnsureFreeCapacity(newInstancesCount); + + fixed (InstanceAllocators* instanceAllocators = &m_InstanceAllocators) + { + InstanceDataSystemBurst.AllocateInstances(allocVariant, + jaggedInstanceIDs, + archetypes, + instanceAllocators, + ref m_RenderWorld, + ref instances, + ref m_RendererToInstanceMap); + } + + EnsureGPUInstanceDataBufferLayout(); + } + + public unsafe void FreeInstances(NativeArray instances) + { + fixed (InstanceAllocators* instanceAllocators = &m_InstanceAllocators) + { + InstanceDataSystemBurst.FreeInstances(instances, + instanceAllocators, + ref m_RenderWorld, + ref m_RendererToInstanceMap); + } + + EnsureGPUInstanceDataBufferLayout(); + } + + public void UpdateInstanceData(NativeArray instances, in MeshRendererUpdateBatch updateBatch, NativeParallelHashMap lodGroupDataMap) + { + Profiler.BeginSample("UpdateInstanceData"); + Assert.IsTrue(instances.Length == updateBatch.TotalLength); + + var jobRanges = JaggedJobRange.FromSpanWithRelaxedBatchSize(updateBatch.instanceIDs, 128, Allocator.TempJob); + + new UpdateRendererInstancesJob + { + jobRanges = jobRanges.AsArray(), + updateBatch = updateBatch, + instances = instances, + lodGroupDataMap = lodGroupDataMap, + renderWorld = m_RenderWorld, + } + .RunParallel(jobRanges); + + jobRanges.Dispose(); + Profiler.EndSample(); + } + + public GPUInstanceUploadData CreateInstanceUploadData(GPUComponentHandle component, int capacity, Allocator allocator) + { + var components = new NativeArray(1, Allocator.Temp); + components[0] = component; + var upload = new GPUInstanceUploadData(ref m_ArchetypeManager.GetRef(), components, capacity, allocator); + components.Dispose(); + return upload; + } + + public GPUInstanceUploadData CreateInstanceUploadData(NativeArray components, int capacity, Allocator allocator) + { + return new GPUInstanceUploadData(ref m_ArchetypeManager.GetRef(), components, capacity, allocator); + } + + public GPUInstanceUploadData CreateInstanceUploadData(GPUComponentSet componentSet, int capacity, Allocator allocator) + { + return CreateInstanceUploadData(componentSet.GetComponents(Allocator.Temp).AsArray(), capacity, allocator); + } + + public void UploadDataToGPU(NativeArray instances, GraphicsBuffer uploadBuffer, in GPUInstanceUploadData uploadData) + { + var gpuIndices = new NativeArray(instances.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); + m_InstanceDataBuffer.QueryInstanceGPUIndices(m_RenderWorld, instances, gpuIndices); + UploadDataToGPU(gpuIndices, uploadBuffer, uploadData); + gpuIndices.Dispose(); + } + + public void UploadDataToGPU(NativeArray gpuIndices, GraphicsBuffer uploadBuffer, in GPUInstanceUploadData uploadData) + { + m_InstanceDataBuffer.UploadDataToGPU(m_CmdBuffer, uploadBuffer, uploadData, gpuIndices); + } + + public GPUInstanceDataBufferReadback ReadbackInstanceDataBuffer() where T : unmanaged + { + var readback = new GPUInstanceDataBufferReadback(); + bool success = readback.Load(m_CmdBuffer, m_InstanceDataBuffer); + Assert.IsTrue(success, "Failed to readback instance data buffer."); + return readback; + } + + public ref readonly GPUComponentDesc GetGPUComponentDesc(GPUComponentHandle component) + { + return ref m_ArchetypeManager.GetRef().GetComponentDesc(component); + } + + public void UpdateAllInstanceProbes() + { + var instances = m_RenderWorld.indexToHandle; + if (instances.Length == 0) + return; + + UpdateInstanceProbesData(instances); + } + + public void InitializeInstanceTransforms(NativeArray instances, + JaggedSpan jaggedLocalToWorldMatrices, + JaggedSpan jaggedPrevLocalToWorldMatrices, + bool anyInstanceUseBlendProbes) + { + UpdateInstanceTransformsData(true, instances, jaggedLocalToWorldMatrices, jaggedPrevLocalToWorldMatrices, anyInstanceUseBlendProbes); + } + + public void UpdateInstanceTransforms(NativeArray instances, JaggedSpan jaggedLocalToWorldMatrices, bool anyInstanceUseBlendProbes) + { + UpdateInstanceTransformsData(false, instances, jaggedLocalToWorldMatrices, jaggedLocalToWorldMatrices, anyInstanceUseBlendProbes); + } + + public void UpdateInstanceMotions() + { + if (m_RenderWorld.instanceCount == 0) + return; + + UpdateInstanceMotionsDataInternal(); + } + + public void QueryInstanceGPUIndices(NativeArray instances, NativeArray gpuIndices) + { + m_InstanceDataBuffer.QueryInstanceGPUIndices(m_RenderWorld, instances, gpuIndices); + } + + public JobHandle ScheduleQueryRendererInstancesJob(JaggedSpan jaggedRenderers, + NativeArray instances, + UnsafeAtomicCounter32 notFoundInstancesCount = default) + { + Assert.AreEqual(jaggedRenderers.totalLength, instances.Length); + + if (jaggedRenderers.totalLength == 0) + return default; + + var jobRanges = JaggedJobRange.FromSpanWithRelaxedBatchSize(jaggedRenderers, 128, Allocator.TempJob); + + var jobHandle = new QueryRendererInstancesJob() + { + jobRanges = jobRanges.AsArray(), + rendererToInstanceMap = m_RendererToInstanceMap, + jaggedRenderers = jaggedRenderers, + instances = instances, + atomicNonFoundInstancesCount = notFoundInstancesCount + } + .Schedule(jobRanges); + + return jobRanges.Dispose(jobHandle); + } + + public void QueryRendererInstances(NativeArray renderers, + NativeArray instances, + UnsafeAtomicCounter32 notFoundInstancesCount = default) + { + var jaggedRenderers = renderers.ToJaggedSpan(Allocator.TempJob); + QueryRendererInstances(jaggedRenderers, instances, notFoundInstancesCount); + jaggedRenderers.Dispose(); + } + + public void QueryRendererInstances(JaggedSpan jaggedRenderers, + NativeArray instances, + UnsafeAtomicCounter32 notFoundInstancesCount = default) + { + Profiler.BeginSample("QueryRendererInstances"); + ScheduleQueryRendererInstancesJob(jaggedRenderers, instances, notFoundInstancesCount).Complete(); + Profiler.EndSample(); + } + + public JobHandle ScheduleQueryRendererInstancesJob(NativeArray renderers, NativeArray instances) + { + var jaggedRenderers = renderers.ToJaggedSpan(Allocator.TempJob); + var jobHandle = ScheduleQueryRendererInstancesJob(jaggedRenderers, instances); + jaggedRenderers.Dispose(jobHandle); + return jobHandle; + } + + public JobHandle ScheduleQuerySortedMeshInstancesJob(NativeArray sortedMeshes, NativeList instances) + { + if (sortedMeshes.Length == 0) + return default; + + instances.Capacity = m_RenderWorld.instanceCount; + + return new QuerySortedMeshInstancesJob() + { + renderWorld = m_RenderWorld, + sortedMeshes = sortedMeshes, + instances = instances + } + .ScheduleBatch(m_RenderWorld.instanceCount, QuerySortedMeshInstancesJob.MaxBatchSize); + } + + public bool AreAllAllocatedInstancesValid() + { + for (int i = 0; i < m_RenderWorld.instanceCount; ++i) + { + if (!m_RenderWorld.IsValidInstance(m_RenderWorld.indexToHandle[i])) + return false; + } + + return true; + } + + public unsafe void GetVisibleTreeInstances(in ParallelBitArray compactedVisibilityMasks, in ParallelBitArray processedBits, NativeList visibeTreeRenderers, + NativeList visibeTreeInstances, bool becomeVisibleOnly, out int becomeVisibeTreeInstancesCount) + { + Assert.AreEqual(visibeTreeRenderers.Length, 0); + Assert.AreEqual(visibeTreeInstances.Length, 0); + + becomeVisibeTreeInstancesCount = 0; + + if (totalTreeCount == 0) + return; + + visibeTreeRenderers.ResizeUninitialized(totalTreeCount); + visibeTreeInstances.ResizeUninitialized(totalTreeCount); + + int visibleTreeInstancesCount = 0; + + new GetVisibleNonProcessedTreeInstancesJob + { + becomeVisible = true, + renderWorld = m_RenderWorld, + compactedVisibilityMasks = compactedVisibilityMasks, + processedBits = processedBits, + renderers = visibeTreeRenderers.AsArray(), + instances = visibeTreeInstances.AsArray(), + atomicTreeInstancesCount = new UnsafeAtomicCounter32(&visibleTreeInstancesCount) + } + .RunBatchParallel(m_RenderWorld.instanceCount, GetVisibleNonProcessedTreeInstancesJob.MaxBatchSize); + + becomeVisibeTreeInstancesCount = visibleTreeInstancesCount; + + if (!becomeVisibleOnly) + { + new GetVisibleNonProcessedTreeInstancesJob + { + becomeVisible = false, + renderWorld = m_RenderWorld, + compactedVisibilityMasks = compactedVisibilityMasks, + processedBits = processedBits, + renderers = visibeTreeRenderers.AsArray(), + instances = visibeTreeInstances.AsArray(), + atomicTreeInstancesCount = new UnsafeAtomicCounter32(&visibleTreeInstancesCount) + } + .RunBatchParallel(m_RenderWorld.instanceCount, GetVisibleNonProcessedTreeInstancesJob.MaxBatchSize); + } + + Assert.IsTrue(becomeVisibeTreeInstancesCount <= visibleTreeInstancesCount); + Assert.IsTrue(visibleTreeInstancesCount <= totalTreeCount); + + visibeTreeRenderers.ResizeUninitialized(visibleTreeInstancesCount); + visibeTreeInstances.ResizeUninitialized(visibleTreeInstancesCount); + } + + public void UpdatePerFrameInstanceVisibility(in ParallelBitArray compactedVisibilityMasks) + { + Assert.AreEqual(m_RenderWorld.handleCount, compactedVisibilityMasks.Length); + + new UpdateCompactedInstanceVisibilityJob + { + renderWorld = m_RenderWorld, + compactedVisibilityMasks = compactedVisibilityMasks + } + .RunBatchParallel(m_RenderWorld.instanceCount, UpdateCompactedInstanceVisibilityJob.MaxBatchSize); + } + + public void ValidateTotalTreeCount() + { + // Disable "Unreachable code detected" warning +#pragma warning disable CS0162 + if (!GPUResidentDrawer.EnableDeepValidation) + return; + + Profiler.BeginSample("DeepValidate.TotalTreeCount"); + + int totalTreeCountTruth = InstanceDataSystemBurst.ComputeTotalTreeCount(m_RenderWorld.rendererSettings); + + if (totalTreeCount != totalTreeCountTruth) + Debug.LogError($"Cached total tree count ({totalTreeCount}) does not match total tree count truth ({totalTreeCountTruth})."); + + Profiler.EndSample(); + +#pragma warning restore CS0162 + } + + private static class InstanceTransformUpdateIDs + { + public static readonly int _TransformUpdateQueueCount = Shader.PropertyToID("_TransformUpdateQueueCount"); + public static readonly int _TransformUpdateOutputL2WVec4Offset = Shader.PropertyToID("_TransformUpdateOutputL2WVec4Offset"); + public static readonly int _TransformUpdateOutputW2LVec4Offset = Shader.PropertyToID("_TransformUpdateOutputW2LVec4Offset"); + public static readonly int _TransformUpdateOutputPrevL2WVec4Offset = Shader.PropertyToID("_TransformUpdateOutputPrevL2WVec4Offset"); + public static readonly int _TransformUpdateOutputPrevW2LVec4Offset = Shader.PropertyToID("_TransformUpdateOutputPrevW2LVec4Offset"); + public static readonly int _BoundingSphereOutputVec4Offset = Shader.PropertyToID("_BoundingSphereOutputVec4Offset"); + public static readonly int _TransformUpdateDataQueue = Shader.PropertyToID("_TransformUpdateDataQueue"); + public static readonly int _TransformUpdateIndexQueue = Shader.PropertyToID("_TransformUpdateIndexQueue"); + public static readonly int _BoundingSphereDataQueue = Shader.PropertyToID("_BoundingSphereDataQueue"); + public static readonly int _OutputTransformBuffer = Shader.PropertyToID("_OutputTransformBuffer"); + + public static readonly int _ProbeUpdateQueueCount = Shader.PropertyToID("_ProbeUpdateQueueCount"); + public static readonly int _SHUpdateVec4Offset = Shader.PropertyToID("_SHUpdateVec4Offset"); + public static readonly int _ProbeUpdateDataQueue = Shader.PropertyToID("_ProbeUpdateDataQueue"); + public static readonly int _ProbeOcclusionUpdateDataQueue = Shader.PropertyToID("_ProbeOcclusionUpdateDataQueue"); + public static readonly int _ProbeUpdateIndexQueue = Shader.PropertyToID("_ProbeUpdateIndexQueue"); + public static readonly int _OutputProbeBuffer = Shader.PropertyToID("_OutputProbeBuffer"); + } + + private static class InstanceWindDataUpdateIDs + { + public static readonly int _WindDataQueueCount = Shader.PropertyToID("_WindDataQueueCount"); + public static readonly int _WindDataUpdateIndexQueue = Shader.PropertyToID("_WindDataUpdateIndexQueue"); + public static readonly int _WindDataBuffer = Shader.PropertyToID("_WindDataBuffer"); + public static readonly int _WindParamAddressArray = Shader.PropertyToID("_WindParamAddressArray"); + public static readonly int _WindHistoryParamAddressArray = Shader.PropertyToID("_WindHistoryParamAddressArray"); + } + + public enum GPUCapacityResizingPolicy + { + DoubleOnGrow, + DoubleOnGrow_HalveOnQuarterShrink + } + } +} diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceDataSystem.cs.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/InstanceDataSystem.cs.meta similarity index 100% rename from Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceDataSystem.cs.meta rename to Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/InstanceDataSystem.cs.meta diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/InstanceDataSystemBurst.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/InstanceDataSystemBurst.cs new file mode 100644 index 00000000000..7ce40282239 --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/InstanceDataSystemBurst.cs @@ -0,0 +1,197 @@ +using System.Runtime.CompilerServices; +using Unity.Collections; +using Unity.Burst; +using UnityEngine.Assertions; + +namespace UnityEngine.Rendering +{ + internal enum InstanceAllocatorVariant + { + Null, + AllocOnly, + GPUReallocOnly, + AllocOrGPURealloc + } + + [BurstCompile] + internal static class InstanceDataSystemBurst + { + + // Length == 1 means the archetype is shared over all the instances + private static GPUArchetypeHandle FetchArchetype(in NativeArray archetypes, int index) => archetypes.Length == 1 ? archetypes[0] : archetypes[index]; + + private static unsafe void AllocOnlyIteration(NativeArray instanceIDSection, + int absoluteIndex, + int localIndex, + in NativeArray archetypes, + InstanceAllocators* instanceAllocators, + ref RenderWorld renderWorld, + ref NativeArray instances, + ref NativeParallelHashMap rendererToInstanceMap) + { + EntityId instanceID = instanceIDSection[localIndex]; + GPUArchetypeHandle archetype = FetchArchetype(archetypes, absoluteIndex); + InstanceHandle newInstance = instanceAllocators->AllocateInstance(); + InstanceGPUHandle newGPUHandle = instanceAllocators->AllocateInstanceGPUHandle(archetype); + int instanceIndex = renderWorld.AddInstanceNoGrow(newInstance); + + renderWorld.gpuHandles.ElementAtRW(instanceIndex) = newGPUHandle; + rendererToInstanceMap.Add(instanceID, newInstance); + instances[absoluteIndex] = newInstance; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static unsafe void AllocOnly(in JaggedSpan jaggedInstanceIDs, + in NativeArray archetypes, + InstanceAllocators* instanceAllocators, + ref RenderWorld renderWorld, + ref NativeArray instances, + ref NativeParallelHashMap rendererToInstanceMap) + { + int absoluteIndex = 0; + + for (int sectionIndex = 0; sectionIndex < jaggedInstanceIDs.sectionCount; sectionIndex++) + { + NativeArray instanceIDs = jaggedInstanceIDs[sectionIndex]; + + for (int localIndex = 0; localIndex < instanceIDs.Length; localIndex++, absoluteIndex++) + { + AllocOnlyIteration(instanceIDs, absoluteIndex, localIndex, archetypes, instanceAllocators, ref renderWorld, ref instances, ref rendererToInstanceMap); + } + } + } + + private static unsafe void GPUReallocOnlyIteration(InstanceHandle instance, + GPUArchetypeHandle archetype, + InstanceAllocators* instanceAllocators, + ref RenderWorld renderWorld) + { + int instanceIndex = renderWorld.HandleToIndex(instance); + InstanceGPUHandle gpuHandle = renderWorld.gpuHandles[instanceIndex]; + Assert.IsTrue(gpuHandle.isValid); + + if (archetype.Equals(gpuHandle.archetype)) + return; + + instanceAllocators->FreeInstanceGPUHandle(gpuHandle); + renderWorld.gpuHandles.ElementAtRW(instanceIndex) = instanceAllocators->AllocateInstanceGPUHandle(archetype); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static unsafe void GPUReallocOnly(in NativeArray archetypes, + InstanceAllocators* instanceAllocators, + ref RenderWorld renderWorld, + ref NativeArray instances) + { + for (int i = 0; i < instances.Length; i++) + { + InstanceHandle instance = instances[i]; + Assert.IsTrue(instance.isValid, "Invalid Instance"); + if (!instance.isValid) + continue; + + GPUArchetypeHandle archetype = FetchArchetype(archetypes, i); + GPUReallocOnlyIteration(instance, archetype, instanceAllocators, ref renderWorld); + } + } + + + [MethodImpl(MethodImplOptions.NoInlining)] + private static unsafe void AllocOrGPURealloc(in JaggedSpan jaggedInstanceIDs, + in NativeArray archetypes, + InstanceAllocators* instanceAllocators, + ref RenderWorld renderWorld, + ref NativeArray instances, + ref NativeParallelHashMap rendererToInstanceMap) + { + int absoluteIndex = 0; + + for (int sectionIndex = 0; sectionIndex < jaggedInstanceIDs.sectionCount; sectionIndex++) + { + NativeArray instanceIDs = jaggedInstanceIDs[sectionIndex]; + + for (int localIndex = 0; localIndex < instanceIDs.Length; localIndex++, absoluteIndex++) + { + GPUArchetypeHandle archetype = FetchArchetype(archetypes, absoluteIndex); + InstanceHandle instance = instances[absoluteIndex]; + + if (instance.isValid) + { + GPUReallocOnlyIteration(instance, archetype, instanceAllocators, ref renderWorld); + } + else + { + AllocOnlyIteration(instanceIDs, absoluteIndex, localIndex, archetypes, instanceAllocators, ref renderWorld, ref instances, ref rendererToInstanceMap); + } + } + } + } + + [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] + // Without using InstanceAllocators as a pointer there is a crash inside native containers allocator. Same everywhere. + public static unsafe void AllocateInstances(InstanceAllocatorVariant allocVariant, + in JaggedSpan jaggedInstanceIDs, + in NativeArray archetypes, + InstanceAllocators* instanceAllocators, + ref RenderWorld renderWorld, + ref NativeArray instances, + ref NativeParallelHashMap rendererToInstanceMap) + { + switch (allocVariant) + { + case InstanceAllocatorVariant.AllocOnly: + AllocOnly(jaggedInstanceIDs, archetypes, instanceAllocators, ref renderWorld, ref instances, ref rendererToInstanceMap); + break; + + case InstanceAllocatorVariant.GPUReallocOnly: + GPUReallocOnly(archetypes, instanceAllocators, ref renderWorld, ref instances); + break; + + case InstanceAllocatorVariant.AllocOrGPURealloc: + AllocOrGPURealloc(jaggedInstanceIDs, archetypes, instanceAllocators, ref renderWorld, ref instances, ref rendererToInstanceMap); + break; + + default: + Assert.IsTrue(false, "Invalid InstanceAllocatorVariant"); + break; + } + } + + [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] + public static unsafe void FreeInstances(in NativeArray instances, + InstanceAllocators* instanceAllocators, + ref RenderWorld renderWorld, + ref NativeParallelHashMap rendererToInstanceMap) + { + foreach (var instance in instances) + { + if (!renderWorld.IsValidInstance(instance)) + continue; + + int instanceIndex = renderWorld.HandleToIndex(instance); + EntityId renderer = renderWorld.instanceIDs[instanceIndex]; + InstanceGPUHandle gpuHandle = renderWorld.gpuHandles[instanceIndex]; + + Assert.IsTrue(rendererToInstanceMap[renderer].Equals(instance)); + rendererToInstanceMap.Remove(renderer); + renderWorld.RemoveInstance(instance); + + instanceAllocators->FreeInstance(instance); + instanceAllocators->FreeInstanceGPUHandle(gpuHandle); + } + } + + [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] + public static int ComputeTotalTreeCount(in NativeArray rendererSettings) + { + int totalTreeCount = 0; + + foreach (var settings in rendererSettings) + { + totalTreeCount += settings.HasTree ? 1 : 0; + } + + return totalTreeCount; + } + } +} diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/InstanceDataSystemBurst.cs.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/InstanceDataSystemBurst.cs.meta new file mode 100644 index 00000000000..b65a7036b7d --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/InstanceDataSystemBurst.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: c449e4f867e4b2b49bf7ff68b4ea66dd \ No newline at end of file diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceTransformUpdateDefs.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/InstanceTransformUpdateDefs.cs similarity index 100% rename from Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceTransformUpdateDefs.cs rename to Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/InstanceTransformUpdateDefs.cs diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceTransformUpdateDefs.cs.hlsl b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/InstanceTransformUpdateDefs.cs.hlsl similarity index 100% rename from Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceTransformUpdateDefs.cs.hlsl rename to Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/InstanceTransformUpdateDefs.cs.hlsl diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceTransformUpdateDefs.cs.hlsl.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/InstanceTransformUpdateDefs.cs.hlsl.meta similarity index 100% rename from Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceTransformUpdateDefs.cs.hlsl.meta rename to Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/InstanceTransformUpdateDefs.cs.hlsl.meta diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceTransformUpdateDefs.cs.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/InstanceTransformUpdateDefs.cs.meta similarity index 100% rename from Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceTransformUpdateDefs.cs.meta rename to Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/InstanceTransformUpdateDefs.cs.meta diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceWindDataUpdateDefs.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/InstanceWindDataUpdateDefs.cs similarity index 77% rename from Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceWindDataUpdateDefs.cs rename to Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/InstanceWindDataUpdateDefs.cs index c932829d329..e8ca425fbe6 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceWindDataUpdateDefs.cs +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/InstanceWindDataUpdateDefs.cs @@ -1,5 +1,4 @@ -using Unity.Mathematics; -using System.Runtime.InteropServices; +using System; namespace UnityEngine.Rendering { diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceWindDataUpdateDefs.cs.hlsl b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/InstanceWindDataUpdateDefs.cs.hlsl similarity index 100% rename from Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceWindDataUpdateDefs.cs.hlsl rename to Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/InstanceWindDataUpdateDefs.cs.hlsl diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceWindDataUpdateDefs.cs.hlsl.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/InstanceWindDataUpdateDefs.cs.hlsl.meta similarity index 100% rename from Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceWindDataUpdateDefs.cs.hlsl.meta rename to Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/InstanceWindDataUpdateDefs.cs.hlsl.meta diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceWindDataUpdateDefs.cs.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/InstanceWindDataUpdateDefs.cs.meta similarity index 100% rename from Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceWindDataUpdateDefs.cs.meta rename to Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/InstanceWindDataUpdateDefs.cs.meta diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/LODGroupDataSystem.Jobs.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/LODGroupDataSystem.Jobs.cs new file mode 100644 index 00000000000..ff7f48a1988 --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/LODGroupDataSystem.Jobs.cs @@ -0,0 +1,179 @@ +using System; +using UnityEngine.Assertions; +using Unity.Collections; +using Unity.Jobs; +using Unity.Collections.LowLevel.Unsafe; +using Unity.Burst; +using Unity.Mathematics; + +namespace UnityEngine.Rendering +{ + [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] + internal struct UpdateLODGroupTransformJob : IJobParallelFor + { + [ReadOnly] public NativeArray jobRanges; + [ReadOnly] public NativeParallelHashMap lodGroupDataHash; + [ReadOnly] public JaggedSpan jaggedLODGroups; + [ReadOnly] public JaggedSpan jaggedWorldSpaceSizes; + [ReadOnly] public JaggedSpan jaggedWorldSpaceReferencePoints; + [ReadOnly] public bool requiresGPUUpload; + [ReadOnly] public bool supportDitheringCrossFade; + + [NativeDisableContainerSafetyRestriction, NoAlias, ReadOnly] public NativeList lodGroupDatas; + [NativeDisableContainerSafetyRestriction, NoAlias, WriteOnly] public NativeList lodGroupCullingDatas; + + public unsafe void Execute(int jobIndex) + { + var jobRange = jobRanges[jobIndex]; + + NativeArray lodGroupSection = jaggedLODGroups[jobRange.sectionIndex]; + NativeArray worldSpaceSizesSection = jaggedWorldSpaceSizes[jobRange.sectionIndex]; + NativeArray worldSpaceReferencePointSection = jaggedWorldSpaceReferencePoints[jobRange.sectionIndex]; + + for (int indexInRange = 0; indexInRange < jobRange.length; indexInRange++) + { + int localIndex = jobRange.localStart + indexInRange; + EntityId lodGroup = lodGroupSection[localIndex]; + + if (lodGroupDataHash.TryGetValue(lodGroup, out var lodGroupInstance)) + { + float worldSpaceSize = worldSpaceSizesSection[localIndex]; + + ref LODGroupData lodGroupData = ref lodGroupDatas.ElementAt(lodGroupInstance.index); + ref LODGroupCullingData lodGroupTransformResult = ref lodGroupCullingDatas.ElementAt(lodGroupInstance.index); + lodGroupTransformResult.worldSpaceSize = worldSpaceSize; + lodGroupTransformResult.worldSpaceReferencePoint = worldSpaceReferencePointSection[localIndex]; + + for (int i = 0; i < lodGroupData.lodCount; ++i) + { + float lodHeight = lodGroupData.screenRelativeTransitionHeights[i]; + + float lodDist = LODRenderingUtils.CalculateLODDistance(lodHeight, worldSpaceSize); + lodGroupTransformResult.sqrDistances[i] = lodDist * lodDist; + + if (supportDitheringCrossFade && !lodGroupTransformResult.percentageFlags[i]) + { + float prevLODHeight = i != 0 ? lodGroupData.screenRelativeTransitionHeights[i - 1] : 1.0f; + float transitionHeight = lodHeight + lodGroupData.fadeTransitionWidth[i] * (prevLODHeight - lodHeight); + float transitionDistance = lodDist - LODRenderingUtils.CalculateLODDistance(transitionHeight, worldSpaceSize); + transitionDistance = Mathf.Max(0.0f, transitionDistance); + lodGroupTransformResult.transitionDistances[i] = transitionDistance; + } + else + { + lodGroupTransformResult.transitionDistances[i] = 0f; + } + } + } + } + } + } + + [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] + internal unsafe struct UpdateLODGroupDataJob : IJobParallelFor + { + [ReadOnly] public NativeArray jobRanges; + [ReadOnly] public NativeArray lodGroupInstances; + [ReadOnly] public LODGroupUpdateBatch updateBatch; + [ReadOnly] public bool supportDitheringCrossFade; + + public NativeArray lodGroupsData; + public NativeArray lodGroupsCullingData; + + [NativeDisableUnsafePtrRestriction] public UnsafeAtomicCounter32 rendererCount; + + public void Execute(int jobIndex) + { + JaggedJobRange jobRange = jobRanges[jobIndex]; + + NativeArray worldReferencePointSection = updateBatch.worldSpaceReferencePoints[jobRange.sectionIndex]; + NativeArray worldSpaceSizeSection = updateBatch.worldSpaceSizes[jobRange.sectionIndex]; + NativeArray lodGroupSettingsSection = updateBatch.lodGroupSettings[jobRange.sectionIndex]; + var hasForceLOD = updateBatch.HasAnyComponent(LODGroupComponentMask.ForceLOD); + NativeArray forceLODMaskSection = hasForceLOD ? updateBatch.forceLODMask[jobRange.sectionIndex] : default; + NativeArray lodBufferSection = updateBatch.lodBuffers[jobRange.sectionIndex]; + + + for (int indexInRange = 0; indexInRange < jobRange.length; indexInRange++) + { + int localIndex = jobRange.localStart + indexInRange; + int absoluteIndex = jobRange.absoluteStart + indexInRange; + + GPUInstanceIndex lodGroupInstance = lodGroupInstances[absoluteIndex]; + float3 worldReferencePoint = worldReferencePointSection[localIndex]; + float worldSpaceSize = worldSpaceSizeSection[localIndex]; + InternalLODGroupSettings lodGroupSettings = lodGroupSettingsSection[localIndex]; + byte forceLODMask = hasForceLOD ? forceLODMaskSection[localIndex] : (byte)0; + ref readonly EmbeddedLODBuffer lodBuffer = ref lodBufferSection.ElementAt(localIndex); + + int lodCount = lodBuffer.Length; + LODFadeMode fadeMode = lodGroupSettings.fadeMode; + bool useDitheringCrossFade = fadeMode != LODFadeMode.None && supportDitheringCrossFade; + bool useSpeedTreeCrossFade = fadeMode == LODFadeMode.SpeedTree; + + int totalRendererCount = 0; + for (int i = 0; i < lodCount; i++) + { + int lodRendererCount = lodBuffer.GetRendererCount(i); + totalRendererCount += lodRendererCount; + } + + ref LODGroupData lodGroupData = ref lodGroupsData.ElementAtRW(lodGroupInstance.index); + ref LODGroupCullingData lodGroupCullingData = ref lodGroupsCullingData.ElementAtRW(lodGroupInstance.index); + + lodGroupData.valid = true; + lodGroupData.lodCount = lodCount; + lodGroupData.rendererCount = useDitheringCrossFade ? totalRendererCount : 0; + lodGroupCullingData.worldSpaceSize = worldSpaceSize; + lodGroupCullingData.worldSpaceReferencePoint = worldReferencePoint; + lodGroupCullingData.forceLODMask = forceLODMask; + lodGroupCullingData.lodCount = lodCount; + + rendererCount.Add(lodGroupData.rendererCount); + + int crossFadeLODBegin = 0; + + if (useSpeedTreeCrossFade) + { + int lastLODIndex = lodCount - 1; + bool hasBillboardLOD = lodCount > 0 && lodBuffer.GetRendererCount(lastLODIndex) == 1 && lodGroupSettings.lastLODIsBillboard; + + if (lodCount == 0) + crossFadeLODBegin = 0; + else if (hasBillboardLOD) + crossFadeLODBegin = math.max(lodCount, 2) - 2; + else + crossFadeLODBegin = lodCount - 1; + } + + for (int i = 0; i < lodCount; ++i) + { + float lodHeight = lodBuffer.GetScreenRelativeTransitionHeight(i); + float lodDist = LODRenderingUtils.CalculateLODDistance(lodHeight, worldSpaceSize); + + lodGroupData.screenRelativeTransitionHeights[i] = lodHeight; + lodGroupData.fadeTransitionWidth[i] = 0.0f; + lodGroupCullingData.sqrDistances[i] = lodDist * lodDist; + lodGroupCullingData.percentageFlags[i] = false; + lodGroupCullingData.transitionDistances[i] = 0.0f; + + if (useSpeedTreeCrossFade && i < crossFadeLODBegin) + { + lodGroupCullingData.percentageFlags[i] = true; + } + else if (useDitheringCrossFade && i >= crossFadeLODBegin) + { + float fadeTransitionWidth = lodBuffer.GetFadeTransitionWidth(i); + float prevLODHeight = i != 0 ? lodBuffer.GetScreenRelativeTransitionHeight(i - 1) : 1.0f; + float transitionHeight = lodHeight + fadeTransitionWidth * (prevLODHeight - lodHeight); + float transitionDistance = lodDist - LODRenderingUtils.CalculateLODDistance(transitionHeight, worldSpaceSize); + transitionDistance = Mathf.Max(0.0f, transitionDistance); + + lodGroupData.fadeTransitionWidth[i] = fadeTransitionWidth; + lodGroupCullingData.transitionDistances[i] = transitionDistance; + } + } + } + } + } +} diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/GPUInstanceDataUploader.cs.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/LODGroupDataSystem.Jobs.cs.meta similarity index 83% rename from Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/GPUInstanceDataUploader.cs.meta rename to Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/LODGroupDataSystem.Jobs.cs.meta index e432ca7a171..009ff41a345 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/GPUInstanceDataUploader.cs.meta +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/LODGroupDataSystem.Jobs.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 200079115ab70b94dafaec0a48ee2455 +guid: f3545fc7afc41164dbfc733522d08386 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/LODGroupDataSystem.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/LODGroupDataSystem.cs new file mode 100644 index 00000000000..ffa84f2409b --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/LODGroupDataSystem.cs @@ -0,0 +1,139 @@ +using System; +using UnityEngine.Assertions; +using Unity.Collections; +using Unity.Jobs; +using Unity.Collections.LowLevel.Unsafe; +using Unity.Mathematics; + +namespace UnityEngine.Rendering +{ + internal unsafe struct LODGroupData + { + public const int k_MaxLODLevelsCount = 8; + + public bool valid; + public int lodCount; + public int rendererCount; + public fixed float screenRelativeTransitionHeights[k_MaxLODLevelsCount]; + public fixed float fadeTransitionWidth[k_MaxLODLevelsCount]; + } + + internal unsafe struct LODGroupCullingData + { + public float3 worldSpaceReferencePoint; + public int lodCount; + public fixed float sqrDistances[LODGroupData.k_MaxLODLevelsCount]; // we use square distance to get rid of a sqrt in gpu culling.. + public fixed float transitionDistances[LODGroupData.k_MaxLODLevelsCount]; // todo - make this a separate data struct (CPUOnly, as we do not support dithering on GPU..) + public float worldSpaceSize;// SpeedTree crossfade. + public fixed bool percentageFlags[LODGroupData.k_MaxLODLevelsCount];// SpeedTree crossfade. + public byte forceLODMask; + } + + //@ Merge this class into InstanceDataSystem there is no need to have two separate classes for this. + internal class LODGroupDataSystem : IDisposable + { + private NativeList m_LODGroupData; + private NativeParallelHashMap m_LODGroupDataHash; + private NativeList m_LODGroupCullingData; + private NativeList m_FreeLODGroupDataHandles; + + private int m_CrossfadedRendererCount; + private bool m_SupportDitheringCrossFade; + + public NativeParallelHashMap lodGroupDataHash => m_LODGroupDataHash; + public NativeList lodGroupCullingData => m_LODGroupCullingData; + public int crossfadedRendererCount => m_CrossfadedRendererCount; + public int activeLodGroupCount => m_LODGroupData.Length; + + public LODGroupDataSystem(bool supportDitheringCrossFade) + { + m_LODGroupData = new NativeList(Allocator.Persistent); + m_LODGroupDataHash = new NativeParallelHashMap(64, Allocator.Persistent); + + m_LODGroupCullingData = new NativeList(Allocator.Persistent); + m_FreeLODGroupDataHandles = new NativeList(Allocator.Persistent); + + m_SupportDitheringCrossFade = supportDitheringCrossFade; + } + + public void Dispose() + { + m_LODGroupData.Dispose(); + m_LODGroupDataHash.Dispose(); + + m_LODGroupCullingData.Dispose(); + m_FreeLODGroupDataHandles.Dispose(); + } + + public NativeArray GetOrAllocateInstances(in LODGroupUpdateBatch updateBatch, Allocator allocator) + { + var instances = new NativeArray(updateBatch.TotalLength, allocator, NativeArrayOptions.UninitializedMemory); + + int previousRendererCount = LODGroupDataSystemBurst.GetOrAllocateLODGroupDataInstances(updateBatch.instanceIDs, + ref m_LODGroupData, + ref m_LODGroupCullingData, + ref m_LODGroupDataHash, + ref m_FreeLODGroupDataHandles, + ref instances); + + m_CrossfadedRendererCount -= previousRendererCount; + Assert.IsTrue(m_CrossfadedRendererCount >= 0); + + return instances; + } + + public unsafe void UpdateLODGroupData(in LODGroupUpdateBatch updateBatch, NativeArray instances) + { + var jobRanges = JaggedJobRange.FromSpanWithRelaxedBatchSize(updateBatch.instanceIDs, 256, Allocator.TempJob); + + int rendererCount = 0; + + new UpdateLODGroupDataJob + { + jobRanges = jobRanges.AsArray(), + lodGroupInstances = instances, + updateBatch = updateBatch, + supportDitheringCrossFade = m_SupportDitheringCrossFade, + lodGroupsData = m_LODGroupData.AsArray(), + lodGroupsCullingData = m_LODGroupCullingData.AsArray(), + rendererCount = new UnsafeAtomicCounter32(&rendererCount), + } + .RunParallel(jobRanges); + + m_CrossfadedRendererCount += rendererCount; + + jobRanges.Dispose(); + } + + public unsafe void UpdateLODGroupTransforms(in LODGroupUpdateBatch updateBatch) + { + var jobRanges = JaggedJobRange.FromSpanWithRelaxedBatchSize(updateBatch.instanceIDs, 256, Allocator.TempJob); + + new UpdateLODGroupTransformJob() + { + jobRanges = jobRanges.AsArray(), + lodGroupDataHash = m_LODGroupDataHash, + jaggedLODGroups = updateBatch.instanceIDs, + jaggedWorldSpaceReferencePoints = updateBatch.worldSpaceReferencePoints, + jaggedWorldSpaceSizes = updateBatch.worldSpaceSizes, + lodGroupDatas = m_LODGroupData, + lodGroupCullingDatas = m_LODGroupCullingData, + supportDitheringCrossFade = m_SupportDitheringCrossFade, + } + .RunParallel(jobRanges); + + jobRanges.Dispose(); + } + + public void FreeLODGroups(NativeArray destroyedLODGroupsID) + { + if (destroyedLODGroupsID.Length == 0) + return; + + int removedRendererCount = LODGroupDataSystemBurst.FreeLODGroupData(destroyedLODGroupsID, ref m_LODGroupData, ref m_LODGroupDataHash, ref m_FreeLODGroupDataHandles); + + m_CrossfadedRendererCount -= removedRendererCount; + Assert.IsTrue(m_CrossfadedRendererCount >= 0); + } + } +} diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/LODGroupDataPool.cs.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/LODGroupDataSystem.cs.meta similarity index 100% rename from Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/LODGroupDataPool.cs.meta rename to Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/LODGroupDataSystem.cs.meta diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/LODGroupDataSystemBurst.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/LODGroupDataSystemBurst.cs new file mode 100644 index 00000000000..891b141981b --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/LODGroupDataSystemBurst.cs @@ -0,0 +1,87 @@ +using System.Runtime.CompilerServices; +using Unity.Collections; +using Unity.Burst; +using Unity.Collections.LowLevel.Unsafe; +using UnityEngine.Assertions; + +namespace UnityEngine.Rendering +{ + [BurstCompile] + internal static class LODGroupDataSystemBurst + { + [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] + public static int GetOrAllocateLODGroupDataInstances(in JaggedSpan jaggedLODGroups, + ref NativeList lodGroupsData, + ref NativeList lodGroupCullingData, + ref NativeParallelHashMap lodGroupDataHash, + ref NativeList freeLODGroupDataHandles, + ref NativeArray lodGroupInstances) + { + int freeHandlesCount = freeLODGroupDataHandles.Length; + int lodDataLength = lodGroupsData.Length; + + int absoluteIndex = 0; + int previousRendererCount = 0; + + for (int sectionIndex = 0; sectionIndex < jaggedLODGroups.sectionCount; ++sectionIndex) + { + NativeArray lodGroups = jaggedLODGroups[sectionIndex]; + + for (int localIndex = 0; localIndex < lodGroups.Length; localIndex++, ++absoluteIndex) + { + EntityId lodGroup = lodGroups[localIndex]; + + if (!lodGroupDataHash.TryGetValue(lodGroup, out var lodGroupInstance)) + { + if (freeHandlesCount == 0) + lodGroupInstance = GPUInstanceIndex.Create(lodDataLength++); + else + lodGroupInstance = freeLODGroupDataHandles[--freeHandlesCount]; + + lodGroupDataHash.TryAdd(lodGroup, lodGroupInstance); + } + else + { + previousRendererCount += lodGroupsData.ElementAt(lodGroupInstance.index).rendererCount; + } + + lodGroupInstances[absoluteIndex] = lodGroupInstance; + } + } + + freeLODGroupDataHandles.ResizeUninitialized(freeHandlesCount); + lodGroupsData.ResizeUninitialized(lodDataLength); + lodGroupCullingData.ResizeUninitialized(lodDataLength); + + return previousRendererCount; + } + + [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] + public static int FreeLODGroupData(in NativeArray destroyedLODGroups, + ref NativeList lodGroupsData, + ref NativeParallelHashMap lodGroupDataHash, + ref NativeList freeLODGroupDataHandles) + { + int removedRendererCount = 0; + + foreach (EntityId lodGroup in destroyedLODGroups) + { + if (lodGroupDataHash.TryGetValue(lodGroup, out var lodGroupInstance)) + { + Assert.IsTrue(lodGroupInstance.valid); + + lodGroupDataHash.Remove(lodGroup); + freeLODGroupDataHandles.Add(lodGroupInstance); + + ref LODGroupData lodGroupData = ref lodGroupsData.ElementAt(lodGroupInstance.index); + Assert.IsTrue(lodGroupData.valid); + + removedRendererCount += lodGroupData.rendererCount; + lodGroupData.valid = false; + } + } + + return removedRendererCount; + } + } +} diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/LODGroupDataSystemBurst.cs.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/LODGroupDataSystemBurst.cs.meta new file mode 100644 index 00000000000..41849ef2262 --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/LODGroupDataSystemBurst.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: a44ea003ea382364d9e18b103d0503cf \ No newline at end of file diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/RenderWorld.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/RenderWorld.cs new file mode 100644 index 00000000000..88d89e78147 --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/RenderWorld.cs @@ -0,0 +1,553 @@ +using System; +using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; +using Unity.Jobs; +using Unity.Mathematics; +using UnityEngine.Assertions; + +#if UNITY_EDITOR +using UnityEditor.SceneManagement; +#endif + +namespace UnityEngine.Rendering +{ + internal struct RenderWorld : IDisposable + { + public static readonly EntityId DefaultMesh = EntityId.None; + public static readonly ushort DefaultSubMeshStartIndex = 0; + public static readonly AABB DefaultLocalBounds = default; + public static readonly InternalMeshRendererSettings DefaultRendererSettings = InternalMeshRendererSettings.Default; + public static readonly InternalMeshLodRendererSettings DefaultMeshLodRendererSettings = InternalMeshLodRendererSettings.Default; + public static readonly int DefaultParentLODGroupID = 0; + public static readonly byte DefaultLODMask = 0; + public static readonly short DefaultLightmapIndex = LightmapUtils.LightmapIndexNull; + public static readonly int DefaultRendererPriority = 0; + +#if UNITY_EDITOR + public static readonly ulong DefaultSceneCullingMask = SceneCullingMasks.DefaultSceneCullingMask; +#endif + + private const int InvalidIndex = -1; + + // Keep these as NativeReference so that they can be modified from Burst jobs operating on the RenderWorld. + // Also keep the length separately because the component NativeArrays length is atually the capacity. + // Arrays growth/realloc doesn't follow a double on realloc policy, it is manually controlled. + private NativeReference m_InstancesCount; + private NativeReference m_TotalTreeCount; + + private NativeList m_HandleToIndex; + private NativeArray m_IndexToHandle; + private NativeArray m_InstanceIDs; + private NativeArray> m_MaterialIDArrays; + private NativeArray m_MeshIDs; + private NativeArray m_MeshLodRendererSettings; + private NativeArray m_SubMeshStartIndices; + private NativeArray m_LocalAABBs; + private NativeArray m_RendererSettings; + private NativeArray m_LightmapIndices; + private NativeArray m_LODGroupIndices; + private NativeArray m_LODMasks; + private NativeArray m_RendererPriorities; + private NativeArray m_GPUHandles; + private ParallelBitArray m_LocalToWorldIsFlippedBits; + private NativeArray m_WorldAABBs; + private NativeArray m_TetrahedronCacheIndices; + private ParallelBitArray m_MovedInCurrentFrameBits; + private ParallelBitArray m_MovedInPreviousFrameBits; + private ParallelBitArray m_VisibleInPreviousFrameBits; + private ParallelBitArray m_RenderingEnabled; + private EditorOnly m_EditorOnly; + private NativeParallelHashMap m_PerCameraInstanceDataMap; + + public NativeArray indexToHandle => m_IndexToHandle.GetSubArray(0, instanceCount); + public NativeArray instanceIDs => m_InstanceIDs.GetSubArray(0, instanceCount); + public NativeArray> materialIDArrays => m_MaterialIDArrays.GetSubArray(0, instanceCount); + public NativeArray meshIDs => m_MeshIDs.GetSubArray(0, instanceCount); + public NativeArray meshLodRendererSettings => m_MeshLodRendererSettings.GetSubArray(0, instanceCount); + public NativeArray subMeshStartIndices => m_SubMeshStartIndices.GetSubArray(0, instanceCount); + public NativeArray localAABBs => m_LocalAABBs.GetSubArray(0, instanceCount); + public NativeArray rendererSettings => m_RendererSettings.GetSubArray(0, instanceCount); + public NativeArray lightmapIndices => m_LightmapIndices.GetSubArray(0, instanceCount); + public NativeArray lodGroupIndices => m_LODGroupIndices.GetSubArray(0, instanceCount); + public NativeArray lodMasks => m_LODMasks.GetSubArray(0, instanceCount); + public NativeArray rendererPriorities => m_RendererPriorities.GetSubArray(0, instanceCount); + public NativeArray gpuHandles => m_GPUHandles.GetSubArray(0, instanceCount); + public ParallelBitArray localToWorldIsFlippedBits => m_LocalToWorldIsFlippedBits.GetSubArray(instanceCount); + public NativeArray worldAABBs => m_WorldAABBs.GetSubArray(0, instanceCount); + public NativeArray tetrahedronCacheIndices => m_TetrahedronCacheIndices.GetSubArray(0, instanceCount); + public ParallelBitArray movedInCurrentFrameBits => m_MovedInCurrentFrameBits.GetSubArray(instanceCount); + public ParallelBitArray movedInPreviousFrameBits => m_MovedInPreviousFrameBits.GetSubArray(instanceCount); + public ParallelBitArray visibleInPreviousFrameBits => m_VisibleInPreviousFrameBits.GetSubArray(instanceCount); + public ParallelBitArray renderingEnabled => m_RenderingEnabled.GetSubArray(instanceCount); + +#if UNITY_EDITOR + public NativeArray sceneCullingMasks => m_EditorOnly.sceneCullingMasks.GetSubArray(0, instanceCount); +#endif + + public int instanceCount { get => m_InstancesCount.Value; private set => m_InstancesCount.Value = value; } + public int handleCount => m_HandleToIndex.Length; + public int totalTreeCount => m_TotalTreeCount.Value; + public int cameraCount => m_PerCameraInstanceDataMap.Count(); + public unsafe UnsafeAtomicCounter32 atomicTotalTreeCount => new UnsafeAtomicCounter32(m_TotalTreeCount.GetUnsafePtr()); + + public void Initialize(int initCapacity) + { + m_InstancesCount = new NativeReference(0, Allocator.Persistent); + m_TotalTreeCount = new NativeReference(0, Allocator.Persistent); + m_HandleToIndex = new NativeList(Allocator.Persistent); + m_IndexToHandle = new NativeArray(initCapacity, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); + m_IndexToHandle.FillArray(InstanceHandle.Invalid); + m_InstanceIDs = new NativeArray(initCapacity, Allocator.Persistent); + m_MaterialIDArrays = new NativeArray>(initCapacity, Allocator.Persistent); + m_MeshIDs = new NativeArray(initCapacity, Allocator.Persistent); + m_MeshLodRendererSettings = new NativeArray(initCapacity, Allocator.Persistent); + m_SubMeshStartIndices = new NativeArray(initCapacity, Allocator.Persistent); + m_LocalAABBs = new NativeArray(initCapacity, Allocator.Persistent); + m_RendererSettings = new NativeArray(initCapacity, Allocator.Persistent); + m_LightmapIndices = new NativeArray(initCapacity, Allocator.Persistent); + m_LODGroupIndices = new NativeArray(initCapacity, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); + m_LODGroupIndices.FillArray(GPUInstanceIndex.Invalid); + m_LODMasks = new NativeArray(initCapacity, Allocator.Persistent); + m_RendererPriorities = new NativeArray(initCapacity, Allocator.Persistent); + m_GPUHandles = new NativeArray(initCapacity, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); + m_GPUHandles.FillArray(InstanceGPUHandle.Invalid); + m_LocalToWorldIsFlippedBits = new ParallelBitArray(initCapacity, Allocator.Persistent); + m_WorldAABBs = new NativeArray(initCapacity, Allocator.Persistent); + m_TetrahedronCacheIndices = new NativeArray(initCapacity, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); + m_TetrahedronCacheIndices.FillArray(InvalidIndex); + m_MovedInCurrentFrameBits = new ParallelBitArray(initCapacity, Allocator.Persistent); + m_MovedInPreviousFrameBits = new ParallelBitArray(initCapacity, Allocator.Persistent); + m_VisibleInPreviousFrameBits = new ParallelBitArray(initCapacity, Allocator.Persistent); + m_RenderingEnabled = new ParallelBitArray(initCapacity, Allocator.Persistent); + m_EditorOnly.Initialize(initCapacity); + m_PerCameraInstanceDataMap = new NativeParallelHashMap(1, Allocator.Persistent); + } + + public void Dispose() + { + int aliveInstanceCount = m_InstancesCount.Value; + + m_InstancesCount.Dispose(); + m_TotalTreeCount.Dispose(); + m_HandleToIndex.Dispose(); + m_IndexToHandle.Dispose(); + m_InstanceIDs.Dispose(); + + for (int i = 0; i < aliveInstanceCount; i++) + m_MaterialIDArrays[i].Dispose(); + m_MaterialIDArrays.Dispose(); + + m_MeshIDs.Dispose(); + m_MeshLodRendererSettings.Dispose(); + m_SubMeshStartIndices.Dispose(); + m_LocalAABBs.Dispose(); + m_RendererSettings.Dispose(); + m_LightmapIndices.Dispose(); + m_LODGroupIndices.Dispose(); + m_LODMasks.Dispose(); + m_RendererPriorities.Dispose(); + m_GPUHandles.Dispose(); + m_LocalToWorldIsFlippedBits.Dispose(); + m_WorldAABBs.Dispose(); + m_TetrahedronCacheIndices.Dispose(); + m_MovedInCurrentFrameBits.Dispose(); + m_MovedInPreviousFrameBits.Dispose(); + m_VisibleInPreviousFrameBits.Dispose(); + m_RenderingEnabled.Dispose(); + m_EditorOnly.Dispose(); + + foreach (var kv in m_PerCameraInstanceDataMap) + kv.Value.Dispose(); + m_PerCameraInstanceDataMap.Dispose(); + } + + private void Grow(int newCapacity) + { + int instancesCapacity = GetInstanceCapacity(); + Assert.IsTrue(newCapacity > instancesCapacity); + + m_IndexToHandle.ResizeArray(newCapacity); + m_IndexToHandle.FillArray(InstanceHandle.Invalid, instancesCapacity); + m_InstanceIDs.ResizeArray(newCapacity); + m_MaterialIDArrays.ResizeArray(newCapacity); + m_MaterialIDArrays.FillArray(default, instancesCapacity); + m_MeshIDs.ResizeArray(newCapacity); + m_MeshLodRendererSettings.ResizeArray(newCapacity); + m_SubMeshStartIndices.ResizeArray(newCapacity); + m_LocalAABBs.ResizeArray(newCapacity); + m_RendererSettings.ResizeArray(newCapacity); + m_LightmapIndices.ResizeArray(newCapacity); + m_LODGroupIndices.ResizeArray(newCapacity); + m_LODGroupIndices.FillArray(GPUInstanceIndex.Invalid, instancesCapacity); + m_LODMasks.ResizeArray(newCapacity); + m_LODMasks.FillArray(DefaultLODMask, instancesCapacity); + m_RendererPriorities.ResizeArray(newCapacity); + m_GPUHandles.ResizeArray(newCapacity); + m_GPUHandles.FillArray(InstanceGPUHandle.Invalid, instancesCapacity); + m_LocalToWorldIsFlippedBits.Resize(newCapacity); + m_WorldAABBs.ResizeArray(newCapacity); + m_TetrahedronCacheIndices.ResizeArray(newCapacity); + m_TetrahedronCacheIndices.FillArray(InvalidIndex, instancesCapacity); + m_MovedInCurrentFrameBits.Resize(newCapacity); + m_MovedInPreviousFrameBits.Resize(newCapacity); + m_VisibleInPreviousFrameBits.Resize(newCapacity); + m_RenderingEnabled.Resize(newCapacity); + m_EditorOnly.Grow(newCapacity); + + foreach (var kv in m_PerCameraInstanceDataMap) + kv.Value.Resize(newCapacity); + } + + private int AddUninitialized(InstanceHandle instance) + { + if (instance.index >= m_HandleToIndex.Length) + { + int prevLength = m_HandleToIndex.Length; + m_HandleToIndex.ResizeUninitialized(instance.index + 1); + + for (int i = prevLength; i < m_HandleToIndex.Length - 1; ++i) + m_HandleToIndex[i] = InvalidIndex; + } + + m_HandleToIndex[instance.index] = instanceCount; + m_IndexToHandle[instanceCount] = instance; + + return instanceCount++; + } + + public int HandleToIndex(InstanceHandle instance) + { + Assert.IsTrue(IsValidInstance(instance)); + return m_HandleToIndex[instance.index]; + } + + public InstanceHandle IndexToHanle(int index) + { + Assert.IsTrue(IsValidIndex(index)); + return m_IndexToHandle[index]; + } + + public bool IsValidInstance(InstanceHandle instance) + { + if (instance.isValid && instance.index < m_HandleToIndex.Length) + { + int index = m_HandleToIndex[instance.index]; + return index >= 0 && index < instanceCount && m_IndexToHandle[index].Equals(instance); + } + + return false; + } + + public bool IsValidIndex(int index) + { + if (index >= 0 && index < instanceCount) + { + InstanceHandle instance = m_IndexToHandle[index]; + return index == m_HandleToIndex[instance.index]; + } + + return false; + } + + public bool IsFreeInstanceHandle(InstanceHandle instance) + { + return instance.isValid && (instance.index >= m_HandleToIndex.Length || m_HandleToIndex[instance.index] == InvalidIndex); + } + + public int GetInstanceCapacity() + { + return m_IndexToHandle.Length; + } + + public int GetFreeCapacity() + { + return GetInstanceCapacity() - instanceCount; + } + + public void EnsureFreeCapacity(int instancesCount) + { + if (instancesCount == 0) + return; + + int freeInstancesCount = GetFreeCapacity(); + int needInstances = instancesCount - freeInstancesCount; + + if (needInstances > 0) + Grow(GetInstanceCapacity() + needInstances + 256); + } + + public int AddInstanceNoGrow(InstanceHandle instance) + { + Assert.IsTrue(instance.isValid); + Assert.IsTrue(IsFreeInstanceHandle(instance)); + Assert.IsTrue(GetFreeCapacity() > 0); + + int instanceIndex = AddUninitialized(instance); + InitializeInstance(instanceIndex); + return instanceIndex; + } + + public void RemoveInstance(InstanceHandle instance) + { + Assert.IsTrue(IsValidInstance(instance)); + + int index = HandleToIndex(instance); + int lastIndex = instanceCount - 1; + + if (m_RendererSettings[index].HasTree) + { + Assert.IsTrue(m_TotalTreeCount.Value > 0); + --m_TotalTreeCount.Value; + } + + m_IndexToHandle[index] = m_IndexToHandle[lastIndex]; + m_InstanceIDs[index] = m_InstanceIDs[lastIndex]; + + m_MaterialIDArrays[index].Dispose(); + m_MaterialIDArrays[index] = m_MaterialIDArrays[lastIndex]; + m_MaterialIDArrays[lastIndex] = default; + + m_MeshIDs[index] = m_MeshIDs[lastIndex]; + m_MeshLodRendererSettings[index] = m_MeshLodRendererSettings[lastIndex]; + m_SubMeshStartIndices[index] = m_SubMeshStartIndices[lastIndex]; + m_LocalAABBs[index] = m_LocalAABBs[lastIndex]; + m_RendererSettings[index] = m_RendererSettings[lastIndex]; + m_LightmapIndices[index] = m_LightmapIndices[lastIndex]; + m_LODGroupIndices[index] = m_LODGroupIndices[lastIndex]; + m_LODMasks[index] = m_LODMasks[lastIndex]; + m_RendererPriorities[index] = m_RendererPriorities[lastIndex]; + m_GPUHandles[index] = m_GPUHandles[lastIndex]; + m_LocalToWorldIsFlippedBits.Set(index, m_LocalToWorldIsFlippedBits.Get(lastIndex)); + m_WorldAABBs[index] = m_WorldAABBs[lastIndex]; + m_TetrahedronCacheIndices[index] = m_TetrahedronCacheIndices[lastIndex]; + m_MovedInCurrentFrameBits.Set(index, m_MovedInCurrentFrameBits.Get(lastIndex)); + m_MovedInPreviousFrameBits.Set(index, m_MovedInPreviousFrameBits.Get(lastIndex)); + m_VisibleInPreviousFrameBits.Set(index, m_VisibleInPreviousFrameBits.Get(lastIndex)); + m_RenderingEnabled.Set(index, m_RenderingEnabled.Get(lastIndex)); + m_EditorOnly.Remove(index, lastIndex); + + foreach (var kv in m_PerCameraInstanceDataMap) + kv.Value.Remove(index, lastIndex); + + m_HandleToIndex[m_IndexToHandle[lastIndex].index] = index; + m_HandleToIndex[instance.index] = InvalidIndex; + instanceCount -= 1; + } + + public void ResetInstance(int instanceIndex) + { + m_MaterialIDArrays[instanceIndex].Dispose(); + m_MaterialIDArrays[instanceIndex] = default; + m_MeshIDs[instanceIndex] = DefaultMesh; + m_MeshLodRendererSettings[instanceIndex] = DefaultMeshLodRendererSettings; + m_SubMeshStartIndices[instanceIndex] = DefaultSubMeshStartIndex; + m_LocalAABBs[instanceIndex] = DefaultLocalBounds; + m_RendererSettings[instanceIndex] = DefaultRendererSettings; + m_LightmapIndices[instanceIndex] = DefaultLightmapIndex; + m_LODGroupIndices[instanceIndex] = GPUInstanceIndex.Invalid; + m_LODMasks[instanceIndex] = DefaultLODMask; + m_RendererPriorities[instanceIndex] = DefaultRendererPriority; + m_LocalToWorldIsFlippedBits.Set(instanceIndex, false); + m_WorldAABBs[instanceIndex] = default; + m_TetrahedronCacheIndices[instanceIndex] = InvalidIndex; + m_RenderingEnabled.Set(instanceIndex, true); + m_EditorOnly.SetDefault(instanceIndex); + + foreach (var kv in m_PerCameraInstanceDataMap) + { + kv.Value.meshLods[instanceIndex] = PerCameraInstanceData.InvalidByteData; + kv.Value.crossFades[instanceIndex] = PerCameraInstanceData.InvalidByteData; + } + } + + private void InitializeInstance(int instanceIndex) + { + ResetInstance(instanceIndex); + m_InstanceIDs[instanceIndex] = EntityId.None; + m_GPUHandles[instanceIndex] = InstanceGPUHandle.Invalid; + m_MovedInCurrentFrameBits.Set(instanceIndex, false); + m_MovedInPreviousFrameBits.Set(instanceIndex, false); + m_VisibleInPreviousFrameBits.Set(instanceIndex, false); + } + + public void AddCameras(NativeArray cameraIDs) + { + foreach (var cameraID in cameraIDs) + { + if (m_PerCameraInstanceDataMap.ContainsKey(cameraID)) + continue; + + m_PerCameraInstanceDataMap.Add(cameraID, new UnsafePerCameraInstanceData(GetInstanceCapacity(), Allocator.Persistent)); + } + } + + public void RemoveCameras(NativeArray cameraIDs) + { + foreach (var cameraID in cameraIDs) + { + if (!m_PerCameraInstanceDataMap.ContainsKey(cameraID)) + continue; + + m_PerCameraInstanceDataMap[cameraID].Dispose(); + m_PerCameraInstanceDataMap.Remove(cameraID); + } + } + + public bool TryGetPerCameraInstanceData(EntityId cameraID, out PerCameraInstanceData perCameraInstanceData) + { + perCameraInstanceData = default; + + if (!m_PerCameraInstanceDataMap.TryGetValue(cameraID, out UnsafePerCameraInstanceData unsafePerCameraData)) + return false; + + perCameraInstanceData = unsafePerCameraData.ToPerCameraInstanceData(instanceCount); + return true; + } + + public PerCameraInstanceData GetPerCameraInstanceData(EntityId cameraID) + { + return m_PerCameraInstanceDataMap[cameraID].ToPerCameraInstanceData(instanceCount); + } + + public struct PerCameraInstanceData + { + public const int InvalidByteData = 0xff; + + public NativeArray meshLods; + public NativeArray crossFades; + + public bool IsCreated => meshLods.IsCreated && crossFades.IsCreated; + + public PerCameraInstanceData(int length, Allocator allocator) + { + meshLods = new NativeArray(length, allocator); + crossFades = new NativeArray(length, allocator); + } + + public void Dispose(JobHandle jobHandle) + { + meshLods.Dispose(jobHandle); + crossFades.Dispose(jobHandle); + } + } + + struct UnsafePerCameraInstanceData : IDisposable + { + public UnsafeList meshLods; + public UnsafeList crossFades; + + public UnsafePerCameraInstanceData(int initCapacity, Allocator allocator) + { + meshLods = new UnsafeList(initCapacity, allocator, NativeArrayOptions.UninitializedMemory); + meshLods.AddReplicate(PerCameraInstanceData.InvalidByteData, initCapacity); + crossFades = new UnsafeList(initCapacity, allocator, NativeArrayOptions.UninitializedMemory); + crossFades.AddReplicate(PerCameraInstanceData.InvalidByteData, initCapacity); + } + + public void Dispose() + { + meshLods.Dispose(); + crossFades.Dispose(); + } + + public void Remove(int index, int lastIndex) + { + meshLods[index] = meshLods[lastIndex]; + crossFades[index] = crossFades[lastIndex]; + } + + public void Resize(int newCapacity) + { + meshLods.Resize(newCapacity); + crossFades.Resize(newCapacity); + } + + public PerCameraInstanceData ToPerCameraInstanceData(int instanceCount) + { + return new PerCameraInstanceData + { + meshLods = meshLods.AsNativeArray().GetSubArray(0, instanceCount), + crossFades = crossFades.AsNativeArray().GetSubArray(0, instanceCount) + }; + } + } + + struct EditorOnly + { +#if UNITY_EDITOR + public NativeArray sceneCullingMasks; + + public void Initialize(int initCapacity) + { + sceneCullingMasks = new NativeArray(initCapacity, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); + sceneCullingMasks.FillArray(ulong.MaxValue); + } + + public void Dispose() + { + sceneCullingMasks.Dispose(); + } + + public void Grow(int newCapacity) + { + sceneCullingMasks.ResizeArray(newCapacity); + } + + public void Remove(int index, int lastIndex) + { + sceneCullingMasks[index] = sceneCullingMasks[lastIndex]; + } + + public void SetDefault(int index) + { + sceneCullingMasks[index] = DefaultSceneCullingMask; + } +#else + public void Initialize(int initCapacity) { } + public void Dispose() { } + public void Grow(int newCapacity) { } + public void Remove(int index, int lastIndex) { } + public void SetDefault(int index) { } +#endif + } + } + + internal struct PackedMatrix + { + /* mat4x3 packed like this: + p1.x, p1.w, p2.z, p3.y, + p1.y, p2.x, p2.w, p3.z, + p1.z, p2.y, p3.x, p3.w, + 0.0, 0.0, 0.0, 1.0 + */ + + public float4 packed0; + public float4 packed1; + public float4 packed2; + + public static PackedMatrix FromMatrix4x4(in Matrix4x4 m) + { + return new PackedMatrix + { + packed0 = new float4(m.m00, m.m10, m.m20, m.m01), + packed1 = new float4(m.m11, m.m21, m.m02, m.m12), + packed2 = new float4(m.m22, m.m03, m.m13, m.m23) + }; + } + + public static PackedMatrix FromFloat4x4(in float4x4 m) + { + return new PackedMatrix + { + packed0 = new float4(m.c0.x, m.c0.y, m.c0.z, m.c1.x), + packed1 = new float4(m.c1.y, m.c1.z, m.c2.x, m.c2.y), + packed2 = new float4(m.c2.z, m.c3.x, m.c3.y, m.c3.z) + }; + } + + public override string ToString() + { + return $"[{packed0}, {packed1}, {packed2}]"; + } + } +} diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceData.cs.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/RenderWorld.cs.meta similarity index 100% rename from Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceData.cs.meta rename to Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/RenderWorld.cs.meta diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling.meta new file mode 100644 index 00000000000..53bba2a81ea --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 99ecf45f0f3a0df48a6c4e7128c4e5cb +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceCuller.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/InstanceCuller.cs similarity index 78% rename from Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceCuller.cs rename to Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/InstanceCuller.cs index 3974f0525da..d7bc6fd4a3f 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceCuller.cs +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/InstanceCuller.cs @@ -31,14 +31,13 @@ internal struct RangeKey : IEquatable public bool Equals(RangeKey other) { - return - layer == other.layer && - renderingLayerMask == other.renderingLayerMask && - motionMode == other.motionMode && - shadowCastingMode == other.shadowCastingMode && - staticShadowCaster == other.staticShadowCaster && - rendererPriority == other.rendererPriority && - supportsIndirect == other.supportsIndirect; + return layer == other.layer + && renderingLayerMask == other.renderingLayerMask + && motionMode == other.motionMode + && shadowCastingMode == other.shadowCastingMode + && staticShadowCaster == other.staticShadowCaster + && rendererPriority == other.rendererPriority + && supportsIndirect == other.supportsIndirect; } public override int GetHashCode() @@ -64,28 +63,27 @@ internal struct DrawRange internal struct DrawKey : IEquatable { + public EntityId transparentInstanceID; // non-zero for transparent instances, to ensure each instance has its own draw command (for sorting) public BatchMeshID meshID; public int submeshIndex; public int activeMeshLod; // or -1 if this draw is not using mesh LOD public BatchMaterialID materialID; public BatchDrawCommandFlags flags; - public EntityId transparentInstanceId; // non-zero for transparent instances, to ensure each instance has its own draw command (for sorting) - public uint overridenComponents; + public GPUArchetypeHandle archetype; public RangeKey range; public int lightmapIndex; public bool Equals(DrawKey other) { - return - meshID == other.meshID && - submeshIndex == other.submeshIndex && - activeMeshLod == other.activeMeshLod && - materialID == other.materialID && - flags == other.flags && - transparentInstanceId == other.transparentInstanceId && - overridenComponents == other.overridenComponents && - range.Equals(other.range) && - lightmapIndex == other.lightmapIndex; + return meshID == other.meshID + && submeshIndex == other.submeshIndex + && activeMeshLod == other.activeMeshLod + && materialID == other.materialID + && flags == other.flags + && transparentInstanceID == other.transparentInstanceID + && archetype.Equals(other.archetype) + && range.Equals(other.range) + && lightmapIndex == other.lightmapIndex; } public override int GetHashCode() @@ -96,9 +94,9 @@ public override int GetHashCode() hash = (hash * 23) + (int)activeMeshLod; hash = (hash * 23) + (int)materialID.value; hash = (hash * 23) + (int)flags; - hash = (hash * 23) + transparentInstanceId.GetHashCode(); + hash = (hash * 23) + (int)transparentInstanceID.GetRawData(); hash = (hash * 23) + range.GetHashCode(); - hash = (hash * 23) + (int)overridenComponents; + hash = (hash * 23) + archetype.index; hash = (hash * 23) + lightmapIndex; return hash; } @@ -109,7 +107,10 @@ internal struct DrawBatch public DrawKey key; public int instanceCount; public int instanceOffset; - public MeshProceduralInfo procInfo; + public MeshTopology topology; + public uint baseVertex; + public uint firstIndex; + public uint indexCount; } internal struct DrawInstance @@ -139,39 +140,35 @@ public int visibilityConfigCount internal struct AnimateCrossFadeJob : IJobParallelFor { public const int k_BatchSize = 512; - public const byte k_MeshLODTransitionToLowerLODBit = 0x80; + public const byte k_MeshLodTransitionToLowerLodBit = 0x80; private const byte k_LODFadeOff = (byte)CullingJob.k_LODFadeOff; private const float k_CrossfadeAnimationTimeS = 0.333f; [ReadOnly] public float deltaTime; - public UnsafeList crossFadeArray; + public NativeArray crossFadeArray; public unsafe void Execute(int instanceIndex) { - ref var crossFadeValue = ref crossFadeArray.ElementAt(instanceIndex); + ref var crossFadeValue = ref crossFadeArray.ElementAtRW(instanceIndex); - if(crossFadeValue == k_LODFadeOff) + if (crossFadeValue == k_LODFadeOff) return; - var prevTransitionBit = (crossFadeValue & k_MeshLODTransitionToLowerLODBit); + var prevTransitionBit = (crossFadeValue & k_MeshLodTransitionToLowerLodBit); crossFadeValue += (byte)((deltaTime / k_CrossfadeAnimationTimeS) * 127.0f); //If done with crossfade - reset mask - if (prevTransitionBit != ((crossFadeValue + 1) & k_MeshLODTransitionToLowerLODBit)) - { + if (prevTransitionBit != ((crossFadeValue + 1) & k_MeshLodTransitionToLowerLodBit)) crossFadeValue = k_LODFadeOff; - } } } [BurstCompile] internal struct CullingJob : IJobParallelFor { - public const int k_BatchSize = 32; - public const uint k_MeshLodCrossfadeActive = 0x40; public const uint k_MeshLodCrossfadeSignBit = 0x80; public const uint k_MeshLodCrossfadeBits = k_MeshLodCrossfadeSignBit | k_MeshLodCrossfadeActive; @@ -194,8 +191,9 @@ enum CrossFadeType kVisible // 3 == instance is visible in both current and next lod level - could not be impacted by fade } + [ReadOnly] public RenderWorld renderWorld; + [ReadOnly] public NativeParallelHashMap meshMap; [ReadOnly] public BinningConfig binningConfig; - [ReadOnly] public BatchCullingViewType viewType; [ReadOnly] public float3 cameraPosition; [ReadOnly] public float sqrMeshLodSelectionConstant; @@ -206,51 +204,50 @@ enum CrossFadeType [ReadOnly] public int maxLOD; [ReadOnly] public uint cullingLayerMask; [ReadOnly] public ulong sceneCullingMask; + [ReadOnly] public float3x3 worldToLightSpaceRotation; [ReadOnly] public bool animateCrossFades; [ReadOnly] public NativeArray frustumPlanePackets; [ReadOnly] public NativeArray frustumSplitInfos; [ReadOnly] public NativeArray lightFacingFrustumPlanes; [ReadOnly] public NativeArray receiverSplitInfos; - public float3x3 worldToLightSpaceRotation; - [ReadOnly] public CPUInstanceData.ReadOnly instanceData; - [ReadOnly] public CPUSharedInstanceData.ReadOnly sharedInstanceData; - [NativeDisableContainerSafetyRestriction, NoAlias] [ReadOnly] public NativeList lodGroupCullingData; - [NativeDisableUnsafePtrRestriction] [ReadOnly] public IntPtr occlusionBuffer; +#if UNITY_EDITOR + [ReadOnly] public IncludeExcludeListFilter includeExcludeListFilter; +#endif - [NativeDisableContainerSafetyRestriction] public CPUPerCameraInstanceData.PerCameraInstanceDataArrays cameraInstanceData; + [NativeDisableContainerSafetyRestriction, NoAlias][ReadOnly] public NativeList lodGroupCullingData; + [NativeDisableUnsafePtrRestriction][ReadOnly] public IntPtr occlusionBuffer; [NativeDisableParallelForRestriction][WriteOnly] public NativeArray rendererVisibilityMasks; [NativeDisableParallelForRestriction][WriteOnly] public NativeArray rendererMeshLodSettings; [NativeDisableParallelForRestriction][WriteOnly] public NativeArray rendererCrossFadeValues; + public RenderWorld.PerCameraInstanceData perCameraInstanceData; // float [-1.0f... 1.0f] -> uint [0...254] static uint PackFloatToUint8(float percent) { uint packed = (uint)((1.0f + percent) * 127.0f + 0.5f); + // avoid zero - if (percent < 0.0f) - packed = math.clamp(packed, 0, 126); - else - packed = math.clamp(packed, 128, 254); - return packed; + return percent < 0.0f + ? math.clamp(packed, 0, 126) + : math.clamp(packed, 128, 254); } - unsafe uint CalculateLODVisibility(int instanceIndex, int sharedInstanceIndex, InstanceFlags instanceFlags) + unsafe uint CalculateLODVisibility(int instanceIndex, bool smallMeshCulling) { - var lodDataIndexAndMask = sharedInstanceData.lodGroupAndMasks[sharedInstanceIndex]; - - if (lodDataIndexAndMask == k_InvalidCrossFadeAndLevel) + GPUInstanceIndex lodGroupIndex = renderWorld.lodGroupIndices[instanceIndex]; + uint lodMask = renderWorld.lodMasks[instanceIndex]; + + if (lodGroupIndex.Equals(GPUInstanceIndex.Invalid) && lodMask == 0) { - if(viewType >= BatchCullingViewType.SelectionOutline - || (instanceFlags & InstanceFlags.SmallMeshCulling) == 0 - || minScreenRelativeHeight == 0.0f) + if (viewType >= BatchCullingViewType.SelectionOutline || !smallMeshCulling || minScreenRelativeHeight == 0.0f) return k_LODFadeOff; // If no LODGroup available - small mesh culling. - ref readonly AABB worldAABB = ref instanceData.worldAABBs.UnsafeElementAt(instanceIndex); + ref readonly AABB worldAABB = ref renderWorld.worldAABBs.ElementAt(instanceIndex); var cameraSqrDist = isOrtho ? sqrScreenRelativeMetric : LODRenderingUtils.CalculateSqrPerspectiveDistance(worldAABB.center, cameraPosition, sqrScreenRelativeMetric); var cameraDist = math.sqrt(cameraSqrDist); @@ -267,12 +264,9 @@ unsafe uint CalculateLODVisibility(int instanceIndex, int sharedInstanceIndex, I return lodPercent > 1.0f ? k_LODFadeOff : PackFloatToUint8(lodPercent); } - var lodIndex = lodDataIndexAndMask >> 8; - var lodMask = lodDataIndexAndMask & 0xFF; Assert.IsTrue(lodMask > 0); - ref var lodGroup = ref lodGroupCullingData.ElementAt((int)lodIndex); - + ref var lodGroup = ref lodGroupCullingData.ElementAt(lodGroupIndex.index); if (lodGroup.forceLODMask != 0) return (lodGroup.forceLODMask & lodMask) != 0 ? k_LODFadeOff : k_LODFadeZeroPacked; @@ -297,7 +291,6 @@ unsafe uint CalculateLODVisibility(int instanceIndex, int sharedInstanceIndex, I if (cameraSqrDistToLODCenter > lodRangeSqrMax) { - ++m; lodMask >>= 1; continue; @@ -308,14 +301,11 @@ unsafe uint CalculateLODVisibility(int instanceIndex, int sharedInstanceIndex, I // Instance is in neither this LOD Level nor next - invisible if (type == CrossFadeType.kDisabled) - { return k_LODFadeZeroPacked; - } + // Instance is in both this and the next lod. No need to fade - fully visible if (type == CrossFadeType.kVisible) - { return k_LODFadeOff; - } var distanceToLodCenter = math.sqrt(cameraSqrDistToLODCenter); var maxDist = math.sqrt(lodRangeSqrMax); @@ -350,43 +340,47 @@ unsafe uint CalculateLODVisibility(int instanceIndex, int sharedInstanceIndex, I if (type == CrossFadeType.kCrossFadeIn) lodPercent = -lodPercent; + return PackFloatToUint8(lodPercent); } return type == CrossFadeType.kCrossFadeOut ? k_LODFadeOff : k_LODFadeZeroPacked; } - } return k_LODFadeZeroPacked; } - private unsafe uint CalculateVisibilityMask(int instanceIndex, int sharedInstanceIndex, InstanceFlags instanceFlags) + private uint CalculateVisibilityMask(int instanceIndex, ShadowCastingMode shadowCastingMode, bool affectsLightmaps) { + if (!renderWorld.renderingEnabled.Get(instanceIndex)) + return 0; + if (cullingLayerMask == 0) return 0; - if ((cullingLayerMask & (1 << sharedInstanceData.gameObjectLayers[sharedInstanceIndex])) == 0) + if ((cullingLayerMask & (1 << renderWorld.rendererSettings[instanceIndex].ObjectLayer)) == 0) return 0; - if (cullLightmappedShadowCasters && (instanceFlags & InstanceFlags.AffectsLightmaps) != 0) + if (cullLightmappedShadowCasters && affectsLightmaps) return 0; #if UNITY_EDITOR - if ((sceneCullingMask & instanceData.editorData.sceneCullingMasks[instanceIndex]) == 0) + if ((sceneCullingMask & renderWorld.sceneCullingMasks[instanceIndex]) == 0) return 0; - if(viewType == BatchCullingViewType.SelectionOutline && !instanceData.editorData.selectedBits.Get(instanceIndex)) + if (viewType == BatchCullingViewType.SelectionOutline && !includeExcludeListFilter.DoesPassFilter(renderWorld.instanceIDs[instanceIndex])) return 0; #endif // cull early for camera and shadow views based on the shadow culling mode - if (viewType == BatchCullingViewType.Camera && (instanceFlags & InstanceFlags.IsShadowsOnly) != 0) + if (viewType == BatchCullingViewType.Camera && shadowCastingMode == ShadowCastingMode.ShadowsOnly) return 0; - if (viewType == BatchCullingViewType.Light && (instanceFlags & InstanceFlags.IsShadowsOff) != 0) + + if (viewType == BatchCullingViewType.Light && shadowCastingMode == ShadowCastingMode.Off) return 0; - ref readonly AABB worldAABB = ref instanceData.worldAABBs.UnsafeElementAt(instanceIndex); + ref readonly AABB worldAABB = ref renderWorld.worldAABBs.ElementAt(instanceIndex); uint visibilityMask = FrustumPlaneCuller.ComputeSplitVisibilityMask(frustumPlanePackets, frustumSplitInfos, worldAABB); if (visibilityMask != 0 && receiverSplitInfos.Length > 0) @@ -400,15 +394,15 @@ private unsafe uint CalculateVisibilityMask(int instanceIndex, int sharedInstanc } // Algorithm is detailed and must be kept in sync with CalculateMeshLod (C++) - private uint ComputeMeshLODLevel(int instanceIndex, int sharedInstanceIndex) + private uint ComputeMeshLODLevel(int instanceIndex, in MeshInfo mesh) { - ref readonly GPUDrivenRendererMeshLodData meshLodData = ref instanceData.meshLodData.UnsafeElementAt(instanceIndex); - var meshLodInfo = sharedInstanceData.meshLodInfos[sharedInstanceIndex]; + InternalMeshLodRendererSettings meshLodSettings = renderWorld.meshLodRendererSettings[instanceIndex]; - if (meshLodData.forceLod >= 0) - return (uint)math.clamp(meshLodData.forceLod, 0, meshLodInfo.levelCount - 1); + if (meshLodSettings.forceLod >= 0) + return (uint)math.clamp(meshLodSettings.forceLod, 0, mesh.meshLodCount - 1); - ref readonly AABB worldAABB = ref instanceData.worldAABBs.UnsafeElementAt(instanceIndex); + Mesh.LodSelectionCurve lodSelectionCurve = mesh.meshLodSelectionCurve; + ref readonly AABB worldAABB = ref renderWorld.worldAABBs.ElementAt(instanceIndex); var radiusSqr = math.max(math.lengthsq(worldAABB.extents), 1e-5f); var diameterSqr = radiusSqr * 4; @@ -417,13 +411,13 @@ private uint ComputeMeshLODLevel(int instanceIndex, int sharedInstanceIndex) var boundsDesiredPercentage = Math.Sqrt(cameraSqrHeightAtDistance / diameterSqr); - var levelIndexFlt = math.log2(boundsDesiredPercentage) * meshLodInfo.lodSlope + meshLodInfo.lodBias; + var levelIndexFlt = math.log2(boundsDesiredPercentage) * lodSelectionCurve.lodSlope + lodSelectionCurve.lodBias; // We apply Bias after max to enforce that a positive bias of +N we would select lodN instead of Lod0 levelIndexFlt = math.max(levelIndexFlt, 0); - levelIndexFlt += meshLodData.lodSelectionBias; + levelIndexFlt += meshLodSettings.lodSelectionBias; - levelIndexFlt = math.clamp(levelIndexFlt, 0, meshLodInfo.levelCount - 1); + levelIndexFlt = math.clamp(levelIndexFlt, 0, mesh.meshLodCount - 1); return (uint)math.floor(levelIndexFlt); } @@ -435,16 +429,16 @@ private uint ComputeMeshLODLevel(int instanceIndex, int sharedInstanceIndex) // This means that we can use (k_MeshLODTransitionToLowerLODBit = 0x80) to select the correct instance to blend with during draw command generation. private unsafe uint ComputeMeshLODCrossfade(int instanceIndex, ref uint meshLodLevel) { - var previousLodLevel = cameraInstanceData.meshLods[instanceIndex]; + var previousLodLevel = perCameraInstanceData.meshLods[instanceIndex]; //1st frame - previous LOD level is invalid. Just update it and return. - if(previousLodLevel == 0xff) + if (previousLodLevel == 0xff) { - cameraInstanceData.meshLods[instanceIndex] = (byte)meshLodLevel; + perCameraInstanceData.meshLods[instanceIndex] = (byte)meshLodLevel; return k_LODFadeOff; } - var currentCrossFade = cameraInstanceData.crossFades[instanceIndex]; + var currentCrossFade = perCameraInstanceData.crossFades[instanceIndex]; // If not already crossfading, check if we changed Lod level this frame, and starts crossfading. if (currentCrossFade == k_LODFadeOff) @@ -452,8 +446,8 @@ private unsafe uint ComputeMeshLODCrossfade(int instanceIndex, ref uint meshLodL if (previousLodLevel == meshLodLevel) return k_LODFadeOff; - cameraInstanceData.meshLods[instanceIndex] = (byte)meshLodLevel; - cameraInstanceData.crossFades[instanceIndex] = (byte)(meshLodLevel < previousLodLevel ? AnimateCrossFadeJob.k_MeshLODTransitionToLowerLODBit : 1); + perCameraInstanceData.meshLods[instanceIndex] = (byte)meshLodLevel; + perCameraInstanceData.crossFades[instanceIndex] = (byte)(meshLodLevel < previousLodLevel ? AnimateCrossFadeJob.k_MeshLodTransitionToLowerLodBit : 1); meshLodLevel = previousLodLevel; return k_LODFadeOff; } @@ -465,35 +459,32 @@ private unsafe uint ComputeMeshLODCrossfade(int instanceIndex, ref uint meshLodL return k_LODFadeOff; } - meshLodLevel = previousLodLevel | (currentCrossFade > k_LODFadeZeroPacked ? k_MeshLodCrossfadeBits : k_MeshLodCrossfadeActive); + meshLodLevel = previousLodLevel | (currentCrossFade > k_LODFadeZeroPacked ? k_MeshLodCrossfadeBits : k_MeshLodCrossfadeActive); return currentCrossFade; } private unsafe void EnforcePreviousFrameMeshLOD(int instanceIndex, ref uint meshLodLevel) { - ref var previousLodLevel = ref cameraInstanceData.meshLods.ElementAt(instanceIndex); - + ref var previousLodLevel = ref perCameraInstanceData.meshLods.ElementAtRW(instanceIndex); if (previousLodLevel != k_LODFadeOff) - { meshLodLevel = previousLodLevel; - } } public void Execute(int instanceIndex) { - InstanceHandle instance = instanceData.instances[instanceIndex]; - int sharedInstanceIndex = sharedInstanceData.InstanceToIndex(instanceData, instance); - var instanceFlags = sharedInstanceData.flags[sharedInstanceIndex].instanceFlags; - - var visibilityMask = CalculateVisibilityMask(instanceIndex, sharedInstanceIndex, instanceFlags); + InstanceHandle instance = renderWorld.indexToHandle[instanceIndex]; + InternalMeshRendererSettings rendererSettings = renderWorld.rendererSettings[instanceIndex]; + bool affectsLightmaps = LightmapUtils.AffectsLightmaps(renderWorld.lightmapIndices[instanceIndex]); + + var visibilityMask = CalculateVisibilityMask(instanceIndex, rendererSettings.ShadowCastingMode, affectsLightmaps); if (visibilityMask == k_VisibilityMaskNotVisible) { rendererVisibilityMasks[instance.index] = (byte)k_VisibilityMaskNotVisible; return; } - var crossFadeValue = CalculateLODVisibility(instanceIndex, sharedInstanceIndex, instanceFlags); + var crossFadeValue = CalculateLODVisibility(instanceIndex, rendererSettings.SmallMeshCulling); if (crossFadeValue == k_LODFadeZeroPacked) { rendererVisibilityMasks[instance.index] = (byte)k_VisibilityMaskNotVisible; @@ -502,21 +493,22 @@ public void Execute(int instanceIndex) if (binningConfig.supportsMotionCheck) { - bool hasMotion = instanceData.movedInPreviousFrameBits.Get(instanceIndex); + bool hasMotion = renderWorld.movedInPreviousFrameBits.Get(instanceIndex); visibilityMask = (visibilityMask << 1) | (hasMotion ? 1U : 0); } - var meshLodLevel = 0u; + EntityId meshID = renderWorld.meshIDs[instanceIndex]; + MeshInfo mesh = meshMap[meshID]; + bool hasMeshLod = mesh.isLodSelectionActive; + // select mesh LOD level - var hasMeshLod = (instanceFlags & InstanceFlags.HasMeshLod) != 0; + uint meshLodLevel = 0u; if (hasMeshLod) - { - meshLodLevel = ComputeMeshLODLevel(instanceIndex, sharedInstanceIndex); - } + meshLodLevel = ComputeMeshLODLevel(instanceIndex, mesh); if (binningConfig.supportsCrossFade) { - if(hasMeshLod && animateCrossFades) + if (hasMeshLod && animateCrossFades) { //Only Crossfade mesh LOD if we aren't already crossfading through LOD groups or SpeedTree if (crossFadeValue == k_LODFadeOff) @@ -546,7 +538,7 @@ internal unsafe struct AllocateBinsPerBatch : IJobParallelFor [ReadOnly] public NativeList drawBatches; [ReadOnly] public NativeArray drawInstanceIndices; - [ReadOnly] public CPUInstanceData.ReadOnly instanceData; + [ReadOnly] public RenderWorld renderWorld; [ReadOnly] public NativeArray rendererVisibilityMasks; [ReadOnly] public NativeArray rendererMeshLodSettings; @@ -562,9 +554,9 @@ internal unsafe struct AllocateBinsPerBatch : IJobParallelFor bool IsInstanceFlipped(int rendererIndex) { - InstanceHandle instance = InstanceHandle.FromInt(rendererIndex); - int instanceIndex = instanceData.InstanceToIndex(instance); - return instanceData.localToWorldIsFlippedBits.Get(instanceIndex); + InstanceHandle instance = InstanceHandle.Create(rendererIndex); + int instanceIndex = renderWorld.HandleToIndex(instance); + return renderWorld.localToWorldIsFlippedBits.Get(instanceIndex); } // Keep in sync with isMeshLodVisible from DrawCommandOutputPerBatch @@ -610,13 +602,13 @@ public void Execute(int batchIndex) int configCount = binningConfig.visibilityConfigCount; // allocate space to keep track of the number of instances per config - var visibleCountPerConfig = stackalloc int[configCount]; + Span visibleCountPerConfig = stackalloc int[configCount]; for (int i = 0; i < configCount; ++i) visibleCountPerConfig[i] = 0; // and space to keep track of which configs have any instances int configMaskCount = (configCount + 63)/64; - var configUsedMasks = stackalloc UInt64[configMaskCount]; + Span configUsedMasks = stackalloc ulong[configMaskCount]; for (int i = 0; i < configMaskCount; ++i) configUsedMasks[i] = 0; @@ -630,7 +622,7 @@ public void Execute(int batchIndex) var rendererIndex = drawInstanceIndices[instanceOffset + i]; bool isFlipped = IsInstanceFlipped(rendererIndex); - int visibilityMask = (int)rendererVisibilityMasks[rendererIndex]; + int visibilityMask = rendererVisibilityMasks[rendererIndex]; bool isVisible = (visibilityMask != 0); if (!isVisible) @@ -653,8 +645,8 @@ public void Execute(int batchIndex) int allocOffsetStart = 0; if (binCount > 0) { - var drawCommandCountPerView = stackalloc int[binningConfig.viewCount]; - var visibleCountPerView = stackalloc int[binningConfig.viewCount]; + Span drawCommandCountPerView = stackalloc int[binningConfig.viewCount]; + Span visibleCountPerView = stackalloc int[binningConfig.viewCount]; for (int i = 0; i < binningConfig.viewCount; ++i) { drawCommandCountPerView[i] = 0; @@ -664,7 +656,7 @@ public void Execute(int batchIndex) bool countVisibilityStats = (debugCounterIndexBase >= 0); int shiftForVisibilityMask = 1 + (binningConfig.supportsMotionCheck ? 1 : 0) + (binningConfig.supportsCrossFade ? 1 : 0); - int *allocCounter = (int *)binAllocCounter.GetUnsafePtr(); + int* allocCounter = (int*)binAllocCounter.GetUnsafePtr(); int allocOffsetEnd = Interlocked.Add(ref UnsafeUtility.AsRef(allocCounter), binCount); allocOffsetStart = allocOffsetEnd - binCount; @@ -711,7 +703,7 @@ public void Execute(int batchIndex) int visibleCount = visibleCountPerView[viewIndex]; if (visibleCount > 0) { - int primitiveCount = GetPrimitiveCount((int)drawBatch.procInfo.indexCount, drawBatch.procInfo.topology, false); + int primitiveCount = GetPrimitiveCount((int)drawBatch.indexCount, drawBatch.topology, false); Interlocked.Add(ref UnsafeUtility.AsRef(counterPtr + (int)InstanceCullerSplitDebugCounter.VisibleInstances), visibleCount); Interlocked.Add(ref UnsafeUtility.AsRef(counterPtr + (int)InstanceCullerSplitDebugCounter.VisiblePrimitives), visibleCount * primitiveCount); @@ -842,7 +834,7 @@ unsafe public void Execute() bool isValid = true; if (allowIndirect) { - int* allocCounters = (int*)indirectAllocationCounters.GetUnsafePtr(); + int* allocCounters = (int*)indirectAllocationCounters.GetUnsafePtr(); var allocInfo = new IndirectBufferAllocInfo(); allocInfo.drawCount = outIndirectCommandIndex; @@ -896,13 +888,13 @@ unsafe public void Execute() internal unsafe struct DrawCommandOutputPerBatch : IJobParallelFor { [ReadOnly] public BinningConfig binningConfig; - [ReadOnly] public NativeParallelHashMap batchIDs; + [ReadOnly] public NativeParallelHashMap batchIDs; [ReadOnly] public GPUInstanceDataBuffer.ReadOnly instanceDataBuffer; [ReadOnly] public NativeList drawBatches; [ReadOnly] public NativeArray drawInstanceIndices; - [ReadOnly] public CPUInstanceData.ReadOnly instanceData; + [ReadOnly] public RenderWorld renderWorld; [ReadOnly] public NativeArray rendererVisibilityMasks; [ReadOnly] public NativeArray rendererMeshLodSettings; @@ -927,24 +919,28 @@ internal unsafe struct DrawCommandOutputPerBatch : IJobParallelFor int EncodeGPUInstanceIndexAndCrossFade(int rendererIndex, bool negateCrossFade) { - var gpuInstanceIndex = instanceDataBuffer.CPUInstanceToGPUInstance(InstanceHandle.FromInt(rendererIndex)); - var crossFadeValue = (int)rendererCrossFadeValues[rendererIndex]; + int instanceIndex = renderWorld.HandleToIndex(InstanceHandle.Create(rendererIndex)); + InstanceGPUHandle gpuHandle = renderWorld.gpuHandles[instanceIndex]; + GPUInstanceIndex gpuIndex = instanceDataBuffer.InstanceGPUHandleToGPUIndex(gpuHandle); + + int crossFadeValue = rendererCrossFadeValues[rendererIndex]; if (crossFadeValue == CullingJob.k_LODFadeOff) - return gpuInstanceIndex.index; + return gpuIndex.index; crossFadeValue -= (int)CullingJob.k_LODFadeZeroPacked; + if (negateCrossFade) crossFadeValue = -crossFadeValue; - gpuInstanceIndex.index |= crossFadeValue << 24; - return gpuInstanceIndex.index; + + return gpuIndex.index | (crossFadeValue << 24); } bool IsInstanceFlipped(int rendererIndex) { - InstanceHandle instance = InstanceHandle.FromInt(rendererIndex); - int instanceIndex = instanceData.InstanceToIndex(instance); - return instanceData.localToWorldIsFlippedBits.Get(instanceIndex); + InstanceHandle instance = InstanceHandle.Create(rendererIndex); + int instanceIndex = renderWorld.HandleToIndex(instance); + return renderWorld.localToWorldIsFlippedBits.Get(instanceIndex); } // This must be kept in sync with IsMeshLodVisible from binning pass @@ -994,12 +990,12 @@ unsafe public void Execute(int batchIndex) int configCount = binningConfig.visibilityConfigCount; // allocate storage for the instance offsets, set to zero - var instanceOffsetPerConfig = stackalloc int[configCount]; + Span instanceOffsetPerConfig = stackalloc int[configCount]; for (int i = 0; i < configCount; ++i) instanceOffsetPerConfig[i] = 0; // allocate storage to be able to look up the draw index per instance (by config) - var drawCommandOffsetPerConfig = stackalloc int[configCount]; + Span drawCommandOffsetPerConfig = stackalloc int[configCount]; // write the draw commands, scatter the allocated offsets to our storage // TODO: fast path when binCount == 1 @@ -1060,8 +1056,8 @@ unsafe public void Execute(int batchIndex) } #if DEBUG - if (!batchIDs.ContainsKey(drawBatch.key.overridenComponents)) - throw new Exception("Draw command created with an invalid BatchID"); + if (!batchIDs.ContainsKey(drawBatch.key.archetype)) + throw new Exception("Draw command created with an invalid GPUArchetypeHandle"); #endif if (isIndirect) { @@ -1074,23 +1070,23 @@ unsafe public void Execute(int batchIndex) indirectDrawInfoGlobalArray[drawInfoGlobalIndex] = new IndirectDrawInfo { - indexCount = drawBatch.procInfo.indexCount, - firstIndex = drawBatch.procInfo.firstIndex, - baseVertex = drawBatch.procInfo.baseVertex, + indexCount = drawBatch.indexCount, + firstIndex = drawBatch.firstIndex, + baseVertex = drawBatch.baseVertex, firstInstanceGlobalIndex = (uint)instanceInfoGlobalIndex, - maxInstanceCountAndTopology = ((uint)visibleInstanceCount << 3) | (uint)drawBatch.procInfo.topology, + maxInstanceCountAndTopology = ((uint)visibleInstanceCount << 3) | (uint)drawBatch.topology, }; output.indirectDrawCommands[drawCommandOffset] = new BatchDrawCommandIndirect { flags = drawFlags, visibleOffset = (uint)instanceInfoGlobalIndex, - batchID = batchIDs[drawBatch.key.overridenComponents], + batchID = batchIDs[drawBatch.key.archetype], materialID = drawBatch.key.materialID, splitVisibilityMask = (ushort)visibilityMask, lightmapIndex = (ushort)drawBatch.key.lightmapIndex, sortingPosition = sortingPosition, meshID = drawBatch.key.meshID, - topology = drawBatch.procInfo.topology, + topology = drawBatch.topology, visibleInstancesBufferHandle = visibleInstancesBufferHandle, indirectArgsBufferHandle = indirectArgsBufferHandle, indirectArgsBufferOffset = (uint)(drawInfoGlobalIndex * GraphicsBuffer.IndirectDrawIndexedArgs.size), @@ -1107,7 +1103,7 @@ unsafe public void Execute(int batchIndex) flags = drawFlags, visibleOffset = (uint)visibleInstanceOffset, visibleCount = (uint)visibleInstanceCount, - batchID = batchIDs[drawBatch.key.overridenComponents], + batchID = batchIDs[drawBatch.key.archetype], materialID = drawBatch.key.materialID, splitVisibilityMask = (ushort)visibilityMask, lightmapIndex = (ushort)drawBatch.key.lightmapIndex, @@ -1226,10 +1222,10 @@ unsafe public void Execute(int batchIndex) // use the first instance position of each batch as the sorting position if necessary if ((drawBatch.key.flags & BatchDrawCommandFlags.HasSortingPosition) != 0) { - InstanceHandle instance = InstanceHandle.FromInt(lastRendererIndex & 0xffffff); - int instanceIndex = instanceData.InstanceToIndex(instance); + InstanceHandle instance = InstanceHandle.Create(lastRendererIndex & 0xffffff); + int instanceIndex = renderWorld.HandleToIndex(instance); - ref readonly AABB worldAABB = ref instanceData.worldAABBs.UnsafeElementAt(instanceIndex); + ref readonly AABB worldAABB = ref renderWorld.worldAABBs.ElementAt(instanceIndex); float3 position = worldAABB.center; int globalCommandOffset = batchDrawCommandOffset; @@ -1247,7 +1243,7 @@ unsafe public void Execute(int batchIndex) [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] internal unsafe struct CompactVisibilityMasksJob : IJobParallelForBatch { - public const int k_BatchSize = 64; + public const int MaxBatchSize = 64; [ReadOnly] public NativeArray rendererVisibilityMasks; @@ -1255,21 +1251,188 @@ internal unsafe struct CompactVisibilityMasksJob : IJobParallelForBatch unsafe public void Execute(int startIndex, int count) { + Assert.IsTrue(MaxBatchSize <= 64 && count <= 64); ulong chunkBits = 0; - for(int i = 0; i < count; ++i) + for (int i = 0; i < count; ++i) { var visibilityMask = rendererVisibilityMasks[startIndex + i]; - if(visibilityMask != 0) - chunkBits |= (1ul << i); + if (visibilityMask != 0) + chunkBits |= 1ul << i; } - var chunkIndex = startIndex / k_BatchSize; + var chunkIndex = startIndex / MaxBatchSize; compactedVisibilityMasks.InterlockedOrChunk(chunkIndex, chunkBits); } } + [BurstCompile] + internal struct IncludeExcludeListFilter + { + public NativeParallelHashSet IncludeInstanceIDs; + public NativeParallelHashSet ExcludeInstanceIDs; + public bool IsIncludeEnabled; + public bool IsExcludeEnabled; + + public bool IsEnabled => IsIncludeEnabled || IsExcludeEnabled; + public bool IsIncludeEmpty => IncludeInstanceIDs.IsEmpty; + public bool IsExcludeEmpty => ExcludeInstanceIDs.IsEmpty; + + public IncludeExcludeListFilter( + NativeArray includeGameObjects, + NativeArray includeEntities, + NativeArray excludeGameObjects, + NativeArray excludeEntities, + Allocator allocator) + { + IncludeInstanceIDs = default; + ExcludeInstanceIDs = default; + + // Null NativeArray means that the list shoudln't be used for filtering + IsIncludeEnabled = includeEntities.IsCreated || includeGameObjects.IsCreated; + IsExcludeEnabled = excludeEntities.IsCreated || excludeGameObjects.IsCreated; + + if (IsIncludeEnabled) + { + IncludeInstanceIDs = new NativeParallelHashSet(includeGameObjects.Length + includeEntities.Length, allocator); + for (int i = 0; i < includeGameObjects.Length; ++i) + IncludeInstanceIDs.Add(includeGameObjects[i]); + + for (int i = 0; i < includeEntities.Length; ++i) + IncludeInstanceIDs.Add(includeEntities[i]); + } + else + { + // NativeParallelHashSet must be non-null even if empty to be passed to jobs. Otherwise errors happen. + IncludeInstanceIDs = new NativeParallelHashSet(0, allocator); + } + + if (IsExcludeEnabled) + { + ExcludeInstanceIDs = new NativeParallelHashSet(excludeGameObjects.Length + excludeEntities.Length, allocator); + for (int i = 0; i < excludeGameObjects.Length; ++i) + ExcludeInstanceIDs.Add(excludeGameObjects[i]); + for (int i = 0; i < excludeEntities.Length; ++i) + ExcludeInstanceIDs.Add(excludeEntities[i]); + } + else + { + // NativeParallelHashSet must be non-null even if empty to be passed to jobs. Otherwise errors happen. + ExcludeInstanceIDs = new NativeParallelHashSet(0, allocator); + } + } + + public void Dispose() + { + if (IncludeInstanceIDs.IsCreated) + IncludeInstanceIDs.Dispose(); + + if (ExcludeInstanceIDs.IsCreated) + ExcludeInstanceIDs.Dispose(); + } + + public JobHandle Dispose(JobHandle dependencies) + { + JobHandle disposeInclude = IncludeInstanceIDs.IsCreated ? IncludeInstanceIDs.Dispose(dependencies) : default; + JobHandle disposeExclude = ExcludeInstanceIDs.IsCreated ? ExcludeInstanceIDs.Dispose(dependencies) : default; + return JobHandle.CombineDependencies(disposeInclude, disposeExclude); + } + + public bool DoesPassFilter(EntityId instanceID) + { + if (IsIncludeEnabled) + { + if (!IncludeInstanceIDs.Contains(instanceID)) + return false; + } + + if (IsExcludeEnabled) + { + if (ExcludeInstanceIDs.Contains(instanceID)) + return false; + } + + return true; + } + + public static IncludeExcludeListFilter GetEmptyFilter(Allocator allocator) + { + return new IncludeExcludeListFilter( + default, + default, + default, + default, + Allocator.TempJob); + } + +#if UNITY_EDITOR + // This function does only return a meaningful IncludeExcludeListFilter object when called from a BRG culling callback. + public static IncludeExcludeListFilter GetFilterForCurrentCullingCallback(in BatchCullingContext cullingContext, Allocator allocator) + { + PickingIncludeExcludeEntityIdList includeExcludeList = default; + + if (cullingContext.viewType == BatchCullingViewType.Picking) + { + includeExcludeList = HandleUtility.GetPickingIncludeExcludeEntityIdList(Allocator.Temp); + } + else if (cullingContext.viewType == BatchCullingViewType.SelectionOutline) + { + includeExcludeList = HandleUtility.GetSelectionOutlineIncludeExcludeEntityIdList(Allocator.Temp); + } + else + { + return GetEmptyFilter(allocator); + } + + NativeArray emptyArray = new NativeArray(0, Allocator.Temp); + NativeArray includeGameObjects = includeExcludeList.IncludeRenderers; + NativeArray includeEntities = includeExcludeList.IncludeEntities; + if (cullingContext.viewType == BatchCullingViewType.SelectionOutline) + { + // Make sure the include list for the selection outline is never null even if there is nothing in it. + // Null NativeArray and empty NativeArray are treated as different things when used to construct an IncludeExcludeListFilter object: + // - Null include list means that nothing is discarded because the filtering is skipped. + // - Empty include list means that everything is discarded because the filtering is enabled but never passes. + // With selection outline culling, we want the filtering to happen in any case even if the array contains nothing so that we don't highlight everything in the latter case. + if (!includeGameObjects.IsCreated) + includeGameObjects = emptyArray; + + if (!includeEntities.IsCreated) + includeEntities = emptyArray; + } + else + { + if (includeGameObjects.Length == 0) + includeGameObjects = default; + + if (includeEntities.Length == 0) + includeEntities = default; + } + + NativeArray excludeGameObjects = includeExcludeList.ExcludeRenderers; + if (excludeGameObjects.Length == 0) + excludeGameObjects = default; + + NativeArray excludeEntities = includeExcludeList.ExcludeEntities; + if (excludeEntities.Length == 0) + excludeEntities = default; + + IncludeExcludeListFilter includeExcludeListFilter = new IncludeExcludeListFilter( + includeGameObjects, + includeEntities, + excludeGameObjects, + excludeEntities, + allocator); + + includeExcludeList.Dispose(); + emptyArray.Dispose(); + + return includeExcludeListFilter; + } +#endif + } + #if UNITY_EDITOR internal enum FilteringJobMode { @@ -1280,8 +1443,8 @@ internal enum FilteringJobMode [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] internal unsafe struct DrawCommandOutputFiltering : IJob { - [ReadOnly] public NativeParallelHashMap batchIDs; - [ReadOnly] public int viewID; + [ReadOnly] public NativeParallelHashMap batchIDs; + [ReadOnly] public EntityId viewID; [ReadOnly] public GPUInstanceDataBuffer.ReadOnly instanceDataBuffer; @@ -1289,8 +1452,7 @@ internal unsafe struct DrawCommandOutputFiltering : IJob [ReadOnly] public NativeArray rendererMeshLodSettings; [ReadOnly] public NativeArray rendererCrossFadeValues; - [ReadOnly] public CPUInstanceData.ReadOnly instanceData; - [ReadOnly] public CPUSharedInstanceData.ReadOnly sharedInstanceData; + [ReadOnly] public RenderWorld renderWorld; [ReadOnly] public NativeArray drawInstanceIndices; [ReadOnly] public NativeList drawBatches; @@ -1298,7 +1460,7 @@ internal unsafe struct DrawCommandOutputFiltering : IJob [ReadOnly] public NativeArray drawBatchIndices; [ReadOnly] public NativeArray filteringResults; - [ReadOnly] public NativeArray excludedRenderers; + [ReadOnly] public IncludeExcludeListFilter includeExcludeListFilter; [ReadOnly] public FilteringJobMode mode; @@ -1351,14 +1513,14 @@ public void Execute() if (visibilityMask == 0) continue; - InstanceHandle instance = InstanceHandle.FromInt(rendererIndex); - int sharedInstanceIndex = sharedInstanceData.InstanceToIndex(instanceData, instance); + InstanceHandle instance = InstanceHandle.Create(rendererIndex); + int instanceIndex = renderWorld.HandleToIndex(instance); - if (mode == FilteringJobMode.Filtering && filteringResults.IsCreated && (sharedInstanceIndex >= filteringResults.Length || !filteringResults[sharedInstanceIndex])) + if (mode == FilteringJobMode.Filtering && filteringResults.IsCreated && (instanceIndex >= filteringResults.Length || !filteringResults[instanceIndex])) continue; - var rendererID = sharedInstanceData.rendererGroupIDs[sharedInstanceIndex]; - if (mode == FilteringJobMode.Picking && excludedRenderers.IsCreated && excludedRenderers.Contains(rendererID)) + EntityId rendererID = renderWorld.instanceIDs[instanceIndex]; + if (mode == FilteringJobMode.Picking && !includeExcludeListFilter.DoesPassFilter(rendererID)) continue; #if DEBUG @@ -1368,17 +1530,20 @@ public void Execute() if (outCommandIndex >= output.drawCommandCount) throw new Exception("Exceeding draw command count"); - if (!batchIDs.ContainsKey(drawBatch.key.overridenComponents)) - throw new Exception("Draw command created with an invalid BatchID"); + if (!batchIDs.ContainsKey(drawBatch.key.archetype)) + throw new Exception("Draw command created with an invalid GPUArchetypeHandle"); #endif - output.visibleInstances[outVisibleInstanceIndex] = instanceDataBuffer.CPUInstanceToGPUInstance(instance).index; + InstanceGPUHandle gpuHandle = renderWorld.gpuHandles[instanceIndex]; + GPUInstanceIndex gpuIndex = instanceDataBuffer.InstanceGPUHandleToGPUIndex(gpuHandle); + + output.visibleInstances[outVisibleInstanceIndex] = gpuIndex.index; output.drawCommandPickingEntityIds[outCommandIndex] = rendererID; output.drawCommands[outCommandIndex] = new BatchDrawCommand { flags = BatchDrawCommandFlags.None, visibleOffset = (uint)outVisibleInstanceIndex, - visibleCount = (uint)1, - batchID = batchIDs[drawBatch.key.overridenComponents], + visibleCount = 1, + batchID = batchIDs[drawBatch.key.archetype], materialID = drawBatch.key.materialID, splitVisibilityMask = 0x1, lightmapIndex = (ushort)drawBatch.key.lightmapIndex, @@ -1434,23 +1599,18 @@ public void Execute() [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] internal struct CullSceneViewHiddenRenderersJob : IJobParallelFor { - public const int k_BatchSize = 128; - - [ReadOnly] public CPUInstanceData.ReadOnly instanceData; - [ReadOnly] public CPUSharedInstanceData.ReadOnly sharedInstanceData; + [ReadOnly] public RenderWorld renderWorld; [ReadOnly] public ParallelBitArray hiddenBits; [NativeDisableParallelForRestriction] public NativeArray rendererVisibilityMasks; public void Execute(int instanceIndex) { - InstanceHandle instance = instanceData.instances[instanceIndex]; + InstanceHandle instance = renderWorld.indexToHandle[instanceIndex]; if (rendererVisibilityMasks[instance.index] > 0) { - int sharedInstanceIndex = sharedInstanceData.InstanceToIndex(instanceData, instance); - - if (hiddenBits.Get(sharedInstanceIndex)) + if (hiddenBits.Get(instanceIndex)) rendererVisibilityMasks[instance.index] = 0; } } @@ -1472,7 +1632,7 @@ internal struct InstanceCullerSplitDebugArray : IDisposable internal struct Info { public BatchCullingViewType viewType; - public EntityId viewInstanceID; + public EntityId viewID; public int splitIndex; } @@ -1496,7 +1656,7 @@ public void Dispose() m_CounterSync.Dispose(); } - public int TryAddSplits(BatchCullingViewType viewType, EntityId viewInstanceID, int splitCount) + public int TryAddSplits(BatchCullingViewType viewType, EntityId viewID, int splitCount) { int baseIndex = m_Info.Length; if (baseIndex + splitCount > MaxSplitCount) @@ -1507,7 +1667,7 @@ public int TryAddSplits(BatchCullingViewType viewType, EntityId viewInstanceID, m_Info.Add(new Info() { viewType = viewType, - viewInstanceID = viewInstanceID, + viewID = viewID, splitIndex = splitIndex, }); } @@ -1537,7 +1697,7 @@ public void MoveToDebugStatsAndClear(DebugRendererBatcherStats debugStats) debugStats.instanceCullerStats.Add(new InstanceCullerViewStats { viewType = info.viewType, - viewInstanceID = info.viewInstanceID, + viewID = info.viewID, splitIndex = info.splitIndex, visibleInstancesOnCPU = m_Counters[counterBase + (int)InstanceCullerSplitDebugCounter.VisibleInstances], visibleInstancesOnGPU = 0, // Unknown at this point, will be filled in later @@ -1560,7 +1720,7 @@ internal struct InstanceOcclusionEventDebugArray : IDisposable internal struct Info { - public EntityId viewInstanceID; + public EntityId viewID; public InstanceOcclusionEventType eventType; public int occluderVersion; public int subviewMask; @@ -1614,7 +1774,7 @@ public void Dispose() m_CounterBuffer.Dispose(); } - public int TryAdd(EntityId viewInstanceID, InstanceOcclusionEventType eventType, int occluderVersion, int subviewMask, OcclusionTest occlusionTest) + public int TryAdd(EntityId viewID, InstanceOcclusionEventType eventType, int occluderVersion, int subviewMask, OcclusionTest occlusionTest) { int passIndex = m_PendingInfo.Length; if (passIndex + 1 > MaxPassCount) @@ -1622,7 +1782,7 @@ public int TryAdd(EntityId viewInstanceID, InstanceOcclusionEventType eventType, m_PendingInfo.Add(new Info() { - viewInstanceID = viewInstanceID, + viewID = viewID, eventType = eventType, occluderVersion = occluderVersion, subviewMask = subviewMask, @@ -1682,7 +1842,7 @@ public void MoveToDebugStatsAndClear(DebugRendererBatcherStats debugStats) for (int prevIndex = 0; prevIndex < index; ++prevIndex) { var prevInfo = m_LatestInfo[prevIndex]; - if (prevInfo.HasVersion() && prevInfo.viewInstanceID == info.viewInstanceID) + if (prevInfo.HasVersion() && prevInfo.viewID == info.viewID) { occluderVersion = info.occluderVersion - prevInfo.occluderVersion; break; @@ -1698,7 +1858,7 @@ public void MoveToDebugStatsAndClear(DebugRendererBatcherStats debugStats) debugStats.instanceOcclusionEventStats.Add(new InstanceOcclusionEventStats { - viewInstanceID = info.viewInstanceID, + viewID = info.viewID, eventType = info.eventType, occluderVersion = occluderVersion, subviewMask = info.subviewMask, @@ -1718,7 +1878,7 @@ public void MoveToDebugStatsAndClear(DebugRendererBatcherStats debugStats) } } - internal struct InstanceCuller : IDisposable + internal class InstanceCuller : IDisposable { private struct AnimatedFadeData { @@ -1768,7 +1928,7 @@ private static class ShaderIDs public static readonly int _OcclusionDebugCounters = Shader.PropertyToID("_OcclusionDebugCounters"); } - internal void Init(GPUResidentDrawerResources resources, DebugRendererBatcherStats debugStats = null) + internal void Initialize(GPUResidentDrawerResources resources, DebugRendererBatcherStats debugStats = null) { m_IndirectStorage.Init(); @@ -1796,66 +1956,70 @@ internal void Init(GPUResidentDrawerResources resources, DebugRendererBatcherSta // This relies on the fact that camera culling is scheduled ahead of shadow culling. - private JobHandle AnimateCrossFades(CPUPerCameraInstanceData perCameraInstanceData, BatchCullingContext cc, out CPUPerCameraInstanceData.PerCameraInstanceDataArrays cameraInstanceData, out bool hasAnimatedCrossfade) + private JobHandle AnimateCrossFades(in RenderWorld renderWorld, + in BatchCullingContext context, + out RenderWorld.PerCameraInstanceData perCameraInstanceData, + out bool hasAnimatedCrossfade) { - var lodHash = cc.lodParameters.GetHashCode(); + perCameraInstanceData = default; + + int lodHash = context.lodParameters.GetHashCode(); hasAnimatedCrossfade = m_LODParamsToCameraID.TryGetValue(lodHash, out var animatedFadeData); if (hasAnimatedCrossfade) { - cameraInstanceData = perCameraInstanceData.perCameraData[animatedFadeData.cameraID]; - Assert.IsTrue(cameraInstanceData.IsCreated); + perCameraInstanceData = renderWorld.GetPerCameraInstanceData(animatedFadeData.cameraID); return animatedFadeData.jobHandle; } - if (cc.viewType != BatchCullingViewType.Camera && !hasAnimatedCrossfade) + if (context.viewType != BatchCullingViewType.Camera && !hasAnimatedCrossfade) { // For picking / filtering and outlining passes. We do not have animated crossfade data. - cameraInstanceData = new CPUPerCameraInstanceData.PerCameraInstanceDataArrays(); return new JobHandle(); } - //For main camera, animate crossfades, and store the result in the hashmap to be retrieved by other cameras - var viewID = cc.viewID.GetInstanceID(); -#pragma warning disable 618 // todo @emilie.thaulow make viewID an EntityId - hasAnimatedCrossfade = perCameraInstanceData.perCameraData.TryGetValue(viewID, out var tmpCameraInstanceData); -#pragma warning restore 618 - if (hasAnimatedCrossfade == false) + // For main camera, animate crossfades, and store the result in the hashmap to be retrieved by other cameras + EntityId viewID = EntityId.From(context.viewID.GetInstanceID()); + + if (!renderWorld.TryGetPerCameraInstanceData(viewID, out perCameraInstanceData)) { // For picking / filtering and outlining passes. We do not have animated crossfade data. - cameraInstanceData = new CPUPerCameraInstanceData.PerCameraInstanceDataArrays(); + hasAnimatedCrossfade = false; return new JobHandle(); } - cameraInstanceData = tmpCameraInstanceData; - Assert.IsTrue(cameraInstanceData.IsCreated); + hasAnimatedCrossfade = true; - var handle = new AnimateCrossFadeJob() + var animatedCrossFadesJob = new AnimateCrossFadeJob { deltaTime = Time.deltaTime, - crossFadeArray = cameraInstanceData.crossFades - }.Schedule(perCameraInstanceData.instancesLength, AnimateCrossFadeJob.k_BatchSize); + crossFadeArray = perCameraInstanceData.crossFades + } + .Schedule(renderWorld.instanceCount, AnimateCrossFadeJob.k_BatchSize); + + m_LODParamsToCameraID.TryAdd(lodHash, new AnimatedFadeData + { + cameraID = viewID, + jobHandle = animatedCrossFadesJob + }); -#pragma warning disable 618 // todo @emilie.thaulow make viewID an EntityId - m_LODParamsToCameraID.TryAdd(lodHash, new AnimatedFadeData(){ cameraID = viewID, jobHandle = handle}); -#pragma warning restore 618 - return handle; + return animatedCrossFadesJob; } - private unsafe JobHandle CreateFrustumCullingJob( - in BatchCullingContext cc, - in CPUInstanceData.ReadOnly instanceData, - in CPUSharedInstanceData.ReadOnly sharedInstanceData, - in CPUPerCameraInstanceData perCameraInstanceData, + private unsafe JobHandle ScheduleFrustumCullingJob( + in BatchCullingContext context, + in RenderWorld renderWorld, + in NativeParallelHashMap meshMap, NativeList lodGroupCullingData, in BinningConfig binningConfig, float smallMeshScreenPercentage, OcclusionCullingCommon occlusionCullingCommon, + in IncludeExcludeListFilter includeExcludeListFilter, NativeArray rendererVisibilityMasks, NativeArray rendererMeshLodSettings, NativeArray rendererCrossFadeValues) { - Assert.IsTrue(cc.cullingSplits.Length <= 6, "InstanceCuller supports up to 6 culling splits."); + Assert.IsTrue(context.cullingSplits.Length <= 6, "InstanceCuller supports up to 6 culling splits."); ReceiverPlanes receiverPlanes; ReceiverSphereCuller receiverSphereCuller; @@ -1863,50 +2027,66 @@ private unsafe JobHandle CreateFrustumCullingJob( float screenRelativeMetric; float meshLodConstant; - fixed (BatchCullingContext* contextPtr = &cc) + fixed (BatchCullingContext* contextPtr = &context) { - InstanceCullerBurst.SetupCullingJobInput(QualitySettings.lodBias, QualitySettings.meshLodThreshold, contextPtr, &receiverPlanes, &receiverSphereCuller, - &frustumPlaneCuller, &screenRelativeMetric, &meshLodConstant); + InstanceCullerBurst.SetupCullingJobInput(QualitySettings.lodBias, + QualitySettings.meshLodThreshold, + contextPtr, + &receiverPlanes, + &receiverSphereCuller, + &frustumPlaneCuller, + &screenRelativeMetric, + &meshLodConstant); } -#pragma warning disable 618 // todo @emilie.thaulow make GetInstanceID return EntityId + if (occlusionCullingCommon != null) - occlusionCullingCommon.UpdateSilhouettePlanes(cc.viewID.GetInstanceID(), receiverPlanes.SilhouettePlaneSubArray()); -#pragma warning restore 618 + occlusionCullingCommon.UpdateSilhouettePlanes(EntityId.From(context.viewID.GetInstanceID()), receiverPlanes.SilhouettePlaneSubArray()); - var jobHandle = AnimateCrossFades(perCameraInstanceData, cc, out var cameraInstanceData, out var hasAnimatedCrossfade); + JobHandle animatedCrossFadesJob = AnimateCrossFades(renderWorld, context, + out RenderWorld.PerCameraInstanceData perCameraInstanceData, + out bool hasAnimatedCrossfade); + var emptyPerCameraInstanceData = new RenderWorld.PerCameraInstanceData(0, Allocator.TempJob); + if (!perCameraInstanceData.IsCreated) + perCameraInstanceData = emptyPerCameraInstanceData; // Set struct with empty NativeArray otherwise safety checks will complain + var cullingJob = new CullingJob { + renderWorld = renderWorld, + meshMap = meshMap, binningConfig = binningConfig, - viewType = cc.viewType, + viewType = context.viewType, frustumPlanePackets = frustumPlaneCuller.planePackets.AsArray(), frustumSplitInfos = frustumPlaneCuller.splitInfos.AsArray(), lightFacingFrustumPlanes = receiverPlanes.LightFacingFrustumPlaneSubArray(), receiverSplitInfos = receiverSphereCuller.splitInfos.AsArray(), worldToLightSpaceRotation = receiverSphereCuller.worldToLightSpaceRotation, - cullLightmappedShadowCasters = (cc.cullingFlags & BatchCullingFlags.CullLightmappedShadowCasters) != 0, - cameraPosition = cc.lodParameters.cameraPosition, + cullLightmappedShadowCasters = (context.cullingFlags & BatchCullingFlags.CullLightmappedShadowCasters) != 0, + cameraPosition = context.lodParameters.cameraPosition, sqrMeshLodSelectionConstant = meshLodConstant * meshLodConstant, sqrScreenRelativeMetric = screenRelativeMetric * screenRelativeMetric, minScreenRelativeHeight = smallMeshScreenPercentage * 0.01f, - isOrtho = cc.lodParameters.isOrthographic, + isOrtho = context.lodParameters.isOrthographic, + maxLOD = QualitySettings.maximumLODLevel, + cullingLayerMask = context.cullingLayerMask, + sceneCullingMask = context.sceneCullingMask, +#if UNITY_EDITOR + includeExcludeListFilter = includeExcludeListFilter, +#endif animateCrossFades = hasAnimatedCrossfade, - instanceData = instanceData, - sharedInstanceData = sharedInstanceData, - cameraInstanceData = cameraInstanceData, lodGroupCullingData = lodGroupCullingData, - occlusionBuffer = cc.occlusionBuffer, + occlusionBuffer = context.occlusionBuffer, rendererVisibilityMasks = rendererVisibilityMasks, rendererMeshLodSettings = rendererMeshLodSettings, rendererCrossFadeValues = rendererCrossFadeValues, - maxLOD = QualitySettings.maximumLODLevel, - cullingLayerMask = cc.cullingLayerMask, - sceneCullingMask = cc.sceneCullingMask, - }.Schedule(instanceData.instancesLength, CullingJob.k_BatchSize, jobHandle); + perCameraInstanceData = perCameraInstanceData, + } + .Schedule(renderWorld.instanceCount, 64, animatedCrossFadesJob); receiverPlanes.Dispose(cullingJob); frustumPlaneCuller.Dispose(cullingJob); receiverSphereCuller.Dispose(cullingJob); + emptyPerCameraInstanceData.Dispose(cullingJob); return cullingJob; } @@ -1945,17 +2125,17 @@ private int ComputeWorstCaseDrawCommandCount( } public unsafe JobHandle CreateCullJobTree( - in BatchCullingContext cc, + in BatchCullingContext context, BatchCullingOutput cullingOutput, - in CPUInstanceData.ReadOnly instanceData, - in CPUSharedInstanceData.ReadOnly sharedInstanceData, - in CPUPerCameraInstanceData perCameraInstanceData, + in RenderWorld renderWorld, + in NativeParallelHashMap meshMap, in GPUInstanceDataBuffer.ReadOnly instanceDataBuffer, NativeList lodGroupCullingData, CPUDrawInstanceData drawInstanceData, - NativeParallelHashMap batchIDs, + NativeParallelHashMap batchIDs, float smallMeshScreenPercentage, - OcclusionCullingCommon occlusionCullingCommon) + OcclusionCullingCommon occlusionCullingCommon, + in IncludeExcludeListFilter includeExcludeListFilter) { // allocate for worst case number of draw ranges (all other arrays allocated after size is known) var drawCommands = new BatchCullingOutputDrawCommands(); @@ -1968,18 +2148,27 @@ public unsafe JobHandle CreateCullJobTree( var binningConfig = new BinningConfig { - viewCount = cc.cullingSplits.Length, + viewCount = context.cullingSplits.Length, supportsCrossFade = QualitySettings.enableLODCrossFade, - supportsMotionCheck = (cc.viewType == BatchCullingViewType.Camera), // TODO: could disable here if RP never needs object motion vectors, for now always batch on it + supportsMotionCheck = context.viewType == BatchCullingViewType.Camera, // TODO: could disable here if RP never needs object motion vectors, for now always batch on it }; - var visibilityLength = instanceData.handlesLength; + var visibilityLength = renderWorld.handleCount; var rendererVisibilityMasks = new NativeArray(visibilityLength, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); var rendererCrossFadeValues = new NativeArray(visibilityLength, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); var rendererMeshLodSettings = new NativeArray(visibilityLength, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); - var cullingJobHandle = CreateFrustumCullingJob(cc, instanceData, sharedInstanceData, perCameraInstanceData, lodGroupCullingData, binningConfig, - smallMeshScreenPercentage, occlusionCullingCommon, rendererVisibilityMasks, rendererMeshLodSettings, rendererCrossFadeValues); + JobHandle cullingJobHandle = ScheduleFrustumCullingJob(context, + renderWorld, + meshMap, + lodGroupCullingData, + binningConfig, + smallMeshScreenPercentage, + occlusionCullingCommon, + includeExcludeListFilter, + rendererVisibilityMasks, + rendererMeshLodSettings, + rendererCrossFadeValues); #if UNITY_EDITOR // Unfortunately BatchCullingContext doesn't provide full visibility and picking context. @@ -1988,37 +2177,56 @@ public unsafe JobHandle CreateCullJobTree( // This should be redesigned in the future. Culler should not be responsible for custom editor handling logic or even know that the editor exist. // This additionally culls game objects hidden in the hierarchy panel or the scene view or in context editing. - cullingJobHandle = CreateSceneViewHiddenObjectsCullingJob_EditorOnly(cc, instanceData, sharedInstanceData, rendererVisibilityMasks, + cullingJobHandle = ScheduleSceneViewHiddenObjectsCullingJob_EditorOnly(context, + renderWorld, + rendererVisibilityMasks, cullingJobHandle); - if (cc.viewType == BatchCullingViewType.Picking) + if (context.viewType == BatchCullingViewType.Picking) { // This outputs picking draw commands for the objects that can be picked. - cullingJobHandle = CreatePickingCullingOutputJob_EditorOnly(cc, cullingOutput, instanceData, sharedInstanceData, instanceDataBuffer, - drawInstanceData, batchIDs, rendererVisibilityMasks, rendererMeshLodSettings, rendererCrossFadeValues, cullingJobHandle); + cullingJobHandle = SchedulePickingCullingOutputJob_EditorOnly(context, + cullingOutput, + renderWorld, + instanceDataBuffer, + drawInstanceData, + includeExcludeListFilter, + batchIDs, + rendererVisibilityMasks, + rendererMeshLodSettings, + rendererCrossFadeValues, + cullingJobHandle); } - else if (cc.viewType == BatchCullingViewType.Filtering) + else if (context.viewType == BatchCullingViewType.Filtering) { // This outputs draw commands for the objects filtered by search input in the hierarchy on in the scene view. - cullingJobHandle = CreateFilteringCullingOutputJob_EditorOnly(cc, cullingOutput, instanceData, sharedInstanceData, instanceDataBuffer, - drawInstanceData, batchIDs, rendererVisibilityMasks, rendererMeshLodSettings, rendererCrossFadeValues, cullingJobHandle); + cullingJobHandle = ScheduleFilteringCullingOutputJob_EditorOnly(context, + cullingOutput, + renderWorld, + instanceDataBuffer, + drawInstanceData, + includeExcludeListFilter, + batchIDs, + rendererVisibilityMasks, + rendererMeshLodSettings, + rendererCrossFadeValues, + cullingJobHandle); } #endif // This outputs regular draw commands. - if (cc.viewType == BatchCullingViewType.Camera || cc.viewType == BatchCullingViewType.Light || cc.viewType == BatchCullingViewType.SelectionOutline) + if (context.viewType == BatchCullingViewType.Camera || context.viewType == BatchCullingViewType.Light || context.viewType == BatchCullingViewType.SelectionOutline) { - cullingJobHandle = CreateCompactedVisibilityMaskJob(instanceData, rendererVisibilityMasks, cullingJobHandle); + cullingJobHandle = ScheduleCompactedVisibilityMaskJob(renderWorld, rendererVisibilityMasks, cullingJobHandle); + EntityId viewID = EntityId.From(context.viewID.GetInstanceID()); int debugCounterBaseIndex = -1; if (m_DebugStats?.enabled ?? false) { -#pragma warning disable 618 // todo @emilie.thaulow make GetInstanceID return EntityId - debugCounterBaseIndex = m_SplitDebugArray.TryAddSplits(cc.viewType, cc.viewID.GetInstanceID(), cc.cullingSplits.Length); -#pragma warning restore 618 + debugCounterBaseIndex = m_SplitDebugArray.TryAddSplits(context.viewType, viewID, context.cullingSplits.Length); } var batchCount = drawInstanceData.drawBatches.Length; - int maxBinCount = ComputeWorstCaseDrawCommandCount(cc, binningConfig, drawInstanceData); + int maxBinCount = ComputeWorstCaseDrawCommandCount(context, binningConfig, drawInstanceData); var batchBinAllocOffsets = new NativeArray(batchCount, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); var batchBinCounts = new NativeArray(batchCount, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); @@ -2029,17 +2237,12 @@ public unsafe JobHandle CreateCullJobTree( var binVisibleInstanceCounts = new NativeArray(maxBinCount, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); var binVisibleInstanceOffsets = new NativeArray(maxBinCount, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); + bool useOcclusionCulling = (occlusionCullingCommon != null) && occlusionCullingCommon.HasOccluderContext(viewID); int indirectContextIndex = -1; -#pragma warning disable 618 //todo @emilie.thaulow make GetInstanceID return EntityId - bool useOcclusionCulling = (occlusionCullingCommon != null) && occlusionCullingCommon.HasOccluderContext(cc.viewID.GetInstanceID()); -#pragma warning restore 618 if (useOcclusionCulling) { -#pragma warning disable 618 // todo @emilie.thaulow make GetInstanceID return EntityId - int viewInstanceID = cc.viewID.GetInstanceID(); - indirectContextIndex = m_IndirectStorage.TryAllocateContext(viewInstanceID); -#pragma warning restore 618 - cullingOutput.customCullingResult[0] = (IntPtr)viewInstanceID; + indirectContextIndex = m_IndirectStorage.TryAllocateContext(viewID); + cullingOutput.customCullingResult[0] = (IntPtr)viewID.GetRawData(); } IndirectBufferLimits indirectBufferLimits = m_IndirectStorage.GetLimits(indirectContextIndex); NativeArray indirectBufferAllocInfo = m_IndirectStorage.GetAllocInfoSubArray(indirectContextIndex); @@ -2049,7 +2252,7 @@ public unsafe JobHandle CreateCullJobTree( binningConfig = binningConfig, drawBatches = drawInstanceData.drawBatches, drawInstanceIndices = drawInstanceData.drawInstanceIndices, - instanceData = instanceData, + renderWorld = renderWorld, rendererVisibilityMasks = rendererVisibilityMasks, rendererMeshLodSettings = rendererMeshLodSettings, batchBinAllocOffsets = batchBinAllocOffsets, @@ -2059,11 +2262,10 @@ public unsafe JobHandle CreateCullJobTree( binVisibleInstanceCounts = binVisibleInstanceCounts, splitDebugCounters = m_SplitDebugArray.Counters, debugCounterIndexBase = debugCounterBaseIndex, - }; - - var allocateBinsHandle = allocateBinsJob.Schedule(batchCount, 1, cullingJobHandle); + } + .Schedule(batchCount, 1, cullingJobHandle); - m_SplitDebugArray.AddSync(debugCounterBaseIndex, allocateBinsHandle); + m_SplitDebugArray.AddSync(debugCounterBaseIndex, allocateBinsJob); var prefixSumJob = new PrefixSumDrawsAndInstances { @@ -2078,9 +2280,8 @@ public unsafe JobHandle CreateCullJobTree( indirectBufferLimits = indirectBufferLimits, indirectBufferAllocInfo = indirectBufferAllocInfo, indirectAllocationCounters = m_IndirectStorage.allocationCounters, - }; - - var prefixSumHandle = prefixSumJob.Schedule(allocateBinsHandle); + } + .Schedule(allocateBinsJob); var drawCommandOutputJob = new DrawCommandOutputPerBatch { @@ -2089,7 +2290,7 @@ public unsafe JobHandle CreateCullJobTree( instanceDataBuffer = instanceDataBuffer, drawBatches = drawInstanceData.drawBatches, drawInstanceIndices = drawInstanceData.drawInstanceIndices, - instanceData = instanceData, + renderWorld = renderWorld, rendererVisibilityMasks = rendererVisibilityMasks, rendererMeshLodSettings = rendererMeshLodSettings, rendererCrossFadeValues = rendererCrossFadeValues, @@ -2106,14 +2307,13 @@ public unsafe JobHandle CreateCullJobTree( indirectBufferAllocInfo = indirectBufferAllocInfo, indirectInstanceInfoGlobalArray = m_IndirectStorage.instanceInfoGlobalArray, indirectDrawInfoGlobalArray = m_IndirectStorage.drawInfoGlobalArray, - }; - - var drawCommandOutputHandle = drawCommandOutputJob.Schedule(batchCount, 1, prefixSumHandle); + } + .Schedule(batchCount, 1, prefixSumJob); if (useOcclusionCulling) - m_IndirectStorage.SetBufferContext(indirectContextIndex, new IndirectBufferContext(drawCommandOutputHandle)); + m_IndirectStorage.SetBufferContext(indirectContextIndex, new IndirectBufferContext(drawCommandOutputJob)); - cullingJobHandle = drawCommandOutputHandle; + cullingJobHandle = drawCommandOutputJob; } cullingJobHandle = rendererVisibilityMasks.Dispose(cullingJobHandle); @@ -2123,34 +2323,37 @@ public unsafe JobHandle CreateCullJobTree( return cullingJobHandle; } - private JobHandle CreateCompactedVisibilityMaskJob(in CPUInstanceData.ReadOnly instanceData, NativeArray rendererVisibilityMasks, JobHandle cullingJobHandle) + private JobHandle ScheduleCompactedVisibilityMaskJob(in RenderWorld renderWorld, NativeArray rendererVisibilityMasks, JobHandle cullingJobHandle) { if (!m_CompactedVisibilityMasks.IsCreated) { Assert.IsTrue(m_CompactedVisibilityMasksJobsHandle.IsCompleted); - m_CompactedVisibilityMasks = new ParallelBitArray(instanceData.handlesLength, Allocator.TempJob); + m_CompactedVisibilityMasks = new ParallelBitArray(renderWorld.handleCount, Allocator.TempJob); } var compactVisibilityMasksJob = new CompactVisibilityMasksJob { rendererVisibilityMasks = rendererVisibilityMasks, compactedVisibilityMasks = m_CompactedVisibilityMasks - }; + } + .ScheduleBatch(rendererVisibilityMasks.Length, CompactVisibilityMasksJob.MaxBatchSize, cullingJobHandle); - var compactVisibilityMasksJobHandle = compactVisibilityMasksJob.ScheduleBatch(rendererVisibilityMasks.Length, CompactVisibilityMasksJob.k_BatchSize, cullingJobHandle); - m_CompactedVisibilityMasksJobsHandle = JobHandle.CombineDependencies(m_CompactedVisibilityMasksJobsHandle, compactVisibilityMasksJobHandle); + m_CompactedVisibilityMasksJobsHandle = JobHandle.CombineDependencies(m_CompactedVisibilityMasksJobsHandle, compactVisibilityMasksJob); - return compactVisibilityMasksJobHandle; + return compactVisibilityMasksJob; } #if UNITY_EDITOR - private JobHandle CreateSceneViewHiddenObjectsCullingJob_EditorOnly(in BatchCullingContext cc, in CPUInstanceData.ReadOnly instanceData, - in CPUSharedInstanceData.ReadOnly sharedInstanceData, NativeArray rendererVisibilityMasks, JobHandle cullingJobHandle) + private JobHandle ScheduleSceneViewHiddenObjectsCullingJob_EditorOnly(in BatchCullingContext context, + in RenderWorld renderWorld, + NativeArray rendererVisibilityMasks, + JobHandle cullingJobHandle) { - bool isSceneViewCamera = m_IsSceneViewCamera && (cc.viewType == BatchCullingViewType.Camera || cc.viewType == BatchCullingViewType.Light); - bool isEditorCullingViewType = cc.viewType == BatchCullingViewType.Picking || cc.viewType == BatchCullingViewType.SelectionOutline - || cc.viewType == BatchCullingViewType.Filtering; + bool isSceneViewCamera = m_IsSceneViewCamera && (context.viewType == BatchCullingViewType.Camera || context.viewType == BatchCullingViewType.Light); + bool isEditorCullingViewType = context.viewType == BatchCullingViewType.Picking + || context.viewType == BatchCullingViewType.SelectionOutline + || context.viewType == BatchCullingViewType.Filtering; if (!isSceneViewCamera && !isEditorCullingViewType) return cullingJobHandle; @@ -2171,119 +2374,114 @@ private JobHandle CreateSceneViewHiddenObjectsCullingJob_EditorOnly(in BatchCull if (!isAnyObjectHidden && !isEditingPrefab) return cullingJobHandle; - int renderersLength = sharedInstanceData.rendererGroupIDs.Length; + int renderersLength = renderWorld.instanceIDs.Length; if (!m_SceneViewHiddenBits.IsCreated) { m_SceneViewHiddenBits = new ParallelBitArray(renderersLength, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); - EditorCameraUtils.GetRenderersHiddenResultBits(sharedInstanceData.rendererGroupIDs, m_SceneViewHiddenBits.GetBitsArray().Reinterpret()); + EditorCameraUtils.GetRenderersHiddenResultBits(renderWorld.instanceIDs, m_SceneViewHiddenBits.GetBitsArray().Reinterpret()); } - var jobHandle = new CullSceneViewHiddenRenderersJob + return new CullSceneViewHiddenRenderersJob { - instanceData = instanceData, - sharedInstanceData = sharedInstanceData, + renderWorld = renderWorld, rendererVisibilityMasks = rendererVisibilityMasks, hiddenBits = m_SceneViewHiddenBits, - }.Schedule(instanceData.instancesLength, CullSceneViewHiddenRenderersJob.k_BatchSize, cullingJobHandle); - - return jobHandle; + } + .Schedule(renderWorld.instanceCount, 128, cullingJobHandle); } - private JobHandle CreateFilteringCullingOutputJob_EditorOnly(in BatchCullingContext cc, BatchCullingOutput cullingOutput, - in CPUInstanceData.ReadOnly instanceData, in CPUSharedInstanceData.ReadOnly sharedInstanceData, in GPUInstanceDataBuffer.ReadOnly instanceDataBuffer, - in CPUDrawInstanceData drawInstanceData, NativeParallelHashMap batchIDs, NativeArray rendererVisibilityMasks, NativeArray rendererMeshLodSettings, - NativeArray rendererCrossFadeValues, JobHandle cullingJobHandle) + private JobHandle ScheduleFilteringCullingOutputJob_EditorOnly(in BatchCullingContext context, + BatchCullingOutput cullingOutput, + in RenderWorld renderWorld, + in GPUInstanceDataBuffer.ReadOnly instanceDataBuffer, + in CPUDrawInstanceData drawInstanceData, + in IncludeExcludeListFilter includeExcludeListFilter, + NativeParallelHashMap batchIDs, + NativeArray rendererVisibilityMasks, + NativeArray rendererMeshLodSettings, + NativeArray rendererCrossFadeValues, + JobHandle cullingJobHandle) { - NativeArray filteredRenderers = new NativeArray(sharedInstanceData.rendererGroupIDs.Length, Allocator.TempJob); - EditorCameraUtils.GetRenderersFilteringResults(sharedInstanceData.rendererGroupIDs, filteredRenderers); - var dummyExcludedRenderers = new NativeArray(0, Allocator.TempJob); + NativeArray filteredRenderers = new NativeArray(renderWorld.instanceIDs.Length, Allocator.TempJob); + EditorCameraUtils.GetRenderersFilteringResults(renderWorld.instanceIDs, filteredRenderers); var drawOutputJob = new DrawCommandOutputFiltering { - viewID = cc.viewID.GetInstanceID(), + renderWorld = renderWorld, + viewID = EntityId.From(context.viewID.GetInstanceID()), batchIDs = batchIDs, instanceDataBuffer = instanceDataBuffer, rendererVisibilityMasks = rendererVisibilityMasks, rendererMeshLodSettings = rendererMeshLodSettings, rendererCrossFadeValues = rendererCrossFadeValues, - instanceData = instanceData, - sharedInstanceData = sharedInstanceData, drawInstanceIndices = drawInstanceData.drawInstanceIndices, drawBatches = drawInstanceData.drawBatches, drawRanges = drawInstanceData.drawRanges, drawBatchIndices = drawInstanceData.drawBatchIndices, filteringResults = filteredRenderers, - excludedRenderers = dummyExcludedRenderers, + includeExcludeListFilter = includeExcludeListFilter, cullingOutput = cullingOutput.drawCommands, mode = FilteringJobMode.Filtering - }; - - var drawOutputHandle = drawOutputJob.Schedule(cullingJobHandle); - - filteredRenderers.Dispose(drawOutputHandle); - dummyExcludedRenderers.Dispose(drawOutputHandle); - - return drawOutputHandle; + } + .Schedule(cullingJobHandle); + filteredRenderers.Dispose(drawOutputJob); + return drawOutputJob; } - private JobHandle CreatePickingCullingOutputJob_EditorOnly(in BatchCullingContext cc, BatchCullingOutput cullingOutput, - in CPUInstanceData.ReadOnly instanceData, in CPUSharedInstanceData.ReadOnly sharedInstanceData, in GPUInstanceDataBuffer.ReadOnly instanceDataBuffer, - in CPUDrawInstanceData drawInstanceData, NativeParallelHashMap batchIDs, NativeArray rendererVisibilityMasks, + private JobHandle SchedulePickingCullingOutputJob_EditorOnly(in BatchCullingContext context, + BatchCullingOutput cullingOutput, + in RenderWorld renderWorld, + in GPUInstanceDataBuffer.ReadOnly instanceDataBuffer, + in CPUDrawInstanceData drawInstanceData, + in IncludeExcludeListFilter includeExcludeListFilter, + NativeParallelHashMap batchIDs, + NativeArray rendererVisibilityMasks, NativeArray rendererMeshLodSettings, - NativeArray rendererCrossFadeValues, JobHandle cullingJobHandle) + NativeArray rendererCrossFadeValues, + JobHandle cullingJobHandle) { - // GPUResindetDrawer doesn't handle rendering of persistent game objects like prefabs. They are rendered by SRP. + // GPUResidentDrawer doesn't handle rendering of persistent game objects like prefabs. They are rendered by SRP. // When we are in prefab editing mode all the objects that are not part of the prefab should not be pickable. if (PrefabStageUtility.GetCurrentPrefabStage() != null) return cullingJobHandle; - var pickingIDs = HandleUtility.GetPickingIncludeExcludeEntityIdList(Allocator.TempJob); - var excludedRenderers = pickingIDs.ExcludeRenderers.IsCreated ? pickingIDs.ExcludeRenderers : new NativeArray(0, Allocator.TempJob); var dummyFilteringResults = new NativeArray(0, Allocator.TempJob); var drawOutputJob = new DrawCommandOutputFiltering { - viewID = cc.viewID.GetInstanceID(), + renderWorld = renderWorld, + viewID = EntityId.From(context.viewID.GetInstanceID()), batchIDs = batchIDs, instanceDataBuffer = instanceDataBuffer, rendererVisibilityMasks = rendererVisibilityMasks, rendererMeshLodSettings = rendererMeshLodSettings, rendererCrossFadeValues = rendererCrossFadeValues, - instanceData = instanceData, - sharedInstanceData = sharedInstanceData, drawInstanceIndices = drawInstanceData.drawInstanceIndices, drawBatches = drawInstanceData.drawBatches, drawRanges = drawInstanceData.drawRanges, drawBatchIndices = drawInstanceData.drawBatchIndices, filteringResults = dummyFilteringResults, - excludedRenderers = excludedRenderers, + includeExcludeListFilter = includeExcludeListFilter, cullingOutput = cullingOutput.drawCommands, mode = FilteringJobMode.Picking - }; - - var drawOutputHandle = drawOutputJob.Schedule(cullingJobHandle); - drawOutputHandle.Complete(); - + } + .Schedule(cullingJobHandle); + drawOutputJob.Complete(); dummyFilteringResults.Dispose(); - if (!pickingIDs.ExcludeRenderers.IsCreated) - excludedRenderers.Dispose(); - pickingIDs.Dispose(); - - return drawOutputHandle; + return drawOutputJob; } #endif - public void InstanceOccludersUpdated(EntityId viewInstanceID, int subviewMask, RenderersBatchersContext batchersContext) + public void InstanceOccludersUpdated(EntityId viewID, int subviewMask, OcclusionCullingCommon occlusionCullingCommon) { if (m_DebugStats?.enabled ?? false) { - var occlusionCullingCommon = batchersContext.occlusionCullingCommon; - bool hasOccluders = occlusionCullingCommon.GetOccluderContext(viewInstanceID, out OccluderContext occluderCtx); + bool hasOccluders = occlusionCullingCommon.GetOccluderContext(viewID, out OccluderContext occluderCtx); if (hasOccluders) { m_OcclusionEventDebugArray.TryAdd( - viewInstanceID, + viewID, InstanceOcclusionEventType.OccluderUpdate, occluderCtx.version, subviewMask, @@ -2319,15 +2517,16 @@ public ParallelBitArray GetCompactedVisibilityMasks(bool syncCullingJobs) private class InstanceOcclusionTestPassData { + public GPUResidentContext grdContext; public OcclusionCullingSettings settings; public InstanceOcclusionTestSubviewSettings subviewSettings; public OccluderHandles occluderHandles; public IndirectBufferContextHandles bufferHandles; } - public void InstanceOcclusionTest(RenderGraph renderGraph, in OcclusionCullingSettings settings, ReadOnlySpan subviewOcclusionTests, RenderersBatchersContext batchersContext) + public void InstanceOcclusionTest(RenderGraph renderGraph, in OcclusionCullingSettings settings, ReadOnlySpan subviewOcclusionTests, GPUResidentContext grdContext) { - if (!batchersContext.occlusionCullingCommon.GetOccluderContext(settings.viewInstanceID, out OccluderContext occluderCtx)) + if (!grdContext.occlusionCullingCommon.GetOccluderContext(settings.viewInstanceID, out OccluderContext occluderCtx)) return; var occluderHandles = occluderCtx.Import(renderGraph); @@ -2338,6 +2537,7 @@ public void InstanceOcclusionTest(RenderGraph renderGraph, in OcclusionCullingSe { builder.AllowGlobalStateModification(true); + passData.grdContext = grdContext; passData.settings = settings; passData.subviewSettings = InstanceOcclusionTestSubviewSettings.FromSpan(subviewOcclusionTests); passData.bufferHandles = m_IndirectStorage.ImportBuffers(renderGraph); @@ -2348,21 +2548,20 @@ public void InstanceOcclusionTest(RenderGraph renderGraph, in OcclusionCullingSe builder.SetRenderFunc(static (InstanceOcclusionTestPassData data, ComputeGraphContext context) => { - var batcher = GPUResidentDrawer.instance.batcher; - batcher.instanceCullingBatcher.culler.AddOcclusionCullingDispatch( + data.grdContext.culler.AddOcclusionCullingDispatch( context.cmd, data.settings, data.subviewSettings, data.bufferHandles, data.occluderHandles, - batcher.batchersContext); + data.grdContext); }); } } - internal void EnsureValidOcclusionTestResults(EntityId viewInstanceID) + internal void EnsureValidOcclusionTestResults(EntityId viewID) { - int indirectContextIndex = m_IndirectStorage.TryGetContextIndex(viewInstanceID); + int indirectContextIndex = m_IndirectStorage.TryGetContextIndex(viewID); if (indirectContextIndex >= 0) { // sync before checking the allocation results @@ -2414,9 +2613,10 @@ private void AddOcclusionCullingDispatch( in InstanceOcclusionTestSubviewSettings subviewSettings, in IndirectBufferContextHandles bufferHandles, in OccluderHandles occluderHandles, - RenderersBatchersContext batchersContext) + GPUResidentContext grdContext) { - var occlusionCullingCommon = batchersContext.occlusionCullingCommon; + var instanceDataSystem = grdContext.instanceDataSystem; + var occlusionCullingCommon = grdContext.occlusionCullingCommon; int indirectContextIndex = m_IndirectStorage.TryGetContextIndex(settings.viewInstanceID); if (indirectContextIndex >= 0) { @@ -2531,6 +2731,7 @@ private void AddOcclusionCullingDispatch( var secondPassKeyword = new LocalKeyword(cs, "OCCLUSION_SECOND_PASS"); OccluderContext.SetKeyword(cmd, cs, firstPassKeyword, isFirstPass); OccluderContext.SetKeyword(cmd, cs, secondPassKeyword, isSecondPass); + GPUComponentHandle boundingSphereGPUComponent = instanceDataSystem.defaultGPUComponents.boundingSphere; m_ShaderVariables[0] = new InstanceOcclusionCullerShaderVariables { @@ -2538,7 +2739,7 @@ private void AddOcclusionCullingDispatch( _DrawInfoCount = (uint)allocInfo.drawCount, _InstanceInfoAllocIndex = (uint)(IndirectBufferContextStorage.kInstanceInfoGpuOffsetMultiplier * allocInfo.instanceAllocIndex), _InstanceInfoCount = (uint)allocInfo.instanceCount, - _BoundingSphereInstanceDataAddress = batchersContext.renderersParameters.boundingSphere.gpuAddress, + _BoundingSphereInstanceDataAddress = instanceDataSystem.gpuBuffer.GetComponentGPUAddress(boundingSphereGPUComponent), _DebugCounterIndex = debugCounterIndex, _InstanceMultiplierShift = (settings.instanceMultiplier == 2) ? 1 : 0, }; @@ -2576,7 +2777,7 @@ private void AddOcclusionCullingDispatch( cmd.SetComputeBufferParam(cs, kernel, ShaderIDs._InstanceInfo, bufferHandles.instanceInfoBuffer); cmd.SetComputeBufferParam(cs, kernel, ShaderIDs._DrawArgs, bufferHandles.drawArgsBuffer); cmd.SetComputeBufferParam(cs, kernel, ShaderIDs._InstanceIndices, bufferHandles.instanceBuffer); - cmd.SetComputeBufferParam(cs, kernel, ShaderIDs._InstanceDataBuffer, batchersContext.gpuInstanceDataBuffer); + cmd.SetComputeBufferParam(cs, kernel, ShaderIDs._InstanceDataBuffer, instanceDataSystem.gpuBuffer.nativeBuffer); cmd.SetComputeBufferParam(cs, kernel, ShaderIDs._OcclusionDebugCounters, m_OcclusionEventDebugArray.CounterBuffer); if (isFirstPass || isSecondPass) diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceCuller.cs.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/InstanceCuller.cs.meta similarity index 100% rename from Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceCuller.cs.meta rename to Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/InstanceCuller.cs.meta diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceCullerBurst.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/InstanceCullerBurst.cs similarity index 75% rename from Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceCullerBurst.cs rename to Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/InstanceCullerBurst.cs index fc4c27e8f04..86b54788111 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceCullerBurst.cs +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/InstanceCullerBurst.cs @@ -7,9 +7,14 @@ namespace UnityEngine.Rendering internal static class InstanceCullerBurst { [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] - public static unsafe void SetupCullingJobInput(float lodBias, float meshLodThreshold, BatchCullingContext* context, - ReceiverPlanes* receiverPlanes, ReceiverSphereCuller* receiverSphereCuller, FrustumPlaneCuller* frustumPlaneCuller, - float* screenRelativeMetric, float* meshLodConstant) + public static unsafe void SetupCullingJobInput(float lodBias, + float meshLodThreshold, + BatchCullingContext* context, + ReceiverPlanes* receiverPlanes, + ReceiverSphereCuller* receiverSphereCuller, + FrustumPlaneCuller* frustumPlaneCuller, + float* screenRelativeMetric, + float* meshLodConstant) { *receiverPlanes = ReceiverPlanes.Create(*context, Allocator.TempJob); *receiverSphereCuller = ReceiverSphereCuller.Create(*context, Allocator.TempJob); diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/InstanceCullerBurst.cs.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/InstanceCullerBurst.cs.meta new file mode 100644 index 00000000000..709a954a33b --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/InstanceCullerBurst.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 86dce443230a0694d8fedef47e43b707 \ No newline at end of file diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceOcclusionCuller.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/InstanceOcclusionCuller.cs similarity index 100% rename from Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceOcclusionCuller.cs rename to Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/InstanceOcclusionCuller.cs index 22a3974c8ed..bc1ff79c100 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceOcclusionCuller.cs +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/InstanceOcclusionCuller.cs @@ -281,8 +281,8 @@ private void AllocateTexturesIfNecessary(bool debugOverlayEnabled) occluderDepthPyramidSize = minDepthPyramidSize; occluderDepthPyramid = RTHandles.Alloc( occluderDepthPyramidSize.x, occluderDepthPyramidSize.y, - format: GraphicsFormat.R32_SFloat, dimension: TextureDimension.Tex2D, + format: GraphicsFormat.R32_SFloat, filterMode: FilterMode.Point, wrapMode: TextureWrapMode.Clamp, enableRandomWrite: true, diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceOcclusionCuller.cs.hlsl b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/InstanceOcclusionCuller.cs.hlsl similarity index 100% rename from Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceOcclusionCuller.cs.hlsl rename to Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/InstanceOcclusionCuller.cs.hlsl diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceOcclusionCuller.cs.hlsl.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/InstanceOcclusionCuller.cs.hlsl.meta similarity index 100% rename from Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceOcclusionCuller.cs.hlsl.meta rename to Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/InstanceOcclusionCuller.cs.hlsl.meta diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceOcclusionCuller.cs.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/InstanceOcclusionCuller.cs.meta similarity index 100% rename from Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceOcclusionCuller.cs.meta rename to Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/InstanceOcclusionCuller.cs.meta diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceOcclusionCullerShaderVariables.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/InstanceOcclusionCullerShaderVariables.cs similarity index 100% rename from Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceOcclusionCullerShaderVariables.cs rename to Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/InstanceOcclusionCullerShaderVariables.cs diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceOcclusionCullerShaderVariables.cs.hlsl b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/InstanceOcclusionCullerShaderVariables.cs.hlsl similarity index 100% rename from Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceOcclusionCullerShaderVariables.cs.hlsl rename to Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/InstanceOcclusionCullerShaderVariables.cs.hlsl diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceOcclusionCullerShaderVariables.cs.hlsl.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/InstanceOcclusionCullerShaderVariables.cs.hlsl.meta similarity index 100% rename from Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceOcclusionCullerShaderVariables.cs.hlsl.meta rename to Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/InstanceOcclusionCullerShaderVariables.cs.hlsl.meta diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceOcclusionCullerShaderVariables.cs.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/InstanceOcclusionCullerShaderVariables.cs.meta similarity index 100% rename from Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceOcclusionCullerShaderVariables.cs.meta rename to Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/InstanceOcclusionCullerShaderVariables.cs.meta diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/OccluderDepthPyramidConstants.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/OccluderDepthPyramidConstants.cs similarity index 100% rename from Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/OccluderDepthPyramidConstants.cs rename to Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/OccluderDepthPyramidConstants.cs diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/OccluderDepthPyramidConstants.cs.hlsl b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/OccluderDepthPyramidConstants.cs.hlsl similarity index 100% rename from Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/OccluderDepthPyramidConstants.cs.hlsl rename to Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/OccluderDepthPyramidConstants.cs.hlsl diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/OccluderDepthPyramidConstants.cs.hlsl.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/OccluderDepthPyramidConstants.cs.hlsl.meta similarity index 100% rename from Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/OccluderDepthPyramidConstants.cs.hlsl.meta rename to Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/OccluderDepthPyramidConstants.cs.hlsl.meta diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/OccluderDepthPyramidConstants.cs.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/OccluderDepthPyramidConstants.cs.meta similarity index 100% rename from Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/OccluderDepthPyramidConstants.cs.meta rename to Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/OccluderDepthPyramidConstants.cs.meta diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/OcclusionCullingCommon.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/OcclusionCullingCommon.cs similarity index 83% rename from Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/OcclusionCullingCommon.cs rename to Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/OcclusionCullingCommon.cs index 7bbd2ae9290..bf535a67089 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/OcclusionCullingCommon.cs +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/OcclusionCullingCommon.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; -using Unity.Mathematics; using UnityEngine.Rendering.RenderGraphModule; namespace UnityEngine.Rendering @@ -40,14 +39,14 @@ internal struct SilhouettePlaneCache : IDisposable private struct Slot { public bool isActive; - public EntityId viewInstanceID; + public EntityId viewID; public int planeCount; // planeIndex = slotIndex * kMaxSilhouettePlanes public int lastUsedFrameIndex; - public Slot(EntityId viewInstanceID, int planeCount, int frameIndex) + public Slot(EntityId viewID, int planeCount, int frameIndex) { this.isActive = true; - this.viewInstanceID = viewInstanceID; + this.viewID = viewID; this.planeCount = planeCount; this.lastUsedFrameIndex = frameIndex; } @@ -55,14 +54,14 @@ public Slot(EntityId viewInstanceID, int planeCount, int frameIndex) private const int kMaxSilhouettePlanes = (int)OcclusionCullingCommonConfig.MaxOccluderSilhouettePlanes; - private NativeParallelHashMap m_SubviewIDToIndexMap; + private NativeParallelHashMap m_SubViewIDToIndexMap; private NativeList m_SlotFreeList; private NativeList m_Slots; private NativeList m_PlaneStorage; public void Init() { - m_SubviewIDToIndexMap = new NativeParallelHashMap(16, Allocator.Persistent); + m_SubViewIDToIndexMap = new NativeParallelHashMap(16, Allocator.Persistent); m_SlotFreeList = new NativeList(16, Allocator.Persistent); m_Slots = new NativeList(16, Allocator.Persistent); m_PlaneStorage = new NativeList(16 * kMaxSilhouettePlanes, Allocator.Persistent); @@ -70,17 +69,17 @@ public void Init() public void Dispose() { - m_SubviewIDToIndexMap.Dispose(); + m_SubViewIDToIndexMap.Dispose(); m_SlotFreeList.Dispose(); m_Slots.Dispose(); m_PlaneStorage.Dispose(); } - public void Update(EntityId viewInstanceID, NativeArray planes, int frameIndex) + public void Update(EntityId viewID, NativeArray planes, int frameIndex) { int planeCount = Math.Min(planes.Length, kMaxSilhouettePlanes); - if (!m_SubviewIDToIndexMap.TryGetValue(viewInstanceID, out int slotIndex)) + if (!m_SubViewIDToIndexMap.TryGetValue(viewID, out int slotIndex)) { if (m_SlotFreeList.Length > 0) { @@ -106,10 +105,10 @@ public void Update(EntityId viewInstanceID, NativeArray planes, int frame } // associate with this view ID - m_SubviewIDToIndexMap.Add(viewInstanceID, slotIndex); + m_SubViewIDToIndexMap.Add(viewID, slotIndex); } - m_Slots[slotIndex] = new Slot(viewInstanceID, planeCount, frameIndex); + m_Slots[slotIndex] = new Slot(viewID, planeCount, frameIndex); m_PlaneStorage.AsArray().GetSubArray(slotIndex * kMaxSilhouettePlanes, planeCount).CopyFrom(planes); } @@ -125,17 +124,17 @@ public void FreeUnusedSlots(int frameIndex, int maximumAge) { slot.isActive = false; m_Slots[slotIndex] = slot; - m_SubviewIDToIndexMap.Remove(slot.viewInstanceID); + m_SubViewIDToIndexMap.Remove(slot.viewID); m_SlotFreeList.Add(slotIndex); } } } - public NativeArray GetSubArray(EntityId viewInstanceID) + public NativeArray GetSubArray(EntityId viewID) { int planeOffset = 0; int planeCount = 0; - if (m_SubviewIDToIndexMap.TryGetValue(viewInstanceID, out int slotIndex)) + if (m_SubViewIDToIndexMap.TryGetValue(viewID, out int slotIndex)) { planeOffset = slotIndex * kMaxSilhouettePlanes; planeCount = m_Slots[slotIndex].planeCount; @@ -150,7 +149,7 @@ private struct OccluderContextSlot { public bool valid; public int lastUsedFrameIndex; - public EntityId viewInstanceID; + public EntityId viewID; } private static readonly int s_MaxContextGCFrame = 8; // Allow a few frames for alternate frame shadow updates before cleanup @@ -181,7 +180,10 @@ private struct OccluderContextSlot private ProfilingSampler m_ProfilingSamplerOcclusionTestOverlay; private ProfilingSampler m_ProfilingSamplerOccluderOverlay; - internal void Init(GPUResidentDrawerResources resources) + private BaseRenderFunc m_ComputePassRenderFunc; + private BaseRenderFunc m_RasterPassRenderFunc; + + internal void Initialize(GPUResidentDrawerResources resources) { m_DebugOcclusionTestMaterial = CoreUtils.CreateEngineMaterial(resources.debugOcclusionTestPS); m_OccluderDebugViewMaterial = CoreUtils.CreateEngineMaterial(resources.debugOccluderPS); @@ -207,6 +209,24 @@ internal void Init(GPUResidentDrawerResources resources) m_CommonConstantBuffer = new ComputeBuffer(1, UnsafeUtility.SizeOf(), ComputeBufferType.Constant); m_DebugShaderVariables = new NativeArray(1, Allocator.Persistent); m_DebugConstantBuffer = new ComputeBuffer(1, UnsafeUtility.SizeOf(), ComputeBufferType.Constant); + + m_ComputePassRenderFunc = (OcclusionTestOverlaySetupPassData data, ComputeGraphContext ctx) => + { + m_DebugShaderVariables[0] = data.cb; + ctx.cmd.SetBufferData(m_DebugConstantBuffer, m_DebugShaderVariables); + + m_DebugOcclusionTestMaterial.SetConstantBuffer( + ShaderIDs.OcclusionCullingDebugShaderVariables, + m_DebugConstantBuffer, + 0, + m_DebugConstantBuffer.stride); + }; + + m_RasterPassRenderFunc = (OcclusionTestOverlayPassData data, RasterGraphContext ctx) => + { + ctx.cmd.SetGlobalBuffer(ShaderIDs._OcclusionDebugOverlay, data.debugPyramid); + CoreUtils.DrawFullScreen(ctx.cmd, m_DebugOcclusionTestMaterial); + }; } private static class ShaderIDs @@ -259,17 +279,16 @@ private class OcclusionTestOverlaySetupPassData private class OcclusionTestOverlayPassData { public BufferHandle debugPyramid; - public Material debugOcclusionTestMaterial; } - public void RenderDebugOcclusionTestOverlay(RenderGraph renderGraph, DebugDisplayGPUResidentDrawer debugSettings, EntityId viewInstanceID, in TextureHandle colorBuffer) + public void RenderDebugOcclusionTestOverlay(RenderGraph renderGraph, DebugDisplayGPUResidentDrawer debugSettings, EntityId viewID, TextureHandle colorBuffer) { if (debugSettings == null) return; if (!debugSettings.occlusionTestOverlayEnable) return; - OcclusionCullingDebugOutput debugOutput = GetOcclusionTestDebugOutput(viewInstanceID); + OcclusionCullingDebugOutput debugOutput = GetOcclusionTestDebugOutput(viewID); if (debugOutput.occlusionDebugOverlay == null) return; @@ -279,20 +298,7 @@ public void RenderDebugOcclusionTestOverlay(RenderGraph renderGraph, DebugDispla passData.cb = debugOutput.cb; - builder.SetRenderFunc( - static (OcclusionTestOverlaySetupPassData data, ComputeGraphContext ctx) => - { - var occ = GPUResidentDrawer.instance.batcher.occlusionCullingCommon; - - occ.m_DebugShaderVariables[0] = data.cb; - ctx.cmd.SetBufferData(occ.m_DebugConstantBuffer, occ.m_DebugShaderVariables); - - occ.m_DebugOcclusionTestMaterial.SetConstantBuffer( - ShaderIDs.OcclusionCullingDebugShaderVariables, - occ.m_DebugConstantBuffer, - 0, - occ.m_DebugConstantBuffer.stride); - }); + builder.SetRenderFunc(m_ComputePassRenderFunc); } using (var builder = renderGraph.AddRasterRenderPass("OcclusionTestOverlay", out var passData, m_ProfilingSamplerOcclusionTestOverlay)) @@ -300,17 +306,11 @@ public void RenderDebugOcclusionTestOverlay(RenderGraph renderGraph, DebugDispla builder.AllowGlobalStateModification(true); passData.debugPyramid = renderGraph.ImportBuffer(debugOutput.occlusionDebugOverlay); - passData.debugOcclusionTestMaterial = m_DebugOcclusionTestMaterial; builder.SetRenderAttachment(colorBuffer, 0); builder.UseBuffer(passData.debugPyramid); - builder.SetRenderFunc( - static (OcclusionTestOverlayPassData data, RasterGraphContext ctx) => - { - ctx.cmd.SetGlobalBuffer(ShaderIDs._OcclusionDebugOverlay, data.debugPyramid); - CoreUtils.DrawFullScreen(ctx.cmd, data.debugOcclusionTestMaterial); - }); + builder.SetRenderFunc(m_RasterPassRenderFunc); } } @@ -330,17 +330,17 @@ class OccluderOverlayPassData public Vector2 validRange; } - public void RenderDebugOccluderOverlay(RenderGraph renderGraph, DebugDisplayGPUResidentDrawer debugSettings, Vector2 screenPos, float maxHeight, in TextureHandle colorBuffer) + public void RenderDebugOccluderOverlay(RenderGraph renderGraph, DebugDisplayGPUResidentDrawer debugSettings, Vector2 screenPos, float maxHeight, TextureHandle colorBuffer) { if (debugSettings == null) return; if (!debugSettings.occluderDebugViewEnable) return; - if (!debugSettings.GetOccluderViewInstanceID(out var viewInstanceID)) + if (!debugSettings.GetOccluderViewID(out var camera)) return; - var occluderTexture = GetOcclusionTestDebugOutput(viewInstanceID).occluderDepthPyramid; + var occluderTexture = GetOcclusionTestDebugOutput(camera).occluderDepthPyramid; if (occluderTexture == null) return; @@ -364,8 +364,8 @@ public void RenderDebugOccluderOverlay(RenderGraph renderGraph, DebugDisplayGPUR passData.passIndex = passIndex; passData.validRange = debugSettings.occluderDebugViewRange; - builder.SetRenderFunc(static - (OccluderOverlayPassData data, RasterGraphContext ctx) => + builder.SetRenderFunc( + static (OccluderOverlayPassData data, RasterGraphContext ctx) => { var mpb = ctx.renderGraphPool.GetTempMaterialPropertyBlock(); @@ -378,9 +378,9 @@ public void RenderDebugOccluderOverlay(RenderGraph renderGraph, DebugDisplayGPUR } } - private void DispatchDebugClear(ComputeCommandBuffer cmd, EntityId viewInstanceID) + private void DispatchDebugClear(ComputeCommandBuffer cmd, EntityId viewID) { - if (!m_ViewIDToIndexMap.TryGetValue(viewInstanceID, out var contextIndex)) + if (!m_ViewIDToIndexMap.TryGetValue(viewID, out var contextIndex)) return; OccluderContext occluderCtx = m_OccluderContextData[contextIndex]; @@ -445,9 +445,10 @@ private class UpdateOccludersPassData public OccluderParameters occluderParams; public List occluderSubviewUpdates; public OccluderHandles occluderHandles; + public GPUResidentContext grdContext; } - public bool UpdateInstanceOccluders(RenderGraph renderGraph, in OccluderParameters occluderParams, ReadOnlySpan occluderSubviewUpdates) + public bool UpdateInstanceOccluders(RenderGraph renderGraph, GPUResidentContext grdContext, in OccluderParameters occluderParams, ReadOnlySpan occluderSubviewUpdates) { var occluderHandles = PrepareOccluders(renderGraph, occluderParams); if (!occluderHandles.occluderDepthPyramid.IsValid()) @@ -457,6 +458,7 @@ public bool UpdateInstanceOccluders(RenderGraph renderGraph, in OccluderParamete { builder.AllowGlobalStateModification(true); + passData.grdContext = grdContext; passData.occluderParams = occluderParams; if (passData.occluderSubviewUpdates is null) passData.occluderSubviewUpdates = new List(); @@ -480,23 +482,23 @@ public bool UpdateInstanceOccluders(RenderGraph renderGraph, in OccluderParamete subviewMask |= 1 << data.occluderSubviewUpdates[i].subviewIndex; } - var batcher = GPUResidentDrawer.instance.batcher; - batcher.occlusionCullingCommon.CreateFarDepthPyramid(context.cmd, in data.occluderParams, occluderSubviewUpdates, in data.occluderHandles); - batcher.instanceCullingBatcher.InstanceOccludersUpdated(data.occluderParams.viewInstanceID, subviewMask); + OcclusionCullingCommon occlusionCullingCommon = data.grdContext.occlusionCullingCommon; + occlusionCullingCommon.CreateFarDepthPyramid(context.cmd, in data.occluderParams, occluderSubviewUpdates, in data.occluderHandles); + data.grdContext.culler.InstanceOccludersUpdated(data.occluderParams.viewInstanceID, subviewMask, occlusionCullingCommon); }); } return true; } - internal void UpdateSilhouettePlanes(EntityId viewInstanceID, NativeArray planes) + internal void UpdateSilhouettePlanes(EntityId viewID, NativeArray planes) { - m_SilhouettePlaneCache.Update(viewInstanceID, planes, m_FrameIndex); + m_SilhouettePlaneCache.Update(viewID, planes, m_FrameIndex); } - internal OcclusionCullingDebugOutput GetOcclusionTestDebugOutput(EntityId viewInstanceID) + internal OcclusionCullingDebugOutput GetOcclusionTestDebugOutput(EntityId viewID) { - if (m_ViewIDToIndexMap.TryGetValue(viewInstanceID, out var contextIndex) && m_OccluderContextSlots[contextIndex].valid) + if (m_ViewIDToIndexMap.TryGetValue(viewID, out var contextIndex) && m_OccluderContextSlots[contextIndex].valid) return m_OccluderContextData[contextIndex].GetDebugOutput(); return new OcclusionCullingDebugOutput(); } @@ -510,7 +512,7 @@ public void UpdateOccluderStats(DebugRendererBatcherStats debugStats) { debugStats.occluderStats.Add(new DebugOccluderStats { - viewInstanceID = pair.Key, + viewID = pair.Key, subviewCount = m_OccluderContextData[pair.Value].subviewCount, occluderMipLayoutSize = m_OccluderContextData[pair.Value].occluderMipLayoutSize, }); @@ -518,14 +520,14 @@ public void UpdateOccluderStats(DebugRendererBatcherStats debugStats) } } - internal bool HasOccluderContext(EntityId viewInstanceID) + internal bool HasOccluderContext(EntityId viewID) { - return m_ViewIDToIndexMap.ContainsKey(viewInstanceID); + return m_ViewIDToIndexMap.ContainsKey(viewID); } - internal bool GetOccluderContext(EntityId viewInstanceID, out OccluderContext occluderContext) + internal bool GetOccluderContext(EntityId viewID, out OccluderContext occluderContext) { - if (m_ViewIDToIndexMap.TryGetValue(viewInstanceID, out var contextIndex) && m_OccluderContextSlots[contextIndex].valid) + if (m_ViewIDToIndexMap.TryGetValue(viewID, out var contextIndex) && m_OccluderContextSlots[contextIndex].valid) { occluderContext = m_OccluderContextData[contextIndex]; return true; @@ -547,7 +549,7 @@ internal void UpdateFrame() //Garbage collect unused contexts for a long time: if ((m_FrameIndex - slot.lastUsedFrameIndex) >= s_MaxContextGCFrame) { - DeleteContext(slot.viewInstanceID); + DeleteContext(slot.viewID); continue; } @@ -558,10 +560,10 @@ internal void UpdateFrame() ++m_FrameIndex; } - private int NewContext(EntityId viewInstanceID) + private int NewContext(EntityId viewID) { int newSlot = -1; - var newCtxSlot = new OccluderContextSlot { valid = true, viewInstanceID = viewInstanceID, lastUsedFrameIndex = m_FrameIndex }; + var newCtxSlot = new OccluderContextSlot { valid = true, viewID = viewID, lastUsedFrameIndex = m_FrameIndex }; var newCtx = new OccluderContext() {}; if (m_FreeOccluderContexts.Length > 0) { @@ -577,19 +579,19 @@ private int NewContext(EntityId viewInstanceID) m_OccluderContextSlots.Add(newCtxSlot); } - m_ViewIDToIndexMap.Add(viewInstanceID, newSlot); + m_ViewIDToIndexMap.Add(viewID, newSlot); return newSlot; } - private void DeleteContext(EntityId viewInstanceID) + private void DeleteContext(EntityId viewID) { - if (!m_ViewIDToIndexMap.TryGetValue(viewInstanceID, out var contextIndex) || !m_OccluderContextSlots[contextIndex].valid) + if (!m_ViewIDToIndexMap.TryGetValue(viewID, out var contextIndex) || !m_OccluderContextSlots[contextIndex].valid) return; m_OccluderContextData[contextIndex].Dispose(); m_OccluderContextSlots[contextIndex] = new OccluderContextSlot { valid = false }; m_FreeOccluderContexts.Add(contextIndex); - m_ViewIDToIndexMap.Remove(viewInstanceID); + m_ViewIDToIndexMap.Remove(viewID); } public void Dispose() diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/OcclusionCullingCommon.cs.hlsl b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/OcclusionCullingCommon.cs.hlsl similarity index 100% rename from Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/OcclusionCullingCommon.cs.hlsl rename to Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/OcclusionCullingCommon.cs.hlsl diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/OcclusionCullingCommon.cs.hlsl.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/OcclusionCullingCommon.cs.hlsl.meta similarity index 100% rename from Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/OcclusionCullingCommon.cs.hlsl.meta rename to Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/OcclusionCullingCommon.cs.hlsl.meta diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/OcclusionCullingCommon.cs.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/OcclusionCullingCommon.cs.meta similarity index 100% rename from Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/OcclusionCullingCommon.cs.meta rename to Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/OcclusionCullingCommon.cs.meta diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/OcclusionCullingCommon.hlsl b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/OcclusionCullingCommon.hlsl similarity index 95% rename from Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/OcclusionCullingCommon.hlsl rename to Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/OcclusionCullingCommon.hlsl index ce94d17c4cf..24915a69190 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/OcclusionCullingCommon.hlsl +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/OcclusionCullingCommon.hlsl @@ -5,10 +5,10 @@ // #pragma multi_compile _ OCCLUSION_DEBUG // before including this file -#include "Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/OcclusionCullingCommon.cs.hlsl" -#include "Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/OcclusionCullingCommonShaderVariables.cs.hlsl" -#include "Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/OcclusionTestCommon.hlsl" -#include "Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/GeometryUtilities.hlsl" +#include "Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/OcclusionCullingCommon.cs.hlsl" +#include "Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/OcclusionCullingCommonShaderVariables.cs.hlsl" +#include "Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/OcclusionTestCommon.hlsl" +#include "Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Utilities/GeometryUtilities.hlsl" #define OCCLUSION_ENABLE_GATHER_TRIM 1 diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/OcclusionCullingCommon.hlsl.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/OcclusionCullingCommon.hlsl.meta similarity index 100% rename from Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/OcclusionCullingCommon.hlsl.meta rename to Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/OcclusionCullingCommon.hlsl.meta diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/OcclusionCullingCommonShaderVariables.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/OcclusionCullingCommonShaderVariables.cs similarity index 100% rename from Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/OcclusionCullingCommonShaderVariables.cs rename to Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/OcclusionCullingCommonShaderVariables.cs diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/OcclusionCullingCommonShaderVariables.cs.hlsl b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/OcclusionCullingCommonShaderVariables.cs.hlsl similarity index 100% rename from Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/OcclusionCullingCommonShaderVariables.cs.hlsl rename to Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/OcclusionCullingCommonShaderVariables.cs.hlsl diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/OcclusionCullingCommonShaderVariables.cs.hlsl.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/OcclusionCullingCommonShaderVariables.cs.hlsl.meta similarity index 100% rename from Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/OcclusionCullingCommonShaderVariables.cs.hlsl.meta rename to Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/OcclusionCullingCommonShaderVariables.cs.hlsl.meta diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/OcclusionCullingCommonShaderVariables.cs.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/OcclusionCullingCommonShaderVariables.cs.meta similarity index 100% rename from Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/OcclusionCullingCommonShaderVariables.cs.meta rename to Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/OcclusionCullingCommonShaderVariables.cs.meta diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/OcclusionCullingDebugShaderVariables.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/OcclusionCullingDebugShaderVariables.cs similarity index 100% rename from Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/OcclusionCullingDebugShaderVariables.cs rename to Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/OcclusionCullingDebugShaderVariables.cs diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/OcclusionCullingDebugShaderVariables.cs.hlsl b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/OcclusionCullingDebugShaderVariables.cs.hlsl similarity index 100% rename from Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/OcclusionCullingDebugShaderVariables.cs.hlsl rename to Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/OcclusionCullingDebugShaderVariables.cs.hlsl diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/OcclusionCullingDebugShaderVariables.cs.hlsl.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/OcclusionCullingDebugShaderVariables.cs.hlsl.meta similarity index 100% rename from Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/OcclusionCullingDebugShaderVariables.cs.hlsl.meta rename to Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/OcclusionCullingDebugShaderVariables.cs.hlsl.meta diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/OcclusionCullingDebugShaderVariables.cs.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/OcclusionCullingDebugShaderVariables.cs.meta similarity index 100% rename from Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/OcclusionCullingDebugShaderVariables.cs.meta rename to Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/OcclusionCullingDebugShaderVariables.cs.meta diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/OcclusionTestCommon.hlsl b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/OcclusionTestCommon.hlsl similarity index 100% rename from Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/OcclusionTestCommon.hlsl rename to Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/OcclusionTestCommon.hlsl diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/OcclusionTestCommon.hlsl.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/OcclusionTestCommon.hlsl.meta similarity index 100% rename from Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/OcclusionTestCommon.hlsl.meta rename to Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/OcclusionTestCommon.hlsl.meta diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Debug/DebugDisplayGPUResidentDrawer.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Debug/DebugDisplayGPUResidentDrawer.cs index a50c4e29c06..08b4db55dc4 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Debug/DebugDisplayGPUResidentDrawer.cs +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Debug/DebugDisplayGPUResidentDrawer.cs @@ -1,10 +1,10 @@ using System; -using System.Collections.Generic; using System.Reflection; using Unity.Collections; #if UNITY_EDITOR using UnityEditor; #endif + using static UnityEngine.Rendering.DebugUI; using static UnityEngine.Rendering.DebugUI.Widget; @@ -36,20 +36,20 @@ private bool displayBatcherStats } } - /// Returns the view instances id for the selected occluder debug view index, or 0 if not valid. - internal bool GetOccluderViewInstanceID(out EntityId viewInstanceID) + /// Returns the view EntityId for the selected occluder debug view index, or EntityId.Null if not valid. + internal bool GetOccluderViewID(out EntityId viewID) { DebugRendererBatcherStats debugStats = GPUResidentDrawer.GetDebugStats(); if (debugStats != null) { if (occluderDebugViewIndex >= 0 && occluderDebugViewIndex < debugStats.occluderStats.Length) { - viewInstanceID = debugStats.occluderStats[occluderDebugViewIndex].viewInstanceID; + viewID = debugStats.occluderStats[occluderDebugViewIndex].viewID; return true; } } - viewInstanceID = EntityId.None; + viewID = EntityId.None; return false; } @@ -165,13 +165,13 @@ private static DebugUI.Table.Row AddInstanceCullerViewDataRow(int viewIndex) { var viewStats = GetInstanceCullerViewStats(viewIndex); #if UNITY_EDITOR - Object view = EditorUtility.EntityIdToObject(viewStats.viewInstanceID); + Object view = EditorUtility.EntityIdToObject(viewStats.viewID); if (view) { - return $"{viewStats.viewInstanceID} ({view.name})"; + return $"{viewStats.viewID} ({view.name})"; } #endif - return viewStats.viewInstanceID; + return viewStats.viewID; } }, new DebugUI.Value { displayName = "Split Index", refreshRate = k_RefreshRate, formatString = k_FormatString, getter = () => GetInstanceCullerViewStats(viewIndex).splitIndex }, @@ -235,13 +235,13 @@ private static DebugUI.Table.Row AddInstanceOcclusionPassDataRow(int eventIndex) { var eventStats = GetInstanceOcclusionEventStats(eventIndex); #if UNITY_EDITOR - Object view = EditorUtility.EntityIdToObject(eventStats.viewInstanceID); + Object view = EditorUtility.EntityIdToObject(eventStats.viewID); if (view) { - return $"{eventStats.viewInstanceID} ({view.name})"; + return $"{eventStats.viewID} ({view.name})"; } #endif - return eventStats.viewInstanceID; + return eventStats.viewID; } }, new DebugUI.Value { displayName = "Event Type", refreshRate = k_RefreshRate, formatString = k_FormatString, getter = () => $"{GetInstanceOcclusionEventStats(eventIndex).eventType}" }, @@ -265,7 +265,7 @@ private static DebugUI.Table.Row AddOcclusionContextDataRow(int index) isHiddenCallback = () => index >= GetOcclusionContextsCounts(), children = { - new DebugUI.Value { displayName = "View Instance ID", refreshRate = k_RefreshRate, formatString = k_FormatString, getter = () => GetOccluderStats(index).viewInstanceID }, + new DebugUI.Value { displayName = "View Instance ID", refreshRate = k_RefreshRate, formatString = k_FormatString, getter = () => GetOccluderStats(index).viewID }, new DebugUI.Value { displayName = "Subview Count", refreshRate = k_RefreshRate, formatString = k_FormatString, getter = () => GetOccluderStats(index).subviewCount }, new DebugUI.Value { displayName = "Size Per Subview", refreshRate = k_RefreshRate, formatString = k_FormatString, getter = () => @@ -301,14 +301,14 @@ public SettingsPanel(DebugDisplayGPUResidentDrawer data) var settings = GPUResidentDrawer.GetGlobalSettingsFromRPAsset(); return GPUResidentDrawer.IsGPUResidentDrawerSupportedBySRP(settings, out var msg, out var _) ? string.Empty : msg; }, - isHiddenCallback = () => GPUResidentDrawer.IsEnabled() + isHiddenCallback = () => GPUResidentDrawer.IsInitialized() }; foldout.children.Add(helpBox); foldout.children.Add(new Container() { displayName = Strings.occlusionCullingTitle, - isHiddenCallback = () => !GPUResidentDrawer.IsEnabled(), + isHiddenCallback = () => !GPUResidentDrawer.IsInitialized(), children = { new DebugUI.BoolField { nameAndTooltip = Strings.occlusionTestOverlayEnable, getter = () => data.occlusionTestOverlayEnable, setter = value => data.occlusionTestOverlayEnable = value}, @@ -328,7 +328,7 @@ public SettingsPanel(DebugDisplayGPUResidentDrawer data) nameAndTooltip = Strings.displayBatcherStats, getter = () => data.displayBatcherStats, setter = value => data.displayBatcherStats = value, - isHiddenCallback = () => !GPUResidentDrawer.IsEnabled() + isHiddenCallback = () => !GPUResidentDrawer.IsInitialized() }); AddInstanceCullingStatsWidget(data); diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/GPUResidentDrawerDebug.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Debug/GPUResidentDrawerDebug.cs similarity index 92% rename from Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/GPUResidentDrawerDebug.cs rename to Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Debug/GPUResidentDrawerDebug.cs index 0741f7d4d39..8357c0be6c5 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/GPUResidentDrawerDebug.cs +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Debug/GPUResidentDrawerDebug.cs @@ -1,14 +1,12 @@ using System; -using System.Collections.Generic; using Unity.Collections; -using UnityEngine.Assertions; namespace UnityEngine.Rendering { internal struct InstanceCullerViewStats { public BatchCullingViewType viewType; - public EntityId viewInstanceID; + public EntityId viewID; public int splitIndex; public int visibleInstancesOnCPU; public int visibleInstancesOnGPU; @@ -25,7 +23,7 @@ internal enum InstanceOcclusionEventType internal struct InstanceOcclusionEventStats { - public EntityId viewInstanceID; + public EntityId viewID; public InstanceOcclusionEventType eventType; public int occluderVersion; public int subviewMask; @@ -38,7 +36,7 @@ internal struct InstanceOcclusionEventStats internal struct DebugOccluderStats { - public EntityId viewInstanceID; + public EntityId viewID; public int subviewCount; public Vector2Int occluderMipLayoutSize; } @@ -69,7 +67,7 @@ public void FinalizeInstanceCullerViewStats() InstanceCullerViewStats cullerStats = instanceCullerStats[viewIndex]; InstanceOcclusionEventStats lastOcclusionEventStats = GetLastInstanceOcclusionEventStatsForView(viewIndex); - if (lastOcclusionEventStats.viewInstanceID == cullerStats.viewInstanceID) + if (lastOcclusionEventStats.viewID == cullerStats.viewID) { // The Min test is because the SelectionOutline view (and probably picking as well) share the same viewInstanceID with // the scene camera for instance, so we pick up the camera's occlusion event. And we can't have more instances on GPU than we had on CPU. @@ -91,10 +89,10 @@ private InstanceOcclusionEventStats GetLastInstanceOcclusionEventStatsForView(in { if (viewIndex < instanceCullerStats.Length) { - EntityId viewInstanceID = instanceCullerStats[viewIndex].viewInstanceID; + EntityId viewID = instanceCullerStats[viewIndex].viewID; for (int passIndex = instanceOcclusionEventStats.Length - 1; passIndex >= 0; passIndex--) { - if (instanceOcclusionEventStats[passIndex].viewInstanceID == viewInstanceID) + if (instanceOcclusionEventStats[passIndex].viewID == viewID) return instanceOcclusionEventStats[passIndex]; } } diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/GPUResidentDrawerDebug.cs.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Debug/GPUResidentDrawerDebug.cs.meta similarity index 100% rename from Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/GPUResidentDrawerDebug.cs.meta rename to Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Debug/GPUResidentDrawerDebug.cs.meta diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/GPUResidentBatcher.SpeedTree.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/GPUResidentBatcher.SpeedTree.cs deleted file mode 100644 index dcc4b8aaeb4..00000000000 --- a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/GPUResidentBatcher.SpeedTree.cs +++ /dev/null @@ -1,98 +0,0 @@ -using System; -using System.Collections.Generic; -using Unity.Collections; -using Unity.Jobs; -using Unity.Jobs.LowLevel.Unsafe; -using Unity.Burst; -using UnityEngine.Profiling; -using Unity.Mathematics; -using UnityEngine.Assertions; - -namespace UnityEngine.Rendering -{ - internal partial class GPUResidentBatcher : IDisposable - { - private ParallelBitArray m_ProcessedThisFrameTreeBits; - - private void ProcessTrees() - { - int treeInstancesCount = m_BatchersContext.GetAliveInstancesOfType(InstanceType.SpeedTree); - - if (treeInstancesCount == 0) - return; - - ParallelBitArray compactedVisibilityMasks = m_InstanceCullingBatcher.GetCompactedVisibilityMasks(syncCullingJobs: false); - - if (!compactedVisibilityMasks.IsCreated) - return; - - Profiler.BeginSample("GPUResidentInstanceBatcher.ProcessTrees"); - - int maxInstancesCount = m_BatchersContext.aliveInstances.Length; - - if(!m_ProcessedThisFrameTreeBits.IsCreated) - m_ProcessedThisFrameTreeBits = new ParallelBitArray(maxInstancesCount, Allocator.TempJob); - else if(m_ProcessedThisFrameTreeBits.Length < maxInstancesCount) - m_ProcessedThisFrameTreeBits.Resize(maxInstancesCount); - - bool becomeVisibleOnly = !Application.isPlaying; - var visibleTreeRendererIDs = new NativeList(Allocator.TempJob); - var visibleTreeInstances = new NativeList(Allocator.TempJob); - - m_BatchersContext.GetVisibleTreeInstances(compactedVisibilityMasks, m_ProcessedThisFrameTreeBits, visibleTreeRendererIDs, visibleTreeInstances, - becomeVisibleOnly, out var becomeVisibleTreeInstancesCount); - - if (visibleTreeRendererIDs.Length > 0) - { - Profiler.BeginSample("GPUResidentInstanceBatcher.UpdateSpeedTreeWindAndUploadWindParamsToGPU"); - - // Become visible trees is a subset of visible trees. - var becomeVisibleTreeRendererIDs = visibleTreeRendererIDs.AsArray().GetSubArray(0, becomeVisibleTreeInstancesCount); - var becomeVisibleTreeInstances = visibleTreeInstances.AsArray().GetSubArray(0, becomeVisibleTreeInstancesCount); - - if (becomeVisibleTreeRendererIDs.Length > 0) - UpdateSpeedTreeWindAndUploadWindParamsToGPU(becomeVisibleTreeRendererIDs, becomeVisibleTreeInstances, history: true); - - UpdateSpeedTreeWindAndUploadWindParamsToGPU(visibleTreeRendererIDs.AsArray(), visibleTreeInstances.AsArray(), history: false); - - Profiler.EndSample(); - } - - visibleTreeRendererIDs.Dispose(); - visibleTreeInstances.Dispose(); - - Profiler.EndSample(); - } - - private unsafe void UpdateSpeedTreeWindAndUploadWindParamsToGPU(NativeArray treeRendererIDs, NativeArray treeInstances, bool history) - { - if (treeRendererIDs.Length == 0) - return; - - Assert.AreEqual(treeRendererIDs.Length, treeInstances.Length); - Assert.AreEqual(m_BatchersContext.renderersParameters.windParams.Length, (int)SpeedTreeWindParamIndex.MaxWindParamsCount); - - var gpuInstanceIndices = new NativeArray(treeInstances.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); - m_BatchersContext.instanceDataBuffer.CPUInstanceArrayToGPUInstanceArray(treeInstances, gpuInstanceIndices); - - if (!history) - m_BatchersContext.UpdateInstanceWindDataHistory(gpuInstanceIndices); - - GPUInstanceDataBufferUploader uploader = m_BatchersContext.CreateDataBufferUploader(treeInstances.Length, InstanceType.SpeedTree); - uploader.AllocateUploadHandles(treeInstances.Length); - - var windParams = new SpeedTreeWindParamsBufferIterator(); - windParams.bufferPtr = uploader.GetUploadBufferPtr(); - for (int i = 0; i < (int)SpeedTreeWindParamIndex.MaxWindParamsCount; ++i) - windParams.uintParamOffsets[i] = uploader.PrepareParamWrite(m_BatchersContext.renderersParameters.windParams[i].index); - windParams.uintStride = uploader.GetUIntPerInstance(); - windParams.elementOffset = 0; - windParams.elementsCount = treeInstances.Length; - SpeedTreeWindManager.UpdateWindAndWriteBufferWindParams(treeRendererIDs, windParams, history); - m_BatchersContext.SubmitToGpu(gpuInstanceIndices, ref uploader, submitOnlyWrittenParams: true); - - gpuInstanceIndices.Dispose(); - uploader.Dispose(); - } - } -} diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/GPUResidentBatcher.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/GPUResidentBatcher.cs deleted file mode 100644 index a7f39e7a5ff..00000000000 --- a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/GPUResidentBatcher.cs +++ /dev/null @@ -1,253 +0,0 @@ -using System; -using System.Collections.Generic; -using Unity.Collections; -using Unity.Jobs; -using UnityEngine.Profiling; -using Unity.Mathematics; -using UnityEngine.Assertions; -using UnityEngine.Rendering.RenderGraphModule; - -namespace UnityEngine.Rendering -{ - internal partial class GPUResidentBatcher : IDisposable - { - private RenderersBatchersContext m_BatchersContext; - private GPUDrivenProcessor m_GPUDrivenProcessor; - private GPUDrivenRendererDataCallback m_UpdateRendererInstancesAndBatchesCallback; - private GPUDrivenRendererDataCallback m_UpdateRendererBatchesCallback; - - internal RenderersBatchersContext batchersContext { get => m_BatchersContext; } - internal OcclusionCullingCommon occlusionCullingCommon { get => m_BatchersContext.occlusionCullingCommon; } - internal InstanceCullingBatcher instanceCullingBatcher { get => m_InstanceCullingBatcher; } - - private InstanceCullingBatcher m_InstanceCullingBatcher = null; - - public GPUResidentBatcher( - RenderersBatchersContext batcherContext, - InstanceCullingBatcherDesc instanceCullerBatcherDesc, - GPUDrivenProcessor gpuDrivenProcessor) - { - m_BatchersContext = batcherContext; - m_GPUDrivenProcessor = gpuDrivenProcessor; - m_UpdateRendererInstancesAndBatchesCallback = UpdateRendererInstancesAndBatches; - m_UpdateRendererBatchesCallback = UpdateRendererBatches; - - m_InstanceCullingBatcher = new InstanceCullingBatcher(batcherContext, instanceCullerBatcherDesc, OnFinishedCulling); - } - - public void Dispose() - { - m_GPUDrivenProcessor.ClearMaterialFilters(); - m_InstanceCullingBatcher.Dispose(); - - if (m_ProcessedThisFrameTreeBits.IsCreated) - m_ProcessedThisFrameTreeBits.Dispose(); - } - - public void OnBeginContextRendering() - { - if (m_ProcessedThisFrameTreeBits.IsCreated) - m_ProcessedThisFrameTreeBits.Dispose(); - } - - public void OnEndContextRendering() - { - m_InstanceCullingBatcher?.OnEndContextRendering(); - } - - public void OnBeginCameraRendering(Camera camera) - { - m_InstanceCullingBatcher?.OnBeginCameraRendering(camera); - } - - public void OnEndCameraRendering(Camera camera) - { - m_InstanceCullingBatcher?.OnEndCameraRendering(camera); - } - - public void UpdateFrame() - { - m_InstanceCullingBatcher.UpdateFrame(); - m_BatchersContext.UpdateFrame(); - } - - public void DestroyMaterials(NativeArray destroyedMaterials) - { - m_InstanceCullingBatcher.DestroyMaterials(destroyedMaterials); - } - - public void DestroyDrawInstances(NativeArray instances) - { - m_InstanceCullingBatcher.DestroyDrawInstances(instances); - } - - public void DestroyMeshes(NativeArray destroyedMeshes) - { - m_InstanceCullingBatcher.DestroyMeshes(destroyedMeshes); - } - - internal void FreeRendererGroupInstances(NativeArray rendererGroupIDs) - { - if (rendererGroupIDs.Length == 0) - return; - - var instances = new NativeList(rendererGroupIDs.Length, Allocator.TempJob); - m_BatchersContext.ScheduleQueryRendererGroupInstancesJob(rendererGroupIDs, instances).Complete(); - DestroyDrawInstances(instances.AsArray()); - instances.Dispose(); - - m_BatchersContext.FreeRendererGroupInstances(rendererGroupIDs); - } - - public void InstanceOcclusionTest(RenderGraph renderGraph, in OcclusionCullingSettings settings, ReadOnlySpan subviewOcclusionTests) - { - if (!m_BatchersContext.hasBoundingSpheres) - return; - - m_InstanceCullingBatcher.culler.InstanceOcclusionTest(renderGraph, settings, subviewOcclusionTests, m_BatchersContext); - } - - public void UpdateInstanceOccluders(RenderGraph renderGraph, in OccluderParameters occluderParams, ReadOnlySpan occluderSubviewUpdates) - { - if (!m_BatchersContext.hasBoundingSpheres) - return; - - m_BatchersContext.occlusionCullingCommon.UpdateInstanceOccluders(renderGraph, occluderParams, occluderSubviewUpdates); - } - - public void UpdateRenderers(NativeArray renderersID, bool materialUpdateOnly = false) - { - if (renderersID.Length == 0) - return; - - m_GPUDrivenProcessor.enablePartialRendering = false; - m_GPUDrivenProcessor.EnableGPUDrivenRenderingAndDispatchRendererData(renderersID, - materialUpdateOnly ? m_UpdateRendererBatchesCallback : m_UpdateRendererInstancesAndBatchesCallback, materialUpdateOnly); - m_GPUDrivenProcessor.enablePartialRendering = false; - } - -#if UNITY_EDITOR - public void UpdateSelectedRenderers(NativeArray renderersID) - { - var instances = new NativeArray(renderersID.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); - m_BatchersContext.ScheduleQueryRendererGroupInstancesJob(renderersID, instances).Complete(); - m_BatchersContext.UpdateSelectedInstances(instances); - instances.Dispose(); - } -#endif - - public JobHandle SchedulePackedMaterialCacheUpdate(NativeArray materialIDs, - NativeArray packedMaterialDatas) - { - return m_InstanceCullingBatcher.SchedulePackedMaterialCacheUpdate(materialIDs, packedMaterialDatas); - } - - public void PostCullBeginCameraRendering(RenderRequestBatcherContext context) - { - m_InstanceCullingBatcher.PostCullBeginCameraRendering(context); - } - - public void OnSetupAmbientProbe() - { - m_BatchersContext.UpdateAmbientProbeAndGpuBuffer(forceUpdate: false); - } - - private void UpdateRendererInstancesAndBatches(in GPUDrivenRendererGroupData rendererData, IList meshes, IList materials) - { - FreeRendererGroupInstances(rendererData.invalidRendererGroupID); - - if (rendererData.rendererGroupID.Length == 0) - return; - - Profiler.BeginSample("GPUResidentInstanceBatcher.UpdateRendererInstancesAndBatches"); - { - // -------------------------------------------------------------------------------------------------------------------------------------- - // Allocate and Update CPU instance data - // -------------------------------------------------------------------------------------------------------------------------------------- - var instances = new NativeArray(rendererData.localToWorldMatrix.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); - - Profiler.BeginSample("AllocateInstanceData"); - { - m_BatchersContext.ReallocateAndGetInstances(rendererData, instances); - var updateInstanceDataJob = m_BatchersContext.ScheduleUpdateInstanceDataJob(instances, rendererData); - - GPUInstanceDataBufferUploader uploader = m_BatchersContext.CreateDataBufferUploader(instances.Length, InstanceType.MeshRenderer); - uploader.AllocateUploadHandles(instances.Length); - JobHandle lightmapSTWriteJobHandle = uploader.WriteInstanceDataJob(m_BatchersContext.renderersParameters.lightmapScale.index, - rendererData.lightmapScaleOffset, - rendererData.rendererGroupIndex); - - JobHandle rendererUserValuesWriteJobHandle = uploader.WriteInstanceDataJob(m_BatchersContext.renderersParameters.rendererUserValues.index, - rendererData.rendererUserValues, - rendererData.rendererGroupIndex); - - JobHandle.CombineDependencies(lightmapSTWriteJobHandle, rendererUserValuesWriteJobHandle).Complete(); - - m_BatchersContext.SubmitToGpu(instances, ref uploader, submitOnlyWrittenParams: true); - m_BatchersContext.ChangeInstanceBufferVersion(); - uploader.Dispose(); - - // -------------------------------------------------------------------------------------------------------------------------------------- - // Update and Upload Transform data to GPU - // ---------------------------------------------------------------------------------------------------------------------------------- - updateInstanceDataJob.Complete(); - m_BatchersContext.InitializeInstanceTransforms(instances, rendererData.localToWorldMatrix, rendererData.prevLocalToWorldMatrix); - - } - Profiler.EndSample(); - - // -------------------------------------------------------------------------------------------------------------------------------------- - // Instance culling batcher - // -------------------------------------------------------------------------------------------------------------------------------------- - - Profiler.BeginSample("InstanceCullingBatcher.BuildBatch"); - { - m_InstanceCullingBatcher.BuildBatch(instances, rendererData, true); - } - Profiler.EndSample(); - - instances.Dispose(); - } - Profiler.EndSample(); - } - - private void UpdateRendererBatches(in GPUDrivenRendererGroupData rendererData, IList meshes, IList materials) - { - if (rendererData.rendererGroupID.Length == 0) - return; - - Profiler.BeginSample("GPUResidentInstanceBatcher.UpdateRendererBatches"); - { - // -------------------------------------------------------------------------------------------------------------------------------------- - // Get Instances - // -------------------------------------------------------------------------------------------------------------------------------------- - var instances = new NativeList(rendererData.localToWorldMatrix.Length, Allocator.TempJob); - - Profiler.BeginSample("QueryInstances"); - { - m_BatchersContext.ScheduleQueryRendererGroupInstancesJob(rendererData.rendererGroupID, instances).Complete(); - } - Profiler.EndSample(); - - // -------------------------------------------------------------------------------------------------------------------------------------- - // Instance culling batcher - // -------------------------------------------------------------------------------------------------------------------------------------- - - Profiler.BeginSample("InstanceCullingBatcher.BuildBatch"); - { - m_InstanceCullingBatcher.BuildBatch(instances.AsArray(), rendererData, false); - } - Profiler.EndSample(); - - instances.Dispose(); - } - Profiler.EndSample(); - } - - private void OnFinishedCulling(IntPtr customCullingResult) - { - ProcessTrees(); - m_InstanceCullingBatcher.OnFinishedCulling(customCullingResult); - } - } -} diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/GPUResidentContext.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/GPUResidentContext.cs new file mode 100644 index 00000000000..19d311eeaa1 --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/GPUResidentContext.cs @@ -0,0 +1,52 @@ +using System; +using Unity.Collections; + +namespace UnityEngine.Rendering +{ + internal class GPUResidentContext : IDisposable + { + private InstanceDataSystem m_InstanceDataSystem; + private LODGroupDataSystem m_LODGroupDataSystem; + private InstanceCuller m_Culler; + private OcclusionCullingCommon m_OcclusionCullingCommon; + private InstanceCullingBatcher m_InstanceCullingBatcher; + private GPUResidentDrawerResources m_Resources; + private DebugRendererBatcherStats m_DebugStats; + + public InstanceDataSystem instanceDataSystem => m_InstanceDataSystem; + public LODGroupDataSystem lodGroupDataSystem => m_LODGroupDataSystem; + public InstanceCuller culler => m_Culler; + internal OcclusionCullingCommon occlusionCullingCommon => m_OcclusionCullingCommon; + public InstanceCullingBatcher batcher => m_InstanceCullingBatcher; + public GPUResidentDrawerResources resources => m_Resources; + internal DebugRendererBatcherStats debugStats => m_DebugStats; + + public SphericalHarmonicsL2 cachedAmbientProbe; + public readonly float smallMeshScreenPercentage; + + public GPUResidentContext(in GPUResidentDrawerSettings settings, + InstanceDataSystem instanceDataSystem, + LODGroupDataSystem lodGroupDataSystem, + InstanceCuller culler, + OcclusionCullingCommon occlusionCullingCommon, + InstanceCullingBatcher instanceCullingBatcher, + GPUResidentDrawerResources resources) + { + m_InstanceDataSystem = instanceDataSystem; + m_LODGroupDataSystem = lodGroupDataSystem; + m_Culler = culler; + m_OcclusionCullingCommon = occlusionCullingCommon; + m_InstanceCullingBatcher = instanceCullingBatcher; + m_Resources = resources; + m_DebugStats = new DebugRendererBatcherStats(); // for now, always allow the possibility of reading counter stats from the cullers. + cachedAmbientProbe = RenderSettings.ambientProbe; + smallMeshScreenPercentage = settings.smallMeshScreenPercentage; + } + + public void Dispose() + { + m_DebugStats?.Dispose(); + m_DebugStats = null; + } + } +} diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/RenderersBatchersContext.cs.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/GPUResidentContext.cs.meta similarity index 100% rename from Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/RenderersBatchersContext.cs.meta rename to Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/GPUResidentContext.cs.meta diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/GPUResidentDrawer.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/GPUResidentDrawer.cs index 798ba16771a..cdf03340a34 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/GPUResidentDrawer.cs +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/GPUResidentDrawer.cs @@ -1,36 +1,67 @@ #define GPU_RESIDENT_DRAWER_ALLOW_FORCE_ON +#if UNITY_EDITOR +#define GPU_RESIDENT_DRAWER_ENABLE_VALIDATION +#endif +//#define GPU_RESIDENT_DRAWER_ENABLE_DEEP_VALIDATION + using System; using System.Collections.Generic; using Unity.Collections; using UnityEngine.Assertions; using UnityEngine.LowLevel; using UnityEngine.PlayerLoop; -using UnityEngine.Profiling; using UnityEngine.SceneManagement; -using static UnityEngine.ObjectDispatcher; -using Unity.Jobs; -using static UnityEngine.Rendering.RenderersParameters; -using Unity.Jobs.LowLevel.Unsafe; using UnityEngine.Rendering.RenderGraphModule; -using Unity.Collections.LowLevel.Unsafe; using Unity.Burst; #if UNITY_EDITOR using UnityEditor; -using UnityEditor.Rendering; #endif namespace UnityEngine.Rendering { + internal struct InternalGPUResidentDrawerSettings + { + public RenderPipelineAsset renderPipelineAsset; + public GPUResidentDrawerResources resources; + public OnCullingCompleteCallback onCompleteCallback; + public bool isManagedByUnitTest; + + public static readonly InternalGPUResidentDrawerSettings Default = new InternalGPUResidentDrawerSettings + { + renderPipelineAsset = null, + resources = null, + onCompleteCallback = null, + isManagedByUnitTest = false, + }; + } + /// /// Static utility class for updating data post cull in begin camera rendering /// + [BurstCompile] public partial class GPUResidentDrawer { - internal static GPUResidentDrawer instance { get => s_Instance; } +#if GPU_RESIDENT_DRAWER_ENABLE_VALIDATION || GPU_RESIDENT_DRAWER_ENABLE_DEEP_VALIDATION + internal const bool EnableValidation = true; +#else + internal const bool EnableValidation = false; +#endif + +#if GPU_RESIDENT_DRAWER_ENABLE_DEEP_VALIDATION + internal const bool EnableDeepValidation = true; +#else + internal const bool EnableDeepValidation = false; +#endif + + internal static bool MaintainContext { get; set; } = false; + internal static bool ForceOcclusion { get; set; } = false; + private static GPUResidentDrawer s_Instance = null; + private static uint s_InstanceVersion = 0; + //////////////////////////////////////// // Public API for rendering pipelines // //////////////////////////////////////// @@ -50,7 +81,7 @@ public static bool IsInstanceOcclusionCullingEnabled() return false; if (s_Instance.settings.enableOcclusionCulling) - return true; + return true; return false; } @@ -63,16 +94,15 @@ public static bool IsInstanceOcclusionCullingEnabled() /// public static void PostCullBeginCameraRendering(RenderRequestBatcherContext context) { - s_Instance?.batcher.PostCullBeginCameraRendering(context); + s_Instance?.OnPostCullBeginCameraRendering(context); } - /// /// Utility function for updating probe data after global ambient probe is set up /// public static void OnSetupAmbientProbe() { - s_Instance?.batcher.OnSetupAmbientProbe(); + s_Instance?.UpdateAmbientProbeAndGPUBuffer(forceUpdate: false); } /// @@ -88,7 +118,9 @@ public static void OnSetupAmbientProbe() /// Specifies the occluder subviews to use with each culling split index. public static void InstanceOcclusionTest(RenderGraph renderGraph, in OcclusionCullingSettings settings, ReadOnlySpan subviewOcclusionTests) { - s_Instance?.batcher.InstanceOcclusionTest(renderGraph, settings, subviewOcclusionTests); + if (s_Instance == null || !s_Instance.m_InstanceDataSystem.hasBoundingSpheres) + return; + s_Instance.m_Culler.InstanceOcclusionTest(renderGraph, settings, subviewOcclusionTests, s_Instance.m_GRDContext); } /// @@ -103,7 +135,9 @@ public static void InstanceOcclusionTest(RenderGraph renderGraph, in OcclusionCu /// Specifies which occluder subviews to update from slices of the input depth buffer. public static void UpdateInstanceOccluders(RenderGraph renderGraph, in OccluderParameters occluderParameters, ReadOnlySpan occluderSubviewUpdates) { - s_Instance?.batcher.UpdateInstanceOccluders(renderGraph, occluderParameters, occluderSubviewUpdates); + if (s_Instance == null || !s_Instance.m_InstanceDataSystem.hasBoundingSpheres) + return; + s_Instance.m_OcclusionCullingCommon.UpdateInstanceOccluders(renderGraph, s_Instance.m_GRDContext, occluderParameters, occluderSubviewUpdates); } /// @@ -113,7 +147,7 @@ public static void UpdateInstanceOccluders(RenderGraph renderGraph, in OccluderP public static void ReinitializeIfNeeded() { #if UNITY_EDITOR - if (!IsForcedOnViaCommandLine() && !MaintainContext && (IsProjectSupported() != IsEnabled())) + if (!IsForcedOnViaCommandLine() && !MaintainContext && (IsProjectSupported() != IsInitialized())) { Reinitialize(); } @@ -129,11 +163,11 @@ public static void ReinitializeIfNeeded() /// /// Render graph that will have a compute pass added. /// The rendering debugger debug settings to read parameters from. - /// The instance ID of the camera using a GPU occlusion test. + /// The EntityId of the camera using a GPU occlusion test. /// The color buffer to render the overlay on. - public static void RenderDebugOcclusionTestOverlay(RenderGraph renderGraph, DebugDisplayGPUResidentDrawer debugSettings, EntityId viewInstanceID, TextureHandle colorBuffer) + public static void RenderDebugOcclusionTestOverlay(RenderGraph renderGraph, DebugDisplayGPUResidentDrawer debugSettings, EntityId viewID, TextureHandle colorBuffer) { - s_Instance?.batcher.occlusionCullingCommon.RenderDebugOcclusionTestOverlay(renderGraph, debugSettings, viewInstanceID, colorBuffer); + s_Instance?.m_OcclusionCullingCommon.RenderDebugOcclusionTestOverlay(renderGraph, debugSettings, viewID, colorBuffer); } /// @@ -146,15 +180,34 @@ public static void RenderDebugOcclusionTestOverlay(RenderGraph renderGraph, Debu /// The color buffer to render the overlay on. public static void RenderDebugOccluderOverlay(RenderGraph renderGraph, DebugDisplayGPUResidentDrawer debugSettings, Vector2 screenPos, float maxHeight, TextureHandle colorBuffer) { - s_Instance?.batcher.occlusionCullingCommon.RenderDebugOccluderOverlay(renderGraph, debugSettings, screenPos, maxHeight, colorBuffer); + s_Instance?.m_OcclusionCullingCommon.RenderDebugOccluderOverlay(renderGraph, debugSettings, screenPos, maxHeight, colorBuffer); } #endregion - internal static DebugRendererBatcherStats GetDebugStats() - { - return s_Instance?.m_BatchersContext.debugStats; - } + internal static bool IsEnabledFromSettings() => GetGlobalSettingsFromRPAsset().mode != GPUResidentDrawerMode.Disabled; + + internal static bool IsInitialized() => s_Instance != null; + + internal static uint GetInstanceVersion() => s_InstanceVersion; + + internal static NativeReference GetGPUArchetypeManager() => s_Instance != null ? s_Instance.m_InstanceDataSystem.archetypeManager : default; + + internal static ref DefaultGPUComponents GetDefaultGPUComponents() => ref s_Instance.m_InstanceDataSystem.defaultGPUComponents; + + internal static GPUInstanceDataBuffer.ReadOnly GetInstanceDataBuffer() => s_Instance != null ? s_Instance.m_InstanceDataSystem.gpuBuffer.AsReadOnly() : default; + + internal static GPUInstanceDataBufferReadback ReadbackInstanceDataBuffer() where T : unmanaged => s_Instance != null ? s_Instance.m_InstanceDataSystem.ReadbackInstanceDataBuffer() : default; + + internal static DebugRendererBatcherStats GetDebugStats() => s_Instance?.m_GRDContext.debugStats; + + internal static void PushMeshRendererUpdateBatches(NativeArray batches) => s_Instance.m_WorldProcessor.PushMeshRendererUpdateBatches(batches); + + internal static void PushLODGroupUpdateBatches(NativeArray batches) => s_Instance.m_WorldProcessor.PushLODGroupUpdateBatches(batches); + + internal static void PushMeshRendererDeletionBatches(NativeArray> batches) => s_Instance.m_WorldProcessor.PushMeshRendererDeletionBatch(batches); + + internal static void PushLODGroupDeletionBatches(NativeArray> batches) => s_Instance.m_WorldProcessor.PushLODGroupDeletionBatch(batches); private void InsertIntoPlayerLoop() { @@ -217,16 +270,10 @@ private void RemoveFromPlayerLoop() #if UNITY_EDITOR private static void OnAssemblyReload() { - if (s_Instance is not null) - s_Instance.Dispose(); + Cleanup(); } #endif - internal static bool IsEnabled() - { - return s_Instance is not null; - } - internal static GPUResidentDrawerSettings GetGlobalSettingsFromRPAsset() { var renderPipelineAsset = GraphicsSettings.currentRenderPipeline; @@ -269,9 +316,6 @@ internal static bool IsOcclusionForcedOnViaCommandLine() #endif } - internal static bool MaintainContext { get; set; } = false; - internal static bool ForceOcclusion { get; set; } = false; - internal static void Reinitialize() { var settings = GetGlobalSettingsFromRPAsset(); @@ -289,26 +333,29 @@ internal static void Reinitialize() { Debug.LogError($"The GPU Resident Drawer encountered an error during initialization. The standard SRP path will be used instead. [Error: {exception.Message}]"); Debug.LogError($"GPU Resident drawer stack trace: {exception.StackTrace}"); - CleanUp(); + Cleanup(); } #endif } - private static void CleanUp() + private static void Cleanup() { if (s_Instance == null) return; s_Instance.Dispose(); s_Instance = null; + ++s_InstanceVersion; } private static void Recreate(GPUResidentDrawerSettings settings) { - CleanUp(); + Cleanup(); + if (IsGPUResidentDrawerSupportedBySRP(settings, out var message, out var severity)) { - s_Instance = new GPUResidentDrawer(settings, 4096, 0); + s_Instance = new GPUResidentDrawer(settings); + ++s_InstanceVersion; } else { @@ -316,19 +363,23 @@ private static void Recreate(GPUResidentDrawerSettings settings) } } - IntPtr m_ContextIntPtr = IntPtr.Zero; - - internal GPUResidentBatcher batcher { get => m_Batcher; } - - internal GPUResidentDrawerSettings settings { get => m_Settings; } - + private IntPtr m_ContextIntPtr = IntPtr.Zero; private GPUResidentDrawerSettings m_Settings; - private GPUDrivenProcessor m_GPUDrivenProcessor = null; - private RenderersBatchersContext m_BatchersContext = null; - private GPUResidentBatcher m_Batcher = null; - - private ObjectDispatcher m_Dispatcher; - + private InternalGPUResidentDrawerSettings m_InternalSettings; + + // Core Systems + internal GPUDrivenProcessor m_GPUDrivenProcessor; + internal ObjectDispatcher m_ObjectDispatcher; + internal InstanceDataSystem m_InstanceDataSystem; + internal LODGroupDataSystem m_LODGroupDataSystem; + internal InstanceCuller m_Culler; + internal OcclusionCullingCommon m_OcclusionCullingCommon; + internal InstanceCullingBatcher m_Batcher; + internal GPUResidentContext m_GRDContext; + internal SpeedTreeWindGPUDataUpdater m_SpeedTreeWindGPUDataUpdater; + internal WorldProcessor m_WorldProcessor; + + internal GPUResidentDrawerSettings settings => m_Settings; #if UNITY_EDITOR private static readonly bool s_IsForcedOnViaCommandLine; @@ -337,11 +388,9 @@ private static void Recreate(GPUResidentDrawerSettings settings) private NativeList m_FrameCameraIDs; private bool m_FrameUpdateNeeded = false; - private bool m_IsSelectionDirty; - static GPUResidentDrawer() { - Lightmapping.bakeCompleted += Reinitialize; + Lightmapping.bakeCompleted += Reinitialize; #if GPU_RESIDENT_DRAWER_ALLOW_FORCE_ON foreach (var arg in Environment.GetCommandLineArgs()) @@ -361,50 +410,60 @@ static GPUResidentDrawer() } #endif - private GPUResidentDrawer(GPUResidentDrawerSettings settings, int maxInstanceCount, int maxTreeInstanceCount) + internal GPUResidentDrawer(in GPUResidentDrawerSettings settings) : this(settings, InternalGPUResidentDrawerSettings.Default) {} + + internal GPUResidentDrawer(in GPUResidentDrawerSettings settings, in InternalGPUResidentDrawerSettings internalSettings) { - var resources = GraphicsSettings.GetRenderPipelineSettings(); - var renderPipelineAsset = GraphicsSettings.currentRenderPipeline; - var mbAsset = renderPipelineAsset as IGPUResidentRenderPipeline; - Debug.Assert(mbAsset != null, "No compatible Render Pipeline found"); - Assert.IsFalse(settings.mode == GPUResidentDrawerMode.Disabled); - m_Settings = settings; + Assert.IsTrue(settings.mode != GPUResidentDrawerMode.Disabled); - var rbcDesc = RenderersBatchersContextDesc.NewDefault(); - rbcDesc.instanceNumInfo = new InstanceNumInfo(meshRendererNum: maxInstanceCount, speedTreeNum: maxTreeInstanceCount); - rbcDesc.supportDitheringCrossFade = settings.supportDitheringCrossFade; - rbcDesc.smallMeshScreenPercentage = settings.smallMeshScreenPercentage; - rbcDesc.enableBoundingSpheresInstanceData = settings.enableOcclusionCulling; - rbcDesc.enableCullerDebugStats = true; // for now, always allow the possibility of reading counter stats from the cullers. + var resources = internalSettings.resources != null ? internalSettings.resources : GraphicsSettings.GetRenderPipelineSettings(); + var renderPipelineAsset = internalSettings.renderPipelineAsset != null ? internalSettings.renderPipelineAsset : GraphicsSettings.currentRenderPipeline; - var instanceCullingBatcherDesc = InstanceCullingBatcherDesc.NewDefault(); -#if UNITY_EDITOR - instanceCullingBatcherDesc.brgPicking = settings.pickingShader; - instanceCullingBatcherDesc.brgLoading = settings.loadingShader; - instanceCullingBatcherDesc.brgError = settings.errorShader; -#endif + if (renderPipelineAsset is not IGPUResidentRenderPipeline) + Assert.IsTrue(internalSettings.isManagedByUnitTest, "No compatible Render Pipeline found"); + m_Settings = settings; + m_InternalSettings = internalSettings; m_GPUDrivenProcessor = new GPUDrivenProcessor(); - m_BatchersContext = new RenderersBatchersContext(rbcDesc, m_GPUDrivenProcessor, resources); - m_Batcher = new GPUResidentBatcher( - m_BatchersContext, - instanceCullingBatcherDesc, - m_GPUDrivenProcessor); - - m_Dispatcher = new ObjectDispatcher(); - m_Dispatcher.EnableTypeTracking(TypeTrackingFlags.SceneObjects); - m_Dispatcher.EnableTypeTracking(); - m_Dispatcher.EnableTypeTracking(); - m_Dispatcher.EnableTypeTracking(TypeTrackingFlags.SceneObjects); - m_Dispatcher.EnableTypeTracking(TypeTrackingFlags.SceneObjects | TypeTrackingFlags.EditorOnlyObjects); - - m_Dispatcher.EnableTransformTracking(TransformTrackingType.GlobalTRS); - m_Dispatcher.EnableTransformTracking(TransformTrackingType.GlobalTRS); + //@ Disable partial rendering for now as it hasn't been widely tested in the main branch. + //@ This feature was initially developed alongside deferred materials and was controlled by deferred materials settings. + //@ It enables rendering only the materials and sub-meshes within the same object that are supported by the BRG, while SRP handles the rest. + m_GPUDrivenProcessor.enablePartialRendering = false; + + m_ObjectDispatcher = new ObjectDispatcher(); + m_InstanceDataSystem = new InstanceDataSystem(1024, settings.enableOcclusionCulling, resources); + m_LODGroupDataSystem = new LODGroupDataSystem(settings.supportDitheringCrossFade); + m_Culler = new InstanceCuller(); + m_OcclusionCullingCommon = new OcclusionCullingCommon(); + m_Batcher = new InstanceCullingBatcher(); + m_GRDContext = new GPUResidentContext(settings, + m_InstanceDataSystem, + m_LODGroupDataSystem, + m_Culler, + m_OcclusionCullingCommon, + m_Batcher, + resources); + m_SpeedTreeWindGPUDataUpdater = new SpeedTreeWindGPUDataUpdater(); + m_WorldProcessor = new WorldProcessor(); + + m_ObjectDispatcher.EnableTypeTracking(); + m_ObjectDispatcher.EnableTypeTracking(); + m_ObjectDispatcher.EnableTypeTracking(ObjectDispatcher.TypeTrackingFlags.SceneObjects); + m_ObjectDispatcher.EnableTypeTracking(ObjectDispatcher.TypeTrackingFlags.SceneObjects); + m_ObjectDispatcher.EnableTypeTracking(ObjectDispatcher.TypeTrackingFlags.SceneObjects | ObjectDispatcher.TypeTrackingFlags.EditorOnlyObjects); + m_ObjectDispatcher.EnableTransformTracking(ObjectDispatcher.TransformTrackingType.GlobalTRS); + m_ObjectDispatcher.EnableTransformTracking(ObjectDispatcher.TransformTrackingType.GlobalTRS); + + m_Culler.Initialize(resources, m_GRDContext.debugStats); + m_OcclusionCullingCommon.Initialize(resources); + m_Batcher.Initialize(m_GRDContext, settings, OnFinishedCulling, internalSettings.onCompleteCallback); + m_SpeedTreeWindGPUDataUpdater.Initialize(m_InstanceDataSystem, m_Culler); + m_WorldProcessor.Initialize(m_GPUDrivenProcessor, m_ObjectDispatcher, m_GRDContext); #if UNITY_EDITOR - AssemblyReloadEvents.beforeAssemblyReload += OnAssemblyReload; m_FrameCameraIDs = new NativeList(1, Allocator.Persistent); - m_IsSelectionDirty = true; // Force at least one selection update + if (!internalSettings.isManagedByUnitTest) + AssemblyReloadEvents.beforeAssemblyReload += OnAssemblyReload; #endif SceneManager.sceneLoaded += OnSceneLoaded; @@ -412,16 +471,9 @@ private GPUResidentDrawer(GPUResidentDrawerSettings settings, int maxInstanceCou RenderPipelineManager.endContextRendering += OnEndContextRendering; RenderPipelineManager.beginCameraRendering += OnBeginCameraRendering; RenderPipelineManager.endCameraRendering += OnEndCameraRendering; -#if UNITY_EDITOR - Selection.selectionChanged += OnSelectionChanged; -#endif - // GPU Resident Drawer only supports legacy lightmap binding. - // Accordingly, we set the keyword globally across all shaders. - const string useLegacyLightmapsKeyword = "USE_LEGACY_LIGHTMAPS"; - Shader.EnableKeyword(useLegacyLightmapsKeyword); - - InsertIntoPlayerLoop(); + if (!internalSettings.isManagedByUnitTest) + InsertIntoPlayerLoop(); string extraText = IsForcedOnViaCommandLine() ? " (forced on via commandline)" : ""; extraText = MaintainContext ? " (forced on via MaintainContext)" : extraText; @@ -434,12 +486,16 @@ private GPUResidentDrawer(GPUResidentDrawerSettings settings, int maxInstanceCou Console.WriteLine($"GPU Resident Drawer created{extraText}."); } - private void Dispose() + internal void Dispose() { - Assert.IsNotNull(s_Instance); + NativeArray rendererIDs = m_InstanceDataSystem.renderWorld.instanceIDs; + if (rendererIDs.Length > 0) + m_GPUDrivenProcessor.DisableGPUDrivenRendering(rendererIDs); #if UNITY_EDITOR - AssemblyReloadEvents.beforeAssemblyReload -= OnAssemblyReload; + if (!m_InternalSettings.isManagedByUnitTest) + AssemblyReloadEvents.beforeAssemblyReload -= OnAssemblyReload; + if (m_FrameCameraIDs.IsCreated) m_FrameCameraIDs.Dispose(); #endif @@ -450,25 +506,30 @@ private void Dispose() RenderPipelineManager.endContextRendering -= OnEndContextRendering; RenderPipelineManager.beginCameraRendering -= OnBeginCameraRendering; RenderPipelineManager.endCameraRendering -= OnEndCameraRendering; -#if UNITY_EDITOR - Selection.selectionChanged -= OnSelectionChanged; -#endif - - RemoveFromPlayerLoop(); - - const string useLegacyLightmapsKeyword = "USE_LEGACY_LIGHTMAPS"; - Shader.DisableKeyword(useLegacyLightmapsKeyword); - - m_Dispatcher.Dispose(); - m_Dispatcher = null; - s_Instance = null; - - m_Batcher?.Dispose(); - - m_BatchersContext.Dispose(); + if (!m_InternalSettings.isManagedByUnitTest) + RemoveFromPlayerLoop(); + + m_WorldProcessor.Dispose(); + m_WorldProcessor = null; + m_SpeedTreeWindGPUDataUpdater.Dispose(); + m_SpeedTreeWindGPUDataUpdater = null; + m_Batcher.Dispose(); + m_Batcher = null; + m_OcclusionCullingCommon?.Dispose(); + m_OcclusionCullingCommon = null; + m_Culler.Dispose(); + m_Culler = null; + m_InstanceDataSystem.Dispose(); + m_InstanceDataSystem = null; + m_LODGroupDataSystem.Dispose(); + m_LODGroupDataSystem = null; + m_GRDContext.Dispose(); + m_GRDContext = null; + m_ObjectDispatcher.Dispose(); + m_ObjectDispatcher = null; m_GPUDrivenProcessor.Dispose(); - + m_GPUDrivenProcessor = null; m_ContextIntPtr = IntPtr.Zero; Console.WriteLine($"GPU Resident Drawer disposed."); @@ -477,8 +538,8 @@ private void Dispose() private void OnSceneLoaded(Scene scene, LoadSceneMode mode) { // Loaded scene might contain light probes that would affect existing objects. Hence we have to update all probes data. - if(mode == LoadSceneMode.Additive) - m_BatchersContext.UpdateAmbientProbeAndGpuBuffer(forceUpdate: true); + if (mode == LoadSceneMode.Additive) + UpdateAmbientProbeAndGPUBuffer(forceUpdate: true); } private static void PostPostLateUpdateStatic() @@ -486,22 +547,6 @@ private static void PostPostLateUpdateStatic() s_Instance?.PostPostLateUpdate(); } - private void OnBeginContextRendering(ScriptableRenderContext context, List cameras) - { - if (s_Instance is null) - return; - - // This logic ensures that EditorFrameUpdate is not called more than once after calling BeginContextRendering, unless EndContextRendering has also been called. - if (m_ContextIntPtr == IntPtr.Zero) - { - m_ContextIntPtr = context.Internal_GetPtr(); -#if UNITY_EDITOR - EditorFrameUpdate(cameras); -#endif - m_Batcher.OnBeginContextRendering(); - } - } - #if UNITY_EDITOR // If running in the editor the player loop might not run // In order to still have a single frame update we keep track of the camera ids @@ -524,456 +569,86 @@ private void EditorFrameUpdate(List cameras) if (newFrame) { if (m_FrameUpdateNeeded) - m_Batcher.UpdateFrame(); + { + CullerUpdateFrame(); + } else + { m_FrameUpdateNeeded = true; + } } } - - private void OnSelectionChanged() - { - m_IsSelectionDirty = true; - } - - private void UpdateSelection() - { - Profiler.BeginSample("GPUResidentDrawer.UpdateSelection"); - - Object[] renderers = Selection.GetFiltered(typeof(MeshRenderer), SelectionMode.Deep); - - var rendererIDs = new NativeArray(renderers.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); - - for (int i = 0; i < renderers.Length; ++i) - rendererIDs[i] = renderers[i] ? renderers[i].GetEntityId() : EntityId.None; - - m_Batcher.UpdateSelectedRenderers(rendererIDs); - - rendererIDs.Dispose(); - - Profiler.EndSample(); - } #endif - private void OnEndContextRendering(ScriptableRenderContext context, List cameras) + private void OnBeginContextRendering(ScriptableRenderContext context, List cameras) { - if (s_Instance is null) - return; - - if (m_ContextIntPtr == context.Internal_GetPtr()) + // This logic ensures that EditorFrameUpdate is not called more than once after calling BeginContextRendering, unless EndContextRendering has also been called. + if (m_ContextIntPtr == IntPtr.Zero) { - m_ContextIntPtr = IntPtr.Zero; - m_Batcher.OnEndContextRendering(); - } - } - - private void OnBeginCameraRendering(ScriptableRenderContext context, Camera camera) - { - m_Batcher.OnBeginCameraRendering(camera); - } - - private void OnEndCameraRendering(ScriptableRenderContext context, Camera camera) - { - m_Batcher.OnEndCameraRendering(camera); - } - - private void PostPostLateUpdate() - { - m_BatchersContext.UpdateAmbientProbeAndGpuBuffer(forceUpdate: false); - - Profiler.BeginSample("GPUResidentDrawer.DispatchChanges"); - var lodGroupTransformData = m_Dispatcher.GetTransformChangesAndClear(TransformTrackingType.GlobalTRS, Allocator.TempJob); - var lodGroupData = m_Dispatcher.GetTypeChangesAndClear(Allocator.TempJob, noScriptingArray: true); - var meshDataSorted = m_Dispatcher.GetTypeChangesAndClear(Allocator.TempJob, sortByInstanceID: true, noScriptingArray: true); - var cameraChanges = m_Dispatcher.GetTypeChangesAndClear(Allocator.TempJob, noScriptingArray: true); - var materialData = m_Dispatcher.GetTypeChangesAndClear(Allocator.TempJob, noScriptingArray: true); - var rendererData = m_Dispatcher.GetTypeChangesAndClear(Allocator.TempJob, noScriptingArray: true); - Profiler.EndSample(); - - Profiler.BeginSample("GPUResidentDrawer.ClassifyMaterials"); - ClassifyMaterials(materialData.changedID, out NativeList unsupportedChangedMaterials, - out NativeList supportedChangedMaterials, - out NativeList supportedChangedPackedMaterialDatas, Allocator.TempJob); - Profiler.EndSample(); - - Profiler.BeginSample("GPUResidentDrawer.FindUnsupportedRenderers"); - NativeList unsupportedRenderers = FindUnsupportedRenderers(unsupportedChangedMaterials.AsArray()); - Profiler.EndSample(); - - Profiler.BeginSample("GPUResidentDrawer.ProcessMaterials"); - ProcessMaterials(materialData.destroyedID, unsupportedChangedMaterials.AsArray()); - Profiler.EndSample(); - - Profiler.BeginSample("GPUResidentDrawer.ProcessMeshes"); - ProcessMeshes(meshDataSorted.destroyedID); - Profiler.EndSample(); - - Profiler.BeginSample("GPUResidentDrawer.ProcessLODGroups"); - ProcessLODGroups(lodGroupData.changedID, lodGroupData.destroyedID, lodGroupTransformData.transformedID); - Profiler.EndSample(); - - Profiler.BeginSample("GPUResidentDrawer.ProcessCameras"); - ProcessCameras(cameraChanges.changedID, cameraChanges.destroyedID); - Profiler.EndSample(); - - Profiler.BeginSample("GPUResidentDrawer.ProcessRenderers"); - ProcessRenderers(rendererData, unsupportedRenderers.AsArray()); - Profiler.EndSample(); - - Profiler.BeginSample("GPUResidentDrawer.ProcessRendererMaterialAndMeshChanges"); - ProcessRendererMaterialAndMeshChanges(rendererData.changedID, supportedChangedMaterials.AsArray(), supportedChangedPackedMaterialDatas.AsArray(), meshDataSorted.changedID); - Profiler.EndSample(); - - lodGroupTransformData.Dispose(); - lodGroupData.Dispose(); - meshDataSorted.Dispose(); - materialData.Dispose(); - cameraChanges.Dispose(); - rendererData.Dispose(); - unsupportedChangedMaterials.Dispose(); - unsupportedRenderers.Dispose(); - supportedChangedMaterials.Dispose(); - supportedChangedPackedMaterialDatas.Dispose(); - - m_BatchersContext.UpdateInstanceMotions(); - m_Batcher.UpdateFrame(); - + m_ContextIntPtr = context.Internal_GetPtr(); #if UNITY_EDITOR - if (m_IsSelectionDirty) - { - UpdateSelection(); - m_IsSelectionDirty = false; - } - - m_FrameUpdateNeeded = false; + EditorFrameUpdate(cameras); #endif - } - - private void ProcessMaterials(NativeArray destroyedID, NativeArray unsupportedMaterials) - { - if (destroyedID.Length > 0) - m_Batcher.DestroyMaterials(destroyedID); - - if (unsupportedMaterials.Length > 0) - m_Batcher.DestroyMaterials(unsupportedMaterials); - } - - private void ProcessCameras(NativeArray changedIDs, NativeArray destroyedIDs) - { - m_BatchersContext.UpdateCameras(changedIDs); - m_BatchersContext.FreePerCameraInstanceData(destroyedIDs); - } - - private void ProcessMeshes(NativeArray destroyedID) - { - if (destroyedID.Length == 0) - return; - - var destroyedMeshInstances = new NativeList(Allocator.TempJob); - ScheduleQueryMeshInstancesJob(destroyedID, destroyedMeshInstances).Complete(); - m_Batcher.DestroyDrawInstances(destroyedMeshInstances.AsArray()); - destroyedMeshInstances.Dispose(); - - //@ Check if we need to update instance bounds and light probe sampling positions after mesh is destroyed. - m_Batcher.DestroyMeshes(destroyedID); - } - - private void ProcessLODGroups(NativeArray changedID, NativeArray destroyed, NativeArray transformedID) - { - m_BatchersContext.DestroyLODGroups(destroyed); - m_BatchersContext.UpdateLODGroups(changedID); - m_BatchersContext.TransformLODGroups(transformedID); - } - - private void ProcessRendererMaterialAndMeshChanges(NativeArray excludedRenderers, NativeArray changedMaterials, NativeArray changedPackedMaterialDatas, NativeArray changedMeshes) - { - if (changedMaterials.Length == 0 && changedMeshes.Length == 0) - return; - - Profiler.BeginSample("GPUResidentDrawer.GetMaterialsWithChangedPackedMaterial"); - - var filteredMaterials = GetMaterialsWithChangedPackedMaterial(changedMaterials, changedPackedMaterialDatas, Allocator.TempJob); - // Make sure we update the packed material cache AFTER filtering the materials as we need to compare the packed materials - // with their previous values. - var updatePackedMaterialCacheJob = m_Batcher.SchedulePackedMaterialCacheUpdate(changedMaterials, changedPackedMaterialDatas); - - Profiler.EndSample(); - - if (filteredMaterials.Count == 0 && changedMeshes.Length == 0) - { - filteredMaterials.Dispose(); - updatePackedMaterialCacheJob.Complete(); - return; - } - - var sortedExcludedRenderers = new NativeArray(excludedRenderers, Allocator.TempJob); - if (sortedExcludedRenderers.Length > 0) - { - Profiler.BeginSample("ProcessRendererMaterialAndMeshChanges.Sort"); - sortedExcludedRenderers.SortJob().Schedule().Complete(); - Profiler.EndSample(); - } - - Profiler.BeginSample("GPUResidentDrawer.FindRenderersFromMaterialsOrMeshes"); - - var (renderersWithChangedMaterials, renderersWithChangedMeshes) = FindRenderersFromMaterialsOrMeshes(sortedExcludedRenderers, filteredMaterials, changedMeshes, Allocator.TempJob); - filteredMaterials.Dispose(); - - Profiler.EndSample(); - - sortedExcludedRenderers.Dispose(); - updatePackedMaterialCacheJob.Complete(); - - if (renderersWithChangedMaterials.Length == 0 && renderersWithChangedMeshes.Length == 0) - { - renderersWithChangedMaterials.Dispose(); - renderersWithChangedMeshes.Dispose(); - return; - } - - Profiler.BeginSample("GPUResidentDrawer.UpdateRenderers"); - { - var changedMaterialsCount = renderersWithChangedMaterials.Length; - var changedMeshesCount = renderersWithChangedMeshes.Length; - var totalCount = changedMaterialsCount + changedMeshesCount; - - var changedInstances = new NativeArray(totalCount, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); - var changedRenderers = new NativeArray(totalCount, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); - - NativeArray.Copy(renderersWithChangedMaterials.AsArray(), changedRenderers, changedMaterialsCount); - NativeArray.Copy(renderersWithChangedMeshes.AsArray(), changedRenderers.GetSubArray(changedMaterialsCount, changedMeshesCount), changedMeshesCount); - - ScheduleQueryRendererGroupInstancesJob(changedRenderers, changedInstances).Complete(); - - m_Batcher.DestroyDrawInstances(changedInstances); - m_Batcher.UpdateRenderers(renderersWithChangedMaterials.AsArray(), true); - m_Batcher.UpdateRenderers(renderersWithChangedMeshes.AsArray(), false); - - changedInstances.Dispose(); - changedRenderers.Dispose(); - - renderersWithChangedMaterials.Dispose(); - renderersWithChangedMeshes.Dispose(); + m_SpeedTreeWindGPUDataUpdater.OnBeginContextRendering(); } - Profiler.EndSample(); } - private void ProcessRenderers(TypeDispatchData rendererChanges, NativeArray unsupportedRenderers) - { - Profiler.BeginSample("GPUResidentDrawer.ProcessRenderers"); - - var changedInstances = new NativeArray(rendererChanges.changedID.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); - ScheduleQueryRendererGroupInstancesJob(rendererChanges.changedID, changedInstances).Complete(); - - m_Batcher.DestroyDrawInstances(changedInstances); - changedInstances.Dispose(); - m_Batcher.UpdateRenderers(rendererChanges.changedID); - - FreeRendererGroupInstances(rendererChanges.destroyedID, unsupportedRenderers); - - Profiler.EndSample(); - - Profiler.BeginSample("GPUResidentDrawer.TransformMeshRenderers"); - var transformChanges = m_Dispatcher.GetTransformChangesAndClear(TransformTrackingType.GlobalTRS, Allocator.TempJob); - var transformedInstances = new NativeArray(transformChanges.transformedID.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); - ScheduleQueryRendererGroupInstancesJob(transformChanges.transformedID, transformedInstances).Complete(); - // We can pull localToWorldMatrices directly from the renderers if we are doing update after PostLatUpdate. - // This will save us transform re computation as matrices are ready inside renderer's TransformInfo. - TransformInstances(transformedInstances, transformChanges.localToWorldMatrices); - transformedInstances.Dispose(); - transformChanges.Dispose(); - - Profiler.EndSample(); - } - - private void TransformInstances(NativeArray instances, NativeArray localToWorldMatrices) - { - Profiler.BeginSample("GPUResidentDrawer.TransformInstances"); - - m_BatchersContext.UpdateInstanceTransforms(instances, localToWorldMatrices); - - Profiler.EndSample(); - } - - private void FreeInstances(NativeArray instances) - { - Profiler.BeginSample("GPUResidentDrawer.FreeInstances"); - - m_Batcher.DestroyDrawInstances(instances); - m_BatchersContext.FreeInstances(instances); - - Profiler.EndSample(); - } - - private void FreeRendererGroupInstances(NativeArray rendererGroupIDs, NativeArray unsupportedRendererGroupIDs) + private void OnEndContextRendering(ScriptableRenderContext context, List cameras) { - Profiler.BeginSample("GPUResidentDrawer.FreeRendererGroupInstances"); + ParallelBitArray compactedVisibilityMasks = m_Culler.GetCompactedVisibilityMasks(syncCullingJobs: true); - m_Batcher.FreeRendererGroupInstances(rendererGroupIDs); - - if (unsupportedRendererGroupIDs.Length > 0) + if (m_ContextIntPtr == context.Internal_GetPtr()) { - m_Batcher.FreeRendererGroupInstances(unsupportedRendererGroupIDs); - m_GPUDrivenProcessor.DisableGPUDrivenRendering(unsupportedRendererGroupIDs); + m_ContextIntPtr = IntPtr.Zero; + if (compactedVisibilityMasks.IsCreated) + m_InstanceDataSystem.UpdatePerFrameInstanceVisibility(compactedVisibilityMasks); } - - Profiler.EndSample(); - } - - //@ Implement later... - private InstanceHandle AppendNewInstance(int rendererGroupID, in Matrix4x4 instanceTransform) - { - throw new NotImplementedException(); } - //@ Additionally we need to implement the way to tie external transforms (not Transform components) with instances. - //@ So that an individual instance could be transformed externally and then updated in the drawer. - - private JobHandle ScheduleQueryRendererGroupInstancesJob(NativeArray rendererGroupIDs, NativeArray instances) + private void OnBeginCameraRendering(ScriptableRenderContext context, Camera camera) { - return m_BatchersContext.ScheduleQueryRendererGroupInstancesJob(rendererGroupIDs, instances); + m_Culler.OnBeginCameraRendering(camera); } - private JobHandle ScheduleQueryRendererGroupInstancesJob(NativeArray rendererGroupIDs, NativeList instances) + private void OnEndCameraRendering(ScriptableRenderContext context, Camera camera) { - return m_BatchersContext.ScheduleQueryRendererGroupInstancesJob(rendererGroupIDs, instances); + m_Culler.OnEndCameraRendering(camera); } - private JobHandle ScheduleQueryRendererGroupInstancesJob(NativeArray rendererGroupIDs, NativeArray instancesOffset, NativeArray instancesCount, NativeList instances) + private void OnFinishedCulling(IntPtr customCullingResult) { - return m_BatchersContext.ScheduleQueryRendererGroupInstancesJob(rendererGroupIDs, instancesOffset, instancesCount, instances); + m_Culler.EnsureValidOcclusionTestResults(viewID : EntityId.From((ulong)customCullingResult)); + m_SpeedTreeWindGPUDataUpdater.UpdateGPUData(); } - private JobHandle ScheduleQueryMeshInstancesJob(NativeArray sortedMeshIDs, NativeList instances) - { - return m_BatchersContext.ScheduleQueryMeshInstancesJob(sortedMeshIDs, instances); - } + private void OnPostCullBeginCameraRendering(RenderRequestBatcherContext context) {} - private void ClassifyMaterials(NativeArray materials, out NativeList unsupportedMaterials, - out NativeList supportedMaterials, out NativeList supportedPackedMaterialDatas, Allocator allocator) + private void UpdateAmbientProbeAndGPUBuffer(bool forceUpdate) { - supportedMaterials = new NativeList(materials.Length, allocator); - unsupportedMaterials = new NativeList(materials.Length, allocator); - supportedPackedMaterialDatas = new NativeList(materials.Length, allocator); - - if (materials.Length > 0) + if (forceUpdate || m_GRDContext.cachedAmbientProbe != RenderSettings.ambientProbe) { - GPUResidentDrawerBurst.ClassifyMaterials(materials, m_Batcher.instanceCullingBatcher.batchMaterialHash.AsReadOnly(), - ref supportedMaterials, ref unsupportedMaterials, ref supportedPackedMaterialDatas); + m_GRDContext.cachedAmbientProbe = RenderSettings.ambientProbe; + m_InstanceDataSystem.UpdateAllInstanceProbes(); } } - private NativeList FindUnsupportedRenderers(NativeArray unsupportedMaterials) + private void CullerUpdateFrame() { - NativeList unsupportedRenderers = new NativeList(Allocator.TempJob); - - if (unsupportedMaterials.Length > 0) - { - GPUResidentDrawerBurst.FindUnsupportedRenderers(unsupportedMaterials, m_BatchersContext.sharedInstanceData.materialIDArrays, - m_BatchersContext.sharedInstanceData.rendererGroupIDs, ref unsupportedRenderers); - } - - return unsupportedRenderers; - } - - private NativeHashSet GetMaterialsWithChangedPackedMaterial(NativeArray materials, NativeArray packedMaterialDatas, Allocator allocator) - { - NativeHashSet filteredMaterials = new NativeHashSet(materials.Length, allocator); - - GPUResidentDrawerBurst.GetMaterialsWithChangedPackedMaterial(materials, packedMaterialDatas, - batcher.instanceCullingBatcher.packedMaterialHash.AsReadOnly(), ref filteredMaterials); - - return filteredMaterials; + m_Culler.UpdateFrame(m_InstanceDataSystem.renderWorld.cameraCount); + m_OcclusionCullingCommon.UpdateFrame(); + if (m_GRDContext.debugStats != null) + m_OcclusionCullingCommon.UpdateOccluderStats(m_GRDContext.debugStats); } - private (NativeList renderersWithMaterials, NativeList renderersWithMeshes) FindRenderersFromMaterialsOrMeshes(NativeArray sortedExcludeRenderers, NativeHashSet materials, NativeArray meshes, Allocator rendererListAllocator) - { - var sharedInstanceData = m_BatchersContext.sharedInstanceData; - var renderersWithMaterials = new NativeList(sharedInstanceData.rendererGroupIDs.Length, rendererListAllocator); - var renderersWithMeshes = new NativeList(sharedInstanceData.rendererGroupIDs.Length, rendererListAllocator); - - var jobHandle = new FindRenderersFromMaterialOrMeshJob - { - materialIDs = materials.AsReadOnly(), - materialIDArrays = sharedInstanceData.materialIDArrays, - meshIDs = meshes.AsReadOnly(), - meshIDArray = sharedInstanceData.meshIDs, - rendererGroupIDs = sharedInstanceData.rendererGroupIDs, - sortedExcludeRendererIDs = sortedExcludeRenderers.AsReadOnly(), - selectedRenderGroupsForMaterials = renderersWithMaterials.AsParallelWriter(), - selectedRenderGroupsForMeshes = renderersWithMeshes.AsParallelWriter() - }.ScheduleBatch(sharedInstanceData.rendererGroupIDs.Length, FindRenderersFromMaterialOrMeshJob.k_BatchSize); - jobHandle.Complete(); - - return (renderersWithMaterials, renderersWithMeshes); - } - - [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] - private unsafe struct FindRenderersFromMaterialOrMeshJob : IJobParallelForBatch + private void PostPostLateUpdate() { - public const int k_BatchSize = 128; - - [ReadOnly] public NativeHashSet.ReadOnly materialIDs; - [ReadOnly] public NativeArray.ReadOnly materialIDArrays; - [ReadOnly] public NativeArray.ReadOnly meshIDs; - [ReadOnly] public NativeArray.ReadOnly meshIDArray; - [ReadOnly] public NativeArray.ReadOnly rendererGroupIDs; - [ReadOnly] public NativeArray.ReadOnly sortedExcludeRendererIDs; - - [WriteOnly] public NativeList.ParallelWriter selectedRenderGroupsForMaterials; - [WriteOnly] public NativeList.ParallelWriter selectedRenderGroupsForMeshes; + UpdateAmbientProbeAndGPUBuffer(forceUpdate: false); + m_WorldProcessor.Update(); + CullerUpdateFrame(); - public void Execute(int startIndex, int count) - { - int* renderersToAddForMaterialsPtr = stackalloc int[k_BatchSize]; - var renderersToAddForMaterials = new UnsafeList(renderersToAddForMaterialsPtr, k_BatchSize); - renderersToAddForMaterials.Length = 0; - - int* renderersToAddForMeshesPtr = stackalloc int[k_BatchSize]; - var renderersToAddForMeshes = new UnsafeList(renderersToAddForMeshesPtr, k_BatchSize); - renderersToAddForMeshes.Length = 0; - - for (int index = 0; index < count; index++) - { - var rendererIndex = startIndex + index; - var rendererID = rendererGroupIDs[rendererIndex]; - - // We ignore this renderer if it is in the excluded list. - if (sortedExcludeRendererIDs.BinarySearch(rendererID) >= 0) - continue; - - { - var meshID = meshIDArray[rendererIndex]; - if (meshIDs.Contains(meshID)) - { -#pragma warning disable 618 // todo @emilie.thaulow make renderID an EntityId - renderersToAddForMeshes.AddNoResize(rendererID); -#pragma warning restore 618 - // We can skip the material check if we found a mesh match since at this point - // the renderer is already added and will be processed by the mesh branch - continue; - } - } - { - var rendererMaterials = materialIDArrays[rendererIndex]; - - for (int materialIndex = 0; materialIndex < rendererMaterials.Length; materialIndex++) - { - var materialID = rendererMaterials[materialIndex]; - if (materialIDs.Contains(materialID)) - { - -#pragma warning disable 618 // todo @emilie.thaulow make renderID an EntityId - renderersToAddForMaterials.AddNoResize(rendererID); -#pragma warning restore 618 - break; - } - } - } - } - - selectedRenderGroupsForMaterials.AddRangeNoResize(renderersToAddForMaterialsPtr, renderersToAddForMaterials.Length); - selectedRenderGroupsForMeshes.AddRangeNoResize(renderersToAddForMeshesPtr, renderersToAddForMeshes.Length); - } +#if UNITY_EDITOR + m_FrameUpdateNeeded = false; +#endif } } } diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/GPUResidentDrawerBurst.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/GPUResidentDrawerBurst.cs deleted file mode 100644 index a9a98043664..00000000000 --- a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/GPUResidentDrawerBurst.cs +++ /dev/null @@ -1,80 +0,0 @@ -using Unity.Collections; -using UnityEngine.Rendering; -using Unity.Burst; - -namespace UnityEngine.Rendering -{ - [BurstCompile] - internal static class GPUResidentDrawerBurst - { - [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] - public static void ClassifyMaterials(in NativeArray materialIDs, in NativeParallelHashMap.ReadOnly batchMaterialHash, - ref NativeList supportedMaterialIDs, ref NativeList unsupportedMaterialIDs, ref NativeList supportedPackedMaterialDatas) - { - var usedMaterialIDs = new NativeList(4, Allocator.Temp); - - foreach (var materialID in materialIDs) - { - if (batchMaterialHash.ContainsKey(materialID)) - usedMaterialIDs.Add(materialID); - } - - if (usedMaterialIDs.IsEmpty) - { - usedMaterialIDs.Dispose(); - return; - } - - unsupportedMaterialIDs.Resize(usedMaterialIDs.Length, NativeArrayOptions.UninitializedMemory); - supportedMaterialIDs.Resize(usedMaterialIDs.Length, NativeArrayOptions.UninitializedMemory); - supportedPackedMaterialDatas.Resize(usedMaterialIDs.Length, NativeArrayOptions.UninitializedMemory); - - int unsupportedMaterialCount = GPUDrivenProcessor.ClassifyMaterials(usedMaterialIDs.AsArray(), unsupportedMaterialIDs.AsArray(), supportedMaterialIDs.AsArray(), supportedPackedMaterialDatas.AsArray()); - - unsupportedMaterialIDs.Resize(unsupportedMaterialCount, NativeArrayOptions.ClearMemory); - supportedMaterialIDs.Resize(usedMaterialIDs.Length - unsupportedMaterialCount, NativeArrayOptions.ClearMemory); - supportedPackedMaterialDatas.Resize(supportedMaterialIDs.Length, NativeArrayOptions.ClearMemory); - - usedMaterialIDs.Dispose(); - } - - [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] - public static void FindUnsupportedRenderers(in NativeArray unsupportedMaterials, in NativeArray.ReadOnly materialIDArrays, in NativeArray.ReadOnly rendererGroups, - ref NativeList unsupportedRenderers) - { - for (int arrayIndex = 0; arrayIndex < materialIDArrays.Length; arrayIndex++) - { - var materialIDs = materialIDArrays[arrayIndex]; - EntityId rendererID = rendererGroups[arrayIndex]; - - for (int i = 0; i < materialIDs.Length; i++) - { - EntityId materialID = materialIDs[i]; - - if (unsupportedMaterials.Contains(materialID)) - { - unsupportedRenderers.Add(rendererID); - break; - } - } - } - } - - [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] - public static void GetMaterialsWithChangedPackedMaterial(in NativeArray materialIDs, in NativeArray packedMaterialDatas, - in NativeParallelHashMap.ReadOnly packedMaterialHash, ref NativeHashSet filteredMaterials) - { - for (int index = 0; index < materialIDs.Length ; index++) - { - var materialID = materialIDs[index]; - var newPackedMaterialData = packedMaterialDatas[index]; - - // Has its packed material changed? If the material isn't in the packed material cache, consider the material has changed. - if (packedMaterialHash.TryGetValue(materialID, out var packedMaterial) && packedMaterial.Equals(newPackedMaterialData)) - continue; - - filteredMaterials.Add(materialID); - } - } - } -} diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/GPUResidentDrawerBurst.cs.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/GPUResidentDrawerBurst.cs.meta deleted file mode 100644 index ececc7dd30e..00000000000 --- a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/GPUResidentDrawerBurst.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 2f50a3b1f0997d342837e27ab3b95e6f \ No newline at end of file diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/IGPUResidentRenderPipeline.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/IGPUResidentRenderPipeline.cs index b46f932509d..5832e037186 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/IGPUResidentRenderPipeline.cs +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/IGPUResidentRenderPipeline.cs @@ -141,7 +141,7 @@ static bool IsGPUResidentDrawerSupportedByProjectConfiguration(bool logReason = /// true if enabled static bool IsGPUResidentDrawerEnabled() { - return GPUResidentDrawer.IsEnabled(); + return GPUResidentDrawer.IsInitialized(); } } } diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceCullerBurst.cs.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceCullerBurst.cs.meta deleted file mode 100644 index dc1b8acb849..00000000000 --- a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceCullerBurst.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 99f3de5decfa27b47a4ab725fc059f50 \ No newline at end of file diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceCullingBatcher.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceCullingBatcher.cs deleted file mode 100644 index 4a805041462..00000000000 --- a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceCullingBatcher.cs +++ /dev/null @@ -1,891 +0,0 @@ -using System; -using System.Threading; -using UnityEngine.Assertions; -using Unity.Mathematics; -using Unity.Collections; -using Unity.Jobs; -using Unity.Jobs.LowLevel.Unsafe; -using Unity.Collections.LowLevel.Unsafe; -using Unity.Burst; -using UnityEngine.Profiling; - -namespace UnityEngine.Rendering -{ - internal delegate void OnCullingCompleteCallback(JobHandle jobHandle, in BatchCullingContext cullingContext, in BatchCullingOutput cullingOutput); - - internal struct InstanceCullingBatcherDesc - { - public OnCullingCompleteCallback onCompleteCallback; - -#if UNITY_EDITOR - public Shader brgPicking; - public Shader brgLoading; - public Shader brgError; -#endif - - public static InstanceCullingBatcherDesc NewDefault() - { - return new InstanceCullingBatcherDesc() - { - onCompleteCallback = null -#if UNITY_EDITOR - ,brgPicking = null - ,brgLoading = null - ,brgError = null -#endif - }; - } - } - - internal struct MeshProceduralInfo - { - public MeshTopology topology; - public uint baseVertex; - public uint firstIndex; - public uint indexCount; - } - - [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] - internal struct PrefixSumDrawInstancesJob : IJob - { - [ReadOnly] public NativeParallelHashMap rangeHash; - - public NativeList drawRanges; - public NativeList drawBatches; - public NativeArray drawBatchIndices; - - public void Execute() - { - Assert.AreEqual(rangeHash.Count(), drawRanges.Length); - Assert.AreEqual(drawBatchIndices.Length, drawBatches.Length); - - // Prefix sum to calculate draw offsets for each DrawRange - int drawPrefixSum = 0; - - for (int i = 0; i < drawRanges.Length; ++i) - { - ref DrawRange drawRange = ref drawRanges.ElementAt(i); - drawRange.drawOffset = drawPrefixSum; - drawPrefixSum += drawRange.drawCount; - } - - // Generate DrawBatch index ranges for each DrawRange - var internalRangeIndex = new NativeArray(drawRanges.Length, Allocator.Temp); - - for (int i = 0; i < drawBatches.Length; ++i) - { - ref DrawBatch drawBatch = ref drawBatches.ElementAt(i); - Assert.IsTrue(drawBatch.instanceCount > 0); - - if (rangeHash.TryGetValue(drawBatch.key.range, out int drawRangeIndex)) - { - ref DrawRange drawRange = ref drawRanges.ElementAt(drawRangeIndex); - drawBatchIndices[drawRange.drawOffset + internalRangeIndex[drawRangeIndex]] = i; - internalRangeIndex[drawRangeIndex]++; - } - } - - // Prefix sum to calculate instance offsets for each DrawCommand - int drawInstancesPrefixSum = 0; - - for (int i = 0; i < drawBatchIndices.Length; ++i) - { - // DrawIndices remap to get DrawCommands ordered by DrawRange - var drawBatchIndex = drawBatchIndices[i]; - ref DrawBatch drawBatch = ref drawBatches.ElementAt(drawBatchIndex); - drawBatch.instanceOffset = drawInstancesPrefixSum; - drawInstancesPrefixSum += drawBatch.instanceCount; - } - - internalRangeIndex.Dispose(); - } - } - - [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] - internal unsafe struct BuildDrawListsJob : IJobParallelFor - { - public const int k_BatchSize = 128; - public const int k_IntsPerCacheLine = JobsUtility.CacheLineSize / sizeof(int); - - [ReadOnly] public NativeParallelHashMap batchHash; - [NativeDisableContainerSafetyRestriction, NoAlias] [ReadOnly] public NativeList drawInstances; - [NativeDisableContainerSafetyRestriction, NoAlias] [ReadOnly] public NativeList drawBatches; - - [NativeDisableContainerSafetyRestriction, NoAlias] [WriteOnly] public NativeArray internalDrawIndex; - [NativeDisableContainerSafetyRestriction, NoAlias] [WriteOnly] public NativeArray drawInstanceIndices; - - private unsafe static int IncrementCounter(int* counter) - { - return Interlocked.Increment(ref UnsafeUtility.AsRef(counter)) - 1; - } - - public void Execute(int index) - { - // Generate instance index ranges for each DrawCommand - ref DrawInstance drawInstance = ref drawInstances.ElementAt(index); - int drawBatchIndex = batchHash[drawInstance.key]; - - ref DrawBatch drawBatch = ref drawBatches.ElementAt(drawBatchIndex); - var offset = IncrementCounter((int*)internalDrawIndex.GetUnsafePtr() + drawBatchIndex * k_IntsPerCacheLine); - var writeIndex = drawBatch.instanceOffset + offset; - drawInstanceIndices[writeIndex] = drawInstance.instanceIndex; - } - } - - [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] - internal unsafe struct FindDrawInstancesJob : IJobParallelForBatch - { - public const int k_BatchSize = 128; - - [ReadOnly] public NativeArray instancesSorted; - [NativeDisableContainerSafetyRestriction, NoAlias] [ReadOnly] public NativeList drawInstances; - - [WriteOnly] public NativeList.ParallelWriter outDrawInstanceIndicesWriter; - - public void Execute(int startIndex, int count) - { - int* instancesToRemove = stackalloc int[k_BatchSize]; - int length = 0; - - for (int i = startIndex; i < startIndex + count; ++i) - { - ref DrawInstance drawInstance = ref drawInstances.ElementAt(i); - - if (instancesSorted.BinarySearch(InstanceHandle.FromInt(drawInstance.instanceIndex)) >= 0) - instancesToRemove[length++] = i; - } - - outDrawInstanceIndicesWriter.AddRangeNoResize(instancesToRemove, length); - } - } - - [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] - internal unsafe struct FindMaterialDrawInstancesJob : IJobParallelForBatch - { - public const int k_BatchSize = 128; - - [ReadOnly] public NativeArray materialsSorted; - [NativeDisableContainerSafetyRestriction, NoAlias] [ReadOnly] public NativeList drawInstances; - - [WriteOnly] public NativeList.ParallelWriter outDrawInstanceIndicesWriter; - - public void Execute(int startIndex, int count) - { - int* instancesToRemove = stackalloc int[k_BatchSize]; - int length = 0; - - for (int i = startIndex; i < startIndex + count; ++i) - { - ref DrawInstance drawInstance = ref drawInstances.ElementAt(i); - - if (materialsSorted.BinarySearch(drawInstance.key.materialID.value) >= 0) - instancesToRemove[length++] = i; - } - - outDrawInstanceIndicesWriter.AddRangeNoResize(instancesToRemove, length); - } - } - - [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] - internal struct FindNonRegisteredMeshesJob : IJobParallelForBatch - { - public const int k_BatchSize = 128; - - [ReadOnly] public NativeArray instanceIDs; - [ReadOnly] public NativeParallelHashMap hashMap; - - [WriteOnly] public NativeList.ParallelWriter outInstancesWriter; - - public unsafe void Execute(int startIndex, int count) - { - EntityId* notFoundinstanceIDsPtr = stackalloc EntityId[k_BatchSize]; - var notFoundinstanceIDs = new UnsafeList(notFoundinstanceIDsPtr, k_BatchSize); - - notFoundinstanceIDs.Length = 0; - - for (int i = startIndex; i < startIndex + count; ++i) - { - var instanceID = instanceIDs[i]; - - if (!hashMap.ContainsKey(instanceID)) - notFoundinstanceIDs.AddNoResize(instanceID); - } - - outInstancesWriter.AddRangeNoResize(notFoundinstanceIDsPtr, notFoundinstanceIDs.Length); - } - } - - [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] - internal struct FindNonRegisteredMaterialsJob : IJobParallelForBatch - { - public const int k_BatchSize = 128; - - [ReadOnly] public NativeArray instanceIDs; - [ReadOnly] public NativeArray packedMaterialDatas; - [ReadOnly] public NativeParallelHashMap hashMap; - - [WriteOnly] public NativeList.ParallelWriter outInstancesWriter; - [WriteOnly] public NativeList.ParallelWriter outPackedMaterialDatasWriter; - - public unsafe void Execute(int startIndex, int count) - { - int* notFoundinstanceIDsPtr = stackalloc int[k_BatchSize]; - var notFoundinstanceIDs = new UnsafeList(notFoundinstanceIDsPtr, k_BatchSize); - - GPUDrivenPackedMaterialData* notFoundPackedMaterialDatasPtr = stackalloc GPUDrivenPackedMaterialData[k_BatchSize]; - var notFoundPackedMaterialDatas = new UnsafeList(notFoundPackedMaterialDatasPtr, k_BatchSize); - - notFoundinstanceIDs.Length = 0; - notFoundPackedMaterialDatas.Length = 0; - - for (int i = startIndex; i < startIndex + count; ++i) - { - EntityId entityId = instanceIDs[i]; - - if (!hashMap.ContainsKey(entityId)) - { -#pragma warning disable 618 // todo @emilie.thaulow fix this - notFoundinstanceIDs.AddNoResize(entityId); -#pragma warning restore 618 - notFoundPackedMaterialDatas.AddNoResize(packedMaterialDatas[i]); - } - } - - outInstancesWriter.AddRangeNoResize(notFoundinstanceIDsPtr, notFoundinstanceIDs.Length); - outPackedMaterialDatasWriter.AddRangeNoResize(notFoundPackedMaterialDatasPtr, notFoundPackedMaterialDatas.Length); - } - } - - [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] - internal struct RegisterNewMeshesJob : IJobParallelFor - { - public const int k_BatchSize = 128; - - [ReadOnly] public NativeArray instanceIDs; - [ReadOnly] public NativeArray batchIDs; - - [WriteOnly] public NativeParallelHashMap.ParallelWriter hashMap; - - public void Execute(int index) - { - hashMap.TryAdd(instanceIDs[index], batchIDs[index]); - } - } - - [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] - internal struct RegisterNewMaterialsJob : IJobParallelFor - { - public const int k_BatchSize = 128; - - [ReadOnly] public NativeArray instanceIDs; - [ReadOnly] public NativeArray packedMaterialDatas; - [ReadOnly] public NativeArray batchIDs; - - [WriteOnly] public NativeParallelHashMap.ParallelWriter batchMaterialHashMap; - [WriteOnly] public NativeParallelHashMap.ParallelWriter packedMaterialHashMap; - - public void Execute(int index) - { - var instanceID = instanceIDs[index]; - batchMaterialHashMap.TryAdd(instanceID, batchIDs[index]); - packedMaterialHashMap.TryAdd(instanceID, packedMaterialDatas[index]); - } - } - - [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] - internal struct UpdatePackedMaterialDataCacheJob : IJob - { - [ReadOnly] public NativeArray.ReadOnly materialIDs; - [ReadOnly] public NativeArray.ReadOnly packedMaterialDatas; - - public NativeParallelHashMap packedMaterialHash; - - private void ProcessMaterial(int i) - { - var materialID = materialIDs[i]; - var packedMaterialData = packedMaterialDatas[i]; - - if (materialID == EntityId.None) - return; - - // Cache the packed material so we can detect a change in material that would need to update the renderer data. - packedMaterialHash[materialID] = packedMaterialData; - } - - public void Execute() - { - for (int i = 0; i < materialIDs.Length; ++i) - ProcessMaterial(i); - } - } - - internal class CPUDrawInstanceData - { - public NativeList drawInstances => m_DrawInstances; - public NativeParallelHashMap batchHash => m_BatchHash; - public NativeList drawBatches => m_DrawBatches; - public NativeParallelHashMap rangeHash => m_RangeHash; - public NativeList drawRanges => m_DrawRanges; - public NativeArray drawBatchIndices => m_DrawBatchIndices.AsArray(); - public NativeArray drawInstanceIndices => m_DrawInstanceIndices.AsArray(); - - private NativeParallelHashMap m_RangeHash; // index in m_DrawRanges, hashes by range state - private NativeList m_DrawRanges; - private NativeParallelHashMap m_BatchHash; // index in m_DrawBatches, hashed by draw state - private NativeList m_DrawBatches; - private NativeList m_DrawInstances; - private NativeList m_DrawInstanceIndices; // DOTS instance index, arranged in contiguous blocks in m_DrawBatches order (see DrawBatch.instanceOffset, DrawBatch.instanceCount) - private NativeList m_DrawBatchIndices; // index in m_DrawBatches, arranged in contiguous blocks in m_DrawRanges order (see DrawRange.drawOffset, DrawRange.drawCount) - - private bool m_NeedsRebuild; - - public bool valid => m_DrawInstances.IsCreated; - - public void Initialize() - { - Assert.IsTrue(!valid); - m_RangeHash = new NativeParallelHashMap(1024, Allocator.Persistent); - m_DrawRanges = new NativeList(Allocator.Persistent); - m_BatchHash = new NativeParallelHashMap(1024, Allocator.Persistent); - m_DrawBatches = new NativeList(Allocator.Persistent); - m_DrawInstances = new NativeList(1024, Allocator.Persistent); - m_DrawInstanceIndices = new NativeList(1024, Allocator.Persistent); - m_DrawBatchIndices = new NativeList(1024, Allocator.Persistent); - } - - public void Dispose() - { - if (m_DrawBatchIndices.IsCreated) - m_DrawBatchIndices.Dispose(); - - if (m_DrawInstanceIndices.IsCreated) - m_DrawInstanceIndices.Dispose(); - - if (m_DrawInstances.IsCreated) - m_DrawInstances.Dispose(); - - if (m_DrawBatches.IsCreated) - m_DrawBatches.Dispose(); - - if (m_BatchHash.IsCreated) - m_BatchHash.Dispose(); - - if (m_DrawRanges.IsCreated) - m_DrawRanges.Dispose(); - - if (m_RangeHash.IsCreated) - m_RangeHash.Dispose(); - } - - public void RebuildDrawListsIfNeeded() - { - if (!m_NeedsRebuild) - return; - - m_NeedsRebuild = false; - - Assert.IsTrue(m_RangeHash.Count() == m_DrawRanges.Length); - Assert.IsTrue(m_BatchHash.Count() == m_DrawBatches.Length); - - m_DrawInstanceIndices.ResizeUninitialized(m_DrawInstances.Length); - m_DrawBatchIndices.ResizeUninitialized(m_DrawBatches.Length); - - var internalDrawIndex = new NativeArray(drawBatches.Length * BuildDrawListsJob.k_IntsPerCacheLine, Allocator.TempJob, NativeArrayOptions.ClearMemory); - - var prefixSumDrawInstancesJob = new PrefixSumDrawInstancesJob() - { - rangeHash = m_RangeHash, - drawRanges = m_DrawRanges, - drawBatches = m_DrawBatches, - drawBatchIndices = m_DrawBatchIndices.AsArray() - }; - - var prefixSumJobHandle = prefixSumDrawInstancesJob.Schedule(); - - var buildDrawListsJob = new BuildDrawListsJob() - { - drawInstances = m_DrawInstances, - batchHash = m_BatchHash, - drawBatches = m_DrawBatches, - internalDrawIndex = internalDrawIndex, - drawInstanceIndices = m_DrawInstanceIndices.AsArray(), - }; - - buildDrawListsJob.Schedule(m_DrawInstances.Length, BuildDrawListsJob.k_BatchSize, prefixSumJobHandle).Complete(); - - internalDrawIndex.Dispose(); - } - - public void DestroyDrawInstanceIndices(NativeArray drawInstanceIndicesToDestroy) - { - Profiler.BeginSample("DestroyDrawInstanceIndices.ParallelSort"); - drawInstanceIndicesToDestroy.ParallelSort().Complete(); - Profiler.EndSample(); - - Profiler.BeginSample("DestroyDrawInstanceIndices.RemoveDrawInstanceIndices"); - InstanceCullingBatcherBurst.RemoveDrawInstanceIndices(drawInstanceIndicesToDestroy, ref m_DrawInstances, ref m_RangeHash, - ref m_BatchHash, ref m_DrawRanges, ref m_DrawBatches); - Profiler.EndSample(); - } - - public unsafe void DestroyDrawInstances(NativeArray destroyedInstances) - { - if (m_DrawInstances.IsEmpty || destroyedInstances.Length == 0) - return; - - NeedsRebuild(); - - var destroyedInstancesSorted = new NativeArray(destroyedInstances, Allocator.TempJob); - Assert.AreEqual(UnsafeUtility.SizeOf(), UnsafeUtility.SizeOf()); - - Profiler.BeginSample("DestroyDrawInstances.ParallelSort"); - destroyedInstancesSorted.Reinterpret().ParallelSort().Complete(); - Profiler.EndSample(); - - var drawInstanceIndicesToDestroy = new NativeList(m_DrawInstances.Length, Allocator.TempJob); - - var findDrawInstancesJobHandle = new FindDrawInstancesJob() - { - instancesSorted = destroyedInstancesSorted, - drawInstances = m_DrawInstances, - outDrawInstanceIndicesWriter = drawInstanceIndicesToDestroy.AsParallelWriter() - }; - - findDrawInstancesJobHandle.ScheduleBatch(m_DrawInstances.Length, FindDrawInstancesJob.k_BatchSize).Complete(); - - DestroyDrawInstanceIndices(drawInstanceIndicesToDestroy.AsArray()); - - destroyedInstancesSorted.Dispose(); - drawInstanceIndicesToDestroy.Dispose(); - } - - public unsafe void DestroyMaterialDrawInstances(NativeArray destroyedBatchMaterials) - { - if (m_DrawInstances.IsEmpty || destroyedBatchMaterials.Length == 0) - return; - - NeedsRebuild(); - - var destroyedBatchMaterialsSorted = new NativeArray(destroyedBatchMaterials, Allocator.TempJob); - - Profiler.BeginSample("DestroyedBatchMaterials.ParallelSort"); - destroyedBatchMaterialsSorted.Reinterpret().ParallelSort().Complete(); - Profiler.EndSample(); - - var drawInstanceIndicesToDestroy = new NativeList(m_DrawInstances.Length, Allocator.TempJob); - - var findDrawInstancesJobHandle = new FindMaterialDrawInstancesJob() - { - materialsSorted = destroyedBatchMaterialsSorted, - drawInstances = m_DrawInstances, - outDrawInstanceIndicesWriter = drawInstanceIndicesToDestroy.AsParallelWriter() - }; - - findDrawInstancesJobHandle.ScheduleBatch(m_DrawInstances.Length, FindMaterialDrawInstancesJob.k_BatchSize).Complete(); - - DestroyDrawInstanceIndices(drawInstanceIndicesToDestroy.AsArray()); - - destroyedBatchMaterialsSorted.Dispose(); - drawInstanceIndicesToDestroy.Dispose(); - } - - public void NeedsRebuild() - { - m_NeedsRebuild = true; - } - } - - internal class InstanceCullingBatcher : IDisposable - { - private RenderersBatchersContext m_BatchersContext; - private CPUDrawInstanceData m_DrawInstanceData; - private BatchRendererGroup m_BRG; - private NativeParallelHashMap m_GlobalBatchIDs; - private InstanceCuller m_Culler; - private NativeParallelHashMap m_BatchMaterialHash; - private NativeParallelHashMap m_PackedMaterialHash; - private NativeParallelHashMap m_BatchMeshHash; - - private int m_CachedInstanceDataBufferLayoutVersion; - - private OnCullingCompleteCallback m_OnCompleteCallback; - - public NativeParallelHashMap batchMaterialHash => m_BatchMaterialHash; - public NativeParallelHashMap packedMaterialHash => m_PackedMaterialHash; - - public InstanceCullingBatcher(RenderersBatchersContext batcherContext, InstanceCullingBatcherDesc desc, BatchRendererGroup.OnFinishedCulling onFinishedCulling) - { - m_BatchersContext = batcherContext; - m_DrawInstanceData = new CPUDrawInstanceData(); - m_DrawInstanceData.Initialize(); - - m_BRG = new BatchRendererGroup(new BatchRendererGroupCreateInfo() - { - cullingCallback = OnPerformCulling, - finishedCullingCallback = onFinishedCulling, - userContext = IntPtr.Zero - }); - -#if UNITY_EDITOR - if (desc.brgPicking != null) - { - var mat = new Material(desc.brgPicking); - mat.hideFlags = HideFlags.HideAndDontSave; - m_BRG.SetPickingMaterial(mat); - } - if (desc.brgLoading != null) - { - var mat = new Material(desc.brgLoading); - mat.hideFlags = HideFlags.HideAndDontSave; - m_BRG.SetLoadingMaterial(mat); - } - if (desc.brgError != null) - { - var mat = new Material(desc.brgError); - mat.hideFlags = HideFlags.HideAndDontSave; - m_BRG.SetErrorMaterial(mat); - } - var viewTypes = new BatchCullingViewType[] { - BatchCullingViewType.Light, - BatchCullingViewType.Camera, - BatchCullingViewType.Picking, - BatchCullingViewType.SelectionOutline, - BatchCullingViewType.Filtering - }; - m_BRG.SetEnabledViewTypes(viewTypes); -#endif - - m_Culler = new InstanceCuller(); - m_Culler.Init(batcherContext.resources, batcherContext.debugStats); - - m_CachedInstanceDataBufferLayoutVersion = -1; - m_OnCompleteCallback = desc.onCompleteCallback; - m_BatchMaterialHash = new NativeParallelHashMap(64, Allocator.Persistent); - m_PackedMaterialHash = new NativeParallelHashMap(64, Allocator.Persistent); - m_BatchMeshHash = new NativeParallelHashMap(64, Allocator.Persistent); - - m_GlobalBatchIDs = new NativeParallelHashMap(6, Allocator.Persistent); - m_GlobalBatchIDs.Add((uint)InstanceComponentGroup.Default, GetBatchID(InstanceComponentGroup.Default)); - m_GlobalBatchIDs.Add((uint)InstanceComponentGroup.DefaultWind, GetBatchID(InstanceComponentGroup.DefaultWind)); - m_GlobalBatchIDs.Add((uint)InstanceComponentGroup.DefaultLightProbe, GetBatchID(InstanceComponentGroup.DefaultLightProbe)); - m_GlobalBatchIDs.Add((uint)InstanceComponentGroup.DefaultLightmap, GetBatchID(InstanceComponentGroup.DefaultLightmap)); - m_GlobalBatchIDs.Add((uint)InstanceComponentGroup.DefaultWindLightProbe, GetBatchID(InstanceComponentGroup.DefaultWindLightProbe)); - m_GlobalBatchIDs.Add((uint)InstanceComponentGroup.DefaultWindLightmap, GetBatchID(InstanceComponentGroup.DefaultWindLightmap)); - } - - internal ref InstanceCuller culler => ref m_Culler; - - public void Dispose() - { - m_OnCompleteCallback = null; - m_Culler.Dispose(); - - foreach (var batchID in m_GlobalBatchIDs) - { - if (!batchID.Value.Equals(BatchID.Null)) - m_BRG.RemoveBatch(batchID.Value); - } - m_GlobalBatchIDs.Dispose(); - - if (m_BRG != null) - m_BRG.Dispose(); - - m_DrawInstanceData.Dispose(); - m_DrawInstanceData = null; - - m_BatchMaterialHash.Dispose(); - m_PackedMaterialHash.Dispose(); - m_BatchMeshHash.Dispose(); - } - - private BatchID GetBatchID(InstanceComponentGroup componentsOverriden) - { - if (m_CachedInstanceDataBufferLayoutVersion != m_BatchersContext.instanceDataBufferLayoutVersion) - return BatchID.Null; - - Assert.IsTrue(m_BatchersContext.defaultDescriptions.Length == m_BatchersContext.defaultMetadata.Length); - - const uint kClearIsOverriddenBit = 0x4FFFFFFF; - var tempMetadata = new NativeList(m_BatchersContext.defaultMetadata.Length, Allocator.Temp); - - for(int i = 0; i < m_BatchersContext.defaultDescriptions.Length; ++i) - { - var componentGroup = m_BatchersContext.defaultDescriptions[i].componentGroup; - var metadata = m_BatchersContext.defaultMetadata[i]; - var value = metadata.Value; - - // if instances in this batch do not override the component, clear the override bit - if ((componentsOverriden & componentGroup) == 0) - value &= kClearIsOverriddenBit; - - tempMetadata.Add(new MetadataValue - { - NameID = metadata.NameID, - Value = value - }); - } - - return m_BRG.AddBatch(tempMetadata.AsArray(), m_BatchersContext.gpuInstanceDataBuffer.bufferHandle); - } - - private void UpdateInstanceDataBufferLayoutVersion() - { - if (m_CachedInstanceDataBufferLayoutVersion != m_BatchersContext.instanceDataBufferLayoutVersion) - { - m_CachedInstanceDataBufferLayoutVersion = m_BatchersContext.instanceDataBufferLayoutVersion; - - foreach (var componentsToBatchID in m_GlobalBatchIDs) - { - var batchID = componentsToBatchID.Value; - if (!batchID.Equals(BatchID.Null)) - m_BRG.RemoveBatch(batchID); - - var componentsOverriden = (InstanceComponentGroup)componentsToBatchID.Key; - componentsToBatchID.Value = GetBatchID(componentsOverriden); - } - } - } - - public CPUDrawInstanceData GetDrawInstanceData() - { - return m_DrawInstanceData; - } - - public unsafe JobHandle OnPerformCulling( - BatchRendererGroup rendererGroup, - BatchCullingContext cc, - BatchCullingOutput cullingOutput, - IntPtr userContext) - { - foreach (var batchID in m_GlobalBatchIDs) - { - if (batchID.Value.Equals(BatchID.Null)) - return new JobHandle(); - } - - m_DrawInstanceData.RebuildDrawListsIfNeeded(); - - bool allowOcclusionCulling = m_BatchersContext.hasBoundingSpheres; - JobHandle jobHandle = m_Culler.CreateCullJobTree( - cc, - cullingOutput, - m_BatchersContext.instanceData, - m_BatchersContext.sharedInstanceData, - m_BatchersContext.perCameraInstanceData, - m_BatchersContext.instanceDataBuffer, - m_BatchersContext.lodGroupCullingData, - m_DrawInstanceData, - m_GlobalBatchIDs, - m_BatchersContext.smallMeshScreenPercentage, - allowOcclusionCulling ? m_BatchersContext.occlusionCullingCommon : null); - - if (m_OnCompleteCallback != null) - m_OnCompleteCallback(jobHandle, cc, cullingOutput); - - return jobHandle; - } - - public void OnFinishedCulling(IntPtr customCullingResult) - { - - int viewInstanceID = (int)customCullingResult; -#pragma warning disable 618 // todo @emilie.thaulow make this into an EntityId - m_Culler.EnsureValidOcclusionTestResults(viewInstanceID); -#pragma warning restore 618 - } - - public void DestroyDrawInstances(NativeArray instances) - { - if (instances.Length == 0) - return; - - Profiler.BeginSample("DestroyDrawInstances"); - - m_DrawInstanceData.DestroyDrawInstances(instances); - - Profiler.EndSample(); - } - - public void DestroyMaterials(NativeArray destroyedMaterials) - { - if (destroyedMaterials.Length == 0) - return; - - Profiler.BeginSample("DestroyMaterials"); - - var destroyedBatchMaterials = new NativeList(destroyedMaterials.Length, Allocator.TempJob); - - foreach (EntityId destroyedMaterial in destroyedMaterials) - { - if (m_BatchMaterialHash.TryGetValue(destroyedMaterial, out var destroyedBatchMaterial)) - { - destroyedBatchMaterials.Add(destroyedBatchMaterial.value); - m_BatchMaterialHash.Remove(destroyedMaterial); - m_PackedMaterialHash.Remove(destroyedMaterial); - m_BRG.UnregisterMaterial(destroyedBatchMaterial); - } - } - - m_DrawInstanceData.DestroyMaterialDrawInstances(destroyedBatchMaterials.AsArray()); - - destroyedBatchMaterials.Dispose(); - - Profiler.EndSample(); - } - - public void DestroyMeshes(NativeArray destroyedMeshes) - { - if (destroyedMeshes.Length == 0) - return; - - Profiler.BeginSample("DestroyMeshes"); - - foreach (EntityId destroyedMesh in destroyedMeshes) - { - if (m_BatchMeshHash.TryGetValue(destroyedMesh, out var destroyedBatchMesh)) - { - m_BatchMeshHash.Remove(destroyedMesh); - m_BRG.UnregisterMesh(destroyedBatchMesh); - } - } - - Profiler.EndSample(); - } - - public void PostCullBeginCameraRendering(RenderRequestBatcherContext context) - { - } - - private void RegisterBatchMeshes(NativeArray meshIDs) - { - var newMeshIDs = new NativeList(meshIDs.Length, Allocator.TempJob); - new FindNonRegisteredMeshesJob - { - instanceIDs = meshIDs, - hashMap = m_BatchMeshHash, - outInstancesWriter = newMeshIDs.AsParallelWriter() - } - .ScheduleBatch(meshIDs.Length, FindNonRegisteredMeshesJob.k_BatchSize).Complete(); - var newBatchMeshIDs = new NativeArray(newMeshIDs.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); - m_BRG.RegisterMeshes(newMeshIDs.AsArray(), newBatchMeshIDs); - - int totalMeshesNum = m_BatchMeshHash.Count() + newBatchMeshIDs.Length; - m_BatchMeshHash.Capacity = Math.Max(m_BatchMeshHash.Capacity, Mathf.CeilToInt(totalMeshesNum / 1023.0f) * 1024); - - new RegisterNewMeshesJob - { - instanceIDs = newMeshIDs.AsArray(), - batchIDs = newBatchMeshIDs, - hashMap = m_BatchMeshHash.AsParallelWriter() - } - .Schedule(newMeshIDs.Length, RegisterNewMeshesJob.k_BatchSize).Complete(); - - newMeshIDs.Dispose(); - newBatchMeshIDs.Dispose(); - } - - private void RegisterBatchMaterials(in NativeArray usedMaterialIDs, in NativeArray usedPackedMaterialDatas) - { - Debug.Assert(usedMaterialIDs.Length == usedPackedMaterialDatas.Length, "Each material ID should correspond to one packed material data."); - var newMaterialIDs = new NativeList(usedMaterialIDs.Length, Allocator.TempJob); - var newPackedMaterialDatas = new NativeList(usedMaterialIDs.Length, Allocator.TempJob); - new FindNonRegisteredMaterialsJob - { - instanceIDs = usedMaterialIDs, - packedMaterialDatas = usedPackedMaterialDatas, - hashMap = m_BatchMaterialHash, - outInstancesWriter = newMaterialIDs.AsParallelWriter(), - outPackedMaterialDatasWriter = newPackedMaterialDatas.AsParallelWriter() - } - .ScheduleBatch(usedMaterialIDs.Length, FindNonRegisteredMaterialsJob.k_BatchSize).Complete(); - - var newBatchMaterialIDs = new NativeArray(newMaterialIDs.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); - m_BRG.RegisterMaterials(newMaterialIDs.AsArray(), newBatchMaterialIDs); - - int totalMaterialsNum = m_BatchMaterialHash.Count() + newMaterialIDs.Length; - m_BatchMaterialHash.Capacity = Math.Max(m_BatchMaterialHash.Capacity, Mathf.CeilToInt(totalMaterialsNum / 1023.0f) * 1024); - m_PackedMaterialHash.Capacity = m_BatchMaterialHash.Capacity; - - new RegisterNewMaterialsJob - { - instanceIDs = newMaterialIDs.AsArray(), - packedMaterialDatas = newPackedMaterialDatas.AsArray(), - batchIDs = newBatchMaterialIDs, - batchMaterialHashMap = m_BatchMaterialHash.AsParallelWriter(), - packedMaterialHashMap = m_PackedMaterialHash.AsParallelWriter() - } - .Schedule(newMaterialIDs.Length, RegisterNewMaterialsJob.k_BatchSize).Complete(); - - newMaterialIDs.Dispose(); - newPackedMaterialDatas.Dispose(); - newBatchMaterialIDs.Dispose(); - } - - public JobHandle SchedulePackedMaterialCacheUpdate(NativeArray materialIDs, NativeArray packedMaterialDatas) - { - return new UpdatePackedMaterialDataCacheJob - { - materialIDs = materialIDs.AsReadOnly(), - packedMaterialDatas = packedMaterialDatas.AsReadOnly(), - packedMaterialHash = m_PackedMaterialHash - }.Schedule(); - } - - public void BuildBatch( - NativeArray instances, - in GPUDrivenRendererGroupData rendererData, - bool registerMaterialsAndMeshes) - { - if (registerMaterialsAndMeshes) - { - RegisterBatchMaterials(rendererData.materialID, rendererData.packedMaterialData); - RegisterBatchMeshes(rendererData.meshID); - } - - var rangeHash = m_DrawInstanceData.rangeHash; - var drawRanges = m_DrawInstanceData.drawRanges; - var batchHash = m_DrawInstanceData.batchHash; - var drawBatches = m_DrawInstanceData.drawBatches; - var drawInstances = m_DrawInstanceData.drawInstances; - - InstanceCullingBatcherBurst.CreateDrawBatches(rendererData.instancesCount.Length == 0, instances, rendererData, - m_BatchMeshHash, m_BatchMaterialHash, m_PackedMaterialHash, ref rangeHash, ref drawRanges, ref batchHash, ref drawBatches, ref drawInstances); - - m_DrawInstanceData.NeedsRebuild(); - UpdateInstanceDataBufferLayoutVersion(); - } - - public void InstanceOccludersUpdated(EntityId viewInstanceID, int subviewMask) - { - m_Culler.InstanceOccludersUpdated(viewInstanceID, subviewMask, m_BatchersContext); - } - - public void UpdateFrame() - { - m_Culler.UpdateFrame(m_BatchersContext.cameraCount); - } - - public ParallelBitArray GetCompactedVisibilityMasks(bool syncCullingJobs) - { - return m_Culler.GetCompactedVisibilityMasks(syncCullingJobs); - } - - public void OnEndContextRendering() - { - ParallelBitArray compactedVisibilityMasks = GetCompactedVisibilityMasks(syncCullingJobs: true); - - if(compactedVisibilityMasks.IsCreated) - m_BatchersContext.UpdatePerFrameInstanceVisibility(compactedVisibilityMasks); - } - - public void OnBeginCameraRendering(Camera camera) - { - m_Culler.OnBeginCameraRendering(camera); - } - - public void OnEndCameraRendering(Camera camera) - { - m_Culler.OnEndCameraRendering(camera); - } - } -} diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceCullingBatcherBurst.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceCullingBatcherBurst.cs deleted file mode 100644 index ae60d8d5546..00000000000 --- a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceCullingBatcherBurst.cs +++ /dev/null @@ -1,309 +0,0 @@ -using System; -using Unity.Collections; -using Unity.Burst; -using Unity.Collections.LowLevel.Unsafe; -using Unity.Jobs; -using Unity.Mathematics; -using UnityEngine.Assertions; - -namespace UnityEngine.Rendering -{ - [BurstCompile] - internal static class InstanceCullingBatcherBurst - { - private static void RemoveDrawRange(in RangeKey key, ref NativeParallelHashMap rangeHash, ref NativeList drawRanges) - { - int drawRangeIndex = rangeHash[key]; - - ref DrawRange lastDrawRange = ref drawRanges.ElementAt(drawRanges.Length - 1); - rangeHash[lastDrawRange.key] = drawRangeIndex; - - rangeHash.Remove(key); - drawRanges.RemoveAtSwapBack(drawRangeIndex); - } - - private static void RemoveDrawBatch(in DrawKey key, ref NativeList drawRanges, ref NativeParallelHashMap rangeHash, - ref NativeParallelHashMap batchHash, ref NativeList drawBatches) - { - int drawBatchIndex = batchHash[key]; - - int drawRangeIndex = rangeHash[key.range]; - ref DrawRange drawRange = ref drawRanges.ElementAt(drawRangeIndex); - - Assert.IsTrue(drawRange.drawCount > 0); - - if (--drawRange.drawCount == 0) - RemoveDrawRange(drawRange.key, ref rangeHash, ref drawRanges); - - ref DrawBatch lastDrawBatch = ref drawBatches.ElementAt(drawBatches.Length - 1); - batchHash[lastDrawBatch.key] = drawBatchIndex; - - batchHash.Remove(key); - drawBatches.RemoveAtSwapBack(drawBatchIndex); - } - - [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] - public static unsafe void RemoveDrawInstanceIndices(in NativeArray drawInstanceIndices, ref NativeList drawInstances, ref NativeParallelHashMap rangeHash, - ref NativeParallelHashMap batchHash, ref NativeList drawRanges, ref NativeList drawBatches) - { - var drawInstancesPtr = (DrawInstance*)drawInstances.GetUnsafePtr(); - var drawInstancesNewBack = drawInstances.Length - 1; - - for (int indexRev = drawInstanceIndices.Length - 1; indexRev >= 0; --indexRev) - { - int indexToRemove = drawInstanceIndices[indexRev]; - DrawInstance* drawInstance = drawInstancesPtr + indexToRemove; - - int drawBatchIndex = batchHash[drawInstance->key]; - ref DrawBatch drawBatch = ref drawBatches.ElementAt(drawBatchIndex); - - Assert.IsTrue(drawBatch.instanceCount > 0); - - if (--drawBatch.instanceCount == 0) - RemoveDrawBatch(drawBatch.key, ref drawRanges, ref rangeHash, ref batchHash, ref drawBatches); - - UnsafeUtility.MemCpy(drawInstance, drawInstancesPtr + drawInstancesNewBack--, sizeof(DrawInstance)); - } - - drawInstances.ResizeUninitialized(drawInstancesNewBack + 1); - } - - private static ref DrawRange EditDrawRange(in RangeKey key, NativeParallelHashMap rangeHash, NativeList drawRanges) - { - int drawRangeIndex; - - if (!rangeHash.TryGetValue(key, out drawRangeIndex)) - { - var drawRange = new DrawRange { key = key, drawCount = 0, drawOffset = 0 }; - drawRangeIndex = drawRanges.Length; - rangeHash.Add(key, drawRangeIndex); - drawRanges.Add(drawRange); - } - - ref DrawRange data = ref drawRanges.ElementAt(drawRangeIndex); - Assert.IsTrue(data.key.Equals(key)); - - return ref data; - } - - private static ref DrawBatch EditDrawBatch(in DrawKey key, in SubMeshDescriptor subMeshDescriptor, NativeParallelHashMap batchHash, NativeList drawBatches) - { - var procInfo = new MeshProceduralInfo(); - procInfo.topology = subMeshDescriptor.topology; - procInfo.baseVertex = (uint)subMeshDescriptor.baseVertex; - procInfo.firstIndex = (uint)subMeshDescriptor.indexStart; - procInfo.indexCount = (uint)subMeshDescriptor.indexCount; - - int drawBatchIndex; - - if (!batchHash.TryGetValue(key, out drawBatchIndex)) - { - var drawBatch = new DrawBatch() { key = key, instanceCount = 0, instanceOffset = 0, procInfo = procInfo }; - drawBatchIndex = drawBatches.Length; - batchHash.Add(key, drawBatchIndex); - drawBatches.Add(drawBatch); - } - - ref DrawBatch data = ref drawBatches.ElementAt(drawBatchIndex); - Assert.IsTrue(data.key.Equals(key)); - - return ref data; - } - - private static void ProcessRenderer(int i, bool implicitInstanceIndices, in GPUDrivenRendererGroupData rendererData, - NativeParallelHashMap batchMeshHash, NativeParallelHashMap packedMaterialDataHash, - NativeParallelHashMap batchMaterialHash, NativeArray instances, NativeList drawInstances, - NativeParallelHashMap rangeHash, NativeList drawRanges, NativeParallelHashMap batchHash, - NativeList drawBatches) - { - var meshIndex = rendererData.meshIndex[i]; - var meshID = rendererData.meshID[meshIndex]; - var meshLodInfo = rendererData.meshLodInfo[meshIndex]; - var submeshCount = rendererData.subMeshCount[meshIndex]; - var subMeshDescOffset = rendererData.subMeshDescOffset[meshIndex]; - var batchMeshID = batchMeshHash[meshID]; - var rendererGroupID = rendererData.rendererGroupID[i]; - var startSubMesh = rendererData.subMeshStartIndex[i]; - var gameObjectLayer = rendererData.gameObjectLayer[i]; - var renderingLayerMask = rendererData.renderingLayerMask[i]; - var materialsOffset = rendererData.materialsOffset[i]; - var materialsCount = rendererData.materialsCount[i]; - var lightmapIndex = rendererData.lightmapIndex[i]; - var packedRendererData = rendererData.packedRendererData[i]; - var rendererPriority = rendererData.rendererPriority[i]; - - int instanceCount; - int instanceOffset; - - if (implicitInstanceIndices) - { - instanceCount = 1; - instanceOffset = i; - } - else - { - instanceCount = rendererData.instancesCount[i]; - instanceOffset = rendererData.instancesOffset[i]; - } - - if (instanceCount == 0) - return; - - const int kLightmapIndexMask = 0xffff; - const int kLightmapIndexInfluenceOnly = 0xfffe; - - var overridenComponents = InstanceComponentGroup.Default; - - // Add per-instance wind parameters - if(packedRendererData.hasTree) - overridenComponents |= InstanceComponentGroup.Wind; - - var lmIndexMasked = lightmapIndex & kLightmapIndexMask; - - // Object doesn't have a valid lightmap Index, -> uses probes for lighting - if (lmIndexMasked >= kLightmapIndexInfluenceOnly) - { - // Only add the component when needed to store blended results (shader will use the ambient probe when not present) - if (packedRendererData.lightProbeUsage == LightProbeUsage.BlendProbes) - overridenComponents |= InstanceComponentGroup.LightProbe; - } - else - { - // Add per-instance lightmap parameters - overridenComponents |= InstanceComponentGroup.Lightmap; - } - - // Scan all materials once to retrieve whether this renderer is indirect-compatible or not (and store it in the RangeKey). - Span packedMaterialDatas = stackalloc GPUDrivenPackedMaterialData[materialsCount]; - - var supportsIndirect = true; - for (int matIndex = 0; matIndex < materialsCount; ++matIndex) - { - if (matIndex >= submeshCount) - { - Debug.LogWarning("Material count in the shared material list is higher than sub mesh count for the mesh. Object may be corrupted."); - continue; - } - - var materialIndex = rendererData.materialIndex[materialsOffset + matIndex]; - GPUDrivenPackedMaterialData packedMaterialData; - - if (rendererData.packedMaterialData.Length > 0) - { - packedMaterialData = rendererData.packedMaterialData[materialIndex]; - } - else - { - var materialID = rendererData.materialID[materialIndex]; - bool isFound = packedMaterialDataHash.TryGetValue(materialID, out packedMaterialData); - Assert.IsTrue(isFound); - } - supportsIndirect &= packedMaterialData.isIndirectSupported; - - packedMaterialDatas[matIndex] = packedMaterialData; - } - - var rangeKey = new RangeKey - { - layer = (byte)gameObjectLayer, - renderingLayerMask = renderingLayerMask, - motionMode = packedRendererData.motionVecGenMode, - shadowCastingMode = packedRendererData.shadowCastingMode, - staticShadowCaster = packedRendererData.staticShadowCaster, - rendererPriority = rendererPriority, - supportsIndirect = supportsIndirect - }; - - ref DrawRange drawRange = ref EditDrawRange(rangeKey, rangeHash, drawRanges); - - for (int matIndex = 0; matIndex < materialsCount; ++matIndex) - { - if (matIndex >= submeshCount) - { - Debug.LogWarning("Material count in the shared material list is higher than sub mesh count for the mesh. Object may be corrupted."); - continue; - } - - var materialIndex = rendererData.materialIndex[materialsOffset + matIndex]; - var materialID = rendererData.materialID[materialIndex]; - var packedMaterialData = packedMaterialDatas[matIndex]; - - if (materialID == EntityId.None) - { - Debug.LogWarning("Material in the shared materials list is null. Object will be partially rendered."); - continue; - } - - batchMaterialHash.TryGetValue(materialID, out BatchMaterialID batchMaterialID); - - // We always provide crossfade value packed in instance index. We don't use None even if there is no LOD to not split the batch. - var flags = BatchDrawCommandFlags.LODCrossFadeValuePacked; - - // Let the engine know if we've opted out of lightmap texture arrays - flags |= BatchDrawCommandFlags.UseLegacyLightmapsKeyword; - - // assume that a custom motion vectors pass contains deformation motion, so should always output motion vectors - // (otherwise this flag is set dynamically during culling only when the transform is changing) - if (packedMaterialData.isMotionVectorsPassEnabled) - flags |= BatchDrawCommandFlags.HasMotion; - - if (packedMaterialData.isTransparent) - flags |= BatchDrawCommandFlags.HasSortingPosition; - - if (packedMaterialData.supportsCrossFade) - flags |= BatchDrawCommandFlags.LODCrossFadeKeyword; - - int lodLoopCount = math.max(meshLodInfo.levelCount, 1); - - for (int lodLoopIndex = 0; lodLoopIndex < lodLoopCount; ++lodLoopIndex) - { - var submeshIndex = startSubMesh + matIndex; - var subMeshDesc = rendererData.subMeshDesc[subMeshDescOffset + submeshIndex*lodLoopCount + lodLoopIndex]; - var drawKey = new DrawKey - { - materialID = batchMaterialID, - meshID = batchMeshID, - submeshIndex = submeshIndex, - activeMeshLod = meshLodInfo.lodSelectionActive ? lodLoopIndex : -1, - flags = flags, - transparentInstanceId = packedMaterialData.isTransparent ? rendererGroupID : EntityId.None, - range = rangeKey, - overridenComponents = (uint)overridenComponents, - // When we've opted out of lightmap texture arrays, we - // need to pass in a valid lightmap index. The engine - // uses this index for sorting and for breaking the - // batch when lightmaps change across draw calls, and - // for binding the correct light map. - lightmapIndex = lightmapIndex - }; - - ref DrawBatch drawBatch = ref EditDrawBatch(drawKey, subMeshDesc, batchHash, drawBatches); - - if (drawBatch.instanceCount == 0) - ++drawRange.drawCount; - - drawBatch.instanceCount += instanceCount; - - for (int j = 0; j < instanceCount; ++j) - { - var instanceIndex = instanceOffset + j; - InstanceHandle instance = instances[instanceIndex]; - drawInstances.Add(new DrawInstance { key = drawKey, instanceIndex = instance.index }); - } - } - } - } - - [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] - public static void CreateDrawBatches(bool implicitInstanceIndices, in NativeArray instances, in GPUDrivenRendererGroupData rendererData, - in NativeParallelHashMap batchMeshHash, in NativeParallelHashMap batchMaterialHash, - in NativeParallelHashMap packedMaterialDataHash, - ref NativeParallelHashMap rangeHash, ref NativeList drawRanges, ref NativeParallelHashMap batchHash, ref NativeList drawBatches, - ref NativeList drawInstances) - { - for (int i = 0; i < rendererData.rendererGroupID.Length; ++i) - ProcessRenderer(i, implicitInstanceIndices, rendererData, batchMeshHash, packedMaterialDataHash, batchMaterialHash, instances, - drawInstances, rangeHash, drawRanges, batchHash, drawBatches); - } - } -} diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceCullingBatcherBurst.cs.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceCullingBatcherBurst.cs.meta deleted file mode 100644 index 1b344d835a7..00000000000 --- a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceCullingBatcherBurst.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 07b72b6a7afa9b448b3103bb66d57ca0 \ No newline at end of file diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/GPUInstanceDataBuffer.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/GPUInstanceDataBuffer.cs deleted file mode 100644 index abfa46e708f..00000000000 --- a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/GPUInstanceDataBuffer.cs +++ /dev/null @@ -1,223 +0,0 @@ -using System; -using System.Collections.Generic; -using Unity.Burst; -using Unity.Collections; -using Unity.Jobs; -using UnityEngine.Assertions; -using static UnityEngine.Rendering.RenderersParameters; - -namespace UnityEngine.Rendering -{ - internal struct GPUInstanceComponentDesc - { - public int propertyID; - public int byteSize; - public bool isOverriden; - public bool isPerInstance; - public InstanceType instanceType; - public InstanceComponentGroup componentGroup; - - public GPUInstanceComponentDesc(int inPropertyID, int inByteSize, bool inIsOverriden, bool inPerInstance, InstanceType inInstanceType, InstanceComponentGroup inComponentType) - { - propertyID = inPropertyID; - byteSize = inByteSize; - isOverriden = inIsOverriden; - isPerInstance = inPerInstance; - instanceType = inInstanceType; - componentGroup = inComponentType; - } - } - - internal class GPUInstanceDataBuffer : IDisposable - { - private static int s_NextLayoutVersion = 0; - public static int NextVersion() { return ++s_NextLayoutVersion; } - - public InstanceNumInfo instanceNumInfo; - public NativeArray instancesNumPrefixSum; - public NativeArray instancesSpan; - public int byteSize; - public int perInstanceComponentCount; - public int version; - public int layoutVersion; - public GraphicsBuffer gpuBuffer; - public GraphicsBuffer validComponentsIndicesGpuBuffer; - public GraphicsBuffer componentAddressesGpuBuffer; - public GraphicsBuffer componentInstanceIndexRangesGpuBuffer; - public GraphicsBuffer componentByteCountsGpuBuffer; - public NativeArray descriptions; - public NativeArray defaultMetadata; - public NativeArray gpuBufferComponentAddress; - public NativeParallelHashMap nameToMetadataMap; - - public bool valid => instancesSpan.IsCreated; - - private static GPUInstanceIndex CPUInstanceToGPUInstance(in NativeArray instancesNumPrefixSum, InstanceHandle instance) - { - bool valid = instance.valid && instance.type < InstanceType.Count; -#if DEBUG - Assert.IsTrue(valid); -#endif - - if (!valid) - return GPUInstanceIndex.Invalid; - - int instanceType = (int)instance.type; - int perTypeInstanceIndex = instance.instanceIndex; - int gpuInstanceIndex = instancesNumPrefixSum[instanceType] + perTypeInstanceIndex; - - return new GPUInstanceIndex { index = gpuInstanceIndex }; - } - - public int GetPropertyIndex(int propertyID, bool assertOnFail = true) - { - if (nameToMetadataMap.TryGetValue(propertyID, out int componentIndex)) - { - return componentIndex; - } - - if (assertOnFail) - Assert.IsTrue(false, "Count not find gpu address for parameter specified: " + propertyID); - return -1; - } - - public int GetGpuAddress(string strName, bool assertOnFail = true) - { - int componentIndex = GetPropertyIndex(Shader.PropertyToID(strName), false); - if (assertOnFail && componentIndex == -1) - Assert.IsTrue(false, "Count not find gpu address for parameter specified: " + strName); - - return componentIndex != -1 ? gpuBufferComponentAddress[componentIndex] : -1; - } - - public int GetGpuAddress(int propertyID, bool assertOnFail = true) - { - int componentIndex = GetPropertyIndex(propertyID, assertOnFail); - return componentIndex != -1 ? gpuBufferComponentAddress[componentIndex] : -1; - } - - public GPUInstanceIndex CPUInstanceToGPUInstance(InstanceHandle instance) - { - return CPUInstanceToGPUInstance(instancesNumPrefixSum, instance); - } - - public unsafe InstanceHandle GPUInstanceToCPUInstance(GPUInstanceIndex gpuInstanceIndex) - { - var instanceIndex = gpuInstanceIndex.index; - InstanceType instanceType = InstanceType.Count; - - for(int i = 0; i < (int)InstanceType.Count; ++i) - { - int instanceNum = instanceNumInfo.GetInstanceNum((InstanceType)i); - if(instanceIndex < instanceNum) - { - instanceType = (InstanceType)i; - break; - } - instanceIndex -= instanceNum; - } - - if(instanceType == InstanceType.Count) - return InstanceHandle.Invalid; - - Assert.IsTrue(instanceIndex < instanceNumInfo.GetInstanceNum(instanceType)); - return InstanceHandle.Create(instanceIndex, instanceType); - } - - public void CPUInstanceArrayToGPUInstanceArray(NativeArray instances, NativeArray gpuInstanceIndices) - { - Assert.AreEqual(instances.Length, gpuInstanceIndices.Length); - - Profiling.Profiler.BeginSample("CPUInstanceArrayToGPUInstanceArray"); - - new ConvertCPUInstancesToGPUInstancesJob { instancesNumPrefixSum = instancesNumPrefixSum, instances = instances, gpuInstanceIndices = gpuInstanceIndices } - .Schedule(instances.Length, ConvertCPUInstancesToGPUInstancesJob.k_BatchSize).Complete(); - - Profiling.Profiler.EndSample(); - } - - public void Dispose() - { - if(instancesSpan.IsCreated) - instancesSpan.Dispose(); - - if(instancesNumPrefixSum.IsCreated) - instancesNumPrefixSum.Dispose(); - - if (descriptions.IsCreated) - descriptions.Dispose(); - - if (defaultMetadata.IsCreated) - defaultMetadata.Dispose(); - - if (gpuBufferComponentAddress.IsCreated) - gpuBufferComponentAddress.Dispose(); - - if (nameToMetadataMap.IsCreated) - nameToMetadataMap.Dispose(); - - if (gpuBuffer != null) - gpuBuffer.Release(); - - if (validComponentsIndicesGpuBuffer != null) - validComponentsIndicesGpuBuffer.Release(); - - if (componentAddressesGpuBuffer != null) - componentAddressesGpuBuffer.Release(); - - if (componentInstanceIndexRangesGpuBuffer != null) - componentInstanceIndexRangesGpuBuffer.Release(); - - if (componentByteCountsGpuBuffer != null) - componentByteCountsGpuBuffer.Release(); - } - - public ReadOnly AsReadOnly() - { - return new ReadOnly(this); - } - - internal readonly struct ReadOnly - { - private readonly NativeArray instancesNumPrefixSum; - - public ReadOnly(GPUInstanceDataBuffer buffer) - { - instancesNumPrefixSum = buffer.instancesNumPrefixSum; - } - - public GPUInstanceIndex CPUInstanceToGPUInstance(InstanceHandle instance) - { - return GPUInstanceDataBuffer.CPUInstanceToGPUInstance(instancesNumPrefixSum, instance); - } - - public void CPUInstanceArrayToGPUInstanceArray(NativeArray instances, NativeArray gpuInstanceIndices) - { - Assert.AreEqual(instances.Length, gpuInstanceIndices.Length); - - Profiling.Profiler.BeginSample("CPUInstanceArrayToGPUInstanceArray"); - - new ConvertCPUInstancesToGPUInstancesJob { instancesNumPrefixSum = instancesNumPrefixSum, instances = instances, gpuInstanceIndices = gpuInstanceIndices } - .Schedule(instances.Length, ConvertCPUInstancesToGPUInstancesJob.k_BatchSize).Complete(); - - Profiling.Profiler.EndSample(); - } - } - - [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] - struct ConvertCPUInstancesToGPUInstancesJob : IJobParallelFor - { - public const int k_BatchSize = 512; - - [ReadOnly] public NativeArray instancesNumPrefixSum; - [ReadOnly] public NativeArray instances; - - [WriteOnly] public NativeArray gpuInstanceIndices; - - public void Execute(int index) - { - gpuInstanceIndices[index] = CPUInstanceToGPUInstance(instancesNumPrefixSum, instances[index]); - } - } - } -} diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/GPUInstanceDataUploader.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/GPUInstanceDataUploader.cs deleted file mode 100644 index 85d1bd6e056..00000000000 --- a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/GPUInstanceDataUploader.cs +++ /dev/null @@ -1,579 +0,0 @@ -using Unity.Burst; -using Unity.Collections; -using UnityEngine.Assertions; -using Unity.Collections.LowLevel.Unsafe; -using Unity.Jobs; -using System; - -namespace UnityEngine.Rendering -{ - internal struct GPUInstanceDataBufferBuilder : IDisposable - { - private NativeList m_Components; - - private MetadataValue CreateMetadataValue(int nameID, int gpuAddress, bool isOverridden) - { - const uint kIsOverriddenBit = 0x80000000; - return new MetadataValue - { - NameID = nameID, - Value = (uint)gpuAddress | (isOverridden ? kIsOverriddenBit : 0), - }; - } - - public void AddComponent(int propertyID, bool isOverriden, bool isPerInstance, InstanceType instanceType, InstanceComponentGroup componentGroup = InstanceComponentGroup.Default) where T : unmanaged - { - AddComponent(propertyID, isOverriden, UnsafeUtility.SizeOf(), isPerInstance, instanceType, componentGroup); - } - - public void AddComponent(int propertyID, bool isOverriden, int byteSize, bool isPerInstance, InstanceType instanceType, InstanceComponentGroup componentGroup) - { - if (!m_Components.IsCreated) - m_Components = new NativeList(64, Allocator.Temp); - - if (m_Components.Length > 0) - Assert.IsTrue(m_Components[m_Components.Length - 1].instanceType <= instanceType, "Added components must be sorted by InstanceType for better memory layout."); - - m_Components.Add(new GPUInstanceComponentDesc(propertyID, byteSize, isOverriden, isPerInstance, instanceType, componentGroup)); - } - - public unsafe GPUInstanceDataBuffer Build(in InstanceNumInfo instanceNumInfo) - { - int perInstanceComponentCounts = 0; - var perInstanceComponentIndices = new NativeArray(m_Components.Length, Allocator.Temp); - var componentAddresses = new NativeArray(m_Components.Length, Allocator.Temp); - var componentByteSizes = new NativeArray(m_Components.Length, Allocator.Temp); - var componentInstanceIndexRanges = new NativeArray(m_Components.Length, Allocator.Temp); - - GPUInstanceDataBuffer newBuffer = new GPUInstanceDataBuffer(); - newBuffer.instanceNumInfo = instanceNumInfo; - newBuffer.instancesNumPrefixSum = new NativeArray((int)InstanceType.Count, Allocator.Persistent); - newBuffer.instancesSpan = new NativeArray((int)InstanceType.Count, Allocator.Persistent); - - int sum = 0; - - for (int i = 0; i < (int)InstanceType.Count; ++i) - { - newBuffer.instancesNumPrefixSum[i] = sum; - sum += instanceNumInfo.InstanceNums[i]; - newBuffer.instancesSpan[i] = instanceNumInfo.GetInstanceNumIncludingChildren((InstanceType)i); - } - - newBuffer.layoutVersion = GPUInstanceDataBuffer.NextVersion(); - newBuffer.version = 0; - newBuffer.defaultMetadata = new NativeArray(m_Components.Length, Allocator.Persistent); - newBuffer.descriptions = new NativeArray(m_Components.Length, Allocator.Persistent); - newBuffer.nameToMetadataMap = new NativeParallelHashMap(m_Components.Length, Allocator.Persistent); - newBuffer.gpuBufferComponentAddress = new NativeArray(m_Components.Length, Allocator.Persistent); - - //Initial offset, must be 0, 0, 0, 0. - int vec4Size = UnsafeUtility.SizeOf(); - int byteOffset = 4 * vec4Size; - - for (int c = 0; c < m_Components.Length; ++c) - { - var componentDesc = m_Components[c]; - newBuffer.descriptions[c] = componentDesc; - - int instancesBegin = newBuffer.instancesNumPrefixSum[(int)componentDesc.instanceType]; - int instancesEnd = instancesBegin + newBuffer.instancesSpan[(int)componentDesc.instanceType]; - int instancesNum = componentDesc.isPerInstance ? instancesEnd - instancesBegin : 1; - Assert.IsTrue(instancesNum >= 0); - - componentInstanceIndexRanges[c] = new Vector2Int(instancesBegin, instancesBegin + instancesNum); - - int componentGPUAddress = byteOffset - instancesBegin * componentDesc.byteSize; - Assert.IsTrue(componentGPUAddress >= 0, "GPUInstanceDataBufferBuilder: GPU address is negative. This is not supported for now. See kIsOverriddenBit." + - "In general, if there is only one root InstanceType (MeshRenderer in our case) with a component that is larger or equal in size than any component in a derived InstanceType." + - "And the number of parent gpu instances are always larger or equal to the number of derived type gpu instances. Than GPU address cannot become negative."); - - newBuffer.gpuBufferComponentAddress[c] = componentGPUAddress; - newBuffer.defaultMetadata[c] = CreateMetadataValue(componentDesc.propertyID, componentGPUAddress, componentDesc.isOverriden); - - componentAddresses[c] = componentGPUAddress; - componentByteSizes[c] = componentDesc.byteSize; - - int componentByteSize = componentDesc.byteSize * instancesNum; - byteOffset += componentByteSize; - - bool addedToMap = newBuffer.nameToMetadataMap.TryAdd(componentDesc.propertyID, c); - Assert.IsTrue(addedToMap, "Repetitive metadata element added to object."); - - if (componentDesc.isPerInstance) - { - perInstanceComponentIndices[perInstanceComponentCounts] = c; - perInstanceComponentCounts++; - } - } - - newBuffer.byteSize = byteOffset; - newBuffer.gpuBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Raw, newBuffer.byteSize / 4, 4); - newBuffer.gpuBuffer.SetData(new NativeArray(4, Allocator.Temp), 0, 0, 4); - newBuffer.validComponentsIndicesGpuBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Raw, perInstanceComponentCounts, 4); - newBuffer.validComponentsIndicesGpuBuffer.SetData(perInstanceComponentIndices, 0, 0, perInstanceComponentCounts); - newBuffer.componentAddressesGpuBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Raw, m_Components.Length, 4); - newBuffer.componentAddressesGpuBuffer.SetData(componentAddresses, 0, 0, m_Components.Length); - newBuffer.componentInstanceIndexRangesGpuBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Raw, m_Components.Length, 8); - newBuffer.componentInstanceIndexRangesGpuBuffer.SetData(componentInstanceIndexRanges, 0, 0, m_Components.Length); - newBuffer.componentByteCountsGpuBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Raw, m_Components.Length, 4); - newBuffer.componentByteCountsGpuBuffer.SetData(componentByteSizes, 0, 0, m_Components.Length); - newBuffer.perInstanceComponentCount = perInstanceComponentCounts; - - perInstanceComponentIndices.Dispose(); - componentAddresses.Dispose(); - componentByteSizes.Dispose(); - - return newBuffer; - } - - public void Dispose() - { - if (m_Components.IsCreated) - m_Components.Dispose(); - } - } - - internal struct GPUInstanceDataBufferUploader : IDisposable - { - private static class UploadKernelIDs - { - public static readonly int _InputValidComponentCounts = Shader.PropertyToID("_InputValidComponentCounts"); - public static readonly int _InputInstanceCounts = Shader.PropertyToID("_InputInstanceCounts"); - public static readonly int _InputInstanceByteSize = Shader.PropertyToID("_InputInstanceByteSize"); - public static readonly int _InputComponentOffsets = Shader.PropertyToID("_InputComponentOffsets"); - public static readonly int _InputInstanceData = Shader.PropertyToID("_InputInstanceData"); - public static readonly int _InputInstanceIndices = Shader.PropertyToID("_InputInstanceIndices"); - public static readonly int _InputValidComponentIndices = Shader.PropertyToID("_InputValidComponentIndices"); - public static readonly int _InputComponentAddresses = Shader.PropertyToID("_InputComponentAddresses"); - public static readonly int _InputComponentByteCounts = Shader.PropertyToID("_InputComponentByteCounts"); - public static readonly int _InputComponentInstanceIndexRanges = Shader.PropertyToID("_InputComponentInstanceIndexRanges"); - public static readonly int _OutputBuffer = Shader.PropertyToID("_OutputBuffer"); - } - - public struct GPUResources : IDisposable - { - public ComputeBuffer instanceData; - public ComputeBuffer instanceIndices; - public ComputeBuffer inputComponentOffsets; - public ComputeBuffer validComponentIndices; - public ComputeShader cs; - public int kernelId; - - private int m_InstanceDataByteSize; - private int m_InstanceCount; - private int m_ComponentCounts; - private int m_ValidComponentIndicesCount; - - public void LoadShaders(GPUResidentDrawerResources resources) - { - if (cs == null) - { - cs = resources.instanceDataBufferUploadKernels; - kernelId = cs.FindKernel("MainUploadScatterInstances"); - } - } - - public void CreateResources(int newInstanceCount, int sizePerInstance, int newComponentCounts, int validComponentIndicesCount) - { - int newInstanceDataByteSize = newInstanceCount * sizePerInstance; - if (newInstanceDataByteSize > m_InstanceDataByteSize || instanceData == null) - { - if (instanceData != null) - instanceData.Release(); - - instanceData = new ComputeBuffer((newInstanceDataByteSize + 3) / 4, 4, ComputeBufferType.Raw); - m_InstanceDataByteSize = newInstanceDataByteSize; - } - - if (newInstanceCount > m_InstanceCount || instanceIndices == null) - { - if (instanceIndices != null) - instanceIndices.Release(); - - instanceIndices = new ComputeBuffer(newInstanceCount, 4, ComputeBufferType.Raw); - m_InstanceCount = newInstanceCount; - } - - if (newComponentCounts > m_ComponentCounts || inputComponentOffsets == null) - { - if (inputComponentOffsets != null) - inputComponentOffsets.Release(); - - inputComponentOffsets = new ComputeBuffer(newComponentCounts, 4, ComputeBufferType.Raw); - m_ComponentCounts = newComponentCounts; - } - - if (validComponentIndicesCount > m_ValidComponentIndicesCount || validComponentIndices == null) - { - if (validComponentIndices != null) - validComponentIndices.Release(); - - validComponentIndices = new ComputeBuffer(validComponentIndicesCount, 4, ComputeBufferType.Raw); - m_ValidComponentIndicesCount = validComponentIndicesCount; - } - } - - public void Dispose() - { - cs = null; - - if (instanceData != null) - instanceData.Release(); - - if (instanceIndices != null) - instanceIndices.Release(); - - if (inputComponentOffsets != null) - inputComponentOffsets.Release(); - - if(validComponentIndices != null) - validComponentIndices.Release(); - } - } - - int m_UintPerInstance; - int m_Capacity; - int m_InstanceCount; - NativeArray m_ComponentIsInstanced; - NativeArray m_ComponentDataIndex; - NativeArray m_DescriptionsUintSize; - NativeArray m_TmpDataBuffer; - NativeList m_WritenComponentIndices; - - private NativeArray m_DummyArray; - - public GPUInstanceDataBufferUploader(in NativeArray descriptions, int capacity, InstanceType instanceType) - { - m_Capacity = capacity; - m_InstanceCount = 0; - m_UintPerInstance = 0; - m_ComponentDataIndex = new NativeArray(descriptions.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); - m_ComponentIsInstanced = new NativeArray(descriptions.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); - m_DescriptionsUintSize = new NativeArray(descriptions.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); - m_WritenComponentIndices = new NativeList(descriptions.Length, Allocator.TempJob); - m_DummyArray = new NativeArray(0, Allocator.Persistent); - - int uintSize = UnsafeUtility.SizeOf(); - - for (int c = 0; c < descriptions.Length; ++c) - { - var componentDesc = descriptions[c]; - m_ComponentIsInstanced[c] = componentDesc.isPerInstance; - if(componentDesc.instanceType == instanceType) - { - m_ComponentDataIndex[c] = m_UintPerInstance; - m_DescriptionsUintSize[c] = descriptions[c].byteSize / uintSize; - m_UintPerInstance += componentDesc.isPerInstance ? (componentDesc.byteSize / uintSize) : 0; - } - else - { - m_ComponentDataIndex[c] = -1; - m_DescriptionsUintSize[c] = 0; - } - } - - m_TmpDataBuffer = new NativeArray(m_Capacity * m_UintPerInstance, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); - } - - public unsafe IntPtr GetUploadBufferPtr() - { - Assert.IsTrue(m_TmpDataBuffer.IsCreated); - Assert.IsTrue(m_TmpDataBuffer.Length > 0 && m_InstanceCount > 0); - return new IntPtr(m_TmpDataBuffer.GetUnsafePtr()); - } - - public int GetUIntPerInstance() - { - return m_UintPerInstance; - } - - public int GetParamUIntOffset(int parameterIndex) - { - Assert.IsTrue(m_ComponentIsInstanced[parameterIndex], "Component is non instanced. Can only call this function on parameters that are for all instances."); - Assert.IsTrue(parameterIndex >= 0 && parameterIndex < m_ComponentDataIndex.Length, "Parameter index invalid."); - Assert.IsTrue(m_ComponentDataIndex[parameterIndex] != -1, "Parameter index is not allocated. Did you allocate proper InstanceType parameters?"); - return m_ComponentDataIndex[parameterIndex]; - } - - public int PrepareParamWrite(int parameterIndex) where T : unmanaged - { - int uintPerParameter = UnsafeUtility.SizeOf() / UnsafeUtility.SizeOf(); - Assert.IsTrue(uintPerParameter == m_DescriptionsUintSize[parameterIndex], "Parameter to write is incompatible, must be same stride as destination."); - if (!m_WritenComponentIndices.Contains(parameterIndex)) - m_WritenComponentIndices.Add(parameterIndex); - return GetParamUIntOffset(parameterIndex); - } - - public unsafe void AllocateUploadHandles(int handlesLength) - { - // No need to preallocate instances anymore, as those are passed as parameters to SubmitToGPU to avoid data duplication - // We just set the instance count here to ensure that a) we have the correct capacity and b) write/gatherInstanceData copies the correct amount - Assert.IsTrue(m_Capacity >= handlesLength); - m_InstanceCount = handlesLength; - } - - public unsafe JobHandle WriteInstanceDataJob(int parameterIndex, NativeArray instanceData) where T : unmanaged - { - return WriteInstanceDataJob(parameterIndex, instanceData, m_DummyArray); - } - - public unsafe JobHandle WriteInstanceDataJob(int parameterIndex, NativeArray instanceData, NativeArray gatherIndices) where T : unmanaged - { - if (m_InstanceCount == 0) - return default; - - var gatherData = gatherIndices.Length != 0; - Assert.IsTrue(gatherData || instanceData.Length == m_InstanceCount); - Assert.IsTrue(!gatherData || gatherIndices.Length == m_InstanceCount); - Assert.IsTrue(UnsafeUtility.SizeOf() >= UnsafeUtility.SizeOf()); - - int uintPerParameter = UnsafeUtility.SizeOf() / UnsafeUtility.SizeOf(); - Assert.IsTrue(m_ComponentIsInstanced[parameterIndex], "Component is non instanced. Can only call this function on parameters that are for all instances."); - Assert.IsTrue(uintPerParameter == m_DescriptionsUintSize[parameterIndex], "Parameter to write is incompatible, must be same stride as destination."); - Assert.IsTrue(parameterIndex >= 0 && parameterIndex < m_ComponentDataIndex.Length, "Parameter index invalid."); - Assert.IsTrue(m_ComponentDataIndex[parameterIndex] != -1, "Parameter index is not allocated. Did you allocate proper InstanceType parameters?"); - - if (!m_WritenComponentIndices.Contains(parameterIndex)) - m_WritenComponentIndices.Add(parameterIndex); - - var writeJob = new WriteInstanceDataParameterJob - { - gatherData = gatherData, - gatherIndices = gatherIndices, - parameterIndex = parameterIndex, - uintPerParameter = uintPerParameter, - uintPerInstance = m_UintPerInstance, - componentDataIndex = m_ComponentDataIndex, - instanceData = instanceData.Reinterpret(UnsafeUtility.SizeOf()), - tmpDataBuffer = m_TmpDataBuffer - }; - - return writeJob.Schedule(m_InstanceCount, WriteInstanceDataParameterJob.k_BatchSize); - } - - public void SubmitToGpu(GPUInstanceDataBuffer instanceDataBuffer, NativeArray gpuInstanceIndices, ref GPUResources gpuResources, bool submitOnlyWrittenParams) - { - if (m_InstanceCount == 0) - return; - - Assert.IsTrue(gpuInstanceIndices.Length == m_InstanceCount); - - ++instanceDataBuffer.version; - int uintSize = UnsafeUtility.SizeOf(); - int instanceByteSize = m_UintPerInstance * uintSize; - gpuResources.CreateResources(m_InstanceCount, instanceByteSize, m_ComponentDataIndex.Length, m_WritenComponentIndices.Length); - gpuResources.instanceData.SetData(m_TmpDataBuffer, 0, 0, m_InstanceCount * m_UintPerInstance); - gpuResources.instanceIndices.SetData(gpuInstanceIndices, 0, 0, m_InstanceCount); - gpuResources.inputComponentOffsets.SetData(m_ComponentDataIndex, 0, 0, m_ComponentDataIndex.Length); - gpuResources.cs.SetInt(UploadKernelIDs._InputInstanceCounts, m_InstanceCount); - gpuResources.cs.SetInt(UploadKernelIDs._InputInstanceByteSize, instanceByteSize); - gpuResources.cs.SetBuffer(gpuResources.kernelId, UploadKernelIDs._InputInstanceData, gpuResources.instanceData); - gpuResources.cs.SetBuffer(gpuResources.kernelId, UploadKernelIDs._InputInstanceIndices, gpuResources.instanceIndices); - gpuResources.cs.SetBuffer(gpuResources.kernelId, UploadKernelIDs._InputComponentOffsets, gpuResources.inputComponentOffsets); - if (submitOnlyWrittenParams) - { - gpuResources.validComponentIndices.SetData(m_WritenComponentIndices.AsArray(), 0, 0, m_WritenComponentIndices.Length); - gpuResources.cs.SetInt(UploadKernelIDs._InputValidComponentCounts, m_WritenComponentIndices.Length); - gpuResources.cs.SetBuffer(gpuResources.kernelId, UploadKernelIDs._InputValidComponentIndices, gpuResources.validComponentIndices); - } - else - { - gpuResources.cs.SetInt(UploadKernelIDs._InputValidComponentCounts, instanceDataBuffer.perInstanceComponentCount); - gpuResources.cs.SetBuffer(gpuResources.kernelId, UploadKernelIDs._InputValidComponentIndices, instanceDataBuffer.validComponentsIndicesGpuBuffer); - } - gpuResources.cs.SetBuffer(gpuResources.kernelId, UploadKernelIDs._InputComponentAddresses, instanceDataBuffer.componentAddressesGpuBuffer); - gpuResources.cs.SetBuffer(gpuResources.kernelId, UploadKernelIDs._InputComponentByteCounts, instanceDataBuffer.componentByteCountsGpuBuffer); - gpuResources.cs.SetBuffer(gpuResources.kernelId, UploadKernelIDs._InputComponentInstanceIndexRanges, instanceDataBuffer.componentInstanceIndexRangesGpuBuffer); - gpuResources.cs.SetBuffer(gpuResources.kernelId, UploadKernelIDs._OutputBuffer, instanceDataBuffer.gpuBuffer); - gpuResources.cs.Dispatch(gpuResources.kernelId, (m_InstanceCount + 63) / 64, 1, 1); - - m_InstanceCount = 0; - m_WritenComponentIndices.Clear(); - } - - public void SubmitToGpu(GPUInstanceDataBuffer instanceDataBuffer, NativeArray instances, ref GPUResources gpuResources, bool submitOnlyWrittenParams) - { - if (m_InstanceCount == 0) - return; - - var gpuInstanceIndices = new NativeArray(instances.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); - instanceDataBuffer.CPUInstanceArrayToGPUInstanceArray(instances, gpuInstanceIndices); - - SubmitToGpu(instanceDataBuffer, gpuInstanceIndices, ref gpuResources, submitOnlyWrittenParams); - - gpuInstanceIndices.Dispose(); - } - - public void Dispose() - { - if (m_ComponentDataIndex.IsCreated) - m_ComponentDataIndex.Dispose(); - - if (m_ComponentIsInstanced.IsCreated) - m_ComponentIsInstanced.Dispose(); - - if (m_DescriptionsUintSize.IsCreated) - m_DescriptionsUintSize.Dispose(); - - if (m_TmpDataBuffer.IsCreated) - m_TmpDataBuffer.Dispose(); - - if (m_WritenComponentIndices.IsCreated) - m_WritenComponentIndices.Dispose(); - - if(m_DummyArray.IsCreated) - m_DummyArray.Dispose(); - } - - [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] - internal struct WriteInstanceDataParameterJob : IJobParallelFor - { - public const int k_BatchSize = 512; - - [ReadOnly] public bool gatherData; - [ReadOnly] public int parameterIndex; - [ReadOnly] public int uintPerParameter; - [ReadOnly] public int uintPerInstance; - [ReadOnly] public NativeArray componentDataIndex; - [ReadOnly] public NativeArray gatherIndices; - [NativeDisableContainerSafetyRestriction, NoAlias][ReadOnly] public NativeArray instanceData; - - [NativeDisableContainerSafetyRestriction, NoAlias][WriteOnly] public NativeArray tmpDataBuffer; - - public unsafe void Execute(int index) - { - Assert.IsTrue(index * uintPerInstance < tmpDataBuffer.Length, "Trying to write to an instance buffer out of bounds."); - - int dataOffset = (gatherData ? gatherIndices[index] : index) * uintPerParameter; - Assert.IsTrue(dataOffset < instanceData.Length); - - int uintSize = UnsafeUtility.SizeOf(); - - uint* data = (uint*)instanceData.GetUnsafePtr() + dataOffset; - UnsafeUtility.MemCpy((uint*)tmpDataBuffer.GetUnsafePtr() + index * uintPerInstance + componentDataIndex[parameterIndex], data, - uintPerParameter * uintSize); - } - } - } - - internal struct GPUInstanceDataBufferGrower : IDisposable - { - private static class CopyInstancesKernelIDs - { - public static readonly int _InputValidComponentCounts = Shader.PropertyToID("_InputValidComponentCounts"); - public static readonly int _InstanceCounts = Shader.PropertyToID("_InstanceCounts"); - public static readonly int _InstanceOffset = Shader.PropertyToID("_InstanceOffset"); - public static readonly int _OutputInstanceOffset = Shader.PropertyToID("_OutputInstanceOffset"); - public static readonly int _ValidComponentIndices = Shader.PropertyToID("_ValidComponentIndices"); - public static readonly int _ComponentByteCounts = Shader.PropertyToID("_ComponentByteCounts"); - public static readonly int _InputComponentAddresses = Shader.PropertyToID("_InputComponentAddresses"); - public static readonly int _OutputComponentAddresses = Shader.PropertyToID("_OutputComponentAddresses"); - public static readonly int _InputComponentInstanceIndexRanges = Shader.PropertyToID("_InputComponentInstanceIndexRanges"); - public static readonly int _InputBuffer = Shader.PropertyToID("_InputBuffer"); - public static readonly int _OutputBuffer = Shader.PropertyToID("_OutputBuffer"); - } - - public struct GPUResources : IDisposable - { - public ComputeShader cs; - public int kernelId; - - public void LoadShaders(GPUResidentDrawerResources resources) - { - if (cs == null) - { - cs = resources.instanceDataBufferCopyKernels; - kernelId = cs.FindKernel("MainCopyInstances"); - } - } - - public void CreateResources() - { - } - - public void Dispose() - { - cs = null; - } - } - - private GPUInstanceDataBuffer m_SrcBuffer; - private GPUInstanceDataBuffer m_DstBuffer; - - //@ We should implement buffer shrinker too, otherwise lots of instances can be allocated for trees for example - //@ while there are no trees in scenes that are in use at all. - public unsafe GPUInstanceDataBufferGrower(GPUInstanceDataBuffer sourceBuffer, in InstanceNumInfo instanceNumInfo) - { - m_SrcBuffer = sourceBuffer; - m_DstBuffer = null; - - bool needToGrow = false; - - for(int i = 0; i < (int)InstanceType.Count; ++i) - { - Assert.IsTrue(instanceNumInfo.InstanceNums[i] >= sourceBuffer.instanceNumInfo.InstanceNums[i], "Shrinking GPU instance buffer is not supported yet."); - - if (instanceNumInfo.InstanceNums[i] > sourceBuffer.instanceNumInfo.InstanceNums[i]) - needToGrow = true; - } - - if (!needToGrow) - return; - - GPUInstanceDataBufferBuilder builder = new GPUInstanceDataBufferBuilder(); - - foreach (GPUInstanceComponentDesc descriptor in sourceBuffer.descriptions) - builder.AddComponent(descriptor.propertyID, descriptor.isOverriden, descriptor.byteSize, descriptor.isPerInstance, descriptor.instanceType, descriptor.componentGroup); - - m_DstBuffer = builder.Build(instanceNumInfo); - builder.Dispose(); - } - - public GPUInstanceDataBuffer SubmitToGpu(ref GPUResources gpuResources) - { - if (m_DstBuffer == null) - return m_SrcBuffer; - - int totalInstanceCount = m_SrcBuffer.instanceNumInfo.GetTotalInstanceNum(); - - if(totalInstanceCount == 0) - return m_DstBuffer; - - Assert.IsTrue(m_SrcBuffer.perInstanceComponentCount == m_DstBuffer.perInstanceComponentCount); - - gpuResources.CreateResources(); - gpuResources.cs.SetInt(CopyInstancesKernelIDs._InputValidComponentCounts, m_SrcBuffer.perInstanceComponentCount); - gpuResources.cs.SetBuffer(gpuResources.kernelId, CopyInstancesKernelIDs._ValidComponentIndices, m_SrcBuffer.validComponentsIndicesGpuBuffer); - gpuResources.cs.SetBuffer(gpuResources.kernelId, CopyInstancesKernelIDs._ComponentByteCounts, m_SrcBuffer.componentByteCountsGpuBuffer); - gpuResources.cs.SetBuffer(gpuResources.kernelId, CopyInstancesKernelIDs._InputComponentAddresses, m_SrcBuffer.componentAddressesGpuBuffer); - gpuResources.cs.SetBuffer(gpuResources.kernelId, CopyInstancesKernelIDs._InputComponentInstanceIndexRanges, m_SrcBuffer.componentInstanceIndexRangesGpuBuffer); - gpuResources.cs.SetBuffer(gpuResources.kernelId, CopyInstancesKernelIDs._OutputComponentAddresses, m_DstBuffer.componentAddressesGpuBuffer); - gpuResources.cs.SetBuffer(gpuResources.kernelId, CopyInstancesKernelIDs._InputBuffer, m_SrcBuffer.gpuBuffer); - gpuResources.cs.SetBuffer(gpuResources.kernelId, CopyInstancesKernelIDs._OutputBuffer, m_DstBuffer.gpuBuffer); - - //@ We could compute new instance indices on CPU and do one dispatch. - //@ Otherwise in theory these multiple dispatches could overlap with no UAV barrier between them as they write to a different parts of the UAV. - //@ Need to profile which is better. - for(int i = 0; i < (int)InstanceType.Count; ++i) - { - int instanceCount = m_SrcBuffer.instanceNumInfo.GetInstanceNum((InstanceType)i); - - if(instanceCount > 0) - { - int instanceOffset = m_SrcBuffer.instancesNumPrefixSum[i]; - int outputInstanceOffset = m_DstBuffer.instancesNumPrefixSum[i]; - gpuResources.cs.SetInt(CopyInstancesKernelIDs._InstanceCounts, instanceCount); - gpuResources.cs.SetInt(CopyInstancesKernelIDs._InstanceOffset, instanceOffset); - gpuResources.cs.SetInt(CopyInstancesKernelIDs._OutputInstanceOffset, outputInstanceOffset); - gpuResources.cs.Dispatch(gpuResources.kernelId, (instanceCount + 63) / 64, 1, 1); - } - } - - return m_DstBuffer; - } - - public void Dispose() - { - } - } -} diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceAllocator.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceAllocator.cs deleted file mode 100644 index fccae9c69e7..00000000000 --- a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceAllocator.cs +++ /dev/null @@ -1,178 +0,0 @@ -using System; -using Unity.Collections; -using Unity.Collections.LowLevel.Unsafe; -using Unity.Mathematics; -using UnityEngine.Assertions; - -namespace UnityEngine.Rendering -{ - //@ Add instance version to detect dangling instance handles. - internal struct InstanceHandle : IEquatable, IComparable - { - // Don't use this index to reference GPU data. This index is to reference CPU data only. - // To reference GPU data convert InstanceHandle to GPUInstanceIndex. - public int index { get; private set; } - - // This is unique instance index for each instance type. - public int instanceIndex => index >> InstanceTypeInfo.kInstanceTypeBitCount; - - // We store type bits as lower bits because this makes max InstanceHandle index bounded by how many instances we have. - // So you can allocate directly indexed arrays. This is fine as long as we have only 1 to 4 instance types. - // If we put type bits in higher bits then we might want to make CPUInstanceData sparse set InstanceIndices table to be paged. - public InstanceType type => (InstanceType)(index & InstanceTypeInfo.kInstanceTypeMask); - - public bool valid => index != -1; - public static readonly InstanceHandle Invalid = new InstanceHandle() { index = -1 }; - public static InstanceHandle Create(int instanceIndex, InstanceType instanceType) { return new InstanceHandle() { index = instanceIndex << InstanceTypeInfo.kInstanceTypeBitCount | (int)instanceType }; } - public static InstanceHandle FromInt(int value) { return new InstanceHandle() { index = value }; } - public bool Equals(InstanceHandle other) => index == other.index; - public int CompareTo(InstanceHandle other) { return index.CompareTo(other.index); } - public override int GetHashCode() { return index; } - } - - internal struct SharedInstanceHandle : IEquatable, IComparable - { - public int index { get; set; } - public bool valid => index != -1; - public static readonly SharedInstanceHandle Invalid = new SharedInstanceHandle() { index = -1 }; - public bool Equals(SharedInstanceHandle other) => index == other.index; - public int CompareTo(SharedInstanceHandle other) { return index.CompareTo(other.index); } - public override int GetHashCode() { return index; } - } - - internal struct GPUInstanceIndex : IEquatable, IComparable - { - public int index { get; set; } - public bool valid => index != -1; - public static readonly GPUInstanceIndex Invalid = new GPUInstanceIndex() { index = -1 }; - public bool Equals(GPUInstanceIndex other) => index == other.index; - public int CompareTo(GPUInstanceIndex other) { return index.CompareTo(other.index); } - public override int GetHashCode() { return index; } - } - - internal struct InstanceAllocator - { - private NativeArray m_StructData; - private NativeList m_FreeInstances; - private int m_BaseInstanceOffset; - private int m_InstanceStride; - - public int length { get => m_StructData[0]; set => m_StructData[0] = value; } - public bool valid => m_StructData.IsCreated; - - public void Initialize(int baseInstanceOffset = 0, int instanceStride = 1) - { - m_StructData = new NativeArray(1, Allocator.Persistent); - m_FreeInstances = new NativeList(Allocator.Persistent); - m_BaseInstanceOffset = baseInstanceOffset; - m_InstanceStride = instanceStride; - } - - public void Dispose() - { - m_StructData.Dispose(); - m_FreeInstances.Dispose(); - } - - public int AllocateInstance() - { - int instance; - - if (m_FreeInstances.Length > 0) - { - instance = m_FreeInstances[m_FreeInstances.Length - 1]; - m_FreeInstances.RemoveAtSwapBack(m_FreeInstances.Length - 1); - } - else - { - instance = length * m_InstanceStride + m_BaseInstanceOffset; - length += 1; - } - - return instance; - } - - public void FreeInstance(int instance) - { - //@ This is a bit weak validation. Need something better but fast. - Assert.IsTrue(instance >= 0 && instance < length * m_InstanceStride); - m_FreeInstances.Add(instance); - } - - public int GetNumAllocated() - { - return length - m_FreeInstances.Length; - } - } - - internal unsafe struct InstanceAllocators - { - private InstanceAllocator m_InstanceAlloc_MeshRenderer; - private InstanceAllocator m_InstanceAlloc_SpeedTree; - private InstanceAllocator m_SharedInstanceAlloc; - - public void Initialize() - { - //@ Will keep it as two separate allocators for two types for now. Nested native containers are not allowed in burst. - m_InstanceAlloc_MeshRenderer = new InstanceAllocator(); - m_InstanceAlloc_SpeedTree = new InstanceAllocator(); - m_InstanceAlloc_MeshRenderer.Initialize((int)InstanceType.MeshRenderer, InstanceTypeInfo.kMaxInstanceTypesCount); - m_InstanceAlloc_SpeedTree.Initialize((int)InstanceType.SpeedTree, InstanceTypeInfo.kMaxInstanceTypesCount); - - m_SharedInstanceAlloc = new InstanceAllocator(); - m_SharedInstanceAlloc.Initialize(); - } - - public unsafe void Dispose() - { - m_InstanceAlloc_MeshRenderer.Dispose(); - m_InstanceAlloc_SpeedTree.Dispose(); - m_SharedInstanceAlloc.Dispose(); - } - - private InstanceAllocator GetInstanceAllocator(InstanceType type) - { - switch (type) - { - case InstanceType.MeshRenderer: - return m_InstanceAlloc_MeshRenderer; - case InstanceType.SpeedTree: - return m_InstanceAlloc_SpeedTree; - default: - throw new ArgumentException("Allocator for this type is not created."); - } - } - - public int GetInstanceHandlesLength(InstanceType type) - { - return GetInstanceAllocator(type).length; - } - - public int GetInstancesLength(InstanceType type) - { - return GetInstanceAllocator(type).GetNumAllocated(); - } - - public InstanceHandle AllocateInstance(InstanceType type) - { - return InstanceHandle.FromInt(GetInstanceAllocator(type).AllocateInstance()); - } - - public void FreeInstance(InstanceHandle instance) - { - Assert.IsTrue(instance.valid); - GetInstanceAllocator(instance.type).FreeInstance(instance.index); - } - - public unsafe SharedInstanceHandle AllocateSharedInstance() - { - return new SharedInstanceHandle { index = m_SharedInstanceAlloc.AllocateInstance() }; - } - - public void FreeSharedInstance(SharedInstanceHandle instance) - { - Assert.IsTrue(instance.valid); - m_SharedInstanceAlloc.FreeInstance(instance.index); - } - } -} diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceData.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceData.cs deleted file mode 100644 index 8bff455389d..00000000000 --- a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceData.cs +++ /dev/null @@ -1,1026 +0,0 @@ -using System; -using Unity.Collections; -using Unity.Collections.LowLevel.Unsafe; -using Unity.Mathematics; -using UnityEngine.Assertions; - -/// ----------------------------------------------------------------------- -/// See the data layout and relationship diagram at the bottom of the file. -/// ----------------------------------------------------------------------- - -namespace UnityEngine.Rendering -{ - internal struct CPUInstanceData : IDisposable - { - private const int k_InvalidIndex = -1; - - private NativeArray m_StructData; - private NativeList m_InstanceIndices; - - public NativeArray instances; - public NativeArray sharedInstances; - public ParallelBitArray localToWorldIsFlippedBits; - public NativeArray worldAABBs; - public NativeArray tetrahedronCacheIndices; - public ParallelBitArray movedInCurrentFrameBits; - public ParallelBitArray movedInPreviousFrameBits; - public ParallelBitArray visibleInPreviousFrameBits; - public EditorInstanceDataArrays editorData; - public NativeArray meshLodData; - - public int instancesLength { get => m_StructData[0]; set => m_StructData[0] = value; } - public int instancesCapacity { get => m_StructData[1]; set => m_StructData[1] = value; } - public int handlesLength => m_InstanceIndices.Length; - - public void Initialize(int initCapacity) - { - m_StructData = new NativeArray(2, Allocator.Persistent); - instancesCapacity = initCapacity; - m_InstanceIndices = new NativeList(Allocator.Persistent); - instances = new NativeArray(instancesCapacity, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); - instances.FillArray(InstanceHandle.Invalid); - sharedInstances = new NativeArray(instancesCapacity, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); - sharedInstances.FillArray(SharedInstanceHandle.Invalid); - localToWorldIsFlippedBits = new ParallelBitArray(instancesCapacity, Allocator.Persistent); - worldAABBs = new NativeArray(instancesCapacity, Allocator.Persistent); - tetrahedronCacheIndices = new NativeArray(instancesCapacity, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); - tetrahedronCacheIndices.FillArray(k_InvalidIndex); - movedInCurrentFrameBits = new ParallelBitArray(instancesCapacity, Allocator.Persistent); - movedInPreviousFrameBits = new ParallelBitArray(instancesCapacity, Allocator.Persistent); - visibleInPreviousFrameBits = new ParallelBitArray(instancesCapacity, Allocator.Persistent); - editorData.Initialize(initCapacity); - meshLodData = new NativeArray(instancesCapacity, Allocator.Persistent); - } - - public void Dispose() - { - m_StructData.Dispose(); - m_InstanceIndices.Dispose(); - instances.Dispose(); - sharedInstances.Dispose(); - localToWorldIsFlippedBits.Dispose(); - worldAABBs.Dispose(); - tetrahedronCacheIndices.Dispose(); - movedInCurrentFrameBits.Dispose(); - movedInPreviousFrameBits.Dispose(); - visibleInPreviousFrameBits.Dispose(); - editorData.Dispose(); - meshLodData.Dispose(); - } - - private void Grow(int newCapacity) - { - Assert.IsTrue(newCapacity > instancesCapacity); - - instances.ResizeArray(newCapacity); - instances.FillArray(InstanceHandle.Invalid, instancesCapacity); - sharedInstances.ResizeArray(newCapacity); - sharedInstances.FillArray(SharedInstanceHandle.Invalid, instancesCapacity); - localToWorldIsFlippedBits.Resize(newCapacity); - worldAABBs.ResizeArray(newCapacity); - tetrahedronCacheIndices.ResizeArray(newCapacity); - tetrahedronCacheIndices.FillArray(k_InvalidIndex, instancesCapacity); - movedInCurrentFrameBits.Resize(newCapacity); - movedInPreviousFrameBits.Resize(newCapacity); - visibleInPreviousFrameBits.Resize(newCapacity); - editorData.Grow(newCapacity); - meshLodData.ResizeArray(newCapacity); - - instancesCapacity = newCapacity; - } - - private void AddUnsafe(InstanceHandle instance) - { - if (instance.index >= m_InstanceIndices.Length) - { - int prevLength = m_InstanceIndices.Length; - m_InstanceIndices.ResizeUninitialized(instance.index + 1); - - for (int i = prevLength; i < m_InstanceIndices.Length - 1; ++i) - m_InstanceIndices[i] = k_InvalidIndex; - } - - m_InstanceIndices[instance.index] = instancesLength; - instances[instancesLength] = instance; - - ++instancesLength; - } - - public int InstanceToIndex(InstanceHandle instance) - { - Assert.IsTrue(IsValidInstance(instance)); - return m_InstanceIndices[instance.index]; - } - - public InstanceHandle IndexToInstance(int index) - { - Assert.IsTrue(IsValidIndex(index)); - return instances[index]; - } - - public bool IsValidInstance(InstanceHandle instance) - { - if (instance.valid && instance.index < m_InstanceIndices.Length) - { - int index = m_InstanceIndices[instance.index]; - return index >= 0 && index < instancesLength && instances[index].Equals(instance); - } - return false; - } - - public bool IsFreeInstanceHandle(InstanceHandle instance) - { - return instance.valid && (instance.index >= m_InstanceIndices.Length || m_InstanceIndices[instance.index] == k_InvalidIndex); - } - - public bool IsValidIndex(int index) - { - if (index >= 0 && index < instancesLength) - { - InstanceHandle instance = instances[index]; - return index == m_InstanceIndices[instance.index]; - } - return false; - } - - public int GetFreeInstancesCount() - { - return instancesCapacity - instancesLength; - } - - public void EnsureFreeInstances(int instancesCount) - { - int freeInstancesCount = GetFreeInstancesCount(); - int needInstances = instancesCount - freeInstancesCount; - - if (needInstances > 0) - Grow(instancesCapacity + needInstances + 256); - } - - public void AddNoGrow(InstanceHandle instance) - { - Assert.IsTrue(instance.valid); - Assert.IsTrue(IsFreeInstanceHandle(instance)); - Assert.IsTrue(GetFreeInstancesCount() > 0); - - AddUnsafe(instance); - SetDefault(instance); - } - - public void Add(InstanceHandle instance) - { - EnsureFreeInstances(1); - AddNoGrow(instance); - } - - public void Remove(InstanceHandle instance) - { - Assert.IsTrue(IsValidInstance(instance)); - - int index = InstanceToIndex(instance); - int lastIndex = instancesLength - 1; - - instances[index] = instances[lastIndex]; - sharedInstances[index] = sharedInstances[lastIndex]; - localToWorldIsFlippedBits.Set(index, localToWorldIsFlippedBits.Get(lastIndex)); - worldAABBs[index] = worldAABBs[lastIndex]; - tetrahedronCacheIndices[index] = tetrahedronCacheIndices[lastIndex]; - movedInCurrentFrameBits.Set(index, movedInCurrentFrameBits.Get(lastIndex)); - movedInPreviousFrameBits.Set(index, movedInPreviousFrameBits.Get(lastIndex)); - visibleInPreviousFrameBits.Set(index, visibleInPreviousFrameBits.Get(lastIndex)); - editorData.Remove(index, lastIndex); - meshLodData[index] = meshLodData[lastIndex]; - - m_InstanceIndices[instances[lastIndex].index] = index; - m_InstanceIndices[instance.index] = k_InvalidIndex; - instancesLength -= 1; - } - - public void Set(InstanceHandle instance, SharedInstanceHandle sharedInstance, bool localToWorldIsFlipped, in AABB worldAABB, int tetrahedronCacheIndex, - bool movedInCurrentFrame, bool movedInPreviousFrame, bool visibleInPreviousFrame, in GPUDrivenRendererMeshLodData meshLod) - { - int index = InstanceToIndex(instance); - sharedInstances[index] = sharedInstance; - localToWorldIsFlippedBits.Set(index, localToWorldIsFlipped); - worldAABBs[index] = worldAABB; - tetrahedronCacheIndices[index] = tetrahedronCacheIndex; - movedInCurrentFrameBits.Set(index, movedInCurrentFrame); - movedInPreviousFrameBits.Set(index, movedInPreviousFrame); - visibleInPreviousFrameBits.Set(index, visibleInPreviousFrame); - editorData.SetDefault(index); - meshLodData[index] = meshLod; - } - - public void SetDefault(InstanceHandle instance) - { - Set(instance, SharedInstanceHandle.Invalid, false, new AABB(), k_InvalidIndex, false, false, false, new GPUDrivenRendererMeshLodData()); - } - - // These accessors just for convenience and additional safety. - // In general prefer converting an instance to an index and access by index. - public SharedInstanceHandle Get_SharedInstance(InstanceHandle instance) { return sharedInstances[InstanceToIndex(instance)]; } - public bool Get_LocalToWorldIsFlipped(InstanceHandle instance) { return localToWorldIsFlippedBits.Get(InstanceToIndex(instance)); } - public AABB Get_WorldAABB(InstanceHandle instance) { return worldAABBs[InstanceToIndex(instance)]; } - public int Get_TetrahedronCacheIndex(InstanceHandle instance) { return tetrahedronCacheIndices[InstanceToIndex(instance)]; } - public unsafe ref AABB Get_WorldBounds(InstanceHandle instance) { return ref UnsafeUtility.ArrayElementAsRef(worldAABBs.GetUnsafePtr(), InstanceToIndex(instance)); } - public bool Get_MovedInCurrentFrame(InstanceHandle instance) { return movedInCurrentFrameBits.Get(InstanceToIndex(instance)); } - public bool Get_MovedInPreviousFrame(InstanceHandle instance) { return movedInPreviousFrameBits.Get(InstanceToIndex(instance)); } - public bool Get_VisibleInPreviousFrame(InstanceHandle instance) { return visibleInPreviousFrameBits.Get(InstanceToIndex(instance)); } - public GPUDrivenRendererMeshLodData Get_MeshLodData(InstanceHandle instance) { return meshLodData[InstanceToIndex(instance)]; } - - public void Set_SharedInstance(InstanceHandle instance, SharedInstanceHandle sharedInstance) { sharedInstances[InstanceToIndex(instance)] = sharedInstance; } - public void Set_LocalToWorldIsFlipped(InstanceHandle instance, bool isFlipped) { localToWorldIsFlippedBits.Set(InstanceToIndex(instance), isFlipped); } - public void Set_WorldAABB(InstanceHandle instance, in AABB worldBounds) { worldAABBs[InstanceToIndex(instance)] = worldBounds; } - public void Set_TetrahedronCacheIndex(InstanceHandle instance, int tetrahedronCacheIndex) { tetrahedronCacheIndices[InstanceToIndex(instance)] = tetrahedronCacheIndex; } - public void Set_MovedInCurrentFrame(InstanceHandle instance, bool movedInCurrentFrame) { movedInCurrentFrameBits.Set(InstanceToIndex(instance), movedInCurrentFrame); } - public void Set_MovedInPreviousFrame(InstanceHandle instance, bool movedInPreviousFrame) { movedInPreviousFrameBits.Set(InstanceToIndex(instance), movedInPreviousFrame); } - public void Set_VisibleInPreviousFrame(InstanceHandle instance, bool visibleInPreviousFrame) { visibleInPreviousFrameBits.Set(InstanceToIndex(instance), visibleInPreviousFrame); } - public void Set_MeshLodData(InstanceHandle instance, GPUDrivenRendererMeshLodData meshLod) { meshLodData[InstanceToIndex(instance)] = meshLod; } - - public ReadOnly AsReadOnly() - { - return new ReadOnly(this); - } - - internal readonly struct ReadOnly - { - public readonly NativeArray.ReadOnly instanceIndices; - public readonly NativeArray.ReadOnly instances; - public readonly NativeArray.ReadOnly sharedInstances; - public readonly ParallelBitArray localToWorldIsFlippedBits; - public readonly NativeArray.ReadOnly worldAABBs; - public readonly NativeArray.ReadOnly tetrahedronCacheIndices; - public readonly ParallelBitArray movedInCurrentFrameBits; - public readonly ParallelBitArray movedInPreviousFrameBits; - public readonly ParallelBitArray visibleInPreviousFrameBits; - public readonly EditorInstanceDataArrays.ReadOnly editorData; - public readonly NativeArray.ReadOnly meshLodData; - public readonly int handlesLength => instanceIndices.Length; - public readonly int instancesLength => instances.Length; - - public ReadOnly(in CPUInstanceData instanceData) - { - instanceIndices = instanceData.m_InstanceIndices.AsArray().AsReadOnly(); - instances = instanceData.instances.GetSubArray(0, instanceData.instancesLength).AsReadOnly(); - sharedInstances = instanceData.sharedInstances.GetSubArray(0, instanceData.instancesLength).AsReadOnly(); - localToWorldIsFlippedBits = instanceData.localToWorldIsFlippedBits.GetSubArray(instanceData.instancesLength); - worldAABBs = instanceData.worldAABBs.GetSubArray(0, instanceData.instancesLength).AsReadOnly(); - tetrahedronCacheIndices = instanceData.tetrahedronCacheIndices.GetSubArray(0, instanceData.instancesLength).AsReadOnly(); - movedInCurrentFrameBits = instanceData.movedInCurrentFrameBits.GetSubArray(instanceData.instancesLength);//.AsReadOnly(); // Implement later. - movedInPreviousFrameBits = instanceData.movedInPreviousFrameBits.GetSubArray(instanceData.instancesLength);//.AsReadOnly(); // Implement later. - visibleInPreviousFrameBits = instanceData.visibleInPreviousFrameBits.GetSubArray(instanceData.instancesLength);//.AsReadOnly(); // Implement later. - editorData = new EditorInstanceDataArrays.ReadOnly(instanceData); - meshLodData = instanceData.meshLodData.GetSubArray(0, instanceData.instancesLength).AsReadOnly(); - } - - public int InstanceToIndex(InstanceHandle instance) - { - Assert.IsTrue(IsValidInstance(instance)); - return instanceIndices[instance.index]; - } - - public InstanceHandle IndexToInstance(int index) - { - Assert.IsTrue(IsValidIndex(index)); - return instances[index]; - } - - public bool IsValidInstance(InstanceHandle instance) - { - if (instance.valid && instance.index < instanceIndices.Length) - { - int index = instanceIndices[instance.index]; - return index >= 0 && index < instances.Length && instances[index].Equals(instance); - } - return false; - } - - public bool IsValidIndex(int index) - { - if (index >= 0 && index < instances.Length) - { - InstanceHandle instance = instances[index]; - return index == instanceIndices[instance.index]; - } - return false; - } - } - } - - internal struct CPUPerCameraInstanceData : IDisposable - { - public const byte k_InvalidByteData = 0xff; - - public NativeParallelHashMap perCameraData; - - private NativeArray m_StructData; - public int instancesLength { get => m_StructData[0]; set => m_StructData[0] = value; } - public int instancesCapacity { get => m_StructData[1]; set => m_StructData[1] = value; } - - public int cameraCount { get { return perCameraData.Count();}} - - internal struct PerCameraInstanceDataArrays : IDisposable - { - internal UnsafeList meshLods; - internal UnsafeList crossFades; - - public bool IsCreated => meshLods.IsCreated && crossFades.IsCreated; - - public PerCameraInstanceDataArrays(int initCapacity) - { - meshLods = new UnsafeList(initCapacity, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); - meshLods.Length = initCapacity; - crossFades = new UnsafeList(initCapacity, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); - crossFades.Length = initCapacity; - } - - public void Dispose() - { - meshLods.Dispose(); - crossFades.Dispose(); - } - - internal void Remove(int index, int lastIndex) - { - meshLods[index] = meshLods[lastIndex]; - crossFades[index] = crossFades[lastIndex]; - } - - internal void Grow(int previousCapacity, int newCapacity) - { - meshLods.Length = newCapacity; - crossFades.Length = newCapacity; - } - - internal void SetDefault(int index) - { - meshLods[index] = k_InvalidByteData; - crossFades[index] = k_InvalidByteData; - } - } - - public void Initialize(int initCapacity) - { - perCameraData = new NativeParallelHashMap(1,Allocator.Persistent); - m_StructData = new NativeArray(2, Allocator.Persistent); - instancesCapacity = initCapacity; - instancesLength = 0; - } - - public void DeallocateCameras(NativeArray cameraIDs) - { - foreach (var cameraID in cameraIDs) - { - if (!perCameraData.TryGetValue(cameraID, out var perCameraInstanceData)) - continue; - - perCameraInstanceData.Dispose(); - perCameraData.Remove(cameraID); - } - } - - public void AllocateCameras(NativeArray cameraIDs) - { - foreach (var cameraID in cameraIDs) - { - if (perCameraData.TryGetValue(cameraID, out var cameraInstanceData)) - continue; - - cameraInstanceData = new PerCameraInstanceDataArrays(instancesCapacity); - perCameraData.Add(cameraID, cameraInstanceData); - } - } - - public void Remove(int index) - { - int lastIndex = instancesLength - 1; - - foreach (var pair in perCameraData) - { - pair.Value.Remove(index, lastIndex); - } - - instancesLength -= 1; - } - - public void IncreaseInstanceCount() - { - instancesLength++; - } - - public void Dispose() - { - foreach (var pair in perCameraData) - { - pair.Value.Dispose(); - } - - m_StructData.Dispose(); - perCameraData.Dispose(); - } - - internal void Grow(int newCapacity) - { - if(newCapacity < instancesCapacity) - return; - - var previousCapacity = instancesCapacity; - instancesCapacity = newCapacity; - - foreach (var pair in perCameraData) - { - pair.Value.Grow(previousCapacity, instancesCapacity); - } - } - - public void SetDefault(int index) - { - foreach (var pair in perCameraData) - { - pair.Value.SetDefault(index); - } - } - - } - - internal struct CPUSharedInstanceData : IDisposable - { - private const int k_InvalidIndex = -1; - private const uint k_InvalidLODGroupAndMask = 0xFFFFFFFF; - - private NativeArray m_StructData; - private NativeList m_InstanceIndices; - - //@ Need to figure out the way to share the code with CPUInstanceData. Both structures are almost identical. - public NativeArray instances; - public NativeArray rendererGroupIDs; - - // For now we just use nested collections since materialIDs are only parsed rarely. E.g. when an unsupported material is detected. - public NativeArray materialIDArrays; - - public NativeArray meshIDs; - public NativeArray localAABBs; - public NativeArray flags; - public NativeArray lodGroupAndMasks; - public NativeArray meshLodInfos; - public NativeArray gameObjectLayers; - public NativeArray refCounts; - - public int instancesLength { get => m_StructData[0]; set => m_StructData[0] = value; } - public int instancesCapacity { get => m_StructData[1]; set => m_StructData[1] = value; } - public int handlesLength => m_InstanceIndices.Length; - - public void Initialize(int initCapacity) - { - m_StructData = new NativeArray(2, Allocator.Persistent); - instancesCapacity = initCapacity; - m_InstanceIndices = new NativeList(Allocator.Persistent); - instances = new NativeArray(instancesCapacity, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); - instances.FillArray(SharedInstanceHandle.Invalid); - rendererGroupIDs = new NativeArray(instancesCapacity, Allocator.Persistent); - materialIDArrays = new NativeArray(instancesCapacity, Allocator.Persistent); - meshIDs = new NativeArray(instancesCapacity, Allocator.Persistent); - localAABBs = new NativeArray(instancesCapacity, Allocator.Persistent); - flags = new NativeArray(instancesCapacity, Allocator.Persistent); - lodGroupAndMasks = new NativeArray(instancesCapacity, Allocator.Persistent); - lodGroupAndMasks.FillArray(k_InvalidLODGroupAndMask); - meshLodInfos = new NativeArray(instancesCapacity, Allocator.Persistent); - gameObjectLayers = new NativeArray(instancesCapacity, Allocator.Persistent); - refCounts = new NativeArray(instancesCapacity, Allocator.Persistent); - } - - public void Dispose() - { - m_StructData.Dispose(); - m_InstanceIndices.Dispose(); - instances.Dispose(); - rendererGroupIDs.Dispose(); - - foreach (var materialIDs in materialIDArrays) - { - materialIDs.Dispose(); - } - materialIDArrays.Dispose(); - - meshIDs.Dispose(); - localAABBs.Dispose(); - flags.Dispose(); - lodGroupAndMasks.Dispose(); - meshLodInfos.Dispose(); - gameObjectLayers.Dispose(); - refCounts.Dispose(); - } - - private void Grow(int newCapacity) - { - Assert.IsTrue(newCapacity > instancesCapacity); - - instances.ResizeArray(newCapacity); - instances.FillArray(SharedInstanceHandle.Invalid, instancesCapacity); - rendererGroupIDs.ResizeArray(newCapacity); - materialIDArrays.ResizeArray(newCapacity); - materialIDArrays.FillArray(default, instancesCapacity); - meshIDs.ResizeArray(newCapacity); - localAABBs.ResizeArray(newCapacity); - flags.ResizeArray(newCapacity); - lodGroupAndMasks.ResizeArray(newCapacity); - lodGroupAndMasks.FillArray(k_InvalidLODGroupAndMask, instancesCapacity); - meshLodInfos.ResizeArray(newCapacity); - gameObjectLayers.ResizeArray(newCapacity); - refCounts.ResizeArray(newCapacity); - - instancesCapacity = newCapacity; - } - - private void AddUnsafe(SharedInstanceHandle instance) - { - if (instance.index >= m_InstanceIndices.Length) - { - int prevLength = m_InstanceIndices.Length; - m_InstanceIndices.ResizeUninitialized(instance.index + 1); - - for (int i = prevLength; i < m_InstanceIndices.Length - 1; ++i) - m_InstanceIndices[i] = k_InvalidIndex; - } - - m_InstanceIndices[instance.index] = instancesLength; - instances[instancesLength] = instance; - - ++instancesLength; - } - - public int SharedInstanceToIndex(SharedInstanceHandle instance) - { - Assert.IsTrue(IsValidInstance(instance)); - return m_InstanceIndices[instance.index]; - } - - public SharedInstanceHandle IndexToSharedInstance(int index) - { - Assert.IsTrue(IsValidIndex(index)); - return instances[index]; - } - - public int InstanceToIndex(in CPUInstanceData instanceData, InstanceHandle instance) - { - int instanceIndex = instanceData.InstanceToIndex(instance); - SharedInstanceHandle sharedInstance = instanceData.sharedInstances[instanceIndex]; - int sharedInstanceIndex = SharedInstanceToIndex(sharedInstance); - return sharedInstanceIndex; - } - - public bool IsValidInstance(SharedInstanceHandle instance) - { - if (instance.valid && instance.index < m_InstanceIndices.Length) - { - int index = m_InstanceIndices[instance.index]; - return index >= 0 && index < instancesLength && instances[index].Equals(instance); - } - return false; - } - - public bool IsFreeInstanceHandle(SharedInstanceHandle instance) - { - return instance.valid && (instance.index >= m_InstanceIndices.Length || m_InstanceIndices[instance.index] == k_InvalidIndex); - } - - public bool IsValidIndex(int index) - { - if (index >= 0 && index < instancesLength) - { - SharedInstanceHandle instance = instances[index]; - return index == m_InstanceIndices[instance.index]; - } - return false; - } - - public int GetFreeInstancesCount() - { - return instancesCapacity - instancesLength; - } - - public void EnsureFreeInstances(int instancesCount) - { - int freeInstancesCount = GetFreeInstancesCount(); - int needInstances = instancesCount - freeInstancesCount; - - if (needInstances > 0) - Grow(instancesCapacity + needInstances + 256); - } - - public void AddNoGrow(SharedInstanceHandle instance) - { - Assert.IsTrue(instance.valid); - Assert.IsTrue(IsFreeInstanceHandle(instance)); - Assert.IsTrue(GetFreeInstancesCount() > 0); - - AddUnsafe(instance); - SetDefault(instance); - } - - public void Add(SharedInstanceHandle instance) - { - EnsureFreeInstances(1); - AddNoGrow(instance); - } - - public void Remove(SharedInstanceHandle instance) - { - Assert.IsTrue(IsValidInstance(instance)); - - int index = SharedInstanceToIndex(instance); - int lastIndex = instancesLength - 1; - - instances[index] = instances[lastIndex]; - rendererGroupIDs[index] = rendererGroupIDs[lastIndex]; - - materialIDArrays[index].Dispose(); - materialIDArrays[index] = materialIDArrays[lastIndex]; - materialIDArrays[lastIndex] = default; - - meshIDs[index] = meshIDs[lastIndex]; - localAABBs[index] = localAABBs[lastIndex]; - flags[index] = flags[lastIndex]; - lodGroupAndMasks[index] = lodGroupAndMasks[lastIndex]; - meshLodInfos[index] = meshLodInfos[lastIndex]; - gameObjectLayers[index] = gameObjectLayers[lastIndex]; - refCounts[index] = refCounts[lastIndex]; - - m_InstanceIndices[instances[lastIndex].index] = index; - m_InstanceIndices[instance.index] = k_InvalidIndex; - instancesLength -= 1; - } - - // These accessors just for convenience and additional safety. - // In general prefer converting an instance to an index and access by index. - public EntityId Get_RendererGroupID(SharedInstanceHandle instance) { return rendererGroupIDs[SharedInstanceToIndex(instance)]; } - public EntityId Get_MeshID(SharedInstanceHandle instance) { return meshIDs[SharedInstanceToIndex(instance)]; } - public unsafe ref AABB Get_LocalAABB(SharedInstanceHandle instance) { return ref UnsafeUtility.ArrayElementAsRef(localAABBs.GetUnsafePtr(), SharedInstanceToIndex(instance)); } - public CPUSharedInstanceFlags Get_Flags(SharedInstanceHandle instance) { return flags[SharedInstanceToIndex(instance)]; } - public uint Get_LODGroupAndMask(SharedInstanceHandle instance) { return lodGroupAndMasks[SharedInstanceToIndex(instance)]; } - public int Get_GameObjectLayer(SharedInstanceHandle instance) { return gameObjectLayers[SharedInstanceToIndex(instance)]; } - public int Get_RefCount(SharedInstanceHandle instance) { return refCounts[SharedInstanceToIndex(instance)]; } - public unsafe ref SmallEntityIdArray Get_MaterialIDs(SharedInstanceHandle instance) { return ref UnsafeUtility.ArrayElementAsRef(materialIDArrays.GetUnsafePtr(), SharedInstanceToIndex(instance)); } - - public void Set_RendererGroupID(SharedInstanceHandle instance, EntityId rendererGroupID) { rendererGroupIDs[SharedInstanceToIndex(instance)] = rendererGroupID; } - public void Set_MeshID(SharedInstanceHandle instance, EntityId meshID) { meshIDs[SharedInstanceToIndex(instance)] = meshID; } - public void Set_LocalAABB(SharedInstanceHandle instance, in AABB localAABB) { localAABBs[SharedInstanceToIndex(instance)] = localAABB; } - public void Set_Flags(SharedInstanceHandle instance, CPUSharedInstanceFlags instanceFlags) { flags[SharedInstanceToIndex(instance)] = instanceFlags; } - public void Set_LODGroupAndMask(SharedInstanceHandle instance, uint lodGroupAndMask) { lodGroupAndMasks[SharedInstanceToIndex(instance)] = lodGroupAndMask; } - public void Set_GameObjectLayer(SharedInstanceHandle instance, int gameObjectLayer) { gameObjectLayers[SharedInstanceToIndex(instance)] = gameObjectLayer; } - public void Set_RefCount(SharedInstanceHandle instance, int refCount) { refCounts[SharedInstanceToIndex(instance)] = refCount; } - public void Set_MaterialIDs(SharedInstanceHandle instance, in SmallEntityIdArray materialIDs) - { - int index = SharedInstanceToIndex(instance); - materialIDArrays[index].Dispose(); - materialIDArrays[index] = materialIDs; - } - - public void Set(SharedInstanceHandle instance, EntityId rendererGroupID, in SmallEntityIdArray materialIDs, EntityId meshID, in AABB localAABB, TransformUpdateFlags transformUpdateFlags, - InstanceFlags instanceFlags, uint lodGroupAndMask, GPUDrivenMeshLodInfo meshLodInfo, int gameObjectLayer, int refCount) - { - int index = SharedInstanceToIndex(instance); - - rendererGroupIDs[index] = rendererGroupID; - materialIDArrays[index].Dispose(); - materialIDArrays[index] = materialIDs; - meshIDs[index] = meshID; - localAABBs[index] = localAABB; - flags[index] = new CPUSharedInstanceFlags { transformUpdateFlags = transformUpdateFlags, instanceFlags = instanceFlags }; - lodGroupAndMasks[index] = lodGroupAndMask; - meshLodInfos[index] = meshLodInfo; - gameObjectLayers[index] = gameObjectLayer; - refCounts[index] = refCount; - } - - public void SetDefault(SharedInstanceHandle instance) - { - Set(instance, EntityId.None, default, EntityId.None, new AABB(), TransformUpdateFlags.None, InstanceFlags.None, k_InvalidLODGroupAndMask, new GPUDrivenMeshLodInfo(), 0, 0); - } - - public ReadOnly AsReadOnly() - { - return new ReadOnly(this); - } - - internal readonly struct ReadOnly - { - public readonly NativeArray.ReadOnly instanceIndices; - public readonly NativeArray.ReadOnly instances; - public readonly NativeArray.ReadOnly rendererGroupIDs; - public readonly NativeArray.ReadOnly materialIDArrays; - public readonly NativeArray.ReadOnly meshIDs; - public readonly NativeArray.ReadOnly localAABBs; - public readonly NativeArray.ReadOnly flags; - public readonly NativeArray.ReadOnly lodGroupAndMasks; - public readonly NativeArray.ReadOnly meshLodInfos; - public readonly NativeArray.ReadOnly gameObjectLayers; - public readonly NativeArray.ReadOnly refCounts; - public readonly int handlesLength => instanceIndices.Length; - public readonly int instancesLength => instances.Length; - - public ReadOnly(in CPUSharedInstanceData instanceData) - { - instanceIndices = instanceData.m_InstanceIndices.AsArray().AsReadOnly(); - instances = instanceData.instances.GetSubArray(0, instanceData.instancesLength).AsReadOnly(); - rendererGroupIDs = instanceData.rendererGroupIDs.GetSubArray(0, instanceData.instancesLength).AsReadOnly(); - materialIDArrays = instanceData.materialIDArrays.GetSubArray(0, instanceData.instancesLength).AsReadOnly(); - meshIDs = instanceData.meshIDs.GetSubArray(0, instanceData.instancesLength).AsReadOnly(); - localAABBs = instanceData.localAABBs.GetSubArray(0, instanceData.instancesLength).AsReadOnly(); - flags = instanceData.flags.GetSubArray(0, instanceData.instancesLength).AsReadOnly(); - lodGroupAndMasks = instanceData.lodGroupAndMasks.GetSubArray(0, instanceData.instancesLength).AsReadOnly(); - meshLodInfos = instanceData.meshLodInfos.GetSubArray(0, instanceData.instancesLength).AsReadOnly(); - gameObjectLayers = instanceData.gameObjectLayers.GetSubArray(0, instanceData.instancesLength).AsReadOnly(); - refCounts = instanceData.refCounts.GetSubArray(0, instanceData.instancesLength).AsReadOnly(); - } - - public int SharedInstanceToIndex(SharedInstanceHandle instance) - { - Assert.IsTrue(IsValidSharedInstance(instance)); - return instanceIndices[instance.index]; - } - - public SharedInstanceHandle IndexToSharedInstance(int index) - { - Assert.IsTrue(IsValidIndex(index)); - return instances[index]; - } - - public bool IsValidSharedInstance(SharedInstanceHandle instance) - { - if (instance.valid && instance.index < instanceIndices.Length) - { - int index = instanceIndices[instance.index]; - return index >= 0 && index < instances.Length && instances[index].Equals(instance); - } - return false; - } - - public bool IsValidIndex(int index) - { - if (index >= 0 && index < instances.Length) - { - SharedInstanceHandle instance = instances[index]; - return index == instanceIndices[instance.index]; - } - return false; - } - - public int InstanceToIndex(in CPUInstanceData.ReadOnly instanceData, InstanceHandle instance) - { - int instanceIndex = instanceData.InstanceToIndex(instance); - SharedInstanceHandle sharedInstance = instanceData.sharedInstances[instanceIndex]; - int sharedInstanceIndex = SharedInstanceToIndex(sharedInstance); - return sharedInstanceIndex; - } - } - } - - internal unsafe struct SmallEntityIdArray : IDisposable - { - private FixedList32Bytes m_FixedArray; - private UnsafeList m_List; - private readonly bool m_IsEmbedded; - - public bool Valid { get; private set; } - public readonly int Length; - - public SmallEntityIdArray(int length, Allocator allocator) - { - m_FixedArray = default; - m_List = default; - Length = length; - Valid = true; - - if (Length <= m_FixedArray.Capacity) - { - m_FixedArray = new FixedList32Bytes(); - m_FixedArray.Length = Length; - m_IsEmbedded = true; - } - else - { - m_List = new UnsafeList(Length, allocator, NativeArrayOptions.UninitializedMemory); - m_List.Resize(Length); - m_IsEmbedded = false; - } - } - - public EntityId this[int index] - { - get - { - Assert.IsTrue(Valid && index < Length); - - if (m_IsEmbedded) - return m_FixedArray[index]; - else - return m_List[index]; - } - set - { - Assert.IsTrue(Valid && index < Length); - - if (m_IsEmbedded) - m_FixedArray[index] = value; - else - m_List[index] = value; - } - } - - public unsafe void Dispose() - { - if (!Valid) - return; - m_List.Dispose(); - Valid = false; - } - } - - internal interface IDataArrays - { - void Initialize(int initCapacity); - void Dispose(); - void Grow(int newCapacity); - void Remove(int index, int lastIndex); - void SetDefault(int index); - } - - internal struct EditorInstanceDataArrays : IDataArrays - { -#if UNITY_EDITOR - public NativeArray sceneCullingMasks; - public ParallelBitArray selectedBits; - - public void Initialize(int initCapacity) - { - sceneCullingMasks = new NativeArray(initCapacity, Allocator.Persistent, NativeArrayOptions.UninitializedMemory); - sceneCullingMasks.FillArray(ulong.MaxValue); - selectedBits = new ParallelBitArray(initCapacity, Allocator.Persistent); - } - - public void Dispose() - { - sceneCullingMasks.Dispose(); - selectedBits.Dispose(); - } - - public void Grow(int newCapacity) - { - sceneCullingMasks.ResizeArray(newCapacity); - selectedBits.Resize(newCapacity); - } - - public void Remove(int index, int lastIndex) - { - sceneCullingMasks[index] = sceneCullingMasks[lastIndex]; - selectedBits.Set(index, selectedBits.Get(lastIndex)); - } - - public void SetDefault(int index) - { - sceneCullingMasks[index] = ulong.MaxValue; - selectedBits.Set(index, false); - } - - internal readonly struct ReadOnly - { - public readonly NativeArray.ReadOnly sceneCullingMasks; - public readonly ParallelBitArray selectedBits; - - public ReadOnly(in CPUInstanceData instanceData) - { - sceneCullingMasks = instanceData.editorData.sceneCullingMasks.GetSubArray(0, instanceData.instancesLength).AsReadOnly(); - selectedBits = instanceData.editorData.selectedBits.GetSubArray(instanceData.instancesLength); - } - } -#else - public void Initialize(int initCapacity) { } - public void Dispose() { } - public void Grow(int newCapacity) { } - public void Remove(int index, int lastIndex) { } - public void SetDefault(int index) { } - internal readonly struct ReadOnly { public ReadOnly(in CPUInstanceData instanceData) { } } -#endif - } - - [Flags] - internal enum TransformUpdateFlags : byte - { - None = 0, - HasLightProbeCombined = 1 << 0, - IsPartOfStaticBatch = 1 << 1 - } - - [Flags] - internal enum InstanceFlags : byte - { - None = 0, - AffectsLightmaps = 1 << 0, // either lightmapped or influence-only - IsShadowsOff = 1 << 1, // shadow casting mode is ShadowCastingMode.Off - IsShadowsOnly = 1 << 2, // shadow casting mode is ShadowCastingMode.ShadowsOnly - HasMeshLod = 1 << 3, - SmallMeshCulling = 1 << 4 - } - - internal struct CPUSharedInstanceFlags - { - public TransformUpdateFlags transformUpdateFlags; - public InstanceFlags instanceFlags; - } - - internal struct PackedMatrix - { - /* mat4x3 packed like this: - p1.x, p1.w, p2.z, p3.y, - p1.y, p2.x, p2.w, p3.z, - p1.z, p2.y, p3.x, p3.w, - 0.0, 0.0, 0.0, 1.0 - */ - - public float4 packed0; - public float4 packed1; - public float4 packed2; - - public static PackedMatrix FromMatrix4x4(in Matrix4x4 m) - { - return new PackedMatrix - { - packed0 = new float4(m.m00, m.m10, m.m20, m.m01), - packed1 = new float4(m.m11, m.m21, m.m02, m.m12), - packed2 = new float4(m.m22, m.m03, m.m13, m.m23) - }; - } - - public static PackedMatrix FromFloat4x4(in float4x4 m) - { - return new PackedMatrix - { - packed0 = new float4(m.c0.x, m.c0.y, m.c0.z, m.c1.x), - packed1 = new float4(m.c1.y, m.c1.z, m.c2.x, m.c2.y), - packed2 = new float4(m.c2.z, m.c3.x, m.c3.y, m.c3.z) - }; - } - } -} - -// +-------------+ -// | Instance | -// | Handle 2 | -// +------^------+ -// +-------------+ | +-------------+ -// | Instance | | | Instance | -// | Handle 0 | | | Handle 3 | -// +------^------+ | +---^---------+ -// | | / -// | | / -// +-----------------------------------------------------------------------------------------------+ -// | | | / | -// | +-v-- ----+--v - +---v---+----+----+----+ | -// | InstanceIndices | 0 |free| 1 | 2 |free|free|free|free|... | -// | +--^-+----+--^-+--^-+----+----+----+----+ | -// | | / / | -// | | / / +------------------------- | -// | | / / | +----------------------- | -// | | / / | | | -// | +--v-+--v-+--v-+--v-+--v-+----+ | -// | Instances | 0 | 2 | 3 | | |... | | -// | +----+----+----+----+----+----+ | -// CPUInstanceData | LocalToWorldMatrices | | | | | |... | | -// | +----+----+----+----+----+----+ | -// | WorldBoundses | | | | | |... | | -// | +----+----+----+----+----+----+ | -// | SharedInstanceHandles | | | | | |... | | -// | +----+----+----+----+----+----+ | -// | MovedInCurrentFrameBits | | | | | | ...| | -// | +----+----+----+----+----+----+ | -// | SharedInstances | 1 | 1 | 1 | 2 | 3 | ...| | -// | +-\--+--|-+--/-+--/-+--/-+----+ | -// | | | / / / | -// +-----------------------------------------------------------------------------------------------+ -// \ / | | | -// \ | / / / -// +-----------------------------------------------------------------------------------------------+ -// | \|/ / / | -// | +----+-v--+-v--+-v--+----+----+----+----+ | -// | SharedInstanceIndices |free| 0 | 1 | 2 |free|free|free|free|... | -// | +----+-|--+--|-+--|-+----+----+----+----+ | -// | / / / | -// | / / / | -// | / / / | -// | | | | | -// | / / / | -// | +-v--+--v-+--v-+----+----+----+ | -// CPUSharedInstanceData | MeshIDs | | | |... |... |... | | -// | +----+----+----+----+----+----+ | -// | LocalBoundses | | | |... |... | ...| | -// | +----+----+----+----+----+----+ | -// | RendererGroupIDs | | | | ...| ...| ...| | -// | +----+----+----+----+----+----+ | -// | GameObjectLayer | | | |... |... | ...| | -// | +----+----+----+----+----+----+ | -// | Flags | | | |... |... | ...| | -// | +----+----+----+----+----+----+ | -// | RefCounts | 3 | 1 | 1 |... |... | ...| | -// | +----+----+----+----+----+----+ | -// +-----------------------------------------------------------------------------------------------+ diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceDataSystem.Jobs.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceDataSystem.Jobs.cs deleted file mode 100644 index 311b24f38a4..00000000000 --- a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceDataSystem.Jobs.cs +++ /dev/null @@ -1,776 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading; -using Unity.Burst; -using Unity.Collections; -using Unity.Collections.LowLevel.Unsafe; -using Unity.Jobs; -using Unity.Mathematics; -using Unity.Profiling; -using UnityEngine.Assertions; -using UnityEngine.Profiling; - -namespace UnityEngine.Rendering -{ - internal partial class InstanceDataSystem : IDisposable - { - private unsafe static int AtomicAddLengthNoResize(in NativeList list, int count) where T : unmanaged - { - UnsafeList* unsafeList = list.GetUnsafeList(); - var newLength = Interlocked.Add(ref unsafeList->m_length, count); - Assert.IsTrue(unsafeList->Capacity >= newLength); - return newLength - count; - } - - [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] - private unsafe struct QueryRendererGroupInstancesCountJob : IJobParallelForBatch - { - public const int k_BatchSize = 128; - - [ReadOnly] public CPUInstanceData instanceData; - [ReadOnly] public CPUSharedInstanceData sharedInstanceData; - [ReadOnly] public NativeParallelMultiHashMap rendererGroupInstanceMultiHash; - [NativeDisableContainerSafetyRestriction, NoAlias][ReadOnly] public NativeArray rendererGroupIDs; - - [NativeDisableContainerSafetyRestriction, NoAlias][WriteOnly] public NativeArray instancesCount; - - public void Execute(int startIndex, int count) - { - for (int i = startIndex; i < startIndex + count; ++i) - { - var rendererGroupID = rendererGroupIDs[i]; - - if (rendererGroupInstanceMultiHash.TryGetFirstValue(rendererGroupID, out var instance, out var it)) - { - var sharedInstance = instanceData.Get_SharedInstance(instance); - var refCount = sharedInstanceData.Get_RefCount(sharedInstance); - instancesCount[i] = refCount; - } - else - { - instancesCount[i] = 0; - } - } - } - } - - [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] - private unsafe struct ComputeInstancesOffsetAndResizeInstancesArrayJob : IJob - { - [ReadOnly] public NativeArray instancesCount; - [WriteOnly] public NativeArray instancesOffset; - public NativeList instances; - - public void Execute() - { - int totalInstancesCount = 0; - - for (int i = 0; i < instancesCount.Length; ++i) - { - instancesOffset[i] = totalInstancesCount; - totalInstancesCount += instancesCount[i]; - } - - instances.ResizeUninitialized(totalInstancesCount); - } - } - - [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] - private unsafe struct QueryRendererGroupInstancesJob : IJobParallelForBatch - { - public const int k_BatchSize = 128; - - [ReadOnly] public NativeParallelMultiHashMap rendererGroupInstanceMultiHash; - [NativeDisableContainerSafetyRestriction, NoAlias][ReadOnly] public NativeArray rendererGroupIDs; - - [NativeDisableContainerSafetyRestriction, NoAlias][WriteOnly] public NativeArray instances; - [NativeDisableUnsafePtrRestriction] public UnsafeAtomicCounter32 atomicNonFoundInstancesCount; - - public void Execute(int startIndex, int count) - { - int newInstancesCountJob = 0; - - for (int i = startIndex; i < startIndex + count; ++i) - { - if (rendererGroupInstanceMultiHash.TryGetFirstValue(rendererGroupIDs[i], out var instance, out var it)) - { - instances[i] = instance; - } - else - { - newInstancesCountJob += 1; - instances[i] = InstanceHandle.Invalid; - } - } - - if (atomicNonFoundInstancesCount.Counter != null && newInstancesCountJob > 0) - atomicNonFoundInstancesCount.Add(newInstancesCountJob); - } - } - - [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] - private unsafe struct QueryRendererGroupInstancesMultiJob : IJobParallelForBatch - { - public const int k_BatchSize = 128; - - [ReadOnly] public NativeParallelMultiHashMap rendererGroupInstanceMultiHash; - [NativeDisableContainerSafetyRestriction, NoAlias][ReadOnly] public NativeArray rendererGroupIDs; - [NativeDisableContainerSafetyRestriction, NoAlias][ReadOnly] public NativeArray instancesOffsets; - [NativeDisableContainerSafetyRestriction, NoAlias][ReadOnly] public NativeArray instancesCounts; - - [NativeDisableContainerSafetyRestriction, NoAlias][WriteOnly] public NativeArray instances; - [NativeDisableUnsafePtrRestriction] public UnsafeAtomicCounter32 atomicNonFoundSharedInstancesCount; - [NativeDisableUnsafePtrRestriction] public UnsafeAtomicCounter32 atomicNonFoundInstancesCount; - - public void Execute(int startIndex, int count) - { - int newSharedInstancesCountJob = 0; - int newInstancesCountJob = 0; - - for (int i = startIndex; i < startIndex + count; ++i) - { - var rendererGroupID = rendererGroupIDs[i]; - int instancesOffset = instancesOffsets[i]; - int instancesCount = instancesCounts[i]; - - bool success = rendererGroupInstanceMultiHash.TryGetFirstValue(rendererGroupID, out var storedInstance, out var it); - - if (!success) - newSharedInstancesCountJob += 1; - - for (int j = 0; j < instancesCount; ++j) - { - int index = instancesOffset + j; - - if (success) - { - instances[index] = storedInstance; - success = rendererGroupInstanceMultiHash.TryGetNextValue(out storedInstance, ref it); - } - else - { - newInstancesCountJob += 1; - instances[index] = InstanceHandle.Invalid; - } - } - } - - if (atomicNonFoundSharedInstancesCount.Counter != null && newSharedInstancesCountJob > 0) - atomicNonFoundSharedInstancesCount.Add(newSharedInstancesCountJob); - - if (atomicNonFoundInstancesCount.Counter != null && newInstancesCountJob > 0) - atomicNonFoundInstancesCount.Add(newInstancesCountJob); - } - } - - [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] - private struct QuerySortedMeshInstancesJob : IJobParallelForBatch - { - public const int k_BatchSize = 64; - - [ReadOnly] public CPUInstanceData instanceData; - [ReadOnly] public CPUSharedInstanceData sharedInstanceData; - [ReadOnly] public NativeArray sortedMeshID; - - [NativeDisableParallelForRestriction][WriteOnly] public NativeList instances; - - public void Execute(int startIndex, int count) - { - ulong validBits = 0; - - for (int i = 0; i < count; ++i) - { - int instanceIndex = startIndex + i; - InstanceHandle instance = instanceData.instances[instanceIndex]; - Assert.IsTrue(instanceData.IsValidInstance(instance)); - SharedInstanceHandle sharedInstance = instanceData.sharedInstances[instanceIndex]; - var meshID = sharedInstanceData.Get_MeshID(sharedInstance); - - if (sortedMeshID.BinarySearch(meshID) >= 0) - validBits |= 1ul << i; - } - - int validBitCount = math.countbits(validBits); - - if (validBitCount > 0) - { - int writeIndex = AtomicAddLengthNoResize(instances, validBitCount); - int validBitIndex = math.tzcnt(validBits); - - while (validBits != 0) - { - int instanceIndex = startIndex + validBitIndex; - instances[writeIndex] = instanceData.instances[instanceIndex]; - - writeIndex += 1; - validBits &= ~(1ul << validBitIndex); - validBitIndex = math.tzcnt(validBits); - } - } - } - } - - [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] - private struct CalculateInterpolatedLightAndOcclusionProbesBatchJob : IJobParallelFor - { - public const int k_BatchSize = 1; - public const int k_CalculatedProbesPerBatch = 8; - - [ReadOnly] public int probesCount; - [ReadOnly] public LightProbesQuery lightProbesQuery; - - [NativeDisableParallelForRestriction][ReadOnly] public NativeArray queryPostitions; - [NativeDisableParallelForRestriction] public NativeArray compactTetrahedronCache; - [NativeDisableParallelForRestriction][WriteOnly] public NativeArray probesSphericalHarmonics; - [NativeDisableParallelForRestriction][WriteOnly] public NativeArray probesOcclusion; - - public void Execute(int index) - { - var startIndex = index * k_CalculatedProbesPerBatch; - var endIndex = math.min(probesCount, startIndex + k_CalculatedProbesPerBatch); - var count = endIndex - startIndex; - - var compactTetrahedronCacheSubArray = compactTetrahedronCache.GetSubArray(startIndex, count); - var queryPostitionsSubArray = queryPostitions.GetSubArray(startIndex, count); - var probesSphericalHarmonicsSubArray = probesSphericalHarmonics.GetSubArray(startIndex, count); - var probesOcclusionSubArray = probesOcclusion.GetSubArray(startIndex, count); - lightProbesQuery.CalculateInterpolatedLightAndOcclusionProbes(queryPostitionsSubArray, compactTetrahedronCacheSubArray, probesSphericalHarmonicsSubArray, probesOcclusionSubArray); - } - } - - [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] - private struct ScatterTetrahedronCacheIndicesJob : IJobParallelFor - { - public const int k_BatchSize = 128; - - [ReadOnly] public NativeArray probeInstances; - [ReadOnly] public NativeArray compactTetrahedronCache; - - [NativeDisableContainerSafetyRestriction, NoAlias][NativeDisableParallelForRestriction] public CPUInstanceData instanceData; - - public void Execute(int index) - { - InstanceHandle instance = probeInstances[index]; - instanceData.Set_TetrahedronCacheIndex(instance, compactTetrahedronCache[index]); - } - } - - [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] - private unsafe struct TransformUpdateJob : IJobParallelForBatch - { - public const int k_BatchSize = 64; - - [ReadOnly] public bool initialize; - [ReadOnly] public bool enableBoundingSpheres; - [ReadOnly] public NativeArray instances; - [ReadOnly] public NativeArray localToWorldMatrices; - [ReadOnly] public NativeArray prevLocalToWorldMatrices; - - [NativeDisableUnsafePtrRestriction] public UnsafeAtomicCounter32 atomicTransformQueueCount; - - [NativeDisableParallelForRestriction] public CPUSharedInstanceData sharedInstanceData; - [NativeDisableParallelForRestriction] public CPUInstanceData instanceData; - - [NativeDisableParallelForRestriction] public NativeArray transformUpdateInstanceQueue; - [NativeDisableParallelForRestriction] public NativeArray transformUpdateDataQueue; - [NativeDisableParallelForRestriction] public NativeArray boundingSpheresDataQueue; - - public void Execute(int startIndex, int count) - { - ulong validBits = 0; - - for (int i = 0; i < count; ++i) - { - InstanceHandle instance = instances[startIndex + i]; - - if (!instance.valid) - continue; - - if (!initialize) - { - int instanceIndex = instanceData.InstanceToIndex(instance); - int sharedInstanceIndex = sharedInstanceData.InstanceToIndex(instanceData, instance); - - TransformUpdateFlags flags = sharedInstanceData.flags[sharedInstanceIndex].transformUpdateFlags; - bool movedCurrentFrame = instanceData.movedInCurrentFrameBits.Get(instanceIndex); - bool isStaticObject = (flags & TransformUpdateFlags.IsPartOfStaticBatch) != 0; - - if (isStaticObject || movedCurrentFrame) - continue; - } - - validBits |= 1ul << i; - } - - int validBitCount = math.countbits(validBits); - - if (validBitCount > 0) - { - int writeIndex = atomicTransformQueueCount.Add(validBitCount); - int validBitIndex = math.tzcnt(validBits); - - while (validBits != 0) - { - int index = startIndex + validBitIndex; - InstanceHandle instance = instances[index]; - int instanceIndex = instanceData.InstanceToIndex(instance); - int sharedInstanceIndex = sharedInstanceData.InstanceToIndex(instanceData, instance); - - TransformUpdateFlags flags = sharedInstanceData.flags[sharedInstanceIndex].transformUpdateFlags; - bool isStaticObject = (flags & TransformUpdateFlags.IsPartOfStaticBatch) != 0; - - instanceData.movedInCurrentFrameBits.Set(instanceIndex, !isStaticObject); - transformUpdateInstanceQueue[writeIndex] = instance; - - ref float4x4 l2w = ref UnsafeUtility.ArrayElementAsRef(localToWorldMatrices.GetUnsafeReadOnlyPtr(), index); - ref AABB localAABB = ref UnsafeUtility.ArrayElementAsRef(sharedInstanceData.localAABBs.GetUnsafePtr(), sharedInstanceIndex); - AABB worldAABB = AABB.Transform(l2w, localAABB); - instanceData.worldAABBs[instanceIndex] = worldAABB; - - if (initialize) - { - PackedMatrix l2wPacked = PackedMatrix.FromFloat4x4(l2w); - PackedMatrix l2wPrevPacked = PackedMatrix.FromMatrix4x4(prevLocalToWorldMatrices[index]); - - transformUpdateDataQueue[writeIndex * 2] = new TransformUpdatePacket() - { - localToWorld0 = l2wPacked.packed0, - localToWorld1 = l2wPacked.packed1, - localToWorld2 = l2wPacked.packed2, - }; - transformUpdateDataQueue[writeIndex * 2 + 1] = new TransformUpdatePacket() - { - localToWorld0 = l2wPrevPacked.packed0, - localToWorld1 = l2wPrevPacked.packed1, - localToWorld2 = l2wPrevPacked.packed2, - }; - - // no need to set instanceData.localToWorldMatrices or instanceData.localToWorldIsFlippedBits - // they have been set up already by UpdateRendererInstancesJob - } - else - { - PackedMatrix l2wPacked = PackedMatrix.FromMatrix4x4(l2w); - - transformUpdateDataQueue[writeIndex] = new TransformUpdatePacket() - { - localToWorld0 = l2wPacked.packed0, - localToWorld1 = l2wPacked.packed1, - localToWorld2 = l2wPacked.packed2, - }; - - float det = math.determinant((float3x3)l2w); - instanceData.localToWorldIsFlippedBits.Set(instanceIndex, det < 0.0f); - } - - if (enableBoundingSpheres) - boundingSpheresDataQueue[writeIndex] = new float4(worldAABB.center.x, worldAABB.center.y, worldAABB.center.z, math.distance(worldAABB.max, worldAABB.min) * 0.5f); - - writeIndex += 1; - validBits &= ~(1ul << validBitIndex); - validBitIndex = math.tzcnt(validBits); - } - } - } - } - - [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] - private unsafe struct ProbesUpdateJob : IJobParallelForBatch - { - public const int k_BatchSize = 64; - - [NativeDisableContainerSafetyRestriction, NoAlias][ReadOnly] public NativeArray instances; - [NativeDisableParallelForRestriction][NativeDisableContainerSafetyRestriction, NoAlias] public CPUInstanceData instanceData; - [ReadOnly] public CPUSharedInstanceData sharedInstanceData; - - [NativeDisableUnsafePtrRestriction] public UnsafeAtomicCounter32 atomicProbesQueueCount; - - [NativeDisableParallelForRestriction] public NativeArray probeInstanceQueue; - [NativeDisableParallelForRestriction] public NativeArray compactTetrahedronCache; - [NativeDisableParallelForRestriction] public NativeArray probeQueryPosition; - - public void Execute(int startIndex, int count) - { - ulong validBits = 0; - - for (int i = 0; i < count; ++i) - { - InstanceHandle instance = instances[startIndex + i]; - - if (!instance.valid) - continue; - - int sharedInstanceIndex = sharedInstanceData.InstanceToIndex(instanceData, instance); - TransformUpdateFlags flags = sharedInstanceData.flags[sharedInstanceIndex].transformUpdateFlags; - bool hasLightProbe = (flags & TransformUpdateFlags.HasLightProbeCombined) != 0; - - if (!hasLightProbe) - continue; - - validBits |= 1ul << i; - } - - int validBitCount = math.countbits(validBits); - - if (validBitCount > 0) - { - int writeIndex = atomicProbesQueueCount.Add(validBitCount); - int validBitIndex = math.tzcnt(validBits); - - while (validBits != 0) - { - InstanceHandle instance = instances[startIndex + validBitIndex]; - int instanceIndex = instanceData.InstanceToIndex(instance); - ref AABB worldAABB = ref UnsafeUtility.ArrayElementAsRef(instanceData.worldAABBs.GetUnsafePtr(), instanceIndex); - - probeInstanceQueue[writeIndex] = instance; - probeQueryPosition[writeIndex] = worldAABB.center; - compactTetrahedronCache[writeIndex] = instanceData.tetrahedronCacheIndices[instanceIndex]; - - writeIndex += 1; - validBits &= ~(1ul << validBitIndex); - validBitIndex = math.tzcnt(validBits); - } - } - } - } - - [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] - private struct MotionUpdateJob : IJobParallelFor - { - public const int k_BatchSize = 16; - - [ReadOnly] public int queueWriteBase; - - [NativeDisableParallelForRestriction] public CPUInstanceData instanceData; - [NativeDisableUnsafePtrRestriction] public UnsafeAtomicCounter32 atomicUpdateQueueCount; - [NativeDisableParallelForRestriction][WriteOnly] public NativeArray transformUpdateInstanceQueue; - - public void Execute(int chunk_index) - { - int maxChunkBitCount = math.min(instanceData.instancesLength - 64 * chunk_index, 64); - ulong chunkBitMask = ~0ul >> (64 - maxChunkBitCount); - - ulong currentChunkBits = instanceData.movedInCurrentFrameBits.GetChunk(chunk_index) & chunkBitMask; - ulong prevChunkBits = instanceData.movedInPreviousFrameBits.GetChunk(chunk_index) & chunkBitMask; - - // update state in memory for the next frame - instanceData.movedInCurrentFrameBits.SetChunk(chunk_index, 0); - instanceData.movedInPreviousFrameBits.SetChunk(chunk_index, currentChunkBits); - - // ensure that objects that were moved last frame update their previous world matrix, if not already fully updated - ulong remainingChunkBits = prevChunkBits & ~currentChunkBits; - - // allocate space for all the writes from this chunk - int chunkBitCount = math.countbits(remainingChunkBits); - int writeIndex = queueWriteBase; - if (chunkBitCount > 0) - writeIndex += atomicUpdateQueueCount.Add(chunkBitCount); - - // loop over set bits to do the writes - int indexInChunk = math.tzcnt(remainingChunkBits); - - while (indexInChunk < 64) - { - int instanceIndex = 64 * chunk_index + indexInChunk; - transformUpdateInstanceQueue[writeIndex] = instanceData.IndexToInstance(instanceIndex); - - writeIndex += 1; - remainingChunkBits &= ~(1ul << indexInChunk); - indexInChunk = math.tzcnt(remainingChunkBits); - } - } - } - - [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] - private unsafe struct UpdateRendererInstancesJob : IJobParallelFor - { - public const int k_BatchSize = 128; - - [ReadOnly] public bool implicitInstanceIndices; - [ReadOnly] public GPUDrivenRendererGroupData rendererData; - [ReadOnly] public NativeArray instances; - [ReadOnly] public NativeParallelHashMap lodGroupDataMap; - - [NativeDisableParallelForRestriction][NativeDisableContainerSafetyRestriction, NoAlias] public CPUInstanceData instanceData; - [NativeDisableParallelForRestriction][NativeDisableContainerSafetyRestriction, NoAlias] public CPUSharedInstanceData sharedInstanceData; - [NativeDisableParallelForRestriction][NativeDisableContainerSafetyRestriction, NoAlias] public CPUPerCameraInstanceData perCameraInstanceData; - - public void Execute(int index) - { - var rendererGroupID = rendererData.rendererGroupID[index]; - int meshIndex = rendererData.meshIndex[index]; - var packedRendererData = rendererData.packedRendererData[index]; - var lodGroupID = rendererData.lodGroupID[index]; - var gameObjectLayer = rendererData.gameObjectLayer[index]; - var lightmapIndex = rendererData.lightmapIndex[index]; - var localAABB = rendererData.localBounds[index].ToAABB(); - int materialOffset = rendererData.materialsOffset[index]; - int materialCount = rendererData.materialsCount[index]; - - EntityId meshID = rendererData.meshID[meshIndex]; - var meshLodInfo = rendererData.meshLodInfo[meshIndex]; - - const int k_LightmapIndexMask = 0xFFFF; - const int k_LightmapIndexNotLightmapped = 0xFFFF; - const int k_LightmapIndexInfluenceOnly = 0xFFFE; - const uint k_InvalidLODGroupAndMask = 0xFFFFFFFF; - - var instanceFlags = InstanceFlags.None; - var transformUpdateFlags = TransformUpdateFlags.None; - - var lmIndexMasked = lightmapIndex & k_LightmapIndexMask; - - // Object doesn't have a valid lightmap Index, -> uses probes for lighting - if (lmIndexMasked >= k_LightmapIndexInfluenceOnly) - { - // Only add the component when needed to store blended results (shader will use the ambient probe when not present) - if (packedRendererData.lightProbeUsage == LightProbeUsage.BlendProbes) - transformUpdateFlags |= TransformUpdateFlags.HasLightProbeCombined; - } - - if (packedRendererData.isPartOfStaticBatch) - transformUpdateFlags |= TransformUpdateFlags.IsPartOfStaticBatch; - - switch (packedRendererData.shadowCastingMode) - { - case ShadowCastingMode.Off: - instanceFlags |= InstanceFlags.IsShadowsOff; - break; - case ShadowCastingMode.ShadowsOnly: - instanceFlags |= InstanceFlags.IsShadowsOnly; - break; - default: - break; - } - - if (meshLodInfo.lodSelectionActive) - instanceFlags |= InstanceFlags.HasMeshLod; - - // If the object is light mapped, or has the special influence-only value, it affects lightmaps - if (lmIndexMasked != k_LightmapIndexNotLightmapped) - instanceFlags |= InstanceFlags.AffectsLightmaps; - - // Mark if it should perform the small-mesh culling test - if (packedRendererData.smallMeshCulling) - instanceFlags |= InstanceFlags.SmallMeshCulling; - - uint lodGroupAndMask = k_InvalidLODGroupAndMask; - - // Renderer's LODGroup could be disabled which means that the renderer is not managed by it. - if (lodGroupDataMap.TryGetValue(lodGroupID, out var lodGroupHandle)) - { - if (packedRendererData.lodMask > 0) - lodGroupAndMask = (uint)lodGroupHandle.index << 8 | packedRendererData.lodMask; - } - - int instancesCount; - int instancesOffset; - - if (implicitInstanceIndices) - { - instancesCount = 1; - instancesOffset = index; - } - else - { - instancesCount = rendererData.instancesCount[index]; - instancesOffset = rendererData.instancesOffset[index]; - } - - if (instancesCount > 0) - { - InstanceHandle instance = instances[instancesOffset]; - SharedInstanceHandle sharedInstance = instanceData.Get_SharedInstance(instance); - Assert.IsTrue(sharedInstance.valid); - - var materialIDs = new SmallEntityIdArray(materialCount, Allocator.Persistent); - for (int i = 0; i < materialCount; i++) - { - int matIndex = rendererData.materialIndex[materialOffset + i]; - EntityId materialInstanceID = rendererData.materialID[matIndex]; - materialIDs[i] = materialInstanceID; - } - - sharedInstanceData.Set(sharedInstance, rendererGroupID, materialIDs, meshID, localAABB, transformUpdateFlags, instanceFlags, lodGroupAndMask, meshLodInfo, gameObjectLayer, - sharedInstanceData.Get_RefCount(sharedInstance)); - - for (int i = 0; i < instancesCount; ++i) - { - int inputIndex = instancesOffset + i; - - ref Matrix4x4 l2w = ref UnsafeUtility.ArrayElementAsRef(rendererData.localToWorldMatrix.GetUnsafeReadOnlyPtr(), inputIndex); - var worldAABB = AABB.Transform(l2w, localAABB); - - instance = instances[inputIndex]; - Assert.IsTrue(instance.valid); - - float det = math.determinant((float3x3)(float4x4)l2w); - bool isFlipped = (det < 0.0f); - - int instanceIndex = instanceData.InstanceToIndex(instance); - perCameraInstanceData.SetDefault(instanceIndex); - instanceData.localToWorldIsFlippedBits.Set(instanceIndex, isFlipped); - instanceData.worldAABBs[instanceIndex] = worldAABB; - instanceData.tetrahedronCacheIndices[instanceIndex] = -1; -#if UNITY_EDITOR - instanceData.editorData.sceneCullingMasks[instanceIndex] = rendererData.editorData[index].sceneCullingMask; - // Store more editor instance data here if needed. -#endif - instanceData.meshLodData[instanceIndex] = rendererData.meshLodData[index]; - } - } - } - } - - [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] - private struct CollectInstancesLODGroupsAndMasksJob : IJobParallelFor - { - public const int k_BatchSize = 128; - - [ReadOnly] public NativeArray instances; - [ReadOnly] public CPUInstanceData.ReadOnly instanceData; - [ReadOnly] public CPUSharedInstanceData.ReadOnly sharedInstanceData; - - [WriteOnly] public NativeArray lodGroupAndMasks; - - public void Execute(int index) - { - var instance = instances[index]; - var sharedInstanceIndex = sharedInstanceData.InstanceToIndex(instanceData, instance); - lodGroupAndMasks[index] = sharedInstanceData.lodGroupAndMasks[sharedInstanceIndex]; - } - } - - [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] - private struct GetVisibleNonProcessedTreeInstancesJob : IJobParallelForBatch - { - public const int k_BatchSize = 64; - - [ReadOnly] public CPUInstanceData instanceData; - [ReadOnly] public CPUSharedInstanceData sharedInstanceData; - [ReadOnly][NativeDisableContainerSafetyRestriction, NoAlias] public ParallelBitArray compactedVisibilityMasks; - [ReadOnly] public bool becomeVisible; - - [NativeDisableParallelForRestriction] public ParallelBitArray processedBits; - - [NativeDisableParallelForRestriction][WriteOnly] public NativeArray rendererIDs; - [NativeDisableParallelForRestriction][WriteOnly] public NativeArray instances; - - [NativeDisableUnsafePtrRestriction] public UnsafeAtomicCounter32 atomicTreeInstancesCount; - - public void Execute(int startIndex, int count) - { - var chunkIndex = startIndex / 64; - var visibleInPrevFrameChunk = instanceData.visibleInPreviousFrameBits.GetChunk(chunkIndex); - var processedChunk = processedBits.GetChunk(chunkIndex); - - ulong validBits = 0; - - for (int i = 0; i < count; ++i) - { - int instanceIndex = startIndex + i; - InstanceHandle instance = instanceData.IndexToInstance(instanceIndex); - bool hasTree = instance.type == InstanceType.SpeedTree; - - if (hasTree && compactedVisibilityMasks.Get(instance.index)) - { - var bitMask = 1ul << i; - - var processedInCurrentFrame = (processedChunk & bitMask) != 0; - - if (!processedInCurrentFrame) - { - bool visibleInPrevFrame = (visibleInPrevFrameChunk & bitMask) != 0; - - if (becomeVisible) - { - if (!visibleInPrevFrame) - validBits |= bitMask; - } - else - { - if (visibleInPrevFrame) - validBits |= bitMask; - } - } - } - } - - int validBitsCount = math.countbits(validBits); - - if (validBitsCount > 0) - { - processedBits.SetChunk(chunkIndex, processedChunk | validBits); - - int writeIndex = atomicTreeInstancesCount.Add(validBitsCount); - int validBitIndex = math.tzcnt(validBits); - - while (validBits != 0) - { - int instanceIndex = startIndex + validBitIndex; - InstanceHandle instance = instanceData.IndexToInstance(instanceIndex); - SharedInstanceHandle sharedInstanceHandle = instanceData.Get_SharedInstance(instance); - EntityId rendererID = sharedInstanceData.Get_RendererGroupID(sharedInstanceHandle); - - rendererIDs[writeIndex] = rendererID; - instances[writeIndex] = instance; - - writeIndex += 1; - validBits &= ~(1ul << validBitIndex); - validBitIndex = math.tzcnt(validBits); - } - } - } - } - - [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] - private struct UpdateCompactedInstanceVisibilityJob : IJobParallelForBatch - { - public const int k_BatchSize = 64; - - [ReadOnly] public ParallelBitArray compactedVisibilityMasks; - - [NativeDisableContainerSafetyRestriction, NoAlias][NativeDisableParallelForRestriction] public CPUInstanceData instanceData; - - public void Execute(int startIndex, int count) - { - ulong visibleBits = 0; - - for (int i = 0; i < count; ++i) - { - int instanceIndex = startIndex + i; - InstanceHandle instance = instanceData.IndexToInstance(instanceIndex); - bool visible = compactedVisibilityMasks.Get(instance.index); - - if (visible) - visibleBits |= 1ul << i; - } - - instanceData.visibleInPreviousFrameBits.SetChunk(startIndex / 64, visibleBits); - } - } - -#if UNITY_EDITOR - - [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] - private struct UpdateSelectedInstancesJob : IJobParallelFor - { - public const int k_BatchSize = 64; - - [ReadOnly] public NativeArray instances; - - [NativeDisableParallelForRestriction] public CPUInstanceData instanceData; - - public void Execute(int index) - { - InstanceHandle instance = instances[index]; - - if (instance.valid) - instanceData.editorData.selectedBits.Set(instanceData.InstanceToIndex(instance), true); - } - } - -#endif - } -} diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceDataSystem.Jobs.cs.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceDataSystem.Jobs.cs.meta deleted file mode 100644 index 11a25ba3c1d..00000000000 --- a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceDataSystem.Jobs.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 4fde0d9aabe2aee46b306a42532dafc7 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceDataSystem.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceDataSystem.cs deleted file mode 100644 index ebd30511d98..00000000000 --- a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceDataSystem.cs +++ /dev/null @@ -1,837 +0,0 @@ -using System; -using Unity.Collections; -using Unity.Collections.LowLevel.Unsafe; -using Unity.Jobs; -using Unity.Mathematics; -using UnityEngine.Assertions; -using UnityEngine.Profiling; - -namespace UnityEngine.Rendering -{ - internal partial class InstanceDataSystem : IDisposable - { - private InstanceAllocators m_InstanceAllocators; - private CPUSharedInstanceData m_SharedInstanceData; - private CPUInstanceData m_InstanceData; - private CPUPerCameraInstanceData m_PerCameraInstanceData; - - //@ We may want something a bit faster instead of multi hash map. Remove and search performance for multiple instances per renderer group is not great. - private NativeParallelMultiHashMap m_RendererGroupInstanceMultiHash; - - private ComputeShader m_TransformUpdateCS; - private ComputeShader m_WindDataUpdateCS; - private int m_TransformInitKernel; - private int m_TransformUpdateKernel; - private int m_MotionUpdateKernel; - private int m_ProbeUpdateKernel; - private int m_LODUpdateKernel; - private int m_WindDataCopyHistoryKernel; - - private ComputeBuffer m_UpdateIndexQueueBuffer; - - private ComputeBuffer m_ProbeUpdateDataQueueBuffer; - private ComputeBuffer m_ProbeOcclusionUpdateDataQueueBuffer; - - private ComputeBuffer m_TransformUpdateDataQueueBuffer; - private ComputeBuffer m_BoundingSpheresUpdateDataQueueBuffer; - - private bool m_EnableBoundingSpheres; - - public bool hasBoundingSpheres { get { return m_EnableBoundingSpheres; } } - - public CPUInstanceData.ReadOnly instanceData { get { return m_InstanceData.AsReadOnly(); } } - public CPUPerCameraInstanceData perCameraInstanceData { get { return m_PerCameraInstanceData; } } - public int cameraCount { get { return m_PerCameraInstanceData.cameraCount; }} - public CPUSharedInstanceData.ReadOnly sharedInstanceData { get { return m_SharedInstanceData.AsReadOnly(); } } - public NativeArray aliveInstances { get { return m_InstanceData.instances.GetSubArray(0, m_InstanceData.instancesLength); } } - - private readonly int[] m_ScratchWindParamAddressArray = new int[(int)SpeedTreeWindParamIndex.MaxWindParamsCount * 4]; - - public InstanceDataSystem(int maxInstances, bool enableBoundingSpheres, GPUResidentDrawerResources resources) - { - m_InstanceAllocators = new InstanceAllocators(); - m_SharedInstanceData = new CPUSharedInstanceData(); - m_InstanceData = new CPUInstanceData(); - m_PerCameraInstanceData = new CPUPerCameraInstanceData(); - - m_InstanceAllocators.Initialize(); - m_SharedInstanceData.Initialize(maxInstances); - m_InstanceData.Initialize(maxInstances); - m_PerCameraInstanceData.Initialize(maxInstances); - Assert.IsTrue(m_PerCameraInstanceData.instancesCapacity == m_InstanceData.instancesCapacity); - - m_RendererGroupInstanceMultiHash = new NativeParallelMultiHashMap(maxInstances, Allocator.Persistent); - - m_TransformUpdateCS = resources.transformUpdaterKernels; - m_WindDataUpdateCS = resources.windDataUpdaterKernels; - - m_TransformInitKernel = m_TransformUpdateCS.FindKernel("ScatterInitTransformMain"); - m_TransformUpdateKernel = m_TransformUpdateCS.FindKernel("ScatterUpdateTransformMain"); - m_MotionUpdateKernel = m_TransformUpdateCS.FindKernel("ScatterUpdateMotionMain"); - m_ProbeUpdateKernel = m_TransformUpdateCS.FindKernel("ScatterUpdateProbesMain"); - if (enableBoundingSpheres) - m_TransformUpdateCS.EnableKeyword("PROCESS_BOUNDING_SPHERES"); - else - m_TransformUpdateCS.DisableKeyword("PROCESS_BOUNDING_SPHERES"); - - m_WindDataCopyHistoryKernel = m_WindDataUpdateCS.FindKernel("WindDataCopyHistoryMain"); - - m_EnableBoundingSpheres = enableBoundingSpheres; - } - - public void Dispose() - { - m_InstanceAllocators.Dispose(); - m_SharedInstanceData.Dispose(); - m_InstanceData.Dispose(); - m_PerCameraInstanceData.Dispose(); - - m_RendererGroupInstanceMultiHash.Dispose(); - - m_UpdateIndexQueueBuffer?.Dispose(); - m_ProbeUpdateDataQueueBuffer?.Dispose(); - m_ProbeOcclusionUpdateDataQueueBuffer?.Dispose(); - m_TransformUpdateDataQueueBuffer?.Dispose(); - m_BoundingSpheresUpdateDataQueueBuffer?.Dispose(); - } - - public int GetMaxInstancesOfType(InstanceType instanceType) - { - return m_InstanceAllocators.GetInstanceHandlesLength(instanceType); - } - - public int GetAliveInstancesOfType(InstanceType instanceType) - { - return m_InstanceAllocators.GetInstancesLength(instanceType); - } - - private void EnsureIndexQueueBufferCapacity(int capacity) - { - if(m_UpdateIndexQueueBuffer == null || m_UpdateIndexQueueBuffer.count < capacity) - { - m_UpdateIndexQueueBuffer?.Dispose(); - m_UpdateIndexQueueBuffer = new ComputeBuffer(capacity, 4, ComputeBufferType.Raw); - } - } - - private void EnsureProbeBuffersCapacity(int capacity) - { - EnsureIndexQueueBufferCapacity(capacity); - - if (m_ProbeUpdateDataQueueBuffer == null || m_ProbeUpdateDataQueueBuffer.count < capacity) - { - m_ProbeUpdateDataQueueBuffer?.Dispose(); - m_ProbeOcclusionUpdateDataQueueBuffer?.Dispose(); - m_ProbeUpdateDataQueueBuffer = new ComputeBuffer(capacity, System.Runtime.InteropServices.Marshal.SizeOf(), ComputeBufferType.Structured); - m_ProbeOcclusionUpdateDataQueueBuffer = new ComputeBuffer(capacity, System.Runtime.InteropServices.Marshal.SizeOf(), ComputeBufferType.Structured); - } - } - - private void EnsureTransformBuffersCapacity(int capacity) - { - EnsureIndexQueueBufferCapacity(capacity); - - // Current and the previous matrices - int transformsCapacity = capacity * 2; - - if (m_TransformUpdateDataQueueBuffer == null || m_TransformUpdateDataQueueBuffer.count < transformsCapacity) - { - m_TransformUpdateDataQueueBuffer?.Dispose(); - m_BoundingSpheresUpdateDataQueueBuffer?.Dispose(); - m_TransformUpdateDataQueueBuffer = new ComputeBuffer(transformsCapacity, System.Runtime.InteropServices.Marshal.SizeOf(), ComputeBufferType.Structured); - if (m_EnableBoundingSpheres) - m_BoundingSpheresUpdateDataQueueBuffer = new ComputeBuffer(capacity, System.Runtime.InteropServices.Marshal.SizeOf(), ComputeBufferType.Structured); - } - } - - private JobHandle ScheduleInterpolateProbesAndUpdateTetrahedronCache(int queueCount, NativeArray probeUpdateInstanceQueue, NativeArray compactTetrahedronCache, - NativeArray probeQueryPosition, NativeArray probeUpdateDataQueue, NativeArray probeOcclusionUpdateDataQueue) - { - var lightProbesQuery = new LightProbesQuery(Allocator.TempJob); - - var calculateProbesJob = new CalculateInterpolatedLightAndOcclusionProbesBatchJob() - { - lightProbesQuery = lightProbesQuery, - probesCount = queueCount, - queryPostitions = probeQueryPosition, - compactTetrahedronCache = compactTetrahedronCache, - probesSphericalHarmonics = probeUpdateDataQueue, - probesOcclusion = probeOcclusionUpdateDataQueue - }; - - var totalBatchCount = 1 + (queueCount / CalculateInterpolatedLightAndOcclusionProbesBatchJob.k_CalculatedProbesPerBatch); - - var calculateProbesJobHandle = calculateProbesJob.Schedule(totalBatchCount, CalculateInterpolatedLightAndOcclusionProbesBatchJob.k_BatchSize); - - lightProbesQuery.Dispose(calculateProbesJobHandle); - - var scatterTetrahedronCacheIndicesJob = new ScatterTetrahedronCacheIndicesJob() - { - compactTetrahedronCache = compactTetrahedronCache, - probeInstances = probeUpdateInstanceQueue, - instanceData = m_InstanceData - }; - - return scatterTetrahedronCacheIndicesJob.Schedule(queueCount, ScatterTetrahedronCacheIndicesJob.k_BatchSize, calculateProbesJobHandle); - } - - private void DispatchProbeUpdateCommand(int queueCount, NativeArray probeInstanceQueue, NativeArray probeUpdateDataQueue, - NativeArray probeOcclusionUpdateDataQueue, RenderersParameters renderersParameters, GPUInstanceDataBuffer outputBuffer) - { - EnsureProbeBuffersCapacity(queueCount); - - var gpuInstanceIndices = new NativeArray(queueCount, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); - outputBuffer.CPUInstanceArrayToGPUInstanceArray(probeInstanceQueue.GetSubArray(0, queueCount), gpuInstanceIndices); - - Profiler.BeginSample("PrepareProbeUpdateDispatch"); - Profiler.BeginSample("ComputeBuffer.SetData"); - m_UpdateIndexQueueBuffer.SetData(gpuInstanceIndices, 0, 0, queueCount); - m_ProbeUpdateDataQueueBuffer.SetData(probeUpdateDataQueue, 0, 0, queueCount); - m_ProbeOcclusionUpdateDataQueueBuffer.SetData(probeOcclusionUpdateDataQueue, 0, 0, queueCount); - Profiler.EndSample(); - m_TransformUpdateCS.SetInt(InstanceTransformUpdateIDs._ProbeUpdateQueueCount, queueCount); - m_TransformUpdateCS.SetInt(InstanceTransformUpdateIDs._SHUpdateVec4Offset, renderersParameters.shCoefficients.uintOffset); - m_TransformUpdateCS.SetBuffer(m_ProbeUpdateKernel, InstanceTransformUpdateIDs._ProbeUpdateIndexQueue, m_UpdateIndexQueueBuffer); - m_TransformUpdateCS.SetBuffer(m_ProbeUpdateKernel, InstanceTransformUpdateIDs._ProbeUpdateDataQueue, m_ProbeUpdateDataQueueBuffer); - m_TransformUpdateCS.SetBuffer(m_ProbeUpdateKernel, InstanceTransformUpdateIDs._ProbeOcclusionUpdateDataQueue, m_ProbeOcclusionUpdateDataQueueBuffer); - m_TransformUpdateCS.SetBuffer(m_ProbeUpdateKernel, InstanceTransformUpdateIDs._OutputProbeBuffer, outputBuffer.gpuBuffer); - Profiler.EndSample(); - m_TransformUpdateCS.Dispatch(m_ProbeUpdateKernel, (queueCount + 63) / 64, 1, 1); - - gpuInstanceIndices.Dispose(); - } - - private void DispatchMotionUpdateCommand(int motionQueueCount, NativeArray transformInstanceQueue, RenderersParameters renderersParameters, GPUInstanceDataBuffer outputBuffer) - { - EnsureTransformBuffersCapacity(motionQueueCount); - - var gpuInstanceIndices = new NativeArray(motionQueueCount, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); - outputBuffer.CPUInstanceArrayToGPUInstanceArray(transformInstanceQueue.GetSubArray(0, motionQueueCount), gpuInstanceIndices); - - Profiler.BeginSample("PrepareMotionUpdateDispatch"); - Profiler.BeginSample("ComputeBuffer.SetData"); - m_UpdateIndexQueueBuffer.SetData(gpuInstanceIndices, 0, 0, motionQueueCount); - Profiler.EndSample(); - m_TransformUpdateCS.SetInt(InstanceTransformUpdateIDs._TransformUpdateQueueCount, motionQueueCount); - m_TransformUpdateCS.SetInt(InstanceTransformUpdateIDs._TransformUpdateOutputL2WVec4Offset, renderersParameters.localToWorld.uintOffset); - m_TransformUpdateCS.SetInt(InstanceTransformUpdateIDs._TransformUpdateOutputW2LVec4Offset, renderersParameters.worldToLocal.uintOffset); - m_TransformUpdateCS.SetInt(InstanceTransformUpdateIDs._TransformUpdateOutputPrevL2WVec4Offset, renderersParameters.matrixPreviousM.uintOffset); - m_TransformUpdateCS.SetInt(InstanceTransformUpdateIDs._TransformUpdateOutputPrevW2LVec4Offset, renderersParameters.matrixPreviousMI.uintOffset); - m_TransformUpdateCS.SetBuffer(m_MotionUpdateKernel, InstanceTransformUpdateIDs._TransformUpdateIndexQueue, m_UpdateIndexQueueBuffer); - m_TransformUpdateCS.SetBuffer(m_MotionUpdateKernel, InstanceTransformUpdateIDs._OutputTransformBuffer, outputBuffer.gpuBuffer); - Profiler.EndSample(); - m_TransformUpdateCS.Dispatch(m_MotionUpdateKernel, (motionQueueCount + 63) / 64, 1, 1); - - gpuInstanceIndices.Dispose(); - } - - private void DispatchTransformUpdateCommand(bool initialize, int transformQueueCount, NativeArray transformInstanceQueue, NativeArray updateDataQueue, - NativeArray boundingSphereUpdateDataQueue, RenderersParameters renderersParameters, GPUInstanceDataBuffer outputBuffer) - { - EnsureTransformBuffersCapacity(transformQueueCount); - - int transformQueueDataSize; - int kernel; - - if(initialize) - { - // When we reinitialize we have the current and the previous matrices per transform. - transformQueueDataSize = transformQueueCount * 2; - kernel = m_TransformInitKernel; - } - else - { - transformQueueDataSize = transformQueueCount; - kernel = m_TransformUpdateKernel; - } - - var gpuInstanceIndices = new NativeArray(transformQueueCount, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); - outputBuffer.CPUInstanceArrayToGPUInstanceArray(transformInstanceQueue.GetSubArray(0, transformQueueCount), gpuInstanceIndices); - - Profiler.BeginSample("PrepareTransformUpdateDispatch"); - Profiler.BeginSample("ComputeBuffer.SetData"); - m_UpdateIndexQueueBuffer.SetData(gpuInstanceIndices, 0, 0, transformQueueCount); - m_TransformUpdateDataQueueBuffer.SetData(updateDataQueue, 0, 0, transformQueueDataSize); - if (m_EnableBoundingSpheres) - m_BoundingSpheresUpdateDataQueueBuffer.SetData(boundingSphereUpdateDataQueue, 0, 0, transformQueueCount); - Profiler.EndSample(); - m_TransformUpdateCS.SetInt(InstanceTransformUpdateIDs._TransformUpdateQueueCount, transformQueueCount); - m_TransformUpdateCS.SetInt(InstanceTransformUpdateIDs._TransformUpdateOutputL2WVec4Offset, renderersParameters.localToWorld.uintOffset); - m_TransformUpdateCS.SetInt(InstanceTransformUpdateIDs._TransformUpdateOutputW2LVec4Offset, renderersParameters.worldToLocal.uintOffset); - m_TransformUpdateCS.SetInt(InstanceTransformUpdateIDs._TransformUpdateOutputPrevL2WVec4Offset, renderersParameters.matrixPreviousM.uintOffset); - m_TransformUpdateCS.SetInt(InstanceTransformUpdateIDs._TransformUpdateOutputPrevW2LVec4Offset, renderersParameters.matrixPreviousMI.uintOffset); - m_TransformUpdateCS.SetBuffer(kernel, InstanceTransformUpdateIDs._TransformUpdateIndexQueue, m_UpdateIndexQueueBuffer); - m_TransformUpdateCS.SetBuffer(kernel, InstanceTransformUpdateIDs._TransformUpdateDataQueue, m_TransformUpdateDataQueueBuffer); - if (m_EnableBoundingSpheres) - { - Assert.IsTrue(renderersParameters.boundingSphere.valid); - m_TransformUpdateCS.SetInt(InstanceTransformUpdateIDs._BoundingSphereOutputVec4Offset, renderersParameters.boundingSphere.uintOffset); - m_TransformUpdateCS.SetBuffer(kernel, InstanceTransformUpdateIDs._BoundingSphereDataQueue, m_BoundingSpheresUpdateDataQueueBuffer); - } - m_TransformUpdateCS.SetBuffer(kernel, InstanceTransformUpdateIDs._OutputTransformBuffer, outputBuffer.gpuBuffer); - Profiler.EndSample(); - m_TransformUpdateCS.Dispatch(kernel, (transformQueueCount + 63) / 64, 1, 1); - - gpuInstanceIndices.Dispose(); - } - - private void DispatchWindDataCopyHistoryCommand(NativeArray gpuInstanceIndices, RenderersParameters renderersParameters, GPUInstanceDataBuffer outputBuffer) - { - Profiler.BeginSample("DispatchWindDataCopyHistory"); - - int kernel = m_WindDataCopyHistoryKernel; - int instancesCount = gpuInstanceIndices.Length; - - EnsureIndexQueueBufferCapacity(instancesCount); - - m_UpdateIndexQueueBuffer.SetData(gpuInstanceIndices, 0, 0, instancesCount); - - m_WindDataUpdateCS.SetInt(InstanceWindDataUpdateIDs._WindDataQueueCount, instancesCount); - for (int i = 0; i < (int)SpeedTreeWindParamIndex.MaxWindParamsCount; ++i) - m_ScratchWindParamAddressArray[i * 4] = renderersParameters.windParams[i].gpuAddress; - m_WindDataUpdateCS.SetInts(InstanceWindDataUpdateIDs._WindParamAddressArray, m_ScratchWindParamAddressArray); - for (int i = 0; i < (int)SpeedTreeWindParamIndex.MaxWindParamsCount; ++i) - m_ScratchWindParamAddressArray[i * 4] = renderersParameters.windHistoryParams[i].gpuAddress; - m_WindDataUpdateCS.SetInts(InstanceWindDataUpdateIDs._WindHistoryParamAddressArray, m_ScratchWindParamAddressArray); - - m_WindDataUpdateCS.SetBuffer(kernel, InstanceWindDataUpdateIDs._WindDataUpdateIndexQueue, m_UpdateIndexQueueBuffer); - m_WindDataUpdateCS.SetBuffer(kernel, InstanceWindDataUpdateIDs._WindDataBuffer, outputBuffer.gpuBuffer); - m_WindDataUpdateCS.Dispatch(kernel, (instancesCount + 63) / 64, 1, 1); - - Profiler.EndSample(); - } - - private unsafe void UpdateInstanceMotionsData(in RenderersParameters renderersParameters, GPUInstanceDataBuffer outputBuffer) - { - var transformUpdateInstanceQueue = new NativeArray(m_InstanceData.instancesLength, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); - - var motionQueueCount = 0; - - new MotionUpdateJob() - { - queueWriteBase = 0, - instanceData = m_InstanceData, - atomicUpdateQueueCount = new UnsafeAtomicCounter32(&motionQueueCount), - transformUpdateInstanceQueue = transformUpdateInstanceQueue, - }.Schedule((m_InstanceData.instancesLength + 63) / 64, MotionUpdateJob.k_BatchSize).Complete(); - - if (motionQueueCount > 0) - DispatchMotionUpdateCommand(motionQueueCount, transformUpdateInstanceQueue, renderersParameters, outputBuffer); - - transformUpdateInstanceQueue.Dispose(); - } - - private unsafe void UpdateInstanceTransformsData(bool initialize, NativeArray instances, NativeArray localToWorldMatrices, NativeArray prevLocalToWorldMatrices, - in RenderersParameters renderersParameters, GPUInstanceDataBuffer outputBuffer) - { - Assert.AreEqual(instances.Length, localToWorldMatrices.Length); - Assert.AreEqual(instances.Length, prevLocalToWorldMatrices.Length); - - var transformUpdateInstanceQueue = new NativeArray(instances.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); - // When we reinitialize we have the current and the previous matrices per transform. - var transformUpdateDataQueue = new NativeArray(initialize ? instances.Length * 2 : instances.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); - var boundingSpheresUpdateDataQueue = new NativeArray(m_EnableBoundingSpheres ? instances.Length : 0, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); - - var probeInstanceQueue = new NativeArray(instances.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); - var compactTetrahedronCache = new NativeArray(instances.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); - var probeQueryPosition = new NativeArray(instances.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); - var probeUpdateDataQueue = new NativeArray(instances.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); - var probeOcclusionUpdateDataQueue = new NativeArray(instances.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); - - var transformQueueCount = 0; - int probesQueueCount = 0; - - var transformJob = new TransformUpdateJob() - { - initialize = initialize, - enableBoundingSpheres = m_EnableBoundingSpheres, - instances = instances, - localToWorldMatrices = localToWorldMatrices, - prevLocalToWorldMatrices = prevLocalToWorldMatrices, - atomicTransformQueueCount = new UnsafeAtomicCounter32(&transformQueueCount), - sharedInstanceData = m_SharedInstanceData, - instanceData = m_InstanceData, - transformUpdateInstanceQueue = transformUpdateInstanceQueue, - transformUpdateDataQueue = transformUpdateDataQueue, - boundingSpheresDataQueue = boundingSpheresUpdateDataQueue, - }; - - var probesJob = new ProbesUpdateJob() - { - instances = instances, - instanceData = m_InstanceData, - sharedInstanceData = m_SharedInstanceData, - atomicProbesQueueCount = new UnsafeAtomicCounter32(&probesQueueCount), - probeInstanceQueue = probeInstanceQueue, - compactTetrahedronCache = compactTetrahedronCache, - probeQueryPosition = probeQueryPosition - }; - - JobHandle jobHandle = transformJob.ScheduleBatch(instances.Length, TransformUpdateJob.k_BatchSize); - jobHandle = probesJob.ScheduleBatch(instances.Length, ProbesUpdateJob.k_BatchSize, jobHandle); - jobHandle.Complete(); - - if (probesQueueCount > 0) - { - ScheduleInterpolateProbesAndUpdateTetrahedronCache(probesQueueCount, probeInstanceQueue, compactTetrahedronCache, probeQueryPosition, - probeUpdateDataQueue, probeOcclusionUpdateDataQueue).Complete(); - - DispatchProbeUpdateCommand(probesQueueCount, probeInstanceQueue, probeUpdateDataQueue, probeOcclusionUpdateDataQueue, renderersParameters, outputBuffer); - } - - if (transformQueueCount > 0) - { - DispatchTransformUpdateCommand(initialize, transformQueueCount, transformUpdateInstanceQueue, transformUpdateDataQueue, boundingSpheresUpdateDataQueue, - renderersParameters, outputBuffer); - } - - transformUpdateInstanceQueue.Dispose(); - transformUpdateDataQueue.Dispose(); - boundingSpheresUpdateDataQueue.Dispose(); - - probeInstanceQueue.Dispose(); - compactTetrahedronCache.Dispose(); - probeQueryPosition.Dispose(); - probeUpdateDataQueue.Dispose(); - probeOcclusionUpdateDataQueue.Dispose(); - } - - private unsafe void UpdateInstanceProbesData(NativeArray instances, in RenderersParameters renderersParameters, GPUInstanceDataBuffer outputBuffer) - { - var probeInstanceQueue = new NativeArray(instances.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); - var compactTetrahedronCache = new NativeArray(instances.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); - var probeQueryPosition = new NativeArray(instances.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); - var probeUpdateDataQueue = new NativeArray(instances.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); - var probeOcclusionUpdateDataQueue = new NativeArray(instances.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); - - int probesQueueCount = 0; - - new ProbesUpdateJob() - { - instances = instances, - instanceData = m_InstanceData, - sharedInstanceData = m_SharedInstanceData, - atomicProbesQueueCount = new UnsafeAtomicCounter32(&probesQueueCount), - probeInstanceQueue = probeInstanceQueue, - compactTetrahedronCache = compactTetrahedronCache, - probeQueryPosition = probeQueryPosition - }.ScheduleBatch(instances.Length, ProbesUpdateJob.k_BatchSize).Complete(); - - if (probesQueueCount > 0) - { - ScheduleInterpolateProbesAndUpdateTetrahedronCache(probesQueueCount, probeInstanceQueue, compactTetrahedronCache, probeQueryPosition, - probeUpdateDataQueue, probeOcclusionUpdateDataQueue).Complete(); - - DispatchProbeUpdateCommand(probesQueueCount, probeInstanceQueue, probeUpdateDataQueue, probeOcclusionUpdateDataQueue, renderersParameters, outputBuffer); - } - - probeInstanceQueue.Dispose(); - compactTetrahedronCache.Dispose(); - probeQueryPosition.Dispose(); - probeUpdateDataQueue.Dispose(); - probeOcclusionUpdateDataQueue.Dispose(); - } - - public void UpdateInstanceWindDataHistory(NativeArray gpuInstanceIndices, RenderersParameters renderersParameters, GPUInstanceDataBuffer outputBuffer) - { - if(gpuInstanceIndices.Length == 0) - return; - - DispatchWindDataCopyHistoryCommand(gpuInstanceIndices, renderersParameters, outputBuffer); - } - - public unsafe void ReallocateAndGetInstances(in GPUDrivenRendererGroupData rendererData, NativeArray instances) - { - Assert.AreEqual(rendererData.localToWorldMatrix.Length, instances.Length); - - int newSharedInstancesCount = 0; - int newInstancesCount = 0; - - bool implicitInstanceIndices = rendererData.instancesCount.Length == 0; - - if (implicitInstanceIndices) - { - var queryJob = new QueryRendererGroupInstancesJob() - { - rendererGroupInstanceMultiHash = m_RendererGroupInstanceMultiHash, - rendererGroupIDs = rendererData.rendererGroupID, - instances = instances, - atomicNonFoundInstancesCount = new UnsafeAtomicCounter32(&newInstancesCount) - }; - - queryJob.ScheduleBatch(rendererData.rendererGroupID.Length, QueryRendererGroupInstancesJob.k_BatchSize).Complete(); - - newSharedInstancesCount = newInstancesCount; - } - else - { - var queryJob = new QueryRendererGroupInstancesMultiJob() - { - rendererGroupInstanceMultiHash = m_RendererGroupInstanceMultiHash, - rendererGroupIDs = rendererData.rendererGroupID, - instancesOffsets = rendererData.instancesOffset, - instancesCounts = rendererData.instancesCount, - instances = instances, - atomicNonFoundSharedInstancesCount = new UnsafeAtomicCounter32(&newSharedInstancesCount), - atomicNonFoundInstancesCount = new UnsafeAtomicCounter32(&newInstancesCount) - }; - - queryJob.ScheduleBatch(rendererData.rendererGroupID.Length, QueryRendererGroupInstancesMultiJob.k_BatchSize).Complete(); - } - - m_InstanceData.EnsureFreeInstances(newInstancesCount); - m_PerCameraInstanceData.Grow(m_InstanceData.instancesCapacity); - Assert.IsTrue(m_InstanceData.instancesCapacity == m_PerCameraInstanceData.instancesCapacity); - m_SharedInstanceData.EnsureFreeInstances(newSharedInstancesCount); - - InstanceDataSystemBurst.ReallocateInstances(implicitInstanceIndices, rendererData.rendererGroupID, rendererData.packedRendererData, - rendererData.instancesOffset, rendererData.instancesCount, ref m_InstanceAllocators, ref m_InstanceData, - ref m_PerCameraInstanceData, ref m_SharedInstanceData, ref instances, ref m_RendererGroupInstanceMultiHash); - } - - public void FreeRendererGroupInstances(NativeArray rendererGroupsID) - { - InstanceDataSystemBurst.FreeRendererGroupInstances(rendererGroupsID.AsReadOnly(), ref m_InstanceAllocators, ref m_InstanceData, - ref m_PerCameraInstanceData, ref m_SharedInstanceData, ref m_RendererGroupInstanceMultiHash); - } - - public void FreeInstances(NativeArray instances) - { - InstanceDataSystemBurst.FreeInstances(instances.AsReadOnly(), ref m_InstanceAllocators, ref m_InstanceData, ref m_PerCameraInstanceData, - ref m_SharedInstanceData, ref m_RendererGroupInstanceMultiHash); - } - - public JobHandle ScheduleUpdateInstanceDataJob(NativeArray instances, in GPUDrivenRendererGroupData rendererData, NativeParallelHashMap lodGroupDataMap) - { - bool implicitInstanceIndices = rendererData.instancesCount.Length == 0; - - if(implicitInstanceIndices) - { - Assert.AreEqual(instances.Length, rendererData.rendererGroupID.Length); - } - else - { - Assert.AreEqual(rendererData.instancesCount.Length, rendererData.rendererGroupID.Length); - Assert.AreEqual(rendererData.instancesOffset.Length, rendererData.rendererGroupID.Length); - } - - Assert.AreEqual(instances.Length, rendererData.localToWorldMatrix.Length); - - return new UpdateRendererInstancesJob - { - implicitInstanceIndices = implicitInstanceIndices, - instances = instances, - rendererData = rendererData, - lodGroupDataMap = lodGroupDataMap, - instanceData = m_InstanceData, - sharedInstanceData = m_SharedInstanceData, - perCameraInstanceData = m_PerCameraInstanceData - }.Schedule(rendererData.rendererGroupID.Length, UpdateRendererInstancesJob.k_BatchSize); - } - - public void UpdateAllInstanceProbes(in RenderersParameters renderersParameters, GPUInstanceDataBuffer outputBuffer) - { - var instances = m_InstanceData.instances.GetSubArray(0, m_InstanceData.instancesLength); - - if (instances.Length == 0) - return; - - UpdateInstanceProbesData(instances, renderersParameters, outputBuffer); - } - - public void InitializeInstanceTransforms(NativeArray instances, NativeArray localToWorldMatrices, - NativeArray prevLocalToWorldMatrices, in RenderersParameters renderersParameters, GPUInstanceDataBuffer outputBuffer) - { - if (instances.Length == 0) - return; - - UpdateInstanceTransformsData(true, instances, localToWorldMatrices, prevLocalToWorldMatrices, renderersParameters, outputBuffer); - } - - public void UpdateInstanceTransforms(NativeArray instances, NativeArray localToWorldMatrices, - in RenderersParameters renderersParameters, GPUInstanceDataBuffer outputBuffer) - { - if (instances.Length == 0) - return; - - UpdateInstanceTransformsData(false, instances, localToWorldMatrices, localToWorldMatrices, renderersParameters, outputBuffer); - } - - public void UpdateInstanceMotions(in RenderersParameters renderersParameters, GPUInstanceDataBuffer outputBuffer) - { - if (m_InstanceData.instancesLength == 0) - return; - - UpdateInstanceMotionsData(renderersParameters, outputBuffer); - } - - public JobHandle ScheduleQueryRendererGroupInstancesJob(NativeArray rendererGroupIDs, NativeArray instances) - { - Assert.AreEqual(rendererGroupIDs.Length, instances.Length); - - if (rendererGroupIDs.Length == 0) - return default; - - var queryJob = new QueryRendererGroupInstancesJob() - { - rendererGroupInstanceMultiHash = m_RendererGroupInstanceMultiHash, - rendererGroupIDs = rendererGroupIDs, - instances = instances - }; - - return queryJob.ScheduleBatch(rendererGroupIDs.Length, QueryRendererGroupInstancesJob.k_BatchSize); - } - - public JobHandle ScheduleQueryRendererGroupInstancesJob(NativeArray rendererGroupIDs, NativeList instances) - { - if (rendererGroupIDs.Length == 0) - return default; - - var instancesOffset = new NativeArray(rendererGroupIDs.Length, Allocator.TempJob); - var instancesCount = new NativeArray(rendererGroupIDs.Length, Allocator.TempJob); - - var jobHandle = ScheduleQueryRendererGroupInstancesJob(rendererGroupIDs, instancesOffset, instancesCount, instances); - - instancesOffset.Dispose(jobHandle); - instancesCount.Dispose(jobHandle); - - return jobHandle; - } - - public JobHandle ScheduleQueryRendererGroupInstancesJob(NativeArray rendererGroupIDs, NativeArray instancesOffset, NativeArray instancesCount, NativeList instances) - { - Assert.AreEqual(rendererGroupIDs.Length, instancesOffset.Length); - Assert.AreEqual(rendererGroupIDs.Length, instancesCount.Length); - - if (rendererGroupIDs.Length == 0) - return default; - - var queryCountJobHandle = new QueryRendererGroupInstancesCountJob - { - instanceData = m_InstanceData, - sharedInstanceData = m_SharedInstanceData, - rendererGroupInstanceMultiHash = m_RendererGroupInstanceMultiHash, - rendererGroupIDs = rendererGroupIDs, - instancesCount = instancesCount, - }.ScheduleBatch(rendererGroupIDs.Length, QueryRendererGroupInstancesCountJob.k_BatchSize); - - var computeOffsetsAndResizeArrayJobHandle = new ComputeInstancesOffsetAndResizeInstancesArrayJob - { - instancesCount = instancesCount, - instancesOffset = instancesOffset, - instances = instances - }.Schedule(queryCountJobHandle); - - return new QueryRendererGroupInstancesMultiJob() - { - rendererGroupInstanceMultiHash = m_RendererGroupInstanceMultiHash, - rendererGroupIDs = rendererGroupIDs, - instancesOffsets = instancesOffset, - instancesCounts = instancesCount, - instances = instances.AsDeferredJobArray() - }.ScheduleBatch(rendererGroupIDs.Length, QueryRendererGroupInstancesMultiJob.k_BatchSize, computeOffsetsAndResizeArrayJobHandle); - } - - public JobHandle ScheduleQuerySortedMeshInstancesJob(NativeArray sortedMeshIDs, NativeList instances) - { - if (sortedMeshIDs.Length == 0) - return default; - - instances.Capacity = m_InstanceData.instancesLength; - - var queryJob = new QuerySortedMeshInstancesJob() - { - instanceData = m_InstanceData, - sharedInstanceData = m_SharedInstanceData, - sortedMeshID = sortedMeshIDs, - instances = instances - }; - - return queryJob.ScheduleBatch(m_InstanceData.instancesLength, QuerySortedMeshInstancesJob.k_BatchSize); - } - - public JobHandle ScheduleCollectInstancesLODGroupAndMasksJob(NativeArray instances, NativeArray lodGroupAndMasks) - { - Assert.AreEqual(instances.Length, lodGroupAndMasks.Length); - - return new CollectInstancesLODGroupsAndMasksJob - { - instanceData = instanceData, - sharedInstanceData = sharedInstanceData, - instances = instances, - lodGroupAndMasks = lodGroupAndMasks - }.Schedule(instances.Length, CollectInstancesLODGroupsAndMasksJob.k_BatchSize); - } - - public bool InternalSanityCheckStates() - { - var instanceRefCountsHash = new NativeParallelHashMap(64, Allocator.Temp); - - int totalValidInstances = 0; - - for(int i = 0; i < m_InstanceData.handlesLength; ++i) - { - var instance = InstanceHandle.FromInt(i); - - if(m_InstanceData.IsValidInstance(instance)) - { - var sharedInstance = m_InstanceData.Get_SharedInstance(instance); - - if (instanceRefCountsHash.TryGetValue(sharedInstance, out var refCounts)) - instanceRefCountsHash[sharedInstance] = refCounts + 1; - else - instanceRefCountsHash.Add(sharedInstance, 1); - - ++totalValidInstances; - } - } - - if (m_InstanceData.instancesLength != totalValidInstances) - return false; - - int totalValidSharedInstances = 0; - - for (int i = 0; i < m_SharedInstanceData.handlesLength; ++i) - { - var sharedInstance = new SharedInstanceHandle { index = i }; - - if (m_SharedInstanceData.IsValidInstance(sharedInstance)) - { - var refCount = m_SharedInstanceData.Get_RefCount(sharedInstance); - - if (instanceRefCountsHash[sharedInstance] != refCount) - return false; - - ++totalValidSharedInstances; - } - } - - if (m_SharedInstanceData.instancesLength != totalValidSharedInstances) - return false; - - return true; - } - - public unsafe void GetVisibleTreeInstances(in ParallelBitArray compactedVisibilityMasks, in ParallelBitArray processedBits, NativeList visibeTreeRendererIDs, - NativeList visibeTreeInstances, bool becomeVisibleOnly, out int becomeVisibeTreeInstancesCount) - { - Assert.AreEqual(visibeTreeRendererIDs.Length, 0); - Assert.AreEqual(visibeTreeInstances.Length, 0); - - becomeVisibeTreeInstancesCount = 0; - - int maxTreeInstancesCount = GetAliveInstancesOfType(InstanceType.SpeedTree); - - if (maxTreeInstancesCount == 0) - return; - - visibeTreeRendererIDs.ResizeUninitialized(maxTreeInstancesCount); - visibeTreeInstances.ResizeUninitialized(maxTreeInstancesCount); - - int visibleTreeInstancesCount = 0; - - new GetVisibleNonProcessedTreeInstancesJob - { - becomeVisible = true, - instanceData = m_InstanceData, - sharedInstanceData = m_SharedInstanceData, - compactedVisibilityMasks = compactedVisibilityMasks, - processedBits = processedBits, - rendererIDs = visibeTreeRendererIDs.AsArray(), - instances = visibeTreeInstances.AsArray(), - atomicTreeInstancesCount = new UnsafeAtomicCounter32(&visibleTreeInstancesCount) - }.ScheduleBatch(m_InstanceData.instancesLength, GetVisibleNonProcessedTreeInstancesJob.k_BatchSize).Complete(); - - becomeVisibeTreeInstancesCount = visibleTreeInstancesCount; - - if(!becomeVisibleOnly) - { - new GetVisibleNonProcessedTreeInstancesJob - { - becomeVisible = false, - instanceData = m_InstanceData, - sharedInstanceData = m_SharedInstanceData, - compactedVisibilityMasks = compactedVisibilityMasks, - processedBits = processedBits, - rendererIDs = visibeTreeRendererIDs.AsArray(), - instances = visibeTreeInstances.AsArray(), - atomicTreeInstancesCount = new UnsafeAtomicCounter32(&visibleTreeInstancesCount) - }.ScheduleBatch(m_InstanceData.instancesLength, GetVisibleNonProcessedTreeInstancesJob.k_BatchSize).Complete(); - } - - Assert.IsTrue(becomeVisibeTreeInstancesCount <= visibleTreeInstancesCount); - Assert.IsTrue(visibleTreeInstancesCount <= maxTreeInstancesCount); - - visibeTreeRendererIDs.ResizeUninitialized(visibleTreeInstancesCount); - visibeTreeInstances.ResizeUninitialized(visibleTreeInstancesCount); - } - - public void UpdatePerFrameInstanceVisibility(in ParallelBitArray compactedVisibilityMasks) - { - Assert.AreEqual(m_InstanceData.handlesLength, compactedVisibilityMasks.Length); - - var updateCompactedInstanceVisibilityJob = new UpdateCompactedInstanceVisibilityJob - { - instanceData = m_InstanceData, - compactedVisibilityMasks = compactedVisibilityMasks - }; - - updateCompactedInstanceVisibilityJob.ScheduleBatch(m_InstanceData.instancesLength, UpdateCompactedInstanceVisibilityJob.k_BatchSize).Complete(); - } - -#if UNITY_EDITOR - public void UpdateSelectedInstances(NativeArray instances) - { - m_InstanceData.editorData.selectedBits.FillZeroes(m_InstanceData.instancesLength); - - new UpdateSelectedInstancesJob - { - instances = instances, - instanceData = m_InstanceData - }.Schedule(instances.Length, UpdateSelectedInstancesJob.k_BatchSize).Complete(); - } -#endif - - private static class InstanceTransformUpdateIDs - { - // Transform update kernel IDs - public static readonly int _TransformUpdateQueueCount = Shader.PropertyToID("_TransformUpdateQueueCount"); - public static readonly int _TransformUpdateOutputL2WVec4Offset = Shader.PropertyToID("_TransformUpdateOutputL2WVec4Offset"); - public static readonly int _TransformUpdateOutputW2LVec4Offset = Shader.PropertyToID("_TransformUpdateOutputW2LVec4Offset"); - public static readonly int _TransformUpdateOutputPrevL2WVec4Offset = Shader.PropertyToID("_TransformUpdateOutputPrevL2WVec4Offset"); - public static readonly int _TransformUpdateOutputPrevW2LVec4Offset = Shader.PropertyToID("_TransformUpdateOutputPrevW2LVec4Offset"); - public static readonly int _BoundingSphereOutputVec4Offset = Shader.PropertyToID("_BoundingSphereOutputVec4Offset"); - public static readonly int _TransformUpdateDataQueue = Shader.PropertyToID("_TransformUpdateDataQueue"); - public static readonly int _TransformUpdateIndexQueue = Shader.PropertyToID("_TransformUpdateIndexQueue"); - public static readonly int _BoundingSphereDataQueue = Shader.PropertyToID("_BoundingSphereDataQueue"); - public static readonly int _OutputTransformBuffer = Shader.PropertyToID("_OutputTransformBuffer"); - - // Probe update kernel IDs - public static readonly int _ProbeUpdateQueueCount = Shader.PropertyToID("_ProbeUpdateQueueCount"); - public static readonly int _SHUpdateVec4Offset = Shader.PropertyToID("_SHUpdateVec4Offset"); - public static readonly int _ProbeUpdateDataQueue = Shader.PropertyToID("_ProbeUpdateDataQueue"); - public static readonly int _ProbeOcclusionUpdateDataQueue = Shader.PropertyToID("_ProbeOcclusionUpdateDataQueue"); - public static readonly int _ProbeUpdateIndexQueue = Shader.PropertyToID("_ProbeUpdateIndexQueue"); - public static readonly int _OutputProbeBuffer = Shader.PropertyToID("_OutputProbeBuffer"); - } - - private static class InstanceWindDataUpdateIDs - { - public static readonly int _WindDataQueueCount = Shader.PropertyToID("_WindDataQueueCount"); - public static readonly int _WindDataUpdateIndexQueue = Shader.PropertyToID("_WindDataUpdateIndexQueue"); - public static readonly int _WindDataBuffer = Shader.PropertyToID("_WindDataBuffer"); - public static readonly int _WindParamAddressArray = Shader.PropertyToID("_WindParamAddressArray"); - public static readonly int _WindHistoryParamAddressArray = Shader.PropertyToID("_WindHistoryParamAddressArray"); - } - - public void DeallocatePerCameraInstanceData(NativeArray cameraIDs) - { - m_PerCameraInstanceData.DeallocateCameras(cameraIDs); - } - - public void AllocatePerCameraInstanceData(NativeArray cameraIDs) - { - m_PerCameraInstanceData.AllocateCameras(cameraIDs); - } - } -} diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceDataSystemBurst.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceDataSystemBurst.cs deleted file mode 100644 index ee8fe52cd7f..00000000000 --- a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceDataSystemBurst.cs +++ /dev/null @@ -1,195 +0,0 @@ -using Unity.Collections; -using Unity.Burst; -using UnityEngine.Assertions; - -namespace UnityEngine.Rendering -{ - [BurstCompile] - internal static class InstanceDataSystemBurst - { - [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] - public static void ReallocateInstances(bool implicitInstanceIndices, in NativeArray rendererGroupIDs, in NativeArray packedRendererData, - in NativeArray instanceOffsets, in NativeArray instanceCounts, ref InstanceAllocators instanceAllocators, ref CPUInstanceData instanceData, - ref CPUPerCameraInstanceData perCameraInstanceData, ref CPUSharedInstanceData sharedInstanceData, ref NativeArray instances, - ref NativeParallelMultiHashMap rendererGroupInstanceMultiHash) - { - for (int i = 0; i < rendererGroupIDs.Length; ++i) - { - var rendererGroupID = rendererGroupIDs[i]; - var hasTree = packedRendererData[i].hasTree; - - int instanceCount; - int instanceOffset; - - if (implicitInstanceIndices) - { - instanceCount = 1; - instanceOffset = i; - } - else - { - instanceCount = instanceCounts[i]; - instanceOffset = instanceOffsets[i]; - } - - SharedInstanceHandle sharedInstance; - - if (rendererGroupInstanceMultiHash.TryGetFirstValue(rendererGroupID, out var instance, out var it)) - { - sharedInstance = instanceData.Get_SharedInstance(instance); - - int currentInstancesCount = sharedInstanceData.Get_RefCount(sharedInstance); - int instancesToFreeCount = currentInstancesCount - instanceCount; - - if (instancesToFreeCount > 0) - { - bool success = true; - int freedInstancesCount = 0; - - for (int j = 0; j < instanceCount; ++j) - success = rendererGroupInstanceMultiHash.TryGetNextValue(out instance, ref it); - - Assert.IsTrue(success); - - while (success) - { - var idx = instanceData.InstanceToIndex(instance); - instanceData.Remove(instance); - perCameraInstanceData.Remove(idx); - instanceAllocators.FreeInstance(instance); - - rendererGroupInstanceMultiHash.Remove(it); - ++freedInstancesCount; - success = rendererGroupInstanceMultiHash.TryGetNextValue(out instance, ref it); - } - - Assert.AreEqual(instancesToFreeCount, freedInstancesCount); - } - } - else - { - sharedInstance = instanceAllocators.AllocateSharedInstance(); - sharedInstanceData.AddNoGrow(sharedInstance); - } - - if (instanceCount > 0) - { - sharedInstanceData.Set_RefCount(sharedInstance, instanceCount); - - for (int j = 0; j < instanceCount; ++j) - { - int instanceIndex = instanceOffset + j; - - if (instances[instanceIndex].valid) - continue; - - InstanceHandle newInstance; - - if (!hasTree) - newInstance = instanceAllocators.AllocateInstance(InstanceType.MeshRenderer); - else - newInstance = instanceAllocators.AllocateInstance(InstanceType.SpeedTree); - - instanceData.AddNoGrow(newInstance); - perCameraInstanceData.IncreaseInstanceCount(); - Assert.IsTrue(instanceData.instancesLength == perCameraInstanceData.instancesLength); - int index = instanceData.InstanceToIndex(newInstance); - instanceData.sharedInstances[index] = sharedInstance; - instanceData.movedInCurrentFrameBits.Set(index, false); - instanceData.movedInPreviousFrameBits.Set(index, false); - instanceData.visibleInPreviousFrameBits.Set(index, false); - - rendererGroupInstanceMultiHash.Add(rendererGroupID, newInstance); - instances[instanceIndex] = newInstance; - } - } - else - { - sharedInstanceData.Remove(sharedInstance); - instanceAllocators.FreeSharedInstance(sharedInstance); - } - } - } - - [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] - public static void FreeRendererGroupInstances(in NativeArray.ReadOnly rendererGroupsID, ref InstanceAllocators instanceAllocators, ref CPUInstanceData instanceData, - ref CPUPerCameraInstanceData perCameraInstanceData, ref CPUSharedInstanceData sharedInstanceData, ref NativeParallelMultiHashMap rendererGroupInstanceMultiHash) - { - foreach (var rendererGroupID in rendererGroupsID) - { - for (bool success = rendererGroupInstanceMultiHash.TryGetFirstValue(rendererGroupID, out var instance, out var it); success;) - { - SharedInstanceHandle sharedInstance = instanceData.Get_SharedInstance(instance); - int sharedInstanceIndex = sharedInstanceData.SharedInstanceToIndex(sharedInstance); - int refCount = sharedInstanceData.refCounts[sharedInstanceIndex]; - - Assert.IsTrue(refCount > 0); - - if (refCount > 1) - { - sharedInstanceData.refCounts[sharedInstanceIndex] = refCount - 1; - } - else - { - sharedInstanceData.Remove(sharedInstance); - instanceAllocators.FreeSharedInstance(sharedInstance); - } - - var idx = instanceData.InstanceToIndex(instance); - instanceData.Remove(instance); - perCameraInstanceData.Remove(idx); - instanceAllocators.FreeInstance(instance); - - success = rendererGroupInstanceMultiHash.TryGetNextValue(out instance, ref it); - } - - rendererGroupInstanceMultiHash.Remove(rendererGroupID); - } - } - - - [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] - public static void FreeInstances(in NativeArray.ReadOnly instances, ref InstanceAllocators instanceAllocators, ref CPUInstanceData instanceData, - ref CPUPerCameraInstanceData perCameraInstanceData, ref CPUSharedInstanceData sharedInstanceData, ref NativeParallelMultiHashMap rendererGroupInstanceMultiHash) - { - foreach (var instance in instances) - { - if (!instanceData.IsValidInstance(instance)) - continue; - - int instanceIndex = instanceData.InstanceToIndex(instance); - SharedInstanceHandle sharedInstance = instanceData.sharedInstances[instanceIndex]; - int sharedInstanceIndex = sharedInstanceData.SharedInstanceToIndex(sharedInstance); - int refCount = sharedInstanceData.refCounts[sharedInstanceIndex]; - var rendererGroupID = sharedInstanceData.rendererGroupIDs[sharedInstanceIndex]; - - Assert.IsTrue(refCount > 0); - - if (refCount > 1) - { - sharedInstanceData.refCounts[sharedInstanceIndex] = refCount - 1; - } - else - { - sharedInstanceData.Remove(sharedInstance); - instanceAllocators.FreeSharedInstance(sharedInstance); - } - var idx = instanceData.InstanceToIndex(instance); - instanceData.Remove(instance); - perCameraInstanceData.Remove(idx); - instanceAllocators.FreeInstance(instance); - - //@ This will have quadratic cost. Optimize later. - for (bool success = rendererGroupInstanceMultiHash.TryGetFirstValue(rendererGroupID, out var i, out var it); success;) - { - if (instance.Equals(i)) - { - rendererGroupInstanceMultiHash.Remove(it); - break; - } - success = rendererGroupInstanceMultiHash.TryGetNextValue(out i, ref it); - } - } - } - } -} diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceDataSystemBurst.cs.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceDataSystemBurst.cs.meta deleted file mode 100644 index 8613808fb3e..00000000000 --- a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceDataSystemBurst.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 302f596d55264be4ba359e52ec407766 \ No newline at end of file diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceType.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceType.cs deleted file mode 100644 index 34a757b386b..00000000000 --- a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceType.cs +++ /dev/null @@ -1,162 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using Unity.Collections; -using Unity.Collections.LowLevel.Unsafe; -using UnityEngine.Assertions; - -namespace UnityEngine.Rendering -{ - internal enum InstanceType - { - MeshRenderer = 0, - SpeedTree = 1, - Count, - - LODGroup = 0, // Aliased for now because it is part of a different instance data space. - } - - internal static class InstanceTypeInfo - { - public const int kInstanceTypeBitCount = 1; - public const int kMaxInstanceTypesCount = 1 << kInstanceTypeBitCount; - public const uint kInstanceTypeMask = kMaxInstanceTypesCount - 1; - - //@ For now it is probably fine to have inheritance, but in the future we might prefer composition if we end up with a lot of different types. - //@ Inheritance is really needed to quickly define overlapped instance data spaces. - private static InstanceType[] s_ParentTypes; - private static List[] s_ChildTypes; - - static InstanceTypeInfo() - { - Assert.IsTrue((int)InstanceType.Count <= kMaxInstanceTypesCount); - InitParentTypes(); - InitChildTypes(); - ValidateTypeRelationsAreCorrectlySorted(); - } - - private static void InitParentTypes() - { - s_ParentTypes = new InstanceType[(int)InstanceType.Count]; - - s_ParentTypes[(int)InstanceType.MeshRenderer] = InstanceType.MeshRenderer; - s_ParentTypes[(int)InstanceType.SpeedTree] = InstanceType.MeshRenderer; - // Add more parent types here if needed... - } - - private static void InitChildTypes() - { - s_ChildTypes = new List[(int)InstanceType.Count]; - - for (int i = 0; i < (int)InstanceType.Count; ++i) - s_ChildTypes[i] = new List(); - - for (int i = 0; i < (int)InstanceType.Count; ++i) - { - var type = (InstanceType)i; - var parentType = s_ParentTypes[(int)type]; - - if (type != parentType) - s_ChildTypes[(int)parentType].Add(type); - } - } - - private static InstanceType GetMaxChildTypeRecursively(InstanceType type) - { - InstanceType maxChildType = type; - - foreach (var childType in s_ChildTypes[(int)type]) - maxChildType = (InstanceType)Mathf.Max((int)maxChildType, (int)GetMaxChildTypeRecursively(childType)); - - return maxChildType; - } - - private static void FlattenChildInstanceTypes(InstanceType instanceType, NativeList instanceTypes) - { - instanceTypes.Add(instanceType); - - foreach (var childType in s_ChildTypes[(int)instanceType]) - FlattenChildInstanceTypes(childType, instanceTypes); - } - - private static void ValidateTypeRelationsAreCorrectlySorted() - { - var instanceTypesFlattened = new NativeList((int)InstanceType.Count, Allocator.Temp); - - for(int i = 0; i < (int)InstanceType.Count; ++i) - { - InstanceType instanceType = (InstanceType)i; - - if(instanceType == s_ParentTypes[i]) - FlattenChildInstanceTypes(instanceType, instanceTypesFlattened); - } - - Assert.AreEqual(instanceTypesFlattened.Length, (int)InstanceType.Count); - - for (int i = 0; i < instanceTypesFlattened.Length; ++i) - Assert.AreEqual((int)instanceTypesFlattened[i], i, "InstanceType relation is not properly ordered. Parent and child InstanceTypes should follow " + - "the depth first order to decrease unrelated type indices overlapping and better memory utilization."); - } - - public static InstanceType GetParentType(InstanceType type) - { - return s_ParentTypes[(int)type]; - } - - public static List GetChildTypes(InstanceType type) - { - return s_ChildTypes[(int)type]; - } - } - - internal unsafe struct InstanceNumInfo - { - public fixed int InstanceNums[(int)InstanceType.Count]; - - public void InitDefault() - { - for (int i = 0; i < (int)InstanceType.Count; ++i) - InstanceNums[i] = 0; - } - - public InstanceNumInfo(InstanceType type, int instanceNum) - { - InitDefault(); - InstanceNums[(int)type] = instanceNum; - } - - public InstanceNumInfo(int meshRendererNum = 0, int speedTreeNum = 0) - { - InitDefault(); - InstanceNums[(int)InstanceType.MeshRenderer] = meshRendererNum; - InstanceNums[(int)InstanceType.SpeedTree] = speedTreeNum; - } - - public int GetInstanceNum(InstanceType type) - { - return InstanceNums[(int)type]; - } - - public int GetInstanceNumIncludingChildren(InstanceType type) - { - int numInstances = GetInstanceNum(type); - - var childTypes = InstanceTypeInfo.GetChildTypes(type); - - foreach (var childType in childTypes) - numInstances += GetInstanceNumIncludingChildren(childType); - - return numInstances; - } - - public int GetTotalInstanceNum() - { - int totalInstanceNum = 0; - - for (int i = 0; i < (int)InstanceType.Count; ++i) - totalInstanceNum += InstanceNums[i]; - - return totalInstanceNum; - } - } -} diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceType.cs.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceType.cs.meta deleted file mode 100644 index 977d48b3d05..00000000000 --- a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceType.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: 77eec85a2a778304f8aec8f23df581ed \ No newline at end of file diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/LODGroupDataPool.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/LODGroupDataPool.cs deleted file mode 100644 index 4d2cf597644..00000000000 --- a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/LODGroupDataPool.cs +++ /dev/null @@ -1,294 +0,0 @@ -using System; -using UnityEngine.Assertions; -using Unity.Collections; -using Unity.Jobs; -using Unity.Collections.LowLevel.Unsafe; -using Unity.Burst; -using Unity.Mathematics; - -namespace UnityEngine.Rendering -{ - internal unsafe struct LODGroupData - { - public const int k_MaxLODLevelsCount = 8; - - public bool valid; - public int lodCount; - public int rendererCount; - public fixed float screenRelativeTransitionHeights[k_MaxLODLevelsCount]; - public fixed float fadeTransitionWidth[k_MaxLODLevelsCount]; - } - - internal unsafe struct LODGroupCullingData - { - public float3 worldSpaceReferencePoint; - public int lodCount; - public fixed float sqrDistances[LODGroupData.k_MaxLODLevelsCount]; // we use square distance to get rid of a sqrt in gpu culling.. - public fixed float transitionDistances[LODGroupData.k_MaxLODLevelsCount]; // todo - make this a separate data struct (CPUOnly, as we do not support dithering on GPU..) - public float worldSpaceSize;// SpeedTree crossfade. - public fixed bool percentageFlags[LODGroupData.k_MaxLODLevelsCount];// SpeedTree crossfade. - public byte forceLODMask; - } - - [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] - internal struct UpdateLODGroupTransformJob : IJobParallelFor - { - public const int k_BatchSize = 256; - - [ReadOnly] public NativeParallelHashMap lodGroupDataHash; - [ReadOnly] public NativeArray lodGroupIDs; - [ReadOnly] public NativeArray worldSpaceReferencePoints; - [ReadOnly] public NativeArray worldSpaceSizes; - [ReadOnly] public bool requiresGPUUpload; - [ReadOnly] public bool supportDitheringCrossFade; - - [NativeDisableContainerSafetyRestriction, NoAlias, ReadOnly] public NativeList lodGroupData; - - [NativeDisableContainerSafetyRestriction, NoAlias, WriteOnly] public NativeList lodGroupCullingData; - - [NativeDisableUnsafePtrRestriction] public UnsafeAtomicCounter32 atomicUpdateCount; - - public unsafe void Execute(int index) - { - EntityId lodGroupID = lodGroupIDs[index]; - - if (lodGroupDataHash.TryGetValue(lodGroupID, out var lodGroupInstance)) - { - var worldSpaceSize = worldSpaceSizes[index]; - - LODGroupData* lodGroup = (LODGroupData*)lodGroupData.GetUnsafePtr() + lodGroupInstance.index; - LODGroupCullingData* lodGroupTransformResult = (LODGroupCullingData*)lodGroupCullingData.GetUnsafePtr() + lodGroupInstance.index; - lodGroupTransformResult->worldSpaceSize = worldSpaceSize; - lodGroupTransformResult->worldSpaceReferencePoint = worldSpaceReferencePoints[index]; - - for (int i = 0; i < lodGroup->lodCount; ++i) - { - float lodHeight = lodGroup->screenRelativeTransitionHeights[i]; - - var lodDist = LODRenderingUtils.CalculateLODDistance(lodHeight, worldSpaceSize); - lodGroupTransformResult->sqrDistances[i] = lodDist * lodDist; - - if (supportDitheringCrossFade && !lodGroupTransformResult->percentageFlags[i]) - { - float prevLODHeight = i != 0 ? lodGroup->screenRelativeTransitionHeights[i - 1] : 1.0f; - float transitionHeight = lodHeight + lodGroup->fadeTransitionWidth[i] * (prevLODHeight - lodHeight); - var transitionDistance = lodDist - LODRenderingUtils.CalculateLODDistance(transitionHeight, worldSpaceSize); - transitionDistance = Mathf.Max(0.0f, transitionDistance); - lodGroupTransformResult->transitionDistances[i] = transitionDistance; - } - else - { - lodGroupTransformResult->transitionDistances[i] = 0f; - } - - } - } - } - } - - [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] - internal unsafe struct UpdateLODGroupDataJob : IJobParallelFor - { - public const int k_BatchSize = 256; - - [ReadOnly] public NativeArray lodGroupInstances; - [ReadOnly] public GPUDrivenLODGroupData inputData; - [ReadOnly] public bool supportDitheringCrossFade; - - public NativeArray lodGroupsData; - public NativeArray lodGroupsCullingData; - - [NativeDisableUnsafePtrRestriction] public UnsafeAtomicCounter32 rendererCount; - - public void Execute(int index) - { - var lodGroupInstance = lodGroupInstances[index]; - var fadeMode = inputData.fadeMode[index]; - var lodOffset = inputData.lodOffset[index]; - var lodCount = inputData.lodCount[index]; - var renderersCount = inputData.renderersCount[index]; - var worldReferencePoint = inputData.worldSpaceReferencePoint[index]; - var worldSpaceSize = inputData.worldSpaceSize[index]; - var lastLODIsBillboard = inputData.lastLODIsBillboard[index]; - var forceLODMask = inputData.forceLODMask[index]; - var useDitheringCrossFade = fadeMode != LODFadeMode.None && supportDitheringCrossFade; - var useSpeedTreeCrossFade = fadeMode == LODFadeMode.SpeedTree; - - LODGroupData* lodGroupData = (LODGroupData*)lodGroupsData.GetUnsafePtr() + lodGroupInstance.index; - LODGroupCullingData* lodGroupCullingData = (LODGroupCullingData*)lodGroupsCullingData.GetUnsafePtr() + lodGroupInstance.index; - - lodGroupData->valid = true; - lodGroupData->lodCount = lodCount; - lodGroupData->rendererCount = useDitheringCrossFade ? renderersCount : 0; - lodGroupCullingData->worldSpaceSize = worldSpaceSize; - lodGroupCullingData->worldSpaceReferencePoint = worldReferencePoint; - lodGroupCullingData->forceLODMask = forceLODMask; - lodGroupCullingData->lodCount = lodCount; - - rendererCount.Add(lodGroupData->rendererCount); - - var crossFadeLODBegin = 0; - - if (useSpeedTreeCrossFade) - { - var lastLODIndex = lodOffset + (lodCount - 1); - var hasBillboardLOD = lodCount > 0 && inputData.lodRenderersCount[lastLODIndex] == 1 && lastLODIsBillboard; - - if (lodCount == 0) - crossFadeLODBegin = 0; - else if (hasBillboardLOD) - crossFadeLODBegin = Math.Max(lodCount, 2) - 2; - else - crossFadeLODBegin = lodCount - 1; - } - - for (int i = 0; i < lodCount; ++i) - { - var lodIndex = lodOffset + i; - var lodHeight = inputData.lodScreenRelativeTransitionHeight[lodIndex]; - var lodDist = LODRenderingUtils.CalculateLODDistance(lodHeight, worldSpaceSize); - - lodGroupData->screenRelativeTransitionHeights[i] = lodHeight; - lodGroupData->fadeTransitionWidth[i] = 0.0f; - lodGroupCullingData->sqrDistances[i] = lodDist * lodDist; - lodGroupCullingData->percentageFlags[i] = false; - lodGroupCullingData->transitionDistances[i] = 0.0f; - - if (useSpeedTreeCrossFade && i < crossFadeLODBegin) - { - lodGroupCullingData->percentageFlags[i] = true; - } - else if (useDitheringCrossFade && i >= crossFadeLODBegin) - { - var fadeTransitionWidth = inputData.lodFadeTransitionWidth[lodIndex]; - var prevLODHeight = i != 0 ? inputData.lodScreenRelativeTransitionHeight[lodIndex - 1] : 1.0f; - var transitionHeight = lodHeight + fadeTransitionWidth * (prevLODHeight - lodHeight); - var transitionDistance = lodDist - LODRenderingUtils.CalculateLODDistance(transitionHeight, worldSpaceSize); - transitionDistance = Mathf.Max(0.0f, transitionDistance); - - lodGroupData->fadeTransitionWidth[i] = fadeTransitionWidth; - lodGroupCullingData->transitionDistances[i] = transitionDistance; - } - } - } - } - - internal class LODGroupDataPool : IDisposable - { - private NativeList m_LODGroupData; - private NativeParallelHashMap m_LODGroupDataHash; - public NativeParallelHashMap lodGroupDataHash => m_LODGroupDataHash; - - private NativeList m_LODGroupCullingData; - private NativeList m_FreeLODGroupDataHandles; - - private int m_CrossfadedRendererCount; - private bool m_SupportDitheringCrossFade; - - public NativeList lodGroupCullingData => m_LODGroupCullingData; - public int crossfadedRendererCount => m_CrossfadedRendererCount; - - public int activeLodGroupCount => m_LODGroupData.Length; - - private static class LodGroupShaderIDs - { - public static readonly int _SupportDitheringCrossFade = Shader.PropertyToID("_SupportDitheringCrossFade"); - public static readonly int _LodGroupCullingDataGPUByteSize = Shader.PropertyToID("_LodGroupCullingDataGPUByteSize"); - public static readonly int _LodGroupCullingDataStartOffset = Shader.PropertyToID("_LodGroupCullingDataStartOffset"); - public static readonly int _LodCullingDataQueueCount = Shader.PropertyToID("_LodCullingDataQueueCount"); - public static readonly int _InputLodCullingDataIndices = Shader.PropertyToID("_InputLodCullingDataIndices"); - public static readonly int _InputLodCullingDataBuffer = Shader.PropertyToID("_InputLodCullingDataBuffer"); - public static readonly int _LodGroupCullingData = Shader.PropertyToID("_LodGroupCullingData"); - } - - public LODGroupDataPool(GPUResidentDrawerResources resources, int initialInstanceCount, bool supportDitheringCrossFade) - { - m_LODGroupData = new NativeList(Allocator.Persistent); - m_LODGroupDataHash = new NativeParallelHashMap(64, Allocator.Persistent); - - m_LODGroupCullingData = new NativeList(Allocator.Persistent); - m_FreeLODGroupDataHandles = new NativeList(Allocator.Persistent); - - m_SupportDitheringCrossFade = supportDitheringCrossFade; - } - - public void Dispose() - { - m_LODGroupData.Dispose(); - m_LODGroupDataHash.Dispose(); - - m_LODGroupCullingData.Dispose(); - m_FreeLODGroupDataHandles.Dispose(); - } - - public unsafe void UpdateLODGroupTransformData(in GPUDrivenLODGroupData inputData) - { - var lodGroupCount = inputData.lodGroupID.Length; - - var updateCount = 0; - - var jobData = new UpdateLODGroupTransformJob() - { - lodGroupDataHash = m_LODGroupDataHash, - lodGroupIDs = inputData.lodGroupID, - worldSpaceReferencePoints = inputData.worldSpaceReferencePoint, - worldSpaceSizes = inputData.worldSpaceSize, - lodGroupData = m_LODGroupData, - lodGroupCullingData = m_LODGroupCullingData, - supportDitheringCrossFade = m_SupportDitheringCrossFade, - atomicUpdateCount = new UnsafeAtomicCounter32(&updateCount), - }; - - if (lodGroupCount >= UpdateLODGroupTransformJob.k_BatchSize) - jobData.Schedule(lodGroupCount, UpdateLODGroupTransformJob.k_BatchSize).Complete(); - else - jobData.Run(lodGroupCount); - } - - public unsafe void UpdateLODGroupData(in GPUDrivenLODGroupData inputData) - { - FreeLODGroupData(inputData.invalidLODGroupID); - - var lodGroupInstances = new NativeArray(inputData.lodGroupID.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); - - int previousRendererCount = LODGroupDataPoolBurst.AllocateOrGetLODGroupDataInstances(inputData.lodGroupID, - ref m_LODGroupData, ref m_LODGroupCullingData, - ref m_LODGroupDataHash, ref m_FreeLODGroupDataHandles, ref lodGroupInstances); - - m_CrossfadedRendererCount -= previousRendererCount; - Assert.IsTrue(m_CrossfadedRendererCount >= 0); - - int rendererCount = 0; - - var updateLODGroupDataJobData = new UpdateLODGroupDataJob - { - lodGroupInstances = lodGroupInstances, - inputData = inputData, - supportDitheringCrossFade = m_SupportDitheringCrossFade, - lodGroupsData = m_LODGroupData.AsArray(), - lodGroupsCullingData = m_LODGroupCullingData.AsArray(), - rendererCount = new UnsafeAtomicCounter32(&rendererCount), - }; - - if (lodGroupInstances.Length >= UpdateLODGroupTransformJob.k_BatchSize) - updateLODGroupDataJobData.Schedule(lodGroupInstances.Length, UpdateLODGroupTransformJob.k_BatchSize).Complete(); - else - updateLODGroupDataJobData.Run(lodGroupInstances.Length); - - m_CrossfadedRendererCount += rendererCount; - - lodGroupInstances.Dispose(); - } - - public void FreeLODGroupData(NativeArray destroyedLODGroupsID) - { - if (destroyedLODGroupsID.Length == 0) - return; - - int removedRendererCount = LODGroupDataPoolBurst.FreeLODGroupData(destroyedLODGroupsID, ref m_LODGroupData, ref m_LODGroupDataHash, ref m_FreeLODGroupDataHandles); - - m_CrossfadedRendererCount -= removedRendererCount; - Assert.IsTrue(m_CrossfadedRendererCount >= 0); - } - } -} diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/LODGroupDataPoolBurst.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/LODGroupDataPoolBurst.cs deleted file mode 100644 index e9c86926498..00000000000 --- a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/LODGroupDataPoolBurst.cs +++ /dev/null @@ -1,72 +0,0 @@ -using Unity.Collections; -using Unity.Burst; -using UnityEngine.Assertions; - -namespace UnityEngine.Rendering -{ - [BurstCompile] - internal static class LODGroupDataPoolBurst - { - [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] - public static int FreeLODGroupData(in NativeArray destroyedLODGroupsID, ref NativeList lodGroupsData, - ref NativeParallelHashMap lodGroupDataHash, ref NativeList freeLODGroupDataHandles) - { - int removedRendererCount = 0; - - foreach (EntityId lodGroupID in destroyedLODGroupsID) - { - if (lodGroupDataHash.TryGetValue(lodGroupID, out var lodGroupInstance)) - { - Assert.IsTrue(lodGroupInstance.valid); - - lodGroupDataHash.Remove(lodGroupID); - freeLODGroupDataHandles.Add(lodGroupInstance); - - ref LODGroupData lodGroupData = ref lodGroupsData.ElementAt(lodGroupInstance.index); - Assert.IsTrue(lodGroupData.valid); - - removedRendererCount += lodGroupData.rendererCount; - lodGroupData.valid = false; - } - } - - return removedRendererCount; - } - - [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] - public static int AllocateOrGetLODGroupDataInstances(in NativeArray lodGroupsID, ref NativeList lodGroupsData, ref NativeList lodGroupCullingData, - ref NativeParallelHashMap lodGroupDataHash, ref NativeList freeLODGroupDataHandles, ref NativeArray lodGroupInstances) - { - int freeHandlesCount = freeLODGroupDataHandles.Length; - int lodDataLength = lodGroupsData.Length; - int previousRendererCount = 0; - - for (int i = 0; i < lodGroupsID.Length; ++i) - { - EntityId lodGroupID = lodGroupsID[i]; - - if (!lodGroupDataHash.TryGetValue(lodGroupID, out var lodGroupInstance)) - { - if (freeHandlesCount == 0) - lodGroupInstance = new GPUInstanceIndex() { index = lodDataLength++ }; - else - lodGroupInstance = freeLODGroupDataHandles[--freeHandlesCount]; - - lodGroupDataHash.TryAdd(lodGroupID, lodGroupInstance); - } - else - { - previousRendererCount += lodGroupsData.ElementAt(lodGroupInstance.index).rendererCount; - } - - lodGroupInstances[i] = lodGroupInstance; - } - - freeLODGroupDataHandles.ResizeUninitialized(freeHandlesCount); - lodGroupsData.ResizeUninitialized(lodDataLength); - lodGroupCullingData.ResizeUninitialized(lodDataLength); - - return previousRendererCount; - } - } -} diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/LODGroupDataPoolBurst.cs.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/LODGroupDataPoolBurst.cs.meta deleted file mode 100644 index 6e2ab16b7ac..00000000000 --- a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/LODGroupDataPoolBurst.cs.meta +++ /dev/null @@ -1,2 +0,0 @@ -fileFormatVersion: 2 -guid: ad57195e4230c9344a64d902de871991 \ No newline at end of file diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/RenderersBatchersContext.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/RenderersBatchersContext.cs deleted file mode 100644 index 4964b94faa2..00000000000 --- a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/RenderersBatchersContext.cs +++ /dev/null @@ -1,408 +0,0 @@ -using System; -using System.Collections.Generic; -using UnityEngine.Assertions; -using Unity.Collections; -using Unity.Collections.LowLevel.Unsafe; -using Unity.Jobs; -using Unity.Mathematics; -using Unity.Burst; -using UnityEngine.Jobs; -using UnityEngine.Profiling; - -namespace UnityEngine.Rendering -{ - internal struct RenderersBatchersContextDesc - { - public InstanceNumInfo instanceNumInfo; - public bool supportDitheringCrossFade; - public bool enableBoundingSpheresInstanceData; - public float smallMeshScreenPercentage; - public bool enableCullerDebugStats; - - public static RenderersBatchersContextDesc NewDefault() - { - return new RenderersBatchersContextDesc() - { - instanceNumInfo = new InstanceNumInfo(meshRendererNum: 1024, speedTreeNum: 32), - }; - } - } - - internal class RenderersBatchersContext : IDisposable - { - public RenderersParameters renderersParameters { get { return m_RenderersParameters; } } - public GraphicsBuffer gpuInstanceDataBuffer { get { return m_InstanceDataBuffer.gpuBuffer; } } - public int activeLodGroupCount { get { return m_LODGroupDataPool.activeLodGroupCount; } } - public NativeArray.ReadOnly defaultDescriptions { get { return m_InstanceDataBuffer.descriptions.AsReadOnly(); } } - public NativeArray defaultMetadata { get { return m_InstanceDataBuffer.defaultMetadata; } } - public NativeList lodGroupCullingData { get { return m_LODGroupDataPool.lodGroupCullingData; } } - public int instanceDataBufferVersion { get { return m_InstanceDataBuffer.version; } } - public int instanceDataBufferLayoutVersion { get { return m_InstanceDataBuffer.layoutVersion; } } - public SphericalHarmonicsL2 cachedAmbientProbe { get { return m_CachedAmbientProbe; } } - - public bool hasBoundingSpheres { get { return m_InstanceDataSystem.hasBoundingSpheres; } } - public int cameraCount { get { return m_InstanceDataSystem.cameraCount; } } - public CPUInstanceData.ReadOnly instanceData { get { return m_InstanceDataSystem.instanceData; } } - public CPUSharedInstanceData.ReadOnly sharedInstanceData { get { return m_InstanceDataSystem.sharedInstanceData; } } - - public CPUPerCameraInstanceData perCameraInstanceData { get { return m_InstanceDataSystem.perCameraInstanceData; } } - public GPUInstanceDataBuffer.ReadOnly instanceDataBuffer { get { return m_InstanceDataBuffer.AsReadOnly(); } } - public NativeArray aliveInstances { get { return m_InstanceDataSystem.aliveInstances; } } - - public float smallMeshScreenPercentage { get { return m_SmallMeshScreenPercentage; } } - - private InstanceDataSystem m_InstanceDataSystem; - - public GPUResidentDrawerResources resources { get { return m_Resources; } } - - private GPUResidentDrawerResources m_Resources; - private GPUDrivenProcessor m_GPUDrivenProcessor; - - private LODGroupDataPool m_LODGroupDataPool; - - internal GPUInstanceDataBuffer m_InstanceDataBuffer; - private RenderersParameters m_RenderersParameters; - private GPUInstanceDataBufferUploader.GPUResources m_UploadResources; - private GPUInstanceDataBufferGrower.GPUResources m_GrowerResources; - - internal CommandBuffer m_CmdBuffer; - - private SphericalHarmonicsL2 m_CachedAmbientProbe; - - private float m_SmallMeshScreenPercentage; - - private GPUDrivenLODGroupDataCallback m_UpdateLODGroupCallback; - private GPUDrivenLODGroupDataCallback m_TransformLODGroupCallback; - - private OcclusionCullingCommon m_OcclusionCullingCommon; - private DebugRendererBatcherStats m_DebugStats; - - internal OcclusionCullingCommon occlusionCullingCommon { get => m_OcclusionCullingCommon; } - internal DebugRendererBatcherStats debugStats { get => m_DebugStats; } - - public RenderersBatchersContext(in RenderersBatchersContextDesc desc, GPUDrivenProcessor gpuDrivenProcessor, GPUResidentDrawerResources resources) - { - m_Resources = resources; - m_GPUDrivenProcessor = gpuDrivenProcessor; - - RenderersParameters.Flags rendererParametersFlags = RenderersParameters.Flags.None; - if (desc.enableBoundingSpheresInstanceData) - rendererParametersFlags |= RenderersParameters.Flags.UseBoundingSphereParameter; - - m_InstanceDataBuffer = RenderersParameters.CreateInstanceDataBuffer(rendererParametersFlags, desc.instanceNumInfo); - m_RenderersParameters = new RenderersParameters(m_InstanceDataBuffer); - m_LODGroupDataPool = new LODGroupDataPool(resources, desc.instanceNumInfo.GetInstanceNum(InstanceType.MeshRenderer), desc.supportDitheringCrossFade); - m_UploadResources = new GPUInstanceDataBufferUploader.GPUResources(); - m_UploadResources.LoadShaders(resources); - - m_GrowerResources = new GPUInstanceDataBufferGrower.GPUResources(); - m_GrowerResources.LoadShaders(resources); - - m_CmdBuffer = new CommandBuffer(); - m_CmdBuffer.name = "GPUCullingCommands"; - - m_CachedAmbientProbe = RenderSettings.ambientProbe; - - m_InstanceDataSystem = new InstanceDataSystem(desc.instanceNumInfo.GetTotalInstanceNum(), desc.enableBoundingSpheresInstanceData, resources); - m_SmallMeshScreenPercentage = desc.smallMeshScreenPercentage; - - m_UpdateLODGroupCallback = UpdateLODGroupData; - m_TransformLODGroupCallback = TransformLODGroupData; - - m_OcclusionCullingCommon = new OcclusionCullingCommon(); - m_OcclusionCullingCommon.Init(resources); - m_DebugStats = desc.enableCullerDebugStats ? new DebugRendererBatcherStats() : null; - } - - public void Dispose() - { - NativeArray.ReadOnly rendererGroupIDs = m_InstanceDataSystem.sharedInstanceData.rendererGroupIDs; - - if (rendererGroupIDs.Length > 0) - m_GPUDrivenProcessor.DisableGPUDrivenRendering(rendererGroupIDs); - - m_InstanceDataSystem.Dispose(); - - m_CmdBuffer.Release(); - m_GrowerResources.Dispose(); - m_UploadResources.Dispose(); - m_LODGroupDataPool.Dispose(); - m_InstanceDataBuffer.Dispose(); - - m_UpdateLODGroupCallback = null; - m_TransformLODGroupCallback = null; - m_DebugStats?.Dispose(); - m_DebugStats = null; - m_OcclusionCullingCommon?.Dispose(); - m_OcclusionCullingCommon = null; - } - - public int GetMaxInstancesOfType(InstanceType instanceType) - { - return m_InstanceDataSystem.GetMaxInstancesOfType(instanceType); - } - - public int GetAliveInstancesOfType(InstanceType instanceType) - { - return m_InstanceDataSystem.GetAliveInstancesOfType(instanceType); - } - - public void GrowInstanceBuffer(in InstanceNumInfo instanceNumInfo) - { - using (var grower = new GPUInstanceDataBufferGrower(m_InstanceDataBuffer, instanceNumInfo)) - { - var newInstanceDataBuffer = grower.SubmitToGpu(ref m_GrowerResources); - - if (newInstanceDataBuffer != m_InstanceDataBuffer) - { - if (m_InstanceDataBuffer != null) - m_InstanceDataBuffer.Dispose(); - - m_InstanceDataBuffer = newInstanceDataBuffer; - } - } - - m_RenderersParameters = new RenderersParameters(m_InstanceDataBuffer); - } - - private void EnsureInstanceBufferCapacity() - { - const int kMeshRendererGrowNum = 1024; - const int kSpeedTreeGrowNum = 256; - - int maxCPUMeshRendererNum = m_InstanceDataSystem.GetMaxInstancesOfType(InstanceType.MeshRenderer); - int maxCPUSpeedTreeNum = m_InstanceDataSystem.GetMaxInstancesOfType(InstanceType.SpeedTree); - - int maxGPUMeshRendererInstances = m_InstanceDataBuffer.instanceNumInfo.GetInstanceNum(InstanceType.MeshRenderer); - int maxGPUSpeedTreeInstances = m_InstanceDataBuffer.instanceNumInfo.GetInstanceNum(InstanceType.SpeedTree); - - bool needToGrow = false; - - if(maxCPUMeshRendererNum > maxGPUMeshRendererInstances) - { - needToGrow = true; - maxGPUMeshRendererInstances = maxCPUMeshRendererNum + kMeshRendererGrowNum; - } - if(maxCPUSpeedTreeNum > maxGPUSpeedTreeInstances) - { - needToGrow = true; - maxGPUSpeedTreeInstances = maxCPUSpeedTreeNum + kSpeedTreeGrowNum; - } - - if (needToGrow) - GrowInstanceBuffer(new InstanceNumInfo(meshRendererNum: maxGPUMeshRendererInstances, speedTreeNum: maxGPUSpeedTreeInstances)); - } - - private void UpdateLODGroupData(in GPUDrivenLODGroupData lodGroupData) - { - Profiler.BeginSample("Convert LODGroups To BRG"); - - m_LODGroupDataPool.UpdateLODGroupData(lodGroupData); - - Profiler.EndSample(); - } - - private void TransformLODGroupData(in GPUDrivenLODGroupData lodGroupData) - { - Profiler.BeginSample("Transform LODGroups"); - - m_LODGroupDataPool.UpdateLODGroupTransformData(lodGroupData); - - Profiler.EndSample(); - } - - public void DestroyLODGroups(NativeArray destroyed) - { - if (destroyed.Length == 0) - return; - - m_LODGroupDataPool.FreeLODGroupData(destroyed); - } - - public void UpdateLODGroups(NativeArray changedID) - { - if (changedID.Length == 0) - return; - - m_GPUDrivenProcessor.DispatchLODGroupData(changedID, m_UpdateLODGroupCallback); - } - - public void ReallocateAndGetInstances(in GPUDrivenRendererGroupData rendererData, NativeArray instances) - { - m_InstanceDataSystem.ReallocateAndGetInstances(rendererData, instances); - - EnsureInstanceBufferCapacity(); - } - - public JobHandle ScheduleUpdateInstanceDataJob(NativeArray instances, in GPUDrivenRendererGroupData rendererData) - { - return m_InstanceDataSystem.ScheduleUpdateInstanceDataJob(instances, rendererData, m_LODGroupDataPool.lodGroupDataHash); - } - - public void FreeRendererGroupInstances(NativeArray rendererGroupsID) - { - m_InstanceDataSystem.FreeRendererGroupInstances(rendererGroupsID); - } - - public void FreeInstances(NativeArray instances) - { - m_InstanceDataSystem.FreeInstances(instances); - } - - public JobHandle ScheduleQueryRendererGroupInstancesJob(NativeArray rendererGroupIDs, NativeArray instances) - { - return m_InstanceDataSystem.ScheduleQueryRendererGroupInstancesJob(rendererGroupIDs, instances); - } - - public JobHandle ScheduleQueryRendererGroupInstancesJob(NativeArray rendererGroupIDs, NativeList instances) - { - return m_InstanceDataSystem.ScheduleQueryRendererGroupInstancesJob(rendererGroupIDs, instances); - } - - public JobHandle ScheduleQueryRendererGroupInstancesJob(NativeArray rendererGroupIDs, NativeArray instancesOffset, NativeArray instancesCount, NativeList instances) - { - return m_InstanceDataSystem.ScheduleQueryRendererGroupInstancesJob(rendererGroupIDs, instancesOffset, instancesCount, instances); - } - - public JobHandle ScheduleQueryMeshInstancesJob(NativeArray sortedMeshIDs, NativeList instances) - { - return m_InstanceDataSystem.ScheduleQuerySortedMeshInstancesJob(sortedMeshIDs, instances); - } - - public void ChangeInstanceBufferVersion() - { - ++m_InstanceDataBuffer.version; - } - - public GPUInstanceDataBufferUploader CreateDataBufferUploader(int capacity, InstanceType instanceType) - { - //@ This is not quite efficient as we will allocate all the parameters/descriptions of an certain type but write only some of them later. - //@ We should allow to preallocate space only for needed parameters/descriptions. - return new GPUInstanceDataBufferUploader(m_InstanceDataBuffer.descriptions, capacity, instanceType); - } - - public void SubmitToGpu(NativeArray instances, ref GPUInstanceDataBufferUploader uploader, bool submitOnlyWrittenParams) - { - uploader.SubmitToGpu(m_InstanceDataBuffer, instances, ref m_UploadResources, submitOnlyWrittenParams); - } - - public void SubmitToGpu(NativeArray gpuInstanceIndices, ref GPUInstanceDataBufferUploader uploader, bool submitOnlyWrittenParams) - { - uploader.SubmitToGpu(m_InstanceDataBuffer, gpuInstanceIndices, ref m_UploadResources, submitOnlyWrittenParams); - } - - public void InitializeInstanceTransforms(NativeArray instances, NativeArray localToWorldMatrices, NativeArray prevLocalToWorldMatrices) - { - if (instances.Length == 0) - return; - - m_InstanceDataSystem.InitializeInstanceTransforms(instances, localToWorldMatrices, prevLocalToWorldMatrices, m_RenderersParameters, m_InstanceDataBuffer); - ChangeInstanceBufferVersion(); - } - - public void UpdateInstanceTransforms(NativeArray instances, NativeArray localToWorldMatrices) - { - if(instances.Length == 0) - return; - - m_InstanceDataSystem.UpdateInstanceTransforms(instances, localToWorldMatrices, m_RenderersParameters, m_InstanceDataBuffer); - ChangeInstanceBufferVersion(); - } - - public void UpdateAmbientProbeAndGpuBuffer(bool forceUpdate) - { - if (forceUpdate || m_CachedAmbientProbe != RenderSettings.ambientProbe) - { - m_CachedAmbientProbe = RenderSettings.ambientProbe; - m_InstanceDataSystem.UpdateAllInstanceProbes(m_RenderersParameters, m_InstanceDataBuffer); - ChangeInstanceBufferVersion(); - } - } - - public void UpdateInstanceWindDataHistory(NativeArray gpuInstanceIndices) - { - if (gpuInstanceIndices.Length == 0) - return; - - m_InstanceDataSystem.UpdateInstanceWindDataHistory(gpuInstanceIndices, m_RenderersParameters, m_InstanceDataBuffer); - ChangeInstanceBufferVersion(); - } - - // This should be called at the end of the frame loop to properly update motion vectors. - public void UpdateInstanceMotions() - { - m_InstanceDataSystem.UpdateInstanceMotions(m_RenderersParameters, m_InstanceDataBuffer); - ChangeInstanceBufferVersion(); - } - - public void TransformLODGroups(NativeArray lodGroupsID) - { - if (lodGroupsID.Length == 0) - return; - - m_GPUDrivenProcessor.DispatchLODGroupData(lodGroupsID, m_TransformLODGroupCallback); - } - - public void UpdatePerFrameInstanceVisibility(in ParallelBitArray compactedVisibilityMasks) - { - m_InstanceDataSystem.UpdatePerFrameInstanceVisibility(compactedVisibilityMasks); - } - - public JobHandle ScheduleCollectInstancesLODGroupAndMasksJob(NativeArray instances, NativeArray lodGroupAndMasks) - { - return m_InstanceDataSystem.ScheduleCollectInstancesLODGroupAndMasksJob(instances, lodGroupAndMasks); - } - - public InstanceHandle GetRendererInstanceHandle(EntityId rendererID) - { - var rendererIDs = new NativeArray(1, Allocator.TempJob); - var instances = new NativeArray(1, Allocator.TempJob); - - rendererIDs[0] = rendererID; - - m_InstanceDataSystem.ScheduleQueryRendererGroupInstancesJob(rendererIDs, instances).Complete(); - - InstanceHandle instance = instances[0]; - - rendererIDs.Dispose(); - instances.Dispose(); - - return instance; - } - - public void GetVisibleTreeInstances(in ParallelBitArray compactedVisibilityMasks, in ParallelBitArray processedBits, NativeList visibeTreeRendererIDs, - NativeList visibeTreeInstances, bool becomeVisibleOnly, out int becomeVisibeTreeInstancesCount) - { - m_InstanceDataSystem.GetVisibleTreeInstances(compactedVisibilityMasks, processedBits, visibeTreeRendererIDs, visibeTreeInstances, becomeVisibleOnly, out becomeVisibeTreeInstancesCount); - } - - public GPUInstanceDataBuffer GetInstanceDataBuffer() - { - return m_InstanceDataBuffer; - } - - public void UpdateFrame() - { - m_OcclusionCullingCommon.UpdateFrame(); - if (m_DebugStats != null) - m_OcclusionCullingCommon.UpdateOccluderStats(m_DebugStats); - } - - public void FreePerCameraInstanceData(NativeArray cameraIDs) - { - m_InstanceDataSystem.DeallocatePerCameraInstanceData(cameraIDs); - } - - public void UpdateCameras(NativeArray cameraIDs) - { - m_InstanceDataSystem.AllocatePerCameraInstanceData(cameraIDs); - } - -#if UNITY_EDITOR - public void UpdateSelectedInstances(NativeArray instances) - { - m_InstanceDataSystem.UpdateSelectedInstances(instances); - } -#endif - } -} diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/RenderersParameters.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/RenderersParameters.cs deleted file mode 100644 index c66a6ba531d..00000000000 --- a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/RenderersParameters.cs +++ /dev/null @@ -1,144 +0,0 @@ -using System; -using System.Collections.Generic; -using UnityEngine.Assertions; -using Unity.Collections; -using Unity.Collections.LowLevel.Unsafe; -using Unity.Jobs; -using Unity.Mathematics; - -namespace UnityEngine.Rendering -{ - [Flags] - internal enum InstanceComponentGroup : uint - { - Default = 1 << 0, - Wind = 1 << 1, - LightProbe = 1 << 2, - Lightmap = 1 << 3, - - DefaultWind = Default | Wind, - DefaultLightProbe = Default | LightProbe, - DefaultLightmap = Default | Lightmap, - DefaultWindLightProbe = Default | Wind | LightProbe, - DefaultWindLightmap = Default | Wind | Lightmap, - } - - internal struct RenderersParameters - { - static private int s_uintSize = UnsafeUtility.SizeOf(); - - [Flags] - public enum Flags - { - None = 0, - UseBoundingSphereParameter = 1 << 0 - } - - public static class ParamNames - { - public static readonly int _BaseColor = Shader.PropertyToID("_BaseColor"); - public static readonly int unity_SpecCube0_HDR = Shader.PropertyToID("unity_SpecCube0_HDR"); - public static readonly int unity_SHCoefficients = Shader.PropertyToID("unity_SHCoefficients"); - public static readonly int unity_LightmapST = Shader.PropertyToID("unity_LightmapST"); - public static readonly int unity_ObjectToWorld = Shader.PropertyToID("unity_ObjectToWorld"); - public static readonly int unity_WorldToObject = Shader.PropertyToID("unity_WorldToObject"); - public static readonly int unity_MatrixPreviousM = Shader.PropertyToID("unity_MatrixPreviousM"); - public static readonly int unity_MatrixPreviousMI = Shader.PropertyToID("unity_MatrixPreviousMI"); - public static readonly int unity_WorldBoundingSphere = Shader.PropertyToID("unity_WorldBoundingSphere"); - public static readonly int unity_RendererUserValuesPropertyEntry = Shader.PropertyToID("unity_RendererUserValuesPropertyEntry"); - - public static readonly int[] DOTS_ST_WindParams = new int[(int)SpeedTreeWindParamIndex.MaxWindParamsCount]; - public static readonly int[] DOTS_ST_WindHistoryParams = new int[(int)SpeedTreeWindParamIndex.MaxWindParamsCount]; - - static ParamNames() - { - for(int i = 0; i < (int)SpeedTreeWindParamIndex.MaxWindParamsCount; ++i) - { - DOTS_ST_WindParams[i] = Shader.PropertyToID($"DOTS_ST_WindParam{i}"); - DOTS_ST_WindHistoryParams[i] = Shader.PropertyToID($"DOTS_ST_WindHistoryParam{i}"); - } - } - } - - public static GPUInstanceDataBuffer CreateInstanceDataBuffer(Flags flags, in InstanceNumInfo instanceNumInfo) - { - using (var builder = new GPUInstanceDataBufferBuilder()) - { - builder.AddComponent(ParamNames._BaseColor, isOverriden: false, isPerInstance: false, InstanceType.MeshRenderer); - builder.AddComponent(ParamNames.unity_SpecCube0_HDR, isOverriden: false, isPerInstance: false, InstanceType.MeshRenderer); - builder.AddComponent(ParamNames.unity_SHCoefficients, isOverriden: true, isPerInstance: true, InstanceType.MeshRenderer, InstanceComponentGroup.LightProbe); - builder.AddComponent(ParamNames.unity_LightmapST, isOverriden: true, isPerInstance: true, InstanceType.MeshRenderer, InstanceComponentGroup.Lightmap); - builder.AddComponent(ParamNames.unity_ObjectToWorld, isOverriden: true, isPerInstance: true, InstanceType.MeshRenderer); - builder.AddComponent(ParamNames.unity_WorldToObject, isOverriden: true, isPerInstance: true, InstanceType.MeshRenderer); - builder.AddComponent(ParamNames.unity_MatrixPreviousM, isOverriden: true, isPerInstance: true, InstanceType.MeshRenderer); - builder.AddComponent(ParamNames.unity_MatrixPreviousMI, isOverriden: true, isPerInstance: true, InstanceType.MeshRenderer); - builder.AddComponent(ParamNames.unity_RendererUserValuesPropertyEntry, isOverriden: true, isPerInstance: true, InstanceType.MeshRenderer); - if ((flags & Flags.UseBoundingSphereParameter) != 0) - { - builder.AddComponent(ParamNames.unity_WorldBoundingSphere, isOverriden: true, isPerInstance: true, InstanceType.MeshRenderer); - } - - //@ Most of SpeedTree parameters could be packed in fp16. Do later. - for (int i = 0; i < (int)SpeedTreeWindParamIndex.MaxWindParamsCount; ++i) - builder.AddComponent(ParamNames.DOTS_ST_WindParams[i], isOverriden: true, isPerInstance: true, InstanceType.SpeedTree, InstanceComponentGroup.Wind); - for (int i = 0; i < (int)SpeedTreeWindParamIndex.MaxWindParamsCount; ++i) - builder.AddComponent(ParamNames.DOTS_ST_WindHistoryParams[i], isOverriden: true, isPerInstance: true, InstanceType.SpeedTree, InstanceComponentGroup.Wind); - - return builder.Build(instanceNumInfo); - } - } - - public struct ParamInfo - { - public int index; - public int gpuAddress; - public int uintOffset; - public bool valid => index != 0; - } - - public ParamInfo lightmapScale; - public ParamInfo localToWorld; - public ParamInfo worldToLocal; - public ParamInfo matrixPreviousM; - public ParamInfo matrixPreviousMI; - public ParamInfo shCoefficients; - public ParamInfo rendererUserValues; - public ParamInfo boundingSphere; - - public ParamInfo[] windParams; - public ParamInfo[] windHistoryParams; - - public RenderersParameters(in GPUInstanceDataBuffer instanceDataBuffer) - { - ParamInfo GetParamInfo(in GPUInstanceDataBuffer instanceDataBuffer, int paramNameIdx, bool assertOnFail = true) - { - int gpuAddress = instanceDataBuffer.GetGpuAddress(paramNameIdx, assertOnFail); - int index = instanceDataBuffer.GetPropertyIndex(paramNameIdx, assertOnFail); - return new ParamInfo() - { - index = index, - gpuAddress = gpuAddress, - uintOffset = gpuAddress / s_uintSize - }; - } - - lightmapScale = GetParamInfo(instanceDataBuffer, ParamNames.unity_LightmapST); - localToWorld = GetParamInfo(instanceDataBuffer, ParamNames.unity_ObjectToWorld); - worldToLocal = GetParamInfo(instanceDataBuffer, ParamNames.unity_WorldToObject); - matrixPreviousM = GetParamInfo(instanceDataBuffer, ParamNames.unity_MatrixPreviousM); - matrixPreviousMI = GetParamInfo(instanceDataBuffer, ParamNames.unity_MatrixPreviousMI); - shCoefficients = GetParamInfo(instanceDataBuffer, ParamNames.unity_SHCoefficients); - rendererUserValues = GetParamInfo(instanceDataBuffer, ParamNames.unity_RendererUserValuesPropertyEntry); - boundingSphere = GetParamInfo(instanceDataBuffer, ParamNames.unity_WorldBoundingSphere, assertOnFail: false); - - windParams = new ParamInfo[(int)SpeedTreeWindParamIndex.MaxWindParamsCount]; - windHistoryParams = new ParamInfo[(int)SpeedTreeWindParamIndex.MaxWindParamsCount]; - - for (int i = 0; i < (int)SpeedTreeWindParamIndex.MaxWindParamsCount; ++i) - { - windParams[i] = GetParamInfo(instanceDataBuffer, ParamNames.DOTS_ST_WindParams[i]); - windHistoryParams[i] = GetParamInfo(instanceDataBuffer, ParamNames.DOTS_ST_WindHistoryParams[i]); - } - } - } -} diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/SceneProcessors.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/SceneProcessors.meta new file mode 100644 index 00000000000..5075bac3ff3 --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/SceneProcessors.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d05cf5ed62f722b4b948cad57b9da7f3 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/SceneProcessors/LODGroupProcessor.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/SceneProcessors/LODGroupProcessor.cs new file mode 100644 index 00000000000..c14d57421ff --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/SceneProcessors/LODGroupProcessor.cs @@ -0,0 +1,89 @@ +using Unity.Collections; +using Unity.Mathematics; +using UnityEngine.Assertions; +using UnityEngine.Profiling; + +namespace UnityEngine.Rendering +{ + internal class LODGroupProcessor + { + private GPUDrivenProcessor m_GPUDrivenProcessor; + private LODGroupDataSystem m_LODGroupDataSystem; + + public LODGroupProcessor(GPUDrivenProcessor gpuDrivenProcessor, GPUResidentContext context) + { + m_GPUDrivenProcessor = gpuDrivenProcessor; + m_LODGroupDataSystem = context.lodGroupDataSystem; + } + + public void DestroyInstances(NativeArray destroyedIDs) + { + Profiler.BeginSample("DestroyLODGroupInstances"); + m_LODGroupDataSystem.FreeLODGroups(destroyedIDs); + Profiler.EndSample(); + } + + public void ProcessGameObjectChanges(NativeArray changedLODGroups, bool transformOnly) + { + m_GPUDrivenProcessor.DispatchLODGroupData(changedLODGroups, transformOnly, ProcessGameObjectUpdateBatch); + } + + public void ProcessUpdateBatch(in LODGroupUpdateBatch updateBatch) + { + if (updateBatch.TotalLength == 0) + return; + + Profiler.BeginSample("ProcessLODGroupUpdateBatch"); + + if (updateBatch.updateMode == LODGroupUpdateBatchMode.MightIncludeNewInstances) + { + Assert.IsTrue(updateBatch.HasAnyComponent(LODGroupComponentMask.GroupSettings)); + Assert.IsTrue(updateBatch.HasAnyComponent(LODGroupComponentMask.WorldSpaceReferencePoint)); + Assert.IsTrue(updateBatch.HasAnyComponent(LODGroupComponentMask.WorldSpaceSize)); + Assert.IsTrue(updateBatch.HasAnyComponent(LODGroupComponentMask.LODBuffer)); + + NativeArray instances = m_LODGroupDataSystem.GetOrAllocateInstances(updateBatch, Allocator.TempJob); + m_LODGroupDataSystem.UpdateLODGroupData(updateBatch, instances); + instances.Dispose(); + } + else if (updateBatch.updateMode == LODGroupUpdateBatchMode.OnlyKnownInstances) + { + // This mode only support transform-only updates for now. + Assert.IsTrue(updateBatch.HasAnyComponent(LODGroupComponentMask.WorldSpaceSize)); + Assert.IsTrue(updateBatch.HasAnyComponent(LODGroupComponentMask.WorldSpaceReferencePoint)); + + m_LODGroupDataSystem.UpdateLODGroupTransforms(updateBatch); + } + + Profiler.EndSample(); + } + + void ProcessGameObjectUpdateBatch(in GPUDrivenLODGroupData inputData) + { + if (inputData.invalidLODGroup.Length > 0) + DestroyInstances(inputData.invalidLODGroup); + + if (inputData.lodGroup.Length == 0) + return; + + var updateMode = inputData.transformOnly ? LODGroupUpdateBatchMode.OnlyKnownInstances : LODGroupUpdateBatchMode.MightIncludeNewInstances; + + var updateBatch = new LODGroupUpdateBatch(new LODGroupUpdateSection + { + instanceIDs = inputData.lodGroup, + worldSpaceReferencePoints = inputData.worldSpaceReferencePoint.Reinterpret(), + worldSpaceSizes = inputData.worldSpaceSize, + lodGroupSettings = inputData.groupSettings, + forceLODMask = inputData.forceLODMask, + lodBuffers = inputData.lodBuffer + }, + updateMode, + Allocator.TempJob); + + updateBatch.Validate(); + ProcessUpdateBatch(updateBatch); + + updateBatch.Dispose(); + } + } +} diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/SceneProcessors/LODGroupProcessor.cs.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/SceneProcessors/LODGroupProcessor.cs.meta new file mode 100644 index 00000000000..8ff4263dd78 --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/SceneProcessors/LODGroupProcessor.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 7e8f0471be47d8f448c16883505d19bc \ No newline at end of file diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/SceneProcessors/LODGroupUpdateBatch.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/SceneProcessors/LODGroupUpdateBatch.cs new file mode 100644 index 00000000000..f3dbc9dd501 --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/SceneProcessors/LODGroupUpdateBatch.cs @@ -0,0 +1,266 @@ +using System; +using Unity.Collections; +using Unity.Mathematics; +using Unity.Profiling; +using UnityEngine.Assertions; + +namespace UnityEngine.Rendering +{ + [Flags] + internal enum LODGroupComponentMask + { + None = 0, + WorldSpaceReferencePoint = 1 << 0, + WorldSpaceSize = 1 << 1, + GroupSettings = 1 << 2, + ForceLOD = 1 << 3, + LODBuffer = 1 << 4, + + PermutationCount = 1 << 5, + AllBits = PermutationCount - 1 + } + + internal enum LODGroupUpdateBatchMode + { + MightIncludeNewInstances, + OnlyKnownInstances, + } + + internal struct LODGroupUpdateSection + { + public NativeArray instanceIDs; + public NativeArray worldSpaceReferencePoints; + public NativeArray worldSpaceSizes; + public NativeArray lodGroupSettings; + public NativeArray forceLODMask; + public NativeArray lodBuffers; + + public LODGroupComponentMask BuildComponentMask() + { + LODGroupComponentMask mask = LODGroupComponentMask.None; + + if (worldSpaceReferencePoints.IsCreated) + mask |= LODGroupComponentMask.WorldSpaceReferencePoint; + + if (worldSpaceSizes.IsCreated) + mask |= LODGroupComponentMask.WorldSpaceSize; + + if (lodGroupSettings.IsCreated) + mask |= LODGroupComponentMask.GroupSettings; + + if (forceLODMask.IsCreated) + mask |= LODGroupComponentMask.ForceLOD; + + if (lodBuffers.IsCreated) + mask |= LODGroupComponentMask.LODBuffer; + + return mask; + } + } + + // This struct stores array views of all the data we want to upload to the RenderWorld for LODGroup-like object. + // The data does not have to come from the managed Unity LODGroup. + // It can come from anywhere, but they are intended to represent something analogous to a LODGroup. + // This struct works in the same way as MeshRendererUpdateBatch. See this struct for more details on the way it works. + internal struct LODGroupUpdateBatch : IDisposable + { + public JaggedSpan instanceIDs; + public JaggedSpan worldSpaceReferencePoints; + public JaggedSpan worldSpaceSizes; + public JaggedSpan lodGroupSettings; + public JaggedSpan forceLODMask; + public JaggedSpan lodBuffers; + public LODGroupComponentMask componentMask; + public LODGroupUpdateBatchMode updateMode; + + public LODGroupUpdateBatch(LODGroupComponentMask componentMask, LODGroupUpdateBatchMode updateMode, int initialCapacity, Allocator allocator) + { + this.updateMode = updateMode; + this.componentMask = componentMask; + this.instanceIDs = new JaggedSpan(initialCapacity, allocator); + this.worldSpaceReferencePoints = new JaggedSpan(componentMask.HasAnyBit(LODGroupComponentMask.WorldSpaceReferencePoint) ? initialCapacity : 0, allocator); + this.worldSpaceSizes = new JaggedSpan(componentMask.HasAnyBit(LODGroupComponentMask.WorldSpaceSize) ? initialCapacity : 0, allocator); + this.lodGroupSettings = new JaggedSpan(componentMask.HasAnyBit(LODGroupComponentMask.GroupSettings) ? initialCapacity : 0, allocator); + this.forceLODMask = new JaggedSpan(componentMask.HasAnyBit(LODGroupComponentMask.ForceLOD) ? initialCapacity : 0, allocator); + this.lodBuffers = new JaggedSpan(componentMask.HasAnyBit(LODGroupComponentMask.LODBuffer) ? initialCapacity : 0, allocator); + } + + public LODGroupUpdateBatch(in LODGroupUpdateSection section, LODGroupUpdateBatchMode updateMode, Allocator allocator) : + this(section.BuildComponentMask(), updateMode, 1, allocator) + { + AddSection(section); + } + + public void Dispose() + { + instanceIDs.Dispose(); + worldSpaceReferencePoints.Dispose(); + worldSpaceSizes.Dispose(); + lodGroupSettings.Dispose(); + forceLODMask.Dispose(); + lodBuffers.Dispose(); + } + + public int SectionCount => instanceIDs.sectionCount; + + public int TotalLength => instanceIDs.totalLength; + + public int GetSectionLength(int sectionIndex) => instanceIDs[sectionIndex].Length; + + public bool HasAnyComponent(LODGroupComponentMask bits) => componentMask.HasAnyBit(bits); + + public void AddSection(in LODGroupUpdateSection section) + { + Assert.IsTrue(section.BuildComponentMask() == componentMask); + + instanceIDs.Add(section.instanceIDs); + + if (HasAnyComponent(LODGroupComponentMask.GroupSettings)) + { + Assert.IsTrue(section.instanceIDs.Length == section.lodGroupSettings.Length); + lodGroupSettings.Add(section.lodGroupSettings); + } + + if (HasAnyComponent(LODGroupComponentMask.WorldSpaceReferencePoint)) + { + Assert.IsTrue(section.instanceIDs.Length == section.worldSpaceReferencePoints.Length); + worldSpaceReferencePoints.Add(section.worldSpaceReferencePoints); + } + + if (HasAnyComponent(LODGroupComponentMask.WorldSpaceSize)) + { + Assert.IsTrue(section.instanceIDs.Length == section.worldSpaceSizes.Length); + worldSpaceSizes.Add(section.worldSpaceSizes); + } + + if (HasAnyComponent(LODGroupComponentMask.ForceLOD)) + { + Assert.IsTrue(section.instanceIDs.Length == section.forceLODMask.Length); + forceLODMask.Add(section.forceLODMask); + } + + if (HasAnyComponent(LODGroupComponentMask.LODBuffer)) + { + Assert.IsTrue(section.instanceIDs.Length == section.lodBuffers.Length); + lodBuffers.Add(section.lodBuffers); + } + } + + internal void Validate() + { + // Disable "Unreachable code detected" warning +#pragma warning disable CS0162 + if (!GPUResidentDrawer.EnableValidation) + return; + + using (new ProfilerMarker("LODGroupUpdateBatch.Validate").Auto()) + { + ValidateImpl(); + } +#pragma warning restore CS0162 + } + + private void ValidateImpl() + { + if (!ValidateEmptyOrSameLayout(LODGroupComponentMask.WorldSpaceReferencePoint, worldSpaceReferencePoints, instanceIDs)) + return; + + if (!ValidateEmptyOrSameLayout(LODGroupComponentMask.WorldSpaceSize, worldSpaceSizes, instanceIDs)) + return; + + if (!ValidateEmptyOrSameLayout(LODGroupComponentMask.GroupSettings, lodGroupSettings, instanceIDs)) + return; + + if (!ValidateEmptyOrSameLayout(LODGroupComponentMask.LODBuffer, lodBuffers, instanceIDs)) + return; + + if (updateMode == LODGroupUpdateBatchMode.MightIncludeNewInstances) + { + if (!ValidateRequiredComponentIsPresent(LODGroupComponentMask.WorldSpaceReferencePoint)) + return; + + if (!ValidateRequiredComponentIsPresent(LODGroupComponentMask.WorldSpaceSize)) + return; + + if (!ValidateRequiredComponentIsPresent(LODGroupComponentMask.GroupSettings)) + return; + + if (!ValidateRequiredComponentIsPresent(LODGroupComponentMask.LODBuffer)) + return; + } + + // This mode is only assumed to be used for transform-only updates for now. + // In the future it could be extended so that each component can be incrementally updated individually. + if (updateMode == LODGroupUpdateBatchMode.OnlyKnownInstances) + { + if (!ValidateRequiredComponentIsPresent(LODGroupComponentMask.WorldSpaceReferencePoint)) + return; + + if (!ValidateRequiredComponentIsPresent(LODGroupComponentMask.WorldSpaceSize)) + return; + + if (HasAnyComponent(LODGroupComponentMask.ForceLOD)) + { + Debug.LogError("LODGroupComponentMask.ForceLOD component is not supported in LODGroupUpdateBatchMode.OnlyKnownInstances mode"); + return; + } + + if (HasAnyComponent(LODGroupComponentMask.GroupSettings)) + { + Debug.LogError("LODGroupComponentMask.GroupSettings component is not supported in LODGroupUpdateBatchMode.OnlyKnownInstances mode"); + return; + } + + if (HasAnyComponent(LODGroupComponentMask.LODBuffer)) + { + Debug.LogError("LODGroupComponentMask.LODBuffer component is not supported in LODGroupUpdateBatchMode.OnlyKnownInstances mode"); + return; + } + } + + if (!DeepValidateImpl()) + return; + } + + private bool DeepValidateImpl() + { + // Disable "Unreachable code detected" warning +#pragma warning disable CS0162 + if (!GPUResidentDrawer.EnableDeepValidation) + return true; + + using (new ProfilerMarker("LODGroupUpdateBatch.DeepValidate").Auto()) + { + if (instanceIDs.HasDuplicates()) + { + Debug.LogError("MeshRendererUpdateBatch contains dupplicate instanceIDs."); + return false; + } + } + + return true; +#pragma warning restore CS0162 + } + private bool ValidateRequiredComponentIsPresent(LODGroupComponentMask component) + { + if (!HasAnyComponent(component)) + { + Debug.LogError($"Invalid LODGroupUpdateBatch. {component} was not provided. This is required when using the update mode {updateMode}."); + return false; + } + + return true; + } + + private bool ValidateEmptyOrSameLayout(LODGroupComponentMask component, JaggedSpan components, JaggedSpan instanceIDs) where T : unmanaged + { + if (!components.isEmpty && !components.HasSameLayout(instanceIDs)) + { + Debug.LogError($"Invalid LODGroupUpdateBatch. {component} jagged span has an unexpected layout."); + return false; + } + + return true; + } + } +} diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/SceneProcessors/LODGroupUpdateBatch.cs.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/SceneProcessors/LODGroupUpdateBatch.cs.meta new file mode 100644 index 00000000000..b799220f10b --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/SceneProcessors/LODGroupUpdateBatch.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 5892965ba0c4a7a4daeb41803ec2a0dc \ No newline at end of file diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/SceneProcessors/MeshRendererProcessor.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/SceneProcessors/MeshRendererProcessor.cs new file mode 100644 index 00000000000..3e7e677005d --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/SceneProcessors/MeshRendererProcessor.cs @@ -0,0 +1,626 @@ +using System; +using Unity.Burst; +using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; +using Unity.Jobs; +using Unity.Mathematics; +using Unity.Profiling; +using UnityEngine.Assertions; +using UnityEngine.Profiling; + +namespace UnityEngine.Rendering +{ + internal class MeshRendererProcessor : IDisposable + { + public struct GPUComponentUploadSource + { + public JaggedSpan data; + public GPUComponentHandle component; + public int componentSize; + } + + private GPUDrivenProcessor m_GPUDrivenProcessor; + private InstanceCullingBatcher m_CullingBatcher; + private NativeReference m_ArchetypeManager; + private InstanceDataSystem m_InstanceDataSystem; + private LODGroupDataSystem m_LODGroupDataSystem; + private GPUDrivenRendererDataCallback m_ProcessGameObjectUpdateBatchCallback; + private NativeArray m_CPUUploadBuffer; + private GraphicsBuffer m_GPUUploadBuffer; + + public MeshRendererProcessor(GPUDrivenProcessor gpuDrivenProcessor, GPUResidentContext grdContext) + { + m_GPUDrivenProcessor = gpuDrivenProcessor; + m_CullingBatcher = grdContext.batcher; + m_ArchetypeManager = grdContext.instanceDataSystem.archetypeManager; + m_InstanceDataSystem = grdContext.instanceDataSystem; + m_LODGroupDataSystem = grdContext.lodGroupDataSystem; + + // Create the delegate object in advance to prevent a GC allocation each time we pass the instance method to the GPUDrivenProcessor. + m_ProcessGameObjectUpdateBatchCallback = ProcessGameObjectUpdateBatch; + } + + public void Dispose() + { + m_CPUUploadBuffer.Dispose(); + m_GPUUploadBuffer?.Release(); + } + + public void DestroyInstances(NativeArray destroyedRenderers) + { + if (destroyedRenderers.Length == 0) + return; + + Profiler.BeginSample("DestroyMeshRendererInstances"); + var destroyedInstances = new NativeArray(destroyedRenderers.Length, Allocator.TempJob); + m_InstanceDataSystem.QueryRendererInstances(destroyedRenderers, destroyedInstances); + m_CullingBatcher.DestroyDrawInstances(destroyedInstances); + m_InstanceDataSystem.FreeInstances(destroyedInstances); + destroyedInstances.Dispose(); + Profiler.EndSample(); + } + + public void ProcessGameObjectChanges(NativeArray changedRenderers) + { + m_GPUDrivenProcessor.EnableGPUDrivenRenderingAndDispatchRendererData(changedRenderers, m_ProcessGameObjectUpdateBatchCallback); + } + + public void ProcessGameObjectTransformChanges(in TransformDispatchData transformChanges) + { + var updateBatch = new MeshRendererUpdateBatch(MeshRendererComponentMask.LocalToWorld, + default, + MeshRendererUpdateType.NoStructuralChanges, + MeshRendererUpdateBatch.LightmapUsage.Unknown, + MeshRendererUpdateBatch.BlendProbesUsage.Unknown, + useSharedSceneCullingMask: false, + 1, Allocator.TempJob); + + updateBatch.AddSection(new MeshRendererUpdateSection + { + instanceIDs = transformChanges.transformedID, + localToWorlds = transformChanges.localToWorldMatrices.Reinterpret() + }); + + updateBatch.Validate(); + ProcessUpdateBatch(ref updateBatch); + + updateBatch.Dispose(); + } + + public void ProcessRendererMaterialAndMeshChanges(NativeArray excludedRenderers, + NativeArray changedMaterials, + NativeArray changedMaterialDatas, + NativeArray changedMeshes) + { + if (changedMaterials.Length == 0 && changedMeshes.Length == 0) + return; + + // Update the material/mesh maps and retrieve the IDs of the materials/meshes for which the data actually changed. + Profiler.BeginSample("GetMaterialsAndMeshesWithChangedData"); + NativeHashSet materialsWithChangedData = m_CullingBatcher.UpdateMaterialData(changedMaterials, changedMaterialDatas, Allocator.TempJob); + NativeHashSet meshesWithChangedData = m_CullingBatcher.UpdateMeshData(changedMeshes, Allocator.TempJob); + Profiler.EndSample(); + + if (materialsWithChangedData.Count == 0 && meshesWithChangedData.Count == 0) + { + materialsWithChangedData.Dispose(); + meshesWithChangedData.Dispose(); + return; + } + + var sortedExcludedRenderers = new NativeArray(excludedRenderers, Allocator.TempJob); + if (sortedExcludedRenderers.Length > 0) + { + Profiler.BeginSample("ProcessRendererMaterialAndMeshChanges.Sort"); + sortedExcludedRenderers.Reinterpret().ParallelSort().Complete(); + Profiler.EndSample(); + } + + Profiler.BeginSample("FindRenderersFromMaterialsOrMeshes"); + var (renderersWithChangedMaterials, renderersWithChangeMeshes) = FindRenderersFromMaterialsOrMeshes(sortedExcludedRenderers, + materialsWithChangedData, + meshesWithChangedData, + Allocator.TempJob); + Profiler.EndSample(); + + materialsWithChangedData.Dispose(); + meshesWithChangedData.Dispose(); + sortedExcludedRenderers.Dispose(); + + if (renderersWithChangedMaterials.Length == 0 && renderersWithChangeMeshes.Length == 0) + { + renderersWithChangedMaterials.Dispose(); + renderersWithChangeMeshes.Dispose(); + return; + } + + Profiler.BeginSample("UpdateRenderers"); + var changedMaterialsCount = renderersWithChangedMaterials.Length; + var changedMeshesCount = renderersWithChangeMeshes.Length; + var totalCount = changedMaterialsCount + changedMeshesCount; + + var changedRenderers = new NativeArray(totalCount, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); + NativeArray.Copy(renderersWithChangedMaterials.AsArray(), changedRenderers, changedMaterialsCount); + NativeArray.Copy(renderersWithChangeMeshes.AsArray(), changedRenderers.GetSubArray(changedMaterialsCount, changedMeshesCount), changedMeshesCount); + + var changedInstances = new NativeArray(totalCount, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); + m_InstanceDataSystem.QueryRendererInstances(changedRenderers, changedInstances); + + m_CullingBatcher.BuildBatches(changedInstances); + + changedRenderers.Dispose(); + changedInstances.Dispose(); + renderersWithChangedMaterials.Dispose(); + renderersWithChangeMeshes.Dispose(); + Profiler.EndSample(); + } + + private (NativeList renderersWithMaterials, NativeList renderersWithMeshes) + FindRenderersFromMaterialsOrMeshes(NativeArray sortedExcludeRenderers, + NativeHashSet materials, + NativeHashSet meshes, + Allocator rendererListAllocator) + { + ref RenderWorld renderWorld = ref m_InstanceDataSystem.renderWorld; + var renderersWithMaterials = new NativeList(renderWorld.instanceCount, rendererListAllocator); + var renderersWithMeshes = new NativeList(renderWorld.instanceCount, rendererListAllocator); + + new FindRenderersFromMaterialOrMeshJob + { + renderWorld = renderWorld, + materialIDs = materials, + meshIDs = meshes, + sortedExcludeRendererIDs = sortedExcludeRenderers, + selectedRenderIDsForMaterials = renderersWithMaterials.AsParallelWriter(), + selectedRenderIDsForMeshes = renderersWithMeshes.AsParallelWriter() + } + .ScheduleBatch(renderWorld.instanceIDs.Length, FindRenderersFromMaterialOrMeshJob.k_BatchSize) + .Complete(); + + return (renderersWithMaterials, renderersWithMeshes); + } + + public unsafe void ProcessUpdateBatch(ref MeshRendererUpdateBatch updateBatch) + { + if (updateBatch.TotalLength == 0) + return; + + Profiler.BeginSample("ProcessMeshRendererUpdateBatch"); + + MeshRendererUpdateType updateType = updateBatch.updateType; + bool anyInstanceUseBlendProbes = updateBatch.blendProbesUsage != MeshRendererUpdateBatch.BlendProbesUsage.AllDisabled; + bool updateArchetype = updateType != MeshRendererUpdateType.NoStructuralChanges; + bool hasOnlyKnowInstances = updateType == MeshRendererUpdateType.NoStructuralChanges + || updateType == MeshRendererUpdateType.RecreateOnlyKnownInstances; + + const MeshRendererComponentMask UpdateInstanceDataMask = MeshRendererComponentMask.Mesh + | MeshRendererComponentMask.Material + | MeshRendererComponentMask.SubMeshStartIndex + | MeshRendererComponentMask.LocalBounds + | MeshRendererComponentMask.RendererSettings + | MeshRendererComponentMask.ParentLODGroup + | MeshRendererComponentMask.LODMask + | MeshRendererComponentMask.MeshLodSettings + | MeshRendererComponentMask.Lightmap + | MeshRendererComponentMask.RendererPriority + | MeshRendererComponentMask.SceneCullingMask + | MeshRendererComponentMask.RenderingEnabled; + + bool updateInstanceData = updateType != MeshRendererUpdateType.NoStructuralChanges || updateBatch.HasAnyComponent(UpdateInstanceDataMask); + + const MeshRendererComponentMask UpdateDrawBatchesMask = MeshRendererComponentMask.Mesh + | MeshRendererComponentMask.Material + | MeshRendererComponentMask.SubMeshStartIndex + | MeshRendererComponentMask.RendererSettings + | MeshRendererComponentMask.Lightmap + | MeshRendererComponentMask.RendererPriority; + + bool updateDrawBatches = updateType != MeshRendererUpdateType.NoStructuralChanges || updateBatch.HasAnyComponent(UpdateDrawBatchesMask); + + GPUComponentSet overrideComponentSet = default; + NativeArray componentUploadSources = default; + if (updateBatch.HasAnyComponent(MeshRendererComponentMask.GPUComponent)) + componentUploadSources = BuildGPUComponentOverrideUploadSources(updateBatch, Allocator.TempJob, out overrideComponentSet); + + NativeArray archetypes = default; + if (updateArchetype) + archetypes = ComputeInstanceGPUArchetypes(ref updateBatch, overrideComponentSet, Allocator.TempJob); + + var instances = new NativeArray(updateBatch.TotalLength, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); + if (updateType == MeshRendererUpdateType.OnlyNewInstances) + { + m_InstanceDataSystem.AllocateNewInstances(updateBatch.instanceIDs, instances, archetypes, instances.Length); + } + else if (updateType == MeshRendererUpdateType.MightIncludeNewInstances) + { + int newInstanceCount = 0; + m_InstanceDataSystem.QueryRendererInstances(updateBatch.instanceIDs, instances, new UnsafeAtomicCounter32(&newInstanceCount)); + m_InstanceDataSystem.AllocOrGPUReallocInstances(updateBatch.instanceIDs, instances, archetypes, newInstanceCount); + } + else if (hasOnlyKnowInstances) + { + m_InstanceDataSystem.QueryRendererInstances(updateBatch.instanceIDs, instances); + + if (updateArchetype) + m_InstanceDataSystem.ReallocateExistingGPUInstances(instances, archetypes); + } + + if (!updateArchetype) + ValidateGPUArchetypesDidNotChange(instances, updateBatch, overrideComponentSet); + + if (!anyInstanceUseBlendProbes) + ValidateNoInstanceUsesBlendProbes(instances); + + if (updateInstanceData) + m_InstanceDataSystem.UpdateInstanceData(instances, updateBatch, m_LODGroupDataSystem.lodGroupDataHash); + + if (!overrideComponentSet.isEmpty) + UploadGPUComponentOverrides(overrideComponentSet, componentUploadSources, instances); + + if (hasOnlyKnowInstances) + { + if (updateBatch.HasAnyComponent(MeshRendererComponentMask.LocalToWorld)) + m_InstanceDataSystem.UpdateInstanceTransforms(instances, updateBatch.localToWorlds, anyInstanceUseBlendProbes); + } + else + { + JaggedSpan prevLocalToWorlds = updateBatch.HasAnyComponent(MeshRendererComponentMask.PrevLocalToWorld) + ? updateBatch.prevLocalToWorlds + : updateBatch.localToWorlds; + + m_InstanceDataSystem.InitializeInstanceTransforms(instances, updateBatch.localToWorlds, prevLocalToWorlds, anyInstanceUseBlendProbes); + } + + if (updateDrawBatches) + m_CullingBatcher.RegisterAndBuildBatches(instances, updateBatch); + + instances.Dispose(); + archetypes.Dispose(); + componentUploadSources.Dispose(); + Profiler.EndSample(); + } + + unsafe NativeArray ComputeInstanceGPUArchetypes(ref MeshRendererUpdateBatch updateBatch, GPUComponentSet overrideComponentSet, Allocator allocator) + { + using (new ProfilerMarker("ComputeInstanceGPUArchetypes").Auto()) + { + bool useSharedGPUArchetype = updateBatch.blendProbesUsage != MeshRendererUpdateBatch.BlendProbesUsage.Unknown + && updateBatch.lightmapUsage != MeshRendererUpdateBatch.LightmapUsage.Unknown + && !updateBatch.mightIncludeTrees; + + int archetypeCount = useSharedGPUArchetype ? 1 : updateBatch.TotalLength; + NativeArray archetypes = new NativeArray(archetypeCount, allocator); + + fixed (MeshRendererUpdateBatch* updateBatchPtr = &updateBatch) + { + MeshRendererProcessorBurst.ComputeInstanceGPUArchetypes(m_ArchetypeManager, + m_InstanceDataSystem.defaultGPUComponents, + updateBatchPtr, + overrideComponentSet, + useSharedGPUArchetype, + ref archetypes); + } + + return archetypes; + } + } + + unsafe NativeArray BuildGPUComponentOverrideUploadSources(in MeshRendererUpdateBatch updateBatch, + Allocator allocator, + out GPUComponentSet overrideComponentSet) + { + NativeArray componentUpdates = updateBatch.gpuComponentUpdates; + if (componentUpdates.Length == 0) + { + overrideComponentSet = default; + return default; + } + + using (new ProfilerMarker("BuildGPUComponentOverrideUploadSources").Auto()) + { + NativeArray uploadSources = new NativeArray(componentUpdates.Length, allocator); + GPUComponentSet componentSet = default; + + MeshRendererProcessorBurst.BuildGPUComponentOverrideUploadSources(m_ArchetypeManager, componentUpdates, ref uploadSources, &componentSet); + + overrideComponentSet = componentSet; + return uploadSources; + } + } + + void UploadGPUComponentOverrides(GPUComponentSet componentSet, NativeArray uploadSources, NativeArray instances) + { + Assert.IsTrue(!componentSet.isEmpty); + Assert.IsTrue(uploadSources.Length > 0); + + Profiler.BeginSample("UploadGPUComponentOverrides"); + + GPUInstanceUploadData uploadData = m_InstanceDataSystem.CreateInstanceUploadData(componentSet, instances.Length, Allocator.TempJob); + + EnsureUploadBufferUintCount(uploadData.uploadDataUIntSize); + + NativeArray writeBuffer = m_CPUUploadBuffer.GetSubArray(0, uploadData.uploadDataUIntSize); + + JobHandle allWritesJobHandle = default; + + for (int i = 0; i < uploadSources.Length; i++) + { + ref readonly GPUComponentUploadSource source = ref uploadSources.ElementAt(i); + JobHandle jobHandle = uploadData.ScheduleWriteComponentsJob(source.data, source.component, source.componentSize, writeBuffer); + allWritesJobHandle = JobHandle.CombineDependencies(jobHandle, allWritesJobHandle); + } + + using (new ProfilerMarker("SyncWriteGPUComponentJobs").Auto()) + { + allWritesJobHandle.Complete(); + } + + m_GPUUploadBuffer.SetData(writeBuffer); + + m_InstanceDataSystem.UploadDataToGPU(instances, m_GPUUploadBuffer, uploadData); + + uploadData.Dispose(); + Profiler.EndSample(); + } + + void EnsureUploadBufferUintCount(int uintCount) + { + int currentCPUBufferLength = m_CPUUploadBuffer.IsCreated ? m_CPUUploadBuffer.Length : 0; + int currentGPUBufferLength = m_GPUUploadBuffer != null ? m_GPUUploadBuffer.count : 0; + + Assert.IsTrue(currentCPUBufferLength == currentGPUBufferLength); + int currentUintCount = currentCPUBufferLength; + + if (uintCount > currentUintCount) + { + // At least double on resize + int newUintCount = math.max(currentUintCount * 2, uintCount); + + m_CPUUploadBuffer.Dispose(); + m_GPUUploadBuffer?.Release(); + + m_CPUUploadBuffer = new NativeArray(newUintCount, Allocator.Persistent); + m_GPUUploadBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Raw, newUintCount, sizeof(uint)); + } + + Assert.IsTrue(m_GPUUploadBuffer.count == m_CPUUploadBuffer.Length); + } + + void ProcessGameObjectUpdateBatch(in GPUDrivenMeshRendererData rendererData) + { + if (rendererData.invalidRenderer.Length > 0) + { + m_GPUDrivenProcessor.DisableGPUDrivenRendering(rendererData.invalidRenderer); + DestroyInstances(rendererData.invalidRenderer); + } + + if (rendererData.renderer.Length == 0) + return; + + var gpuComponents = new NativeArray(2, Allocator.Temp); + gpuComponents[0] = new GPUComponent(DefaultShaderPropertyID.unity_LightmapST, UnsafeUtility.SizeOf()); + gpuComponents[1] = new GPUComponent(DefaultShaderPropertyID.unity_RendererUserValuesPropertyEntry, UnsafeUtility.SizeOf()); + + var gpuComponentUpdates = new NativeArray(2, Allocator.Temp); + gpuComponentUpdates[0] = GPUComponentUpdate.FromArray(gpuComponents[0], rendererData.lightmapScaleOffset); + gpuComponentUpdates[1] = GPUComponentUpdate.FromArray(gpuComponents[1], rendererData.rendererUserValues); + + const MeshRendererComponentMask UpdateMask = MeshRendererComponentMask.LocalToWorld + | MeshRendererComponentMask.PrevLocalToWorld + | MeshRendererComponentMask.Mesh + | MeshRendererComponentMask.Material + | MeshRendererComponentMask.SubMeshStartIndex + | MeshRendererComponentMask.LocalBounds + | MeshRendererComponentMask.RendererSettings + | MeshRendererComponentMask.ParentLODGroup + | MeshRendererComponentMask.LODMask + | MeshRendererComponentMask.MeshLodSettings + | MeshRendererComponentMask.Lightmap + | MeshRendererComponentMask.RendererPriority + | MeshRendererComponentMask.GPUComponent + | MeshRendererComponentMask.SceneCullingMask; + + var updateBatch = new MeshRendererUpdateBatch(UpdateMask, + gpuComponents, + MeshRendererUpdateType.MightIncludeNewInstances, + MeshRendererUpdateBatch.LightmapUsage.Unknown, + MeshRendererUpdateBatch.BlendProbesUsage.Unknown, + useSharedSceneCullingMask: false, + 1, Allocator.TempJob); + + updateBatch.mightIncludeTrees = true; + + updateBatch.AddSection(new MeshRendererUpdateSection + { + instanceIDs = rendererData.renderer, + localToWorlds = rendererData.localToWorldMatrix.Reinterpret(), + prevLocalToWorlds = rendererData.prevLocalToWorldMatrix.Reinterpret(), + meshIDs = rendererData.mesh, + materialIDs = rendererData.material, + subMaterialRanges = rendererData.subMaterialRange, + subMeshStartIndices = rendererData.subMeshStartIndex, + localBounds = rendererData.localBounds.Reinterpret(), + rendererSettings = rendererData.rendererSettings, + parentLODGroupIDs = rendererData.lodGroup, + lodMasks = rendererData.lodMask, + meshLodSettings = rendererData.meshLodSettings, + lightmapIndices = rendererData.lightmapIndex, + rendererPriorities = rendererData.rendererPriority, + sceneCullingMasks = rendererData.sceneCullingMask, + gpuComponentUpdates = gpuComponentUpdates, + }); + + updateBatch.Validate(); + ProcessUpdateBatch(ref updateBatch); + + updateBatch.Dispose(); + } + + internal static GPUComponentSet ComputeComponentSet(in DefaultGPUComponents defaultGPUComponents, + MeshRendererUpdateBatch.LightmapUsage lightmapUsage, + MeshRendererUpdateBatch.BlendProbesUsage blendProbesUsage) + { + Assert.IsTrue(lightmapUsage != MeshRendererUpdateBatch.LightmapUsage.Unknown); + Assert.IsTrue(blendProbesUsage != MeshRendererUpdateBatch.BlendProbesUsage.Unknown); + + bool useLightmaps = lightmapUsage == MeshRendererUpdateBatch.LightmapUsage.All; + bool blendProbes = blendProbesUsage == MeshRendererUpdateBatch.BlendProbesUsage.AllEnabled; + bool hasTree = false; + + return ComputeComponentSet(defaultGPUComponents, + useLightmaps, + blendProbes, + hasTree); + } + + internal static GPUComponentSet ComputeComponentSet(in DefaultGPUComponents defaultGPUComponents, + InternalMeshRendererSettings rendererSettings, + int lightmapIndex) + { + bool useLightmaps = LightmapUtils.UsesLightmaps(lightmapIndex); + bool blendProbes = rendererSettings.LightProbeUsage == LightProbeUsage.BlendProbes; + bool hasTree = rendererSettings.HasTree; + + return ComputeComponentSet(defaultGPUComponents, + useLightmaps, + blendProbes, + hasTree); + } + + internal static GPUComponentSet ComputeComponentSet(in DefaultGPUComponents defaultGPUComponents, bool useLightmaps, bool blendProbes, bool hasTree) + { + GPUComponentSet componentSet = defaultGPUComponents.requiredComponentSet; + + if (useLightmaps) + { + // Add per-instance lightmap parameters + componentSet.Add(defaultGPUComponents.lightmapScaleOffset); + } + else + { + // Only add the component when needed to store blended results (shader will use the ambient probe when not present) + if (blendProbes) + componentSet.Add(defaultGPUComponents.shCoefficients); + } + + if (hasTree) + componentSet.AddSet(defaultGPUComponents.speedTreeComponentSet); + + return componentSet; + } + + void ValidateNoInstanceUsesBlendProbes(NativeArray instances) + { + // Disable "Unreachable code detected" warning +#pragma warning disable CS0162 + if (!GPUResidentDrawer.EnableDeepValidation) + return; + + using (new ProfilerMarker("DeepValidation.NoInstanceUsesBlendProbes").Auto()) + { + if (AnyInstanceUseBlendProbes(instances)) + Debug.LogError("One instance has LightProbeUsage == LightProbeUsage.BlendProbes whereas it wasn't expected."); + } + +#pragma warning restore CS0162 + } + + bool AnyInstanceUseBlendProbes(NativeArray instances) => MeshRendererProcessorBurst.AnyInstanceUseBlendProbes(instances, ref m_InstanceDataSystem.renderWorld); + + [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] + private unsafe struct FindRenderersFromMaterialOrMeshJob : IJobParallelForBatch + { + public const int k_BatchSize = 128; + + [ReadOnly] public RenderWorld renderWorld; + [ReadOnly] public NativeHashSet materialIDs; + [ReadOnly] public NativeHashSet meshIDs; + [ReadOnly] public NativeArray sortedExcludeRendererIDs; + + [WriteOnly] public NativeList.ParallelWriter selectedRenderIDsForMaterials; + [WriteOnly] public NativeList.ParallelWriter selectedRenderIDsForMeshes; + + public void Execute(int startIndex, int count) + { + EntityId* renderersToAddForMaterialsPtr = stackalloc EntityId[k_BatchSize]; + var renderersToAddForMaterials = new UnsafeList(renderersToAddForMaterialsPtr, k_BatchSize); + renderersToAddForMaterials.Length = 0; + + EntityId* renderersToAddForMeshesPtr = stackalloc EntityId[k_BatchSize]; + var renderersToAddForMeshes = new UnsafeList(renderersToAddForMeshesPtr, k_BatchSize); + renderersToAddForMeshes.Length = 0; + + for (int index = 0; index < count; index++) + { + int rendererIndex = startIndex + index; + EntityId rendererID = renderWorld.instanceIDs[rendererIndex]; + + // We ignore this renderer if it is in the excluded list. + if (sortedExcludeRendererIDs.BinarySearch(rendererID) >= 0) + continue; + + EntityId meshID = renderWorld.meshIDs[rendererIndex]; + if (meshIDs.Contains(meshID)) + { + renderersToAddForMeshes.AddNoResize(rendererID); + // We can skip the material check if we found a mesh match since at this point + // the renderer is already added and will be processed by the mesh branch + continue; + } + + EmbeddedArray32 rendererMaterials = renderWorld.materialIDArrays[rendererIndex]; + for (int materialIndex = 0; materialIndex < rendererMaterials.Length; materialIndex++) + { + var materialID = rendererMaterials[materialIndex]; + if (materialIDs.Contains(materialID)) + { + renderersToAddForMaterials.AddNoResize(rendererID); + break; + } + } + } + + selectedRenderIDsForMaterials.AddRangeNoResize(renderersToAddForMaterialsPtr, renderersToAddForMaterials.Length); + selectedRenderIDsForMeshes.AddRangeNoResize(renderersToAddForMeshesPtr, renderersToAddForMeshes.Length); + } + } + + void ValidateGPUArchetypesDidNotChange(NativeArray instances, + in MeshRendererUpdateBatch updateBatch, + GPUComponentSet overrideComponentSet) + { + // Disable "Unreachable code detected" warning +#pragma warning disable CS0162 + if (!GPUResidentDrawer.EnableDeepValidation) + return; + + using (new ProfilerMarker("DeepValidation.GPUArchetypesDidNotChange").Auto()) + { + bool archetypeMightHaveChanged = updateBatch.HasAnyComponent(MeshRendererComponentMask.RendererSettings + | MeshRendererComponentMask.Lightmap + | MeshRendererComponentMask.GPUComponent); + + if (!archetypeMightHaveChanged) + return; + + if (DidGPUArchetypesChange(instances, updateBatch, overrideComponentSet)) + Debug.LogError($"Unexpected GPUArchetype changes with the update type {updateBatch.updateType}."); + } + +#pragma warning restore CS0162 + } + + unsafe bool DidGPUArchetypesChange(NativeArray instances, in MeshRendererUpdateBatch updateBatch, GPUComponentSet overrideComponentSet) + { + fixed (MeshRendererUpdateBatch* updateBatchPtr = &updateBatch) + { + return MeshRendererProcessorBurst.DidGPUArchetypeChange(m_ArchetypeManager, + m_InstanceDataSystem.defaultGPUComponents, + instances, + updateBatchPtr, + ref m_InstanceDataSystem.renderWorld, + overrideComponentSet); + } + } + } +} diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/SceneProcessors/MeshRendererProcessor.cs.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/SceneProcessors/MeshRendererProcessor.cs.meta new file mode 100644 index 00000000000..ee2a55243e5 --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/SceneProcessors/MeshRendererProcessor.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: e428597381f61bd4a80f81781acbfcef \ No newline at end of file diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/SceneProcessors/MeshRendererProcessorBurst.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/SceneProcessors/MeshRendererProcessorBurst.cs new file mode 100644 index 00000000000..37e6f847df2 --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/SceneProcessors/MeshRendererProcessorBurst.cs @@ -0,0 +1,159 @@ +using Unity.Collections; +using Unity.Burst; +using UnityEngine.Assertions; + +namespace UnityEngine.Rendering +{ + [BurstCompile] + internal static class MeshRendererProcessorBurst + { + [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] + public static unsafe void ComputeInstanceGPUArchetypes(in NativeReference archetypeManager, + in DefaultGPUComponents defaultGPUComponents, + MeshRendererUpdateBatch* updateBatch, + in GPUComponentSet overrideComponentSet, + bool useSharedGPUArchetype, + ref NativeArray archetypes) + { + if (useSharedGPUArchetype) + { + Assert.IsTrue(archetypes.Length == 1); + + GPUComponentSet componentSet = MeshRendererProcessor.ComputeComponentSet(defaultGPUComponents, updateBatch->lightmapUsage, updateBatch->blendProbesUsage); + componentSet.AddSet(overrideComponentSet); + archetypes[0] = archetypeManager.GetRef().GetOrCreateArchetype(componentSet); + } + else + { + Assert.IsTrue(archetypes.Length == updateBatch->TotalLength); + + bool hasMeshRendererSettings = updateBatch->HasAnyComponent(MeshRendererComponentMask.RendererSettings); + bool hasLightmap = updateBatch->HasAnyComponent(MeshRendererComponentMask.Lightmap); + + int flatIndex = 0; + for (int sectionIndex = 0; sectionIndex < updateBatch->SectionCount; sectionIndex++) + { + NativeArray rendererSettingsSection = updateBatch->GetRendererSettingsSectionOrDefault(sectionIndex); + NativeArray lightmapIndexSection = updateBatch->GetLightmapIndexSectionOrDefault(sectionIndex); + + for (int i = 0; i < updateBatch->GetSectionLength(sectionIndex); i++) + { + InternalMeshRendererSettings rendererSettings = hasMeshRendererSettings ? rendererSettingsSection[i] : RenderWorld.DefaultRendererSettings; + int lightmapIndex = hasLightmap ? lightmapIndexSection[i] : RenderWorld.DefaultLightmapIndex; + GPUComponentSet componentSet = MeshRendererProcessor.ComputeComponentSet(defaultGPUComponents, rendererSettings, lightmapIndex); + componentSet.AddSet(overrideComponentSet); + + archetypes[flatIndex] = archetypeManager.GetRef().GetOrCreateArchetype(componentSet); + ++flatIndex; + } + } + } + } + + [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] + public static unsafe void BuildGPUComponentOverrideUploadSources(in NativeReference archetypeManager, + in NativeArray componentUpdates, + ref NativeArray uploadSources, + GPUComponentSet* overrideComponentSet) + { + Assert.IsTrue(componentUpdates.Length == componentUpdates.Length); + + GPUComponentSet componentSet = default; + + for (int i = 0; i < componentUpdates.Length; i++) + { + ref readonly GPUComponentJaggedUpdate update = ref componentUpdates.ElementAt(i); + GPUComponentHandle component = archetypeManager.GetRef().GetOrCreateComponent(update.PropertyID, update.StrideInBytes, perInstance: true); + componentSet.Add(component); + + MeshRendererProcessor.GPUComponentUploadSource source = default; + source.component = component; + source.data = update.Data; + source.componentSize = update.StrideInBytes; + + uploadSources[i] = source; + } + + *overrideComponentSet = componentSet; + } + + [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] + public static bool AnyInstanceUseBlendProbes(in NativeArray instances, ref RenderWorld renderWorld) + { + for (int i = 0; i < instances.Length; i++) + { + InstanceHandle instance = instances[i]; + Assert.IsTrue(instance.isValid); + if (!instance.isValid) + continue; + + int instanceIndex = renderWorld.HandleToIndex(instance); + if (renderWorld.rendererSettings[instanceIndex].LightProbeUsage == LightProbeUsage.BlendProbes) + { + return true; + } + } + + return false; + } + + [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] + public static unsafe bool DidGPUArchetypeChange(in NativeReference archetypeManager, + in DefaultGPUComponents defaultGPUComponents, + in NativeArray instances, + MeshRendererUpdateBatch* updateBatch, + ref RenderWorld renderWorld, + in GPUComponentSet overrideComponentSet) + { + int flatIndex = 0; + for (int sectionIndex = 0; sectionIndex < updateBatch->SectionCount; sectionIndex++) + { + NativeArray rendererSettingsSection = updateBatch->GetRendererSettingsSectionOrDefault(sectionIndex); + NativeArray lightmapIndexSection = updateBatch->GetLightmapIndexSectionOrDefault(sectionIndex); + + for (int i = 0; i < updateBatch->GetSectionLength(sectionIndex); i++) + { + InstanceHandle instance = instances[flatIndex]; + int instanceIndex = renderWorld.HandleToIndex(instance); + + InternalMeshRendererSettings oldRendererSettings = renderWorld.rendererSettings[instanceIndex]; + int oldLightmapIndex = renderWorld.lightmapIndices[instanceIndex]; + + InternalMeshRendererSettings newRendererSettings = updateBatch->HasAnyComponent(MeshRendererComponentMask.RendererSettings) + ? rendererSettingsSection[i] + : oldRendererSettings; + + int newLightmapIndex = updateBatch->HasAnyComponent(MeshRendererComponentMask.Lightmap) + ? lightmapIndexSection[i] + : oldLightmapIndex; + + ulong oldBaseComponentMask = MeshRendererProcessor.ComputeComponentSet(defaultGPUComponents, oldRendererSettings, oldLightmapIndex).componentsMask; + ulong newBaseComponentMask = MeshRendererProcessor.ComputeComponentSet(defaultGPUComponents, newRendererSettings, newLightmapIndex).componentsMask; + + // First compare the "base" component mask. So the component mask excluding the shader property overrides. + // Those two mask must match exactly, otherwise it means the update changed the GPU archetype. + if (oldBaseComponentMask != newBaseComponentMask) + { + return true; + } + + // Then retrieve the full component mask, which includes the shader property overrides. + GPUArchetypeHandle currentArchetype = renderWorld.gpuHandles[instanceIndex].archetype; + ulong fullComponentMask = archetypeManager.GetRefRO().FindComponentSet(currentArchetype).componentsMask; + ulong overrideComponentMask = overrideComponentSet.componentsMask; + + // For shader property overrides, it is only required that the components we want to update were already part of the archetype. + // It is possible to just update one shader property override even if the instance has many overrides for example. + if ((overrideComponentMask & fullComponentMask) != overrideComponentMask) + { + return true; + } + + ++flatIndex; + } + } + + return false; + } + } +} diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/SceneProcessors/MeshRendererProcessorBurst.cs.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/SceneProcessors/MeshRendererProcessorBurst.cs.meta new file mode 100644 index 00000000000..92f159bb718 --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/SceneProcessors/MeshRendererProcessorBurst.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 8fdab23f404440c4cbe085e51d53cc07 \ No newline at end of file diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/SceneProcessors/MeshRendererUpdateBatch.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/SceneProcessors/MeshRendererUpdateBatch.cs new file mode 100644 index 00000000000..a6d79489662 --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/SceneProcessors/MeshRendererUpdateBatch.cs @@ -0,0 +1,702 @@ +using System; +using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; +using Unity.Mathematics; +using Unity.Profiling; +using UnityEngine.Assertions; + +namespace UnityEngine.Rendering +{ + [Flags] + internal enum MeshRendererComponentMask + { + None = 0, + LocalToWorld = 1 << 0, + PrevLocalToWorld = 1 << 1, + Mesh = 1 << 2, + Material = 1 << 3, + SubMeshStartIndex = 1 << 4, + LocalBounds = 1 << 5, + RendererSettings = 1 << 6, + ParentLODGroup = 1 << 7, + LODMask = 1 << 8, + MeshLodSettings = 1 << 9, + Lightmap = 1 << 10, + RendererPriority = 1 << 11, + SceneCullingMask = 1 << 12, + RenderingEnabled = 1 << 13, + GPUComponent = 1 << 14, + } + + internal struct GPUComponent + { + public int PropertyID; + public int SizeInBytes; + + public GPUComponent(int propertyID, int sizeInBytes) + { + PropertyID = propertyID; + SizeInBytes = sizeInBytes; + } + } + + internal struct GPUComponentUpdate + { + UnsafeList m_Data; + GPUComponent m_Component; + + public static GPUComponentUpdate FromArray(GPUComponent component, NativeArray data) where T : unmanaged + { + var typeSize = UnsafeUtility.SizeOf(); + Assert.IsTrue(typeSize == component.SizeInBytes); + return new GPUComponentUpdate(component, data.Reinterpret(typeSize)); + } + + public GPUComponentUpdate(GPUComponent component, NativeArray data) + { + Assert.IsTrue(data.Length % component.SizeInBytes == 0); + m_Data = data.AsUnsafeListReadOnly(); + m_Component = component; + } + + public GPUComponent Component => m_Component; + public int PropertyID => m_Component.PropertyID; + public int StrideInBytes => m_Component.SizeInBytes; + public NativeArray Data => m_Data.AsNativeArray(); + } + + internal struct GPUComponentJaggedUpdate : IDisposable + { + JaggedSpan m_Data; + GPUComponent m_Component; + + public GPUComponentJaggedUpdate(int initialCapacity, Allocator allocator, GPUComponent component) + { + m_Data = new JaggedSpan(initialCapacity, allocator); + m_Component = component; + } + + public void Dispose() + { + m_Data.Dispose(); + } + + public void Append(in GPUComponentUpdate section) + { + Assert.IsTrue(PropertyID == section.PropertyID); + Assert.IsTrue(StrideInBytes == section.StrideInBytes); + m_Data.Add(section.Data); + } + + public int PropertyID => m_Component.PropertyID; + public int StrideInBytes => m_Component.SizeInBytes; + public JaggedSpan Data => m_Data; + } + + internal enum MeshRendererUpdateType + { + Null, + MightIncludeNewInstances, + NoStructuralChanges, + RecreateOnlyKnownInstances, + OnlyNewInstances, + } + + internal struct MeshRendererUpdateSection + { + public NativeArray instanceIDs; + public NativeArray localToWorlds; + public NativeArray prevLocalToWorlds; + public NativeArray meshIDs; + public NativeArray materialIDs; + public NativeArray subMaterialRanges; + public NativeArray subMeshStartIndices; + public NativeArray localBounds; + public NativeArray rendererSettings; + public NativeArray parentLODGroupIDs; + public NativeArray lodMasks; + public NativeArray meshLodSettings; + public NativeArray lightmapIndices; + public NativeArray rendererPriorities; + public NativeArray gpuComponentUpdates; + public NativeBitArray renderingEnabled; + public NativeArray sceneCullingMasks; + public ulong sharedSceneCullingMask; + } + + // This struct stores array views of all the data we want to upload to the RenderWorld for MeshRenderer-like object. + // The data does not have to come from the managed Unity MeshRenderer. + // It can come from anywhere, but it is expected to represent something analogous to a MeshRenderer. + // + // We use a custom JaggedSpan struct to store each component array view. It allows us to represent a logical sequence with multiple memory blocks (sections). + // This is very important for DOTS which stores Entities data in chunks containing at most 128 entities. + // Using JaggedSpans here allow our jobs to go wide over multiple chunks/sections when uploading data to the RenderWorld. + // This is much faster than doing one update batch per EntityId chunk since it would drastically reduce parallelism. + // + // Most component sequences are optional. If nothing is provided, the static readonly default values will be used when needed. + // + // With MeshRendererUpdateType.NoStructuralChanges, GPUResidentDrawer will assume this batch deals only with known instances. + // It will use the provided components to update the RenderWorld representation of the instances incrementally. + // No components are expected to be added or removed in this mode, including the GPU components. + // This is very useful to do fast incremental updates like for transforms or material property overrides for example. + // Any other update type will cause the instances in the RenderWorld to be rebuilt. + // So in that case, if a component sequence is not provided then a default value will be used to rebuild the instance. + internal struct MeshRendererUpdateBatch : IDisposable + { + public enum LightmapUsage + { + Unknown, + All, + None + } + + public enum BlendProbesUsage + { + Unknown, + AllEnabled, + AllDisabled + } + + public JaggedSpan instanceIDs; + public JaggedSpan localToWorlds; + public JaggedSpan prevLocalToWorlds; + public JaggedSpan meshIDs; + public JaggedSpan materialIDs; // Buffer indexed using subMaterialRanges + public JaggedSpan subMaterialRanges; + public JaggedSpan subMeshStartIndices; + public JaggedSpan localBounds; + public JaggedSpan rendererSettings; + public JaggedSpan parentLODGroupIDs; + public JaggedSpan lodMasks; + public JaggedSpan meshLodSettings; + public JaggedSpan lightmapIndices; + public JaggedSpan rendererPriorities; + public JaggedSpan sceneCullingMasks; + public NativeList sharedSceneCullingMasks; + public NativeArray gpuComponentUpdates; + public JaggedBitSpan renderingEnabled; + + public MeshRendererComponentMask componentMask; + public MeshRendererUpdateType updateType; + public LightmapUsage lightmapUsage; + public BlendProbesUsage blendProbesUsage; + public bool useSharedSceneCullingMask; // If true, each sceneCullingMask array in a section contains only one entry that is shared for all instances. + internal bool mightIncludeTrees; // Only used internally to support Speed Tree with GameObjects. + + public MeshRendererUpdateBatch(MeshRendererComponentMask componentMask, + NativeArray gpuComponents, + MeshRendererUpdateType updateType, + LightmapUsage lightmapUsage, + BlendProbesUsage blendProbesUsage, + bool useSharedSceneCullingMask, + int initialCapacity, + Allocator allocator) + { + if (useSharedSceneCullingMask) + Assert.IsTrue(componentMask.HasAnyBit(MeshRendererComponentMask.SceneCullingMask)); + + this.componentMask = componentMask; + this.updateType = updateType; + this.lightmapUsage = lightmapUsage; + this.blendProbesUsage = blendProbesUsage; + this.useSharedSceneCullingMask = useSharedSceneCullingMask; + this.mightIncludeTrees = false; + + instanceIDs = new JaggedSpan(initialCapacity, allocator); + localToWorlds = new JaggedSpan(componentMask.HasAnyBit(MeshRendererComponentMask.LocalToWorld | MeshRendererComponentMask.LocalBounds) ? initialCapacity : 0, allocator); + prevLocalToWorlds = new JaggedSpan(componentMask.HasAnyBit(MeshRendererComponentMask.PrevLocalToWorld) ? initialCapacity : 0, allocator); + meshIDs = new JaggedSpan(componentMask.HasAnyBit(MeshRendererComponentMask.Mesh) ? initialCapacity : 0, allocator); + materialIDs = new JaggedSpan(componentMask.HasAnyBit(MeshRendererComponentMask.Material) ? initialCapacity : 0, allocator); + subMaterialRanges = new JaggedSpan(componentMask.HasAnyBit(MeshRendererComponentMask.Material) ? initialCapacity : 0, allocator); + subMeshStartIndices = new JaggedSpan(componentMask.HasAnyBit(MeshRendererComponentMask.SubMeshStartIndex) ? initialCapacity : 0, allocator); + localBounds = new JaggedSpan(componentMask.HasAnyBit(MeshRendererComponentMask.LocalBounds) ? initialCapacity : 0, allocator); + rendererSettings = new JaggedSpan(componentMask.HasAnyBit(MeshRendererComponentMask.RendererSettings) ? initialCapacity : 0, allocator); + parentLODGroupIDs = new JaggedSpan(componentMask.HasAnyBit(MeshRendererComponentMask.ParentLODGroup) ? initialCapacity : 0, allocator); + lodMasks = new JaggedSpan(componentMask.HasAnyBit(MeshRendererComponentMask.LODMask) ? initialCapacity : 0, allocator); + meshLodSettings = new JaggedSpan(componentMask.HasAnyBit(MeshRendererComponentMask.MeshLodSettings) ? initialCapacity : 0, allocator); + lightmapIndices = new JaggedSpan(componentMask.HasAnyBit(MeshRendererComponentMask.Lightmap) ? initialCapacity : 0, allocator); + rendererPriorities = new JaggedSpan(componentMask.HasAnyBit(MeshRendererComponentMask.RendererPriority) ? initialCapacity : 0, allocator); + sceneCullingMasks = new JaggedSpan(componentMask.HasAnyBit(MeshRendererComponentMask.SceneCullingMask) ? initialCapacity : 0, allocator); + sharedSceneCullingMasks = new NativeList(useSharedSceneCullingMask ? initialCapacity : 0, allocator); + renderingEnabled = new JaggedBitSpan(componentMask.HasAnyBit(MeshRendererComponentMask.RenderingEnabled) ? initialCapacity : 0, allocator); + + if (componentMask.HasAnyBit(MeshRendererComponentMask.GPUComponent)) + { + gpuComponentUpdates = new NativeArray(gpuComponents.Length, allocator); + for (int i = 0; i < gpuComponents.Length; i++) + { + gpuComponentUpdates[i] = new GPUComponentJaggedUpdate(initialCapacity, allocator, gpuComponents[i]); + } + } + else + { + Assert.IsTrue(gpuComponents.Length == 0); + gpuComponentUpdates = new NativeArray(0, allocator); + } + } + + public void Dispose() + { + instanceIDs.Dispose(); + localToWorlds.Dispose(); + prevLocalToWorlds.Dispose(); + meshIDs.Dispose(); + materialIDs.Dispose(); + subMaterialRanges.Dispose(); + subMeshStartIndices.Dispose(); + localBounds.Dispose(); + rendererSettings.Dispose(); + parentLODGroupIDs.Dispose(); + lodMasks.Dispose(); + meshLodSettings.Dispose(); + lightmapIndices.Dispose(); + rendererPriorities.Dispose(); + sceneCullingMasks.Dispose(); + sharedSceneCullingMasks.Dispose(); + renderingEnabled.Dispose(); + foreach (GPUComponentJaggedUpdate update in gpuComponentUpdates) + update.Dispose(); + gpuComponentUpdates.Dispose(); + } + + public int SectionCount => instanceIDs.sectionCount; + + public int TotalLength => instanceIDs.totalLength; + + public int GetSectionLength(int sectionIndex) => instanceIDs[sectionIndex].Length; + + public bool HasAnyComponent(MeshRendererComponentMask bits) => componentMask.HasAnyBit(bits); + + public NativeArray GetLocalToWorldSectionOrDefault(int index) => HasAnyComponent(MeshRendererComponentMask.LocalToWorld | MeshRendererComponentMask.LocalBounds) ? localToWorlds[index] : default; + public NativeArray GetLocalBoundsSectionOrDefault(int index) => HasAnyComponent(MeshRendererComponentMask.LocalBounds) ? localBounds[index] : default; + public NativeArray GetMaterialSectionOrDefault(int index) => HasAnyComponent(MeshRendererComponentMask.Material) ? materialIDs[index] : default; + public NativeArray GetSubMaterialRangeSectionOrDefault(int index) => HasAnyComponent(MeshRendererComponentMask.Material) ? subMaterialRanges[index] : default; + public NativeArray GetMeshSectionOrDefault(int index) => HasAnyComponent(MeshRendererComponentMask.Mesh) ? meshIDs[index] : default; + public NativeArray GetLightmapIndexSectionOrDefault(int index) => HasAnyComponent(MeshRendererComponentMask.Lightmap) ? lightmapIndices[index] : default; + public NativeArray GetRendererPrioritySectionOrDefault(int index) => HasAnyComponent(MeshRendererComponentMask.RendererPriority) ? rendererPriorities[index] : default; + public NativeArray GetSubMeshStartIndexSectionOrDefault(int index) => HasAnyComponent(MeshRendererComponentMask.SubMeshStartIndex) ? subMeshStartIndices[index] : default; + public NativeArray GetRendererSettingsSectionOrDefault(int index) => HasAnyComponent(MeshRendererComponentMask.RendererSettings) ? rendererSettings[index] : default; + public NativeArray GetParentLODGroupIDSectionOrDefault(int index) => HasAnyComponent(MeshRendererComponentMask.ParentLODGroup) ? parentLODGroupIDs[index] : default; + public NativeArray GetLODMaskSectionOrDefault(int index) => HasAnyComponent(MeshRendererComponentMask.LODMask) ? lodMasks[index] : default; + public NativeArray GetMeshLodSettingsSectionOrDefault(int index) => HasAnyComponent(MeshRendererComponentMask.MeshLodSettings) ? meshLodSettings[index] : default; + public UnsafeBitArray GetRenderingEnabledSectionOrDefault(int index) => HasAnyComponent(MeshRendererComponentMask.RenderingEnabled) ? renderingEnabled[index] : default; + + public void AddSection(in MeshRendererUpdateSection section) + { + instanceIDs.Add(section.instanceIDs); + + if (HasAnyComponent(MeshRendererComponentMask.LocalToWorld | MeshRendererComponentMask.LocalBounds)) + { + Assert.IsTrue(section.instanceIDs.Length == section.localToWorlds.Length); + localToWorlds.Add(section.localToWorlds); + } + else + { + Assert.IsTrue(!section.localToWorlds.IsCreated); + } + + if (HasAnyComponent(MeshRendererComponentMask.PrevLocalToWorld)) + { + Assert.IsTrue(section.instanceIDs.Length == section.prevLocalToWorlds.Length); + prevLocalToWorlds.Add(section.prevLocalToWorlds); + } + else + { + Assert.IsTrue(!section.prevLocalToWorlds.IsCreated); + } + + if (HasAnyComponent(MeshRendererComponentMask.Mesh)) + { + Assert.IsTrue(section.instanceIDs.Length == section.meshIDs.Length); + meshIDs.Add(section.meshIDs); + } + else + { + Assert.IsTrue(!section.meshIDs.IsCreated); + } + + if (HasAnyComponent(MeshRendererComponentMask.Material)) + { + Assert.IsTrue(section.instanceIDs.Length == section.subMaterialRanges.Length); + + materialIDs.Add(section.materialIDs); + subMaterialRanges.Add(section.subMaterialRanges); + } + else + { + Assert.IsTrue(!section.materialIDs.IsCreated); + Assert.IsTrue(!section.subMaterialRanges.IsCreated); + } + + if (HasAnyComponent(MeshRendererComponentMask.SubMeshStartIndex)) + { + Assert.IsTrue(section.instanceIDs.Length == section.subMeshStartIndices.Length); + subMeshStartIndices.Add(section.subMeshStartIndices); + } + else + { + Assert.IsTrue(!section.subMeshStartIndices.IsCreated); + } + + if (HasAnyComponent(MeshRendererComponentMask.LocalBounds)) + { + Assert.IsTrue(section.instanceIDs.Length == section.localBounds.Length); + localBounds.Add(section.localBounds); + } + else + { + Assert.IsTrue(!section.localBounds.IsCreated); + } + + if (HasAnyComponent(MeshRendererComponentMask.RendererSettings)) + { + Assert.IsTrue(section.instanceIDs.Length == section.rendererSettings.Length); + rendererSettings.Add(section.rendererSettings); + } + else + { + Assert.IsTrue(!section.rendererSettings.IsCreated); + } + + if (HasAnyComponent(MeshRendererComponentMask.ParentLODGroup)) + { + Assert.IsTrue(section.instanceIDs.Length == section.parentLODGroupIDs.Length); + parentLODGroupIDs.Add(section.parentLODGroupIDs); + } + else + { + Assert.IsTrue(!section.parentLODGroupIDs.IsCreated); + } + + if (HasAnyComponent(MeshRendererComponentMask.LODMask)) + { + Assert.IsTrue(section.instanceIDs.Length == section.lodMasks.Length); + lodMasks.Add(section.lodMasks); + } + else + { + Assert.IsTrue(!section.lodMasks.IsCreated); + } + + if (HasAnyComponent(MeshRendererComponentMask.MeshLodSettings)) + { + Assert.IsTrue(section.instanceIDs.Length == section.meshLodSettings.Length); + meshLodSettings.Add(section.meshLodSettings); + } + else + { + Assert.IsTrue(!section.meshLodSettings.IsCreated); + } + + if (HasAnyComponent(MeshRendererComponentMask.Lightmap)) + { + Assert.IsTrue(section.instanceIDs.Length == section.lightmapIndices.Length); + lightmapIndices.Add(section.lightmapIndices); + } + else + { + Assert.IsTrue(!section.lightmapIndices.IsCreated); + } + + if (HasAnyComponent(MeshRendererComponentMask.RendererPriority)) + { + Assert.IsTrue(section.instanceIDs.Length == section.rendererPriorities.Length); + rendererPriorities.Add(section.rendererPriorities); + } + else + { + Assert.IsTrue(!section.rendererPriorities.IsCreated); + } + + if (HasAnyComponent(MeshRendererComponentMask.SceneCullingMask)) + { + if (useSharedSceneCullingMask) + { + Assert.IsTrue(!section.sceneCullingMasks.IsCreated); + sharedSceneCullingMasks.Add(section.sharedSceneCullingMask); + } + else + { + Assert.IsTrue(section.instanceIDs.Length == section.sceneCullingMasks.Length); + Assert.IsTrue(section.sharedSceneCullingMask == 0); + sceneCullingMasks.Add(section.sceneCullingMasks); + } + } + else + { + Assert.IsTrue(!section.sceneCullingMasks.IsCreated); + Assert.IsTrue(section.sharedSceneCullingMask == 0); + } + + if (HasAnyComponent(MeshRendererComponentMask.RenderingEnabled)) + { + Assert.IsTrue(section.instanceIDs.Length == section.renderingEnabled.Length); + renderingEnabled.Add(section.renderingEnabled); + } + else + { + Assert.IsTrue(!section.renderingEnabled.IsCreated); + } + + if (HasAnyComponent(MeshRendererComponentMask.GPUComponent)) + { + // Layout must match exactly + Assert.IsTrue(gpuComponentUpdates.Length == section.gpuComponentUpdates.Length); + + for (int i = 0; i < section.gpuComponentUpdates.Length; i++) + { + ref GPUComponentJaggedUpdate jaggedUpdate = ref gpuComponentUpdates.ElementAtRW(i); + jaggedUpdate.Append(section.gpuComponentUpdates.ElementAt(i)); + } + } + else + { + Assert.IsTrue(!section.gpuComponentUpdates.IsCreated); + } + } + + internal void Validate() + { + // Disable "Unreachable code detected" warning +#pragma warning disable CS0162 + if (!GPUResidentDrawer.EnableValidation) + return; + + using (new ProfilerMarker("MeshRendererUpdateBatch.Validate").Auto()) + { + ValidateImpl(); + } +#pragma warning restore CS0162 + } + + private void ValidateImpl() + { + if (!ValidateEmptyOrSameLayout(MeshRendererComponentMask.LocalToWorld, localToWorlds, instanceIDs)) + return; + + if (!ValidateEmptyOrSameLayout(MeshRendererComponentMask.PrevLocalToWorld, prevLocalToWorlds, instanceIDs)) + return; + + if (!ValidateEmptyOrSameLayout(MeshRendererComponentMask.Mesh, meshIDs, instanceIDs)) + return; + + if (!ValidateEmptyOrSameLayout(MeshRendererComponentMask.Material, subMaterialRanges, instanceIDs)) + return; + + if (!ValidateEmptyOrSameLayout(MeshRendererComponentMask.SubMeshStartIndex, subMeshStartIndices, instanceIDs)) + return; + + if (!ValidateEmptyOrSameLayout(MeshRendererComponentMask.LocalBounds, localBounds, instanceIDs)) + return; + + if (!ValidateEmptyOrSameLayout(MeshRendererComponentMask.RendererSettings, rendererSettings, instanceIDs)) + return; + + if (!ValidateEmptyOrSameLayout(MeshRendererComponentMask.ParentLODGroup, parentLODGroupIDs, instanceIDs)) + return; + + if (!ValidateEmptyOrSameLayout(MeshRendererComponentMask.LODMask, lodMasks, instanceIDs)) + return; + + if (!ValidateEmptyOrSameLayout(MeshRendererComponentMask.MeshLodSettings, meshLodSettings, instanceIDs)) + return; + + if (!ValidateEmptyOrSameLayout(MeshRendererComponentMask.Lightmap, lightmapIndices, instanceIDs)) + return; + + if (!ValidateEmptyOrSameLayout(MeshRendererComponentMask.RendererPriority, rendererPriorities, instanceIDs)) + return; + + if (!ValidateEmptyOrSameSectionCount(MeshRendererComponentMask.Material, materialIDs, instanceIDs)) + return; + + if (!ValidateSceneCullingMask(sceneCullingMasks, sharedSceneCullingMasks, useSharedSceneCullingMask, instanceIDs)) + return; + + foreach (GPUComponentJaggedUpdate jaggedUpdate in gpuComponentUpdates) + { + if (!ValidateGPUComponentUpdates(jaggedUpdate, instanceIDs)) + return; + } + + if (updateType == MeshRendererUpdateType.NoStructuralChanges) + { + if (HasAnyComponent(MeshRendererComponentMask.PrevLocalToWorld)) + { + // This component must only be set when new instances might be created. + // It is only useful to generate correct motion vectors when objects that just got created moved before they got rendered. + // Otherwise previous local to worlds will be handled automatically by GRD. + Debug.LogError("Invalid MeshRendererUpdateBatch. PrevLocalToWorld component was set in MeshRendererUpdateType.NoStructuralChanges."); + return; + } + } + + if (HasAnyComponent(MeshRendererComponentMask.PrevLocalToWorld)) + { + if (!HasAnyComponent(MeshRendererComponentMask.LocalToWorld)) + { + Debug.LogError("Invalid MeshRendererUpdateBatch. PrevLocalToWorld component was set, but not the LocalToWorld."); + return; + } + } + + if (HasAnyComponent(MeshRendererComponentMask.LocalBounds)) + { + // Note that we dont check for MeshRendererComponentMask.LocalToWorld here. + // This is because setting the LocalToWorld bit means the matrices have changed, which is not always what we want. + // Sometimes the matrices won't have changed, but they still need to be provided alongside the local bounds so that we can compute the new world bounds. + // GRD doesn't store the local to worlds on the CPU. That would be wasteful memory wise. + if (!localToWorlds.HasSameLayout(instanceIDs)) + { + Debug.LogError("Invalid MeshRendererUpdateBatch. LocalBounds component was set, but local to worlds were not provided."); + return; + } + } + + if (!ValidateNoDuplicatePropertyID(gpuComponentUpdates)) + return; + + if (!DeepValidateImpl()) + return; + } + + private bool DeepValidateImpl() + { + // Disable "Unreachable code detected" warning +#pragma warning disable CS0162 + if (!GPUResidentDrawer.EnableDeepValidation) + return true; + + using (new ProfilerMarker("MeshRendererUpdateBatch.DeepValidate").Auto()) + { + if (instanceIDs.HasDuplicates()) + { + Debug.LogError("MeshRendererUpdateBatch contains dupplicate instanceIDs."); + return false; + } + } + + return true; +#pragma warning restore CS0162 + } + + private bool ValidateSceneCullingMask(JaggedSpan sceneCullingMasks, + NativeList sharedSceneCullingMasks, + bool useSharedSceneCullingMask, + JaggedSpan instanceIDs) + { + if (useSharedSceneCullingMask) + { + if (!sceneCullingMasks.isEmpty) + { + Debug.LogError("Invalid MeshRendererUpdateBatch. MeshRendererUpdateBatch.useSharedSceneCullingMask is true but MeshRendererUpdateBatch.sceneCullingMasks is not empty."); + return false; + } + + if (!sharedSceneCullingMasks.IsEmpty && sharedSceneCullingMasks.Length != instanceIDs.sectionCount) + { + Debug.LogError($"Invalid MeshRendererUpdateBatch. MeshRendererUpdateBatch.sharedSceneCullingMasks has an unexpected layout."); + return false; + } + } + else + { + if (!sharedSceneCullingMasks.IsEmpty) + { + Debug.LogError("Invalid MeshRendererUpdateBatch. MeshRendererUpdateBatch.useSharedSceneCullingMask is false but MeshRendererUpdateBatch.sharedSceneCullingMasks is not empty."); + return false; + } + + if (!sceneCullingMasks.isEmpty && !sceneCullingMasks.HasSameLayout(instanceIDs)) + { + Debug.LogError($"Invalid MeshRendererUpdateBatch. MeshRendererUpdateBatch.sceneCullingMasks has an unexpected layout."); + return false; + } + } + + return true; + } + + private bool ValidateGPUComponentUpdates(in GPUComponentJaggedUpdate update, JaggedSpan instanceIDs) + { + if (!HasSameLayout(update, instanceIDs)) + { + // Replace when Shader.PropertyIDToName API lands + // string propertyName = Shader.PropertyIDToName(update.PropertyID); + string propertyName = ""; + Debug.LogError($"Invalid MeshRendererUpdateBatch. Material property update jagged span has an unexpected layout. Shader property: \"{propertyName}\". StrideInBytes: ({update.StrideInBytes})."); + return false; + } + + if (update.StrideInBytes % sizeof(uint) != 0) + { + // Replace when Shader.PropertyIDToName API lands + // string propertyName = Shader.PropertyIDToName(update.PropertyID); + string propertyName = ""; + Debug.LogError($"Invalid MeshRendererUpdateBatch. Material property size must be a multiple of 4. ByteAddressBuffer only works at 32-bits granularity. Shader property: \"{propertyName}\". StrideInBytes: ({update.StrideInBytes})."); + return false; + } + + return true; + } + + private bool ValidateNoDuplicatePropertyID(in NativeArray updates) + { + if (updates.Length == 0) + return true; + + var uniqueIDs = new NativeHashSet(updates.Length, Allocator.Temp); + + for (int i = 0; i < updates.Length; i++) + { + int propertyID = updates[i].PropertyID; + if (!uniqueIDs.Add(propertyID)) + { + // Replace when Shader.PropertyIDToName API lands + // string propertyName = Shader.PropertyIDToName(propertyID); + string propertyName = ""; + Debug.LogError($"Multiple MaterialPropertyJaggedUpdate refer to the same shader property \"{propertyName}\")"); + return false; + } + } + + return true; + } + + private bool ValidateEmptyOrSameLayout(MeshRendererComponentMask component, JaggedSpan components, JaggedSpan instanceIDs) where T : unmanaged + { + if (!components.isEmpty && !components.HasSameLayout(instanceIDs)) + { + Debug.LogError($"Invalid MeshRendererUpdateBatch. {component} jagged span has an unexpected layout."); + return false; + } + + return true; + } + + private bool ValidateEmptyOrSameSectionCount(MeshRendererComponentMask component, JaggedSpan components, JaggedSpan instanceIDs) where T : unmanaged + { + if (!components.isEmpty && components.sectionCount != instanceIDs.sectionCount) + { + Debug.LogError($"Invalid MeshRendererUpdateBatch. {component} jagged span has an unexpected SectionCount ({components.sectionCount}). Expected value is ({instanceIDs.sectionCount})"); + return false; + } + + return true; + } + + private bool HasSameLayout(in GPUComponentJaggedUpdate update, in JaggedSpan instanceIDs) + { + if (update.Data.sectionCount != instanceIDs.sectionCount) + return false; + + for (int i = 0; i < SectionCount; i++) + { + if ((update.Data[i].Length / update.StrideInBytes) != instanceIDs[i].Length) + return false; + } + + return true; + } + } +} diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/SceneProcessors/MeshRendererUpdateBatch.cs.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/SceneProcessors/MeshRendererUpdateBatch.cs.meta new file mode 100644 index 00000000000..ebfdc19a6fa --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/SceneProcessors/MeshRendererUpdateBatch.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 4c6adc47d80c5df4ebc71aeda3c7d283 \ No newline at end of file diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/SceneProcessors/SpeedTreeWindGPUDataUpdater.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/SceneProcessors/SpeedTreeWindGPUDataUpdater.cs new file mode 100644 index 00000000000..6f1d77e21b7 --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/SceneProcessors/SpeedTreeWindGPUDataUpdater.cs @@ -0,0 +1,149 @@ +using System; +using Unity.Collections; +using UnityEngine.Profiling; +using UnityEngine.Assertions; +using Unity.Collections.LowLevel.Unsafe; +using Unity.Mathematics; + +namespace UnityEngine.Rendering +{ + internal class SpeedTreeWindGPUDataUpdater : IDisposable + { + private InstanceDataSystem m_InstanceDataSystem; + private InstanceCuller m_Culler; + private ParallelBitArray m_ProcessedThisFrameTreeBits; + private NativeArray m_CPUUploadBuffer; + private GraphicsBuffer m_GPUUploadBuffer; + + public void Initialize(InstanceDataSystem instanceDataSystem, InstanceCuller culler) + { + m_InstanceDataSystem = instanceDataSystem; + m_Culler = culler; + } + + public void Dispose() + { + m_CPUUploadBuffer.Dispose(); + m_GPUUploadBuffer?.Release(); + + if (m_ProcessedThisFrameTreeBits.IsCreated) + m_ProcessedThisFrameTreeBits.Dispose(); + } + + public void OnBeginContextRendering() + { + if (m_ProcessedThisFrameTreeBits.IsCreated) + m_ProcessedThisFrameTreeBits.Dispose(); + } + + public void UpdateGPUData() + { + if (m_InstanceDataSystem.totalTreeCount == 0) + return; + + ParallelBitArray compactedVisibilityMasks = m_Culler.GetCompactedVisibilityMasks(syncCullingJobs: false); + + if (!compactedVisibilityMasks.IsCreated) + return; + + Profiler.BeginSample("SpeedTreeGPUWindDataUpdater.UpdateGPUData"); + + int maxInstancesCount = m_InstanceDataSystem.indexToHandle.Length; + + if (!m_ProcessedThisFrameTreeBits.IsCreated) + m_ProcessedThisFrameTreeBits = new ParallelBitArray(maxInstancesCount, Allocator.TempJob); + else if (m_ProcessedThisFrameTreeBits.Length < maxInstancesCount) + m_ProcessedThisFrameTreeBits.Resize(maxInstancesCount); + + bool becomeVisibleOnly = !Application.isPlaying; + var visibleTreeRenderers = new NativeList(Allocator.TempJob); + var visibleTreeInstances = new NativeList(Allocator.TempJob); + + m_InstanceDataSystem.GetVisibleTreeInstances(compactedVisibilityMasks, m_ProcessedThisFrameTreeBits, visibleTreeRenderers, visibleTreeInstances, + becomeVisibleOnly, out var becomeVisibleTreeInstancesCount); + + if (visibleTreeRenderers.Length > 0) + { + Profiler.BeginSample("SpeedTreeGPUWindDataUpdater.UpdateSpeedTreeWindAndUploadWindParamsToGPU"); + + // Become visible trees is a subset of visible trees. + var becomeVisibleTreeRendererIDs = visibleTreeRenderers.AsArray().GetSubArray(0, becomeVisibleTreeInstancesCount); + var becomeVisibleTreeInstances = visibleTreeInstances.AsArray().GetSubArray(0, becomeVisibleTreeInstancesCount); + + if (becomeVisibleTreeRendererIDs.Length > 0) + UpdateSpeedTreeWindAndUploadWindParamsToGPU(becomeVisibleTreeRendererIDs, becomeVisibleTreeInstances, history: true); + + UpdateSpeedTreeWindAndUploadWindParamsToGPU(visibleTreeRenderers.AsArray(), visibleTreeInstances.AsArray(), history: false); + + Profiler.EndSample(); + } + + visibleTreeRenderers.Dispose(); + visibleTreeInstances.Dispose(); + + Profiler.EndSample(); + } + + private unsafe void UpdateSpeedTreeWindAndUploadWindParamsToGPU(NativeArray treeRenderers, NativeArray treeInstances, bool history) + { + if (treeRenderers.Length == 0) + return; + + ref DefaultGPUComponents defaultGPUComponents = ref m_InstanceDataSystem.defaultGPUComponents; + + Assert.AreEqual(treeRenderers.Length, treeInstances.Length); + Assert.AreEqual(defaultGPUComponents.speedTreeWind.Length, (int)SpeedTreeWindParamIndex.MaxWindParamsCount); + + var gpuIndices = new NativeArray(treeInstances.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); + m_InstanceDataSystem.QueryInstanceGPUIndices(treeInstances, gpuIndices); + + if (!history) + m_InstanceDataSystem.UpdateInstanceWindDataHistory(gpuIndices); + + GPUInstanceUploadData uploadData = m_InstanceDataSystem.CreateInstanceUploadData(defaultGPUComponents.speedTreeWind, treeInstances.Length, Allocator.TempJob); + + EnsureUploadBufferUintCount(uploadData.uploadDataUIntSize); + + NativeArray writeBuffer = m_CPUUploadBuffer.GetSubArray(0, uploadData.uploadDataUIntSize); + + var windParams = new SpeedTreeWindParamsBufferIterator(); + windParams.bufferPtr = (IntPtr)writeBuffer.GetUnsafePtr(); + for (int i = 0; i < (int)SpeedTreeWindParamIndex.MaxWindParamsCount; ++i) + windParams.uintParamOffsets[i] = uploadData.PrepareComponentWrite(defaultGPUComponents.speedTreeWind[i]); + windParams.uintStride = UnsafeUtility.SizeOf() / UnsafeUtility.SizeOf(); + windParams.elementOffset = 0; + windParams.elementsCount = treeInstances.Length; + SpeedTreeWindManager.UpdateWindAndWriteBufferWindParams(treeRenderers, windParams, history); + + m_GPUUploadBuffer.SetData(writeBuffer); + + m_InstanceDataSystem.UploadDataToGPU(gpuIndices, m_GPUUploadBuffer, uploadData); + + uploadData.Dispose(); + gpuIndices.Dispose(); + } + + void EnsureUploadBufferUintCount(int uintCount) + { + int currentCPUBufferLength = m_CPUUploadBuffer.IsCreated ? m_CPUUploadBuffer.Length : 0; + int currentGPUBufferLength = m_GPUUploadBuffer != null ? m_GPUUploadBuffer.count : 0; + + Assert.IsTrue(currentCPUBufferLength == currentGPUBufferLength); + int currentUintCount = currentCPUBufferLength; + + if (uintCount > currentUintCount) + { + // At least double on resize + int newUintCount = math.max(currentUintCount * 2, uintCount); + + m_CPUUploadBuffer.Dispose(); + m_GPUUploadBuffer?.Release(); + + m_CPUUploadBuffer = new NativeArray(newUintCount, Allocator.Persistent); + m_GPUUploadBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Raw, newUintCount, sizeof(uint)); + } + + Assert.IsTrue(m_GPUUploadBuffer.count == m_CPUUploadBuffer.Length); + } + } +} diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/GPUResidentBatcher.SpeedTree.cs.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/SceneProcessors/SpeedTreeWindGPUDataUpdater.cs.meta similarity index 100% rename from Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/GPUResidentBatcher.SpeedTree.cs.meta rename to Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/SceneProcessors/SpeedTreeWindGPUDataUpdater.cs.meta diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/SceneProcessors/WorldProcessor.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/SceneProcessors/WorldProcessor.cs new file mode 100644 index 00000000000..5c68e41b1c5 --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/SceneProcessors/WorldProcessor.cs @@ -0,0 +1,300 @@ +using System; +using Unity.Burst; +using Unity.Collections; +using Unity.Jobs; +using UnityEngine.Profiling; + +namespace UnityEngine.Rendering +{ + internal class WorldProcessor : IDisposable + { + private GPUDrivenProcessor m_GPUDrivenProcessor; + private ObjectDispatcher m_ObjectDispatcher; + private InstanceDataSystem m_InstanceDataSystem; + private InstanceCullingBatcher m_Batcher; + private MeshRendererProcessor m_MeshRendererProcessor; + private LODGroupProcessor m_LODGroupProcessor; + + // Update batches + private NativeList m_MeshRendererUpdateBatches; + private NativeList m_LODGroupUpdateBatches; + private NativeList> m_MeshRendererDeletionBatches; + private NativeList> m_LODGroupDeletionBatches; + + public MeshRendererProcessor meshRendererProcessor => m_MeshRendererProcessor; + public LODGroupProcessor lodDGroupProcessor => m_LODGroupProcessor; + + public void Initialize(GPUDrivenProcessor gpuDrivenProcessor, ObjectDispatcher objectDispatcher, GPUResidentContext context) + { + m_GPUDrivenProcessor = gpuDrivenProcessor; + m_ObjectDispatcher = objectDispatcher; + m_InstanceDataSystem = context.instanceDataSystem; + m_Batcher = context.batcher; + m_LODGroupProcessor = new LODGroupProcessor(gpuDrivenProcessor, context); + m_MeshRendererProcessor = new MeshRendererProcessor(gpuDrivenProcessor, context); + + m_MeshRendererUpdateBatches = new NativeList(16, Allocator.Persistent); + m_LODGroupUpdateBatches = new NativeList(16, Allocator.Persistent); + m_MeshRendererDeletionBatches = new NativeList>(16, Allocator.Persistent); + m_LODGroupDeletionBatches = new NativeList>(16, Allocator.Persistent); + } + + public void Dispose() + { + m_MeshRendererUpdateBatches.Dispose(); + m_LODGroupUpdateBatches.Dispose(); + m_MeshRendererDeletionBatches.Dispose(); + m_LODGroupDeletionBatches.Dispose(); + m_MeshRendererProcessor.Dispose(); + + m_MeshRendererProcessor = null; + m_LODGroupProcessor = null; + } + + public void Update() + { + Profiler.BeginSample("WorldProcessor.Update"); + + Profiler.BeginSample("FetchAllChanges"); + var meshDataSorted = m_ObjectDispatcher.GetTypeChangesAndClear(Allocator.TempJob, sortByInstanceID: true, noScriptingArray: true); + var materialData = m_ObjectDispatcher.GetTypeChangesAndClear(Allocator.TempJob, noScriptingArray: true); + var cameraData = m_ObjectDispatcher.GetTypeChangesAndClear(Allocator.TempJob, noScriptingArray: true); + var lodGroupData = m_ObjectDispatcher.GetTypeChangesAndClear(Allocator.TempJob, noScriptingArray: true); + var lodGroupTransformData = m_ObjectDispatcher.GetTransformChangesAndClear(ObjectDispatcher.TransformTrackingType.GlobalTRS, Allocator.TempJob); + var rendererData = m_ObjectDispatcher.GetTypeChangesAndClear(Allocator.TempJob, noScriptingArray: true); + var transformChanges = m_ObjectDispatcher.GetTransformChangesAndClear(ObjectDispatcher.TransformTrackingType.GlobalTRS, Allocator.TempJob); + Profiler.EndSample(); + + if (cameraData.changedID.Length > 0) + { + Profiler.BeginSample("ProcessCameraChanges"); + m_InstanceDataSystem.AddCameras(cameraData.changedID); + m_InstanceDataSystem.RemoveCameras(cameraData.destroyedID); + Profiler.EndSample(); + } + + ClassifyMaterials(materialData.changedID, + materialData.destroyedID, + out NativeList unsupportedMaterials, + out NativeList changedMaterials, + out NativeList destroyedMaterials, + out NativeList changedMaterialDatas, + Allocator.TempJob); + + NativeList changedMeshes = FindOnlyUsedMeshes(meshDataSorted.changedID, Allocator.TempJob); + + NativeList unsupportedRenderers = FindUnsupportedRenderers(unsupportedMaterials.AsArray(), Allocator.TempJob); + + if (unsupportedRenderers.Length > 0) + { + Profiler.BeginSample("DestroyUnsupportedRenderers"); + m_GPUDrivenProcessor.DisableGPUDrivenRendering(unsupportedRenderers.AsArray()); + m_MeshRendererProcessor.DestroyInstances(unsupportedRenderers.AsArray()); + Profiler.EndSample(); + } + + m_Batcher.DestroyMaterials(destroyedMaterials.AsArray()); + m_Batcher.DestroyMaterials(unsupportedMaterials.AsArray()); + + if (meshDataSorted.destroyedID.Length > 0) + { + Profiler.BeginSample("DestroyMeshes"); + var destroyedMeshInstances = new NativeList(Allocator.TempJob); + m_InstanceDataSystem.ScheduleQuerySortedMeshInstancesJob(meshDataSorted.destroyedID, destroyedMeshInstances).Complete(); + m_Batcher.DestroyDrawInstances(destroyedMeshInstances.AsArray()); + //@ Check if we need to update instance bounds and light probe sampling positions after mesh is destroyed. + m_Batcher.DestroyMeshes(meshDataSorted.destroyedID); + destroyedMeshInstances.Dispose(); + Profiler.EndSample(); + } + + if (lodGroupData.changedID.Length > 0) + { + Profiler.BeginSample("ProcessLODGroupChanges"); + m_LODGroupProcessor.ProcessGameObjectChanges(lodGroupData.changedID, transformOnly: false); + Profiler.EndSample(); + } + + if (lodGroupTransformData.transformedID.Length > 0) + { + Profiler.BeginSample("ProcessLODGroupTransformChanges"); + m_LODGroupProcessor.ProcessGameObjectChanges(lodGroupTransformData.transformedID, transformOnly: true); + Profiler.EndSample(); + } + + if (lodGroupData.destroyedID.Length > 0) + m_LODGroupProcessor.DestroyInstances(lodGroupData.destroyedID); + + if (rendererData.changedID.Length > 0) + { + Profiler.BeginSample("ProcessMeshRendererChanges"); + m_MeshRendererProcessor.ProcessGameObjectChanges(rendererData.changedID); + Profiler.EndSample(); + } + + if (transformChanges.transformedID.Length > 0) + { + Profiler.BeginSample("ProcessMeshRendererTransformChanges"); + m_MeshRendererProcessor.ProcessGameObjectTransformChanges(transformChanges); + Profiler.EndSample(); + } + + if (rendererData.destroyedID.Length > 0) + m_MeshRendererProcessor.DestroyInstances(rendererData.destroyedID); + + Profiler.BeginSample("ProcessRendererMaterialAndMeshChanges"); + m_MeshRendererProcessor.ProcessRendererMaterialAndMeshChanges(rendererData.changedID, + changedMaterials.AsArray(), + changedMaterialDatas.AsArray(), + changedMeshes.AsArray()); + Profiler.EndSample(); + + try + { + ProcessUpdateBatches(); + } + finally + { + // Clear everything in a finally block so that GRD does not attempt to process update batches each frame after an exception was thrown. + // Since the update batches are only valid for a frame, this is always results in error spam. + ClearUpdateBatches(); + } + + m_InstanceDataSystem.UpdateInstanceMotions(); + m_InstanceDataSystem.ValidateTotalTreeCount(); + + unsupportedRenderers.Dispose(); + changedMaterials.Dispose(); + unsupportedMaterials.Dispose(); + destroyedMaterials.Dispose(); + changedMaterialDatas.Dispose(); + changedMeshes.Dispose(); + transformChanges.Dispose(); + rendererData.Dispose(); + lodGroupTransformData.Dispose(); + lodGroupData.Dispose(); + cameraData.Dispose(); + materialData.Dispose(); + meshDataSorted.Dispose(); + + Profiler.EndSample(); + } + + public void PushMeshRendererUpdateBatches(NativeArray batches) + { + foreach (var batch in batches) + { + batch.Validate(); + m_MeshRendererUpdateBatches.Add(batch); + } + } + + public void PushLODGroupUpdateBatches(NativeArray batches) + { + foreach (var batch in batches) + { + batch.Validate(); + m_LODGroupUpdateBatches.Add(batch); + } + } + + public void PushMeshRendererDeletionBatch(NativeArray> batches) + { + m_MeshRendererDeletionBatches.AddRange(batches); + } + + public void PushLODGroupDeletionBatch(NativeArray> batches) + { + m_LODGroupDeletionBatches.AddRange(batches); + } + + private void ProcessUpdateBatches() + { + foreach (var batch in m_LODGroupDeletionBatches) + m_LODGroupProcessor.DestroyInstances(batch); + + foreach (var batch in m_MeshRendererDeletionBatches) + m_MeshRendererProcessor.DestroyInstances(batch); + + // Update LODs before instances otherwise some LODGroupIDs might be unknown when updating the instances + for (int i = 0; i < m_LODGroupUpdateBatches.Length; i++) + { + m_LODGroupProcessor.ProcessUpdateBatch(m_LODGroupUpdateBatches.ElementAt(i)); + } + + for (int i = 0; i < m_MeshRendererUpdateBatches.Length; i++) + { + m_MeshRendererProcessor.ProcessUpdateBatch(ref m_MeshRendererUpdateBatches.ElementAt(i)); + } + } + + private void ClearUpdateBatches() + { + foreach (var batch in m_MeshRendererDeletionBatches) + batch.Dispose(); + m_MeshRendererDeletionBatches.Clear(); + + foreach (var batch in m_LODGroupDeletionBatches) + batch.Dispose(); + m_LODGroupDeletionBatches.Clear(); + + foreach (var batch in m_MeshRendererUpdateBatches) + batch.Dispose(); + m_MeshRendererUpdateBatches.Clear(); + + foreach (var batch in m_LODGroupUpdateBatches) + batch.Dispose(); + m_LODGroupUpdateBatches.Clear(); + } + + public void ClassifyMaterials(NativeArray allChangedMaterials, + NativeArray allDestroyedMaterials, + out NativeList unsupportedMaterials, + out NativeList changedMaterials, + out NativeList destroyedMaterials, + out NativeList changedMaterialDatas, + Allocator allocator) + { + Profiler.BeginSample("ClassifyMaterials"); + + WorldProcessorBurst.ClassifyMaterials(m_Batcher.materialMap, + allChangedMaterials, + allDestroyedMaterials, + out changedMaterials, + out unsupportedMaterials, + out destroyedMaterials, + out changedMaterialDatas, + allocator); + + Profiler.EndSample(); + } + + public NativeList FindOnlyUsedMeshes(NativeArray changedMeshes, Allocator allocator) + { + NativeList usedMeshes; + + Profiler.BeginSample("FindOnlyUsedMeshes"); + WorldProcessorBurst.FindOnlyUsedMeshes(m_Batcher.meshMap, changedMeshes, allocator, out usedMeshes); + Profiler.EndSample(); + + return usedMeshes; + } + + private NativeList FindUnsupportedRenderers(NativeArray unsupportedMaterials, Allocator allocator) + { + Profiler.BeginSample("FindUnsupportedRenderers"); + + var unsupportedRenderers = new NativeList(allocator); + + if (unsupportedMaterials.Length > 0) + { + ref RenderWorld renderWorld = ref m_InstanceDataSystem.renderWorld; + + WorldProcessorBurst.FindUnsupportedRenderers(unsupportedMaterials, renderWorld.materialIDArrays, renderWorld.instanceIDs, ref unsupportedRenderers); + } + + Profiler.EndSample(); + return unsupportedRenderers; + } + } +} diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/GPUResidentBatcher.cs.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/SceneProcessors/WorldProcessor.cs.meta similarity index 100% rename from Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/GPUResidentBatcher.cs.meta rename to Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/SceneProcessors/WorldProcessor.cs.meta diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/SceneProcessors/WorldProcessorBurst.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/SceneProcessors/WorldProcessorBurst.cs new file mode 100644 index 00000000000..b28b99a4c29 --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/SceneProcessors/WorldProcessorBurst.cs @@ -0,0 +1,108 @@ +using Unity.Collections; +using Unity.Burst; + +namespace UnityEngine.Rendering +{ + [BurstCompile] + internal static class WorldProcessorBurst + { + [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] + public static void ClassifyMaterials(in NativeParallelHashMap materialMap, + in NativeArray allChangedMaterials, + in NativeArray allDestroyedMaterials, + out NativeList supportedChangedMaterials, + out NativeList unsupportedChangedMaterials, + out NativeList destroyedMaterials, + out NativeList supportedChangedMaterialDatas, + Allocator allocator) + { + var usedChangedMaterials = new NativeList(16, Allocator.Temp); + + foreach (EntityId material in allChangedMaterials) + { + if (materialMap.ContainsKey(material)) + usedChangedMaterials.Add(material); + } + + supportedChangedMaterials = new NativeList(allChangedMaterials.Length, allocator); + unsupportedChangedMaterials = new NativeList(allChangedMaterials.Length, allocator); + supportedChangedMaterialDatas = new NativeList(allChangedMaterials.Length, allocator); + + if (!usedChangedMaterials.IsEmpty) + { + unsupportedChangedMaterials.Resize(usedChangedMaterials.Length, NativeArrayOptions.UninitializedMemory); + supportedChangedMaterials.Resize(usedChangedMaterials.Length, NativeArrayOptions.UninitializedMemory); + supportedChangedMaterialDatas.Resize(usedChangedMaterials.Length, NativeArrayOptions.UninitializedMemory); + + int unsupportedMaterialCount = GPUDrivenProcessor.ClassifyMaterials(usedChangedMaterials.AsArray(), + unsupportedChangedMaterials.AsArray(), + supportedChangedMaterials.AsArray(), + supportedChangedMaterialDatas.AsArray()); + + unsupportedChangedMaterials.Resize(unsupportedMaterialCount, NativeArrayOptions.ClearMemory); + supportedChangedMaterials.Resize(usedChangedMaterials.Length - unsupportedMaterialCount, NativeArrayOptions.ClearMemory); + supportedChangedMaterialDatas.Resize(supportedChangedMaterials.Length, NativeArrayOptions.ClearMemory); + } + + destroyedMaterials = new NativeList(allDestroyedMaterials.Length, allocator); + + foreach (var destroyedMaterial in allDestroyedMaterials) + { + // Unused material, don't add to the list + if (!materialMap.ContainsKey(destroyedMaterial)) + continue; + + // Edge case: If the material has been both changed and destroyed, we can't know for sure what should be done with it. + // If it's in the supported list though, it means it still exists and it was possible to fetch material data. + // So in this case assume it was changed and don't append to the destroy list. + if (supportedChangedMaterials.Contains(destroyedMaterial)) + continue; + + // If the material is unsupported don't also add to the destroy list. + if (unsupportedChangedMaterials.Contains(destroyedMaterial)) + continue; + + destroyedMaterials.Add(destroyedMaterial); + } + } + + [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] + public static void FindOnlyUsedMeshes(in NativeParallelHashMap meshMap, + in NativeArray changedMeshes, + Allocator allocator, + out NativeList usedMeshes) + { + usedMeshes = new NativeList(16, allocator); + + foreach (EntityId mesh in changedMeshes) + { + if (meshMap.ContainsKey(mesh)) + usedMeshes.Add(mesh); + } + } + + [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] + public static void FindUnsupportedRenderers(in NativeArray unsupportedMaterials, + in NativeArray> materialArrays, + in NativeArray renderers, + ref NativeList unsupportedRenderers) + { + for (int arrayIndex = 0; arrayIndex < materialArrays.Length; arrayIndex++) + { + EmbeddedArray32 materials = materialArrays[arrayIndex]; + EntityId renderer = renderers[arrayIndex]; + + for (int i = 0; i < materials.Length; i++) + { + EntityId material = materials[i]; + + if (unsupportedMaterials.Contains(material)) + { + unsupportedRenderers.Add(renderer); + break; + } + } + } + } + } +} diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/SceneProcessors/WorldProcessorBurst.cs.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/SceneProcessors/WorldProcessorBurst.cs.meta new file mode 100644 index 00000000000..44109b74569 --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/SceneProcessors/WorldProcessorBurst.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 9dbad2118b2e7704aa686c28ea8a1f3c \ No newline at end of file diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Utilities.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Utilities.meta index 5e250a55c07..75ef3baf0c0 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Utilities.meta +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Utilities.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 0d54b02a80b94d94a90b9de832436341 +guid: 77b47391851f11747b5ee5e17a8ac87b folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/AABB.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Utilities/AABB.cs similarity index 61% rename from Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/AABB.cs rename to Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Utilities/AABB.cs index d99f5112f0d..22ff51063d4 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/AABB.cs +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Utilities/AABB.cs @@ -1,5 +1,4 @@ using System; -using UnityEngine; using Unity.Mathematics; namespace UnityEngine.Rendering @@ -18,19 +17,6 @@ public override string ToString() { return $"AABB(Center:{center}, Extents:{extents}"; } - - static float3 RotateExtents(float3 extents, float3 m0, float3 m1, float3 m2) - { - return math.abs(m0 * extents.x) + math.abs(m1 * extents.y) + math.abs(m2 * extents.z); - } - - public static AABB Transform(float4x4 transform, AABB localBounds) - { - AABB transformed; - transformed.extents = RotateExtents(localBounds.extents, transform.c0.xyz, transform.c1.xyz, transform.c2.xyz); - transformed.center = math.transform(transform, localBounds.center); - return transformed; - } } internal static class AABBExtensions diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/AABB.cs.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Utilities/AABB.cs.meta similarity index 100% rename from Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/AABB.cs.meta rename to Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Utilities/AABB.cs.meta diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Utilities/EmbeddedArray.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Utilities/EmbeddedArray.cs new file mode 100644 index 00000000000..59757d51aa1 --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Utilities/EmbeddedArray.cs @@ -0,0 +1,131 @@ +using System; +using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; +using UnityEngine.Assertions; + +namespace UnityEngine.Rendering +{ + internal unsafe struct EmbeddedArray32 : IDisposable where T : unmanaged + { + FixedList32Bytes m_FixedArray; + UnsafeList m_List; + int m_Length; + bool m_Created; + bool m_IsEmbedded; + + public int Length => m_Length; + + public EmbeddedArray32(NativeArray array, Allocator allocator) + { + m_FixedArray = default; + m_List = default; + m_Length = array.Length; + m_Created = true; + + if (array.Length <= m_FixedArray.Capacity) + { + m_FixedArray.AddRangeNoResize(array.GetUnsafeReadOnlyPtr(), array.Length); + m_IsEmbedded = true; + } + else + { + m_List = new UnsafeList(array.Length, allocator, NativeArrayOptions.UninitializedMemory); + m_List.AddRangeNoResize(array.GetUnsafeReadOnlyPtr(), array.Length); + m_IsEmbedded = false; + } + } + + public T this[int index] + { + get + { + Assert.IsTrue(m_Created && index < Length); + + if (m_IsEmbedded) + return m_FixedArray[index]; + else + return m_List[index]; + } + set + { + Assert.IsTrue(m_Created && index < Length); + + if (m_IsEmbedded) + m_FixedArray[index] = value; + else + m_List[index] = value; + } + } + + public unsafe void Dispose() + { + if (!m_Created) + return; + + m_List.Dispose(); + m_Created = false; + } + } + + internal unsafe struct EmbeddedArray64 : IDisposable where T : unmanaged + { + FixedList64Bytes m_FixedArray; + UnsafeList m_List; + int m_Length; + bool m_Created; + bool m_IsEmbedded; + + public int Length => m_Length; + + public EmbeddedArray64(NativeArray array, Allocator allocator) + { + m_FixedArray = default; + m_List = default; + m_Length = array.Length; + m_Created = true; + + if (array.Length <= m_FixedArray.Capacity) + { + m_FixedArray.AddRangeNoResize(array.GetUnsafeReadOnlyPtr(), array.Length); + m_IsEmbedded = true; + } + else + { + m_List = new UnsafeList(array.Length, allocator, NativeArrayOptions.UninitializedMemory); + m_List.AddRangeNoResize(array.GetUnsafeReadOnlyPtr(), array.Length); + m_IsEmbedded = false; + } + } + + public T this[int index] + { + get + { + Assert.IsTrue(m_Created && index < Length); + + if (m_IsEmbedded) + return m_FixedArray[index]; + else + return m_List[index]; + } + set + { + Assert.IsTrue(m_Created && index < Length); + + if (m_IsEmbedded) + m_FixedArray[index] = value; + else + m_List[index] = value; + } + } + + public unsafe void Dispose() + { + if (!m_Created) + return; + + m_List.Dispose(); + m_Created = false; + } + } +} diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Utilities/EmbeddedArray.cs.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Utilities/EmbeddedArray.cs.meta new file mode 100644 index 00000000000..e450ec391a9 --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Utilities/EmbeddedArray.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: a722f3be8f8fbb146a033ed4785f895e diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/FrustumPlanes.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Utilities/FrustumPlanes.cs similarity index 100% rename from Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/FrustumPlanes.cs rename to Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Utilities/FrustumPlanes.cs diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/FrustumPlanes.cs.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Utilities/FrustumPlanes.cs.meta similarity index 100% rename from Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/FrustumPlanes.cs.meta rename to Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Utilities/FrustumPlanes.cs.meta diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Utilities/GPUResidentUtils.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Utilities/GPUResidentUtils.cs new file mode 100644 index 00000000000..13c69f0f2bf --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Utilities/GPUResidentUtils.cs @@ -0,0 +1,118 @@ +using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; +using Unity.Jobs; +using Unity.Mathematics; +using UnityEngine.Assertions; + +namespace UnityEngine.Rendering +{ + internal static class GPUResidentUtils + { + public static void RunParallelByRef(this ref T jobData, int arrayLength, int innerloopBatchCount, JobHandle dependsOn = default) where T : unmanaged, IJobParallelFor + { + if (arrayLength > innerloopBatchCount) + { + jobData.ScheduleByRef(arrayLength, innerloopBatchCount, dependsOn).Complete(); + } + else + { + dependsOn.Complete(); + jobData.RunByRef(arrayLength); + } + } + + public static void RunParallel(this T jobData, int arrayLength, int innerloopBatchCount, JobHandle dependsOn = default) where T : unmanaged, IJobParallelFor + { + RunParallelByRef(ref jobData, arrayLength, innerloopBatchCount, dependsOn); + } + + public static void RunBatchParallelByRef(this ref T jobData, int arrayLength, int innerloopBatchCount, JobHandle dependsOn = default) where T : unmanaged, IJobParallelForBatch + { + if (arrayLength > innerloopBatchCount) + { + jobData.ScheduleBatchByRef(arrayLength, innerloopBatchCount, dependsOn).Complete(); + } + else + { + dependsOn.Complete(); + jobData.RunByRef(arrayLength, innerloopBatchCount); + } + } + + public static void RunBatchParallel(this T jobData, int arrayLength, int innerloopBatchCount, JobHandle dependsOn = default) where T : unmanaged, IJobParallelForBatch + { + RunBatchParallelByRef(ref jobData, arrayLength, innerloopBatchCount, dependsOn); + } + + public static unsafe ref T ElementAtRW(this NativeArray array, int index) where T : unmanaged + { + return ref UnsafeUtility.ArrayElementAsRef(array.GetUnsafePtr(), index); + } + + public static unsafe ref readonly T ElementAt(this NativeArray array, int index) where T : unmanaged + { + return ref UnsafeUtility.ArrayElementAsRef(array.GetUnsafeReadOnlyPtr(), index); + } + + public static unsafe NativeArray AsNativeArray(this UnsafeList list) where T : unmanaged + { + var array = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray(list.Ptr, + list.Length, + Allocator.None); + +#if ENABLE_UNITY_COLLECTIONS_CHECKS + NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref array, AtomicSafetyHandle.Create()); +#endif + + return array; + } + + public static unsafe UnsafeList AsUnsafeList(this NativeArray array) where T : unmanaged + { + return new UnsafeList((T*)array.GetUnsafePtr(), array.Length); + } + + public static unsafe UnsafeList AsUnsafeListReadOnly(this NativeArray array) where T : unmanaged + { + return new UnsafeList((T*)array.GetUnsafeReadOnlyPtr(), array.Length); + } + + public static unsafe UnsafeList AsUntypedUnsafeList(this UnsafeList> list) where T : unmanaged + { + Assert.IsTrue(UnsafeUtility.SizeOf>() == UnsafeUtility.SizeOf()); + return *(UnsafeList*)&list; + } + + public static unsafe UnsafeBitArray AsUnsafeBitArray(this in NativeBitArray section) => *section.m_BitArray; + + public static unsafe ref T GetRef(this NativeReference reference) where T : unmanaged => ref *reference.GetUnsafePtr(); + public static unsafe ref readonly T GetRefRO(this NativeReference reference) where T : unmanaged => ref *reference.GetUnsafeReadOnlyPtr(); + + public static bool HasAnyBit(this MeshRendererComponentMask mask, MeshRendererComponentMask bits) => (mask & bits) != 0; + + public static bool HasAnyBit(this LODGroupComponentMask mask, LODGroupComponentMask bits) => (mask & bits) != 0; + } + + internal static class LightmapUtils + { + // See doc for more infos on lightmap index special values -1 and -2 + // https://docs.unity3d.com/Documentation/ScriptReference/Renderer-lightmapIndex.html + + // Object doesn't use lightmaps and don't influence them. + public const short LightmapIndexNull = -1; + + // Object only influences lightmaps, but does not use them itself. + public const short LightmapIndexInfluenceOnly = -2; + + public static readonly float4 kDefaultLightmapST = new float4(1.0f, 1.0f, 0.0f, 0.0f); + + public static bool IsNull(int lightmapIndex) => ((short)lightmapIndex) == LightmapIndexNull; + + public static bool IsInfluenceOnly(int lightmapIndex) => ((short)lightmapIndex) == LightmapIndexInfluenceOnly; + + public static bool UsesLightmaps(int lightmapIndex) => ((short)lightmapIndex) >= 0; + + // If the object is light mapped, or has the special influence-only value, it affects lightmaps + public static bool AffectsLightmaps(int lightmapIndex) => ((short)lightmapIndex) >= 0 || ((short)lightmapIndex) == LightmapIndexInfluenceOnly; + } +} diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Utilities/GPUResidentUtils.cs.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Utilities/GPUResidentUtils.cs.meta new file mode 100644 index 00000000000..ab4c1979e64 --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Utilities/GPUResidentUtils.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: c996da3d8d7bf4046a6be511a477e3ee \ No newline at end of file diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/GeometryUtilities.hlsl b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Utilities/GeometryUtilities.hlsl similarity index 100% rename from Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/GeometryUtilities.hlsl rename to Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Utilities/GeometryUtilities.hlsl diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/GeometryUtilities.hlsl.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Utilities/GeometryUtilities.hlsl.meta similarity index 100% rename from Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/GeometryUtilities.hlsl.meta rename to Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Utilities/GeometryUtilities.hlsl.meta diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Utilities/JaggedJobRange.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Utilities/JaggedJobRange.cs new file mode 100644 index 00000000000..1d17092accc --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Utilities/JaggedJobRange.cs @@ -0,0 +1,75 @@ +using Unity.Burst; +using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; +using Unity.Jobs; +using Unity.Jobs.LowLevel.Unsafe; +using Unity.Mathematics; +using UnityEngine.Assertions; +using UnityEngine.Profiling; + +namespace UnityEngine.Rendering +{ + internal struct JaggedJobRange + { + public int sectionIndex; + public int localStart; + public int absoluteStart; + public int length; + + public int localEnd => localStart + length; + public int absoluteEnd => absoluteStart + length; + + public static NativeList FromSpanWithRelaxedBatchSize(JaggedSpan jaggedSpan, int batchSizeHint, Allocator allocator) where T : unmanaged + { + return ComputeRanges(jaggedSpan, batchSizeHint, canExceedBatchSizeHint: true, allocator); + } + + public static NativeList FromSpanWithMaxBatchSize(JaggedSpan jaggedSpan, int maxBatchSize, Allocator allocator) where T : unmanaged + { + return ComputeRanges(jaggedSpan, maxBatchSize, canExceedBatchSizeHint: false, allocator); + } + + private static NativeList ComputeRanges(JaggedSpan jaggedSpan, int batchSizeHint, bool canExceedBatchSizeHint, Allocator allocator) where T : unmanaged + { + Assert.IsTrue(batchSizeHint > 0); + Assert.IsTrue(allocator == Allocator.TempJob || allocator == Allocator.Persistent, + "Allocator must be either TempJob or Persistent"); + + if (jaggedSpan.sectionCount == 0) + return default; + + var jobRanges = new NativeList(allocator); + + JaggedJobRangeBurst.ComputeRanges(JobsUtility.JobWorkerCount, batchSizeHint, jaggedSpan.totalLength, canExceedBatchSizeHint, + jaggedSpan.untypedSections, ref jobRanges); + + return jobRanges; + } + } + + internal static class JaggedJobRangeExtensions + { + public static JobHandle Schedule(this T job, in NativeList jobRanges, JobHandle dependsOn = default) where T : unmanaged, IJobParallelFor + { + return jobRanges.IsEmpty ? dependsOn : job.ScheduleByRef(jobRanges.Length, 1, dependsOn); + } + + public static JobHandle ScheduleByRef(ref this T job, in NativeList jobRanges, JobHandle dependsOn = default) where T : unmanaged, IJobParallelFor + { + return jobRanges.IsEmpty ? dependsOn : job.ScheduleByRef(jobRanges.Length, 1, dependsOn); + } + + public static void RunParallel(this T job, in NativeList jobRanges, JobHandle dependsOn = default) where T : unmanaged, IJobParallelFor + { + if (jobRanges.Length == 1) + { + dependsOn.Complete(); + job.RunByRef(1); + } + else + { + job.ScheduleByRef(jobRanges, dependsOn).Complete(); + } + } + } +} diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Utilities/JaggedJobRange.cs.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Utilities/JaggedJobRange.cs.meta new file mode 100644 index 00000000000..12fe162e13a --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Utilities/JaggedJobRange.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 1e113f4d261f3b74b818176f34f8c2f1 \ No newline at end of file diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Utilities/JaggedJobRangeBurst.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Utilities/JaggedJobRangeBurst.cs new file mode 100644 index 00000000000..c400182bc85 --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Utilities/JaggedJobRangeBurst.cs @@ -0,0 +1,75 @@ +using Unity.Collections; +using Unity.Burst; +using Unity.Collections.LowLevel.Unsafe; +using Unity.Mathematics; +using UnityEngine.Assertions; + +namespace UnityEngine.Rendering +{ + [BurstCompile] + internal static class JaggedJobRangeBurst + { + private static int ComputeIdealJobCount(int totalLength, int batchSizeHint, int workerThreadCount) + { + Assert.IsTrue(totalLength > 0); + Assert.IsTrue(batchSizeHint > 0); + + // If there are no worker threads then it should run on the main thread. + if (workerThreadCount == 0) + return 1; + + int jobCountBasedOnSizeHint = CoreUtils.DivRoundUp(totalLength, batchSizeHint); + + // Most jobs are waited for on the thread that schedules them. So to even it out (workerThreadCount + 1). + // Then * 2 because often jobs are not executed perfectly even so if some jobs are longer at least it distributes a bit more. + // This is no perfect math, just empirically tweaked on profiling data. + int jobCountBasedOnWorkerThreads = (workerThreadCount + 1) * 2; + + return math.min(jobCountBasedOnSizeHint, jobCountBasedOnWorkerThreads); + } + + [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] + public static void ComputeRanges(int workerThreadCount, int batchSizeHint, int totalLength, bool canExceedBatchSizeHint, + in NativeArray sections, ref NativeList jobRanges) + { + int idealJobCount = ComputeIdealJobCount(totalLength, batchSizeHint, workerThreadCount); + Assert.IsTrue(idealJobCount > 0); + + int idealBatchSize = CoreUtils.DivRoundUp(totalLength, idealJobCount); + Assert.IsTrue(idealBatchSize > 0); + + // Try to account for the jobs being possibly split when they would otherwise span over more than one chunk + int jobRangesInitialCapacity = idealJobCount * 2; + jobRanges.SetCapacity(jobRangesInitialCapacity); + + int absoluteStart = 0; + for (int sectionIndex = 0; sectionIndex < sections.Length; sectionIndex++) + { + int localStart = 0; + int remainingLength = sections.ElementAt(sectionIndex).m_length; + while (remainingLength > 0) + { + // Check for idealBatchSize * 1.5 so that if there is small batch left a the end it is merged with the previous job. + int batchSizeUncapped = remainingLength >= (int)(idealBatchSize * 1.5) ? idealBatchSize : remainingLength; + + int batchSize = batchSizeUncapped; + if (!canExceedBatchSizeHint && batchSize > batchSizeHint) + batchSize = batchSizeHint; + + JaggedJobRange jobRange = default; + jobRange.sectionIndex = sectionIndex; + jobRange.absoluteStart = absoluteStart; + jobRange.localStart = localStart; + jobRange.length = batchSize; + jobRanges.Add(jobRange); + + localStart += batchSize; + absoluteStart += batchSize; + remainingLength -= batchSize; + } + + Assert.AreEqual(remainingLength, 0); + } + } + } +} diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Utilities/JaggedJobRangeBurst.cs.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Utilities/JaggedJobRangeBurst.cs.meta new file mode 100644 index 00000000000..08f8a99dff6 --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Utilities/JaggedJobRangeBurst.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 35f8971d8d5e7b74ba94c42615c1e65e \ No newline at end of file diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Utilities/JaggedSpan.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Utilities/JaggedSpan.cs new file mode 100644 index 00000000000..60c7606139b --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Utilities/JaggedSpan.cs @@ -0,0 +1,166 @@ +using System; +using System.Runtime.InteropServices; +using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; +using Unity.Jobs; + +namespace UnityEngine.Rendering +{ + [StructLayout(LayoutKind.Sequential)] + internal struct JaggedSpan : IDisposable where T : unmanaged + { + UnsafeList> m_Sections; + int m_TotalLength; + + public bool isCreated => m_Sections.IsCreated; + public int sectionCount => m_Sections.IsCreated ? m_Sections.Length : 0; + public int totalLength => m_TotalLength; + public bool isEmpty => totalLength == 0; + public NativeArray> sections => m_Sections.IsCreated ? m_Sections.AsNativeArray() : default; + public NativeArray untypedSections => m_Sections.IsCreated ? m_Sections.AsUntypedUnsafeList().AsNativeArray() : default; + + public JaggedSpan(int initialCapacity, Allocator allocator) + { + m_Sections = new UnsafeList>(initialCapacity, allocator); + m_TotalLength = 0; + } + + public void Dispose() + { + m_Sections.Dispose(); + } + + public JobHandle Dispose(JobHandle jobHandle) + { + return m_Sections.Dispose(jobHandle); + } + + // NativeArray lifetime must match or exceed the lifetime of the JaggedSpan + public void Add(in NativeArray section) + { + m_Sections.Add(SectionAsUnsafeList(section)); + m_TotalLength += section.Length; + } + + public bool HasSameLayout(in JaggedSpan other) + where U : unmanaged + { + if (sectionCount != other.sectionCount) + return false; + + for (int i = 0; i < sectionCount; i++) + { + if (this[i].Length != other[i].Length) + return false; + } + + return true; + } + + public NativeArray this[int index] + { + get => SectionAsArray(m_Sections[index]); + set + { + ref UnsafeList section = ref m_Sections.ElementAt(index); + int deltaLength = value.Length - section.Length; + + section = SectionAsUnsafeList(value); + m_TotalLength += deltaLength; + } + } + + private static NativeArray SectionAsArray(in UnsafeList section) => section.AsNativeArray(); + private static UnsafeList SectionAsUnsafeList(in NativeArray section) => section.AsUnsafeList(); + } + + internal struct JaggedBitSpan + { + UnsafeList m_Sections; + int m_TotalLength; + + public bool isCreated => m_Sections.IsCreated; + public int sectionCount => m_Sections.IsCreated ? m_Sections.Length : 0; + public int totalLength => m_TotalLength; + public bool isEmpty => totalLength == 0; + public NativeArray sections => m_Sections.IsCreated ? m_Sections.AsNativeArray() : default; + + public JaggedBitSpan(int initialCapacity, Allocator allocator) + { + m_Sections = new UnsafeList(initialCapacity, allocator); + m_TotalLength = 0; + } + + public void Dispose() + { + m_Sections.Dispose(); + } + + public JobHandle Dispose(JobHandle jobHandle) + { + return m_Sections.Dispose(jobHandle); + } + + public void Add(in NativeBitArray section) + { + m_Sections.Add(section.AsUnsafeBitArray()); + m_TotalLength += section.Length; + } + + public bool HasSameLayout(in JaggedSpan other) + where U : unmanaged + { + if (sectionCount != other.sectionCount) + return false; + + for (int i = 0; i < sectionCount; i++) + { + if (this[i].Length != other[i].Length) + return false; + } + + return true; + } + + public UnsafeBitArray this[int index] + { + get => m_Sections[index]; + set + { + ref UnsafeBitArray section = ref m_Sections.ElementAt(index); + int deltaLength = value.Length - section.Length; + + section = value; + m_TotalLength += deltaLength; + } + } + } + + internal static class JaggedSpanExtensions + { + public static JaggedSpan ToJaggedSpan(this NativeArray array, Allocator allocator) where T : unmanaged + { + var jaggedSpan = new JaggedSpan(1, allocator); + jaggedSpan.Add(array); + return jaggedSpan; + } + + public static bool HasDuplicates(this JaggedSpan jaggedSpan) + { + var uniqueItems = new NativeHashSet(jaggedSpan.totalLength, Allocator.Temp); + + for (int s = 0; s < jaggedSpan.sectionCount; s++) + { + NativeArray section = jaggedSpan[s]; + + for (int i = 0; i < section.Length; i++) + { + if (!uniqueItems.Add(section[i])) + return true; + } + } + + return false; + } + } +} diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Utilities/JaggedSpan.cs.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Utilities/JaggedSpan.cs.meta new file mode 100644 index 00000000000..05e717bd440 --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Utilities/JaggedSpan.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 97a69240652682c46913f30d9e721493 \ No newline at end of file diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/LODRenderingUtils.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Utilities/LODRenderingUtils.cs similarity index 95% rename from Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/LODRenderingUtils.cs rename to Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Utilities/LODRenderingUtils.cs index 8043ba9d280..b5534f006a0 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/LODRenderingUtils.cs +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Utilities/LODRenderingUtils.cs @@ -1,7 +1,3 @@ -using System.Collections; -using System.Collections.Generic; -using Unity.Mathematics; -using UnityEngine; namespace UnityEngine.Rendering { diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/LODRenderingUtils.cs.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Utilities/LODRenderingUtils.cs.meta similarity index 100% rename from Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/LODRenderingUtils.cs.meta rename to Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Utilities/LODRenderingUtils.cs.meta diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Utilities/NativeHandleAllocator.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Utilities/NativeHandleAllocator.cs new file mode 100644 index 00000000000..2bd3f792734 --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Utilities/NativeHandleAllocator.cs @@ -0,0 +1,206 @@ +using System; +using Unity.Burst; +using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; +using Unity.Mathematics; +using UnityEngine.Assertions; + +namespace UnityEngine.Rendering +{ + internal struct NativeHandleAllocator + { + private const int InvalidChunkIndex = -1; + private const int BitChunkSize = 32; + + // Store length/freeCount inside an UnsafeList of size 2 so that they can be modified from Burst jobs while the struct is passed by value. + private UnsafeList m_StructData; + + private UnsafeList m_FreeBitChunksDense; + private UnsafeList m_FreeChunkIndicesSparse; + + public int length { get => m_StructData[0]; private set => m_StructData[0] = value; } + + public int freeCount { get => m_StructData[1]; private set => m_StructData[1] = value; } + + public int allocatedCount => isValid ? length - freeCount : 0; + + public bool isValid => m_StructData.IsCreated; + + public void Initialize(int initialCapacity = 128) + { + Assert.IsTrue(math.ispow2(BitChunkSize)); + + int bitChunkCount = CoreUtils.DivRoundUp(initialCapacity, BitChunkSize); + + m_StructData = new UnsafeList(2, Allocator.Persistent) { 0, 0 }; + m_FreeBitChunksDense = new UnsafeList(bitChunkCount, Allocator.Persistent); + m_FreeChunkIndicesSparse = new UnsafeList(bitChunkCount, Allocator.Persistent); + } + + public void Dispose() + { + if (isValid) + { + m_StructData.Dispose(); + m_FreeBitChunksDense.Dispose(); + m_FreeChunkIndicesSparse.Dispose(); + } + } + + public int Allocate() + { + Assert.IsTrue(isValid, "Allocator not initialized."); + + if (freeCount == 0) + { + Assert.IsTrue(m_FreeBitChunksDense.Length == 0, "Found non empty handles chunks while freeHandles is zero."); + return length++; + } + + Assert.IsTrue(m_FreeBitChunksDense.Length > 0, "No free chunks available."); + + int lastChunkIndex = m_FreeBitChunksDense.Length - 1; + FreeBitsChunk freeBitsChunk = m_FreeBitChunksDense[lastChunkIndex]; + Assert.IsTrue(freeBitsChunk.freeBits != 0, "No free bits in the chunk."); + + int freeBitIndex = math.tzcnt(freeBitsChunk.freeBits); + freeBitsChunk.freeBits &= ~(1u << freeBitIndex); + m_FreeBitChunksDense[lastChunkIndex] = freeBitsChunk; + + int handle = freeBitsChunk.chunk * BitChunkSize + freeBitIndex; + Assert.IsTrue(handle < length, "Handle exceeds length."); + + if (freeBitsChunk.freeBits == 0) + { + m_FreeChunkIndicesSparse[freeBitsChunk.chunk] = InvalidChunkIndex; + m_FreeBitChunksDense.Resize(m_FreeBitChunksDense.Length - 1); + } + + freeCount -= 1; + + return handle; + } + + public void Free(int handle) + { + Assert.IsTrue(math.ispow2(BitChunkSize)); + Assert.IsTrue(isValid, "Allocator not initialized."); + Assert.IsTrue(handle >= 0 && handle < length, "Handle is out of allocated range."); + + int chunk = handle / BitChunkSize; + int bitIndex = handle & (BitChunkSize - 1); + uint bitMask = 1u << bitIndex; + + int chunkIndex; + + if (chunk >= m_FreeChunkIndicesSparse.Length) + { + m_FreeChunkIndicesSparse.AddReplicate(InvalidChunkIndex, chunk - m_FreeChunkIndicesSparse.Length + 1); + chunkIndex = InvalidChunkIndex; + } + else + { + chunkIndex = m_FreeChunkIndicesSparse[chunk]; + } + + if (chunkIndex != InvalidChunkIndex) + { + FreeBitsChunk freeBitsChunk = m_FreeBitChunksDense[chunkIndex]; + Assert.IsTrue((freeBitsChunk.freeBits & bitMask) == 0, "Handle is freed already."); + Assert.IsTrue(freeBitsChunk.chunk == chunk, "Chunk index mismatch."); + freeBitsChunk.freeBits |= bitMask; + m_FreeBitChunksDense[chunkIndex] = freeBitsChunk; + } + else + { + m_FreeChunkIndicesSparse[chunk] = m_FreeBitChunksDense.Length; + m_FreeBitChunksDense.Add(new FreeBitsChunk(chunk, bitMask)); + } + + freeCount += 1; + } + + internal void TrimLengthImpl() + { + if (!isValid || length == 0) + return; + + int lastChunk = (length - 1) / BitChunkSize; + + if (lastChunk != m_FreeChunkIndicesSparse.Length - 1) + return; + + int usedBitsInChunk = Math.Max(length - BitChunkSize * lastChunk, 0); + int nonUsedBitsInChunk = BitChunkSize - usedBitsInChunk; + uint usedBitsMask = 0xFFFFFFFF >> nonUsedBitsInChunk; + + while (lastChunk >= 0) + { + int lastChunkIndex = m_FreeChunkIndicesSparse[lastChunk]; + + if (lastChunkIndex == InvalidChunkIndex) + return; + + FreeBitsChunk lastBitsChunk = m_FreeBitChunksDense[lastChunkIndex]; + Assert.IsTrue(lastBitsChunk.chunk == lastChunk, "Chunk index mismatch."); + + int freeLeadBits = math.lzcnt(~lastBitsChunk.freeBits & usedBitsMask) - nonUsedBitsInChunk; + lastBitsChunk.freeBits &= (uint)((ulong)usedBitsMask >> freeLeadBits); + + if (lastBitsChunk.freeBits == 0) + { + int lastDenseChunk = m_FreeBitChunksDense[m_FreeBitChunksDense.Length - 1].chunk; + m_FreeBitChunksDense.RemoveAtSwapBack(lastChunkIndex); + m_FreeChunkIndicesSparse[lastDenseChunk] = lastChunkIndex; + m_FreeChunkIndicesSparse.Resize(m_FreeChunkIndicesSparse.Length - 1); + } + else + { + m_FreeBitChunksDense[lastChunkIndex] = lastBitsChunk; + } + + length -= freeLeadBits; + freeCount -= freeLeadBits; + Assert.IsTrue(length >= 0 && freeCount >= 0, "Length or freeCount went negative."); + + if (freeLeadBits < usedBitsInChunk) + return; + + if (usedBitsInChunk < BitChunkSize) + { + usedBitsInChunk = BitChunkSize; + nonUsedBitsInChunk = 0; + usedBitsMask = 0xFFFFFFFF; + } + + lastChunk -= 1; + } + } + + public unsafe void TrimLength() + { + fixed (NativeHandleAllocator* thisPtr = &this) + { + NativeHandleAllocatorBurst.TrimLength(thisPtr); + } + } + + struct FreeBitsChunk + { + public readonly int chunk; + public uint freeBits; + + public FreeBitsChunk(int chunk, uint freeBits) + { + this.chunk = chunk; + this.freeBits = freeBits; + } + } + } + + [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] + internal static unsafe class NativeHandleAllocatorBurst + { + public static void TrimLength(NativeHandleAllocator* allocator) => allocator->TrimLengthImpl(); + } +} diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Utilities/NativeHandleAllocator.cs.meta b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Utilities/NativeHandleAllocator.cs.meta new file mode 100644 index 00000000000..3c1c62a6aca --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Utilities/NativeHandleAllocator.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: f6607226450929441ace28f2ff712695 \ No newline at end of file diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Utilities/ParallelBitArray.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Utilities/ParallelBitArray.cs index 6bad58b38db..6ad06059f49 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Utilities/ParallelBitArray.cs +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Utilities/ParallelBitArray.cs @@ -1,13 +1,13 @@ using System; -using System.Diagnostics; using System.Threading; using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; using Unity.Jobs; +using UnityEngine.Assertions; namespace UnityEngine.Rendering { - internal struct ParallelBitArray + internal struct ParallelBitArray : IDisposable { private Allocator m_Allocator; private NativeArray m_Bits; @@ -77,7 +77,7 @@ public void Set(int index, bool value) { unsafe { - Debug.Assert(0 <= index && index < m_Length); + Assert.IsTrue(0 <= index && index < m_Length); int entry_index = index >> 6; long* entries = (long*)m_Bits.GetUnsafePtr(); @@ -99,7 +99,7 @@ public bool Get(int index) { unsafe { - Debug.Assert(0 <= index && index < m_Length); + Assert.IsTrue(0 <= index && index < m_Length); int entry_index = index >> 6; long* entries = (long*)m_Bits.GetUnsafeReadOnlyPtr(); diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Utilities/ParallelSortExtensions.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Utilities/ParallelSortExtensions.cs index 1df7b2dd1b4..a5a56c74b67 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Utilities/ParallelSortExtensions.cs +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Utilities/ParallelSortExtensions.cs @@ -1,3 +1,5 @@ +using System; +using System.Runtime.InteropServices; using System.Threading; using UnityEngine.Assertions; using Unity.Mathematics; @@ -6,17 +8,56 @@ using Unity.Jobs.LowLevel.Unsafe; using Unity.Collections.LowLevel.Unsafe; using Unity.Burst; -using UnityEngine.Profiling; + +[assembly: RegisterGenericJobType(typeof(UnityEngine.Rendering.ParallelSortExtensions.RadixSortBucketCountJob))] +[assembly: RegisterGenericJobType(typeof(UnityEngine.Rendering.ParallelSortExtensions.RadixSortBucketCountJob))] + +[assembly: RegisterGenericJobType(typeof(UnityEngine.Rendering.ParallelSortExtensions.RadixSortBatchPrefixSumJob))] +[assembly: RegisterGenericJobType(typeof(UnityEngine.Rendering.ParallelSortExtensions.RadixSortBatchPrefixSumJob))] + +[assembly: RegisterGenericJobType(typeof(UnityEngine.Rendering.ParallelSortExtensions.RadixSortBucketSortJob))] +[assembly: RegisterGenericJobType(typeof(UnityEngine.Rendering.ParallelSortExtensions.RadixSortBucketSortJob))] + +[assembly: RegisterGenericJobType(typeof(SortJob>))] +[assembly: RegisterGenericJobType(typeof(SortJob>))] namespace UnityEngine.Rendering { internal static class ParallelSortExtensions { - const int kMinRadixSortArraySize = 2048; + // This constant is used in the ParallelSort unit test to make sure we're hitting the parallel code path. + internal const int kMinRadixSortArraySize = 2048; const int kMinRadixSortBatchSize = 256; - internal static JobHandle ParallelSort(this NativeArray array) + internal enum ParallelSortValueType + { + Int, + ULong + } + + private static int GetBucketIndex(int value, int radix) + { + return (value >> radix * 8) & 0xFF; + } + + private static int GetBucketIndex(ulong value, int radix) { + return (int)((value >> radix * 8) & 0xFF); + } + + private static void Swap(ref NativeArray a, ref NativeArray b) where T : unmanaged + { + NativeArray temp = a; + a = b; + b = temp; + } + + // The method supports for the moment only keys of type int or ulong. + internal static JobHandle ParallelSort(this NativeArray array) where T : unmanaged, IComparable + { + // Only these two integer types are supported + Assert.IsTrue(typeof(T) == typeof(ulong) || typeof(T) == typeof(int)); + if (array.Length <= 1) return new JobHandle(); @@ -27,10 +68,12 @@ internal static JobHandle ParallelSort(this NativeArray array) int workersCount = Mathf.Max(JobsUtility.JobWorkerCount + 1, 1); int batchSize = Mathf.Max(kMinRadixSortBatchSize, Mathf.CeilToInt((float)array.Length / workersCount)); int jobsCount = Mathf.CeilToInt((float)array.Length / batchSize); + int keyByteSize = Marshal.SizeOf(); + int signBitRadixIndex = keyByteSize - 1; Assert.IsTrue(jobsCount * batchSize >= array.Length); - var supportArray = new NativeArray(array.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); + var supportArray = new NativeArray(array.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); var counter = new NativeArray(1, Allocator.TempJob); var buckets = new NativeArray(jobsCount * 256, Allocator.TempJob); var indices = new NativeArray(jobsCount * 256, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); @@ -39,18 +82,27 @@ internal static JobHandle ParallelSort(this NativeArray array) var arraySource = array; var arrayDest = supportArray; - for (int radix = 0; radix < 4; ++radix) + ParallelSortValueType valueType = typeof(T) == typeof(int) ? ParallelSortValueType.Int : ParallelSortValueType.ULong; + + // Add any unsigned value type to this condition + if (valueType == ParallelSortValueType.ULong) { - var bucketCountJobData = new RadixSortBucketCountJob + // There are no sign bits in this type. + signBitRadixIndex = -1; + } + + for (int radix = 0; radix < keyByteSize; ++radix) + { + var bucketCountJobData = new RadixSortBucketCountJob { radix = radix, - jobsCount = jobsCount, batchSize = batchSize, buckets = buckets, - array = arraySource + array = arraySource, + valueType = valueType }; - var batchPrefixSumJobData = new RadixSortBatchPrefixSumJob + var batchPrefixSumJobData = new RadixSortBatchPrefixSumJob { radix = radix, jobsCount = jobsCount, @@ -58,7 +110,8 @@ internal static JobHandle ParallelSort(this NativeArray array) counter = counter, buckets = buckets, indices = indices, - indicesSum = indicesSum + indicesSum = indicesSum, + signBitRadixIndex = signBitRadixIndex }; var prefixSumJobData = new RadixSortPrefixSumJob @@ -68,13 +121,14 @@ internal static JobHandle ParallelSort(this NativeArray array) indicesSum = indicesSum }; - var bucketSortJobData = new RadixSortBucketSortJob + var bucketSortJobData = new RadixSortBucketSortJob { radix = radix, batchSize = batchSize, indices = indices, array = arraySource, - arraySorted = arrayDest + arraySorted = arrayDest, + valueType = valueType }; jobHandle = bucketCountJobData.ScheduleParallel(jobsCount, 1, jobHandle); @@ -84,13 +138,6 @@ internal static JobHandle ParallelSort(this NativeArray array) JobHandle.ScheduleBatchedJobs(); - static void Swap(ref NativeArray a, ref NativeArray b) - { - NativeArray temp = a; - a = b; - b = temp; - } - Swap(ref arraySource, ref arrayDest); } @@ -109,12 +156,12 @@ static void Swap(ref NativeArray a, ref NativeArray b) } [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] - internal struct RadixSortBucketCountJob : IJobFor + internal struct RadixSortBucketCountJob : IJobFor where T : unmanaged { [ReadOnly] public int radix; - [ReadOnly] public int jobsCount; [ReadOnly] public int batchSize; - [ReadOnly] [NativeDisableContainerSafetyRestriction, NoAlias] public NativeArray array; + [ReadOnly] public ParallelSortValueType valueType; + [ReadOnly] [NativeDisableContainerSafetyRestriction, NoAlias] public NativeArray array; [NativeDisableContainerSafetyRestriction, NoAlias] public NativeArray buckets; @@ -125,21 +172,39 @@ public void Execute(int index) int jobBuckets = index * 256; - for (int i = start; i < end; ++i) + // This hacky system instead of relying solely on C# generics is because our current version of C# doesn't support IBinaryWriter + // which would let us restrain the generic type to types that accept the binary shift and "and" operations + // used in GetBucketIndex. + if (valueType == ParallelSortValueType.Int) { - int value = array[i]; - int bucket = (value >> radix * 8) & 0xFF; - buckets[jobBuckets + bucket] += 1; + NativeArray intArray = array.Reinterpret(4); + for (int i = start; i < end; ++i) + { + int value = intArray[i]; + int bucket = GetBucketIndex(value, radix); + buckets[jobBuckets + bucket] += 1; + } + } + else + { + NativeArray ulongArray = array.Reinterpret(8); + for (int i = start; i < end; ++i) + { + ulong value = ulongArray[i]; + int bucket = GetBucketIndex(value, radix); + buckets[jobBuckets + bucket] += 1; + } } } } [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] - internal struct RadixSortBatchPrefixSumJob : IJobFor + internal struct RadixSortBatchPrefixSumJob : IJobFor where T : unmanaged { [ReadOnly] public int radix; [ReadOnly] public int jobsCount; - [ReadOnly] [NativeDisableContainerSafetyRestriction, NoAlias] public NativeArray array; + [ReadOnly] public int signBitRadixIndex; + [ReadOnly] [NativeDisableContainerSafetyRestriction, NoAlias] public NativeArray array; [NativeDisableContainerSafetyRestriction, NoAlias] public NativeArray counter; [NativeDisableContainerSafetyRestriction, NoAlias] public NativeArray indicesSum; @@ -181,7 +246,7 @@ public void Execute(int index) { int sum = 0; - if(radix < 3) + if (radix != signBitRadixIndex) { for (int i = 0; i < 16; ++i) { @@ -190,7 +255,8 @@ public void Execute(int index) sum += indexSum; } } - else // Negative + // The radix contains the sign bit so might be negative + else { for (int i = 8; i < 16; ++i) { @@ -240,14 +306,15 @@ public void Execute(int index) } [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] - internal struct RadixSortBucketSortJob : IJobFor + internal struct RadixSortBucketSortJob : IJobFor where T : unmanaged { [ReadOnly] public int radix; [ReadOnly] public int batchSize; - [ReadOnly] [NativeDisableContainerSafetyRestriction, NoAlias] public NativeArray array; + [ReadOnly] public ParallelSortValueType valueType; + [ReadOnly] [NativeDisableContainerSafetyRestriction, NoAlias] public NativeArray array; [NativeDisableContainerSafetyRestriction, NoAlias] public NativeArray indices; - [NativeDisableContainerSafetyRestriction, NoAlias] public NativeArray arraySorted; + [NativeDisableContainerSafetyRestriction, NoAlias] public NativeArray arraySorted; public void Execute(int index) { @@ -256,12 +323,32 @@ public void Execute(int index) int jobIndices = index * 256; - for (int i = start; i < end; ++i) + // This hacky system instead of relying solely on C# generics is because our current version of C# doesn't support IBinaryWriter + // which would let us restrain the generic type to types that accept the binary shift and "and" operations + // used in GetBucketIndex. + if (valueType == ParallelSortValueType.Int) { - int value = array[i]; - int bucket = (value >> radix * 8) & 0xFF; - int sortedIndex = indices[jobIndices + bucket]++; - arraySorted[sortedIndex] = value; + NativeArray inArray = array.Reinterpret(4); + NativeArray outArray = arraySorted.Reinterpret(4); + for (int i = start; i < end; ++i) + { + int value = inArray[i]; + int bucket = GetBucketIndex(value, radix); + int sortedIndex = indices[jobIndices + bucket]++; + outArray[sortedIndex] = value; + } + } + else + { + NativeArray inArray = array.Reinterpret(8); + NativeArray outArray = arraySorted.Reinterpret(8); + for (int i = start; i < end; ++i) + { + ulong value = inArray[i]; + int bucket = GetBucketIndex(value, radix); + int sortedIndex = indices[jobIndices + bucket]++; + outArray[sortedIndex] = value; + } } } } diff --git a/Packages/com.unity.render-pipelines.core/Runtime/Lighting/ProbeVolume/ProbeReferenceVolume.ReflProbeNormalization.cs b/Packages/com.unity.render-pipelines.core/Runtime/Lighting/ProbeVolume/ProbeReferenceVolume.ReflProbeNormalization.cs index 7552b23e8da..1eb3df280c2 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/Lighting/ProbeVolume/ProbeReferenceVolume.ReflProbeNormalization.cs +++ b/Packages/com.unity.render-pipelines.core/Runtime/Lighting/ProbeVolume/ProbeReferenceVolume.ReflProbeNormalization.cs @@ -123,7 +123,7 @@ static internal bool GetPositionForRequest(EntityId probeInstanceID, out Vector3 /// Update the capture location for the probe request. /// /// The instance ID of the probe doing the request and that wants the capture position updated. - /// The position at which a probe is baked. + /// The position at which a probe is baked. public void UpdatePositionForRequest(EntityId probeInstanceID, Vector3 newPosition) { if (m_SHCoefficients.ContainsKey(probeInstanceID)) @@ -160,7 +160,7 @@ static bool IsZero(in SphericalHarmonicsL2 s) return true; } - static void SetSHCoefficients(NativeArray sh, NativeArray validity) + static internal void SetSHCoefficients(NativeArray sh, NativeArray validity) { Debug.Assert(sh.Length == m_SHCoefficients.Count); Debug.Assert(sh.Length == validity.Length); diff --git a/Packages/com.unity.render-pipelines.core/Runtime/Lighting/SurfaceCache/Defrag.compute b/Packages/com.unity.render-pipelines.core/Runtime/Lighting/SurfaceCache/Defrag.compute index f1d25501212..7da105bccc0 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/Lighting/SurfaceCache/Defrag.compute +++ b/Packages/com.unity.render-pipelines.core/Runtime/Lighting/SurfaceCache/Defrag.compute @@ -33,7 +33,6 @@ RWStructuredBuffer _RingConfigBuffer; RWStructuredBuffer _CellPatchIndices; RWStructuredBuffer _PatchCellIndices; -RWStructuredBuffer _PatchCounterSets; RWStructuredBuffer _PatchIrradiances0; RWStructuredBuffer _PatchIrradiances1; RWStructuredBuffer _PatchGeometries; @@ -80,7 +79,6 @@ void Defrag(Threading::Group group) const uint newPatchIdx = (patchIdx + rightReclaimableCount) % patchCapacity; _PatchCellIndices[newPatchIdx] = oldPatchCellIndex; - _PatchCounterSets[newPatchIdx] = _PatchCounterSets[patchIdx]; _PatchIrradiances0[newPatchIdx] = _PatchIrradiances0[patchIdx]; _PatchIrradiances1[newPatchIdx] = _PatchIrradiances1[patchIdx]; _PatchGeometries[newPatchIdx] = _PatchGeometries[patchIdx]; diff --git a/Packages/com.unity.render-pipelines.core/Runtime/Lighting/SurfaceCache/Estimation.hlsl b/Packages/com.unity.render-pipelines.core/Runtime/Lighting/SurfaceCache/Estimation.hlsl index c2464297292..f9e4343307d 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/Lighting/SurfaceCache/Estimation.hlsl +++ b/Packages/com.unity.render-pipelines.core/Runtime/Lighting/SurfaceCache/Estimation.hlsl @@ -2,32 +2,34 @@ #include "PatchUtil.hlsl" #include "TemporalFiltering.hlsl" -void ProcessAndStoreRadianceSample(RWStructuredBuffer patchIrradiances, RWStructuredBuffer patchStatistics, RWStructuredBuffer patchCounterSets, uint patchIdx, SphericalHarmonics::RGBL1 radianceSample, float shortHysteresis) +void ProcessAndStoreRadianceSample(RWStructuredBuffer patchIrradiances, RWStructuredBuffer patchStatistics, uint patchIdx, SphericalHarmonics::RGBL1 radianceSample, float shortHysteresis) { SphericalHarmonics::CosineConvolve(radianceSample); PatchUtil::PatchStatisticsSet oldStats = patchStatistics[patchIdx]; + const uint oldUpdateCount = PatchUtil::GetUpdateCount(oldStats.patchCounters); + const uint newUpdateCount = min(oldUpdateCount + 1, PatchUtil::updateMax); const SphericalHarmonics::RGBL1 oldIrradiance = patchIrradiances[patchIdx]; - const float3 newL0ShortIrradiance = lerp(radianceSample.l0, oldStats.mean, shortHysteresis); - const float3 varianceSample = (radianceSample.l0 - newL0ShortIrradiance) * (radianceSample.l0 - oldStats.mean); - const float3 newVariance = lerp(varianceSample, oldStats.variance, shortHysteresis); - const PatchUtil::PatchCounterSet oldCounterSet = patchCounterSets[patchIdx]; - const uint oldUpdateCount = PatchUtil::GetUpdateCount(patchCounterSets[patchIdx]); - const uint newUpdateCount = min(oldUpdateCount + 1, PatchUtil::updateMax); + float shortIrradianceUpdateWeight; + if (oldUpdateCount == 0) + shortIrradianceUpdateWeight = 0; + else + shortIrradianceUpdateWeight = min(1.0f - rcp(oldUpdateCount), shortHysteresis); - PatchUtil::PatchCounterSet newCounterSet = oldCounterSet; - PatchUtil::SetUpdateCount(newCounterSet, newUpdateCount); + const float3 newL0ShortIrradiance = lerp(radianceSample.l0, oldStats.mean, shortIrradianceUpdateWeight); + const float3 varianceSample = (radianceSample.l0 - newL0ShortIrradiance) * (radianceSample.l0 - oldStats.mean); + const float3 newVariance = lerp(varianceSample, oldStats.variance, shortHysteresis); SphericalHarmonics::RGBL1 output = FilterTemporallyVarianceGuided(shortHysteresis, newUpdateCount, newVariance, newL0ShortIrradiance, radianceSample, oldIrradiance); patchIrradiances[patchIdx] = output; - if (!PatchUtil::IsEqual(oldCounterSet, newCounterSet)) - patchCounterSets[patchIdx] = newCounterSet; PatchUtil::PatchStatisticsSet newStats; newStats.mean = newL0ShortIrradiance; newStats.variance = newVariance; + newStats.patchCounters = oldStats.patchCounters; + PatchUtil::SetUpdateCount(newStats.patchCounters, newUpdateCount); patchStatistics[patchIdx] = newStats; } diff --git a/Packages/com.unity.render-pipelines.core/Runtime/Lighting/SurfaceCache/Eviction.compute b/Packages/com.unity.render-pipelines.core/Runtime/Lighting/SurfaceCache/Eviction.compute index c54944f89bb..3824a6b98dc 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/Lighting/SurfaceCache/Eviction.compute +++ b/Packages/com.unity.render-pipelines.core/Runtime/Lighting/SurfaceCache/Eviction.compute @@ -5,7 +5,7 @@ #include "PatchUtil.hlsl" StructuredBuffer _RingConfigBuffer; -StructuredBuffer _PatchCounterSets; +StructuredBuffer _PatchStatistics; RWStructuredBuffer _CellAllocationMarks; RWStructuredBuffer _CellPatchIndices; RWStructuredBuffer _PatchCellIndices; @@ -29,7 +29,7 @@ void Evict(uint patchIdx : SV_DispatchThreadID) if (cellIdx == PatchUtil::invalidCellIndex) return; - const PatchUtil::PatchCounterSet counterSet = _PatchCounterSets[patchIdx]; + const PatchUtil::PatchCounterSet counterSet = _PatchStatistics[patchIdx].patchCounters; const uint lastAccessFrameIdx = PatchUtil::GetLastAccessFrame(counterSet); // Here we take into account that last frame access index is in [0, 2^16-1]. diff --git a/Packages/com.unity.render-pipelines.core/Runtime/Lighting/SurfaceCache/PatchUtil.hlsl b/Packages/com.unity.render-pipelines.core/Runtime/Lighting/SurfaceCache/PatchUtil.hlsl index 5246c4163a1..da06c04ae78 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/Lighting/SurfaceCache/PatchUtil.hlsl +++ b/Packages/com.unity.render-pipelines.core/Runtime/Lighting/SurfaceCache/PatchUtil.hlsl @@ -38,15 +38,16 @@ namespace PatchUtil float3 normal; }; - struct PatchStatisticsSet + struct PatchCounterSet { - float3 mean; - float3 variance; + uint data; }; - struct PatchCounterSet + struct PatchStatisticsSet { - uint data; + float3 mean; + float3 variance; + PatchCounterSet patchCounters; }; void Reset(inout PatchCounterSet set) @@ -79,11 +80,11 @@ namespace PatchUtil return a.data == b.data; } - void WriteLastFrameAccess(RWStructuredBuffer counterSets, uint patchIdx, uint frameIdx) + void WriteLastFrameAccess(RWStructuredBuffer statisticsSets, uint patchIdx, uint frameIdx) { - PatchCounterSet counterSet = counterSets[patchIdx]; + PatchCounterSet counterSet = statisticsSets[patchIdx].patchCounters; SetLastAccessFrame(counterSet, frameIdx); - counterSets[patchIdx] = counterSet; + statisticsSets[patchIdx].patchCounters = counterSet; } float GetVoxelSize(float voxelMinSize, uint cascadeIdx) @@ -306,12 +307,12 @@ namespace PatchUtil } } - uint FindPatchIndexAndUpdateLastAccess(float3 volumeTargetPos, StructuredBuffer cellPatchIndices, uint spatialResolution, StructuredBuffer cascadeOffsets, RWStructuredBuffer patchCounterSets, uint cascadeCount, float voxelMinSize, float3 worldPosition, float3 worldNormal, uint frameIdx) + uint FindPatchIndexAndUpdateLastAccess(float3 volumeTargetPos, StructuredBuffer cellPatchIndices, uint spatialResolution, StructuredBuffer cascadeOffsets, RWStructuredBuffer patchStatisticSets, uint cascadeCount, float voxelMinSize, float3 worldPosition, float3 worldNormal, uint frameIdx) { const uint patchIdx = FindPatchIndex(volumeTargetPos, cellPatchIndices, spatialResolution, cascadeOffsets, cascadeCount, voxelMinSize,worldPosition, worldNormal); if (patchIdx != invalidPatchIndex) { - WriteLastFrameAccess(patchCounterSets, patchIdx, frameIdx); + WriteLastFrameAccess(patchStatisticSets, patchIdx, frameIdx); } return patchIdx; } diff --git a/Packages/com.unity.render-pipelines.core/Runtime/Lighting/SurfaceCache/PathTracing.hlsl b/Packages/com.unity.render-pipelines.core/Runtime/Lighting/SurfaceCache/PathTracing.hlsl index f84b3d39692..b8da6e6796a 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/Lighting/SurfaceCache/PathTracing.hlsl +++ b/Packages/com.unity.render-pipelines.core/Runtime/Lighting/SurfaceCache/PathTracing.hlsl @@ -7,7 +7,7 @@ #include "Packages/com.unity.render-pipelines.core/Runtime/PathTracing/MaterialPool/MaterialPool.hlsl" #include "Common.hlsl" #include "PatchUtil.hlsl" -#include "PunctualLightSample.hlsl" +#include "PunctualLights.hlsl" struct SurfaceGeometry { @@ -76,7 +76,6 @@ PunctualLightBounceRadianceSample SamplePunctualLightBounceRadiance( StructuredBuffer punctualLightSamples, uint punctualLightSampleCount, float uniformRand, - float3 spotLightIntensity, float3 position, float3 normal) { @@ -111,20 +110,27 @@ PunctualLightBounceRadianceSample SamplePunctualLightBounceRadiance( const float bounceCosTerm = dot(-punctualLightSample.dir, punctualLightSample.hitNormal); const float bounceSolidAngleToAreaJacobian = 1.0f / (punctualLightSample.distance * punctualLightSample.distance); // To integrate over punctual light we must switch to area measure. const float3 brdf = punctualLightSample.hitAlbedo * INV_PI; - const float3 punctualLightBouncedRadiance = bounceCosTerm * bounceSolidAngleToAreaJacobian * spotLightIntensity * brdf; + const float3 punctualLightBouncedRadiance = bounceCosTerm * bounceSolidAngleToAreaJacobian * punctualLightSample.intensity * brdf; // We transform from patch solid angle measure to (common) surface area measure to punctual light solid angle measure. const float patchSolidAngleToBounceAreaJacobian = dot(-reconnectionRay.direction, punctualLightSample.hitNormal) / (reconnectionResult.hitDistance * reconnectionResult.hitDistance); const float bounceAreaToLightSolidAngleJacobian = punctualLightSample.distance * punctualLightSample.distance / dot(-punctualLightSample.dir, punctualLightSample.hitNormal); - const float patchSolidAngleToLightSolidAngleJacbian = patchSolidAngleToBounceAreaJacobian * bounceAreaToLightSolidAngleJacobian; + const float patchSolidAngleToLightSolidAngleJacobian = patchSolidAngleToBounceAreaJacobian * bounceAreaToLightSolidAngleJacobian; - result.radianceOverDensity = punctualLightBouncedRadiance * patchSolidAngleToLightSolidAngleJacbian * punctualLightSample.reciprocalDensity; + if (isfinite(bounceSolidAngleToAreaJacobian) && isfinite(patchSolidAngleToBounceAreaJacobian)) + result.radianceOverDensity = punctualLightBouncedRadiance * patchSolidAngleToLightSolidAngleJacobian * punctualLightSample.reciprocalDensity; + else + result.MarkInvalid(); #else // optimized version - result.radianceOverDensity = - INV_PI * dot(-reconnectionRay.direction, punctualLightSample.hitNormal) * - punctualLightSample.reciprocalDensity * - rcp(reconnectionResult.hitDistance * reconnectionResult.hitDistance) * - spotLightIntensity * punctualLightSample.hitAlbedo; + const float reciprocalSquaredDistance = rcp(reconnectionResult.hitDistance * reconnectionResult.hitDistance); + if (isfinite(reciprocalSquaredDistance)) + result.radianceOverDensity = + INV_PI * dot(-reconnectionRay.direction, punctualLightSample.hitNormal) * + punctualLightSample.reciprocalDensity * + reciprocalSquaredDistance * + punctualLightSample.intensity * punctualLightSample.hitAlbedo; + else + result.MarkInvalid(); #endif } } diff --git a/Packages/com.unity.render-pipelines.core/Runtime/Lighting/SurfaceCache/PunctualLightSampling.hlsl b/Packages/com.unity.render-pipelines.core/Runtime/Lighting/SurfaceCache/PunctualLightSampling.hlsl index 70698aea9d9..dd043f96206 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/Lighting/SurfaceCache/PunctualLightSampling.hlsl +++ b/Packages/com.unity.render-pipelines.core/Runtime/Lighting/SurfaceCache/PunctualLightSampling.hlsl @@ -7,10 +7,11 @@ #include "Packages/com.unity.render-pipelines.core/Runtime/UnifiedRayTracing/FetchGeometry.hlsl" #include "Packages/com.unity.render-pipelines.core/Runtime/Sampling/QuasiRandom.hlsl" #include "PathTracing.hlsl" -#include "PunctualLightSample.hlsl" +#include "PunctualLights.hlsl" UNIFIED_RT_DECLARE_ACCEL_STRUCT(_RayTracingAccelerationStructure); +StructuredBuffer _PunctualLights; RWStructuredBuffer _Samples; StructuredBuffer _MaterialEntries; @@ -23,10 +24,7 @@ SamplerState sampler_TransmissionTextures; float _MaterialAtlasTexelSize; float _AlbedoBoost; uint _FrameIdx; - -float3 _SpotLightPosition; -float3 _SpotLightDirection; -float _SpotLightCosAngle; +uint _PunctualLightCount; void SamplePunctualLights(UnifiedRT::DispatchInfo dispatchInfo) { @@ -46,18 +44,21 @@ void SamplePunctualLights(UnifiedRT::DispatchInfo dispatchInfo) QrngKronecker rng; rng.Init(dispatchInfo.globalThreadIndex.x, _FrameIdx); + const uint spotLightIndex = min(rng.GetFloat(0) * _PunctualLightCount, _PunctualLightCount - 1); + const PunctualLight light = _PunctualLights[spotLightIndex]; + UnifiedRT::Ray ray; ray.tMin = 0; ray.tMax = FLT_MAX; - ray.origin = _SpotLightPosition; + ray.origin = light.position; { - float3 localDir = SampleConeUniform(rng.GetFloat(0), rng.GetFloat(1), _SpotLightCosAngle); - float3x3 spotBasis = OrthoBasisFromVector(_SpotLightDirection); + float3 localDir = SampleConeUniform(rng.GetFloat(1), rng.GetFloat(1), light.cosAngle); + float3x3 spotBasis = OrthoBasisFromVector(light.direction); ray.direction = mul(spotBasis, localDir); } - PunctualLightSample sample = (PunctualLightSample)0; - sample.dir = ray.direction; + PunctualLightSample lightSample = (PunctualLightSample)0; + lightSample.dir = ray.direction; UnifiedRT::Hit hitResult = UnifiedRT::TraceRayClosestHit(dispatchInfo, accelStruct, 0xFFFFFFFF, ray, UnifiedRT::kRayFlagNone); if (hitResult.IsValid() && hitResult.isFrontFace) @@ -78,18 +79,19 @@ void SamplePunctualLights(UnifiedRT::DispatchInfo dispatchInfo) hitGeo.uv0, hitGeo.uv1); - sample.hitPos = ray.origin + ray.direction * hitResult.hitDistance; - sample.hitNormal = hitGeo.normal; - sample.distance = hitResult.hitDistance; - sample.hitAlbedo = hitMat.baseColor; - sample.reciprocalDensity = AreaOfSphericalCapWithRadiusOne(_SpotLightCosAngle); - sample.hitInstanceId = hitResult.instanceID; - sample.hitPrimitiveIndex = hitResult.primitiveIndex; + lightSample.hitPos = ray.origin + ray.direction * hitResult.hitDistance; + lightSample.hitNormal = hitGeo.normal; + lightSample.distance = hitResult.hitDistance; + lightSample.hitAlbedo = hitMat.baseColor; + lightSample.reciprocalDensity = AreaOfSphericalCapWithRadiusOne(light.cosAngle) * _PunctualLightCount; + lightSample.hitInstanceId = hitResult.instanceID; + lightSample.hitPrimitiveIndex = hitResult.primitiveIndex; + lightSample.intensity = light.intensity; } else { - sample.MarkNoHit(); + lightSample.MarkNoHit(); } - _Samples[dispatchInfo.globalThreadIndex.x] = sample; + _Samples[dispatchInfo.globalThreadIndex.x] = lightSample; } diff --git a/Packages/com.unity.render-pipelines.core/Runtime/Lighting/SurfaceCache/PunctualLightSample.hlsl b/Packages/com.unity.render-pipelines.core/Runtime/Lighting/SurfaceCache/PunctualLights.hlsl similarity index 78% rename from Packages/com.unity.render-pipelines.core/Runtime/Lighting/SurfaceCache/PunctualLightSample.hlsl rename to Packages/com.unity.render-pipelines.core/Runtime/Lighting/SurfaceCache/PunctualLights.hlsl index 15ea995a65b..28f07e6fb19 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/Lighting/SurfaceCache/PunctualLightSample.hlsl +++ b/Packages/com.unity.render-pipelines.core/Runtime/Lighting/SurfaceCache/PunctualLights.hlsl @@ -1,6 +1,14 @@ #ifndef SURFACE_CACHE_PUNCTUAL_LIGHT_SAMPLE #define SURFACE_CACHE_PUNCTUAL_LIGHT_SAMPLE +struct PunctualLight +{ + float3 position; + float3 direction; + float3 intensity; + float cosAngle; +}; + // This represents a sample ray shot from the light. struct PunctualLightSample { @@ -8,6 +16,7 @@ struct PunctualLightSample float3 hitNormal; float3 hitAlbedo; float3 dir; + float3 intensity; float distance; uint hitInstanceId; uint hitPrimitiveIndex; diff --git a/Packages/com.unity.render-pipelines.core/Runtime/Lighting/SurfaceCache/PunctualLightSample.hlsl.meta b/Packages/com.unity.render-pipelines.core/Runtime/Lighting/SurfaceCache/PunctualLights.hlsl.meta similarity index 100% rename from Packages/com.unity.render-pipelines.core/Runtime/Lighting/SurfaceCache/PunctualLightSample.hlsl.meta rename to Packages/com.unity.render-pipelines.core/Runtime/Lighting/SurfaceCache/PunctualLights.hlsl.meta diff --git a/Packages/com.unity.render-pipelines.core/Runtime/Lighting/SurfaceCache/RestirEstimation.compute b/Packages/com.unity.render-pipelines.core/Runtime/Lighting/SurfaceCache/RestirEstimation.compute index 3089d32bfde..85700741ed0 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/Lighting/SurfaceCache/RestirEstimation.compute +++ b/Packages/com.unity.render-pipelines.core/Runtime/Lighting/SurfaceCache/RestirEstimation.compute @@ -12,7 +12,6 @@ StructuredBuffer _PatchRealizations; StructuredBuffer _PatchGeometries; RWStructuredBuffer _PatchIrradiances; RWStructuredBuffer _PatchStatistics; -RWStructuredBuffer _PatchCounterSets; uint _RingConfigOffset; float _ShortHysteresis; @@ -44,6 +43,6 @@ void Estimate(uint patchIdx : SV_DispatchThreadID) estimate.l1s[1] = realization.sample.radiance * SphericalHarmonics::y1Constant * rayDirection.z; estimate.l1s[2] = realization.sample.radiance * SphericalHarmonics::y1Constant * rayDirection.x; SphericalHarmonics::MulMut(estimate, realization.weight); - ProcessAndStoreRadianceSample(_PatchIrradiances, _PatchStatistics, _PatchCounterSets, patchIdx, estimate, _ShortHysteresis); + ProcessAndStoreRadianceSample(_PatchIrradiances, _PatchStatistics, patchIdx, estimate, _ShortHysteresis); } } diff --git a/Packages/com.unity.render-pipelines.core/Runtime/Lighting/SurfaceCache/RisEstimation.hlsl b/Packages/com.unity.render-pipelines.core/Runtime/Lighting/SurfaceCache/RisEstimation.hlsl index 93c52068961..b892df2507e 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/Lighting/SurfaceCache/RisEstimation.hlsl +++ b/Packages/com.unity.render-pipelines.core/Runtime/Lighting/SurfaceCache/RisEstimation.hlsl @@ -35,7 +35,6 @@ StructuredBuffer _RingConfigBuffer; RWStructuredBuffer _PatchIrradiances; RWStructuredBuffer _PatchStatistics; StructuredBuffer _PatchGeometries; -RWStructuredBuffer _PatchCounterSets; StructuredBuffer _CellPatchIndices; StructuredBuffer _CascadeOffsets; RWStructuredBuffer _PatchAccumulatedLuminances; @@ -185,6 +184,6 @@ void Estimate(UnifiedRT::DispatchInfo dispatchInfo) ProcessAndStoreLuminanceSample(_PatchAccumulatedLuminances, patchIdx, luminanceEstimate, _TargetFunctionUpdateWeight); const SphericalHarmonics::RGBL1 estimate = EstimateFromSampleAndWeight(radianceSample, reservoir.sample.direction, outputWeight); - ProcessAndStoreRadianceSample(_PatchIrradiances, _PatchStatistics, _PatchCounterSets, patchIdx, estimate, _ShortHysteresis); + ProcessAndStoreRadianceSample(_PatchIrradiances, _PatchStatistics, patchIdx, estimate, _ShortHysteresis); } } diff --git a/Packages/com.unity.render-pipelines.core/Runtime/Lighting/SurfaceCache/SurfaceCache.cs b/Packages/com.unity.render-pipelines.core/Runtime/Lighting/SurfaceCache/SurfaceCache.cs index 6cfd916c090..7a5c71c6d58 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/Lighting/SurfaceCache/SurfaceCache.cs +++ b/Packages/com.unity.render-pipelines.core/Runtime/Lighting/SurfaceCache/SurfaceCache.cs @@ -56,7 +56,6 @@ internal class SurfaceCachePatchList : IDisposable private GraphicsBuffer _geometries; private GraphicsBuffer _cellIndices; - private GraphicsBuffer _counterSets; private GraphicsBuffer[] _irradiances; private GraphicsBuffer _statistics; private GraphicsBuffer[] _restirRealizations; @@ -65,7 +64,6 @@ internal class SurfaceCachePatchList : IDisposable public uint Capacity => _capacity; public GraphicsBuffer Geometries => _geometries; public GraphicsBuffer CellIndices => _cellIndices; - public GraphicsBuffer CounterSets => _counterSets; public GraphicsBuffer[] Irradiances => _irradiances; public GraphicsBuffer Statistics => _statistics; public GraphicsBuffer[] RestirRealizations => _restirRealizations; @@ -79,13 +77,12 @@ public SurfaceCachePatchList(uint capacity, SurfaceCacheEstimationMethod estimat _geometries = new GraphicsBuffer(GraphicsBuffer.Target.Structured, capacityInt, sizeof(float) * 3 * 2); _cellIndices = new GraphicsBuffer(GraphicsBuffer.Target.Structured, capacityInt, sizeof(uint)); - _counterSets = new GraphicsBuffer(GraphicsBuffer.Target.Structured, capacityInt, sizeof(uint)); _irradiances = new GraphicsBuffer[3]; _irradiances[0] = new GraphicsBuffer(GraphicsBuffer.Target.Structured, capacityInt, irradianceStride); _irradiances[1] = new GraphicsBuffer(GraphicsBuffer.Target.Structured, capacityInt, irradianceStride); _irradiances[2] = new GraphicsBuffer(GraphicsBuffer.Target.Structured, capacityInt, irradianceStride); - _statistics = new GraphicsBuffer(GraphicsBuffer.Target.Structured, capacityInt, sizeof(float) * 3 * 2); + _statistics = new GraphicsBuffer(GraphicsBuffer.Target.Structured, capacityInt, sizeof(float) * 3 * 2 + sizeof(uint)); if (estimationMethod == SurfaceCacheEstimationMethod.Restir) { @@ -108,7 +105,6 @@ public void Dispose() { b.Dispose(); } - _counterSets.Dispose(); _geometries.Dispose(); _cellIndices.Dispose(); _statistics.Dispose(); @@ -362,7 +358,7 @@ private class EvictionPassData internal int KernelIndex; internal uint3 ThreadGroupSize; internal GraphicsBuffer RingConfigBuffer; - internal GraphicsBuffer PatchCounterSets; + internal GraphicsBuffer PatchStatistics; internal GraphicsBuffer PatchCellIndices; internal GraphicsBuffer CellAllocationMarks; internal GraphicsBuffer CellPatchIndices; @@ -381,7 +377,6 @@ private class DefragPassData internal uint IterationCount; internal GraphicsBuffer RingConfigBuffer; internal GraphicsBuffer PatchCellIndices; - internal GraphicsBuffer PatchCounterSets; internal GraphicsBuffer PatchIrradiances0; internal GraphicsBuffer PatchIrradiances1; internal GraphicsBuffer PatchGeometries; @@ -401,7 +396,6 @@ private class UniformEstimationPassData internal GraphicsBuffer PatchIrradiances; internal GraphicsBuffer PatchGeometries; internal GraphicsBuffer PatchStatistics; - internal GraphicsBuffer PatchCounterSets; internal GraphicsBuffer CellPatchIndices; internal GraphicsBuffer CascadeOffsets; internal GraphicsBuffer PunctualLightSamples; @@ -476,7 +470,6 @@ private class RestirEstimationPassData internal GraphicsBuffer PatchIrradiances; internal GraphicsBuffer PatchStatistics; internal GraphicsBuffer PatchGeometries; - internal GraphicsBuffer PatchCounterSets; internal GraphicsBuffer PatchRealizations; internal uint RingConfigOffset; internal float ShortHysteresis; @@ -490,7 +483,6 @@ private class RisEstimationPassData internal GraphicsBuffer PatchIrradiances; internal GraphicsBuffer PatchStatistics; internal GraphicsBuffer PatchGeometries; - internal GraphicsBuffer PatchCounterSets; internal GraphicsBuffer CascadeOffsets; internal GraphicsBuffer CellPatchIndices; internal GraphicsBuffer PatchAccumulatedLuminances; @@ -540,7 +532,6 @@ private class TemporalFilterPassData internal GraphicsBuffer OutputPatchIrradiances; internal GraphicsBuffer PatchStatistics; internal GraphicsBuffer RingConfigBuffer; - internal GraphicsBuffer PatchCounterSets; internal uint PatchCapacity; internal uint RingConfigOffset; internal float ShortHysteresis; @@ -556,6 +547,7 @@ internal static class ShaderIDs public static readonly int _DirectionalLightDirection = Shader.PropertyToID("_DirectionalLightDirection"); public static readonly int _DirectionalLightIntensity = Shader.PropertyToID("_DirectionalLightIntensity"); public static readonly int _MaterialAtlasTexelSize = Shader.PropertyToID("_MaterialAtlasTexelSize"); + public static readonly int _PunctualLightCount = Shader.PropertyToID("_PunctualLightCount"); public static readonly int _TransmissionTextures = Shader.PropertyToID("_TransmissionTextures"); public static readonly int _EmissionTextures = Shader.PropertyToID("_EmissionTextures"); public static readonly int _VolumeTargetPos = Shader.PropertyToID("_VolumeTargetPos"); @@ -576,15 +568,12 @@ internal static class ShaderIDs public static readonly int _ShortHysteresis = Shader.PropertyToID("_ShortHysteresis"); public static readonly int _PatchCellIndices = Shader.PropertyToID("_PatchCellIndices"); public static readonly int _RingConfigBuffer = Shader.PropertyToID("_RingConfigBuffer"); - public static readonly int _SpotLightPosition = Shader.PropertyToID("_SpotLightPosition"); - public static readonly int _SpotLightDirection = Shader.PropertyToID("_SpotLightDirection"); - public static readonly int _SpotLightCosAngle = Shader.PropertyToID("_SpotLightCosAngle"); + public static readonly int _PunctualLights = Shader.PropertyToID("_PunctualLights"); public static readonly int _Radius = Shader.PropertyToID("_Radius"); public static readonly int _InputPatchIrradiances = Shader.PropertyToID("_InputPatchIrradiances"); public static readonly int _OutputPatchIrradiances = Shader.PropertyToID("_OutputPatchIrradiances"); public static readonly int _PatchIrradiances = Shader.PropertyToID("_PatchIrradiances"); public static readonly int _FrameIdx = Shader.PropertyToID("_FrameIdx"); - public static readonly int _PatchCounterSets = Shader.PropertyToID("_PatchCounterSets"); public static readonly int _CascadeOffsets = Shader.PropertyToID("_CascadeOffsets"); public static readonly int _PatchIrradiances0 = Shader.PropertyToID("_PatchIrradiances0"); public static readonly int _PatchIrradiances1 = Shader.PropertyToID("_PatchIrradiances1"); @@ -623,7 +612,7 @@ public SurfaceCache( _volume = new SurfaceCacheVolume(volParams.Resolution, volParams.CascadeCount, volParams.Size); _ringConfig = new SurfaceCacheRingConfig(); _patches = new SurfaceCachePatchList(patchCapacity, estimationParams.Method); - _punctualLightSamples = new GraphicsBuffer(GraphicsBuffer.Target.Structured, (int)punctualLightSampleCount, sizeof(float) * 16); + _punctualLightSamples = new GraphicsBuffer(GraphicsBuffer.Target.Structured, (int)punctualLightSampleCount, sizeof(float) * 19); _estimationParams = estimationParams; _patchFilteringParams = patchFilteringParams; @@ -661,7 +650,6 @@ private void RecordDefragmentation(RenderGraph renderGraph, uint frameIdx) passData.ThreadGroupSize = _resources.DefragKernelGroupSize; passData.RingConfigBuffer = RingConfig.Buffer; passData.PatchCellIndices = Patches.CellIndices; - passData.PatchCounterSets = Patches.CounterSets; passData.PatchIrradiances0 = Patches.Irradiances[0]; passData.PatchIrradiances1 = Patches.Irradiances[2]; passData.PatchGeometries = Patches.Geometries; @@ -687,8 +675,8 @@ private void RecordEviction(RenderGraph renderGraph, uint frameIdx) passData.ThreadGroupSize = _resources.EvictionKernelGroupSize; passData.RingConfigBuffer = RingConfig.Buffer; passData.RingConfigOffset = RingConfig.OffsetA; - passData.PatchCounterSets = Patches.CounterSets; passData.PatchCellIndices = Patches.CellIndices; + passData.PatchStatistics = Patches.Statistics; passData.CellAllocationMarks = Volume.CellAllocationMarks; passData.CellPatchIndices = Volume.CellPatchIndices; passData.PatchCapacity = Patches.Capacity; @@ -742,7 +730,6 @@ private uint RecordFiltering(RenderGraph renderGraph, uint frameIdx) passData.OutputPatchIrradiances = Patches.Irradiances[2]; passData.PatchStatistics = Patches.Statistics; passData.RingConfigBuffer = RingConfig.Buffer; - passData.PatchCounterSets = Patches.CounterSets; passData.PatchCapacity = Patches.Capacity; passData.RingConfigOffset = RingConfig.OffsetA; passData.ShortHysteresis = _shortHysteresis; @@ -769,7 +756,6 @@ private void RecordEstimation(RenderGraph renderGraph, uint frameIdx, SurfaceCac passData.PatchIrradiances = Patches.Irradiances[0]; passData.PatchGeometries = Patches.Geometries; passData.PatchStatistics = Patches.Statistics; - passData.PatchCounterSets = Patches.CounterSets; passData.PunctualLightSamples = PunctualLightSamples; passData.PunctualLightSampleCount = (uint)PunctualLightSamples.count; passData.World = world; @@ -857,7 +843,6 @@ private void RecordEstimation(RenderGraph renderGraph, uint frameIdx, SurfaceCac passData.RingConfigBuffer = RingConfig.Buffer; passData.PatchGeometries = Patches.Geometries; passData.PatchRealizations = Patches.RestirRealizations[1]; - passData.PatchCounterSets = Patches.CounterSets; passData.PatchIrradiances = Patches.Irradiances[0]; passData.PatchStatistics = Patches.Statistics; passData.RingConfigOffset = RingConfig.OffsetA; @@ -879,7 +864,6 @@ private void RecordEstimation(RenderGraph renderGraph, uint frameIdx, SurfaceCac passData.PatchIrradiances = Patches.Irradiances[0]; passData.PatchStatistics = Patches.Statistics; passData.PatchGeometries = Patches.Geometries; - passData.PatchCounterSets = Patches.CounterSets; passData.CascadeOffsets = Volume.CascadeOffsetBuffer; passData.World = world; passData.AlbedoBoost = _albedoBoost; @@ -952,16 +936,15 @@ private void RecordScrolling(RenderGraph renderGraph) static void UniformEstimate(UniformEstimationPassData data, UnsafeGraphContext graphCtx) { var cmd = CommandBufferHelpers.GetNativeCommandBuffer(graphCtx.cmd); - var nullableSpotLight = data.World.GetSpotLight(); + var punctualLightBuffer = data.World.GetPunctualLightBuffer(); + var punctualLightCount = (int)data.World.GetPunctualLightCount(); - if (nullableSpotLight.HasValue) + if (punctualLightCount != 0) { - var spotLight = nullableSpotLight.Value; var shader = data.PunctualLightSamplingShader; data.World.GetAccelerationStructure().Bind(cmd, "_RayTracingAccelerationStructure", shader); - shader.SetVectorParam(cmd, ShaderIDs._SpotLightPosition, spotLight.Position); - shader.SetVectorParam(cmd, ShaderIDs._SpotLightDirection, spotLight.Direction); - shader.SetFloatParam(cmd, ShaderIDs._SpotLightCosAngle, Mathf.Cos(spotLight.Angle / 360.0f * 2.0f * Mathf.PI * 0.5f)); + shader.SetBufferParam(cmd, ShaderIDs._PunctualLights, punctualLightBuffer); + shader.SetIntParam(cmd, ShaderIDs._PunctualLightCount, punctualLightCount); shader.SetFloatParam(cmd, ShaderIDs._FrameIdx, data.FrameIdx); shader.SetBufferParam(cmd, ShaderIDs._Samples, data.PunctualLightSamples); shader.SetBufferParam(cmd, ShaderIDs._MaterialEntries, data.World.GetMaterialListBuffer()); @@ -980,7 +963,6 @@ static void UniformEstimate(UniformEstimationPassData data, UnsafeGraphContext g shader.SetBufferParam(cmd, ShaderIDs._PatchIrradiances, data.PatchIrradiances); shader.SetBufferParam(cmd, ShaderIDs._PatchGeometries, data.PatchGeometries); shader.SetBufferParam(cmd, ShaderIDs._PatchStatistics, data.PatchStatistics); - shader.SetBufferParam(cmd, ShaderIDs._PatchCounterSets, data.PatchCounterSets); shader.SetBufferParam(cmd, ShaderIDs._CascadeOffsets, data.CascadeOffsets); shader.SetIntParam(cmd, ShaderIDs._FrameIdx, (int)data.FrameIdx); shader.SetIntParam(cmd, ShaderIDs._VolumeSpatialResolution, (int)data.VolumeSpatialResolution); @@ -1000,8 +982,7 @@ static void UniformEstimate(UniformEstimationPassData data, UnsafeGraphContext g shader.SetTextureParam(cmd, ShaderIDs._TransmissionTextures, data.World.GetMaterialTransmissionTextures()); shader.SetFloatParam(cmd, ShaderIDs._AlbedoBoost, data.AlbedoBoost); shader.SetFloatParam(cmd, ShaderIDs._MaterialAtlasTexelSize, GetMaterialAtlasTexelSize(data.World.GetMaterialAlbedoTextures())); - shader.SetIntParam(cmd, Shader.PropertyToID("_HasSpotLight"), nullableSpotLight.HasValue ? 1 : 0); - shader.SetVectorParam(cmd, Shader.PropertyToID("_SpotLightIntensity"), nullableSpotLight.HasValue ? nullableSpotLight.Value.Intensity : Vector3.zero); + shader.SetIntParam(cmd, ShaderIDs._PunctualLightCount, punctualLightCount); var (dirLightDirection, dirLightIntensity) = GetDirectionalLightUniforms(data.World.GetDirectionalLight()); shader.SetVectorParam(cmd, ShaderIDs._DirectionalLightDirection, dirLightDirection); @@ -1091,7 +1072,6 @@ static void RestirEstimate(RestirEstimationPassData data, ComputeGraphContext cg cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._PatchIrradiances, data.PatchIrradiances); cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._PatchStatistics, data.PatchStatistics); cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._PatchRealizations, data.PatchRealizations); - cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._PatchCounterSets, data.PatchCounterSets); cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._PatchGeometries, data.PatchGeometries); cmd.SetComputeIntParam(shader, ShaderIDs._RingConfigOffset, (int)data.RingConfigOffset); cmd.SetComputeFloatParam(shader, ShaderIDs._ShortHysteresis, data.ShortHysteresis); @@ -1109,7 +1089,6 @@ static void RisEstimate(RisEstimationPassData data, UnsafeGraphContext graphCtx) shader.SetBufferParam(cmd, ShaderIDs._PatchIrradiances, data.PatchIrradiances); shader.SetBufferParam(cmd, ShaderIDs._PatchStatistics, data.PatchStatistics); shader.SetBufferParam(cmd, ShaderIDs._PatchGeometries, data.PatchGeometries); - shader.SetBufferParam(cmd, ShaderIDs._PatchCounterSets, data.PatchCounterSets); shader.SetBufferParam(cmd, ShaderIDs._CascadeOffsets, data.CascadeOffsets); shader.SetIntParam(cmd, ShaderIDs._FrameIdx, (int)data.FrameIdx); shader.SetIntParam(cmd, ShaderIDs._VolumeSpatialResolution, (int)data.VolumeSpatialResolution); @@ -1150,7 +1129,6 @@ static void Defrag(DefragPassData data, ComputeGraphContext cgContext) cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._RingConfigBuffer, data.RingConfigBuffer); cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._PatchCellIndices, data.PatchCellIndices); cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._CellPatchIndices, data.CellPatchIndices); - cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._PatchCounterSets, data.PatchCounterSets); cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._PatchIrradiances0, data.PatchIrradiances0); cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._PatchIrradiances1, data.PatchIrradiances1); cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._PatchGeometries, data.PatchGeometries); @@ -1207,7 +1185,6 @@ static void FilterTemporally(TemporalFilterPassData data, ComputeGraphContext cg var shader = data.Shader; var kernelIndex = data.KernelIndex; cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._RingConfigBuffer, data.RingConfigBuffer); - cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._PatchCounterSets, data.PatchCounterSets); cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._PatchStatistics, data.PatchStatistics); cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._InputPatchIrradiances, data.InputPatchIrradiances); cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._OutputPatchIrradiances, data.OutputPatchIrradiances); @@ -1265,7 +1242,7 @@ static void Evict(EvictionPassData passData, ComputeGraphContext cgContext) var shader = passData.Shader; var kernelIndex = passData.KernelIndex; cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._RingConfigBuffer, passData.RingConfigBuffer); - cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._PatchCounterSets, passData.PatchCounterSets); + cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._PatchStatistics, passData.PatchStatistics); cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._PatchCellIndices, passData.PatchCellIndices); cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._CellAllocationMarks, passData.CellAllocationMarks); cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._CellPatchIndices, passData.CellPatchIndices); diff --git a/Packages/com.unity.render-pipelines.core/Runtime/Lighting/SurfaceCache/SurfaceCacheWorld.cs b/Packages/com.unity.render-pipelines.core/Runtime/Lighting/SurfaceCache/SurfaceCacheWorld.cs index 530fc6955ae..c47b9838cb3 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/Lighting/SurfaceCache/SurfaceCacheWorld.cs +++ b/Packages/com.unity.render-pipelines.core/Runtime/Lighting/SurfaceCache/SurfaceCacheWorld.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using Unity.Collections.LowLevel.Unsafe; using UnityEngine.PathTracing.Core; using UnityEngine.Rendering.UnifiedRayTracing; @@ -38,69 +39,239 @@ internal struct DirectionalLight internal Vector3 Intensity; } - internal struct SpotLight + internal struct PunctualLight { internal Vector3 Position; internal Vector3 Direction; internal Vector3 Intensity; - internal float Angle; + internal float CosAngle; } - internal class LightSet + internal class LightSet : IDisposable { - (LightHandle, DirectionalLight)? _directionalLightPair; - (LightHandle, SpotLight)? _spotLightPair; - Dictionary _handleToTypeMap = new(); - LightHandleSet _handles = new(); - - public DirectionalLight? DirectionalLight => _directionalLightPair.HasValue ? _directionalLightPair.Value.Item2 : null; - public SpotLight? SpotLight => _spotLightPair.HasValue ? _spotLightPair.Value.Item2 : null; + struct Light { } - internal LightHandle Add(LightDescriptor desc) + class LightList : IDisposable where T : struct { - var handle = _handles.Add(); - _handleToTypeMap[handle] = desc.Type; - if (desc.Type == LightType.Directional && !_directionalLightPair.HasValue) + private HandleSet _handles = new(); + private List _list = new(); + private Dictionary, int> _handleToIndex = new(); + private Dictionary> _indexToHandle = new(); + private bool _gpuDirty = false; + private GraphicsBuffer _buffer; + + internal uint Count => (uint)_list.Count; + internal List Values => _list; + internal GraphicsBuffer Buffer => _buffer; + + internal Handle Add(T light) { - var dirLight = new DirectionalLight() - { - Direction = desc.Transform.GetColumn(2), - Intensity = desc.LinearLightColor - }; - _directionalLightPair = (handle, dirLight); + var handle = _handles.Add(); + var index = _list.Count; + _handleToIndex[handle] = index; + _indexToHandle[index] = handle; + _list.Add(light); + _gpuDirty = true; + return handle; } - else if (desc.Type == LightType.Spot && !_spotLightPair.HasValue) + + internal void Update(Handle handle, T light) { - var dirLight = new SpotLight() + Debug.Assert(_handleToIndex.ContainsKey(handle)); + _gpuDirty = true; + _list[_handleToIndex[handle]] = light; + } + + internal void Remove(Handle handle) + { + Debug.Assert(_handleToIndex.ContainsKey(handle)); + _gpuDirty = true; + var swapIndex = _handleToIndex[handle]; + _handleToIndex.Remove(handle); + + int endIndex = _list.Count - 1; + Handle moveHandle = _indexToHandle[endIndex]; + + _list[swapIndex] = _list[endIndex]; + _list.RemoveAt(endIndex); + _indexToHandle[swapIndex] = moveHandle; + _handleToIndex[moveHandle] = swapIndex; + } + + internal void Commit(CommandBuffer cmd) + { + if (_gpuDirty) { - Position = desc.Transform.GetPosition(), - Direction = desc.Transform.GetColumn(2), - Intensity = desc.LinearLightColor, - Angle = desc.SpotAngle - }; - _spotLightPair = (handle, dirLight); + _gpuDirty = false; + if (_buffer == null || _buffer.count < _list.Count) + { + _buffer?.Dispose(); + _buffer = new GraphicsBuffer(GraphicsBuffer.Target.Structured, _list.Count, UnsafeUtility.SizeOf()); + } + cmd.SetBufferData(_buffer, _list); + } } - return handle; + public void Dispose() + { + _buffer?.Dispose(); + } } - internal void Remove(LightHandle handle) + LightHandleSet _handles = new(); + Dictionary)> _handleToTypeAndSubHandleMap = new(); + LightList _punctualLights = new(); + LightList _directionalLights = new(); + + public DirectionalLight? DirectionalLight => 0 < _directionalLights.Count ? _directionalLights.Values[0] : null; + public GraphicsBuffer PunctualLightBuffer => _punctualLights.Buffer; + public uint PunctualLightCount => _punctualLights.Count; + + Handle AddToList(LightDescriptor desc) + { + if (desc.Type == LightType.Directional) + return _directionalLights.Add(ConvertDirectionalLight(desc)); + else if (desc.Type == LightType.Spot) + return _punctualLights.Add(ConvertSpotLight(desc)); + else if (desc.Type == LightType.Point) + return _punctualLights.Add(ConvertPointLight(desc)); + else + return Handle.Invalid; + } + + void RemoveFromList(LightType type, Handle subHandle) + { + if (type == LightType.Directional) + _directionalLights.Remove(subHandle); + else if (type == LightType.Spot || type == LightType.Point) + _punctualLights.Remove(subHandle); + } + + void UpdateInList(Handle subHandle, LightDescriptor desc) + { + if (desc.Type == LightType.Directional) + _directionalLights.Update(subHandle, ConvertDirectionalLight(desc)); + else if (desc.Type == LightType.Spot) + _punctualLights.Update(subHandle, ConvertSpotLight(desc)); + else if (desc.Type == LightType.Point) + _punctualLights.Update(subHandle, ConvertPointLight(desc)); + } + + static Vector3 GetLightDirection(LightDescriptor desc) + { + return desc.Transform.GetColumn(2).normalized; + } + + static DirectionalLight ConvertDirectionalLight(LightDescriptor desc) + { + return new DirectionalLight() + { + Direction = GetLightDirection(desc), + Intensity = desc.LinearLightColor + }; + } + + static PunctualLight ConvertSpotLight(LightDescriptor desc) + { + return new PunctualLight() + { + Position = desc.Transform.GetPosition(), + Direction = GetLightDirection(desc), + Intensity = desc.LinearLightColor, + CosAngle = Mathf.Cos(desc.SpotAngle / 360.0f * 2.0f * Mathf.PI * 0.5f) + }; + } + + static PunctualLight ConvertPointLight(LightDescriptor desc) + { + return new PunctualLight() + { + Position = desc.Transform.GetPosition(), + Direction = Vector3.up, // doesn't matter + Intensity = desc.LinearLightColor, + CosAngle = -1.0f // cos(pi) = -1 + }; + } + + static bool ShouldBeInList(LightDescriptor desc) { - Debug.Assert(_handleToTypeMap.ContainsKey(handle), "Unexpected light handle."); + return desc.LinearLightColor != Vector3.zero; + } - var type = _handleToTypeMap[handle]; + internal void Update(LightHandle handle, LightDescriptor desc) + { + var (type, subHandle) = _handleToTypeAndSubHandleMap[handle]; - _handleToTypeMap.Remove(handle); - _handles.Remove(handle); - if (type == LightType.Directional && _directionalLightPair.HasValue && handle == _directionalLightPair.Value.Item1) + if (type == desc.Type) { - _directionalLightPair = null; + bool isInList = subHandle != Handle.Invalid; + if (ShouldBeInList(desc)) + { + if (isInList) + { + UpdateInList(subHandle, desc); + } + else + { + subHandle = AddToList(desc); + _handleToTypeAndSubHandleMap[handle] = (type, subHandle); + } + } + else + { + if (isInList) + { + RemoveFromList(type, subHandle); + _handleToTypeAndSubHandleMap[handle] = (type, Handle.Invalid); + } + } } - if (type == LightType.Spot && _spotLightPair.HasValue && handle == _spotLightPair.Value.Item1) + else { - _spotLightPair = null; + var oldType = type; + var newType = desc.Type; + RemoveFromList(oldType, subHandle); + Handle newSubHandle = Handle.Invalid; + if (ShouldBeInList(desc)) + newSubHandle = AddToList(desc); + _handleToTypeAndSubHandleMap[handle] = (newType, newSubHandle); } } + + internal LightHandle Add(LightDescriptor desc) + { + var handle = _handles.Add(); + Handle subHandle = Handle.Invalid; + + if (ShouldBeInList(desc)) + subHandle = AddToList(desc); + + _handleToTypeAndSubHandleMap[handle] = (desc.Type, subHandle); + return handle; + } + + internal void Remove(LightHandle handle) + { + Debug.Assert(_handleToTypeAndSubHandleMap.ContainsKey(handle), "Unexpected light handle."); + + var (type, subHandle) = _handleToTypeAndSubHandleMap[handle]; + _handleToTypeAndSubHandleMap.Remove(handle); + + if (subHandle != Handle.Invalid) + RemoveFromList(type, subHandle); + } + + internal void Commit(CommandBuffer cmd) + { + _directionalLights.Commit(cmd); + _punctualLights.Commit(cmd); + } + + public void Dispose() + { + _directionalLights.Dispose(); + _punctualLights.Dispose(); + } } private readonly InstanceHandleSet _instanceHandleSet = new(); @@ -134,9 +305,14 @@ public void SetEnvironmentMode(CubemapRender.Mode mode) _cubemapRender.SetMode(mode); } - public SpotLight? GetSpotLight() + public GraphicsBuffer GetPunctualLightBuffer() { - return _lights.SpotLight; + return _lights.PunctualLightBuffer; + } + + public uint GetPunctualLightCount() + { + return _lights.PunctualLightCount; } public void SetEnvironmentMaterial(Material mat) @@ -179,6 +355,7 @@ public void Dispose() _rayTracingAccelerationStructure?.Dispose(); _materialPool?.Dispose(); _cubemapRender?.Dispose(); + _lights.Dispose(); } public AccelStructAdapter GetAccelerationStructure() @@ -237,6 +414,30 @@ public InstanceHandle AddInstance( return instance; } + public InstanceHandle AddInstance( + Terrain terrain, + MaterialHandle material, + uint mask, + in Matrix4x4 localToWorldMatrix) + { + Debug.Assert(terrain.terrainData != null); + Debug.Assert(material != MaterialHandle.Invalid); + + Span masks = stackalloc uint[1] { mask }; + + Span materialIndices = stackalloc uint[1]; + Span isOpaque = stackalloc bool[1]; + + _materialPool.GetMaterialInfo(material.Value, out materialIndices[0], out bool isTransmissive); + isOpaque[0] = !isTransmissive; + + Component comp = terrain; + InstanceHandle instance = _instanceHandleSet.Add(); + + _rayTracingAccelerationStructure.AddInstance(instance.Value, comp, masks, materialIndices, isOpaque, terrain.renderingLayerMask); + return instance; + } + public void UpdateInstanceTransform(InstanceHandle instance, Matrix4x4 localToWorldMatrix) { _rayTracingAccelerationStructure.UpdateInstanceTransform(instance.Value, localToWorldMatrix); @@ -266,7 +467,6 @@ public LightHandle[] AddLights(Span lightDescs) var handle = _lights.Add(lightDescs[i]); handles[i] = handle; } - UpdateLights(handles, lightDescs); return handles; } @@ -277,8 +477,7 @@ public void UpdateLights(LightHandle[] lightHandles, Span light { ref readonly LightDescriptor descriptor = ref lightDescriptors[i]; var handle = lightHandles[i]; - _lights.Remove(handle); - lightHandles[i] = _lights.Add(descriptor); + _lights.Update(handle, descriptor); } } @@ -290,12 +489,13 @@ public void RemoveLights(Span lightHandles) } } - public void Build(CommandBuffer cmdBuf, ref GraphicsBuffer scratchBuffer, uint envCubemapResolution, UnityEngine.Light sun) + public void Commit(CommandBuffer cmdBuf, ref GraphicsBuffer scratchBuffer, uint envCubemapResolution, UnityEngine.Light sun) { Debug.Assert(_rayTracingAccelerationStructure != null); _materialPool.Build(cmdBuf); _rayTracingAccelerationStructure.Build(cmdBuf, ref scratchBuffer); _cubemapRender.Update(cmdBuf, sun, (int)envCubemapResolution); + _lights.Commit(cmdBuf); } } } diff --git a/Packages/com.unity.render-pipelines.core/Runtime/Lighting/SurfaceCache/TemporalFiltering.compute b/Packages/com.unity.render-pipelines.core/Runtime/Lighting/SurfaceCache/TemporalFiltering.compute index 34e56fdaf93..9cb0cfd9bd6 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/Lighting/SurfaceCache/TemporalFiltering.compute +++ b/Packages/com.unity.render-pipelines.core/Runtime/Lighting/SurfaceCache/TemporalFiltering.compute @@ -8,7 +8,6 @@ #include "TemporalFiltering.hlsl" StructuredBuffer _RingConfigBuffer; -StructuredBuffer _PatchCounterSets; StructuredBuffer _InputPatchIrradiances; StructuredBuffer _PatchStatistics; RWStructuredBuffer _OutputPatchIrradiances; @@ -26,7 +25,7 @@ void FilterTemporally(uint patchIdx : SV_DispatchThreadID) const SphericalHarmonics::RGBL1 oldIrradiance = _OutputPatchIrradiances[patchIdx]; const PatchUtil::PatchStatisticsSet stats = _PatchStatistics[patchIdx]; - const uint updateCount = PatchUtil::GetUpdateCount(_PatchCounterSets[patchIdx]); + const uint updateCount = PatchUtil::GetUpdateCount(_PatchStatistics[patchIdx].patchCounters); SphericalHarmonics::RGBL1 output = FilterTemporallyVarianceGuided(_ShortHysteresis, updateCount, stats.variance, stats.mean, newIrradiance, oldIrradiance); _OutputPatchIrradiances[patchIdx] = output; diff --git a/Packages/com.unity.render-pipelines.core/Runtime/Lighting/SurfaceCache/TemporalFiltering.hlsl b/Packages/com.unity.render-pipelines.core/Runtime/Lighting/SurfaceCache/TemporalFiltering.hlsl index b04dff284fb..e29a5643808 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/Lighting/SurfaceCache/TemporalFiltering.hlsl +++ b/Packages/com.unity.render-pipelines.core/Runtime/Lighting/SurfaceCache/TemporalFiltering.hlsl @@ -15,7 +15,7 @@ SphericalHarmonics::RGBL1 FilterTemporallyVarianceGuided(float shortHysteresis, const float3 driftWeight = smoothstep(0, 1, drift * driftScaling); const float longHys = 0.995f; - float3 updateWeight = lerp(longHys, shortHysteresis, driftWeight); + float3 updateWeight = lerp((float3)longHys, (float3)shortHysteresis, driftWeight); if (updateCount != PatchUtil::updateMax) updateWeight = 1.0f - rcp(updateCount); diff --git a/Packages/com.unity.render-pipelines.core/Runtime/Lighting/SurfaceCache/UniformEstimation.hlsl b/Packages/com.unity.render-pipelines.core/Runtime/Lighting/SurfaceCache/UniformEstimation.hlsl index f342f50890a..9dbb7d41780 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/Lighting/SurfaceCache/UniformEstimation.hlsl +++ b/Packages/com.unity.render-pipelines.core/Runtime/Lighting/SurfaceCache/UniformEstimation.hlsl @@ -6,13 +6,12 @@ #include "PathTracing.hlsl" #include "Estimation.hlsl" #include "RingBuffer.hlsl" -#include "PunctualLightSample.hlsl" +#include "PunctualLights.hlsl" StructuredBuffer _RingConfigBuffer; RWStructuredBuffer _PatchIrradiances; StructuredBuffer _PatchGeometries; RWStructuredBuffer _PatchStatistics; -RWStructuredBuffer _PatchCounterSets; StructuredBuffer _CellPatchIndices; StructuredBuffer _CascadeOffsets; StructuredBuffer _MaterialEntries; @@ -27,8 +26,7 @@ TextureCube _EnvironmentCubemap; SamplerState sampler_EnvironmentCubemap; UNIFIED_RT_DECLARE_ACCEL_STRUCT(_RayTracingAccelerationStructure); -uint _HasSpotLight; -float3 _SpotLightIntensity; +uint _PunctualLightCount; uint _FrameIdx; uint _VolumeSpatialResolution; uint _CascadeCount; @@ -61,7 +59,7 @@ void SamplePunctualLightBounceRadiance( inout SphericalHarmonics::RGBL1 accumulator, inout bool gotValidSamples) { - rng.Init(patchIdx, _FrameIdx * _SampleCount + 1024); + rng.Init(patchIdx, _FrameIdx * _SampleCount); SphericalHarmonics::RGBL1 radianceAccumulator = (SphericalHarmonics::RGBL1)0; uint validSampleCount = 0; @@ -73,7 +71,6 @@ void SamplePunctualLightBounceRadiance( _PunctualLightSamples, _PunctualLightSampleCount, rng.GetFloat(0), - _SpotLightIntensity, patchGeo.position, patchGeo.normal); @@ -189,7 +186,7 @@ void Estimate(UnifiedRT::DispatchInfo dispatchInfo) radianceSampleMean, gotValidSamples); - if (_HasSpotLight) + if (_PunctualLightCount != 0) { SamplePunctualLightBounceRadiance( rng, @@ -202,5 +199,5 @@ void Estimate(UnifiedRT::DispatchInfo dispatchInfo) } if (gotValidSamples) - ProcessAndStoreRadianceSample(_PatchIrradiances, _PatchStatistics, _PatchCounterSets, patchIdx, radianceSampleMean, _ShortHysteresis); + ProcessAndStoreRadianceSample(_PatchIrradiances, _PatchStatistics, patchIdx, radianceSampleMean, _ShortHysteresis); } diff --git a/Packages/com.unity.render-pipelines.core/Runtime/PathTracing/Environment/CubemapRender.cs b/Packages/com.unity.render-pipelines.core/Runtime/PathTracing/Environment/CubemapRender.cs index 7eb8a63f480..293fe19145d 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/PathTracing/Environment/CubemapRender.cs +++ b/Packages/com.unity.render-pipelines.core/Runtime/PathTracing/Environment/CubemapRender.cs @@ -102,7 +102,6 @@ public void Update(CommandBuffer cmd, Light sun, int resolution) } else { - // Note: prefabs have a null skyboxMaterial newHash = 42; ReleaseCubemapIfExists(); } @@ -152,6 +151,11 @@ private void RenderWithMaterial(CommandBuffer cmd, Light sun, int cubemapResolut properties.SetVector(Shader.PropertyToID("_LightColor0"), LightColorInRenderingSpace(sun)); properties.SetVector(Shader.PropertyToID("_WorldSpaceLightPos0"), -sun.GetComponent().forward); } + else + { + properties.SetVector(Shader.PropertyToID("_LightColor0"), Color.black); + properties.SetVector(Shader.PropertyToID("_WorldSpaceLightPos0"), new Vector4(0, 0, -1, 0)); + } for (int faceIndex = 0; faceIndex < 6; ++faceIndex) { diff --git a/Packages/com.unity.render-pipelines.core/Runtime/PathTracing/ProbeIntegrator.cs b/Packages/com.unity.render-pipelines.core/Runtime/PathTracing/ProbeIntegrator.cs index d9d5f840a58..4f8b2f0b610 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/PathTracing/ProbeIntegrator.cs +++ b/Packages/com.unity.render-pipelines.core/Runtime/PathTracing/ProbeIntegrator.cs @@ -190,7 +190,8 @@ private void DispatchProbeKernel( } } } - internal static ulong CalculateWorkSteps(uint probesCount, uint sampleCount, uint bounceCount) => probesCount*sampleCount*(0 == bounceCount ? 1 : bounceCount); + internal static ulong CalculateWorkSteps(ulong probesCount, ulong sampleCount, ulong bounceCount) => + probesCount * sampleCount * (0 == bounceCount ? 1 : bounceCount); internal void EstimateIndirectRadianceShl2( CommandBuffer cmd, diff --git a/Packages/com.unity.render-pipelines.core/Runtime/PathTracing/RenderPipeline/SceneUpdatesTracker.cs b/Packages/com.unity.render-pipelines.core/Runtime/PathTracing/RenderPipeline/SceneUpdatesTracker.cs index 5f7fb89bd83..013db4ee8b9 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/PathTracing/RenderPipeline/SceneUpdatesTracker.cs +++ b/Packages/com.unity.render-pipelines.core/Runtime/PathTracing/RenderPipeline/SceneUpdatesTracker.cs @@ -11,9 +11,14 @@ namespace UnityEngine.Rendering.LiveGI { internal class SceneChanges { - public List addedInstances; - public List changedInstances; - public List removedInstances; + public List addedMeshRenderers; + public List changedMeshRenderers; + public List removedMeshRenderers; + + public List addedTerrains; + public List changedTerrains; + public List removedTerrains; + public List changedTerrainData; public List addedMaterials; public List removedMaterials; @@ -25,9 +30,13 @@ internal class SceneChanges public SceneChanges() { - addedInstances = new List(); - changedInstances = new List(); - removedInstances = new List(); + addedMeshRenderers = new List(); + changedMeshRenderers = new List(); + removedMeshRenderers = new List(); + + addedTerrains = new List(); + changedTerrains = new List(); + removedTerrains = new List(); addedMaterials = new List(); removedMaterials = new List(); @@ -40,16 +49,21 @@ public SceneChanges() public bool HasChanges() { - return (addedInstances.Count | removedInstances.Count | changedInstances.Count + return (addedMeshRenderers.Count | removedMeshRenderers.Count | changedMeshRenderers.Count + | addedTerrains.Count | changedTerrains.Count | removedTerrains.Count | addedMaterials.Count | removedMaterials.Count | changedMaterials.Count | addedLights.Count | removedLights.Count | changedLights.Count) != 0; } public void Clear() { - addedInstances.Clear(); - removedInstances.Clear(); - changedInstances.Clear(); + addedMeshRenderers.Clear(); + removedMeshRenderers.Clear(); + changedMeshRenderers.Clear(); + + addedTerrains.Clear(); + removedTerrains.Clear(); + changedTerrains.Clear(); addedMaterials.Clear(); removedMaterials.Clear(); @@ -63,9 +77,16 @@ public void Clear() [System.Flags] internal enum ModifiedProperties { Transform = 1, Material = 2, IsStatic = 4, ShadowCasting = 8, Layer = 16 } - internal struct InstanceChanges + + internal struct MeshRendererInstanceChanges { - public MeshRenderer meshRenderer; + public MeshRenderer instance; + public ModifiedProperties changes; + } + + internal struct TerrainInstanceChanges + { + public Terrain instance; public ModifiedProperties changes; } @@ -98,7 +119,7 @@ public MaterialData(Material material, uint timestamp) public Timestamp timestamp; } - class InstanceData + class MeshRendererInstanceData { public Timestamp timestamp; public EntityId[] materialIDs; @@ -108,6 +129,24 @@ class InstanceData public ShadowCastingMode shadowCastingMode; } + class TerrainInstanceData + { + public Timestamp timestamp; + public EntityId materialID; + public Material material; + public Terrain terrain; + public bool isStatic; + public ShadowCastingMode shadowCastingMode; + } + + class TerrainDataSnapshot + { + public Timestamp timestamp; + public Texture heightmapTexture; + public int treeInstanceCount; + public UnityEngine.Hash128 heightmapImageContentsHash; + } + class LightData { public Light light; @@ -115,8 +154,10 @@ class LightData } ObjectDispatcher m_ObjectDispatcher; - Dictionary m_Instances; + Dictionary m_MeshRenderers; + Dictionary m_Terrains; Dictionary m_Materials; + Dictionary m_TerrainData; Dictionary m_Lights; SceneChanges m_Changes; uint m_Timestamp; @@ -125,7 +166,9 @@ class LightData public SceneUpdatesTracker() { m_Changes = new SceneChanges(); - m_Instances = new Dictionary(); + m_MeshRenderers = new Dictionary(); + m_Terrains = new Dictionary(); + m_TerrainData = new Dictionary(); m_Materials = new Dictionary(); m_Lights = new Dictionary(); @@ -136,6 +179,8 @@ public SceneUpdatesTracker() #endif m_ObjectDispatcher.EnableTypeTracking(ObjectDispatcher.TypeTrackingFlags.SceneObjects); m_ObjectDispatcher.EnableTransformTracking(ObjectDispatcher.TransformTrackingType.GlobalTRS); + m_ObjectDispatcher.EnableTypeTracking(ObjectDispatcher.TypeTrackingFlags.SceneObjects); + m_ObjectDispatcher.EnableTransformTracking(ObjectDispatcher.TransformTrackingType.GlobalTRS); m_ObjectDispatcher.EnableTypeTracking(ObjectDispatcher.TypeTrackingFlags.SceneObjects | ObjectDispatcher.TypeTrackingFlags.Assets); m_ObjectDispatcher.EnableTypeTracking(ObjectDispatcher.TypeTrackingFlags.SceneObjects); m_ObjectDispatcher.EnableTransformTracking(ObjectDispatcher.TransformTrackingType.GlobalTRS); @@ -152,7 +197,8 @@ public SceneChanges GetChanges(bool filterBakedLights) m_Timestamp++; m_Changes.Clear(); - FindInstancesChanges(); + FindMeshRendererChanges(); + FindTerrainChanges(); FindMaterialsChanges(); FindLightChanges(filterBakedLights); @@ -163,10 +209,10 @@ private void FindMaterialsChanges() { using var materialChanges = m_ObjectDispatcher.GetTypeChangesAndClear(Unity.Collections.Allocator.Temp); - // Handle added materials - foreach (var instance in m_Instances.Values) + // Handle added materials in mesh renderers + foreach (var meshRendererInstance in m_MeshRenderers.Values) { - foreach (var material in instance.materials) + foreach (var material in meshRendererInstance.materials) { MaterialData data = null; if (material == null) @@ -183,6 +229,24 @@ private void FindMaterialsChanges() } } + // Handle added materials in terrains + foreach (var terrainInstance in m_Terrains.Values) + { + var material = terrainInstance.material; + MaterialData data = null; + if (material == null) + continue; + if (m_Materials.TryGetValue(material.GetEntityId(), out data)) + { + data.timestamp.lastVisit = m_Timestamp; + } + else + { + m_Changes.addedMaterials.Add(material); + m_Materials.Add(material.GetEntityId(), new MaterialData(material, m_Timestamp)); + } + } + var justCompiledMaterials = new HashSet(); // Handle removed and uncompiled materials @@ -232,38 +296,40 @@ private void FindMaterialsChanges() #endif } - private void FindInstancesChanges() + private static bool IntArraySequenceEqual(EntityId[] firstArray, EntityId[] secondArray) => ((ReadOnlySpan)firstArray).SequenceEqual(secondArray); + + private void FindMeshRendererChanges() { - // Handle changed instances + // Handle changed mesh renderers using var meshRendererChanges = m_ObjectDispatcher.GetTypeChangesAndClear(Unity.Collections.Allocator.Temp); var transformChanges = m_ObjectDispatcher.GetTransformChangesAndClear(ObjectDispatcher.TransformTrackingType.GlobalTRS, false); var changedRenderers = MergeChanges(meshRendererChanges.changed, transformChanges); - // Handle removed instances + // Handle removed mesh renderers foreach (var key in meshRendererChanges.destroyedID) { - m_Changes.removedInstances.Add(key); + m_Changes.removedMeshRenderers.Add(key); } // Update the remaining timestamps of the active mesh renderers - List keys = new List(m_Instances.Keys); + List keys = new List(m_MeshRenderers.Keys); foreach (var key in keys) { - if (!m_Instances[key].renderer) + if (!m_MeshRenderers[key].renderer) continue; - if (m_Instances[key].renderer.enabled && m_Instances[key].renderer.gameObject.activeInHierarchy) + if (m_MeshRenderers[key].renderer.enabled && m_MeshRenderers[key].renderer.gameObject.activeInHierarchy) { - m_Instances[key].timestamp.lastVisit = m_Timestamp; + m_MeshRenderers[key].timestamp.lastVisit = m_Timestamp; } else { - m_Changes.removedInstances.Add(key); + m_Changes.removedMeshRenderers.Add(key); } } - foreach (var key in m_Changes.removedInstances) - m_Instances.Remove(key); + foreach (var key in m_Changes.removedMeshRenderers) + m_MeshRenderers.Remove(key); foreach (var item in changedRenderers) @@ -276,13 +342,12 @@ private void FindInstancesChanges() continue; } - InstanceData oldData; - if (!m_Instances.TryGetValue(meshRenderer.GetEntityId(), out oldData)) + if (!m_MeshRenderers.TryGetValue(meshRenderer.GetEntityId(), out var oldData)) { // This renderer was just added var newData = CreateInstanceData(m_Timestamp, meshRenderer); - m_Instances.Add(meshRenderer.GetEntityId(), newData); - m_Changes.addedInstances.Add(meshRenderer); + m_MeshRenderers.Add(meshRenderer.GetEntityId(), newData); + m_Changes.addedMeshRenderers.Add(meshRenderer); continue; } @@ -293,9 +358,6 @@ private void FindInstancesChanges() if (tranformChanged) changes |= ModifiedProperties.Transform; - bool IntArraySequenceEqual(EntityId[] firstArray, EntityId[] secondArray) => - ((ReadOnlySpan)firstArray).SequenceEqual(secondArray); - if (!IntArraySequenceEqual(oldData.materialIDs, data.materialIDs)) changes |= ModifiedProperties.Material; @@ -307,20 +369,101 @@ bool IntArraySequenceEqual(EntityId[] firstArray, EntityId[] secondArray) => if (changes != 0) { - m_Changes.changedInstances.Add(new InstanceChanges() + m_Changes.changedMeshRenderers.Add(new MeshRendererInstanceChanges() + { + changes = changes, + instance = meshRenderer + }); + + m_MeshRenderers[meshRenderer.GetEntityId()] = data; + } + } + } + + private void FindTerrainChanges() + { + // Handle changed terrains + using var terrainChanges = m_ObjectDispatcher.GetTypeChangesAndClear(Unity.Collections.Allocator.Temp); + var terrainTransformChanges = m_ObjectDispatcher.GetTransformChangesAndClear(ObjectDispatcher.TransformTrackingType.GlobalTRS, false); + var changedTerrains = MergeChanges(terrainChanges.changed, terrainTransformChanges); + + // Handle removed terrains + foreach (var key in terrainChanges.destroyedID) + { + m_Changes.removedTerrains.Add(key); + } + + // Update the remaining timestamps of the active terrains + List keys = new List(m_Terrains.Keys); + foreach (var key in keys) + { + if (!m_Terrains[key].terrain) + continue; + + if (m_Terrains[key].terrain.enabled && m_Terrains[key].terrain.gameObject.activeInHierarchy) + { + m_Terrains[key].timestamp.lastVisit = m_Timestamp; + } + else + { + m_Changes.removedTerrains.Add(key); + } + } + + foreach (var key in m_Changes.removedTerrains) + m_Terrains.Remove(key); + + foreach (var item in changedTerrains) + { + var terrain = item.Value.objectReference; + bool tranformChanged = item.Value.transformChanged; + + if (!terrain.enabled || !terrain.gameObject.activeInHierarchy) + { + continue; + } + + if (!m_Terrains.TryGetValue(terrain.GetEntityId(), out var oldData)) + { + // This terrain was just added + var newData = CreateInstanceData(m_Timestamp, terrain); + m_Terrains.Add(terrain.GetEntityId(), newData); + m_Changes.addedTerrains.Add(terrain); + continue; + } + + var data = CreateInstanceData(m_Timestamp, terrain); + + ModifiedProperties changes = 0; + + if (tranformChanged) + changes |= ModifiedProperties.Transform; + + if (oldData.materialID != data.materialID) + changes |= ModifiedProperties.Material; + + if (oldData.isStatic != data.isStatic) + changes |= ModifiedProperties.IsStatic; + + if (oldData.shadowCastingMode != data.shadowCastingMode) + changes |= ModifiedProperties.ShadowCasting; + + if (changes != 0) + { + m_Changes.changedTerrains.Add(new TerrainInstanceChanges() { changes = changes, - meshRenderer = meshRenderer + instance = terrain }); - m_Instances[meshRenderer.GetEntityId()] = data; + m_Terrains[terrain.GetEntityId()] = data; } } } - InstanceData CreateInstanceData(uint timestamp, MeshRenderer meshRenderer) + static MeshRendererInstanceData CreateInstanceData(uint timestamp, MeshRenderer meshRenderer) { - return new InstanceData() + return new MeshRendererInstanceData() { timestamp = new Timestamp { lastVisit = timestamp, creation = timestamp }, isStatic = meshRenderer.gameObject.isStatic, @@ -331,6 +474,19 @@ InstanceData CreateInstanceData(uint timestamp, MeshRenderer meshRenderer) }; } + static TerrainInstanceData CreateInstanceData(uint timestamp, Terrain terrain) + { + return new TerrainInstanceData() + { + timestamp = new Timestamp { lastVisit = timestamp, creation = timestamp }, + isStatic = terrain.gameObject.isStatic, + material = terrain.splatBaseMaterial, + materialID = terrain.splatBaseMaterial != null ? terrain.splatBaseMaterial.GetEntityId() : EntityId.None, + shadowCastingMode = terrain.shadowCastingMode, + terrain = terrain, + }; + } + static private bool ShouldIncludeLight(Light light, bool filterBakedLights) { return light.enabled && diff --git a/Packages/com.unity.render-pipelines.core/Runtime/PathTracing/Shaders/ProbeIntegrationOcclusion.urtshader b/Packages/com.unity.render-pipelines.core/Runtime/PathTracing/Shaders/ProbeIntegrationOcclusion.urtshader index 7b066134748..c94cb8bedbb 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/PathTracing/Shaders/ProbeIntegrationOcclusion.urtshader +++ b/Packages/com.unity.render-pipelines.core/Runtime/PathTracing/Shaders/ProbeIntegrationOcclusion.urtshader @@ -40,27 +40,35 @@ void IntegrateOcclusion(UnifiedRT::DispatchInfo dispatchInfo) PathTracingSampler rngState; rngState.Init(inProbeIdxWithOffset, g_SampleOffset + inProbeSampleIdx); - uint lightIndex = g_PerProbeLightIndices[perProbeLightIndicesIdx]; - if (lightIndex == -1) + // NOTE: Do NOT attempt to refactor these nested if-statements to use 'continue'. + // FXC chokes on 'continue' instructions, resulting in completely incorrect output. + int lightIndex = g_PerProbeLightIndices[perProbeLightIndicesIdx]; + if (lightIndex >= 0) { - g_Occlusion[outOcclusionValueIdx] = 0.0f; - continue; + PTLight light = FetchLight(lightIndex); + if (light.type == SPOT_LIGHT || light.type == POINT_LIGHT || light.type == DIRECTIONAL_LIGHT) + { + uint dimsOffset = 0; + uint dimsUsed = 0; + float3 attenuation = 1.0f; + bool isVisible = IsLightVisibleFromPoint(dispatchInfo, accelStruct, g_AccelStructInstanceList, SHADOW_RAY_VIS_MASK, worldPosition, rngState, dimsOffset, dimsUsed, light, true, attenuation); + if (isVisible) + { + g_Occlusion[outOcclusionValueIdx] = 1.0f / g_SampleCount; + } + else + { + g_Occlusion[outOcclusionValueIdx] = 0.0f; + } + } + else + { + g_Occlusion[outOcclusionValueIdx] = 0.0f; + } } - - PTLight light = FetchLight(lightIndex); - if (light.type != SPOT_LIGHT && light.type != POINT_LIGHT && light.type != DIRECTIONAL_LIGHT) + else { g_Occlusion[outOcclusionValueIdx] = 0.0f; - continue; - } - - uint dimsOffset = 0; - uint dimsUsed = 0; - float3 attenuation = 1.0f; - bool isVisible = IsLightVisibleFromPoint(dispatchInfo, accelStruct, g_AccelStructInstanceList, SHADOW_RAY_VIS_MASK, worldPosition, rngState, dimsOffset, dimsUsed, light, true, attenuation); - if (isVisible) - { - g_Occlusion[outOcclusionValueIdx] = 1.0f / g_SampleCount; } } } diff --git a/Packages/com.unity.render-pipelines.core/Runtime/RenderGraph/Compiler/NativePassCompiler.cs b/Packages/com.unity.render-pipelines.core/Runtime/RenderGraph/Compiler/NativePassCompiler.cs index 7b445aa24bb..14a4754fb08 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/RenderGraph/Compiler/NativePassCompiler.cs +++ b/Packages/com.unity.render-pipelines.core/Runtime/RenderGraph/Compiler/NativePassCompiler.cs @@ -36,7 +36,7 @@ internal struct RenderGraphInputInfo NativeList m_BeginRenderPassAttachments; // Contains the index of the non culled passes for native render passes that has at least one pass culled. - NativeList m_NonCulledPassIndicesForRasterPasses; + internal NativeList m_NonCulledPassIndicesForRasterPasses; internal static bool s_ForceGenerateAuditsForTests = false; @@ -711,6 +711,14 @@ void TryMergeNativePasses() if (passWasCulled) { CollectNonCulledPassIndicesForRasterPasses(ctx.passData.Length, indexSinceLastCulledPass, clearList: !nonCulledPassIndicesListWasCleared); + nonCulledPassIndicesListWasCleared = true; + } + + // We need to clear this data to avoid reusing stale data from a previously compiled graph. + if (!nonCulledPassIndicesListWasCleared && m_NonCulledPassIndicesForRasterPasses.IsCreated) + { + m_NonCulledPassIndicesForRasterPasses.Clear(); + m_NonCulledPassIndicesForRasterPasses.SetCapacity(ctx.passData.Length); } if (activeNativePassId >= 0) diff --git a/Packages/com.unity.render-pipelines.core/Runtime/RenderGraph/Debug/RenderGraphDebugSession.cs b/Packages/com.unity.render-pipelines.core/Runtime/RenderGraph/Debug/RenderGraphDebugSession.cs index ddca2c9eff5..4b749c72c59 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/RenderGraph/Debug/RenderGraphDebugSession.cs +++ b/Packages/com.unity.render-pipelines.core/Runtime/RenderGraph/Debug/RenderGraphDebugSession.cs @@ -55,7 +55,7 @@ public List GetExecutions(string graphName) public DebugData GetDebugData(string renderGraph, EntityId executionId) { if (!m_Container.TryGetValue(renderGraph, out var debugDataForGraph)) - throw new InvalidOperationException(); + throw new InvalidOperationException($"RenderGraph '{renderGraph}' was never registered with the debug session."); return debugDataForGraph[executionId]; } @@ -149,6 +149,17 @@ protected void InvalidateData() s_CurrentDebugSession = new TSession(); } + // internal: Required in test + internal static void Create(Type sessionType) + { + EndSession(); + + if (sessionType.IsAssignableFrom(typeof(RenderGraphDebugSession))) + throw new ArgumentException("Incorrect session type. Type should be derived from RenderGraphDebugSession."); + + s_CurrentDebugSession = Activator.CreateInstance(sessionType) as RenderGraphDebugSession; + } + public static void EndSession() { if (s_CurrentDebugSession != null) diff --git a/Packages/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraph.cs b/Packages/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraph.cs index 8e1bb369c73..0942f33f687 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraph.cs +++ b/Packages/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraph.cs @@ -118,6 +118,7 @@ public class InternalRenderGraphContext internal RenderGraphPass executingPass; internal NativeRenderPassCompiler.CompilerContextData compilerContext; internal bool contextlessTesting; + internal bool forceResourceCreation; } // InternalRenderGraphContext is public (but all members are internal) @@ -355,7 +356,7 @@ public partial class RenderGraph public bool nativeRenderPassesEnabled { get; set; } = true; internal/*for tests*/ RenderGraphResourceRegistry m_Resources; - RenderGraphObjectPool m_RenderGraphPool = new RenderGraphObjectPool(); + internal/*for tests*/ RenderGraphObjectPool m_RenderGraphPool = new RenderGraphObjectPool(); RenderGraphBuilders m_builderInstance = new RenderGraphBuilders(); internal/*for tests*/ List m_RenderPasses = new List(64); List m_RendererLists = new List(32); @@ -1227,6 +1228,15 @@ public void BeginRecording(in RenderGraphParameters parameters) m_RenderGraphContext.contextlessTesting = parameters.invalidContextForTesting; m_RenderGraphContext.renderGraphPool = m_RenderGraphPool; m_RenderGraphContext.defaultResources = m_DefaultResources; + + // With the actual implementation of the Frame Debugger, we cannot re-use resources during the same frame + // or it breaks the rendering of the pass preview, since the FD copies the texture after the execution of the RG. + m_RenderGraphContext.forceResourceCreation = +#if UNITY_EDITOR || DEVELOPMENT_BUILD + FrameDebugger.enabled; +#else + false; +#endif } /// diff --git a/Packages/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphResourceRegistry.cs b/Packages/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphResourceRegistry.cs index ac077b12d18..a1cb9aebe0c 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphResourceRegistry.cs +++ b/Packages/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphResourceRegistry.cs @@ -1010,7 +1010,7 @@ internal bool CreatePooledResource(InternalRenderGraphContext rgContext, int typ var resource = m_RenderGraphResources[type].resourceArray[index]; if (!resource.imported) { - resource.CreatePooledGraphicsResource(); + resource.CreatePooledGraphicsResource(rgContext.forceResourceCreation); executedWork = m_RenderGraphResources[type].createResourceCallback?.Invoke(rgContext, resource); } diff --git a/Packages/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphResources.cs b/Packages/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphResources.cs index 5247638039d..4f93f130107 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphResources.cs +++ b/Packages/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphResources.cs @@ -183,7 +183,7 @@ public virtual bool NeedsFallBack() return requestFallBack && writeCount == 0; } - public virtual void CreatePooledGraphicsResource() { } + public virtual void CreatePooledGraphicsResource(bool forceResourceCreation) { } public virtual void CreateGraphicsResource() { } public virtual void UpdateGraphicsResource() { } public virtual void ReleasePooledGraphicsResource(int frameIndex) { } @@ -229,7 +229,7 @@ public override void ReleaseGraphicsResource() graphicsResource = null; } - public override void CreatePooledGraphicsResource() + public override void CreatePooledGraphicsResource(bool forceResourceCreation) { Debug.Assert(m_Pool != null, "RenderGraphResource: CreatePooledGraphicsResource should only be called for regular pooled resources"); @@ -240,7 +240,7 @@ public override void CreatePooledGraphicsResource() // If the pool doesn't have any available resource that we can use, we will create one // In any case, we will update the graphicsResource name based on the RenderGraph resource name - if (!m_Pool.TryGetResource(hashCode, out graphicsResource)) + if (forceResourceCreation || !m_Pool.TryGetResource(hashCode, out graphicsResource)) { CreateGraphicsResource(); } diff --git a/Packages/com.unity.render-pipelines.core/Runtime/RenderPipelineResources/GPUDriven/DebugOcclusionTest.shader b/Packages/com.unity.render-pipelines.core/Runtime/RenderPipelineResources/GPUDriven/DebugOcclusionTest.shader index ac340d0bd4e..cc0112dc306 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/RenderPipelineResources/GPUDriven/DebugOcclusionTest.shader +++ b/Packages/com.unity.render-pipelines.core/Runtime/RenderPipelineResources/GPUDriven/DebugOcclusionTest.shader @@ -22,9 +22,9 @@ Shader "Hidden/Core/DebugOcclusionTest" #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Debug.hlsl" #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/TextureXR.hlsl" #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/UnityInstancing.hlsl" - #include "Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceOcclusionCuller.cs.hlsl" - #include "Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/OcclusionCullingDebugShaderVariables.cs.hlsl" - #include "Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/OcclusionCullingCommon.cs.hlsl" + #include "Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/InstanceOcclusionCuller.cs.hlsl" + #include "Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/OcclusionCullingDebugShaderVariables.cs.hlsl" + #include "Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/OcclusionCullingCommon.cs.hlsl" StructuredBuffer _OcclusionDebugOverlay; diff --git a/Packages/com.unity.render-pipelines.core/Runtime/RenderPipelineResources/GPUDriven/InstanceDataBufferCopyKernels.compute b/Packages/com.unity.render-pipelines.core/Runtime/RenderPipelineResources/GPUDriven/InstanceDataBufferCopyKernels.compute index ff00b8a3567..db743061cf0 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/RenderPipelineResources/GPUDriven/InstanceDataBufferCopyKernels.compute +++ b/Packages/com.unity.render-pipelines.core/Runtime/RenderPipelineResources/GPUDriven/InstanceDataBufferCopyKernels.compute @@ -1,49 +1,105 @@ -#pragma exclude_renderers gles - #pragma kernel MainCopyInstances +#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl" +#include "Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/GPUInstanceDataBuffer.cs.hlsl" + //#pragma enable_d3d11_debug_symbols -int _InputValidComponentCounts; -int _InstanceOffset; -int _InstanceCounts; -int _OutputInstanceOffset; -ByteAddressBuffer _ValidComponentIndices; -ByteAddressBuffer _ComponentByteCounts; -ByteAddressBuffer _InputComponentAddresses; -ByteAddressBuffer _InputComponentInstanceIndexRanges; -ByteAddressBuffer _OutputComponentAddresses; -ByteAddressBuffer _OutputComponentInstanceIndexRanges; +static const uint kThreadGroupSize = GPUINSTANCEDATABUFFERCONSTANTS_THREAD_GROUP_SIZE; +static const uint kUIntPerThread = GPUINSTANCEDATABUFFERCONSTANTS_UINT_PER_THREAD; +static const uint kUIntPerThreadGroup = GPUINSTANCEDATABUFFERCONSTANTS_UINT_PER_THREAD_GROUP; +static const uint kMaxThreadGroupsPerDispatch = GPUINSTANCEDATABUFFERCONSTANTS_MAX_THREAD_GROUPS_PER_DISPATCH; + +int _DispatchThreadGroupBase; +int _InputComponentsCount; + +StructuredBuffer _InputThreadGroupBeginIndices; +StructuredBuffer _InputComponentDataAddresses; +StructuredBuffer _OutputComponentDataAddresses; +StructuredBuffer _OutputComponentDataUIntSizes; + ByteAddressBuffer _InputBuffer; RWByteAddressBuffer _OutputBuffer; -[numthreads(64,1,1)] -void MainCopyInstances(uint3 dispatchThreadID : SV_DispatchThreadID) +//@ Check performance. +uint BinarySearchComponentDataIndex(uint globalGroupIndex, out uint2 groupRange) { - if (dispatchThreadID.x >= (uint) _InstanceCounts) - return; - - uint instanceIndex = _InstanceOffset + dispatchThreadID.x; - uint instanceOutputIndex = _OutputInstanceOffset + dispatchThreadID.x; - - [loop] - for (int validComponentIndex = 0; validComponentIndex < _InputValidComponentCounts; ++validComponentIndex) + uint left = 0; + uint right = _InputComponentsCount - 1; + uint index = 0; + + while (left <= right) { - uint componentIndex = _ValidComponentIndices.Load(validComponentIndex << 2); - uint instanceInputIndexBegin = _InputComponentInstanceIndexRanges.Load(componentIndex * 8); - uint instanceInputIndexEnd = _InputComponentInstanceIndexRanges.Load(componentIndex * 8 + 4); - if (instanceIndex < instanceInputIndexBegin || instanceIndex >= instanceInputIndexEnd) - continue; - uint inputComponentAddress = _InputComponentAddresses.Load(componentIndex << 2); - uint outputComponentAddress = _OutputComponentAddresses.Load(componentIndex << 2); - uint componentByteSize = _ComponentByteCounts.Load(componentIndex << 2); - for (uint uintIndex = 0; uintIndex < componentByteSize / 4u; ++uintIndex) + index = (left + right) / 2; + groupRange.x = _InputThreadGroupBeginIndices[index]; + groupRange.y = _InputThreadGroupBeginIndices[index + 1]; + if (globalGroupIndex >= groupRange.x && globalGroupIndex < groupRange.y) + break; + else if (globalGroupIndex < groupRange.x) + right = index - 1; + else + left = index + 1; + } + + return index; +} + +[numthreads(kThreadGroupSize, 1, 1)] +void MainCopyInstances(uint groupThreadID : SV_GroupThreadID, uint groupID : SV_GroupID) +{ + uint globalGroupIndex = _DispatchThreadGroupBase + groupID; + + uint2 groupRange; + uint index = BinarySearchComponentDataIndex(globalGroupIndex, groupRange); + uint inputComponentDataAddress = _InputComponentDataAddresses[index]; + uint outputComponentDataAddress = _OutputComponentDataAddresses[index]; + uint componentDataUIntSize = _OutputComponentDataUIntSizes[index]; + + uint groupOffset = globalGroupIndex - groupRange.x; + uint groupInputDataAddress = inputComponentDataAddress + groupOffset * kUIntPerThreadGroup * 4; + uint groupOutputDataAddress = outputComponentDataAddress + groupOffset * kUIntPerThreadGroup * 4; + + bool isLastGroup = globalGroupIndex == groupRange.y - 1; + + uint localData[kUIntPerThread]; + + if (!isLastGroup) + { + uint i = 0; + + [loop] + for (i = 0; i < kUIntPerThread; ++i) + { + uint threadDataUIntOffset = kThreadGroupSize * i + groupThreadID; + localData[i] = _InputBuffer.Load(groupInputDataAddress + threadDataUIntOffset * 4); + } + [loop] + for (i = 0; i < kUIntPerThread; ++i) + { + uint threadDataUIntOffset = kThreadGroupSize * i + groupThreadID; + _OutputBuffer.Store(groupOutputDataAddress + threadDataUIntOffset * 4, localData[i]); + } + } + else + { + uint i = 0; + uint groupDataUIntSize = min(componentDataUIntSize - groupOffset * kUIntPerThreadGroup, kUIntPerThreadGroup); + + [loop] + for (i = 0; i < kUIntPerThread; ++i) + { + uint threadDataUIntOffset = kThreadGroupSize * i + groupThreadID; + if (threadDataUIntOffset >= groupDataUIntSize) + break; + localData[i] = _InputBuffer.Load(groupInputDataAddress + threadDataUIntOffset * 4); + } + [loop] + for (i = 0; i < kUIntPerThread; ++i) { - uint byteOffsetInput = instanceIndex * componentByteSize + uintIndex * 4; - uint byteOffsetOutput = instanceOutputIndex * componentByteSize + uintIndex * 4; - uint inputAddress = inputComponentAddress + byteOffsetInput; - uint outputAddress = outputComponentAddress + byteOffsetOutput; - _OutputBuffer.Store(outputAddress, _InputBuffer.Load(inputAddress)); + uint threadDataUIntOffset = kThreadGroupSize * i + groupThreadID; + if (threadDataUIntOffset >= groupDataUIntSize) + break; + _OutputBuffer.Store(groupOutputDataAddress + threadDataUIntOffset * 4, localData[i]); } } } diff --git a/Packages/com.unity.render-pipelines.core/Runtime/RenderPipelineResources/GPUDriven/InstanceDataBufferUploadKernels.compute b/Packages/com.unity.render-pipelines.core/Runtime/RenderPipelineResources/GPUDriven/InstanceDataBufferUploadKernels.compute index 7dd5c54578d..a695d2d6f00 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/RenderPipelineResources/GPUDriven/InstanceDataBufferUploadKernels.compute +++ b/Packages/com.unity.render-pipelines.core/Runtime/RenderPipelineResources/GPUDriven/InstanceDataBufferUploadKernels.compute @@ -1,45 +1,51 @@ -#pragma exclude_renderers gles - #pragma kernel MainUploadScatterInstances //#pragma enable_d3d11_debug_symbols -int _InputValidComponentCounts; -int _InputInstanceCounts; -int _InputInstanceByteSize; +int _InputComponentsCount; +int _InputInstancesCount; ByteAddressBuffer _InputInstanceData; ByteAddressBuffer _InputInstanceIndices; -ByteAddressBuffer _InputComponentOffsets; -ByteAddressBuffer _InputValidComponentIndices; ByteAddressBuffer _InputComponentAddresses; -ByteAddressBuffer _InputComponentByteCounts; -ByteAddressBuffer _InputComponentInstanceIndexRanges; +ByteAddressBuffer _OutputComponentByteCounts; +ByteAddressBuffer _OutputComponentIndices; +ByteAddressBuffer _OutputComponentInstanceIndexRanges; +ByteAddressBuffer _OutputComponentIsPerInstance; +ByteAddressBuffer _OutputComponentAddresses; RWByteAddressBuffer _OutputBuffer; -[numthreads(64,1,1)] +[numthreads(64, 1, 1)] void MainUploadScatterInstances(uint3 dispatchThreadID : SV_DispatchThreadID) { - uint instanceOffset = dispatchThreadID.x; - if (instanceOffset >= (uint)_InputInstanceCounts) + uint inputInstanceIndex = dispatchThreadID.x; + + if (inputInstanceIndex >= (uint) _InputInstancesCount) return; - uint instanceIndex = _InputInstanceIndices.Load(instanceOffset << 2); + uint outputInstanceIndex = _InputInstanceIndices.Load(inputInstanceIndex << 2); [loop] - for (int validComponentIndex = 0; validComponentIndex < _InputValidComponentCounts; ++validComponentIndex) + for (int inputComponent = 0; inputComponent < _InputComponentsCount; ++inputComponent) { - uint componentIndex = _InputValidComponentIndices.Load(validComponentIndex << 2); - uint instanceIndexBegin = _InputComponentInstanceIndexRanges.Load(componentIndex * 8); - uint instanceIndexEnd = _InputComponentInstanceIndexRanges.Load(componentIndex * 8 + 4); - if (instanceIndex < instanceIndexBegin || instanceIndex >= instanceIndexEnd) + uint outputComponent = _OutputComponentIndices.Load(inputComponent << 2); + uint instanceIndexRangeBegin = _OutputComponentInstanceIndexRanges.Load(outputComponent * 8); + uint instanceIndexRangeEnd = _OutputComponentInstanceIndexRanges.Load(outputComponent * 8 + 4); + + // Not every instance has all the components. + if (outputInstanceIndex < instanceIndexRangeBegin || outputInstanceIndex >= instanceIndexRangeEnd) continue; - uint inputComponentOffset = _InputComponentOffsets.Load(componentIndex << 2); - uint componentAddress = _InputComponentAddresses.Load(componentIndex << 2); - uint componentByteSize = _InputComponentByteCounts.Load(componentIndex << 2); + + uint inputComponentAddress = _InputComponentAddresses.Load(inputComponent << 2); + uint componentByteSize = _OutputComponentByteCounts.Load(outputComponent << 2); + uint outputComponentAddress = _OutputComponentAddresses.Load(outputComponent << 2); + + // Some components are shared between all instances. + uint isPerInstanceComponent = _OutputComponentIsPerInstance.Load(outputComponent << 2); + + uint inputAddress = inputComponentAddress + inputInstanceIndex * componentByteSize * isPerInstanceComponent; + uint outputAddress = outputComponentAddress + outputInstanceIndex * componentByteSize * isPerInstanceComponent; + for (uint uintIndex = 0; uintIndex < componentByteSize / 4u; ++uintIndex) - { - uint outputAddress = componentAddress + instanceIndex * componentByteSize + uintIndex * 4; - _OutputBuffer.Store(outputAddress, _InputInstanceData.Load(instanceOffset * _InputInstanceByteSize + (inputComponentOffset + uintIndex) * 4)); - } + _OutputBuffer.Store(outputAddress + uintIndex * 4, _InputInstanceData.Load(inputAddress + uintIndex * 4)); } } diff --git a/Packages/com.unity.render-pipelines.core/Runtime/RenderPipelineResources/GPUDriven/InstanceOcclusionCullingKernels.compute b/Packages/com.unity.render-pipelines.core/Runtime/RenderPipelineResources/GPUDriven/InstanceOcclusionCullingKernels.compute index 6c9fc7378a6..7fec91dc9af 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/RenderPipelineResources/GPUDriven/InstanceOcclusionCullingKernels.compute +++ b/Packages/com.unity.render-pipelines.core/Runtime/RenderPipelineResources/GPUDriven/InstanceOcclusionCullingKernels.compute @@ -1,16 +1,14 @@ -#pragma exclude_renderers gles - #pragma kernel ResetDrawArgs #pragma kernel CopyInstances #pragma kernel CullInstances #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl" -#include "Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceOcclusionCuller.cs.hlsl" -#include "Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceOcclusionCullerShaderVariables.cs.hlsl" +#include "Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/InstanceOcclusionCuller.cs.hlsl" +#include "Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/InstanceOcclusionCullerShaderVariables.cs.hlsl" #pragma multi_compile _ USE_ARRAY #pragma multi_compile _ OCCLUSION_DEBUG -#include "Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/OcclusionCullingCommon.hlsl" +#include "Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/OcclusionCullingCommon.hlsl" #pragma multi_compile _ OCCLUSION_FIRST_PASS OCCLUSION_SECOND_PASS diff --git a/Packages/com.unity.render-pipelines.core/Runtime/RenderPipelineResources/GPUDriven/InstanceTransformUpdateKernels.compute b/Packages/com.unity.render-pipelines.core/Runtime/RenderPipelineResources/GPUDriven/InstanceTransformUpdateKernels.compute index 1e3276abd6f..676be9294a3 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/RenderPipelineResources/GPUDriven/InstanceTransformUpdateKernels.compute +++ b/Packages/com.unity.render-pipelines.core/Runtime/RenderPipelineResources/GPUDriven/InstanceTransformUpdateKernels.compute @@ -1,5 +1,5 @@ #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl" -#include "Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceTransformUpdateDefs.cs.hlsl" +#include "Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/InstanceTransformUpdateDefs.cs.hlsl" #pragma kernel ScatterInitTransformMain #pragma kernel ScatterUpdateTransformMain diff --git a/Packages/com.unity.render-pipelines.core/Runtime/RenderPipelineResources/GPUDriven/InstanceWindDataUpdateKernels.compute b/Packages/com.unity.render-pipelines.core/Runtime/RenderPipelineResources/GPUDriven/InstanceWindDataUpdateKernels.compute index bd11857d7f0..12ac2d46877 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/RenderPipelineResources/GPUDriven/InstanceWindDataUpdateKernels.compute +++ b/Packages/com.unity.render-pipelines.core/Runtime/RenderPipelineResources/GPUDriven/InstanceWindDataUpdateKernels.compute @@ -1,5 +1,5 @@ #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl" -#include "Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceWindDataUpdateDefs.cs.hlsl" +#include "Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/CoreDataSystems/InstanceWindDataUpdateDefs.cs.hlsl" #pragma kernel WindDataCopyHistoryMain diff --git a/Packages/com.unity.render-pipelines.core/Runtime/RenderPipelineResources/GPUDriven/OccluderDepthPyramidKernels.compute b/Packages/com.unity.render-pipelines.core/Runtime/RenderPipelineResources/GPUDriven/OccluderDepthPyramidKernels.compute index 4a2c3dce7a8..3d1df37f8dc 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/RenderPipelineResources/GPUDriven/OccluderDepthPyramidKernels.compute +++ b/Packages/com.unity.render-pipelines.core/Runtime/RenderPipelineResources/GPUDriven/OccluderDepthPyramidKernels.compute @@ -3,8 +3,8 @@ #pragma only_renderers d3d11 playstation xboxone xboxseries vulkan metal switch switch2 webgpu #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl" -#include "Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/OccluderDepthPyramidConstants.cs.hlsl" -#include "Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/OcclusionTestCommon.hlsl" +#include "Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/OccluderDepthPyramidConstants.cs.hlsl" +#include "Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/OcclusionTestCommon.hlsl" #pragma multi_compile _ USE_SRC #pragma multi_compile _ SRC_IS_ARRAY diff --git a/Packages/com.unity.render-pipelines.core/Runtime/RenderPipelineResources/GPUDriven/OcclusionCullingDebug.compute b/Packages/com.unity.render-pipelines.core/Runtime/RenderPipelineResources/GPUDriven/OcclusionCullingDebug.compute index c201535fc54..96550f9a6b3 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/RenderPipelineResources/GPUDriven/OcclusionCullingDebug.compute +++ b/Packages/com.unity.render-pipelines.core/Runtime/RenderPipelineResources/GPUDriven/OcclusionCullingDebug.compute @@ -4,7 +4,7 @@ #define OCCLUSION_DEBUG 1 -#include "Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/OcclusionCullingCommon.hlsl" +#include "Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/Culling/OcclusionCullingCommon.hlsl" [numthreads(8, 8, 1)] void ClearOcclusionDebug(uint2 dispatchIndex : SV_DispatchThreadID, uint3 groupId : SV_GroupID) diff --git a/Packages/com.unity.render-pipelines.core/Runtime/Vrs/VrsRenderPipelineRuntimeResources.cs b/Packages/com.unity.render-pipelines.core/Runtime/Vrs/VrsRenderPipelineRuntimeResources.cs index f1f02491c40..9fe8aa1ddb9 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/Vrs/VrsRenderPipelineRuntimeResources.cs +++ b/Packages/com.unity.render-pipelines.core/Runtime/Vrs/VrsRenderPipelineRuntimeResources.cs @@ -7,7 +7,7 @@ namespace UnityEngine.Rendering /// [Serializable] [SupportedOnRenderPipeline] - [Categorization.CategoryInfo(Name = "R: VRS - Runtime Resources", Order = 1000)] + [Categorization.CategoryInfo(Name = "VRS - Runtime Resources", Order = 1000)] public sealed class VrsRenderPipelineRuntimeResources : IRenderPipelineResources { /// @@ -18,6 +18,7 @@ public sealed class VrsRenderPipelineRuntimeResources : IRenderPipelineResources bool IRenderPipelineGraphicsSettings.isAvailableInPlayerBuild => true; [SerializeField] + [Tooltip("Compute shader used for converting textures to shading rate values")] [ResourcePath("Runtime/Vrs/Shaders/VrsTexture.compute")] ComputeShader m_TextureComputeShader; @@ -31,6 +32,7 @@ public ComputeShader textureComputeShader } [SerializeField] + [Tooltip("Shader used when visualizing shading rate values as a color image")] [ResourcePath("Runtime/Vrs/Shaders/VrsVisualization.shader")] Shader m_VisualizationShader; diff --git a/Packages/com.unity.render-pipelines.core/Tests/Editor/GPUDriven/GPUDrivenInstanceDataTests.cs b/Packages/com.unity.render-pipelines.core/Tests/Editor/GPUDriven/GPUDrivenInstanceDataTests.cs new file mode 100644 index 00000000000..e3ac186a4bc --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Tests/Editor/GPUDriven/GPUDrivenInstanceDataTests.cs @@ -0,0 +1,942 @@ +using NUnit.Framework; +using NUnit.Framework.Internal; +using System; +using System.Linq; +using Unity.Collections; +using Unity.Collections.LowLevel.Unsafe; +using UnityEngine.TestTools; + +#if UNITY_EDITOR +using UnityEditor.Rendering; +#endif + +namespace UnityEngine.Rendering.Tests +{ + internal class GPUDrivenInstanceDataTests + { + private CommandBuffer m_Cmd; + private GPUArchetypeManager m_ArchetypeMgr; + private RenderPassTest m_RenderPipe; + private RenderPipelineAsset m_OldPipelineAsset; + private GPUResidentDrawerResources m_Resources; + private RenderPassGlobalSettings m_GlobalSettings; + + [OneTimeSetUp] + public void OneTimeSetup() + { + m_GlobalSettings = ScriptableObject.CreateInstance(); +#if UNITY_EDITOR + EditorGraphicsSettings.SetRenderPipelineGlobalSettingsAsset(m_GlobalSettings); +#endif + } + + [OneTimeTearDown] + public void OneTimeTearDown() + { +#if UNITY_EDITOR + EditorGraphicsSettings.SetRenderPipelineGlobalSettingsAsset(null); +#endif + Object.DestroyImmediate(m_GlobalSettings); + } + + [SetUp] + public void OnSetup() + { + m_Cmd = new CommandBuffer(); + m_ArchetypeMgr.Initialize(); + m_RenderPipe = ScriptableObject.CreateInstance(); + m_OldPipelineAsset = GraphicsSettings.defaultRenderPipeline; + GraphicsSettings.defaultRenderPipeline = m_RenderPipe; + m_Resources = GraphicsSettings.GetRenderPipelineSettings(); + } + + [TearDown] + public void OnTearDown() + { + m_Resources = null; + GraphicsSettings.defaultRenderPipeline = m_OldPipelineAsset; + m_OldPipelineAsset = null; + m_RenderPipe = null; + m_ArchetypeMgr.Dispose(); + m_ArchetypeMgr = default; + m_Cmd.Dispose(); + m_Cmd = null; + } + + [Test, ConditionalIgnore("IgnoreGfxAPI", "Graphics API Not Supported.")] + public unsafe void TestGPUComponentSet() + { + for(int i = 0; i < GPUArchetypeManager.kMaxComponentsCount; ++i) + m_ArchetypeMgr.CreateComponent(Shader.PropertyToID($"Component{i}"), isPerInstance: true); + + var components = new NativeList(Allocator.Temp); + components.Add(m_ArchetypeMgr.FindComponent(Shader.PropertyToID($"Component0"))); + components.Add(m_ArchetypeMgr.FindComponent(Shader.PropertyToID($"Component3"))); + components.Add(m_ArchetypeMgr.FindComponent(Shader.PropertyToID($"Component6"))); + components.Add(m_ArchetypeMgr.FindComponent(Shader.PropertyToID($"Component18"))); + components.Add(m_ArchetypeMgr.FindComponent(Shader.PropertyToID($"Component24"))); + components.Add(m_ArchetypeMgr.FindComponent(Shader.PropertyToID($"Component45"))); + components.Add(m_ArchetypeMgr.FindComponent(Shader.PropertyToID($"Component63"))); + + var componentSet = new GPUComponentSet(components.AsArray()); + Assert.IsTrue(componentSet.GetComponentsCount() == components.Length); + + for (int i = 0; i < components.Length; ++i) + Assert.AreEqual(componentSet.GetComponentByIndex(i), components[i]); + + var archetype = m_ArchetypeMgr.CreateArchetype(componentSet); + var archetypeComponents = m_ArchetypeMgr.GetArchetypeDesc(archetype).components; + Assert.IsTrue(archetypeComponents.ArraysEqual(components)); + } + + [Test, ConditionalIgnore("IgnoreGfxAPI", "Graphics API Not Supported.")] + public unsafe void TestInstanceUploadDataSimple() + { + const int instancesCount = 16; + + var component = m_ArchetypeMgr.CreateComponent(Shader.PropertyToID("Vector4"), isPerInstance: true); + var components = new NativeList(Allocator.Temp) { component }; + + using (var upload = new GPUInstanceUploadData(ref m_ArchetypeMgr, components.AsArray(), instancesCount, Allocator.Temp)) + { + var writeBuffer = new NativeArray(upload.uploadDataUIntSize, Allocator.TempJob); + + var componentData = new NativeArray(instancesCount, Allocator.TempJob); + for (int i = 0; i < instancesCount; ++i) + componentData[i] = new Vector4(i, i, i, i); + upload.ScheduleWriteComponentsJob(componentData, component, writeBuffer).Complete(); + + uint* writeBufferPtr = (uint*)writeBuffer.GetUnsafePtr(); + + Assert.AreEqual(upload.uploadDataUIntSize, instancesCount * 4); + + for (int i = 0; i < instancesCount; ++i) + { + Vector4* data = ((Vector4*)writeBufferPtr) + i; + Assert.AreEqual(*data, new Vector4(i, i, i, i)); + } + + componentData.Dispose(); + writeBuffer.Dispose(); + } + } + + [Test, ConditionalIgnore("IgnoreGfxAPI", "Graphics API Not Supported.")] + public unsafe void TestInstanceUploadDataMultiple() + { + var uintComponent = m_ArchetypeMgr.CreateComponent(Shader.PropertyToID("UInt"), isPerInstance: true); + var floatComponent = m_ArchetypeMgr.CreateComponent(Shader.PropertyToID("Float"), isPerInstance: true); + var vecComponent = m_ArchetypeMgr.CreateComponent(Shader.PropertyToID("Vector4"), isPerInstance: true); + + var components = new NativeList(Allocator.Temp) + { + uintComponent, + floatComponent, + vecComponent + }; + + const int instancesCount = 31; + + using (var upload = new GPUInstanceUploadData(ref m_ArchetypeMgr, components.AsArray(), instancesCount, Allocator.Temp)) + { + var writeBuffer = new NativeArray(upload.uploadDataUIntSize, Allocator.TempJob); + var uintData = new NativeArray(instancesCount, Allocator.TempJob); + var floatData = new NativeArray(instancesCount, Allocator.TempJob); + var vecData = new NativeArray(instancesCount, Allocator.TempJob); + for (int i = 0; i < instancesCount; ++i) + { + uintData[i] = (uint)i; + floatData[i] = i; + vecData[i] = new Vector4(i, i, i, i); + } + upload.ScheduleWriteComponentsJob(uintData, uintComponent, writeBuffer).Complete(); + upload.ScheduleWriteComponentsJob(floatData, floatComponent, writeBuffer).Complete(); + upload.ScheduleWriteComponentsJob(vecData, vecComponent, writeBuffer).Complete(); + + uint* bufferPtr = (uint*)writeBuffer.GetUnsafePtr(); + Assert.AreEqual(upload.uploadDataUIntSize, instancesCount * 6); + + for (int i = 0; i < instancesCount; ++i) + { + uint* uintComp = bufferPtr + i; + float* floatComp = (float*)(bufferPtr + instancesCount) + i; + Vector4* vecComp = (Vector4*)(bufferPtr + instancesCount + instancesCount) + i; + Assert.AreEqual(*uintComp, i); + Assert.AreEqual(*floatComp, i); + Assert.AreEqual(*vecComp, new Vector4(i, i, i, i)); + } + + uintData.Dispose(); + floatData.Dispose(); + vecData.Dispose(); + writeBuffer.Dispose(); + } + } + + [Test, ConditionalIgnore("IgnoreGfxAPI", "Graphics API Not Supported.")] + public unsafe void TestInstanceUploadDataNonPerInstanceHasOneElement() + { + const int instancesCount = 4; + + var singleComponent = m_ArchetypeMgr.CreateComponent(Shader.PropertyToID("Vector4Single"), isPerInstance: false); + var perInstanceComponent = m_ArchetypeMgr.CreateComponent(Shader.PropertyToID("Vector4PerInstance"), isPerInstance: true); + var components = new NativeList(Allocator.Temp) + { + singleComponent, + perInstanceComponent + }; + + using (var upload = new GPUInstanceUploadData(ref m_ArchetypeMgr, components.AsArray(), instancesCount, Allocator.Temp)) + { + var writeBuffer = new NativeArray(upload.uploadDataUIntSize, Allocator.TempJob); + var singleComponentData = new NativeArray(1, Allocator.TempJob); + var perInstanceComponentData = new NativeArray(instancesCount, Allocator.TempJob); + singleComponentData[0] = new Vector4(1.0f, 2.0f, 3.0f, 4.0f); + for (int i = 0; i < instancesCount; ++i) + perInstanceComponentData[i] = new Vector4(i, i, i, i); + upload.ScheduleWriteComponentsJob(singleComponentData, singleComponent, writeBuffer).Complete(); + upload.ScheduleWriteComponentsJob(perInstanceComponentData, perInstanceComponent, writeBuffer).Complete(); + + uint* writeBufferPtr = (uint*)writeBuffer.GetUnsafePtr(); + Assert.AreEqual(upload.uploadDataUIntSize, (1 + instancesCount) * 4); + + Vector4* singleData = ((Vector4*)writeBufferPtr); + Assert.AreEqual(*singleData, new Vector4(1.0f, 2.0f, 3.0f, 4.0f)); + + for (int i = 0; i < instancesCount; ++i) + { + Vector4* perInstanceData = ((Vector4*)writeBufferPtr) + 1 + i; + Assert.AreEqual(*perInstanceData, new Vector4(i, i, i, i)); + } + + singleComponentData.Dispose(); + perInstanceComponentData.Dispose(); + writeBuffer.Dispose(); + } + } + + [Test, ConditionalIgnore("IgnoreGfxAPI", "Graphics API Not Supported.")] + public void TestInstanceDataBufferUploadSimple() + { + const int count = 16; + + var comp = m_ArchetypeMgr.CreateComponent(Shader.PropertyToID("Vector"), isPerInstance: true); + var comps = new NativeList(Allocator.Temp) { comp }; + var arch = m_ArchetypeMgr.CreateArchetype(new GPUComponentSet(comps.AsArray())); + + var buffer = new GPUInstanceDataBuffer(ref m_ArchetypeMgr, new GPUInstanceDataBufferLayout { { arch, count } }, m_Resources); + var upload = new GPUInstanceUploadData(ref m_ArchetypeMgr, comps.AsArray(), count, Allocator.Temp); + + var compData = new NativeArray(count, Allocator.TempJob); + for (int i = 0; i < count; i++) + compData[i] = new Vector4(i, i, i, i); + + GraphicsBuffer uploadBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Raw, + GraphicsBuffer.UsageFlags.LockBufferForWrite, + upload.uploadDataUIntSize, + sizeof(uint)); + + var writeBuffer = uploadBuffer.LockBufferForWrite(0, upload.uploadDataUIntSize); + upload.ScheduleWriteComponentsJob(compData, comp, writeBuffer).Complete(); + uploadBuffer.UnlockBufferAfterWrite(upload.uploadDataUIntSize); + + var archIndex = buffer.GetArchetypeIndex(arch); + + var gpuIndices = new NativeArray(count, Allocator.Temp); + for (int i = 0; i < count; i++) + gpuIndices[i] = buffer.InstanceToGPUIndex(archIndex, i); + + buffer.UploadDataToGPU(m_Cmd, uploadBuffer, upload, gpuIndices); + + var readback = new GPUInstanceDataBufferReadback(); + Assert.IsTrue(readback.Load(m_Cmd, buffer)); + + for (int i = 0; i < count; i++) + { + Vector4 element = readback.LoadData(comp, gpuIndices[i]); + Assert.AreEqual(element, new Vector4(i, i, i, i)); + } + + uploadBuffer.Release(); + upload.Dispose(); + compData.Dispose(); + buffer.Dispose(); + readback.Dispose(); + } + + [Test, ConditionalIgnore("IgnoreGfxAPI", "Graphics API Not Supported.")] + public void TestInstanceDataBufferUploadMultiple() + { + const int count = 32; + const int salt = 31; + + var compVector = m_ArchetypeMgr.CreateComponent(Shader.PropertyToID("Vector"), isPerInstance: true); + var compInt = m_ArchetypeMgr.CreateComponent(Shader.PropertyToID("Int"), isPerInstance: true); + var comps = new NativeList(Allocator.Temp) { compVector, compInt }; + var arch = m_ArchetypeMgr.CreateArchetype(new GPUComponentSet(comps.AsArray())); + + var buffer = new GPUInstanceDataBuffer(ref m_ArchetypeMgr, new GPUInstanceDataBufferLayout { { arch, count } }, m_Resources); + var upload = new GPUInstanceUploadData(ref m_ArchetypeMgr, comps.AsArray(), count, Allocator.Temp); + + var compVectorData = new NativeArray(count, Allocator.TempJob); + var compIntData = new NativeArray(count, Allocator.TempJob); + for (int i = 0; i < count; i++) + { + compVectorData[i] = new Vector4(i, i, i, i); + compIntData[i] = i + salt; + } + + GraphicsBuffer uploadBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Raw, + GraphicsBuffer.UsageFlags.LockBufferForWrite, + upload.uploadDataUIntSize, + sizeof(uint)); + + var writeBuffer = uploadBuffer.LockBufferForWrite(0, upload.uploadDataUIntSize); + { + upload.ScheduleWriteComponentsJob(compVectorData, compVector, writeBuffer).Complete(); + upload.ScheduleWriteComponentsJob(compIntData, compInt, writeBuffer).Complete(); + } + uploadBuffer.UnlockBufferAfterWrite(upload.uploadDataUIntSize); + + var archIndex = buffer.GetArchetypeIndex(arch); + + var gpuIndices = new NativeArray(count, Allocator.Temp); + for (int i = 0; i < count; i++) + gpuIndices[i] = buffer.InstanceToGPUIndex(archIndex, i); + + buffer.UploadDataToGPU(m_Cmd, uploadBuffer, upload, gpuIndices); + + var readback = new GPUInstanceDataBufferReadback(); + Assert.IsTrue(readback.Load(m_Cmd, buffer)); + + for (int i = 0; i < count; i++) + { + Vector4 elementVec = readback.LoadData(compVector, gpuIndices[i]); + int elementInt = readback.LoadData(compInt, gpuIndices[i]); + Assert.AreEqual(elementVec, new Vector4(i, i, i, i)); + Assert.AreEqual(elementInt, i + salt); + } + + uploadBuffer.Release(); + upload.Dispose(); + compVectorData.Dispose(); + compIntData.Dispose(); + buffer.Dispose(); + readback.Dispose(); + } + + [Test, ConditionalIgnore("IgnoreGfxAPI", "Graphics API Not Supported.")] + public unsafe void TestInstanceDataBufferNonPerInstanceComponentHasOneElement() + { + const int count = 32; + const float floatSingle = 999.0f; + + var compVector = m_ArchetypeMgr.CreateComponent(Shader.PropertyToID("Vector"), isPerInstance: true); + var compInt = m_ArchetypeMgr.CreateComponent(Shader.PropertyToID("Int"), isPerInstance: true); + var compFloatSingle = m_ArchetypeMgr.CreateComponent(Shader.PropertyToID("FloatSingle"), isPerInstance: false); + + var comps = new NativeList(Allocator.Temp) { compVector, compFloatSingle, compInt }; + var arch = m_ArchetypeMgr.CreateArchetype(new GPUComponentSet(comps.AsArray())); + + var buffer = new GPUInstanceDataBuffer(ref m_ArchetypeMgr, new GPUInstanceDataBufferLayout { { arch, count } }, m_Resources); + var upload = new GPUInstanceUploadData(ref m_ArchetypeMgr, comps.AsArray(), count, Allocator.Temp); + + var compVectorData = new NativeArray(count, Allocator.TempJob); + var compIntData = new NativeArray(count, Allocator.TempJob); + var compFloatSingleData = new NativeArray(1, Allocator.TempJob); + compFloatSingleData[0] = floatSingle; + for (int i = 0; i < count; i++) + { + compVectorData[i] = new Vector4(i, i, i, i); + compIntData[i] = i; + } + + GraphicsBuffer uploadBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Raw, + GraphicsBuffer.UsageFlags.LockBufferForWrite, + upload.uploadDataUIntSize, + sizeof(uint)); + + var writeBuffer = uploadBuffer.LockBufferForWrite(0, upload.uploadDataUIntSize); + { + upload.ScheduleWriteComponentsJob(compVectorData, compVector, writeBuffer).Complete(); + upload.ScheduleWriteComponentsJob(compIntData, compInt, writeBuffer).Complete(); + upload.ScheduleWriteComponentsJob(compFloatSingleData, compFloatSingle, writeBuffer).Complete(); + } + uploadBuffer.UnlockBufferAfterWrite(upload.uploadDataUIntSize); + + var archIndex = buffer.GetArchetypeIndex(arch); + + var gpuIndices = new NativeArray(count, Allocator.Temp); + for (int i = 0; i < count; i++) + gpuIndices[i] = buffer.InstanceToGPUIndex(archIndex, i); + + buffer.UploadDataToGPU(m_Cmd, uploadBuffer, upload, gpuIndices); + + var readback = new GPUInstanceDataBufferReadback(); + Assert.IsTrue(readback.Load(m_Cmd, buffer)); + + for (int i = 0; i < count; i++) + { + Vector4 elementVec = readback.LoadData(compVector, gpuIndices[i]); + int elementInt = readback.LoadData(compInt, gpuIndices[i]); + float elementFloatSingle = readback.LoadData(compFloatSingle, gpuIndices[i]); + Assert.AreEqual(elementVec, new Vector4(i, i, i, i)); + Assert.AreEqual(elementInt, i); + Assert.AreEqual(elementFloatSingle, floatSingle); + } + + uploadBuffer.Release(); + upload.Dispose(); + compVectorData.Dispose(); + compIntData.Dispose(); + compFloatSingleData.Dispose(); + buffer.Dispose(); + readback.Dispose(); + } + + [Test, ConditionalIgnore("IgnoreGfxAPI", "Graphics API Not Supported.")] + public void TestInstanceDataBufferUploadMultipleArchetypes() + { + const int archCount0 = 31; + const int archCount1 = 15; + const int archCount2 = 7; + + var compVector = m_ArchetypeMgr.CreateComponent(Shader.PropertyToID("Vector"), isPerInstance: true); + var compInt = m_ArchetypeMgr.CreateComponent(Shader.PropertyToID("Int"), isPerInstance: true); + var compFloat = m_ArchetypeMgr.CreateComponent(Shader.PropertyToID("Float"), isPerInstance: true); + var compUInt = m_ArchetypeMgr.CreateComponent(Shader.PropertyToID("UInt"), isPerInstance: true); + + var comps0 = new NativeList(Allocator.Temp) { compVector, compInt }; + var comps1 = new NativeList(Allocator.Temp) { compVector, compInt, compFloat }; + var comps2 = new NativeList(Allocator.Temp) { compUInt }; + + var arch0 = m_ArchetypeMgr.CreateArchetype(new GPUComponentSet(comps0.AsArray())); + var arch1 = m_ArchetypeMgr.CreateArchetype(new GPUComponentSet(comps1.AsArray())); + var arch2 = m_ArchetypeMgr.CreateArchetype(new GPUComponentSet(comps2.AsArray())); + + var buffer = new GPUInstanceDataBuffer(ref m_ArchetypeMgr, new GPUInstanceDataBufferLayout + { + { arch0, archCount0 }, + { arch1, archCount1 }, + { arch2, archCount2 } + }, + m_Resources); + + var archIndex0 = buffer.GetArchetypeIndex(arch0); + var archIndex1 = buffer.GetArchetypeIndex(arch1); + var archIndex2 = buffer.GetArchetypeIndex(arch2); + + { + var archDesc = m_ArchetypeMgr.GetArchetypeDesc(arch0); + var upload = new GPUInstanceUploadData(ref m_ArchetypeMgr, archDesc.components.AsArray(), archCount0, Allocator.Temp); + + var compVectorData = new NativeArray(archCount0, Allocator.TempJob); + var compIntData = new NativeArray(archCount0, Allocator.TempJob); + for (int i = 0; i < archCount0; i++) + { + compVectorData[i] = new Vector4(i, i, i, i); + compIntData[i] = i; + } + + GraphicsBuffer uploadBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Raw, + GraphicsBuffer.UsageFlags.LockBufferForWrite, + upload.uploadDataUIntSize, + sizeof(uint)); + + var writeBuffer = uploadBuffer.LockBufferForWrite(0, upload.uploadDataUIntSize); + { + upload.ScheduleWriteComponentsJob(compVectorData, compVector, writeBuffer).Complete(); + upload.ScheduleWriteComponentsJob(compIntData, compInt, writeBuffer).Complete(); + } + uploadBuffer.UnlockBufferAfterWrite(upload.uploadDataUIntSize); + + var gpuIndices = new NativeArray(archCount0, Allocator.Temp); + for (int i = 0; i < archCount0; i++) + gpuIndices[i] = buffer.InstanceToGPUIndex(archIndex0, i); + + buffer.UploadDataToGPU(m_Cmd, uploadBuffer, upload, gpuIndices); + + uploadBuffer.Release(); + upload.Dispose(); + compVectorData.Dispose(); + compIntData.Dispose(); + } + + { + var archDesc = m_ArchetypeMgr.GetArchetypeDesc(arch1); + var upload = new GPUInstanceUploadData(ref m_ArchetypeMgr, archDesc.components.AsArray(), archCount1, Allocator.Temp); + + var compVectorData = new NativeArray(archCount1, Allocator.TempJob); + var compIntData = new NativeArray(archCount1, Allocator.TempJob); + var compFloatData = new NativeArray(archCount1, Allocator.TempJob); + for (int i = 0; i < archCount1; i++) + { + compVectorData[i] = new Vector4(archCount0 + i, archCount0 + i, archCount0 + i, archCount0 + i); + compIntData[i] = archCount0 + i; + compFloatData[i] = archCount0 + i; + } + + GraphicsBuffer uploadBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Raw, + GraphicsBuffer.UsageFlags.LockBufferForWrite, + upload.uploadDataUIntSize, + sizeof(uint)); + + var writeBuffer = uploadBuffer.LockBufferForWrite(0, upload.uploadDataUIntSize); + { + upload.ScheduleWriteComponentsJob(compVectorData, compVector, writeBuffer).Complete(); + upload.ScheduleWriteComponentsJob(compIntData, compInt, writeBuffer).Complete(); + upload.ScheduleWriteComponentsJob(compFloatData, compFloat, writeBuffer).Complete(); + } + uploadBuffer.UnlockBufferAfterWrite(upload.uploadDataUIntSize); + + var gpuIndices = new NativeArray(archCount1, Allocator.Temp); + for (int i = 0; i < archCount1; i++) + gpuIndices[i] = buffer.InstanceToGPUIndex(archIndex1, i); + + buffer.UploadDataToGPU(m_Cmd, uploadBuffer, upload, gpuIndices); + + uploadBuffer.Release(); + upload.Dispose(); + compVectorData.Dispose(); + compIntData.Dispose(); + compFloatData.Dispose(); + } + + { + var archDesc = m_ArchetypeMgr.GetArchetypeDesc(arch2); + var upload = new GPUInstanceUploadData(ref m_ArchetypeMgr, archDesc.components.AsArray(), archCount2, Allocator.Temp); + + var compUIntData = new NativeArray(archCount2, Allocator.TempJob); + for (int i = 0; i < archCount2; i++) + compUIntData[i] = archCount0 + archCount1 + (uint)i; + + GraphicsBuffer uploadBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Raw, + GraphicsBuffer.UsageFlags.LockBufferForWrite, + upload.uploadDataUIntSize, + sizeof(uint)); + + var writeBuffer = uploadBuffer.LockBufferForWrite(0, upload.uploadDataUIntSize); + upload.ScheduleWriteComponentsJob(compUIntData, compUInt, writeBuffer).Complete(); + uploadBuffer.UnlockBufferAfterWrite(upload.uploadDataUIntSize); + + var gpuIndices = new NativeArray(archCount2, Allocator.Temp); + for (int i = 0; i < archCount2; i++) + gpuIndices[i] = buffer.InstanceToGPUIndex(archIndex2, i); + + buffer.UploadDataToGPU(m_Cmd, uploadBuffer, upload, gpuIndices); + + uploadBuffer.Release(); + upload.Dispose(); + compUIntData.Dispose(); + } + + var readback = new GPUInstanceDataBufferReadback(); + Assert.IsTrue(readback.Load(m_Cmd, buffer)); + + for (int i = 0; i < archCount0; i++) + { + var gpuIndex = buffer.InstanceToGPUIndex(archIndex0, i); + Vector4 elementVec = readback.LoadData(compVector, gpuIndex); + int elementInt = readback.LoadData(compInt, gpuIndex); + Assert.AreEqual(elementVec, new Vector4(i, i, i, i)); + Assert.AreEqual(elementInt, i); + } + + for (int i = 0; i < archCount1; i++) + { + int index = archCount0 + i; + var gpuIndex = buffer.InstanceToGPUIndex(archIndex1, i); + Vector4 elementVec = readback.LoadData(compVector, gpuIndex); + int elementInt = readback.LoadData(compInt, gpuIndex); + float elementFloat = readback.LoadData(compFloat, gpuIndex); + Assert.AreEqual(elementVec, new Vector4(index, index, index, index)); + Assert.AreEqual(elementInt, index); + Assert.AreEqual(elementFloat, index); + } + + for (int i = 0; i < archCount2; i++) + { + int index = archCount0 + archCount1 + i; + var gpuIndex = buffer.InstanceToGPUIndex(archIndex2, i); + uint elementUInt = readback.LoadData(compUInt, gpuIndex); + Assert.AreEqual(elementUInt, index); + } + + buffer.Dispose(); + readback.Dispose(); + } + + [Test, ConditionalIgnore("IgnoreGfxAPI", "Graphics API Not Supported.")] + public void TestInstanceDataBufferGrowNonPerInstanceComponentsHasOneElement() + { + const int initCount = 16; + const int newCount = 32; + + var compVecNonInst = m_ArchetypeMgr.CreateComponent(Shader.PropertyToID("VectorNonInst"), isPerInstance: false); + var compVec = m_ArchetypeMgr.CreateComponent(Shader.PropertyToID("Vector"), isPerInstance: true); + var compIntNonInst = m_ArchetypeMgr.CreateComponent(Shader.PropertyToID("IntNonInst"), isPerInstance: false); + var comps = new NativeList(Allocator.Temp) { compVecNonInst, compVec, compIntNonInst }; + + var arch = m_ArchetypeMgr.CreateArchetype(new GPUComponentSet(comps.AsArray())); + + var buffer = new GPUInstanceDataBuffer(ref m_ArchetypeMgr, new GPUInstanceDataBufferLayout { { arch, initCount } }, m_Resources); + var upload = new GPUInstanceUploadData(ref m_ArchetypeMgr, comps.AsArray(), initCount, Allocator.Temp); + + var compVecNonInstData = new NativeArray(1, Allocator.TempJob); + compVecNonInstData[0] = new Vector4(1.0f, 2.0f, 3.0f, 4.0f); + + var compVecData = new NativeArray(initCount, Allocator.TempJob); + var compIntNonInstData = new NativeArray(1, Allocator.TempJob); + for (int i = 0; i < initCount; i++) + compVecData[i] = new Vector4(i, i, i, i); + compIntNonInstData[0] = 123; + + GraphicsBuffer uploadBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Raw, + GraphicsBuffer.UsageFlags.LockBufferForWrite, + upload.uploadDataUIntSize, + sizeof(uint)); + + var writeBuffer = uploadBuffer.LockBufferForWrite(0, upload.uploadDataUIntSize); + { + upload.ScheduleWriteComponentsJob(compVecNonInstData, compVecNonInst, writeBuffer).Complete(); + upload.ScheduleWriteComponentsJob(compVecData, compVec, writeBuffer).Complete(); + upload.ScheduleWriteComponentsJob(compIntNonInstData, compIntNonInst, writeBuffer).Complete(); + } + uploadBuffer.UnlockBufferAfterWrite(upload.uploadDataUIntSize); + + var archIndex = buffer.GetArchetypeIndex(arch); + + var gpuIndices = new NativeArray(initCount, Allocator.Temp); + for (int i = 0; i < initCount; i++) + gpuIndices[i] = buffer.InstanceToGPUIndex(archIndex, i); + + buffer.UploadDataToGPU(m_Cmd, uploadBuffer, upload, gpuIndices); + + buffer.SetGPULayout(m_Cmd, ref m_ArchetypeMgr, new GPUInstanceDataBufferLayout { { arch, newCount } }, submitCmdBuffer: true); + + int newUploadCount = newCount - initCount; + var newUpload = new GPUInstanceUploadData(ref m_ArchetypeMgr, comps.AsArray(), newUploadCount, Allocator.Temp); + + var newCompData = new NativeArray(newUploadCount, Allocator.TempJob); + for (int i = 0; i < newUploadCount; i++) + newCompData[i] = new Vector4(initCount + i, initCount + i, initCount + i, initCount + i); + + GraphicsBuffer newUploadBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Raw, + GraphicsBuffer.UsageFlags.LockBufferForWrite, + newUpload.uploadDataUIntSize, + sizeof(uint)); + + var newWriteBuffer = newUploadBuffer.LockBufferForWrite(0, newUpload.uploadDataUIntSize); + newUpload.ScheduleWriteComponentsJob(newCompData, compVec, newWriteBuffer).Complete(); + newUploadBuffer.UnlockBufferAfterWrite(newUpload.uploadDataUIntSize); + + archIndex = buffer.GetArchetypeIndex(arch); + + var newGPUIndices = new NativeArray(newUploadCount, Allocator.Temp); + for (int i = 0; i < newUploadCount; i++) + newGPUIndices[i] = buffer.InstanceToGPUIndex(archIndex, initCount + i); + + buffer.UploadDataToGPU(m_Cmd, newUploadBuffer, newUpload, newGPUIndices); + + var readback = new GPUInstanceDataBufferReadback(); + Assert.IsTrue(readback.Load(m_Cmd, buffer)); + + for (int i = 0; i < newCount; i++) + { + var gpuIndex = buffer.InstanceToGPUIndex(archIndex, i); + Vector4 element = readback.LoadData(compVec, gpuIndex); + Vector4 elementSingle = readback.LoadData(compVecNonInst, gpuIndex); + int elementInt = readback.LoadData(compIntNonInst, gpuIndex); + Assert.AreEqual(element, new Vector4(i, i, i, i)); + Assert.AreEqual(elementSingle, new Vector4(1.0f, 2.0f, 3.0f, 4.0f)); + Assert.AreEqual(elementInt, 123); + } + + newUploadBuffer.Release(); + uploadBuffer.Release(); + upload.Dispose(); + newUpload.Dispose(); + newCompData.Dispose(); + compVecNonInstData.Dispose(); + compVecData.Dispose(); + compIntNonInstData.Dispose(); + readback.Dispose(); + buffer.Dispose(); + } + + [Test, ConditionalIgnore("IgnoreGfxAPI", "Graphics API Not Supported.")] + public void TestInstanceDataBufferNewLayoutWithMoreArchetypes() + { + const int count = 63; + + var compMatrix = m_ArchetypeMgr.CreateComponent(Shader.PropertyToID("Matrix"), isPerInstance: true); + var compIntNonInst = m_ArchetypeMgr.CreateComponent(Shader.PropertyToID("IntNonInst"), isPerInstance: false); + var comps0 = new NativeList(Allocator.Temp) { compMatrix, compIntNonInst }; + + var arch0 = m_ArchetypeMgr.CreateArchetype(new GPUComponentSet(comps0.AsArray())); + + var buffer = new GPUInstanceDataBuffer(ref m_ArchetypeMgr, new GPUInstanceDataBufferLayout { { arch0, count } }, m_Resources); + var upload = new GPUInstanceUploadData(ref m_ArchetypeMgr, comps0.AsArray(), count, Allocator.Temp); + + var compMatrixData = new NativeArray(count, Allocator.TempJob); + var compIntNonInstData = new NativeArray(1, Allocator.TempJob); + for (int i = 0; i < count; i++) + compMatrixData[i] = new Matrix4x4(new Vector4(i, i, i, i), new Vector4(i, i, i, i), new Vector4(i, i, i, i), new Vector4(i, i, i, i)); + compIntNonInstData[0] = 123; + + GraphicsBuffer uploadBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Raw, + GraphicsBuffer.UsageFlags.LockBufferForWrite, + upload.uploadDataUIntSize, + sizeof(uint)); + + var writeBuffer = uploadBuffer.LockBufferForWrite(0, upload.uploadDataUIntSize); + { + upload.ScheduleWriteComponentsJob(compMatrixData, compMatrix, writeBuffer).Complete(); + upload.ScheduleWriteComponentsJob(compIntNonInstData, compIntNonInst, writeBuffer).Complete(); + } + uploadBuffer.UnlockBufferAfterWrite(upload.uploadDataUIntSize); + + var archIndex0 = buffer.GetArchetypeIndex(arch0); + + var gpuIndices = new NativeArray(count, Allocator.Temp); + for (int i = 0; i < count; i++) + gpuIndices[i] = buffer.InstanceToGPUIndex(archIndex0, i); + + buffer.UploadDataToGPU(m_Cmd, uploadBuffer, upload, gpuIndices); + + var compVec = m_ArchetypeMgr.CreateComponent(Shader.PropertyToID("Vector"), isPerInstance: true); + var comps1 = new NativeList(Allocator.Temp) { compMatrix, compIntNonInst, compVec }; + var arch1 = m_ArchetypeMgr.CreateArchetype(new GPUComponentSet(comps1.AsArray())); + + buffer.SetGPULayout(m_Cmd, ref m_ArchetypeMgr, new GPUInstanceDataBufferLayout { { arch0, count }, { arch1, count } }, submitCmdBuffer: true); + + var newUpload = new GPUInstanceUploadData(ref m_ArchetypeMgr, comps1.AsArray(), count, Allocator.Temp); + + var newCompMatrixData = new NativeArray(count, Allocator.TempJob); + var newCompVecData = new NativeArray(count, Allocator.TempJob); + for (int i = 0; i < count; i++) + { + var vec = new Vector4(i + count, i + count, i + count, i + count); + newCompMatrixData[i] = new Matrix4x4(vec, vec, vec, vec); + newCompVecData[i] = new Vector4(i + 123, i + 123, i + 123, i + 123); + } + + GraphicsBuffer newUploadBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Raw, + GraphicsBuffer.UsageFlags.LockBufferForWrite, + newUpload.uploadDataUIntSize, + sizeof(uint)); + + var newWriteBuffer = newUploadBuffer.LockBufferForWrite(0, newUpload.uploadDataUIntSize); + { + newUpload.ScheduleWriteComponentsJob(newCompMatrixData, compMatrix, newWriteBuffer).Complete(); + newUpload.ScheduleWriteComponentsJob(newCompVecData, compVec, newWriteBuffer).Complete(); + } + newUploadBuffer.UnlockBufferAfterWrite(newUpload.uploadDataUIntSize); + + archIndex0 = buffer.GetArchetypeIndex(arch0); + var archIndex1 = buffer.GetArchetypeIndex(arch1); + + var newGPUIndices = new NativeArray(count, Allocator.Temp); + for (int i = 0; i < count; i++) + newGPUIndices[i] = buffer.InstanceToGPUIndex(archIndex1, i); + + buffer.UploadDataToGPU(m_Cmd, newUploadBuffer, newUpload, newGPUIndices); + + var readback = new GPUInstanceDataBufferReadback(); + Assert.IsTrue(readback.Load(m_Cmd, buffer)); + + for (int i = 0; i < count; i++) + { + var gpuIndex = buffer.InstanceToGPUIndex(archIndex0, i); + Matrix4x4 matrix = readback.LoadData(compMatrix, gpuIndex); + int singleInt = readback.LoadData(compIntNonInst, gpuIndex); + Assert.AreEqual(matrix, new Matrix4x4(new Vector4(i, i, i, i), new Vector4(i, i, i, i), new Vector4(i, i, i, i), new Vector4(i, i, i, i))); + Assert.AreEqual(singleInt, 123); + } + + for (int i = 0; i < count; i++) + { + var gpuIndex = buffer.InstanceToGPUIndex(archIndex1, i); + Matrix4x4 matrix = readback.LoadData(compMatrix, gpuIndex); + int singleInt = readback.LoadData(compIntNonInst, gpuIndex); + Vector4 vector = readback.LoadData(compVec, gpuIndex); + var vec = new Vector4(i + count, i + count, i + count, i + count); + Assert.AreEqual(matrix, new Matrix4x4(vec, vec, vec, vec)); + Assert.AreEqual(singleInt, 123); + Assert.AreEqual(vector, new Vector4(i + 123, i + 123, i + 123, i + 123)); + } + + newUploadBuffer.Release(); + uploadBuffer.Release(); + upload.Dispose(); + newUpload.Dispose(); + newCompMatrixData.Dispose(); + newCompVecData.Dispose(); + compMatrixData.Dispose(); + compIntNonInstData.Dispose(); + readback.Dispose(); + buffer.Dispose(); + } + + [Test, ConditionalIgnore("IgnoreGfxAPI", "Graphics API Not Supported.")] + public void TestInstanceDataBufferNewLayoutWithLessArchetypes() + { + const int count = 16001; + + var compMatrix = m_ArchetypeMgr.CreateComponent(Shader.PropertyToID("Matrix"), isPerInstance: true); + var compIntNonInst = m_ArchetypeMgr.CreateComponent(Shader.PropertyToID("IntNonInst"), isPerInstance: false); + var compVec = m_ArchetypeMgr.CreateComponent(Shader.PropertyToID("Vector"), isPerInstance: true); + + var comps0 = new NativeList(Allocator.Temp) { compMatrix, compIntNonInst, compVec }; + var comps1 = new NativeList(Allocator.Temp) { compIntNonInst, compVec }; + + var arch0 = m_ArchetypeMgr.CreateArchetype(new GPUComponentSet(comps0.AsArray())); + var arch1 = m_ArchetypeMgr.CreateArchetype(new GPUComponentSet(comps1.AsArray())); + + var buffer = new GPUInstanceDataBuffer(ref m_ArchetypeMgr, new GPUInstanceDataBufferLayout + { + { arch0, count }, + { arch1, count } + }, + m_Resources); + + var upload = new GPUInstanceUploadData(ref m_ArchetypeMgr, comps1.AsArray(), count, Allocator.Temp); + + var compVecData = new NativeArray(count, Allocator.TempJob); + var compIntNonInstData = new NativeArray(1, Allocator.TempJob); + for (int i = 0; i < count; i++) + compVecData[i] = new Vector4(i, i, i, i); + compIntNonInstData[0] = 123; + + GraphicsBuffer uploadBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Raw, + GraphicsBuffer.UsageFlags.LockBufferForWrite, + upload.uploadDataUIntSize, + sizeof(uint)); + + var writeBuffer = uploadBuffer.LockBufferForWrite(0, upload.uploadDataUIntSize); + { + upload.ScheduleWriteComponentsJob(compVecData, compVec, writeBuffer).Complete(); + upload.ScheduleWriteComponentsJob(compIntNonInstData, compIntNonInst, writeBuffer).Complete(); + } + uploadBuffer.UnlockBufferAfterWrite(upload.uploadDataUIntSize); + + var archIndex1 = buffer.GetArchetypeIndex(arch1); + + var gpuIndices = new NativeArray(count, Allocator.Temp); + for (int i = 0; i < count; i++) + gpuIndices[i] = buffer.InstanceToGPUIndex(archIndex1, i); + + buffer.UploadDataToGPU(m_Cmd, uploadBuffer, upload, gpuIndices); + + buffer.SetGPULayout(m_Cmd, ref m_ArchetypeMgr, new GPUInstanceDataBufferLayout { { arch1, count } }, submitCmdBuffer: true); + + archIndex1 = buffer.GetArchetypeIndex(arch1); + + var readback = new GPUInstanceDataBufferReadback(); + Assert.IsTrue(readback.Load(m_Cmd, buffer)); + + for (int i = 0; i < count; i++) + { + var gpuIndex = buffer.InstanceToGPUIndex(archIndex1, i); + Vector4 vec = readback.LoadData(compVec, gpuIndex); + int singleInt = readback.LoadData(compIntNonInst, gpuIndex); + Assert.AreEqual(vec, new Vector4(i, i, i, i)); + Assert.AreEqual(singleInt, 123); + } + + uploadBuffer.Release(); + upload.Dispose(); + compVecData.Dispose(); + compIntNonInstData.Dispose(); + readback.Dispose(); + buffer.Dispose(); + } + + [Test, ConditionalIgnore("IgnoreGfxAPI", "Graphics API Not Supported.")] + public void TestInstanceDataBufferGrowManyArchetypes() + { + const int archetypesCount = 64; + + var archetypes = new NativeArray(archetypesCount, Allocator.Temp); + var comps = new NativeList(Allocator.Temp); + + for (int i = 0; i < archetypesCount; i++) + { + comps.Add(m_ArchetypeMgr.CreateComponent(Shader.PropertyToID("Vector" + i), isPerInstance: true)); + archetypes[i] = m_ArchetypeMgr.CreateArchetype(new GPUComponentSet(comps.AsArray())); + } + + var buffer = new GPUInstanceDataBuffer(ref m_ArchetypeMgr, new GPUInstanceDataBufferLayout { { archetypes[0], 0 } }, m_Resources); + + for(int i = 0; i < archetypesCount; ++i) + ChangeLayoutAndTest(i + 1); + + buffer.Dispose(); + + void ChangeLayoutAndTest(int newCount) + { + var newLayout = new GPUInstanceDataBufferLayout(); + for (int i = 0; i < newCount; i++) + newLayout.Add(archetypes[i], i + 1); + + buffer.SetGPULayout(m_Cmd, ref m_ArchetypeMgr, newLayout, submitCmdBuffer: true); + + var archIndex = buffer.GetArchetypeIndex(newLayout.archetypes.Last()); + var instancesCount = newLayout.instancesCount.Last(); + + var archetypeDesc = m_ArchetypeMgr.GetArchetypeDesc(newLayout.archetypes.Last()); + var upload = new GPUInstanceUploadData(ref m_ArchetypeMgr, archetypeDesc.components.AsArray(), instancesCount, Allocator.Temp); + + GraphicsBuffer uploadBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Raw, + GraphicsBuffer.UsageFlags.LockBufferForWrite, + upload.uploadDataUIntSize, + sizeof(uint)); + + var writeBuffer = uploadBuffer.LockBufferForWrite(0, upload.uploadDataUIntSize); + for (int i = 0; i < archetypeDesc.components.Length; i++) + { + var comp = archetypeDesc.components[i]; + var checkValue = new Vector4(comp.index, comp.index, comp.index, comp.index); + var compData = new NativeArray(instancesCount, Allocator.TempJob); + for(int j = 0; j < instancesCount; j++) + compData[j] = checkValue; + upload.ScheduleWriteComponentsJob(compData, comp, writeBuffer).Complete(); + compData.Dispose(); + } + uploadBuffer.UnlockBufferAfterWrite(upload.uploadDataUIntSize); + + var newGPUIndices = new NativeArray(instancesCount, Allocator.Temp); + for (int i = 0; i < instancesCount; i++) + newGPUIndices[i] = buffer.InstanceToGPUIndex(archIndex, i); + + buffer.UploadDataToGPU(m_Cmd, uploadBuffer, upload, newGPUIndices); + + var readback = new GPUInstanceDataBufferReadback(); + Assert.IsTrue(readback.Load(m_Cmd, buffer)); + + for (int a = 0; a < newCount; a++) + { + archetypeDesc = m_ArchetypeMgr.GetArchetypeDesc(newLayout.archetypes[a]); + archIndex = buffer.GetArchetypeIndex(newLayout.archetypes[a]); + instancesCount = newLayout.instancesCount[a]; + + for (int c = 0; c < archetypeDesc.components.Length; c++) + { + var comp = archetypeDesc.components[c]; + var checkValue = new Vector4(comp.index, comp.index, comp.index, comp.index); + + for (int i = 0; i < instancesCount; i++) + { + Vector4 element = readback.LoadData(comp, buffer.InstanceToGPUIndex(archIndex, i)); + Assert.AreEqual(element, checkValue); + } + } + } + + uploadBuffer.Release(); + upload.Dispose(); + readback.Dispose(); + } + } + } +} diff --git a/Packages/com.unity.render-pipelines.core/Tests/Editor/GPUDriven/GPUDrivenInstanceDataTests.cs.meta b/Packages/com.unity.render-pipelines.core/Tests/Editor/GPUDriven/GPUDrivenInstanceDataTests.cs.meta new file mode 100644 index 00000000000..e36bdde808a --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Tests/Editor/GPUDriven/GPUDrivenInstanceDataTests.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: bfbaec287ab4ada4686561bca86c4b0b \ No newline at end of file diff --git a/Packages/com.unity.render-pipelines.core/Tests/Editor/GPUDriven/GPUDrivenRenderingTests.cs b/Packages/com.unity.render-pipelines.core/Tests/Editor/GPUDriven/GPUDrivenRenderingTests.cs index 487b2a56a41..2c801a03a9c 100644 --- a/Packages/com.unity.render-pipelines.core/Tests/Editor/GPUDriven/GPUDrivenRenderingTests.cs +++ b/Packages/com.unity.render-pipelines.core/Tests/Editor/GPUDriven/GPUDrivenRenderingTests.cs @@ -7,6 +7,8 @@ using Unity.Mathematics; using NUnit.Framework.Internal; using System.Reflection; +using System; + #if UNITY_EDITOR using UnityEditor.Rendering; #endif @@ -38,7 +40,16 @@ class GPUDrivenRenderingTests private RenderPipelineAsset m_OldPipelineAsset; private GPUResidentDrawerResources m_Resources; private RenderPassGlobalSettings m_GlobalSettings; - private bool m_oldEnableLodCrossFade; + + private GPUResidentDrawer m_GPUResidentDrawer; + private GPUDrivenProcessor m_GPUDrivenProcessor; + private InstanceDataSystem m_InstanceDataSystem; + private InstanceCuller m_Culler; + private InstanceCullingBatcher m_Batcher; + private LODGroupProcessor m_LODGroupProcessor; + private MeshRendererProcessor m_MeshRendererProcessor; + + private bool m_OldEnableLodCrossFade; class BoxedCounter { @@ -86,7 +97,7 @@ public void OnSetup() m_OldPipelineAsset = GraphicsSettings.defaultRenderPipeline; GraphicsSettings.defaultRenderPipeline = m_RenderPipe; m_Resources = GraphicsSettings.GetRenderPipelineSettings(); - m_oldEnableLodCrossFade = QualitySettings.enableLODCrossFade; + m_OldEnableLodCrossFade = QualitySettings.enableLODCrossFade; QualitySettings.enableLODCrossFade = true; } @@ -96,12 +107,50 @@ public void OnTearDown() m_RenderPipe = null; m_MeshTestData.Dispose(); GraphicsSettings.defaultRenderPipeline = m_OldPipelineAsset; - QualitySettings.enableLODCrossFade = m_oldEnableLodCrossFade; + QualitySettings.enableLODCrossFade = m_OldEnableLodCrossFade; + } + + private void InitializeGPUResidentDrawer(OnCullingCompleteCallback onCompleteCallback = null, bool supportDitheringCrossFade = false, float smallMeshScreenPercentage = 0f) + { + m_GPUResidentDrawer = new GPUResidentDrawer(new GPUResidentDrawerSettings + { + mode = GPUResidentDrawerMode.InstancedDrawing, + supportDitheringCrossFade = supportDitheringCrossFade, + smallMeshScreenPercentage = smallMeshScreenPercentage + }, + new InternalGPUResidentDrawerSettings + { + renderPipelineAsset = m_RenderPipe, + resources = m_Resources, + onCompleteCallback = onCompleteCallback, + isManagedByUnitTest = true, + }); + + m_GPUDrivenProcessor = m_GPUResidentDrawer.m_GPUDrivenProcessor; + m_InstanceDataSystem = m_GPUResidentDrawer.m_InstanceDataSystem; + m_Culler = m_GPUResidentDrawer.m_Culler; + m_Batcher = m_GPUResidentDrawer.m_Batcher; + m_LODGroupProcessor = m_GPUResidentDrawer.m_WorldProcessor.lodDGroupProcessor; + m_MeshRendererProcessor = m_GPUResidentDrawer.m_WorldProcessor.meshRendererProcessor; + } + + private void ShutdownGPUResidentDrawer() + { + m_GPUDrivenProcessor = null; + m_InstanceDataSystem = null; + m_Culler = null; + m_Batcher = null; + m_LODGroupProcessor = null; + m_MeshRendererProcessor = null; + m_GPUResidentDrawer.Dispose(); + m_GPUResidentDrawer = null; } [Test, ConditionalIgnore("IgnoreGfxAPI", "Graphics API Not Supported.")] public void TestInstanceCullingBatcherAddRemove() { + InitializeGPUResidentDrawer(); + var go0 = GameObject.CreatePrimitive(PrimitiveType.Cube); var go1 = GameObject.CreatePrimitive(PrimitiveType.Cube); var go2 = GameObject.CreatePrimitive(PrimitiveType.Sphere); @@ -111,51 +160,67 @@ public void TestInstanceCullingBatcherAddRemove() objList.Add(go1.GetComponent()); objList.Add(go2.GetComponent()); - var objIDs = new NativeList(Allocator.TempJob); + var instanceIDs = new NativeList(Allocator.TempJob); Shader dotsShader = Shader.Find("Unlit/SimpleDots"); var dotsMaterial = new Material(dotsShader); foreach (var obj in objList) { obj.material = dotsMaterial; - objIDs.Add(obj.GetEntityId()); + instanceIDs.Add(obj.GetEntityId()); } - var instances = new NativeArray(objList.Count, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); - for (int i = 0; i < objList.Count; ++i) - instances[i] = InstanceHandle.Invalid; + m_MeshRendererProcessor.ProcessGameObjectChanges(instanceIDs.AsArray()); + Assert.IsTrue(m_Batcher.GetDrawInstanceData().valid); + Assert.IsTrue(m_Batcher.GetDrawInstanceData().drawInstances.Length == 3); - var rbcDesc = RenderersBatchersContextDesc.NewDefault(); - rbcDesc.instanceNumInfo = new InstanceNumInfo(meshRendererNum: 4096, 0); - rbcDesc.supportDitheringCrossFade = false; + m_MeshRendererProcessor.DestroyInstances(instanceIDs.AsArray()); + Assert.IsTrue(m_Batcher.GetDrawInstanceData().drawInstances.Length == 0); - var gpuDrivenProcessor = new GPUDrivenProcessor(); + instanceIDs.Dispose(); - using (var brgContext = new RenderersBatchersContext(rbcDesc, gpuDrivenProcessor, m_Resources)) + ShutdownGPUResidentDrawer(); + } + + [Test, ConditionalIgnore("IgnoreGfxAPI", "Graphics API Not Supported.")] + public void TestInstanceCullingTier0() + { + var callbackCounter = new BoxedCounter(); + OnCullingCompleteCallback onCompleteCallback = (JobHandle jobHandle, in BatchCullingContext cc, in BatchCullingOutput cullingOutput) => { - using (var brg = new GPUResidentBatcher(brgContext, InstanceCullingBatcherDesc.NewDefault(), gpuDrivenProcessor)) - { - brg.UpdateRenderers(objIDs.AsArray()); + jobHandle.Complete(); - Assert.IsTrue(brg.instanceCullingBatcher.GetDrawInstanceData().valid); - Assert.IsTrue(brg.instanceCullingBatcher.GetDrawInstanceData().drawInstances.Length == 3); + if (cc.viewType != BatchCullingViewType.Camera) + return; - brgContext.ScheduleQueryRendererGroupInstancesJob(objIDs.AsArray(), instances).Complete(); - brg.DestroyDrawInstances(instances); + BatchCullingOutputDrawCommands drawCommands = cullingOutput.drawCommands[0]; - Assert.IsTrue(brg.instanceCullingBatcher.GetDrawInstanceData().drawInstances.Length == 0); + var materials = new NativeParallelHashSet(10, Allocator.Temp); + + var drawCommandCount = 0U; + unsafe + { + for (int i = 0; i < drawCommands.drawRangeCount; ++i) + { + BatchDrawRange range = drawCommands.drawRanges[i]; + drawCommandCount += range.drawCommandsCount; + for (int c = 0; c < range.drawCommandsCount; ++c) + { + BatchDrawCommand cmd = drawCommands.drawCommands[range.drawCommandsBegin + c]; + materials.Add(cmd.materialID); + } + } } - } - gpuDrivenProcessor.Dispose(); + Assert.AreEqual(2, drawCommandCount); + Assert.AreEqual(1, materials.Count()); + callbackCounter.Value += 1; - instances.Dispose(); - objIDs.Dispose(); - } + materials.Dispose(); + }; + + InitializeGPUResidentDrawer(onCompleteCallback: onCompleteCallback); - [Test, ConditionalIgnore("IgnoreGfxAPI", "Graphics API Not Supported.")] - public void TestInstanceCullingTier0() - { var go0 = GameObject.CreatePrimitive(PrimitiveType.Cube); var go1 = GameObject.CreatePrimitive(PrimitiveType.Cube); var go2 = GameObject.CreatePrimitive(PrimitiveType.Sphere); @@ -165,7 +230,7 @@ public void TestInstanceCullingTier0() objList.Add(go1.GetComponent()); objList.Add(go2.GetComponent()); - var objIDs = new NativeList(Allocator.TempJob); + var instanceIDs = new NativeList(Allocator.TempJob); Shader simpleDots = Shader.Find("Unlit/SimpleDots"); Material simpleDotsMat = new Material(simpleDots); @@ -173,159 +238,110 @@ public void TestInstanceCullingTier0() foreach (var obj in objList) { obj.material = simpleDotsMat; - objIDs.Add(obj.GetEntityId()); + instanceIDs.Add(obj.GetEntityId()); } - var instances = new NativeArray(objList.Count, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); - for (int i = 0; i < objList.Count; ++i) - instances[i] = InstanceHandle.Invalid; - - var rbcDesc = RenderersBatchersContextDesc.NewDefault(); - rbcDesc.instanceNumInfo = new InstanceNumInfo(meshRendererNum: 1, 0); - rbcDesc.supportDitheringCrossFade = false; + m_MeshRendererProcessor.ProcessGameObjectChanges(instanceIDs.AsArray()); - var gpuDrivenProcessor = new GPUDrivenProcessor(); + var cameraObject = new GameObject("myCamera"); + var mainCamera = cameraObject.AddComponent(); + SubmitCameraRenderRequest(mainCamera); + Assert.AreEqual(1, callbackCounter.Value); - //Using instance count of 1 to test for instance grow - using (var brgContext = new RenderersBatchersContext(rbcDesc, gpuDrivenProcessor, m_Resources)) - { - var cpuDrivenDesc = InstanceCullingBatcherDesc.NewDefault(); - var callbackCounter = new BoxedCounter(); - cpuDrivenDesc.onCompleteCallback = (JobHandle jobHandle, in BatchCullingContext cc, in BatchCullingOutput cullingOutput) => - { - jobHandle.Complete(); + m_MeshRendererProcessor.DestroyInstances(instanceIDs.AsArray()); - if (cc.viewType != BatchCullingViewType.Camera) - return; + mainCamera = null; + GameObject.DestroyImmediate(cameraObject); - BatchCullingOutputDrawCommands drawCommands = cullingOutput.drawCommands[0]; + instanceIDs.Dispose(); - var materials = new NativeParallelHashSet(10, Allocator.Temp); - - var drawCommandCount = 0U; - unsafe - { - for (int i = 0; i < drawCommands.drawRangeCount; ++i) - { - BatchDrawRange range = drawCommands.drawRanges[i]; - drawCommandCount += range.drawCommandsCount; - for (int c = 0; c < range.drawCommandsCount; ++c) - { - BatchDrawCommand cmd = drawCommands.drawCommands[range.drawCommandsBegin + c]; - materials.Add(cmd.materialID); - } - } - } - - Assert.AreEqual(2, drawCommandCount); - Assert.AreEqual(1, materials.Count()); - callbackCounter.Value += 1; - - materials.Dispose(); - }; - - using (var brg = new GPUResidentBatcher(brgContext, cpuDrivenDesc, gpuDrivenProcessor)) - { - brg.OnBeginContextRendering(); - - brg.UpdateRenderers(objIDs.AsArray()); - - var cameraObject = new GameObject("myCamera"); - var mainCamera = cameraObject.AddComponent(); - - SubmitCameraRenderRequest(mainCamera); - - brg.OnEndContextRendering(); - - Assert.AreEqual(1, callbackCounter.Value); - - mainCamera = null; - GameObject.DestroyImmediate(cameraObject); - - brgContext.ScheduleQueryRendererGroupInstancesJob(objIDs.AsArray(), instances).Complete(); - brg.DestroyDrawInstances(instances); - } - } - - gpuDrivenProcessor.Dispose(); - - instances.Dispose(); - objIDs.Dispose(); + ShutdownGPUResidentDrawer(); } [Test, ConditionalIgnore("IgnoreGfxAPI", "Graphics API Not Supported.")] [Ignore("Disabled for Instability https://jira.unity3d.com/browse/UUM-71039")] public void TestSceneViewHiddenRenderersCullingTier0() { + var callbackCounter = new BoxedCounter(); + OnCullingCompleteCallback onCompleteCallback = (JobHandle jobHandle, in BatchCullingContext cc, in BatchCullingOutput cullingOutput) => + { + jobHandle.Complete(); + + if (cc.viewType != BatchCullingViewType.Camera) + return; + + BatchCullingOutputDrawCommands drawCommands = cullingOutput.drawCommands[0]; + callbackCounter.Value = drawCommands.visibleInstanceCount; + }; + + InitializeGPUResidentDrawer(onCompleteCallback: onCompleteCallback); + var go = GameObject.CreatePrimitive(PrimitiveType.Cube); var simpleDots = Shader.Find("Unlit/SimpleDots"); var simpleDotsMat = new Material(simpleDots); var renderer = go.GetComponent(); renderer.material = simpleDotsMat; - var objIDs = new NativeArray(1, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); - var instances = new NativeArray(1, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); - objIDs[0] = renderer.GetEntityId(); - instances[0] = InstanceHandle.Invalid; - - var gpuDrivenProcessor = new GPUDrivenProcessor(); + var instanceIDs = new NativeArray(1, Allocator.TempJob); + instanceIDs[0] = renderer.GetEntityId(); - using (var brgContext = new RenderersBatchersContext(RenderersBatchersContextDesc.NewDefault(), gpuDrivenProcessor, m_Resources)) - { - var cpuDrivenDesc = InstanceCullingBatcherDesc.NewDefault(); - var callbackCounter = new BoxedCounter(); + m_MeshRendererProcessor.ProcessGameObjectChanges(instanceIDs); - cpuDrivenDesc.onCompleteCallback = (JobHandle jobHandle, in BatchCullingContext cc, in BatchCullingOutput cullingOutput) => - { - jobHandle.Complete(); + var cameraObject = new GameObject("SceneViewCamera"); + var mainCamera = cameraObject.AddComponent(); + mainCamera.cameraType = CameraType.SceneView; - if (cc.viewType != BatchCullingViewType.Camera) - return; + SceneVisibilityManager.instance.Hide(go, true); - BatchCullingOutputDrawCommands drawCommands = cullingOutput.drawCommands[0]; - callbackCounter.Value = drawCommands.visibleInstanceCount; - }; + m_Culler.OnBeginCameraRendering(mainCamera); + SubmitCameraRenderRequest(mainCamera); + m_Culler.OnEndCameraRendering(mainCamera); + Assert.AreEqual(callbackCounter.Value, 0); - using (var brg = new GPUResidentBatcher(brgContext, cpuDrivenDesc, gpuDrivenProcessor)) - { - brg.OnBeginContextRendering(); + SceneVisibilityManager.instance.Show(go, true); - brg.UpdateRenderers(objIDs); + m_Culler.OnBeginCameraRendering(mainCamera); + SubmitCameraRenderRequest(mainCamera); + m_Culler.OnEndCameraRendering(mainCamera); + Assert.AreEqual(callbackCounter.Value, 1); - var cameraObject = new GameObject("SceneViewCamera"); - var mainCamera = cameraObject.AddComponent(); - mainCamera.cameraType = CameraType.SceneView; + m_MeshRendererProcessor.DestroyInstances(instanceIDs); - SceneVisibilityManager.instance.Hide(go, true); + instanceIDs.Dispose(); - brg.OnBeginCameraRendering(mainCamera); - SubmitCameraRenderRequest(mainCamera); - brg.OnEndCameraRendering(mainCamera); - Assert.AreEqual(callbackCounter.Value, 0); + ShutdownGPUResidentDrawer(); + } - SceneVisibilityManager.instance.Show(go, true); + [Test, Ignore("Error in test shader (it is not DOTS compatible"), ConditionalIgnore("IgnoreGfxAPI", "Graphics API Not Supported.")] + public void TestMultipleMetadata() + { + OnCullingCompleteCallback onCompleteCallback = (JobHandle jobHandle, in BatchCullingContext cc, in BatchCullingOutput cullingOutput) => + { + jobHandle.Complete(); - brg.OnBeginCameraRendering(mainCamera); - SubmitCameraRenderRequest(mainCamera); - brg.OnEndCameraRendering(mainCamera); - Assert.AreEqual(callbackCounter.Value, 1); + if (cc.viewType != BatchCullingViewType.Camera) + return; - brg.OnEndContextRendering(); + BatchCullingOutputDrawCommands drawCommands = cullingOutput.drawCommands[0]; - GameObject.DestroyImmediate(cameraObject); - brgContext.ScheduleQueryRendererGroupInstancesJob(objIDs, instances).Complete(); - brg.DestroyDrawInstances(instances); + var drawCommandCount = 0U; + unsafe + { + for (int i = 0; i < drawCommands.drawRangeCount; ++i) + { + BatchDrawRange range = drawCommands.drawRanges[i]; + drawCommandCount += range.drawCommandsCount; + for (int c = 0; c < range.drawCommandsCount; ++c) + { + BatchDrawCommand cmd = drawCommands.drawCommands[range.drawCommandsBegin + c]; + } + } } - } + Assert.AreEqual(3, drawCommandCount); + }; - gpuDrivenProcessor.Dispose(); - instances.Dispose(); - objIDs.Dispose(); - } + InitializeGPUResidentDrawer(onCompleteCallback: onCompleteCallback); - [Test, Ignore("Error in test shader (it is not DOTS compatible"), ConditionalIgnore("IgnoreGfxAPI", "Graphics API Not Supported.")] - public void TestMultipleMetadata() - { var go0 = GameObject.CreatePrimitive(PrimitiveType.Cube); var go1 = GameObject.CreatePrimitive(PrimitiveType.Capsule); var go2 = GameObject.CreatePrimitive(PrimitiveType.Cube); @@ -335,7 +351,7 @@ public void TestMultipleMetadata() objList.Add(go1.GetComponent()); objList.Add(go2.GetComponent()); - var objIDs = new NativeList(Allocator.TempJob); + var instanceIDs = new NativeList(Allocator.TempJob); Shader simpleDots = Shader.Find("Unlit/SimpleDots"); Material simpleDotsMat = new Material(simpleDots); @@ -345,74 +361,60 @@ public void TestMultipleMetadata() obj.receiveGI = ReceiveGI.LightProbes; obj.lightProbeUsage = LightProbeUsage.BlendProbes; obj.material = simpleDotsMat; - objIDs.Add(obj.GetEntityId()); + instanceIDs.Add(obj.GetEntityId()); } objList[2].lightProbeUsage = LightProbeUsage.Off; - var instances = new NativeArray(objList.Count, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); - for (int i = 0; i < objList.Count; ++i) - instances[i] = InstanceHandle.Invalid; + m_MeshRendererProcessor.ProcessGameObjectChanges(instanceIDs.AsArray()); - var gpuDrivenProcessor = new GPUDrivenProcessor(); + var cameraObject = new GameObject("myCamera"); + var mainCamera = cameraObject.AddComponent(); - using (var brgContext = new RenderersBatchersContext(new RenderersBatchersContextDesc() { instanceNumInfo = new InstanceNumInfo(meshRendererNum: 64, 0), supportDitheringCrossFade = false }, gpuDrivenProcessor, m_Resources)) - { - var cpuDrivenDesc = InstanceCullingBatcherDesc.NewDefault(); - cpuDrivenDesc.onCompleteCallback = (JobHandle jobHandle, in BatchCullingContext cc, in BatchCullingOutput cullingOutput) => - { - jobHandle.Complete(); + SubmitCameraRenderRequest(mainCamera); - if (cc.viewType != BatchCullingViewType.Camera) - return; + m_MeshRendererProcessor.DestroyInstances(instanceIDs.AsArray()); - BatchCullingOutputDrawCommands drawCommands = cullingOutput.drawCommands[0]; + mainCamera = null; + GameObject.DestroyImmediate(cameraObject); - var drawCommandCount = 0U; - unsafe - { - for (int i = 0; i < drawCommands.drawRangeCount; ++i) - { - BatchDrawRange range = drawCommands.drawRanges[i]; - drawCommandCount += range.drawCommandsCount; - for (int c = 0; c < range.drawCommandsCount; ++c) - { - BatchDrawCommand cmd = drawCommands.drawCommands[range.drawCommandsBegin + c]; - } - } - } - Assert.AreEqual(3, drawCommandCount); - }; - - using (var brg = new GPUResidentBatcher(brgContext, cpuDrivenDesc, gpuDrivenProcessor)) - { - brg.OnBeginContextRendering(); - - brg.UpdateRenderers(objIDs.AsArray()); + instanceIDs.Dispose(); - var cameraObject = new GameObject("myCamera"); - var mainCamera = cameraObject.AddComponent(); + ShutdownGPUResidentDrawer(); + } - SubmitCameraRenderRequest(mainCamera); + [Test, Ignore("Error in test shader (it is not DOTS compatible"), ConditionalIgnore("IgnoreGfxAPI", "Graphics API Not Supported.")] + public void TestCPULODSelection() + { + var callbackCounter = new BoxedCounter(); + var expectedMeshID = 1; + var expectedDrawCommandCount = 2; + OnCullingCompleteCallback onCompleteCallback = (JobHandle jobHandle, in BatchCullingContext cc, in BatchCullingOutput cullingOutput) => + { + jobHandle.Complete(); - brg.OnEndContextRendering(); + if (cc.viewType != BatchCullingViewType.Camera) + return; - mainCamera = null; - GameObject.DestroyImmediate(cameraObject); + BatchCullingOutputDrawCommands drawCommands = cullingOutput.drawCommands[0]; - brgContext.ScheduleQueryRendererGroupInstancesJob(objIDs.AsArray(), instances).Complete(); - brg.DestroyDrawInstances(instances); + var drawCommandCount = 0U; + unsafe + { + for (int i = 0; i < drawCommands.drawRangeCount; ++i) + { + BatchDrawRange range = drawCommands.drawRanges[i]; + drawCommandCount += range.drawCommandsCount; + BatchDrawCommand cmd = drawCommands.drawCommands[range.drawCommandsBegin]; + Assert.AreEqual(expectedMeshID, cmd.meshID.value, "Incorrect mesh rendered"); + } } - } + Assert.IsTrue(drawCommandCount == expectedDrawCommandCount, "Incorrect draw command count"); - gpuDrivenProcessor.Dispose(); + callbackCounter.Value += 1; + }; - instances.Dispose(); - objIDs.Dispose(); - } + InitializeGPUResidentDrawer(onCompleteCallback: onCompleteCallback); - [Test, Ignore("Error in test shader (it is not DOTS compatible"), ConditionalIgnore("IgnoreGfxAPI", "Graphics API Not Supported.")] - public void TestCPULODSelection() - { var previousLodBias = QualitySettings.lodBias; QualitySettings.lodBias = 1.0f; @@ -439,8 +441,8 @@ public void TestCPULODSelection() gos[lodCount].transform.parent = gameObject.transform; lodGroup.SetLODs(lods); - var lodGroupInstancesID = new NativeList(Allocator.TempJob); - lodGroupInstancesID.Add(lodGroup.GetEntityId()); + var lodGroupIDs = new NativeList(Allocator.TempJob); + lodGroupIDs.Add(lodGroup.GetEntityId()); var objList = new List(); for (var i = 0; i < lodCount; i++) @@ -449,136 +451,115 @@ public void TestCPULODSelection() } objList.Add(gos[lodCount].GetComponent()); - var objIDs = new NativeList(Allocator.TempJob); + var rendererIDs = new NativeList(Allocator.TempJob); Shader dotsShader = Shader.Find("Unlit/SimpleDots"); var dotsMaterial = new Material(dotsShader); foreach (var obj in objList) { obj.material = dotsMaterial; - objIDs.Add(obj.GetEntityId()); + rendererIDs.Add(obj.GetEntityId()); } - var instances = new NativeArray(objList.Count, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); - for (int i = 0; i < objList.Count; ++i) - instances[i] = InstanceHandle.Invalid; - - var rbcDesc = RenderersBatchersContextDesc.NewDefault(); - rbcDesc.instanceNumInfo = new InstanceNumInfo(meshRendererNum: 64, 0); - rbcDesc.supportDitheringCrossFade = false; + m_LODGroupProcessor.ProcessGameObjectChanges(lodGroupIDs.AsArray(), transformOnly: false); + m_MeshRendererProcessor.ProcessGameObjectChanges(rendererIDs.AsArray()); - var gpuDrivenProcessor = new GPUDrivenProcessor(); + var cameraObject = new GameObject("myCamera"); + var mainCamera = cameraObject.AddComponent(); + mainCamera.fieldOfView = 60; - using (var brgContext = new RenderersBatchersContext(rbcDesc, gpuDrivenProcessor, m_Resources)) - { - var cpuDrivenDesc = InstanceCullingBatcherDesc.NewDefault(); - var callbackCounter = new BoxedCounter(); - var expectedMeshID = 1; - var expectedDrawCommandCount = 2; - cpuDrivenDesc.onCompleteCallback = (JobHandle jobHandle, in BatchCullingContext cc, in BatchCullingOutput cullingOutput) => - { - jobHandle.Complete(); + //Test 1 - Should render Lod0 (range 0 - 6.66) + cameraObject.transform.position = new Vector3(0.0f, 0.0f, -1.0f); + SubmitCameraRenderRequest(mainCamera); + cameraObject.transform.position = new Vector3(0.0f, 0.0f, -5.65f); + SubmitCameraRenderRequest(mainCamera); - if (cc.viewType != BatchCullingViewType.Camera) - return; - - BatchCullingOutputDrawCommands drawCommands = cullingOutput.drawCommands[0]; - - var drawCommandCount = 0U; - unsafe - { - for (int i = 0; i < drawCommands.drawRangeCount; ++i) - { - BatchDrawRange range = drawCommands.drawRanges[i]; - drawCommandCount += range.drawCommandsCount; - BatchDrawCommand cmd = drawCommands.drawCommands[range.drawCommandsBegin]; - Assert.AreEqual(expectedMeshID, cmd.meshID.value, "Incorrect mesh rendered"); - } - } - Assert.IsTrue(drawCommandCount == expectedDrawCommandCount, "Incorrect draw command count"); + //Test 2 - Should render Lod1(range 6.66 - 12.5) + expectedMeshID = 2; + cameraObject.transform.position = new Vector3(0.0f, 0.0f, -6.67f); + SubmitCameraRenderRequest(mainCamera); + cameraObject.transform.position = new Vector3(0.0f, 0.0f, -10.5f); + SubmitCameraRenderRequest(mainCamera); - callbackCounter.Value += 1; - }; + //Test 3 - Should render Lod2 (range 12.5 - 99.9) + expectedMeshID = 3; + gameObject.transform.localScale *= 0.5f; - using (var brg = new GPUResidentBatcher(brgContext, cpuDrivenDesc, gpuDrivenProcessor)) - { - brg.OnBeginContextRendering(); + // For now we have to manually dispatch lod group transform changes. + Vector3 worldRefPoint = lodGroup.GetWorldReferencePoint(); + float worldSize = lodGroup.GetWorldSpaceSize(); - brgContext.UpdateLODGroups(lodGroupInstancesID.AsArray()); - brg.UpdateRenderers(objIDs.AsArray()); + var transformedLODGroupIDs = new NativeArray(1, Allocator.Temp); + transformedLODGroupIDs[0] = lodGroup.GetEntityId(); - var cameraObject = new GameObject("myCamera"); - var mainCamera = cameraObject.AddComponent(); - mainCamera.fieldOfView = 60; + m_LODGroupProcessor.ProcessGameObjectChanges(transformedLODGroupIDs, transformOnly: true); - //Test 1 - Should render Lod0 (range 0 - 6.66) - cameraObject.transform.position = new Vector3(0.0f, 0.0f, -1.0f); - SubmitCameraRenderRequest(mainCamera); - cameraObject.transform.position = new Vector3(0.0f, 0.0f, -5.65f); - SubmitCameraRenderRequest(mainCamera); + cameraObject.transform.position = new Vector3(0.0f, 0.0f, -6.5f); + SubmitCameraRenderRequest(mainCamera); + cameraObject.transform.position = new Vector3(0.0f, 0.0f, -40.3f); + SubmitCameraRenderRequest(mainCamera); - //Test 2 - Should render Lod1(range 6.66 - 12.5) - expectedMeshID = 2; - cameraObject.transform.position = new Vector3(0.0f, 0.0f, -6.67f); - SubmitCameraRenderRequest(mainCamera); - cameraObject.transform.position = new Vector3(0.0f, 0.0f, -10.5f); - SubmitCameraRenderRequest(mainCamera); + //Test 3 - Should size cull (range 99.9 - Inf.) + cameraObject.transform.position = new Vector3(0.0f, 0.0f, -50.4f); + expectedMeshID = 4; + expectedDrawCommandCount = 1; + SubmitCameraRenderRequest(mainCamera); - //Test 3 - Should render Lod2 (range 12.5 - 99.9) - expectedMeshID = 3; - gameObject.transform.localScale *= 0.5f; + Assert.AreEqual(7, callbackCounter.Value); - // For now we have to manually dispatch lod group transform changes. - Vector3 worldRefPoint = lodGroup.GetWorldReferencePoint(); - float worldSize = lodGroup.GetWorldSpaceSize(); + m_LODGroupProcessor.DestroyInstances(lodGroupIDs.AsArray()); + m_MeshRendererProcessor.DestroyInstances(rendererIDs.AsArray()); - var transformedLODGroups = new NativeArray(1, Allocator.Temp); - transformedLODGroups[0] = lodGroup.GetEntityId(); + mainCamera = null; + GameObject.DestroyImmediate(cameraObject); - brgContext.TransformLODGroups(transformedLODGroups); + lodGroupIDs.Dispose(); + rendererIDs.Dispose(); - cameraObject.transform.position = new Vector3(0.0f, 0.0f, -6.5f); - SubmitCameraRenderRequest(mainCamera); - cameraObject.transform.position = new Vector3(0.0f, 0.0f, -40.3f); - SubmitCameraRenderRequest(mainCamera); + QualitySettings.lodBias = previousLodBias; - //Test 3 - Should size cull (range 99.9 - Inf.) - cameraObject.transform.position = new Vector3(0.0f, 0.0f, -50.4f); - expectedMeshID = 4; - expectedDrawCommandCount = 1; - SubmitCameraRenderRequest(mainCamera); + ShutdownGPUResidentDrawer(); + } - brg.OnEndContextRendering(); + [Test, ConditionalIgnore("IgnoreGfxAPI", "Graphics API Not Supported.")] + public void TestCPULODCrossfade() + { + var expectedMeshIDs = new List(); + var expectedFlags = new List(); + var expectedDrawCommandCount = new BoxedCounter(); + OnCullingCompleteCallback onCompleteCallback = (JobHandle jobHandle, in BatchCullingContext cc, in BatchCullingOutput cullingOutput) => + { + jobHandle.Complete(); - Assert.AreEqual(7, callbackCounter.Value); + if (cc.viewType != BatchCullingViewType.Camera) + return; - mainCamera = null; - GameObject.DestroyImmediate(cameraObject); + BatchCullingOutputDrawCommands drawCommands = cullingOutput.drawCommands[0]; - brgContext.ScheduleQueryRendererGroupInstancesJob(objIDs.AsArray(), instances).Complete(); - brg.DestroyDrawInstances(instances); + unsafe + { + Assert.AreEqual(1, drawCommands.drawRangeCount); + BatchDrawRange range = drawCommands.drawRanges[0]; + Assert.AreEqual(range.drawCommandsCount, expectedDrawCommandCount.Value, " Incorrect draw Command Count"); + for (int i = 0; i < range.drawCommandsCount; ++i) + { + BatchDrawCommand cmd = drawCommands.drawCommands[range.drawCommandsBegin + i]; + Assert.AreEqual(expectedMeshIDs[i], cmd.meshID.value, "Incorrect mesh rendered"); + Assert.AreEqual(expectedFlags[i], cmd.flags & BatchDrawCommandFlags.LODCrossFade, "Incorrect flag for the current draw command"); + } } - } - - lodGroupInstancesID.Dispose(); - gpuDrivenProcessor.Dispose(); - - objIDs.Dispose(); - instances.Dispose(); + }; - QualitySettings.lodBias = previousLodBias; - } + InitializeGPUResidentDrawer(supportDitheringCrossFade: true, onCompleteCallback: onCompleteCallback); - [Test, ConditionalIgnore("IgnoreGfxAPI", "Graphics API Not Supported.")] - public void TestCPULODCrossfade() - { var previousLodBias = QualitySettings.lodBias; QualitySettings.lodBias = 1.0f; var gameObject = new GameObject("LODGroup"); gameObject.AddComponent(); - GameObject[] gos = new GameObject[] { + GameObject[] gos = new GameObject[] + { GameObject.CreatePrimitive(PrimitiveType.Cube), GameObject.CreatePrimitive(PrimitiveType.Sphere), GameObject.CreatePrimitive(PrimitiveType.Sphere), @@ -598,141 +579,130 @@ public void TestCPULODCrossfade() lodGroup.SetLODs(lods); var objList = new List(); - for (var i = 0; i < lodCount; i++) + for (var i = 0; i < gos.Length; i++) { objList.Add(gos[i].GetComponent()); } - objList.Add(gos[lodCount].GetComponent()); - var lodGroupInstancesID = new NativeList(Allocator.TempJob); - lodGroupInstancesID.Add(lodGroup.GetEntityId()); + var lodGroupIDs = new NativeList(Allocator.TempJob); + lodGroupIDs.Add(lodGroup.GetEntityId()); - var objIDs = new NativeList(Allocator.TempJob); + var rendererIDs = new NativeList(Allocator.TempJob); var simpleDots = Shader.Find("Unlit/SimpleDots"); var simpleDotsMat = new Material(simpleDots); foreach (var obj in objList) { obj.material = simpleDotsMat; - objIDs.Add(obj.GetEntityId()); + rendererIDs.Add(obj.GetEntityId()); } - var instances = new NativeArray(objList.Count, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); - for (int i = 0; i < objList.Count; ++i) - instances[i] = InstanceHandle.Invalid; + m_LODGroupProcessor.ProcessGameObjectChanges(lodGroupIDs.AsArray(), transformOnly: false); + m_MeshRendererProcessor.ProcessGameObjectChanges(rendererIDs.AsArray()); + + EntityId cubeMeshInstanceID = gos[0].GetComponent().sharedMesh.GetEntityId(); + EntityId sphereMeshInstanceID = gos[1].GetComponent().sharedMesh.GetEntityId(); + + BatchMeshID cubeMeshID = m_Batcher.meshMap[cubeMeshInstanceID].meshID; + BatchMeshID sphereMeshID = m_Batcher.meshMap[sphereMeshInstanceID].meshID; + + var cameraObject = new GameObject("myCamera"); + var mainCamera = cameraObject.AddComponent(); + mainCamera.fieldOfView = 60; + + // Cube Mesh ID : 1 (Lod 0) + // Sphere Mesh ID : 2 (Lod 1 + non Loded) + //Test 0 - Should render Lod0 (cube) + non loded sphere + expectedMeshIDs.Add(cubeMeshID.value); + expectedMeshIDs.Add(sphereMeshID.value); + expectedFlags.Add(BatchDrawCommandFlags.LODCrossFadeValuePacked); + expectedFlags.Add(BatchDrawCommandFlags.LODCrossFadeValuePacked); + expectedDrawCommandCount.Value = 2; + cameraObject.transform.position = new Vector3(0.0f, 0.0f, -1.0f); + SubmitCameraRenderRequest(mainCamera); + + //Test 1 - Should render Lod0 and 1 crossfaded + non loded sphere + expectedMeshIDs.Clear(); + expectedMeshIDs.Add(cubeMeshID.value); + expectedMeshIDs.Add(sphereMeshID.value); + expectedMeshIDs.Add(sphereMeshID.value); + expectedFlags.Clear(); + expectedFlags.Add(BatchDrawCommandFlags.LODCrossFade); + expectedFlags.Add(BatchDrawCommandFlags.LODCrossFadeValuePacked); + expectedFlags.Add(BatchDrawCommandFlags.LODCrossFade); + expectedDrawCommandCount.Value = 3; + cameraObject.transform.position = new Vector3(0.0f, 0.0f, -2.0f); + SubmitCameraRenderRequest(mainCamera); + + //Test 2 - Should render Lod1 + non loded sphere (single Draw Command as they are both spheres) + expectedMeshIDs.Clear(); + expectedMeshIDs.Add(sphereMeshID.value); + expectedFlags.Clear(); + expectedFlags.Add(BatchDrawCommandFlags.LODCrossFadeValuePacked); + expectedDrawCommandCount.Value = 1; + cameraObject.transform.position = new Vector3(0.0f, 0.0f, -3.0f); + SubmitCameraRenderRequest(mainCamera); + + //Test 3 - Should render Lod1 crossfaded + non loded sphere + expectedMeshIDs.Clear(); + expectedMeshIDs.Add(sphereMeshID.value); + expectedMeshIDs.Add(sphereMeshID.value); + expectedFlags.Clear(); + expectedFlags.Add(BatchDrawCommandFlags.LODCrossFadeValuePacked); + expectedFlags.Add(BatchDrawCommandFlags.LODCrossFade); + expectedDrawCommandCount.Value = 2; + cameraObject.transform.position = new Vector3(0.0f, 0.0f, -4.0f); + SubmitCameraRenderRequest(mainCamera); + + m_MeshRendererProcessor.DestroyInstances(rendererIDs.AsArray()); + m_LODGroupProcessor.DestroyInstances(lodGroupIDs.AsArray()); + + mainCamera = null; + GameObject.DestroyImmediate(cameraObject); + + lodGroupIDs.Dispose(); + rendererIDs.Dispose(); - var rbcDesc = RenderersBatchersContextDesc.NewDefault(); - rbcDesc.instanceNumInfo = new InstanceNumInfo(meshRendererNum: 64, 0); - rbcDesc.supportDitheringCrossFade = true; + QualitySettings.lodBias = previousLodBias; + + ShutdownGPUResidentDrawer(); + } - var gpuDrivenProcessor = new GPUDrivenProcessor(); + [Test, ConditionalIgnore("IgnoreGfxAPI", "Graphics API Not Supported.")] + public void TestGpuDrivenSmallMeshCulling() + { + var expectedMeshIDs = new List(); + var expectedFlags = new List(); + var expectedDrawCommandCount = new BoxedCounter(); + + var lastLodBias = QualitySettings.lodBias; + QualitySettings.lodBias = 1.0f; - using (var brgContext = new RenderersBatchersContext(rbcDesc, gpuDrivenProcessor, m_Resources)) + OnCullingCompleteCallback onCompleteCallback = (JobHandle jobHandle, in BatchCullingContext cc, in BatchCullingOutput cullingOutput) => { - var cpuDrivenDesc = InstanceCullingBatcherDesc.NewDefault(); - var expectedMeshIDs = new List(); - var expectedFlags = new List(); - var expectedDrawCommandCount = 0; - cpuDrivenDesc.onCompleteCallback = (JobHandle jobHandle, in BatchCullingContext cc, in BatchCullingOutput cullingOutput) => - { - jobHandle.Complete(); + jobHandle.Complete(); - if (cc.viewType != BatchCullingViewType.Camera) - return; + if (cc.viewType != BatchCullingViewType.Camera) + return; - BatchCullingOutputDrawCommands drawCommands = cullingOutput.drawCommands[0]; + BatchCullingOutputDrawCommands drawCommands = cullingOutput.drawCommands[0]; - unsafe + unsafe + { + Assert.AreEqual(1, drawCommands.drawRangeCount); + BatchDrawRange range = drawCommands.drawRanges[0]; + Assert.AreEqual(range.drawCommandsCount, expectedDrawCommandCount.Value, " Incorrect draw Command Count"); + for (int i = 0; i < range.drawCommandsCount; ++i) { - Assert.AreEqual(1, drawCommands.drawRangeCount); - BatchDrawRange range = drawCommands.drawRanges[0]; - Assert.AreEqual(range.drawCommandsCount, expectedDrawCommandCount, " Incorrect draw Command Count"); - for (int i = 0; i < range.drawCommandsCount; ++i) - { - BatchDrawCommand cmd = drawCommands.drawCommands[range.drawCommandsBegin + i]; - Assert.AreEqual(expectedMeshIDs[i], cmd.meshID.value, "Incorrect mesh rendered"); - Assert.AreEqual(expectedFlags[i], cmd.flags & BatchDrawCommandFlags.LODCrossFade, "Incorrect flag for the current draw command"); - } + BatchDrawCommand cmd = drawCommands.drawCommands[range.drawCommandsBegin + i]; + Assert.AreEqual(expectedMeshIDs[i], cmd.meshID.value, "Incorrect mesh rendered"); + Assert.AreEqual(expectedFlags[i], cmd.flags & BatchDrawCommandFlags.LODCrossFade, "Incorrect flag for the current draw command"); } - }; - - using (var brg = new GPUResidentBatcher(brgContext, cpuDrivenDesc, gpuDrivenProcessor)) - { - brg.OnBeginContextRendering(); - - brgContext.UpdateLODGroups(lodGroupInstancesID.AsArray()); - brg.UpdateRenderers(objIDs.AsArray()); - - var cameraObject = new GameObject("myCamera"); - var mainCamera = cameraObject.AddComponent(); - mainCamera.fieldOfView = 60; - - // Cube Mesh ID : 1 (Lod 0) - // Sphere Mesh ID : 2 (Lod 1 + non Loded) - //Test 0 - Should render Lod0 (cube) + non loded sphere - expectedMeshIDs.Add(1); - expectedMeshIDs.Add(2); - expectedFlags.Add(BatchDrawCommandFlags.LODCrossFadeValuePacked); - expectedFlags.Add(BatchDrawCommandFlags.LODCrossFadeValuePacked); - expectedDrawCommandCount = 2; - cameraObject.transform.position = new Vector3(0.0f, 0.0f, -1.0f); - SubmitCameraRenderRequest(mainCamera); - - //Test 1 - Should render Lod0 and 1 crossfaded + non loded sphere - expectedMeshIDs.Clear(); - expectedMeshIDs.Add(1); - expectedMeshIDs.Add(2); - expectedMeshIDs.Add(2); - expectedFlags.Clear(); - expectedFlags.Add(BatchDrawCommandFlags.LODCrossFade); - expectedFlags.Add(BatchDrawCommandFlags.LODCrossFadeValuePacked); - expectedFlags.Add(BatchDrawCommandFlags.LODCrossFade); - expectedDrawCommandCount = 3; - cameraObject.transform.position = new Vector3(0.0f, 0.0f, -2.0f); - SubmitCameraRenderRequest(mainCamera); - - //Test 2 - Should render Lod1 + non loded sphere (single Draw Command as they are both spheres) - expectedMeshIDs.Clear(); - expectedMeshIDs.Add(2); - expectedFlags.Clear(); - expectedFlags.Add(BatchDrawCommandFlags.LODCrossFadeValuePacked); - expectedDrawCommandCount = 1; - cameraObject.transform.position = new Vector3(0.0f, 0.0f, -3.0f); - SubmitCameraRenderRequest(mainCamera); - - //Test 3 - Should render Lod1 crossfaded + non loded sphere - expectedMeshIDs.Clear(); - expectedMeshIDs.Add(2); - expectedMeshIDs.Add(2); - expectedFlags.Clear(); - expectedFlags.Add(BatchDrawCommandFlags.LODCrossFadeValuePacked); - expectedFlags.Add(BatchDrawCommandFlags.LODCrossFade); - expectedDrawCommandCount = 2; - cameraObject.transform.position = new Vector3(0.0f, 0.0f, -4.0f); - SubmitCameraRenderRequest(mainCamera); - - brg.OnEndContextRendering(); - - mainCamera = null; - GameObject.DestroyImmediate(cameraObject); - - brgContext.ScheduleQueryRendererGroupInstancesJob(objIDs.AsArray(), instances).Complete(); - brg.DestroyDrawInstances(instances); } - } - - lodGroupInstancesID.Dispose(); - gpuDrivenProcessor.Dispose(); - - objIDs.Dispose(); - instances.Dispose(); + }; - QualitySettings.lodBias = previousLodBias; - } + InitializeGPUResidentDrawer(supportDitheringCrossFade: true, smallMeshScreenPercentage: 10f, onCompleteCallback: onCompleteCallback); - [Test, ConditionalIgnore("IgnoreGfxAPI", "Graphics API Not Supported.")] - public void TestGpuDrivenSmallMeshCulling() - { var gameObject = new GameObject("Root"); var sphere0 = GameObject.CreatePrimitive(PrimitiveType.Sphere); sphere0.transform.parent = gameObject.transform; @@ -744,427 +714,199 @@ public void TestGpuDrivenSmallMeshCulling() objList.Add(sphere0.GetComponent()); objList.Add(sphere1.GetComponent()); - var objIDs = new NativeList(Allocator.TempJob); + var instanceIDs = new NativeList(Allocator.TempJob); var simpleDots = Shader.Find("Unlit/SimpleDots"); var simpleDotsMat = new Material(simpleDots); foreach (var obj in objList) { obj.material = simpleDotsMat; - objIDs.Add(obj.GetEntityId()); + instanceIDs.Add(obj.GetEntityId()); } - var instances = new NativeArray(objList.Count, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); - for (int i = 0; i < objList.Count; ++i) - instances[i] = InstanceHandle.Invalid; - - var rbcDesc = RenderersBatchersContextDesc.NewDefault(); - rbcDesc.instanceNumInfo = new InstanceNumInfo(meshRendererNum: 64, 0); - rbcDesc.supportDitheringCrossFade = true; - rbcDesc.smallMeshScreenPercentage = 10.0f; - - var lastLodBias = QualitySettings.lodBias; - QualitySettings.lodBias = 1.0f; - - var gpuDrivenProcessor = new GPUDrivenProcessor(); - - using (var brgContext = new RenderersBatchersContext(rbcDesc, gpuDrivenProcessor, m_Resources)) - { - var cpuDrivenDesc = InstanceCullingBatcherDesc.NewDefault(); - var expectedMeshIDs = new List(); - var expectedFlags = new List(); - var expectedDrawCommandCount = 0; - cpuDrivenDesc.onCompleteCallback = (JobHandle jobHandle, in BatchCullingContext cc, in BatchCullingOutput cullingOutput) => - { - jobHandle.Complete(); - - if (cc.viewType != BatchCullingViewType.Camera) - return; - - BatchCullingOutputDrawCommands drawCommands = cullingOutput.drawCommands[0]; - - unsafe - { - Assert.AreEqual(1, drawCommands.drawRangeCount); - BatchDrawRange range = drawCommands.drawRanges[0]; - Assert.AreEqual(range.drawCommandsCount, expectedDrawCommandCount, " Incorrect draw Command Count"); - for (int i = 0; i < range.drawCommandsCount; ++i) - { - BatchDrawCommand cmd = drawCommands.drawCommands[range.drawCommandsBegin + i]; - Assert.AreEqual(expectedMeshIDs[i], cmd.meshID.value, "Incorrect mesh rendered"); - Assert.AreEqual(expectedFlags[i], cmd.flags & BatchDrawCommandFlags.LODCrossFade, "Incorrect flag for the current draw command"); - } - } - }; - - using (var brg = new GPUResidentBatcher(brgContext, cpuDrivenDesc, gpuDrivenProcessor)) - { - brg.OnBeginContextRendering(); - - brg.UpdateRenderers(objIDs.AsArray()); - - var cameraObject = new GameObject("myCamera"); - var mainCamera = cameraObject.AddComponent(); - mainCamera.fieldOfView = 60; - - //Test 0 - (1m) Should render both spheres. - expectedMeshIDs.Add(1); - expectedMeshIDs.Add(1); - expectedFlags.Add(BatchDrawCommandFlags.LODCrossFadeValuePacked); - expectedFlags.Add(BatchDrawCommandFlags.LODCrossFadeValuePacked); - expectedDrawCommandCount = 1; - cameraObject.transform.position = new Vector3(0.0f, 0.0f, -1.0f); - SubmitCameraRenderRequest(mainCamera); - - //Test 1 - (8.5m) Should render sphere1 + crossfaded sphere0. - expectedMeshIDs.Clear(); - expectedMeshIDs.Add(1); - expectedMeshIDs.Add(1); - expectedFlags.Clear(); - expectedFlags.Add(BatchDrawCommandFlags.LODCrossFadeValuePacked); - expectedFlags.Add(BatchDrawCommandFlags.LODCrossFade); - expectedDrawCommandCount = 2; - cameraObject.transform.position = new Vector3(0.0f, 0.0f, -8.5f); - SubmitCameraRenderRequest(mainCamera); - - //Test 2 - (10m) Should only render sphere1. - expectedMeshIDs.Clear(); - expectedMeshIDs.Add(1); - expectedFlags.Clear(); - expectedFlags.Add(BatchDrawCommandFlags.LODCrossFadeValuePacked); - expectedDrawCommandCount = 1; - cameraObject.transform.position = new Vector3(0.0f, 0.0f, -10.0f); - SubmitCameraRenderRequest(mainCamera); - - brg.OnEndContextRendering(); - - mainCamera = null; - GameObject.DestroyImmediate(cameraObject); - - brgContext.ScheduleQueryRendererGroupInstancesJob(objIDs.AsArray(), instances).Complete(); - brg.DestroyDrawInstances(instances); - } - } + m_MeshRendererProcessor.ProcessGameObjectChanges(instanceIDs.AsArray()); + + var cameraObject = new GameObject("myCamera"); + var mainCamera = cameraObject.AddComponent(); + mainCamera.fieldOfView = 60; + + //Test 0 - (1m) Should render both spheres. + expectedMeshIDs.Add(1); + expectedMeshIDs.Add(1); + expectedFlags.Add(BatchDrawCommandFlags.LODCrossFadeValuePacked); + expectedFlags.Add(BatchDrawCommandFlags.LODCrossFadeValuePacked); + expectedDrawCommandCount.Value = 1; + cameraObject.transform.position = new Vector3(0.0f, 0.0f, -1.0f); + SubmitCameraRenderRequest(mainCamera); + + //Test 1 - (8.5m) Should render sphere1 + crossfaded sphere0. + expectedMeshIDs.Clear(); + expectedMeshIDs.Add(1); + expectedMeshIDs.Add(1); + expectedFlags.Clear(); + expectedFlags.Add(BatchDrawCommandFlags.LODCrossFadeValuePacked); + expectedFlags.Add(BatchDrawCommandFlags.LODCrossFade); + expectedDrawCommandCount.Value = 2; + cameraObject.transform.position = new Vector3(0.0f, 0.0f, -8.5f); + SubmitCameraRenderRequest(mainCamera); + + //Test 2 - (10m) Should only render sphere1. + expectedMeshIDs.Clear(); + expectedMeshIDs.Add(1); + expectedFlags.Clear(); + expectedFlags.Add(BatchDrawCommandFlags.LODCrossFadeValuePacked); + expectedDrawCommandCount.Value = 1; + cameraObject.transform.position = new Vector3(0.0f, 0.0f, -10.0f); + SubmitCameraRenderRequest(mainCamera); + + m_MeshRendererProcessor.DestroyInstances(instanceIDs.AsArray()); QualitySettings.lodBias = lastLodBias; - gpuDrivenProcessor.Dispose(); - - objIDs.Dispose(); - instances.Dispose(); - } - - [Test, ConditionalIgnore("IgnoreGfxAPI", "Graphics API Not Supported.")] - public void TestInstanceDataBuffer() - { - var gpuResources = new GPUInstanceDataBufferUploader.GPUResources(); - gpuResources.LoadShaders(m_Resources); - - var meshInstancesCount = 4; - var instanceNumInfo = new InstanceNumInfo(meshRendererNum: meshInstancesCount, 0); - - using (var instanceBuffer = RenderersParameters.CreateInstanceDataBuffer(RenderersParameters.Flags.None, instanceNumInfo)) - { - var renderersParameters = new RenderersParameters(instanceBuffer); - - var instances = new NativeArray(meshInstancesCount, Allocator.TempJob); - var lightmapScaleOffsets = new NativeArray(meshInstancesCount, Allocator.TempJob); - - for (int i = 0; i < meshInstancesCount; ++i) - instances[i] = new GPUInstanceIndex { index = i }; - - for (int i = 0; i < meshInstancesCount; ++i) - lightmapScaleOffsets[i] = Vector4.one * i; - - using (var instanceUploader0 = new GPUInstanceDataBufferUploader(instanceBuffer.descriptions, meshInstancesCount, InstanceType.MeshRenderer)) - { - instanceUploader0.AllocateUploadHandles(instances.Length); - instanceUploader0.WriteInstanceDataJob(renderersParameters.lightmapScale.index, lightmapScaleOffsets).Complete(); - instanceUploader0.SubmitToGpu(instanceBuffer, instances, ref gpuResources, submitOnlyWrittenParams: false); - } - - using (var readbackData = new InstanceDataBufferCPUReadbackData()) - { - if (readbackData.Load(instanceBuffer)) - { - for (int i = 0; i < meshInstancesCount; ++i) - { - var lightmapScaleOffset = readbackData.LoadData(instances[i], RenderersParameters.ParamNames.unity_LightmapST); - Assert.AreEqual(lightmapScaleOffset, lightmapScaleOffsets[i]); - } - } - } - - instances.Dispose(); - lightmapScaleOffsets.Dispose(); - } - gpuResources.Dispose(); - } - - [Test, ConditionalIgnore("IgnoreGfxAPI", "Graphics API Not Supported.")] - public void TestGrowInstanceDataBuffer() - { - var uploadResources = new GPUInstanceDataBufferUploader.GPUResources(); - var growResources = new GPUInstanceDataBufferGrower.GPUResources(); - uploadResources.LoadShaders(m_Resources); - growResources.LoadShaders(m_Resources); - - var meshInstancesCount = 8; - var instanceNumInfo = new InstanceNumInfo(meshRendererNum: meshInstancesCount, 0); - - using (var instanceBuffer = RenderersParameters.CreateInstanceDataBuffer(RenderersParameters.Flags.None, instanceNumInfo)) - { - var renderersParameters = new RenderersParameters(instanceBuffer); - - var instances = new NativeArray(meshInstancesCount, Allocator.TempJob); - var lightmapTextureIndices = new NativeArray(meshInstancesCount, Allocator.TempJob); - var lightmapScaleOffsets = new NativeArray(meshInstancesCount, Allocator.TempJob); - - for (int i = 0; i < meshInstancesCount; ++i) - instances[i] = new GPUInstanceIndex { index = i }; - - for (int i = 0; i < meshInstancesCount; ++i) - lightmapScaleOffsets[i] = Vector4.one * i; - - using (var instanceUploader0 = new GPUInstanceDataBufferUploader(instanceBuffer.descriptions, meshInstancesCount, InstanceType.MeshRenderer)) - { - instanceUploader0.AllocateUploadHandles(instances.Length); - instanceUploader0.WriteInstanceDataJob(renderersParameters.lightmapScale.index, lightmapScaleOffsets).Complete(); - instanceUploader0.SubmitToGpu(instanceBuffer, instances, ref uploadResources, submitOnlyWrittenParams: false); - } - - var instanceGrower = new GPUInstanceDataBufferGrower(instanceBuffer, new InstanceNumInfo(meshRendererNum: meshInstancesCount * 2, 0)); - var newGPUDataBuffer = instanceGrower.SubmitToGpu(ref growResources); - instanceGrower.Dispose(); - - using (var readbackData = new InstanceDataBufferCPUReadbackData()) - { - if (readbackData.Load(newGPUDataBuffer)) - { - for (int i = 0; i < meshInstancesCount; ++i) - { - var lightmapScaleOffset = readbackData.LoadData(instances[i], RenderersParameters.ParamNames.unity_LightmapST); - Assert.AreEqual(lightmapScaleOffset, lightmapScaleOffsets[i]); - } - } - } - - newGPUDataBuffer.Dispose(); + mainCamera = null; + GameObject.DestroyImmediate(cameraObject); - instances.Dispose(); - lightmapTextureIndices.Dispose(); - lightmapScaleOffsets.Dispose(); - } - uploadResources.Dispose(); + instanceIDs.Dispose(); + ShutdownGPUResidentDrawer(); } [Test, ConditionalIgnore("IgnoreGfxAPI", "Graphics API Not Supported.")] public void TestInstanceData() { - var gpuDrivenProcessor = new GPUDrivenProcessor(); - - using (var instanceSystem = new InstanceDataSystem(5, enableBoundingSpheres: false, m_Resources)) - { - var simpleDots = Shader.Find("Unlit/SimpleDots"); - var simpleDotsMat = new Material(simpleDots); - - var gameObjects = new GameObject[7] - { - GameObject.CreatePrimitive(PrimitiveType.Cube), - GameObject.CreatePrimitive(PrimitiveType.Cube), - GameObject.CreatePrimitive(PrimitiveType.Cube), - GameObject.CreatePrimitive(PrimitiveType.Cube), - GameObject.CreatePrimitive(PrimitiveType.Cube), - GameObject.CreatePrimitive(PrimitiveType.Cube), - GameObject.CreatePrimitive(PrimitiveType.Cube) - }; - - foreach(var go in gameObjects) - { - MeshRenderer renderer = go.GetComponent(); - renderer.sharedMaterial = simpleDotsMat; - } - - var renderersID = new NativeArray(3, Allocator.TempJob); - renderersID[0] = gameObjects[0].GetComponent().GetEntityId(); - renderersID[1] = gameObjects[1].GetComponent().GetEntityId(); - renderersID[2] = gameObjects[2].GetComponent().GetEntityId(); - - var lodGroupDataMap = new NativeParallelHashMap(64, Allocator.TempJob); - - gpuDrivenProcessor.EnableGPUDrivenRenderingAndDispatchRendererData(renderersID, (in GPUDrivenRendererGroupData rendererData, IList meshes, IList materials) => - { - var instances = new NativeArray(3, Allocator.TempJob); - instances[0] = InstanceHandle.Invalid; - instances[1] = InstanceHandle.Invalid; - instances[2] = InstanceHandle.Invalid; - - instanceSystem.ReallocateAndGetInstances(rendererData, instances); - instanceSystem.ScheduleUpdateInstanceDataJob(instances, rendererData, lodGroupDataMap).Complete(); - - Assert.IsTrue(instanceSystem.InternalSanityCheckStates()); - - instanceSystem.FreeInstances(instances); + InitializeGPUResidentDrawer(); - instances.Dispose(); - }); - - Assert.IsTrue(instanceSystem.InternalSanityCheckStates()); - - renderersID[0] = gameObjects[3].GetComponent().GetEntityId(); - renderersID[1] = gameObjects[4].GetComponent().GetEntityId(); - renderersID[2] = gameObjects[5].GetComponent().GetEntityId(); - - gpuDrivenProcessor.EnableGPUDrivenRenderingAndDispatchRendererData(renderersID, (in GPUDrivenRendererGroupData rendererData, IList meshes, IList materials) => - { - var instances = new NativeArray(3, Allocator.TempJob); - instances[0] = InstanceHandle.Invalid; - instances[1] = InstanceHandle.Invalid; - instances[2] = InstanceHandle.Invalid; + var simpleDots = Shader.Find("Unlit/SimpleDots"); + var simpleDotsMat = new Material(simpleDots); - instanceSystem.ReallocateAndGetInstances(rendererData, instances); - instanceSystem.ScheduleUpdateInstanceDataJob(instances, rendererData, lodGroupDataMap).Complete(); + var gameObjects = new GameObject[7] + { + GameObject.CreatePrimitive(PrimitiveType.Cube), + GameObject.CreatePrimitive(PrimitiveType.Cube), + GameObject.CreatePrimitive(PrimitiveType.Cube), + GameObject.CreatePrimitive(PrimitiveType.Cube), + GameObject.CreatePrimitive(PrimitiveType.Cube), + GameObject.CreatePrimitive(PrimitiveType.Cube), + GameObject.CreatePrimitive(PrimitiveType.Cube) + }; - Assert.IsTrue(instanceSystem.InternalSanityCheckStates()); + var meshRenderers = new MeshRenderer[gameObjects.Length]; - instanceSystem.FreeInstances(instances); + for (int i = 0; i < meshRenderers.Length;i ++) + meshRenderers[i] = gameObjects[i].GetComponent(); - instances.Dispose(); - }); + foreach (MeshRenderer renderer in meshRenderers) + renderer.sharedMaterial = simpleDotsMat; - Assert.IsTrue(instanceSystem.InternalSanityCheckStates()); + var instanceIDs = new NativeList(8, Allocator.TempJob); - renderersID.Dispose(); + instanceIDs.Add(meshRenderers[0].GetEntityId()); + instanceIDs.Add(meshRenderers[1].GetEntityId()); + instanceIDs.Add(meshRenderers[2].GetEntityId()); + m_MeshRendererProcessor.ProcessGameObjectChanges(instanceIDs.AsArray()); + Assert.IsTrue(m_InstanceDataSystem.AreAllAllocatedInstancesValid()); - renderersID = new NativeArray(1, Allocator.TempJob); - renderersID[0] = gameObjects[6].GetComponent().GetEntityId(); + m_MeshRendererProcessor.DestroyInstances(instanceIDs.AsArray()); + Assert.IsTrue(m_InstanceDataSystem.AreAllAllocatedInstancesValid()); + instanceIDs.Clear(); - gpuDrivenProcessor.EnableGPUDrivenRenderingAndDispatchRendererData(renderersID, (in GPUDrivenRendererGroupData rendererData, IList meshes, IList materials) => - { - var instances = new NativeArray(1, Allocator.TempJob); - instances[0] = InstanceHandle.Invalid; - instanceSystem.ReallocateAndGetInstances(rendererData, instances); - instanceSystem.ScheduleUpdateInstanceDataJob(instances, rendererData, lodGroupDataMap).Complete(); + instanceIDs.Add(meshRenderers[3].GetEntityId()); + instanceIDs.Add(meshRenderers[4].GetEntityId()); + instanceIDs.Add(meshRenderers[5].GetEntityId()); + m_MeshRendererProcessor.ProcessGameObjectChanges(instanceIDs.AsArray()); + Assert.IsTrue(m_InstanceDataSystem.AreAllAllocatedInstancesValid()); - Assert.IsTrue(instanceSystem.InternalSanityCheckStates()); + m_MeshRendererProcessor.DestroyInstances(instanceIDs.AsArray()); + Assert.IsTrue(m_InstanceDataSystem.AreAllAllocatedInstancesValid()); + instanceIDs.Clear(); - instanceSystem.FreeInstances(instances); - instances.Dispose(); - }); + instanceIDs.Add(meshRenderers[6].GetEntityId()); + m_MeshRendererProcessor.ProcessGameObjectChanges(instanceIDs.AsArray()); + Assert.IsTrue(m_InstanceDataSystem.AreAllAllocatedInstancesValid()); - Assert.IsTrue(instanceSystem.InternalSanityCheckStates()); + m_MeshRendererProcessor.DestroyInstances(instanceIDs.AsArray()); + Assert.IsTrue(m_InstanceDataSystem.AreAllAllocatedInstancesValid()); + instanceIDs.Clear(); - renderersID.Dispose(); - lodGroupDataMap.Dispose(); - foreach (var go in gameObjects) - GameObject.DestroyImmediate(go); + foreach (var go in gameObjects) + GameObject.DestroyImmediate(go); - GameObject.DestroyImmediate(simpleDotsMat); - } + GameObject.DestroyImmediate(simpleDotsMat); - gpuDrivenProcessor.Dispose(); + instanceIDs.Dispose(); + ShutdownGPUResidentDrawer(); } [Test, ConditionalIgnore("IgnoreGfxAPI", "Graphics API Not Supported.")] public void TestStaticBatching() { - var gpuDrivenProcessor = new GPUDrivenProcessor(); - - var dispatcher = new ObjectDispatcher(); - dispatcher.EnableTransformTracking(ObjectDispatcher.TransformTrackingType.GlobalTRS); - - using (var brgContext = new RenderersBatchersContext(RenderersBatchersContextDesc.NewDefault(), gpuDrivenProcessor, m_Resources)) - { - var simpleDots = Shader.Find("Unlit/SimpleDots"); - var simpleDotsMat = new Material(simpleDots); + InitializeGPUResidentDrawer(); + ref DefaultGPUComponents defaultGPUComponents = ref m_InstanceDataSystem.defaultGPUComponents; - var staticBatchingRoot = new GameObject(); - staticBatchingRoot.transform.position = new Vector3(10, 0, 0); - - var gameObjects = new GameObject[2] - { - GameObject.CreatePrimitive(PrimitiveType.Cube), - GameObject.CreatePrimitive(PrimitiveType.Cube) - }; - - foreach (var go in gameObjects) - { - MeshRenderer renderer = go.GetComponent(); - renderer.sharedMaterial = simpleDotsMat; - } - - gameObjects[0].transform.position = new Vector3(2, 0, 0); - gameObjects[1].transform.position = new Vector3(-2, 0, 0); - - StaticBatchingUtility.Combine(gameObjects, staticBatchingRoot); + var simpleDots = Shader.Find("Unlit/SimpleDots"); + var simpleDotsMat = new Material(simpleDots); - var renderersID = new NativeArray(2, Allocator.TempJob); - renderersID[0] = gameObjects[0].GetComponent().GetEntityId(); - renderersID[1] = gameObjects[1].GetComponent().GetEntityId(); + var staticBatchingRoot = new GameObject(); + staticBatchingRoot.transform.position = new Vector3(10, 0, 0); - var lodGroupDataMap = new NativeParallelHashMap(64, Allocator.TempJob); - var instances = new NativeArray(2, Allocator.TempJob); - var localToWorldMatrices = new NativeArray(2, Allocator.Temp); + var gameObjects = new GameObject[2] + { + GameObject.CreatePrimitive(PrimitiveType.Cube), + GameObject.CreatePrimitive(PrimitiveType.Cube) + }; - gpuDrivenProcessor.EnableGPUDrivenRenderingAndDispatchRendererData(renderersID, (in GPUDrivenRendererGroupData rendererData, IList meshes, IList materials) => - { - Assert.IsTrue(rendererData.packedRendererData[0].isPartOfStaticBatch); - Assert.IsTrue(rendererData.packedRendererData[1].isPartOfStaticBatch); + foreach (GameObject go in gameObjects) + { + MeshRenderer renderer = go.GetComponent(); + renderer.receiveGI = ReceiveGI.LightProbes; + renderer.lightProbeUsage = LightProbeUsage.BlendProbes; + renderer.sharedMaterial = simpleDotsMat; + } - brgContext.ReallocateAndGetInstances(rendererData, instances); - brgContext.ScheduleUpdateInstanceDataJob(instances, rendererData).Complete(); - brgContext.InitializeInstanceTransforms(instances, rendererData.localToWorldMatrix, rendererData.localToWorldMatrix); + gameObjects[0].transform.position = new Vector3(2, 0, 0); + gameObjects[1].transform.position = new Vector3(-2, 0, 0); - for(int i = 0; i < localToWorldMatrices.Length; ++i) - localToWorldMatrices[i] = rendererData.localToWorldMatrix[i]; - }); + StaticBatchingUtility.Combine(gameObjects, staticBatchingRoot); - gameObjects[0].transform.position = new Vector3(100, 0, 0); - gameObjects[1].transform.position = new Vector3(-100, 0, 0); + var instanceIDs = new NativeArray(2, Allocator.TempJob); + instanceIDs[0] = gameObjects[0].GetComponent().GetEntityId(); + instanceIDs[1] = gameObjects[1].GetComponent().GetEntityId(); - var transfomData = dispatcher.GetTransformChangesAndClear(ObjectDispatcher.TransformTrackingType.GlobalTRS, Allocator.TempJob); - Assert.AreEqual(transfomData.transformedID.Length, 2); + var localToWorldMatrices = new NativeArray(2, Allocator.Temp); + localToWorldMatrices[0] = gameObjects[0].transform.localToWorldMatrix; + localToWorldMatrices[1] = gameObjects[1].transform.localToWorldMatrix; - brgContext.UpdateInstanceTransforms(instances, transfomData.localToWorldMatrices); + m_MeshRendererProcessor.ProcessGameObjectChanges(instanceIDs); - using (var readbackData = new InstanceDataBufferCPUReadbackData()) - { - if (readbackData.Load(brgContext.GetInstanceDataBuffer())) - { - var localToWorldMatrix0 = readbackData.LoadData(instances[0], RenderersParameters.ParamNames.unity_ObjectToWorld); - var localToWorldMatrix1 = readbackData.LoadData(instances[1], RenderersParameters.ParamNames.unity_ObjectToWorld); + var instances = new NativeArray(instanceIDs.Length, Allocator.TempJob); + m_InstanceDataSystem.QueryRendererInstances(instanceIDs, instances); - Assert.AreEqual(localToWorldMatrix0, PackedMatrix.FromMatrix4x4(localToWorldMatrices[0])); - Assert.AreEqual(localToWorldMatrix1, PackedMatrix.FromMatrix4x4(localToWorldMatrices[1])); - } - else - { - Assert.IsTrue(false, "Unable to read instance data."); - } - } + foreach (InstanceHandle instance in instances) + { + int index = m_InstanceDataSystem.renderWorld.HandleToIndex(instance); + InternalMeshRendererSettings settings = m_InstanceDataSystem.renderWorld.rendererSettings[index]; + Assert.IsTrue(settings.IsPartOfStaticBatch); + } - transfomData.Dispose(); - renderersID.Dispose(); - lodGroupDataMap.Dispose(); - instances.Dispose(); + instanceIDs.Dispose(); + instances.Dispose(); - foreach (var go in gameObjects) - GameObject.DestroyImmediate(go); + foreach (var go in gameObjects) + GameObject.DestroyImmediate(go); - GameObject.DestroyImmediate(simpleDotsMat); - GameObject.DestroyImmediate(staticBatchingRoot); - } + GameObject.DestroyImmediate(staticBatchingRoot); + GameObject.DestroyImmediate(simpleDotsMat); - gpuDrivenProcessor.Dispose(); - dispatcher.Dispose(); + ShutdownGPUResidentDrawer(); } [Test, ConditionalIgnore("IgnoreGfxAPI", "Graphics API Not Supported.")] public void TestDisallowGPUDrivenRendering() { + InitializeGPUResidentDrawer(); + var simpleDots = Shader.Find("Unlit/SimpleDots"); var simpleDotsMat = new Material(simpleDots); @@ -1175,44 +917,42 @@ public void TestDisallowGPUDrivenRendering() renderer0.sharedMaterial = simpleDotsMat; renderer1.sharedMaterial = simpleDotsMat; - var rendererIDs = new NativeArray(2, Allocator.Temp); - rendererIDs[0] = renderer0.GetEntityId(); - rendererIDs[1] = renderer1.GetEntityId(); + var instanceIDs = new NativeArray(2, Allocator.Temp); + instanceIDs[0] = renderer0.GetEntityId(); + instanceIDs[1] = renderer1.GetEntityId(); bool dispatched = false; - var gpuDrivenProcessor = new GPUDrivenProcessor(); - - gpuDrivenProcessor.EnableGPUDrivenRenderingAndDispatchRendererData(rendererIDs, (in GPUDrivenRendererGroupData rendererData, IList meshes, IList materials) => + m_GPUDrivenProcessor.EnableGPUDrivenRenderingAndDispatchRendererData(instanceIDs, (in GPUDrivenMeshRendererData rendererData) => { - Assert.IsTrue(rendererData.rendererGroupID.Length == 2); + Assert.IsTrue(rendererData.renderer.Length == 2); dispatched = true; - }, true); + }); Assert.IsTrue(dispatched); dispatched = false; renderer0.allowGPUDrivenRendering = false; - gpuDrivenProcessor.EnableGPUDrivenRenderingAndDispatchRendererData(rendererIDs, (in GPUDrivenRendererGroupData rendererData, IList meshes, IList materials) => + m_GPUDrivenProcessor.EnableGPUDrivenRenderingAndDispatchRendererData(instanceIDs, (in GPUDrivenMeshRendererData rendererData) => { - Assert.IsTrue(rendererData.rendererGroupID.Length == 1); - Assert.IsTrue(rendererData.rendererGroupID[0] == renderer1.GetEntityId()); - Assert.IsTrue(rendererData.invalidRendererGroupID.Length == 1); - Assert.IsTrue(rendererData.invalidRendererGroupID[0] == renderer0.GetEntityId()); + Assert.IsTrue(rendererData.renderer.Length == 1); + Assert.IsTrue(rendererData.renderer[0] == renderer1.GetEntityId()); + Assert.IsTrue(rendererData.invalidRenderer.Length == 1); + Assert.IsTrue(rendererData.invalidRenderer[0] == renderer0.GetEntityId()); dispatched = true; - }, true); + }); Assert.IsTrue(dispatched); dispatched = false; renderer1.allowGPUDrivenRendering = false; - gpuDrivenProcessor.EnableGPUDrivenRenderingAndDispatchRendererData(rendererIDs, (in GPUDrivenRendererGroupData rendererData, IList meshes, IList materials) => + m_GPUDrivenProcessor.EnableGPUDrivenRenderingAndDispatchRendererData(instanceIDs, (in GPUDrivenMeshRendererData rendererData) => { - Assert.IsTrue(rendererData.invalidRendererGroupID.Length == 2); + Assert.IsTrue(rendererData.invalidRenderer.Length == 2); dispatched = true; - }, true); + }); Assert.IsTrue(dispatched); @@ -1220,12 +960,14 @@ public void TestDisallowGPUDrivenRendering() Object.DestroyImmediate(gameObject0); Object.DestroyImmediate(gameObject1); - gpuDrivenProcessor.Dispose(); + ShutdownGPUResidentDrawer(); } [Test, ConditionalIgnore("IgnoreGfxAPI", "Graphics API Not Supported.")] public void TestUnsupportedCallbacks() { + InitializeGPUResidentDrawer(); + var simpleDots = Shader.Find("Unlit/SimpleDots"); var simpleDotsMat = new Material(simpleDots); @@ -1244,25 +986,19 @@ public void TestUnsupportedCallbacks() renderer2.sharedMaterial = simpleDotsMat; renderer3.sharedMaterial = simpleDotsMat; - var rendererIDs = new NativeArray(4, Allocator.Temp); - rendererIDs[0] = renderer0.GetEntityId(); - rendererIDs[1] = renderer1.GetEntityId(); - rendererIDs[2] = renderer2.GetEntityId(); - rendererIDs[3] = renderer3.GetEntityId(); + var instanceIDs = new NativeArray(4, Allocator.Temp); + instanceIDs[0] = renderer0.GetEntityId(); + instanceIDs[1] = renderer1.GetEntityId(); + instanceIDs[2] = renderer2.GetEntityId(); + instanceIDs[3] = renderer3.GetEntityId(); bool dispatched = false; - var gpuDrivenProcessor = new GPUDrivenProcessor(); - - gpuDrivenProcessor.EnableGPUDrivenRenderingAndDispatchRendererData(rendererIDs, (in GPUDrivenRendererGroupData rendererData, IList meshes, IList materials) => + m_GPUDrivenProcessor.EnableGPUDrivenRenderingAndDispatchRendererData(instanceIDs, (in GPUDrivenMeshRendererData rendererData) => { - Assert.IsTrue(rendererData.localBounds.Length == 0); - Assert.IsTrue(rendererData.localToWorldMatrix.Length == 0); - Assert.IsTrue(rendererData.prevLocalToWorldMatrix.Length == 0); - Assert.IsTrue(rendererData.lodGroupID.Length == 0); - Assert.IsTrue(rendererData.rendererGroupID.Length == 4); + Assert.IsTrue(rendererData.renderer.Length == 4); dispatched = true; - }, true); + }); Assert.IsTrue(dispatched); @@ -1272,11 +1008,11 @@ public void TestUnsupportedCallbacks() dispatched = false; - gpuDrivenProcessor.EnableGPUDrivenRenderingAndDispatchRendererData(rendererIDs, (in GPUDrivenRendererGroupData rendererData, IList meshes, IList materials) => + m_GPUDrivenProcessor.EnableGPUDrivenRenderingAndDispatchRendererData(instanceIDs, (in GPUDrivenMeshRendererData rendererData) => { - Assert.IsTrue(rendererData.rendererGroupID.Length == 1); + Assert.IsTrue(rendererData.renderer.Length == 1); dispatched = true; - }, true); + }); Assert.IsTrue(dispatched); @@ -1286,12 +1022,14 @@ public void TestUnsupportedCallbacks() Object.DestroyImmediate(gameObject2); Object.DestroyImmediate(gameObject3); - gpuDrivenProcessor.Dispose(); + ShutdownGPUResidentDrawer(); } [Test, ConditionalIgnore("IgnoreGfxAPI", "Graphics API Not Supported.")] public void TestForceRenderingOff() { + InitializeGPUResidentDrawer(); + var simpleDots = Shader.Find("Unlit/SimpleDots"); var simpleDotsMat = new Material(simpleDots); @@ -1306,19 +1044,17 @@ public void TestForceRenderingOff() renderer0.forceRenderingOff = true; - var rendererIDs = new NativeArray(2, Allocator.Temp); - rendererIDs[0] = renderer0.GetEntityId(); - rendererIDs[1] = renderer1.GetEntityId(); + var instanceIDs = new NativeArray(2, Allocator.Temp); + instanceIDs[0] = renderer0.GetEntityId(); + instanceIDs[1] = renderer1.GetEntityId(); bool dispatched = false; - var gpuDrivenProcessor = new GPUDrivenProcessor(); - - gpuDrivenProcessor.EnableGPUDrivenRenderingAndDispatchRendererData(rendererIDs, (in GPUDrivenRendererGroupData rendererData, IList meshes, IList materials) => + m_GPUDrivenProcessor.EnableGPUDrivenRenderingAndDispatchRendererData(instanceIDs, (in GPUDrivenMeshRendererData rendererData) => { - Assert.IsTrue(rendererData.rendererGroupID.Length == 1); + Assert.IsTrue(rendererData.renderer.Length == 1); dispatched = true; - }, true); + }); Assert.IsTrue(dispatched); @@ -1326,7 +1062,7 @@ public void TestForceRenderingOff() Object.DestroyImmediate(gameObject0); Object.DestroyImmediate(gameObject1); - gpuDrivenProcessor.Dispose(); + ShutdownGPUResidentDrawer(); } class OnWillRenderObjectBehaviour : MonoBehaviour { void OnWillRenderObject() { } } @@ -1336,6 +1072,9 @@ class OnBecameVisibleBehaviour : MonoBehaviour { void OnBecameVisible() { } } [Test, ConditionalIgnore("IgnoreGfxAPI", "Graphics API Not Supported.")] public void TestSimpleSpeedTree() { + InitializeGPUResidentDrawer(supportDitheringCrossFade: true); + ref DefaultGPUComponents defaultGPUComponents = ref m_InstanceDataSystem.defaultGPUComponents; + var simpleSpeedTreeDots = Shader.Find("Unlit/SimpleSpeedTreeDots"); var simpleSpeedTreeDotsMat = new Material(simpleSpeedTreeDots); @@ -1357,61 +1096,63 @@ SpeedTreeWindAsset CreateDummySpeedTreeWindAsset(params object[] args) tree0.AddComponent().windAsset = dummyWindAsset; tree1.AddComponent().windAsset = dummyWindAsset; - var renderers = new List + var renderers = new MeshRenderer[] { tree0.GetComponent(), tree1.GetComponent() }; - var rendererIDs = new NativeList(Allocator.TempJob); + var instanceIDs = new NativeList(Allocator.TempJob); foreach (var renderer in renderers) { renderer.receiveGI = ReceiveGI.LightProbes; renderer.lightProbeUsage = LightProbeUsage.BlendProbes; renderer.material = simpleSpeedTreeDotsMat; - rendererIDs.Add(renderer.GetEntityId()); + instanceIDs.Add(renderer.GetEntityId()); } - var instances = new NativeArray(renderers.Count, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); + GPUComponentSet componentSet = defaultGPUComponents.defaultSpeedTreeComponentSet; + componentSet.AddSet(defaultGPUComponents.lightProbesComponentSet); + GPUArchetypeHandle expectedArchetype = m_InstanceDataSystem.archetypeManager.GetRef().GetOrCreateArchetype(componentSet); - for (int i = 0; i < renderers.Count; ++i) - instances[i] = InstanceHandle.Invalid; + m_MeshRendererProcessor.ProcessGameObjectChanges(instanceIDs.AsArray()); - var gpuDrivenProcessor = new GPUDrivenProcessor(); + var instances = new NativeArray(renderers.Length, Allocator.TempJob); + instances.FillArray(InstanceHandle.Invalid); + m_InstanceDataSystem.QueryRendererInstances(instanceIDs.AsArray(), instances); - using (var context = new RenderersBatchersContext(new RenderersBatchersContextDesc() { instanceNumInfo = new InstanceNumInfo(speedTreeNum: 8), supportDitheringCrossFade = true }, gpuDrivenProcessor, m_Resources)) - using (var batcher = new GPUResidentBatcher(context, InstanceCullingBatcherDesc.NewDefault(), gpuDrivenProcessor)) - { - batcher.UpdateRenderers(rendererIDs.AsArray()); - context.ScheduleQueryRendererGroupInstancesJob(rendererIDs.AsArray(), instances).Complete(); + var instanceIndex0 = m_InstanceDataSystem.renderWorld.HandleToIndex(instances[0]); + InternalMeshRendererSettings settings0 = m_InstanceDataSystem.renderWorld.rendererSettings[instanceIndex0]; + var instanceIndex1 = m_InstanceDataSystem.renderWorld.HandleToIndex(instances[1]); + InternalMeshRendererSettings settings1 = m_InstanceDataSystem.renderWorld.rendererSettings[instanceIndex1]; - Assert.AreEqual(2, instances.Length); - Assert.AreEqual(instances[0].type, InstanceType.SpeedTree); - Assert.AreEqual(instances[1].type, InstanceType.SpeedTree); + Assert.AreEqual(2, m_InstanceDataSystem.totalTreeCount); - Assert.AreEqual(context.GetAliveInstancesOfType(InstanceType.MeshRenderer), 0); - Assert.AreEqual(context.GetAliveInstancesOfType(InstanceType.SpeedTree), 2); + Assert.AreEqual(tree0.GetComponent().GetEntityId(), m_InstanceDataSystem.renderWorld.instanceIDs[instanceIndex0]); + Assert.AreEqual(LightProbeUsage.BlendProbes, settings0.LightProbeUsage); + Assert.IsTrue(settings0.HasTree); - var instanceIndex0 = context.instanceData.InstanceToIndex(instances[0]); - var instanceIndex1 = context.instanceData.InstanceToIndex(instances[1]); - var sharedInstance0 = context.instanceData.sharedInstances[instanceIndex0]; - var sharedInstance1 = context.instanceData.sharedInstances[instanceIndex1]; - var sharedInstanceIndex0 = context.sharedInstanceData.SharedInstanceToIndex(sharedInstance0); - var sharedInstanceIndex1 = context.sharedInstanceData.SharedInstanceToIndex(sharedInstance1); + Assert.AreEqual(tree1.GetComponent().GetEntityId(), m_InstanceDataSystem.renderWorld.instanceIDs[instanceIndex1]); + Assert.AreEqual(LightProbeUsage.BlendProbes, settings1.LightProbeUsage); + Assert.IsTrue(settings1.HasTree); - Assert.AreEqual(context.sharedInstanceData.rendererGroupIDs[sharedInstanceIndex0], tree0.GetComponent().GetEntityId()); - Assert.AreEqual(context.sharedInstanceData.rendererGroupIDs[sharedInstanceIndex1], tree1.GetComponent().GetEntityId()); + Assert.AreEqual(2, instances.Length); + Assert.AreEqual(0, m_InstanceDataSystem.GetGPUArchetypeAliveInstancesCount(defaultGPUComponents.defaultGOArchetype)); + Assert.AreEqual(2, m_InstanceDataSystem.GetGPUArchetypeAliveInstancesCount(expectedArchetype)); - context.FreeInstances(instances); + m_MeshRendererProcessor.DestroyInstances(instanceIDs.AsArray()); - Assert.AreEqual(context.GetAliveInstancesOfType(InstanceType.SpeedTree), 0); - } + Assert.AreEqual(0, m_InstanceDataSystem.GetGPUArchetypeAliveInstancesCount(defaultGPUComponents.defaultGOArchetype)); + Assert.AreEqual(0, m_InstanceDataSystem.GetGPUArchetypeAliveInstancesCount(expectedArchetype)); - gpuDrivenProcessor.Dispose(); + GameObject.DestroyImmediate(tree0); + GameObject.DestroyImmediate(tree1); instances.Dispose(); - rendererIDs.Dispose(); + instanceIDs.Dispose(); + ShutdownGPUResidentDrawer(); + dummyWindAsset = null; } } } diff --git a/Packages/com.unity.render-pipelines.core/Tests/Editor/GPUDriven/GPUDrivenRenderingUtils.cs b/Packages/com.unity.render-pipelines.core/Tests/Editor/GPUDriven/GPUDrivenRenderingUtils.cs index c6a6ade5751..bcee0ad2dd7 100644 --- a/Packages/com.unity.render-pipelines.core/Tests/Editor/GPUDriven/GPUDrivenRenderingUtils.cs +++ b/Packages/com.unity.render-pipelines.core/Tests/Editor/GPUDriven/GPUDrivenRenderingUtils.cs @@ -109,61 +109,6 @@ public void Dispose() } } - //Helper class containing a snapshot of the GPU big instance buffer data - internal struct InstanceDataBufferCPUReadbackData : IDisposable - { - public NativeArray data; - GPUInstanceDataBuffer m_InstanceDataBuffer; - - public bool Load(GPUInstanceDataBuffer instanceDataBuffer) - { - int errorCount = 0; - m_InstanceDataBuffer = instanceDataBuffer; - var cmdBuffer = new CommandBuffer(); - int uintSize = UnsafeUtility.SizeOf(); - var localData = new NativeArray(instanceDataBuffer.byteSize / uintSize, Allocator.Persistent); - cmdBuffer.RequestAsyncReadback(instanceDataBuffer.gpuBuffer, (AsyncGPUReadbackRequest req) => - { - if (req.done) - localData.CopyFrom(req.GetData()); - else ++errorCount; - }); - cmdBuffer.WaitAllAsyncReadbackRequests(); - Graphics.ExecuteCommandBuffer(cmdBuffer); - cmdBuffer.Release(); - data = localData; - if (errorCount != 0) - Debug.LogError("GPU Readback fail: Instance buffer data. Abandoning test."); - return errorCount == 0; - } - - public T LoadData(InstanceHandle instance, int propertyID) where T : unmanaged - { - return LoadData(m_InstanceDataBuffer.CPUInstanceToGPUInstance(instance), propertyID); - } - - public T LoadData(GPUInstanceIndex gpuInstanceIndex, int propertyID) where T : unmanaged - { - int uintSize = UnsafeUtility.SizeOf(); - int propertyIndex = m_InstanceDataBuffer.GetPropertyIndex(propertyID); - Assert.IsTrue(m_InstanceDataBuffer.descriptions[propertyIndex].isPerInstance); - int gpuBaseAddress = m_InstanceDataBuffer.gpuBufferComponentAddress[propertyIndex]; - int indexInArray = (gpuBaseAddress + m_InstanceDataBuffer.descriptions[propertyIndex].byteSize * gpuInstanceIndex.index) / uintSize; - - unsafe - { - uint* dataPtr = (uint*)data.GetUnsafePtr() + indexInArray; - T result = *(T*)(dataPtr); - return result; - } - } - - public void Dispose() - { - data.Dispose(); - } - } - internal class RenderPassTest : RenderPipelineAsset { public delegate void TestDelegate(ScriptableRenderContext ctx, Camera[] cameras); diff --git a/Packages/com.unity.render-pipelines.core/Tests/Editor/GPUDriven/ParallelSortTests.cs b/Packages/com.unity.render-pipelines.core/Tests/Editor/GPUDriven/ParallelSortTests.cs new file mode 100644 index 00000000000..1bcd91f55bf --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Tests/Editor/GPUDriven/ParallelSortTests.cs @@ -0,0 +1,88 @@ +using System; +using NUnit.Framework; +using Unity.Collections; +using UnityEngine.Rendering; + +#if UNITY_EDITOR +using UnityEditor.Rendering; +#endif + +namespace UnityEngine.Rendering.Tests +{ + class ParallelSortTests + { + const int kSmallArraySize = 17; + const int kLargeArraySize = ParallelSortExtensions.kMinRadixSortArraySize * 3; + + private struct RNG + { + public ulong seed; + + public ulong GetNext() + { + seed = seed * 1664525 + 1013904223; + return seed; + } + } + + static void VerifyOrder(NativeList array) where T : unmanaged, IComparable + { + T prevValue = array[0]; + + for (int i = 1; i < array.Length; i++) + { + var value = array[i]; + Assert.IsTrue(value.CompareTo(prevValue) >= 0); + prevValue = value; + } + } + + void TestParallelSortInt(RNG data, NativeList array, int size) + { + array.Clear(); + for (int i = 0; i < size; i++) + { + array.Add((int)data.GetNext()); + } + array.AsArray().ParallelSort().Complete(); + VerifyOrder(array); + } + + void TestParallelSortULong(RNG data, NativeList array, int size) + { + array.Clear(); + for (int i = 0; i < size; i++) + { + array.Add(data.GetNext()); + } + array.AsArray().ParallelSort().Complete(); + VerifyOrder(array); + } + + [Test] + public void TestParallelSortInt() + { + NativeList array = new NativeList(Allocator.Temp); + RNG data = new RNG { seed = 12345678 }; + + TestParallelSortInt(data, array, 1); + TestParallelSortInt(data, array, kSmallArraySize); + TestParallelSortInt(data, array, kLargeArraySize); + + array.Dispose(); + } + + [Test] + public void TestParallelSortULong() + { + NativeList array = new NativeList(Allocator.Temp); + RNG data = new RNG { seed = 12345678 }; + + TestParallelSortULong(data, array, 1); + TestParallelSortULong(data, array, kSmallArraySize); + TestParallelSortULong(data, array, kLargeArraySize); + + array.Dispose(); + } + } +} diff --git a/Packages/com.unity.render-pipelines.core/Tests/Editor/GPUDriven/ParallelSortTests.cs.meta b/Packages/com.unity.render-pipelines.core/Tests/Editor/GPUDriven/ParallelSortTests.cs.meta new file mode 100644 index 00000000000..9cb572317b3 --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Tests/Editor/GPUDriven/ParallelSortTests.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 1252b259c66f14d4fa64a647d154373e diff --git a/Packages/com.unity.render-pipelines.core/Tests/Editor/NativePassCompilerRenderGraphTests.cs b/Packages/com.unity.render-pipelines.core/Tests/Editor/NativePassCompilerRenderGraphTests.cs index a033dc2e052..8f961ade5e0 100644 --- a/Packages/com.unity.render-pipelines.core/Tests/Editor/NativePassCompilerRenderGraphTests.cs +++ b/Packages/com.unity.render-pipelines.core/Tests/Editor/NativePassCompilerRenderGraphTests.cs @@ -2338,5 +2338,102 @@ public void CompactedNonCulledRasterPassesWorks_LastPassIsCulled() // They are now contiguous in memory. Assert.IsTrue(passes[1].firstCompactedNonCulledRasterPass == 0 && passes[1].lastCompactedNonCulledRasterPass == 1); } + + [Test] + public void NonCulledPassIndicesAreClearedBetweenRenderGraphs() + { + var g = AllocateRenderGraph(); + var renderTargets = ImportAndCreateRenderTargets(g); + + // New native renderpass + using (var builder = g.AddRasterRenderPass("Pass0", out var passData)) + { + builder.SetRenderAttachment(renderTargets.extraTextures[1], 0); + builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { }); + builder.AllowPassCulling(false); + } + + // Same attachments, we should merge in the same subpass as Pass3's one + // This pass is being culled + using (var builder = g.AddRasterRenderPass("Pass1", out var passData)) + { + builder.SetRenderAttachment(renderTargets.extraTextures[2], 0); + builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { }); + builder.AllowPassCulling(true); + } + + // Same attachments, we should merge in the same subpass as Pass4's one + using (var builder = g.AddRasterRenderPass("Pass2", out var passData)) + { + builder.SetRenderAttachment(renderTargets.extraTextures[1], 0); + builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { }); + builder.AllowPassCulling(false); + } + + // New native renderpass + using (var builder = g.AddRasterRenderPass("Pass3", out var passData)) + { + builder.SetRenderAttachment(renderTargets.extraTextures[1], 0); + builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { }); + builder.AllowPassCulling(false); + } + + // Same attachments, we should merge in the same subpass as Pass3's one + // This pass is being culled + using (var builder = g.AddRasterRenderPass("Pass4", out var passData)) + { + builder.SetRenderAttachment(renderTargets.extraTextures[2], 0); + builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { }); + builder.AllowPassCulling(true); + } + + // Same attachments, we should merge in the same subpass as Pass4's one + using (var builder = g.AddRasterRenderPass("Pass5", out var passData)) + { + builder.SetRenderAttachment(renderTargets.extraTextures[1], 0); + builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { }); + builder.AllowPassCulling(false); + } + + var result = g.CompileNativeRenderGraph(g.ComputeGraphHash()); + + // 2 passes are culled, so the optimization culled system should be active. + Assert.IsTrue(result.m_NonCulledPassIndicesForRasterPasses.Length != 0); + + g.ClearCurrentCompiledGraph(); + g.m_RenderGraphPool.Cleanup(); + + renderTargets = ImportAndCreateRenderTargets(g); + + // New native renderpass + using (var builder = g.AddRasterRenderPass("Pass6", out var passData)) + { + builder.SetRenderAttachment(renderTargets.extraTextures[1], 0); + builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { }); + builder.AllowPassCulling(false); + } + + // Same attachments, we should merge in the same subpass as Pass3's one + // This pass is being culled + using (var builder = g.AddRasterRenderPass("Pass7", out var passData)) + { + builder.SetRenderAttachment(renderTargets.extraTextures[2], 0); + builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { }); + builder.AllowPassCulling(false); + } + + // Same attachments, we should merge in the same subpass as Pass4's one + using (var builder = g.AddRasterRenderPass("Pass8", out var passData)) + { + builder.SetRenderAttachment(renderTargets.extraTextures[1], 0); + builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { }); + builder.AllowPassCulling(false); + } + + result = g.CompileNativeRenderGraph(g.ComputeGraphHash()); + + // No passes are culled, so the optimization culled system must be disabled and cleared. + Assert.IsTrue(result.m_NonCulledPassIndicesForRasterPasses.Length == 0); + } } } diff --git a/Packages/com.unity.render-pipelines.core/Tests/Editor/UnifiedRayTracing/TraceTransparentRays.urtshader b/Packages/com.unity.render-pipelines.core/Tests/Editor/UnifiedRayTracing/TraceTransparentRays.urtshader index 03686c9de4d..0dcc4bcdd3a 100644 --- a/Packages/com.unity.render-pipelines.core/Tests/Editor/UnifiedRayTracing/TraceTransparentRays.urtshader +++ b/Packages/com.unity.render-pipelines.core/Tests/Editor/UnifiedRayTracing/TraceTransparentRays.urtshader @@ -17,7 +17,7 @@ int _AnyHitDecision; uint AnyHitExecute(UnifiedRT::HitContext hitContext, inout RayPayload payload) { - payload.anyHits |= (1 << hitContext.InstanceID()); + payload.anyHits |= (1u << hitContext.InstanceID()); return _AnyHitDecision; } diff --git a/Packages/com.unity.render-pipelines.core/package.json b/Packages/com.unity.render-pipelines.core/package.json index 7b6384bbbd9..3eb42c61025 100644 --- a/Packages/com.unity.render-pipelines.core/package.json +++ b/Packages/com.unity.render-pipelines.core/package.json @@ -6,7 +6,6 @@ "displayName": "Scriptable Render Pipeline Core", "dependencies": { "com.unity.burst": "1.8.14", - "com.unity.mathematics": "1.3.2", "com.unity.collections": "2.4.3", "com.unity.modules.terrain": "1.0.0", "com.unity.modules.jsonserialize": "1.0.0" diff --git a/Packages/com.unity.render-pipelines.high-definition/Editor/BuildProcessors/HDRPBuildData.cs b/Packages/com.unity.render-pipelines.high-definition/Editor/BuildProcessors/HDRPBuildData.cs index 08a985da7e7..148eb9b848f 100644 --- a/Packages/com.unity.render-pipelines.high-definition/Editor/BuildProcessors/HDRPBuildData.cs +++ b/Packages/com.unity.render-pipelines.high-definition/Editor/BuildProcessors/HDRPBuildData.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using UnityEditor.SceneManagement; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.Rendering.HighDefinition; @@ -17,7 +16,6 @@ internal class HDRPBuildData : IDisposable public List renderPipelineAssets { get; private set; } = new List(); public bool playerNeedRaytracing { get; private set; } public bool stripDebugVariants { get; private set; } = true; - public bool dynamicLightmapsUsed { get; private set; } public bool waterDecalMaskAndCurrent { get; private set; } public Dictionary rayTracingComputeShaderCache { get; private set; } = new(); public Dictionary computeShaderCache { get; private set; } = new(); @@ -71,35 +69,6 @@ public HDRPBuildData(BuildTarget buildTarget, bool isDevelopmentBuild) m_Instance = this; } - public void SetDynamicLightmapsUsedInBuildScenes() - { - dynamicLightmapsUsed = DynamicLightmapsUsedInBuildScenes(); - } - - static bool DynamicLightmapsUsedInBuildScenes() - { - var originalSetup = EditorSceneManager.GetSceneManagerSetup(); - - bool dynamicLightmapsUsed = false; - foreach (EditorBuildSettingsScene scene in EditorBuildSettings.scenes) - { - if (!scene.enabled) continue; - - EditorSceneManager.OpenScene(scene.path, OpenSceneMode.Single); - - if (Lightmapping.HasDynamicGILightmapTextures()) - { - dynamicLightmapsUsed = true; - break; - } - } - - if (originalSetup.Length > 0) - EditorSceneManager.RestoreSceneManagerSetup(originalSetup); - - return dynamicLightmapsUsed; - } - public void Dispose() { renderPipelineAssets?.Clear(); @@ -107,7 +76,6 @@ public void Dispose() computeShaderCache?.Clear(); playerNeedRaytracing = false; stripDebugVariants = true; - dynamicLightmapsUsed = false; waterDecalMaskAndCurrent = false; buildingPlayerForHDRenderPipeline = false; runtimeShaders = null; diff --git a/Packages/com.unity.render-pipelines.high-definition/Editor/BuildProcessors/HDRPPreprocessBuild.cs b/Packages/com.unity.render-pipelines.high-definition/Editor/BuildProcessors/HDRPPreprocessBuild.cs index b75c370ff69..5a9a93713c7 100644 --- a/Packages/com.unity.render-pipelines.high-definition/Editor/BuildProcessors/HDRPPreprocessBuild.cs +++ b/Packages/com.unity.render-pipelines.high-definition/Editor/BuildProcessors/HDRPPreprocessBuild.cs @@ -33,9 +33,6 @@ public void OnPreprocessBuild(BuildReport report) bool isDevelopmentBuild = (report.summary.options & BuildOptions.Development) != 0; m_BuildData = new HDRPBuildData(EditorUserBuildSettings.activeBuildTarget, isDevelopmentBuild); - // Since the HDRPBuildData instance is used in a lot of places, doing this check here ensures that it is done before the build starts. - m_BuildData.SetDynamicLightmapsUsedInBuildScenes(); - if (m_BuildData.buildingPlayerForHDRenderPipeline) { // Now that we know that we are on HDRP we need to make sure everything is correct, otherwise we break the build. diff --git a/Packages/com.unity.render-pipelines.high-definition/Editor/BuildProcessors/HDRPPreprocessShaders.cs b/Packages/com.unity.render-pipelines.high-definition/Editor/BuildProcessors/HDRPPreprocessShaders.cs index bc6a6b9754f..c5ff8626778 100644 --- a/Packages/com.unity.render-pipelines.high-definition/Editor/BuildProcessors/HDRPPreprocessShaders.cs +++ b/Packages/com.unity.render-pipelines.high-definition/Editor/BuildProcessors/HDRPPreprocessShaders.cs @@ -1,4 +1,3 @@ -using UnityEditor.SceneManagement; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.Rendering.HighDefinition; @@ -71,10 +70,8 @@ protected override bool DoShadersStripper(HDRenderPipelineAsset hdrpAsset, Shade // Remove editor only pass bool isSceneSelectionPass = snippet.passName == "SceneSelectionPass"; bool isScenePickingPass = snippet.passName == "ScenePickingPass"; - - bool isEnlightenSupported = SupportedRenderingFeatures.active.enlighten && ((int)SupportedRenderingFeatures.active.lightmapBakeTypes | (int)LightmapBakeType.Realtime) != 0; - bool metaPassUnused = (snippet.passName == "META") && (!isEnlightenSupported || !HDRPBuildData.instance.dynamicLightmapsUsed); - + bool metaPassUnused = (snippet.passName == "META") && (SupportedRenderingFeatures.active.enlighten == false || + ((int)SupportedRenderingFeatures.active.lightmapBakeTypes | (int)LightmapBakeType.Realtime) == 0); bool editorVisualization = inputData.shaderKeywordSet.IsEnabled(m_EditorVisualization); if (isSceneSelectionPass || isScenePickingPass || metaPassUnused || editorVisualization) return true; diff --git a/Packages/com.unity.render-pipelines.high-definition/Runtime/Lighting/GlobalIlluminationUtils.cs b/Packages/com.unity.render-pipelines.high-definition/Runtime/Lighting/GlobalIlluminationUtils.cs index 38dff4b9d78..d1a54547ee3 100644 --- a/Packages/com.unity.render-pipelines.high-definition/Runtime/Lighting/GlobalIlluminationUtils.cs +++ b/Packages/com.unity.render-pipelines.high-definition/Runtime/Lighting/GlobalIlluminationUtils.cs @@ -79,16 +79,6 @@ public static bool LightDataGIExtract(Light light, ref LightDataGI lightDataGI) lightDataGI.shadow = (byte)(light.shadows != LightShadows.None ? 1 : 0); LightType lightType = add.legacyLight.type; - if (!lightType.IsArea()) - { - // For HDRP we need to divide the analytic light color by PI (HDRP do explicit PI division for Lambert, but built in Unity and the GI don't for punctual lights) - // We apply it on both direct and indirect are they are separated, seems that direct is no used if we used mixed mode with indirect or shadowmask bake. - lightDataGI.color.intensity /= Mathf.PI; - lightDataGI.indirectColor.intensity /= Mathf.PI; - directColor.intensity /= Mathf.PI; - indirectColor.intensity /= Mathf.PI; - } - switch (lightType) { case LightType.Directional: diff --git a/Packages/com.unity.render-pipelines.high-definition/Runtime/Material/Water/Water.hlsl b/Packages/com.unity.render-pipelines.high-definition/Runtime/Material/Water/Water.hlsl index b7aa880f3ed..50f64e95860 100644 --- a/Packages/com.unity.render-pipelines.high-definition/Runtime/Material/Water/Water.hlsl +++ b/Packages/com.unity.render-pipelines.high-definition/Runtime/Material/Water/Water.hlsl @@ -415,9 +415,6 @@ PreLightData GetPreLightData(float3 V, PositionInputs posInput, inout BSDFData b // Grab the water profile of this surface WaterSurfaceProfile profile = _WaterSurfaceProfiles[bsdfData.surfaceIndex]; - // Make sure to apply the smoothness fade - EvaluateSmoothnessFade(posInput.positionWS, profile, bsdfData); - // Profile data preLightData.tipScatteringHeight = profile.tipScatteringHeight; preLightData.bodyScatteringHeight = profile.bodyScatteringHeight; diff --git a/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.Debug.cs b/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.Debug.cs index cf40a2ae781..86ccba8c462 100644 --- a/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.Debug.cs +++ b/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.Debug.cs @@ -1629,7 +1629,11 @@ TextureHandle RenderDebugViewMaterial(RenderGraph renderGraph, CullingResults cu m_WaterSystem.RenderWaterDebug(renderGraph, hdCamera, output, depth); // Render the debug lines. - RenderLines(renderGraph, depthBuffer, hdCamera, lightLists); + TransparentPrepassOutput prepassOutput = default; + prepassOutput.depthBufferPreRefraction = depthBuffer; + m_WaterSystem.InitializeWaterPrepassOutput(renderGraph, ref prepassOutput); + + RenderLines(renderGraph, depthBuffer, hdCamera, prepassOutput, lightLists); ComposeLines(renderGraph, hdCamera, output, depth, TextureHandle.nullHandle, -1); using (var builder = renderGraph.AddUnsafePass("DisplayDebug ViewMaterial", out var passData, ProfilingSampler.Get(HDProfileId.DisplayDebugViewMaterial))) diff --git a/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.LightLoop.cs b/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.LightLoop.cs index 94e24c4ace4..8b379232b18 100644 --- a/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.LightLoop.cs +++ b/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.LightLoop.cs @@ -374,7 +374,7 @@ static void BuildDispatchIndirectArguments(BuildGPULightListPassData data, bool // clear dispatch indirect buffer cmd.SetComputeBufferParam(data.clearDispatchIndirectShader, s_ClearDispatchIndirectKernel, HDShaderIDs.g_DispatchIndirectBuffer, data.output.dispatchIndirectBuffer); - cmd.DispatchCompute(data.clearDispatchIndirectShader, s_ClearDispatchIndirectKernel, 1, 1, 1); + cmd.DispatchCompute(data.clearDispatchIndirectShader, s_ClearDispatchIndirectKernel, HDUtils.DivRoundUp(LightDefinitions.s_NumFeatureVariants, k_ThreadGroupOptimalSize), 1, data.viewCount); // add tiles to indirect buffer cmd.SetComputeBufferParam(data.buildDispatchIndirectShader, s_BuildIndirectKernel, HDShaderIDs.g_DispatchIndirectBuffer, data.output.dispatchIndirectBuffer); diff --git a/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.PostProcess.cs b/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.PostProcess.cs index a336135d697..3692a07df93 100644 --- a/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.PostProcess.cs +++ b/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.PostProcess.cs @@ -4142,6 +4142,7 @@ TextureHandle LensFlareScreenSpacePass(RenderGraph renderGraph, HDCamera hdCamer int ratio = (int)m_LensFlareScreenSpace.resolution.value; Color tintColor = m_LensFlareScreenSpace.tintColor.value; + int bloomMip = m_LensFlareScreenSpace.bloomMip.value; using (var builder = renderGraph.AddUnsafePass("Lens Flare Screen Space", out var passData, ProfilingSampler.Get(HDProfileId.LensFlareScreenSpace))) { @@ -4151,7 +4152,10 @@ TextureHandle LensFlareScreenSpacePass(RenderGraph renderGraph, HDCamera hdCamer passData.viewport = postProcessViewportSize; passData.hdCamera = hdCamera; passData.screenSpaceLensFlareBloomMipTexture = screenSpaceLensFlareBloomMipTexture; - builder.UseTexture(passData.screenSpaceLensFlareBloomMipTexture, AccessFlags.ReadWrite); + // NOTE: SSLF mip texture is usually the bloom.mip[N] and the BloomTexture is bloom.mip[0]. Sometimes N == 0 which causes double UseTexture error. + // Check if we are trying to use the same texture twice in the RG. + if(bloomMip != 0) + builder.UseTexture(passData.screenSpaceLensFlareBloomMipTexture, AccessFlags.ReadWrite); passData.originalBloomTexture = originalBloomTexture; builder.UseTexture(passData.originalBloomTexture, AccessFlags.ReadWrite); diff --git a/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.RenderGraph.cs b/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.RenderGraph.cs index dbfef7bd6ba..18db752b227 100644 --- a/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.RenderGraph.cs +++ b/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDRenderPipeline.RenderGraph.cs @@ -1788,7 +1788,7 @@ TextureHandle RenderTransparency(RenderGraph renderGraph, RenderProbeVolumeDebug(renderGraph, hdCamera, prepassOutput.depthPyramidTexture, normalBuffer); // Render the software line raster path. - RenderLines(m_RenderGraph, prepassOutput.depthPyramidTexture, hdCamera, lightLists); + RenderLines(m_RenderGraph, prepassOutput.depthPyramidTexture, hdCamera, transparentPrepass, lightLists); // Immediately compose the lines if the user wants lines in the color pyramid (refraction), but with poor TAA ghosting. ComposeLines(renderGraph, hdCamera, colorBuffer, prepassOutput.resolvedDepthBuffer, prepassOutput.motionVectorsBuffer, (int)LineRendering.CompositionMode.BeforeColorPyramid); diff --git a/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/LineRendering/HDRenderPipeline.LineRendering.cs b/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/LineRendering/HDRenderPipeline.LineRendering.cs index 225579136f1..883478fd6cb 100644 --- a/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/LineRendering/HDRenderPipeline.LineRendering.cs +++ b/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/LineRendering/HDRenderPipeline.LineRendering.cs @@ -108,6 +108,13 @@ class LineRendererSetupData public BufferHandle perVoxelOffset; public BufferHandle perTileLogBaseTweak; + public BufferHandle waterLine; + public BufferHandle cameraHeightBuffer; + public BufferHandle waterSurfaceProfiles; + public TextureHandle waterGBuffer3; + + public TextureHandle waterDepthStencil; + public TextureHandle targetColor; public TextureHandle targetDepth; public TextureHandle targetMV; @@ -154,7 +161,7 @@ internal static bool LineRenderingIsEnabled(HDCamera hdCamera, out HighQualityLi return true; } - void RenderLines(RenderGraph renderGraph, in TextureHandle depthPrepassTexture, HDCamera hdCamera, BuildGPULightListOutput lightLists) + void RenderLines(RenderGraph renderGraph, in TextureHandle depthPrepassTexture, HDCamera hdCamera, in TransparentPrepassOutput transparentPrepass, BuildGPULightListOutput lightLists) { if (!LineRenderingIsEnabled(hdCamera, out var settings)) return; @@ -171,6 +178,18 @@ void RenderLines(RenderGraph renderGraph, in TextureHandle depthPrepassTexture, passData.perTileLogBaseTweak = lightLists.perTileLogBaseTweak; builder.UseBuffer(passData.perTileLogBaseTweak, AccessFlags.Read); + // Water absorption buffers + passData.waterDepthStencil = transparentPrepass.depthBufferPreRefraction; + builder.UseTexture(passData.waterDepthStencil, AccessFlags.Read); + passData.waterSurfaceProfiles = transparentPrepass.waterSurfaceProfiles; + builder.UseBuffer(passData.waterSurfaceProfiles, AccessFlags.Read); + passData.cameraHeightBuffer = transparentPrepass.waterGBuffer.cameraHeight; + builder.UseBuffer(passData.cameraHeightBuffer, AccessFlags.Read); + passData.waterLine = transparentPrepass.waterLine; + builder.UseBuffer(passData.waterLine, AccessFlags.Read); + passData.waterGBuffer3 = transparentPrepass.waterGBuffer.waterGBuffer3; + builder.UseTexture(passData.waterGBuffer3, AccessFlags.Read); + passData.targetColor = m_LineColorBuffer; builder.UseTexture(passData.targetColor, AccessFlags.Write); passData.targetDepth = m_LineDepthBuffer; @@ -196,6 +215,17 @@ void RenderLines(RenderGraph renderGraph, in TextureHandle depthPrepassTexture, CoreUtils.SetKeyword(natCmd, "USE_CLUSTERED_LIGHTLIST", true); } + // Water absorption buffers + { + natCmd.SetGlobalTexture(HDShaderIDs._StencilTexture, data.waterDepthStencil, RenderTextureSubElement.Stencil); + natCmd.SetGlobalTexture(HDShaderIDs._RefractiveDepthBuffer, data.waterDepthStencil, RenderTextureSubElement.Depth); + + natCmd.SetGlobalBuffer(HDShaderIDs._WaterCameraHeightBuffer, data.cameraHeightBuffer); + natCmd.SetGlobalBuffer(HDShaderIDs._WaterSurfaceProfiles, data.waterSurfaceProfiles); + natCmd.SetGlobalBuffer(HDShaderIDs._WaterLineBuffer, data.waterLine); + natCmd.SetGlobalTexture(HDShaderIDs._WaterGBufferTexture3, data.waterGBuffer3); + } + // Clear the internal targets. CoreUtils.SetRenderTarget(natCmd, data.targetColor, ClearFlag.Color, Color.black); CoreUtils.SetRenderTarget(natCmd, data.targetDepth, ClearFlag.Color, Color.black); diff --git a/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/LineRendering/Kernels/StageRasterBin.compute b/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/LineRendering/Kernels/StageRasterBin.compute index 0825ed4f0c2..10a7fb045d3 100644 --- a/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/LineRendering/Kernels/StageRasterBin.compute +++ b/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/LineRendering/Kernels/StageRasterBin.compute @@ -109,7 +109,8 @@ void Main(Group group) uint2 v_segmentMin, v_segmentMax; - if(s_segmentIndex < gs_ActiveSegmentCount) + // Dynamic branch must be enforced to avoid out-of-bounds memory access in _Vertex1RecordBuffer on certain platforms. + UNITY_BRANCH if(s_segmentIndex < gs_ActiveSegmentCount) { const float w0 = asfloat(_Vertex1RecordBuffer.Load((s_segment.vertexIndex0 << 4) + 12u)); const float w1 = asfloat(_Vertex1RecordBuffer.Load((s_segment.vertexIndex1 << 4) + 12u)); diff --git a/Packages/com.unity.render-pipelines.high-definition/Runtime/Water/Shaders/ShaderPassWaterGBuffer.hlsl b/Packages/com.unity.render-pipelines.high-definition/Runtime/Water/Shaders/ShaderPassWaterGBuffer.hlsl index d19a9c1cd93..f3fe34d7ed9 100644 --- a/Packages/com.unity.render-pipelines.high-definition/Runtime/Water/Shaders/ShaderPassWaterGBuffer.hlsl +++ b/Packages/com.unity.render-pipelines.high-definition/Runtime/Water/Shaders/ShaderPassWaterGBuffer.hlsl @@ -51,6 +51,9 @@ void Frag(PackedVaryingsToPS packedInput, // Compute the BSDF Data BSDFData bsdfData = ConvertSurfaceDataToBSDFData(input.positionSS.xy, surfaceData); + WaterSurfaceProfile profile = _WaterSurfaceProfiles[bsdfData.surfaceIndex]; + EvaluateSmoothnessFade(posInput.positionWS, profile, bsdfData); + // If the camera is in the underwater region of this surface and the the camera is under the surface #if defined(SHADER_STAGE_FRAGMENT) diff --git a/Packages/com.unity.render-pipelines.high-definition/Samples~/ParticleSystemShaderSamples/Scenes/ParticleShaderGraphsSampleScene.unity b/Packages/com.unity.render-pipelines.high-definition/Samples~/ParticleSystemShaderSamples/Scenes/ParticleShaderGraphsSampleScene.unity index 9fd94245627..aab3686f92c 100644 --- a/Packages/com.unity.render-pipelines.high-definition/Samples~/ParticleSystemShaderSamples/Scenes/ParticleShaderGraphsSampleScene.unity +++ b/Packages/com.unity.render-pipelines.high-definition/Samples~/ParticleSystemShaderSamples/Scenes/ParticleShaderGraphsSampleScene.unity @@ -13,7 +13,7 @@ OcclusionCullingSettings: --- !u!104 &2 RenderSettings: m_ObjectHideFlags: 0 - serializedVersion: 9 + serializedVersion: 10 m_Fog: 0 m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} m_FogMode: 3 @@ -38,13 +38,12 @@ RenderSettings: m_ReflectionIntensity: 1 m_CustomReflection: {fileID: 0} m_Sun: {fileID: 705507994} - m_IndirectSpecularColor: {r: 0, g: 0, b: 0, a: 1} m_UseRadianceAmbientProbe: 0 --- !u!157 &3 LightmapSettings: m_ObjectHideFlags: 0 - serializedVersion: 12 - m_GIWorkflowMode: 0 + serializedVersion: 13 + m_BakeOnSceneLoad: 1 m_GISettings: serializedVersion: 2 m_BounceScale: 1 @@ -67,9 +66,6 @@ LightmapSettings: m_LightmapParameters: {fileID: 0} m_LightmapsBakeMode: 1 m_TextureCompression: 1 - m_FinalGather: 0 - m_FinalGatherFiltering: 1 - m_FinalGatherRayCount: 256 m_ReflectionCompression: 2 m_MixedBakeMode: 2 m_BakeBackend: 1 @@ -97,14 +93,14 @@ LightmapSettings: m_ExportTrainingData: 0 m_TrainingDataDestination: TrainingData m_LightProbeSampleCountMultiplier: 4 - m_LightingDataAsset: {fileID: 0} + m_LightingDataAsset: {fileID: 112000000, guid: 4c23f3546abcb7247a9e7dbda3b561d4, type: 2} m_LightingSettings: {fileID: 320409059} --- !u!196 &4 NavMeshSettings: serializedVersion: 2 m_ObjectHideFlags: 0 m_BuildSettings: - serializedVersion: 2 + serializedVersion: 3 agentTypeID: 0 agentRadius: 0.5 agentHeight: 2 @@ -117,7 +113,7 @@ NavMeshSettings: cellSize: 0.16666667 manualTileSize: 0 tileSize: 256 - accuratePlacement: 0 + buildHeightMesh: 0 maxJobWorkers: 0 preserveTilesOutsideBounds: 0 debug: @@ -143,7 +139,7 @@ GameObject: m_IsActive: 1 --- !u!199 &106222600 ParticleSystemRenderer: - serializedVersion: 6 + serializedVersion: 7 m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} @@ -153,11 +149,17 @@ ParticleSystemRenderer: m_CastShadows: 0 m_ReceiveShadows: 0 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 0 m_ReflectionProbeUsage: 0 m_RayTracingMode: 0 m_RayTraceProcedural: 0 + m_RayTracingAccelStructBuildFlagsOverride: 0 + m_RayTracingAccelStructBuildFlags: 1 + m_SmallMeshCulling: 1 + m_ForceMeshLod: -1 + m_MeshLodSelectionBias: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -180,10 +182,13 @@ ParticleSystemRenderer: m_AutoUVMaxDistance: 0.5 m_AutoUVMaxAngle: 89 m_LightmapParameters: {fileID: 0} + m_GlobalIlluminationMeshLod: 0 m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 + m_MaskInteraction: 0 m_RenderMode: 0 + m_MeshDistribution: 0 m_SortMode: 1 m_MinParticleSize: 0 m_MaxParticleSize: 0.5 @@ -196,18 +201,23 @@ ParticleSystemRenderer: m_RenderAlignment: 0 m_Pivot: {x: 0, y: 0, z: 0} m_Flip: {x: 0, y: 0, z: 0} - m_UseCustomVertexStreams: 0 m_EnableGPUInstancing: 1 m_ApplyActiveColorSpace: 1 m_AllowRoll: 1 m_FreeformStretching: 0 m_RotateWithStretchDirection: 1 + m_UseCustomVertexStreams: 0 m_VertexStreams: 00010304 + m_UseCustomTrailVertexStreams: 0 + m_TrailVertexStreams: 00010304 m_Mesh: {fileID: 0} m_Mesh1: {fileID: 0} m_Mesh2: {fileID: 0} m_Mesh3: {fileID: 0} - m_MaskInteraction: 0 + m_MeshWeighting: 1 + m_MeshWeighting1: 1 + m_MeshWeighting2: 1 + m_MeshWeighting3: 1 --- !u!198 &106222601 ParticleSystem: m_ObjectHideFlags: 0 @@ -215,19 +225,19 @@ ParticleSystem: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 106222599} - serializedVersion: 7 + serializedVersion: 8 lengthInSec: 5 simulationSpeed: 1 stopAction: 0 cullingMode: 0 ringBufferMode: 0 ringBufferLoopRange: {x: 0, y: 1} + emitterVelocityMode: 0 looping: 1 prewarm: 0 playOnAwake: 1 useUnscaledTime: 0 autoRandomSeed: 1 - useRigidbodyForVelocity: 1 startDelay: serializedVersion: 2 minMaxState: 0 @@ -426,6 +436,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -455,6 +466,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 startSize: @@ -776,7 +788,9 @@ ParticleSystem: m_PostInfinity: 2 m_RotationOrder: 4 randomizeRotationDirection: 0 + gravitySource: 0 maxNumParticles: 5000 + customEmitterVelocity: {x: 0, y: 0, z: 0} size3D: 0 rotation3D: 0 gravityModifier: @@ -1522,6 +1536,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -1551,6 +1566,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 UVModule: @@ -3771,6 +3787,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -3800,6 +3817,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 range: {x: 0, y: 1} @@ -4189,6 +4207,7 @@ ParticleSystem: m_RotationOrder: 4 minVertexDistance: 0.2 textureMode: 0 + textureScale: {x: 1, y: 1} ribbonCount: 1 shadowBias: 0.5 worldSpace: 0 @@ -4231,6 +4250,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -4260,6 +4280,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 widthOverTrail: @@ -4347,6 +4368,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -4376,6 +4398,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 CustomDataModule: @@ -4414,6 +4437,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -4443,6 +4467,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 colorLabel0: Color @@ -4696,6 +4721,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -4725,6 +4751,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 colorLabel1: Color @@ -4951,12 +4978,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 106222599} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 3.3, y: 15, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 973550119} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &162109877 GameObject: @@ -4983,16 +5011,17 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 162109877} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 116.2, y: 15, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1735128053} - m_RootOrder: 5 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!199 &162109879 ParticleSystemRenderer: - serializedVersion: 6 + serializedVersion: 7 m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} @@ -5002,11 +5031,17 @@ ParticleSystemRenderer: m_CastShadows: 0 m_ReceiveShadows: 0 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 0 m_ReflectionProbeUsage: 0 m_RayTracingMode: 0 m_RayTraceProcedural: 0 + m_RayTracingAccelStructBuildFlagsOverride: 0 + m_RayTracingAccelStructBuildFlags: 1 + m_SmallMeshCulling: 1 + m_ForceMeshLod: -1 + m_MeshLodSelectionBias: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -5029,10 +5064,13 @@ ParticleSystemRenderer: m_AutoUVMaxDistance: 0.5 m_AutoUVMaxAngle: 89 m_LightmapParameters: {fileID: 0} + m_GlobalIlluminationMeshLod: 0 m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 + m_MaskInteraction: 0 m_RenderMode: 0 + m_MeshDistribution: 0 m_SortMode: 1 m_MinParticleSize: 0 m_MaxParticleSize: 0.5 @@ -5045,18 +5083,23 @@ ParticleSystemRenderer: m_RenderAlignment: 0 m_Pivot: {x: 0, y: 0, z: 0} m_Flip: {x: 0, y: 0, z: 0} - m_UseCustomVertexStreams: 0 m_EnableGPUInstancing: 1 m_ApplyActiveColorSpace: 1 m_AllowRoll: 1 m_FreeformStretching: 0 m_RotateWithStretchDirection: 1 + m_UseCustomVertexStreams: 0 m_VertexStreams: 00010304 + m_UseCustomTrailVertexStreams: 0 + m_TrailVertexStreams: 00010304 m_Mesh: {fileID: 0} m_Mesh1: {fileID: 0} m_Mesh2: {fileID: 0} m_Mesh3: {fileID: 0} - m_MaskInteraction: 0 + m_MeshWeighting: 1 + m_MeshWeighting1: 1 + m_MeshWeighting2: 1 + m_MeshWeighting3: 1 --- !u!198 &162109880 ParticleSystem: m_ObjectHideFlags: 0 @@ -5064,19 +5107,19 @@ ParticleSystem: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 162109877} - serializedVersion: 7 + serializedVersion: 8 lengthInSec: 5 simulationSpeed: 1 stopAction: 0 cullingMode: 0 ringBufferMode: 0 ringBufferLoopRange: {x: 0, y: 1} + emitterVelocityMode: 0 looping: 1 prewarm: 0 playOnAwake: 1 useUnscaledTime: 0 autoRandomSeed: 1 - useRigidbodyForVelocity: 1 startDelay: serializedVersion: 2 minMaxState: 0 @@ -5275,6 +5318,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -5304,6 +5348,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 startSize: @@ -5625,7 +5670,9 @@ ParticleSystem: m_PostInfinity: 2 m_RotationOrder: 4 randomizeRotationDirection: 0 + gravitySource: 0 maxNumParticles: 5000 + customEmitterVelocity: {x: 0, y: 0, z: 0} size3D: 0 rotation3D: 0 gravityModifier: @@ -6371,6 +6418,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -6400,6 +6448,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 UVModule: @@ -8620,6 +8669,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -8649,6 +8699,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 range: {x: 0, y: 1} @@ -9038,6 +9089,7 @@ ParticleSystem: m_RotationOrder: 4 minVertexDistance: 0.2 textureMode: 0 + textureScale: {x: 1, y: 1} ribbonCount: 1 shadowBias: 0.5 worldSpace: 0 @@ -9080,6 +9132,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -9109,6 +9162,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 widthOverTrail: @@ -9196,6 +9250,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -9225,6 +9280,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 CustomDataModule: @@ -9263,6 +9319,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -9292,6 +9349,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 colorLabel0: Color @@ -9545,6 +9603,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -9574,6 +9633,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 colorLabel1: Color @@ -9800,8 +9860,7 @@ LightingSettings: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_Name: Settings.lighting - serializedVersion: 3 - m_GIWorkflowMode: 0 + serializedVersion: 10 m_EnableBakedLightmaps: 1 m_EnableRealtimeLightmaps: 1 m_RealtimeEnvironmentLighting: 1 @@ -9811,9 +9870,17 @@ LightingSettings: m_UsingShadowmask: 1 m_BakeBackend: 1 m_LightmapMaxSize: 1024 + m_LightmapSizeFixed: 0 + m_UseMipmapLimits: 1 m_BakeResolution: 40 m_Padding: 2 - m_TextureCompression: 1 + m_LightmapCompression: 3 + m_LightmapPackingMode: 1 + m_LightmapPackingMethod: 0 + m_XAtlasPackingAttempts: 16384 + m_XAtlasBruteForce: 0 + m_XAtlasBlockAlign: 0 + m_XAtlasRepackUnderutilizedLightmaps: 1 m_AO: 0 m_AOMaxDistance: 1 m_CompAOExponent: 1 @@ -9824,23 +9891,21 @@ LightingSettings: m_FilterMode: 1 m_LightmapParameters: {fileID: 15204, guid: 0000000000000000f000000000000000, type: 0} m_ExportTrainingData: 0 + m_EnableWorkerProcessBaking: 1 m_TrainingDataDestination: TrainingData m_RealtimeResolution: 2 m_ForceWhiteAlbedo: 0 m_ForceUpdates: 0 - m_FinalGather: 0 - m_FinalGatherRayCount: 256 - m_FinalGatherFiltering: 1 m_PVRCulling: 1 m_PVRSampling: 1 m_PVRDirectSampleCount: 32 - m_PVRSampleCount: 500 - m_PVREnvironmentSampleCount: 500 + m_PVRSampleCount: 512 + m_PVREnvironmentSampleCount: 512 m_PVREnvironmentReferencePointCount: 2048 m_LightProbeSampleCountMultiplier: 4 m_PVRBounces: 2 m_PVRMinBounces: 2 - m_PVREnvironmentMIS: 0 + m_PVREnvironmentImportanceSampling: 0 m_PVRFilteringMode: 2 m_PVRDenoiserTypeDirect: 0 m_PVRDenoiserTypeIndirect: 0 @@ -9854,6 +9919,7 @@ LightingSettings: m_PVRFilteringAtrousPositionSigmaDirect: 0.5 m_PVRFilteringAtrousPositionSigmaIndirect: 2 m_PVRFilteringAtrousPositionSigmaAO: 1 + m_RespectSceneVisibilityWhenBakingGI: 0 --- !u!1 &552162874 GameObject: m_ObjectHideFlags: 0 @@ -9879,16 +9945,17 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 552162874} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 72.4, y: 15, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1735128053} - m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!199 &552162876 ParticleSystemRenderer: - serializedVersion: 6 + serializedVersion: 7 m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} @@ -9898,11 +9965,17 @@ ParticleSystemRenderer: m_CastShadows: 0 m_ReceiveShadows: 0 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 0 m_ReflectionProbeUsage: 0 m_RayTracingMode: 0 m_RayTraceProcedural: 0 + m_RayTracingAccelStructBuildFlagsOverride: 0 + m_RayTracingAccelStructBuildFlags: 1 + m_SmallMeshCulling: 1 + m_ForceMeshLod: -1 + m_MeshLodSelectionBias: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -9925,10 +9998,13 @@ ParticleSystemRenderer: m_AutoUVMaxDistance: 0.5 m_AutoUVMaxAngle: 89 m_LightmapParameters: {fileID: 0} + m_GlobalIlluminationMeshLod: 0 m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 + m_MaskInteraction: 0 m_RenderMode: 0 + m_MeshDistribution: 0 m_SortMode: 1 m_MinParticleSize: 0 m_MaxParticleSize: 0.5 @@ -9941,18 +10017,23 @@ ParticleSystemRenderer: m_RenderAlignment: 0 m_Pivot: {x: 0, y: 0, z: 0} m_Flip: {x: 0, y: 0, z: 0} - m_UseCustomVertexStreams: 1 m_EnableGPUInstancing: 1 m_ApplyActiveColorSpace: 1 m_AllowRoll: 1 m_FreeformStretching: 0 m_RotateWithStretchDirection: 1 + m_UseCustomVertexStreams: 1 m_VertexStreams: 000103040508 + m_UseCustomTrailVertexStreams: 0 + m_TrailVertexStreams: 00010304 m_Mesh: {fileID: 0} m_Mesh1: {fileID: 0} m_Mesh2: {fileID: 0} m_Mesh3: {fileID: 0} - m_MaskInteraction: 0 + m_MeshWeighting: 1 + m_MeshWeighting1: 1 + m_MeshWeighting2: 1 + m_MeshWeighting3: 1 --- !u!198 &552162877 ParticleSystem: m_ObjectHideFlags: 0 @@ -9960,19 +10041,19 @@ ParticleSystem: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 552162874} - serializedVersion: 7 + serializedVersion: 8 lengthInSec: 5 simulationSpeed: 1 stopAction: 0 cullingMode: 0 ringBufferMode: 0 ringBufferLoopRange: {x: 0, y: 1} + emitterVelocityMode: 0 looping: 1 prewarm: 0 playOnAwake: 1 useUnscaledTime: 0 autoRandomSeed: 1 - useRigidbodyForVelocity: 1 startDelay: serializedVersion: 2 minMaxState: 0 @@ -10171,6 +10252,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -10200,6 +10282,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 startSize: @@ -10521,7 +10604,9 @@ ParticleSystem: m_PostInfinity: 2 m_RotationOrder: 4 randomizeRotationDirection: 0 + gravitySource: 0 maxNumParticles: 5000 + customEmitterVelocity: {x: 0, y: 0, z: 0} size3D: 0 rotation3D: 0 gravityModifier: @@ -11267,6 +11352,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -11296,6 +11382,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 UVModule: @@ -13516,6 +13603,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -13545,6 +13633,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 range: {x: 0, y: 1} @@ -13934,6 +14023,7 @@ ParticleSystem: m_RotationOrder: 4 minVertexDistance: 0.2 textureMode: 0 + textureScale: {x: 1, y: 1} ribbonCount: 1 shadowBias: 0.5 worldSpace: 0 @@ -13976,6 +14066,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -14005,6 +14096,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 widthOverTrail: @@ -14092,6 +14184,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -14121,6 +14214,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 CustomDataModule: @@ -14159,6 +14253,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -14188,6 +14283,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 colorLabel0: Color @@ -14441,6 +14537,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -14470,6 +14567,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 colorLabel1: Color @@ -14717,9 +14815,9 @@ RectTransform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1090639377} - m_RootOrder: 4 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -14796,15 +14894,14 @@ Light: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 705507993} m_Enabled: 1 - serializedVersion: 10 + serializedVersion: 13 m_Type: 1 - m_Shape: 0 m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1} m_Intensity: 5 m_Range: 10 m_SpotAngle: 30 - m_InnerSpotAngle: 21.80208 - m_CookieSize: 10 + m_InnerSpotAngle: 0 + m_CookieSize2D: {x: 0.5, y: 0.5} m_Shadows: m_Type: 0 m_Resolution: -1 @@ -14848,8 +14945,12 @@ Light: m_BoundingSphereOverride: {x: 0, y: 1.1e-44, z: 0, w: 1.6114932e-38} m_UseBoundingSphereOverride: 0 m_UseViewFrustumForShadowCasterCull: 1 - m_ShadowRadius: 0 + m_ForceVisible: 0 + m_ShapeRadius: 0 m_ShadowAngle: 0 + m_LightUnit: 2 + m_LuxAtDistance: 1 + m_EnableSpotReflector: 0 --- !u!4 &705507995 Transform: m_ObjectHideFlags: 0 @@ -14857,12 +14958,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 705507993} + serializedVersion: 2 m_LocalRotation: {x: 0.34921905, y: 0.1140769, z: -0.042878684, w: 0.9290823} m_LocalPosition: {x: 0, y: 3, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 41.2, y: 14, z: 0} --- !u!114 &705507996 MonoBehaviour: @@ -14876,31 +14978,26 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 7a68c43fe1f2a47cfa234b5eeaa98012, type: 3} m_Name: m_EditorClassIdentifier: - m_Version: 11 - m_ObsoleteShadowResolutionTier: 1 - m_ObsoleteUseShadowQualitySettings: 0 - m_ObsoleteCustomShadowResolution: 512 - m_ObsoleteContactShadows: 0 m_PointlightHDType: 0 m_SpotLightShape: 0 m_AreaLightShape: 0 - m_Intensity: 5 m_EnableSpotReflector: 0 + m_LightUnit: 2 m_LuxAtDistance: 1 - m_InnerSpotPercent: 0 + m_Intensity: 5 + m_InnerSpotPercent: -1 + m_ShapeWidth: -1 + m_ShapeHeight: -1 + m_AspectRatio: 1 + m_ShapeRadius: -1 m_SpotIESCutoffPercent: 100 m_LightDimmer: 1 m_VolumetricDimmer: 1 - m_LightUnit: 2 m_FadeDistance: 10000 m_VolumetricFadeDistance: 10000 m_AffectDiffuse: 1 m_AffectSpecular: 1 m_NonLightmappedOnly: 0 - m_ShapeWidth: 0.5 - m_ShapeHeight: 0.5 - m_AspectRatio: 1 - m_ShapeRadius: 0 m_SoftnessScale: 1 m_UseCustomSpotLightShadowCone: 0 m_CustomSpotLightShadowCone: 30 @@ -14911,22 +15008,33 @@ MonoBehaviour: m_IESPoint: {fileID: 0} m_IESSpot: {fileID: 0} m_IncludeForRayTracing: 1 + m_IncludeForPathTracing: 1 m_AreaLightShadowCone: 120 m_UseScreenSpaceShadows: 0 m_InteractsWithSky: 1 m_AngularDiameter: 0.5 - m_FlareSize: 2 - m_FlareTint: {r: 1, g: 1, b: 1, a: 1} - m_FlareFalloff: 4 - m_SurfaceTexture: {fileID: 0} - m_SurfaceTint: {r: 1, g: 1, b: 1, a: 1} + diameterMultiplerMode: 0 + diameterMultiplier: 1 + diameterOverride: 0.5 + celestialBodyShadingSource: 1 + sunLightOverride: {fileID: 0} + sunColor: {r: 1, g: 1, b: 1, a: 1} + sunIntensity: 130000 + moonPhase: 0.2 + moonPhaseRotation: 0 + earthshine: 1 + flareSize: 2 + flareTint: {r: 1, g: 1, b: 1, a: 1} + flareFalloff: 4 + flareMultiplier: 1 + surfaceTexture: {fileID: 0} + surfaceTint: {r: 1, g: 1, b: 1, a: 1} m_Distance: 1.5e+11 m_UseRayTracedShadows: 0 m_NumRayTracingSamples: 4 m_FilterTracedShadow: 1 m_FilterSizeTraced: 16 m_SunLightConeAngle: 0.5 - m_LightShadowRadius: 0.5 m_SemiTransparentShadow: 0 m_ColorShadow: 1 m_DistanceBasedFiltering: 0 @@ -14940,6 +15048,14 @@ MonoBehaviour: m_BlockerSampleCount: 24 m_FilterSampleCount: 16 m_MinFilterSize: 0.01 + m_DirLightPCSSBlockerSampleCount: 24 + m_DirLightPCSSFilterSampleCount: 16 + m_DirLightPCSSMaxPenumbraSize: 0.56 + m_DirLightPCSSMaxSamplingDistance: 0.5 + m_DirLightPCSSMinFilterSizeTexels: 1.5 + m_DirLightPCSSMinFilterMaxAngularDiameter: 10 + m_DirLightPCSSBlockerSearchAngularDiameter: 12 + m_DirLightPCSSBlockerSamplingClumpExponent: 2 m_KernelSize: 5 m_LightAngle: 1 m_MaxDepthBias: 0.001 @@ -14967,6 +15083,7 @@ MonoBehaviour: m_BarnDoorAngle: 90 m_BarnDoorLength: 0.05 m_preserveCachedShadow: 0 + m_OnDemandShadowRenderOnPlacement: 1 m_ShadowCascadeRatios: - 0.05 - 0.2 @@ -14982,10 +15099,14 @@ MonoBehaviour: useOldInspector: 0 useVolumetric: 1 featuresFoldout: 1 - showAdditionalSettings: 0 m_AreaLightEmissiveMeshShadowCastingMode: 0 m_AreaLightEmissiveMeshMotionVectorGenerationMode: 0 m_AreaLightEmissiveMeshLayer: -1 + m_Version: 15 + m_ObsoleteShadowResolutionTier: 1 + m_ObsoleteUseShadowQualitySettings: 0 + m_ObsoleteCustomShadowResolution: 512 + m_ObsoleteContactShadows: 0 --- !u!1 &839247704 GameObject: m_ObjectHideFlags: 0 @@ -15011,16 +15132,17 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 839247704} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 26.9, y: 15, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1735128053} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!199 &839247706 ParticleSystemRenderer: - serializedVersion: 6 + serializedVersion: 7 m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} @@ -15030,11 +15152,17 @@ ParticleSystemRenderer: m_CastShadows: 0 m_ReceiveShadows: 0 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 0 m_ReflectionProbeUsage: 0 m_RayTracingMode: 0 m_RayTraceProcedural: 0 + m_RayTracingAccelStructBuildFlagsOverride: 0 + m_RayTracingAccelStructBuildFlags: 1 + m_SmallMeshCulling: 1 + m_ForceMeshLod: -1 + m_MeshLodSelectionBias: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -15057,10 +15185,13 @@ ParticleSystemRenderer: m_AutoUVMaxDistance: 0.5 m_AutoUVMaxAngle: 89 m_LightmapParameters: {fileID: 0} + m_GlobalIlluminationMeshLod: 0 m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 + m_MaskInteraction: 0 m_RenderMode: 0 + m_MeshDistribution: 0 m_SortMode: 1 m_MinParticleSize: 0 m_MaxParticleSize: 0.5 @@ -15073,18 +15204,23 @@ ParticleSystemRenderer: m_RenderAlignment: 0 m_Pivot: {x: 0, y: 0, z: 0} m_Flip: {x: 0, y: 0, z: 0} - m_UseCustomVertexStreams: 0 m_EnableGPUInstancing: 1 m_ApplyActiveColorSpace: 1 m_AllowRoll: 1 m_FreeformStretching: 0 m_RotateWithStretchDirection: 1 + m_UseCustomVertexStreams: 0 m_VertexStreams: 00010304 + m_UseCustomTrailVertexStreams: 0 + m_TrailVertexStreams: 00010304 m_Mesh: {fileID: 0} m_Mesh1: {fileID: 0} m_Mesh2: {fileID: 0} m_Mesh3: {fileID: 0} - m_MaskInteraction: 0 + m_MeshWeighting: 1 + m_MeshWeighting1: 1 + m_MeshWeighting2: 1 + m_MeshWeighting3: 1 --- !u!198 &839247707 ParticleSystem: m_ObjectHideFlags: 0 @@ -15092,19 +15228,19 @@ ParticleSystem: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 839247704} - serializedVersion: 7 + serializedVersion: 8 lengthInSec: 5 simulationSpeed: 1 stopAction: 0 cullingMode: 0 ringBufferMode: 0 ringBufferLoopRange: {x: 0, y: 1} + emitterVelocityMode: 0 looping: 1 prewarm: 0 playOnAwake: 1 useUnscaledTime: 0 autoRandomSeed: 1 - useRigidbodyForVelocity: 1 startDelay: serializedVersion: 2 minMaxState: 0 @@ -15303,6 +15439,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -15332,6 +15469,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 startSize: @@ -15653,7 +15791,9 @@ ParticleSystem: m_PostInfinity: 2 m_RotationOrder: 4 randomizeRotationDirection: 0.5 + gravitySource: 0 maxNumParticles: 5000 + customEmitterVelocity: {x: 0, y: 0, z: 0} size3D: 0 rotation3D: 0 gravityModifier: @@ -16399,6 +16539,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -16428,6 +16569,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 UVModule: @@ -18648,6 +18790,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -18677,6 +18820,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 range: {x: 0, y: 1} @@ -19066,6 +19210,7 @@ ParticleSystem: m_RotationOrder: 4 minVertexDistance: 0.2 textureMode: 0 + textureScale: {x: 1, y: 1} ribbonCount: 1 shadowBias: 0.5 worldSpace: 0 @@ -19108,6 +19253,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -19137,6 +19283,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 widthOverTrail: @@ -19224,6 +19371,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -19253,6 +19401,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 CustomDataModule: @@ -19291,6 +19440,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -19320,6 +19470,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 colorLabel0: Color @@ -19573,6 +19724,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -19602,6 +19754,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 colorLabel1: Color @@ -19849,9 +20002,9 @@ RectTransform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1090639377} - m_RootOrder: 5 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -19928,9 +20081,9 @@ RectTransform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1090639377} - m_RootOrder: 7 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -20020,9 +20173,17 @@ Camera: m_projectionMatrixMode: 1 m_GateFitMode: 2 m_FOVAxisMode: 0 + m_Iso: 200 + m_ShutterSpeed: 0.005 + m_Aperture: 16 + m_FocusDistance: 10 + m_FocalLength: 50 + m_BladeCount: 5 + m_Curvature: {x: 2, y: 11} + m_BarrelClipping: 0.25 + m_Anamorphism: 0 m_SensorSize: {x: 36, y: 24} m_LensShift: {x: 0, y: 0} - m_FocalLength: 50 m_NormalizedViewPortRect: serializedVersion: 2 x: 0 @@ -20056,12 +20217,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 963194225} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 50.7, y: 1, z: -68.1} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!114 &963194229 MonoBehaviour: @@ -20075,53 +20237,6 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 23c1ce4fb46143f46bc5cb5224c934f6, type: 3} m_Name: m_EditorClassIdentifier: - m_Version: 7 - m_ObsoleteRenderingPath: 0 - m_ObsoleteFrameSettings: - overrides: 0 - enableShadow: 0 - enableContactShadows: 0 - enableShadowMask: 0 - enableSSR: 0 - enableSSAO: 0 - enableSubsurfaceScattering: 0 - enableTransmission: 0 - enableAtmosphericScattering: 0 - enableVolumetrics: 0 - enableReprojectionForVolumetrics: 0 - enableLightLayers: 0 - enableExposureControl: 1 - diffuseGlobalDimmer: 0 - specularGlobalDimmer: 0 - shaderLitMode: 0 - enableDepthPrepassWithDeferredRendering: 0 - enableTransparentPrepass: 0 - enableMotionVectors: 0 - enableObjectMotionVectors: 0 - enableDecals: 0 - enableRoughRefraction: 0 - enableTransparentPostpass: 0 - enableDistortion: 0 - enablePostprocess: 0 - enableOpaqueObjects: 0 - enableTransparentObjects: 0 - enableRealtimePlanarReflection: 0 - enableMSAA: 0 - enableAsyncCompute: 0 - runLightListAsync: 0 - runSSRAsync: 0 - runSSAOAsync: 0 - runContactShadowsAsync: 0 - runVolumeVoxelizationAsync: 0 - lightLoopSettings: - overrides: 0 - enableDeferredTileAndCluster: 0 - enableComputeLightEvaluation: 0 - enableComputeLightVariants: 0 - enableComputeMaterialVariants: 0 - enableFptlForForwardOpaque: 0 - enableBigTilePrepass: 0 - isFptlEnabled: 0 clearColorMode: 0 backgroundColorHDR: {r: 0, g: 0, b: 0, a: 0} clearDepth: 1 @@ -20135,14 +20250,19 @@ MonoBehaviour: stopNaNs: 0 taaSharpenStrength: 0.6 TAAQuality: 1 + taaSharpenMode: 0 + taaRingingReduction: 0 taaHistorySharpening: 0.35 taaAntiFlicker: 0.5 taaMotionVectorRejection: 0 taaAntiHistoryRinging: 0 + taaBaseBlendFactor: 0.875 + taaJitterScale: 1 physicalParameters: m_Iso: 200 m_ShutterSpeed: 0.005 m_Aperture: 16 + m_FocusDistance: 10 m_BladeCount: 5 m_Curvature: {x: 2, y: 11} m_BarrelClipping: 0.25 @@ -20157,7 +20277,25 @@ MonoBehaviour: serializedVersion: 2 m_Bits: 4294967295 hasPersistentHistory: 0 + screenSizeOverride: {x: 0, y: 0, z: 0, w: 0} + screenCoordScaleBias: {x: 0, y: 0, z: 0, w: 0} + allowDeepLearningSuperSampling: 1 + deepLearningSuperSamplingUseCustomQualitySettings: 0 + deepLearningSuperSamplingQuality: 0 + deepLearningSuperSamplingUseCustomAttributes: 0 + deepLearningSuperSamplingUseOptimalSettings: 1 + deepLearningSuperSamplingSharpening: 0 + allowFidelityFX2SuperResolution: 1 + fidelityFX2SuperResolutionUseCustomQualitySettings: 0 + fidelityFX2SuperResolutionQuality: 0 + fidelityFX2SuperResolutionUseCustomAttributes: 0 + fidelityFX2SuperResolutionUseOptimalSettings: 1 + fidelityFX2SuperResolutionEnableSharpening: 0 + fidelityFX2SuperResolutionSharpening: 0 + fsrOverrideSharpness: 0 + fsrSharpness: 0.92 exposureTarget: {fileID: 0} + materialMipBias: 0 m_RenderingPathCustomFrameSettings: bitDatas: data1: 70005819440989 @@ -20171,12 +20309,61 @@ MonoBehaviour: sssQualityMode: 0 sssQualityLevel: 0 sssCustomSampleBudget: 20 + sssCustomDownsampleSteps: 0 + msaaMode: 1 materialQuality: 0 renderingPathCustomFrameSettingsOverrideMask: mask: data1: 0 data2: 0 defaultFrameSettings: 0 + m_Version: 9 + m_ObsoleteRenderingPath: 0 + m_ObsoleteFrameSettings: + overrides: 0 + enableShadow: 0 + enableContactShadows: 0 + enableShadowMask: 0 + enableSSR: 0 + enableSSAO: 0 + enableSubsurfaceScattering: 0 + enableTransmission: 0 + enableAtmosphericScattering: 0 + enableVolumetrics: 0 + enableReprojectionForVolumetrics: 0 + enableLightLayers: 0 + enableExposureControl: 1 + diffuseGlobalDimmer: 0 + specularGlobalDimmer: 0 + shaderLitMode: 0 + enableDepthPrepassWithDeferredRendering: 0 + enableTransparentPrepass: 0 + enableMotionVectors: 0 + enableObjectMotionVectors: 0 + enableDecals: 0 + enableRoughRefraction: 0 + enableTransparentPostpass: 0 + enableDistortion: 0 + enablePostprocess: 0 + enableOpaqueObjects: 0 + enableTransparentObjects: 0 + enableRealtimePlanarReflection: 0 + enableMSAA: 0 + enableAsyncCompute: 0 + runLightListAsync: 0 + runSSRAsync: 0 + runSSAOAsync: 0 + runContactShadowsAsync: 0 + runVolumeVoxelizationAsync: 0 + lightLoopSettings: + overrides: 0 + enableDeferredTileAndCluster: 0 + enableComputeLightEvaluation: 0 + enableComputeLightVariants: 0 + enableComputeMaterialVariants: 0 + enableFptlForForwardOpaque: 0 + enableBigTilePrepass: 0 + isFptlEnabled: 0 --- !u!1 &973550118 GameObject: m_ObjectHideFlags: 0 @@ -20200,9 +20387,11 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 973550118} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: - {fileID: 106222602} - {fileID: 2102630558} @@ -20211,7 +20400,6 @@ Transform: - {fileID: 1282917331} - {fileID: 1361481443} m_Father: {fileID: 0} - m_RootOrder: 5 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &1090639373 GameObject: @@ -20289,7 +20477,10 @@ Canvas: m_OverrideSorting: 0 m_OverridePixelPerfect: 0 m_SortingBucketNormalizedSize: 0 + m_VertexColorAlwaysGammaSpace: 0 + m_UseReflectionProbes: 0 m_AdditionalShaderChannelsFlag: 0 + m_UpdateRectTransformForStandalone: 0 m_SortingLayerID: 0 m_SortingOrder: 0 m_TargetDisplay: 0 @@ -20303,6 +20494,7 @@ RectTransform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 0.105, y: 0.105, z: 0.1} + m_ConstrainProportionsScale: 0 m_Children: - {fileID: 1537901259} - {fileID: 1214436072} @@ -20313,7 +20505,6 @@ RectTransform: - {fileID: 1306555494} - {fileID: 942917500} m_Father: {fileID: 0} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} m_AnchorMax: {x: 0, y: 0} @@ -20345,16 +20536,17 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1189360801} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 3.3, y: 15, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1735128053} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!199 &1189360803 ParticleSystemRenderer: - serializedVersion: 6 + serializedVersion: 7 m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} @@ -20364,11 +20556,17 @@ ParticleSystemRenderer: m_CastShadows: 0 m_ReceiveShadows: 0 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 0 m_ReflectionProbeUsage: 0 m_RayTracingMode: 0 m_RayTraceProcedural: 0 + m_RayTracingAccelStructBuildFlagsOverride: 0 + m_RayTracingAccelStructBuildFlags: 1 + m_SmallMeshCulling: 1 + m_ForceMeshLod: -1 + m_MeshLodSelectionBias: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -20391,10 +20589,13 @@ ParticleSystemRenderer: m_AutoUVMaxDistance: 0.5 m_AutoUVMaxAngle: 89 m_LightmapParameters: {fileID: 0} + m_GlobalIlluminationMeshLod: 0 m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 + m_MaskInteraction: 0 m_RenderMode: 0 + m_MeshDistribution: 0 m_SortMode: 1 m_MinParticleSize: 0 m_MaxParticleSize: 0.5 @@ -20407,18 +20608,23 @@ ParticleSystemRenderer: m_RenderAlignment: 0 m_Pivot: {x: 0, y: 0, z: 0} m_Flip: {x: 0, y: 0, z: 0} - m_UseCustomVertexStreams: 0 m_EnableGPUInstancing: 1 m_ApplyActiveColorSpace: 1 m_AllowRoll: 1 m_FreeformStretching: 0 m_RotateWithStretchDirection: 1 + m_UseCustomVertexStreams: 0 m_VertexStreams: 00010304 + m_UseCustomTrailVertexStreams: 0 + m_TrailVertexStreams: 00010304 m_Mesh: {fileID: 0} m_Mesh1: {fileID: 0} m_Mesh2: {fileID: 0} m_Mesh3: {fileID: 0} - m_MaskInteraction: 0 + m_MeshWeighting: 1 + m_MeshWeighting1: 1 + m_MeshWeighting2: 1 + m_MeshWeighting3: 1 --- !u!198 &1189360804 ParticleSystem: m_ObjectHideFlags: 0 @@ -20426,19 +20632,19 @@ ParticleSystem: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1189360801} - serializedVersion: 7 + serializedVersion: 8 lengthInSec: 5 simulationSpeed: 1 stopAction: 0 cullingMode: 0 ringBufferMode: 0 ringBufferLoopRange: {x: 0, y: 1} + emitterVelocityMode: 0 looping: 1 prewarm: 0 playOnAwake: 1 useUnscaledTime: 0 autoRandomSeed: 1 - useRigidbodyForVelocity: 1 startDelay: serializedVersion: 2 minMaxState: 0 @@ -20637,6 +20843,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -20666,6 +20873,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 startSize: @@ -20987,7 +21195,9 @@ ParticleSystem: m_PostInfinity: 2 m_RotationOrder: 4 randomizeRotationDirection: 0 + gravitySource: 0 maxNumParticles: 5000 + customEmitterVelocity: {x: 0, y: 0, z: 0} size3D: 0 rotation3D: 0 gravityModifier: @@ -21733,6 +21943,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -21762,6 +21973,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 UVModule: @@ -23982,6 +24194,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -24011,6 +24224,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 range: {x: 0, y: 1} @@ -24400,6 +24614,7 @@ ParticleSystem: m_RotationOrder: 4 minVertexDistance: 0.2 textureMode: 0 + textureScale: {x: 1, y: 1} ribbonCount: 1 shadowBias: 0.5 worldSpace: 0 @@ -24442,6 +24657,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -24471,6 +24687,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 widthOverTrail: @@ -24558,6 +24775,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -24587,6 +24805,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 CustomDataModule: @@ -24625,6 +24844,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -24654,6 +24874,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 colorLabel0: Color @@ -24907,6 +25128,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -24936,6 +25158,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 colorLabel1: Color @@ -25183,9 +25406,9 @@ RectTransform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1090639377} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -25254,7 +25477,7 @@ GameObject: m_IsActive: 1 --- !u!199 &1264922522 ParticleSystemRenderer: - serializedVersion: 6 + serializedVersion: 7 m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} @@ -25264,11 +25487,17 @@ ParticleSystemRenderer: m_CastShadows: 0 m_ReceiveShadows: 0 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 0 m_ReflectionProbeUsage: 0 m_RayTracingMode: 0 m_RayTraceProcedural: 0 + m_RayTracingAccelStructBuildFlagsOverride: 0 + m_RayTracingAccelStructBuildFlags: 1 + m_SmallMeshCulling: 1 + m_ForceMeshLod: -1 + m_MeshLodSelectionBias: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -25291,10 +25520,13 @@ ParticleSystemRenderer: m_AutoUVMaxDistance: 0.5 m_AutoUVMaxAngle: 89 m_LightmapParameters: {fileID: 0} + m_GlobalIlluminationMeshLod: 0 m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 + m_MaskInteraction: 0 m_RenderMode: 0 + m_MeshDistribution: 0 m_SortMode: 1 m_MinParticleSize: 0 m_MaxParticleSize: 0.5 @@ -25307,18 +25539,23 @@ ParticleSystemRenderer: m_RenderAlignment: 0 m_Pivot: {x: 0, y: 0, z: 0} m_Flip: {x: 0, y: 0, z: 0} - m_UseCustomVertexStreams: 1 m_EnableGPUInstancing: 1 m_ApplyActiveColorSpace: 1 m_AllowRoll: 1 m_FreeformStretching: 0 m_RotateWithStretchDirection: 1 + m_UseCustomVertexStreams: 1 m_VertexStreams: 000103040508 + m_UseCustomTrailVertexStreams: 0 + m_TrailVertexStreams: 00010304 m_Mesh: {fileID: 0} m_Mesh1: {fileID: 0} m_Mesh2: {fileID: 0} m_Mesh3: {fileID: 0} - m_MaskInteraction: 0 + m_MeshWeighting: 1 + m_MeshWeighting1: 1 + m_MeshWeighting2: 1 + m_MeshWeighting3: 1 --- !u!198 &1264922523 ParticleSystem: m_ObjectHideFlags: 0 @@ -25326,19 +25563,19 @@ ParticleSystem: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1264922521} - serializedVersion: 7 + serializedVersion: 8 lengthInSec: 5 simulationSpeed: 1 stopAction: 0 cullingMode: 0 ringBufferMode: 0 ringBufferLoopRange: {x: 0, y: 1} + emitterVelocityMode: 0 looping: 1 prewarm: 0 playOnAwake: 1 useUnscaledTime: 0 autoRandomSeed: 1 - useRigidbodyForVelocity: 1 startDelay: serializedVersion: 2 minMaxState: 0 @@ -25537,6 +25774,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -25566,6 +25804,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 startSize: @@ -25887,7 +26126,9 @@ ParticleSystem: m_PostInfinity: 2 m_RotationOrder: 4 randomizeRotationDirection: 0 + gravitySource: 0 maxNumParticles: 5000 + customEmitterVelocity: {x: 0, y: 0, z: 0} size3D: 0 rotation3D: 0 gravityModifier: @@ -26633,6 +26874,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -26662,6 +26904,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 UVModule: @@ -28882,6 +29125,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -28911,6 +29155,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 range: {x: 0, y: 1} @@ -29300,6 +29545,7 @@ ParticleSystem: m_RotationOrder: 4 minVertexDistance: 0.2 textureMode: 0 + textureScale: {x: 1, y: 1} ribbonCount: 1 shadowBias: 0.5 worldSpace: 0 @@ -29342,6 +29588,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -29371,6 +29618,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 widthOverTrail: @@ -29458,6 +29706,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -29487,6 +29736,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 CustomDataModule: @@ -29525,6 +29775,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -29554,6 +29805,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 colorLabel0: Color @@ -29807,6 +30059,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -29836,6 +30089,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 colorLabel1: Color @@ -30062,12 +30316,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1264922521} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 72.4, y: 15, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 973550119} - m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &1282917328 GameObject: @@ -30089,7 +30344,7 @@ GameObject: m_IsActive: 1 --- !u!199 &1282917329 ParticleSystemRenderer: - serializedVersion: 6 + serializedVersion: 7 m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} @@ -30099,11 +30354,17 @@ ParticleSystemRenderer: m_CastShadows: 0 m_ReceiveShadows: 0 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 0 m_ReflectionProbeUsage: 0 m_RayTracingMode: 0 m_RayTraceProcedural: 0 + m_RayTracingAccelStructBuildFlagsOverride: 0 + m_RayTracingAccelStructBuildFlags: 1 + m_SmallMeshCulling: 1 + m_ForceMeshLod: -1 + m_MeshLodSelectionBias: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -30126,10 +30387,13 @@ ParticleSystemRenderer: m_AutoUVMaxDistance: 0.5 m_AutoUVMaxAngle: 89 m_LightmapParameters: {fileID: 0} + m_GlobalIlluminationMeshLod: 0 m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 + m_MaskInteraction: 0 m_RenderMode: 0 + m_MeshDistribution: 0 m_SortMode: 1 m_MinParticleSize: 0 m_MaxParticleSize: 0.5 @@ -30142,18 +30406,23 @@ ParticleSystemRenderer: m_RenderAlignment: 0 m_Pivot: {x: 0, y: 0, z: 0} m_Flip: {x: 0, y: 0, z: 0} - m_UseCustomVertexStreams: 1 m_EnableGPUInstancing: 1 m_ApplyActiveColorSpace: 1 m_AllowRoll: 1 m_FreeformStretching: 0 m_RotateWithStretchDirection: 1 + m_UseCustomVertexStreams: 1 m_VertexStreams: 000103040508 + m_UseCustomTrailVertexStreams: 0 + m_TrailVertexStreams: 00010304 m_Mesh: {fileID: 0} m_Mesh1: {fileID: 0} m_Mesh2: {fileID: 0} m_Mesh3: {fileID: 0} - m_MaskInteraction: 0 + m_MeshWeighting: 1 + m_MeshWeighting1: 1 + m_MeshWeighting2: 1 + m_MeshWeighting3: 1 --- !u!198 &1282917330 ParticleSystem: m_ObjectHideFlags: 0 @@ -30161,19 +30430,19 @@ ParticleSystem: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1282917328} - serializedVersion: 7 + serializedVersion: 8 lengthInSec: 5 simulationSpeed: 1 stopAction: 0 cullingMode: 0 ringBufferMode: 0 ringBufferLoopRange: {x: 0, y: 1} + emitterVelocityMode: 0 looping: 1 prewarm: 0 playOnAwake: 1 useUnscaledTime: 0 autoRandomSeed: 1 - useRigidbodyForVelocity: 1 startDelay: serializedVersion: 2 minMaxState: 0 @@ -30372,6 +30641,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -30401,6 +30671,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 startSize: @@ -30722,7 +30993,9 @@ ParticleSystem: m_PostInfinity: 2 m_RotationOrder: 4 randomizeRotationDirection: 0 + gravitySource: 0 maxNumParticles: 5000 + customEmitterVelocity: {x: 0, y: 0, z: 0} size3D: 0 rotation3D: 0 gravityModifier: @@ -31468,6 +31741,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -31497,6 +31771,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 UVModule: @@ -33717,6 +33992,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -33746,6 +34022,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 range: {x: 0, y: 1} @@ -34135,6 +34412,7 @@ ParticleSystem: m_RotationOrder: 4 minVertexDistance: 0.2 textureMode: 0 + textureScale: {x: 1, y: 1} ribbonCount: 1 shadowBias: 0.5 worldSpace: 0 @@ -34177,6 +34455,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -34206,6 +34485,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 widthOverTrail: @@ -34293,6 +34573,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -34322,6 +34603,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 CustomDataModule: @@ -34360,6 +34642,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -34389,6 +34672,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 colorLabel0: Color @@ -34642,6 +34926,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -34671,6 +34956,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 colorLabel1: Color @@ -34897,12 +35183,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1282917328} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 95.6, y: 15, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 973550119} - m_RootOrder: 4 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &1294102433 GameObject: @@ -34931,9 +35218,17 @@ BoxCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1294102433} m_Material: {fileID: 0} + m_IncludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_ExcludeLayers: + serializedVersion: 2 + m_Bits: 0 + m_LayerOverridePriority: 0 m_IsTrigger: 0 + m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 2 + serializedVersion: 3 m_Size: {x: 1, y: 1, z: 1} m_Center: {x: 0, y: 0, z: 0} --- !u!23 &1294102435 @@ -34947,11 +35242,17 @@ MeshRenderer: m_CastShadows: 0 m_ReceiveShadows: 1 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 m_RayTracingMode: 2 m_RayTraceProcedural: 0 + m_RayTracingAccelStructBuildFlagsOverride: 0 + m_RayTracingAccelStructBuildFlags: 1 + m_SmallMeshCulling: 1 + m_ForceMeshLod: -1 + m_MeshLodSelectionBias: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -34973,9 +35274,11 @@ MeshRenderer: m_AutoUVMaxDistance: 0.5 m_AutoUVMaxAngle: 89 m_LightmapParameters: {fileID: 0} + m_GlobalIlluminationMeshLod: 0 m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 + m_MaskInteraction: 0 m_AdditionalVertexStreams: {fileID: 0} --- !u!33 &1294102436 MeshFilter: @@ -34992,12 +35295,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1294102433} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 130.8, y: 0, z: 20.6} m_LocalScale: {x: 23.9, y: 124.7, z: 59.9} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} - m_RootOrder: 4 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &1306555493 GameObject: @@ -35027,9 +35331,9 @@ RectTransform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1090639377} - m_RootOrder: 6 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -35100,7 +35404,7 @@ GameObject: m_IsActive: 1 --- !u!199 &1361481441 ParticleSystemRenderer: - serializedVersion: 6 + serializedVersion: 7 m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} @@ -35110,11 +35414,17 @@ ParticleSystemRenderer: m_CastShadows: 0 m_ReceiveShadows: 0 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 0 m_ReflectionProbeUsage: 0 m_RayTracingMode: 0 m_RayTraceProcedural: 0 + m_RayTracingAccelStructBuildFlagsOverride: 0 + m_RayTracingAccelStructBuildFlags: 1 + m_SmallMeshCulling: 1 + m_ForceMeshLod: -1 + m_MeshLodSelectionBias: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -35137,10 +35447,13 @@ ParticleSystemRenderer: m_AutoUVMaxDistance: 0.5 m_AutoUVMaxAngle: 89 m_LightmapParameters: {fileID: 0} + m_GlobalIlluminationMeshLod: 0 m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 + m_MaskInteraction: 0 m_RenderMode: 0 + m_MeshDistribution: 0 m_SortMode: 1 m_MinParticleSize: 0 m_MaxParticleSize: 0.5 @@ -35153,18 +35466,23 @@ ParticleSystemRenderer: m_RenderAlignment: 0 m_Pivot: {x: 0, y: 0, z: 0} m_Flip: {x: 0, y: 0, z: 0} - m_UseCustomVertexStreams: 0 m_EnableGPUInstancing: 1 m_ApplyActiveColorSpace: 1 m_AllowRoll: 1 m_FreeformStretching: 0 m_RotateWithStretchDirection: 1 + m_UseCustomVertexStreams: 0 m_VertexStreams: 00010304 + m_UseCustomTrailVertexStreams: 0 + m_TrailVertexStreams: 00010304 m_Mesh: {fileID: 0} m_Mesh1: {fileID: 0} m_Mesh2: {fileID: 0} m_Mesh3: {fileID: 0} - m_MaskInteraction: 0 + m_MeshWeighting: 1 + m_MeshWeighting1: 1 + m_MeshWeighting2: 1 + m_MeshWeighting3: 1 --- !u!198 &1361481442 ParticleSystem: m_ObjectHideFlags: 0 @@ -35172,19 +35490,19 @@ ParticleSystem: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1361481440} - serializedVersion: 7 + serializedVersion: 8 lengthInSec: 5 simulationSpeed: 1 stopAction: 0 cullingMode: 0 ringBufferMode: 0 ringBufferLoopRange: {x: 0, y: 1} + emitterVelocityMode: 0 looping: 1 prewarm: 0 playOnAwake: 1 useUnscaledTime: 0 autoRandomSeed: 1 - useRigidbodyForVelocity: 1 startDelay: serializedVersion: 2 minMaxState: 0 @@ -35383,6 +35701,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -35412,6 +35731,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 startSize: @@ -35733,7 +36053,9 @@ ParticleSystem: m_PostInfinity: 2 m_RotationOrder: 4 randomizeRotationDirection: 0 + gravitySource: 0 maxNumParticles: 5000 + customEmitterVelocity: {x: 0, y: 0, z: 0} size3D: 0 rotation3D: 0 gravityModifier: @@ -36479,6 +36801,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -36508,6 +36831,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 UVModule: @@ -38728,6 +39052,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -38757,6 +39082,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 range: {x: 0, y: 1} @@ -39146,6 +39472,7 @@ ParticleSystem: m_RotationOrder: 4 minVertexDistance: 0.2 textureMode: 0 + textureScale: {x: 1, y: 1} ribbonCount: 1 shadowBias: 0.5 worldSpace: 0 @@ -39188,6 +39515,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -39217,6 +39545,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 widthOverTrail: @@ -39304,6 +39633,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -39333,6 +39663,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 CustomDataModule: @@ -39371,6 +39702,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -39400,6 +39732,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 colorLabel0: Color @@ -39653,6 +39986,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -39682,6 +40016,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 colorLabel1: Color @@ -39908,12 +40243,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1361481440} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 116.2, y: 15, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 973550119} - m_RootOrder: 5 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &1415527221 GameObject: @@ -39943,9 +40279,9 @@ RectTransform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1090639377} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -40022,9 +40358,9 @@ RectTransform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1090639377} - m_RootOrder: 0 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -40093,7 +40429,7 @@ GameObject: m_IsActive: 1 --- !u!199 &1554756582 ParticleSystemRenderer: - serializedVersion: 6 + serializedVersion: 7 m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} @@ -40103,11 +40439,17 @@ ParticleSystemRenderer: m_CastShadows: 0 m_ReceiveShadows: 0 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 0 m_ReflectionProbeUsage: 0 m_RayTracingMode: 0 m_RayTraceProcedural: 0 + m_RayTracingAccelStructBuildFlagsOverride: 0 + m_RayTracingAccelStructBuildFlags: 1 + m_SmallMeshCulling: 1 + m_ForceMeshLod: -1 + m_MeshLodSelectionBias: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -40130,10 +40472,13 @@ ParticleSystemRenderer: m_AutoUVMaxDistance: 0.5 m_AutoUVMaxAngle: 89 m_LightmapParameters: {fileID: 0} + m_GlobalIlluminationMeshLod: 0 m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 + m_MaskInteraction: 0 m_RenderMode: 0 + m_MeshDistribution: 0 m_SortMode: 1 m_MinParticleSize: 0 m_MaxParticleSize: 0.5 @@ -40146,18 +40491,23 @@ ParticleSystemRenderer: m_RenderAlignment: 0 m_Pivot: {x: 0, y: 0, z: 0} m_Flip: {x: 0, y: 0, z: 0} - m_UseCustomVertexStreams: 0 m_EnableGPUInstancing: 1 m_ApplyActiveColorSpace: 1 m_AllowRoll: 1 m_FreeformStretching: 0 m_RotateWithStretchDirection: 1 + m_UseCustomVertexStreams: 0 m_VertexStreams: 00010304 + m_UseCustomTrailVertexStreams: 0 + m_TrailVertexStreams: 00010304 m_Mesh: {fileID: 0} m_Mesh1: {fileID: 0} m_Mesh2: {fileID: 0} m_Mesh3: {fileID: 0} - m_MaskInteraction: 0 + m_MeshWeighting: 1 + m_MeshWeighting1: 1 + m_MeshWeighting2: 1 + m_MeshWeighting3: 1 --- !u!198 &1554756583 ParticleSystem: m_ObjectHideFlags: 0 @@ -40165,19 +40515,19 @@ ParticleSystem: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1554756581} - serializedVersion: 7 + serializedVersion: 8 lengthInSec: 5 simulationSpeed: 1 stopAction: 0 cullingMode: 0 ringBufferMode: 0 ringBufferLoopRange: {x: 0, y: 1} + emitterVelocityMode: 0 looping: 1 prewarm: 0 playOnAwake: 1 useUnscaledTime: 0 autoRandomSeed: 1 - useRigidbodyForVelocity: 1 startDelay: serializedVersion: 2 minMaxState: 0 @@ -40376,6 +40726,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -40405,6 +40756,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 startSize: @@ -40726,7 +41078,9 @@ ParticleSystem: m_PostInfinity: 2 m_RotationOrder: 4 randomizeRotationDirection: 0 + gravitySource: 0 maxNumParticles: 5000 + customEmitterVelocity: {x: 0, y: 0, z: 0} size3D: 0 rotation3D: 0 gravityModifier: @@ -41472,6 +41826,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -41501,6 +41856,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 UVModule: @@ -43721,6 +44077,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -43750,6 +44107,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 range: {x: 0, y: 1} @@ -44139,6 +44497,7 @@ ParticleSystem: m_RotationOrder: 4 minVertexDistance: 0.2 textureMode: 0 + textureScale: {x: 1, y: 1} ribbonCount: 1 shadowBias: 0.5 worldSpace: 0 @@ -44181,6 +44540,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -44210,6 +44570,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 widthOverTrail: @@ -44297,6 +44658,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -44326,6 +44688,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 CustomDataModule: @@ -44364,6 +44727,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -44393,6 +44757,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 colorLabel0: Color @@ -44646,6 +45011,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -44675,6 +45041,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 colorLabel1: Color @@ -44901,12 +45268,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1554756581} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 50, y: 15, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 973550119} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &1605974626 GameObject: @@ -44933,16 +45301,17 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1605974626} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 95.6, y: 15, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1735128053} - m_RootOrder: 4 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!199 &1605974628 ParticleSystemRenderer: - serializedVersion: 6 + serializedVersion: 7 m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} @@ -44952,11 +45321,17 @@ ParticleSystemRenderer: m_CastShadows: 0 m_ReceiveShadows: 0 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 0 m_ReflectionProbeUsage: 0 m_RayTracingMode: 0 m_RayTraceProcedural: 0 + m_RayTracingAccelStructBuildFlagsOverride: 0 + m_RayTracingAccelStructBuildFlags: 1 + m_SmallMeshCulling: 1 + m_ForceMeshLod: -1 + m_MeshLodSelectionBias: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -44979,10 +45354,13 @@ ParticleSystemRenderer: m_AutoUVMaxDistance: 0.5 m_AutoUVMaxAngle: 89 m_LightmapParameters: {fileID: 0} + m_GlobalIlluminationMeshLod: 0 m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 + m_MaskInteraction: 0 m_RenderMode: 0 + m_MeshDistribution: 0 m_SortMode: 1 m_MinParticleSize: 0 m_MaxParticleSize: 0.5 @@ -44995,18 +45373,23 @@ ParticleSystemRenderer: m_RenderAlignment: 0 m_Pivot: {x: 0, y: 0, z: 0} m_Flip: {x: 0, y: 0, z: 0} - m_UseCustomVertexStreams: 1 m_EnableGPUInstancing: 1 m_ApplyActiveColorSpace: 1 m_AllowRoll: 1 m_FreeformStretching: 0 m_RotateWithStretchDirection: 1 + m_UseCustomVertexStreams: 1 m_VertexStreams: 000103040508 + m_UseCustomTrailVertexStreams: 0 + m_TrailVertexStreams: 00010304 m_Mesh: {fileID: 0} m_Mesh1: {fileID: 0} m_Mesh2: {fileID: 0} m_Mesh3: {fileID: 0} - m_MaskInteraction: 0 + m_MeshWeighting: 1 + m_MeshWeighting1: 1 + m_MeshWeighting2: 1 + m_MeshWeighting3: 1 --- !u!198 &1605974629 ParticleSystem: m_ObjectHideFlags: 0 @@ -45014,19 +45397,19 @@ ParticleSystem: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1605974626} - serializedVersion: 7 + serializedVersion: 8 lengthInSec: 5 simulationSpeed: 1 stopAction: 0 cullingMode: 0 ringBufferMode: 0 ringBufferLoopRange: {x: 0, y: 1} + emitterVelocityMode: 0 looping: 1 prewarm: 0 playOnAwake: 1 useUnscaledTime: 0 autoRandomSeed: 1 - useRigidbodyForVelocity: 1 startDelay: serializedVersion: 2 minMaxState: 0 @@ -45225,6 +45608,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -45254,6 +45638,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 startSize: @@ -45575,7 +45960,9 @@ ParticleSystem: m_PostInfinity: 2 m_RotationOrder: 4 randomizeRotationDirection: 0 + gravitySource: 0 maxNumParticles: 5000 + customEmitterVelocity: {x: 0, y: 0, z: 0} size3D: 0 rotation3D: 0 gravityModifier: @@ -46321,6 +46708,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -46350,6 +46738,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 UVModule: @@ -48570,6 +48959,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -48599,6 +48989,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 range: {x: 0, y: 1} @@ -48988,6 +49379,7 @@ ParticleSystem: m_RotationOrder: 4 minVertexDistance: 0.2 textureMode: 0 + textureScale: {x: 1, y: 1} ribbonCount: 1 shadowBias: 0.5 worldSpace: 0 @@ -49030,6 +49422,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -49059,6 +49452,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 widthOverTrail: @@ -49146,6 +49540,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -49175,6 +49570,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 CustomDataModule: @@ -49213,6 +49609,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -49242,6 +49639,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 colorLabel0: Color @@ -49495,6 +49893,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -49524,6 +49923,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 colorLabel1: Color @@ -49772,7 +50172,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 172515602e62fb746b5d573b38a5fe58, type: 3} m_Name: m_EditorClassIdentifier: - isGlobal: 1 + m_IsGlobal: 1 priority: 0 blendDistance: 0 weight: 1 @@ -49784,12 +50184,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1674862041} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} - m_RootOrder: 8 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &1735128052 GameObject: @@ -49814,9 +50215,11 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1735128052} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: -30.2, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: - {fileID: 1189360802} - {fileID: 839247705} @@ -49825,7 +50228,6 @@ Transform: - {fileID: 1605974627} - {fileID: 162109878} m_Father: {fileID: 0} - m_RootOrder: 6 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &1817530608 GameObject: @@ -49858,6 +50260,9 @@ MonoBehaviour: m_EditorClassIdentifier: m_Profile: {fileID: 0} m_StaticLightingSkyUniqueID: 0 + m_StaticLightingCloudsUniqueID: 0 + m_StaticLightingVolumetricClouds: 0 + bounces: 1 --- !u!4 &1817530610 Transform: m_ObjectHideFlags: 1 @@ -49865,12 +50270,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1817530608} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} - m_RootOrder: 7 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &1919406429 GameObject: @@ -49900,9 +50306,9 @@ RectTransform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1090639377} - m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} @@ -49962,7 +50368,6 @@ GameObject: - component: {fileID: 1966227930} - component: {fileID: 1966227929} - component: {fileID: 1966227928} - - component: {fileID: 1966227927} m_Layer: 0 m_Name: Background m_TagString: Untagged @@ -49970,19 +50375,6 @@ GameObject: m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!65 &1966227927 -BoxCollider: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 1966227926} - m_Material: {fileID: 0} - m_IsTrigger: 0 - m_Enabled: 1 - serializedVersion: 2 - m_Size: {x: 1, y: 1, z: 1} - m_Center: {x: 0, y: 0, z: 0} --- !u!23 &1966227928 MeshRenderer: m_ObjectHideFlags: 0 @@ -49994,11 +50386,17 @@ MeshRenderer: m_CastShadows: 1 m_ReceiveShadows: 1 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 m_RayTracingMode: 2 m_RayTraceProcedural: 0 + m_RayTracingAccelStructBuildFlagsOverride: 0 + m_RayTracingAccelStructBuildFlags: 1 + m_SmallMeshCulling: 1 + m_ForceMeshLod: -1 + m_MeshLodSelectionBias: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -50020,9 +50418,11 @@ MeshRenderer: m_AutoUVMaxDistance: 0.5 m_AutoUVMaxAngle: 89 m_LightmapParameters: {fileID: 0} + m_GlobalIlluminationMeshLod: 0 m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 + m_MaskInteraction: 0 m_AdditionalVertexStreams: {fileID: 0} --- !u!33 &1966227929 MeshFilter: @@ -50039,12 +50439,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 1966227926} + serializedVersion: 2 m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 44.9, y: 0, z: 64.5} m_LocalScale: {x: -187.9, y: -99.3, z: 100} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} - m_RootOrder: 3 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &2102630555 GameObject: @@ -50066,7 +50467,7 @@ GameObject: m_IsActive: 1 --- !u!199 &2102630556 ParticleSystemRenderer: - serializedVersion: 6 + serializedVersion: 7 m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} @@ -50076,11 +50477,17 @@ ParticleSystemRenderer: m_CastShadows: 0 m_ReceiveShadows: 0 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 0 m_ReflectionProbeUsage: 0 m_RayTracingMode: 0 m_RayTraceProcedural: 0 + m_RayTracingAccelStructBuildFlagsOverride: 0 + m_RayTracingAccelStructBuildFlags: 1 + m_SmallMeshCulling: 1 + m_ForceMeshLod: -1 + m_MeshLodSelectionBias: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -50103,10 +50510,13 @@ ParticleSystemRenderer: m_AutoUVMaxDistance: 0.5 m_AutoUVMaxAngle: 89 m_LightmapParameters: {fileID: 0} + m_GlobalIlluminationMeshLod: 0 m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 + m_MaskInteraction: 0 m_RenderMode: 0 + m_MeshDistribution: 0 m_SortMode: 1 m_MinParticleSize: 0 m_MaxParticleSize: 0.5 @@ -50119,18 +50529,23 @@ ParticleSystemRenderer: m_RenderAlignment: 0 m_Pivot: {x: 0, y: 0, z: 0} m_Flip: {x: 0, y: 0, z: 0} - m_UseCustomVertexStreams: 0 m_EnableGPUInstancing: 1 m_ApplyActiveColorSpace: 1 m_AllowRoll: 1 m_FreeformStretching: 0 m_RotateWithStretchDirection: 1 + m_UseCustomVertexStreams: 0 m_VertexStreams: 00010304 + m_UseCustomTrailVertexStreams: 0 + m_TrailVertexStreams: 00010304 m_Mesh: {fileID: 0} m_Mesh1: {fileID: 0} m_Mesh2: {fileID: 0} m_Mesh3: {fileID: 0} - m_MaskInteraction: 0 + m_MeshWeighting: 1 + m_MeshWeighting1: 1 + m_MeshWeighting2: 1 + m_MeshWeighting3: 1 --- !u!198 &2102630557 ParticleSystem: m_ObjectHideFlags: 0 @@ -50138,19 +50553,19 @@ ParticleSystem: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 2102630555} - serializedVersion: 7 + serializedVersion: 8 lengthInSec: 5 simulationSpeed: 1 stopAction: 0 cullingMode: 0 ringBufferMode: 0 ringBufferLoopRange: {x: 0, y: 1} + emitterVelocityMode: 0 looping: 1 prewarm: 0 playOnAwake: 1 useUnscaledTime: 0 autoRandomSeed: 1 - useRigidbodyForVelocity: 1 startDelay: serializedVersion: 2 minMaxState: 0 @@ -50349,6 +50764,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -50378,6 +50794,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 startSize: @@ -50699,7 +51116,9 @@ ParticleSystem: m_PostInfinity: 2 m_RotationOrder: 4 randomizeRotationDirection: 0.5 + gravitySource: 0 maxNumParticles: 5000 + customEmitterVelocity: {x: 0, y: 0, z: 0} size3D: 0 rotation3D: 0 gravityModifier: @@ -51445,6 +51864,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -51474,6 +51894,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 UVModule: @@ -53694,6 +54115,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -53723,6 +54145,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 range: {x: 0, y: 1} @@ -54112,6 +54535,7 @@ ParticleSystem: m_RotationOrder: 4 minVertexDistance: 0.2 textureMode: 0 + textureScale: {x: 1, y: 1} ribbonCount: 1 shadowBias: 0.5 worldSpace: 0 @@ -54154,6 +54578,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -54183,6 +54608,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 widthOverTrail: @@ -54270,6 +54696,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -54299,6 +54726,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 CustomDataModule: @@ -54337,6 +54765,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -54366,6 +54795,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 colorLabel0: Color @@ -54619,6 +55049,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -54648,6 +55079,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 colorLabel1: Color @@ -54874,12 +55306,13 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 2102630555} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 26.9, y: 15, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 973550119} - m_RootOrder: 1 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!1 &2107816550 GameObject: @@ -54906,16 +55339,17 @@ Transform: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 2107816550} + serializedVersion: 2 m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} m_LocalPosition: {x: 50, y: 15, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} + m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 1735128053} - m_RootOrder: 2 m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!199 &2107816552 ParticleSystemRenderer: - serializedVersion: 6 + serializedVersion: 7 m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} @@ -54925,11 +55359,17 @@ ParticleSystemRenderer: m_CastShadows: 0 m_ReceiveShadows: 0 m_DynamicOccludee: 1 + m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 0 m_ReflectionProbeUsage: 0 m_RayTracingMode: 0 m_RayTraceProcedural: 0 + m_RayTracingAccelStructBuildFlagsOverride: 0 + m_RayTracingAccelStructBuildFlags: 1 + m_SmallMeshCulling: 1 + m_ForceMeshLod: -1 + m_MeshLodSelectionBias: 0 m_RenderingLayerMask: 1 m_RendererPriority: 0 m_Materials: @@ -54952,10 +55392,13 @@ ParticleSystemRenderer: m_AutoUVMaxDistance: 0.5 m_AutoUVMaxAngle: 89 m_LightmapParameters: {fileID: 0} + m_GlobalIlluminationMeshLod: 0 m_SortingLayerID: 0 m_SortingLayer: 0 m_SortingOrder: 0 + m_MaskInteraction: 0 m_RenderMode: 0 + m_MeshDistribution: 0 m_SortMode: 1 m_MinParticleSize: 0 m_MaxParticleSize: 0.5 @@ -54968,18 +55411,23 @@ ParticleSystemRenderer: m_RenderAlignment: 0 m_Pivot: {x: 0, y: 0, z: 0} m_Flip: {x: 0, y: 0, z: 0} - m_UseCustomVertexStreams: 0 m_EnableGPUInstancing: 1 m_ApplyActiveColorSpace: 1 m_AllowRoll: 1 m_FreeformStretching: 0 m_RotateWithStretchDirection: 1 + m_UseCustomVertexStreams: 0 m_VertexStreams: 00010304 + m_UseCustomTrailVertexStreams: 0 + m_TrailVertexStreams: 00010304 m_Mesh: {fileID: 0} m_Mesh1: {fileID: 0} m_Mesh2: {fileID: 0} m_Mesh3: {fileID: 0} - m_MaskInteraction: 0 + m_MeshWeighting: 1 + m_MeshWeighting1: 1 + m_MeshWeighting2: 1 + m_MeshWeighting3: 1 --- !u!198 &2107816553 ParticleSystem: m_ObjectHideFlags: 0 @@ -54987,19 +55435,19 @@ ParticleSystem: m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 2107816550} - serializedVersion: 7 + serializedVersion: 8 lengthInSec: 5 simulationSpeed: 1 stopAction: 0 cullingMode: 0 ringBufferMode: 0 ringBufferLoopRange: {x: 0, y: 1} + emitterVelocityMode: 0 looping: 1 prewarm: 0 playOnAwake: 1 useUnscaledTime: 0 autoRandomSeed: 1 - useRigidbodyForVelocity: 1 startDelay: serializedVersion: 2 minMaxState: 0 @@ -55198,6 +55646,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -55227,6 +55676,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 startSize: @@ -55548,7 +55998,9 @@ ParticleSystem: m_PostInfinity: 2 m_RotationOrder: 4 randomizeRotationDirection: 0 + gravitySource: 0 maxNumParticles: 5000 + customEmitterVelocity: {x: 0, y: 0, z: 0} size3D: 0 rotation3D: 0 gravityModifier: @@ -56294,6 +56746,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -56323,6 +56776,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 UVModule: @@ -58543,6 +58997,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -58572,6 +59027,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 range: {x: 0, y: 1} @@ -58961,6 +59417,7 @@ ParticleSystem: m_RotationOrder: 4 minVertexDistance: 0.2 textureMode: 0 + textureScale: {x: 1, y: 1} ribbonCount: 1 shadowBias: 0.5 worldSpace: 0 @@ -59003,6 +59460,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -59032,6 +59490,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 widthOverTrail: @@ -59119,6 +59578,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -59148,6 +59608,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 CustomDataModule: @@ -59186,6 +59647,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -59215,6 +59677,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 colorLabel0: Color @@ -59468,6 +59931,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 minGradient: @@ -59497,6 +59961,7 @@ ParticleSystem: atime6: 0 atime7: 0 m_Mode: 0 + m_ColorSpace: -1 m_NumColorKeys: 2 m_NumAlphaKeys: 2 colorLabel1: Color @@ -59716,3 +60181,16 @@ ParticleSystem: m_PostInfinity: 2 m_RotationOrder: 4 vectorLabel1_3: W +--- !u!1660057539 &9223372036854775807 +SceneRoots: + m_ObjectHideFlags: 0 + m_Roots: + - {fileID: 963194228} + - {fileID: 705507995} + - {fileID: 1090639377} + - {fileID: 1966227930} + - {fileID: 1294102437} + - {fileID: 973550119} + - {fileID: 1735128053} + - {fileID: 1817530610} + - {fileID: 1674862043} diff --git a/Packages/com.unity.render-pipelines.high-definition/Samples~/TransparentSamples/Scenes/Scene Resources/Transparent Required Settings.asset b/Packages/com.unity.render-pipelines.high-definition/Samples~/TransparentSamples/Scenes/Scene Resources/Transparent Required Settings.asset index b06ff1b6b14..d97bd7f9e61 100644 --- a/Packages/com.unity.render-pipelines.high-definition/Samples~/TransparentSamples/Scenes/Scene Resources/Transparent Required Settings.asset +++ b/Packages/com.unity.render-pipelines.high-definition/Samples~/TransparentSamples/Scenes/Scene Resources/Transparent Required Settings.asset @@ -45,7 +45,7 @@ MonoBehaviour: validationType: 0 uiSectionInt: 16 uiSubSectionInt: 64 - - m_name: Set Compute Thickness Layer Mask to TransparentFX & IgnoreRaycast + - m_name: Check TransparentFX & IgnoreRaycast in Layer Mask m_description: Set Compute Thickness Layer Mask to TransparentFX & IgnoreRaycast propertyPath: m_RenderPipelineSettings.computeThicknessLayerMask valueType: 1 diff --git a/Packages/com.unity.render-pipelines.universal/Editor/2D/Overrides/SortingGroupEditor2DURP.cs b/Packages/com.unity.render-pipelines.universal/Editor/2D/Overrides/SortingGroupEditor2DURP.cs index 1c69efee40f..cc3a0064490 100644 --- a/Packages/com.unity.render-pipelines.universal/Editor/2D/Overrides/SortingGroupEditor2DURP.cs +++ b/Packages/com.unity.render-pipelines.universal/Editor/2D/Overrides/SortingGroupEditor2DURP.cs @@ -1,101 +1,120 @@ -using UnityEditorInternal; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.Rendering.Universal; namespace UnityEditor { - [CustomEditor(typeof(UnityEngine.Rendering.SortingGroup))] + [CustomEditor(typeof(SortingGroup))] [SupportedOnRenderPipeline(typeof(UniversalRenderPipelineAsset))] [CanEditMultipleObjects] internal class SortingGroupEditor2DURP : SortingGroupEditor { - private static class Styles + private enum SortType { - public static GUIContent sort3DAs2D = EditorGUIUtility.TrTextContent("Sort 3D as 2D", "Clears z values on 3D meshes affected by a Sorting Group allowing them to sort with other 2D objects and Sort 3D as 2D sorting groups."); + Default, + SortAtRoot, + Sort3DAs2D + } + + private static class GUIStyles + { + public static GUIContent Default = EditorGUIUtility.TrTextContent("Sorting Type", + "Default sorting based on sorting layer and sorting order."); + + public static GUIContent sortAtRootStyle = EditorGUIUtility.TrTextContent("Sorting Type", + "Ignores all parent Sorting Groups and sorts at the root level against other Sorting Groups and Renderers"); + + public static GUIContent sort3DAs2D = EditorGUIUtility.TrTextContent("Sorting Type", + "Clears z values on 3D meshes affected by a Sorting Group allowing them to sort with other 2D objects and Sort 3D as 2D sorting groups. This option also enables Sort At Root"); } - private SerializedProperty m_SortingOrder; - private SerializedProperty m_SortingLayerID; private SerializedProperty m_Sort3DAs2D; + private SortType m_SortType; public override void OnEnable() { base.OnEnable(); - alwaysAllowExpansion = true; - m_SortingOrder = serializedObject.FindProperty("m_SortingOrder"); - m_SortingLayerID = serializedObject.FindProperty("m_SortingLayerID"); m_Sort3DAs2D = serializedObject.FindProperty("m_Sort3DAs2D"); + + // Initialize m_SortType + m_SortType = m_Sort3DAs2D.boolValue ? SortType.Sort3DAs2D : m_SortAtRoot.boolValue ? SortType.SortAtRoot : SortType.Default; } - public RenderAs2D TryToFindCreatedRenderAs2D(SortingGroup sortingGroup) + void OnInspectorGUIFor2D() { - RenderAs2D[] renderAs2Ds = sortingGroup.GetComponents(); - foreach (RenderAs2D renderAs2D in renderAs2Ds) - { - if (renderAs2D.IsOwner(sortingGroup)) - return renderAs2D; - } + serializedObject.Update(); - return null; - } + SortingLayerEditorUtility.RenderSortingLayerFields(m_SortingOrder, m_SortingLayerID); - bool DrawToggleWithLayout(bool flatten, GUIContent content) - { - Rect rect = EditorGUILayout.GetControlRect(); - var boolValue = EditorGUI.Toggle(rect, content, flatten); - return boolValue; - } + var prevSortType = m_SortType; + var label = m_Sort3DAs2D.boolValue ? GUIStyles.sort3DAs2D : m_SortAtRoot.boolValue ? GUIStyles.sortAtRootStyle : GUIStyles.Default; + m_SortType = (SortType)EditorGUILayout.EnumPopup(label, m_SortType); - void DirtyScene() - { - UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(UnityEngine.SceneManagement.SceneManager.GetActiveScene()); - } + if (prevSortType != m_SortType) + { + switch (m_SortType) + { + case SortType.SortAtRoot: + m_SortAtRoot.boolValue = true; + m_Sort3DAs2D.boolValue = false; + break; + + case SortType.Sort3DAs2D: + m_SortAtRoot.boolValue = true; + m_Sort3DAs2D.boolValue = true; + break; + + default: + m_SortAtRoot.boolValue = false; + m_Sort3DAs2D.boolValue = false; + break; + } + } - void RenderSort3DAs2D() - { - EditorGUILayout.PropertyField(m_Sort3DAs2D, Styles.sort3DAs2D); foreach (var target in targets) { SortingGroup sortingGroup = (SortingGroup)target; + GameObject go = sortingGroup.gameObject; + go.TryGetComponent(out RenderAs2D renderAs2D); + if (sortingGroup.sort3DAs2D) { - GameObject go = sortingGroup.gameObject; - go.TryGetComponent(out RenderAs2D renderAs2D); - if (renderAs2D != null && !renderAs2D.IsOwner(sortingGroup)) { - Component.DestroyImmediate(renderAs2D, true); + DestroyImmediate(renderAs2D, true); renderAs2D = null; } - if(renderAs2D == null) + if (renderAs2D == null) { Material mat = AssetDatabase.LoadAssetAtPath("Packages/com.unity.render-pipelines.universal/Runtime/Materials/RenderAs2D-Flattening.mat"); renderAs2D = go.AddComponent(); renderAs2D.Init(sortingGroup); renderAs2D.material = mat; - EditorUtility.SetDirty(sortingGroup.gameObject); + EditorUtility.SetDirty(go); + } + } + else + { + if (renderAs2D != null) + { + DestroyImmediate(renderAs2D, true); + renderAs2D = null; } } } + + serializedObject.ApplyModifiedProperties(); } public override void OnInspectorGUI() { - serializedObject.Update(); - var rpAsset = UniversalRenderPipeline.asset; if (rpAsset != null && (rpAsset.scriptableRenderer is Renderer2D)) - { - SortingLayerEditorUtility.RenderSortingLayerFields(m_SortingOrder, m_SortingLayerID); - RenderSort3DAs2D(); - } + OnInspectorGUIFor2D(); else base.OnInspectorGUI(); - - serializedObject.ApplyModifiedProperties(); } } } diff --git a/Packages/com.unity.render-pipelines.universal/Editor/Camera/UniversalRenderPipelineCameraUI.Drawers.cs b/Packages/com.unity.render-pipelines.universal/Editor/Camera/UniversalRenderPipelineCameraUI.Drawers.cs index b98125c0477..63daeb2c342 100644 --- a/Packages/com.unity.render-pipelines.universal/Editor/Camera/UniversalRenderPipelineCameraUI.Drawers.cs +++ b/Packages/com.unity.render-pipelines.universal/Editor/Camera/UniversalRenderPipelineCameraUI.Drawers.cs @@ -1,3 +1,4 @@ +using System; using UnityEngine; using UnityEngine.Rendering.Universal; @@ -56,6 +57,7 @@ public enum ExpandableAdditional public static readonly CED.IDrawer[] Inspector = { CED.Group( + PrepareOnTileValidationWarning, DrawerCameraType ), SectionProjectionSettings, @@ -111,7 +113,103 @@ static void DrawerStackCameras(UniversalRenderPipelineSerializedCamera p, Editor if (owner is UniversalRenderPipelineCameraEditor cameraEditor) { cameraEditor.DrawStackSettings(); + DisplayOnTileValidationWarning(p.cameras, p => p.arraySize != 0, Styles.cameraStackLabelForOnTileValidation, p); } } + + struct OnTileValidationInfos + { + public readonly bool enabled; + public readonly string rendererName; + public readonly ScriptableRendererData assetToOpen; + public OnTileValidationInfos(string rendererName, ScriptableRendererData assetToOpen) + { + enabled = true; + this.rendererName = rendererName; + this.assetToOpen = assetToOpen; + } + } + + static OnTileValidationInfos lastOnTileValidationInfos; + + static void PrepareOnTileValidationWarning(UniversalRenderPipelineSerializedCamera serialized, Editor owner) + { + // Rules: + // - mono selection: Display warning if RendererData's OnTileValidation is enabled (with 'Open' behaviour) + // - multi selection: + // - Display warning if all RendererData's OnTileValidation are enabled + // - Only have 'Open' behaviour if all RendererData are the same + + lastOnTileValidationInfos = default; + + // Note: UniversalRenderPipeline.asset should not be null or this inspector would not be shown. + // Just in case off though: + if (UniversalRenderPipeline.asset == null) + return; + + bool HasOnTileValidationAtIndex(int index, out ScriptableRendererData rendererData) + => UniversalRenderPipeline.asset.TryGetRendererData(index, out rendererData) + && rendererData is UniversalRendererData universalData + && universalData.onTileValidation; + + // If impacted section are not opened, early exit + if (!(k_ExpandedState[Expandable.Rendering] || k_ExpandedState[Expandable.Stack])) + return; + + ScriptableRendererData rendererData = null; + + if (!serialized.renderer.hasMultipleDifferentValues) + { + if (!HasOnTileValidationAtIndex(serialized.renderer.intValue, out rendererData)) + return; + + lastOnTileValidationInfos = new OnTileValidationInfos($"'{rendererData.name}'", assetToOpen: rendererData); + return; + } + + bool targetSameAsset = true; + var firstAdditionalData = (UniversalAdditionalCameraData)serialized.serializedAdditionalDataObject.targetObjects[0]; + if (!HasOnTileValidationAtIndex(firstAdditionalData.rendererIndex, out rendererData)) + return; + + using var o = StringBuilderPool.Get(out var sb); + sb.Append("'"); + sb.Append(rendererData.name); + sb.Append("'"); + for (int i = 1; i < serialized.serializedAdditionalDataObject.targetObjects.Length; ++i) + { + var additionalCameraData = (UniversalAdditionalCameraData)serialized.serializedAdditionalDataObject.targetObjects[i]; + if (!HasOnTileValidationAtIndex(additionalCameraData.rendererIndex, out var otherRenderer)) + return; + + targetSameAsset &= rendererData == otherRenderer; + sb.Append(", '"); + sb.Append(otherRenderer.name); + sb.Append("'"); + } + + lastOnTileValidationInfos = new OnTileValidationInfos(sb.ToString(), assetToOpen: targetSameAsset ? rendererData : null); + } + + static void DisplayOnTileValidationWarning(SerializedProperty prop, Func shouldDisplayWarning, GUIContent label, UniversalRenderPipelineSerializedCamera serialized) + { + if (!lastOnTileValidationInfos.enabled + || prop == null + || shouldDisplayWarning == null + || prop.hasMultipleDifferentValues + || !shouldDisplayWarning(prop)) + return; + + if (lastOnTileValidationInfos.assetToOpen != null) + CoreEditorUtils.DrawFixMeBox( + string.Format(Styles.formaterOnTileValidation, label == null ? prop.displayName : label.text, lastOnTileValidationInfos.rendererName), + MessageType.Warning, + "Open", + () => AssetDatabase.OpenAsset(lastOnTileValidationInfos.assetToOpen)); + else + EditorGUILayout.HelpBox( + string.Format(Styles.formaterOnTileValidation, label == null ? prop.displayName : label.text, lastOnTileValidationInfos.rendererName), + MessageType.Warning); + } } } diff --git a/Packages/com.unity.render-pipelines.universal/Editor/Camera/UniversalRenderPipelineCameraUI.Rendering.Drawers.cs b/Packages/com.unity.render-pipelines.universal/Editor/Camera/UniversalRenderPipelineCameraUI.Rendering.Drawers.cs index 1e2583279e6..582444b553a 100644 --- a/Packages/com.unity.render-pipelines.universal/Editor/Camera/UniversalRenderPipelineCameraUI.Rendering.Drawers.cs +++ b/Packages/com.unity.render-pipelines.universal/Editor/Camera/UniversalRenderPipelineCameraUI.Rendering.Drawers.cs @@ -133,7 +133,8 @@ static Rendering() OverlayCameraRenderTypeDrawer, CED.Group( CameraUI.Rendering.Drawer_Rendering_CullingMask, - CameraUI.Rendering.Drawer_Rendering_OcclusionCulling + CameraUI.Rendering.Drawer_Rendering_OcclusionCulling, + OcclusionCullingWithWarningOnTileValidation ) ), CED.noop, @@ -148,7 +149,8 @@ static Rendering() FoldoutOption.Indent, CED.Group( CameraUI.Rendering.Drawer_Rendering_CullingMask, - CameraUI.Rendering.Drawer_Rendering_OcclusionCulling + CameraUI.Rendering.Drawer_Rendering_OcclusionCulling, + OcclusionCullingWithWarningOnTileValidation ) ); } @@ -272,24 +274,38 @@ static void DrawerRenderingTAAQuality(UniversalRenderPipelineSerializedCamera p, } } - static void DrawerRenderingRenderPostProcessing(UniversalRenderPipelineSerializedCamera p, Editor owner) + static void DrawerRenderingRenderPostProcessing(UniversalRenderPipelineSerializedCamera serialized, Editor owner) { - EditorGUILayout.PropertyField(p.renderPostProcessing, Styles.renderPostProcessing); + EditorGUILayout.PropertyField(serialized.renderPostProcessing, Styles.renderPostProcessing); + + // There is other issue displayed if one try to use PP while Renderer does not allow it + bool enabledInRenderer = serialized.renderer.hasMultipleDifferentValues + || (UniversalRenderPipeline.asset.TryGetRendererData(serialized.renderer.intValue, out var rendererData) + && rendererData is UniversalRendererData universalData + && universalData.postProcessData != null); + DisplayOnTileValidationWarning(serialized.renderPostProcessing, p => p.boolValue && enabledInRenderer, Styles.renderPostProcessing, serialized); } - static void DrawerRenderingPriority(UniversalRenderPipelineSerializedCamera p, Editor owner) + static void DrawerRenderingPriority(UniversalRenderPipelineSerializedCamera serialized, Editor owner) { - EditorGUILayout.PropertyField(p.baseCameraSettings.depth, Styles.priority); + EditorGUILayout.PropertyField(serialized.baseCameraSettings.depth, Styles.priority); } - static void DrawerRenderingDepthTexture(UniversalRenderPipelineSerializedCamera p, Editor owner) + static void DrawerRenderingDepthTexture(UniversalRenderPipelineSerializedCamera serialized, Editor owner) { - EditorGUILayout.PropertyField(p.renderDepth, Styles.requireDepthTexture); + EditorGUILayout.PropertyField(serialized.renderDepth, Styles.requireDepthTexture); + DisplayOnTileValidationWarning(serialized.renderDepth, p => p.intValue == (int)CameraOverrideOption.On, Styles.requireDepthTexture, serialized); } - static void DrawerRenderingOpaqueTexture(UniversalRenderPipelineSerializedCamera p, Editor owner) + static void DrawerRenderingOpaqueTexture(UniversalRenderPipelineSerializedCamera serialized, Editor owner) { - EditorGUILayout.PropertyField(p.renderOpaque, Styles.requireOpaqueTexture); + EditorGUILayout.PropertyField(serialized.renderOpaque, Styles.requireOpaqueTexture); + DisplayOnTileValidationWarning(serialized.renderOpaque, p => p.intValue == (int)CameraOverrideOption.On, Styles.requireOpaqueTexture, serialized); + } + + static void OcclusionCullingWithWarningOnTileValidation(UniversalRenderPipelineSerializedCamera serialized, Editor owner) + { + DisplayOnTileValidationWarning(serialized.baseCameraSettings.occlusionCulling, p => p.boolValue, CameraUI.Rendering.Styles.occlusionCulling, serialized); } } } diff --git a/Packages/com.unity.render-pipelines.universal/Editor/Camera/UniversalRenderPipelineCameraUI.Rendering.Skin.cs b/Packages/com.unity.render-pipelines.universal/Editor/Camera/UniversalRenderPipelineCameraUI.Rendering.Skin.cs index 24045a8f2ca..c6fdd33ccb1 100644 --- a/Packages/com.unity.render-pipelines.universal/Editor/Camera/UniversalRenderPipelineCameraUI.Rendering.Skin.cs +++ b/Packages/com.unity.render-pipelines.universal/Editor/Camera/UniversalRenderPipelineCameraUI.Rendering.Skin.cs @@ -1,4 +1,3 @@ -using System.Linq; using UnityEngine; namespace UnityEditor.Rendering.Universal diff --git a/Packages/com.unity.render-pipelines.universal/Editor/Camera/UniversalRenderPipelineCameraUI.Skin.cs b/Packages/com.unity.render-pipelines.universal/Editor/Camera/UniversalRenderPipelineCameraUI.Skin.cs index f5db65305bd..5bd359b4ba3 100644 --- a/Packages/com.unity.render-pipelines.universal/Editor/Camera/UniversalRenderPipelineCameraUI.Skin.cs +++ b/Packages/com.unity.render-pipelines.universal/Editor/Camera/UniversalRenderPipelineCameraUI.Skin.cs @@ -6,13 +6,16 @@ static partial class UniversalRenderPipelineCameraUI { public class Styles { - public static GUIContent cameraType = EditorGUIUtility.TrTextContent("Render Type", "Defines if a camera renders directly to a target or overlays on top of another camera’s output. Overlay option is not available when Deferred Render Data is in use."); + public static readonly GUIContent cameraType = EditorGUIUtility.TrTextContent("Render Type", "Defines if a camera renders directly to a target or overlays on top of another camera’s output. Overlay option is not available when Deferred Render Data is in use."); public static readonly string pixelPerfectInfo = L10n.Tr("Projection settings have been overriden by the Pixel Perfect Camera."); // Stack cameras - public static GUIContent stackSettingsText = EditorGUIUtility.TrTextContent("Stack", "The list of overlay cameras assigned to this camera."); - public static GUIContent cameras = EditorGUIUtility.TrTextContent("Cameras", "The list of overlay cameras assigned to this camera."); - public static string inspectorOverlayCameraText = L10n.Tr("Inspector Overlay Camera"); + public static readonly GUIContent stackSettingsText = EditorGUIUtility.TrTextContent("Stack", "The list of overlay cameras assigned to this camera."); + public static readonly GUIContent cameras = EditorGUIUtility.TrTextContent("Cameras", "The list of overlay cameras assigned to this camera."); + public static readonly string inspectorOverlayCameraText = L10n.Tr("Inspector Overlay Camera"); + + public static readonly string formaterOnTileValidation = L10n.Tr("'{0}' will be skipped because it is incompatible with the enabled 'On-Tile Validation' on the assigned Renderer: {1}."); + public static readonly GUIContent cameraStackLabelForOnTileValidation = EditorGUIUtility.TrTextContent("Camera Stacking"); } } } diff --git a/Packages/com.unity.render-pipelines.universal/Editor/ShaderBuildPreprocessor.cs b/Packages/com.unity.render-pipelines.universal/Editor/ShaderBuildPreprocessor.cs index 1e94dfa46f9..e0b82806b1f 100644 --- a/Packages/com.unity.render-pipelines.universal/Editor/ShaderBuildPreprocessor.cs +++ b/Packages/com.unity.render-pipelines.universal/Editor/ShaderBuildPreprocessor.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using UnityEditor.Build; using UnityEditor.Build.Reporting; -using UnityEditor.SceneManagement; using UnityEngine; using UnityEngine.Rendering.Universal; using UnityEngine.Rendering; @@ -134,7 +133,6 @@ class ShaderBuildPreprocessor : IPreprocessBuildWithReport, IPostprocessBuildWit public static bool s_Strip2DPasses; public static bool s_UseSoftShadowQualityLevelKeywords; public static bool s_StripXRVariants; - public static bool s_UsesDynamicLightmaps; public static List supportedFeaturesList { @@ -433,27 +431,6 @@ private static void GetEveryShaderFeatureAndUpdateURPAssets(List // The path for gathering shader features for normal shader stripping private static void HandleEnabledShaderStripping() { - var originalSetup = EditorSceneManager.GetSceneManagerSetup(); - - bool dynamicLightmapsUsed = false; - foreach (EditorBuildSettingsScene scene in EditorBuildSettings.scenes) - { - if (!scene.enabled) continue; - - EditorSceneManager.OpenScene(scene.path, OpenSceneMode.Single); - - if (Lightmapping.HasDynamicGILightmapTextures()) - { - dynamicLightmapsUsed = true; - break; - } - } - - if (originalSetup.Length > 0) - EditorSceneManager.RestoreSceneManagerSetup(originalSetup); - - s_UsesDynamicLightmaps = dynamicLightmapsUsed; - s_Strip2DPasses = true; using (ListPool.Get(out List urpAssets)) { diff --git a/Packages/com.unity.render-pipelines.universal/Editor/ShaderScriptableStripper.cs b/Packages/com.unity.render-pipelines.universal/Editor/ShaderScriptableStripper.cs index 409a3e715a5..35cbf2342c5 100644 --- a/Packages/com.unity.render-pipelines.universal/Editor/ShaderScriptableStripper.cs +++ b/Packages/com.unity.render-pipelines.universal/Editor/ShaderScriptableStripper.cs @@ -31,7 +31,6 @@ internal interface IShaderScriptableStrippingData public bool stripUnusedVariants { get; set; } public bool stripUnusedPostProcessingVariants { get; set; } public bool stripUnusedXRVariants { get; set; } - public bool usesDynamicLightmaps { get; set; } public Shader shader { get; set; } public ShaderType shaderType { get; set; } @@ -70,7 +69,6 @@ internal struct StrippingData : IShaderScriptableStrippingData public bool stripUnusedVariants { get; set; } public bool stripUnusedPostProcessingVariants { get; set; } public bool stripUnusedXRVariants { get; set; } - public bool usesDynamicLightmaps { get; set; } public Shader shader { get; set; } public ShaderType shaderType { get => passData.shaderType; set{} } @@ -405,7 +403,6 @@ internal bool StripUnusedFeatures_ScreenSpaceIrradiance(ref IShaderScriptableStr #if SURFACE_CACHE if (strippingData.PassHasKeyword(m_ScreenSpaceIrradiance)) { - bool useScreenSpaceIrradiance = strippingData.IsShaderFeatureEnabled(ShaderFeatures.SurfaceCache); return !strippingData.IsShaderFeatureEnabled(ShaderFeatures.SurfaceCache) && strippingData.IsKeywordEnabled(m_ScreenSpaceIrradiance); } return false; @@ -1058,17 +1055,15 @@ internal bool StripUnusedPass_2D(ref IShaderScriptableStrippingData strippingDat internal bool StripUnusedPass_Meta(ref IShaderScriptableStrippingData strippingData) { - bool isEnlightenSupported = SupportedRenderingFeatures.active.enlighten && ((int)SupportedRenderingFeatures.active.lightmapBakeTypes | (int)LightmapBakeType.Realtime) != 0; - - // Meta pass is needed in the player for Enlighten Precomputed Realtime GI albedo and emission, as well as Surface Cache Global Illumination. + // Meta pass is needed in the player for Enlighten Precomputed Realtime GI albedo and emission. if (strippingData.passType == PassType.Meta) { - if ((!isEnlightenSupported || !strippingData.usesDynamicLightmaps) - + if (SupportedRenderingFeatures.active.enlighten == false + || ((int)SupportedRenderingFeatures.active.lightmapBakeTypes | (int)LightmapBakeType.Realtime) == 0 #if SURFACE_CACHE - && !strippingData.IsShaderFeatureEnabled(ShaderFeatures.SurfaceCache) + || !strippingData.IsShaderFeatureEnabled(ShaderFeatures.SurfaceCache) #endif - ) + ) return true; } return false; @@ -1234,7 +1229,6 @@ public bool CanRemoveVariant([DisallowNull] Shader shader, ShaderSnippetData pas stripUnusedVariants = ShaderBuildPreprocessor.s_StripUnusedVariants, stripUnusedPostProcessingVariants = ShaderBuildPreprocessor.s_StripUnusedPostProcessingVariants, stripUnusedXRVariants = ShaderBuildPreprocessor.s_StripXRVariants, - usesDynamicLightmaps = ShaderBuildPreprocessor.s_UsesDynamicLightmaps, IsHDRDisplaySupportEnabled = PlayerSettings.allowHDRDisplaySupport, shader = shader, passData = passData, diff --git a/Packages/com.unity.render-pipelines.universal/Editor/UniversalRenderPipelineAsset/SerializedUniversalRenderPipelineAsset.cs b/Packages/com.unity.render-pipelines.universal/Editor/UniversalRenderPipelineAsset/SerializedUniversalRenderPipelineAsset.cs index 79d38947f35..1a397448b09 100644 --- a/Packages/com.unity.render-pipelines.universal/Editor/UniversalRenderPipelineAsset/SerializedUniversalRenderPipelineAsset.cs +++ b/Packages/com.unity.render-pipelines.universal/Editor/UniversalRenderPipelineAsset/SerializedUniversalRenderPipelineAsset.cs @@ -5,9 +5,7 @@ namespace UnityEditor.Rendering.Universal { internal class SerializedUniversalRenderPipelineAsset { - public SerializedProperty rendererDataProp { get; } - public SerializedProperty defaultRendererProp { get; } - + public SerializedProperty rendererDatas { get; } public SerializedProperty requireDepthTextureProp { get; } public SerializedProperty requireOpaqueTextureProp { get; } public SerializedProperty opaqueDownsamplingProp { get; } @@ -77,7 +75,6 @@ internal class SerializedUniversalRenderPipelineAsset public SerializedProperty mixedLightingSupportedProp { get; } public SerializedProperty useRenderingLayers { get; } public SerializedProperty supportsLightCookies { get; } - public SerializedProperty debugLevelProp { get; } public SerializedProperty volumeFrameworkUpdateModeProp { get; } public SerializedProperty volumeProfileProp { get; } @@ -106,6 +103,8 @@ public SerializedUniversalRenderPipelineAsset(SerializedObject serializedObject) asset = serializedObject.targetObject as UniversalRenderPipelineAsset; this.serializedObject = serializedObject; + rendererDatas = serializedObject.FindProperty("m_RendererDataList"); + requireDepthTextureProp = serializedObject.FindProperty("m_RequireDepthTexture"); requireOpaqueTextureProp = serializedObject.FindProperty("m_RequireOpaqueTexture"); opaqueDownsamplingProp = serializedObject.FindProperty("m_OpaqueDownsampling"); @@ -174,7 +173,6 @@ public SerializedUniversalRenderPipelineAsset(SerializedObject serializedObject) mixedLightingSupportedProp = serializedObject.FindProperty("m_MixedLightingSupported"); useRenderingLayers = serializedObject.FindProperty("m_SupportsLightLayers"); supportsLightCookies = serializedObject.FindProperty("m_SupportsLightCookies"); - debugLevelProp = serializedObject.FindProperty("m_DebugLevel"); volumeFrameworkUpdateModeProp = serializedObject.FindProperty("m_VolumeFrameworkUpdateMode"); volumeProfileProp = serializedObject.FindProperty("m_VolumeProfile"); diff --git a/Packages/com.unity.render-pipelines.universal/Editor/UniversalRenderPipelineAsset/UniversalRenderPipelineAssetUI.Drawers.cs b/Packages/com.unity.render-pipelines.universal/Editor/UniversalRenderPipelineAsset/UniversalRenderPipelineAssetUI.Drawers.cs index d730edeaf3c..e6a3645e5ee 100644 --- a/Packages/com.unity.render-pipelines.universal/Editor/UniversalRenderPipelineAsset/UniversalRenderPipelineAssetUI.Drawers.cs +++ b/Packages/com.unity.render-pipelines.universal/Editor/UniversalRenderPipelineAsset/UniversalRenderPipelineAssetUI.Drawers.cs @@ -1,5 +1,7 @@ using System; +using System.Collections.Generic; using System.Reflection; +using System.Text; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.Rendering.Universal; @@ -8,6 +10,20 @@ namespace UnityEditor.Rendering.Universal { using CED = CoreEditorDrawer; + //The internal one is private + static class StringBuilderPool + { + internal static readonly UnityEngine.Pool.ObjectPool s_Pool = new ( + () => new StringBuilder(), + null, + sb => sb.Clear() //clear on release + ); + + public static StringBuilder Get() => s_Pool.Get(); + public static UnityEngine.Pool.PooledObject Get(out StringBuilder value) => s_Pool.Get(out value); + public static void Release(StringBuilder toRelease) => s_Pool.Release(toRelease); + } + internal partial class UniversalRenderPipelineAssetUI { internal enum Expandable @@ -101,6 +117,7 @@ internal static void Expand(Expandable expandable, bool state) } public static readonly CED.IDrawer Inspector = CED.Group( + CED.Group(PrepareOnTileValidationWarning), CED.AdditionalPropertiesFoldoutGroup(Styles.renderingSettingsText, Expandable.Rendering, k_ExpandedState, ExpandableAdditional.Rendering, k_AdditionalPropertiesState, DrawRendering, DrawRenderingAdditional), CED.FoldoutGroup(Styles.qualitySettingsText, Expandable.Quality, k_ExpandedState, DrawQuality), CED.AdditionalPropertiesFoldoutGroup(Styles.lightingSettingsText, Expandable.Lighting, k_ExpandedState, ExpandableAdditional.Lighting, k_AdditionalPropertiesState, DrawLighting, DrawLightingAdditional), @@ -114,45 +131,50 @@ internal static void Expand(Expandable expandable, bool state) static void DrawRendering(SerializedUniversalRenderPipelineAsset serialized, Editor ownerEditor) { - if (ownerEditor is UniversalRenderPipelineAssetEditor urpAssetEditor) - { - EditorGUILayout.Space(); - urpAssetEditor.rendererList.DoLayoutList(); - - if (!serialized.asset.ValidateRendererData(-1)) - EditorGUILayout.HelpBox(Styles.rendererMissingDefaultMessage.text, MessageType.Error, true); - else if (!serialized.asset.ValidateRendererDataList(true)) - EditorGUILayout.HelpBox(Styles.rendererMissingMessage.text, MessageType.Warning, true); - else if (!ValidateRendererGraphicsAPIs(serialized.asset, out var unsupportedGraphicsApisMessage)) - EditorGUILayout.HelpBox(Styles.rendererUnsupportedAPIMessage.text + unsupportedGraphicsApisMessage, MessageType.Warning, true); - - EditorGUILayout.PropertyField(serialized.requireDepthTextureProp, Styles.requireDepthTextureText); - EditorGUILayout.PropertyField(serialized.requireOpaqueTextureProp, Styles.requireOpaqueTextureText); - EditorGUI.BeginDisabledGroup(!serialized.requireOpaqueTextureProp.boolValue); - EditorGUILayout.PropertyField(serialized.opaqueDownsamplingProp, Styles.opaqueDownsamplingText); - EditorGUI.EndDisabledGroup(); - EditorGUILayout.PropertyField(serialized.supportsTerrainHolesProp, Styles.supportsTerrainHolesText); + if (ownerEditor is not UniversalRenderPipelineAssetEditor urpAssetEditor) + return; - EditorGUILayout.PropertyField(serialized.gpuResidentDrawerMode, Styles.gpuResidentDrawerMode); + EditorGUILayout.Space(); + urpAssetEditor.rendererList.DoLayoutList(); - var brgStrippingError = EditorGraphicsSettings.batchRendererGroupShaderStrippingMode != BatchRendererGroupStrippingMode.KeepAll; - var lightingModeError = !HasCorrectLightingModes(serialized.asset); - var staticBatchingWarning = PlayerSettings.GetStaticBatchingForPlatform(EditorUserBuildSettings.activeBuildTarget); + if (!serialized.asset.ValidateRendererData(-1)) + EditorGUILayout.HelpBox(Styles.rendererMissingDefaultMessage.text, MessageType.Error, true); + else if (!serialized.asset.ValidateRendererDataList(true)) + EditorGUILayout.HelpBox(Styles.rendererMissingMessage.text, MessageType.Warning, true); + else if (!ValidateRendererGraphicsAPIs(serialized.asset, out var unsupportedGraphicsApisMessage)) + EditorGUILayout.HelpBox(Styles.rendererUnsupportedAPIMessage.text + unsupportedGraphicsApisMessage, MessageType.Warning, true); - if ((GPUResidentDrawerMode)serialized.gpuResidentDrawerMode.intValue != GPUResidentDrawerMode.Disabled) - { - ++EditorGUI.indentLevel; - serialized.smallMeshScreenPercentage.floatValue = Mathf.Clamp(EditorGUILayout.FloatField(Styles.smallMeshScreenPercentage, serialized.smallMeshScreenPercentage.floatValue), 0.0f, 20.0f); - EditorGUILayout.PropertyField(serialized.gpuResidentDrawerEnableOcclusionCullingInCameras, Styles.gpuResidentDrawerEnableOcclusionCullingInCameras); - --EditorGUI.indentLevel; + EditorGUILayout.PropertyField(serialized.requireDepthTextureProp, Styles.requireDepthTextureText); + DisplayOnTileValidationWarning(serialized.requireDepthTextureProp, p => p.boolValue, Styles.requireDepthTextureText); - if (brgStrippingError) - EditorGUILayout.HelpBox(Styles.brgShaderStrippingErrorMessage.text, MessageType.Warning, true); - if (lightingModeError) - EditorGUILayout.HelpBox(Styles.lightModeErrorMessage.text, MessageType.Warning, true); - if (staticBatchingWarning) - EditorGUILayout.HelpBox(Styles.staticBatchingInfoMessage.text, MessageType.Info, true); - } + EditorGUILayout.PropertyField(serialized.requireOpaqueTextureProp, Styles.requireOpaqueTextureText); + DisplayOnTileValidationWarning(serialized.requireOpaqueTextureProp, p => p.boolValue, Styles.requireOpaqueTextureText); + + EditorGUI.BeginDisabledGroup(!serialized.requireOpaqueTextureProp.boolValue); + EditorGUILayout.PropertyField(serialized.opaqueDownsamplingProp, Styles.opaqueDownsamplingText); + EditorGUI.EndDisabledGroup(); + EditorGUILayout.PropertyField(serialized.supportsTerrainHolesProp, Styles.supportsTerrainHolesText); + + EditorGUILayout.PropertyField(serialized.gpuResidentDrawerMode, Styles.gpuResidentDrawerMode); + + var brgStrippingError = EditorGraphicsSettings.batchRendererGroupShaderStrippingMode != BatchRendererGroupStrippingMode.KeepAll; + var lightingModeError = !HasCorrectLightingModes(serialized.asset); + var staticBatchingWarning = PlayerSettings.GetStaticBatchingForPlatform(EditorUserBuildSettings.activeBuildTarget); + + if ((GPUResidentDrawerMode)serialized.gpuResidentDrawerMode.intValue != GPUResidentDrawerMode.Disabled) + { + ++EditorGUI.indentLevel; + serialized.smallMeshScreenPercentage.floatValue = Mathf.Clamp(EditorGUILayout.FloatField(Styles.smallMeshScreenPercentage, serialized.smallMeshScreenPercentage.floatValue), 0.0f, 20.0f); + EditorGUILayout.PropertyField(serialized.gpuResidentDrawerEnableOcclusionCullingInCameras, Styles.gpuResidentDrawerEnableOcclusionCullingInCameras); + DisplayOnTileValidationWarning(serialized.gpuResidentDrawerEnableOcclusionCullingInCameras, p => p.boolValue, Styles.gpuResidentDrawerEnableOcclusionCullingInCameras); + --EditorGUI.indentLevel; + + if (brgStrippingError) + EditorGUILayout.HelpBox(Styles.brgShaderStrippingErrorMessage.text, MessageType.Warning, true); + if (lightingModeError) + EditorGUILayout.HelpBox(Styles.lightModeErrorMessage.text, MessageType.Warning, true); + if (staticBatchingWarning) + EditorGUILayout.HelpBox(Styles.staticBatchingInfoMessage.text, MessageType.Info, true); } } @@ -176,16 +198,52 @@ static void DrawRenderingAdditional(SerializedUniversalRenderPipelineAsset seria { EditorGUILayout.PropertyField(serialized.srpBatcher, Styles.srpBatcher); EditorGUILayout.PropertyField(serialized.supportsDynamicBatching, Styles.dynamicBatching); - EditorGUILayout.PropertyField(serialized.debugLevelProp, Styles.debugLevel); EditorGUILayout.PropertyField(serialized.storeActionsOptimizationProperty, Styles.storeActionsOptimizationText); } + static bool IsAndroidXRTargetted() //Include Quest platform + { +#if XR_MANAGEMENT_4_0_1_OR_NEWER + var buildTargetGroup = BuildPipeline.GetBuildTargetGroup(EditorUserBuildSettings.activeBuildTarget); + if (buildTargetGroup != BuildTargetGroup.Android) + return false; + + var buildTargetSettings = XR.Management.XRGeneralSettingsPerBuildTarget.XRGeneralSettingsForBuildTarget(buildTargetGroup); + return buildTargetSettings != null + && buildTargetSettings.AssignedSettings != null + && buildTargetSettings.AssignedSettings.activeLoaders.Count > 0; +#else + return false; +#endif + } + static void DrawQuality(SerializedUniversalRenderPipelineAsset serialized, Editor ownerEditor) { DrawHDR(serialized, ownerEditor); - EditorGUILayout.PropertyField(serialized.msaa, Styles.msaaText); + EditorGUILayout.PropertyField(serialized.msaa, Styles.msaaText); + DisplayOnTileValidationWarning( + serialized.msaa, + p => p.intValue != (int)MsaaQuality.Disabled + // This operation is actually ok on Quest + && !IsAndroidXRTargetted(), + Styles.msaaText); + serialized.renderScale.floatValue = EditorGUILayout.Slider(Styles.renderScaleText, serialized.renderScale.floatValue, UniversalRenderPipeline.minRenderScale, UniversalRenderPipeline.maxRenderScale); + DisplayOnTileValidationWarning( + serialized.renderScale, + p => + { + // Duplicating logic from UniversalRenderPipeline.InitializeStackedCameraData + const float kRenderScaleThreshold = 0.05f; + bool canRequireIntermediateTexture = Mathf.Abs(1.0f - p.floatValue) >= kRenderScaleThreshold; + if (!canRequireIntermediateTexture) + return false; + + // This operation is actually ok on Quest + return !IsAndroidXRTargetted(); + }, + Styles.renderScaleText); DrawUpscalingFilterDropdownAndOptions(serialized, ownerEditor); @@ -299,6 +357,8 @@ static void DrawUpscalingFilterDropdownAndOptions(SerializedUniversalRenderPipel #endif } + DisplayOnTileValidationWarning(serialized.upscalingFilter, p => p.intValue != (int)UpscalingFilterSelection.Auto, Styles.upscalingFilterText); + // draw upscaler options, if any switch (serialized.asset.upscalingFilter) { @@ -351,6 +411,7 @@ static void DrawUpscalingFilterDropdownAndOptions(SerializedUniversalRenderPipel static void DrawHDR(SerializedUniversalRenderPipelineAsset serialized, Editor ownerEditor) { EditorGUILayout.PropertyField(serialized.hdr, Styles.hdrText); + DisplayOnTileValidationWarning(serialized.hdr, p => p.boolValue, Styles.hdrText); // Nested and in-between additional property bool additionalProperties = k_ExpandedState[Expandable.Quality] && k_AdditionalPropertiesState[ExpandableAdditional.Quality]; @@ -749,6 +810,8 @@ static void DrawCascades(SerializedUniversalRenderPipelineAsset serialized, int static void DrawPostProcessing(SerializedUniversalRenderPipelineAsset serialized, Editor ownerEditor) { + DisplayOnTileValidationWarningForPostProcessingSection(Styles.postProcessingSettingsText); + EditorGUILayout.PropertyField(serialized.colorGradingMode, Styles.colorGradingMode); bool isHdrOn = serialized.hdr.boolValue; if (!isHdrOn && serialized.colorGradingMode.intValue == (int)ColorGradingMode.HighDynamicRange) @@ -830,5 +893,220 @@ static void DrawAdaptivePerformance(SerializedUniversalRenderPipelineAsset seria EditorGUILayout.PropertyField(serialized.useAdaptivePerformance, Styles.useAdaptivePerformance); } #endif + + struct OnTileValidationInfos + { + public bool enabled => !string.IsNullOrEmpty(formatter); + public readonly string formatter; + public readonly string rendererNames; + public readonly string rendererNamesWithPostProcess; + + public OnTileValidationInfos(string formatter, string rendererNames, string rendererNamesWithPostProcess) + { + this.formatter = formatter; + this.rendererNames = rendererNames; + this.rendererNamesWithPostProcess = rendererNamesWithPostProcess; + } + } + + static OnTileValidationInfos lastOnTileValidationInfos; //prevent computing this multiple time for this ImGUI frame + + static void PrepareOnTileValidationWarning(SerializedUniversalRenderPipelineAsset serialized, Editor ownerEditor) + { + // Rules: + // - mono selection: + // - only 1 Renderer in the list: Display warning if RendererData's OnTileValidation is enabled + // - many Renderers in the list: Display warning with list of RendererData's where OnTileValidation is enabled + // - multi selection: + // - compute the list interection of RendererDatas where OnTileValidation is enabled amongst all URPAsset in selection + // - If list is not empty, display warning with this list. Additionaly specify for item iden due to being at different position + // - Additionally for both, for Post Processing section: only show names where Post Processing is enabled + + lastOnTileValidationInfos = default; + + // If impacted section are not opened, early exit + if (!(k_ExpandedState[Expandable.Rendering] || k_ExpandedState[Expandable.Quality] || k_ExpandedState[Expandable.PostProcessing])) + return; + + // Helper to iterate property of an array quickly (without GetArrayElementAtIndex) + IEnumerable ArrayElementPropertyEnumerator(SerializedProperty property) + { + if (!property.hasVisibleChildren) + yield break; + + var iterator = property.Copy(); + var end = iterator.GetEndProperty(); + + // Move to the first child property + iterator.NextVisible(enterChildren: true); + iterator.NextVisible(enterChildren: false); //skip size + + while (!SerializedProperty.EqualContents(iterator, end)) + { + yield return iterator; + iterator.NextVisible(enterChildren: false); + } + } + + // Helper to filter the list and get only unique result of UniversalRendererData that have the OnTileValidation. + // The returned IDisposable is for being able to return the HashSet to the pool when Dispose is call like at end of Using. + IDisposable SelectUniqueAndCast(IEnumerable properties, out HashSet uniques) + { + var e = properties.GetEnumerator(); + var disposer = HashSetPool.Get(out uniques); + while (e.MoveNext()) + if (!e.Current.hasMultipleDifferentValues + && e.Current.boxedValue is UniversalRendererData universalData + && universalData.onTileValidation) + uniques.Add(universalData); + return disposer; + } + + // Additional select to filter the one that have PostProcessing enabled. + IEnumerable WherePostProcessingEnabled(IEnumerable renderer) + { + var e = renderer.GetEnumerator(); + while (e.MoveNext()) + if (e.Current.postProcessData != null) + yield return e.Current; + } + + // Helper to draw the name in the collection as a string, between '' and with a coma separator + string ListElementNames(IEnumerable collection, string suffix = "") + { + var e = collection.GetEnumerator(); + if (!e.MoveNext()) + return string.Empty; + + string GetName(IEnumerator e) + => $"'{e.Current.name}'{suffix}"; + + string last = GetName(e); + if (!e.MoveNext()) + return last; + + using var o = StringBuilderPool.Get(out var sb); + do + { + sb.Append(last); + last = $", {GetName(e)}"; + } + while (e.MoveNext()); + sb.Append(last); + + return sb.ToString(); + } + + // Helper for multiple selection to distinguish element that remain at stable position (in the selection) from others + string ConcatCollectionInName(IEnumerable rightlyPositioned, IEnumerable wronglyPositioned) + { + var firstPart = ListElementNames(rightlyPositioned); + var secondPart = ListElementNames(wronglyPositioned, Styles.suffixWhenDifferentPositionOnTileValidation); + if (string.IsNullOrEmpty(firstPart)) + return secondPart; + if (string.IsNullOrEmpty(secondPart)) + return firstPart; + return $"{firstPart}, {secondPart}"; + } + + string names = null; + string namesWithPostProcess = null; + if (!serialized.rendererDatas.hasMultipleDifferentValues) + { + // Simple case: all element selected share the same list. + // Minimize the operation alongs foldout opened + using (SelectUniqueAndCast(ArrayElementPropertyEnumerator(serialized.rendererDatas), out var renderers)) + { + if (renderers.Count == 0) + return; + + if (k_ExpandedState[Expandable.Rendering] || k_ExpandedState[Expandable.Quality]) + names = ListElementNames(renderers); + + if (k_ExpandedState[Expandable.PostProcessing]) + namesWithPostProcess = ListElementNames(WherePostProcessingEnabled(renderers)); + + lastOnTileValidationInfos = new OnTileValidationInfos( + serialized.rendererDatas.arraySize == 1 ? Styles.formatterOnTileValidationOneRenderer : Styles.formatterOnTileValidationMultipleRenderer, + names, + namesWithPostProcess); + return; + } + } + + // Complex case: the renderer list is different in some elements of the selection + + // Let's compute the intersection of each RendererList where it is a UniversalRenderer with On-Tile Validation enabled. + // If the intersection is empty, it would means no RendererData validate the criteria so we early exit. + + // We can retrieve element at stable position by directly checking the serialization of the selection. + // Elements in the intersection that are not in the stable positio list are elements shared in all list but with moving index. + + + // Helper to build the HashSet of UniversalRenderer that have OnTileValidation on one targeted asset. + // The returned IDisposable is for being able to return the HashSet to the pool when Dispose is call like at end of Using. + IDisposable GetUniversalRendererWithOnTileValidationEnabled(UniversalRenderPipelineAsset asset, out HashSet set) + { + IDisposable disposer = HashSetPool.Get(out set); + for (int rendererIndex = 0; rendererIndex < asset.rendererDataList.Length; ++rendererIndex) + if (asset.rendererDataList[rendererIndex] is UniversalRendererData universalData && universalData.onTileValidation) + set.Add(universalData); + return disposer; + } + + using (GetUniversalRendererWithOnTileValidationEnabled((UniversalRenderPipelineAsset)serialized.serializedObject.targetObjects[0], out var movingPositions)) + { + if (movingPositions.Count == 0) + return; + + for (int i = 1; i < serialized.serializedObject.targetObjects.Length; ++i) + using (GetUniversalRendererWithOnTileValidationEnabled((UniversalRenderPipelineAsset)serialized.serializedObject.targetObjects[i], out var otherIntersection)) + { + if (otherIntersection.Count == 0) + return; + movingPositions.IntersectWith(otherIntersection); + if (movingPositions.Count == 0) + return; + } + + using (SelectUniqueAndCast(ArrayElementPropertyEnumerator(serialized.rendererDatas), out var stablePositions)) + { + foreach (var stablePositionElement in stablePositions) + movingPositions.Remove(stablePositionElement); + + if (k_ExpandedState[Expandable.Rendering] || k_ExpandedState[Expandable.Quality]) + names = ConcatCollectionInName(stablePositions, movingPositions); + + if (k_ExpandedState[Expandable.PostProcessing]) + namesWithPostProcess = ConcatCollectionInName(WherePostProcessingEnabled(stablePositions), WherePostProcessingEnabled(movingPositions)); + } + + lastOnTileValidationInfos = new OnTileValidationInfos(Styles.formatterOnTileValidationMultipleRenderer, names, namesWithPostProcess); + } + } + + static void DisplayOnTileValidationWarning(SerializedProperty prop, Func shouldDisplayWarning, GUIContent label = null) + { + if (prop == null + || shouldDisplayWarning == null + || !lastOnTileValidationInfos.enabled + || !shouldDisplayWarning(prop)) + return; + + EditorGUILayout.HelpBox( + string.Format(lastOnTileValidationInfos.formatter, label == null ? prop.displayName : label.text, lastOnTileValidationInfos.rendererNames), + MessageType.Warning); + } + + //variant for a whole section such as post processing + static void DisplayOnTileValidationWarningForPostProcessingSection(GUIContent label) + { + if (label == null || !lastOnTileValidationInfos.enabled || string.IsNullOrEmpty(lastOnTileValidationInfos.rendererNamesWithPostProcess)) + return; + + EditorGUILayout.HelpBox( + string.Format(lastOnTileValidationInfos.formatter, label.text, lastOnTileValidationInfos.rendererNamesWithPostProcess), + MessageType.Warning); + } } } diff --git a/Packages/com.unity.render-pipelines.universal/Editor/UniversalRenderPipelineAsset/UniversalRenderPipelineAssetUI.Skin.cs b/Packages/com.unity.render-pipelines.universal/Editor/UniversalRenderPipelineAsset/UniversalRenderPipelineAssetUI.Skin.cs index e0f6934023f..b5557ee9c28 100644 --- a/Packages/com.unity.render-pipelines.universal/Editor/UniversalRenderPipelineAsset/UniversalRenderPipelineAssetUI.Skin.cs +++ b/Packages/com.unity.render-pipelines.universal/Editor/UniversalRenderPipelineAsset/UniversalRenderPipelineAssetUI.Skin.cs @@ -14,7 +14,6 @@ internal static class Styles public static GUIContent shadowSettingsText = EditorGUIUtility.TrTextContent("Shadows", "Settings that configure how shadows look and behave, and can be used to balance between the visual quality and performance of shadows."); public static GUIContent postProcessingSettingsText = EditorGUIUtility.TrTextContent("Post-processing", "Settings that allow for fine tuning of post-processing effects in the Scene when this Render Pipeline Asset is in use."); public static GUIContent volumeSettingsText = EditorGUIUtility.TrTextContent("Volumes", "Settings related to usage of Volume Components."); - public static GUIContent advancedSettingsText = EditorGUIUtility.TrTextContent("Advanced"); public static GUIContent adaptivePerformanceText = EditorGUIUtility.TrTextContent("Adaptive Performance"); // Rendering @@ -22,8 +21,6 @@ internal static class Styles public static GUIContent rendererDefaultText = EditorGUIUtility.TrTextContent("Default", "This renderer is currently the default for the render pipeline."); public static GUIContent rendererSetDefaultText = EditorGUIUtility.TrTextContent("Set Default", "Makes this renderer the default for the render pipeline."); public static GUIContent rendererSettingsText = EditorGUIUtility.TrIconContent("_Menu", "Opens settings for this renderer."); - public static GUIContent rendererMissingText = EditorGUIUtility.TrIconContent("console.warnicon.sml", "Renderer missing. Click this to select a new renderer."); - public static GUIContent rendererDefaultMissingText = EditorGUIUtility.TrIconContent("console.erroricon.sml", "Default renderer missing. Click this to select a new renderer."); public static GUIContent requireDepthTextureText = EditorGUIUtility.TrTextContent("Depth Texture", "If enabled the pipeline will generate camera's depth that can be bound in shaders as _CameraDepthTexture."); public static GUIContent requireOpaqueTextureText = EditorGUIUtility.TrTextContent("Opaque Texture", "If enabled the pipeline will copy the screen to texture after opaque objects are drawn. For transparent objects this can be bound in shaders as _CameraOpaqueTexture."); public static GUIContent opaqueDownsamplingText = EditorGUIUtility.TrTextContent("Opaque Downsampling", "The downsampling method that is used for the opaque texture"); @@ -31,7 +28,6 @@ internal static class Styles public static GUIContent srpBatcher = EditorGUIUtility.TrTextContent("SRP Batcher", "If enabled, the render pipeline uses the SRP batcher."); public static GUIContent storeActionsOptimizationText = EditorGUIUtility.TrTextContent("Store Actions", "Sets the store actions policy on tile based GPUs. Affects render targets memory usage and will impact performance."); public static GUIContent dynamicBatching = EditorGUIUtility.TrTextContent("Dynamic Batching", "If enabled, the render pipeline will batch drawcalls with few triangles together by copying their vertex buffers into a shared buffer on a per-frame basis."); - public static GUIContent debugLevel = EditorGUIUtility.TrTextContent("Debug Level", "Controls the level of debug information generated by the render pipeline. When Profiling is selected, the pipeline provides detailed profiling tags."); // Quality public static GUIContent hdrText = EditorGUIUtility.TrTextContent("HDR", "Controls the global HDR settings."); @@ -44,7 +40,6 @@ internal static class Styles public static GUIContent enableLODCrossFadeText = EditorGUIUtility.TrTextContent("LOD Cross Fade", "Controls whether LOD Cross Fade enabled or disabled."); public static GUIContent lodCrossFadeDitheringTypeText = EditorGUIUtility.TrTextContent("LOD Cross Fade Dithering Type", "Controls the LOD Cross Fade Dithering Type that will be used to draw Renderer LOD when LODGroup has CrossFade Fade Mode selected."); public static GUIContent shEvalModeText = EditorGUIUtility.TrTextContent("SH Evaluation Mode", "Defines the Spherical Harmonic (SH) lighting evaluation type (per vertex, per pixel, or mixed)."); - public static readonly string stpRequiresRenderGraph = "STP is selected but Render Graph is not enabled. STP requires Render Graph in order to function. Unity will fall back to the Automatic option."; public static readonly string stpMobilePlatformWarning = "STP is selected for use on a mobile platform. STP is only supported on modern compute-capable hardware and its performance overhead may make it impractical on lower-end devices."; // Main light @@ -70,9 +65,9 @@ internal static class Styles public static GUIContent additionalLightsShadowResolutionTiers = EditorGUIUtility.TrTextContent("Shadow Resolution Tiers", $"Additional Lights Shadow Resolution Tiers. Rounded to the next power of two, and clamped to be at least {UniversalAdditionalLightData.AdditionalLightsShadowMinimumResolution}."); public static GUIContent[] additionalLightsShadowResolutionTierNames = { - new GUIContent("Low"), - new GUIContent("Medium"), - new GUIContent("High") + new("Low"), + new("Medium"), + new("High") }; public static GUIContent additionalLightsCookieResolution = EditorGUIUtility.TrTextContent("Cookie Atlas Resolution", "All additional lights are packed into a single cookie atlas. This setting controls the atlas size."); public static GUIContent additionalLightsCookieFormat = EditorGUIUtility.TrTextContent("Cookie Atlas Format", "All additional lights are packed into a single cookie atlas. This setting controls the atlas format."); @@ -156,11 +151,14 @@ internal static class Styles EditorGUIUtility.TrTextContent("Render Graph must be enabled to use occlusion culling."); public static GUIContent stencilLodCrossFadeWarningMessage = EditorGUIUtility.TrTextContent("LOD Cross Fade with stencil dithering is not compatible with stencil override in Renderer."); + + public static readonly string formatterOnTileValidationOneRenderer = L10n.Tr("'{0}' will be skipped because it is incompatible with the enabled 'On-Tile Validation' on the Renderer: {1}."); + public static readonly string formatterOnTileValidationMultipleRenderer = L10n.Tr("'{0}' will be skipped whenever an active renderer uses 'On-Tile Validation' setting, such as: {1}."); + public static readonly string suffixWhenDifferentPositionOnTileValidation = L10n.Tr(" (different position)"); // Dropdown menu options public static string[] mainLightOptions = { "Disabled", "Per Pixel" }; public static string[] volumeFrameworkUpdateOptions = { "Every Frame", "Via Scripting" }; - public static string[] opaqueDownsamplingOptions = { "None", "2x (Bilinear)", "4x (Box)", "4x (Bilinear)" }; } } } diff --git a/Packages/com.unity.render-pipelines.universal/Editor/UniversalRendererDataEditor.cs b/Packages/com.unity.render-pipelines.universal/Editor/UniversalRendererDataEditor.cs index 9c071b83fe4..9ddadfb8cc7 100644 --- a/Packages/com.unity.render-pipelines.universal/Editor/UniversalRendererDataEditor.cs +++ b/Packages/com.unity.render-pipelines.universal/Editor/UniversalRendererDataEditor.cs @@ -45,6 +45,11 @@ private static class Styles public static readonly GUIContent intermediateTextureMode = EditorGUIUtility.TrTextContent("Intermediate Texture (Obsolete)", "Should be set to Auto. Controls when URP renders via an intermediate texture."); public static readonly GUIContent warningIntermediateTextureMode = EditorGUIUtility.TrTextContent("'Always' is Obsolete. Change it to Auto. This can improve performance. The setting will disappear once it is corrected to 'Auto'."); public static readonly GUIContent deferredPlusIncompatibleWarning = EditorGUIUtility.TrTextContent("Deferred+ is only available with Render Graph. In compatibility mode, Deferred+ falls back to Forward+."); + public static readonly GUIContent onTileValidation = EditorGUIUtility.TrTextContent("On-Tile Validation", "Enables the feature validation to prevent going off tile. This is mainly useful for tile based architectures."); + public static readonly string onTileValidationWarning = L10n.Tr("On-Tile validation is enabled. All ScriptableRendererFeatures and other features requiring a full-screen pass will be skipped to preserve the On-Tile optimization."); + public static readonly string deferredOnTileValidationWarning = L10n.Tr("Deferred rendering path is incompatible with the enabled 'On-Tile Validation' and will fallback to Forward."); + public static readonly string deferredPlusOnTileValidationWarning = L10n.Tr("Deferred+ rendering path is incompatible with the enabled 'On-Tile Validation' and will fallback to Forward+."); + public static readonly string postProcessingOnTileValidationWarning = L10n.Tr("'Post-processing' will be skipped because it is incompatible with the enabled 'On-Tile Validation'."); } SerializedProperty m_PrepassLayerMask; @@ -61,6 +66,7 @@ private static class Styles SerializedProperty m_Shaders; SerializedProperty m_ShadowTransparentReceiveProp; SerializedProperty m_IntermediateTextureMode; + SerializedProperty m_OnTileValidation; List m_DepthFormatStrings = new List(); @@ -80,6 +86,7 @@ private void OnEnable() m_Shaders = serializedObject.FindProperty("shaders"); m_ShadowTransparentReceiveProp = serializedObject.FindProperty("m_ShadowTransparentReceive"); m_IntermediateTextureMode = serializedObject.FindProperty("m_IntermediateTextureMode"); + m_OnTileValidation = serializedObject.FindProperty("m_OnTileValidation"); } private void PopulateCompatibleDepthFormats(int renderingMode) @@ -181,6 +188,19 @@ public override void OnInspectorGUI() PopulateCompatibleDepthFormats(m_RenderingMode.intValue); depthFormatIndex = GetDepthFormatIndex((DepthFormat)m_DepthAttachmentFormat.intValue, m_RenderingMode.intValue); } + + if (m_OnTileValidation.boolValue) + { + switch ((RenderingMode)m_RenderingMode.intValue) + { + case RenderingMode.Deferred: + EditorGUILayout.HelpBox(Styles.deferredOnTileValidationWarning, MessageType.Warning); + break; + case RenderingMode.DeferredPlus: + EditorGUILayout.HelpBox(Styles.deferredPlusOnTileValidationWarning, MessageType.Warning); + break; + } + } if (m_RenderingMode.intValue == (int)RenderingMode.Deferred || m_RenderingMode.intValue == (int)RenderingMode.DeferredPlus) { @@ -215,6 +235,10 @@ public override void OnInspectorGUI() m_DepthAttachmentFormat.intValue = (int)GetDepthFormatAt(depthFormatIndex, m_RenderingMode.intValue); EditorGUILayout.PropertyField(m_DepthTextureFormat, Styles.DepthTextureFormat); + + EditorGUILayout.PropertyField(m_OnTileValidation, Styles.onTileValidation); + if (m_OnTileValidation.boolValue) + EditorGUILayout.HelpBox(Styles.onTileValidationWarning, MessageType.Warning); EditorGUI.indentLevel--; EditorGUILayout.Space(); @@ -232,6 +256,8 @@ public override void OnInspectorGUI() { m_PostProcessData.objectReferenceValue = postProcessIncluded ? PostProcessData.GetDefaultPostProcessData() : null; } + if (postProcessIncluded && m_OnTileValidation.boolValue) + EditorGUILayout.HelpBox(Styles.postProcessingOnTileValidationWarning, MessageType.Warning); EditorGUI.indentLevel++; EditorGUILayout.PropertyField(m_PostProcessData, Styles.PostProcessLabel); EditorGUI.indentLevel--; diff --git a/Packages/com.unity.render-pipelines.universal/Runtime/2D/RendererFeatures/ScriptableRendererFeature2D.cs b/Packages/com.unity.render-pipelines.universal/Runtime/2D/RendererFeatures/ScriptableRendererFeature2D.cs index 73360c18e4d..b238901b2a2 100644 --- a/Packages/com.unity.render-pipelines.universal/Runtime/2D/RendererFeatures/ScriptableRendererFeature2D.cs +++ b/Packages/com.unity.render-pipelines.universal/Runtime/2D/RendererFeatures/ScriptableRendererFeature2D.cs @@ -6,6 +6,7 @@ namespace UnityEngine.Rendering.Universal /// /// [ExcludeFromPreset] + [SupportedOnRenderer(typeof(Renderer2DData))] public abstract partial class ScriptableRendererFeature2D : ScriptableRendererFeature { /// diff --git a/Packages/com.unity.render-pipelines.universal/Runtime/Data/UniversalRenderPipelineAsset.cs b/Packages/com.unity.render-pipelines.universal/Runtime/Data/UniversalRenderPipelineAsset.cs index 0e20a923567..a4fd32e3d43 100644 --- a/Packages/com.unity.render-pipelines.universal/Runtime/Data/UniversalRenderPipelineAsset.cs +++ b/Packages/com.unity.render-pipelines.universal/Runtime/Data/UniversalRenderPipelineAsset.cs @@ -246,22 +246,6 @@ public enum LightRenderingMode PerPixel = 1, } - /// - /// Defines if profiling is logged or not. This enum is not longer in use, use the Profiler instead. - /// - [Obsolete("PipelineDebugLevel is replaced to use the profiler and has no effect. #from(2022.2) #breakingFrom(2023.1)", true)] - public enum PipelineDebugLevel - { - /// - /// Disabled logging for profiling. - /// - Disabled, - /// - /// Enabled logging for profiling. - /// - Profiling, - } - /// /// Options to select the type of Renderer to use. /// @@ -582,7 +566,6 @@ public partial class UniversalRenderPipelineAsset : RenderPipelineAsset= m_RendererDataList.Length) + { + if (m_DefaultRendererIndex < 0 || m_DefaultRendererIndex >= m_RendererDataList.Length) + return false; + + index = m_DefaultRendererIndex; //out of range index fallback on default + } + + result = m_RendererDataList[index]; + return result != null; + } + internal GUIContent[] rendererDisplayList { get @@ -1589,12 +1590,6 @@ public VolumeProfile volumeProfile set => m_VolumeProfile = value; } - /// - /// Previously returned the debug level for this Render Pipeline Asset but is now deprecated. Replaced to use the profiler and is no longer used. - /// - [Obsolete("PipelineDebugLevel is deprecated and replaced to use the profiler. Calling debugLevel is not necessary. #from(2022.2) #breakingFrom(2023.1)", true)] - public PipelineDebugLevel debugLevel => PipelineDebugLevel.Disabled; - /// /// Specifies if SRPBacher is used by this UniversalRenderPipelineAsset. /// @@ -1727,7 +1722,8 @@ public bool gpuResidentDrawerEnableOcclusionCullingInCameras static class Strings { - public static readonly string notURPRenderer = $"{nameof(GPUResidentDrawer)} Disabled due to some configured Universal Renderers not being {nameof(UniversalRendererData)}."; + public static readonly string nullRenderer = $"{nameof(GPUResidentDrawer)} Disabled. One or more Scriptable Renderer in the Render Pipeline Asset is null."; + public static readonly string notURPRenderer = $"{nameof(GPUResidentDrawer)} Disabled. One or more Scriptable Renderer in the Render Pipeline Asset is not of the type {nameof(UniversalRendererData)}."; public static readonly string renderingModeIncompatible = $"{nameof(GPUResidentDrawer)} Disabled due to some configured Universal Renderers not using the Forward+ or Deferred+ rendering paths."; } @@ -1741,6 +1737,12 @@ public bool IsGPUResidentDrawerSupportedBySRP(out string message, out LogType se // since BiRP-style per-object lights and reflection probes are incompatible with DOTS instancing. foreach (var rendererData in m_RendererDataList) { + if (rendererData == null) + { + message = Strings.nullRenderer; + return false; + } + if (rendererData is not UniversalRendererData universalRendererData) { message = Strings.notURPRenderer; diff --git a/Packages/com.unity.render-pipelines.universal/Runtime/Passes/AdditionalLightsShadowCasterPass.cs b/Packages/com.unity.render-pipelines.universal/Runtime/Passes/AdditionalLightsShadowCasterPass.cs index ae941941cfe..e1f9f84cd45 100644 --- a/Packages/com.unity.render-pipelines.universal/Runtime/Passes/AdditionalLightsShadowCasterPass.cs +++ b/Packages/com.unity.render-pipelines.universal/Runtime/Passes/AdditionalLightsShadowCasterPass.cs @@ -715,7 +715,7 @@ bool SetupForEmptyRendering(bool stripShadowsOffVariants, bool shadowsEnabled, U bool lightHasSoftShadows = shadows != LightShadows.Soft; bool supportsSoftShadows = shadowData.supportsSoftShadows; float softShadows = ShadowUtils.SoftShadowQualityToShaderProperty(light, (supportsSoftShadows && lightHasSoftShadows)); - s_EmptyAdditionalLightIndexToShadowParams[lightIndexToUse] = new Vector4(light.shadowStrength, softShadows, lightType, lightIndexToUse); + s_EmptyAdditionalLightIndexToShadowParams[lightIndexToUse] = new Vector4(light.shadowStrength, softShadows, lightType, c_DefaultShadowParams.w); } else { diff --git a/Packages/com.unity.render-pipelines.universal/Runtime/RendererFeatures/SurfaceCacheGlobalIlluminationRendererFeature/Debug.compute b/Packages/com.unity.render-pipelines.universal/Runtime/RendererFeatures/SurfaceCacheGlobalIlluminationRendererFeature/Debug.compute index 91299877128..4b5ccaa7408 100644 --- a/Packages/com.unity.render-pipelines.universal/Runtime/RendererFeatures/SurfaceCacheGlobalIlluminationRendererFeature/Debug.compute +++ b/Packages/com.unity.render-pipelines.universal/Runtime/RendererFeatures/SurfaceCacheGlobalIlluminationRendererFeature/Debug.compute @@ -13,10 +13,9 @@ StructuredBuffer _RingConfigBuffer; StructuredBuffer _PatchIrradiances; StructuredBuffer _PatchCellIndices; StructuredBuffer _PatchGeometries; -StructuredBuffer _PatchStatistics; +RWStructuredBuffer _PatchStatistics; StructuredBuffer _CellPatchIndices; StructuredBuffer _CascadeOffsets; -RWStructuredBuffer _PatchCounterSets; RWTexture2D _Result; Texture2D _ScreenDepths; @@ -73,6 +72,10 @@ void Visualize(uint2 threadIdx : SV_DispatchThreadID) if (ndcDepth == invalidNdcDepth) return; + const float3 green = float3(0, 1, 0); + const float3 red = float3(1, 0, 0); + const float3 pink = float3(1, 0, 1); + const float2 uv = (float2(threadIdx.xy) + 0.5f) / float2(screenSize); const float3 worldPos = ComputeWorldSpacePosition(uv, ndcDepth, _ClipToWorldTransform); const float3 worldShadedNormal = UnpackGBufferNormal(_ScreenShadedNormals[threadIdx.xy]); @@ -94,7 +97,7 @@ void Visualize(uint2 threadIdx : SV_DispatchThreadID) { patchIrradiance = _PatchIrradiances[patchIdx]; patchPos = _PatchGeometries[patchIdx].position; - PatchUtil::WriteLastFrameAccess(_PatchCounterSets, patchIdx, _FrameIdx); + PatchUtil::WriteLastFrameAccess(_PatchStatistics, patchIdx, _FrameIdx); } if (_ShowSamplePosition == 1 && hasPatch && length(worldPos - patchPos) < 0.12f) @@ -114,9 +117,9 @@ void Visualize(uint2 threadIdx : SV_DispatchThreadID) { const float3 shEval = SphericalHarmonics::Eval(patchIrradiance, worldShadedNormal); if (any(IsActuallyNan(shEval))) - output = float3(1, 0, 0); + output = red; else if (any(IsActuallyInf(shEval))) - output = float3(0, 1, 0); + output = green; else output = max(0, shEval); } @@ -151,16 +154,20 @@ void Visualize(uint2 threadIdx : SV_DispatchThreadID) { if (hasPatch) { - output = sqrt(_PatchStatistics[patchIdx].variance) * 0.1f; + float3 variance = _PatchStatistics[patchIdx].variance; + if (any(isinf(variance)) || any(isnan(variance))) + output = pink; + else if (any(variance < 0)) + output = red; // Since this is an estimate, negative values may happen. + else + output = sqrt(variance) * 0.1f; } } else if (_ViewMode == 6) { if (hasPatch) { - const float3 green = float3(0, 1, 0); - const float3 red = float3(1, 0, 0); - const PatchUtil::PatchCounterSet counterSet = _PatchCounterSets[patchIdx]; + const PatchUtil::PatchCounterSet counterSet = _PatchStatistics[patchIdx].patchCounters; const uint updateCount = PatchUtil::GetUpdateCount(counterSet); const float s = float(updateCount) / PatchUtil::updateMax; const float3 redGreenMix = lerp(red, green, s); diff --git a/Packages/com.unity.render-pipelines.universal/Runtime/RendererFeatures/SurfaceCacheGlobalIlluminationRendererFeature/PatchAllocation.compute b/Packages/com.unity.render-pipelines.universal/Runtime/RendererFeatures/SurfaceCacheGlobalIlluminationRendererFeature/PatchAllocation.compute index e743666b014..481fb2b1f28 100644 --- a/Packages/com.unity.render-pipelines.universal/Runtime/RendererFeatures/SurfaceCacheGlobalIlluminationRendererFeature/PatchAllocation.compute +++ b/Packages/com.unity.render-pipelines.universal/Runtime/RendererFeatures/SurfaceCacheGlobalIlluminationRendererFeature/PatchAllocation.compute @@ -15,7 +15,7 @@ RWStructuredBuffer _RingConfigBuffer; RWStructuredBuffer _PatchGeometries; RWStructuredBuffer _PatchCellIndices; -RWStructuredBuffer _PatchCounterSets; +RWStructuredBuffer _PatchStatistics; RWStructuredBuffer _PatchIrradiances0; RWStructuredBuffer _PatchIrradiances1; RWStructuredBuffer _CellAllocationMarks; @@ -221,7 +221,11 @@ void Allocate(uint2 lowResPixelPos : SV_DispatchThreadID) _PatchIrradiances0[resolutionResult.patchIdx] = irradianceSeed; _PatchIrradiances1[resolutionResult.patchIdx] = irradianceSeed; - _PatchCounterSets[resolutionResult.patchIdx] = counterSet; + PatchUtil::PatchStatisticsSet stats; + stats.mean = irradianceSeed.l0; + stats.variance = 0; + stats.patchCounters = counterSet; + _PatchStatistics[resolutionResult.patchIdx] = stats; } } } diff --git a/Packages/com.unity.render-pipelines.universal/Runtime/RendererFeatures/SurfaceCacheGlobalIlluminationRendererFeature/ScreenResolveLookup.compute b/Packages/com.unity.render-pipelines.universal/Runtime/RendererFeatures/SurfaceCacheGlobalIlluminationRendererFeature/ScreenResolveLookup.compute index 9fc7af46f70..0ec75b670bb 100644 --- a/Packages/com.unity.render-pipelines.universal/Runtime/RendererFeatures/SurfaceCacheGlobalIlluminationRendererFeature/ScreenResolveLookup.compute +++ b/Packages/com.unity.render-pipelines.universal/Runtime/RendererFeatures/SurfaceCacheGlobalIlluminationRendererFeature/ScreenResolveLookup.compute @@ -16,7 +16,7 @@ RWTexture2D _ResultL10; RWTexture2D _ResultL11; RWTexture2D _ResultL12; RWTexture2D _ResultNdcDepths; -RWStructuredBuffer _PatchCounterSets; +RWStructuredBuffer _PatchStatistics; StructuredBuffer _PatchIrradiances; StructuredBuffer _CellPatchIndices; @@ -65,7 +65,7 @@ void Lookup(uint2 lowResPixelPos : SV_DispatchThreadID) SphericalHarmonics::RGBL1 irradianceSum = (SphericalHarmonics::RGBL1)0; float weightSum = 0.0f; { - uint patchIdx = PatchUtil::FindPatchIndexAndUpdateLastAccess(_VolumeTargetPos, _CellPatchIndices, _VolumeSpatialResolution, _CascadeOffsets, _PatchCounterSets, _CascadeCount, _VolumeVoxelMinSize, worldPos, worldNormal, _FrameIdx); + uint patchIdx = PatchUtil::FindPatchIndexAndUpdateLastAccess(_VolumeTargetPos, _CellPatchIndices, _VolumeSpatialResolution, _CascadeOffsets, _PatchStatistics, _CascadeCount, _VolumeVoxelMinSize, worldPos, worldNormal, _FrameIdx); if (patchIdx != PatchUtil::invalidPatchIndex) { const float weight = 0.01f; @@ -85,7 +85,7 @@ void Lookup(uint2 lowResPixelPos : SV_DispatchThreadID) const float3 localJitteredDirection = SampleConeUniform(jitter.x, jitter.y, cosSpread); const float3 jitteredWorldDir = mul(orthoBasis, localJitteredDirection); - uint patchIdx = PatchUtil::FindPatchIndexAndUpdateLastAccess(_VolumeTargetPos, _CellPatchIndices, _VolumeSpatialResolution, _CascadeOffsets, _PatchCounterSets, _CascadeCount, _VolumeVoxelMinSize, jitteredWorldPos, jitteredWorldDir, _FrameIdx); + uint patchIdx = PatchUtil::FindPatchIndexAndUpdateLastAccess(_VolumeTargetPos, _CellPatchIndices, _VolumeSpatialResolution, _CascadeOffsets, _PatchStatistics, _CascadeCount, _VolumeVoxelMinSize, jitteredWorldPos, jitteredWorldDir, _FrameIdx); if (patchIdx != PatchUtil::invalidPatchIndex) { SphericalHarmonics::AddMut(irradianceSum, _PatchIrradiances[patchIdx]); diff --git a/Packages/com.unity.render-pipelines.universal/Runtime/RendererFeatures/SurfaceCacheGlobalIlluminationRendererFeature/SurfaceCacheGlobalIlluminationRendererFeature.cs b/Packages/com.unity.render-pipelines.universal/Runtime/RendererFeatures/SurfaceCacheGlobalIlluminationRendererFeature/SurfaceCacheGlobalIlluminationRendererFeature.cs index 2687c930496..91fe6256dbb 100644 --- a/Packages/com.unity.render-pipelines.universal/Runtime/RendererFeatures/SurfaceCacheGlobalIlluminationRendererFeature/SurfaceCacheGlobalIlluminationRendererFeature.cs +++ b/Packages/com.unity.render-pipelines.universal/Runtime/RendererFeatures/SurfaceCacheGlobalIlluminationRendererFeature/SurfaceCacheGlobalIlluminationRendererFeature.cs @@ -110,7 +110,6 @@ internal static class ShaderIDs public static readonly int _PatchGeometries = Shader.PropertyToID("_PatchGeometries"); public static readonly int _PatchIrradiances0 = Shader.PropertyToID("_PatchIrradiances0"); public static readonly int _PatchIrradiances1 = Shader.PropertyToID("_PatchIrradiances1"); - public static readonly int _PatchCounterSets = Shader.PropertyToID("_PatchCounterSets"); public static readonly int _CellAllocationMarks = Shader.PropertyToID("_CellAllocationMarks"); public static readonly int _CellPatchIndices = Shader.PropertyToID("_CellPatchIndices"); public static readonly int _Result = Shader.PropertyToID("_Result"); @@ -183,7 +182,6 @@ private class DebugPassData internal GraphicsBuffer PatchGeometries; internal GraphicsBuffer PatchCellIndices; internal GraphicsBuffer PatchStatistics; - internal GraphicsBuffer PatchCounterSets; internal DebugViewMode_ ViewMode; internal uint FrameIndex; internal bool ShowSamplePosition; @@ -228,7 +226,7 @@ private class PatchAllocationPassData internal GraphicsBuffer PatchIrradiances1; internal GraphicsBuffer PatchGeometries; internal GraphicsBuffer PatchCellIndices; - internal GraphicsBuffer PatchCounterSets; + internal GraphicsBuffer PatchStatistics; internal uint FrameIdx; internal uint VolumeSpatialResolution; internal uint VolumeCascadeCount; @@ -258,7 +256,7 @@ private class ScreenIrradianceLookupPassData internal TextureHandle LowResScreenNdcDepths; internal GraphicsBuffer CellPatchIndices; internal GraphicsBuffer PatchIrradiances; - internal GraphicsBuffer PatchCounterSets; + internal GraphicsBuffer PatchStatistics; internal GraphicsBuffer CascadeOffsets; internal uint VolumeSpatialResolution; internal uint VolumeCascadeCount; @@ -526,7 +524,7 @@ public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer passData.PatchIrradiances1 = _cache.Patches.Irradiances[2]; passData.PatchGeometries = _cache.Patches.Geometries; passData.PatchCellIndices = _cache.Patches.CellIndices; - passData.PatchCounterSets = _cache.Patches.CounterSets; + passData.PatchStatistics = _cache.Patches.Statistics; passData.FrameIdx = _frameIdx; passData.VolumeSpatialResolution = _cache.Volume.SpatialResolution; passData.VolumeCascadeCount = _cache.Volume.CascadeCount; @@ -566,7 +564,19 @@ public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer var changes = _sceneTracker.GetChanges(filterBakedLights); _worldAdapter.UpdateMaterials(_world, changes.addedMaterials, changes.removedMaterials, changes.changedMaterials); - _worldAdapter.UpdateInstances(_world, changes.addedInstances, changes.changedInstances, changes.removedInstances, _fallbackMaterial); + _worldAdapter.UpdateMeshRenderers( + _world, + changes.addedMeshRenderers, + changes.changedMeshRenderers, + changes.removedMeshRenderers, + _fallbackMaterial); + _worldAdapter.UpdateTerrains( + _world, + changes.addedTerrains, + changes.changedTerrains, + changes.removedTerrains, + _fallbackMaterial); + const bool multiplyPunctualLightIntensityByPI = false; _worldAdapter.UpdateLights(_world, changes.addedLights, changes.removedLights, changes.changedLights, multiplyPunctualLightIntensityByPI); @@ -610,7 +620,7 @@ public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer data.LowResScreenNdcDepths = lowResScreenNdcDepthsHandle; data.CellPatchIndices = _cache.Volume.CellPatchIndices; data.PatchIrradiances = _cache.Patches.Irradiances[outputIrradianceBufferIdx]; - data.PatchCounterSets = _cache.Patches.CounterSets; + data.PatchStatistics = _cache.Patches.Statistics; data.CascadeOffsets = _cache.Volume.CascadeOffsetBuffer; data.VolumeSpatialResolution = _cache.Volume.SpatialResolution; data.VolumeVoxelMinSize = _cache.Volume.VoxelMinSize; @@ -651,7 +661,6 @@ public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer passData.PatchIrradiances = _cache.Patches.Irradiances[outputIrradianceBufferIdx]; passData.PatchGeometries = _cache.Patches.Geometries; passData.PatchStatistics = _cache.Patches.Statistics; - passData.PatchCounterSets = _cache.Patches.CounterSets; passData.VolumeSpatialResolution = _cache.Volume.SpatialResolution; passData.VolumeVoxelMinSize = _cache.Volume.VoxelMinSize; passData.VolumeCascadeCount = _cache.Volume.CascadeCount; @@ -712,7 +721,7 @@ public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer static void UpdateWorld(WorldUpdatePassData data, UnsafeGraphContext graphCtx, ref GraphicsBuffer scratch) { var cmd = CommandBufferHelpers.GetNativeCommandBuffer(graphCtx.cmd); - data.World.Build(cmd, ref scratch, data.EnvCubemapResolution, data.Sun); + data.World.Commit(cmd, ref scratch, data.EnvCubemapResolution, data.Sun); } static void LookupScreenIrradiance(ScreenIrradianceLookupPassData data, ComputeGraphContext cgContext) @@ -730,7 +739,7 @@ static void LookupScreenIrradiance(ScreenIrradianceLookupPassData data, ComputeG cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._CellPatchIndices, data.CellPatchIndices); cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._PatchIrradiances, data.PatchIrradiances); cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._CascadeOffsets, data.CascadeOffsets); - cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._PatchCounterSets, data.PatchCounterSets); + cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._PatchStatistics, data.PatchStatistics); cmd.SetComputeIntParam(shader, ShaderIDs._VolumeSpatialResolution, (int)data.VolumeSpatialResolution); cmd.SetComputeIntParam(shader, ShaderIDs._CascadeCount, (int)data.VolumeCascadeCount); cmd.SetComputeIntParam(shader, ShaderIDs._SampleCount, (int)data.SampleCount); @@ -798,7 +807,7 @@ static void AllocatePatches(PatchAllocationPassData data, ComputeGraphContext cg cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._PatchIrradiances1, data.PatchIrradiances1); cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._PatchGeometries, data.PatchGeometries); cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._PatchCellIndices, data.PatchCellIndices); - cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._PatchCounterSets, data.PatchCounterSets); + cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._PatchStatistics, data.PatchStatistics); cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._CascadeOffsets, data.CascadeOffsets); cmd.SetComputeIntParam(shader, ShaderIDs._FrameIdx, (int)data.FrameIdx); cmd.SetComputeIntParam(shader, ShaderIDs._VolumeSpatialResolution, (int)data.VolumeSpatialResolution); @@ -832,7 +841,6 @@ static void RenderDebug(DebugPassData data, ComputeGraphContext cgContext) cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._CascadeOffsets, data.CascadeOffsets); cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._PatchCellIndices, data.PatchCellIndices); cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._PatchStatistics, data.PatchStatistics); - cmd.SetComputeBufferParam(shader, kernelIndex, ShaderIDs._PatchCounterSets, data.PatchCounterSets); cmd.SetComputeIntParam(shader, ShaderIDs._VolumeSpatialResolution, (int)data.VolumeSpatialResolution); cmd.SetComputeIntParam(shader, ShaderIDs._CascadeCount, (int)data.VolumeCascadeCount); @@ -968,26 +976,36 @@ private static void UpdateLights( world.UpdateLights(handlesToUpdate, ConvertUnityLightsToLightDescriptors(changedLights.ToArray(), multiplyPunctualLightIntensityByPI)); } - internal void UpdateInstances( + internal void UpdateMeshRenderers( SurfaceCacheWorld world, - List addedInstances, - List changedInstances, - List removedInstances, + List addedMeshRenderers, + List changedMeshRenderers, + List removedMeshRenderers, Material fallbackMaterial) { - UpdateInstances(world, _entityIDToWorldInstanceHandles, _entityIDToWorldMaterialHandles, addedInstances, changedInstances, removedInstances, fallbackMaterial); + UpdateMeshRenderers(world, _entityIDToWorldInstanceHandles, _entityIDToWorldMaterialHandles, addedMeshRenderers, changedMeshRenderers, removedMeshRenderers, fallbackMaterial); } - private static void UpdateInstances( + internal void UpdateTerrains( + SurfaceCacheWorld world, + List addedTerrains, + List changedTerrains, + List removedTerrains, + Material fallbackMaterial) + { + UpdateTerrains(world, _entityIDToWorldInstanceHandles, _entityIDToWorldMaterialHandles, addedTerrains, changedTerrains, removedTerrains, fallbackMaterial); + } + + private static void UpdateMeshRenderers( SurfaceCacheWorld world, Dictionary entityIDToInstanceHandle, Dictionary entityIDToMaterialHandle, - List addedInstances, - List changedInstances, - List removedInstances, + List addedMeshRenderers, + List changedMeshRenderers, + List removedMeshRenderers, Material fallbackMaterial) { - foreach (var meshRendererEntityID in removedInstances) + foreach (var meshRendererEntityID in removedMeshRenderers) { if (entityIDToInstanceHandle.TryGetValue(meshRendererEntityID, out var instanceHandle)) { @@ -996,7 +1014,7 @@ private static void UpdateInstances( } } - foreach (var meshRenderer in addedInstances) + foreach (var meshRenderer in addedMeshRenderers) { Debug.Assert(!meshRenderer.isPartOfStaticBatch); @@ -1026,20 +1044,20 @@ private static void UpdateInstances( entityIDToInstanceHandle.Add(entityID, instance); } - foreach (var instanceUpdate in changedInstances) + foreach (var meshRendererUpdate in changedMeshRenderers) { - var meshRenderer = instanceUpdate.meshRenderer; + var meshRenderer = meshRendererUpdate.instance; var gameObject = meshRenderer.gameObject; Debug.Assert(entityIDToInstanceHandle.ContainsKey(meshRenderer.GetEntityId())); var instanceHandle = entityIDToInstanceHandle[meshRenderer.GetEntityId()]; - if ((instanceUpdate.changes & ModifiedProperties.Transform) != 0) + if ((meshRendererUpdate.changes & ModifiedProperties.Transform) != 0) { world.UpdateInstanceTransform(instanceHandle, gameObject.transform.localToWorldMatrix); } - if ((instanceUpdate.changes & ModifiedProperties.Material) != 0) + if ((meshRendererUpdate.changes & ModifiedProperties.Material) != 0) { var materials = Util.GetMaterials(meshRenderer); var materialHandles = new MaterialHandle[materials.Length]; @@ -1062,6 +1080,68 @@ private static void UpdateInstances( } } + private void UpdateTerrains( + SurfaceCacheWorld world, + Dictionary entityIDToInstanceHandle, + Dictionary entityIDToMaterialHandle, + List addedTerrains, + List changedTerrains, + List removedTerrains, + Material fallbackMaterial) + { + foreach (var terrainEntityID in removedTerrains) + { + if (entityIDToInstanceHandle.TryGetValue(terrainEntityID, out var instanceHandle)) + { + world.RemoveInstance(instanceHandle); + entityIDToInstanceHandle.Remove(terrainEntityID); + } + } + + foreach (var terrain in addedTerrains) + { + var localToWorldMatrix = terrain.transform.localToWorldMatrix; + + var material = terrain.splatBaseMaterial; + var matEntityId = material == null ? fallbackMaterial.GetEntityId() : material.GetEntityId(); + var materialHandle = entityIDToMaterialHandle[matEntityId]; + uint mask = 1u; + + InstanceHandle instance = world.AddInstance(terrain, materialHandle, mask, in localToWorldMatrix); + var entityID = terrain.GetEntityId(); + Debug.Assert(!entityIDToInstanceHandle.ContainsKey(entityID)); + entityIDToInstanceHandle.Add(entityID, instance); + } + + foreach (var terrainUpdate in changedTerrains) + { + var terrain = terrainUpdate.instance; + var gameObject = terrain.gameObject; + + Debug.Assert(entityIDToInstanceHandle.ContainsKey(terrain.GetEntityId())); + var instanceHandle = entityIDToInstanceHandle[terrain.GetEntityId()]; + + if ((terrainUpdate.changes & ModifiedProperties.Transform) != 0) + { + world.UpdateInstanceTransform(instanceHandle, gameObject.transform.localToWorldMatrix); + } + + if ((terrainUpdate.changes & ModifiedProperties.Material) != 0) + { + var material = terrain.splatBaseMaterial; + + var matEntityId = material == null ? fallbackMaterial.GetEntityId() : material.GetEntityId(); + var materialHandle = entityIDToMaterialHandle[matEntityId]; + + world.UpdateInstanceMaterials(instanceHandle, new MaterialHandle[] { materialHandle }); + + var mask = material != null ? 1u : 0u; + + world.UpdateInstanceMask(instanceHandle, new uint[] { mask } ); + } + } + } + public void Dispose() { CoreUtils.Destroy(_fallbackMaterialDescriptor.Albedo); diff --git a/Packages/com.unity.render-pipelines.universal/Runtime/ScriptableRenderer.cs b/Packages/com.unity.render-pipelines.universal/Runtime/ScriptableRenderer.cs index 30519471bac..dfcd6511970 100644 --- a/Packages/com.unity.render-pipelines.universal/Runtime/ScriptableRenderer.cs +++ b/Packages/com.unity.render-pipelines.universal/Runtime/ScriptableRenderer.cs @@ -235,7 +235,9 @@ void SetPerCameraShaderVariables(RasterCommandBuffer cmd, UniversalCameraData ca float invNear = Mathf.Approximately(near, 0.0f) ? 0.0f : 1.0f / near; float invFar = Mathf.Approximately(far, 0.0f) ? 0.0f : 1.0f / far; float isOrthographic = camera.orthographic ? 1.0f : 0.0f; - +#if (UNITY_META_QUEST) + cmd.SetKeyword(ShaderGlobalKeywords.META_QUEST_ORTHO_PROJ, camera.orthographic); +#endif // From http://www.humus.name/temp/Linearize%20depth.txt // But as depth component textures on OpenGL always return in 0..1 range (as in D3D), we have to use // the same constants for both D3D and OpenGL here. diff --git a/Packages/com.unity.render-pipelines.universal/Runtime/UniversalAdditionalCameraData.cs b/Packages/com.unity.render-pipelines.universal/Runtime/UniversalAdditionalCameraData.cs index 0bdc4312796..d2cb2eb5bf4 100644 --- a/Packages/com.unity.render-pipelines.universal/Runtime/UniversalAdditionalCameraData.cs +++ b/Packages/com.unity.render-pipelines.universal/Runtime/UniversalAdditionalCameraData.cs @@ -491,7 +491,7 @@ public partial class UniversalAdditionalCameraData : MonoBehaviour, ISerializati [NonSerialized] internal UniversalCameraHistory m_History = new UniversalCameraHistory(); [SerializeField] internal TemporalAA.Settings m_TaaSettings = TemporalAA.Settings.Create(); - + static UniversalAdditionalCameraData s_DefaultAdditionalCameraData = null; internal static UniversalAdditionalCameraData defaultAdditionalCameraData { @@ -611,6 +611,24 @@ internal void UpdateCameraStack() } } + // internal: Required in test + internal bool TryAddCameraToStack(Camera overlayCamera) + { + if (overlayCamera == null + || !overlayCamera.TryGetComponent(out UniversalAdditionalCameraData urpCameraData) + || urpCameraData.renderType != CameraRenderType.Overlay + || renderType != CameraRenderType.Base) + return false; + + var overlayRenderer = urpCameraData.scriptableRenderer; + if (overlayRenderer.GetType() != scriptableRenderer.GetType() + || (overlayRenderer.SupportedCameraStackingTypes() & 1 << (int)CameraRenderType.Overlay) == 0) + return false; + + m_Cameras.Add(overlayCamera); + return true; + } + /// /// If true, this camera will clear depth value before rendering. Only valid for Overlay cameras. /// @@ -997,15 +1015,17 @@ ScriptableRenderer GetRawRenderer() return renderers[m_RendererIndex]; } - + + internal int rendererIndex => m_RendererIndex; + enum Version { Initial = 0, DepthAndOpaqueTextureOptions = 2, - + Count } - + [SerializeField] Version m_Version = Version.Count; // This piece of code is needed because some objects could have been created before existence of Version enum @@ -1021,7 +1041,7 @@ void ISerializationCallbackReceiver.OnAfterDeserialize() { if (m_Version == Version.Count) // deserializing and object without version m_Version = Version.Initial; // reset to run the migration - + if (m_Version < Version.DepthAndOpaqueTextureOptions) { m_RequiresDepthTextureOption = (m_RequiresDepthTexture) ? CameraOverrideOption.On : CameraOverrideOption.Off; diff --git a/Packages/com.unity.render-pipelines.universal/Runtime/UniversalRenderPipeline.cs b/Packages/com.unity.render-pipelines.universal/Runtime/UniversalRenderPipeline.cs index a207698f4cc..a2d376700f8 100644 --- a/Packages/com.unity.render-pipelines.universal/Runtime/UniversalRenderPipeline.cs +++ b/Packages/com.unity.render-pipelines.universal/Runtime/UniversalRenderPipeline.cs @@ -981,6 +981,8 @@ static void RenderCameraStack(ScriptableRenderContext context, Camera baseCamera } bool isStackedRendering = lastActiveOverlayCameraIndex != -1; + if (isStackedRendering && renderer is UniversalRenderer {onTileValidation: true}) + throw new ArgumentException("The active URP Renderer has 'On Tile Validation' on. This currently does not allow Camera Stacking usage. Check your scene and remove all overlay Cameras."); // Prepare XR rendering var xrActive = false; @@ -1368,6 +1370,9 @@ static UniversalCameraData CreateCameraData(ContextContainer frameData, Camera c if (cameraData.xrRendering && rendererSupportsMSAA && camera.targetTexture == null) msaaSamples = (int)XRSystem.GetDisplayMSAASamples(); + if (renderer is UniversalRenderer { onTileValidation: true } && UniversalRenderer.PlatformRequiresExplicitMsaaResolve()) + msaaSamples = 1; + bool needsAlphaChannel = Graphics.preserveFramebufferAlpha; cameraData.hdrColorBufferPrecision = asset ? asset.hdrColorBufferPrecision : HDRColorBufferPrecision._32Bits; diff --git a/Packages/com.unity.render-pipelines.universal/Runtime/UniversalRenderPipelineCore.cs b/Packages/com.unity.render-pipelines.universal/Runtime/UniversalRenderPipelineCore.cs index f4bd163d80f..8209fbc91d3 100644 --- a/Packages/com.unity.render-pipelines.universal/Runtime/UniversalRenderPipelineCore.cs +++ b/Packages/com.unity.render-pipelines.universal/Runtime/UniversalRenderPipelineCore.cs @@ -977,8 +977,8 @@ internal static class ShaderGlobalKeywords public static GlobalKeyword LinearToSRGBConversion; public static GlobalKeyword _ENABLE_ALPHA_OUTPUT; public static GlobalKeyword ForwardPlus; // Backward compatibility. Deprecated in 6.1. - -#if UNITY_META_QUEST +#if (UNITY_META_QUEST) + public static GlobalKeyword META_QUEST_ORTHO_PROJ; public static GlobalKeyword META_QUEST_LIGHTUNROLL; #endif @@ -1095,7 +1095,8 @@ public static void InitializeShaderGlobalKeywords() ShaderGlobalKeywords.LinearToSRGBConversion = GlobalKeyword.Create(ShaderKeywordStrings.LinearToSRGBConversion); ShaderGlobalKeywords._ENABLE_ALPHA_OUTPUT = GlobalKeyword.Create(ShaderKeywordStrings._ENABLE_ALPHA_OUTPUT); ShaderGlobalKeywords.ForwardPlus = GlobalKeyword.Create(ShaderKeywordStrings.ForwardPlus); // Backward compatibility. Deprecated in 6.1. -#if UNITY_META_QUEST +#if (UNITY_META_QUEST) + ShaderGlobalKeywords.META_QUEST_ORTHO_PROJ = GlobalKeyword.Create(ShaderKeywordStrings.META_QUEST_ORTHO_PROJ); ShaderGlobalKeywords.META_QUEST_LIGHTUNROLL = GlobalKeyword.Create(ShaderKeywordStrings.META_QUEST_LIGHTUNROLL); #endif @@ -1431,7 +1432,10 @@ public static class ShaderKeywordStrings /// Deprecated keyword. Use ClusterLightLoop instead. internal const string ForwardPlus = "_FORWARD_PLUS"; // Backward compatibility. Deprecated in 6.1. -#if UNITY_META_QUEST +#if (UNITY_META_QUEST) + /// Used to statically branch when checking for projection type on Meta Quest device . + internal const string META_QUEST_ORTHO_PROJ = "META_QUEST_ORTHO_PROJ"; + /// Unroll light loop if there is only one additional light on Meta Quest device . internal const string META_QUEST_LIGHTUNROLL = "META_QUEST_LIGHTUNROLL"; #endif @@ -1603,6 +1607,12 @@ internal static RenderTextureDescriptor CreateRenderTextureDescriptor(Camera cam private static Lightmapping.RequestLightsDelegate lightsDelegate = (Light[] requests, NativeArray lightsOutput) => { LightDataGI lightData = new LightDataGI(); + + // URP uses a game like lambertian response for punctual lights, they are off by a factor PI. + // Since LightBaker expects its punctual lights to be expressed in standard radiometric units, the intensity is pre-multiplied by PI here to counteract this. + // This ensures that the baked punctual light intensity matches realtime intensity. (See GFXLIGHT-1755) + const float piCorrection = Mathf.PI; + #if UNITY_EDITOR // Always extract lights in the Editor. for (int i = 0; i < requests.Length; i++) @@ -1617,6 +1627,8 @@ internal static RenderTextureDescriptor CreateRenderTextureDescriptor(Camera cam case LightType.Directional: DirectionalLight directionalLight = new DirectionalLight(); LightmapperUtils.Extract(light, ref directionalLight); + directionalLight.color.intensity *= piCorrection; + directionalLight.indirectColor.intensity *= piCorrection; if (light.cookie != null) { @@ -1638,11 +1650,15 @@ internal static RenderTextureDescriptor CreateRenderTextureDescriptor(Camera cam case LightType.Point: PointLight pointLight = new PointLight(); LightmapperUtils.Extract(light, ref pointLight); + pointLight.color.intensity *= piCorrection; + pointLight.indirectColor.intensity *= piCorrection; lightData.Init(ref pointLight, ref cookie); break; case LightType.Spot: SpotLight spotLight = new SpotLight(); LightmapperUtils.Extract(light, ref spotLight); + spotLight.color.intensity *= piCorrection; + spotLight.indirectColor.intensity *= piCorrection; spotLight.innerConeAngle = light.innerSpotAngle * Mathf.Deg2Rad; spotLight.angularFalloff = AngularFalloffType.AnalyticAndInnerAngle; lightData.Init(ref spotLight, ref cookie); @@ -1688,16 +1704,22 @@ internal static RenderTextureDescriptor CreateRenderTextureDescriptor(Camera cam case LightType.Directional: DirectionalLight directionalLight = new DirectionalLight(); LightmapperUtils.Extract(light, ref directionalLight); + directionalLight.color.intensity *= piCorrection; + directionalLight.indirectColor.intensity *= piCorrection; lightData.Init(ref directionalLight); break; case LightType.Point: PointLight pointLight = new PointLight(); LightmapperUtils.Extract(light, ref pointLight); + pointLight.color.intensity *= piCorrection; + pointLight.indirectColor.intensity *= piCorrection; lightData.Init(ref pointLight); break; case LightType.Spot: SpotLight spotLight = new SpotLight(); LightmapperUtils.Extract(light, ref spotLight); + spotLight.color.intensity *= piCorrection; + spotLight.indirectColor.intensity *= piCorrection; spotLight.innerConeAngle = light.innerSpotAngle * Mathf.Deg2Rad; spotLight.angularFalloff = AngularFalloffType.AnalyticAndInnerAngle; lightData.Init(ref spotLight); diff --git a/Packages/com.unity.render-pipelines.universal/Runtime/UniversalRenderer.cs b/Packages/com.unity.render-pipelines.universal/Runtime/UniversalRenderer.cs index e8a84918429..3d1cf7ef69a 100644 --- a/Packages/com.unity.render-pipelines.universal/Runtime/UniversalRenderer.cs +++ b/Packages/com.unity.render-pipelines.universal/Runtime/UniversalRenderer.cs @@ -200,6 +200,7 @@ internal RenderingMode renderingModeActual { internal LayerMask opaqueLayerMask { get; set; } internal LayerMask transparentLayerMask { get; set; } internal bool shadowTransparentReceive { get; set; } + internal bool onTileValidation { get; set; } internal GraphicsFormat cameraDepthTextureFormat { get => (m_CameraDepthTextureFormat != DepthFormat.Default) ? (GraphicsFormat)m_CameraDepthTextureFormat : CoreUtils.GetDefaultDepthStencilFormat(); } internal GraphicsFormat cameraDepthAttachmentFormat { get => (m_CameraDepthAttachmentFormat != DepthFormat.Default) ? (GraphicsFormat)m_CameraDepthAttachmentFormat : CoreUtils.GetDefaultDepthStencilFormat(); } @@ -256,6 +257,8 @@ public UniversalRenderer(UniversalRendererData data) : base(data) transparentLayerMask = data.transparentLayerMask; shadowTransparentReceive = data.shadowTransparentReceive; + onTileValidation = data.onTileValidation; + var asset = UniversalRenderPipeline.asset; if (asset != null && asset.supportsLightCookies) { @@ -385,10 +388,12 @@ public UniversalRenderer(UniversalRendererData data) : base(data) supportedRenderingFeatures = new RenderingFeatures(); - if (renderingModeRequested == RenderingMode.Deferred || renderingModeRequested == RenderingMode.DeferredPlus) + if (renderingModeRequested is RenderingMode.Deferred or RenderingMode.DeferredPlus) { // Deferred rendering does not support MSAA. - this.supportedRenderingFeatures.msaa = false; + // if On-Tile Validation is enabled, Deferred(+) will fallback to Forward(+). Thus we must not fix msaa value in this case. + if (!onTileValidation) + supportedRenderingFeatures.msaa = false; } LensFlareCommonSRP.mergeNeeded = 0; @@ -648,7 +653,7 @@ void AddRequirementsOfInternalFeatures(ref RenderPassInputSummary inputSummary, break; case CopyDepthMode.AfterOpaques: earliestDepth = RenderPassEvent.AfterRenderingOpaques; - break; + break; } inputSummary.requiresDepthTextureEarliestEvent = (RenderPassEvent)Mathf.Min((int)earliestDepth, (int)inputSummary.requiresDepthTextureEarliestEvent); @@ -668,7 +673,7 @@ void AddRequirementsOfInternalFeatures(ref RenderPassInputSummary inputSummary, inputSummary.requiresDepthTexture = true; inputSummary.requiresDepthTextureEarliestEvent = (RenderPassEvent)Mathf.Min( (int)RenderPassEvent.BeforeRenderingPostProcessing, (int)inputSummary.requiresDepthTextureEarliestEvent); } - + } // Motion vectors imply depth diff --git a/Packages/com.unity.render-pipelines.universal/Runtime/UniversalRendererData.cs b/Packages/com.unity.render-pipelines.universal/Runtime/UniversalRendererData.cs index 1560e64ba0e..8a13ae00419 100644 --- a/Packages/com.unity.render-pipelines.universal/Runtime/UniversalRendererData.cs +++ b/Packages/com.unity.render-pipelines.universal/Runtime/UniversalRendererData.cs @@ -154,6 +154,7 @@ static void CreateUniversalRendererData() bool m_AccurateGbufferNormals = false; [SerializeField] IntermediateTextureMode m_IntermediateTextureMode = IntermediateTextureMode.Always; + [SerializeField] bool m_OnTileValidation = false; /// protected override ScriptableRenderer Create() @@ -346,6 +347,23 @@ public IntermediateTextureMode intermediateTextureMode } } + /// + /// On-Tile validation validates features to prevent going off tile. + /// This is mainly useful for tile based architectures. + /// + public bool onTileValidation + { + get => m_OnTileValidation; + set + { + if (m_OnTileValidation == value) + return; + + SetDirty(); + m_OnTileValidation = value; + } + } + /// /// Returns true if the renderer uses a deferred lighting pass and GBuffers. /// This is true for the Deferred and Deferred+ rendering paths. diff --git a/Packages/com.unity.render-pipelines.universal/Runtime/UniversalRendererRenderGraph.cs b/Packages/com.unity.render-pipelines.universal/Runtime/UniversalRendererRenderGraph.cs index e47b5636e67..3226bd3b3ad 100644 --- a/Packages/com.unity.render-pipelines.universal/Runtime/UniversalRendererRenderGraph.cs +++ b/Packages/com.unity.render-pipelines.universal/Runtime/UniversalRendererRenderGraph.cs @@ -1,4 +1,6 @@ using System; +using System.Collections.Generic; +using System.Diagnostics; using System.Runtime.CompilerServices; using UnityEngine.Experimental.Rendering; using UnityEngine.Rendering.RenderGraphModule; @@ -555,7 +557,7 @@ private void RenderRawColorDepthHistory(RenderGraph renderGraph, UniversalCamera //On GLES we don't support sampling the MSAA targets, so if auto depth resolve is not available, the only thing that works is rendering to a color target. //This has been the behavior from at least 6.0. However, it results in the format mostly being color on the different graphics APIs, even when - //it could be a depth format if MSAA sampling for depht is allowed. + //it could be a depth format if MSAA sampling for depht is allowed. if (RenderingUtils.MultisampleDepthResolveSupported()) { tempColorDepthDesc.graphicsFormat = GraphicsFormat.None; @@ -566,7 +568,7 @@ private void RenderRawColorDepthHistory(RenderGraph renderGraph, UniversalCamera tempColorDepthDesc.depthStencilFormat = GraphicsFormat.None; } - depthHistory.Update(ref tempColorDepthDesc, xrMultipassEnabled); + depthHistory.Update(ref tempColorDepthDesc, xrMultipassEnabled); if (depthHistory.GetCurrentTexture(multipassId) != null) { @@ -589,15 +591,69 @@ public override void OnBeginRenderGraphFrame() resourceData.InitFrame(); } + static void ApplyConstraints(bool onTileValidation, UniversalRenderingData renderingData, UniversalCameraData cameraData, UniversalPostProcessingData postProcessingData, List activeRenderPassQueue, ref RenderingMode renderingMode, ref DepthPrimingMode depthPrimingMode) + { + if (!onTileValidation) + return; + + cameraData.requiresOpaqueTexture = false; + cameraData.requiresDepthTexture = false; + + cameraData.postProcessEnabled = false; + cameraData.stackAnyPostProcessingEnabled = false; + postProcessingData.isEnabled = false; + + cameraData.useGPUOcclusionCulling = false; + cameraData.isHdrEnabled = false; + + if (!PlatformAutoDetect.isXRMobile) + { + cameraData.renderScale = 1.0f; + cameraData.imageScalingMode = ImageScalingMode.None; + } + + if (renderingData.renderingMode == RenderingMode.DeferredPlus) + renderingData.renderingMode = RenderingMode.ForwardPlus; + + if (renderingMode == RenderingMode.DeferredPlus) + renderingMode = RenderingMode.ForwardPlus; + + if (renderingData.renderingMode == RenderingMode.Deferred) + renderingData.renderingMode = RenderingMode.Forward; + + if (renderingMode == RenderingMode.Deferred) + renderingMode = RenderingMode.Forward; + + if (cameraData.baseCamera != null && cameraData.baseCamera != cameraData.camera) + throw new ArgumentException("The active URP Renderer has 'On Tile Validation' on. This currently does not allow Camera Stacking usage. Check your scene and remove all overlay Cameras."); + + if (activeRenderPassQueue.Count > 0) + throw new ArgumentException("The active URP Renderer has 'On Tile Validation' on. This currently does not allow any ScriptableRenderFeature enabled, and it does not allow enqueuing any ScriptableRenderPass. Check your Renderer asset and disable all Renderer Features. Also, ensure that no C# script enqueus any passes on the renderer."); + } + + //Catches mistakes by Unity devs with regards to clearing the settings + [Conditional("DEVELOPMENT_BUILD"), Conditional("UNITY_EDITOR")] + static void ValidateCorrectnessOfConstraints(bool onTileValidation, in RenderPassInputSummary renderPassInputs, bool requireIntermediateTextures) + { + if (!onTileValidation) + return; + + if (renderPassInputs is { requiresColorTexture: false, requiresDepthTexture: false, requiresMotionVectors: false, requiresNormalsTexture: false} && !requireIntermediateTextures) + return; + + throw new ArgumentException("On Tile Validation is on but certain features still added requirements that would validate this."); + } + internal override void OnRecordRenderGraph(RenderGraph renderGraph, ScriptableRenderContext context) { UniversalResourceData resourceData = frameData.Get(); - UniversalRenderingData renderingData = frameData.Get(); UniversalCameraData cameraData = frameData.Get(); UniversalLightData lightData = frameData.Get(); UniversalPostProcessingData postProcessingData = frameData.Get(); + ApplyConstraints(onTileValidation, renderingData, cameraData, postProcessingData,activeRenderPassQueue, ref m_RenderingMode, ref m_DepthPrimingMode); + MotionVectorRenderPass.SetRenderGraphMotionVectorGlobalMatrices(renderGraph, cameraData); SetupRenderGraphLights(renderGraph, renderingData, cameraData, lightData); @@ -607,7 +663,7 @@ internal override void OnRecordRenderGraph(RenderGraph renderGraph, ScriptableRe bool isCameraTargetOffscreenDepth = cameraData.camera.targetTexture != null && cameraData.camera.targetTexture.format == RenderTextureFormat.Depth; bool applyPostProcessing = cameraData.postProcessEnabled && m_PostProcess != null; - //First get the input requirements for the the ScriptableRenderPasses. These are all user passes plus potentially some that URP adds. + //First get the input requirements for the the ScriptableRenderPasses. These are all user passes plus potentially some that URP adds. RenderPassInputSummary renderPassInputs = GetRenderPassInputs(activeRenderPassQueue); //Then add all the requirements of internal features that are not implemented as ScriptableRenderPass's. After this call we have a complete view on the render pass input requirements for the entire frame. AddRequirementsOfInternalFeatures(ref renderPassInputs, cameraData, applyPostProcessing, m_RenderingLayerProvidesByDepthNormalPass, m_MotionVectorPass, m_CopyDepthMode); @@ -628,6 +684,8 @@ internal override void OnRecordRenderGraph(RenderGraph renderGraph, ScriptableRe if (cameraData.renderType == CameraRenderType.Base) s_RequiresIntermediateAttachments = RequiresIntermediateAttachments(cameraData, in renderPassInputs, requireCopyFromDepth, applyPostProcessing); + ValidateCorrectnessOfConstraints(onTileValidation, renderPassInputs, s_RequiresIntermediateAttachments); + CreateRenderGraphCameraRenderTargets(renderGraph, isCameraTargetOffscreenDepth, s_RequiresIntermediateAttachments, depthTextureIsDepthFormat); if (DebugHandler != null) @@ -897,7 +955,7 @@ static private DepthCopySchedule CalculateDepthCopySchedule(RenderPassEvent earl else if ((earliestDepthReadEvent < RenderPassEvent.AfterRenderingOpaques)) { // If we have a partial prepass (or no prepass), we must finish rendering the gbuffer before complete depth data is available. - schedule = DepthCopySchedule.AfterGBuffer; + schedule = DepthCopySchedule.AfterGBuffer; } else if (earliestDepthReadEvent < RenderPassEvent.AfterRenderingSkybox) { @@ -907,7 +965,7 @@ static private DepthCopySchedule CalculateDepthCopySchedule(RenderPassEvent earl { schedule = DepthCopySchedule.AfterSkybox; } - else + else { schedule = DepthCopySchedule.AfterTransparents; } @@ -1384,7 +1442,7 @@ private void OnAfterRendering(RenderGraph renderGraph, bool applyPostProcessing) SetupAfterPostRenderGraphFinalPassDebug(renderGraph, frameData); } - //We already checked the passes so we can skip here if there are none as a small optimization + //We already checked the passes so we can skip here if there are none as a small optimization if (hasPassesAfterPostProcessing) RecordCustomRenderGraphPasses(renderGraph, RenderPassEvent.AfterRenderingPostProcessing); @@ -1409,7 +1467,7 @@ private void OnAfterRendering(RenderGraph renderGraph, bool applyPostProcessing) debugHandler?.UpdateShaderGlobalPropertiesForFinalValidationPass(renderGraph, cameraData, !resolveToDebugScreen); //Will swap the active camera targets to backbuffer (resourceData.SwitchActiveTexturesToBackbuffer) - m_FinalBlitPass.RecordRenderGraph(renderGraph, frameData); + m_FinalBlitPass.RecordRenderGraph(renderGraph, frameData); } RecordCustomRenderGraphPasses(renderGraph, RenderPassEvent.AfterRendering); @@ -1771,7 +1829,7 @@ void CreateCameraDepthCopyTexture(RenderGraph renderGraph, TextureDesc descripto var depthDescriptor = descriptor; depthDescriptor.msaaSamples = MSAASamples.None;// Depth-Only pass don't use MSAA - + if (isDepthTexture) { diff --git a/Packages/com.unity.render-pipelines.universal/Samples~/URPPackageSamples/RendererFeatures/AmbientOcclusion/AmbientOcclusion/LightingData.asset b/Packages/com.unity.render-pipelines.universal/Samples~/URPPackageSamples/RendererFeatures/AmbientOcclusion/AmbientOcclusion/LightingData.asset index cf26039a42d..e406e54b196 100644 Binary files a/Packages/com.unity.render-pipelines.universal/Samples~/URPPackageSamples/RendererFeatures/AmbientOcclusion/AmbientOcclusion/LightingData.asset and b/Packages/com.unity.render-pipelines.universal/Samples~/URPPackageSamples/RendererFeatures/AmbientOcclusion/AmbientOcclusion/LightingData.asset differ diff --git a/Packages/com.unity.render-pipelines.universal/ShaderLibrary/ShaderVariablesFunctions.hlsl b/Packages/com.unity.render-pipelines.universal/ShaderLibrary/ShaderVariablesFunctions.hlsl index b10860d604b..4027b51331d 100644 --- a/Packages/com.unity.render-pipelines.universal/ShaderLibrary/ShaderVariablesFunctions.hlsl +++ b/Packages/com.unity.render-pipelines.universal/ShaderLibrary/ShaderVariablesFunctions.hlsl @@ -48,7 +48,18 @@ float4 GetScaledScreenParams() // Returns 'true' if the current view performs a perspective projection. bool IsPerspectiveProjection() { +#if defined(UNITY_PLATFORM_META_QUEST) + #if defined(META_QUEST_ORTHO_PROJ_KEYWORD_DECLARED) + if (META_QUEST_ORTHO_PROJ) + return false; + else + return true; + #else + return true; + #endif +#else return (unity_OrthoParams.w == 0); +#endif } float3 GetCameraPositionWS() diff --git a/Packages/com.unity.render-pipelines.universal/Shaders/ComplexLit.shader b/Packages/com.unity.render-pipelines.universal/Shaders/ComplexLit.shader index 0f49fdd6518..f7ec19eada2 100644 --- a/Packages/com.unity.render-pipelines.universal/Shaders/ComplexLit.shader +++ b/Packages/com.unity.render-pipelines.universal/Shaders/ComplexLit.shader @@ -143,6 +143,9 @@ Shader "Universal Render Pipeline/Complex Lit" #pragma multi_compile _ EVALUATE_SH_MIXED EVALUATE_SH_VERTEX #pragma multi_compile _ _LIGHT_LAYERS #pragma multi_compile _ _CLUSTER_LIGHT_LOOP +#if defined(UNITY_PLATFORM_META_QUEST) + #pragma multi_compile _ META_QUEST_ORTHO_PROJ +#endif #pragma multi_compile_fragment _ _ADDITIONAL_LIGHT_SHADOWS #pragma multi_compile_fragment _ _REFLECTION_PROBE_BLENDING #pragma multi_compile_fragment _ _REFLECTION_PROBE_BOX_PROJECTION diff --git a/Packages/com.unity.render-pipelines.universal/Shaders/Lit.shader b/Packages/com.unity.render-pipelines.universal/Shaders/Lit.shader index 03fe611e5c1..1b5eb802ffd 100644 --- a/Packages/com.unity.render-pipelines.universal/Shaders/Lit.shader +++ b/Packages/com.unity.render-pipelines.universal/Shaders/Lit.shader @@ -152,6 +152,9 @@ Shader "Universal Render Pipeline/Lit" #pragma multi_compile_fragment _ _LIGHT_COOKIES #pragma multi_compile _ _LIGHT_LAYERS #pragma multi_compile _ _CLUSTER_LIGHT_LOOP +#if defined(UNITY_PLATFORM_META_QUEST) + #pragma multi_compile _ META_QUEST_ORTHO_PROJ +#endif #include_with_pragmas "Packages/com.unity.render-pipelines.core/ShaderLibrary/FoveatedRenderingKeywords.hlsl" #include_with_pragmas "Packages/com.unity.render-pipelines.universal/ShaderLibrary/RenderingLayers.hlsl" diff --git a/Packages/com.unity.render-pipelines.universal/Shaders/Particles/ParticlesLit.shader b/Packages/com.unity.render-pipelines.universal/Shaders/Particles/ParticlesLit.shader index 50f952ebdc6..9d82a9f6f3c 100644 --- a/Packages/com.unity.render-pipelines.universal/Shaders/Particles/ParticlesLit.shader +++ b/Packages/com.unity.render-pipelines.universal/Shaders/Particles/ParticlesLit.shader @@ -145,6 +145,9 @@ Shader "Universal Render Pipeline/Particles/Lit" #pragma multi_compile_fragment _ _LIGHT_COOKIES #pragma multi_compile _ _CLUSTER_LIGHT_LOOP #pragma multi_compile _ EVALUATE_SH_MIXED EVALUATE_SH_VERTEX +#if defined(UNITY_PLATFORM_META_QUEST) + #pragma multi_compile _ META_QUEST_ORTHO_PROJ +#endif #include_with_pragmas "Packages/com.unity.render-pipelines.core/ShaderLibrary/FoveatedRenderingKeywords.hlsl" #include_with_pragmas "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Fog.hlsl" #include_with_pragmas "Packages/com.unity.render-pipelines.universal/ShaderLibrary/ProbeVolumeVariants.hlsl" diff --git a/Packages/com.unity.render-pipelines.universal/Shaders/Particles/ParticlesSimpleLit.shader b/Packages/com.unity.render-pipelines.universal/Shaders/Particles/ParticlesSimpleLit.shader index b0c05dd22e3..e2d7663e586 100644 --- a/Packages/com.unity.render-pipelines.universal/Shaders/Particles/ParticlesSimpleLit.shader +++ b/Packages/com.unity.render-pipelines.universal/Shaders/Particles/ParticlesSimpleLit.shader @@ -145,6 +145,9 @@ Shader "Universal Render Pipeline/Particles/Simple Lit" #pragma multi_compile_fragment _ _LIGHT_COOKIES #pragma multi_compile _ _CLUSTER_LIGHT_LOOP #pragma multi_compile _ EVALUATE_SH_MIXED EVALUATE_SH_VERTEX +#if defined(UNITY_PLATFORM_META_QUEST) + #pragma multi_compile _ META_QUEST_ORTHO_PROJ +#endif #include_with_pragmas "Packages/com.unity.render-pipelines.core/ShaderLibrary/FoveatedRenderingKeywords.hlsl" #include_with_pragmas "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Fog.hlsl" #include_with_pragmas "Packages/com.unity.render-pipelines.universal/ShaderLibrary/ProbeVolumeVariants.hlsl" diff --git a/Packages/com.unity.render-pipelines.universal/Shaders/SimpleLit.shader b/Packages/com.unity.render-pipelines.universal/Shaders/SimpleLit.shader index 482e89c6eac..572e79676f6 100644 --- a/Packages/com.unity.render-pipelines.universal/Shaders/SimpleLit.shader +++ b/Packages/com.unity.render-pipelines.universal/Shaders/SimpleLit.shader @@ -110,6 +110,9 @@ Shader "Universal Render Pipeline/Simple Lit" #pragma multi_compile _ SHADOWS_SHADOWMASK #pragma multi_compile _ _LIGHT_LAYERS #pragma multi_compile _ _CLUSTER_LIGHT_LOOP +#if defined(UNITY_PLATFORM_META_QUEST) + #pragma multi_compile _ META_QUEST_ORTHO_PROJ +#endif #pragma multi_compile_fragment _ _ADDITIONAL_LIGHT_SHADOWS #pragma multi_compile_fragment _ _SHADOWS_SOFT #pragma multi_compile_fragment _ _SHADOWS_SOFT_LOW _SHADOWS_SOFT_MEDIUM _SHADOWS_SOFT_HIGH diff --git a/Packages/com.unity.render-pipelines.universal/Shaders/Terrain/TerrainLit.shader b/Packages/com.unity.render-pipelines.universal/Shaders/Terrain/TerrainLit.shader index 95cb5fad49d..49cc10ceca4 100644 --- a/Packages/com.unity.render-pipelines.universal/Shaders/Terrain/TerrainLit.shader +++ b/Packages/com.unity.render-pipelines.universal/Shaders/Terrain/TerrainLit.shader @@ -72,6 +72,9 @@ Shader "Universal Render Pipeline/Terrain/Lit" #pragma multi_compile _ _LIGHT_LAYERS #pragma multi_compile _ _CLUSTER_LIGHT_LOOP #pragma multi_compile _ EVALUATE_SH_MIXED EVALUATE_SH_VERTEX +#if defined(UNITY_PLATFORM_META_QUEST) + #pragma multi_compile _ META_QUEST_ORTHO_PROJ +#endif #pragma multi_compile_fragment _ _ADDITIONAL_LIGHT_SHADOWS #pragma multi_compile_fragment _ _REFLECTION_PROBE_BLENDING #pragma multi_compile_fragment _ _REFLECTION_PROBE_ATLAS diff --git a/Packages/com.unity.render-pipelines.universal/Shaders/Terrain/TerrainLitAdd.shader b/Packages/com.unity.render-pipelines.universal/Shaders/Terrain/TerrainLitAdd.shader index 063504bd47b..fbe3a75511d 100644 --- a/Packages/com.unity.render-pipelines.universal/Shaders/Terrain/TerrainLitAdd.shader +++ b/Packages/com.unity.render-pipelines.universal/Shaders/Terrain/TerrainLitAdd.shader @@ -66,6 +66,9 @@ Shader "Hidden/Universal Render Pipeline/Terrain/Lit (Add Pass)" #pragma multi_compile _ SHADOWS_SHADOWMASK #pragma multi_compile _ _LIGHT_LAYERS #pragma multi_compile _ _CLUSTER_LIGHT_LOOP +#if defined(UNITY_PLATFORM_META_QUEST) + #pragma multi_compile _ META_QUEST_ORTHO_PROJ +#endif #pragma multi_compile_fragment _ _ADDITIONAL_LIGHT_SHADOWS #pragma multi_compile_fragment _ _REFLECTION_PROBE_BLENDING #pragma multi_compile_fragment _ _REFLECTION_PROBE_ATLAS diff --git a/Packages/com.unity.render-pipelines.universal/Shaders/Terrain/TerrainLitBase.shader b/Packages/com.unity.render-pipelines.universal/Shaders/Terrain/TerrainLitBase.shader index c92bbb4ab92..ee94536f073 100644 --- a/Packages/com.unity.render-pipelines.universal/Shaders/Terrain/TerrainLitBase.shader +++ b/Packages/com.unity.render-pipelines.universal/Shaders/Terrain/TerrainLitBase.shader @@ -44,6 +44,9 @@ Shader "Hidden/Universal Render Pipeline/Terrain/Lit (Base Pass)" #pragma multi_compile _ SHADOWS_SHADOWMASK #pragma multi_compile _ _LIGHT_LAYERS #pragma multi_compile _ _CLUSTER_LIGHT_LOOP +#if defined(UNITY_PLATFORM_META_QUEST) + #pragma multi_compile _ META_QUEST_ORTHO_PROJ +#endif #pragma multi_compile_fragment _ _ADDITIONAL_LIGHT_SHADOWS #pragma multi_compile_fragment _ _REFLECTION_PROBE_BLENDING #pragma multi_compile_fragment _ _REFLECTION_PROBE_ATLAS diff --git a/Packages/com.unity.render-pipelines.universal/Shaders/Terrain/WavingGrass.shader b/Packages/com.unity.render-pipelines.universal/Shaders/Terrain/WavingGrass.shader index 800b36c96fe..0eb3644d176 100644 --- a/Packages/com.unity.render-pipelines.universal/Shaders/Terrain/WavingGrass.shader +++ b/Packages/com.unity.render-pipelines.universal/Shaders/Terrain/WavingGrass.shader @@ -35,6 +35,9 @@ Shader "Hidden/TerrainEngine/Details/UniversalPipeline/WavingDoublePass" #pragma multi_compile _ _LIGHT_LAYERS #pragma multi_compile _ _CLUSTER_LIGHT_LOOP #pragma multi_compile _ EVALUATE_SH_MIXED EVALUATE_SH_VERTEX +#if defined(UNITY_PLATFORM_META_QUEST) + #pragma multi_compile _ META_QUEST_ORTHO_PROJ +#endif #pragma multi_compile_fragment _ _ADDITIONAL_LIGHT_SHADOWS #pragma multi_compile_fragment _ _SHADOWS_SOFT _SHADOWS_SOFT_LOW _SHADOWS_SOFT_MEDIUM _SHADOWS_SOFT_HIGH #pragma multi_compile_fragment _ _SCREEN_SPACE_OCCLUSION diff --git a/Packages/com.unity.render-pipelines.universal/Shaders/Terrain/WavingGrassBillboard.shader b/Packages/com.unity.render-pipelines.universal/Shaders/Terrain/WavingGrassBillboard.shader index 86f24509d19..b3ff35c24b8 100644 --- a/Packages/com.unity.render-pipelines.universal/Shaders/Terrain/WavingGrassBillboard.shader +++ b/Packages/com.unity.render-pipelines.universal/Shaders/Terrain/WavingGrassBillboard.shader @@ -35,6 +35,9 @@ Shader "Hidden/TerrainEngine/Details/UniversalPipeline/BillboardWavingDoublePass #pragma multi_compile _ LIGHTMAP_SHADOW_MIXING #pragma multi_compile _ SHADOWS_SHADOWMASK #pragma multi_compile _ _CLUSTER_LIGHT_LOOP +#if defined(UNITY_PLATFORM_META_QUEST) + #pragma multi_compile _ META_QUEST_ORTHO_PROJ +#endif #pragma multi_compile_fragment _ _SCREEN_SPACE_IRRADIANCE #include_with_pragmas "Packages/com.unity.render-pipelines.core/ShaderLibrary/FoveatedRenderingKeywords.hlsl" #include_with_pragmas "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Fog.hlsl" diff --git a/Packages/com.unity.render-pipelines.universal/Tests/Editor/ShaderScriptableStripperTests.cs b/Packages/com.unity.render-pipelines.universal/Tests/Editor/ShaderScriptableStripperTests.cs index 8c102078f17..1daee4775bf 100644 --- a/Packages/com.unity.render-pipelines.universal/Tests/Editor/ShaderScriptableStripperTests.cs +++ b/Packages/com.unity.render-pipelines.universal/Tests/Editor/ShaderScriptableStripperTests.cs @@ -29,7 +29,6 @@ internal struct TestStrippingData : IShaderScriptableStrippingData public bool stripUnusedVariants { get; set; } public bool stripUnusedPostProcessingVariants { get; set; } public bool stripUnusedXRVariants { get; set; } - public bool usesDynamicLightmaps { get; set; } public bool IsHDRDisplaySupportEnabled { get; set; } public bool IsRenderCompatibilityMode { get; set; } @@ -227,7 +226,6 @@ public void TestStripUnusedPass(string shaderName) helper.IsFalse(helper.stripper.StripUnusedPass(ref helper.data)); TestStripUnusedPass_2D(shader); - TestStripUnusedPass_Meta(shader); TestStripUnusedPass_XR(shader); TestStripUnusedPass_ShadowCaster(shader); TestStripUnusedPass_Decals(shader); @@ -250,76 +248,6 @@ public void TestStripUnusedPass_2D(Shader shader) helper.IsTrue(helper.stripper.StripUnusedPass(ref helper.data)); } - public void TestStripUnusedPass_Meta(Shader shader) - { - TestHelper helper; - - bool enlightenPrev = SupportedRenderingFeatures.active.enlighten; - LightmapBakeType lightmapBakeTypesPrev = SupportedRenderingFeatures.active.lightmapBakeTypes; - - SupportedRenderingFeatures.active.enlighten = false; - SupportedRenderingFeatures.active.lightmapBakeTypes = LightmapBakeType.Mixed | LightmapBakeType.Baked; - - helper = new TestHelper(shader, ShaderFeatures.None); - helper.data.usesDynamicLightmaps = false; - helper.data.passType = PassType.Meta; - helper.IsTrue(helper.stripper.StripUnusedPass_Meta(ref helper.data)); - helper.IsTrue(helper.stripper.StripUnusedPass(ref helper.data)); - - SupportedRenderingFeatures.active.enlighten = true; - SupportedRenderingFeatures.active.lightmapBakeTypes = LightmapBakeType.Mixed | LightmapBakeType.Baked; - helper = new TestHelper(shader, ShaderFeatures.None); - helper.data.usesDynamicLightmaps = false; - helper.data.passType = PassType.Meta; - helper.IsTrue(helper.stripper.StripUnusedPass_Meta(ref helper.data)); - helper.IsTrue(helper.stripper.StripUnusedPass(ref helper.data)); - - SupportedRenderingFeatures.active.enlighten = false; - SupportedRenderingFeatures.active.lightmapBakeTypes = LightmapBakeType.Realtime | LightmapBakeType.Mixed | LightmapBakeType.Baked; - helper = new TestHelper(shader, ShaderFeatures.None); - helper.data.usesDynamicLightmaps = false; - helper.data.passType = PassType.Meta; - helper.IsTrue(helper.stripper.StripUnusedPass_Meta(ref helper.data)); - helper.IsTrue(helper.stripper.StripUnusedPass(ref helper.data)); - - SupportedRenderingFeatures.active.enlighten = false; - SupportedRenderingFeatures.active.lightmapBakeTypes = LightmapBakeType.Mixed | LightmapBakeType.Baked; - helper = new TestHelper(shader, ShaderFeatures.None); - helper.data.usesDynamicLightmaps = true; - helper.data.passType = PassType.Meta; - helper.IsTrue(helper.stripper.StripUnusedPass_Meta(ref helper.data)); - helper.IsTrue(helper.stripper.StripUnusedPass(ref helper.data)); - - SupportedRenderingFeatures.active.enlighten = true; - SupportedRenderingFeatures.active.lightmapBakeTypes = LightmapBakeType.Realtime | LightmapBakeType.Mixed | LightmapBakeType.Baked; - helper = new TestHelper(shader, ShaderFeatures.None); - helper.data.usesDynamicLightmaps = true; - helper.data.passType = PassType.Meta; - helper.IsFalse(helper.stripper.StripUnusedPass_Meta(ref helper.data)); - helper.IsFalse(helper.stripper.StripUnusedPass(ref helper.data)); - -#if SURFACE_CACHE - SupportedRenderingFeatures.active.enlighten = false; - SupportedRenderingFeatures.active.lightmapBakeTypes = LightmapBakeType.Mixed | LightmapBakeType.Baked; - helper = new TestHelper(shader, ShaderFeatures.SurfaceCache); - helper.data.usesDynamicLightmaps = false; - helper.data.passType = PassType.Meta; - helper.IsFalse(helper.stripper.StripUnusedPass_Meta(ref helper.data)); - helper.IsFalse(helper.stripper.StripUnusedPass(ref helper.data)); - - SupportedRenderingFeatures.active.enlighten = true; - SupportedRenderingFeatures.active.lightmapBakeTypes = LightmapBakeType.Realtime | LightmapBakeType.Mixed | LightmapBakeType.Baked; - helper = new TestHelper(shader, ShaderFeatures.SurfaceCache); - helper.data.usesDynamicLightmaps = true; - helper.data.passType = PassType.Meta; - helper.IsFalse(helper.stripper.StripUnusedPass_Meta(ref helper.data)); - helper.IsFalse(helper.stripper.StripUnusedPass(ref helper.data)); -#endif - - // Restore previous SupportedRenderingFeatures values - SupportedRenderingFeatures.active.enlighten = enlightenPrev; - SupportedRenderingFeatures.active.lightmapBakeTypes = lightmapBakeTypesPrev; - } public void TestStripUnusedPass_XR(Shader shader) { diff --git a/Packages/com.unity.render-pipelines.universal/Tests/Editor/ShaderStripToolTests.cs b/Packages/com.unity.render-pipelines.universal/Tests/Editor/ShaderStripToolTests.cs index 38376cf5ae7..bdc56e6804f 100644 --- a/Packages/com.unity.render-pipelines.universal/Tests/Editor/ShaderStripToolTests.cs +++ b/Packages/com.unity.render-pipelines.universal/Tests/Editor/ShaderStripToolTests.cs @@ -30,7 +30,6 @@ internal struct TestStrippingData : IShaderScriptableStrippingData public bool stripUnusedVariants { get; set; } public bool stripUnusedPostProcessingVariants { get; set; } public bool stripUnusedXRVariants { get; set; } - public bool usesDynamicLightmaps { get; set; } public Shader shader { get; set; } public ShaderType shaderType { get; set; } diff --git a/Packages/com.unity.render-pipelines.universal/Tests/Runtime/RenderGraphConstraintsTests.cs b/Packages/com.unity.render-pipelines.universal/Tests/Runtime/RenderGraphConstraintsTests.cs new file mode 100644 index 00000000000..dbffc717eb8 --- /dev/null +++ b/Packages/com.unity.render-pipelines.universal/Tests/Runtime/RenderGraphConstraintsTests.cs @@ -0,0 +1,372 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Text.RegularExpressions; +using NUnit.Framework; +using UnityEngine.Rendering.RenderGraphModule; +using UnityEngine.TestTools; + +namespace UnityEngine.Rendering.Universal.Tests +{ + [TestFixture] + class RenderGraphConstraintsTests + { + RenderPipelineAsset m_PreviousQualityRenderPipelineAsset; + Type m_PreviousSessionType; + + UniversalRenderPipelineAsset m_UniversalRenderPipelineAsset; + UniversalRendererData m_UniversalRendererData; + OnTileValidationConfiguration m_DefaultOnTileValidationConfiguration; + EntityId m_CameraEntityId; + + static UniversalAdditionalCameraData s_AdditionalCameraData; + static PostProcessData s_PostProcessData; + static HashSet s_RenderingModes; + + readonly HashSet m_ResourceTestNames = new() + { + "_CameraTargetAttachmentA", + "_CameraTargetAttachmentB", + "_CameraTargetAttachment", + "_CameraDepthAttachment", + "_CameraDepthTexture", + "_CameraOpaqueTexture", + "_CameraNormalsTexture", + "_MotionVectorTexture", + "_MotionVectorDepthTexture", + "_CameraRenderingLayersTexture", + "_GBuffer0", + "_GBuffer1", + "_GBuffer2", + "_GBuffer3", + "_GBuffer4", + "_GBuffer5", + "_GBuffer6", + "_DBufferTexture0", + "_DBufferTexture1", + "_DBufferTexture2", + "_ScreenSpaceShadowmapTexture", + "_CameraColorAfterPostProcessing", + "_CameraColorFullScreenPass", + }; + + [OneTimeSetUp] + public void OneTimeSetUp() + { + if (GraphicsSettings.currentRenderPipeline is not UniversalRenderPipelineAsset) + Assert.Ignore($"This test will be only executed when URP is the current pipeline"); + +#if UNITY_EDITOR + if (UnityEditor.EditorWindow.HasOpenInstances()) + UnityEditor.EditorWindow.GetWindow().Close(); +#endif + + m_PreviousSessionType = RenderGraphDebugSession.currentDebugSession?.GetType(); + m_PreviousQualityRenderPipelineAsset = QualitySettings.renderPipeline; + + RenderGraphDebugSession.Create(); + + // Create new URP asset + m_UniversalRenderPipelineAsset = Create(); + QualitySettings.renderPipeline = m_UniversalRenderPipelineAsset; + Assume.That(m_UniversalRenderPipelineAsset.rendererDataList != null && m_UniversalRenderPipelineAsset.rendererDataList.Length > 0); + + // Get URP Renderer Data + m_UniversalRendererData = m_UniversalRenderPipelineAsset.rendererDataList[0] as UniversalRendererData; + Assume.That(m_UniversalRendererData != null); + + m_DefaultOnTileValidationConfiguration = new OnTileValidationConfiguration(); + + var gameObject = new GameObject(); + var camera = gameObject.AddComponent(); + m_CameraEntityId = camera.GetEntityId(); + s_AdditionalCameraData = gameObject.AddComponent(); + s_AdditionalCameraData.renderPostProcessing = true; + s_AdditionalCameraData.SetRenderer(0); + } + + [UnitySetUp] + public IEnumerator SetUp() + { + m_DefaultOnTileValidationConfiguration.ApplyToAssetAndRenderer(m_UniversalRenderPipelineAsset, m_UniversalRendererData); + + yield return null; + + if (!RenderGraphDebugSession.GetRegisteredGraphs().Contains("URPRenderGraph")) + Assert.Ignore("Ignore this test as we couldn't setup debug session for URPRenderGraph."); + } + + UniversalRenderPipelineAsset Create() + { + // Create Universal RP Asset + var instance = ScriptableObject.CreateInstance(); + + // Initialize default renderer data + instance.m_RendererDataList[0] = ScriptableObject.CreateInstance(); + + // Setup PP from existing urp asset + s_RenderingModes = new HashSet(); + + ProcessRenderPipelineAsset(GraphicsSettings.defaultRenderPipeline, instance); + for (int i = 0; i < QualitySettings.count; i++) + ProcessRenderPipelineAsset(QualitySettings.GetRenderPipelineAssetAt(i), instance); + + return instance; + } + + static void ProcessRenderPipelineAsset(RenderPipelineAsset rpAsset, UniversalRenderPipelineAsset instance) + { + if (rpAsset == null || rpAsset is not UniversalRenderPipelineAsset urpAsset) + return; + + foreach (var existingRendererData in urpAsset.rendererDataList) + { + if (existingRendererData == null || existingRendererData is not UniversalRendererData urpRendererData) + continue; + + if (urpRendererData.postProcessData != null && s_PostProcessData == null) + { + s_PostProcessData = urpRendererData.postProcessData; + ((UniversalRendererData)instance.m_RendererDataList[0]).postProcessData = + urpRendererData.postProcessData; + } + + s_RenderingModes.Add(urpRendererData.renderingMode); + } + } + + [OneTimeTearDown] + public void OneTimeTearDown() + { + if (m_PreviousSessionType != null) + RenderGraphDebugSession.Create(m_PreviousSessionType); + else + RenderGraphDebugSession.EndSession(); + + QualitySettings.renderPipeline = m_PreviousQualityRenderPipelineAsset; + + Object.DestroyImmediate(m_UniversalRenderPipelineAsset); + Object.DestroyImmediate(m_UniversalRendererData); + Object.DestroyImmediate(s_AdditionalCameraData.gameObject); + } + + public class OnTileValidationConfiguration + { + public bool supportsCameraOpaqueTexture { get; set; } = false; + public bool supportsCameraDepthTexture { get; set; } = false; + public bool supportsHDR { get; set; } = false; + public int msaaSampleCount { get; set; } = 1; + public RenderingMode renderingMode { get; set; } = RenderingMode.Forward; + public bool occlusionCulling { get; set; } = false; + public float renderScale { get; set; } = 1.0f; + public bool postProcessingEnabled { get; set; } = false; + public bool onTileValidation { get; set; } = false; + + public List rendererFeatures { get; set; } = new(); + + bool TryApplyToAssetAndRenderer(UniversalRenderPipelineAsset asset, UniversalRendererData rendererData) + { + // Apply asset settings + asset.supportsCameraDepthTexture = supportsCameraDepthTexture; + asset.supportsCameraOpaqueTexture = supportsCameraOpaqueTexture; + asset.supportsHDR = supportsHDR; + asset.msaaSampleCount = msaaSampleCount; + asset.renderScale = renderScale; + if (occlusionCulling) + { + if (!Application.isEditor) + return false; +#if UNITY_EDITOR + if (UnityEditor.Rendering.EditorGraphicsSettings.batchRendererGroupShaderStrippingMode != + UnityEditor.Rendering.BatchRendererGroupStrippingMode.KeepAll) + return false; +#endif + asset.gpuResidentDrawerMode = GPUResidentDrawerMode.InstancedDrawing; + asset.gpuResidentDrawerEnableOcclusionCullingInCameras = occlusionCulling; + renderingMode = RenderingMode.ForwardPlus; + } + + // Apply renderer settings + rendererData.onTileValidation = onTileValidation; + + if (renderingMode != RenderingMode.Forward && !s_RenderingModes.Contains(renderingMode)) + return false; + + rendererData.renderingMode = renderingMode; + + if (postProcessingEnabled && s_PostProcessData == null) + return false; + + rendererData.postProcessData = s_PostProcessData != null ? s_PostProcessData : null; + + // Clear + rendererData.rendererFeatures.Clear(); + foreach (var feature in rendererFeatures) + rendererData.rendererFeatures.Add(feature); + return true; + } + + public void ApplyToAssetAndRenderer(UniversalRenderPipelineAsset asset, UniversalRendererData rendererData) + { + if (!TryApplyToAssetAndRenderer(asset, rendererData)) + Assert.Ignore("We can't run this test for On Tile Validation because some resources could be missing."); + } + } + + static TestCaseData[] s_OnTileValidationCases = + { + new TestCaseData(new OnTileValidationConfiguration { supportsCameraOpaqueTexture = true }) + .SetName("Camera Opaque Texture on Universal Render Pipeline Asset.").Returns(null), + new TestCaseData(new OnTileValidationConfiguration { supportsCameraDepthTexture = true }) + .SetName("Camera Depth Texture on Universal Render Pipeline Asset.").Returns(null), + new TestCaseData(new OnTileValidationConfiguration { supportsHDR = true }) + .SetName("Support HDR on Universal Render Pipeline Asset.").Returns(null), + new TestCaseData(new OnTileValidationConfiguration { msaaSampleCount = 4 }) + .SetName("MSAA Samples > 1 on Universal Render Pipeline Asset.").Returns(null), + new TestCaseData(new OnTileValidationConfiguration { occlusionCulling = true }) + .SetName("GPU Occlusion Culling on Universal Render Pipeline Asset.").Returns(null), + new TestCaseData(new OnTileValidationConfiguration { renderingMode = RenderingMode.Deferred }) + .SetName("Deferred Rendering Mode on Universal Renderer.").Returns(null), + new TestCaseData(new OnTileValidationConfiguration { renderingMode = RenderingMode.DeferredPlus }) + .SetName("Deferred Plus Rendering Mode on Universal Renderer.").Returns(null), + new TestCaseData(new OnTileValidationConfiguration { renderScale = 0.65f }) + .SetName("Render Scale on Universal Renderer.").Returns(null), + new TestCaseData(new OnTileValidationConfiguration { postProcessingEnabled = true }) + .SetName("Post Processing on Universal Renderer.").Returns(null), + }; + + [UnityTest] + [TestCaseSource(nameof(s_OnTileValidationCases))] + public IEnumerator URPSettingsShouldNotCauseNonMemorylessTargets(OnTileValidationConfiguration config) + { + // Arrange + LogAssert.ignoreFailingMessages = true; + config.ApplyToAssetAndRenderer(m_UniversalRenderPipelineAsset, m_UniversalRendererData); + + // Act + m_UniversalRendererData.onTileValidation = true; + + yield return null; + + Assert.That(OnlyBackbufferOrMemoryless(outputNonMemoryless: true), Is.True, "Non-memoryless intermediate targets were produced."); + LogAssert.ignoreFailingMessages = false; + } + + [UnityTest] + public IEnumerator AnyScriptableRendererFeatureProduceAnException() + { + // Arrange + var config = new OnTileValidationConfiguration(); + config.rendererFeatures.Add(ScriptableObject.CreateInstance()); + config.ApplyToAssetAndRenderer(m_UniversalRenderPipelineAsset, m_UniversalRendererData); + + yield return null; + + // Act + LogAssert.Expect(LogType.Error, new Regex("Render Graph Execution error")); + LogAssert.Expect(LogType.Exception, new Regex("ArgumentException")); + + m_UniversalRendererData.onTileValidation = true; + yield return null; + } + + [UnityTest] + public IEnumerator CameraStackingProduceAnException() + { + // Arrange + var gameObject = new GameObject(); + var camera = gameObject.AddComponent(); + var additionalCameraData = gameObject.AddComponent(); + additionalCameraData.renderType = CameraRenderType.Overlay; + + Assume.That(s_AdditionalCameraData.TryAddCameraToStack(camera), "Couldn't add camera to stack."); + yield return null; + + Assume.That(OnlyBackbufferOrMemoryless(), Is.False, "Intermediate Targets expected without On Tile Validation."); + + yield return null; + + // Act + LogAssert.Expect(LogType.Exception, new Regex("ArgumentException")); + + m_UniversalRendererData.onTileValidation = true; + yield return null; + + m_UniversalRendererData.onTileValidation = false; + ((UniversalRenderer)additionalCameraData.scriptableRenderer).onTileValidation = true; + + LogAssert.Expect(LogType.Exception, new Regex("ArgumentException")); + + yield return null; + + Object.DestroyImmediate(gameObject); + s_AdditionalCameraData.UpdateCameraStack(); + } + + + bool OnlyBackbufferOrMemoryless(bool outputNonMemoryless = false) + { + var onlyMemoryless = true; + var debugData = RenderGraphDebugSession.GetDebugData("URPRenderGraph", m_CameraEntityId); + var textureList = debugData.resourceLists[(int)RenderGraphResourceType.Texture]; + for (int i = 0; i < textureList.Count; i++) + { + var resource = textureList[i]; + if (resource is { releasePassIndex: -1, creationPassIndex: -1 }) + continue; + + if (!IsCameraTargetAttachment(resource.name) || resource.memoryless) + continue; + + if (!outputNonMemoryless) + return false; + + Debug.Log($"{resource.name} isn't memoryless."); + onlyMemoryless = false; + } + + return onlyMemoryless; + } + + bool IsCameraTargetAttachment(string name) + { + return m_ResourceTestNames.Contains(name); + } + + class TestRenderGraphDebugSession : RenderGraphDebugSession + { + public override bool isActive => true; + + public TestRenderGraphDebugSession() + { + RegisterAllLocallyKnownGraphsAndExecutions(); + } + } + + class TestScriptableRendererFeature : ScriptableRendererFeature + { + TestScriptableRendererPass m_TestScriptableRendererPass; + + public override void Create() + { + m_TestScriptableRendererPass = new TestScriptableRendererPass(); + } + + public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData) + { + renderer.EnqueuePass(m_TestScriptableRendererPass); + } + } + + public class TestPass { } + + class TestScriptableRendererPass : ScriptableRenderPass + { + public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData) + { + using var builder = renderGraph.AddRasterRenderPass("Test Scriptable Renderer Pass", out _); + builder.SetRenderFunc(static (TestPass _, RasterGraphContext _) => { }); + } + } + } +} diff --git a/Packages/com.unity.render-pipelines.universal/Tests/Runtime/RenderGraphConstraintsTests.cs.meta b/Packages/com.unity.render-pipelines.universal/Tests/Runtime/RenderGraphConstraintsTests.cs.meta new file mode 100644 index 00000000000..4f0b07be40f --- /dev/null +++ b/Packages/com.unity.render-pipelines.universal/Tests/Runtime/RenderGraphConstraintsTests.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 993d9a6b07bf33c4dbf1f13f5879bd34 \ No newline at end of file diff --git a/Packages/com.unity.render-pipelines.universal/Tests/Runtime/Unity.RenderPipelines.Universal.Runtime.Tests.asmdef b/Packages/com.unity.render-pipelines.universal/Tests/Runtime/Unity.RenderPipelines.Universal.Runtime.Tests.asmdef index 502762ff501..10db3883844 100644 --- a/Packages/com.unity.render-pipelines.universal/Tests/Runtime/Unity.RenderPipelines.Universal.Runtime.Tests.asmdef +++ b/Packages/com.unity.render-pipelines.universal/Tests/Runtime/Unity.RenderPipelines.Universal.Runtime.Tests.asmdef @@ -8,7 +8,9 @@ "GUID:516a5277b8c3b4f4c8cc86b77b1591ff", "GUID:d8b63aba1907145bea998dd612889d6b", "GUID:df380645f10b7bc4b97d4f5eb6303d95", - "GUID:41524c21c95e5fe4dbc5b48bd21995a4" + "GUID:41524c21c95e5fe4dbc5b48bd21995a4", + "GUID:4fd6538c1c56b409fb53fdf0183170ec", + "GUID:3eae0364be2026648bf74846acb8a731" ], "includePlatforms": [], "excludePlatforms": [], diff --git a/Packages/com.unity.shadergraph/Documentation~/Custom-Render-Texture-Nodes.md b/Packages/com.unity.shadergraph/Documentation~/Custom-Render-Texture-Nodes.md index 2ae0b3724bd..34a482954f0 100644 --- a/Packages/com.unity.shadergraph/Documentation~/Custom-Render-Texture-Nodes.md +++ b/Packages/com.unity.shadergraph/Documentation~/Custom-Render-Texture-Nodes.md @@ -4,6 +4,6 @@ Access properties and data of custom render textures, including size, slice inde | **Topic** | **Description** | |--------------------------------------------------------|----------------------------------------------------------------| -| [Custom Render Texture Slice](Custom-Texture-Slice.md) | Access the custom render texture slice index and cubemap face. | -| [Custom Render Texture Size](Custom-Texture-Size.md) | Access the custom render texture size. | -| [Custom Render Texture Self](Custom-Texture-Self.md) | Access the custom render texture from the previous update. | +| [Custom Render Texture Self](Custom-Render-Texture-Self-Node.md) | Access the custom render texture from the previous update. | +| [Custom Render Texture Size](Custom-Render-Texture-Size-Node.md) | Access the custom render texture size. | +| [Slice Index / Cubemap Face](Slice-Index-Cubemap-Face-Node.md) | Access the custom render texture slice index and cubemap face. | diff --git a/Packages/com.unity.shadergraph/Documentation~/TableOfContents.md b/Packages/com.unity.shadergraph/Documentation~/TableOfContents.md index 26a6227d29c..144b17054f4 100644 --- a/Packages/com.unity.shadergraph/Documentation~/TableOfContents.md +++ b/Packages/com.unity.shadergraph/Documentation~/TableOfContents.md @@ -90,7 +90,7 @@ * [Custom Render Texture](Custom-Render-Texture-Nodes.md) * [Self](Custom-Render-Texture-Self-Node.md) * [Size](Custom-Render-Texture-Size-Node.md) - * [Slice](Slice-Index-Cubemap-Face-Node.md) + * [Slice Index / Cubemap Face](Slice-Index-Cubemap-Face-Node.md) * [Dropdown](Dropdown-Node.md) * [Input](Input-Nodes.md) * Basic @@ -131,7 +131,7 @@ * Lighting * [Ambient](Ambient-Node.md) * [Baked GI](Baked-GI-Node.md) - * [Main Light Direction](https://docs.unity3d.com/Packages/com.unity.shadergraph@13.1/manual/Main-Light-Direction-Node.html) + * [Main Light Direction](Main-Light-Direction-Node.md) * [Reflection Probe](Reflection-Probe-Node.md) * Matrix * [Matrix 2x2](Matrix-2x2-Node.md) diff --git a/Packages/com.unity.shadergraph/Documentation~/template-browser.md b/Packages/com.unity.shadergraph/Documentation~/template-browser.md index db9769b3d2c..ae6a01d2670 100644 --- a/Packages/com.unity.shadergraph/Documentation~/template-browser.md +++ b/Packages/com.unity.shadergraph/Documentation~/template-browser.md @@ -33,4 +33,4 @@ To create a custom shader graph template, follow these steps: ## Additional resources -* [Create a new shader graph from a template](create-shader-graph-template.md) +* [Create a new shader graph](Create-Shader-Graph.md) diff --git a/Packages/com.unity.shadergraph/Editor/Data/Nodes/Artistic/Adjustment/HueNode.cs b/Packages/com.unity.shadergraph/Editor/Data/Nodes/Artistic/Adjustment/HueNode.cs index e1c7fddc74a..7a6dd189911 100644 --- a/Packages/com.unity.shadergraph/Editor/Data/Nodes/Artistic/Adjustment/HueNode.cs +++ b/Packages/com.unity.shadergraph/Editor/Data/Nodes/Artistic/Adjustment/HueNode.cs @@ -61,7 +61,7 @@ static string Unity_Hue_Degrees( $precision4 P = lerp($precision4(In.bg, K.wz), $precision4(In.gb, K.xy), step(In.b, In.g)); $precision4 Q = lerp($precision4(P.xyw, In.r), $precision4(In.r, P.yzx), step(P.x, In.r)); $precision D = Q.x - min(Q.w, Q.y); - $precision E = 1e-10; + $precision E = 1e-4; $precision V = (D == 0) ? Q.x : (Q.x + E); $precision3 hsv = $precision3(abs(Q.z + (Q.w - Q.y)/(6.0 * D + E)), D / (Q.x + E), V); @@ -93,7 +93,7 @@ static string Unity_Hue_Normalized( $precision4 P = lerp($precision4(In.bg, K.wz), $precision4(In.gb, K.xy), step(In.b, In.g)); $precision4 Q = lerp($precision4(P.xyw, In.r), $precision4(In.r, P.yzx), step(P.x, In.r)); $precision D = Q.x - min(Q.w, Q.y); - $precision E = 1e-10; + $precision E = 1e-4; $precision V = (D == 0) ? Q.x : (Q.x + E); $precision3 hsv = $precision3(abs(Q.z + (Q.w - Q.y)/(6.0 * D + E)), D / (Q.x + E), V); diff --git a/Packages/com.unity.shadergraph/Editor/Drawing/Colors/ShaderGraphHeatmapValues.cs b/Packages/com.unity.shadergraph/Editor/Drawing/Colors/ShaderGraphHeatmapValues.cs index 1867f956536..387dde52b9e 100644 --- a/Packages/com.unity.shadergraph/Editor/Drawing/Colors/ShaderGraphHeatmapValues.cs +++ b/Packages/com.unity.shadergraph/Editor/Drawing/Colors/ShaderGraphHeatmapValues.cs @@ -46,6 +46,7 @@ public bool TryGetCategory(string nodeTypeName, out int heatValue) } } + [CoreRPHelpURL("index","com.unity.shadergraph")] class ShaderGraphHeatmapValues : ScriptableObject { const string k_PackageDefaultsPath = "Packages/com.unity.shadergraph/Editor/Resources/DefaultHeatmapValues.asset"; diff --git a/Packages/com.unity.shadergraph/Editor/Drawing/Views/GraphEditorView.cs b/Packages/com.unity.shadergraph/Editor/Drawing/Views/GraphEditorView.cs index eacf96e3fbc..3aa71e400b2 100644 --- a/Packages/com.unity.shadergraph/Editor/Drawing/Views/GraphEditorView.cs +++ b/Packages/com.unity.shadergraph/Editor/Drawing/Views/GraphEditorView.cs @@ -153,6 +153,42 @@ void InstallSample(string sampleName) private static readonly ProfilerMarker AddGroupsMarker = new ProfilerMarker("AddGroups"); private static readonly ProfilerMarker AddStickyNotesMarker = new ProfilerMarker("AddStickyNotes"); + + static GUIContent saveIcon; + static GUIContent SaveIcon => + saveIcon ??= new GUIContent(EditorGUIUtility.IconContent("SaveActive").image, "Save"); + + static GUIContent dropdownIcon; + static GUIContent DropdownIcon => + dropdownIcon ??= EditorGUIUtility.IconContent("Dropdown"); + + static GUIContent blackboardIcon; + static GUIContent BlackboardIcon + { + get + { + if (blackboardIcon == null) + { + var suffix = (EditorGUIUtility.isProSkin ? "_dark" : "") + (EditorGUIUtility.pixelsPerPoint >= 2 ? "@2x" : ""); + var path = $"Icons/blackboard{suffix}"; + blackboardIcon = new GUIContent(Resources.Load(path), "Blackboard"); + } + return blackboardIcon; + } + } + + static GUIContent inspectorIcon; + static GUIContent InspectorIcon => + inspectorIcon ??= new GUIContent(EditorGUIUtility.IconContent("UnityEditor.InspectorWindow").image, "Graph Inspector"); + + static GUIContent previewIcon; + static GUIContent PreviewIcon => + previewIcon ??= new GUIContent(EditorGUIUtility.IconContent("PreMatSphere").image, "Main Preview"); + + static GUIContent helpIcon; + static GUIContent HelpIcon => + helpIcon ??= new GUIContent(EditorGUIUtility.IconContent("_Help").image, "Open Shader Graph User Manual"); + public GraphEditorView(EditorWindow editorWindow, GraphData graph, MessageManager messageManager, string graphName) { m_GraphViewGroupTitleChanged = OnGroupTitleChanged; @@ -172,7 +208,6 @@ public GraphEditorView(EditorWindow editorWindow, GraphData graph, MessageManage m_UserViewSettings = JsonUtility.FromJson(serializedSettings) ?? new UserViewSettings(); m_ColorManager = new ColorManager(m_UserViewSettings.colorProvider); - List toolbarExtensions = new(); foreach (var type in TypeCache.GetTypesDerivedFrom(typeof(IShaderGraphToolbarExtension)).Where(e => !e.IsGenericType)) { @@ -183,12 +218,12 @@ public GraphEditorView(EditorWindow editorWindow, GraphData graph, MessageManage var toolbar = new IMGUIContainer(() => { GUILayout.BeginHorizontal(EditorStyles.toolbar); - if (GUILayout.Button(new GUIContent(EditorGUIUtility.FindTexture("SaveActive"), "Save"), EditorStyles.toolbarButton)) + if (GUILayout.Button(SaveIcon, EditorStyles.toolbarButton)) { if (saveRequested != null) saveRequested(); } - if (GUILayout.Button(EditorResources.Load("d_dropdown"), EditorStyles.toolbarButton)) + if (GUILayout.Button(DropdownIcon, EditorStyles.toolbarButton)) { GenericMenu menu = new GenericMenu(); menu.AddItem(new GUIContent("Save As..."), false, () => saveAsRequested()); @@ -218,22 +253,22 @@ public GraphEditorView(EditorWindow editorWindow, GraphData graph, MessageManage GUILayout.Label("Color Mode"); var newColorIndex = EditorGUILayout.Popup(m_ColorManager.activeIndex, colorProviders, GUILayout.Width(100f)); GUILayout.Space(4); - m_UserViewSettings.isBlackboardVisible = GUILayout.Toggle(m_UserViewSettings.isBlackboardVisible, new GUIContent(Resources.Load("Icons/blackboard"), "Blackboard"), EditorStyles.toolbarButton); + + m_UserViewSettings.isBlackboardVisible = GUILayout.Toggle(m_UserViewSettings.isBlackboardVisible, BlackboardIcon, EditorStyles.toolbarButton); GUILayout.Space(6); - m_UserViewSettings.isInspectorVisible = GUILayout.Toggle(m_UserViewSettings.isInspectorVisible, new GUIContent(EditorGUIUtility.TrIconContent("d_UnityEditor.InspectorWindow").image, "Graph Inspector"), EditorStyles.toolbarButton); + m_UserViewSettings.isInspectorVisible = GUILayout.Toggle(m_UserViewSettings.isInspectorVisible, InspectorIcon, EditorStyles.toolbarButton); GUILayout.Space(6); - m_UserViewSettings.isPreviewVisible = GUILayout.Toggle(m_UserViewSettings.isPreviewVisible, new GUIContent(EditorGUIUtility.FindTexture("PreMatSphere"), "Main Preview"), EditorStyles.toolbarButton); + m_UserViewSettings.isPreviewVisible = GUILayout.Toggle(m_UserViewSettings.isPreviewVisible, PreviewIcon, EditorStyles.toolbarButton); - if (GUILayout.Button(new GUIContent(EditorGUIUtility.TrIconContent("_Help").image, "Open Shader Graph User Manual"), EditorStyles.toolbarButton)) + if (GUILayout.Button(HelpIcon, EditorStyles.toolbarButton)) { Application.OpenURL(UnityEngine.Rendering.ShaderGraph.Documentation.GetPageLink("index")); - //Application.OpenURL("https://docs.unity3d.com/Packages/com.unity.shadergraph@17.0/manual/index.html"); // TODO : point to latest? } - if (GUILayout.Button(EditorResources.Load("d_dropdown"), EditorStyles.toolbarButton)) + if (GUILayout.Button(DropdownIcon, EditorStyles.toolbarButton)) { GenericMenu menu = new GenericMenu(); menu.AddItem(new GUIContent("Shader Graph Samples"), false, () => @@ -257,10 +292,6 @@ public GraphEditorView(EditorWindow editorWindow, GraphData graph, MessageManage { Application.OpenURL("https://discussions.unity.com/tag/Shader-Graph"); }); - menu.AddItem(new GUIContent("Shader Graph Roadmap"), false, () => - { - Application.OpenURL("https://portal.productboard.com/unity/1-unity-platform-rendering-visual-effects/tabs/7-shader-graph"); - }); menu.ShowAsContext(); } diff --git a/Packages/com.unity.shadergraph/Editor/Drawing/Views/MaterialGraphView.cs b/Packages/com.unity.shadergraph/Editor/Drawing/Views/MaterialGraphView.cs index 0ce96188f94..20ccafd8706 100644 --- a/Packages/com.unity.shadergraph/Editor/Drawing/Views/MaterialGraphView.cs +++ b/Packages/com.unity.shadergraph/Editor/Drawing/Views/MaterialGraphView.cs @@ -656,15 +656,6 @@ private void InitializeViewSubMenu(ContextualMenuPopulateEvent evt) void ChangeCustomNodeColor(DropdownMenuAction menuAction) { - // Color Picker is internal :( - var t = typeof(EditorWindow).Assembly.GetTypes().FirstOrDefault(ty => ty.Name == "ColorPicker"); - var m = t?.GetMethod("Show", new[] { typeof(Action), typeof(Color), typeof(bool), typeof(bool) }); - if (m == null) - { - Debug.LogWarning("Could not invoke Color Picker for ShaderGraph."); - return; - } - var editorView = GetFirstAncestorOfType(); var defaultColor = Color.gray; if (selection.FirstOrDefault(sel => sel is MaterialNodeView) is MaterialNodeView selNode1) @@ -686,7 +677,7 @@ void ApplyColor(Color pickedColor) } graph.owner.RegisterCompleteObjectUndo("Change Node Color"); - m.Invoke(null, new object[] { (Action)ApplyColor, defaultColor, true, false }); + ColorPicker.Show(ApplyColor, defaultColor, true, false); } protected internal override bool canDeleteSelection diff --git a/Packages/com.unity.shadergraph/Editor/Importers/RenderPipelineChangedCallback.cs b/Packages/com.unity.shadergraph/Editor/Importers/RenderPipelineChangedCallback.cs index 6edcf31d089..fedb7e0f677 100644 --- a/Packages/com.unity.shadergraph/Editor/Importers/RenderPipelineChangedCallback.cs +++ b/Packages/com.unity.shadergraph/Editor/Importers/RenderPipelineChangedCallback.cs @@ -12,6 +12,7 @@ private static void RegisterSRPChangeCallback() { RenderPipelineManager.activeRenderPipelineTypeChanged -= SRPChanged; RenderPipelineManager.activeRenderPipelineTypeChanged += SRPChanged; + SRPChanged(); } static Hash128 ComputeCurrentRenderPipelineHash() diff --git a/Packages/com.unity.shadergraph/Editor/Importers/ShaderGraphImporterEditor.cs b/Packages/com.unity.shadergraph/Editor/Importers/ShaderGraphImporterEditor.cs index a8affe50e5e..f16a0d85771 100644 --- a/Packages/com.unity.shadergraph/Editor/Importers/ShaderGraphImporterEditor.cs +++ b/Packages/com.unity.shadergraph/Editor/Importers/ShaderGraphImporterEditor.cs @@ -20,6 +20,8 @@ class ShaderGraphImporterEditor : ScriptedImporterEditor { protected override bool needsApplyRevert => false; MaterialEditor materialEditor = null; + bool needsSaveMetaFile = false; + bool needsReimport = false; public override void OnInspectorGUI() { @@ -138,7 +140,7 @@ GraphData GetGraphData(AssetImporter importer) EditorGUILayout.Space(); EditorGUI.BeginChangeCheck(); EditorGUILayout.PropertyField(serializedObject.FindProperty(ShaderGraphImporter.UseAsTemplateFieldName)); - bool needsReimport = EditorGUI.EndChangeCheck(); + needsReimport |= EditorGUI.EndChangeCheck(); using (new EditorGUI.IndentLevelScope(1)) using (new EditorGUI.DisabledScope(!(target as ShaderGraphImporter)?.UseAsTemplate ?? true)) { @@ -146,15 +148,12 @@ GraphData GetGraphData(AssetImporter importer) EditorGUILayout.PropertyField(serializedObject.FindProperty(ShaderGraphImporter.ExposeTemplateAsShaderFieldName), new GUIContent("Expose as Shader", "Toggle whether or not the template shader should be exposed in shader dropdowns.")); needsReimport |= EditorGUI.EndChangeCheck(); + EditorGUI.BeginChangeCheck(); EditorGUILayout.PropertyField(serializedObject.FindProperty(ShaderGraphImporter.TemplateFieldName)); + needsSaveMetaFile |= EditorGUI.EndChangeCheck(); } ApplyRevertGUI(); - if (needsReimport) - { - AssetImporter importer = target as AssetImporter; - AssetDatabase.ImportAsset(importer.assetPath); - } if (materialEditor) { @@ -165,6 +164,22 @@ GraphData GetGraphData(AssetImporter importer) } } + protected override void Apply() + { + base.Apply(); + var importer = (AssetImporter)target; + if (needsSaveMetaFile) + { + AssetDatabase.ForceReserializeAssets(new []{ importer.assetPath }, ForceReserializeAssetsOptions.ReserializeMetadata); + } + if (needsReimport) + { + AssetDatabase.ImportAsset(importer.assetPath); + } + needsSaveMetaFile = false; + needsReimport = false; + } + public override void OnEnable() { base.OnEnable(); diff --git a/Packages/com.unity.shadergraph/Editor/Importers/ShaderGraphIndexerExtension.cs b/Packages/com.unity.shadergraph/Editor/Importers/ShaderGraphIndexerExtension.cs new file mode 100644 index 00000000000..df7420bbe44 --- /dev/null +++ b/Packages/com.unity.shadergraph/Editor/Importers/ShaderGraphIndexerExtension.cs @@ -0,0 +1,18 @@ +using UnityEditor.Experimental.GraphView; +using UnityEditor.Search; +using UnityEngine; + +namespace UnityEditor.ShaderGraph +{ + static class ShaderGraphIndexerExtension + { + [CustomObjectIndexer(typeof(Shader), version = 0)] + internal static void ShaderGraphImporterIndexer(CustomObjectIndexerTarget context, ObjectIndexer indexer) + { + if (ShaderGraphTemplateHelper.TryGetTemplateStatic(context.id, out var template)) + { + GraphViewIndexerExtension.IndexCommonData(context, indexer, template); + } + } + } +} diff --git a/Packages/com.unity.shadergraph/Editor/Importers/ShaderGraphIndexerExtension.cs.meta b/Packages/com.unity.shadergraph/Editor/Importers/ShaderGraphIndexerExtension.cs.meta new file mode 100644 index 00000000000..27a5c075ed8 --- /dev/null +++ b/Packages/com.unity.shadergraph/Editor/Importers/ShaderGraphIndexerExtension.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 1da453bbfb3b450fa1b226dbffebffbb +timeCreated: 1763474542 \ No newline at end of file diff --git a/Packages/com.unity.shadergraph/Editor/Importers/ShaderGraphTemplateHelper.cs b/Packages/com.unity.shadergraph/Editor/Importers/ShaderGraphTemplateHelper.cs index 0ad2212d83f..2fdb7e38184 100644 --- a/Packages/com.unity.shadergraph/Editor/Importers/ShaderGraphTemplateHelper.cs +++ b/Packages/com.unity.shadergraph/Editor/Importers/ShaderGraphTemplateHelper.cs @@ -1,5 +1,9 @@ +using System; using System.IO; + using UnityEditor.Experimental.GraphView; +using UnityEditor.Search; +using UnityEngine; using UnityEngine.Rendering.ShaderGraph; namespace UnityEditor.ShaderGraph @@ -17,20 +21,20 @@ public string OpenSaveFileDialog() } } + public static string ShaderGraphToolKey => "ShaderGraph"; + + public string toolKey => ShaderGraphToolKey; public string packageInfoName => "Shader Graph"; public string learningSampleName => string.Empty; public string templateWindowDocUrl => Documentation.GetPageLink("index"); public string builtInTemplatePath => k_BuiltInTemplatePath; public string builtInCategory => "Default Shader Graph Templates"; - public string assetType => "Shader"; - public string emptyTemplateName => "Empty Shader Graph"; - public string emptyTemplateDescription => "Create a completely empty Shader Graph asset"; - public string lastSelectedGuidKey => "ShaderGraphTemplateWindow.LastSelectedGuid"; + public Type assetType => typeof(Shader); public string createNewAssetTitle => "Create new Shader Graph Asset"; public string insertTemplateTitle => "Insert a template into current Shader Graph Asset"; - public string emptyTemplateIconPath => "Packages/com.unity.shadergraph/Editor/Resources/Icons/sg_graph_icon@2x.png"; - public string emptyTemplateScreenshotPath => ""; - public string customTemplateIcon => emptyTemplateIconPath; + public string emptyTemplateGuid => null; + public string customTemplateIcon => "Packages/com.unity.shadergraph/Editor/Resources/Icons/sg_graph_icon@2x.png"; + public bool showPackageIndexingBanner { get; set; } = true; public GraphViewTemplateWindow.ISaveFileDialogHelper saveFileDialogHelper { get; set; } = new SaveFileDialog(); @@ -39,20 +43,18 @@ public void RaiseImportSampleDependencies(PackageManager.PackageInfo packageInfo public void RaiseTemplateUsed(GraphViewTemplateDescriptor usedTemplate) => ShaderGraphAnalytics.SendShaderGraphTemplateEvent(usedTemplate); - public bool TryGetTemplate(string assetPath, out GraphViewTemplateDescriptor graphViewTemplate) + public bool TryGetTemplate(string assetPath, out GraphViewTemplateDescriptor graphViewTemplate) => TryGetTemplateStatic(assetPath, out graphViewTemplate); + internal static bool TryGetTemplateStatic(string assetPath, out GraphViewTemplateDescriptor graphViewTemplate) { if (FileUtilities.TryGetImporter(assetPath, out var importer)) { var template = importer.Template; if (importer.UseAsTemplate) { - var templateName = !string.IsNullOrEmpty(template.name) ? template.name : Path.GetFileNameWithoutExtension(assetPath); - var templateCategory = !string.IsNullOrEmpty(template.category) ? template.category : "uncategorized"; - - graphViewTemplate = new GraphViewTemplateDescriptor + graphViewTemplate = new GraphViewTemplateDescriptor(ShaderGraphToolKey) { - name = templateName, - category = templateCategory, + name = template.name, + category = template.category, description = template.description, icon = template.icon, thumbnail = template.thumbnail, @@ -83,5 +85,8 @@ public bool TrySetTemplate(string assetPath, GraphViewTemplateDescriptor graphVi } return false; } + + public ITemplateSorter[] GetTemplateSorter() => Array.Empty(); + public SearchProposition[] GetSearchPropositions() => Array.Empty(); } } diff --git a/Packages/com.unity.shadergraph/Editor/Resources/Icons/blackboard.png b/Packages/com.unity.shadergraph/Editor/Resources/Icons/blackboard.png index a1890e476f6..ef9cf24ebf2 100644 Binary files a/Packages/com.unity.shadergraph/Editor/Resources/Icons/blackboard.png and b/Packages/com.unity.shadergraph/Editor/Resources/Icons/blackboard.png differ diff --git a/Packages/com.unity.shadergraph/Editor/Resources/Icons/blackboard.png.meta b/Packages/com.unity.shadergraph/Editor/Resources/Icons/blackboard.png.meta index 261716fb516..1459c548f29 100644 --- a/Packages/com.unity.shadergraph/Editor/Resources/Icons/blackboard.png.meta +++ b/Packages/com.unity.shadergraph/Editor/Resources/Icons/blackboard.png.meta @@ -3,10 +3,10 @@ guid: 954de74b6fa234cfbb474898019e0acf TextureImporter: internalIDToNameTable: [] externalObjects: {} - serializedVersion: 11 + serializedVersion: 13 mipmaps: mipMapMode: 0 - enableMipMap: 1 + enableMipMap: 0 sRGBTexture: 1 linearTexture: 0 fadeOut: 0 @@ -20,11 +20,12 @@ TextureImporter: externalNormalMap: 0 heightScale: 0.25 normalMapFilter: 0 + flipGreenChannel: 0 isReadable: 0 streamingMipmaps: 0 streamingMipmapsPriority: 0 vTOnly: 0 - ignoreMasterTextureLimit: 0 + ignoreMipmapLimit: 0 grayScaleToAlpha: 0 generateCubemap: 6 cubemapConvolution: 0 @@ -36,10 +37,10 @@ TextureImporter: filterMode: 1 aniso: 1 mipBias: 0 - wrapU: 0 - wrapV: 0 + wrapU: 1 + wrapV: 1 wrapW: 0 - nPOTScale: 1 + nPOTScale: 0 lightmap: 0 compressionQuality: 50 spriteMode: 0 @@ -51,9 +52,9 @@ TextureImporter: spriteBorder: {x: 0, y: 0, z: 0, w: 0} spriteGenerateFallbackPhysicsShape: 1 alphaUsage: 1 - alphaIsTransparency: 0 + alphaIsTransparency: 1 spriteTessellationDetail: -1 - textureType: 0 + textureType: 2 textureShape: 1 singleChannelComponent: 0 flipbookRows: 1 @@ -63,8 +64,10 @@ TextureImporter: textureFormatSet: 0 ignorePngGamma: 0 applyGammaDecoding: 0 + swizzle: 50462976 + cookieLightType: 1 platformSettings: - - serializedVersion: 3 + - serializedVersion: 4 buildTarget: DefaultTexturePlatform maxTextureSize: 2048 resizeAlgorithm: 0 @@ -74,12 +77,27 @@ TextureImporter: crunchedCompression: 0 allowsAlphaSplitting: 0 overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 4 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 androidETC2FallbackOverride: 0 forceMaximumCompressionQuality_BC6H_BC7: 0 spriteSheet: serializedVersion: 2 sprites: [] outline: [] + customData: physicsShape: [] bones: [] spriteID: @@ -89,10 +107,11 @@ TextureImporter: edges: [] weights: [] secondaryTextures: [] + spriteCustomMetadata: + entries: [] nameFileIdTable: {} - spritePackingTag: + mipmapLimitGroupName: pSDRemoveMatte: 0 - pSDShowRemoveMatteOption: 0 userData: assetBundleName: assetBundleVariant: diff --git a/Packages/com.unity.shadergraph/Editor/Resources/Icons/blackboard@2x.png b/Packages/com.unity.shadergraph/Editor/Resources/Icons/blackboard@2x.png index 9ac3ac234d1..b6e7cfb49ec 100644 Binary files a/Packages/com.unity.shadergraph/Editor/Resources/Icons/blackboard@2x.png and b/Packages/com.unity.shadergraph/Editor/Resources/Icons/blackboard@2x.png differ diff --git a/Packages/com.unity.shadergraph/Editor/Resources/Icons/blackboard@2x.png.meta b/Packages/com.unity.shadergraph/Editor/Resources/Icons/blackboard@2x.png.meta index 5110aac8a38..cc505911d1c 100644 --- a/Packages/com.unity.shadergraph/Editor/Resources/Icons/blackboard@2x.png.meta +++ b/Packages/com.unity.shadergraph/Editor/Resources/Icons/blackboard@2x.png.meta @@ -3,10 +3,10 @@ guid: aed2175e23cfe426e8678485b004714e TextureImporter: internalIDToNameTable: [] externalObjects: {} - serializedVersion: 11 + serializedVersion: 13 mipmaps: mipMapMode: 0 - enableMipMap: 1 + enableMipMap: 0 sRGBTexture: 1 linearTexture: 0 fadeOut: 0 @@ -20,11 +20,12 @@ TextureImporter: externalNormalMap: 0 heightScale: 0.25 normalMapFilter: 0 + flipGreenChannel: 0 isReadable: 0 streamingMipmaps: 0 streamingMipmapsPriority: 0 vTOnly: 0 - ignoreMasterTextureLimit: 0 + ignoreMipmapLimit: 0 grayScaleToAlpha: 0 generateCubemap: 6 cubemapConvolution: 0 @@ -36,10 +37,10 @@ TextureImporter: filterMode: 1 aniso: 1 mipBias: 0 - wrapU: 0 - wrapV: 0 + wrapU: 1 + wrapV: 1 wrapW: 0 - nPOTScale: 1 + nPOTScale: 0 lightmap: 0 compressionQuality: 50 spriteMode: 0 @@ -51,9 +52,9 @@ TextureImporter: spriteBorder: {x: 0, y: 0, z: 0, w: 0} spriteGenerateFallbackPhysicsShape: 1 alphaUsage: 1 - alphaIsTransparency: 0 + alphaIsTransparency: 1 spriteTessellationDetail: -1 - textureType: 0 + textureType: 2 textureShape: 1 singleChannelComponent: 0 flipbookRows: 1 @@ -63,8 +64,10 @@ TextureImporter: textureFormatSet: 0 ignorePngGamma: 0 applyGammaDecoding: 0 + swizzle: 50462976 + cookieLightType: 1 platformSettings: - - serializedVersion: 3 + - serializedVersion: 4 buildTarget: DefaultTexturePlatform maxTextureSize: 2048 resizeAlgorithm: 0 @@ -74,12 +77,27 @@ TextureImporter: crunchedCompression: 0 allowsAlphaSplitting: 0 overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 4 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 androidETC2FallbackOverride: 0 forceMaximumCompressionQuality_BC6H_BC7: 0 spriteSheet: serializedVersion: 2 sprites: [] outline: [] + customData: physicsShape: [] bones: [] spriteID: @@ -89,10 +107,11 @@ TextureImporter: edges: [] weights: [] secondaryTextures: [] + spriteCustomMetadata: + entries: [] nameFileIdTable: {} - spritePackingTag: + mipmapLimitGroupName: pSDRemoveMatte: 0 - pSDShowRemoveMatteOption: 0 userData: assetBundleName: assetBundleVariant: diff --git a/Packages/com.unity.shadergraph/Editor/Resources/Icons/blackboard_dark.png b/Packages/com.unity.shadergraph/Editor/Resources/Icons/blackboard_dark.png new file mode 100644 index 00000000000..a1890e476f6 Binary files /dev/null and b/Packages/com.unity.shadergraph/Editor/Resources/Icons/blackboard_dark.png differ diff --git a/Packages/com.unity.shadergraph/Editor/Resources/Icons/blackboard_dark.png.meta b/Packages/com.unity.shadergraph/Editor/Resources/Icons/blackboard_dark.png.meta new file mode 100644 index 00000000000..87c105f225a --- /dev/null +++ b/Packages/com.unity.shadergraph/Editor/Resources/Icons/blackboard_dark.png.meta @@ -0,0 +1,117 @@ +fileFormatVersion: 2 +guid: 2162b791346804a7792168e6fc526ced +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 13 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + flipGreenChannel: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMipmapLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 2 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + swizzle: 50462976 + cookieLightType: 0 + platformSettings: + - serializedVersion: 4 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 4 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + customData: + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spriteCustomMetadata: + entries: [] + nameFileIdTable: {} + mipmapLimitGroupName: + pSDRemoveMatte: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.shadergraph/Editor/Resources/Icons/blackboard_dark@2x.png b/Packages/com.unity.shadergraph/Editor/Resources/Icons/blackboard_dark@2x.png new file mode 100644 index 00000000000..8d845e531e6 Binary files /dev/null and b/Packages/com.unity.shadergraph/Editor/Resources/Icons/blackboard_dark@2x.png differ diff --git a/Packages/com.unity.shadergraph/Editor/Resources/Icons/blackboard_dark@2x.png.meta b/Packages/com.unity.shadergraph/Editor/Resources/Icons/blackboard_dark@2x.png.meta new file mode 100644 index 00000000000..47cd3253e57 --- /dev/null +++ b/Packages/com.unity.shadergraph/Editor/Resources/Icons/blackboard_dark@2x.png.meta @@ -0,0 +1,117 @@ +fileFormatVersion: 2 +guid: aa09d480b04b3437db2ba9c9cdbc77be +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 13 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + flipGreenChannel: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMipmapLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 1 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 2 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + swizzle: 50462976 + cookieLightType: 0 + platformSettings: + - serializedVersion: 4 + buildTarget: DefaultTexturePlatform + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 4 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + ignorePlatformSupport: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + customData: + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + spriteCustomMetadata: + entries: [] + nameFileIdTable: {} + mipmapLimitGroupName: + pSDRemoveMatte: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.shadergraph/Editor/Resources/Styles/SGBlackboard.uss b/Packages/com.unity.shadergraph/Editor/Resources/Styles/SGBlackboard.uss index 0d0d5201da2..9c39f2ce94b 100644 --- a/Packages/com.unity.shadergraph/Editor/Resources/Styles/SGBlackboard.uss +++ b/Packages/com.unity.shadergraph/Editor/Resources/Styles/SGBlackboard.uss @@ -213,7 +213,7 @@ } .blackboardCategory > .mainContainer > #categoryHeader > #categoryTitleLabel { - color: #606060; + color: #A0A0A0; font-size: 11px; } diff --git a/Packages/com.unity.shadergraph/Editor/ShaderGraphProjectSettings.cs b/Packages/com.unity.shadergraph/Editor/ShaderGraphProjectSettings.cs index 7d7793819c5..008a7c4cfed 100644 --- a/Packages/com.unity.shadergraph/Editor/ShaderGraphProjectSettings.cs +++ b/Packages/com.unity.shadergraph/Editor/ShaderGraphProjectSettings.cs @@ -46,10 +46,28 @@ private class Styles public static readonly GUIContent CustomInterpLabel = L10n.TextContent("Custom Interpolator Channel Settings", ""); public static readonly GUIContent CustomInterpWarnThresholdLabel = L10n.TextContent("Warning Threshold", $"Shader Graph displays a warning when the user creates more custom interpolators than permitted by this setting. The number of interpolators that trigger this warning must be between {kMinChannelThreshold} and the Error Threshold."); public static readonly GUIContent CustomInterpErrorThresholdLabel = L10n.TextContent("Error Threshold", $"Shader Graph displays an error message when the user tries to create more custom interpolators than permitted by this setting. The number of interpolators that trigger this error must be between {kMinChannelThreshold} and {kMaxChannelThreshold}."); - public static readonly GUIContent ReadMore = L10n.TextContent("Read more"); + public static readonly string kReadMore = "read more"; + public static readonly GUIStyle helpBoxIconStyle; + public static readonly GUIStyle readMoreStyle; public static readonly GUIContent HeatmapSectionLabel = L10n.TextContent("Heatmap Color Mode Settings", ""); public static readonly GUIContent HeatmapAssetLabel = L10n.TextContent("Custom Values", "Specifies a custom Heatmap Values asset with data to display in the Heatmap color mode. If empty, a set of default values will be used."); + + static Styles() + { + helpBoxIconStyle = new GUIStyle(); + helpBoxIconStyle.fixedWidth = 16; + helpBoxIconStyle.fixedHeight = 16; + // We want the spacing of an EditorStyles.wordWrappedMiniLabel within an EditorStyles.helpBox, + // but adding padding to the icon means we can't guarantee the icon's size is 16x16, so manually + // compute margins + helpBoxIconStyle.margin.left = EditorStyles.wordWrappedMiniLabel.padding.left + EditorStyles.wordWrappedMiniLabel.margin.left; + helpBoxIconStyle.margin.top = EditorStyles.wordWrappedMiniLabel.padding.top + EditorStyles.wordWrappedMiniLabel.margin.top; + + readMoreStyle = new GUIStyle(EditorStyles.linkLabel); + readMoreStyle.fontSize = EditorStyles.miniLabel.fontSize; + readMoreStyle.wordWrap = true; + } } SerializedObject m_SerializedObject; @@ -113,14 +131,20 @@ void OnGUIHandler(string searchContext) m_customInterpError.intValue = Mathf.Clamp(newError, oldWarn, kMaxChannelThreshold); m_customInterpWarn.intValue = Mathf.Clamp(newWarn, kMinChannelThreshold, oldError); - GUILayout.BeginHorizontal(EditorStyles.helpBox); - GUILayout.Label(EditorGUIUtility.IconContent("console.infoicon"), GUILayout.ExpandWidth(true)); - GUILayout.Box(kCustomInterpolatorHelpBox, EditorStyles.wordWrappedLabel); - if (EditorGUILayout.LinkButton(Styles.ReadMore)) + GUILayout.BeginVertical(EditorStyles.helpBox); + GUILayout.BeginHorizontal(); + GUILayout.Label(EditorGUIUtility.GetHelpIcon(MessageType.Info), Styles.helpBoxIconStyle); + GUILayout.Label(kCustomInterpolatorHelpBox, EditorStyles.wordWrappedMiniLabel); + GUILayout.EndHorizontal(); + GUILayout.BeginHorizontal(); + GUILayout.FlexibleSpace(); + if (GUILayout.Button(Styles.kReadMore, Styles.readMoreStyle)) { System.Diagnostics.Process.Start(kCustomInterpolatorDocumentationURL); } + EditorGUIUtility.AddCursorRect(GUILayoutUtility.GetLastRect(), MouseCursor.Link); GUILayout.EndHorizontal(); + GUILayout.EndVertical(); EditorGUI.indentLevel--; EditorGUILayout.LabelField(Styles.HeatmapSectionLabel, EditorStyles.boldLabel); diff --git a/Packages/com.unity.shadergraph/Editor/ShaderGraphShortcuts.cs b/Packages/com.unity.shadergraph/Editor/ShaderGraphShortcuts.cs index 3009ac8823f..786037f228f 100644 --- a/Packages/com.unity.shadergraph/Editor/ShaderGraphShortcuts.cs +++ b/Packages/com.unity.shadergraph/Editor/ShaderGraphShortcuts.cs @@ -203,13 +203,21 @@ static void Group(ShortcutArguments args) { CheckBindings(nodeGroupShortcutID); var graphView = GetGraphView(); + bool selectionHasGroupableElements = false; foreach(var selected in graphView.selection) + { if ((selected is IShaderNodeView nodeView && nodeView.node is AbstractMaterialNode) || selected.GetType() == typeof(Drawing.StickyNote)) { - graphView.GroupSelection(); - break; + selectionHasGroupableElements = true; } + if (selected is Group) + { + return; + } + } + if (selectionHasGroupableElements) + graphView.GroupSelection(); } internal const string nodeUnGroupShortcutID = "ShaderGraph/Selection: Node Ungroup"; diff --git a/Packages/com.unity.shadergraph/Samples~/UGUIShaders/Scripts/Runtime/CustomToggle.cs b/Packages/com.unity.shadergraph/Samples~/UGUIShaders/Scripts/Runtime/CustomToggle.cs index 78f076b33b8..c952b694feb 100644 --- a/Packages/com.unity.shadergraph/Samples~/UGUIShaders/Scripts/Runtime/CustomToggle.cs +++ b/Packages/com.unity.shadergraph/Samples~/UGUIShaders/Scripts/Runtime/CustomToggle.cs @@ -61,6 +61,7 @@ public Graphic Graphic protected override void Awake() { base.Awake(); + onValueChanged.AddListener((x) => UpdateMaterial()); } #if UNITY_EDITOR @@ -85,31 +86,13 @@ protected override void OnValidate() if (!PrefabUtility.IsPartOfPrefabAsset(this) && !Application.isPlaying) CanvasUpdateRegistry.RegisterCanvasElementForLayoutRebuild(this); - UpdateMaterial(true); + UpdateMaterial(); } #endif - public void UpdateMaterial(bool findGroupToggles = false) + public void UpdateMaterial() { - if (group != null) - { - if (findGroupToggles) // only used in Edit mode when ToggleGroup isn't initialized already - { - foreach (var t in FindObjectsByType(FindObjectsInactive.Include, FindObjectsSortMode.None)) - if (t.group == group) - t.Graphic.SetMaterialDirty(); - } - else - { - foreach(var t in group.ActiveToggles()) - if (t is CustomToggle customToggle) - customToggle.Graphic.SetMaterialDirty(); - } - } - else - { - Graphic.SetMaterialDirty(); - } + Graphic.SetMaterialDirty(); } protected override void DoStateTransition(SelectionState state, bool instant) @@ -122,7 +105,6 @@ protected override void DoStateTransition(SelectionState state, bool instant) public virtual Material GetModifiedMaterial(Material baseMaterial) { _material ??= new(baseMaterial); - _material.CopyPropertiesFromMaterial(baseMaterial); if (_material.HasFloat(StatePropertyId)) @@ -130,7 +112,7 @@ public virtual Material GetModifiedMaterial(Material baseMaterial) if (_material.HasFloat(IsOnPropertyId)) _material.SetFloat(IsOnPropertyId, isOn ? 1 : 0); - + return _material; } diff --git a/Packages/com.unity.visualeffectgraph/Documentation~/Images/templates-window.png b/Packages/com.unity.visualeffectgraph/Documentation~/Images/templates-window.png index bdaf1aa201e..94486cf3b9b 100644 Binary files a/Packages/com.unity.visualeffectgraph/Documentation~/Images/templates-window.png and b/Packages/com.unity.visualeffectgraph/Documentation~/Images/templates-window.png differ diff --git a/Packages/com.unity.visualeffectgraph/Documentation~/Templates-window.md b/Packages/com.unity.visualeffectgraph/Documentation~/Templates-window.md index 4d13415710b..8fe60e26d8c 100644 --- a/Packages/com.unity.visualeffectgraph/Documentation~/Templates-window.md +++ b/Packages/com.unity.visualeffectgraph/Documentation~/Templates-window.md @@ -1,48 +1,41 @@ -# Default VFX Graph Templates window - +# VFX Graph Templates window Use the template window to create a VFX Graph asset with a predefined effect. You can use these templates as a starting point for your own effects. -Each template has a description and an image to describe its behavior. +Each template has a description and an image to describe its behavior. +This window is displayed when you create a new Visual Effect Graph from the `Create` context menu in the project browser. -![Template-Window](Images/templates-window.png) +![Template-Window](Images/templates-window.png) -## Use a VFX Graph Template +## Open from VFX Graph editor toolbar +![toolbar](Images/templates-window-toolbar.png) -![toolbar](Images/templates-window-toolbar.png) -To open the VFX Graph Templates window: -1. Select the dropdown arrow next to the **Add** (**+**) icon in the Visual Effect graph toolbar. -2. Select one of the following options: - * **Create from template** - Creates a new VFX Graph asset based on a VFX Graph template. - * **Insert template** - adds a VFX Graph template to the VFX Graph asset that is currently open. -3. In the Create new VFX Asset window, select a Default VFX Graph template. -4. Double-click the Template asset, or select **Create** +You can open the templates window from the **Add** (+) button in the VFX Graph editor toolbar. This button includes a drop-down to either `insert` a template in the current graph, or `create` a new asset file from a template. -The Add **[+]** button opens the templates window to insert a template in the current VFX (it will be placed at the center of the screen). -You can also use the **Insert template** option from the context menu in the graph. +When you insert a template, Unity places it at the center of the screen. > [!TIP] -> If you hold the **CTRL** key while clicking on **[+]** button, the templates window will open to create a new VFX asset. - -## Create a custom VFX Graph template +> If you hold the `CTRL` key while you click on the **Add** (+) button, the templates window opens to create a new VFX asset. -VFX Graph includes an API that you can use to create and manage your own VFX Graph templates. +## Open from the VFX Graph context menu +When you right click in the VFX Graph editor window, the context menu shows the **Insert template** option, which inserts the template at the mouse position. -To create a new VFX Graph template, use the `VFXTemplateHelper.TrySetTemplate` method. -Include the following in your script: - - The path to the VFX asset. - - A `VFXTemplateDescriptor` structure with following information: - - Name: Name of the template. - - Category: The category this template appears in. - - Description: A description for the template to display in the template window details panel. - - Icon: (optional) An image icon to show in the template window list of templates. - - Thumbnail: (optional) An image to display in the template window details panel. +## Search and filter templates +The template window includes a search field to filter templates according to various criteria. +The search field works the same way as with the main [Unity search window](https://docs.unity3d.com/Manual/search-window-reference.html), although there are fewer filters available. -The method returns `true` when the script creates a new template, otherwise it returns `false`. -Custom templates appear in the templates window in the Category you defined. +## Sort templates +Next to the search field, a dropdown allows to sort templates by: +- Name +- Order +- Modification date +- Last used +- Favorite -### Use an existing VFX Graph template in script +> [!NOTE] +> The sorting is applied inside each category, but categories are always sorted by name. -To get an existing template descriptor: -1. Use the method `VFXTemplateHelper.TryGetTemplate`. -2.Provide the path to the asset and a `VFXTemplateDescriptor` structure that will be filled if the asset is found and is a template. +## VFX Graph template Editor -The method returns `true` when the script finds the template, otherwise it returns `false`. +When you select a [Visual Effect Asset](VisualEffectGraphAsset.md) in the Project window, the inspector displays a section dedicated to template description. +To make a Visual Effect asset become a template, activate the **Use as Template** option. +By default the template name is the asset name, but you can override it by editing the `Name` field. +The description, icon and thumbnail fields are optional, but can help to understand the purpose of the template when browsing in the template window. diff --git a/Packages/com.unity.visualeffectgraph/Editor/GraphView/VFXViewPreference.cs b/Packages/com.unity.visualeffectgraph/Editor/GraphView/VFXViewPreference.cs index 306eae5f863..8f64919fe18 100644 --- a/Packages/com.unity.visualeffectgraph/Editor/GraphView/VFXViewPreference.cs +++ b/Packages/com.unity.visualeffectgraph/Editor/GraphView/VFXViewPreference.cs @@ -24,6 +24,7 @@ static class VFXViewPreference private static VFXMainCameraBufferFallback m_CameraBuffersFallback = VFXMainCameraBufferFallback.PreferMainCamera; private static bool m_MultithreadUpdateEnabled = true; private static bool m_InstancingEnabled = true; + private static bool m_ShowPackageIndexingBanner = true; private static int m_AuthoringPrewarmStepCountPerSeconds = kAuthoringPrewarmStepCountPerSecondsDefault; private static float m_AuthoringPrewarmMaxTime = kAuthoringPrewarmMaxTimeDefault; private static bool m_VisualEffectTargetListed = false; @@ -100,6 +101,20 @@ public static bool instancingEnabled } } + public static bool showPackageIndexingBanner + { + get + { + LoadIfNeeded(); + return m_ShowPackageIndexingBanner; + } + set + { + m_ShowPackageIndexingBanner = value; + EditorPrefs.SetBool(showPackageIndexingBannerKey, m_ShowPackageIndexingBanner); + } + } + public static int authoringPrewarmStepCountPerSeconds { get @@ -141,6 +156,7 @@ public static void SetDirty() public const string cameraBuffersFallbackKey = "VFX.CameraBuffersFallback"; public const string multithreadUpdateEnabledKey = "VFX.MultithreadUpdateEnabled"; public const string instancingEnabledKey = "VFX.InstancingEnabled"; + public const string showPackageIndexingBannerKey = "VFX.ShowPackageIndexingBanner"; public const string authoringPrewarmStepCountPerSecondsKey = "VFX.AuthoringPrewarmStepCountPerSeconds"; public const string authoringPrewarmMaxTimeKey = "VFX.AuthoringPrewarmMaxTimeKey"; public const string visualEffectTargetListedKey = UnityEditor.ShaderGraph.VFXTarget.kVisualEffectTargetListedKey; @@ -158,6 +174,7 @@ private static void LoadIfNeeded() m_CameraBuffersFallback = (VFXMainCameraBufferFallback)EditorPrefs.GetInt(cameraBuffersFallbackKey, (int)VFXMainCameraBufferFallback.PreferMainCamera); m_MultithreadUpdateEnabled = EditorPrefs.GetBool(multithreadUpdateEnabledKey, true); m_InstancingEnabled = EditorPrefs.GetBool(instancingEnabledKey, true); + m_ShowPackageIndexingBanner = EditorPrefs.GetBool(showPackageIndexingBannerKey, true); m_AuthoringPrewarmStepCountPerSeconds = EditorPrefs.GetInt(authoringPrewarmStepCountPerSecondsKey, kAuthoringPrewarmStepCountPerSecondsDefault); m_AuthoringPrewarmStepCountPerSeconds = Mathf.Clamp(m_AuthoringPrewarmStepCountPerSeconds, 0, kAuthoringPrewarmStepCountPerSecondsMax); @@ -235,6 +252,8 @@ public override void OnGUI(string searchContext) } #endif + m_ShowPackageIndexingBanner = EditorGUILayout.Toggle(new GUIContent("Show package indexing banner", "When enabled, a banner will be displayed in the template window if packages are not indexed"), m_ShowPackageIndexingBanner); + m_CameraBuffersFallback = (VFXMainCameraBufferFallback)EditorGUILayout.EnumPopup(new GUIContent("Main Camera fallback", "Specifies the camera source for MainCamera Operators and Blocks to use when in the editor."), m_CameraBuffersFallback); m_VisualEffectTargetListed = EditorGUILayout.Toggle(new GUIContent("Show Target in Shader Graph (deprecated)", "When enabled, the Visual Effect Target is listed in Active Targets dropdown in Shader Graph."), m_VisualEffectTargetListed); @@ -252,6 +271,7 @@ public override void OnGUI(string searchContext) EditorPrefs.SetInt(cameraBuffersFallbackKey, (int)m_CameraBuffersFallback); EditorPrefs.SetBool(multithreadUpdateEnabledKey, m_MultithreadUpdateEnabled); EditorPrefs.SetBool(instancingEnabledKey, m_InstancingEnabled); + EditorPrefs.SetBool(showPackageIndexingBannerKey, m_ShowPackageIndexingBanner); m_AuthoringPrewarmStepCountPerSeconds = Mathf.Clamp(m_AuthoringPrewarmStepCountPerSeconds, 0, kAuthoringPrewarmStepCountPerSecondsMax); EditorPrefs.SetInt(authoringPrewarmStepCountPerSecondsKey, m_AuthoringPrewarmStepCountPerSeconds); diff --git a/Packages/com.unity.visualeffectgraph/Editor/GraphView/Views/VFXPicker.cs b/Packages/com.unity.visualeffectgraph/Editor/GraphView/Views/VFXPicker.cs index 4885c19fccf..29171004cb7 100644 --- a/Packages/com.unity.visualeffectgraph/Editor/GraphView/Views/VFXPicker.cs +++ b/Packages/com.unity.visualeffectgraph/Editor/GraphView/Views/VFXPicker.cs @@ -19,6 +19,7 @@ internal static void Pick(VisualEffectAsset vfxAsset, Action selec SearchService.CreateContext(CreateSceneRefProvider(vfxAsset)), (item, canceled) => SelectItem(item, canceled, selectHandler)); viewState.title = "Visual Effect"; + viewState.context.useExplicitProvidersAsNormalProviders = true; viewState.flags |= SearchViewFlags.DisableInspectorPreview | SearchViewFlags.CompactView; SearchService.ShowPicker(viewState); } diff --git a/Packages/com.unity.visualeffectgraph/Editor/Inspector/VFXAssetEditor.cs b/Packages/com.unity.visualeffectgraph/Editor/Inspector/VFXAssetEditor.cs index ce739a198ad..d1b5f093475 100644 --- a/Packages/com.unity.visualeffectgraph/Editor/Inspector/VFXAssetEditor.cs +++ b/Packages/com.unity.visualeffectgraph/Editor/Inspector/VFXAssetEditor.cs @@ -643,15 +643,16 @@ public override VisualElement CreateInspectorGUI() // Template section uses UIToolkit var importers = new List(); - foreach (var t in targets) + var paths = targets.Select(AssetDatabase.GetAssetPath).ToArray(); + foreach (var path in paths) { - var path = AssetDatabase.GetAssetPath(t); var importer = AssetImporter.GetAtPath(path) as VisualEffectImporter; if (importer != null) { importers.Add(importer); } } + if (importers.Count > 0) { root.styleSheets.Add(VFXView.LoadStyleSheet("VisualEffectAssetEditor")); @@ -664,24 +665,6 @@ public override VisualElement CreateInspectorGUI() var useAsTemplateProperty = allImportersSerializedObject.FindProperty("m_UseAsTemplate"); var useAsTemplateCheckbox = new Toggle("Use as Template") { tooltip = "When enabled, this asset will be used as a template for new Visual Effect Assets" }; useAsTemplateCheckbox.BindProperty(useAsTemplateProperty); - useAsTemplateCheckbox.TrackPropertyValue(useAsTemplateProperty, - x => - { - if (x.boolValue) - { - // When enabling the template for the first time, initialize the template name with the asset name if not already done - // so that the name is never empty when used as a template. - for (int i = 0; i < importers.Count; i++) - { - var template = importers[i].templateProperty; - if (string.IsNullOrEmpty(template.name)) - { - template.name = targets[i].name; - importers[i].templateProperty = template; - } - } - } - }); header.Add(useAsTemplateCheckbox); var expander = new Foldout { text = "Template", style = { marginLeft = 15f } }; @@ -724,6 +707,18 @@ public override VisualElement CreateInspectorGUI() var thumbnailField = new ObjectField("Thumbnail") { objectType = typeof(Texture2D), tooltip = "Thumbnail of the template, used to represent the template in the Template window details view." }; thumbnailField.BindProperty(allImportersSerializedObject.FindProperty("m_Template.thumbnail")); expander.Add(thumbnailField); + + header.TrackSerializedObjectValue(allImportersSerializedObject, x => + { + // Does not work + /*foreach (var t in targets) + { + EditorUtility.SetDirty(t); + AssetDatabase.SaveAssetIfDirty(t); + }*/ + // This works + AssetDatabase.ForceReserializeAssets(paths, ForceReserializeAssetsOptions.ReserializeMetadata); + }); } return root; diff --git a/Packages/com.unity.visualeffectgraph/Editor/Inspector/VisualEffectEditor.cs b/Packages/com.unity.visualeffectgraph/Editor/Inspector/VisualEffectEditor.cs index f1a60366c02..ff31ffd3590 100644 --- a/Packages/com.unity.visualeffectgraph/Editor/Inspector/VisualEffectEditor.cs +++ b/Packages/com.unity.visualeffectgraph/Editor/Inspector/VisualEffectEditor.cs @@ -72,6 +72,7 @@ class VisualEffectEditor : Editor SerializedProperty m_RandomSeed; SerializedProperty m_VFXPropertySheet; SerializedProperty m_AllowInstancing; + SerializedProperty m_ReleaseInstanceOnDisable; RendererEditor m_RendererEditor; @@ -107,6 +108,7 @@ protected void OnEnable() m_VisualEffectAsset = serializedObject.FindProperty("m_Asset"); m_VFXPropertySheet = m_SingleSerializedObject.FindProperty("m_PropertySheet"); m_AllowInstancing = serializedObject.FindProperty("m_AllowInstancing"); + m_ReleaseInstanceOnDisable = serializedObject.FindProperty("m_ReleaseInstanceOnDisable"); var renderers = targets.Cast().Select(t => t.GetComponent()).ToArray(); m_RendererEditor = new RendererEditor(renderers); @@ -1271,13 +1273,14 @@ private void DrawInstancingProperties() { EditorGUI.BeginChangeCheck(); EditorGUILayout.PropertyField(m_AllowInstancing, Contents.allowInstancing); + EditorGUILayout.PropertyField(m_ReleaseInstanceOnDisable, Contents.releaseInstanceOnDisable); if (EditorGUI.EndChangeCheck()) { serializedObject.ApplyModifiedProperties(); foreach (var visualEffect in targets.OfType()) { - visualEffect.RecreateData(); + visualEffect.RecreateBatchInstance(); } } } @@ -1503,6 +1506,7 @@ protected static class Contents public static readonly GUILayoutOption playRateDropdownWidth = GUILayout.Width(40); public static readonly GUIContent allowInstancing = EditorGUIUtility.TrTextContent("Allow Instancing", "When enabled, the effect will try to be batched with other of the same type."); + public static readonly GUIContent releaseInstanceOnDisable = EditorGUIUtility.TrTextContent("Release Instance On Disable", "When enabled, the effect instance will be created when the component is enabled, and released when it is disabled, to optimize memory usage."); public static readonly GUIContent graphInBundle = EditorGUIUtility.TrTextContent("Exposed properties are hidden in the Inspector when Visual Effect Assets are stored in Asset Bundles."); public static readonly GUIContent play = new GUIContent("Send Play Event"); diff --git a/Packages/com.unity.visualeffectgraph/Editor/Models/VFXGraph.cs b/Packages/com.unity.visualeffectgraph/Editor/Models/VFXGraph.cs index 94b8a9beb2a..01128738ead 100644 --- a/Packages/com.unity.visualeffectgraph/Editor/Models/VFXGraph.cs +++ b/Packages/com.unity.visualeffectgraph/Editor/Models/VFXGraph.cs @@ -214,6 +214,8 @@ static void CheckCompilationVersion() compiledVersionProperty.intValue = (int)VFXGraphCompiledData.compiledVersion; runtimeVersionProperty.intValue = (int)VisualEffectAsset.currentRuntimeDataVersion; serializedVFXManager.ApplyModifiedProperties(); + EditorUtility.SetDirty(vfxmanager); + AssetDatabase.SaveAssets(); AssetDatabase.StartAssetEditing(); try diff --git a/Packages/com.unity.visualeffectgraph/Editor/TemplateWindow/VFXTemplateHelper.cs b/Packages/com.unity.visualeffectgraph/Editor/TemplateWindow/VFXTemplateHelper.cs index d47a70c26f0..57bd0e09cbb 100644 --- a/Packages/com.unity.visualeffectgraph/Editor/TemplateWindow/VFXTemplateHelper.cs +++ b/Packages/com.unity.visualeffectgraph/Editor/TemplateWindow/VFXTemplateHelper.cs @@ -73,7 +73,7 @@ public static bool TryGetTemplate(string vfxPath, out VFXTemplateDescriptor vfxT /// Returns true if the template is created, otherwise it returns false. public static bool TrySetTemplate(string vfxPath, VFXTemplateDescriptor vfxTemplateDescriptor) { - return VFXTemplateHelperInternal.TrySetTemplateStatic(vfxPath, new GraphViewTemplateDescriptor + return VFXTemplateHelperInternal.TrySetTemplateStatic(vfxPath, new GraphViewTemplateDescriptor(VFXTemplateHelperInternal.VFXGraphToolKey) { name = vfxTemplateDescriptor.name, category = vfxTemplateDescriptor.category, diff --git a/Packages/com.unity.visualeffectgraph/Editor/TemplateWindow/VFXTemplateHelperInternal.cs b/Packages/com.unity.visualeffectgraph/Editor/TemplateWindow/VFXTemplateHelperInternal.cs index 1a7dfedd433..be74341fea3 100644 --- a/Packages/com.unity.visualeffectgraph/Editor/TemplateWindow/VFXTemplateHelperInternal.cs +++ b/Packages/com.unity.visualeffectgraph/Editor/TemplateWindow/VFXTemplateHelperInternal.cs @@ -1,7 +1,10 @@ using System; +using System.IO; using System.Reflection; using UnityEditor.Experimental.GraphView; using UnityEngine; +using UnityEditor.Search; +using UnityEngine.VFX; namespace UnityEditor.VFX { @@ -19,22 +22,43 @@ public string OpenSaveFileDialog() } } + const string k_EmptyTemplateRelativePath = "/Editor/Templates/Empty.vfx"; + + string m_EmptyTemplateGuid; + + public static string VFXGraphToolKey => "VFXGraph"; + public string toolKey => VFXGraphToolKey; public string packageInfoName => VisualEffectGraphPackageInfo.name; public string learningSampleName => "Learning Templates"; public string templateWindowDocUrl =>Documentation.GetPageLink("Templates-window"); public string builtInTemplatePath => VisualEffectAssetEditorUtility.templatePath; public string builtInCategory => "Default VFX Graph Templates"; - public string assetType => "VisualEffectAsset"; - public string emptyTemplateName => "Empty VFX"; - public string emptyTemplateDescription => "Create a completely empty VFX asset"; - public string lastSelectedGuidKey => "VFXTemplateWindow.LastSelectedGuid"; + public Type assetType => typeof(VisualEffectAsset); + + public string emptyTemplateGuid + { + get + { + if (string.IsNullOrEmpty(m_EmptyTemplateGuid)) + { + m_EmptyTemplateGuid = FindEmptyTemplateDescriptor(); + } + + return m_EmptyTemplateGuid; + } + } + public string createNewAssetTitle => "Create new VFX Asset"; public string insertTemplateTitle => "Insert a template into current VFX Asset"; - public string emptyTemplateIconPath => $"{VisualEffectGraphPackageInfo.assetPackagePath}/Editor/Templates/UI/EmptyTemplate@256.png"; - public string emptyTemplateScreenshotPath => $"{VisualEffectGraphPackageInfo.assetPackagePath}/Editor/Templates/UI/3d_Empty.png"; public string customTemplateIcon => $"{VisualEffectGraphPackageInfo.assetPackagePath}/Editor/Templates/UI/CustomVFXGraph@256.png"; - public GraphViewTemplateWindow.ISaveFileDialogHelper saveFileDialogHelper { get; set; } = new SaveFileDialog(); + public bool showPackageIndexingBanner + { + get => VFXViewPreference.showPackageIndexingBanner; + set => VFXViewPreference.showPackageIndexingBanner = value; + } + + public GraphViewTemplateWindow.ISaveFileDialogHelper saveFileDialogHelper { get; set; } = new SaveFileDialog(); public static void ImportSampleDependencies(PackageManager.PackageInfo packageInfo, PackageManager.UI.Sample sample) { @@ -93,18 +117,29 @@ public void RaiseTemplateUsed(GraphViewTemplateDescriptor usedTemplate) /// Returns true if the template is created, otherwise it returns false. public bool TrySetTemplate(string vfxPath, GraphViewTemplateDescriptor graphViewTemplate) => TrySetTemplateStatic(vfxPath, graphViewTemplate); + public SearchProposition[] GetSearchPropositions() => Array.Empty(); + + public ITemplateSorter[] GetTemplateSorter() => Array.Empty(); + internal static bool TryGetTemplateStatic(string vfxPath, out GraphViewTemplateDescriptor graphViewTemplate) { - var importer = (VisualEffectImporter)AssetImporter.GetAtPath(vfxPath); - if (importer is { useAsTemplateProperty: true, templateProperty: var nativeTemplate } && !string.IsNullOrEmpty(nativeTemplate.name)) + // Can happen because the search engine sometimes finds prefabs + if (!vfxPath.EndsWith(VisualEffectResource.Extension)) + { + graphViewTemplate = default; + return false; + } + + if (AssetImporter.GetAtPath(vfxPath) is VisualEffectImporter { useAsTemplateProperty: true, templateProperty: var nativeTemplate }) { - graphViewTemplate = new GraphViewTemplateDescriptor + graphViewTemplate = new GraphViewTemplateDescriptor(VFXGraphToolKey) { name = nativeTemplate.name, category = nativeTemplate.category, description = nativeTemplate.description, icon = nativeTemplate.icon, thumbnail = nativeTemplate.thumbnail, + order = nativeTemplate.order, }; return true; @@ -137,5 +172,10 @@ internal static bool TrySetTemplateStatic(string vfxPath, GraphViewTemplateDescr return false; } + + string FindEmptyTemplateDescriptor() + { + return AssetDatabase.GUIDFromAssetPath(VisualEffectGraphPackageInfo.assetPackagePath + k_EmptyTemplateRelativePath).ToString(); + } } } diff --git a/Packages/com.unity.visualeffectgraph/Editor/TemplateWindow/VisualEffectIndexerExtension.cs b/Packages/com.unity.visualeffectgraph/Editor/TemplateWindow/VisualEffectIndexerExtension.cs new file mode 100644 index 00000000000..b72850a6dc3 --- /dev/null +++ b/Packages/com.unity.visualeffectgraph/Editor/TemplateWindow/VisualEffectIndexerExtension.cs @@ -0,0 +1,18 @@ +using UnityEditor.Experimental.GraphView; +using UnityEditor.Search; +using UnityEngine.VFX; + +namespace UnityEditor.VFX +{ + static class VisualEffectIndexerExtension + { + [CustomObjectIndexer(typeof(VisualEffectAsset), version = 0)] + internal static void VisualEffectImporterIndexer(CustomObjectIndexerTarget context, ObjectIndexer indexer) + { + if (VFXTemplateHelperInternal.TryGetTemplateStatic(context.id, out var template)) + { + GraphViewIndexerExtension.IndexCommonData(context, indexer, template); + } + } + } +} diff --git a/Packages/com.unity.visualeffectgraph/Editor/TemplateWindow/VisualEffectIndexerExtension.cs.meta b/Packages/com.unity.visualeffectgraph/Editor/TemplateWindow/VisualEffectIndexerExtension.cs.meta new file mode 100644 index 00000000000..ded130dae17 --- /dev/null +++ b/Packages/com.unity.visualeffectgraph/Editor/TemplateWindow/VisualEffectIndexerExtension.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 77260aa383a340899a03c5be5f3bce16 +timeCreated: 1743514251 \ No newline at end of file diff --git a/Packages/com.unity.visualeffectgraph/Editor/Templates/Empty.vfx b/Packages/com.unity.visualeffectgraph/Editor/Templates/Empty.vfx new file mode 100644 index 00000000000..fcc2174629e --- /dev/null +++ b/Packages/com.unity.visualeffectgraph/Editor/Templates/Empty.vfx @@ -0,0 +1,68 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &114340500867371532 +MonoBehaviour: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: d01270efd3285ea4a9d6c555cb0a8027, type: 3} + m_Name: VFXUI + m_EditorClassIdentifier: + groupInfos: [] + stickyNoteInfos: [] + categories: [] + uiBounds: + serializedVersion: 2 + x: 975 + y: 499 + width: 474 + height: 1989 +--- !u!114 &114350483966674976 +MonoBehaviour: + m_ObjectHideFlags: 1 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 7d4c867f6b72b714dbb5fd1780afe208, type: 3} + m_Name: New VFX + m_EditorClassIdentifier: + m_UIIgnoredErrors: [] + m_Parent: {fileID: 0} + m_Children: [] + m_UIPosition: {x: 0, y: 0} + m_UICollapsed: 1 + m_UISuperCollapsed: 0 + m_UIInfos: {fileID: 114340500867371532} + m_CustomAttributes: [] + m_ParameterInfo: [] + m_ImportDependencies: [] + m_GraphVersion: 19 + m_ResourceVersion: 1 + m_SubgraphDependencies: [] + m_CategoryPath: +--- !u!2058629511 &8926484042661614527 +VisualEffectResource: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: New VFX + m_Graph: {fileID: 114350483966674976} + m_Infos: + m_RendererSettings: + motionVectorGenerationMode: 0 + shadowCastingMode: 0 + m_CullingFlags: 0 + m_UpdateMode: 0 + m_PreWarmDeltaTime: 0.05 + m_PreWarmStepCount: 0 + m_InitialEventName: OnPlay + m_InstancingMode: 0 + m_InstancingCapacity: 64 diff --git a/Packages/com.unity.visualeffectgraph/Editor/Templates/Empty.vfx.meta b/Packages/com.unity.visualeffectgraph/Editor/Templates/Empty.vfx.meta new file mode 100644 index 00000000000..b54b75a1e45 --- /dev/null +++ b/Packages/com.unity.visualeffectgraph/Editor/Templates/Empty.vfx.meta @@ -0,0 +1,16 @@ +fileFormatVersion: 2 +guid: 6bcf2b5bb4032c74fa455ddd0af12db8 +VisualEffectImporter: + externalObjects: {} + serializedVersion: 2 + template: + name: Empty VFX + category: Built-in + description: Create a completely empty VFX asset + icon: {fileID: 2800000, guid: e0b0c3eb88471ae4e816e77dcb0daa77, type: 3} + thumbnail: {fileID: 2800000, guid: 6e3e22332cef02447956ecfdf9352294, type: 3} + order: 6 + useAsTemplate: 1 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/com.unity.visualeffectgraph/Editor/Templates/06_Firework.vfx b/Packages/com.unity.visualeffectgraph/Editor/Templates/Firework.vfx similarity index 100% rename from Packages/com.unity.visualeffectgraph/Editor/Templates/06_Firework.vfx rename to Packages/com.unity.visualeffectgraph/Editor/Templates/Firework.vfx diff --git a/Packages/com.unity.visualeffectgraph/Editor/Templates/06_Firework.vfx.meta b/Packages/com.unity.visualeffectgraph/Editor/Templates/Firework.vfx.meta similarity index 98% rename from Packages/com.unity.visualeffectgraph/Editor/Templates/06_Firework.vfx.meta rename to Packages/com.unity.visualeffectgraph/Editor/Templates/Firework.vfx.meta index 6205ff69e79..38b6361ffc8 100644 --- a/Packages/com.unity.visualeffectgraph/Editor/Templates/06_Firework.vfx.meta +++ b/Packages/com.unity.visualeffectgraph/Editor/Templates/Firework.vfx.meta @@ -11,6 +11,7 @@ VisualEffectImporter: and can be adjusted to create effects such as fireworks, impacts, hits, etc. icon: {fileID: 2800000, guid: e81d1aad004b59347b9428aa392dd26f, type: 3} thumbnail: {fileID: 2800000, guid: a470305953dc9c941b100d56afe7edd0, type: 3} + order: 5 useAsTemplate: 1 userData: assetBundleName: diff --git a/Packages/com.unity.visualeffectgraph/Editor/Templates/05_Head_Trail.vfx b/Packages/com.unity.visualeffectgraph/Editor/Templates/Head_Trail.vfx similarity index 100% rename from Packages/com.unity.visualeffectgraph/Editor/Templates/05_Head_Trail.vfx rename to Packages/com.unity.visualeffectgraph/Editor/Templates/Head_Trail.vfx diff --git a/Packages/com.unity.visualeffectgraph/Editor/Templates/05_Head_Trail.vfx.meta b/Packages/com.unity.visualeffectgraph/Editor/Templates/Head_Trail.vfx.meta similarity index 98% rename from Packages/com.unity.visualeffectgraph/Editor/Templates/05_Head_Trail.vfx.meta rename to Packages/com.unity.visualeffectgraph/Editor/Templates/Head_Trail.vfx.meta index 8324dded41e..6d1f3b778fb 100644 --- a/Packages/com.unity.visualeffectgraph/Editor/Templates/05_Head_Trail.vfx.meta +++ b/Packages/com.unity.visualeffectgraph/Editor/Templates/Head_Trail.vfx.meta @@ -12,6 +12,7 @@ VisualEffectImporter: spells, magic abilities, missile trails, etc. icon: {fileID: 2800000, guid: caebc41a219a402419aebd2158be2e8c, type: 3} thumbnail: {fileID: 2800000, guid: a7160eac4dcba2b478fa804f93baed89, type: 3} + order: 4 useAsTemplate: 1 userData: assetBundleName: diff --git a/Packages/com.unity.visualeffectgraph/Editor/Templates/01_Minimal_System.vfx b/Packages/com.unity.visualeffectgraph/Editor/Templates/Minimal_System.vfx similarity index 100% rename from Packages/com.unity.visualeffectgraph/Editor/Templates/01_Minimal_System.vfx rename to Packages/com.unity.visualeffectgraph/Editor/Templates/Minimal_System.vfx diff --git a/Packages/com.unity.visualeffectgraph/Editor/Templates/01_Minimal_System.vfx.meta b/Packages/com.unity.visualeffectgraph/Editor/Templates/Minimal_System.vfx.meta similarity index 97% rename from Packages/com.unity.visualeffectgraph/Editor/Templates/01_Minimal_System.vfx.meta rename to Packages/com.unity.visualeffectgraph/Editor/Templates/Minimal_System.vfx.meta index 6a71252ce0c..7711d224888 100644 --- a/Packages/com.unity.visualeffectgraph/Editor/Templates/01_Minimal_System.vfx.meta +++ b/Packages/com.unity.visualeffectgraph/Editor/Templates/Minimal_System.vfx.meta @@ -11,6 +11,7 @@ VisualEffectImporter: setup.' icon: {fileID: 2800000, guid: cfaffeb6eec377d43902caf7b0b9c935, type: 3} thumbnail: {fileID: 2800000, guid: 6e3e22332cef02447956ecfdf9352294, type: 3} + order: 0 useAsTemplate: 1 userData: assetBundleName: diff --git a/Packages/com.unity.visualeffectgraph/Editor/Templates/03_Simple_Burst.vfx b/Packages/com.unity.visualeffectgraph/Editor/Templates/Simple_Burst.vfx similarity index 99% rename from Packages/com.unity.visualeffectgraph/Editor/Templates/03_Simple_Burst.vfx rename to Packages/com.unity.visualeffectgraph/Editor/Templates/Simple_Burst.vfx index 6e857429adb..40c958b9c44 100644 --- a/Packages/com.unity.visualeffectgraph/Editor/Templates/03_Simple_Burst.vfx +++ b/Packages/com.unity.visualeffectgraph/Editor/Templates/Simple_Burst.vfx @@ -77,7 +77,7 @@ VisualEffectResource: m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_Name: 03_Simple_Burst + m_Name: Simple_Burst m_Graph: {fileID: 114350483966674976} m_Infos: m_RendererSettings: diff --git a/Packages/com.unity.visualeffectgraph/Editor/Templates/03_Simple_Burst.vfx.meta b/Packages/com.unity.visualeffectgraph/Editor/Templates/Simple_Burst.vfx.meta similarity index 98% rename from Packages/com.unity.visualeffectgraph/Editor/Templates/03_Simple_Burst.vfx.meta rename to Packages/com.unity.visualeffectgraph/Editor/Templates/Simple_Burst.vfx.meta index df783acf506..3fca09fd999 100644 --- a/Packages/com.unity.visualeffectgraph/Editor/Templates/03_Simple_Burst.vfx.meta +++ b/Packages/com.unity.visualeffectgraph/Editor/Templates/Simple_Burst.vfx.meta @@ -11,6 +11,7 @@ VisualEffectImporter: effects such as hits, impacts, explosions, sparks, etc. icon: {fileID: 2800000, guid: 521a2f1ecfc48fe41bb234842f8c93cc, type: 3} thumbnail: {fileID: 2800000, guid: edc22bdb6d4484a4e87b7ff5a619008b, type: 3} + order: 2 useAsTemplate: 1 userData: assetBundleName: diff --git a/Packages/com.unity.visualeffectgraph/Editor/Templates/02_Simple_Loop.vfx b/Packages/com.unity.visualeffectgraph/Editor/Templates/Simple_Loop.vfx similarity index 99% rename from Packages/com.unity.visualeffectgraph/Editor/Templates/02_Simple_Loop.vfx rename to Packages/com.unity.visualeffectgraph/Editor/Templates/Simple_Loop.vfx index b234b31aca9..51e7dcf72cf 100644 --- a/Packages/com.unity.visualeffectgraph/Editor/Templates/02_Simple_Loop.vfx +++ b/Packages/com.unity.visualeffectgraph/Editor/Templates/Simple_Loop.vfx @@ -77,7 +77,7 @@ VisualEffectResource: m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_Name: 02_Simple_Loop + m_Name: Simple_Loop m_Graph: {fileID: 114350483966674976} m_Infos: m_RendererSettings: diff --git a/Packages/com.unity.visualeffectgraph/Editor/Templates/02_Simple_Loop.vfx.meta b/Packages/com.unity.visualeffectgraph/Editor/Templates/Simple_Loop.vfx.meta similarity index 98% rename from Packages/com.unity.visualeffectgraph/Editor/Templates/02_Simple_Loop.vfx.meta rename to Packages/com.unity.visualeffectgraph/Editor/Templates/Simple_Loop.vfx.meta index 41757265762..094f1c18506 100644 --- a/Packages/com.unity.visualeffectgraph/Editor/Templates/02_Simple_Loop.vfx.meta +++ b/Packages/com.unity.visualeffectgraph/Editor/Templates/Simple_Loop.vfx.meta @@ -12,6 +12,7 @@ VisualEffectImporter: smoke stacks, rain, waterfalls, etc." icon: {fileID: 2800000, guid: 05467359047240c4d96993adad333488, type: 3} thumbnail: {fileID: 2800000, guid: 4a42d40ceca84d442be405142e3f9e18, type: 3} + order: 1 useAsTemplate: 1 userData: assetBundleName: diff --git a/Packages/com.unity.visualeffectgraph/Editor/Templates/04_Simple_Trail.vfx b/Packages/com.unity.visualeffectgraph/Editor/Templates/Simple_Trail.vfx similarity index 100% rename from Packages/com.unity.visualeffectgraph/Editor/Templates/04_Simple_Trail.vfx rename to Packages/com.unity.visualeffectgraph/Editor/Templates/Simple_Trail.vfx diff --git a/Packages/com.unity.visualeffectgraph/Editor/Templates/04_Simple_Trail.vfx.meta b/Packages/com.unity.visualeffectgraph/Editor/Templates/Simple_Trail.vfx.meta similarity index 98% rename from Packages/com.unity.visualeffectgraph/Editor/Templates/04_Simple_Trail.vfx.meta rename to Packages/com.unity.visualeffectgraph/Editor/Templates/Simple_Trail.vfx.meta index 5bfb9535d8b..17f31ecb731 100644 --- a/Packages/com.unity.visualeffectgraph/Editor/Templates/04_Simple_Trail.vfx.meta +++ b/Packages/com.unity.visualeffectgraph/Editor/Templates/Simple_Trail.vfx.meta @@ -12,6 +12,7 @@ VisualEffectImporter: trails, etc. icon: {fileID: 2800000, guid: e5a019bfd128b514ba2b9e0b2a60fc67, type: 3} thumbnail: {fileID: 2800000, guid: 9daa3fda7c4df3f4d8ce3e8a479b199b, type: 3} + order: 3 useAsTemplate: 1 userData: assetBundleName: diff --git a/Packages/com.unity.visualeffectgraph/Editor/UIResources/uss/VFXSystemBorder.uss b/Packages/com.unity.visualeffectgraph/Editor/UIResources/uss/VFXSystemBorder.uss index d017a06198b..1340e0b7bc1 100644 --- a/Packages/com.unity.visualeffectgraph/Editor/UIResources/uss/VFXSystemBorder.uss +++ b/Packages/com.unity.visualeffectgraph/Editor/UIResources/uss/VFXSystemBorder.uss @@ -16,14 +16,7 @@ VFXSystemBorder.graphElement Label border-left-width: 1px; border-right-width: 1px; border-bottom-width: 1px; - border-top-width: 1px; - border-left-width: 1px; - border-right-width: 1px; - border-bottom-width: 1px; - margin-left: 12px; - margin-right: 12px; - margin-top: 1px; - margin-bottom: 1px; + margin: 1px 12px; padding-left:0; padding-right:0; } @@ -36,52 +29,25 @@ VFXSystemBorder.graphElement Label:hover #title-field { - position: absolute; + margin: 1px 10px; } -#title, #title-field +#title, #unity-text-input { + color:#17AB6A; font-size: 48px; white-space: normal; } #title { - color:#17AB6A; opacity:0.75; -} - -#title.empty -{ - height : 12px; -} - -VFXSystemBorder.graphElement TextField#title-field -{ - padding-left:0; - padding-right:0; - padding-top:0; - padding-bottom:0; - margin-left:0; - margin-right:0; - margin-top:0; - margin-bottom:0; - - position: absolute; + min-height: 32px; } VFXSystemBorder.graphElement TextField#title-field #unity-text-input { - padding-left:0; - padding-right:0; - padding-top:0; - padding-bottom:0; - margin-left:0; - margin-right:0; - margin-top:0; - margin-bottom:0; - background-color:rgba(0,0,0,0); - background-image:none; - font-size: 48px; - white-space: normal; + padding: 0; + margin: 0; + background-color: transparent; } diff --git a/Packages/com.unity.visualeffectgraph/Editor/Utils/VFXSystemBorder.cs b/Packages/com.unity.visualeffectgraph/Editor/Utils/VFXSystemBorder.cs index 3081e6e923d..48c39550bce 100644 --- a/Packages/com.unity.visualeffectgraph/Editor/Utils/VFXSystemBorder.cs +++ b/Packages/com.unity.visualeffectgraph/Editor/Utils/VFXSystemBorder.cs @@ -1,8 +1,7 @@ -using System.Collections; -using System.Collections.Generic; using UnityEngine; using UnityEngine.UIElements; using UnityEditor.Experimental.GraphView; + using System; using System.Linq; @@ -61,6 +60,7 @@ protected override void ImmediateRepaint() } Material m_Mat; + bool m_WaitingRecompute; static Mesh s_Mesh; @@ -76,16 +76,16 @@ public VFXSystemBorder() this.style.overflow = Overflow.Visible; m_Title = this.Query