Skip to content
Open
Show file tree
Hide file tree
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
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ await genesisTerrain.GenerateGenesisTerrainAndShowAsync(
processReport: landscapeLoadReport,
cancellationToken: ct);
else
await genesisTerrain.ShowAsync(landscapeLoadReport);
await genesisTerrain.ShowAsync(landscapeLoadReport, ct);
}
else
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@ private Mesh CreateParcelMesh()
private void InstantiateTrees(int2 parcel, ParcelData parcelData)
{
ITerrain terrain = landscape.CurrentTerrain;
var instances = terrain.Trees!.GetTreeInstances(parcel);
var instances = terrain.Trees!.GetTreeInstancesAsSpan(parcel);

foreach (var instance in instances)
{
Expand Down
18 changes: 9 additions & 9 deletions Explorer/Assets/DCL/Landscape/TerrainGenerator.cs
Original file line number Diff line number Diff line change
@@ -1,24 +1,20 @@
using Cysharp.Threading.Tasks;
using DCL.Diagnostics;
using DCL.Ipfs;
using DCL.Landscape.Jobs;
using DCL.Landscape.Settings;
using DCL.Landscape.Utils;
using System;
using System.Collections.Generic;
using System.Threading;
using DCL.Profiling;
using DCL.Utilities;
using ECS;
using ECS.SceneLifeCycle.Realm;
using System;
using System.Collections.Generic;
using System.Threading;
using TerrainProto;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Mathematics;
using UnityEngine;
using Utility;
using static Unity.Mathematics.math;
using JobHandle = Unity.Jobs.JobHandle;

namespace DCL.Landscape
{
Expand Down Expand Up @@ -100,7 +96,7 @@ public void Dispose()
public int GetChunkSize() =>
terrainGenData.chunkSize;

public async UniTask ShowAsync(AsyncLoadProcessReport postRealmLoadReport)
public async UniTask ShowAsync(AsyncLoadProcessReport postRealmLoadReport, CancellationToken ct)
{
if (!isInitialized) return;

Expand All @@ -112,9 +108,13 @@ public async UniTask ShowAsync(AsyncLoadProcessReport postRealmLoadReport)

if (landscapeData.RenderTrees)
{
// Fixes: https://github.com/decentraland/unity-explorer/issues/5873
// We need to re-upload the transforms because the
// buffer might have been overwritten by WorldTerrainGenerator
Trees!.Instantiate();
// Fixes: https://github.com/decentraland/unity-explorer/issues/7150
// The trees regeneration might be a very expensive operation with over than 100k iterations
// We need to throttle the process to prevent possible thread-locks
await Trees!.InstantiateAsync(ct);
Trees!.Show();
}

Expand Down
76 changes: 69 additions & 7 deletions Explorer/Assets/DCL/Landscape/TreeData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Threading;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Mathematics;
Expand Down Expand Up @@ -48,20 +49,34 @@ public void Dispose()
treeInstances.Dispose();
}

public ReadOnlySpan<TreeInstanceData> GetTreeInstances(int2 parcel)
public ReadOnlySpan<TreeInstanceData> GetTreeInstancesAsSpan(int2 parcel) =>
TrySolveTreeInstancesSlice(parcel, out int start, out int end)
? treeInstances.AsReadOnlySpan().Slice(start, end - start)
: ReadOnlySpan<TreeInstanceData>.Empty;

private NativeSlice<TreeInstanceData> GetTreeInstancesAsNativeArray(int2 parcel) =>
TrySolveTreeInstancesSlice(parcel, out int start, out int end)
? treeInstances.Slice(start, end - start)
: default(NativeSlice<TreeInstanceData>);

private bool TrySolveTreeInstancesSlice(int2 parcel, out int start, out int end)
{
// If tree data has not been loaded, minParcel == maxParcel, and so this is false, and we
// don't need to check if treeInstances is empty or anything like that.
if (parcel.x < treeMinParcel.x || parcel.x >= treeMaxParcel.x
|| parcel.y < treeMinParcel.y || parcel.y >= treeMaxParcel.y)
return ReadOnlySpan<TreeInstanceData>.Empty;
{
start = -1;
end = -1;
return false;
}

int index = ((parcel.y - treeMinParcel.y) * (treeMaxParcel.x - treeMinParcel.x))
+ parcel.x - treeMinParcel.x;

int start = treeIndices[index++];
int end = index < treeIndices.Length ? treeIndices[index] : treeInstances.Length;
return treeInstances.AsReadOnlySpan().Slice(start, end - start);
start = treeIndices[index++];
end = index < treeIndices.Length ? treeIndices[index] : treeInstances.Length;
return true;
}

public bool GetTreeTransform(int2 parcel, TreeInstanceData instance, out Vector3 position,
Expand Down Expand Up @@ -115,7 +130,51 @@ public void Instantiate()
for (int i = 0; i < treeIndices.Length; i++)
{
int2 parcel = int2(i % stride, i / stride) + treeMinParcel;
ReadOnlySpan<TreeInstanceData> instances = GetTreeInstances(parcel);
ReadOnlySpan<TreeInstanceData> instances = GetTreeInstancesAsSpan(parcel);

foreach (TreeInstanceData instance in instances)
{
if (GetTreeTransform(parcel, instance, out Vector3 position,
out Quaternion rotation, out Vector3 scale))
{
transforms[instance.PrototypeIndex]
.Add(Matrix4x4.TRS(position, rotation, scale));
}
}
}

for (int prototypeIndex = 0; prototypeIndex < terrainData.treeAssets.Length;
prototypeIndex++)
{
List<Matrix4x4> matrices = transforms[prototypeIndex];
instanceCounts[prototypeIndex] = matrices.Count;
GPUICoreAPI.SetTransformBufferData(rendererKeys[prototypeIndex],
matrices, 0, 0, matrices.Count);
}
}

public async UniTask InstantiateAsync(CancellationToken ct)
{
const int THROTTLE_ITERATIONS = 1000;

int stride = treeMaxParcel.x - treeMinParcel.x;
var transforms = new List<Matrix4x4>[terrainData.treeAssets.Length];

for (var i = 0; i < transforms.Length; i++)
{
if (i > 0 && i % THROTTLE_ITERATIONS == 0)
await UniTask.NextFrame(ct);

transforms[i] = new List<Matrix4x4>();
}

for (var i = 0; i < treeIndices.Length; i++)
{
if (i > 0 && i % THROTTLE_ITERATIONS == 0)
await UniTask.NextFrame(ct);

int2 parcel = int2(i % stride, i / stride) + treeMinParcel;
NativeSlice<TreeInstanceData> instances = GetTreeInstancesAsNativeArray(parcel);

foreach (TreeInstanceData instance in instances)
{
Expand All @@ -131,6 +190,9 @@ public void Instantiate()
for (int prototypeIndex = 0; prototypeIndex < terrainData.treeAssets.Length;
prototypeIndex++)
{
if (prototypeIndex > 0 && prototypeIndex % THROTTLE_ITERATIONS == 0)
await UniTask.NextFrame(ct);

List<Matrix4x4> matrices = transforms[prototypeIndex];
instanceCounts[prototypeIndex] = matrices.Count;
GPUICoreAPI.SetTransformBufferData(rendererKeys[prototypeIndex],
Expand Down Expand Up @@ -301,4 +363,4 @@ public void Show()
instanceCounts[prototypeIndex]);
}
}
}
}
Loading