<?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>Forem: Javier Salcedo</title>
    <description>The latest articles on Forem by Javier Salcedo (@javiersalcedopuyo).</description>
    <link>https://forem.com/javiersalcedopuyo</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%2F381316%2F93107b01-8369-40e9-9bc4-04c0a354cbee.jpeg</url>
      <title>Forem: Javier Salcedo</title>
      <link>https://forem.com/javiersalcedopuyo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/javiersalcedopuyo"/>
    <language>en</language>
    <item>
      <title>Writing a "Hello, Triangle!" with Metal 4 and exclusively C++</title>
      <dc:creator>Javier Salcedo</dc:creator>
      <pubDate>Mon, 09 Feb 2026 22:39:25 +0000</pubDate>
      <link>https://forem.com/javiersalcedopuyo/writing-a-hello-triangle-with-metal-4-and-exclusively-c-3kgm</link>
      <guid>https://forem.com/javiersalcedopuyo/writing-a-hello-triangle-with-metal-4-and-exclusively-c-3kgm</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This article was initially published &lt;a href="https://javiersalcedopuyo.xyz/blog/2026/metal_4_cpp_hello_triangle.html" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This article will serve as both a tutorial for those who don't want to have to deal with ObjC, and as a demonstration of how I work with my own custom C++ build system "Dedalo".&lt;/p&gt;

&lt;h2&gt;
  
  
  The build system
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/javiersalcedopuyo/dedalo" rel="noopener noreferrer"&gt;Dedalo&lt;/a&gt; is a strongly opinionated C++ build system, inspired by Swift and Zig.&lt;br&gt;
The goal is to have a build system that doesn't require anything other than a C++ compiler and C++ code to work, no need to install anything, nor to learn a different bespoke language.&lt;/p&gt;

&lt;p&gt;You &lt;em&gt;can&lt;/em&gt; install Dedalo by copying the binary and cpp file in the appropriate paths (or using &lt;code&gt;make install&lt;/code&gt;), but you can also distribute it with your program, so you don't need to worry about versioning. It's just a single cpp file with no dependencies other than the STL. The only thing needed to build a C++ project with Dedalo is a C++ compiler that supports C++20.&lt;/p&gt;
&lt;h3&gt;
  
  
  Getting started
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Create a new directory and place &lt;code&gt;dedalo.cpp&lt;/code&gt; in there.&lt;/li&gt;
&lt;li&gt;Compile it like this &lt;code&gt;clang++ -std=c++20 -O3 dedalo.cpp -o ddl&lt;/code&gt;

&lt;ol&gt;
&lt;li&gt;If you want logs, add &lt;code&gt;-DENABLE_LOGS&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;Initialise the project like this &lt;code&gt;./ddl init&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You should now have a file structure like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;├── build
│   ├── cache
│   │   └── lto
│   └── obj
├── build.cpp
├── ddl
├── dedalo.cpp
├── lib
└── src
    └── main.cpp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you run it like this &lt;code&gt;./ddl run&lt;/code&gt; you should get a "Hello, World!" message.&lt;/p&gt;




&lt;h2&gt;
  
  
  Sorting out the dependencies
&lt;/h2&gt;

&lt;h3&gt;
  
  
  GLFW
&lt;/h3&gt;

&lt;p&gt;To keep it simple, I'll be using &lt;a href="https://github.com/glfw/glfw" rel="noopener noreferrer"&gt;GLFW&lt;/a&gt; for the window management. Please refer to their website to see how to install it.&lt;br&gt;
Since it's a dynamic system library (in my case in &lt;code&gt;/usr/local/include/GLFW&lt;/code&gt;), adding it to the project is very easy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// build.cpp&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;Project&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;MainArgvSlice&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;project&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;project&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Project&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;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"metal-4-only-cpp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dependencies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"glfw"&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;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Apple Frameworks
&lt;/h3&gt;

&lt;p&gt;The official metal-cpp headers are incomplete, but there're workarounds. Just get &lt;a href="https://github.com/javiersalcedopuyo/metal-cpp-headers" rel="noopener noreferrer"&gt;this repo&lt;/a&gt; and copy it into &lt;code&gt;./lib&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;├── dedalo.cpp
├── ddl
├── build.cpp
├── lib
│   ├── AppKit
│   ├── Foundation
│   ├── Metal
│   ├── MetalFX
│   ├── MetalKit
│   └── QuartzCore
└── src
    └── main.hpp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now just add them to the &lt;code&gt;build.cpp&lt;/code&gt;, and disable the &lt;code&gt;gnu-anonymous-struct&lt;/code&gt; and &lt;code&gt;nested-anon-types&lt;/code&gt; warnings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// build.cpp&lt;/span&gt;
&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;project&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Project&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;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"metal-4-only-cpp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dependencies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"glfw"&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;span class="n"&gt;frameworks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"AppKit"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"Foundation"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"QuartzCore"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"Metal"&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;span class="n"&gt;common_compiler_flags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// metal-cpp needs these&lt;/span&gt;
        &lt;span class="s"&gt;"Wno-gnu-anonymous-struct"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"Wno-nested-anon-types"&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;h2&gt;
  
  
  Hello, Triangle!
&lt;/h2&gt;

&lt;p&gt;From now on, I'll use some custom pieces of code that I won't explain here but you can find in the &lt;code&gt;utils.hpp&lt;/code&gt; file, like a &lt;code&gt;defer&lt;/code&gt; macro or renaming some types.&lt;/p&gt;

&lt;p&gt;For brevity, I'm also going to assume no errors can happen, so I won't check or assert results.&lt;/p&gt;

&lt;p&gt;After initialising the Metal device and creating the window, I'll be following &lt;a href="https://developer.apple.com/documentation/metal/drawing-a-triangle-with-metal-4" rel="noopener noreferrer"&gt;this article&lt;/a&gt; from Apple, but using C++ instead of ObjC.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating the window
&lt;/h3&gt;

&lt;p&gt;This is the same as with any other GLFW app. Just remember to define &lt;code&gt;GFW_EXPOSE_NATIVE_COCOA&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="cp"&gt;#define GLFW_INCLUDE_NONE
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;GLFW/glfw3.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#define GFW_EXPOSE_NATIVE_COCOA
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;GLFW/glfw3native.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;"utils.hpp"&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;constexpr&lt;/span&gt; &lt;span class="n"&gt;u32&lt;/span&gt; &lt;span class="n"&gt;WIN_WIDTH&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;800&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;constexpr&lt;/span&gt; &lt;span class="n"&gt;u32&lt;/span&gt; &lt;span class="n"&gt;WIN_HEIGHT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;i32&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;i32&lt;/span&gt; &lt;span class="n"&gt;argc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;argv&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;span class="n"&gt;glfwInit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="n"&gt;glfwWindowHint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;GLFW_CLIENT_API&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;GLFW_NO_API&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Because GLFW wasn't designed to work with Metal&lt;/span&gt;
    &lt;span class="n"&gt;GLFWwindow&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;glfw_window&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;glfwCreateWindow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;WIN_WIDTH&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;WIN_HEIGHT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Metal Only C++"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;nullptr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;nullptr&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;defer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;glfwDestroyWindow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;glfw_window&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;glfwWindowShouldClose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;glfw_window&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;span class="n"&gt;glfwPollEvents&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="c1"&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;p&gt;If you run the program now you should get an empty window:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fao380v92kggv8ptrdg7u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fao380v92kggv8ptrdg7u.png" alt="Blank Window" width="800" height="652"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Initialising Metal
&lt;/h3&gt;
&lt;h4&gt;
  
  
  Creating the Metal device and layer
&lt;/h4&gt;

&lt;p&gt;We need the device to issue commands, and the layer to connect with the GLFW window.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="cp"&gt;#define MTL_PRIVATE_IMPLEMENTATION
#define NS_PRIVATE_IMPLEMENTATION
#include&lt;/span&gt; &lt;span class="cpf"&gt;"Metal/Metal.hpp"&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="cp"&gt;#define CA_PRIVATE_IMPLEMENTATION
#include&lt;/span&gt; &lt;span class="cpf"&gt;"QuartzCore/QuartzCore.hpp"&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;&lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;constexpr&lt;/span&gt; &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;MAX_FRAMES_IN_FLIGHT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3u&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;constexpr&lt;/span&gt; &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;PIXEL_FORMAT&lt;/span&gt;         &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MTL&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;PixelFormatBGRA8Unorm_sRGB&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="n"&gt;MTL&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Device&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;    &lt;span class="n"&gt;device&lt;/span&gt;      &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MTL&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CreateSystemDefaultDevice&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;CA&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;MetalLayer&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;metal_layer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;CA&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;MetalLayer&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;layer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;metal_layer&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;setDevice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;device&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;metal_layer&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;setPixelFormat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;PIXEL_FORMAT&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;metal_layer&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;setMaximumDrawableCount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;MAX_FRAMES_IN_FLIGHT&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="c1"&gt;// Create the GLFW window&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;auto&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;metal_window&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;reinterpret_cast&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;NS&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Window&lt;/span&gt;&lt;span class="o"&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;glfwGetCocoaWindow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;glfw_window&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;NS&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;View&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;view&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;metal_window&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;contentView&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;setLayer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;metal_layer&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;setWantsLayer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Set up the GLFW window to work with Metal
&lt;/h4&gt;

&lt;p&gt;Now that we have both the window and the Metal device/layer, we can link them together so Metal knows where to present.&lt;br&gt;
To do that, we need to get the view of the GLFW window's underlying Cocoa window:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;auto&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;metal_window&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;reinterpret_cast&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;NS&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Window&lt;/span&gt;&lt;span class="o"&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;glfwGetCocoaWindow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;glfw_window&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;NS&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;View&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;view&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;metal_window&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;contentView&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;setLayer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;metal_layer&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;setWantsLayer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Command Queue, Buffer and Allocators
&lt;/h3&gt;

&lt;p&gt;Metal 4 changes a bit how commands work:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8y04jytfp7xdhl7f8pik.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8y04jytfp7xdhl7f8pik.png" alt="Metal 4 Command Management" width="800" height="621"&gt;&lt;/a&gt;&lt;br&gt;
You can watch &lt;a href="https://developer.apple.com/videos/play/wwdc2025/205/?time=104" rel="noopener noreferrer"&gt;this video&lt;/a&gt; to learn more.&lt;/p&gt;

&lt;p&gt;In Metal 3, command buffers were created from their queues, but in Metal 4 you can create them independently and commit multiple of them at once to a queue later.&lt;br&gt;
They can also be reused, which is why it's a single member variable of the renderer and not a temporary object created per frame like on Metal 3.&lt;/p&gt;

&lt;p&gt;Metal 4 also introduces command allocators, that manage the memory necessary for the encoded commands.&lt;br&gt;
While the buffer can be reused, each frame needs its own allocator, so we'll make an array of 3, 1 for each frame in flight.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// In the Renderer struct/class&lt;/span&gt;
&lt;span class="n"&gt;MTL4&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CommandQueue&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;  &lt;span class="n"&gt;cmd_queue&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;nullptr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;MTL4&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CommandBuffer&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;cmd_buf&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;nullptr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;MTL4&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CommandAllocator&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;MAX_FRAMES_IN_FLIGHT&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;cmd_allocators&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// In the initialisation:&lt;/span&gt;
&lt;span class="n"&gt;cmd_queue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;newMTL4CommandQueue&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;cmd_buf&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;newCommandBuffer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;auto&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;alloc&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;cmd_allocators&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;alloc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;newCommandAllocator&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;
  
  
  Frame Synchronisation and Presentation
&lt;/h3&gt;

&lt;p&gt;Each frame, before getting the appropriate per-frame objects (vertex buffer, command allocator, ...), we need to make sure the GPU has finished work with at least one of the frames in-flight.&lt;br&gt;
To do that we'll use a &lt;a href="https://developer.apple.com/documentation/metal/mtlsharedevent" rel="noopener noreferrer"&gt;Shared Event&lt;/a&gt;, an object used to synchronise operations between the CPU and the GPU (like Vulkan Fences).&lt;/p&gt;

&lt;p&gt;We need to keep the event itself, and also a count of all the frames rendered so far:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// In the Renderer struct/class&lt;/span&gt;
&lt;span class="n"&gt;MTL&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;SharedEvent&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;    &lt;span class="n"&gt;frame_available_shared_event&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;nullptr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kt"&gt;size_t&lt;/span&gt;               &lt;span class="n"&gt;frame_num&lt;/span&gt;                    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The even can be created like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;frame_available_shared_event&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;newSharedEvent&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;frame_available_shared_event&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;setSignaledValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Shared Events work this way:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The GPU sets a predetermined value (signalling) once the work is done. We do this on the command queue after we've finished encoding command buffers:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ... encoding command buffers ...&lt;/span&gt;
&lt;span class="c1"&gt;// ... presenting drawable ...&lt;/span&gt;
&lt;span class="n"&gt;cmd_queue&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;signalEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;frame_available_shared_event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;frame_num&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;frame_num&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// Done for this frame&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;The CPU waits until the event has that value. Because we have 3 frames in flight, we wait for the one 3 frames ago to be done. We do this before we start encoding commands.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;frame_available_shared_event&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;waitUntilSignaledValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;frame_num&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;MAX_FRAMES_IN_FLIGHT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;timeout_in_ms&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Now we can safely access this frame's triple-buffered resources&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;u8&lt;/span&gt; &lt;span class="n"&gt;frame_idx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;frame_num&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="n"&gt;MAX_FRAMES_IN_FLIGHT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;MTL4&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CommandAllocator&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;cmd_alloc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cmd_allocators&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="n"&gt;frame_idx&lt;/span&gt; &lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before we can present the results of a frame, we first need to get the next drawable, and the queue will have to explicitly synchronise with it. This is a bit more verbose than Metal 3, where you could just encode a &lt;code&gt;presentDrawable&lt;/code&gt; command directly on the command buffer.&lt;br&gt;
But it's not that complex either.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;MTL&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Drawable&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;surface&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;metal_layer&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;nextDrawable&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// According to the documentation you *must* do it in this exact order&lt;/span&gt;
&lt;span class="n"&gt;cmd_queue&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;wait&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;surface&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;cmd_queue&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;commit&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;cmd_buf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;cmd_queue&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;signalDrawable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;surface&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;surface&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;present&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you run it now it would crash because we haven't set up the command buffer.&lt;br&gt;
Just so we can get something on the screen, we'll set it up with no actual commands encoded.&lt;br&gt;
The command buffer(s) frame workflow is pretty straightforward, you begin it, encode commands in it, and end it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;cmd_buf&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;beginCommandBuffer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;cmd_alloc&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// We'll encode commands here later...&lt;/span&gt;
&lt;span class="n"&gt;cmd_buf&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;endCommandBuffer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, if you compile and run it, you should get a fully opaque black window:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feg6e42w1mw22j5qcaoml.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feg6e42w1mw22j5qcaoml.png" alt="Black Window" width="800" height="652"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Encoding commands
&lt;/h3&gt;
&lt;h4&gt;
  
  
  Render pass and encoder
