I have the following scenario for which I've been experiencing performance problems:
The context is a level editor of a new engine for an old video game (whose source is unavailable) in Unity. Basically I'm writing a level editor for Unity to shape old data structures into contemporary, more usable ones, i.e. parts/entities instead of a bunch of unrelated models/polygons.
Here's a tree of how things are organized in the old game:
- Container (dozens)
- Models (hundreds)
- Polygons (thousands)
- Models (hundreds)
Here's what I'd like to achieve:
- Group(s): for grouping related objects
- Object(s): a tree, a ship etc ...
- Part(s): an object's parts with specific material etc ...
- Object(s): a tree, a ship etc ...
Depending what the final object looks like on the screen, the initial data (models/polygons) is a mess, where there are merged or unrelated parts in a model and its polygons.
Other problems emerge due to this, for instance, what used to be a simple pointer update to update a texture or color is simply not possible from within Unity.
Here things I need and what I've done:
I need to be able to track implicit and explicit references to models and polygons. This is to ensure that valid objects are built and therefore prevent incorrect content.
Basically the user can create a part from either the model or or one of its polygons, the opposite being disallowed depending what's chosen first.
Here's a picture with explanations:
- Scene9 is of type
Container
- M123_456_ABC... are of type
Model
- P123_456_ABC... are of type
Polygon
- A green icon is an explicit reference
- A yellow icon is an implicit reference
- A red icon is a non-referenced item
Also, the user interface is augmented by elements getting disabled whenever appropriate to help user picking the right actions when building the level, etc ...
Here, the issues I am experiencing:
Querying for explicit/implicit references between around 300 models and 7000 polygons becomes slow over time as relationships gets created.
I've been using a couple of List<T>
and do LINQ queries between them:
- everything
- all models
- all polygons
- implicit models
- implicit polygons
- explicit models
- explicit polygons
Minus the spaghetti logic, it does works but it's slow. Then I've upgraded to HashSet<T>
with custom Comparer<T>
which greatly improved speed but it's still not fast enough. All this results in freezes in the user interface.
Main consideration is that within Unity, loops are rather tight, calls at high rate, immediate mode UI and no asynchronous calls features (Mono is roughly .NET 3.5).
I am starting to consider that my approach using lists is flawed and was thinking that a better data structure could be used for performant queries.
Can you suggest a more appropriate data structure to use for such scenario ?
EDIT
Here is a diagram showing the new approach I'm trying after your suggestions:
Notes:
IScenePrimitive
consists of 3int
and anEnum
ISceneReference.IsRef
is simplyIsRefExplicit || IsRefImplicit
- Missing things here are a dictionary in
Scene
for quick access to a model/polygon, when user selects something I retrieve these using hashes without having to enumerate, etc ...
Interesting parts in code:
SceneModel:
public override bool IsRefExplicit
{
get { return _isRefExplicit; }
set
{
_isRefExplicit = value;
if (value)
{
Debug.Assert(_polygons.None(s => s.IsRefExplicit));
foreach (var polygon in _polygons)
polygon.IsRefImplicit = true;
}
else
{
Debug.Assert(_polygons.All(s => s.IsRefImplicit));
foreach (var polygon in _polygons)
polygon.IsRefImplicit = false;
}
}
}
public override bool IsRefImplicit
{
get { return _isRefImplicit; }
set
{
_isRefImplicit = value;
if (value)
Debug.Assert(_polygons.Any(s => s.IsRefExplicit));
}
}
ScenePolygon:
public override bool IsRefExplicit
{
get { return _isRefExplicit; }
set
{
_isRefExplicit = value;
if (value)
{
Debug.Assert(!Model.IsRefExplicit);
Model.IsRefImplicit = true;
}
else
{
Debug.Assert(Model.IsRefImplicit);
Model.IsRefImplicit = false;
}
}
}
public override bool IsRefImplicit
{
get { return _isRefImplicit; }
set
{
_isRefImplicit = value;
Debug.Assert(Model.IsRefExplicit);
}
}
That's all that really happens, LINQ on a dozen items instead of many more (300 models querying each 7000 polygons X). I still have to test it to see how well it performs but it should perform much faster, to be continued.