batocera-linux/batocera-emulationstation

Delayed-launch scripts in ES3 ?

Closed this issue · 10 comments

Hello,

There has probably been a change in the launch scenario for the ES (in ES3) game-selected and system-selected scripts, which are now launched once all the media have been loaded after a game/scrolling change.
This has the effect of creating an impression of "latency" in the execution of a script. When, in fact, it's just that the script is launched at the very end (and therefore about a second late now).
Is there an explanation for this? Can we put the scripts back in priority in the execution chain? ( I know that ES has changed a lot of things in its latest version )

Thank you for your work on ES

Yes it has changed, and No, it's not a good idea to revert this change.

Before v39, ES used to wait for the scripts to end before continuing which totally freeze the UI.
It's not a problem when a script takes 2ms, but it was changed because many scripts take very long time to finish, which created lot of lags in the navigation.
Now Es launches the script, but does not wait for the script to end anymore.
Also, scripts are launched in the same timing as before. If there's a "latency", it's probably related to the script that takes time to load / execute.
There is no plan to move back and wait the scripts to end again as it was very problematic.

I think there is some confusion in understanding my request.
Of course, I don't want ES to wait for the files to finish executing.
I thought that ES didn't wait for this anyway, and that it launched an independent run thread asynchronously, without waiting for a result. So I think this evolution is very good. Thank you for that.

On the other hand, I'd just like this asynchronous script launch to take place at the very beginning of the media update run, as I think was the case before. Because now, the script is launched at the end of media loading, I think.
I'm just guessing because I don't know exactly what's happening.
I just know that now I wait a second before the script launches, whereas before it launched almost as soon as the key was pressed.

It's not at all a question of the latency of the launched script I think, because a simple script is also launched after a second and was launched immediately in the previous version of ES. (tested with same script in the 2 ES versions)
I'm not criticizing the evolution of the launch, just the moment when it happens.
Thanks again.


I have a second hypothesis. The latest version of ES is really fast at displaying a game's media when you switch games.
In the previous version, after pressing a key, the view took just under a second to react.
After a press, I assume you loaded the media and then, once all loaded, launched the media transition animation. It may take that long, I think.
I guess now you pre-load the +1 and -1 media in advance (maybe more before and more after) to gain performance and that's why the new version is very fast because you launch the animation immediately.
It may be that the script is always launched at roughly the same moment in your run, but since the animation starts immediately, this creates a latency effect.
So I think you should launch the script just before the animation, if possible ? (Given that it's an asynchronous launch now, I don't think this would be a problem)


Of course, ideally, if you could push the event+parameters to a local url and a local port, that would be even better. No script launch. Or a socket to plug into to get the information.
Using a .exe is a real constraint when all you want to do is retrieve the event, the system and/or the current navigation
(game/system/collection) at time t. And when dozens of process of the same exe are launched in a flurry during fast scrolling, it makes no sense.

As I said in my previous answer : Script are launched at the exact same timing as before.
The only difference is that we don't wait for the scripts to return anymore.

As you said : " In the previous version, after pressing a key, the view took just under a second to react."
That's the exact reason why we changed this. Your script took about 1 second to execute. Creating huge lags in the UI.

Now, as ES does not wait anymore for the script to end, and as your script take 1 second to load and execute its code...
What happens is normal, and is 100% related to your script, not to emulationstation.

As you're talking about an exe, I thing you are using Windows/Retrobat and it's very probable you compiled your exe with Python or something.
I don't know why, but scripts compiled as executables with Python generally take about 1 second to load.

