<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="/feed.xml" rel="self" type="application/atom+xml" /><link href="/" rel="alternate" type="text/html" /><updated>2025-11-22T17:24:50+00:00</updated><id>/feed.xml</id><title type="html">leod’s blog</title><subtitle>Blog posts on gamedev and whatever else.</subtitle><entry><title type="html">Introducing posh: Type-Safe Graphics Programming with Functional Shaders in Rust</title><link href="/rust/gamedev/posh/2023/06/04/posh.html" rel="alternate" type="text/html" title="Introducing posh: Type-Safe Graphics Programming with Functional Shaders in Rust" /><published>2023-06-04T08:00:00+00:00</published><updated>2023-06-04T08:00:00+00:00</updated><id>/rust/gamedev/posh/2023/06/04/posh</id><content type="html" xml:base="/rust/gamedev/posh/2023/06/04/posh.html"><![CDATA[<p>This article introduces <a href="https://github.com/leod/posh"><code class="language-plaintext highlighter-rouge">posh</code></a>, an experimental Rust graphics library aimed at enhancing the type-safety, composability, and overall experience of graphics programming.
The post covers the fundamental concepts of <code class="language-plaintext highlighter-rouge">posh</code>, showcases examples, discusses related work and limitations of the approach, and, for readers who might be interested in details, delves into the internal workings of the library.</p>

<p><code class="language-plaintext highlighter-rouge">posh</code> consists of two closely integrated modules: <code class="language-plaintext highlighter-rouge">posh::gl</code>, a graphics library responsible for uploading data to the GPU and executing draw calls, and <code class="language-plaintext highlighter-rouge">posh::sl</code>, a functional shading language embedded within Rust.
The tight integration between these modules enables static verification, ensuring that the data provided in draw calls aligns with the shader’s signature.</p>

<p>The typical structure of <code class="language-plaintext highlighter-rouge">posh</code> code follows this pattern:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">use</span> <span class="nn">posh</span><span class="p">::{</span><span class="n">gl</span><span class="p">,</span> <span class="n">sl</span><span class="p">};</span>

<span class="c1">// ... define custom shader interface types U, V, W, and F ...</span>

<span class="k">fn</span> <span class="nf">vertex_shader</span><span class="p">(</span><span class="n">uniform</span><span class="p">:</span> <span class="n">U</span><span class="p">,</span> <span class="n">vertex</span><span class="p">:</span> <span class="n">V</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nn">sl</span><span class="p">::</span><span class="n">VsOutput</span><span class="o">&lt;</span><span class="n">W</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="c1">// ... compute `sl::VsOutput { clip_position, interpolant }` ...</span>
<span class="p">}</span>

<span class="k">fn</span> <span class="nf">fragment_shader</span><span class="p">(</span><span class="n">uniform</span><span class="p">:</span> <span class="n">U</span><span class="p">,</span> <span class="n">interpolant</span><span class="p">:</span> <span class="n">W</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="n">F</span> <span class="p">{</span>
    <span class="c1">// ... compute F ...</span>
<span class="p">}</span>

<span class="k">let</span> <span class="n">program</span><span class="p">:</span> <span class="nn">gl</span><span class="p">::</span><span class="n">Program</span><span class="o">&lt;</span><span class="n">U</span><span class="p">,</span> <span class="n">V</span><span class="p">,</span> <span class="n">F</span><span class="o">&gt;</span> <span class="o">=</span> <span class="n">gl</span><span class="nf">.create_program</span><span class="p">(</span>
    <span class="n">vertex_shader</span><span class="p">,</span>
    <span class="n">fragment_shader</span><span class="p">,</span>
<span class="p">)</span><span class="o">?</span><span class="p">;</span>

<span class="n">program</span>
    <span class="nf">.with_uniforms</span><span class="p">(</span><span class="cm">/* uniform bindings matching U */</span><span class="p">)</span>
    <span class="nf">.with_framebuffer</span><span class="p">(</span><span class="cm">/* framebuffer matching F */</span><span class="p">)</span>
    <span class="nf">.with_settings</span><span class="p">(</span><span class="cm">/* draw settings */</span><span class="p">)</span>
    <span class="nf">.draw</span><span class="p">(</span><span class="cm">/* vertex specification matching V */</span><span class="p">)</span><span class="o">?</span><span class="p">;</span>
</code></pre></div></div>
<p>Shader functions are written as normal Rust code that interacts with types from <code class="language-plaintext highlighter-rouge">posh::sl</code>, thereby leveraging the benefits of Rust’s type checking and composability.
Internally, at runtime, shader functions generate expression graphs that are translated to GLSL.
Since these are regular Rust functions, their signatures naturally become part of the <code class="language-plaintext highlighter-rouge">program</code>’s type, ensuring type safety when invoking the <code class="language-plaintext highlighter-rouge">draw</code> method.</p>

<p>The library has been designed to avoid the need for procedural macros when writing shader code.
Instead, all macro magic is contained within derive macros for user-defined types that occur in shader signatures.
This approach aims to make shader code transparently readable.</p>

<p>The development of <code class="language-plaintext highlighter-rouge">posh</code> is motivated by the aim to simplify and streamline graphics programming.
Traditional graphics code often involves two distinct languages: the host language (e.g., Rust) and the shading language (e.g., GLSL).
This separation leads to boilerplate code on both sides and hampers the composition of functionality.
Moreover, draw calls, which act as a foreign function interface from host code to shader code, typically lack static type checks.
To address these challenges, <code class="language-plaintext highlighter-rouge">posh</code> provides a platform for defining shaders using a functional language embedded within Rust and integrates shader signatures with the graphics library.</p>

<h2 id="table-of-contents">Table of Contents</h2>

<ul id="markdown-toc">
  <li><a href="#table-of-contents" id="markdown-toc-table-of-contents">Table of Contents</a></li>
  <li><a href="#current-status" id="markdown-toc-current-status">Current Status</a></li>
  <li><a href="#a-basic-example-hello-triangle" id="markdown-toc-a-basic-example-hello-triangle">A Basic Example: Hello, Triangle!</a>    <ul>
      <li><a href="#1-define-shader-interface-types" id="markdown-toc-1-define-shader-interface-types">1) Define Shader Interface Types</a></li>
      <li><a href="#2-write-shader-code" id="markdown-toc-2-write-shader-code">2) Write Shader Code</a></li>
      <li><a href="#3-write-host-code" id="markdown-toc-3-write-host-code">3) Write Host Code</a></li>
    </ul>
  </li>
  <li><a href="#a-more-complex-example-shadow-mapping" id="markdown-toc-a-more-complex-example-shadow-mapping">A More Complex Example: Shadow Mapping</a>    <ul>
      <li><a href="#1-define-shader-interface-types-1" id="markdown-toc-1-define-shader-interface-types-1">1) Define Shader Interface Types</a></li>
      <li><a href="#2-write-shader-code-1" id="markdown-toc-2-write-shader-code-1">2) Write Shader Code</a></li>
      <li><a href="#3-write-host-code-1" id="markdown-toc-3-write-host-code-1">3) Write Host Code</a></li>
    </ul>
  </li>
  <li><a href="#related-work" id="markdown-toc-related-work">Related Work</a></li>
  <li><a href="#discussion" id="markdown-toc-discussion">Discussion</a></li>
  <li><a href="#how-does-it-work" id="markdown-toc-how-does-it-work">How Does it Work?</a>    <ul>
      <li><a href="#shader-interface-traits" id="markdown-toc-shader-interface-traits">Shader Interface Traits</a>        <ul>
          <li><a href="#block-data" id="markdown-toc-block-data">Block Data</a></li>
          <li><a href="#uniform-interface" id="markdown-toc-uniform-interface">Uniform Interface</a></li>
          <li><a href="#vertex-shader-interface" id="markdown-toc-vertex-shader-interface">Vertex Shader Interface</a></li>
          <li><a href="#fragment-shader-interface" id="markdown-toc-fragment-shader-interface">Fragment Shader Interface</a></li>
        </ul>
      </li>
      <li><a href="#the-shading-language" id="markdown-toc-the-shading-language">The Shading Language</a>        <ul>
          <li><a href="#types" id="markdown-toc-types">Types</a></li>
          <li><a href="#values" id="markdown-toc-values">Values</a></li>
          <li><a href="#primitives" id="markdown-toc-primitives">Primitives</a></li>
          <li><a href="#transpilation" id="markdown-toc-transpilation">Transpilation</a></li>
        </ul>
      </li>
      <li><a href="#the-graphics-library" id="markdown-toc-the-graphics-library">The Graphics Library</a></li>
    </ul>
  </li>
</ul>

<h2 id="current-status">Current Status</h2>

<p>Currently, in order to narrow down the initial scope of the project, <code class="language-plaintext highlighter-rouge">posh</code> targets subsets of <a href="https://registry.khronos.org/OpenGL/specs/es/3.0/es_spec_3.0.pdf">OpenGL ES 3.0</a> and <a href="https://registry.khronos.org/OpenGL/specs/es/3.0/GLSL_ES_Specification_3.00.pdf">GLSL ES 3.0</a>.
However, we are considering to transition to <a href="https://github.com/gfx-rs/wgpu">wgpu</a> in the long run.</p>

<p>It is important to note that <code class="language-plaintext highlighter-rouge">posh</code> is still in its early stages and may require several revisions to reach stabilization.
We welcome contributions to the design and implementation. Feel free to explore <code class="language-plaintext highlighter-rouge">posh</code>’s <a href="https://github.com/leod/posh">repository</a>, including its <a href="https://github.com/leod/posh/tree/main/examples">examples</a> and <a href="https://github.com/leod/posh/issues">open issues</a>, to get an idea of where we currently stand.
Please be aware that there is no release available on <a href="https://crates.io/">crates.io</a> at this time.</p>

<h2 id="a-basic-example-hello-triangle">A Basic Example: Hello, Triangle!</h2>

<p><img src="/assets/hello_triangle.png" alt="Example: A triangle" /></p>

<p>In order to introduce the basic concepts behind <code class="language-plaintext highlighter-rouge">posh</code>, let us draw a triangle whose position and shading depend on the current time.
You can find the complete source code for this example in the <a href="https://github.com/leod/posh/blob/main/examples/hello_triangle.rs">repository</a>.</p>

<p>When writing <code class="language-plaintext highlighter-rouge">posh</code> code, there are three basic steps to follow.</p>

<h3 id="1-define-shader-interface-types">1) Define Shader Interface Types</h3>

<p>Shaders typically require access to uniform inputs provided by the host.
In this example, we will need the current time and the desired size of the triangle.
To achieve this, we create a struct and derive <code class="language-plaintext highlighter-rouge">Block</code> for it, which allows us to use it as a uniform block.</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">use</span> <span class="nn">posh</span><span class="p">::{</span><span class="n">Block</span><span class="p">,</span> <span class="n">BlockDom</span><span class="p">};</span>

<span class="nd">#[derive(Clone,</span> <span class="nd">Copy,</span> <span class="nd">Block)]</span>
<span class="nd">#[repr(C)]</span>
<span class="k">struct</span> <span class="n">MyGlobals</span><span class="o">&lt;</span><span class="n">D</span><span class="p">:</span> <span class="n">BlockDom</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="n">time</span><span class="p">:</span> <span class="nn">D</span><span class="p">::</span><span class="n">F32</span><span class="p">,</span>
    <span class="n">size</span><span class="p">:</span> <span class="nn">D</span><span class="p">::</span><span class="n">Vec2</span><span class="p">,</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Shader interface types like <code class="language-plaintext highlighter-rouge">MyGlobals&lt;D&gt;</code> are generic in the domain <code class="language-plaintext highlighter-rouge">D</code>.
There are two domains: <code class="language-plaintext highlighter-rouge">posh::Gl</code>, which provides data and bindings for draw calls, and <code class="language-plaintext highlighter-rouge">posh::Sl</code>, which provides types used in shader definitions.
The concept of domains connects the two sides of graphics programming.
We’ll use <code class="language-plaintext highlighter-rouge">MyGlobals&lt;Gl&gt;</code> to describe data to be stored in buffers on the GPU and <code class="language-plaintext highlighter-rouge">MyGlobals&lt;Sl&gt;</code> to access inputs in our shader code.</p>

<h3 id="2-write-shader-code">2) Write Shader Code</h3>

<p>Next, we will write shader code using <code class="language-plaintext highlighter-rouge">posh::sl</code>.
We will define the vertex shader and the fragment shader functions to specify where and how the triangle should be drawn.</p>

<p>In <code class="language-plaintext highlighter-rouge">posh::sl</code>, shader functions have two arguments.
The first argument is the uniform input, while the second argument is the current input of the shader (vertex input for the vertex shader and interpolant input for the fragment shader).</p>

<p>In this example, the vertex shader receives <code class="language-plaintext highlighter-rouge">MyGlobals&lt;Sl&gt;</code> as uniform input, and a two-dimensional position as vertex input.
The shader computes the <code class="language-plaintext highlighter-rouge">clip_position</code> output (equivalent to <code class="language-plaintext highlighter-rouge">gl_Position</code> in GLSL) along with an <code class="language-plaintext highlighter-rouge">interpolant</code>, which is interpolated and passed on to the fragment shader by the GPU:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">use</span> <span class="nn">posh</span><span class="p">::{</span><span class="n">sl</span><span class="p">,</span> <span class="n">Sl</span><span class="p">};</span>

<span class="k">fn</span> <span class="nf">vertex_shader</span><span class="p">(</span>
    <span class="n">globals</span><span class="p">:</span> <span class="n">MyGlobals</span><span class="o">&lt;</span><span class="n">Sl</span><span class="o">&gt;</span><span class="p">,</span>
    <span class="n">vertex</span><span class="p">:</span> <span class="nn">sl</span><span class="p">::</span><span class="n">Vec2</span><span class="p">,</span>
<span class="p">)</span> <span class="k">-&gt;</span> <span class="nn">sl</span><span class="p">::</span><span class="n">VsOutput</span><span class="o">&lt;</span><span class="nn">sl</span><span class="p">::</span><span class="n">Vec2</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="k">let</span> <span class="n">position</span> <span class="o">=</span> <span class="nn">sl</span><span class="p">::</span><span class="nn">Vec2</span><span class="p">::</span><span class="nf">from_angle</span><span class="p">(</span><span class="n">globals</span><span class="py">.time</span><span class="p">)</span>
        <span class="nf">.rotate</span><span class="p">(</span><span class="n">vertex</span> <span class="o">*</span> <span class="n">globals</span><span class="py">.size</span><span class="p">);</span>

    <span class="nn">sl</span><span class="p">::</span><span class="n">VsOutput</span> <span class="p">{</span>
        <span class="n">clip_position</span><span class="p">:</span> <span class="nn">sl</span><span class="p">::</span><span class="nf">vec4</span><span class="p">(</span><span class="n">position</span><span class="py">.x</span><span class="p">,</span> <span class="n">position</span><span class="py">.y</span><span class="p">,</span> <span class="mf">0.0</span><span class="p">,</span> <span class="mf">1.0</span><span class="p">),</span>
        <span class="n">interpolant</span><span class="p">:</span> <span class="n">vertex</span><span class="p">,</span>
    <span class="p">}</span>
<span class="p">}</span>

</code></pre></div></div>

