Let's come back to retro-gtk. In the previous articles I explained how bad retro-gtk was, what I did to start improving it and more importantly what I did to prepare the terrain for further development. This article will detail the aforementioned planed improvements!
Unless stated otherwise, I don't plan these changes to be part of retro-gtk 0.14 and I have no idea when they will be implemented. If I say anything you know to be wrong or if you know something which could help the library's development, please share it in the comments!
Stabilization of the API
As stated in the previous article, I want retro-gtk's API to stop breaking as much as it did in the past. Starting with 0.14, we will avoid API and ABI breaks as much as possible, and if we do any we will document them properly. The API should be stable but given that some big changes are coming I don't feel comfortable promising proper stability just yet.
Gitlab
I requested to move retro-gtk to GNOME's GitLab. This would allow retro-gtk to stop parasiting gnome-games' Bugzilla and live its own life. This is particularly important as retro-gtk is expected to become more stable and hence more used. I hope it will be done before 0.14 get released.
Meson! 😃
I started porting retro-gtk's build system to Meson, nothing is on master yet and I am studying Meson as I do the port. I hope it will be done before 0.14 get released.
Y U NO Rust‽
The library has two major constraints: it has to use the low-level Libretro C API and it has to expose an introspected GObject C API. As far as I know Rust supports both these constraints, but I don't know Rust yet and the Vala code needed to be ported to some low-level language so I could develop the library more freely and make it move forward.
Rust support of GObject — even though it is very promising — is still in development, so porting retro-gtk to it while I had to learn the language would have delayed the needed low-level language port. I may port parts of the library to Rust later, once I took the time to learn it and once the GObject integration matured. I expect a C to Rust transition to be less painful while maintaining the ABI than the Vala to C was.
Hardware Rendering and Shaders
Currently retro-gtk renders the video output of the core by converting it with custom code and drawing it with Cairo. It was quick and easy to implement with the knowledge I had back then and it did the job so I let it that way so far, even though it's very clearly far from optimal. retro-gtk should instead use hardware acceleration to render the video.
The only problem is that I still know nothing about graphics APIs such as OpenGL. 🙂 I hope the learning curve won't be too steep, if you have any knowledge to share, please do! I'll rely on Emanuele's GtkGLArea
presentation and Anton's OpenGL 4 Tutorials to start working.
This would allow retro-gtk to render the cores' video with shaders, such as CRT shaders, Game Boy shaders and more!
I plan to work on this during SUSE Hack Week 16, so it may land in 0.14! 🙂
Hardware Rendering for the Cores
Libretro defines RETRO_ENVIRONMENT_SET_HW_RENDER
and struct retro_hw_render_callback
to allow the cores to request a hardware rendering interface to the frontend. Some Libretro cores can't work without it, so retro-gtk should implement it.
This would allow retro-gtk (and by extension Games) to run Nintendo 64 emulators and many more Libretro cores it can't yet!
I also plan to work on it during SUSE Hack Week 16.
Gamepad Handling
retro-gtk handles some controllers internally via RetroCoreView
, but it doesn't handle gamepads directly and rely on the application to implement gamepad objects. I am currently exporting the gamepad handling code of Games into its own library, it may be a good opportunity to let retro-gtk handle all its controllers — gamepad included — internally, avoiding to expose too much of its internals. I am not sure whether it is a good idea or not yet.
Run the Core in a Subproccess
This is by far the biggest change planned for retro-gtk, I have absolutely no idea when it will be implemented: I would like retro-gtk to run Libretro cores into their own subprocesses. I would like this to not change the API — or as little as possible — which is made easier by the new higher level API as it reduces the surface the IPC must cover, moving more of the code in the subprocess. Here are the effects I expect such a change to have.
Calling Back Core Support Improvements
As each core would be alone in its subprocess we can be sure of which core is calling back, so there is no need to track its identity, making retro-gtk able to handle Libretro cores calling back from a different thread. As a result the API is expected to be more asynchronous.
Avoid Static Memory Collisions
When a module is loaded twice in a process it is actually loaded just once, making the two instances sharing the same static memory space. Most Libretro cores where once standalone games or applications, hence they were free to use static variables and sometimes did so. Some of this is still present in some Libretro cores, making them buggy when two instances try to access the same static memory. retro-gtk avoid these clashes in a very dirty way: the module is copied to a temporary file which is loaded instead, tricking the loading mechanism in actually loading is twice… I hope that loading each module from a different process will solve that, but I really have no idea if it will: if you know anything about this please contact me.
Improved Stability
We could make retro-gtk resilient to crashes of the subprocess, whether it is caused by the Libretro core or the lower layers of retro-gtk, the applications could handle this as they want for example by kindly notifying the users. I hope it won't make debugging the cores harder and that meaningful backtraces and other useful debugging information will remain obtainable.
Improved Security?
libseccomp could be used in the subprocess to avoid the cores to run unauthorized syscalls, which could be used to mitigate exploits and other 0days. The problem is that we can't know which syscalls the Libretro cores need so it may be tricky to filter enough but not too much. It still needs some reflection.
Regarding Performances
On one hand, performances could get improved as the UI and the Libretro core would run on two different CPU cores at the same time. On the other hand, high performance IPCs are tricky to get right, especially as I have next to no experience in that field. Copies and locks should be avoided as much as possible but I don't expect to avoid them all. This strategy turned out well for web browsers so I can only hope it will here too. 🙂
Unit Testing, Code Coverage ans Non-Regression Tests
Because we should do all of that, period. Special Libretro cores may have to be crafted for testing, but we should use already existing ones as much as possible to avoid accidentally writing ones not following precisely Libretro's semantic and working only in retro-gtk.
Core Integration Tests and Non-Regression Tests
It would be really useful for retro-gtk to distribute a Libretro core integration testing tool. Games would no doubt benefit from it as we already encountered regressions in Libretro cores we ship with its Flatpak version, which is particularly bad when it causes your user's saves to be lost.
Conclusion
This article concludes the mini-series about retro-gtk. I hope this library to become the best way to build a GTK+ based Libretro frontend, and if all of these features get implemented I am sure it will be! 😊
About guaranteeing API stability in a library, I have this advice: it's important to be able to do refactorings later, the APIs developed now are maybe not convenient 5 years later. Don't hesitate to bump the major/API version, making the new version parallel-installable with the previous major versions (like GTK+ 3 and GTK+ 4), to do refactorings and improving the API. I think it's important to have clean code.
RépondreSupprimerI think that a lot of library developers don't want to change the API once it's declared stable, so after a decade like this, applications still need to use horrible APIs, and as a result there can be more bugs since the code in the application is less clear, etc.
See the section “Iterative API design and stability guarantees” in the Tepl intro:
https://developer.gnome.org/tepl/unstable/intro.html
Developing Tepl this way is like a new freedom for me. And apps using older versions of Tepl still work fine.