&lt;/h4&gt;

&lt;p&gt;A Render Pass is essentially a group of commands that share the same outputs (attachments).&lt;br&gt;
You can configure a bunch of settings and fixed function operations for each of the attachments, but let's get started by clearing the surface with a given color (magenta in this example):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// First we create the pass descriptor...&lt;/span&gt;
&lt;span class="k"&gt;auto&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;pass_desc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MTL4&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;RenderPassDescriptor&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;alloc&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;defer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;pass_desc&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;release&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// ...then we setup its first color attachment&lt;/span&gt;
&lt;span class="n"&gt;MTL&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;RenderPassColorAttachmentDescriptor&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;color_attachment&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pass_desc&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;colorAttachments&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;color_attachment&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;setTexture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;surface&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;texture&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;color_attachment&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;setClearColor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;MTL&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ClearColor&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1.0&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;p&gt;We also need to tell Metal what to do before (load) and after (store) it starts rendering to this attachment.&lt;br&gt;
We just want to clear it to the given color and then store it to be used in future passes, but there're other options if you need to preserve the previous contents, or if you don't care about them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;color_attachment&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;setLoadAction&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;MTL&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;LoadActionClear&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;color_attachment&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;setStoreAction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;MTL&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;StoreActionStore&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally we need to encode the pass into the command buffer. To do that we'll need Command Encoders. They configure the render pipeline, and set up resources for draw calls.&lt;br&gt;
From the &lt;a href="https://developer.apple.com/documentation/metal/understanding-the-metal-4-core-api" rel="noopener noreferrer"&gt;docs&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The most important difference with Metal 4 encoders is that they don’t have methods that bind individual buffers, textures, and heaps. Instead, you configure the resource bindings in an argument table and then bind that table to one or more pipeline stages with a command encoder&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There're more differences specific for &lt;em&gt;render&lt;/em&gt; command encoders, but I won't get into those here because they're not relevant for a simple "Hello, Triangle!".&lt;/p&gt;

&lt;p&gt;We'll create the encoder and leave it empty for now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;MTL4&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;RenderCommandEncoder&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;encoder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cmd_buf&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;renderCommandEncoder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;pass_desc&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;defer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;encoder&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;endEncoding&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;p&gt;Once we're done with that, we need to end the command buffer, commit it to the queue, and present it; like we already did in the "Frame Synchronisation" section.&lt;/p&gt;

&lt;p&gt;If you run it now you should get a magenta window:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjb54zj554z78cn3bdbph.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjb54zj554z78cn3bdbph.png" alt="Magenta Window" width="800" height="652"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  The Pipeline and Shaders
&lt;/h3&gt;
&lt;h4&gt;
  
  
  The Metal 4 Compiler
&lt;/h4&gt;

&lt;p&gt;Metal 4 introduces a new class that can compile both shaders and PSOs: &lt;code&gt;MTL4::Compiler&lt;/code&gt;.&lt;br&gt;
We only need it when we build the PSO, so (for this example) it doesn't need to be a member or anything:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// In the initialisation:&lt;/span&gt;
&lt;span class="n"&gt;MTL4&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Compiler&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;compiler&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;auto&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;compiler_desc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MTL4&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CompilerDescriptor&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;alloc&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;defer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;compiler_desc&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;release&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;compiler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;newCompiler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;compiler_desc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;nullptr&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;h4&gt;
  
  
  The Shaders and Library
&lt;/h4&gt;

&lt;p&gt;For now we'll hardcode the vertex positions and colors in the shader.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/shaders/triangle.metal&lt;/span&gt;

&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;metal_stdlib&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="k"&gt;namespace&lt;/span&gt; &lt;span class="n"&gt;metal&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;constant&lt;/span&gt; &lt;span class="n"&gt;float4&lt;/span&gt; &lt;span class="n"&gt;positions&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;  &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;  &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="n"&gt;constant&lt;/span&gt; &lt;span class="n"&gt;float4&lt;/span&gt; &lt;span class="n"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;VertexOut&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;float4&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;position&lt;/span&gt;&lt;span class="p"&gt;]];&lt;/span&gt;
    &lt;span class="n"&gt;float4&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="n"&gt;vertex&lt;/span&gt;
&lt;span class="n"&gt;VertexOut&lt;/span&gt; &lt;span class="nf"&gt;vertex_shader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;uint&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="n"&gt;vertex_id&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;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;position&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;positions&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;color&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="n"&gt;id&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;span class="n"&gt;fragment&lt;/span&gt;
&lt;span class="n"&gt;float4&lt;/span&gt; &lt;span class="nf"&gt;fragment_shader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;VertexOut&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="n"&gt;stage_in&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;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;color&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;p&gt;You can compile it like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# compile individual shader&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;xcrun metal &lt;span class="nt"&gt;-c&lt;/span&gt; src/shaders/triangle.metal &lt;span class="nt"&gt;-o&lt;/span&gt; build/bin/shaders/triangle.air
&lt;span class="c"&gt;# link into a library&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;xcrun metal &lt;span class="nt"&gt;-o&lt;/span&gt; /build/bin/shaders/shaders.metallib build/bin/shaders/&lt;span class="k"&gt;*&lt;/span&gt;.air 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I added it to the &lt;code&gt;build.cpp&lt;/code&gt; as a post build script.&lt;/p&gt;

&lt;p&gt;You could also use the new &lt;code&gt;MTL4::Compiler&lt;/code&gt; we created before to compile the shaders on runtime (learn more &lt;a href="https://developer.apple.com/documentation/metal/mtl4compiler/makelibrary(descriptor:)" rel="noopener noreferrer"&gt;here&lt;/a&gt;), but I prefer to compile them offline.&lt;/p&gt;

&lt;p&gt;A shader library contains the project's shaders, now it's the time to create one. Many tutorials use the "default library", but that requires using Xcode bundles and other complications that I'm actively trying to avoid here.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// In the Renderer struct/class&lt;/span&gt;
&lt;span class="n"&gt;MTL&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Library&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;shader_lib&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;nullptr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// In the initialisation:&lt;/span&gt;
&lt;span class="n"&gt;shader_lib&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;newLibrary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;ns_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s"&gt;"build/bin/shaders/shaders.metallib"&lt;/span&gt; &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nb"&gt;nullptr&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On Metal 3 you could now create &lt;code&gt;MTL::Function&lt;/code&gt; objects from the library to pass them to the PSO descriptor.&lt;br&gt;
Metal 4 does things slightly differently. The new PSO descriptor requires &lt;code&gt;MTL4::LibraryFunctionDescriptor&lt;/code&gt;  that have the library as a member:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Metal 3&lt;/span&gt;
&lt;span class="k"&gt;auto&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;vert_shader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;shader_lib&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;newFunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;ns_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s"&gt;"vertex_shader"&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;auto&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;frag_shader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;shader_lib&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;newFunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;ns_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s"&gt;"fragment_shader"&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Metal 4&lt;/span&gt;
&lt;span class="k"&gt;auto&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;vertex_fun&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MTL4&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;LibraryFunctionDescriptor&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;alloc&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;vertex_fun&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;setLibrary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;shader_lib&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;vertex_fun&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;setName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;ns_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s"&gt;"vertex_shader"&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;auto&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;fragment_fun&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MTL4&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;LibraryFunctionDescriptor&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;alloc&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;fragment_fun&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;setLibrary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;shader_lib&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;fragment_fun&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;setName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;ns_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s"&gt;"fragment_shader"&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;p&gt;Metal 3 was nicer to use imo, but I'm guessing the reasoning behind the new system is so everything is done at once by the &lt;code&gt;MTL4::Compiler&lt;/code&gt; when it creates the PSO.&lt;/p&gt;

&lt;h4&gt;
  
  
  The PSO
&lt;/h4&gt;

&lt;p&gt;We now have everything we need to create the PSO.&lt;br&gt;
It's pretty straightforward, and the only difference from Metal 3 is that we now do it through the compiler instead of through the device:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// In the Renderer struct/class:&lt;/span&gt;
&lt;span class="n"&gt;MTL&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;RenderPipelineState&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;pso&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;nullptr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// In the initialisation:&lt;/span&gt;
&lt;span class="k"&gt;auto&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MTL4&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;RenderPipelineDescriptor&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;alloc&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;defer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;desc&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;release&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;desc&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;setLabel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;ns_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s"&gt;"Hello Triangle PSO"&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;desc&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;colorAttachments&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;setPixelFormat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;PIXEL_FORMAT&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;desc&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;setVertexFunctionDescriptor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;vertex_fun&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;desc&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;setFragmentFunctionDescriptor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;fragment_fun&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;pso&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;compiler&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;newRenderPipelineState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;desc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MTL4&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CompilerTaskOptions&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;nullptr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;NS&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;nullptr&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also provide a &lt;code&gt;MTL4::CompilerTaskOptions&lt;/code&gt; with an array of archives that can be used to speed up compilation, but I'm skipping that for this example.&lt;/p&gt;

&lt;h3&gt;
  
  
  DRAW!
&lt;/h3&gt;

&lt;p&gt;We are ready!&lt;br&gt;
Head back to the command encoder, set the PSO, and add the drawcall:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;encoder&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;setRenderPipelineState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;pso&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;encoder&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;drawPrimitives&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;MTL&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;PrimitiveTypeTriangle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I also changed the clear color to a calmer grey.&lt;/p&gt;

&lt;p&gt;If you now run it you should get something like this:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvkvp1s0kq4bo73x4htyu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvkvp1s0kq4bo73x4htyu.png" alt="Hello Triangle" width="800" height="652"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  BONUS: Using a proper Vertex Buffer
&lt;/h3&gt;

&lt;p&gt;So far we've used a hardcoded vertex buffer in the shader.&lt;/p&gt;

&lt;p&gt;An important caveat is that Metal 4 treats vertex buffers as any other buffer. There's no &lt;code&gt;encoder-&amp;gt;setVertexBuffer()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We'll need 3 buffers, one for each frame in flight, although we could use just 1 for this example because they won't change.&lt;br&gt;
But before we get there I  need to explain  2 concepts: &lt;em&gt;Argument Tables&lt;/em&gt; and &lt;em&gt;Residency Sets&lt;/em&gt;.&lt;/p&gt;
&lt;h4&gt;
  
  
  Argument Tables
&lt;/h4&gt;

&lt;p&gt;Argument Tables are new (at least as something exposed to the programmer) in Metal 4, and are essentially a list of the resource binding points that an encoder needs per stage (they can be reused and shared across encoders). For bindless, they'll probably have just 1 buffer. &lt;br&gt;
For this, all we need is a single table with 1 buffer (the vertex buffer):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// In the Renderer struct/class&lt;/span&gt;
&lt;span class="n"&gt;MTL4&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ArgumentTable&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;arg_table&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;nullptr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// In the initialisation:&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Personally, I'm not a fan of allocating a *descriptor*, but wcyd?&lt;/span&gt;
    &lt;span class="k"&gt;auto&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MTL4&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ArgumentTableDescriptor&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;alloc&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;defer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;desc&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;release&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;desc&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;setMaxBufferBindCount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// For the sake of simplicity I'm completely ignoring error management&lt;/span&gt;
    &lt;span class="n"&gt;arg_table&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;newArgumentTable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;desc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="cm"&gt;/* error */&lt;/span&gt; &lt;span class="nb"&gt;nullptr&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;h4&gt;
  
  
  Residency Sets
&lt;/h4&gt;

&lt;p&gt;To make sure a resource is available for the GPU before using it you can manually make it resident (&lt;code&gt;useResource&lt;/code&gt; and &lt;code&gt;useHeap&lt;/code&gt;), but that has an overhead that builds up quickly. &lt;br&gt;
Residency sets on the other hand can make them all resident at the same time, with less cost.&lt;br&gt;
You can populate/update them at any point, and then you can attach them to a command buffer or the whole queue.&lt;br&gt;
The driver will then make all the resources resident at once when you call &lt;code&gt;commit&lt;/code&gt; on the command buffer (unless you explicitly request ahead-of-time residency).&lt;br&gt;
It's a pretty neat feature, you can learn more about it &lt;a href="https://developer.apple.com/documentation/metal/simplifying-gpu-resource-management-with-residency-sets" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For now, let's simply create a long-term (meaning resources in this set will stay resident for the whole lifetime of the application) set:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// In the Renderer struct/class&lt;/span&gt;
&lt;span class="n"&gt;MTL&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ResidencySet&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;residency_set&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;nullptr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// In the initialisation:&lt;/span&gt;
&lt;span class="k"&gt;auto&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MTL&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ResidencySetDescriptor&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;alloc&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;residency_set&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;newResidencySet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;desc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;nullptr&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;desc&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;release&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After adding resources to the set (which we'll do in a minute), we need to commit it, and add it to the command queue.&lt;br&gt;
We should also add the Metal layer's residency set, which is managed by AppKit.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// In the initialisation:&lt;/span&gt;
&lt;span class="n"&gt;residency_set&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;commit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;cmd_queue&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;addResidencySet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;residency_set&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;cmd_queue&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;addResidencySet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;metal_layer&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;residencySet&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;h4&gt;
  
  
  Creating the Vertex Buffer(s)
&lt;/h4&gt;

&lt;p&gt;Now that we have the argument table and residence set, we can create the vertex buffers.&lt;br&gt;
As I mentioned before, Metal 4 doesn't have a &lt;code&gt;setVertexBuffer()&lt;/code&gt;, they are treated as regular buffers.&lt;br&gt;
We'll create them with the &lt;code&gt;Shared&lt;/code&gt; storage mode so we can write to them directly from the CPU.&lt;br&gt;
We also need to add them to the residency set so the driver can make sure everything is available before it starts drawing.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;WARNING &lt;br&gt;
Because for this example these buffers won't change, I'll just populate and add them to the residency set and argument table during initialisation. However, in a normal use case we would do it before the draw call, and/or manage the argument table and residency set differently.&lt;br&gt;
&lt;/p&gt;


&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;constexpr&lt;/span&gt; &lt;span class="n"&gt;u8&lt;/span&gt; &lt;span class="n"&gt;VERTEX_BUFFER_BINDING_IDX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;Vertex&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;vec4&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;vec4&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;constexpr&lt;/span&gt; &lt;span class="n"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;Vertex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;triangle_vertices&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Vertex&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;position&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;Vertex&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;position&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;Vertex&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;position&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&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;span class="c1"&gt;// In the Renderer struct/class&lt;/span&gt;
&lt;span class="n"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;MTL&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Buffer&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;MAX_FRAMES_IN_FLIGHT&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;vertex_buffers&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// In the initialisation&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;u8&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;vertex_buffers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="o"&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="n"&gt;vertex_buffers&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="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;device&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;newBuffer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="cm"&gt;/* length  */&lt;/span&gt; &lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;Vertex&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;triangle_vertices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="cm"&gt;/* options */&lt;/span&gt; &lt;span class="n"&gt;MTL&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ResourceStorageModeShared&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s"&gt;"Vertex Buffer for frame #{}"&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;vertex_buffers&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="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;setLabel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;ns_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;c_str&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;span class="c1"&gt;// Populate the vertex buffer&lt;/span&gt;
    &lt;span class="n"&gt;memcpy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;vertex_buffers&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="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="n"&gt;triangle_vertices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="k"&gt;sizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;Vertex&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;triangle_vertices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;residency_set&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;addAllocation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;vertex_buffers&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="n"&gt;arg_table&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;setAddress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;vertex_buffers&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="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;gpuAddress&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;VERTEX_BUFFER_BINDING_IDX&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;p&gt;Then, each frame before encoding the draw call, we set the argument table:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Renderer::render_frame()&lt;/span&gt;
&lt;span class="n"&gt;MTL4&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;RenderCommandEncoder&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;encoder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cmd_buf&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;renderCommandEncoder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;pass_desc&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="n"&gt;encoder&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;setArgumentTable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;arg_table&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;MTL&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;RenderStageVertex&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="n"&gt;encoder&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;drawPrimitives&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;MTL&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;PrimitiveTypeTriangle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, we make the appropriate changes to the vertex shader:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// These two could be defined in a header file included by both the Renderer and the shader&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;constant&lt;/span&gt; &lt;span class="n"&gt;uint&lt;/span&gt; &lt;span class="n"&gt;VERTEX_BUFFER_BINDING_IDX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;VertexIn&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;float4&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;float4&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;VertexOut&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;float4&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;position&lt;/span&gt; &lt;span class="p"&gt;]];&lt;/span&gt;
    &lt;span class="n"&gt;float4&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="n"&gt;vertex&lt;/span&gt;
&lt;span class="n"&gt;VertexOut&lt;/span&gt; &lt;span class="nf"&gt;vertex_shader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;uint&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt; &lt;span class="n"&gt;vertex_id&lt;/span&gt; &lt;span class="p"&gt;]],&lt;/span&gt;
    &lt;span class="n"&gt;constant&lt;/span&gt; &lt;span class="n"&gt;VertexIn&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;vertices&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt; &lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;VERTEX_BUFFER_BINDING_IDX&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;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;vertices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="n"&gt;id&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;vertices&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;color&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;



