Preloading assets without the busy cursor icon

Blitz3D Forums/Blitz3D Programming/Preloading assets without the busy cursor icon

videz(Posted 2015) [#1]
The busy icon looks really awkward when loading some medium to heavy assets for a game. I was wondering if there is a way or workaround where you could preload assets with full control (progress bars, etc) and make it look like those in professional builds without having that busy cursor spinning around and make it seem more like to crash on any moment..

Could this be just easy as using threads in Blitz (fast pointers, external libs, etc.. if available), hacking the cursor icon and change it to the default?

Any ideas??..


Matty(Posted 2015) [#2]
Unfortunately no muktithreading im blitz. However. .. it is still possible to have a progress bar by breaking your load routine up with statements to update the screen between each file that is loaded.


Rroff(Posted 2015) [#3]
Can use fastpointer to thread stuff but... loading stuff in one thread and using it in another is a bit... complicated. At the very least you really need custom loading and hand off functions for model, texture, etc. loading and probably a 3rd party sound API, etc.


videz(Posted 2015) [#4]
@Matty,

Have you done something similar and how did other's got this to work?

@Rroff

Yep I see it is a possibility with fastpointer and I don't mind getting my hands dirty when it becomes complicated. Maybe set all asset names to globals or something. I might use default sound fmod and not use threading with ogg files only.


I see that the opensource Stranded 2 uses preload screen but looking at their models and assets, it's not much of a burden to get that windows cursor panic and switch to busy state.

There's also Kryzchan's BlitzTiles to get some idea..


Rroff(Posted 2015) [#5]
Its a lot more complicated than global defining assets as you have to make sure they are locked to the render thread, etc. if being manipulated by another thread and all the rest of that thread semaphoring fun.


videz(Posted 2015) [#6]
Ok given that, maybe it comes back to not using threads. I've seen a lot of Blitz3D/Max games using it to some extent (BattleTanks 2, etc)

So what's the routine how can this be done? Is there a code archive or posts somewhere which demonstrates a basic idea?

I googled in "blitzbasic preloading" search and I don't see anything related.

Is this feature still a mystery here?

thank you.


videz(Posted 2015) [#7]
Ok maybe the other way around.

The main loop loads all the asset one by one, then a separate thread for the loading screen or counter update.


Matty(Posted 2015) [#8]
Yes....(I have done something similar)


Usually in a game you are loading a number of files from the user's hard drive (or elsewhere) and most likely you will keep a list of what files need to be loaded before loading them....

Often I design my game programs with the following structure (loosely)

Initialise - ie set up windows/main render loop etc
Load stuff
Play Game
Unload Stuff

during the 'loadstuff' stage you will most likely be reading files in one by one...

Override the standard loading functions (like loadimage etc) by writing your own load function...

eg instead of loadimage(filename$) write:

function myloadimage(filename$)

;in here call the regular loadimage command but before you do call another function, and at the end call another function ...eg like so:

updatestatusbar("Opening file:"+filename)
myimage = loadimage(filename$)
if(myimage=0) then 
     updatestatusbar("Error opening file:"+filename)
     ;;perform any necessary error handling stuff if the file didn't load
    return 0
endif
updatestatusbar("Completed opening file:"+filename)
return myimage

end function 

;and your function for updatestatusbar would look like this:
function updatestatusbar(statustext$)

;draw status text in here (perhaps have a globally accessible loading image which gets displayed called global_loadingimage

cls
drawimage global_loadingimage,0,0
text 0,0,statustext
flip


end function 




Similar processed could be put in place for sounds, 3d models, everything really...

I've just written that in my lunch break - took about 5 minutes...but it would look something like that in a model without threads....


Now with a multithreaded model it is not that hard - if it is done right.
I don't know about blitz but in java what I would do (and what I currently do) is this:

Have a boolean flag indicating whether or not the "loading" thread is currently running.
Initially when the app/program boots up it will be set to false. Upon entering the load routine the first line checks if the boolean variable is true or not. If it is true then skip out of the method....otherwise continue and set the boolean to true and then immediately spawn the new thread. Inside the new thread do all your work. Then at the last statement in the thread (or if it exits early) set the boolean back to false.

Keep your main thread for UI stuff, and only use threads for anything which has to run in the background.


videz(Posted 2015) [#9]
Thanks Matt for your ideas here.


I don't know about blitz but in java what I would do (and what I currently do) is this..



Yes in other languages or engines there are lots of ways to do this, even supports it, I have done this already as well. I'm asking it when using Blitz products, at least B3D/BP+, now with external libs offering multithreading and pointers.


Now with a multithreaded model it is not that hard - if it is done right.


ok sounds good, at least I got some opinion that this is plausible without modifying source or sth.


Noted on the gui main render loop.. yes I was thinking of a counter variable or just a splash screen just to show the game is loading in progress and doing something.


Rroff(Posted 2015) [#10]
Problem is the non threaded nature of DX7 - if your mid way loading a graphic asset on the main (native) thread and try to do a render update on another thread it could cause all kinds of issues.

You can definitely use it for loading other stuff though while rendering a loading screen.


videz(Posted 2015) [#11]
Hey Rroff, can you provide a small demo code or snippet on how you think this could work?


Rroff(Posted 2015) [#12]
Hmm if you don't call renderworld, etc. I guess you could do 2D flipping while loading meshes, textures, etc. on another thread, never tried it though.

(Most of my thought on this has been from the angle of streaming assets in during playing the game).


videz(Posted 2015) [#13]
Ok I guess that needs to be tested. I'm up for any solution, threaded or not, that at least will not show the windows busy cursor.


xlsior(Posted 2015) [#14]
Ok I guess that needs to be tested. I'm up for any solution, threaded or not, that at least will not show the windows busy cursor.


HidePointer ;-)


videz(Posted 2015) [#15]
Thanks xlsior. That's really a quick solution but I still like having the default cursor show up just to inform users that the game is not hanging.

I guess that will be the fail safe solution for now.


xlsior(Posted 2015) [#16]
Thanks xlsior. That's really a quick solution but I still like having the default cursor show up just to inform users that the game is not hanging.


Draw a progressbar or text statement tot he screen indicating progress, update after each individual asset gets loaded?


Yasha(Posted 2015) [#17]
Hmm if you don't call renderworld, etc. I guess you could do 2D flipping while loading meshes, textures, etc. on another thread, never tried it though.


For anyone reading this still thinking about threads, it's worth mentioning that the entire BlitzBasic runtime is non-thread-safe. Strings and type instances and all manner of other things have hidden reference counts and other forms of global state that absolutely will become corrupted the second you try to do anything clever with this. Any function that uses any assets other than pure integers and floats is unsafe.


Rroff(Posted 2015) [#18]
^^ Indeed hence my post above - however if you hand off globals to a thread to load assets and don't touch those variables until the thread has exited (would need to subsequently check those variables to make sure they had validly loaded something) and are careful what you do while the thread is in flight then it should all work ok.

Agreed though unless you have carnal knowledge of exactly what is going on under the hood in that section of code while the thread(s) are active its very possible to have unexpected things happen usually = crash.


videz(Posted 2015) [#19]
Thanks Yasha for your input. While it is enlightening to get informed with all these details. At this time, I'd be more interested in something to tell 'what to do' and 'how to do it' rather than seeing 'what not to do'.

Main reason why I posted this question here and maybe others who have the same problem can also get something from it. With this kind of problem and feature that has been used on so many games esp. PC, I thought this has been done already by someone here (code arcs etc) and given the language/product is over ten years old.


As for fast pointers, I remember seeing one example there where the render routine (turning cube) is happening on the thread loop and not the main loop itself.

If hiding the mouse pointer is all I can get then I guess I'll settle for this solution with blitz..


Yasha(Posted 2015) [#20]
Sorry, I didn't read the thread in depth and thought you were now looking on a different track.

Anyway, looking into it some more, we can refine the above restriction. You can use local integers, local floats, and any other asset command that you know is thread safe. i.e. have looked at the source to confirm that it is safe rather than assume safe until proven otherwise.

I haven't gone over it in the detail that one should to make that assessment, but I'm pretty sure 2D image blitting is semi-safe (i.e. no risk as long as only one thread is actually trying to do it). UDP messaging also looks safe.

Assuming the messaging system itself is safe, you can communicate between threads using networking the same way you'd otherwise communicate between separate processes on separate machines: the host OS takes care of synchronising the message for you (ridiculous overkill, but it is safe, unlike anything involving globals).

Hence:




videz(Posted 2015) [#21]
awesome this is great Yasha! Thanks for shedding new light on this. Yes, I haven't look at the thread safe part in source but that is good to know. :-)

great insight, as always! :) will try this example..


videz(Posted 2015) [#22]
After testing the code, I'm still getting a busy cursor and it always crashes after a few seconds.

Any ideas on this Yasha?


Yasha(Posted 2015) [#23]
Apart from a missing Flip at the end (added), the above runs correctly on two different machines, in both Debug and non-Debug, and even under Wine. Sorry, I got nothin'. I hope the underlying idea is useful even if the example is not.

The busy cursor might just be to do with lack of Delay pauses in the loops (don't know, don't see it here).


Kryzon(Posted 2015) [#24]
There are ways to asynchronously read data from a file, you can use the Win32 API for that.

The problem is that Blitz3D does not offer any official method of loading an asset from a memory buffer, which is the result of reading a file asynchronously. All of the Load[something] functions take in file paths as parameters and load from the hard disk.

Also, there isn't a way to add bones to a mesh procedurally. The API for that isn't exposed for some reason, so you can't do this with skinned meshes for example.

You can use BlitzMax and MiniB3D for that, with minor adjustments to read from a memory buffer instead of a file.
Like you said, Blitz3D is over ten years old. If the reason that you're using Blitz3D is that it's free, there are far more modern free and open source game engine alternatives that have asynchronous asset loading.


RemiD(Posted 2015) [#25]

Also, there isn't a way to add bones to a mesh procedurally. The API for that isn't exposed for some reason, so you can't do this with skinned meshes for example.


What about this :
http://www.blitzbasic.com/codearcs/codearcs.php?code=2485

I have never tried it because i don't need it, but it may be useful.


Kryzon(Posted 2015) [#26]
I see the need to hack into the engine to add features that should've been standard as a sign that I need to use some other engine that is better suited to my needs.


Blitzplotter(Posted 2015) [#27]
How about running two separate Blitz3D executables. The main executable could write to a directory with a progress status - this could be simply a number, or potentially writing a numerous small text file(s) to said directory. The separate progress B3D executable could present a progress bar that would be dependent upon potentially how many files are present in said directory, or the number contained within said directory. Might need a semaphore to prevent both executables trying to access a single file at the same time.


Blitzplotter(Posted 2015) [#28]
This is some loading progress stuff - you could interleave writing out to a .dat file within the start of your main. Might not be what your looking for, but was something I'd been toying with myself for a while.

You'd need to interleave the loading of the 'big' assets with a write to a progress.dat file with an Integer of your choosing to depict what percentage has been loaded.









RemiD(Posted 2015) [#29]
Why not use hidepointer() and draw an image of the os cursor at mousex() mousey() ?

To capture the image of the os cursor, with windows :
go to control panel>mouse>pointer and do a screen capture (print key) of the cursor, then paste this cursor in your favorite image editing program, then make sure the top left of the image is the edge of the cursor, then color all pixels which must be invisible, in black (0,0,0) then save this image as bmp or png.
Then load this image and the black pixels should be invisible and you will be able to draw the cursor at the mouse position.

Oh i have just noticed that it may not work if you are loading many/big files during a long time...
To prevent this you could load the files progressively and update the screen regularly (at least 1fps) (=stop to load files for this frame if the looptime is superior to 1000ms)


Blitzplotter(Posted 2015) [#30]
RemiD, that seems a little convoluted. I admit, I've read your post a few times - and it doesn't seem immediatly clear what your driving at.

Sorry, I suppose I should investigate the hidepointer() thingy... ;)


RemiD(Posted 2015) [#31]
@blitzplotter>>Basically, what i meant was that if the "wait cursor" of Windows appears, it is probably after the program has been "unresponsive" after xs or xms, so in order to prevent that, the idea is to load components, a few at a time, and update the graphics window regularly.
I have not idea after how many s or ms Windows considers that a program becomes "unresponsive" so my suggestion is to continue to load components if the loop time is inferior to 1000ms (just an example, it can be more ms), then to update the graphics window, then to continue to load others components.

Here is an example :


hidepointer() may not be necessary in this case.

(when ProgramState = CUpdate, press the escape key to exit the program)


RemiD(Posted 2015) [#32]
Another example :


(when ProgramState = CUpdate, press the escape key to exit the program)


Blitzplotter(Posted 2015) [#33]
Ah, thanks for the additional clarification, appreciated.