<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Jakub's tech blog</title><link>https://jakubtomsu.github.io/</link><description>Recent content on Jakub's tech blog</description><generator>Hugo -- gohugo.io</generator><language>en-US</language><copyright>Jakub Tomšů</copyright><lastBuildDate>Fri, 20 Feb 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://jakubtomsu.github.io/index.xml" rel="self" type="application/rss+xml"/><item><title>Projects</title><link>https://jakubtomsu.github.io/projects/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://jakubtomsu.github.io/projects/</guid><description>&lt;p&gt;This is an overview of some of the projects I&amp;rsquo;ve done.&lt;/p&gt;
&lt;h1 id="games"&gt;Games&lt;/h1&gt;
&lt;h2 id="solar-storm"&gt;Solar Storm&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://store.steampowered.com/app/2754920/Solar_Storm/"&gt;Steam&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;My first commercial game, and the first game I realeased on Steam. I made it in 4 months in a custom game engine built with Odin and Sokol.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://jakubtomsu.github.io/solarstorm/solar_storm.gif" alt="gif"&gt;&lt;/p&gt;
&lt;h2 id="hyper-heist"&gt;Hyper Heist&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://jakubtomsu.itch.io/hyperheist"&gt;itch.io&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Hardcore fast-paced cyberpunk platformer. Submission to SpeedJam 1 organized by ScoreSpace, ended up #3rd overall and #1 organizers choice.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://img.itch.zone/aW1hZ2UvMTkyOTU1Ny8xMTM4NDU2My5naWY=/original/eykWQm.gif" alt="gif"&gt;&lt;/p&gt;
&lt;h2 id="no-gun-shotgun"&gt;No-Gun Shotgun&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://jakubtomsu.itch.io/nogun-shotgun"&gt;itch.io&lt;/a&gt;&lt;/p&gt;</description><content:encoded><![CDATA[<p>This is an overview of some of the projects I&rsquo;ve done.</p>
<h1 id="games">Games</h1>
<h2 id="solar-storm">Solar Storm</h2>
<p><a href="https://store.steampowered.com/app/2754920/Solar_Storm/">Steam</a></p>
<p>My first commercial game, and the first game I realeased on Steam. I made it in 4 months in a custom game engine built with Odin and Sokol.</p>
<p><img src="/solarstorm/solar_storm.gif" alt="gif"></p>
<h2 id="hyper-heist">Hyper Heist</h2>
<p><a href="https://jakubtomsu.itch.io/hyperheist">itch.io</a></p>
<p>Hardcore fast-paced cyberpunk platformer. Submission to SpeedJam 1 organized by ScoreSpace, ended up #3rd overall and #1 organizers choice.</p>
<p><img src="https://img.itch.zone/aW1hZ2UvMTkyOTU1Ny8xMTM4NDU2My5naWY=/original/eykWQm.gif" alt="gif"></p>
<h2 id="no-gun-shotgun">No-Gun Shotgun</h2>
<p><a href="https://jakubtomsu.itch.io/nogun-shotgun">itch.io</a></p>
<p>This is a 2d tower defense platformer. Submission to Mini Jam 117: Ghosts, the first game jam I ever attented. Made in Godot 3.5.</p>
<p><img src="https://img.itch.zone/aW1hZ2UvMTc0OTcwNy8xMDMwMTkyOC5naWY=/347x500/YkCfir.gif" alt="gif"></p>
<h2 id="dungeon-of-quake">Dungeon of Quake</h2>
<p><a href="https://jakubtomsu.itch.io/dungeon-of-quake">itch.io</a>
<a href="https://github.com/jakubtomsu/dungeon-of-quake">Github</a></p>
<p>A retro FPS game written from scratch in Odin and Raylib. Comes with a very simple level editor.</p>
<h2 id="voxel-caves">Voxel Caves</h2>
<p><a href="https://jakubtomsu.itch.io/terrain-editor">itch.io</a></p>
<p>A small 3d game prototype, where you explore procedurally generated infinite cave system.
The terrain is generated with the marching cubes algorithm in a compute shader on the GPU.</p>
<p><img src="https://img.itch.zone/aW1nLzQ2MDAxMjEuZ2lm/original/2bBjK5.gif" alt="gif"></p>
<h2 id="jump-prince">Jump Prince</h2>
<p>Simple Jump King clone made with raylib, in &lt;700 lines of C code.</p>
<p><a href="https://github.com/jakubtomsu/raylib-jump-prince">Github</a></p>
<p><img src="https://github.com/jakubtomsu/raylib-jump-prince/raw/main/showcase.gif" alt="gif"></p>
<h2 id="fragile-handle-with-care">FRAGILE: handle with care</h2>
<p>Top-down game set in a sci-fi warehouse, where you use boxes to fight off enemies. Submission to Ludum Dare 53.</p>
<p><a href="https://jakubtomsu.itch.io/fragile-handle-with-care">itch.io</a></p>
<p><a href="https://github.com/jakubtomsu/ludum-dare-53">Github</a></p>
<p><img src="https://img.itch.zone/aW1hZ2UvMjA0OTM3Ny8xMjA1MzMzNC5naWY=/347x500/BAceBV.gif" alt="gif"></p>
<h1 id="open-source-projects">Open-source projects</h1>
<h2 id="vmem">VMem</h2>
<p><a href="https://github.com/jakubtomsu/vmem">Github</a></p>
<p>STB-style single-header C library for cross platform virtual memory management.</p>
<h2 id="3d-iscp-software-renderer">3d ISCP software renderer</h2>
<p><a href="https://github.com/jakubtomsu/ispc-3d-renderer">Github</a></p>
<p>Simple 3d rasterizer based on fgeisen&rsquo;s blog <a href="https://fgiesen.wordpress.com/2013/02/08/triangle-rasterization-in-practice/">Triangle rasterization in practice</a>, written in ISPC.</p>
<p><img src="https://github.com/jakubtomsu/ispc-3d-renderer/raw/main/screenshots/swordfish.png" alt="img"></p>
<h1 id="odin-bindings">Odin bindings</h1>
<ul>
<li><a href="https://github.com/jakubtomsu/odin-steamworks">odin-steamworks</a></li>
<li><a href="https://github.com/jakubtomsu/odin-ldtk">odin-ldtk</a></li>
<li><a href="https://github.com/jakubtomsu/odin-fmod">odin-fmod</a></li>
<li><a href="https://github.com/jakubtomsu/odin-lz4">odin-lz4</a></li>
<li><a href="https://github.com/jakubtomsu/odin-mimalloc">odin-mimalloc</a></li>
</ul>
]]></content:encoded></item><item><title>Solar Storm</title><link>https://jakubtomsu.github.io/solarstorm/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://jakubtomsu.github.io/solarstorm/</guid><description>&lt;h3 id="solar-storm-press-kit"&gt;Solar Storm Press Kit&lt;/h3&gt;
&lt;p&gt;&lt;img src="https://jakubtomsu.github.io/solarstorm/solar_storm.gif" alt="logo"&gt;&lt;/p&gt;
&lt;h1 id="about"&gt;About&lt;/h1&gt;
&lt;p&gt;Solar Storm is a turn-based scifi artillery game, inspired by 90s local-multiplayer games like Scorched Earth and Worms.&lt;/p&gt;
&lt;p&gt;Solar Storm releases on Steam &lt;strong&gt;April 3rd 2024&lt;/strong&gt; with Windows support.&lt;/p&gt;
&lt;h3 id="fully-destructible-procedural-maps"&gt;Fully Destructible Procedural Maps&lt;/h3&gt;
&lt;p&gt;Each battle takes place in an unique, procedurally generated arena. There are a number of Biomes, where each one of them has unique characteristics.&lt;/p&gt;
&lt;h3 id="powerful-weapons-of-mass-destruction"&gt;Powerful Weapons of Mass Destruction&lt;/h3&gt;
&lt;p&gt;There is a wide arsenal of powerful weapons, which can help you out in any circumstances.&lt;/p&gt;</description><content:encoded><![CDATA[<h3 id="solar-storm-press-kit">Solar Storm Press Kit</h3>
<p><img src="/solarstorm/solar_storm.gif" alt="logo"></p>
<h1 id="about">About</h1>
<p>Solar Storm is a turn-based scifi artillery game, inspired by 90s local-multiplayer games like Scorched Earth and Worms.</p>
<p>Solar Storm releases on Steam <strong>April 3rd 2024</strong> with Windows support.</p>
<h3 id="fully-destructible-procedural-maps">Fully Destructible Procedural Maps</h3>
<p>Each battle takes place in an unique, procedurally generated arena. There are a number of Biomes, where each one of them has unique characteristics.</p>
<h3 id="powerful-weapons-of-mass-destruction">Powerful Weapons of Mass Destruction</h3>
<p>There is a wide arsenal of powerful weapons, which can help you out in any circumstances.</p>
<ul>
<li><strong>Weapon Powerups</strong> which significantly change the weapon</li>
<li>You can choose a <strong>Weapon Mode</strong> each turn. This impacts whether a projectile explodes on impact, bounces, or drills through the terrain.</li>
</ul>
<h3 id="disasters">Disasters</h3>
<p>Watch out for disaster! Meteorites might be falling from the sky out of nowhere!</p>
<h2 id="key-features-summarized">Key Features, Summarized:</h2>
<ul>
<li>30+ Biomes, each with unique procedural maps</li>
<li>Deep weapon system with 40+ powerful Weapons</li>
<li>Local Multiplayer against players or bots</li>
<li>Free for All and Team Deatchmatch game modes</li>
<li>Unexpected Disasters</li>
</ul>
<h2 id="supported-languages">Supported Languages</h2>
<ul>
<li>English</li>
<li>Simplified Chinese - 简体中文</li>
<li>Spanish - Deutsch</li>
<li>German - Čeština</li>
<li>Czech - Español</li>
<li>Greek - Ελληνικά</li>
<li>Russian - Русский</li>
<li>Swedish - Svenska</li>
<li>Croatian - Hrvatski</li>
<li>Portuguese - Português</li>
<li>Finnish - Suomi</li>
<li>Turkish - Türkçe</li>
<li>Dutch - Nederlands</li>
</ul>
<h2 id="price">Price</h2>
<ul>
<li>$6.99</li>
<li>€6,89</li>
<li>£5.89</li>
<li>170 CZK</li>
</ul>
<h1 id="contact-and-links">Contact and Links</h1>
<ul>
<li>Business Inquiries: <a href="mailto:jakubtomsu@proton.me">jakubtomsu@proton.me</a></li>
<li><a href="https://store.steampowered.com/app/2754920/Solar_Storm/">Steam</a></li>
<li><a href="https://discord.com/invite/wn5jMMMYe4">Discord Server</a></li>
<li><a href="https://youtube.com/@jakubtomsu">YouTube</a></li>
<li><a href="https://twitter.com/jakubtomsu_">Dev Twitter</a></li>
<li><a href="https://handmade.network/p/485/solar-storm/">Handmade Network</a></li>
</ul>
<h1 id="credits">Credits</h1>
<p>Jakub Tomšů - Game Design, Programming and Art</p>
<p>Dylan Siebert - Soundtrack</p>
<h1 id="technology">Technology</h1>
<p>The entire game is written in the <a href="https://odin-lang.org">Odin programming language</a> and uses a custom game engine built on <a href="https://github.com/floooh/sokol">Sokol</a> and <a href="https://www.fmod.com/">FMOD</a></p>
<p>The goal is to ensure high quality and good performance, even on low power devices.</p>
<h1 id="videos">Videos</h1>
<p>Demo Trailer</p>
<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/5_ru0hrqeZs?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<h1 id="logos">Logos</h1>
<p><img src="/solarstorm/logo.png" alt="logo">
<img src="/solarstorm/logo_no_globe.png" alt="logo_no_globe"></p>
<h1 id="gifs">Gifs</h1>
<p><img src="/solarstorm/gif0.gif" alt="gif">
<img src="/solarstorm/gif1.gif" alt="gif">
<img src="/solarstorm/gif2.gif" alt="gif"></p>
<h1 id="screenshots">Screenshots</h1>
<p><img src="/solarstorm/Screenshot%202024-03-31%20180935.png" alt="screenshot">
<img src="/solarstorm/Screenshot%202024-03-31%20181119.png" alt="screenshot">
<img src="/solarstorm/Screenshot%202024-03-31%20181220.png" alt="screenshot">
<img src="/solarstorm/Screenshot%202024-03-31%20181251.png" alt="screenshot">
<img src="/solarstorm/Screenshot%202024-03-31%20181305.png" alt="screenshot">
<img src="/solarstorm/Screenshot%202024-03-31%20181328.png" alt="screenshot">
<img src="/solarstorm/Screenshot%202024-03-31%20181352.png" alt="screenshot">
<img src="/solarstorm/Screenshot%202024-03-31%20181408.png" alt="screenshot">
<img src="/solarstorm/Screenshot%202024-03-31%20181529.png" alt="screenshot">
<img src="/solarstorm/Screenshot%202024-03-31%20181605.png" alt="screenshot">
<img src="/solarstorm/Screenshot%202024-03-31%20181705.png" alt="screenshot">
<img src="/solarstorm/Screenshot%202024-03-31%20183612.png" alt="screenshot">
<img src="/solarstorm/Screenshot%202024-03-31%20183731.png" alt="screenshot">
<img src="/solarstorm/Screenshot%202024-03-31%20183914.png" alt="screenshot">
<img src="/solarstorm/Screenshot%202024-03-31%20184150.png" alt="screenshot">
<img src="/solarstorm/Screenshot%202024-03-31%20184251.png" alt="screenshot">
<img src="/solarstorm/Screenshot%202024-03-31%20184259.png" alt="screenshot">
<img src="/solarstorm/Screenshot%202024-03-31%20184321.png" alt="screenshot">
<img src="/solarstorm/Screenshot%202024-03-31%20184331.png" alt="screenshot">
<img src="/solarstorm/Screenshot%202024-03-31%20184408.png" alt="screenshot">
<img src="/solarstorm/Screenshot%202024-03-31%20184451.png" alt="screenshot">
<img src="/solarstorm/Screenshot%202024-03-31%20184512.png" alt="screenshot">
<img src="/solarstorm/Screenshot%202024-03-31%20184727.png" alt="screenshot">
<img src="/solarstorm/Screenshot%202024-03-31%20184756.png" alt="screenshot">
<img src="/solarstorm/Screenshot%202024-03-31%20184854.png" alt="screenshot">
<img src="/solarstorm/Screenshot%202024-03-31%20185251.png" alt="screenshot">
<img src="/solarstorm/Screenshot%202024-03-31%20185430.png" alt="screenshot">
<img src="/solarstorm/Screenshot%202024-03-31%20185830.png" alt="screenshot">
<img src="/solarstorm/Screenshot%202024-03-31%20190243.png" alt="screenshot">
<img src="/solarstorm/Screenshot%202024-03-31%20190400.png" alt="screenshot">
<img src="/solarstorm/Screenshot%202024-03-31%20190459.png" alt="screenshot">
<img src="/solarstorm/Screenshot%202024-03-31%20190542.png" alt="screenshot"></p>
<p><img src="/solarstorm/screenshot0.png" alt="screenshot">
<img src="/solarstorm/screenshot1.png" alt="screenshot">
<img src="/solarstorm/screenshot2.png" alt="screenshot">
<img src="/solarstorm/screenshot3.png" alt="screenshot">
<img src="/solarstorm/screenshot4.png" alt="screenshot">
<img src="/solarstorm/screenshot5.png" alt="screenshot"></p>
]]></content:encoded></item><item><title>You don't need free lists!</title><link>https://jakubtomsu.github.io/posts/bit_pools/</link><pubDate>Fri, 20 Feb 2026 00:00:00 +0000</pubDate><guid>https://jakubtomsu.github.io/posts/bit_pools/</guid><description>&lt;p&gt;Pool-like datastructures are incredibly practical, especially when &lt;a href="https://floooh.github.io/2018/06/17/handles-vs-pointers.html"&gt;combined with generational handles&lt;/a&gt;.
They let you have an array of reusable item slots, but crucially all actions such as insertions, removals and lookups are &lt;em&gt;always O(1)&lt;/em&gt;, including worst case! (Assuming you preallocate all buffers)&lt;/p&gt;
&lt;p&gt;The usual way to implement the slot-reuse mechanism is to store some kind of a list of currently unused slots.
There are a bunch of different ways to implement this list, from a dumb array with slot indices, an intrusive/non-intrusive singly-linked-list.
They essentially do the same, except minor memory overhead differences.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Pool-like datastructures are incredibly practical, especially when <a href="https://floooh.github.io/2018/06/17/handles-vs-pointers.html">combined with generational handles</a>.
They let you have an array of reusable item slots, but crucially all actions such as insertions, removals and lookups are <em>always O(1)</em>, including worst case! (Assuming you preallocate all buffers)</p>
<p>The usual way to implement the slot-reuse mechanism is to store some kind of a list of currently unused slots.
There are a bunch of different ways to implement this list, from a dumb array with slot indices, an intrusive/non-intrusive singly-linked-list.
They essentially do the same, except minor memory overhead differences.</p>
<p>Here&rsquo;s how the datastructure internals could look like:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="ln">1</span><span class="cl"><span class="n">Pool</span> <span class="o">::</span> <span class="k">struct</span><span class="p">(</span><span class="err">$</span><span class="nl">N</span><span class="p">:</span> <span class="kt">int</span><span class="p">,</span> <span class="err">$</span><span class="nl">T</span><span class="p">:</span> <span class="n">typeid</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">    <span class="nl">data</span><span class="p">:</span>   <span class="p">[</span><span class="n">N</span><span class="p">]</span><span class="n">T</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl">    <span class="nl">next</span><span class="p">:</span>   <span class="p">[</span><span class="n">N</span><span class="p">]</span><span class="n">u32</span><span class="p">,</span> <span class="c1">// per-item free list &#39;next&#39;
</span></span></span><span class="line"><span class="ln">4</span><span class="cl">    <span class="nl">free</span><span class="p">:</span>   <span class="n">u32</span><span class="p">,</span> <span class="c1">// free list head
</span></span></span><span class="line"><span class="ln">5</span><span class="cl">    <span class="nl">max</span><span class="p">:</span>    <span class="n">u32</span><span class="p">,</span> <span class="c1">// max used slot
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><p>But I have been using a very different approach lately, which is <em>NOT</em> based on any kind of free-list-like system.
It has a few nice performance properties:</p>
<ul>
<li>only ~1 bit overhead per slot</li>
<li>significantly better slot allocation strategy with improved memory locality</li>
</ul>
<p>My approach is inspired by the <a href="http://www.gii.upv.es/tlsf/">TLSF memory allocator</a>, though it should be a lot simpler to understand and implement from scratch.</p>
<h2 id="demo">Demo</h2>
<blockquote class="twitter-tweet"><p lang="en" dir="ltr">free-list-based pools have an weird property people don&#39;t talk about - at some point the slot allocation pattern becomes almost random.<br><br>But there are alternative approaches, which don&#39;t use free lists at all. Lately I&#39;ve been using a multi-level bit pool: <a href="https://t.co/zx1zaoT0un">pic.twitter.com/zx1zaoT0un</a></p>&mdash; Jakub Tomšů (@jakubtomsu_) <a href="https://twitter.com/jakubtomsu_/status/2025892087451816092?ref_src=twsrc%5Etfw">February 23, 2026</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


<h1 id="bits">Bits</h1>
<p>The idea is to just store a single bit, which determines whether a slot is in use or not.
Let&rsquo;s start with this, though we will improve it a lot later:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="ln">1</span><span class="cl"><span class="n">Pool</span> <span class="o">::</span> <span class="k">struct</span><span class="p">(</span><span class="err">$</span><span class="nl">N</span><span class="p">:</span> <span class="kt">int</span><span class="p">,</span> <span class="err">$</span><span class="nl">T</span><span class="p">:</span> <span class="n">typeid</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">    <span class="n">where</span> <span class="n">N</span> <span class="o">%</span> <span class="mi">64</span> <span class="o">==</span> <span class="mi">0</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl">    <span class="nl">data</span><span class="p">:</span>   <span class="p">[</span><span class="n">N</span><span class="p">]</span><span class="n">T</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">    <span class="nl">used</span><span class="p">:</span>   <span class="p">[</span><span class="n">N</span><span class="o">/</span><span class="mi">64</span><span class="p">]</span><span class="n">u64</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><blockquote>
<p>For the rest of this article we assume all pools store a multiple of 64 elements.</p>
</blockquote>
<p>This of course has a fatal flaw - once you have thousands of items, you need to search through all the individual bits.</p>
<h2 id="tzcnt-is-your-friend"><em>tzcnt</em> is your friend</h2>
<p>It&rsquo;s wasteful to search the 64 bits of each <code>used</code> field individually with something like a loop of shr+mask+branch.</p>
<p>There is <code>tzcnt</code>, which stands for trailing zero count. It&rsquo;s an x64 instruction which has been supported on all consumer chips for many years. It&rsquo;s exposed to the programmer in the form of compiler intrinsics, and it lets you find the index of a lowest binary 1 in essentially a <a href="https://uops.info/html-instr/TZCNT_R16_R16.html">single cycle</a>.</p>
<p>And while a smart optimizing compiler could convert a tiny loop into a single <code>tzcnt</code> call, using it yourself explicitly makes it clearer what&rsquo;s going on and it will be fast even in debug builds.</p>
<blockquote>
<p>In Odin, I use a procedure called <code>count_trailing_zeros</code> from the <code>base:intrinsics</code> package, which implements this in a cross-platform way.</p>
</blockquote>
<h2 id="searching-slots-with-tzcnt">Searching slots with <em>tzcnt</em></h2>
<p>All you need to do to find an empty slot in a single <code>u64</code> is to call <code>count_trailing_zeros(~used[index])</code>. We&rsquo;re negating the used mask to actually <em>count trailing ones</em> (used slots) to <em>find the first zero</em> (unused) bit index.</p>
<p>The code could look like this:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="n">find_empty_slot</span> <span class="o">::</span> <span class="nf">proc</span><span class="p">(</span><span class="nl">pool</span><span class="p">:</span> <span class="err">$</span><span class="n">T</span><span class="o">/</span><span class="err">$</span><span class="nf">Pool</span><span class="p">(</span><span class="err">$</span><span class="n">N</span><span class="p">,</span> <span class="err">$</span><span class="n">V</span><span class="p">))</span> <span class="o">-&gt;</span> <span class="p">(</span><span class="nl">result</span><span class="p">:</span> <span class="kt">int</span><span class="p">,</span> <span class="nl">ok</span><span class="p">:</span> <span class="kt">bool</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">    <span class="k">for</span> <span class="n">i</span> <span class="n">in</span> <span class="mf">0.</span><span class="p">.</span><span class="o">&lt;</span><span class="n">N</span><span class="o">/</span><span class="mi">64</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">        <span class="nl">bit</span> <span class="p">:</span><span class="o">=</span> <span class="kt">int</span><span class="p">(</span><span class="n">intrinsics</span><span class="p">.</span><span class="nf">count_trailing_zeros</span><span class="p">(</span><span class="o">~</span><span class="n">pool</span><span class="p">.</span><span class="n">used</span><span class="p">[</span><span class="n">i</span><span class="p">]))</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">        <span class="c1">// check for full block
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl">        <span class="k">if</span> <span class="n">bit</span> <span class="o">!=</span> <span class="mi">64</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">            <span class="k">return</span> <span class="n">i</span> <span class="o">*</span> <span class="mi">64</span> <span class="o">+</span> <span class="n">bit</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">    <span class="c1">// Pool is full
</span></span></span><span class="line"><span class="ln">10</span><span class="cl">    <span class="k">return</span> <span class="mi">0</span><span class="p">,</span> <span class="nb">false</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><p>Okay, this is quite a nice improvement, but you still have to search <code>N/64</code> u64s in the worst case.
Can we improve this? Can we somehow quickly search among all those u64s and find one that isn&rsquo;t full?</p>
<h1 id="2-level-bit-arrays">2-level bit arrays</h1>
<p>The answer is yes. The solution is another, but smaller bit array.
While the original bit array stores 0/1 for each slot, we can add another array to store bit per each block, which is 1 when a block is full.</p>
<p>The actual struct ends up something like this. I&rsquo;ll only implement the bit pool part without the actual data, since that&rsquo;s how I use it in my code.</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="ln">1</span><span class="cl"><span class="n">Bit_Pool</span> <span class="o">::</span> <span class="k">struct</span><span class="p">(</span><span class="err">$</span><span class="nl">N</span><span class="p">:</span> <span class="kt">int</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">    <span class="n">where</span> <span class="n">N</span> <span class="o">%</span> <span class="mi">64</span> <span class="o">==</span> <span class="mi">0</span>
</span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="ln">4</span><span class="cl">    <span class="c1">// round up div by 64*64
</span></span></span><span class="line"><span class="ln">5</span><span class="cl">    <span class="nl">l1</span><span class="p">:</span> <span class="p">[(</span><span class="n">N</span> <span class="o">+</span> <span class="mi">4095</span><span class="p">)</span> <span class="o">/</span> <span class="mi">4096</span><span class="p">]</span><span class="n">u64</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">6</span><span class="cl">    <span class="c1">// &#39;used&#39; array from before
</span></span></span><span class="line"><span class="ln">7</span><span class="cl">    <span class="nl">l0</span><span class="p">:</span> <span class="p">[</span><span class="n">N</span> <span class="o">/</span> <span class="mi">64</span><span class="p">]</span><span class="n">u64</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><p>Each field in the L1 array stores info about 64 fields in L0. This way each <code>tzcnt</code> instruction effectively searches 4096 slots.</p>
<p>The arrows in this illustration show the implicit mappings between the data:</p>





<pre tabindex="0"><code>data             L0               L1
┌──────────┐   ┌──────┐      ┌────────┐
│0..63     │◄──┤0     │◄─────┤0       │
├──────────┤   ├──────┤      ├────────┤
│64..127   │◄──┤1     │   ┌──┤1       │
└──────────┘   ├──────┤   │  ├────────┤
               │...   │   │  │...     │
┌──────────┐   ├──────┤   │  │────────│
│4032..4095│◄──┤63    │   │  │N/4096-1│
└──────────┘   └──────┘   │  └────────┘
┌──────────┐   ┌───────┐  │
│4096..8191│◄──┤64..127│◄─┘
└──────────┘   └───────┘</code></pre><h2 id="search">Search</h2>
<p>This is how the code looks like. There is a loop so while the &ldquo;theoretical asymptotic complexity&rdquo; is still based on N.
In practice, the N is either quite small so this loop is taken only a few times, or you <a href="/posts/bit_pools/#more-levels">add more levels</a>, which scales logarithmically in memory footprint and with a tiny constant overhead at each additional level.</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="n">find_0</span> <span class="o">::</span> <span class="nf">proc</span><span class="p">(</span><span class="nl">bp</span><span class="p">:</span> <span class="nf">Bit_Pool</span><span class="p">(</span><span class="err">$</span><span class="n">N</span><span class="p">))</span> <span class="o">-&gt;</span> <span class="p">(</span><span class="nl">index</span><span class="p">:</span> <span class="kt">int</span><span class="p">,</span> <span class="nl">ok</span><span class="p">:</span> <span class="kt">bool</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">    <span class="nl">l0_index</span> <span class="p">:</span><span class="o">=</span> <span class="n">N</span> <span class="o">&gt;</span> <span class="mi">64</span> <span class="o">?</span> <span class="o">-</span><span class="mi">1</span> <span class="o">:</span> <span class="mi">0</span> <span class="c1">// ignore the L1 in small pools
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl">    <span class="c1">// Find suitable L0 block by searching L1
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl">    <span class="k">for</span> <span class="n">used</span><span class="p">,</span> <span class="n">i</span> <span class="n">in</span> <span class="n">bp</span><span class="p">.</span><span class="n">l1</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">        <span class="nl">l1_slot</span> <span class="p">:</span><span class="o">=</span> <span class="kt">int</span><span class="p">(</span><span class="n">intrinsics</span><span class="p">.</span><span class="nf">count_trailing_zeros</span><span class="p">(</span><span class="o">~</span><span class="n">used</span><span class="p">))</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">        <span class="k">if</span> <span class="n">l1_slot</span> <span class="o">!=</span> <span class="mi">64</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">            <span class="n">l0_index</span> <span class="o">=</span> <span class="mi">64</span> <span class="o">*</span> <span class="n">i</span> <span class="o">+</span> <span class="n">l1_slot</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">            <span class="k">break</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">
</span></span><span class="line"><span class="ln">12</span><span class="cl">    <span class="k">if</span> <span class="n">l0_index</span> <span class="o">==</span> <span class="o">-</span><span class="mi">1</span> <span class="o">||</span> <span class="n">l0_index</span> <span class="o">&gt;=</span> <span class="p">(</span><span class="n">N</span> <span class="o">/</span> <span class="mi">64</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">        <span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="nb">false</span> <span class="c1">// Pool is full
</span></span></span><span class="line"><span class="ln">14</span><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">
</span></span><span class="line"><span class="ln">16</span><span class="cl">    <span class="c1">// Find the actual slot within the L0 block
</span></span></span><span class="line"><span class="ln">17</span><span class="cl">    <span class="nl">l0_slot</span> <span class="p">:</span><span class="o">=</span> <span class="kt">int</span><span class="p">(</span><span class="n">intrinsics</span><span class="p">.</span><span class="nf">count_trailing_zeros</span><span class="p">(</span><span class="o">~</span><span class="n">bp</span><span class="p">.</span><span class="n">l0</span><span class="p">[</span><span class="n">l0_index</span><span class="p">]))</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">    <span class="k">if</span> <span class="n">l0_slot</span> <span class="o">!=</span> <span class="mi">64</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">        <span class="k">return</span> <span class="n">l0_index</span> <span class="o">*</span> <span class="mi">64</span> <span class="o">+</span> <span class="n">l0_slot</span><span class="p">,</span> <span class="nb">true</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="ln">21</span><span class="cl">
</span></span><span class="line"><span class="ln">22</span><span class="cl">    <span class="k">return</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="nb">false</span> <span class="c1">// Pool is full
</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><p>As you can see this has a nice &ldquo;front loading&rdquo; property, where it always allocates slots with the lowest index first. This is <em>great</em> for memory locality, especially in burst-like slot allocation patterns.
A regular list-based pool would scatter the values all across the pool!</p>
<p>The generated code also looks reasonably nice, LLVM can unroll the loop in many cases and even collapse it.
Check out this <a href="https://godbolt.org/z/hq88fGrdG">Godbolt example</a> where N = 8192.</p>
<h3 id="code">Code</h3>
<p>A full implementation is on <a href="https://github.com/jakubtomsu/raven/blob/main/base/base_bit_pool.odin">Github</a>, as a part of my Raven engine. It includes procedures for actually setting bits and lookups.</p>
<h2 id="more-levels">More levels</h2>
<p>If your <strong>N</strong> gets <em>significantly</em> larger than 4096, you could trivially add more levels. It&rsquo;s still all O(1) in every case. The only reason I don&rsquo;t do this is because my pools tend to be relatively small, tens of thousands of items at most.</p>
<p>For example, if you added <code>l2</code> bit array, a single <code>tzcnt</code> instruction would effectively search 64^3 (262144) slots. That is a lot of slots.</p>
<h2 id="aosoa">AoSoA</h2>
<p>This block-based approach lends itself to SIMD AoSoA (arrays of structures of arrays).</p>
<p>The L0 mask is the &ldquo;ground truth&rdquo; determining free slots, but since it&rsquo;s a bitmask of packed elements it&rsquo;s exactly the layout you would use as a SIMD op mask or something similar.</p>
<p>I quite enjoy keeping datastructures simple, instead of wrapping everything in multiple levels of generic containers. Here&rsquo;s an example of AoSoA combined with Bit_Pool, for something like a particle system:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="n">MAX_FOOS</span> <span class="o">::</span> <span class="mi">1024</span> <span class="o">*</span> <span class="mi">16</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="n">Foo_Batch</span> <span class="o">::</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">    <span class="nl">pos</span><span class="p">:</span>    <span class="p">[</span><span class="mi">3</span><span class="p">]</span><span class="err">#</span><span class="n">simd</span><span class="p">[</span><span class="mi">64</span><span class="p">]</span><span class="n">f32</span><span class="p">,</span> <span class="c1">// xyz 64 times
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl">    <span class="nl">vel</span><span class="p">:</span>    <span class="p">[</span><span class="mi">3</span><span class="p">]</span><span class="err">#</span><span class="n">simd</span><span class="p">[</span><span class="mi">64</span><span class="p">]</span><span class="n">f32</span><span class="p">,</span> <span class="c1">// xyz 64 times
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl">    <span class="nl">timer</span><span class="p">:</span>  <span class="err">#</span><span class="n">simd</span><span class="p">[</span><span class="mi">64</span><span class="p">]</span><span class="n">f32</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">    <span class="nl">flags</span><span class="p">:</span>  <span class="err">#</span><span class="n">simd</span><span class="p">[</span><span class="mi">64</span><span class="p">]</span><span class="n">u16</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">
</span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="c1">// Each value in used.l0 corresponds to one batch
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="n">Foos</span> <span class="o">::</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">    <span class="nl">used</span><span class="p">:</span>       <span class="nf">Bit_Pool</span><span class="p">(</span><span class="n">MAX_FOOS</span><span class="p">),</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">    <span class="nl">batches</span><span class="p">:</span>    <span class="p">[</span><span class="n">MAX_FOOS</span> <span class="o">/</span> <span class="mi">64</span><span class="p">]</span><span class="n">Foo_Batch</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><p>Explaining this in more detail is out of scope for this article, but I&rsquo;ll leave this here just as a vague idea for inspiration.</p>
<h2 id="possibly-atomic">Possibly atomic?</h2>
<p>I haven&rsquo;t really looked into this yet, but I wonder if insertion/removal could be completely lockless using atomics.
It seems relatively trivial in the one-level case, maybe I could order the insertion and removal memory writes to eliminate data races?</p>
<p>In case you figure something out, please do let me know! I would be very interested in experiments and also correctness proof.</p>
<h1 id="conclusion">Conclusion</h1>
<p>This approach is a bit more complicated than a dumb free slot array, but I really like the fact it&rsquo;s just a basic bit array, no super complicated metadata to take care of.</p>
<p>I&rsquo;m not necessarily trying to convince anyone to use this approach, however I think these kinds of algorithms are still relatively uncommon, so it was worth writing about. It&rsquo;s certainly nothing novel, but I hope it&rsquo;s valuable!</p>
<p>Thank you for reading!</p>
<p>Jakub</p>
<blockquote>
<p>2026-02-22: Minor updates, correct free list memory overhead</p>
</blockquote>
<blockquote>
<p>2026-02-23: Typo fixes, demo</p>
</blockquote>
]]></content:encoded></item><item><title>Odin compilation speed tips</title><link>https://jakubtomsu.github.io/posts/odin_comp_speed/</link><pubDate>Mon, 19 Jan 2026 00:00:00 +0000</pubDate><guid>https://jakubtomsu.github.io/posts/odin_comp_speed/</guid><description>&lt;p&gt;Compilation speed is &lt;em&gt;really important&lt;/em&gt;. If you hit build and it takes so long it pulls you out of the flow and you start scrolling twitter, that&amp;rsquo;s a problem.&lt;/p&gt;
&lt;p&gt;So what can we do to compile Odin programs faster? Here I&amp;rsquo;ll try to explain some ways of troubleshooting Odin compile times and ways to improve them.&lt;/p&gt;
&lt;h2 id="tldr"&gt;TL;DR&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Use faster linkers&lt;/li&gt;
&lt;li&gt;There is a &lt;code&gt;-show-debug-messages&lt;/code&gt; flag to dump things like &lt;code&gt;LOC/s&lt;/code&gt; and total parsed lines/tokens/packages, plus a lot of debug info.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-o:minimal&lt;/code&gt; and &lt;code&gt;-microarch:native&lt;/code&gt; is essentially free&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id="small-programs"&gt;Small programs&lt;/h1&gt;
&lt;p&gt;Compilation speed of &lt;strong&gt;large&lt;/strong&gt; projects is probably the most important thing when measuring compiler performance.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Compilation speed is <em>really important</em>. If you hit build and it takes so long it pulls you out of the flow and you start scrolling twitter, that&rsquo;s a problem.</p>
<p>So what can we do to compile Odin programs faster? Here I&rsquo;ll try to explain some ways of troubleshooting Odin compile times and ways to improve them.</p>
<h2 id="tldr">TL;DR</h2>
<ul>
<li>Use faster linkers</li>
<li>There is a <code>-show-debug-messages</code> flag to dump things like <code>LOC/s</code> and total parsed lines/tokens/packages, plus a lot of debug info.</li>
<li><code>-o:minimal</code> and <code>-microarch:native</code> is essentially free</li>
</ul>
<h1 id="small-programs">Small programs</h1>
<p>Compilation speed of <strong>large</strong> projects is probably the most important thing when measuring compiler performance.</p>
<p>However in Odin, I often write many small utility &ldquo;scripts&rdquo; instead of using shell/batch/python.
It&rsquo;s very convenient, I get access to the core libraries running is simple as <code>odin run my_tool</code>.</p>
<p>So let&rsquo;s consider the following program. It&rsquo;s just a simple hello world, but all small tools need to print to console so it&rsquo;s at least a little representative of the real world.</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-odin" data-lang="odin"><span class="line"><span class="ln">1</span><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="s">&#34;core:fmt&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="n">main</span><span class="w"> </span><span class="o">::</span><span class="w"> </span><span class="kd">proc</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="w">    </span><span class="n">fmt</span><span class="p">.</span><span class="n">printfln</span><span class="p">(</span><span class="s">&#34;Hello %s %i!&#34;</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;World&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">123</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><p>To get the compilation speed, we can run <code>odin build</code> with the  <code>-show-timings</code>. On my machine, result is something like this:</p>





<pre tabindex="0"><code>Total Time                         -   270.198 ms - 100.00%
initialization                     -     7.618 ms -   2.81%
parse files                        -    12.968 ms -   4.79%
type check                         -    66.492 ms -  24.60%
LLVM API Code Gen (   54 modules ) -    82.512 ms -  30.53%
msvc-link                          -   100.602 ms -  37.23%</code></pre><p>This is not <em>too</em> bad! But interestingly, compiling some of my projects (with tens of thousands lines of code, and a lot more in dependencies) don&rsquo;t compile that much slower.</p>
<p>So can we do better?</p>
<h1 id="linker">Linker</h1>
<p>In the example above, almost 40% of the time is spent in the linker. So let&rsquo;s try a different one.</p>
<p>Using <code>-linker:radlink</code> gets it down to about 15% on my machine. If you&rsquo;re on linux you can also try <code>mold</code>.</p>
<blockquote>
<p>In general, a faster linker can also improve compile times when the binary gets large (e.g. large dependencies, or big <code>#load</code> files).</p>
</blockquote>
<h1 id="more-timings">More Timings</h1>
<p>There is a <code>-show-more-timings</code> which lists timings for all the internal compilation stages.</p>
<p>In most cases it doesn&rsquo;t tell you that much (it&rsquo;s mostly valuable for compiler developers), and most time is spent in <code>LLVM Object Generation</code> stage.</p>
<p>However, you still might want to look at the results if you need to debug <em>why</em> something is compiling slower than expected - sometimes you hit a slow path.</p>
<blockquote>
<p>For example, I ran into a case parsing a file with gigantic embedded arrays of integer constants generated by sokol shader compiler was adding ~3 seconds to my compile time.</p>
<p>Always profile, especially when things go wrong.</p>
</blockquote>
<h1 id="internal-flags">Internal Flags</h1>
<p>You can often find interesting hidden flags in the <a href="https://github.com/odin-lang/Odin/blob/c36bd4f85813c991cd6c155f072cdea637972199/src/main.cpp#L518">compiler source code</a>. Let&rsquo;s look at some of them.</p>
<h1 id="debug-messages">Debug Messages</h1>
<p>There is a flag called <code>-show-debug-messages</code>, and while it&rsquo;s not intended for regular users, it&rsquo;s extremely useful. It dumps a LOT of good statistics to <em>stderr</em> useful for debugging and profiling.</p>
<p>So let&rsquo;s measure Hello World again:</p>





<pre tabindex="0"><code>odin build hello -show-timings -linker:radlink -show-more-timings -show-debug-messages</code></pre><p>When you scroll a bit past the <code>LOC/s</code> sections (which are also extremely useful!), you&rsquo;ll see something like this:</p>





<pre tabindex="0"><code>Peak Memory Size: 271.000 MiB

Total Lines     - 80727
Total Tokens    - 395951
Total Files     - 162
Total Packages  - 26
Total File Size - 2572637</code></pre><p>This is an overview of <em><strong>all</strong></em> the things the compiler had to parse to compile your program.</p>
<p>But 26 packages and 80k lines of code seems like a lot for a Hello World. All of that was pulled in by <code>core:fmt</code> and it&rsquo;s dependencies. Let&rsquo;s see if we can do something about it.</p>
<h1 id="build-diagnostics">Build Diagnostics</h1>
<p>When you call <code>odin build</code> with the <code>-build-diagnostics</code> flag it&rsquo;s going to generate big tables from LLVM backend information.</p>
<p>Currently this includes two primary kinds of tables:</p>
<ul>
<li>parapoly instatiation statistics</li>
<li>module diagnostics</li>
</ul>
<p>The parapoly tables just list the generic procedures with the most instantiations, plus their instruction counts.</p>
<p>The module diagnostics list per-module total instruction count as well as other info, which can be useful for determining which module is the slowest to compile by LLVM.</p>
<h3 id="dependencies">Dependencies</h3>
<p>A good way to diagnose the dependencies is to add a <code>-keep-temp-files</code> flag to the build command, and look at what <code>.ll</code> IR files are generated.
The list of files tells you which packages got actually included in the build. And the file size roughly reflects the compile time.</p>
<h2 id="baseruntime-only"><code>base:runtime</code> only</h2>
<p>We can do something like the following to write data directly to <em>stderr</em>. The <code>base:runtime</code> is always included by default as it contains builtin implementations and other required features, so there&rsquo;s not much better we could do.</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-odin" data-lang="odin"><span class="line"><span class="ln">1</span><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="s">&#34;base:runtime&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="n">main</span><span class="w"> </span><span class="o">::</span><span class="w"> </span><span class="kd">proc</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="w">    </span><span class="n">runtime</span><span class="p">.</span><span class="n">print_string</span><span class="p">(</span><span class="s">&#34;Hello World 123!&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><p>The results look a lot nicer:</p>





<pre tabindex="0"><code>Peak Memory Size: 76.703 MiB

Total Lines     - 9211
Total Tokens    - 59321
Total Files     - 31
Total Packages  - 3
Total File Size - 290122</code></pre>




<pre tabindex="0"><code>Total Time                         -    58.230 ms - 100.00%
initialization                     -     7.189 ms -  12.34%
parse files                        -     2.853 ms -   4.90%
type check                         -     5.993 ms -  10.29%
LLVM API Code Gen (   31 modules ) -    14.681 ms -  25.21%
rad-link                           -    27.510 ms -  47.24%</code></pre><p>Only 9k LOC, 3 packages, and 60 milliseconds to compile the entire program! That&rsquo;s <em>really</em> nice.</p>
<p>But there&rsquo;s a problem: we lost all the nice <code>core:fmt</code>&rsquo;s formatting functionality. This is a big issue, because it&rsquo;s very cumbersome to do all the formatting by hand.</p>
<h2 id="μ-fmt-experiment">μ-fmt experiment</h2>
<p>This lead me to write an experimental <code>ufmt</code> (micro-fmt) package. It does only the bare minimum, but it covers 90% of my own <code>core:fmt</code> use-cases.</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-odin" data-lang="odin"><span class="line"><span class="ln">1</span><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="s">&#34;ufmt&#34;</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="n">main</span><span class="w"> </span><span class="o">::</span><span class="w"> </span><span class="kd">proc</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="w">    </span><span class="n">ufmt</span><span class="p">.</span><span class="n">printfln</span><span class="p">(</span><span class="s">&#34;Hello %s %i!&#34;</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;World&#34;</span><span class="p">,</span><span class="w"> </span><span class="nx">123</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><p>The entire implementation is &lt;200 lines of code and depends only on <code>base:runtime</code>.
It also compiles in 60 milliseconds and includes only 9k LOC.</p>
<p>There is only <code>tprintf</code>, <code>printf</code> and <code>printfln</code>. Only supported format qualifiers are the following:</p>
<ul>
<li><code>%s</code>: string and cstring, no cstring16</li>
<li><code>%i</code>: all integer types</li>
<li><code>%x</code>: all integer types in hexadecimal, always zero padded</li>
<li><code>%f</code>: 16, 32 and 64 bit floats, with basic NaN/Inf detection</li>
<li><code>%%</code>: to print literal <code>%</code> characters</li>
</ul>
<p>Here&rsquo;s the <a href="https://gist.github.com/jakubtomsu/54ffb22fa7b8341cb9f573598c873d62">initial ufmt version</a> as a github gist.</p>
<p>Update: I expanded the initial version and now it lives in the source tree of my game engine. I added very lightweight RTTI crawl for printing any values including structs, arrays, etc automatically. It&rsquo;s not <em>100% complete</em>, but it&rsquo;s reasonably usable and I&rsquo;ll continue to improve it. Here&rsquo;s the <a href="https://github.com/jakubtomsu/raven/tree/main/base/ufmt">link</a>.</p>
<blockquote>
<p>Of course, using this has no effect if you import a package which depends on <code>core:fmt</code>. This is pretty annoying, currently there is no good way
to see what exactly is your program importing and why, apart of looking at temp obj files.</p>
<p>On my branch of the compiler I experimented with printing a graphviz graph of all the packages with their includes, but that&rsquo;s not official.
Update: see <a href="https://github.com/odin-lang/Odin/pull/6253">this PR</a> for the graph generator, and a userspace solution using <code>core:odin/parser</code>.</p>
</blockquote>
<h1 id="llvm">LLVM</h1>
<p>In general the slowest part of the compilaton pipeline is the LLVM backend. Even with all optimizations disabled it takes quite a while.</p>
<p>Odin already codegen&rsquo;s each package independently to utilize all the CPU threads (LLVM cannot be multithreaded with better granularity).
And as a general rule, LLVM scales very poorly with the codegen amount. For example <code>-disable-assert</code> and <code>-no-bounds-check</code> can help by a very tiny bit because it&rsquo;s slightly less things to generate (so I don&rsquo;t think it&rsquo;s worth it for debug builds).</p>
<p>On the other hand, <code>-debug</code> <strong>has a HUGE impact</strong>. Generating the PDB is no small feat, and it can make compile times take <strong>20-80% longer</strong> in my experience.</p>
<h1 id="optimization">Optimization</h1>
<p>Obviously, it&rsquo;s not a good idea to compile with <code>-o:speed/size/aggressive</code> if you want good compilation speed. But I still want debug builds that run fast!</p>
<p>However <code>-o:minimal</code> is almost completely free, all it does is enable inlining of <code>#force_inline</code> procedures. It&rsquo;s the default when NOT compiling with <code>-debug</code>.</p>
<p>Similarly, using <code>-microarch:native</code> adds no overhead. I&rsquo;m pretty sure it just triggers a slightly different code path in the LLVM lowering passes, but could possibly yield some runtime perf benefits.</p>
<h1 id="parapoly-variants">Parapoly Variants</h1>
<p>Another issue which can appear in larger codebases is a duplicate explosion from <a href="https://odin-lang.org/docs/overview/#parametric-polymorphism">parametric-polymorphism</a> variants.</p>
<p>For example if you have code like this:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-odin" data-lang="odin"><span class="line"><span class="ln">1</span><span class="cl"><span class="n">some_large_procedure</span><span class="w"> </span><span class="o">::</span><span class="w"> </span><span class="kd">proc</span><span class="p">(</span><span class="err">$</span><span class="n">N</span><span class="o">:</span><span class="w"> </span><span class="kt">int</span><span class="p">,</span><span class="w"> </span><span class="p">...)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="w">    </span><span class="c1">// ... lot of code
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><p>The compiler will literally need to generate the code for each <code>N</code> separately. So while the parsing cost is paid once, the type checker and the backend has to work extra hard.</p>
<p>The overhead is totally negligible even with hundreds of variants <em>when the procedure body is small</em>. But once the codegen amount gets larger you start running into all kinds of bottlenecks, one of them being LLVM (as always).</p>
<h3 id="what-to-do-about-this">What to do about this</h3>
<blockquote>
<p>Update: there is now a <code>-build-diagnostics</code> flag. But the following approach also works.</p>
</blockquote>
<p>There isn&rsquo;t any kind of diagnostics in the Odin compiler itself to check if this is happening. What you can do however is compile your code and check the symbols. For example like this on Windows:</p>





<pre tabindex="0"><code>odin build my_program -build-mode:lib
dumpbin /SYMBOLS my_program.lib &gt; symbols.txt</code></pre><p>Or with <code>objdump</code> on Linux. You&rsquo;re looking for a long list of repeated procedure symbols, something like: <code>my_package::my_procedure:proc(my_param:$$value, ...)</code>.</p>
<p>To help mitigate this issue, you could try putting all the common code into a non-parapoly procedure and calling it indirectly. Or, if you don&rsquo;t actually <em>need</em> the parapoly specialization and only use it for &ldquo;optimization&rdquo;, just get rid of it.</p>
<h1 id="conclusion">Conclusion</h1>
<p>GingerBill says the Odin compiler can still be a lot faster. And while there&rsquo;s plenty of room for optimization, it&rsquo;s not too bad in the current state.</p>
<p>For a simple hello world, we got nearly <strong>5x faster</strong> compile times by compiling with the right flags and being careful about dependencies.
Of course, it&rsquo;s different in real, bigger projects but I hope I shed some light on the various ways you could debug these issues, and some general rules to follow.</p>
<p>I recomment the following command as a reasonable default for compiling lightweight tools:</p>





<pre tabindex="0"><code>odin build my_tool -linker:radlink -microarch:native</code></pre><p>Thank you for reading!</p>
<h4 id="also-big-thanks-to-all-my-patrons-3">Also big thanks to all my Patrons! &lt;3</h4>
<ul>
<li>Voodoo51</li>
<li>p1xelHer0</li>
<li>Coedo</li>
<li>Filip Aničić</li>
<li>Moritz Falk</li>
<li>Lion Schitik</li>
<li>Ondřej Jamriška</li>
<li>Alastair Marshall</li>
</ul>
<blockquote>
<p>Edit 2026-01-22: Added a section about parapoly, and more <code>-o:minimal</code> info.</p>
</blockquote>
<blockquote>
<p>Edit 2026-01-23: A small section about checking dependency IR</p>
</blockquote>
<blockquote>
<p>Edit 2026-02-10: Linked graph generator and new ufmt version</p>
</blockquote>
<blockquote>
<p>Edit 2026-04-11: Build Diagnostics flag</p>
</blockquote>
]]></content:encoded></item><item><title>Coroutine-less way to visualize iterations</title><link>https://jakubtomsu.github.io/posts/iter_debug/</link><pubDate>Fri, 26 Dec 2025 00:00:00 +0000</pubDate><guid>https://jakubtomsu.github.io/posts/iter_debug/</guid><description>&lt;p&gt;Let&amp;rsquo;s say you&amp;rsquo;re working on a procgen system, or a similar problem where you combine multiple iterative algorithms to get visual results.
It could look something like this:&lt;/p&gt;





&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-odin" data-lang="odin"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Imagine this is for a tile-based 2D game or something
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;generate_level&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;proc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;some_params&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;...)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;some_temp_state_1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;some_temp_state_2&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;0&lt;/span&gt;&lt;span class="o"&gt;..&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// generate land tiles ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;iter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;0&lt;/span&gt;&lt;span class="o"&gt;..&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;4&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// run cellular automata ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;0&lt;/span&gt;&lt;span class="o"&gt;..&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;SIZE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;0&lt;/span&gt;&lt;span class="o"&gt;..&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;SIZE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// spawn entity ...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;All the logic being contained within one big procedure (possibly with a few helpers) is quite nice, especially for prototyping.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Let&rsquo;s say you&rsquo;re working on a procgen system, or a similar problem where you combine multiple iterative algorithms to get visual results.
It could look something like this:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-odin" data-lang="odin"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="c1">// Imagine this is for a tile-based 2D game or something
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="n">generate_level</span><span class="w"> </span><span class="o">::</span><span class="w"> </span><span class="kd">proc</span><span class="p">(</span><span class="n">some_params</span><span class="o">:</span><span class="w"> </span><span class="p">...)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w">    </span><span class="n">some_temp_state_1</span><span class="p">,</span><span class="w"> </span><span class="n">some_temp_state_2</span><span class="o">:</span><span class="w"> </span><span class="kt">int</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w">    </span><span class="k">for</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="nx">0</span><span class="o">..&lt;</span><span class="n">N</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w">        </span><span class="c1">// generate land tiles ...
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w">    </span><span class="k">for</span><span class="w"> </span><span class="n">iter</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="nx">0</span><span class="o">..&lt;</span><span class="nx">4</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w">        </span><span class="c1">// run cellular automata ...
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w">    </span><span class="k">for</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="nx">0</span><span class="o">..&lt;</span><span class="n">SIZE</span><span class="w"> </span><span class="k">do</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="nx">0</span><span class="o">..&lt;</span><span class="n">SIZE</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w">        </span><span class="c1">// spawn entity ...
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><blockquote>
<p>All the logic being contained within one big procedure (possibly with a few helpers) is quite nice, especially for prototyping.</p>
</blockquote>
<p>But, what if something goes wrong, and the debugger, logging or 2D/3D visuals don&rsquo;t help?</p>
<p>You might want to inspect what the actual <em><strong>iterations</strong></em> are doing.</p>
<h2 id="the-usual-solution">The usual solution</h2>
<p>If you&rsquo;re using a managed language with coroutines, you can just <code>yield</code> from the procedure.</p>
<p>In other cases, you likely need to completely refactor your state management, put in a state machine, and split up the code a lot. I really don&rsquo;t want to do that.</p>
<h2 id="no-refactoring-approach">No-refactoring approach</h2>
<p>So here&rsquo;s my 1-million-IQ solution.</p>
<p>What if you just <strong>re-ran the entire procedure</strong>, stopping at a different point each time? The observable effect is just like with coroutines.</p>
<p>So let&rsquo;s try that! Here&rsquo;s a helper proc to mark each &ldquo;step&rdquo; of the algorithm:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-odin" data-lang="odin"><span class="line"><span class="ln">1</span><span class="cl"><span class="n">max_iter</span><span class="o">:</span><span class="w"> </span><span class="kt">int</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="n">curr_iter</span><span class="o">:</span><span class="w"> </span><span class="kt">int</span><span class="w">
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="n">_iter</span><span class="w"> </span><span class="o">::</span><span class="w"> </span><span class="kd">proc</span><span class="p">(</span><span class="n">n</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">1</span><span class="p">)</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">ok</span><span class="o">:</span><span class="w"> </span><span class="kt">bool</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="w">    </span><span class="n">ok</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">curr_iter</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="n">max_iter</span><span class="w">
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="w">    </span><span class="n">curr_iter</span><span class="w"> </span><span class="o">+=</span><span class="w"> </span><span class="n">n</span><span class="w">
</span></span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="w">    </span><span class="k">return</span><span class="w"> </span><span class="n">ok</span><span class="w">
</span></span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><p>Now all you need is to instrument your <code>generate_level</code> procedure:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-odin" data-lang="odin"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="n">generate_level</span><span class="w"> </span><span class="o">::</span><span class="w"> </span><span class="kd">proc</span><span class="p">(</span><span class="n">some_params</span><span class="o">:</span><span class="w"> </span><span class="p">...)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="w">    </span><span class="n">some_temp_state_1</span><span class="p">,</span><span class="w"> </span><span class="n">some_temp_state_2</span><span class="o">:</span><span class="w"> </span><span class="kt">int</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w">    </span><span class="k">for</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="nx">0</span><span class="o">..&lt;</span><span class="n">N</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w">        </span><span class="c1">// generate land tiles ...
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w">        </span><span class="n">_iter</span><span class="p">()</span><span class="w"> </span><span class="k">or_return</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w">    </span><span class="k">for</span><span class="w"> </span><span class="n">iter</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="nx">0</span><span class="o">..&lt;</span><span class="nx">4</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w">        </span><span class="c1">// run cellular automata ...
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w">        </span><span class="n">_iter</span><span class="p">()</span><span class="w"> </span><span class="k">or_return</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w">    </span><span class="k">for</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="nx">0</span><span class="o">..&lt;</span><span class="n">SIZE</span><span class="w"> </span><span class="k">do</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="nx">0</span><span class="o">..&lt;</span><span class="n">SIZE</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w">        </span><span class="c1">// spawn entity ...
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w">        </span><span class="n">_iter</span><span class="p">()</span><span class="w"> </span><span class="k">or_return</span><span class="w">
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><blockquote>
<p>In case you&rsquo;re not using Odin, you can use <code>if (!_iter()) return</code> or another equivalent instead of <code>or_return</code></p>
</blockquote>
<p>And a way to control <code>max_iter</code>:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-odin" data-lang="odin"><span class="line"><span class="ln">1</span><span class="cl"><span class="n">max_iter</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">0</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="k">for</span><span class="w"> </span><span class="n">app_main_loop_running</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="w">    </span><span class="n">max_iter</span><span class="w"> </span><span class="o">+=</span><span class="w"> </span><span class="nx">1</span><span class="w">
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="w">    </span><span class="n">generate_level</span><span class="p">(...)</span><span class="w">
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="w">    </span><span class="n">debug_draw_level</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><p>And that&rsquo;s it. Of course it works only if <code>generate_level</code> is reasonably fast, otherwise you&rsquo;d have use other methods.</p>
<p>Thanks for reading, I hope it saves you some refactoring time.</p>
]]></content:encoded></item><item><title>Gameplay code validation and catching NaNs</title><link>https://jakubtomsu.github.io/posts/game_code_validation/</link><pubDate>Wed, 04 Jun 2025 00:00:00 +0000</pubDate><guid>https://jakubtomsu.github.io/posts/game_code_validation/</guid><description>&lt;p&gt;I recently ran into a NaN propagation bug in my gameplay code, which required a bit more effort than usual.&lt;/p&gt;
&lt;p&gt;Often, those problems are straightforward to solve: it&amp;rsquo;s just a forgotten zero-check before dividing, normalizing zero-length vector, etc.
I almost always find it immediately by looking through my most recent changes. But this time I had no idea!&lt;/p&gt;
&lt;p&gt;So I came up with a simple way to &lt;em&gt;help&lt;/em&gt; catching these bugs in general (in fact, it&amp;rsquo;s so simple it&amp;rsquo;s probably the first thing you would think of too).
But it&amp;rsquo;s a nice pattern, so I thought I would share it anyway.&lt;/p&gt;</description><content:encoded><![CDATA[<p>I recently ran into a NaN propagation bug in my gameplay code, which required a bit more effort than usual.</p>
<p>Often, those problems are straightforward to solve: it&rsquo;s just a forgotten zero-check before dividing, normalizing zero-length vector, etc.
I almost always find it immediately by looking through my most recent changes. But this time I had no idea!</p>
<p>So I came up with a simple way to <em>help</em> catching these bugs in general (in fact, it&rsquo;s so simple it&rsquo;s probably the first thing you would think of too).
But it&rsquo;s a nice pattern, so I thought I would share it anyway.</p>
<h2 id="gameplay-testing-in-general">Gameplay testing in general</h2>
<p>Testing games can be really complicated, since the final output is just pixels and audio.
I have few &ldquo;real unit tests&rdquo; in my codebase, mostly for things like file formats.
Those usually look like this:</p>
<ol>
<li>Setup state</li>
<li>Execute some logic</li>
<li>Assert correct results</li>
</ol>
<p>That&rsquo;s just <em>not useful</em> for most gameplay code.</p>
<p>Most of the bugs happen when the setup gets complex enough and the multiple systems interact together.
And the expected results change all the time as you&rsquo;re iterating on your game design.</p>
<p>So, what if instead of writing isolated tests, I just inserted the correctness validation (step 3) throughout the codebase?
It limits what you can do, but IMO it&rsquo;s still useful.</p>
<h2 id="validation">Validation</h2>
<p>First I created validation procedures for common data types, e.g. a float:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-odin" data-lang="odin"><span class="line"><span class="ln">1</span><span class="cl"><span class="n">validate_f32</span><span class="w"> </span><span class="o">::</span><span class="w"> </span><span class="kd">proc</span><span class="p">(</span><span class="n">x</span><span class="o">:</span><span class="w"> </span><span class="kt">f32</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="w">    </span><span class="n">assert</span><span class="p">(</span><span class="err">!</span><span class="n">math</span><span class="p">.</span><span class="n">is_nan</span><span class="p">(</span><span class="n">x</span><span class="p">))</span><span class="w">
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="w">    </span><span class="n">assert</span><span class="p">(</span><span class="err">!</span><span class="n">math</span><span class="p">.</span><span class="n">is_inf</span><span class="p">(</span><span class="n">x</span><span class="p">))</span><span class="w">
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><p>Then it&rsquo;s easy to use this for validating other data:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-odin" data-lang="odin"><span class="line"><span class="ln">1</span><span class="cl"><span class="n">validate_vec</span><span class="w"> </span><span class="o">::</span><span class="w"> </span><span class="kd">proc</span><span class="p">(</span><span class="n">v</span><span class="o">:</span><span class="w"> </span><span class="p">[</span><span class="err">$</span><span class="n">N</span><span class="p">]</span><span class="kt">f32</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="w">    </span><span class="k">for</span><span class="w"> </span><span class="n">f</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="n">v</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="w">        </span><span class="n">validate_f32</span><span class="p">(</span><span class="n">f</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><p>And then validating actual entities:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-odin" data-lang="odin"><span class="line"><span class="ln">1</span><span class="cl"><span class="n">validate_entity</span><span class="w"> </span><span class="o">::</span><span class="w"> </span><span class="kd">proc</span><span class="p">(</span><span class="n">ent</span><span class="o">:</span><span class="w"> </span><span class="o">^</span><span class="n">Entity</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="w">    </span><span class="n">validate_vec</span><span class="p">(</span><span class="n">ent</span><span class="p">.</span><span class="n">position</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="w">    </span><span class="n">validate_vec</span><span class="p">(</span><span class="n">ent</span><span class="p">.</span><span class="n">velocity</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="w">    </span><span class="c1">// arbitrary limits depending on what&#39;s reasonable for the game
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="w">    </span><span class="n">assert</span><span class="p">(</span><span class="n">ent</span><span class="p">.</span><span class="n">position</span><span class="p">.</span><span class="n">y</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="o">-</span><span class="nx">1000</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="w">    </span><span class="n">assert</span><span class="p">(</span><span class="n">linalg</span><span class="p">.</span><span class="n">length</span><span class="p">(</span><span class="n">ent</span><span class="p">.</span><span class="n">position</span><span class="p">)</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="nx">10000</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="w">    </span><span class="n">assert</span><span class="p">(</span><span class="n">linalg</span><span class="p">.</span><span class="n">length</span><span class="p">(</span><span class="n">ent</span><span class="p">.</span><span class="n">velocity</span><span class="p">)</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="nx">10000</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><p>The validation procedure asserts the data isn&rsquo;t in a completely unreasonable state.</p>
<blockquote>
<p>I also add a <code>@(disabled = !DEV_BUILD)</code> to all those procedures since I don&rsquo;t really need validation in release mode.</p>
</blockquote>
<h2 id="instrumenting-code-with-validation">Instrumenting code with validation</h2>
<p>Now, let&rsquo;s just slap the validation in the most important parts of the codebase.
I know this is really grug-brained, but just a few calls in the right places got me a lot of coverage!</p>
<p>The point is to <em>reliably</em> catch possible errors soon after they happen, rather than letting them propagate for multiple frames before the issue becomes visible.
This is about <em>debuggability</em> - not necessarily 100% correctness.</p>
<p>Here is a pattern I use a lot: validate every entity at the start and the end of interface procs like <code>entity_tick</code>, <code>entity_damage</code>, <code>entity_interact</code> etc.
The defer makes sure it&rsquo;s called on every return.</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-odin" data-lang="odin"><span class="line"><span class="ln">1</span><span class="cl"><span class="n">entity_tick</span><span class="w"> </span><span class="o">::</span><span class="w"> </span><span class="kd">proc</span><span class="p">(</span><span class="n">ent</span><span class="o">:</span><span class="w"> </span><span class="o">^</span><span class="n">Entity</span><span class="p">,</span><span class="w"> </span><span class="n">delta</span><span class="o">:</span><span class="w"> </span><span class="kt">f32</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="w">    </span><span class="n">validate_entity</span><span class="p">(</span><span class="n">ent</span><span class="o">^</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="w">    </span><span class="k">defer</span><span class="w"> </span><span class="n">validate_entity</span><span class="p">(</span><span class="n">ent</span><span class="o">^</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="w">    </span><span class="c1">// gameplay logic...
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><p>This is kinda like the &ldquo;assert everywhere&rdquo; advice, just in a slightly different form.</p>
<p>With this in place, I&rsquo;m significantly more confident I can debug issues with NaN etc much easier.</p>
<h2 id="failed-approach-catching-fp-exceptions">Failed approach: catching FP exceptions</h2>
<p>Before this approach I also tried to catch FP exceptions directly at the source.
Turns out this isn&rsquo;t really viable due to SIMD codegen of modern compilers (I won&rsquo;t go into details here&hellip;)</p>
<p>But in case you wanna try it yourself, here is a <a href="https://gist.github.com/jakubtomsu/361f9319f73ed0ae9c0e36df887226b9">Github gist</a> for enabling the exceptions on win32 with Odin.</p>
]]></content:encoded></item><item><title>Fixed timestep without interpolation</title><link>https://jakubtomsu.github.io/posts/fixed_timestep_without_interpolation/</link><pubDate>Fri, 11 Oct 2024 00:00:00 +0000</pubDate><guid>https://jakubtomsu.github.io/posts/fixed_timestep_without_interpolation/</guid><description>&lt;p&gt;Some time ago I wrote a &lt;a href="https://jakubtomsu.github.io/posts/input_in_fixed_timestep/"&gt;blog post&lt;/a&gt; about the tick setup in my engine, why and how I use fixed timestep update loops. It also explained how to actually use it in practice when dealing with input etc.&lt;/p&gt;
&lt;p&gt;The general idea is this: the game would keep an accumulator timer and only simulate steps with a constant delta time, which is great for stability and predictability.&lt;/p&gt;
&lt;p&gt;This was mostly inspired by server code for multiplayer games, so I naturally also implemented game state interpolation, which multiplayer games use as well. However I have some issues with regular interpolation, it adds unnecessary latency and it&amp;rsquo;s also annoying to implement.&lt;/p&gt;</description><content:encoded><![CDATA[<p>Some time ago I wrote a <a href="https://jakubtomsu.github.io/posts/input_in_fixed_timestep/">blog post</a> about the tick setup in my engine, why and how I use fixed timestep update loops. It also explained how to actually use it in practice when dealing with input etc.</p>
<p>The general idea is this: the game would keep an accumulator timer and only simulate steps with a constant delta time, which is great for stability and predictability.</p>
<p>This was mostly inspired by server code for multiplayer games, so I naturally also implemented game state interpolation, which multiplayer games use as well. However I have some issues with regular interpolation, it adds unnecessary latency and it&rsquo;s also annoying to implement.</p>
<p>Turns out there are other ways to do it! It&rsquo;s possible to use the game tick to &ldquo;predict&rdquo; the render game state without explicitly writing any interpolation code. That&rsquo;s what this post is about.</p>
<p>I also made a <a href="/posts/fixed_timestep_without_interpolation/#demo">Demo</a> program which implements all of the different methods mentioned.</p>
<h1 id="the-old-way">The &ldquo;old&rdquo; way</h1>
<p>Here is what the <strong>old</strong> game loop looked like. The game kept track of the game state from the previous tick, and then used this for interpolation with the time remainder.</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-odin" data-lang="odin"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="n">DELTA</span><span class="w"> </span><span class="o">::</span><span class="w"> </span><span class="nx">1</span><span class="mf">.0</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="nx">60</span><span class="mf">.0</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="n">accumulator</span><span class="o">:</span><span class="w"> </span><span class="kt">f32</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="n">game</span><span class="o">:</span><span class="w"> </span><span class="n">Game</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="n">prev_game</span><span class="o">:</span><span class="w"> </span><span class="n">Game</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="n">prev_time</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="n">time</span><span class="p">.</span><span class="n">tick_now</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="k">for</span><span class="w"> </span><span class="err">!</span><span class="n">quit</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w">    </span><span class="n">frame_duration</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="kt">f32</span><span class="p">(</span><span class="n">time</span><span class="p">.</span><span class="n">duration_seconds</span><span class="p">(</span><span class="n">time</span><span class="p">.</span><span class="n">tick_since</span><span class="p">(</span><span class="n">prev_time</span><span class="p">)))</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w">    </span><span class="n">prev_time</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">time</span><span class="p">.</span><span class="n">tick_now</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w">    </span><span class="n">accumulator</span><span class="w"> </span><span class="o">+=</span><span class="w"> </span><span class="n">frame_duration</span><span class="w">
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w">    </span><span class="k">for</span><span class="w"> </span><span class="p">;</span><span class="n">accumulator</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="n">DELTA</span><span class="p">;</span><span class="w"> </span><span class="n">accumulator</span><span class="w"> </span><span class="o">-=</span><span class="w"> </span><span class="n">DELTA</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w">        </span><span class="n">runtime</span><span class="p">.</span><span class="n">mem_copy_non_overlapping</span><span class="p">(</span><span class="o">&amp;</span><span class="n">prev_game</span><span class="p">,</span><span class="w"> </span><span class="o">&amp;</span><span class="n">game</span><span class="p">,</span><span class="w"> </span><span class="n">size_of</span><span class="p">(</span><span class="n">Game</span><span class="p">))</span><span class="w">
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="w">        </span><span class="n">game_tick</span><span class="p">(</span><span class="o">&amp;</span><span class="n">game</span><span class="p">,</span><span class="w"> </span><span class="n">DELTA</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="w">    </span><span class="c1">// Remainder to go from fixed time to real time.
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="w">    </span><span class="c1">// Always in 0..1 range.
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="w">    </span><span class="n">alpha</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="n">accumulator</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="n">DELTA</span><span class="w">
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="w">    </span><span class="c1">// Interpolates between previous and current game state with the alpha factor.
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="w">    </span><span class="n">game_draw</span><span class="p">(</span><span class="n">game</span><span class="p">,</span><span class="w"> </span><span class="n">prev_game</span><span class="p">,</span><span class="w"> </span><span class="n">alpha</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><h1 id="can-we-do-better">Can we do better?</h1>
<p>The fundamental issue is that the fixed timestep simulation does as many timesteps as possible, however because the frame-rate is variable, the real time doesn&rsquo;t always match the fixed time.</p>
<p>So the solution is very simple, after the fixed timestep simulation is done, we need to simulate one more additional tick with the remaining time. However this additional tick could mess up our game state, because the time step is no longer fixed - this is a problem especially for things like physics.</p>
<h2 id="render-tick">Render Tick</h2>
<p>For this reason we <strong>duplicate</strong> the entire game state into a new, temporary render-only game state, and simulate the tick on this one. That way the fixed timestep game state stays untouched. So we <strong>&ldquo;predict&rdquo;</strong> what&rsquo;s gonna happen at a particular point in time between current fixed tick and the next one.</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-odin" data-lang="odin"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="n">DELTA</span><span class="w"> </span><span class="o">::</span><span class="w"> </span><span class="nx">1</span><span class="mf">.0</span><span class="w"> </span><span class="o">/</span><span class="w"> </span><span class="nx">60</span><span class="mf">.0</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="n">accumulator</span><span class="o">:</span><span class="w"> </span><span class="kt">f32</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="n">game</span><span class="o">:</span><span class="w"> </span><span class="n">Game</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="n">temp_game</span><span class="o">:</span><span class="w"> </span><span class="n">Game</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="n">prev_time</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="n">time</span><span class="p">.</span><span class="n">tick_now</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="k">for</span><span class="w"> </span><span class="err">!</span><span class="n">quit</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w">    </span><span class="n">frame_duration</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="kt">f32</span><span class="p">(</span><span class="n">time</span><span class="p">.</span><span class="n">duration_seconds</span><span class="p">(</span><span class="n">time</span><span class="p">.</span><span class="n">tick_since</span><span class="p">(</span><span class="n">prev_time</span><span class="p">)))</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w">    </span><span class="n">prev_time</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">time</span><span class="p">.</span><span class="n">tick_now</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w">    </span><span class="n">accumulator</span><span class="w"> </span><span class="o">+=</span><span class="w"> </span><span class="n">frame_duration</span><span class="w">
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w">    </span><span class="k">for</span><span class="w"> </span><span class="p">;</span><span class="n">accumulator</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="n">DELTA</span><span class="p">;</span><span class="w"> </span><span class="n">accumulator</span><span class="w"> </span><span class="o">-=</span><span class="w"> </span><span class="n">DELTA</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w">        </span><span class="c1">// Simulate the game state (without keeping previous game state!)
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="w">        </span><span class="n">game_tick</span><span class="p">(</span><span class="o">&amp;</span><span class="n">game</span><span class="p">,</span><span class="w"> </span><span class="n">DELTA</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="w">    </span><span class="c1">// Remainder to go from fixed time to real time.
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="w">    </span><span class="c1">// Always in 0..DELTA range.
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="w">    </span><span class="n">alpha</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="n">accumulator</span><span class="w">
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="w">    </span><span class="c1">// &#34;Create&#34; a duplicate version of the game state so our
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="w">    </span><span class="c1">// &#34;render tick&#34; doesn&#39;t affect the real game state.
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="w">    </span><span class="n">runtime</span><span class="p">.</span><span class="n">mem_copy_non_overlapping</span><span class="p">(</span><span class="o">&amp;</span><span class="n">temp_game</span><span class="p">,</span><span class="w"> </span><span class="o">&amp;</span><span class="n">game</span><span class="p">,</span><span class="w"> </span><span class="n">size_of</span><span class="p">(</span><span class="n">Game</span><span class="p">))</span><span class="w">
</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="w">    </span><span class="c1">// Simulate the temp state forward in time to match reality.
</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="w">    </span><span class="n">game_tick</span><span class="p">(</span><span class="o">&amp;</span><span class="n">temp_game</span><span class="p">,</span><span class="w"> </span><span class="n">alpha</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="w">    </span><span class="c1">// No need to do any interpolation now, just render the game state as it is.
</span></span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="w">    </span><span class="c1">// It matches real time pretty much perfectly.
</span></span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="w">    </span><span class="n">game_draw</span><span class="p">(</span><span class="n">temp_game</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><blockquote>
<p>Note:
This method probably won&rsquo;t work very well in cases when <code>game_tick</code> can take a very significant amount of time, or the game state is gigantic, e.g. in a big AAA game which needs to run the physics engine and whatnot.</p>
</blockquote>
<h2 id="game-state-duplication">Game state duplication</h2>
<p>In a more traditional system with individually dynamically allocated objects this might be a bit of an issue.
Even if your language can do a <em>deep copy</em> of an object which holds the game state, the individual allocations could take a long time or it could make the GC sad.</p>
<p>In my case the <em>entire</em> game state is trivially copyable, because:</p>
<ol>
<li>I never store any pointers. ever. Only integer indexes or handles.</li>
<li>All of the datastructures are fixed size, no dynamic allocations.</li>
</ol>
<p>That means I can easily do <code>memcpy</code> and get a duplicate game state without any issues. This setup makes things a whole lot easier to deal with.</p>
<h2 id="what-about-input">What about input?</h2>
<p>Just like the last time, the <code>game_tick</code> needs to get the input from somewhere. This is easy, because the main update loop is pretty much the same like in the interpolation case! So the solution from my original post still applies, you can just use a separate <code>frame_input</code> and a <code>tick_input</code>.</p>
<p>But what about the temp render tick?</p>
<p>But I found re-applying the <code>tick_input</code> in exactly the same way as in the fixed update loop works decently well. You also want to clear the &ldquo;pressed&rdquo; and &ldquo;released&rdquo; flags from the input, because those were already applied in the fixed update loop. If you use my implementation from previous article you don&rsquo;t need to do this directly, the fixed update loop does this on it&rsquo;s own anyway.</p>
<p>So here is how to accumulate the input states every frame:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-odin" data-lang="odin"><span class="line"><span class="ln">1</span><span class="cl"><span class="n">tick_input</span><span class="p">.</span><span class="n">cursor</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">frame_input</span><span class="p">.</span><span class="n">cursor</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="c1">// _accumulate_ temp flags instead of overwriting
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="k">for</span><span class="w"> </span><span class="n">flags</span><span class="p">,</span><span class="w"> </span><span class="n">action</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="n">frame_input</span><span class="p">.</span><span class="n">actions</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="w">    </span><span class="c1">// Bitwise OR the bit flags
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="w">    </span><span class="n">tick_input</span><span class="p">.</span><span class="n">actions</span><span class="p">[</span><span class="n">action</span><span class="p">]</span><span class="w"> </span><span class="o">+=</span><span class="w"> </span><span class="n">flags</span><span class="w">
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><p>And this is how to apply it. Note how the temp flags are cleared after every tick, and all flags are cleared at the end of a frame when any tick happened.</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-odin" data-lang="odin"><span class="line"><span class="ln">1</span><span class="cl"><span class="n">any_tick</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="n">accumulator</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="n">delta</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="k">for</span><span class="w"> </span><span class="p">;</span><span class="n">accumulator</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="n">delta</span><span class="p">;</span><span class="w"> </span><span class="n">accumulator</span><span class="w"> </span><span class="o">-=</span><span class="w"> </span><span class="n">delta</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="w">    </span><span class="n">game_tick</span><span class="p">(</span><span class="o">&amp;</span><span class="n">game</span><span class="p">,</span><span class="w"> </span><span class="n">tick_input</span><span class="p">,</span><span class="w"> </span><span class="n">delta</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="w">    </span><span class="n">input_clear_temp</span><span class="p">(</span><span class="o">&amp;</span><span class="n">tick_input</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="n">runtime</span><span class="p">.</span><span class="n">mem_copy_non_overlapping</span><span class="p">(</span><span class="o">&amp;</span><span class="n">temp_game</span><span class="p">,</span><span class="w"> </span><span class="o">&amp;</span><span class="n">game</span><span class="p">,</span><span class="w"> </span><span class="n">size_of</span><span class="p">(</span><span class="n">Game</span><span class="p">))</span><span class="w">
</span></span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="n">game_tick</span><span class="p">(</span><span class="o">&amp;</span><span class="n">temp_game</span><span class="p">,</span><span class="w"> </span><span class="n">tick_input</span><span class="p">,</span><span class="w"> </span><span class="n">accumulator</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="n">game_draw</span><span class="p">(</span><span class="n">temp_game</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">9</span><span class="cl"><span class="k">if</span><span class="w"> </span><span class="n">any_tick</span><span class="w"> </span><span class="k">do</span><span class="w"> </span><span class="n">tick_input</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{}</span></span></span></code></pre></div><p>(see the <a href="/posts/fixed_timestep_without_interpolation/#demo">Demo</a> source code for the full implementation)</p>
<h2 id="different-tps-is-a-fundamentally-different-problem">Different TPS is a fundamentally different problem</h2>
<p>Something I&rsquo;ve realized while working on this post and the Demo is how different the problem you&rsquo;re trying to solve is, depending on your TPS. So here is what I think is a good way to roughly categorize it:</p>
<ul>
<li>
<p>~10 TPS: Here the way you render the in-between game states matters <em>a lot</em>. Latency from interpolation is <em>huge</em> but the predicted render ticks can be jittery because of different simulation results. And the exact way you accumulate inputs and how you pass them in the game matters a lot as well. This is completely unusable for any kind of real time action game.</p>
</li>
<li>
<p>30-60 TPS: This is the most common for regular games. In this case the render tick method works really well, because there is almost no jitter due to input sampling inaccuracies. But the smoothing is still very visible. The added latency from interpolation is not <em>that</em> noticeable. However if you&rsquo;re making a fast-paced action game, you probably want to eliminate every millisecond of unnecessary latency.</p>
</li>
<li>
<p>~120+ TPS: At this point any kind of interpolation accounts for only a tiny fraction of a second. The only reason you might want to use it to remove some jitter, but it&rsquo;s just not very significant.</p>
</li>
</ul>
<h3 id="accumulated-render-tick">Accumulated render tick</h3>
<p>In case you wanted to run only a very small number of fixed update ticks per second, the basic render tick method described above has some limitations. The temporary render tick always uses the last fixed tick, which means as your fixed delta increases your render tick might be doing a single, very large time step.</p>
<blockquote>
<p>This is not an issue in most singleplayer PC games. That&rsquo;s because you <em>probably</em> want to simulate &gt;30 ticks per second anyway, for stability reasons and just because there&rsquo;s no reason to push TPS lower.</p>
</blockquote>
<p>The solution is to copy game state into temp game state <em>only</em> if you simulated a fixed tick that frame. Otherwise you keep simulating the old temp game state. This also means you need to keep another separate remainder timer, because alpha is no longer valid for each render tick.</p>
<p>This is similar to client-side prediction in real-time multiplayer games.</p>
<p>One issue is jitter, the difference in frequency of input between fixed and predicted ticks is so significant the render game state can get out of sync. One solution is to interpolate to sync the game states instead of doing that instantly. Alternatively you could modify the render tick inputs to eliminate high frequency inputs, and maybe even completely disable &ldquo;pressed&rdquo; and &ldquo;released&rdquo; key events.</p>
<p>I don&rsquo;t do this in my game, simply because I didn&rsquo;t run into issues with the current, simpler approach. But I thought I should mention it anyway.</p>
<h1 id="demo">Demo</h1>
<p>I wrote a small demo game in Odin and Raylib to demonstrate the different fixed timestep rendering methods.</p>
<p><strong>See the <a href="https://github.com/jakubtomsu/fixed-timestep-demo">Github Repository</a> for source code.</strong></p>
<p>Here is how the various simulation modes look like when running at 4 ticks per second. This is a very low TPS only for demonstration purposes.</p>
<p>Without any kind of smoothing at all the game is very choppy:</p>
<p><img src="/posts/fixed_timestep_without_interpolation/no_smoothing.gif" alt="no smoohting"></p>
<p>The regular interpolation looks smooth but it&rsquo;s a bit delayed (the &ldquo;onion skinning&rdquo; shows the current game state drawn as green, previous as red):</p>
<p><img src="/posts/fixed_timestep_without_interpolation/interpolated.gif" alt="inerpolation"></p>
<p>This is the regular render tick method, it looks fairly smooth and importantly there is no lag:</p>
<p><img src="/posts/fixed_timestep_without_interpolation/render_tick.gif" alt="render tick"></p>
<p>Here is a very high TPS simulation just for comparison:</p>
<p><img src="/posts/fixed_timestep_without_interpolation/reference.gif" alt="reference"></p>
<h2 id="overview">Overview</h2>
<p>Let&rsquo;s summarize the all the different methods from the demo and how they compare to each other. There is no clear winner, it depends on your engine, game and your circumstances.</p>
<ul>
<li>
<p>Interpolation: Always smooth. Needs a way to interpolate two game states, this can be a pain to implement. Always lags one tick behind - this means worse latency, especially with low TPS.</p>
</li>
<li>
<p>Render Tick: No added latency, matches real time &ldquo;perfectly&rdquo;. Very easy to implement, especially if your game state and tick is set up in a nice way. However the single render tick can get inaccurate with low TPS because each update step is linear.</p>
</li>
<li>
<p>Accumulated Render Tick: More accurate in low TPS simulations, however because of the input frequency is so different between the predicted and the fixed ticks it can get out of sync.</p>
</li>
</ul>
<blockquote>
<p>In my case, I&rsquo;m making a fast paced singleplayer game and I have no issue setting the TPS to 30 or even 60. For this reason the Render Tick is by far the best fit.</p>
</blockquote>
<h1 id="tangent-determinism-and-replay">Tangent: determinism and replay</h1>
<p>This isn&rsquo;t related to the main topic of this post, because it applies to any kind of gameplay where the timestep is fixed (it doesn&rsquo;t matter whether you use prediction or interpolation). But if your gameplay is deterministic, meaning you always get the same game state T+1 by simulating game state T with the same input, there is a neat trick to do game replay.</p>
<p>Regular replay systems would have to periodically store the game state, which ends up consuming a lot of memory, even if you try to compress it. But if your game is deterministic you only need to store the <em>starting</em> game state (or even just the starting settings to generate the game state), and then inputs for all the fixed timesteps. Then when you can just simulate the original game state N times to get any tick you want, once you need to do the replay.</p>
<p>By the way, this is what Media Molecule does in Dreams. The Trackmania racing games do this as well, to verify runs and make sure people aren&rsquo;t cheating. Even their 3d physics engine is fully deterministic! very cool stuff.</p>
<blockquote>
<p>Notes:</p>
<ol>
<li>You need to make sure entities are always updated in the same order. This means deterministic O(1) datastructures like pools are your friend.</li>
<li>If you use random numbers then you need to make sure the seeds match at the start of every tick as well. You can probably get by storing only one seed along with the first</li>
<li>The stored replay gets invalidated once you change your gameplay logic, so this method is generally useful for debugging only.</li>
</ol>
</blockquote>
<h1 id="thats-all">That&rsquo;s all!</h1>
<p>Thank you for reading, I hope this can be useful in your engine/game project! Also let me know if you have any feedback, if you know how to improve any of the methods I explained here, or you know of an even better method:)</p>
<h2 id="credit">Credit</h2>
<p>After I published the original blog post, a number of people messaged me it helped them with use fixed timestep update loops in their game/engine as well. However some people complained specifically about the interpolation step, it can be kinda cumbersome to do, especially if you want to interpolate more properties than just the entity transform.</p>
<p>I was told about the render tick method by Radek Jurga on discord, a fellow handmade programmer. He uses it in his engine as well! And he found out about it from an old <a href="https://youtu.be/fdAOPHgW7qM?si=chCqgUTNoOLuHDvy">Jonathan Blow Q&amp;A stream</a> which is a much more in-depth explanation of this entire topic.</p>
]]></content:encoded></item><item><title>Logo Design behind-the-scenes</title><link>https://jakubtomsu.github.io/posts/logo_design_tutorial/</link><pubDate>Wed, 11 Sep 2024 00:00:00 +0000</pubDate><guid>https://jakubtomsu.github.io/posts/logo_design_tutorial/</guid><description>&lt;blockquote&gt;
&lt;p&gt;Note: this was initially supposed to be my first supporter-only Patreon post.
But their tools for writing posts kinda suck so I thought hey, let&amp;rsquo;s just release it for free.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Anyway it would mean a lot to me if you decided to support me anyway! Here is &lt;a href="https://www.patreon.com/jakubtomsu"&gt;my Patreon link&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is an behind-the-scenes look at my design process for the first iteration of a logo for my new game, BEHEADER.&lt;/p&gt;</description><content:encoded><![CDATA[<blockquote>
<p>Note: this was initially supposed to be my first supporter-only Patreon post.
But their tools for writing posts kinda suck so I thought hey, let&rsquo;s just release it for free.</p>
</blockquote>
<blockquote>
<p>Anyway it would mean a lot to me if you decided to support me anyway! Here is <a href="https://www.patreon.com/jakubtomsu">my Patreon link</a></p>
</blockquote>
<p>This is an behind-the-scenes look at my design process for the first iteration of a logo for my new game, BEHEADER.</p>
<p>I use Krita, it&rsquo;s a pretty good and free photoshop alternative. It doesn&rsquo;t have all of the fancy features but there are enough filters for me to do all I need.</p>
<h2 id="basic-shape">Basic Shape</h2>
<p>I started with a basic text with gothic font (Old English Text MT). This serves as a base for everything else. I duplicated the layer and &ldquo;flattened&rdquo; it so I can work with the pixels instead of the vector representation.</p>
<p><img src="/posts/logo_design_tutorial/src.png" alt="source"></p>
<p>Then I added distortions using the transform tool. First I added a bit of perspective to make the bottom larger, then I used the warp tool to make the center curve up a bit. And then I used the liquify tool as a brush to distort it even more and add those spiky details.</p>
<p><img src="/posts/logo_design_tutorial/transform.png" alt="transformed"></p>
<p>Here is what it looks like after all those distortions:</p>
<p><img src="/posts/logo_design_tutorial/warped.png" alt="warped"></p>
<h2 id="shading">Shading</h2>
<p>Now the shape looks good, let&rsquo;s add the colors. I wanted the reflective chrome effect with lighting etc, so I started by generating a normal map.</p>
<p><img src="/posts/logo_design_tutorial/normals.png" alt="normals"></p>
<p>But the default normal map is kinda flat, so let&rsquo;s just use the Gaussian Blur filter to smoothen it out a bit.</p>
<p><img src="/posts/logo_design_tutorial/blur_normals.png" alt="blurred normals"></p>
<p>And since I only care about the &ldquo;Y-axis normal&rdquo;, I removed the red and blue channels, and then desaturated it to get this black and white texture:</p>
<p><img src="/posts/logo_design_tutorial/normal_y.png" alt="Y normals"></p>
<p>Okay, that&rsquo;s all we need to start doing the shading. I used a Gradient Map filter to map specific brightness to some cool colors.</p>
<p>I also used dithering with a grunge texture instead of the default linear interpolation. This depends on the style you&rsquo;re going for.</p>
<blockquote>
<p>Pro tip: you can click the &ldquo;Create Filter Mask&rdquo; button in the filter settings to create a non-destructive filter layer.</p>
</blockquote>
<p><img src="/posts/logo_design_tutorial/gradient_opts.png" alt="gradient options"></p>
<p>And here is what it looks like with the gradient:</p>
<p><img src="/posts/logo_design_tutorial/gradient.png" alt="gradient"></p>
<h2 id="finishing">Finishing</h2>
<p>But because the normal map and blur filters changed the text borders, we need to somehow mask it to make the edges crisp again. I did this by duplicating the original white texture and inverting the alpha (with color curves). Then you place this layer above all other layers and it clips everything correctly.</p>
<p><img src="/posts/logo_design_tutorial/mask.png" alt="mask"></p>
<p>And this is the final result! I&rsquo;m happy with it, especially since it&rsquo;s the first iteration of the logo. I still plan on improving it before I launch the Steam Page. I think I would like to make it all caps instead, and also improve the readability a bit.</p>
<p><img src="/posts/logo_design_tutorial/final.png" alt="final"></p>
<h2 id="alternative-shading-method-blinn-phong-lights">Alternative shading method: blinn Phong lights</h2>
<p>Here is anoter color combination I made. Instead of gradient map I used the phong shading filter which lets you set up lights from various directions.
<img src="/posts/logo_design_tutorial/blinn_phong.png" alt="blinn phong"></p>
<p>I used the following settings to sort of a metal material.
<img src="/posts/logo_design_tutorial/blinn_phong_opts.png" alt="blinn phong options"></p>
<p>And here is the settings for light sources, many possibilities here
<img src="/posts/logo_design_tutorial/blinn_phong_lights.png" alt="bling phong lights"></p>
]]></content:encoded></item><item><title>Reliable fixed timestep &amp; inputs</title><link>https://jakubtomsu.github.io/posts/input_in_fixed_timestep/</link><pubDate>Sun, 04 Aug 2024 00:00:00 +0000</pubDate><guid>https://jakubtomsu.github.io/posts/input_in_fixed_timestep/</guid><description>&lt;p&gt;&lt;a href="https://www.gafferongames.com/post/fix_your_timestep/"&gt;Fix your Timestep!&lt;/a&gt; is an amazing article by Glenn Fiedler about fixed timestep update loops for games and physics simulations.
It explains how to properly accumulate a timer and run ticks with a fixed delta time within your main loop.&lt;/p&gt;
&lt;p&gt;But it seems like it&amp;rsquo;s mostly targeted at physics simulations, and it doesn&amp;rsquo;t cover a few things you need to make your entire game run on fixed timesteps. For example, how to handle input polling properly! That&amp;rsquo;s what I&amp;rsquo;ll try to cover today, as well as some reasoning behind this game architecture.&lt;/p&gt;</description><content:encoded><![CDATA[<p><a href="https://www.gafferongames.com/post/fix_your_timestep/">Fix your Timestep!</a> is an amazing article by Glenn Fiedler about fixed timestep update loops for games and physics simulations.
It explains how to properly accumulate a timer and run ticks with a fixed delta time within your main loop.</p>
<p>But it seems like it&rsquo;s mostly targeted at physics simulations, and it doesn&rsquo;t cover a few things you need to make your entire game run on fixed timesteps. For example, how to handle input polling properly! That&rsquo;s what I&rsquo;ll try to cover today, as well as some reasoning behind this game architecture.</p>
<p>I use a slightly modified version of the update loop from Gaffer&rsquo;s article, here is a simplified version in Odin:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="n">DELTA</span> <span class="o">::</span> <span class="mf">1.0</span> <span class="o">/</span> <span class="mf">60.0</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="nl">accumulator</span><span class="p">:</span> <span class="n">f32</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="nl">game</span><span class="p">:</span> <span class="n">Game</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="nl">prev_game</span><span class="p">:</span> <span class="n">Game</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="nl">prev_time</span> <span class="p">:</span><span class="o">=</span> <span class="n">time</span><span class="p">.</span><span class="nf">tick_now</span><span class="p">()</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="k">for</span> <span class="o">!</span><span class="n">quit</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">    <span class="nl">frame_duration</span> <span class="p">:</span><span class="o">=</span> <span class="nf">f32</span><span class="p">(</span><span class="n">time</span><span class="p">.</span><span class="nf">duration_seconds</span><span class="p">(</span><span class="n">time</span><span class="p">.</span><span class="nf">tick_since</span><span class="p">(</span><span class="n">prev_time</span><span class="p">)))</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">    <span class="n">prev_time</span> <span class="o">=</span> <span class="n">time</span><span class="p">.</span><span class="nf">tick_now</span><span class="p">()</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">    <span class="n">accumulator</span> <span class="o">+=</span> <span class="n">frame_duration</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">    <span class="nl">num_ticks</span> <span class="p">:</span><span class="o">=</span> <span class="kt">int</span><span class="p">(</span><span class="nf">floor</span><span class="p">(</span><span class="n">accumulator</span> <span class="o">/</span> <span class="n">DELTA</span><span class="p">))</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">    <span class="n">accumulator</span> <span class="o">-=</span> <span class="nf">f32</span><span class="p">(</span><span class="n">num_ticks</span><span class="p">)</span> <span class="o">*</span> <span class="n">DELTA</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">    <span class="k">for</span> <span class="n">tick_index</span> <span class="n">in</span> <span class="mi">0</span> <span class="p">..</span><span class="o">&lt;</span> <span class="n">num_ticks</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">        <span class="n">runtime</span><span class="p">.</span><span class="nf">mem_copy_non_overlapping</span><span class="p">(</span><span class="o">&amp;</span><span class="n">prev_game</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">game</span><span class="p">,</span> <span class="nf">size_of</span><span class="p">(</span><span class="n">Game</span><span class="p">))</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">        <span class="nf">game_tick</span><span class="p">(</span><span class="o">&amp;</span><span class="n">game</span><span class="p">,</span> <span class="n">prev_game</span><span class="p">,</span> <span class="n">input</span><span class="p">,</span> <span class="n">DELTA</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">    <span class="n">alpha</span> <span class="o">=</span> <span class="n">accumulator</span> <span class="o">/</span> <span class="n">DELTA</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">    <span class="nf">game_draw</span><span class="p">(</span><span class="n">game</span><span class="p">,</span> <span class="n">prev_game</span><span class="p">,</span> <span class="n">alpha</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><p>The main difference is that I calculate number of ticks from the accumulator, instead of running a while loop and subtracting delta from the accumulator directly.
I find this a bit nicer but there isn&rsquo;t much difference.</p>
<blockquote>
<p>Note: if you use floating point numbers you might run into float cancellation precision issues. An alternative is to use 64 bit ints to store nanoseconds directly and do all computations with those. And then convert the delta to float later once you need it. Thanks to Graphite for telling me about this!</p>
</blockquote>
<h2 id="how-engines-separate-update-and-physics-ticks">How engines separate update and physics ticks</h2>
<p>It&rsquo;s fairly common for game engines to separate regular ticks and physics ticks. For example, unity components have an <code>Update</code> function which runs every frame and <code>FixedUpdate</code> function which runs 50 times a second (by default) during physics tick.</p>
<p>I really don&rsquo;t like this separation.</p>
<p>It often makes gameplay code which interacts with physics much more convoluted, because it forces you to split your update logic into two places.
It can be very confusing for beginner programmers.
And in general setting up your simulation this way makes it easier to write frame-rate dependent and less stable gameplay code.</p>
<h2 id="a-single-fixed-timestep-tick-for-both-gameplay-and-physics">A single fixed timestep tick for both gameplay and physics</h2>
<p>So what&rsquo;s the alternative?</p>
<p>To have one big <code>tick</code> procedure which updates <em>everything</em> in the game, including gameplay, physics, etc.
And then run this tick in a fixed update loop.</p>
<p>This way the game simulation is completely independent from &ldquo;frames&rdquo; and rendering.</p>
<p>It could be complicated to do this in an already existing engine, or if you use one of the common rigidbody physics engines.
But I have a custom engine and custom collision system, so I can set it up however I like :P</p>
<h2 id="interpolating-everything">Interpolating everything</h2>
<p>Okay, what about rendering then?</p>
<p>You <em>could</em> just run your ticks in a fixed update loop, and then rendered the final game state every frame.
However the &ldquo;tick time&rdquo; and &ldquo;frame time&rdquo; doesn&rsquo;t match perfectly. And in cases when the frame rate is much higher than tick rate, the game could look very laggy.</p>
<p>The solution is interpolation.</p>
<p>Now <code>game_draw</code> takes two game states, and interpolates between them based on the alpha factor.
Here is an example of interpolating the camera and enemies:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="n">game_draw</span> <span class="o">::</span> <span class="nf">proc</span><span class="p">(</span><span class="n">game</span><span class="p">,</span> <span class="nl">prev_game</span><span class="p">:</span> <span class="n">Game</span><span class="p">,</span> <span class="nl">alpha</span><span class="p">:</span> <span class="n">f32</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">    <span class="nl">camera</span> <span class="p">:</span><span class="o">=</span> <span class="n">Camera</span><span class="p">{</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">        <span class="n">pos</span> <span class="o">=</span> <span class="nf">lerp</span><span class="p">(</span><span class="n">prev_game</span><span class="p">.</span><span class="n">camera</span><span class="p">.</span><span class="n">pos</span><span class="p">,</span> <span class="n">game</span><span class="p">.</span><span class="n">camera</span><span class="p">.</span><span class="n">pos</span><span class="p">,</span> <span class="n">alpha</span><span class="p">),</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">        <span class="n">rot</span> <span class="o">=</span> <span class="nf">slerp</span><span class="p">(</span><span class="n">prev_game</span><span class="p">.</span><span class="n">camera</span><span class="p">.</span><span class="n">rot</span><span class="p">,</span> <span class="n">game</span><span class="p">.</span><span class="n">camera</span><span class="p">.</span><span class="n">rot</span><span class="p">,</span> <span class="n">alpha</span><span class="p">),</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">    <span class="k">for</span> <span class="n">curr_enemy</span><span class="p">,</span> <span class="n">i</span> <span class="n">in</span> <span class="n">game</span><span class="p">.</span><span class="n">enemies</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">        <span class="nl">prev_enemy</span> <span class="p">:</span><span class="o">=</span> <span class="n">prev_game</span><span class="p">.</span><span class="n">enemies</span><span class="p">[</span><span class="n">i</span><span class="p">]</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">        <span class="nl">enemy</span> <span class="p">:</span><span class="o">=</span> <span class="n">curr_enemy</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">        <span class="n">enemy</span><span class="p">.</span><span class="n">transform</span> <span class="o">=</span> <span class="nf">lerp_transform</span><span class="p">(</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">            <span class="n">prev_enemy</span><span class="p">.</span><span class="n">transform</span><span class="p">,</span> <span class="n">enemy</span><span class="p">.</span><span class="n">transform</span><span class="p">,</span> <span class="n">alpha</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">        <span class="nf">draw_model</span><span class="p">(.</span><span class="n">Grunt_Enemy</span><span class="p">,</span> <span class="n">enemy</span><span class="p">.</span><span class="n">transform</span><span class="p">,</span> <span class="n">COLOR_RED</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><p>The same concept applies to everything else, you can interpolate whatever properties you need.
You could probably even automate it in a proper entity system, or if you use a scripting language.</p>
<h2 id="double-buffered-game-state">Double-buffered game state</h2>
<p>As you can see there are two copies of entire game state.
This makes it easy to do interpolation, and you can also use the previous game state for many things in gameplay code as well.</p>
<p>I initially got this idea from Dreams by Media Molecule.
It&rsquo;s a really smart engine architecture, which Alex Evans explained in one of their <a href="https://youtu.be/1Gce4l5orts?si=TMOJP0Bt_jxXazIm">streams</a>.</p>
<p>If you use fixed size datastructures and never dynamically allocate memory like I do, copying the game state is just a single big <code>memcpy</code> once per tick.
But in case you want to use dynamic memory, you could use <a href="https://www.rfleury.com/p/untangling-lifetimes-the-arena-allocator">arena allocators</a> or some kind of memory pools to make copying game state trivial as well.</p>
<h2 id="what-about-input">What about input?</h2>
<p>As you can see the <code>game_tick</code> procedure takes an <code>input</code> parameter, but where did that come from?
Glenn&rsquo;s article doesn&rsquo;t cover this, probably because he&rsquo;s mostly concerned with physics simulations. But in our case it&rsquo;s crucial to get the correct results.</p>
<p>Just for context, here is how my (simplified) input state looks like. This isn&rsquo;t very important, but note I don&rsquo;t use an &ldquo;input event queue&rdquo; in gameplay code. I just use procedures like <code>input_is_key_down(inp, .Left_Shift)</code> etc.</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="n">Input</span> <span class="o">::</span> <span class="k">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">    <span class="nl">cursor_pos</span><span class="p">:</span>    <span class="n">Vec2</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">    <span class="nl">cursor_delta</span><span class="p">:</span>  <span class="n">Vec2</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl">    <span class="nl">scroll_delta</span><span class="p">:</span>  <span class="n">f32</span><span class="p">,</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">    <span class="nl">mouse_buttons</span><span class="p">:</span> <span class="p">[</span><span class="n">Input_Mouse_Button</span><span class="p">]</span><span class="n">bit_set</span><span class="p">[</span><span class="n">Input_Digital_Flags</span><span class="p">],</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">    <span class="nl">keys</span><span class="p">:</span>          <span class="p">[</span><span class="n">Input_Key</span><span class="p">]</span><span class="n">bit_set</span><span class="p">[</span><span class="n">Input_Digital_Flags</span><span class="p">],</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="n">Input_Digital_Flags</span> <span class="o">::</span> <span class="k">enum</span> <span class="n">u8</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">    <span class="n">Down</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">    <span class="n">Changed_This_Frame</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">    <span class="n">Repeated</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">    <span class="n">Multiple_Changes_This_Frame</span><span class="p">,</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><p>Okay, now let&rsquo;s see how to pass the inputs into the <code>game_tick</code> procedure.</p>
<p>The naive solution is to just pass your regular input state like you would normally:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="nl">frame_input</span><span class="p">:</span> <span class="n">Input</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1">// ...
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl">
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="k">for</span> <span class="o">!</span><span class="n">quit</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">    <span class="k">for</span> <span class="n">event</span> <span class="n">in</span> <span class="n">events</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">    <span class="k">case</span> <span class="p">.</span><span class="nl">Key_Down</span><span class="p">:</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">        <span class="nf">input_add_key_down</span><span class="p">(</span><span class="o">&amp;</span><span class="n">frame_input</span><span class="p">,</span> <span class="n">key</span><span class="p">)</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">    <span class="c1">// handle other (input) events...
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl">    <span class="c1">// ...
</span></span></span><span class="line"><span class="ln">12</span><span class="cl">        <span class="nf">game_tick</span><span class="p">(</span><span class="o">&amp;</span><span class="n">game</span><span class="p">,</span> <span class="n">prev_game</span><span class="p">,</span> <span class="n">frame_input</span><span class="p">,</span> <span class="n">DELTA</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl">    <span class="c1">// ...
</span></span></span><span class="line"><span class="ln">14</span><span class="cl">
</span></span><span class="line"><span class="ln">15</span><span class="cl">    <span class="c1">// Clear input flags for things like &#34;pressed&#34;, &#34;released&#34; etc.
</span></span></span><span class="line"><span class="ln">16</span><span class="cl">    <span class="c1">// but keep &#34;down&#34; states
</span></span></span><span class="line"><span class="ln">17</span><span class="cl">    <span class="nf">input_clear_temp</span><span class="p">(</span><span class="o">&amp;</span><span class="n">frame_input</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><p>But, this has a number of problems:</p>
<ol>
<li>Inputs are lost when there are no ticks to run for few frames.</li>
<li>Mouse delta is applied multiple times</li>
<li>Key states which should only be valid for one frame (like Pressed and Released) are passed into <code>game_tick</code> multiple times.</li>
</ol>
<h3 id="solution-1-poll-inputs-every-tick">Solution 1: Poll inputs every tick</h3>
<p>This might technically be the &ldquo;best&rdquo; solution to this problem, because you also get the most accurate results this way.
However it might not be practical because your app only has one event loop, and you need regular frame inputs too.
You could probably poll events and then &ldquo;save&rdquo; non-input ones for later, but IMO it&rsquo;s probably unnecessary for many games.</p>
<h3 id="solution-2-accumulate-tick-inputs-separately">Solution 2: Accumulate tick inputs separately</h3>
<p>This is what I went with, because it&rsquo;s simple and the accuracy loss from not polling inputs every tick isn&rsquo;t noticeable.</p>
<p>The idea is to keep <em>two</em> separate input states, one for &ldquo;per frame&rdquo; stuff and the other one for ticks.
Both are updated with the same events at the start of your frame, but the state is updated in a different way during the frame.</p>
<p>First idea is to discard temporary key flags (like Pressed and Released) after every tick, so they aren&rsquo;t applied multiple times.</p>
<p>Then, we can also divide the mouse delta by number of ticks to apply it over time.
Alternatively you could skip this and set the delta to zero after every tick, but I think splitting the delta over multiple ticks is nicer.
Finally, you clear the rest of temporary input data once you ran all your ticks.</p>
<p>Here is the update loop example code:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-c" data-lang="c"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="n">DELTA</span> <span class="o">::</span> <span class="mf">1.0</span> <span class="o">/</span> <span class="mf">60.0</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="nl">frame_input</span><span class="p">:</span> <span class="n">Input</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="nl">tick_input</span><span class="p">:</span> <span class="n">Input</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl">
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="nl">accumulator</span><span class="p">:</span> <span class="n">f32</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="nl">game</span><span class="p">:</span> <span class="n">Game</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="nl">prev_game</span><span class="p">:</span> <span class="n">Game</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="nl">prev_time</span> <span class="p">:</span><span class="o">=</span> <span class="n">time</span><span class="p">.</span><span class="nf">tick_now</span><span class="p">()</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="k">for</span> <span class="o">!</span><span class="n">quit</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl">    <span class="c1">// Accumulates the input events to both frame and tick input states
</span></span></span><span class="line"><span class="ln">13</span><span class="cl">    <span class="k">for</span> <span class="n">event</span> <span class="n">in</span> <span class="n">events</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">    <span class="k">case</span> <span class="p">.</span><span class="nl">Key_Down</span><span class="p">:</span>
</span></span><span class="line"><span class="ln">15</span><span class="cl">        <span class="nf">input_add_key_down</span><span class="p">(</span><span class="o">&amp;</span><span class="n">frame_input</span><span class="p">,</span> <span class="n">event</span><span class="p">.</span><span class="n">key</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl">        <span class="nf">input_add_key_down</span><span class="p">(</span><span class="o">&amp;</span><span class="n">tick_input</span><span class="p">,</span> <span class="n">eveny</span><span class="p">.</span><span class="n">key</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl">    <span class="k">case</span> <span class="p">.</span><span class="nl">Key_Up</span><span class="p">:</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl">        <span class="nf">input_add_key_up</span><span class="p">(</span><span class="o">&amp;</span><span class="n">frame_input</span><span class="p">,</span> <span class="n">event</span><span class="p">.</span><span class="n">key</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl">        <span class="nf">input_add_key_up</span><span class="p">(</span><span class="o">&amp;</span><span class="n">tick_input</span><span class="p">,</span> <span class="n">event</span><span class="p">.</span><span class="n">key</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">    <span class="c1">// case ...
</span></span></span><span class="line"><span class="ln">21</span><span class="cl">        <span class="c1">// handle other (input) events, as well as mouse buttons and mouse move...
</span></span></span><span class="line"><span class="ln">22</span><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl">
</span></span><span class="line"><span class="ln">24</span><span class="cl">    <span class="nl">frame_duration</span> <span class="p">:</span><span class="o">=</span> <span class="nf">f32</span><span class="p">(</span><span class="n">time</span><span class="p">.</span><span class="nf">duration_seconds</span><span class="p">(</span><span class="n">time</span><span class="p">.</span><span class="nf">tick_since</span><span class="p">(</span><span class="n">prev_time</span><span class="p">)))</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl">    <span class="n">prev_time</span> <span class="o">=</span> <span class="n">time</span><span class="p">.</span><span class="nf">tick_now</span><span class="p">()</span>
</span></span><span class="line"><span class="ln">26</span><span class="cl">    <span class="n">accumulator</span> <span class="o">+=</span> <span class="n">frame_duration</span>
</span></span><span class="line"><span class="ln">27</span><span class="cl">    <span class="nl">num_ticks</span> <span class="p">:</span><span class="o">=</span> <span class="kt">int</span><span class="p">(</span><span class="nf">floor</span><span class="p">(</span><span class="n">accumulator</span> <span class="o">/</span> <span class="n">DELTA</span><span class="p">))</span>
</span></span><span class="line"><span class="ln">28</span><span class="cl">    <span class="n">accumulator</span> <span class="o">-=</span> <span class="nf">f32</span><span class="p">(</span><span class="n">num_ticks</span><span class="p">)</span> <span class="o">*</span> <span class="n">DELTA</span>
</span></span><span class="line"><span class="ln">29</span><span class="cl">
</span></span><span class="line"><span class="ln">30</span><span class="cl">    <span class="k">if</span> <span class="n">num_ticks</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">31</span><span class="cl">        <span class="n">tick_input</span><span class="p">.</span><span class="n">cursor_delta</span> <span class="o">/=</span> <span class="nf">f32</span><span class="p">(</span><span class="n">num_ticks</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">32</span><span class="cl">        <span class="n">tick_input</span><span class="p">.</span><span class="n">scroll_delta</span> <span class="o">/=</span> <span class="nf">f32</span><span class="p">(</span><span class="n">num_ticks</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">33</span><span class="cl">
</span></span><span class="line"><span class="ln">34</span><span class="cl">        <span class="k">for</span> <span class="n">tick_index</span> <span class="n">in</span> <span class="mi">0</span> <span class="p">..</span><span class="o">&lt;</span> <span class="n">num_ticks</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">35</span><span class="cl">            <span class="n">runtime</span><span class="p">.</span><span class="nf">mem_copy_non_overlapping</span><span class="p">(</span><span class="o">&amp;</span><span class="n">prev_game</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">game</span><span class="p">,</span> <span class="nf">size_of</span><span class="p">(</span><span class="n">Game</span><span class="p">))</span>
</span></span><span class="line"><span class="ln">36</span><span class="cl">            <span class="nf">game_tick</span><span class="p">(</span><span class="o">&amp;</span><span class="n">game</span><span class="p">,</span> <span class="n">prev_game</span><span class="p">,</span> <span class="n">tick_input</span><span class="p">,</span> <span class="n">DELTA</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">37</span><span class="cl">            <span class="c1">// Clear temporary flags
</span></span></span><span class="line"><span class="ln">38</span><span class="cl">            <span class="k">for</span> <span class="o">&amp;</span><span class="n">flags</span> <span class="n">in</span> <span class="n">tick_input</span><span class="p">.</span><span class="n">keys</span> <span class="k">do</span> <span class="n">flags</span> <span class="o">&amp;=</span> <span class="p">{.</span><span class="n">Down</span><span class="p">}</span>
</span></span><span class="line"><span class="ln">39</span><span class="cl">            <span class="k">for</span> <span class="o">&amp;</span><span class="n">flags</span> <span class="n">in</span> <span class="n">tick_input</span><span class="p">.</span><span class="n">mouse_buttons</span> <span class="k">do</span> <span class="n">flags</span> <span class="o">&amp;=</span> <span class="p">{.</span><span class="n">Down</span><span class="p">}</span>
</span></span><span class="line"><span class="ln">40</span><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="ln">41</span><span class="cl">
</span></span><span class="line"><span class="ln">42</span><span class="cl">        <span class="c1">// Now clear the rest of the input state
</span></span></span><span class="line"><span class="ln">43</span><span class="cl">        <span class="n">tick_input</span><span class="p">.</span><span class="n">cursor_delta</span> <span class="o">=</span> <span class="p">{}</span>
</span></span><span class="line"><span class="ln">44</span><span class="cl">        <span class="n">tick_input</span><span class="p">.</span><span class="n">scroll_delta</span> <span class="o">=</span> <span class="p">{}</span>
</span></span><span class="line"><span class="ln">45</span><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="ln">46</span><span class="cl">
</span></span><span class="line"><span class="ln">47</span><span class="cl">    <span class="n">alpha</span> <span class="o">=</span> <span class="n">accumulator</span> <span class="o">/</span> <span class="n">DELTA</span>
</span></span><span class="line"><span class="ln">48</span><span class="cl">    <span class="nf">game_draw</span><span class="p">(</span><span class="n">game</span><span class="p">,</span> <span class="n">prev_game</span><span class="p">,</span> <span class="n">alpha</span><span class="p">)</span>
</span></span><span class="line"><span class="ln">49</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><p>And that&rsquo;s it! This is the solution I currently use in my engine and works well so far. Let me know if you run into any problems or if you have a better solution in mind :)</p>
<p>Thanks for reading!</p>
]]></content:encoded></item><item><title>Simple Undo/Redo System in Odin</title><link>https://jakubtomsu.github.io/posts/simple_undo_redo_in_odin/</link><pubDate>Tue, 28 May 2024 00:00:00 +0000</pubDate><guid>https://jakubtomsu.github.io/posts/simple_undo_redo_in_odin/</guid><description>&lt;p&gt;This is a short blog post about something I&amp;rsquo;ve had to implement recently: editor undo/redo functionality.&lt;/p&gt;
&lt;p&gt;A few weeks ago I started working on a new project, it&amp;rsquo;s a 3D FPS game inspired by Quake and other &amp;rsquo;90s shooters.
The world is a 16x16x16 uniform grid of blocks, and it loops infinitely. Here is a clip of the game prototype:&lt;/p&gt;
&lt;blockquote class="twitter-tweet"&gt;&lt;p lang="en" dir="ltr"&gt;This is a small boomer shooter prototype I&amp;#39;ve been working on lately. The world is 16x16x16 blocks and it infinitely repeats itself.&lt;a href="https://twitter.com/hashtag/gamedev?src=hash&amp;amp;ref_src=twsrc%5Etfw"&gt;#gamedev&lt;/a&gt; &lt;a href="https://t.co/ONrkzmf5t2"&gt;pic.twitter.com/ONrkzmf5t2&lt;/a&gt;&lt;/p&gt;</description><content:encoded><![CDATA[<p>This is a short blog post about something I&rsquo;ve had to implement recently: editor undo/redo functionality.</p>
<p>A few weeks ago I started working on a new project, it&rsquo;s a 3D FPS game inspired by Quake and other &rsquo;90s shooters.
The world is a 16x16x16 uniform grid of blocks, and it loops infinitely. Here is a clip of the game prototype:</p>
<blockquote class="twitter-tweet"><p lang="en" dir="ltr">This is a small boomer shooter prototype I&#39;ve been working on lately. The world is 16x16x16 blocks and it infinitely repeats itself.<a href="https://twitter.com/hashtag/gamedev?src=hash&amp;ref_src=twsrc%5Etfw">#gamedev</a> <a href="https://t.co/ONrkzmf5t2">pic.twitter.com/ONrkzmf5t2</a></p>&mdash; Jakub Tomšů (@jakubtomsu_) <a href="https://twitter.com/jakubtomsu_/status/1794059547146936800?ref_src=twsrc%5Etfw">May 24, 2024</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


<p>This clip uses procedurally generated levels. Those are cool, but don&rsquo;t have that much structure and interesting stuff going on. So I quickly decided I need to hand-craft levels for them to reach the full potential.</p>
<p>So I wrote a simple level editor. It was surprisingly quick, I had all of the basic functionality implemented within a few hours. One of the problems I had to solve was undo/redo system, which seems very challenging but doesn&rsquo;t have to be. I found a very simple way to structure the code to make the implementation trivial.</p>
<p>My implementation is inspired by rxi&rsquo;s <a href="https://rxi.github.io/a_simple_undo_system.html">Simple Undo System</a> and Dennis Gustaffson&rsquo;s <a href="https://blog.voxagon.se/2018/07/10/undo-for-lazy-programmers.html">Undo for Lazy Programmers</a></p>
<h2 id="level-representation">Level Representation</h2>
<p>First, let&rsquo;s define the data for our level. In my case it&rsquo;s very simple, but it&rsquo;s easy to extend if necessary.</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-odin" data-lang="odin"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="n">Level</span><span class="w"> </span><span class="o">::</span><span class="w"> </span><span class="kd">struct</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="w">    </span><span class="k">using</span><span class="w"> </span><span class="n">info</span><span class="o">:</span><span class="w">   </span><span class="n">Level_Info</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w">    </span><span class="n">cells</span><span class="o">:</span><span class="w">        </span><span class="n">Level_Cells</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w">    </span><span class="n">detail_cells</span><span class="o">:</span><span class="w"> </span><span class="n">Level_Detail_Cells</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="n">Level_Cells</span><span class="w"> </span><span class="o">::</span><span class="w"> </span><span class="p">[</span><span class="n">LEVEL_BOUNDS_X</span><span class="p">][</span><span class="n">LEVEL_BOUNDS_Y</span><span class="p">][</span><span class="n">LEVEL_BOUNDS_Z</span><span class="p">]</span><span class="n">Cell</span><span class="w"> </span><span class="c1">// Cell is a u8 enum
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="n">Level_Detail_Cells</span><span class="w"> </span><span class="o">::</span><span class="w"> </span><span class="p">[</span><span class="n">LEVEL_BOUNDS_X</span><span class="p">][</span><span class="n">LEVEL_BOUNDS_Y</span><span class="p">][</span><span class="n">LEVEL_BOUNDS_Z</span><span class="p">]</span><span class="n">Detail_Cell</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="n">Level_Info</span><span class="w"> </span><span class="o">::</span><span class="w"> </span><span class="kd">struct</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w">    </span><span class="n">size</span><span class="o">:</span><span class="w">      </span><span class="n">IVec3</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w">    </span><span class="n">fog_color</span><span class="o">:</span><span class="w"> </span><span class="n">Vec3</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><p>The goal here is to organize the level representation into a bunch of &ldquo;chunks&rdquo; (mostly by size and importance). All miscellaneous level metadata goes into <code>Level_Info</code> but other big chunks of data are a separate member.</p>
<p>Note: this assumes all of your level data is statically allocated and trivially copyable. I do this for <em>all</em> of my data anyway, I think there isn&rsquo;t a reason to use any dynamic allocation for the use cases I care about. I might write a blog about this another time, it&rsquo;s a very useful way to think about data and not many people talk about it. It&rsquo;s always borrow checker/RAII/Arenas/Custom Allocators&hellip;</p>
<h2 id="save-points">Save Points</h2>
<p>Now, let&rsquo;s define a way to store a single &ldquo;change&rdquo; to the data. This acts as a save point the user can go back to.</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-odin" data-lang="odin"><span class="line"><span class="ln">1</span><span class="cl"><span class="n">Editor_Undo_Item</span><span class="w"> </span><span class="o">::</span><span class="w"> </span><span class="kd">union</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="w">    </span><span class="n">Level_Info</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="w">    </span><span class="n">Level_Cells</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="w">    </span><span class="n">Level_Detail_Cells</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><p>This stores a change to a part of a level. It&rsquo;s a <a href="https://odin-lang.org/docs/overview/#unions">tagged union</a>, so the total size is the size of the largest item.</p>
<p>This allows me to easily store a change to any part of a level. That&rsquo;s why I separated the level into chunks in the previous step, it makes it easier to organize changes into groups. In theory you <em>could</em> save the entire level on every change. But this is almost as simple and can save a lot of memory.</p>
<p>This way every change takes up memory for the &ldquo;worst-case&rdquo;, which might seem bad at first but it&rsquo;s actually completely fine. In my case one undo item is 4 kilobytes, which is almost nothing these days. This system also scales really well in cases where you modify <em>most or all</em> of the cells, and doesn&rsquo;t create unexpected spikes. But the reasons for focusing on worst-case computation is a big topic, let&rsquo;s leave that for another blog post :)</p>
<h2 id="history-buffers">History Buffers</h2>
<p>Let&rsquo;s define a way to store the actual edits within our editor state.</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-odin" data-lang="odin"><span class="line"><span class="ln">1</span><span class="cl"><span class="n">Editor</span><span class="w"> </span><span class="o">::</span><span class="w"> </span><span class="kd">struct</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="w">    </span><span class="n">level</span><span class="o">:</span><span class="w"> </span><span class="n">Level</span><span class="p">,</span><span class="w"> </span><span class="c1">// current level data
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="w">    </span><span class="n">undo</span><span class="o">:</span><span class="w">  </span><span class="n">Queue</span><span class="p">(</span><span class="nx">2048</span><span class="p">,</span><span class="w"> </span><span class="n">Editor_Undo_Item</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="w">    </span><span class="n">redo</span><span class="o">:</span><span class="w">  </span><span class="n">Queue</span><span class="p">(</span><span class="nx">2048</span><span class="p">,</span><span class="w"> </span><span class="n">Editor_Undo_Item</span><span class="p">),</span><span class="w">
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="w">    </span><span class="c1">// other editor state...
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><p>The editor stores two queues (ring buffers) of edits, one for Undo and one for Redo. The reason why I use a queue is to have the ability to &ldquo;force push&rdquo; an edit at the end. If I used a regular array/stack, I would need to shift all other items down by one slot. The queue I use comes from my own small library for static datastructures, but you could use something like <code>core:container/queue</code> as well.</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-odin" data-lang="odin"><span class="line"><span class="ln">1</span><span class="cl"><span class="n">editor_undo_push</span><span class="w"> </span><span class="o">::</span><span class="w"> </span><span class="kd">proc</span><span class="p">(</span><span class="n">ed</span><span class="o">:</span><span class="w"> </span><span class="o">^</span><span class="n">Editor</span><span class="p">,</span><span class="w"> </span><span class="n">item</span><span class="o">:</span><span class="w"> </span><span class="n">Editor_Undo_Item</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="w">    </span><span class="c1">// Makes sure the item is always pushed back into the queue, even if it&#39;s full.
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="w">    </span><span class="n">queue_push_back_force</span><span class="p">(</span><span class="o">&amp;</span><span class="n">ed</span><span class="p">.</span><span class="n">undo</span><span class="p">,</span><span class="w"> </span><span class="n">item</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><p>This is the procedure for pushing save points before any edits. I pass the state which will be changed, and then perform the change. Here is an example of placing wall blocks on a mouse click:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-odin" data-lang="odin"><span class="line"><span class="ln">1</span><span class="cl"><span class="k">if</span><span class="w"> </span><span class="n">input_pressed</span><span class="p">(.</span><span class="n">Mouse_Left</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="w">    </span><span class="n">editor_undo_push</span><span class="p">(</span><span class="n">ed</span><span class="p">,</span><span class="w"> </span><span class="n">ed</span><span class="p">.</span><span class="n">level</span><span class="p">.</span><span class="n">cells</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="w">    </span><span class="c1">// Do any modifications to level.cells...
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="w">    </span><span class="n">ed</span><span class="p">.</span><span class="n">level</span><span class="p">.</span><span class="n">cells</span><span class="p">[</span><span class="n">cursor</span><span class="p">.</span><span class="n">x</span><span class="p">][</span><span class="n">cursor</span><span class="p">.</span><span class="n">y</span><span class="p">][</span><span class="n">cursor</span><span class="p">.</span><span class="n">z</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">.</span><span class="n">Wall</span><span class="w">
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><h2 id="ctrlz-and-ctrlshiftz">Ctrl+Z and Ctrl+Shift+Z</h2>
<p>This is all I need to implement the actual undo/redo functionality. This logic is the same as in rxi&rsquo;s article. Pop from one buffer, and before applying the data push the current state to the other buffer.</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-odin" data-lang="odin"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="n">block</span><span class="o">:</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="n">input_pressed</span><span class="p">(</span><span class="n">inp</span><span class="p">,</span><span class="w"> </span><span class="p">.</span><span class="n">Z</span><span class="p">)</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="n">input_repeated</span><span class="p">(</span><span class="n">inp</span><span class="p">,</span><span class="w"> </span><span class="p">.</span><span class="n">Z</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="w">    </span><span class="n">modifiers</span><span class="o">:</span><span class="w"> </span><span class="k">bit_set</span><span class="p">[</span><span class="n">Input_Modifier</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">input_modifiers_down</span><span class="p">(</span><span class="n">inp</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="w">    </span><span class="c1">// Pop the data from a change buffer depending on the operation
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="w">    </span><span class="c1">// Breaks out of this entire scope if it&#39;s empty
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w">    </span><span class="n">change</span><span class="o">:</span><span class="w"> </span><span class="n">Editor_Undo_Item</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w">    </span><span class="k">switch</span><span class="w"> </span><span class="n">modifiers</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="p">{.</span><span class="n">Left_Control</span><span class="p">}</span><span class="o">:</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w">        </span><span class="n">change</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">queue_pop_back_safe</span><span class="p">(</span><span class="o">&amp;</span><span class="n">ed</span><span class="p">.</span><span class="n">undo</span><span class="p">)</span><span class="w"> </span><span class="n">or_break</span><span class="w"> </span><span class="n">block</span><span class="w">
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="p">{.</span><span class="n">Left_Control</span><span class="p">,</span><span class="w"> </span><span class="p">.</span><span class="n">Left_Shift</span><span class="p">}</span><span class="o">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w">        </span><span class="n">change</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">queue_pop_back_safe</span><span class="p">(</span><span class="o">&amp;</span><span class="n">ed</span><span class="p">.</span><span class="n">redo</span><span class="p">)</span><span class="w"> </span><span class="n">or_break</span><span class="w"> </span><span class="n">block</span><span class="w">
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w">    </span><span class="k">case</span><span class="o">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w">        </span><span class="k">break</span><span class="w"> </span><span class="n">block</span><span class="w">
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="w">    </span><span class="c1">// Prepare a save point for the current data
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="w">    </span><span class="c1">// Write the change to the current state
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="w">    </span><span class="n">prev</span><span class="o">:</span><span class="w"> </span><span class="n">Editor_Undo_Item</span><span class="w">
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="w">    </span><span class="k">switch</span><span class="w"> </span><span class="n">v</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="n">change</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="n">Level_Info</span><span class="o">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="w">        </span><span class="n">prev</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">ed</span><span class="p">.</span><span class="n">level</span><span class="p">.</span><span class="n">info</span><span class="w">
</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="w">        </span><span class="n">ed</span><span class="p">.</span><span class="n">level</span><span class="p">.</span><span class="n">info</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">v</span><span class="w">
</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="n">Level_Cells</span><span class="o">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="w">        </span><span class="n">prev</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">ed</span><span class="p">.</span><span class="n">level</span><span class="p">.</span><span class="n">cells</span><span class="w">
</span></span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="w">        </span><span class="n">ed</span><span class="p">.</span><span class="n">level</span><span class="p">.</span><span class="n">cells</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">v</span><span class="w">
</span></span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="n">Level_Detail_Cells</span><span class="w">
</span></span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="w">        </span><span class="n">prev</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">ed</span><span class="p">.</span><span class="n">level</span><span class="p">.</span><span class="n">detail_cells</span><span class="w">
</span></span></span><span class="line"><span class="ln">28</span><span class="cl"><span class="w">        </span><span class="n">ed</span><span class="p">.</span><span class="n">level</span><span class="p">.</span><span class="n">detail_cells</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">v</span><span class="w">
</span></span></span><span class="line"><span class="ln">29</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">30</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">31</span><span class="cl"><span class="w">    </span><span class="c1">// Push the current data into the _other_ buffer
</span></span></span><span class="line"><span class="ln">32</span><span class="cl"><span class="w">    </span><span class="k">switch</span><span class="w"> </span><span class="n">modifiers</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">33</span><span class="cl"><span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="p">{.</span><span class="n">Left_Control</span><span class="p">}</span><span class="o">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">34</span><span class="cl"><span class="w">        </span><span class="n">queue_push_back_force</span><span class="p">(</span><span class="o">&amp;</span><span class="n">ed</span><span class="p">.</span><span class="n">redo</span><span class="p">,</span><span class="w"> </span><span class="n">prev</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">35</span><span class="cl"><span class="w">    </span><span class="k">case</span><span class="w"> </span><span class="p">{.</span><span class="n">Left_Control</span><span class="p">,</span><span class="w"> </span><span class="p">.</span><span class="n">Left_Shift</span><span class="p">}</span><span class="o">:</span><span class="w">
</span></span></span><span class="line"><span class="ln">36</span><span class="cl"><span class="w">        </span><span class="n">queue_push_back_force</span><span class="p">(</span><span class="o">&amp;</span><span class="n">ed</span><span class="p">.</span><span class="n">undo</span><span class="p">,</span><span class="w"> </span><span class="n">prev</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">37</span><span class="cl"><span class="w">    </span><span class="p">}</span><span class="w">
</span></span></span><span class="line"><span class="ln">38</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><p>And that&rsquo;s it! Turns out this entire system is not many lines of code at all, and is efficient even when you use worst-case-sized statically allocated data structures. That said, it&rsquo;s not a one-size-fits-all method. If you have gigantic scenes you probably need to look into other approaches (maybe XOR and RLE compressed delta states? LZ4? idk).</p>
<p>But this is fine for basically anything an indie developer might need in 99% of cases. Hope this helps, thank you for reading!</p>
]]></content:encoded></item><item><title>Solar Storm Rendering</title><link>https://jakubtomsu.github.io/posts/solar_storm_renderer/</link><pubDate>Wed, 06 Mar 2024 00:00:00 +0000</pubDate><guid>https://jakubtomsu.github.io/posts/solar_storm_renderer/</guid><description>&lt;p&gt;&lt;img src="https://jakubtomsu.github.io/posts/solar_storm_renderer/solar_storm.gif" alt="gif"&gt;&lt;/p&gt;
&lt;p&gt;Solar Storm is an upcoming tactical sci-fi artillery game. It’s a solo project I’ve been writing in a custom engine for the last ~4 months. It’s inspired by local multiplayer games from the 90s like Scorched Earth (1991) and Worms (1995). The entire game is written from scratch in the &lt;a href="https://odin-lang.org"&gt;Odin programming language&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id="play-solar-storm-on-steam-and-join-our-discord"&gt;&lt;a href="https://store.steampowered.com/app/2754920/Solar_Storm/"&gt;Play Solar Storm on Steam&lt;/a&gt; and &lt;a href="https://discord.com/invite/wn5jMMMYe4"&gt;Join our Discord!&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The game engine is built with the help of &lt;a href="https://github.com/floooh/sokol"&gt;Sokol&lt;/a&gt;. It’s a set of STB-style cross-platform libraries for C/C++, with an official support for Odin as well as some other languages. In particular, I use sokol_app for windowing and interfacing with the OS, and sokol_gfx as an rendering API abstraction.&lt;/p&gt;</description><content:encoded><![CDATA[<p><img src="/posts/solar_storm_renderer/solar_storm.gif" alt="gif"></p>
<p>Solar Storm is an upcoming tactical sci-fi artillery game. It’s a solo project I’ve been writing in a custom engine for the last ~4 months. It’s inspired by local multiplayer games from the 90s like Scorched Earth (1991) and Worms (1995). The entire game is written from scratch in the <a href="https://odin-lang.org">Odin programming language</a>.</p>
<h3 id="play-solar-storm-on-steam-and-join-our-discord"><a href="https://store.steampowered.com/app/2754920/Solar_Storm/">Play Solar Storm on Steam</a> and <a href="https://discord.com/invite/wn5jMMMYe4">Join our Discord!</a></h3>
<p>The game engine is built with the help of <a href="https://github.com/floooh/sokol">Sokol</a>. It’s a set of STB-style cross-platform libraries for C/C++, with an official support for Odin as well as some other languages. In particular, I use sokol_app for windowing and interfacing with the OS, and sokol_gfx as an rendering API abstraction.</p>
<p>Similar to libraries like SDL2 or GLFW, <code>sokol_app</code> provides a cross-platform API for interacting with the OS. But like all other sokol modules, it&rsquo;s just a single, lightweight C/C++ header file, which makes it really nice to work with.</p>
<p>So let’s talk about the Solar Storm rendering a bit. The entire renderer is built on <code>sokol_gfx</code>, with custom shaders, render passes, and all that fun stuff.</p>
<p>Here is a sneak peek at the different steps of the rendering process:
<img src="/posts/solar_storm_renderer/steps.gif" alt="screenspace pass"></p>
<h1 id="shaders">Shaders</h1>
<p>The shaders are written in Sokol’s flavor of GLSL, which has a cross-platform shader compiler called sokol-shdc. Usually writing shaders for multiple rendering APIs is a lot of pain. But sokol-shdc uses libraries like GLSLang, SPIRV-Tools, SPIRV-Cross to transpile them for the appropriate platform.</p>
<p>Another nice aspect is that the compiler can generate source code and boilerplate for any language supported by Sokol. Here is an example command of compiling shaders with <code>sokol-shdc</code>:</p>





<pre tabindex="0"><code>sokol-shdc.exe -i shaders.glsl -o shaders.odin -l hlsl5:glsl330 -f sokol_odin</code></pre><p>And here is how a simple sokol-style GLSL looks like:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-glsl" data-lang="glsl"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="err">@</span><span class="n">vs</span> <span class="n">vs</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="k">uniform</span> <span class="n">vs_params</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 3</span><span class="cl">    <span class="n">mat4</span> <span class="n">mvp</span><span class="p">;</span> <span class="c1">// transformation matrix for current mesh</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="k">in</span> <span class="k">vec4</span> <span class="n">position</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="k">in</span> <span class="k">vec2</span> <span class="n">texcoord0</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="k">out</span> <span class="k">vec2</span> <span class="n">uv</span><span class="p">;</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">
</span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="k">void</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">    <span class="n">gl_Position</span> <span class="o">=</span> <span class="n">mvp</span> <span class="o">*</span> <span class="n">position</span><span class="p">;</span> <span class="c1">// transform vertex position</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl">    <span class="n">uv</span> <span class="o">=</span> <span class="n">texcoord0</span><span class="p">;</span> <span class="c1">// pass mesh UVs to fragment shader</span>
</span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="err">@</span><span class="n">end</span>
</span></span><span class="line"><span class="ln">14</span><span class="cl">
</span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="err">@</span><span class="n">fs</span> <span class="n">fs</span>
</span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="k">uniform</span> <span class="n">texture2D</span> <span class="n">tex</span><span class="p">;</span> <span class="c1">// Note the texture and the sampler is separated</span>
</span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="k">uniform</span> <span class="n">sampler</span> <span class="n">smp</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="k">in</span> <span class="k">vec2</span> <span class="n">uv</span><span class="p">;</span>
</span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="k">out</span> <span class="k">vec4</span> <span class="n">frag_color</span><span class="p">;</span> <span class="c1">// pixel color</span>
</span></span><span class="line"><span class="ln">20</span><span class="cl">
</span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="k">void</span> <span class="n">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln">22</span><span class="cl">    <span class="n">frag_color</span> <span class="o">=</span> <span class="n">texture</span><span class="p">(</span><span class="k">sampler2D</span><span class="p">(</span><span class="n">tex</span><span class="p">,</span> <span class="n">smp</span><span class="p">),</span> <span class="n">uv</span><span class="p">);</span>
</span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="err">@</span><span class="n">end</span>
</span></span><span class="line"><span class="ln">25</span><span class="cl">
</span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="err">@</span><span class="n">program</span> <span class="n">foo</span> <span class="n">vs</span> <span class="n">fs</span></span></span></code></pre></div><h1 id="immediate-mode-apis-are-amazing">Immediate-mode APIs are amazing</h1>
<p>A core philosophy of the renderer is to do as much as possible with <a href="https://caseymuratori.com/blog_0001">immediate-mode APIs</a>, and group rendering operations so Instancing is trivial.</p>
<p>Immediate-mode APIs are well known for UIs, but the core idea can actually be applied to almost any context. In games, where you often do an action every frame, the idea is incredibly powerful. I use it many places in the engine, for things like collision, physics, drawing shapes and text, UI, debug gizmos, localization and particle effects.</p>
<p>The game uses immediate mode API exposed by the renderer for all drawing operations. Each frame game calls <code>draw_shape</code>, <code>draw_text</code>, <code>draw_sprite</code> and <code>draw_ui_rect</code> to draw everything. That includes players, particle effects, ImGui system, projectiles and explosions. This way it becomes much easier to do dynamic effects, since there is no state at all that needs to be managed. Here is an example of drawing simple animated octagon which rotates over time:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-odin" data-lang="odin"><span class="line"><span class="ln">1</span><span class="cl"><span class="n">draw_shape</span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="w">   </span><span class="n">pos</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span><span class="nx">0</span><span class="p">,</span><span class="w"> </span><span class="nx">0</span><span class="p">},</span><span class="w">
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="w">   </span><span class="n">scale</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">10</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="w">   </span><span class="n">rot</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">time_passed</span><span class="p">(),</span><span class="w">
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="w">   </span><span class="n">color</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">COLOR_RED</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="w">   </span><span class="n">z</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">0</span><span class="mf">.1</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="w">   </span><span class="n">shape</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">.</span><span class="n">Octagon</span><span class="p">)</span></span></span></code></pre></div><p>Immediate-mode text drawing has an interesting benefit of dynamic animations. The <code>draw_text</code> procedure returns a slice of the text character instances, so you can do any modifications yourself. Here is a simple example:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-odin" data-lang="odin"><span class="line"><span class="ln">1</span><span class="cl"><span class="n">chars</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="n">draw_text</span><span class="p">({</span><span class="nx">100</span><span class="p">,</span><span class="w"> </span><span class="nx">200</span><span class="p">},</span><span class="w"> </span><span class="s">&#34;Hello world!&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="k">for</span><span class="w"> </span><span class="o">&amp;</span><span class="n">ch</span><span class="p">,</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="n">chars</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="w">    </span><span class="n">ch</span><span class="p">.</span><span class="n">pos</span><span class="p">.</span><span class="n">y</span><span class="w"> </span><span class="o">+=</span><span class="w"> </span><span class="n">sin</span><span class="p">(</span><span class="n">time_passed</span><span class="p">()</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="kt">f32</span><span class="p">(</span><span class="n">i</span><span class="p">))</span><span class="w"> </span><span class="c1">// animate up and down
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><p>This code runs every frame, and updates the character instances right before being submitted to the GPU and displayed. There is state in the background, but the API is fully immediate-mode. <em>immediate-mode != stateless</em></p>
<h1 id="world-pass">World pass</h1>
<p>The terrain is drawn as a single quad. A shader which reads from the terrain SDF texture and a color texture and does the shading. Explaining the terrain in-depth is out of scope for this article, let&rsquo;s do that another time&hellip;</p>
<p>There is also a shader for rendering the background and another one for wind. The background shader draws stars using perlin noise raised to a very high power. The wind is also just perlin noise, but this time it’s stretched horizontally and has some distortion to add some <em>wiggle</em>.</p>
<p><img src="/posts/solar_storm_renderer/env.png" alt="environment"></p>
<p>All world-space elements are rendered into a custom render target. It&rsquo;s a texture with single channel per pixel in the R8_UNORM format. This pass also uses a depth buffer for ordering objects instead of sorting. It&rsquo;s essentially free on modern hardware and it makes things easier.</p>
<h2 id="shapes">Shapes</h2>
<p>All of the other things in the game are rendered using primitive shapes. Every frame, the game pushes shape instances to instance buffers, which then get uploaded to the GPU memory once it’s time to render them. This uses an immediate API in the form of <code>draw_shape</code> procedure.</p>
<p>There is one instance buffer per shape type. It’s a simple fixed array of a shape instance struct, which has a memory layout the GPU can feed to the vertex shader in the form of per-instance parameters.</p>
<p>Here is the full list of supported shapes in the game as of now.</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-odin" data-lang="odin"><span class="line"><span class="ln">1</span><span class="cl"><span class="n">Renderer_Shape</span><span class="w"> </span><span class="o">::</span><span class="w"> </span><span class="kd">enum</span><span class="w"> </span><span class="kt">u8</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="w">    </span><span class="n">Triangle</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="w">    </span><span class="n">Rectangle</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="w">    </span><span class="n">Rhombus</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="w">    </span><span class="n">Pentagon</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="w">    </span><span class="n">Hexagon</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">7</span><span class="cl"><span class="w">    </span><span class="n">Octagon</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">8</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><blockquote>
<p>Fun fact: the reason there is both rectangle and a rhombus is because they have different properties when doing non-uniform scaling. Rhombus is primarily used for sparks and other particle effects.</p>
</blockquote>
<p>More complex shapes like tanks are made up of multiple shapes.
<img src="/posts/solar_storm_renderer/tank.png" alt="tank"></p>
<p>This is the shape instance struct, as it is handled both on the CPU and the GPU side. It&rsquo;s 32 bytes with alignment, which could be significantly reduced but probably isn&rsquo;t worth the effort. The <code>right</code> and <code>up</code> vectors have baked in rotation and scale, so they work like a 2x2 rotation matrix.</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-odin" data-lang="odin"><span class="line"><span class="ln">1</span><span class="cl"><span class="n">Renderer_Shape_Instance</span><span class="w"> </span><span class="o">::</span><span class="w"> </span><span class="kd">struct</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="w">    </span><span class="n">pos</span><span class="o">:</span><span class="w">   </span><span class="n">Vec3</span><span class="p">,</span><span class="w"> </span><span class="c1">// world-space position
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="w">    </span><span class="n">right</span><span class="o">:</span><span class="w"> </span><span class="n">Vec2</span><span class="p">,</span><span class="w"> </span><span class="c1">// local-space vector pointing to the right. 
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="w">    </span><span class="n">up</span><span class="o">:</span><span class="w">    </span><span class="n">Vec2</span><span class="p">,</span><span class="w"> </span><span class="c1">// local-space vector pointing up
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="w">    </span><span class="n">color</span><span class="o">:</span><span class="w"> </span><span class="n">Color8</span><span class="p">,</span><span class="w"> </span><span class="c1">// RGBA8_UNORM color
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><p>Here is all the state renderer keeps for shapes. The pipeline is shared for all shape types, and the buffers are created at load time. <code>shape_instance_bufs</code> are filled with instances from <code>shape_instances</code> prior to rendering. <code>Array</code> is just <code>struct { data: [N]T, len: int }</code>, it&rsquo;s a custom version of odin&rsquo;s <code>core:container/small_array</code>.</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-odin" data-lang="odin"><span class="line"><span class="ln">1</span><span class="cl"><span class="n">shape_pipeline</span><span class="o">:</span><span class="w">      </span><span class="n">sg</span><span class="p">.</span><span class="n">Pipeline</span><span class="p">,</span><span class="w"> </span><span class="c1">// shader for all shapes
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="n">shape_vertex_bufs</span><span class="o">:</span><span class="w">   </span><span class="p">[</span><span class="n">Renderer_Shape</span><span class="p">]</span><span class="n">sg</span><span class="p">.</span><span class="n">Buffer</span><span class="p">,</span><span class="w"> </span><span class="c1">// mesh vertices for each shape type
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="n">shape_index_bufs</span><span class="o">:</span><span class="w">    </span><span class="p">[</span><span class="n">Renderer_Shape</span><span class="p">]</span><span class="n">sg</span><span class="p">.</span><span class="n">Buffer</span><span class="p">,</span><span class="w"> </span><span class="c1">// mesh indicies for each shape type
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="n">shape_instance_bufs</span><span class="o">:</span><span class="w"> </span><span class="p">[</span><span class="n">Renderer_Shape</span><span class="p">]</span><span class="n">sg</span><span class="p">.</span><span class="n">Buffer</span><span class="p">,</span><span class="w"> </span><span class="c1">// GPU buffer for instance data
</span></span></span><span class="line"><span class="ln">5</span><span class="cl"><span class="n">shape_instances</span><span class="o">:</span><span class="w">     </span><span class="p">[</span><span class="n">Renderer_Shape</span><span class="p">]</span><span class="n">Array</span><span class="p">(</span><span class="n">BUDGET_MAX_RENDERER_SHAPE_INSTANCES</span><span class="p">,</span><span class="w"> </span><span class="n">Renderer_Shape_Instance</span><span class="p">),</span></span></span></code></pre></div><p>All shapes of certain type are rendered using GPU instancing, this means it takes only about 6 drawcalls to render all shapes in the worst case. Here is how the game dispatches draw calls. Note: this code is a heavily simplified for the sake of clarity.</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-odin" data-lang="odin"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="n">sg</span><span class="p">.</span><span class="n">apply_pipeline</span><span class="p">(</span><span class="n">renderer</span><span class="p">.</span><span class="n">shape_pipeline</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 2</span><span class="cl"><span class="c1">// Apply projection matrix
</span></span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="n">sg</span><span class="p">.</span><span class="n">apply_uniforms</span><span class="p">(.</span><span class="n">VS</span><span class="p">,</span><span class="w"> </span><span class="n">shaders</span><span class="p">.</span><span class="n">SLOT_shape_vs_params</span><span class="p">,</span><span class="w"> </span><span class="n">sg_range</span><span class="p">(</span><span class="o">&amp;</span><span class="n">shaders</span><span class="p">.</span><span class="n">Shape_Vs_Params</span><span class="p">{</span><span class="n">mvp</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">mvp</span><span class="p">}))</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1">// For each shape type...
</span></span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="k">for</span><span class="w"> </span><span class="o">&amp;</span><span class="n">arr</span><span class="p">,</span><span class="w"> </span><span class="n">shape</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="n">renderer</span><span class="p">.</span><span class="n">shape_instances</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 6</span><span class="cl"><span class="w">    </span><span class="c1">// Clear the instances when done, since it&#39;s immediate-mode
</span></span></span><span class="line"><span class="ln"> 7</span><span class="cl"><span class="w">    </span><span class="k">defer</span><span class="w"> </span><span class="k">clear</span><span class="p">(</span><span class="o">&amp;</span><span class="n">arr</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="ln"> 8</span><span class="cl"><span class="w">    
</span></span></span><span class="line"><span class="ln"> 9</span><span class="cl"><span class="w">    </span><span class="c1">// Upload the instance data for a particular type to the GPU
</span></span></span><span class="line"><span class="ln">10</span><span class="cl"><span class="w">    </span><span class="n">sg</span><span class="p">.</span><span class="n">update_buffer</span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="w">        </span><span class="n">renderer</span><span class="p">.</span><span class="n">shape_instance_bufs</span><span class="p">[</span><span class="n">shape</span><span class="p">],</span><span class="w">
</span></span></span><span class="line"><span class="ln">12</span><span class="cl"><span class="w">        </span><span class="p">{</span><span class="n">ptr</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="o">&amp;</span><span class="n">arr</span><span class="p">[</span><span class="nx">0</span><span class="p">],</span><span class="w"> </span><span class="n">size</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kt">u64</span><span class="p">(</span><span class="n">size_of</span><span class="p">(</span><span class="n">Renderer_Shape_Instance</span><span class="p">)</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="k">len</span><span class="p">(</span><span class="n">arr</span><span class="p">))})</span><span class="w">
</span></span></span><span class="line"><span class="ln">13</span><span class="cl"><span class="w">    
</span></span></span><span class="line"><span class="ln">14</span><span class="cl"><span class="w">    </span><span class="c1">// Bind the mesh
</span></span></span><span class="line"><span class="ln">15</span><span class="cl"><span class="w">    </span><span class="n">sg</span><span class="p">.</span><span class="n">apply_bindings</span><span class="p">({</span><span class="w">
</span></span></span><span class="line"><span class="ln">16</span><span class="cl"><span class="w">        </span><span class="n">vertex_buffers</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">{</span><span class="w">
</span></span></span><span class="line"><span class="ln">17</span><span class="cl"><span class="w">            </span><span class="nx">0</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">renderer</span><span class="p">.</span><span class="n">shape_vertex_bufs</span><span class="p">[</span><span class="n">shape</span><span class="p">],</span><span class="w">
</span></span></span><span class="line"><span class="ln">18</span><span class="cl"><span class="w">            </span><span class="nx">1</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">renderer</span><span class="p">.</span><span class="n">shape_instance_bufs</span><span class="p">[</span><span class="n">shape</span><span class="p">]},</span><span class="w">
</span></span></span><span class="line"><span class="ln">19</span><span class="cl"><span class="w">        </span><span class="n">index_buffer</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">renderer</span><span class="p">.</span><span class="n">shape_index_bufs</span><span class="p">[</span><span class="n">shape</span><span class="p">]})</span><span class="w">
</span></span></span><span class="line"><span class="ln">20</span><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="ln">21</span><span class="cl"><span class="w">    </span><span class="c1">// Dispatch the drawcall for all instances of a shape.
</span></span></span><span class="line"><span class="ln">22</span><span class="cl"><span class="w">    </span><span class="c1">// Single &#39;DrawIndexed&#39; call when using D3D11
</span></span></span><span class="line"><span class="ln">23</span><span class="cl"><span class="w">    </span><span class="n">sg</span><span class="p">.</span><span class="n">draw</span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="ln">24</span><span class="cl"><span class="w">        </span><span class="n">base_element</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nx">0</span><span class="p">,</span><span class="w">
</span></span></span><span class="line"><span class="ln">25</span><span class="cl"><span class="w">        </span><span class="n">num_elements</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">shape_num_elems</span><span class="p">[</span><span class="n">shape</span><span class="p">],</span><span class="w">
</span></span></span><span class="line"><span class="ln">26</span><span class="cl"><span class="w">        </span><span class="n">num_instances</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">len</span><span class="p">(</span><span class="n">arr</span><span class="p">))</span><span class="w">
</span></span></span><span class="line"><span class="ln">27</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><p>Here is an example of one drawcall, in this case hexagons:
<img src="/posts/solar_storm_renderer/shape_drawcall.png" alt="shape drawcall"></p>
<p>That&rsquo;s it for the world render pass, here is the result so far:
<img src="/posts/solar_storm_renderer/world_final_tex.png" alt="final texture"></p>
<h1 id="postprocess-pass">Postprocess pass</h1>
<p>In the post processing pass, a shader takes the single channel world texture and does all post processing. That includes water reflections, palettization and outlines (in order).</p>
<p>The water relections involve calculating the UV coordinate from the reflected world-space coordinate, which is then sample the world texture again. So this is completely done in screenspace. The UV coordinate is also distorted with 1D and 2D perlin noise to make the surface look like actual water waves. The reflected color is also darkened the further from the water surface it is, and a soft vignette is applied around screen edges to make the transition smoother. The reflection is then blended with the world behind the water.</p>
<p>The palettization is there to convert the single-channel value to a RGB color. The value is used as an UV coordinate for looking up a color from a palette texture. Each biome in the game has a combination of colors to make it look more unique. The palette texture uses Nearest sampling mode, which is a stylistic choice.</p>
<p><img src="/posts/solar_storm_renderer/palette.png" alt="palette"></p>
<p>Finally, the outlines are applied. Each pixel compares the depth value to it&rsquo;s neighbors in a 5x5 block, and calculates outline weight based on that. The outline is applied to the already palettized color, to make it go towards pure black instead of the darkest palette color. It improves clarity, especially in biomes with a palette without much contrast.</p>
<p><img src="/posts/solar_storm_renderer/postprocessed.png" alt="postprocessed texture"></p>
<h1 id="screenspace-pass">Screenspace pass</h1>
<p>Then all text character, icons, sprites and UI rectangles get drawn on top of the postprocessed image. There is nothing fancy about this step, but again, everything is drawn with just a couple of draw calls. This pass uses the depth buffer for ordering UI and text elements.</p>
<p>The UI, icons and text rendering are a big topic on their own, so I&rsquo;ll leave that for later. I&rsquo;m also considering switching from my own custom text rendeing to FontStash.</p>
<p><img src="/posts/solar_storm_renderer/screenspace.png" alt="screenspace pass"></p>
<h1 id="display-pass">Display pass</h1>
<p>And that’s it for the rendering! The last render pass just draws the final image to the actual swapchain texture, which gets displayed on your monitor.</p>
<h1 id="conclusion">Conclusion</h1>
<p>The renderer spends up to 50 microseconds on the CPU submitting the commands. At the time of writing, it takes around 100 microseconds on a RTX2060 to actually draw everything.</p>
<p>This is CPU profiler trace using Spall:</p>
<p><img src="/posts/solar_storm_renderer/cpu_trace.png" alt="cpu trace"></p>
<p>Here is another profiler trace, this time from the GPU using PIX:</p>
<p><img src="/posts/solar_storm_renderer/pix.png" alt="gpu trace"></p>
<p>And here is the entire renderdoc capture of the game. The entire thing is just a couple of draw calls and buffer uploads.</p>
<p><img src="/posts/solar_storm_renderer/renderdoc.png" alt="renderdoc"></p>
]]></content:encoded></item><item><title>Dungeon of Quake Alpha</title><link>https://jakubtomsu.github.io/posts/dungeon-of-quake-alpha/</link><pubDate>Thu, 24 Mar 2022 00:00:00 +0000</pubDate><guid>https://jakubtomsu.github.io/posts/dungeon-of-quake-alpha/</guid><description>&lt;p&gt;Dungeon of Quake is a Quake-like FPS, using &lt;a href="https://odin-lang.org"&gt;Odin&lt;/a&gt; and &lt;a href="https://www.raylib.com/"&gt;Raylib&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/jakubtomsu/dungeon-of-quake"&gt;&lt;code&gt;github&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://jakubtomsu.github.io/dungeon_of_quake_logo.png" alt="DoQ logo"&gt;&lt;/p&gt;
&lt;p&gt;DoQ is currently in Alpha version - there are still a lot of bugs to fix and features to add.
The initial development took ~1 month.&lt;/p&gt;
&lt;h2 id="screenshots"&gt;screenshots&lt;/h2&gt;
&lt;p&gt;&lt;img src="https://jakubtomsu.github.io/dungeon_of_quake_screenshot0.png" alt="screenshot"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://jakubtomsu.github.io/dungeon_of_quake_screenshot1.png" alt="screenshot"&gt;&lt;/p&gt;
&lt;h2 id="dqmake"&gt;dqmake&lt;/h2&gt;
&lt;p&gt;DQMake is a super-simple tool for editing DoQ maps.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://jakubtomsu.github.io/dqmake_screenshot0.png" alt="screenshot"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="https://jakubtomsu.github.io/dqmake_screenshot1.png" alt="screenshot"&gt;&lt;/p&gt;</description><content:encoded><![CDATA[<p>Dungeon of Quake is a Quake-like FPS, using <a href="https://odin-lang.org">Odin</a> and <a href="https://www.raylib.com/">Raylib</a></p>
<p><a href="https://github.com/jakubtomsu/dungeon-of-quake"><code>github</code></a></p>
<p><img src="/dungeon_of_quake_logo.png" alt="DoQ logo"></p>
<p>DoQ is currently in Alpha version - there are still a lot of bugs to fix and features to add.
The initial development took ~1 month.</p>
<h2 id="screenshots">screenshots</h2>
<p><img src="/dungeon_of_quake_screenshot0.png" alt="screenshot"></p>
<p><img src="/dungeon_of_quake_screenshot1.png" alt="screenshot"></p>
<h2 id="dqmake">dqmake</h2>
<p>DQMake is a super-simple tool for editing DoQ maps.</p>
<p><img src="/dqmake_screenshot0.png" alt="screenshot"></p>
<p><img src="/dqmake_screenshot1.png" alt="screenshot"></p>
]]></content:encoded></item><item><title>Reverse engineering Unity games</title><link>https://jakubtomsu.github.io/posts/reverse-engineering-unity-games/</link><pubDate>Thu, 24 Mar 2022 00:00:00 +0000</pubDate><guid>https://jakubtomsu.github.io/posts/reverse-engineering-unity-games/</guid><description>&lt;p&gt;Few months ago, I made a level editor mod for &lt;a href="https://store.steampowered.com/app/1155650/Shady_Knight/"&gt;Shady Knight&lt;/a&gt;,
an action game with lots of movement, parkour and combat. I thought I could share a few things related to reverse engineering the game and modding.&lt;/p&gt;
&lt;h2 id="accessing-source-code"&gt;accessing source code&lt;/h2&gt;
&lt;p&gt;For a game, all of the gameplay source code is stored in &lt;code&gt;\SomeGame\SomeGame_Data\Managed\Assembly-CSharp.dll&lt;/code&gt;. This is a &lt;strong&gt;dll&lt;/strong&gt; (dynamically-linked library), which is generated from source files when the game is built.
To get the original source code, you need to decompile this dll. Fortunately, C# is quite easy to decompile, and there are many free and open-source tools on the internet.&lt;br&gt;
I personally really like &lt;a href="https://github.com/dnSpy/dnSpy/releases/tag/v6.1.8"&gt;dnSpy&lt;/a&gt;,
since it&amp;rsquo;s also capable of compiling C# code (this means you can just change code from the original game)&lt;/p&gt;</description><content:encoded><![CDATA[<p>Few months ago, I made a level editor mod for <a href="https://store.steampowered.com/app/1155650/Shady_Knight/">Shady Knight</a>,
an action game with lots of movement, parkour and combat. I thought I could share a few things related to reverse engineering the game and modding.</p>
<h2 id="accessing-source-code">accessing source code</h2>
<p>For a game, all of the gameplay source code is stored in <code>\SomeGame\SomeGame_Data\Managed\Assembly-CSharp.dll</code>. This is a <strong>dll</strong> (dynamically-linked library), which is generated from source files when the game is built.
To get the original source code, you need to decompile this dll. Fortunately, C# is quite easy to decompile, and there are many free and open-source tools on the internet.<br>
I personally really like <a href="https://github.com/dnSpy/dnSpy/releases/tag/v6.1.8">dnSpy</a>,
since it&rsquo;s also capable of compiling C# code (this means you can just change code from the original game)</p>
<h2 id="injecting-custom-code">injecting custom code</h2>
<p>It is of course possible to develop an entire mod by editing the <code>Assembly-CSharp.dll</code>, but it&rsquo;s easier to just create it separately and load it at startup time.
This way your game can support multiple mods at the same time, because games can have one common loader for all mods.</p>
<p>If you want to inject the code yourself, here is a simple example how to load and run a C# dll:</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-csharp" data-lang="csharp"><span class="line"><span class="ln"> 1</span><span class="cl"><span class="k">using</span> <span class="nn">System.Reflection</span><span class="p">;</span> <span class="c1">// loading dll&#39;s at runtime</span>
</span></span><span class="line"><span class="ln"> 2</span><span class="cl">
</span></span><span class="line"><span class="ln"> 3</span><span class="cl"><span class="c1">// just loop all types and find the entry point method</span>
</span></span><span class="line"><span class="ln"> 4</span><span class="cl"><span class="c1">// there are probably much better ways of doing this, but this worked for me</span>
</span></span><span class="line"><span class="ln"> 5</span><span class="cl"><span class="k">foreach</span><span class="p">(</span><span class="n">Type</span> <span class="n">type</span> <span class="k">in</span> <span class="n">Assembly</span><span class="p">.</span><span class="n">LoadFile</span><span class="p">(</span><span class="s">&#34;some dll path&#34;</span><span class="p">).</span><span class="n">GetTypes</span><span class="p">())</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 6</span><span class="cl">	<span class="n">MethodInfo</span> <span class="n">method</span> <span class="p">=</span> <span class="n">type</span><span class="p">.</span><span class="n">GetMethod</span><span class="p">(</span><span class="s">&#34;some method with unique name&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="ln"> 7</span><span class="cl">	<span class="k">if</span> <span class="p">(!(</span><span class="n">method</span> <span class="p">==</span> <span class="kc">null</span><span class="p">))</span> <span class="p">{</span>
</span></span><span class="line"><span class="ln"> 8</span><span class="cl">		<span class="kt">object</span> <span class="n">obj</span> <span class="p">=</span> <span class="n">Activator</span><span class="p">.</span><span class="n">CreateInstance</span><span class="p">(</span><span class="n">type</span><span class="p">);</span>
</span></span><span class="line"><span class="ln"> 9</span><span class="cl">		<span class="n">method</span><span class="p">.</span><span class="n">Invoke</span><span class="p">(</span><span class="n">obj</span><span class="p">,</span> <span class="k">new</span> <span class="kt">object</span><span class="p">[</span><span class="m">0</span><span class="p">]);</span>
</span></span><span class="line"><span class="ln">10</span><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="ln">11</span><span class="cl"><span class="p">}</span></span></span></code></pre></div><p>This code sould be ideally placed in some method that runs <em>as early as possible</em>.
Probably <code>Awake</code> function on a component somewhere on the first loading screen, main menu, etc. is good enough.</p>
<h2 id="inspecting-the-renderer">inspecting the renderer</h2>
<p>With tools like <a href="https://developer.nvidia.com/nsight-graphics">NVIDIA Nsight</a>, <a href="https://renderdoc.org/">RenderDoc</a>, etc.
you can easily inspect what your GPU is doing, and how the game actually renders each frame.
You can also take a look at all kinds of resources in the game - shaders, textures, models, &hellip;</p>
<p>Here is a screenshot from Shady Knight when using RenderDoc:
<img src="/shady_knight_renderdoc.png" alt="shady knight screenshot">
as you can see, it shows everything you might need</p>
]]></content:encoded></item></channel></rss>