</description>
      <category>cpp</category>
      <category>graphics</category>
      <category>tutorial</category>
      <category>metal</category>
    </item>
    <item>
      <title>I ❤️ Offline</title>
      <dc:creator>Javier Salcedo</dc:creator>
      <pubDate>Mon, 01 Sep 2025 22:31:40 +0000</pubDate>
      <link>https://forem.com/javiersalcedopuyo/i-offline-2i0i</link>
      <guid>https://forem.com/javiersalcedopuyo/i-offline-2i0i</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foyv6a0rgnco9ct006arm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foyv6a0rgnco9ct006arm.png" alt="I LOVE OFFLINE FILES AND TOOLS!" width="800" height="771"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let me start by saying this blogpost doesn’t come from a place of hate. I don’t &lt;em&gt;hate&lt;/em&gt; online tools, I don’t &lt;em&gt;hate&lt;/em&gt; media streaming. Many times they’re just more convenient, and I use them &lt;strong&gt;a lot&lt;/strong&gt; (I even had a &lt;a href="https://en.m.wikipedia.org/wiki/Google_Stadia" rel="noopener noreferrer"&gt;Stadia&lt;/a&gt; back in the day).&lt;br&gt;
But I just &lt;em&gt;love&lt;/em&gt; not needing it. That feeling of independence, of not having to ask a server for permission to access your own stuff, is why I went back to buy physical media.&lt;br&gt;
It’s faster to access, it doesn’t require having a power and memory hungry program like a modern web browser constantly open, and it works even when I’m on a plane, or somewhere with spotty or slow internet.&lt;br&gt;
The main problem is that you need to know what you'll need &lt;em&gt;in advance&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;However, sometimes what you need is mainly online, or simply my terminally online brain struggles to conceive that you don't &lt;em&gt;need&lt;/em&gt; a web browser for that. So it always feels really nice to discover new tools that allow you to access some resources offline.&lt;br&gt;
Here are some of my favourites.&lt;/p&gt;

&lt;h2&gt;
  
  
  Notes and TODOs
&lt;/h2&gt;

&lt;p&gt;The weird thing is that we accepted online-first or even online-only note taking apps.&lt;br&gt;
I used to be a huge fan of &lt;a href="https://trello.com" rel="noopener noreferrer"&gt;Trello&lt;/a&gt; and later &lt;a href="https://www.notion.com" rel="noopener noreferrer"&gt;Notion&lt;/a&gt;, but their online-first nature ended up getting in the way.&lt;br&gt;
Nowadays I just use a very simple system of templated Markdown files. I'm even considering trying out Org-mode (outside emacs, I'm a vim type of guy).&lt;/p&gt;

&lt;h2&gt;
  
  
  Documentation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Man pages
&lt;/h3&gt;

&lt;p&gt;Good old &lt;strong&gt;man pages&lt;/strong&gt;. That's it. They nailed it back in the 70s.&lt;br&gt;
The navigation could be improved, but they're fast and exhaustive. They were created in a time &lt;em&gt;before&lt;/em&gt; the Internet after all (why, they even predate &lt;strong&gt;C&lt;/strong&gt;!)&lt;br&gt;
Depending on your platform, there might be GUI apps to navigate them a bit more comfortably, like &lt;a href="https://gitlab.gnome.org/GNOME/yelp/" rel="noopener noreferrer"&gt;Yelp&lt;/a&gt; on Linux, or &lt;a href="https://troz.net/manreader/" rel="noopener noreferrer"&gt;Man Reader&lt;/a&gt; on macOS, but TUI pagers like &lt;code&gt;less&lt;/code&gt; or even &lt;code&gt;vim&lt;/code&gt; do a good enough job imho.&lt;/p&gt;

&lt;p&gt;I also really like the &lt;a href="https://github.com/aitjcize/cppman" rel="noopener noreferrer"&gt;cppman&lt;/a&gt; project, which allows you to download and navigate &lt;a href="https://cppreference.com" rel="noopener noreferrer"&gt;cppreference&lt;/a&gt; in a similar manner.&lt;/p&gt;

&lt;h3&gt;
  
  
  Kiwix
&lt;/h3&gt;

&lt;p&gt;I recently discovered &lt;a href="https://kiwix.org" rel="noopener noreferrer"&gt;Kiwix&lt;/a&gt;, which is meant for offline Wikipedia access but also has a bunch of documentation for APIs, tools and languages like Vulkan, CMake, Python, etc.&lt;br&gt;
You can even download the &lt;em&gt;whole&lt;/em&gt; of StackOverflow (80GB at the time of writing).&lt;/p&gt;

&lt;h3&gt;
  
  
  macOS' 1st party apps
&lt;/h3&gt;

&lt;p&gt;If you're on macOS, Apple’s own &lt;em&gt;Developer&lt;/em&gt; app allows you to download the &lt;strong&gt;excellent&lt;/strong&gt; educational videos from past WWDC.&lt;br&gt;
&lt;em&gt;Xcode&lt;/em&gt; also includes an offline version of all the Apple frameworks’ documentation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Blogs &amp;amp; Websites
&lt;/h2&gt;

&lt;p&gt;I use &lt;a href="https://goodlinks.app" rel="noopener noreferrer"&gt;GoodLinks&lt;/a&gt; to save and synchronise  articles across all my devices using iCloud.&lt;br&gt;
It’s a paid app but it’s absolutely worth it.&lt;br&gt;
I used to be one of the dozens of &lt;a href="https://getpocket.com/home" rel="noopener noreferrer"&gt;Pocket&lt;/a&gt; fans, until Mozilla killed it. Same with &lt;a href="https://omnivore.app" rel="noopener noreferrer"&gt;Omnivore&lt;/a&gt;. 🥀🪦&lt;/p&gt;

&lt;p&gt;For other pages that might not translate so well to the “Reader View”, I use the “Reading List” in Safari (or lately on &lt;a href="https://kagi.com/orion/" rel="noopener noreferrer"&gt;Orion&lt;/a&gt;). It allows you to download the whole page as it is.&lt;br&gt;
I'm not sure if there's a similar feature for Chromium or Firefox, but you can always print the page as a PDF!&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>tools</category>
      <category>recommendations</category>
    </item>
    <item>
      <title>One must imagine Sisyphus writing a new JS framework</title>
      <dc:creator>Javier Salcedo</dc:creator>
      <pubDate>Fri, 27 Sep 2024 19:51:33 +0000</pubDate>
      <link>https://forem.com/javiersalcedopuyo/one-must-imagine-sisyphus-writing-a-new-js-framework-3h04</link>
      <guid>https://forem.com/javiersalcedopuyo/one-must-imagine-sisyphus-writing-a-new-js-framework-3h04</guid>
      <description>&lt;p&gt;We inherited incredibly complex systems built by the greatest minds of previous generations.&lt;/p&gt;

&lt;p&gt;With the fatal arrogance of a teenager that got their heart broken for the first time and thinks no one understands them or has ever felt what they feel, we saw their limitations, pitfalls and compromises, and attribuited them to incompetence or even malice. And promptly dedicated our efforts to reinvent the wheel, &lt;em&gt;because we knew better&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;We added layer upon layer of complexity, and each time a new model, theory or framework failed us, rather than reevaluating and stepping back, we added a new one. A patch to fix the uninteded consequences of the previous patch... ad aeternum.&lt;/p&gt;

&lt;p&gt;And a new bureaucratic class of scrum masters, and agile coaches, and productivity gurus was born to manage all of that.&lt;/p&gt;

&lt;p&gt;And then things started to break down.&lt;/p&gt;

&lt;p&gt;What used to be done fast and cheaply now takes orders or magnitude longer, with much bigger teams, and somehow it feels lower quality.&lt;br&gt;
We have much greater resources at our disposal and yet we can't seem to achieve things that we already did decades ago.&lt;br&gt;
And in an schizophrenic combination of learned helplessness and god complex we deceive ourselves, coming up with rationalizations about how, actually, it can't and it shouldn't be done like that.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We're doing great!&lt;/p&gt;

&lt;p&gt;Our latest self-analysis shows an improvement of N%!&lt;/p&gt;

&lt;p&gt;Each year/version we're sweeping the floor with the last!&lt;/p&gt;

&lt;p&gt;The old ways aren't feasible nowadays! It was unsafe! Systems are too complex now!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;(Also, when so many people suffer of impostor syndrome, &lt;em&gt;something&lt;/em&gt; is not right)&lt;/p&gt;

&lt;p&gt;But people start to notice anyway, and then new factions started to emerge.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Reject modernity! Embrace C99 and Emacs!&lt;/p&gt;

&lt;p&gt;No! That wasn't &lt;em&gt;really&lt;/em&gt; applying the methodology of &lt;em&gt;my&lt;/em&gt; ambiguous book! We need just another layer of abstraction!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The End is NOT nigh
&lt;/h2&gt;

&lt;p&gt;They're all wrong. I say we're actually in a golden age of computing.&lt;/p&gt;

&lt;p&gt;At some point, each generation thinks they have it uniquely difficult. Even &lt;a href="https://www.azquotes.com/quote/673142" rel="noopener noreferrer"&gt;Socrates&lt;/a&gt; (allegedly) believed the youths of his time were proof of the unique decay of his society.&lt;/p&gt;

&lt;p&gt;True, there're issues. Shitty code and bloated, buggy projects are everywhere nowadays, but so is knowledge. It's in our hands to build something better.&lt;/p&gt;

&lt;p&gt;We have access to computers orders of magnitudes more powerful than the mainframes of the &lt;em&gt;olden days&lt;/em&gt;, and dirt cheap.&lt;br&gt;
In fact, so cheap, I've seen homeless people in the park with laptops.&lt;/p&gt;

&lt;p&gt;You can install a &lt;a href="https://xubuntu.org" rel="noopener noreferrer"&gt;light weight Linux distro&lt;/a&gt; (free of course) on almost any old piece of junk with a CPU and you'll get a perfectly good programming machine.&lt;/p&gt;

