From 8ed1a10b5f912f910f309180f6b879a89e07b906 Mon Sep 17 00:00:00 2001 From: Cameron Date: Mon, 20 May 2024 02:39:09 -0700 Subject: [PATCH] perf: Multithreaded the tileset tile building calculations --- .../Editor/Builders/LDtkBuilderTileset.cs | 194 ++++++++---------- .../Editor/Builders/OffsetTilemapStack.cs | 68 ------ .../Builders/OffsetTilemapStack.cs.meta | 11 - .../Editor/Builders/OffsetTilemapStacks.cs | 62 ------ .../Builders/OffsetTilemapStacks.cs.meta | 11 - .../Editor/Builders/TileBuildingJob.cs | 84 ++++++++ .../Editor/Builders/TileBuildingJob.cs.meta | 3 + .../Editor/Builders/TilemapTilesBuilder.cs | 6 + 8 files changed, 176 insertions(+), 263 deletions(-) delete mode 100644 Assets/LDtkUnity/Editor/Builders/OffsetTilemapStack.cs delete mode 100644 Assets/LDtkUnity/Editor/Builders/OffsetTilemapStack.cs.meta delete mode 100644 Assets/LDtkUnity/Editor/Builders/OffsetTilemapStacks.cs delete mode 100644 Assets/LDtkUnity/Editor/Builders/OffsetTilemapStacks.cs.meta create mode 100644 Assets/LDtkUnity/Editor/Builders/TileBuildingJob.cs create mode 100644 Assets/LDtkUnity/Editor/Builders/TileBuildingJob.cs.meta diff --git a/Assets/LDtkUnity/Editor/Builders/LDtkBuilderTileset.cs b/Assets/LDtkUnity/Editor/Builders/LDtkBuilderTileset.cs index a0b300785..417b279f1 100644 --- a/Assets/LDtkUnity/Editor/Builders/LDtkBuilderTileset.cs +++ b/Assets/LDtkUnity/Editor/Builders/LDtkBuilderTileset.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Unity.Jobs; using UnityEngine; using UnityEngine.Profiling; using UnityEngine.Tilemaps; @@ -18,10 +19,52 @@ public LDtkBuilderTileset(LDtkProjectImporter project, Level level, LDtkComponen { } + private void ConstructNewTilemap() + { + SortingOrder.Next(); + + string tilemapName = Layer.IsTilesLayer ? "Tiles" : "AutoLayer"; + + LDtkProfiler.BeginSample("CreateChildGameObject"); + GameObject tilemapObj = LayerGameObject.CreateChildGameObject(tilemapName); + LDtkProfiler.EndSample(); + + LDtkProfiler.BeginSample("AddComponent"); + Map = tilemapObj.AddComponent(); + LDtkProfiler.EndSample(); + + LDtkProfiler.BeginSample("SetOffset"); + AddLayerOffset(Map); + LDtkProfiler.EndSample(); + + LDtkProfiler.BeginSample("AddComponent"); + TilemapRenderer renderer = tilemapObj.AddComponent(); + LDtkProfiler.EndSample(); + + LDtkProfiler.BeginSample("sortingOrder ="); + renderer.sortingOrder = SortingOrder.SortingOrderValue; + LDtkProfiler.EndSample(); + + LDtkProfiler.BeginSample("AddTilemapCollider"); + AddTilemapCollider(tilemapObj); + LDtkProfiler.EndSample(); + } + public void BuildTileset(TileInstance[] tiles) { _tiles = tiles; + LDtkProfiler.BeginSample("construct TileBuildingJob"); + TileBuildingJob job = new TileBuildingJob(_tiles, Layer, LayerScale); + LDtkProfiler.EndSample(); + + //figure out the number of jobs to put into processors. +1 to round up + LDtkProfiler.BeginSample("TileBuildingJob.Schedule"); + int tilesLength = _tiles.Length; + int innerLoopBatchCount = Mathf.Max(1, (tilesLength / System.Environment.ProcessorCount) + 1); + JobHandle handle = job.ScheduleParallel(tilesLength, innerLoopBatchCount, default); + LDtkProfiler.EndSample(); + LDtkProfiler.BeginSample("ConstructNewTilemap"); ConstructNewTilemap(); LDtkProfiler.EndSample(); @@ -38,15 +81,21 @@ public void BuildTileset(TileInstance[] tiles) } LDtkProfiler.EndSample(); + LDtkProfiler.BeginSample("SetOpacity"); + Map.SetOpacity(Layer); + LDtkProfiler.EndSample(); + LDtkProfiler.BeginSample("EvaluateTilesetDefinition"); TilesetDefinition tilesetDef = EvaluateTilesetDefinition(); + LDtkProfiler.EndSample(); if (tilesetDef == null) { //It is possible that a layer has no tileset definition assigned. In this case, it's fine to not build any tiles. - LDtkProfiler.EndSample(); + handle.Complete(); + job.Input.Dispose(); + job.Output.Dispose(); return; } - LDtkProfiler.EndSample(); LDtkProfiler.BeginSample("LoadTilesetArtifacts"); LDtkArtifactAssetsTileset artifacts = Importer.LoadTilesetArtifacts(Project, tilesetDef); @@ -55,142 +104,65 @@ public void BuildTileset(TileInstance[] tiles) if (artifacts == null) { //failure to load should not spend time calculating tiles + handle.Complete(); + job.Input.Dispose(); + job.Output.Dispose(); return; } - //figure out if we have already built a tile in this position. otherwise, build up to the next tilemap. build in a completely separate path if this is an offset position from the normal standard coordinates - LDtkProfiler.BeginSample("AddTiles"); - for (int i = _tiles.Length - 1; i >= 0; i--) + LDtkProfiler.BeginSample("CacheNeededTilesArtifacts"); + TileBase[] tileAssets = new TileBase[tilesLength]; + for (int i = 0; i < tilesLength; i++) { - TileInstance tileInstance = _tiles[i]; - - LDtkProfiler.BeginSample("GetTileArtifact"); - TileBase tile; - int tileID = tileInstance.T; + int t = _tiles[i].T; try { - tile = artifacts._tiles[tileInstance.T]; + tileAssets[i] = artifacts._tiles[t]; } catch (Exception e) { - - Importer.Logger.LogError($"Failed to load a tile artifact at id \"{tileID}\" from \"{tilesetDef.Identifier}\". It's possible that the tileset definition file has imported improperly.\nLevel: {Level.Identifier}, Layer: {Layer.Identifier}\n{e}"); - tile = null; + Importer.Logger.LogError($"Failed to load a tile artifact at id \"{t}\" from \"{tilesetDef.Identifier}\". It's possible that the tileset definition file has imported improperly.\nLevel: {Level.Identifier}, Layer: {Layer.Identifier}\n{e}"); } - LDtkProfiler.EndSample(); - - LDtkProfiler.BeginSample("SetPendingTile"); - SetPendingTile(tileInstance, tile); - LDtkProfiler.EndSample(); } LDtkProfiler.EndSample(); - LDtkProfiler.BeginSample("ApplyPendingTiles"); - _tilesetProvider.ApplyPendingTiles(false); - LDtkProfiler.EndSample(); + handle.Complete(); + job.Input.Dispose(); - LDtkProfiler.BeginSample("SetOpacity"); - Map.SetOpacity(Layer); - LDtkProfiler.EndSample(); - } - - public static Vector3Int GetCellForTileCoord(TileInstance tile, LayerInstance layer) - { - int id = layer.IsAutoLayer ? tile.AutoLayerCoordId : tile.TileLayerCoordId; - - int x = id % layer.CWid; - int y = id / layer.CWid; - - return new Vector3Int(x, y, 0); - } - - private TilesetDefinition EvaluateTilesetDefinition() - { - if (Layer.OverrideTilesetUid != null) + LDtkProfiler.BeginSample("AddTiles"); + Vector3Int[] cells = new Vector3Int[tilesLength]; + for (int i = 0; i < tilesLength; i++) { - return Layer.OverrideTilesetDefinition; + cells[i] = job.Output[i].Cell; + cells[i].z = _tilesetProvider.GetNextCellZ(cells[i]); } - - return Layer.TilesetDefinition; - } - - private void ConstructNewTilemap() - { - SortingOrder.Next(); - - string tilemapName = Layer.IsTilesLayer ? "Tiles" : "AutoLayer"; - - - LDtkProfiler.BeginSample("CreateChildGameObject"); - GameObject tilemapObj = LayerGameObject.CreateChildGameObject(tilemapName); LDtkProfiler.EndSample(); - LDtkProfiler.BeginSample("AddComponent"); - Map = tilemapObj.AddComponent(); + LDtkProfiler.BeginSample("Tilemap.SetTiles"); + Map.SetTiles(cells, tileAssets); LDtkProfiler.EndSample(); - LDtkProfiler.BeginSample("SetOffset"); - AddLayerOffset(Map); - LDtkProfiler.EndSample(); - - LDtkProfiler.BeginSample("AddComponent"); - TilemapRenderer renderer = tilemapObj.AddComponent(); - LDtkProfiler.EndSample(); - - LDtkProfiler.BeginSample("sortingOrder ="); - renderer.sortingOrder = SortingOrder.SortingOrderValue; - LDtkProfiler.EndSample(); - - LDtkProfiler.BeginSample("AddTilemapCollider"); - AddTilemapCollider(tilemapObj); - LDtkProfiler.EndSample(); - } - - private void SetPendingTile(TileInstance tileData, TileBase tile) - { - LDtkProfiler.BeginSample("GetCellForTileCoord"); - Vector3Int cell = GetCellForTileCoord(tileData, Layer); - LDtkProfiler.EndSample(); - - LDtkProfiler.BeginSample("GetTileInstanceFlips"); - Matrix4x4 matrix = GetTileInstanceFlips(cell, tileData); - LDtkProfiler.EndSample(); - - LDtkProfiler.BeginSample("ConvertCellCoord"); - cell = ConvertCellCoord(cell); + LDtkProfiler.BeginSample("SetColorAndMatrix"); + for (int i = 0; i < tilesLength; i++) + { + Color color = new Color(1, 1, 1, _tiles[i].A); + Matrix4x4 matrix = job.Output[i].Matrix; + _tilesetProvider.SetColorAndMatrix(cells[i], ref color, ref matrix); + } LDtkProfiler.EndSample(); - LDtkProfiler.BeginSample("GetNextCellZ"); - cell.z = _tilesetProvider.GetNextCellZ(cell); - LDtkProfiler.EndSample(); + job.Output.Dispose(); - LDtkProfiler.BeginSample("SetPendingTile"); - _tilesetProvider.SetPendingTile(cell, tile); - LDtkProfiler.EndSample(); - - LDtkProfiler.BeginSample("SetColorAndMatrix"); - Color color = new Color(1, 1, 1, tileData.A); - _tilesetProvider.SetColorAndMatrix(cell, ref color, ref matrix); + LDtkProfiler.BeginSample("CompressBounds"); + Map.CompressBounds(); LDtkProfiler.EndSample(); } - private Matrix4x4 GetTileInstanceFlips(Vector3Int cell, TileInstance tileData) + private TilesetDefinition EvaluateTilesetDefinition() { - int gridSize = Layer.GridSize; - - int pxOffsetX = tileData.Px[0] - cell.x * gridSize; - int pxOffsetY = tileData.Px[1] - cell.y * gridSize; - - Vector3 offset = Vector3.zero; - offset.x = pxOffsetX / (float)gridSize; - offset.y = -pxOffsetY / (float)gridSize; - - float scaleFactor = 1f / LayerScale; - Vector3 scale = new Vector3(scaleFactor, scaleFactor, 1); - scale.x *= tileData.FlipX ? -1 : 1; - scale.y *= tileData.FlipY ? -1 : 1; - - return Matrix4x4.TRS(offset, Quaternion.identity, scale); + return Layer.OverrideTilesetUid != null ? Layer.OverrideTilesetDefinition : Layer.TilesetDefinition; } + + } } diff --git a/Assets/LDtkUnity/Editor/Builders/OffsetTilemapStack.cs b/Assets/LDtkUnity/Editor/Builders/OffsetTilemapStack.cs deleted file mode 100644 index 411ed28f2..000000000 --- a/Assets/LDtkUnity/Editor/Builders/OffsetTilemapStack.cs +++ /dev/null @@ -1,68 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using UnityEngine; -using UnityEngine.Tilemaps; - -namespace LDtkUnity.Editor -{ - /*internal sealed class OffsetTilemapStack - { - private readonly TilemapCreation _creationAction; - private readonly Dictionary _stacking = new Dictionary(); - private readonly Dictionary _tilemaps = new Dictionary(); - - private readonly Vector2Int _offset; - private readonly int _gridSize; - - public IEnumerable Tilemaps => _tilemaps.Values.Select(p => p.Map); - - //tile position to order - public OffsetTilemapStack(int gridSize, Vector2Int offset, TilemapCreation creationAction) - { - _gridSize = gridSize; - _offset = offset; - _creationAction = creationAction; - } - - public TilemapTilesBuilder GetTilemapForTilePosition(Vector2Int pos) - { - pos -= _offset; - - int stackOrder = GetStackOrderToBuildOn(pos); - if (_tilemaps.ContainsKey(stackOrder)) - { - return _tilemaps[stackOrder]; - } - - Tilemap tilemap = _creationAction.Invoke(); - - //modify tile anchor so that they are correctly aligned even if some rules tell the tiles to be an odd-formation (ie. The shelves in Typical 2D Platformer) - Vector2 extraAnchor = (Vector2)_offset / _gridSize; - extraAnchor.y = -extraAnchor.y; - tilemap.tileAnchor += (Vector3)extraAnchor; - - TilemapTilesBuilder builder = new TilemapTilesBuilder(tilemap, 1); - _tilemaps.Add(stackOrder, builder); - return builder; - } - - private int GetStackOrderToBuildOn(Vector2Int pos) - { - if (_stacking.ContainsKey(pos)) - { - return ++_stacking[pos]; - } - - _stacking.Add(pos, 0); - return 0; - } - - public void ApplyPendingTiles() - { - foreach (TilemapTilesBuilder builder in _tilemaps.Values) - { - builder.ApplyPendingTiles(false); - } - } - }*/ -} \ No newline at end of file diff --git a/Assets/LDtkUnity/Editor/Builders/OffsetTilemapStack.cs.meta b/Assets/LDtkUnity/Editor/Builders/OffsetTilemapStack.cs.meta deleted file mode 100644 index b66ad7486..000000000 --- a/Assets/LDtkUnity/Editor/Builders/OffsetTilemapStack.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 482604b6735a4e3b868825a8e8518f3b -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/LDtkUnity/Editor/Builders/OffsetTilemapStacks.cs b/Assets/LDtkUnity/Editor/Builders/OffsetTilemapStacks.cs deleted file mode 100644 index b82946441..000000000 --- a/Assets/LDtkUnity/Editor/Builders/OffsetTilemapStacks.cs +++ /dev/null @@ -1,62 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using UnityEngine; -using UnityEngine.Tilemaps; - -namespace LDtkUnity.Editor -{ - /*internal sealed class OffsetTilemapStacks - { - private readonly Dictionary _stacks = new Dictionary(); - private readonly TilemapCreation _creationAction; - - public IEnumerable Tilemaps => _stacks.Values.SelectMany(stack => stack.Tilemaps); - - public OffsetTilemapStacks(TilemapCreation creationAction) - { - _creationAction = creationAction; - } - - public TilemapTilesBuilder GetTilemapFromStacks(Vector2Int pxPos, int gridSize) - { - if (gridSize == 0) - { - LDtkDebug.LogError("Unexpected problem"); - return null; - } - - OffsetTilemapStack stack = GetStack(pxPos, gridSize); - return stack.GetTilemapForTilePosition(pxPos); - } - - private OffsetTilemapStack GetStack(Vector2Int pxPos, int gridSize) - { - Vector2Int offset = pxPos; - offset.x %= gridSize; - offset.y %= gridSize; - - if (_stacks.ContainsKey(offset)) - { - return _stacks[offset]; - } - - OffsetTilemapStack newStack = new OffsetTilemapStack(gridSize, offset, _creationAction); - _stacks.Add(offset, newStack); - - return newStack; - } - - public void ApplyPendingTiles() - { - foreach (OffsetTilemapStack builder in _stacks.Values) - { - builder.ApplyPendingTiles(); - } - } - - public void Clear() - { - _stacks.Clear(); - } - }*/ -} \ No newline at end of file diff --git a/Assets/LDtkUnity/Editor/Builders/OffsetTilemapStacks.cs.meta b/Assets/LDtkUnity/Editor/Builders/OffsetTilemapStacks.cs.meta deleted file mode 100644 index 51642fba8..000000000 --- a/Assets/LDtkUnity/Editor/Builders/OffsetTilemapStacks.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: b435c8629f804a0ab7e55cc077e3bfaf -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Assets/LDtkUnity/Editor/Builders/TileBuildingJob.cs b/Assets/LDtkUnity/Editor/Builders/TileBuildingJob.cs new file mode 100644 index 000000000..950cdc09b --- /dev/null +++ b/Assets/LDtkUnity/Editor/Builders/TileBuildingJob.cs @@ -0,0 +1,84 @@ +using Unity.Collections; +using Unity.Jobs; +using UnityEngine; + +namespace LDtkUnity.Editor +{ + internal struct TileBuildingJob : IJobFor + { + public struct InputData + { + public int CoordId; + public int PxX; + public int PxY; + public bool FlipX; + public bool FlipY; + } + + public struct OutputData + { + public Vector3Int Cell; + public Matrix4x4 Matrix; + } + + [ReadOnly] public NativeArray Input; + [WriteOnly] public NativeArray Output; + + [ReadOnly] public int LayerGridSize; + [ReadOnly] public int LayerCWid; + [ReadOnly] public int LayerCHei; + [ReadOnly] public float ScaleFactor; + + public TileBuildingJob(TileInstance[] tiles, LayerInstance layer, float layerScale) + { + LayerGridSize = layer.GridSize; + LayerCWid = layer.CWid; + LayerCHei = layer.CHei; + ScaleFactor = 1 / layerScale; + + int tilesCount = tiles.Length; + Input = new NativeArray(tilesCount, Allocator.TempJob); + Output = new NativeArray(tilesCount, Allocator.TempJob); + + bool isAutoLayer = layer.IsAutoLayer; + for (int i = 0; i < tilesCount; i++) + { + TileInstance tile = tiles[i]; + Input[i] = new InputData + { + CoordId = isAutoLayer ? tile.D[1] : tile.D[0], + PxX = tile.Px[0], + PxY = tile.Px[1], + FlipX = tile.FlipX, + FlipY = tile.FlipY, + }; + } + } + + public void Execute(int i) + { + int cX = Input[i].CoordId % LayerCWid; + int cY = Input[i].CoordId / LayerCWid; + + int pxOffsetX = Input[i].PxX - cX * LayerGridSize; + int pxOffsetY = Input[i].PxY - cY * LayerGridSize; + + Vector3 offset = Vector3.zero; + offset.x = pxOffsetX / (float)LayerGridSize; + offset.y = -pxOffsetY / (float)LayerGridSize; + + Vector3 scale = new Vector3(ScaleFactor, ScaleFactor, 1); + scale.x *= Input[i].FlipX ? -1 : 1; + scale.y *= Input[i].FlipY ? -1 : 1; + + //convert y into unity tilemap coordinate space + cY = -cY + LayerCHei - 1; + + Output[i] = new OutputData() + { + Cell = new Vector3Int(cX, cY, 0), + Matrix = Matrix4x4.TRS(offset, Quaternion.identity, scale), + }; + } + } +} \ No newline at end of file diff --git a/Assets/LDtkUnity/Editor/Builders/TileBuildingJob.cs.meta b/Assets/LDtkUnity/Editor/Builders/TileBuildingJob.cs.meta new file mode 100644 index 000000000..7c60458f9 --- /dev/null +++ b/Assets/LDtkUnity/Editor/Builders/TileBuildingJob.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 2698a83d8cfa47b19632c5418096ac2a +timeCreated: 1716182884 \ No newline at end of file diff --git a/Assets/LDtkUnity/Editor/Builders/TilemapTilesBuilder.cs b/Assets/LDtkUnity/Editor/Builders/TilemapTilesBuilder.cs index bc8642ef7..d503f1f4f 100644 --- a/Assets/LDtkUnity/Editor/Builders/TilemapTilesBuilder.cs +++ b/Assets/LDtkUnity/Editor/Builders/TilemapTilesBuilder.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using Unity.Collections; using UnityEngine; using UnityEngine.Profiling; using UnityEngine.Tilemaps; @@ -134,5 +135,10 @@ public void SetColorAndMatrix(Vector3Int cell, ref Color color, ref Matrix4x4 ma _extraData[cell].color = color; _extraData[cell].matrix = matrix; } + + public void SetAllTiles(Vector3Int[] cells, TileBase[] tiles) + { + + } } } \ No newline at end of file