[Solved] PhysX safe Async operations

In terrain generation tutorial, there is an async actor creation example: HOWTO: Create terrain from code | Flax Documentation

It runs without warning as it is, but just increasing patch counts from (2,2) to higher number creates physx errors in log window. I enabled Support Cooking At Runtime in options FYI.

I increased it to (16, 16) and met these thread collision errors:

[ 17:49:59.271 ]: [Error] PhysX Error! Code: 8.
PxShape::setSimulationFilterData() not allowed while simulation is running. Call will be ignored.
Source: F:\FlaxEngineDotnet\Cache\Intermediate\Deps\PhysX\physx\source\physx\src\NpShape.cpp : 364.
[ 17:49:59.505 ]: [Error] PhysX Error! Code: 8.
PxShape::setSimulationFilterData() not allowed while simulation is running. Call will be ignored.
Source: F:\FlaxEngineDotnet\Cache\Intermediate\Deps\PhysX\physx\source\physx\src\NpShape.cpp : 364.
[ 17:49:59.505 ]: [Error] PhysX Error! Code: 8.
PxShape::setQueryFilterData() not allowed while simulation is running. Call will be ignored.
Source: F:\FlaxEngineDotnet\Cache\Intermediate\Deps\PhysX\physx\source\physx\src\NpShape.cpp : 385.
[ 17:49:59.954 ]: [Error] PhysX Error! Code: 8.
PxShape::setSimulationFilterData() not allowed while simulation is running. Call will be ignored.
Source: F:\FlaxEngineDotnet\Cache\Intermediate\Deps\PhysX\physx\source\physx\src\NpShape.cpp : 364.
[ 17:50:00.004 ]: [Error] PhysX Error! Code: 8.
PxShape::setSimulationFilterData() not allowed while simulation is running. Call will be ignored.
Source: F:\FlaxEngineDotnet\Cache\Intermediate\Deps\PhysX\physx\source\physx\src\NpShape.cpp : 364.
[ 17:50:00.121 ]: [Error] PhysX Error! Code: 8.
PxShape::setSimulationFilterData() not allowed while simulation is running. Call will be ignored.
Source: F:\FlaxEngineDotnet\Cache\Intermediate\Deps\PhysX\physx\source\physx\src\NpShape.cpp : 364.
[ 17:50:00.321 ]: [Error] PhysX Error! Code: 8.
PxShape::setSimulationFilterData() not allowed while simulation is running. Call will be ignored.
Source: F:\FlaxEngineDotnet\Cache\Intermediate\Deps\PhysX\physx\source\physx\src\NpShape.cpp : 364.
[ 17:50:00.538 ]: [Error] PhysX Error! Code: 8.
PxShape::setSimulationFilterData() not allowed while simulation is running. Call will be ignored.
Source: F:\FlaxEngineDotnet\Cache\Intermediate\Deps\PhysX\physx\source\physx\src\NpShape.cpp : 364.
[ 17:50:00.538 ]: [Error] PhysX Error! Code: 8.
PxShape::setQueryFilterData() not allowed while simulation is running. Call will be ignored.
Source: F:\FlaxEngineDotnet\Cache\Intermediate\Deps\PhysX\physx\source\physx\src\NpShape.cpp : 385.
[ 17:50:00.671 ]: [Error] PhysX Error! Code: 8.
PxScene::addActor() not allowed while simulation is running. Call will be ignored.
Source: F:\FlaxEngineDotnet\Cache\Intermediate\Deps\PhysX\physx\source\physx\src\NpScene.cpp : 476.
[ 17:50:00.788 ]: [Error] PhysX Error! Code: 8.
PxShape::setSimulationFilterData() not allowed while simulation is running. Call will be ignored.
Source: F:\FlaxEngineDotnet\Cache\Intermediate\Deps\PhysX\physx\source\physx\src\NpShape.cpp : 364.
[ 17:50:00.788 ]: [Error] PhysX Error! Code: 8.
PxShape::setQueryFilterData() not allowed while simulation is running. Call will be ignored.
Source: F:\FlaxEngineDotnet\Cache\Intermediate\Deps\PhysX\physx\source\physx\src\NpShape.cpp : 385.

It would be nice to know the official way to ‘work properly with physics object asynchronously’ if exists.

You need to remove actor from scene when it’s being edited prevent such issues. Simpyl set Parent = null and then add it back.

Current :

    public override void OnStart()
    {
        // Create new dynamic terrain actor and add it to the scene
        _terrain = new Terrain();
        _terrain.HideFlags = HideFlags.DontSave;
        _terrain.Name = "My Terrain";
        _terrain.Setup();
        _terrain.Parent = Actor;

        // Generate terrain using the async task to prevent game stalls
        // You can also use C# thread to perform this work
        // Your game can display progress bar or loading screen while terrain is being generated
        Task.Run(new Action(GenerateTerrain));
    }

To : [Edit : this code is incorrect. Flax does not let actor modification outside of main thread]

    public override void OnStart()
    {
        // Create new dynamic terrain actor and add it to the scene
        _terrain = new Terrain();
        _terrain.HideFlags = HideFlags.DontSave;
        _terrain.Name = "My Terrain";
        _terrain.Setup();
        _terrain.Parent = null;

        // Generate terrain using the async task to prevent game stalls
        // You can also use C# thread to perform this work
        // Your game can display progress bar or loading screen while terrain is being generated
        Task.Run(new Action(GenerateTerrain)).ContinueWith(task => _terrain.Parent = Actor);
    }

Kind of?
I’ll try soon!

I tested with the following code(still with the default template), I could see the terrain appears to the scene after it fully created.
(ContinueWith creates a task so it is not guaranteed to be in mainthread, so I ditched it by the kind error message)

Task t;

public override void OnStart()
{
    // Create new dynamic terrain actor and add it to the scene
    _terrain = new Terrain();
    _terrain.HideFlags = HideFlags.DontSave;
    _terrain.Name = "My Terrain";
    _terrain.Parent = null;
    _terrain.Setup();
    //_terrain.Parent = Actor;

    t = Task.Run(new Action(GenerateTerrain));
}


public override void OnUpdate()
{
    if (t.IsCompleted)
    {
        _terrain.Parent = Actor;
    }
}

But it still creates same PhysX error while in midst time of creation.

[ 00:26:06.199 ]: [Error] PhysX Error! Code: 8.
PxShape::setSimulationFilterData() not allowed while simulation is running. Call will be ignored.
Source: F:\FlaxEngineDotnet\Cache\Intermediate\Deps\PhysX\physx\source\physx\src\NpShape.cpp : 364.

And aside of task completion check and parent assignment, terrain generation with Parent = null itself gives me PhysX error when enoughly large patch arrays applied. It means removing terrain completion and leaving it unparented forever makes the same result, so somehow Parent = null is insufficient for solving the physics problem.

Am I missing right way to set de-parenting the actor? Any help could be thankful.

[Edited]
Log says error occured from setSimulationFilterData() / PxShape::setQueryFilterData in PhysX engine call,

[ 00:12:32.597 ]: [Error] PhysX Error! Code: 8.
PxShape::setSimulationFilterData() not allowed while simulation is running. Call will be ignored.
Source: F:\FlaxEngineDotnet\Cache\Intermediate\Deps\PhysX\physx\source\physx\src\NpShape.cpp : 364.
[ 00:12:33.797 ]: [Error] PhysX Error! Code: 8.
PxShape::setQueryFilterData() not allowed while simulation is running. Call will be ignored.
Source: F:\FlaxEngineDotnet\Cache\Intermediate\Deps\PhysX\physx\source\physx\src\NpShape.cpp : 385.

And that call is might be from UpdateLayerBits() in Terrain class (which calls PhysicsBackend::SetShapeFilterMask(), using both physX functions).

