It’s been a hot minute since I posted, and I’d like to talk about what’s been going on with Desperado.
At the start I didn’t know what shape the engine needed to be. All I knew is what I did and didn’t like about the other mainstream engines. So that’s what I started with.
It took me a few months to ponder about what the core philosophies should be. What I settled with was modularity, scalability, cross compatibility, and enforcing personal responsibility on the end user. Desperado provides only what you need to get started and a few examples of how to put some pieces together. All else is up to the end user to decide what their project needs and to assemble it themselves.
Desperado starts with a gem system inspired by O3DE.

The idea here is that you can isolate modules to not know about each other. If you have an external dependency, let’s say jolt physics, you wrap it and it will self-register with the engine at its designated init() time. Additionally, the modules are operated via interface. Continuing with jolt for example, it is called into via an IPhysicsInterface from wherever in your project. This makes it easier to swap out jolt for physx, bullet, etc. The implementation stays within the gem, the engine never needs to know what is receiving the calls. This works the same for rendering backends and whatever other system.
Next up, we have the hierarchy split.

The engine provides the core structures and types that your application, the editor, etc will need. Nothing ever communicates sideways or up the chain, we only waterfall downwards. The engine also provides some template classes you can inherit from like a lifetime managed class and the gem system core. You do not need to use these if you do not want to. You can use the engine and gems ala carte as a library, from the lifetime managed class, or as a runtime that you call into with angel scripts, or whatever scripting language you want to rig up. You do not need to use the editor either and, ideally, the editor should work for your standalone projects even if they weren’t made with it. “App” currently is just a built-in test target that will evolve later.
At the moment, Desperado attempts to have as few dependencies as possible. In the long term, an attempt will be made to reduce what is currently linked. Hard dependencies at the moment include:
- CMake + Ninja
- GLM
- imgui
- SDL3
- vcpkg
- Slang
- Assimp
- catch2
- A few header only classes for basic types…

We also bake docs directly to the repo. There are a few reasons for this:
- Every PR requires that docs are updated with changes
- This makes traversal easier for agentic workflow
- Offline browsing
- Self-serve
This will be cleaned up in the future; there are some lingering ideas from previous agentic workflows left over at the moment.

For VSCode, Desperado has an extension built directly into the repo to assist with development. This makes it simpler to extend your workflow however you like, especially if you’re not using the editor. You can also see the self-serve docs running here via MKDocs. There’s some ai slop to clean admitedly.

Some of the remaining ideas that I like from other engines that I’m looking to replicate are:
- Unreal’s hierarchical config files
- Unreal’s stat cycle counters for performance metrics
- Godot’s concept of the editor being built from the engine and being able to run anywhere
- IDTech’s lockless jobs and fibers system
- and a few other bits and bobs from other gameplay frameworks
In the future the goal is to support any kind of workflow a user may like. Desperado does not enforce a coding paradigm like OOP or Functional, it is up to you how you want to use it. The example path that will be there, simply because my own project requires screaming fast performance, is the ecs, lockless jobs, fibers, etc.
In a separate repo, but included in my personal workspace, next to Desperado is an engineless XR-RPG-Minimal project. This is where I do my dirty, fail fast work that is not hindered by the current state of Desperado. Any lesson I learn here is lifted to Desperado and when refined is brought back down in a cleaner format. This project has zero dependency on Desperado. At a later date I’ll have a similar example project that does use Desperado.

Other than some GitHub project automation stuff for QOL, the only other thing worth mentioning is that I try to provide a Docker setup. This is so everyone can have reliable results when trying to compile cross platform projects. Desperado currently should compile on every major platform fine on the host OS, but this is also here for say compiling and testing Linux while on Windows or a containerized Android build setup. It works, but it’s not in the shape I want it to be, so we’ll see where it goes.
Lastly, here are some of the latest performance screenshots.
- On a Ryzen 3950x paired with an RTX 3080
- Debug build with all validation layers enabled etc.
- Frustrum culling
- GPU culling
- One directional light
- MSAA 4x
- Single Threaded
- Bistro GLB
- 1500+ entities
- No Instancing
- No LOD
- Yes MIPs
- 2.8+ Million Triangles
- When using Mock XR / Stereo rendering
- Combined culling frustrum
- Full Render per Eye
We get about 3000 fps average when no debug ui is present and about 900 when debug ui is present.
Mock XR gets about 700 with debug ui and about 1500 fps without it, so definitely some stuff to hunt down there.
My personal target is to be able to maintain a stable 120 fps, at all times, for a dynamic open world running on a Meta Quest 3 / Steam Frame in standalone mode.




When testing under the same conditions on a Samsung galaxy s8+ tablet, we’re just shy of 60 fps under any settings. The current theory is that we’re memory bound with the sheer number of triangles, no lods, or occlusion culling… but I’m suspicious. MSAA is disabled here as I haven’t sorted artifacts when writing directly to the swap chain.
