diff --git a/Packages/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbeVolumeBuildProcessor.cs b/Packages/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbeVolumeBuildProcessor.cs index 62805d21890..65552dc6aaf 100644 --- a/Packages/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbeVolumeBuildProcessor.cs +++ b/Packages/com.unity.render-pipelines.core/Editor/Lighting/ProbeVolume/ProbeVolumeBuildProcessor.cs @@ -150,7 +150,11 @@ public override void PrepareForBuild(BuildPlayerContext buildPlayerContext) IncludeStreamableAsset(scenario.Value.cellOptionalDataAsset, basePath, useStreamingAsset); else StripStreambleAsset(scenario.Value.cellOptionalDataAsset); - IncludeStreamableAsset(scenario.Value.cellProbeOcclusionDataAsset, basePath, useStreamingAsset); + + if (bakingSet.bakedProbeOcclusion) + IncludeStreamableAsset(scenario.Value.cellProbeOcclusionDataAsset, basePath, useStreamingAsset); + else + StripStreambleAsset(scenario.Value.cellProbeOcclusionDataAsset); } s_BakingSetsProcessedLastBuild.Add(bakingSet); 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 a6782816899..c556fbdce65 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 @@ -48,7 +48,6 @@ static class Styles public static readonly string msgEnableAll = "Some loaded Scenes are disabled by this Baking Set. These Scenes will not contribute to the generation of probe data."; public static readonly string msgUnloadOther = "Scene(s) not belonging to this Baking Set are currently loaded in the Hierarchy. This might result in incorrect lighting."; public static readonly string msgLoadForBake = "Some scene(s) in this Baking Set are not currently loaded in the Hierarchy. This might result in missing or incomplete lighting."; - public static readonly string msgIncorrectBakingSet = "The currently loaded scenes do not match the assigned Baking Set. To resolve this, make sure to load the scenes that correspond to the assigned Baking Set, unload any scenes that don’t belong, and verify the scene setup before generating lighting to ensure everything bakes correctly."; public const float statusLabelWidth = 80; @@ -222,18 +221,6 @@ public override void OnGUI() Initialize(); var prv = ProbeReferenceVolume.instance; - // The CurrentBakingSet may change when you open an additional Scene with APV - if (prv.currentBakingSet != activeSet) - { - if (prv.currentBakingSet == null) - { - m_SingleSceneMode = true; - } - else - { - OpenBakingSet(prv.currentBakingSet); - } - } // In single scene mode, user can't control active set, so we automatically create a new one // in case the active scene doesn't have a baking set so that we can display baking settings @@ -372,9 +359,8 @@ void BakingGUI() { if (newSet != null) { EditorUtility.SetDirty(newSet); newSet.singleSceneMode = false; } activeSet = newSet; - + ProbeReferenceVolume.instance.Clear(); - ProbeReferenceVolume.instance.SetActiveBakingSet(activeSet); } if (activeSet != null) @@ -497,24 +483,6 @@ void ShowWarnings() } } - if (activeSet == ProbeReferenceVolume.instance.currentBakingSet) - { - hasWarnings = true; - foreach (var sceneDataList in ProbeReferenceVolume.instance.perSceneDataList) - { - if (sceneDataList.bakingSet == activeSet) - { - hasWarnings = false; - break; - } - } - - if (hasWarnings) - { - EditorGUILayout.HelpBox(Styles.msgIncorrectBakingSet, MessageType.Warning); - } - } - scenesForBake.Clear(); scenesToUnload.Clear(); scenesToEnable.Clear(); diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/.buginfo b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/.buginfo index 3561c5aedbc..e5dac0e764f 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/.buginfo +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/.buginfo @@ -1 +1 @@ -area: Graphics Optimization Systems \ No newline at end of file +area: GPU Resident Drawer 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 1c459a8e22a..1b7c36dc77e 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/GPUResidentDrawer.cs +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/GPUResidentDrawer.cs @@ -534,7 +534,7 @@ private void UpdateSelection() rendererIDs[i] = renderers[i] ? renderers[i].GetInstanceID() : 0; m_Batcher.UpdateSelectedRenderers(rendererIDs); - + rendererIDs.Dispose(); Profiler.EndSample(); @@ -630,7 +630,7 @@ private void PostPostLateUpdate() UpdateSelection(); m_IsSelectionDirty = false; } - + m_FrameUpdateNeeded = false; #endif } @@ -830,14 +830,8 @@ private void ClassifyMaterials(NativeArray materials, out NativeList u if (materials.Length > 0) { - new ClassifyMaterialsJob - { - materialIDs = materials.AsReadOnly(), - batchMaterialHash = m_Batcher.instanceCullingBatcher.batchMaterialHash.AsReadOnly(), - unsupportedMaterialIDs = unsupportedMaterials, - supportedMaterialIDs = supportedMaterials, - supportedPackedMaterialDatas = supportedPackedMaterialDatas - }.Run(); + GPUResidentDrawerBurst.ClassifyMaterials(materials, m_Batcher.instanceCullingBatcher.batchMaterialHash.AsReadOnly(), + ref supportedMaterials, ref unsupportedMaterials, ref supportedPackedMaterialDatas); } } @@ -847,13 +841,8 @@ private NativeList FindUnsupportedRenderers(NativeArray unsupportedMat if (unsupportedMaterials.Length > 0) { - new FindUnsupportedRenderersJob - { - unsupportedMaterials = unsupportedMaterials.AsReadOnly(), - materialIDArrays = m_BatchersContext.sharedInstanceData.materialIDArrays, - rendererGroups = m_BatchersContext.sharedInstanceData.rendererGroupIDs, - unsupportedRenderers = unsupportedRenderers, - }.Run(); + GPUResidentDrawerBurst.FindUnsupportedRenderers(unsupportedMaterials, m_BatchersContext.sharedInstanceData.materialIDArrays, + m_BatchersContext.sharedInstanceData.rendererGroupIDs, ref unsupportedRenderers); } return unsupportedRenderers; @@ -863,13 +852,8 @@ private NativeHashSet GetMaterialsWithChangedPackedMaterial(NativeArray filteredMaterials = new NativeHashSet(materials.Length, allocator); - new GetMaterialsWithChangedPackedMaterialJob - { - materialIDs = materials.AsReadOnly(), - packedMaterialDatas = packedMaterialDatas.AsReadOnly(), - packedMaterialHash = batcher.instanceCullingBatcher.packedMaterialHash.AsReadOnly(), - filteredMaterials = filteredMaterials - }.Run(); + GPUResidentDrawerBurst.GetMaterialsWithChangedPackedMaterial(materials, packedMaterialDatas, + batcher.instanceCullingBatcher.packedMaterialHash.AsReadOnly(), ref filteredMaterials); return filteredMaterials; } @@ -892,79 +876,6 @@ private NativeList FindRenderersFromMaterials(NativeArray sortedExclud return renderers; } - [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] - private struct ClassifyMaterialsJob : IJob - { - [ReadOnly] public NativeParallelHashMap.ReadOnly batchMaterialHash; - [ReadOnly] public NativeArray.ReadOnly materialIDs; - - public NativeList supportedMaterialIDs; - public NativeList unsupportedMaterialIDs; - public NativeList supportedPackedMaterialDatas; - - public void Execute() - { - var usedMaterialIDs = new NativeList(4, Allocator.TempJob); - - 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)] - private struct FindUnsupportedRenderersJob : IJob - { - [ReadOnly] public NativeArray.ReadOnly unsupportedMaterials; - [ReadOnly] public NativeArray.ReadOnly materialIDArrays; - [ReadOnly] public NativeArray.ReadOnly rendererGroups; - - public NativeList unsupportedRenderers; - - public unsafe void Execute() - { - if (unsupportedMaterials.Length == 0) - return; - - for (int arrayIndex = 0; arrayIndex < materialIDArrays.Length; arrayIndex++) - { - var materialIDs = materialIDArrays[arrayIndex]; - int rendererID = rendererGroups[arrayIndex]; - - for (int i = 0; i < materialIDs.Length; i++) - { - int materialID = materialIDs[i]; - - if (unsupportedMaterials.Contains(materialID)) - { - unsupportedRenderers.Add(rendererID); - break; - } - } - } - } - } - [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] private unsafe struct FindRenderersFromMaterialJob : IJobParallelForBatch { @@ -1008,30 +919,5 @@ public void Execute(int startIndex, int count) selectedRenderGroups.AddRangeNoResize(renderersToAddPtr, renderersToAdd.Length); } } - - [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] - private struct GetMaterialsWithChangedPackedMaterialJob : IJob - { - [ReadOnly] public NativeArray.ReadOnly materialIDs; - [ReadOnly] public NativeArray.ReadOnly packedMaterialDatas; - [ReadOnly] public NativeParallelHashMap.ReadOnly packedMaterialHash; - - [WriteOnly] public NativeHashSet filteredMaterials; - - public void Execute() - { - 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 b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/GPUResidentDrawerBurst.cs new file mode 100644 index 00000000000..ddb0db8c566 --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/GPUResidentDrawerBurst.cs @@ -0,0 +1,80 @@ +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]; + int rendererID = rendererGroups[arrayIndex]; + + for (int i = 0; i < materialIDs.Length; i++) + { + int 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 new file mode 100644 index 00000000000..ececc7dd30e --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/GPUResidentDrawerBurst.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 2f50a3b1f0997d342837e27ab3b95e6f \ No newline at end of file diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceCuller.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceCuller.cs index 8fcbfa9c336..ed9bf2e6f13 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceCuller.cs +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceCuller.cs @@ -1812,29 +1812,6 @@ private JobHandle AnimateCrossFades(CPUPerCameraInstanceData perCameraInstanceDa return handle; } - [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] - private unsafe struct SetupCullingJobInput : IJob - { - public float lodBias; - public float meshLodThreshold; - [NativeDisableUnsafePtrRestriction] public BatchCullingContext* context; - [NativeDisableUnsafePtrRestriction] public ReceiverPlanes* receiverPlanes; - [NativeDisableUnsafePtrRestriction] public ReceiverSphereCuller* receiverSphereCuller; - [NativeDisableUnsafePtrRestriction] public FrustumPlaneCuller* frustumPlaneCuller; - [NativeDisableUnsafePtrRestriction] public float* screenRelativeMetric; - [NativeDisableUnsafePtrRestriction] public float* meshLodConstant; - - public void Execute() - { - *receiverPlanes = ReceiverPlanes.Create(*context, Allocator.TempJob); - *receiverSphereCuller = ReceiverSphereCuller.Create(*context, Allocator.TempJob); - *frustumPlaneCuller = FrustumPlaneCuller.Create(*context, receiverPlanes->planes.AsArray(), *receiverSphereCuller, Allocator.TempJob); - *screenRelativeMetric = LODRenderingUtils.CalculateScreenRelativeMetricNoBias(context->lodParameters); - *meshLodConstant = LODRenderingUtils.CalculateMeshLodConstant(context->lodParameters, *screenRelativeMetric, meshLodThreshold); - *screenRelativeMetric /= lodBias; - } - } - private unsafe JobHandle CreateFrustumCullingJob( in BatchCullingContext cc, in CPUInstanceData.ReadOnly instanceData, @@ -1848,7 +1825,7 @@ private unsafe JobHandle CreateFrustumCullingJob( NativeArray rendererMeshLodSettings, NativeArray rendererCrossFadeValues) { - Assert.IsTrue(cc.cullingSplits.Length <= 6, "InstanceCullingBatcher supports up to 6 culling splits."); + Assert.IsTrue(cc.cullingSplits.Length <= 6, "InstanceCuller supports up to 6 culling splits."); ReceiverPlanes receiverPlanes; ReceiverSphereCuller receiverSphereCuller; @@ -1858,17 +1835,8 @@ private unsafe JobHandle CreateFrustumCullingJob( fixed (BatchCullingContext* contextPtr = &cc) { - new SetupCullingJobInput() - { - lodBias = QualitySettings.lodBias, - meshLodThreshold = QualitySettings.meshLodThreshold, - context = contextPtr, - frustumPlaneCuller = &frustumPlaneCuller, - receiverPlanes = &receiverPlanes, - receiverSphereCuller = &receiverSphereCuller, - screenRelativeMetric = &screenRelativeMetric, - meshLodConstant = &meshLodConstant, - }.Run(); + InstanceCullerBurst.SetupCullingJobInput(QualitySettings.lodBias, QualitySettings.meshLodThreshold, contextPtr, &receiverPlanes, &receiverSphereCuller, + &frustumPlaneCuller, &screenRelativeMetric, &meshLodConstant); } if (occlusionCullingCommon != null) diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceCullerBurst.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceCullerBurst.cs new file mode 100644 index 00000000000..fc4c27e8f04 --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceCullerBurst.cs @@ -0,0 +1,22 @@ +using Unity.Collections; +using Unity.Burst; + +namespace UnityEngine.Rendering +{ + [BurstCompile] + 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) + { + *receiverPlanes = ReceiverPlanes.Create(*context, Allocator.TempJob); + *receiverSphereCuller = ReceiverSphereCuller.Create(*context, Allocator.TempJob); + *frustumPlaneCuller = FrustumPlaneCuller.Create(*context, receiverPlanes->planes.AsArray(), *receiverSphereCuller, Allocator.TempJob); + *screenRelativeMetric = LODRenderingUtils.CalculateScreenRelativeMetricNoBias(context->lodParameters); + *meshLodConstant = LODRenderingUtils.CalculateMeshLodConstant(context->lodParameters, *screenRelativeMetric, meshLodThreshold); + *screenRelativeMetric /= lodBias; + } + } +} 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 new file mode 100644 index 00000000000..dc1b8acb849 --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceCullerBurst.cs.meta @@ -0,0 +1,2 @@ +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 index a6423ab2106..292bf6a899a 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceCullingBatcher.cs +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceCullingBatcher.cs @@ -234,74 +234,6 @@ public unsafe void Execute(int index) } } - [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] - internal struct RemoveDrawInstanceIndicesJob : IJob - { - [NativeDisableContainerSafetyRestriction, NoAlias] [ReadOnly] public NativeArray drawInstanceIndices; - - public NativeList drawInstances; - public NativeParallelHashMap rangeHash; - public NativeParallelHashMap batchHash; - public NativeList drawRanges; - public NativeList drawBatches; - - public void RemoveDrawRange(in RangeKey key) - { - int drawRangeIndex = rangeHash[key]; - - ref DrawRange lastDrawRange = ref drawRanges.ElementAt(drawRanges.Length - 1); - rangeHash[lastDrawRange.key] = drawRangeIndex; - - rangeHash.Remove(key); - drawRanges.RemoveAtSwapBack(drawRangeIndex); - } - - public void RemoveDrawBatch(in DrawKey key) - { - 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 DrawBatch lastDrawBatch = ref drawBatches.ElementAt(drawBatches.Length - 1); - batchHash[lastDrawBatch.key] = drawBatchIndex; - - batchHash.Remove(key); - drawBatches.RemoveAtSwapBack(drawBatchIndex); - } - - public unsafe void Execute() - { - 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); - - UnsafeUtility.MemCpy(drawInstance, drawInstancesPtr + drawInstancesNewBack--, sizeof(DrawInstance)); - } - - drawInstances.ResizeUninitialized(drawInstancesNewBack + 1); - } - } - [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] internal struct UpdatePackedMaterialDataCacheJob : IJob { @@ -329,252 +261,6 @@ public void Execute() } } - [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] - internal struct CreateDrawBatchesJob : IJob - { - [ReadOnly] public bool implicitInstanceIndices; - [ReadOnly] public NativeArray instances; - [ReadOnly] public GPUDrivenRendererGroupData rendererData; - [ReadOnly] public NativeParallelHashMap.ReadOnly batchMeshHash; - [ReadOnly] public NativeParallelHashMap.ReadOnly batchMaterialHash; - [ReadOnly] public NativeParallelHashMap.ReadOnly packedMaterialDataHash; - - public NativeParallelHashMap rangeHash; - public NativeList drawRanges; - public NativeParallelHashMap batchHash; - public NativeList drawBatches; - - [WriteOnly] public NativeList drawInstances; - - private ref DrawRange EditDrawRange(in RangeKey key) - { - 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 ref DrawBatch EditDrawBatch(in DrawKey key, in SubMeshDescriptor subMeshDescriptor) - { - 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; - } - - public void ProcessRenderer(int i) - { - 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); - - 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 == 0) - { - 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 : 0, - 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); - - 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 }); - } - } - } - } - - public void Execute() - { - for (int i = 0; i < rendererData.rendererGroupID.Length; ++i) - ProcessRenderer(i); - } - } - internal class CPUDrawInstanceData { public NativeList drawInstances => m_DrawInstances; @@ -672,23 +358,16 @@ public void RebuildDrawListsIfNeeded() internalDrawIndex.Dispose(); } - public unsafe void DestroyDrawInstanceIndices(NativeArray drawInstanceIndicesToDestroy) + public void DestroyDrawInstanceIndices(NativeArray drawInstanceIndicesToDestroy) { Profiler.BeginSample("DestroyDrawInstanceIndices.ParallelSort"); drawInstanceIndicesToDestroy.ParallelSort().Complete(); Profiler.EndSample(); - var removeDrawInstanceIndicesJob = new RemoveDrawInstanceIndicesJob - { - drawInstanceIndices = drawInstanceIndicesToDestroy, - drawInstances = m_DrawInstances, - drawBatches = m_DrawBatches, - drawRanges = m_DrawRanges, - batchHash = m_BatchHash, - rangeHash = m_RangeHash - }; - - removeDrawInstanceIndicesJob.Run(); + 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) @@ -1097,20 +776,14 @@ public void BuildBatch( RegisterBatchMeshes(usedMeshIDs); } - new CreateDrawBatchesJob - { - implicitInstanceIndices = rendererData.instancesCount.Length == 0, - instances = instances, - rendererData = rendererData, - batchMeshHash = m_BatchMeshHash.AsReadOnly(), - batchMaterialHash = m_BatchMaterialHash.AsReadOnly(), - packedMaterialDataHash = m_PackedMaterialHash.AsReadOnly(), - rangeHash = m_DrawInstanceData.rangeHash, - drawRanges = m_DrawInstanceData.drawRanges, - batchHash = m_DrawInstanceData.batchHash, - drawBatches = m_DrawInstanceData.drawBatches, - drawInstances = m_DrawInstanceData.drawInstances - }.Run(); + 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(); diff --git a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceCullingBatcherBurst.cs b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceCullingBatcherBurst.cs new file mode 100644 index 00000000000..f429d38b291 --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceCullingBatcherBurst.cs @@ -0,0 +1,309 @@ +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 == 0) + { + 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 : 0, + 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 new file mode 100644 index 00000000000..1b344d835a7 --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceCullingBatcherBurst.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 07b72b6a7afa9b448b3103bb66d57ca0 \ No newline at end of file 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 index 22f3ce514ba..a31a90a4a80 100644 --- 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 @@ -482,224 +482,6 @@ public void Execute(int chunk_index) } } - [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] - private struct ReallocateInstancesJob : IJob - { - [ReadOnly] public bool implicitInstanceIndices; - [ReadOnly] public NativeArray rendererGroupIDs; - [ReadOnly] public NativeArray packedRendererData; - [ReadOnly] public NativeArray instanceOffsets; - [ReadOnly] public NativeArray instanceCounts; - - public InstanceAllocators instanceAllocators; - public CPUInstanceData instanceData; - [NativeDisableContainerSafetyRestriction] public CPUPerCameraInstanceData perCameraInstanceData; - public CPUSharedInstanceData sharedInstanceData; - public NativeArray instances; - public NativeParallelMultiHashMap rendererGroupInstanceMultiHash; - - public void Execute() - { - 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)] - private struct FreeInstancesJob : IJob - { - [ReadOnly] public NativeArray instances; - - public InstanceAllocators instanceAllocators; - public CPUInstanceData instanceData; - - [NativeDisableContainerSafetyRestriction] public CPUPerCameraInstanceData perCameraInstanceData; - public CPUSharedInstanceData sharedInstanceData; - public NativeParallelMultiHashMap rendererGroupInstanceMultiHash; - - public void Execute() - { - 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); - } - } - } - } - - [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] - private struct FreeRendererGroupInstancesJob : IJob - { - [ReadOnly] public NativeArray rendererGroupsID; - - public InstanceAllocators instanceAllocators; - public CPUInstanceData instanceData; - [NativeDisableContainerSafetyRestriction] public CPUPerCameraInstanceData perCameraInstanceData; - public CPUSharedInstanceData sharedInstanceData; - public NativeParallelMultiHashMap rendererGroupInstanceMultiHash; - - public void Execute() - { - 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)] private unsafe struct UpdateRendererInstancesJob : IJobParallelFor { 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 index 64a11c20539..7db7f119fce 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceDataSystem.cs +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceDataSystem.cs @@ -484,24 +484,21 @@ public unsafe void ReallocateAndGetInstances(in GPUDrivenRendererGroupData rende Assert.IsTrue(m_InstanceData.instancesCapacity == m_PerCameraInstanceData.instancesCapacity); m_SharedInstanceData.EnsureFreeInstances(newSharedInstancesCount); - new ReallocateInstancesJob { implicitInstanceIndices = implicitInstanceIndices, rendererGroupInstanceMultiHash = m_RendererGroupInstanceMultiHash, - instanceAllocators = m_InstanceAllocators, sharedInstanceData = m_SharedInstanceData, instanceData = m_InstanceData, perCameraInstanceData = m_PerCameraInstanceData, - rendererGroupIDs = rendererData.rendererGroupID, packedRendererData = rendererData.packedRendererData, instanceOffsets = rendererData.instancesOffset, - instanceCounts = rendererData.instancesCount, instances = instances }.Run(); + 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) { - new FreeRendererGroupInstancesJob { rendererGroupInstanceMultiHash = m_RendererGroupInstanceMultiHash, - instanceAllocators = m_InstanceAllocators, sharedInstanceData = m_SharedInstanceData, instanceData = m_InstanceData, perCameraInstanceData = m_PerCameraInstanceData, - rendererGroupsID = rendererGroupsID }.Run(); + InstanceDataSystemBurst.FreeRendererGroupInstances(rendererGroupsID.AsReadOnly(), ref m_InstanceAllocators, ref m_InstanceData, + ref m_PerCameraInstanceData, ref m_SharedInstanceData, ref m_RendererGroupInstanceMultiHash); } public void FreeInstances(NativeArray instances) { - new FreeInstancesJob { rendererGroupInstanceMultiHash = m_RendererGroupInstanceMultiHash, - instanceAllocators = m_InstanceAllocators, sharedInstanceData = m_SharedInstanceData, instanceData = m_InstanceData, perCameraInstanceData = m_PerCameraInstanceData, - instances = instances }.Run(); + 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) 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 new file mode 100644 index 00000000000..2f62c89a558 --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceDataSystemBurst.cs @@ -0,0 +1,195 @@ +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 new file mode 100644 index 00000000000..8613808fb3e --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/InstanceData/InstanceDataSystemBurst.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 302f596d55264be4ba359e52ec407766 \ 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 index b290404e81c..33e07168a0f 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/LODGroupDataPool.cs +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/LODGroupDataPool.cs @@ -86,52 +86,6 @@ public unsafe void Execute(int index) } } - [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] - internal unsafe struct AllocateOrGetLODGroupDataInstancesJob : IJob - { - [ReadOnly] public NativeArray lodGroupsID; - - public NativeList lodGroupsData; - public NativeList lodGroupCullingData; - public NativeParallelHashMap lodGroupDataHash; - public NativeList freeLODGroupDataHandles; - - [WriteOnly] public NativeArray lodGroupInstances; - - [NativeDisableUnsafePtrRestriction] public int* previousRendererCount; - - public void Execute() - { - int freeHandlesCount = freeLODGroupDataHandles.Length; - int lodDataLength = lodGroupsData.Length; - - for (int i = 0; i < lodGroupsID.Length; ++i) - { - int 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); - } - } - [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] internal unsafe struct UpdateLODGroupDataJob : IJobParallelFor { @@ -219,38 +173,6 @@ public void Execute(int index) } } - [BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)] - internal unsafe struct FreeLODGroupDataJob : IJob - { - [ReadOnly] public NativeArray destroyedLODGroupsID; - - public NativeList lodGroupsData; - public NativeParallelHashMap lodGroupDataHash; - public NativeList freeLODGroupDataHandles; - - [NativeDisableUnsafePtrRestriction] public int* removedRendererCount; - - public void Execute() - { - foreach (int 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; - } - } - } - } - internal class LODGroupDataPool : IDisposable { private NativeList m_LODGroupData; @@ -329,18 +251,9 @@ public unsafe void UpdateLODGroupData(in GPUDrivenLODGroupData inputData) var lodGroupInstances = new NativeArray(inputData.lodGroupID.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory); - int previousRendererCount = 0; - - new AllocateOrGetLODGroupDataInstancesJob - { - lodGroupsID = inputData.lodGroupID, - lodGroupsData = m_LODGroupData, - lodGroupCullingData = m_LODGroupCullingData, - lodGroupDataHash = m_LODGroupDataHash, - freeLODGroupDataHandles = m_FreeLODGroupDataHandles, - lodGroupInstances = lodGroupInstances, - previousRendererCount = &previousRendererCount - }.Run(); + 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); @@ -367,21 +280,12 @@ public unsafe void UpdateLODGroupData(in GPUDrivenLODGroupData inputData) lodGroupInstances.Dispose(); } - public unsafe void FreeLODGroupData(NativeArray destroyedLODGroupsID) + public void FreeLODGroupData(NativeArray destroyedLODGroupsID) { if (destroyedLODGroupsID.Length == 0) return; - int removedRendererCount = 0; - - new FreeLODGroupDataJob - { - destroyedLODGroupsID = destroyedLODGroupsID, - lodGroupsData = m_LODGroupData, - lodGroupDataHash = m_LODGroupDataHash, - freeLODGroupDataHandles = m_FreeLODGroupDataHandles, - removedRendererCount = &removedRendererCount - }.Run(); + 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 new file mode 100644 index 00000000000..efe0342a9fe --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/LODGroupDataPoolBurst.cs @@ -0,0 +1,72 @@ +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 (int 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) + { + int 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 new file mode 100644 index 00000000000..6e2ab16b7ac --- /dev/null +++ b/Packages/com.unity.render-pipelines.core/Runtime/GPUDriven/LODGroupDataPoolBurst.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: ad57195e4230c9344a64d902de871991 \ No newline at end of file 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 4a7c581a9e0..b23c7e8d2f2 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 @@ -118,7 +118,7 @@ internal enum NativeCompilerProfileId NRPRGComp_TryMergeNativePasses, NRPRGComp_FindResourceUsageRanges, NRPRGComp_DetectMemorylessResources, - NRPRGComp_ExecuteCreateResources, + NRPRGComp_ExecuteInitializeResources, NRPRGComp_ExecuteBeginRenderpassCommand, NRPRGComp_ExecuteDestroyResources, } @@ -731,10 +731,9 @@ internal static bool IsSameNativeSubPass(ref SubPassDescriptor a, ref SubPassDes return true; } - - private void ExecuteCreateRessource(InternalRenderGraphContext rgContext, RenderGraphResourceRegistry resources, in PassData pass) + private void ExecuteInitializeResource(InternalRenderGraphContext rgContext, RenderGraphResourceRegistry resources, in PassData pass) { - using (new ProfilingScope(ProfilingSampler.Get(NativeCompilerProfileId.NRPRGComp_ExecuteCreateResources))) + using (new ProfilingScope(ProfilingSampler.Get(NativeCompilerProfileId.NRPRGComp_ExecuteInitializeResources))) { resources.forceManualClearOfResource = true; @@ -749,15 +748,23 @@ private void ExecuteCreateRessource(InternalRenderGraphContext rgContext, Render foreach (ref readonly var res in subPass.FirstUsedResources(contextData)) { ref readonly var resInfo = ref contextData.UnversionedResourceData(res); - if (resInfo.isImported == false && resInfo.memoryLess == false) + if (!resInfo.memoryLess) { - bool usedAsFragmentThisPass = subPass.IsUsedAsFragment(res, contextData); + if (!resInfo.isImported) + { + bool usedAsFragmentThisPass = subPass.IsUsedAsFragment(res, contextData); - // This resource is read for the first time as a regular texture and not as a framebuffer attachment - // so we need to explicitly clear it, as loadAction.clear only works on framebuffer attachments - // TODO: Should this be a performance warning?? Maybe rare enough in practice? - resources.forceManualClearOfResource = !usedAsFragmentThisPass; - resources.CreatePooledResource(rgContext, res.iType, res.index); + // This resource is read for the first time as a regular texture and not as a framebuffer attachment + // so we need to explicitly clear it, as loadAction.clear only works on framebuffer attachments + // TODO: Should this be a performance warning?? Maybe rare enough in practice? + resources.forceManualClearOfResource = !usedAsFragmentThisPass; + resources.CreatePooledResource(rgContext, res.iType, res.index); + } + else // Imported resource + { + if (resInfo.clear) + resources.ClearResource(rgContext, res.iType, res.index); + } } } } @@ -769,10 +776,15 @@ private void ExecuteCreateRessource(InternalRenderGraphContext rgContext, Render foreach (ref readonly var create in pass.FirstUsedResources(contextData)) { ref readonly var pointTo = ref contextData.UnversionedResourceData(create); - if (pointTo.isImported == false) + if (!pointTo.isImported) { resources.CreatePooledResource(rgContext, create.iType, create.index); } + else // Imported resource + { + if (pointTo.clear) + resources.ClearResource(rgContext, create.iType, create.index); + } } } @@ -1067,7 +1079,7 @@ void DetermineLoadStoreActions(ref NativePassData nativePass) { // Depth attachment always comes first if existing bool isDepthAttachment = (nativePass.hasDepth && nativePass.attachments.size == 0); - + // For color attachment, we only discard the MSAA buffers and keep the resolve texture // This is a design decision due to the restrictive ImportResourceParams API, it could be revised later storeAction = isDepthAttachment @@ -1103,7 +1115,7 @@ void DetermineLoadStoreActions(ref NativePassData nativePass) throw new Exception("Resource was marked as memoryless but is trying to store or resolve."); #endif } - + var newAttachment = new NativePassAttachment( handle, loadAction, @@ -1112,7 +1124,7 @@ void DetermineLoadStoreActions(ref NativePassData nativePass) mipLevel, depthSlice ); - + nativePass.attachments.Add(newAttachment); } } @@ -1204,7 +1216,7 @@ internal unsafe void ExecuteBeginRenderPass(InternalRenderGraphContext rgContext ValidateAttachment(renderTargetInfo, resources, width, height, samples, isVrs); ref var currBeginAttachment = ref m_BeginRenderPassAttachments.ElementAt(i); - currBeginAttachment = new AttachmentDescriptor(renderTargetInfo.format); + currBeginAttachment = new AttachmentDescriptor(renderTargetInfo.format); // Set up the RT pointers if (attachments[i].memoryless == false) @@ -1429,7 +1441,7 @@ public void ExecuteGraph(InternalRenderGraphContext rgContext, RenderGraphResour var isRaster = pass.type == RenderGraphPassType.Raster; - ExecuteCreateRessource(rgContext, resources, pass); + ExecuteInitializeResource(rgContext, resources, pass); var isAsyncCompute = pass.type == RenderGraphPassType.Compute && pass.asyncCompute == true; if (isAsyncCompute) @@ -1553,4 +1565,4 @@ public void ExecuteGraph(InternalRenderGraphContext rgContext, RenderGraphResour } } } -} \ No newline at end of file +} diff --git a/Packages/com.unity.render-pipelines.core/Runtime/RenderGraph/Compiler/PassesData.cs b/Packages/com.unity.render-pipelines.core/Runtime/RenderGraph/Compiler/PassesData.cs index 7bdd2f70aa4..2d727c66342 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/RenderGraph/Compiler/PassesData.cs +++ b/Packages/com.unity.render-pipelines.core/Runtime/RenderGraph/Compiler/PassesData.cs @@ -1152,89 +1152,63 @@ public static void TryMergeNativeSubPass(CompilerContextData contextData, ref Na passToMerge.nativeSubPassIndex = nativePass.numNativeSubPasses - 1; } - // In the case where we add a new graph pass with depth to a native pass that didn't have it, we need to update - // inputs in all the previous native subpasses of the native pass - static void UpdateNativeSubPassesAttachments(CompilerContextData contextData, ref NativePassData nativePass) + // Call this function while merging a graph pass and you need to add the depth attachment used by this graph pass + // Make sure to call it before adding the rest of the graph pass attachments and its generated subpass + void AddDepthAttachmentFirstDuringMerge(CompilerContextData contextData, in PassFragmentData depthAttachment) { - int lastVisitedNativeSubpassIdx = -1; - ref readonly var fragmentList = ref nativePass.fragments; + // Native pass can only have a single depth attachment + Debug.Assert(!hasDepth); - var countPasses = nativePass.lastGraphPass - nativePass.firstGraphPass + 1; + fragments.Add(depthAttachment); + hasDepth = true; - // Do not iterate over the last graph pass as it is the one we are currently adding - for (var graphPassIdx = 0; graphPassIdx < countPasses - 1; ++graphPassIdx) - { - // We only check the first graph pass of each existing native subpass - if other graph passes - // have been merged into a native subpass, it's because they had the same attachments. - ref readonly var currGraphPass = - ref contextData.passData.ElementAt(nativePass.firstGraphPass + graphPassIdx); + var size = fragments.size; - // Already updated this native subpass - if (currGraphPass.nativeSubPassIndex + nativePass.firstNativeSubPass == lastVisitedNativeSubpassIdx) - { - continue; - } + // If depth is the only attachment of the native pass, we are done + if (size == 1) return; - // Shouldn't be necessary since we only check the first graph pass of each existing native subpass - // But let's be safe and check anyway if the pass has been culled or not. - if (currGraphPass.culled) - { - continue; - } + // size > 1 + // In this case, we are adding depth attachment to a native pass with other existing attachments + int prevDepthIdx = size - 1; - lastVisitedNativeSubpassIdx = currGraphPass.nativeSubPassIndex + nativePass.firstNativeSubPass; - ref var nativeSubPassDescriptor = - ref contextData.nativeSubPassData.ElementAt(lastVisitedNativeSubpassIdx); + // Depth must always been the first attachment, so we switch the previous first one with the recently added depth attachment + (fragments[0], fragments[prevDepthIdx]) = (fragments[prevDepthIdx], fragments[0]); + + var depthFlag = GetSubPassFlagForMerging(); + + // We also need to increment the attachment indices of all the previous subpasses of this native pass. + // Otherwise the existing subpasses will point to the wrong attachments with depth being set as the first one + for (var nativeSubPassIndex = firstNativeSubPass; nativeSubPassIndex < firstNativeSubPass + numNativeSubPasses; nativeSubPassIndex++) + { + ref var subPassDesc = ref contextData.nativeSubPassData.ElementAt(nativeSubPassIndex); // If depth ends up being bound only because of merging - if (!currGraphPass.fragmentInfoHasDepth && nativePass.hasDepth) + // Set SubPassFlags to best match the pass we are trying to merge with + subPassDesc.flags = depthFlag; + + // Updating subpass color outputs + for (int i = 0; i < subPassDesc.colorOutputs.Length; i++) { - // Set SubPassFlags to best match the pass we are trying to merge with - nativeSubPassDescriptor.flags = nativePass.GetSubPassFlagForMerging(); + if (subPassDesc.colorOutputs[i] == 0) + { + subPassDesc.colorOutputs[i] = prevDepthIdx; + } } - // MRT attachments + // Updating subpass color inputs (framebuffer fetch) + for (int i = 0; i < subPassDesc.inputs.Length; i++) { - int fragmentIdx = 0; - int colorOffset = (currGraphPass.fragmentInfoHasDepth) ? -1 : 0; - - nativeSubPassDescriptor.colorOutputs = - new AttachmentIndexArray(currGraphPass.numFragments + colorOffset); - - foreach (ref readonly var graphPassFragment in currGraphPass.Fragments(contextData)) + if (subPassDesc.inputs[i] == 0) { - // Check if we're handling the depth attachment - if (currGraphPass.fragmentInfoHasDepth && fragmentIdx == 0) - { - nativeSubPassDescriptor.flags = (graphPassFragment.accessFlags.HasFlag(AccessFlags.Write)) - ? SubPassFlags.None - : SubPassFlags.ReadOnlyDepth; - } - // It's a color attachment - else - { - // Find the index of this subpass's attachment in the native renderpass attachment list - int colorAttachmentIdx = -1; - for (int fragmentId = 0; fragmentId < fragmentList.size; ++fragmentId) - { - if (fragmentList[fragmentId].resource.index == graphPassFragment.resource.index) - { - colorAttachmentIdx = fragmentId; - break; - } - } - - Debug.Assert(colorAttachmentIdx >= - 0); // If this is not the case it means we are using an attachment in a sub pass that is not part of the native pass !?!? clear bug - - // Set up the color indexes - nativeSubPassDescriptor.colorOutputs[fragmentIdx + colorOffset] = colorAttachmentIdx; - } - - fragmentIdx++; + subPassDesc.inputs[i] = prevDepthIdx; } } + } + // We also need to update the shading rate image index (VRS) + if (hasShadingRateImage && shadingRateImageIndex == 0) + { + shadingRateImageIndex = prevDepthIdx; } } @@ -1257,18 +1231,11 @@ public static PassBreakAudit TryMerge(CompilerContextData contextData, int activ nativePass.numGraphPasses++; nativePass.lastGraphPass = passIdToMerge; - // Depth needs special handling if the native pass doesn't have depth and merges with a pass that does + // Depth needs special handling if the native pass doesn't have depth and merges with a graph pass that does // as we require the depth attachment to be at index 0 if (!nativePass.hasDepth && passToMerge.fragmentInfoHasDepth) { - nativePass.hasDepth = true; - nativePass.fragments.Add(contextData.fragmentData[passToMerge.firstFragment]); - var size = nativePass.fragments.size; - if (size > 1) - (nativePass.fragments[0], nativePass.fragments[size - 1]) = (nativePass.fragments[size - 1], nativePass.fragments[0]); - - // Must update indices from Native subPasses created before - UpdateNativeSubPassesAttachments(contextData, ref nativePass); + nativePass.AddDepthAttachmentFirstDuringMerge(contextData, contextData.fragmentData[passToMerge.firstFragment]); } // Update versions and flags of existing attachments and 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 4b6a3d9be56..b5442e49916 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphResourceRegistry.cs +++ b/Packages/com.unity.render-pipelines.core/Runtime/RenderGraph/RenderGraphResourceRegistry.cs @@ -13,7 +13,7 @@ namespace UnityEngine.Rendering.RenderGraphModule /// /// Basic properties of a RTHandle needed by the render graph compiler. It is not always possible to derive these /// given an RTHandle so the user needs to pass these in. - /// + /// /// We don't use a full RenderTargetDescriptor here as filling out a full descriptor may not be trivial for users and not all /// members of the descriptor are actually used by the render graph. This struct is the minimum set of info needed by the render graph. /// If you want to develop some utility framework to work with render textures, etc. it's probably better to use RenderTargetDescriptor. @@ -91,7 +91,7 @@ internal static RenderGraphResourceRegistry current m_CurrentRegistry = value; } } - + delegate bool ResourceCreateCallback(InternalRenderGraphContext rgContext, IRenderGraphResource res); delegate void ResourceCallback(InternalRenderGraphContext rgContext, IRenderGraphResource res); @@ -550,12 +550,12 @@ internal TextureHandle ImportTexture(in RTHandle rt, RenderTargetInfo info, in I // Store the info in the descriptor structure to avoid having a separate info structure being saved per resource // This descriptor will then be used to reconstruct the info (see GetRenderTargetInfo) but is not a full featured descriptor. // This is ok as this descriptor will never be used to create textures (as they are imported into the graph and thus externally created). - + texResource.desc.format = info.format; texResource.desc.width = info.width; texResource.desc.height = info.height; texResource.desc.slices = info.volumeDepth; - texResource.desc.msaaSamples = (MSAASamples)info.msaaSamples; + texResource.desc.msaaSamples = (MSAASamples)info.msaaSamples; texResource.desc.bindTextureMS = info.bindMS; texResource.desc.clearBuffer = importParams.clearOnFirstUse; texResource.desc.clearColor = importParams.clearColor; @@ -761,7 +761,7 @@ internal void GetRenderTargetInfo(in ResourceHandle res, out RenderTargetInfo ou // so we just say we don't know what this rt is and rely on the user passing in the info to us. var desc = GetTextureResourceDesc(res, true); #if DEVELOPMENT_BUILD || UNITY_EDITOR - if (desc.width == 0 || desc.height == 0 || desc.slices == 0 || desc.msaaSamples == 0 || desc.format == GraphicsFormat.None) + if (desc.width == 0 || desc.height == 0 || desc.slices == 0 || desc.msaaSamples == 0 || desc.format == GraphicsFormat.None) { throw new Exception("Invalid imported texture. A RTHandle wrapping an RenderTargetIdentifier was imported without providing valid RenderTargetInfo."); } @@ -791,13 +791,13 @@ internal void GetRenderTargetInfo(in ResourceHandle res, out RenderTargetInfo ou outInfo.msaaSamples = (int)desc.msaaSamples; outInfo.bindMS = desc.bindTextureMS; - outInfo.format = desc.format; + outInfo.format = desc.format; } } internal GraphicsFormat GetFormat(GraphicsFormat color, GraphicsFormat depthStencil) { - ValidateFormat(color, depthStencil); + ValidateFormat(color, depthStencil); return (depthStencil != GraphicsFormat.None) ? depthStencil : color; } @@ -1066,17 +1066,35 @@ bool CreateTextureCallback(InternalRenderGraphContext rgContext, IRenderGraphRes #endif bool executedWork = false; + if ((forceManualClearOfResource && resource.desc.clearBuffer) || m_RenderGraphDebug.clearRenderTargetsAtCreation) { - bool debugClear = m_RenderGraphDebug.clearRenderTargetsAtCreation && !resource.desc.clearBuffer; - var clearFlag = GraphicsFormatUtility.IsDepthStencilFormat(resource.desc.format) ? ClearFlag.DepthStencil : ClearFlag.Color; - var clearColor = debugClear ? Color.magenta : resource.desc.clearColor; - CoreUtils.SetRenderTarget(rgContext.cmd, resource.graphicsResource, clearFlag, clearColor); + ClearTexture(rgContext, resource); executedWork = true; } return executedWork; } + internal void ClearResource(InternalRenderGraphContext rgContext, int type, int index) + { + var resource = m_RenderGraphResources[type].resourceArray[index]; + + // Only TextureResource for now, but we expect to want to handle other types of resources in the future + if (resource is TextureResource textureResource) + { + ClearTexture(rgContext, textureResource); + } + } + + private void ClearTexture(InternalRenderGraphContext rgContext, TextureResource resource) + { + if (resource == null) return; + var debugClear = m_RenderGraphDebug.clearRenderTargetsAtCreation && !resource.desc.clearBuffer; + var clearFlag = GraphicsFormatUtility.IsDepthStencilFormat(resource.desc.format) ? ClearFlag.DepthStencil : ClearFlag.Color; + var clearColor = debugClear ? Color.magenta : resource.desc.clearColor; + CoreUtils.SetRenderTarget(rgContext.cmd, resource.graphicsResource, clearFlag, clearColor); + } + internal void ReleasePooledResource(InternalRenderGraphContext rgContext, int type, int index) { var resource = m_RenderGraphResources[type].resourceArray[index]; @@ -1139,7 +1157,7 @@ void ValidateTextureDesc(in TextureDesc desc) } } - // Bind ms textures need to use the ms texture sampling functions so there is no "silent" fallback or interoperability between a "non-ms texture" and an "ms texture which happens to have 1 sample" + // Bind ms textures need to use the ms texture sampling functions so there is no "silent" fallback or interoperability between a "non-ms texture" and an "ms texture which happens to have 1 sample" // it's either ms with > 1 sample or "normal texture". This is unlike array textures where you can have an array with 1 slice. if ((int)desc.msaaSamples <= 1 && desc.bindTextureMS == true) { diff --git a/Packages/com.unity.render-pipelines.core/Runtime/RenderPipelineResources/GPUDriven/.buginfo b/Packages/com.unity.render-pipelines.core/Runtime/RenderPipelineResources/GPUDriven/.buginfo index 58dd2dca33f..e5dac0e764f 100644 --- a/Packages/com.unity.render-pipelines.core/Runtime/RenderPipelineResources/GPUDriven/.buginfo +++ b/Packages/com.unity.render-pipelines.core/Runtime/RenderPipelineResources/GPUDriven/.buginfo @@ -1 +1 @@ -area: Graphics Optimization Systems +area: GPU Resident Drawer diff --git a/Packages/com.unity.render-pipelines.core/Tests/Editor/GPUDriven/.buginfo b/Packages/com.unity.render-pipelines.core/Tests/Editor/GPUDriven/.buginfo index 3561c5aedbc..e5dac0e764f 100644 --- a/Packages/com.unity.render-pipelines.core/Tests/Editor/GPUDriven/.buginfo +++ b/Packages/com.unity.render-pipelines.core/Tests/Editor/GPUDriven/.buginfo @@ -1 +1 @@ -area: Graphics Optimization Systems \ No newline at end of file +area: GPU Resident Drawer 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 7dc8ad23e43..0aee4175baa 100644 --- a/Packages/com.unity.render-pipelines.core/Tests/Editor/NativePassCompilerRenderGraphTests.cs +++ b/Packages/com.unity.render-pipelines.core/Tests/Editor/NativePassCompilerRenderGraphTests.cs @@ -1416,5 +1416,167 @@ int CountGCAllocs(Action action) return gcAllocRecorder.sampleBlockCount; } } + + [Test] + public void UpdateSubpassAttachmentIndices_WhenDepthAttachmentIsAdded() + { + var g = AllocateRenderGraph(); + var buffers = ImportAndCreateBuffers(g); + + using (var builder = g.AddRasterRenderPass("NoDepth0_Subpass0", out var passData)) + { + builder.SetRenderAttachment(buffers.extraBuffers[0], 0); + builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { }); + builder.AllowPassCulling(false); + } + + // Render Pass + // attachments: [extraBuffers[0]] + // subpass 0: color outputs : [0] + + using (var builder = g.AddRasterRenderPass("NoDepth1_Subpass0", out var passData)) + { + builder.SetRenderAttachment(buffers.extraBuffers[0], 0); + builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { }); + builder.AllowPassCulling(false); + } + + // Render Pass + // attachments: [extraBuffers[0]] + // subpass 0: color outputs : [0] + + using (var builder = g.AddRasterRenderPass("NoDepth2_Subpass1", out var passData)) + { + builder.SetRenderAttachment(buffers.extraBuffers[1], 0); + builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { }); + builder.AllowPassCulling(false); + } + + // Render Pass + // attachments: [extraBuffers[0], extraBuffers[1]] + // subpass 0: color outputs : [0] + // subpass 1: color outputs : [1] + + using (var builder = g.AddRasterRenderPass("NoDepth3_Subpass2", out var passData)) + { + builder.SetInputAttachment(buffers.extraBuffers[0], 0); + builder.SetInputAttachment(buffers.extraBuffers[1], 1); + builder.SetRenderAttachment(buffers.extraBuffers[2], 0); + builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { }); + builder.AllowPassCulling(false); + } + + // Render Pass + // attachments: [extraBuffers[0], extraBuffers[1], extraBuffers[2]] + // subpass 0: color outputs : [0] + // subpass 1: color outputs : [1] + // subpass 2: color outputs : [2], inputs : [0, 1] + + using (var builder = g.AddRasterRenderPass("Depth_Subpass3", out var passData)) + { + builder.SetInputAttachment(buffers.extraBuffers[0], 0); + builder.SetRenderAttachmentDepth(buffers.depthBuffer, AccessFlags.Write); + builder.SetRenderAttachment(buffers.extraBuffers[3], 0); + builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { }); + builder.AllowPassCulling(false); + } + + // Render Pass + // attachments: [depthBuffer, extraBuffers[1], extraBuffers[2], extraBuffers[0], extraBuffers[3]] + // subpass 0: color outputs : [0 -> 3] + // subpass 1: color outputs : [1] + // subpass 2: color outputs : [2], inputs : [0 -> 3, 1] + // subpass 3: color outputs : [4], inputs : [3] + + var result = g.CompileNativeRenderGraph(g.ComputeGraphHash()); + var passes = result.contextData.GetNativePasses(); + + // All graph passes are merged in the same render pass + Assert.IsTrue(passes != null && passes.Count == 1 && passes[0].numGraphPasses == 5 && passes[0].numNativeSubPasses == 4); + + // Depth is the first attachment + Assert.IsTrue(passes[0].attachments[0].handle.index == buffers.depthBuffer.handle.index); + Assert.IsTrue(passes[0].attachments[1].handle.index == buffers.extraBuffers[1].handle.index); + Assert.IsTrue(passes[0].attachments[2].handle.index == buffers.extraBuffers[2].handle.index); + Assert.IsTrue(passes[0].attachments[3].handle.index == buffers.extraBuffers[0].handle.index); + Assert.IsTrue(passes[0].attachments[4].handle.index == buffers.extraBuffers[3].handle.index); + + // Check first subpass is correctly updated + ref var subPassDesc0 = ref result.contextData.nativeSubPassData.ElementAt(0); + Assert.IsTrue(subPassDesc0.colorOutputs.Length == 1); + Assert.IsTrue(subPassDesc0.colorOutputs[0] == 3); + + // Check second subpass is correctly updated + ref var subPassDesc1 = ref result.contextData.nativeSubPassData.ElementAt(1); + Assert.IsTrue(subPassDesc1.colorOutputs.Length == 1); + Assert.IsTrue(subPassDesc1.colorOutputs[0] == 1); + + // Check third subpass is correctly updated + ref var subPassDesc2 = ref result.contextData.nativeSubPassData.ElementAt(2); + Assert.IsTrue(subPassDesc2.colorOutputs.Length == 1); + Assert.IsTrue(subPassDesc2.colorOutputs[0] == 2); + Assert.IsTrue(subPassDesc2.inputs.Length == 2); + Assert.IsTrue(subPassDesc2.inputs[0] == 3); + Assert.IsTrue(subPassDesc2.inputs[1] == 1); + + // Check fourth subpass with depth is correct + ref var subPassDesc3 = ref result.contextData.nativeSubPassData.ElementAt(3); + Assert.IsTrue(subPassDesc3.colorOutputs.Length == 1); + Assert.IsTrue(subPassDesc3.colorOutputs[0] == 4); + Assert.IsTrue(subPassDesc3.inputs.Length == 1); + Assert.IsTrue(subPassDesc3.inputs[0] == 3); + } + +/* //VRS bug. It seems that there is a bug with VRS forcing pass breaking between passes using the same shading rate image where it shouldn't: UUM-102113. + [Test] + public void UpdateShadingRateImageIndex_WhenDepthAttachmentIsAdded() + { + var g = AllocateRenderGraph(); + var buffers = ImportAndCreateBuffers(g); + + using (var builder = g.AddRasterRenderPass("NoDepth_Subpass0", out var passData)) + { + builder.SetShadingRateImageAttachment(buffers.extraBuffers[0]); + builder.SetRenderAttachment(buffers.extraBuffers[1], 0); + builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { }); + builder.AllowPassCulling(false); + } + + // Render Pass + // attachments: [extraBuffers[0], extraBuffers[1]] + // shading rate image : [0] + // subpass 0: color outputs : [1] + + using (var builder = g.AddRasterRenderPass("Depth_Subpass1", out var passData)) + { + builder.SetShadingRateImageAttachment(buffers.extraBuffers[0]); + builder.SetRenderAttachmentDepth(buffers.depthBuffer, AccessFlags.Write); + builder.SetRenderAttachment(buffers.extraBuffers[2], 0); + builder.SetRenderFunc((RenderGraphTestPassData data, RasterGraphContext context) => { }); + builder.AllowPassCulling(false); + } + + // Render Pass + // attachments: [depthBuffer, extraBuffers[1], extraBuffers[0], extraBuffers[2]] + // shading rate image : [0 -> 2] + // subpass 0: color outputs : [1] + // subpass 1: color outputs : [3] + + var result = g.CompileNativeRenderGraph(g.ComputeGraphHash()); + var passes = result.contextData.GetNativePasses(); + + // All graph passes are merged in the same render pass + Assert.IsTrue(passes != null && passes.Count == 1 && passes[0].numGraphPasses == 2 && passes[0].numNativeSubPasses == 2); + + // Depth is the first attachment + Assert.IsTrue(passes[0].attachments[0].handle.index == buffers.depthBuffer.handle.index); + Assert.IsTrue(passes[0].attachments[1].handle.index == buffers.extraBuffers[1].handle.index); + Assert.IsTrue(passes[0].attachments[2].handle.index == buffers.extraBuffers[0].handle.index); + Assert.IsTrue(passes[0].attachments[3].handle.index == buffers.extraBuffers[2].handle.index); + + // Check Shading Rate Image index is correctly updated + Assert.IsTrue(passes[0].shadingRateImageIndex == buffers.extraBuffers[0].handle.index); + } +*/ } } diff --git a/Packages/com.unity.render-pipelines.core/Tests/Editor/RenderGraphTests.cs b/Packages/com.unity.render-pipelines.core/Tests/Editor/RenderGraphTests.cs index cd783e957c8..bdeae2c97d0 100644 --- a/Packages/com.unity.render-pipelines.core/Tests/Editor/RenderGraphTests.cs +++ b/Packages/com.unity.render-pipelines.core/Tests/Editor/RenderGraphTests.cs @@ -812,7 +812,7 @@ public void TextureDescFormatPropertiesWork() var importedDesc = importedTexture.GetDescriptor(m_RenderGraph); Assert.AreEqual(textureDesc.format, importedDesc.format); - } + } textureDesc.colorFormat = formatR32; Assert.AreEqual(textureDesc.depthBufferBits, DepthBits.None); @@ -823,7 +823,7 @@ public void TextureDescFormatPropertiesWork() var importedDesc = importedTexture.GetDescriptor(m_RenderGraph); Assert.AreEqual(textureDesc.format, importedDesc.format); - } + } } [Test] @@ -920,7 +920,7 @@ public void RenderPassWithNoRenderFuncThrows() { builder.AllowPassCulling(false); - // no render func + // no render func } }; LogAssert.Expect(LogType.Error, "Render Graph Execution error"); @@ -991,11 +991,66 @@ class RenderGraphAsyncRequestTestData public NativeArray pixels; } - private bool m_AsyncReadbackDone = false; + [Test] + public void ImportedTexturesAreClearedOnFirstUse() + { + bool asyncReadbackDone = false; + const int kWidth = 4; + const int kHeight = 4; + const GraphicsFormat format = GraphicsFormat.R8G8B8A8_SRGB; + + NativeArray pixels = default; + + m_RenderGraphTestPipeline.recordRenderGraphBody = (context, camera, cmd) => + { + var redTexture = CreateRedTexture(kWidth, kHeight); + ImportResourceParams importParams = new ImportResourceParams() + { + clearColor = Color.blue, clearOnFirstUse = true + }; + var importedTexture = m_RenderGraph.ImportTexture(redTexture, importParams); + + pixels = new NativeArray(kWidth * kHeight * 4, Allocator.Persistent, + NativeArrayOptions.UninitializedMemory); + + using (var builder = + m_RenderGraph.AddUnsafePass("ImportedTextureTest", out var passData)) + { + builder.AllowPassCulling(false); + builder.UseTexture(importedTexture, AccessFlags.ReadWrite); + + passData.texture = importedTexture; + passData.pixels = pixels; + + builder.SetRenderFunc((RenderGraphAsyncRequestTestData data, UnsafeGraphContext context) => + { + context.cmd.RequestAsyncReadbackIntoNativeArray(ref data.pixels, data.texture, 0, format, + request => RenderGraphTest_AsyncReadbackCallback(request, ref asyncReadbackDone)); + }); + } + }; + + m_Camera.Render(); + + AsyncGPUReadback.WaitAllRequests(); + Assert.True(asyncReadbackDone); + + // Texture should be clear color instead of original red color + for (int i = 0; i < kWidth * kHeight; i += 4) + { + Assert.True(pixels[i] / 255.0f == Color.blue.r); + Assert.True(pixels[i + 1] / 255.0f == Color.blue.g); + Assert.True(pixels[i + 2] / 255.0f == Color.blue.b); + Assert.True(pixels[i + 3] / 255.0f == Color.blue.a); + } + + pixels.Dispose(); + } [Test] public void RequestAsyncReadbackIntoNativeArrayWorks() { + bool asyncReadbackDone = false; const int kWidth = 4; const int kHeight = 4; const GraphicsFormat format = GraphicsFormat.R8G8B8A8_SRGB; @@ -1027,7 +1082,8 @@ public void RequestAsyncReadbackIntoNativeArrayWorks() builder.SetRenderFunc((RenderGraphAsyncRequestTestData data, UnsafeGraphContext context) => { - context.cmd.RequestAsyncReadbackIntoNativeArray(ref data.pixels, data.texture, 0, format, RenderGraphTest_AsyncReadbackCallback); + context.cmd.RequestAsyncReadbackIntoNativeArray(ref data.pixels, data.texture, 0, format, + request => RenderGraphTest_AsyncReadbackCallback(request, ref asyncReadbackDone)); }); } }; @@ -1036,7 +1092,7 @@ public void RequestAsyncReadbackIntoNativeArrayWorks() AsyncGPUReadback.WaitAllRequests(); - Assert.True(m_AsyncReadbackDone); + Assert.True(asyncReadbackDone); for (int i = 0; i < kWidth * kHeight; i += 4) { @@ -1049,16 +1105,16 @@ public void RequestAsyncReadbackIntoNativeArrayWorks() pixels.Dispose(); } - void RenderGraphTest_AsyncReadbackCallback(AsyncGPUReadbackRequest request) + void RenderGraphTest_AsyncReadbackCallback(AsyncGPUReadbackRequest request, ref bool asyncReadbackDone) { if (request.hasError) { // We shouldn't have any error, asserting. - Assert.True(m_AsyncReadbackDone); + Assert.True(asyncReadbackDone); } else if (request.done) { - m_AsyncReadbackDone = true; + asyncReadbackDone = true; } } diff --git a/Packages/com.unity.render-pipelines.core/Tests/Runtime/Threading/FunctionTests.cs b/Packages/com.unity.render-pipelines.core/Tests/Runtime/Threading/FunctionTests.cs index 703ddd0bd75..3023fb5a05e 100644 --- a/Packages/com.unity.render-pipelines.core/Tests/Runtime/Threading/FunctionTests.cs +++ b/Packages/com.unity.render-pipelines.core/Tests/Runtime/Threading/FunctionTests.cs @@ -170,6 +170,15 @@ private void ValidateDeviceComputeGroupSize(int requiredComputeGroupSizeX) Assert.Ignore($"The device's max compute group size X dimension ({deviceMaxComputeGroupSizeX}) does not meet the minimum requirement ({requiredComputeGroupSizeX}) for this test"); } + [SetUp] + public void SetupIgnores() + { + if (SystemInfo.graphicsDeviceType == GraphicsDeviceType.WebGPU) + { + Assert.Ignore("These tests are not supported on WebGPU"); + } + } + [Test] [UnityPlatform(exclude = new[] { RuntimePlatform.WindowsEditor, RuntimePlatform.WSAPlayerX64, RuntimePlatform.WindowsPlayer })] //https://jira.unity3d.com/browse/UUM-78016 public void WaveTest([Values]Kernel kernel, [Values]WaveSizeKeyword waveSizeKeyword) diff --git a/Packages/com.unity.render-pipelines.core/package.json b/Packages/com.unity.render-pipelines.core/package.json index b2da79d5644..f0fc1f646a2 100644 --- a/Packages/com.unity.render-pipelines.core/package.json +++ b/Packages/com.unity.render-pipelines.core/package.json @@ -3,7 +3,7 @@ "description": "SRP Core makes it easier to create or customize a Scriptable Render Pipeline (SRP). SRP Core contains reusable code, including boilerplate code for working with platform-specific graphics APIs, utility functions for common rendering operations, and shader libraries. The code in SRP Core is use by the High Definition Render Pipeline (HDRP) and Universal Render Pipeline (URP). If you are creating a custom SRP from scratch or customizing a prebuilt SRP, using SRP Core will save you time.", "version": "17.2.0", "unity": "6000.2", - "displayName": "Core RP Library", + "displayName": "Scriptable Render Pipeline Core", "dependencies": { "com.unity.burst": "1.8.14", "com.unity.mathematics": "1.3.2", diff --git a/Packages/com.unity.render-pipelines.high-definition-config/package.json b/Packages/com.unity.render-pipelines.high-definition-config/package.json index c4c32eefed7..dd22b4d35b9 100644 --- a/Packages/com.unity.render-pipelines.high-definition-config/package.json +++ b/Packages/com.unity.render-pipelines.high-definition-config/package.json @@ -3,7 +3,7 @@ "description": "Configuration files for the High Definition Render Pipeline.", "version": "17.2.0", "unity": "6000.2", - "displayName": "High Definition RP Config", + "displayName": "High Definition Render Pipeline Config", "dependencies": { "com.unity.render-pipelines.core": "17.2.0" } diff --git a/Packages/com.unity.render-pipelines.high-definition/Documentation~/Light-Component.md b/Packages/com.unity.render-pipelines.high-definition/Documentation~/Light-Component.md index 6ed657b6865..aaa53ef5b24 100644 --- a/Packages/com.unity.render-pipelines.high-definition/Documentation~/Light-Component.md +++ b/Packages/com.unity.render-pipelines.high-definition/Documentation~/Light-Component.md @@ -39,8 +39,6 @@ To make a Light work with the **Animation window**, when you click on the **Add -| **- Flare Tint** | Controls the tint of the flare of the celestial body. | -| **- Flare Multiplier** | Multiplier applied on the flare intensity. | HDRP allows you to use a RenderTexture as a light cookie. However, for the sake of performance, if you make any changes to the RenderTexture, HDRP doesn't automatically update the cookie atlas. To notify the system that the RenderTexture content has changed and so make the system upload the change to the cookie atlas, call `IncrementUpdateCount()` on the RenderTexture. If you don't do this, the system doesn't update the cookie. ## Preset diff --git a/Packages/com.unity.render-pipelines.high-definition/Documentation~/TableOfContents.md b/Packages/com.unity.render-pipelines.high-definition/Documentation~/TableOfContents.md index 2b721cd1d73..cdb1494d035 100644 --- a/Packages/com.unity.render-pipelines.high-definition/Documentation~/TableOfContents.md +++ b/Packages/com.unity.render-pipelines.high-definition/Documentation~/TableOfContents.md @@ -360,7 +360,7 @@ * [Lighting environment reference](reference-lighting-environment.md) * [Ambient Occlusion reference](reference-ambient-occlusion.md) * [IES Importer reference](IES-Importer.md) - * [Lens flares reference](shared/lens-flare/lens-flare-lens-flares-reference.md) + * [Lens flares reference](shared/lens-flare/lens-flare-reference.md) * [Lens Flare (SRP) reference](shared/lens-flare/lens-flare-reference.md) * [Lens Flare (SRP) Data Asset reference](shared/lens-flare/lens-flare-asset.md) * [Screen Space Lens Flare override reference](shared/lens-flare/reference-screen-space-lens-flare.md) diff --git a/Packages/com.unity.render-pipelines.high-definition/Documentation~/probevolumes-skyocclusion.md b/Packages/com.unity.render-pipelines.high-definition/Documentation~/probevolumes-skyocclusion.md index 0124c58c3b5..dfd4a3bc593 100644 --- a/Packages/com.unity.render-pipelines.high-definition/Documentation~/probevolumes-skyocclusion.md +++ b/Packages/com.unity.render-pipelines.high-definition/Documentation~/probevolumes-skyocclusion.md @@ -15,6 +15,12 @@ At runtime, when a static or dynamic GameObject samples an Adaptive Probe Volume - A sky color from the ambient probe, which updates when the sky color changes. - The sky occlusion value, which is static. +## Limitations + +When Unity calculates sky occlusion values, Unity treats the surfaces of GameObjects as opaque and a gray color. As a result, transparent or translucent GameObjects like windows or leaves bounce light away instead of transmitting it through. Also, dark-colored walls reflect the same amount of light as light-colored walls. To minimize lighting issues, disable **Contribute GI** in the [**Static Editor Flags** property](xref:um-static-objects) for transparent or translucent GameObjects. + +To override the gray color Unity uses, go to **Sky Occlusion Settings** in the [Adaptive Probe Volumes panel](probevolumes-lighting-panel-reference.html#sky-occlusion-settings) and adjust **Albedo Override**. + ## Enable sky occlusion First, enable the GPU lightmapper. Unity doesn't support sky occlusion if you use **Progressive CPU** instead. diff --git a/Packages/com.unity.render-pipelines.high-definition/Documentation~/raytracing-troubleshooting.md b/Packages/com.unity.render-pipelines.high-definition/Documentation~/raytracing-troubleshooting.md index 4cc87068da3..edec6e0a39f 100644 --- a/Packages/com.unity.render-pipelines.high-definition/Documentation~/raytracing-troubleshooting.md +++ b/Packages/com.unity.render-pipelines.high-definition/Documentation~/raytracing-troubleshooting.md @@ -6,30 +6,23 @@ Incorrect lighting, such as missing shadows or lighting leaks, can occur if the - Lighting leaks and insufficient light contributions can occur in scenes with closed environments, improper probe placement, or incorrect fallback settings. - > [!NOTE] - > For each rasterized pixel, the lighting comes from at least five distinct sources: - > * Bullet text. - > * Directional (global) lights and their shadows - > * Direct local lights (point, spot, area) and their shadows - > * Indirect specular lighting: - > * Screen Space Reflection (SSR)/Ray Traced Reflection (RTR) - > * Planar reflection - > * Reflection probes - > * Sky cubemap - -- Indirect diffuse lighting: - - - Screen Space Global Illumination (SSGI)/Ray-Traced Global Illumination (RTGI) - - - Adaptive Probe Volumes (APV) - - - Light probe groups - - - Lightmaps - - - Ambient probes - -- Fog +To resolve lighting issues, you need to check the following light sources, which contribute to the illumination of each rasterized pixel in your scene: + +* Directional (global) lights and their shadows +* Direct local lights (point, spot, area) and their shadows +* Indirect specular lighting: + * Screen Space Reflection (SSR)/Ray Traced Reflection (RTR) + * Planar reflection + * Reflection probes + * Sky cubemap + +* Indirect diffuse lighting: + * Screen Space Global Illumination (SSGI)/Ray-Traced Global Illumination (RTGI) + * Adaptive Probe Volumes (APV) + * Light probe groups + * Lightmaps + * Ambient probes +* Fog ## Fix missing shadows from directional lights diff --git a/Packages/com.unity.render-pipelines.high-definition/Editor/PropertyDrawers/LookDevVolumeProfileSettingsPropertyDrawer.cs b/Packages/com.unity.render-pipelines.high-definition/Editor/PropertyDrawers/LookDevVolumeProfileSettingsPropertyDrawer.cs index cd0e7f96e4b..99cc74a5039 100644 --- a/Packages/com.unity.render-pipelines.high-definition/Editor/PropertyDrawers/LookDevVolumeProfileSettingsPropertyDrawer.cs +++ b/Packages/com.unity.render-pipelines.high-definition/Editor/PropertyDrawers/LookDevVolumeProfileSettingsPropertyDrawer.cs @@ -111,7 +111,7 @@ VisualElement CreateAssetFieldUI() if (lookDevVolumeProfileSettings.volumeProfile == null) { lookDevVolumeProfileSettings.volumeProfile = VolumeUtils.CopyVolumeProfileFromResourcesToAssets( - GraphicsSettings.GetRenderPipelineSettings().defaultVolumeProfile); + GraphicsSettings.GetRenderPipelineSettings().lookDevVolumeProfile); } m_VolumeProfileSerializedProperty.objectReferenceValue = lookDevVolumeProfileSettings.volumeProfile; diff --git a/Packages/com.unity.render-pipelines.high-definition/Runtime/Settings/LookDevVolumeProfileSettings.cs b/Packages/com.unity.render-pipelines.high-definition/Runtime/Settings/LookDevVolumeProfileSettings.cs index cd3226f8cfe..0ff3a3c7794 100644 --- a/Packages/com.unity.render-pipelines.high-definition/Runtime/Settings/LookDevVolumeProfileSettings.cs +++ b/Packages/com.unity.render-pipelines.high-definition/Runtime/Settings/LookDevVolumeProfileSettings.cs @@ -69,6 +69,13 @@ void IRenderPipelineGraphicsSettings.Reset() #if UNITY_EDITOR if (UnityEditor.Rendering.EditorGraphicsSettings.TryGetRenderPipelineSettingsForPipeline(out var rpgs)) { + //UUM-100350 + //For some reason, when the one in the HDRP template is modified, all its components are nullified instead of replaced. + //Removing it fully and creating it solve the issue. + string path = VolumeUtils.BuildDefaultNameForVolumeProfile(rpgs.lookDevVolumeProfile); + if (UnityEditor.AssetDatabase.AssetPathExists(path)) + UnityEditor.AssetDatabase.DeleteAsset(path); + volumeProfile = VolumeUtils.CopyVolumeProfileFromResourcesToAssets(rpgs.lookDevVolumeProfile); } #endif diff --git a/Packages/com.unity.render-pipelines.high-definition/Runtime/Sky/PhysicallyBasedSky/PhysicallyBasedSkyRenderer.cs b/Packages/com.unity.render-pipelines.high-definition/Runtime/Sky/PhysicallyBasedSky/PhysicallyBasedSkyRenderer.cs index 619efe2f353..7bd3fae1bc9 100644 --- a/Packages/com.unity.render-pipelines.high-definition/Runtime/Sky/PhysicallyBasedSky/PhysicallyBasedSkyRenderer.cs +++ b/Packages/com.unity.render-pipelines.high-definition/Runtime/Sky/PhysicallyBasedSky/PhysicallyBasedSkyRenderer.cs @@ -68,6 +68,9 @@ class PrecomputationData RTHandle m_AtmosphericScatteringLut; + // BUG: https://jira.unity3d.com/browse/UUM-86915 RwTex3D outputs red. Disable on some devices as a workaround. + bool m_EnableAtmosphericScatteringBlur; + bool IsWorldSpace() => m_InScatteredRadianceTables != null; RTHandle AllocateGroundIrradianceTable() @@ -150,6 +153,13 @@ public void Allocate(BuiltinSkyParameters builtinParams) colorFormat: s_ColorFormat, enableRandomWrite: true, name: "AtmosphericScatteringLUT"); + + // BUG: https://jira.unity3d.com/browse/UUM-86915 + m_EnableAtmosphericScatteringBlur = !(s_ColorFormat == GraphicsFormat.B10G11R11_UFloatPack32 && + SystemInfo.graphicsDeviceName.Contains("Graphics") && + (SystemInfo.graphicsDeviceName.Contains("HD") || // and UHD + SystemInfo.graphicsDeviceName.Contains("Iris") || + SystemInfo.graphicsDeviceName.Contains("Xe"))); } } @@ -269,10 +279,14 @@ internal void RenderAtmosphericScatteringLut(BuiltinSkyParameters builtinParams) // Perform a blur pass on the buffer to reduce resolution artefacts cmd.SetComputeTextureParam(s_SkyLUTGenerator, s_AtmosphericScatteringBlurKernel, HDShaderIDs._AtmosphericScatteringLUT_RW, m_AtmosphericScatteringLut); - cmd.DispatchCompute(s_SkyLUTGenerator, s_AtmosphericScatteringBlurKernel, - 1, - 1, - (int)PbrSkyConfig.AtmosphericScatteringLutDepth); + if(m_EnableAtmosphericScatteringBlur) + { + cmd.DispatchCompute(s_SkyLUTGenerator, s_AtmosphericScatteringBlurKernel, + 1, + 1, + (int)PbrSkyConfig.AtmosphericScatteringLutDepth); + } + } public void BindGlobalBuffers(CommandBuffer cmd) diff --git a/Packages/com.unity.render-pipelines.high-definition/package.json b/Packages/com.unity.render-pipelines.high-definition/package.json index f9c1c271451..fa32c868905 100644 --- a/Packages/com.unity.render-pipelines.high-definition/package.json +++ b/Packages/com.unity.render-pipelines.high-definition/package.json @@ -3,7 +3,7 @@ "description": "The High Definition Render Pipeline (HDRP) is a high-fidelity Scriptable Render Pipeline built by Unity to target modern (Compute Shader compatible) platforms. HDRP utilizes Physically-Based Lighting techniques, linear lighting, HDR lighting, and a configurable hybrid Tile/Cluster deferred/Forward lighting architecture and gives you the tools you need to create games, technical demos, animations, and more to a high graphical standard.", "version": "17.2.0", "unity": "6000.2", - "displayName": "High Definition RP", + "displayName": "High Definition Render Pipeline", "dependencies": { "com.unity.modules.video": "1.0.0", "com.unity.modules.animation": "1.0.0", diff --git a/Packages/com.unity.render-pipelines.universal-config/package.json b/Packages/com.unity.render-pipelines.universal-config/package.json index 1c36d042789..d674f473a1f 100644 --- a/Packages/com.unity.render-pipelines.universal-config/package.json +++ b/Packages/com.unity.render-pipelines.universal-config/package.json @@ -3,7 +3,7 @@ "description": "Configuration files for the Universal Render Pipeline.", "version": "17.0.3", "unity": "6000.0", - "displayName": "Universal RP Config", + "displayName": "Universal Render Pipeline Config", "dependencies": { "com.unity.render-pipelines.core": "17.0.3" } diff --git a/Packages/com.unity.render-pipelines.universal/Editor/2D/Shadows/ShadowCaster2DEditor.cs b/Packages/com.unity.render-pipelines.universal/Editor/2D/Shadows/ShadowCaster2DEditor.cs index 57459f42989..b220620fe8d 100644 --- a/Packages/com.unity.render-pipelines.universal/Editor/2D/Shadows/ShadowCaster2DEditor.cs +++ b/Packages/com.unity.render-pipelines.universal/Editor/2D/Shadows/ShadowCaster2DEditor.cs @@ -131,7 +131,8 @@ public override void OnInspectorGUI() else if (EditorToolManager.IsActiveTool()) ToolManager.RestorePreviousTool(); - EditorGUILayout.PropertyField(m_ShadowShape2DProvider, Styles.shadowShape2DProvider, true); + if(m_ShadowShape2DProvider != null) + EditorGUILayout.PropertyField(m_ShadowShape2DProvider, Styles.shadowShape2DProvider, true); serializedObject.ApplyModifiedProperties(); } diff --git a/Packages/com.unity.render-pipelines.universal/Editor/2D/Shadows/ShadowProvider/ShadowShape2DProvider_ProperyDrawer.cs b/Packages/com.unity.render-pipelines.universal/Editor/2D/Shadows/ShadowProvider/ShadowShape2DProvider_ProperyDrawer.cs index f7744662b62..e6b3fa4d411 100644 --- a/Packages/com.unity.render-pipelines.universal/Editor/2D/Shadows/ShadowProvider/ShadowShape2DProvider_ProperyDrawer.cs +++ b/Packages/com.unity.render-pipelines.universal/Editor/2D/Shadows/ShadowProvider/ShadowShape2DProvider_ProperyDrawer.cs @@ -25,7 +25,13 @@ bool IsChildVisible(Type parentType, SerializedProperty child) void ProcessChildren(SerializedProperty parentProperty, ProcessChild onProcessChild) { + if (parentProperty == null) + return; + var enumerator = parentProperty.GetEnumerator(); + if (enumerator.Current == null) + return; + object parentObj = parentProperty.managedReferenceValue; Type parentType = parentObj.GetType(); diff --git a/Packages/com.unity.render-pipelines.universal/Editor/ShaderScriptableStripper.cs b/Packages/com.unity.render-pipelines.universal/Editor/ShaderScriptableStripper.cs index 43eb10ecf4e..7b4dc83ee19 100644 --- a/Packages/com.unity.render-pipelines.universal/Editor/ShaderScriptableStripper.cs +++ b/Packages/com.unity.render-pipelines.universal/Editor/ShaderScriptableStripper.cs @@ -111,6 +111,8 @@ public bool PassHasKeyword(LocalKeyword keyword) Shader m_PaniniProjection = Shader.Find("Hidden/Universal Render Pipeline/PaniniProjection"); Shader m_Bloom = Shader.Find("Hidden/Universal Render Pipeline/Bloom"); Shader m_TerrainLit = Shader.Find("Universal Render Pipeline/Terrain/Lit"); + Shader m_TerrainLitAddPass = Shader.Find("Universal Render Pipeline/Terrain/Lit (Add Pass)"); + Shader m_TerrainLitBasePass = Shader.Find("Universal Render Pipeline/Terrain/Lit (Base Pass)"); Shader m_StencilDeferred = Shader.Find("Hidden/Universal Render Pipeline/StencilDeferred"); Shader m_ClusterDeferred = Shader.Find("Hidden/Universal Render Pipeline/ClusterDeferred"); Shader m_UberPostShader = Shader.Find("Hidden/Universal Render Pipeline/UberPost"); @@ -958,7 +960,7 @@ internal bool StripInvalidVariants_HDR(ref IShaderScriptableStrippingData stripp internal bool StripInvalidVariants_TerrainHoles(ref IShaderScriptableStrippingData strippingData) { - if (strippingData.shader == m_TerrainLit) + if (strippingData.shader == m_TerrainLit || strippingData.shader == m_TerrainLitAddPass || strippingData.shader == m_TerrainLitBasePass) if (!strippingData.IsShaderFeatureEnabled(ShaderFeatures.TerrainHoles) && strippingData.IsKeywordEnabled(m_AlphaTestOn)) return true; return false; diff --git a/Packages/com.unity.render-pipelines.universal/Runtime/2D/Renderer2D.cs b/Packages/com.unity.render-pipelines.universal/Runtime/2D/Renderer2D.cs index a29331be976..2ddce56534c 100644 --- a/Packages/com.unity.render-pipelines.universal/Runtime/2D/Renderer2D.cs +++ b/Packages/com.unity.render-pipelines.universal/Runtime/2D/Renderer2D.cs @@ -177,12 +177,11 @@ private RenderPassInputSummary GetRenderPassInputs(UniversalCameraData cameraDat || !cameraData.isDefaultViewport || cameraData.requireSrgbConversion || !cameraData.resolveFinalTarget + || cameraData.cameraTargetDescriptor.msaaSamples > 1 && UniversalRenderer.PlatformRequiresExplicitMsaaResolve() || m_Renderer2DData.useCameraSortingLayerTexture || !Mathf.Approximately(cameraData.renderScale, 1.0f) || (DebugHandler != null && DebugHandler.WriteToDebugScreenTexture(cameraData.resolveFinalTarget)); - inputSummary.requiresDepthTexture |= (!cameraData.resolveFinalTarget && m_UseDepthStencilBuffer); - return inputSummary; } diff --git a/Packages/com.unity.render-pipelines.universal/Runtime/2D/Rendergraph/Renderer2DRendergraph.cs b/Packages/com.unity.render-pipelines.universal/Runtime/2D/Rendergraph/Renderer2DRendergraph.cs index 0ffc6c62799..acd0603683e 100644 --- a/Packages/com.unity.render-pipelines.universal/Runtime/2D/Rendergraph/Renderer2DRendergraph.cs +++ b/Packages/com.unity.render-pipelines.universal/Runtime/2D/Rendergraph/Renderer2DRendergraph.cs @@ -61,7 +61,12 @@ private RTHandle nextRenderGraphCameraColorHandle private bool IsPixelPerfectCameraEnabled(UniversalCameraData cameraData) { - cameraData.camera.TryGetComponent(out var ppc); + PixelPerfectCamera ppc = null; + + // Pixel Perfect Camera doesn't support camera stacking. + if (cameraData.renderType == CameraRenderType.Base && cameraData.resolveFinalTarget) + cameraData.camera.TryGetComponent(out ppc); + return ppc != null && ppc.enabled && ppc.cropFrame != PixelPerfectCamera.CropFrame.None; } diff --git a/Packages/com.unity.render-pipelines.universal/Runtime/2D/Shadows/ShadowProvider/ShadowMesh2D.cs b/Packages/com.unity.render-pipelines.universal/Runtime/2D/Shadows/ShadowProvider/ShadowMesh2D.cs index 3d5899d5b44..a0c9a165228 100644 --- a/Packages/com.unity.render-pipelines.universal/Runtime/2D/Shadows/ShadowProvider/ShadowMesh2D.cs +++ b/Packages/com.unity.render-pipelines.universal/Runtime/2D/Shadows/ShadowProvider/ShadowMesh2D.cs @@ -318,8 +318,11 @@ bool AreDegenerateVertices(NativeArray vertices) public override void SetShape(NativeArray vertices, NativeArray indices, ShadowShape2D.OutlineTopology outlineTopology, ShadowShape2D.WindingOrder windingOrder = ShadowShape2D.WindingOrder.Clockwise, bool allowTrimming = true, bool createInteriorGeometry = false) { - if (AreDegenerateVertices(vertices)) + if (AreDegenerateVertices(vertices) || indices == null || indices.Length == 0) + { + m_Mesh.Clear(); return; + } if (m_TrimEdge == k_TrimEdgeUninitialized) m_TrimEdge = m_InitialTrim; @@ -333,12 +336,6 @@ public override void SetShape(NativeArray vertices, NativeArray in if (m_Mesh == null) m_Mesh = new Mesh(); - if (indices.Length == 0) - { - m_Mesh.Clear(); - return; - } - if (outlineTopology == ShadowShape2D.OutlineTopology.Triangles) { NativeArray newVertices; diff --git a/Packages/com.unity.render-pipelines.universal/Runtime/DeferredLights.cs b/Packages/com.unity.render-pipelines.universal/Runtime/DeferredLights.cs index c5cb7d4edd5..3249ac5f6e5 100644 --- a/Packages/com.unity.render-pipelines.universal/Runtime/DeferredLights.cs +++ b/Packages/com.unity.render-pipelines.universal/Runtime/DeferredLights.cs @@ -219,10 +219,6 @@ internal GraphicsFormat GetGBufferFormat(int index) internal GraphicsFormat[] GbufferFormats { get; set; } internal RTHandle DepthAttachmentHandle { get; set; } - // Render Graph only. - // True if GBuffer pass has previously been recorded this frame. If false, GBuffers might not contain valid data. - internal bool IsGBufferValid { get; set; } - // Visible lights indices rendered using stencil volumes. NativeArray m_stencilVisLights; // Offset of each type of lights in m_stencilVisLights. @@ -429,10 +425,6 @@ internal void ResolveMixedLightingMode(UniversalLightData lightData) } } } - // Once the mixed lighting mode has been discovered, we know how many MRTs we need for the gbuffer. - // Subtractive mixed lighting requires shadowMask output, which is actually used to store unity_ProbesOcclusion values. - - CreateGbufferResources(); } // In cases when custom pass is injected between GBuffer and Deferred passes we need to fallback @@ -494,6 +486,39 @@ internal void CreateGbufferResources() } } + internal void CreateGbufferResourcesRenderGraph(RenderGraph renderGraph, UniversalResourceData resourceData) + { + int gbufferSliceCount = GBufferSliceCount; + if (GbufferTextureHandles == null || GbufferTextureHandles.Length != gbufferSliceCount) + { + GbufferFormats = new GraphicsFormat[gbufferSliceCount]; + GbufferTextureHandles = new TextureHandle[gbufferSliceCount]; + } + + bool useCameraRenderingLayersTexture = UseRenderingLayers && !UseLightLayers; + Debug.Assert(resourceData.cameraColor.IsValid(), "Deferred Renderer assumes that the intermediate (color) texture is available."); + + for (int i = 0; i < gbufferSliceCount; ++i) + { + GbufferFormats[i] = GetGBufferFormat(i); + + if (i == GBufferNormalSmoothnessIndex && HasNormalPrepass) + GbufferTextureHandles[i] = resourceData.cameraNormalsTexture; + else if (i == GBufferRenderingLayers && useCameraRenderingLayersTexture) + GbufferTextureHandles[i] = resourceData.renderingLayersTexture; + else if (i != GBufferLightingIndex) + { + var gbufferSlice = resourceData.cameraColor.GetDescriptor(renderGraph); + gbufferSlice.format = GetGBufferFormat(i); + gbufferSlice.name = k_GBufferNames[i]; + gbufferSlice.clearBuffer = true; + GbufferTextureHandles[i] = renderGraph.CreateTexture(gbufferSlice); + } + else + GbufferTextureHandles[i] = resourceData.cameraColor; + } + } + internal void UpdateDeferredInputAttachments() { this.DeferredInputAttachments[0] = this.GbufferAttachments[0]; @@ -565,7 +590,6 @@ public void Setup( internal void Setup(AdditionalLightsShadowCasterPass additionalLightsShadowCasterPass) { m_AdditionalLightsShadowCasterPass = additionalLightsShadowCasterPass; - IsGBufferValid = false; } public void OnCameraCleanup(CommandBuffer cmd) @@ -665,10 +689,10 @@ internal void ExecuteDeferredPass(RasterCommandBuffer cmd, UniversalCameraData c if (!UseFramebufferFetch) { Material deferredMaterial = m_UseDeferredPlus ? m_ClusterDeferredMaterial : m_StencilDeferredMaterial; - for (int i = 0; i < GbufferTextureHandles.Length; i++) + for (int i = 0; i < GbufferRTHandles.Length; i++) { if (i != GBufferLightingIndex) - deferredMaterial.SetTexture(k_GBufferShaderPropertyIDs[i], GbufferTextureHandles[i]); + deferredMaterial.SetTexture(k_GBufferShaderPropertyIDs[i], GbufferRTHandles[i]); } } diff --git a/Packages/com.unity.render-pipelines.universal/Runtime/Passes/GBufferPass.cs b/Packages/com.unity.render-pipelines.universal/Runtime/Passes/GBufferPass.cs index db569365561..97aa109ebb4 100644 --- a/Packages/com.unity.render-pipelines.universal/Runtime/Passes/GBufferPass.cs +++ b/Packages/com.unity.render-pipelines.universal/Runtime/Passes/GBufferPass.cs @@ -233,44 +233,13 @@ internal void Render(RenderGraph renderGraph, ContextContainer frameData, Textur bool useCameraRenderingLayersTexture = m_DeferredLights.UseRenderingLayers && !m_DeferredLights.UseLightLayers; passData.gbuffer = m_DeferredLights.GbufferTextureHandles; - - // If the gbuffer targets are already set up, then assume we are setting up for the second gbuffer pass when doing two-pass occlusion culling. - // We want to continue to render to the same targets as the first pass in this case, so just attach them for this pass. - if (m_DeferredLights.IsGBufferValid) - { - for (int i = 0; i < m_DeferredLights.GBufferSliceCount; i++) - { - builder.SetRenderAttachment(passData.gbuffer[i], i, AccessFlags.Write); - } - } - else + for (int i = 0; i < m_DeferredLights.GBufferSliceCount; i++) { - for (int i = 0; i < m_DeferredLights.GBufferSliceCount; i++) - { - var gbufferSlice = cameraData.cameraTargetDescriptor; - gbufferSlice.depthStencilFormat = GraphicsFormat.None; // make sure no depth surface is actually created - gbufferSlice.stencilFormat = GraphicsFormat.None; - - if (i == m_DeferredLights.GBufferNormalSmoothnessIndex && m_DeferredLights.HasNormalPrepass) - passData.gbuffer[i] = resourceData.cameraNormalsTexture; - else if (i == m_DeferredLights.GBufferRenderingLayers && useCameraRenderingLayersTexture) - passData.gbuffer[i] = resourceData.renderingLayersTexture; - else if (i != m_DeferredLights.GBufferLightingIndex) - { - gbufferSlice.graphicsFormat = m_DeferredLights.GetGBufferFormat(i); - passData.gbuffer[i] = UniversalRenderer.CreateRenderGraphTexture(renderGraph, gbufferSlice, DeferredLights.k_GBufferNames[i], true); - } - else - passData.gbuffer[i] = cameraColor; - - builder.SetRenderAttachment(passData.gbuffer[i], i, AccessFlags.Write); - } - - m_DeferredLights.IsGBufferValid = true; + Debug.Assert(passData.gbuffer[i].IsValid()); + builder.SetRenderAttachment(passData.gbuffer[i], i, AccessFlags.Write); } - RenderGraphUtils.UseDBufferIfValid(builder, resourceData); - resourceData.gBuffer = passData.gbuffer; + RenderGraphUtils.UseDBufferIfValid(builder, resourceData); passData.depth = cameraDepth; builder.SetRenderAttachmentDepth(cameraDepth, AccessFlags.Write); @@ -279,8 +248,8 @@ internal void Render(RenderGraph renderGraph, ContextContainer frameData, Textur InitRendererLists(ref passData, default(ScriptableRenderContext), renderGraph, renderingData, cameraData, lightData, true); builder.UseRendererList(passData.rendererListHdl); builder.UseRendererList(passData.objectsWithErrorRendererListHdl); - - if (setGlobalTextures) + + if (setGlobalTextures) { builder.SetGlobalTextureAfterPass(resourceData.cameraNormalsTexture, s_CameraNormalsTextureID); @@ -292,7 +261,7 @@ internal void Render(RenderGraph renderGraph, ContextContainer frameData, Textur builder.SetRenderFunc((PassData data, RasterGraphContext context) => { - ExecutePass(context.cmd, data, data.rendererListHdl, data.objectsWithErrorRendererListHdl); + ExecutePass(context.cmd, data, data.rendererListHdl, data.objectsWithErrorRendererListHdl); }); } } diff --git a/Packages/com.unity.render-pipelines.universal/Runtime/UniversalRenderer.cs b/Packages/com.unity.render-pipelines.universal/Runtime/UniversalRenderer.cs index 240405c6d22..8c771d6e81d 100644 --- a/Packages/com.unity.render-pipelines.universal/Runtime/UniversalRenderer.cs +++ b/Packages/com.unity.render-pipelines.universal/Runtime/UniversalRenderer.cs @@ -811,6 +811,10 @@ public override void Setup(ScriptableRenderContext context, ref RenderingData re m_DeferredLights.ResolveMixedLightingMode(lightData); + // Once the mixed lighting mode has been discovered, we know how many MRTs we need for the gbuffer. + // Subtractive mixed lighting requires shadowMask output, which is actually used to store unity_ProbesOcclusion values. + m_DeferredLights.CreateGbufferResources(); + if (m_DeferredLights.UseFramebufferFetch) { // At this point we only have injected renderer features in the queue and can do assumptions on whether we'll need Framebuffer Fetch @@ -1893,7 +1897,7 @@ void CreateCameraRenderTarget(ScriptableRenderContext context, ref RenderTexture cmd.Clear(); } - bool PlatformRequiresExplicitMsaaResolve() + internal static bool PlatformRequiresExplicitMsaaResolve() { #if UNITY_EDITOR // In the editor play-mode we use a Game View Render Texture, with diff --git a/Packages/com.unity.render-pipelines.universal/Runtime/UniversalRendererRenderGraph.cs b/Packages/com.unity.render-pipelines.universal/Runtime/UniversalRendererRenderGraph.cs index 584d2335bc8..36cec88134c 100644 --- a/Packages/com.unity.render-pipelines.universal/Runtime/UniversalRendererRenderGraph.cs +++ b/Packages/com.unity.render-pipelines.universal/Runtime/UniversalRendererRenderGraph.cs @@ -392,7 +392,7 @@ private readonly struct ClearCameraParams internal readonly bool mustClearColor; internal readonly bool mustClearDepth; internal readonly Color clearValue; - + internal ClearCameraParams(bool clearColor, bool clearDepth, Color clearVal) { mustClearColor = clearColor; @@ -1084,6 +1084,11 @@ private void OnMainRendering(RenderGraph renderGraph, ScriptableRenderContext co m_DeferredLights.HasNormalPrepass = isDepthNormalPrepass; m_DeferredLights.HasDepthPrepass = requiresPrepass; m_DeferredLights.ResolveMixedLightingMode(lightData); + // Once the mixed lighting mode has been discovered, we know how many MRTs we need for the gbuffer. + // Subtractive mixed lighting requires shadowMask output, which is actually used to store unity_ProbesOcclusion values. + m_DeferredLights.CreateGbufferResourcesRenderGraph(renderGraph, resourceData); + resourceData.gBuffer = m_DeferredLights.GbufferTextureHandles; + RecordCustomRenderGraphPasses(renderGraph, RenderPassEvent.BeforeRenderingGbuffer); @@ -1385,6 +1390,11 @@ private void OnAfterRendering(RenderGraph renderGraph, bool applyPostProcessing) RecordCustomRenderGraphPasses(renderGraph, RenderPassEvent.AfterRenderingPostProcessing); + if (cameraData.captureActions != null) + { + m_CapturePass.RecordRenderGraph(renderGraph, frameData); + } + if (applyFinalPostProcessing) { TextureHandle backbuffer = resourceData.backBufferColor; @@ -1407,20 +1417,12 @@ private void OnAfterRendering(RenderGraph renderGraph, bool applyPostProcessing) resourceData.activeDepthID = UniversalResourceData.ActiveID.BackBuffer; } - if (cameraData.captureActions != null) - { - m_CapturePass.RecordRenderGraph(renderGraph, frameData); - } - cameraTargetResolved = // final PP always blit to camera target applyFinalPostProcessing || // no final PP but we have PP stack. In that case it blit unless there are render pass after PP (applyPostProcessing && !hasPassesAfterPostProcessing && !hasCaptureActions); - // TODO RENDERGRAPH: we need to discuss and decide if RenderPassEvent.AfterRendering injected passes should only be called after the last camera in the stack - RecordCustomRenderGraphPasses(renderGraph, RenderPassEvent.AfterRendering); - if (!resourceData.isActiveTargetBackBuffer && cameraData.resolveFinalTarget && !cameraTargetResolved) { TextureHandle backbuffer = resourceData.backBufferColor; @@ -1443,6 +1445,9 @@ private void OnAfterRendering(RenderGraph renderGraph, bool applyPostProcessing) resourceData.activeDepthID = UniversalResourceData.ActiveID.BackBuffer; } + // TODO RENDERGRAPH: we need to discuss and decide if RenderPassEvent.AfterRendering injected passes should only be called after the last camera in the stack + RecordCustomRenderGraphPasses(renderGraph, RenderPassEvent.AfterRendering); + // We can explicitely render the overlay UI from URP when HDR output is not enabled. // SupportedRenderingFeatures.active.rendersUIOverlay should also be set to true. bool shouldRenderUI = cameraData.rendersOverlayUI && cameraData.isLastBaseCamera; 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 6b296c439a2..57d613fe218 100644 --- a/Packages/com.unity.render-pipelines.universal/Shaders/Terrain/TerrainLit.shader +++ b/Packages/com.unity.render-pipelines.universal/Shaders/Terrain/TerrainLit.shader @@ -162,8 +162,8 @@ Shader "Universal Render Pipeline/Terrain/Lit" //#pragma multi_compile _ _ADDITIONAL_LIGHT_SHADOWS #pragma multi_compile_fragment _ _REFLECTION_PROBE_BLENDING #pragma multi_compile_fragment _ _SHADOWS_SOFT _SHADOWS_SOFT_LOW _SHADOWS_SOFT_MEDIUM _SHADOWS_SOFT_HIGH - #pragma multi_compile _ _MIXED_LIGHTING_SUBTRACTIVE #pragma multi_compile_fragment _ _DBUFFER_MRT1 _DBUFFER_MRT2 _DBUFFER_MRT3 + #pragma multi_compile_fragment _ _RENDER_PASS_ENABLED #pragma multi_compile _ _CLUSTER_LIGHT_LOOP #include_with_pragmas "Packages/com.unity.render-pipelines.universal/ShaderLibrary/RenderingLayers.hlsl" @@ -175,10 +175,8 @@ Shader "Universal Render Pipeline/Terrain/Lit" #pragma multi_compile _ LIGHTMAP_ON #pragma multi_compile_fragment _ LIGHTMAP_BICUBIC_SAMPLING #pragma multi_compile _ DYNAMICLIGHTMAP_ON - #include_with_pragmas "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Fog.hlsl" #include_with_pragmas "Packages/com.unity.render-pipelines.universal/ShaderLibrary/ProbeVolumeVariants.hlsl" #pragma multi_compile_fragment _ _GBUFFER_NORMALS_OCT - #pragma multi_compile_fragment _ _RENDER_PASS_ENABLED #pragma multi_compile_instancing #pragma instancing_options assumeuniformscaling nomatrices nolightprobe nolightmap 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 ffb530fa354..598142eafbb 100644 --- a/Packages/com.unity.render-pipelines.universal/Shaders/Terrain/TerrainLitAdd.shader +++ b/Packages/com.unity.render-pipelines.universal/Shaders/Terrain/TerrainLitAdd.shader @@ -48,8 +48,9 @@ Shader "Hidden/Universal Render Pipeline/Terrain/Lit (Add Pass)" Pass { - Name "TerrainAddLit" + Name "ForwardLit" Tags { "LightMode" = "UniversalForward" } + ZWrite Off Blend One One HLSLPROGRAM #pragma target 3.0 @@ -104,6 +105,7 @@ Shader "Hidden/Universal Render Pipeline/Terrain/Lit (Add Pass)" Name "GBuffer" Tags{"LightMode" = "UniversalGBuffer"} + ZWrite Off Blend 0 One One Blend 1 One One Blend 2 One One @@ -134,12 +136,13 @@ Shader "Hidden/Universal Render Pipeline/Terrain/Lit (Add Pass)" #pragma multi_compile _ _MAIN_LIGHT_SHADOWS _MAIN_LIGHT_SHADOWS_CASCADE _MAIN_LIGHT_SHADOWS_SCREEN #pragma multi_compile_fragment _ _REFLECTION_PROBE_BLENDING #pragma multi_compile_fragment _ _SHADOWS_SOFT _SHADOWS_SOFT_LOW _SHADOWS_SOFT_MEDIUM _SHADOWS_SOFT_HIGH - #pragma multi_compile _ _MIXED_LIGHTING_SUBTRACTIVE #pragma multi_compile _ _CLUSTER_LIGHT_LOOP #include_with_pragmas "Packages/com.unity.render-pipelines.universal/ShaderLibrary/RenderingLayers.hlsl" // ------------------------------------- // Unity defined keywords + #pragma multi_compile _ LIGHTMAP_SHADOW_MIXING + #pragma multi_compile _ SHADOWS_SHADOWMASK #pragma multi_compile _ DIRLIGHTMAP_COMBINED #pragma multi_compile _ LIGHTMAP_ON #pragma multi_compile_fragment _ LIGHTMAP_BICUBIC_SAMPLING 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 f053f0988f5..ce1846ca79e 100644 --- a/Packages/com.unity.render-pipelines.universal/Shaders/Terrain/TerrainLitBase.shader +++ b/Packages/com.unity.render-pipelines.universal/Shaders/Terrain/TerrainLitBase.shader @@ -41,7 +41,7 @@ Shader "Hidden/Universal Render Pipeline/Terrain/Lit (Base Pass)" #pragma multi_compile _ _MAIN_LIGHT_SHADOWS _MAIN_LIGHT_SHADOWS_CASCADE _MAIN_LIGHT_SHADOWS_SCREEN #pragma multi_compile _ _ADDITIONAL_LIGHTS_VERTEX _ADDITIONAL_LIGHTS #pragma multi_compile _ LIGHTMAP_SHADOW_MIXING - #pragma multi_compile _ SHADOWS_SHADOWMASKs + #pragma multi_compile _ SHADOWS_SHADOWMASK #pragma multi_compile _ _LIGHT_LAYERS #pragma multi_compile _ _CLUSTER_LIGHT_LOOP #pragma multi_compile_fragment _ _ADDITIONAL_LIGHT_SHADOWS @@ -128,7 +128,6 @@ Shader "Hidden/Universal Render Pipeline/Terrain/Lit (Base Pass)" //#pragma multi_compile _ _ADDITIONAL_LIGHT_SHADOWS #pragma multi_compile_fragment _ _REFLECTION_PROBE_BLENDING #pragma multi_compile_fragment _ _SHADOWS_SOFT _SHADOWS_SOFT_LOW _SHADOWS_SOFT_MEDIUM _SHADOWS_SOFT_HIGH - #pragma multi_compile _ _MIXED_LIGHTING_SUBTRACTIVE #pragma multi_compile _ _CLUSTER_LIGHT_LOOP #include_with_pragmas "Packages/com.unity.render-pipelines.universal/ShaderLibrary/RenderingLayers.hlsl" diff --git a/Packages/com.unity.render-pipelines.universal/package.json b/Packages/com.unity.render-pipelines.universal/package.json index b9f2a6fbdb9..acfafe8d695 100644 --- a/Packages/com.unity.render-pipelines.universal/package.json +++ b/Packages/com.unity.render-pipelines.universal/package.json @@ -3,7 +3,7 @@ "description": "The Universal Render Pipeline (URP) is a prebuilt Scriptable Render Pipeline, made by Unity. URP provides artist-friendly workflows that let you quickly and easily create optimized graphics across a range of platforms, from mobile to high-end consoles and PCs.", "version": "17.2.0", "unity": "6000.2", - "displayName": "Universal RP", + "displayName": "Universal Render Pipeline", "dependencies": { "com.unity.render-pipelines.core": "17.2.0", "com.unity.shadergraph": "17.2.0", diff --git a/Packages/com.unity.shadergraph/Editor/AssetCallbacks/CreateShaderSubGraph.cs b/Packages/com.unity.shadergraph/Editor/AssetCallbacks/CreateShaderSubGraph.cs index eda117d75b1..3f1091a67ef 100644 --- a/Packages/com.unity.shadergraph/Editor/AssetCallbacks/CreateShaderSubGraph.cs +++ b/Packages/com.unity.shadergraph/Editor/AssetCallbacks/CreateShaderSubGraph.cs @@ -10,7 +10,7 @@ class CreateShaderSubGraph : EndNameEditAction public static void CreateMaterialSubGraph() { ProjectWindowUtil.StartNameEditingIfProjectWindowExists(0, CreateInstance(), - string.Format("New Shader Sub Graph.{0}", ShaderSubGraphImporter.Extension), null, null); + string.Format("New Shader Sub Graph.{0}", ShaderSubGraphImporter.Extension), ShaderSubGraphImporter.GetIcon(), null); } public override void Action(int instanceId, string pathName, string resourceFile) diff --git a/Packages/com.unity.shadergraph/Editor/Data/Graphs/GraphData.cs b/Packages/com.unity.shadergraph/Editor/Data/Graphs/GraphData.cs index e5e6f21fb4c..f8cc1a59e9f 100644 --- a/Packages/com.unity.shadergraph/Editor/Data/Graphs/GraphData.cs +++ b/Packages/com.unity.shadergraph/Editor/Data/Graphs/GraphData.cs @@ -1371,12 +1371,33 @@ public void CollectShaderProperties(PropertyCollector collector, GenerationMode } } + private static void CollectSubgraphKeywordsR(KeywordCollector collector, SubGraphAsset asset) + { + if (asset is null || !asset.isValid || asset.isNull) + return; + + foreach(var keyword in asset.keywords) + { + collector.AddShaderKeyword(keyword); + } + foreach(var guid in asset.children) + { + var path = AssetDatabase.GUIDToAssetPath(guid); + var child = AssetDatabase.LoadAssetAtPath(path); + CollectSubgraphKeywordsR(collector, child); + } + } + public void CollectShaderKeywords(KeywordCollector collector, GenerationMode generationMode) { foreach (var keyword in keywords) { collector.AddShaderKeyword(keyword); } + foreach(var node in GetNodes()) + { + CollectSubgraphKeywordsR(collector, node.asset); + } // Alwways calculate permutations when collecting collector.CalculateKeywordPermutations(); @@ -2854,6 +2875,8 @@ public void OnEnable() node.OnEnable(); } + // OnEnable may be called multiple times. Ensure the callback only exists once. + ShaderGraphPreferences.onVariantLimitChanged -= OnKeywordChanged; ShaderGraphPreferences.onVariantLimitChanged += OnKeywordChanged; } diff --git a/Packages/com.unity.shadergraph/Editor/Data/SubGraph/SubGraphAsset.cs b/Packages/com.unity.shadergraph/Editor/Data/SubGraph/SubGraphAsset.cs index 023bdeb5d3f..797e52a61f5 100644 --- a/Packages/com.unity.shadergraph/Editor/Data/SubGraph/SubGraphAsset.cs +++ b/Packages/com.unity.shadergraph/Editor/Data/SubGraph/SubGraphAsset.cs @@ -83,6 +83,8 @@ class SubGraphAsset : ScriptableObject, ISerializationCallbackReceiver public string assetGuid; + public bool isNull => m_SubGraphData == null; + public ShaderGraphRequirements requirements; public string path; diff --git a/Packages/com.unity.shadergraph/Editor/Data/Util/GraphUtil.cs b/Packages/com.unity.shadergraph/Editor/Data/Util/GraphUtil.cs index b72980df4e5..93815cac066 100644 --- a/Packages/com.unity.shadergraph/Editor/Data/Util/GraphUtil.cs +++ b/Packages/com.unity.shadergraph/Editor/Data/Util/GraphUtil.cs @@ -188,7 +188,7 @@ public static void CreateNewGraph() var graphItem = ScriptableObject.CreateInstance(); graphItem.targets = null; ProjectWindowUtil.StartNameEditingIfProjectWindowExists(0, graphItem, - string.Format("New Shader Graph.{0}", ShaderGraphImporter.Extension), null, null); + string.Format("New Shader Graph.{0}", ShaderGraphImporter.Extension), ShaderGraphImporter.GetIcon(), null); } public static void CreateNewGraphWithOutputs(Target[] targets, BlockFieldDescriptor[] blockDescriptors) @@ -197,7 +197,7 @@ public static void CreateNewGraphWithOutputs(Target[] targets, BlockFieldDescrip graphItem.targets = targets; graphItem.blocks = blockDescriptors; ProjectWindowUtil.StartNameEditingIfProjectWindowExists(0, graphItem, - string.Format("New Shader Graph.{0}", ShaderGraphImporter.Extension), null, null); + string.Format("New Shader Graph.{0}", ShaderGraphImporter.Extension), ShaderGraphImporter.GetIcon(), null); } public static bool TryGetMetadataOfType(this Shader shader, out T obj) where T : ScriptableObject diff --git a/Packages/com.unity.shadergraph/Editor/Drawing/MaterialGraphEditWindow.cs b/Packages/com.unity.shadergraph/Editor/Drawing/MaterialGraphEditWindow.cs index 9e60d1c425f..cfbdd073ee0 100644 --- a/Packages/com.unity.shadergraph/Editor/Drawing/MaterialGraphEditWindow.cs +++ b/Packages/com.unity.shadergraph/Editor/Drawing/MaterialGraphEditWindow.cs @@ -486,7 +486,7 @@ public void AssetWasDeleted() UpdateTitle(); } - public void UpdateTitle() + public void UpdateTitle(bool ignoreUnsavedChanges = false) { string assetPath = AssetDatabase.GUIDToAssetPath(selectedGuid); string shaderName = Path.GetFileNameWithoutExtension(assetPath); @@ -501,7 +501,7 @@ public void UpdateTitle() title = title + " (nothing loaded)"; else { - if (GraphHasChangedSinceLastSerialization()) + if (!ignoreUnsavedChanges && GraphHasChangedSinceLastSerialization()) { hasUnsavedChanges = true; // This is the message EditorWindow will show when prompting to close while dirty @@ -1260,6 +1260,7 @@ public void Initialize(string assetGuid) using (GraphLoadMarker.Auto()) { m_LastSerializedFileContents = File.ReadAllText(path, Encoding.UTF8); + graphObject = CreateInstance(); graphObject.hideFlags = HideFlags.HideAndDontSave; graphObject.graph = new GraphData @@ -1268,6 +1269,7 @@ public void Initialize(string assetGuid) isSubGraph = isSubGraph, messageManager = messageManager }; + MultiJson.Deserialize(graphObject.graph, m_LastSerializedFileContents); graphObject.graph.OnEnable(); graphObject.graph.ValidateGraph(); @@ -1281,8 +1283,7 @@ public void Initialize(string assetGuid) }; } - UpdateTitle(); - + UpdateTitle(ignoreUnsavedChanges: true); Repaint(); } catch (Exception) diff --git a/Packages/com.unity.shadergraph/Editor/Importers/ShaderGraphImporter.cs b/Packages/com.unity.shadergraph/Editor/Importers/ShaderGraphImporter.cs index aae75129fcc..2eaa4ae3129 100644 --- a/Packages/com.unity.shadergraph/Editor/Importers/ShaderGraphImporter.cs +++ b/Packages/com.unity.shadergraph/Editor/Importers/ShaderGraphImporter.cs @@ -64,6 +64,8 @@ fixed4 frag (v2f i) : SV_Target Fallback Off }"; + public static Texture2D GetIcon() => EditorGUIUtility.IconContent(IconBasePath)?.image as Texture2D; + [SuppressMessage("ReSharper", "UnusedMember.Local")] static string[] GatherDependenciesFromSourceFile(string assetPath) { @@ -237,11 +239,7 @@ public override void OnImportAsset(AssetImportContext ctx) mainObject = ShaderUtil.CreateShaderAsset(ctx, k_ErrorShader, false); } - if (EditorGUIUtility.IconContent(IconBasePath)?.image is Texture2D icon) - { - ctx.AddObjectToAsset("MainAsset", mainObject, icon); - } - + ctx.AddObjectToAsset("MainAsset", mainObject, GetIcon()); ctx.SetMainObject(mainObject); var graphDataReadOnly = new GraphDataReadOnly(graph); diff --git a/Packages/com.unity.shadergraph/Editor/Importers/ShaderSubGraphImporter.cs b/Packages/com.unity.shadergraph/Editor/Importers/ShaderSubGraphImporter.cs index 04001304d70..46b3d9205cc 100644 --- a/Packages/com.unity.shadergraph/Editor/Importers/ShaderSubGraphImporter.cs +++ b/Packages/com.unity.shadergraph/Editor/Importers/ShaderSubGraphImporter.cs @@ -25,6 +25,8 @@ class ShaderSubGraphImporter : ScriptedImporter public const string Extension = "shadersubgraph"; const string IconBasePath = "Packages/com.unity.shadergraph/Editor/Resources/Icons/sg_subgraph_icon.png"; + public static Texture2D GetIcon() => EditorGUIUtility.IconContent(IconBasePath)?.image as Texture2D; + [SuppressMessage("ReSharper", "UnusedMember.Local")] static string[] GatherDependenciesFromSourceFile(string assetPath) { @@ -113,11 +115,7 @@ public override void OnImportAsset(AssetImportContext ctx) messageManager.ClearAll(); } - if (EditorGUIUtility.IconContent(IconBasePath)?.image is Texture2D icon) - { - ctx.AddObjectToAsset("MainAsset", graphAsset, icon); - } - + ctx.AddObjectToAsset("MainAsset", graphAsset, GetIcon()); ctx.SetMainObject(graphAsset); var metadata = ScriptableObject.CreateInstance(); diff --git a/Packages/com.unity.visualeffectgraph/Editor/GraphView/Views/VFXView.cs b/Packages/com.unity.visualeffectgraph/Editor/GraphView/Views/VFXView.cs index 0a16d099879..c72c6c6ee9d 100644 --- a/Packages/com.unity.visualeffectgraph/Editor/GraphView/Views/VFXView.cs +++ b/Packages/com.unity.visualeffectgraph/Editor/GraphView/Views/VFXView.cs @@ -582,7 +582,7 @@ public static Texture2D LoadImage(string text) private void OnCreateAsset() { - VFXTemplateWindow.ShowCreateFromTemplate(this, null); + GraphViewTemplateWindow.ShowCreateFromTemplate(new VFXTemplateHelperInternal(), CreateNewFromTemplate); } public VFXView() @@ -1774,6 +1774,14 @@ void OnCreateNode(NodeCreationContext ctx) } } + public void CreateNewFromTemplate(string templatePath, string assetPath) + { + VisualEffectAssetEditorUtility.CreateTemplateAsset(assetPath, templatePath); + var vfxAsset = AssetDatabase.LoadAssetAtPath(assetPath); + var window = VFXViewWindow.GetWindow(vfxAsset, false); + window.LoadAsset(vfxAsset, null); + } + public void CreateTemplateSystem(string path, Vector2 tPos, VFXGroupNode groupNode) { var resource = VisualEffectResource.GetResourceAtPath(path); diff --git a/Packages/com.unity.visualeffectgraph/Editor/Inspector/AdvancedVisualEffectEditor.cs b/Packages/com.unity.visualeffectgraph/Editor/Inspector/AdvancedVisualEffectEditor.cs index 51c66d95fe2..108d88966e6 100644 --- a/Packages/com.unity.visualeffectgraph/Editor/Inspector/AdvancedVisualEffectEditor.cs +++ b/Packages/com.unity.visualeffectgraph/Editor/Inspector/AdvancedVisualEffectEditor.cs @@ -2,6 +2,8 @@ using System.Linq; using System.Collections.Generic; using System.Reflection; + +using UnityEditor.Experimental.GraphView; using UnityEngine; using UnityEngine.VFX; using UnityEditor.VFX.UI; @@ -753,11 +755,12 @@ private void DetachIfDeleted() private void CreateNewVFX() { - void OnTemplateCreate(string templateFilePath) + void OnTemplateCreate(string templateFilePath, string assetPath) { if (!string.IsNullOrEmpty(templateFilePath)) { - var asset = AssetDatabase.LoadAssetAtPath(templateFilePath); + VisualEffectAssetEditorUtility.CreateTemplateAsset(assetPath, templateFilePath); + var asset = AssetDatabase.LoadAssetAtPath(assetPath); m_VisualEffectAsset.objectReferenceValue = asset; serializedObject.ApplyModifiedProperties(); @@ -765,7 +768,7 @@ void OnTemplateCreate(string templateFilePath) } } - VFXTemplateWindow.ShowCreateFromTemplate(null, OnTemplateCreate); + GraphViewTemplateWindow.ShowCreateFromTemplate(new VFXTemplateHelperInternal(), OnTemplateCreate); } } } diff --git a/Packages/com.unity.visualeffectgraph/Editor/Models/Contexts/Implementations/VFXComposedShading.cs b/Packages/com.unity.visualeffectgraph/Editor/Models/Contexts/Implementations/VFXComposedShading.cs index e05f385587e..5d62e387dec 100644 --- a/Packages/com.unity.visualeffectgraph/Editor/Models/Contexts/Implementations/VFXComposedShading.cs +++ b/Packages/com.unity.visualeffectgraph/Editor/Models/Contexts/Implementations/VFXComposedShading.cs @@ -89,24 +89,26 @@ public override TraitDescription GetDescription(VFXAbstractComposedParticleOutpu desc.hiddenSettings = new(kAlwaysFilteredOutSettings); var srpBinder = VFXLibrary.currentSRPBinder; - if (actualShaderGraph != null && srpBinder != null) + if (actualShaderGraph != null) { - desc.name = shaderGraph != null ? srpBinder.GetShaderName(actualShaderGraph) : string.Empty; - - if (srpBinder.TryGetCastShadowFromMaterial(actualShaderGraph, materialSettings, out var hasShadowCasting)) + desc.name = string.Empty; + if (srpBinder != null) { - desc.hasShadowCasting = hasShadowCasting; - desc.hiddenSettings.Add("castShadows"); - } + desc.name = shaderGraph != null ? srpBinder.GetShaderName(actualShaderGraph) : string.Empty; + if (srpBinder.TryGetCastShadowFromMaterial(actualShaderGraph, materialSettings, out var hasShadowCasting)) + { + desc.hasShadowCasting = hasShadowCasting; + desc.hiddenSettings.Add("castShadows"); + } - if (srpBinder.TryGetQueueOffset(actualShaderGraph, materialSettings, out var sortingPriority)) - { - desc.sortingPriority = sortingPriority; - desc.hiddenSettings.Add("sortingPriority"); + if (srpBinder.TryGetQueueOffset(actualShaderGraph, materialSettings, out var sortingPriority)) + { + desc.sortingPriority = sortingPriority; + desc.hiddenSettings.Add("sortingPriority"); + } + desc.supportMotionVectorPerVertex = srpBinder.GetSupportsMotionVectorPerVertex(actualShaderGraph, materialSettings); + desc.blendMode = srpBinder.GetBlendModeFromMaterial(actualShaderGraph, materialSettings); } - - desc.supportMotionVectorPerVertex = srpBinder.GetSupportsMotionVectorPerVertex(actualShaderGraph, materialSettings); - desc.blendMode = srpBinder.GetBlendModeFromMaterial(actualShaderGraph, materialSettings); desc.hasAlphaClipping = actualShaderGraph.alphaClipping; var shaderGraphProperties = VFXShaderGraphHelpers.GetProperties(actualShaderGraph); diff --git a/Packages/com.unity.visualeffectgraph/Editor/TemplateWindow/CreateFromTemplateDropDownButton.cs b/Packages/com.unity.visualeffectgraph/Editor/TemplateWindow/CreateFromTemplateDropDownButton.cs index e8bb0072095..15136f62cf7 100644 --- a/Packages/com.unity.visualeffectgraph/Editor/TemplateWindow/CreateFromTemplateDropDownButton.cs +++ b/Packages/com.unity.visualeffectgraph/Editor/TemplateWindow/CreateFromTemplateDropDownButton.cs @@ -1,4 +1,5 @@ using UnityEditor.Experimental; +using UnityEditor.Experimental.GraphView; using UnityEditor.VFX.UI; using UnityEngine; @@ -8,9 +9,9 @@ namespace UnityEditor.VFX { class CreateFromTemplateDropDownButton : DropDownButtonBase { - private const string mainButtonName = "create-button"; + private const string k_MainButtonName = "create-button"; - private Button m_InsertButton; + private readonly Button m_InsertButton; public CreateFromTemplateDropDownButton(VFXView vfxView) : base( @@ -18,7 +19,7 @@ public CreateFromTemplateDropDownButton(VFXView vfxView) vfxView, "VFXCreateFromTemplateDropDownPanel", "Insert a template into current asset\nHold CTRL key and click to create a new VFX", - mainButtonName, + k_MainButtonName, EditorResources.iconsPath + "CreateAddNew.png", true, false) @@ -26,7 +27,7 @@ public CreateFromTemplateDropDownButton(VFXView vfxView) var createNew = m_PopupContent.Q /// The path to a Visual Effect asset. - /// The structure that contains template information. + /// The structure that contains template information. /// Returns true if the Visual Effect asset has template information, otherwise it returns false. - public static bool TryGetTemplate(string vfxPath, out VFXTemplateDescriptor template) + public static bool TryGetTemplate(string vfxPath, out VFXTemplateDescriptor vfxTemplateDescriptor) { - var importer = (VisualEffectImporter)AssetImporter.GetAtPath(vfxPath); - var nativeTemplate = importer.templateProperty; - - if (!string.IsNullOrEmpty(nativeTemplate.name)) + if (VFXTemplateHelperInternal.TryGetTemplateStatic(vfxPath, out var graphViewTemplate)) { - template = new VFXTemplateDescriptor + vfxTemplateDescriptor = new VFXTemplateDescriptor { - name = nativeTemplate.name, - category = nativeTemplate.category, - description = nativeTemplate.description, - icon = nativeTemplate.icon, - thumbnail = nativeTemplate.thumbnail, + name = graphViewTemplate.name, + category = graphViewTemplate.category, + description = graphViewTemplate.description, + icon = graphViewTemplate.icon, + thumbnail = graphViewTemplate.thumbnail, }; - return true; } - template = default; + vfxTemplateDescriptor = default; return false; } @@ -38,29 +69,18 @@ public static bool TryGetTemplate(string vfxPath, out VFXTemplateDescriptor temp /// This method creates or updates a Visual Effect asset template. /// /// The path to the existing Visual Effect asset. - /// The structure that contains all template information. + /// The structure that contains all template information. /// Returns true if the template is created, otherwise it returns false. - public static bool TrySetTemplate(string vfxPath, VFXTemplateDescriptor template) + public static bool TrySetTemplate(string vfxPath, VFXTemplateDescriptor vfxTemplateDescriptor) { - if (string.IsNullOrEmpty(vfxPath)) - return false; - - if (AssetDatabase.AssetPathExists(vfxPath)) + return VFXTemplateHelperInternal.TrySetTemplateStatic(vfxPath, new GraphViewTemplateDescriptor { - var importer = (VisualEffectImporter)AssetImporter.GetAtPath(vfxPath); - var nativeTemplate = new VFXTemplate - { - name = template.name, - category = template.category, - description = template.description, - icon = template.icon, - thumbnail = template.thumbnail, - }; - importer.templateProperty = nativeTemplate; - return true; - } - - return false; + name = vfxTemplateDescriptor.name, + category = vfxTemplateDescriptor.category, + description = vfxTemplateDescriptor.description, + icon = vfxTemplateDescriptor.icon, + thumbnail = vfxTemplateDescriptor.thumbnail, + }); } } } diff --git a/Packages/com.unity.visualeffectgraph/Editor/TemplateWindow/VFXTemplateHelper.cs.meta b/Packages/com.unity.visualeffectgraph/Editor/TemplateWindow/VFXTemplateHelper.cs.meta index 75c8c9b3fa1..1ac1c682c2a 100644 --- a/Packages/com.unity.visualeffectgraph/Editor/TemplateWindow/VFXTemplateHelper.cs.meta +++ b/Packages/com.unity.visualeffectgraph/Editor/TemplateWindow/VFXTemplateHelper.cs.meta @@ -1,3 +1,3 @@ fileFormatVersion: 2 -guid: 020bd1d48e04472486fdf803a279364a -timeCreated: 1673371801 \ No newline at end of file +guid: 1544e89dbff4436991f7b7617c5a5578 +timeCreated: 1742490022 \ No newline at end of file diff --git a/Packages/com.unity.visualeffectgraph/Editor/TemplateWindow/VFXTemplateHelperInternal.cs b/Packages/com.unity.visualeffectgraph/Editor/TemplateWindow/VFXTemplateHelperInternal.cs new file mode 100644 index 00000000000..d737807a5c7 --- /dev/null +++ b/Packages/com.unity.visualeffectgraph/Editor/TemplateWindow/VFXTemplateHelperInternal.cs @@ -0,0 +1,114 @@ +using System; + +using UnityEditor.Experimental.GraphView; + +namespace UnityEditor.VFX +{ + /// + /// This class provides all useful information for the GraphView template window + /// It also allows to read or write template information from/to a VFX asset + /// + class VFXTemplateHelperInternal : ITemplateHelper + { + class SaveFileDialog : GraphViewTemplateWindow.ISaveFileDialogHelper + { + public string OpenSaveFileDialog() + { + return EditorUtility.SaveFilePanelInProject("", "New VFX", "vfx", "Create new VisualEffect Graph"); + } + } + + 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 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(); + + + /// + /// This method is called each time a template is used. + /// This is the good place to implement analytics + /// + /// Template that has just been used + public void RaiseTemplateUsed(GraphViewTemplateDescriptor usedTemplate) + { + // For legal reason we should only monitor built-in templates usage + if (string.Compare(usedTemplate.category, builtInCategory, StringComparison.OrdinalIgnoreCase) == 0) + { + VFXAnalytics.GetInstance().OnSystemTemplateCreated(usedTemplate.name); + } + } + + /// + /// This method gets template information for any Visual Effect asset. + /// + /// The path to a Visual Effect asset. + /// The structure that contains template information. + /// Returns true if the Visual Effect asset has template information, otherwise it returns false. + public bool TryGetTemplate(string vfxPath, out GraphViewTemplateDescriptor graphViewTemplate) => TryGetTemplateStatic(vfxPath, out graphViewTemplate); + + /// + /// This method creates or updates a Visual Effect asset template. + /// + /// The path to the existing Visual Effect asset. + /// The structure that contains all template information. + /// Returns true if the template is created, otherwise it returns false. + public bool TrySetTemplate(string vfxPath, GraphViewTemplateDescriptor graphViewTemplate) => TrySetTemplateStatic(vfxPath, graphViewTemplate); + + internal static bool TryGetTemplateStatic(string vfxPath, out GraphViewTemplateDescriptor graphViewTemplate) + { + var importer = (VisualEffectImporter)AssetImporter.GetAtPath(vfxPath); + var nativeTemplate = importer.templateProperty; + + if (!string.IsNullOrEmpty(nativeTemplate.name)) + { + graphViewTemplate = new GraphViewTemplateDescriptor + { + name = nativeTemplate.name, + category = nativeTemplate.category, + description = nativeTemplate.description, + icon = nativeTemplate.icon, + thumbnail = nativeTemplate.thumbnail, + }; + + return true; + } + + graphViewTemplate = default; + return false; + } + + internal static bool TrySetTemplateStatic(string vfxPath, GraphViewTemplateDescriptor graphViewTemplate) + { + if (string.IsNullOrEmpty(vfxPath)) + return false; + + if (AssetDatabase.AssetPathExists(vfxPath)) + { + var importer = (VisualEffectImporter)AssetImporter.GetAtPath(vfxPath); + var nativeTemplate = new VFXTemplate + { + name = graphViewTemplate.name, + category = graphViewTemplate.category, + description = graphViewTemplate.description, + icon = graphViewTemplate.icon, + thumbnail = graphViewTemplate.thumbnail, + }; + importer.templateProperty = nativeTemplate; + return true; + } + + return false; + } + } +} diff --git a/Packages/com.unity.visualeffectgraph/Editor/TemplateWindow/VFXTemplateHelperInternal.cs.meta b/Packages/com.unity.visualeffectgraph/Editor/TemplateWindow/VFXTemplateHelperInternal.cs.meta new file mode 100644 index 00000000000..75c8c9b3fa1 --- /dev/null +++ b/Packages/com.unity.visualeffectgraph/Editor/TemplateWindow/VFXTemplateHelperInternal.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 020bd1d48e04472486fdf803a279364a +timeCreated: 1673371801 \ No newline at end of file diff --git a/Packages/com.unity.visualeffectgraph/Editor/TemplateWindow/VFXTemplateWindow.cs b/Packages/com.unity.visualeffectgraph/Editor/TemplateWindow/VFXTemplateWindow.cs deleted file mode 100644 index d22a4418aaf..00000000000 --- a/Packages/com.unity.visualeffectgraph/Editor/TemplateWindow/VFXTemplateWindow.cs +++ /dev/null @@ -1,489 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Threading; - -using Unity.UI.Builder; -using UnityEditor.Experimental; -using UnityEditor.PackageManager; -using UnityEditor.PackageManager.UI; -using UnityEditor.VFX.UI; - -using UnityEngine; -using UnityEngine.Rendering; -using UnityEngine.UIElements; -using UnityEngine.VFX; - -namespace UnityEditor.VFX -{ - internal interface IVFXTemplateDescriptor - { - string header { get; } - } - - internal class VFXTemplateWindow : EditorWindow - { - internal interface ISaveFileDialogHelper - { - string OpenSaveFileDialog(string title, string defaultName, string extension, string message); - } - - private class SaveFileDialogHelper : ISaveFileDialogHelper - { - public string OpenSaveFileDialog(string title, string defaultName, string extension, string message) => EditorUtility.SaveFilePanelInProject(title, defaultName, extension, message); - } - - private class VFXTemplateSection : IVFXTemplateDescriptor - { - public VFXTemplateSection(string text) - { - header = text; - } - public string header { get; } - } - - private const string LearningSampleName = "Learning Templates"; - private const string VFXTemplateWindowDocUrl = "https://docs.unity3d.com/Packages/com.unity.visualeffectgraph@{0}/manual/Templates-window.html"; - private const string BuiltInCategory = "Default VFX Graph Templates"; - private const string EmptyTemplateName = "Empty VFX"; - private const string EmptyTemplateDescription = "Create a completely empty VFX asset"; - private const string LastSelectedGuidKey = "VFXTemplateWindow.LastSelectedGuid"; - private const string CreateNewVFXAssetTitle = "Create new VFX Asset"; - private const string InsertTemplateTitle = "Insert a template into current VFX Asset"; - private const float PackageManagerTimeout = 5f; // 5s - - private static readonly Dictionary s_ModeToTitle = new () - { - { CreateMode.CreateNew, CreateNewVFXAssetTitle }, - { CreateMode.Insert, InsertTemplateTitle }, - { CreateMode.None, CreateNewVFXAssetTitle }, - }; - - private readonly List> m_TemplatesTree = new (); - private readonly ISaveFileDialogHelper m_SaveFileDialogHelper = new SaveFileDialogHelper(); - - private TreeView m_ListOfTemplates; - private Texture2D m_CustomTemplateIcon; - private Image m_DetailsScreenshot; - private Label m_DetailsTitle; - private Label m_DetailsDescription; - private VisualTreeAsset m_ItemTemplate; - private Action m_VFXAssetCreationCallback; - private string m_LastSelectedTemplatePath; - private int m_LastSelectedIndex; - private CreateMode m_CurrentMode; - private Action m_UserCallback; - private string m_LastSelectedTemplateGuid; - private VFXView m_VfxView; - private VFXTemplateDescriptor m_SelectedTemplate; - private Button installButton; - - private enum CreateMode - { - CreateNew, - Insert, - None, - } - - public static void ShowCreateFromTemplate(VFXView vfxView, Action callback) => ShowInternal(vfxView, CreateMode.CreateNew, callback); - public static void ShowInsertTemplate(VFXView vfxView) => ShowInternal(vfxView, CreateMode.Insert); - public static void PickTemplate(Action callback) => ShowInternal(null, CreateMode.None, callback); - - private static void ShowInternal(VFXView vfxView, CreateMode mode, Action callback = null) - { - var templateWindow = EditorWindow.GetWindow(true, s_ModeToTitle[mode], false); - templateWindow.Setup(vfxView, mode, callback); - } - - private void Setup(VFXView vfxView, CreateMode mode, Action callback) - { - minSize = new Vector2(800, 300); - m_VfxView = vfxView; - m_UserCallback = callback; - m_CurrentMode = mode; - SetCallBack(); - LoadTemplates(); - } - - private void CreateGUI() - { - m_ItemTemplate = VFXView.LoadUXML("VFXTemplateItem"); - var tpl = VFXView.LoadUXML("VFXTemplateWindow"); - tpl.CloneTree(rootVisualElement); - rootVisualElement.AddStyleSheetPath("VFXTemplateWindow"); - - rootVisualElement.name = "VFXTemplateWindowRoot"; - rootVisualElement.Q