What I've been working on
Yo waddup, I’ve been able to pick development back up the past week, mainly focusing on reducing build-size and improving the ‘Engine’.
[ Reducing the Binary ]
I spend a few hours splitting up crates and removing unnecessary modules, which accounted for about 4-5MB less binary bloat. One system I reworked was how the game assets were bundled with the game. It used to just embed all the files as bytes in the binary, leading to a lot of duplicated data in the native builds because I build for AMD64 and AMD64-V3 separately, but now I have a dedicated pre-build step that collects them all, encodes them to something nicer, and spits out a big bundle.
[ Pre Building Assets ]
A major benefit to not just slapping raw import data into the game is: you get to re-encode and throw away things you don’t need. Ldtk world files are very large and inefficient to ship to users, the current world file accounted for roughly 8MiB in the binary and encoding the engine representation with some zstd compression instead, slimmed it to about 100KiB. The world data-size is still improvable but much less of an issue now, and future work will de-bloat it on its own.
Aseprite files also surprisingly got quite the reduction in size, I saw anywhere from 50% to 70% smaller files. My assumption is Zstd is able to compress all frame data in the same buffer at once. The biggest boon for Aseprite and Ldtk is actually loading times, everything now loads in the microsecond range, punting my need to add threading to asset loads further down the road.
The pre-build step also allowed me to re-encode the sound files without having to do it manually. The nice thing about Ldtk and Aseprite files is that you don’t need to export them in any fancy way manually every time you edit them. The same cannot be said for audio, sadly. I’m using Ardour to mix and create sound effects and Beepbox for music, both of which require some manual labor on change, BUT the resulting files can now be re-encoded to be smaller. Web gets to enjoy faster downloads with lower quality audio than native, with native audio quality being about 112 Kb/s and webs about 60kb/s. I hope the difference is not too noticeable and with the way I plan to use music, split up as multiple dynamic tracks mixed together at runtime, my hope is that the encoder will have to do a lot less work for each track, with of course a slight hit to assetbundle size. (I have yet to confirm that it leads to better quality)
which leads me into:
[ Rewriting Audio ]
I used to use Kira, a nice little audio rendering library, but it had a lot of performance issues for me, a chunk took about 4000us per audio source. After looking at the source code, a while back, I attributed it to the architecture of the crate, but convinced myself to fix it after the demo is released.
I then thought refitting the entire crate to work the way I want isn’t feasible nor fun. So just creating my own more custom fit, game aware solution, would be the better option for me. Functionality wise it can’t hold a candle to Kira yet (and I don’t plan to), but I get around 0.5us-1.5us per audio source on native, 2us on Wasm Firefox and 4us on chrome, which is quite the improvement.
There is still quite the number of things to tackle: resampling audio with different playback speeds, filters, effects, stereo mixing and I want to experiment with audio traversal and reverberation. While I did already write some of the pieces for it, like low pass filters, resampling, a general architecture to follow for effects, etc, while experimenting on the backend, I still need to fit them into the final system. Another big todo is stream-decoding the audio. Currently long sound files take about 500ms to decode and smaller sound effects take about 3-10ms which is not realistic to do in a frame. So I’ll need to extend my asset loader to allow streaming, which I’m excited about because it could also be used for streaming the world/level file (I’m currently loading the entire thing teehee).
[ Extending the Assetloader ]
In general i want to allow the subsystems to own the loaded asset files more, currently my assetstore contains all the buffers for the data, which is nice in some way, but not so nice in others.
You can imagine it looking like this (because it does):
pub struct AssetStore {
loaded_assets: Vec<LoadedAsset>,
loaded_by_path: HashMap<String, AssetStoreEntry>,
pub sprites: GenVec<SpriteAsset>,
pub atlas: Atlas,
pub kdl: GenVec<KdlDocument>,
pub kdl_assets: GenVec<Box<dyn KdlAsset>>,
pub ldtk: Option<ldtk2::Ldtk>,
pub levels: GenVec<WorldMapData>,
pub sounds: GenVec<SoundAsset>,
pub sample_rate: u32,
// pub meshies: GenVec<model::MeshCol>,
pub shaders: GenVec<ShaderAsset>,
pub hot_reloader: HashMap<TypeId, AssetReloader>,
last_hot_check: Instant,
pub pack: AssetPack,
}
As you can see the sprite atlas lives in there, which is used by the sprite-render subsystem, which is not ideal.
One thing I really like is the use of KDL for properties on Gameobjects, being able to hot-reload and muck about values is really nice, which I recently extended to shaders as well, removing duplication and making it easier to add and edit uniforms, vertex buffers, ubo’s, textures, you name it.
I now have KDL defined uniforms for example and vertex- and fragment-shader are in the same file. One issue you might see is how in the hell do you pre-build those properties if they are different types, but all come from kdl??? The dirty little secret is: I don’t :[], I just write a string into the assetpack and then decode it at runtime. The pre-built asset can be the original asset after all. The way KDL files are loaded is also something I’d like to improve. I’m not decoding them into the correct structures directly, I’m first decoding the KDL as a Json like object and then reading in the correct nodes (but its not been a big enough issue for me to fix yet)
[ Renderiengieng ]
I started reworking how the game is rendered, it used to render at full window resolution with sprites getting pixel locked to an internal resolution in the shader, which is not only very funny but also hurts performance a lot on devices that have stupid resolutions, with no power to actually render at that resolution, as I render sprites through the vertex pipeline – I love overdraw.
I thought about writing a software renderer that pre-bins sprites into regions/tiles etc, but that will have to wait for later. Now its rendered into a 640x360 internal buffer that then gets blitted into the window with Indigo Quilez pixelart AA filter.
Next up I want to improve my GPU API layer: I had a GPU abstraction for projects before ‘Machine Heart’ but didn’t use it because I wanted to just make a simple game when I created what would eventually become the backend of ‘Machine Heart’ months later and settled on Miniquad as the GPU abstraction. Though during development I started to really dislike how incompatible Miniquad was with what I wanted, and that was after I switched from Raylib a week earlier. So early on I just copied it over (which is extremely nice and one of Miniquads major strengths), made it use ‘glow’ and removed the globals to fit into my backend better. Overtime I started modifying it to look more like the previous abstraction, which is something I want to get back to and improve in the future.
[ Devtoooools ]
Another thing I will work on next is improving my logging system to allow for system events like allocations, Gameobject creation and single entity events to be reported and filtered inside the dev-gui i have setup right now. For example I want to be able to halt the game loop, click on an enemy on screen and see all the events it fired, introspect the current state, explore what it had for dinner, see who their crush is and look at its world position or something.
I might want to allow rendering the game at 240hz (this is the frequency the game updates at) and merge all frames into the target Vsync rate for smoother visuals, but it also just might be a waste of effort (but I’d really like to see what that would look like) In order to do that I want to change how I use buffers, currently I reuse the same instance buffer for drawn sprites each frame but ideally I’d have an easy way to automatically flip flop between 3-4, but that would require a little more design work on the GPU abstraction layer and regarding the sprite atlas
[ Closing Thoughts ]
You may notice from what I’ve been working on and planning, there’s not a lot of game being discussed here. That has a few reasons, one of which is I initially planned to create a vertical slice type demo for what would be the game, which lead me to keep adding things to the pile for “after the demo”. The problem for me is that those things are what excite me to work on it, and having to bend and twist myself into releasing a working demo just ends up cluster bombing me with depression, working on things to “get it done”, rather than enjoying the process of creation, is nothing but poison to the brain.
That’s it! That’s all I have for today, and anything I talked about I may or may not end up doing.
Have a good day and enjoy creating.
Files
Get Machine Heart :: Prototype
Machine Heart :: Prototype
Status | Prototype |
Author | Bonbli ★★★ |
Genre | Adventure |
More posts
- Windows And Linux BuildsJul 16, 2024
Leave a comment
Log in with itch.io to leave a comment.