<p>The fragment shader uses the <code class="language-plaintext highlighter-rouge">interpolant</code> to compute a time-dependent color for each fragment:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fn</span> <span class="nf">fragment_shader</span><span class="p">(</span><span class="n">globals</span><span class="p">:</span> <span class="n">MyGlobals</span><span class="o">&lt;</span><span class="n">Sl</span><span class="o">&gt;</span><span class="p">,</span> <span class="n">interpolant</span><span class="p">:</span> <span class="nn">sl</span><span class="p">::</span><span class="n">Vec2</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nn">sl</span><span class="p">::</span><span class="n">Vec4</span> <span class="p">{</span>
    <span class="k">let</span> <span class="n">rg</span> <span class="o">=</span> <span class="p">(</span><span class="n">interpolant</span> <span class="o">+</span> <span class="n">globals</span><span class="py">.time</span><span class="p">)</span><span class="nf">.cos</span><span class="p">()</span><span class="nf">.powf</span><span class="p">(</span><span class="mf">2.0</span><span class="p">);</span>

    <span class="nn">sl</span><span class="p">::</span><span class="nf">vec4</span><span class="p">(</span><span class="n">rg</span><span class="py">.x</span><span class="p">,</span> <span class="n">rg</span><span class="py">.y</span><span class="p">,</span> <span class="mf">0.5</span><span class="p">,</span> <span class="mf">1.0</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Internally, types like <code class="language-plaintext highlighter-rouge">MyGlobals&lt;Sl&gt;</code> or <code class="language-plaintext highlighter-rouge">sl::Vec2</code> represent expression graphs that can be transformed to GLSL.
For instance, the variables <code class="language-plaintext highlighter-rouge">globals</code> and <code class="language-plaintext highlighter-rouge">interpolant</code> are leaf nodes representing inputs of the fragment shader.
Expressions like <code class="language-plaintext highlighter-rouge">interpolant + globals.time</code> are nodes in the expression graph that describe how their values are computed.
This makes it possible to transpile <code class="language-plaintext highlighter-rouge">vertex_shader</code> and <code class="language-plaintext highlighter-rouge">fragment_shader</code> as a whole to GLSL without requiring macro magic.</p>

<h3 id="3-write-host-code">3) Write Host Code</h3>

<p>Now, we will write host code using <code class="language-plaintext highlighter-rouge">posh::gl</code>.
We will set up buffer data on the GPU and then perform a draw call with the shader that we have defined above.
For the purpose of this example, we will assume that we already have a variable <code class="language-plaintext highlighter-rouge">gl: gl::Context</code> available (ignoring context creation).</p>

<p>First, we compile our two shader functions into a program.
Note that the program’s type carries the shader’s signature:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">use</span> <span class="nn">posh</span><span class="p">::</span><span class="n">gl</span><span class="p">;</span>

<span class="k">let</span> <span class="n">program</span><span class="p">:</span> <span class="nn">gl</span><span class="p">::</span><span class="n">Program</span><span class="o">&lt;</span><span class="n">MyGlobals</span><span class="o">&lt;</span><span class="n">Sl</span><span class="o">&gt;</span><span class="p">,</span> <span class="nn">sl</span><span class="p">::</span><span class="n">Vec2</span><span class="o">&gt;</span> <span class="o">=</span> <span class="n">gl</span><span class="nf">.create_program</span><span class="p">(</span>
    <span class="n">vertex_shader</span><span class="p">,</span>
    <span class="n">fragment_shader</span><span class="p">,</span>
<span class="p">)</span><span class="o">?</span><span class="p">;</span>
</code></pre></div></div>

<p>Next, we set up buffers on the GPU to hold data to be supplied to the shader.
The types of <code class="language-plaintext highlighter-rouge">globals</code> and <code class="language-plaintext highlighter-rouge">vertices</code> align with the signature of the <code class="language-plaintext highlighter-rouge">program</code>:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="n">globals</span><span class="p">:</span> <span class="nn">gl</span><span class="p">::</span><span class="n">UniformBuffer</span><span class="o">&lt;</span><span class="n">MyGlobals</span><span class="o">&lt;</span><span class="n">Gl</span><span class="o">&gt;&gt;</span> <span class="o">=</span> <span class="n">gl</span><span class="nf">.create_uniform_buffer</span><span class="p">(</span>
    <span class="n">MyGlobals</span> <span class="p">{</span>
        <span class="n">time</span><span class="p">:</span> <span class="mf">42.0</span><span class="p">,</span>
        <span class="n">size</span><span class="p">:</span> <span class="p">[</span><span class="mf">1.0</span><span class="p">,</span> <span class="mf">1.0</span><span class="p">]</span><span class="nf">.into</span><span class="p">(),</span>
    <span class="p">},</span>
    <span class="nn">gl</span><span class="p">::</span><span class="nn">BufferUsage</span><span class="p">::</span><span class="n">StreamDraw</span><span class="p">,</span>
<span class="p">)</span><span class="o">?</span><span class="p">;</span>

<span class="k">let</span> <span class="n">vertices</span><span class="p">:</span> <span class="nn">gl</span><span class="p">::</span><span class="n">VertexBuffer</span><span class="o">&lt;</span><span class="nn">gl</span><span class="p">::</span><span class="n">Vec2</span><span class="o">&gt;</span> <span class="o">=</span> <span class="n">gl</span><span class="nf">.create_vertex_buffer</span><span class="p">(</span>
    <span class="o">&amp;</span><span class="p">[</span>
        <span class="p">[</span><span class="mf">0.0f32</span><span class="p">,</span> <span class="mf">1.0</span><span class="p">]</span><span class="nf">.into</span><span class="p">(),</span>
        <span class="p">[</span><span class="o">-</span><span class="mf">0.5</span><span class="p">,</span> <span class="o">-</span><span class="mf">0.5</span><span class="p">]</span><span class="nf">.into</span><span class="p">(),</span>
        <span class="p">[</span><span class="mf">0.5</span><span class="p">,</span> <span class="o">-</span><span class="mf">0.5</span><span class="p">]</span><span class="nf">.into</span><span class="p">(),</span>
    <span class="p">],</span>
    <span class="nn">gl</span><span class="p">::</span><span class="nn">BufferUsage</span><span class="p">::</span><span class="n">StreamDraw</span><span class="p">,</span>
<span class="p">)</span><span class="o">?</span><span class="p">;</span>
</code></pre></div></div>

<p>Finally, we perform a draw call using a method of <code class="language-plaintext highlighter-rouge">program</code>.
This requires us to supply bindings for uniform inputs and vertex inputs, along with various draw settings.</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">program</span>
    <span class="nf">.with_uniforms</span><span class="p">(</span><span class="n">globals</span><span class="nf">.as_binding</span><span class="p">())</span>
    <span class="nf">.with_settings</span><span class="p">(</span>
        <span class="nn">gl</span><span class="p">::</span><span class="nn">DrawSettings</span><span class="p">::</span><span class="nf">default</span><span class="p">()</span>
            <span class="nf">.with_clear_color</span><span class="p">([</span><span class="mf">0.1</span><span class="p">,</span> <span class="mf">0.2</span><span class="p">,</span> <span class="mf">0.3</span><span class="p">,</span> <span class="mf">1.0</span><span class="p">])</span>
    <span class="p">)</span>
    <span class="nf">.draw</span><span class="p">(</span><span class="n">vertices</span><span class="nf">.as_vertex_spec</span><span class="p">(</span><span class="nn">gl</span><span class="p">::</span><span class="nn">PrimitiveMode</span><span class="p">::</span><span class="n">Triangles</span><span class="p">))</span><span class="o">?</span><span class="p">;</span>
</code></pre></div></div>

<p>The draw call is where everything comes together in <code class="language-plaintext highlighter-rouge">posh</code>.
It takes our compiled shader, supplies GPU buffer bindings, and draws something to a framebuffer (in this instance, the default framebuffer).
The <code class="language-plaintext highlighter-rouge">draw</code> method benefits from static type-checking, allowing the Rust compiler to help ensure that the data we provide matches the expected types in the shader.
This provides an additional level of correctness to our graphics programming workflow.</p>

<h2 id="a-more-complex-example-shadow-mapping">A More Complex Example: Shadow Mapping</h2>

<p><img src="/assets/shadow_map.png" alt="Example: Shadow Mapping" /></p>

<p>In this section, we will a explore a more complex example.
We will look at a way to render a scene with <a href="https://en.wikipedia.org/wiki/Shadow_mapping">shadow mapping</a> (assuming that the shadow map has already been created).
You can find the complete source code for this example in the <a href="https://github.com/leod/posh/blob/main/examples/shadow_map.rs">repository</a>.</p>

<p>Without delving into details of how shadow mapping works, let us again follow the three basic steps of writing <code class="language-plaintext highlighter-rouge">posh</code> code.</p>

<h3 id="1-define-shader-interface-types-1">1) Define Shader Interface Types</h3>

<p>First, we define a custom vertex type, <code class="language-plaintext highlighter-rouge">SceneVertex&lt;D&gt;</code>, to hold world-space information of the scene to be drawn.
We also define the <code class="language-plaintext highlighter-rouge">Camera&lt;D&gt;</code> and <code class="language-plaintext highlighter-rouge">Light&lt;D&gt;</code> types for the view camera and the light source.</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">use</span> <span class="nn">posh</span><span class="p">::{</span><span class="n">Block</span><span class="p">,</span> <span class="n">BlockDom</span><span class="p">};</span>

<span class="nd">#[derive(Clone,</span> <span class="nd">Copy,</span> <span class="nd">Block)]</span>
<span class="nd">#[repr(C)]</span>
<span class="k">struct</span> <span class="n">SceneVertex</span><span class="o">&lt;</span><span class="n">D</span><span class="p">:</span> <span class="n">BlockDom</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="n">world_pos</span><span class="p">:</span> <span class="nn">D</span><span class="p">::</span><span class="n">Vec3</span><span class="p">,</span>
    <span class="n">world_normal</span><span class="p">:</span> <span class="nn">D</span><span class="p">::</span><span class="n">Vec3</span><span class="p">,</span>
    <span class="n">color</span><span class="p">:</span> <span class="nn">D</span><span class="p">::</span><span class="n">Vec3</span><span class="p">,</span>
<span class="p">}</span>

<span class="nd">#[derive(Clone,</span> <span class="nd">Copy,</span> <span class="nd">Block)]</span>
<span class="nd">#[repr(C)]</span>
<span class="k">struct</span> <span class="n">Camera</span><span class="o">&lt;</span><span class="n">D</span><span class="p">:</span> <span class="n">BlockDom</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="n">world_to_eye</span><span class="p">:</span> <span class="nn">D</span><span class="p">::</span><span class="n">Mat4</span><span class="p">,</span>
    <span class="n">eye_to_clip</span><span class="p">:</span> <span class="nn">D</span><span class="p">::</span><span class="n">Mat4</span><span class="p">,</span>
<span class="p">}</span>

<span class="nd">#[derive(Clone,</span> <span class="nd">Copy,</span> <span class="nd">Block)]</span>
<span class="nd">#[repr(C)]</span>
<span class="k">struct</span> <span class="n">Light</span><span class="o">&lt;</span><span class="n">D</span><span class="p">:</span> <span class="n">BlockDom</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="n">camera</span><span class="p">:</span> <span class="n">Camera</span><span class="o">&lt;</span><span class="n">D</span><span class="o">&gt;</span><span class="p">,</span>
    <span class="n">world_pos</span><span class="p">:</span> <span class="nn">D</span><span class="p">::</span><span class="n">Vec3</span><span class="p">,</span>
    <span class="n">color</span><span class="p">:</span> <span class="nn">D</span><span class="p">::</span><span class="n">Vec3</span><span class="p">,</span>
    <span class="n">ambient</span><span class="p">:</span> <span class="nn">D</span><span class="p">::</span><span class="n">Vec3</span><span class="p">,</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Lastly, we define the <code class="language-plaintext highlighter-rouge">SceneUniforms&lt;D&gt;</code> type, which encapsulates the uniform inputs required by our shaders, including the depth map sampler.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">use</span> <span class="nn">posh</span><span class="p">::{</span><span class="n">UniformInterface</span><span class="p">,</span> <span class="n">UniformInterfaceDom</span><span class="p">};</span>

<span class="nd">#[derive(Clone,</span> <span class="nd">UniformInterface)]</span>
<span class="k">struct</span> <span class="n">SceneUniforms</span><span class="o">&lt;</span><span class="n">D</span><span class="p">:</span> <span class="n">UniformInterfaceDom</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="n">camera</span><span class="p">:</span> <span class="nn">D</span><span class="p">::</span><span class="n">Block</span><span class="o">&lt;</span><span class="n">Camera</span><span class="o">&lt;</span><span class="n">Sl</span><span class="o">&gt;&gt;</span><span class="p">,</span>
    <span class="n">light</span><span class="p">:</span> <span class="nn">D</span><span class="p">::</span><span class="n">Block</span><span class="o">&lt;</span><span class="n">Light</span><span class="o">&lt;</span><span class="n">Sl</span><span class="o">&gt;&gt;</span><span class="p">,</span>
    <span class="n">light_depth_map</span><span class="p">:</span> <span class="nn">D</span><span class="p">::</span><span class="n">ComparisonSampler2d</span><span class="p">,</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Any type that implements <code class="language-plaintext highlighter-rouge">UniformInterface</code> can be used as uniform input for shaders.
Similar to <code class="language-plaintext highlighter-rouge">Block</code> declarations, <code class="language-plaintext highlighter-rouge">UniformInterface</code> declarations are generic in the domain <code class="language-plaintext highlighter-rouge">D</code>.
In this example, <code class="language-plaintext highlighter-rouge">SceneUniforms&lt;Gl&gt;</code> contains bindings of uniform buffers and samplers, while <code class="language-plaintext highlighter-rouge">SceneUniforms&lt;Sl&gt;</code> provides the inputs for shader definitions.</p>

<h3 id="2-write-shader-code-1">2) Write Shader Code</h3>

<p>Next, let us define an <code class="language-plaintext highlighter-rouge">Interpolant</code> struct, which connects the vertex and fragment shaders.
The fragment shader requires access to the interpolated input vertex and the input vertex’s position in the light source’s clip space.</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">use</span> <span class="nn">posh</span><span class="p">::{</span><span class="n">sl</span><span class="p">,</span> <span class="n">Sl</span><span class="p">};</span>

