<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Meno Ruge]]></title><description><![CDATA[Meno Ruge]]></description><link>https://xixixao.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Mon, 26 Oct 2026 23:02:03 GMT</lastBuildDate><atom:link href="https://xixixao.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[How I Learned to Stop Worrying and Love the Unsafe]]></title><description><![CDATA[I played with in Rust and wanted to speed up the rendering. I spent a bunch of time trying to figure out how to share a buffer between threads, and…]]></description><link>https://xixixao.hashnode.dev/how-i-learned-to-stop-worrying-and-love-the-unsafe</link><guid isPermaLink="true">https://xixixao.hashnode.dev/how-i-learned-to-stop-worrying-and-love-the-unsafe</guid><category><![CDATA[Rust]]></category><dc:creator><![CDATA[Michal Srb]]></dc:creator><pubDate>Sun, 20 Dec 2020 05:42:37 GMT</pubDate><content:encoded><![CDATA[<p>I played with https://raytracing.github.io/ in Rust and wanted to speed up the rendering. I spent a bunch of time trying to figure out how to share a buffer between threads, and eventually I ended up doing the same thing as this <a target="_blank" href="https://nelari.us/post/raytracer_with_rust_and_zig/#multithreaded-rendering">post</a> describes:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> buffer: Arc&lt;Mutex&lt;<span class="hljs-built_in">Vec</span>&lt;<span class="hljs-built_in">u32</span>&gt;&gt;&gt; =
    Arc::new(Mutex::new(<span class="hljs-built_in">vec!</span>[<span class="hljs-number">0</span>; width * height]));
</code></pre>
<p>This would have been fine if I was writing to the buffer once, when the whole rendering finished.</p>
<p>Of course this is not how I set up the ray-tracer. I set it up so that I could observe the raytracing process. I was updating the window with the in-progress buffer, using another buffer to store the sum of all samples, like this:</p>
<pre><code class="lang-rust">progress_buffer[index] += render_ray(&amp;ray, &amp;*scene.world, ray_depth);
buffer[index] = color_u32(gamma_correction(
    progress_buffer[index] / (<span class="hljs-number">1.0</span> + sample <span class="hljs-keyword">as</span> <span class="hljs-built_in">f64</span>),
));
</code></pre>
<p>When I implemented multi-threading using the <code>Mutex</code> I ran into a starvation issue which was hard to debug - the main thread tried to render at 60 FPS, but if it took slightly longer to update the window, it would monopolize the <code>Mutex</code>, eventually grinding the ray-tracer to a halt.</p>
<h4 id="unsafe-to-the-rescue">Unsafe to the rescue</h4>
<p>I always knew that the way I set up multi-threading - by giving each worker thread its own piece of the buffer to render, should not require any synchronization. But such shenanigans are not the Rust way. Finally I decided to take the leap.</p>
<p>This is the final demo:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">use</span> minifb::{Key, Window, WindowOptions};
<span class="hljs-keyword">use</span> std::thread;

<span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {
    <span class="hljs-keyword">let</span> (window_width, window_height) = (<span class="hljs-number">400</span>, <span class="hljs-number">200</span>);
    <span class="hljs-keyword">let</span> target_fps = <span class="hljs-number">10.0</span>;
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> window = Window::new(
        <span class="hljs-string">"Raytracer"</span>,
        window_width,
        window_height,
        WindowOptions::default(),
    )
    .unwrap();
    <span class="hljs-keyword">let</span> buffer_len = window_width * window_height;
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> buffer = <span class="hljs-built_in">vec!</span>[color((<span class="hljs-number">1.0</span>, <span class="hljs-number">0.7</span>, <span class="hljs-number">0.0</span>)); buffer_len];
    <span class="hljs-keyword">let</span> buffer_ptr = buffer.as_mut_ptr() <span class="hljs-keyword">as</span> <span class="hljs-built_in">usize</span>;

    <span class="hljs-keyword">let</span> num_workers = <span class="hljs-number">8</span>;
    <span class="hljs-keyword">let</span> worker_len = buffer_len / num_workers;

    <span class="hljs-keyword">for</span> ithread <span class="hljs-keyword">in</span> <span class="hljs-number">0</span>..num_workers {
        thread::spawn(<span class="hljs-keyword">move</span> || {
            <span class="hljs-keyword">let</span> thread_ptr = buffer_ptr <span class="hljs-keyword">as</span> *<span class="hljs-keyword">mut</span> <span class="hljs-built_in">u32</span>;
            <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> <span class="hljs-number">0</span>..worker_len {
                thread::sleep(std::time::Duration::from_millis(<span class="hljs-number">2</span>));
                <span class="hljs-keyword">unsafe</span> {
                    *thread_ptr.add(ithread * worker_len + i) =
                        color((<span class="hljs-number">1.0</span> / ((<span class="hljs-number">1</span> + ithread) <span class="hljs-keyword">as</span> <span class="hljs-built_in">f64</span>).powf(<span class="hljs-number">0.3</span>), <span class="hljs-number">1.0</span>, <span class="hljs-number">0.5</span>));
                }
            }
        });
    }

    window.limit_update_rate(<span class="hljs-literal">None</span>);
    <span class="hljs-keyword">while</span> window.is_open() &amp;&amp; !window.is_key_down(Key::Escape) {
        window
            .update_with_buffer(&amp;buffer, window_width, window_height)
            .unwrap();
        thread::sleep(std::time::Duration::from_secs_f64(<span class="hljs-number">1.0</span> / target_fps))
    }
}

<span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">color</span></span>((r, g, b): (<span class="hljs-built_in">f64</span>, <span class="hljs-built_in">f64</span>, <span class="hljs-built_in">f64</span>)) -&gt; <span class="hljs-built_in">u32</span> {
    color_256_u32(r * <span class="hljs-number">255.999</span>, g * <span class="hljs-number">255.999</span>, b * <span class="hljs-number">255.999</span>, <span class="hljs-number">255.0</span>)
}

<span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">color_256_u32</span></span>(red: <span class="hljs-built_in">f64</span>, green: <span class="hljs-built_in">f64</span>, blue: <span class="hljs-built_in">f64</span>, alpha: <span class="hljs-built_in">f64</span>) -&gt; <span class="hljs-built_in">u32</span> {
    ((alpha <span class="hljs-keyword">as</span> <span class="hljs-built_in">u32</span>) &lt;&lt; <span class="hljs-number">24</span>) + <span class="hljs-comment">// shift to align</span>
     ((red <span class="hljs-keyword">as</span> <span class="hljs-built_in">u32</span>) &lt;&lt; <span class="hljs-number">16</span>) + <span class="hljs-comment">// shift to align</span>
     ((green <span class="hljs-keyword">as</span> <span class="hljs-built_in">u32</span>) &lt;&lt; <span class="hljs-number">8</span>) + <span class="hljs-comment">// shift to align</span>
     (blue <span class="hljs-keyword">as</span> <span class="hljs-built_in">u32</span>)
}
</code></pre>
<p>Let's ignore the <code>minifb</code> boilerplate and focus on the interesting piece.</p>
<p>First of all, Rust doesn't allow passing pointers between threads, as a safety measure. To get around it, we need to cast the pointer to a primitive integer:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> buffer_ptr = buffer.as_mut_ptr() <span class="hljs-keyword">as</span> <span class="hljs-built_in">usize</span>;
</code></pre>
<p>It's important to use <code>usize</code>, otherwise we could trim the pointer and end pointing to an entirely different place in memory and <code>SIGSEGV</code> panics.</p>
<p>Once in the worker thread, we can turn the integer back to a pointer:</p>
<pre><code><span class="hljs-keyword">let</span> thread_ptr = buffer_ptr <span class="hljs-keyword">as</span> *<span class="hljs-keyword">mut</span> <span class="hljs-built_in">u32</span>;
</code></pre><p>Here instead the <code>u32</code> determines the type of what we want to store at the pointer location, which for <code>minifb</code> buffers are colors encoded into 32 bits.</p>
<p>Finally, we write to the correct place in the buffer, and voila, we get our real-time progress demo. There is no need to synchronize here because we don't care about the state of each worker thread - even if it was mid-writing a byte, in the worst case it would give us some strange color for a single pixel. In practice, the whole 32bit write looks atomic on my OS.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1608442894222/55MjZXhiM.gif" alt="ezgif.com-gif-maker.gif" /></p>
]]></content:encoded></item></channel></rss>