Merge remote-tracking branch 'origin/master' into 1.11

This commit is contained in:
Wojtek Figat
2025-06-18 22:46:10 +02:00
5 changed files with 188 additions and 98 deletions

View File

@@ -709,7 +709,7 @@ public:
--_count; --_count;
T* data = _allocation.Get(); T* data = _allocation.Get();
if (_count) if (_count)
data[index] = data[_count]; data[index] = MoveTemp(data[_count]);
Memory::DestructItems(data + _count, 1); Memory::DestructItems(data + _count, 1);
} }

View File

@@ -16,7 +16,6 @@ API_CLASS(InBuild) class BitArray
public: public:
using ItemType = uint64; using ItemType = uint64;
using AllocationData = typename AllocationType::template Data<ItemType>; using AllocationData = typename AllocationType::template Data<ItemType>;
static constexpr int32 ItemBitCount = 64;
private: private:
int32 _count; int32 _count;
@@ -210,8 +209,8 @@ public:
bool Get(const int32 index) const bool Get(const int32 index) const
{ {
ASSERT(index >= 0 && index < _count); ASSERT(index >= 0 && index < _count);
const ItemType offset = index / ItemBitCount; const ItemType offset = index / 64;
const ItemType bitMask = (ItemType)(1 << (index & (ItemBitCount - 1))); const ItemType bitMask = 1ull << (index & 63ull);
const ItemType item = ((ItemType*)_allocation.Get())[offset]; const ItemType item = ((ItemType*)_allocation.Get())[offset];
return (item & bitMask) != 0; return (item & bitMask) != 0;
} }
@@ -224,8 +223,8 @@ public:
void Set(const int32 index, const bool value) void Set(const int32 index, const bool value)
{ {
ASSERT(index >= 0 && index < _count); ASSERT(index >= 0 && index < _count);
const ItemType offset = index / ItemBitCount; const ItemType offset = index / 64;
const ItemType bitMask = (ItemType)(1 << (index & (ItemBitCount - 1))); const ItemType bitMask = 1ull << (index & 63ull);
ItemType& item = ((ItemType*)_allocation.Get())[offset]; ItemType& item = ((ItemType*)_allocation.Get())[offset];
if (value) if (value)
item |= bitMask; // Set the bit item |= bitMask; // Set the bit

View File

@@ -10,6 +10,7 @@
#include "NavModifierVolume.h" #include "NavModifierVolume.h"
#include "NavMeshRuntime.h" #include "NavMeshRuntime.h"
#include "Engine/Core/Log.h" #include "Engine/Core/Log.h"
#include "Engine/Core/ScopeExit.h"
#include "Engine/Core/Math/BoundingBox.h" #include "Engine/Core/Math/BoundingBox.h"
#include "Engine/Core/Math/Vector3.h" #include "Engine/Core/Math/Vector3.h"
#include "Engine/Physics/Colliders/BoxCollider.h" #include "Engine/Physics/Colliders/BoxCollider.h"
@@ -68,6 +69,11 @@ struct Modifier
NavAreaProperties* NavArea; NavAreaProperties* NavArea;
}; };
struct TileId
{
int32 X, Y, Layer;
};
struct NavSceneRasterizer struct NavSceneRasterizer
{ {
NavMesh* NavMesh; NavMesh* NavMesh;
@@ -198,13 +204,15 @@ struct NavSceneRasterizer
// Transform vertices into world space vertex buffer // Transform vertices into world space vertex buffer
vb.Resize(vertexCount); vb.Resize(vertexCount);
Float3* vbData = vb.Get();
for (int32 i = 0; i < vertexCount; i++) for (int32 i = 0; i < vertexCount; i++)
vb[i] = sphere.Center + vertices[i] * sphere.Radius; vbData[i] = sphere.Center + vertices[i] * sphere.Radius;
// Generate index buffer // Generate index buffer
const int32 stride = horizontalSegments + 1; const int32 stride = horizontalSegments + 1;
int32 indexCount = 0; int32 indexCount = 0;
ib.Resize(verticalSegments * (horizontalSegments + 1) * 6); ib.Resize(verticalSegments * (horizontalSegments + 1) * 6);
int32* ibData = ib.Get();
for (int32 i = 0; i < verticalSegments; i++) for (int32 i = 0; i < verticalSegments; i++)
{ {
const int32 nextI = i + 1; const int32 nextI = i + 1;
@@ -212,13 +220,13 @@ struct NavSceneRasterizer
{ {
const int32 nextJ = (j + 1) % stride; const int32 nextJ = (j + 1) % stride;
ib[indexCount++] = i * stride + j; ibData[indexCount++] = i * stride + j;
ib[indexCount++] = nextI * stride + j; ibData[indexCount++] = nextI * stride + j;
ib[indexCount++] = i * stride + nextJ; ibData[indexCount++] = i * stride + nextJ;
ib[indexCount++] = i * stride + nextJ; ibData[indexCount++] = i * stride + nextJ;
ib[indexCount++] = nextI * stride + j; ibData[indexCount++] = nextI * stride + j;
ib[indexCount++] = nextI * stride + nextJ; ibData[indexCount++] = nextI * stride + nextJ;
} }
} }
} }
@@ -335,54 +343,8 @@ struct NavSceneRasterizer
} }
}; };
// Builds navmesh tile bounds and check if there are any valid navmesh volumes at that tile location void CancelNavMeshTileBuildTasks(NavMeshRuntime* runtime);
// Returns true if tile is intersecting with any navmesh bounds volume actor - which means tile is in use void CancelNavMeshTileBuildTasks(NavMeshRuntime* runtime, int32 x, int32 y);
bool GetNavMeshTileBounds(Scene* scene, NavMesh* navMesh, int32 x, int32 y, float tileSize, BoundingBox& tileBoundsNavMesh, const Matrix& worldToNavMesh)
{
// Build initial tile bounds (with infinite extent)
tileBoundsNavMesh.Minimum.X = (float)x * tileSize;
tileBoundsNavMesh.Minimum.Y = -NAV_MESH_TILE_MAX_EXTENT;
tileBoundsNavMesh.Minimum.Z = (float)y * tileSize;
tileBoundsNavMesh.Maximum.X = tileBoundsNavMesh.Minimum.X + tileSize;
tileBoundsNavMesh.Maximum.Y = NAV_MESH_TILE_MAX_EXTENT;
tileBoundsNavMesh.Maximum.Z = tileBoundsNavMesh.Minimum.Z + tileSize;
// Check if any navmesh volume intersects with the tile
bool foundAnyVolume = false;
Vector2 rangeY;
for (int32 i = 0; i < scene->Navigation.Volumes.Count(); i++)
{
const auto volume = scene->Navigation.Volumes[i];
if (!volume->AgentsMask.IsNavMeshSupported(navMesh->Properties))
continue;
const auto& volumeBounds = volume->GetBox();
BoundingBox volumeBoundsNavMesh;
BoundingBox::Transform(volumeBounds, worldToNavMesh, volumeBoundsNavMesh);
if (volumeBoundsNavMesh.Intersects(tileBoundsNavMesh))
{
if (foundAnyVolume)
{
rangeY.X = Math::Min(rangeY.X, volumeBoundsNavMesh.Minimum.Y);
rangeY.Y = Math::Max(rangeY.Y, volumeBoundsNavMesh.Maximum.Y);
}
else
{
rangeY.X = volumeBoundsNavMesh.Minimum.Y;
rangeY.Y = volumeBoundsNavMesh.Maximum.Y;
}
foundAnyVolume = true;
}
}
if (foundAnyVolume)
{
// Build proper tile bounds
tileBoundsNavMesh.Minimum.Y = rangeY.X;
tileBoundsNavMesh.Maximum.Y = rangeY.Y;
}
return foundAnyVolume;
}
void RemoveTile(NavMesh* navMesh, NavMeshRuntime* runtime, int32 x, int32 y, int32 layer) void RemoveTile(NavMesh* navMesh, NavMeshRuntime* runtime, int32 x, int32 y, int32 layer)
{ {
@@ -404,9 +366,10 @@ void RemoveTile(NavMesh* navMesh, NavMeshRuntime* runtime, int32 x, int32 y, int
runtime->RemoveTile(x, y, layer); runtime->RemoveTile(x, y, layer);
} }
bool GenerateTile(NavMesh* navMesh, NavMeshRuntime* runtime, int32 x, int32 y, BoundingBox& tileBoundsNavMesh, const Matrix& worldToNavMesh, float tileSize, rcConfig& config) bool GenerateTile(NavMesh* navMesh, NavMeshRuntime* runtime, int32 x, int32 y, BoundingBox& tileBoundsNavMesh, const Matrix& worldToNavMesh, float tileSize, rcConfig& config, Task* task)
{ {
rcContext context; rcContext context;
context.enableLog(false);
int32 layer = 0; int32 layer = 0;
// Expand tile bounds by a certain margin // Expand tile bounds by a certain margin
@@ -423,6 +386,7 @@ bool GenerateTile(NavMesh* navMesh, NavMeshRuntime* runtime, int32 x, int32 y, B
LOG(Warning, "Could not generate navmesh: Out of memory for heightfield."); LOG(Warning, "Could not generate navmesh: Out of memory for heightfield.");
return true; return true;
} }
SCOPE_EXIT{ rcFreeHeightField(heightfield); };
if (!rcCreateHeightfield(&context, *heightfield, config.width, config.height, config.bmin, config.bmax, config.cs, config.ch)) if (!rcCreateHeightfield(&context, *heightfield, config.width, config.height, config.bmin, config.bmax, config.cs, config.ch))
{ {
LOG(Warning, "Could not generate navmesh: Could not create solid heightfield."); LOG(Warning, "Could not generate navmesh: Could not create solid heightfield.");
@@ -463,6 +427,9 @@ bool GenerateTile(NavMesh* navMesh, NavMeshRuntime* runtime, int32 x, int32 y, B
} }
} }
if (task->IsCancelRequested())
return false;
{ {
PROFILE_CPU_NAMED("FilterHeightfield"); PROFILE_CPU_NAMED("FilterHeightfield");
rcFilterLowHangingWalkableObstacles(&context, config.walkableClimb, *heightfield); rcFilterLowHangingWalkableObstacles(&context, config.walkableClimb, *heightfield);
@@ -476,6 +443,7 @@ bool GenerateTile(NavMesh* navMesh, NavMeshRuntime* runtime, int32 x, int32 y, B
LOG(Warning, "Could not generate navmesh: Out of memory compact heightfield."); LOG(Warning, "Could not generate navmesh: Out of memory compact heightfield.");
return true; return true;
} }
SCOPE_EXIT{ rcFreeCompactHeightfield(compactHeightfield); };
{ {
PROFILE_CPU_NAMED("CompactHeightfield"); PROFILE_CPU_NAMED("CompactHeightfield");
if (!rcBuildCompactHeightfield(&context, config.walkableHeight, config.walkableClimb, *heightfield, *compactHeightfield)) if (!rcBuildCompactHeightfield(&context, config.walkableHeight, config.walkableClimb, *heightfield, *compactHeightfield))
@@ -484,7 +452,6 @@ bool GenerateTile(NavMesh* navMesh, NavMeshRuntime* runtime, int32 x, int32 y, B
return true; return true;
} }
} }
rcFreeHeightField(heightfield);
{ {
PROFILE_CPU_NAMED("ErodeWalkableArea"); PROFILE_CPU_NAMED("ErodeWalkableArea");
if (!rcErodeWalkableArea(&context, config.walkableRadius, *compactHeightfield)) if (!rcErodeWalkableArea(&context, config.walkableRadius, *compactHeightfield))
@@ -506,6 +473,9 @@ bool GenerateTile(NavMesh* navMesh, NavMeshRuntime* runtime, int32 x, int32 y, B
} }
} }
if (task->IsCancelRequested())
return false;
{ {
PROFILE_CPU_NAMED("BuildDistanceField"); PROFILE_CPU_NAMED("BuildDistanceField");
if (!rcBuildDistanceField(&context, *compactHeightfield)) if (!rcBuildDistanceField(&context, *compactHeightfield))
@@ -529,6 +499,7 @@ bool GenerateTile(NavMesh* navMesh, NavMeshRuntime* runtime, int32 x, int32 y, B
LOG(Warning, "Could not generate navmesh: Out of memory for contour set."); LOG(Warning, "Could not generate navmesh: Out of memory for contour set.");
return true; return true;
} }
SCOPE_EXIT{ rcFreeContourSet(contourSet); };
{ {
PROFILE_CPU_NAMED("BuildContours"); PROFILE_CPU_NAMED("BuildContours");
if (!rcBuildContours(&context, *compactHeightfield, config.maxSimplificationError, config.maxEdgeLen, *contourSet)) if (!rcBuildContours(&context, *compactHeightfield, config.maxSimplificationError, config.maxEdgeLen, *contourSet))
@@ -544,6 +515,7 @@ bool GenerateTile(NavMesh* navMesh, NavMeshRuntime* runtime, int32 x, int32 y, B
LOG(Warning, "Could not generate navmesh: Out of memory for poly mesh."); LOG(Warning, "Could not generate navmesh: Out of memory for poly mesh.");
return true; return true;
} }
SCOPE_EXIT{ rcFreePolyMesh(polyMesh); };
{ {
PROFILE_CPU_NAMED("BuildPolyMesh"); PROFILE_CPU_NAMED("BuildPolyMesh");
if (!rcBuildPolyMesh(&context, *contourSet, config.maxVertsPerPoly, *polyMesh)) if (!rcBuildPolyMesh(&context, *contourSet, config.maxVertsPerPoly, *polyMesh))
@@ -559,6 +531,7 @@ bool GenerateTile(NavMesh* navMesh, NavMeshRuntime* runtime, int32 x, int32 y, B
LOG(Warning, "Could not generate navmesh: Out of memory for detail mesh."); LOG(Warning, "Could not generate navmesh: Out of memory for detail mesh.");
return true; return true;
} }
SCOPE_EXIT{ rcFreePolyMeshDetail(detailMesh); };
{ {
PROFILE_CPU_NAMED("BuildPolyMeshDetail"); PROFILE_CPU_NAMED("BuildPolyMeshDetail");
if (!rcBuildPolyMeshDetail(&context, *polyMesh, *compactHeightfield, config.detailSampleDist, config.detailSampleMaxError, *detailMesh)) if (!rcBuildPolyMeshDetail(&context, *polyMesh, *compactHeightfield, config.detailSampleDist, config.detailSampleMaxError, *detailMesh))
@@ -568,9 +541,6 @@ bool GenerateTile(NavMesh* navMesh, NavMeshRuntime* runtime, int32 x, int32 y, B
} }
} }
rcFreeCompactHeightfield(compactHeightfield);
rcFreeContourSet(contourSet);
for (int i = 0; i < polyMesh->npolys; i++) for (int i = 0; i < polyMesh->npolys; i++)
polyMesh->flags[i] = polyMesh->areas[i] != RC_NULL_AREA ? 1 : 0; polyMesh->flags[i] = polyMesh->areas[i] != RC_NULL_AREA ? 1 : 0;
if (polyMesh->nverts == 0) if (polyMesh->nverts == 0)
@@ -647,6 +617,9 @@ bool GenerateTile(NavMesh* navMesh, NavMeshRuntime* runtime, int32 x, int32 y, B
params.offMeshConUserID = offMeshId.Get(); params.offMeshConUserID = offMeshId.Get();
} }
if (task->IsCancelRequested())
return false;
// Generate navmesh tile data // Generate navmesh tile data
unsigned char* navData = nullptr; unsigned char* navData = nullptr;
int navDataSize = 0; int navDataSize = 0;
@@ -660,9 +633,9 @@ bool GenerateTile(NavMesh* navMesh, NavMeshRuntime* runtime, int32 x, int32 y, B
} }
ASSERT_LOW_LAYER(navDataSize > 4 && *(uint32*)navData == DT_NAVMESH_MAGIC); // Sanity check for Detour header ASSERT_LOW_LAYER(navDataSize > 4 && *(uint32*)navData == DT_NAVMESH_MAGIC); // Sanity check for Detour header
if (!task->IsCancelRequested())
{ {
PROFILE_CPU_NAMED("CreateTiles"); PROFILE_CPU_NAMED("CreateTiles");
ScopeLock lock(runtime->Locker); ScopeLock lock(runtime->Locker);
navMesh->IsDataDirty = true; navMesh->IsDataDirty = true;
@@ -762,7 +735,7 @@ public:
const auto navMesh = NavMesh.Get(); const auto navMesh = NavMesh.Get();
if (!navMesh) if (!navMesh)
return false; return false;
if (GenerateTile(NavMesh, Runtime, X, Y, TileBoundsNavMesh, WorldToNavMesh, TileSize, Config)) if (GenerateTile(NavMesh, Runtime, X, Y, TileBoundsNavMesh, WorldToNavMesh, TileSize, Config, this))
{ {
LOG(Warning, "Failed to generate navmesh tile at {0}x{1}.", X, Y); LOG(Warning, "Failed to generate navmesh tile at {0}x{1}.", X, Y);
} }
@@ -779,6 +752,50 @@ public:
} }
}; };
void CancelNavMeshTileBuildTasks(NavMeshRuntime* runtime)
{
NavBuildTasksLocker.Lock();
for (int32 i = 0; i < NavBuildTasks.Count(); i++)
{
auto task = NavBuildTasks[i];
if (task->Runtime == runtime)
{
NavBuildTasksLocker.Unlock();
// Cancel task but without locking queue from this thread to prevent deadlocks
task->Cancel();
NavBuildTasksLocker.Lock();
i--;
if (NavBuildTasks.IsEmpty())
break;
}
}
NavBuildTasksLocker.Unlock();
}
void CancelNavMeshTileBuildTasks(NavMeshRuntime* runtime, int32 x, int32 y)
{
NavBuildTasksLocker.Lock();
for (int32 i = 0; i < NavBuildTasks.Count(); i++)
{
auto task = NavBuildTasks[i];
if (task->Runtime == runtime && task->X == x && task->Y == y)
{
NavBuildTasksLocker.Unlock();
// Cancel task but without locking queue from this thread to prevent deadlocks
task->Cancel();
NavBuildTasksLocker.Lock();
i--;
if (NavBuildTasks.IsEmpty())
break;
}
}
NavBuildTasksLocker.Unlock();
}
void OnSceneUnloading(Scene* scene, const Guid& sceneId) void OnSceneUnloading(Scene* scene, const Guid& sceneId)
{ {
// Cancel pending build requests // Cancel pending build requests
@@ -843,14 +860,15 @@ float NavMeshBuilder::GetNavMeshBuildingProgress()
void BuildTileAsync(NavMesh* navMesh, const int32 x, const int32 y, const rcConfig& config, const BoundingBox& tileBoundsNavMesh, const Matrix& worldToNavMesh, float tileSize) void BuildTileAsync(NavMesh* navMesh, const int32 x, const int32 y, const rcConfig& config, const BoundingBox& tileBoundsNavMesh, const Matrix& worldToNavMesh, float tileSize)
{ {
PROFILE_CPU();
NavMeshRuntime* runtime = navMesh->GetRuntime(); NavMeshRuntime* runtime = navMesh->GetRuntime();
NavBuildTasksLocker.Lock(); NavBuildTasksLocker.Lock();
// Skip if this tile is already during cooking // Skip if this tile is already during cooking
for (int32 i = 0; i < NavBuildTasks.Count(); i++) for (int32 i = 0; i < NavBuildTasks.Count(); i++)
{ {
const auto task = NavBuildTasks[i]; const auto task = NavBuildTasks.Get()[i];
if (task->X == x && task->Y == y && task->Runtime == runtime) if (task->GetState() == TaskState::Queued && task->X == x && task->Y == y && task->Runtime == runtime)
{ {
NavBuildTasksLocker.Unlock(); NavBuildTasksLocker.Unlock();
return; return;
@@ -899,11 +917,16 @@ void BuildDirtyBounds(Scene* scene, NavMesh* navMesh, const BoundingBox& dirtyBo
{ {
PROFILE_CPU_NAMED("Prepare"); PROFILE_CPU_NAMED("Prepare");
runtime->Locker.Lock();
// Prepare scene data and navmesh // Prepare scene data and navmesh
rebuild |= Math::NotNearEqual(navMesh->Data.TileSize, tileSize); rebuild |= Math::NotNearEqual(navMesh->Data.TileSize, tileSize);
if (rebuild) if (rebuild)
{ {
runtime->Locker.Unlock();
CancelNavMeshTileBuildTasks(runtime);
runtime->Locker.Lock();
// Remove all tiles from navmesh runtime // Remove all tiles from navmesh runtime
runtime->RemoveTiles(navMesh); runtime->RemoveTiles(navMesh);
runtime->SetTileSize(tileSize); runtime->SetTileSize(tileSize);
@@ -918,9 +941,10 @@ void BuildDirtyBounds(Scene* scene, NavMesh* navMesh, const BoundingBox& dirtyBo
else else
{ {
// Ensure to have enough memory for tiles // Ensure to have enough memory for tiles
runtime->SetTileSize(tileSize);
runtime->EnsureCapacity(tilesX * tilesY); runtime->EnsureCapacity(tilesX * tilesY);
} }
runtime->Locker.Unlock();
} }
// Initialize nav mesh configuration // Initialize nav mesh configuration
@@ -931,21 +955,93 @@ void BuildDirtyBounds(Scene* scene, NavMesh* navMesh, const BoundingBox& dirtyBo
{ {
PROFILE_CPU_NAMED("StartBuildingTiles"); PROFILE_CPU_NAMED("StartBuildingTiles");
// Cache navmesh volumes
Array<BoundingBox, InlinedAllocation<8>> volumes;
for (int32 i = 0; i < scene->Navigation.Volumes.Count(); i++)
{
const auto volume = scene->Navigation.Volumes.Get()[i];
if (!volume->AgentsMask.IsNavMeshSupported(navMesh->Properties) ||
!volume->GetBox().Intersects(dirtyBoundsAligned))
continue;
auto& bounds = volumes.AddOne();
BoundingBox::Transform(volume->GetBox(), worldToNavMesh, bounds);
}
Array<TileId> unusedTiles;
Array<Pair<TileId, BoundingBox>> usedTiles;
for (int32 y = tilesMin.Z; y < tilesMax.Z; y++) for (int32 y = tilesMin.Z; y < tilesMax.Z; y++)
{ {
for (int32 x = tilesMin.X; x < tilesMax.X; x++) for (int32 x = tilesMin.X; x < tilesMax.X; x++)
{ {
// Build initial tile bounds (with infinite extent)
BoundingBox tileBoundsNavMesh; BoundingBox tileBoundsNavMesh;
if (GetNavMeshTileBounds(scene, navMesh, x, y, tileSize, tileBoundsNavMesh, worldToNavMesh)) tileBoundsNavMesh.Minimum.X = (float)x * tileSize;
tileBoundsNavMesh.Minimum.Y = -NAV_MESH_TILE_MAX_EXTENT;
tileBoundsNavMesh.Minimum.Z = (float)y * tileSize;
tileBoundsNavMesh.Maximum.X = tileBoundsNavMesh.Minimum.X + tileSize;
tileBoundsNavMesh.Maximum.Y = NAV_MESH_TILE_MAX_EXTENT;
tileBoundsNavMesh.Maximum.Z = tileBoundsNavMesh.Minimum.Z + tileSize;
// Check if any navmesh volume intersects with the tile
bool foundAnyVolume = false;
Vector2 rangeY;
for (const auto& bounds : volumes)
{ {
BuildTileAsync(navMesh, x, y, config, tileBoundsNavMesh, worldToNavMesh, tileSize); if (bounds.Intersects(tileBoundsNavMesh))
{
if (foundAnyVolume)
{
rangeY.X = Math::Min(rangeY.X, bounds.Minimum.Y);
rangeY.Y = Math::Max(rangeY.Y, bounds.Maximum.Y);
}
else
{
rangeY.X = bounds.Minimum.Y;
rangeY.Y = bounds.Maximum.Y;
foundAnyVolume = true;
}
}
}
// Check if tile is intersecting with any navmesh bounds volume actor - which means tile is in use
if (foundAnyVolume)
{
// Setup proper tile bounds
tileBoundsNavMesh.Minimum.Y = rangeY.X;
tileBoundsNavMesh.Maximum.Y = rangeY.Y;
usedTiles.Add({ { x, y, 0 }, tileBoundsNavMesh });
} }
else else
{ {
RemoveTile(navMesh, runtime, x, y, 0); unusedTiles.Add({ x, y, 0 });
} }
} }
} }
// Remove unused tiles
{
PROFILE_CPU_NAMED("RemoveUnused");
for (const auto& tile : unusedTiles)
{
// Wait for any async tasks that are producing this tile
CancelNavMeshTileBuildTasks(runtime, tile.X, tile.Y);
}
runtime->Locker.Lock();
for (const auto& tile : unusedTiles)
{
RemoveTile(navMesh, runtime, tile.X, tile.Y, 0);
}
runtime->Locker.Unlock();
}
// Build used tiles
{
PROFILE_CPU_NAMED("AddNew");
for (const auto& e : usedTiles)
{
BuildTileAsync(navMesh, e.First.X, e.First.Y, config, e.Second, worldToNavMesh, tileSize);
}
}
} }
} }
@@ -1025,7 +1121,7 @@ void BuildDirtyBounds(Scene* scene, const BoundingBox& dirtyBounds, bool rebuild
NavBuildTasksLocker.Lock(); NavBuildTasksLocker.Lock();
for (int32 i = 0; i < NavBuildTasks.Count(); i++) for (int32 i = 0; i < NavBuildTasks.Count(); i++)
{ {
if (NavBuildTasks[i]->NavMesh == navMesh) if (NavBuildTasks.Get()[i]->NavMesh == navMesh)
usageCount++; usageCount++;
} }
NavBuildTasksLocker.Unlock(); NavBuildTasksLocker.Unlock();
@@ -1064,7 +1160,7 @@ void NavMeshBuilder::Update()
const auto now = DateTime::NowUTC(); const auto now = DateTime::NowUTC();
for (int32 i = 0; NavBuildQueue.HasItems() && i < NavBuildQueue.Count(); i++) for (int32 i = 0; NavBuildQueue.HasItems() && i < NavBuildQueue.Count(); i++)
{ {
auto req = NavBuildQueue[i]; auto req = NavBuildQueue.Get()[i];
if (now - req.Time >= 0) if (now - req.Time >= 0)
{ {
NavBuildQueue.RemoveAt(i--); NavBuildQueue.RemoveAt(i--);
@@ -1118,7 +1214,7 @@ void NavMeshBuilder::Build(Scene* scene, float timeoutMs)
for (int32 i = 0; i < NavBuildQueue.Count(); i++) for (int32 i = 0; i < NavBuildQueue.Count(); i++)
{ {
auto& e = NavBuildQueue[i]; auto& e = NavBuildQueue.Get()[i];
if (e.Scene == scene && e.DirtyBounds == req.DirtyBounds) if (e.Scene == scene && e.DirtyBounds == req.DirtyBounds)
{ {
e = req; e = req;

View File

@@ -16,6 +16,19 @@
#define USE_DATA_LINK 0 #define USE_DATA_LINK 0
#define USE_NAV_MESH_ALLOC 0 #define USE_NAV_MESH_ALLOC 0
#if USE_NAV_MESH_ALLOC
#define GET_NAV_TILE_DATA(tile) \
const int32 dataSize = (tile).Data.Length(); \
const auto flags = DT_TILE_FREE_DATA; \
const auto data = (byte*)dtAlloc(dataSize, DT_ALLOC_PERM); \
Platform::MemoryCopy(data, (tile).Data.Get(), dataSize)
#else
#define GET_NAV_TILE_DATA(tile) \
const int32 dataSize = (tile).Data.Length(); \
const auto flags = 0; \
const auto data = (tile).Data.Get()
#endif
namespace namespace
{ {
FORCE_INLINE void InitFilter(dtQueryFilter& filter) FORCE_INLINE void InitFilter(dtQueryFilter& filter)
@@ -355,15 +368,7 @@ void NavMeshRuntime::EnsureCapacity(int32 tilesToAddCount)
// Restore previous tiles // Restore previous tiles
for (auto& tile : _tiles) for (auto& tile : _tiles)
{ {
const int32 dataSize = tile.Data.Length(); GET_NAV_TILE_DATA(tile);
#if USE_NAV_MESH_ALLOC
const auto flags = DT_TILE_FREE_DATA;
const auto data = (byte*)dtAlloc(dataSize, DT_ALLOC_PERM);
Platform::MemoryCopy(data, tile.Data.Get(), dataSize);
#else
const auto flags = 0;
const auto data = tile.Data.Get();
#endif
const auto result = _navMesh->addTile(data, dataSize, flags, 0, nullptr); const auto result = _navMesh->addTile(data, dataSize, flags, 0, nullptr);
if (dtStatusFailed(result)) if (dtStatusFailed(result))
{ {
@@ -665,15 +670,7 @@ void NavMeshRuntime::AddTileInternal(NavMesh* navMesh, NavMeshTileData& tileData
#endif #endif
// Add tile to navmesh // Add tile to navmesh
const int32 dataSize = tile->Data.Length(); GET_NAV_TILE_DATA(*tile);
#if USE_NAV_MESH_ALLOC
const auto flags = DT_TILE_FREE_DATA;
const auto data = (byte*)dtAlloc(dataSize, DT_ALLOC_PERM);
Platform::MemoryCopy(data, tile->Data.Get(), dataSize);
#else
const auto flags = 0;
const auto data = tile->Data.Get();
#endif
const auto result = _navMesh->addTile(data, dataSize, flags, 0, nullptr); const auto result = _navMesh->addTile(data, dataSize, flags, 0, nullptr);
if (dtStatusFailed(result)) if (dtStatusFailed(result))
{ {

View File

@@ -43,8 +43,6 @@ bool Task::Wait(double timeoutMilliseconds) const
ZoneColor(TracyWaitZoneColor); ZoneColor(TracyWaitZoneColor);
const double startTime = Platform::GetTimeSeconds(); const double startTime = Platform::GetTimeSeconds();
// TODO: no active waiting! use a semaphore!
do do
{ {
auto state = GetState(); auto state = GetState();
@@ -211,7 +209,7 @@ void Task::OnCancel()
{ {
// Wait for it a little bit // Wait for it a little bit
constexpr double timeout = 10000.0; // 10s constexpr double timeout = 10000.0; // 10s
LOG(Warning, "Cannot cancel \'{0}\' because it's still running, waiting for end with timeout: {1}ms", ToString(), timeout); //LOG(Warning, "Cannot cancel \'{0}\' because it's still running, waiting for end with timeout: {1}ms", ToString(), timeout);
Wait(timeout); Wait(timeout);
} }