<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: João Koritar</title>
    <description>The latest articles on DEV Community by João Koritar (@jakoritarleite).</description>
    <link>https://dev.to/jakoritarleite</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F676532%2F3de78efa-ac87-4fb6-b43f-840fc23b2a51.jpeg</url>
      <title>DEV Community: João Koritar</title>
      <link>https://dev.to/jakoritarleite</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jakoritarleite"/>
    <language>en</language>
    <item>
      <title>Writing a game engine in Rust and Vulkan</title>
      <dc:creator>João Koritar</dc:creator>
      <pubDate>Tue, 26 Sep 2023 19:15:38 +0000</pubDate>
      <link>https://dev.to/jakoritarleite/writing-a-game-engine-in-rust-and-vulkan-4243</link>
      <guid>https://dev.to/jakoritarleite/writing-a-game-engine-in-rust-and-vulkan-4243</guid>
      <description>&lt;p&gt;Since I'm learning about game engine development, I'll take notes and document the things I learn and my development process.&lt;/p&gt;

&lt;p&gt;At the time of writing this post, I have a very basic 3D rendering engine with a custom ECS (Entity Component System) architecture. I can draw simple geometries with custom colors, move the camera around, as well as rotate it with the mouse, as shown in this GIF:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3vz8t2CG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4kzk0m8hxm1wqw1bzbo5.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3vz8t2CG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4kzk0m8hxm1wqw1bzbo5.gif" alt="Running the engine using cargo" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Doing premature optimization
&lt;/h2&gt;

&lt;p&gt;I spent last weekend working on improving my ECS, and I'll show my process for doing it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Benchmark
&lt;/h3&gt;

&lt;p&gt;The first step was to benchmark the library's spawn and query performance, and the results were neither decent nor satisfactory:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Spawning&lt;/strong&gt; 1 million entities (very basic entities with 1-2 components each) took 5 seconds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Querying&lt;/strong&gt; those entities took almost 3 seconds.&lt;/p&gt;

&lt;p&gt;The code for spawning looks something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[derive(Component)]&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nf"&gt;Position&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;u128&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;u128&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nd"&gt;#[derive(Component)]&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nf"&gt;Velocity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;u128&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;world&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;World&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;..=&lt;/span&gt;&lt;span class="mi"&gt;1_000_000&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;world&lt;/span&gt;&lt;span class="nf"&gt;.spawn&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nf"&gt;Position&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;Velocity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
        &lt;span class="n"&gt;world&lt;/span&gt;&lt;span class="nf"&gt;.spawn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Velocity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;world&lt;/span&gt;&lt;span class="py"&gt;.query&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Position&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Velocity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="nf"&gt;.iter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.for_each&lt;/span&gt;&lt;span class="p"&gt;(|(&lt;/span&gt;&lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;velocity&lt;/span&gt;&lt;span class="p"&gt;)|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;assert_eq!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="na"&gt;.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="na"&gt;.1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nd"&gt;assert_eq!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="na"&gt;.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;velocity&lt;/span&gt;&lt;span class="na"&gt;.0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Profiling
&lt;/h3&gt;

&lt;p&gt;And well, after seeing those horrendous benchmark results, I used &lt;a href="https://github.com/brendangregg/FlameGraph"&gt;flamegraph&lt;/a&gt; to determine what was consuming the most CPU time.&lt;/p&gt;

&lt;h4&gt;
  
  
  Spawning
&lt;/h4&gt;

&lt;p&gt;The first thing I noticed was that the Entity creation had the largest share of the result, as you can see below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1peOalL0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q0r7aqbq0psk0xly5yov.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1peOalL0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q0r7aqbq0psk0xly5yov.png" alt="Stack trace visualization with an arrow pointing to the Entity::new method" width="800" height="328"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The problem was that I was passing a &lt;a href="https://doc.rust-lang.org/std/vec/index.html"&gt;vector&lt;/a&gt; of tuples to the &lt;code&gt;Entity&lt;/code&gt; constructor and using the &lt;a href="https://docs.rs/dashmap/5.5.3/dashmap/struct.DashMap.html#impl-FromParallelIterator%3C(K,+V)%3E-for-DashMap%3CK,+V,+S%3E"&gt;&lt;code&gt;DashMap::from_iter_par&lt;/code&gt;&lt;/a&gt; method, which creates the map from a parallel iterator. However, for some reason, it was taking a lot of time to do it.&lt;/p&gt;

&lt;p&gt;The solution I've come to is that instead of creating the map from the vector and storing it, I stored the vector itself. It became significantly faster and didn't affect query performance.&lt;/p&gt;

&lt;h4&gt;
  
  
  Querying
&lt;/h4&gt;

&lt;p&gt;The query performance wasn't too bad; indeed, it was okay since I was doing it single-threaded. Anyway, I did some refactoring to be able to use the parallel iterator from &lt;a href="https://docs.rs/rayon/latest/rayon/index.html"&gt;rayon&lt;/a&gt;, and it improved a bit, though not drastically. Then, I noticed that &lt;code&gt;DashMap&lt;/code&gt; was using Rust's default hasher, which isn't quite as speedy as I wanted. So, I changed it to use &lt;a href="https://docs.rs/rustc-hash/latest/rustc_hash/struct.FxHasher.html"&gt;FxHasher&lt;/a&gt;, which is much faster than the default one. With this change, the query operation now takes 0.4 seconds, as shown in the following image:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2F8Czzyp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/feipghu8afh71pslxl5b.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2F8Czzyp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/feipghu8afh71pslxl5b.jpg" alt="Image showing time elapsed for query" width="242" height="84"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can ignore the Positions and Velocity logs since I was using them just for debugging purposes.&lt;/p&gt;

&lt;p&gt;Well, this was my process of refactoring and optimizing some parts of my ECS library. I hope you liked it. See you in my next post.&lt;/p&gt;

&lt;p&gt;GitHub repository: &lt;a href="https://github.com/jakoritarleite/woody"&gt;woody&lt;/a&gt;&lt;/p&gt;

</description>
      <category>rust</category>
      <category>vulkan</category>
      <category>gamedev</category>
      <category>ecs</category>
    </item>
  </channel>
</rss>
