Force OpenFL stage to render during synchronous processing

I have a game made of minigames, so if I understand things right, I can’t use a standard preloader if I want to delay asset loading of each minigame until a minigame is selected.

It happens to be HaxeFlixel 4.1.0 but even their preloader has to use OpenFL (er, NME?) directly, since it has to side-step the flixel stuff built on top of OpenFL to work, so I figured I would ask here from the beginning.

One minigame has > 100 MB of assets, so even on native, it takes a few seconds to load them all. I added just a basic TextField to the OpenFL stage, and it’s visible no matter which flixel SubState I’m on.

The problem is, it goes from the default text to “100%”, without updating in between, while I’m loading the assets (i.e. putting them into animation objects and so on.) I’m calling my update function after each asset is loaded. How can I force it to redraw after each asset is loaded? I tried invalidate() but that doesn’t work, probably because my code doesn’t return control to the flixel loop to return it to openfl yet at that point.

Is there a synchronous way to force a redraw of my text indicator?

Thanks,
-GA

Normally, this would be a job for asynchronous loading, but it sounds like you’ve considered it and you need to do this synchronously.

My suggestion is to wait a frame after loading each asset (or after every 2-3 assets). This is the easiest and most future-proof way to make OpenFL redraw.

Thanks player_03, but if I am going to refactor my code to allow for that, I might as well go full async. (It’s more than a bit of a PITA in this particular context, because of how flixel animations work, and how I’m not loading from a sprite sheet because it won’t fit in memory.) Hence my question…there must be a way to force a draw, no? I just don’t know what it is because I’m used to flixel’s wrappers, right?

I assume so. OpenFL has to have a function (or multiple functions) for rendering, and as long as you call the same functions in the same order, it should have the same result.

Start with this one and see if it makes a difference.

Thanks, I think that might be what I’m looking for. I can’t seem to get it to work though. I added import lime.app.Application; to my indicator class (and haxelib lime to project.xml), then in the function of it that I call (showNewProgressImmediately(nowLoaded:Int)) I put this:

lime.app.Application.current.render(lime.app.Application.current.renderer);

(Also without the lime.app prefix, I get the same results.)

But trying to compile for windows/debug I get:

./src/minigames/general/LoadingIndicator.cpp(101): error C2653: 'lime': is not a class or namespace name
./src/minigames/general/LoadingIndicator.cpp(101): error C2065: 'Application': undeclared identifier
./src/minigames/general/LoadingIndicator.cpp(101): error C2146: syntax error: missing ';' before identifier 'tmp6'
./src/minigames/general/LoadingIndicator.cpp(101): error C2065: 'tmp6': undeclared identifier
./src/minigames/general/LoadingIndicator.cpp(101): error C3083: 'lime': the symbol to the left of a '::' must be a type
./src/minigames/general/LoadingIndicator.cpp(101): error C3083: 'app': the symbol to the left of a '::' must be a type
./src/minigames/general/LoadingIndicator.cpp(101): error C3083: 'Application_obj': the symbol to the left of a '::' must be a type
./src/minigames/general/LoadingIndicator.cpp(101): error C2039: 'current': is not a member of '`global namespace''
./src/minigames/general/LoadingIndicator.cpp(101): error C2065: 'current': undeclared identifier
./src/minigames/general/LoadingIndicator.cpp(103): error C2653: 'lime': is not a class or namespace name
./src/minigames/general/LoadingIndicator.cpp(103): error C2065: 'Application': undeclared identifier
./src/minigames/general/LoadingIndicator.cpp(103): error C2146: syntax error: missing ';' before identifier 'tmp7'
./src/minigames/general/LoadingIndicator.cpp(103): error C2065: 'tmp7': undeclared identifier
./src/minigames/general/LoadingIndicator.cpp(103): error C3083: 'lime': the symbol to the left of a '::' must be a type
./src/minigames/general/LoadingIndicator.cpp(103): error C3083: 'app': the symbol to the left of a '::' must be a type
./src/minigames/general/LoadingIndicator.cpp(103): error C3083: 'Application_obj': the symbol to the left of a '::' must be a type
./src/minigames/general/LoadingIndicator.cpp(103): error C2039: 'current': is not a member of '`global namespace''
./src/minigames/general/LoadingIndicator.cpp(103): error C2065: 'current': undeclared identifier
./src/minigames/general/LoadingIndicator.cpp(105): error C2653: 'lime': is not a class or namespace name
./src/minigames/general/LoadingIndicator.cpp(105): error C2065: 'Renderer': undeclared identifier
./src/minigames/general/LoadingIndicator.cpp(105): error C2146: syntax error: missing ';' before identifier 'tmp8'
./src/minigames/general/LoadingIndicator.cpp(105): error C2065: 'tmp8': undeclared identifier
./src/minigames/general/LoadingIndicator.cpp(105): error C2065: 'tmp7': undeclared identifier
./src/minigames/general/LoadingIndicator.cpp(105): error C2227: left of '->renderers' must point to class/struct/union/generic type
./src/minigames/general/LoadingIndicator.cpp(105): note: type is 'unknown-type'
./src/minigames/general/LoadingIndicator.cpp(105): error C2227: left of '->__GetItem' must point to class/struct/union/generic type
./src/minigames/general/LoadingIndicator.cpp(107): error C2065: 'tmp6': undeclared identifier
./src/minigames/general/LoadingIndicator.cpp(107): error C2227: left of '->render' must point to class/struct/union/generic type
./src/minigames/general/LoadingIndicator.cpp(107): note: type is 'unknown-type'
./src/minigames/general/LoadingIndicator.cpp(107): error C2065: 'tmp8': undeclared identifier

