Boxes everywhere…#
OK, February was the month Gremlin moved house. Lots of boxes got carried from one room to another, a few things got thrown in the skip, and I finally fixed some plumbing that had been leaking for ages. It was also the busiest month of the year so far - which, after January, isn’t saying much - and almost all of it was foundational stuff.
A new floor plan#
On 1 February I finished the restructure I started in January, in one long afternoon of renames. Framework/ became Core/, Gfx/ became Platform/, the systems and components moved under ECS/, and the app loop, AppObjects, states and transitions all got their own App/ folder. A few days later the dependency-free data components - plain structs that don’t need the ECS to make sense - moved down into Core too.
Why bother? Because the folders now tell you which way the dependencies point:
Coredepends on nothing.Platformwraps the outside world - windows, the GPU, files.ECSbuilds on both.Appties it all together.
When I come back to Gremlin after a few weeks away - which, let’s be honest, is the normal state of affairs - I want the layout itself to tell me where something belongs. I shouldn’t need to remember. My experience with the r36 Max last month really emphasised the benefits of keeping clean dependency architecture and now I feel I’ve taken the first steps towards being able to build Gremlin in different ways for different tasks.
There is a thin line between clever… and stupid!#
This is my favourite change of the month. Gremlin’s IndexBuffer used to keep a CPU-side copy of its data and upload it implicitly inside DrawIndexedTriangles whenever it had changed. Convenient? Yes. Also a lovely way to get frame stalls - an upload happening in the middle of drawing, at a moment you didn’t choose.
So the two ideas are now completely separate:
CPUIndexArrayandCPUVertexArrayare plain CPU-side containers you can build and fiddle with freely.- GPU buffers hold no CPU-side data at all. You
Setthem explicitly from a CPU array before drawing, and a draw call never uploads anything behind your back.- There are convenience factories to create a GPU buffer straight from a CPU array, so the explicit version isn’t any more typing.
- Typed vertex structs (
SpriteVertexand friends) replace rawstd::vector<float>and byte-pushing. One renderer lost about 40 lines of hand-built attribute descriptions to a singleGetVertexAttributes()call. Nice.
What gave the game away was a new debug system profiler, which landed the day before the fix. Debug builds now shout about any AppObject whose frame time spikes to 5x normal or more, and once Buccaneer started throwing particles and billboards around, it had plenty to shout about. Without it I’d have just had a vague feeling that things were a bit juddery.
Who owns this texture?#
Until now, textures were passed around as shared_ptr<Texture>, which really means ’nobody is quite sure who owns this, so everybody does’. February brought a proper AssetManager, with handles, pools, policies (persistent, or scoped to a state) and AssetScopes that release a state’s assets when it goes away.
Now that ownership is clearly the manager’s job, everything else can go back to holding a plain Texture*. I know some of you will be clutching your smart pointers in horror, but it’s actually more honest: the raw pointer says “I’m only borrowing this”, and the manager is the one place that decides how long it lives.
A few related tidy-ups came along for the ride - texture source rectangles are now uint32_t (a negative pixel coordinate in a texture is meaningless), there’s optional mipmap generation on load, and textures can be created from data as well as from files. Every app’s texture loading moved over to it the same day.
I’m planning a separate post on asset management, so I’ll save the details for that.
UI, take two#
The old UISystem and ButtonSystem were monoliths - one system each doing layout, input, state and drawing for every widget. That’s exactly the kind of thing an ECS is supposed to save you from, so they’re gone, replaced by lots of small components and systems that each do one job:
- Navigation - a new navigation layer connects the
ActionController(gamepad, keyboard) to theInteractionSystem, so menus work without a mouse. - Rendering - all UI drawing now happens in one
UIRenderSystem. - Theming -
UIThemesupports nine-patch textures, plus overlay patches for richer hover states. Selected items can gently pulse their background colour, thanks to a newColourlerp.

Sprite also moved down into the Platform layer as the way sprite data gets passed around - sprite sheets, animation and UI all speak it now.
Retiring the old systems deleted about 1,400 lines. It also meant every app had to move to the new patterns in the same week: CNCWorkbench, Skirmish, MiniBlaster and Cannonade all picked up the themed nine-patch buttons, and Skirmish got the fancy hover overlays. That’s the price of having no backwards-compatibility policy… and also the joy of it.
Bits & bobs#
A few smaller things worth a mention:
ScreenWidth/ScreenHeightcan be overridden fromSettings.txt, and VictorVector got a basic layout that adapts to different screen sizes.- Fixed some incorrect indexing for non-square textures.
- The old particle class, already replaced by
ParticleSystem+BillboardRenderer, finally got deleted. - Buccaneer got physics and some fixed input controllers.
- A first
Caddata document with SVG load and save, for some CNC experiments.

