You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

309 lines
15 KiB

using System;
using Unity.Collections;
using Unity.Burst;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Jobs;
using Unity.Mathematics;
using UnityEngine.Assertions;
namespace UnityEngine.Rendering
{
[BurstCompile]
internal static class InstanceCullingBatcherBurst
{
private static void RemoveDrawRange(in RangeKey key, ref NativeParallelHashMap<RangeKey, int> rangeHash, ref NativeList<DrawRange> drawRanges)
{
int drawRangeIndex = rangeHash[key];
ref DrawRange lastDrawRange = ref drawRanges.ElementAt(drawRanges.Length - 1);
rangeHash[lastDrawRange.key] = drawRangeIndex;
rangeHash.Remove(key);
drawRanges.RemoveAtSwapBack(drawRangeIndex);
}
private static void RemoveDrawBatch(in DrawKey key, ref NativeList<DrawRange> drawRanges, ref NativeParallelHashMap<RangeKey, int> rangeHash,
ref NativeParallelHashMap<DrawKey, int> batchHash, ref NativeList<DrawBatch> drawBatches)
{
int drawBatchIndex = batchHash[key];
int drawRangeIndex = rangeHash[key.range];
ref DrawRange drawRange = ref drawRanges.ElementAt(drawRangeIndex);
Assert.IsTrue(drawRange.drawCount > 0);
if (--drawRange.drawCount == 0)
RemoveDrawRange(drawRange.key, ref rangeHash, ref drawRanges);
ref DrawBatch lastDrawBatch = ref drawBatches.ElementAt(drawBatches.Length - 1);
batchHash[lastDrawBatch.key] = drawBatchIndex;
batchHash.Remove(key);
drawBatches.RemoveAtSwapBack(drawBatchIndex);
}
[BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)]
public static unsafe void RemoveDrawInstanceIndices(in NativeArray<int> drawInstanceIndices, ref NativeList<DrawInstance> drawInstances, ref NativeParallelHashMap<RangeKey, int> rangeHash,
ref NativeParallelHashMap<DrawKey, int> batchHash, ref NativeList<DrawRange> drawRanges, ref NativeList<DrawBatch> drawBatches)
{
var drawInstancesPtr = (DrawInstance*)drawInstances.GetUnsafePtr();
var drawInstancesNewBack = drawInstances.Length - 1;
for (int indexRev = drawInstanceIndices.Length - 1; indexRev >= 0; --indexRev)
{
int indexToRemove = drawInstanceIndices[indexRev];
DrawInstance* drawInstance = drawInstancesPtr + indexToRemove;
int drawBatchIndex = batchHash[drawInstance->key];
ref DrawBatch drawBatch = ref drawBatches.ElementAt(drawBatchIndex);
Assert.IsTrue(drawBatch.instanceCount > 0);
if (--drawBatch.instanceCount == 0)
RemoveDrawBatch(drawBatch.key, ref drawRanges, ref rangeHash, ref batchHash, ref drawBatches);
UnsafeUtility.MemCpy(drawInstance, drawInstancesPtr + drawInstancesNewBack--, sizeof(DrawInstance));
}
drawInstances.ResizeUninitialized(drawInstancesNewBack + 1);
}
private static ref DrawRange EditDrawRange(in RangeKey key, NativeParallelHashMap<RangeKey, int> rangeHash, NativeList<DrawRange> drawRanges)
{
int drawRangeIndex;
if (!rangeHash.TryGetValue(key, out drawRangeIndex))
{
var drawRange = new DrawRange { key = key, drawCount = 0, drawOffset = 0 };
drawRangeIndex = drawRanges.Length;
rangeHash.Add(key, drawRangeIndex);
drawRanges.Add(drawRange);
}
ref DrawRange data = ref drawRanges.ElementAt(drawRangeIndex);
Assert.IsTrue(data.key.Equals(key));
return ref data;
}
private static ref DrawBatch EditDrawBatch(in DrawKey key, in SubMeshDescriptor subMeshDescriptor, NativeParallelHashMap<DrawKey, int> batchHash, NativeList<DrawBatch> drawBatches)
{
var procInfo = new MeshProceduralInfo();
procInfo.topology = subMeshDescriptor.topology;
procInfo.baseVertex = (uint)subMeshDescriptor.baseVertex;
procInfo.firstIndex = (uint)subMeshDescriptor.indexStart;
procInfo.indexCount = (uint)subMeshDescriptor.indexCount;
int drawBatchIndex;
if (!batchHash.TryGetValue(key, out drawBatchIndex))
{
var drawBatch = new DrawBatch() { key = key, instanceCount = 0, instanceOffset = 0, procInfo = procInfo };
drawBatchIndex = drawBatches.Length;
batchHash.Add(key, drawBatchIndex);
drawBatches.Add(drawBatch);
}
ref DrawBatch data = ref drawBatches.ElementAt(drawBatchIndex);
Assert.IsTrue(data.key.Equals(key));
return ref data;
}
private static void ProcessRenderer(int i, bool implicitInstanceIndices, in GPUDrivenRendererGroupData rendererData,
NativeParallelHashMap<EntityId, BatchMeshID> batchMeshHash, NativeParallelHashMap<EntityId, GPUDrivenPackedMaterialData> packedMaterialDataHash,
NativeParallelHashMap<EntityId, BatchMaterialID> batchMaterialHash, NativeArray<InstanceHandle> instances, NativeList<DrawInstance> drawInstances,
NativeParallelHashMap<RangeKey, int> rangeHash, NativeList<DrawRange> drawRanges, NativeParallelHashMap<DrawKey, int> batchHash,
NativeList<DrawBatch> drawBatches)
{
var meshIndex = rendererData.meshIndex[i];
var meshID = rendererData.meshID[meshIndex];
var meshLodInfo = rendererData.meshLodInfo[meshIndex];
var submeshCount = rendererData.subMeshCount[meshIndex];
var subMeshDescOffset = rendererData.subMeshDescOffset[meshIndex];
var batchMeshID = batchMeshHash[meshID];
var rendererGroupID = rendererData.rendererGroupID[i];
var startSubMesh = rendererData.subMeshStartIndex[i];
var gameObjectLayer = rendererData.gameObjectLayer[i];
var renderingLayerMask = rendererData.renderingLayerMask[i];
var materialsOffset = rendererData.materialsOffset[i];
var materialsCount = rendererData.materialsCount[i];
var lightmapIndex = rendererData.lightmapIndex[i];
var packedRendererData = rendererData.packedRendererData[i];
var rendererPriority = rendererData.rendererPriority[i];
int instanceCount;
int instanceOffset;
if (implicitInstanceIndices)
{
instanceCount = 1;
instanceOffset = i;
}
else
{
instanceCount = rendererData.instancesCount[i];
instanceOffset = rendererData.instancesOffset[i];
}
if (instanceCount == 0)
return;
const int kLightmapIndexMask = 0xffff;
const int kLightmapIndexInfluenceOnly = 0xfffe;
var overridenComponents = InstanceComponentGroup.Default;
// Add per-instance wind parameters
if(packedRendererData.hasTree)
overridenComponents |= InstanceComponentGroup.Wind;
var lmIndexMasked = lightmapIndex & kLightmapIndexMask;
// Object doesn't have a valid lightmap Index, -> uses probes for lighting
if (lmIndexMasked >= kLightmapIndexInfluenceOnly)
{
// Only add the component when needed to store blended results (shader will use the ambient probe when not present)
if (packedRendererData.lightProbeUsage == LightProbeUsage.BlendProbes)
overridenComponents |= InstanceComponentGroup.LightProbe;
}
else
{
// Add per-instance lightmap parameters
overridenComponents |= InstanceComponentGroup.Lightmap;
}
// Scan all materials once to retrieve whether this renderer is indirect-compatible or not (and store it in the RangeKey).
Span<GPUDrivenPackedMaterialData> packedMaterialDatas = stackalloc GPUDrivenPackedMaterialData[materialsCount];
var supportsIndirect = true;
for (int matIndex = 0; matIndex < materialsCount; ++matIndex)
{
if (matIndex >= submeshCount)
{
Debug.LogWarning("Material count in the shared material list is higher than sub mesh count for the mesh. Object may be corrupted.");
continue;
}
var materialIndex = rendererData.materialIndex[materialsOffset + matIndex];
GPUDrivenPackedMaterialData packedMaterialData;
if (rendererData.packedMaterialData.Length > 0)
{
packedMaterialData = rendererData.packedMaterialData[materialIndex];
}
else
{
var materialID = rendererData.materialID[materialIndex];
bool isFound = packedMaterialDataHash.TryGetValue(materialID, out packedMaterialData);
Assert.IsTrue(isFound);
}
supportsIndirect &= packedMaterialData.isIndirectSupported;
packedMaterialDatas[matIndex] = packedMaterialData;
}
var rangeKey = new RangeKey
{
layer = (byte)gameObjectLayer,
renderingLayerMask = renderingLayerMask,
motionMode = packedRendererData.motionVecGenMode,
shadowCastingMode = packedRendererData.shadowCastingMode,
staticShadowCaster = packedRendererData.staticShadowCaster,
rendererPriority = rendererPriority,
supportsIndirect = supportsIndirect
};
ref DrawRange drawRange = ref EditDrawRange(rangeKey, rangeHash, drawRanges);
for (int matIndex = 0; matIndex < materialsCount; ++matIndex)
{
if (matIndex >= submeshCount)
{
Debug.LogWarning("Material count in the shared material list is higher than sub mesh count for the mesh. Object may be corrupted.");
continue;
}
var materialIndex = rendererData.materialIndex[materialsOffset + matIndex];
var materialID = rendererData.materialID[materialIndex];
var packedMaterialData = packedMaterialDatas[matIndex];
if (materialID == 0)
{
Debug.LogWarning("Material in the shared materials list is null. Object will be partially rendered.");
continue;
}
batchMaterialHash.TryGetValue(materialID, out BatchMaterialID batchMaterialID);
// We always provide crossfade value packed in instance index. We don't use None even if there is no LOD to not split the batch.
var flags = BatchDrawCommandFlags.LODCrossFadeValuePacked;
// Let the engine know if we've opted out of lightmap texture arrays
flags |= BatchDrawCommandFlags.UseLegacyLightmapsKeyword;
// assume that a custom motion vectors pass contains deformation motion, so should always output motion vectors
// (otherwise this flag is set dynamically during culling only when the transform is changing)
if (packedMaterialData.isMotionVectorsPassEnabled)
flags |= BatchDrawCommandFlags.HasMotion;
if (packedMaterialData.isTransparent)
flags |= BatchDrawCommandFlags.HasSortingPosition;
if (packedMaterialData.supportsCrossFade)
flags |= BatchDrawCommandFlags.LODCrossFadeKeyword;
int lodLoopCount = math.max(meshLodInfo.levelCount, 1);
for (int lodLoopIndex = 0; lodLoopIndex < lodLoopCount; ++lodLoopIndex)
{
var submeshIndex = startSubMesh + matIndex;
var subMeshDesc = rendererData.subMeshDesc[subMeshDescOffset + submeshIndex*lodLoopCount + lodLoopIndex];
var drawKey = new DrawKey
{
materialID = batchMaterialID,
meshID = batchMeshID,
submeshIndex = submeshIndex,
activeMeshLod = meshLodInfo.lodSelectionActive ? lodLoopIndex : -1,
flags = flags,
transparentInstanceId = packedMaterialData.isTransparent ? rendererGroupID : 0,
range = rangeKey,
overridenComponents = (uint)overridenComponents,
// When we've opted out of lightmap texture arrays, we
// need to pass in a valid lightmap index. The engine
// uses this index for sorting and for breaking the
// batch when lightmaps change across draw calls, and
// for binding the correct light map.
lightmapIndex = lightmapIndex
};
ref DrawBatch drawBatch = ref EditDrawBatch(drawKey, subMeshDesc, batchHash, drawBatches);
if (drawBatch.instanceCount == 0)
++drawRange.drawCount;
drawBatch.instanceCount += instanceCount;
for (int j = 0; j < instanceCount; ++j)
{
var instanceIndex = instanceOffset + j;
InstanceHandle instance = instances[instanceIndex];
drawInstances.Add(new DrawInstance { key = drawKey, instanceIndex = instance.index });
}
}
}
}
[BurstCompile(DisableSafetyChecks = true, OptimizeFor = OptimizeFor.Performance)]
public static void CreateDrawBatches(bool implicitInstanceIndices, in NativeArray<InstanceHandle> instances, in GPUDrivenRendererGroupData rendererData,
in NativeParallelHashMap<EntityId, BatchMeshID> batchMeshHash, in NativeParallelHashMap<EntityId, BatchMaterialID> batchMaterialHash,
in NativeParallelHashMap<EntityId, GPUDrivenPackedMaterialData> packedMaterialDataHash,
ref NativeParallelHashMap<RangeKey, int> rangeHash, ref NativeList<DrawRange> drawRanges, ref NativeParallelHashMap<DrawKey, int> batchHash, ref NativeList<DrawBatch> drawBatches,
ref NativeList<DrawInstance> drawInstances)
{
for (int i = 0; i < rendererData.rendererGroupID.Length; ++i)
ProcessRenderer(i, implicitInstanceIndices, rendererData, batchMeshHash, packedMaterialDataHash, batchMaterialHash, instances,
drawInstances, rangeHash, drawRanges, batchHash, drawBatches);
}
}
}