I would’ve expected this if I forgot to include lime in the project file. Anyway, I tried to compile for flash/debug instead, and I think the results were more illuminating:

export/flash/haxe/ApplicationMain.hx:19: characters 2-47 : flixel.system.FlxPreloader should be lime.app.Preloader
export/flash/haxe/ApplicationMain.hx:22: characters 2-19 : Main should be lime.app.Application

I guess that means you can’t use lime classes directly in a HaxeFlixel project, like you can with OpenFL classes? Or, you can use them, just don’t add lime to your project.xml. If I take it away from project.xml it at least compiles under flash, but the same compilation errors for windows.

But I had already tried to use OpenFL instead of lime here: import openfl.display.Application;…the result being that the Application class doesn’t have a field called current, only new in the dropdown, and it won’t compile if I ignore that and put it anyway. How do you get the current OpenFL application if you don’t already have it? Hmm…

Aha! openfl.Lib allows Lib.application.render(Lib.application.renderer);. It was actually one of the first things I had tried, but I was tricked by HaxeDevelop’s sometimes-but-not-always laggy autocomplete, which at first just shows .create() for Lib.application. After a few seconds you get .render() too! OK, but now I get (only on Windows…flash compiles OK):

source/minigames/general/LoadingIndicator.hx:32: characters 2-17 : Class<openfl._legacy.Lib> has no field application
source/minigames/general/LoadingIndicator.hx:32: characters 25-40 : Class<openfl._legacy.Lib> has no field application

Meanwhile, under flash/debug, the added line doesn’t seem to have any effect. It still jumps from default to 100%. In fact that’s the case for both of these methods:

        Lib.application.render(Lib.application.renderer);
        Application.current.render(Application.current.renderer);

It turns out, that’s only if you disable legacy mode or enable hybrid mode:

https://github.com/openfl/openfl/blob/master/openfl/_legacy/Lib.hx#L37

OpenFL doesn’t control the render process in Flash, so you’re out of luck there.

A quick search turned up this function, but even that doesn’t get around your problem.

There’s also updateAfterEvent(), but that’s only available for MouseEvent and TimerEvent.


Flash is not designed to do what you’re trying to do. Either wait a frame, or give up on compiling to Flash.

There’s a hybrid mode? I had missed that.

I don’t have legacy disabled, I’m not using next, and I don’t believe I unintentionally enabled hybrid mode. I think autocomplete doesn’t necessarily take into account compiler flags, right?

That’s an interesting snippet…when I pressed F4 on Lib.application I got openfl/Lib.hx with

package openfl; #if !macro #if !openfl_legacy


import lime.system.System;
import openfl.display.Application;
import openfl.display.MovieClip;
import openfl.display.Stage;
import openfl.net.URLRequest;

#if (js && html5)
import js.Browser;
#end


@:access(openfl.display.Stage) class Lib {
    
    
    public static var application:Application;

Good to know about Flash. That’s not my main target, so I’m actually only needing this on Windows at the moment, but I use Flash to debug (and in this case just to compare, to help get to the root of things.)

OK, I guess there’s no way around an ugly refactor. Thanks for your help.

Sometimes.

That’s the non-legacy version. Note the #if !openfl_legacy at the top. If you scroll all the way down, you’ll see what happens when openfl_legacy is set:

Sadly, the “go to declaration” command (F4) doesn’t understand any of this, and it always brings you to the non-legacy file.

Ah, this makes sense. Thanks for explaining.

I’m adding to this thread as I’m trying to get something very similar done.
Though not in flash, I’m targeting HTML5.

I have a haxeui-openfl gui program and for instance while I’m generating a big pdf document the gui isn’t updating.
As I understand it threads are currently not supported on HTML5. So I’m looking into a way to force the window to render in between my slow code.
Invalidating windows does not work, and the application.render no longer seems to exist.

Currently I’ve hacked in a way to call openfl.display.Stage.__onLimeRender and that does call the rendering code. But the display is not updated.
I figure it might be double buffered, and the buffers are not getting swapped, but I’m having trouble finding info on that.

Can anybody tell if I’m on the right track or if there’s an easier way to do this please? Cheers!