Well, rewrite your scripts with another technology. You have many choices : batch files, powershell scripts, or some other technology ( C++ / C# ) which load very fast.
Trust me : you'll see the difference.

About the "if you could push the event+parameters to a local url and a local port" I agree, it could be an interesting idea, but it would require a lot of work - an would impose strict declaration for the service ( http/rest methods.. ) and all this is quite useless :
You can write simple .bat or .ps1 scripts that performs all the necessary calls ( see Invoke-WebRequest ).

I get the same result with a batch file. It has nothing to do with my exe.
The latest version of ES is really fast at displaying a game's media when you switch games.

In the previous version, after pressing a key :

  • the view took just a second to react. The system loaded the media and then, once all loaded, launched the media transition animation. It may take that long, I think.

In the current version :

  • the system media force to gain performance and that's why the new version is very fast because it launch the animation immediately.

It may be that the script is always launched at roughly the same moment in your run, but since the animation starts immediately, this creates a latency effect when scripts in scripts folders are launched.
I suggest the system launch the scripts in folder "scripts/*" just before the animation (Given that it's an asynchronous launch now, I don't think this would be a problem)

I just ran tests with a batch launching a c# executable. The time elapsed between the script queuing and the execution is almost instant :

12:40:20.253 DEBUG fireEvent: system-selected model3
12:40:20.279 DEBUG executed: H:/[Emulz]/emulationstation/.emulationstation/scripts/automarquee.bat system-selected "model3"

It took 26 millseconds to launch the bat file !

Then the batch file launched a c# executable :

C# executable side :
2024-03-09 12:40:20.388 [DEBUG] marquee system-selected model3

So it took about 110 milliseconds for the c# executable to load/initialize and run.
It would probably be faster without the .bat intermediate file, and a c++ executable.

A latency of 130 milliseconds is totally acceptable.

I'll add that, the PC I used to test this is from 2008 ( with my scripts & executable on an old mecanical HDD !!!! )

If you have latency problems, don't expect anything on EmulationStation's side :
We can't launch earlier, and we won't revert back the fact that we don't wait scripts to end anymore.

Anyway : I don't really see why it's a problem ? Unless you're modifying medias ( which would be a bad thing )

I don't think we understand each other.
I'm not talking about launch latency, because it's very fast indeed.
I'm talking about a perceived latency from a User Experience point of view, because the exe, bat or whatever is launched once the carousel view has finished its animation.
This felt latency effect didn't exist before, because before the animation started later and now it starts immediately (which is superb and a nice improvement for sure). This is reflected, for example, in the display of the marquee 1 second later, as the script is launched after the end of the animation. This is a perceived latency, not a functional one. I may have misspoken before, I'm sorry.
I think the script should be launched before the view is animated.(so as not to have this impression of delay).

I do understand what you explained, and anwered 2 times ( now 4 as there are 2 issues )
And you probably don't understand what I explained.

When you say : "I think the script should be launched before the view is animated" makes me wonder if you really read what I wrote : WE CAN'T LAUNCH SCRIPTS EARLIER !

The issue is all related to the time taken by the script to load (given it's framework) !!!!!!
So the real question is : what the best technology/framework to use and with the shortest initialisation time.

I answered all this in the other issue you opened, with benchmarkings comparing timings for .bat, .ps1 and c# executable.

Hope you will ll understand this time.
Now this conversation is over for me.

My apologies, I'm a quick study but I need a long explanation.
May I make a suggestion to call the game-selected event earlier, like system-selected?
I'm really sorry to bother you with this.

I examined the project code and noticed that :

**System :** VERY FAST

void ViewController::goToSystemView

Scripting::fireEvent("system-selected", dest->getName());
mCurrentView = systemList;
[..]	
playViewTransition(forceImmediate);


**Game :** NOT VERY FAST

batocera-emulationstation/es-app/src/views/ViewController.cpp
void ViewController::goToGameList(SystemData* system, bool forceImmediate)
{ [...]
std::shared_ptr<IGameListView> view = getGameListView(destinationSystem);
[...]
if (forceImmediate || Settings::TransitionStyle() == "fade")
	{
		if (mCurrentView)
			mCurrentView->onHide();

		mCurrentView = view;

		playViewTransition(forceImmediate);
	}

std::shared_ptr<IGameListView> ViewController::getGameListView(SystemData* system, bool loadIfnull, const std::function<void()>& createAsPopupAndSetExitFunction)
{  [...]
file->setSelectedGame();

batocera-emulationstation/es-app/src/FileData.cpp
void FileData::setSelectedGame()
{  [...]
	TextToSpeech::getInstance()->say(getName(), false);
	Scripting::fireEvent("game-selected", getSourceFileData()->getSystem()->getName(), getPath(), getName());

Perhaps it would be possible to do something like this?

void ViewController::goToGameList(SystemData* system, bool forceImmediate)
{
    [...]
    std::shared_ptr<IGameListView> view = getGameListView(system);
    ISimpleGameListView* simpleListView = dynamic_cast<ISimpleGameListView*>(view.get());
    if (simpleListView != nullptr)
    {
        FileData* selectedGame = simpleListView->getCursor();
        if (selectedGame != nullptr && selectedGame->getType() == GAME)
        {
            Scripting::fireEvent("game-selected", 
                                 selectedGame->getSourceFileData()->getSystem()->getName(), 
                                 selectedGame->getPath(), 
                                 selectedGame->getName());
        }
    }
   [...]
   if (forceImmediate || Settings::TransitionStyle() == "fade")
	{
		if (mCurrentView)
			mCurrentView->onHide();

		mCurrentView = view;

		playViewTransition(forceImmediate);
	}

The aim is to launch Scripting::fireEvent("game-selected", getSourceFileData()->getSystem()->getName(), getPath(), getName()); as soon as possible before playViewTransition, as for the system
This is just a suggestion as I'm not familiar with C

I also noticed that updateThemeExtrasBindings is done before setSelectedGame here:

BasicGameListView::BasicGameListView(Window* window, FolderData* root)
	: ISimpleGameListView(window, root), mList(window)
{
	mList.longMouseClick += this;

	mList.setSize(mSize.x(), mSize.y() * 0.8f);
	mList.setPosition(0, mSize.y() * 0.2f);
	mList.setDefaultZIndex(20);

	mList.setCursorChangedCallback([&](const CursorState& /*state*/) 
		{
		updateThemeExtrasBindings();
		  FileData* file = (mList.size() == 0 || mList.isScrolling()) ? NULL : mList.getSelected();
		  if (file != nullptr)
		    file->setSelectedGame();
		  
			if (mRoot->getSystem()->isCollection())
				updateHelpPrompts();
		});

	addChild(&mList);

	populateList(root->getChildrenListToDisplay());
}

Ok I get it, this updates fileData according to getCursor. So it has to be done before logically.

I'm sorry, everything I wrote was stupid.