Jekyll2024-02-19T04:36:37-08:00https://www.anthropicstudios.com/feed.xmlAnthropic StudiosAn independent game company.AnthropicStGenerating unique values for IMGUI IDs2023-11-03T00:00:00-07:002023-11-03T00:00:00-07:00https://www.anthropicstudios.com/2023/11/03/uniques<figure>
<img width="100%" src="/assets/posts/2023-11-03-unique/level-editor.jpeg" />
<figcaption>An IMGUI.</figcaption>
</figure>
<p>Sometimes, you just want some unique bits to use as an ID. Maybe you’re working with an <a href="https://www.youtube.com/watch?v=Z1qyvQsjK5Y">immediate mode GUI</a>, or need to generate IDs for <a href="https://twitter.com/masonremaley/status/1708350679247646780">cancellable sounds.</a></p>
<p>In many cases you could technically get away with manually enumerating the various unique IDs in a header somewhere, but this is very inconvenient–especially in the case of IMGUIs: if you do this, every time you add or remove a UI element, you need to make a secondary change elsewhere in the codebase. You won’t be able to freely copy paste UI code.</p>
<!--more-->
<h1 id="table-of-contents">Table of Contents</h1>
<ul id="markdown-toc">
<li><a href="#table-of-contents" id="markdown-toc-table-of-contents">Table of Contents</a> <ul>
<li><a href="#implementations" id="markdown-toc-implementations">Implementations</a> <ul>
<li><a href="#cc" id="markdown-toc-cc">C/C++</a></li>
<li><a href="#zig" id="markdown-toc-zig">Zig</a></li>
</ul>
</li>
<li><a href="#interesting-use-cases" id="markdown-toc-interesting-use-cases">Interesting Use Cases</a> <ul>
<li><a href="#imgui-iteration" id="markdown-toc-imgui-iteration">IMGUI Iteration</a></li>
<li><a href="#cancellable-sounds" id="markdown-toc-cancellable-sounds">Cancellable Sounds</a></li>
</ul>
</li>
</ul>
</li>
</ul>
<h2 id="implementations">Implementations</h2>
<p>Here are a few easy ways to conveniently generate unique IDs usable in the context of immediate mode GUIs in C, C++, and Zig.</p>
<h3 id="cc">C/C++</h3>
<p>You can generate a unique value at compile time in C and C++ by using the preprocessor to concatenate the current file path and line number. Assuming you only use the macro once per line, the resulting string’s memory address will be unique:</p>
<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#define CONCAT(lhs, rhs) lhs # rhs
#define CONCAT_WRAPPER(lhs, rhs) CONCAT(lhs, rhs)
#define UNIQUE CONCAT_WRAPPER(__FILE__, __LINE__)
</span>
<span class="c1">// ...</span>
<span class="k">if</span> <span class="p">(</span><span class="n">button</span><span class="p">(</span><span class="s">"click me"</span><span class="p">,</span> <span class="n">UNIQUE</span><span class="p">))</span> <span class="p">{</span>
<span class="c1">// do stuff</span>
<span class="p">}</span>
</code></pre></div></div>
<p>An earlier version of this post mentioned an alternative approach involving the non-standard (but widely supported) macro <a href="https://clang.llvm.org/docs/LanguageExtensions.html#builtin-macros"><code class="language-plaintext highlighter-rouge">__COUNTER__</code></a>. If you only need your value to be unique within a source file this is an easier solution, but that’s not often the case.</p>
<h3 id="zig">Zig</h3>
<p>In Zig, we could implement a similar approach with <a href="https://ziglang.org/documentation/master/#toc-src"><code class="language-plaintext highlighter-rouge">@src()</code></a> and <a href="https://ziglang.org/documentation/master/std/#A;std:fmt.comptimePrint"><code class="language-plaintext highlighter-rouge">std.fmt.comptimePrint</code></a>. However, when Zig gets function-level incremental compilation, this will result in a lot of unnecessary recompilation since functions containing <code class="language-plaintext highlighter-rouge">@src()</code> need to be compiled whenever their line numbers change.</p>
<p>Instead, we can use <a href="https://ziglang.org/documentation/master/#returnAddress"><code class="language-plaintext highlighter-rouge">@returnAddress()</code></a> (and <a href="https://ziglang.org/documentation/master/#toc-Keyword-Reference"><code class="language-plaintext highlighter-rouge">noinline</code></a>):</p>
<div class="language-rs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">pub</span> <span class="k">const</span> <span class="n">Id</span> <span class="o">=</span> <span class="k">enum</span><span class="p">(</span><span class="nb">usize</span><span class="p">)</span> <span class="p">{</span>
<span class="mi">_</span><span class="p">,</span>
<span class="k">pub</span> <span class="n">noinline</span> <span class="k">fn</span> <span class="nf">init</span><span class="p">()</span> <span class="n">Id</span> <span class="p">{</span>
<span class="k">return</span> <span class="o">@</span><span class="nf">enumFromInt</span><span class="p">(</span><span class="o">@</span><span class="nf">returnAddress</span><span class="p">());</span>
<span class="p">}</span>
<span class="p">};</span>
<span class="c">// ...</span>
<span class="k">if</span> <span class="p">(</span><span class="nf">button</span><span class="p">(</span><span class="s">"click me"</span><span class="p">,</span> <span class="n">Id</span><span class="nf">.init</span><span class="p">()))</span> <span class="p">{</span>
<span class="c">// do stuff</span>
<span class="p">}</span>
</code></pre></div></div>
<p>In fact, if our use case is an IMGUI, the user doesn’t even need to pass in the ID since we can create it inside of <code class="language-plaintext highlighter-rouge">button</code>:</p>
<div class="language-rs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">noinline</span> <span class="k">fn</span> <span class="nf">button</span><span class="p">(</span><span class="n">name</span><span class="p">:</span> <span class="p">[]</span><span class="k">const</span> <span class="nb">u8</span><span class="p">)</span> <span class="nb">bool</span> <span class="p">{</span>
<span class="k">const</span> <span class="n">id</span> <span class="o">=</span> <span class="o">@</span><span class="nf">returnAddress</span><span class="p">();</span>
<span class="c">// ...</span>
<span class="p">}</span>
<span class="c">// ...</span>
<span class="k">if</span> <span class="p">(</span><span class="nf">button</span><span class="p">(</span><span class="s">"click me"</span><span class="p">))</span> <span class="p">{</span>
<span class="c">// do stuff</span>
<span class="p">}</span>
</code></pre></div></div>
<h2 id="interesting-use-cases">Interesting Use Cases</h2>
<h3 id="imgui-iteration">IMGUI Iteration</h3>
<p>If you’re using the unique value to create an IMGUI ID, you can pair it with an index to allow for creating UI elements in a loop.</p>
<p>For example, in Zig that might look like this:</p>
<div class="language-rs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">pub</span> <span class="k">const</span> <span class="n">Id</span> <span class="o">=</span> <span class="k">struct</span> <span class="p">{</span>
<span class="n">returnAddress</span><span class="p">:</span> <span class="nb">usize</span><span class="p">,</span>
<span class="n">index</span><span class="p">:</span> <span class="nb">usize</span><span class="p">,</span>
<span class="p">};</span>
<span class="k">pub</span> <span class="k">const</span> <span class="n">ButtonDesc</span> <span class="o">=</span> <span class="k">struct</span> <span class="p">{</span>
<span class="n">name</span><span class="p">:</span> <span class="p">[]</span><span class="k">const</span> <span class="nb">u8</span><span class="p">,</span>
<span class="n">index</span><span class="p">:</span> <span class="nb">usize</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span>
<span class="c">// ...</span>
<span class="p">};</span>
<span class="n">noinline</span> <span class="k">fn</span> <span class="nf">button</span><span class="p">(</span><span class="n">desc</span><span class="p">:</span> <span class="n">ButtonDesc</span><span class="p">)</span> <span class="nb">bool</span> <span class="p">{</span>
<span class="k">const</span> <span class="n">id</span> <span class="o">=</span> <span class="n">Id</span><span class="p">{</span>
<span class="py">.index</span> <span class="o">=</span> <span class="n">desc</span><span class="py">.index</span><span class="p">,</span>
<span class="py">.returnAddress</span> <span class="o">=</span> <span class="o">@</span><span class="nf">returnAddress</span><span class="p">(),</span>
<span class="p">};</span>
<span class="c">// ...</span>
<span class="p">}</span>
<span class="c">// ...</span>
<span class="k">const</span> <span class="n">items</span> <span class="o">=</span> <span class="p">[</span><span class="mi">_</span><span class="p">][]</span><span class="k">const</span> <span class="nb">u8</span><span class="p">{</span>
<span class="s">"foo"</span><span class="p">,</span>
<span class="s">"bar"</span><span class="p">,</span>
<span class="s">"baz"</span><span class="p">,</span>
<span class="p">};</span>
<span class="k">for</span> <span class="p">(</span><span class="n">items</span><span class="p">,</span> <span class="mi">0</span><span class="o">..</span><span class="p">)</span> <span class="p">|</span><span class="n">item</span><span class="p">,</span> <span class="n">i</span><span class="p">|</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nf">button</span><span class="p">(</span><span class="err">.</span><span class="p">{</span> <span class="py">.name</span> <span class="o">=</span> <span class="n">item</span><span class="p">,</span> <span class="py">.index</span> <span class="o">=</span> <span class="n">i</span> <span class="p">}))</span> <span class="p">{</span>
<span class="n">std</span><span class="py">.debug</span><span class="nf">.print</span><span class="p">(</span><span class="s">"You clicked {s}!</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="err">.</span><span class="p">{</span><span class="n">item</span><span class="p">});</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">if</span> <span class="p">(</span><span class="nf">button</span><span class="p">(</span><span class="err">.</span><span class="p">{</span> <span class="py">.name</span> <span class="o">=</span> <span class="s">"back"</span> <span class="p">}))</span> <span class="p">{</span>
<span class="n">std</span><span class="py">.debug</span><span class="nf">.print</span><span class="p">(</span><span class="s">"closed</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="err">.</span><span class="p">{});</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Alternatively, if you need arbitrarily nested loops and a shared index isn’t an option, you could store a slice of indices. I have not yet had a use case that requires this, so I don’t do it.</p>
<h3 id="cancellable-sounds">Cancellable Sounds</h3>
<p>In Way of Rhea, I wanted voice lines to interrupt each other.</p>
<p>I could have each sound return an ID that you can keep track of, and later use to cancel the sound, but that’s a lot of work:</p>
<div class="language-rs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="k">let</span> <span class="nf">Some</span><span class="p">(</span><span class="n">id</span><span class="p">)</span> <span class="o">=</span> <span class="k">self</span><span class="py">.last_line</span> <span class="p">{</span>
<span class="nn">mixer</span><span class="p">::</span><span class="nf">cancel_sound</span><span class="p">(</span><span class="k">self</span><span class="py">.last_line</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">self</span><span class="py">.last_line</span> <span class="o">=</span> <span class="nf">Some</span><span class="p">(</span><span class="nn">mixer</span><span class="p">::</span><span class="nf">play_sound</span><span class="p">(</span><span class="n">f</span><span class="s">"dialogue/{sound}.ogg"</span><span class="p">,</span> <span class="mf">0.5</span><span class="p">));</span>
</code></pre></div></div>
<p>Instead I have the user pass in a unique ID. When you play a sound, you have the option to pass in the ID, and any sounds already playing with that same ID are cancelled. If you need to explicitly cancel a sound, you can reference it later by the same ID.</p>
<p>The above block becomes a single line:</p>
<div class="language-rs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nn">mixer</span><span class="p">::</span><span class="nf">play_sound_ex</span><span class="p">(</span><span class="n">f</span><span class="s">"dialogue/{sound}.ogg"</span><span class="p">,</span> <span class="mf">0.5</span><span class="p">,</span> <span class="n">unique</span><span class="p">);</span>
</code></pre></div></div>
<p>(<code class="language-plaintext highlighter-rouge">unique</code> is my scripting language’s keyword for generating a unique value at compile time.)</p>
<p>An alternate solution would be to dedicate a mixer channel to voices. This has the benefit of making it easy to control voice volume via channel gain, and to guarantee voices are given priority if too many sounds are playing.</p>
<p>I opted for the unique ID solution because Way of Rhea characters speak gibberish so volume is not important, there are never too many sounds so priority is not an issue, and it only took 15 minutes to implement & lets me cancel other sounds as well.</p>Mason RemaleyAn IMGUI. Sometimes, you just want some unique bits to use as an ID. Maybe you’re working with an immediate mode GUI, or need to generate IDs for cancellable sounds. In many cases you could technically get away with manually enumerating the various unique IDs in a header somewhere, but this is very inconvenient–especially in the case of IMGUIs: if you do this, every time you add or remove a UI element, you need to make a secondary change elsewhere in the codebase. You won’t be able to freely copy paste UI code.Announcing the Way of Rhea Closed Beta!2023-11-03T00:00:00-07:002023-11-03T00:00:00-07:00https://www.anthropicstudios.com/2023/11/03/wor-closed-beta<figure>
<img width="80%" src="/assets/posts/2024-02-19-wor-closed-beta/cover.png" />
</figure>
<p>Exciting news–we’re starting the closed beta of Way of Rhea!</p>
<p>I’m going to be running it through our <a href="https://discord.gg/JGeVt5XwPP">Discord</a>. I’m only adding a couple people at a time so that I have time to address feedback between participants–if you want me to add you to the list, ping me there!</p>
<!--more-->
<h2 id="v010-release-notes">v0.1.0 Release Notes</h2>
<ul>
<li>All levels now have unique music</li>
<li>Marked areas navigable by crabs with crystals</li>
<li>Improved speed controls, speed controls (optionally) affect music</li>
<li>Improved biome progress visuals</li>
<li>Shows controls on screen in more cases</li>
<li>Fixed performance problem with inactive NPCs</li>
<li>Resolved issues connections between some puzzles in the end-game, polished end game puzzles</li>
<li>Fixed problem where if you move slow enough you wouldn’t trigger gates</li>
<li>Added accel/decl to character controllers</li>
<li>Adds more background art to various areas</li>
<li>Makes paths in forest biome more visually readable</li>
<li>Added input buffering</li>
<li>Displays song titles in game</li>
<li>Stops messing with Windows power schemes</li>
<li>Warn instead of erroring when keys can’t be mapped</li>
<li>Improves music transitions and looping</li>
<li>Hub world now less cramped</li>
<li>Implemented all achievements and secrets</li>
<li>Don’t repeatedly warn on save point mismatches
Makes first ice puzzle clearer. Previously it was possible to solve it by accident, and it would appear as if the outcome was “random” since the important interaction was happening off screen. Also causes the crab to walk right instead of left carrying the orb, making it visually clearer that they have picked * up since it is sorted in front of them.</li>
<li>Named all levels</li>
<li>Removes some puzzles from the forest biome that were redundant and/or didn’t fit with the spirit of the game</li>
<li>Starts adding character voices (gibberish)</li>
<li>Improves undo/redo further, especially in crab levels</li>
<li>Fixes problem where elevator visuals didn’t align with interactivity</li>
<li>Fixed minor dialogue glitches, improved dialogue</li>
<li>Implements credits</li>
</ul>
<h2 id="v020-release-notes">v0.2.0 Release Notes</h2>
<ul>
<li>Ships debug symbols on Windows</li>
<li>Fixed inadvertent red herring in second ice level, crystals now always indicate a path a crab could take</li>
<li>Made navigable areas more visually consistent in the ice biome</li>
<li>Fixed case sensitivity issue that lead to some missing textures on Linux and Steam Deck</li>
<li>Fixed weather related performance issue</li>
<li>Added graphics options to allow for toggling weather and distortion effects</li>
<li>Fixed issue where the first puzzle in the last forest level could be bypassed</li>
<li>Fixed issue where cloud saves wouldn’t sync cross platform</li>
</ul>
<p><em>Future beta release notes will be posted to <a href="https://steamcommunity.com/games/1110620/announcements/detail/7665759271877780610?snr=2___">Steam</a> and <a href="https://discord.gg/JGeVt5XwPP">Discord</a>.</em></p>Mason RemaleyExciting news–we’re starting the closed beta of Way of Rhea! I’m going to be running it through our Discord. I’m only adding a couple people at a time so that I have time to address feedback between participants–if you want me to add you to the list, ping me there!Replying to YouTube Comments on my SYCL Talk2023-09-23T00:00:00-07:002023-09-23T00:00:00-07:00https://www.anthropicstudios.com/2023/09/23/art-tools-comments<!-- TODO:
share: email, twitter, mastodon, youtube through loris, etc
-->
<iframe width="740px" height="416px" src="https://www.youtube-nocookie.com/embed/89bLKVvF85M?si=-WcbW7CUiTG2HHvC" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen=""></iframe>
<p><br /></p>
<p>If there’s one thing I’ve learned from 29 years of internet, it’s not to reply to YouTube comments.</p>
<p>Let’s do it anyway!</p>
<hr />
<p>I gave a talk at Software You Can Love (<a href="http://softwareyoucan.love/">SYCL</a>) titled <a href="/2023/09/18/game-engines-are-art-tools/"><em>It’s Not About The Technology - Game Engines are Art Tools.</em></a> The <a href="https://youtu.be/89bLKVvF85M">YouTube recording</a> premiered on Monday, and there was a lot of high quality discussion in the comments.</p>
<!--more-->
<h1 id="table-of-contents">Table of Contents</h1>
<ul id="markdown-toc">
<li><a href="#table-of-contents" id="markdown-toc-table-of-contents">Table of Contents</a></li>
<li><a href="#building-a-game-engine-as-an-educational-endeavor" id="markdown-toc-building-a-game-engine-as-an-educational-endeavor">Building a game engine as an educational endeavor</a></li>
<li><a href="#using-off-the-shelf-engines" id="markdown-toc-using-off-the-shelf-engines">Using off the shelf engines</a></li>
<li><a href="#engines-for-design-vs-for-production" id="markdown-toc-engines-for-design-vs-for-production">Engines for design vs for production</a></li>
<li><a href="#the-danger-of-going-too-far" id="markdown-toc-the-danger-of-going-too-far">The danger of going too far</a></li>
<li><a href="#what-about-tech-demos" id="markdown-toc-what-about-tech-demos">What about tech demos?</a></li>
<li><a href="#highlights-from-chat" id="markdown-toc-highlights-from-chat">Highlights from chat</a> <ul>
<li><a href="#hot-swapping-without-having-to-implement-hot-swapping" id="markdown-toc-hot-swapping-without-having-to-implement-hot-swapping">Hot swapping without having to implement hot swapping</a></li>
<li><a href="#people-love-ecs-but-dont-use-it" id="markdown-toc-people-love-ecs-but-dont-use-it">People love ECS but don’t use it</a></li>
</ul>
</li>
</ul>
<h1 id="building-a-game-engine-as-an-educational-endeavor">Building a game engine as an educational endeavor</h1>
<p><img src="/assets/posts/2023-09-23-art-tools-comments/for-education.png" alt="" /></p>
<blockquote>
<p>@kenneth_romero: Great talk, loved the slide on the different experience levels and questioning the current popular patterns. Been thinking about making a game engine, honestly might take your first approach to just learn and enjoy it.</p>
</blockquote>
<p>I think that’s a great idea.</p>
<p>In my talk, I mentioned that I took a minimalist approach to my first game engine, and that in retrospect this was the wrong approach to building an engine that’s productive to work with. However, it’s a great approach if you want to learn!</p>
<p>By rejecting standard patterns, you get to make your own mistakes. As a result you gain a deep appreciation for the patterns that work in a way you wouldn’t have if you just gone with the flow.</p>
<h1 id="using-off-the-shelf-engines">Using off the shelf engines</h1>
<p><img src="/assets/posts/2023-09-23-art-tools-comments/off-the-shelf.png" alt="" /></p>
<blockquote>
<p>@distantforest2481: Fantastic talk Mason, a timely talk that highlights the hidden costs of not focusing on what really matters, the needs of the users. If we dont focus on that, In our case as developers, it’s misplaced time. And on a larger scale, the whole cough Unity debacle.</p>
<p>Something on the top of my mind though is that if we take this thought to its conclusion, why not use a game engine? The users don’t care what engine you use, as long as the game is “Good™”. I’m wrestling with this myself, as I’m the same where I’m stubborn and I want to create my own game engine. Sometimes there’s a reason why Game Engines do the things they do even if you don’t initially see the reason why (Such as the ECS thing). Game engines like Godot have spent years already tackling issues that have risen on various points on the development cycle. What are your thoughts to this?</p>
</blockquote>
<p>“The users don’t care what engine you use” is a great point, I think a lot of programmers fail to consider the generalized version of this statement–your users don’t care what language you use, how your code is indented, how elegant your algorithms are, if you organize your files well, etc, they care whether or not your software provides them with value. Those other things all matter, but they only matter insofar as they support you in providing value to the user.</p>
<p>So, should you use an off the shelf game engine? IMO it depends on your goals.</p>
<p>If you’re making games as a hobby, then you should do whatever’s the most fun for you. If you want to learn, then you should decide whether you want to learn more about low level programming, or game design.</p>
<p>If you’re making games professionally, or have aspirations to, then it’s a bit trickier. I definitely <a href="https://www.anthropicstudios.com/2023/09/13/make-your-own-game-engine/">would not recommend anyone use Unity</a> for new projects of course, but like you mentioned, open source engines like <a href="https://godotengine.org/">Godot</a> are a great choice.</p>
<p>Some games <em>require</em> rolling custom tech (e.g. <a href="https://store.steampowered.com/app/1167630/Teardown/">Teardown</a>) which makes the decision easy. If the game you’re building doesn’t, then you have to ask yourself, what about your engine would be an improvement for your workflow over using something off the shelf, and is that worth the cost in dev time?</p>
<p>That being said, <a href="https://open.spotify.com/episode/4PBr3FcL4t3tD31Pgh0k0s">you don’t owe the world pragmatism!</a> If you like doing engine work, and can afford to, then go for it. Unless you sold your company to investors or have employees depending on you, you’re allowed to choose to have fun if you can afford it, and <em>someone</em> needs to carry the torch or there will be nobody left to maintain the popular general purpose engines in ten years. You have some flexibility here too because you can lessen the cost in dev time by not doing everything from scratch. There’s nothing wrong with writing a game engine that is essentially glue code for various middleware.</p>
<p>All that being said–I think using an off the shelf engine is a great choice (and often the only choice) for many teams/projects.</p>
<h1 id="engines-for-design-vs-for-production">Engines for design vs for production</h1>
<p><img src="/assets/posts/2023-09-23-art-tools-comments/two-engines.png" alt="" /></p>
<blockquote>
<p>@loabrasumente2283: Fantastic talk! Normally we don’t see talk like this here.</p>
<p>I have a question about shipping. You mentioned game engine as an art tool for game designers. But I feel like it’s not in the same sense as Photoshop, because after you finished the design of an asset, you don’t need Photoshop anymore but need to embed it into “production environment” lets say. On the contrary, after we are done experimenting a game design idea, say the fast forward, in an “art tool” engine, should we port it to an engine that’s more mature, or we should make the art tool powerful enough so it’s shippable by itself, otherwise potentially we will have to maintain two parallel implementations of the same game, one for shipping, one for prototyping?</p>
<p>I’m not a game dev, I don’t know if a game engine can exist solely for the purpose of prototyping. I also don’t know the status quo of existing game engines, how good they are as an art tool. So I’m really curious about the direction you are heading.</p>
<p>Also I was wondering if we can design in Unity, come back with a bag of ideas and implement them in a minimal way?</p>
</blockquote>
<p>In my talk, I claimed that a game engine is to a game designer what Photoshop is to a visual artist.</p>
<p>There <em>is</em> a fundamental difference between these two things insofar as visual artists don’t ship Photoshop when they distribute their work. However, the idea here isn’t that these things are identical, but rather that looking at engines through this lens results in the correct prioritization of effort.</p>
<p>It might seem like focusing on the designer and not the player misses 50% of the picture, but, since the designer’s job is to care about the player’s experience and the runtime directly impacts that, we can justify all work on the runtime through the lens of giving the designer what they need to create a good experience for the player. In looking at things this way, we correctly see the runtime as part of a bigger picture.</p>
<p>In response to the idea of using one engine for design and a separate engine for release–you’re onto something here, but I’m gonna tweak the idea a little bit!</p>
<p>Porting a finished game from one engine to another is typically very costly, so exactly as written this is unlikely to be worth it in most cases. Furthermore, an engine that’s good as an art tool likely has a solid runtime too since the two are intertwined, and the runtime is easier to get right.</p>
<p>(Unity is an interesting case since you don’t want to ship Unity games anymore purely for <em>business</em> reasons, but the prior point still stands.)</p>
<p>All that being said, many people <em>do</em> follow this pattern, just at a smaller scale. For example if you’re making a card game, you might prototype it with physical cards first. Or if you’re making a first person narrative game, you might make a mock up of the story in something like <a href="http://twinery.org/">Twine</a> to try out the main ideas. This workflow is a big reason <a href="https://itch.io/jams">game jams</a> are popular.</p>
<p>The idea here is typically to explore the unique/risky/important parts of an idea as quickly as possible to de-risk before committing to the idea and building a stable foundation. The cost of porting isn’t an issue here because this prototype is typically small, and intentionally only covers easy to port ideas or things that need to be redone at higher fidelity before shipping anyway.</p>
<h1 id="the-danger-of-going-too-far">The danger of going too far</h1>
<p><img src="/assets/posts/2023-09-23-art-tools-comments/too-far.png" alt="" /></p>
<blockquote>
<p>@geertenvink: Loved the talk, really interesting. The end sounds like you’re well on your way to finding some parallels with the wonderful world of web dev, where everything is user stories and customer journeys and “program for the user” to sometimes extreme degrees. I’d say “build with the user in mind” is great, but the common pitfall is “spend way too much time taking inventory of what the users want and what the users like” instead of just building what you, a team of experts, know will be good enough for 90% of the people that will use your product.</p>
</blockquote>
<p>I’ve worked for companies that operated this way, and I agree it’s not productive at all. Big companies are where good paradigms go to die. :)</p>
<p>I think most small developers were born with antibodies against this trap, but it still gets people sometimes.</p>
<p>I used to help run a <a href="https://www.meetup.com/indie-game-developers-of-silicon-valley/events/?type=upcoming">monthly playtest night</a> for UCSC when I lived in the area, and I would see this a lot in people running their first playtests. It was common for developers to ask the players questions like “did you think the game was fun?” or “what features did you like or dislike?” and then try to directly implement the feedback they got.</p>
<p>You have to come into these things knowing that you’re the expert. You’re asking the users for perspective, not for answers. That is if you’re even asking–often I get more perspective from of watching users interact with my work than from asking them questions.</p>
<h1 id="what-about-tech-demos">What about tech demos?</h1>
<blockquote>
<p>Some engines are entirely about the tech and the “game” part is completely reliant on the technology. It’s okay for games to be tech demos, or almost entirely about the tech–assuming the tech is novel and enjoyabel to people. Wolf3D, Doom, and Quake were basically tech demos. They came up witha game design and art assets to fit the engine–not the other way around.</p>
</blockquote>
<p>(I’m paraphrasing this one because the author was weirdly angry and had an inappropriate username, but I think this excerpt raises an interesting point.)</p>
<p>There’s always feedback in both directions, but I wouldn’t personally call Doom/Wolf3D/Quake tech demos–Doom actually had <a href="https://doomwiki.org/wiki/Doom_v0.2">a separate tech demo</a>. If I remember the <a href="https://en.wikipedia.org/wiki/Masters_of_Doom">books about early Id Software</a> correctly, it seemed like John Carmack saw his job as building tools for John Romero to build fun games with which is the perspective I’m advocating for.</p>
<p>However, pure tech demos <em>do</em> exist. I certainly love <a href="https://www.youtube.com/watch?v=WU0gvPcc3jQ">a good tech demo</a>.</p>
<p>So right off the bat–sure! The more you’re building something that is literally about the technology, the less the advice in my talk titled “It’s Not About The Technology” will apply to you.</p>
<p>That being said, a tech demo is also art right? It’s essentially a high budget demoscene demo where you forgot that demoscene demos aren’t supposed to be 80gb. You ship some software to the user, it does cool and impressive stuff with their hardware, and they enjoy the results!</p>
<p>If you’re building an engine exclusively for tech demos–usually you aren’t, but you could be–you might prioritize different engine features, but you’re still building tools that eventually get used to build novel experiences for the player. The player doesn’t like your tech demo because it uses RTX, the player likes your tech demo because it <em>looks good</em> using RTX.</p>
<h1 id="highlights-from-chat">Highlights from chat</h1>
<h2 id="hot-swapping-without-having-to-implement-hot-swapping">Hot swapping without having to implement hot swapping</h2>
<p><img src="/assets/posts/2023-09-23-art-tools-comments/hot-swapping.png" width="60%" /></p>
<blockquote>
<p>Aaron Winter: I figured hot swapping might not be necessary if the compiler recompiles the code fast enough that it wouldn’t bother me - however it would not bypass the asset loading when restarting the game.</p>
</blockquote>
<p>This is a great point I don’t see discussed often–hot swapping support is a gradient, you don’t have to take it all the way to start to get benefits.</p>
<p>For example, if you implement a hot key in your engine that saves, exits, recompiles, and then relaunches from save, you’ll get most of the convenience of hot swapping without actually having to implement the tricky parts.</p>
<p>That is, provided your compile times are short and your assets load quickly. For small games this can actually be viable! Just make sure you don’t store your textures in a format that takes a while to decode like PNG; right now I <a href="https://github.com/nothings/stb/blob/master/stb_dxt.h">DXT compress</a> my images and then lz4 them before saving to disk.</p>
<h2 id="people-love-ecs-but-dont-use-it">People love ECS but don’t use it</h2>
<p><img src="/assets/posts/2023-09-23-art-tools-comments/ecs.png" width="60%" /></p>
<blockquote>
<p>Michael λ: ECS is so well loved and yet I never run into it in practice</p>
</blockquote>
<p>I spent some time in my talk explaining why everyone says that <a href="https://cowboyprogramming.com/2007/01/05/evolve-your-heirachy/">ECS</a> is a good pattern, why their reasons are wrong, and why it’s a good pattern anyway.</p>
<p>While I was on this topic I said “I’m not a AAA person, but my understanding is that [ECS] is industry standard.”</p>
<p>Since then I’ve been told that it’s <em>not</em> industry standard, it’s the thing everyone wishes they were doing but isn’t. I guess this isn’t too surprising–none of the common off the shelf engines have first class support, and most in house engines have been around <em>a long</em> time.</p>Mason RemaleyIf there’s one thing I’ve learned from 29 years of internet, it’s not to reply to YouTube comments. Let’s do it anyway! I gave a talk at Software You Can Love (SYCL) titled It’s Not About The Technology - Game Engines are Art Tools. The YouTube recording premiered on Monday, and there was a lot of high quality discussion in the comments.It’s Not About the Technology - Game Engines are Art Tools2023-09-18T00:00:00-07:002023-09-18T00:00:00-07:00https://www.anthropicstudios.com/2023/09/18/game-engines-are-art-tools<!-- TODO: make youtube playlist of my talks? update twitter link, maybe others, etc -->
<p>In June, I gave a talk at <a href="https://softwareyoucan.love/">Software You Can Love</a> titled <em>It’s Not About The Technology - Game Engines are Art Tools</em>. The intended audience was systems programmers, but there are lessons here for people evaluating off the shelf systems as well.</p>
<p>This is a part two to my talk from Handmade Seattle last year, <a href="/2022/12/23/survivorship-bias/">It’s Not Survivorship Bias - On Successful Software Endeavors</a>.</p>
<p>Please note that I’m playing a little joke at the beginning that may not work as well without a captive audience considering <a href="/2023/09/13/make-your-own-game-engine/">the recent Unity BS</a>, so please watch the whole thing before yelling at me.</p>
<p>See my replies to comments <a href="/2023/09/23/art-tools-comments/">here</a>.</p>
<p><strong><a href="https://www.youtube.com/watch?v=89bLKVvF85M">Recording (premieres at 12pm PST)</a></strong></p>
<iframe width="740px" height="416px" src="https://www.youtube-nocookie.com/embed/89bLKVvF85M?si=-WcbW7CUiTG2HHvC" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen=""></iframe>
<p><strong><a href="https://docs.google.com/presentation/d/1UKe5AuEIWIg1kzyw3AwSBYSF0J6FyrqEQ-jK81W-_a0/">Slides</a></strong></p>
<iframe src="https://docs.google.com/presentation/d/e/2PACX-1vTKIFZRhwQtBsIsGPZ7NRyuxwQZ9JapW34eOXs9PUcOqtWSfAH0Im3aiIa4UMJarooqWbbbAm1V8klH/embed?start=false&loop=false&delayms=60000" frameborder="0" width="740" height="452" allowfullscreen="true" mozallowfullscreen="true" webkitallowfullscreen="true"></iframe>Mason RemaleyIn June, I gave a talk at Software You Can Love titled It’s Not About The Technology - Game Engines are Art Tools. The intended audience was systems programmers, but there are lessons here for people evaluating off the shelf systems as well. This is a part two to my talk from Handmade Seattle last year, It’s Not Survivorship Bias - On Successful Software Endeavors. Please note that I’m playing a little joke at the beginning that may not work as well without a captive audience considering the recent Unity BS, so please watch the whole thing before yelling at me. See my replies to comments here. Recording (premieres at 12pm PST) SlidesLUCID Kickstarter Goes Live!2023-09-18T00:00:00-07:002023-09-18T00:00:00-07:00https://www.anthropicstudios.com/2023/09/18/lucid-kickstarter<p><a href="https://www.kickstarter.com/projects/matteblackstudio/lucid-2"><img width="100%" src="/assets/posts/2023-09-18-lucid-kickstarter/lucid.gif" /></a></p>
<p>A game I do some programming for, LUCID, just went live with <a href="https://www.kickstarter.com/projects/matteblackstudio/lucid-2">its Kickstarter</a>!</p>
<!--more-->
<p>I’m sure Eric would appreciate your support. Don’t worry—I’m still hard at work on Way of Rhea—I typically have a few projects in the oven at a time. :)</p>Mason RemaleyA game I do some programming for, LUCID, just went live with its Kickstarter!Make Your Own Game Engine2023-09-13T00:00:00-07:002023-09-13T00:00:00-07:00https://www.anthropicstudios.com/2023/09/13/make-your-own-game-engine<p><img style="width:100%" src="/assets/posts/2023-09-13-how-to-make-a-game-engine/unity.png" alt="unity logo wearing a top hat and monocle" /></p>
<p><a href="https://www.axios.com/2023/09/13/unity-runtime-fee-policy-marc-whitten">Recent events</a> have a lot of studios <a href="https://twitter.com/megacrit/status/1702077576209207611?s=46&t=Vk6MuilKkI8SdxRKjM_YXQ">thinking about their choice of game engine</a>, and at first glance, options appear to be limited. But things don’t have to be this way.</p>
<p>For most studios, the optimal short term strategy is to wait and see if Unity is forced to back down. Retroactively altering the engine license for shipped games certainly doesn’t <em>sound</em> legal.</p>
<p>While waiting it out may be pragmatic for existing projects, it doesn’t save you from the nightmare of having your entire business beholden to the whims of Unity Technologies™.</p>
<p>You could decide to switch engines for future games. Many people will go to Unreal. I trust Epic more than I trust Unity, in particular because they give everyone source access, but if you’re switching anyway I recommend looking into the free and open source engine <a href="https://godotengine.org/">Godot</a> which is MIT licensed. Info on console support <a href="https://docs.godotengine.org/en/stable/tutorials/platform/consoles.html">here.</a></p>
<p>There is, however, another option. The <em>nuclear option.</em></p>
<p>You could write your own game engine.</p>
<!--more-->
<h2 id="is-writing-an-engine-even-viable-anymore-why-put-in-the-effort">Is writing an engine even viable anymore? Why put in the effort?</h2>
<p><img style="width:100%" src="/assets/posts/2023-09-13-how-to-make-a-game-engine/editor.gif" alt="Way of Rhea's editor" /></p>
<p>Other developers are often surprised that I wrote a custom engine for <a href="https://store.steampowered.com/app/1110620?utm_campaign=makeyourowngameengine">Way of Rhea</a>, but they shouldn’t be. I say this without any smugness: I wrote my own engine in part to avoid the exact sort of nightmare that all Unity users are facing right now.</p>
<p>If it’s doable, why doesn’t anyone do it?</p>
<p>Well, they do:</p>
<ul>
<li><a href="https://en.wikipedia.org/wiki/Don't_Starve">Don’t Starve</a></li>
<li><a href="https://en.wikipedia.org/wiki/No_Man's_Sky">No Man’s Sky</a></li>
<li><a href="https://en.wikipedia.org/wiki/Soma_(video_game)">Soma</a></li>
<li><a href="https://en.wikipedia.org/wiki/Guacamelee!">Guacamelee</a></li>
<li><a href="https://en.wikipedia.org/wiki/Factorio">Factorio</a></li>
<li><a href="https://en.wikipedia.org/wiki/Prison_Architect">Prison Architect</a></li>
<li><a href="https://en.wikipedia.org/wiki/The_Witness_(2016_video_game)">The Witness</a></li>
<li><a href="https://en.wikipedia.org/wiki/Thimbleweed_Park">Thimbleweed Park</a></li>
<li><a href="https://en.wikipedia.org/wiki/Celeste_(video_game)">Celeste</a></li>
<li><a href="https://en.wikipedia.org/wiki/Overgrowth_(video_game)">Overgrowth</a></li>
<li><a href="https://en.wikipedia.org/wiki/Slay_the_Spire">Slay The Spire</a></li>
<li><a href="https://en.wikipedia.org/wiki/Hades_(video_game)">Hades</a></li>
<li><a href="https://en.wikipedia.org/wiki/Pyre_(video_game)">Pyre</a></li>
<li><a href="https://en.wikipedia.org/wiki/FTL:_Faster_Than_Light">FTL</a></li>
<li><a href="https://en.wikipedia.org/wiki/Thumper_(video_game)">Thumper</a></li>
<li><a href="https://en.wikipedia.org/wiki/Papers%2C_Please">Papers Please</a></li>
<li><a href="https://en.wikipedia.org/wiki/Stardew_Valley">Stardew Valley</a></li>
<li><a href="https://en.wikipedia.org/wiki/Teardown_(video_game)">Teardown</a></li>
<li><a href="https://en.wikipedia.org/wiki/Darkest_Dungeon">Darkest Dungeon</a></li>
<li>…</li>
</ul>
<p>Making a custom engine is less common than using Unity because it’s more work. It’s not the right choice for everyone. But for a strong programmer, or a team of strong programmers, it’s perfectly <em>viable</em>.</p>
<p>If you write your own engine, you control your own destiny. Nobody can take it away from you. Nobody can retroactively change the pricing model. You can fix your own bugs. Support platforms and features you care about. Etc.</p>
<p>And, if your engine finds a niche, you can help everyone else out by open sourcing it and turning it into its own business, <a href="https://ziglang.org/news/announcing-zig-software-foundation/">preferably a non-profit.</a></p>
<p>Don’t start there though. Make it work for you first.</p>
<h2 id="advice-on-engine-design">Advice on engine design</h2>
<p><img style="width:100%" src="/assets/posts/2023-09-13-how-to-make-a-game-engine/makegame.png" alt="int main(int argc, char* argv[]) { if (DoButton("Make Game")) { MakeVideoGame(); } return 0;}" /></p>
<p>Unity attempts to be an engine for every genre, and every team. Building an engine like this is a massive undertaking. If you decide to build an engine, don’t try to build Unity. Build an engine for you.</p>
<p>If your game is graphically intensive, learn a low level language (<a href="https://ziglang.org/">Zig</a>, <a href="https://www.rust-lang.org/">Rust</a>, C, C++.) Learn either a graphics API (<a href="https://learnopengl.com/">OpenGL</a>, <a href="https://vulkan-tutorial.com/">Vulkan</a>, DirectX, WebGPU), a wrapper (<a href="https://gpuopen-librariesandsdks.github.io/V-EZ/">V-EZ</a>, <a href="https://github.com/floooh/sokol">sokol</a>), or a graphics framework (<a href="https://www.ogre3d.org/">Ogre</a>, etc.).</p>
<p>If your game is less performance hungry, <strong>write the engine in whatever language you’re most comfortable with.</strong> Consider frameworks and libraries like <a href="https://en.wikipedia.org/wiki/MonoGame">MonoGame</a> and <a href="https://www.raylib.com/">RayLib</a>. Leverage middleware, preferably FOSS middleware. You don’t have to write the engine from scratch.</p>
<p>Learn from others. Don’t overengineer, but don’t underinvest in your tools either, your tools are what help you iterate, iteration is what makes your game fun. Employ patterns that have been shown to be productive.</p>
<h2 id="helpful-links">Helpful links</h2>
<p>Here are a few generally applicable links:</p>
<ul>
<li><a href="https://gameprogrammingpatterns.com/">Game Programming Patterns</a></li>
<li><a href="https://www.gafferongames.com/post/fix_your_timestep/">Fix Your Timestep</a></li>
<li><a href="https://medium.com/geekculture/how-to-make-your-own-game-engine-and-why-ddf0acbc5f3">How To Make Your Own Game Engine and Why</a></li>
</ul>
<p>Here are some resources on lower level stuff I’ve found useful, if you go that route:</p>
<ul>
<li><a href="https://www.gameenginebook.com/">Game Engine Architecture</a></li>
<li><a href="http://foundationsofgameenginedev.com/">Foundations of Game Engine Development</a></li>
<li><a href="https://ruby0x1.github.io/machinery_blog_archive/">Archive of the Our Machinery blog</a></li>
<li><a href="https://handmadehero.org/">Handmade Hero</a></li>
<li><a href="https://www.amazon.com/Game-Physics-Engine-Development-Commercial-Grade/dp/0123819768/">Game Engine Physics Development</a></li>
<li><a href="https://learnopengl.com/">Learn OpenGL</a></li>
<li><a href="https://vulkan-tutorial.com/">Vulkan Tutorial</a></li>
</ul>
<p>I hope this post inspires at least one person to roll their own tech. Let’s escape the nightmare together.</p>Mason RemaleyRecent events have a lot of studios thinking about their choice of game engine, and at first glance, options appear to be limited. But things don’t have to be this way. For most studios, the optimal short term strategy is to wait and see if Unity is forced to back down. Retroactively altering the engine license for shipped games certainly doesn’t sound legal. While waiting it out may be pragmatic for existing projects, it doesn’t save you from the nightmare of having your entire business beholden to the whims of Unity Technologies™. You could decide to switch engines for future games. Many people will go to Unreal. I trust Epic more than I trust Unity, in particular because they give everyone source access, but if you’re switching anyway I recommend looking into the free and open source engine Godot which is MIT licensed. Info on console support here. There is, however, another option. The nuclear option. You could write your own game engine.Way of Rhea - Linux and Steamdeck Support!2023-08-21T00:00:00-07:002023-08-21T00:00:00-07:00https://www.anthropicstudios.com/2023/08/21/way-of-rhea-linux<p><img style="width:100%" src="/assets/posts/wor-linux/rendering.jpg" alt="Way of Rhea running natively on Steam Deck" /></p>
<p>My puzzle game Way of Rhea now runs natively on Linux, and Steam Deck! This is in addition to the existing Windows support.</p>
<p>The new demo also has some quality of life improvements–namely the ability to pause and fast-forward time.</p>
<p>You can <a href="https://store.steampowered.com/app/1110620?utm_source=linux-post">give the free demo a try here</a>, if you run into any issues please <a href="mailto:mason@anthropicstudios.com">let me know</a>. :) <strong>Please note that if you’ve ever run the game through Proton, Steam will default to Proton despite the presence of a native build. The UI does not always reflect this correctly, you need to toggle Proton on and back off.</strong></p>
<p>The rest of this post covers some technical details for those interested in the porting process, and unfortunately, announces that I’m dropping the planned macOS support.</p>
<!--more-->
<p><em>If you work at Valve and want to review the build so we can be part of Steam Deck Verified, that would be much appreciated! I’m also happy to help you repro the bugs I worked around if they aren’t known issues, <a href="mailto:mason@anthropicstudios.com">shoot me an email</a>.</em></p>
<h1 id="table-of-contents">Table of Contents</h1>
<ul id="markdown-toc">
<li><a href="#table-of-contents" id="markdown-toc-table-of-contents">Table of Contents</a></li>
<li><a href="#technical-info" id="markdown-toc-technical-info">Technical info</a> <ul>
<li><a href="#why-port-to-linux" id="markdown-toc-why-port-to-linux">Why Port to Linux?</a></li>
<li><a href="#why-not-rely-on-proton" id="markdown-toc-why-not-rely-on-proton">Why not rely on Proton?</a></li>
<li><a href="#windowing" id="markdown-toc-windowing">Windowing</a></li>
<li><a href="#graphics-card-selection" id="markdown-toc-graphics-card-selection">Graphics card selection</a></li>
<li><a href="#steam-deck-specific-tweaks" id="markdown-toc-steam-deck-specific-tweaks">Steam Deck specific tweaks</a> <ul>
<li><a href="#audio-problems" id="markdown-toc-audio-problems">Audio problems</a></li>
<li><a href="#steam-input-problems" id="markdown-toc-steam-input-problems">Steam Input problems</a> <ul>
<li><a href="#ovelay-issues" id="markdown-toc-ovelay-issues">Ovelay issues</a></li>
<li><a href="#action-sets" id="markdown-toc-action-sets">Action sets</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="#takeaways" id="markdown-toc-takeaways">Takeaways</a></li>
</ul>
</li>
<li><a href="#way-of-rhea-drops-plans-to-support-macos" id="markdown-toc-way-of-rhea-drops-plans-to-support-macos">Way of Rhea drops plans to support macOS</a> <ul>
<li><a href="#why-im-dropping-macos-support" id="markdown-toc-why-im-dropping-macos-support">Why I’m dropping macOS support</a></li>
<li><a href="#how-can-i-play-way-of-rhea-if-im-a-mac-user" id="markdown-toc-how-can-i-play-way-of-rhea-if-im-a-mac-user">How can I play Way of Rhea if I’m a Mac user?</a></li>
</ul>
</li>
</ul>
<h1 id="technical-info">Technical info</h1>
<h3 id="why-port-to-linux">Why Port to Linux?</h3>
<p><img style="width:50%" src="/assets/posts/wor-linux/hardware-survey.png" alt="Steam Hardware Survey, Windows: 96.2%, Linux: 1.96%, macOS 1.84%" /></p>
<p>The <a href="https://store.steampowered.com/hwsurvey">Steam Hardware Survey</a> is a great resource that (with user consent) reports aggregated information on what hardware/software Steam users are running.</p>
<p>As of the most recent survey, there are more Linux users on Steam than macOS! This is partly due to the release of the Steam Deck.</p>
<p>This is exciting, but, it’s still only 1.96% of users. I decided to port to Linux despite the low percentage:</p>
<ul>
<li>It wasn’t a lot of work, and ideologically I prefer Linux</li>
<li>Linux users are over-represented in the group of people that follow my work WRT Rust/Zig/etc</li>
<li>Linux is a much more pleasant environment for technical work than Windows</li>
<li>And lastly, I think my game is a great fit for the Steam Deck, which runs Linux!</li>
</ul>
<h3 id="why-not-rely-on-proton">Why not rely on Proton?</h3>
<p><a href="https://github.com/ValveSoftware/Proton">Proton</a> is Valve’s fork of <a href="https://www.winehq.org/">Wine</a>. You can use it to run Windows games on Linux–it’s is how the Steam Deck is able to play Windows only games.</p>
<p>My experience with Proton was largely positive, but when players encountered problems I found them hard to debug and hard to work around–you can’t make Proton specific tweaks since it’s pretending to be Windows.</p>
<p>Porting the game to run natively on Linux simplifies both support and development, and has the added benefit of allowing me to compile the game from Linux machines without cross compilation.</p>
<p>To everyone who has sent me crash logs from Proton–thank you! I read every crash log I get, and believe that the native Linux demo has resolved all unresolved crashes.</p>
<h3 id="windowing">Windowing</h3>
<p>I’m a glutton for punishment, so I wrote custom context creation/windowing/input code for my engine instead of using SDL2 or GLFW like a normal person.</p>
<p>On Windows this means working with <a href="https://learn.microsoft.com/en-us/windows/win32/">win32</a>, on Linux I chose to use <a href="https://www.x.org/releases/X11R7.7/doc/libX11/libX11/libX11.html">libX11</a> but will probably try Wayland next time.</p>
<p>Windowing code is always a bit of a nightmare, but my experience with X11 was better on average than working with win32. One quick tip for anyone else porting to X11, it took me forever to figure out how to make a window fullscreen because it requires <a href="https://specifications.freedesktop.org/wm-spec/latest/">extended window manager hints</a>:</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">XEvent</span> <span class="n">event</span> <span class="o">=</span> <span class="p">{};</span>
<span class="n">event</span><span class="p">.</span><span class="n">type</span> <span class="o">=</span> <span class="n">ClientMessage</span><span class="p">;</span>
<span class="n">event</span><span class="p">.</span><span class="n">xclient</span><span class="p">.</span><span class="n">window</span> <span class="o">=</span> <span class="n">window</span><span class="p">;</span>
<span class="n">event</span><span class="p">.</span><span class="n">xclient</span><span class="p">.</span><span class="n">message_type</span> <span class="o">=</span> <span class="n">XInternAtom</span><span class="p">(</span><span class="n">display</span><span class="p">,</span> <span class="s">"_NET_WM_STATE"</span><span class="p">,</span> <span class="nb">false</span><span class="p">);</span>
<span class="n">event</span><span class="p">.</span><span class="n">xclient</span><span class="p">.</span><span class="n">format</span> <span class="o">=</span> <span class="mi">32</span><span class="p">;</span>
<span class="n">event</span><span class="p">.</span><span class="n">xclient</span><span class="p">.</span><span class="n">data</span><span class="p">.</span><span class="n">l</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="n">_NET_WM_STATE_ADD</span><span class="p">;</span>
<span class="n">event</span><span class="p">.</span><span class="n">xclient</span><span class="p">.</span><span class="n">data</span><span class="p">.</span><span class="n">l</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="n">XInternAtom</span><span class="p">(</span><span class="n">display</span><span class="p">,</span> <span class="s">"_NET_WM_STATE_FULLSCREEN"</span><span class="p">,</span> <span class="nb">false</span><span class="p">);</span>
<span class="n">XSendEvent</span><span class="p">(</span>
<span class="n">display</span><span class="p">,</span>
<span class="n">DefaultRootWindow</span><span class="p">(</span><span class="n">display</span><span class="p">),</span>
<span class="nb">false</span><span class="p">,</span>
<span class="n">SubstructureNotifyMask</span> <span class="o">|</span> <span class="n">SubstructureRedirectMask</span><span class="p">,</span>
<span class="o">&</span><span class="n">event</span>
<span class="p">);</span>
</code></pre></div></div>
<h3 id="graphics-card-selection">Graphics card selection</h3>
<p>Many computers now have two graphics cards: an integrated card, and a discrete card. The integrated card is good for power saving, the discrete card is good for <em>going fast.</em></p>
<p>While most new graphics APIs allow you to enumerate the available cards and pick one, I built Way of Rhea on top of OpenGL which lacks this functionality. On Windows you can <a href="/2022/01/13/asking-windows-nicely/#hinting-you-want-the-faster-gpu">export some magic symbols</a> to request the fast card but this does not appear to work on Linux.</p>
<p>You can set the environment variables <code class="language-plaintext highlighter-rouge">DRI_PRIME</code> and <code class="language-plaintext highlighter-rouge">__NV_PRIME_RENDER_OFFLOAD</code> to 1, but this will only work if you’re using the open source Nvidia drives which in my experience are <em>slower</em> than using the integrated card because Nvidia is anticompetitive (<a href="https://nouveau.freedesktop.org/">signed firmware is required to change the clock rate.</a>)</p>
<p>The proprietary drivers <em>will</em> respect <code class="language-plaintext highlighter-rouge">__GLX_VENDOR_LIBRARY_NAME="nvidia"</code>, but that’s not an option since not all players will have Nvidia cards.</p>
<p>I sadly had to give up on solving this problem. Way of Rhea will run on the default GPU on Linux unless otherwise configured by the user, I don’t expect this to be a huge support issue since it runs well on integrated cards. In future games I’ll just use the newer graphics APIs.</p>
<h3 id="steam-deck-specific-tweaks">Steam Deck specific tweaks</h3>
<h4 id="audio-problems">Audio problems</h4>
<p>At the time of writing, some games have intermittent audio problems on Steam Deck: audio occasionally drops out, or plays at an increased tempo (but not an increased pitch.)</p>
<p>I didn’t want to ship Way of Rhea with audio glitches even if other games have them–the glitches are rare but they break immersion/focus–so I dug into the problem a bit, and was able to determine the sequence of events leading to the glitch:</p>
<ol>
<li>An underflow occurs
<ul>
<li>An underflow is when you fail to fill the audio buffer in time</li>
<li>When programming audio you want to minimize the frequency of these, but you can’t guarantee you’ll <em>never</em> underflow in a game</li>
</ul>
</li>
<li>After the underflow, pulseaudio requests <strong>more</strong> data on the next write callback
<ul>
<li>Presumably they are trying to play catch up</li>
<li>I’ve only see this behavior on Steam Deck</li>
</ul>
</li>
<li>If you honor this request, pulseaudio complains that you overflowed the buffer (bug?)</li>
<li>This repeats for all future frames, and as a result, audio is played with increased tempo
<ul>
<li>More bytes of audio are being consumed per second than should</li>
<li>The extra bytes are seemingly thrown out before playback, presumably because they’re considered overflow</li>
<li>This is essentially an inadvertent implementation of granular synthesis–<a href="https://blog.demofox.org/2018/03/05/granular-audio-synthesis/">a method for implementing tempo changes</a>!</li>
</ul>
</li>
</ol>
<p>The fix is to call <code class="language-plaintext highlighter-rouge">pa_stream_flush(stream, NULL, NULL)</code> from your underflow callback.</p>
<p>(Or at least, that fixes it for me. If you’re a pulseaudio expert and think I should be solving the problem some other way <a href="mailto:mason@anthropicstudios.com">let me know</a>!)</p>
<h4 id="steam-input-problems">Steam Input problems</h4>
<p><img style="width:100%" src="/assets/posts/wor-linux/steam-input.png" alt="The Steam Input UI" /></p>
<p><a href="https://partner.steamgames.com/doc/features/steam_controller">Steam Input</a> is a player facing feature that allows players to remap inputs, for example to add controller support to a game that didn’t ship with it.</p>
<p>It’s a <em>great</em> idea on Valve’s part, and it’s why you can play games on Steam Deck that shipped without any controller support whatsoever.</p>
<p>It also has an optional developer facing API that can be used in place of e.g. XInput. Unfortunately this API isn’t super pleasant to work with right now:</p>
<ul>
<li>Its behavior is inconsistent in important ways across platforms</li>
<li>Some advanced features are buggy/incomplete/under-documented</li>
<li>Updating your default controller layouts involves managing Valve’s custom DSLs, and the parser for their DSLs does not report errors very well</li>
<li>The API is missing some obvious safety checks</li>
</ul>
<p>Developers are free to ignore this API–that’s probably what I’ll do in the future, the player facing features will work regardless–but Way of Rhea relies on it. This section covers some Steam Input related issues I encountered while porting to Steam Deck, and how I worked around them.</p>
<h5 id="ovelay-issues">Ovelay issues</h5>
<p>Way of Rhea triggers the Steam Overlay a few places in game by calling <code class="language-plaintext highlighter-rouge">SteamAPI_ISteamInput_ShowBindingPanel</code>/<code class="language-plaintext highlighter-rouge">SteamAPI_ISteamFriends_ActivateGameOverlayToWebPage</code>.</p>
<p>Right now, if initiated on button down this operation usually fails on Steam Deck, because the game regains focus on button up. You can work around this by triggering the overlay on button up instead of on button down.</p>
<p><em>Note that the overlay will also fail to open if you previously returned from a web page in the overlay with “B” instead of by exiting the browser. You can’t work around this AFAIK, I’m just mentioning it because being unaware of it could confound your testing of the above fix.</em></p>
<h5 id="action-sets">Action sets</h5>
<p>Steam Input’s Action Sets implementation is inconsistent across platforms in a way that I cannot work around:</p>
<ol>
<li>On most devices, if you switch action sets and then call <code class="language-plaintext highlighter-rouge">SteamAPI_ISteamInput_RunFrame</code>, the following frame of data incorrectly reports all actions as inactive (inactive == not available in the active action set)</li>
<li>On the Steam Deck, it also reports all action states as false (state == pressed or not)</li>
<li>As a result, we cannot get consistent behavior across platforms if the same control is assigned to different actions in different action sets</li>
</ol>
<p>This is not a nitpick–it breaks common patterns like assigning escape to “pause” in the game action set, and to “dismiss” in the menu action set.</p>
<p>The solution is to implement the action set functionality in game instead of relying on Steam. I actually had the code for this already, because I needed it for keyboard input!</p>
<p>Unfortunately, this means that all Steam Input actions need to be stored in a single Steam Input Action Set, which is not always possible because Steam does not allow multiple actions to be bound to the same analog control. e.g. if you bind <code class="language-plaintext highlighter-rouge">menu up</code>/<code class="language-plaintext highlighter-rouge">menu down</code> to the left stick, you can’t also bind <code class="language-plaintext highlighter-rouge">player move</code> to the left stick. This isn’t an issue on digital controls.</p>
<p>There’s not really a nice answer here. I finagled things so that I’d only have one action per analog input in the default binding by re-purposing existing actions where necessary (e.g. instead of “player move” and “ui move” there’s just “move” that controls both.)</p>
<h2 id="takeaways">Takeaways</h2>
<p>While there were a few hiccups, supporting Steam Deck wasn’t a huge undertaking once the game supported Linux. Many games will work out of the box on Steam Deck because of Proton with only minor tweaks to the controls.</p>
<p>Porting to Linux itself was not a lot of work either, and has already made my development environment much pleasanter. I plan to build future games with native Linux support from the start.</p>
<h1 id="way-of-rhea-drops-plans-to-support-macos">Way of Rhea drops plans to support macOS</h1>
<p>With sincere apologies to the mac users who were looking forward to playing Way of Rhea, I’m dropping the planned macOS support. This has been a long time coming, and it’s time that I make it official. :(</p>
<h2 id="why-im-dropping-macos-support">Why I’m dropping macOS support</h2>
<p>I was an Apple user my entire life until a few years ago. However, Apple has done virtually everything within their power to dissuade me from releasing software for their platform:</p>
<ol>
<li>They deprecated OpenGL, and did not adopt Vulkan
<ul>
<li>OpenGL still works right now, but I can’t accept money for deprecated technology on a platform not known for backwards compatibility</li>
<li>I also can’t justify the cost of porting to Metal. Translation layers may be viable, but…</li>
</ul>
</li>
<li>They switched to ARM
<ul>
<li>I have nothing against ARM! But to develop for macOS, I’d need to spend thousands of dollars on a new Mac. Last time I did that all the keys fell off…</li>
<li>I’m self funded, so my resources are limited. For the price of a new Mac, I could show my game at PAX and get 10x more wishlists in a week than the total number of Mac users that have wishlisted my game in the past 5 years.</li>
</ul>
</li>
<li>Lastly, Apple now <strong>charges developers a subscription fee for the right to make or maintain software for macOS</strong>
<ul>
<li>This is inexcusable. It’s inexcusable that they did it on their phones, and it’s inexcusable that they’re bringing the practice to personal computers. I am ideologically at odds with this practice and refuse to participate.</li>
</ul>
</li>
</ol>
<p>Keep in mind, on top of all this, Apple has no leverage here–they are the <a href="https://store.steampowered.com/hwsurvey/">minority platform on Steam</a>. At the time of writing Windows users make up 96.2% of Steam users, Linux 1.96%, and macOS <em>1.84%</em>.</p>
<p>Apple is aware that they have no leverage here, but games like mine don’t affect their bottom line. Thankfully, Apple doesn’t affect my bottom line either.</p>
<h2 id="how-can-i-play-way-of-rhea-if-im-a-mac-user">How can I play Way of Rhea if I’m a Mac user?</h2>
<figure>
<img style="width:100%" src="/assets/posts/wor-linux/mac.jpg" alt="Way of Rhea running on a MacBook via Bootcamp" />
<figcatption>Way of Rhea running on a MacBook via Bootcamp.</figcatption>
</figure>
<p>If you’re a Mac user, and you still want to play Way of Rhea despite the lack of support, here are some options. I apologize that it’s not as easy as just downloading it and pressing play:</p>
<ul>
<li><a href="https://support.apple.com/guide/bootcamp-control-panel/start-up-your-mac-in-windows-or-macos-bcmp29b8ac66/mac">Dual boot</a> macOS with Windows or Linux.</li>
<li>Try running <a href="https://www.winehq.org/">Wine</a>–if you get this working <a href="mailto:mason@anthropicstudios.com">let me know</a> and I’ll update this post to mention that it’s been done!</li>
<li>Purchase a cheap computer and run Windows or Linux on it. Way of Rhea is not particularly performance intensive.</li>
<li>Purchase a <a href="https://www.steamdeck.com/en/">Steam Deck</a>. They’re cheap, and run Way of Rhea (and a bunch of other games that your Mac won’t support) great.</li>
<li>Purchase a gaming computer. If you’re used to paying for Macs, gaming computers may be more affordable than you think, and once you get one you’ll be able to run pretty much any game on Steam.</li>
</ul>Mason RemaleyMy puzzle game Way of Rhea now runs natively on Linux, and Steam Deck! This is in addition to the existing Windows support. The new demo also has some quality of life improvements–namely the ability to pause and fast-forward time. You can give the free demo a try here, if you run into any issues please let me know. :) Please note that if you’ve ever run the game through Proton, Steam will default to Proton despite the presence of a native build. The UI does not always reflect this correctly, you need to toggle Proton on and back off. The rest of this post covers some technical details for those interested in the porting process, and unfortunately, announces that I’m dropping the planned macOS support.Thoughts from GDC ‘232023-05-01T00:00:00-07:002023-05-01T00:00:00-07:00https://www.anthropicstudios.com/2023/05/01/thoughts-from-gdc<p><img style="width:100%" src="/assets/posts/gdc-2023/bus-cropped.jpg" alt="'GDC discounts for big bus. I worked at GameStop for 7 years and miss all your wonderful faces. Please drop by and say hi!!!'" /></p>
<p>Posting this a little late, but I had a great time at GDC this year! I got to catch up with a lot of people I don’t get to see very often, and meet a lot of new folks.</p>
<p>On top of that, a member of <a href="https://gumbonyc.org/">GUMBO</a> won the IGF Grand Prize, <em>and</em> the Nuovo award, with his game <a href="https://store.steampowered.com/app/1885750/Betrayal_At_Club_Low/">Betrayal At Club Low</a>. You should play it–Cosmo makes great games, and you don’t have to take my word for it this time what with the whole winning the IGF thing.</p>
<p>I’ve written up my personal takeaways from some of the talks I attended below.</p>
<!--more-->
<h1 id="table-of-contents">Table of Contents</h1>
<ul id="markdown-toc">
<li><a href="#table-of-contents" id="markdown-toc-table-of-contents">Table of Contents</a></li>
<li><a href="#talk-notes" id="markdown-toc-talk-notes">Talk Notes</a> <ul>
<li><a href="#add-senior-to-that-title-real-career-talk" id="markdown-toc-add-senior-to-that-title-real-career-talk">Add Senior to That Title: Real Career Talk</a></li>
<li><a href="#business-development-for-self-publishing-studios-and-developers" id="markdown-toc-business-development-for-self-publishing-studios-and-developers">Business Development for Self-Publishing Studios and Developers</a></li>
<li><a href="#indie-soapbox" id="markdown-toc-indie-soapbox">Indie Soapbox</a></li>
<li><a href="#game-pitch-competition" id="markdown-toc-game-pitch-competition">Game Pitch Competition</a></li>
<li><a href="#porting-on-a-budget-there-and-back-again" id="markdown-toc-porting-on-a-budget-there-and-back-again">Porting on a Budget: There and Back Again</a></li>
</ul>
</li>
<li><a href="#thoughts-on-gdc" id="markdown-toc-thoughts-on-gdc">Thoughts on GDC</a></li>
</ul>
<h1 id="talk-notes">Talk Notes</h1>
<h2 id="add-senior-to-that-title-real-career-talk">Add Senior to That Title: Real Career Talk</h2>
<figure>
<a href="https://www.gdcvault.com/play/1028912/Add-Senior-to-That-Title"><img src="/assets/posts/gdc-2023/senior.jpg" alt="cheesy graphic that says 'everybody wants to move up' with a fish swimming upstream'" /></a>
<figcaption><a href="https://www.gdcvault.com/play/1028912/Add-Senior-to-That-Title">Recording</a>, <a href="https://www.gdcvault.com/play/1028715/Add-Senior-to-That-Title">Slides</a></figcaption>
</figure>
<p>This talk by <a href="https://twitter.com/DrOctothorpe">Ben Schneider</a> walked through the difference between entry level and senior positions. I figure that since I’m working for myself, on some level I’ve sort self appointed myself “senior” and should probably understand what that actually entails!</p>
<p>My main takeaways were:</p>
<ul>
<li>A senior moves the project forward at a high level
<ul>
<li>“A real rockstar plays with the band”</li>
</ul>
</li>
<li>To do this effectively, you have to have been there before and know the way</li>
</ul>
<p>I think this is actionable advice for me. I have a tendency to get distracted by the <em>fun</em> parts of a project, at the expense of the totality. That’s fine for hobby projects, but for Game 2 I think I need some more structure in the way I work and in the way I delegate work!</p>
<h2 id="business-development-for-self-publishing-studios-and-developers">Business Development for Self-Publishing Studios and Developers</h2>
<figure>
<a href="https://www.gdcvault.com/play/1029176/Independent-Games-Summit-Business-Development"><img src="/assets/posts/gdc-2023/bizdev.jpg" alt="'getting good (experience)' and a picture of someone haphazardly? skillfully? hanging from a bunch of wires" /></a>
<figcaption><a href="https://www.gdcvault.com/play/1029176/Independent-Games-Summit-Business-Development">Recording</a>, <a href="https://www.gdcvault.com/play/1028735/Independent-Games-Summit-Business-Development">Slides</a></figcaption>
</figure>
<p><a href="https://twitter.com/lucaspessoaf">Lucas Pessoa</a> and <a href="https://twitter.com/richiedewit">Richie de Wit</a> talked about the practical and ideological differences between publishing and self publishing from the perspective of a self published studio. This was a great talk, I recommend watching it for yourself.</p>
<p>The talk defines bizdev as “all activities that increase upside, decrease risk, or stimulate growth of your business or game”.</p>
<p>My main takeaway was that the focus of business development changes in each stage of development. Early on the focus is on decreasing risk, but over time the shift should be to increasing upside at launch, and then to stimulating growth of the game and company post launch.</p>
<h2 id="indie-soapbox">Indie Soapbox</h2>
<figure>
<a href="https://www.gdcvault.com/play/1029188/Independent-Games-Summit-Indie"><img src="/assets/posts/gdc-2023/soapbox.jpg" alt="'let's talk about CONTENT'" /></a>
<figcaption><a href="https://www.gdcvault.com/play/1029188/Independent-Games-Summit-Indie">Recording</a></figcaption>
</figure>
<p>The indie soapbox is always fun, it usually features 4 or so indies and gives them a platform to share recent lessons etc.</p>
<p>My main takeaway was that TikTok is still giving away visiblity to attract people to the platform. I think many indies put too much focus on social media, but as long as you <a href="https://howtomarketagame.com/2021/11/01/dont-build-your-castle-in-other-peoples-kingdoms/">don’t build your castle</a> on their platform, there is potential upside to participating.</p>
<p>I don’t love how parasocial some TikTok marketing feels, but I don’t think it has to be that way.</p>
<h2 id="game-pitch-competition">Game Pitch Competition</h2>
<p>I attended one of the many pitch competitions as an audience member. There’s no recoridng for this one, sorry!</p>
<p>I hadn’t attended a pitch competition before, so this was super interesting to me. I think the judges held back a lot to avoid appearing rude, which was a disappointing but respectable choice. I suspect I generally knew what they were thinking regardless.</p>
<p>My main takeaway was that the key to a good pitch is having a game worth funding, which implies that you know what makes a game worth funding and how to demonstrate that you have one. It was very clear to me who was sort of bluffing and who wasn’t. I don’t think the party you’re pitching to is gonna believe in your project more than you do!</p>
<h2 id="porting-on-a-budget-there-and-back-again">Porting on a Budget: There and Back Again</h2>
<figure>
<a href="https://www.gdcvault.com/play/1029193/Independent-Games-Summit-Porting-on"><img src="/assets/posts/gdc-2023/porting.jpg" alt="porting on a budget: 'there and ack again'" /></a>
<figcaption><a href="https://www.gdcvault.com/play/1029193/Independent-Games-Summit-Porting-on">Recording</a>, <a href="https://www.gdcvault.com/play/1028836/Independent-Games-Summit-Porting-on">Slides</a></figcaption>
</figure>
<p><a href="https://schedule.gdconf.com/speaker/farina-lucas/68095">Lucas Farina</a> shared thoughts on the porting process. He organizes his porting work around a set of milestones: a base reference, an ugly but compatible build, an enhanced build, and a certification candidate.</p>
<hr />
<h1 id="thoughts-on-gdc">Thoughts on GDC</h1>
<p>Despite the high price of admission and silly crypto sponsors, I always get a lot out of GDC. It’s also always the most exhausting week of my entire year, and I’m a fairly active person. That sorta makes sense–if there’s going to be the <em>one big game conference</em>, then everything is gonna happen that week.</p>
<p>I think there’s a bit of a missing middle when it comes to game conferences. You can attend small local meetups, or conferences around narrower parts of game development, or you can attend <em>the big one.</em></p>
<p>I definitely miss Indiecade. I’d love to see some indies put together in-person regional conferences, maybe with a bit more of a business focus than indie events typically have–everything’s not about money, but sustainabe indie dev means more, higher quality indie games.</p>
<p>Maybe that’s something to work on once I figure out the whole sustainabile indie dev problem myself!</p>Mason RemaleyPosting this a little late, but I had a great time at GDC this year! I got to catch up with a lot of people I don’t get to see very often, and meet a lot of new folks. On top of that, a member of GUMBO won the IGF Grand Prize, and the Nuovo award, with his game Betrayal At Club Low. You should play it–Cosmo makes great games, and you don’t have to take my word for it this time what with the whole winning the IGF thing. I’ve written up my personal takeaways from some of the talks I attended below.It’s Not Survivorship Bias (Talk)2022-12-23T00:00:00-08:002022-12-23T00:00:00-08:00https://www.anthropicstudios.com/2022/12/23/survivorship-bias<!--
TODO:
* [ ] check notifications hadnamde discord (also, check chat server??)
* [ ] pin on twitter, update twitter link maybe reddit link
* [ ] follow up with old pinned tweet about game?
* [ ] pin on mastodon
* [ ] follow up with old pinned tweet about game?
* [ ] post in howtomarketagame (not put on spot)
* [ ] mailing list
* [ ] get handmade emails first
* [ ] but maybe post directly instead of linking to blog
* [ ] convert to blog post!
* [ ] explain who handmade community is in text post since for wider audience, adapt as needed
* [ ] share with abner
* [ ] linkedin?
* [ ] share in discord?
* [ ] update old posts to mention mastodon and rss at bottom?
* [ ] rss post next?
* [ ] use kprotty's drawing in the text post
-->
<p>Hey everyone! I recently gave a talk at <a href="https://handmade-seattle.com/">Handmade Seattle</a> titled <em>It’s Not Survivorship Bias - On Successful Software Endeavours</em> on why some software projects find an audience and others don’t, with a focus on independent games.</p>
<p>I’m working on converting this talk into a blog post, in the meantime here are the recordings & slides!
<br /><br /></p>
<iframe src="https://player.vimeo.com/video/783465506?h=6992c02a02&color=ff0179" width="100%" style="aspect-ratio: 16/9;" frameborder="0" allow="autoplay; fullscreen; picture-in-picture" allowfullscreen=""></iframe>
<p><a href="https://guide.handmade-seattle.com/c/2022/its-not-survivorship-bias/">Talk Annotations @ The Handmade Guide</a>
<br /><br /></p>
<iframe src="https://player.vimeo.com/video/783467957?h=38989500df&color=ff0179" width="100%" style="aspect-ratio: 16/9;" frameborder="0" allow="autoplay; fullscreen; picture-in-picture" allowfullscreen=""></iframe>
<p><a href="https://guide.handmade-seattle.com/c/2022/its-not-survivorship-bias-qa/">Q&A Annotations @ The Handmade Guide</a>
<br /><br /></p>
<iframe src="https://docs.google.com/presentation/d/e/2PACX-1vRwMKGJQCRMORqO4sSrx5zFnqHOlYVmK2V7gKTwOKS1ghtFxbGQ6qo3BO67rr-WwI8QMaomH_Axkn5A/embed?start=false&loop=false&delayms=3000" frameborder="0" width="100%" style="aspect-ratio: 16/9;" allowfullscreen="true" mozallowfullscreen="true" webkitallowfullscreen="true"></iframe>
<!--more-->
<hr />
<p>If you enjoyed this post and want to be notified about future posts, you can <a href="/newsletter/signup/tech">sign up for my mailing list</a> or <a href="https://twitter.com/masonremaley">follow me on Twitter</a>, <a href="https://mastodon.gamedev.place/@MasonRemaley">Mastodon</a>, or <a href="/feed.xml">RSS</a>.</p>
<p>Sharing this post on your social platform of choice is always much appreciated.</p>Mason RemaleyHey everyone! I recently gave a talk at Handmade Seattle titled It’s Not Survivorship Bias - On Successful Software Endeavours on why some software projects find an audience and others don’t, with a focus on independent games. I’m working on converting this talk into a blog post, in the meantime here are the recordings & slides! Talk Annotations @ The Handmade Guide Q&A Annotations @ The Handmade GuideMaking Your Game Go Fast by Asking Windows Nicely2022-01-13T00:00:00-08:002022-01-13T00:00:00-08:00https://www.anthropicstudios.com/2022/01/13/asking-windows-nicely<figure>
<a href="/assets/posts/2022-01-13-asking-windows-nicely/speed-limit.jpg"><img src="/assets/posts/2022-01-13-asking-windows-nicely/speed-limit.jpg" alt="a speed limit sign that says '45 fps'" /></a>
<figcaption><a href="https://www.publicdomainpictures.net/en/view-image.php?image=197252&picture=speed-limit-45">(original)</a></figcaption>
</figure>
<p>Normally, to make your software go faster, it has to do less work.</p>
<p>This usually involves improving your algorithms, skipping work the user won’t see, <a href="https://media.handmade-seattle.com/practical-data-oriented-design/">factoring your target hardware into the design process</a>, or modifying your game’s content.</p>
<p>We’re not talking about any of that today. This post is a list of ways to make your game run faster on Windows–without making any major changes to your game’s content, code, or algorithms.</p>
<p>I employ these optimizations in <a href="https://store.steampowered.com/app/1110620/Way_of_Rhea/?utm_campaign=blog&utm_source=website&utm_content=asking-nicely">Way of Rhea</a>, a puzzle adventure written in a mix of <a href="https://www.rust-lang.org/">Rust</a> and a custom Rust scripting language. While this project is written in Rust, all the optimizations listed here are language independent–where the translation isn’t straightforward I’ve provided both Rust and C++ examples.</p>
<!--more-->
<h1 id="table-of-contents">Table of Contents</h1>
<ul id="markdown-toc">
<li><a href="#table-of-contents" id="markdown-toc-table-of-contents">Table of Contents</a></li>
<li><a href="#trick-1-asking-windows-nicely" id="markdown-toc-trick-1-asking-windows-nicely">Trick #1: Asking Windows Nicely</a></li>
<li><a href="#trick-2-set-your-dpi-correctly" id="markdown-toc-trick-2-set-your-dpi-correctly">Trick #2: Set your DPI Correctly</a> <ul>
<li><a href="#setting-your-dpi-awareness" id="markdown-toc-setting-your-dpi-awareness">Setting your “DPI Awareness”</a> <ul>
<li><a href="#setting-dpi-awareness-programmatically" id="markdown-toc-setting-dpi-awareness-programmatically">Setting DPI Awareness Programmatically</a></li>
<li><a href="#setting-dpi-awareness-with-an-application-manifest" id="markdown-toc-setting-dpi-awareness-with-an-application-manifest">Setting DPI Awareness with an Application Manifest</a></li>
</ul>
</li>
<li><a href="#getting-the-scale-factor" id="markdown-toc-getting-the-scale-factor">Getting the Scale Factor</a></li>
<li><a href="#changing-the-resolution" id="markdown-toc-changing-the-resolution">Changing the Resolution</a></li>
</ul>
</li>
<li><a href="#trick-3-use-the-faster-gpu" id="markdown-toc-trick-3-use-the-faster-gpu">Trick #3: Use the Faster GPU</a> <ul>
<li><a href="#hinting-you-want-the-faster-gpu" id="markdown-toc-hinting-you-want-the-faster-gpu">Hinting You Want the Faster GPU</a></li>
<li><a href="#update-linker-flags-in-rust-without-makefiles" id="markdown-toc-update-linker-flags-in-rust-without-makefiles">Update: Linker Flags in Rust Without Makefiles</a></li>
<li><a href="#checking-that-it-worked" id="markdown-toc-checking-that-it-worked">Checking That It Worked</a></li>
</ul>
</li>
<li><a href="#other-ideas" id="markdown-toc-other-ideas">Other ideas…</a></li>
</ul>
<h1 id="trick-1-asking-windows-nicely">Trick #1: Asking Windows Nicely</h1>
<figure>
<a href="/assets/posts/2022-01-13-asking-windows-nicely/power-cable-cropped.jpg"><img src="/assets/posts/2022-01-13-asking-windows-nicely/power-cable-cropped.jpg" alt="an unplugged laptop" /></a>
</figure>
<table><tr><td>
<p><b>2024 Update:</b></p>
<p><b>I no longer recommend this workaround.</b></p>
<p>I've left this section up as a curiosity, but recent version of Windows seem to have resolved the issue—Windows 11 appears to auto detect that I'm in a game and switch to a high performance mode, and AFAICT while Windows 10 doesn't do this it appears that whatever bad heuristic caused the throttling while waiting for vsync has been fixed (or at least is the problem is now rare enough that I can't recreate it anymore.)</p>
<p>This was an iffy fix to begin with, the setting is OS-wide, so it's not something you want to change without asking the user.</p>
</td></tr></table>
<p>One day, while working on some optimizations, I discovered that completely disabling one of Way of Rhea’s subsystems made the game <em>slower</em>. That…didn’t add up. Doing less work should take less time, so I did some investigating.</p>
<p>My initial assumption was that some weird dependency on that subsystem resulted in <em>more</em> work being done when it was absent, but that turned out not to be the case.</p>
<p>When running at 60hz, and especially with a major subsystem disabled, Way of Rhea easily spends the majority of the 16.66ms frame blocking–waiting for the next <a href="https://en.wikipedia.org/wiki/Vertical_blanking_interval">vblank</a>–and apparently this lead Windows to believe that the workload was low priority and should be throttled, increasing both the frame time and frame time variability, causing me to start occasionally <em>missing</em> vblank which is how I noticed the problem in the first place.</p>
<p>You can ask Windows nicely not to do this by linking with <code class="language-plaintext highlighter-rouge">PowrProf.dll</code>, and then calling <a href="https://docs.microsoft.com/en-us/windows/win32/api/powersetting/nf-powersetting-powersetactivescheme">this function</a> from <code class="language-plaintext highlighter-rouge">powersetting.h</code> as follows:</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="p">(</span><span class="n">PowerSetActiveScheme</span><span class="p">(</span><span class="nb">NULL</span><span class="p">,</span> <span class="o">&</span><span class="n">GUID_MIN_POWER_SAVINGS</span><span class="p">)</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
<span class="n">LOG_WARN</span><span class="p">(</span><span class="s">"PowerSetActiveScheme failed"</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>This solved my odd performance problem, and had a positive impact on my game’s performance when running on battery power.</p>
<table><tr><td>
<p><b>Aside: How much of a difference should I expect?</b></p>
<p>I added a debug option to Way of Rhea that toggles between power schemes at runtime, and tested the result on two laptops, both plugged in and on battery power.</p>
<p>The actual measured frame times vary too much to make a readable table–they depend on how much and when Windows chooses to throttle you–but here are my key takeaways:</p>
<ul>
<li>I've repeatedly seen replacing the default scheme by calling this API instantly turn 20ms frames into 7ms frames.
<ul><li>You won't see this unless you're currently throttled, though. Throttling occurs most often but not solely when you're using a laptop running on battery power.</li></ul>
</li>
<li>
<code>GUID_MAX_POWER_SAVINGS</code> is the slowest mode, <code>GUID_MIN_POWER_SAVINGS</code> is the fastest.
<ul>
<li><code>GUID_MIN_POWER_SAVINGS</code> and <code>GUID_TYPICAL_POWER_SAVINGS</code> often behave similarly, but not always. You would expect typical to be the default, but this does not seem to be the case (?).</li>
</ul>
</li>
<li>While this API helps, it does not completely turn off throttling.</li>
</ul>
</td></tr></table>
<h1 id="trick-2-set-your-dpi-correctly">Trick #2: Set your DPI Correctly</h1>
<figure>
<a href="/assets/posts/2022-01-13-asking-windows-nicely/pixelated.png"><img src="/assets/posts/2022-01-13-asking-windows-nicely/pixelated.png" alt="a screenshot pixelated beyond recognition" /></a>
</figure>
<p>Your players probably play other games, right? So they probably have a bunch of fancy hardware, like a 4k monitor that they wanna play your game on?</p>
<p>Well, to prevent scaling issues in legacy apps, on high DPI monitors Windows renders all applications at a lower resolution and then upscales them, unless the application explicitly opts out by becoming “DPI aware.”</p>
<p>Not only does this mean your players won’t get to take advantage of their nice monitors, <strong>it also means you have less headroom before vblank because you have to go through <a href="https://raphlinus.github.io/ui/graphics/2020/09/13/compositor-is-evil.html">the compositor</a> for upscaling</strong>, which likely also rules out <a href="https://www.anthropicstudios.com/2021/02/20/fullscreen-exclusive-is-a-lie/">fullscreen exclusivity</a>.</p>
<table>
<tbody>
<tr>
<td><strong>Aside: Measuring Perf</strong><br /><br />If you want to measure the perf cost of this at home, you need to measure headroom–not throughput, not frame time. The upres happens outside of your code so it won’t affect your frame time, and vsync needs to be off to measure throughput which means you may draw extra frames that never make it to the compositor and therefore don’t need to be upresed, biasing your measurement.<br /><br />Your headroom is equivalent to the amount of time you can busy wait before you miss vblank. On a 60hz monitor you’d expect nearly 16.66ms of headroom, but I found that when my DPI was not set correctly, it was closer to 14ms. That’s ~2.66ms less you have to work with before your game stutters. The heuristics for fullscreen exclusivity and the details of the compositor appear to change over time, and none of this is well documented, so YMMV.<br /><br /><em>Please note that it may be harder than you think to check if you’re missing vblank.</em> Vsync on Windows recently changed–in the past when you missed vblank with vsync enabled, you’d end up with a multiple of your target framerate. e.g. 60hz would become 30hz. As of a Windows update a year or two ago, your frame time doesn’t double in this scenario, but you don’t tear either–you just get stuttering instead. Don’t believe me? Try it. If you have any information why this might have changed, <a href="mailto:mason@anthropicstudios.com">I’d love to hear from you</a>.</td>
</tr>
</tbody>
</table>
<h2 id="setting-your-dpi-awareness">Setting your “DPI Awareness”</h2>
<p>Every couple versions of Windows, Microsoft introduces an entirely new way of handling DPI to fix issues that they couldn’t possibly have anticipated in previous versions, like users with multiple monitors.</p>
<p>We can either become DPI aware programmatically, or through an “Application Manifest”. I’ll demonstrate both methods here, and leave the choice up to you.</p>
<p><em>For those of you who support Linux via <a href="https://github.com/ValveSoftware/Proton/">Proton</a>, either option is fine–apps run through proton appear to be automatically DPI aware even if you do nothing.</em></p>
<h3 id="setting-dpi-awareness-programmatically">Setting DPI Awareness Programmatically</h3>
<p>From <a href="https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setprocessdpiawarenesscontext">Microsoft</a>, emphasis mine:</p>
<blockquote>
<p>It is recommended that you set the process-default DPI awareness via application manifest. See Setting the default DPI awareness for a process for more information. <strong>Setting the process-default DPI awareness via API call can lead to unexpected application behavior.</strong></p>
</blockquote>
<p>This is probably bullshit. I’ve never seen this API cause a problem, and moreover, if using the DPI API can cause “unexpected application behavior” then why the fuck is there a DPI API in the first place? They didn’t have to write this API, and they sure as hell didn’t have to document it.</p>
<p><a href="https://github.com/glfw/glfw/blob/df8d7bc892937a8b0f7c604c92a9f64f383cf48c/src/win32_init.c#L629">GLFW uses the forbidden APIs</a>, <a href="https://github.com/SFML/SFML/blob/c36a7821eeb87c47a3413a953c7a6baf74ee48f2/src/SFML/Window/Win32/WindowImplWin32.cpp#L86">SFML uses the forbidden APIs</a>, and <a href="https://github.com/libsdl-org/SDL/issues/2119">SDL probably will soon too</a>.</p>
<p>So it’s probably fine. <a href="/assets/posts/2022-01-13-asking-windows-nicely/garfield.png">If you’re feeling brave</a>, call this function before anything that depends on the DPI to opt yourself into DPI awareness programmatically:</p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">SetProcessDpiAwarenessContext</span><span class="p">(</span><span class="n">DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2</span><span class="p">))</span> <span class="p">{</span>
<span class="n">LOG_WARN</span><span class="p">(</span><span class="s">"SetProcessDpiAwarenessContext failed"</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<p><em>This function is part of <code class="language-plaintext highlighter-rouge">User32.lib</code>, and is defined in <code class="language-plaintext highlighter-rouge">winuser.h</code> which is included in <code class="language-plaintext highlighter-rouge">Windows.h</code>.</em></p>
<p>If your monitor is set to anything other than 100% scaling in Windows Settings, you should be able to see the difference visually, and any APIs that output measurements in pixels should now output actual pixels instead of scaled pixels.</p>
<table>
<tbody>
<tr>
<td><strong>Aside: Compatability</strong><br /><br />As of April 5th 2017 with the release of Windows 10 Version 1703, <a href="https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setprocessdpiawarenesscontext"><code class="language-plaintext highlighter-rouge">SetProcessDpiAwarenessContext</code></a> used above is the replacement for <a href="https://docs.microsoft.com/en-us/windows/win32/api/shellscalingapi/nf-shellscalingapi-setprocessdpiawareness"><code class="language-plaintext highlighter-rouge">SetProcessDpiAwareness</code></a>, which in turn was a replacement for <a href="https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setprocessdpiaware"><code class="language-plaintext highlighter-rouge">SetProcessDPIAware</code></a>. Love the clear naming scheme.<br /><br />You might be tempted to call one of the older versions of this function to remain compatible with older systems–I don’t recommend doing that. <a href="https://docs.microsoft.com/en-us/windows/win32/api/shellscalingapi/nf-shellscalingapi-setprocessdpiawareness"><code class="language-plaintext highlighter-rouge">SetProcessDpiAwareness</code></a> requires you do additional work on top of calling this function, to keep the titlebar in sync in Windowed mode, and <a href="https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setprocessdpiaware"><code class="language-plaintext highlighter-rouge">SetProcessDPIAware</code></a> doesn’t properly support multiple monitors. Besides, as of December 2021, <a href="https://store.steampowered.com/hwsurvey">only 4.16% of Windows users surveyed by Steam haven’t updated to Windows 10 or Windows 11</a>, and of those users nearly 100% should be well past Windows 10 Version 1703 since Windows Update no longer asks for consent before messing with your system.<br /><br />If you need to be backwards compatible you can load this API dynamically and skip it if it doesn’t exist, or just go the manifest route.</td>
</tr>
</tbody>
</table>
<h3 id="setting-dpi-awareness-with-an-application-manifest">Setting DPI Awareness with an Application Manifest</h3>
<p><a href="https://docs.microsoft.com/en-us/windows/win32/sbscs/application-manifests">“Application Manifests”</a> are <a href="https://apenwarr.ca/log/20180914">xml</a> files that define options for your application. They are usually compiled and linked into your executable. Calling C functions to configure your app was too easy, I guess?</p>
<p>If you’re using Visual Studio then you probably already have one of these.</p>
<p>For the rest of us, it turns out that the compilation and linking step is optional, which is great, because it means we don’t need to use Microsoft’s compiler or linker to include a manifest. <a href="https://docs.microsoft.com/en-us/cpp/build/manifest-generation-in-visual-studio?view=msvc-170">From Microsoft</a>:</p>
<blockquote>
<p>The build system in Visual Studio allows the manifest to be embedded in the final binary application file, or generated as an external file.</p>
</blockquote>
<p>Microsoft <em><a href="https://docs.microsoft.com/en-us/cpp/build/how-to-embed-a-manifest-inside-a-c-cpp-application?view=msvc-170">recommends</a></em> you embed your manifest files in the executable, but this is presumably so users don’t move the exe without bringing the manifest along for the ride, or mess with the contents itself. Most games already have external data folders with the same problem so this isn’t an issue for us.</p>
<p>If your game is located at <code class="language-plaintext highlighter-rouge">foo/bar/game.exe</code>, then you just need to create <a href="https://docs.microsoft.com/en-us/windows/win32/sbscs/application-manifests#file-location"><code class="language-plaintext highlighter-rouge">foo/bar/game.exe.manifest</code></a> with <a href="https://docs.microsoft.com/en-us/windows/win32/hidpi/setting-the-default-dpi-awareness-for-a-process#setting-default-awareness-with-the-application-manifest">the following content</a>.</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp"><?xml version="1.0" encoding="UTF-8" standalone="yes"?></span>
<span class="nt"><assembly</span> <span class="na">xmlns=</span><span class="s">"urn:schemas-microsoft-com:asm.v1"</span> <span class="na">manifestVersion=</span><span class="s">"1.0"</span> <span class="na">xmlns:asmv3=</span><span class="s">"urn:schemas-microsoft-com:asm.v3"</span><span class="nt">></span>
<span class="nt"><asmv3:application></span>
<span class="nt"><asmv3:windowsSettings></span>
<span class="nt"><dpiAware</span> <span class="na">xmlns=</span><span class="s">"http://schemas.microsoft.com/SMI/2005/WindowsSettings"</span><span class="nt">></span>true<span class="nt"></dpiAware></span>
<span class="nt"><dpiAwareness</span> <span class="na">xmlns=</span><span class="s">"http://schemas.microsoft.com/SMI/2016/WindowsSettings"</span><span class="nt">></span>PerMonitorV2<span class="nt"></dpiAwareness></span>
<span class="nt"></asmv3:windowsSettings></span>
<span class="nt"></asmv3:application></span>
<span class="nt"></assembly></span>
</code></pre></div></div>
<p>This manifest will enable <code class="language-plaintext highlighter-rouge">DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2</code> where supported, falling back first to <code class="language-plaintext highlighter-rouge">PROCESS_SYSTEM_DPI_AWARE</code>, and then to <code class="language-plaintext highlighter-rouge">PROCESS_DPI_UNAWARE</code>. <code class="language-plaintext highlighter-rouge">PROCESS_PER_MONITOR_DPI_AWARE</code> is intentionally left off since it requires additional code changes to render the titlebar correctly.</p>
<p>Just like with the programtic route: If your monitor is set to anything other than 100% scaling in Windows Settings, you should be able to see the difference visually, and any APIs that output measurements in pixels should now output actual pixels instead of scaled pixels.</p>
<h2 id="getting-the-scale-factor">Getting the Scale Factor</h2>
<p>This isn’t often relevant for games, but, if you need to check how much things <em>would</em> have been scaled if you weren’t DPI aware, you can call <a href="https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getdpiforwindow"><code class="language-plaintext highlighter-rouge">GetDpiForWindow</code></a> and <a href="https://docs.microsoft.com/en-us/windows/win32/hidpi/wm-dpichanged#remarks">divide the result by 96</a>.</p>
<h2 id="changing-the-resolution">Changing the Resolution</h2>
<p>If you followed these steps, but your game is running <em>slower</em> now, I didn’t lie to you–you’re just rendering more pixels than you were before. That’s a good thing, you want the option to support the user’s native resolution right?</p>
<p>You probably want the option to render at a lower (or higher!) resolution too.</p>
<p>This should be done by rendering to a framebuffer that the user can resize from the options menu of your game. I don’t recommend messing with the user’s desktop resolution, I’m willing to believe that was worthwhile in the past but in the admittedly informal tests that I’ve done on Windows 10, I’ve seen no benefit and it’s a bad experience for your players. The framebuffer method is also more flexible–you have the option to render your UI at a higher resolution than everything else.</p>
<p>I provide the player with an option called “resolution” that can be set anywhere from 20% to 200%. I think other games call this “render scale”, I guess I could rename it. The framebuffer is set to the given percent of the game window’s current size. AFAICT there’s not advantage to a preset list of supported resolutions unless you’re messing with the monitor itself. Careful with aspect ratios though.</p>
<h1 id="trick-3-use-the-faster-gpu">Trick #3: Use the Faster GPU</h1>
<figure>
<a href="/assets/posts/2022-01-13-asking-windows-nicely/graphics-card-vs.jpg"><img src="/assets/posts/2022-01-13-asking-windows-nicely/graphics-card-vs.jpg" alt="A TI80 next to an intel CPU that has an integrated GPU with versus written between them" /></a>
</figure>
<p>Here’s the scene. Someone buys your game. They bought a laptop with a fancy 3080 or something (because scalpers bought all the standalone cards so <a href="https://www.pcmag.com/news/how-to-beat-the-scalpers-and-score-an-rtx-3000-card-ryzen-5000-chip">how else are you gonna get one</a>), and they wanna run your game on it. <strong>When they boot it up, it launches on their integrated Intel GPU instead of their 3080 and runs super slow.</strong> They don’t know that’s what happened though. To them, your game is just so slow that it can’t even run on their 3080.</p>
<p>Alright that’s no good. If we’re running on a laptop and we don’t explicitly request the faster card, we’ll often end up running on the slower integrated GPU instead. How do we make that request? Well, if you’re running a new graphics API, you may be able to enumerate the available graphics cards and pick the fastest one (or let the user pick it). I haven’t updated to the newer APIs yet, but if you’re on one of them and that’s correct, awesome–problem solved.</p>
<p>But what about all of us still on OpenGL?</p>
<h2 id="hinting-you-want-the-faster-gpu">Hinting You Want the Faster GPU</h2>
<p>You’re gonna have to hint to each graphics card vendor that you want the fast card. You can find the documentation for <a href="https://docs.nvidia.com/gameworks/content/technologies/desktop/optimus.htm">Nvidia here</a>. At one point I also managed to track down AMD’s documentation on this, but I can’t seem to find it anymore.</p>
<p>In case your idea of a fun afternoon doesn’t involve piecing through the Nvidia docs to figure this out on your own, and then hunting down wherever AMD hid their explanation, I’ve written up what I learned from reading the docs and implementing their recommendations myself.</p>
<table>
<tbody>
<tr>
<td><strong>Aside: Testing on AMD GPUs</strong><br /><br />I’ve tested this code with Nvidia cards to make sure it has the desired effect. There seems to be little room for error, especially considering that I’ve been able to verify that AAA games often use the same method as I’ll show later, but unfortunately I don’t have the hardware to personally verify that this works on AMD.<br /><br />If you have a computer with both an integrated GPU and a discrete AMD GPU and wanna see if this works <a href="mailto:mason@anthropicstudios.com">I’d much appreciate that</a>, my demo is <a href="https://store.steampowered.com/app/1110620/Way_of_Rhea/?utm_campaign=blog&utm_source=website&utm_content=asking-nicely">here</a>, it should always run on the discrete GPU when available (you can see which GPU it’s using by enabling basic stats in the debug menu.) Keep in mind that Bootcamp doesn’t support GPU switching, so unfortunately this test won’t provide meaningful results on dual booted Macbooks.</td>
</tr>
</tbody>
</table>
<p>The NVIDIA docs provide two methods of preferring the NVIDIA card over integrated cards:</p>
<ul>
<li>Linking with one of a long list of NVIDIA libraries</li>
<li>Exporting a special symbol</li>
</ul>
<p>The former did not work in my tests, so I’m only demonstrating the latter (+ the AMD equivalent.) First, you need to add this code somewhere in your project:</p>
<p><em>(rust version)</em></p>
<div class="language-rs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">#[no_mangle]</span>
<span class="k">pub</span> <span class="k">static</span> <span class="n">NvOptimusEnablement</span><span class="p">:</span> <span class="nb">i32</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="nd">#[no_mangle]</span>
<span class="k">pub</span> <span class="k">static</span> <span class="n">AmdPowerXpressRequestHighPerformance</span><span class="p">:</span> <span class="nb">i32</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
</code></pre></div></div>
<p><em>(c++ version)</em></p>
<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">extern</span> <span class="s">"C"</span> <span class="p">{</span>
<span class="n">_declspec</span><span class="p">(</span><span class="n">dllexport</span><span class="p">)</span> <span class="n">DWORD</span> <span class="n">NvOptimusEnablement</span> <span class="o">=</span> <span class="mh">0x00000001</span><span class="p">;</span>
<span class="n">_declspec</span><span class="p">(</span><span class="n">dllexport</span><span class="p">)</span> <span class="n">DWORD</span> <span class="n">AmdPowerXpressRequestHighPerformance</span> <span class="o">=</span> <span class="mh">0x00000001</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Next, when you compile, <strong>make sure your compiler exports these variables</strong>.</p>
<p>In some languages this will happen automatically if the proper keywords are provided. In Rust, you need to set the following linker flags to get this to happen. <a href="#update-linker-flags-in-rust-without-makefiles"><del>(I’m unfortunately not aware of any way to set this from <code class="language-plaintext highlighter-rouge">Cargo.toml</code>, I ended up creating a Makefile just for this purpose.)</del></a></p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cargo.exe rustc <span class="nt">--release</span> <span class="nt">--</span> <span class="nt">-C</span> link-args<span class="o">=</span><span class="s2">"/EXPORT:NvOptimusEnablement /EXPORT:AmdPowerXpressRequestHighPerformance"</span>
</code></pre></div></div>
<p>If you’ve done this, then on computers that have an integrated card and a discrete Nvidia or AMD card, the discrete card should be used by default.</p>
<h2 id="update-linker-flags-in-rust-without-makefiles">Update: Linker Flags in Rust Without Makefiles</h2>
<p>Turns out it’s possible to do this with a <code class="language-plaintext highlighter-rouge">build.rs</code> script!</p>
<p>Thanks to <a href="https://twitter.com/asajeffrey">@asajeffrey</a> for helping me figure out the correct syntax–a few people suggested this route, but the syntax was harder to get right than you’d think seeing the result! Instead of creating a Makefile, you can create a <a href="https://doc.rust-lang.org/cargo/reference/build-scripts.html"><code class="language-plaintext highlighter-rouge">build.rs</code></a> script with the following content:</p>
<div class="language-rs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
<span class="nd">println!</span><span class="p">(</span><span class="s">"cargo:rustc-link-arg=/EXPORT:NvOptimusEnablement"</span><span class="p">);</span>
<span class="nd">println!</span><span class="p">(</span><span class="s">"cargo:rustc-link-arg=/EXPORT:AmdPowerXpressRequestHighPerformance"</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<h2 id="checking-that-it-worked">Checking That It Worked</h2>
<p>How do we know it worked? Your game will, presumably, render faster, but we should also verify that we’re having the intended effect directly.</p>
<p>First, lets check that we successfully exported the symbols. Start a “Developer Command Prompt” (this requires installing Visual Studio), and then enter the following:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>dumpbin /exports <span class="nv">$YOUR_GAME</span>.exe
</code></pre></div></div>
<p>Our two exported variables, as well as anything else you exported, should show up in the output.</p>
<p>You can also use this to check if AAA games use this method; in my experience they do.</p>
<p>Next, assuming you have hardware to test with, we can check that the exports are being respected by using <a href="https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glGetString.xhtml"><code class="language-plaintext highlighter-rouge">glGetString</code></a> with the following constants:</p>
<ul>
<li><code class="language-plaintext highlighter-rouge">GL_VENDOR</code></li>
<li><code class="language-plaintext highlighter-rouge">GL_RENDERER</code></li>
<li><code class="language-plaintext highlighter-rouge">GL_VERSION</code></li>
<li><code class="language-plaintext highlighter-rouge">GL_SHADING_LANGUAGE_VERSION</code></li>
</ul>
<p>Without this change I get the following:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>vendor: Intel
renderer: Intel<span class="o">(</span>R<span class="o">)</span> UHD Graphics 630
version: 4.1.0 - Build 26.20.100.7261
shading_language_version: 4.10 - Build 26.20.100.7261
</code></pre></div></div>
<p>With the change I get this:</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>vendor: NVIDIA Corporation
renderer: NVIDIA GeForce RTX 2060/PCIe/SSE2
version: 4.1.0 NVIDIA 471.68
shading_language_version: 4.10 NVIDIA via Cg compiler
</code></pre></div></div>
<h1 id="other-ideas">Other ideas…</h1>
<figure>
<a href="/assets/posts/2022-01-13-asking-windows-nicely/profiler.png"><img src="/assets/posts/2022-01-13-asking-windows-nicely/profiler.png" alt="Way of Rhea's in-game profiler" /></a>
</figure>
<p>Windows is complicated, graphics cards are complicated. There are probably other flags and stuff I don’t know about. Feel free to <a href="mailto:mason@anthropicstudios.com">email</a> or <a href="https://twitter.com/masonremaley">Tweet</a> at me if I missed anything!</p>
<p>Here are a couple of other things you could look into:</p>
<ul>
<li>You could <a href="https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-setpriorityclass">set your process’ priority class</a>. This is, I believe, equivalent to changing the priority in Task Manager. I saw no benefit when I tried this, so I left it alone.</li>
<li>You could <a href="https://www.anthropicstudios.com/2021/02/20/fullscreen-exclusive-is-a-lie/">attempt to support fullscreen exclusivity</a>.</li>
<li>You could ask NVIDIA nicely to go fast. <a href="https://docs.nvidia.com/gameworks/content/gameworkslibrary/coresdk/nvapi/group__gpupstate.html">This documentation</a> claims to allow you to get and set the current performance state, but in the list of functions I only see getters, so I’m not sure if I’m understanding correctly. If you know how <a href="mailto:mason@anthropicstudios.com">email</a> or <a href="https://twitter.com/masonremaley">tweet at</a> me, I’d love to hear from you.</li>
<li>You could become big enough that <a href="https://www.nvidia.com/download/driverResults.aspx/159420/en-us">Nvidia ships drivers specifically optimized for your game</a>, and try not to think about the long term effects of that dynamic.</li>
</ul>
<hr />
<p>If you enjoyed this post and want to be notified about future posts, you can <a href="/newsletter/signup/tech">sign up for my mailing list</a> or <a href="https://twitter.com/masonremaley">follow me on Twitter</a>.</p>
<p>If this article helped you quickly shave a few milliseconds off your frame time, you have my permission to spend the rest of the work day playing <a href="https://store.steampowered.com/app/1110620/Way_of_Rhea/?utm_campaign=blog&utm_source=website&utm_content=asking-nicely">Way of Rhea’s demo</a> with full confidence that Windows will not unnecessarily throttle your experience.</p>
<p>Sharing this article your social platform of choice is always much appreciated.</p>Mason Remaley(original) Normally, to make your software go faster, it has to do less work. This usually involves improving your algorithms, skipping work the user won’t see, factoring your target hardware into the design process, or modifying your game’s content. We’re not talking about any of that today. This post is a list of ways to make your game run faster on Windows–without making any major changes to your game’s content, code, or algorithms. I employ these optimizations in Way of Rhea, a puzzle adventure written in a mix of Rust and a custom Rust scripting language. While this project is written in Rust, all the optimizations listed here are language independent–where the translation isn’t straightforward I’ve provided both Rust and C++ examples.