<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>encelo.github.io</title><link>https://encelo.github.io/</link><description>Recent content on encelo.github.io</description><generator>Hugo -- gohugo.io</generator><language>en</language><lastBuildDate>Sat, 27 Jun 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://encelo.github.io/index.xml" rel="self" type="application/rss+xml"/><item><title>nCine Dev Update 23</title><link>https://encelo.github.io/2026-06-27-ncine-dev-update-23/</link><pubDate>Sat, 27 Jun 2026 00:00:00 +0000</pubDate><guid>https://encelo.github.io/2026-06-27-ncine-dev-update-23/</guid><description>&lt;p&gt;Welcome back to another development update for the nCine, covering what has been accomplished in the last quarter of 2025 and the first half of 2026.&lt;/p&gt;
&lt;p&gt;Before diving into the technical part of the article, I should probably mention that a few days ago marked the 15th anniversary of the &lt;a href="https://github.com/nCine/nCine/commit/6bf318de" target="_blank" rel="noreferrer"&gt;first commit&lt;/a&gt; of the project. &amp;#x1f382;&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s also cheer for the &lt;a href="https://ncine.github.io/news/2025-09-28-new-site/" target="_blank" rel="noreferrer"&gt;new Hugo site&lt;/a&gt; and the availability of &lt;a href="https://ncine.github.io/news/2025-11-05-signed-commits/" target="_blank" rel="noreferrer"&gt;signed commits&lt;/a&gt; &amp;#x1f511;, two important infrastructure updates from last year.&lt;/p&gt;
&lt;h2 class="relative group"&gt;GrAIL
&lt;div id="grail" class="anchor"&gt;&lt;/div&gt;
&lt;/h2&gt;
&lt;p&gt;Probably the biggest development news is the work in progress toward a Render Hardware Interface (RHI) that will allow modern graphics APIs to be employed.
In the nCine the RHI is called &lt;em&gt;Graphics API Integration Layer&lt;/em&gt;, or GrAIL.&lt;/p&gt;
&lt;p&gt;The first iteration only supports Vulkan, but implements the underlying architecture that will make it possible to support other backends.
From the &lt;a href="https://advances.realtimerendering.com/s2023/AaltonenHypeHypeAdvances2023.pdf" target="_blank" rel="noreferrer"&gt;HypeHype&lt;/a&gt; rendering architecture presentation by Sebastian Aaltonen, I borrowed the concept of using an opaque handle (made of an index and a generation, like in my job system) for all the common resources, like textures, pipelines, and buffers. I also copied the concept of immutable bind groups, an abstraction over descriptors, from WebGPU.&lt;/p&gt;
&lt;p&gt;The entry point of GrAIL, similarly to other modern APIs, is the device object. The device is not a traditional C++ base class, but a common header with different implementation files chosen at compile time by CMake. I chose to sacrifice run-time API selection to remove a layer of virtual call dispatch.&lt;/p&gt;
&lt;p&gt;The plan is to use Vulkan on Linux and Windows (maybe I will add D3D12 in the future), to add WebGPU for Emscripten, and to test KosmicKrisp and MoltenVK on macOS before eventually adding a Metal backend.&lt;/p&gt;
&lt;p&gt;Another important missing piece is the actual link between GrAIL and the scenegraph system, which will be handled by a new renderer class. The renderer will make it possible to remove rendering code from the nodes and will represent a chance to reorganize rendering data in a more data-oriented fashion.
For the time being I will also keep the legacy OpenGL code around, which means I will need to port the old system to this new renderer approach.&lt;/p&gt;
&lt;p&gt;At the moment, to isolate the work on this feature, I&amp;rsquo;m taking advantage of the new &lt;code&gt;NCINE_WITH_SCENEGRAPH&lt;/code&gt; CMake variable that allows the nCine to be compiled without any scenegraph-related classes, like the nodes, the render commands, or viewports.&lt;/p&gt;
&lt;p&gt;There is still a lot of work before you can use GrAIL instead of OpenGL, but you can already try the &lt;code&gt;grail&lt;/code&gt; branch on GitHub and play with the particle simulation test.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;&lt;img
class="my-0 rounded-md"
loading="lazy"
decoding="async"
fetchpriority="low"
alt="Current GrAIL apptest, with compute simulated particles and a rotating nCine logo"
src="/images/grlapptest_compute_texture.png"
&gt;&lt;figcaption&gt;Current GrAIL apptest, with compute simulated particles and a rotating nCine logo&lt;/figcaption&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 class="relative group"&gt;Batching with instances
&lt;div id="batching-with-instances" class="anchor"&gt;&lt;/div&gt;
&lt;/h2&gt;
&lt;p&gt;While GrAIL represents the future, it does not mean that the OpenGL path would be deprecated any time soon. As a matter of fact, in June I have worked on optimizing regular sprite blitting.&lt;/p&gt;
&lt;p&gt;As you might remember, the &lt;code&gt;RenderBatcher&lt;/code&gt; class is in charge of automatically creating draw batches for every type of rendering commands that it encounters while navigating the sorted queues.&lt;/p&gt;
&lt;p&gt;I have added another command collection path that works with regular sprites (so no &lt;code&gt;TextNode&lt;/code&gt; or &lt;code&gt;MeshSprite&lt;/code&gt; nodes) and uses a completely different approach.
Instead of using a Uniform Buffer Object (UBO) to gather the information about each sprite and expand their 4 vertices triangle strip into two triangles and 6 vertices, it uses instancing and vertex attributes.&lt;/p&gt;
&lt;p&gt;This introduces two nice properties: with instances we can still use the 4 vertices triangle strip primitives for each sprite, and with the attributes we can gather the data in a Vertex Buffer Object (VBO). VBOs don&amp;rsquo;t have the size restriction that UBOs have (usually 64 kb), meaning that it is now possible to batch a lot more sprites together in the same draw call.&lt;/p&gt;
&lt;p&gt;The trick that enables vertex attributes to be used per instance and not per vertex is a call to &lt;code&gt;glVertexAttribDivisor()&lt;/code&gt; with a divisor value of 1.&lt;/p&gt;
&lt;p&gt;There is also another optimization that allows for bigger batches, and it also works when using UBOs: packing the data needed to render nodes.&lt;/p&gt;
&lt;p&gt;For example, let&amp;rsquo;s have a look at the &lt;code&gt;InstanceBlock&lt;/code&gt; structure in the &lt;code&gt;sprite_vs.glsl&lt;/code&gt; shader:&lt;/p&gt;
&lt;div
class="tab__container w-full"
&gt;
&lt;div class="tab__nav" role="tablist"&gt;
&lt;div class="flex flex-wrap gap-1"&gt;&lt;button
class="tab__button px-3 py-2 text-sm font-semibold border-b-2 border-transparent rounded-t-md hover:bg-neutral-200 dark:hover:bg-neutral-700 tab--active"
role="tab"
aria-selected="true"
data-tab-index="0"
data-tab-label="Before"&gt;
&lt;span class="flex items-center gap-1"&gt;
Before
&lt;/span&gt;
&lt;/button&gt;&lt;button
class="tab__button px-3 py-2 text-sm font-semibold border-b-2 border-transparent rounded-t-md hover:bg-neutral-200 dark:hover:bg-neutral-700 "
role="tab"
aria-selected="false"
data-tab-index="1"
data-tab-label="After"&gt;
&lt;span class="flex items-center gap-1"&gt;
After
&lt;/span&gt;
&lt;/button&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="tab__content mt-4"&gt;&lt;div class="tab__panel tab--active" data-tab-index="0"&gt;
&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-glsl" data-lang="glsl"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// 104 bytes, rounded to 112 for alignment purposes&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;layout (std140) &lt;span style="color:#66d9ef"&gt;uniform&lt;/span&gt; InstanceBlock
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; mat4 modelMatrix;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;vec4&lt;/span&gt; color;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;vec4&lt;/span&gt; texRect;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;vec2&lt;/span&gt; spriteSize;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="tab__panel " data-tab-index="1"&gt;
&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-glsl" data-lang="glsl"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// 48 bytes in total, no padding needed&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;layout (std140) &lt;span style="color:#66d9ef"&gt;uniform&lt;/span&gt; InstanceBlock
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;vec4&lt;/span&gt; transform;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;vec4&lt;/span&gt; translation;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; uint color;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; uint spriteSize;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; uint uvEndpointsU;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; uint uvEndpointsV;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;/div&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;The new structure needs less than half the memory, allowing for more than double the number of sprites in the same batch.&lt;/p&gt;
&lt;p&gt;On the C++ code side, the framework will try to first update the new uniforms, but it will fall back to the old fields if the process fails.
On the shader side, new functions will unpack the data into floats, trading the space gains for some additional low-effort per vertex computation.&lt;/p&gt;
&lt;h3 class="relative group"&gt;Benchmarks
&lt;div id="benchmarks" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;I have tested the instancing on my slowest laptop, a Xiaomi Mi Notebook Pro with an i7-8550U, and further limited the CPU and GPU frequencies to the minimum to stabilize the results and to highlight any performance benefit on slow devices. I ran the updated &lt;code&gt;apptest_bunnymark&lt;/code&gt; with 5000 bunnies and no V-Sync, then I toggled instancing on and off.&lt;/p&gt;
&lt;pre class="not-prose mermaid"&gt;
xychart
title "Five seconds average runs"
x-axis ["Run 1", "Run 2", "Run 3", "Run 4", "Run 5"]
y-axis "Frametime (ms)" 0 --&gt; 35
bar "A" [26.201, 27.067, 25.969, 26.232, 26.187]
bar "B" [19.890, 19.366, 18.398, 19.654, 19.780]
&lt;/pre&gt;
&lt;p&gt;As you can see from the chart, enabling instancing cut the frametime to 74% of the baseline, from an average of 26.3312 ms to 19.4176 ms.&lt;/p&gt;
&lt;h2 class="relative group"&gt;Faster directory traversing on Windows
&lt;div id="faster-directory-traversing-on-windows" class="anchor"&gt;&lt;/div&gt;
&lt;/h2&gt;
&lt;p&gt;I am certainly not an expert in Windows API, but I remember reading an article about how to make directory traversal faster by using &lt;code&gt;FindFirstFileExA()&lt;/code&gt; and its additional parameters, like &lt;a href="https://blog.s-schoener.com/2024-06-09-find-first-large-fetch/" target="_blank" rel="noreferrer"&gt;FIND_FIRST_EX_LARGE_FETCH&lt;/a&gt;.
I then went to have a look in the &lt;code&gt;FileSystem&lt;/code&gt; namespace and I experimented with the new approach. I removed some unneeded string copies, and I changed the API call from a simple &lt;code&gt;FindFirstFile()&lt;/code&gt; to:&lt;/p&gt;
&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;hFindFile_ &lt;span style="color:#f92672"&gt;=&lt;/span&gt; FindFirstFileExA(buffer, FindExInfoBasic, &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;findFileData, FindExSearchNameMatch, &lt;span style="color:#66d9ef"&gt;nullptr&lt;/span&gt;, FIND_FIRST_EX_LARGE_FETCH);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;pre class="not-prose mermaid"&gt;
xychart
title "Scanning C:/Windows/System32 (4786 files)"
x-axis ["Run 1", "Run 2", "Run 3", "Run 4", "Run 5"]
y-axis "Time (ms)" 0 --&gt; 4
bar [3.4946, 2.8509, 2.9092, 2.8022, 3.4254]
bar [3.2654, 2.3800, 2.3263, 2.3020, 2.4079]
&lt;/pre&gt;
&lt;p&gt;In this first case, using the new API call lowered the average from 3.09646 ms to 2.53632 ms, or a new time that is 82% of the old one.&lt;/p&gt;
&lt;pre class="not-prose mermaid"&gt;
xychart
title "Scanning C:/msys64/usr/bin (604 files)"
x-axis ["Run 1", "Run 2", "Run 3", "Run 4", "Run 5"]
y-axis "Time (ms)" 0 --&gt; 1
bar [0.7142, 0.5205, 0.5406, 0.6141, 0.6587]
bar [0.5092, 0.5084, 0.5168, 0.4214, 0.4306]
&lt;/pre&gt;
&lt;p&gt;In the second case, the new API call reduced the average from 0.60962 ms to 0.47728 ms, or a new time that is 78% of the old one.&lt;/p&gt;
&lt;h2 class="relative group"&gt;nCTL updates
&lt;div id="nctl-updates" class="anchor"&gt;&lt;/div&gt;
&lt;/h2&gt;
&lt;h3 class="relative group"&gt;Optional class
&lt;div id="optional-class" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;While working on GrAIL, I needed a way to control when to construct objects that are fields of other objects. I usually do this with &lt;code&gt;nctl::UniquePtr&lt;/code&gt;, by choosing the right time to call &lt;code&gt;nctl::makeUnique()&lt;/code&gt;, but in this case I wanted to reserve the space for the object beforehand, and avoid allocating memory.&lt;/p&gt;
&lt;p&gt;I ended up replicating some functionalities from &lt;code&gt;std::optional&lt;/code&gt;, a class that is usually used to return an object or a value that can be interpreted as &lt;em&gt;null&lt;/em&gt; (when the optional class is not &amp;ldquo;engaged&amp;rdquo;) when a function returns.&lt;/p&gt;
&lt;p&gt;The class is really just a buffer as big as its template argument, and objects are created there with a placement new call. These features satisfy my requirements and that&amp;rsquo;s how &lt;code&gt;nctl::Optional&lt;/code&gt; was born, of course with its accompanying set of unit tests, as is tradition in nCTL.&lt;/p&gt;
&lt;h3 class="relative group"&gt;Iterators refactoring
&lt;div id="iterators-refactoring" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;All iterators have been refactored so that they obey the same invariants, and behave similarly to STL.&lt;/p&gt;
&lt;p&gt;For example, a reverse iterator at the beginning of a container is now always constructed from a regular iterator at the end of the same container.
The same is valid for the symmetric situation, a reverse iterator at the end of a container.&lt;/p&gt;
&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;/// Returns a reverse iterator to the beginning
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;inline&lt;/span&gt; ReverseIterator &lt;span style="color:#a6e22e"&gt;rBegin&lt;/span&gt;() { &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; ReverseIterator(end()); }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;/// Returns a reverse iterator to the end
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;inline&lt;/span&gt; ReverseIterator &lt;span style="color:#a6e22e"&gt;rEnd&lt;/span&gt;() { &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; ReverseIterator(begin()); }&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;I now explicitly check for those equivalences in the unit tests, together with other invariant checks, like checking that when I increment then decrement a &lt;code&gt;begin()&lt;/code&gt; iterator, or do the opposite with an &lt;code&gt;end()&lt;/code&gt; one, I don&amp;rsquo;t alter it in a way that makes it no longer equivalent to &lt;code&gt;begin()&lt;/code&gt; or &lt;code&gt;end()&lt;/code&gt; themselves.&lt;/p&gt;
&lt;p&gt;Last but not least, I rewrote the &lt;code&gt;operator!=&lt;/code&gt; in all iterators to be consistent and just always negate the result from &lt;code&gt;operator==&lt;/code&gt;.&lt;/p&gt;
&lt;h3 class="relative group"&gt;Pair class
&lt;div id="pair-class" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;Just like &lt;code&gt;nctl::Optional&lt;/code&gt;, the template library keeps extending with some less used but sometimes useful classes, for example a reimplementation of &lt;code&gt;std::pair&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;It does not replace the custom pair implementation used by &lt;code&gt;nctl::UniquePtr&lt;/code&gt; to store its deleter, as it is a class intended for general use cases.&lt;/p&gt;
&lt;p&gt;While the optional class needed some new type traits to remove both the &lt;code&gt;const&lt;/code&gt; and the &lt;code&gt;volatile&lt;/code&gt; qualifiers from types, the pair class needs type decay when creating objects with &lt;code&gt;makePair()&lt;/code&gt;.&lt;/p&gt;
&lt;h3 class="relative group"&gt;Hash containers refactoring
&lt;div id="hash-containers-refactoring" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;Hash containers have also been refactored. Hashmaps, for example, now accept a &lt;code&gt;nctl::Pair&lt;/code&gt; when inserting a key and a value.&lt;/p&gt;
&lt;p&gt;Most importantly, I have refactored the &lt;code&gt;HashMap&lt;/code&gt;/&lt;code&gt;StaticHashMap&lt;/code&gt; and &lt;code&gt;HashSet&lt;/code&gt;/&lt;code&gt;StaticHashSet&lt;/code&gt; classes so they don&amp;rsquo;t duplicate any code when traversing the data structure.&lt;/p&gt;
&lt;p&gt;The results of probing into them are now grouped in a structure:&lt;/p&gt;
&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;/// The returning structure after probing for a key
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;struct&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;ProbeResult&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;/// The ideal bucket index for the hash
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;unsigned&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; ideal;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;/// Only valid if the found flag is true
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;unsigned&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; found;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;/// First empty node in a chain
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;unsigned&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; empty;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;/// Previous node in a chain (for delta patching)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;unsigned&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; prev;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;/// True if the node contains a value
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;bool&lt;/span&gt; foundFlag;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; };&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;For &lt;code&gt;HashMapList&lt;/code&gt; and &lt;code&gt;HashSetList&lt;/code&gt; classes, the total number of elements stored is now saved in a variable, instead of calculated from the buckets.&lt;/p&gt;
&lt;h3 class="relative group"&gt;Initializer list support
&lt;div id="initializer-list-support" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;I have added yet another feature that makes nCTL containers more powerful and compatible with STL ones, the ability to use initializer lists to specify the initial content at construct time.&lt;/p&gt;
&lt;p&gt;Take a look at this snippet from a &lt;code&gt;nctl::Array&lt;/code&gt; unit test, for example:&lt;/p&gt;
&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;nctl&lt;span style="color:#f92672"&gt;::&lt;/span&gt;Array&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;int&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; newArray({ &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;4&lt;/span&gt; }, Capacity);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The array is constructed with a copy of the objects passed in the initializer list, similarly to what you would do with a &lt;code&gt;std::vector&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For this to work I had to add &lt;code&gt;#include &amp;lt;initializer_list&amp;gt;&lt;/code&gt;, as there is no way to reimplement this header, the implementation is too tightly integrated with the compiler. But it does not bring in any STL dependency, the include is minimal and only serves the purpose of supporting the initialization through the curly braces.&lt;/p&gt;
&lt;p&gt;Initializer lists can now be used with nearly all containers (&lt;code&gt;Array&lt;/code&gt;, &lt;code&gt;HashMap&lt;/code&gt;, &lt;code&gt;HashSet&lt;/code&gt;, &lt;code&gt;List&lt;/code&gt;, &lt;code&gt;SparseSet&lt;/code&gt;) and unit tests have been updated to use them.&lt;/p&gt;
&lt;h3 class="relative group"&gt;Other additions
&lt;div id="other-additions" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;I removed the fixed capacity option from the &lt;code&gt;nctl::String&lt;/code&gt; class, and extended the size for the small buffer from 16 to 24 bytes.&lt;/li&gt;
&lt;li&gt;I have added a new &lt;code&gt;nctl::StringView&lt;/code&gt; class that doesn&amp;rsquo;t own memory and allows you to format C-style arrays of characters, similarly to &lt;code&gt;std::string_view&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The new &lt;code&gt;nctl::Span&lt;/code&gt; class does something similar to &lt;code&gt;nctl::StringView&lt;/code&gt;, but for arrays that are not characters.&lt;/li&gt;
&lt;li&gt;Surprisingly I wasn&amp;rsquo;t checking for self-assignment in &lt;code&gt;StaticHashMap&lt;/code&gt; and &lt;code&gt;StaticHashSet&lt;/code&gt; classes, this has been fixed.&lt;/li&gt;
&lt;li&gt;I rewrote the functions in the &lt;code&gt;nctl::PointerMath&lt;/code&gt; namespace, now all the alignment parameters have been extended from &lt;code&gt;uint8_t&lt;/code&gt; to &lt;code&gt;size_t&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 class="relative group"&gt;Hashing functions
&lt;div id="hashing-functions" class="anchor"&gt;&lt;/div&gt;
&lt;/h2&gt;
&lt;p&gt;This is another change that was needed for GrAIL, where I heavily use hashing for caching purposes.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;fasthash64()&lt;/code&gt; function now works more reliably, as it does not require padding to 64 bits anymore. I have also made it the default function for hashing keys in all &lt;code&gt;HashMap&lt;/code&gt; and &lt;code&gt;HashSet&lt;/code&gt; containers. It is a lot faster than &lt;em&gt;FNV1a&lt;/em&gt; and statistically stronger.&lt;/p&gt;
&lt;p&gt;To reach this conclusion I added a micro-benchmark and some unit tests that compare the various hashing functions on different inputs.&lt;/p&gt;
&lt;p&gt;I have also added a couple of functions that, given a 64-bit hash, can create a proper 32-bit version of it without losing too much information. This is a task that is often needed in GrAIL, where 64-bit Vulkan opaque handles are converted to 32-bit keys for containers.&lt;/p&gt;
&lt;h3 class="relative group"&gt;Benchmarks
&lt;div id="benchmarks-1" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;Hashing a short string repeatedly with the various functions returns the following results on my Asus laptop in Performance mode.&lt;/p&gt;
&lt;pre class="not-prose mermaid"&gt;
xychart
title "Hashing a 256 bytes string"
x-axis ["Sax", "Jenkins", "FNV1a", "FastHash64"]
y-axis "Bandwidth (GiB/s)" 0 --&gt; 10
bar [1.08797, 0.871923, 1.11025, 7.83879]
&lt;/pre&gt;
&lt;p&gt;The FastHash function is nearly 8x faster than all the others, achieving a bandwidth close to 8 GiB/s on a single core. &amp;#x1f680;&lt;/p&gt;
&lt;h2 class="relative group"&gt;Application configuration structures
&lt;div id="application-configuration-structures" class="anchor"&gt;&lt;/div&gt;
&lt;/h2&gt;
&lt;p&gt;Yet another change that spawned from the work done on GrAIL. This was made to ease future extensions of the &lt;code&gt;AppConfiguration&lt;/code&gt; settings when GrAIL will be merged.&lt;/p&gt;
&lt;p&gt;Instead of having all the flags and values together as fields of the class, they are now organized in structures.
There is a &lt;code&gt;Logging&lt;/code&gt; structure, a &lt;code&gt;Window&lt;/code&gt; one, a &lt;code&gt;Graphics&lt;/code&gt; structure, an &lt;code&gt;Audio&lt;/code&gt; one, and so on.&lt;/p&gt;
&lt;div
class="tab__container w-full"
&gt;
&lt;div class="tab__nav" role="tablist"&gt;
&lt;div class="flex flex-wrap gap-1"&gt;&lt;button
class="tab__button px-3 py-2 text-sm font-semibold border-b-2 border-transparent rounded-t-md hover:bg-neutral-200 dark:hover:bg-neutral-700 tab--active"
role="tab"
aria-selected="true"
data-tab-index="0"
data-tab-label="Before"&gt;
&lt;span class="flex items-center gap-1"&gt;
Before
&lt;/span&gt;
&lt;/button&gt;&lt;button
class="tab__button px-3 py-2 text-sm font-semibold border-b-2 border-transparent rounded-t-md hover:bg-neutral-200 dark:hover:bg-neutral-700 "
role="tab"
aria-selected="false"
data-tab-index="1"
data-tab-label="After"&gt;
&lt;span class="flex items-center gap-1"&gt;
After
&lt;/span&gt;
&lt;/button&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="tab__content mt-4"&gt;&lt;div class="tab__panel tab--active" data-tab-index="0"&gt;
&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; MyEventHandler&lt;span style="color:#f92672"&gt;::&lt;/span&gt;onPreInit(nc&lt;span style="color:#f92672"&gt;::&lt;/span&gt;AppConfiguration &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;config)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; config.consoleLogLevel &lt;span style="color:#f92672"&gt;=&lt;/span&gt; nc&lt;span style="color:#f92672"&gt;::&lt;/span&gt;ILogger&lt;span style="color:#f92672"&gt;::&lt;/span&gt;LogLevel&lt;span style="color:#f92672"&gt;::&lt;/span&gt;OFF;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; config.resolution.set(&lt;span style="color:#ae81ff"&gt;1920&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;1080&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; config.deferShaderQueries &lt;span style="color:#f92672"&gt;=&lt;/span&gt; false;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;/div&gt;&lt;div class="tab__panel " data-tab-index="1"&gt;
&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; MyEventHandler&lt;span style="color:#f92672"&gt;::&lt;/span&gt;onPreInit(nc&lt;span style="color:#f92672"&gt;::&lt;/span&gt;AppConfiguration &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;config)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; config.logging.consoleLevel &lt;span style="color:#f92672"&gt;=&lt;/span&gt; nc&lt;span style="color:#f92672"&gt;::&lt;/span&gt;ILogger&lt;span style="color:#f92672"&gt;::&lt;/span&gt;LogLevel&lt;span style="color:#f92672"&gt;::&lt;/span&gt;OFF;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; config.window.resolution.set(&lt;span style="color:#ae81ff"&gt;1920&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;1080&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; config.graphics.opengl.deferShaderQueries &lt;span style="color:#f92672"&gt;=&lt;/span&gt; false;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;/div&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;This gives a lot of room for future changes, and it has also been ported to the &lt;a href="https://github.com/nCine/nCine/wiki/AppCfg-EnvVars" target="_blank" rel="noreferrer"&gt;environment variables&lt;/a&gt; that you can specify when running an nCine executable.&lt;/p&gt;
&lt;h2 class="relative group"&gt;Minor changes
&lt;div id="minor-changes" class="anchor"&gt;&lt;/div&gt;
&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;Vector4f&lt;/code&gt; and &lt;code&gt;Matrix4x4f&lt;/code&gt; classes now align at 16 bytes, to help the compiler&amp;rsquo;s auto-vectorization.&lt;/li&gt;
&lt;li&gt;On desktop, it is now possible to set the resizable flag of the window at run-time. The feature was already available in all backends and there was no reason not to expose it.&lt;/li&gt;
&lt;li&gt;By using &lt;code&gt;RUNPATH&lt;/code&gt; for Linux executables and by copying MinGW dependency libraries when creating a package, those two platforms should now correctly support completely portable installations, without relying on system-wide installed libraries.&lt;/li&gt;
&lt;li&gt;GitHub Actions workflows now write a manifest JSON file with information about the packages they assemble. This in turn is used by the website to always offer the latest binaries for download.&lt;/li&gt;
&lt;li&gt;Texture loader classes are now just image loaders and are not coupled with OpenGL at all, facilitating their use with GrAIL.&lt;/li&gt;
&lt;li&gt;I have split some long CMake scripts in smaller chunks based on the platform, and moved them in subdirectories.&lt;/li&gt;
&lt;li&gt;Now that &lt;a href="https://www.lua.org/versions.html#5.5" target="_blank" rel="noreferrer"&gt;Lua 5.5&lt;/a&gt; is out, I have added proper support for it.&lt;/li&gt;
&lt;li&gt;For consistency I renamed all occurrences of &lt;em&gt;FullScreen&lt;/em&gt; to &lt;em&gt;Fullscreen&lt;/em&gt;, &lt;em&gt;fullScreen&lt;/em&gt; to &lt;em&gt;fullscreen&lt;/em&gt;, and &lt;em&gt;full_screen&lt;/em&gt; to &lt;em&gt;fullscreen&lt;/em&gt; in code and documentation, treating it as a single word.&lt;/li&gt;
&lt;li&gt;I fixed some JNI version checks before calling newer Android APIs, and now nCine runs again on my 2016 Xiaomi Mi 5 running Android 8! &amp;#x1f916;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>An Intro to Wave Intrinsics</title><link>https://encelo.github.io/2026-03-27-an-intro-to-wave-intrinsics/</link><pubDate>Fri, 27 Mar 2026 00:00:00 +0000</pubDate><guid>https://encelo.github.io/2026-03-27-an-intro-to-wave-intrinsics/</guid><description>&lt;p&gt;Most graphics programmers know about the concept of a warp (or a wave in AMD parlance).
It is a group of GPU threads (or lanes), typically 32, though some architectures use more, that execute the same instructions in lockstep.&lt;/p&gt;
&lt;p&gt;Maybe not all of them know about wave intrinsics, though. They are a group of special GPU instructions exposed in HLSL (and similarly in other shading languages) that a shader programmer can use to orchestrate work across the threads in the same warp.&lt;/p&gt;
&lt;h2 class="relative group"&gt;Enforcing Uniform Control Flow
&lt;div id="enforcing-uniform-control-flow" class="anchor"&gt;&lt;/div&gt;
&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s start with a basic example, using the &lt;code&gt;WaveAllTrue(expression)&lt;/code&gt; intrinsic.
This instruction returns &lt;code&gt;true&lt;/code&gt; only if all threads evaluate the expression as &lt;code&gt;true&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;We said previously that all waves share the same instructions, but they operate on per-lane values of the same variable, stored in vector registers.
It&amp;rsquo;s like running the same function with different parameters, the return value won&amp;rsquo;t be the same.
Wave threads are similar: they have an execution context determined by a set of vector registers, and each thread sees its own version of the same variable.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s why they can evaluate the same condition in different ways and why knowing whether all lanes produce a uniform result with &lt;code&gt;WaveAllTrue&lt;/code&gt; can be so useful.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s suppose that we have a condition called &lt;code&gt;fastPath&lt;/code&gt; that tells us if a shader computation can use a less general but faster approach.&lt;/p&gt;
&lt;p&gt;In this case we could write a snippet like this:&lt;/p&gt;
&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-glsl" data-lang="glsl"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;bool&lt;/span&gt; useFastPath &lt;span style="color:#f92672"&gt;=&lt;/span&gt; WaveAllTrue(fastPath);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (useFastPath)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// All threads go through the fast path&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// All threads go through the general path&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;What does this achieve? We have reduced divergence on the fast path.
Now either all lanes remain active for the fast path or none of them do.&lt;/p&gt;
&lt;p&gt;Why is this beneficial? Why not just write &lt;code&gt;if (fastPath)&lt;/code&gt;?&lt;/p&gt;
&lt;p&gt;Because in this case the GPU evaluates &lt;code&gt;fastPath&lt;/code&gt; per lane and uses an execution mask to track which lanes are active. This mask enables or disables lanes for subsequent instructions. Lanes where the condition evaluates to &lt;code&gt;true&lt;/code&gt; remain active, while the others are masked off. The hardware then executes the &lt;code&gt;if&lt;/code&gt; body with that mask, flips it, and executes the else path for the remaining lanes.&lt;/p&gt;
&lt;p&gt;From the programmer&amp;rsquo;s perspective this looks like a branch, but in practice both paths are typically executed sequentially under different masks.&lt;/p&gt;
&lt;div class="admonition relative overflow-hidden rounded-lg border-l-4 my-3 px-4 py-3 shadow-sm" data-type="note"&gt;
&lt;div class="flex items-center gap-2 font-semibold text-inherit"&gt;
&lt;div class="flex shrink-0 h-5 w-5 items-center justify-center text-lg"&gt;&lt;span class="relative block icon"&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"&gt;&lt;path fill="currentColor" d="M256 0C114.6 0 0 114.6 0 256s114.6 256 256 256s256-114.6 256-256S397.4 0 256 0zM256 128c17.67 0 32 14.33 32 32c0 17.67-14.33 32-32 32S224 177.7 224 160C224 142.3 238.3 128 256 128zM296 384h-80C202.8 384 192 373.3 192 360s10.75-24 24-24h16v-64H224c-13.25 0-24-10.75-24-24S210.8 224 224 224h32c13.25 0 24 10.75 24 24v88h16c13.25 0 24 10.75 24 24S309.3 384 296 384z"/&gt;&lt;/svg&gt;
&lt;/span&gt;&lt;/div&gt;
&lt;div class="grow"&gt;
Note
&lt;/div&gt;
&lt;/div&gt;&lt;div class="admonition-content mt-3 text-base leading-relaxed text-inherit"&gt;&lt;p&gt;The execution mask persists across control flow, and wave intrinsics operate over the currently active lanes rather than the full wave.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 class="relative group"&gt;Delegating Work to a Single Lane
&lt;div id="delegating-work-to-a-single-lane" class="anchor"&gt;&lt;/div&gt;
&lt;/h2&gt;
&lt;p&gt;Other times we want to spare most of the threads from doing an expensive operation, especially an atomic one.
An atomic operation would often be serialized between threads, so it makes sense to reduce its use.&lt;/p&gt;
&lt;p&gt;We can do exactly this with an intrinsic like &lt;code&gt;WaveIsFirstLane()&lt;/code&gt; and the help of another one called &lt;code&gt;WaveReadLaneFirst(expression)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s have a look at the following snippet first.&lt;/p&gt;
&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-glsl" data-lang="glsl"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;uint oldValue;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (WaveIsFirstLane())
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Buffer.InterlockedAdd(bufferOffset, addend, oldValue);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;oldValue &lt;span style="color:#f92672"&gt;=&lt;/span&gt; WaveReadLaneFirst(oldValue);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;In this example the expensive &lt;code&gt;InterlockAdd&lt;/code&gt; operation is guarded by a wave intrinsic. This pattern ensures that only the first active lane in the execution mask will execute the &lt;code&gt;if&lt;/code&gt; statement.
After performing the operation on the first thread, the value is broadcast to the remaining lanes by using the &lt;code&gt;WaveReadLaneFirst()&lt;/code&gt; function.&lt;/p&gt;
&lt;h2 class="relative group"&gt;Reserving Space in a Buffer
&lt;div id="reserving-space-in-a-buffer" class="anchor"&gt;&lt;/div&gt;
&lt;/h2&gt;
&lt;p&gt;The previous example can be further expanded to show how it is possible to reserve some space in a buffer for the whole warp to operate on it without stomping on other waves&amp;rsquo; space.&lt;/p&gt;
&lt;p&gt;Suppose that each thread has a different number of items to write to a buffer.
How can we know how many items the whole warp needs to reserve? And how can we be sure that each thread writes its items in a sub-slice of the allocated space?
Yet again, wave intrinsics come to our aid.&lt;/p&gt;
&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-glsl" data-lang="glsl"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// The number of items that this lane has to write to the buffer&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; uint laneNumItems &lt;span style="color:#f92672"&gt;=&lt;/span&gt; ...;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// The offset in the reserved space where this lane can write its items&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; uint laneOffset &lt;span style="color:#f92672"&gt;=&lt;/span&gt; WavePrefixSum(laneNumItems);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// The starting offset for the space reserved by the whole warp&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;uint absoluteOffset;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// The following statement will be executed only by the last lane in the warp&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (WaveGetLaneIndex() &lt;span style="color:#f92672"&gt;==&lt;/span&gt; WaveGetLaneCount() &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; uint totalNumItems &lt;span style="color:#f92672"&gt;=&lt;/span&gt; laneOffset &lt;span style="color:#f92672"&gt;+&lt;/span&gt; laneNumItems;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; CounterBuffer.InterlockedAdd(&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, totalNumItems , absoluteOffset);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;absoluteOffset &lt;span style="color:#f92672"&gt;=&lt;/span&gt; WaveReadLaneAt(absoluteOffset, WaveGetLaneCount() &lt;span style="color:#f92672"&gt;-&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// Each lane writes its items at the designated offset&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; (uint i &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;; i &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; laneNumItems; i&lt;span style="color:#f92672"&gt;++&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; DataBuffer[absoluteOffset &lt;span style="color:#f92672"&gt;+&lt;/span&gt; laneOffset &lt;span style="color:#f92672"&gt;+&lt;/span&gt; i] &lt;span style="color:#f92672"&gt;=&lt;/span&gt; value;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Let&amp;rsquo;s analyze the code in more detail. First of all, each lane has its own number of items to store.
Then we encounter a new intrinsic: &lt;code&gt;WavePrefixSum()&lt;/code&gt;. This instruction computes a prefix sum of &lt;code&gt;laneNumItems&lt;/code&gt;. Each lane receives the sum of all previous lanes, excluding its own value. So if the first active lane wants to write 2 elements, the second 3, and the third 1, the prefix sum would be 0 at the first lane, 0+2 at the second, and 0+2+3 at the third.
This gives us the offset at which a lane needs to write its items.&lt;/p&gt;
&lt;p&gt;The next &lt;code&gt;if&lt;/code&gt; statement executes only on the last lane in the warp, using &lt;code&gt;WaveGetLaneIndex()&lt;/code&gt; to get the index of the lane executing it and &lt;code&gt;WaveGetLaneCount()&lt;/code&gt; to know the total number of lanes in the warp. Inside the statement, only the last lane will perform the atomic add to reserve space for the total amount of items to store.
To determine the size, it adds its number of items to its prefix sum.&lt;/p&gt;
&lt;p&gt;Now every lane knows both the absolute and local offsets to correctly append their items to the buffer without affecting the others.&lt;/p&gt;
&lt;h2 class="relative group"&gt;Conclusions
&lt;div id="conclusions" class="anchor"&gt;&lt;/div&gt;
&lt;/h2&gt;
&lt;p&gt;We have seen different ways in which wave intrinsics can help orchestrate the work between the threads composing a warp.
Used wisely, they can improve performance in more complex patterns.&lt;/p&gt;
&lt;p&gt;Nevertheless, always profile your baseline before implementing them in your code.&lt;/p&gt;</description></item><item><title>A New Site</title><link>https://encelo.github.io/2025-09-25-a-new-site/</link><pubDate>Thu, 25 Sep 2025 00:00:00 +0000</pubDate><guid>https://encelo.github.io/2025-09-25-a-new-site/</guid><description>&lt;p&gt;After eight years of serving me well, I decided to retire my old &lt;a href="https://jekyllrb.com/" target="_blank" rel="noreferrer"&gt;Jekyll&lt;/a&gt;-based site, which used the &lt;a href="https://beautifuljekyll.com/" target="_blank" rel="noreferrer"&gt;Beautiful Jekyll&lt;/a&gt; theme, a popular Jekyll template created by &lt;a href="https://attalitech.com/" target="_blank" rel="noreferrer"&gt;Dean Attali&lt;/a&gt;. All content has now been migrated to &lt;a href="https://gohugo.io/" target="_blank" rel="noreferrer"&gt;Hugo&lt;/a&gt;, powered by the &lt;a href="https://blowfish.page/" target="_blank" rel="noreferrer"&gt;Blowfish&lt;/a&gt; theme by &lt;a href="https://n9o.xyz/" target="_blank" rel="noreferrer"&gt;Nuno Coração&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I needed more flexibility and more speed. Hugo feels like the perfect choice to handle my site for at least the next eight years! 💪&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m also looking forward to experimenting with what Hugo and Blowfish make possible, so expect more improvements and updates over time.&lt;/p&gt;</description></item><item><title>nCine Compilation Benchmark 3</title><link>https://encelo.github.io/2025-09-22-ncine-compilation-benchmark-3/</link><pubDate>Mon, 22 Sep 2025 00:00:00 +0000</pubDate><guid>https://encelo.github.io/2025-09-22-ncine-compilation-benchmark-3/</guid><description>&lt;p&gt;Compilation speed directly affects iteration time when developing an engine. Since nCine continues to grow, I thought it would be interesting to measure how long a full build takes today compared to 2018, when I wrote the &lt;a href="/2018-03-04-ncine-compilation-benchmark" &gt;first article&lt;/a&gt;, and 2022, when I wrote the &lt;a href="/2022-10-06-ncine-compilation-benchmark-2" &gt;second one&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I was thinking about writing this since the end of last year, when I bought both the second hand Mac and the small 14&amp;quot; Intel laptop.&lt;/p&gt;
&lt;p&gt;Just like in the past, let&amp;rsquo;s start with the hardware and the software I&amp;rsquo;ve used for my tests.&lt;/p&gt;
&lt;h2 class="relative group"&gt;Hardware and software
&lt;div id="hardware-and-software" class="anchor"&gt;&lt;/div&gt;
&lt;/h2&gt;
&lt;h3 class="relative group"&gt;Asus ROG Zephyrus G15 (2022)
&lt;div id="asus-rog-zephyrus-g15-2022" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;h4 class="relative group"&gt;Hardware
&lt;div id="hardware" class="anchor"&gt;&lt;/div&gt;
&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;AMD Ryzen 7 6800HS&lt;/li&gt;
&lt;li&gt;16GB RAM, DDR5 4800 MHz, dual channel&lt;/li&gt;
&lt;li&gt;Western Digital SN735 NVMe SSD, 1TB&lt;/li&gt;
&lt;li&gt;NVIDIA GeForce RTX 3060, 6GB GDDR6&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 class="relative group"&gt;OS
&lt;div id="os" class="anchor"&gt;&lt;/div&gt;
&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Microsoft Windows 11 Home (x64)&lt;/li&gt;
&lt;li&gt;Arch Linux (x86_64), Linux 6.16.8&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 class="relative group"&gt;Alurin Flex Advance
&lt;div id="alurin-flex-advance" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;h4 class="relative group"&gt;Hardware
&lt;div id="hardware-1" class="anchor"&gt;&lt;/div&gt;
&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Intel Core i5-1235U&lt;/li&gt;
&lt;li&gt;16GB RAM, LPDDR4X 4266 MHz, single channel&lt;/li&gt;
&lt;li&gt;Western Digital Blue SN580 NVMe SSD, 512GB&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 class="relative group"&gt;OS
&lt;div id="os-1" class="anchor"&gt;&lt;/div&gt;
&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Microsoft Windows 11 Professional (x64)&lt;/li&gt;
&lt;li&gt;Arch Linux (x86_64), Linux 6.16.8&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 class="relative group"&gt;Mac Mini M1 (2020)
&lt;div id="mac-mini-m1-2020" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;h4 class="relative group"&gt;Hardware
&lt;div id="hardware-2" class="anchor"&gt;&lt;/div&gt;
&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Apple Silicon M1&lt;/li&gt;
&lt;li&gt;8GB RAM, LPDDR4X 4266 MHz, single channel&lt;/li&gt;
&lt;li&gt;NVMe SSD, 256GB&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 class="relative group"&gt;OS
&lt;div id="os-2" class="anchor"&gt;&lt;/div&gt;
&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;macOS Tahoe 26&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 class="relative group"&gt;Compilers and tools
&lt;div id="compilers-and-tools" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;h4 class="relative group"&gt;Arch Linux
&lt;div id="arch-linux" class="anchor"&gt;&lt;/div&gt;
&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;GCC 15.2.1&lt;/li&gt;
&lt;li&gt;CMake 4.1.1&lt;/li&gt;
&lt;li&gt;Ninja 1.12.1&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 class="relative group"&gt;macOS
&lt;div id="macos" class="anchor"&gt;&lt;/div&gt;
&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;AppleClang 17&lt;/li&gt;
&lt;li&gt;CMake 4.1.1&lt;/li&gt;
&lt;li&gt;Ninja 1.13.1&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 class="relative group"&gt;Results
&lt;div id="results" class="anchor"&gt;&lt;/div&gt;
&lt;/h2&gt;
&lt;p&gt;The nCine version is the &lt;code&gt;master&lt;/code&gt; branch as of today, compiled with &lt;code&gt;CMAKE_BUILD_TYPE=Release&lt;/code&gt;, but left all the rest untouched, which means it was compiled with the GLFW backend, with &lt;code&gt;NCINE_BUILD_TESTS&lt;/code&gt; set to &lt;code&gt;ON&lt;/code&gt;, and &lt;code&gt;NCINE_BUILD_UNIT_TESTS&lt;/code&gt; and &lt;code&gt;NCINE_BUILD_ANDROID&lt;/code&gt; both set to &lt;code&gt;OFF&lt;/code&gt;.
The tests have been conducted by running the compilation process multiple times and recording the best timings.&lt;/p&gt;
&lt;p&gt;This time I&amp;rsquo;ve only timed the build phase, using &lt;code&gt;ninja&lt;/code&gt; on all devices. For the laptops, I’ve repeated the test on both the power saving and the performance profile (the former limits CPU frequency and power draw to extend battery life, while the latter allows higher sustained clocks and power consumption).&lt;/p&gt;
&lt;h3 class="relative group"&gt;Tables and charts
&lt;div id="tables-and-charts" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;h4 class="relative group"&gt;Arch Linux
&lt;div id="arch-linux-1" class="anchor"&gt;&lt;/div&gt;
&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th style="text-align: right"&gt;Asus&lt;/th&gt;
&lt;th style="text-align: right"&gt;Alurin&lt;/th&gt;
&lt;th style="text-align: right"&gt;Mini&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Power Save&lt;/td&gt;
&lt;td style="text-align: right"&gt;22.359s&lt;/td&gt;
&lt;td style="text-align: right"&gt;47.213s&lt;/td&gt;
&lt;td style="text-align: right"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Performance&lt;/td&gt;
&lt;td style="text-align: right"&gt;14.379s&lt;/td&gt;
&lt;td style="text-align: right"&gt;17.725s&lt;/td&gt;
&lt;td style="text-align: right"&gt;37.33s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;figure&gt;&lt;img
class="my-0 rounded-md"
loading="lazy"
decoding="async"
fetchpriority="low"
alt="Compilation Chart 2025"
src="/images/compilation_chart_2025.png"
&gt;&lt;figcaption&gt;Compilation Chart 2025&lt;/figcaption&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 class="relative group"&gt;Conclusions
&lt;div id="conclusions" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;It&amp;rsquo;s quite strange to see the Ryzen 6800HS and the Intel i5-1235U so close in Performance mode, maybe compiling C++ code is not the most parallelizable process in the world. &amp;#x1f605;
With other benchmarks, like Cinebench, this Ryzen CPU dominates the smaller i5 on the multi-core test, with around double the performance.&lt;/p&gt;
&lt;p&gt;It seems that power profiles are tuned quite differently between the two laptops, seeing how much the Intel one loses in Power Save.&lt;/p&gt;
&lt;p&gt;I was a bit disappointed by the M1, on paper it should be a bit faster than the Intel on the CPU side with Cinebench, but it loses in my tests. Maybe it&amp;rsquo;s just that I&amp;rsquo;m comparing apples and oranges, with a different OS, environment, and compiler.&lt;/p&gt;
&lt;p&gt;I hope you have found this second benchmark post at least as interesting as the first one. &amp;#x1f609;&lt;/p&gt;</description></item><item><title>nCine Dev Update 22</title><link>https://encelo.github.io/2025-09-21-ncine-dev-update-22/</link><pubDate>Sun, 21 Sep 2025 00:00:00 +0000</pubDate><guid>https://encelo.github.io/2025-09-21-ncine-dev-update-22/</guid><description>&lt;p&gt;Welcome back to another development update for the nCine, covering what has been accomplished in the first part of this year.&lt;/p&gt;
&lt;h2 class="relative group"&gt;Introspective sort
&lt;div id="introspective-sort" class="anchor"&gt;&lt;/div&gt;
&lt;/h2&gt;
&lt;p&gt;Back in December 2023, the author of Jazz² Resurrection reported a crash when sorting a render queue with more than 3000 commands. The sequence was unbalanced and quicksort recursion went too deep, overflowing the stack.&lt;/p&gt;
&lt;p&gt;He suggested switching to introspective sort (a hybrid of quicksort, heapsort, and insertion sort) which is exactly what I ended up implementing, using the same thresholds as many standard implementations.&lt;/p&gt;
&lt;p&gt;The algorithm sets a maximum quicksort depth at twice the base-2 logarithm of the number of elements. Up to that depth, recursive quicksort partitioning is used, which is safe for slightly unbalanced trees. If the depth is exceeded, the algorithm falls back to an iterative heapsort, avoiding stack growth. Finally, for partitions smaller than 16 elements, insertion sort takes over, as it&amp;rsquo;s faster on small ranges.&lt;/p&gt;
&lt;h2 class="relative group"&gt;Global Game Jam 2025
&lt;div id="global-game-jam-2025" class="anchor"&gt;&lt;/div&gt;
&lt;/h2&gt;
&lt;p&gt;I took part in the Global Game Jam 2025 in my city, where we built a game using nCine. After the jam I polished it and released it on GitHub as &lt;a href="https://github.com/encelo/WetPaper" target="_blank" rel="noreferrer"&gt;Wet Paper&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Compared to the jam version, I added several features: a complete menu system, player statistics, TOML-based configuration, custom blur and refraction shaders, crossfading for music, a low-pass filter when pausing, and joystick vibration.&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="lazy" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/jvhKzdlgR4Q?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="Wet Paper – Gameplay Demo (GGJ 2025 / nCine Game)"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;The jam also gave me a chance to improve nCine itself with new features and quality-of-life tweaks. For example, the engine now keeps track of the previous frame state for keyboard and joystick input, making it trivial to check exactly when a button was pressed or released.&lt;/p&gt;
&lt;p&gt;I also made the vector classes return a zero vector when the length is too short (like many other engines), renamed &lt;code&gt;interval()&lt;/code&gt; to &lt;code&gt;frameTime()&lt;/code&gt; and &lt;code&gt;apptest_scene&lt;/code&gt; to &lt;code&gt;apptest_gui&lt;/code&gt; for clarity, and fixed broken alpha getters in the &lt;code&gt;SceneNode&lt;/code&gt; class. A color setter override that accepted a float parameter has been renamed, and a method was added to set a &lt;code&gt;TimeStamp&lt;/code&gt; to the current time without allocating a new object.&lt;/p&gt;
&lt;p&gt;Working on custom shaders for Wet Paper also led me to fix viewport clearing logic and repair OpenGL debug groups, which hadn&amp;rsquo;t been working properly for viewports.&lt;/p&gt;
&lt;p&gt;One of the last additions was joystick vibration. SDL2&amp;rsquo;s rumble API (&lt;a href="https://wiki.libsdl.org/SDL2/SDL_JoystickRumble" target="_blank" rel="noreferrer"&gt;&lt;code&gt;SDL_JoystickRumble()&lt;/code&gt;&lt;/a&gt;) is quite barebones: you set the intensity for the two motors and a duration in milliseconds, but each call cancels the previous effect. That&amp;rsquo;s why I started working on a &lt;code&gt;JoyVibrator&lt;/code&gt; class to interpolate motor intensities independently. The work isn&amp;rsquo;t finished yet and lives in a local &lt;code&gt;joy_vibrator&lt;/code&gt; branch. &amp;#x1f605;&lt;/p&gt;
&lt;h2 class="relative group"&gt;Presentation at /dev/games
&lt;div id="presentation-at-devgames" class="anchor"&gt;&lt;/div&gt;
&lt;/h2&gt;
&lt;p&gt;&lt;figure&gt;&lt;img
class="my-0 rounded-md"
loading="lazy"
decoding="async"
fetchpriority="low"
alt="Presenting at /dev/games 2025"
src="https://ncine.github.io/img/posts/DevGames2025.jpg"
&gt;&lt;figcaption&gt;Presenting at /dev/games 2025&lt;/figcaption&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;On June 5th I was invited to Rome to present my 14-year journey with nCine at the &lt;a href="https://devgames.org/" target="_blank" rel="noreferrer"&gt;/dev/games&lt;/a&gt; conference.&lt;/p&gt;
&lt;p&gt;It was both a chance to catch up with friends and an opportunity to show developers and students the craft, the struggles, and the technical insights involved in sustaining a long-term project like this.&lt;/p&gt;
&lt;p&gt;You can browse the &lt;a href="https://encelo.github.io/nCine_14Years_Presentation/" target="_blank" rel="noreferrer"&gt;presentation&lt;/a&gt; online. I made it with Slidev and published the Markdown source on GitHub. The talk was recorded and should appear on the official YouTube channel later this year.&lt;/p&gt;
&lt;h3 class="relative group"&gt;RenderDoc integration update
&lt;div id="renderdoc-integration-update" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;While preparing fresh screenshots for the talk, I revisited the RenderDoc integration and updated it to the latest 1.6.0 API. This allowed me to add features such as capture titles, automatically opening the RenderDoc UI after a capture, and enabling API validation and callstack capturing by default when using an OpenGL debug context.&lt;/p&gt;
&lt;h2 class="relative group"&gt;Run-time environment variables
&lt;div id="run-time-environment-variables" class="anchor"&gt;&lt;/div&gt;
&lt;/h2&gt;
&lt;p&gt;Here&amp;rsquo;s a small but useful quality-of-life change. You can now override &lt;code&gt;AppConfiguration&lt;/code&gt; values at runtime through environment variables instead of recompiling your application.&lt;/p&gt;
&lt;p&gt;This makes it easy to test your game under varying conditions: from sound frequencies and shader cache usage to window resolutions, render command pool size, or log levels.&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;NCINE_APPCFG_CONSOLE_LOG_LEVEL&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt; NCINE_APPCFG_FILE_LOG_LEVEL&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#ae81ff"&gt;3&lt;/span&gt; NCINE_APPCFG_LOG_FILE&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;game.txt ./my-ncine-game&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This command disables console logging while redirecting it to a file of your choice. Combine this with scripts and you can quickly test multiple configurations. More details are available in the wiki &lt;a href="https://github.com/nCine/nCine/wiki/AppCfg-EnvVars" target="_blank" rel="noreferrer"&gt;article&lt;/a&gt;.&lt;/p&gt;
&lt;h2 class="relative group"&gt;Crashpad integration
&lt;div id="crashpad-integration" class="anchor"&gt;&lt;/div&gt;
&lt;/h2&gt;
&lt;p&gt;More than three years ago I began integrating Google Crashpad, a modern replacement for the now unsupported CrashRpt that I had used in &lt;code&gt;ncParticleEditor&lt;/code&gt;. Crashpad is cross-platform, actively maintained by Google (they use it in Chrome), and runs as an out-of-process component, a perfect fit for my requirements.&lt;/p&gt;
&lt;p&gt;The tricky part was Android, where distributing the &lt;code&gt;crashpad_handler&lt;/code&gt; executable can trigger security restrictions depending on OS version. The solution was to ship it disguised as a JNI library and use &lt;code&gt;nativeLibraryDir()&lt;/code&gt; to retrieve its location.&lt;/p&gt;
&lt;p&gt;Another addition is the ability to extract debug info files to a user-specified directory without enabling Crashpad:&lt;/p&gt;
&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cmake -S nCine -B nCine-build -D NCINE_DEBUGINFO&lt;span style="color:#f92672"&gt;=&lt;/span&gt;EXTRACT -D DEBUGINFO_DIR&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;${&lt;/span&gt;CMAKE_BINARY_DIR&lt;span style="color:#e6db74"&gt;}&lt;/span&gt;/symbols&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This way you can upload debug symbols to platforms like Sentry, which itself uses Crashpad internally.&lt;/p&gt;
&lt;h2 class="relative group"&gt;Array improvements
&lt;div id="array-improvements" class="anchor"&gt;&lt;/div&gt;
&lt;/h2&gt;
&lt;p&gt;Even the well-proven &lt;code&gt;Array&lt;/code&gt; class had room for improvement.&lt;/p&gt;
&lt;p&gt;First, I fixed a subtle bug with insertions and removals in the middle: the old implementation overwrote elements without destroying them first. Thanks to &lt;a href="https://github.com/W4RH4WK" target="_blank" rel="noreferrer"&gt;W4RH4WK&lt;/a&gt; on Discord for reporting it! There&amp;rsquo;s now a unit test checking construction, destruction, and assignment counts for these operations.&lt;/p&gt;
&lt;p&gt;Second, I reworked the type-trait helpers in &lt;code&gt;utility.h&lt;/code&gt;. They now distinguish between trivially copyable, movable and copyable, movable-only, copyable-only, and fully non-movable/non-copyable objects.
Two new &lt;code&gt;setCapacity()&lt;/code&gt; implementations now use tag dispatching to select the right behavior based on the Array class&amp;rsquo;s template type. Thanks to this, you can create Array instances containing non-copyable and non-movable objects, with the restriction that arrays must be empty to resize and new elements can only be added at the back via &lt;code&gt;emplaceBack()&lt;/code&gt;.
This wasn&amp;rsquo;t even compiling before. &amp;#x1f62e;&lt;/p&gt;
&lt;h2 class="relative group"&gt;Multi-threaded job system
&lt;div id="multi-threaded-job-system" class="anchor"&gt;&lt;/div&gt;
&lt;/h2&gt;
&lt;p&gt;&lt;figure&gt;&lt;img
class="my-0 rounded-md"
loading="lazy"
decoding="async"
fetchpriority="low"
alt="Tracy capture of apptest_jobsystem"
src="/images/Tracy_apptest_jobsystem.png"
&gt;&lt;figcaption&gt;Tracy capture of apptest_jobsystem&lt;/figcaption&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;The star of this update is the new job system. After months of iteration, it has finally stabilized in structure and API.&lt;/p&gt;
&lt;p&gt;Jobs now use opaque &lt;code&gt;JobId&lt;/code&gt; handles that encode both a pool index and a generation number, which makes detecting stale IDs easy. I spent a long time experimenting with a fully lock-free job pool but couldn&amp;rsquo;t get correct behavior. The final design uses per-thread job caches and a global pool protected by a mutex.&lt;/p&gt;
&lt;p&gt;Worker synchronization now uses semaphores instead of a mutex + condition variable pair to wake threads when new jobs are available. This avoids the extra lock/unlock overhead and makes the wakeup path more direct. On each platform the fastest available primitive is used: on Linux a futex-based userspace semaphore, on Windows &lt;a href="https://learn.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-waitonaddress" target="_blank" rel="noreferrer"&gt;&lt;code&gt;WaitOnAddress()&lt;/code&gt;&lt;/a&gt; with atomics instead of a kernel semaphore object, and on macOS a Grand Central Dispatch semaphore instead of a pthreads implementation.&lt;/p&gt;
&lt;p&gt;To tame the many threading issues during development, I added &lt;code&gt;jobsystem_debug.h&lt;/code&gt;, which lets you enable Tracy zones and plots, statistical counters, additional logs, and job state tracking via compile-time flags. These debug tools have been lifesavers.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s also a serial job system: if you set a single-thread mode in &lt;code&gt;AppConfiguration&lt;/code&gt; you&amp;rsquo;ll get the same API without synchronization, perfect for debugging or measuring scalability.&lt;/p&gt;
&lt;p&gt;A recent commit also added a handle class with an object-oriented API over job IDs, new job state flags to prevent double submissions and support cancellation, and a new submit call to allow for multiple jobs to be submitted at once.&lt;/p&gt;
&lt;h3 class="relative group"&gt;CPU topology
&lt;div id="cpu-topology" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;&lt;figure&gt;&lt;img
class="my-0 rounded-md"
loading="lazy"
decoding="async"
fetchpriority="low"
alt="CPU Topologies Diagram"
src="/images/CPU_Topologies.png"
&gt;&lt;figcaption&gt;CPU Topologies Diagram&lt;/figcaption&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;I completely reworked how the thread pool is created and how affinity is set, making the system topology-aware.&lt;/p&gt;
&lt;p&gt;The idea is to sort cores by speed, to leave the main thread unpinned, and to start pinning worker threads from the second fastest physical core.&lt;/p&gt;
&lt;p&gt;I have tested it on some of my devices:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;On a Ryzen 9 6900HS (8C/16T), it creates 7 workers pinned to physical cores, leaving the main thread free.&lt;/li&gt;
&lt;li&gt;On an Intel i5-1235U (2P + 8E, 10C/12T), it creates 9 workers: one on the second performance core and eight on the efficiency cores, with the main thread unpinned.&lt;/li&gt;
&lt;li&gt;On a Mac Mini M1 (4P + 4E), it pins 7 workers, leaving the main thread unpinned and the first performance core free.&lt;/li&gt;
&lt;li&gt;On a Snapdragon 8 Gen 3 (1Pr + 3P + 2P + 2E), it uses sysfs &lt;code&gt;cpu_capacity&lt;/code&gt; values to pin 7 workers across the three weaker clusters, leaving the main thread unpinned and the Prime core free.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This could eventually allow more advanced scheduling in the future, with certain job tags mapped to faster or slower cores depending on priority.&lt;/p&gt;
&lt;h3 class="relative group"&gt;Conclusions
&lt;div id="conclusions" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;The best part: the job system is fully exposed to applications. You can try it right now in the new &lt;code&gt;apptest_jobsystem&lt;/code&gt; test. &amp;#x1f64c;&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;job_system&lt;/code&gt; branch is already live and up to date, though it needs more testing before merging. Meanwhile, I&amp;rsquo;ve begun work on a data-oriented ECS, an ideal testing ground for the job system. More on that in the next update.&lt;/p&gt;
&lt;h2 class="relative group"&gt;Minor changes
&lt;div id="minor-changes" class="anchor"&gt;&lt;/div&gt;
&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Added conversion functions between &lt;code&gt;Vector*i&lt;/code&gt; and &lt;code&gt;Vector*f&lt;/code&gt;, and between &lt;code&gt;Recti&lt;/code&gt; and &lt;code&gt;Rectf&lt;/code&gt; classes&lt;/li&gt;
&lt;li&gt;Updated &lt;code&gt;README&lt;/code&gt; with documentation links and screenshots &amp;#x1f4f7;&lt;/li&gt;
&lt;li&gt;Added new dirty bits to nodes (thanks to &lt;a href="https://github.com/jugilus" target="_blank" rel="noreferrer"&gt;Jugilus&lt;/a&gt;) to avoid redundant transformations of culled nodes&lt;/li&gt;
&lt;li&gt;Fixed a long-standing bug with string capacity changes when using custom allocators&lt;/li&gt;
&lt;li&gt;Updated GitHub Actions runners: added macOS 15 and Ubuntu 24.04, retired VS2019&lt;/li&gt;
&lt;li&gt;Vector, matrix, and quaternion classes now have inequality operators &amp;#x1f604;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>nCine Dev Update 21</title><link>https://encelo.github.io/2025-01-14-ncine-dev-update-21/</link><pubDate>Tue, 14 Jan 2025 00:00:00 +0000</pubDate><guid>https://encelo.github.io/2025-01-14-ncine-dev-update-21/</guid><description>&lt;p&gt;In this article, we’ll go over the progress of the nCine throughout 2024.&lt;/p&gt;
&lt;h2 class="relative group"&gt;OpenAL EFX
&lt;div id="openal-efx" class="anchor"&gt;&lt;/div&gt;
&lt;/h2&gt;
&lt;p&gt;The biggest change this year has been support for the OpenAL EFX extension. You can now apply effects to any audio player, like reverb, echo, flanger, and more, and use low and high-pass filters.&lt;/p&gt;
&lt;p&gt;This was also a chance to improve the OpenAL code by adding new features and fixing some old bugs.&lt;/p&gt;
&lt;p&gt;For example, you can now adjust the OpenAL context attributes to change the number of audio sources or adjust the frequency. Sources now support more properties, like velocity, direction, and cone angles. With the EFX extension, even more properties become available.&lt;/p&gt;
&lt;p&gt;There’s now a pool of OpenAL sources that are assigned to players as needed. This lets you have more players than hardware sources, as only those actively playing need a source. You can also lock sources to frequently used players to avoid the system from reassigning properties. And if there’s an OGG audio dropout (like an &lt;code&gt;OV_HOLE&lt;/code&gt; error), decoding continues without stopping.&lt;/p&gt;
&lt;p&gt;The new effects and filters API is also available in Lua, and the &lt;code&gt;apptest_audio&lt;/code&gt; application has been updated to test both it and the source pool.&lt;/p&gt;
&lt;h2 class="relative group"&gt;Multi-threaded job system
&lt;div id="multi-threaded-job-system" class="anchor"&gt;&lt;/div&gt;
&lt;/h2&gt;
&lt;p&gt;Another major feature in progress is a multi-threaded job system, though it’s not finished yet.&lt;/p&gt;
&lt;p&gt;I’ve been thinking about addressing CPU bottlenecks in the nCine by implementing a data-oriented ECS with a multi-threaded job system. I started working on the &lt;a href="https://blog.molecular-matters.com/2015/08/24/job-system-2-0-lock-free-work-stealing-part-1-basics/" target="_blank" rel="noreferrer"&gt;molecular matters&lt;/a&gt; job system, which includes a lock-free work-stealing queue, parent-child jobs, continuations, and parallel for loops. Their articles provide a simple implementation of a solid system that seems to fit my needs well.&lt;/p&gt;
&lt;p&gt;While making the logger thread-safe using a queue of messages, I paused work on the job system to focus on something else.&lt;/p&gt;
&lt;h2 class="relative group"&gt;Lua development improvements
&lt;div id="lua-development-improvements" class="anchor"&gt;&lt;/div&gt;
&lt;/h2&gt;
&lt;p&gt;While working on the job system, I came across the Lua Language Server and decided to add support for it. Improving IDE support for the nCine with Lua has always been on my mind, and LuaLS was the right tool for the job.&lt;/p&gt;
&lt;p&gt;It took a few weeks to collect all the Lua API functions into &lt;a href="https://github.com/nCine/nCine-LuaCATS" target="_blank" rel="noreferrer"&gt;LuaCATS&lt;/a&gt; definition files, but it was worth it.&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="lazy" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/vyXqnrW5_5Y?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="The new Lua development workflow with the nCine"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;As shown in the video, using the Lua extension for Visual Studio Code now gives you autocomplete, type checking, and inline API documentation. This makes the nCine experience similar to using frameworks like LÖVE 2D or Solar2D. The LuaCATS work also made it easy to create a new &lt;a href="https://ncine.github.io/docs/lua_master/" target="_blank" rel="noreferrer"&gt;LDoc documentation&lt;/a&gt; that’s available online.&lt;/p&gt;
&lt;p&gt;Reviewing the Lua API also allowed me to fix missing function exports, rename inconsistent methods, and write documentation that filled in missing Doxygen comments for the C++ API.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;&lt;img
class="my-0 rounded-md"
loading="lazy"
decoding="async"
fetchpriority="low"
alt="Lua Language Server"
src="/images/Lua_Language_Server.png"
&gt;&lt;figcaption&gt;Lua Language Server&lt;/figcaption&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 class="relative group"&gt;Improvements to &lt;code&gt;apptest_lua&lt;/code&gt;
&lt;div id="improvements-to-apptest_lua" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;apptest_lua&lt;/code&gt; application, which becomes the &lt;code&gt;ncinelua&lt;/code&gt; executable in the Lua Distribution version, is the tool users use to write their games in Lua.&lt;/p&gt;
&lt;p&gt;It already supported features like script hot-reloading and basic on-screen error display, but not all errors were shown, and users often had to rely on the console log.&lt;/p&gt;
&lt;p&gt;Now, errors from application callbacks like &lt;code&gt;on_init()&lt;/code&gt; or &lt;code&gt;on_frame_start()&lt;/code&gt; are displayed on-screen, just like in the console. I also added support for tab characters in the &lt;code&gt;TextNode&lt;/code&gt; class, so Lua call stacks are properly indented. &amp;#x1f604;&lt;/p&gt;
&lt;p&gt;The executable can now be run with &lt;code&gt;-h&lt;/code&gt;/&lt;code&gt;--help&lt;/code&gt; and &lt;code&gt;-v&lt;/code&gt;/&lt;code&gt;--version&lt;/code&gt; parameters, making it more like a standard command-line tool. To support this, I added a way to quit the framework from the &lt;code&gt;onPreInit()&lt;/code&gt; callback, so no initialization happens, and no window briefly appears. You can also disable logging from this callback to completely silence the framework. 🤐&lt;/p&gt;
&lt;h3 class="relative group"&gt;Minor changes
&lt;div id="minor-changes" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Joystick axes mapped as buttons are now supported&lt;/li&gt;
&lt;li&gt;Added new constructors to the &lt;code&gt;MemoryFile&lt;/code&gt; class to take ownership of externally allocated buffers&lt;/li&gt;
&lt;li&gt;Fixed a typo in a preprocessor conditional that prevented joystick hats from working in GLFW&lt;/li&gt;
&lt;li&gt;Moved the &lt;code&gt;Particle&lt;/code&gt; class header to public headers to fix a forward declaration warning in &lt;code&gt;ParticleSystem&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;macOS builds now use two GitHub Actions runners, with one compiling nCine natively for Apple Silicon&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>nCine Dev Update 20</title><link>https://encelo.github.io/2023-12-12-ncine-dev-update-20/</link><pubDate>Tue, 12 Dec 2023 00:00:00 +0000</pubDate><guid>https://encelo.github.io/2023-12-12-ncine-dev-update-20/</guid><description>&lt;p&gt;Lately, the development rate of the nCine slowed down a bit. I think it is normal for a project that spans so many years, and developed by a single person, to see some oscillations.
This is why this article covers such a long period, a period in which there have been maybe a few new features, but important ones.&lt;/p&gt;
&lt;h2 class="relative group"&gt;Binary shaders
&lt;div id="binary-shaders" class="anchor"&gt;&lt;/div&gt;
&lt;/h2&gt;
&lt;p&gt;For sure the most interesting, and time-consuming, feature added since the last article is the support for binary shaders.&lt;/p&gt;
&lt;p&gt;When it is enabled, OpenGL shaders are compiled only the first time an application runs. They are then cached on disk as binary blobs and loaded on subsequent runs.
Loading a binary file is, of course, a lot faster than loading and compiling sources.&lt;/p&gt;
&lt;p&gt;The feature was developed mainly for ANGLE, as its OpenGL to DirectX shader translation and compilation layer is quite slow. ANGLE is used on Windows inside browsers, but WebGL disables loading and saving binary shaders for security reasons.
The main interest in making ANGLE shaders load faster resided in &lt;a href="https://github.com/deathkiller/jazz2-native" target="_blank" rel="noreferrer"&gt;Jazz² Resurrection&lt;/a&gt; support for Universal Windows Platform and thus Xbox. &amp;#x1f3ae;&lt;/p&gt;
&lt;p&gt;The feature is enabled by default on Android as it can help slow devices to start an application faster.&lt;/p&gt;
&lt;h3 class="relative group"&gt;Hashing
&lt;div id="hashing" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;A fundamental part of this new feature is hashing shader sources. If shader sources have not changed, we can continue to load the same binary file, otherwise, we need to compile and save a new one.&lt;/p&gt;
&lt;p&gt;I was initially hashing all the sources used by a shader, every time I was loading it. This leads to correct results all the time, of course, but it can be slow and superfluous, especially for the default shaders that come with the nCine.&lt;/p&gt;
&lt;p&gt;Default shader sources can be embedded in the nCine library or loaded from files, in both cases hashing the sources at run-time is not needed. In the first case, I use CMake to calculate an MD5 hash of source contents and embed it in the library. In the second case, I calculate a custom hash value from the name, size, and modification date of the file.&lt;/p&gt;
&lt;p&gt;The hash is then compared with the value in a shader info file, a text file that contains a list of all successfully compiled shaders.&lt;/p&gt;
&lt;p&gt;While the hash calculated by sources is a very important value to identify a particular shader, there is also another hash value that is combined to create the binary shader filename: the platform hash. It is calculated from the &lt;code&gt;GL_RENDERER&lt;/code&gt; and the &lt;code&gt;GL_VERSION&lt;/code&gt; strings and it ensures that, if the OpenGL driver is updated, shaders will be recompiled.&lt;/p&gt;
&lt;p&gt;The hashing methods for strings and files are part of a new &lt;code&gt;Hash64&lt;/code&gt; class that internally uses &lt;code&gt;fasthash64&lt;/code&gt; and provides a Lua API.&lt;/p&gt;
&lt;h3 class="relative group"&gt;Maximum batch size
&lt;div id="maximum-batch-size" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;Not related to binary shaders, but implemented while revamping the shader compilation pipeline, is the double compilation of batched shaders. Under certain circumstances, for example when the maximum supported size for Uniform Buffer Objects is less than 64 Kb, a batched shader is compiled a first time with a batch size of one so that its size requirements can be queried.&lt;/p&gt;
&lt;p&gt;It is then compiled a second time with the batch array perfectly sized to match the available UBO maximum size. This feature improves the compatibility on some Android devices that do not support the classic 64 Kb UBO size and failed the compilation. In all other cases, the batch size value in shader sources is adjusted for a 64 Kb UBO and the shader does not need to be compiled two times.&lt;/p&gt;
&lt;p&gt;While testing this feature I also encountered a bug in the &lt;code&gt;RenderBatcher&lt;/code&gt; class that could have caused issues with small UBOs and big batches: basically, the condition that forced a split in a batch upon finishing UBO space was wrong. &amp;#x1f605;&lt;/p&gt;
&lt;h2 class="relative group"&gt;HiDPI support
&lt;div id="hidpi-support" class="anchor"&gt;&lt;/div&gt;
&lt;/h2&gt;
&lt;p&gt;Another quite important feature added last year was the support for HiDPI displays.&lt;/p&gt;
&lt;p&gt;The first step towards this goal was extracting as much information as possible from the backends. This is why multiple monitors are now supported.&lt;/p&gt;
&lt;p&gt;Based on the backend, it is now possible to know in real-time if a monitor has been connected or disconnected and to move a window from one monitor to another.
Even on Android, using some JNI glue code, it should be possible to handle monitor connection and disconnection events.&lt;/p&gt;
&lt;p&gt;I have also added the possibility to specify a particular refresh rate on init, for when the application starts in fullscreen.
The user can also specify a window position so that the application can start on the specified monitor and at the specified position.&lt;/p&gt;
&lt;p&gt;But supporting HiDPI also means adapting the window size and its contents to the scaling factor applied by the operating system. This feature is demonstrated by the new &lt;code&gt;apptest_scaling&lt;/code&gt; application.
It uses the new &lt;code&gt;onChangeScalingFactor()&lt;/code&gt; callback to detect if the user explicitly changed the scaling factor of the monitor or dragged the application window to a different monitor.&lt;/p&gt;
&lt;p&gt;Unfortunately, even after a long period of testing, the feature does not seem to be working perfectly, especially on Emscripten. I hope things will get better with new versions of the backends.&lt;/p&gt;
&lt;h2 class="relative group"&gt;Thread class move fixes
&lt;div id="thread-class-move-fixes" class="anchor"&gt;&lt;/div&gt;
&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;Thread&lt;/code&gt; class was developed years ago and tested only marginally, it is only normal it had some hidden bugs.&lt;/p&gt;
&lt;p&gt;One of these was discovered by the Jazz² Resurrection author and happened when a thread object was moved into another one. Sometimes the source object went out of scope before the OS scheduled a new thread, and the pointer passed to the OS API function became invalid, causing a crash.&lt;/p&gt;
&lt;p&gt;To fix this, I wrote custom move special member functions and a swap method, and I moved the threading information structure inside a &lt;code&gt;UniquePtr&lt;/code&gt; so that it is allocated on the heap and moved correctly.&lt;/p&gt;
&lt;p&gt;I also used the opportunity to rename some internal variables, change some method signatures to return a success boolean flag, and add a new &lt;code&gt;detach()&lt;/code&gt; method.&lt;/p&gt;
&lt;h2 class="relative group"&gt;Bunnymark
&lt;div id="bunnymark" class="anchor"&gt;&lt;/div&gt;
&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://github.com/openfl/openfl-samples/tree/master/demos/BunnyMark" target="_blank" rel="noreferrer"&gt;Bunnymark&lt;/a&gt; is a famous 2D benchmark that has been implemented in many different engines and frameworks to showcase the rendering and game logic performance.&lt;/p&gt;
&lt;p&gt;I have implemented a nCine version, &lt;code&gt;apptest_bunnymark&lt;/code&gt;, using the same sprite and logic from the original one. I have also improved the frame timer class and it is now more powerful and flexible.&lt;/p&gt;
&lt;p&gt;Unfortunately, the performance isn&amp;rsquo;t yet where I would like them to be, but the benchmark will be an additional tool to help make the nCine even faster! &amp;#x1f680;&lt;/p&gt;
&lt;h2 class="relative group"&gt;Improvements to particle systems and affectors
&lt;div id="improvements-to-particle-systems-and-affectors" class="anchor"&gt;&lt;/div&gt;
&lt;/h2&gt;
&lt;p&gt;There have been some minor improvements to the particle systems and affectors. For example, affectors can now add steps even if specified out of order, instead of rejecting them. They also come with a new Lua API, so that complex systems can be built with scripting.&lt;/p&gt;
&lt;p&gt;I have also resolved a minor issue when initializing a new particle system with random parameters: they had to respect certain numeric constraints or the system would not work as expected (or even assert and exit &amp;#x1f4a3;). Now the constraints are not only checked but enforced by clamping or reordering values.&lt;/p&gt;
&lt;p&gt;To have more flexibility with complex systems, there is now the option to disable a specific affector or to completely disable the particle update.&lt;/p&gt;
&lt;h2 class="relative group"&gt;Minor changes
&lt;div id="minor-changes" class="anchor"&gt;&lt;/div&gt;
&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Long error textnodes in &lt;code&gt;apptest_lua&lt;/code&gt; are now scaled down to be readable in their entirety&lt;/li&gt;
&lt;li&gt;There is a new method to add multiple rectangles to an animation at once&lt;/li&gt;
&lt;li&gt;Two new CMake presets have been added to support unit tests and micro-benchmarks use cases&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;apptest_lua&lt;/code&gt; application will detect any changes to the &lt;code&gt;script.lua&lt;/code&gt; file and hot-reload it automatically&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;audio&lt;/code&gt;, &lt;code&gt;font&lt;/code&gt;, and &lt;code&gt;particles&lt;/code&gt; apptests have now an ImGui debug interface&lt;/li&gt;
&lt;li&gt;The Android JNI code should now be more stable by using global references&lt;/li&gt;
&lt;li&gt;All input backends have now the support for a &amp;ldquo;&lt;em&gt;drop files&lt;/em&gt;&amp;rdquo; event&lt;/li&gt;
&lt;li&gt;The application will never exit when a file cannot be opened&lt;/li&gt;
&lt;li&gt;You can now choose to render &lt;code&gt;TextNode&lt;/code&gt; objects by using the fragment shaders of regular sprites&lt;/li&gt;
&lt;li&gt;Android gamepad code has been revamped by supporting a fallback system mapping, more button strings, and mapping buttons as axes&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>nCine Compilation Benchmark 2</title><link>https://encelo.github.io/2022-10-06-ncine-compilation-benchmark-2/</link><pubDate>Thu, 06 Oct 2022 00:00:00 +0000</pubDate><guid>https://encelo.github.io/2022-10-06-ncine-compilation-benchmark-2/</guid><description>&lt;p&gt;I just got a new laptop, an Asus ROG Zephyrus G15 GA503RM (2022), and of course, I&amp;rsquo;m timing the nCine compilation to see how much time it will make me save. &amp;#x1f609;
Should you be interested in the first compilation benchmark article, it is available &lt;a href="/2018-03-04-ncine-compilation-benchmark" &gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;As always, let&amp;rsquo;s start with the hardware and software details.&lt;/p&gt;
&lt;h2 class="relative group"&gt;Hardware and software
&lt;div id="hardware-and-software" class="anchor"&gt;&lt;/div&gt;
&lt;/h2&gt;
&lt;h3 class="relative group"&gt;Xiaomi Mi Notebook Pro (2017)
&lt;div id="xiaomi-mi-notebook-pro-2017" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Intel Core i7-8550U&lt;/li&gt;
&lt;li&gt;16GB RAM, DDR4 2400 MHz, dual channel&lt;/li&gt;
&lt;li&gt;Samsung 970 EVO Plus NVMe SSD, 512GB&lt;/li&gt;
&lt;li&gt;NVIDIA GeForce MX150, 2GB GDDR5&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 class="relative group"&gt;Asus Zephyrus G15 (2022)
&lt;div id="asus-zephyrus-g15-2022" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;AMD Ryzen 7 6800HS&lt;/li&gt;
&lt;li&gt;16GB RAM, DDR5 4800MHz, dual channel&lt;/li&gt;
&lt;li&gt;Western Digital PC SN735 NVMe SSD, 1TB&lt;/li&gt;
&lt;li&gt;NVIDIA GeForce RTX 3060, 6GB GDDR6&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 class="relative group"&gt;OS, Compilers, and Tools
&lt;div id="os-compilers-and-tools" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Arch Linux 5.19.13 x86_64&lt;/li&gt;
&lt;li&gt;GCC 12.2.0&lt;/li&gt;
&lt;li&gt;CMake 3.24.2&lt;/li&gt;
&lt;li&gt;Ninja 1.11.1&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 class="relative group"&gt;Results
&lt;div id="results" class="anchor"&gt;&lt;/div&gt;
&lt;/h2&gt;
&lt;p&gt;The nCine version is &lt;code&gt;2020.05.r115.g001bdce&lt;/code&gt; (or &lt;code&gt;r422.001bdce&lt;/code&gt;) from the &lt;code&gt;master&lt;/code&gt; branch, compiled with the default options:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;NCINE_PREFERRED_BACKEND=GLFW&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;NCINE_BUILD_TESTS=ON&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;NCINE_BUILD_UNIT_TESTS=OFF&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;NCINE_BUILD_ANDROID=OFF&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As usual, the tests have been conducted by running the compilation process multiple times and recording the best times.&lt;/p&gt;
&lt;p&gt;In the &lt;em&gt;Configure&lt;/em&gt; phase, CMake is invoked to configure the project and generate Ninja files.&lt;/p&gt;
&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;time cmake -S nCine -B nCine-build -G Ninja &amp;amp;&amp;gt; /dev/null&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;In the &lt;em&gt;Build&lt;/em&gt; phase, ninja is invoked to build the project with all or just one core.&lt;/p&gt;
&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;time ninja &amp;amp;&amp;gt; /dev/null
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;time ninja -j1 &amp;amp;&amp;gt; /dev/null&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;In both phases, I redirected all the output to &lt;code&gt;/dev/null&lt;/code&gt; to save the console printing time.&lt;/p&gt;
&lt;h3 class="relative group"&gt;Tables and charts
&lt;div id="tables-and-charts" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th style="text-align: right"&gt;Mi Notebook&lt;/th&gt;
&lt;th style="text-align: right"&gt;Zephyrus G15&lt;/th&gt;
&lt;th style="text-align: right"&gt;Ratio&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Configure Ninja&lt;/td&gt;
&lt;td style="text-align: right"&gt;1.715 s&lt;/td&gt;
&lt;td style="text-align: right"&gt;1.330 s&lt;/td&gt;
&lt;td style="text-align: right"&gt;1.289x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Build Ninja&lt;/td&gt;
&lt;td style="text-align: right"&gt;12.774 s&lt;/td&gt;
&lt;td style="text-align: right"&gt;4.683 s&lt;/td&gt;
&lt;td style="text-align: right"&gt;2.727x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Build Ninja &lt;code&gt;-j1&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align: right"&gt;48.141 s&lt;/td&gt;
&lt;td style="text-align: right"&gt;31.731 s&lt;/td&gt;
&lt;td style="text-align: right"&gt;1.517x&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;figure&gt;&lt;img
class="my-0 rounded-md"
loading="lazy"
decoding="async"
fetchpriority="low"
alt="Ninja multi-core chart"
src="/images/Zephyrus_ninja_multi_chart.png"
&gt;&lt;figcaption&gt;Ninja multi-core chart&lt;/figcaption&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;&lt;img
class="my-0 rounded-md"
loading="lazy"
decoding="async"
fetchpriority="low"
alt="Ninja single-core chart"
src="/images/Zephyrus_ninja_single_chart.png"
&gt;&lt;figcaption&gt;Ninja single-core chart&lt;/figcaption&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 class="relative group"&gt;Conclusions
&lt;div id="conclusions" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;The Ryzen 6800HS, with its eight Zen 3+ cores, smashes the old Mi Notebook Pro, with its four Kaby Lake R cores, compiling the nCine in one-third of the time! &amp;#x1f4aa;
I&amp;rsquo;m sure that the faster SSD and RAM also help in this test.&lt;/p&gt;
&lt;p&gt;I was expecting a bit more from the single-core compilation results, but completing the compilation in roughly 65% of the time is not bad.&lt;/p&gt;
&lt;p&gt;These results are very close to what we can expect by running &lt;a href="https://www.cpu-monkey.com/en/compare_cpu-intel_core_i7_8550u-vs-amd_ryzen_7_6800hs" target="_blank" rel="noreferrer"&gt;Cinebench R23&lt;/a&gt;, for example.&lt;/p&gt;
&lt;p&gt;The smaller difference can be found in the configuration phase: the new laptop still needs 3/4 of the time of the old one. CMake is surely using just one core and maybe hitting an I/O bottleneck, but a small improvement can still be found.&lt;/p&gt;
&lt;p&gt;I hope you enjoyed this benchmarking article as much as I am enjoying my new machine. &amp;#x1f609;&lt;/p&gt;</description></item><item><title>nCine Dev Update 19</title><link>https://encelo.github.io/2022-09-07-ncine-dev-update-19/</link><pubDate>Wed, 07 Sep 2022 00:00:00 +0000</pubDate><guid>https://encelo.github.io/2022-09-07-ncine-dev-update-19/</guid><description>&lt;p&gt;Yet another update coming after a very long time since the previous, apologies for that. Well, at least it comes packed with a lot of enhancements from the last months. &amp;#x1f4aa;&lt;/p&gt;
&lt;h2 class="relative group"&gt;Custom shaders
&lt;div id="custom-shaders" class="anchor"&gt;&lt;/div&gt;
&lt;/h2&gt;
&lt;p&gt;Probably the biggest feature of 2022, and the culmination of work that started with viewports a year ago, is the support for custom shaders.&lt;/p&gt;
&lt;p&gt;You can now write your own vertex and fragment shaders and assign them to a node. All of that is possible while also retaining the usual automatic batching. &amp;#x1f632;
In combination with viewports, it is now possible to create complex scenes, featuring post-processing and modern effects.&lt;/p&gt;
&lt;p&gt;Take for example the new &lt;code&gt;apptest_shaders&lt;/code&gt;, it shows regular and mesh sprites, all of which use custom shaders, custom vertex attributes, and they preserve automatic batching.
You will notice that some sprites are rendered with normal and specular mapping and that the light position can be moved in real-time! &amp;#x1f4a1;&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s not all, with the ImGui interface or with the keyboard, you can enable a full screen gaussian blur post-processing or a bloom effect.
The latter is achieved with a quite complex viewports setup, with off-screen rendering, down-sampling, blurring, up-sampling, and compositing.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;&lt;img
class="my-0 rounded-md"
loading="lazy"
decoding="async"
fetchpriority="low"
alt="apptest_shaders"
src="/images/apptest_shaders.png"
&gt;&lt;figcaption&gt;apptest_shaders&lt;/figcaption&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;But the road to achieving all that was quite long and required many months. It all started by checking out a very old branch in which I was planning out how to approach the matter in question.&lt;/p&gt;
&lt;h3 class="relative group"&gt;ShaderState class
&lt;div id="shaderstate-class" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;In the first iteration the &lt;code&gt;ShaderState&lt;/code&gt; class, the one handling shader uniforms, was embedded inside a &lt;code&gt;DrawableNode&lt;/code&gt;.
The user could then set a lambda function through the &lt;code&gt;ShaderState&lt;/code&gt; object that the parent node would call each time it was going to be rendered.&lt;/p&gt;
&lt;p&gt;In the second one, I moved the &lt;code&gt;ShaderState&lt;/code&gt; class outside, so that nodes using the default shaders would know nothing about it.
To change uniforms you could query the culling state of a node in the &lt;code&gt;onPostUpdate()&lt;/code&gt; callback and update their values.&lt;/p&gt;
&lt;h3 class="relative group"&gt;OpenGL layer
&lt;div id="opengl-layer" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;The OpenGL classes have seen upgrades too, for example in the way shaders and shader programs were created and loaded. Shaders are now just another resource, they can be loaded from files or strings multiple times, and if they fail to compile this would only affect the nodes that use them for rendering.&lt;/p&gt;
&lt;p&gt;Two other important upgrades were fundamental to support the normal mapping and bloom effects in &lt;code&gt;apptest_shaders&lt;/code&gt;: the first is multi-texturing and the other is multiple render targets.
The former allows more than one texture to be used as input for a shader, while the latter allows a shader to write on more than one texture.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;GLShaderAttributes&lt;/code&gt; class has been deleted and its functionality has been moved inside the &lt;code&gt;GLShaderProgram&lt;/code&gt; class. Missing the flexibility of changing the attribute setup per node is not going to affect the rendering at all, while the change simplifies the code of many classes with a possible uplift in performance.&lt;/p&gt;
&lt;p&gt;If you run your game with a debug context inside a graphics debugger like RenderDoc, you should see additional information thanks to more &lt;code&gt;glObjectLabel()&lt;/code&gt; calls.&lt;/p&gt;
&lt;h3 class="relative group"&gt;Viewports
&lt;div id="viewports" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;Viewports have been upgraded as well. First of all, setting up a chain of viewports for rendering is a lot easier now as you can directly access the &lt;code&gt;Viewport::chain()&lt;/code&gt; array instead of using multiple obscure and error-prone calls to &lt;code&gt;setNextViewport()&lt;/code&gt;. It is also possible to easily set up cycles for multi-pass rendering techniques.&lt;/p&gt;
&lt;p&gt;The second important change, one that simplifies the internals while adding flexibility, is the removal of the texture inside the &lt;code&gt;Viewport&lt;/code&gt; class. A &lt;code&gt;Texture&lt;/code&gt; object should now be passed to a viewport from outside, a solution that opens a range of possibilities, from sharing a texture between multiple viewports to using the output of one as the input of another.&lt;/p&gt;
&lt;p&gt;Those two enhancements are at the base of the blur and bloom setups in &lt;code&gt;apptest_shaders&lt;/code&gt;.&lt;/p&gt;
&lt;h3 class="relative group"&gt;More changes
&lt;div id="more-changes" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;There is now a new &lt;code&gt;onResizeWindow()&lt;/code&gt; callback that the game can use to know when the window resolution changes. It comes in handy to recreate viewport textures if you have a post-processing setup in place.&lt;/p&gt;
&lt;p&gt;The example Lua script has been updated as well to show how you can use viewports and shaders with scripts.&lt;/p&gt;
&lt;h2 class="relative group"&gt;Static string class
&lt;div id="static-string-class" class="anchor"&gt;&lt;/div&gt;
&lt;/h2&gt;
&lt;p&gt;Like many other items on my to-do list, this was something I have been thinking about for some time. Having a string class that does not allocate space for characters should remove the need for a plain old &lt;code&gt;char&lt;/code&gt; array while retaining all the useful formatting, appending, and comparing methods of the standard &lt;code&gt;String&lt;/code&gt; class.&lt;/p&gt;
&lt;p&gt;I have begun to use it in new code and tried to backport in some old cases where it made the most sense.
As usual, it comes with a battery of GTest unit tests that should ensure its correct functioning.&lt;/p&gt;
&lt;h2 class="relative group"&gt;Fixes to viewports
&lt;div id="fixes-to-viewports" class="anchor"&gt;&lt;/div&gt;
&lt;/h2&gt;
&lt;p&gt;Apart from the big viewports API overhaul made in the &lt;code&gt;custom_shaders&lt;/code&gt; branch, there were other small issues that I discovered after merging the corresponding branch.&lt;/p&gt;
&lt;p&gt;For example, if multiple viewports were rendering the same scene node, its &lt;code&gt;update()&lt;/code&gt; method would have been called multiple times. &amp;#x1f605;
This is now fixed by using a counter that stores the last frame a node has been updated by any viewport.&lt;/p&gt;
&lt;p&gt;I have inverted the position and rotation values used by a camera so that it feels like an object that you can move in the scene.
It leaves back the old concept of simulating movement by moving the rest of the world in the opposite direction.&lt;/p&gt;
&lt;p&gt;When using the Qt5 backend the viewports were not rendering correctly as Qt5 uses its own framebuffer object to render a &lt;code&gt;QOpenGLWidget&lt;/code&gt; off-screen and composite it later.&lt;/p&gt;
&lt;h2 class="relative group"&gt;Rendering order based on node visit
&lt;div id="rendering-order-based-on-node-visit" class="anchor"&gt;&lt;/div&gt;
&lt;/h2&gt;
&lt;p&gt;This change brings the nCine closer to the behavior of other frameworks like &lt;a href="https://docs.godotengine.org/en/stable/tutorials/2d/canvas_layers.html#canvaslayers" target="_blank" rel="noreferrer"&gt;Godot&lt;/a&gt;, &lt;a href="https://defold.com/manuals/gui/#draw-order" target="_blank" rel="noreferrer"&gt;Defold&lt;/a&gt;, or &lt;a href="https://docs.cocos.com/creator/manual/en/ui-system/components/engine/priority.html#ui-node-ordering" target="_blank" rel="noreferrer"&gt;Cocos&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The user can still affect the drawing order of nodes with layers, but by default, it is dictated by the visiting order of the node in the scenegraph. This change makes the order more reliable, intuitive, and similar to what users are already used to.&lt;/p&gt;
&lt;h2 class="relative group"&gt;Safer Lua pointers dereferencing
&lt;div id="safer-lua-pointers-dereferencing" class="anchor"&gt;&lt;/div&gt;
&lt;/h2&gt;
&lt;p&gt;This is a very important change for scripters, before this change they might have encountered a crash too frequently while working with user data pointers.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s say you created a nCine object inside a Lua script, like a sprite. The engine would then create a &lt;code&gt;Sprite&lt;/code&gt; object and a &lt;code&gt;UserDataWrapper&lt;/code&gt; object.
The latter would be set to contain the pointer and type of the sprite, added to an array of wrappers, then its pointer returned to the user inside a light userdata.&lt;/p&gt;
&lt;p&gt;You could have now used this variable to change the properties of the original sprite that you just created. But if you passed a non-valid wrapper, like one containing the pointer of a recently deleted object, the application would just crash.&lt;/p&gt;
&lt;p&gt;This should not be the case anymore: every Lua function that operates on objects like sprites, textures, fonts, particle systems, and so on, will now check if the variable used is valid.
There is no wrapper object anymore, the light userdata contains the pointer to the native object and it is checked by accessing a hashmap where the key is the pointer itself and the value is its type.&lt;/p&gt;
&lt;p&gt;No more arrays to iterate, if the hashmap does not return a value or if the type is not the expected one, the operation is not carried out and nothing happens!&lt;/p&gt;
&lt;h2 class="relative group"&gt;Updates to the Android building process
&lt;div id="updates-to-the-android-building-process" class="anchor"&gt;&lt;/div&gt;
&lt;/h2&gt;
&lt;p&gt;It has been quite some time before I had a look at the Android Developers documentation, the Android Gradle plugin, and the rest of the SDK tools.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;build.gradle&lt;/code&gt; script has been rewritten to support the latest plugin version. While doing so, all source files have been moved inside an &lt;code&gt;app&lt;/code&gt; directory that now represents a separate building module.&lt;/p&gt;
&lt;p&gt;A minor Android change is the support of the &lt;code&gt;description&lt;/code&gt; attribute in the manifest.&lt;/p&gt;
&lt;h2 class="relative group"&gt;Minor changes
&lt;div id="minor-changes" class="anchor"&gt;&lt;/div&gt;
&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;The vector, quaternion, and matrix classes now initialize their values. A small change that will make happy some users without affecting performance.&lt;/li&gt;
&lt;li&gt;Thanks to the support for &lt;code&gt;glDebugMessageInsert()&lt;/code&gt;, the OpenGL debug context should produce some new debugging messages.&lt;/li&gt;
&lt;li&gt;Hashmaps and hashsets could not perform a rehash if they contained non-copyable objects.&lt;/li&gt;
&lt;li&gt;The window resizable option has been fixed when using the Qt5 backend.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;Font&lt;/code&gt; class can now use an external &lt;code&gt;Texture&lt;/code&gt; object, enabling texture sharing among multiple nodes and modifications while the texture is in use.&lt;/li&gt;
&lt;li&gt;The color classes use a group of variables instead of a &lt;code&gt;StaticArray&lt;/code&gt; for channels. It should help with understanding the color value in a debugger.&lt;/li&gt;
&lt;li&gt;Starting with macOS 10.12, &lt;code&gt;clock_gettime_nsec_np()&lt;/code&gt; is the preferred way to query for a timer and makes the old &lt;code&gt;mach_absolute_time()&lt;/code&gt; unsafe and deprecated.&lt;/li&gt;
&lt;li&gt;An audio player will now check if it&amp;rsquo;s possible to register itself in the audio device before setting its state to &lt;code&gt;PLAYING&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The Lua API can now manipulate vectors with four elements.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I hope you enjoyed this development update installment and some of the background work around custom shaders.&lt;/p&gt;</description></item><item><title>nCine Dev Update 18</title><link>https://encelo.github.io/2022-01-30-ncine-dev-update-18/</link><pubDate>Sun, 30 Jan 2022 00:00:00 +0000</pubDate><guid>https://encelo.github.io/2022-01-30-ncine-dev-update-18/</guid><description>&lt;p&gt;After a very long time without an update, here comes a new one. It is filled with all the work done in the last six months. &amp;#x1f4aa;&lt;/p&gt;
&lt;h3 class="relative group"&gt;Template project files alongside the engine
&lt;div id="template-project-files-alongside-the-engine" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;This was a very important change that I had in mind for quite a while.&lt;/p&gt;
&lt;p&gt;By moving the template CMake scripts inside the engine repository, I can now update them once and reap the benefits in all nCine projects.&lt;/p&gt;
&lt;p&gt;Often an engine update needs a change in the way projects are built, and now a single commit can logically capture that.&lt;/p&gt;
&lt;p&gt;As the template project demonstrates, you now only need a simple &lt;a href="https://github.com/nCine/ncTemplate/blob/master/CMakeLists.txt" target="_blank" rel="noreferrer"&gt;CMakeLists.txt&lt;/a&gt; file, and your project is good to go!&lt;/p&gt;
&lt;h3 class="relative group"&gt;Lua developer distribution
&lt;div id="lua-developer-distribution" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;I have added a new distribution option presets of the engine in the &lt;code&gt;nCine-artifacts&lt;/code&gt; repository.
It should make it easier to use nCine with Lua, in a fashion similar to LÖVE.&lt;/p&gt;
&lt;p&gt;This version only has one executable: &lt;code&gt;apptest_lua&lt;/code&gt;. You can pass it an argument on the command line for the script you want to load and, thanks to a &lt;code&gt;TextNode&lt;/code&gt;, it will show you on screen a message should any errors occur.&lt;/p&gt;
&lt;p&gt;Yes, this also means you can now pass command line arguments to any of your nCine applications. &amp;#x1f609;&lt;/p&gt;
&lt;h3 class="relative group"&gt;Raspberry Pi support
&lt;div id="raspberry-pi-support" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;I bought a nice Raspberry Pi 4 Model B with 8GB of RAM, an Argon ONE M.2 case, and a 240GB Kingston A400 SATA SSD.
The main objective was to have SpookyGhost running on it. &amp;#x1f47b;&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;&lt;img
class="my-0 rounded-md"
loading="lazy"
decoding="async"
fetchpriority="low"
alt="Raspberry Pi 4B"
src="/images/RaspberryPi4B.jpg"
&gt;&lt;figcaption&gt;Raspberry Pi 4B&lt;/figcaption&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;To achieve that I had to tinker a lot with the CMake scripts. Even if OpenGL ES was already supported to enable the Android port, I had to resolve additional issues that only came to the surface when having OpenGL ES on Linux.&lt;/p&gt;
&lt;p&gt;I had to add a CMake script to find and check the compilation results of the atomic library on 32bit systems on ARM (Raspberry OS is a 32bit distribution), add a new &lt;code&gt;WITH_OPENGLES&lt;/code&gt; preprocessor definition, and check the CMake version before using &lt;code&gt;file(ARCHIVE_EXTRACT)&lt;/code&gt; (Raspberry OS comes with an older CMake version).&lt;/p&gt;
&lt;p&gt;In the end, the result was rewarding and recognized by both &lt;a href="https://www.reddit.com/r/raspberry_pi/comments/qkhdus/spookyghost_my_opensource_procedural_animation/" target="_blank" rel="noreferrer"&gt;reddit&lt;/a&gt; and &lt;a href="https://www.tomshardware.com/uk/news/spookyghost-comes-to-raspberry-pi" target="_blank" rel="noreferrer"&gt;Tom&amp;rsquo;s Hardware&lt;/a&gt;. &amp;#x1f60a;&lt;/p&gt;
&lt;h2 class="relative group"&gt;Viewports and cameras
&lt;div id="viewports-and-cameras" class="anchor"&gt;&lt;/div&gt;
&lt;/h2&gt;
&lt;p&gt;The biggest achievement of this update is represented by the two months&amp;rsquo; work on viewports and cameras.&lt;/p&gt;
&lt;p&gt;It is now possible to have multiple viewports, each one with its size, its render queue, an optional offscreen render target, an optional camera, and a particular order in the rendering chain.&lt;/p&gt;
&lt;p&gt;The use cases are various. With the upcoming work on custom shaders you can use a viewport texture to perform full screen post-processing like blur (even with a separable filter, if you setup the viewports order chain accordingly), or you can have multiple viewports and cameras and implement a split screen!&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;&lt;img
class="my-0 rounded-md"
loading="lazy"
decoding="async"
fetchpriority="low"
alt="apptest_viewports"
src="/images/apptest_viewports.png"
&gt;&lt;figcaption&gt;apptest_viewports&lt;/figcaption&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;When you use a camera instead of a common camera node you can potentially improve performance as the matrix multiplications to transform nodes would be performed in a shader and not on the CPU. This can be seen in action in &lt;code&gt;apptest_camera&lt;/code&gt; by pressing &lt;code&gt;V&lt;/code&gt; to switch the new viewport method on and off.&lt;/p&gt;
&lt;p&gt;There is even a greater performance gain margin when you pause sprites animations by pressing &lt;code&gt;P&lt;/code&gt;. In this case, a new dirty flag system will completely skip node transformations, AABB calculations, culling rectangle intersections, and render command updates!&lt;/p&gt;
&lt;p&gt;The dirty flags are wrapped inside a new nCTL &lt;code&gt;BitSet&lt;/code&gt; container class similar to the standard &lt;code&gt;bitset&lt;/code&gt; one.&lt;/p&gt;
&lt;p&gt;The new camera system has been also ported to bigger projects like &lt;code&gt;ncTiledViewer&lt;/code&gt;, &lt;code&gt;ncJump&lt;/code&gt;, and JugiMap based ones.
The following tests have been performed on my Mi Notebook Pro (2017) by forcing the CPU speed to 800Mhz.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Project Name&lt;/th&gt;
&lt;th&gt;Frames (20s)&lt;/th&gt;
&lt;th&gt;FPS&lt;/th&gt;
&lt;th&gt;Frame Time&lt;/th&gt;
&lt;th&gt;Percentage&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ncJugiMapFrameWorkDemo (old)&lt;/td&gt;
&lt;td&gt;6387&lt;/td&gt;
&lt;td&gt;319.35&lt;/td&gt;
&lt;td&gt;3.131ms&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ncJugiMapFrameWorkDemo (new)&lt;/td&gt;
&lt;td&gt;8830&lt;/td&gt;
&lt;td&gt;441.5&lt;/td&gt;
&lt;td&gt;2.265ms&lt;/td&gt;
&lt;td&gt;72%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ncJugiMapParallaxScrollingDemo (old)&lt;/td&gt;
&lt;td&gt;7168&lt;/td&gt;
&lt;td&gt;358.4&lt;/td&gt;
&lt;td&gt;2.79ms&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ncJugiMapParallaxScrollingDemo (new)&lt;/td&gt;
&lt;td&gt;8682&lt;/td&gt;
&lt;td&gt;434.1&lt;/td&gt;
&lt;td&gt;2.30ms&lt;/td&gt;
&lt;td&gt;82.5%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ncJugiMapSpriteTimelineAnimation (old)&lt;/td&gt;
&lt;td&gt;3537&lt;/td&gt;
&lt;td&gt;176.85&lt;/td&gt;
&lt;td&gt;5.65ms&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ncJugiMapSpriteTimelineAnimation (new)&lt;/td&gt;
&lt;td&gt;5058&lt;/td&gt;
&lt;td&gt;252.9&lt;/td&gt;
&lt;td&gt;3.95m&lt;/td&gt;
&lt;td&gt;69.9%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ncJugiMapGuiDemo (old)&lt;/td&gt;
&lt;td&gt;7913&lt;/td&gt;
&lt;td&gt;395.65&lt;/td&gt;
&lt;td&gt;2.52ms&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ncJugiMapGuiDemo (new)&lt;/td&gt;
&lt;td&gt;10691&lt;/td&gt;
&lt;td&gt;534.55&lt;/td&gt;
&lt;td&gt;1.87ms&lt;/td&gt;
&lt;td&gt;74.0%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ncJump (old)&lt;/td&gt;
&lt;td&gt;4879&lt;/td&gt;
&lt;td&gt;243.95&lt;/td&gt;
&lt;td&gt;4.10ms&lt;/td&gt;
&lt;td&gt;100%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ncJump (new)&lt;/td&gt;
&lt;td&gt;6047&lt;/td&gt;
&lt;td&gt;302.35&lt;/td&gt;
&lt;td&gt;3.30ms&lt;/td&gt;
&lt;td&gt;80.7%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 class="relative group"&gt;Continuous integration fixes
&lt;div id="continuous-integration-fixes" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;For a long time, I have had a list of problems in &lt;a href="https://github.com/nCine/nCine/issues/11" target="_blank" rel="noreferrer"&gt;issue #11&lt;/a&gt; related to the GitHub Actions continuous integration.&lt;/p&gt;
&lt;h4 class="relative group"&gt;macOS libraries
&lt;div id="macos-libraries" class="anchor"&gt;&lt;/div&gt;
&lt;/h4&gt;
&lt;p&gt;On macOS, the Vorbis library could not dynamically link to the OGG one because, at compile-time, the system installed one was at a different path than the one on my machine and &lt;code&gt;install_name_tool&lt;/code&gt; could not perform the &lt;code&gt;@rpath&lt;/code&gt; substitution.
This was recently fixed in &lt;code&gt;nCine-libraries&lt;/code&gt; by modifying the Vorbis CMake building script to run &lt;code&gt;otool&lt;/code&gt; to discover the embedded OGG path:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;COMMAND sh -c &amp;quot;install_name_tool -change $(otool -L ${FRAMEWORK_DIR_VORBIS}/${TARGET_VORBIS} | grep ${DYLIBNAME_OGG} | cut -f2 | cut -d \&amp;quot; \&amp;quot; -f1) \&amp;quot;@rpath/${TARGET_OGG}.framework/${TARGET_OGG}\&amp;quot; ${FRAMEWORK_DIR_VORBIS}/${TARGET_VORBIS}&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Thanks to Fahien and Coda for testing out the new libraries on the latest version of macOS. &amp;#x1f64f;&lt;/p&gt;
&lt;h4 class="relative group"&gt;Clang strip on MinGW
&lt;div id="clang-strip-on-mingw" class="anchor"&gt;&lt;/div&gt;
&lt;/h4&gt;
&lt;p&gt;On MinGW with recent versions of Clang, the stripping of binaries fails with an obscure &lt;code&gt;unexpected associative section index&lt;/code&gt; message by &lt;code&gt;llvm-strip&lt;/code&gt;. I disabled it when running on this platform and opened an &lt;a href="https://github.com/llvm/llvm-project/issues/53433" target="_blank" rel="noreferrer"&gt;issue&lt;/a&gt; to report it.&lt;/p&gt;
&lt;h4 class="relative group"&gt;GCC optimization bug
&lt;div id="gcc-optimization-bug" class="anchor"&gt;&lt;/div&gt;
&lt;/h4&gt;
&lt;p&gt;With newer versions of GCC, both on Linux and MinGW, some of the tests in the &lt;code&gt;gtest_list_movable&lt;/code&gt; unit test fail in release mode. Upon further investigation, I discovered that turning down optimizations from O3 to O2 in &lt;code&gt;List.h&lt;/code&gt; with &lt;code&gt;#pragma GCC optimize (&amp;quot;O2&amp;quot;)&lt;/code&gt; fixed the issue.
This made me think it is a compiler optimization bug, you know, one of those that goes away when you put &lt;code&gt;printf()&lt;/code&gt; calls to understand what&amp;rsquo;s happening. &amp;#x1f605;&lt;/p&gt;
&lt;p&gt;It seems that in this particular test the &lt;code&gt;front()&lt;/code&gt; method of the &lt;code&gt;List&lt;/code&gt; class returns the wrong address the first time it is called, and the correct one after that. I was going to &amp;ldquo;fix&amp;rdquo; the issue by reordering the instructions in my test but then I discovered that the workaround worked on newer versions of GCC but made the error appear on older ones. &amp;#x1f629;&lt;/p&gt;
&lt;p&gt;While I was creating an isolated repro I also discovered that &lt;code&gt;-O3 -funsafe-math-optimizations&lt;/code&gt; was working and that not linking the GoogleTest libraries and putting the test code in a &lt;code&gt;main()&lt;/code&gt; function was also working. Changing the version of GoogleTest from the latest &lt;code&gt;v1.11.0&lt;/code&gt; to &lt;code&gt;v1.10.0&lt;/code&gt; or to the &lt;code&gt;main&lt;/code&gt; branch didn&amp;rsquo;t affect the result. &amp;#x1f61e;&lt;/p&gt;
&lt;p&gt;In the end, I decided to disable the &lt;code&gt;gtest_list_movable&lt;/code&gt; unit test altogether when compiling in release with GCC. Better safe than sorry!&lt;/p&gt;
&lt;h3 class="relative group"&gt;Minor changes
&lt;div id="minor-changes" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;The user has now access to the &lt;code&gt;ITextureSaver&lt;/code&gt; interface to save textures in PNG or WebP formats.&lt;/li&gt;
&lt;li&gt;SDL 2.0.16 added a method to flash the window to request the user&amp;rsquo;s attention. As this functionality was already present in the other two desktop backends, it was possible to add a generic method to the API for this to work regardless of the preferred backend used.&lt;/li&gt;
&lt;li&gt;The user can now use the &lt;code&gt;NCINE_WITH_SCRIPTING_API&lt;/code&gt; CMake variable to enable or disable the Lua API, even when Lua utilities functions and the state manager are available.&lt;/li&gt;
&lt;li&gt;The standard vector and list classes are known to have undefined behavior when trying to retrieve a front or back element from an empty container. But I decided to change the nCTL versions to fatal assert in this case. It has already proven useful to fix an issue in a unit test.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I hope you enjoyed the return of development updates with this packed installment. &amp;#x1f609;&lt;/p&gt;</description></item><item><title>nCine Dev Update 17</title><link>https://encelo.github.io/2021-06-28-ncine-dev-update-17/</link><pubDate>Mon, 28 Jun 2021 00:00:00 +0000</pubDate><guid>https://encelo.github.io/2021-06-28-ncine-dev-update-17/</guid><description>&lt;p&gt;Quite some time has passed since the previous development update but I&amp;rsquo;m here again to talk about the latest nCine progress.
By the way, in case you missed the latest &lt;a href="/2021-06-21-ten-years-ncine/" &gt;article&lt;/a&gt;, the project has recently reached its tenth anniversary. &amp;#x1f609;&lt;/p&gt;
&lt;h3 class="relative group"&gt;New nCine projects
&lt;div id="new-ncine-projects" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;Many of the following fixes and features have been driven by the work on some new nCine projects that have seen the light of day during those months:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/nCine/ncTiledViewer" target="_blank" rel="noreferrer"&gt;ncTiledViewer&lt;/a&gt; is a loader and viewer of &lt;a href="https://www.mapeditor.org/" target="_blank" rel="noreferrer"&gt;Tiled&lt;/a&gt; maps that you can easily integrate into your game.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Fahien/ncJump" target="_blank" rel="noreferrer"&gt;ncJump&lt;/a&gt; is a platform game from Fahien. He has integrated Box2D physics to create one of the most complex nCine projects to date! &amp;#x1f64f;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/SpookyGhost2D/SpookyGhost" target="_blank" rel="noreferrer"&gt;SpookyGhost&lt;/a&gt;, my procedural sprite animation tool, has recently become free and open source. I took advantage of this opportunity to add many new features.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 class="relative group"&gt;Default constructors for nodes
&lt;div id="default-constructors-for-nodes" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;As I was anticipating in the last update, I pushed the resource system even further by having nodes that can initially be constructed without any resource assigned to them.&lt;/p&gt;
&lt;p&gt;Think for example of a bunch of sprites in an array that are constructed via their default constructor. They will have no texture or parent but they can still have a color.
Solid color sprites will use a specific shader that doesn&amp;rsquo;t sample any textures at all. They will be batched together and render faster than regular textured sprites.&lt;/p&gt;
&lt;p&gt;But it does not end here: animated and mesh sprites can both be default constructed and not be associated with a texture. Text nodes can be default constructed too and particle systems can have texture-less particles.&lt;/p&gt;
&lt;p&gt;This functionality has been extended to audio buffer players as well, they can be default constructed as well.&lt;/p&gt;
&lt;p&gt;You can of course assign a resource to a default constructed node at a later time.&lt;/p&gt;
&lt;h3 class="relative group"&gt;Colored console messages
&lt;div id="colored-console-messages" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;This feature was requested by Fahien while he was working on ncJump and I couldn&amp;rsquo;t help but satisfy the user working on the most ambitious project. &amp;#x1f609;&lt;/p&gt;
&lt;p&gt;So not only I added this feature but I fixed the lack of a console window on Windows, an issue plaguing this platform since I changed the default subsystem.&lt;/p&gt;
&lt;p&gt;Now we can have the windows subsystem and the &lt;code&gt;WinMain&lt;/code&gt; function, and open a console if the user wants it or if we are running a debug build.
This is possible by using the &lt;a href="https://docs.microsoft.com/en-us/windows/console/attachconsole" target="_blank" rel="noreferrer"&gt;AttachConsole&lt;/a&gt; API function.&lt;/p&gt;
&lt;p&gt;As a plus, I have ported from &lt;code&gt;ncline&lt;/code&gt; the code to set the &lt;code&gt;ENABLE_VIRTUAL_TERMINAL_PROCESSING&lt;/code&gt; and have color output in the Windows command prompt. &amp;#x1f4aa;&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;&lt;img
class="my-0 rounded-md"
loading="lazy"
decoding="async"
fetchpriority="low"
alt="Colored Console Messages"
src="/images/colored_console_messages.png"
&gt;&lt;figcaption&gt;Colored Console Messages&lt;/figcaption&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 class="relative group"&gt;Fault-tolerant Lua scripts execution
&lt;div id="fault-tolerant-lua-scripts-execution" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;The most important new feature of this update is probably the new way of running Lua scripts. In the past, an error in a Lua script caused an assertion that killed the application.
This is, of course, not acceptable. Users should be able to safely play with scripts, either in games or tools. For example, writing scripted animations in &lt;code&gt;SpookyGhost&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The main change was to replace each &lt;code&gt;lua_call()&lt;/code&gt; with &lt;code&gt;lua_pcall()&lt;/code&gt;. The latter works in protected mode and returns an error in case the script is malfunctioning.&lt;/p&gt;
&lt;p&gt;The error message is optionally returned to the application with additional information extracted from a &lt;code&gt;lua_Debug&lt;/code&gt; structure. The user can use it to show where the error is in the script source.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;&lt;img
class="my-0 rounded-md"
loading="lazy"
decoding="async"
fetchpriority="low"
alt="SpookyGhost Script Error"
src="/images/SpookyGhost_script_error.png"
&gt;&lt;figcaption&gt;SpookyGhost Script Error&lt;/figcaption&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;I have also removed all occurrences of &lt;code&gt;luaL_argerror()&lt;/code&gt; and replaced them with simple warning messages to avoid any exit caused by this kind of error.&lt;/p&gt;
&lt;p&gt;To give the user even more flexibility I made it possible to load a script without running it immediately: loading a Lua &amp;ldquo;chunk&amp;rdquo; and calling it are now two separate actions.&lt;/p&gt;
&lt;p&gt;As a bonus, I also fixed the parameter order of all calls to &lt;code&gt;lua_createtable()&lt;/code&gt;. &amp;#x1f605;&lt;/p&gt;
&lt;h3 class="relative group"&gt;New application and input events
&lt;div id="new-application-and-input-events" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;h4 class="relative group"&gt;Text input event
&lt;div id="text-input-event" class="anchor"&gt;&lt;/div&gt;
&lt;/h4&gt;
&lt;p&gt;Reacting only to keypresses is not enough in the world of Unicode and UTF8, as text characters are not the same as pressed keys.
For this reason, I have exposed to the user a new input event that should make it easier to process text: &lt;code&gt;onTextInput()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;SDL2 already came with an &lt;code&gt;SDL_TEXTINPUT&lt;/code&gt; event and GLFW with a &lt;code&gt;CharCallback&lt;/code&gt;. They work slightly differently but wrapping them was easy enough.&lt;/p&gt;
&lt;p&gt;For Qt5 I check the length of the string returned by &lt;code&gt;QKeyEvent::text()&lt;/code&gt; and generate the event if it&amp;rsquo;s longer than zero. It means that this particular key event is the last in a Unicode codepoint sequence.&lt;/p&gt;
&lt;p&gt;The Android implementation is probably the weaker one as &lt;code&gt;KeyEvent::getUnicodeChar()&lt;/code&gt; does not seem to support codepoint sequences of more than one key.&lt;/p&gt;
&lt;h4 class="relative group"&gt;Quit request
&lt;div id="quit-request" class="anchor"&gt;&lt;/div&gt;
&lt;/h4&gt;
&lt;p&gt;I have added an &lt;code&gt;onQuitRequest()&lt;/code&gt; input event that makes it possible to override a quit request coming from the system.&lt;/p&gt;
&lt;p&gt;Think about pressing the window close button or using the contextual menu from the taskbar to perform the same action.
In those cases, the new event gets called and the user has a chance to save the game or to display a dialog window. The latter is what happens with SpookyGhost now.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;&lt;img
class="my-0 rounded-md"
loading="lazy"
decoding="async"
fetchpriority="low"
alt="SpookyGhost Quit Confirmation"
src="/images/SpookyGhost_quit_confirmation.png"
&gt;&lt;figcaption&gt;SpookyGhost Quit Confirmation&lt;/figcaption&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 class="relative group"&gt;Post update callback
&lt;div id="post-update-callback" class="anchor"&gt;&lt;/div&gt;
&lt;/h4&gt;
&lt;p&gt;The new &lt;code&gt;onPostUpdate()&lt;/code&gt; application event solves an old problem: being able to know the absolute transformation of a node before it gets rendered.&lt;/p&gt;
&lt;p&gt;It gives a new meaning to query methods like &lt;code&gt;SceneNode::absPosition()&lt;/code&gt; as it can now be called after all nodes have been transformed so you don&amp;rsquo;t get stale data from the last frame.
I have also added a &lt;code&gt;SceneNode::setWorldMatrix()&lt;/code&gt; method to further modify the transformation before rendering.&lt;/p&gt;
&lt;p&gt;This change, together with a fix that caused an additional frame delay when rendering ImGui or Nuklear in presence of a scenegraph, made the overlay in &lt;code&gt;ncTiledViewer&lt;/code&gt; perfectly synchronized with the movements of the underlying map.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;&lt;img
class="my-0 rounded-md"
loading="lazy"
decoding="async"
fetchpriority="low"
alt="ncTiledViewer Imgui Overlay"
src="/images/ncTiledViewer_ImGui_overlay.png"
&gt;&lt;figcaption&gt;ncTiledViewer Imgui Overlay&lt;/figcaption&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 class="relative group"&gt;Automatic capacity extension for strings
&lt;div id="automatic-capacity-extension-for-strings" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;String capacity is another quite annoying issue that I have been willing to address for a long time.
In the early days of nCine, I opted for fixed strings, you decided the capacity at construction time and the object allocated a buffer only once.&lt;/p&gt;
&lt;p&gt;Great for performance, right? Possibly, but also a great source for bugs all around the place, with truncations happening when you least expect them.&lt;/p&gt;
&lt;p&gt;I decided to change the default behavior: now strings reallocate if they need more space. You can keep appending text and the string object will make sure a truncation never happens. Of course, the user can still choose the old fixed behavior if needed.&lt;/p&gt;
&lt;p&gt;This feature will make the &lt;em&gt;Savefile Size&lt;/em&gt; configuration entry in SpookyGhost obsolete. The program will not need to preallocate a big enough string to contain the project file.&lt;/p&gt;
&lt;h3 class="relative group"&gt;Fahien&amp;rsquo;s suggestions and feedback
&lt;div id="fahiens-suggestions-and-feedback" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;I fixed an issue he discovered while working on &lt;code&gt;ncJump&lt;/code&gt; in the behavior of &lt;code&gt;Array::setSize()&lt;/code&gt;. Now it creates objects when extending the size, same as &lt;code&gt;StaticArray&lt;/code&gt; and as you would expect. &amp;#x1f926;&amp;zwj;&amp;#x2642;&amp;#xfe0f;&lt;/li&gt;
&lt;li&gt;He requested the ability to move-construct and move-assign scenegraph nodes and to add a &lt;code&gt;clone()&lt;/code&gt; method. This change will hopefully make both Rust and modern C++ users more comfortable. &amp;#x1f3e0;&lt;/li&gt;
&lt;li&gt;To make it easier for &lt;code&gt;ncJump&lt;/code&gt; to use the &lt;code&gt;docking&lt;/code&gt; ImGui branch I exported the version tag of integrated software as CMake variables. It is now also possible to automatically download and extract a GitHub release instead of using a repository.&lt;/li&gt;
&lt;li&gt;Thanks to a smart suggestion I created a specialized version of the hash functions for strings that simplified a lot the declaration of hashmaps with a string key. They now work as expected with both string objects and array of characters.&lt;/li&gt;
&lt;li&gt;Additional feedback led to a new method to delete all children of a node and to a method to retrieve the current rectangle animation of an animated sprite.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 class="relative group"&gt;Minor changes
&lt;div id="minor-changes" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;I have added the option to mark a color as the chroma key when loading an RGB texture. I can now load some Tiled example tilesets that use magenta for transparency.&lt;/li&gt;
&lt;li&gt;Another functionality dictated by Tiled capabilities is the custom frame durations for rectangle animations: every animation frame can have a unique duration different from others.&lt;/li&gt;
&lt;li&gt;With the support of ImGui v1.80 comes the new &lt;a href="https://github.com/ocornut/imgui/issues/3740" target="_blank" rel="noreferrer"&gt;Tables API&lt;/a&gt; which far surpasses the old Columns API.&lt;/li&gt;
&lt;li&gt;No more documentation is distributed with the nCine. It is now pushed online at every commit by GitHub Actions.&lt;/li&gt;
&lt;li&gt;For SpookyGhost, I needed separate blending functions for RGB and alpha channels so I now store additional states in the nCine wrapper class for OpenGL blending.&lt;/li&gt;
&lt;li&gt;I exposed yet another window property from the three desktop backends: its position. The user can now move the window around with code or query its current position.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I hope the article was worth your wait and you enjoyed the new features. &amp;#x1f609;&lt;/p&gt;</description></item><item><title>Ten years of nCine</title><link>https://encelo.github.io/2021-06-21-ten-years-of-ncine/</link><pubDate>Mon, 21 Jun 2021 00:00:00 +0000</pubDate><guid>https://encelo.github.io/2021-06-21-ten-years-of-ncine/</guid><description>&lt;p&gt;A bit more than ten years have passed since that &lt;a href="https://github.com/nCine/nCine/commit/6bf318de68ed5c453eaacd867c8e83c853f64edc" target="_blank" rel="noreferrer"&gt;first commit&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The presence of a &lt;code&gt;.hgignore&lt;/code&gt; file reveals that I was using Mercurial at the time, an easier transition to DCVS for someone like me used to Subversion.&lt;/p&gt;
&lt;p&gt;Some things were already there and stayed the same until now: like the Doxygen comments or the CMake building process (my first CMake real project after years of SCons).
Many other things have changed instead, as can be expected from a project with such a long life span.&lt;/p&gt;
&lt;p&gt;But what made me persevere for a decade on the same project?&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;&lt;img
class="my-0 rounded-md"
loading="lazy"
decoding="async"
fetchpriority="low"
alt="nCine Logotype"
src="/images/nCine_logotype.png"
&gt;&lt;figcaption&gt;nCine logotype&lt;/figcaption&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 class="relative group"&gt;The early years
&lt;div id="the-early-years" class="anchor"&gt;&lt;/div&gt;
&lt;/h2&gt;
&lt;p&gt;I have been publishing open-source software for more than 20 years. &amp;#x1f4be;&lt;/p&gt;
&lt;p&gt;The very first was &lt;a href="http://aminet.net/package/dev/src/MiniStat" target="_blank" rel="noreferrer"&gt;MiniStat&lt;/a&gt;, published on Aminet in 2000, a very simple program to perform some statistical calculations that I wrote to practice what I learnt after reading my first C course.
At that point, I had been an Amiga user for ten years and even if I was extremely fascinated by games and the demo scene, I was also quite attracted to system programming.&lt;/p&gt;
&lt;p&gt;So when I became a Linux user shortly after, I oddly decided to work on a CGI script. I had recently finished reading the &lt;a href="https://gapil.gnulinux.it/" target="_blank" rel="noreferrer"&gt;GaPiL&lt;/a&gt; book about Linux programming and those were the days of slow always-on ADSL connections and home servers.
That&amp;rsquo;s how &lt;a href="http://sonda.sourceforge.net/" target="_blank" rel="noreferrer"&gt;Sonda&lt;/a&gt; (2002-2003) was born, a CGI script for user polls written in C and hosted on a friend&amp;rsquo;s home server. &amp;#x1f604; The project was my first contact with SourceForge, with licenses, with CVS, and later with SVN.&lt;/p&gt;
&lt;p&gt;Then Doom 3 came out and my interest switched back to games and shiny graphics. First I joined my good friend Vivaladav to work on &lt;a href="https://sourceforge.net/projects/mars/" target="_blank" rel="noreferrer"&gt;Mars, Land of No Mercy&lt;/a&gt;, a turn-based strategy game with isometric graphics, and then I started experimenting with &lt;a href="https://www.autistici.org/encelo/prog_gldemos.php" target="_blank" rel="noreferrer"&gt;OpenGL demos&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;With &lt;a href="http://globs.sourceforge.net/" target="_blank" rel="noreferrer"&gt;GL O.B.S.&lt;/a&gt; (2006-2007), my next big project, the focus stayed more or less the same: graphics, performance, and tools.
Globs was a benchmarking solution based on a PyGTK interface, a PHP script to gather user results, and my OpenGL demos as individual benchmarks.&lt;/p&gt;
&lt;p&gt;The tool was even used briefly by &lt;a href="https://www.phoronix.com/scan.php?page=article&amp;amp;item=599&amp;amp;num=7" target="_blank" rel="noreferrer"&gt;Phoronix&lt;/a&gt; long before OpenBenchmarking.org was a reality. &amp;#x1f631;&lt;/p&gt;
&lt;p&gt;Later I had some time to deviate a bit from graphics with &lt;a href="https://github.com/encelo/pacstats" target="_blank" rel="noreferrer"&gt;PacStats&lt;/a&gt; (2007-2010): a little PyGTK project that extracted information from the Arch Linux pacman log file, stored it in an SQLite database, and visualized it with Matplotlib.&lt;/p&gt;
&lt;p&gt;Finally, in July 2009, I graduated with a thesis on computer graphics and I was ready to join the game industry! &amp;#x1f4aa;&lt;/p&gt;
&lt;h2 class="relative group"&gt;The beginning
&lt;div id="the-beginning" class="anchor"&gt;&lt;/div&gt;
&lt;/h2&gt;
&lt;p&gt;I looked for a job for a year, doing phone and on-site interviews in the UK with no luck. I remember I was considering going indie with a twin-stick shooter I was programming with XNA. &amp;#x1f606;&lt;/p&gt;
&lt;p&gt;But then the opportunity presented itself and I started working in a small indie studio in Italy.&lt;/p&gt;
&lt;p&gt;While I was daydreaming about AAA games, rendering, and engine programming on next-gen consoles, the reality was a lot different: I was stuck with GUI programming with no escape routes.&lt;/p&gt;
&lt;p&gt;But that is the precise moment the nCine was born: at work, I was coding boring user interfaces but at home, I could be whoever I want, and I wanted to be an engine and graphics programmer! &amp;#x2699;&amp;#xfe0f;&lt;/p&gt;
&lt;p&gt;My first two working machines were two very small and slow netbooks: a &lt;a href="http://encelo.netsons.org/old/my-computers/atom-2/" target="_blank" rel="noreferrer"&gt;Medion E1222&lt;/a&gt; first and a &lt;a href="http://encelo.netsons.org/old/my-computers/gluon/" target="_blank" rel="noreferrer"&gt;Lenovo IdeaPad S205&lt;/a&gt; a bit later. &amp;#x1f62b;
But they had all I needed: Arch Linux, Qt Creator, GCC, CMake, and Doxygen. Those were my main tools then as they are today.&lt;/p&gt;
&lt;p&gt;When I planned my work I deliberately decided to start with simple rendering: just 2D sprites and no shaders. My main focus at the time was the engine architecture, the data structures, the sound system, the abstractions&amp;hellip; all things that I neglected before.&lt;/p&gt;
&lt;p&gt;Of course, I carried my obsession with &amp;ldquo;perfect&amp;rdquo; commits to this new project. My policy dictates that I rebase a commit if I later discover an issue as if every commit was a micro release itself.
This constant rebasing is the reason why there are very few commits and also makes a collaborator&amp;rsquo;s life harder. Fortunately, I have none. &amp;#x1f605;&lt;/p&gt;
&lt;h2 class="relative group"&gt;The consolidation
&lt;div id="the-consolidation" class="anchor"&gt;&lt;/div&gt;
&lt;/h2&gt;
&lt;p&gt;Over the years I changed jobs several times to come closer to low-level graphics and engine programming. This didn&amp;rsquo;t stop me from working on the project.&lt;/p&gt;
&lt;p&gt;My two jobs in the UK were centered around mobile and Android development, and Android became an important platform for me.&lt;/p&gt;
&lt;p&gt;At the time it represented the closest thing to a console accessible by indie developers: those were the days of OUYA, GameStick, Gamepop, MOJO, and Android TV set-top boxes like the Shield or the Fire TV.&lt;/p&gt;
&lt;p&gt;When I began working on the nCine I knew I wanted to release it as open-source one day, but I believed it was too rough and I always delayed the date.&lt;/p&gt;
&lt;p&gt;Both my employers in the UK allowed me to release the source code whenever I wanted so I thought it was not going to be a problem with my new company in Sweden. Unfortunately, I quickly learnt that their legal team was very strict on this point: I could never release the source while working with them.&lt;/p&gt;
&lt;p&gt;So I continued to work behind closed doors but I started to open up a bit to the world. I wrote development updates on this blog, distributed binaries to some friend developers, and opened a &lt;a href="https://discord.gg/495ab6Y" target="_blank" rel="noreferrer"&gt;Discord&lt;/a&gt; server.&lt;/p&gt;
&lt;p&gt;Around this time I began exploring the idea of creating a real game with the nCine. I put together a &lt;a href="https://docs.google.com/spreadsheets/d/1z1RV6w4HjPODFcao34h72Vw-9KxtQ9rv0ZM7folTUMU/edit" target="_blank" rel="noreferrer"&gt;list&lt;/a&gt; of 2D games made with custom engines by small indie teams to motivate me and keep the dream alive. &amp;#x1f604;&lt;/p&gt;
&lt;h2 class="relative group"&gt;The present day
&lt;div id="the-present-day" class="anchor"&gt;&lt;/div&gt;
&lt;/h2&gt;
&lt;p&gt;Being unable to make my work public was one of the reasons I quit my job. I moved thousands of kilometers to the south, in Spain, and for two years straight I worked day and night on my projects alone.&lt;/p&gt;
&lt;p&gt;I started by working on a new game: a turn-based isometric prototype code-named &lt;code&gt;ncIsometric&lt;/code&gt; but after just a couple of months I got stuck with the utility AI code and put it on hold. &amp;#x1f605;&lt;/p&gt;
&lt;p&gt;The second, more important effort, was keeping the promise with myself and release the engine as free and open-source software. The 30th of May 2019 was the big day: the nCine source code was released on GitHub with an MIT license! :godmode:&lt;/p&gt;
&lt;p&gt;It was covered by both &lt;a href="https://www.phoronix.com/scan.php?page=news_item&amp;amp;px=nCine-Game-Engine" target="_blank" rel="noreferrer"&gt;Phoronix&lt;/a&gt; and &lt;a href="https://gamefromscratch.com/ncine-2d-open-source-game-engine/" target="_blank" rel="noreferrer"&gt;Game From Scratch&lt;/a&gt;, and this initial press coverage that quickly raised the number of GitHub &lt;a href="https://github.com/nCine/nCine/stargazers" target="_blank" rel="noreferrer"&gt;stargazers&lt;/a&gt;.
The stars showed the appreciation of the people and made me happy, but what I needed was battle-testing it on the ground: could others use it to create beautiful games?&lt;/p&gt;
&lt;p&gt;This was a very important point because before even feeling the excitement of creating a game, I was longing for the excitement of creating a tool that someone else could use to create one.
I believe this weird indirect feeling defined me as an engine, graphics, and tools programmer: the satisfaction of making people happy and grateful for my support and efforts.&lt;/p&gt;
&lt;p&gt;At this point, I created a template project to make it easier for potential users to develop games and a command-line tool to automate the download and compilation process.&lt;/p&gt;
&lt;p&gt;Then the unexpected happened: Jugilus asked for help to support the nCine in &lt;a href="http://jugimap.com/" target="_blank" rel="noreferrer"&gt;JugiMap&lt;/a&gt;, his 2D level and map editor. The tool lets you create levels, animations, collisions, and some logic, then a runtime library loads the data and uses the underlying engine to render everything.&lt;/p&gt;
&lt;p&gt;JugiMap&amp;rsquo;s runtime was a great way to stress-test the nCine and reveal a lot of bugs and limitations that, once addressed, made it a lot more capable and solid! &amp;#x1f4aa;&lt;/p&gt;
&lt;p&gt;Then it came the time for me to create my tool: &lt;a href="https://encelo.itch.io/spookyghost" target="_blank" rel="noreferrer"&gt;SpookyGhost&lt;/a&gt;. By now you already know the joy I feel when a content creator uses one of my tools and I have wished to create one for pixel artists for a long time.
Both because I love pixel art and because I always wanted to create a game with this style.&lt;/p&gt;
&lt;p&gt;I planned to sell it on Itch.io, get rich and start an indie company about game technology, tools, and game development. Naturally, nothing of that ever happened, I sold a handful of copies and I had to return being an employee in the game industry. &amp;#x1f605;&lt;/p&gt;
&lt;p&gt;While a traditional job means less spare time, it also means more economic resources. Those resources might be invested in the project one day: they could translate into a marketing campaign or in a game jam with prizes. &amp;#x1f3c6;&lt;/p&gt;
&lt;p&gt;Speaking of economic resources, at the beginning of this year I was awarded the &lt;a href="https://icculus.org/microgrant/2020/" target="_blank" rel="noreferrer"&gt;Icculus MicroGrant 2020&lt;/a&gt;, a small help but a great recognition for my ten years of hard work.&lt;/p&gt;
&lt;p&gt;Besides, now that I don&amp;rsquo;t need the money so bad I might as well make SpookyGhost an &lt;a href="https://encelo.itch.io/spookyghost/devlog/256745/spookyghost-is-now-free-and-open-source" target="_blank" rel="noreferrer"&gt;open-source&lt;/a&gt; software and hope more artists will notice it. &amp;#x1f914;
And that&amp;rsquo;s precisely what I did: I recently released it on GitHub under an MIT license hoping more users will try it.&lt;/p&gt;
&lt;h2 class="relative group"&gt;The future
&lt;div id="the-future" class="anchor"&gt;&lt;/div&gt;
&lt;/h2&gt;
&lt;p&gt;After all those words, where are we today and where are we going tomorrow?&lt;/p&gt;
&lt;p&gt;Ten years have passed but the user base is still very sparse and small. The majority of people I come in contact with will just test it a bit and disappear in the end.
I can&amp;rsquo;t blame them, the project is not well documented or marketed and there are no real games made with it, so why should they risk and be the first?&lt;/p&gt;
&lt;p&gt;For this reason, I often think I should maybe be the one creating something: a simple but polished game that shows the capabilities and the potential. It would also satisfy my ancestral dream of creating a game completely from scratch.
The main issue is that every time I start with a game project I end up adding a missing feature or fixing a bug in the engine. &amp;#x1f604;&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s our curse, engine programmers, always thinking at the foundations and never at the finished product.&lt;/p&gt;
&lt;p&gt;A question I get sometimes is how I managed to be focused all this time on the same project. Well, the nCine initiative is a collection of different projects, I never get bored and I can always start a new one.&lt;/p&gt;
&lt;p&gt;What keeps me going is the dream of seeing a successful indie game made with my technology. I work on my project a bit every day, with both small actionable tasks and a long-term roadmap, treating it as a real product but never missing out on the opportunity to have some fun. &amp;#x1f609;&lt;/p&gt;
&lt;p&gt;I hope you enjoyed the journey so far and I hope to have the strength to keep going for at least another ten years. &amp;#x1f4aa;&lt;/p&gt;</description></item><item><title>nCine Dev Update 16</title><link>https://encelo.github.io/2020-11-28-ncine-dev-update-16/</link><pubDate>Sat, 28 Nov 2020 00:00:00 +0000</pubDate><guid>https://encelo.github.io/2020-11-28-ncine-dev-update-16/</guid><description>&lt;p&gt;If you follow the project on &lt;a href="https://github.com/nCine" target="_blank" rel="noreferrer"&gt;GitHub&lt;/a&gt; you might have noticed a big development slowdown during the summer. I blame it on a combination of excessive heat and fatigue that led to a general lack of motivation and perseverance. &amp;#x2600;&amp;#xfe0f;&lt;/p&gt;
&lt;p&gt;Fortunately, this does not mean that development didn&amp;rsquo;t resume at its normal pace or that there are no things to talk about in this article. &amp;#x1f609;&lt;/p&gt;
&lt;h3 class="relative group"&gt;Loading resources from memory
&lt;div id="loading-resources-from-memory" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;Let&amp;rsquo;s start with a feature that was requested by a user on &lt;a href="https://discord.gg/495ab6Y" target="_blank" rel="noreferrer"&gt;Discord&lt;/a&gt;: the ability to load a resource from a memory buffer.&lt;/p&gt;
&lt;p&gt;To minimize changes and reuse interfaces I just implemented a &lt;code&gt;MemoryFile&lt;/code&gt; class. You can open, close, seek, read and write to it just as if it were a normal file on disk.&lt;/p&gt;
&lt;p&gt;This way a texture, a font, an audio buffer&amp;hellip; everything can be loaded from memory, giving much more flexibility to the user.&lt;/p&gt;
&lt;h3 class="relative group"&gt;Fault-tolerant loading and reloading of resources
&lt;div id="fault-tolerant-loading-and-reloading-of-resources" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;But loading from memory was not enough, I had more ideas to enhance the resource loading API and grant even more flexibility.
One of the main limitations of the system was apparent if the user tried to load a non-existent file: the whole application would just suddenly exit.&lt;/p&gt;
&lt;p&gt;This behavior might be acceptable if you are debugging a game but it is incompatible with any kind of tool workflow, where the user might erroneously try to load a sound file in place of a texture.&lt;/p&gt;
&lt;p&gt;The first idea I had to solve this was to provide additional classes that could attempt to load a specific data type and, if successful, be consumed by the corresponding resource.&lt;/p&gt;
&lt;p&gt;Of course, those new classes alone would have not been enough, I also needed to remove all &lt;code&gt;exit()&lt;/code&gt; calls from loader classes that take care of specific file format, like Png or Ogg Vorbis.&lt;/p&gt;
&lt;p&gt;The new system was working as expected: should the user construct a &lt;code&gt;Texture&lt;/code&gt; from a non-existent file the engine would straight out exit just as before. But now it was also possible to explicitly check if the loading process was successful by using one of the new data classes and then pass it to the resource object. In this example, the user would use a &lt;code&gt;TextureData&lt;/code&gt; instance to load an image from the disk, check if the loading was successful, and then pass it to the &lt;code&gt;Texture&lt;/code&gt; constructor.&lt;/p&gt;
&lt;p&gt;While this solution was working sufficiently well I didn&amp;rsquo;t like the fact that the user had to rely on those new classes to perform a safe loading and I scraped them altogether.&lt;/p&gt;
&lt;p&gt;I took one step forward and made all resources class completely dynamic when it comes to loading data. Take again the &lt;code&gt;Texture&lt;/code&gt; class, for example, you can now create an object with a default constructor, which might come useful when you need a pool of textures.
But what is even more interesting is that you can load data in a texture (or an audio buffer, or a font) multiple times. You can assign an empty texture to a sprite and it will initially render nothing. Later on, you can load an image and have it applied to your sprite, and even load a second one later on.&lt;/p&gt;
&lt;p&gt;Anytime there is a loading error nothing will happen to the application nor to the texture object: it will retain whatever data had before the last attempt. I was inspired by the SFML &lt;a href="https://www.sfml-dev.org/documentation/2.5.1/classsf_1_1Texture.php" target="_blank" rel="noreferrer"&gt;Texture&lt;/a&gt; class, with its default constructor and multiple load methods. In the future, I might even push further and have a &lt;a href="https://www.sfml-dev.org/documentation/2.5.1/classsf_1_1Sprite.php" target="_blank" rel="noreferrer"&gt;Sprite&lt;/a&gt; that can be created with a default constructor and have a texture assigned at a later time.&lt;/p&gt;
&lt;p&gt;I have also added a brand new feature: it is now possible to load uncompressed texels in a texture and PCM samples in an audio buffer, allowing for CPU generated procedural data. &amp;#x1f389;&lt;/p&gt;
&lt;h3 class="relative group"&gt;GitHub Actions
&lt;div id="github-actions" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;As I have written in a &lt;a href="https://ncine.github.io/2020-11-04-github-actions/" target="_blank" rel="noreferrer"&gt;news&lt;/a&gt; on the nCine site I now use GitHub Actions instead of Azure Pipelines for &lt;em&gt;continuous integration&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;I have been following the development of &lt;a href="https://github.com/features/actions" target="_blank" rel="noreferrer"&gt;Actions&lt;/a&gt; for some time and I think it has now all the features I needed so I spent some days converting the building scripts and testing the new &lt;em&gt;workflows&lt;/em&gt;, as they are now called.&lt;/p&gt;
&lt;p&gt;The nice thing is the integration: the code and the C.I. are very close together, on the same page. Besides, the configuration is easier as I don&amp;rsquo;t have to visit a different site for setting the C.I. up.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;&lt;img
class="my-0 rounded-md"
loading="lazy"
decoding="async"
fetchpriority="low"
alt="nCine page for GitHub Actions"
src="/images/GitHub_Actions.png"
&gt;&lt;figcaption&gt;nCine page for GitHub Actions&lt;/figcaption&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;As I was taking advantage of an advanced feature of GitHub I thought I could honor an old note I wrote a long time ago: make use of its &lt;a href="https://github.com/features/project-management/" target="_blank" rel="noreferrer"&gt;project management&lt;/a&gt; features by bringing my Trello tickets to Issues and having a roadmap with Projects.&lt;/p&gt;
&lt;p&gt;There are no milestones or project boards yet but I started tracking some tasks with &lt;a href="https://github.com/nCine/nCine/issues" target="_blank" rel="noreferrer"&gt;issues&lt;/a&gt;. &amp;#x1f5d2;&amp;#xfe0f;&lt;/p&gt;
&lt;p&gt;I have also decided to simplify the development process by getting rid of the &lt;code&gt;develop&lt;/code&gt; branch and just rely on feature branches, like in the &lt;a href="https://guides.github.com/introduction/flow/" target="_blank" rel="noreferrer"&gt;GitHub flow&lt;/a&gt; model.&lt;/p&gt;
&lt;h3 class="relative group"&gt;Decoding UTF-8 strings
&lt;div id="decoding-utf-8-strings" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;While &lt;a href="https://github.com/Jugilus" target="_blank" rel="noreferrer"&gt;Jugilus&lt;/a&gt; was working on the &lt;a href="https://jugilus.github.io/Jugimap-GuiDemo/JugimapGuiDemo.html" target="_blank" rel="noreferrer"&gt;Gui Demo&lt;/a&gt; he asked me about supporting UTF-8 strings. At the time I had very little knowledge about the topic except for &lt;a href="https://www.joelonsoftware.com/2003/10/08/the-absolute-minimum-every-software-developer-absolutely-positively-must-know-about-unicode-and-character-sets-no-excuses/" target="_blank" rel="noreferrer"&gt;the absolute minimum every developer must know&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;As many times with the nCine, my coding from scratch efforts is an excuse to research more about something, and the same happened this time. In a short amount of time, I knew a lot more about Unicode, code points, &lt;a href="https://www.instructables.com/Programming--how-to-detect-and-read-UTF-8-charact/" target="_blank" rel="noreferrer"&gt;decoding&lt;/a&gt; UTF-8, &lt;a href="https://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt" target="_blank" rel="noreferrer"&gt;testing&lt;/a&gt; it, or how to interpret some of the information from a table like &lt;a href="https://www.compart.com/en/unicode/U&amp;#43;00e8" target="_blank" rel="noreferrer"&gt;this&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;&lt;img
class="my-0 rounded-md"
loading="lazy"
decoding="async"
fetchpriority="low"
alt="apptest_font rendering Unicode glyphs"
src="/images/apptest_font_unicode.png"
&gt;&lt;figcaption&gt;&lt;code&gt;apptest_font&lt;/code&gt; rendering Unicode glyphs&lt;/figcaption&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 class="relative group"&gt;Android soft keyboard
&lt;div id="android-soft-keyboard" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;I always wanted to enable the use of the Android virtual keyboard to allow users to enter text in as it is expected from a mobile device, that is without plugging in a physical keyboard. &amp;#x1f605;&lt;/p&gt;
&lt;p&gt;I just didn&amp;rsquo;t have the will to dig into the Android API and JNI once more to support the feature. In the end, I was able to gather my strength and implement it.&lt;/p&gt;
&lt;p&gt;One downside is that &lt;a href="https://developer.android.com/reference/android/view/inputmethod/InputMethodManager#toggleSoftInput%28int,%20int%29" target="_blank" rel="noreferrer"&gt;toggling&lt;/a&gt; visibility seems to be more reliable than querying and settings the visibility state, as per this StackOverflow question about &lt;a href="https://stackoverflow.com/questions/13694995/android-softkeyboard-showsoftinput-vs-togglesoftinput" target="_blank" rel="noreferrer"&gt;&lt;code&gt;showSoftInput&lt;/code&gt; vs &lt;code&gt;toggleSoftInput&lt;/code&gt;&lt;/a&gt;. &amp;#x1f61e;&lt;/p&gt;
&lt;h3 class="relative group"&gt;Tracy multiple memory pools tracking
&lt;div id="tracy-multiple-memory-pools-tracking" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;I remember back at the end of March when I asked Tracy author on Discord if he was considering adding a way to tag allocations so that it would be possible to tell which custom allocator was responsible for it.&lt;/p&gt;
&lt;p&gt;I didn&amp;rsquo;t even have custom allocators at the time, but I was starting working on them. And now, after many months, we don&amp;rsquo;t only have custom allocators in the nCine, but string tags in Tracy &lt;a href="https://github.com/wolfpld/tracy/releases/tag/v0.7.3" target="_blank" rel="noreferrer"&gt;v0.7.3&lt;/a&gt; allocation macros! &amp;#x1f4aa;&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;&lt;img
class="my-0 rounded-md"
loading="lazy"
decoding="async"
fetchpriority="low"
alt="Multiple Memory Pools in Tracy v0.7.3"
src="/images/Tracy073_memory_pools.png"
&gt;&lt;figcaption&gt;Multiple Memory Pools in Tracy v0.7.3&lt;/figcaption&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 class="relative group"&gt;Minor changes
&lt;div id="minor-changes" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;After a very long time since the last release, &lt;a href="https://www.lua.org/versions.html#5.4" target="_blank" rel="noreferrer"&gt;Lua 5.4&lt;/a&gt; is finally out. It broke a couple of things, from CMake older than version &lt;a href="https://blog.kitware.com/cmake-3-18-0-rc4-is-ready-for-testing/" target="_blank" rel="noreferrer"&gt;3.18&lt;/a&gt; not being able to find it to a compilation error due to a &lt;a href="https://www.lua.org/manual/5.4/manual.html#8.3" target="_blank" rel="noreferrer"&gt;missing constant&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The FileSystem API was updated to support Android assets: it&amp;rsquo;s now possible to perform queries on asset files and traverse asset directories.&lt;/li&gt;
&lt;li&gt;A user made me notice that the root node was not being transformed, breaking the assumption that parent nodes affect children&amp;rsquo;s transformations. I fixed it by slightly changing the nodes tree traversal.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Hopefully, this update was enjoyable enough that you are coming back for the next one. Stay tuned! &amp;#x1f609;&lt;/p&gt;</description></item><item><title>nCine Dev Update 15</title><link>https://encelo.github.io/2020-07-14-ncine-dev-update-15/</link><pubDate>Tue, 14 Jul 2020 00:00:00 +0000</pubDate><guid>https://encelo.github.io/2020-07-14-ncine-dev-update-15/</guid><description>&lt;p&gt;I have spent nearly two months on a big task this spring: custom memory allocators.
They can be useful in different scenarios to alleviate the performance cost of allocating and deallocating memory.&lt;/p&gt;
&lt;p&gt;But before diving into that I had to be sure that the containers were ready.&lt;/p&gt;
&lt;h3 class="relative group"&gt;Split allocation and construction
&lt;div id="split-allocation-and-construction" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;Just like with STL ones, there has always been a difference between capacity and size in nCine containers.&lt;/p&gt;
&lt;p&gt;Size represents the number of elements currently stored in the container and it has an initial value of zero.
The capacity defines the maximum number of elements that can be stored in a container.&lt;/p&gt;
&lt;p&gt;Some containers like the &lt;code&gt;Array&lt;/code&gt; class are dynamic, similarly to an STL &lt;code&gt;vector&lt;/code&gt;, it can grow to accommodate more elements by allocating a bigger chunk of memory and copying over the old ones.
If you already know how many elements the container is going to store during its lifetime, you can reserve an initial capacity to avoid reallocations and copies.&lt;/p&gt;
&lt;p&gt;Now, the big difference between an old nCine &lt;code&gt;Array&lt;/code&gt; and an STL &lt;code&gt;vector&lt;/code&gt; is in their construction.
The first will allocate its memory like this: &lt;code&gt;array_ = new T[capacity_]&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;What is wrong with a perfectly safe statement like that?
The problem is that the class is both allocating memory and constructing elements up to its maximum capacity.&lt;/p&gt;
&lt;p&gt;This behavior has several implications:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Time is spent constructing objects even if no one asked for that&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;T&lt;/code&gt; class needs a default constructor&lt;/li&gt;
&lt;li&gt;New elements can only be copy-assigned or move-assigned&lt;/li&gt;
&lt;li&gt;It is not possible to emplace new elements&lt;/li&gt;
&lt;li&gt;Popping elements does not destruct them&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The solution is to split the allocation phase from the elements construction.
The &lt;code&gt;Array&lt;/code&gt; allocation then becomes: &lt;code&gt;array_ = static_cast&amp;lt;T *&amp;gt;(::operator new(capacity_ * sizeof(T)))&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;When we want to add new elements they will be copy-constructed (&lt;code&gt;new (extendOne()) T(element)&lt;/code&gt;) or move-constructed (&lt;code&gt;new (extendOne()) T(nctl::move(element))&lt;/code&gt;) in the array using a &lt;a href="https://en.cppreference.com/w/cpp/language/new#Placement_new" target="_blank" rel="noreferrer"&gt;placement new&lt;/a&gt; operator. Something similar will happen when we emplace back (&lt;code&gt;new (extendOne()) T(nctl::forward&amp;lt;Args&amp;gt;(args)...)&lt;/code&gt;) an element.
Popping elements will destruct them as you would expect from an STL &lt;code&gt;vector&lt;/code&gt; class.&lt;/p&gt;
&lt;p&gt;There is also an additional important optimization based on type traits: if an element is &lt;a href="https://en.cppreference.com/w/cpp/types/is_destructible" target="_blank" rel="noreferrer"&gt;trivially destructible&lt;/a&gt; then its destructor will not be called upon deallocation.&lt;/p&gt;
&lt;p&gt;All nCine containers and the relative unit tests and benchmarks were modified and updated to support the new behaviors and functionalities.&lt;/p&gt;
&lt;h3 class="relative group"&gt;Custom memory allocators
&lt;div id="custom-memory-allocators" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;Now that the containers have a clear and separated allocation phase it is time to work on the allocators themselves.&lt;/p&gt;
&lt;p&gt;They are always initialized with a pointer to the beginning of a memory arena and its size. This makes the allocators very flexible as the memory can be allocated in different ways, it can be a region accessible by the CPU or mapped by the GPU. It can even be a subset of a region managed by another custom allocator!&lt;/p&gt;
&lt;p&gt;The memory arena is usually allocated only once by the operating system then control is passed to custom allocators. They are generally faster to allocate and deallocate memory and they can achieve even more performance within the use cases they were designed for.&lt;/p&gt;
&lt;p&gt;One of my main references has been an article on Gamedev.net called &lt;a href="https://www.gamedev.net/tutorials/_/technical/general-programming/c-custom-memory-allocation-r3010/" target="_blank" rel="noreferrer"&gt;C++: Custom memory allocation&lt;/a&gt;. It shows a basic interface for allocators plus four different implementations.&lt;/p&gt;
&lt;p&gt;The engine implements the same four types of allocators:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;Linear&lt;/strong&gt; allocator is the simplest one and allocates in constant time. It doesn’t have any kind of memory overhead beyond the alignment requirements but it doesn&amp;rsquo;t do any allocation bookkeeping and thus it cannot deallocate. It supports a &lt;em&gt;clear&lt;/em&gt; operation to deallocate all memory at once though, a useful feature that can be used to implement a fast per-frame scratchpad memory.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;Stack&lt;/strong&gt; allocator introduces a header at the start of each allocation to keep track of the address adjustment made to satisfy the alignment requirements. This makes it possible to deallocate the last allocation made.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;Pool&lt;/strong&gt; allocator is a very classic allocator type for game development. It allows very fast allocations and out-of-order deallocations when the allocations have all the same size and the same alignment requirement.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;Free List&lt;/strong&gt; allocator is capable of serving allocations of different sizes and alignments and deallocating out-of-order.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There are also some differences between the article and my implementation:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The allocator interface has no virtual methods but relies on function pointers.&lt;/li&gt;
&lt;li&gt;The allocator interface supports reallocation. It can happen in place or by allocating a bigger chunk and copying the old elements, depending on the situation. Reallocation is very important to properly support Lua garbage collector and it is also used by Nuklear memory functions.&lt;/li&gt;
&lt;li&gt;The Free List allocator performs fast compaction on deallocation to keep external fragmentation low. It has also three different fit strategies for allocations: &lt;em&gt;Best&lt;/em&gt;, &lt;em&gt;Worst&lt;/em&gt;, and &lt;em&gt;First&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 class="relative group"&gt;Allocation manager
&lt;div id="allocation-manager" class="anchor"&gt;&lt;/div&gt;
&lt;/h4&gt;
&lt;p&gt;The entry point for allocators setup is the &lt;code&gt;AllocManager&lt;/code&gt; class. It is responsible for creating allocators before the first allocation is ever made and for providing those allocators to the application so that memory can be acquired and released.&lt;/p&gt;
&lt;p&gt;Ensuring that the allocation manager initializes the allocators before even static variables have a chance to allocate memory is the tricky part.
The first solution I investigated was the &lt;a href="https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Nifty_Counter" target="_blank" rel="noreferrer"&gt;Nifty Counter&lt;/a&gt;, but it is a dirty hack that can have performance implications.&lt;/p&gt;
&lt;p&gt;I then changed my implementation to use specific compiler features such as the &lt;a href="https://gcc.gnu.org/onlinedocs/gcc/C_002b_002b-Attributes.html#C_002b_002b-Attributes" target="_blank" rel="noreferrer"&gt;init_priority attribute&lt;/a&gt; of GCC and Clang, and the &lt;a href="https://docs.microsoft.com/en-us/cpp/preprocessor/init-seg?view=vs-2019" target="_blank" rel="noreferrer"&gt;init_seg pragma&lt;/a&gt; of MSVC.&lt;/p&gt;
&lt;p&gt;There are a couple of optional defines in the &lt;code&gt;AllocManager&lt;/code&gt; class that can be used to tweak some aspects.&lt;/p&gt;
&lt;p&gt;The first is &lt;code&gt;OVERRIDE_NEW&lt;/code&gt;, by defining it the class will redefine the global &lt;code&gt;new&lt;/code&gt; and &lt;code&gt;delete&lt;/code&gt; operators so that every allocation and deallocation will use custom allocators transparently.&lt;/p&gt;
&lt;p&gt;The second one is &lt;code&gt;RECORD_ALLOCATIONS&lt;/code&gt;, by defining it every allocator will record an entry for each allocation and deallocation made, with information about the number of bytes, the alignment, the timestamp, and so on.
You can then use some included functions to print all entries or to query the list and know which allocation has never been freed.&lt;/p&gt;
&lt;h4 class="relative group"&gt;Apptest_allocators
&lt;div id="apptest_allocators" class="anchor"&gt;&lt;/div&gt;
&lt;/h4&gt;
&lt;p&gt;Last but not least, a new application test has been added to show how allocators work: &lt;code&gt;apptest_allocators&lt;/code&gt;. It uses some custom ImGui drawing code to display a memory map similar to a disk partition editor.&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="lazy" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/2kAoyVvgLyo?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="nCine Custom Memory Allocators Test"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;h3 class="relative group"&gt;Minor changes
&lt;div id="minor-changes" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;There is now a &lt;a href="https://www.youtube.com/watch?v=lPMd8fI99gI" target="_blank" rel="noreferrer"&gt;node inspector&lt;/a&gt; in the debug overlay. It is useful for understanding what&amp;rsquo;s going on in the scene and for changing node properties.&lt;/li&gt;
&lt;li&gt;The FileSystem API has been extended to support Android assets files and directories.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I hope you found this article interesting and useful.
Ah, and don&amp;rsquo;t forget to check the &lt;a href="https://github.com/nCine/nCine/releases/tag/2020.05" target="_blank" rel="noreferrer"&gt;2020.05&lt;/a&gt; release! &amp;#x1f609;&lt;/p&gt;</description></item><item><title>nCine Intel Mesa 20 Driver Benchmark</title><link>https://encelo.github.io/2020-03-30-ncine-intel-mesa-20-driver-benchmark/</link><pubDate>Mon, 30 Mar 2020 00:00:00 +0000</pubDate><guid>https://encelo.github.io/2020-03-30-ncine-intel-mesa-20-driver-benchmark/</guid><description>&lt;p&gt;Today I upgraded my Arch Linux workstation with pacman as I usually do every day and a little surprise was waiting for me.
After a long time in &lt;code&gt;[testing]&lt;/code&gt;, &lt;a href="https://www.archlinux.org/packages/extra/x86_64/mesa/" target="_blank" rel="noreferrer"&gt;Mesa 20&lt;/a&gt; came out of the &lt;code&gt;[extra]&lt;/code&gt; repository, ready to be installed.&lt;/p&gt;
&lt;p&gt;This &lt;a href="https://lists.freedesktop.org/archives/mesa-dev/2020-February/224132.html" target="_blank" rel="noreferrer"&gt;release&lt;/a&gt; brings a ton of fixes and new features, alongside the new &lt;a href="https://xdc2018.x.org/slides/optimizing-i965-for-the-future.pdf" target="_blank" rel="noreferrer"&gt;iris&lt;/a&gt; driver for modern Intel integrated GPUs based on Gallium3D.&lt;/p&gt;
&lt;p&gt;I have conducted some benchmarks on my Intel Core i7-8550U with Intel HD Graphics 620 (GT2), an Intel Gen 9.5 GPU.&lt;/p&gt;
&lt;h3 class="relative group"&gt;Setup
&lt;div id="setup" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;I have compiled the nCine in release and disabled V-Sync, then I ran a script to minimize CPU throttling and frequency variations:&lt;/p&gt;
&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;echo performance &amp;gt; /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;echo &lt;span style="color:#ae81ff"&gt;800000&lt;/span&gt; &amp;gt; /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;echo &lt;span style="color:#ae81ff"&gt;800000&lt;/span&gt; &amp;gt; /sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;[&lt;/span&gt;repeat &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; remaining cores, cpu1 to cpu7&lt;span style="color:#f92672"&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;performance&lt;/code&gt; &lt;a href="https://wiki.archlinux.org/index.php/CPU_frequency_scaling#Scaling_governors" target="_blank" rel="noreferrer"&gt;governor&lt;/a&gt; should keep the CPU running at the same frequency all the time while the minimum frequency of 800 Mhz is set as both the minimum and maximum overall frequency. Those steps should both prevent the CPU from overheating and possibly highlight the reduced CPU load of &lt;code&gt;iris&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To choose between the two drivers I used the &lt;code&gt;MESA_LOADER_DRIVER_OVERRIDE&lt;/code&gt; environment variable. I set it to &lt;code&gt;i965&lt;/code&gt; for the legacy one or unset the variable for the new one, as it is now the default.&lt;/p&gt;
&lt;p&gt;To confirm which driver was in use during a test I set the &lt;code&gt;LIBGL_DEBUG&lt;/code&gt; environment variable to &lt;code&gt;verbose&lt;/code&gt;.&lt;/p&gt;
&lt;h3 class="relative group"&gt;Tests
&lt;div id="tests" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;Let&amp;rsquo;s see how many frames can be rendered over a time of five seconds.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Test&lt;/th&gt;
&lt;th style="text-align: right"&gt;i965&lt;/th&gt;
&lt;th style="text-align: right"&gt;iris&lt;/th&gt;
&lt;th style="text-align: right"&gt;Ratio&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;apptest_camera&lt;/code&gt; (800 Mhz)&lt;/td&gt;
&lt;td style="text-align: right"&gt;1665&lt;/td&gt;
&lt;td style="text-align: right"&gt;1659&lt;/td&gt;
&lt;td style="text-align: right"&gt;0.99x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;apptest_camera&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align: right"&gt;4532&lt;/td&gt;
&lt;td style="text-align: right"&gt;4646&lt;/td&gt;
&lt;td style="text-align: right"&gt;1.02x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;apptest_meshsprites&lt;/code&gt; (800 Mhz)&lt;/td&gt;
&lt;td style="text-align: right"&gt;1281&lt;/td&gt;
&lt;td style="text-align: right"&gt;1294&lt;/td&gt;
&lt;td style="text-align: right"&gt;1.01x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;apptest_meshsprites&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align: right"&gt;3451&lt;/td&gt;
&lt;td style="text-align: right"&gt;3515&lt;/td&gt;
&lt;td style="text-align: right"&gt;1.01x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;apptest_particles_100x&lt;/code&gt; (800 Mhz)&lt;/td&gt;
&lt;td style="text-align: right"&gt;385&lt;/td&gt;
&lt;td style="text-align: right"&gt;387&lt;/td&gt;
&lt;td style="text-align: right"&gt;1.00x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;apptest_particles_100x&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align: right"&gt;509&lt;/td&gt;
&lt;td style="text-align: right"&gt;508&lt;/td&gt;
&lt;td style="text-align: right"&gt;1.00x&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 class="relative group"&gt;Conclusions
&lt;div id="conclusions" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;It seems that for the nCine kind of workload, 2D sprites and low vertex count, there is practically no difference between the two drivers on my machine. The results do not change when the CPU frequency is capped at its minimum.&lt;/p&gt;
&lt;p&gt;This is not necessarily a bad thing. I would have been glad to see some performance uplift with those 2D tests but I&amp;rsquo;m glad to see that &lt;code&gt;iris&lt;/code&gt; does not seem to suffer at all from being a lot less mature than &lt;code&gt;i965&lt;/code&gt;.&lt;/p&gt;</description></item><item><title>nCine Dev Update 14</title><link>https://encelo.github.io/2020-03-23-ncine-dev-update-14/</link><pubDate>Mon, 23 Mar 2020 00:00:00 +0000</pubDate><guid>https://encelo.github.io/2020-03-23-ncine-dev-update-14/</guid><description>&lt;p&gt;Welcome to another nCine development update! As usual, there are a lot of new things to cover.&lt;/p&gt;
&lt;h3 class="relative group"&gt;ANGLE
&lt;div id="angle" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;To extend the support to more devices and platforms I have ported the nCine to &lt;a href="http://angleproject.org" target="_blank" rel="noreferrer"&gt;ANGLE&lt;/a&gt;.
It was an easy task as I just needed to tell GLFW and SDL2 to use EGL and open an OpenGL ES 3.0 context on Windows.&lt;/p&gt;
&lt;p&gt;With ANGLE the application would use Direct3D 11, overcoming any possible bug in old OpenGL drivers on old GPUs. It would also enable a future porting to &lt;a href="https://en.wikipedia.org/wiki/Universal_Windows_Platform" target="_blank" rel="noreferrer"&gt;UWP&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You have to sacrifice some performance, unfortunately. On my Intel i7-8850u with UHD 620 GT2 the &lt;code&gt;apptest_camera&lt;/code&gt; test is capable of rendering nearly 1.5x frames in 5 seconds when using the OpenGL driver instead of ANGLE.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Version&lt;/th&gt;
&lt;th&gt;Batches size&lt;/th&gt;
&lt;th style="text-align: right"&gt;Frames&lt;/th&gt;
&lt;th style="text-align: right"&gt;Ratio&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;OpenGL 3.3&lt;/td&gt;
&lt;td&gt;unlimited&lt;/td&gt;
&lt;td style="text-align: right"&gt;6263&lt;/td&gt;
&lt;td style="text-align: right"&gt;1.461&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OpenGL 3.3&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right"&gt;6006&lt;/td&gt;
&lt;td style="text-align: right"&gt;1.401&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ANGLE&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right"&gt;4286&lt;/td&gt;
&lt;td style="text-align: right"&gt;1.000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OpenGL 3.3&lt;/td&gt;
&lt;td&gt;disabled&lt;/td&gt;
&lt;td style="text-align: right"&gt;4819&lt;/td&gt;
&lt;td style="text-align: right"&gt;1.432&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ANGLE&lt;/td&gt;
&lt;td&gt;disabled&lt;/td&gt;
&lt;td style="text-align: right"&gt;3365&lt;/td&gt;
&lt;td style="text-align: right"&gt;1.000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;When buffer mapping is enabled the performance takes a huge hit. &amp;#x1f623;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Version&lt;/th&gt;
&lt;th&gt;Batches size&lt;/th&gt;
&lt;th style="text-align: right"&gt;Frames&lt;/th&gt;
&lt;th style="text-align: right"&gt;Ratio&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;OpenGL 3.3&lt;/td&gt;
&lt;td&gt;unlimited&lt;/td&gt;
&lt;td style="text-align: right"&gt;5559&lt;/td&gt;
&lt;td style="text-align: right"&gt;2.871&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OpenGL 3.3&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right"&gt;5302&lt;/td&gt;
&lt;td style="text-align: right"&gt;2.738&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ANGLE&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td style="text-align: right"&gt;1936&lt;/td&gt;
&lt;td style="text-align: right"&gt;1.000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OpenGL 3.3&lt;/td&gt;
&lt;td&gt;disabled&lt;/td&gt;
&lt;td style="text-align: right"&gt;4069&lt;/td&gt;
&lt;td style="text-align: right"&gt;1.432&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ANGLE&lt;/td&gt;
&lt;td&gt;disabled&lt;/td&gt;
&lt;td style="text-align: right"&gt;974&lt;/td&gt;
&lt;td style="text-align: right"&gt;4.177&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 class="relative group"&gt;Qt5 backend
&lt;div id="qt5-backend" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;I have added &lt;a href="https://www.qt.io/" target="_blank" rel="noreferrer"&gt;Qt5&lt;/a&gt; as a new desktop backend.&lt;/p&gt;
&lt;p&gt;This will open more possibilities for tool developers, as they can now create a Qt5 interface and embed the nCine as a custom &lt;a href="https://doc.qt.io/qt-5/qopenglwidget.html" target="_blank" rel="noreferrer"&gt;QOpenGLWidget&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Using Qt5 for the interface does not mean you can&amp;rsquo;t continue to use ImGui and Nuklear too. You can even use all three together if you so want, as the updated &lt;code&gt;apptest_scene&lt;/code&gt; demonstrates. &amp;#x1f609;&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="lazy" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/PpVLD3ShiCw?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="nCine as a custom Qt5 widget"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;If the &lt;a href="https://doc.qt.io/qt-5/qtgamepad-index.html" target="_blank" rel="noreferrer"&gt;Qt Gamepad&lt;/a&gt; library is found then gamepad events will be supported. I also decided to expose touch events to desktop applications, as both SDL2 and Qt5 support them.&lt;/p&gt;
&lt;h3 class="relative group"&gt;FileSystem API
&lt;div id="filesystem-api" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;Another big addition is the new file system API. It allows an application to manipulate paths, query file and directory attributes, copy, rename or delete files, create directories and inspect their contents and so on.&lt;/p&gt;
&lt;p&gt;It works on all supported platforms as it features both a POSIX and a Windows API implementation.&lt;/p&gt;
&lt;p&gt;It comes with a unit test that should assure everything works as expected and with a new apptest. &lt;code&gt;apptest_filebowser&lt;/code&gt; displays a file selection window made with ImGui that makes it easy to browse the file system.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;&lt;img
class="my-0 rounded-md"
loading="lazy"
decoding="async"
fetchpriority="low"
alt="apptest_filebrowser"
src="/images/apptest_filebrowser.png"
&gt;&lt;figcaption&gt;apptest_filebrowser&lt;/figcaption&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 class="relative group"&gt;Emscripten
&lt;div id="emscripten" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;I fixed the compilation with Emscripten version &lt;a href="https://emscripten.org/docs/introducing_emscripten/release_notes.html" target="_blank" rel="noreferrer"&gt;1.39.5&lt;/a&gt; that makes &lt;code&gt;DISABLE_DEPRECATED_FIND_EVENT_TARGET_BEHAVIOR&lt;/code&gt; the &lt;a href="https://groups.google.com/forum/#!msg/emscripten-discuss/xScZ_LRIByk/_gEy67utDgAJ" target="_blank" rel="noreferrer"&gt;default&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;It means it is not possible anymore to pass a &lt;code&gt;nullptr&lt;/code&gt; as a target in the &lt;a href="https://emscripten.org/docs/api_reference/html5.h.html" target="_blank" rel="noreferrer"&gt;HTML5&lt;/a&gt; API and expect it will automatically choose a reasonable element. You have to be explicit and pass things like &lt;code&gt;&amp;quot;canvas&amp;quot;&lt;/code&gt; or &lt;code&gt;EMSCRIPTEN_EVENT_TARGET_WINDOW&lt;/code&gt;.&lt;/p&gt;
&lt;h3 class="relative group"&gt;Minor changes
&lt;div id="minor-changes" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;As it is sometimes useful, it is now possible to multiply a vector by a matrix with the matrix on the right side of the multiplication.&lt;/li&gt;
&lt;li&gt;C-style strings can now correctly be used as hashmap keys. Previously the key comparison code was comparing memory pointers instead of characters, making this type of strings useless with hashmap containers.&lt;/li&gt;
&lt;li&gt;If you compile the engine statically you will have access to a couple of classes that save textures as PNG or WebP images.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It is all for now. See you for the next update! &amp;#x1f609;&lt;/p&gt;</description></item><item><title>nCine Dev Update 13</title><link>https://encelo.github.io/2019-12-24-ncine-dev-update-13/</link><pubDate>Tue, 24 Dec 2019 00:00:00 +0000</pubDate><guid>https://encelo.github.io/2019-12-24-ncine-dev-update-13/</guid><description>&lt;p&gt;A lot of work has been put into the project as usual during those last months of the year.&lt;/p&gt;
&lt;p&gt;Plenty of new and important features have been added to the engine, many of them are related to extending the capabilities of sprite rendering.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;&lt;img
class="my-0 rounded-md"
loading="lazy"
decoding="async"
fetchpriority="low"
alt="apptest_anchor"
src="/images/apptest_anchor.png"
&gt;&lt;figcaption&gt;apptest_anchor&lt;/figcaption&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 class="relative group"&gt;JugiMap
&lt;div id="jugimap" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;I have been recently contacted by &lt;a href="https://github.com/Jugilus" target="_blank" rel="noreferrer"&gt;Jugilus&lt;/a&gt;, &lt;a href="http://jugimap.com/" target="_blank" rel="noreferrer"&gt;JugiMap&lt;/a&gt;&amp;rsquo;s author, for a collaboration. He was interested in the nCine as a sprite rendering backend, alongside &lt;a href="https://www.cocos.com/en/" target="_blank" rel="noreferrer"&gt;Cocos2d-x&lt;/a&gt;, &lt;a href="https://www.appgamekit.com/" target="_blank" rel="noreferrer"&gt;AppGameKit&lt;/a&gt;, and &lt;a href="https://www.sfml-dev.org/" target="_blank" rel="noreferrer"&gt;SFML&lt;/a&gt;, to test the tool &lt;a href="https://github.com/Jugilus/JugiMapAPI" target="_blank" rel="noreferrer"&gt;integration API&lt;/a&gt; and preview in real-time exported maps and logic.&lt;/p&gt;
&lt;p&gt;He made a list of requirements for features that were not in the engine at the time but that every other backend had.
To be honest the features he mentioned were, in a way or another, already in my to-do list so I just had to change my priorities to accommodate the need of the project. &amp;#x1f609;&lt;/p&gt;
&lt;p&gt;The features I have implemented from his list are the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Custom anchor points&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;It allows a node to be anchored to a point different than its center, like one of the four corners, and be transformed relative to it.
The feature needed a lot of refinements to work in every case and with every type of node.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Non-uniform scaling&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Nodes can now be scaled independently along the horizontal or the vertical axis.
This feature was already requested by a tester of ncParticleEditor a long time ago. &amp;#x1f605;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Texture flipping with a boolean flag&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This change makes a lot of sense as you can now query and check if a sprite is flipped along one of the axes.
Previously flipping was just an action to perform and there was no way to tell whether the sprite was flipped or not.
This also means that, just like the custom anchor point, if you change something in the sprite, like the texture, the engine needs to automatically reapply any flipping.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Custom blending factors&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Another feature requested by a tester of ncParticleEditor a long time ago. &amp;#x1f604;
The user can now specify a custom source or destination factor for blending or use one of the presets, allowing for &lt;a href="https://en.wikipedia.org/wiki/Alpha_compositing#Straight_versus_premultiplied" target="_blank" rel="noreferrer"&gt;premultiplied alpha&lt;/a&gt; textures or additive effects.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I was very happy to have the API demo test as a big stress test for the sprite rendering, it highlighted serious issues like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Frame latencies with parent/child transformations&lt;/li&gt;
&lt;li&gt;Drawable nodes culling not working with negative scaling&lt;/li&gt;
&lt;li&gt;One frame delay to update a text node cached boundaries&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can find the project repositories on GitHub: &lt;a href="https://github.com/nCine/ncJugiMapAPIDemo" target="_blank" rel="noreferrer"&gt;ncJugiMapAPIDemo&lt;/a&gt; and &lt;a href="https://github.com/nCine/ncJugiMapAPIDemo-data" target="_blank" rel="noreferrer"&gt;ncJugiMapAPIDemo-data&lt;/a&gt;.
You can also test the Emscripten &lt;a href="https://ncine.github.io/ncjugimapapidemo/" target="_blank" rel="noreferrer"&gt;web test&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;&lt;img
class="my-0 rounded-md"
loading="lazy"
decoding="async"
fetchpriority="low"
alt="ncJugiMapAPIDemo"
src="/images/ncJugiMapAPIDemo.png"
&gt;&lt;figcaption&gt;ncJugiMapAPIDemo&lt;/figcaption&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 class="relative group"&gt;Emscripten enhancements
&lt;div id="emscripten-enhancements" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;The Emscripten port has seen some improvements and fixes too. First of all, it can now build with version 1.39.0 or newer, which made the &lt;a href="https://v8.dev/blog/emscripten-llvm-wasm" target="_blank" rel="noreferrer"&gt;LLVM WebAssembly backend&lt;/a&gt; the &lt;a href="https://github.com/emscripten-core/emsdk/pull/373" target="_blank" rel="noreferrer"&gt;default&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Another important fix is the correct handling of browser window resizing: an Emscripten application should now be correctly displayed regardless of browser window size or fullscreen state.
Continuous resizing should now also work for desktop native versions where the perspective matrix was previously not updated. &amp;#x1f605;&lt;/p&gt;
&lt;p&gt;Deploying a nCine based tool to Emscripten makes a lot more sense as it is now possible to load and save files locally. It means you can load a file from your computer into a web build or save it from there to your computer. You can test this feature in the updated &lt;a href="https://ncine.github.io/ncparticleeditor" target="_blank" rel="noreferrer"&gt;ncParticleEditor&lt;/a&gt; web test. You will also notice the support for ImGui custom font loading in the form of &lt;a href="https://fontawesome.com/" target="_blank" rel="noreferrer"&gt;FontAwesome&lt;/a&gt; icons. &amp;#x1f609;&lt;/p&gt;
&lt;p&gt;Last but not least, after a long tribulation, Emscripten builds can now use automatic render commands batching! &amp;#x1f4aa;
There is just one small catch, the batch size is fixed. &amp;#x1f629;&lt;/p&gt;
&lt;p&gt;Sure, you can configure it on initialization, but it is fixed, there is no minimum or maximum, the application will collect a certain amount of commands per batch. It will split a batch if it has reached the predetermined size or it will render single commands unbatched if there aren&amp;rsquo;t enough.
You should also keep this number quite small to prevent the D3D shader compiler in ANGLE from taking a lot of time. &amp;#x1f604;&lt;/p&gt;
&lt;h3 class="relative group"&gt;Nuklear integration
&lt;div id="nuklear-integration" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://github.com/Immediate-Mode-UI/Nuklear" target="_blank" rel="noreferrer"&gt;Nuklear&lt;/a&gt; is an immediate UI similar in concept to &lt;a href="https://github.com/ocornut/imgui" target="_blank" rel="noreferrer"&gt;Dear ImGui&lt;/a&gt; but skinnable. I have added this integration as I expect a game to use Nuklear and customize the graphics while a tool would use the superior flexibility of Dear ImGui.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;&lt;img
class="my-0 rounded-md"
loading="lazy"
decoding="async"
fetchpriority="low"
alt="Nuklear integration"
src="/images/Nuklear_integration.png"
&gt;&lt;figcaption&gt;Nuklear integration&lt;/figcaption&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 class="relative group"&gt;Additional improvements
&lt;div id="additional-improvements" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;There are a lot of smaller things that have been added during this period:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;There are two new application events you can subscribe to, &lt;code&gt;onSuspend&lt;/code&gt; and &lt;code&gt;onResume&lt;/code&gt;. Frame and profile timers will not take into account the time while the application has been suspended.&lt;/li&gt;
&lt;li&gt;There is now a &lt;code&gt;ColorHdr&lt;/code&gt; class that allows for unclamped floating-point color values which can be used for supporting HDR in tools or demos.&lt;/li&gt;
&lt;li&gt;Sorting of render commands is now stable, there should be no more random popping of a sprite in front of another. Two commands with the same material sort key will be sorted according to their creation time.&lt;/li&gt;
&lt;li&gt;The 4x4 matrix class supports in place transformations. It means that less memory will be used and fewer multiplications will be performed when translating, rotating or scaling in place.&lt;/li&gt;
&lt;li&gt;The deletion of children scene nodes upon parent destruction is now optional.&lt;/li&gt;
&lt;li&gt;As usual, the integrations with ImGui and Tracy have been updated to support the latest versions at the time of writing.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Enjoy the new nCine features and this festive season, whether you celebrate Christmas or not. &amp;#x26c4;&lt;/p&gt;</description></item><item><title>nCine Dev Update 12</title><link>https://encelo.github.io/2019-09-21-ncine-dev-update-12/</link><pubDate>Sat, 21 Sep 2019 00:00:00 +0000</pubDate><guid>https://encelo.github.io/2019-09-21-ncine-dev-update-12/</guid><description>&lt;p&gt;The last two months of work on the nCine were mostly dedicated to the quality of life improvements for users.&lt;/p&gt;
&lt;p&gt;First of all, I decided to get rid of the legacy debug overlay. It was a very old and problematic code that didn&amp;rsquo;t have any reason to be today. With the ImGui and Tracy integrations in place, the nCine is more than covered in that aspect. &amp;#x1f4aa;&lt;/p&gt;
&lt;p&gt;In an attempt to make the use of &lt;code&gt;nctl::string&lt;/code&gt; less confusing for users of &lt;code&gt;std::string&lt;/code&gt; I refactored and renamed many &lt;code&gt;copy&lt;/code&gt; methods to &lt;code&gt;assign&lt;/code&gt;.&lt;/p&gt;
&lt;h3 class="relative group"&gt;FNT parser
&lt;div id="fnt-parser" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;While I was explaining to a user how to make the font in his game bigger by creating a new texture, I suggested &lt;a href="https://github.com/andryblack/fontbuilder" target="_blank" rel="noreferrer"&gt;fontbuilder&lt;/a&gt;, a multi-platform tool I found on GitHub. I then tried the tool myself and when I used the generated texture the text was not properly rendered in the nCine. &amp;#x1f605;&lt;/p&gt;
&lt;p&gt;I knew the &lt;a href="http://www.angelcode.com/products/bmfont/doc/file_format.html" target="_blank" rel="noreferrer"&gt;FNT format&lt;/a&gt; has different ways to encode glyph data but I was supporting only a couple of them.
I took some time to rewrite my &lt;code&gt;FNT&lt;/code&gt; parser with the specifications in mind, extracting all the available data allowing me to add some additional encoding formats.
Most importantly it provides the engine with the reasons why a specific encoding is not supported, leading to more meaningful error messages.&lt;/p&gt;
&lt;h3 class="relative group"&gt;Timer refactoring
&lt;div id="timer-refactoring" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;I refactored the &lt;code&gt;Timer&lt;/code&gt; class to be just an interface to a stopwatch. The platform specific code to access the monotonic clock and retrieve the elapsed ticks is now inside a &lt;code&gt;Clock&lt;/code&gt; class.&lt;/p&gt;
&lt;p&gt;Besides using the timer, the user can now also use the &lt;code&gt;TimeStamp&lt;/code&gt; class to record or accumulate counter values, with the possibility to convert them to various units of measure in a way similar to &lt;a href="https://en.cppreference.com/w/cpp/chrono/duration" target="_blank" rel="noreferrer"&gt;std::chrono::duration&lt;/a&gt;.&lt;/p&gt;
&lt;h3 class="relative group"&gt;Tracy v0.5
&lt;div id="tracy-v05" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;With the release of &lt;a href="https://bitbucket.org/wolfpld/tracy/src/v0.5/" target="_blank" rel="noreferrer"&gt;Tracy v0.5&lt;/a&gt; last August it was time to update its integration inside the engine and make it easier for external projects to use it.&lt;/p&gt;
&lt;p&gt;Additionally, you can now rename a thread to easily spot it in the main profiler window and access all engine log entries in the messages section.
Good news for macOS users, memory profiling is now working and has been re-enabled. This new version of Tracy should also be able to compile on MinGW.&lt;/p&gt;
&lt;h3 class="relative group"&gt;Windows changes
&lt;div id="windows-changes" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;The Windows port was given some care, with the removal of every &lt;code&gt;Windows.h&lt;/code&gt; inclusion and the &lt;a href="https://aras-p.info/blog/2018/01/12/Minimizing-windows.h/" target="_blank" rel="noreferrer"&gt;minimization&lt;/a&gt; of the headers used.
The atomic implementations were moved from headers to sources, removing the need for including a Windows API header in &lt;code&gt;&amp;lt;nctl/Atomic.h&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Two more Windows improvements are the use of the Windows subsystem instead of console for apptest executables and the installation of a desktop link by the NSIS generated installers.&lt;/p&gt;
&lt;p&gt;Last but not least the engine and the game projects now generate a &lt;a href="https://docs.microsoft.com/en-us/windows/win32/menurc/versioninfo-resource" target="_blank" rel="noreferrer"&gt;VERSIONINFO resource&lt;/a&gt; file.
Thanks to it you can open the properties window of files like &lt;code&gt;ncine.dll&lt;/code&gt; or &lt;code&gt;ncpong.exe&lt;/code&gt; and retrieve product and version information.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;&lt;img
class="my-0 rounded-md"
loading="lazy"
decoding="async"
fetchpriority="low"
alt="VERSIONINFO resource"
src="/images/VERSIONINFO.png"
&gt;&lt;figcaption&gt;VERSIONINFO resource&lt;/figcaption&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 class="relative group"&gt;Granular library dependencies
&lt;div id="granular-library-dependencies" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;Some users didn&amp;rsquo;t like Lua at all and asked me how to remove the bindings integration. &amp;#x1f604;
I told them this was totally possible as the nCine was modular since its inception: you could already take out library dependencies at the expense of functionality.
The problem was that the feature was not easily accessible to users and properly tested among all combinations.&lt;/p&gt;
&lt;p&gt;Now it is and you can turn off the &lt;code&gt;NCINE_WITH_LUA&lt;/code&gt; CMake option to disable the integration. The engine will not be linked with the Lua library and not packaged with it in installers or archives. There are similar options to disable threads, audio or the integration with decoding libraries such as &lt;code&gt;libpng&lt;/code&gt;, &lt;code&gt;libwebp&lt;/code&gt; or &lt;code&gt;libvorbis&lt;/code&gt;.&lt;/p&gt;
&lt;h3 class="relative group"&gt;Additional projects
&lt;div id="additional-projects" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;A lot of work was devoted to adding or improving accompanying projects too, in an attempt to further ease the life of nCine users.&lt;/p&gt;
&lt;p&gt;For example, a new &lt;a href="https://github.com/nCine/ncTemplate" target="_blank" rel="noreferrer"&gt;ncTemplate&lt;/a&gt; repository was pushed to GitHub. It is supposed to be the starting point for creating a new project with the engine.
By using it you ensure that all the CMake logic to support all platforms is in place.
Check the &lt;a href="https://github.com/nCine/ncTemplate/blob/master/README.md" target="_blank" rel="noreferrer"&gt;README.md&lt;/a&gt; file for additional information.&lt;/p&gt;
&lt;p&gt;There is a second repository that has been created to make working with the nCine easier: &lt;a href="https://github.com/nCine/ncline" target="_blank" rel="noreferrer"&gt;ncline&lt;/a&gt;, the nCine command line tool.
It all started a couple years ago when I thought a Python script could automate some of the tasks related to calling CMake on different platforms.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s when &lt;code&gt;ncDo.py&lt;/code&gt; was born, an internal tool I used to speed up the compilation and testing of the nCine when switching from an operating system to the other.
Today the &lt;em&gt;continuous integration&lt;/em&gt; helps a lot in that regard but an automation tool is still very valuable for me and for users.&lt;/p&gt;
&lt;p&gt;I decided to rewrite it in C++ as it was growing more complex and I wasn&amp;rsquo;t comfortable with Python anymore. Thanks to &lt;a href="https://github.com/muellan/clipp" target="_blank" rel="noreferrer"&gt;clipp&lt;/a&gt; and &lt;a href="https://github.com/skystrife/cpptoml" target="_blank" rel="noreferrer"&gt;cpptoml&lt;/a&gt; I managed to keep all the features that were once carried out by &lt;a href="https://docs.python.org/3/library/argparse.html" target="_blank" rel="noreferrer"&gt;argparse&lt;/a&gt; and &lt;a href="https://docs.python.org/3/library/configparser.html" target="_blank" rel="noreferrer"&gt;configparseer&lt;/a&gt;.
The tool has now more functionalities than ever before, being able to download source code or artifacts using Git.
Just like earlier, have a look at the &lt;a href="https://github.com/nCine/ncline/blob/master/README.md" target="_blank" rel="noreferrer"&gt;README.md&lt;/a&gt; file for more information.&lt;/p&gt;
&lt;p&gt;Last but not least, I made a first GitHub &lt;a href="https://github.com/nCine/nCine/releases/tag/2019.05" target="_blank" rel="noreferrer"&gt;release&lt;/a&gt; and started &lt;a href="https://ncine.github.io/download-develop/" target="_blank" rel="noreferrer"&gt;keeping track&lt;/a&gt; of differences between releases and the &lt;code&gt;develop&lt;/code&gt; branch. Yet another small quality of life improvement, especially for users migrating to a newer version.&lt;/p&gt;
&lt;p&gt;I hope those changes will make the nCine easier to use to create awesome games! &amp;#x1f609;&lt;/p&gt;</description></item><item><title>nCine Dev Update 11</title><link>https://encelo.github.io/2019-07-16-ncine-dev-update-11/</link><pubDate>Tue, 16 Jul 2019 00:00:00 +0000</pubDate><guid>https://encelo.github.io/2019-07-16-ncine-dev-update-11/</guid><description>&lt;p&gt;Exciting news for this development update: a new supported platform! &amp;#x1f37e;&lt;/p&gt;
&lt;h3 class="relative group"&gt;Emscripten
&lt;div id="emscripten" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;I remember playing with the idea of porting the nCine to &lt;a href="https://emscripten.org/" target="_blank" rel="noreferrer"&gt;Emscripten&lt;/a&gt; years ago.
After all I had every requirement in place: I used OpenGL ES for Android, GLFW and SDL2 as input backends, OpenAL and Vorbis for audio, libpng for images and already supported a POSIX API.&lt;/p&gt;
&lt;p&gt;Unfortunately there was always a showstopper preventing me to achieve some progress and gain more motivation.
I remember managing to compile unit tests and run them from the console with Node.js but that was it, no apptest was ever working or sometimes even compiling.&lt;/p&gt;
&lt;p&gt;This time was different, I studied the documentation in detail, put a lot more energy and dedication but here we are, it works!&lt;/p&gt;
&lt;p&gt;Of course there were a lot of little issues that ended up eating plenty of time. &amp;#x1f629;&lt;/p&gt;
&lt;h4 class="relative group"&gt;Rendering
&lt;div id="rendering" class="anchor"&gt;&lt;/div&gt;
&lt;/h4&gt;
&lt;p&gt;I encountered the first issue when I tried running apptests on Windows, the browser just froze.
Further investigations led me to the culprit: shader compilation was super slow when using &lt;a href="http://angleproject.org" target="_blank" rel="noreferrer"&gt;ANGLE&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I did some research on the internet and I stumbled upon the &lt;a href="http://toji.github.io/shader-perf/" target="_blank" rel="noreferrer"&gt;Asynchronous Shader Compilation&lt;/a&gt; demo.
The article explains a way to give browsers a chance to compile shaders asynchronously while performing other loading related tasks.&lt;/p&gt;
&lt;p&gt;I followed the advices: compile and link shaders but defer any error checking or introspection to the first use.
It didn&amp;rsquo;t fix the issue at all but made loading time faster on every platform. &amp;#x1f605;&lt;/p&gt;
&lt;p&gt;Just have a look at the following traces and compare how much &lt;code&gt;initCommon()&lt;/code&gt; takes with immediate (&amp;quot;&lt;em&gt;This trace&lt;/em&gt;&amp;quot;) versus deferred (&amp;quot;&lt;em&gt;External trace&lt;/em&gt;&amp;quot;) queries.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;&lt;img
class="my-0 rounded-md"
loading="lazy"
decoding="async"
fetchpriority="low"
alt="Tracy_deferShaderQueries"
src="/images/Tracy_deferShaderQueries.png"
&gt;&lt;figcaption&gt;Tracy - deferShaderQueries&lt;/figcaption&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;I then consulted a friend about ANGLE and he concluded that the long arrays I used for collecting batching instances information were the cause, and that the Direct3D HLSL compiler is well-known for having a hard time with them.
After redeclaring those as single element arrays the compilation speed was reasonable again.&lt;/p&gt;
&lt;p&gt;If the shader declares a single element array then it should only be able to access that element, even if you bind a bigger buffer, right? According to my tests that&amp;rsquo;s not true and every GPU on every OS out there will perform an out of bounds access and render every instance.
Well, every combination except Catalyst drivers on Windows, therefore restricting these shaders modifications to Emscripten only.&lt;/p&gt;
&lt;p&gt;But batching was broken no matter what, with or without single element arrays there was always some instance not being drawn.
I suspected an issue with &lt;em&gt;Uniform Buffer Objects&lt;/em&gt; alignment as &lt;code&gt;apptest_camera&lt;/code&gt; with culling enabled rendered flickering sprites, very similarly to what happens when alignment is incorrect.
I tried for days with different alignments and buffer sizes but I didn&amp;rsquo;t have any luck and I decided to disable the feature on Emscripten for the time being. &amp;#x1f622;&lt;/p&gt;
&lt;p&gt;When I was lucky rendering fixes came from extending Android OpenGL ES specific &lt;code&gt;#define&lt;/code&gt; conditions to include Emscripten, like going from &lt;code&gt;#ifndef __ANDROID__&lt;/code&gt; to &lt;code&gt;#if !defined(__ANDROID__) &amp;amp;&amp;amp; !defined(__EMSCRIPTEN__)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Buffer mapping might have been another big issue as it is not supported on WebGL 2. Fortunately it gets emulated when you pass &lt;code&gt;-s FULL_ES3=1&lt;/code&gt; to &lt;em&gt;emcc&lt;/em&gt; and it&amp;rsquo;s an optional engine feature that can be disabled.
This way I got the code to compile with no modifications while avoiding a possibly slower emulated path.&lt;/p&gt;
&lt;h4 class="relative group"&gt;Input backends
&lt;div id="input-backends" class="anchor"&gt;&lt;/div&gt;
&lt;/h4&gt;
&lt;p&gt;Rendering was not the only area in need for a change. On SDL2 auto-suspension on focus lost just hangs the application indefinitely. That is caused by &lt;code&gt;SDL_WaitEvent()&lt;/code&gt; and it appears the only fix is not to call this function.&lt;/p&gt;
&lt;p&gt;Emscripten also doesn&amp;rsquo;t seem to play well with GLFW windows at the moment, it needs a little help in the form of a &lt;code&gt;glfwWindowHint(GLFW_FOCUSED, 1)&lt;/code&gt; when you open one.
I also had to exclude some GLFW 3.3 code I added recently, but nothing that couldn&amp;rsquo;t be fixed by a &lt;code&gt;#if GLFW_VERSION_MAJOR == 3 &amp;amp;&amp;amp; GLFW_VERSION_MINOR &amp;gt;= 3&lt;/code&gt;. &amp;#x1f609;&lt;/p&gt;
&lt;p&gt;At least joystick mapping worked without much hassle, I just had to return the special &amp;ldquo;&lt;code&gt;default&lt;/code&gt;&amp;rdquo; GUID for every connected joystick in order to match the Emscripten entry in the SDL2 game controller database.&lt;/p&gt;
&lt;h4 class="relative group"&gt;Additional changes
&lt;div id="additional-changes" class="anchor"&gt;&lt;/div&gt;
&lt;/h4&gt;
&lt;p&gt;After guarding &lt;a href="https://github.com/google/googletest/blob/master/googletest/docs/advanced.md#death-tests" target="_blank" rel="noreferrer"&gt;death tests&lt;/a&gt; with &lt;code&gt;#ifndef __EMSCRIPTEN__&lt;/code&gt;, unit tests were also good to go. Thanks to the Emscripten CMake platform script, running &lt;code&gt;ctest&lt;/code&gt; invokes &lt;code&gt;node&lt;/code&gt; as the &lt;code&gt;CMAKE_CROSSCOMPILING_EMULATOR&lt;/code&gt; allowing unit tests to run from the console. &amp;#x1f4aa;&lt;/p&gt;
&lt;p&gt;Apptests were also mostly working, with the exception of &lt;code&gt;apptest_simdbench&lt;/code&gt;. I had to refactor it in order to use a lot less memory and allocate it from the heap.&lt;/p&gt;
&lt;p&gt;As you might have already &lt;a href="https://emscripten.org/docs/porting/files/file_systems_overview.html" target="_blank" rel="noreferrer"&gt;read&lt;/a&gt; on Emscripten website, accessing the file system is different too.
The C API is emulated, and that&amp;rsquo;s very useful, but you still have to provide data to your web application in a special way.&lt;/p&gt;
&lt;p&gt;I decided to go with &lt;em&gt;preloading&lt;/em&gt; instead of &lt;em&gt;embedding&lt;/em&gt; to always have a separation between data and code.
For apptests I directly call the &lt;a href="https://emscripten.org/docs/porting/files/packaging_files.html#packaging-using-the-file-packager-tool" target="_blank" rel="noreferrer"&gt;file packager tool&lt;/a&gt; within CMake, I generate the data once and share the file between them.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;nCine-libraries&lt;/code&gt; project also gained Emscripten support and it can now compile Lua and WebP for the new platform.&lt;/p&gt;
&lt;h3 class="relative group"&gt;OpenAL fixes
&lt;div id="openal-fixes" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;While working on the port I had some people try my &lt;a href="https://ncine.github.io/web-tests/" target="_blank" rel="noreferrer"&gt;web tests&lt;/a&gt; and a tester reported a strange issue with music and sound effects in &lt;code&gt;apptest_audio&lt;/code&gt;.
In the beginning I thought it was something related with the new platform but the issue could be found on all of them. &amp;#x1f631;&lt;/p&gt;
&lt;p&gt;The OpenAL implementation was probably one of the oldest pieces of code, it had not been touched in a very long time and it showed. &amp;#x1f605;&lt;/p&gt;
&lt;p&gt;First of all the source id recycling was pretty much wrong and paused or stopped players never relinquished their ids. Also, they could never transition from a paused to a stopped state. &amp;#x1f629;&lt;/p&gt;
&lt;p&gt;Even if the number of OpenAL sources is fixed and there is only one source per active player and vice versa, I was managing currently playing players with a list.
The optimization was as easy as to swap &lt;code&gt;nctl::List&amp;lt;IAudioPlayer *&amp;gt; players_&lt;/code&gt; with &lt;code&gt;nctl::StaticArray&amp;lt;IAudioPlayer *, MaxSources&amp;gt; players_&lt;/code&gt; and change some accessing code. &amp;#x1f609;&lt;/p&gt;
&lt;p&gt;While I was there I took some time to sprinkle a couple of &lt;code&gt;alGetError()&lt;/code&gt; calls around source and buffer generations, just as suggested by the &lt;a href="https://www.openal.org/documentation/OpenAL_Programmers_Guide.pdf" target="_blank" rel="noreferrer"&gt;OpenAL Programmer&amp;rsquo;s Guide&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I have also added querying methods for buffers, streams and players that allowed for the creation of a new section in the ImGui debug overlay, one showing information about active audio players.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;&lt;img
class="my-0 rounded-md"
loading="lazy"
decoding="async"
fetchpriority="low"
alt="ImGui debug overlay - AudioPlayers"
src="/images/ImGui_AudioPlayers.png"
&gt;&lt;figcaption&gt;ImGui debug overlay - AudioPlayers&lt;/figcaption&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 class="relative group"&gt;Monitor video modes
&lt;div id="monitor-video-modes" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;Last but not least, there is now a way for games to query and change monitor video modes on PC.
The implementation uses functions like &lt;code&gt;glfwGetVideoMode()&lt;/code&gt; and &lt;code&gt;glfwSetWindowMonitor()&lt;/code&gt; on GLFW and &lt;code&gt;SDL_GetDisplayMode()&lt;/code&gt; and &lt;code&gt;SDL_SetWindowDisplayMode()&lt;/code&gt; on SDL.&lt;/p&gt;
&lt;p&gt;The ImGui debug overlay has been updated to take advantage of this new feature. The user can select a full screen video mode from a drop-down list of supported resolutions and refresh rates combinations.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;&lt;img
class="my-0 rounded-md"
loading="lazy"
decoding="async"
fetchpriority="low"
alt="ImGui debug overlay - WindowSettings"
src="/images/ImGui_WindowSettings.png"
&gt;&lt;figcaption&gt;ImGui debug overlay - WindowSettings&lt;/figcaption&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s all for now and I hope you are excited about porting your nCine projects to the web. &amp;#x1f578;&amp;#xfe0f;&lt;/p&gt;</description></item><item><title>nCine Dev Update 10</title><link>https://encelo.github.io/2019-07-03-ncine-dev-update-10/</link><pubDate>Wed, 03 Jul 2019 00:00:00 +0000</pubDate><guid>https://encelo.github.io/2019-07-03-ncine-dev-update-10/</guid><description>&lt;p&gt;I&amp;rsquo;m sure many of you have heard it already: the nCine source code has been released on &lt;a href="https://github.com/nCine" target="_blank" rel="noreferrer"&gt;GitHub&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;This means that lately most of the time was dedicated to publication related tasks, for example updates to the site like the addition of a &amp;ldquo;&lt;a href="https://ncine.github.io/why/" target="_blank" rel="noreferrer"&gt;why nCine?&lt;/a&gt;&amp;rdquo; page and a &lt;a href="https://ncine.github.io/gallery/" target="_blank" rel="noreferrer"&gt;gallery&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;But definitely one of the most complex task has been continuous integration, which has a new &lt;a href="https://ncine.github.io/ci/" target="_blank" rel="noreferrer"&gt;page&lt;/a&gt; on the site too. &amp;#x1f609;&lt;/p&gt;
&lt;h3 class="relative group"&gt;Continuous Integration
&lt;div id="continuous-integration" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;A couple of years ago I began experimenting with &lt;a href="https://travis-ci.org/" target="_blank" rel="noreferrer"&gt;Travis&lt;/a&gt; and &lt;a href="https://www.appveyor.com/" target="_blank" rel="noreferrer"&gt;AppVeyor&lt;/a&gt; and managed to have them build the libraries, the engine and ncPong for all supported platforms: Linux, Windows (both MSVC and MinGW), macOS and Android.&lt;/p&gt;
&lt;p&gt;In an effort to simplify this setup I decided to migrate everything to &lt;a href="https://azure.microsoft.com/en-us/services/devops/pipelines/" target="_blank" rel="noreferrer"&gt;Azure Pipelines&lt;/a&gt;.
It supports all major desktop systems meaning I now need only one script to support everything. It also features a more flexible YAML language that organizes the whole process as a sequence of jobs and steps with conditions, as opposed to specific steps like &lt;code&gt;after_test&lt;/code&gt; or &lt;code&gt;before_install&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Now that the source code is available online, let&amp;rsquo;s have a look at those scripts.&lt;/p&gt;
&lt;h4 class="relative group"&gt;Building the libraries
&lt;div id="building-the-libraries" class="anchor"&gt;&lt;/div&gt;
&lt;/h4&gt;
&lt;p&gt;First thing to do is to build the dependencies, both for &lt;a href="https://github.com/nCine/nCine-libraries/blob/master/azure-pipelines.yml" target="_blank" rel="noreferrer"&gt;desktop&lt;/a&gt; and for &lt;a href="https://github.com/nCine/nCine-android-libraries/blob/master/azure-pipelines.yml" target="_blank" rel="noreferrer"&gt;Android&lt;/a&gt;.
As you can notice libraries are built on Linux with both GCC and clang and on Windows with both VS2019 and VS2017. On Android libraries are built for the three supported architectures but only on Linux.&lt;/p&gt;
&lt;p&gt;Next is when things become interesting, an idea that I kept from my original tests in 2017. In order to have artifacts available regardless of the C.I. platform and available for users to download I created some repositories on GitHub with the sole purpose of storing them.&lt;/p&gt;
&lt;p&gt;For libraries the repository is named &lt;a href="https://github.com/nCine/nCine-libraries-artifacts" target="_blank" rel="noreferrer"&gt;nCine-libraries-artifacts&lt;/a&gt; and is made of multiple &lt;a href="https://github.com/nCine/nCine-libraries-artifacts/branches/all" target="_blank" rel="noreferrer"&gt;branches&lt;/a&gt;, each one for a specific platform.&lt;/p&gt;
&lt;h4 class="relative group"&gt;Building the engine
&lt;div id="building-the-engine" class="anchor"&gt;&lt;/div&gt;
&lt;/h4&gt;
&lt;p&gt;Next is the engine itself, it pulls the library artifacts and engine data and push installers and portable archives.
The &lt;a href="https://github.com/nCine/nCine/blob/master/azure-pipelines.yml" target="_blank" rel="noreferrer"&gt;script&lt;/a&gt; gets more complicated by a test matrix that now includes build types, like &lt;em&gt;Debug&lt;/em&gt; and &lt;em&gt;Release&lt;/em&gt;, together with the &lt;em&gt;DevDist&lt;/em&gt; preset used for installers.&lt;/p&gt;
&lt;p&gt;The first steps prepare the environment, they download &lt;a href="http://www.doxygen.nl/" target="_blank" rel="noreferrer"&gt;Doxygen&lt;/a&gt; and &lt;a href="https://www.graphviz.org/" target="_blank" rel="noreferrer"&gt;Graphviz&lt;/a&gt; to build the documentation and the NDK to compile for Android.
On Windows and Linux they also download the RenderDoc API header because CMake is invoked with &lt;code&gt;-D NCINE_WITH_RENDERDOC=ON&lt;/code&gt;.
Additional steps will build unit tests and run them through &lt;code&gt;CTest&lt;/code&gt;. Benchmarks will also be built but not executed.&lt;/p&gt;
&lt;p&gt;At the end packages are created via the &lt;code&gt;package&lt;/code&gt; CMake target and files pushed in the &lt;a href="https://github.com/nCine/nCine-artifacts" target="_blank" rel="noreferrer"&gt;nCine-artifacts&lt;/a&gt; repository. It will again have multiple &lt;a href="https://github.com/nCine/nCine-artifacts/branches/all" target="_blank" rel="noreferrer"&gt;branches&lt;/a&gt; depending on platforms but this time they will also also depend on engine source branches.&lt;/p&gt;
&lt;h4 class="relative group"&gt;Building the projects
&lt;div id="building-the-projects" class="anchor"&gt;&lt;/div&gt;
&lt;/h4&gt;
&lt;p&gt;Now that the engine has been built and its artifacts pushed on GitHub it is the turn of the accompanying projects.
Both the &lt;a href="https://github.com/nCine/ncPong/blob/master/azure-pipelines.yml" target="_blank" rel="noreferrer"&gt;pong&lt;/a&gt; example game and the &lt;a href="https://github.com/nCine/ncParticleEditor/blob/master/azure-pipelines.yml" target="_blank" rel="noreferrer"&gt;particle editor&lt;/a&gt; have very similar C.I. scripts.&lt;/p&gt;
&lt;p&gt;They download the libraries, the engine and the project data and they build for all supported platforms, including Android APKs using Gradle.
They push artifacts in the &lt;a href="https://github.com/nCine/ncPong-artifacts" target="_blank" rel="noreferrer"&gt;ncPong-artifacts&lt;/a&gt; and &lt;a href="https://github.com/nCine/ncParticleEditor-artifacts" target="_blank" rel="noreferrer"&gt;ncParticleEditor-artifacts&lt;/a&gt; GitHub repositories.&lt;/p&gt;
&lt;h4 class="relative group"&gt;Some notes
&lt;div id="some-notes" class="anchor"&gt;&lt;/div&gt;
&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;On Linux CMake is not very recent and a special step has to download and install an updated version from the official site.&lt;/li&gt;
&lt;li&gt;On Windows all steps use &lt;a href="https://Microsoft.com/PowerShell" target="_blank" rel="noreferrer"&gt;PowerShell&lt;/a&gt;. Life is too short to mess with the Command Prompt. &amp;#x1f605;&lt;/li&gt;
&lt;li&gt;Often enough git commands write their output to &lt;code&gt;stderr&lt;/code&gt; and make scripts fail. That&amp;rsquo;s the reason for &lt;code&gt;$env:GIT_REDIRECT_STDERR = '2&amp;gt;&amp;amp;1'&lt;/code&gt; in PowerShell steps.&lt;/li&gt;
&lt;li&gt;On PowerShell &lt;code&gt;curl&lt;/code&gt; is an alias to an internal &lt;code&gt;Invoke-WebRequest&lt;/code&gt; command which does not understand all curl options. The solution is &lt;code&gt;Remove-item alias:curl&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Sometimes there is a need for git commands to invoke a fallback when they fail, it&amp;rsquo;s achieved this way:
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;git fetch --unshallow || true&lt;/code&gt; on Bash&lt;/li&gt;
&lt;li&gt;&lt;code&gt;git fetch --unshallow; if (-not $?) { return }&lt;/code&gt; on PowerShell&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;MSYS steps always set the &lt;code&gt;CHERE_INVOKING&lt;/code&gt; environment variable for Bash to use the current working directory.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 class="relative group"&gt;More changes
&lt;div id="more-changes" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;Continuos integration has been a good way to spot some tricky issues occurring only with specific combinations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Tracy memory profiling not working on macOS&lt;/li&gt;
&lt;li&gt;Wrong atomics version used on MinGW&lt;/li&gt;
&lt;li&gt;Unit tests and benchmarks not compiling on MSVC&lt;/li&gt;
&lt;li&gt;Wrong version of OpenGL headers included on macOS&lt;/li&gt;
&lt;li&gt;Static library support breaking in some situations&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Fortunately in the end it wasn&amp;rsquo;t all about C.I. and I had some time for a small new feature: a configurable frame limiter.
It might come in handy especially on mobile to limit FPS below VSync. &amp;#x1f4aa;&lt;/p&gt;</description></item><item><title>nCine Dev Update 9</title><link>https://encelo.github.io/2019-05-14-ncine-dev-update-9/</link><pubDate>Tue, 14 May 2019 00:00:00 +0000</pubDate><guid>https://encelo.github.io/2019-05-14-ncine-dev-update-9/</guid><description>&lt;p&gt;It has been a month and a half of small but useful updates for the nCine.&lt;/p&gt;
&lt;h3 class="relative group"&gt;LibPNG
&lt;div id="libpng" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;The PNG image loader has been modified to support more color types, by copying some code from the libpng &lt;a href="https://sourceforge.net/p/libpng/code/ci/master/tree/example.c" target="_blank" rel="noreferrer"&gt;example&lt;/a&gt;. It means that any nCine game is now able to properly load PNG images with palette or with gray-alpha channels and to expand or strip bit depths that are different than the standard 8 bits.&lt;/p&gt;
&lt;h3 class="relative group"&gt;Lua fixes
&lt;div id="lua-fixes" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;I have also sucessfully converted the ncPong test game to a Lua script and fixed a lot of Lua bindings bugs in the making. &amp;#x1f41b;
Things like missing methods or constants that were never exported to the &lt;a href="https://ncine.github.io/lua_api.html" target="_blank" rel="noreferrer"&gt;Lua API&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;There are also some Lua utils additions and it is now possible for games to set and retrieve fields directly or to set and retrieve globals.&lt;/p&gt;
&lt;h3 class="relative group"&gt;Headers
&lt;div id="headers" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;Another small change that improves the quality of life of a game developer using the nCine is the moving of headers inside an &lt;code&gt;ncine&lt;/code&gt; subdirectory.
It is now very clear when you are using your game headers or the engine ones, as you would now see something in the likes of:&lt;/p&gt;
&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#include&lt;/span&gt; &lt;span style="color:#75715e"&gt;&amp;lt;nctl/Array.h&amp;gt;&lt;/span&gt;&lt;span style="color:#75715e"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#include&lt;/span&gt; &lt;span style="color:#75715e"&gt;&amp;lt;ncine/Sprite.h&amp;gt;&lt;/span&gt;&lt;span style="color:#75715e"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;#include&lt;/span&gt; &lt;span style="color:#75715e"&gt;&amp;#34;mygame.h&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Using the angle brackets makes it also easier to spot them, and they made their way into the included examples as well.&lt;/p&gt;
&lt;h3 class="relative group"&gt;Symbol stripping
&lt;div id="symbol-stripping" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;Next is symbol stripping from binaries, a way to reduce the size of Android libraries on all supported platforms. It is also a way to strip the base libraries on ArchLinux as the &lt;code&gt;makepkg&lt;/code&gt; stripping &lt;a href="https://www.archlinux.org/pacman/makepkg.conf.5.html" target="_blank" rel="noreferrer"&gt;option&lt;/a&gt; is not &lt;a href="https://bugs.archlinux.org/task/42848" target="_blank" rel="noreferrer"&gt;cross-compiler aware&lt;/a&gt; and it ends up stripping Android ARM libraries with the x86_64 &lt;code&gt;strip&lt;/code&gt; command, ruining their content. &amp;#x1f605;&lt;/p&gt;
&lt;h3 class="relative group"&gt;Uniforms update
&lt;div id="uniforms-update" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;I have decided to finally spend some time identifying and squashing a long standing bug introduced with the latest renderer revamp that would cause a one frame delay when committing uniform variables. The result was very noticeable when disabling, moving and later re-enabling drawable nodes: they would be rendered in the old position for only one frame causing a very annoying graphical glitch.&lt;/p&gt;
&lt;p&gt;At the same time I also fixed another small graphics bug that would treat 3 channels textures as gray and render them with the wrong shader.
It was evident in the texture formats example test, as it would render RGB textures in gray shades. &amp;#x1f61e;&lt;/p&gt;
&lt;h3 class="relative group"&gt;Joystick hats
&lt;div id="joystick-hats" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;The next change is all dedicated to joystick hats, the work was in stand-by as I was waiting for the release of &lt;a href="https://github.com/glfw/glfw/releases/tag/3.3" target="_blank" rel="noreferrer"&gt;GLFW 3.3&lt;/a&gt;. Now hats are completely separated from buttons (on GLFW with &lt;code&gt;glfwInitHint(GLFW_JOYSTICK_HAT_BUTTONS, GLFW_FALSE)&lt;/code&gt;) and queried on their own on all three backends: GLFW, SDL2 and Android. When talking about joysticks one should expect the usual magic involving bit masks when working on Android, in this case to make both &lt;code&gt;AINPUT_EVENT_TYPE_KEY&lt;/code&gt; and &lt;code&gt;AINPUT_EVENT_TYPE_MOTION&lt;/code&gt; events update the hat state, as some joystick report one or the other type of event (see also the official Android developer article about &lt;a href="https://developer.android.com/training/game-controllers/controller-input#dpad" target="_blank" rel="noreferrer"&gt;processing directional pad input&lt;/a&gt;). &amp;#x1f604;&lt;/p&gt;
&lt;p&gt;This change made it also possible to add the directional pad as a set of buttons when using the gamepad mapping, finally bringing it on par with the &lt;a href="https://wiki.libsdl.org/CategoryGameController" target="_blank" rel="noreferrer"&gt;original functionality&lt;/a&gt; in SDL2. &amp;#x1f4aa;&lt;/p&gt;
&lt;h3 class="relative group"&gt;Application suspension
&lt;div id="application-suspension" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;After some feedback by one tester I decided to rename the application pause related methods to &lt;em&gt;suspension&lt;/em&gt;, a word that makes it clear that in-game pause has nothing to do with global event suspension at the application level. &amp;#x263a;&amp;#xfe0f;&lt;/p&gt;
&lt;p&gt;As a bonus I have also introduced a flag that the user can set to disable the automatic suspension that happens when the focus is lost.&lt;/p&gt;
&lt;h3 class="relative group"&gt;Application configuration
&lt;div id="application-configuration" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;Another small quality of life change is the removal of all the getter and setter methods from the application configuration class.
Now the user can just set a value in the configuration structure, similarly to what was already happening when changing the configuration in Lua.&lt;/p&gt;
&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;config.withVSync &lt;span style="color:#f92672"&gt;=&lt;/span&gt; false; &lt;span style="color:#75715e"&gt;// instead of config.enableVSync(false);
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;config.windowTitle &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#e6db74"&gt;&amp;#34;My Game&amp;#34;&lt;/span&gt;; &lt;span style="color:#75715e"&gt;// instead of config.setWindowTitle(&amp;#34;My Game&amp;#34;);
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3 class="relative group"&gt;clang-format
&lt;div id="clang-format" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;I finally decided to commit myself to a code beautifier and stick with it. After years of playing with &lt;a href="http://astyle.sourceforge.net/" target="_blank" rel="noreferrer"&gt;Artistic Style&lt;/a&gt; and &lt;a href="http://uncrustify.sourceforge.net/" target="_blank" rel="noreferrer"&gt;Uncrustify&lt;/a&gt; without ever fully liking the results, I decided to put together a &lt;code&gt;.clang-format&lt;/code&gt; configuration file.&lt;/p&gt;
&lt;p&gt;Maybe I&amp;rsquo;m getting old and tired or maybe I&amp;rsquo;m just less scared by the stylistic compromises but I should admit I&amp;rsquo;m pretty satisfied with &lt;a href="https://clang.llvm.org/docs/ClangFormat.html" target="_blank" rel="noreferrer"&gt;it&lt;/a&gt;. The only exception being the &lt;code&gt;IndentPPDirectives&lt;/code&gt; option: I would really like to be able to use &lt;code&gt;BeforeHash&lt;/code&gt; instead of manually edit the results given by &lt;code&gt;AfterHash&lt;/code&gt;, but it seems I will have to wait for the &lt;a href="https://reviews.llvm.org/D52150" target="_blank" rel="noreferrer"&gt;Clang 9&lt;/a&gt; release. &amp;#x1f604;&lt;/p&gt;
&lt;h3 class="relative group"&gt;nCTL containers
&lt;div id="nctl-containers" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;The nCine Template Library has been given some care too, with the addition of some containers and many new unit tests (now more than 1000! &amp;#x1f631;) and microbenchmarks.
Most containers have seen the addition of an emplace method that uses &lt;a href="https://en.cppreference.com/w/cpp/language/new#Placement_new" target="_blank" rel="noreferrer"&gt;placement new&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I have also added set containers, some are based on the hashmap ones stripped of the value associated to the key but there is also a new &lt;a href="https://www.geeksforgeeks.org/sparse-set/" target="_blank" rel="noreferrer"&gt;sparse set&lt;/a&gt; container that should be very fast when storing numbers and when the &lt;a href="https://en.wikipedia.org/wiki/Space%E2%80%93time_tradeoff" target="_blank" rel="noreferrer"&gt;space-time tradeoff&lt;/a&gt; is advantageous.&lt;/p&gt;
&lt;h3 class="relative group"&gt;RenderDoc integration
&lt;div id="renderdoc-integration" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;Next we are back with graphics, with the &lt;a href="https://renderdoc.org/docs/in_application_api.html" target="_blank" rel="noreferrer"&gt;in-application&lt;/a&gt; integration of RenderDoc. This means that the user can now &lt;a href="https://renderdoc.org/docs/in_application_api.html#_CPPv214TriggerCapturev" target="_blank" rel="noreferrer"&gt;trigger&lt;/a&gt; a capture, &lt;a href="https://renderdoc.org/docs/in_application_api.html#_CPPv214LaunchReplayUI8uint32_tPKc" target="_blank" rel="noreferrer"&gt;launch&lt;/a&gt; the Qt interface when running under &lt;code&gt;renderdoccmd&lt;/code&gt;, disable the &lt;a href="https://renderdoc.org/docs/in_application_api.html#_CPPv215MaskOverlayBits8uint32_t8uint32_t" target="_blank" rel="noreferrer"&gt;overlay&lt;/a&gt;, provide &lt;a href="https://renderdoc.org/docs/in_application_api.html#_CPPv222SetCaptureFileCommentsPKcPKc" target="_blank" rel="noreferrer"&gt;comments&lt;/a&gt; for a capture, set the &lt;a href="https://renderdoc.org/docs/in_application_api.html#_CPPv226SetCaptureFilePathTemplatePKc" target="_blank" rel="noreferrer"&gt;file path template&lt;/a&gt; for saving and more.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;&lt;img
class="my-0 rounded-md"
loading="lazy"
decoding="async"
fetchpriority="low"
alt="RenderDoc in-application integration"
src="/images/RenderDoc_integration.png"
&gt;&lt;figcaption&gt;RenderDoc in-application integration&lt;/figcaption&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 class="relative group"&gt;Buffer mapping
&lt;div id="buffer-mapping" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;The last change I&amp;rsquo;m going to cover in this update is another long standing graphics issue that seems to only manifest itself on Windows 10 and Intel GPUs. There are some reproducible cases when the driver would end up displaying the first frame and doing nothing more, while the application continues to run and process events. The error does not occur when running the tests under &lt;a href="http://apitrace.github.io/" target="_blank" rel="noreferrer"&gt;apitrace&lt;/a&gt;, &lt;a href="https://renderdoc.org/" target="_blank" rel="noreferrer"&gt;RenderDoc&lt;/a&gt; or &lt;a href="https://software.intel.com/en-us/gpa" target="_blank" rel="noreferrer"&gt;Intel GPA&lt;/a&gt;, making it harder to examine.&lt;/p&gt;
&lt;p&gt;I have discovered that the problem is related to mapping and unmapping of &lt;a href="https://www.khronos.org/opengl/wiki/Uniform_Buffer_Object" target="_blank" rel="noreferrer"&gt;uniform buffer objects&lt;/a&gt; and my solution so far has been to disable the feature altogether while providing an option for the user to enable it again. &amp;#x1f629;
When mapping is not available the renderer falls back to using a single per-frame call to &lt;code&gt;glBufferSubData()&lt;/code&gt; followed by buffer orphaning, resulting in comparable performances in many situations.&lt;/p&gt;
&lt;p&gt;And that&amp;rsquo;s the end of another pretty long and detailed update, I hope you enjoyed it! &amp;#x1f609;&lt;/p&gt;</description></item><item><title>nCine Dev Update 8</title><link>https://encelo.github.io/2019-04-01-ncine-dev-update-8/</link><pubDate>Mon, 01 Apr 2019 00:00:00 +0000</pubDate><guid>https://encelo.github.io/2019-04-01-ncine-dev-update-8/</guid><description>&lt;p&gt;I have spent some very intense weeks this March to completely overhaul and refactor my CMake scripts. We are talking about more than two thousand lines of code! &amp;#x1f631;&lt;/p&gt;
&lt;h3 class="relative group"&gt;Android
&lt;div id="android" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;I started by changing the way I cross-compile on Android. I dropped the use of CMake &lt;a href="https://cmake.org/cmake/help/v3.7/manual/cmake-toolchains.7.html#cross-compiling-for-android-with-the-ndk" target="_blank" rel="noreferrer"&gt;integrated NDK support&lt;/a&gt; introduced with version 3.7 and went back using the &lt;a href="https://cmake.org/cmake/help/latest/manual/cmake-toolchains.7.html#cross-compiling" target="_blank" rel="noreferrer"&gt;toolchain file&lt;/a&gt; provided by the NDK itself. It seems &lt;a href="https://github.com/android-ndk/ndk/issues/463" target="_blank" rel="noreferrer"&gt;the way to go&lt;/a&gt; in order to provide full control and flexibility to each NDK update.&lt;/p&gt;
&lt;p&gt;I have also used this opportunity to add support for NDK r19 and its embedded &lt;a href="https://developer.android.com/ndk/guides/standalone_toolchain" target="_blank" rel="noreferrer"&gt;stand-alone toolchain&lt;/a&gt;, the new &lt;a href="https://developer.android.com/studio/releases/gradle-plugin#3-0-0" target="_blank" rel="noreferrer"&gt;3.x Gradle plugin&lt;/a&gt; and LLVM&amp;rsquo;s &lt;a href="https://developer.android.com/ndk/guides/cpp-support#libc" target="_blank" rel="noreferrer"&gt;&lt;code&gt;libc++_shared&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;h3 class="relative group"&gt;Target properties and expressions
&lt;div id="target-properties-and-expressions" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;I then started to broaden the use of more target related CMake commands, as &lt;a href="https://cmake.org/cmake/help/latest/command/target_sources.html" target="_blank" rel="noreferrer"&gt;&lt;code&gt;target_sources&lt;/code&gt;&lt;/a&gt; or &lt;a href="https://cmake.org/cmake/help/latest/command/target_link_options.html" target="_blank" rel="noreferrer"&gt;&lt;code&gt;target_link_options&lt;/code&gt;&lt;/a&gt;. This last command raised the minimum CMake version to &lt;a href="https://cmake.org/cmake/help/v3.13/release/3.13.html" target="_blank" rel="noreferrer"&gt;3.13&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Taking full advantage of CMake targets allowed me to completely remove any &lt;code&gt;set&lt;/code&gt; command involving variables like &lt;code&gt;CMAKE_CXX_FLAGS&lt;/code&gt; or &lt;code&gt;CMAKE_SHARED_LINKER_FLAGS&lt;/code&gt;.
Besides that I also started to rely more deeply on &lt;a href="https://cmake.org/cmake/help/latest/manual/cmake-generator-expressions.7.html" target="_blank" rel="noreferrer"&gt;generator expressions&lt;/a&gt; for things like &lt;code&gt;$&amp;lt;TARGET_FILE:tgt&amp;gt;&lt;/code&gt;, &lt;code&gt;$&amp;lt;BUILD_INTERFACE:...&amp;gt;&lt;/code&gt; or &lt;code&gt;$&amp;lt;CONFIG:cfg&amp;gt;&lt;/code&gt;. This last expression got rid of every &lt;code&gt;if(CMAKE_BUILD_TYPE MATCHES &amp;quot;Debug&amp;quot;)&lt;/code&gt; conditional check. &amp;#x1f60e;&lt;/p&gt;
&lt;h3 class="relative group"&gt;Imported targets
&lt;div id="imported-targets" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;But I didn&amp;rsquo;t stop there and I went on to convert my scripts to use targets everywhere. I now use &lt;a href="https://cmake.org/cmake/help/latest/manual/cmake-buildsystem.7.html#imported-targets" target="_blank" rel="noreferrer"&gt;imported targets&lt;/a&gt; for every external dependency library.
In the end it&amp;rsquo;s a better and more flexible way of handling the problem but it put a lot of stress on testing as not all &lt;code&gt;find_package&lt;/code&gt; scripts correctly set targets and some platforms need additional tweaking too.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;For example MSYS/MinGW needed a custom CMake function to find the DLL and set the &lt;code&gt;IMPORTED_LOCATION&lt;/code&gt; target property as the corresponding &lt;code&gt;find_package&lt;/code&gt; scripts only discovered the import library.&lt;/li&gt;
&lt;li&gt;As another example on macOS I needed a different custom CMake function to split the link options set by &lt;code&gt;find_package&lt;/code&gt; scripts into the ones representing framework directories and the ones being &lt;code&gt;-framework&lt;/code&gt; link directives.
There is also a need to append the library symlink name to the framework directory as that is what the &lt;code&gt;IMPORTED_LOCATION&lt;/code&gt; target property &lt;a href="https://cmake.org/cmake/help/v3.14/prop_tgt/IMPORTED_LOCATION.html" target="_blank" rel="noreferrer"&gt;expects&lt;/a&gt; when dealing with macOS frameworks (someone has even opened an &lt;a href="https://gitlab.kitware.com/cmake/cmake/issues/18753" target="_blank" rel="noreferrer"&gt;issue&lt;/a&gt; ticket about that).&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 class="relative group"&gt;Discoverability and exported targets
&lt;div id="discoverability-and-exported-targets" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;In an attempt to make nCine discoverability more robust for external projects I have changed a lot of code based on cascaded conditions to a bunch of a lot cleaner &lt;code&gt;find_path&lt;/code&gt; calls.&lt;/p&gt;
&lt;p&gt;I now also always use &lt;a href="https://gitlab.kitware.com/cmake/community/wikis/doc/tutorials/Exporting-and-Importing-Targets#exporting-targets" target="_blank" rel="noreferrer"&gt;exported targets&lt;/a&gt; and this has two very important benefits:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It is now possible to link to the &lt;code&gt;ncine&lt;/code&gt; target and automatically have access to interface properties like for example &lt;a href="https://cmake.org/cmake/help/latest/prop_tgt/INTERFACE_INCLUDE_DIRECTORIES.html" target="_blank" rel="noreferrer"&gt;&lt;code&gt;INTERFACE_INCLUDE_DIRECTORIES&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;By using the &lt;code&gt;nCine_DIR&lt;/code&gt; CMake variable when configuring a project it is possible to import the targets from a build directory.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The outcome of this last change is that there is no more &lt;code&gt;NCINE_HOME&lt;/code&gt; custom variable, all the discovery use cases are handled by the CMake way of doing things: using a &lt;code&gt;&amp;lt;package&amp;gt;_DIR&lt;/code&gt; variable that points to where the &lt;code&gt;nCineConfig.cmake&lt;/code&gt; script is. This will in turn include the &lt;code&gt;nCineTargets.cmake&lt;/code&gt; script to bring all the imported nCine targets into the namespace of an external package &lt;code&gt;CMakeLists.txt&lt;/code&gt; file.&lt;/p&gt;
&lt;h3 class="relative group"&gt;Bonus
&lt;div id="bonus" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;A couple of additional changes are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I now build a new &lt;code&gt;ncine_main&lt;/code&gt; static library and export it as a target, instead of distributing a &lt;code&gt;main.cpp&lt;/code&gt; file that the user never really had the need to modify.&lt;/li&gt;
&lt;li&gt;I have added an &lt;a href="https://cmake.org/cmake/help/v3.14/manual/cmake-buildsystem.7.html#interface-libraries" target="_blank" rel="noreferrer"&gt;interface library&lt;/a&gt; pseudo target for the code coverage compiler and linker options as a way, as the CMake manual states, &amp;ldquo;&lt;em&gt;to employ an entirely target-focussed design for usage requirements&lt;/em&gt;&amp;rdquo;. &amp;#x1f4aa;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That&amp;rsquo;s all for this update, I hope you enjoyed this in-depth review of CMake changes.&lt;/p&gt;</description></item><item><title>nCine Dev Update 7</title><link>https://encelo.github.io/2019-01-08-ncine-dev-update-7/</link><pubDate>Tue, 08 Jan 2019 00:00:00 +0000</pubDate><guid>https://encelo.github.io/2019-01-08-ncine-dev-update-7/</guid><description>&lt;p&gt;Just a few weeks after the last update here I&amp;rsquo;m again to write the next one in which I&amp;rsquo;m going to show you the performance of my new hash table implementation.&lt;/p&gt;
&lt;p&gt;It is based on the &lt;a href="https://preshing.com/20160314/leapfrog-probing/" target="_blank" rel="noreferrer"&gt;Leapfrog Probing&lt;/a&gt; article by Jeff Preshing, where the author explains a new probing algorithm for collision resolution when using &lt;a href="https://en.wikipedia.org/wiki/Open_addressing" target="_blank" rel="noreferrer"&gt;open addressing&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;My version is not multi-threaded but in return it is able to remove elements, a task that turned out to be quite more challenging than what I expected in the beginning. &amp;#x1f605;
I also took the opportunity to change the default hash function to &lt;a href="https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function#FNV-1a_hash" target="_blank" rel="noreferrer"&gt;FNV-1a&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;My previous implementation was based on &lt;a href="https://en.wikipedia.org/wiki/Hash_table#Separate_chaining_with_list_head_cells" target="_blank" rel="noreferrer"&gt;separate chaining with list head cells&lt;/a&gt;, a pretty common technique that allows for unlimited number of stored elements in the face of many potential cache misses when the load factor increases.&lt;/p&gt;
&lt;p&gt;The following results are a courtesy of the &lt;a href="https://github.com/google/benchmark" target="_blank" rel="noreferrer"&gt;Google Benchmark&lt;/a&gt; library, the latest third-party software integrated in the nCine.
They have been recorded on an &lt;a href="https://ark.intel.com/products/122589/Intel-Core-i7-8550U-Processor-8M-Cache-up-to-4-00-GHz-" target="_blank" rel="noreferrer"&gt;Intel i7-8550U&lt;/a&gt;, running Arch Linux with the &lt;code&gt;performance&lt;/code&gt; scaling governor and a fixed frequency of 800 MHz.&lt;/p&gt;
&lt;p&gt;The tests have been performed on different implementations, they all have in common an &lt;code&gt;unsigned int&lt;/code&gt; for the key and the value and 1024 buckets:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;StaticHashMap&lt;/code&gt; is the new open addressing hash map in a version that takes the number of buckets as a template argument, similarly to &lt;code&gt;std::array&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;HashMap&lt;/code&gt; is the new open addressing hash map in a more classic version that allocates on the heap&lt;/li&gt;
&lt;li&gt;&lt;code&gt;HashMapList&lt;/code&gt; is the old hash map with separate chaining&lt;/li&gt;
&lt;li&gt;&lt;code&gt;unordered_map&lt;/code&gt; is the standard hash table implementation from the STL&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Some tests have a number after their name representing either the number of elements already in the hash table, like for &lt;em&gt;Copy&lt;/em&gt;, &lt;em&gt;Retrieve&lt;/em&gt; or &lt;em&gt;Clear&lt;/em&gt;, or the number of times the operation is repeated, like for &lt;em&gt;Insert&lt;/em&gt;, &lt;em&gt;Remove&lt;/em&gt; or &lt;em&gt;ReverseRemove&lt;/em&gt;.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Benchmark&lt;/th&gt;
&lt;th style="text-align: right"&gt;StaticHashMap&lt;/th&gt;
&lt;th style="text-align: right"&gt;HashMap&lt;/th&gt;
&lt;th style="text-align: right"&gt;HashMapList&lt;/th&gt;
&lt;th style="text-align: right"&gt;unordered_map&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Creation&lt;/td&gt;
&lt;td style="text-align: right"&gt;443 ns&lt;/td&gt;
&lt;td style="text-align: right"&gt;34106 ns&lt;/td&gt;
&lt;td style="text-align: right"&gt;86589 ns&lt;/td&gt;
&lt;td style="text-align: right"&gt;13143 ns&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Copy/256&lt;/td&gt;
&lt;td style="text-align: right"&gt;2859 ns&lt;/td&gt;
&lt;td style="text-align: right"&gt;31501 ns&lt;/td&gt;
&lt;td style="text-align: right"&gt;78531 ns&lt;/td&gt;
&lt;td style="text-align: right"&gt;1656125 ns&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Copy/512&lt;/td&gt;
&lt;td style="text-align: right"&gt;2912 ns&lt;/td&gt;
&lt;td style="text-align: right"&gt;31004 ns&lt;/td&gt;
&lt;td style="text-align: right"&gt;745993 ns&lt;/td&gt;
&lt;td style="text-align: right"&gt;3600840 ns&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Copy/768&lt;/td&gt;
&lt;td style="text-align: right"&gt;2895 ns&lt;/td&gt;
&lt;td style="text-align: right"&gt;34312 ns&lt;/td&gt;
&lt;td style="text-align: right"&gt;1075128 ns&lt;/td&gt;
&lt;td style="text-align: right"&gt;4931709 ns&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Insert/256&lt;/td&gt;
&lt;td style="text-align: right"&gt;4725 ns&lt;/td&gt;
&lt;td style="text-align: right"&gt;9670 ns&lt;/td&gt;
&lt;td style="text-align: right"&gt;9313 ns&lt;/td&gt;
&lt;td style="text-align: right"&gt;1581495 ns&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Insert/512&lt;/td&gt;
&lt;td style="text-align: right"&gt;8423 ns&lt;/td&gt;
&lt;td style="text-align: right"&gt;19566 ns&lt;/td&gt;
&lt;td style="text-align: right"&gt;674695 ns&lt;/td&gt;
&lt;td style="text-align: right"&gt;3169524 ns&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Insert/768&lt;/td&gt;
&lt;td style="text-align: right"&gt;13896 ns&lt;/td&gt;
&lt;td style="text-align: right"&gt;33050 ns&lt;/td&gt;
&lt;td style="text-align: right"&gt;1007072 ns&lt;/td&gt;
&lt;td style="text-align: right"&gt;6504032 ns&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Retrieve/256&lt;/td&gt;
&lt;td style="text-align: right"&gt;66 ns&lt;/td&gt;
&lt;td style="text-align: right"&gt;67 ns&lt;/td&gt;
&lt;td style="text-align: right"&gt;75 ns&lt;/td&gt;
&lt;td style="text-align: right"&gt;72 ns&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Retrieve/512&lt;/td&gt;
&lt;td style="text-align: right"&gt;71 ns&lt;/td&gt;
&lt;td style="text-align: right"&gt;74 ns&lt;/td&gt;
&lt;td style="text-align: right"&gt;81 ns&lt;/td&gt;
&lt;td style="text-align: right"&gt;72 ns&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Retrieve/768&lt;/td&gt;
&lt;td style="text-align: right"&gt;76 ns&lt;/td&gt;
&lt;td style="text-align: right"&gt;77 ns&lt;/td&gt;
&lt;td style="text-align: right"&gt;83 ns&lt;/td&gt;
&lt;td style="text-align: right"&gt;73 ns&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Clear/256&lt;/td&gt;
&lt;td style="text-align: right"&gt;1733 ns&lt;/td&gt;
&lt;td style="text-align: right"&gt;22215 ns&lt;/td&gt;
&lt;td style="text-align: right"&gt;42527 ns&lt;/td&gt;
&lt;td style="text-align: right"&gt;1541229 ns&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Clear/512&lt;/td&gt;
&lt;td style="text-align: right"&gt;1733 ns&lt;/td&gt;
&lt;td style="text-align: right"&gt;21789 ns&lt;/td&gt;
&lt;td style="text-align: right"&gt;701556 ns&lt;/td&gt;
&lt;td style="text-align: right"&gt;3078237 ns&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Clear/768&lt;/td&gt;
&lt;td style="text-align: right"&gt;1734 ns&lt;/td&gt;
&lt;td style="text-align: right"&gt;22076 ns&lt;/td&gt;
&lt;td style="text-align: right"&gt;1025812 ns&lt;/td&gt;
&lt;td style="text-align: right"&gt;4609838 ns&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Remove/256&lt;/td&gt;
&lt;td style="text-align: right"&gt;5083 ns&lt;/td&gt;
&lt;td style="text-align: right"&gt;25895 ns&lt;/td&gt;
&lt;td style="text-align: right"&gt;35680 ns&lt;/td&gt;
&lt;td style="text-align: right"&gt;1565479 ns&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Remove/512&lt;/td&gt;
&lt;td style="text-align: right"&gt;9459 ns&lt;/td&gt;
&lt;td style="text-align: right"&gt;38793 ns&lt;/td&gt;
&lt;td style="text-align: right"&gt;703407 ns&lt;/td&gt;
&lt;td style="text-align: right"&gt;3136167 ns&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Remove/768&lt;/td&gt;
&lt;td style="text-align: right"&gt;14828 ns&lt;/td&gt;
&lt;td style="text-align: right"&gt;53607 ns&lt;/td&gt;
&lt;td style="text-align: right"&gt;1040247 ns&lt;/td&gt;
&lt;td style="text-align: right"&gt;4662149 ns&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ReverseRemove/256&lt;/td&gt;
&lt;td style="text-align: right"&gt;4906 ns&lt;/td&gt;
&lt;td style="text-align: right"&gt;25564 ns&lt;/td&gt;
&lt;td style="text-align: right"&gt;35849 ns&lt;/td&gt;
&lt;td style="text-align: right"&gt;1560456 ns&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ReverseRemove/512&lt;/td&gt;
&lt;td style="text-align: right"&gt;9620 ns&lt;/td&gt;
&lt;td style="text-align: right"&gt;38567 ns&lt;/td&gt;
&lt;td style="text-align: right"&gt;700163 ns&lt;/td&gt;
&lt;td style="text-align: right"&gt;3113046 ns&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ReverseRemove/768&lt;/td&gt;
&lt;td style="text-align: right"&gt;14527 ns&lt;/td&gt;
&lt;td style="text-align: right"&gt;51159 ns&lt;/td&gt;
&lt;td style="text-align: right"&gt;1031670 ns&lt;/td&gt;
&lt;td style="text-align: right"&gt;4669154 ns&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Last but not least, I have replaced all the occurrences of the classic &lt;code&gt;rand()&lt;/code&gt; function with a new &lt;a href="http://www.pcg-random.org/" target="_blank" rel="noreferrer"&gt;PCG32&lt;/a&gt; random number generator based on the official &lt;a href="http://www.pcg-random.org/using-pcg-c-basic.html" target="_blank" rel="noreferrer"&gt;minimal C implementation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;It has a higher statistical quality while also being a lot faster. In my microbenchmarks, with the same CPU settings as the hash map ones, it is able to generate 1024 integers in around 14203 ns, as opposed to 74303 ns when using simple &lt;code&gt;rand()&lt;/code&gt; calls.&lt;/p&gt;</description></item><item><title>nCine Dev Update 6</title><link>https://encelo.github.io/2018-12-16-ncine-dev-update-6/</link><pubDate>Sun, 16 Dec 2018 00:00:00 +0000</pubDate><guid>https://encelo.github.io/2018-12-16-ncine-dev-update-6/</guid><description>&lt;p&gt;After all the work carried out during the last months and culminated in the previous update, I took some time to experiment with some different things.
I wanted to leave the rendering side for a bit and take a look at how to optimize other parts of the engine.&lt;/p&gt;
&lt;p&gt;I got my hands dirty straightaway with low-level aspects by playing with SIMD intrinsics, both &lt;a href="https://software.intel.com/sites/landingpage/IntrinsicsGuide/" target="_blank" rel="noreferrer"&gt;Intel SSE&lt;/a&gt; and &lt;a href="https://developer.arm.com/technologies/neon/intrinsics" target="_blank" rel="noreferrer"&gt;ARM NEON&lt;/a&gt;.
The project involved quite a lot of different tasks, such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Looking online for example code (like &lt;a href="https://stackoverflow.com/questions/18542894/how-to-multiply-two-quaternions-with-minimal-instructions" target="_blank" rel="noreferrer"&gt;SSE Quaternion Multiplication&lt;/a&gt; or &lt;a href="https://lxjk.github.io/2017/09/03/Fast-4x4-Matrix-Inverse-with-SSE-SIMD-Explained.html" target="_blank" rel="noreferrer"&gt;SSE 4x4 Matrix Inversion&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Implementing functions for both SSE and NEON&lt;/li&gt;
&lt;li&gt;Unit testing my code for correctness and parity against non SIMD one&lt;/li&gt;
&lt;li&gt;Dealing with compilers details (&lt;a href="https://gcc.gnu.org/onlinedocs/gcc/Inline.html" target="_blank" rel="noreferrer"&gt;always_inline&lt;/a&gt;, &lt;a href="https://gcc.gnu.org/onlinedocs/gcc/Vector-Extensions.html" target="_blank" rel="noreferrer"&gt;Vector Extensions&lt;/a&gt;, &lt;a href="https://en.cppreference.com/w/cpp/language/alignas" target="_blank" rel="noreferrer"&gt;alignas&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Analyzing generated assembly on &lt;a href="https://godbolt.org/" target="_blank" rel="noreferrer"&gt;godbolt.org&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Measuring performance on both PC and Android&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;At the end the performances were too close to what the compiler alone could achieve with auto-vectorization and instead of pursuing more optimizations I put the task on hold.
It was still a fun and learning experience and it allows me to show you my SIMD benchmark utility. &amp;#x1f604;&lt;/p&gt;
&lt;p&gt;It is powered by ImGUI, it can load and save data from test runs using Lua and then compare them.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;&lt;img
class="my-0 rounded-md"
loading="lazy"
decoding="async"
fetchpriority="low"
alt="apptest_simdbench"
src="/images/apptest_simdbench.png"
&gt;&lt;figcaption&gt;apptest_simdbench&lt;/figcaption&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;After archiving the &lt;code&gt;simd&lt;/code&gt; branch on my repository I jumped on another big project: trying to come up with an ECS implementation for the nCine.
It would flatten the hierarchy of game elements and allow for a faster and possibly multi-threaded approach at updating them.&lt;/p&gt;
&lt;p&gt;I have already put together some early tests but before going on I took some days to have a look at some profiling tools.
Embedding custom timers and graphs into the engine can be fun, as can be writing ad hoc benchmarks, but for this task I needed something more powerful.&lt;/p&gt;
&lt;p&gt;I stumbled upon &lt;a href="https://bitbucket.org/wolfpld/tracy" target="_blank" rel="noreferrer"&gt;Tracy&lt;/a&gt;, a very capable frame profiler that shows CPU and GPU zones, has Lua instrumentation, annotates allocations and locks and displays call stacks.
Instrumentation of user code is easy, capturing data has a very low overhead and works locally or remotely on Linux, Windows, macOS and Android.&lt;/p&gt;
&lt;p&gt;The profiler that collects and visualize the data has a super rich ImGui interface with line plots, histograms, colored sections and the ability to compare two captures side by side.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;&lt;img
class="my-0 rounded-md"
loading="lazy"
decoding="async"
fetchpriority="low"
alt="Tracy profiler"
src="/images/Tracy.png"
&gt;&lt;figcaption&gt;Tracy profiler&lt;/figcaption&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;With the integration in place I can now be sure that I will be able to assess any performance change related with big refactorings or optimizations. &amp;#x1f4aa;&lt;/p&gt;</description></item><item><title>nCine Dev Update 5</title><link>https://encelo.github.io/2018-09-29-ncine-dev-update-5/</link><pubDate>Sat, 29 Sep 2018 00:00:00 +0000</pubDate><guid>https://encelo.github.io/2018-09-29-ncine-dev-update-5/</guid><description>&lt;p&gt;During those months two very important features appeared in the nCine.&lt;/p&gt;
&lt;p&gt;The first one is the integration of &lt;a href="https://www.lua.org/" target="_blank" rel="noreferrer"&gt;Lua&lt;/a&gt; for scripting, a language which is very easy to integrate and runs very fast.
With Lua the user can quickly prototype ideas or actually write the entire game with just scripts, in a way similar to other engines.&lt;/p&gt;
&lt;p&gt;There is also a second way of interacting with the language, as the engine has now a bunch of useful methods to exchange data with it.
You can load data from Lua tables to use a data-driven approach, or you can script the behavior of specific game objects.
Scripts can also be reloaded while the application is running, for a quicker iteration time and easier tuning of the game.&lt;/p&gt;
&lt;p&gt;The second feature is the integration of &lt;a href="https://github.com/ocornut/imgui/" target="_blank" rel="noreferrer"&gt;ImGui&lt;/a&gt;, a widely used immediate mode GUI that allows the engine to sports more meaningful and detailed performance data in a brand new debug overlay.
The old on-screen HUD has been rewritten and plenty new statistical plots have been added, including a new section dedicated to Lua showing things
like the amount of memory used or the number of operations per second.
While integrating ImGui, the rendering system was enhanced in order to accomodate the &lt;em&gt;scissor test&lt;/em&gt; and more flexible options for indexed drawing and vertex formats.&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="lazy" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/PQRnxeBpo-c?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="nCine debug overlay"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;I have also worked for a month on &lt;em&gt;ncParticleEditor&lt;/em&gt;, an editor for particle systems that use both new features: Lua for loading and saving and ImGui for the interface.
As the editor is really just an nCine application, it can run with no problems on Windows, Linux, macOS and of course Android!
The particle system was overhauled to support additional features like new particle affectors, a new particle random initializer structure and grayscale sprite rendering.&lt;/p&gt;
&lt;p&gt;I have to thank my friend &lt;a href="https://www.linkedin.com/in/marcolisci/" target="_blank" rel="noreferrer"&gt;Helba&lt;/a&gt; for his infinite patience while testing the program and for allowing me to show some of his projects in the video.
To help with testing I have integrated &lt;a href="http://crashrpt.sourceforge.net/" target="_blank" rel="noreferrer"&gt;CrashRpt&lt;/a&gt; in my debug Windows builds to ease the creation of &lt;a href="https://docs.microsoft.com/en-us/windows/desktop/debug/minidump-files" target="_blank" rel="noreferrer"&gt;Minidump&lt;/a&gt; files.&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="lazy" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/RLNI5NMCJ1E?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="ncParticleEditor"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;I hope you have enjoyed both the videos and the new features. &amp;#x1f609;&lt;/p&gt;</description></item><item><title>nCine Dev Update 4</title><link>https://encelo.github.io/2018-08-06-ncine-dev-update-4/</link><pubDate>Mon, 06 Aug 2018 00:00:00 +0000</pubDate><guid>https://encelo.github.io/2018-08-06-ncine-dev-update-4/</guid><description>&lt;p&gt;Plenty of work has been done during these few last months as I decided once again to update the engine renderer.
This time it went from using OpenGL 2 and OpenGL ES 2 to 3.3 and 3.0 respectively.&lt;/p&gt;
&lt;p&gt;I started by rewriting all the shaders to support the new &lt;code&gt;in&lt;/code&gt; and &lt;code&gt;out&lt;/code&gt; keywords in place of the old &lt;code&gt;attribute&lt;/code&gt; and &lt;code&gt;varying&lt;/code&gt; ones.
For the sprite shader I also changed it in a way that it doesn&amp;rsquo;t need any vertex data anymore, it generates a unit square by itself.
I also took the chance to add support for &lt;a href="https://www.khronos.org/opengl/wiki/Uniform_Buffer_Object" target="_blank" rel="noreferrer"&gt;Uniform Buffer Objects&lt;/a&gt;, one of the important new features of this OpenGL version.&lt;/p&gt;
&lt;p&gt;As I wanted to share an UBO between multiple rendering entities I have written an OpenGL buffer manager so that a rendering command, say for example a command used to render a sprite,
can ask for a certain amount of bytes without worrying about alignment or creating a new buffer when there is no more free space left.
The manager is also responsible for mapping and unmapping memory once per frame.&lt;/p&gt;
&lt;p&gt;For mapping I usually use a combination of &lt;code&gt;GL_MAP_INVALIDATE_BUFFER_BIT&lt;/code&gt; and &lt;code&gt;GL_MAP_FLUSH_EXPLICIT_BIT&lt;/code&gt;, as suggested on
the &lt;a href="https://www.khronos.org/opengl/wiki/Buffer_Object_Streaming" target="_blank" rel="noreferrer"&gt;Buffer Object Streaming&lt;/a&gt; wiki page, in order to maximize performances.
I don&amp;rsquo;t use persistent mapping as the &lt;a href="https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_buffer_storage.txt" target="_blank" rel="noreferrer"&gt;ARB_buffer_storage&lt;/a&gt; extension is only available with newer versions of the API.
I have extended the manager to also handle VBO memory and thus enabling the use of a single common buffer for all the vertices that don&amp;rsquo;t live in a custom VBO (which is a VBO that is only used by a single render command).&lt;/p&gt;
&lt;p&gt;Another important addition is the support for the &lt;a href="https://www.khronos.org/registry/OpenGL/extensions/KHR/KHR_debug.txt" target="_blank" rel="noreferrer"&gt;KHR_debug&lt;/a&gt; extension in the form of debug groups and object labels.
This feature is really important with tools like &lt;a href="http://apitrace.github.io/" target="_blank" rel="noreferrer"&gt;apitrace&lt;/a&gt; and &lt;a href="https://renderdoc.org/" target="_blank" rel="noreferrer"&gt;RenderDoc&lt;/a&gt; that offer support for them.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;&lt;img
class="my-0 rounded-md"
loading="lazy"
decoding="async"
fetchpriority="low"
alt="apitrace"
src="/images/apitrace.png"
&gt;&lt;figcaption&gt;apitrace&lt;/figcaption&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;Handling vertex formats has been renewed as well and there is now a pool of &lt;a href="https://www.khronos.org/opengl/wiki/Vertex_Specification#Vertex_Array_Object" target="_blank" rel="noreferrer"&gt;Vertex Array Objects&lt;/a&gt; that minimize the number of calls when changing formats.&lt;/p&gt;
&lt;p&gt;One feature I was willing to add for a long time is the ability to render sprites with a custom mesh. It can be used to split the transparent outline from the opaque part of a sprite,
like it is described in this &lt;a href="https://community.arm.com/graphics/b/blog/posts/mali-performance-7-accelerating-2d-rendering-using-opengl-es" target="_blank" rel="noreferrer"&gt;article&lt;/a&gt;, or to trim a particle down to the area with non completely transparent pixels,
or to animate with shape deformations. It also allows for the definition of a set of indices in order to reuse vertices, a feature that was not available in the engine before and that required some effort and testing.
As per vertex data, indices are handled by the memory manager and collected each frame in a set of common buffer objects.&lt;/p&gt;
&lt;p&gt;The new OpenGL ES 3.0 Android renderer allows the use of the &lt;a href="https://en.wikipedia.org/wiki/Ericsson_Texture_Compression#ETC2_and_EAC" target="_blank" rel="noreferrer"&gt;ETC2&lt;/a&gt; compressed format and immutable textures.
For this second feature to work on desktop I need to check the presence of the &lt;a href="https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_texture_storage.txt" target="_blank" rel="noreferrer"&gt;ARB_texture_storage&lt;/a&gt; estension as OpenGL 3.3 doesn&amp;rsquo;t offer it out of the box.
Viceversa there is one thing I use on desktop OpenGL that is not supported by OpenGL ES 3.0: &lt;a href="https://www.khronos.org/registry/OpenGL-Refpages/es3/html/glDrawElementsBaseVertex.xhtml" target="_blank" rel="noreferrer"&gt;glDrawElementsBaseVertex&lt;/a&gt;.
On Android I have to simulate this drawing command variation by using an additional offset.&lt;/p&gt;
&lt;p&gt;For sure one of the most complicate new feature, as it has many corner cases that needed extensive debugging, is the automatic batcher.
It allows to reduce the total number of draw calls issued by aggregating uniforms, vertices and indices from multiple commands into a single one. It can batch text nodes, regular sprites and custom mesh sprites.&lt;/p&gt;
&lt;p&gt;For entities with external vertex data, like text nodes and custom meshes, it has to supply an additional vertex attribute to act as a mesh id, while for custom meshes it has to add artificial indices to sprites that do not use them if they are going to be batched together with sprites that have user supplied ones.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s degenerate vertices and patched indices all the way down. &amp;#x1f609;&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;&lt;img
class="my-0 rounded-md"
loading="lazy"
decoding="async"
fetchpriority="low"
alt="RenderDoc"
src="/images/RenderDoc.png"
&gt;&lt;figcaption&gt;RenderDoc&lt;/figcaption&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;Some of those new features have a runtime setting, like switching automatic batching on and off:&lt;/p&gt;
&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;/// True if batching is enabled
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;bool&lt;/span&gt; batchingEnabled;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;/// True if using indices for vertex batching
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;bool&lt;/span&gt; batchingWithIndices;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;/// True if node culling is enabled
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;bool&lt;/span&gt; cullingEnabled;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;/// Minimum size for a batch to be collected
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;unsigned&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; minBatchSize;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;/// Maximum size for a batch before a forced split
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;unsigned&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; maxBatchSize;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Some others have an initialization configuration, like the dimension of each common VBO or the size of the VAO pool:&lt;/p&gt;
&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;/// Sets the maximum size in bytes for each VBO collecting geometry data
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;setVboSize&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;unsigned&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;long&lt;/span&gt; vboSize);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;/// Sets the maximum size in bytes for each IBO collecting index data
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;setIboSize&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;unsigned&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;long&lt;/span&gt; iboSize);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;/// Sets the maximum size for the pool of VAOs
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;setVaoPoolSize&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;unsigned&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; vaoPoolSize);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;/// Enables OpenGL debug context
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;enableGlDebug&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;bool&lt;/span&gt; shouldEnable);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;There is also a bunch of new rendering statistics that are aggregated per frame and displayed on screen, like the amount of video memory used by textures and buffer objects, or the number of VAO pool recycles.&lt;/p&gt;
&lt;p&gt;&lt;figure&gt;&lt;img
class="my-0 rounded-md"
loading="lazy"
decoding="async"
fetchpriority="low"
alt="apptest_sinescroller"
src="/images/apptest_sinescroller.png"
&gt;&lt;figcaption&gt;apptest_sinescroller&lt;/figcaption&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;Rendering should now be more efficient on all platforms, with fewer draw calls and more sprites on screen! &amp;#x1f4aa;&lt;/p&gt;</description></item><item><title>nCine Compilation Benchmark</title><link>https://encelo.github.io/2018-03-04-ncine-compilation-benchmark/</link><pubDate>Sun, 04 Mar 2018 00:00:00 +0000</pubDate><guid>https://encelo.github.io/2018-03-04-ncine-compilation-benchmark/</guid><description>&lt;p&gt;Are you curious about the time I spend to compile the nCine? &amp;#x1f604;
In this post I&amp;rsquo;m going to show how much time is needed to compile the engine on different platforms.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s start with the hardware and the software I&amp;rsquo;ve used for my tests.&lt;/p&gt;
&lt;h2 class="relative group"&gt;Hardware and software
&lt;div id="hardware-and-software" class="anchor"&gt;&lt;/div&gt;
&lt;/h2&gt;
&lt;h3 class="relative group"&gt;Xiaomi Mi Notebook Pro
&lt;div id="xiaomi-mi-notebook-pro" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;h4 class="relative group"&gt;Hardware
&lt;div id="hardware" class="anchor"&gt;&lt;/div&gt;
&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Intel Core i7-8550U&lt;/li&gt;
&lt;li&gt;16GB RAM, DDR4 2400 MHz, dual channel&lt;/li&gt;
&lt;li&gt;Samsung PM961 NVMe SSD, 256GB&lt;/li&gt;
&lt;li&gt;NVIDIA GeForce MX150, 2GB GDDR5&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 class="relative group"&gt;OS
&lt;div id="os" class="anchor"&gt;&lt;/div&gt;
&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Microsoft Windows 10 Professional (x64), Build 16299.248&lt;/li&gt;
&lt;li&gt;Arch Linux x86_64, Linux 4.15.3&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 class="relative group"&gt;Xiaomi Mi Notebook Air 13 2016
&lt;div id="xiaomi-mi-notebook-air-13-2016" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;h4 class="relative group"&gt;Hardware
&lt;div id="hardware-1" class="anchor"&gt;&lt;/div&gt;
&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Intel Core i5-6200U&lt;/li&gt;
&lt;li&gt;8GB RAM, DDR4 2133 MHz, single channel&lt;/li&gt;
&lt;li&gt;Samsung PM951 NVMe SSD, 256GB&lt;/li&gt;
&lt;li&gt;NVIDIA GeForce 940MX, 2GB GDDR5&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 class="relative group"&gt;OS
&lt;div id="os-1" class="anchor"&gt;&lt;/div&gt;
&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Microsoft Windows 10 Professional (x64), Build 16299.248&lt;/li&gt;
&lt;li&gt;Arch Linux x86_64, Linux 4.15.3&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 class="relative group"&gt;Chuwi Hi 10
&lt;div id="chuwi-hi-10" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;h4 class="relative group"&gt;Hardware
&lt;div id="hardware-2" class="anchor"&gt;&lt;/div&gt;
&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Intel Atom x5-Z8300&lt;/li&gt;
&lt;li&gt;4G RAM, DDR3 1600 MHz, single channel&lt;/li&gt;
&lt;li&gt;Hynix HCG8e eMMC 5.1, 64GB&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 class="relative group"&gt;OS
&lt;div id="os-2" class="anchor"&gt;&lt;/div&gt;
&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Microsoft Windows 10 Home (x64), Build 16299.248&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 class="relative group"&gt;Compilers and tools
&lt;div id="compilers-and-tools" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;h4 class="relative group"&gt;Windows
&lt;div id="windows" class="anchor"&gt;&lt;/div&gt;
&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;VS2017 15.5.6 (MSVC 19.12.25835)&lt;/li&gt;
&lt;li&gt;CMake 3.10.2 64bit&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 class="relative group"&gt;MSYS2 and Arch Linux
&lt;div id="msys2-and-arch-linux" class="anchor"&gt;&lt;/div&gt;
&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;GCC 7.3.0&lt;/li&gt;
&lt;li&gt;CMake 3.10.2&lt;/li&gt;
&lt;li&gt;Ninja 1.8.2&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 class="relative group"&gt;Results
&lt;div id="results" class="anchor"&gt;&lt;/div&gt;
&lt;/h2&gt;
&lt;p&gt;The nCine version is &lt;code&gt;2018.02.r183-2d21790&lt;/code&gt;, compiled with GLFW, &lt;code&gt;NCINE_BUILD_TESTS&lt;/code&gt; set to &lt;code&gt;ON&lt;/code&gt;, &lt;code&gt;NCINE_BUILD_UNIT_TESTS&lt;/code&gt; and &lt;code&gt;NCINE_BUILD_ANDROID&lt;/code&gt; both set to &lt;code&gt;OFF&lt;/code&gt;.
The tests have been conducted by running the compilation process multiple times and recording the best timings.&lt;/p&gt;
&lt;p&gt;In the &lt;em&gt;Configure&lt;/em&gt; phase CMake is invoked in order to configure the project and generate either a Visual Studio solutions, or Makefiles, or Ninja files.
In the &lt;em&gt;Build&lt;/em&gt; phase CMake is invoked with the &lt;code&gt;--build&lt;/code&gt; option in order to build the project. It takes advantage of multiple cores to compile only if using Ninja or MSVC with the &lt;code&gt;/MP&lt;/code&gt; option. When using standard makefiles I have additionally provided the timings of invoking &lt;code&gt;make -jN&lt;/code&gt; for the multicore compilation.&lt;/p&gt;
&lt;h3 class="relative group"&gt;Tables and charts
&lt;div id="tables-and-charts" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;h4 class="relative group"&gt;MSVC
&lt;div id="msvc" class="anchor"&gt;&lt;/div&gt;
&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th style="text-align: right"&gt;Mi Pro&lt;/th&gt;
&lt;th style="text-align: right"&gt;Mi Air&lt;/th&gt;
&lt;th style="text-align: right"&gt;Chuwi&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Configure&lt;/td&gt;
&lt;td style="text-align: right"&gt;14.20s&lt;/td&gt;
&lt;td style="text-align: right"&gt;12.02s&lt;/td&gt;
&lt;td style="text-align: right"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Build&lt;/td&gt;
&lt;td style="text-align: right"&gt;22.23s&lt;/td&gt;
&lt;td style="text-align: right"&gt;32.93s&lt;/td&gt;
&lt;td style="text-align: right"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Build with &lt;code&gt;/MP&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align: right"&gt;14.87s&lt;/td&gt;
&lt;td style="text-align: right"&gt;26.48s&lt;/td&gt;
&lt;td style="text-align: right"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;figure&gt;&lt;img
class="my-0 rounded-md"
loading="lazy"
decoding="async"
fetchpriority="low"
alt="MSVC chart"
src="/images/MSVC_chart.png"
&gt;&lt;figcaption&gt;MSVC chart&lt;/figcaption&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 class="relative group"&gt;MSYS2
&lt;div id="msys2" class="anchor"&gt;&lt;/div&gt;
&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th style="text-align: right"&gt;Mi Pro&lt;/th&gt;
&lt;th style="text-align: right"&gt;Mi Air&lt;/th&gt;
&lt;th style="text-align: right"&gt;Chuwi&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Configure Ninja&lt;/td&gt;
&lt;td style="text-align: right"&gt;8.13s&lt;/td&gt;
&lt;td style="text-align: right"&gt;12.33s&lt;/td&gt;
&lt;td style="text-align: right"&gt;40.90s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Build Ninja&lt;/td&gt;
&lt;td style="text-align: right"&gt;7.13s&lt;/td&gt;
&lt;td style="text-align: right"&gt;18.84s&lt;/td&gt;
&lt;td style="text-align: right"&gt;55.71s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Configure Make&lt;/td&gt;
&lt;td style="text-align: right"&gt;14.14s&lt;/td&gt;
&lt;td style="text-align: right"&gt;19.75s&lt;/td&gt;
&lt;td style="text-align: right"&gt;61.13s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Build Make&lt;/td&gt;
&lt;td style="text-align: right"&gt;46.80s&lt;/td&gt;
&lt;td style="text-align: right"&gt;66.12s&lt;/td&gt;
&lt;td style="text-align: right"&gt;226.53s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Build Make &lt;code&gt;-j&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align: right"&gt;12.65s&lt;/td&gt;
&lt;td style="text-align: right"&gt;30.59s&lt;/td&gt;
&lt;td style="text-align: right"&gt;84.90s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;figure&gt;&lt;img
class="my-0 rounded-md"
loading="lazy"
decoding="async"
fetchpriority="low"
alt="MSYS2 chart"
src="/images/MSYS2_chart.png"
&gt;&lt;figcaption&gt;MSYS2 chart&lt;/figcaption&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 class="relative group"&gt;Arch Linux
&lt;div id="arch-linux" class="anchor"&gt;&lt;/div&gt;
&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th style="text-align: right"&gt;Mi Pro&lt;/th&gt;
&lt;th style="text-align: right"&gt;Mi Air&lt;/th&gt;
&lt;th style="text-align: right"&gt;Chuwi&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Configure Ninja&lt;/td&gt;
&lt;td style="text-align: right"&gt;1.03s&lt;/td&gt;
&lt;td style="text-align: right"&gt;1.31s&lt;/td&gt;
&lt;td style="text-align: right"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Build Ninja&lt;/td&gt;
&lt;td style="text-align: right"&gt;3.68s&lt;/td&gt;
&lt;td style="text-align: right"&gt;7.85s&lt;/td&gt;
&lt;td style="text-align: right"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Configure Make&lt;/td&gt;
&lt;td style="text-align: right"&gt;1.25s&lt;/td&gt;
&lt;td style="text-align: right"&gt;1.58s&lt;/td&gt;
&lt;td style="text-align: right"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Build Make&lt;/td&gt;
&lt;td style="text-align: right"&gt;13.56s&lt;/td&gt;
&lt;td style="text-align: right"&gt;17.31s&lt;/td&gt;
&lt;td style="text-align: right"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Build Make &lt;code&gt;-j&lt;/code&gt;&lt;/td&gt;
&lt;td style="text-align: right"&gt;4.35s&lt;/td&gt;
&lt;td style="text-align: right"&gt;9.17s&lt;/td&gt;
&lt;td style="text-align: right"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;figure&gt;&lt;img
class="my-0 rounded-md"
loading="lazy"
decoding="async"
fetchpriority="low"
alt="Arch Linux chart"
src="/images/ArchLinux_chart.png"
&gt;&lt;figcaption&gt;Arch Linux chart&lt;/figcaption&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 class="relative group"&gt;Conclusions
&lt;div id="conclusions" class="anchor"&gt;&lt;/div&gt;
&lt;/h3&gt;
&lt;p&gt;The Mi Notebook Pro, with its Kaby Lake R featuring 4C/8T, achieves more than double the performance of the smaller Mi Notebook Air 13 when compiling with Ninja or &lt;code&gt;make -j8&lt;/code&gt; on all platforms. On Windows the speed-up is slightly less, but still very noticeable.
The Mi Notebook Pro comes with a faster SSD and dual channel RAM which surely have affected timings. The little and fanless Chuwi Hi 10 comes last when talking about performances, but it can still configure and build the project in under 100 seconds with Ninja. &amp;#x1f604;&lt;/p&gt;
&lt;p&gt;Speaking about the configure phase, it strangely takes a bit more time on the Mi Notebook Pro compared to the slower Mi Notebook Air on Windows.
It is also interesting to note that the Ninja generator in CMake is faster than the Makefiles one on all platforms, even if the difference on Linux is barely noticeable.
Speaking about Linux, I wasn&amp;rsquo;t prepared for such a big margin when compared with Windows. Can it be a matter of Ext4 vs NTFS? Or maybe a slowdown when invoking commands through the MSYS2 console? I&amp;rsquo;m not sure of the reasons but lucky me for I usually develop on Linux and then test on the supported platforms. &amp;#x1f605;&lt;/p&gt;
&lt;p&gt;I hope you have found this post interesting even if it wasn&amp;rsquo;t about a development update. They will come back soon so stay tuned! &amp;#x1f609;&lt;/p&gt;</description></item><item><title>nCine Dev Update 3</title><link>https://encelo.github.io/2018-02-10-ncine-dev-update-3/</link><pubDate>Sat, 10 Feb 2018 00:00:00 +0000</pubDate><guid>https://encelo.github.io/2018-02-10-ncine-dev-update-3/</guid><description>&lt;p&gt;A lot of work has been carried out during the last three and a half months.&lt;/p&gt;
&lt;p&gt;First of all I have added some macros to allow asserts in the code, think about checks like this:
&lt;code&gt;ASSERT_MSG_X(index &amp;lt; size_, &amp;quot;Index %u is out of bounds (size: %u)&amp;quot;, index, size_);&lt;/code&gt;
The macro also invokes a breakpoint if a debugger is connected, allowing to inspect the context around the failure.
It was a long needed feature that I finally had the time to implement. &amp;#x1f604;&lt;/p&gt;
&lt;p&gt;I have also found the time to configure &lt;a href="http://uncrustify.sourceforge.net/" target="_blank" rel="noreferrer"&gt;Uncrustify&lt;/a&gt; so that I could use it alongside &lt;a href="http://astyle.sourceforge.net/" target="_blank" rel="noreferrer"&gt;Artistic Style&lt;/a&gt; in order to further check consistency with my code conventions.&lt;/p&gt;
&lt;p&gt;But the big change is the support of many features of C++11, a leap forward for the whole codebase!&lt;/p&gt;
&lt;p&gt;I was wise enough to stop for a moment and think about the plethora of changes that the template library would require.
It is then that I realized that I would need to be disciplined and write unit tests first. I adopted &lt;a href="https://github.com/google/googletest" target="_blank" rel="noreferrer"&gt;Google Test&lt;/a&gt;, which is well supported by &lt;a href="https://www.qt.io/qt-features-libraries-apis-tools-and-ide/" target="_blank" rel="noreferrer"&gt;Qt Creator&lt;/a&gt;, and I started to write tests, plenty of them. The compiler helped with &lt;em&gt;code coverage&lt;/em&gt; data and &lt;a href="http://gcovr.com/" target="_blank" rel="noreferrer"&gt;Gcovr&lt;/a&gt; with HTML reports. After a month of work I had more than 500 tests and discovered many subtle bugs. &amp;#x1f605;&lt;/p&gt;
&lt;p&gt;Now I was ready to work on supporting C++11.
I setup a feature branch and started small by collecting low-hanging fruits first.
The very first change has been the replacement of all &lt;code&gt;NULL&lt;/code&gt; occurrencies with &lt;code&gt;nullptr&lt;/code&gt;, a thing that alone needed &lt;code&gt;set(CMAKE_CXX_STANDARD 11)&lt;/code&gt;. &amp;#x1f604;
Then more simple changes came, using &lt;code&gt;=delete&lt;/code&gt; for special member functions that were previously &lt;code&gt;private&lt;/code&gt;, the &lt;code&gt;override&lt;/code&gt; specifier, delegating construtors instead of init functions, enum classes, alias declarations instead of &lt;code&gt;typedef&lt;/code&gt;s and then I was ready for some bigger changes&amp;hellip;&lt;/p&gt;
&lt;p&gt;I moved the template library in its own namespace, &lt;code&gt;nctl&lt;/code&gt;, standing for &lt;em&gt;nCine Template Library&lt;/em&gt;. &amp;#x1f609;
I added a reverse iterator adapter for templated iterators, and added a sentinel to the hashmap iterator and to the list class in order to enable an easy and flexible way to iterate in both directions. But the code couldn&amp;rsquo;t be modern without some range-based for loops and some lambda functions all around. &amp;#x1f609;&lt;/p&gt;
&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;nctl&lt;span style="color:#f92672"&gt;::&lt;/span&gt;Array&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;int&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; array;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; (&lt;span style="color:#66d9ef"&gt;int&lt;/span&gt; i : array_)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; printf(&lt;span style="color:#e6db74"&gt;&amp;#34;%d &amp;#34;&lt;/span&gt;, i);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;forEach(players_.begin(), players_.end(), [](IAudioPlayer &lt;span style="color:#f92672"&gt;*&lt;/span&gt;player){ player&lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt;pause(); });&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Then it was time for the biggest features, move semantics and smart pointers.
In order to support the first one I have added &lt;em&gt;move constructors&lt;/em&gt; and &lt;em&gt;move assignment operators&lt;/em&gt; to all the containers, but also new insertion methods to allow the insertion of movable only objects, for example:&lt;/p&gt;
&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;inline&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;pushBack&lt;/span&gt;(&lt;span style="color:#66d9ef"&gt;const&lt;/span&gt; T &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;element) { &lt;span style="color:#66d9ef"&gt;operator&lt;/span&gt;[](size_) &lt;span style="color:#f92672"&gt;=&lt;/span&gt; element; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;inline&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;pushBack&lt;/span&gt;(T &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt;element) { &lt;span style="color:#66d9ef"&gt;operator&lt;/span&gt;[](size_) &lt;span style="color:#f92672"&gt;=&lt;/span&gt; nctl&lt;span style="color:#f92672"&gt;::&lt;/span&gt;move(element); }&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Time for smart pointers, enter &lt;code&gt;nctl::UniquePtr&lt;/code&gt; and &lt;code&gt;nctl::SharedPtr&lt;/code&gt;!
For the shared one to work reliably with multiple threads I have added atomics on all supported platforms.
I have replaced most of the raw pointers with unique ones and all factories now return a unique pointer to clearly show ownership transfer.&lt;/p&gt;
&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-cpp" data-lang="cpp"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;nctl&lt;span style="color:#f92672"&gt;::&lt;/span&gt;UniquePtr&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;ITextureLoader&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; texLoader &lt;span style="color:#f92672"&gt;=&lt;/span&gt; ITextureLoader&lt;span style="color:#f92672"&gt;::&lt;/span&gt;createFromFile(filename);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;load(&lt;span style="color:#f92672"&gt;*&lt;/span&gt;texLoader.get());&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;It has been a very long but also very satisfactory task and I&amp;rsquo;m proud of how the new codebase has turned out.
Now it&amp;rsquo;s time again to think about something entirely new and exciting!&lt;/p&gt;</description></item><item><title>nCine Dev Update 2</title><link>https://encelo.github.io/2017-10-22-ncine-dev-update-2/</link><pubDate>Sun, 22 Oct 2017 00:00:00 +0000</pubDate><guid>https://encelo.github.io/2017-10-22-ncine-dev-update-2/</guid><description>&lt;p&gt;During those three months I have been working on two big features.&lt;/p&gt;
&lt;p&gt;The first one has been the support of SDL2 GameController &lt;a href="https://wiki.libsdl.org/SDL_GameControllerAddMapping" target="_blank" rel="noreferrer"&gt;mapping&lt;/a&gt; format.
Initially my plan was to build a layer on top of my joystick input functions and leave the mapping code outside, as helper functions in a file distributed along the source of my tests and only linked by them.
Later on I decided to refactor everything and bring the code inside the engine, in an effort to make it easier for an application to transparently use it.&lt;/p&gt;
&lt;p&gt;The mapping logic is still just a layer on top of the original joystick functions, but now the state of a mapped joystick can be queried easily from the input manager, just as you can do with the non mapped one. It is also possible for an application to listen to some new mapped events I have introduced and completely disregard the old ones.
So, for example, instead of being notified of button 1 being pressed, the application would be notified of button &amp;ldquo;B&amp;rdquo; being pressed. This also works for axis, of course, so that an application can perform its logic on named axis, like X direction on the left stick, instead of an anonymous axis 5.&lt;/p&gt;
&lt;p&gt;In order for the mapping to work, the joystick must be recognized. SDL2 uses a custom code per each platform in order to derive a GUID to identify the connected joystick. When compiled against SDL2 the engine can just use &lt;a href="https://wiki.libsdl.org/SDL_JoystickGetGUID" target="_blank" rel="noreferrer"&gt;&lt;code&gt;SDL_JoystickGetGUID()&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://wiki.libsdl.org/SDL_JoystickGetGUIDString" target="_blank" rel="noreferrer"&gt;&lt;code&gt;SDL_JoystickGetGUIDString()&lt;/code&gt;&lt;/a&gt;, but on GLFW things are different. It seems like next version, GLFW 3.3, is going to have full SDL2 GameController suppport and provide a &lt;code&gt;glfwGetJoystickGUID()&lt;/code&gt; function, which I&amp;rsquo;m already using inside a &lt;code&gt;#if GLFW_VERSION_MAJOR == 3 &amp;amp;&amp;amp; GLFW_VERSION_MINOR &amp;gt;= 3&lt;/code&gt; block.&lt;/p&gt;
&lt;p&gt;For previous versions of GLFW there is no GUID to return and the joystick mapping code falls back on comparing the name of the connected joystick with the one in the database, it is not completely reliable but it still mostly works.
When running on Android, SDL 2.0.6 uses the first 16 characters of the joystick name as its GUID, a less than optimal way that just resembles closely my GLFW 3.2 fall back method. &amp;#x1f604; That is why I tried to do things better, especially because it is not so hard, when on API level 19, to just call the &lt;a href="https://developer.android.com/reference/android/view/InputDevice.html#getVendorId%28%29" target="_blank" rel="noreferrer"&gt;&lt;code&gt;getVendorId()&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://developer.android.com/reference/android/view/InputDevice.html#getProductId%28%29" target="_blank" rel="noreferrer"&gt;&lt;code&gt;getProductId()&lt;/code&gt;&lt;/a&gt; of the &lt;code&gt;InputDevice&lt;/code&gt; class and create an unique GUID with those.&lt;/p&gt;
&lt;p&gt;I have mentioned the mappings database before, it is assembled by combining all strings from &lt;a href="https://hg.libsdl.org/SDL/file/8df7a59b5528/src/joystick/SDL_gamecontrollerdb.h" target="_blank" rel="noreferrer"&gt;&lt;code&gt;SDL_gamecontrollerdb.h&lt;/code&gt;&lt;/a&gt;, all strings from &lt;a href="https://github.com/gabomdq/SDL_GameControllerDB/blob/master/gamecontrollerdb.txt" target="_blank" rel="noreferrer"&gt;&lt;code&gt;gamecontrollerdb.txt&lt;/code&gt;&lt;/a&gt; plus some more that are useful when running on GLFW 3.2 or on Android, given their particular GUID code.&lt;/p&gt;
&lt;p&gt;The second big task has been the complete transition from &lt;code&gt;ndk-build&lt;/code&gt; to CMake, as starting from version &lt;a href="https://cmake.org/cmake/help/v3.7/release/3.7.html#platforms" target="_blank" rel="noreferrer"&gt;3.7&lt;/a&gt; the developers have added the support for Android as a platform through the NDK or a standalone toolchain.
You start by defining &lt;code&gt;CMAKE_SYSTEM_NAME=Android&lt;/code&gt; and some more additional CMake variables to specify the API level, the NDK location, the CPU architecture or the STL type.
I have decided not to use the modified CMake 3.6 that is distributed with the Android SDK, even if it is natively supported by the Android Plugin for Gradle, but the upstream latest version. This way I can support and test a single version of CMake to build on every platform.&lt;/p&gt;
&lt;p&gt;See you again soon with new and exciting updates from the nCine development! &amp;#x1f609;&lt;/p&gt;</description></item><item><title>nCine Dev Update 1</title><link>https://encelo.github.io/2017-09-18-ncine-dev-update-1/</link><pubDate>Mon, 18 Sep 2017 00:00:00 +0000</pubDate><guid>https://encelo.github.io/2017-09-18-ncine-dev-update-1/</guid><description>&lt;p&gt;During June and July 2017 I have been working as usual, in my spare time, on the project. &amp;#x1f609;&lt;/p&gt;
&lt;p&gt;The first big June addition has been the automatic &lt;em&gt;screen culling&lt;/em&gt; of sprites, a very important feature needed in order to support games extending on multiple screens. The culling works on sprites of any kind (regular ones, particles, text nodes) and regardless of their scaling or rotation parameters. If those sprites are completely outside of the screen they will just not be rendered, saving draw calls from being issued.&lt;/p&gt;
&lt;p&gt;The next feature has been the complete support for mouse and keyboard on Android, a task which was made more interesting by some hacks I had to perform. For example, I had to implement a combination of bitmask operations just to support the right mouse button, a trick needed because most Android devices (but not my Shield TV) map the right mouse button to the special back button, which is handled as a key. &amp;#x1f62b;
It means that the &lt;code&gt;AINPUT_SOURCE_MOUSE&lt;/code&gt; can generate &lt;code&gt;AINPUT_EVENT_TYPE_KEY&lt;/code&gt;, just as if it was a &lt;code&gt;AINPUT_SOURCE_KEYBOARD&lt;/code&gt;. In this case if the keycode is &lt;code&gt;AKEYCODE_BACK&lt;/code&gt; I have to simulate a press from &lt;code&gt;AMOTION_EVENT_BUTTON_SECONDARY&lt;/code&gt;. &amp;#x1f605;&lt;/p&gt;
&lt;p&gt;Those changes together made possible the creation of a new test example where many different sprites wave randomly across the screen while the user can move the view as if controlling a top-down camera. As usual in my nCine tests the user can use multiple input methods like keyboard, mouse, joystick or the touch screen on Android.&lt;/p&gt;
&lt;p&gt;The last change is the full support for SDL2 that I added in July. It was required because the old SDL1 back-end was diverging too much in terms of functionalities when compared with the GLFW one. Now both back-ends support events for the connection and disconnection of a joystick, events for the mouse wheel scrolling (which on SDL1 I could only simulate) and, above all, the possibility to pass parameters during the OpenGL context creation. This last feature will be vital for a future support of newer versions of OpenGL and OpenGL ES.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s all for now, see you soon in a next installment of the nCine Dev Updates! &amp;#x1f609;&lt;/p&gt;</description></item><item><title>Get to know the nCine</title><link>https://encelo.github.io/2017-08-20-get-to-know-the-ncine/</link><pubDate>Sun, 20 Aug 2017 00:00:00 +0000</pubDate><guid>https://encelo.github.io/2017-08-20-get-to-know-the-ncine/</guid><description>&lt;p&gt;I am writing this to present my latest and biggest project to date, the nCine.
I was originally going to publish an article the day that I was going to release the source code but for various reasons you will have to wait a bit more. &amp;#x1f622;&lt;/p&gt;
&lt;p&gt;I am nevertheless going to describe today some of the technical aspects behind it hoping that when it’s ready for release there would already be someone interested in using it. &amp;#x1f609;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://ncine.github.io" target="_blank" rel="noreferrer"&gt;&lt;figure&gt;&lt;img
class="my-0 rounded-md"
loading="lazy"
decoding="async"
fetchpriority="low"
alt="The nCine banner"
src="/images/nCine_banner.png"
&gt;&lt;figcaption&gt;The nCine banner&lt;/figcaption&gt;&lt;/figure&gt;
&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;nCine is a multi-platform 2D game engine that works both on PC (meaning Linux, Windows and OS X) and on Android. Yes, the name is a portmanteau of my nickname “Encelo” and the word “engine”. &amp;#x1f605;&lt;/p&gt;
&lt;p&gt;I started working on it in June 2011, at the time I was using a Mercurial repository to keep track of changes but later on I converted it to a Git one. Some of the developing tools I use all the time are &lt;a href="https://www.qt.io/ide/" target="_blank" rel="noreferrer"&gt;Qt Creator&lt;/a&gt; as my IDE of choice (plus the Community Edition of &lt;a href="https://www.visualstudio.com/vs/community/" target="_blank" rel="noreferrer"&gt;Visual Studio&lt;/a&gt; on Windows), &lt;a href="https://cmake.org/" target="_blank" rel="noreferrer"&gt;CMake&lt;/a&gt; for everything related with the building and packaging phases, &lt;a href="http://www.doxygen.org/" target="_blank" rel="noreferrer"&gt;Doxygen&lt;/a&gt; and &lt;a href="http://www.graphviz.org/" target="_blank" rel="noreferrer"&gt;Graphviz&lt;/a&gt; for the documentation, &lt;a href="http://cppcheck.sourceforge.net/" target="_blank" rel="noreferrer"&gt;Cppcheck&lt;/a&gt; for the static analysis, &lt;a href="http://valgrind.org/" target="_blank" rel="noreferrer"&gt;Valgrind&lt;/a&gt; for additional memory debug and &lt;a href="http://astyle.sourceforge.net/" target="_blank" rel="noreferrer"&gt;Artistic Style&lt;/a&gt; for the automatic formatting.&lt;/p&gt;
&lt;p&gt;The project has a wide support in compilers as well, GCC and LLVM on Linux, MSVC and GCC (via &lt;a href="http://www.msys2.org/" target="_blank" rel="noreferrer"&gt;MSYS2&lt;/a&gt;/MinGW-w64) on Windows, LLVM on OS X and GCC and LLVM on Android with the NDK. At the moment there are more than one and a half thousand lines of CMake scripts and more than twenty five thousand of C++ code, excluding comments and blank lines. &amp;#x1f4aa;&lt;/p&gt;
&lt;p&gt;The nCine is a framework of classes and can be built as a static or dynamic library.&lt;/p&gt;
&lt;p&gt;This work has always been intended as a way for me to learn new things and develop my skills as an engine programmer, this is the reason why I have implemented many things from scratch instead of relying on external libraries.&lt;/p&gt;
&lt;p&gt;For example I don’t use the STL but I have my own implementation of template based containers (list, array, hashmap, string, …), algorithms and iterators. It was a very good way to overcome the fear of templates and learn about type traits, concepts, tag dispatching or SFINAE. &amp;#x1f604;&lt;/p&gt;
&lt;p&gt;The project also gave me the opportunity to learn low-level OS APIs such as monotonic timers, threads and their synchronization and affinity. All those primitives are handled differently by the Linux, Windows and Mach (OS X) kernels.&lt;/p&gt;
&lt;div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;"&gt;
&lt;iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen" loading="lazy" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/fUNGf3C8SOM?autoplay=0&amp;amp;controls=1&amp;amp;end=0&amp;amp;loop=0&amp;amp;mute=0&amp;amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="nCine tests"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p&gt;There are still, of course, some external dependencies: for the rendering I am relying on OpenGL, capable of reaching all the OSes I’m targeting, while for the sound I have used &lt;a href="http://kcat.strangesoft.net/openal.html" target="_blank" rel="noreferrer"&gt;OpenAL Soft&lt;/a&gt;. On top of the latter I use &lt;a href="https://www.xiph.org/ogg/" target="_blank" rel="noreferrer"&gt;Ogg&lt;/a&gt; and &lt;a href="https://www.xiph.org/vorbis/" target="_blank" rel="noreferrer"&gt;Vorbis&lt;/a&gt; to enable music streaming and playing compressed sound samples (with uncompressed Wave being another supported option). For interfacing with the window and the input system I use both &lt;a href="http://www.glfw.org/" target="_blank" rel="noreferrer"&gt;GLFW&lt;/a&gt; 3 and &lt;a href="https://www.libsdl.org/" target="_blank" rel="noreferrer"&gt;SDL&lt;/a&gt; 2, depending on which one is available, while on Android I use EGL and the Android API, sometimes through the NDK API and sometimes, like when accessing the joystick, directly through JNI calls.&lt;/p&gt;
&lt;p&gt;For texture loading I use &lt;a href="http://www.libpng.org/pub/png/libpng.html" target="_blank" rel="noreferrer"&gt;libpng&lt;/a&gt; and &lt;a href="https://developers.google.com/speed/webp/" target="_blank" rel="noreferrer"&gt;WebP&lt;/a&gt;, but the engine is also perfectly capable of loading GPU compressed formats such as DXT, ETC1, ATC, PVR and ASTC, embedded in DDS, &lt;a href="https://www.khronos.org/opengles/sdk/tools/KTX/" target="_blank" rel="noreferrer"&gt;KTX&lt;/a&gt; or PVR texture container formats.&lt;/p&gt;
&lt;p&gt;When it comes to font rendering I have written a parser for AngelCode’s &lt;a href="http://www.angelcode.com/products/bmfont/" target="_blank" rel="noreferrer"&gt;BMFont&lt;/a&gt; FNT format so that the engine can render text strings with support for &lt;a href="https://en.wikipedia.org/wiki/Kerning" target="_blank" rel="noreferrer"&gt;kerning&lt;/a&gt; pairs and customizable horizontal alignment between multiple lines.&lt;/p&gt;
&lt;p&gt;The dependencies that I have just mentioned are compiled directly from upstream sources for all supported platforms thanks to a set of custom CMake scripts. I am able to create the .so shared libraries on Linux, the DLLs on Windows, the frameworks on OS X and cross-compile for all the supported Android architectures (armeabi-v7a, arm64-v8a and x86_64). The scripts are also responsible for putting together the NSI installer and the ZIP portable archive on Windows, the application bundle on OS X and the TAR.GZ portable archive on Linux. For Arch Linux and MSYS2, which are both based on Pacman, I have written the corresponding PKGBUILD scripts to create a compressed package.&lt;/p&gt;
&lt;p&gt;I will soon release the engine on GitHub under a MIT license. At the moment you can have a look at the &lt;a href="https://ncine.github.io/" target="_blank" rel="noreferrer"&gt;website&lt;/a&gt;, which contains the initial API &lt;a href="https://ncine.github.io/docs/" target="_blank" rel="noreferrer"&gt;documentation&lt;/a&gt;, and you can follow the project on &lt;a href="https://twitter.com/ncine2d" target="_blank" rel="noreferrer"&gt;Twitter&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I hope to come back often in the future to write about new features and very soon to announce a full release!&lt;/p&gt;</description></item><item><title>About me</title><link>https://encelo.github.io/about/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://encelo.github.io/about/</guid><description>&lt;p&gt;I started my journey in computing on the &lt;strong&gt;Amiga&lt;/strong&gt;, first with a stock A500 in 1991, and later with a towered A1200 upgraded with a &lt;a href="http://amiga.resource.cx/exp/blizzardppc" target="_blank" rel="noreferrer"&gt;BlizzardPPC&lt;/a&gt; and &lt;a href="http://amiga.resource.cx/exp/blizzardvision" target="_blank" rel="noreferrer"&gt;BVision&lt;/a&gt;.
Those years shaped my fascination with graphics, games, programming, and the culture around making machines do more than anyone thought possible.&lt;/p&gt;
&lt;p&gt;In &lt;a href="https://twitter.com/encelo/status/1241380961079693315" target="_blank" rel="noreferrer"&gt;March 2000&lt;/a&gt; I had my first real contact with the *nix world, running &lt;a href="http://www.netbsd.org/releases/formal-1.4/NetBSD-1.4.2.html" target="_blank" rel="noreferrer"&gt;NetBSD 1.4.2&lt;/a&gt; on my 68030 A1200, followed soon after by LinuxPPC 2000.
That opened the door to free software and the joy (and pain) of tinkering with low-level systems.&lt;/p&gt;
&lt;p&gt;Since &lt;a href="https://twitter.com/encelo/status/1235202504826277895" target="_blank" rel="noreferrer"&gt;February 2005&lt;/a&gt; I&amp;rsquo;ve been a daily &lt;strong&gt;Arch Linux&lt;/strong&gt; user. It fits the way I like to work: simple, transparent, and close to the metal.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve always loved the &lt;a href="https://en.wikipedia.org/wiki/Demoscene" target="_blank" rel="noreferrer"&gt;demoscene&lt;/a&gt;, &lt;a href="https://en.wikipedia.org/wiki/Chiptune" target="_blank" rel="noreferrer"&gt;chiptunes&lt;/a&gt;, sinus scrollers, and roto-zoomers: the playful side of pushing hardware limits. At the same time, I follow modern breakthroughs in real-time graphics with the same excitement.&lt;/p&gt;
&lt;p&gt;This site is a place to share my work, notes, and projects.
All opinions expressed here are mine alone.&lt;/p&gt;</description></item><item><title>Credits</title><link>https://encelo.github.io/credits/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://encelo.github.io/credits/</guid><description>&lt;p&gt;This site is built with &lt;a href="https://gohugo.io/" target="_blank" rel="noreferrer"&gt;Hugo&lt;/a&gt; using the &lt;a href="https://blowfish.page/" target="_blank" rel="noreferrer"&gt;Blowfish&lt;/a&gt; theme by &lt;a href="https://n9o.xyz/" target="_blank" rel="noreferrer"&gt;Nuno Coração&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;a href="https://unsplash.com/photos/a-close-up-of-a-pink-heart-EaFX0kRvXT8" target="_blank" rel="noreferrer"&gt;background image&lt;/a&gt; is by &lt;a href="https://unsplash.com/@philipsfuture" target="_blank" rel="noreferrer"&gt;Philip Oroni&lt;/a&gt; on Unsplash.&lt;/p&gt;
&lt;p&gt;This site is licensed under the &lt;a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank" rel="noreferrer"&gt;Creative Commons BY-NC-SA 4.0 International&lt;/a&gt; license.&lt;/p&gt;</description></item><item><title>GnuPG Public Key</title><link>https://encelo.github.io/gpg/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://encelo.github.io/gpg/</guid><description>&lt;p&gt;This is my personal GnuPG public key, used to sign my Git commits.&lt;/p&gt;
&lt;p&gt;The same key is also published on &lt;a href="https://ncine.github.io/gpg/" target="_blank" rel="noreferrer"&gt;ncine.github.io&lt;/a&gt;, &lt;a href="https://github.com/encelo.gpg" target="_blank" rel="noreferrer"&gt;GitHub&lt;/a&gt;, &lt;a href="https://keys.openpgp.org/vks/v1/by-fingerprint/387CDEFE8A87AEB328978C80728985A18FEE4962" target="_blank" rel="noreferrer"&gt;keys.openpgp.org&lt;/a&gt;, and &lt;a href="https://keyserver.ubuntu.com/pks/lookup?op=get&amp;amp;search=0x387cdefe8a87aeb328978c80728985a18fee4962" target="_blank" rel="noreferrer"&gt;keyserver.ubuntu.com&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Fingerprint&lt;/strong&gt;: &lt;code&gt;387C DEFE 8A87 AEB3 2897 8C80 7289 85A1 8FEE 4962&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-----BEGIN PGP PUBLIC KEY BLOCK-----
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;mDMEaQtgRxYJKwYBBAHaRw8BAQdAOsr/iHk21mVkazyC+buM5GsA5KyivEF3OQEQ
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;/V11CrK0I0FuZ2VsbyBUaGVvZG9yb3UgPGVuY2Vsb0BnbWFpbC5jb20+iJAEExYK
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ADgWIQQ4fN7+ioeusyiXjIByiYWhj+5JYgUCaQtgRwIbAwULCQgHAgYVCgkICwIE
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;FgIDAQIeAQIXgAAKCRByiYWhj+5JYvN8AQDmNlD2nmwFD9CGfh7/QEq0kb358azG
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;/6I5nbVF/zEZ5wEA+/HCoaJg2eAyK0jS1LrPdVE+3U4Ixl2LeMla7QCyRwO4OARp
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;C2BHEgorBgEEAZdVAQUBAQdAwpyzK7PaJzREnEQMbCpUIwbV3dIpvBEcquiJflA7
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Qg8DAQgHiHgEGBYKACAWIQQ4fN7+ioeusyiXjIByiYWhj+5JYgUCaQtgRwIbDAAK
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;CRByiYWhj+5JYnaQAQCtBL/OOglVfsIYX9U7oerRitbbiuQg4p6Kw0fNPS3POwEA
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;49aeyHjT11/4dlFV2wrMtaeh+imyOGyvBqd5p4bKdQ64MwRpC2r7FgkrBgEEAdpH
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;DwEBB0Bs7RiybfBx2yEjYMPEpk98eWt/G6Ulh4Y9UUB3R/Na9IjvBBgWCgAgFiEE
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;OHze/oqHrrMol4yAcomFoY/uSWIFAmkLavsCGwIAgQkQcomFoY/uSWJ2IAQZFgoA
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;HRYhBCc/915Oia6QVMcBcb+ZWujTPolgBQJpC2r7AAoJEL+ZWujTPolgR3UBAIiu
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;2+IRG4xmAEXAMTut+5ZcGhdCjeyRSYQvE0T0FiPvAQC14YDsgOoqv35tWbXEchlJ
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;4kI2AFNgfFZKlXIra2YiCGztAQCALqegst0KwnY81JKdZ2o2LD6p7XUzzq64Wplm
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;bRH/iwEAw8ESd9PPfSK2H/uJnkpakhRYmLU/FQl8mZ8fno04SAG4MwRpC9BBFgkr
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;BgEEAdpHDwEBB0BdwJI1hsw2l1hwe+5lThHsDX6dBs5cw3IgrtmaZF/lTIh4BBgW
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;CgAgFiEEOHze/oqHrrMol4yAcomFoY/uSWIFAmkL0EECGyAACgkQcomFoY/uSWJ5
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;XgD/Ys9XJW6l3vM9VstjHxgdgt8ra/1YYIugd9NMv2X26qcBAPn4+EYfTagYc4xp
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ale7w5HwiNr4P3FwoxtrlB5geggE
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;=KP+Z
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;-----END PGP PUBLIC KEY BLOCK-----&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;</description></item><item><title>Support</title><link>https://encelo.github.io/support/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://encelo.github.io/support/</guid><description>&lt;p&gt;My work on &lt;strong&gt;open source projects&lt;/strong&gt; like nCine and SpookyGhost has always been a labor of love.
I build and maintain these tools in my free time, and I share them so anyone can learn, create, or just enjoy tinkering with code and graphics.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;ve found value in my work, or simply want to help me keep developing and documenting these projects, you can support me through one of the platforms below.
Every contribution, big or small, makes a real difference and helps me dedicate more time to open source.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/sponsors/encelo" target="_blank" rel="noreferrer"&gt;GitHub Sponsors&lt;/a&gt;— best for ongoing monthly support&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.patreon.com/bePatron?u=7042981" target="_blank" rel="noreferrer"&gt;Patreon&lt;/a&gt; — another way to support regularly&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ko-fi.com/R6R3167R8" target="_blank" rel="noreferrer"&gt;Ko-Fi&lt;/a&gt; — quick one-time tips (or coffees! &amp;#x2615;)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://buymeacoffee.com/encelo" target="_blank" rel="noreferrer"&gt;Buy Me a Coffee&lt;/a&gt; — same idea, just another friendly platform&lt;/li&gt;
&lt;li&gt;&lt;a href="https://liberapay.com/encelo/donate" target="_blank" rel="noreferrer"&gt;LiberaPay&lt;/a&gt; — focused on free software support&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.paypal.com/donate/?hosted_button_id=5Y9V7492ZNMAC" target="_blank" rel="noreferrer"&gt;PayPal&lt;/a&gt; — direct donations&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Thank you for helping me keep these projects alive and evolving. 🙏&lt;/p&gt;</description></item></channel></rss>