Initial project commit

This commit is contained in:
2026-01-08 16:50:20 +00:00
commit f0c5a8b267
29596 changed files with 4861782 additions and 0 deletions

View File

@@ -0,0 +1,223 @@
using System;
using System.Collections.Generic;
using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
using UnityEngine.Assertions;
using static UnityEngine.Rendering.RenderersParameters;
namespace UnityEngine.Rendering
{
internal struct GPUInstanceComponentDesc
{
public int propertyID;
public int byteSize;
public bool isOverriden;
public bool isPerInstance;
public InstanceType instanceType;
public InstanceComponentGroup componentGroup;
public GPUInstanceComponentDesc(int inPropertyID, int inByteSize, bool inIsOverriden, bool inPerInstance, InstanceType inInstanceType, InstanceComponentGroup inComponentType)
{
propertyID = inPropertyID;
byteSize = inByteSize;
isOverriden = inIsOverriden;
isPerInstance = inPerInstance;
instanceType = inInstanceType;
componentGroup = inComponentType;
}
}
internal class GPUInstanceDataBuffer : IDisposable
{
private static int s_NextLayoutVersion = 0;
public static int NextVersion() { return ++s_NextLayoutVersion; }
public InstanceNumInfo instanceNumInfo;
public NativeArray<int> instancesNumPrefixSum;
public NativeArray<int> instancesSpan;
public int byteSize;
public int perInstanceComponentCount;
public int version;
public int layoutVersion;
public GraphicsBuffer gpuBuffer;
public GraphicsBuffer validComponentsIndicesGpuBuffer;
public GraphicsBuffer componentAddressesGpuBuffer;
public GraphicsBuffer componentInstanceIndexRangesGpuBuffer;
public GraphicsBuffer componentByteCountsGpuBuffer;
public NativeArray<GPUInstanceComponentDesc> descriptions;
public NativeArray<MetadataValue> defaultMetadata;
public NativeArray<int> gpuBufferComponentAddress;
public NativeParallelHashMap<int, int> nameToMetadataMap;
public bool valid => instancesSpan.IsCreated;
private static GPUInstanceIndex CPUInstanceToGPUInstance(in NativeArray<int> instancesNumPrefixSum, InstanceHandle instance)
{
bool valid = instance.valid && instance.type < InstanceType.Count;
#if DEBUG
Assert.IsTrue(valid);
#endif
if (!valid)
return GPUInstanceIndex.Invalid;
int instanceType = (int)instance.type;
int perTypeInstanceIndex = instance.instanceIndex;
int gpuInstanceIndex = instancesNumPrefixSum[instanceType] + perTypeInstanceIndex;
return new GPUInstanceIndex { index = gpuInstanceIndex };
}
public int GetPropertyIndex(int propertyID, bool assertOnFail = true)
{
if (nameToMetadataMap.TryGetValue(propertyID, out int componentIndex))
{
return componentIndex;
}
if (assertOnFail)
Assert.IsTrue(false, "Count not find gpu address for parameter specified: " + propertyID);
return -1;
}
public int GetGpuAddress(string strName, bool assertOnFail = true)
{
int componentIndex = GetPropertyIndex(Shader.PropertyToID(strName), false);
if (assertOnFail && componentIndex == -1)
Assert.IsTrue(false, "Count not find gpu address for parameter specified: " + strName);
return componentIndex != -1 ? gpuBufferComponentAddress[componentIndex] : -1;
}
public int GetGpuAddress(int propertyID, bool assertOnFail = true)
{
int componentIndex = GetPropertyIndex(propertyID, assertOnFail);
return componentIndex != -1 ? gpuBufferComponentAddress[componentIndex] : -1;
}
public GPUInstanceIndex CPUInstanceToGPUInstance(InstanceHandle instance)
{
return CPUInstanceToGPUInstance(instancesNumPrefixSum, instance);
}
public unsafe InstanceHandle GPUInstanceToCPUInstance(GPUInstanceIndex gpuInstanceIndex)
{
var instanceIndex = gpuInstanceIndex.index;
InstanceType instanceType = InstanceType.Count;
for(int i = 0; i < (int)InstanceType.Count; ++i)
{
int instanceNum = instanceNumInfo.GetInstanceNum((InstanceType)i);
if(instanceIndex < instanceNum)
{
instanceType = (InstanceType)i;
break;
}
instanceIndex -= instanceNum;
}
if(instanceType == InstanceType.Count)
return InstanceHandle.Invalid;
Assert.IsTrue(instanceIndex < instanceNumInfo.GetInstanceNum(instanceType));
return InstanceHandle.Create(instanceIndex, instanceType);
}
public void CPUInstanceArrayToGPUInstanceArray(NativeArray<InstanceHandle> instances, NativeArray<GPUInstanceIndex> gpuInstanceIndices)
{
Assert.AreEqual(instances.Length, gpuInstanceIndices.Length);
Profiling.Profiler.BeginSample("CPUInstanceArrayToGPUInstanceArray");
new ConvertCPUInstancesToGPUInstancesJob { instancesNumPrefixSum = instancesNumPrefixSum, instances = instances, gpuInstanceIndices = gpuInstanceIndices }
.Schedule(instances.Length, ConvertCPUInstancesToGPUInstancesJob.k_BatchSize).Complete();
Profiling.Profiler.EndSample();
}
public void Dispose()
{
if(instancesSpan.IsCreated)
instancesSpan.Dispose();
if(instancesNumPrefixSum.IsCreated)
instancesNumPrefixSum.Dispose();
if (descriptions.IsCreated)
descriptions.Dispose();
if (defaultMetadata.IsCreated)
defaultMetadata.Dispose();
if (gpuBufferComponentAddress.IsCreated)
gpuBufferComponentAddress.Dispose();
if (nameToMetadataMap.IsCreated)
nameToMetadataMap.Dispose();
if (gpuBuffer != null)
gpuBuffer.Release();
if (validComponentsIndicesGpuBuffer != null)
validComponentsIndicesGpuBuffer.Release();
if (componentAddressesGpuBuffer != null)
componentAddressesGpuBuffer.Release();
if (componentInstanceIndexRangesGpuBuffer != null)
componentInstanceIndexRangesGpuBuffer.Release();
if (componentByteCountsGpuBuffer != null)
componentByteCountsGpuBuffer.Release();
}
public ReadOnly AsReadOnly()
{
return new ReadOnly(this);
}
internal readonly struct ReadOnly
{
private readonly NativeArray<int> instancesNumPrefixSum;
public ReadOnly(GPUInstanceDataBuffer buffer)
{
instancesNumPrefixSum = buffer.instancesNumPrefixSum;
}
public GPUInstanceIndex CPUInstanceToGPUInstance(InstanceHandle instance)
{
return GPUInstanceDataBuffer.CPUInstanceToGPUInstance(instancesNumPrefixSum, instance);
}
public void CPUInstanceArrayToGPUInstanceArray(NativeArray<InstanceHandle> instances, NativeArray<GPUInstanceIndex> gpuInstanceIndices)
{
Assert.AreEqual(instances.Length, gpuInstanceIndices.Length);
Profiling.Profiler.BeginSample("CPUInstanceArrayToGPUInstanceArray");
new ConvertCPUInstancesToGPUInstancesJob { instancesNumPrefixSum = instancesNumPrefixSum, instances = instances, gpuInstanceIndices = gpuInstanceIndices }
.Schedule(instances.Length, ConvertCPUInstancesToGPUInstancesJob.k_BatchSize).Complete();
Profiling.Profiler.EndSample();
}
}
[BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)]
struct ConvertCPUInstancesToGPUInstancesJob : IJobParallelFor
{
public const int k_BatchSize = 512;
[ReadOnly] public NativeArray<int> instancesNumPrefixSum;
[ReadOnly] public NativeArray<InstanceHandle> instances;
[WriteOnly] public NativeArray<GPUInstanceIndex> gpuInstanceIndices;
public void Execute(int index)
{
gpuInstanceIndices[index] = CPUInstanceToGPUInstance(instancesNumPrefixSum, instances[index]);
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 390ad9eb5576b8e46bf6230c1bb8081b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,579 @@
using Unity.Burst;
using Unity.Collections;
using UnityEngine.Assertions;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Jobs;
using System;
namespace UnityEngine.Rendering
{
internal struct GPUInstanceDataBufferBuilder : IDisposable
{
private NativeList<GPUInstanceComponentDesc> m_Components;
private MetadataValue CreateMetadataValue(int nameID, int gpuAddress, bool isOverridden)
{
const uint kIsOverriddenBit = 0x80000000;
return new MetadataValue
{
NameID = nameID,
Value = (uint)gpuAddress | (isOverridden ? kIsOverriddenBit : 0),
};
}
public void AddComponent<T>(int propertyID, bool isOverriden, bool isPerInstance, InstanceType instanceType, InstanceComponentGroup componentGroup = InstanceComponentGroup.Default) where T : unmanaged
{
AddComponent(propertyID, isOverriden, UnsafeUtility.SizeOf<T>(), isPerInstance, instanceType, componentGroup);
}
public void AddComponent(int propertyID, bool isOverriden, int byteSize, bool isPerInstance, InstanceType instanceType, InstanceComponentGroup componentGroup)
{
if (!m_Components.IsCreated)
m_Components = new NativeList<GPUInstanceComponentDesc>(64, Allocator.Temp);
if (m_Components.Length > 0)
Assert.IsTrue(m_Components[m_Components.Length - 1].instanceType <= instanceType, "Added components must be sorted by InstanceType for better memory layout.");
m_Components.Add(new GPUInstanceComponentDesc(propertyID, byteSize, isOverriden, isPerInstance, instanceType, componentGroup));
}
public unsafe GPUInstanceDataBuffer Build(in InstanceNumInfo instanceNumInfo)
{
int perInstanceComponentCounts = 0;
var perInstanceComponentIndices = new NativeArray<int>(m_Components.Length, Allocator.Temp);
var componentAddresses = new NativeArray<int>(m_Components.Length, Allocator.Temp);
var componentByteSizes = new NativeArray<int>(m_Components.Length, Allocator.Temp);
var componentInstanceIndexRanges = new NativeArray<Vector2Int>(m_Components.Length, Allocator.Temp);
GPUInstanceDataBuffer newBuffer = new GPUInstanceDataBuffer();
newBuffer.instanceNumInfo = instanceNumInfo;
newBuffer.instancesNumPrefixSum = new NativeArray<int>((int)InstanceType.Count, Allocator.Persistent);
newBuffer.instancesSpan = new NativeArray<int>((int)InstanceType.Count, Allocator.Persistent);
int sum = 0;
for (int i = 0; i < (int)InstanceType.Count; ++i)
{
newBuffer.instancesNumPrefixSum[i] = sum;
sum += instanceNumInfo.InstanceNums[i];
newBuffer.instancesSpan[i] = instanceNumInfo.GetInstanceNumIncludingChildren((InstanceType)i);
}
newBuffer.layoutVersion = GPUInstanceDataBuffer.NextVersion();
newBuffer.version = 0;
newBuffer.defaultMetadata = new NativeArray<MetadataValue>(m_Components.Length, Allocator.Persistent);
newBuffer.descriptions = new NativeArray<GPUInstanceComponentDesc>(m_Components.Length, Allocator.Persistent);
newBuffer.nameToMetadataMap = new NativeParallelHashMap<int, int>(m_Components.Length, Allocator.Persistent);
newBuffer.gpuBufferComponentAddress = new NativeArray<int>(m_Components.Length, Allocator.Persistent);
//Initial offset, must be 0, 0, 0, 0.
int vec4Size = UnsafeUtility.SizeOf<Vector4>();
int byteOffset = 4 * vec4Size;
for (int c = 0; c < m_Components.Length; ++c)
{
var componentDesc = m_Components[c];
newBuffer.descriptions[c] = componentDesc;
int instancesBegin = newBuffer.instancesNumPrefixSum[(int)componentDesc.instanceType];
int instancesEnd = instancesBegin + newBuffer.instancesSpan[(int)componentDesc.instanceType];
int instancesNum = componentDesc.isPerInstance ? instancesEnd - instancesBegin : 1;
Assert.IsTrue(instancesNum >= 0);
componentInstanceIndexRanges[c] = new Vector2Int(instancesBegin, instancesBegin + instancesNum);
int componentGPUAddress = byteOffset - instancesBegin * componentDesc.byteSize;
Assert.IsTrue(componentGPUAddress >= 0, "GPUInstanceDataBufferBuilder: GPU address is negative. This is not supported for now. See kIsOverriddenBit." +
"In general, if there is only one root InstanceType (MeshRenderer in our case) with a component that is larger or equal in size than any component in a derived InstanceType." +
"And the number of parent gpu instances are always larger or equal to the number of derived type gpu instances. Than GPU address cannot become negative.");
newBuffer.gpuBufferComponentAddress[c] = componentGPUAddress;
newBuffer.defaultMetadata[c] = CreateMetadataValue(componentDesc.propertyID, componentGPUAddress, componentDesc.isOverriden);
componentAddresses[c] = componentGPUAddress;
componentByteSizes[c] = componentDesc.byteSize;
int componentByteSize = componentDesc.byteSize * instancesNum;
byteOffset += componentByteSize;
bool addedToMap = newBuffer.nameToMetadataMap.TryAdd(componentDesc.propertyID, c);
Assert.IsTrue(addedToMap, "Repetitive metadata element added to object.");
if (componentDesc.isPerInstance)
{
perInstanceComponentIndices[perInstanceComponentCounts] = c;
perInstanceComponentCounts++;
}
}
newBuffer.byteSize = byteOffset;
newBuffer.gpuBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Raw, newBuffer.byteSize / 4, 4);
newBuffer.gpuBuffer.SetData(new NativeArray<Vector4>(4, Allocator.Temp), 0, 0, 4);
newBuffer.validComponentsIndicesGpuBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Raw, perInstanceComponentCounts, 4);
newBuffer.validComponentsIndicesGpuBuffer.SetData(perInstanceComponentIndices, 0, 0, perInstanceComponentCounts);
newBuffer.componentAddressesGpuBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Raw, m_Components.Length, 4);
newBuffer.componentAddressesGpuBuffer.SetData(componentAddresses, 0, 0, m_Components.Length);
newBuffer.componentInstanceIndexRangesGpuBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Raw, m_Components.Length, 8);
newBuffer.componentInstanceIndexRangesGpuBuffer.SetData(componentInstanceIndexRanges, 0, 0, m_Components.Length);
newBuffer.componentByteCountsGpuBuffer = new GraphicsBuffer(GraphicsBuffer.Target.Raw, m_Components.Length, 4);
newBuffer.componentByteCountsGpuBuffer.SetData(componentByteSizes, 0, 0, m_Components.Length);
newBuffer.perInstanceComponentCount = perInstanceComponentCounts;
perInstanceComponentIndices.Dispose();
componentAddresses.Dispose();
componentByteSizes.Dispose();
return newBuffer;
}
public void Dispose()
{
if (m_Components.IsCreated)
m_Components.Dispose();
}
}
internal struct GPUInstanceDataBufferUploader : IDisposable
{
private static class UploadKernelIDs
{
public static readonly int _InputValidComponentCounts = Shader.PropertyToID("_InputValidComponentCounts");
public static readonly int _InputInstanceCounts = Shader.PropertyToID("_InputInstanceCounts");
public static readonly int _InputInstanceByteSize = Shader.PropertyToID("_InputInstanceByteSize");
public static readonly int _InputComponentOffsets = Shader.PropertyToID("_InputComponentOffsets");
public static readonly int _InputInstanceData = Shader.PropertyToID("_InputInstanceData");
public static readonly int _InputInstanceIndices = Shader.PropertyToID("_InputInstanceIndices");
public static readonly int _InputValidComponentIndices = Shader.PropertyToID("_InputValidComponentIndices");
public static readonly int _InputComponentAddresses = Shader.PropertyToID("_InputComponentAddresses");
public static readonly int _InputComponentByteCounts = Shader.PropertyToID("_InputComponentByteCounts");
public static readonly int _InputComponentInstanceIndexRanges = Shader.PropertyToID("_InputComponentInstanceIndexRanges");
public static readonly int _OutputBuffer = Shader.PropertyToID("_OutputBuffer");
}
public struct GPUResources : IDisposable
{
public ComputeBuffer instanceData;
public ComputeBuffer instanceIndices;
public ComputeBuffer inputComponentOffsets;
public ComputeBuffer validComponentIndices;
public ComputeShader cs;
public int kernelId;
private int m_InstanceDataByteSize;
private int m_InstanceCount;
private int m_ComponentCounts;
private int m_ValidComponentIndicesCount;
public void LoadShaders(GPUResidentDrawerResources resources)
{
if (cs == null)
{
cs = resources.instanceDataBufferUploadKernels;
kernelId = cs.FindKernel("MainUploadScatterInstances");
}
}
public void CreateResources(int newInstanceCount, int sizePerInstance, int newComponentCounts, int validComponentIndicesCount)
{
int newInstanceDataByteSize = newInstanceCount * sizePerInstance;
if (newInstanceDataByteSize > m_InstanceDataByteSize || instanceData == null)
{
if (instanceData != null)
instanceData.Release();
instanceData = new ComputeBuffer((newInstanceDataByteSize + 3) / 4, 4, ComputeBufferType.Raw);
m_InstanceDataByteSize = newInstanceDataByteSize;
}
if (newInstanceCount > m_InstanceCount || instanceIndices == null)
{
if (instanceIndices != null)
instanceIndices.Release();
instanceIndices = new ComputeBuffer(newInstanceCount, 4, ComputeBufferType.Raw);
m_InstanceCount = newInstanceCount;
}
if (newComponentCounts > m_ComponentCounts || inputComponentOffsets == null)
{
if (inputComponentOffsets != null)
inputComponentOffsets.Release();
inputComponentOffsets = new ComputeBuffer(newComponentCounts, 4, ComputeBufferType.Raw);
m_ComponentCounts = newComponentCounts;
}
if (validComponentIndicesCount > m_ValidComponentIndicesCount || validComponentIndices == null)
{
if (validComponentIndices != null)
validComponentIndices.Release();
validComponentIndices = new ComputeBuffer(validComponentIndicesCount, 4, ComputeBufferType.Raw);
m_ValidComponentIndicesCount = validComponentIndicesCount;
}
}
public void Dispose()
{
cs = null;
if (instanceData != null)
instanceData.Release();
if (instanceIndices != null)
instanceIndices.Release();
if (inputComponentOffsets != null)
inputComponentOffsets.Release();
if(validComponentIndices != null)
validComponentIndices.Release();
}
}
int m_UintPerInstance;
int m_Capacity;
int m_InstanceCount;
NativeArray<bool> m_ComponentIsInstanced;
NativeArray<int> m_ComponentDataIndex;
NativeArray<int> m_DescriptionsUintSize;
NativeArray<uint> m_TmpDataBuffer;
NativeList<int> m_WritenComponentIndices;
private NativeArray<int> m_DummyArray;
public GPUInstanceDataBufferUploader(in NativeArray<GPUInstanceComponentDesc> descriptions, int capacity, InstanceType instanceType)
{
m_Capacity = capacity;
m_InstanceCount = 0;
m_UintPerInstance = 0;
m_ComponentDataIndex = new NativeArray<int>(descriptions.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
m_ComponentIsInstanced = new NativeArray<bool>(descriptions.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
m_DescriptionsUintSize = new NativeArray<int>(descriptions.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
m_WritenComponentIndices = new NativeList<int>(descriptions.Length, Allocator.TempJob);
m_DummyArray = new NativeArray<int>(0, Allocator.Persistent);
int uintSize = UnsafeUtility.SizeOf<uint>();
for (int c = 0; c < descriptions.Length; ++c)
{
var componentDesc = descriptions[c];
m_ComponentIsInstanced[c] = componentDesc.isPerInstance;
if(componentDesc.instanceType == instanceType)
{
m_ComponentDataIndex[c] = m_UintPerInstance;
m_DescriptionsUintSize[c] = descriptions[c].byteSize / uintSize;
m_UintPerInstance += componentDesc.isPerInstance ? (componentDesc.byteSize / uintSize) : 0;
}
else
{
m_ComponentDataIndex[c] = -1;
m_DescriptionsUintSize[c] = 0;
}
}
m_TmpDataBuffer = new NativeArray<uint>(m_Capacity * m_UintPerInstance, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
}
public unsafe IntPtr GetUploadBufferPtr()
{
Assert.IsTrue(m_TmpDataBuffer.IsCreated);
Assert.IsTrue(m_TmpDataBuffer.Length > 0 && m_InstanceCount > 0);
return new IntPtr(m_TmpDataBuffer.GetUnsafePtr());
}
public int GetUIntPerInstance()
{
return m_UintPerInstance;
}
public int GetParamUIntOffset(int parameterIndex)
{
Assert.IsTrue(m_ComponentIsInstanced[parameterIndex], "Component is non instanced. Can only call this function on parameters that are for all instances.");
Assert.IsTrue(parameterIndex >= 0 && parameterIndex < m_ComponentDataIndex.Length, "Parameter index invalid.");
Assert.IsTrue(m_ComponentDataIndex[parameterIndex] != -1, "Parameter index is not allocated. Did you allocate proper InstanceType parameters?");
return m_ComponentDataIndex[parameterIndex];
}
public int PrepareParamWrite<T>(int parameterIndex) where T : unmanaged
{
int uintPerParameter = UnsafeUtility.SizeOf<T>() / UnsafeUtility.SizeOf<uint>();
Assert.IsTrue(uintPerParameter == m_DescriptionsUintSize[parameterIndex], "Parameter to write is incompatible, must be same stride as destination.");
if (!m_WritenComponentIndices.Contains(parameterIndex))
m_WritenComponentIndices.Add(parameterIndex);
return GetParamUIntOffset(parameterIndex);
}
public unsafe void AllocateUploadHandles(int handlesLength)
{
// No need to preallocate instances anymore, as those are passed as parameters to SubmitToGPU to avoid data duplication
// We just set the instance count here to ensure that a) we have the correct capacity and b) write/gatherInstanceData copies the correct amount
Assert.IsTrue(m_Capacity >= handlesLength);
m_InstanceCount = handlesLength;
}
public unsafe JobHandle WriteInstanceDataJob<T>(int parameterIndex, NativeArray<T> instanceData) where T : unmanaged
{
return WriteInstanceDataJob(parameterIndex, instanceData, m_DummyArray);
}
public unsafe JobHandle WriteInstanceDataJob<T>(int parameterIndex, NativeArray<T> instanceData, NativeArray<int> gatherIndices) where T : unmanaged
{
if (m_InstanceCount == 0)
return default;
var gatherData = gatherIndices.Length != 0;
Assert.IsTrue(gatherData || instanceData.Length == m_InstanceCount);
Assert.IsTrue(!gatherData || gatherIndices.Length == m_InstanceCount);
Assert.IsTrue(UnsafeUtility.SizeOf<T>() >= UnsafeUtility.SizeOf<uint>());
int uintPerParameter = UnsafeUtility.SizeOf<T>() / UnsafeUtility.SizeOf<uint>();
Assert.IsTrue(m_ComponentIsInstanced[parameterIndex], "Component is non instanced. Can only call this function on parameters that are for all instances.");
Assert.IsTrue(uintPerParameter == m_DescriptionsUintSize[parameterIndex], "Parameter to write is incompatible, must be same stride as destination.");
Assert.IsTrue(parameterIndex >= 0 && parameterIndex < m_ComponentDataIndex.Length, "Parameter index invalid.");
Assert.IsTrue(m_ComponentDataIndex[parameterIndex] != -1, "Parameter index is not allocated. Did you allocate proper InstanceType parameters?");
if (!m_WritenComponentIndices.Contains(parameterIndex))
m_WritenComponentIndices.Add(parameterIndex);
var writeJob = new WriteInstanceDataParameterJob
{
gatherData = gatherData,
gatherIndices = gatherIndices,
parameterIndex = parameterIndex,
uintPerParameter = uintPerParameter,
uintPerInstance = m_UintPerInstance,
componentDataIndex = m_ComponentDataIndex,
instanceData = instanceData.Reinterpret<uint>(UnsafeUtility.SizeOf<T>()),
tmpDataBuffer = m_TmpDataBuffer
};
return writeJob.Schedule(m_InstanceCount, WriteInstanceDataParameterJob.k_BatchSize);
}
public void SubmitToGpu(GPUInstanceDataBuffer instanceDataBuffer, NativeArray<GPUInstanceIndex> gpuInstanceIndices, ref GPUResources gpuResources, bool submitOnlyWrittenParams)
{
if (m_InstanceCount == 0)
return;
Assert.IsTrue(gpuInstanceIndices.Length == m_InstanceCount);
++instanceDataBuffer.version;
int uintSize = UnsafeUtility.SizeOf<uint>();
int instanceByteSize = m_UintPerInstance * uintSize;
gpuResources.CreateResources(m_InstanceCount, instanceByteSize, m_ComponentDataIndex.Length, m_WritenComponentIndices.Length);
gpuResources.instanceData.SetData(m_TmpDataBuffer, 0, 0, m_InstanceCount * m_UintPerInstance);
gpuResources.instanceIndices.SetData(gpuInstanceIndices, 0, 0, m_InstanceCount);
gpuResources.inputComponentOffsets.SetData(m_ComponentDataIndex, 0, 0, m_ComponentDataIndex.Length);
gpuResources.cs.SetInt(UploadKernelIDs._InputInstanceCounts, m_InstanceCount);
gpuResources.cs.SetInt(UploadKernelIDs._InputInstanceByteSize, instanceByteSize);
gpuResources.cs.SetBuffer(gpuResources.kernelId, UploadKernelIDs._InputInstanceData, gpuResources.instanceData);
gpuResources.cs.SetBuffer(gpuResources.kernelId, UploadKernelIDs._InputInstanceIndices, gpuResources.instanceIndices);
gpuResources.cs.SetBuffer(gpuResources.kernelId, UploadKernelIDs._InputComponentOffsets, gpuResources.inputComponentOffsets);
if (submitOnlyWrittenParams)
{
gpuResources.validComponentIndices.SetData(m_WritenComponentIndices.AsArray(), 0, 0, m_WritenComponentIndices.Length);
gpuResources.cs.SetInt(UploadKernelIDs._InputValidComponentCounts, m_WritenComponentIndices.Length);
gpuResources.cs.SetBuffer(gpuResources.kernelId, UploadKernelIDs._InputValidComponentIndices, gpuResources.validComponentIndices);
}
else
{
gpuResources.cs.SetInt(UploadKernelIDs._InputValidComponentCounts, instanceDataBuffer.perInstanceComponentCount);
gpuResources.cs.SetBuffer(gpuResources.kernelId, UploadKernelIDs._InputValidComponentIndices, instanceDataBuffer.validComponentsIndicesGpuBuffer);
}
gpuResources.cs.SetBuffer(gpuResources.kernelId, UploadKernelIDs._InputComponentAddresses, instanceDataBuffer.componentAddressesGpuBuffer);
gpuResources.cs.SetBuffer(gpuResources.kernelId, UploadKernelIDs._InputComponentByteCounts, instanceDataBuffer.componentByteCountsGpuBuffer);
gpuResources.cs.SetBuffer(gpuResources.kernelId, UploadKernelIDs._InputComponentInstanceIndexRanges, instanceDataBuffer.componentInstanceIndexRangesGpuBuffer);
gpuResources.cs.SetBuffer(gpuResources.kernelId, UploadKernelIDs._OutputBuffer, instanceDataBuffer.gpuBuffer);
gpuResources.cs.Dispatch(gpuResources.kernelId, (m_InstanceCount + 63) / 64, 1, 1);
m_InstanceCount = 0;
m_WritenComponentIndices.Clear();
}
public void SubmitToGpu(GPUInstanceDataBuffer instanceDataBuffer, NativeArray<InstanceHandle> instances, ref GPUResources gpuResources, bool submitOnlyWrittenParams)
{
if (m_InstanceCount == 0)
return;
var gpuInstanceIndices = new NativeArray<GPUInstanceIndex>(instances.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
instanceDataBuffer.CPUInstanceArrayToGPUInstanceArray(instances, gpuInstanceIndices);
SubmitToGpu(instanceDataBuffer, gpuInstanceIndices, ref gpuResources, submitOnlyWrittenParams);
gpuInstanceIndices.Dispose();
}
public void Dispose()
{
if (m_ComponentDataIndex.IsCreated)
m_ComponentDataIndex.Dispose();
if (m_ComponentIsInstanced.IsCreated)
m_ComponentIsInstanced.Dispose();
if (m_DescriptionsUintSize.IsCreated)
m_DescriptionsUintSize.Dispose();
if (m_TmpDataBuffer.IsCreated)
m_TmpDataBuffer.Dispose();
if (m_WritenComponentIndices.IsCreated)
m_WritenComponentIndices.Dispose();
if(m_DummyArray.IsCreated)
m_DummyArray.Dispose();
}
[BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)]
internal struct WriteInstanceDataParameterJob : IJobParallelFor
{
public const int k_BatchSize = 512;
[ReadOnly] public bool gatherData;
[ReadOnly] public int parameterIndex;
[ReadOnly] public int uintPerParameter;
[ReadOnly] public int uintPerInstance;
[ReadOnly] public NativeArray<int> componentDataIndex;
[ReadOnly] public NativeArray<int> gatherIndices;
[NativeDisableContainerSafetyRestriction, NoAlias][ReadOnly] public NativeArray<uint> instanceData;
[NativeDisableContainerSafetyRestriction, NoAlias][WriteOnly] public NativeArray<uint> tmpDataBuffer;
public unsafe void Execute(int index)
{
Assert.IsTrue(index * uintPerInstance < tmpDataBuffer.Length, "Trying to write to an instance buffer out of bounds.");
int dataOffset = (gatherData ? gatherIndices[index] : index) * uintPerParameter;
Assert.IsTrue(dataOffset < instanceData.Length);
int uintSize = UnsafeUtility.SizeOf<uint>();
uint* data = (uint*)instanceData.GetUnsafePtr() + dataOffset;
UnsafeUtility.MemCpy((uint*)tmpDataBuffer.GetUnsafePtr() + index * uintPerInstance + componentDataIndex[parameterIndex], data,
uintPerParameter * uintSize);
}
}
}
internal struct GPUInstanceDataBufferGrower : IDisposable
{
private static class CopyInstancesKernelIDs
{
public static readonly int _InputValidComponentCounts = Shader.PropertyToID("_InputValidComponentCounts");
public static readonly int _InstanceCounts = Shader.PropertyToID("_InstanceCounts");
public static readonly int _InstanceOffset = Shader.PropertyToID("_InstanceOffset");
public static readonly int _OutputInstanceOffset = Shader.PropertyToID("_OutputInstanceOffset");
public static readonly int _ValidComponentIndices = Shader.PropertyToID("_ValidComponentIndices");
public static readonly int _ComponentByteCounts = Shader.PropertyToID("_ComponentByteCounts");
public static readonly int _InputComponentAddresses = Shader.PropertyToID("_InputComponentAddresses");
public static readonly int _OutputComponentAddresses = Shader.PropertyToID("_OutputComponentAddresses");
public static readonly int _InputComponentInstanceIndexRanges = Shader.PropertyToID("_InputComponentInstanceIndexRanges");
public static readonly int _InputBuffer = Shader.PropertyToID("_InputBuffer");
public static readonly int _OutputBuffer = Shader.PropertyToID("_OutputBuffer");
}
public struct GPUResources : IDisposable
{
public ComputeShader cs;
public int kernelId;
public void LoadShaders(GPUResidentDrawerResources resources)
{
if (cs == null)
{
cs = resources.instanceDataBufferCopyKernels;
kernelId = cs.FindKernel("MainCopyInstances");
}
}
public void CreateResources()
{
}
public void Dispose()
{
cs = null;
}
}
private GPUInstanceDataBuffer m_SrcBuffer;
private GPUInstanceDataBuffer m_DstBuffer;
//@ We should implement buffer shrinker too, otherwise lots of instances can be allocated for trees for example
//@ while there are no trees in scenes that are in use at all.
public unsafe GPUInstanceDataBufferGrower(GPUInstanceDataBuffer sourceBuffer, in InstanceNumInfo instanceNumInfo)
{
m_SrcBuffer = sourceBuffer;
m_DstBuffer = null;
bool needToGrow = false;
for(int i = 0; i < (int)InstanceType.Count; ++i)
{
Assert.IsTrue(instanceNumInfo.InstanceNums[i] >= sourceBuffer.instanceNumInfo.InstanceNums[i], "Shrinking GPU instance buffer is not supported yet.");
if (instanceNumInfo.InstanceNums[i] > sourceBuffer.instanceNumInfo.InstanceNums[i])
needToGrow = true;
}
if (!needToGrow)
return;
GPUInstanceDataBufferBuilder builder = new GPUInstanceDataBufferBuilder();
foreach (GPUInstanceComponentDesc descriptor in sourceBuffer.descriptions)
builder.AddComponent(descriptor.propertyID, descriptor.isOverriden, descriptor.byteSize, descriptor.isPerInstance, descriptor.instanceType, descriptor.componentGroup);
m_DstBuffer = builder.Build(instanceNumInfo);
builder.Dispose();
}
public GPUInstanceDataBuffer SubmitToGpu(ref GPUResources gpuResources)
{
if (m_DstBuffer == null)
return m_SrcBuffer;
int totalInstanceCount = m_SrcBuffer.instanceNumInfo.GetTotalInstanceNum();
if(totalInstanceCount == 0)
return m_DstBuffer;
Assert.IsTrue(m_SrcBuffer.perInstanceComponentCount == m_DstBuffer.perInstanceComponentCount);
gpuResources.CreateResources();
gpuResources.cs.SetInt(CopyInstancesKernelIDs._InputValidComponentCounts, m_SrcBuffer.perInstanceComponentCount);
gpuResources.cs.SetBuffer(gpuResources.kernelId, CopyInstancesKernelIDs._ValidComponentIndices, m_SrcBuffer.validComponentsIndicesGpuBuffer);
gpuResources.cs.SetBuffer(gpuResources.kernelId, CopyInstancesKernelIDs._ComponentByteCounts, m_SrcBuffer.componentByteCountsGpuBuffer);
gpuResources.cs.SetBuffer(gpuResources.kernelId, CopyInstancesKernelIDs._InputComponentAddresses, m_SrcBuffer.componentAddressesGpuBuffer);
gpuResources.cs.SetBuffer(gpuResources.kernelId, CopyInstancesKernelIDs._InputComponentInstanceIndexRanges, m_SrcBuffer.componentInstanceIndexRangesGpuBuffer);
gpuResources.cs.SetBuffer(gpuResources.kernelId, CopyInstancesKernelIDs._OutputComponentAddresses, m_DstBuffer.componentAddressesGpuBuffer);
gpuResources.cs.SetBuffer(gpuResources.kernelId, CopyInstancesKernelIDs._InputBuffer, m_SrcBuffer.gpuBuffer);
gpuResources.cs.SetBuffer(gpuResources.kernelId, CopyInstancesKernelIDs._OutputBuffer, m_DstBuffer.gpuBuffer);
//@ We could compute new instance indices on CPU and do one dispatch.
//@ Otherwise in theory these multiple dispatches could overlap with no UAV barrier between them as they write to a different parts of the UAV.
//@ Need to profile which is better.
for(int i = 0; i < (int)InstanceType.Count; ++i)
{
int instanceCount = m_SrcBuffer.instanceNumInfo.GetInstanceNum((InstanceType)i);
if(instanceCount > 0)
{
int instanceOffset = m_SrcBuffer.instancesNumPrefixSum[i];
int outputInstanceOffset = m_DstBuffer.instancesNumPrefixSum[i];
gpuResources.cs.SetInt(CopyInstancesKernelIDs._InstanceCounts, instanceCount);
gpuResources.cs.SetInt(CopyInstancesKernelIDs._InstanceOffset, instanceOffset);
gpuResources.cs.SetInt(CopyInstancesKernelIDs._OutputInstanceOffset, outputInstanceOffset);
gpuResources.cs.Dispatch(gpuResources.kernelId, (instanceCount + 63) / 64, 1, 1);
}
}
return m_DstBuffer;
}
public void Dispose()
{
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 200079115ab70b94dafaec0a48ee2455
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,178 @@
using System;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Mathematics;
using UnityEngine.Assertions;
namespace UnityEngine.Rendering
{
//@ Add instance version to detect dangling instance handles.
internal struct InstanceHandle : IEquatable<InstanceHandle>, IComparable<InstanceHandle>
{
// Don't use this index to reference GPU data. This index is to reference CPU data only.
// To reference GPU data convert InstanceHandle to GPUInstanceIndex.
public int index { get; private set; }
// This is unique instance index for each instance type.
public int instanceIndex => index >> InstanceTypeInfo.kInstanceTypeBitCount;
// We store type bits as lower bits because this makes max InstanceHandle index bounded by how many instances we have.
// So you can allocate directly indexed arrays. This is fine as long as we have only 1 to 4 instance types.
// If we put type bits in higher bits then we might want to make CPUInstanceData sparse set InstanceIndices table to be paged.
public InstanceType type => (InstanceType)(index & InstanceTypeInfo.kInstanceTypeMask);
public bool valid => index != -1;
public static readonly InstanceHandle Invalid = new InstanceHandle() { index = -1 };
public static InstanceHandle Create(int instanceIndex, InstanceType instanceType) { return new InstanceHandle() { index = instanceIndex << InstanceTypeInfo.kInstanceTypeBitCount | (int)instanceType }; }
public static InstanceHandle FromInt(int value) { return new InstanceHandle() { index = value }; }
public bool Equals(InstanceHandle other) => index == other.index;
public int CompareTo(InstanceHandle other) { return index.CompareTo(other.index); }
public override int GetHashCode() { return index; }
}
internal struct SharedInstanceHandle : IEquatable<SharedInstanceHandle>, IComparable<SharedInstanceHandle>
{
public int index { get; set; }
public bool valid => index != -1;
public static readonly SharedInstanceHandle Invalid = new SharedInstanceHandle() { index = -1 };
public bool Equals(SharedInstanceHandle other) => index == other.index;
public int CompareTo(SharedInstanceHandle other) { return index.CompareTo(other.index); }
public override int GetHashCode() { return index; }
}
internal struct GPUInstanceIndex : IEquatable<GPUInstanceIndex>, IComparable<GPUInstanceIndex>
{
public int index { get; set; }
public bool valid => index != -1;
public static readonly GPUInstanceIndex Invalid = new GPUInstanceIndex() { index = -1 };
public bool Equals(GPUInstanceIndex other) => index == other.index;
public int CompareTo(GPUInstanceIndex other) { return index.CompareTo(other.index); }
public override int GetHashCode() { return index; }
}
internal struct InstanceAllocator
{
private NativeArray<int> m_StructData;
private NativeList<int> m_FreeInstances;
private int m_BaseInstanceOffset;
private int m_InstanceStride;
public int length { get => m_StructData[0]; set => m_StructData[0] = value; }
public bool valid => m_StructData.IsCreated;
public void Initialize(int baseInstanceOffset = 0, int instanceStride = 1)
{
m_StructData = new NativeArray<int>(1, Allocator.Persistent);
m_FreeInstances = new NativeList<int>(Allocator.Persistent);
m_BaseInstanceOffset = baseInstanceOffset;
m_InstanceStride = instanceStride;
}
public void Dispose()
{
m_StructData.Dispose();
m_FreeInstances.Dispose();
}
public int AllocateInstance()
{
int instance;
if (m_FreeInstances.Length > 0)
{
instance = m_FreeInstances[m_FreeInstances.Length - 1];
m_FreeInstances.RemoveAtSwapBack(m_FreeInstances.Length - 1);
}
else
{
instance = length * m_InstanceStride + m_BaseInstanceOffset;
length += 1;
}
return instance;
}
public void FreeInstance(int instance)
{
//@ This is a bit weak validation. Need something better but fast.
Assert.IsTrue(instance >= 0 && instance < length * m_InstanceStride);
m_FreeInstances.Add(instance);
}
public int GetNumAllocated()
{
return length - m_FreeInstances.Length;
}
}
internal unsafe struct InstanceAllocators
{
private InstanceAllocator m_InstanceAlloc_MeshRenderer;
private InstanceAllocator m_InstanceAlloc_SpeedTree;
private InstanceAllocator m_SharedInstanceAlloc;
public void Initialize()
{
//@ Will keep it as two separate allocators for two types for now. Nested native containers are not allowed in burst.
m_InstanceAlloc_MeshRenderer = new InstanceAllocator();
m_InstanceAlloc_SpeedTree = new InstanceAllocator();
m_InstanceAlloc_MeshRenderer.Initialize((int)InstanceType.MeshRenderer, InstanceTypeInfo.kMaxInstanceTypesCount);
m_InstanceAlloc_SpeedTree.Initialize((int)InstanceType.SpeedTree, InstanceTypeInfo.kMaxInstanceTypesCount);
m_SharedInstanceAlloc = new InstanceAllocator();
m_SharedInstanceAlloc.Initialize();
}
public unsafe void Dispose()
{
m_InstanceAlloc_MeshRenderer.Dispose();
m_InstanceAlloc_SpeedTree.Dispose();
m_SharedInstanceAlloc.Dispose();
}
private InstanceAllocator GetInstanceAllocator(InstanceType type)
{
switch (type)
{
case InstanceType.MeshRenderer:
return m_InstanceAlloc_MeshRenderer;
case InstanceType.SpeedTree:
return m_InstanceAlloc_SpeedTree;
default:
throw new ArgumentException("Allocator for this type is not created.");
}
}
public int GetInstanceHandlesLength(InstanceType type)
{
return GetInstanceAllocator(type).length;
}
public int GetInstancesLength(InstanceType type)
{
return GetInstanceAllocator(type).GetNumAllocated();
}
public InstanceHandle AllocateInstance(InstanceType type)
{
return InstanceHandle.FromInt(GetInstanceAllocator(type).AllocateInstance());
}
public void FreeInstance(InstanceHandle instance)
{
Assert.IsTrue(instance.valid);
GetInstanceAllocator(instance.type).FreeInstance(instance.index);
}
public unsafe SharedInstanceHandle AllocateSharedInstance()
{
return new SharedInstanceHandle { index = m_SharedInstanceAlloc.AllocateInstance() };
}
public void FreeSharedInstance(SharedInstanceHandle instance)
{
Assert.IsTrue(instance.valid);
m_SharedInstanceAlloc.FreeInstance(instance.index);
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: c82098f48a6ef0540a1073dd15a52c80

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 3d4a5f192498160488916c4e31a73a29
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,776 @@
using System;
using System.Collections.Generic;
using System.Threading;
using Unity.Burst;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Jobs;
using Unity.Mathematics;
using Unity.Profiling;
using UnityEngine.Assertions;
using UnityEngine.Profiling;
namespace UnityEngine.Rendering
{
internal partial class InstanceDataSystem : IDisposable
{
private unsafe static int AtomicAddLengthNoResize<T>(in NativeList<T> list, int count) where T : unmanaged
{
UnsafeList<T>* unsafeList = list.GetUnsafeList();
var newLength = Interlocked.Add(ref unsafeList->m_length, count);
Assert.IsTrue(unsafeList->Capacity >= newLength);
return newLength - count;
}
[BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)]
private unsafe struct QueryRendererGroupInstancesCountJob : IJobParallelForBatch
{
public const int k_BatchSize = 128;
[ReadOnly] public CPUInstanceData instanceData;
[ReadOnly] public CPUSharedInstanceData sharedInstanceData;
[ReadOnly] public NativeParallelMultiHashMap<int, InstanceHandle> rendererGroupInstanceMultiHash;
[NativeDisableContainerSafetyRestriction, NoAlias][ReadOnly] public NativeArray<int> rendererGroupIDs;
[NativeDisableContainerSafetyRestriction, NoAlias][WriteOnly] public NativeArray<int> instancesCount;
public void Execute(int startIndex, int count)
{
for (int i = startIndex; i < startIndex + count; ++i)
{
var rendererGroupID = rendererGroupIDs[i];
if (rendererGroupInstanceMultiHash.TryGetFirstValue(rendererGroupID, out var instance, out var it))
{
var sharedInstance = instanceData.Get_SharedInstance(instance);
var refCount = sharedInstanceData.Get_RefCount(sharedInstance);
instancesCount[i] = refCount;
}
else
{
instancesCount[i] = 0;
}
}
}
}
[BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)]
private unsafe struct ComputeInstancesOffsetAndResizeInstancesArrayJob : IJob
{
[ReadOnly] public NativeArray<int> instancesCount;
[WriteOnly] public NativeArray<int> instancesOffset;
public NativeList<InstanceHandle> instances;
public void Execute()
{
int totalInstancesCount = 0;
for (int i = 0; i < instancesCount.Length; ++i)
{
instancesOffset[i] = totalInstancesCount;
totalInstancesCount += instancesCount[i];
}
instances.ResizeUninitialized(totalInstancesCount);
}
}
[BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)]
private unsafe struct QueryRendererGroupInstancesJob : IJobParallelForBatch
{
public const int k_BatchSize = 128;
[ReadOnly] public NativeParallelMultiHashMap<int, InstanceHandle> rendererGroupInstanceMultiHash;
[NativeDisableContainerSafetyRestriction, NoAlias][ReadOnly] public NativeArray<int> rendererGroupIDs;
[NativeDisableContainerSafetyRestriction, NoAlias][WriteOnly] public NativeArray<InstanceHandle> instances;
[NativeDisableUnsafePtrRestriction] public UnsafeAtomicCounter32 atomicNonFoundInstancesCount;
public void Execute(int startIndex, int count)
{
int newInstancesCountJob = 0;
for (int i = startIndex; i < startIndex + count; ++i)
{
if (rendererGroupInstanceMultiHash.TryGetFirstValue(rendererGroupIDs[i], out var instance, out var it))
{
instances[i] = instance;
}
else
{
newInstancesCountJob += 1;
instances[i] = InstanceHandle.Invalid;
}
}
if (atomicNonFoundInstancesCount.Counter != null && newInstancesCountJob > 0)
atomicNonFoundInstancesCount.Add(newInstancesCountJob);
}
}
[BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)]
private unsafe struct QueryRendererGroupInstancesMultiJob : IJobParallelForBatch
{
public const int k_BatchSize = 128;
[ReadOnly] public NativeParallelMultiHashMap<int, InstanceHandle> rendererGroupInstanceMultiHash;
[NativeDisableContainerSafetyRestriction, NoAlias][ReadOnly] public NativeArray<int> rendererGroupIDs;
[NativeDisableContainerSafetyRestriction, NoAlias][ReadOnly] public NativeArray<int> instancesOffsets;
[NativeDisableContainerSafetyRestriction, NoAlias][ReadOnly] public NativeArray<int> instancesCounts;
[NativeDisableContainerSafetyRestriction, NoAlias][WriteOnly] public NativeArray<InstanceHandle> instances;
[NativeDisableUnsafePtrRestriction] public UnsafeAtomicCounter32 atomicNonFoundSharedInstancesCount;
[NativeDisableUnsafePtrRestriction] public UnsafeAtomicCounter32 atomicNonFoundInstancesCount;
public void Execute(int startIndex, int count)
{
int newSharedInstancesCountJob = 0;
int newInstancesCountJob = 0;
for (int i = startIndex; i < startIndex + count; ++i)
{
var rendererGroupID = rendererGroupIDs[i];
int instancesOffset = instancesOffsets[i];
int instancesCount = instancesCounts[i];
bool success = rendererGroupInstanceMultiHash.TryGetFirstValue(rendererGroupID, out var storedInstance, out var it);
if (!success)
newSharedInstancesCountJob += 1;
for (int j = 0; j < instancesCount; ++j)
{
int index = instancesOffset + j;
if (success)
{
instances[index] = storedInstance;
success = rendererGroupInstanceMultiHash.TryGetNextValue(out storedInstance, ref it);
}
else
{
newInstancesCountJob += 1;
instances[index] = InstanceHandle.Invalid;
}
}
}
if (atomicNonFoundSharedInstancesCount.Counter != null && newSharedInstancesCountJob > 0)
atomicNonFoundSharedInstancesCount.Add(newSharedInstancesCountJob);
if (atomicNonFoundInstancesCount.Counter != null && newInstancesCountJob > 0)
atomicNonFoundInstancesCount.Add(newInstancesCountJob);
}
}
[BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)]
private struct QuerySortedMeshInstancesJob : IJobParallelForBatch
{
public const int k_BatchSize = 64;
[ReadOnly] public CPUInstanceData instanceData;
[ReadOnly] public CPUSharedInstanceData sharedInstanceData;
[ReadOnly] public NativeArray<int> sortedMeshID;
[NativeDisableParallelForRestriction][WriteOnly] public NativeList<InstanceHandle> instances;
public void Execute(int startIndex, int count)
{
ulong validBits = 0;
for (int i = 0; i < count; ++i)
{
int instanceIndex = startIndex + i;
InstanceHandle instance = instanceData.instances[instanceIndex];
Assert.IsTrue(instanceData.IsValidInstance(instance));
SharedInstanceHandle sharedInstance = instanceData.sharedInstances[instanceIndex];
var meshID = sharedInstanceData.Get_MeshID(sharedInstance);
if (sortedMeshID.BinarySearch(meshID) >= 0)
validBits |= 1ul << i;
}
int validBitCount = math.countbits(validBits);
if (validBitCount > 0)
{
int writeIndex = AtomicAddLengthNoResize(instances, validBitCount);
int validBitIndex = math.tzcnt(validBits);
while (validBits != 0)
{
int instanceIndex = startIndex + validBitIndex;
instances[writeIndex] = instanceData.instances[instanceIndex];
writeIndex += 1;
validBits &= ~(1ul << validBitIndex);
validBitIndex = math.tzcnt(validBits);
}
}
}
}
[BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)]
private struct CalculateInterpolatedLightAndOcclusionProbesBatchJob : IJobParallelFor
{
public const int k_BatchSize = 1;
public const int k_CalculatedProbesPerBatch = 8;
[ReadOnly] public int probesCount;
[ReadOnly] public LightProbesQuery lightProbesQuery;
[NativeDisableParallelForRestriction][ReadOnly] public NativeArray<Vector3> queryPostitions;
[NativeDisableParallelForRestriction] public NativeArray<int> compactTetrahedronCache;
[NativeDisableParallelForRestriction][WriteOnly] public NativeArray<SphericalHarmonicsL2> probesSphericalHarmonics;
[NativeDisableParallelForRestriction][WriteOnly] public NativeArray<Vector4> probesOcclusion;
public void Execute(int index)
{
var startIndex = index * k_CalculatedProbesPerBatch;
var endIndex = math.min(probesCount, startIndex + k_CalculatedProbesPerBatch);
var count = endIndex - startIndex;
var compactTetrahedronCacheSubArray = compactTetrahedronCache.GetSubArray(startIndex, count);
var queryPostitionsSubArray = queryPostitions.GetSubArray(startIndex, count);
var probesSphericalHarmonicsSubArray = probesSphericalHarmonics.GetSubArray(startIndex, count);
var probesOcclusionSubArray = probesOcclusion.GetSubArray(startIndex, count);
lightProbesQuery.CalculateInterpolatedLightAndOcclusionProbes(queryPostitionsSubArray, compactTetrahedronCacheSubArray, probesSphericalHarmonicsSubArray, probesOcclusionSubArray);
}
}
[BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)]
private struct ScatterTetrahedronCacheIndicesJob : IJobParallelFor
{
public const int k_BatchSize = 128;
[ReadOnly] public NativeArray<InstanceHandle> probeInstances;
[ReadOnly] public NativeArray<int> compactTetrahedronCache;
[NativeDisableContainerSafetyRestriction, NoAlias][NativeDisableParallelForRestriction] public CPUInstanceData instanceData;
public void Execute(int index)
{
InstanceHandle instance = probeInstances[index];
instanceData.Set_TetrahedronCacheIndex(instance, compactTetrahedronCache[index]);
}
}
[BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)]
private unsafe struct TransformUpdateJob : IJobParallelForBatch
{
public const int k_BatchSize = 64;
[ReadOnly] public bool initialize;
[ReadOnly] public bool enableBoundingSpheres;
[ReadOnly] public NativeArray<InstanceHandle> instances;
[ReadOnly] public NativeArray<Matrix4x4> localToWorldMatrices;
[ReadOnly] public NativeArray<Matrix4x4> prevLocalToWorldMatrices;
[NativeDisableUnsafePtrRestriction] public UnsafeAtomicCounter32 atomicTransformQueueCount;
[NativeDisableParallelForRestriction] public CPUSharedInstanceData sharedInstanceData;
[NativeDisableParallelForRestriction] public CPUInstanceData instanceData;
[NativeDisableParallelForRestriction] public NativeArray<InstanceHandle> transformUpdateInstanceQueue;
[NativeDisableParallelForRestriction] public NativeArray<TransformUpdatePacket> transformUpdateDataQueue;
[NativeDisableParallelForRestriction] public NativeArray<float4> boundingSpheresDataQueue;
public void Execute(int startIndex, int count)
{
ulong validBits = 0;
for (int i = 0; i < count; ++i)
{
InstanceHandle instance = instances[startIndex + i];
if (!instance.valid)
continue;
if (!initialize)
{
int instanceIndex = instanceData.InstanceToIndex(instance);
int sharedInstanceIndex = sharedInstanceData.InstanceToIndex(instanceData, instance);
TransformUpdateFlags flags = sharedInstanceData.flags[sharedInstanceIndex].transformUpdateFlags;
bool movedCurrentFrame = instanceData.movedInCurrentFrameBits.Get(instanceIndex);
bool isStaticObject = (flags & TransformUpdateFlags.IsPartOfStaticBatch) != 0;
if (isStaticObject || movedCurrentFrame)
continue;
}
validBits |= 1ul << i;
}
int validBitCount = math.countbits(validBits);
if (validBitCount > 0)
{
int writeIndex = atomicTransformQueueCount.Add(validBitCount);
int validBitIndex = math.tzcnt(validBits);
while (validBits != 0)
{
int index = startIndex + validBitIndex;
InstanceHandle instance = instances[index];
int instanceIndex = instanceData.InstanceToIndex(instance);
int sharedInstanceIndex = sharedInstanceData.InstanceToIndex(instanceData, instance);
TransformUpdateFlags flags = sharedInstanceData.flags[sharedInstanceIndex].transformUpdateFlags;
bool isStaticObject = (flags & TransformUpdateFlags.IsPartOfStaticBatch) != 0;
instanceData.movedInCurrentFrameBits.Set(instanceIndex, !isStaticObject);
transformUpdateInstanceQueue[writeIndex] = instance;
ref float4x4 l2w = ref UnsafeUtility.ArrayElementAsRef<float4x4>(localToWorldMatrices.GetUnsafeReadOnlyPtr(), index);
ref AABB localAABB = ref UnsafeUtility.ArrayElementAsRef<AABB>(sharedInstanceData.localAABBs.GetUnsafePtr(), sharedInstanceIndex);
AABB worldAABB = AABB.Transform(l2w, localAABB);
instanceData.worldAABBs[instanceIndex] = worldAABB;
if (initialize)
{
PackedMatrix l2wPacked = PackedMatrix.FromFloat4x4(l2w);
PackedMatrix l2wPrevPacked = PackedMatrix.FromMatrix4x4(prevLocalToWorldMatrices[index]);
transformUpdateDataQueue[writeIndex * 2] = new TransformUpdatePacket()
{
localToWorld0 = l2wPacked.packed0,
localToWorld1 = l2wPacked.packed1,
localToWorld2 = l2wPacked.packed2,
};
transformUpdateDataQueue[writeIndex * 2 + 1] = new TransformUpdatePacket()
{
localToWorld0 = l2wPrevPacked.packed0,
localToWorld1 = l2wPrevPacked.packed1,
localToWorld2 = l2wPrevPacked.packed2,
};
// no need to set instanceData.localToWorldMatrices or instanceData.localToWorldIsFlippedBits
// they have been set up already by UpdateRendererInstancesJob
}
else
{
PackedMatrix l2wPacked = PackedMatrix.FromMatrix4x4(l2w);
transformUpdateDataQueue[writeIndex] = new TransformUpdatePacket()
{
localToWorld0 = l2wPacked.packed0,
localToWorld1 = l2wPacked.packed1,
localToWorld2 = l2wPacked.packed2,
};
float det = math.determinant((float3x3)l2w);
instanceData.localToWorldIsFlippedBits.Set(instanceIndex, det < 0.0f);
}
if (enableBoundingSpheres)
boundingSpheresDataQueue[writeIndex] = new float4(worldAABB.center.x, worldAABB.center.y, worldAABB.center.z, math.distance(worldAABB.max, worldAABB.min) * 0.5f);
writeIndex += 1;
validBits &= ~(1ul << validBitIndex);
validBitIndex = math.tzcnt(validBits);
}
}
}
}
[BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)]
private unsafe struct ProbesUpdateJob : IJobParallelForBatch
{
public const int k_BatchSize = 64;
[NativeDisableContainerSafetyRestriction, NoAlias][ReadOnly] public NativeArray<InstanceHandle> instances;
[NativeDisableParallelForRestriction][NativeDisableContainerSafetyRestriction, NoAlias] public CPUInstanceData instanceData;
[ReadOnly] public CPUSharedInstanceData sharedInstanceData;
[NativeDisableUnsafePtrRestriction] public UnsafeAtomicCounter32 atomicProbesQueueCount;
[NativeDisableParallelForRestriction] public NativeArray<InstanceHandle> probeInstanceQueue;
[NativeDisableParallelForRestriction] public NativeArray<int> compactTetrahedronCache;
[NativeDisableParallelForRestriction] public NativeArray<Vector3> probeQueryPosition;
public void Execute(int startIndex, int count)
{
ulong validBits = 0;
for (int i = 0; i < count; ++i)
{
InstanceHandle instance = instances[startIndex + i];
if (!instance.valid)
continue;
int sharedInstanceIndex = sharedInstanceData.InstanceToIndex(instanceData, instance);
TransformUpdateFlags flags = sharedInstanceData.flags[sharedInstanceIndex].transformUpdateFlags;
bool hasLightProbe = (flags & TransformUpdateFlags.HasLightProbeCombined) != 0;
if (!hasLightProbe)
continue;
validBits |= 1ul << i;
}
int validBitCount = math.countbits(validBits);
if (validBitCount > 0)
{
int writeIndex = atomicProbesQueueCount.Add(validBitCount);
int validBitIndex = math.tzcnt(validBits);
while (validBits != 0)
{
InstanceHandle instance = instances[startIndex + validBitIndex];
int instanceIndex = instanceData.InstanceToIndex(instance);
ref AABB worldAABB = ref UnsafeUtility.ArrayElementAsRef<AABB>(instanceData.worldAABBs.GetUnsafePtr(), instanceIndex);
probeInstanceQueue[writeIndex] = instance;
probeQueryPosition[writeIndex] = worldAABB.center;
compactTetrahedronCache[writeIndex] = instanceData.tetrahedronCacheIndices[instanceIndex];
writeIndex += 1;
validBits &= ~(1ul << validBitIndex);
validBitIndex = math.tzcnt(validBits);
}
}
}
}
[BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)]
private struct MotionUpdateJob : IJobParallelFor
{
public const int k_BatchSize = 16;
[ReadOnly] public int queueWriteBase;
[NativeDisableParallelForRestriction] public CPUInstanceData instanceData;
[NativeDisableUnsafePtrRestriction] public UnsafeAtomicCounter32 atomicUpdateQueueCount;
[NativeDisableParallelForRestriction][WriteOnly] public NativeArray<InstanceHandle> transformUpdateInstanceQueue;
public void Execute(int chunk_index)
{
int maxChunkBitCount = math.min(instanceData.instancesLength - 64 * chunk_index, 64);
ulong chunkBitMask = ~0ul >> (64 - maxChunkBitCount);
ulong currentChunkBits = instanceData.movedInCurrentFrameBits.GetChunk(chunk_index) & chunkBitMask;
ulong prevChunkBits = instanceData.movedInPreviousFrameBits.GetChunk(chunk_index) & chunkBitMask;
// update state in memory for the next frame
instanceData.movedInCurrentFrameBits.SetChunk(chunk_index, 0);
instanceData.movedInPreviousFrameBits.SetChunk(chunk_index, currentChunkBits);
// ensure that objects that were moved last frame update their previous world matrix, if not already fully updated
ulong remainingChunkBits = prevChunkBits & ~currentChunkBits;
// allocate space for all the writes from this chunk
int chunkBitCount = math.countbits(remainingChunkBits);
int writeIndex = queueWriteBase;
if (chunkBitCount > 0)
writeIndex += atomicUpdateQueueCount.Add(chunkBitCount);
// loop over set bits to do the writes
int indexInChunk = math.tzcnt(remainingChunkBits);
while (indexInChunk < 64)
{
int instanceIndex = 64 * chunk_index + indexInChunk;
transformUpdateInstanceQueue[writeIndex] = instanceData.IndexToInstance(instanceIndex);
writeIndex += 1;
remainingChunkBits &= ~(1ul << indexInChunk);
indexInChunk = math.tzcnt(remainingChunkBits);
}
}
}
[BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)]
private unsafe struct UpdateRendererInstancesJob : IJobParallelFor
{
public const int k_BatchSize = 128;
[ReadOnly] public bool implicitInstanceIndices;
[ReadOnly] public GPUDrivenRendererGroupData rendererData;
[ReadOnly] public NativeArray<InstanceHandle> instances;
[ReadOnly] public NativeParallelHashMap<int, GPUInstanceIndex> lodGroupDataMap;
[NativeDisableParallelForRestriction][NativeDisableContainerSafetyRestriction, NoAlias] public CPUInstanceData instanceData;
[NativeDisableParallelForRestriction][NativeDisableContainerSafetyRestriction, NoAlias] public CPUSharedInstanceData sharedInstanceData;
[NativeDisableParallelForRestriction][NativeDisableContainerSafetyRestriction, NoAlias] public CPUPerCameraInstanceData perCameraInstanceData;
public void Execute(int index)
{
var rendererGroupID = rendererData.rendererGroupID[index];
int meshIndex = rendererData.meshIndex[index];
var packedRendererData = rendererData.packedRendererData[index];
var lodGroupID = rendererData.lodGroupID[index];
var gameObjectLayer = rendererData.gameObjectLayer[index];
var lightmapIndex = rendererData.lightmapIndex[index];
var localAABB = rendererData.localBounds[index].ToAABB();
int materialOffset = rendererData.materialsOffset[index];
int materialCount = rendererData.materialsCount[index];
int meshID = rendererData.meshID[meshIndex];
var meshLodInfo = rendererData.meshLodInfo[meshIndex];
const int k_LightmapIndexMask = 0xFFFF;
const int k_LightmapIndexNotLightmapped = 0xFFFF;
const int k_LightmapIndexInfluenceOnly = 0xFFFE;
const uint k_InvalidLODGroupAndMask = 0xFFFFFFFF;
var instanceFlags = InstanceFlags.None;
var transformUpdateFlags = TransformUpdateFlags.None;
var lmIndexMasked = lightmapIndex & k_LightmapIndexMask;
// Object doesn't have a valid lightmap Index, -> uses probes for lighting
if (lmIndexMasked >= k_LightmapIndexInfluenceOnly)
{
// Only add the component when needed to store blended results (shader will use the ambient probe when not present)
if (packedRendererData.lightProbeUsage == LightProbeUsage.BlendProbes)
transformUpdateFlags |= TransformUpdateFlags.HasLightProbeCombined;
}
if (packedRendererData.isPartOfStaticBatch)
transformUpdateFlags |= TransformUpdateFlags.IsPartOfStaticBatch;
switch (packedRendererData.shadowCastingMode)
{
case ShadowCastingMode.Off:
instanceFlags |= InstanceFlags.IsShadowsOff;
break;
case ShadowCastingMode.ShadowsOnly:
instanceFlags |= InstanceFlags.IsShadowsOnly;
break;
default:
break;
}
if (meshLodInfo.lodSelectionActive)
instanceFlags |= InstanceFlags.HasMeshLod;
// If the object is light mapped, or has the special influence-only value, it affects lightmaps
if (lmIndexMasked != k_LightmapIndexNotLightmapped)
instanceFlags |= InstanceFlags.AffectsLightmaps;
// Mark if it should perform the small-mesh culling test
if (packedRendererData.smallMeshCulling)
instanceFlags |= InstanceFlags.SmallMeshCulling;
uint lodGroupAndMask = k_InvalidLODGroupAndMask;
// Renderer's LODGroup could be disabled which means that the renderer is not managed by it.
if (lodGroupDataMap.TryGetValue(lodGroupID, out var lodGroupHandle))
{
if (packedRendererData.lodMask > 0)
lodGroupAndMask = (uint)lodGroupHandle.index << 8 | packedRendererData.lodMask;
}
int instancesCount;
int instancesOffset;
if (implicitInstanceIndices)
{
instancesCount = 1;
instancesOffset = index;
}
else
{
instancesCount = rendererData.instancesCount[index];
instancesOffset = rendererData.instancesOffset[index];
}
if (instancesCount > 0)
{
InstanceHandle instance = instances[instancesOffset];
SharedInstanceHandle sharedInstance = instanceData.Get_SharedInstance(instance);
Assert.IsTrue(sharedInstance.valid);
var materialIDs = new SmallIntegerArray(materialCount, Allocator.Persistent);
for (int i = 0; i < materialCount; i++)
{
int matIndex = rendererData.materialIndex[materialOffset + i];
int materialInstanceID = rendererData.materialID[matIndex];
materialIDs[i] = materialInstanceID;
}
sharedInstanceData.Set(sharedInstance, rendererGroupID, materialIDs, meshID, localAABB, transformUpdateFlags, instanceFlags, lodGroupAndMask, meshLodInfo, gameObjectLayer,
sharedInstanceData.Get_RefCount(sharedInstance));
for (int i = 0; i < instancesCount; ++i)
{
int inputIndex = instancesOffset + i;
ref Matrix4x4 l2w = ref UnsafeUtility.ArrayElementAsRef<Matrix4x4>(rendererData.localToWorldMatrix.GetUnsafeReadOnlyPtr(), inputIndex);
var worldAABB = AABB.Transform(l2w, localAABB);
instance = instances[inputIndex];
Assert.IsTrue(instance.valid);
float det = math.determinant((float3x3)(float4x4)l2w);
bool isFlipped = (det < 0.0f);
int instanceIndex = instanceData.InstanceToIndex(instance);
perCameraInstanceData.SetDefault(instanceIndex);
instanceData.localToWorldIsFlippedBits.Set(instanceIndex, isFlipped);
instanceData.worldAABBs[instanceIndex] = worldAABB;
instanceData.tetrahedronCacheIndices[instanceIndex] = -1;
#if UNITY_EDITOR
instanceData.editorData.sceneCullingMasks[instanceIndex] = rendererData.editorData[index].sceneCullingMask;
// Store more editor instance data here if needed.
#endif
instanceData.meshLodData[instanceIndex] = rendererData.meshLodData[index];
}
}
}
}
[BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)]
private struct CollectInstancesLODGroupsAndMasksJob : IJobParallelFor
{
public const int k_BatchSize = 128;
[ReadOnly] public NativeArray<InstanceHandle> instances;
[ReadOnly] public CPUInstanceData.ReadOnly instanceData;
[ReadOnly] public CPUSharedInstanceData.ReadOnly sharedInstanceData;
[WriteOnly] public NativeArray<uint> lodGroupAndMasks;
public void Execute(int index)
{
var instance = instances[index];
var sharedInstanceIndex = sharedInstanceData.InstanceToIndex(instanceData, instance);
lodGroupAndMasks[index] = sharedInstanceData.lodGroupAndMasks[sharedInstanceIndex];
}
}
[BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)]
private struct GetVisibleNonProcessedTreeInstancesJob : IJobParallelForBatch
{
public const int k_BatchSize = 64;
[ReadOnly] public CPUInstanceData instanceData;
[ReadOnly] public CPUSharedInstanceData sharedInstanceData;
[ReadOnly][NativeDisableContainerSafetyRestriction, NoAlias] public ParallelBitArray compactedVisibilityMasks;
[ReadOnly] public bool becomeVisible;
[NativeDisableParallelForRestriction] public ParallelBitArray processedBits;
[NativeDisableParallelForRestriction][WriteOnly] public NativeArray<int> rendererIDs;
[NativeDisableParallelForRestriction][WriteOnly] public NativeArray<InstanceHandle> instances;
[NativeDisableUnsafePtrRestriction] public UnsafeAtomicCounter32 atomicTreeInstancesCount;
public void Execute(int startIndex, int count)
{
var chunkIndex = startIndex / 64;
var visibleInPrevFrameChunk = instanceData.visibleInPreviousFrameBits.GetChunk(chunkIndex);
var processedChunk = processedBits.GetChunk(chunkIndex);
ulong validBits = 0;
for (int i = 0; i < count; ++i)
{
int instanceIndex = startIndex + i;
InstanceHandle instance = instanceData.IndexToInstance(instanceIndex);
bool hasTree = instance.type == InstanceType.SpeedTree;
if (hasTree && compactedVisibilityMasks.Get(instance.index))
{
var bitMask = 1ul << i;
var processedInCurrentFrame = (processedChunk & bitMask) != 0;
if (!processedInCurrentFrame)
{
bool visibleInPrevFrame = (visibleInPrevFrameChunk & bitMask) != 0;
if (becomeVisible)
{
if (!visibleInPrevFrame)
validBits |= bitMask;
}
else
{
if (visibleInPrevFrame)
validBits |= bitMask;
}
}
}
}
int validBitsCount = math.countbits(validBits);
if (validBitsCount > 0)
{
processedBits.SetChunk(chunkIndex, processedChunk | validBits);
int writeIndex = atomicTreeInstancesCount.Add(validBitsCount);
int validBitIndex = math.tzcnt(validBits);
while (validBits != 0)
{
int instanceIndex = startIndex + validBitIndex;
InstanceHandle instance = instanceData.IndexToInstance(instanceIndex);
SharedInstanceHandle sharedInstanceHandle = instanceData.Get_SharedInstance(instance);
int rendererID = sharedInstanceData.Get_RendererGroupID(sharedInstanceHandle);
rendererIDs[writeIndex] = rendererID;
instances[writeIndex] = instance;
writeIndex += 1;
validBits &= ~(1ul << validBitIndex);
validBitIndex = math.tzcnt(validBits);
}
}
}
}
[BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)]
private struct UpdateCompactedInstanceVisibilityJob : IJobParallelForBatch
{
public const int k_BatchSize = 64;
[ReadOnly] public ParallelBitArray compactedVisibilityMasks;
[NativeDisableContainerSafetyRestriction, NoAlias][NativeDisableParallelForRestriction] public CPUInstanceData instanceData;
public void Execute(int startIndex, int count)
{
ulong visibleBits = 0;
for (int i = 0; i < count; ++i)
{
int instanceIndex = startIndex + i;
InstanceHandle instance = instanceData.IndexToInstance(instanceIndex);
bool visible = compactedVisibilityMasks.Get(instance.index);
if (visible)
visibleBits |= 1ul << i;
}
instanceData.visibleInPreviousFrameBits.SetChunk(startIndex / 64, visibleBits);
}
}
#if UNITY_EDITOR
[BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)]
private struct UpdateSelectedInstancesJob : IJobParallelFor
{
public const int k_BatchSize = 64;
[ReadOnly] public NativeArray<InstanceHandle> instances;
[NativeDisableParallelForRestriction] public CPUInstanceData instanceData;
public void Execute(int index)
{
InstanceHandle instance = instances[index];
if (instance.valid)
instanceData.editorData.selectedBits.Set(instanceData.InstanceToIndex(instance), true);
}
}
#endif
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4fde0d9aabe2aee46b306a42532dafc7
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,837 @@
using System;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Jobs;
using Unity.Mathematics;
using UnityEngine.Assertions;
using UnityEngine.Profiling;
namespace UnityEngine.Rendering
{
internal partial class InstanceDataSystem : IDisposable
{
private InstanceAllocators m_InstanceAllocators;
private CPUSharedInstanceData m_SharedInstanceData;
private CPUInstanceData m_InstanceData;
private CPUPerCameraInstanceData m_PerCameraInstanceData;
//@ We may want something a bit faster instead of multi hash map. Remove and search performance for multiple instances per renderer group is not great.
private NativeParallelMultiHashMap<int, InstanceHandle> m_RendererGroupInstanceMultiHash;
private ComputeShader m_TransformUpdateCS;
private ComputeShader m_WindDataUpdateCS;
private int m_TransformInitKernel;
private int m_TransformUpdateKernel;
private int m_MotionUpdateKernel;
private int m_ProbeUpdateKernel;
private int m_LODUpdateKernel;
private int m_WindDataCopyHistoryKernel;
private ComputeBuffer m_UpdateIndexQueueBuffer;
private ComputeBuffer m_ProbeUpdateDataQueueBuffer;
private ComputeBuffer m_ProbeOcclusionUpdateDataQueueBuffer;
private ComputeBuffer m_TransformUpdateDataQueueBuffer;
private ComputeBuffer m_BoundingSpheresUpdateDataQueueBuffer;
private bool m_EnableBoundingSpheres;
public bool hasBoundingSpheres { get { return m_EnableBoundingSpheres; } }
public CPUInstanceData.ReadOnly instanceData { get { return m_InstanceData.AsReadOnly(); } }
public CPUPerCameraInstanceData perCameraInstanceData { get { return m_PerCameraInstanceData; } }
public int cameraCount { get { return m_PerCameraInstanceData.cameraCount; }}
public CPUSharedInstanceData.ReadOnly sharedInstanceData { get { return m_SharedInstanceData.AsReadOnly(); } }
public NativeArray<InstanceHandle> aliveInstances { get { return m_InstanceData.instances.GetSubArray(0, m_InstanceData.instancesLength); } }
private readonly int[] m_ScratchWindParamAddressArray = new int[(int)SpeedTreeWindParamIndex.MaxWindParamsCount * 4];
public InstanceDataSystem(int maxInstances, bool enableBoundingSpheres, GPUResidentDrawerResources resources)
{
m_InstanceAllocators = new InstanceAllocators();
m_SharedInstanceData = new CPUSharedInstanceData();
m_InstanceData = new CPUInstanceData();
m_PerCameraInstanceData = new CPUPerCameraInstanceData();
m_InstanceAllocators.Initialize();
m_SharedInstanceData.Initialize(maxInstances);
m_InstanceData.Initialize(maxInstances);
m_PerCameraInstanceData.Initialize(maxInstances);
Assert.IsTrue(m_PerCameraInstanceData.instancesCapacity == m_InstanceData.instancesCapacity);
m_RendererGroupInstanceMultiHash = new NativeParallelMultiHashMap<int, InstanceHandle>(maxInstances, Allocator.Persistent);
m_TransformUpdateCS = resources.transformUpdaterKernels;
m_WindDataUpdateCS = resources.windDataUpdaterKernels;
m_TransformInitKernel = m_TransformUpdateCS.FindKernel("ScatterInitTransformMain");
m_TransformUpdateKernel = m_TransformUpdateCS.FindKernel("ScatterUpdateTransformMain");
m_MotionUpdateKernel = m_TransformUpdateCS.FindKernel("ScatterUpdateMotionMain");
m_ProbeUpdateKernel = m_TransformUpdateCS.FindKernel("ScatterUpdateProbesMain");
if (enableBoundingSpheres)
m_TransformUpdateCS.EnableKeyword("PROCESS_BOUNDING_SPHERES");
else
m_TransformUpdateCS.DisableKeyword("PROCESS_BOUNDING_SPHERES");
m_WindDataCopyHistoryKernel = m_WindDataUpdateCS.FindKernel("WindDataCopyHistoryMain");
m_EnableBoundingSpheres = enableBoundingSpheres;
}
public void Dispose()
{
m_InstanceAllocators.Dispose();
m_SharedInstanceData.Dispose();
m_InstanceData.Dispose();
m_PerCameraInstanceData.Dispose();
m_RendererGroupInstanceMultiHash.Dispose();
m_UpdateIndexQueueBuffer?.Dispose();
m_ProbeUpdateDataQueueBuffer?.Dispose();
m_ProbeOcclusionUpdateDataQueueBuffer?.Dispose();
m_TransformUpdateDataQueueBuffer?.Dispose();
m_BoundingSpheresUpdateDataQueueBuffer?.Dispose();
}
public int GetMaxInstancesOfType(InstanceType instanceType)
{
return m_InstanceAllocators.GetInstanceHandlesLength(instanceType);
}
public int GetAliveInstancesOfType(InstanceType instanceType)
{
return m_InstanceAllocators.GetInstancesLength(instanceType);
}
private void EnsureIndexQueueBufferCapacity(int capacity)
{
if(m_UpdateIndexQueueBuffer == null || m_UpdateIndexQueueBuffer.count < capacity)
{
m_UpdateIndexQueueBuffer?.Dispose();
m_UpdateIndexQueueBuffer = new ComputeBuffer(capacity, 4, ComputeBufferType.Raw);
}
}
private void EnsureProbeBuffersCapacity(int capacity)
{
EnsureIndexQueueBufferCapacity(capacity);
if (m_ProbeUpdateDataQueueBuffer == null || m_ProbeUpdateDataQueueBuffer.count < capacity)
{
m_ProbeUpdateDataQueueBuffer?.Dispose();
m_ProbeOcclusionUpdateDataQueueBuffer?.Dispose();
m_ProbeUpdateDataQueueBuffer = new ComputeBuffer(capacity, System.Runtime.InteropServices.Marshal.SizeOf<SHUpdatePacket>(), ComputeBufferType.Structured);
m_ProbeOcclusionUpdateDataQueueBuffer = new ComputeBuffer(capacity, System.Runtime.InteropServices.Marshal.SizeOf<Vector4>(), ComputeBufferType.Structured);
}
}
private void EnsureTransformBuffersCapacity(int capacity)
{
EnsureIndexQueueBufferCapacity(capacity);
// Current and the previous matrices
int transformsCapacity = capacity * 2;
if (m_TransformUpdateDataQueueBuffer == null || m_TransformUpdateDataQueueBuffer.count < transformsCapacity)
{
m_TransformUpdateDataQueueBuffer?.Dispose();
m_BoundingSpheresUpdateDataQueueBuffer?.Dispose();
m_TransformUpdateDataQueueBuffer = new ComputeBuffer(transformsCapacity, System.Runtime.InteropServices.Marshal.SizeOf<TransformUpdatePacket>(), ComputeBufferType.Structured);
if (m_EnableBoundingSpheres)
m_BoundingSpheresUpdateDataQueueBuffer = new ComputeBuffer(capacity, System.Runtime.InteropServices.Marshal.SizeOf<float4>(), ComputeBufferType.Structured);
}
}
private JobHandle ScheduleInterpolateProbesAndUpdateTetrahedronCache(int queueCount, NativeArray<InstanceHandle> probeUpdateInstanceQueue, NativeArray<int> compactTetrahedronCache,
NativeArray<Vector3> probeQueryPosition, NativeArray<SphericalHarmonicsL2> probeUpdateDataQueue, NativeArray<Vector4> probeOcclusionUpdateDataQueue)
{
var lightProbesQuery = new LightProbesQuery(Allocator.TempJob);
var calculateProbesJob = new CalculateInterpolatedLightAndOcclusionProbesBatchJob()
{
lightProbesQuery = lightProbesQuery,
probesCount = queueCount,
queryPostitions = probeQueryPosition,
compactTetrahedronCache = compactTetrahedronCache,
probesSphericalHarmonics = probeUpdateDataQueue,
probesOcclusion = probeOcclusionUpdateDataQueue
};
var totalBatchCount = 1 + (queueCount / CalculateInterpolatedLightAndOcclusionProbesBatchJob.k_CalculatedProbesPerBatch);
var calculateProbesJobHandle = calculateProbesJob.Schedule(totalBatchCount, CalculateInterpolatedLightAndOcclusionProbesBatchJob.k_BatchSize);
lightProbesQuery.Dispose(calculateProbesJobHandle);
var scatterTetrahedronCacheIndicesJob = new ScatterTetrahedronCacheIndicesJob()
{
compactTetrahedronCache = compactTetrahedronCache,
probeInstances = probeUpdateInstanceQueue,
instanceData = m_InstanceData
};
return scatterTetrahedronCacheIndicesJob.Schedule(queueCount, ScatterTetrahedronCacheIndicesJob.k_BatchSize, calculateProbesJobHandle);
}
private void DispatchProbeUpdateCommand(int queueCount, NativeArray<InstanceHandle> probeInstanceQueue, NativeArray<SphericalHarmonicsL2> probeUpdateDataQueue,
NativeArray<Vector4> probeOcclusionUpdateDataQueue, RenderersParameters renderersParameters, GPUInstanceDataBuffer outputBuffer)
{
EnsureProbeBuffersCapacity(queueCount);
var gpuInstanceIndices = new NativeArray<GPUInstanceIndex>(queueCount, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
outputBuffer.CPUInstanceArrayToGPUInstanceArray(probeInstanceQueue.GetSubArray(0, queueCount), gpuInstanceIndices);
Profiler.BeginSample("PrepareProbeUpdateDispatch");
Profiler.BeginSample("ComputeBuffer.SetData");
m_UpdateIndexQueueBuffer.SetData(gpuInstanceIndices, 0, 0, queueCount);
m_ProbeUpdateDataQueueBuffer.SetData(probeUpdateDataQueue, 0, 0, queueCount);
m_ProbeOcclusionUpdateDataQueueBuffer.SetData(probeOcclusionUpdateDataQueue, 0, 0, queueCount);
Profiler.EndSample();
m_TransformUpdateCS.SetInt(InstanceTransformUpdateIDs._ProbeUpdateQueueCount, queueCount);
m_TransformUpdateCS.SetInt(InstanceTransformUpdateIDs._SHUpdateVec4Offset, renderersParameters.shCoefficients.uintOffset);
m_TransformUpdateCS.SetBuffer(m_ProbeUpdateKernel, InstanceTransformUpdateIDs._ProbeUpdateIndexQueue, m_UpdateIndexQueueBuffer);
m_TransformUpdateCS.SetBuffer(m_ProbeUpdateKernel, InstanceTransformUpdateIDs._ProbeUpdateDataQueue, m_ProbeUpdateDataQueueBuffer);
m_TransformUpdateCS.SetBuffer(m_ProbeUpdateKernel, InstanceTransformUpdateIDs._ProbeOcclusionUpdateDataQueue, m_ProbeOcclusionUpdateDataQueueBuffer);
m_TransformUpdateCS.SetBuffer(m_ProbeUpdateKernel, InstanceTransformUpdateIDs._OutputProbeBuffer, outputBuffer.gpuBuffer);
Profiler.EndSample();
m_TransformUpdateCS.Dispatch(m_ProbeUpdateKernel, (queueCount + 63) / 64, 1, 1);
gpuInstanceIndices.Dispose();
}
private void DispatchMotionUpdateCommand(int motionQueueCount, NativeArray<InstanceHandle> transformInstanceQueue, RenderersParameters renderersParameters, GPUInstanceDataBuffer outputBuffer)
{
EnsureTransformBuffersCapacity(motionQueueCount);
var gpuInstanceIndices = new NativeArray<GPUInstanceIndex>(motionQueueCount, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
outputBuffer.CPUInstanceArrayToGPUInstanceArray(transformInstanceQueue.GetSubArray(0, motionQueueCount), gpuInstanceIndices);
Profiler.BeginSample("PrepareMotionUpdateDispatch");
Profiler.BeginSample("ComputeBuffer.SetData");
m_UpdateIndexQueueBuffer.SetData(gpuInstanceIndices, 0, 0, motionQueueCount);
Profiler.EndSample();
m_TransformUpdateCS.SetInt(InstanceTransformUpdateIDs._TransformUpdateQueueCount, motionQueueCount);
m_TransformUpdateCS.SetInt(InstanceTransformUpdateIDs._TransformUpdateOutputL2WVec4Offset, renderersParameters.localToWorld.uintOffset);
m_TransformUpdateCS.SetInt(InstanceTransformUpdateIDs._TransformUpdateOutputW2LVec4Offset, renderersParameters.worldToLocal.uintOffset);
m_TransformUpdateCS.SetInt(InstanceTransformUpdateIDs._TransformUpdateOutputPrevL2WVec4Offset, renderersParameters.matrixPreviousM.uintOffset);
m_TransformUpdateCS.SetInt(InstanceTransformUpdateIDs._TransformUpdateOutputPrevW2LVec4Offset, renderersParameters.matrixPreviousMI.uintOffset);
m_TransformUpdateCS.SetBuffer(m_MotionUpdateKernel, InstanceTransformUpdateIDs._TransformUpdateIndexQueue, m_UpdateIndexQueueBuffer);
m_TransformUpdateCS.SetBuffer(m_MotionUpdateKernel, InstanceTransformUpdateIDs._OutputTransformBuffer, outputBuffer.gpuBuffer);
Profiler.EndSample();
m_TransformUpdateCS.Dispatch(m_MotionUpdateKernel, (motionQueueCount + 63) / 64, 1, 1);
gpuInstanceIndices.Dispose();
}
private void DispatchTransformUpdateCommand(bool initialize, int transformQueueCount, NativeArray<InstanceHandle> transformInstanceQueue, NativeArray<TransformUpdatePacket> updateDataQueue,
NativeArray<float4> boundingSphereUpdateDataQueue, RenderersParameters renderersParameters, GPUInstanceDataBuffer outputBuffer)
{
EnsureTransformBuffersCapacity(transformQueueCount);
int transformQueueDataSize;
int kernel;
if(initialize)
{
// When we reinitialize we have the current and the previous matrices per transform.
transformQueueDataSize = transformQueueCount * 2;
kernel = m_TransformInitKernel;
}
else
{
transformQueueDataSize = transformQueueCount;
kernel = m_TransformUpdateKernel;
}
var gpuInstanceIndices = new NativeArray<GPUInstanceIndex>(transformQueueCount, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
outputBuffer.CPUInstanceArrayToGPUInstanceArray(transformInstanceQueue.GetSubArray(0, transformQueueCount), gpuInstanceIndices);
Profiler.BeginSample("PrepareTransformUpdateDispatch");
Profiler.BeginSample("ComputeBuffer.SetData");
m_UpdateIndexQueueBuffer.SetData(gpuInstanceIndices, 0, 0, transformQueueCount);
m_TransformUpdateDataQueueBuffer.SetData(updateDataQueue, 0, 0, transformQueueDataSize);
if (m_EnableBoundingSpheres)
m_BoundingSpheresUpdateDataQueueBuffer.SetData(boundingSphereUpdateDataQueue, 0, 0, transformQueueCount);
Profiler.EndSample();
m_TransformUpdateCS.SetInt(InstanceTransformUpdateIDs._TransformUpdateQueueCount, transformQueueCount);
m_TransformUpdateCS.SetInt(InstanceTransformUpdateIDs._TransformUpdateOutputL2WVec4Offset, renderersParameters.localToWorld.uintOffset);
m_TransformUpdateCS.SetInt(InstanceTransformUpdateIDs._TransformUpdateOutputW2LVec4Offset, renderersParameters.worldToLocal.uintOffset);
m_TransformUpdateCS.SetInt(InstanceTransformUpdateIDs._TransformUpdateOutputPrevL2WVec4Offset, renderersParameters.matrixPreviousM.uintOffset);
m_TransformUpdateCS.SetInt(InstanceTransformUpdateIDs._TransformUpdateOutputPrevW2LVec4Offset, renderersParameters.matrixPreviousMI.uintOffset);
m_TransformUpdateCS.SetBuffer(kernel, InstanceTransformUpdateIDs._TransformUpdateIndexQueue, m_UpdateIndexQueueBuffer);
m_TransformUpdateCS.SetBuffer(kernel, InstanceTransformUpdateIDs._TransformUpdateDataQueue, m_TransformUpdateDataQueueBuffer);
if (m_EnableBoundingSpheres)
{
Assert.IsTrue(renderersParameters.boundingSphere.valid);
m_TransformUpdateCS.SetInt(InstanceTransformUpdateIDs._BoundingSphereOutputVec4Offset, renderersParameters.boundingSphere.uintOffset);
m_TransformUpdateCS.SetBuffer(kernel, InstanceTransformUpdateIDs._BoundingSphereDataQueue, m_BoundingSpheresUpdateDataQueueBuffer);
}
m_TransformUpdateCS.SetBuffer(kernel, InstanceTransformUpdateIDs._OutputTransformBuffer, outputBuffer.gpuBuffer);
Profiler.EndSample();
m_TransformUpdateCS.Dispatch(kernel, (transformQueueCount + 63) / 64, 1, 1);
gpuInstanceIndices.Dispose();
}
private void DispatchWindDataCopyHistoryCommand(NativeArray<GPUInstanceIndex> gpuInstanceIndices, RenderersParameters renderersParameters, GPUInstanceDataBuffer outputBuffer)
{
Profiler.BeginSample("DispatchWindDataCopyHistory");
int kernel = m_WindDataCopyHistoryKernel;
int instancesCount = gpuInstanceIndices.Length;
EnsureIndexQueueBufferCapacity(instancesCount);
m_UpdateIndexQueueBuffer.SetData(gpuInstanceIndices, 0, 0, instancesCount);
m_WindDataUpdateCS.SetInt(InstanceWindDataUpdateIDs._WindDataQueueCount, instancesCount);
for (int i = 0; i < (int)SpeedTreeWindParamIndex.MaxWindParamsCount; ++i)
m_ScratchWindParamAddressArray[i * 4] = renderersParameters.windParams[i].gpuAddress;
m_WindDataUpdateCS.SetInts(InstanceWindDataUpdateIDs._WindParamAddressArray, m_ScratchWindParamAddressArray);
for (int i = 0; i < (int)SpeedTreeWindParamIndex.MaxWindParamsCount; ++i)
m_ScratchWindParamAddressArray[i * 4] = renderersParameters.windHistoryParams[i].gpuAddress;
m_WindDataUpdateCS.SetInts(InstanceWindDataUpdateIDs._WindHistoryParamAddressArray, m_ScratchWindParamAddressArray);
m_WindDataUpdateCS.SetBuffer(kernel, InstanceWindDataUpdateIDs._WindDataUpdateIndexQueue, m_UpdateIndexQueueBuffer);
m_WindDataUpdateCS.SetBuffer(kernel, InstanceWindDataUpdateIDs._WindDataBuffer, outputBuffer.gpuBuffer);
m_WindDataUpdateCS.Dispatch(kernel, (instancesCount + 63) / 64, 1, 1);
Profiler.EndSample();
}
private unsafe void UpdateInstanceMotionsData(in RenderersParameters renderersParameters, GPUInstanceDataBuffer outputBuffer)
{
var transformUpdateInstanceQueue = new NativeArray<InstanceHandle>(m_InstanceData.instancesLength, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
var motionQueueCount = 0;
new MotionUpdateJob()
{
queueWriteBase = 0,
instanceData = m_InstanceData,
atomicUpdateQueueCount = new UnsafeAtomicCounter32(&motionQueueCount),
transformUpdateInstanceQueue = transformUpdateInstanceQueue,
}.Schedule((m_InstanceData.instancesLength + 63) / 64, MotionUpdateJob.k_BatchSize).Complete();
if (motionQueueCount > 0)
DispatchMotionUpdateCommand(motionQueueCount, transformUpdateInstanceQueue, renderersParameters, outputBuffer);
transformUpdateInstanceQueue.Dispose();
}
private unsafe void UpdateInstanceTransformsData(bool initialize, NativeArray<InstanceHandle> instances, NativeArray<Matrix4x4> localToWorldMatrices, NativeArray<Matrix4x4> prevLocalToWorldMatrices,
in RenderersParameters renderersParameters, GPUInstanceDataBuffer outputBuffer)
{
Assert.AreEqual(instances.Length, localToWorldMatrices.Length);
Assert.AreEqual(instances.Length, prevLocalToWorldMatrices.Length);
var transformUpdateInstanceQueue = new NativeArray<InstanceHandle>(instances.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
// When we reinitialize we have the current and the previous matrices per transform.
var transformUpdateDataQueue = new NativeArray<TransformUpdatePacket>(initialize ? instances.Length * 2 : instances.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
var boundingSpheresUpdateDataQueue = new NativeArray<float4>(m_EnableBoundingSpheres ? instances.Length : 0, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
var probeInstanceQueue = new NativeArray<InstanceHandle>(instances.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
var compactTetrahedronCache = new NativeArray<int>(instances.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
var probeQueryPosition = new NativeArray<Vector3>(instances.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
var probeUpdateDataQueue = new NativeArray<SphericalHarmonicsL2>(instances.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
var probeOcclusionUpdateDataQueue = new NativeArray<Vector4>(instances.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
var transformQueueCount = 0;
int probesQueueCount = 0;
var transformJob = new TransformUpdateJob()
{
initialize = initialize,
enableBoundingSpheres = m_EnableBoundingSpheres,
instances = instances,
localToWorldMatrices = localToWorldMatrices,
prevLocalToWorldMatrices = prevLocalToWorldMatrices,
atomicTransformQueueCount = new UnsafeAtomicCounter32(&transformQueueCount),
sharedInstanceData = m_SharedInstanceData,
instanceData = m_InstanceData,
transformUpdateInstanceQueue = transformUpdateInstanceQueue,
transformUpdateDataQueue = transformUpdateDataQueue,
boundingSpheresDataQueue = boundingSpheresUpdateDataQueue,
};
var probesJob = new ProbesUpdateJob()
{
instances = instances,
instanceData = m_InstanceData,
sharedInstanceData = m_SharedInstanceData,
atomicProbesQueueCount = new UnsafeAtomicCounter32(&probesQueueCount),
probeInstanceQueue = probeInstanceQueue,
compactTetrahedronCache = compactTetrahedronCache,
probeQueryPosition = probeQueryPosition
};
JobHandle jobHandle = transformJob.ScheduleBatch(instances.Length, TransformUpdateJob.k_BatchSize);
jobHandle = probesJob.ScheduleBatch(instances.Length, ProbesUpdateJob.k_BatchSize, jobHandle);
jobHandle.Complete();
if (probesQueueCount > 0)
{
ScheduleInterpolateProbesAndUpdateTetrahedronCache(probesQueueCount, probeInstanceQueue, compactTetrahedronCache, probeQueryPosition,
probeUpdateDataQueue, probeOcclusionUpdateDataQueue).Complete();
DispatchProbeUpdateCommand(probesQueueCount, probeInstanceQueue, probeUpdateDataQueue, probeOcclusionUpdateDataQueue, renderersParameters, outputBuffer);
}
if (transformQueueCount > 0)
{
DispatchTransformUpdateCommand(initialize, transformQueueCount, transformUpdateInstanceQueue, transformUpdateDataQueue, boundingSpheresUpdateDataQueue,
renderersParameters, outputBuffer);
}
transformUpdateInstanceQueue.Dispose();
transformUpdateDataQueue.Dispose();
boundingSpheresUpdateDataQueue.Dispose();
probeInstanceQueue.Dispose();
compactTetrahedronCache.Dispose();
probeQueryPosition.Dispose();
probeUpdateDataQueue.Dispose();
probeOcclusionUpdateDataQueue.Dispose();
}
private unsafe void UpdateInstanceProbesData(NativeArray<InstanceHandle> instances, in RenderersParameters renderersParameters, GPUInstanceDataBuffer outputBuffer)
{
var probeInstanceQueue = new NativeArray<InstanceHandle>(instances.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
var compactTetrahedronCache = new NativeArray<int>(instances.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
var probeQueryPosition = new NativeArray<Vector3>(instances.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
var probeUpdateDataQueue = new NativeArray<SphericalHarmonicsL2>(instances.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
var probeOcclusionUpdateDataQueue = new NativeArray<Vector4>(instances.Length, Allocator.TempJob, NativeArrayOptions.UninitializedMemory);
int probesQueueCount = 0;
new ProbesUpdateJob()
{
instances = instances,
instanceData = m_InstanceData,
sharedInstanceData = m_SharedInstanceData,
atomicProbesQueueCount = new UnsafeAtomicCounter32(&probesQueueCount),
probeInstanceQueue = probeInstanceQueue,
compactTetrahedronCache = compactTetrahedronCache,
probeQueryPosition = probeQueryPosition
}.ScheduleBatch(instances.Length, ProbesUpdateJob.k_BatchSize).Complete();
if (probesQueueCount > 0)
{
ScheduleInterpolateProbesAndUpdateTetrahedronCache(probesQueueCount, probeInstanceQueue, compactTetrahedronCache, probeQueryPosition,
probeUpdateDataQueue, probeOcclusionUpdateDataQueue).Complete();
DispatchProbeUpdateCommand(probesQueueCount, probeInstanceQueue, probeUpdateDataQueue, probeOcclusionUpdateDataQueue, renderersParameters, outputBuffer);
}
probeInstanceQueue.Dispose();
compactTetrahedronCache.Dispose();
probeQueryPosition.Dispose();
probeUpdateDataQueue.Dispose();
probeOcclusionUpdateDataQueue.Dispose();
}
public void UpdateInstanceWindDataHistory(NativeArray<GPUInstanceIndex> gpuInstanceIndices, RenderersParameters renderersParameters, GPUInstanceDataBuffer outputBuffer)
{
if(gpuInstanceIndices.Length == 0)
return;
DispatchWindDataCopyHistoryCommand(gpuInstanceIndices, renderersParameters, outputBuffer);
}
public unsafe void ReallocateAndGetInstances(in GPUDrivenRendererGroupData rendererData, NativeArray<InstanceHandle> instances)
{
Assert.AreEqual(rendererData.localToWorldMatrix.Length, instances.Length);
int newSharedInstancesCount = 0;
int newInstancesCount = 0;
bool implicitInstanceIndices = rendererData.instancesCount.Length == 0;
if (implicitInstanceIndices)
{
var queryJob = new QueryRendererGroupInstancesJob()
{
rendererGroupInstanceMultiHash = m_RendererGroupInstanceMultiHash,
rendererGroupIDs = rendererData.rendererGroupID,
instances = instances,
atomicNonFoundInstancesCount = new UnsafeAtomicCounter32(&newInstancesCount)
};
queryJob.ScheduleBatch(rendererData.rendererGroupID.Length, QueryRendererGroupInstancesJob.k_BatchSize).Complete();
newSharedInstancesCount = newInstancesCount;
}
else
{
var queryJob = new QueryRendererGroupInstancesMultiJob()
{
rendererGroupInstanceMultiHash = m_RendererGroupInstanceMultiHash,
rendererGroupIDs = rendererData.rendererGroupID,
instancesOffsets = rendererData.instancesOffset,
instancesCounts = rendererData.instancesCount,
instances = instances,
atomicNonFoundSharedInstancesCount = new UnsafeAtomicCounter32(&newSharedInstancesCount),
atomicNonFoundInstancesCount = new UnsafeAtomicCounter32(&newInstancesCount)
};
queryJob.ScheduleBatch(rendererData.rendererGroupID.Length, QueryRendererGroupInstancesMultiJob.k_BatchSize).Complete();
}
m_InstanceData.EnsureFreeInstances(newInstancesCount);
m_PerCameraInstanceData.Grow(m_InstanceData.instancesCapacity);
Assert.IsTrue(m_InstanceData.instancesCapacity == m_PerCameraInstanceData.instancesCapacity);
m_SharedInstanceData.EnsureFreeInstances(newSharedInstancesCount);
InstanceDataSystemBurst.ReallocateInstances(implicitInstanceIndices, rendererData.rendererGroupID, rendererData.packedRendererData,
rendererData.instancesOffset, rendererData.instancesCount, ref m_InstanceAllocators, ref m_InstanceData,
ref m_PerCameraInstanceData, ref m_SharedInstanceData, ref instances, ref m_RendererGroupInstanceMultiHash);
}
public void FreeRendererGroupInstances(NativeArray<int> rendererGroupsID)
{
InstanceDataSystemBurst.FreeRendererGroupInstances(rendererGroupsID.AsReadOnly(), ref m_InstanceAllocators, ref m_InstanceData,
ref m_PerCameraInstanceData, ref m_SharedInstanceData, ref m_RendererGroupInstanceMultiHash);
}
public void FreeInstances(NativeArray<InstanceHandle> instances)
{
InstanceDataSystemBurst.FreeInstances(instances.AsReadOnly(), ref m_InstanceAllocators, ref m_InstanceData, ref m_PerCameraInstanceData,
ref m_SharedInstanceData, ref m_RendererGroupInstanceMultiHash);
}
public JobHandle ScheduleUpdateInstanceDataJob(NativeArray<InstanceHandle> instances, in GPUDrivenRendererGroupData rendererData, NativeParallelHashMap<int, GPUInstanceIndex> lodGroupDataMap)
{
bool implicitInstanceIndices = rendererData.instancesCount.Length == 0;
if(implicitInstanceIndices)
{
Assert.AreEqual(instances.Length, rendererData.rendererGroupID.Length);
}
else
{
Assert.AreEqual(rendererData.instancesCount.Length, rendererData.rendererGroupID.Length);
Assert.AreEqual(rendererData.instancesOffset.Length, rendererData.rendererGroupID.Length);
}
Assert.AreEqual(instances.Length, rendererData.localToWorldMatrix.Length);
return new UpdateRendererInstancesJob
{
implicitInstanceIndices = implicitInstanceIndices,
instances = instances,
rendererData = rendererData,
lodGroupDataMap = lodGroupDataMap,
instanceData = m_InstanceData,
sharedInstanceData = m_SharedInstanceData,
perCameraInstanceData = m_PerCameraInstanceData
}.Schedule(rendererData.rendererGroupID.Length, UpdateRendererInstancesJob.k_BatchSize);
}
public void UpdateAllInstanceProbes(in RenderersParameters renderersParameters, GPUInstanceDataBuffer outputBuffer)
{
var instances = m_InstanceData.instances.GetSubArray(0, m_InstanceData.instancesLength);
if (instances.Length == 0)
return;
UpdateInstanceProbesData(instances, renderersParameters, outputBuffer);
}
public void InitializeInstanceTransforms(NativeArray<InstanceHandle> instances, NativeArray<Matrix4x4> localToWorldMatrices,
NativeArray<Matrix4x4> prevLocalToWorldMatrices, in RenderersParameters renderersParameters, GPUInstanceDataBuffer outputBuffer)
{
if (instances.Length == 0)
return;
UpdateInstanceTransformsData(true, instances, localToWorldMatrices, prevLocalToWorldMatrices, renderersParameters, outputBuffer);
}
public void UpdateInstanceTransforms(NativeArray<InstanceHandle> instances, NativeArray<Matrix4x4> localToWorldMatrices,
in RenderersParameters renderersParameters, GPUInstanceDataBuffer outputBuffer)
{
if (instances.Length == 0)
return;
UpdateInstanceTransformsData(false, instances, localToWorldMatrices, localToWorldMatrices, renderersParameters, outputBuffer);
}
public void UpdateInstanceMotions(in RenderersParameters renderersParameters, GPUInstanceDataBuffer outputBuffer)
{
if (m_InstanceData.instancesLength == 0)
return;
UpdateInstanceMotionsData(renderersParameters, outputBuffer);
}
public JobHandle ScheduleQueryRendererGroupInstancesJob(NativeArray<int> rendererGroupIDs, NativeArray<InstanceHandle> instances)
{
Assert.AreEqual(rendererGroupIDs.Length, instances.Length);
if (rendererGroupIDs.Length == 0)
return default;
var queryJob = new QueryRendererGroupInstancesJob()
{
rendererGroupInstanceMultiHash = m_RendererGroupInstanceMultiHash,
rendererGroupIDs = rendererGroupIDs,
instances = instances
};
return queryJob.ScheduleBatch(rendererGroupIDs.Length, QueryRendererGroupInstancesJob.k_BatchSize);
}
public JobHandle ScheduleQueryRendererGroupInstancesJob(NativeArray<int> rendererGroupIDs, NativeList<InstanceHandle> instances)
{
if (rendererGroupIDs.Length == 0)
return default;
var instancesOffset = new NativeArray<int>(rendererGroupIDs.Length, Allocator.TempJob);
var instancesCount = new NativeArray<int>(rendererGroupIDs.Length, Allocator.TempJob);
var jobHandle = ScheduleQueryRendererGroupInstancesJob(rendererGroupIDs, instancesOffset, instancesCount, instances);
instancesOffset.Dispose(jobHandle);
instancesCount.Dispose(jobHandle);
return jobHandle;
}
public JobHandle ScheduleQueryRendererGroupInstancesJob(NativeArray<int> rendererGroupIDs, NativeArray<int> instancesOffset, NativeArray<int> instancesCount, NativeList<InstanceHandle> instances)
{
Assert.AreEqual(rendererGroupIDs.Length, instancesOffset.Length);
Assert.AreEqual(rendererGroupIDs.Length, instancesCount.Length);
if (rendererGroupIDs.Length == 0)
return default;
var queryCountJobHandle = new QueryRendererGroupInstancesCountJob
{
instanceData = m_InstanceData,
sharedInstanceData = m_SharedInstanceData,
rendererGroupInstanceMultiHash = m_RendererGroupInstanceMultiHash,
rendererGroupIDs = rendererGroupIDs,
instancesCount = instancesCount,
}.ScheduleBatch(rendererGroupIDs.Length, QueryRendererGroupInstancesCountJob.k_BatchSize);
var computeOffsetsAndResizeArrayJobHandle = new ComputeInstancesOffsetAndResizeInstancesArrayJob
{
instancesCount = instancesCount,
instancesOffset = instancesOffset,
instances = instances
}.Schedule(queryCountJobHandle);
return new QueryRendererGroupInstancesMultiJob()
{
rendererGroupInstanceMultiHash = m_RendererGroupInstanceMultiHash,
rendererGroupIDs = rendererGroupIDs,
instancesOffsets = instancesOffset,
instancesCounts = instancesCount,
instances = instances.AsDeferredJobArray()
}.ScheduleBatch(rendererGroupIDs.Length, QueryRendererGroupInstancesMultiJob.k_BatchSize, computeOffsetsAndResizeArrayJobHandle);
}
public JobHandle ScheduleQuerySortedMeshInstancesJob(NativeArray<int> sortedMeshIDs, NativeList<InstanceHandle> instances)
{
if (sortedMeshIDs.Length == 0)
return default;
instances.Capacity = m_InstanceData.instancesLength;
var queryJob = new QuerySortedMeshInstancesJob()
{
instanceData = m_InstanceData,
sharedInstanceData = m_SharedInstanceData,
sortedMeshID = sortedMeshIDs,
instances = instances
};
return queryJob.ScheduleBatch(m_InstanceData.instancesLength, QuerySortedMeshInstancesJob.k_BatchSize);
}
public JobHandle ScheduleCollectInstancesLODGroupAndMasksJob(NativeArray<InstanceHandle> instances, NativeArray<uint> lodGroupAndMasks)
{
Assert.AreEqual(instances.Length, lodGroupAndMasks.Length);
return new CollectInstancesLODGroupsAndMasksJob
{
instanceData = instanceData,
sharedInstanceData = sharedInstanceData,
instances = instances,
lodGroupAndMasks = lodGroupAndMasks
}.Schedule(instances.Length, CollectInstancesLODGroupsAndMasksJob.k_BatchSize);
}
public bool InternalSanityCheckStates()
{
var instanceRefCountsHash = new NativeParallelHashMap<SharedInstanceHandle, int>(64, Allocator.Temp);
int totalValidInstances = 0;
for(int i = 0; i < m_InstanceData.handlesLength; ++i)
{
var instance = InstanceHandle.FromInt(i);
if(m_InstanceData.IsValidInstance(instance))
{
var sharedInstance = m_InstanceData.Get_SharedInstance(instance);
if (instanceRefCountsHash.TryGetValue(sharedInstance, out var refCounts))
instanceRefCountsHash[sharedInstance] = refCounts + 1;
else
instanceRefCountsHash.Add(sharedInstance, 1);
++totalValidInstances;
}
}
if (m_InstanceData.instancesLength != totalValidInstances)
return false;
int totalValidSharedInstances = 0;
for (int i = 0; i < m_SharedInstanceData.handlesLength; ++i)
{
var sharedInstance = new SharedInstanceHandle { index = i };
if (m_SharedInstanceData.IsValidInstance(sharedInstance))
{
var refCount = m_SharedInstanceData.Get_RefCount(sharedInstance);
if (instanceRefCountsHash[sharedInstance] != refCount)
return false;
++totalValidSharedInstances;
}
}
if (m_SharedInstanceData.instancesLength != totalValidSharedInstances)
return false;
return true;
}
public unsafe void GetVisibleTreeInstances(in ParallelBitArray compactedVisibilityMasks, in ParallelBitArray processedBits, NativeList<int> visibeTreeRendererIDs,
NativeList<InstanceHandle> visibeTreeInstances, bool becomeVisibleOnly, out int becomeVisibeTreeInstancesCount)
{
Assert.AreEqual(visibeTreeRendererIDs.Length, 0);
Assert.AreEqual(visibeTreeInstances.Length, 0);
becomeVisibeTreeInstancesCount = 0;
int maxTreeInstancesCount = GetAliveInstancesOfType(InstanceType.SpeedTree);
if (maxTreeInstancesCount == 0)
return;
visibeTreeRendererIDs.ResizeUninitialized(maxTreeInstancesCount);
visibeTreeInstances.ResizeUninitialized(maxTreeInstancesCount);
int visibleTreeInstancesCount = 0;
new GetVisibleNonProcessedTreeInstancesJob
{
becomeVisible = true,
instanceData = m_InstanceData,
sharedInstanceData = m_SharedInstanceData,
compactedVisibilityMasks = compactedVisibilityMasks,
processedBits = processedBits,
rendererIDs = visibeTreeRendererIDs.AsArray(),
instances = visibeTreeInstances.AsArray(),
atomicTreeInstancesCount = new UnsafeAtomicCounter32(&visibleTreeInstancesCount)
}.ScheduleBatch(m_InstanceData.instancesLength, GetVisibleNonProcessedTreeInstancesJob.k_BatchSize).Complete();
becomeVisibeTreeInstancesCount = visibleTreeInstancesCount;
if(!becomeVisibleOnly)
{
new GetVisibleNonProcessedTreeInstancesJob
{
becomeVisible = false,
instanceData = m_InstanceData,
sharedInstanceData = m_SharedInstanceData,
compactedVisibilityMasks = compactedVisibilityMasks,
processedBits = processedBits,
rendererIDs = visibeTreeRendererIDs.AsArray(),
instances = visibeTreeInstances.AsArray(),
atomicTreeInstancesCount = new UnsafeAtomicCounter32(&visibleTreeInstancesCount)
}.ScheduleBatch(m_InstanceData.instancesLength, GetVisibleNonProcessedTreeInstancesJob.k_BatchSize).Complete();
}
Assert.IsTrue(becomeVisibeTreeInstancesCount <= visibleTreeInstancesCount);
Assert.IsTrue(visibleTreeInstancesCount <= maxTreeInstancesCount);
visibeTreeRendererIDs.ResizeUninitialized(visibleTreeInstancesCount);
visibeTreeInstances.ResizeUninitialized(visibleTreeInstancesCount);
}
public void UpdatePerFrameInstanceVisibility(in ParallelBitArray compactedVisibilityMasks)
{
Assert.AreEqual(m_InstanceData.handlesLength, compactedVisibilityMasks.Length);
var updateCompactedInstanceVisibilityJob = new UpdateCompactedInstanceVisibilityJob
{
instanceData = m_InstanceData,
compactedVisibilityMasks = compactedVisibilityMasks
};
updateCompactedInstanceVisibilityJob.ScheduleBatch(m_InstanceData.instancesLength, UpdateCompactedInstanceVisibilityJob.k_BatchSize).Complete();
}
#if UNITY_EDITOR
public void UpdateSelectedInstances(NativeArray<InstanceHandle> instances)
{
m_InstanceData.editorData.selectedBits.FillZeroes(m_InstanceData.instancesLength);
new UpdateSelectedInstancesJob
{
instances = instances,
instanceData = m_InstanceData
}.Schedule(instances.Length, UpdateSelectedInstancesJob.k_BatchSize).Complete();
}
#endif
private static class InstanceTransformUpdateIDs
{
// Transform update kernel IDs
public static readonly int _TransformUpdateQueueCount = Shader.PropertyToID("_TransformUpdateQueueCount");
public static readonly int _TransformUpdateOutputL2WVec4Offset = Shader.PropertyToID("_TransformUpdateOutputL2WVec4Offset");
public static readonly int _TransformUpdateOutputW2LVec4Offset = Shader.PropertyToID("_TransformUpdateOutputW2LVec4Offset");
public static readonly int _TransformUpdateOutputPrevL2WVec4Offset = Shader.PropertyToID("_TransformUpdateOutputPrevL2WVec4Offset");
public static readonly int _TransformUpdateOutputPrevW2LVec4Offset = Shader.PropertyToID("_TransformUpdateOutputPrevW2LVec4Offset");
public static readonly int _BoundingSphereOutputVec4Offset = Shader.PropertyToID("_BoundingSphereOutputVec4Offset");
public static readonly int _TransformUpdateDataQueue = Shader.PropertyToID("_TransformUpdateDataQueue");
public static readonly int _TransformUpdateIndexQueue = Shader.PropertyToID("_TransformUpdateIndexQueue");
public static readonly int _BoundingSphereDataQueue = Shader.PropertyToID("_BoundingSphereDataQueue");
public static readonly int _OutputTransformBuffer = Shader.PropertyToID("_OutputTransformBuffer");
// Probe update kernel IDs
public static readonly int _ProbeUpdateQueueCount = Shader.PropertyToID("_ProbeUpdateQueueCount");
public static readonly int _SHUpdateVec4Offset = Shader.PropertyToID("_SHUpdateVec4Offset");
public static readonly int _ProbeUpdateDataQueue = Shader.PropertyToID("_ProbeUpdateDataQueue");
public static readonly int _ProbeOcclusionUpdateDataQueue = Shader.PropertyToID("_ProbeOcclusionUpdateDataQueue");
public static readonly int _ProbeUpdateIndexQueue = Shader.PropertyToID("_ProbeUpdateIndexQueue");
public static readonly int _OutputProbeBuffer = Shader.PropertyToID("_OutputProbeBuffer");
}
private static class InstanceWindDataUpdateIDs
{
public static readonly int _WindDataQueueCount = Shader.PropertyToID("_WindDataQueueCount");
public static readonly int _WindDataUpdateIndexQueue = Shader.PropertyToID("_WindDataUpdateIndexQueue");
public static readonly int _WindDataBuffer = Shader.PropertyToID("_WindDataBuffer");
public static readonly int _WindParamAddressArray = Shader.PropertyToID("_WindParamAddressArray");
public static readonly int _WindHistoryParamAddressArray = Shader.PropertyToID("_WindHistoryParamAddressArray");
}
public void DeallocatePerCameraInstanceData(NativeArray<int> cameraIDs)
{
m_PerCameraInstanceData.DeallocateCameras(cameraIDs);
}
public void AllocatePerCameraInstanceData(NativeArray<int> cameraIDs)
{
m_PerCameraInstanceData.AllocateCameras(cameraIDs);
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1b8bad1fc5971e549920c6b3e6c4b309
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -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<int> rendererGroupIDs, in NativeArray<GPUDrivenPackedRendererData> packedRendererData,
in NativeArray<int> instanceOffsets, in NativeArray<int> instanceCounts, ref InstanceAllocators instanceAllocators, ref CPUInstanceData instanceData,
ref CPUPerCameraInstanceData perCameraInstanceData, ref CPUSharedInstanceData sharedInstanceData, ref NativeArray<InstanceHandle> instances,
ref NativeParallelMultiHashMap<int, InstanceHandle> 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<int>.ReadOnly rendererGroupsID, ref InstanceAllocators instanceAllocators, ref CPUInstanceData instanceData,
ref CPUPerCameraInstanceData perCameraInstanceData, ref CPUSharedInstanceData sharedInstanceData, ref NativeParallelMultiHashMap<int, InstanceHandle> 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<InstanceHandle>.ReadOnly instances, ref InstanceAllocators instanceAllocators, ref CPUInstanceData instanceData,
ref CPUPerCameraInstanceData perCameraInstanceData, ref CPUSharedInstanceData sharedInstanceData, ref NativeParallelMultiHashMap<int, InstanceHandle> 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);
}
}
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 302f596d55264be4ba359e52ec407766

View File

@@ -0,0 +1,23 @@
using Unity.Mathematics;
using System.Runtime.InteropServices;
namespace UnityEngine.Rendering
{
[GenerateHLSL]
internal struct TransformUpdatePacket
{
public float4 localToWorld0;
public float4 localToWorld1;
public float4 localToWorld2;
}
//Must be a copy of SphericalHarmonicsL2
[GenerateHLSL]
[StructLayout(LayoutKind.Sequential)]
internal struct SHUpdatePacket
{
public float shr0, shr1, shr2, shr3, shr4, shr5, shr6, shr7, shr8;
public float shg0, shg1, shg2, shg3, shg4, shg5, shg6, shg7, shg8;
public float shb0, shb1, shb2, shb3, shb4, shb5, shb6, shb7, shb8;
}
}

View File

@@ -0,0 +1,176 @@
//
// This file was automatically generated. Please don't edit by hand. Execute Editor command [ Edit > Rendering > Generate Shader Includes ] instead
//
#ifndef INSTANCETRANSFORMUPDATEDEFS_CS_HLSL
#define INSTANCETRANSFORMUPDATEDEFS_CS_HLSL
// Generated from UnityEngine.Rendering.SHUpdatePacket
// PackingRules = Exact
struct SHUpdatePacket
{
float shr0;
float shr1;
float shr2;
float shr3;
float shr4;
float shr5;
float shr6;
float shr7;
float shr8;
float shg0;
float shg1;
float shg2;
float shg3;
float shg4;
float shg5;
float shg6;
float shg7;
float shg8;
float shb0;
float shb1;
float shb2;
float shb3;
float shb4;
float shb5;
float shb6;
float shb7;
float shb8;
};
// Generated from UnityEngine.Rendering.TransformUpdatePacket
// PackingRules = Exact
struct TransformUpdatePacket
{
float4 localToWorld0;
float4 localToWorld1;
float4 localToWorld2;
};
//
// Accessors for UnityEngine.Rendering.SHUpdatePacket
//
float GetShr0(SHUpdatePacket value)
{
return value.shr0;
}
float GetShr1(SHUpdatePacket value)
{
return value.shr1;
}
float GetShr2(SHUpdatePacket value)
{
return value.shr2;
}
float GetShr3(SHUpdatePacket value)
{
return value.shr3;
}
float GetShr4(SHUpdatePacket value)
{
return value.shr4;
}
float GetShr5(SHUpdatePacket value)
{
return value.shr5;
}
float GetShr6(SHUpdatePacket value)
{
return value.shr6;
}
float GetShr7(SHUpdatePacket value)
{
return value.shr7;
}
float GetShr8(SHUpdatePacket value)
{
return value.shr8;
}
float GetShg0(SHUpdatePacket value)
{
return value.shg0;
}
float GetShg1(SHUpdatePacket value)
{
return value.shg1;
}
float GetShg2(SHUpdatePacket value)
{
return value.shg2;
}
float GetShg3(SHUpdatePacket value)
{
return value.shg3;
}
float GetShg4(SHUpdatePacket value)
{
return value.shg4;
}
float GetShg5(SHUpdatePacket value)
{
return value.shg5;
}
float GetShg6(SHUpdatePacket value)
{
return value.shg6;
}
float GetShg7(SHUpdatePacket value)
{
return value.shg7;
}
float GetShg8(SHUpdatePacket value)
{
return value.shg8;
}
float GetShb0(SHUpdatePacket value)
{
return value.shb0;
}
float GetShb1(SHUpdatePacket value)
{
return value.shb1;
}
float GetShb2(SHUpdatePacket value)
{
return value.shb2;
}
float GetShb3(SHUpdatePacket value)
{
return value.shb3;
}
float GetShb4(SHUpdatePacket value)
{
return value.shb4;
}
float GetShb5(SHUpdatePacket value)
{
return value.shb5;
}
float GetShb6(SHUpdatePacket value)
{
return value.shb6;
}
float GetShb7(SHUpdatePacket value)
{
return value.shb7;
}
float GetShb8(SHUpdatePacket value)
{
return value.shb8;
}
//
// Accessors for UnityEngine.Rendering.TransformUpdatePacket
//
float4 GetLocalToWorld0(TransformUpdatePacket value)
{
return value.localToWorld0;
}
float4 GetLocalToWorld1(TransformUpdatePacket value)
{
return value.localToWorld1;
}
float4 GetLocalToWorld2(TransformUpdatePacket value)
{
return value.localToWorld2;
}
#endif

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: d73035ddef2bea3469c6784cf7f2e865
ShaderIncludeImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 39c61558833551e4baa6edfa795338be
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,162 @@
using System;
using System.Collections;
using System.Collections.Generic;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using UnityEngine.Assertions;
namespace UnityEngine.Rendering
{
internal enum InstanceType
{
MeshRenderer = 0,
SpeedTree = 1,
Count,
LODGroup = 0, // Aliased for now because it is part of a different instance data space.
}
internal static class InstanceTypeInfo
{
public const int kInstanceTypeBitCount = 1;
public const int kMaxInstanceTypesCount = 1 << kInstanceTypeBitCount;
public const uint kInstanceTypeMask = kMaxInstanceTypesCount - 1;
//@ For now it is probably fine to have inheritance, but in the future we might prefer composition if we end up with a lot of different types.
//@ Inheritance is really needed to quickly define overlapped instance data spaces.
private static InstanceType[] s_ParentTypes;
private static List<InstanceType>[] s_ChildTypes;
static InstanceTypeInfo()
{
Assert.IsTrue((int)InstanceType.Count <= kMaxInstanceTypesCount);
InitParentTypes();
InitChildTypes();
ValidateTypeRelationsAreCorrectlySorted();
}
private static void InitParentTypes()
{
s_ParentTypes = new InstanceType[(int)InstanceType.Count];
s_ParentTypes[(int)InstanceType.MeshRenderer] = InstanceType.MeshRenderer;
s_ParentTypes[(int)InstanceType.SpeedTree] = InstanceType.MeshRenderer;
// Add more parent types here if needed...
}
private static void InitChildTypes()
{
s_ChildTypes = new List<InstanceType>[(int)InstanceType.Count];
for (int i = 0; i < (int)InstanceType.Count; ++i)
s_ChildTypes[i] = new List<InstanceType>();
for (int i = 0; i < (int)InstanceType.Count; ++i)
{
var type = (InstanceType)i;
var parentType = s_ParentTypes[(int)type];
if (type != parentType)
s_ChildTypes[(int)parentType].Add(type);
}
}
private static InstanceType GetMaxChildTypeRecursively(InstanceType type)
{
InstanceType maxChildType = type;
foreach (var childType in s_ChildTypes[(int)type])
maxChildType = (InstanceType)Mathf.Max((int)maxChildType, (int)GetMaxChildTypeRecursively(childType));
return maxChildType;
}
private static void FlattenChildInstanceTypes(InstanceType instanceType, NativeList<InstanceType> instanceTypes)
{
instanceTypes.Add(instanceType);
foreach (var childType in s_ChildTypes[(int)instanceType])
FlattenChildInstanceTypes(childType, instanceTypes);
}
private static void ValidateTypeRelationsAreCorrectlySorted()
{
var instanceTypesFlattened = new NativeList<InstanceType>((int)InstanceType.Count, Allocator.Temp);
for(int i = 0; i < (int)InstanceType.Count; ++i)
{
InstanceType instanceType = (InstanceType)i;
if(instanceType == s_ParentTypes[i])
FlattenChildInstanceTypes(instanceType, instanceTypesFlattened);
}
Assert.AreEqual(instanceTypesFlattened.Length, (int)InstanceType.Count);
for (int i = 0; i < instanceTypesFlattened.Length; ++i)
Assert.AreEqual((int)instanceTypesFlattened[i], i, "InstanceType relation is not properly ordered. Parent and child InstanceTypes should follow " +
"the depth first order to decrease unrelated type indices overlapping and better memory utilization.");
}
public static InstanceType GetParentType(InstanceType type)
{
return s_ParentTypes[(int)type];
}
public static List<InstanceType> GetChildTypes(InstanceType type)
{
return s_ChildTypes[(int)type];
}
}
internal unsafe struct InstanceNumInfo
{
public fixed int InstanceNums[(int)InstanceType.Count];
public void InitDefault()
{
for (int i = 0; i < (int)InstanceType.Count; ++i)
InstanceNums[i] = 0;
}
public InstanceNumInfo(InstanceType type, int instanceNum)
{
InitDefault();
InstanceNums[(int)type] = instanceNum;
}
public InstanceNumInfo(int meshRendererNum = 0, int speedTreeNum = 0)
{
InitDefault();
InstanceNums[(int)InstanceType.MeshRenderer] = meshRendererNum;
InstanceNums[(int)InstanceType.SpeedTree] = speedTreeNum;
}
public int GetInstanceNum(InstanceType type)
{
return InstanceNums[(int)type];
}
public int GetInstanceNumIncludingChildren(InstanceType type)
{
int numInstances = GetInstanceNum(type);
var childTypes = InstanceTypeInfo.GetChildTypes(type);
foreach (var childType in childTypes)
numInstances += GetInstanceNumIncludingChildren(childType);
return numInstances;
}
public int GetTotalInstanceNum()
{
int totalInstanceNum = 0;
for (int i = 0; i < (int)InstanceType.Count; ++i)
totalInstanceNum += InstanceNums[i];
return totalInstanceNum;
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 77eec85a2a778304f8aec8f23df581ed

View File

@@ -0,0 +1,11 @@
using Unity.Mathematics;
using System.Runtime.InteropServices;
namespace UnityEngine.Rendering
{
[GenerateHLSL]
internal static class SpeedTreeWindShaderDef
{
public const int kMaxWindParamsCount = (int)SpeedTreeWindParamIndex.MaxWindParamsCount;
};
}

View File

@@ -0,0 +1,13 @@
//
// This file was automatically generated. Please don't edit by hand. Execute Editor command [ Edit > Rendering > Generate Shader Includes ] instead
//
#ifndef INSTANCEWINDDATAUPDATEDEFS_CS_HLSL
#define INSTANCEWINDDATAUPDATEDEFS_CS_HLSL
//
// UnityEngine.Rendering.SpeedTreeWindShaderDef: static fields
//
#define MAX_WIND_PARAMS_COUNT (16)
#endif

View File

@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: f267e53cd6dfdee4c820be8c35548ffe
ShaderIncludeImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e2393c2c53c02d24b9313fde572f87af
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: