Updating Progress Label During Long Calculation in Start

I have a loop and a 10-second pause for calculations in Start.
I run it inside WaitForRenderAndCall.

I also have a label to display the progress percentage. But since the Update event is not called, I tried to update the display using LabelPercent.Render;, but it didn’t work.

What is the solution for this?

Additionally, on Android this causes an “Application Not Responding” (ANR) warning.

We used to have a way to display a loading screen while loading resources, but @michalis removed it. This thread’s discussion may interest you Show a loading progress using external dll - #5 by michalis

If the calculation is not related to loading resources from disk, then you should move it to Update.

Thank you for your answer. I found that topic while searching, but using a DLL is not possible on multiple platforms.

In Windows, there is Application.ProcessMessages for this purpose, or the option to run the process in a separate thread. However, this is almost not possible in CGE because the UI is updated in Update.

Executing a long-running code during loading will indeed cause ANR or similar messages. From the OS perspective, your application does not respond to the messages.

We have indeed removed old approach that was calling Application.ProcessMessages periodically during loading. There’s nothing in CGE making it impossible, it was and is possible with our UI… but only on certain platforms, like Windows or other desktops, where we “control the event loop”. It cannot work on platforms like Nintendo Switch, web, or iOS – where this is not possible, we cannot control event loop there, we must obey the “external event loop”: something external tells us to update+redraw.

( It was also somewhat hard to maintain – as necessarily we were making redraws in half-finished state. But I would probably try to keep it, if it was cross-platform. )

Looking at others, Unity also doesn’t have anything like Application.ProcessMessages. Your application only reacts to events (like update, redraw).

So the ultimate solution is as shown in our zombie_fighter. This forces you to split loading code into multiple small steps (and puts responsibility on your side to make these steps “small enough”) and allows to make redraws as the steps progress.

I know, it requires some manual work to make it really work. You cannot just drop a complicated design with scenes, and tick a checkbox “load it with a nice progress bar” :slight_smile: Which would be nice! But it’s just not something we can do right now. I hope to improve it on the engine side, but it’s a more long-term task which we have to figure out.

We will also have asynchronous loading which solve the problem of “making these steps small enough” that I mentioned above. With async loading, your application can remain responsive even when doing longer task.

Here is a clear English version of what you wrote:

I have enough knowledge about Application.ProcessMessages and I know it cannot be a cross-platform solution.

Splitting the code into smaller pieces does not help when we are inside a loop.

One possible approach is to execute the loading logic in Update, meaning that one iteration of the loop runs on each update call. This may sound simple and logical, but in practice it creates problems and additional complexity during loading.

The solution used in the zombie_fighter example is not really practical and seems to be more of a cosmetic demonstration.

When I try to load using AnonymousThread and update the UI using Synchronize, there are still problems. At startup I get this error:

Project CastleWorld_standalone.exe raised exception class EAssertionFailed with message 'Assertion failure (C:\Users\hamid\AppData\Local\Programs\Castle Game Engine\src\files\castledownload_asynchronous.inc, line 563)'.

Also, since Synchronize is used and the UI update is probably delayed, the loading process becomes much slower.

Indeed it is not cross-platform, that is correct. We provide Application.ProcessMessage on platforms where it can work – desktops (Windows, Linux, FreeBSD, macOS), and Android. On others (Nintendo Switch, web, iOS) there’s no way to provide this.

If you load inside a loop, you indeed need to reorganize the code, to not have a loop inside a routine, but to track the loop status in a view, execute one loop step, and then do WaitForRenderAndCall.

I agree it increases complexity to reorganize code to do this.

We simply don’t have now an easier solution, to just load an existing big design (like a viewport with multiple scenes) and show a progress bar along the way. The asynchronous loading will be one piece of a puzzle.

I would need to see a testcase to debug what goes wrong. The exception says that TCastleDownload landed in a state it never should have been.

If you access the CGE code from a single thread (which it seems you do) things should work correctly. Please submit a testcase to reproduce the problem, and I’ll take a look.

1 Like

In my game I pretty much do it the same way as in the demo zombie_fighter (well since my scripting language supports coroutines, writing such load code is pretty natural and straightforward).

3 Likes

I moved this part out of the thread:

CapsuleCollider := TCastleCapsuleCollider.Create(FreeAtStop);
Player.AddBehavior(CapsuleCollider);

Body := TCastleRigidBody.Create(FreeAtStop);
Body.Dynamic := false;
Player.AddBehavior(Body);

Player.Collides := True;
Player.PreciseCollisions := True;

For now, it does not produce an error.

Maybe Player.AddBehavior(Body); should be inside Synchronize. Since it did not cause any visible change in the scene, I had not synchronized it before.

Edit :
It occasionally gives this error.

Update:
On Android, the ANR warning was still being displayed.
I changed Synchronize to Queue. The ANR warning is no longer shown, but the label value is not updating.

I’m afraid I’m not clear what do you do, and what of it is in a thread, and how do you use Synchronize. Please submit a testcase (complete code + data) to allow us to investigate, otherwise there’s too much guessing where things can go wrong :slight_smile:

To be clear: all the usage of CGE API must happen from the same, single thread. As documented on Threads | Manual | Castle Game Engine . Our API is not thread-safe (which is similar to other game engines and Pascal libraries too :slight_smile: ). Don’t try executing CGE API from random threads and looking whether it will work – due to how threads work, some incorrect code may sometimes randomly work, randomly crash. The only thing that is guaranteed to work is when you execute all CGE API from one, single thread, which is the thread where your code is during Application.OnInitialize, views methods etc.

Engine internally may employ threads to do other things (like downloads, streaming music, and in the future the async loading of assets mentioned here). On Android there are already 2 threads, one for communication with Java. None of this should be visible to you, and it’s platform-dependent when threads are used.

To be clear, you can use threads for your own work, of course. Just make sure to synchronize all the work such that all CGE API calls are within our thread. From your post, I can see you do such synchronization, but I’m not sure how do you do it / whether it is correct :slight_smile: Again, a testcase showing your code would make it clear.

1 Like

I will try to create a small program that simulates my application.

I created a sample program. In the sample program, the error does not occur.

It works well with Delphi, and the progress percentage updates correctly, but this is not the case with FPC.

I am trying to figure out why the example does not produce an error and what the difference is compared to the main program. (Of course, the main program is more complex and performs more tasks.)

1 Like

@michalis
This example loads correctly with Delphi and does not produce any errors.
However, when it is compiled with FPC for any platform, it does not work.

*In the main program, it occasionally encounters an error, but I cannot debug it at all, and the call stack also does not show which part of my code caused it.
I need to make the example more similar to the main program.

CopyAnimation.zip (1.2 MB)

Thank you – we had a bug in the engine that made TThread.Synchronize do nothing when used with CastleWindow (not LCL) with FPC.

Fixed now :slight_smile: in this commit. As usual, the engine download will soon contain the fixed version, once the new engine passes a few automated tests. You can observe this page, when it will no longer contain a commit titled “Make TThread.Synchronize work in FPC applications using CastleWindow…” then the fix is part of download.

So this makes your example work the same with FPC and Delphi.

Update: A subsequent commit fixes FPC+Android which required one thing to be correct. So please wait until also the commit titled “Set MainThreadID on Android…” is part of the downloads, to have it working flawlessly on FPC+Android too.

That said:

Your application calls CGE API from non-main thread. The application you send is already not guaranteed to work.

Please heed the warning I mentioned a few posts above and emphasized on threads manual page:

The application you submitted does use CGE API from non-main thread:

  • you iterate over X3D graph,
  • you do Target.RootNode.AddRoute,
  • you setup TCastleCapsuleCollider

← you do these things in TViewMain.DoLoad and CopyAnimationOnly which are not in the main thread.

I understand what you’re trying to solve (speedup animation copying by threads) but this way is really not reliable. It may work by accident (it seems it does now, at least in the simple application you submitted, but evidently your larger application has random errors). Our API is not thread-safe (and it’s the same as LCL, VCL, or other game engines).

If you want to speedup animation → this can likely be done by just making that code faster, even in single thread. I see your testcase in Sharing Animations Between Multiple Characters With the Same Skeleton - #19 by michalis – thank you, I have it on TODO to investigate and propose a speedup.

1 Like

Thank you, @michalis .

Yes, I wanted to use threads to make the animation copying faster and also keep part of the loading process happening while the loading screen is shown. I thought that since nothing was being rendered on the screen, it would not cause any problems.

For now, I will wait until the updated version is released.

Thank you for your help and guidance.

1 Like

@michalis seems that this commit broke the resulting app on android.

bc yesterday I was running everything as usual (I’m using GHA, which relies on latest master obviously), today I observe

Exception: EThread

CheckSynchronyze was called from non-main thread

this is the text of exception I see (and typed by watching it multiple times) for a fraction of a second, after that app crashes on android

This indicates you happened to have engine version

Indeed the result will be Android application crashing like you show. I see that there was a period when our GHA build first commit, but not yet the latter (also resulting Docker contained such broken version). Everything should be good now → so just upgrade and you should see everything working :slight_smile:

1 Like

Yes, Thanks! I Can confirm. without any changes from my side, rebuilt apk started to work, so it definitely was an accidental fall inbetween of different castle code changes. Sorry for “panicking” :sweat_smile:

1 Like