diff --git a/GameRealisticMap.Arma3.CommandLine/MapWorkspace.cs b/GameRealisticMap.Arma3.CommandLine/MapWorkspace.cs index 2bf0ff55..4ec4dede 100644 --- a/GameRealisticMap.Arma3.CommandLine/MapWorkspace.cs +++ b/GameRealisticMap.Arma3.CommandLine/MapWorkspace.cs @@ -53,6 +53,7 @@ public static async Task Create(Arma3MapConfig a3config, string se public void Dispose() { Progress.Dispose(); + Console.WriteLine($"Done in {Progress.Root.ElapsedMilliseconds} msec"); } } } diff --git a/GameRealisticMap.Arma3.Test/ContextMock.cs b/GameRealisticMap.Arma3.Test/ContextMock.cs index aecec51d..e05884d7 100644 --- a/GameRealisticMap.Arma3.Test/ContextMock.cs +++ b/GameRealisticMap.Arma3.Test/ContextMock.cs @@ -34,5 +34,10 @@ public void Add(T value) where T : class { Add(typeof(T), value); } + + public Task GetDataAsync(IProgressScope? parentScope = null) where T : class + { + return Task.FromResult(GetData(parentScope)); + } } } diff --git a/GameRealisticMap.Arma3/Arma3MapGenerator.cs b/GameRealisticMap.Arma3/Arma3MapGenerator.cs index 82b04a91..55408183 100644 --- a/GameRealisticMap.Arma3/Arma3MapGenerator.cs +++ b/GameRealisticMap.Arma3/Arma3MapGenerator.cs @@ -1,4 +1,5 @@ -using System.Runtime.Versioning; +using System.Collections.Concurrent; +using System.Runtime.Versioning; using BIS.WRP; using GameRealisticMap.Arma3.Assets; using GameRealisticMap.Arma3.GameEngine; @@ -133,6 +134,7 @@ protected virtual async Task LoadOsmData(IProgressScope progress // Imagery var imageryCompiler = new ImageryCompiler(assets.Materials, progress, projectDrive); + var gridTask = context.GetDataAsync(); var tiles = imageryCompiler.Compile(config, CreateImagerySource(progress, config, context)); if (progress.CancellationToken.IsCancellationRequested) { @@ -142,8 +144,7 @@ protected virtual async Task LoadOsmData(IProgressScope progress // Objects + WRP var wrpBuilder = new WrpCompiler(progress, projectDrive); - var grid = context.GetData().Elevation; - + var grid = gridTask.Result.Elevation; var size = area.SizeInMeters; var objects = GetObjects(progress, config, context, generators, grid) @@ -168,20 +169,16 @@ protected virtual IEnumerable GetObjects(IProgressScope progr private IEnumerable GenerateObjects(IProgressScope progress, IArma3MapConfig config, IContext context, Arma3LayerGeneratorCatalog generators) { + var result = new ConcurrentQueue>(); + using (var scope = progress.CreateScope("Objects", generators.Generators.Count)) { - foreach (var tb in generators.Generators) + Parallel.ForEachAsync(generators.Generators, new ParallelOptions() { CancellationToken = progress.CancellationToken, MaxDegreeOfParallelism = 4 }, async (tb, _) => { - if (progress.CancellationToken.IsCancellationRequested) - { - break; - } - foreach (var obj in tb.Generate(config, context, scope)) - { - yield return obj; - } - } + result.Enqueue(await tb.Generate(config, context, scope)); + }).Wait(); } + return result.SelectMany(o => o); } private bool IsStrictlyInside(EditableWrpObject o, float size) diff --git a/GameRealisticMap.Arma3/Arma3TerrainBuilderGenerator.cs b/GameRealisticMap.Arma3/Arma3TerrainBuilderGenerator.cs index ac8c3e0c..325c3005 100644 --- a/GameRealisticMap.Arma3/Arma3TerrainBuilderGenerator.cs +++ b/GameRealisticMap.Arma3/Arma3TerrainBuilderGenerator.cs @@ -130,7 +130,7 @@ private async Task ExportObjects(IProgressScope progress, Arma3MapConfig config, foreach (var tb in generators.Generators) { var name = GetLayerName(tb); - var entries = tb.Generate(config, context, progress).ToList(); + var entries = (await tb.Generate(config, context, progress)).ToList(); foreach (var entry in entries) { usedModels.Add(entry.Model); @@ -413,7 +413,7 @@ public async Task GenerateOnlyOneLayer(IProgressScope progress, Arma3MapConfig a throw new ApplicationException($"Layer '{layerName}' does not exists."); } - var result = generator.Generate(a3config, context, progress); + var result = await generator.Generate(a3config, context, progress); await WriteLayers(targetDirectory, GetLayerName(generator), result.ToList()); } diff --git a/GameRealisticMap.Arma3/GameEngine/ImageryCompiler.cs b/GameRealisticMap.Arma3/GameEngine/ImageryCompiler.cs index bc631dcb..102d8bb0 100644 --- a/GameRealisticMap.Arma3/GameEngine/ImageryCompiler.cs +++ b/GameRealisticMap.Arma3/GameEngine/ImageryCompiler.cs @@ -55,19 +55,28 @@ public ImageryTiler Compile(IArma3MapConfig config, IImagerySource source) var tiler = new ImageryTiler(config); - using (var idMap = source.CreateIdMap()) + var idTask = Task.Run(() => { - // idMap.SaveAsPng("idmap.png"); - GenerateIdMapTilesAndRvMat(config, idMap, tiler); - } + using (var idMap = source.CreateIdMap()) + { + // idMap.SaveAsPng("idmap.png"); + GenerateIdMapTilesAndRvMat(config, idMap, tiler); + } + }); CreateConfigCppImages(gameFileSystemWriter, config, source); - using (var satMap = source.CreateSatMap()) + var satTask = Task.Run(() => { - // satMap.SaveAsPng("satmap.png"); - GenerateSatMapTiles(config, satMap, tiler); - } + using (var satMap = source.CreateSatMap()) + { + // satMap.SaveAsPng("satmap.png"); + GenerateSatMapTiles(config, satMap, tiler); + } + }); + + idTask.Wait(); + satTask.Wait(); return tiler; } diff --git a/GameRealisticMap.Arma3/ITerrainBuilderLayerGenerator.cs b/GameRealisticMap.Arma3/ITerrainBuilderLayerGenerator.cs index 9ee1a967..d7603313 100644 --- a/GameRealisticMap.Arma3/ITerrainBuilderLayerGenerator.cs +++ b/GameRealisticMap.Arma3/ITerrainBuilderLayerGenerator.cs @@ -5,6 +5,6 @@ namespace GameRealisticMap.Arma3 { public interface ITerrainBuilderLayerGenerator { - IEnumerable Generate(IArma3MapConfig config, IContext context, IProgressScope scope); + Task> Generate(IArma3MapConfig config, IContext context, IProgressScope scope); } } \ No newline at end of file diff --git a/GameRealisticMap.Arma3/ManMade/BridgeGenerator.cs b/GameRealisticMap.Arma3/ManMade/BridgeGenerator.cs index df2752c8..960bb7d1 100644 --- a/GameRealisticMap.Arma3/ManMade/BridgeGenerator.cs +++ b/GameRealisticMap.Arma3/ManMade/BridgeGenerator.cs @@ -18,10 +18,10 @@ public BridgeGenerator(IArma3RegionAssets assets) this.assets = assets; } - public IEnumerable Generate(IArma3MapConfig config, IContext context, IProgressScope scope) + public async Task> Generate(IArma3MapConfig config, IContext context, IProgressScope scope) { - var grid = context.GetData().Elevation; - var roads = context.GetData().Roads.Where(r => r.SpecialSegment == WaySpecialSegment.Bridge).WithProgress(scope, "Bridges"); + var grid = (await context.GetDataAsync()).Elevation; + var roads = (await context.GetDataAsync()).Roads.Where(r => r.SpecialSegment == WaySpecialSegment.Bridge).WithProgress(scope, "Bridges"); var result = new List(); foreach(var bridge in roads) { diff --git a/GameRealisticMap.Arma3/ManMade/BuildingGenerator.cs b/GameRealisticMap.Arma3/ManMade/BuildingGenerator.cs index db0a6c87..52cf456d 100644 --- a/GameRealisticMap.Arma3/ManMade/BuildingGenerator.cs +++ b/GameRealisticMap.Arma3/ManMade/BuildingGenerator.cs @@ -45,11 +45,11 @@ public IEnumerable ToObjects() } } - public IEnumerable Generate(IArma3MapConfig config, IContext context, IProgressScope scope) + public async Task> Generate(IArma3MapConfig config, IContext context, IProgressScope scope) { var result = new List(); - var buildings = context.GetData().Buildings; - var roads = context.GetData().Roads; + var buildings = (await context.GetDataAsync()).Buildings; + var roads = (await context.GetDataAsync()).Roads; var prevailingWind = 0f; if (buildings.Any(b => b.TypeId == BuildingTypeId.WindTurbine)) diff --git a/GameRealisticMap.Arma3/ManMade/Farmlands/OrchardGenerator.cs b/GameRealisticMap.Arma3/ManMade/Farmlands/OrchardGenerator.cs index 5626ffcc..1bc9e53c 100644 --- a/GameRealisticMap.Arma3/ManMade/Farmlands/OrchardGenerator.cs +++ b/GameRealisticMap.Arma3/ManMade/Farmlands/OrchardGenerator.cs @@ -21,7 +21,7 @@ public OrchardGenerator(IArma3RegionAssets assets) this.assets = assets; } - public IEnumerable Generate(IArma3MapConfig config, IContext context, IProgressScope scope) + public async Task> Generate(IArma3MapConfig config, IContext context, IProgressScope scope) { var layer1 = new List>(); var layer2 = new RadiusPlacedLayer(new Vector2(config.SizeInMeters)); @@ -30,7 +30,7 @@ public IEnumerable Generate(IArma3MapConfig config, IConte if (lib1.Count > 0 || lib2.Count > 0) { var small = new List(); - var orchards = context.GetData().Polygons; + var orchards = (await context.GetDataAsync()).Polygons; foreach (var orchard in orchards.WithProgress(scope, "Orchards")) { if (orchard.Area > 4000) @@ -46,7 +46,7 @@ public IEnumerable Generate(IArma3MapConfig config, IConte using (var subscope = scope.CreateScope("SmallOrchards")) { - new FillAreaBasic(subscope, lib2).FillPolygons(layer2, small, context.GetData()); + new FillAreaBasic(subscope, lib2).FillPolygons(layer2, small, await context.GetDataAsync()); } } return layer1.SelectMany(o => o.Model.ToTerrainBuilderObjects(o)) diff --git a/GameRealisticMap.Arma3/ManMade/Farmlands/VineyardsGenerator.cs b/GameRealisticMap.Arma3/ManMade/Farmlands/VineyardsGenerator.cs index e6ab50a2..f3a7f089 100644 --- a/GameRealisticMap.Arma3/ManMade/Farmlands/VineyardsGenerator.cs +++ b/GameRealisticMap.Arma3/ManMade/Farmlands/VineyardsGenerator.cs @@ -17,13 +17,13 @@ public VineyardsGenerator(IArma3RegionAssets assets) this.assets = assets; } - public IEnumerable Generate(IArma3MapConfig config, IContext context, IProgressScope scope) + public async Task> Generate(IArma3MapConfig config, IContext context, IProgressScope scope) { var layer = new List>(); var lib = assets.GetNaturalRows(Assets.Rows.NaturalRowType.VineyardRow); if (lib.Count > 0) { - var vineyards = context.GetData().Polygons; + var vineyards = (await context.GetDataAsync()).Polygons; foreach (var vineyard in vineyards.WithProgress(scope, "Vineyards")) { var rnd = RandomHelper.CreateRandom(vineyard.Centroid); diff --git a/GameRealisticMap.Arma3/ManMade/FenceGenerator.cs b/GameRealisticMap.Arma3/ManMade/FenceGenerator.cs index ff557d15..a2cd0d04 100644 --- a/GameRealisticMap.Arma3/ManMade/FenceGenerator.cs +++ b/GameRealisticMap.Arma3/ManMade/FenceGenerator.cs @@ -17,10 +17,10 @@ public FenceGenerator(IArma3RegionAssets assets) this.assets = assets; } - public IEnumerable Generate(IArma3MapConfig config, IContext context, IProgressScope scope) + public async Task> Generate(IArma3MapConfig config, IContext context, IProgressScope scope) { - var evaluator = context.GetData(); - var fences = context.GetData().Fences; + var evaluator = await context.GetDataAsync(); + var fences = (await context.GetDataAsync()).Fences; var layer = new List>(); foreach (var fence in fences.WithProgress(scope, "Fences")) { diff --git a/GameRealisticMap.Arma3/ManMade/OrientedObjectsGenerator.cs b/GameRealisticMap.Arma3/ManMade/OrientedObjectsGenerator.cs index 7e7581be..07324718 100644 --- a/GameRealisticMap.Arma3/ManMade/OrientedObjectsGenerator.cs +++ b/GameRealisticMap.Arma3/ManMade/OrientedObjectsGenerator.cs @@ -16,10 +16,10 @@ public OrientedObjectsGenerator(IArma3RegionAssets assets) this.assets = assets; } - public IEnumerable Generate(IArma3MapConfig config, IContext context, IProgressScope scope) + public async Task> Generate(IArma3MapConfig config, IContext context, IProgressScope scope) { - var evaluator = context.GetData(); - var objects = context.GetData().Objects; + var evaluator = await context.GetDataAsync(); + var objects = (await context.GetDataAsync()).Objects; var result = new List(); foreach(var obj in objects.WithProgress(scope, "OrientedObjects")) { @@ -37,7 +37,7 @@ public IEnumerable Generate(IArma3MapConfig config, IConte var lamps = assets.GetObjects(ObjectTypeId.StreetLamp); if (lamps.Count > 0) { - foreach (var obj in context.GetData().Objects.WithProgress(scope, "ProceduralStreetLamps")) + foreach (var obj in (await context.GetDataAsync()).Objects.WithProgress(scope, "ProceduralStreetLamps")) { var definition = lamps.GetRandom(obj.Point, evaluator.GetPointContext(obj.Point, obj.Road)); if (definition != null) diff --git a/GameRealisticMap.Arma3/ManMade/RailwayGenerator.cs b/GameRealisticMap.Arma3/ManMade/RailwayGenerator.cs index 3632310c..303fbfc2 100644 --- a/GameRealisticMap.Arma3/ManMade/RailwayGenerator.cs +++ b/GameRealisticMap.Arma3/ManMade/RailwayGenerator.cs @@ -17,13 +17,13 @@ public RailwayGenerator(IArma3RegionAssets assets) this.assets = assets.Railways; } - public IEnumerable Generate(IArma3MapConfig config, IContext context, IProgressScope scope) + public async Task> Generate(IArma3MapConfig config, IContext context, IProgressScope scope) { if (assets == null || assets.Straights.Count == 0) { return Enumerable.Empty(); } - var railways = context.GetData().Railways; + var railways = (await context.GetDataAsync()).Railways; var layer = new List>(); foreach (var segment in railways.WithProgress(scope, "Railways")) { diff --git a/GameRealisticMap.Arma3/ManMade/SidewalksGenerator.cs b/GameRealisticMap.Arma3/ManMade/SidewalksGenerator.cs index 74528054..205680fe 100644 --- a/GameRealisticMap.Arma3/ManMade/SidewalksGenerator.cs +++ b/GameRealisticMap.Arma3/ManMade/SidewalksGenerator.cs @@ -17,14 +17,14 @@ public SidewalksGenerator(IArma3RegionAssets assets) this.assets = assets; } - public IEnumerable Generate(IArma3MapConfig config, IContext context, IProgressScope scope) + public async Task> Generate(IArma3MapConfig config, IContext context, IProgressScope scope) { var layer = new List>(); if (assets.Sidewalks.Count != 0) { - var evaluator = context.GetData(); - var paths = context.GetData().Paths; + var evaluator = await context.GetDataAsync(); + var paths = (await context.GetDataAsync()).Paths; foreach (var path in paths.WithProgress(scope, "Sidewalks")) { if (path.Points.Count > 1) diff --git a/GameRealisticMap.Arma3/Nature/GeneratorBase.cs b/GameRealisticMap.Arma3/Nature/GeneratorBase.cs index 6fd2f814..07eaf699 100644 --- a/GameRealisticMap.Arma3/Nature/GeneratorBase.cs +++ b/GameRealisticMap.Arma3/Nature/GeneratorBase.cs @@ -21,21 +21,21 @@ public GeneratorBase(IArma3RegionAssets assets) protected virtual bool ShouldGenerate => true; - public IEnumerable Generate(IArma3MapConfig config, IContext context, IProgressScope progress) + public async Task> Generate(IArma3MapConfig config, IContext context, IProgressScope progress) { if (!ShouldGenerate) { return new List(0); } - var evaluator = context.GetData(); + var evaluator = context.GetDataAsync(); - using var scope = progress.CreateScope(GetType().Name.Replace("Generator","")); + var polygons = context.GetDataAsync(); - var polygons = context.GetData().Polygons; + using var scope = progress.CreateScope(GetType().Name.Replace("Generator", "")); var layer = new RadiusPlacedLayer(new Vector2(config.SizeInMeters)); - Generate(layer, polygons, evaluator, scope); + Generate(layer, (await polygons).Polygons, await evaluator, scope); return layer.SelectMany(item => item.Model.ToTerrainBuilderObjects(item)); } diff --git a/GameRealisticMap.Arma3/Nature/Lakes/LakeSurfaceGenerator.cs b/GameRealisticMap.Arma3/Nature/Lakes/LakeSurfaceGenerator.cs index 32b9dd25..8a261872 100644 --- a/GameRealisticMap.Arma3/Nature/Lakes/LakeSurfaceGenerator.cs +++ b/GameRealisticMap.Arma3/Nature/Lakes/LakeSurfaceGenerator.cs @@ -22,9 +22,9 @@ public LakeSurfaceGenerator(IArma3RegionAssets assets) this.assets = assets; } - public IEnumerable Generate(IArma3MapConfig config, IContext context, IProgressScope scope) + public async Task> Generate(IArma3MapConfig config, IContext context, IProgressScope scope) { - var lakes = context.GetData().Lakes; + var lakes = (await context.GetDataAsync()).Lakes; var result = new List(); var minimalPondSize = GetMinimalPondSize(); if (minimalPondSize != null) diff --git a/GameRealisticMap.Arma3/Nature/Trees/TreeRowsGenerator.cs b/GameRealisticMap.Arma3/Nature/Trees/TreeRowsGenerator.cs index bcdcc271..f70bf28c 100644 --- a/GameRealisticMap.Arma3/Nature/Trees/TreeRowsGenerator.cs +++ b/GameRealisticMap.Arma3/Nature/Trees/TreeRowsGenerator.cs @@ -17,13 +17,13 @@ public TreeRowsGenerator(IArma3RegionAssets assets) this.assets = assets; } - public IEnumerable Generate(IArma3MapConfig config, IContext context, IProgressScope scope) + public async Task> Generate(IArma3MapConfig config, IContext context, IProgressScope scope) { var layer = new List>(); var definitions = assets.GetNaturalRows(NaturalRowType.TreeRow); if (definitions.Count > 0) { - var rows = context.GetData().Rows; + var rows = (await context.GetDataAsync()).Rows; foreach (var row in rows.WithProgress(scope, "TreeRows")) { var random = RandomHelper.CreateRandom(row.FirstPoint); diff --git a/GameRealisticMap.Arma3/Nature/Trees/TreesGenerator.cs b/GameRealisticMap.Arma3/Nature/Trees/TreesGenerator.cs index d224bd4e..89b34c24 100644 --- a/GameRealisticMap.Arma3/Nature/Trees/TreesGenerator.cs +++ b/GameRealisticMap.Arma3/Nature/Trees/TreesGenerator.cs @@ -17,7 +17,7 @@ public TreesGenerator(IArma3RegionAssets assets) this.assets = assets; } - public IEnumerable Generate(IArma3MapConfig config, IContext context, IProgressScope scope) + public async Task> Generate(IArma3MapConfig config, IContext context, IProgressScope scope) { var evaluator = context.GetData(); @@ -25,7 +25,7 @@ public IEnumerable Generate(IArma3MapConfig config, IConte var candidates = assets.GetObjects(ObjectTypeId.Tree); if (candidates.Count > 0) { - var points = context.GetData().Points; + var points = (await context.GetDataAsync()).Points; foreach (var point in points.WithProgress(scope, "Trees")) { var random = RandomHelper.CreateRandom(point); diff --git a/GameRealisticMap.Test/BuildContextMock.cs b/GameRealisticMap.Test/BuildContextMock.cs index 346ce34c..d766c40e 100644 --- a/GameRealisticMap.Test/BuildContextMock.cs +++ b/GameRealisticMap.Test/BuildContextMock.cs @@ -39,5 +39,10 @@ public void SetData(T value) { datas[typeof(T)] = value; } + + public Task GetDataAsync(IProgressScope? parentScope = null) where T : class + { + return Task.FromResult(GetData(parentScope)); + } } } diff --git a/GameRealisticMap/BuildContext.cs b/GameRealisticMap/BuildContext.cs index e6157e51..dc0e9045 100644 --- a/GameRealisticMap/BuildContext.cs +++ b/GameRealisticMap/BuildContext.cs @@ -6,7 +6,8 @@ namespace GameRealisticMap { public class BuildContext : IBuildContext { - private readonly Dictionary datas = new Dictionary(); + private readonly SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1, 1); + private readonly Dictionary datas = new Dictionary(); private readonly IProgressScope rootScope; private readonly IBuidersCatalog catalog; @@ -38,18 +39,52 @@ public void DisposeHugeImages() public T GetData(IProgressScope? parentScope = null) where T : class { - if (datas.TryGetValue(typeof(T), out var cachedData)) + return GetDataAsync(parentScope).Result; + } + + public Task GetDataAsync(IProgressScope? parentScope = null) where T : class + { + return GetDataTask(parentScope).Unwrap(); + } + + public async Task> GetDataTask(IProgressScope? parentScope = null) where T : class + { + await semaphoreSlim.WaitAsync().ConfigureAwait(false); + try { - return (T)cachedData; + if (datas.TryGetValue(typeof(T), out var data)) + { + return (Task)data; + } + var newTask = CreateDataTask(parentScope); + datas.Add(typeof(T), newTask); + return newTask; } + finally + { + semaphoreSlim.Release(); + } + } + private Task CreateDataTask(IProgressScope? parentScope) where T : class + { var builder = catalog.Get(); - using (var scope = (parentScope ?? rootScope).CreateScope(builder.GetType().Name.Replace("Builder", ""))) + return Task.Run(async () => { - var builtData = builder.Build(this, scope); - datas[typeof(T)] = builtData; - return builtData; - } + var tasks = builder.Dependencies.Select(d => d.PreAcquire(this, parentScope)).ToArray(); + if (tasks.Length > 0) + { + await Task.WhenAll(tasks).ConfigureAwait(false); + } + using (var scope = (parentScope ?? rootScope).CreateScope(builder.GetType().Name.Replace("Builder", ""))) + { + if (builder is IDataBuilderAsync asyncBuilder) + { + return await asyncBuilder.BuildAsync(this, scope).ConfigureAwait(false); + } + return builder.Build(this, scope); + } + }); } public IEnumerable GetOfType() where T : class @@ -60,7 +95,7 @@ public IEnumerable GetOfType() where T : class public void SetData(T value) where T : class { - datas[typeof(T)] = value; + datas[typeof(T)] = Task.FromResult(value); } } } diff --git a/GameRealisticMap/BuilderAdapter.cs b/GameRealisticMap/BuilderAdapter.cs index 174c4d0e..b0ffb958 100644 --- a/GameRealisticMap/BuilderAdapter.cs +++ b/GameRealisticMap/BuilderAdapter.cs @@ -19,6 +19,11 @@ public object Get(IContext ctx) return ctx.GetData(); } + public async Task GetAsync(IContext ctx) + { + return await ctx.GetDataAsync(); + } + public TResult Accept(IDataBuilderVisitor visitor) { return visitor.Visit(builder); diff --git a/GameRealisticMap/BuildersCatalog.cs b/GameRealisticMap/BuildersCatalog.cs index e86155b8..5b0cc03b 100644 --- a/GameRealisticMap/BuildersCatalog.cs +++ b/GameRealisticMap/BuildersCatalog.cs @@ -112,6 +112,16 @@ public IEnumerable GetOfType(IContext ctx, Func? filter = null) .Select(g => (T)g.Value.Get(ctx)); } + public async Task> GetOfTypeAsync(IContext ctx, Func? filter = null) where T : class + { + var tasks = builders + .Where(p => typeof(T).IsAssignableFrom(p.Key) && (filter == null || filter(p.Key))) + .Select(g => g.Value.GetAsync(ctx)) + .ToArray(); + await Task.WhenAll(tasks); + return tasks.Select(t => (T)t.Result); + } + public int CountOfType(Func? filter = null) where T : class { return builders diff --git a/GameRealisticMap/Conditions/ConditionEvaluator.cs b/GameRealisticMap/Conditions/ConditionEvaluator.cs index ca42b71c..6ec633ab 100644 --- a/GameRealisticMap/Conditions/ConditionEvaluator.cs +++ b/GameRealisticMap/Conditions/ConditionEvaluator.cs @@ -23,14 +23,34 @@ public class ConditionEvaluator : IConditionEvaluator internal const float MaxRoadBoxSearch = 75f; internal const float MaxRoadDistance = MaxRoadBoxSearch * 1.414f; // MaxRoadBoxSearch * sqrt(2) - public ConditionEvaluator(IBuildContext context) - { - this.areas = context.GetData(); - this.cities = context.GetData(); - this.elevation = context.GetData(); - this.ocean = context.GetData(); - this.roads = new TerrainSpacialIndex(context.Area); - roads.AddRange(context.GetData().Roads); + //public ConditionEvaluator(IBuildContext context) + //{ + // this.areas = context.GetData(); + // this.cities = context.GetData(); + // this.elevation = context.GetData(); + // this.ocean = context.GetData(); + // this.roads = new TerrainSpacialIndex(context.Area); + // roads.AddRange(context.GetData().Roads); + //} + + public ConditionEvaluator(ITerrainArea aera, CategoryAreaData areas, CitiesData cities, ElevationData elevation, OceanData ocean, RoadsData roads) + { + this.areas = areas; + this.cities = cities; + this.elevation = elevation; + this.ocean = ocean; + this.roads = new TerrainSpacialIndex(aera); + this.roads.AddRange(roads.Roads); + } + + public static async Task CreateAsync(IBuildContext context) + { + var areas = context.GetDataAsync(); + var cities = context.GetDataAsync(); + var elevation = context.GetDataAsync(); + var ocean = context.GetDataAsync(); + var roads = context.GetDataAsync(); + return new ConditionEvaluator(context.Area, await areas, await cities, await elevation, await ocean, await roads); } public IPointConditionContext GetPointContext(TerrainPoint point, Road? road = null) diff --git a/GameRealisticMap/Conditions/ConditionEvaluatorBuilder.cs b/GameRealisticMap/Conditions/ConditionEvaluatorBuilder.cs index e9159233..d95764ec 100644 --- a/GameRealisticMap/Conditions/ConditionEvaluatorBuilder.cs +++ b/GameRealisticMap/Conditions/ConditionEvaluatorBuilder.cs @@ -1,12 +1,25 @@ -using Pmad.ProgressTracking; +using GameRealisticMap.ElevationModel; +using GameRealisticMap.ManMade.Places; +using GameRealisticMap.ManMade.Roads; +using GameRealisticMap.ManMade; +using GameRealisticMap.Nature.Ocean; +using Pmad.ProgressTracking; namespace GameRealisticMap.Conditions { - internal class ConditionEvaluatorBuilder : IDataBuilder + internal class ConditionEvaluatorBuilder : IDataBuilderAsync { - public ConditionEvaluator Build(IBuildContext context, IProgressScope scope) + public IEnumerable Dependencies => [ + new DataDependency(), + new DataDependency(), + new DataDependency(), + new DataDependency(), + new DataDependency() + ]; + + public Task BuildAsync(IBuildContext context, IProgressScope scope) { - return new ConditionEvaluator(context); + return ConditionEvaluator.CreateAsync(context); } } } diff --git a/GameRealisticMap/DataDependency.cs b/GameRealisticMap/DataDependency.cs new file mode 100644 index 00000000..df70ca91 --- /dev/null +++ b/GameRealisticMap/DataDependency.cs @@ -0,0 +1,14 @@ +using Pmad.ProgressTracking; + +namespace GameRealisticMap +{ + public class DataDependency : IDataDependency where T : class + { + public Task PreAcquire(IContext context, IProgressScope? parentScope = null) + { + return context.GetDataAsync(parentScope); + } + + public Type Type => typeof(T); + } +} diff --git a/GameRealisticMap/ElevationModel/ElevationWithLakesBuilder.cs b/GameRealisticMap/ElevationModel/ElevationWithLakesBuilder.cs index 319c0bb7..46b5a9ab 100644 --- a/GameRealisticMap/ElevationModel/ElevationWithLakesBuilder.cs +++ b/GameRealisticMap/ElevationModel/ElevationWithLakesBuilder.cs @@ -39,6 +39,14 @@ public ElevationWithLakesBuilder() this.processors = new IElevationProcessorStage1[] { new AerowaysElevationProcessor() }; } + public IEnumerable Dependencies => [ + new DataDependency(), + new DataDependency(), + new DataDependency(), + new DataDependency(), + new DataDependency(), + new DataDependency()]; + public ElevationWithLakesData Build(IBuildContext context, IProgressScope scope) { var raw = context.GetData(); diff --git a/GameRealisticMap/IBuidersCatalog.cs b/GameRealisticMap/IBuidersCatalog.cs index 1921e6ea..2a3ee71a 100644 --- a/GameRealisticMap/IBuidersCatalog.cs +++ b/GameRealisticMap/IBuidersCatalog.cs @@ -11,5 +11,7 @@ IDataBuilder Get() IEnumerable VisitAll(IDataBuilderVisitor visitor); IEnumerable GetOfType(IContext ctx, Func? filter = null) where T : class; + + Task> GetOfTypeAsync(IContext ctx, Func? filter = null) where T : class; } } \ No newline at end of file diff --git a/GameRealisticMap/IBuilderAdapter.cs b/GameRealisticMap/IBuilderAdapter.cs index d4cf8afc..78ede5e4 100644 --- a/GameRealisticMap/IBuilderAdapter.cs +++ b/GameRealisticMap/IBuilderAdapter.cs @@ -1,6 +1,4 @@ -using GameRealisticMap.IO; - -namespace GameRealisticMap +namespace GameRealisticMap { internal interface IBuilderAdapter { @@ -9,5 +7,7 @@ internal interface IBuilderAdapter TResult Accept(IDataBuilderVisitor visitor); object Get(IContext ctx); + + Task GetAsync(IContext ctx); } } \ No newline at end of file diff --git a/GameRealisticMap/IContext.cs b/GameRealisticMap/IContext.cs index 8f9b0720..72c55480 100644 --- a/GameRealisticMap/IContext.cs +++ b/GameRealisticMap/IContext.cs @@ -7,6 +7,8 @@ public interface IContext { T GetData(IProgressScope? parentScope = null) where T : class; + Task GetDataAsync(IProgressScope? parentScope = null) where T : class; + IEnumerable GetOfType() where T : class; IHugeImageStorage HugeImageStorage { get; } diff --git a/GameRealisticMap/IDataBuilder.cs b/GameRealisticMap/IDataBuilder.cs index 7f3bb3e3..507216a2 100644 --- a/GameRealisticMap/IDataBuilder.cs +++ b/GameRealisticMap/IDataBuilder.cs @@ -4,6 +4,8 @@ namespace GameRealisticMap { public interface IDataBuilder where T : class { + IEnumerable Dependencies => Enumerable.Empty(); + T Build(IBuildContext context, IProgressScope scope); } } diff --git a/GameRealisticMap/IDataBuilderAsync.cs b/GameRealisticMap/IDataBuilderAsync.cs new file mode 100644 index 00000000..58685763 --- /dev/null +++ b/GameRealisticMap/IDataBuilderAsync.cs @@ -0,0 +1,14 @@ +using Pmad.ProgressTracking; + +namespace GameRealisticMap +{ + internal interface IDataBuilderAsync : IDataBuilder where T : class + { + T IDataBuilder.Build(IBuildContext context, IProgressScope scope) + { + return BuildAsync(context, scope).Result; + } + + Task BuildAsync(IBuildContext context, IProgressScope scope); + } +} \ No newline at end of file diff --git a/GameRealisticMap/IDataDependency.cs b/GameRealisticMap/IDataDependency.cs new file mode 100644 index 00000000..7bd016c3 --- /dev/null +++ b/GameRealisticMap/IDataDependency.cs @@ -0,0 +1,11 @@ +using Pmad.ProgressTracking; + +namespace GameRealisticMap +{ + public interface IDataDependency + { + Task PreAcquire(IContext context, IProgressScope? parentScope = null); + + Type Type { get; } + } +} \ No newline at end of file diff --git a/GameRealisticMap/IO/ContextReader.cs b/GameRealisticMap/IO/ContextReader.cs index 78875291..9f06221a 100644 --- a/GameRealisticMap/IO/ContextReader.cs +++ b/GameRealisticMap/IO/ContextReader.cs @@ -33,6 +33,11 @@ public T GetData(IProgressScope? parentScope = null) where T : class return result; } + public Task GetDataAsync(IProgressScope? parentScope = null) where T : class + { + return Task.FromResult(GetData(parentScope)); + } + public IEnumerable GetOfType() where T : class { return catalog.GetOfType(this); diff --git a/GameRealisticMap/ManMade/Farmlands/FarmlandsBuilder.cs b/GameRealisticMap/ManMade/Farmlands/FarmlandsBuilder.cs index 9cf24788..a5fa28d8 100644 --- a/GameRealisticMap/ManMade/Farmlands/FarmlandsBuilder.cs +++ b/GameRealisticMap/ManMade/Farmlands/FarmlandsBuilder.cs @@ -27,6 +27,12 @@ protected override IEnumerable GetPriority(IBuildContext context .Concat(context.GetData().Polygons); } + public override IEnumerable Dependencies => base.Dependencies.Concat([ + new DataDependency(), + new DataDependency(), + new DataDependency() + ]); + protected override List MergeIfRequired(List polygons, IProgressScope scope) { return polygons; // Do not merge, to be able to place objects at edges and to be able to post-process satellite image diff --git a/GameRealisticMap/ManMade/Farmlands/OrchardBuilder.cs b/GameRealisticMap/ManMade/Farmlands/OrchardBuilder.cs index 347b010d..8531d9f3 100644 --- a/GameRealisticMap/ManMade/Farmlands/OrchardBuilder.cs +++ b/GameRealisticMap/ManMade/Farmlands/OrchardBuilder.cs @@ -25,6 +25,11 @@ protected override IEnumerable GetPriority(IBuildContext context .Concat(context.GetData().Polygons); } + public override IEnumerable Dependencies => base.Dependencies.Concat([ + new DataDependency(), + new DataDependency() + ]); + protected override List MergeIfRequired(List polygons, IProgressScope scope) { return polygons; // Do not merge, to be able to place objects at edges and to be able to post-process satellite image diff --git a/GameRealisticMap/ManMade/Farmlands/VineyardBuilder.cs b/GameRealisticMap/ManMade/Farmlands/VineyardBuilder.cs index b93b09ce..9408a977 100644 --- a/GameRealisticMap/ManMade/Farmlands/VineyardBuilder.cs +++ b/GameRealisticMap/ManMade/Farmlands/VineyardBuilder.cs @@ -24,6 +24,8 @@ protected override IEnumerable GetPriority(IBuildContext context .Concat(context.GetData().Polygons); } + public override IEnumerable Dependencies => base.Dependencies.Concat([new DataDependency()]); + protected override List MergeIfRequired(List polygons, IProgressScope scope) { return polygons; // Do not merge, to be able to place objects at edges and to be able to post-process satellite image diff --git a/GameRealisticMap/ManMade/Surfaces/AsphaltBuilder.cs b/GameRealisticMap/ManMade/Surfaces/AsphaltBuilder.cs index c4429f77..f772ff59 100644 --- a/GameRealisticMap/ManMade/Surfaces/AsphaltBuilder.cs +++ b/GameRealisticMap/ManMade/Surfaces/AsphaltBuilder.cs @@ -34,6 +34,11 @@ protected override IEnumerable GetPriority(IBuildContext context return context.GetData().Polygons; } + public override IEnumerable Dependencies => [ + new DataDependency(), + new DataDependency() + ]; + public override AsphaltData Build(IBuildContext context, IProgressScope scope) { return CreateWrapper(GetPolygons(context, diff --git a/GameRealisticMap/Nature/BasicBuilderBase.cs b/GameRealisticMap/Nature/BasicBuilderBase.cs index 5cb1e466..aaf54c65 100644 --- a/GameRealisticMap/Nature/BasicBuilderBase.cs +++ b/GameRealisticMap/Nature/BasicBuilderBase.cs @@ -23,6 +23,13 @@ protected override IEnumerable GetPriority(IBuildContext context .Concat(lakes.Lakes.Select(l => l.TerrainPolygon)); } + public virtual IEnumerable Dependencies => [ + new DataDependency(), + new DataDependency(), + new DataDependency(), + new DataDependency() + ]; + protected abstract T CreateWrapper(List polygons); public virtual T Build(IBuildContext context, IProgressScope scope) diff --git a/GameRealisticMap/Nature/BasicRadialBuilder.cs b/GameRealisticMap/Nature/BasicRadialBuilder.cs index bdcafb0e..b5ea3e5c 100644 --- a/GameRealisticMap/Nature/BasicRadialBuilder.cs +++ b/GameRealisticMap/Nature/BasicRadialBuilder.cs @@ -43,6 +43,23 @@ protected virtual IEnumerable GetPriority(IBuildContext context) .Concat(context.GetData().Polygons); } + public virtual IEnumerable Dependencies => [ + new DataDependency(), + new DataDependency(), + new DataDependency(), + new DataDependency(), + new DataDependency(), + new DataDependency(), + new DataDependency(), + new DataDependency(), + new DataDependency(), + new DataDependency(), + new DataDependency(), + new DataDependency(), + new DataDependency(), + new DataDependency() + ]; + public TEdge Build(IBuildContext context, IProgressScope scope) { var forest = context.GetData(); diff --git a/GameRealisticMap/Nature/Forests/ForestBuilder.cs b/GameRealisticMap/Nature/Forests/ForestBuilder.cs index ad853245..caa8a30d 100644 --- a/GameRealisticMap/Nature/Forests/ForestBuilder.cs +++ b/GameRealisticMap/Nature/Forests/ForestBuilder.cs @@ -23,5 +23,7 @@ protected override IEnumerable GetPriority(IBuildContext context return base.GetPriority(context) .Concat(cutlines.Polygons); } + + public override IEnumerable Dependencies => base.Dependencies.Concat([new DataDependency()]); } } diff --git a/GameRealisticMap/Nature/Forests/ForestEdgeBuilder.cs b/GameRealisticMap/Nature/Forests/ForestEdgeBuilder.cs index 2d741924..96e87722 100644 --- a/GameRealisticMap/Nature/Forests/ForestEdgeBuilder.cs +++ b/GameRealisticMap/Nature/Forests/ForestEdgeBuilder.cs @@ -7,6 +7,12 @@ namespace GameRealisticMap.Nature.Forests { internal class ForestEdgeBuilder : IDataBuilder { + public IEnumerable Dependencies => [ + new DataDependency(), + new DataDependency(), + new DataDependency() + ]; + public ForestEdgeData Build(IBuildContext context, IProgressScope scope) { var roads = context.GetData().Roads; diff --git a/GameRealisticMap/Nature/Forests/ForestRadialBuilder.cs b/GameRealisticMap/Nature/Forests/ForestRadialBuilder.cs index 174d80a6..858f4d81 100644 --- a/GameRealisticMap/Nature/Forests/ForestRadialBuilder.cs +++ b/GameRealisticMap/Nature/Forests/ForestRadialBuilder.cs @@ -18,6 +18,9 @@ protected override IEnumerable GetPriority(IBuildContext context return base.GetPriority(context) .Concat(cutlines.Polygons); } + public override IEnumerable Dependencies => base.Dependencies.Concat([ + new DataDependency() + ]); protected override ForestRadialData CreateWrapper(List polygons) { diff --git a/GameRealisticMap/Nature/Lakes/LakesBuilder.cs b/GameRealisticMap/Nature/Lakes/LakesBuilder.cs index 215e16fa..5a8091e3 100644 --- a/GameRealisticMap/Nature/Lakes/LakesBuilder.cs +++ b/GameRealisticMap/Nature/Lakes/LakesBuilder.cs @@ -41,5 +41,9 @@ protected override IEnumerable GetPriority(IBuildContext context .SelectMany(s => s.Path.ToTerrainPolygon(s.Width + embankmentMargin)) .ToList(); } + + public override IEnumerable Dependencies => [ + new DataDependency() + ]; } } diff --git a/GameRealisticMap/Nature/RockAreas/RocksBuilder.cs b/GameRealisticMap/Nature/RockAreas/RocksBuilder.cs index 5da3a223..bab8ee4d 100644 --- a/GameRealisticMap/Nature/RockAreas/RocksBuilder.cs +++ b/GameRealisticMap/Nature/RockAreas/RocksBuilder.cs @@ -23,5 +23,10 @@ protected override IEnumerable GetPriority(IBuildContext context .Concat(context.GetData().Polygons) .Concat(context.GetData().Polygons); } + + public override IEnumerable Dependencies => base.Dependencies.Concat([ + new DataDependency(), + new DataDependency() + ]); } } diff --git a/GameRealisticMap/Nature/RockAreas/ScreeBuilder.cs b/GameRealisticMap/Nature/RockAreas/ScreeBuilder.cs index 8889b2da..0255fc42 100644 --- a/GameRealisticMap/Nature/RockAreas/ScreeBuilder.cs +++ b/GameRealisticMap/Nature/RockAreas/ScreeBuilder.cs @@ -24,5 +24,12 @@ protected override IEnumerable GetPriority(IBuildContext context .Concat(context.GetData().Polygons) .Concat(context.GetData().Polygons); } + + public override IEnumerable Dependencies => base.Dependencies.Concat([ + new DataDependency(), + new DataDependency(), + new DataDependency() + ]); + } } diff --git a/GameRealisticMap/Nature/Scrubs/ScrubBuilder.cs b/GameRealisticMap/Nature/Scrubs/ScrubBuilder.cs index 1403209d..a84aefd8 100644 --- a/GameRealisticMap/Nature/Scrubs/ScrubBuilder.cs +++ b/GameRealisticMap/Nature/Scrubs/ScrubBuilder.cs @@ -21,5 +21,9 @@ protected override IEnumerable GetPriority(IBuildContext context return base.GetPriority(context) .Concat(context.GetData().Polygons); } + + public override IEnumerable Dependencies => base.Dependencies.Concat([ + new DataDependency() + ]); } } diff --git a/GameRealisticMap/Nature/Scrubs/ScrubRadialBuilder.cs b/GameRealisticMap/Nature/Scrubs/ScrubRadialBuilder.cs index ee88fd0d..0e0f2e5a 100644 --- a/GameRealisticMap/Nature/Scrubs/ScrubRadialBuilder.cs +++ b/GameRealisticMap/Nature/Scrubs/ScrubRadialBuilder.cs @@ -18,6 +18,10 @@ protected override IEnumerable GetPriority(IBuildContext context .Concat(forestEdge.Polygons); } + public override IEnumerable Dependencies => base.Dependencies.Concat([ + new DataDependency() + ]); + protected override ScrubRadialData CreateWrapper(List polygons) { return new ScrubRadialData(polygons); diff --git a/GameRealisticMap/Nature/Surfaces/GrassBuilder.cs b/GameRealisticMap/Nature/Surfaces/GrassBuilder.cs index 7fa04766..1e528526 100644 --- a/GameRealisticMap/Nature/Surfaces/GrassBuilder.cs +++ b/GameRealisticMap/Nature/Surfaces/GrassBuilder.cs @@ -51,6 +51,10 @@ protected override IEnumerable GetPriority(IBuildContext context return base.GetPriority(context) .Concat(context.GetData().Polygons); } + public override IEnumerable Dependencies => base.Dependencies.Concat([ + new DataDependency() + ]); + public override GrassData Build(IBuildContext context, IProgressScope scope) { return CreateWrapper(GetPolygons(context, diff --git a/GameRealisticMap/Nature/Surfaces/IceSurfaceBuilder.cs b/GameRealisticMap/Nature/Surfaces/IceSurfaceBuilder.cs index 8e17d708..55eb6913 100644 --- a/GameRealisticMap/Nature/Surfaces/IceSurfaceBuilder.cs +++ b/GameRealisticMap/Nature/Surfaces/IceSurfaceBuilder.cs @@ -30,5 +30,12 @@ protected override IEnumerable GetPriority(IBuildContext context .Concat(context.GetData().Polygons) .Concat(context.GetData().Polygons); } + + public override IEnumerable Dependencies => base.Dependencies.Concat([ + new DataDependency(), + new DataDependency(), + new DataDependency(), + new DataDependency() + ]); } } diff --git a/GameRealisticMap/Nature/Surfaces/MeadowsBuilder.cs b/GameRealisticMap/Nature/Surfaces/MeadowsBuilder.cs index 84cb0ae9..69d5ffef 100644 --- a/GameRealisticMap/Nature/Surfaces/MeadowsBuilder.cs +++ b/GameRealisticMap/Nature/Surfaces/MeadowsBuilder.cs @@ -1,6 +1,5 @@ using GameRealisticMap.Geometries; using GameRealisticMap.Nature.Forests; -using GameRealisticMap.Reporting; using OsmSharp.Tags; namespace GameRealisticMap.Nature.Surfaces @@ -33,5 +32,8 @@ protected override IEnumerable GetPriority(IBuildContext context return base.GetPriority(context) .Concat(context.GetData().Polygons); } + public override IEnumerable Dependencies => base.Dependencies.Concat([ + new DataDependency() + ]); } } diff --git a/GameRealisticMap/Nature/Surfaces/SandSurfacesBuilder.cs b/GameRealisticMap/Nature/Surfaces/SandSurfacesBuilder.cs index 76aa2668..b0223b27 100644 --- a/GameRealisticMap/Nature/Surfaces/SandSurfacesBuilder.cs +++ b/GameRealisticMap/Nature/Surfaces/SandSurfacesBuilder.cs @@ -25,5 +25,8 @@ protected override IEnumerable GetPriority(IBuildContext context { return Enumerable.Empty(); } + + public override IEnumerable Dependencies => Enumerable.Empty(); + } } diff --git a/GameRealisticMap/Nature/Weather/WeatherBuilder.cs b/GameRealisticMap/Nature/Weather/WeatherBuilder.cs index c65f3da6..006c904f 100644 --- a/GameRealisticMap/Nature/Weather/WeatherBuilder.cs +++ b/GameRealisticMap/Nature/Weather/WeatherBuilder.cs @@ -4,7 +4,7 @@ namespace GameRealisticMap.Nature.Weather { - internal class WeatherBuilder : IDataBuilder + internal class WeatherBuilder : IDataBuilderAsync { private readonly ISourceLocations sources; @@ -13,7 +13,7 @@ public WeatherBuilder(ISourceLocations sources) this.sources = sources; } - public WeatherData Build(IBuildContext context, IProgressScope scope) + public async Task BuildAsync(IBuildContext context, IProgressScope scope) { var db = WeatherStatsDatabase.Create(sources.WeatherStats.AbsoluteUri); @@ -24,7 +24,7 @@ public WeatherData Build(IBuildContext context, IProgressScope scope) using var report = scope.CreateSingle("WeatherStats"); - var data = db.GetStats(center.Y, center.X).Result; + var data = await db.GetStats(center.Y, center.X); return new WeatherData(data); }