Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/gpu instancing/roads indirect #3165

Closed
wants to merge 17 commits into from
Closed
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
adjusted PerInstance map to match Indirect shader
popuz committed Jan 23, 2025
commit 3749bccd7a45bf8a6568a5551abae01ffa25c954
Original file line number Diff line number Diff line change
@@ -10,29 +10,29 @@ public class GPUInstancingService
{
private const int BATCH_SIZE = 511;

internal readonly Dictionary<GPUInstancedRenderer, List<Matrix4x4>> gpuInstancingMap = new ();
private readonly Dictionary<GPUInstancedRenderer, List<Matrix4x4[]>> gpuBatchesMap = new ();
internal readonly Dictionary<GPUInstancedRenderer, List<PerInstance>> gpuInstancingMap = new ();
private readonly Dictionary<GPUInstancedRenderer, List<PerInstance[]>> gpuBatchesMap = new ();

public void RenderInstanced()
{
foreach (KeyValuePair<GPUInstancedRenderer, List<Matrix4x4>> renderInstances in gpuInstancingMap)
foreach (KeyValuePair<GPUInstancedRenderer, List<PerInstance>> renderInstances in gpuInstancingMap)
{
for (var i = 0; i < renderInstances.Key.RenderParamsArray.Length; i++) // foreach submesh
for (var j = 0; j < renderInstances.Value.Count; j += BATCH_SIZE)
{
Matrix4x4[] batch = renderInstances.Value.Skip(j).Take(BATCH_SIZE).ToArray();
PerInstance[] batch = renderInstances.Value.Skip(j).Take(BATCH_SIZE).ToArray();
Graphics.RenderMeshInstanced(in renderInstances.Key.RenderParamsArray[i], renderInstances.Key.Mesh, i, batch);
}
}
}

public void RenderInstancedBatched()
{
foreach ((GPUInstancedRenderer renderer, List<Matrix4x4[]> batches) in gpuBatchesMap)
foreach ((GPUInstancedRenderer renderer, List<PerInstance[]> batches) in gpuBatchesMap)
{
for (var subMeshIndex = 0; subMeshIndex < renderer.RenderParamsArray.Length; subMeshIndex++)
{
foreach (Matrix4x4[] batch in batches)
foreach (PerInstance[] batch in batches)
{
if (batch.Length != 0)
Graphics.RenderMeshInstanced(in renderer.RenderParamsArray[subMeshIndex], renderer.Mesh, subMeshIndex, batch);
@@ -45,14 +45,14 @@ public void PrepareBatches()
{
gpuBatchesMap.Clear();

foreach ((GPUInstancedRenderer renderer, List<Matrix4x4> matrices) in gpuInstancingMap)
foreach ((GPUInstancedRenderer renderer, List<PerInstance> matrices) in gpuInstancingMap)
{
var batches = new List<Matrix4x4[]>();
var batches = new List<PerInstance[]>();

for (var i = 0; i < matrices.Count; i += BATCH_SIZE)
{
int count = Mathf.Min(BATCH_SIZE, matrices.Count - i);
var batch = new Matrix4x4[count];
var batch = new PerInstance[count];
matrices.CopyTo(i, batch, 0, count);
batches.Add(batch);
}
@@ -84,14 +84,14 @@ public void AddToInstancing(List<MeshInstanceData> meshInstances, Matrix4x4 road
{
var instancedRenderer = prefabMeshInstance.MeshData.ToGPUInstancedRenderer();

if (!gpuInstancingMap.TryGetValue(instancedRenderer, out List<Matrix4x4> matrix))
if (!gpuInstancingMap.TryGetValue(instancedRenderer, out List<PerInstance> matrix))
{
matrix = new List<Matrix4x4>(prefabMeshInstance.InstancesMatrices.Count);
matrix = new List<PerInstance>(prefabMeshInstance.InstancesMatrices.Count);
gpuInstancingMap.Add(instancedRenderer, matrix);
}

foreach (Matrix4x4 instanceMatrix in prefabMeshInstance.InstancesMatrices)
matrix.Add(roadRoot * instanceMatrix);
foreach (PerInstance instanceData in prefabMeshInstance.InstancesMatrices)
matrix.Add(new PerInstance { objectToWorld = roadRoot * instanceData.objectToWorld });
}
}

@@ -100,13 +100,12 @@ private void AddToInstancing(MeshData[] meshes, Matrix4x4 roadRoot)
foreach (MeshData meshData in meshes)
{
var instancedRenderer = meshData.ToGPUInstancedRenderer();
var instanceData = new PerInstance { objectToWorld = roadRoot * meshData.LocalToRootMatrix };

Matrix4x4 localMatrix = meshData.LocalToRootMatrix;

if (gpuInstancingMap.TryGetValue(instancedRenderer, out List<Matrix4x4> matrix))
matrix.Add(roadRoot * localMatrix);
if (gpuInstancingMap.TryGetValue(instancedRenderer, out List<PerInstance> matrix))
matrix.Add(instanceData);
else
gpuInstancingMap.Add(instancedRenderer, new List<Matrix4x4> { roadRoot * localMatrix });
gpuInstancingMap.Add(instancedRenderer, new List<PerInstance> { instanceData });
}
}

Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
#if UNITY_EDITOR
@@ -7,11 +8,59 @@

namespace DCL.Roads.GPUInstancing.Playground
{
[System.Serializable]
[Serializable]
public class MeshInstanceData
{
public MeshData MeshData;
public List<Matrix4x4> InstancesMatrices;
public List<PerInstance> InstancesMatrices;
}

[Serializable]
public struct PerInstance : IEquatable<PerInstance>
{
public Matrix4x4 objectToWorld;
public Color colour;

public bool Equals(PerInstance other) =>
objectToWorld.Equals(other.objectToWorld) && colour.Equals(other.colour);

public override bool Equals(object obj) =>
obj is PerInstance other && Equals(other);

public override int GetHashCode() =>
HashCode.Combine(objectToWorld, colour);

// private const float EPSILON = 0.0001f;

// public bool Equals(Matrix4x4 a, Matrix4x4 b) =>
// Mathf.Abs(a.m00 - b.m00) < EPSILON &&
// Mathf.Abs(a.m01 - b.m01) < EPSILON &&
// Mathf.Abs(a.m02 - b.m02) < EPSILON &&
// Mathf.Abs(a.m03 - b.m03) < EPSILON &&
// Mathf.Abs(a.m10 - b.m10) < EPSILON &&
// Mathf.Abs(a.m11 - b.m11) < EPSILON &&
// Mathf.Abs(a.m12 - b.m12) < EPSILON &&
// Mathf.Abs(a.m13 - b.m13) < EPSILON &&
// Mathf.Abs(a.m20 - b.m20) < EPSILON &&
// Mathf.Abs(a.m21 - b.m21) < EPSILON &&
// Mathf.Abs(a.m22 - b.m22) < EPSILON &&
// Mathf.Abs(a.m23 - b.m23) < EPSILON &&
// Mathf.Abs(a.m30 - b.m30) < EPSILON &&
// Mathf.Abs(a.m31 - b.m31) < EPSILON &&
// Mathf.Abs(a.m32 - b.m32) < EPSILON &&
// Mathf.Abs(a.m33 - b.m33) < EPSILON;

// public int GetHashCode(Matrix4x4 matrix)
// {
// unchecked
// {
// var hash = 17;
// hash = (hash * 23) + (int)(matrix.m03 / EPSILON);
// hash = (hash * 23) + (int)(matrix.m13 / EPSILON);
// hash = (hash * 23) + (int)(matrix.m23 / EPSILON);
// return hash;
// }
// }
}

public class PrefabInstanceDataBehaviour : MonoBehaviour
@@ -59,30 +108,30 @@ public void HideVisuals()

private void CollectDataFromPrefabAsset()
{
var tempMeshToMatrices = new Dictionary<MeshData, HashSet<Matrix4x4>>();
var tempMeshToMatrices = new Dictionary<MeshData, HashSet<PerInstance>>();

Meshes = CollectStandaloneMeshesData(tempMeshToMatrices);
LODGroups = CollectLODGroupDatas(tempMeshToMatrices);

meshInstances = new List<MeshInstanceData>(tempMeshToMatrices.Keys.Count);
foreach (var kvp in tempMeshToMatrices)
foreach (KeyValuePair<MeshData, HashSet<PerInstance>> kvp in tempMeshToMatrices)
meshInstances.Add(new MeshInstanceData { MeshData = kvp.Key, InstancesMatrices = kvp.Value.ToList() });
}

private MeshData[] CollectStandaloneMeshesData(Dictionary<MeshData, HashSet<Matrix4x4>> tempMeshToMatrices)
private MeshData[] CollectStandaloneMeshesData(Dictionary<MeshData, HashSet<PerInstance>> tempMeshToMatrices)
{
Renderer[] standaloneRenderers = gameObject.GetComponentsInChildren<Renderer>(true)
.Where(r => !AssignedToLODGroupInPrefabHierarchy(r.transform)).ToArray();

return CollectMeshData(standaloneRenderers, tempMeshToMatrices).ToArray();
}

private LODGroupData[] CollectLODGroupDatas(Dictionary<MeshData, HashSet<Matrix4x4>> tempMeshToMatrices) =>
private LODGroupData[] CollectLODGroupDatas(Dictionary<MeshData, HashSet<PerInstance>> tempMeshToMatrices) =>
gameObject.GetComponentsInChildren<LODGroup>(true)
.Select(group => CollectLODGroupData(group, tempMeshToMatrices))
.Where(lodGroupData => lodGroupData.LODs.Length != 0 && lodGroupData.LODs[0].Meshes.Length != 0).ToArray();

private List<MeshData> CollectMeshData(Renderer[] renderers, Dictionary<MeshData, HashSet<Matrix4x4>> tempMeshToMatrices)
private List<MeshData> CollectMeshData(Renderer[] renderers, Dictionary<MeshData, HashSet<PerInstance>> tempMeshToMatrices)
{
var list = new List<MeshData>();

@@ -107,10 +156,15 @@ private List<MeshData> CollectMeshData(Renderer[] renderers, Dictionary<MeshData

list.Add(meshData);

PerInstance data = new PerInstance
{
objectToWorld = meshData.LocalToRootMatrix,
};

if (tempMeshToMatrices.TryGetValue(meshData, out var matrices))
matrices.Add(meshData.LocalToRootMatrix);
matrices.Add(data);
else
tempMeshToMatrices[meshData] = new HashSet<Matrix4x4> { meshData.LocalToRootMatrix };
tempMeshToMatrices[meshData] = new HashSet<PerInstance> { data };
}

return list;
@@ -132,7 +186,7 @@ private bool AssignedToLODGroupInPrefabHierarchy(Transform transform)
return false;
}

private LODGroupData CollectLODGroupData(LODGroup lodGroup, Dictionary<MeshData, HashSet<Matrix4x4>> tempMeshToMatrices)
private LODGroupData CollectLODGroupData(LODGroup lodGroup, Dictionary<MeshData, HashSet<PerInstance>> tempMeshToMatrices)
{
lodGroup.RecalculateBounds();

@@ -160,6 +214,7 @@ private static void CalculateGroupBounds(LODGroupData lodGroup)
{
var isInitialized = false;


foreach (LODEntryMeshData mid in lodGroup.LODs)
foreach (MeshData data in mid.Meshes)
{
Original file line number Diff line number Diff line change
@@ -46,19 +46,6 @@ public void Update()
gpuInstancingService.RenderInstancedBatched();
}

// private IEnumerator PrepareInstancesMapAsync()
// {
// gpuInstancingService.Clear();
//
// foreach (var prefabInstance in debugRoot.GetComponentsInChildren<PrefabInstanceDataBehaviour>())
// {
// gpuInstancingService.AddToInstancing(prefabInstance.PrefabInstance, debugRoot.transform);
// yield return null;
// }
//
// CollectDebugInfo();
// }

[ContextMenu("DEBUG - Cache Prefabs")]
private void CachePrefabs()
{
@@ -105,12 +92,9 @@ private void TransferFromConfigToService()
[ContextMenu("DEBUG - Spawn Roads")]
private void SpawnRoads()
{
// gpuInstancingService = new GPUInstancingService();

debugRoot = new GameObject("RoadsRoot").transform;
debugRoot.gameObject.SetActive(false);

// Spawn roadss
foreach (RoadDescription roadDescription in RoadsConfig.RoadDescriptions)
{
if (IsOutOfRange(roadDescription.RoadCoordinate)) continue;
@@ -122,17 +106,12 @@ private void SpawnRoads()
continue;
}

// var roadRoot = Matrix4x4.TRS(roadDescription.RoadCoordinate.ParcelToPositionFlat() + ParcelMathHelper.RoadPivotDeviation, roadDescription.Rotation, Vector3.one);
// gpuInstancingService.AddToInstancing(prefab.meshInstances, roadRoot);

Transform roadAsset =
Instantiate(prefab, roadDescription.RoadCoordinate.ParcelToPositionFlat() + ParcelMathHelper.RoadPivotDeviation, roadDescription.Rotation, debugRoot)
.transform;

roadAsset.gameObject.SetActive(true);
}

// CollectDebugInfo();
}

private void CollectDebugInfo()
14 changes: 7 additions & 7 deletions Explorer/Assets/DCL/Roads/Settings/RoadSettingsAsset.cs
Original file line number Diff line number Diff line change
@@ -27,17 +27,17 @@ public void CollectAllMeshInstances()
{
Dictionary<string, PrefabInstanceDataBehaviour> loadedPrefabs = LoadAllPrefabs();

Dictionary<MeshData, HashSet<Matrix4x4>> tempMeshToMatrices = CollectInstancesMap(loadedPrefabs);
Dictionary<MeshData, HashSet<PerInstance>> tempMeshToMatrices = CollectInstancesMap(loadedPrefabs);

RoadsMeshesGPUInstances = tempMeshToMatrices.Select(kvp => new MeshInstanceData { MeshData = kvp.Key, InstancesMatrices = kvp.Value.ToList() }).ToList();

EditorUtility.SetDirty(this);
AssetDatabase.SaveAssets();
}

private Dictionary<MeshData, HashSet<Matrix4x4>> CollectInstancesMap(Dictionary<string, PrefabInstanceDataBehaviour> loadedPrefabs)
private Dictionary<MeshData, HashSet<PerInstance>> CollectInstancesMap(Dictionary<string, PrefabInstanceDataBehaviour> loadedPrefabs)
{
var tempMeshToMatrices = new Dictionary<MeshData, HashSet<Matrix4x4>>();
var tempMeshToMatrices = new Dictionary<MeshData, HashSet<PerInstance>>();

foreach (RoadDescription roadDescription in RoadDescriptions)
{
@@ -51,14 +51,14 @@ private Dictionary<MeshData, HashSet<Matrix4x4>> CollectInstancesMap(Dictionary<

foreach (MeshInstanceData meshInstance in prefab.meshInstances)
{
if (!tempMeshToMatrices.TryGetValue(meshInstance.MeshData, out HashSet<Matrix4x4> matrices))
if (!tempMeshToMatrices.TryGetValue(meshInstance.MeshData, out HashSet<PerInstance> matrices))
{
matrices = new HashSet<Matrix4x4>(Matrix4X4Comparer.DEFAULT);
matrices = new HashSet<PerInstance>();
tempMeshToMatrices.Add(meshInstance.MeshData, matrices);
}

foreach (Matrix4x4 localMatrix in meshInstance.InstancesMatrices)
matrices.Add(roadRoot * localMatrix);
foreach (PerInstance instanceData in meshInstance.InstancesMatrices)
matrices.Add(new PerInstance { objectToWorld = roadRoot * instanceData.objectToWorld });
}
}