&lt;p&gt;You can choose from probably hundreds of programming languages. Or you can use top quality C/C++ compilers for free (that wasn't a thing back in the day!).&lt;/p&gt;

&lt;p&gt;There's also an incredibly wide selection of free and powerful tools.&lt;br&gt;
We even have lengthy discussions about what free editor or IDE is best.&lt;/p&gt;

&lt;p&gt;You can host your code for free and even allow others to review and modify it in places like &lt;a href="https://github.com" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; or &lt;a href="https://gitlab.com" rel="noopener noreferrer"&gt;GitLab&lt;/a&gt;.&lt;br&gt;
They even have CI/CD tools for free as well!&lt;br&gt;
And you can use it to read other people's code and learn from it. The source code of &lt;a href="https://github.com/id-Software/DOOM" rel="noopener noreferrer"&gt;Doom&lt;/a&gt; is available for you to look around!&lt;/p&gt;

&lt;p&gt;And, thanks to the modern Internet we have free and instant access to programming books, talks, conferences, classes and much more!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Harvard publishes CS lectures for free on &lt;a href="https://www.youtube.com/watch?v=3LPJfIKxwWc&amp;amp;list=PLhQjrBD2T381WAHyx1pq-sBfykqMBI7V4&amp;amp;index=1" rel="noopener noreferrer"&gt; YouTube &lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;So does the University of Utah about &lt;a href="https://www.youtube.com/watch?v=UVCuWQV_-Es&amp;amp;list=PLplnkTzzqsZS3R5DjmCQsqupu43oS9CFN" rel="noopener noreferrer"&gt;Computer Graphics&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;You have courses on a lot of different languages and technologies in &lt;a href="https://www.w3schools.com" rel="noopener noreferrer"&gt;W3Schools&lt;/a&gt; for free!&lt;/li&gt;
&lt;li&gt;I've also heard there're hidden websites in the dark corners of the interwebs where you can get free pdf copies of books...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We have free online communities full of people happy to discuss and teach.&lt;/p&gt;

&lt;h2&gt;
  
  
  "But I'm a single little dev. What change can I make?"
&lt;/h2&gt;

&lt;p&gt;You're not alone!&lt;/p&gt;

&lt;p&gt;Technology has democratised the production of many things. Empowering tiny teams to compete with (and even beat) multinationals.&lt;br&gt;
We're seeing the same phenomenon all over the place:&lt;/p&gt;

&lt;p&gt;Record companies produce bland and repetitive songs from cloned singers.&lt;br&gt;
But nowadays anyone can produce an album in their bedrooms. You just need a laptop and a cheap sound card (and maybe not even that).&lt;br&gt;
And places like Soundcloud are full of awful music from amateurs as proof of that.&lt;br&gt;
But some of those amateurs improve, and grow, and now you have fresh, original, and incredibly niche bands that would've never been able to make a living with their music in years past.&lt;/p&gt;

&lt;p&gt;The film and TV industries are producing increasingly mediocre and boring movies and series.&lt;br&gt;
But some &lt;a href="https://www.youtube.com/watch?v=9gk_rl3y_SU" rel="noopener noreferrer"&gt;nerdy kids with a laptop webcam&lt;/a&gt; have built multimedia empires because now anyone can have access to cheap recording equipment and video editing software.&lt;/p&gt;

&lt;p&gt;The same happened with video games thanks to projects like &lt;a href="https://unity.com" rel="noopener noreferrer"&gt;Unity&lt;/a&gt; or &lt;a href="https://www.blender.org" rel="noopener noreferrer"&gt;Blender&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We even have more people writing and selling &lt;a href="https://ourworldindata.org/books#all-charts" rel="noopener noreferrer"&gt;books&lt;/a&gt; than ever before!&lt;/p&gt;

&lt;p&gt;And &lt;em&gt;YOU&lt;/em&gt; can do the same for software.&lt;/p&gt;

&lt;p&gt;Big companies are the main ones producing all that bloated buggy code. They grew too big and fat, with tons of legacy code. Now they move slowly and can't take any risks.&lt;br&gt;
The democratisation of code opened the flood gates and &lt;em&gt;a lot&lt;/em&gt; of shitty code and shitty devs poured in, but so did a lot of talented devs that just need some experience. And experienced ones that just needed a push.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get inspired!
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Don't like having to use gigantic game engines to make games? Here's a whole suite of beautifully simple libraries with bindings for a lot of languages: &lt;a href="https://www.raylib.com" rel="noopener noreferrer"&gt;raylib&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;VisualStudio is too slow and bloated? Write your own IDE! &lt;a href="https://10xeditor.com" rel="noopener noreferrer"&gt;10x&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Debugging C++ is painfully slow? How about a super fast, tiny and portable debugger? &lt;a href="https://remedybg.itch.io/remedybg" rel="noopener noreferrer"&gt;remedybg&lt;/a&gt;?&lt;/li&gt;
&lt;li&gt;You crave the simplicity of C, but miss modern features and a nice build system? You got &lt;a href="https://ziglang.org" rel="noopener noreferrer"&gt;Zig&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Or maybe you want a language that's more geared towards game dev, and includes a lot of the commonly use libraries? &lt;a href="https://odin-lang.org" rel="noopener noreferrer"&gt;Odin&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Websites are too complex nowadays? Why not trying to make one using only text? &lt;a href="https://owickstrom.github.io/the-monospace-web/" rel="noopener noreferrer"&gt;Monospace Web&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;You don't like the licensing of your OS? BUILD YOUR OWN! &lt;a href="https://en.wikipedia.org/wiki/History_of_Linux#The_creation_of_Linux" rel="noopener noreferrer"&gt;Linux&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These were all built (or at least created) by single devs, passionate for their craft. And there are many other examples.&lt;br&gt;
I personally like the collection of the &lt;a href="https://handmade.network/projects" rel="noopener noreferrer"&gt;Handmade Network&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I love the concept of "recreational programming" pushed by &lt;a href="https://www.youtube.com/@TsodingDaily" rel="noopener noreferrer"&gt;Tsoding&lt;/a&gt;.&lt;br&gt;
And people like &lt;a href="https://www.youtube.com/@ThePrimeagen" rel="noopener noreferrer"&gt;ThePrimeagen&lt;/a&gt;, and &lt;a href="https://www.youtube.com/@LowLevel-TV" rel="noopener noreferrer"&gt;Low Level Learning&lt;/a&gt; not only demystify a lot of the scary parts of software, they also have a contagious passion about it.&lt;/p&gt;

&lt;p&gt;Now, some of these people are very opinionated about &lt;em&gt;the right way&lt;/em&gt; to write software. I tend to agree with them, but that's not the point of this article.&lt;br&gt;
Forget about &lt;em&gt;the right way&lt;/em&gt;. Just build stuff! Build crappy software. Refine it, tweak it, learn from it, improve. Find something you're excited about, or a piece of software you don't like, and write code.&lt;br&gt;
Don't let the doomsayers scare you, but remember to put love and care into it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One must imagine Sisyphus doing a (free) operating system (just for fun).&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>programming</category>
      <category>career</category>
      <category>learning</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Simple "Infinite" Grid Shader</title>
      <dc:creator>Javier Salcedo</dc:creator>
      <pubDate>Mon, 03 Jun 2024 20:25:12 +0000</pubDate>
      <link>https://forem.com/javiersalcedopuyo/simple-infinite-grid-shader-5fah</link>
      <guid>https://forem.com/javiersalcedopuyo/simple-infinite-grid-shader-5fah</guid>
      <description>&lt;p&gt;I tried to follow a "recipe" for an infinite grid shader, but after hitting a bug and not being able to fix it because I didn't understand the code, I decided to write it from scratch myself so I could understand it properly.&lt;/p&gt;

&lt;p&gt;In the process I found better implementations than the original one I was following, and better than my own.&lt;/p&gt;

&lt;p&gt;In the end I came up with a different, simpler approach, although more limited in some aspects, that ticks all my boxes at the moment.&lt;/p&gt;

&lt;p&gt;You can see the code &lt;a href="https://gitlab.com/ludusestars/Talos3D/-/commit/f15d291f764021ec4759a17dd601ad4448430ab1" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;br&gt;
Here's a detailed step-by-step explanation of how it works.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ DISCLAMER 1:&lt;br&gt;
The goal of this article is just to document and organise my thought process while I was trying to understand the code behind another implementation.&lt;br&gt;
If you want something robust / production-worthy, you should read &lt;a href="https://bgolus.medium.com/the-best-darn-grid-shader-yet-727f9278b9d8" rel="noopener noreferrer"&gt;this&lt;/a&gt; instead.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4vrcn2unvu187g9ufhsh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4vrcn2unvu187g9ufhsh.png" alt="this one is mine"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;⚠️ DISCLAIMER 2:&lt;br&gt;
I'm using MSL (Metal Shading Language) because I like it and because my toy renderer uses Metal as the graphics API anyway, but it should be pretty straightforward to translate it to GLSL or HLSL.&lt;/p&gt;

&lt;p&gt;⚠️ DISCLAIMER 3:&lt;br&gt;
This article will only focus on the shader part, but the setup in the host (CPU) side should be very simple regardless of API:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set everything up to render a quad (2 triangles)&lt;/li&gt;
&lt;li&gt;Enable depth testing&lt;/li&gt;
&lt;li&gt;Disable face culling (we want to be able to see it from bellow too)&lt;/li&gt;
&lt;li&gt;Because it's transparent it should be rendered &lt;em&gt;after&lt;/em&gt; the opaque objects&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  1. Finite version
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1.1 Vertex Shader
&lt;/h3&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;

&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;constant&lt;/span&gt; &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;grid_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;constant&lt;/span&gt; &lt;span class="n"&gt;float4&lt;/span&gt; &lt;span class="n"&gt;positions&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;VertexOut&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="n"&gt;float4&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;position&lt;/span&gt; &lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt; &lt;span class="n"&gt;invariant&lt;/span&gt; &lt;span class="p"&gt;]];&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="n"&gt;float2&lt;/span&gt; &lt;span class="n"&gt;coords&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="n"&gt;vertex&lt;/span&gt;
&lt;span class="n"&gt;VertexOut&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;uint&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="n"&gt;vertex_id&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
    &lt;span class="n"&gt;float4x4&lt;/span&gt; &lt;span class="n"&gt;view_proj&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="p"&gt;)]])&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;world_pos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;positions&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="n"&gt;world_pos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;xyz&lt;/span&gt; &lt;span class="o"&gt;*=&lt;/span&gt; &lt;span class="n"&gt;grid_size&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;position&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;view_proj&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;world_pos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;coords&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;world_pos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;xz&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;p&gt;This will create a quad of size 1x1 centred at the origin.&lt;/p&gt;

&lt;p&gt;If we output the normalised coordinates in the fragment shader...&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;

&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="n"&gt;FragmentIn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;VertexOut&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="n"&gt;fragment&lt;/span&gt;
&lt;span class="n"&gt;float4&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FragmentIn&lt;/span&gt; &lt;span class="n"&gt;frag&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="n"&gt;stage_in&lt;/span&gt;&lt;span class="p"&gt;]])&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;float4&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;frag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;coords&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;grid_size&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;c&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;p&gt;...you'll get something like this:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faedju9jejmspyhrfpbzq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faedju9jejmspyhrfpbzq.png" alt="quad UVs"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  1.2 Fragment Shader
&lt;/h3&gt;

&lt;h4&gt;
  
  
  1.2.0 Modulo
&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ IMPORTANT in case you're using MSL or HLSL&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://registry.khronos.org/OpenGL-Refpages/gl4/html/mod.xhtml" rel="noopener noreferrer"&gt;GLSL&lt;/a&gt; defines its modulo function as &lt;code&gt;x - y * floor(x/y)&lt;/code&gt;&lt;br&gt;
which produces this type of repeating pattern:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4ct3hsicim821iwvydad.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4ct3hsicim821iwvydad.png" alt="GLSL's mod"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, both &lt;a href="https://developer.apple.com/metal/Metal-Shading-Language-Specification.pdf" rel="noopener noreferrer"&gt;MSL&lt;/a&gt; and &lt;a href="https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-fmod" rel="noopener noreferrer"&gt;HLSL&lt;/a&gt; define it as &lt;code&gt;x - y * trunc(x/y)&lt;/code&gt;,&lt;br&gt;
which produces this:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftppjmz0q6zpac028ttdl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftppjmz0q6zpac028ttdl.png" alt="fmod"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since I'm using MSL, I'll add a template function that mimics GLSL's &lt;code&gt;mod&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;

&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typename&lt;/span&gt; &lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;typename&lt;/span&gt; &lt;span class="nc"&gt;U&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;constexpr&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="nf"&gt;mod&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;U&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;y&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;p&gt;For HLSL you'll have to rely on macros:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;

&lt;span class="cp"&gt;#define mod(x,y) ((x) - (y) * floor((x)/(y)))
&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;EDIT: From HLSL 2021 you can also use templates.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  1.2.1 Draw the cells
&lt;/h4&gt;

&lt;p&gt;We'll subdivide the grid into 2 types of cells:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Big 1x1m cells&lt;/li&gt;
&lt;li&gt;Small 0.1x0.1 (sub)cells
To make it clearer to see, I'm increasing the plane size to 2x2m
```C++
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;static constant auto grid_size = 2.0f;&lt;/p&gt;

&lt;p&gt;static constant auto cell_size = 1.0f;&lt;br&gt;
static constant auto half_cell_size = cell_size * 0.5f;&lt;/p&gt;

&lt;p&gt;static constant auto subcell_size = 0.1f;&lt;br&gt;
static constant auto half_subcell_size = subcell_size * 0.5f;&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
We start by calculating the coordinates inside the cells and subcells.

1. First, displace the plane coordinates so the world's origin is in a corner rather than in the middle of the (sub)cell.
2. Then get the coordinates inside the (sub)cell
```C++


auto cell_coords    = mod( frag.coords + half_cell_size,    cell_size    );
auto subcell_coords = mod( frag.coords + half_subcell_size, subcell_size );


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;If we normalise and output this as a colour, we'll get something like this:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Cell UVs&lt;/th&gt;
&lt;th&gt;Subcell UVs&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fby7kljl5g81dl2qinglf.png" alt="Cell UVs"&gt;&lt;/td&gt;
&lt;td&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2filpq4k6bibef04gk26.png" alt="Subcell UVs"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Next we calculate the distances in U and V to the (sub)cell's edge.&lt;br&gt;
The coordinates we calculated before are in the [0, (sub)cell_size] range so:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;First, we transform them into the [-(sub)cell_size/2, (sub)cell_size/2] range so the center of the (sub)cell has the (0,0) coordinates.&lt;/li&gt;
&lt;li&gt;Then, the distance to the edge is simply the absolute of these new coordinates&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;

&lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;distance_to_cell&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;cell_coords&lt;/span&gt;    &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;half_cell_size&lt;/span&gt;    &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;distance_to_subcell&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;subcell_coords&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;half_subcell_size&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now it's time to draw the lines.&lt;br&gt;
Comparing that distance to the (sub)cell line thickness, we determine if we should draw the line or not:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;

&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;constant&lt;/span&gt; &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;cell_line_thickness&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.01&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;constant&lt;/span&gt; &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;subcell_line_thickness&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.001&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;constant&lt;/span&gt; &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;cell_colour&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;float4&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="mf"&gt;0.75&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.75&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.75&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;constant&lt;/span&gt; &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;subcell_colour&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;float4&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;  &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="p"&gt;...&lt;/span&gt;

&lt;span class="c1"&gt;// In the fragment shader&lt;/span&gt;
&lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;float4&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;distance_to_subcell&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;subcell_line_thickness&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subcell_color&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;distance_to_cell&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;cell_line_thickness&lt;/span&gt;    &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cell_color&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The line thicknesses are halved because only half the line is within a given (sub)cell, as the other half is in the neighbouring (sub)cell&lt;/p&gt;

&lt;p&gt;However, this has obvious issues (I made the plane opaque and the lines yellow to make it clearer):&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flhe798jk5pyr4n22ne94.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flhe798jk5pyr4n22ne94.png" alt="First Grid"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  1.2.2 Get uniform lines
&lt;/h4&gt;

&lt;p&gt;So the problem here is that, due to perspective, the coordinates can change drastically from one fragment to another, meaning that the exact coordinates that fall within the line might be skipped from one fragment to the next.&lt;/p&gt;

&lt;p&gt;An easy solution is to increase the line width by how much the coordinates change across fragments.&lt;br&gt;
Thankfully, we get a very handy tool to get that change: partial derivatives.&lt;br&gt;
If you don't know what they are or how they work, I recommend reading &lt;a href="https://www.aclockworkberry.com/shader-derivative-functions/" rel="noopener noreferrer"&gt;this article&lt;/a&gt;.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;

&lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;fwidth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;frag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;coords&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;adjusted_cell_line_thickness&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;cell_line_thickness&lt;/span&gt;&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;adjusted_subcell_line_thickness&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;subcell_line_thickness&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;float4&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;distance_to_subcell&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;adjusted_subcell_line_thickness&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subcell_color&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="n"&gt;distance_to_cell&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;adjusted_cell_line_thickness&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;    &lt;span class="n"&gt;color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cell_color&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh9qtev9gs3mflbvmp43k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh9qtev9gs3mflbvmp43k.png" alt="Corrected Grid"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And this is how it looks with dimensions 100x100m and the actual colour:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9rwc7zalp6bpe4a1k3we.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9rwc7zalp6bpe4a1k3we.png" alt="Corrected Grid 2"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  1.3 Fade out
&lt;/h3&gt;