void Terrain::UpdateLayerBits()
{

// Update the shapes layer bits
for (int32 pathIndex = 0; pathIndex < _patches.Count(); pathIndex++)
{
    const auto patch = _patches[pathIndex];
    if (patch->HasCollision())
    {
        PhysicsBackend::SetShapeFilterMask(patch->_physicsShape, mask0, mask1);
    }
}

}

UpdateLayerBits() is called directly from AddPatches().

void Terrain::AddPatches(const Int2& numberOfPatches)
{

if (IsDuringPlay())
{
    ...
    UpdateLayerBits();
}

}

Physics access might not be visible right from the surface, but just from the call tree itself there is not much relation between render graph tree (parent = something problem) assignment and PxShape::setSimulationFilterData() function call.

I’m in curious…

Could you remove _terrain.Parent = null; and link actor to level after terrain generation i nasync to be invoked in a main thread via: Scripting.InvokeOnUpdate(() => _terrain.Parent = Actor); ? Maybe it helps.

Is this means queueing invoke at the end of the generation task? I’ll try!

Unfortunately the error did not go away.
This is the base code modified with your hint.

private Terrain _terrain;

[Limit(1, 64), Tooltip("Terrain patches count (in each direction on XZ plane).")]
public Int2 PatchesCount = new Int2(16, 16);

public override void OnStart()
{
    // Create new dynamic terrain actor and add it to the scene
    _terrain = new Terrain();
    _terrain.HideFlags = HideFlags.DontSave;
    _terrain.Name = "My Terrain";
    _terrain.Setup();

    // Generate terrain using the async task to prevent game stalls
    // You can also use C# thread to perform this work
    // Your game can display progress bar or loading screen while terrain is being generated
    Task.Run(new Action(GenerateTerrain));
}

private void GenerateTerrain()
{
    var chunkSize = _terrain.ChunkSize;
    var heightMapSize = chunkSize * FlaxEngine.Terrain.PatchEdgeChunksCount + 1;
    var heightMapLength = heightMapSize * heightMapSize;
    var heightmap = new float[heightMapLength];

    _terrain.AddPatches(ref PatchesCount);

    for (int patchZ = 0; patchZ < PatchesCount.Y; patchZ++)
    {
        for (int patchX = 0; patchX < PatchesCount.X; patchX++)
        {
            // Generate heightmap (simple sine wave for example showcase)
            for (int z = 0; z < heightMapSize; z++)
            {
                for (int x = 0; x < heightMapSize; x++)
                {
                    heightmap[z * heightMapSize + x] = Mathf.Sin((float)x / chunkSize * Mathf.PiOverFour * 3.0f) * 3000.0f;
                }
            }

            // Initialize patch (virtual)
            var patchCoord = new Int2(patchX, patchZ);
            //_terrain.AddPatch(ref patchCoord);
            _terrain.SetupPatchHeightMap(ref patchCoord, heightmap, null, true);
        }
    }
    Scripting.InvokeOnUpdate(() => _terrain.Parent = Actor);
}

At first I thought problem was solved but it was my mistake while editing the source code.
With calling GenerateTerrain() by Task the issue is still there.

Putting everything aside,

  1. Remove _terrain.Parent = Actor; line
  2. Do not add InvokeOnUpdate() (Do not touch scene tree at all, from start to end)
  3. Just try to make a bit large terrain

Results PhysX error.

And in effort of isolating the error:

  1. _terrain.AddPatch() seems not the major reason of error
  2. delay inside task seems not affect the error
  3. Existance of _terrain.SetupPatchHeightMap() highly affects the occurance of error
    → But this is also the bottleneck of CPU calculation hang - cannot move to the main thread

Fixed in Fix creating virtual terrain collision of actor that is not in a game · FlaxEngine/FlaxEngine@db8adf7 · GitHub

1 Like

Expecting for the next release!

With version 1.8.2 async creation does not makes error, thanks!