diff --git a/com.unity.render-pipelines.core/Runtime/Common/ListBuffer.cs b/com.unity.render-pipelines.core/Runtime/Common/ListBuffer.cs new file mode 100644 index 00000000000..594313c44f8 --- /dev/null +++ b/com.unity.render-pipelines.core/Runtime/Common/ListBuffer.cs @@ -0,0 +1,163 @@ +using System; +using Unity.Collections.LowLevel.Unsafe; + +namespace UnityEngine.Rendering +{ + /// + /// A list that stores value on a provided memory buffer. + /// + /// Usually use this to have a list on stack allocated memory. + /// + /// The type of the data stored in the list. + public unsafe struct ListBuffer + where T: unmanaged + { + private T* m_BufferPtr; + private int m_Capacity; + private int* m_CountPtr; + + /// + /// The pointer to the memory storage. + /// + internal T* BufferPtr => m_BufferPtr; + + /// + /// The number of item in the list. + /// + public int Count => *m_CountPtr; + + /// + /// The maximum number of item stored in this list. + /// + public int Capacity => m_Capacity; + + /// + /// Instantiate a new list. + /// + /// The address in memory to store the data. + /// The address in memory to store the number of item of this list.. + /// The number of that can be stored in the buffer. + public ListBuffer(T* bufferPtr, int* countPtr, int capacity) + { + m_BufferPtr = bufferPtr; + m_Capacity = capacity; + m_CountPtr = countPtr; + } + + /// + /// Get an item from the list. + /// + /// The index of the item to get. + /// A reference to the item. + /// If the index is invalid. + public ref T this[in int index] + { + get + { + if (index < 0 || index >= Count) + throw new IndexOutOfRangeException( + $"Expected a value between 0 and {Count}, but received {index}."); + return ref m_BufferPtr[index]; + } + } + + /// + /// Get an item from the list. + /// + /// Safety: index must be inside the bounds of the list. + /// + /// The index of the item to get. + /// A reference to the item. + public unsafe ref T GetUnchecked(in int index) => ref m_BufferPtr[index]; + + /// + /// Try to add a value in the list. + /// + /// A reference to the value to add. + /// + /// true when the value was added, + /// false when the value was not added because the capacity was reached. + /// + public bool TryAdd(in T value) + { + if (Count >= m_Capacity) + return false; + + m_BufferPtr[Count] = value; + ++*m_CountPtr; + return true; + } + + /// + /// Copy the content of this list into another buffer in memory. + /// + /// Safety: + /// * The destination must have enough memory to receive the copied data. + /// + /// The destination buffer of the copy operation. + /// The index of the first element that will be copied in the destination buffer. + /// The number of item to copy. + public unsafe void CopyTo(T* dstBuffer, int startDstIndex, int copyCount) + { + UnsafeUtility.MemCpy( dstBuffer + startDstIndex, m_BufferPtr, + UnsafeUtility.SizeOf() * copyCount); + } + + /// + /// Try to copy the list into another list. + /// + /// The destination of the copy. + /// + /// * true when the copy was performed. + /// * false when the copy was aborted because the destination have a capacity too small. + /// + public bool TryCopyTo(ListBuffer other) + { + if (other.Count + Count >= other.m_Capacity) + return false; + + UnsafeUtility.MemCpy( other.m_BufferPtr + other.Count, m_BufferPtr, UnsafeUtility.SizeOf() * Count); + *other.m_CountPtr += Count; + return true; + } + + /// + /// Try to copy the data from a buffer in this list. + /// + /// The pointer of the source memory to copy. + /// The number of item to copy from the source buffer. + /// + /// * true when the copy was performed. + /// * false when the copy was aborted because the capacity of this list is too small. + /// + public bool TryCopyFrom(T* srcPtr, int count) + { + if (count + Count > m_Capacity) + return false; + + UnsafeUtility.MemCpy( m_BufferPtr + Count, srcPtr, UnsafeUtility.SizeOf() * count); + *m_CountPtr += count; + return true; + } + } + + /// + /// Extensions for . + /// + public static class ListBufferExtensions + { + /// + /// Perform a quick sort on a . + /// + /// The list to sort. + /// The type of the element in the list. + public static void QuickSort(this ListBuffer self) + where T : unmanaged, IComparable + { + unsafe + { + CoreUnsafeUtils.QuickSort(self.Count, self.BufferPtr); + } + } + } +} diff --git a/com.unity.render-pipelines.core/Runtime/Common/ListBuffer.cs.meta b/com.unity.render-pipelines.core/Runtime/Common/ListBuffer.cs.meta new file mode 100644 index 00000000000..1259d25752c --- /dev/null +++ b/com.unity.render-pipelines.core/Runtime/Common/ListBuffer.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 188d5dc897b64646b3757571725337ce +timeCreated: 1591792904 \ No newline at end of file diff --git a/com.unity.render-pipelines.high-definition/CHANGELOG.md b/com.unity.render-pipelines.high-definition/CHANGELOG.md index f29fc7bf234..13ec4f22be2 100644 --- a/com.unity.render-pipelines.high-definition/CHANGELOG.md +++ b/com.unity.render-pipelines.high-definition/CHANGELOG.md @@ -695,6 +695,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Fixed an issue where manipulating the color wheels in a volume component would reset the cursor every time. - Fixed an issue where static sky lighting would not be updated for a new scene until it's reloaded at least once. - Fixed culling for decals when used in prefabs and edited in context. +- Force to rebake probe with missing baked texture. (1253367) ### Changed - Improve MIP selection for decals on Transparents diff --git a/com.unity.render-pipelines.high-definition/Editor/Lighting/Reflection/HDBakedReflectionSystem.cs b/com.unity.render-pipelines.high-definition/Editor/Lighting/Reflection/HDBakedReflectionSystem.cs index a72d37ea47e..f173f379020 100644 --- a/com.unity.render-pipelines.high-definition/Editor/Lighting/Reflection/HDBakedReflectionSystem.cs +++ b/com.unity.render-pipelines.high-definition/Editor/Lighting/Reflection/HDBakedReflectionSystem.cs @@ -119,6 +119,8 @@ IScriptableBakedReflectionSystemStageNotifier handle // a. If we have to remove a baked data // b. If we have to bake a probe // 4. Bake all required probes + // a. Bake probe that were added or modified + // b. Bake probe with a missing baked texture // 5. Remove unused baked data // 6. Update probe assets @@ -135,11 +137,31 @@ IScriptableBakedReflectionSystemStageNotifier handle // == 2. == var states = stackalloc HDProbeBakingState[bakedProbeCount]; + // A list of indices of probe we may want to force to rebake, even if the hashes matches. + // Usually, add a probe when something external to its state or the world state forces the bake. + var probeForcedToBakeIndices = stackalloc int[bakedProbeCount]; + var probeForcedToBakeIndicesCount = 0; + var probeForcedToBakeIndicesList = new ListBuffer( + probeForcedToBakeIndices, + &probeForcedToBakeIndicesCount, + bakedProbeCount + ); + ComputeProbeInstanceID(bakedProbes, states); ComputeProbeSettingsHashes(bakedProbes, states); // TODO: Handle bounce dependency here ComputeProbeBakingHashes(bakedProbeCount, allProbeDependencyHash, states); + // Force to rebake probe with missing baked texture + for (var i = 0; i < bakedProbeCount; ++i) + { + var instanceId = states[i].instanceID; + var probe = (HDProbe)EditorUtility.InstanceIDToObject(instanceId); + if (probe.bakedTexture != null && !probe.bakedTexture.Equals(null)) continue; + + probeForcedToBakeIndicesList.TryAdd(i); + } + CoreUnsafeUtils.QuickSort( bakedProbeCount, states ); @@ -173,7 +195,7 @@ IScriptableBakedReflectionSystemStageNotifier handle } } - if (operationCount > 0) + if (operationCount > 0 || probeForcedToBakeIndicesList.Count > 0) { // == 4. == var cubemapSize = (int)hdPipeline.currentPlatformRenderPipelineSettings.lightLoopSettings.reflectionCubemapSize; @@ -185,33 +207,66 @@ IScriptableBakedReflectionSystemStageNotifier handle 0 ); - // Render probes - for (int i = 0; i < addCount; ++i) + // Compute indices of probes to bake: added, modified probe or with a missing baked texture. + var toBakeIndices = stackalloc int[bakedProbeCount]; + var toBakeIndicesCount = 0; + var toBakeIndicesList = new ListBuffer(toBakeIndices, &toBakeIndicesCount, bakedProbeCount); + { + // Note: we will add probes from change check and baked texture missing check. + // So we can add at most 2 time the probe in the list. + var toBakeIndicesTmp = stackalloc int[bakedProbeCount * 2]; + var toBakeIndicesTmpCount = 0; + var toBakeIndicesTmpList = + new ListBuffer(toBakeIndicesTmp, &toBakeIndicesTmpCount, bakedProbeCount * 2); + + // Add the indices from the added or modified detection check + toBakeIndicesTmpList.TryCopyFrom(addIndices, addCount); + // Add the probe with missing baked texture check + probeForcedToBakeIndicesList.TryCopyTo(toBakeIndicesTmpList); + + // Sort indices + toBakeIndicesTmpList.QuickSort(); + // Add to final list without the duplicates + var lastValue = int.MaxValue; + for (var i = 0; i < toBakeIndicesTmpList.Count; ++i) + { + if (lastValue == toBakeIndicesTmpList.GetUnchecked(i)) + // Skip duplicates + continue; + + lastValue = toBakeIndicesTmpList.GetUnchecked(i); + toBakeIndicesList.TryAdd(lastValue); + } + } + + // Render probes that were added or modified + for (int i = 0; i < toBakeIndicesList.Count; ++i) { handle.EnterStage( (int)BakingStages.ReflectionProbes, string.Format("Reflection Probes | {0} jobs", addCount), - i / (float)addCount + i / (float)toBakeIndicesCount ); - var index = addIndices[i]; + var index = toBakeIndicesList.GetUnchecked(i); var instanceId = states[index].instanceID; var probe = (HDProbe)EditorUtility.InstanceIDToObject(instanceId); var cacheFile = GetGICacheFileForHDProbe(states[index].probeBakingHash); - var planarRT = HDRenderUtilities.CreatePlanarProbeRenderTarget((int)probe.resolution); // Get from cache or render the probe if (!File.Exists(cacheFile)) + { + var planarRT = HDRenderUtilities.CreatePlanarProbeRenderTarget((int)probe.resolution); RenderAndWriteToFile(probe, cacheFile, cubeRT, planarRT); - - planarRT.Release(); + planarRT.Release(); + } } cubeRT.Release(); // Copy texture from cache - for (int i = 0; i < addCount; ++i) + for (int i = 0; i < toBakeIndicesList.Count; ++i) { - var index = addIndices[i]; + var index = toBakeIndicesList.GetUnchecked(i); var instanceId = states[index].instanceID; var probe = (HDProbe)EditorUtility.InstanceIDToObject(instanceId); var cacheFile = GetGICacheFileForHDProbe(states[index].probeBakingHash); @@ -235,7 +290,7 @@ IScriptableBakedReflectionSystemStageNotifier handle AssetDatabase.StartAssetEditing(); for (int i = 0; i < bakedProbeCount; ++i) { - var index = addIndices[i]; + var index = toBakeIndicesList.GetUnchecked(i); var instanceId = states[index].instanceID; var probe = (HDProbe)EditorUtility.InstanceIDToObject(instanceId); var bakedTexturePath = HDBakingUtilities.GetBakedTextureFilePath(probe); @@ -246,9 +301,9 @@ IScriptableBakedReflectionSystemStageNotifier handle } // Import assets AssetDatabase.StartAssetEditing(); - for (int i = 0; i < addCount; ++i) + for (int i = 0; i < toBakeIndicesList.Count; ++i) { - var index = addIndices[i]; + var index = toBakeIndicesList.GetUnchecked(i); var instanceId = states[index].instanceID; var probe = (HDProbe)EditorUtility.InstanceIDToObject(instanceId); var bakedTexturePath = HDBakingUtilities.GetBakedTextureFilePath(probe); @@ -275,9 +330,9 @@ IScriptableBakedReflectionSystemStageNotifier handle targetBakedStates[targetI++] = m_HDProbeBakedStates[i]; } // Add new baked states - for (int i = 0; i < addCount; ++i) + for (int i = 0; i < toBakeIndicesList.Count; ++i) { - var state = states[addIndices[i]]; + var state = states[toBakeIndicesList.GetUnchecked(i)]; targetBakedStates[targetI++] = new HDProbeBakedState { instanceID = state.instanceID,