&lt;p&gt;This already looks pretty good, but you can see issues in the distance: mainly aliasing and &lt;a href="https://en.wikipedia.org/wiki/Moir%C3%A9_pattern" rel="noopener noreferrer"&gt;Moiré patterns&lt;/a&gt;.&lt;br&gt;
There's a lot of literature about how to fix these kind of issues, but I decided to take a different approach...&lt;/p&gt;

&lt;p&gt;...just fade out the grid around the camera so you can't see them!&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzctd4hc5atzm4ng8jznq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzctd4hc5atzm4ng8jznq.png" alt="Lazy Filtering"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;First, we need the camera position.&lt;br&gt;
Let's make the necessary changes in the vertex shader:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;

&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;VertexIn&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;float4x4&lt;/span&gt; &lt;span class="n"&gt;view_proj&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;float4&lt;/span&gt; &lt;span class="n"&gt;camera_pos&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;VertexOut&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;float4&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;position&lt;/span&gt; &lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt; &lt;span class="n"&gt;invariant&lt;/span&gt; &lt;span class="p"&gt;]];&lt;/span&gt;
    &lt;span class="n"&gt;float3&lt;/span&gt; &lt;span class="n"&gt;camera_pos&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt; &lt;span class="n"&gt;flat&lt;/span&gt; &lt;span class="p"&gt;]];&lt;/span&gt;
    &lt;span class="n"&gt;float2&lt;/span&gt; &lt;span class="n"&gt;coords&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="n"&gt;vertex&lt;/span&gt;
&lt;span class="n"&gt;VertexOut&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;uint&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="n"&gt;vertex_id&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
    &lt;span class="n"&gt;constant&lt;/span&gt; &lt;span class="n"&gt;VertexIn&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;vert_in&lt;/span&gt; &lt;span class="p"&gt;[[&lt;/span&gt;&lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;N&lt;/span&gt;&lt;span class="p"&gt;)]])&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;world_pos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;positions&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="n"&gt;world_pos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;xyz&lt;/span&gt; &lt;span class="o"&gt;*=&lt;/span&gt; &lt;span class="n"&gt;grid_size&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;position&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;vert_in&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;view_proj&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;world_pos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;camera_pos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;vert_in&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;camera_pos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;coords&lt;/span&gt;     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;world_pos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;xz&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;p&gt;Note the &lt;code&gt;[[ flat ]]&lt;/code&gt;: this is an attribute that disables interpolation, so we have the same value for all fragments.&lt;/p&gt;

&lt;p&gt;Of course, in the host you'll need to add the camera position to the buffer too.&lt;/p&gt;

&lt;p&gt;After that we calculate the horizontal distance to the camera, and use that to interpolate:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;

&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;max_fade_distance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;25.0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;...&lt;/span&gt;

&lt;span class="c1"&gt;// In the fragment shader&lt;/span&gt;
&lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;opacity_falloff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;distance_to_camera&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;frag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;coords&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;frag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;camera_pos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;xz&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;opacity_falloff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;smoothstep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;distance_to_camera&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;max_fade_distance&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;opacity_falloff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;I've found that a 25m fade radius works pretty well at a camera height of 1m, but it's too small when the camera is very high, and too big if it's very low.&lt;br&gt;
So the fade radius will now be adjusted by height, keeping that 1:25 ratio.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;

&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;constant&lt;/span&gt; &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;height_to_fade_distance_ratio&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;25.0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;...&lt;/span&gt;

&lt;span class="c1"&gt;// In the fragment shader&lt;/span&gt;
&lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;fade_distance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;frag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;camera_pos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;height_to_fade_distance_ratio&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;opacity_falloff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;smoothstep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;distance_to_camera&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;fade_distance&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;However, if the camera gets very close to the plane, that radius will approach zero, so I added a minimum radius.&lt;br&gt;
And, if the camera gets very high, you can see the shape of the quad, so I added a maximum.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;

&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;constant&lt;/span&gt; &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;min_fade_distance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;grid_size&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;constant&lt;/span&gt; &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;max_fade_distance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;grid_size&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;...&lt;/span&gt;

&lt;span class="c1"&gt;// In the fragment shader&lt;/span&gt;
&lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;fade_distance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;frag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;camera_pos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;height_to_fade_distance_ratio&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;fade_distance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fade_distance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;min_fade_distance&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;fade_distance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fade_distance&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_fade_distance&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;opacity_falloff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;smoothstep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;distance_to_camera&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;fade_distance&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Et voilà!&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8fgjioiafly5efjc07rb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8fgjioiafly5efjc07rb.png" alt="Final Grid"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Make it "infinite"
&lt;/h2&gt;

&lt;p&gt;We're almost done, but at the moment you can (eventually) move outside of the plane.&lt;br&gt;
Thankfully, it has a very easy solution, just move the plane &lt;em&gt;and the UVs&lt;/em&gt; with the camera. Think of it as a moving treadmill.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi2p9ruv7lctg6ka1lqyo.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi2p9ruv7lctg6ka1lqyo.gif" alt="Treadmill cat"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the vertex shader:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;

&lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;world_pos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;positions&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="n"&gt;world_pos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;xyz&lt;/span&gt; &lt;span class="o"&gt;*=&lt;/span&gt; &lt;span class="n"&gt;grid_size&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;world_pos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;xz&lt;/span&gt;  &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;vert_in&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;camera_pos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;xz&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;p&gt;An important detail is to ignore the Y component of the camera position. We only want the grid to move &lt;em&gt;horizontally&lt;/em&gt;, it shouldn't change when you move vertically.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Conclusions and future work
&lt;/h2&gt;

&lt;p&gt;Even though there're objectively better approaches, this solution has scratched my brain itch (for now) and provides a good enough gizmo for my purposes.&lt;/p&gt;

&lt;p&gt;The process of reinventing the wheel was a bit frustrating at times, but I'm glad I did. I wouldn't be happy having a copy-pasted piece of code that I don't understand and thus can't debug.&lt;br&gt;
I learn best by doing so it was a great exercise.&lt;/p&gt;

&lt;p&gt;Also, the process of writing this article forced me to dissect the code to be able to provide step by step examples. That uncovered a series of bugs and misunderstandings in my renderer and the shader itself. As a result, the version explained here is significantly different from the first one I wrote.&lt;/p&gt;

&lt;p&gt;In terms of future work, there's still a bunch of stuff to be done:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fix the fade to black in the distance&lt;/li&gt;
&lt;li&gt;Actually filter the grid instead of using that hacky fade&lt;/li&gt;
&lt;li&gt;Colour the X/Z axis&lt;/li&gt;
&lt;li&gt;Add a Y axis at 0,0?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, at the time of writing this, neither sound very fun, and I have a loong list of other features that do.&lt;br&gt;
At the end of the day, this is a toy project and its sole purpose is to have fun while I experiment and learn.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmdgo1c05ufhts9rrvk0u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmdgo1c05ufhts9rrvk0u.png" alt="This one doesn't spark joy"&gt;&lt;/a&gt;&lt;br&gt;
So I consider it done (for now).&lt;/p&gt;




&lt;h2&gt;
  
  
  📚 References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://bgolus.medium.com/the-best-darn-grid-shader-yet-727f9278b9d8" rel="noopener noreferrer"&gt;The Best Darn Grid Shader (Yet)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.aclockworkberry.com/shader-derivative-functions/" rel="noopener noreferrer"&gt;An introduction to shader derivative functions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://registry.khronos.org/OpenGL-Refpages/gl4/html/mod.xhtml" rel="noopener noreferrer"&gt;GLSL's mod&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-fmod" rel="noopener noreferrer"&gt;HLSL's fmod&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.apple.com/metal/Metal-Shading-Language-Specification.pdf" rel="noopener noreferrer"&gt;MSL spec&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>graphics</category>
      <category>tutorial</category>
      <category>shader</category>
      <category>metal</category>
    </item>
    <item>
      <title>🌄 Rendering a Screen-space Skybox</title>
      <dc:creator>Javier Salcedo</dc:creator>
      <pubDate>Thu, 27 Apr 2023 22:18:09 +0000</pubDate>
      <link>https://forem.com/javiersalcedopuyo/rendering-a-screen-space-skybox-57ca</link>
      <guid>https://forem.com/javiersalcedopuyo/rendering-a-screen-space-skybox-57ca</guid>
      <description>&lt;p&gt;&lt;em&gt;NOTE: This won't be an article that goes very deep into details, it's more a way to document how I implemented the skybox in my renderer. I use Metal, but it should translate to other APIs almost 1-to-1.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;When I decided to implement a skybox on &lt;a href="https://github.com/javiersalcedopuyo/Talos3D" rel="noopener noreferrer"&gt;my renderer&lt;/a&gt; my first impulse was to follow &lt;a href="https://learnopengl.com/Advanced-OpenGL/Cubemaps" rel="noopener noreferrer"&gt;the ol' reliable LearnOpenGL article on cubemaps&lt;/a&gt;, which essentially consists on rendering a giant cube around the camera.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flearnopengl.com%2Fimg%2Fadvanced%2Fcubemaps_sampling.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flearnopengl.com%2Fimg%2Fadvanced%2Fcubemaps_sampling.png" alt="LearnOpenGL cubemap example"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's a simple and very intuitive approach but I wanted something a wee bit fancier. &lt;br&gt;