<span class="nd">#[derive(Clone,</span> <span class="nd">Copy,</span> <span class="nd">sl::Value,</span> <span class="nd">sl::Interpolant)]</span>
<span class="k">struct</span> <span class="n">MyInterpolant</span> <span class="p">{</span>
    <span class="n">vertex</span><span class="p">:</span> <span class="n">SceneVertex</span><span class="o">&lt;</span><span class="n">Sl</span><span class="o">&gt;</span><span class="p">,</span>
    <span class="n">light_clip_pos</span><span class="p">:</span> <span class="nn">sl</span><span class="p">::</span><span class="n">Vec4</span><span class="p">,</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Now, we can implement our vertex shader, using a utility function for <code class="language-plaintext highlighter-rouge">Camera&lt;Sl&gt;</code>:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">impl</span> <span class="n">Camera</span><span class="o">&lt;</span><span class="n">Sl</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="k">fn</span> <span class="nf">world_to_clip</span><span class="p">(</span><span class="k">self</span><span class="p">,</span> <span class="n">world_pos</span><span class="p">:</span> <span class="nn">sl</span><span class="p">::</span><span class="n">Vec3</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nn">sl</span><span class="p">::</span><span class="n">Vec4</span> <span class="p">{</span>
        <span class="k">self</span><span class="py">.eye_to_clip</span> <span class="o">*</span> <span class="k">self</span><span class="py">.world_to_eye</span> <span class="o">*</span> <span class="n">world_pos</span><span class="nf">.extend</span><span class="p">(</span><span class="mf">1.0</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="k">fn</span> <span class="nf">vertex_shader</span><span class="p">(</span>
    <span class="n">SceneUniforms</span> <span class="p">{</span> <span class="n">light</span><span class="p">,</span> <span class="n">camera</span><span class="p">,</span> <span class="o">..</span> <span class="p">}:</span> <span class="n">SceneUniforms</span><span class="o">&lt;</span><span class="n">Sl</span><span class="o">&gt;</span><span class="p">,</span>
    <span class="n">vertex</span><span class="p">:</span> <span class="n">SceneVertex</span><span class="o">&lt;</span><span class="n">Sl</span><span class="o">&gt;</span><span class="p">,</span>
<span class="p">)</span> <span class="k">-&gt;</span> <span class="nn">sl</span><span class="p">::</span><span class="n">VsOutput</span><span class="o">&lt;</span><span class="n">MyInterpolant</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="c1">// Slightly extrude along the normal to reduce shadow artifacts.</span>
    <span class="k">const</span> <span class="n">EXTRUDE</span><span class="p">:</span> <span class="nb">f32</span> <span class="o">=</span> <span class="mf">0.1</span><span class="p">;</span>
    <span class="k">let</span> <span class="n">light_clip_pos</span> <span class="o">=</span> <span class="n">light</span>
        <span class="py">.camera</span>
        <span class="nf">.world_to_clip</span><span class="p">(</span><span class="n">vertex</span><span class="py">.world_pos</span> <span class="o">+</span> <span class="n">vertex</span><span class="py">.world_normal</span> <span class="o">*</span> <span class="n">EXTRUDE</span><span class="p">);</span>

    <span class="nn">sl</span><span class="p">::</span><span class="n">VsOutput</span> <span class="p">{</span>
        <span class="n">clip_position</span><span class="p">:</span> <span class="n">camera</span><span class="nf">.world_to_clip</span><span class="p">(</span><span class="n">vertex</span><span class="py">.world_pos</span><span class="p">),</span>
        <span class="n">interpolant</span><span class="p">:</span> <span class="n">MyInterpolant</span> <span class="p">{</span> <span class="n">vertex</span><span class="p">,</span> <span class="n">light_clip_pos</span> <span class="p">},</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">MyInterpolant</code> value is fed from the vertex shader to the fragment shader, which uses it to sample the shadow map and shade the fragment.</p>

<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fn</span> <span class="nf">sample_shadow</span><span class="p">(</span>
    <span class="n">light_depth_map</span><span class="p">:</span> <span class="nn">sl</span><span class="p">::</span><span class="n">ComparisonSampler2d</span><span class="p">,</span>
    <span class="n">light_clip_pos</span><span class="p">:</span> <span class="nn">sl</span><span class="p">::</span><span class="n">Vec4</span><span class="p">,</span>
<span class="p">)</span> <span class="k">-&gt;</span> <span class="nn">sl</span><span class="p">::</span><span class="n">F32</span> <span class="p">{</span>
    <span class="k">let</span> <span class="n">ndc</span> <span class="o">=</span> <span class="n">light_clip_pos</span><span class="nf">.xyz</span><span class="p">()</span> <span class="o">/</span> <span class="n">light_clip_pos</span><span class="py">.w</span><span class="p">;</span>
    <span class="k">let</span> <span class="n">uvw</span> <span class="o">=</span> <span class="n">ndc</span> <span class="o">*</span> <span class="mf">0.5</span> <span class="o">+</span> <span class="mf">0.5</span><span class="p">;</span>

    <span class="c1">// Fall back to zero if the UV coordinates would be clamped.</span>
    <span class="k">let</span> <span class="n">clamp</span> <span class="o">=</span> <span class="nn">sl</span><span class="p">::</span><span class="nf">any</span><span class="p">([</span>
        <span class="n">uvw</span><span class="py">.x</span><span class="nf">.lt</span><span class="p">(</span><span class="mf">0.0</span><span class="p">),</span>
        <span class="n">uvw</span><span class="py">.x</span><span class="nf">.gt</span><span class="p">(</span><span class="mf">1.0</span><span class="p">),</span>
        <span class="n">uvw</span><span class="py">.y</span><span class="nf">.lt</span><span class="p">(</span><span class="mf">0.0</span><span class="p">),</span>
        <span class="n">uvw</span><span class="py">.y</span><span class="nf">.gt</span><span class="p">(</span><span class="mf">1.0</span><span class="p">),</span>
    <span class="p">]);</span>

    <span class="nn">sl</span><span class="p">::</span><span class="nf">branch</span><span class="p">(</span>
        <span class="n">clamp</span><span class="p">,</span>
        <span class="mf">0.0</span><span class="p">,</span>
        <span class="n">light_depth_map</span><span class="nf">.sample_compare</span><span class="p">(</span><span class="n">uvw</span><span class="nf">.xy</span><span class="p">(),</span> <span class="n">uvw</span><span class="py">.z</span><span class="p">),</span>
    <span class="p">)</span>
<span class="p">}</span>

<span class="k">fn</span> <span class="nf">fragment_shader</span><span class="p">(</span>
    <span class="n">SceneUniforms</span> <span class="p">{</span> <span class="n">light</span><span class="p">,</span> <span class="n">light_depth_map</span><span class="p">,</span> <span class="o">..</span>  <span class="p">}:</span> <span class="n">SceneUniforms</span><span class="o">&lt;</span><span class="n">Sl</span><span class="o">&gt;</span><span class="p">,</span>
    <span class="n">MyInterpolant</span> <span class="p">{</span> <span class="n">vertex</span><span class="p">,</span> <span class="n">light_clip_pos</span> <span class="p">}:</span> <span class="n">MyInterpolant</span><span class="p">,</span>
<span class="p">)</span> <span class="k">-&gt;</span> <span class="nn">sl</span><span class="p">::</span><span class="n">Vec4</span> <span class="p">{</span>
    <span class="k">let</span> <span class="n">light_dir</span> <span class="o">=</span> <span class="p">(</span><span class="n">light</span><span class="py">.world_pos</span> <span class="o">-</span> <span class="n">vertex</span><span class="py">.world_pos</span><span class="p">)</span><span class="nf">.normalize</span><span class="p">();</span>
    <span class="k">let</span> <span class="n">diffuse</span> <span class="o">=</span> <span class="n">light</span><span class="py">.color</span> <span class="o">*</span> <span class="n">vertex</span><span class="py">.world_normal</span><span class="nf">.dot</span><span class="p">(</span><span class="n">light_dir</span><span class="p">)</span><span class="nf">.max</span><span class="p">(</span><span class="mf">0.0</span><span class="p">);</span>
    <span class="k">let</span> <span class="n">shadow</span> <span class="o">=</span> <span class="nf">sample_shadow</span><span class="p">(</span><span class="n">light_depth_map</span><span class="p">,</span> <span class="n">light_clip_pos</span><span class="p">);</span>
    <span class="k">let</span> <span class="n">color</span> <span class="o">=</span> <span class="p">(</span><span class="n">light</span><span class="py">.ambient</span> <span class="o">+</span> <span class="n">shadow</span> <span class="o">*</span> <span class="n">diffuse</span><span class="p">)</span> <span class="o">*</span> <span class="n">vertex</span><span class="py">.color</span><span class="p">;</span>

    <span class="n">color</span><span class="nf">.extend</span><span class="p">(</span><span class="mf">1.0</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="3-write-host-code-1">3) Write Host Code</h3>

<p>On the host side, we compile the two shader functions into a program:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">use</span> <span class="nn">posh</span><span class="p">::</span><span class="n">gl</span><span class="p">;</span>

<span class="k">let</span> <span class="n">program</span><span class="p">:</span> <span class="nn">gl</span><span class="p">::</span><span class="n">Program</span><span class="o">&lt;</span><span class="n">SceneUniforms</span><span class="o">&lt;</span><span class="n">Sl</span><span class="o">&gt;</span><span class="p">,</span> <span class="n">SceneVertex</span><span class="o">&lt;</span><span class="n">Sl</span><span class="o">&gt;&gt;</span> <span class="o">=</span>
    <span class="n">gl</span><span class="nf">.create_program</span><span class="p">(</span><span class="n">vertex_shader</span><span class="p">,</span> <span class="n">fragment_shader</span><span class="p">)</span><span class="o">?</span><span class="p">;</span>
</code></pre></div></div>

<p>We also need to set up several GPU buffers.
Ignoring the contents of the buffers, we have the following objects:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="n">camera_buffer</span><span class="p">:</span> <span class="nn">gl</span><span class="p">::</span><span class="n">UniformBuffer</span><span class="o">&lt;</span><span class="n">Camera</span><span class="o">&lt;</span><span class="n">Gl</span><span class="o">&gt;&gt;</span> <span class="o">=</span> <span class="nd">todo!</span><span class="p">();</span>
<span class="k">let</span> <span class="n">light_buffer</span><span class="p">:</span> <span class="nn">gl</span><span class="p">::</span><span class="n">UniformBuffer</span><span class="o">&lt;</span><span class="n">Light</span><span class="o">&lt;</span><span class="n">Gl</span><span class="o">&gt;&gt;</span> <span class="o">=</span> <span class="nd">todo!</span><span class="p">();</span>
<span class="k">let</span> <span class="n">light_depth_map</span><span class="p">:</span> <span class="nn">gl</span><span class="p">::</span><span class="n">DepthTexture2d</span> <span class="o">=</span> <span class="nd">todo!</span><span class="p">();</span>
<span class="k">let</span> <span class="n">scene_vertices</span><span class="p">:</span> <span class="nn">gl</span><span class="p">::</span><span class="n">VertexBuffer</span><span class="o">&lt;</span><span class="n">SceneVertex</span><span class="o">&lt;</span><span class="n">Gl</span><span class="o">&gt;&gt;</span> <span class="o">=</span> <span class="nd">todo!</span><span class="p">();</span>
<span class="k">let</span> <span class="n">scene_elements</span><span class="p">:</span> <span class="nn">gl</span><span class="p">::</span><span class="n">ElementBuffer</span> <span class="o">=</span> <span class="nd">todo!</span><span class="p">();</span>
</code></pre></div></div>

<p>Finally, we can render the scene with shadow mapping:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">scene_program</span>
    <span class="nf">.with_uniforms</span><span class="p">(</span><span class="n">SceneUniforms</span> <span class="p">{</span>
        <span class="n">camera</span><span class="p">:</span> <span class="n">camera_buffer</span><span class="nf">.as_binding</span><span class="p">(),</span>
        <span class="n">light</span><span class="p">:</span> <span class="n">light_buffer</span><span class="nf">.as_binding</span><span class="p">(),</span>
        <span class="n">light_depth_map</span><span class="p">:</span> <span class="n">light_depth_map</span><span class="nf">.as_comparison_sampler</span><span class="p">(</span>
            <span class="nn">gl</span><span class="p">::</span><span class="nn">Sampler2dSettings</span><span class="p">::</span><span class="nf">linear</span><span class="p">(),</span>
            <span class="nn">gl</span><span class="p">::</span><span class="nn">Comparison</span><span class="p">::</span><span class="n">Less</span><span class="p">,</span>
        <span class="p">),</span>
    <span class="p">})</span>
    <span class="nf">.with_settings</span><span class="p">(</span>
        <span class="nn">gl</span><span class="p">::</span><span class="nn">DrawSettings</span><span class="p">::</span><span class="nf">default</span><span class="p">()</span>
            <span class="nf">.with_clear_color</span><span class="p">([</span><span class="mf">1.0</span><span class="p">,</span> <span class="mf">1.0</span><span class="p">,</span> <span class="mf">1.0</span><span class="p">,</span> <span class="mf">1.0</span><span class="p">]</span><span class="nf">.into</span><span class="p">())</span>
            <span class="nf">.with_clear_depth</span><span class="p">(</span><span class="mf">2.0</span><span class="p">)</span>
            <span class="nf">.with_depth_test</span><span class="p">(</span><span class="nn">gl</span><span class="p">::</span><span class="nn">Comparison</span><span class="p">::</span><span class="n">Less</span><span class="p">)</span>
            <span class="nf">.with_cull_face</span><span class="p">(</span><span class="nn">gl</span><span class="p">::</span><span class="nn">CullFace</span><span class="p">::</span><span class="n">Back</span><span class="p">)</span>
    <span class="p">)</span>
    <span class="nf">.draw</span><span class="p">(</span>
        <span class="n">scene_vertices</span>
            <span class="nf">.as_vertex_spec</span><span class="p">(</span><span class="nn">gl</span><span class="p">::</span><span class="nn">PrimitiveMode</span><span class="p">::</span><span class="n">Triangles</span><span class="p">)</span>
            <span class="nf">.with_element_data</span><span class="p">(</span><span class="n">scene_elements</span><span class="nf">.as_binding</span><span class="p">())</span>
    <span class="p">)</span><span class="o">?</span><span class="p">;</span>
</code></pre></div></div>

<p>Once again, the draw call serves as the point where all the components come together in a type-safe manner.
In the <code class="language-plaintext highlighter-rouge">with_uniforms</code> call, we provide uniform bindings of type <code class="language-plaintext highlighter-rouge">SceneUniforms&lt;Gl&gt;</code>, and in the subsequent <code class="language-plaintext highlighter-rouge">draw</code> call, we supply a vertex buffer of type <code class="language-plaintext highlighter-rouge">SceneVertex&lt;Gl&gt;</code>.
These types precisely match the expected <code class="language-plaintext highlighter-rouge">SceneUniforms&lt;Sl&gt;</code> and <code class="language-plaintext highlighter-rouge">SceneVertex&lt;Sl&gt;</code> types in the program’s signature.</p>

<h2 id="related-work">Related Work</h2>

<p>The development of <code class="language-plaintext highlighter-rouge">posh</code> has drawn inspiration from several amazing existing projects.</p>

<p><a href="https://github.com/EmbarkStudios/rust-gpu"><code class="language-plaintext highlighter-rouge">rust-gpu</code></a> enables Rust to be used as a first-class language for writing shaders by implementing a <code class="language-plaintext highlighter-rouge">rustc</code> backend that generates <a href="https://en.wikipedia.org/wiki/Standard_Portable_Intermediate_Representation">SPIR-V</a>.
While <code class="language-plaintext highlighter-rouge">posh</code> shares the goal of enhancing shader development, it takes a different approach.
Instead of treating Rust as a primary shading language, <code class="language-plaintext highlighter-rouge">posh</code> employs a functional language embedded within Rust to implement shaders.
Additionally, while <code class="language-plaintext highlighter-rouge">rust-gpu</code> primarily focuses on shader code, <code class="language-plaintext highlighter-rouge">posh</code> places emphasis on achieving a type-safe integration between shader code and host code.</p>

<p><a href="https://github.com/phaazon/shades">Shades</a> provides an embedded domain-specific language for shaders, similar to <code class="language-plaintext highlighter-rouge">posh</code>.
However, Shades is designed as a general-purpose library without specific ties to a target shading language or graphics library.
In contrast, <code class="language-plaintext highlighter-rouge">posh</code> intentionally narrows its scope to a functional subset of GLSL and aligns itselfs with a subset of OpenGL.
We hope that this limitation will allow us to iterate quickly and focus on the integration of shader code with host code.
However, as a result, <code class="language-plaintext highlighter-rouge">posh</code> is less powerful than Shades in many ways.</p>

<p><a href="https://github.com/glium/glium">glium</a> is an OpenGL wrapper that demonstrates that OpenGL can be used elegantly in Rust.
In glium, the dependencies of a draw call are consolidated into a single method.
<code class="language-plaintext highlighter-rouge">posh</code> builds upon this concept by introducing typed shader signatures.</p>

<h2 id="discussion">Discussion</h2>

<p><img src="/assets/aquadise.png" alt="Aquadise Island: A Project that uses `posh`" /></p>

<p>I have been dogfooding <code class="language-plaintext highlighter-rouge">posh</code> for a WebGL2-based game project that I am involved with (screenshot above).
After many iterations on the library, the experience has become quite pleasant to me.
Despite some rough edges, I find myself truly enjoying the process of writing shaders and making draw calls with <code class="language-plaintext highlighter-rouge">posh</code>.</p>

<p>However, it is important to acknowledge that the approach taken by <code class="language-plaintext highlighter-rouge">posh</code> does have its disadvantages.
While some of these drawbacks can be addressed or mitigated to some extent, others are inherent to the approach itself.
Let us examine some of these drawbacks:</p>

<ol>
  <li>Writing shader code in <code class="language-plaintext highlighter-rouge">posh::sl</code> differs from writing normal Rust code.
   Shader functions are evaluated at shader compile-time, which happens during the host program’s runtime.
   As a result, Rust <code class="language-plaintext highlighter-rouge">if</code> expressions turn into shader compile-time branches, requiring users to familiarize themselves with this concept and utilize <code class="language-plaintext highlighter-rouge">sl::branch</code> for dynamic branches.</li>
  <li>In certain cases, such as when optimizing shader code or performing uniformity analysis, users may need to develop a mental model of how shaders are transpiled into GLSL.</li>
  <li>Certain usage patterns in <code class="language-plaintext highlighter-rouge">posh::sl</code> can lead to the generation of redundant GLSL code (see also <a href="https://github.com/leod/posh/issues/96">issue #96</a>).
In particular, when a function call is made in shader code, such as the call to <code class="language-plaintext highlighter-rouge">sample_shadow</code> in the earlier example, the result of the function is inlined in the generated GLSL code.
If the same function is called multiple times, it will be inlined each time, potentially leading to bloat in the generated GLSL code.
Previous iterations of <code class="language-plaintext highlighter-rouge">posh</code> attempted to address this issue with a procedural macro <code class="language-plaintext highlighter-rouge">#[posh::define_function]</code> that could be attached to functions, but 
it introduced cognitive overhead when reading shader code.</li>
  <li>The shading language in <code class="language-plaintext highlighter-rouge">posh</code> lacks support for mutable variables.
While this is a deliberate design choice, it does limit certain programming patterns that rely on mutable state within shaders.</li>
  <li>Making values in the shading language <code class="language-plaintext highlighter-rouge">Copy</code> requires some trickery.
The section below provides more detail on this aspect.</li>
  <li>The types of the bindings provided in host code must precisely match the shader signature. There is no concept of allowing the binding of supersets of data required by the shader signature, limiting flexibility in some situations.</li>
  <li>Changing shaders requires recompilation. This can be addressed to some degree by hotloading the shader code.</li>
  <li>Clearly, type safety is just one aspect of writing correct shaders.
<code class="language-plaintext highlighter-rouge">posh</code> does not solve the problem that shaders are fundamentally hard to get right.
However, in my experience, it does help to have these basic guardrails in place.</li>
  <li>Finally, this is a lot to take in just to write some graphics code!
I’ve become familiar with it, and now feel empowered by it, but I’m not sure if it would be easy for others to get into it.</li>
</ol>

<p>Despite these challenges, I remain confident in the value of an integrated approach to graphics programming.
There may be better ways of getting there than the one currently taken by <code class="language-plaintext highlighter-rouge">posh</code>.
It is worth exploring alternative approaches to address the identified drawbacks and improve the overall experience.
Making the behavior of the library transparent to the user, containing the “magic” parts to a limited number of places, can be a key factor in ensuring its usability.</p>

<p>This domain feels underexplored to me, and I hope that <code class="language-plaintext highlighter-rouge">posh</code> can serve as inspiration for further investigations.</p>

<h2 id="how-does-it-work">How Does it Work?</h2>

<p><code class="language-plaintext highlighter-rouge">posh</code> utilizes Rust’s powerful trait system and provides several traits and derive macros to establish the integration between the <code class="language-plaintext highlighter-rouge">posh::gl</code> graphics library and the <code class="language-plaintext highlighter-rouge">posh::sl</code> shading language.</p>

<p>This section delves into some implementation details of <code class="language-plaintext highlighter-rouge">posh</code> and explores the underlying mechanisms that enable interoperability between the two modules.
It is intended for readers who are interested in understanding the inner workings of <code class="language-plaintext highlighter-rouge">posh</code>.
However, it is worth noting that <code class="language-plaintext highlighter-rouge">posh</code> can be used effectively without a deep understanding of implementation details.</p>

<h3 id="shader-interface-traits">Shader Interface Traits</h3>

<p>The core of <code class="language-plaintext highlighter-rouge">posh</code>’s integration lies in the concept of shader interfaces, which are defined through traits.
These traits enable types to be used in shader signatures, allowing for type-safe interactions between the host code and the shaders.</p>

<p>The following traits play a crucial role in <code class="language-plaintext highlighter-rouge">posh</code>:</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">UniformInterface&lt;D&gt;</code>: Allows a type to be used as a uniform input in shaders.</li>
  <li><code class="language-plaintext highlighter-rouge">VsInterface&lt;D&gt;</code>: Represents the vertex shader input interface.</li>
  <li><code class="language-plaintext highlighter-rouge">FsInterface&lt;D&gt;</code>: Represents the fragment shader output interface.</li>
</ul>

<p>Additionally, the <code class="language-plaintext highlighter-rouge">Block&lt;D&gt;</code> trait enables types to be used as part of uniform inputs or vertex shader inputs.</p>

<p>These traits are generic over the domain <code class="language-plaintext highlighter-rouge">D</code>, which can be either <code class="language-plaintext highlighter-rouge">posh::Sl</code> (representing the shading language domain) or <code class="language-plaintext highlighter-rouge">posh::Gl</code> (representing the graphics library domain).
This duality allows for the same struct to serve both as part of a shader definition and as the actual input data for the shader.</p>

<p>To simplify the implementation of these traits for user-defined structs, <code class="language-plaintext highlighter-rouge">posh</code> provides derive macros.
These macros automate the generation of trait implementations, reducing the boilerplate and making it easier to define shaders and bind input data.
Importantly, <code class="language-plaintext highlighter-rouge">posh</code> avoids the use of procedural macros in other areas, allowing user code to remain familiar and maintain its readability as plain Rust.</p>

<h4 id="block-data">Block Data</h4>

<p>By implementing the <code class="language-plaintext highlighter-rouge">Block&lt;D&gt;</code> trait, user-defined types can be used in uniform buffers or in vertex buffers, i.e. it enables them to be used as part of a <code class="language-plaintext highlighter-rouge">UniformInterface&lt;D&gt;</code> or a <code class="language-plaintext highlighter-rouge">VsInterface&lt;D&gt;</code>.</p>

<p><code class="language-plaintext highlighter-rouge">Block&lt;D&gt;</code> is generic in <code class="language-plaintext highlighter-rouge">D: BlockDom</code>, which represents a mapping for the core types that can be put into blocks. It defines core type representations as follows:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Defined in `posh`:</span>

<span class="k">pub</span> <span class="k">trait</span> <span class="n">BlockDom</span> <span class="p">{</span>
    <span class="k">type</span> <span class="n">F32</span><span class="p">:</span> <span class="n">Block</span><span class="o">&lt;</span><span class="k">Self</span><span class="o">&gt;</span><span class="p">;</span>
    <span class="k">type</span> <span class="n">I32</span><span class="p">:</span> <span class="n">Block</span><span class="o">&lt;</span><span class="k">Self</span><span class="o">&gt;</span><span class="p">;</span>
    <span class="k">type</span> <span class="n">U32</span><span class="p">:</span> <span class="n">Block</span><span class="o">&lt;</span><span class="k">Self</span><span class="o">&gt;</span><span class="p">;</span>
    <span class="k">type</span> <span class="n">Vec2</span><span class="p">:</span> <span class="n">Block</span><span class="o">&lt;</span><span class="k">Self</span><span class="o">&gt;</span><span class="p">;</span>
    <span class="c1">// ...</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The implementations of <code class="language-plaintext highlighter-rouge">BlockDom</code> for <code class="language-plaintext highlighter-rouge">posh::Gl</code> and <code class="language-plaintext highlighter-rouge">posh::Sl</code> simply map to their respective types:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Implemented in `posh`:</span>

<span class="k">impl</span> <span class="n">BlockDom</span> <span class="k">for</span> <span class="n">Gl</span> <span class="p">{</span>
    <span class="k">type</span> <span class="n">F32</span> <span class="o">=</span> <span class="nb">f32</span><span class="p">;</span>
    <span class="k">type</span> <span class="n">I32</span> <span class="o">=</span> <span class="nb">i32</span><span class="p">;</span>
    <span class="k">type</span> <span class="n">U32</span> <span class="o">=</span> <span class="nb">u32</span><span class="p">;</span>
    <span class="k">type</span> <span class="n">Vec2</span> <span class="o">=</span> <span class="nn">gl</span><span class="p">::</span><span class="n">Vec2</span><span class="p">;</span>
    <span class="c1">// ...</span>
<span class="p">}</span>

<span class="k">impl</span> <span class="n">BlockDom</span> <span class="k">for</span> <span class="n">Sl</span> <span class="p">{</span>
    <span class="k">type</span> <span class="n">F32</span> <span class="o">=</span> <span class="nn">sl</span><span class="p">::</span><span class="n">F32</span><span class="p">;</span>
    <span class="k">type</span> <span class="n">I32</span> <span class="o">=</span> <span class="nn">sl</span><span class="p">::</span><span class="n">I32</span><span class="p">;</span>
    <span class="k">type</span> <span class="n">U32</span> <span class="o">=</span> <span class="nn">sl</span><span class="p">::</span><span class="n">U32</span><span class="p">;</span>
    <span class="k">type</span> <span class="n">Vec2</span> <span class="o">=</span> <span class="nn">sl</span><span class="p">::</span><span class="n">Vec2</span><span class="p">;</span>
    <span class="c1">// ...</span>
<span class="p">}</span>
</code></pre></div></div>

<p>With these definitions, <code class="language-plaintext highlighter-rouge">Block&lt;D&gt;</code> is defined as follows:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Defined in `posh`:</span>

<span class="k">pub</span> <span class="k">unsafe</span> <span class="k">trait</span> <span class="n">Block</span><span class="o">&lt;</span><span class="n">D</span><span class="p">:</span> <span class="n">BlockDom</span><span class="o">&gt;</span><span class="p">:</span> <span class="n">ToSl</span> <span class="p">{</span>
    <span class="k">type</span> <span class="n">Gl</span><span class="p">:</span> <span class="n">Block</span><span class="o">&lt;</span><span class="n">Gl</span><span class="o">&gt;</span> <span class="o">+</span> <span class="n">AsStd140</span> <span class="o">+</span> <span class="n">Pod</span> <span class="o">+</span> <span class="n">ToSl</span><span class="o">&lt;</span><span class="n">Output</span> <span class="o">=</span> <span class="k">Self</span><span class="p">::</span><span class="n">Sl</span><span class="o">&gt;</span><span class="p">;</span>
    <span class="k">type</span> <span class="n">Sl</span><span class="p">:</span> <span class="n">Block</span><span class="o">&lt;</span><span class="n">Sl</span><span class="o">&gt;</span> <span class="o">+</span> <span class="nn">sl</span><span class="p">::</span><span class="n">Interpolant</span> <span class="o">+</span> <span class="n">ToSl</span><span class="o">&lt;</span><span class="n">Output</span> <span class="o">=</span> <span class="k">Self</span><span class="p">::</span><span class="n">Sl</span><span class="o">&gt;</span><span class="p">;</span>
    <span class="c1">// ... ignoring implementation details ...</span>
<span class="p">}</span>
</code></pre></div></div>
<p>The associated types <code class="language-plaintext highlighter-rouge">Block::Gl</code> and <code class="language-plaintext highlighter-rouge">Block::Sl</code> enable <code class="language-plaintext highlighter-rouge">posh</code> to map between the shading language representation and the graphics library representation of <code class="language-plaintext highlighter-rouge">MyGlobals&lt;D&gt;</code>.
The bounds on the associated types specify the required traits that the struct needs to implement in the respective domains.</p>

<p>Let us revisit the <code class="language-plaintext highlighter-rouge">MyGlobals&lt;D&gt;</code> type from the initial example:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">#[derive(Clone,</span> <span class="nd">Copy,</span> <span class="nd">Block)]</span>
<span class="k">struct</span> <span class="n">MyGlobals</span><span class="o">&lt;</span><span class="n">D</span><span class="p">:</span> <span class="n">BlockDom</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="n">time</span><span class="p">:</span> <span class="nn">D</span><span class="p">::</span><span class="n">F32</span><span class="p">,</span>
    <span class="n">size</span><span class="p">:</span> <span class="nn">D</span><span class="p">::</span><span class="n">Vec2</span><span class="p">,</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">Block</code> derive macro generates the necessary trait implementations for <code class="language-plaintext highlighter-rouge">MyGlobals&lt;Gl&gt;</code> and <code class="language-plaintext highlighter-rouge">MyGlobals&lt;Sl&gt;</code>:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Generated by `derive(Block)`:</span>

<span class="c1">// ... impl `AsStd140`, `Pod`, and `ToSl` for `MyGlobals&lt;Gl&gt;` ...</span>
<span class="c1">// ... impl `sl::Value` and `sl::Interpolant` for `MyGlobals&lt;Sl&gt;` ...</span>

<span class="k">unsafe</span> <span class="k">impl</span> <span class="n">Block</span><span class="o">&lt;</span><span class="n">Gl</span><span class="o">&gt;</span> <span class="k">for</span> <span class="n">MyGlobals</span><span class="o">&lt;</span><span class="n">Gl</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="k">type</span> <span class="n">Gl</span> <span class="o">=</span> <span class="n">MyGlobals</span><span class="o">&lt;</span><span class="n">Gl</span><span class="o">&gt;</span><span class="p">;</span>
    <span class="k">type</span> <span class="n">Sl</span> <span class="o">=</span> <span class="n">MyGlobals</span><span class="o">&lt;</span><span class="n">Sl</span><span class="o">&gt;</span><span class="p">;</span>
    <span class="c1">// ...</span>
<span class="p">}</span>

<span class="k">unsafe</span> <span class="k">impl</span> <span class="n">Block</span><span class="o">&lt;</span><span class="n">Sl</span><span class="o">&gt;</span> <span class="k">for</span> <span class="n">MyGlobals</span><span class="o">&lt;</span><span class="n">Sl</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="k">type</span> <span class="n">Gl</span> <span class="o">=</span> <span class="n">MyGlobals</span><span class="o">&lt;</span><span class="n">Gl</span><span class="o">&gt;</span><span class="p">;</span>
    <span class="k">type</span> <span class="n">Sl</span> <span class="o">=</span> <span class="n">MyGlobals</span><span class="o">&lt;</span><span class="n">Sl</span><span class="o">&gt;</span><span class="p">;</span>
    <span class="c1">// ...</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Core types like <code class="language-plaintext highlighter-rouge">f32</code>, <code class="language-plaintext highlighter-rouge">i32</code>, <code class="language-plaintext highlighter-rouge">u32</code>, <code class="language-plaintext highlighter-rouge">gl::Vec2</code>, etc. already come with implementations of <code class="language-plaintext highlighter-rouge">Block&lt;Gl&gt;</code>.
Therefore, they can be used directly in uniform buffers or vertex buffers without requiring a custom type definition.</p>

<h4 id="uniform-interface">Uniform Interface</h4>

<p>The uniform interface contains data that is constant on the level of individual draw calls.
It encompasses uniform blocks and texture samplers and is the first argument passed to shader functions.</p>

<p>Similar to other interface traits, <code class="language-plaintext highlighter-rouge">UniformInterface&lt;D&gt;</code> has a corresponding domain trait that provides a mapping for the types that can be used as uniform inputs:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Defined in `posh`:</span>

<span class="k">pub</span> <span class="k">trait</span> <span class="n">UniformInterfaceDom</span> <span class="p">{</span>
    <span class="k">type</span> <span class="n">Block</span><span class="o">&lt;</span><span class="n">B</span><span class="p">:</span> <span class="n">Block</span><span class="o">&lt;</span><span class="n">Sl</span><span class="p">,</span> <span class="n">Sl</span> <span class="o">=</span> <span class="n">B</span><span class="o">&gt;&gt;</span><span class="p">:</span> <span class="n">UniformInterface</span><span class="o">&lt;</span><span class="k">Self</span><span class="o">&gt;</span><span class="p">;</span>
    <span class="k">type</span> <span class="n">ColorSampler2d</span><span class="o">&lt;</span><span class="n">S</span><span class="p">:</span> <span class="nn">sl</span><span class="p">::</span><span class="n">ColorSample</span><span class="o">&gt;</span><span class="p">:</span> <span class="n">UniformInterface</span><span class="o">&lt;</span><span class="k">Self</span><span class="o">&gt;</span><span class="p">;</span>
    <span class="c1">// ...</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The implementation of <code class="language-plaintext highlighter-rouge">UniformInterfaceDom</code> for <code class="language-plaintext highlighter-rouge">posh::Gl</code> provides uniform binding types, while <code class="language-plaintext highlighter-rouge">posh::Sl</code> provides types for accessing uniforms in shader definitions:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Implemented in `posh`:</span>

<span class="k">impl</span> <span class="n">UniformInterfaceDom</span> <span class="k">for</span> <span class="n">Gl</span> <span class="p">{</span>
    <span class="k">type</span> <span class="n">Block</span><span class="o">&lt;</span><span class="n">B</span><span class="p">:</span> <span class="n">Block</span><span class="o">&lt;</span><span class="n">Sl</span><span class="p">,</span> <span class="n">Sl</span> <span class="o">=</span> <span class="n">B</span><span class="o">&gt;&gt;</span> <span class="o">=</span> <span class="nn">gl</span><span class="p">::</span><span class="n">UniformBufferBinding</span><span class="o">&lt;</span><span class="n">B</span><span class="o">&gt;</span><span class="p">;</span>
    <span class="k">type</span> <span class="n">ColorSampler2d</span><span class="o">&lt;</span><span class="n">S</span><span class="p">:</span> <span class="nn">sl</span><span class="p">::</span><span class="n">ColorSample</span><span class="o">&gt;</span> <span class="o">=</span> <span class="nn">gl</span><span class="p">::</span><span class="n">ColorSampler2d</span><span class="o">&lt;</span><span class="n">S</span><span class="o">&gt;</span><span class="p">;</span>
    <span class="c1">// ...</span>
<span class="p">}</span>

<span class="k">impl</span> <span class="n">UniformInterfaceDom</span> <span class="k">for</span> <span class="n">Sl</span> <span class="p">{</span>
    <span class="k">type</span> <span class="n">Block</span><span class="o">&lt;</span><span class="n">B</span><span class="p">:</span> <span class="n">Block</span><span class="o">&lt;</span><span class="n">Sl</span><span class="p">,</span> <span class="n">Sl</span> <span class="o">=</span> <span class="n">B</span><span class="o">&gt;&gt;</span> <span class="o">=</span> <span class="n">B</span><span class="p">;</span>
    <span class="k">type</span> <span class="n">ColorSampler2d</span><span class="o">&lt;</span><span class="n">S</span><span class="p">:</span> <span class="nn">sl</span><span class="p">::</span><span class="n">ColorSample</span><span class="o">&gt;</span> <span class="o">=</span> <span class="nn">sl</span><span class="p">::</span><span class="n">ColorSampler2d</span><span class="o">&lt;</span><span class="n">S</span><span class="o">&gt;</span><span class="p">;</span>
    <span class="c1">// ...</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The definition of <code class="language-plaintext highlighter-rouge">UniformInterface&lt;D&gt;</code> is straightforward.
Like <code class="language-plaintext highlighter-rouge">Block&lt;D&gt;</code> and other interface traits, it provides associated types <code class="language-plaintext highlighter-rouge">Gl</code> and <code class="language-plaintext highlighter-rouge">Sl</code> for mapping the implementing struct between the two domains.</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Defined in `posh`:</span>

<span class="k">pub</span> <span class="k">unsafe</span> <span class="k">trait</span> <span class="n">UniformInterface</span><span class="o">&lt;</span><span class="n">D</span><span class="p">:</span> <span class="n">UniformInterfaceDom</span><span class="o">&gt;</span><span class="p">:</span> <span class="nb">Sized</span> <span class="p">{</span>
    <span class="k">type</span> <span class="n">Gl</span><span class="p">:</span> <span class="n">UniformInterface</span><span class="o">&lt;</span><span class="n">Gl</span><span class="o">&gt;</span><span class="p">;</span>
    <span class="k">type</span> <span class="n">Sl</span><span class="p">:</span> <span class="n">UniformInterface</span><span class="o">&lt;</span><span class="n">Sl</span><span class="o">&gt;</span><span class="p">;</span>
    <span class="c1">// ... ignoring implementation details ...</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Recalling <code class="language-plaintext highlighter-rouge">SceneUniforms&lt;D&gt;</code> from the shadow mapping example shown earlier, we used a derive macro to implement <code class="language-plaintext highlighter-rouge">UniformInterface&lt;D&gt;</code> for a custom type.</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">#[derive(Clone,</span> <span class="nd">UniformInterface)]</span>
<span class="k">struct</span> <span class="n">SceneUniforms</span><span class="o">&lt;</span><span class="n">D</span><span class="p">:</span> <span class="n">UniformInterfaceDom</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="n">camera</span><span class="p">:</span> <span class="nn">D</span><span class="p">::</span><span class="n">Block</span><span class="o">&lt;</span><span class="n">Camera</span><span class="o">&lt;</span><span class="n">Sl</span><span class="o">&gt;&gt;</span><span class="p">,</span>
    <span class="n">light</span><span class="p">:</span> <span class="nn">D</span><span class="p">::</span><span class="n">Block</span><span class="o">&lt;</span><span class="n">Light</span><span class="o">&lt;</span><span class="n">Sl</span><span class="o">&gt;&gt;</span><span class="p">,</span>
    <span class="n">light_depth_map</span><span class="p">:</span> <span class="nn">D</span><span class="p">::</span><span class="n">ComparisonSampler2d</span><span class="p">,</span>
<span class="p">}</span>
</code></pre></div></div>

<p>As a result of implementing <code class="language-plaintext highlighter-rouge">UniformInterface&lt;D&gt;</code>, we can use <code class="language-plaintext highlighter-rouge">SceneUniforms&lt;Sl&gt;</code> as uniform shader input (i.e., the first argument in shader functions) and <code class="language-plaintext highlighter-rouge">SceneUniforms&lt;Gl&gt;</code> to provide uniform bindings for draw calls.</p>

<p>Types that implement <code class="language-plaintext highlighter-rouge">Block&lt;D&gt;</code> can be used directly as <code class="language-plaintext highlighter-rouge">UniformInterface&lt;D&gt;</code> without the need to define a custom type that contains the block.
This is achieved through blanket implementations provided by <code class="language-plaintext highlighter-rouge">posh</code>:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Implemented in `posh`:</span>

<span class="k">unsafe</span> <span class="k">impl</span><span class="o">&lt;</span><span class="n">B</span><span class="p">:</span> <span class="n">Block</span><span class="o">&lt;</span><span class="n">Sl</span><span class="p">,</span> <span class="n">Sl</span> <span class="o">=</span> <span class="n">B</span><span class="o">&gt;&gt;</span> <span class="n">UniformInterface</span><span class="o">&lt;</span><span class="n">Gl</span><span class="o">&gt;</span>
    <span class="k">for</span> <span class="nn">gl</span><span class="p">::</span><span class="n">UniformBufferBinding</span><span class="o">&lt;</span><span class="n">U</span><span class="o">&gt;</span> 
<span class="p">{</span>
    <span class="k">type</span> <span class="n">Gl</span> <span class="o">=</span> <span class="nn">gl</span><span class="p">::</span><span class="n">UniformBufferBinding</span><span class="o">&lt;</span><span class="n">B</span><span class="o">&gt;</span><span class="p">;</span>
    <span class="k">type</span> <span class="n">Sl</span> <span class="o">=</span> <span class="n">B</span><span class="p">;</span>
    <span class="c1">// ...</span>
<span class="p">}</span>

<span class="k">unsafe</span> <span class="k">impl</span><span class="o">&lt;</span><span class="n">B</span><span class="p">:</span> <span class="n">Block</span><span class="o">&lt;</span><span class="n">Sl</span><span class="p">,</span> <span class="n">Sl</span> <span class="o">=</span> <span class="n">B</span><span class="o">&gt;&gt;</span> <span class="n">UniformInterface</span><span class="o">&lt;</span><span class="n">Sl</span><span class="o">&gt;</span> <span class="k">for</span> <span class="n">B</span> <span class="p">{</span>
    <span class="k">type</span> <span class="n">Gl</span> <span class="o">=</span> <span class="nn">gl</span><span class="p">::</span><span class="n">UniformBufferBinding</span><span class="o">&lt;</span><span class="n">B</span><span class="o">&gt;</span><span class="p">;</span>
    <span class="k">type</span> <span class="n">Sl</span> <span class="o">=</span> <span class="n">B</span><span class="p">;</span>
    <span class="c1">// ...</span>
<span class="p">}</span>
</code></pre></div></div>
<p>The same applies to sampler types.</p>

<p>In practice, the vertex shader often requires a different subset of uniform inputs than the fragment shader.
However, <code class="language-plaintext highlighter-rouge">gl::Program&lt;U, ...&gt;</code> specifies only a single type <code class="language-plaintext highlighter-rouge">U: UniformInterface&lt;Sl&gt;</code>.
To simplify the definition of shader functions, the uniform types required by the fragment shader and the vertex shader can be unified into a single uniform type using the <code class="language-plaintext highlighter-rouge">UniformUnion</code> trait.</p>

<p>The <code class="language-plaintext highlighter-rouge">UniformUnion</code> trait allows for the following unifications (assuming <code class="language-plaintext highlighter-rouge">U, U1, U2: UniformInterface&lt;Sl&gt;</code>):</p>
<ol>
  <li><code class="language-plaintext highlighter-rouge">U + () -&gt; U</code> if <code class="language-plaintext highlighter-rouge">U ≠ ()</code>.</li>
  <li><code class="language-plaintext highlighter-rouge">() + U -&gt; U</code> if <code class="language-plaintext highlighter-rouge">U ≠ ()</code>.</li>
  <li><code class="language-plaintext highlighter-rouge">() + () -&gt; ()</code>.</li>
  <li><code class="language-plaintext highlighter-rouge">U + U -&gt; U</code> if <code class="language-plaintext highlighter-rouge">U ≠ ()</code>.</li>
  <li><code class="language-plaintext highlighter-rouge">U1 + U2 -&gt; (U1, U2)</code> if <code class="language-plaintext highlighter-rouge">U1 ≠ ()</code> and <code class="language-plaintext highlighter-rouge">U2 ≠ ()</code>.</li>
  <li><code class="language-plaintext highlighter-rouge">U1 + (U1, U2) -&gt; (U1, U2)</code> if <code class="language-plaintext highlighter-rouge">U1 ≠ ()</code>.</li>
  <li><code class="language-plaintext highlighter-rouge">(U1, U2) + U1 -&gt; (U1, U2)</code> if <code class="language-plaintext highlighter-rouge">U1 ≠ ()</code>.</li>
</ol>

<p>For example, using unification #5, we can receive a uniform block containing a projection matrix in a vertex shader and a uniform sampler in a fragment shader, and compile these two shaders into a program that takes the pair of the two individual uniform inputs as follows:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fn</span> <span class="nf">vertex_shader</span><span class="p">(</span><span class="n">projection</span><span class="p">:</span> <span class="nn">sl</span><span class="p">::</span><span class="n">Mat4</span><span class="p">,</span> <span class="n">vertex</span><span class="p">:</span> <span class="nn">sl</span><span class="p">::</span><span class="n">Vec4</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nn">sl</span><span class="p">::</span><span class="n">Vec4</span> <span class="p">{</span>
    <span class="nd">todo!</span><span class="p">()</span>
<span class="p">}</span>

<span class="k">fn</span> <span class="nf">fragment_shader</span><span class="p">(</span><span class="n">sampler</span><span class="p">:</span> <span class="nn">sl</span><span class="p">::</span><span class="n">ColorSampler2d</span><span class="p">,</span> <span class="n">interpolant</span><span class="p">:</span> <span class="p">())</span> <span class="k">-&gt;</span> <span class="nn">sl</span><span class="p">::</span><span class="n">Vec4</span> <span class="p">{</span>
    <span class="nd">todo!</span><span class="p">()</span>
<span class="p">}</span>

<span class="c1">// The uniform input types of the two shader functions are unified to a pair.</span>
<span class="k">let</span> <span class="n">program</span><span class="p">:</span> <span class="nn">gl</span><span class="p">::</span><span class="n">Program</span><span class="o">&lt;</span><span class="p">(</span><span class="nn">sl</span><span class="p">::</span><span class="n">Mat4</span><span class="p">,</span> <span class="nn">sl</span><span class="p">::</span><span class="n">ColorSampler2d</span><span class="p">)</span><span class="o">&gt;</span> <span class="o">=</span> <span class="n">gl</span><span class="nf">.create_program</span><span class="p">(</span>
    <span class="n">vertex_shader</span><span class="p">,</span>
    <span class="n">fragment_shader</span><span class="p">,</span>
<span class="p">);</span>
</code></pre></div></div>

<h4 id="vertex-shader-interface">Vertex Shader Interface</h4>

<p>In most cases, defining a custom vertex struct containing attributes (such as position, color, etc.), and deriving <code class="language-plaintext highlighter-rouge">Block&lt;D&gt;</code> for it is sufficient.
Such vertex data can be stored in a vertex buffer in the graphics library, and individual vertex values can be read from it in the shading language.</p>

<p>However, in certain scenarios like instanced rendering, it becomes necessary to bind vertex data from multiple vertex buffers.
To support this, <code class="language-plaintext highlighter-rouge">posh</code> allows users to implement <code class="language-plaintext highlighter-rouge">VsInterface&lt;D&gt;</code> for their own structs.</p>

<p>The corresponding domain trait is defined as follows:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Defined in `posh`:</span>

<span class="k">pub</span> <span class="k">trait</span> <span class="n">VsInterfaceDom</span><span class="p">:</span> <span class="n">BlockDom</span> <span class="p">{</span>
    <span class="k">type</span> <span class="n">Block</span><span class="o">&lt;</span><span class="n">B</span><span class="p">:</span> <span class="n">Block</span><span class="o">&lt;</span><span class="n">Sl</span><span class="o">&gt;&gt;</span><span class="p">:</span> <span class="n">VertexField</span><span class="o">&lt;</span><span class="k">Self</span><span class="o">&gt;</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Its implementation for <code class="language-plaintext highlighter-rouge">posh::Gl</code> provides bindings of vertex buffers, while <code class="language-plaintext highlighter-rouge">posh::Sl</code> allows reading from an individual vertex input:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Implemented in `posh`:</span>

<span class="k">impl</span> <span class="n">VsInterfaceDom</span> <span class="k">for</span> <span class="n">Gl</span> <span class="p">{</span>
    <span class="k">type</span> <span class="n">Block</span><span class="o">&lt;</span><span class="n">B</span><span class="p">:</span> <span class="n">Block</span><span class="o">&lt;</span><span class="n">Sl</span><span class="o">&gt;&gt;</span> <span class="o">=</span> <span class="nn">gl</span><span class="p">::</span><span class="n">VertexBufferBinding</span><span class="o">&lt;</span><span class="n">B</span><span class="o">&gt;</span><span class="p">;</span>
<span class="p">}</span>

<span class="k">impl</span> <span class="n">VsInterfaceDom</span> <span class="k">for</span> <span class="n">Sl</span> <span class="p">{</span>
    <span class="k">type</span> <span class="n">Block</span><span class="o">&lt;</span><span class="n">B</span><span class="p">:</span> <span class="n">Block</span><span class="o">&lt;</span><span class="n">Sl</span><span class="o">&gt;&gt;</span> <span class="o">=</span> <span class="n">B</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Based on this, the interface trait is defined as follows, following the pattern of other interface traits:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Defined in `posh`:</span>

<span class="k">pub</span> <span class="k">unsafe</span> <span class="k">trait</span> <span class="n">VsInterface</span><span class="o">&lt;</span><span class="n">D</span><span class="p">:</span> <span class="n">VsInterfaceDom</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="k">type</span> <span class="n">Gl</span><span class="p">:</span> <span class="n">VsInterface</span><span class="o">&lt;</span><span class="n">Gl</span><span class="o">&gt;</span><span class="p">;</span>
    <span class="k">type</span> <span class="n">Sl</span><span class="p">:</span> <span class="n">VsInterface</span><span class="o">&lt;</span><span class="n">Sl</span><span class="o">&gt;</span><span class="p">;</span>
    <span class="c1">// ...</span>
<span class="p">}</span>
</code></pre></div></div>

<p>In the <a href="https://github.com/leod/posh/blob/main/examples/instancing.rs">instancing example</a> in the repository, a custom struct <code class="language-plaintext highlighter-rouge">VsInput&lt;D&gt;</code> is defined, which contains both per-instance input (a custom block type, <code class="language-plaintext highlighter-rouge">Instance&lt;D&gt;</code>) and per-vertex input (a position vector).
These inputs can then be accessed in the vertex shader to obtain their current values:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">#[derive(Clone,</span> <span class="nd">Copy,</span> <span class="nd">Block)]</span>
<span class="nd">#[repr(C)]</span>
<span class="k">struct</span> <span class="n">Instance</span><span class="o">&lt;</span><span class="n">D</span><span class="p">:</span> <span class="n">BlockDom</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="n">model_to_view</span><span class="p">:</span> <span class="nn">D</span><span class="p">::</span><span class="n">Mat4</span><span class="p">,</span>
    <span class="n">color</span><span class="p">:</span> <span class="nn">D</span><span class="p">::</span><span class="n">Vec3</span><span class="p">,</span>
<span class="p">}</span>

<span class="nd">#[derive(Copy,</span> <span class="nd">Clone,</span> <span class="nd">VsInterface)]</span>
<span class="k">struct</span> <span class="n">VsInput</span><span class="o">&lt;</span><span class="n">D</span><span class="p">:</span> <span class="n">VsInterfaceDom</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="n">instance</span><span class="p">:</span> <span class="nn">D</span><span class="p">::</span><span class="n">Block</span><span class="o">&lt;</span><span class="n">Instance</span><span class="o">&lt;</span><span class="n">Sl</span><span class="o">&gt;&gt;</span><span class="p">,</span>
    <span class="n">model_pos</span><span class="p">:</span> <span class="nn">D</span><span class="p">::</span><span class="n">Block</span><span class="o">&lt;</span><span class="nn">sl</span><span class="p">::</span><span class="n">Vec3</span><span class="o">&gt;</span><span class="p">,</span>
<span class="p">}</span>

<span class="k">fn</span> <span class="nf">vertex_shader</span><span class="p">(</span>
    <span class="n">camera</span><span class="p">:</span> <span class="n">Camera</span><span class="o">&lt;</span><span class="n">Sl</span><span class="o">&gt;</span><span class="p">,</span>
    <span class="n">vertex</span><span class="p">:</span> <span class="n">VsInput</span><span class="o">&lt;</span><span class="n">Sl</span><span class="o">&gt;</span><span class="p">,</span>
<span class="p">)</span> <span class="k">-&gt;</span> <span class="nn">sl</span><span class="p">::</span><span class="n">VsOutput</span><span class="o">&lt;</span><span class="nn">sl</span><span class="p">::</span><span class="n">Vec3</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="nn">sl</span><span class="p">::</span><span class="n">VsOutput</span> <span class="p">{</span>
        <span class="n">clip_position</span><span class="p">:</span> <span class="n">camera</span><span class="py">.view_to_screen</span>
            <span class="o">*</span> <span class="n">camera</span><span class="py">.world_to_view</span>
            <span class="o">*</span> <span class="n">vertex</span><span class="py">.instance.model_to_view</span>
            <span class="o">*</span> <span class="n">vertex</span><span class="py">.model_pos</span><span class="nf">.extend</span><span class="p">(</span><span class="mf">1.0</span><span class="p">),</span>
        <span class="n">interpolant</span><span class="p">:</span> <span class="n">vertex</span><span class="py">.instance.color</span><span class="p">,</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>On the host side, individual vertex buffer bindings need to be provided for instance data and vertex data.
To mark a vertex buffer binding as per-instance data, the <code class="language-plaintext highlighter-rouge">with_instancing()</code> method is used.</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="n">program</span><span class="p">:</span> <span class="nn">gl</span><span class="p">::</span><span class="n">Program</span><span class="o">&lt;</span><span class="n">Camera</span><span class="o">&lt;</span><span class="n">Sl</span><span class="o">&gt;</span><span class="p">,</span> <span class="n">VsInput</span><span class="o">&lt;</span><span class="n">Sl</span><span class="o">&gt;&gt;</span> <span class="o">=</span> <span class="n">gl</span><span class="nf">.create_program</span><span class="p">(</span>
    <span class="n">vertex_shader</span><span class="p">,</span>
    <span class="n">fragment_shader</span><span class="p">,</span>
<span class="p">)</span><span class="o">?</span><span class="p">;</span>

<span class="c1">// Ignoring buffer creation...</span>
<span class="k">let</span> <span class="n">instances</span><span class="p">:</span> <span class="nn">gl</span><span class="p">::</span><span class="n">VertexBuffer</span><span class="o">&lt;</span><span class="n">Instance</span><span class="o">&lt;</span><span class="n">Gl</span><span class="o">&gt;&gt;</span> <span class="o">=</span> <span class="nd">todo!</span><span class="p">();</span>
<span class="k">let</span> <span class="n">teapot</span><span class="p">:</span> <span class="nn">gl</span><span class="p">::</span><span class="n">VertexBuffer</span><span class="o">&lt;</span><span class="nn">gl</span><span class="p">::</span><span class="n">Vec3</span><span class="o">&gt;</span> <span class="o">=</span> <span class="nd">todo!</span><span class="p">();</span>

<span class="n">program</span>
    <span class="c1">// ... other bindings ...</span>
    <span class="nf">.draw</span><span class="p">(</span>
        <span class="nn">gl</span><span class="p">::</span><span class="nn">VertexSpec</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="nn">gl</span><span class="p">::</span><span class="nn">PrimitiveMode</span><span class="p">::</span><span class="n">Triangles</span><span class="p">)</span>
            <span class="nf">.with_vertex_data</span><span class="p">(</span><span class="n">VsInput</span> <span class="p">{</span>
                <span class="n">instance</span><span class="p">:</span> <span class="n">instances</span><span class="nf">.as_binding</span><span class="p">()</span><span class="nf">.with_instancing</span><span class="p">(),</span>
                <span class="n">model_pos</span><span class="p">:</span> <span class="n">teapot</span><span class="nf">.as_binding</span><span class="p">(),</span>
            <span class="p">}),</span>
    <span class="p">)</span><span class="o">?</span><span class="p">;</span>
</code></pre></div></div>

<h4 id="fragment-shader-interface">Fragment Shader Interface</h4>

<p>Finally, let us take a look at custom fragment shader interfaces.
So far, the examples have computed a single <code class="language-plaintext highlighter-rouge">sl::Vec4</code> color in their fragment shaders.
However, in some cases, it may be necessary to compute multiple colors that are written into individual framebuffer attachments on the host side.
To achieve this, we need to implement <code class="language-plaintext highlighter-rouge">FsInterface&lt;D&gt;</code> for a custom struct.</p>

<p>The corresponding domain trait is defined as follows, where <code class="language-plaintext highlighter-rouge">sl::ColorSample</code> is a trait implemented for core types representing a single sample of the framebuffer in the shading language (e.g., <code class="language-plaintext highlighter-rouge">sl::F32</code> or <code class="language-plaintext highlighter-rouge">sl::Vec4</code>):</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Defined in `posh`:</span>

<span class="k">pub</span> <span class="k">trait</span> <span class="n">FsInterfaceDom</span> <span class="p">{</span>
    <span class="k">type</span> <span class="n">ColorAttachment</span><span class="o">&lt;</span><span class="n">S</span><span class="p">:</span> <span class="nn">sl</span><span class="p">::</span><span class="n">ColorSample</span><span class="o">&gt;</span><span class="p">:</span> <span class="n">FsInterface</span><span class="o">&lt;</span><span class="k">Self</span><span class="o">&gt;</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Its implementation for <code class="language-plaintext highlighter-rouge">posh::Gl</code> provides framebuffer attachments, while <code class="language-plaintext highlighter-rouge">posh::Sl</code> provides the type that contains a single output value to be written to the framebuffer in the shading language.</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Implemented in `posh`:</span>

<span class="nd">#[sealed]</span>
<span class="k">impl</span> <span class="n">FsInterfaceDom</span> <span class="k">for</span> <span class="n">Gl</span> <span class="p">{</span>
    <span class="k">type</span> <span class="n">ColorAttachment</span><span class="o">&lt;</span><span class="n">S</span><span class="p">:</span> <span class="nn">sl</span><span class="p">::</span><span class="n">ColorSample</span><span class="o">&gt;</span> <span class="o">=</span> <span class="nn">gl</span><span class="p">::</span><span class="n">ColorAttachment</span><span class="o">&lt;</span><span class="n">S</span><span class="o">&gt;</span><span class="p">;</span>
<span class="p">}</span>

<span class="nd">#[sealed]</span>
<span class="k">impl</span> <span class="n">FsInterfaceDom</span> <span class="k">for</span> <span class="n">Sl</span> <span class="p">{</span>
    <span class="k">type</span> <span class="n">ColorAttachment</span><span class="o">&lt;</span><span class="n">S</span><span class="p">:</span> <span class="nn">sl</span><span class="p">::</span><span class="n">ColorSample</span><span class="o">&gt;</span> <span class="o">=</span> <span class="n">S</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The interface trait follows the same pattern as other interface traits we have seen:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Defined in `posh`:</span>

<span class="k">pub</span> <span class="k">unsafe</span> <span class="k">trait</span> <span class="n">FsInterface</span><span class="o">&lt;</span><span class="n">D</span><span class="p">:</span> <span class="n">FsInterfaceDom</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="k">type</span> <span class="n">Gl</span><span class="p">:</span> <span class="n">FsInterface</span><span class="o">&lt;</span><span class="n">Gl</span><span class="o">&gt;</span><span class="p">;</span>
    <span class="k">type</span> <span class="n">Sl</span><span class="p">:</span> <span class="n">FsInterface</span><span class="o">&lt;</span><span class="n">Sl</span><span class="o">&gt;</span> <span class="o">+</span> <span class="nn">sl</span><span class="p">::</span><span class="n">Interpolant</span> <span class="o">+</span> <span class="n">ToSl</span><span class="o">&lt;</span><span class="n">Output</span> <span class="o">=</span> <span class="k">Self</span><span class="p">::</span><span class="n">Sl</span><span class="o">&gt;</span><span class="p">;</span>
    <span class="c1">// ...</span>
<span class="p">}</span>
</code></pre></div></div>

<p>A typical use case for a custom fragment shader interface is deferred shading, where the scene is first rendered to screen space textures containing information such as world position, world normal, or color.
In the <a href="https://github.com/leod/posh/blob/main/examples/deferred.rs">deferred example</a> in the repository, <code class="language-plaintext highlighter-rouge">FsInterface&lt;D&gt;</code> is derived for a custom struct that contains three output fields:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">#[derive(Clone,</span> <span class="nd">Copy,</span> <span class="nd">FsInterface)]</span>
<span class="k">pub</span> <span class="k">struct</span> <span class="n">SceneAttachments</span><span class="o">&lt;</span><span class="n">D</span><span class="p">:</span> <span class="n">FsInterfaceDom</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="n">albedo</span><span class="p">:</span> <span class="nn">D</span><span class="p">::</span><span class="n">ColorAttachment</span><span class="o">&lt;</span><span class="nn">sl</span><span class="p">::</span><span class="n">Vec3</span><span class="o">&gt;</span><span class="p">,</span>
    <span class="n">world_normal</span><span class="p">:</span> <span class="nn">D</span><span class="p">::</span><span class="n">ColorAttachment</span><span class="o">&lt;</span><span class="nn">sl</span><span class="p">::</span><span class="n">Vec3</span><span class="o">&gt;</span><span class="p">,</span>
    <span class="n">world_pos</span><span class="p">:</span> <span class="nn">D</span><span class="p">::</span><span class="n">ColorAttachment</span><span class="o">&lt;</span><span class="nn">sl</span><span class="p">::</span><span class="n">Vec3</span><span class="o">&gt;</span><span class="p">,</span>
<span class="p">}</span>
</code></pre></div></div>
<p>On the host side, the struct <code class="language-plaintext highlighter-rouge">SceneAttachments&lt;Gl&gt;</code> contains attachments of three individual textures that will be rendered into.
On the shading language side, the struct <code class="language-plaintext highlighter-rouge">SceneAttachments&lt;Sl&gt;</code> contains expressions in the shading language for the output values of a single fragment shader invocation.</p>

<p>In compiled programs, the shader signature type <code class="language-plaintext highlighter-rouge">F: FsInterface&lt;Sl&gt;</code> is captured as the third generic argument to <code class="language-plaintext highlighter-rouge">gl::Program&lt;U, V, F&gt;</code>.
By default, it is set to <code class="language-plaintext highlighter-rouge">sl::Vec4</code>, which is the fragment shader output type compatible with the default framebuffer.</p>

<h3 id="the-shading-language">The Shading Language</h3>

<p>The shading language <code class="language-plaintext highlighter-rouge">posh::sl</code> provides a set of types and primitives that are designed to enable the definition of shaders in readable code embedded in Rust. 
By embedding shaders in Rust, it becomes trivial to capture their type signatures.
This is what makes it possible to integrate typed shaders with the graphics library <code class="language-plaintext highlighter-rouge">posh::gl</code> based on the shader interface traits defined in the previous section.</p>

<p>This section takes a closer look at the types and primitives provided by <code class="language-plaintext highlighter-rouge">posh::sl</code>.
It also shows how these types enable transpiling user-defined shader functions to valid GLSL code at runtime.</p>

<h4 id="types">Types</h4>

<p>The shading language provides a variety of types that correspond to GLSL types.
Here are the available core types:</p>
<ul>
  <li>Scalar types <code class="language-plaintext highlighter-rouge">sl::F32</code>, <code class="language-plaintext highlighter-rouge">sl::I32</code>, <code class="language-plaintext highlighter-rouge">sl::U32</code>, <code class="language-plaintext highlighter-rouge">sl::Bool</code>.</li>
  <li>Floating-point vector types: <code class="language-plaintext highlighter-rouge">sl::Vec2</code>, <code class="language-plaintext highlighter-rouge">sl::Vec3</code>, and <code class="language-plaintext highlighter-rouge">sl::Vec4</code>.</li>
  <li>Integer vector types: <code class="language-plaintext highlighter-rouge">sl::IVec2</code>, <code class="language-plaintext highlighter-rouge">sl::IVec3</code>, and <code class="language-plaintext highlighter-rouge">sl::IVec4</code>.</li>
  <li>Unsigned integer vector types: <code class="language-plaintext highlighter-rouge">sl::UVec2</code>, <code class="language-plaintext highlighter-rouge">sl::UVec3</code>, and <code class="language-plaintext highlighter-rouge">sl::UVec4</code>.</li>
  <li>Boolean vector types: <code class="language-plaintext highlighter-rouge">sl::BVec2</code>, <code class="language-plaintext highlighter-rouge">sl::BVec3</code>, and <code class="language-plaintext highlighter-rouge">sl::BVec4</code>.</li>
  <li>Floating-point matrix types: <code class="language-plaintext highlighter-rouge">sl::Mat2</code>, <code class="language-plaintext highlighter-rouge">sl::Mat3</code>, and <code class="language-plaintext highlighter-rouge">sl::Mat4</code>.</li>
</ul>

<p>The API for vector and matrix types largely imitates <a href="https://github.com/bitshifter/glam-rs">glam</a>.
In some places, methods have been modified to better match GLSL.</p>

<p>Constant values can be converted to <code class="language-plaintext highlighter-rouge">posh::sl</code> with the <code class="language-plaintext highlighter-rouge">sl::ToSl</code> trait, which provides the <code class="language-plaintext highlighter-rouge">to_sl()</code> method.
For example, you can convert a <code class="language-plaintext highlighter-rouge">f32</code> to an <code class="language-plaintext highlighter-rouge">sl::F32</code> type as follows:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">use</span> <span class="nn">posh</span><span class="p">::</span><span class="n">ToSl</span><span class="p">;</span>

<span class="k">let</span> <span class="n">c</span><span class="p">:</span> <span class="nn">sl</span><span class="p">::</span><span class="n">F32</span> <span class="o">=</span> <span class="mf">5.0f32</span><span class="nf">.to_sl</span><span class="p">();</span>
</code></pre></div></div>

<p>The shading language also supports pairs of arbitrary types.
However, larger tuples are yet to be implemented.
Constant-size arrays are supported through <code class="language-plaintext highlighter-rouge">sl::Array&lt;V, N&gt;</code>, where <code class="language-plaintext highlighter-rouge">V</code> is the inner type and <code class="language-plaintext highlighter-rouge">const N: usize</code> is the size of the array.</p>

<p>In addition to these basic types, <code class="language-plaintext highlighter-rouge">posh::sl</code> provides sampler types such as <code class="language-plaintext highlighter-rouge">sl::ColorSampler2d&lt;S&gt;</code> and <code class="language-plaintext highlighter-rouge">sl::ComparisonSampler2d</code>.
However, some sampler types, like cube maps, are yet to be implemented.</p>

<h4 id="values">Values</h4>

<p>Internally, all types that occur in <code class="language-plaintext highlighter-rouge">posh::sl</code> expressions implement the trait <code class="language-plaintext highlighter-rouge">sl::Object</code>, which allows converting the value into an expression graph.</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Defined in `posh`:</span>

<span class="k">pub</span> <span class="k">trait</span> <span class="n">Object</span> <span class="p">{</span>
    <span class="k">fn</span> <span class="nf">ty</span><span class="p">()</span> <span class="k">-&gt;</span> <span class="n">Type</span><span class="p">;</span>

    <span class="c1">// Implementation detail:</span>
    <span class="k">fn</span> <span class="nf">expr</span><span class="p">(</span><span class="o">&amp;</span><span class="k">self</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">Rc</span><span class="o">&lt;</span><span class="n">Expr</span><span class="o">&gt;</span><span class="p">;</span>

    <span class="c1">// ...</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">Expr</code> type is an internal enum in <code class="language-plaintext highlighter-rouge">posh::sl</code> that represents an expression graph.
It is used for transpilation to GLSL.
It consists of different variants such as <code class="language-plaintext highlighter-rouge">Binary</code> for binary operations and <code class="language-plaintext highlighter-rouge">Field</code> for accessing struct fields.</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Defined in `posh`:</span>

<span class="k">enum</span> <span class="n">Expr</span> <span class="p">{</span>
    <span class="n">Binary</span> <span class="p">{</span>
        <span class="n">left</span><span class="p">:</span> <span class="nb">Rc</span><span class="o">&lt;</span><span class="n">Expr</span><span class="o">&gt;</span><span class="p">,</span>
        <span class="n">op</span><span class="p">:</span> <span class="n">BinaryOp</span><span class="p">,</span>
        <span class="n">right</span><span class="p">:</span> <span class="nb">Rc</span><span class="o">&lt;</span><span class="n">Expr</span><span class="o">&gt;</span><span class="p">,</span>
        <span class="n">ty</span><span class="p">:</span> <span class="n">Type</span><span class="p">,</span>
    <span class="p">},</span>
    <span class="n">Field</span> <span class="p">{</span>
        <span class="n">base</span><span class="p">:</span> <span class="nb">Rc</span><span class="o">&lt;</span><span class="n">Expr</span><span class="o">&gt;</span><span class="p">,</span>
        <span class="n">name</span><span class="p">:</span> <span class="o">&amp;</span><span class="k">'static</span> <span class="nb">str</span><span class="p">,</span>
        <span class="n">ty</span><span class="p">:</span> <span class="n">Type</span><span class="p">,</span>
    <span class="p">},</span>
    <span class="c1">// ...</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Most types in the shading language also implement the <code class="language-plaintext highlighter-rouge">Value</code> trait in addition to <code class="language-plaintext highlighter-rouge">Object</code>.
The <code class="language-plaintext highlighter-rouge">Value</code> trait indicates that a type can be stored in variables in the generated GLSL code.
Types implementing <code class="language-plaintext highlighter-rouge">Value</code> are called transparent.
This distinction is necessary due to a rule in GLSL, by which opaque types like <code class="language-plaintext highlighter-rouge">sampler2D</code> can not be stored in variables.
In order to implement <code class="language-plaintext highlighter-rouge">Value</code>, types need to provide a method for constructing <code class="language-plaintext highlighter-rouge">Self</code> from an <code class="language-plaintext highlighter-rouge">Expr</code>:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Defined in `posh`:</span>

<span class="k">pub</span> <span class="k">trait</span> <span class="n">Value</span><span class="p">:</span> <span class="n">Object</span> <span class="o">+</span> <span class="nb">Copy</span> <span class="o">+</span> <span class="n">ToSl</span><span class="o">&lt;</span><span class="n">Output</span> <span class="o">=</span> <span class="k">Self</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="c1">// Implementation detail:</span>
    <span class="k">fn</span> <span class="nf">from_expr</span><span class="p">(</span><span class="n">expr</span><span class="p">:</span> <span class="n">Expr</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="k">Self</span><span class="p">;</span>

    <span class="c1">// ...</span>
<span class="p">}</span>
</code></pre></div></div>
<p>The core types provided by the shading language implement the <code class="language-plaintext highlighter-rouge">Value</code> and <code class="language-plaintext highlighter-rouge">Object</code> traits as appropriate.
User-defined structs can implement <code class="language-plaintext highlighter-rouge">Value</code> using the derive macro <code class="language-plaintext highlighter-rouge">derive(Value)</code>.</p>

<p>One important detail to notice is that <code class="language-plaintext highlighter-rouge">Value</code> has <code class="language-plaintext highlighter-rouge">Copy</code> as a base trait.
This design choice was made to make shader code look more natural, as values in GLSL are implicitly copyable.
However, this poses a problem, because the <code class="language-plaintext highlighter-rouge">Expr</code> type cannot implement <code class="language-plaintext highlighter-rouge">Copy</code> due to its fields of type <code class="language-plaintext highlighter-rouge">Rc&lt;Expr&gt;</code>.
To work around this, posh uses a hack where <code class="language-plaintext highlighter-rouge">Rc&lt;Expr&gt;</code> instances are stored in a global <code class="language-plaintext highlighter-rouge">thread_local</code> registry of type <code class="language-plaintext highlighter-rouge">BTreeMap&lt;usize, Rc&lt;Expr&gt;&gt;</code>.
Values in the shading language then store the key for looking up the <code class="language-plaintext highlighter-rouge">Rc&lt;Expr&gt;</code> from this map with a wrapper type called <code class="language-plaintext highlighter-rouge">Trace</code>.
For example, the scalar floating-point type <code class="language-plaintext highlighter-rouge">sl::F32</code> is defined like this:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Defined in `posh`:</span>

<span class="nd">#[derive(Debug,</span> <span class="nd">Copy,</span> <span class="nd">Clone)]</span>
<span class="k">pub</span> <span class="k">struct</span> <span class="nf">F32</span><span class="p">(</span><span class="n">Trace</span><span class="p">);</span>

<span class="k">impl</span> <span class="n">Object</span> <span class="k">for</span> <span class="n">F32</span> <span class="p">{</span>
    <span class="k">fn</span> <span class="nf">ty</span><span class="p">()</span> <span class="k">-&gt;</span> <span class="n">Type</span> <span class="p">{</span>
        <span class="nn">Type</span><span class="p">::</span><span class="nf">BuiltIn</span><span class="p">(</span><span class="nn">BuiltInType</span><span class="p">::</span><span class="n">F32</span><span class="p">)</span>
    <span class="p">}</span>

    <span class="k">fn</span> <span class="nf">expr</span><span class="p">(</span><span class="o">&amp;</span><span class="k">self</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">Rc</span><span class="o">&lt;</span><span class="n">Expr</span><span class="o">&gt;</span> <span class="p">{</span>
        <span class="c1">// Look up `Rc&lt;Expr&gt;` from the global registry using our `Trace` key.</span>
        <span class="k">self</span><span class="na">.0</span><span class="nf">.expr</span><span class="p">()</span>
    <span class="p">}</span>
<span class="p">}</span>

<span class="k">impl</span> <span class="n">Value</span> <span class="k">for</span> <span class="n">F32</span> <span class="p">{</span>
    <span class="k">fn</span> <span class="nf">from_expr</span><span class="p">(</span><span class="n">expr</span><span class="p">:</span> <span class="n">Expr</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="k">Self</span> <span class="p">{</span>
        <span class="c1">// Make a new entry in the global registry and obtain a `Trace` key.</span>
        <span class="k">Self</span><span class="p">(</span><span class="nn">Trace</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="n">expr</span><span class="p">))</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>This approach allows values to be <code class="language-plaintext highlighter-rouge">Copy</code>, making shader code more intuitive to write.
However, it has some downsides, such as the question of invalidation of entries in the registry, which is currently not addressed in the implementation.</p>

<h4 id="primitives">Primitives</h4>

<p>The shading language <code class="language-plaintext highlighter-rouge">posh::sl</code> provides a range of primitive functions that allow the creation of complex expressions out of simpler ones.</p>

<p>For scalar types, vector types, and matrix types, the arithmetic operators are overloaded.
This enables, for example, vector-vector, vector-scalar, matrix-matrix, or matrix-vector operations.
These operators take the expressions of their inputs, represented as <code class="language-plaintext highlighter-rouge">Rc&lt;Expr&gt;</code>, and combine them into a new expression that describes the computation.
As an example, the addition operator is implemented for the scalar floating-point type <code class="language-plaintext highlighter-rouge">sl::F32</code> as follows:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Implemented in `posh`:</span>

<span class="k">impl</span> <span class="nn">std</span><span class="p">::</span><span class="nn">ops</span><span class="p">::</span><span class="nb">Add</span><span class="o">&lt;</span><span class="nn">sl</span><span class="p">::</span><span class="n">F32</span><span class="o">&gt;</span> <span class="k">for</span> <span class="nn">sl</span><span class="p">::</span><span class="n">F32</span> <span class="p">{</span>
    <span class="k">type</span> <span class="n">Output</span> <span class="o">=</span> <span class="k">Self</span><span class="p">;</span>

    <span class="k">fn</span> <span class="nf">add</span><span class="p">(</span><span class="k">self</span><span class="p">,</span> <span class="n">right</span><span class="p">:</span> <span class="nn">sl</span><span class="p">::</span><span class="n">F32</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="k">Self</span> <span class="p">{</span>
        <span class="nn">F32</span><span class="p">::</span><span class="nf">from_expr</span><span class="p">(</span><span class="nn">Expr</span><span class="p">::</span><span class="n">Binary</span> <span class="p">{</span>
            <span class="n">ty</span><span class="p">:</span> <span class="k">Self</span><span class="p">::</span><span class="nf">ty</span><span class="p">(),</span>
            <span class="n">left</span><span class="p">:</span> <span class="k">self</span><span class="nf">.expr</span><span class="p">(),</span>
            <span class="n">op</span><span class="p">:</span> <span class="nn">BinaryOp</span><span class="p">::</span><span class="nb">Add</span><span class="p">,</span>
            <span class="n">right</span><span class="p">:</span> <span class="n">right</span><span class="nf">.expr</span><span class="p">(),</span>
        <span class="p">})</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Other operators provided by <code class="language-plaintext highlighter-rouge">posh::sl</code> follow the same pattern, utilizing the expressions of their constituents to construct a new expression.</p>

<p>In addition to overloaded operators, there are functions and methods for additional primitives that cannot be defined through operator overloading in Rust:</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">sl::and</code> and <code class="language-plaintext highlighter-rouge">sl::or</code>: Binary boolean operations on <code class="language-plaintext highlighter-rouge">sl::Bool</code>.</li>
  <li><code class="language-plaintext highlighter-rouge">sl::all</code> and <code class="language-plaintext highlighter-rouge">sl::any</code>: Boolean operations on iterators of <code class="language-plaintext highlighter-rouge">sl::Bool</code>.</li>
  <li><code class="language-plaintext highlighter-rouge">sl::Value::eq</code>: Checks equality between two values.</li>
  <li>Methods like <code class="language-plaintext highlighter-rouge">sl::I32::lt</code> (less than) for comparing values.</li>
</ul>

<p>One important primitive function is <code class="language-plaintext highlighter-rouge">sl::branch(condition, yes, no)</code>, which represents a conditional expression.
If the <code class="language-plaintext highlighter-rouge">condition</code> is true, the <code class="language-plaintext highlighter-rouge">yes</code> value is returned; otherwise, the <code class="language-plaintext highlighter-rouge">no</code> is returned.
In the implementation of <code class="language-plaintext highlighter-rouge">posh::sl</code>, transpilation of this function requires special care to ensure correct scoping of variables.</p>

<p>It is worth noting that all of the provided primitives in <code class="language-plaintext highlighter-rouge">posh::sl</code> are functional, meaning they are stateless and do not have side effects.
As of now, <code class="language-plaintext highlighter-rouge">posh::sl</code> does not support mutable values, and there are no immediate plans to introduce them.
However, there are plans to provide a primitive function like <code class="language-plaintext highlighter-rouge">sl::iterate</code> to support loop constructs in the future.</p>

<h4 id="transpilation">Transpilation</h4>

<p>The shading language in <code class="language-plaintext highlighter-rouge">posh</code> has been specifically designed to obtain <code class="language-plaintext highlighter-rouge">Rc&lt;Expr&gt;</code> representations of user-defined shader functions.
This is achieved by evaluating the user’s vertex shader and fragment shader functions once at runtime, resulting in <code class="language-plaintext highlighter-rouge">posh::sl</code> values that carry expression graphs.</p>

<p>Now, given a list of <code class="language-plaintext highlighter-rouge">Rc&lt;Expr&gt;</code>, we could simply turn them into GLSL code recursively, but this is not practical.
By defining variables (with Rust’s usual <code class="language-plaintext highlighter-rouge">let</code> statement), it becomes possible for users to refer to the same expression multiple times.
This would lead to a possible exponential blowup in the size of the generated GLSL code.</p>

<p>Therefore, <code class="language-plaintext highlighter-rouge">posh::sl</code> takes care to turn expressions that are referred to multiple times into actual variables in the GLSL code.
This is done internally by <a href="https://github.com/leod/posh/blob/main/src/sl/codegen/var_form.rs"><code class="language-plaintext highlighter-rouge">VarForm</code></a>, which applies a topological sort to expressions.</p>

<p>However, the introduction of GLSL variables leads to a second challenge, since <code class="language-plaintext highlighter-rouge">posh</code> offers conditional expressions in the form of <code class="language-plaintext highlighter-rouge">sl::branch</code>.
If a user refers to an expression in only one of the branches, the inferred variable must be scoped to that branch.
<code class="language-plaintext highlighter-rouge">posh</code> addresses this problem by inferring a tree of scopes and placing variable declarations in the lowest common ancestor of all scopes that refer to the expression.
This is done internally by <a href="https://github.com/leod/posh/blob/main/src/sl/codegen/scope_form.rs"><code class="language-plaintext highlighter-rouge">ScopeForm</code></a>.</p>

<p>Here is an example. Consider this (nonsensical) vertex shader:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">#[derive(Copy,</span> <span class="nd">Clone,</span> <span class="nd">Block)]</span>
<span class="nd">#[repr(C)]</span>
<span class="k">struct</span> <span class="n">MyVertex</span><span class="o">&lt;</span><span class="n">D</span><span class="p">:</span> <span class="n">BlockDom</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="n">position</span><span class="p">:</span> <span class="nn">D</span><span class="p">::</span><span class="n">Vec4</span><span class="p">,</span>
    <span class="n">color</span><span class="p">:</span> <span class="nn">D</span><span class="p">::</span><span class="n">Vec4</span><span class="p">,</span>
<span class="p">}</span>

<span class="k">fn</span> <span class="nf">my_vertex_shader</span><span class="p">(</span>
    <span class="n">mode</span><span class="p">:</span> <span class="nn">sl</span><span class="p">::</span><span class="n">U32</span><span class="p">,</span>
    <span class="n">vertex</span><span class="p">:</span> <span class="n">MyVertex</span><span class="o">&lt;</span><span class="n">Sl</span><span class="o">&gt;</span><span class="p">,</span>
<span class="p">)</span> <span class="k">-&gt;</span> <span class="nn">sl</span><span class="p">::</span><span class="n">VsOutput</span><span class="o">&lt;</span><span class="n">MyVertex</span><span class="o">&lt;</span><span class="n">Sl</span><span class="o">&gt;&gt;</span> <span class="p">{</span>
    <span class="k">let</span> <span class="n">shifted_position</span> <span class="o">=</span> <span class="n">vertex</span><span class="py">.position</span> <span class="o">+</span> <span class="mf">2.0</span><span class="p">;</span>
    <span class="k">let</span> <span class="n">sin_position</span> <span class="o">=</span> <span class="n">shifted_position</span><span class="nf">.sin</span><span class="p">();</span>
    <span class="k">let</span> <span class="n">complex_vertex</span> <span class="o">=</span> <span class="nn">MyVertex</span><span class="p">::</span><span class="o">&lt;</span><span class="n">Sl</span><span class="o">&gt;</span> <span class="p">{</span>
        <span class="n">position</span><span class="p">:</span> <span class="n">sin_position</span><span class="nf">.cos</span><span class="p">()</span><span class="nf">.powf</span><span class="p">(</span><span class="mf">2.0</span><span class="p">),</span>
        <span class="n">color</span><span class="p">:</span> <span class="n">sin_position</span><span class="p">,</span>
    <span class="p">};</span>

    <span class="k">let</span> <span class="n">interpolant</span> <span class="o">=</span> <span class="nn">sl</span><span class="p">::</span><span class="nf">branch</span><span class="p">(</span><span class="n">mode</span><span class="nf">.eq</span><span class="p">(</span><span class="mi">42u32</span><span class="p">),</span> <span class="n">vertex</span><span class="p">,</span> <span class="n">complex_vertex</span><span class="p">);</span>

    <span class="nn">sl</span><span class="p">::</span><span class="n">VsOutput</span> <span class="p">{</span>
        <span class="n">clip_position</span><span class="p">:</span> <span class="n">vertex</span><span class="py">.position</span><span class="p">,</span>
        <span class="n">interpolant</span><span class="p">,</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>The generated GLSL code contains the definitions of user-defined structs used in the shader.
It also includes the inputs and outputs of the shader stage.
Notably, variables such as <code class="language-plaintext highlighter-rouge">var_0</code> and <code class="language-plaintext highlighter-rouge">var_1</code> are inferred since their expressions are referenced multiple times.
Crucially, <code class="language-plaintext highlighter-rouge">var_0</code> is scoped to the <code class="language-plaintext highlighter-rouge">else</code> branch because the expression is only used there.</p>
<div class="language-glsl highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">MyVertex_Posh0</span> <span class="p">{</span>
    <span class="kt">vec4</span> <span class="n">position</span><span class="p">;</span>
    <span class="kt">vec4</span> <span class="n">color</span><span class="p">;</span>
<span class="p">};</span>

<span class="k">layout</span><span class="p">(</span><span class="n">std140</span><span class="p">)</span> <span class="k">uniform</span> <span class="n">uniforms_posh_block</span> <span class="p">{</span>
    <span class="kt">uint</span> <span class="n">uniforms</span><span class="p">;</span>
<span class="p">};</span>

<span class="k">in</span> <span class="kt">vec4</span> <span class="n">vertex_input_position</span><span class="p">;</span>
<span class="k">in</span> <span class="kt">vec4</span> <span class="n">vertex_input_color</span><span class="p">;</span>
<span class="k">smooth</span> <span class="k">out</span> <span class="kt">vec4</span> <span class="n">vertex_output_position</span><span class="p">;</span>
<span class="k">smooth</span> <span class="k">out</span> <span class="kt">vec4</span> <span class="n">vertex_output_color</span><span class="p">;</span>

<span class="kt">void</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="n">MyVertex_Posh0</span> <span class="n">var_1</span><span class="p">;</span>
    <span class="k">if</span> <span class="p">((</span><span class="n">uniforms</span> <span class="o">==</span> <span class="mi">42u</span><span class="p">))</span> <span class="p">{</span>
        <span class="n">var_1</span> <span class="o">=</span> <span class="n">MyVertex_Posh0</span><span class="p">(</span><span class="n">vertex_input_position</span><span class="p">,</span> <span class="n">vertex_input_color</span><span class="p">);</span>
    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
        <span class="kt">vec4</span> <span class="n">var_0</span> <span class="o">=</span> <span class="n">sin</span><span class="p">((</span><span class="n">vertex_input_position</span> <span class="o">+</span> <span class="mi">2</span><span class="p">.</span><span class="mi">0</span><span class="p">));</span>
        <span class="n">var_1</span> <span class="o">=</span> <span class="n">MyVertex_Posh0</span><span class="p">(</span><span class="n">pow</span><span class="p">(</span><span class="n">cos</span><span class="p">(</span><span class="n">var_0</span><span class="p">),</span> <span class="p">(</span><span class="kt">vec4</span><span class="p">(</span><span class="mi">1</span><span class="p">.</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">.</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">.</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">.</span><span class="mi">0</span><span class="p">)</span> <span class="o">*</span> <span class="mi">2</span><span class="p">.</span><span class="mi">0</span><span class="p">)),</span> <span class="n">var_0</span><span class="p">);</span>
    <span class="p">}</span>
    <span class="nb">gl_Position</span> <span class="o">=</span> <span class="n">vertex_input_position</span><span class="p">;</span>
    <span class="n">vertex_output_position</span> <span class="o">=</span> <span class="n">var_1</span><span class="p">.</span><span class="n">position</span><span class="p">;</span>
    <span class="n">vertex_output_color</span> <span class="o">=</span> <span class="n">var_1</span><span class="p">.</span><span class="n">color</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<h3 id="the-graphics-library">The Graphics Library</h3>

<p>The graphics library <code class="language-plaintext highlighter-rouge">posh::gl</code> offers methods to create and update GPU buffers.
These GPU buffers can be used as bindings in draw calls as long as they match the signature of the shader.</p>

<p>This article will not delve into the full API details of <code class="language-plaintext highlighter-rouge">posh::gl</code>. However, here is a brief summary of the current GPU objects available:</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">VertexBuffer&lt;B&gt;</code> where <code class="language-plaintext highlighter-rouge">B: Block&lt;Gl&gt;</code>, which can be bound as a <code class="language-plaintext highlighter-rouge">VertexBufferBinding&lt;B&gt;</code>.</li>
  <li><code class="language-plaintext highlighter-rouge">ElementBuffer&lt;E&gt;</code> where <code class="language-plaintext highlighter-rouge">E</code> is <code class="language-plaintext highlighter-rouge">u16</code> or <code class="language-plaintext highlighter-rouge">u32</code>.</li>
  <li><code class="language-plaintext highlighter-rouge">UniformBuffer&lt;B&gt;</code> where <code class="language-plaintext highlighter-rouge">B: Block&lt;Gl&gt;</code>, which can be bound as a <code class="language-plaintext highlighter-rouge">UniformBufferBinding&lt;B&gt;</code>.</li>
  <li><code class="language-plaintext highlighter-rouge">ColorTexture2d&lt;S&gt;</code> where <code class="language-plaintext highlighter-rouge">S: ColorSample</code>, which can be bound as a <code class="language-plaintext highlighter-rouge">ColorSampler2d&lt;S&gt;</code>, or attached to a framebuffer as a <code class="language-plaintext highlighter-rouge">ColorAttachment&lt;S&gt;</code>.</li>
  <li><code class="language-plaintext highlighter-rouge">DepthTexture2d</code>, which can be bound as a <code class="language-plaintext highlighter-rouge">ColorSampler&lt;sl::F32&gt;</code>, or as a <code class="language-plaintext highlighter-rouge">ComparisonSampler2d</code>, or attached to a framebuffer as a <code class="language-plaintext highlighter-rouge">DepthAttachment</code>.</li>
</ul>

<p>Internally, these objects are generic wrappers around untyped OpenGL code in <code class="language-plaintext highlighter-rouge">posh::gl::raw</code>.
This design allows clear separation between the actual implementation of OpenGL code and the typed abstraction provided by the library.</p>

<p>The key component that ties everything together is the <code class="language-plaintext highlighter-rouge">Program&lt;U, V, F&gt;</code> type, where:</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">U: UniformInterface&lt;Sl&gt;</code> defines the uniform inputs of the program.</li>
  <li><code class="language-plaintext highlighter-rouge">V: VsInterface&lt;Sl&gt;</code> defines the inputs of the vertex shader.</li>
  <li><code class="language-plaintext highlighter-rouge">F: FsInterface&lt;Sl&gt;</code> defines the outputs of the fragment shader.</li>
</ul>

<p>Typically, users compile their shader functions into a <code class="language-plaintext highlighter-rouge">Program&lt;U, V, F&gt;</code> at the beginning of their program, and then use it for drawing in the main loop.</p>

<p>Now, finally, we can look at the <code class="language-plaintext highlighter-rouge">draw</code> method, which performs a draw call using compatible GPU buffer bindings.
To simplify the setup of draw calls, the necessary inputs are provided by using the builder pattern.
Here is an example path through the builder, focusing on the type signatures:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Implemented in `posh`:</span>

<span class="k">impl</span><span class="o">&lt;</span><span class="n">U</span><span class="p">,</span> <span class="n">V</span><span class="p">,</span> <span class="n">F</span><span class="o">&gt;</span> <span class="n">Program</span><span class="o">&lt;</span><span class="n">U</span><span class="p">,</span> <span class="n">V</span><span class="p">,</span> <span class="n">F</span><span class="o">&gt;</span>
<span class="k">where</span>
    <span class="n">U</span><span class="p">:</span> <span class="n">UniformInterface</span><span class="o">&lt;</span><span class="n">Sl</span><span class="o">&gt;</span><span class="p">,</span>
    <span class="n">V</span><span class="p">:</span> <span class="n">VsInterface</span><span class="o">&lt;</span><span class="n">Sl</span><span class="o">&gt;</span><span class="p">,</span>
    <span class="n">F</span><span class="p">:</span> <span class="n">FsInterface</span><span class="o">&lt;</span><span class="n">Sl</span><span class="o">&gt;</span><span class="p">,</span>
<span class="p">{</span>
    <span class="nd">#[must_use]</span>
    <span class="k">pub</span> <span class="k">fn</span> <span class="nf">with_uniforms</span><span class="p">(</span>
        <span class="o">&amp;</span><span class="k">self</span><span class="p">,</span>
        <span class="n">uniforms</span><span class="p">:</span> <span class="nn">U</span><span class="p">::</span><span class="n">Gl</span><span class="p">,</span>
    <span class="p">)</span> <span class="k">-&gt;</span> <span class="n">DrawBuilderWithUniforms</span><span class="o">&lt;</span><span class="n">U</span><span class="p">,</span> <span class="n">V</span><span class="p">,</span> <span class="n">F</span><span class="o">&gt;</span> <span class="p">{</span>
        <span class="c1">// ...</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>In this code snippet, <code class="language-plaintext highlighter-rouge">U::Gl</code> is used to map the representation of <code class="language-plaintext highlighter-rouge">U</code> from the shading language to its representation in the graphics library, through which uniform bindings are provided.</p>

<p>The <code class="language-plaintext highlighter-rouge">DrawBuilderWithUniforms&lt;U, V, F&gt;</code> struct serves as a helper for the builder, carrying the information that data for <code class="language-plaintext highlighter-rouge">U</code> has already been provided.
Once users have this struct, they only need to provide a vertex specification to complete the draw call:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Implemented in `posh`:</span>

<span class="k">impl</span><span class="o">&lt;</span><span class="n">U</span><span class="p">,</span> <span class="n">V</span><span class="o">&gt;</span> <span class="n">DrawBuilderWithUniforms</span><span class="o">&lt;</span><span class="n">U</span><span class="p">,</span> <span class="n">V</span><span class="p">,</span> <span class="nn">sl</span><span class="p">::</span><span class="n">Vec4</span><span class="o">&gt;</span>
<span class="k">where</span>
    <span class="n">U</span><span class="p">:</span> <span class="n">UniformInterface</span><span class="o">&lt;</span><span class="n">Sl</span><span class="o">&gt;</span><span class="p">,</span>
    <span class="n">V</span><span class="p">:</span> <span class="n">VsInterface</span><span class="o">&lt;</span><span class="n">Sl</span><span class="o">&gt;</span><span class="p">,</span>
<span class="p">{</span>
    <span class="k">pub</span> <span class="k">fn</span> <span class="nf">draw</span><span class="p">(</span><span class="k">self</span><span class="p">,</span> <span class="n">vertex_spec</span><span class="p">:</span> <span class="n">VertexSpec</span><span class="o">&lt;</span><span class="n">V</span><span class="o">&gt;</span><span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">Result</span><span class="o">&lt;</span><span class="k">Self</span><span class="p">,</span> <span class="n">DrawError</span><span class="o">&gt;</span> <span class="p">{</span>
        <span class="c1">// ... actually perform the draw call with `posh::gl::raw` ...</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Here, <code class="language-plaintext highlighter-rouge">VertexSpec&lt;V&gt;</code> represents a vertex stream compatible with <code class="language-plaintext highlighter-rouge">V</code>.
It contains bindings for the required vertex buffers, the primitive type, and optionally an element buffer.</p>

<p>In this example, we have not considered user-defined fragment shader interfaces, so this particular <code class="language-plaintext highlighter-rouge">draw</code> method is useful only if <code class="language-plaintext highlighter-rouge">F = sl::Vec4</code>.
There are other paths through the builder that allow providing framebuffers.</p>

<p>That is all!
By defining the draw method in this way, the library ensures that the GPU bindings are aligned with the shader signature, providing type safety and preventing mismatched bindings during draw calls.</p>]]></content><author><name></name></author><category term="rust" /><category term="gamedev" /><category term="posh" /><summary type="html"><![CDATA[This article introduces posh, an experimental Rust graphics library aimed at enhancing the type-safety, composability, and overall experience of graphics programming. The post covers the fundamental concepts of posh, showcases examples, discusses related work and limitations of the approach, and, for readers who might be interested in details, delves into the internal workings of the library.]]></summary></entry><entry><title type="html">Introduction to Rendology</title><link href="/rust/gamedev/rendology/2019/12/13/introduction-to-rendology.html" rel="alternate" type="text/html" title="Introduction to Rendology" /><published>2019-12-13T09:05:53+00:00</published><updated>2019-12-13T09:05:53+00:00</updated><id>/rust/gamedev/rendology/2019/12/13/introduction-to-rendology</id><content type="html" xml:base="/rust/gamedev/rendology/2019/12/13/introduction-to-rendology.html"><![CDATA[<p><img src="/assets/rendology_hdr_0_4.png" alt="Ultimate Scale screenshot" /></p>

<p><a href="https://github.com/leod/rendology"><em>Rendology</em></a> is a 3D rendering pipeline based on
<a href="https://github.com/glium/glium">Glium</a> and written in Rust. It features basic implementations of
<a href="https://learnopengl.com/Advanced-Lighting/Shadows/Shadow-Mapping">shadow mapping</a>,
<a href="https://learnopengl.com/Advanced-Lighting/Deferred-Shading">deferred shading</a>,
<a href="https://learnopengl.com/Advanced-Lighting/Bloom">a glow effect</a>,
<a href="https://en.wikipedia.org/wiki/Fast_approximate_anti-aliasing">FXAA</a>
and instanced rendering. In this blog post, I’ll outline some of the concepts of Rendology and
describe how they came to be this way.</p>

<p>Rendology defines shaders in a decomposed fashion, which allows writing functions that successively
<em>transform</em> shaders. This addresses the problem that arises when allowing arbitrary combinations of
the above effects as well as allowing users to implement custom scene shaders. Then, the pipeline
guides users through drawing a frame by mimicking a finite-state automaton on the type level.
Drawing a frame looks somewhat like this:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">self</span><span class="py">.rendology</span>
    <span class="nf">.start_frame</span><span class="p">(</span><span class="n">facade</span><span class="p">,</span> <span class="p">(</span><span class="mf">0.0</span><span class="p">,</span> <span class="mf">0.0</span><span class="p">,</span> <span class="mf">0.0</span><span class="p">),</span> <span class="n">context</span><span class="nf">.clone</span><span class="p">(),</span> <span class="n">target</span><span class="p">)</span><span class="o">?</span>
    <span class="nf">.shadow_pass</span><span class="p">()</span>
    <span class="nf">.draw</span><span class="p">(</span>
        <span class="o">&amp;</span><span class="k">self</span><span class="py">.my_shadow_pass</span><span class="p">,</span>
        <span class="o">&amp;</span><span class="n">scene</span><span class="py">.my_cubes</span><span class="nf">.as_drawable</span><span class="p">(</span><span class="o">&amp;</span><span class="k">self</span><span class="py">.cube</span><span class="p">),</span>
        <span class="o">&amp;</span><span class="n">my_params</span><span class="p">,</span>
        <span class="o">&amp;</span><span class="nn">Default</span><span class="p">::</span><span class="nf">default</span><span class="p">(),</span>
    <span class="p">)</span><span class="o">?</span>
    <span class="nf">.shaded_scene_pass</span><span class="p">()</span>
    <span class="nf">.draw</span><span class="p">(</span>
        <span class="o">&amp;</span><span class="k">self</span><span class="py">.scene_pass</span><span class="p">,</span>
        <span class="o">&amp;</span><span class="n">scene</span><span class="py">.cubes</span><span class="nf">.as_drawable</span><span class="p">(</span><span class="o">&amp;</span><span class="k">self</span><span class="py">.cube</span><span class="p">),</span>
        <span class="o">&amp;</span><span class="p">(),</span>
        <span class="o">&amp;</span><span class="n">draw_params</span><span class="p">,</span>
    <span class="p">)</span><span class="o">?</span>
    <span class="nf">.draw</span><span class="p">(</span>
        <span class="o">&amp;</span><span class="k">self</span><span class="py">.my_scene_pass</span><span class="p">,</span>
        <span class="o">&amp;</span><span class="n">scene</span><span class="py">.my_cubes</span><span class="nf">.as_drawable</span><span class="p">(</span><span class="o">&amp;</span><span class="k">self</span><span class="py">.cube</span><span class="p">),</span>
        <span class="o">&amp;</span><span class="n">my_params</span><span class="p">,</span>
        <span class="o">&amp;</span><span class="nn">Default</span><span class="p">::</span><span class="nf">default</span><span class="p">(),</span>
    <span class="p">)</span><span class="o">?</span>
    <span class="nf">.compose</span><span class="p">(</span><span class="o">&amp;</span><span class="n">scene</span><span class="py">.lights</span><span class="p">)</span><span class="o">?</span>
    <span class="nf">.postprocess</span><span class="p">()</span><span class="o">?</span>
    <span class="nf">.present</span><span class="p">();</span>
</code></pre></div></div>
<p>See <a href="https://github.com/leod/rendology/blob/master/examples/cube.rs">examples/cube.rs</a> for a more
complete example.</p>

<blockquote class="warning" id="myid" title="my title">
Note that this is written from the perspective of an amateur game developer, so take
everything
with two grains of salt. Also, please keep in mind that Rendology is unstable, undocumented and not
ready for general usage yet. 
</blockquote>

<iframe width="560" height="315" src="https://www.youtube.com/embed/qOKUS2cwufg" frameborder="0" allow="accelerometer; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""> </iframe>

<h2 id="background">Background</h2>
<p>Rendology was split off from my puzzle game project
<a href="https://github.com/leod/ultimate-scale"><em>Ultimate Scale</em></a>. When I started this project, I wanted
to focus on the game concept — certainly, I thought, drawing some plain cubes would more than
suffice. Pretty soon, however, I got tired of looking at the graphics, so I inexplicably decided
to implement simple versions of shadow mapping<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup> and deferred shading<sup id="fnref:2" role="doc-noteref"><a href="#fn:2" class="footnote" rel="footnote">2</a></sup>. Later on, I added
support for FXAA <sup id="fnref:3" role="doc-noteref"><a href="#fn:3" class="footnote" rel="footnote">3</a></sup> and a glow effect.</p>

<p>It was important to me to allow turning off each of the rendering effects separately, so that 
development would be possible on my puny little laptop. Each combination of rendering effects needs 
a different shader, so you get a bit of a combinatorial explosion<sup id="fnref:4" role="doc-noteref"><a href="#fn:4" class="footnote" rel="footnote">4</a></sup>.  At first, I handled this by
manually splicing together shader source fragments, adding a few lines here and there if some effect
was enabled and so on. This worked fine for a while. Then, however,
<a href="https://github.com/Vollkornaffe">@Vollkornaffe</a> came up with a great idea for a spiral effect,
which would require doing some transformations in the vertex and fragment shader. I loved the idea,
but then it hit me: since the core of the shaders would be different, I would need to once again
implement support for all of the combinations of rendering effects around that!</p>

<p>Assumably, there are industry-proven solutions to this problem, but after some deliberation I came
up with a somewhat overengineered (and yet hacky) method of successively transforming shaders.
This method later turned into the core of Rendology.</p>

<h2 id="shader-cores-and-their-transformations">Shader Cores and their Transformations</h2>
<p>Rendology defines a <code class="language-plaintext highlighter-rouge">shader::Core&lt;P, I, V&gt;</code> as consisting of a vertex shader
<code class="language-plaintext highlighter-rouge">shader::VertexCore&lt;P, I, V&gt;</code> and a fragment shader <code class="language-plaintext highlighter-rouge">shader::FragmentCore&lt;P&gt;</code>. The type parameters
define data that has to be provided from the CPU side when drawing with the shader:</p>
<ul>
  <li><code class="language-plaintext highlighter-rouge">P</code> is per-draw-call uniform data,</li>
  <li><code class="language-plaintext highlighter-rouge">I</code> is per-instance data, and</li>
  <li><code class="language-plaintext highlighter-rouge">V</code> is per-vertex data.</li>
</ul>

<p>Rendology shader types store GLSL shaders in a decomposed form. Both vertex and fragment cores
consist of output variable declarations, a body, and a list of output expressions. The fragment core
additionally has input declarations for varying variables coming from the vertex shader. This
decomposed form allows successive transformations to be applied to shaders.</p>

<p>Before we can look at an example
(full code: <a href="https://github.com/leod/rendology/blob/master/examples/shader.rs"><code class="language-plaintext highlighter-rouge">examples/shader.rs</code></a>),
we need to define the data that flows into the shader:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">use</span> <span class="n">nalgebra</span> <span class="k">as</span> <span class="n">na</span><span class="p">;</span>

<span class="k">struct</span> <span class="n">Params</span> <span class="p">{</span>
    <span class="n">projection_matrix</span><span class="p">:</span> <span class="nn">na</span><span class="p">::</span><span class="n">Matrix4</span><span class="o">&lt;</span><span class="nb">f32</span><span class="o">&gt;</span><span class="p">,</span>
    <span class="n">view_matrix</span><span class="p">:</span> <span class="nn">na</span><span class="p">::</span><span class="n">Matrix4</span><span class="o">&lt;</span><span class="nb">f32</span><span class="o">&gt;</span><span class="p">,</span>
    <span class="n">light_pos</span><span class="p">:</span> <span class="nn">na</span><span class="p">::</span><span class="n">Vector3</span><span class="o">&lt;</span><span class="nb">f32</span><span class="o">&gt;</span><span class="p">,</span>
<span class="p">}</span>

<span class="k">struct</span> <span class="n">Instance</span> <span class="p">{</span>
    <span class="n">instance_matrix</span><span class="p">:</span> <span class="nn">na</span><span class="p">::</span><span class="n">Matrix4</span><span class="o">&lt;</span><span class="nb">f32</span><span class="o">&gt;</span><span class="p">,</span>
<span class="p">}</span>

<span class="k">struct</span> <span class="n">Vertex</span> <span class="p">{</span>
    <span class="n">vertex_pos</span><span class="p">:</span> <span class="p">[</span><span class="nb">f32</span><span class="p">;</span> <span class="mi">3</span><span class="p">],</span>
    <span class="n">vertex_normal</span><span class="p">:</span> <span class="p">[</span><span class="nb">f32</span><span class="p">;</span> <span class="mi">3</span><span class="p">],</span>
<span class="p">}</span>

<span class="c1">// (Skipping some trait implementations here.)</span>
</code></pre></div></div>
<p>Given these definitions, we can define a simple shader:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">use</span> <span class="nn">rendology</span><span class="p">::</span><span class="n">shader</span><span class="p">;</span>

<span class="k">fn</span> <span class="nf">scene_core</span><span class="p">()</span> <span class="k">-&gt;</span> <span class="nn">shader</span><span class="p">::</span><span class="n">Core</span><span class="o">&lt;</span><span class="n">Params</span><span class="p">,</span> <span class="n">Instance</span><span class="p">,</span> <span class="n">Vertex</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="k">let</span> <span class="n">vertex</span> <span class="o">=</span> <span class="nn">shader</span><span class="p">::</span><span class="nn">VertexCore</span><span class="p">::</span><span class="nf">empty</span><span class="p">()</span>
        <span class="nf">.with_body</span><span class="p">(</span>
            <span class="s">"mat4 normal_matrix = transpose(inverse(mat3(instance_matrix)));"</span>
        <span class="p">)</span>
        <span class="nf">.with_out</span><span class="p">(</span>
            <span class="nn">shader</span><span class="p">::</span><span class="nn">defs</span><span class="p">::</span><span class="n">V_WORLD_POS</span><span class="p">,</span>
            <span class="s">"instance_matrix * vec4(vertex_pos, 1)"</span><span class="p">,</span>
        <span class="p">)</span>
        <span class="nf">.with_out</span><span class="p">(</span>
            <span class="nn">shader</span><span class="p">::</span><span class="nn">defs</span><span class="p">::</span><span class="n">V_WORLD_NORMAL</span><span class="p">,</span>
            <span class="s">"normal_matrix * vertex_normal"</span><span class="p">,</span>
        <span class="p">)</span>
        <span class="nf">.with_out</span><span class="p">(</span>
            <span class="nn">shader</span><span class="p">::</span><span class="nn">defs</span><span class="p">::</span><span class="n">V_POS</span><span class="p">,</span>
            <span class="s">"projection_matrix * view_matrix * v_world_pos"</span><span class="p">,</span>
        <span class="p">);</span>

    <span class="k">let</span> <span class="n">fragment</span> <span class="o">=</span> <span class="nn">shader</span><span class="p">::</span><span class="nn">FragmentCore</span><span class="p">::</span><span class="nf">empty</span><span class="p">()</span>
        <span class="nf">.with_out</span><span class="p">(</span><span class="nn">shader</span><span class="p">::</span><span class="nn">defs</span><span class="p">::</span><span class="n">F_COLOR</span><span class="p">,</span> <span class="s">"vec4(1, 0, 0, 1)"</span><span class="p">);</span>

    <span class="nn">shader</span><span class="p">::</span><span class="n">Core</span> <span class="p">{</span> <span class="n">vertex</span><span class="p">,</span> <span class="n">fragment</span> <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>A <code class="language-plaintext highlighter-rouge">shader::Core</code> can be compiled into raw GLSL code. Declarations for input
data types (e.g. <code class="language-plaintext highlighter-rouge">Params</code>) are generated implicitly.</p>

<p>You may notice that this shader is not very <em>shady</em> — it just outputs flat red colors.
Let’s define a shader core transformation, i.e. a function that takes a shader, modifies it in
some way, and produces a new shader. In this case, we will use the <code class="language-plaintext highlighter-rouge">v_world_pos</code> and
<code class="language-plaintext highlighter-rouge">v_world_normal</code> outputs of the above shader to calculate diffuse lighting.</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fn</span> <span class="n">diffuse_transform</span><span class="o">&lt;</span><span class="n">I</span><span class="p">,</span> <span class="n">V</span><span class="o">&gt;</span><span class="p">(</span>
    <span class="n">core</span><span class="p">:</span> <span class="nn">shader</span><span class="p">::</span><span class="n">Core</span><span class="o">&lt;</span><span class="n">Params</span><span class="p">,</span> <span class="n">I</span><span class="p">,</span> <span class="n">V</span><span class="o">&gt;</span><span class="p">,</span>
<span class="p">)</span> <span class="k">-&gt;</span> <span class="nn">shader</span><span class="p">::</span><span class="n">Core</span><span class="o">&lt;</span><span class="n">Params</span><span class="p">,</span> <span class="n">I</span><span class="p">,</span> <span class="n">V</span><span class="o">&gt;</span> <span class="p">{</span>
    <span class="k">let</span> <span class="n">fragment</span> <span class="o">=</span> <span class="n">core</span><span class="py">.fragment</span>
        <span class="nf">.with_in_def</span><span class="p">(</span><span class="nn">shader</span><span class="p">::</span><span class="nn">defs</span><span class="p">::</span><span class="n">V_WORLD_POS</span><span class="p">)</span>
        <span class="nf">.with_in_def</span><span class="p">(</span><span class="nn">shader</span><span class="p">::</span><span class="nn">defs</span><span class="p">::</span><span class="n">V_WORLD_NORMAL</span><span class="p">)</span>
        <span class="nf">.with_body</span><span class="p">(</span>
            <span class="s">"
            float diffuse = max(
                0.0,
                dot(v_world_normal, normalize(light_pos - v_world_pos.xyz))
            );
            "</span>
        <span class="p">)</span>
        <span class="nf">.with_out_expr</span><span class="p">(</span><span class="s">"f_color"</span><span class="p">,</span> <span class="s">"diffuse * f_color"</span><span class="p">);</span>

    <span class="nn">shader</span><span class="p">::</span><span class="n">Core</span> <span class="p">{</span>
        <span class="n">vertex</span><span class="p">:</span> <span class="n">core</span><span class="py">.vertex</span><span class="p">,</span>
        <span class="n">fragment</span><span class="p">,</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>The fragment shader is transformed such that it now takes <code class="language-plaintext highlighter-rouge">v_world_pos</code> and <code class="language-plaintext highlighter-rouge">v_world_normal</code> as
varying input. As output, it scales the original <code class="language-plaintext highlighter-rouge">f_color</code> by the factor <code class="language-plaintext highlighter-rouge">diffuse</code>.  The given
vertex shader is left unmodified. Note that <code class="language-plaintext highlighter-rouge">diffuse_transform</code> is generic in the instance data
<code class="language-plaintext highlighter-rouge">I</code> as well as the vertex data <code class="language-plaintext highlighter-rouge">V</code>; all that is required is that <code class="language-plaintext highlighter-rouge">Params</code> is given, so that we have
access to <code class="language-plaintext highlighter-rouge">light_pos</code>. Thus, the same transformation can be applied to different kinds of shaders.</p>

<p>While this is a simple case, the same principles are applied in the implementation of Rendology
multiple times. A scene shader needs to be defined only once. Depending on the configuration of the
pipeline, the shader then undergoes various transformations, by which support for shadow mapping, 
deferred shading and other effects may be added successively.</p>

<h2 id="rendering-pipeline">Rendering Pipeline</h2>
<p>Rendology’s pipeline ensures at compile time that the necessary data for running your scene shader
is given when drawing. One only needs to implement the <code class="language-plaintext highlighter-rouge">SceneCore</code> trait for the scene shader.
For a full example, see
<a href="https://github.com/leod/rendology/blob/master/examples/custom_scene_core.rs"><code class="language-plaintext highlighter-rouge">examples/custom_scene_core.rs</code></a>,
where texturing is implemented. Then, it is possible to create shadow passes, shaded scene passes
and plain scene passes for your <code class="language-plaintext highlighter-rouge">SceneCore</code> implementation. The pipeline’s <code class="language-plaintext highlighter-rouge">draw</code> function for a
shaded scene pass is declared as follows:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">pub</span> <span class="k">fn</span> <span class="n">draw</span><span class="o">&lt;</span><span class="n">C</span><span class="p">,</span> <span class="n">D</span><span class="p">,</span> <span class="n">P</span><span class="o">&gt;</span><span class="p">(</span>
    <span class="k">self</span><span class="p">,</span>
    <span class="n">pass</span><span class="p">:</span> <span class="o">&amp;</span><span class="n">ShadedScenePass</span><span class="o">&lt;</span><span class="n">C</span><span class="o">&gt;</span><span class="p">,</span>
    <span class="n">drawable</span><span class="p">:</span> <span class="o">&amp;</span><span class="n">D</span><span class="p">,</span>
    <span class="n">params</span><span class="p">:</span> <span class="o">&amp;</span><span class="n">P</span><span class="p">,</span>
    <span class="n">draw_params</span><span class="p">:</span> <span class="o">&amp;</span><span class="nn">glium</span><span class="p">::</span><span class="n">DrawParameters</span><span class="p">,</span>
<span class="p">)</span> <span class="k">-&gt;</span> <span class="nb">Result</span><span class="o">&lt;</span><span class="k">Self</span><span class="p">,</span> <span class="n">DrawError</span><span class="o">&gt;</span>
<span class="k">where</span>
    <span class="n">C</span><span class="p">:</span> <span class="n">SceneCore</span><span class="p">,</span>
    <span class="n">D</span><span class="p">:</span> <span class="n">Drawable</span><span class="o">&lt;</span><span class="nn">C</span><span class="p">::</span><span class="n">Instance</span><span class="p">,</span> <span class="nn">C</span><span class="p">::</span><span class="n">Vertex</span><span class="o">&gt;</span><span class="p">,</span>
    <span class="n">P</span><span class="p">:</span> <span class="nn">shader</span><span class="p">::</span><span class="nn">input</span><span class="p">::</span><span class="n">CompatibleWith</span><span class="o">&lt;</span><span class="nn">C</span><span class="p">::</span><span class="n">Params</span><span class="o">&gt;</span><span class="p">,</span>
</code></pre></div></div>
<p>Here, <code class="language-plaintext highlighter-rouge">C::Params</code> are the uniforms for <code class="language-plaintext highlighter-rouge">C</code>, <code class="language-plaintext highlighter-rouge">C::Instance</code> is the per-instance data and <code class="language-plaintext highlighter-rouge">C::Vertex</code>
is the mesh’s vertex type. <code class="language-plaintext highlighter-rouge">drawable</code> holds instances as well as the mesh that is to be drawn.</p>

<p>There is a certain order of operations that must be respected when drawing a frame. Consider the
case of using shadow mapping and deferred shading. A typical frame may look like this:</p>
<ol>
  <li>Clear buffers.</li>
  <li>Draw scene from the main light’s perspective, creating a shadow texture.</li>
  <li>Draw scene from the camera’s perspective, creating albedo and normal textures.</li>
  <li>Calculate light in another texture by making use of the normal texture.</li>
  <li>Compose by multiplying light and albedo texture.</li>
  <li>Draw plain and/or translucent objects.</li>
  <li>Apply postprocessing and present the frame.</li>
</ol>

<p>Of course, there are many ways of messing up this order; for instance, it would not make sense to
create the shadow texture <em>after</em> step five. Rendology enforces this order of operations by defining
a series of types that represent a finite-state automaton. Each operation takes <code class="language-plaintext highlighter-rouge">self</code> by-move and
returns an instance of a type with the legal follow-up operations. Furthermore, the types are
annotated with <code class="language-plaintext highlighter-rouge">#[must_use]</code>. Taken together, these definitions ensure that whenever you start a
frame, you will follow a path through the automaton until the result is presented to the user.
The following diagram shows the paths that are currently possible when drawing a frame:
<img src="/assets/rendology_pipeline.png" alt="drawing a frame" /></p>

<h2 id="footnotes">Footnotes</h2>
<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1" role="doc-endnote">
      <p>Glium’s <a href="https://github.com/glium/glium/blob/master/examples/shadow_mapping.rs">shadow mapping example</a> was of great help in this. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:2" role="doc-endnote">
      <p>Again, Glium’s <a href="https://github.com/glium/glium/blob/master/examples/deferred.rs">deferred shading example</a> was helpful. <a href="#fnref:2" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:3" role="doc-endnote">
      <p>Or something resembling FXAA somewhat, hopefully. <a href="#fnref:3" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:4" role="doc-endnote">
      <p>For example, the shaders for deferred lighting change slightly if you want to support shadow mapping, and then they change again if you want to add the glow effect into the pipeline. <a href="#fnref:4" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name></name></author><category term="rust" /><category term="gamedev" /><category term="rendology" /><summary type="html"><![CDATA[]]></summary></entry></feed>