Also, this technique requires uploading a lot of data (all the cube's vertices, a new view matrix so it "stays in place" as you move the camera, etc) which wouldn't be necessary on a screen-space approach.&lt;/p&gt;

&lt;h2&gt;
  
  
  📺 Screen-space implementation
&lt;/h2&gt;

&lt;p&gt;For those unfamiliar with the term, screen-space basically means that we render onto a plane (normally a quad or a giant triangle) that covers the whole screen.&lt;br&gt;
If you want to be super efficient you can use a giant triangle but I opted for a quad because it was simpler to set up (I can hardcode the output vertex positions as the corners of the  normalised device coordinates space at the near plane).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Funzm532489tg3ifxv1s2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Funzm532489tg3ifxv1s2.png" alt="Screen-Space geometry"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The fragment shader will sample the cube texture with the view direction &lt;strong&gt;in world space&lt;/strong&gt;.&lt;br&gt;
To get it, I simply multiply the &lt;strong&gt;vertex&lt;/strong&gt; position by the inverse view and projection matrices and let the GPU interpolate it.&lt;/p&gt;

&lt;p&gt;Normally, in vertex shader we go from object to world, to camera, and finally to clip/device space, but in this case we already have the device coordinates and we need the world's.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hlsl"&gt;&lt;code&gt;

&lt;span class="c1"&gt;// So instead of doing:&lt;/span&gt;
&lt;span class="n"&gt;device_space_pos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;projection&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;view&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;objec_space_pos&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// We need the inverse transformation, which implies multiplying by the inverse&lt;/span&gt;
&lt;span class="c1"&gt;// matrices, in reverse order:&lt;/span&gt;
&lt;span class="n"&gt;world_space_pos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;inverse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;inverse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;projection&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;device_space_pos&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;🚀 Performance tip: Inverting a matrix is an expensive operation, but we can take a shortcut.&lt;br&gt;
Orthonormal matrices (matrices that don't change the relative position between points) have the convenient property that their inverse is the same as its transpose, which is way easier to compute.&lt;br&gt;
Thankfully, the view matrix is orthonormal, so we can save some work there. However, the same doesn't apply to the projection matrix and it has to be inverted "properly".&lt;br&gt;
ℹ️ You could precompute these inverse matrices on the CPU instead of re-calculating them for every vertex, but since there're only 4 vertices (potentially 3), the cost of uploading and binding the extra matrices would probably be higher.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If we render the skybox the first thing in the pass, that'd be it. The skybox will "clear" the render target and everything else would go on top (just remember to disable depth write).&lt;br&gt;
However I wanted to go further and optimise it a bit more.&lt;/p&gt;

&lt;h2&gt;
  
  
  🥱 Avoiding work
&lt;/h2&gt;

&lt;p&gt;I got the idea from this reply to one of my tweets:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F66xl0bsqcp49owralvyl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F66xl0bsqcp49owralvyl.png" alt="Embedded tweets don't seem to work"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It makes a lot of sense!&lt;/p&gt;

&lt;p&gt;My test scene only has 3 objects at the moment: the floor, a Stanford Bunny and a Utah Teapot, but a "real" environment will have a lot of things on the screen, and the skybox will take only a small percentage of the render target. Why would we want to calculate the skybox for every single fragment if most of them will get overwritten anyway? And keep in mind that we are doing 2 expensive operations here: inverting a matrix (vertex stage) and &lt;strong&gt;sampling a cubemap (fragment stage)&lt;/strong&gt;.&lt;br&gt;
A stencil is the perfect fit.&lt;/p&gt;

&lt;p&gt;In this case we don't need anything fancy, we just need to know if that fragment is "free". So what I did was:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Move the skybox draw call to &lt;strong&gt;the end&lt;/strong&gt; of the scene pass.&lt;/li&gt;
&lt;li&gt;For every scene object, increment the stencil when a fragment is drawn.&lt;/li&gt;
&lt;li&gt;When rendering the skybox, only draw the fragments that still have a stencil of 0.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is how my stencil looks at the end of the pass:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzijunmkzqmdro8w5gica.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzijunmkzqmdro8w5gica.png" alt="Stencil buffer"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The skybox fragment shader will only run for the pixels in black, saving a lot of computing.&lt;/p&gt;

&lt;p&gt;To achieve this in the code I had to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Change the Z buffer format to include stencil&lt;/li&gt;
&lt;li&gt;Create 2 separated stencil states. One for the scene rendering, and another for the skybox.
```swift
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;mainStencilDesc.depthStencilPassOperation   = .incrementClamp&lt;br&gt;
mainStencilDesc.stencilCompareFunction      = .always&lt;/p&gt;

&lt;p&gt;// Only render the skybox for the fragments that have the same stencil value as the reference&lt;br&gt;
skyboxStencilDesc.stencilCompareFunction    = .equal&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;2.1 BONUS: Disable depth writing and testing for the skybox. It's not 100% necessary because the quad will be so close to the camera that it'll always pass, but it has a cost that we can easily avoid.
```swift


skyboxDSDesc.frontFaceStencil       = skyboxStencilDesc
skyboxDSDesc.isDepthWriteEnabled    = false
skyboxDSDesc.depthCompareFunction   = .always


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ol&gt;
&lt;li&gt;Swap the states and set the reference value
```swift
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;...&lt;br&gt;
// All other scene draw call encoding&lt;br&gt;
...&lt;br&gt;
cmdEncoder.setDepthStencilState(skyboxDepthStencilState)&lt;br&gt;
cmdEncoder.setStencilReferenceValue(0);&lt;br&gt;
// Encode the skybox draw call&lt;br&gt;
...&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
Et voilà! 

![Demo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5fve9r5180rfs8gzywun.gif)

---

## 🧑‍🏫 Conclusions
### 😄 The good
- Pretty simple to implement.
- Ridiculously small bandwidth cost: This approach only requires us to upload the cubemap and 4 vertices (3 if we used a triangle) that don't really need to carry any information. We could even just upload a point and generate the quad in the geometry stage. The cubemap can (and will) be used for other techniques so it's not really an extra cost and it can stay bound, it uses the same view and projection matrices so we don't need to bind any, and it uses the same attachments as the rest of the scene so it can stay in the same pass.
- The most expensive part (cubemap sampling) is only done where it's stricly necessary.

### 😭 The bad
- Not as "intuitive" as the giant cube approach?

### 👹 The ugly
- I spent more time than what I'm proud to admit debugging a "lense distortion" and doubting my math and graphics knowledge. Turns out it was caused by me "inverting" the projection matrix by transposing it, despite it not being orthonormal.
- Managing the depth-stencil state can be annoying in some graphic APIs?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>rendering</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Rendering Shadows in Real-Time Applications 2: Shadow Maps</title>
      <dc:creator>Javier Salcedo</dc:creator>
      <pubDate>Sun, 05 Feb 2023 21:06:23 +0000</pubDate>
      <link>https://forem.com/javiersalcedopuyo/rendering-shadows-in-real-time-applications-2-shadow-maps-bnn</link>
      <guid>https://forem.com/javiersalcedopuyo/rendering-shadows-in-real-time-applications-2-shadow-maps-bnn</guid>
      <description>&lt;p&gt;In the &lt;a href="https://dev.to/javiersalcedopuyo/rendering-shadows-in-real-time-applications-1-static-shadows-58e4"&gt;previous article&lt;/a&gt;, I gave a basic explanation on how static shadows can be done in real-time applications.&lt;br&gt;
Now it's the turn for dynamic shadows.&lt;/p&gt;

&lt;p&gt;This article will only cover the basics but, since it lays the foundations for more complex techniques, it'll be a bit longer and more in depth.&lt;/p&gt;
&lt;h2&gt;
  
  
  Dynamic Shadows
&lt;/h2&gt;

&lt;p&gt;All dynamic shadow maps are essentially the same technique, simply changing the number of maps, the positioning of the lights’ views and the blending between maps (if there’re more than 1), but they have different use cases and greatly vary in complexity.&lt;/p&gt;

&lt;p&gt;They also share the same limiting fact: each shadow-casting light source will need to render and apply its own shadow map.&lt;br&gt;
This greatly limits the number of shadow-casting lights present in a scene, since each one implies rendering the scene [1,N] times from different viewpoints. This makes them a good fit for other techniques such as bindless rendering.&lt;/p&gt;
&lt;h2&gt;
  
  
  2D Shadow Maps
&lt;/h2&gt;

&lt;p&gt;The main problem on rasterised real-time applications regarding shadows is that, at a given fragment, we don’t know anything else about the rest of the object or the scene so initially it’s not possible to know if the fragment is illuminated or not.&lt;/p&gt;

&lt;p&gt;A smart way to bypass this limitation of information is to check against what the light “sees”:&lt;br&gt;
If a fragment is visible from the camera, but not from the light, it means something else is in between the light and the fragment, so (for that particular light) it’s shaded.&lt;br&gt;
But, how do we know that?&lt;/p&gt;

&lt;p&gt;We can easily transform the fragment’s position to the light’s coordinate space if we create a view matrix for the light. The z/depth of that transformed fragment would be the depth from the light if nothing was in between the two.&lt;br&gt;
If we now sample the light’s depth buffer at that fragment’s coordinates and it’s smaller we know something else got rendered on top of it, so it’s shaded.&lt;/p&gt;

&lt;p&gt;Of course, this means we need to render the scene from the light’s perspective first, so we can get the depth buffer. This is the reason why this technique can get very expensive very quickly if we have too many shadow-casting lights.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1ao6ioat6s5u2avut52e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1ao6ioat6s5u2avut52e.png" alt="Diagram showing the cases of a directional light (left) and a spot light (right)" width="800" height="620"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is a perfect technique for spot lights like flashlights, headlights or floodlights but it also works for directional lights, as long as the covered area is small.&lt;/p&gt;

&lt;p&gt;The general steps to follow are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Create a new texture and bind it as the depth attachment of a new render pass&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Bind the light matrix as the view and a perspective or orthogonal matrix as the projection (it’ll be perspective or orthogonal depending on the type of light). &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Render the scene. We can skip non-shadow-casting objects and the fragment stage, since we only want the depth buffer. (Keep in mind some objects like cloud billboards might need to have back-face culling disabled or flipped) &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Bind light’s view and projection matrices (you can pre-multiply them) as/inside a buffer for the vertex stage.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Bind the now filled depth buffer as a regular texture for the fragment stage. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Render the scene normally with the following additions to the shaders:&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In the &lt;strong&gt;vertex shader&lt;/strong&gt;, the output now must include the transformed position of the vertex.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;vertexOut&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PositionInLightSpace&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lightViewProj&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;vertex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;position&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;strong&gt;fragment shader&lt;/strong&gt; on the other hand will use the interpolated position in light clip space to sample the shadow map.&lt;br&gt;
First, the position needs to be normalised. That requires:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Transforming it into device space by dividing it by the w component (perspective divide)&lt;/li&gt;
&lt;li&gt;Transform it into the [0,1] range (this will depend on the projection matrix and device coordinate space chosen)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Finally, the x and y components can be used to sample the shadow map, and the z component can be used to compare agains that result. If the fragment depth (z component) is greater than the shadow map’s it means the fragment was not visible from the light, and thus it’s in the shade.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="nf"&gt;ComputeShadow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;float4&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;texture2d&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;float&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;shadowMap&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;constexpr&lt;/span&gt; &lt;span class="n"&gt;sampler&lt;/span&gt; &lt;span class="n"&gt;smp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;min_filter&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;linear&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                          &lt;span class="n"&gt;mag_filter&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;linear&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                          &lt;span class="n"&gt;s_address&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;clamp_to_border&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                          &lt;span class="n"&gt;t_address&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;clamp_to_border&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Transform from clip to device coordinate system.&lt;/span&gt;
    &lt;span class="c1"&gt;// Orthographic projection matrices don't need this.&lt;/span&gt;
    &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;lightCoords&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;position&lt;/span&gt; &lt;span class="o"&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;w&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Normalise the coordinates.&lt;/span&gt;
    &lt;span class="c1"&gt;// Depends on the projection matrix and device coordinate&lt;/span&gt;
    &lt;span class="c1"&gt;// system.&lt;/span&gt;
    &lt;span class="n"&gt;lightCoords&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;xy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lightCoords&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;xy&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;closestDepth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;shadowMap&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sample&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;smp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lightCoords&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;xy&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;currentDepth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lightCoords&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;currentDepth&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;closestDepth&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&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;h2&gt;
  
  
  Problems
&lt;/h2&gt;

&lt;p&gt;As it is, this presents an issue known as &lt;strong&gt;shadow acne&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe8htbcos44tc5w1pg6sb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe8htbcos44tc5w1pg6sb.png" alt="Example of shadow acne" width="800" height="675"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Shadow acne happens when a single texel of the shadow map covers multiple fragments at the same time. This means they all will compare against a single depth value. That means the surface’s orientation relative to the light can cause some fragments to have different depths for the same shadow map texel, causing the distinct shadow patters.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fduzznwx2ek8evzsu27jk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fduzznwx2ek8evzsu27jk.png" alt="Diagram explaining how shadow acne occurs (Lengyel, 2019)[3]" width="590" height="410"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can solve this pushing the shadow map’s depth away from the light by adding a &lt;em&gt;bias&lt;/em&gt; to &lt;code&gt;closestDepth&lt;/code&gt; but, if pushed too far, the shadow can appear “detached” from the object. We call that &lt;strong&gt;peter-panning&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3h8dfuu8i4ime0un9fxn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3h8dfuu8i4ime0un9fxn.png" alt="Example of peter-panning" width="800" height="669"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A hardcoded bias however is never going to work for all cases, since it depends on the relative orientation of a fragment with the light. For that reason, multiple techniques to adapt the depth bias to the geometry exist, from simply adjust it according to the angle between the light and normal vectors, to more complex techniques like Receiver Plane Depth Bias (&lt;em&gt;Isidoro J.R, 2006[4]&lt;/em&gt;) that uses derivatives to calculate how much the depth changes in the neighbouring fragments, or Adaptative Depth Bias (&lt;em&gt;Ehm et al., 2014[5]&lt;/em&gt;).&lt;/p&gt;

&lt;p&gt;Thankfully nowadays APIs provide ways to adapt the depth bias automatically, depending on the gradient of the primitives.&lt;br&gt;
For example, in Metal these values perform quite well:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="n"&gt;commandEncoder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setDepthBias&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;slopeScale&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;clamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;128&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is applied while rendering the shadow map, so no bias has to be added to the fragment shader.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzm6tzx3chwc94jtvs6i2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzm6tzx3chwc94jtvs6i2.png" alt="Example of the result of choosing an appropriate depth bias" width="800" height="649"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This technique as presented here produces hard shadows. Soft shadows techniques require sampling multiple times the shadow map, which of course will add a certain performance cost depending on the complexity of the filtering done.&lt;br&gt;
This will be covered in future articles.&lt;/p&gt;

&lt;p&gt;The main disadvantage of this technique however is that the greater the area lit by the light source, the bigger the shadow map should be. Otherwise, we’ll miss small or far away objects, and the rest of the shadows will lose sharpness.&lt;br&gt;
It’s easy to see how this can become a big issue in terms of memory and bandwidth.&lt;br&gt;
This is particularly bad for directional lights like the Sun when the scene is big. In that case, it’s better to use Cascaded Shadow Maps.&lt;/p&gt;

&lt;p&gt;I’d choose this technique for the player's in a first person game, headlights of vehicles, or other spot light sources like floodlights or searchlights.&lt;br&gt;
For directional lights I’d avoid this as a rule of thumb, but it could be used as the sun if the scene is relatively small (or the player’s view distance is limited), or maybe as an approximation for a big area light.&lt;/p&gt;




&lt;h2&gt;
  
  
  📚 References
&lt;/h2&gt;

&lt;p&gt;[3] Lengyel, E. (2019), Foundations of Game Engine Development: Rendering, Terathon Software LLC&lt;br&gt;
[4] Isidoro J. R. (2006), Shadow Mapping GPU-based Tips and Techniques, GDC 2006&lt;br&gt;
[5] Ehm, A., Ederer, A., Klein, A., &amp;amp; Nischwitz, A. (2015). Adaptive depth bias for soft shadows&lt;/p&gt;

&lt;p&gt;📷 Photo by &lt;a href="https://unsplash.com/@octoberroses?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Brie Odom-Mabey&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/rCx_m5bcDgk?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>gamedev</category>
      <category>graphics</category>
      <category>rendering</category>
      <category>programming</category>
    </item>
    <item>
      <title>Rendering Shadows in Real-Time Applications 1: Static Shadows</title>
      <dc:creator>Javier Salcedo</dc:creator>
      <pubDate>Thu, 02 Feb 2023 21:24:58 +0000</pubDate>
      <link>https://forem.com/javiersalcedopuyo/rendering-shadows-in-real-time-applications-1-static-shadows-58e4</link>
      <guid>https://forem.com/javiersalcedopuyo/rendering-shadows-in-real-time-applications-1-static-shadows-58e4</guid>
      <description>&lt;p&gt;Rendering shadows on Real Time Applications presents distinct challenges compared to their offline counterparts. On Offline ray/path-traced applications, shadows come “for free” as part of the rendering algorithm but for real time we normally need to rely on physically inaccurate, multi-pass techniques.&lt;/p&gt;

&lt;p&gt;On this series of articles, I'll explain different ways we can achieve this.&lt;/p&gt;

&lt;p&gt;I would classify the most common techniques in the following way, depending on use case and type of light:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Static

&lt;ul&gt;
&lt;li&gt;Baked Light Maps&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Dynamic

&lt;ul&gt;
&lt;li&gt;Shadow Maps

&lt;ul&gt;
&lt;li&gt;Directional and spot lights

&lt;ul&gt;
&lt;li&gt;Single&lt;/li&gt;
&lt;li&gt;Cascaded&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Point Lights&lt;/li&gt;
&lt;li&gt;Cube&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Stencil Shadows&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In any case, all these techniques involve generating one or multiple textures with the shadow information that will be sampled in the lighting pass to determine if a fragment is in shadow or not.&lt;/p&gt;

&lt;p&gt;Let's start with static shadows.&lt;/p&gt;

&lt;h2&gt;
  
  
  Baked Light Maps
&lt;/h2&gt;

&lt;p&gt;If we know for sure that the light sources and the shadow-casting objects are not going to change, we can use the power and simplicity of our offline cousins and create a baked light map.&lt;br&gt;
This would have the extra benefit of providing high quality lighting information as well, although we could bake just the shadows if we want to.&lt;/p&gt;

&lt;p&gt;To bake a light map we ray/path trace the scene from each light, adding the contribution of each ray to one or many textures that map every static object of the scene, and will be stored on disk after we’re finished.&lt;br&gt;
Later, when we render the scene, for each static object, we simply sample the texture(s).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5d0ua76yi13htn61z50y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5d0ua76yi13htn61z50y.png" alt="Example of a baked light map from Unity’s online documentation (1)" width="800" height="667"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This of course would require us to organise the scene into a hierarchy that allows not only the ray/path tracing algorithm to work, but also to map each static surface to a unique region of the texture, and in a way that the texture coordinates can be retrieved during rendering.&lt;br&gt;
This technique will provide the highest quality and most physically correct shadows as we’re simulating the light rays bounces around the scene, but won’t be applied to moving or changing objects, which can create visually incoherent issues if not handled properly.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcdqfmif8x77a1ed4fdwf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcdqfmif8x77a1ed4fdwf.png" alt="Example of a broken lightbulb still illuminating and casting shadows in Battlefield 4 (2)" width="780" height="803"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This technique is also the cheapest at runtime, since it only requires sampling a single texture.&lt;/p&gt;

&lt;p&gt;It could also be combined with other real time techniques to be able to use dynamic light sources such as flashlights or vehicle headlights, or to add shadows for moving objects.&lt;/p&gt;

&lt;p&gt;I’d choose this technique if:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The environment and main shadow-casting light sources are static.&lt;/li&gt;
&lt;li&gt;You want the highest quality and most realistic shadows possible.&lt;/li&gt;
&lt;li&gt;You want to reduce the number of real-time shadow-casting light sources while keeping the shadows they cast on the environment.&lt;/li&gt;
&lt;li&gt;And/Or you want a high number of shadow-casting lights&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An example of an &lt;em&gt;ideal&lt;/em&gt; game for this technique would be a photo-realistic first person walking simulator in a space ship.&lt;/p&gt;

&lt;p&gt;I’d avoid it if:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You have a changing environment:

&lt;ul&gt;
&lt;li&gt;Trees moving with the wind&lt;/li&gt;
&lt;li&gt;Destructible structures&lt;/li&gt;
&lt;li&gt;User-built structures&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;The shadow-casting light sources change color, position or intensity over time:

&lt;ul&gt;
&lt;li&gt;Day-night cycle&lt;/li&gt;
&lt;li&gt;Flickering or switchable lights&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An example of a &lt;em&gt;worst case&lt;/em&gt; game for this technique would be a cartoonish real time strategy game with a day-night cycle.&lt;/p&gt;




&lt;h2&gt;
  
  
  📚 References
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;“Lightmapping: Getting Started” (Unity docs) - &lt;a href="https://docs.unity3d.com/2018.2/Documentation/Manual/Lightmapping.html"&gt;https://docs.unity3d.com/2018.2/Documentation/Manual/Lightmapping.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Battlefield 4 example: “What is Baked Lighting? (Lightmap)”: &lt;a href="https://www.youtube.com/watch?v=eGmXDOGjKPA"&gt;https://www.youtube.com/watch?v=eGmXDOGjKPA&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;📷 Photo by &lt;a href="https://unsplash.com/@marusyaionova?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Maria Ionova&lt;/a&gt; on &lt;a href="https://unsplash.com/photos/rHVLapr8nZY?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
      <category>rendering</category>
      <category>graphics</category>
      <category>programming</category>
      <category>gamedev</category>
    </item>
    <item>
      <title>Replicating Swift's Enumeration Associated Values in C++</title>
      <dc:creator>Javier Salcedo</dc:creator>
      <pubDate>Wed, 21 Dec 2022 21:44:52 +0000</pubDate>
      <link>https://forem.com/javiersalcedopuyo/replicating-swifts-enumeration-associated-values-in-c-4hc7</link>
      <guid>https://forem.com/javiersalcedopuyo/replicating-swifts-enumeration-associated-values-in-c-4hc7</guid>
      <description>&lt;p&gt;Lately I'm working on a Metal engine as a hobby project.&lt;br&gt;
I work with Metal in my day job and it has become my favourite  graphics API in terms of developer experience.&lt;br&gt;
At home I wanted the full &lt;em&gt;"Apple Experience"&lt;/em&gt; so I decided to use Swift instead of the usual suspects (C/C++, Rust...).&lt;/p&gt;

&lt;p&gt;I've never worked with Swift before so it's all new. Coming from C++ I feel a bit constrained from time to time (I still heavily dislike the way structs and classes work), but so far I'd describe it as a &lt;em&gt;"comfy"&lt;/em&gt; language. It's easy to work with, the syntax is simple (most of the time), the tooling is pretty good, and it has &lt;strong&gt;a lot&lt;/strong&gt; of syntax sugar.&lt;/p&gt;

&lt;p&gt;One example of syntax sugar that I love is they way you can add arbitrary values to enums, officially called &lt;strong&gt;Associated Values&lt;/strong&gt;.&lt;br&gt;
I love it because it allows me to simplify APIs and reduce boiler plate a lot.&lt;/p&gt;

&lt;p&gt;Let's see a simplified example from my project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;PI&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Float&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;3.1416&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;TAU&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="kt"&gt;PI&lt;/span&gt;

&lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="kt"&gt;Vec3&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;z&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Float&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;// Imagine there's vector math here&lt;/span&gt;

&lt;span class="kd"&gt;enum&lt;/span&gt; &lt;span class="kt"&gt;Axis&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="kt"&gt;X&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Z&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="kt"&gt;CUSTOM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Vec3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;struct&lt;/span&gt; &lt;span class="kt"&gt;Transform&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;rotate_around&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;axis&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Axis&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;radians&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Float&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;the_axis&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Vec3&lt;/span&gt;
        &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="n"&gt;axis&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;X&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;the_axis&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Vec3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;z&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;the_axis&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Vec3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;z&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Z&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;the_axis&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Vec3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;z&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;CUSTOM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;a&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="n"&gt;the_axis&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Rotating &lt;/span&gt;&lt;span class="se"&gt;\(&lt;/span&gt;&lt;span class="n"&gt;radians&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="s"&gt; radians around the(&lt;/span&gt;&lt;span class="se"&gt;\(&lt;/span&gt;&lt;span class="n"&gt;the_axis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="se"&gt;\(&lt;/span&gt;&lt;span class="n"&gt;the_axis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="s"&gt;. &lt;/span&gt;&lt;span class="se"&gt;\(&lt;/span&gt;&lt;span class="n"&gt;the_axis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="s"&gt;) axis"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;// Do math stuff with the axis&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Transform&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="cm"&gt;/* ctr arguments*/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rotate_around&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;axis&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;X&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;radians&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.25&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="kt"&gt;TAU&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rotate_around&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;axis&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;radians&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;  &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="kt"&gt;TAU&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rotate_around&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;axis&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;Z&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;radians&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.75&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="kt"&gt;TAU&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;my_axis&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Vec3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.3333&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;z&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.6667&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rotate_around&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;axis&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kt"&gt;CUSTOM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_axis&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nv"&gt;radians&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;TAU&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output of which is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Rotating 1.5708 radians around the(1.0, 0.0. 0.0) axis&lt;br&gt;
Rotating 3.1416 radians around the(0.0, 0.0. 0.0) axis&lt;br&gt;
Rotating 4.7124 radians around the(0.0, 0.0. 1.0) axis&lt;br&gt;
Rotating 6.2832 radians around the(0.0, 0.3333. 0.6667) axis&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Look at how nice and simple the implementation of &lt;code&gt;rotate_around&lt;/code&gt; is! ❤️&lt;/p&gt;

&lt;p&gt;It was just a matter of time until I encountered something at work that could use a feature like this. And that moment came today.&lt;br&gt;
At work we use C++, and enums there are not this complex, so APIs like this can get a lot of boilerplate to get them working.&lt;br&gt;
But thankfully, this line in &lt;a href="https://docs.swift.org/swift-book/LanguageGuide/Enumerations.html"&gt;Swift's docs&lt;/a&gt; made me realise there's a way to get a similar thing working on C++:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Enumerations similar to these are known as discriminated unions, tagged unions, or variants in other programming languages.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;Variants&lt;/em&gt;!&lt;br&gt;
So I went on and drafted a quick C++ alternative to my Swift code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;iostream&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;variant&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="k"&gt;constexpr&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;PI&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;3.1416&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;constexpr&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;TAU&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;PI&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;Vec3&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt; &lt;span class="c1"&gt;// Imagine there's vector math here&lt;/span&gt;

&lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PredefinedAxis&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Z&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="n"&gt;CustomAxis&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Vec3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="n"&gt;Axis&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;variant&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;PredefinedAxis&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CustomAxis&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;Transform&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;rotate_around_axis&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Axis&lt;/span&gt; &lt;span class="n"&gt;axis&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;float&lt;/span&gt; &lt;span class="n"&gt;radians&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Vec3&lt;/span&gt; &lt;span class="n"&gt;the_axis&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;auto&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;predefined&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;get_if&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;PredefinedAxis&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&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;axis&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;predefined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;PredefinedAxis&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;the_axis&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Vec3&lt;/span&gt;&lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt; &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;PredefinedAxis&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;the_axis&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Vec3&lt;/span&gt;&lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt; &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;PredefinedAxis&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Z&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;the_axis&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Vec3&lt;/span&gt;&lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;z&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt; &lt;span class="k"&gt;break&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;span class="k"&gt;else&lt;/span&gt; &lt;span class="nf"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;auto&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;custom&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;get_if&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CustomAxis&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&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;axis&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;the_axis&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;custom&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// We shouldn't get here!!&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;cout&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;"Rotating "&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;radians&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;" radians around the ("&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;the_axis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;", "&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;the_axis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;", "&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;the_axis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;z&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s"&gt;") axis"&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;endl&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="c1"&gt;// Do math stuff with the axis&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Transform&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* ctr arguments */&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rotate_around_axis&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PredefinedAxis&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;X&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.25&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;TAU&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rotate_around_axis&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PredefinedAxis&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;  &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;TAU&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rotate_around_axis&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PredefinedAxis&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Z&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.75&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;TAU&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;my_axis&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Vec3&lt;/span&gt;&lt;span class="p"&gt;{.&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.3333&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;z&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.6667&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rotate_around_axis&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_axis&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TAU&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;p&gt;Which outputs:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Rotating 1.5708 radians around the (1, 0, 0) axis&lt;br&gt;
Rotating 3.1416 radians around the (0, 1, 0) axis&lt;br&gt;
Rotating 4.7124 radians around the (0, 0, 1) axis&lt;br&gt;
Rotating 6.2832 radians around the (0, 0.3333, 0.6667) axis&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Of course, this being C++ it's more verbose and it feels a bit &lt;em&gt;hacky&lt;/em&gt; (especially the &lt;code&gt;using&lt;/code&gt; part), but it works!&lt;br&gt;
The implementation of &lt;code&gt;rotate_around_axis&lt;/code&gt; is messier, but I actually like the &lt;em&gt;use&lt;/em&gt; of it way better!&lt;/p&gt;

&lt;p&gt;I look forward to learning more cool features from Swift that I can copy in C++!&lt;/p&gt;

</description>
      <category>swift</category>
      <category>cpp</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Pet projects shouldn't be optimal</title>
      <dc:creator>Javier Salcedo</dc:creator>
      <pubDate>Mon, 23 May 2022 20:16:03 +0000</pubDate>
      <link>https://forem.com/javiersalcedopuyo/pet-projects-shouldnt-be-optimal-84o</link>
      <guid>https://forem.com/javiersalcedopuyo/pet-projects-shouldnt-be-optimal-84o</guid>
      <description>&lt;p&gt;Over the years I started noticing a pattern in my life. It goes somewhat like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Find new hobby / skill / technology.&lt;/li&gt;
&lt;li&gt;Start working on it and notice progress.&lt;/li&gt;
&lt;li&gt;Become obsessed with it, read articles, watch videos, listen to podcasts, etc.&lt;/li&gt;
&lt;li&gt;Start worrying about the optimal way to get results or the best practices.&lt;/li&gt;
&lt;li&gt;It becomes a chore and I end up not working on it anymore.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I first noticed it with powerlifting.&lt;br&gt;
I started going to the gym with my friends, having a good time and becoming really passionate about it.&lt;br&gt;
After a couple of years I decided to join a club and start competing.&lt;br&gt;
A year later I was working out for hours every day by myself, following strict and hard programs to add every pound I could to my maxs, and barely going out because I could feel my performance dropping if I skipped the diet.&lt;br&gt;
In the end I left the club and powerlifting entirely. It took me more than a year to start truly enjoying it again.&lt;br&gt;
However, it didn't click in my head. I blamed the long sessions, or the strict diet, or the insufficient results I was getting.&lt;/p&gt;

&lt;p&gt;And of course, a similar thing happened when I started my first rendering engine.&lt;br&gt;
I had a bit of experience with OpenGL but I decided to go all in with C++ and Vulkan.&lt;br&gt;
I got pretty far pretty quickly, but eventually the time and mental cost of dealing with such low level tools led me to give up.&lt;/p&gt;

&lt;p&gt;But then, a while I go I saw a tweet (sadly I haven't been able to find it again) about how &lt;em&gt;JavaScript&lt;/em&gt; of all languages is actually used in computer graphics research.&lt;/p&gt;

&lt;p&gt;Anyone who has worked with computer graphics knows that performance is &lt;strong&gt;everything&lt;/strong&gt;. C++ is king, with Rust trying to make a breaktrhough. So this was mindblowing to me. &lt;em&gt;JavaScript?? For real time graphics?? Really??&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The reasoning however was quite simple: When you are doing research, trying to develop new techniques, you don't really care about performance. You care about &lt;strong&gt;relative&lt;/strong&gt; performance compared to other techniques.&lt;br&gt;
What is vital though is &lt;strong&gt;development&lt;/strong&gt; speed. How quickly you can iterate and add new features. And languages like JavaScript or Python are perfect for that.&lt;/p&gt;

&lt;p&gt;And I believe the same applies to hobby projects (as long as the point is not precisely to get the most optimised solution you can, obviously).&lt;/p&gt;

&lt;p&gt;So forget about finding the perfect language, API or framework for your pet projects. Focus on choosing one that you &lt;strong&gt;enjoy&lt;/strong&gt; working with, and start coding.&lt;/p&gt;




&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@anniespratt?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Annie Spratt&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/art-workshop?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>My best daily dev tool</title>
      <dc:creator>Javier Salcedo</dc:creator>
      <pubDate>Sat, 21 Aug 2021 17:29:37 +0000</pubDate>
      <link>https://forem.com/javiersalcedopuyo/my-best-daily-dev-tool-1io8</link>
      <guid>https://forem.com/javiersalcedopuyo/my-best-daily-dev-tool-1io8</guid>
      <description>&lt;p&gt;I like to travel and work light. When I'm choosing tools, languages or frameworks, I like to be able to work with minimal setups. The less things I need to use or learn, the better.&lt;/p&gt;

&lt;p&gt;And that's why my favourite tool that I use on a day to day basis is &lt;strong&gt;my tablet&lt;/strong&gt;.&lt;br&gt;
It allows me to merge a lot of other things into a single device that I can carry around and takes almost no space.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frj0c5swaico207tmymed.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frj0c5swaico207tmymed.png" alt="image" width="800" height="778"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  📓 Notebook
&lt;/h2&gt;

&lt;p&gt;This is most basic tool for any programmer. Period.&lt;br&gt;&lt;br&gt;
If you are not taking notes of &lt;em&gt;everything&lt;/em&gt; already I don't know what are you waiting for, go get yourself a cheap notebook and a pen right now.&lt;/p&gt;

&lt;p&gt;But if you're looking for a cleaner and more flexible alternative, a tablet + pen combo is the best.&lt;br&gt;
It can do all a regular notebook can + allowing you to organise, clean up and backup your notes (and you can also use multiple colours, add pics, etc).&lt;/p&gt;

&lt;p&gt;I personally use &lt;a href="https://www.goodnotes.com/"&gt;GoodNotes&lt;/a&gt;, and I really like the fact that it also has a desktop app.&lt;/p&gt;

&lt;h2&gt;
  
  
  🖊 Whiteboard
&lt;/h2&gt;

&lt;p&gt;Sadly, with whiteboards size matters, but it's still very useful to make quick diagrams and schemes, while being able to erase and/or modify them, use multiple colour markers, etc.&lt;/p&gt;

&lt;p&gt;There are also apps that allow you to share and present the whiteboard with others, so you can still have the whiteboard collaborative experience.&lt;/p&gt;

&lt;p&gt;Definitely a great feature during this last year and probably a must for any remote programmer.&lt;/p&gt;

&lt;h2&gt;
  
  
  📚 eBook
&lt;/h2&gt;

&lt;p&gt;As programmers, we always strive for expanding our knowledge.&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcpywywm1we5xxza3j7oi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcpywywm1we5xxza3j7oi.png" alt="image" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That means that we read a lot, and we can end up with a lot of books that we might need to consult.&lt;br&gt;
But carry them around just in case is not viable.&lt;br&gt;
Unless you have them digitalised.&lt;/p&gt;

&lt;p&gt;I still prefer paper books though, specially for technical books.&lt;br&gt;
I simply enjoy reading them more, and I like seeing my little library growing.&lt;br&gt;&lt;br&gt;
However this is all about optimising and convenience.&lt;br&gt;
And for that there's simply nothing better than having your books accessible from anywhere, all in one (digital) place.&lt;br&gt;
That's why nowadays I consider my paper books more like collection items or a treat.&lt;/p&gt;

&lt;p&gt;It also makes searching for specific stuff much faster and eBook versions of books tend to be much much cheaper.&lt;/p&gt;

&lt;h2&gt;
  
  
  📝 BONUS: Annotating papers
&lt;/h2&gt;

&lt;p&gt;I guess this could go with the eBook, but papers are normally PDFs, so you're not really replacing anything.&lt;br&gt;&lt;br&gt;
In my line of work, it's not unusual to read research papers fairly often, and being able to take notes directly over them makes it much more convenient and enjoyable.  &lt;/p&gt;

&lt;p&gt;Before having my iPad I used to &lt;em&gt;print&lt;/em&gt; the ones I was more interested on. So I guess it's also a good move for the environment. 🍃&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I use a relatively old iPad with a 1st gen Apple pencil, but obviously you don't need to get into the (expensive) Apple ecosystem. Any tablet compatible with a digital pen would work just as good (if not better).&lt;/p&gt;

&lt;p&gt;If I were to buy one right now I would probably consider getting an e-ink device (the recently announced &lt;a href="https://www.pine64.org/2021/08/15/introducing-the-pinenote/"&gt;PineNote&lt;/a&gt; looks promising.&lt;br&gt;
You give up colours, but they are generally cheaper and better for your eyes and they have less distractions such as games, social media apps, streaming services, etc.&lt;/p&gt;

&lt;p&gt;At the end of the day the best tool is the one you take the most value from, and I use my iPad &lt;strong&gt;constantly&lt;/strong&gt; both for my work as for my personal projects.&lt;/p&gt;

&lt;p&gt;So if you're looking for a tool to improve your development experience and productivity, before you spend money on a fancy keyboard, laptop, IDE license, etc; consider getting one of these.&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>programming</category>
      <category>watercooler</category>
    </item>
    <item>
      <title>💥 EXTREME documenting: My workflow</title>
      <dc:creator>Javier Salcedo</dc:creator>
      <pubDate>Sun, 18 Jul 2021 21:38:05 +0000</pubDate>
      <link>https://forem.com/javiersalcedopuyo/extreme-documenting-my-workflow-130m</link>
      <guid>https://forem.com/javiersalcedopuyo/extreme-documenting-my-workflow-130m</guid>
      <description>&lt;p&gt;Writing documentation is one of those things we all despise.&lt;br&gt;
It's boring, it's tedious and takes way too much time that we could be using to build a new feature, or to fix another bug.&lt;/p&gt;

&lt;p&gt;However, we all know too how important is to have proper documentation (and if you don't, just wait until you have to work on an old, abandoned part of the code whose authors are long gone from the company).&lt;/p&gt;

&lt;p&gt;(Dealing with legacy code is a whole other topic, but I always recommend &lt;a href="https://www.amazon.co.uk/Legacy-Code-Programmers-Toolbox-Developers-ebook/dp/B07Y6T2VN1"&gt;this book&lt;/a&gt; if you are interested in learning more.)&lt;/p&gt;

&lt;p&gt;In general, my advise is to write down &lt;em&gt;EVERYTHING&lt;/em&gt; (hence the click-baity title) and filter it later if necessary. Act like if you were going to forget everything tomorrow (because you will).&lt;/p&gt;




&lt;h2&gt;
  
  
  Note taking
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frhj1v56kzq9e9g6fbl4x.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frhj1v56kzq9e9g6fbl4x.jpeg" alt="green-chameleon-s9CC2SKySJM-unsplash" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The good old reliable. What can I say?&lt;/p&gt;

&lt;p&gt;I normally take notes by hand during my research but, after months and months, it's easy to lose them and, even if you find the appropriate ones, you will probably not understand them anymore, since you are not in the same mental space and they are likely to lack any structure.&lt;/p&gt;

&lt;p&gt;My first iteration to solve this was to buy an &lt;em&gt;iPad + iPencil&lt;/em&gt;. That way I have my notes synced and centralised, and I can rearrange / organise them.&lt;br&gt;
But of course this is still less than ideal and it still means that if I leave the company that knowledge leaves with me.&lt;/p&gt;

&lt;p&gt;Recently I added Notion to the mix, but I'll write another article about that.&lt;/p&gt;




&lt;h2&gt;
  
  
  Leverage your wiki
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwr2onr2tcxfk8ftg4wkm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwr2onr2tcxfk8ftg4wkm.png" alt="Hive mind big brains" width="700" height="525"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If a notebook is your personal knowledge hub, the wiki is your team's (or even the whole company's). It's your shared hive brain.&lt;br&gt;
Every team should have one (and if you don't, I strongly suggest you to set one up asap).&lt;/p&gt;

&lt;p&gt;It doesn't really matter if it's Confluence, Notion, or a Git repo with a lot of Markdown files (I've even read articles recommending adding a Markdown file for each source file, in the same directory), the important part is to have it easily accessible and modifiable by all team members.&lt;/p&gt;

&lt;p&gt;Equally important is to keep it &lt;strong&gt;updated&lt;/strong&gt; and &lt;strong&gt;reviewed&lt;/strong&gt;!&lt;br&gt;
I like apps like Notion, but a great advantage of simpler formats like Markdown is that you can easily integrate documenting into your usual code review pipeline.&lt;/p&gt;




&lt;h2&gt;
  
  
  Level up your ticket game
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvmiy3oslocmwtacdsv6u.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvmiy3oslocmwtacdsv6u.jpeg" alt="geeky-shots-hQ4BQ3wdHsQ-unsplash" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My next step into documenting fanaticism was a while after fixing a particularly complex bug. I had to go back to fix a new related issue and I found that I no longer remembered the inner workings of that system. The main issue was that I found the solution of that bug on an article online, and I didn't remember where.&lt;/p&gt;

&lt;p&gt;So I decided to start writing down every source of information I used, not only my own thoughts/ideas.&lt;/p&gt;

&lt;p&gt;But that turned out to be &lt;strong&gt;a lot&lt;/strong&gt; of information, so I needed a better organising system.&lt;br&gt;&lt;br&gt;
My solution was to create a Notion template for a ticket/bug with all the fields that I'm likely to need in the future.&lt;/p&gt;

&lt;h3&gt;
  
  
  Hoarding browser tabs / links
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvvqsyom176wom3yoxj75.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvvqsyom176wom3yoxj75.png" alt="Too many tabs" width="800" height="96"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We've all been there, tens of open tabs, with StackOverflow posts, blog articles, YouTube videos...&lt;br&gt;&lt;br&gt;
It can get hard to navigate, even overwhelming. And on top of that, what happens to all of that when you're done? Normally just closed and forgotten.&lt;/p&gt;

&lt;p&gt;That's why nowadays I put them into their own list on Notion.&lt;br&gt;
That way I can safely close the tabs once I read them and I know where to find them when I need them again, be it next hour or next month.&lt;br&gt;
And it keeps my browser lean and tidy too.&lt;/p&gt;

&lt;h3&gt;
  
  
  Logs
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnhz2h16grfhqrqfa5xfc.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnhz2h16grfhqrqfa5xfc.jpeg" alt="radek-grzybowski-8tem2WpFPhM-unsplash" width="800" height="341"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Logs are your friends. No matter how much loggin are you doing, you should probably do more. Period.&lt;/p&gt;

&lt;p&gt;I keep any related / suspicious logging message saved too, that way, if the bug comes back from the dead, I can check if the logs have changed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ticket postmortem
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl0djq3c5sryw3ql78nsk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl0djq3c5sryw3ql78nsk.png" alt="Soy el Homer malo" width="480" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We already do this for whole features / products, time to go down to the ticket level.&lt;/p&gt;

&lt;p&gt;All the information above can be a lot to process weeks/months down the line, so a quick summary of what you did to fix/implement it and why can save you a lot of headaches.&lt;/p&gt;




&lt;h2&gt;
  
  
  Diagrams
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fge4vki6wb262lr045g8e.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fge4vki6wb262lr045g8e.jpeg" alt="conspiracy" width="800" height="324"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Maybe it's because I'm a very visual person but for me, there's nothing better than a flow/class diagram to get me to understand how a piece of code works/is structured.  &lt;/p&gt;

&lt;p&gt;Code can sometimes be a labyrinth and diagrams are your map. A map that can be improved and updated by and passed to other team members.&lt;/p&gt;

&lt;p&gt;A lot of people like to use UML diagrams with things like PlantUML, that allows you to create diagrams through a text file that can be easily added to your version control system of choice.&lt;br&gt;&lt;br&gt;
But I personally prefer &lt;a href="https://app.diagrams.net/"&gt;Diagrams.net&lt;/a&gt; (previously known as Draw.io) because you don't need to learn a whole new syntax and you can rearrange and color code it as you please. Its save file format is basically xml so it is git-friendly too.&lt;/p&gt;




&lt;h2&gt;
  
  
  Trick yourself into writing
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1njsdv8rnr1xxphbrvyb.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1njsdv8rnr1xxphbrvyb.jpeg" alt="clint-bustrillos-7WUo_GEWGo8-unsplash" width="800" height="421"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now to the hardes part, actually doing it.&lt;/p&gt;

&lt;p&gt;We don't like to write documentation for a reason, and a super detailed and strict new system is not going to make it any easier (specially if imposed from upper management).&lt;/p&gt;

&lt;p&gt;So the most important thing, more important than the system itself, is to build the habit of documenting our work.&lt;/p&gt;

&lt;p&gt;For me, documenting is a pretty mindless chore, since I already take lots of notes during normal development. All I have left to do is recopilate and organise it. So I choose the most boring and lowest energy moment of my day to do it: the afternoon slump.&lt;br&gt;&lt;br&gt;
At that moment of the day my mental energy is non-existent and any attempt at actual thinking/reasoning will be fruitless, so copying notes into a database is easy enough to do and ramps me up into more mentally demanding work.&lt;/p&gt;




&lt;h2&gt;
  
  
  📷 Pics
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Header&lt;/strong&gt;: Photo by &lt;a href="https://unsplash.com/@rpnickson?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Roberto Nickson&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/extreme?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Unsplash&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Manual note taking&lt;/strong&gt;: Photo by &lt;a href="https://unsplash.com/@craftedbygc?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Green Chameleon&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/note?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Unsplash&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Level up your ticket game&lt;/strong&gt;: Photo by &lt;a href="https://unsplash.com/@geekyshots?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Geeky Shots&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/power-up?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Hoarding browser tabs / links&lt;/strong&gt;: &lt;a href="https://www.reddit.com/r/ProgrammerHumor/comments/8mcu37/the_best_feeling_closing_all_your_tabs_after/"&gt;Reddit&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Logs&lt;/strong&gt;: Photo by &lt;a href="https://unsplash.com/@rgrzybowski?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Radek Grzybowski&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/logs?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Trick yourself&lt;/strong&gt;: Photo by &lt;a href="https://unsplash.com/@clintbustrillos?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Clint Bustrillos&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/lazy?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>productivity</category>
      <category>programming</category>
    </item>
    <item>
      <title>In defence of daily stand-ups</title>
      <dc:creator>Javier Salcedo</dc:creator>
      <pubDate>Sat, 01 May 2021 17:41:51 +0000</pubDate>
      <link>https://forem.com/javiersalcedopuyo/in-defence-of-daily-stand-ups-1k4b</link>
      <guid>https://forem.com/javiersalcedopuyo/in-defence-of-daily-stand-ups-1k4b</guid>
      <description>&lt;p&gt;Photo by &lt;a href="https://unsplash.com/@dylandgillis?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Dylan Gillis&lt;/a&gt; on &lt;a href="https://unsplash.com/s/photos/meeting?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I've seen lately a lot of hate towards dailies and I want to add my 2 cents on what I think is the real issue behind.&lt;/p&gt;

&lt;p&gt;A common critique I've heard is that they are distractions and they are a waste of time.&lt;/p&gt;

&lt;p&gt;Daily stand-ups, meetings that should last about 10 minutes, are a waste of time?&lt;br&gt;
I think we have a hint on what can be going on here.&lt;/p&gt;

&lt;p&gt;I've seen and heard of dailies that take too long. My personal record is attending one that lasted &lt;strong&gt;over an hour&lt;/strong&gt;, but the average seems to be around 20-30 minutes.&lt;/p&gt;

&lt;p&gt;No wonder people think like that!&lt;/p&gt;

&lt;p&gt;But how did we get here?&lt;br&gt;
Let's remember what a daily &lt;strong&gt;should&lt;/strong&gt; be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Less than 9 attendees &lt;/li&gt;
&lt;li&gt;Each person speaks for ~1 minute, saying:

&lt;ul&gt;
&lt;li&gt;What did they do yesterday&lt;/li&gt;
&lt;li&gt;What will they do today&lt;/li&gt;
&lt;li&gt;If they need any help&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal is to keep the individuals aware of the state and progress of the project and the rest of the team, and ask for / offer help if needed. Nothing more.&lt;/p&gt;

&lt;p&gt;That wouldn't even take 15 min on the worst cases, so what happened?&lt;/p&gt;

&lt;p&gt;In my opinion, we lost our way. And they became nothing more than an opportunity to brag about how smart we are, explaining in detail how we implemented that new tricky feature, or how we fixed that elusive bug. And the sad truth is &lt;strong&gt;no one cares&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Without seeing the code and the appropriate context and references, &lt;strong&gt;no one&lt;/strong&gt; can follow a complex (and incomplete) explanation, and people just stop paying attention, simply waiting for their turn to do the same.&lt;/p&gt;

&lt;p&gt;That way, when everybody leaves the meeting (bored to death), no one really knows what the rest are doing and they feel (rightly so) that they just lost a valuable part of their day with nothing in return.&lt;/p&gt;

&lt;p&gt;Dailies are useful, Agile methodologies are useful.&lt;br&gt;
But only if implemented properly (and if they're actually appropriate for the specific project).&lt;br&gt;
They are mere tools. No one would complain that a screwdriver is a useless tool while holding it backwards (and/or trying to use it with a nail).&lt;br&gt;
&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Font6fgonnh0rd7uxj4pb.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Font6fgonnh0rd7uxj4pb.jpeg" alt="Alt Text" width="242" height="242"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So let's go back to the basics, and Keep It Simple (Stupid).&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>healthydebate</category>
      <category>management</category>
      <category>agile</category>
    </item>
  </channel>
</rss>
